1 /*
2 ** Copyright 2002-2006, Double Precision Inc.
3 **
4 ** See COPYING for distribution information.
5 */
6 #include "libmail_config.h"
7 #include "mboxadd.H"
8 #include "mboxsighandler.H"
9 #include "file.H"
10 
11 #include <errno.h>
12 #include <pwd.h>
13 #include <time.h>
14 
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 
18 using namespace std;
19 
add(mail::mbox & mboxArg,string pathArg,mail::callback & callbackArg)20 mail::mbox::folder::add::add(mail::mbox &mboxArg, string pathArg,
21 					   mail::callback &callbackArg)
22 	: mail::addMessage(&mboxArg), path(pathArg),
23 	  callback(callbackArg),
24 	  fp(tmpfile()),
25 	  mboxAccount(&mboxArg)
26 {
27 }
28 
~add()29 mail::mbox::folder::add::~add()
30 {
31 	if (fp)
32 		fclose(fp);
33 }
34 
saveMessageContents(string msg)35 void mail::mbox::folder::add::saveMessageContents(string msg)
36 {
37 	if (msg.empty())
38 		return;
39 
40 	if (fp)
41 		if (fwrite(&msg[0], msg.size(), 1, fp) != 1)
42 			; // Ignore gcc warning
43 }
44 
fail(string msg)45 void mail::mbox::folder::add::fail(string msg)
46 {
47 	callback.fail(msg);
48 	delete this;
49 }
50 
success(string msg)51 void mail::mbox::folder::add::success(string msg)
52 {
53 	callback.success(msg);
54 	delete this;
55 }
56 
reportProgress(size_t bytesCompleted,size_t bytesEstimatedTotal,size_t messagesCompleted,size_t messagesEstimatedTotal)57 void mail::mbox::folder::add::reportProgress(size_t bytesCompleted,
58 					     size_t bytesEstimatedTotal,
59 
60 					     size_t messagesCompleted,
61 					     size_t messagesEstimatedTotal)
62 {
63 	callback.reportProgress(bytesCompleted, bytesEstimatedTotal,
64 				messagesCompleted, messagesEstimatedTotal);
65 }
66 
go()67 void mail::mbox::folder::add::go()
68 {
69 	if (!fp)
70 		fail(strerror(errno));
71 
72 	if (mboxAccount.isDestroyed())
73 	{
74 		fail("Server connection aborted");
75 		return;
76 	}
77 
78 	try {
79 		if (path == mboxAccount->currentFolder)
80 		{
81 			mboxAccount->installTask(new LockCurrentFolder( *this ));
82 		}
83 		else
84 		{
85 			mboxAccount->installTask(new LockCurrentFolder( *this,
86 								 path));
87 		}
88 	} catch (...) {
89 		fail("An exception occured while adding message.");
90 		return;
91 	}
92 }
93 
94 //
95 // Folder's now locked.
96 //
97 
copyTo(mail::file & file)98 void mail::mbox::folder::add::copyTo(mail::file &file)
99 {
100 	struct stat st;
101 
102 	mail::mbox::sighandler updating(fileno(static_cast<FILE *>(file)));
103 
104 	try {
105 
106 		// Make sure the mboxAccount file ends with a trailing newline
107 
108 		if (fstat(fileno(static_cast<FILE *>(file)), &st) < 0)
109 		{
110 			fail(strerror(errno));
111 			return;
112 		}
113 
114 		int ch='\n';
115 
116 		if (st.st_size > 0)
117 		{
118 			if (fseek(file, -1, SEEK_END) < 0 ||
119 			    (ch=getc(file)) == EOF)
120 			{
121 				fail(strerror(errno));
122 				return;
123 			}
124 		}
125 
126 		if (fseek(file, 0L, SEEK_END) < 0 ||
127 		    fseek(fp, 0L, SEEK_SET) < 0)
128 		{
129 			fail(strerror(errno));
130 			return;
131 		}
132 
133 		if (ch != '\n')
134 			putc('\n', file);
135 
136 		mail::file f(fileno(fp), "w+");
137 
138 		messageInfo.uid= mail::mboxMagicTag()
139 			.getMessageInfo().uid;
140 
141 		string hdr=mail::mboxMagicTag(messageInfo.uid,
142 					      messageInfo,
143 
144 					      mail::keywords::Message()
145 					      // KEYWORDS
146 
147 					      ).toString();
148 
149 		struct passwd *pw;
150 		pw=getpwuid(getuid());
151 
152 		fprintf(file, "From %s %s%s\n",
153 			pw ? pw->pw_name:"nobody",
154 			ctime(&messageDate),
155 			hdr.c_str());
156 
157 		size_t bytesDone=0;
158 		size_t bytesNextReport=0;
159 
160 		while (!feof(f))
161 		{
162 			string l=f.getline();
163 
164 			if (l.size() == 0 && feof(f))
165 				break;
166 
167 			const char *p=l.c_str();
168 
169 			while (*p == '>')
170 				p++;
171 
172 			if (strncmp(p, "From ", 5) == 0)
173 				l=">" + l;
174 
175 			l += "\n";
176 
177 			if (fwrite(&l[0], l.size(), 1, file) != 1)
178 				; // Ignore gcc warning
179 
180 			bytesDone += l.size();
181 
182 			if (bytesDone >= bytesNextReport)
183 			{
184 				bytesNextReport=bytesDone + BUFSIZ;
185 				callback.reportProgress(bytesDone, 0, 0, 1);
186 			}
187 		}
188 
189 		if (fflush(file) < 0 || ferror(file))
190 		{
191 			updating.rollback();
192 			fail(strerror(errno));
193 			return;
194 		}
195 
196 		callback.reportProgress(bytesDone, bytesDone, 1, 1);
197 		updating.block();
198 	} catch (...) {
199 		updating.rollback();
200 		fail("An exception occured while adding message.");
201 		return;
202 	}
203 
204 	try {
205 		fclose(fp);
206 		fp=NULL;
207 		success("OK");
208 	} catch (...) {
209 		delete this;
210 		LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
211 	}
212 }
213 
214 ///////////////////////////////////////////////////////////////////////////
215 
216 mail::mbox::folder::add::LockCurrentFolder
LockCurrentFolder(add & meArg,string pathArg)217 ::LockCurrentFolder(add &meArg, string pathArg)
218 	: LockTask(*meArg.mboxAccount, meArg, pathArg), me(meArg)
219 {
220 }
221 
~LockCurrentFolder()222 mail::mbox::folder::add::LockCurrentFolder::~LockCurrentFolder()
223 {
224 }
225 
226 
locked(mail::file & lockedFile)227 bool mail::mbox::folder::add::LockCurrentFolder::locked(mail::file
228 							&lockedFile)
229 {
230 	// If the current folder is opened in read-only mode, this is a no-go.
231 
232 	if (me.path == me.mboxAccount->currentFolder && mboxAccount.currentFolderReadOnly)
233 	{
234 		me.fail("Folder opened in read-only mode.");
235 		done();
236 		return true;
237 	}
238 
239 	me.copyTo(lockedFile);
240 	done();
241 	return true;
242 }
243