Alfresco Virus Checker action

What do you think about a missing feature in Alfresco – a working document Virus Checker action?

Alfresco Virus Checker Action, select action

Features:
– Java based Alfresco action
– Configurable, scriptable virus checker backend interface (works with ClamAV too)

Alfresco Virus Checker Action, result

Let see an example Action Executer:

/*
 * Copyright (C) 2009 louise@louise.hu
 */

package hu.louise.VirusChecker;

import java.io.*;
import java.io.IOException;
import java.io.Serializable;
import java.io.File;
import java.io.BufferedWriter;
import java.util.List;
import java.io.InputStream;
import java.util.Locale;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Map;
import java.text.SimpleDateFormat;
import java.util.Calendar;

import java.lang.Runtime;

import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.ParameterDefinitionImpl;
import org.alfresco.repo.action.executer.ActionExecuterAbstractBase;
import org.alfresco.repo.content.*;

import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.namespace.QName;

import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ParameterDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.ActionImpl;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.BaseSpringTest;
import org.alfresco.util.GUID;
import org.alfresco.service.cmr.repository.ChildAssociationRef;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


/**
 * Alfresco Action executer.
 * 
 * @version 1.62
 */

public class VirusCheckerActionExecuter extends ActionExecuterAbstractBase {
    /** The name of the action */
    public static final String NAME = "VirusChecker-action";

    /** shell script name with full path */
    public static final String VirusCheckerScript = "/opt/clamav/check_clamav.sh";
    
    /** parameter's name */
    public static final String PARAM_TESTMODE = "param-testmode";

    /** name space */
    public static final String NAMESPACE = "???";

    /** test mode flag */
    // private boolean VirusCheckerTestMode = false;

    /** The logger */
    private static Log logger = LogFactory.getLog("hu.louise.VirusCheckerAction");

    /** node service object */
    private NodeService nodeService;

    /** content service object */
    private ContentService contentService;

    /**
     * set the node's service (Alfresco internal use)
     * 
     * @param nodeService
     */
    public void setNodeService(NodeService nodeService) {
        this.nodeService = nodeService;
    }

    /**
     * set the content's service (Alfresco internal use)
     * 
     * @param contentService
     */
    public void setContentService(ContentService contentService) {
        this.contentService = contentService;
    }

    /**
     * This action will check node for Virus infection
     */
    @Override
    protected void executeImpl(Action action, NodeRef actionedUponNodeRef) {
        // check for valid node
        if (actionedUponNodeRef != null && // check node
                this.nodeService.exists(actionedUponNodeRef))                                            // aspect
        {
            Serializable plainFileName = this.nodeService.getProperty(actionedUponNodeRef, ContentModel.PROP_NAME);
            logger.debug("*** NodeName: " + plainFileName);

            String pfname = plainFileName.toString();
            String baseName = pfname.substring(0, pfname.lastIndexOf("."));

            // get the content of the document
            ContentReader reader = this.contentService.getReader(actionedUponNodeRef, ContentModel.PROP_CONTENT);
            //reader.setEncoding("UTF-8");
            
            InputStream data=null;
            data=reader.getContentInputStream();

            /* Write content to temp file */
            String tempFileName = null;
            File f = null;
            try {
	            // File f = new File(tempFileName);
	            f = File.createTempFile("VCA_", "_"+pfname);
	            tempFileName = f.getCanonicalPath();
	            
	            // f.deleteOnExit();	// Removing when alfresco exists...

	            OutputStream out=new FileOutputStream(f);
	            
	            byte buf[]=new byte[1024];
	            int len;
	            while((len=data.read(buf))>0) {
	            	out.write(buf,0,len);
	            }
	            out.close();
            } catch(Exception e) {
                System.out.println("Write content to file: " + e);
            }

            try {
            	data.close();
            } catch(Exception e) {
                System.out.println("Close input stream: " + e);
            }
                        
            /* Execute shell script to virus check */
            int retCode = -1;
            Runtime rtime = Runtime.getRuntime();
            try {
                logger.info("*** VirusCheck shell script: " + VirusCheckerScript + " " + tempFileName);
            	Process child = rtime.exec(VirusCheckerScript + " " + tempFileName);

                /* To get the return code from the shell script */
                retCode = child.waitFor();

                logger.info("*** VirusCheck result code: " + retCode);
            }
            catch(Exception e) {
                System.out.println("Executing script: " + e);
            }

            /* Set content aspect/name by result of virus check */
            logger.info("*** TODO: set return value to node and timestamp of checking procedure");
            if(retCode == 1) {
                logger.info("*** Document is infected: " + pfname);
                this.nodeService.setProperty(actionedUponNodeRef,
                		ContentModel.PROP_NAME,
						"VIRUS_INFECTED-"+pfname);
            } else {
            	if(retCode == 0) {
            		logger.info("*** Document is clean: " + pfname);
            	} else {
            		// other answer code from virus checker
            		// TODO: ???
            	}
            }
            
            /* Remove temp file */
            logger.info("*** Remove temp file: " + tempFileName);
            if(f!=null) {
            	f.delete();
            }
        } else {
            logger.error("Something is wrong with node reference (not exists or missing signable aspect!");
        }
    }

    /**
     * @see org.alfresco.repo.action.
     *      ParameterizedItemAbstractBase#addParameterDefinitions(java.util.List)
     */
    @Override
    protected void addParameterDefinitions(List paramList) {
        // Specify the parameters
        paramList.add(new ParameterDefinitionImpl(PARAM_TESTMODE, DataTypeDefinition.BOOLEAN, false,
                getParamDisplayLabel(PARAM_TESTMODE)));
    }
    
    /**
     * Log trace into logger.error.
     * 
     * @param exception
     *            exception
     */
    protected static void LogBackTrace(Exception exception) {
        LogBackTrace(exception, exception.getLocalizedMessage());
    }

    /**
     * Log trace into logger.error with customized message.
     * 
     * @param exception
     *            exception
     * @param message
     *            error message
     */
    protected static void LogBackTrace(Exception exception, String message) {
        logger.error(message);
        for (StackTraceElement element : exception.getStackTrace())
            message += "\n\tat " + element;
        logger.debug(message);
    }

 
    /**
     * 
     * Convert InputStream to byte[] 
     *
     * @param in
     * @return
     * @throws IOException
     */
    public static byte[] ConvertInputStreamtoByteArray(InputStream in) throws IOException {
	    StringBuffer out = new StringBuffer();
	    byte[] b = new byte[4096];
	    
	    for (int n; (n = in.read(b)) != -1;) {
	    	out.append(new String(b, 0, n));
	    }
	    
	    return out.toString().getBytes();
    }
    
    public static final String DATE_FORMAT_NOW = "yyyyMMddHHmmss";

    /**
     * returns with the current date
     * 
     * 
     * @return Date and Time of Now
     */
    public static String now() {
      Calendar cal = Calendar.getInstance();
      
      SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT_NOW);
      
      return sdf.format(cal.getTime());
    }
}

6 thoughts on “Alfresco Virus Checker action”

  1. Hello,

    Is the antivirus action GPL avaialable ? I Have now built a solution where CLAMAV scans the files via de CIFS or Webdav interface, I can scan and remove the document i a proper way, but I Can not inform the repository or the document owner that a document was found infected and removed.

    I tried to build my own Antivirus workflow, but Iam not that well trained in java and scripting.

    I like to hear from you.

    With kind regards.

    Remco Hannink
    The Netherlands

Leave a Reply

Your email address will not be published. Required fields are marked *