~ubuntu-branches/ubuntu/wily/feed2imap/wily-proposed

« back to all changes in this revision

Viewing changes to lib/feed2imap/maildir.rb

  • Committer: Bazaar Package Importer
  • Author(s): Lucas Nussbaum
  • Date: 2010-04-18 14:53:50 UTC
  • mfrom: (1.2.3 upstream) (3.1.7 maverick)
  • Revision ID: james.westby@ubuntu.com-20100418145350-gxnud8v0feinqei8
Tags: 1.0-1
* New upstream release. Closes: #577795.
* Bumped Standards-Version to 3.8.4. No changes needed.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
=begin
 
2
Feed2Imap - RSS/Atom Aggregator uploading to an IMAP Server, or local Maildir
 
3
Copyright (c) 2009 Andreas Rottmann
 
4
 
 
5
This program is free software; you can redistribute it and/or modify
 
6
it under the terms of the GNU General Public License as published by
 
7
the Free Software Foundation; either version 2 of the License, or
 
8
(at your option) any later version.
 
9
 
 
10
This program is distributed in the hope that it will be useful,
 
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
GNU General Public License for more details.
 
14
 
 
15
You should have received a copy of the GNU General Public License
 
16
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
17
=end
 
18
 
 
19
require 'uri'
 
20
require 'fileutils'
 
21
require 'fcntl'
 
22
 
 
23
class MaildirAccount
 
24
  MYHOSTNAME = Socket.gethostname
 
25
 
 
26
  attr_reader :uri
 
27
 
 
28
  def putmail(folder, mail, date = Time::now)
 
29
    store_message(folder_dir(folder), date, nil) do |f|
 
30
      f.puts(mail)
 
31
    end
 
32
  end
 
33
 
 
34
  def updatemail(folder, mail, idx, date = Time::now)
 
35
    dir = folder_dir(folder)
 
36
    guarantee_maildir(dir)
 
37
    mail_files = find_mails(dir, idx)
 
38
    flags = nil
 
39
    if mail_files.length > 0
 
40
      # get the info from the first result and delete everything
 
41
      info = maildir_file_info(mail_files[0])
 
42
      mail_files.each { |f| File.delete(File.join(dir, f)) }
 
43
    end
 
44
    store_message(dir, date, info) { |f| f.puts(mail) }
 
45
  end
 
46
 
 
47
  def to_s
 
48
    uri.to_s
 
49
  end
 
50
 
 
51
  def cleanup(folder, dryrun = false)
 
52
    dir = folder_dir(folder)
 
53
    puts "-- Considering #{dir}:"
 
54
    guarantee_maildir(dir)
 
55
 
 
56
    del_count = 0
 
57
    recent_time = Time.now() -- (3 * 24 * 60 * 60) # 3 days
 
58
    Dir[File.join(dir, 'cur', '*')].each do |fn|
 
59
      flags = maildir_file_info_flags(fn)
 
60
      # don't consider not-seen, flagged, or recent messages
 
61
      mtime = File.mtime(fn)
 
62
      next if (not flags.index('S') or
 
63
               flags.index('F') or
 
64
               mtime > recent_time)
 
65
      File.open(fn) do |f|
 
66
        mail = RMail::Parser.read(f)
 
67
      end
 
68
      if dryrun
 
69
        puts "To remove: #{subject} #{mtime}"
 
70
      else
 
71
        puts "Removing: #{subject} #{mtime}"
 
72
        File.delete(fn)
 
73
      end
 
74
      del_count += 1
 
75
    end
 
76
    puts "-- Deleted #{del_count} messages"
 
77
    return del_count
 
78
  end
 
79
 
 
80
  private
 
81
 
 
82
  def folder_dir(folder)
 
83
    return File.join('/', folder)
 
84
  end
 
85
 
 
86
  def store_message(dir, date, info, &block)
 
87
    # TODO: handle `date'
 
88
 
 
89
    guarantee_maildir(dir)
 
90
 
 
91
    stored = false
 
92
    Dir.chdir(dir) do |d|
 
93
      timer = 30
 
94
      fd = nil
 
95
      while timer >= 0
 
96
        new_fn = new_maildir_basefn
 
97
        tmp_path = File.join(dir, 'tmp', new_fn)
 
98
        new_path = File.join(dir, 'new', new_fn)
 
99
        begin
 
100
          fd = IO::sysopen(tmp_path,
 
101
                           Fcntl::O_WRONLY | Fcntl::O_EXCL | Fcntl::O_CREAT)
 
102
          break
 
103
        rescue Errno::EEXIST
 
104
          sleep 2
 
105
          timer -= 2
 
106
          next
 
107
        end
 
108
      end
 
109
 
 
110
      if fd
 
111
        begin
 
112
          f = IO.open(fd)
 
113
          # provide a writable interface for the caller
 
114
          yield f
 
115
          f.fsync
 
116
          File.link tmp_path, new_path
 
117
          stored = true
 
118
        ensure
 
119
          File.unlink tmp_path if File.exists? tmp_path
 
120
        end
 
121
      end
 
122
 
 
123
      if stored and info
 
124
        cur_path = File.join(dir, 'cur', new_fn + ':' + info)
 
125
        File.rename(new_path, cur_path)
 
126
      end
 
127
    end # Dir.chdir
 
128
 
 
129
    return stored
 
130
  end
 
131
 
 
132
  def find_mails(dir, idx)
 
133
    dir_paths = []
 
134
    ['cur', 'new'].each do |d|
 
135
      subdir = File.join(dir, d)
 
136
      raise "#{subdir} not a directory" unless File.directory? subdir
 
137
      Dir[File.join(subdir, '*')].each do |fn|
 
138
        File.open(fn) do |f|
 
139
          mail = RMail::Parser.read(f)
 
140
          cache_index = mail.header['Message-Id']
 
141
          next if not (cache_index and cache_index == idx)
 
142
          dir_paths.push(File.join(d, File.basename(fn)))
 
143
        end
 
144
      end
 
145
    end
 
146
    return dir_paths
 
147
  end
 
148
 
 
149
  def guarantee_maildir(dir)
 
150
    # Ensure maildir-folderness
 
151
    ['new', 'cur', 'tmp'].each do |d|
 
152
      FileUtils.mkdir_p(File.join(dir, d))
 
153
    end
 
154
  end
 
155
 
 
156
  def maildir_file_info(file)
 
157
    basename = File.basename(file)
 
158
    colon = basename.rindex(':')
 
159
 
 
160
    return (colon and basename.slice(colon + 1, -1))
 
161
  end
 
162
 
 
163
  # Shamelessly taken from
 
164
  # http://gitorious.org/sup/mainline/blobs/master/lib/sup/maildir.rb
 
165
  def new_maildir_basefn
 
166
    Kernel::srand()
 
167
    "#{Time.now.to_i.to_s}.#{$$}#{Kernel.rand(1000000)}.#{MYHOSTNAME}"
 
168
  end
 
169
end
 
170