vpopmail 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/README ADDED
@@ -0,0 +1,59 @@
1
+ = VPOPMail
2
+
3
+ VPOPMail is a library that helps to manipulate vpopmail (http://www.inter7.com/vpopmail), a software package to provide an easy way to manage virtual email domains.
4
+
5
+ This library can be used to write email filters, spam learners, etc.
6
+
7
+ = Requirements
8
+
9
+ * Ruby 1.8.* (or newer). Only tested under Linux, should be fine under any Unix.
10
+
11
+ * Installed {vpopmail}[http://www.inter7.com/vpopmail] software.
12
+ * Installed {spamassassin}[http://spamassassin.apache.org] for spam learning.
13
+
14
+ = Gem requirements
15
+
16
+ {RubyMail}[http://rubyforge.org/projects/rubymail] 0.17 or newer
17
+
18
+ = Documentation
19
+
20
+ The documentation is in RDoc format in the doc subdirectory.
21
+
22
+ = Install
23
+
24
+ Type the following while in the package directory:
25
+
26
+ ruby install.rb config
27
+ ruby install.rb setup
28
+ ruby install.rb install
29
+
30
+ You may need special permissions to execute the last line. If you
31
+ want to just install RubyMail to a custom location, just copy the
32
+ rmail subdirectory manually.
33
+
34
+ = Support
35
+
36
+ To reach the author of ruby-vpopmail, send mail to gildas@breizh.org.
37
+
38
+ = License
39
+
40
+ Copyright (c) 2006 Gildas Cherruel
41
+
42
+ Permission is hereby granted, free of charge, to any person obtaining
43
+ a copy of this software and associated documentation files (the
44
+ "Software"), to deal in the Software without restriction, including
45
+ without limitation the rights to use, copy, modify, merge, publish,
46
+ distribute, sublicense, and/or sell copies of the Software, and to
47
+ permit persons to whom the Software is furnished to do so, subject to
48
+ the following conditions:
49
+
50
+ The above copyright notice and this permission notice shall be
51
+ included in all copies or substantial portions of the Software.
52
+
53
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
54
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
55
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
56
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
57
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
58
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
59
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,102 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/rdoctask'
4
+ require 'rake/packagetask'
5
+ require 'rake/gempackagetask'
6
+ require 'rake/contrib/rubyforgepublisher'
7
+ require File.join(File.dirname(__FILE__), 'lib', 'vpopmail')
8
+
9
+ PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
10
+ PKG_NAME = 'vpopmail'
11
+ PKG_VERSION = VPOPMail::VERSION::STRING + PKG_BUILD
12
+ PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
13
+
14
+ RELEASE_NAME = "REL #{PKG_VERSION}"
15
+
16
+ RUBY_FORGE_PROJECT = "ruby-vpopmail"
17
+ RUBY_FORGE_USER = "gildasc"
18
+
19
+ PKG_FILES = FileList[
20
+ "lib/**/*", "test/**/*", "examples/**/*", "doc/**/*", "[A-Z]*", "install.rb", "Rakefile"
21
+ ].exclude(/\bCVS\b|~$/)
22
+
23
+
24
+ # Generate the RDoc documentation
25
+
26
+ Rake::RDocTask.new { |rdoc|
27
+ rdoc.rdoc_dir = 'doc'
28
+ rdoc.title = "Ruby VPOPMail -- VPOPMail interface for Ruby"
29
+ rdoc.options << '--line-numbers' << '--inline-source'
30
+ rdoc.template = "#{ENV['template']}.rb" if ENV['template']
31
+ rdoc.rdoc_files.include('README')
32
+ rdoc.rdoc_files.include('lib/**/*.rb')
33
+ }
34
+
35
+ # Create compressed packages
36
+
37
+ spec = Gem::Specification.new do |s|
38
+ s.name = PKG_NAME
39
+ s.version = PKG_VERSION
40
+ s.summary = "Ruby VPOPMail interface"
41
+ s.description = %q{VPOPMail interface for Ruby. Allows to write filters, spam learners, etc. using the vpopmail software package.}
42
+
43
+ s.files = FileList["{lib,examples}/**/*"].exclude("rdoc").to_a
44
+ s.files += [ "Rakefile", "install.rb", "README" ]
45
+
46
+ s.add_dependency('rmail', '>= 0.17' + PKG_BUILD)
47
+
48
+ s.require_path = 'lib'
49
+ s.autorequire = 'vpopmail'
50
+
51
+ s.has_rdoc = true
52
+ s.extra_rdoc_files = %w( README )
53
+ s.rdoc_options.concat ['--main', 'README']
54
+
55
+ s.author = "Gildas Cherruel"
56
+ s.email = "gildas@breizh.org"
57
+ s.homepage = "http://ruby-vpopmail.rubyforge.org"
58
+ s.rubyforge_project = "ruby-vpopmail"
59
+ end
60
+
61
+ Rake::GemPackageTask.new(spec) do |p|
62
+ p.gem_spec = spec
63
+ p.need_tar = true
64
+ p.need_zip = true
65
+ end
66
+
67
+ task :lines do
68
+ lines, codelines, total_lines, total_codelines = 0, 0, 0, 0
69
+
70
+ for file_name in FileList["lib/vpopmail/**/*.rb"]
71
+ f = File.open(file_name)
72
+
73
+ while line = f.gets
74
+ lines += 1
75
+ next if line =~ /^\s*$/
76
+ next if line =~ /^\s*#/
77
+ codelines += 1
78
+ end
79
+ puts "L: #{sprintf("%4d", lines)}, LOC #{sprintf("%4d", codelines)} | #{file_name}"
80
+
81
+ total_lines += lines
82
+ total_codelines += codelines
83
+
84
+ lines, codelines = 0, 0
85
+ end
86
+
87
+ puts "Total: Lines #{total_lines}, LOC #{total_codelines}"
88
+ end
89
+
90
+
91
+ # Publishing ------------------------------------------------------
92
+
93
+ desc "Publish the release files to RubyForge."
94
+ task :release => [ :package ] do
95
+ `rubyforge login`
96
+
97
+ for ext in %w( gem tgz zip )
98
+ release_command = "rubyforge add_release #{PKG_NAME} #{PKG_NAME} 'REL #{PKG_VERSION}' pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}"
99
+ puts release_command
100
+ system(release_command)
101
+ end
102
+ end
@@ -0,0 +1,11 @@
1
+ require 'log4r'
2
+ require 'vpopmail'
3
+
4
+ logname = File.basename(__FILE__, '.rb')
5
+ log = Log4r::Logger.new(logname)
6
+ log.add(Log4r::FileOutputter.new(logname, :filename => logname + '.log', :level => Log4r::DEBUG))
7
+ VPOPMail::logger = log
8
+
9
+ log.info "Starting emailcheck"
10
+ valid = VPOPMail::Message.validAddress?(ARGV[0], 'postmaster@breizh.org')
11
+ puts "Address #{ARGV[0]} is " + (valid ? "valid" : "invalid")
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/env ruby
2
+ require 'getoptlong'
3
+ require 'vpopmail'
4
+
5
+ $CFG = { "Junk Folder" => "Junk E-mails",
6
+ "Learn SPAM Folder" => "Learn Junk",
7
+ "Learn HAM Folder" => "Learn Good",
8
+ "archive user" => "postmaster",
9
+ "archive SPAM Folder" => "SPAM",
10
+ "archive HAM Folder" => "HAM",
11
+ }
12
+
13
+ $VERBOSE = true
14
+ $OPTS = GetoptLong.new(
15
+ ["--domain", "-d", GetoptLong::REQUIRED_ARGUMENT],
16
+ ["--folder", "-f", GetoptLong::REQUIRED_ARGUMENT],
17
+ ["--ham", GetoptLong::NO_ARGUMENT],
18
+ ["--help", "-h", GetoptLong::NO_ARGUMENT],
19
+ ["--silent", "-s", GetoptLong::NO_ARGUMENT],
20
+ ["--spam", GetoptLong::NO_ARGUMENT],
21
+ ["--user", "-u", GetoptLong::REQUIRED_ARGUMENT],
22
+ ["--verbose", "-v", GetoptLong::NO_ARGUMENT])
23
+
24
+ #------------------------------------------------------------------------------------------
25
+ # function: Verbose {{{
26
+ def Verbose(p_string)
27
+ puts p_string if $VERBOSE
28
+ end # }}}
29
+
30
+ # function: usage {{{
31
+ def usage(p_error = nil, p_errno = 0, p_wantQuit = true)
32
+ puts "Usage: vlearn.rb [options] [filename]"
33
+ puts "Error: #{p_error}" if ! p_error.nil?
34
+ puts "Options:"
35
+ puts " -d domain, --domain=domain Will work only on this vpopmail domain. (Default: all)"
36
+ puts " -u user, --user=user Will work only on this vpopmail user. (Default: all)"
37
+ puts " --ham Will consider messages as good messages"
38
+ puts " --spam Will consider messages as spam/junk messages"
39
+ puts " -h, --help Prints this help."
40
+ puts " -s, --silent Proceed silently."
41
+ puts " -v, --verbose Proceed verbosely."
42
+ exit p_errno if p_wantQuit
43
+ end # }}}
44
+
45
+ # function: loadFolder {{{
46
+ def loadFolder(p_user, p_name = nil)
47
+ begin
48
+ return VPOPMail::Folder.new(p_user, p_name)
49
+ rescue Errno::ENOENT => p_e
50
+ Verbose "User postmaster: Folder #{p_name} does not exist"
51
+ return nil
52
+ end
53
+ end # }}}
54
+
55
+ # main {{{
56
+ domainname = nil
57
+ foldername = nil
58
+ username = nil
59
+ kind = nil
60
+
61
+ $OPTS.each {|opt, arg|
62
+ case opt
63
+ when "--domain" then domainname = arg
64
+ when "--folder" then foldername = arg
65
+ when "--user" then username = arg
66
+ when "--ham" then kind = "ham"
67
+ when "--spam" then kind = "spam"
68
+ when "--help" then usage()
69
+ when "--silent" then $VERBOSE = false
70
+ when "--verbose" then $VERBOSE = true
71
+ end
72
+ }
73
+
74
+ if !domainname.nil? and !username.nil? and !foldername.nil? then
75
+ domain = VPOPMail::Domain.FindDomains(domainname)[0]
76
+ Verbose "Analyzing domain #{domain.name}"
77
+ user = VPOPMail::User.FindUsers(domain, username)[0]
78
+ Verbose "Analyzing user #{user.name}"
79
+ folder = VPOPMail::Folder.new(user, foldername)
80
+ Verbose "Analyzing folder #{folder}, size = #{folder.size}"
81
+ messageid = ARGV[0]
82
+
83
+ if !messageid.nil? and !messageid.empty? then
84
+ message = VPOPMail::Message.new(folder, messageid)
85
+ Verbose "Processing: #{message.id}"
86
+ message.markAs(kind) if !kind.nil?
87
+ else
88
+ folder.each {|message|
89
+ Verbose "Processing: #{message.id}"
90
+ message.markAs(kind) if !kind.nil?
91
+ }
92
+ end
93
+ else
94
+ VPOPMail::Domain.FindDomains.each { |domain|
95
+ Verbose "Analyzing domain #{domain.name}"
96
+ archiveUser = VPOPMail::User.FindUsers(domain, $CFG["archive user"])[0]
97
+ archiveSPAM = loadFolder(archiveUser, $CFG["archive SPAM Folder"])
98
+ archiveHAM = loadFolder(archiveUser, $CFG["archive HAM Folder"])
99
+
100
+ VPOPMail::User.FindUsers(domain).each { |user|
101
+ Verbose "Analyzing user #{user.name}"
102
+ junkFolder = loadFolder(user, $CFG["Junk Folder"])
103
+ learnFolder = loadFolder(user, $CFG["Learn SPAM Folder"])
104
+ learnFolder.learnAs("spam", junkFolder, archiveSPAM) if !learnFolder.nil?
105
+
106
+ inboxFolder = loadFolder(user, nil)
107
+ learnFolder = loadFolder(user, $CFG["Learn HAM Folder"])
108
+ learnFolder.learnAs("ham", inboxFolder, archiveHAM) if !learnFolder.nil?
109
+ }
110
+ }
111
+ end
112
+ exit 0
113
+ # }}}
data/install.rb ADDED
@@ -0,0 +1,30 @@
1
+ require 'rbconfig'
2
+ require 'find'
3
+ require 'ftools'
4
+
5
+ include Config
6
+
7
+ # this was adapted from rdoc's install.rb by ways of Log4r
8
+
9
+ $sitedir = CONFIG["sitelibdir"]
10
+ unless $sitedir
11
+ version = CONFIG["MAJOR"] + "." + CONFIG["MINOR"]
12
+ $libdir = File.join(CONFIG["libdir"], "ruby", version)
13
+ $sitedir = $:.find {|x| x =~ /site_ruby/ }
14
+ if !$sitedir
15
+ $sitedir = File.join($libdir, "site_ruby")
16
+ elsif $sitedir !~ Regexp.quote(version)
17
+ $sitedir = File.join($sitedir, version)
18
+ end
19
+ end
20
+
21
+ # the acual gruntwork
22
+ Dir.chdir("lib")
23
+
24
+ Find.find("vpopmail", "vpopmail.rb") { |f|
25
+ if f[-3..-1] == ".rb"
26
+ File::install(f, File.join($sitedir, *f.split(/\//)), 0644, true)
27
+ else
28
+ File::makedirs(File.join($sitedir, *f.split(/\//)))
29
+ end
30
+ }
data/lib/vpopmail.rb ADDED
@@ -0,0 +1,47 @@
1
+ #--
2
+ # Copyright (c) 2006 Gildas Cherruel
3
+ # Read COPYRIGHT in the root of VPOPMail library for copyright info
4
+ # Creator: gildasc@rubyforg.org
5
+ # Author: $Author: gildasc $
6
+ # Version: $Id: vpopmail.rb 4 2006-10-07 23:18:13Z gildasc $
7
+ #++
8
+
9
+ $:.unshift(File.dirname(__FILE__)) unless
10
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
11
+
12
+ require 'vpopmail/domain'
13
+ require 'vpopmail/user'
14
+ require 'vpopmail/imapdb'
15
+ require 'vpopmail/folder'
16
+ require 'vpopmail/message'
17
+
18
+ #------------------------------------------------------------------------------------------
19
+ #------------------------------------------------------------------------------------------
20
+ # module: VPOPMail {{{
21
+ #++
22
+ # The VPOPMail module contains all classes used to manipulate {vpopmail}[http://www.inter7.com/vpopmail] objects
23
+ module VPOPMail
24
+
25
+ #--------------------------------------------------------------------------------------
26
+ # Versionning # {{{
27
+ module VERSION #:nodoc:
28
+ MAJOR = 1
29
+ MINOR = 0
30
+ TINY = 0
31
+ BUILD = %q$Id: vpopmail.rb 4 2006-10-07 23:18:13Z gildasc $
32
+ STRING = [MAJOR, MINOR, TINY].join('.')
33
+ end # }}}
34
+
35
+ #--------------------------------------------------------------------------------------
36
+ # module function: logger= {{{
37
+ #++
38
+ # Assigns the Logger object to all classes of the VPOPMail module.
39
+ # <tt>p_logger</tt> must comply to the Logger class.
40
+ def VPOPMail.logger=(p_logger)
41
+ Domain.logger = p_logger
42
+ User.logger = p_logger
43
+ Folder.logger = p_logger
44
+ Message.logger = p_logger
45
+ IMAPDB.logger = p_logger
46
+ end # }}}
47
+ end # }}}
@@ -0,0 +1,22 @@
1
+ #--
2
+ # Copyright (c) 2006 Gildas Cherruel
3
+ # Read COPYRIGHT in the root of VPOPMail library for copyright info
4
+ # Creator: gildasc@rubyforg.org
5
+ # Author: $Author: gildasc $
6
+ # Version: $Id: config.rb 4 2006-10-07 23:18:13Z gildasc $
7
+ #++
8
+
9
+
10
+ #------------------------------------------------------------------------------------------
11
+ # module: VPOPMail {{{
12
+ #++
13
+ module VPOPMail
14
+
15
+ # CFG contains default values for manipulating the vpopmail software package
16
+ CFG = { "VPOP Bin" => "/home/vpopmail/bin",
17
+ "SPAM Mark" => "***SPAM*** ",
18
+ "SPAM Subject Field" => "X-Spam-Prev-Subject",
19
+ "IMAPDB" => "courierimapuiddb",
20
+ }
21
+
22
+ end # }}}
@@ -0,0 +1,105 @@
1
+ #--
2
+ # Copyright (c) 2006 Gildas Cherruel
3
+ # Read COPYRIGHT in the root of VPOPMail library for copyright info
4
+ # Creator: gildasc@rubyforg.org
5
+ # Author: $Author: gildasc $
6
+ # Version: $Id: domain.rb 4 2006-10-07 23:18:13Z gildasc $
7
+ #++
8
+
9
+ require 'rmail'
10
+ require 'rexml/document'
11
+ require 'vpopmail/config'
12
+
13
+ #------------------------------------------------------------------------------------------
14
+ #------------------------------------------------------------------------------------------
15
+ # module: VPOPMail {{{
16
+ #++
17
+ module VPOPMail
18
+
19
+ #------------------------------------------------------------------------------------------
20
+ # class: Domain {{{
21
+ #++
22
+ # The Domain class represents a virtual domain in {vpopmail}[http://www.inter7.com/vpopmail]
23
+ class Domain
24
+ attr_reader :name, :path
25
+ attr_writer :name, :path
26
+
27
+ #--------------------------------------------------------------------------------------
28
+ # class attribute: logger {{{
29
+ #++
30
+ @@logger = nil
31
+ def self.logger ; @@logger ; end
32
+ def self.logger=(p_object) ; @@logger = p_object ; end
33
+ def logger(p_object) ; @@logger ; end
34
+ def logger=(p_object) ; @@logger = p_object ; end
35
+ # }}}
36
+
37
+ #----------------------------------------------------------------------------------
38
+ # method: initialize {{{
39
+ #++
40
+ # Creates a new Domain object
41
+ def initialize
42
+ @name = @path = ""
43
+ end # }}}
44
+
45
+ #----------------------------------------------------------------------------------
46
+ # class method: FindDomains {{{
47
+ #++
48
+ # Finds all virtual domains that match the pattern <tt>p_name</tt>. If the pattern
49
+ # is not present, finds all virtual domains.
50
+ # Returns an array of Domain objects.
51
+ def Domain.FindDomains(p_name = nil)
52
+ cmdString = "#{VPOPMail::CFG['VPOP Bin']}/vdominfo -n -d"
53
+ if !p_name.nil? and !p_name.empty? then
54
+ raise ArgumentError unless p_name.kind_of?(String)
55
+ cmdString = cmdString + " #{p_name}"
56
+ end
57
+ cmd = IO.popen(cmdString, "w+")
58
+ cmd.close_write
59
+
60
+ domains = Array.new
61
+ domain = Domain.new
62
+ getName = true
63
+ while line = cmd.gets do
64
+ line = line.gsub(/[\r\n]*$/, "")
65
+ next if line.empty?
66
+ if getName then
67
+ domain.name = line
68
+ else
69
+ domain.path = line
70
+ domains << domain
71
+ domain = Domain.new
72
+ end
73
+ getName = !getName
74
+ end
75
+ return domains
76
+ end # }}}
77
+
78
+ #----------------------------------------------------------------------------------
79
+ # method: postmaster {{{
80
+ #++
81
+ # Calculates the default postmaster email address if the Domain.
82
+ # If <tt>p_name</tt> is present, it will be used instead of 'postmaster'
83
+ # Returns a RMail::Address object
84
+ def postmaster(p_name = 'postmaster')
85
+ return RMail::Address.parse("#{name}@#{@name}")[0]
86
+ end # }}}
87
+
88
+ #----------------------------------------------------------------------------------
89
+ # method: to_xml {{{
90
+ #++
91
+ # Returns the REXML::Document that represents the Domain object
92
+ def to_xml
93
+ return REXML::Document.new("<Domain name=\"#{@name}\" path=\"#{@path}\" />")
94
+ end # }}}
95
+
96
+ #----------------------------------------------------------------------------------
97
+ # method: to_s {{{
98
+ #++
99
+ # Returns the String representation of the Domain object
100
+ def to_s
101
+ return "Domain #{@name} stored in #{@path}"
102
+ end # }}}
103
+ end # }}}
104
+
105
+ end # }}}
@@ -0,0 +1,131 @@
1
+ #--
2
+ # Copyright (c) 2006 Gildas Cherruel
3
+ # Read COPYRIGHT in the root of VPOPMail library for copyright info
4
+ # Creator: gildasc@rubyforg.org
5
+ # Author: $Author: gildasc $
6
+ # Version: $Id: folder.rb 4 2006-10-07 23:18:13Z gildasc $
7
+ #++
8
+
9
+ require 'rexml/document'
10
+ require 'vpopmail/config'
11
+ require 'vpopmail/domain'
12
+ require 'vpopmail/user'
13
+ require 'vpopmail/imapdb'
14
+ require 'vpopmail/message'
15
+
16
+ #------------------------------------------------------------------------------------------
17
+ #------------------------------------------------------------------------------------------
18
+ # module: VPOPMail {{{
19
+ #++
20
+ module VPOPMail
21
+
22
+ #------------------------------------------------------------------------------------------
23
+ # class: Folder {{{
24
+ #++
25
+ # The Folder class represents a Maildir folder
26
+ class Folder
27
+ attr_reader :name, :path, :curpath, :newpath, :tmppath, :imapdb, :user
28
+
29
+ #--------------------------------------------------------------------------------------
30
+ # class attribute: logger {{{
31
+ @@logger = nil
32
+ def self.logger ; @@logger ; end
33
+ def self.logger=(p_object) ; @@logger = p_object ; end
34
+ def logger(p_object) ; @@logger ; end
35
+ def logger=(p_object) ; @@logger = p_object ; end
36
+ # }}}
37
+
38
+ #----------------------------------------------------------------------------------
39
+ # method: initialize {{{
40
+ #++
41
+ # Creates a new Folder object after <tt>p_name</tt> and owned by the User <tt>p_user</tt>
42
+ # If <tt>p_name</tt> is not given opens the Inbox.
43
+ #
44
+ # Initializes the IMAPDB Database for the given Folder and set the working directory
45
+ # to the <i>cur</i> Maildir folder.
46
+ #
47
+ # Raises a SystemCallError if the Maildir folder does not exist
48
+ def initialize(p_user, p_name = nil)
49
+ @user = p_user
50
+ @name = p_name
51
+ @name = "INBOX" if @name.nil? or @name.empty?
52
+ @path = p_user.path + File::SEPARATOR + "Maildir"
53
+ @path = @path + File::SEPARATOR + '.' + @name if @name !~ /inbox/i
54
+ @path = @path
55
+ @curpath = @path + File::SEPARATOR + "cur"
56
+ @newpath = @path + File::SEPARATOR + "new"
57
+ @tmppath = @path + File::SEPARATOR + "tmp"
58
+ @dir = Dir.new(@curpath) # This will raise a if the path does not exist
59
+ @imapdb = IMAPDB.new(self)
60
+ end # }}}
61
+
62
+ #----------------------------------------------------------------------------------
63
+ # method: domain {{{
64
+ #++
65
+ # Gives the Domain of the User that owns the Folder
66
+ def domain
67
+ return @user.domain
68
+ end # }}}
69
+
70
+ #----------------------------------------------------------------------------------
71
+ # method: size {{{
72
+ #++
73
+ # Gives the number of Message objects stored in the Folder
74
+ def size
75
+ return @dir.entries.size - 2 # . and .. should not be counted
76
+ end # }}}
77
+
78
+ #----------------------------------------------------------------------------------
79
+ # method: each {{{
80
+ #++
81
+ # Apply the <i>block</i> to all Message objects stored in the Folder
82
+ # Returns <b>self</b>
83
+ def each #p_block
84
+ @dir.each { |entry|
85
+ next if entry == "."
86
+ next if entry == ".."
87
+ message = Message.new(self, entry)
88
+ yield(message)
89
+ }
90
+ self
91
+ end # }}}
92
+
93
+ #----------------------------------------------------------------------------------
94
+ # method: learnAs {{{
95
+ #++
96
+ # Learns all Message objects as <tt>p_kind</tt>.
97
+ # Moves them to the <tt>p_properFolder</tt> Folder if present.
98
+ # And makes a copy of each Message to the <tt>p_archiveFolder</tt> if present.
99
+ # This allows to re-train the spam analyzer with a bunch of ham and spam messages later on.
100
+ #
101
+ # Returns <b>self</b>
102
+ def learnAs(p_kind = "spam", p_properFolder = nil, p_archiveFolder = nil)
103
+ raise ArgumentError unless p_kind == "spam" or p_kind == "ham"
104
+ self.each {|message|
105
+ logger.info "Processing message #{message.id}" if !@@logger.nil?
106
+ message.learnAs(p_kind)
107
+ message.copyTo(p_archiveFolder) if !p_archiveFolder.nil?
108
+ message.markAs(p_kind)
109
+ message.moveTo(p_properFolder) if !p_properFolder.nil?
110
+ }
111
+ self
112
+ end # }}}
113
+
114
+ #----------------------------------------------------------------------------------
115
+ # method: to_xml {{{
116
+ #++
117
+ # Returns the REXML::Document that represents the Folder object
118
+ def to_xml
119
+ return REXML::Document.new("<Folder name=\"#{@name}\" path=\"#{@path}\" />")
120
+ end # }}}
121
+
122
+ #----------------------------------------------------------------------------------
123
+ # method: to_s {{{
124
+ #++
125
+ # Returns the String representation of the Domain object
126
+ def to_s
127
+ return "Folder #{@name}, path=\"#{@path}\""
128
+ end # }}}
129
+ end # }}}
130
+
131
+ end # }}}
@@ -0,0 +1,154 @@
1
+ #--
2
+ # Copyright (c) 2006 Gildas Cherruel
3
+ # Read COPYRIGHT in the root of VPOPMail library for copyright info
4
+ # Creator: gildasc@rubyforg.org
5
+ # Author: $Author: gildasc $
6
+ # Version: $Id: imapdb.rb 4 2006-10-07 23:18:13Z gildasc $
7
+ #++
8
+
9
+ require 'rexml/document'
10
+ require 'vpopmail/config'
11
+ require 'vpopmail/domain'
12
+ require 'vpopmail/user'
13
+ require 'vpopmail/message'
14
+
15
+ #------------------------------------------------------------------------------------------
16
+ #------------------------------------------------------------------------------------------
17
+ # module: VPOPMail {{{
18
+ #++
19
+ module VPOPMail
20
+
21
+ #------------------------------------------------------------------------------------------
22
+ # class: IMAPDB {{{
23
+ #++
24
+ # The IMAPDB class is used to manipulate the status of Message object in a Folder
25
+ class IMAPDB
26
+ attr_reader :name, :path
27
+
28
+ #--------------------------------------------------------------------------------------
29
+ # class attribute: logger {{{
30
+ @@logger = nil
31
+ def self.logger ; @@logger ; end
32
+ def self.logger=(p_object) ; @@logger = p_object ; end
33
+ def logger(p_object) ; @@logger ; end
34
+ def logger=(p_object) ; @@logger = p_object ; end
35
+ # }}}
36
+
37
+ #----------------------------------------------------------------------------------
38
+ # method: initialize {{{
39
+ #++
40
+ # Creates a new IMAPDB object attached to the Folder <tt>p_folder</tt>
41
+ #
42
+ # Uses <b>VPOPMail::CFG["IMAPDB"]</b> as the name of the file that contains the IMAP database.
43
+ def initialize(p_folder)
44
+ raise ArgumentError unless p_folder.kind_of?(Folder)
45
+ @name = VPOPMail::CFG["IMAPDB"]
46
+ @folder = p_folder
47
+ @path = @folder.path
48
+ @filename = @folder.path + File::SEPARATOR + @name
49
+ end # }}}
50
+
51
+ #----------------------------------------------------------------------------------
52
+ # method: update {{{
53
+ #++
54
+ # Marks the Message <tt>p_message</tt> as updated in the IMAP Database.
55
+ def update(p_message)
56
+ raise ArgumentError unless p_message.kind_of?(Message)
57
+ lines = self.load
58
+ return if lines.empty?
59
+
60
+ # Search and destroy the message in the IMAPDB
61
+ lines.delete_if {|line| line =~ /^\d+ #{p_message.id}/}
62
+
63
+ # Add the message at the end with the highest uid
64
+ lines << "#{@newuid} #{p_message.id}\n"
65
+ @newuid = @newuid.to_i + 1
66
+ lines[0] = "1 #{@uidv} #{@newuid}\n"
67
+
68
+ self.save(lines)
69
+ end # }}}
70
+
71
+ #----------------------------------------------------------------------------------
72
+ # method: add {{{
73
+ #++
74
+ # Adds the Message <tt>p_message</tt> to the IMAP Database.
75
+ def add(p_message)
76
+ raise ArgumentError unless p_message.kind_of?(Message)
77
+ lines = self.load
78
+ return if lines.empty?
79
+
80
+ # Search the message in the IMAPDB
81
+ lines.each {|line| return if line =~ /^\d+ #{p_message.id}/}
82
+
83
+ # Add the message at the end with the highest uid
84
+ lines << "#{@newuid} #{p_message.id}\n"
85
+ @newuid = @newuid.to_i + 1
86
+ lines[0] = "1 #{@uidv} #{@newuid}\n"
87
+
88
+ self.save(lines)
89
+ end # }}}
90
+
91
+ #----------------------------------------------------------------------------------
92
+ # method: delete {{{
93
+ #++
94
+ # Deletes the Message <tt>p_message</tt> from the IMAP Database.
95
+ def delete(p_message)
96
+ raise ArgumentError unless p_message.kind_of?(Message)
97
+ lines = self.load
98
+ return if lines.empty?
99
+
100
+ # Search and destroy the message in the IMAPDB
101
+ deleted = lines.delete_if {|line| line =~ /^\d+ #{p_message.id}/}
102
+ return if deleted.empty?
103
+
104
+ self.save(lines)
105
+ end # }}}
106
+
107
+ #----------------------------------------------------------------------------------
108
+ # method: to_xml {{{
109
+ #++
110
+ # Returns the REXML::Document that represents the IMAPDB object
111
+ def to_xml
112
+ return REXML::Document.new("<IMAPDB name=\"#{@name}\" path=\"#{@path}\" />")
113
+ end # }}}
114
+
115
+ #----------------------------------------------------------------------------------
116
+ # method: to_s {{{
117
+ #++
118
+ # Returns the String representation of the Domain object
119
+ def to_s
120
+ return "IMAP Database @name, path=\"#{@path}\""
121
+ end # }}}
122
+
123
+ #----------------------------------------------------------------------------------
124
+ # method: load {{{
125
+ #++
126
+ # Loads the IMAPDB in memory from its file.
127
+ def load
128
+ begin
129
+ lines = IO.readlines(@filename)
130
+ rescue Errno::ENOENT
131
+ logger.warning "Folder #{@folder.name}: IMAPDB does not exist, nothing to do" if !@@logger.nil?
132
+ return Array.new
133
+ end
134
+ if lines[0] !~ /^1 (\d+) (\d+)/ then
135
+ logger.error "Folder #{@folder.name}: Wrong IMAPDB version, aborting" if !@@logger.nil?
136
+ return Array.new
137
+ end
138
+ @uidv = $1
139
+ @newuid = $2
140
+ logger.info "Folder #{@folder.name}: uidv=#{@uidv}, new uid=#{@newuid}" if !@@logger.nil?
141
+ return lines
142
+ end # }}}
143
+
144
+ #----------------------------------------------------------------------------------
145
+ # method: save {{{
146
+ #++
147
+ # Saves the IMAPDB to its file.
148
+ def save(p_lines)
149
+ File.open(@filename, 'w') { |file| file.puts p_lines }
150
+ logger.info "Folder #{@folder.name}: IMAPDB saved" if !@@logger.nil?
151
+ end # }}}
152
+ end # }}}
153
+
154
+ end # }}}
@@ -0,0 +1,311 @@
1
+ #--
2
+ # Copyright (c) 2006 Gildas Cherruel
3
+ # Read COPYRIGHT in the root of VPOPMail library for copyright info
4
+ # Creator: gildasc@rubyforg.org
5
+ # Author: $Author: gildasc $
6
+ # Version: $Id: message.rb 4 2006-10-07 23:18:13Z gildasc $
7
+ #++
8
+
9
+ require 'rexml/document'
10
+ require 'rmail'
11
+ require 'net/smtp'
12
+ require 'net/dns'
13
+ require 'vpopmail/config'
14
+ require 'vpopmail/domain'
15
+ require 'vpopmail/user'
16
+ require 'vpopmail/imapdb'
17
+
18
+ #------------------------------------------------------------------------------------------
19
+ #------------------------------------------------------------------------------------------
20
+ # module: Net {{{
21
+ #++
22
+ module Net # :nodoc: all
23
+ #------------------------------------------------------------------------------------------
24
+ # class: SMTP {{{
25
+ #++
26
+ class SMTP
27
+ #--------------------------------------------------------------------------------------
28
+ # method: validAddress? {{{
29
+ #++
30
+ def validAddress?(p_address, p_local)
31
+ begin
32
+ mailfrom p_local
33
+ rcptto p_address
34
+ getok('RSET')
35
+ rescue SMTPFatalError
36
+ begin
37
+ getok('RSET')
38
+ rescue
39
+ end
40
+ return false
41
+ rescue Exception => p_e
42
+ STDERR.puts "Unknown Error! #{p_e}"
43
+ return false
44
+ end
45
+ return true
46
+ end # }}}
47
+
48
+ end # }}}
49
+ end # }}}
50
+
51
+ #------------------------------------------------------------------------------------------
52
+ #------------------------------------------------------------------------------------------
53
+ # module: VPOPMail {{{
54
+ #++
55
+ module VPOPMail
56
+
57
+ #------------------------------------------------------------------------------------------
58
+ # class: Message {{{
59
+ #++
60
+ # The Message class represents an email message stored in a Folder
61
+ class Message
62
+ attr_reader :filename, :id, :path, :info, :flags
63
+
64
+ #--------------------------------------------------------------------------------------
65
+ # class attribute: logger {{{
66
+ @@logger = nil
67
+ def self.logger ; @@logger ; end
68
+ def self.logger=(p_object) ; @@logger = p_object ; end
69
+ def logger(p_object) ; @@logger ; end
70
+ def logger=(p_object) ; @@logger = p_object ; end
71
+ # }}}
72
+
73
+ #--------------------------------------------------------------------------------------
74
+ # method: initialize {{{
75
+ #++
76
+ # Creates a new Message object after stored in the Folder <tt>p_folder</tt>.
77
+ #
78
+ # <tt>p_name</tt> contains the name of the file that contains the email.
79
+ # <tt>p_name</tt> follows the Maildir filename formatt as described in
80
+ # http://cr.yp.to/proto/maildir.html
81
+ def initialize(p_folder, p_name)
82
+ # The name analysis comes from http://cr.yp.to/proto/maildir.html
83
+ raise ArgumentError unless p_folder.kind_of?(Folder)
84
+ @folder = p_folder
85
+ @filename = p_name
86
+ @id = p_name
87
+ @info = nil
88
+ @flags = nil
89
+ if p_name =~ /.*:(\d),([A-Z]*)$/ then
90
+ @info = $1
91
+ @flags = $2
92
+ @id = p_name.gsub(/:\d,[A-Z]*$/, '')
93
+ end
94
+ self.load
95
+ end # }}}
96
+
97
+ #--------------------------------------------------------------------------------------
98
+ # class method: validAddress? {{{
99
+ #++
100
+ # Tells if the RMail::Address <tt>p_address</tt> is valid or not. Uses <tt>p_local</tt>
101
+ # as the origin email address when querying the mail server of <tt>p_address</tt>
102
+ #
103
+ # This methods queries the DNS of the domain contains in <tt>p_address</tt> for its
104
+ # MX Servers. Then it will use the SMTP protocol to query the MX servers for the validity
105
+ # of <tt>p_address</tt>
106
+ def self.validAddress?(p_address, p_local)
107
+ p_address = RMail::Address.parse(p_address)[0] if !p_address.kind_of? RMail::Address
108
+ p_local = RMail::Address.parse(p_local)[0] if !p_local.kind_of? RMail::Address
109
+
110
+ logger.info "Checking Domain: #{p_address.domain}" if !@@logger.nil?
111
+ resolver = Net::DNS::Resolver.new
112
+ logger.debug "#{resolver.inspect}"
113
+ answer = resolver.query(p_address.domain, Net::DNS::MX)
114
+ logger.debug "#{answer.inspect}"
115
+ if answer.nil? or answer.header.anCount == 0
116
+ logger.warn "No nameserver found for domain: #{resolver.errorstring}" if !@@logger.nil?
117
+ return false
118
+ end
119
+ hosts = answer.answer.sort {|x, y| x.preference <=> y.preference}
120
+
121
+ hosts.each { |host|
122
+ logger.info "Connecting to MX #{host.exchange}" if !@@logger.nil?
123
+ begin
124
+ Net::SMTP.start(host.exchange, 25, p_local.domain) { |smtp|
125
+ logger.debug "Validating #{p_address.address}" if !@@logger.nil?
126
+ return smtp.validAddress?(p_address.address, p_local.address)
127
+ }
128
+ rescue Errno::ETIMEDOUT => p_e
129
+ # That means the MX Host does not answer
130
+ logger.warn "timeout in querying #{host.exchange}" if !@@logger.nil?
131
+ rescue Exception => p_e
132
+ logger.error "Unknown error: #{p_e}" if !@@logger.nil?
133
+ end
134
+ }
135
+ return false
136
+ end # }}}
137
+
138
+ #--------------------------------------------------------------------------------------
139
+ # method: validAddress? {{{
140
+ #++
141
+ # Tells if the RMail::Address <tt>p_address</tt> is valid or not. Extracts
142
+ # the origin email address when querying the mail server of <tt>p_address</tt>
143
+ # from the current Message
144
+ #
145
+ # See Message.validAddress?
146
+ def validAddress?(p_address)
147
+ return Message.validAddress?(p_address, @folder.domain.postmaster)
148
+ end # }}}
149
+
150
+ #--------------------------------------------------------------------------------------
151
+ # method: markAs {{{
152
+ #++
153
+ # Marks the Message as <i>ham</i> or <i>spam</i>
154
+ #
155
+ # Raises an ArgumentError if <tt>p_kind</tt> contains another value.
156
+ def markAs(p_kind)
157
+ logger.info "Marking message as #{p_kind}" if !@@logger.nil?
158
+ field = VPOPMail::CFG["SPAM Subject Field"]
159
+ header = @rmail.header
160
+ case p_kind
161
+ when /spam/i
162
+ if !header.field?(field) then
163
+ header[field] = header.subject || ''
164
+ header.subject = VPOPMail::CFG["SPAM Mark"] + (header.subject || '')
165
+ self.save
166
+ else
167
+ logger.info "Message #{@id} is already marked as SPAM" if !@@logger.nil?
168
+ end
169
+ when /ham/i
170
+ if header.field?(field) then
171
+ logger.debug "There is a #{field} field,\nSubject: #{header.subject}\nSPAM: #{header[field]}" if !@@logger.nil?
172
+ header.subject = header[field]
173
+ header.delete(field)
174
+ self.save
175
+ else
176
+ logger.info "Message #{@id} is already marked as HAM" if !@@logger.nil?
177
+ end
178
+ else
179
+ raise ArgumentError, "Bad kind: #{p_kind}", caller
180
+ end
181
+ end # }}}
182
+
183
+ #--------------------------------------------------------------------------------------
184
+ # method: learnAs {{{
185
+ #++
186
+ # Learns the Message as <i>ham</i> or <i>spam</i> using
187
+ # {spamassassin}[http://spamassassin.apache.org]
188
+ def learnAs(p_kind)
189
+ raise ArgumentError unless p_kind == "spam" or p_kind == "ham"
190
+ logger.info "Learning message as #{p_kind}" if !@@logger.nil?
191
+ debug = "-D bayes,learn,dns"
192
+ debug = ""
193
+ cmdString = "/usr/bin/sa-learn --single #{debug} --username=vpopmail --#{p_kind}"
194
+ cmd = IO.popen(cmdString, "w+")
195
+ RMail::Serialize.write(cmd, @rmail)
196
+ cmd.close_write
197
+
198
+ while line = cmd.gets do
199
+ line = line.gsub(/[\r\n]*$/, "")
200
+ next if line.empty?
201
+ logger.debug "SA-Learn: #{line}" if !@@logger.nil?
202
+ end
203
+ end # }}}
204
+
205
+ #--------------------------------------------------------------------------------------
206
+ # method: load {{{
207
+ #++
208
+ # Loads the Message from its file in its <tt>@rmail</tt> attribute (RMail::Message)
209
+ def load
210
+ if @filename == @id then
211
+ path = @folder.newpath + File::SEPARATOR + @filename
212
+ else
213
+ path = @folder.curpath + File::SEPARATOR + @filename
214
+ end
215
+ logger.info "Loading #{path}" if !@@logger.nil?
216
+ begin
217
+ @rmail = File.open(path) { |file| RMail::Parser.read(file) }
218
+ stat = File.stat(path)
219
+ @uid = stat.uid
220
+ @gid = stat.gid
221
+ logger.debug "Loaded. uid=#{@uid}, gid=#{@gid}" if !@@logger.nil?
222
+ rescue Errno::ENOENT
223
+ logger.error "Cannot load #{path}" if !@@logger.nil?
224
+ exit -1
225
+ end
226
+ end # }}}
227
+
228
+ #--------------------------------------------------------------------------------------
229
+ # method: save {{{
230
+ #++
231
+ # Saves the Message to its file, by serializing its <tt>@rmail</tt> attribute.
232
+ def save
233
+ @folder.imapdb.update(self)
234
+ path = @folder.newpath + File::SEPARATOR + @id
235
+ logger.info "Saving #{path}" if !@@logger.nil?
236
+ File.open(path, "w") { |file| RMail::Serialize.write(file, @rmail) }
237
+ File.chmod(0644, path)
238
+ File.chown(@uid, @gid, path)
239
+ File.delete(@folder.curpath + File::SEPARATOR + @filename)
240
+ @filename = @id
241
+ @info = nil
242
+ @flags = nil
243
+ logger.debug "Saved. new filename: #{@filename}" if !@@logger.nil?
244
+ end # }}}
245
+
246
+ #--------------------------------------------------------------------------------------
247
+ # method: copyTo {{{
248
+ #++
249
+ # Copies the Message to the Folder <tt>p_folder</tt>
250
+ def copyTo(p_folder)
251
+ logger.info "Copying to folder #{p_folder.name}" if !@@logger.nil?
252
+ p_folder.imapdb.add(self)
253
+ path = p_folder.newpath + File::SEPARATOR + @id
254
+ File.open(path, "w") { |file| RMail::Serialize.write(file, @rmail) }
255
+ File.chmod(0644, path)
256
+ File.chown(@uid, @gid, path)
257
+ end # }}}
258
+
259
+ #--------------------------------------------------------------------------------------
260
+ # method: deleteFrom {{{
261
+ #++
262
+ # Deletes the Message from the Folder <tt>p_folder</tt>
263
+ def deleteFrom(p_folder)
264
+ logger.info "Deleting from folder #{p_folder.name}" if !@@logger.nil?
265
+ p_folder.imapdb.delete(self)
266
+ begin
267
+ File.delete(p_folder.curpath + File::SEPARATOR + @filename)
268
+ rescue => p_e
269
+ begin
270
+ File.delete(p_folder.newpath + File::SEPARATOR + @filename)
271
+ rescue => p_e
272
+ logger.error "Problem while deleting: #{p_e}" if !@@logger.nil?
273
+ end
274
+ end
275
+ end # }}}
276
+
277
+ #--------------------------------------------------------------------------------------
278
+ # method: moveTo {{{
279
+ #++
280
+ # Moves the Message to the Folder <tt>p_folder</tt>
281
+ def moveTo(p_folder)
282
+ logger.info "Moving to folder #{p_folder.name}" if !@@logger.nil?
283
+ self.copyTo(p_folder)
284
+ self.deleteFrom(@folder)
285
+ logger.info "Reloading from #{p_folder.name}" if !@@logger.nil?
286
+ @folder = p_folder
287
+ self.load
288
+ end # }}}
289
+
290
+ #--------------------------------------------------------------------------------------
291
+ # method: to_xml {{{
292
+ #++
293
+ # Returns the REXML::Document that represents the Message object
294
+ def to_xml
295
+ xml = "<Message id=\"#{@id}\""
296
+ xml += " info=\"#{@info}\"" if !@info.nil?
297
+ xml += " flags=\"#{@flags}\"" if !@flags.nil?
298
+ xml += " path=\"#{@path}\" />"
299
+ return REXML::Document.new(xml)
300
+ end # }}}
301
+
302
+ #--------------------------------------------------------------------------------------
303
+ # method: to_s {{{
304
+ #++
305
+ # Returns the String representation of the Message object
306
+ def to_s
307
+ return "Message #{@id}, info=#{@info}, flags=#{@flags}, path=\"#{@path}\""
308
+ end # }}}
309
+ end # }}}
310
+
311
+ end # }}}
@@ -0,0 +1,89 @@
1
+ #--
2
+ # Copyright (c) 2006 Gildas Cherruel
3
+ # Read COPYRIGHT in the root of VPOPMail library for copyright info
4
+ # Creator: gildasc@rubyforg.org
5
+ # Author: $Author: gildasc $
6
+ # Version: $Id: user.rb 4 2006-10-07 23:18:13Z gildasc $
7
+ #++
8
+
9
+ require 'vpopmail/config'
10
+ require 'vpopmail/domain'
11
+
12
+ #------------------------------------------------------------------------------------------
13
+ #------------------------------------------------------------------------------------------
14
+ # module: VPOPMail {{{
15
+ module VPOPMail
16
+
17
+ #------------------------------------------------------------------------------------------
18
+ # class: User {{{
19
+ class User
20
+ attr_reader :name, :path, :domain
21
+ attr_writer :name, :path
22
+
23
+ #--------------------------------------------------------------------------------------
24
+ # class attribute: logger {{{
25
+ @@logger = nil
26
+ def self.logger ; @@logger ; end
27
+ def self.logger=(p_object) ; @@logger = p_object ; end
28
+ def logger(p_object) ; @@logger ; end
29
+ def logger=(p_object) ; @@logger = p_object ; end
30
+ # }}}
31
+
32
+ #----------------------------------------------------------------------------------
33
+ # method: initialize {{{
34
+ def initialize(p_domain)
35
+ @name = @path = ""
36
+ @domain = p_domain
37
+ end # }}}
38
+
39
+ #----------------------------------------------------------------------------------
40
+ # class method: FindUsers {{{
41
+ def self.FindUsers(p_domain, p_name = nil)
42
+ raise ArgumentError unless p_domain.kind_of?(Domain)
43
+ cmdString = "#{VPOPMail::CFG['VPOP Bin']}/vuserinfo -n -d"
44
+ if p_name.nil? or p_name.empty? then
45
+ cmdString = cmdString + " -D #{p_domain.name}"
46
+ else
47
+ raise ArgumentError unless p_name.kind_of?(String)
48
+ cmdString = cmdString + " #{p_name}@#{p_domain.name}"
49
+ end
50
+ cmd = IO.popen(cmdString, "w+")
51
+ cmd.close_write
52
+
53
+ users = Array.new
54
+ user = User.new(p_domain)
55
+ getName = true
56
+ while line = cmd.gets do
57
+ line = line.gsub(/[\r\n]*$/, "")
58
+ next if line.empty?
59
+ if getName then
60
+ user.name = line
61
+ else
62
+ user.path = line
63
+ users << user
64
+ user = User.new(p_domain)
65
+ end
66
+ getName = !getName
67
+ end
68
+ return users
69
+ end # }}}
70
+
71
+ #----------------------------------------------------------------------------------
72
+ # method: to_xml {{{
73
+ def to_xml
74
+ return "<User name=\"#{@name}\" path=\"#{@path}\" />"
75
+ end # }}}
76
+
77
+ #----------------------------------------------------------------------------------
78
+ # method: to_s {{{
79
+ def to_s
80
+ return to_xml
81
+ end # }}}
82
+
83
+ #----------------------------------------------------------------------------------
84
+ # Access Control {{{
85
+ public :name, :path, :name=, :path=
86
+ # }}}
87
+ end # }}}
88
+
89
+ end # }}}
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.8.10
3
+ specification_version: 1
4
+ name: vpopmail
5
+ version: !ruby/object:Gem::Version
6
+ version: 1.0.0
7
+ date: 2006-10-08
8
+ summary: Ruby VPOPMail interface
9
+ require_paths:
10
+ - lib
11
+ email: gildas@breizh.org
12
+ homepage: http://ruby-vpopmail.rubyforge.org
13
+ rubyforge_project: ruby-vpopmail
14
+ description: "VPOPMail interface for Ruby. Allows to write filters, spam learners, etc. using
15
+ the vpopmail software package."
16
+ autorequire: vpopmail
17
+ default_executable:
18
+ bindir: bin
19
+ has_rdoc: true
20
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
21
+ requirements:
22
+ -
23
+ - ">"
24
+ - !ruby/object:Gem::Version
25
+ version: 0.0.0
26
+ version:
27
+ platform: ruby
28
+ authors:
29
+ - Gildas Cherruel
30
+ files:
31
+ - lib/vpopmail
32
+ - lib/vpopmail.rb
33
+ - lib/vpopmail/config.rb
34
+ - lib/vpopmail/domain.rb
35
+ - lib/vpopmail/folder.rb
36
+ - lib/vpopmail/imapdb.rb
37
+ - lib/vpopmail/message.rb
38
+ - lib/vpopmail/user.rb
39
+ - examples/emailcheck.rb
40
+ - examples/vlearn.rb
41
+ - Rakefile
42
+ - install.rb
43
+ - README
44
+ test_files: []
45
+ rdoc_options:
46
+ - "--main"
47
+ - README
48
+ extra_rdoc_files:
49
+ - README
50
+ executables: []
51
+ extensions: []
52
+ requirements: []
53
+ dependencies:
54
+ - !ruby/object:Gem::Dependency
55
+ name: rmail
56
+ version_requirement:
57
+ version_requirements: !ruby/object:Gem::Version::Requirement
58
+ requirements:
59
+ -
60
+ - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: "0.17"
63
+ version: