clamd 0.0.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/Gemfile CHANGED
@@ -1 +1,3 @@
1
1
  source 'https://rubygems.org'
2
+ gemspec
3
+ gem 'rspec'
data/README.md CHANGED
@@ -1,8 +1,6 @@
1
1
  # Clamd
2
2
 
3
- Ruby gem to speak with Clamd daemon
4
-
5
- NOTE: Still under development
3
+ Ruby client to interact with ClamAV daemon
6
4
 
7
5
  ## Installation
8
6
 
@@ -14,76 +12,97 @@ And then execute:
14
12
 
15
13
  $ bundle
16
14
 
17
- Or install it yourself as:
15
+ Install clamd directly
18
16
 
19
17
  $ gem install clamd
20
18
 
21
19
  ## Configuration
22
20
 
23
- Refer the below code to configure the Clamd gem
24
-
25
- Clamd.configure do |config|
26
- config.host = "localhost" #mandatory
27
- config.port = 9321 #mandatory
28
- config.open_timeout = 5 #optional
29
- config.read_timeout = 20 #optional
30
- config.chunk_size = 10240 #optional
31
- end
21
+ Clamd by default connects to 9321 port in localhost. You can also configure the
22
+ host, port, open_timeout(seconds), read_timeout(seconds) and chunk_size(bytes).
23
+ Refer the following code to configure Clamd.
24
+
25
+ Client.configure do |config|
26
+ config.host = 'localhost'
27
+ config.port = 9321
28
+ config.open_timeout = 5
29
+ config.read_timeout = 20
30
+ config.chunk_size = 102400
31
+ end
32
32
 
33
33
  ## Usage
34
34
 
35
- To check ClamAV version
35
+ @clamd = Clamd::Client.new
36
36
 
37
- Clamd.version
37
+ ### PING
38
38
 
39
- Response will be like below
39
+ @clamd.ping
40
+ =>"PONG"
40
41
 
41
- =>"ClamAV 0.97.5/15468/Wed Oct 17 01:13:58 2012\n"
42
+ ### RELOAD
42
43
 
43
- To check Clamd is alive or not.
44
+ @clamd.reload
45
+ =>"RELOADING"
44
46
 
45
- Clamd.ping
47
+ ### SHUTDOWN
46
48
 
47
- Response from Clamd daemon will be "PONG" if it is alive
49
+ @clamd.shutdown
50
+ => true
48
51
 
49
- =>"PONG"
52
+ ### SCAN
50
53
 
51
- To reload Clamd virus signature database
54
+ @clamd.scan("/file/path")
55
+ =>"/file/path: OK"
52
56
 
53
- Clamd.reload
57
+ ### CONTSCAN
54
58
 
55
- Response from Clamd daemon will be "RELOADING"
59
+ @clamd.contscan("/file/path")
60
+ =>"/file/path: OK"
56
61
 
57
- =>"RELOADING"
62
+ ### MULTISCAN
63
+
64
+ @clamd.multiscan("/file/path")
65
+ =>"/file/path: OK"
58
66
 
59
- To shutdown Clamd daemon
67
+ ### INSTREAM
60
68
 
61
- Clamd.shutdown
69
+ @clamd.instream("/file/path/to/stream/to/clamd")
70
+ =>"stream: OK"
62
71
 
63
- Response will be blank if shutdown success
72
+ ### STATS
64
73
 
65
- =>""
74
+ @clamd.stats
75
+ => "POOLS: 1STATE: VALID PRIMARYTHREADS: live 1 idle 0 max 12 idle-timeout 30QUEUE: 0 items"
66
76
 
67
- To scan a file
77
+ ### VERSION
68
78
 
69
- Clamd.scan("/file/path")
70
- =>"/file/path: OK"
79
+ @clamd.version
80
+ => "ClamAV 0.97.8/18237/Sat Dec 14 11:13:16 2013"
71
81
 
72
- To use CONTSCAN facility of Clamd
82
+ ### Connecting multiple ClamdAV daemon
73
83
 
74
- Clamd.contscan("/file/path")
75
- =>"/file/path: OK"
84
+ You can also connect to multiple ClamdAV daemon running on different machine at
85
+ the same time.
76
86
 
77
- To use MULTISCAN facility of Clamd
87
+ @clamd1 = Clamd::Client.new(host: '192.16.20.11', port: 9321)
88
+ @clamd2 = Clamd::Client.new(host: '172.16.50.21', port: 8321)
78
89
 
79
- Clamd.multiscan("/file/path")
80
- =>"/file/path: OK"
90
+ @clamd1.ping
91
+ => "PONG"
81
92
 
82
- To use INSTREAM facility of Clamd
93
+ @clamd2.ping
94
+ => "PONG"
83
95
 
84
- Clamd.instream("/file/path/to/stream/to/clamd")
85
- =>"stream: OK"
86
-
87
- To know Clamd scan queue status
96
+ ## License
97
+
98
+ Clamd is released under the [MIT License](http://www.opensource.org/licenses/MIT).
99
+
100
+ ## Code Status
101
+
102
+ [![Code Climate](https://codeclimate.com/github/soundarapandian/clamd.png)](https://codeclimate.com/github/soundarapandian/clamd)
103
+
104
+ ## Test
105
+
106
+ Run spec
88
107
 
89
- Clamd.stats
108
+ rspec spec/
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.expand_path('../lib', File.dirname(__FILE__))
4
+
5
+ require 'irb'
6
+ require 'clamd'
7
+
8
+ ARGV.clear
9
+ IRB.start
@@ -1,19 +1,17 @@
1
1
  # -*- encoding: utf-8 -*-
2
- lib = File.expand_path('../lib', __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'clamd/version'
2
+ require File.expand_path('../lib/clamd/version', __FILE__)
5
3
 
6
4
  Gem::Specification.new do |gem|
7
- gem.name = "clamd"
5
+ gem.name = 'clamd'
8
6
  gem.version = Clamd::VERSION
9
- gem.authors = ["soundarapandian rathinasamy"]
10
- gem.email = ["soundar.rathinasamy@gmail.com"]
7
+ gem.authors = ['Soundarapandian Rathinasamy']
8
+ gem.email = ['soundar.rathinasamy@gmail.com']
11
9
  gem.description = %q{Ruby gem to interact with ClamAV daemon(Clamd)}
12
- gem.summary = %q{Clamd gem enables you to feed the simple VERSION command to
10
+ gem.summary = %q{Clamd gem enables you to feed the simple VERSION command to
13
11
  complex INSTREAM command to ClamAV antivirus daemon(Clamd)}
14
- gem.homepage = "https://github.com/soundarapandian/clamd"
15
-
12
+ gem.homepage = 'https://github.com/soundarapandian/clamd'
13
+ gem.license = 'MIT'
16
14
  gem.files = `git ls-files`.split($/)
17
15
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
- gem.require_paths = ["lib"]
16
+ gem.require_paths = ['lib']
19
17
  end
@@ -1,7 +1,35 @@
1
- require "clamd/command"
1
+ require "clamd/client"
2
2
  require "clamd/configuration"
3
3
  require "clamd/version"
4
4
 
5
5
  module Clamd
6
- extend Command
6
+ ##
7
+ # Used to configure the Clamd globally
8
+ #
9
+ # Usage:
10
+ #
11
+ # Clamd.configure do |config|
12
+ # config.host = 'localhost'
13
+ # config.port = 9321
14
+ # config.open_timeout = 5
15
+ # config.read_timeout = 10
16
+ # config.chunk_size = 10240
17
+ # end
18
+ def self.configure
19
+ @configuration ||= Configuration.new
20
+
21
+ yield(@configuration) if block_given?
22
+
23
+ @configuration
24
+ end
25
+
26
+ ##
27
+ # Gets current configuration details of Clamd
28
+
29
+ def self.configuration
30
+ @configuration
31
+ end
32
+
33
+ # Configure defaults
34
+ configure
7
35
  end
@@ -0,0 +1,179 @@
1
+ require 'timeout'
2
+ require 'clamd/socket_manager'
3
+
4
+ module Clamd
5
+ ##
6
+ # == Class that interacts with ClamdAV daemon
7
+ #
8
+ # Clamd::Client is responsible for interacting with ClamdAV daemon.
9
+ #
10
+ # For example you can connect ClamdAV daemon as given below
11
+ #
12
+ # clamd = Clamd::Client.new
13
+ # clamd.ping
14
+ #
15
+ # In the above example the ClamdAV daemon details will be get from the
16
+ # Clamd::Configuration
17
+ #
18
+ # You can also override the globla Clamd::Configuration as given below
19
+ #
20
+ # clamd = Clamd::Client.new(host: '172.15.20.11', port: 8321)
21
+ # clamd.ping
22
+
23
+ class Client
24
+ attr_accessor :host, :port, :open_timeout, :read_timeout, :chunk_size
25
+
26
+ ##
27
+ # Supported ClamdAV daemon commands
28
+
29
+ COMMAND = {
30
+ ping: 'PING',
31
+ version: 'VERSION',
32
+ reload: 'RELOAD',
33
+ shutdown: 'SHUTDOWN',
34
+ scan: 'SCAN',
35
+ contscan: 'CONTSCAN',
36
+ multiscan: 'MULTISCAN',
37
+ instream: 'zINSTREAM\0',
38
+ stats: 'zSTATS\0'
39
+ }.freeze
40
+
41
+ include SocketManager
42
+
43
+ def initialize(options = {})
44
+ self.host = options[:host] || Clamd.configuration.host
45
+ self.port = options[:port] || Clamd.configuration.port
46
+ self.open_timeout = options[:open_timeout] || Clamd.configuration.open_timeout
47
+ self.read_timeout = options[:read_timeout] || Clamd.configuration.read_timeout
48
+ self.chunk_size = options[:chunk_size] || Clamd.configuration.chunk_size
49
+ end
50
+
51
+ ##
52
+ # Method used to feed PING command to ClamdAV daemon
53
+ #
54
+ # Usage:
55
+ #
56
+ # clamd = Clamd::Client.new
57
+ # clamd.ping
58
+ def ping
59
+ exec(COMMAND[:ping])
60
+ end
61
+
62
+ ##
63
+ # Method used to feed VERSION command to ClamdAV daemon
64
+ #
65
+ # Usage:
66
+ #
67
+ # clamd = Clamd::Client.new
68
+ # clamd.version
69
+
70
+ def version
71
+ exec(COMMAND[:version])
72
+ end
73
+
74
+ ##
75
+ # Method used to feed RELOAD command to ClamdAV daemon
76
+ #
77
+ # Usage:
78
+ #
79
+ # clamd = Clamd::Client.new
80
+ # clamd.reload
81
+
82
+ def reload
83
+ exec(COMMAND[:reload])
84
+ end
85
+
86
+ ##
87
+ # Method used to feed SHUTDOWN command to ClamdAV daemon
88
+ #
89
+ # Usage:
90
+ #
91
+ # clamd = Clamd::Client.new
92
+ # clamd.shutdown
93
+ #
94
+ # Return value: +true+ on success +false+ on failure
95
+
96
+ def shutdown
97
+ exec(COMMAND[:shutdown])
98
+
99
+ ping =~ /^ERROR: Connection refused.*$/ ? true : false
100
+ end
101
+
102
+ ##
103
+ # Method used to feed SCAN command to ClamdAV daemon
104
+ #
105
+ # Usage:
106
+ #
107
+ # clamd = Clamd::Client.new
108
+ # clamd.scan('/home/soundar/documents/doc.pdf') (file)
109
+ # clamd.scan('/home/soundar/documents') (directory)
110
+
111
+ def scan(path)
112
+ exec(COMMAND[:scan], path)
113
+ end
114
+
115
+ ##
116
+ # Method used to feed CONTSCAN command to ClamdAV daemon
117
+ #
118
+ # Usage:
119
+ #
120
+ # clamd = Clamd::Client.new
121
+ # clamd.contscan('/home/soundar/documents/doc.pdf') (file)
122
+ # clamd.contscan('/home/soundar/documents') (directory)
123
+
124
+ def contscan(path)
125
+ exec(COMMAND[:contscan], path)
126
+ end
127
+
128
+ ##
129
+ # Method used to feed MULTISCAN command to ClamdAV daemon
130
+ #
131
+ # Usage:
132
+ #
133
+ # clamd = Clamd::Client.new
134
+ # clamd.multiscan('/home/soundar/documents/doc.pdf') # file
135
+ # clamd.multiscan('/home/soundar/documents') # directory
136
+
137
+ def multiscan(path)
138
+ exec(COMMAND[:multiscan], path)
139
+ end
140
+
141
+ ##
142
+ # Method used to feed INSTREAM command to ClamdAV daemon
143
+ #
144
+ # Usage:
145
+ #
146
+ # clamd = Clamd::Client.new
147
+ # clamd.instream('/home/soundar/documents/doc.pdf') # file
148
+
149
+ def instream(path)
150
+ exec(COMMAND[:instream], path)
151
+ end
152
+
153
+ ##
154
+ # Method used to feed STATS command to ClamdAV daemon
155
+ #
156
+ # Usage:
157
+ #
158
+ # clamd = Clamd::Client.new
159
+ # clamd.stats
160
+
161
+ def stats
162
+ exec(COMMAND[:stats])
163
+ end
164
+
165
+ private
166
+
167
+ def exec(command, path=nil)
168
+ begin
169
+ socket = Timeout::timeout(open_timeout) { open_socket(host, port) }
170
+ write_socket(socket, command, path)
171
+ Timeout::timeout(read_timeout) { read_socket(socket, command) }
172
+ rescue Exception => e
173
+ "ERROR: #{e.message.gsub('ERROR', '')}"
174
+ ensure
175
+ close_socket(socket) if socket
176
+ end
177
+ end
178
+ end
179
+ end
@@ -1,35 +1,24 @@
1
1
  module Clamd
2
2
  class Configuration
3
3
  attr_accessor :host, :port, :open_timeout, :read_timeout, :chunk_size
4
- DEFAULT_CONFIGURATION = { :open_timeout => 5, :read_timeout => 30, :chunk_size => 10240 }
5
4
 
6
- def initialize
7
- @open_timeout = DEFAULT_CONFIGURATION[:open_timeout]
8
- @read_timeout = DEFAULT_CONFIGURATION[:read_timeout]
9
- @chunk_size = DEFAULT_CONFIGURATION[:chunk_size]
10
- end
11
- end
12
-
13
- def self.configure
14
- @configuration ||= Configuration.new
15
- yield(@configuration) if block_given?
16
- missing = []
17
- missing << "host" unless @configuration.host
18
- missing << "port" unless @configuration.port
19
- raise ConfigurationError,
20
- "Missing configuration: #{missing.join(",")}" unless missing.empty?
21
- @configuration
22
- end
5
+ ##
6
+ # Default configuration for Clamd
23
7
 
24
- def self.configuration
25
- return unless @configuration
26
- @configuration
27
- end
8
+ DEFAULTS = {
9
+ host: 'localhost',
10
+ port: 9321,
11
+ open_timeout: 5,
12
+ read_timeout: 30,
13
+ chunk_size: 10240
14
+ }
28
15
 
29
- def self.configured?
30
- return true if configuration
31
- false
16
+ def initialize
17
+ self.host = DEFAULTS[:host]
18
+ self.port = DEFAULTS[:port]
19
+ self.open_timeout = DEFAULTS[:open_timeout]
20
+ self.read_timeout = DEFAULTS[:read_timeout]
21
+ self.chunk_size = DEFAULTS[:chunk_size]
22
+ end
32
23
  end
33
-
34
- class ConfigurationError < StandardError;end
35
24
  end
@@ -0,0 +1,87 @@
1
+ require 'socket'
2
+
3
+ module Clamd
4
+ module SocketManager
5
+ ##
6
+ # Opens socket for the given +host+ and +port+
7
+
8
+ def open_socket(host, port)
9
+ TCPSocket.open(host, port)
10
+ end
11
+
12
+ ##
13
+ # Closes the given socket
14
+
15
+ def close_socket(socket)
16
+ socket.close
17
+ end
18
+
19
+ ##
20
+ # Reads the response for the given command from the socket
21
+
22
+ def read_socket(socket, command)
23
+ socket.recv(clamd_response_size(command)).gsub(/(\u0000)|(\n)/, "").strip
24
+ end
25
+
26
+ ##
27
+ # Writes the command to the given sicket
28
+
29
+ def write_socket(socket, command, path)
30
+ if path && command != 'zINSTREAM\0'
31
+ socket.write("#{command} #{path}")
32
+ else
33
+ socket.write(command)
34
+ end
35
+ stream_to_clamd(socket, path) if command == 'zINSTREAM\0'
36
+ end
37
+
38
+ ##
39
+ # Determines the number of bytes to be read for the command
40
+
41
+ def clamd_response_size(command)
42
+ case command
43
+ when 'PING'
44
+ 4
45
+ when 'RELOAD'
46
+ 9
47
+ when 'SHUTDOWN'
48
+ 1
49
+ else
50
+ 1024
51
+ end
52
+ end
53
+
54
+ ##
55
+ # Streams file content to the ClamAV daemon
56
+
57
+ def stream_to_clamd(socket, path)
58
+ begin
59
+ file = File.open(path, "rb")
60
+ bytes = file.read(chunk_size)
61
+
62
+ while bytes
63
+ write_chunk(socket, bytes)
64
+ bytes = file.read(chunk_size)
65
+ end
66
+ stop_streaming(socket)
67
+ ensure
68
+ file.close if file
69
+ end
70
+ end
71
+
72
+ ##
73
+ # Writes the size of chunk(bytes), chunk(bytes) to the socket
74
+
75
+ def write_chunk(socket, chunk)
76
+ socket.write([chunk.size].pack("N"))
77
+ socket.write(chunk)
78
+ end
79
+
80
+ ##
81
+ # Stops streaming to ClamAV daemon
82
+
83
+ def stop_streaming(socket)
84
+ socket.write([0].pack("N"))
85
+ end
86
+ end
87
+ end
@@ -1,3 +1,3 @@
1
1
  module Clamd
2
- VERSION = "0.0.1"
2
+ VERSION = "1.0.0"
3
3
  end
Binary file
@@ -0,0 +1 @@
1
+ X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+
3
+ describe Clamd::Client do
4
+ let(:client) {described_class.new}
5
+ let(:file_path) {File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'clamdoc.pdf'))}
6
+ let(:directory_path) {File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'documents'))}
7
+ let(:file_with_virus_path) {File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'virus'))}
8
+
9
+ shared_examples 'virus scanner' do |mode|
10
+ it 'scans the given file' do
11
+ expect(client.send(mode, file_path)).to match(/^.*: OK$/)
12
+ end
13
+
14
+ it 'scans the given directory' do
15
+ expect(client.send(mode, directory_path)).to match(/^.*: OK$/)
16
+ end
17
+
18
+ it "reports virus if found" do
19
+ expect(client.send(mode, file_with_virus_path)).to match(/^.*: (.+?) FOUND$/)
20
+ end
21
+ end
22
+
23
+ describe '#ping' do
24
+ it 'gets PONG if ClamAV daemon alive' do
25
+ expect(client.ping).to eq('PONG')
26
+ end
27
+ end
28
+
29
+ describe '#version' do
30
+ it 'gets version of the ClamAV' do
31
+ expect(client.version).to match(/^ClamAV \d+.\d+.\d+/)
32
+ end
33
+ end
34
+
35
+ describe '#reload' do
36
+ it 'reloads ClamAV virus signature database' do
37
+ expect(client.reload).to eq('RELOADING')
38
+ end
39
+ end
40
+
41
+ describe '#scan' do
42
+ include_examples 'virus scanner', 'scan'
43
+ end
44
+
45
+ describe '#multiscan' do
46
+ include_examples 'virus scanner', 'multiscan'
47
+ end
48
+
49
+ describe '#contscan' do
50
+ include_examples 'virus scanner', 'contscan'
51
+ end
52
+
53
+ it 'supports to connect multiple ClamAV daemon with different configuration' do
54
+ clamd1 = described_class.new(host: 'localhost')
55
+ clamd2 = described_class.new(host: '127.0.0.1')
56
+
57
+ expect(clamd1.ping).to eq('PONG')
58
+ expect(clamd2.ping).to eq('PONG')
59
+ end
60
+
61
+ it 'reads global configuration if not specified for current client' do
62
+ clamd = described_class.new
63
+
64
+ expect(clamd.host).to eq('localhost')
65
+ expect(clamd.port).to eq(9321)
66
+ end
67
+ end
@@ -0,0 +1,15 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'clamd'
4
+
5
+ RSpec.configure do |config|
6
+ config.treat_symbols_as_metadata_keys_with_true_values = true
7
+ config.run_all_when_everything_filtered = true
8
+ config.filter_run :focus
9
+
10
+ # Run specs in random order to surface order dependencies. If you find an
11
+ # order dependency and want to debug it, you can fix the order by providing
12
+ # the seed, which is printed after each run.
13
+ # --seed 1234
14
+ config.order = 'random'
15
+ end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: clamd
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 1.0.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
8
- - soundarapandian rathinasamy
8
+ - Soundarapandian Rathinasamy
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-10-16 00:00:00.000000000 Z
12
+ date: 2013-12-19 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: Ruby gem to interact with ClamAV daemon(Clamd)
15
15
  email:
@@ -19,19 +19,25 @@ extensions: []
19
19
  extra_rdoc_files: []
20
20
  files:
21
21
  - .gitignore
22
+ - .rspec
22
23
  - Gemfile
23
- - LICENSE.txt
24
24
  - README.md
25
- - Rakefile
25
+ - bin/clamdc
26
26
  - clamd.gemspec
27
27
  - lib/clamd.rb
28
- - lib/clamd/command.rb
28
+ - lib/clamd/client.rb
29
29
  - lib/clamd/configuration.rb
30
- - lib/clamd/instream_helper.rb
31
- - lib/clamd/socket_utility.rb
30
+ - lib/clamd/socket_manager.rb
32
31
  - lib/clamd/version.rb
32
+ - spec/fixtures/clamdoc.pdf
33
+ - spec/fixtures/documents/clamdoc.pdf
34
+ - spec/fixtures/documents/clamdoc1.pdf
35
+ - spec/fixtures/virus
36
+ - spec/lib/clamd/client_spec.rb
37
+ - spec/spec_helper.rb
33
38
  homepage: https://github.com/soundarapandian/clamd
34
- licenses: []
39
+ licenses:
40
+ - MIT
35
41
  post_install_message:
36
42
  rdoc_options: []
37
43
  require_paths:
@@ -50,9 +56,15 @@ required_rubygems_version: !ruby/object:Gem::Requirement
50
56
  version: '0'
51
57
  requirements: []
52
58
  rubyforge_project:
53
- rubygems_version: 1.8.24
59
+ rubygems_version: 1.8.25
54
60
  signing_key:
55
61
  specification_version: 3
56
- summary: Clamd gem enables you to feed the simple VERSION command to complex INSTREAM
62
+ summary: Clamd gem enables you to feed the simple VERSION command to complex INSTREAM
57
63
  command to ClamAV antivirus daemon(Clamd)
58
- test_files: []
64
+ test_files:
65
+ - spec/fixtures/clamdoc.pdf
66
+ - spec/fixtures/documents/clamdoc.pdf
67
+ - spec/fixtures/documents/clamdoc1.pdf
68
+ - spec/fixtures/virus
69
+ - spec/lib/clamd/client_spec.rb
70
+ - spec/spec_helper.rb
@@ -1,22 +0,0 @@
1
- Copyright (c) 2012 soundarapandian rathinasamy
2
-
3
- MIT License
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining
6
- a copy of this software and associated documentation files (the
7
- "Software"), to deal in the Software without restriction, including
8
- without limitation the rights to use, copy, modify, merge, publish,
9
- distribute, sublicense, and/or sell copies of the Software, and to
10
- permit persons to whom the Software is furnished to do so, subject to
11
- the following conditions:
12
-
13
- The above copyright notice and this permission notice shall be
14
- included in all copies or substantial portions of the Software.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile DELETED
@@ -1 +0,0 @@
1
- require "bundler/gem_tasks"
@@ -1,100 +0,0 @@
1
- require 'timeout'
2
- require 'clamd/socket_utility'
3
- require 'clamd/instream_helper'
4
-
5
- module Clamd
6
- module Command
7
- COMMAND = {
8
- :ping => "PING",
9
- :version => "VERSION",
10
- :reload => "RELOAD",
11
- :shutdown => "SHUTDOWN",
12
- :scan => "SCAN",
13
- :contscan => "CONTSCAN",
14
- :multiscan => "MULTISCAN",
15
- :instream => "zINSTREAM\0",
16
- :stats => "zSTATS\0" }.freeze
17
-
18
- include SocketUtility
19
- include InstreamHelper
20
-
21
- def exec(command, path=nil)
22
- begin
23
- return "ERROR: Please configure Clamd first" unless Clamd.configured?
24
- socket = Timeout::timeout(Clamd.configuration.open_timeout) { open_socket }
25
- write_socket(socket, command, path)
26
- Timeout::timeout(Clamd.configuration.read_timeout) { read_socket(socket, command) }
27
- rescue Errno::ECONNREFUSED
28
- "ERROR: Failed to connect to Clamd daemon"
29
- rescue Errno::ECONNRESET, Errno::ECONNABORTED, Errno::EPIPE
30
- "ERROR: Connection with Clamd daemon closed unexpectedly"
31
- rescue Timeout::Error
32
- "ERROR: Timeout error occurred"
33
- ensure
34
- close_socket(socket) if socket
35
- end
36
- end
37
-
38
- def read_socket(socket, command)
39
- socket.recv(clamd_response_size(command)).gsub(/(\u0000)|(\n)/, "")
40
- end
41
-
42
- def write_socket(socket, command, path)
43
- if path && command != COMMAND[:instream]
44
- socket.write("#{command} #{path}")
45
- else
46
- socket.write(command)
47
- end
48
- stream_to_clamd(socket, path) if command == COMMAND[:instream]
49
- end
50
-
51
- def clamd_response_size(command)
52
- case command
53
- when COMMAND[:ping]
54
- 4
55
- when COMMAND[:reload]
56
- 9
57
- when COMMAND[:shutdown]
58
- 1
59
- else
60
- 1024
61
- end
62
- end
63
-
64
- def ping
65
- exec(COMMAND[:ping])
66
- end
67
-
68
- def version
69
- exec(COMMAND[:version])
70
- end
71
-
72
- def reload
73
- exec(COMMAND[:reload])
74
- end
75
-
76
- def shutdown
77
- exec(COMMAND[:shutdown])
78
- end
79
-
80
- def scan(path)
81
- exec(COMMAND[:scan], path)
82
- end
83
-
84
- def contscan(path)
85
- exec(COMMAND[:contscan], path)
86
- end
87
-
88
- def multiscan(path)
89
- exec(COMMAND[:multiscan], path)
90
- end
91
-
92
- def instream(path)
93
- exec(COMMAND[:instream], path)
94
- end
95
-
96
- def stats
97
- exec(COMMAND[:stats])
98
- end
99
- end
100
- end
@@ -1,26 +0,0 @@
1
- module Clamd
2
- module InstreamHelper
3
- def stream_to_clamd(socket, path)
4
- begin
5
- file = File.open(path, "rb")
6
- read = file.read(Clamd.configuration.chunk_size)
7
- while read
8
- write_chunk(socket, read)
9
- read = file.read(10240)
10
- end
11
- stop_streaming(socket)
12
- ensure
13
- file.close if file
14
- end
15
- end
16
-
17
- def write_chunk(socket, chunk)
18
- socket.write([chunk.size].pack("N"))
19
- socket.write(chunk)
20
- end
21
-
22
- def stop_streaming(socket)
23
- socket.write([0].pack("N"))
24
- end
25
- end
26
- end
@@ -1,13 +0,0 @@
1
- require 'socket'
2
-
3
- module Clamd
4
- module SocketUtility
5
- def open_socket
6
- TCPSocket.open(Clamd.configuration.host, Clamd.configuration.port)
7
- end
8
-
9
- def close_socket(socket)
10
- socket.close
11
- end
12
- end
13
- end