Back to index

courier  0.68.2
maildirrename.c
Go to the documentation of this file.
00001 /*
00002 ** Copyright 2002-2003 Double Precision, Inc.
00003 ** See COPYING for distribution information.
00004 */
00005 
00006 #if HAVE_CONFIG_H
00007 #include "config.h"
00008 #endif
00009 
00010 #include      <sys/types.h>
00011 #include      <sys/stat.h>
00012 #include      <string.h>
00013 #include      <stdlib.h>
00014 #include      <time.h>
00015 #if    HAVE_UNISTD_H
00016 #include      <unistd.h>
00017 #endif
00018 #include      <ctype.h>
00019 #include      <errno.h>
00020 #include      <stdio.h>
00021 #if HAVE_DIRENT_H
00022 #include <dirent.h>
00023 #define NAMLEN(dirent) strlen((dirent)->d_name)
00024 #else
00025 #define dirent direct
00026 #define NAMLEN(dirent) (dirent)->d_namlen
00027 #if HAVE_SYS_NDIR_H
00028 #include <sys/ndir.h>
00029 #endif
00030 #if HAVE_SYS_DIR_H
00031 #include <sys/dir.h>
00032 #endif
00033 #if HAVE_NDIR_H
00034 #include <ndir.h>
00035 #endif
00036 #endif
00037 
00038 #include      "maildirmisc.h"
00039 #include      "maildiraclt.h"
00040 
00041 /* Hierarchical rename */
00042 
00043 static int validrename(const char *oldname, const char *newname)
00044 {
00045        size_t l=strlen(oldname);
00046 
00047        if (strncmp(oldname, newname, l) == 0 &&
00048            (newname[l] == 0 || newname[l] == '.'))
00049               return (-1); /* Can't rename to its subdir */
00050 
00051        if (strchr(oldname, '/') ||
00052            strchr(newname, '/') ||
00053            oldname[0] != '.' ||
00054            newname[0] != '.' ||
00055            strcmp(newname, ".") == 0)
00056               return (-1);
00057 
00058        while (*newname)
00059        {
00060               if (*newname == '.')
00061               {
00062                      if (newname[1] == '.' || newname[1] == 0)
00063                             return -1;
00064               }
00065               ++newname;
00066        }
00067        return 0;
00068 }
00069 
00070 struct rename_list {
00071        struct rename_list *next;
00072        char *o, *n;
00073 };
00074 
00075 static int cmp_fn(const void *a, const void *b)
00076 {
00077        return strcmp( (*(struct rename_list **)a)->o,
00078                      (*(struct rename_list **)b)->o);
00079 }
00080 
00081 static int scan_maildir_rename(const char *, const char *, const char *,
00082                             int,
00083                             struct rename_list **);
00084 
00085 static int scan_aclhier_rename(const char *, const char *, const char *,
00086                             int,
00087                             struct rename_list **);
00088 
00089 int maildir_rename(const char *maildir,
00090                  const char *oldname, const char *newname, int flags,
00091                  void (*rename_func)(const char *old_path,
00092                                    const char *new_path))
00093 {
00094        struct rename_list *rl;
00095        int rc= -1;
00096 
00097        if (validrename(oldname, newname))
00098        {
00099               errno=EINVAL;
00100               return (-1);
00101        }
00102 
00103        rl=NULL;
00104 
00105        if (scan_maildir_rename(maildir, oldname, newname, flags, &rl) == 0 &&
00106            scan_aclhier_rename(maildir, oldname+1, newname+1, flags, &rl)
00107            == 0)
00108        {
00109               size_t n=0;
00110               struct rename_list *p, **a;
00111 
00112               for (p=rl; p; p=p->next)
00113                      ++n;
00114 
00115               if ((a=malloc(sizeof(struct rename_list *)*(n+1))) != NULL)
00116               {
00117                      n=0;
00118                      for (p=rl; p; p=p->next)
00119                             a[n++]=p;
00120                      a[n]=NULL;
00121                      if (n)
00122                             qsort(a, n, sizeof(*a), cmp_fn);
00123 
00124 
00125                      rc=0;
00126                      for (n=0; a[n]; n++)
00127                      {
00128                             if (rename(a[n]->o, a[n]->n))
00129                             {
00130                                    rc= -1;
00131                                    /* Try to undo the damage */
00132 
00133                                    while (n)
00134                                    {
00135                                           --n;
00136                                           rename(a[n]->n,
00137                                                  a[n]->o);
00138                                    }
00139                                    break;
00140                             }
00141                             if (rename_func)
00142                                    (*rename_func)(a[n]->o,
00143                                                  a[n]->n);
00144                      }
00145                      free(a);
00146               }
00147        }
00148        while (rl)
00149        {
00150               struct rename_list *p=rl;
00151 
00152               rl=rl->next;
00153               free(p->o);
00154               free(p->n);
00155               free(p);
00156        }
00157        return (rc);
00158 }
00159 
00160 static int add_rename_nochk(const char *on, const char *nn,
00161                          struct rename_list **rn)
00162 {
00163        struct rename_list *p;
00164 
00165        if ((p=malloc(sizeof(struct rename_list))) == NULL)
00166               return -1;
00167 
00168        if ((p->o=strdup(on)) == NULL)
00169        {
00170               free(p);
00171               return -1;
00172        }
00173 
00174        if ((p->n=strdup(nn)) == NULL)
00175        {
00176               free(p);
00177               return -1;
00178        }
00179 
00180        p->next= *rn;
00181        *rn=p;
00182        return 0;
00183 
00184 }
00185 
00186 static int add_rename(const char *on, const char *nn,
00187                     struct rename_list **rn)
00188 {
00189        if (access(nn, 0) == 0)
00190        {
00191               errno=EEXIST;
00192               return (-1);  /* Destination folder exists */
00193        }
00194 
00195        return add_rename_nochk(on, nn, rn);
00196 }
00197 
00198 static int scan_maildir_rename(const char *maildir,
00199                             const char *oldname,
00200                             const char *newname,
00201                             int flags,
00202                             struct rename_list **rename_list_head)
00203 {
00204        char *new_p;
00205 
00206        int oldname_l=strlen(oldname);
00207        DIR *dirp;
00208        struct dirent *de;
00209 
00210        dirp=opendir(maildir);
00211        while (dirp && (de=readdir(dirp)) != 0)
00212        {
00213               char *tst_cur;
00214 
00215               if (de->d_name[0] != '.')
00216                      continue;
00217               if (strcmp(de->d_name, "..") == 0)
00218                      continue;
00219 
00220               if ((tst_cur=malloc(strlen(maildir) + strlen(de->d_name)
00221                                 + sizeof("//cur")))
00222                   == NULL)
00223               {
00224                      closedir(dirp);
00225                      return (-1);
00226               }
00227               strcat(strcat(strcat(strcpy(tst_cur, maildir), "/"),
00228                            de->d_name), "/cur");
00229               if (access(tst_cur, 0))
00230               {
00231                      free(tst_cur);
00232                      continue;
00233               }
00234               if (strncmp(de->d_name, oldname, oldname_l))
00235               {
00236                      free(tst_cur);
00237                      continue;
00238               }
00239 
00240               if (de->d_name[oldname_l] == 0)
00241               {
00242                      if (!(flags & MAILDIR_RENAME_FOLDER))
00243                      {
00244                             free(tst_cur);
00245                             continue;     /* Only the hierarchy */
00246                      }
00247 
00248                      strcat(strcat(strcpy(tst_cur, maildir), "/"),
00249                             de->d_name);
00250 
00251                      new_p=malloc(strlen(maildir)+sizeof("/")
00252                                  +strlen(newname));
00253 
00254                      if (!new_p)
00255                      {
00256                             free(tst_cur);
00257                             closedir(dirp);
00258                             return (-1);
00259                      }
00260 
00261                      strcat(strcat(strcpy(new_p, maildir), "/"), newname);
00262 
00263                      if ( add_rename(tst_cur, new_p, rename_list_head))
00264                      {
00265                             free(new_p);
00266                             free(tst_cur);
00267                             closedir(dirp);
00268                             return (-1);
00269                      }
00270                      free(new_p);
00271                      free(tst_cur);
00272                      continue;
00273               }
00274               free(tst_cur);
00275 
00276                if (de->d_name[oldname_l] != '.')
00277                      continue;
00278 
00279               if (!(flags & MAILDIR_RENAME_SUBFOLDERS))
00280                      continue;
00281 
00282               tst_cur=malloc(strlen(maildir) + 
00283                             strlen(newname) + strlen(de->d_name+oldname_l)
00284                             + sizeof("/"));
00285 
00286               if (!tst_cur)
00287               {
00288                      closedir(dirp);
00289                      return (-1);
00290               }
00291 
00292               strcat(strcat(strcat(strcpy(tst_cur, maildir),
00293                                  "/"), newname), de->d_name+oldname_l);
00294 
00295               new_p=malloc(strlen(maildir) + strlen(de->d_name)
00296                           + sizeof("/"));
00297 
00298               if (!new_p)
00299               {
00300                      free(tst_cur);
00301                      closedir(dirp);
00302                      return (-1);
00303               }
00304 
00305               strcat(strcat(strcpy(new_p, maildir), "/"),
00306                      de->d_name);
00307 
00308               if ( add_rename(new_p, tst_cur, rename_list_head))
00309               {
00310                      free(new_p);
00311                      free(tst_cur);
00312                      closedir(dirp);
00313                      return (-1);
00314               }
00315               free(new_p);
00316               free(tst_cur);
00317        }
00318        if (dirp) closedir(dirp);
00319        return (0);
00320 }
00321 
00322 static int scan_aclhier2_rename(const char *aclhierdir,
00323                             const char *oldname,
00324                             const char *newname,
00325                             int flags,
00326                             struct rename_list **rename_list_head);
00327 
00328 static int scan_aclhier_rename(const char *maildir,
00329                             const char *oldname,
00330                             const char *newname,
00331                             int flags,
00332                             struct rename_list **rename_list_head)
00333 {
00334 
00335        char *aclhier=malloc(strlen(maildir)+sizeof("/" ACLHIERDIR));
00336        int rc;
00337 
00338        if (!aclhier)
00339               return -1;
00340 
00341        rc=scan_aclhier2_rename(strcat(strcpy(aclhier, maildir),
00342                                    "/" ACLHIERDIR), oldname, newname,
00343                             flags, rename_list_head);
00344 
00345        free(aclhier);
00346        return rc;
00347 }
00348 
00349 static int scan_aclhier2_rename(const char *aclhierdir,
00350                             const char *oldname,
00351                             const char *newname,
00352                             int flags,
00353                             struct rename_list **rename_list_head)
00354 {
00355        char *new_p;
00356 
00357        int oldname_l=strlen(oldname);
00358        DIR *dirp;
00359        struct dirent *de;
00360 
00361        dirp=opendir(aclhierdir);
00362        while (dirp && (de=readdir(dirp)) != 0)
00363        {
00364               char *tst_cur;
00365 
00366               if (de->d_name[0] == '.')
00367                      continue;
00368 
00369               if (strncmp(de->d_name, oldname, oldname_l))
00370               {
00371                      continue;
00372               }
00373 
00374               if ((tst_cur=malloc(strlen(aclhierdir) + strlen(de->d_name)
00375                                 + sizeof("/")))
00376                   == NULL)
00377               {
00378                      closedir(dirp);
00379                      return (-1);
00380               }
00381 
00382               if (de->d_name[oldname_l] == 0)
00383               {
00384                      if (!(flags & MAILDIR_RENAME_FOLDER))
00385                      {
00386                             free(tst_cur);
00387                             continue;     /* Only the hierarchy */
00388                      }
00389 
00390                      strcat(strcat(strcpy(tst_cur, aclhierdir), "/"),
00391                             de->d_name);
00392 
00393                      new_p=malloc(strlen(aclhierdir)+sizeof("/")
00394                                  +strlen(newname));
00395 
00396                      if (!new_p)
00397                      {
00398                             free(tst_cur);
00399                             closedir(dirp);
00400                             return (-1);
00401                      }
00402 
00403                      strcat(strcat(strcpy(new_p, aclhierdir), "/"), newname);
00404 
00405                      if ( add_rename_nochk(tst_cur, new_p,
00406                                          rename_list_head))
00407                      {
00408                             free(new_p);
00409                             free(tst_cur);
00410                             closedir(dirp);
00411                             return (-1);
00412                      }
00413                      free(new_p);
00414                      free(tst_cur);
00415                      continue;
00416               }
00417               free(tst_cur);
00418 
00419                if (de->d_name[oldname_l] != '.')
00420                      continue;
00421 
00422               if (!(flags & MAILDIR_RENAME_SUBFOLDERS))
00423                      continue;
00424 
00425               tst_cur=malloc(strlen(aclhierdir) + 
00426                             strlen(newname) + strlen(de->d_name+oldname_l)
00427                             + sizeof("/"));
00428 
00429               if (!tst_cur)
00430               {
00431                      closedir(dirp);
00432                      return (-1);
00433               }
00434 
00435               strcat(strcat(strcat(strcpy(tst_cur, aclhierdir),
00436                                  "/"), newname), de->d_name+oldname_l);
00437 
00438               new_p=malloc(strlen(aclhierdir) + strlen(de->d_name)
00439                           + sizeof("/"));
00440 
00441               if (!new_p)
00442               {
00443                      free(tst_cur);
00444                      closedir(dirp);
00445                      return (-1);
00446               }
00447 
00448               strcat(strcat(strcpy(new_p, aclhierdir), "/"),
00449                      de->d_name);
00450 
00451               if ( add_rename_nochk(new_p, tst_cur, rename_list_head))
00452               {
00453                      free(new_p);
00454                      free(tst_cur);
00455                      closedir(dirp);
00456                      return (-1);
00457               }
00458               free(new_p);
00459               free(tst_cur);
00460        }
00461        if (dirp) closedir(dirp);
00462        return (0);
00463 }