Initial import
[maildir-archive.git] / maildir-archive.c
1 #include <stdlib.h>
2 #include <sys/stat.h>
3 #include <sys/types.h>
4 #include <fcntl.h>
5 #include <dirent.h>
6 #include <stdio.h>
7 #include <time.h>
8 #include <string.h>
9 #include <errno.h>
10
11 #define ARCHIVE_PREFIX "Archive-"
12
13 char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
14 char fromfolder[512];
15 char fromdirectory[512];
16 int skipped = 0;
17
18 struct folder_list_t {
19   char *name;
20   int emails;
21   struct folder_list_t *next;
22 };
23
24 struct folder_list_t *folderhead;
25
26 void move_email(const char *name, const char *folder)
27 {
28   struct folder_list_t *lp;
29   int foundfolder = 0;
30   char oldfilename[512];
31   char filename[512];
32   
33   for (lp = folderhead; lp->next; lp = lp->next)
34     if (!strcmp(folder, lp->name))
35       break;
36   
37   if (!lp->next)
38   {
39     int fd;
40     
41     lp = malloc(sizeof(*folderhead));
42     lp->next = folderhead;
43     lp->emails = 0;
44     lp->name = strdup(folder);
45     folderhead = lp;
46     
47     /* create the folder */
48     sprintf(filename, ".INBOX.%s", folder);
49     mkdir(filename, 0700);
50     
51     sprintf(filename, ".INBOX.%s/cur", folder);
52     if (mkdir(filename, 0700) && (errno != EEXIST))
53     {
54       perror("mkdir(cur)");
55       exit(1);
56     } 
57
58     sprintf(filename, ".INBOX.%s/new", folder);
59     if (mkdir(filename, 0700) && (errno != EEXIST))
60     {
61       perror("mkdir(new)");
62       exit(1);
63     } 
64
65     sprintf(filename, ".INBOX.%s/tmp", folder);
66     if (mkdir(filename, 0700) && (errno != EEXIST))
67     {
68       perror("mkdir(tmp)");
69       exit(1);
70     } 
71     
72     sprintf(filename, ".INBOX.%s/maildirfolder", folder);
73     if ((fd = open(filename, O_CREAT | O_WRONLY, 0700)) < 0)
74     {
75       perror("open(maildirfolder)");
76       exit(1);
77     }
78     close(fd);
79   }
80   
81   lp->emails++;
82   
83   sprintf(oldfilename, "%s/%s", fromdirectory, name);
84   sprintf(filename, ".INBOX.%s/cur/%s", folder, name);
85   
86   /* skipped if it's the same thing, since the link won't catch it, and the unlink will kill it */
87   if (!strcmp(oldfilename, filename))
88   {
89     skipped++;
90     return;
91   }
92
93   if (link(oldfilename, filename) && (errno != EEXIST))
94   {
95     char theerror[1024];
96     sprintf(theerror, "link(%s, %s)", oldfilename, filename);
97     perror(theerror);
98     exit(1);
99   }
100   
101   if (unlink(oldfilename))
102   {
103     char theerror[1024];
104     sprintf(theerror, "unlink(%s)", oldfilename);
105     perror(theerror);
106     exit(1);
107   }
108 }
109
110 int spinnerstate = 0;
111 char spinner[] = "/-\\|";
112 #define SPINNERSTATES 4
113 #define MAXAGE 7*24*60*60
114
115 void main(int argc, char **argv)
116 {
117   DIR *cur;
118   struct dirent *de;
119   off_t dirstart;
120   int emails = 0, notemails = 0, toonew = 0, starttime = time(NULL);
121   
122   if (argc == 2)
123   {
124     strcpy(fromfolder, argv[1]);
125     sprintf(fromdirectory, ".INBOX.%s/cur", fromfolder);
126   } else if (argc == 1) {
127     strcpy(fromdirectory, "cur");
128     strcpy(fromfolder, "");
129   } else {
130     printf("maildir-archiver: Usage: %s [<folder name>]\n", argv[0]);
131     exit(1);
132   }
133   
134   folderhead = malloc(sizeof(*folderhead));
135   folderhead->next = NULL;
136   
137   printf("maildir-archiver: Opening %s/... ", fromdirectory);
138   fflush(stdout);
139   cur = opendir(fromdirectory);
140   if (!cur)
141   {
142     perror("opendir(cur)");
143     exit(1);
144   }
145   dirstart = telldir(cur);
146   printf("done\n");
147   
148   printf("maildir-archiver: Doing preliminary scan of e-mails... .");
149   fflush(stdout);
150   while ((de = readdir(cur)))
151   {
152     time_t tt;
153     struct tm *tm;
154     char archivefolder[128];
155     
156     tt = atoi(de->d_name);
157     if (tt == 0)
158     {
159       notemails++;
160       continue;
161     }
162     if ((starttime - tt) < MAXAGE)
163     {
164       toonew++;
165       continue;
166     }
167     emails++;
168     if ((emails % 10000) == 0)
169     {
170       printf("\b%c", spinner[(spinnerstate++) % SPINNERSTATES]);
171       fflush(stdout);
172     }
173   }
174   printf("\bdone (%d mails to archive, %d too new, %d invalid)\n", emails, toonew, notemails);
175   seekdir(cur, dirstart);
176   
177   printf("maildir-archiver: Archiving e-mail... [>                               ]");
178   fflush(stdout);
179   while ((de = readdir(cur)))
180   {
181     static int progresscounter = 0;
182     static int archived = 0;
183     time_t tt;
184     struct tm *tm;
185     char archivefolder[128];
186     int islist = 0;
187     char fn[256];
188     FILE *fp;
189     
190     tt = atoi(de->d_name);
191     if (tt == 0)
192       continue;
193     if ((starttime - tt) < MAXAGE)
194       continue;
195     
196     tm = localtime(&tt);
197     
198     /* Open the file to determine if it's a mailing list or what */
199     sprintf(fn, "%s/%s", fromdirectory, de->d_name);
200     fp = fopen(fn, "r");
201     if (!fp)
202     {
203       perror("fopen(fn, r)");
204       exit(1);
205     }
206     {
207       char line[512];
208       while (fgets(line, 512, fp))
209       {
210         if (!strncmp(line, "X-BeenThere: ", 13))
211           islist++;
212         if (!strncmp(line, "List-Unsubscribe: ", 18))
213           islist++;
214         if (!strncmp(line, "X-Mailing-List", 14))
215           islist++;
216         if (!strncmp(line, "Precedence: bulk", 16))
217           islist++;
218         
219         if (!line[0])
220           break;
221       }
222     }
223     fclose(fp);
224     
225     /* sprintf(archivefolder, ARCHIVE_PREFIX "%d-%02d", 1900 + tm->tm_year, tm->tm_mon + 1); */
226     sprintf(archivefolder, ARCHIVE_PREFIX "%d-Q%d-%s", 1900 + tm->tm_year, (tm->tm_mon) / 3 + 1, islist ? "List" : "Personal");
227     move_email(de->d_name, archivefolder);
228     archived++;
229     
230     if (((archived % 10) == 0) || (archived == emails))
231     {
232       int i;
233       progresscounter = archived * 32 / emails;
234       printf("\rmaildir-archiver: Archiving e-mail... [");
235       for (i=0; i < progresscounter; i++)
236         printf("=");
237       if (progresscounter != 32)
238         printf(">");
239       for (i=0; i < (31 - progresscounter); i++)
240         printf(" ");
241       printf("] ");
242       printf("%c ", spinner[(spinnerstate++) % SPINNERSTATES]);
243       printf("%d/%d", archived, emails);
244       fflush(stdout);
245     }
246   }
247   printf("\n");
248   printf("maildir-archiver: Statistics:\n");
249   printf("maildir-archiver: All operations performed in %d seconds\n", time(NULL)-starttime);
250   printf("maildir-archiver:\n");
251   printf("maildir-archiver: -------------------------------+----------\n");
252   printf("maildir-archiver: %-30s | Emails\n", "Status");
253   printf("maildir-archiver: -------------------------------+----------\n");
254   printf("maildir-archived: %-30s | %6d\n", "Processed", emails);
255   printf("maildir-archived: %-30s | %6d\n", "Too new", toonew);
256   printf("maildir-archived: %-30s | %6d\n", "Not an e-mail", notemails);
257   printf("maildir-archived: %-30s | %6d\n", "Already in correct folder", skipped);
258   printf("maildir-archiver: -------------------------------+----------\n");
259   printf("maildir-archiver:\n");
260   printf("maildir-archiver: -------------------------------+----------\n");
261   printf("maildir-archiver: %-30s | Emails\n", "Folder");
262   printf("maildir-archiver: -------------------------------+----------\n");
263   {
264     struct folder_list_t *lp;
265     int total = 0;
266     
267     for (lp = folderhead; lp->next; lp = lp->next)
268     {
269       printf("maildir-archiver: %-30s | %6d\n", lp->name, lp->emails);
270       total += lp->emails;
271     }
272     printf("maildir-archiver: %-30s | %6d\n", "Total", total);
273   }
274   printf("maildir-archiver: -------------------------------+----------\n");
275 }
This page took 0.147279 seconds and 4 git commands to generate.