Initial import
[maildir-archive.git] / maildir-archive.c
CommitLineData
38b6fc57
JW
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
13char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
14char fromfolder[512];
15char fromdirectory[512];
16int skipped = 0;
17
18struct folder_list_t {
19 char *name;
20 int emails;
21 struct folder_list_t *next;
22};
23
24struct folder_list_t *folderhead;
25
26void 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
110int spinnerstate = 0;
111char spinner[] = "/-\\|";
112#define SPINNERSTATES 4
113#define MAXAGE 7*24*60*60
114
115void 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.040707 seconds and 4 git commands to generate.