clamd 0.0.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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