vpopmail 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: