1 /*
2 ** Copyright 1998 - 2009 Double Precision, Inc.
3 ** See COPYING for distribution information.
4 */
5
6 #include "config.h"
7 #include "maildir.h"
8 #include "mio.h"
9 #include "alarmtimer.h"
10 #include "alarmsleep.h"
11 #include "config.h"
12 #include "xconfig.h"
13 #include "funcs.h"
14 #include "varlist.h"
15 #if HAVE_SYS_STAT_H
16 #include <sys/stat.h>
17 #endif
18 #include <errno.h>
19 #include <stdlib.h>
20 #include "mytime.h"
21
22 #if HAVE_UNISTD_H
23 #include <unistd.h>
24 #endif
25 #include "../maildir/maildirquota.h"
26 #include "../maildir/maildircreate.h"
27 #include "../maildir/maildirmisc.h"
28 #include "../maildir/maildirkeywords.h"
29
30 static const char rcsid[]="$Id: maildir.C,v 1.22 2009/09/05 23:12:45 mrsam Exp $";
31
32 #include "../maildir/maildirquota.h"
33 extern int quota_warn_percent;
34 extern const char *quota_warn_message;
35
Maildir()36 Maildir::Maildir() : is_open(0) , is_afs(0)
37 {
38 }
39
~Maildir()40 Maildir::~Maildir()
41 {
42 MaildirAbort();
43 }
44
45 ////////////////////////////////////////////////////////////////////////////
46 //
47 // Attempt to detect if this is a Maildir directory.
48 //
49 ////////////////////////////////////////////////////////////////////////////
50
IsMaildir(const char * name)51 int Maildir::IsMaildir(const char *name)
52 {
53 Buffer dirname;
54 Buffer subdirname;
55 struct stat stat_buf;
56
57 int c;
58
59 if (!name || !*name) return (0); // Nope, not a Maildir
60 dirname=name;
61 c=dirname.pop();
62 if (c != SLASH_CHAR) dirname.push(c); // Strip trailing /
63 subdirname=dirname;
64 subdirname += "/tmp";
65 subdirname += '\0';
66 if ( stat( (const char *)subdirname, &stat_buf ) ||
67 ! S_ISDIR(stat_buf.st_mode) ) return (0);
68 subdirname=dirname;
69 subdirname += "/new";
70 subdirname += '\0';
71 if ( stat( (const char *)subdirname, &stat_buf ) ||
72 ! S_ISDIR(stat_buf.st_mode) ) return (0);
73 subdirname=dirname;
74 subdirname += "/cur";
75 subdirname += '\0';
76 if ( stat( (const char *)subdirname, &stat_buf ) ||
77 ! S_ISDIR(stat_buf.st_mode) ) return (0);
78 return (1); // If it looks like a duck, walks like a duck...
79 }
80
MaildirOpen(const char * dir,Mio & file,off_t s)81 int Maildir::MaildirOpen(const char *dir, Mio &file, off_t s)
82 {
83 Buffer buf;
84 struct maildirsize quotainfo;
85
86 const char *quotap;
87
88 Buffer quotabuf;
89
90 quotabuf="MAILDIRQUOTA"; /* Reuse a convenient buffer */
91 quotabuf= *GetVar(quotabuf);
92 quotabuf += '\0';
93
94 quotap=quotabuf;
95
96 if (!*quotap)
97 quotap=NULL;
98
99 MaildirAbort();
100
101 AlarmTimer abort_timer;
102 static long counter=0;
103
104 buf.set(counter++);
105 buf += '\0';
106
107 struct maildir_tmpcreate_info createInfo;
108
109 maildir_tmpcreate_init(&createInfo);
110
111 createInfo.maildir=dir;
112 createInfo.uniq=(const char *)buf;
113 createInfo.msgsize=s;
114 createInfo.openmode=0666;
115
116 abort_timer.Set( 24 * 60 * 60 );
117 while (!abort_timer.Expired())
118 {
119 Buffer name_buf;
120
121 name_buf="UMASK";
122 const char *um=GetVarStr(name_buf);
123 unsigned int umask_val=077;
124
125 sscanf(um, "%o", &umask_val);
126
127 umask_val=umask(umask_val);
128
129 int f=maildir_tmpcreate_fd(&createInfo);
130 umask(umask_val);
131
132 if (f >= 0)
133 {
134 tmpname=createInfo.tmpname;
135 newname=createInfo.newname;
136 tmpname += '\0';
137 newname += '\0';
138 maildir_tmpcreate_free(&createInfo);
139
140 file.fd(f);
141 is_open=1;
142 maildirRoot=dir;
143 maildirRoot += '\0';
144
145 if (maildir_quota_add_start(dir, "ainfo, s,
146 1, quotap))
147 {
148 file.fd(-1);
149 unlink( (const char *)tmpname );
150 is_open=0;
151 maildir_deliver_quota_warning(dir,
152 quota_warn_percent,
153 quota_warn_message);
154 merr << "maildrop: maildir over quota.\n";
155 return (-1);
156 }
157
158 maildir_quota_add_end("ainfo, s, 1);
159 return (0);
160 }
161
162 if (errno != EAGAIN)
163 {
164 merr << "maildrop: " << dir << ": " << strerror(errno)
165 << "\n";
166 return -1;
167 }
168
169 AlarmSleep try_again(2);
170 }
171
172 merr << "maildrop: time out on maildir directory.\n";
173 return (-1);
174 }
175
MaildirSave()176 void Maildir::MaildirSave()
177 {
178 if (is_open)
179 {
180 Buffer keywords;
181
182 keywords="KEYWORDS";
183 keywords=*GetVar(keywords);
184 keywords += '\0';
185
186 const char *keywords_s=keywords;
187
188 while (*keywords_s && *keywords_s == ',')
189 ++keywords_s;
190
191 if (*keywords_s)
192 {
193 struct libmail_kwHashtable kwh;
194 struct libmail_kwMessage *kwm;
195
196 libmail_kwhInit(&kwh);
197
198 if ((kwm=libmail_kwmCreate()) == NULL)
199 throw strerror(errno);
200
201 while (*keywords_s)
202 {
203 const char *p=keywords_s;
204
205 while (*keywords_s && *keywords_s != ',')
206 ++keywords_s;
207
208 char *n=new char [keywords_s - p + 1];
209
210 if (!n)
211 {
212 libmail_kwmDestroy(kwm);
213 throw strerror(errno);
214 }
215
216 memcpy(n, p, keywords_s - p);
217 n[keywords_s - p]=0;
218
219 while (*keywords_s && *keywords_s == ',')
220 ++keywords_s;
221
222 if (libmail_kwmSetName(&kwh, kwm, n) < 0)
223 {
224 delete n;
225 libmail_kwmDestroy(kwm);
226 throw strerror(errno);
227 }
228 delete n;
229 }
230
231 char *tmpkname, *newkname;
232
233 if (maildir_kwSave( (const char *)maildirRoot,
234 strrchr(newname, '/')+1, kwm,
235 &tmpkname, &newkname, 0) < 0)
236 {
237 libmail_kwmDestroy(kwm);
238 throw "maildir_kwSave() failed.";
239 }
240
241 libmail_kwmDestroy(kwm);
242
243 if (rename(tmpkname, newkname) < 0)
244 {
245 /* Maybe the keyword directory needs creating */
246
247 struct stat stat_buf;
248
249 if (stat(maildirRoot, &stat_buf) < 0)
250 {
251 free(tmpkname);
252 free(newkname);
253 throw strerror(errno);
254 }
255
256 char *keywordDir=strrchr(newkname, '/');
257
258 *keywordDir=0;
259 mkdir(newkname, 0700);
260 chmod(newkname, stat_buf.st_mode & 0777);
261 *keywordDir='/';
262
263 if (rename(tmpkname, newkname) < 0)
264 {
265 free(tmpkname);
266 free(newkname);
267 throw strerror(errno);
268 }
269 }
270 free(tmpkname);
271 free(newkname);
272 }
273
274 Buffer dir;
275
276 if (link( (const char *)tmpname, (const char *)newname) < 0)
277 {
278 if (errno == EXDEV){
279 if(rename((const char *)tmpname, (const char *)newname) < 0)
280 throw "rename() failed.";
281 is_afs = 1;
282 }
283 else
284 {
285 throw "link() failed.";
286 }
287 }
288 dir=newname;
289 const char *p=dir;
290 const char *q=strrchr(p, '/');
291
292 if (q)
293 {
294 dir.Length(q-p);
295 dir.push((char)0);
296
297 #if EXPLICITDIRSYNC
298 int syncfd=open(dir, O_RDONLY);
299
300 if (syncfd >= 0)
301 {
302 fsync(syncfd);
303 close(syncfd);
304 }
305 #endif
306
307 dir.Length(q-p);
308 dir += "/../";
309 dir.push((char)0);
310
311 maildir_deliver_quota_warning(dir, quota_warn_percent,
312 quota_warn_message);
313 }
314 }
315 }
316
MaildirAbort()317 void Maildir::MaildirAbort()
318 {
319 if (is_open && !is_afs) unlink( (const char *)tmpname );
320 }
321