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.
@@ -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
@@ -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
@@ -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