1 package org.paneris.messageboard.receivemail;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.io.InputStream;
6 import java.sql.Connection;
7 import java.sql.ResultSet;
8 import java.sql.SQLException;
9 import java.sql.Statement;
10
11 import javax.mail.MessagingException;
12 import javax.mail.Multipart;
13 import javax.mail.Part;
14 import javax.mail.Session;
15 import javax.mail.internet.InternetAddress;
16 import javax.mail.internet.MimeMessage;
17
18 import org.paneris.jal.model.DBConnectionManager;
19 import org.paneris.jal.model.DDField;
20 import org.paneris.jal.model.DDRecord;
21 import org.paneris.jal.model.Log;
22 import org.paneris.messageboard.model.Attachment;
23 import org.paneris.messageboard.model.Message;
24 import org.paneris.util.ExceptionUtils;
25 import org.paneris.util.IoUtils;
26 import org.paneris.util.SQLUtils;
27
28
29
30
31
32
33 class MessageBoardStore {
34 private String database;
35 private DBConnectionManager connMgr;
36 private Connection conn;
37 private Log log;
38
39
40
41
42
43
44
45
46
47 public MessageBoardStore(DBConnectionManager connMgr, String database,
48 Log log)
49 throws IOException {
50 this.connMgr = connMgr;
51 conn = connMgr.getConnection("ReceiveMail", database);
52 if (conn == null) throw new RuntimeException("DB connection for " +
53 database + " is null.");
54 this.database = database;
55 this.log = log;
56 }
57
58
59
60
61
62 public void close() {
63 connMgr.freeConnection(database, conn);
64 }
65
66 protected void finalize() {
67 close();
68 }
69
70 class SenderID {
71 protected String id;
72 }
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88 public SenderID senderIDOfAddress(InternetAddress email)
89 throws MessagingException, SQLException, Exception {
90 Statement s = conn.createStatement();
91 String from = email.getAddress();
92 try {
93 String upper = connMgr.sqlUppercaseOperator(database);
94 ResultSet rs =
95 s.executeQuery("SELECT id FROM users " +
96 "WHERE TRIM(" + upper + "(email)) = " +
97 "'" + SQLUtils.escapedString(from.toUpperCase()) + "'");
98 if (rs.next()) {
99 SenderID id = new SenderID();
100 id.id = rs.getString(1);
101 return id;
102 }
103 else
104 throw new MessagingException(
105 "user `" + email + "' " +
106 "not authorised to post on this message board");
107 }
108 finally {
109 s.close();
110 }
111 }
112
113 class RecipientID {
114 Integer boardID;
115 Integer parentID;
116 }
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136 public RecipientID recipientIDOfAddress(InternetAddress email)
137 throws MessagingException, SQLException, Exception {
138 try {
139 String to = email.getAddress();
140
141 RecipientID recipientID = new RecipientID();
142 String boardName;
143
144 int atIndex = to.indexOf('@');
145 if (atIndex == -1) atIndex = to.length() - 1;
146 int dotIndex = to.indexOf('.');
147 if (dotIndex > 0 && dotIndex < atIndex) {
148 boardName = to.substring(dotIndex + 1, atIndex);
149 try {
150 recipientID.parentID = Integer.valueOf(to.substring(0, dotIndex));
151 }
152 catch (NumberFormatException e) {
153 throw new MessagingException("illegal parent message ID `" +
154 to.substring(0, dotIndex) + "'");
155 }
156 }
157 else {
158 boardName = to.substring(0, atIndex);
159 recipientID.parentID = new Integer(0);
160 }
161
162 Statement s = conn.createStatement();
163 try {
164 String upper = connMgr.sqlUppercaseOperator(database);
165 ResultSet rs =
166 s.executeQuery("SELECT id FROM messageboards " +
167 "WHERE TRIM(" + upper + "(name)) = " +
168 "'" + SQLUtils.escapedString(boardName.toUpperCase()) +
169 "'");
170 if (rs.next())
171 recipientID.boardID = new Integer(rs.getInt(1));
172 else
173 throw new MessagingException("unknown messageboard `" +
174 boardName + "'");
175 }
176 finally {
177 s.close();
178 }
179
180 return recipientID;
181 }
182 catch (SQLException e) {
183 throw new MessagingException("SQL error " + e);
184 }
185 }
186
187
188
189
190
191 private void attachmentWrite(
192 String database, Connection conn, Integer boardId, Integer id,
193 String fileName, String contentType, byte[] content)
194 throws Exception {
195
196 Attachment att = new Attachment(database);
197 ((DDField)att.get("message")).setValue(id);
198 att.setFieldValue("filename", fileName);
199 att.setBoard(boardId);
200 att.setData(content);
201
202
203
204 int semicolonIndex = contentType.indexOf(';');
205 if (semicolonIndex != -1)
206 contentType = contentType.substring(0, semicolonIndex);
207
208 Statement s = conn.createStatement();
209 ResultSet rs = s.executeQuery(
210 "SELECT id FROM attachmenttypes WHERE type = '" +
211 SQLUtils.escapedString(contentType) + "'");
212 Integer ct;
213 if (rs.next()) {
214 ct = new Integer(rs.getInt(1));
215 } else {
216 DDRecord attachmentType = new DDRecord(database,"attachmenttypes");
217 attachmentType.setFieldValue("type", contentType);
218 attachmentType.write();
219 DDField atid = (DDField)attachmentType.get("id");
220 ct = (Integer)atid.getValue();
221 }
222 ((DDField)att.get("type")).setValue(ct);
223 s.close();
224
225 att.write();
226
227 if (contentType.startsWith("application/msword")) {
228 byte[] htmlContent = null;
229 try {
230 String attPath = att.getAttachmentPath();
231 String imgSubDir = att.getFieldValue("id") + "-wvHtmlIMG";
232
233 String imgPath = attPath + "/" + imgSubDir;
234 if (!new File(imgPath).mkdir())
235 throw new IOException("can't mkdir " + imgPath);
236
237 htmlContent = WordToHTML.translated(content, attPath, imgSubDir);
238 }
239 catch (Exception e) {
240 e.printStackTrace();
241 log.warning("Translating Word document: " + e.getMessage());
242 }
243 catch (Error e) {
244 e.printStackTrace();
245 throw e;
246 }
247
248 if (htmlContent != null)
249 attachmentWrite(database, conn, boardId, id,
250 fileName == null ? "anon.html" : fileName + ".html",
251 "text/html", htmlContent);
252 }
253 }
254
255 private Object getContent(MimeMessage message, Part part)
256 throws MessagingException, IOException {
257 if (part.getContentType().startsWith("text/plain")) {
258 String[] mailer = message.getHeader("X-Mailer");
259 if (mailer != null && mailer.length > 0 &&
260 mailer[0].startsWith("Mozilla 4.6"))
261 part.setHeader("Content-Transfer-Encoding", "8bit");
262 }
263
264 return part.getContent();
265 }
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280 public Object messageAccept(SenderID senderID, RecipientID recipientID,
281 InputStream text) throws Exception {
282 MimeMessage message = new MimeMessage(
283 Session.getDefaultInstance(System.getProperties(), null),
284 text);
285
286
287
288
289 Message m = new Message(database);
290 m.write();
291 DDField idf = (DDField)m.get("id");
292 Integer id = (Integer)idf.getValue();
293
294
295
296 m.setFieldValue("subject",
297 message.getSubject() == null ?
298 "(no subject)" :
299 message.getSubject());
300
301 ((DDField)m.get("board")).setValue(recipientID.boardID);
302 ((DDField)m.get("parent")).setValue(recipientID.parentID);
303 ((DDField)m.get("author")).setValue(senderID.id);
304
305
306
307 StringBuffer bodyText = new StringBuffer(100);
308
309 Object content = getContent(message, message);
310 if (content instanceof String) {
311
312 bodyText.append((String)content);
313 }
314 else if (content instanceof Multipart) {
315 Multipart parts = (Multipart)content;
316 for (int p = 0; p < parts.getCount(); ++p) {
317 Part part = parts.getBodyPart(p);
318
319
320
321 Object partContent = getContent(message, part);
322
323 if (partContent instanceof String &&
324 part.getFileName() == null)
325 bodyText.append((String)partContent);
326 else
327 attachmentWrite(database, conn, recipientID.boardID, id,
328 part.getFileName(), part.getContentType(),
329 IoUtils.slurp(part.getInputStream(), 1024));
330 }
331 }
332 else {
333
334
335 attachmentWrite(database, conn, recipientID.boardID, id,
336 null, message.getContentType(),
337 IoUtils.slurp(message.getInputStream(), 100));
338 }
339
340
341
342 String messageText = bodyText.toString();
343 if (messageText.length() > 7168) {
344 attachmentWrite(database, conn, recipientID.boardID, id,
345 "", "truncation", messageText.getBytes());
346 messageText = messageText.substring(0,7168);
347 messageText += "\n\nThis message has been truncated, the complete message has been stored as an attachment.";
348 }
349 m.setFieldValue("message", messageText);
350 m.write();
351 m.setAttachments();
352 (new DistributeThread(m, log)).start();
353
354 return id;
355 }
356 }
357
358
359
360
361
362 class DistributeThread extends Thread {
363 private Message m;
364 private Log log;
365
366 public DistributeThread(Message m, Log log) {
367 this.m = m;
368 this.log = log;
369 }
370
371 public void run() {
372 try {
373 m.distribute();
374 }
375 catch (Exception e) {
376 log.error("Redistributing incoming email: " +
377 ExceptionUtils.stackTrace(e));
378 }
379 }
380 }