clamav.module in ClamAV 6
Same filename and directory in other branches
Integrate ClamAV to allow uploaded files to be scanned for viruses.
File
clamav.moduleView source
<?php
/**
* @file
* Integrate ClamAV to allow uploaded files to be scanned for viruses.
*/
// Scan using a ClamAV Daemon
define('CLAMAV_USE_DAEMON', 0);
// Scan using a ClamAV executable
define('CLAMAV_USE_EXECUTABLE', 1);
// Default: use Daemon-mode
define('CLAMAV_DEFAULT_MODE', CLAMAV_USE_EXECUTABLE);
// Behaviour if the ClamAV scanner is unavailable or does not respond.
// Prevent unchecked files from being uploaded
define('CLAMAV_BLOCK_UNCHECKED', 0);
// Allow unchecked files to be uploaded
define('CLAMAV_ALLOW_UNCHECKED', 1);
// Default behaviour for unchecked files - Block unchecked files.
define('CLAMAV_DEFAULT_UNCHECKED', CLAMAV_BLOCK_UNCHECKED);
// Default host (in Daemon-mode)
define('CLAMAV_DEFAULT_HOST', 'localhost');
// Default port (in Daemon-mode)
define('CLAMAV_DEFAULT_PORT', 3310);
// Default path (in Executable-mode)
define('CLAMAV_DEFAULT_PATH', '/usr/bin/clamscan');
// The file was not checked (e.g. because the AV daemon wasn't running).
define('CLAMAV_SCANRESULT_UNCHECKED', -1);
// The file was checked and found to be clean.
define('CLAMAV_SCANRESULT_CLEAN', 0);
// The file was checked and found to be infected.
define('CLAMAV_SCANRESULT_INFECTED', 1);
/**
* Implementation of hook_help().
*/
function clamav_help($path, $arg) {
$output = '';
switch ($path) {
case "admin/help#clamav":
$output .= '<p>' . t('Clam AntiVirus is an open source anti-virus toolkit for UNIX.') . '</p>';
$output .= '<p>' . t('The ClamAV module allows files which are uploaded to Drupal to be scanned by Clam AntiVirus.') . '<br />';
$output .= t('The module does not install ClamAV - visit <a href="http://www.clamav.net/">the ClamAV website</a> for help installing ClamAV.') . '</p>';
break;
case 'admin/settings/clamav':
break;
}
return $output;
}
/**
* Implementation of hook_menu().
*/
function clamav_menu() {
return array(
'clamav/upload/js' => array(
'page callback' => 'clamav_upload_js',
'access arguments' => array(
'upload files',
),
'type' => MENU_CALLBACK,
),
'admin/settings/clamav' => array(
'title' => 'Anti-virus (ClamAV)',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'clamav_admin_settings',
),
'access arguments' => array(
'administer site configuration',
),
'file' => 'clamav.admin.inc',
),
);
}
/**
* Implementation of hook_elements().
*/
function clamav_elements() {
$elements = array();
// support the core 'file' FAPI element.
if (variable_get('clamav_enable_element_file', TRUE)) {
$elements['file']['#element_validate'] = array(
'clamav_elements_file_validate',
);
}
return $elements;
}
/**
* Form element validator for the file FAPI type.
*
* @param Array $element
*/
function clamav_elements_file_validate($element) {
$key = $element['#parents'][0];
if (is_array($_FILES['files']['tmp_name']) && array_key_exists($key, $_FILES['files']['tmp_name']) && !empty($_FILES['files']['tmp_name'][$key])) {
$filepath = $_FILES['files']['tmp_name'][$key];
// filepath to the uploaded file
$form_error_key = implode('][', $element['#parents']);
// form-element to use with form_set_error
require_once dirname(__FILE__) . '/clamav.inc';
clamav_scan($filepath, $form_error_key);
}
}
/**
* Implementation of hook_form_alter().
* Validation of CCK filefield and imagefield is applied here.
*/
function clamav_form_alter(&$form, $form_state, $form_id) {
if (isset($form['type']) && isset($form['#node']) && $form['type']['#value'] . '_node_form' == $form_id) {
// Check for CCK integration and handle supported CCK fields.
if (module_exists('content')) {
$type = content_types($form['#node']->type);
if (!empty($type['fields'])) {
foreach ($type['fields'] as $field_name => $field) {
$function = 'clamav_' . $field['widget']['type'] . '_alter';
if (function_exists($function)) {
$function($form[$field_name]);
}
}
}
}
if (isset($form['attachments'])) {
// Replace upload.module submission handler with a custom one that can
// refuse to attach files containing viruses
foreach ($form['#submit'] as $key => $handler) {
if ($handler == 'upload_node_form_submit') {
$form['#submit'][$key] = 'clamav_upload_node_form_submit';
}
}
// Override default upload/js callback to add clamav validation
$form['attachments']['wrapper']['new']['attach']['#ahah']['path'] = 'clamav/upload/js';
}
}
}
/**
* Implementation of our custom hook_FIELDNAME_alter for filefield widget.
*/
function clamav_filefield_widget_alter(&$field) {
if (module_exists('filefield') && variable_get('clamav_enable_element_filefield_widget', TRUE)) {
$field[0]['#upload_validators']['clamav_elements_filefield_validate'] = array();
}
}
/**
* Implementation of our custom hook_FIELDNAME_alter for filefield widget.
*/
function clamav_imagefield_widget_alter(&$field) {
if (module_exists('imagefield') && variable_get('clamav_enable_element_imagefield_widget', TRUE)) {
$field[0]['#upload_validators']['clamav_elements_filefield_validate'] = array();
}
}
/**
* Validator for filefield widget and imagefield widget.
* This is an implementation of a file upload_validator.
*/
function clamav_elements_filefield_validate($file) {
$filepath = $file->filepath;
$form_error_key = $file->source;
require_once dirname(__FILE__) . '/clamav.inc';
$result = clamav_scan_file($filepath);
$errors = array();
if ($result == CLAMAV_SCANRESULT_INFECTED) {
$errors[] = t('A virus has been detected in the file. The file will not be accepted.');
}
elseif ($result == CLAMAV_SCANRESULT_UNCHECKED && variable_get('clamav_unchecked_files', CLAMAV_DEFAULT_UNCHECKED) == CLAMAV_BLOCK_UNCHECKED) {
$errors[] = t('The anti-virus scanner was not able to check the file. The file cannot be uploaded.');
}
return $errors;
}
/**
* Validator for upload.module attachments.
*
* Nearly an exact copy of upload_node_form_submit() in upload.module
*/
function clamav_upload_node_form_submit(&$form, &$form_state) {
global $user;
$limits = _upload_file_limits($user);
$validators = array(
'clamav_elements_filefield_validate' => array(),
'file_validate_extensions' => array(
$limits['extensions'],
),
'file_validate_image_resolution' => array(
$limits['resolution'],
),
'file_validate_size' => array(
$limits['file_size'],
$limits['user_size'],
),
);
// Save new file uploads.
if (user_access('upload files') && ($file = file_save_upload('upload', $validators, file_directory_path()))) {
$file->list = variable_get('upload_list_default', 1);
$file->description = $file->filename;
$file->weight = 0;
$file->new = TRUE;
$form['#node']->files[$file->fid] = $file;
$form_state['values']['files'][$file->fid] = (array) $file;
}
if (isset($form_state['values']['files'])) {
foreach ($form_state['values']['files'] as $fid => $file) {
// If the node was previewed prior to saving, $form['#node']->files[$fid]
// is an array instead of an object. Convert file to object for compatibility.
$form['#node']->files[$fid] = (object) $form['#node']->files[$fid];
$form_state['values']['files'][$fid]['new'] = !empty($form['#node']->files[$fid]->new);
}
}
// Order the form according to the set file weight values.
if (!empty($form_state['values']['files'])) {
$microweight = 0.001;
foreach ($form_state['values']['files'] as $fid => $file) {
if (is_numeric($fid)) {
$form_state['values']['files'][$fid]['#weight'] = $file['weight'] + $microweight;
$microweight += 0.001;
}
}
uasort($form_state['values']['files'], 'element_sort');
}
}
/**
* Menu-callback for JavaScript-based uploads.
*
* Nearly an exact copy of upload_js() in upload.module
*/
function clamav_upload_js() {
$cached_form_state = array();
$files = array();
// Load the form from the Form API cache.
if (!($cached_form = form_get_cache($_POST['form_build_id'], $cached_form_state)) || !isset($cached_form['#node']) || !isset($cached_form['attachments'])) {
form_set_error('form_token', t('Validation error, please try again. If this error persists, please contact the site administrator.'));
$output = theme('status_messages');
print drupal_to_js(array(
'status' => TRUE,
'data' => $output,
));
exit;
}
$form_state = array(
'values' => $_POST,
);
// Handle new uploads, and merge tmp files into node-files.
clamav_upload_node_form_submit($cached_form, $form_state);
if (!empty($form_state['values']['files'])) {
foreach ($form_state['values']['files'] as $fid => $file) {
if (isset($cached_form['#node']->files[$fid])) {
$files[$fid] = $cached_form['#node']->files[$fid];
}
}
}
$node = $cached_form['#node'];
$node->files = $files;
$form = _upload_form($node);
unset($cached_form['attachments']['wrapper']['new']);
$cached_form['attachments']['wrapper'] = array_merge($cached_form['attachments']['wrapper'], $form);
$cached_form['attachments']['#collapsed'] = FALSE;
form_set_cache($_POST['form_build_id'], $cached_form, $cached_form_state);
foreach ($files as $fid => $file) {
if (is_numeric($fid)) {
$form['files'][$fid]['description']['#default_value'] = $form_state['values']['files'][$fid]['description'];
$form['files'][$fid]['list']['#default_value'] = !empty($form_state['values']['files'][$fid]['list']);
$form['files'][$fid]['remove']['#default_value'] = !empty($form_state['values']['files'][$fid]['remove']);
$form['files'][$fid]['weight']['#default_value'] = $form_state['values']['files'][$fid]['weight'];
}
}
// Render the form for output.
$form += array(
'#post' => $_POST,
'#programmed' => FALSE,
'#tree' => FALSE,
'#parents' => array(),
);
drupal_alter('form', $form, array(), 'upload_js');
$form_state = array(
'submitted' => FALSE,
);
$form = form_builder('upload_js', $form, $form_state);
$output = theme('status_messages') . drupal_render($form);
// We send the updated file attachments form.
// Don't call drupal_json(). ahah.js uses an iframe and
// the header output by drupal_json() causes problems in some browsers.
print drupal_to_js(array(
'status' => TRUE,
'data' => $output,
));
exit;
}
Functions
Name | Description |
---|---|
clamav_elements | Implementation of hook_elements(). |
clamav_elements_filefield_validate | Validator for filefield widget and imagefield widget. This is an implementation of a file upload_validator. |
clamav_elements_file_validate | Form element validator for the file FAPI type. |
clamav_filefield_widget_alter | Implementation of our custom hook_FIELDNAME_alter for filefield widget. |
clamav_form_alter | Implementation of hook_form_alter(). Validation of CCK filefield and imagefield is applied here. |
clamav_help | Implementation of hook_help(). |
clamav_imagefield_widget_alter | Implementation of our custom hook_FIELDNAME_alter for filefield widget. |
clamav_menu | Implementation of hook_menu(). |
clamav_upload_js | Menu-callback for JavaScript-based uploads. |
clamav_upload_node_form_submit | Validator for upload.module attachments. |