james-server-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From vince...@apache.org
Subject svn commit: r155194 - james/server/branches/branch_2_1_fcs/src/java/org/apache/james/transport/mailets/ClamAVScan.java
Date Thu, 24 Feb 2005 16:00:27 GMT
Author: vincenzo
Date: Thu Feb 24 08:00:26 2005
New Revision: 155194

URL: http://svn.apache.org/viewcvs?view=rev&rev=155194
Log:
ClamAV antivirus scanner mailet.

Added:
    james/server/branches/branch_2_1_fcs/src/java/org/apache/james/transport/mailets/ClamAVScan.java

Added: james/server/branches/branch_2_1_fcs/src/java/org/apache/james/transport/mailets/ClamAVScan.java
URL: http://svn.apache.org/viewcvs/james/server/branches/branch_2_1_fcs/src/java/org/apache/james/transport/mailets/ClamAVScan.java?view=auto&rev=155194
==============================================================================
--- james/server/branches/branch_2_1_fcs/src/java/org/apache/james/transport/mailets/ClamAVScan.java
(added)
+++ james/server/branches/branch_2_1_fcs/src/java/org/apache/james/transport/mailets/ClamAVScan.java
Thu Feb 24 08:00:26 2005
@@ -0,0 +1,927 @@
+/***********************************************************************
+ * Copyright (c) 2000-2004 The Apache Software Foundation.             *
+ * All rights reserved.                                                *
+ * ------------------------------------------------------------------- *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you *
+ * may not use this file except in compliance with the License. You    *
+ * may obtain a copy of the License at:                                *
+ *                                                                     *
+ *     http://www.apache.org/licenses/LICENSE-2.0                      *
+ *                                                                     *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS,   *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or     *
+ * implied.  See the License for the specific language governing       *
+ * permissions and limitations under the License.                      *
+ ***********************************************************************/
+
+package org.apache.james.transport.mailets;
+
+import org.apache.james.util.RFC2822Headers;
+import org.apache.mailet.GenericMailet;
+import org.apache.mailet.Mail;
+import org.apache.mailet.MailAddress;
+import org.apache.mailet.MailetException;
+
+import javax.mail.Address;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeBodyPart;
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeMultipart;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.ArrayList;
+import java.net.*;
+import java.io.*;
+
+
+/**
+ * <P>Does an antivirus scan check using a ClamAV daemon (CLAMD)</P>
+ * 
+ * <P> Interacts directly with the daemon using the <I>stream</I> method,
+ * which should have the lowest possible overhead.</P>
+ * <P>The CLAMD daemon will typically reside on <I>localhost</I>, but could
reside on a
+ * different host.
+ * It may also consist on a set of multiple daemons, each residing on a different
+ * server and on different IP number.
+ * In such case a DNS host name with multiple IP addresses (round-robin load sharing)
+ * is supported by the mailet (but on the same port number).</P>
+ * 
+ * <P>Handles the following init parameters:</P>
+ * <UL>
+ *    <LI>&lt;<CODE>debug</CODE>&gt;.</LI>
+ *    <LI>&lt;<CODE>host</CODE>&gt;: the host name of the server
where CLAMD runs. It can either be
+ *        a machine name, such as
+ *        "<code>java.sun.com</code>", or a textual representation of its
+ *        IP address. If a literal IP address is supplied, only the
+ *        validity of the address format is checked.
+ *        If the machine name resolves to multiple IP addresses, <I>round-robin load
sharing</I> will
+ *        be used.
+ *        The default is <CODE>localhost</CODE>.</LI>
+ *    <LI>&lt;<CODE>port</CODE>&gt;: the port on which CLAMD listens.
The default is <I>3310</I>.</LI>
+ *    <LI>&lt;<CODE>maxPings</CODE>&gt;: the maximum number of
connection retries during startup.
+ *        If the value is <I>0</I> no startup test will be done.
+ *        The default is <I>6</I>.</LI>
+ *    <LI>&lt;<CODE>pingIntervalMilli</CODE>&gt;: the interval
between each connection retry during startup.
+ *        The default is <I>30000</I> (30 seconds).</LI>
+ *    <LI>&lt;<CODE>streamBufferSize</CODE>&gt;: the BufferedOutputStream
buffer size to use 
+ *        writing to the <I>stream connection</I>. The default is <I>8192</I>.</LI>
+ * </UL>
+ * 
+ * <P>The actions performed are as follows:</P>
+ * <UL>
+ *    <LI>During initialization:</LI>
+ *    <OL>
+ *        <LI>Gets all <CODE>config.xml</CODE> parameters, handling the
defaults;</LI>
+ *        <LI>resolves the <CODE>host</CODE> parameter, creating the round-robin
IP list;</LI>
+ *        <LI>connects to CLAMD at the first IP in the round-robin list, on
+ *            the specified <CODE>port</CODE>;</LI>
+ *        <LI>if unsuccessful, retries every <CODE>pingIntervalMilli</CODE>
milliseconds up to
+ *            <CODE>maxPings</CODE> times;</LI>
+ *        <LI>sends a <CODE>PING</CODE> request;</LI>
+ *        <LI>waits for a <CODE>PONG</CODE> answer;</LI>
+ *        <LI>repeats steps 3-6 for every other IP resolved.
+ *    </OL>
+ *    <LI>For every mail</LI>
+ *    <OL>
+ *        <LI>connects to CLAMD at the "next" IP in the round-robin list, on
+ *            the specified <CODE>port</CODE>, and increments the "next" index;
+ *            if the connection request is not accepted tries with the next one
+ *            in the list unless all of them have failed;</LI>
+ *        <LI>sends a <CODE>STREAM</CODE> request;</LI>
+ *        <LI>parses the <CODE>PORT <I>streamPort</I></CODE>
answer obtaining the port number;</LI>
+ *        <LI>makes a second connection (the <I>stream connection</I>)
to CLAMD at the same host (or IP)
+ *            on the <I>streamPort</I> just obtained;</LI>
+ *        <LI>sends the mime message to CLAMD (using {@link MimeMessage#writeTo(OutputStream)})
+ *            through the <I>stream connection</I>;</LI>
+ *        <LI>closes the <I>stream connection</I>;</LI>
+ *        <LI>gets the <CODE>OK</CODE> or <CODE>FOUND</CODE>
answer from the main connection;</LI>
+ *        <LI>closes the main connection;</LI>
+ *        <LI>sets the <CODE>org.apache.james.infected</CODE> <I>mail
attribute</I> to either
+ *            <CODE>true</CODE> or <CODE>false</CODE>;</LI>
+ *        <LI>adds the <CODE>X-MessageIsInfected</CODE> <I>header</I>
to either
+ *            <CODE>true</CODE> or <CODE>false</CODE>;</LI>
+ *    </OL>
+ * </UL>
+ * 
+ * <P>Some notes regarding <a href="http://www.clamav.net/">clamav.conf</a>:</p>
+ * <UL>
+ *    <LI><CODE>LocalSocket</CODE> must be commented out</LI>
+ *    <LI><CODE>TCPSocket</CODE> must be set to a port# (typically 3310)</LI>
+ *    <LI><CODE>StreamMaxLength</CODE> must be &gt;= the James config.xml
parameter
+ *    &lt;<CODE>maxmessagesize</CODE>&gt; in SMTP &lt;<CODE>handler</CODE>&gt;</LI>
+ *    <LI><CODE>MaxThreads</CODE> should? be &gt;= the James config.xml
parameter
+ *    &lt;<CODE>threads</CODE>&gt; in &lt;<CODE>spoolmanager</CODE>&gt;</LI>
+ *    <LI><CODE>ScanMail</CODE> must be uncommented</LI>
+ * </UL>
+ *
+ * <P>Here follows an example of config.xml definitions deploying CLAMD on localhost,
+ * and handling the infected messages:</P>
+ * <PRE><CODE>
+ *
+ * ...
+ *
+ *    &lt;mailet match="All" class="ClamAVScan" onMailetException="ignore"&gt;
+ *
+ *    &lt;!-- If infected go to virus processor --&gt;
+ *    &lt;mailet match="HasMailAttributeWithValue=org.apache.james.infected, true" class="ToProcessor"&gt;
+ *       &lt;processor&gt; virus &lt;/processor&gt;
+ *    &lt;/mailet&gt;
+ *
+ * ...
+ *
+ * &lt;!-- Messages containing viruses --&gt;
+ *
+ * &lt;!-- To avoid a loop while bouncing --&gt;
+ * &lt;processor name="virus"&gt;
+ *    &lt;mailet match="All" class="SetMailAttribute"&gt;
+ *       &lt;org.apache.james.infected&gt;true, bouncing&lt;/org.apache.james.infected&gt;
+ *    &lt;/mailet&gt;
+ *
+ *    &lt;mailet match="SMTPAuthSuccessful" class="Bounce"&gt;
+ *       &lt;sender&gt;bounce-admin@xxx.com&lt;/sender&gt;
+ *       &lt;inline&gt;heads&lt;/inline&gt;
+ *       &lt;attachment&gt;none&lt;/attachment&gt;
+ *       &lt;notice&gt; Warning: We were unable to deliver the message below because
it was found infected by virus(es). &lt;/notice&gt;
+ *    &lt;/mailet&gt;
+ *
+ *    &lt;!--
+ *    &lt;mailet match="All" class="ToRepository"&gt;
+ *       &lt;repositoryPath&gt;file://var/mail/infected/&lt;/repositoryPath&gt;
+ *    &lt;/mailet&gt;
+ *    --&gt;
+ *
+ *    &lt;mailet match="All" class="Null" /&gt;
+ * &lt;/processor&gt;
+ * </CODE></PRE>
+ *
+ * @version 2.2.1
+ * @since 2.2.1
+ * @see <a href="http://www.clamav.net/">ClamAV Home Page</a>
+ * @see <a href="http://www.sosdg.org/clamav-win32/">ClamAV For Windows</a>
+ */
+public class ClamAVScan extends GenericMailet {
+    
+    private static final int DEFAULT_PORT = 3310;
+    
+    private static final int DEFAULT_MAX_PINGS = 6;
+    
+    private static final int DEFAULT_PING_INTERVAL_MILLI = 30000;
+    
+    private static final int DEFAULT_STREAM_BUFFER_SIZE = 8192;
+    
+    private static final String STREAM_PORT_STRING = "PORT ";
+    
+    private static final String FOUND_STRING = "FOUND";
+    
+    private static final String MAIL_ATTRIBUTE_NAME = "org.apache.james.infected";
+    
+    private static final String HEADER_NAME = "X-MessageIsInfected";
+    
+    /**
+     * Holds value of property debug.
+     */
+    private boolean debug;
+    
+    /**
+     * Holds value of property host.
+     */
+    private String host;
+    
+    /**
+     * Holds value of property port.
+     */
+    private int port;
+    
+    /**
+     * Holds value of property maxPings.
+     */
+    private int maxPings;
+    
+    /**
+     * Holds value of property pingIntervalMilli.
+     */
+    private int pingIntervalMilli;
+    
+    /**
+     * Holds value of property streamBufferSize.
+     */
+    private int streamBufferSize;
+
+    /**
+     * Holds value of property addresses.
+     */
+    private InetAddress[] addresses;
+    
+    /**
+     * Holds the index of the next address to connect to
+     */
+    private int nextAddressIndex;
+
+    /**
+     * Return a string describing this mailet.
+     *
+     * @return a string describing this mailet
+     */
+    public String getMailetInfo() {
+        return "Antivirus Check using ClamAV (CLAMD)";
+    }
+    
+    /** Gets the expected init parameters. */
+    protected  String[] getAllowedInitParameters() {
+        String[] allowedArray = {
+            //            "static",
+            "debug",
+                    "host",
+                    "port",
+                    "maxPings",
+                    "pingIntervalMilli",
+                    "streamBufferSize"
+        };
+        return allowedArray;
+    }
+    
+    /**
+     * Initializer for property debug.
+     */
+    protected void initDebug() {
+        String debugParam = getInitParameter("debug");
+        setDebug((debugParam == null) ? false : new Boolean(debugParam).booleanValue());
+    }
+    
+    /**
+     * Getter for property debug.
+     * @return Value of property debug.
+     */
+    public boolean isDebug() {
+        return this.debug;
+    }
+    
+    /**
+     * Setter for property debug.
+     * @param debug New value of property debug.
+     */
+    public void setDebug(boolean debug) {
+        this.debug = debug;
+    }
+    
+    /**
+     * Initializer for property host.
+     * @throws UnknownHostException if unable to resolve the host name, or if invalid
+     */
+    protected void initHost() throws UnknownHostException {
+        setHost(getInitParameter("host"));
+        if (isDebug()) {
+            log("host: " + getHost());
+        }
+    }
+    
+    /**
+     * Getter for property host.
+     * @return Value of property host.
+     */
+    public String getHost() {
+        
+        return this.host;
+    }
+    
+    /**
+     * Setter for property host.
+     * Resolves also the host name into the corresponding IP addresses, issues
+     * a {@link #setAddresses} and resets the <CODE>nextAddressIndex</CODE>
+     * variable to <I>0</I> for dealing with <I>round-robin</I>.
+     * @param host New value of property host.
+     * @throws UnknownHostException if unable to resolve the host name, or if invalid
+     */
+    public void setHost(String host) throws UnknownHostException {
+        
+        this.host = host;
+        
+        setAddresses(InetAddress.getAllByName(host));
+        
+        nextAddressIndex = 0;
+    }
+    
+    /**
+     * Initializer for property port.
+     */
+    protected void initPort() {
+        String portParam = getInitParameter("port");
+        setPort((portParam == null) ? DEFAULT_PORT : Integer.parseInt(portParam));
+        if (isDebug()) {
+            log("port: " + getPort());
+        }
+    }
+    
+    /**
+     * Getter for property port.
+     * @return Value of property port.
+     */
+    public int getPort() {
+        
+        return this.port;
+    }
+    
+    /**
+     * Setter for property port.
+     * @param port New value of property port.
+     */
+    public void setPort(int port) {
+        
+        this.port = port;
+    }
+    
+    /**
+     * Initializer for property maxPings.
+     */
+    protected void initMaxPings() {
+        String maxPingsParam = getInitParameter("maxPings");
+        setMaxPings((maxPingsParam == null) ? DEFAULT_MAX_PINGS : Integer.parseInt(maxPingsParam));
+        if (isDebug()) {
+            log("maxPings: " + getMaxPings());
+        }
+    }
+    
+    /**
+     * Getter for property maxPings.
+     * @return Value of property maxPings.
+     */
+    public int getMaxPings() {
+        
+        return this.maxPings;
+    }
+    
+    /**
+     * Setter for property maxPings.
+     * @param maxPings New value of property maxPings.
+     */
+    public void setMaxPings(int maxPings) {
+        
+        this.maxPings = maxPings;
+    }
+    
+    /**
+     * Initializer for property pingIntervalMilli.
+     */
+    protected void initPingIntervalMilli() {
+        String pingIntervalMilliParam = getInitParameter("pingIntervalMilli");
+        setPingIntervalMilli((pingIntervalMilliParam == null) ? DEFAULT_PING_INTERVAL_MILLI
: Integer.parseInt(pingIntervalMilliParam));
+        if (isDebug()) {
+            log("pingIntervalMilli: " + getPingIntervalMilli());
+        }
+    }
+    
+    /**
+     * Getter for property pingIntervalMilli.
+     * @return Value of property pingIntervalMilli.
+     */
+    public int getPingIntervalMilli() {
+        
+        return this.pingIntervalMilli;
+    }
+    
+    /**
+     * Setter for property pingIntervalMilli.
+     * @param pingIntervalMilli New value of property pingIntervalMilli.
+     */
+    public void setPingIntervalMilli(int pingIntervalMilli) {
+        
+        this.pingIntervalMilli = pingIntervalMilli;
+    }
+    
+    /**
+     * Initializer for property streamBufferSize.
+     */
+    protected void initStreamBufferSize() {
+        String streamBufferSizeParam = getInitParameter("streamBufferSize");
+        setStreamBufferSize((streamBufferSizeParam == null) ? DEFAULT_STREAM_BUFFER_SIZE
: Integer.parseInt(streamBufferSizeParam));
+        if (isDebug()) {
+            log("streamBufferSize: " + getStreamBufferSize());
+        }
+    }
+    
+    /**
+     * Getter for property streamBufferSize.
+     * @return Value of property streamBufferSize.
+     */
+    public int getStreamBufferSize() {
+        
+        return this.streamBufferSize;
+    }
+    
+    /**
+     * Setter for property streamBufferSize.
+     * @param streamBufferSize New value of property streamBufferSize.
+     */
+    public void setStreamBufferSize(int streamBufferSize) {
+        
+        this.streamBufferSize = streamBufferSize;
+    }
+    
+    /**
+     * Indexed getter for property addresses.
+     * @param index Index of the property.
+     * @return Value of the property at <CODE>index</CODE>.
+     */
+    protected InetAddress getAddresses(int index) {
+
+        return this.addresses[index];
+    }
+
+    /**
+     * Getter for property addresses.
+     * @return Value of property addresses.
+     */
+    protected InetAddress[] getAddresses() {
+
+        return this.addresses;
+    }
+
+    /**
+     * Setter for property addresses.
+     * @param addresses New value of property addresses.
+     */
+    protected void setAddresses(InetAddress[] addresses) {
+
+        this.addresses = addresses;
+    }
+    
+    /**
+     * Getter for property nextAddress.
+     * 
+     * Gets the address of the next CLAMD server to connect to in this round, using round-robin.
+     * Increments the nextAddressIndex for the next round.
+     * @return Value of property address.
+     */
+    protected synchronized InetAddress getNextAddress() {
+        
+        InetAddress address = getAddresses(nextAddressIndex);
+        
+        nextAddressIndex++;
+        if (nextAddressIndex >= getAddresses().length) {
+            nextAddressIndex = 0;
+        }
+        
+        return address;
+    }
+
+    /**
+     * Gets a Socket connected to CLAMD.
+     * 
+     * Will loop though the round-robin address list until the first one accepts
+     * the connection.
+     * @return a socket connected to CLAMD
+     * @throws MessagingException if no CLAMD in the round-robin address list has accepted
the connection
+     */
+    protected Socket getClamdSocket() throws MessagingException {
+        
+        InetAddress firstAddress = getNextAddress();
+        
+        InetAddress address = firstAddress;
+        do {
+            try {
+                // get the socket
+                return new Socket(address, getPort());
+            } catch (IOException ioe) {
+                log("Exception caught acquiring main socket to CLAMD on " + address + " on
port " + getPort() + ": " + ioe.getMessage());
+                address = getNextAddress();
+                // retry
+                continue;
+            }
+            
+        } while (address != firstAddress);
+        
+        String logText = "Unable to connect to CLAMD. All addresses failed.";
+        log(logText + " Giving up.");
+        throw new MessagingException(logText);
+    }
+    
+    /**
+     * Mailet initialization routine.
+     */
+    public void init() throws MessagingException {
+        
+        // check that all init parameters have been declared in allowedInitParameters
+        checkInitParameters(getAllowedInitParameters());
+        
+        try {
+            initDebug();
+            if (isDebug()) {
+                log("Initializing");
+            }
+            
+            initHost();
+            initPort();
+            initMaxPings();
+            initPingIntervalMilli();
+            initStreamBufferSize();
+            
+            // If "maxPings is > ping the CLAMD server to check if it is up
+            if (getMaxPings() > 0) {
+                ping();
+            }
+            
+        } catch (Exception e) {
+            log("Exception thrown", e);
+            throw new MessagingException("Exception thrown", e);
+        }
+        
+    }
+    
+    /**
+     * Scans the mail.
+     *
+     * @param mail the mail to scan
+     * @throws MessagingException if a problem arises
+     */
+    public void service(Mail mail) throws MessagingException {
+        
+        // if already checked no action
+        if (mail.getAttribute(MAIL_ATTRIBUTE_NAME) != null) {
+            return;
+        }
+        
+        MimeMessage mimeMessage = mail.getMessage();
+        
+        if (mimeMessage == null) {
+            log("Null MimeMessage. Will send to ghost");
+            // write mail info to log
+            logMailInfo(mail);
+            mail.setState(Mail.GHOST);
+            return;
+        }
+        
+        // get the socket
+        Socket socket = getClamdSocket();
+        BufferedReader reader = null;
+        PrintWriter writer = null;
+        Socket streamSocket = null;
+        BufferedOutputStream bos = null;
+            
+        try {
+            
+            // prepare the reader and writer for the commands
+            reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "ASCII"));
+            writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())),
true);
+            
+            // write a request for a port to use for streaming out the data to scan
+            writer.println("STREAM");
+            writer.flush();
+            
+            // parse and get the "stream" port#
+            int streamPort = getStreamPortFromAnswer(reader.readLine());
+            
+            // get the "stream" socket and the related (buffered) output stream
+            streamSocket = new Socket(socket.getInetAddress(), streamPort);
+            bos = new BufferedOutputStream(streamSocket.getOutputStream(), getStreamBufferSize());
+            
+            // stream out the message to the scanner
+            mimeMessage.writeTo(bos);
+            bos.flush();
+            bos.close();
+            streamSocket.close();
+            
+            String answer = null;
+            boolean virusFound = false;
+            String logMessage = "";
+            for (;;) {
+                answer = reader.readLine();
+                if (answer != null) {
+                    answer = answer.trim();
+                    
+                    // if a virus is found the answer will be '... FOUND'
+                    if (answer.substring(answer.length() - FOUND_STRING.length()).equals(FOUND_STRING))
{
+                        virusFound = true;
+                        logMessage = answer + " (by CLAMD on " + socket.getInetAddress()
+ ")";
+                        log(logMessage);
+                    }
+                } else {
+                    break;
+                }
+            }
+            
+            reader.close();
+            writer.close();
+            
+            if (virusFound) {
+                String errorMessage = mail.getErrorMessage();
+                if (errorMessage == null) {
+                    errorMessage = "";
+                } else {
+                    errorMessage += "\r\n";
+                }
+                StringBuffer sb = new StringBuffer(errorMessage);
+                sb.append(logMessage + "\r\n");
+                
+                // write mail and message info to log
+                logMailInfo(mail);
+                logMessageInfo(mimeMessage);
+                
+                // mark the mail with a mail attribute to check later on by other matchers/mailets
+                mail.setAttribute(MAIL_ATTRIBUTE_NAME, "true");
+                
+                // sets the error message to be shown in any "notifyXxx" message
+                mail.setErrorMessage(sb.toString());
+                
+                // mark the message with a header string
+                mimeMessage.setHeader(HEADER_NAME, "true");
+                
+            } else {
+                if (isDebug()) {
+                    log("OK (by CLAMD on " + socket.getInetAddress() + ")");
+                }
+                mail.setAttribute(MAIL_ATTRIBUTE_NAME, "false");
+                
+                // mark the message with a header string
+                mimeMessage.setHeader(HEADER_NAME, "false");
+                
+            }
+            
+            try {
+                saveChanges(mimeMessage);
+            } catch (Exception ex) {
+                log("Exception caught while saving changes (header) to the MimeMessage. Ignoring
...", ex);
+            }
+            
+        } catch (Exception ex) {
+            log("Exception caught calling CLAMD on " + socket.getInetAddress() + ": " + ex.getMessage(),
ex);
+            throw new MessagingException("Exception caught", ex);
+        } finally {
+            try {
+                if (reader != null) {
+                    reader.close();
+                }
+            } catch (Throwable t) {}
+            try {
+                if (writer != null) {
+                    writer.close();
+                }
+            } catch (Throwable t) {}
+            try {
+                if (bos != null) {
+                    bos.close();
+                }
+            } catch (Throwable t) {}
+            try {
+                if (streamSocket != null) {
+                    streamSocket.close();
+                }
+            } catch (Throwable t) {}
+            try {
+                if (socket != null) {
+                    socket.close();
+                }
+            } catch (Throwable t) {}
+        }
+        
+    }
+    
+    /**
+     * Checks if there are unallowed init parameters specified in the configuration file
+     * against the String[] allowedInitParameters.
+     * @param allowedArray array of strings containing the allowed parameter names
+     * @throws MessagingException if an unknown parameter name is found
+     */
+    protected final void checkInitParameters(String[] allowedArray) throws MessagingException
{
+        // if null then no check is requested
+        if (allowedArray == null) {
+            return;
+        }
+        
+        Collection allowed = new HashSet();
+        Collection bad = new ArrayList();
+        
+        for (int i = 0; i < allowedArray.length; i++) {
+            allowed.add(allowedArray[i]);
+        }
+        
+        Iterator iterator = getInitParameterNames();
+        while (iterator.hasNext()) {
+            String parameter = (String) iterator.next();
+            if (!allowed.contains(parameter)) {
+                bad.add(parameter);
+            }
+        }
+        
+        if (bad.size() > 0) {
+            throw new MessagingException("Unexpected init parameters found: "
+                    + arrayToString(bad.toArray()));
+        }
+    }
+    
+    /**
+     * Utility method for obtaining a string representation of an array of Objects.
+     */
+    private final String arrayToString(Object[] array) {
+        if (array == null) {
+            return "null";
+        }
+        StringBuffer sb = new StringBuffer(1024);
+        sb.append("[");
+        for (int i = 0; i < array.length; i++) {
+            if (i > 0) {
+                sb.append(",");
+            }
+            sb.append(array[i]);
+        }
+        sb.append("]");
+        return sb.toString();
+    }
+    
+    /**
+     * Tries to "ping" all the CLAMD daemons to
+     * check if they are up and accepting requests.
+     **/
+    
+    protected void ping() throws Exception {
+        
+        for (int i = 0; i < addresses.length; i++) {
+            ping(addresses[i]);
+        }
+    }
+    
+    /**
+     * Tries (and retries as specified up to 'getMaxPings()') to "ping" the specified CLAMD
daemon to
+     * check if it is up and accepting requests.
+     * @param address the address to "ping"
+     */
+    protected void ping(InetAddress address) throws Exception {
+        Socket socket = null;
+        
+        int ping = 1;
+        for (; ; ) {
+            if (isDebug()) {
+                log("Trial #" + ping + "/" + getMaxPings() + " - creating socket connected
to " + address + " on port " + getPort());
+            }
+            try {
+                socket = new Socket(address, getPort());
+                break;
+            } catch (ConnectException ce) {
+                log("Trial #" + ping + "/" + getMaxPings() + " - exception caught: " + ce.toString()
+ " while creating socket connected to " + address + " on port " + getPort());
+                ping++;
+                if (ping <= getMaxPings()) {
+                    log("Waiting " + getPingIntervalMilli() + " milliseconds before retrying
...");
+                    Thread.sleep(getPingIntervalMilli());
+                } else {
+                    break;
+                }
+            }
+        }
+        
+        // if 'socket' is still null then 'maxPings' has been exceeded
+        if (socket == null) {
+            throw new ConnectException("maxPings exceeded: " + getMaxPings() + ". Giving
up.");
+        }
+        
+        try {
+            // get the reader and writer to ping and receive pong
+            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream(),
"ASCII"));
+            PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())),
true);
+            
+            log("Sending: \"PING\" to " + address + " ...");
+            writer.println("PING");
+            writer.flush();
+            
+            boolean pongReceived = false;
+            for (;;) {
+                String answer = reader.readLine();
+                if (answer != null) {
+                    answer = answer.trim();
+                    log("Received: \"" + answer + "\"");
+                    answer = answer.trim();
+                    if (answer.equals("PONG")) {
+                        pongReceived = true;
+                    }
+                    
+                } else {
+                    break;
+                }
+            }
+            
+            reader.close();
+            writer.close();
+            
+            if (!pongReceived) {
+                throw new ConnectException("Bad answer from \"PING\" probe: expecting \"PONG\"");
+            }
+        } finally {
+            socket.close();
+        }
+    }
+    
+    /**
+     * Parses the answer from a STREAM request and gets the port number.
+     *
+     * @param answer the answer from CLAMD containing the port number
+     * @return the port number for streaming out the data to scan
+     */
+    protected final int getStreamPortFromAnswer(String answer) throws ConnectException {
+        int port = -1;
+        if (answer != null && answer.startsWith(STREAM_PORT_STRING)) {
+            try {
+                port = Integer.parseInt(answer.substring(STREAM_PORT_STRING.length()));
+            } catch (NumberFormatException nfe) {
+                
+            }
+        }
+        
+        if (port <= 0) {
+            throw new ConnectException("\"PORT nn\" expected - unable to parse: " + "\""
+ answer + "\"");
+        }
+        
+        return port;
+    }
+    
+    /**
+     * Saves changes resetting the original message id.
+     *
+     * @param message the message to save
+     */
+    protected final void saveChanges(MimeMessage message) throws MessagingException {
+        String messageId = message.getMessageID();
+        message.saveChanges();
+        if (messageId != null) {
+            message.setHeader(RFC2822Headers.MESSAGE_ID, messageId);
+        }
+    }
+
+    private void logMailInfo(Mail mail) {
+        
+        // writes the error message to the log
+        StringWriter sout = new StringWriter();
+        PrintWriter out = new PrintWriter(sout, true);
+        
+        out.print("Mail details:");
+        out.print(" MAIL FROM: " + mail.getSender());
+        Iterator rcptTo = mail.getRecipients().iterator();
+        out.print(", RCPT TO: " + rcptTo.next());
+        while (rcptTo.hasNext()) {
+            out.print(", " + rcptTo.next());
+        }
+                
+        log(sout.toString());
+    }
+    
+    private void logMessageInfo(MimeMessage mimeMessage) {
+        
+        // writes the error message to the log
+        StringWriter sout = new StringWriter();
+        PrintWriter out = new PrintWriter(sout, true);
+        
+        out.println("MimeMessage details:");
+        
+        try {
+            if (mimeMessage.getSubject() != null) {
+                out.println("  Subject: " + mimeMessage.getSubject());
+            }
+            if (mimeMessage.getSentDate() != null) {
+                out.println("  Sent date: " + mimeMessage.getSentDate());
+            }
+            String[] sender = null;
+            sender = mimeMessage.getHeader(RFC2822Headers.FROM);
+            if (sender != null) {
+                out.print("  From: ");
+                for (int i = 0; i < sender.length; i++) {
+                    out.print(sender[i] + " ");
+                }
+                out.println();
+            }
+            String[] rcpts = null;
+            rcpts = mimeMessage.getHeader(RFC2822Headers.TO);
+            if (rcpts != null) {
+                out.print("  To: ");
+                for (int i = 0; i < rcpts.length; i++) {
+                    out.print(rcpts[i] + " ");
+                }
+                out.println();
+            }
+            rcpts = mimeMessage.getHeader(RFC2822Headers.CC);
+            if (rcpts != null) {
+                out.print("  CC: ");
+                for (int i = 0; i < rcpts.length; i++) {
+                    out.print(rcpts[i] + " ");
+                }
+                out.println();
+            }
+            out.print("  Size (in bytes): " + mimeMessage.getSize());
+            if (mimeMessage.getLineCount() >= 0) {
+                out.print(", Number of lines: " + mimeMessage.getLineCount());
+            }
+        } catch (MessagingException  me) {
+            log("Exception caught reporting message details", me);
+        }
+        
+        log(sout.toString());
+    }
+
+}
+



---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


Mime
View raw message