KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jsmtpd > plugins > deliveryServices > UnixMailboxWriter


1 /*
2  *
3  * Jsmtpd, Java SMTP daemon
4  * Copyright (C) 2005 Jean-Francois POUX, jf.poux@laposte.net
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  *
20  */

21 package org.jsmtpd.plugins.deliveryServices;
22
23 import java.io.File JavaDoc;
24 import java.io.FileNotFoundException JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.io.RandomAccessFile JavaDoc;
27 import java.nio.channels.FileChannel JavaDoc;
28 import java.nio.channels.FileLock JavaDoc;
29 import java.util.Collections JavaDoc;
30 import java.util.HashMap JavaDoc;
31 import java.util.List JavaDoc;
32 import java.util.Map JavaDoc;
33
34 import org.apache.commons.logging.Log;
35 import org.apache.commons.logging.LogFactory;
36 import org.jsmtpd.core.common.PluginInitException;
37 import org.jsmtpd.core.common.delivery.FatalDeliveryException;
38 import org.jsmtpd.core.common.delivery.IDeliveryService;
39 import org.jsmtpd.core.common.delivery.TemporaryDeliveryException;
40 import org.jsmtpd.core.common.io.InvalidStreamParserInitialisation;
41 import org.jsmtpd.core.common.io.dataStream.DataStreamParser;
42 import org.jsmtpd.core.mail.Email;
43 import org.jsmtpd.core.mail.EmailAddress;
44 import org.jsmtpd.core.mail.Rcpt;
45 import org.jsmtpd.tools.ByteArrayTool;
46 import org.jsmtpd.tools.DateUtil;
47 import org.jsmtpd.tools.rights.IChown;
48 import org.jsmtpd.tools.rights.RightException;
49 import org.jsmtpd.tools.rights.UnixChown;
50
51 /**
52  * Re implemented (23/07/2005), should work as first intended
53  * Multiple write against different files
54  * Now uses FileLock + a Map to maintain lock states
55  * @author Jean-Francois POUX
56  */

57 public class UnixMailboxWriter implements IDeliveryService {
58     private Log log = LogFactory.getLog(UnixMailboxWriter.class);
59     private String JavaDoc mailboxDir = null;
60     private Map JavaDoc<String JavaDoc,FileLock JavaDoc> locks = Collections.synchronizedMap(new HashMap JavaDoc<String JavaDoc,FileLock JavaDoc>());
61     private boolean tryChown = false;
62     private IChown chown = new UnixChown();
63     
64     public void setTryChown(boolean tryChown) {
65         this.tryChown = tryChown;
66     }
67
68     public void doDelivery(Email in, List JavaDoc<Rcpt> rcpts) {
69         log.debug("Begin ");
70         for (Rcpt rcpt : rcpts) {
71             try {
72                 doSingleDelivery(in,rcpt.getEmailAddress());
73                 rcpt.setDelivered(Rcpt.STATUS_DELIVERED);
74             } catch (FatalDeliveryException e) {
75                 rcpt.setDelivered(Rcpt.STATUS_ERROR_FATAL);
76             } catch (TemporaryDeliveryException e) {
77                 rcpt.setDelivered(Rcpt.STATUS_ERROR_NOT_FATAL);
78             }
79         }
80         log.debug("end");
81     }
82     
83     /**
84      * Delivers to one box
85      * @param in the message
86      * @param rcpt one recipient
87      * @throws FatalDeliveryException
88      * @throws TemporaryDeliveryException
89      */

90     public void doSingleDelivery(Email in, EmailAddress rcpt) throws FatalDeliveryException, TemporaryDeliveryException {
91         RandomAccessFile JavaDoc fp = null;
92         String JavaDoc mailbox = mailboxDir + "/" + rcpt.getUser().toLowerCase();
93         boolean exist = new File JavaDoc (mailbox).exists();
94         try {
95             fp = openMBox(mailbox);
96             //File is locked
97
log.debug("Locked mailbox: " + rcpt.getUser());
98             fp.seek(fp.length());
99             try {
100                 DataStreamParser dsp = new DataStreamParser(512, 512);
101                 dsp.appendString("From " + in.getFrom().toString() + " " + DateUtil.currentMailboxDate());
102                 fp.write(ByteArrayTool.replaceBytes(dsp.getData(), ByteArrayTool.CRLF, ByteArrayTool.LF));
103             } catch (InvalidStreamParserInitialisation e1) {
104             }
105
106             byte[] pattern = { '\n', 'F', 'r', 'o', 'm', ' ' };
107             byte[] replace = { '\n', '>', 'F', 'r', 'o', 'm', ' ' };
108             //convert crlf to lf (unix text files)
109
byte[] tempo = ByteArrayTool.replaceBytes(in.getDataAsByte(), ByteArrayTool.CRLF, ByteArrayTool.LF);
110             //preserve from :
111
tempo = ByteArrayTool.replaceBytes(tempo, pattern, replace);
112             fp.write(tempo);
113
114             //apend <lf>
115
byte[] lf = new byte[1];
116             lf[0] = 10;
117             fp.write(lf);
118             if ((!exist) && tryChown)
119                 chown.chown(mailbox,rcpt.getUser().toLowerCase());
120             //File is unlocked in the finally
121
log.debug("Mail "+in.getDiskName()+ " delivered to : " + rcpt.getUser());
122         }
123
124         catch (TemporaryDeliveryException tde) {
125             log.info("Could not open/lock local mailbox: " + rcpt.getUser() + ", delivery delayed");
126             throw tde;
127         }
128
129         catch (IOException JavaDoc e) {
130             //Disk full, disk error, quota, etc => fatal error for this rcpt
131
log.info("IO Error delivering mail from " + in.getFrom().toString() + " to mailbox " + rcpt.toString()
132                     + ", mail to this rcpt is lost",e);
133             throw new FatalDeliveryException();
134         } catch (RightException e) {
135             log.error("Things are written, but I could not perfom chown on file ...",e);
136         } finally {
137             closeMBox(mailbox,fp);
138         }
139     }
140
141     private void goSleep() {
142         try {
143             Thread.sleep(1000);
144         } catch (InterruptedException JavaDoc e) {
145         }
146     }
147
148     public String JavaDoc getPluginName() {
149         return "UnixMailboxWriter plugin for jsmtpd";
150     }
151
152     /*
153      * (non-Javadoc)
154      *
155      * @see smtpd.common.IGenericPlugin#initPlugin()
156      */

157     public void initPlugin() throws PluginInitException {
158
159     }
160
161     /* (non-Javadoc)
162      * @see jsmtpd.common.IGenericPlugin#shutdownPlugin()
163      */

164     public void shutdownPlugin() {
165
166     }
167
168     //Autoconf
169
public void setMailDir(String JavaDoc mailboxesDirectory) {
170         this.mailboxDir = mailboxesDirectory;
171     }
172     /**
173      * Opens the RandomAccesFile of the mailbox, and ensures its locked system wide (the file lock) and in the vm (in the hashmap)
174      * @param fileName name of the mailbox (full path)
175      * @return
176      * @throws TemporaryDeliveryException if the mbox is no lockable
177      */

178     private synchronized RandomAccessFile JavaDoc openMBox (String JavaDoc fileName) throws TemporaryDeliveryException {
179         RandomAccessFile JavaDoc target = null;
180         FileLock JavaDoc lock = null;
181         // If our lock contains the mbox...
182
if (locks.containsKey(fileName))
183             throw new TemporaryDeliveryException();
184         else {
185             locks.put(fileName,null);
186             try {
187                 target = getRandomAccessFile(fileName);
188                 lock = getLock(target.getChannel(),fileName);
189                 if (!lock.isValid()) {
190                     throw new TemporaryDeliveryException();
191                 }
192                 locks.put(fileName,lock);
193             } catch (TemporaryDeliveryException e) {
194                 locks.remove(fileName);
195                 throw e;
196             }
197         }
198         return target;
199     }
200     /**
201      * Tries to acquire a lock on the given channel
202      * @param channel
203      * @param mbox
204      * @return
205      * @throws TemporaryDeliveryException after 10 failed tries
206      */

207     private FileLock JavaDoc getLock (FileChannel JavaDoc channel,String JavaDoc mbox) throws TemporaryDeliveryException {
208         FileLock JavaDoc lock = null;
209         int tryCount = 0;
210         //Try to lock the file for io access
211
while (lock == null) {
212             try {
213                 lock = channel.tryLock();
214             } catch (IOException JavaDoc e) {
215                 lock=null;
216             }
217             if (lock == null) {
218                 log.debug("Can't lock mailbox:"+mbox+" retrying");
219                 tryCount++;
220                 goSleep();
221             }
222             if (tryCount > 10)
223                 throw new TemporaryDeliveryException();
224         }
225         return lock;
226     }
227     /**
228      * Tries to open a mbox.
229      * @param mailbox
230      * @return
231      * @throws TemporaryDeliveryException
232      */

233     // Note: This is for win32, reports a File not found ex if the file is lock by another process
234
private RandomAccessFile JavaDoc getRandomAccessFile (String JavaDoc mailbox) throws TemporaryDeliveryException {
235         RandomAccessFile JavaDoc fp = null;
236         int tryCount = 0;
237          while (fp == null) {
238              if (tryCount > 10)
239                  throw new TemporaryDeliveryException();
240
241              try {
242                  tryCount++;
243                  fp = new RandomAccessFile JavaDoc(mailbox, "rw");
244              } catch (FileNotFoundException JavaDoc fileNotFoundException) {
245                  log.debug("Can't open mailbox: "+mailbox+"retrying");
246              }
247              if (fp == null)
248                  goSleep();
249          }
250          return fp;
251     }
252     
253     private synchronized void closeMBox (String JavaDoc fileName,RandomAccessFile JavaDoc fp){
254         if (locks.containsKey(fileName)) {
255             FileLock JavaDoc lock = locks.get(fileName);
256             locks.remove(fileName);
257             if (lock!= null) {
258                 try {
259                     lock.release();
260                     log.debug("UNlocked mailbox: " + fileName);
261                 } catch (IOException JavaDoc e) {
262                     log.error("Error Unlocking mailbox: " + fileName,e);
263                 }
264             } else
265                 log.error("Lock is not a file lock ?! "+lock);
266         }
267         if (fp != null) {
268             try {
269                 fp.close();
270             } catch (IOException JavaDoc e) {
271                 log.error("Error closing File ",e);
272             }
273         }
274     }
275 }
Popular Tags