Source Code Cross Referenced for ClamAVScan.java in  » Net » james-2.3.1 » org » apache » james » transport » mailets » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Net » james 2.3.1 » org.apache.james.transport.mailets 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /****************************************************************
002:         * Licensed to the Apache Software Foundation (ASF) under one   *
003:         * or more contributor license agreements.  See the NOTICE file *
004:         * distributed with this work for additional information        *
005:         * regarding copyright ownership.  The ASF licenses this file   *
006:         * to you under the Apache License, Version 2.0 (the            *
007:         * "License"); you may not use this file except in compliance   *
008:         * with the License.  You may obtain a copy of the License at   *
009:         *                                                              *
010:         *   http://www.apache.org/licenses/LICENSE-2.0                 *
011:         *                                                              *
012:         * Unless required by applicable law or agreed to in writing,   *
013:         * software distributed under the License is distributed on an  *
014:         * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
015:         * KIND, either express or implied.  See the License for the    *
016:         * specific language governing permissions and limitations      *
017:         * under the License.                                           *
018:         ****************************************************************/package org.apache.james.transport.mailets;
019:
020:        import org.apache.mailet.RFC2822Headers;
021:        import org.apache.mailet.GenericMailet;
022:        import org.apache.mailet.Mail;
023:
024:        import javax.mail.MessagingException;
025:        import javax.mail.internet.MimeMessage;
026:
027:        import java.io.BufferedOutputStream;
028:        import java.io.BufferedReader;
029:        import java.io.BufferedWriter;
030:        import java.io.IOException;
031:        import java.io.InputStreamReader;
032:        import java.io.OutputStreamWriter;
033:        import java.io.PrintWriter;
034:        import java.io.StringWriter;
035:        import java.net.ConnectException;
036:        import java.net.InetAddress;
037:        import java.net.Socket;
038:        import java.net.UnknownHostException;
039:        import java.util.ArrayList;
040:        import java.util.Collection;
041:        import java.util.HashSet;
042:        import java.util.Iterator;
043:        import java.util.Set;
044:
045:        /**
046:         * <P>Does an antivirus scan check using a ClamAV daemon (CLAMD)</P>
047:         * 
048:         * <P> Interacts directly with the daemon using the "stream" method,
049:         * which should have the lowest possible overhead.</P>
050:         * <P>The CLAMD daemon will typically reside on <I>localhost</I>, but could reside on a
051:         * different host.
052:         * It may also consist on a set of multiple daemons, each residing on a different
053:         * server and on different IP number.
054:         * In such case a DNS host name with multiple IP addresses (round-robin load sharing)
055:         * is supported by the mailet (but on the same port number).</P>
056:         * 
057:         * <P>Handles the following init parameters:</P>
058:         * <UL>
059:         *    <LI><CODE>&lt;debug&gt;</CODE>.</LI>
060:         *    <LI><CODE>&lt;host&gt;</CODE>: the host name of the server where CLAMD runs. It can either be
061:         *        a machine name, such as
062:         *        "<code>java.sun.com</code>", or a textual representation of its
063:         *        IP address. If a literal IP address is supplied, only the
064:         *        validity of the address format is checked.
065:         *        If the machine name resolves to multiple IP addresses, <I>round-robin load sharing</I> will
066:         *        be used.
067:         *        The default is <CODE>localhost</CODE>.</LI>
068:         *    <LI><CODE>&lt;port&gt;</CODE>: the port on which CLAMD listens. The default is <I>3310</I>.</LI>
069:         *    <LI><CODE>&lt;maxPings&gt;</CODE>: the maximum number of connection retries during startup.
070:         *        If the value is <I>0</I> no startup test will be done.
071:         *        The default is <I>6</I>.</LI>
072:         *    <LI><CODE>&lt;pingIntervalMilli&gt;</CODE>: the interval (in milliseconds)
073:         *        between each connection retry during startup.
074:         *        The default is <I>30000</I> (30 seconds).</LI>
075:         *    <LI><CODE>&lt;streamBufferSize&gt;</CODE>: the BufferedOutputStream buffer size to use 
076:         *        writing to the <I>stream connection</I>. The default is <I>8192</I>.</LI>
077:         * </UL>
078:         * 
079:         * <P>The actions performed are as follows:</P>
080:         * <UL>
081:         *    <LI>During initialization:</LI>
082:         *    <OL>
083:         *        <LI>Gets all <CODE>config.xml</CODE> parameters, handling the defaults;</LI>
084:         *        <LI>resolves the <CODE>&lt;host&gt;</CODE> parameter, creating the round-robin IP list;</LI>
085:         *        <LI>connects to CLAMD at the first IP in the round-robin list, on
086:         *            the specified <CODE>&lt;port&gt;</CODE>;</LI>
087:         *        <LI>if unsuccessful, retries every <CODE>&lt;pingIntervalMilli&gt;</CODE> milliseconds up to
088:         *            <CODE>&lt;maxPings&gt;</CODE> times;</LI>
089:         *        <LI>sends a <CODE>PING</CODE> request;</LI>
090:         *        <LI>waits for a <CODE>PONG</CODE> answer;</LI>
091:         *        <LI>repeats steps 3-6 for every other IP resolved.
092:         *    </OL>
093:         *    <LI>For every mail</LI>
094:         *    <OL>
095:         *        <LI>connects to CLAMD at the "next" IP in the round-robin list, on
096:         *            the specified <CODE>&lt;port&gt;</CODE>, and increments the "next" index;
097:         *            if the connection request is not accepted tries with the next one
098:         *            in the list unless all of them have failed;</LI>
099:         *        <LI>sends a "<CODE>STREAM</CODE>" request;</LI>
100:         *        <LI>parses the "<CODE>PORT <I>streamPort</I></CODE>" answer obtaining the port number;</LI>
101:         *        <LI>makes a second connection (the <I>stream connection</I>) to CLAMD at the same host (or IP)
102:         *            on the <I>streamPort</I> just obtained;</LI>
103:         *        <LI>sends the mime message to CLAMD (using {@link MimeMessage#writeTo(OutputStream)})
104:         *            through the <I>stream connection</I>;</LI>
105:         *        <LI>closes the <I>stream connection</I>;</LI>
106:         *        <LI>gets the "<CODE>OK</CODE>" or "<CODE>... FOUND</CODE>" answer from the main connection;</LI>
107:         *        <LI>closes the main connection;</LI>
108:         *        <LI>sets the "<CODE>org.apache.james.infected</CODE>" <I>mail attribute</I> to either
109:         *            "<CODE>true</CODE>" or "<CODE>false</CODE>";</LI>
110:         *        <LI>adds the "<CODE>X-MessageIsInfected</CODE>" <I>header</I> to either
111:         *            "<CODE>true</CODE>" or "<CODE>false</CODE>";</LI>
112:         *    </OL>
113:         * </UL>
114:         * 
115:         * <P>Some notes regarding <a href="http://www.clamav.net/">clamav.conf</a>:</p>
116:         * <UL>
117:         *    <LI><CODE>LocalSocket</CODE> must be commented out</LI>
118:         *    <LI><CODE>TCPSocket</CODE> must be set to a port# (typically 3310)</LI>
119:         *    <LI><CODE>StreamMaxLength</CODE> must be &gt;= the James config.xml parameter
120:         *    &lt;<CODE>maxmessagesize</CODE>&gt; in SMTP &lt;<CODE>handler</CODE>&gt;</LI>
121:         *    <LI><CODE>MaxThreads</CODE> should? be &gt;= the James config.xml parameter
122:         *    &lt;<CODE>threads</CODE>&gt; in &lt;<CODE>spoolmanager</CODE>&gt;</LI>
123:         *    <LI><CODE>ScanMail</CODE> must be uncommented</LI>
124:         * </UL>
125:         *
126:         * <P>Here follows an example of config.xml definitions deploying CLAMD on localhost,
127:         * and handling the infected messages:</P>
128:         * <PRE><CODE>
129:         *
130:         * ...
131:         *
132:         *    &lt;!-- Do an antivirus scan --&gt;
133:         *    &lt;mailet match="All" class="ClamAVScan" onMailetException="ignore"/&gt;
134:         *
135:         *    &lt;!-- If infected go to virus processor --&gt;
136:         *    &lt;mailet match="HasMailAttributeWithValue=org.apache.james.infected, true" class="ToProcessor"&gt;
137:         *       &lt;processor&gt; virus &lt;/processor&gt;
138:         *    &lt;/mailet&gt;
139:         *
140:         *    &lt;!-- Check attachment extensions for possible viruses --&gt;
141:         *    &lt;mailet match="AttachmentFileNameIs=-d -z *.exe *.com *.bat *.cmd *.pif *.scr *.vbs *.avi *.mp3 *.mpeg *.shs" class="ToProcessor"&gt;
142:         *       &lt;processor&gt; bad-extensions &lt;/processor&gt;
143:         *    &lt;/mailet&gt;
144:         *
145:         * ...
146:         *
147:         * &lt;!-- Messages containing viruses --&gt;
148:         * &lt;processor name="virus"&gt;
149:         *
150:         *    &lt;!-- To avoid a loop while bouncing --&gt;
151:         *    &lt;mailet match="All" class="SetMailAttribute"&gt;
152:         *       &lt;org.apache.james.infected&gt;true, bouncing&lt;/org.apache.james.infected&gt;
153:         *    &lt;/mailet&gt;
154:         *
155:         *    &lt;mailet match="SMTPAuthSuccessful" class="Bounce"&gt;
156:         *       &lt;sender&gt;bounce-admin@xxx.com&lt;/sender&gt;
157:         *       &lt;inline&gt;heads&lt;/inline&gt;
158:         *       &lt;attachment&gt;none&lt;/attachment&gt;
159:         *       &lt;notice&gt; Warning: We were unable to deliver the message below because it was found infected by virus(es). &lt;/notice&gt;
160:         *    &lt;/mailet&gt;
161:         *
162:         *    &lt;!--
163:         *    &lt;mailet match="All" class="ToRepository"&gt;
164:         *       &lt;repositoryPath&gt;file://var/mail/infected/&lt;/repositoryPath&gt;
165:         *    &lt;/mailet&gt;
166:         *    --&gt;
167:         *
168:         *    &lt;mailet match="All" class="Null" /&gt;
169:         * &lt;/processor&gt;
170:         * </CODE></PRE>
171:         *
172:         * @version 2.2.1
173:         * @since 2.2.1
174:         * @see <a href="http://www.clamav.net/">ClamAV Home Page</a>
175:         * @see <a href="http://www.sosdg.org/clamav-win32/">ClamAV For Windows</a>
176:         */
177:        public class ClamAVScan extends GenericMailet {
178:
179:            private static final int DEFAULT_PORT = 3310;
180:
181:            private static final int DEFAULT_MAX_PINGS = 6;
182:
183:            private static final int DEFAULT_PING_INTERVAL_MILLI = 30000;
184:
185:            private static final int DEFAULT_STREAM_BUFFER_SIZE = 8192;
186:
187:            private static final int DEFAULT_CONNECTION_TIMEOUT = 20000;
188:
189:            private static final String STREAM_PORT_STRING = "PORT ";
190:
191:            private static final String FOUND_STRING = "FOUND";
192:
193:            private static final String MAIL_ATTRIBUTE_NAME = "org.apache.james.infected";
194:
195:            private static final String HEADER_NAME = "X-MessageIsInfected";
196:
197:            /**
198:             * Holds value of property debug.
199:             */
200:            private boolean debug;
201:
202:            /**
203:             * Holds value of property host.
204:             */
205:            private String host;
206:
207:            /**
208:             * Holds value of property port.
209:             */
210:            private int port;
211:
212:            /**
213:             * Holds value of property maxPings.
214:             */
215:            private int maxPings;
216:
217:            /**
218:             * Holds value of property pingIntervalMilli.
219:             */
220:            private int pingIntervalMilli;
221:
222:            /**
223:             * Holds value of property streamBufferSize.
224:             */
225:            private int streamBufferSize;
226:
227:            /**
228:             * Holds value of property addresses.
229:             */
230:            private InetAddress[] addresses;
231:
232:            /**
233:             * Holds the index of the next address to connect to
234:             */
235:            private int nextAddressIndex;
236:
237:            /**
238:             * Return a string describing this mailet.
239:             *
240:             * @return a string describing this mailet
241:             */
242:            public String getMailetInfo() {
243:                return "Antivirus Check using ClamAV (CLAMD)";
244:            }
245:
246:            /** Gets the expected init parameters. */
247:            protected String[] getAllowedInitParameters() {
248:                String[] allowedArray = {
249:                        //            "static",
250:                        "debug", "host", "port", "maxPings",
251:                        "pingIntervalMilli", "streamBufferSize" };
252:                return allowedArray;
253:            }
254:
255:            /**
256:             * Initializer for property debug.
257:             */
258:            protected void initDebug() {
259:                String debugParam = getInitParameter("debug");
260:                setDebug((debugParam == null) ? false : new Boolean(debugParam)
261:                        .booleanValue());
262:            }
263:
264:            /**
265:             * Getter for property debug.
266:             * @return Value of property debug.
267:             */
268:            public boolean isDebug() {
269:                return this .debug;
270:            }
271:
272:            /**
273:             * Setter for property debug.
274:             * @param debug New value of property debug.
275:             */
276:            public void setDebug(boolean debug) {
277:                this .debug = debug;
278:            }
279:
280:            /**
281:             * Initializer for property host.
282:             * @throws UnknownHostException if unable to resolve the host name, or if invalid
283:             */
284:            protected void initHost() throws UnknownHostException {
285:                setHost(getInitParameter("host"));
286:                if (isDebug()) {
287:                    log("host: " + getHost());
288:                }
289:            }
290:
291:            /**
292:             * Getter for property host.
293:             * @return Value of property host.
294:             */
295:            public String getHost() {
296:
297:                return this .host;
298:            }
299:
300:            /**
301:             * Setter for property host.
302:             * Resolves also the host name into the corresponding IP addresses, issues
303:             * a {@link #setAddresses} and resets the <CODE>nextAddressIndex</CODE>
304:             * variable to <I>0</I> for dealing with <I>round-robin</I>.
305:             * @param host New value of property host.
306:             * @throws UnknownHostException if unable to resolve the host name, or if invalid
307:             */
308:            public void setHost(String host) throws UnknownHostException {
309:
310:                this .host = host;
311:
312:                setAddresses(InetAddress.getAllByName(host));
313:
314:                nextAddressIndex = 0;
315:            }
316:
317:            /**
318:             * Initializer for property port.
319:             */
320:            protected void initPort() {
321:                String portParam = getInitParameter("port");
322:                setPort((portParam == null) ? DEFAULT_PORT : Integer
323:                        .parseInt(portParam));
324:                if (isDebug()) {
325:                    log("port: " + getPort());
326:                }
327:            }
328:
329:            /**
330:             * Getter for property port.
331:             * @return Value of property port.
332:             */
333:            public int getPort() {
334:
335:                return this .port;
336:            }
337:
338:            /**
339:             * Setter for property port.
340:             * @param port New value of property port.
341:             */
342:            public void setPort(int port) {
343:
344:                this .port = port;
345:            }
346:
347:            /**
348:             * Initializer for property maxPings.
349:             */
350:            protected void initMaxPings() {
351:                String maxPingsParam = getInitParameter("maxPings");
352:                setMaxPings((maxPingsParam == null) ? DEFAULT_MAX_PINGS
353:                        : Integer.parseInt(maxPingsParam));
354:                if (isDebug()) {
355:                    log("maxPings: " + getMaxPings());
356:                }
357:            }
358:
359:            /**
360:             * Getter for property maxPings.
361:             * @return Value of property maxPings.
362:             */
363:            public int getMaxPings() {
364:
365:                return this .maxPings;
366:            }
367:
368:            /**
369:             * Setter for property maxPings.
370:             * @param maxPings New value of property maxPings.
371:             */
372:            public void setMaxPings(int maxPings) {
373:
374:                this .maxPings = maxPings;
375:            }
376:
377:            /**
378:             * Initializer for property pingIntervalMilli.
379:             */
380:            protected void initPingIntervalMilli() {
381:                String pingIntervalMilliParam = getInitParameter("pingIntervalMilli");
382:                setPingIntervalMilli((pingIntervalMilliParam == null) ? DEFAULT_PING_INTERVAL_MILLI
383:                        : Integer.parseInt(pingIntervalMilliParam));
384:                if (isDebug()) {
385:                    log("pingIntervalMilli: " + getPingIntervalMilli());
386:                }
387:            }
388:
389:            /**
390:             * Getter for property pingIntervalMilli.
391:             * @return Value of property pingIntervalMilli.
392:             */
393:            public int getPingIntervalMilli() {
394:
395:                return this .pingIntervalMilli;
396:            }
397:
398:            /**
399:             * Setter for property pingIntervalMilli.
400:             * @param pingIntervalMilli New value of property pingIntervalMilli.
401:             */
402:            public void setPingIntervalMilli(int pingIntervalMilli) {
403:
404:                this .pingIntervalMilli = pingIntervalMilli;
405:            }
406:
407:            /**
408:             * Initializer for property streamBufferSize.
409:             */
410:            protected void initStreamBufferSize() {
411:                String streamBufferSizeParam = getInitParameter("streamBufferSize");
412:                setStreamBufferSize((streamBufferSizeParam == null) ? DEFAULT_STREAM_BUFFER_SIZE
413:                        : Integer.parseInt(streamBufferSizeParam));
414:                if (isDebug()) {
415:                    log("streamBufferSize: " + getStreamBufferSize());
416:                }
417:            }
418:
419:            /**
420:             * Getter for property streamBufferSize.
421:             * @return Value of property streamBufferSize.
422:             */
423:            public int getStreamBufferSize() {
424:
425:                return this .streamBufferSize;
426:            }
427:
428:            /**
429:             * Setter for property streamBufferSize.
430:             * @param streamBufferSize New value of property streamBufferSize.
431:             */
432:            public void setStreamBufferSize(int streamBufferSize) {
433:
434:                this .streamBufferSize = streamBufferSize;
435:            }
436:
437:            /**
438:             * Indexed getter for property addresses.
439:             * @param index Index of the property.
440:             * @return Value of the property at <CODE>index</CODE>.
441:             */
442:            protected InetAddress getAddresses(int index) {
443:
444:                return this .addresses[index];
445:            }
446:
447:            /**
448:             * Getter for property addresses.
449:             * @return Value of property addresses.
450:             */
451:            protected InetAddress[] getAddresses() {
452:
453:                return this .addresses;
454:            }
455:
456:            /**
457:             * Setter for property addresses.
458:             * @param addresses New value of property addresses.
459:             */
460:            protected void setAddresses(InetAddress[] addresses) {
461:
462:                this .addresses = addresses;
463:            }
464:
465:            /**
466:             * Getter for property nextAddress.
467:             * 
468:             * Gets the address of the next CLAMD server to connect to in this round, using round-robin.
469:             * Increments the nextAddressIndex for the next round.
470:             * @return Value of property address.
471:             */
472:            protected synchronized InetAddress getNextAddress() {
473:
474:                InetAddress address = getAddresses(nextAddressIndex);
475:
476:                nextAddressIndex++;
477:                if (nextAddressIndex >= getAddressesCount()) {
478:                    nextAddressIndex = 0;
479:                }
480:
481:                return address;
482:            }
483:
484:            /**
485:             * Getter for property addressesCount.
486:             * @return Value of property addressesCount.
487:             */
488:            public int getAddressesCount() {
489:                return getAddresses().length;
490:            }
491:
492:            /**
493:             * Gets a Socket connected to CLAMD.
494:             * 
495:             * Will loop though the round-robin address list until the first one accepts
496:             * the connection.
497:             * @return a socket connected to CLAMD
498:             * @throws MessagingException if no CLAMD in the round-robin address list has accepted the connection
499:             */
500:            protected Socket getClamdSocket() throws MessagingException {
501:
502:                InetAddress address = null;
503:
504:                Set usedAddresses = new HashSet(getAddressesCount());
505:                for (;;) {
506:                    // this do-while loop is needed because other threads could in the meantime
507:                    // calling getNextAddress(), and because of that the current thread may skip
508:                    // some working address
509:                    do {
510:                        if (usedAddresses.size() >= getAddressesCount()) {
511:                            String logText = "Unable to connect to CLAMD. All addresses failed.";
512:                            log(logText + " Giving up.");
513:                            throw new MessagingException(logText);
514:                        }
515:                        address = getNextAddress();
516:                    } while (!usedAddresses.add(address));
517:                    try {
518:                        // get the socket
519:                        return new Socket(address, getPort());
520:                    } catch (IOException ioe) {
521:                        log("Exception caught acquiring main socket to CLAMD on "
522:                                + address
523:                                + " on port "
524:                                + getPort()
525:                                + ": "
526:                                + ioe.getMessage());
527:                        address = getNextAddress();
528:                        // retry
529:                        continue;
530:                    }
531:                }
532:            }
533:
534:            /**
535:             * Mailet initialization routine.
536:             */
537:            public void init() throws MessagingException {
538:
539:                // check that all init parameters have been declared in allowedInitParameters
540:                checkInitParameters(getAllowedInitParameters());
541:
542:                try {
543:                    initDebug();
544:                    if (isDebug()) {
545:                        log("Initializing");
546:                    }
547:
548:                    initHost();
549:                    initPort();
550:                    initMaxPings();
551:                    initPingIntervalMilli();
552:                    initStreamBufferSize();
553:
554:                    // If "maxPings is > ping the CLAMD server to check if it is up
555:                    if (getMaxPings() > 0) {
556:                        ping();
557:                    }
558:
559:                } catch (Exception e) {
560:                    log("Exception thrown", e);
561:                    throw new MessagingException("Exception thrown", e);
562:                }
563:
564:            }
565:
566:            /**
567:             * Scans the mail.
568:             *
569:             * @param mail the mail to scan
570:             * @throws MessagingException if a problem arises
571:             */
572:            public void service(Mail mail) throws MessagingException {
573:
574:                // if already checked no action
575:                if (mail.getAttribute(MAIL_ATTRIBUTE_NAME) != null) {
576:                    return;
577:                }
578:
579:                MimeMessage mimeMessage = mail.getMessage();
580:
581:                if (mimeMessage == null) {
582:                    log("Null MimeMessage. Will send to ghost");
583:                    // write mail info to log
584:                    logMailInfo(mail);
585:                    mail.setState(Mail.GHOST);
586:                    return;
587:                }
588:
589:                // get the socket
590:                Socket socket = getClamdSocket();
591:                BufferedReader reader = null;
592:                PrintWriter writer = null;
593:                Socket streamSocket = null;
594:                BufferedOutputStream bos = null;
595:
596:                try {
597:
598:                    // prepare the reader and writer for the commands
599:                    reader = new BufferedReader(new InputStreamReader(socket
600:                            .getInputStream(), "ASCII"));
601:                    writer = new PrintWriter(new BufferedWriter(
602:                            new OutputStreamWriter(socket.getOutputStream())),
603:                            true);
604:
605:                    // write a request for a port to use for streaming out the data to scan
606:                    writer.println("STREAM");
607:                    writer.flush();
608:
609:                    // parse and get the "stream" port#
610:                    int streamPort = getStreamPortFromAnswer(reader.readLine());
611:
612:                    // get the "stream" socket and the related (buffered) output stream
613:                    streamSocket = new Socket(socket.getInetAddress(),
614:                            streamPort);
615:                    bos = new BufferedOutputStream(streamSocket
616:                            .getOutputStream(), getStreamBufferSize());
617:
618:                    // stream out the message to the scanner
619:                    mimeMessage.writeTo(bos);
620:                    bos.flush();
621:                    bos.close();
622:                    streamSocket.close();
623:
624:                    String answer = null;
625:                    boolean virusFound = false;
626:                    String logMessage = "";
627:                    for (;;) {
628:                        answer = reader.readLine();
629:                        if (answer != null) {
630:                            answer = answer.trim();
631:
632:                            // if a virus is found the answer will be '... FOUND'
633:                            if (answer.substring(
634:                                    answer.length() - FOUND_STRING.length())
635:                                    .equals(FOUND_STRING)) {
636:                                virusFound = true;
637:                                logMessage = answer + " (by CLAMD on "
638:                                        + socket.getInetAddress() + ")";
639:                                log(logMessage);
640:                            }
641:                        } else {
642:                            break;
643:                        }
644:                    }
645:
646:                    reader.close();
647:                    writer.close();
648:
649:                    if (virusFound) {
650:                        String errorMessage = mail.getErrorMessage();
651:                        if (errorMessage == null) {
652:                            errorMessage = "";
653:                        } else {
654:                            errorMessage += "\r\n";
655:                        }
656:                        StringBuffer sb = new StringBuffer(errorMessage);
657:                        sb.append(logMessage + "\r\n");
658:
659:                        // write mail and message info to log
660:                        logMailInfo(mail);
661:                        logMessageInfo(mimeMessage);
662:
663:                        // mark the mail with a mail attribute to check later on by other matchers/mailets
664:                        mail.setAttribute(MAIL_ATTRIBUTE_NAME, "true");
665:
666:                        // sets the error message to be shown in any "notifyXxx" message
667:                        mail.setErrorMessage(sb.toString());
668:
669:                        // mark the message with a header string
670:                        mimeMessage.setHeader(HEADER_NAME, "true");
671:
672:                    } else {
673:                        if (isDebug()) {
674:                            log("OK (by CLAMD on " + socket.getInetAddress()
675:                                    + ")");
676:                        }
677:                        mail.setAttribute(MAIL_ATTRIBUTE_NAME, "false");
678:
679:                        // mark the message with a header string
680:                        mimeMessage.setHeader(HEADER_NAME, "false");
681:
682:                    }
683:
684:                    try {
685:                        saveChanges(mimeMessage);
686:                    } catch (Exception ex) {
687:                        log(
688:                                "Exception caught while saving changes (header) to the MimeMessage. Ignoring ...",
689:                                ex);
690:                    }
691:
692:                } catch (Exception ex) {
693:                    log("Exception caught calling CLAMD on "
694:                            + socket.getInetAddress() + ": " + ex.getMessage(),
695:                            ex);
696:                    throw new MessagingException("Exception caught", ex);
697:                } finally {
698:                    try {
699:                        if (reader != null) {
700:                            reader.close();
701:                        }
702:                    } catch (Throwable t) {
703:                    }
704:                    try {
705:                        if (writer != null) {
706:                            writer.close();
707:                        }
708:                    } catch (Throwable t) {
709:                    }
710:                    try {
711:                        if (bos != null) {
712:                            bos.close();
713:                        }
714:                    } catch (Throwable t) {
715:                    }
716:                    try {
717:                        if (streamSocket != null) {
718:                            streamSocket.close();
719:                        }
720:                    } catch (Throwable t) {
721:                    }
722:                    try {
723:                        if (socket != null) {
724:                            socket.close();
725:                        }
726:                    } catch (Throwable t) {
727:                    }
728:                }
729:
730:            }
731:
732:            /**
733:             * Checks if there are unallowed init parameters specified in the configuration file
734:             * against the String[] allowedInitParameters.
735:             * @param allowedArray array of strings containing the allowed parameter names
736:             * @throws MessagingException if an unknown parameter name is found
737:             */
738:            protected final void checkInitParameters(String[] allowedArray)
739:                    throws MessagingException {
740:                // if null then no check is requested
741:                if (allowedArray == null) {
742:                    return;
743:                }
744:
745:                Collection allowed = new HashSet();
746:                Collection bad = new ArrayList();
747:
748:                for (int i = 0; i < allowedArray.length; i++) {
749:                    allowed.add(allowedArray[i]);
750:                }
751:
752:                Iterator iterator = getInitParameterNames();
753:                while (iterator.hasNext()) {
754:                    String parameter = (String) iterator.next();
755:                    if (!allowed.contains(parameter)) {
756:                        bad.add(parameter);
757:                    }
758:                }
759:
760:                if (bad.size() > 0) {
761:                    throw new MessagingException(
762:                            "Unexpected init parameters found: "
763:                                    + arrayToString(bad.toArray()));
764:                }
765:            }
766:
767:            /**
768:             * Utility method for obtaining a string representation of an array of Objects.
769:             */
770:            private final String arrayToString(Object[] array) {
771:                if (array == null) {
772:                    return "null";
773:                }
774:                StringBuffer sb = new StringBuffer(1024);
775:                sb.append("[");
776:                for (int i = 0; i < array.length; i++) {
777:                    if (i > 0) {
778:                        sb.append(",");
779:                    }
780:                    sb.append(array[i]);
781:                }
782:                sb.append("]");
783:                return sb.toString();
784:            }
785:
786:            /**
787:             * Tries to "ping" all the CLAMD daemons to
788:             * check if they are up and accepting requests.
789:             **/
790:
791:            protected void ping() throws Exception {
792:
793:                for (int i = 0; i < getAddressesCount(); i++) {
794:                    ping(getAddresses(i));
795:                }
796:            }
797:
798:            /**
799:             * Tries (and retries as specified up to 'getMaxPings()') to "ping" the specified CLAMD daemon to
800:             * check if it is up and accepting requests.
801:             * @param address the address to "ping"
802:             */
803:            protected void ping(InetAddress address) throws Exception {
804:                Socket socket = null;
805:
806:                int ping = 1;
807:                for (;;) {
808:                    if (isDebug()) {
809:                        log("Trial #" + ping + "/" + getMaxPings()
810:                                + " - creating socket connected to " + address
811:                                + " on port " + getPort());
812:                    }
813:                    try {
814:                        socket = new Socket(address, getPort());
815:                        break;
816:                    } catch (ConnectException ce) {
817:                        log("Trial #" + ping + "/" + getMaxPings()
818:                                + " - exception caught: " + ce.toString()
819:                                + " while creating socket connected to "
820:                                + address + " on port " + getPort());
821:                        ping++;
822:                        if (ping <= getMaxPings()) {
823:                            log("Waiting " + getPingIntervalMilli()
824:                                    + " milliseconds before retrying ...");
825:                            Thread.sleep(getPingIntervalMilli());
826:                        } else {
827:                            break;
828:                        }
829:                    }
830:                }
831:
832:                // if 'socket' is still null then 'maxPings' has been exceeded
833:                if (socket == null) {
834:                    throw new ConnectException(
835:                            "maxPings exceeded: "
836:                                    + getMaxPings()
837:                                    + ". Giving up. The clamd daemon seems not to be running");
838:                }
839:
840:                try {
841:                    // get the reader and writer to ping and receive pong
842:                    BufferedReader reader = new BufferedReader(
843:                            new InputStreamReader(socket.getInputStream(),
844:                                    "ASCII"));
845:                    PrintWriter writer = new PrintWriter(new BufferedWriter(
846:                            new OutputStreamWriter(socket.getOutputStream())),
847:                            true);
848:
849:                    log("Sending: \"PING\" to " + address + " ...");
850:                    writer.println("PING");
851:                    writer.flush();
852:
853:                    boolean pongReceived = false;
854:                    for (;;) {
855:                        String answer = reader.readLine();
856:                        if (answer != null) {
857:                            answer = answer.trim();
858:                            log("Received: \"" + answer + "\"");
859:                            answer = answer.trim();
860:                            if (answer.equals("PONG")) {
861:                                pongReceived = true;
862:                            }
863:
864:                        } else {
865:                            break;
866:                        }
867:                    }
868:
869:                    reader.close();
870:                    writer.close();
871:
872:                    if (!pongReceived) {
873:                        throw new ConnectException(
874:                                "Bad answer from \"PING\" probe: expecting \"PONG\"");
875:                    }
876:                } finally {
877:                    socket.close();
878:                }
879:            }
880:
881:            /**
882:             * Parses the answer from a STREAM request and gets the port number.
883:             *
884:             * @param answer the answer from CLAMD containing the port number
885:             * @return the port number for streaming out the data to scan
886:             */
887:            protected final int getStreamPortFromAnswer(String answer)
888:                    throws ConnectException {
889:                int port = -1;
890:                if (answer != null && answer.startsWith(STREAM_PORT_STRING)) {
891:                    try {
892:                        port = Integer.parseInt(answer
893:                                .substring(STREAM_PORT_STRING.length()));
894:                    } catch (NumberFormatException nfe) {
895:
896:                    }
897:                }
898:
899:                if (port <= 0) {
900:                    throw new ConnectException(
901:                            "\"PORT nn\" expected - unable to parse: " + "\""
902:                                    + answer + "\"");
903:                }
904:
905:                return port;
906:            }
907:
908:            /**
909:             * Saves changes resetting the original message id.
910:             *
911:             * @param message the message to save
912:             */
913:            protected final void saveChanges(MimeMessage message)
914:                    throws MessagingException {
915:                String messageId = message.getMessageID();
916:                message.saveChanges();
917:                if (messageId != null) {
918:                    message.setHeader(RFC2822Headers.MESSAGE_ID, messageId);
919:                }
920:            }
921:
922:            private void logMailInfo(Mail mail) {
923:
924:                // writes the error message to the log
925:                StringWriter sout = new StringWriter();
926:                PrintWriter out = new PrintWriter(sout, true);
927:
928:                out.print("Mail details:");
929:                out.print(" MAIL FROM: " + mail.getSender());
930:                Iterator rcptTo = mail.getRecipients().iterator();
931:                out.print(", RCPT TO: " + rcptTo.next());
932:                while (rcptTo.hasNext()) {
933:                    out.print(", " + rcptTo.next());
934:                }
935:
936:                log(sout.toString());
937:            }
938:
939:            private void logMessageInfo(MimeMessage mimeMessage) {
940:
941:                // writes the error message to the log
942:                StringWriter sout = new StringWriter();
943:                PrintWriter out = new PrintWriter(sout, true);
944:
945:                out.println("MimeMessage details:");
946:
947:                try {
948:                    if (mimeMessage.getSubject() != null) {
949:                        out.println("  Subject: " + mimeMessage.getSubject());
950:                    }
951:                    if (mimeMessage.getSentDate() != null) {
952:                        out
953:                                .println("  Sent date: "
954:                                        + mimeMessage.getSentDate());
955:                    }
956:                    String[] sender = null;
957:                    sender = mimeMessage.getHeader(RFC2822Headers.FROM);
958:                    if (sender != null) {
959:                        out.print("  From: ");
960:                        for (int i = 0; i < sender.length; i++) {
961:                            out.print(sender[i] + " ");
962:                        }
963:                        out.println();
964:                    }
965:                    String[] rcpts = null;
966:                    rcpts = mimeMessage.getHeader(RFC2822Headers.TO);
967:                    if (rcpts != null) {
968:                        out.print("  To: ");
969:                        for (int i = 0; i < rcpts.length; i++) {
970:                            out.print(rcpts[i] + " ");
971:                        }
972:                        out.println();
973:                    }
974:                    rcpts = mimeMessage.getHeader(RFC2822Headers.CC);
975:                    if (rcpts != null) {
976:                        out.print("  CC: ");
977:                        for (int i = 0; i < rcpts.length; i++) {
978:                            out.print(rcpts[i] + " ");
979:                        }
980:                        out.println();
981:                    }
982:                    out.print("  Size (in bytes): " + mimeMessage.getSize());
983:                    if (mimeMessage.getLineCount() >= 0) {
984:                        out.print(", Number of lines: "
985:                                + mimeMessage.getLineCount());
986:                    }
987:                } catch (MessagingException me) {
988:                    log("Exception caught reporting message details", me);
989:                }
990:
991:                log(sout.toString());
992:            }
993:
994:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.