Back to index

courier  0.68.2
Classes | Defines | Functions | Variables
smapsnapshot.c File Reference
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <signal.h>
#include <fcntl.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "mysignal.h"
#include "imapd.h"
#include "imapscanclient.h"
#include "imapwrite.h"
#include "maildir/config.h"
#include "maildir/maildircreate.h"
#include "maildir/maildirrequota.h"
#include "maildir/maildirgetquota.h"
#include "maildir/maildirquota.h"
#include "maildir/maildirmisc.h"
#include "maildir/maildirwatch.h"

Go to the source code of this file.

Classes

struct  snapshot_list

Defines

#define dirent   direct
#define NAMLEN(dirent)   (dirent)->d_namlen

Functions

int keywords ()
char * readline (unsigned i, FILE *)
void set_time (const char *tmpname, time_t timestamp)
void smapword (const char *)
static struct snapshot_listfind_next_snapshot (struct snapshot_list *s, const char *n)
static void delete_snapshot (struct snapshot_list *snn)
static int restore_snapshot2 (const char *snapshot_dir, FILE *fp, struct imapscaninfo *new_index)
static int restore_snapshot (const char *dir, FILE *snapshot_fp, char **last_snapshot)
void snapshot_select (int flag)
int snapshot_init (const char *folder, const char *snapshot)
void snapshot_needed ()
void snapshot_save ()

Variables

char * current_mailbox
static char * snapshot_dir
static char * snapshot_last
static char * snapshot_cur
static int index_dirty
static int snapshots_enabled

Class Documentation

struct snapshot_list

Definition at line 98 of file smapsnapshot.c.

Collaboration diagram for snapshot_list:
Class Members
char * filename
time_t mtime
struct snapshot_list * next
char * prev

Define Documentation

#define dirent   direct

Definition at line 56 of file smapsnapshot.c.

#define NAMLEN (   dirent)    (dirent)->d_namlen

Definition at line 57 of file smapsnapshot.c.


Function Documentation

static void delete_snapshot ( struct snapshot_list snn) [static]

Definition at line 140 of file smapsnapshot.c.

{
       char *p=malloc(strlen(snapshot_dir)+strlen(snn->filename)+2);

       if (p)
       {
              strcat(strcat(strcpy(p, snapshot_dir), "/"), snn->filename);
              unlink(p);
       }

       free(snn->filename);
       free(snn->prev);
       free(snn);
}

Here is the caller graph for this function:

static struct snapshot_list* find_next_snapshot ( struct snapshot_list s,
const char *  n 
) [static, read]

Definition at line 113 of file smapsnapshot.c.

{
       const char *p, *q;

       p=strrchr(n, '/');

       if (p)
              n=p+1;

       while (s)
       {
              p=s->prev;
              q=p ? strrchr(p, '/'):NULL;

              if (q && strcmp(q+1, n) == 0)
                     return s;

              s=s->next;
       }
       return NULL;
}

Here is the caller graph for this function:

int keywords ( )

Definition at line 51 of file capability.c.

{
       return capa_keywords != 0;
}

Here is the caller graph for this function:

char* readline ( unsigned  i,
FILE *   
)

Definition at line 325 of file imapscanclient.c.

{
int    c;

       for (;;)
       {
              if (i >= 10000)
                     --i;   /* DOS check */

              if (i >= readbufsize)
              {
              char   *p= readbuf ? realloc(readbuf, readbufsize+256):
                                   malloc(readbufsize+256);

                     if (!p)       write_error_exit(0);
                     readbuf=p;
                     readbufsize += 256;
              }

              c=getc(fp);
              if (c == EOF || c == '\n')
              {
                     readbuf[i]=0;
                     return (c == EOF ? 0:readbuf);
              }
              readbuf[i++]=c;
       }
}

Here is the call graph for this function:

static int restore_snapshot ( const char *  dir,
FILE *  snapshot_fp,
char **  last_snapshot 
) [static]

Definition at line 168 of file smapsnapshot.c.

{
       int format;
       unsigned long s_nmessages, s_uidv, s_nextuid;
       char *p;
       char *buf;
       struct imapscaninfo new_index;

       if ((buf=readline(0, snapshot_fp)) == NULL)
              return 0;

       p=strchr(buf, ':');
       if (p)
              *p++=0;

       *last_snapshot=NULL;

       if (sscanf(buf, "%d %lu %lu %lu", &format, &s_nmessages, &s_uidv,
                 &s_nextuid) != 4 || format != SNAPSHOTVERSION)
              return 0; /* Don't recognize the header */

       /* Save the previous snapshot ID */

       if (p)
       {
              *last_snapshot=malloc(strlen(dir)+strlen(p)+2);

              if (!last_snapshot)
              {
                     write_error_exit(0);
                     return 0;
              }

              strcat(strcat(strcpy(*last_snapshot, dir), "/"), p);
       }

       imapscan_init(&new_index);

       if (s_nmessages && (new_index.msgs=(struct imapscanmessageinfo *)
                         malloc(s_nmessages * sizeof(*new_index.msgs)))
           == 0)
       {
              write_error_exit(0);
              return (0);
       }
       memset(new_index.msgs, 0, s_nmessages * sizeof(*new_index.msgs));

       new_index.nmessages=s_nmessages;
       new_index.uidv=s_uidv;
       new_index.nextuid=s_nextuid;

       if (restore_snapshot2(dir, snapshot_fp, &new_index))
       {
              imapscan_copy(&current_maildir_info, &new_index);
              imapscan_free(&new_index);
              return 1;
       }
       imapscan_free(&new_index);

       if (*last_snapshot)
       {
              free(*last_snapshot);
              *last_snapshot=0;
       }

       return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int restore_snapshot2 ( const char *  snapshot_dir,
FILE *  fp,
struct imapscaninfo new_index 
) [static]

Definition at line 242 of file smapsnapshot.c.

{
       unsigned long i;
       char *p=malloc(strlen(snapshot_dir) + sizeof("/../" IMAPDB));
       FILE *courierimapuiddb;
       int version;
       unsigned long uidv;
       unsigned long nextuid;
       char *uid_line;
       unsigned long uid=0;

       if (!p)
       {
              write_error_exit(0);
              return 0;
       }

       strcat(strcpy(p, snapshot_dir), "/../" IMAPDB);

       courierimapuiddb=fopen(p, "r");
       free(p);

       if (!courierimapuiddb)
              return 0; /* Can't open the uiddb file, no dice */

       if ((p=readline(0, courierimapuiddb)) == NULL ||
           sscanf(p, "%d %lu %lu", &version, &uidv, &nextuid) != 3 ||
           version != IMAPDBVERSION /* Do not recognize the uiddb file */

           || uidv != new_index->uidv /* Something major happened, abort */ )
       {
              fclose(courierimapuiddb);
              return 0;
       }

       uid_line=readline(0, courierimapuiddb);

       if (uid_line)
       {
              if (sscanf(uid_line, "%lu", &uid) != 1)
              {
                     fclose(courierimapuiddb);
                     return 0;
              }
       }

       /*
       ** Both the snapshot file and courierimapuiddb should be in sorted
       ** order, by UIDs, rely on that and do what amounts to a merge sort.
       */

       for (i=0; i<new_index->nmessages; i++)
       {
              unsigned long s_uid;
              char flag_buf[128];

              p=fgets(flag_buf, sizeof(flag_buf)-1, fp);

              if (p == NULL || (p=strchr(p, '\n')) == NULL ||
                  (*p = 0, sscanf(flag_buf, "%lu", &s_uid)) != 1 ||
                  (p=strchr(flag_buf, ':')) == NULL) /* Corrupted file */
              {
                     fclose(courierimapuiddb);
                     return 0;
              }

              new_index->msgs[i].uid=s_uid;

              /* Try to fill in the filenames to as much of an extent as
              ** possible.  If IMAPDB no longer has a particular uid listed,
              ** that's ok, because the message is now gone, so we just
              ** insert an empty filename, which will be expunged by
              ** noop() processing, after the snapshot is restored.
              */

              while (uid_line && uid <= s_uid)
              {
                     if (uid == s_uid &&
                         (uid_line=strchr(uid_line, ' ')) != NULL)
                            /* Jackpot */
                     {
                            new_index->msgs[i].filename=
                                   malloc(strlen(uid_line)+
                                          strlen(flag_buf)+2);

                            if (!new_index->msgs[i].filename)
                            {
                                   fclose(courierimapuiddb);
                                   write_error_exit(0);
                                   return 0;
                            }

                            strcpy(new_index->msgs[i].filename,
                                   uid_line+1);

                            if (p)
                            {
                                   strcat(strcat(new_index->msgs[i]
                                                .filename, MDIRSEP),
                                          p+1);
                            }
                     }

                     uid_line=readline(0, courierimapuiddb);

                     if (uid_line)
                     {
                            if (sscanf(uid_line, "%lu", &uid) != 1)
                            {
                                   fclose(courierimapuiddb);
                                   return 0;
                            }
                     }
              }


              if (new_index->msgs[i].filename == 0)
              {
                     new_index->msgs[i].filename=strdup("");
                     /* A noop should get rid of this entry anyway */

                     if (!new_index->msgs[i].filename)
                     {
                            fclose(courierimapuiddb);
                            write_error_exit(0);
                            return 0;
                     }
              }
       }

       fclose(courierimapuiddb);
       if (keywords())
              imapscan_restoreKeywordSnapshot(fp, new_index);
       return 1;
}

Here is the call graph for this function:

Here is the caller graph for this function:

void set_time ( const char *  tmpname,
time_t  timestamp 
)

Definition at line 652 of file imapd.c.

{
#if    HAVE_UTIME
       if (timestamp)
       {
       struct utimbuf ub;

              ub.actime=ub.modtime=timestamp;
              utime(tmpname, &ub);
       }
#else
#if    HAVE_UTIMES
       if (timestamp)
       {
       struct timeval       tv;

              tv.tv_sec=timestamp;
              tv.tv_usec=0;
              utimes(tmpname, &tv);
       }
#endif
#endif
}

Here is the caller graph for this function:

void smapword ( const char *  )

Definition at line 211 of file smap.c.

{
       writes("\"");
       smapword_s(w);
       writes("\"");
}

Here is the call graph for this function:

Here is the caller graph for this function:

int snapshot_init ( const char *  folder,
const char *  snapshot 
)

Definition at line 405 of file smapsnapshot.c.

{
       struct snapshot_list *sl=NULL;
       DIR *dirp;
       struct dirent *de;
       struct snapshot_list *snn, **ptr;
       int cnt;
       char *new_dir;
       int rc=0;
       char *new_snapshot_cur=NULL;
       char *new_snapshot_last=NULL;

       if ((new_dir=malloc(strlen(folder)+sizeof("/" SNAPSHOTDIR)))
           == NULL)
       {
              write_error_exit(0);
              return rc;
       }

       strcat(strcpy(new_dir, folder), "/" SNAPSHOTDIR);
       mkdir(new_dir, 0755); /* Create, if doesn't exist */

       if (snapshot)
       {
              FILE *fp;

              if (*snapshot == 0 || strchr(snapshot, '/') ||
                  *snapshot == '.') /* Monkey business */
              {
                     free(new_dir);
                     return 0;
              }

              new_snapshot_cur=malloc(strlen(new_dir) +
                                   strlen(snapshot) + 2);

              if (!new_snapshot_cur)
              {
                     free(new_dir);
                     write_error_exit(0);
                     return rc;
              }

              strcat(strcat(strcpy(new_snapshot_cur, new_dir), "/"),
                     snapshot);

              if ((fp=fopen(new_snapshot_cur, "r")) != NULL &&
                  restore_snapshot(new_dir, fp, &new_snapshot_last))
              {
                     set_time(new_snapshot_cur, time(NULL));
                     rc=1; /* We're good to go.  Finish everything else */
              }

              if (fp)
              {
                     fclose(fp);
                     fp=NULL;
              }

              if (!rc) /* Couldn't get the snapshot, abort */
              {
                     free(new_snapshot_cur);
                     free(new_dir);
                     return 0;
              }
       }

       if (snapshot_dir) free(snapshot_dir);
       if (snapshot_last) free(snapshot_last);
       if (snapshot_cur) free(snapshot_cur);

       snapshot_dir=NULL;
       snapshot_last=new_snapshot_last;
       snapshot_cur=new_snapshot_cur;

       snapshot_dir=new_dir;

       index_dirty=1;

       /* Get rid of old snapshots as follows */

       /* Step 1, compile a list of snapshots, sorted in mtime order */

       dirp=opendir(snapshot_dir);

       while (dirp && (de=readdir(dirp)) != NULL)
       {
              FILE *fp;
              struct stat stat_buf;

              char *n;

              if (de->d_name[0] == '.') continue;

              n=malloc(strlen(snapshot_dir)+strlen(de->d_name)+2);
              if (!n) break; /* Furrfu */

              strcat(strcat(strcpy(n, snapshot_dir), "/"), de->d_name);

              fp=fopen(n, "r");

              if (fp)
              {
                     char buf[1024];

                     if (fgets(buf, sizeof(buf)-1, fp) != NULL &&
                         fstat(fileno(fp), &stat_buf) == 0)
                     {
                            char *p=strchr(buf, '\n');
                            int fmt;

                            if (p) *p=0;

                            p=strchr(buf, ':');

                            if (p)
                                   *p++=0;


                            if (sscanf(buf, "%d", &fmt) == 1 &&
                                fmt == SNAPSHOTVERSION)
                            {
                                   snn=malloc(sizeof(*sl));

                                   if (snn) memset(snn, 0, sizeof(*snn));

                                   if (snn == NULL ||
                                       (snn->filename=strdup(de->d_name))
                                       == NULL ||
                                       (snn->prev=strdup(p ? p:""))
                                       == NULL)
                                   {
                                          if (snn && snn->filename)
                                                 free(snn->filename);
                                          if (snn)
                                                 free(snn);

                                          snn=NULL;
                                   }

                                   if (snn)
                                   {
                                          snn->mtime=stat_buf.st_mtime;

                                          for (ptr= &sl; *ptr;
                                               ptr=&(*ptr)->next)
                                          {
                                                 if ( (*ptr)->mtime >
                                                      snn->mtime)
                                                        break;
                                          }

                                          snn->next= *ptr;
                                          *ptr=snn;
                                   }
                                   free(n);
                                   n=NULL;
                            }

                     }
                     fclose(fp);
              }
              if (n)
              {
                     unlink(n);
                     free(n);
              }
       }
       if (dirp)
              closedir(dirp);

       /* Step 2: drop snapshots that are definitely obsolete */

       for (ptr= &sl; *ptr; )
       {
              if ((snn=find_next_snapshot(sl, (*ptr)->filename)) &&
                  find_next_snapshot(sl, snn->filename))
              {
                     snn= *ptr;

                     *ptr=snn->next;

                     delete_snapshot(snn);
              }
              else
                     ptr=&(*ptr)->next;

       }

       /* If there are more than 10 snapshots, drop older snapshots */

       cnt=0;
       for (snn=sl; snn; snn=snn->next)
              ++cnt;

       if (cnt > 10)
       {
              time_t now=time(NULL);

              while (sl && sl->mtime < now &&
                     (now - sl->mtime) > 60 * 60 * 24 * (7 + (cnt-10)*2))
              {
                     snn=sl;
                     sl=sl->next;
                     delete_snapshot(snn);
                     --cnt;
              }
       }

       /* All right, put a lid on 50 snapshots */

       while (cnt > 50)
       {
              snn=sl;
              sl=sl->next;
              delete_snapshot(snn);
              --cnt;
       }

       return rc;
}

Here is the call graph for this function:

Here is the caller graph for this function:

void snapshot_needed ( )

Definition at line 632 of file smapsnapshot.c.

{
       index_dirty=1;
}

Here is the caller graph for this function:

void snapshot_save ( )

Definition at line 641 of file smapsnapshot.c.

{
       int rc;
       struct maildir_tmpcreate_info createInfo;
       FILE *fp;
       unsigned long i;
       const char *q;

       if (!index_dirty || !snapshots_enabled)
              return;

       index_dirty=0;

       maildir_tmpcreate_init(&createInfo);

       createInfo.maildir=current_mailbox;
       createInfo.uniq="snapshot";
       createInfo.hostname=getenv("HOSTNAME");
       createInfo.doordie=1;

       if ((rc=maildir_tmpcreate_fd(&createInfo)) < 0)
       {
              perror("maildir_tmpcreate_fd");
              return;
       }
       close(rc);

       q=strrchr(createInfo.tmpname, '/'); /* Always there */

       free(createInfo.newname);
       createInfo.newname=malloc(strlen(snapshot_dir)+strlen(q)+2);

       if (!createInfo.newname)
       {
              unlink(createInfo.tmpname);
              maildir_tmpcreate_free(&createInfo);
              perror("malloc");
              return;
       }

       strcat(strcat(strcpy(createInfo.newname, snapshot_dir), "/"), q);

       if ((fp=fopen(createInfo.tmpname, "w")) == NULL)
       {
              perror(createInfo.tmpname);
              maildir_tmpcreate_free(&createInfo);
              return;
       }

       fprintf(fp, "%d %lu %lu %lu", SNAPSHOTVERSION,
              current_maildir_info.nmessages,
              current_maildir_info.uidv,
              current_maildir_info.nextuid);
       if (snapshot_cur)
              fprintf(fp, ":%s", strrchr(snapshot_cur, '/')+1);
       fprintf(fp, "\n");

       for (i=0; i<current_maildir_info.nmessages; i++)
       {
              struct imapscanmessageinfo *p=current_maildir_info.msgs + i;
              q=strrchr(p->filename, MDIRSEP[0]);

              fprintf(fp, "%lu:%s\n", p->uid, q ? q+1:"");
       }

       if (keywords())
              imapscan_saveKeywordSnapshot(fp, &current_maildir_info);

       if (fflush(fp) < 0 || ferror(fp) < 0)
       {
              fclose(fp);
              perror(createInfo.tmpname);
              unlink(createInfo.tmpname);
              maildir_tmpcreate_free(&createInfo);
              return;
       }
       fclose(fp);
       if (rename(createInfo.tmpname, createInfo.newname) < 0)
       {
              perror(createInfo.tmpname);
              unlink(createInfo.tmpname);
              maildir_tmpcreate_free(&createInfo);
              return;
       }
       if (snapshot_last)
       {
              unlink(snapshot_last); /* Obsolete snapshot */
              free(snapshot_last);
       }

       snapshot_last=snapshot_cur;
       snapshot_cur=createInfo.newname;
       createInfo.newname=NULL;
       maildir_tmpcreate_free(&createInfo);

       writes("* SNAPSHOT ");
       smapword(strrchr(snapshot_cur, '/')+1);
       writes("\n");
}

Here is the call graph for this function:

Here is the caller graph for this function:

void snapshot_select ( int  flag)

Definition at line 380 of file smapsnapshot.c.

Here is the caller graph for this function:


Variable Documentation

Definition at line 124 of file imapd.c.

int index_dirty [static]

Definition at line 92 of file smapsnapshot.c.

char* snapshot_cur [static]

Definition at line 90 of file smapsnapshot.c.

char* snapshot_dir [static]

Definition at line 87 of file smapsnapshot.c.

char* snapshot_last [static]

Definition at line 89 of file smapsnapshot.c.

int snapshots_enabled [static]

Definition at line 93 of file smapsnapshot.c.