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, &quotainfo, 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(&quotainfo, 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