This file is indexed.

/usr/lib/ruby/1.8/gurgitate-mail.rb is in gurgitate-mail 1.10.0-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
#------------------------------------------------------------------------
# Mail filter package
#------------------------------------------------------------------------

require 'etc'

require 'gurgitate/mailmessage'
require 'gurgitate/deliver'

module Gurgitate
    #========================================================================
    # The actual gurgitator; reads a message and then it can do
    # other stuff with it, like save to a mailbox or forward
    # it somewhere else.
    class Gurgitate < Mailmessage
        include Deliver

        # Instead of the usual attributes, I went with a
        # reader-is-writer type thing (as seen quite often in Perl and
        # C++ code) so that in your .gurgitate-rules, you can say
        # 
        # maildir "#{homedir}/Mail"
        # sendmail "/usr/sbin/sendmail"
        # spoolfile "Maildir"
        # spooldir homedir
        #
        # This is because of an oddity in Ruby where, even if an
        # accessor exists in the current object, if you say:
        #    name = value
        # it'll always create a local variable.  Not quite what you
        # want when you're trying to set a config parameter.  You have
        # to say "self.name = value", which (I think) is ugly.
        #
        # In the interests of promoting harmony, of course, the previous
        # syntax will continue to work.
        def self.attr_configparam(*syms)
            syms.each do |sym|
                class_eval %/
                    def #{sym} (*vals)
                        if vals.length == 1
                            @#{sym} = vals[0]
                        elsif vals.length == 0
                            @#{sym}
                        else
                            raise ArgumentError, 
                                "wrong number of arguments " +
                                "(\#{vals.length} for 0 or 1)"
                        end
                    end

                    # Don't break it for the nice people who use 
                    # old-style accessors though.  Breaking people's
                    # .gurgitate-rules is a bad idea.
                    attr_writer :#{sym}
                /
            end
        end

        # The directory you want to put mail folders into
        attr_configparam :maildir

        # The path to your log file
        attr_configparam :logfile

        # The full path of your "sendmail" program
        attr_configparam :sendmail

        # Your home directory
        attr_configparam :homedir 

        # Your default mail spool
        attr_configparam :spoolfile

        # The directory where user mail spools live
        attr_configparam :spooldir

        # What kind of mailboxes you prefer
        # attr_configparam :folderstyle

        # What kind of mailboxes you prefer
        def folderstyle(*style)
            if style.length == 0 then
                @folderstyle
            elsif style.length == 1 then
                if style[0] == Maildir then
                    spooldir homedir
                    spoolfile File.join(spooldir,"Maildir")
                    maildir spoolfile
                elsif style[0] == MH then
                    mh_profile_path = File.join(ENV["HOME"],".mh_profile")
                    if File.exists?(mh_profile_path) then
                        mh_profile = YAML.load(File.read(mh_profile_path))
                        maildir mh_profile["Path"]
                    else
                        maildir File.join(ENV["HOME"],"Mail")
                    end
                    spoolfile File.join(maildir,"inbox")
                else
                    spooldir "/var/spool/mail"
                    spoolfile File.join(spooldir, @passwd.name)
                end

                @folderstyle = style[0]
            else
                raise ArgumentError, "wrong number of arguments "+
                                     "(#{style.length} for 0 or 1)"
            end
            @folderstyle
        end

        # Set config params to defaults, read in mail message from
        # +input+
        # input::
        #   Either the text of the email message in RFC-822 format,
        #   or a filehandle where the email message can be read from
        # recipient::
        #   The contents of the envelope recipient parameter
        # sender::
        #   The envelope sender parameter
        # spooldir::
        #   The location of the mail spools directory.
        def initialize(input=nil,
                       recipient=nil,
                       sender=nil, 
                       spooldir="/var/spool/mail",
                       &block)
            @passwd      = Etc.getpwuid
            @homedir     = @passwd.dir;
            @maildir     = File.join(@passwd.dir,"Mail")
            @logfile     = File.join(@passwd.dir,".gurgitate.log")
            @sendmail    = "/usr/lib/sendmail"
            @spooldir    = spooldir
            @spoolfile   = File.join(@spooldir,@passwd.name )
            @folderstyle = MBox
            @rules       = []

            input_text = ""
            input.each_line do |l| input_text << l end
            super(input_text, recipient, sender)
            instance_eval(&block) if block_given?
        end

        def add_rules(filename, options = {})
            if not Hash === options
                raise ArgumentError.new("Expected hash of options")
            end
            if filename == :default
                filename=homedir+"/.gurgitate-rules"
            end
            if not FileTest.exist?(filename)
                filename = filename + '.rb'
            end
            if not FileTest.exist?(filename)
              if options.has_key?(:user)
                log("#{filename} does not exist.")
              end
              return false
            end
            if FileTest.file?(filename) and
                ( ( not options.has_key? :system and
                    FileTest.owned?(filename) ) or
                  ( options.has_key? :system and
                    options[:system] == true and
                    File.stat(filename).uid == 0 ) ) and
                FileTest.readable?(filename)
                @rules << filename
            else
                log("#{filename} has bad permissions or ownership, not using rules")
                return false
            end
        end

        # Deletes the current message.
        def delete
            # Well, nothing here, really.
        end

        # This is a neat one.  You can get any header as a method.
        # Say, if you want the header "X-Face", then you call
        # x_face and that gets it for you.  It raises NameError if
        # that header isn't found.
        # meth::
        #   The method that the caller tried to call which isn't
        #   handled any other way.
        def method_missing(meth)
            headername=meth.to_s.split(/_/).map {|x| x.capitalize}.join("-")
            if headers[headername] then
                return headers[headername]
            else
                raise NameError,"undefined local variable or method, or header not found `#{meth}' for #{self}:#{self.class}"
            end
        end

        # Forwards the message to +address+.
        # address::
        #   A valid email address to forward the message to.
        def forward(address)
            self.log "Forwarding to "+address
            IO.popen(@sendmail+" "+address,"w") do |f|
                f.print(self.to_s)
            end
        end

        # Writes +message+ to the log file.
        def log(message)
            if(@logfile)then
                File.open(@logfile,"a") do |f|
                    f.flock(File::LOCK_EX)
                    f.print(Time.new.to_s+" "+message+"\n")
                    f.flock(File::LOCK_UN)
                end
            end
        end

        # Pipes the message through +program+.  If +program+
        # fails, puts the message into +spoolfile+
        def pipe(program)
            self.log "Piping through "+program
            IO.popen(program,"w") do |f|
                f.print(self.to_s)
            end
            return $?>>8
        end

        # Pipes the message through +program+, and returns another
        # +Gurgitate+ object containing the output of the filter
        def filter(program,&block)
            self.log "Filtering with "+program
            IO.popen("-","w+") do |filter|
                unless filter then
                    begin
                        exec(program)
                    rescue
                        exit! # should not get here anyway
                    end
                else
                    if fork
                        filter.close_write
                        g=Gurgitate.new(filter)
                        g.instance_eval(&block) if block_given?
                        return g
                    else
                        begin
                            filter.close_read
                            filter.print to_s
                            filter.close
                        rescue
                            nil
                        ensure
                            exit!
                        end
                    end
                end
            end
        end

        # Processes your .gurgitate-rules.rb.
        def process(&block)
            begin
                if @rules.size > 0 or block
                    @rules.each do |configfilespec|
                        begin
                            eval File.new(configfilespec).read, nil,
                                configfilespec
                        rescue ScriptError
                            log "Couldn't load #{configfilespec}: "+$!
                            save(spoolfile)
                        rescue Exception
                            log "Error while executing #{configfilespec}: #{$!}"
                            $@.each { |tr| log "Backtrace: #{tr}" }
                            folderstyle = MBox
                            save(spoolfile)
                        end
                    end
                    if block
                        instance_eval(&block)
                    end
                    log "Mail not covered by rules, saving to default spool"
                    save(spoolfile)
                else
                    save(spoolfile)
                end
            rescue Exception
                log "Error while executing rules: #{$!}"
                $@.each { |tr| log "Backtrace: #{tr}" }
                log "Attempting to save to spoolfile after error"
                folderstyle = MBox
                save(spoolfile)
            end
        end
    end
end