Back to index

courier  0.68.2
maildiraclt.c
Go to the documentation of this file.
00001 /*
00002 ** Copyright 2003-2004 Double Precision, Inc.
00003 ** See COPYING for distribution information.
00004 */
00005 
00006 #include      "maildiraclt.h"
00007 #include      "maildirmisc.h"
00008 #include      "maildircreate.h"
00009 #include      <time.h>
00010 #if    HAVE_UNISTD_H
00011 #include      <unistd.h>
00012 #endif
00013 #if HAVE_DIRENT_H
00014 #include <dirent.h>
00015 #define NAMLEN(dirent) strlen((dirent)->d_name)
00016 #else
00017 #define dirent direct
00018 #define NAMLEN(dirent) (dirent)->d_namlen
00019 #if HAVE_SYS_NDIR_H
00020 #include <sys/ndir.h>
00021 #endif
00022 #if HAVE_SYS_DIR_H
00023 #include <sys/dir.h>
00024 #endif
00025 #if HAVE_NDIR_H
00026 #include <ndir.h>
00027 #endif
00028 #endif
00029 #include      <string.h>
00030 #include      <errno.h>
00031 #include      <stdio.h>
00032 #include      <stdlib.h>
00033 #include      <assert.h>
00034 
00035 
00036 int maildir_acl_disabled=0;
00037 
00038 static int compar_aclt(const void *a, const void *b)
00039 {
00040        char ca=*(const char *)a;
00041        char cb=*(const char *)b;
00042 
00043        return (int)(unsigned char)ca - (int)(unsigned char)cb;
00044 }
00045 
00046 /* Post-op fixup of an aclt: collate, remove dupes. */
00047 
00048 static void fixup(maildir_aclt *aclt)
00049 {
00050        char *a, *b;
00051 
00052        qsort(*aclt, strlen(*aclt), 1, compar_aclt);
00053 
00054        for (a=b=*aclt; *a; a++)
00055        {
00056               if (*a == a[1])
00057                      continue;
00058               if ((int)(unsigned char)*a <= ' ')
00059                      continue; /* Silently drop bad access rights */
00060 
00061               *b++= *a;
00062        }
00063        *b=0;
00064 }
00065 
00066 static int validacl(const char *p)
00067 {
00068        while (*p)
00069        {
00070               if ((int)(unsigned char)*p <= ' ')
00071               {
00072                      errno=EINVAL;
00073                      return -1;
00074               }
00075               ++p;
00076        }
00077 
00078        return 0;
00079 }
00080 
00081 int maildir_aclt_init(maildir_aclt *aclt,
00082                     const char *initvalue_cstr,
00083                     const maildir_aclt *initvalue_cpy)
00084 {
00085        if (initvalue_cpy)
00086               initvalue_cstr= *initvalue_cpy;
00087 
00088        *aclt=NULL;
00089 
00090        if (!initvalue_cstr || !*initvalue_cstr)
00091               return 0;
00092 
00093        if (validacl(initvalue_cstr) < 0)
00094               return -1;
00095 
00096        if ( (*aclt=strdup(initvalue_cstr)) == NULL)
00097               return -1;
00098        fixup(aclt);
00099        return 0;
00100 }
00101 
00102 /* Destroy an aclt after it is no longer used. */
00103 
00104 void maildir_aclt_destroy(maildir_aclt *aclt)
00105 {
00106        if (*aclt)
00107               free(*aclt);
00108 }
00109 
00110 
00111 /* Add or remove access chars. */
00112 
00113 int maildir_aclt_add(maildir_aclt *aclt,
00114                    const char *add_strs,
00115                    const maildir_aclt *add_aclt)
00116 {
00117        if (add_aclt)
00118               add_strs= *add_aclt;
00119 
00120        if (!add_strs || !*add_strs)
00121               return 0;
00122 
00123        if (validacl(add_strs) < 0)
00124               return -1;
00125 
00126        if (*aclt)
00127        {
00128               char *p=realloc(*aclt, strlen(*aclt)+strlen(add_strs)+1);
00129 
00130               if (!p)
00131                      return -1;
00132               strcat(p, add_strs);
00133               *aclt=p;
00134 
00135        }
00136        else if ( ((*aclt)=strdup(add_strs)) == NULL)
00137               return -1;
00138 
00139        fixup(aclt);
00140        return 0;
00141 }
00142 
00143 int maildir_aclt_del(maildir_aclt *aclt,
00144                    const char *del_strs,
00145                    const maildir_aclt *del_aclt)
00146 {
00147        char *a, *b;
00148 
00149        if (del_aclt)
00150               del_strs= *del_aclt;
00151 
00152        if (!del_strs)
00153               return 0;
00154 
00155        if (!*aclt)
00156               return 0;
00157 
00158        for (a=b=*aclt; *a; a++)
00159        {
00160               if (strchr(del_strs, *a))
00161                      continue;
00162               *b++= *a;
00163        }
00164        *b=0;
00165 
00166        if (**aclt == 0)
00167        {
00168               free(*aclt);
00169               *aclt=NULL;
00170        }
00171        return 0;
00172 }
00173 
00174 /* -------------------------------------------------------------------- */
00175 
00176 
00177 void maildir_aclt_list_init(maildir_aclt_list *aclt_list)
00178 {
00179        aclt_list->head=NULL;
00180        aclt_list->tail=NULL;
00181 }
00182 
00183 void maildir_aclt_list_destroy(maildir_aclt_list *aclt_list)
00184 {
00185        struct maildir_aclt_node *p;
00186 
00187        for (p=aclt_list->head; p; )
00188        {
00189               struct maildir_aclt_node *q=p->next;
00190 
00191               free(p->identifier);
00192               maildir_aclt_destroy(&p->acl);
00193               free(p);
00194               p=q;
00195        }
00196        maildir_aclt_list_init(aclt_list);
00197 }
00198 
00199 
00200 /* Add an <identifier,acl> pair.  Returns 0 on success, -1 on failure */
00201 
00202 int maildir_aclt_list_add(maildir_aclt_list *aclt_list,
00203                        const char *identifier,
00204                        const char *aclt_str,
00205                        maildir_aclt *aclt_cpy)
00206 {
00207        struct maildir_aclt_node *p;
00208        const char *q;
00209 
00210        /* Check for valid identifiers */
00211 
00212        for (q=identifier; *q; q++)
00213               if ( (int)(unsigned char)*q <= ' ')
00214               {
00215                      errno=EINVAL;
00216                      return -1;
00217               }
00218 
00219        if (*identifier == 0)
00220        {
00221               errno=EINVAL;
00222               return -1;
00223        }
00224 
00225        if (aclt_cpy && *aclt_cpy)
00226               aclt_str= *aclt_cpy;
00227 
00228        for (p=aclt_list->head; p; p=p->next)
00229        {
00230               if (strcmp(p->identifier, identifier) == 0)
00231               {
00232                      maildir_aclt_destroy(&p->acl);
00233                      return maildir_aclt_init(&p->acl, aclt_str, NULL);
00234               }
00235        }
00236 
00237        if ((p=malloc(sizeof(*p))) == NULL ||
00238            (p->identifier=strdup(identifier)) == NULL)
00239        {
00240               if (p) free(p);
00241               return -1;
00242        }
00243 
00244        if (maildir_aclt_init(&p->acl, aclt_str, NULL) < 0)
00245        {
00246               free(p->identifier);
00247               free(p);
00248               return -1;
00249        }
00250 
00251        p->next=NULL;
00252        if ((p->prev=aclt_list->tail) != NULL)
00253               p->prev->next=p;
00254        else
00255               aclt_list->head=p;
00256        aclt_list->tail=p;
00257        return 0;
00258 }
00259 
00260 /*
00261 ** Remove 'identifier' from the ACL list.
00262 */
00263 
00264 int maildir_aclt_list_del(maildir_aclt_list *aclt_list,
00265                        const char *identifier)
00266 {
00267        struct maildir_aclt_node *p;
00268 
00269        for (p=aclt_list->head; p; p=p->next)
00270        {
00271               if (strcmp(p->identifier, identifier) == 0)
00272               {
00273                      if (p->prev)
00274                             p->prev->next=p->next;
00275                      else aclt_list->head=p->next;
00276 
00277                      if (p->next)
00278                             p->next->prev=p->prev;
00279                      else aclt_list->tail=p->prev;
00280 
00281                      maildir_aclt_destroy(&p->acl);
00282                      free(p->identifier);
00283                      free(p);
00284                      return 0;
00285               }
00286        }
00287        return 0;
00288 }
00289 
00290 /*
00291 ** Generic enumeration.
00292 */
00293 
00294 int maildir_aclt_list_enum(maildir_aclt_list *aclt_list,
00295                         int (*cb_func)(const char *identifier,
00296                                      const maildir_aclt *acl,
00297                                      void *cb_arg),
00298                         void *cb_arg)
00299 {
00300        struct maildir_aclt_node *p;
00301        int rc;
00302 
00303        for (p=aclt_list->head; p; p=p->next)
00304        {
00305               rc= (*cb_func)(p->identifier, &p->acl, cb_arg);
00306 
00307               if (rc)
00308                      return rc;
00309        }
00310        return 0;
00311 }
00312 
00313 const maildir_aclt *maildir_aclt_list_find(maildir_aclt_list *aclt_list,
00314                                       const char *identifier)
00315 {
00316        struct maildir_aclt_node *p;
00317 
00318        for (p=aclt_list->head; p; p=p->next)
00319        {
00320               if (strcmp(p->identifier, identifier) == 0)
00321                      return &p->acl;
00322        }
00323        return NULL;
00324 }
00325 
00326 /* ---------------------------------------------------------------------- */
00327 
00328 static int maildir_acl_read_check(maildir_aclt_list *aclt_list,
00329                              const char *maildir,
00330                              const char *path);
00331 
00332 int maildir_acl_read(maildir_aclt_list *aclt_list,
00333                    const char *maildir,
00334                    const char *path)
00335 {
00336        int rc=maildir_acl_read_check(aclt_list, maildir, path);
00337        char *p, *q;
00338 
00339        if (rc)
00340               maildir_aclt_list_destroy(aclt_list);
00341 
00342        if (rc <= 0)
00343               return rc;
00344 
00345        /*
00346        ** If the ACL config file for this folder was not found,
00347        ** check for the ACL config file for its parent folder.
00348        */
00349 
00350        if ((p=strdup(path)) == NULL)
00351               return -1;
00352 
00353        strcpy(p, path);
00354 
00355        q=strrchr(p, '.');
00356 
00357        if (!q)
00358        {
00359               free(p);
00360               errno=EIO; /* Should not happen */
00361               return -1;
00362        }
00363 
00364        *q=0;
00365 
00366        rc=maildir_acl_read(aclt_list, maildir, p);
00367        if (rc == 0)
00368        {
00369               /* Make sure to save the default acl list */
00370 
00371               rc=maildir_acl_write(aclt_list, maildir, path, NULL, NULL);
00372               if (rc >= 0) /* Ok if rc=1 */
00373                      rc=0;
00374               if (rc)
00375                      maildir_aclt_list_destroy(aclt_list);
00376        }
00377        free(p);
00378        return rc;
00379 }
00380 
00381 /*
00382 ** Attempt to retrieve the ACL set for the specified folder.
00383 **
00384 ** Returns -1 if error.
00385 ** Returns 0 if the ACL was retrieved.
00386 ** Returns 1 if the ACL configuration file does not exist.
00387 */
00388 
00389 static int maildir_aclt_add_default_admin(maildir_aclt_list *aclt_list);
00390 
00391 static int maildir_acl_read_check(maildir_aclt_list *aclt_list,
00392                               const char *maildir,
00393                               const char *path)
00394 {
00395        char *p, *q;
00396        FILE *fp;
00397        char buffer[BUFSIZ];
00398 
00399        maildir_aclt_list_init(aclt_list);
00400 
00401        if (!maildir || !*maildir)
00402               maildir=".";
00403        if (!path || !*path)
00404               path=".";
00405 
00406        if (strchr(path, '/') || *path != '.')
00407        {
00408               errno=EINVAL;
00409               return -1;
00410        }
00411 
00412        if (maildir_acl_disabled)
00413        {
00414               if (maildir_aclt_list_add(aclt_list, "owner",
00415                                      ACL_LOOKUP ACL_READ
00416                                      ACL_SEEN ACL_WRITE ACL_INSERT
00417                                      ACL_CREATE
00418                                      ACL_DELETEFOLDER
00419                                      ACL_DELETEMSGS ACL_EXPUNGE,
00420                                      NULL) < 0 ||
00421                   maildir_aclt_add_default_admin(aclt_list))
00422               {
00423                      maildir_aclt_list_destroy(aclt_list);
00424                      return -1;
00425               }
00426               return 0;
00427        }
00428 
00429        p=malloc(strlen(maildir)+strlen(path)+2);
00430 
00431        if (!p)
00432               return -1;
00433 
00434        strcat(strcat(strcpy(p, maildir), "/"), path);
00435 
00436        q=malloc(strlen(p)+sizeof("/" ACLFILE));
00437        if (!q)
00438        {
00439               free(p);
00440               return -1;
00441        }
00442        fp=fopen(strcat(strcpy(q, p), "/" ACLFILE), "r");
00443        free(p);
00444        free(q);
00445 
00446        if (fp == NULL)
00447        {
00448               if (strcmp(path, ".") == 0)
00449               {
00450                      /* INBOX ACL default */
00451 
00452                      if (maildir_aclt_list_add(aclt_list, "owner",
00453                                             ACL_ALL, NULL) < 0 ||
00454                          maildir_aclt_add_default_admin(aclt_list))
00455                      {
00456                             return -1;
00457                      }
00458                      return 0;
00459               }
00460 
00461               q=malloc(strlen(maildir)+sizeof("/" ACLHIERDIR "/") +
00462                       strlen(path));
00463               if (!q)
00464                      return -1;
00465 
00466               strcat(strcat(strcpy(q, maildir), "/" ACLHIERDIR "/"),
00467                      path+1);
00468 
00469               fp=fopen(q, "r");
00470               free(q);
00471        }
00472 
00473        if (!fp && errno != ENOENT)
00474               return -1;
00475 
00476        if (!fp)
00477               return 1;
00478 
00479        errno=0;
00480 
00481        while (fgets(buffer, sizeof(buffer), fp) != NULL)
00482        {
00483               char *p=strchr(buffer, '\n');
00484 
00485               if (p) *p=0;
00486 
00487               for (p=buffer; *p; p++)
00488                      if (*p == ' ')
00489                      {
00490                             *p=0;
00491                             do
00492                             {
00493                                    ++p;
00494                             } while (*p && *p == ' ');
00495                             break;
00496                      }
00497 
00498               if (maildir_aclt_list_add(aclt_list, buffer, p, NULL) < 0)
00499               {
00500                      if (errno != EINVAL)
00501                             return -1;
00502                      /* Sweep crap in the ACL file under the carpet */
00503               }
00504        }
00505        if (ferror(fp))
00506        {
00507               fclose(fp);
00508               return -1;
00509        }
00510        fclose(fp);
00511        if (maildir_aclt_add_default_admin(aclt_list))
00512               return -1;
00513        return 0;
00514 }
00515 
00516 /*
00517 ** Add the default ACL permissions to the administrators group.
00518 **
00519 ** Make sure that the ACL entry for "administrators" includes all
00520 ** rights.
00521 **
00522 ** Make sure that any ACL entries for "-administrators" or
00523 ** "-group=administrators" do not have LOOKUP and ADMIN.
00524 */
00525 
00526 static int maildir_aclt_add_default_admin(maildir_aclt_list *aclt_list)
00527 {
00528        const maildir_aclt *old_acl;
00529 
00530        static const char * const drop_acls[]={"-administrators",
00531                                           "-group=administrators"};
00532        size_t i;
00533 
00534        if ((old_acl=maildir_aclt_list_find(aclt_list, "group=administrators"))
00535            != NULL)
00536        {
00537               maildir_aclt new_acl;
00538 
00539               if (maildir_aclt_init(&new_acl, ACL_ALL, NULL))
00540                      return -1;
00541 
00542               if (maildir_aclt_add(&new_acl, NULL, old_acl) ||
00543                   maildir_aclt_list_add(aclt_list, "group=administrators",
00544                                      NULL, &new_acl))
00545               {
00546                      maildir_aclt_destroy(&new_acl);
00547                      return -1;
00548               }
00549               maildir_aclt_destroy(&new_acl);
00550        }
00551        else
00552        {
00553               maildir_aclt new_acl;
00554 
00555               old_acl=maildir_aclt_list_find(aclt_list, "administrators");
00556 
00557               if (maildir_aclt_init(&new_acl, ACL_ALL, NULL))
00558                      return -1;
00559 
00560               if (maildir_aclt_add(&new_acl, NULL, old_acl) ||
00561                   maildir_aclt_list_add(aclt_list, "administrators",
00562                                      NULL, &new_acl))
00563               {
00564                      maildir_aclt_destroy(&new_acl);
00565                      return -1;
00566               }
00567               maildir_aclt_destroy(&new_acl);
00568        }
00569 
00570        for (i=0; i<2; i++)
00571        {
00572               const char *n=drop_acls[i];
00573               if (maildir_aclt_list_del(aclt_list, n) < 0)
00574                      return -1;
00575        }
00576 
00577        return 0;
00578 }
00579 
00580 int maildir_acl_delete(const char *maildir,
00581                      const char *path)
00582 {
00583        char *p, *q;
00584 
00585 #if 0
00586        if (strcmp(path, SHARED) == 0)
00587               return 0;
00588 
00589        if (strncmp(path, SHARED ".", sizeof(SHARED)) == 0)
00590               return 0;
00591 #endif
00592        if (!maildir || !*maildir)
00593               maildir=".";
00594        if (!path || !*path)
00595               path=".";
00596 
00597        if (strchr(path, '/') || *path != '.')
00598        {
00599               errno=EINVAL;
00600               return -1;
00601        }
00602 
00603        p=malloc(strlen(maildir)+strlen(path)+2);
00604 
00605        if (!p)
00606               return -1;
00607 
00608        strcat(strcat(strcpy(p, maildir), "/"), path);
00609 
00610        q=malloc(strlen(p)+sizeof("/" ACLFILE));
00611        if (!q)
00612        {
00613               free(p);
00614               return -1;
00615        }
00616 
00617        unlink(strcat(strcpy(q, p), "/" ACLFILE));
00618        free(p);
00619        free(q);
00620 
00621        if (strcmp(path, ".") == 0)
00622        {
00623               /* INBOX ACL default */
00624 
00625               return 0;
00626        }
00627 
00628        q=malloc(strlen(maildir)+sizeof("/" ACLHIERDIR "/") +
00629                strlen(path));
00630        if (!q)
00631        {
00632               free(p);
00633               return -1;
00634        }
00635        strcat(strcat(strcpy(q, maildir), "/" ACLHIERDIR "/"),
00636               path+1);
00637 
00638        unlink(q);
00639        free(q);
00640        return 0;
00641 }
00642 
00643 static int save_acl(const char *identifier, const maildir_aclt *acl,
00644                   void *cb_arg);
00645 
00646 
00647 static int is_owner(const char *isme, void *void_arg)
00648 {
00649        if (void_arg && strcmp(isme, (const char *)void_arg) == 0)
00650               return 1;
00651 
00652        return strcmp(isme, "owner") == 0;
00653 }
00654 
00655 static int is_admin(const char *isme, void *void_arg)
00656 {
00657        return strcmp(isme, "administrators") == 0;
00658 
00659        /* We don't need to check for group=administrators, see chk_admin() */
00660 }
00661 
00662 static int check_adminrights(maildir_aclt *list)
00663 {
00664        if (strchr(maildir_aclt_ascstr(list), ACL_LOOKUP[0]) == NULL ||
00665            strchr(maildir_aclt_ascstr(list), ACL_ADMINISTER[0]) == NULL)
00666        {
00667               maildir_aclt_destroy(list);
00668               return -1;
00669        }
00670 
00671        maildir_aclt_destroy(list);
00672        return 0;
00673 }
00674 
00675 static int check_allrights(maildir_aclt *list)
00676 {
00677        const char *all=ACL_ALL;
00678 
00679        while (*all)
00680        {
00681               if (strchr(maildir_aclt_ascstr(list), *all) == NULL)
00682               {
00683                      maildir_aclt_destroy(list);
00684                      return -1;
00685               }
00686               ++all;
00687        }
00688 
00689        maildir_aclt_destroy(list);
00690        return 0;
00691 }
00692 
00693 static int maildir_acl_compute_chkowner(maildir_aclt *aclt,
00694                                    maildir_aclt_list *aclt_list,
00695                                    int (*cb_func)(const char *isme,
00696                                                  void *void_arg),
00697                                    void *void_arg,
00698                                    int chkowner);
00699 
00700 int maildir_acl_write(maildir_aclt_list *aclt_list,
00701                     const char *maildir,
00702                     const char *path,
00703 
00704                     const char *owner,
00705                     const char **err_failedrights)
00706 {
00707        int trycreate;
00708        struct maildir_tmpcreate_info tci;
00709        FILE *fp;
00710        char *p, *q;
00711        const char *dummy_string;
00712        maildir_aclt chkacls;
00713 
00714        if (!err_failedrights)
00715               err_failedrights= &dummy_string;
00716 
00717        if (!maildir || !*maildir)
00718               maildir=".";
00719        if (!path || !*path)
00720               path=".";
00721 
00722        if (strchr(path, '/') || *path != '.')
00723        {
00724               errno=EINVAL;
00725               return -1;
00726        }
00727 
00728        if (strcmp(path, ".")) /* Sanity check */
00729               for (dummy_string=path; *dummy_string; dummy_string++)
00730                      if (*dummy_string == '.' &&
00731                          (dummy_string[1] == '.' ||
00732                           dummy_string[1] == 0))
00733                      {
00734                             errno=EINVAL;
00735                             return -1;
00736                      }
00737 
00738 
00739        if (maildir_acl_compute_chkowner(&chkacls, aclt_list, is_owner, NULL,
00740                                     0))
00741        {
00742               maildir_aclt_destroy(&chkacls);
00743               errno=EINVAL;
00744               return -1;
00745        }
00746 
00747        if (check_adminrights(&chkacls))
00748        {
00749               *err_failedrights="owner";
00750               errno=EINVAL;
00751               return -1;
00752        }
00753 
00754        if (owner)
00755        {
00756               if (maildir_acl_compute_chkowner(&chkacls, aclt_list, is_owner,
00757                                            (void *)owner, 0))
00758               {
00759                      maildir_aclt_destroy(&chkacls);
00760                      errno=EINVAL;
00761                      return -1;
00762               }
00763               if (check_adminrights(&chkacls))
00764               {
00765                      *err_failedrights=owner;
00766                      errno=EINVAL;
00767                      return -1;
00768               }
00769        }
00770 
00771        if (maildir_acl_compute(&chkacls, aclt_list, is_admin, NULL))
00772        {
00773               maildir_aclt_destroy(&chkacls);
00774               errno=EINVAL;
00775               return -1;
00776        }
00777        if (check_allrights(&chkacls))
00778        {
00779               errno=EINVAL;
00780               return -1;
00781        }
00782 
00783        p=malloc(strlen(maildir)+strlen(path)+2);
00784 
00785        if (!p)
00786               return -1;
00787 
00788        strcat(strcat(strcpy(p, maildir), "/"), path);
00789 
00790        maildir_tmpcreate_init(&tci);
00791 
00792        tci.maildir=p;
00793        tci.uniq="acl";
00794        tci.doordie=1;
00795 
00796        fp=maildir_tmpcreate_fp(&tci);
00797 
00798        trycreate=0;
00799 
00800        if (fp)
00801        {
00802               q=malloc(strlen(p) + sizeof("/" ACLFILE));
00803               if (!q)
00804               {
00805                      fclose(fp);
00806                      unlink(tci.tmpname);
00807                      maildir_tmpcreate_free(&tci);
00808                      free(p);
00809                      return -1;
00810               }
00811               strcat(strcpy(q, p), "/" ACLFILE);
00812               free(tci.newname);
00813               tci.newname=q;
00814               free(p);
00815        }
00816        else
00817        {
00818               free(p);
00819 
00820               q=malloc(strlen(maildir)+sizeof("/" ACLHIERDIR "/") +
00821                       strlen(path));
00822               if (!q)
00823               {
00824                      maildir_tmpcreate_free(&tci);
00825                      return -1;
00826               }
00827               strcat(strcat(strcpy(q, maildir), "/" ACLHIERDIR "/"), path+1);
00828 
00829               tci.maildir=maildir;
00830               tci.uniq="acl";
00831               tci.doordie=1;
00832 
00833               fp=maildir_tmpcreate_fp(&tci);
00834 
00835               if (!fp)
00836               {
00837                      free(q);
00838                      maildir_tmpcreate_free(&tci);
00839                      return -1;
00840               }
00841               free(tci.newname);
00842               tci.newname=q;
00843               trycreate=1;
00844        }
00845 
00846        if (maildir_aclt_list_enum(aclt_list, save_acl, fp) < 0 ||
00847            ferror(fp) || fflush(fp) < 0)
00848        {
00849               fclose(fp);
00850               unlink(tci.tmpname);
00851               maildir_tmpcreate_free(&tci);
00852               return -1;
00853        }
00854        fclose(fp);
00855 
00856        if (rename(tci.tmpname, tci.newname) < 0)
00857        {
00858               /* Perhaps ACLHIERDIR needs to be created? */
00859 
00860               if (!trycreate)
00861               {
00862                      unlink(tci.tmpname);
00863                      maildir_tmpcreate_free(&tci);
00864                      return -1;
00865               }
00866 
00867               p=strrchr(tci.newname, '/');
00868               *p=0;
00869               mkdir(tci.newname, 0755);
00870               *p='/';
00871 
00872               if (rename(tci.tmpname, tci.newname) < 0)
00873               {
00874                      unlink(tci.tmpname);
00875                      maildir_tmpcreate_free(&tci);
00876                      return -1;
00877               }
00878        }
00879        maildir_tmpcreate_free(&tci);
00880        return 0;
00881 }
00882 
00883 static int save_acl(const char *identifier, const maildir_aclt *acl,
00884                   void *cb_arg)
00885 {
00886        if (fprintf((FILE *)cb_arg, "%s %s\n",
00887                   identifier,
00888                   maildir_aclt_ascstr(acl)) < 0)
00889               return -1;
00890        return 0;
00891 }
00892 
00893 struct maildir_acl_resetList {
00894        struct maildir_acl_resetList *next;
00895        char *mbox;
00896 };
00897 
00898 /*
00899 ** When a maildir is opened check for stale entries in Maildir/ACLHIERDIR.
00900 **
00901 ** Maildir/ACLHIERDIR/folder.subfolder should be removed unless there exists
00902 ** Maildir/.folder.subfolder.subsubfolder
00903 **
00904 **
00905 ** acl_check_cb is the callback function for maildir_list, which receives
00906 ** INBOX.folder.subfolder.subsubfolder.  It goes through the link list with
00907 ** Maildir/ACLHIERDIR's contents, and removes folder.subfolder if its found.
00908 **
00909 ** After maildir_list is done, anything that's left in the list can be safely
00910 ** removed.
00911 */
00912 
00913 static void acl_check_cb(const char *mbox, void *voidarg)
00914 {
00915        struct maildir_acl_resetList **l=
00916               (struct maildir_acl_resetList **)voidarg;
00917 
00918        if (strncmp(mbox, INBOX ".", sizeof(INBOX ".")-1))
00919               return; /* Huh? */
00920 
00921        mbox += sizeof(INBOX ".")-1;
00922 
00923        while (*l)
00924        {
00925               int cl= strlen( (*l)->mbox );
00926 
00927               if (strncmp(mbox, (*l)->mbox, cl) == 0 &&
00928                   mbox[cl] == '.')
00929               {
00930                      struct maildir_acl_resetList *p= *l;
00931 
00932                      *l= p->next;
00933                      free(p->mbox);
00934                      free(p);
00935                      continue;
00936               }
00937 
00938               l= &(*l)->next;
00939        }
00940 }
00941 
00942 int maildir_acl_reset(const char *maildir)
00943 {
00944        DIR *dirp;
00945        struct dirent *de;
00946        char *p;
00947        struct maildir_acl_resetList *rl=NULL;
00948        struct maildir_acl_resetList *r;
00949        time_t now;
00950        struct stat stat_buf;
00951 
00952        p=malloc(strlen(maildir) + sizeof("/" ACLHIERDIR));
00953        if (!p)
00954               return -1;
00955 
00956        strcat(strcpy(p, maildir), "/" ACLHIERDIR);
00957 
00958        dirp=opendir(p);
00959 
00960        if (!dirp)
00961        {
00962               mkdir(p, 0755);
00963               dirp=opendir(p);
00964        }
00965        free(p);
00966 
00967        while (dirp && (de=readdir(dirp)) != NULL)
00968        {
00969               if (de->d_name[0] == '.')
00970                      continue;
00971 
00972               if ((r=malloc(sizeof(struct maildir_acl_resetList))) == NULL
00973                   || (r->mbox=strdup(de->d_name)) == NULL)
00974               {
00975                      if (r)
00976                             free(r);
00977 
00978                      while (rl)
00979                      {
00980                             r=rl;
00981                             rl=r->next;
00982                             free(r->mbox);
00983                             free(r);
00984                      }
00985                      closedir(dirp);
00986                      return -1;
00987               }
00988 
00989               r->next=rl;
00990               rl=r;
00991        }
00992        if (dirp) closedir(dirp);
00993 
00994        maildir_list(maildir, acl_check_cb, &rl);
00995 
00996        time(&now);
00997 
00998        while (rl)
00999        {
01000               r=rl;
01001               rl=r->next;
01002 
01003               p=malloc(strlen(maildir)+strlen(r->mbox) +
01004                       sizeof("/" ACLHIERDIR "/"));
01005               if (p)
01006               {
01007                      strcat(strcat(strcpy(p, maildir),
01008                                   "/" ACLHIERDIR "/"), r->mbox);
01009 
01010                      /* Only unlink stale entries after 1 hour (race) */
01011 
01012                      if (stat(p, &stat_buf) == 0 &&
01013                          stat_buf.st_mtime < now - 60*60)
01014                             unlink(p);
01015                      free(p);
01016               }
01017               free(r->mbox);
01018               free(r);
01019        }
01020        return 0;
01021 }
01022 
01023 /*
01024 ** An ACL entry for "administrators" or "group=administrators" will match
01025 ** either one.
01026 */
01027 
01028 static int chk_admin(int (*cb_func)(const char *isme,
01029                                 void *void_arg),
01030                    const char *identifier,
01031                    void *void_arg)
01032 {
01033        if (strcmp(identifier, "administrators") == 0 ||
01034            strcmp(identifier, "group=administrators") == 0)
01035        {
01036               int rc=(*cb_func)("administrators", void_arg);
01037 
01038               if (rc == 0)
01039                      rc=(*cb_func)("group=administrators", void_arg);
01040               return rc;
01041        }
01042 
01043        return (*cb_func)(identifier, void_arg);
01044 }
01045 
01046 #define ISIDENT(s) \
01047        (MAILDIR_ACL_ANYONE(s) ? 1: chk_admin(cb_func, (s), void_arg))
01048 
01049 static int maildir_acl_compute_chkowner(maildir_aclt *aclt,
01050                                    maildir_aclt_list *aclt_list,
01051                                    int (*cb_func)(const char *isme,
01052                                                  void *void_arg),
01053                                    void *void_arg,
01054                                    int chkowner)
01055 {
01056        struct maildir_aclt_node *p;
01057        int rc;
01058 
01059        if (maildir_aclt_init(aclt, "", NULL) < 0)
01060               return -1;
01061 
01062        for (p=aclt_list->head; p; p=p->next)
01063        {
01064               if (p->identifier[0] == '-')
01065                      continue;
01066 
01067               rc= ISIDENT(p->identifier);
01068 
01069               if (rc < 0)
01070               {
01071                      maildir_aclt_destroy(aclt);
01072                      return rc;
01073               }
01074 
01075               if (rc == 0)
01076                      continue;
01077 
01078               if (maildir_aclt_add(aclt, NULL, &p->acl) < 0)
01079               {
01080                      maildir_aclt_destroy(aclt);
01081                      return rc;
01082               }
01083        }
01084 
01085        for (p=aclt_list->head; p; p=p->next)
01086        {
01087               if (p->identifier[0] != '-')
01088                      continue;
01089 
01090               rc= ISIDENT(p->identifier+1);
01091 
01092               if (rc < 0)
01093               {
01094                      maildir_aclt_destroy(aclt);
01095                      return rc;
01096               }
01097 
01098               if (rc == 0)
01099                      continue;
01100 
01101               if (maildir_aclt_del(aclt, NULL, &p->acl) < 0)
01102               {
01103                      maildir_aclt_destroy(aclt);
01104                      return rc;
01105               }
01106        }
01107 
01108        /*
01109        ** In our scheme, the owner always gets admin rights.
01110        */
01111 
01112        rc=chkowner ? (*cb_func)("owner", void_arg):0;
01113 
01114        if (maildir_acl_disabled)
01115               rc=0;  /* Except when ACLs are disabled */
01116 
01117        if (rc < 0)
01118        {
01119               maildir_aclt_destroy(aclt);
01120               return rc;
01121        }
01122 
01123        if (rc)
01124        {
01125               if (maildir_aclt_add(aclt, ACL_ADMINISTER, NULL) < 0)
01126               {
01127                      maildir_aclt_destroy(aclt);
01128                      return rc;
01129               }
01130        }
01131        return 0;
01132 }
01133 
01134 int maildir_acl_compute(maildir_aclt *aclt, maildir_aclt_list *aclt_list,
01135                      int (*cb_func)(const char *isme,
01136                                    void *void_arg), void *void_arg)
01137 {
01138        return maildir_acl_compute_chkowner(aclt, aclt_list, cb_func, void_arg,
01139                                        1);
01140 }
01141 
01142 static int chk_array(const char *identifier, void *void_arg);
01143 
01144 int maildir_acl_compute_array(maildir_aclt *aclt,
01145                            maildir_aclt_list *aclt_list,
01146                            const char * const *identifiers)
01147 {
01148        return maildir_acl_compute(aclt, aclt_list, chk_array,
01149                                (void *)identifiers);
01150 }
01151 
01152 static int chk_array(const char *identifier, void *void_arg)
01153 {
01154        const char * const *p=(const char * const *)void_arg;
01155        size_t i;
01156 
01157        for (i=0; p[i]; i++)
01158               if (strcmp(identifier, p[i]) == 0)
01159                      return 1;
01160        return 0;
01161 }
01162 
01163 /* -------------------------------------------------------------------- */
01164 
01165 int maildir_acl_canlistrights(const char *myrights)
01166 {
01167        return (strchr(myrights, ACL_LOOKUP[0]) ||
01168               strchr(myrights, ACL_READ[0]) ||
01169               strchr(myrights, ACL_INSERT[0]) ||
01170               strchr(myrights, ACL_CREATE[0]) ||
01171               strchr(myrights, ACL_DELETEFOLDER[0]) ||
01172               strchr(myrights, ACL_EXPUNGE[0]) ||
01173               strchr(myrights, ACL_ADMINISTER[0]));
01174 }
01175 
01176 /* --------------------------------------------------------------------- */
01177 
01178 /*
01179 ** Compute owner ACL identifiers applicable to a mailbox that's owned by
01180 ** 'mailbox_owner'.
01181 */
01182 
01183 static int get_owner_list( int (*cb_func)(const char *, void *),
01184                         const char *c,
01185                         const char *mailbox_owner, void *arg)
01186 {
01187        char *a;
01188        int rc;
01189        const char *p, *q;
01190 
01191        a=malloc(sizeof("user=")+strlen(c));
01192        if (!a)
01193               return -1;
01194 
01195        strcat(strcpy(a, "user="), c);
01196 
01197        rc=(*cb_func)(a, arg);
01198 
01199        if (rc == 0 && strcmp(a, mailbox_owner) == 0)
01200               rc=(*cb_func)("owner", arg);
01201        free(a);
01202 
01203        if (rc)
01204               return rc;
01205 
01206        c=getenv("OPTIONS");
01207 
01208        for (p=c; p && *p; )
01209        {
01210               if (*p == ',')
01211               {
01212                      ++p;
01213                      continue;
01214               }
01215 
01216               q=p;
01217               while (*p && *p != ',')
01218                      ++p;
01219 
01220               if (strncmp(q, "group=", 6) == 0)
01221               {
01222                      a=malloc(p-q+1);
01223                      if (!a)
01224                             return -1;
01225 
01226                      memcpy(a, q, p-q);
01227                      a[p-q]=0;
01228                      rc=(*cb_func)(a, arg);
01229                      free(a);
01230                      if (rc)
01231                             return rc;
01232               }
01233        }
01234        return 0;
01235 }
01236 
01237 static int count_owner_list(const char *o, void *arg)
01238 {
01239        ++*(size_t *)arg;
01240 
01241        return 0;
01242 }
01243 
01244 static int save_owner_list(const char *o, void *arg)
01245 {
01246        char ***p=(char ***)arg;
01247 
01248        **p=strdup(o);
01249 
01250        if (**p == NULL)
01251               return -1;
01252 
01253        ++*p;
01254        return 0;
01255 }
01256 
01257 int maildir_acl_computerights(maildir_aclt *rights,
01258                            maildir_aclt_list *acl_list,
01259                            const char *me,
01260                            const char *folder_owner)
01261 {
01262        char **owner_array;
01263        size_t owner_cnt;
01264        char **p;
01265        int rc;
01266 
01267        owner_cnt=1;
01268 
01269        if (get_owner_list(count_owner_list, me, folder_owner,
01270                         (void *)&owner_cnt) ||
01271            (owner_array=calloc(owner_cnt, sizeof(char *))) == NULL)
01272               return -1;
01273 
01274        p=owner_array;
01275 
01276        if (get_owner_list(save_owner_list, me, folder_owner, (void *)&p))
01277        {
01278               for (owner_cnt=0; owner_array[owner_cnt]; ++owner_cnt)
01279                      free(owner_array[owner_cnt]);
01280               free(owner_array);
01281               return -1;
01282        }
01283 
01284        rc=maildir_acl_compute_array(rights, acl_list,
01285                                  (const char * const *)owner_array);
01286 
01287        for (owner_cnt=0; owner_array[owner_cnt]; ++owner_cnt)
01288               free(owner_array[owner_cnt]);
01289        free(owner_array);
01290        return rc;
01291 }