popthis 0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (5) hide show
  1. data/README.markdown +31 -0
  2. data/Rakefile +51 -0
  3. data/bin/popthis +35 -0
  4. data/lib/pop_this.rb +218 -0
  5. metadata +60 -0
@@ -0,0 +1,31 @@
1
+ # Can't POP(3) This.
2
+
3
+ <img src="http://cwninja.github.com/popthis/hammertime.gif" alt="Hammer Time"/>
4
+
5
+ So you downloaded [inaction_mailer](http://github.com/cwninja/inaction_mailer), and you had a folder full of e-mails generated while writing your app?
6
+
7
+ Now you want to preview your e-mails?!? You people are never happy!
8
+
9
+ Well, I'm a little tipsy, so I'm going to be nice, and I'm going to let you get at your mails with POP3!
10
+
11
+ I hope you like it. I do!
12
+
13
+ ## Usage:
14
+
15
+ Start the server:
16
+
17
+ popthis tmp/mails/
18
+
19
+ Now, configure your mail client as follows:
20
+
21
+ * Server: **localhost**
22
+ * Protocol: **POP3**
23
+ * Port: **2220**
24
+ * Username: anything
25
+ * Password: anything
26
+
27
+ You should now be able to see the contents of your tmp/mails folder in your mail client!
28
+
29
+ ## Origins:
30
+
31
+ I stole most of the code from [here](http://snippets.dzone.com/posts/show/5906)... so credit where credit's due.
@@ -0,0 +1,51 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/testtask'
4
+ require 'rake/gempackagetask'
5
+
6
+ desc 'Default: run unit tests.'
7
+ task :default => :test
8
+
9
+ desc 'Test the plugin.'
10
+ Rake::TestTask.new(:test) do |t|
11
+ t.libs << 'lib'
12
+ t.pattern = 'test/**/*_test.rb'
13
+ t.verbose = true
14
+ end
15
+
16
+ spec = Gem::Specification.new do |s|
17
+ s.name = "popthis"
18
+ s.version = "0.3"
19
+ s.summary = "Run a pop server serving up the current dir."
20
+ s.description = "Run a pop server serving up the current dir."
21
+
22
+ s.files = FileList['[A-Z]*', 'lib/**/*.rb', 'test/**/*.rb', 'rails/*']
23
+ s.require_path = 'lib'
24
+ s.test_files = Dir[*['test/**/*_test.rb']]
25
+ s.bindir = 'bin'
26
+ s.executables = FileList["bin/*"].map { |f| File.basename(f) }
27
+
28
+ s.has_rdoc = true
29
+ s.extra_rdoc_files = ["README.markdown"]
30
+ s.rdoc_options = ['--line-numbers', '--inline-source', "--main", "README.textile"]
31
+
32
+ s.authors = ["Tom Lea"]
33
+ s.email = %q{commit@tomlea.co.uk}
34
+
35
+ s.platform = Gem::Platform::RUBY
36
+ end
37
+
38
+ Rake::GemPackageTask.new spec do |pkg|
39
+ pkg.need_tar = true
40
+ pkg.need_zip = true
41
+ end
42
+
43
+ desc "Clean files generated by rake tasks"
44
+ task :clobber => [:clobber_package]
45
+
46
+ desc "Generate a gemspec file"
47
+ task :gemspec do
48
+ File.open("#{spec.name}.gemspec", 'w') do |f|
49
+ f.write spec.to_ruby
50
+ end
51
+ end
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env ruby
2
+ require 'optparse'
3
+ require File.join(File.dirname(__FILE__), *%w[.. lib pop_this])
4
+
5
+ options = {
6
+ :host => "localhost",
7
+ :port => 2220
8
+ }
9
+
10
+ OptionParser.new do |opts|
11
+ opts.banner = "Usage: popthis [options] [path]"
12
+
13
+ opts.on("-p", "--port PORT", "Listen on PORT (default 2220)") do |v|
14
+ options[:port] = v.to_i
15
+ end
16
+
17
+ opts.on("-H", "--host HOST", "Listen on HOST (default localhost)") do |v|
18
+ options[:host] = v
19
+ end
20
+
21
+ opts.on_tail("-h", "--help", "Show this message") do
22
+ puts opts
23
+ exit
24
+ end
25
+ end.parse!
26
+
27
+ options[:path] = File.expand_path( ARGV.shift || Dir.pwd )
28
+
29
+ p options
30
+ server = PopThis::POP3Server.new(options)
31
+
32
+ trap("INT"){ server.stop }
33
+
34
+ server.start
35
+ server.join
@@ -0,0 +1,218 @@
1
+ require 'gserver'
2
+ require 'digest/md5'
3
+ module PopThis
4
+ class POP3Server < GServer
5
+
6
+ def initialize(options)
7
+ @hostname = options[:host]
8
+ super(options[:port])
9
+ @dir = options[:path]
10
+ end
11
+
12
+ class Email
13
+ attr_reader :filename
14
+ attr_accessor :deleted
15
+
16
+ def self.all(dir)
17
+ Dir.glob("#{ dir }/*").reject{|fn| fn =~ /^\./ || File.directory?(fn) }.map{|fn| Email.new(fn) }
18
+ end
19
+
20
+ def self.delete(email)
21
+ File.delete(email.filename)
22
+ end
23
+
24
+ def initialize(filename)
25
+ @filename = filename
26
+ @deleted = false
27
+ end
28
+
29
+ def email
30
+ @email ||= File.read(@filename)
31
+ end
32
+
33
+ def deleted?
34
+ deleted
35
+ end
36
+ end
37
+
38
+ def serve(io)
39
+ @state = 'auth'
40
+ @failed = 0
41
+ io.print("+OK POP3 server ready\r\n")
42
+ loop do
43
+ if IO.select([io], nil, nil, 0.1)
44
+ begin
45
+ data = io.readpartial(4096)
46
+ puts ">> #{data.chomp}"
47
+ ok, op = process_line(data)
48
+ puts "<< #{op.chomp}"
49
+ io.print op
50
+ break unless ok
51
+ rescue Exception
52
+ end
53
+ end
54
+ break if io.closed?
55
+ end
56
+ io.close unless io.closed?
57
+ end
58
+
59
+ def emails
60
+ @emails = Email.all(@dir)
61
+ end
62
+
63
+ def stat
64
+ msgs = bytes = 0
65
+ @emails.each do |e|
66
+ next if e.deleted?
67
+ msgs += 1
68
+ bytes += e.email.length
69
+ end
70
+ return msgs, bytes
71
+ end
72
+
73
+ def list(msgid = nil)
74
+ msgid = msgid.to_i if msgid
75
+ if msgid
76
+ return false if msgid > @emails.length or @emails[msgid-1].deleted?
77
+ return [ [msgid, @emails[msgid].email.length] ]
78
+ else
79
+ msgs = []
80
+ @emails.each_with_index do |e,i|
81
+ msgs << [ i + 1, e.email.length ]
82
+ end
83
+ msgs
84
+ end
85
+ end
86
+
87
+ def retr(msgid)
88
+ msgid = msgid.to_i
89
+ return false if msgid > @emails.length or @emails[msgid-1].deleted?
90
+ @emails[msgid-1].email
91
+ end
92
+
93
+ def dele(msgid)
94
+ msgid = msgid.to_i
95
+ return false if msgid > @emails.length
96
+ @emails[msgid-1].deleted = true
97
+ end
98
+
99
+ def rset
100
+ @emails.each do |e|
101
+ e.deleted = false
102
+ end
103
+ end
104
+
105
+ def quit
106
+ @emails.each do |e|
107
+ if e.deleted?
108
+ Email.delete(e)
109
+ end
110
+ end
111
+ end
112
+
113
+ def process_line(line)
114
+ line.chomp!
115
+ case @state
116
+ when 'auth'
117
+ case line
118
+ when /^QUIT$/
119
+ return false, "+OK popthis POP3 server signing off\r\n"
120
+ when /^USER (.+)$/
121
+ return true, "+OK #{$1} is most welcome here\r\n"
122
+ when /^PASS (.+)$/
123
+ @state = 'trans'
124
+ emails
125
+ msgs, bytes = stat
126
+ return true, "+OK #{msgs} messages (#{bytes} octets)\r\n"
127
+ end
128
+ when 'trans'
129
+ case line
130
+ when /^NOOP$/
131
+ return true, "+OK\r\n"
132
+ when /^STAT$/
133
+ msgs, bytes = stat
134
+ return true, "+OK #{msgs} #{bytes}\r\n"
135
+ when /^LIST$/
136
+ msgs, bytes = stat
137
+ msg = "+OK #{msgs} messages (#{bytes} octets)\r\n"
138
+ list.each do |num, bytes|
139
+ msg += "#{num} #{bytes}\r\n"
140
+ end
141
+ msg += ".\r\n"
142
+ return true, msg
143
+ when /^LIST (\d+)$/
144
+ msgs, bytes = stat
145
+ num, bytes = list($1)
146
+ if num
147
+ return true, "+OK #{num} #{bytes}\r\n"
148
+ else
149
+ return true, "-ERR no such message, only #{msgs} messages in maildrop\r\n"
150
+ end
151
+ when /^RETR (\d+)$/
152
+ msg = retr($1)
153
+ if msg
154
+ msg = "+OK #{msg.length} octets\r\n" + msg
155
+ msg += "\r\n.\r\n"
156
+ else
157
+ msg = "-ERR no such message\r\n"
158
+ end
159
+ return true, msg
160
+ when /^DELE (\d+)$/
161
+ if dele($1)
162
+ return true, "+OK message #{$1} deleted\r\n"
163
+ else
164
+ return true, "-ERR message #{$1} already deleted\r\n"
165
+ end
166
+ when /^RSET$/
167
+ rset
168
+ msgs, bytes = stat
169
+ return true, "+OK maildrop has #{msgs} messages (#{bytes} octets)\r\n"
170
+ when /^QUIT$/
171
+ @state = 'update'
172
+ quit
173
+ msgs, bytes = stat
174
+ if msgs > 0
175
+ return false, "+OK popthis server signing off (#{msgs} messages left)\r\n"
176
+ else
177
+ return false, "+OK popthis server signing off (folder empty)\r\n"
178
+ end
179
+ when /^TOP (\d+) (\d+)$/
180
+ lines = $2
181
+ msg = retr($1)
182
+ unless msg
183
+ return true, "-ERR no such message\r\n"
184
+ end
185
+ cnt = nil
186
+ final = ""
187
+ msg.split(/\n/).each do |l|
188
+ final += l+"\n"
189
+ if cnt
190
+ cnt += 1
191
+ break if cnt > lines
192
+ end
193
+ if l !~ /\w/
194
+ cnt = 0
195
+ end
196
+ end
197
+ return true, "+OK\r\n"+final+".\r\n"
198
+ when /^UIDL$/
199
+ msgid = 0
200
+ msg = ''
201
+ @emails.each do |e|
202
+ msgid += 1
203
+ next if e.deleted?
204
+ msg += "#{msgid} #{Digest::MD5.hexdigest(e.email)}\r\n"
205
+ end
206
+ return true, "+OK\r\n#{msg}.\r\n";
207
+ end
208
+ when 'update'
209
+ case line
210
+ when /^QUIT$/
211
+ return true, "+OK popthis server signing off\r\n"
212
+ end
213
+ end
214
+ return true, "-ERR unknown command\r\n"
215
+ end
216
+
217
+ end
218
+ end
metadata ADDED
@@ -0,0 +1,60 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: popthis
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.3"
5
+ platform: ruby
6
+ authors:
7
+ - Tom Lea
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-23 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Run a pop server serving up the current dir.
17
+ email: commit@tomlea.co.uk
18
+ executables:
19
+ - popthis
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.markdown
24
+ files:
25
+ - Rakefile
26
+ - README.markdown
27
+ - lib/pop_this.rb
28
+ has_rdoc: true
29
+ homepage:
30
+ licenses: []
31
+
32
+ post_install_message:
33
+ rdoc_options:
34
+ - --line-numbers
35
+ - --inline-source
36
+ - --main
37
+ - README.textile
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: "0"
45
+ version:
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: "0"
51
+ version:
52
+ requirements: []
53
+
54
+ rubyforge_project:
55
+ rubygems_version: 1.3.5
56
+ signing_key:
57
+ specification_version: 3
58
+ summary: Run a pop server serving up the current dir.
59
+ test_files: []
60
+