carterdte_smtp_filter 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +42 -0
- data/Rakefile +9 -0
- data/bin/carterdte_smtp_filter +66 -0
- data/carterdte_smtp_filter.gemspec +33 -0
- data/lib/carterdte_smtp_filter.rb +27 -0
- data/lib/carterdte_smtp_filter/api_client.rb +46 -0
- data/lib/carterdte_smtp_filter/config.rb +19 -0
- data/lib/carterdte_smtp_filter/dte.rb +90 -0
- data/lib/carterdte_smtp_filter/message.rb +65 -0
- data/lib/carterdte_smtp_filter/smtp_server.rb +33 -0
- data/lib/carterdte_smtp_filter/version.rb +3 -0
- data/o-ri/bundle +23 -0
- data/o-ri/bundler +23 -0
- data/test/fake_api.rb +69 -0
- data/test/fixtures/acuse.xml +108 -0
- data/test/fixtures/config.yml +13 -0
- data/test/fixtures/email_with_attachment_no_dte.eml +33 -0
- data/test/fixtures/envio_dte_33.xml +238 -0
- data/test/fixtures/guia_despacho.xml +222 -0
- data/test/fixtures/invalid_dte.xml +4 -0
- data/test/fixtures/mail.tmp +11 -0
- data/test/fixtures/mail_with_dte.eml +269 -0
- data/test/fixtures/mail_with_multiple_attachments.eml +283 -0
- data/test/fixtures/recepcion_dte_33.xml +200 -0
- data/test/fixtures/resultado_revision_sii.xml +17 -0
- data/test/test_api_client.rb +43 -0
- data/test/test_config.rb +20 -0
- data/test/test_dte.rb +79 -0
- data/test/test_helper.rb +277 -0
- data/test/test_message.rb +67 -0
- data/test/test_smtp_server.rb +42 -0
- metadata +252 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: bf26a99cf494c091ab984734d04020553fd7cbe2
|
4
|
+
data.tar.gz: cc6a525a307cabb04baee63c654f9705a3f5869d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 067f9613141e0dbca9111bc38913797f8fbbea550ee511e752eccb64367f5dc07ebeb5218f27096370997d8a6e0ae0fd57a0d43e80fa42259ea9d7535946135e
|
7
|
+
data.tar.gz: bbe85d37804f6565dce72353a6a9de274947f5a69b628c1ef7c31d69cd161ee98a45756f07f4c4f310db12118b06e4f84d6bae01323db3f85f68a2c36a2373b8
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Patricio Bruna
|
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/README.md
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# CarterdteSmtpFilter
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'carterdte_smtp_filter'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install carterdte_smtp_filter
|
18
|
+
|
19
|
+
## Configuration
|
20
|
+
|
21
|
+
```yaml
|
22
|
+
bind_address: 127.0.0.1
|
23
|
+
bind_port: 30024
|
24
|
+
return_host: 127.0.0.1
|
25
|
+
return_port: 30025
|
26
|
+
max_connections: 10
|
27
|
+
elasticsearch_host: 127.0.0.1
|
28
|
+
elasticsearch_port: 9200
|
29
|
+
api_user: pbruna@example.com
|
30
|
+
api_password: 123456
|
31
|
+
api_host: "api.dte.zboxapp.com"
|
32
|
+
log_file: "./test/tmp/carterdte_smtp_filter.log"
|
33
|
+
stand_alone: ""
|
34
|
+
```
|
35
|
+
|
36
|
+
## Contributing
|
37
|
+
|
38
|
+
1. Fork it ( https://github.com/[my-github-username]/carterdte_smtp_filter/fork )
|
39
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
40
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
41
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
42
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'carterdte_smtp_filter'
|
5
|
+
require 'optparse'
|
6
|
+
|
7
|
+
# Parsing options
|
8
|
+
ARGV << '-h' if ARGV.empty?
|
9
|
+
|
10
|
+
options = {}
|
11
|
+
|
12
|
+
optparse = OptionParser.new do |opts|
|
13
|
+
|
14
|
+
opts.banner = "Usage: carterdte_smtp_filter --config [YAML Config File]"
|
15
|
+
|
16
|
+
opts.on("-cCONFIG", "--config=CONFIG", "Yaml Config File") do |o|
|
17
|
+
options[:config] = o
|
18
|
+
end
|
19
|
+
|
20
|
+
opts.on("-h", "--help", "Prints this help") do
|
21
|
+
puts opts
|
22
|
+
exit
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
optparse.parse!
|
28
|
+
# We load the configuration
|
29
|
+
if options[:config]
|
30
|
+
CarterdteSmtpFilter::Config.parse(options[:config])
|
31
|
+
|
32
|
+
logger = CarterdteSmtpFilter.logger
|
33
|
+
logger.info("#{Time.now}: Starting CarterDte SMTP Filter")
|
34
|
+
|
35
|
+
# Create a new server instance
|
36
|
+
server = CarterdteSmtpFilter::SmtpServer.new()
|
37
|
+
|
38
|
+
# Start the server
|
39
|
+
server.start
|
40
|
+
|
41
|
+
# Wait a second
|
42
|
+
sleep 1
|
43
|
+
|
44
|
+
# Run forever and ever
|
45
|
+
server.join
|
46
|
+
|
47
|
+
# setup exit code
|
48
|
+
|
49
|
+
end
|
50
|
+
BEGIN {
|
51
|
+
at_exit {
|
52
|
+
# check to shutdown connection
|
53
|
+
if server
|
54
|
+
# Output for debug
|
55
|
+
puts("#{Time.now}: Shutdown CarterDte SMTP Filter...")
|
56
|
+
# gracefully connections down
|
57
|
+
server.shutdown
|
58
|
+
# check once if some connection(s) need(s) more time
|
59
|
+
sleep 2 unless server.connections == 0
|
60
|
+
# stop all threads and connections
|
61
|
+
server.stop
|
62
|
+
puts("#{Time.now}: CarterDte SMTP Filter down!")
|
63
|
+
end
|
64
|
+
# Output for debug
|
65
|
+
}
|
66
|
+
}
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'carterdte_smtp_filter/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "carterdte_smtp_filter"
|
8
|
+
spec.version = CarterdteSmtpFilter::VERSION
|
9
|
+
spec.authors = ["Patricio Bruna"]
|
10
|
+
spec.email = ["pbruna@gmail.com"]
|
11
|
+
spec.summary = "Postfix SMTP Filter to parse DTE files to CarterDTE Platform"
|
12
|
+
spec.description = "Description - #{spec.summary}"
|
13
|
+
spec.homepage = "https://github.com/ZBoxApp/carterdte_smtp_filter"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency 'mail', "~> 2.6"
|
22
|
+
spec.add_dependency "midi-smtp-server", "~> 2.0"
|
23
|
+
spec.add_dependency 'xml-simple', "~> 1.1"
|
24
|
+
spec.add_dependency "rest-client", "~> 1.7"
|
25
|
+
|
26
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
27
|
+
spec.add_development_dependency "rake", "~> 10.4"
|
28
|
+
spec.add_development_dependency "guard", "~> 2.12"
|
29
|
+
spec.add_development_dependency "guard-minitest", "~> 2.4"
|
30
|
+
spec.add_development_dependency "minitest-reporters", "~> 1.0"
|
31
|
+
spec.add_development_dependency "sinatra", "~> 1.4"
|
32
|
+
spec.add_development_dependency "webmock", "~> 1.20"
|
33
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "midi-smtp-server"
|
2
|
+
require "mail"
|
3
|
+
require 'json'
|
4
|
+
require 'xmlsimple'
|
5
|
+
require 'time'
|
6
|
+
require 'date'
|
7
|
+
require 'logger'
|
8
|
+
require 'rest-client'
|
9
|
+
|
10
|
+
require "carterdte_smtp_filter/version"
|
11
|
+
require "carterdte_smtp_filter/config"
|
12
|
+
require "carterdte_smtp_filter/smtp_server"
|
13
|
+
require "carterdte_smtp_filter/message"
|
14
|
+
require "carterdte_smtp_filter/dte"
|
15
|
+
require "carterdte_smtp_filter/api_client"
|
16
|
+
|
17
|
+
module CarterdteSmtpFilter
|
18
|
+
|
19
|
+
def self.logger
|
20
|
+
logger_dest = CarterdteSmtpFilter::Config::log_file.nil? ? "/dev/null" : CarterdteSmtpFilter::Config::log_file
|
21
|
+
@logger = Logger.new(logger_dest)
|
22
|
+
@logger.datetime_format = '%Y-%m-%d %H:%M:%S'
|
23
|
+
@logger.formatter = proc { |severity, datetime, progname, msg| "#{datetime}: [#{severity}] #{msg.chomp}\n" }
|
24
|
+
@logger
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module CarterdteSmtpFilter
|
2
|
+
|
3
|
+
module ApiClient
|
4
|
+
require 'rest-client'
|
5
|
+
|
6
|
+
def self.logger
|
7
|
+
CarterdteSmtpFilter.logger
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.push(message)
|
11
|
+
post({payload: message})
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.api_user
|
15
|
+
CarterdteSmtpFilter::Config::api_user
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.api_host
|
19
|
+
CarterdteSmtpFilter::Config::api_host
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.api_password
|
23
|
+
CarterdteSmtpFilter::Config::api_password
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.post(opts = {})
|
27
|
+
url = opts[:url] || "https://#{CarterdteSmtpFilter::Config::api_host}/dtes"
|
28
|
+
payload = opts[:payload] || {}
|
29
|
+
begin
|
30
|
+
# We make sure we are sending JSON
|
31
|
+
JSON.parse payload
|
32
|
+
logger.info("Post #{payload} to #{url}")
|
33
|
+
resource = RestClient::Resource.new url, api_user, api_password
|
34
|
+
return if CarterdteSmtpFilter::Config::testing
|
35
|
+
response = resource.post payload, :content_type => :json, :accept => :json, :verify_ssl => OpenSSL::SSL::VERIFY_NONE
|
36
|
+
logger.info("Api response #{response}")
|
37
|
+
rescue Exception => e
|
38
|
+
logger.error("#{e} - #{url}")
|
39
|
+
response = false
|
40
|
+
end
|
41
|
+
response
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module CarterdteSmtpFilter
|
2
|
+
|
3
|
+
module Config
|
4
|
+
attr_accessor :config
|
5
|
+
OPTIONS = %w(bind_address bind_port return_host return_port elasticsearch_host elasticsearch_port max_connections debug api_user api_password api_host log_file stand_alone testing)
|
6
|
+
|
7
|
+
def self.parse(file = nil)
|
8
|
+
@config = YAML.load_file(file)
|
9
|
+
end
|
10
|
+
|
11
|
+
OPTIONS.each do |op|
|
12
|
+
self.class.instance_eval do
|
13
|
+
define_method(op) {@config[op].nil? ? false : @config[op].to_s}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module CarterdteSmtpFilter
|
2
|
+
|
3
|
+
class Dte
|
4
|
+
|
5
|
+
VALID_NODES = %w(SetDTE Resultado)
|
6
|
+
attr_accessor :dte_hash
|
7
|
+
|
8
|
+
def initialize(xml_string)
|
9
|
+
@dte_hash = XmlSimple.xml_in xml_string
|
10
|
+
return false unless valid?
|
11
|
+
end
|
12
|
+
|
13
|
+
def valid?
|
14
|
+
(dte_hash.keys & VALID_NODES).any?
|
15
|
+
end
|
16
|
+
|
17
|
+
def envio?
|
18
|
+
msg_type == "envio"
|
19
|
+
end
|
20
|
+
|
21
|
+
def respuesta?
|
22
|
+
msg_type == "respuesta"
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_json
|
26
|
+
return JSON.generate({}) unless valid?
|
27
|
+
JSON.generate({
|
28
|
+
rut_receptor: rut_receptor,
|
29
|
+
rut_emisor: rut_emisor,
|
30
|
+
msg_type: msg_type,
|
31
|
+
setdte_id: setdte_id,
|
32
|
+
dte_type: dte_type,
|
33
|
+
fecha_emision: fecha_emision,
|
34
|
+
fecha_recepcion: fecha_recepcion
|
35
|
+
})
|
36
|
+
end
|
37
|
+
|
38
|
+
def root_name
|
39
|
+
(dte_hash.keys & VALID_NODES).first
|
40
|
+
end
|
41
|
+
|
42
|
+
def root_node
|
43
|
+
@dte_hash[root_name].first
|
44
|
+
end
|
45
|
+
|
46
|
+
def get_data(data)
|
47
|
+
result = root_node["Caratula"].first[data].first if envio?
|
48
|
+
result = root_node["RecepcionEnvio"].first[data].first if respuesta?
|
49
|
+
return result.first if result.is_a? Array
|
50
|
+
result
|
51
|
+
end
|
52
|
+
|
53
|
+
def msg_type
|
54
|
+
return "respuesta" if root_name == "Resultado"
|
55
|
+
return "envio" if root_name == "SetDTE"
|
56
|
+
end
|
57
|
+
|
58
|
+
def setdte_id
|
59
|
+
return root_node["ID"] if envio?
|
60
|
+
get_data "EnvioDTEID"
|
61
|
+
end
|
62
|
+
|
63
|
+
def rut_emisor
|
64
|
+
get_data "RutEmisor"
|
65
|
+
end
|
66
|
+
|
67
|
+
def rut_receptor
|
68
|
+
get_data "RutReceptor"
|
69
|
+
end
|
70
|
+
|
71
|
+
def dte_type
|
72
|
+
return root_node["Caratula"].first["SubTotDTE"].first["TpoDTE"].first if envio?
|
73
|
+
return root_node["RecepcionEnvio"].first["RecepcionDTE"].first["TipoDTE"].first if respuesta?
|
74
|
+
end
|
75
|
+
|
76
|
+
def fecha_emision
|
77
|
+
time_stamp = get_data "TmstFirmaEnv" if envio?
|
78
|
+
time_stamp = root_node["Caratula"].first["TmstFirmaResp"].first if respuesta?
|
79
|
+
Time.parse(time_stamp).to_date
|
80
|
+
end
|
81
|
+
|
82
|
+
def fecha_recepcion
|
83
|
+
return nil if envio?
|
84
|
+
time_stamp = get_data "FchRecep"
|
85
|
+
Time.parse(time_stamp).to_date
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module CarterdteSmtpFilter
|
2
|
+
|
3
|
+
class Message
|
4
|
+
|
5
|
+
attr_accessor :raw_data, :qid, :email, :response, :dte
|
6
|
+
|
7
|
+
def initialize(raw_data)
|
8
|
+
set_mail_defaults
|
9
|
+
@raw_data = raw_data
|
10
|
+
@email = Mail.read_from_string raw_data
|
11
|
+
@dte = extract_dte
|
12
|
+
@logger = CarterdteSmtpFilter.logger
|
13
|
+
@qid = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def extract_dte
|
17
|
+
return false unless @email.attachments.any?
|
18
|
+
file = @email.attachments.select {|m| m.sub_type == "xml"}.first
|
19
|
+
return false unless file
|
20
|
+
CarterdteSmtpFilter::Dte.new file.body.decoded
|
21
|
+
end
|
22
|
+
|
23
|
+
def process
|
24
|
+
return_email
|
25
|
+
return unless has_dte?
|
26
|
+
extract_qid_from_response
|
27
|
+
end
|
28
|
+
|
29
|
+
def has_dte?
|
30
|
+
@dte ? true : false
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_json
|
34
|
+
JSON.generate({
|
35
|
+
to: @email.to,
|
36
|
+
from: @email.from,
|
37
|
+
cc: @email.cc,
|
38
|
+
date: @email.date.to_s,
|
39
|
+
qid: qid,
|
40
|
+
dte: JSON.parse(@dte.to_json)
|
41
|
+
})
|
42
|
+
end
|
43
|
+
|
44
|
+
def set_mail_defaults
|
45
|
+
Mail.defaults do
|
46
|
+
delivery_method :smtp, address: CarterdteSmtpFilter::Config::return_host, port: CarterdteSmtpFilter::Config::return_port,
|
47
|
+
return_response: true, enable_starttls_auto: false
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def extract_qid_from_response
|
52
|
+
return false unless response.status == "250"
|
53
|
+
# We suppose the string is like "250 2.0.0 Ok: queued as J5D0AWOR4F8\n"
|
54
|
+
return false unless /Ok: queued as/.match response.string
|
55
|
+
@qid = response.string.split(/\s+/).last
|
56
|
+
end
|
57
|
+
|
58
|
+
def return_email
|
59
|
+
@logger.info("Returning email <#{email.message_id}> to #{CarterdteSmtpFilter::Config::return_host}:#{CarterdteSmtpFilter::Config::return_port}")
|
60
|
+
@response = email.deliver!
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|