gurgitate-mail 1.10.3 → 1.10.5
Sign up to get free protection for your applications and to get access to all the features.
- data/.gemtest +0 -0
- data/Rakefile +15 -0
- data/bin/gurgitate-mail +1 -1
- data/test/gurgitate-test.rb +3 -1
- metadata +36 -16
- data/lib/gurgitate-mail.rb +0 -385
- data/lib/gurgitate/deliver.rb +0 -86
- data/lib/gurgitate/deliver/maildir.rb +0 -104
- data/lib/gurgitate/deliver/mbox.rb +0 -47
- data/lib/gurgitate/deliver/mh.rb +0 -147
- data/lib/gurgitate/header.rb +0 -81
- data/lib/gurgitate/headers.rb +0 -282
- data/lib/gurgitate/mail_headers.rb +0 -166
- data/lib/gurgitate/mailmessage.rb +0 -124
- data/lib/gurgitate/message.rb +0 -113
data/lib/gurgitate/deliver.rb
DELETED
@@ -1,86 +0,0 @@
|
|
1
|
-
#!/opt/bin/ruby -w
|
2
|
-
|
3
|
-
#------------------------------------------------------------------------
|
4
|
-
# Code to handle saving a message to a mailbox (and a framework for detecting
|
5
|
-
# what kind of mailbox it is)
|
6
|
-
#------------------------------------------------------------------------
|
7
|
-
|
8
|
-
require "gurgitate/deliver/mbox"
|
9
|
-
require "gurgitate/deliver/maildir"
|
10
|
-
require "gurgitate/deliver/mh"
|
11
|
-
|
12
|
-
module Gurgitate
|
13
|
-
module Deliver
|
14
|
-
|
15
|
-
class MailboxFound < Exception
|
16
|
-
# more of a "flag" than an exception really
|
17
|
-
end
|
18
|
-
|
19
|
-
# Saves a message to +mailbox+, after detecting what the mailbox's
|
20
|
-
# format is.
|
21
|
-
# mailbox::
|
22
|
-
# A string containing the path of the mailbox to save
|
23
|
-
# the message to. If it is of the form "=mailbox", it
|
24
|
-
# saves the message to +Maildir+/+mailbox+. Otherwise,
|
25
|
-
# it simply saves the message to the file +mailbox+.
|
26
|
-
def save(mailbox)
|
27
|
-
|
28
|
-
if mailbox[0,1]=='=' and @maildir != nil then
|
29
|
-
if @folderstyle == Maildir and mailbox !~ /^=\./ then
|
30
|
-
mailbox["="]=@maildir+"/."
|
31
|
-
else
|
32
|
-
mailbox["="]=@maildir+"/"
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
if mailbox[0,1] != '/' then
|
37
|
-
log("Cannot save to relative filenames! Saving to spool file");
|
38
|
-
mailbox=spoolfile
|
39
|
-
end
|
40
|
-
|
41
|
-
begin
|
42
|
-
[MBox,Maildir,MH].each do |mod|
|
43
|
-
if mod::check_mailbox(mailbox) then
|
44
|
-
extend mod
|
45
|
-
raise MailboxFound
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
# Huh, nothing could find anything. Oh well,
|
50
|
-
# let's default to whatever's in @folderstyle. (I
|
51
|
-
# guess we'll be making a new mailbox, eh?)
|
52
|
-
|
53
|
-
if Module === @folderstyle then
|
54
|
-
#
|
55
|
-
# Careful we don't get the wrong instance variable
|
56
|
-
folderstyle=@folderstyle
|
57
|
-
|
58
|
-
extend folderstyle
|
59
|
-
else
|
60
|
-
# No hints from the user either. Let's guess!
|
61
|
-
# I'll use the same heuristic that Postfix uses--if the
|
62
|
-
# mailbox name ends with a /, then make it a Maildir,
|
63
|
-
# otherwise make it a mail file
|
64
|
-
if mailbox =~ /\/$/ then
|
65
|
-
extend Maildir
|
66
|
-
else
|
67
|
-
extend MBox
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
rescue MailboxFound
|
72
|
-
# Don't need to do anything--we only have to worry
|
73
|
-
# about it if there wasn't a mailbox there.
|
74
|
-
nil
|
75
|
-
end
|
76
|
-
|
77
|
-
begin
|
78
|
-
deliver_message(mailbox)
|
79
|
-
rescue SystemCallError
|
80
|
-
self.log "Gack! Something went wrong: " + $!.to_s
|
81
|
-
raise
|
82
|
-
# exit 75
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
@@ -1,104 +0,0 @@
|
|
1
|
-
#!/opt/bin/ruby -w
|
2
|
-
|
3
|
-
#------------------------------------------------------------------------
|
4
|
-
# Deliver mail to a maildir (also, detects maildir)
|
5
|
-
#------------------------------------------------------------------------
|
6
|
-
|
7
|
-
require "socket" # for gethostname (!)
|
8
|
-
|
9
|
-
module Gurgitate
|
10
|
-
module Deliver
|
11
|
-
module Maildir
|
12
|
-
# Figures out if +mailbox+ is a Maildir mailbox
|
13
|
-
# mailbox::
|
14
|
-
# A string containing the path of the mailbox to save the
|
15
|
-
# message to. If it is of the form "=mailbox", it saves
|
16
|
-
# the message to +Maildir+/+mailbox+. Otherwise, it
|
17
|
-
# simply saves the message to the file +mailbox+.
|
18
|
-
def self::check_mailbox mailbox
|
19
|
-
begin
|
20
|
-
if File.stat(mailbox).directory? then
|
21
|
-
if File.stat(File.join(mailbox,"cur")).directory? then
|
22
|
-
return Maildir
|
23
|
-
end
|
24
|
-
end
|
25
|
-
rescue Errno::ENOENT
|
26
|
-
return nil
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
# Figures out the first available filename in the mail dir
|
31
|
-
# +dir+ and returns the filename to use.
|
32
|
-
# dir::
|
33
|
-
# One of "+mailbox+/tmp" or "+mailbox+/new", but that's
|
34
|
-
# only because that's what the maildir spec
|
35
|
-
# (http://cr.yp.to/proto/maildir.html) says.
|
36
|
-
def maildir_getfilename dir
|
37
|
-
time=Time.now.to_f
|
38
|
-
counter=0
|
39
|
-
hostname=Socket::gethostname
|
40
|
-
filename=nil
|
41
|
-
loop do
|
42
|
-
filename=File.join(dir,sprintf("%.4f.%d_%d.%s",
|
43
|
-
time,$$,counter,hostname))
|
44
|
-
break if not File.exists?(filename)
|
45
|
-
counter+=1
|
46
|
-
end
|
47
|
-
return filename
|
48
|
-
end
|
49
|
-
|
50
|
-
# Creates a new Maildir folder +mailbox+
|
51
|
-
# mailbox::
|
52
|
-
# The full path of the new folder to be created
|
53
|
-
def make_mailbox mailbox
|
54
|
-
Dir.mkdir(mailbox)
|
55
|
-
%w{cur tmp new}.each do |dir|
|
56
|
-
Dir.mkdir(File.join(mailbox,dir))
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
# Delivers a message to the maildir-format mailbox +mailbox+.
|
61
|
-
# mailbox::
|
62
|
-
# A string containing the path of the mailbox to save the
|
63
|
-
# message to. If it is of the form "=mailbox", it saves
|
64
|
-
# the message to +Maildir+/+mailbox+. Otherwise, it
|
65
|
-
# simply saves the message to the file +mailbox+.
|
66
|
-
def deliver_message mailbox
|
67
|
-
begin
|
68
|
-
File.stat(mailbox)
|
69
|
-
rescue Errno::ENOENT
|
70
|
-
make_mailbox(mailbox)
|
71
|
-
end
|
72
|
-
|
73
|
-
unless File.stat(mailbox).directory?
|
74
|
-
raise SystemError, 'not a directory'
|
75
|
-
end
|
76
|
-
|
77
|
-
tmpfilename=maildir_getfilename(File.join(mailbox,"tmp"))
|
78
|
-
File.open(tmpfilename,File::CREAT|File::WRONLY) do |fh|
|
79
|
-
fh.write(self.to_s)
|
80
|
-
fh.flush
|
81
|
-
# I should put a caveat here, unfortunately. Ruby's
|
82
|
-
# IO#flush only flushes Ruby's buffers, not the
|
83
|
-
# operating system's. If anyone knows how to force
|
84
|
-
# a real fflush(), I'd love to know. Otherwise, I'm
|
85
|
-
# going to hope that closing the file does the trick
|
86
|
-
# for me.
|
87
|
-
end
|
88
|
-
|
89
|
-
# ...and link to new.
|
90
|
-
# (I guess Maildir mailboxes don't work too well
|
91
|
-
# on Windows, eh?)
|
92
|
-
newfilename = maildir_getfilename(
|
93
|
-
File.join(mailbox,"new"))
|
94
|
-
begin
|
95
|
-
File.link(tmpfilename,newfilename)
|
96
|
-
rescue SystemCallError
|
97
|
-
log("Couldn't create maildir link to \"new\"!")
|
98
|
-
exit 75 # Argh, I tried, it didn't work out
|
99
|
-
end
|
100
|
-
File.delete(tmpfilename)
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
@@ -1,47 +0,0 @@
|
|
1
|
-
#!/opt/bin/ruby -w
|
2
|
-
|
3
|
-
#------------------------------------------------------------------------
|
4
|
-
# Delivers a message to an mbox (also includes mbox detector)
|
5
|
-
#------------------------------------------------------------------------
|
6
|
-
|
7
|
-
module Gurgitate
|
8
|
-
module Deliver
|
9
|
-
module MBox
|
10
|
-
# Checks to see if +mailbox+ is an mbox mailbox
|
11
|
-
# mailbox::
|
12
|
-
# A string containing the path of the mailbox to save
|
13
|
-
# the message to. If it is of the form "=mailbox", it
|
14
|
-
# saves the message to +Maildir+/+mailbox+. Otherwise,
|
15
|
-
# it simply saves the message to the file +mailbox+.
|
16
|
-
def self::check_mailbox mailbox
|
17
|
-
|
18
|
-
begin
|
19
|
-
if File.stat(mailbox).file? then
|
20
|
-
return MBox
|
21
|
-
else
|
22
|
-
return nil
|
23
|
-
end
|
24
|
-
rescue Errno::ENOENT
|
25
|
-
return nil
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
# Delivers the message to +mailbox+
|
30
|
-
# mailbox::
|
31
|
-
# A string containing the path of the mailbox to save
|
32
|
-
# the message to. If it is of the form "=mailbox", it
|
33
|
-
# saves the message to +Maildir+/+mailbox+. Otherwise,
|
34
|
-
# it simply saves the message to the file +mailbox+.
|
35
|
-
def deliver_message mailbox
|
36
|
-
File.open(mailbox,File::WRONLY |
|
37
|
-
File::APPEND |
|
38
|
-
File::CREAT) do |f|
|
39
|
-
f.flock(File::LOCK_EX)
|
40
|
-
message=(if f.stat.size > 0 then "\n" else "" end) + to_mbox
|
41
|
-
f.print message
|
42
|
-
f.flock(File::LOCK_UN)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
data/lib/gurgitate/deliver/mh.rb
DELETED
@@ -1,147 +0,0 @@
|
|
1
|
-
#!/opt/bin/ruby -w
|
2
|
-
|
3
|
-
require "yaml"
|
4
|
-
|
5
|
-
#------------------------------------------------------------------------
|
6
|
-
# Delivers a message to an mbox (also includes mbox detector)
|
7
|
-
#------------------------------------------------------------------------
|
8
|
-
|
9
|
-
module Gurgitate
|
10
|
-
module Deliver
|
11
|
-
module MH
|
12
|
-
# Checks to see if +mailbox+ is an mbox mailbox
|
13
|
-
# mailbox::
|
14
|
-
# A string containing the path of the mailbox to save
|
15
|
-
# the message to. If it is of the form "=mailbox", it
|
16
|
-
# saves the message to +Maildir+/+mailbox+. Otherwise,
|
17
|
-
# it simply saves the message to the file +mailbox+.
|
18
|
-
def self::check_mailbox mailbox
|
19
|
-
begin
|
20
|
-
# Rather annoyingly, pretty well any directory can
|
21
|
-
# be a MH mailbox, but this just checks to make sure
|
22
|
-
# it's not actually a Maildir by mistake.
|
23
|
-
#
|
24
|
-
# I could put in a check for the path given in
|
25
|
-
# $HOME/.mh_profile, but Claws-Mail uses MH mailboxes and
|
26
|
-
# disregards $HOME/.mh_profile.
|
27
|
-
if File.stat(mailbox).directory? and
|
28
|
-
not ( File.exists?(File.join(mailbox, "cur")) or
|
29
|
-
File.exists?(File.join(mailbox, "tmp")) or
|
30
|
-
File.exists?(File.join(mailbox, "new"))) then
|
31
|
-
return MH
|
32
|
-
end
|
33
|
-
rescue Errno::ENOENT
|
34
|
-
return nil
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
# Delivers the message to +mailbox+
|
39
|
-
# mailbox::
|
40
|
-
# A string containing the path of the mailbox to save
|
41
|
-
# the message to. If it is of the form "=mailbox", it
|
42
|
-
# saves the message to +Maildir+/+mailbox+. Otherwise,
|
43
|
-
# it simply saves the message to the file +mailbox+.
|
44
|
-
def deliver_message mailbox
|
45
|
-
if ! File.exists? mailbox then
|
46
|
-
Dir.mkdir(mailbox)
|
47
|
-
end
|
48
|
-
|
49
|
-
if File.exists? mailbox and not File.directory? mailbox then
|
50
|
-
raise SystemError, "not a directory"
|
51
|
-
end
|
52
|
-
|
53
|
-
new_msgnum = next_message(mailbox) do |filehandle|
|
54
|
-
filehandle.print self.to_s
|
55
|
-
end
|
56
|
-
|
57
|
-
update_sequences(mailbox, new_msgnum)
|
58
|
-
end
|
59
|
-
|
60
|
-
private
|
61
|
-
|
62
|
-
def update_sequences mailbox, msgnum
|
63
|
-
sequences = File.join(mailbox, ".mh_sequences")
|
64
|
-
lockfile = sequences + ".lock" # how quaint
|
65
|
-
counter=0
|
66
|
-
while counter < 10 do
|
67
|
-
begin
|
68
|
-
File.open(lockfile,
|
69
|
-
File::WRONLY |
|
70
|
-
File::CREAT |
|
71
|
-
File::EXCL ) do |lock|
|
72
|
-
File.open(sequences,
|
73
|
-
File::RDWR | File::CREAT) do |seq|
|
74
|
-
|
75
|
-
seq.flock(File::LOCK_EX)
|
76
|
-
metadata = YAML.load(seq.read) || Hash.new
|
77
|
-
|
78
|
-
metadata["unseen"] = update_unseen \
|
79
|
-
metadata["unseen"], msgnum
|
80
|
-
|
81
|
-
seq.rewind
|
82
|
-
metadata.each do |key, val|
|
83
|
-
seq.puts "#{key}: #{val}"
|
84
|
-
end
|
85
|
-
seq.truncate seq.tell
|
86
|
-
seq.flock(File::LOCK_UN)
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
File.unlink lockfile
|
91
|
-
break
|
92
|
-
rescue Errno::EEXIST
|
93
|
-
# some other process is doing something, so wait a few
|
94
|
-
# milliseconds until it's done
|
95
|
-
counter += 1
|
96
|
-
sleep(0.1)
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
# If it's still around after 10 tries, then obviously
|
101
|
-
# something bigger went wrong; forcibly remove it and
|
102
|
-
# try again.
|
103
|
-
if counter == 10 then
|
104
|
-
File.unlink lockfile
|
105
|
-
update_sequences mailbox, msgnum
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
def update_unseen unseen, msgnum
|
110
|
-
prevmsg = msgnum - 1
|
111
|
-
if unseen
|
112
|
-
unseenstring = unseen.to_s
|
113
|
-
|
114
|
-
if unseenstring =~ /-#{prevmsg}/ then
|
115
|
-
return unseenstring.sub(/\b#{prevmsg}\b/, msgnum.to_s)
|
116
|
-
end
|
117
|
-
|
118
|
-
if unseenstring.match(/\b#{prevmsg}\b/) then
|
119
|
-
return "#{unseenstring}-#{msgnum}"
|
120
|
-
end
|
121
|
-
|
122
|
-
return "#{unseenstring} #{msgnum}"
|
123
|
-
else
|
124
|
-
return msgnum
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
def next_message mailbox
|
129
|
-
next_msgnum = Dir.open(mailbox).map { |ent| ent.to_i }.max + 1
|
130
|
-
loop do
|
131
|
-
begin
|
132
|
-
File.open(File.join(mailbox, next_msgnum.to_s),
|
133
|
-
File::WRONLY |
|
134
|
-
File::CREAT |
|
135
|
-
File::EXCL ) do |filehandle|
|
136
|
-
yield filehandle
|
137
|
-
end
|
138
|
-
break
|
139
|
-
rescue Errno::EEXIST
|
140
|
-
next_msgnum += 1
|
141
|
-
end
|
142
|
-
end
|
143
|
-
return next_msgnum
|
144
|
-
end
|
145
|
-
end
|
146
|
-
end
|
147
|
-
end
|
data/lib/gurgitate/header.rb
DELETED
@@ -1,81 +0,0 @@
|
|
1
|
-
#!/opt/bin/ruby -w
|
2
|
-
|
3
|
-
module Gurgitate
|
4
|
-
class IllegalHeader < RuntimeError ; end
|
5
|
-
|
6
|
-
# A little class for a single header
|
7
|
-
class Header
|
8
|
-
# The name of the header
|
9
|
-
attr_accessor :name
|
10
|
-
# The contents of the header
|
11
|
-
attr_accessor :contents
|
12
|
-
|
13
|
-
alias_method :value, :contents
|
14
|
-
|
15
|
-
# A recent rash of viruses has forced me to canonicalize
|
16
|
-
# the capitalization of headers. Sigh.
|
17
|
-
def capitalize_words(s)
|
18
|
-
return s.split(/-/).map { |w| w.capitalize }.join("-")
|
19
|
-
rescue
|
20
|
-
return s
|
21
|
-
end
|
22
|
-
|
23
|
-
private :capitalize_words
|
24
|
-
|
25
|
-
# Creates a Header object.
|
26
|
-
# header::
|
27
|
-
# The text of the email-message header
|
28
|
-
def initialize(*header)
|
29
|
-
name,contents=nil,nil
|
30
|
-
if header.length == 1 then
|
31
|
-
# RFC822 says that a header consists of some (printable,
|
32
|
-
# non-whitespace) crap, followed by a colon, followed by
|
33
|
-
# some more (printable, but can include whitespaces)
|
34
|
-
# crap.
|
35
|
-
if(header[0] =~ /^[\x21-\x39\x3b-\x7e]+:/) then
|
36
|
-
(name,contents)=header[0].split(/:\s*/,2)
|
37
|
-
if(name =~ /:$/ and contents == nil) then
|
38
|
-
# It looks like someone is using Becky!
|
39
|
-
name=header[0].gsub(/:$/,"")
|
40
|
-
contents = ""
|
41
|
-
end
|
42
|
-
|
43
|
-
raise IllegalHeader, "Empty name" \
|
44
|
-
if (name == "" or name == nil)
|
45
|
-
contents="" if contents == nil
|
46
|
-
|
47
|
-
@@lastname=name
|
48
|
-
else
|
49
|
-
raise IllegalHeader, "Bad header syntax: no colon in #{header}"
|
50
|
-
end
|
51
|
-
elsif header.length == 2 then
|
52
|
-
name,contents = *header
|
53
|
-
end
|
54
|
-
|
55
|
-
@name=capitalize_words(name)
|
56
|
-
@contents=contents
|
57
|
-
end
|
58
|
-
|
59
|
-
# Extended header
|
60
|
-
def << (text)
|
61
|
-
@contents += "\n" + text
|
62
|
-
end
|
63
|
-
|
64
|
-
# Matches a header's contents.
|
65
|
-
# regex::
|
66
|
-
# The regular expression to match against the header's contents
|
67
|
-
def matches (regex)
|
68
|
-
if String === regex
|
69
|
-
regex = Regexp.new(Regexp.escape(regex))
|
70
|
-
end
|
71
|
-
@contents =~ regex
|
72
|
-
end
|
73
|
-
|
74
|
-
alias :=~ :matches
|
75
|
-
|
76
|
-
# Returns the header, ready to put into an email message
|
77
|
-
def to_s
|
78
|
-
@name+": "+@contents
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|