wwmd 0.2.20.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.
- data/History.txt +38 -0
- data/README.rdoc +87 -0
- data/Rakefile +33 -0
- data/examples/config_example.yaml +24 -0
- data/examples/wwmd_example.rb +73 -0
- data/lib/wwmd.rb +84 -0
- data/lib/wwmd/class_extensions.rb +4 -0
- data/lib/wwmd/class_extensions/extensions_base.rb +251 -0
- data/lib/wwmd/class_extensions/extensions_encoding.rb +79 -0
- data/lib/wwmd/class_extensions/extensions_external.rb +18 -0
- data/lib/wwmd/class_extensions/extensions_nilclass.rb +11 -0
- data/lib/wwmd/class_extensions/extensions_rbkb.rb +193 -0
- data/lib/wwmd/class_extensions/mixins_string_encoding.rb +40 -0
- data/lib/wwmd/guid.rb +155 -0
- data/lib/wwmd/page.rb +3 -0
- data/lib/wwmd/page/_fa.old +302 -0
- data/lib/wwmd/page/auth.rb +17 -0
- data/lib/wwmd/page/constants.rb +63 -0
- data/lib/wwmd/page/form.rb +99 -0
- data/lib/wwmd/page/form_array.rb +304 -0
- data/lib/wwmd/page/headers.rb +118 -0
- data/lib/wwmd/page/helpers.rb +41 -0
- data/lib/wwmd/page/html2text_hpricot.rb +76 -0
- data/lib/wwmd/page/html2text_nokogiri.rb +42 -0
- data/lib/wwmd/page/inputs.rb +47 -0
- data/lib/wwmd/page/irb_helpers.rb +114 -0
- data/lib/wwmd/page/page.rb +257 -0
- data/lib/wwmd/page/parsing_convenience.rb +98 -0
- data/lib/wwmd/page/reporting_helpers.rb +89 -0
- data/lib/wwmd/page/scrape.rb +196 -0
- data/lib/wwmd/page/spider.rb +127 -0
- data/lib/wwmd/urlparse.rb +125 -0
- data/lib/wwmd/viewstate.rb +17 -0
- data/lib/wwmd/viewstate/viewstate.rb +101 -0
- data/lib/wwmd/viewstate/viewstate_deserializer_methods.rb +217 -0
- data/lib/wwmd/viewstate/viewstate_from_xml.rb +129 -0
- data/lib/wwmd/viewstate/viewstate_types.rb +51 -0
- data/lib/wwmd/viewstate/viewstate_utils.rb +164 -0
- data/lib/wwmd/viewstate/viewstate_yaml.rb +25 -0
- data/lib/wwmd/viewstate/vs_stubs.rb +22 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_array.rb +38 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_binary_serialized.rb +30 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_hashtable.rb +42 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_hybrid_dict.rb +42 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_indexed_string.rb +6 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_indexed_string_ref.rb +24 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_int_enum.rb +27 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_list.rb +34 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_pair.rb +29 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_read_types.rb +11 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_read_value.rb +35 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_sparse_array.rb +58 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_string.rb +33 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_string_array.rb +39 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_string_formatted.rb +32 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_stub_helpers.rb +37 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_triplet.rb +31 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_type.rb +23 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_unit.rb +30 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_value.rb +35 -0
- data/lib/wwmd/wwmd_config.rb +52 -0
- data/lib/wwmd/wwmd_puts.rb +9 -0
- data/lib/wwmd/wwmd_utils.rb +28 -0
- data/spec/README +3 -0
- data/spec/form_array.spec +49 -0
- data/spec/spider_csrf_test.spec +28 -0
- data/spec/urlparse_test.spec +101 -0
- data/tasks/ann.rake +80 -0
- data/tasks/bones.rake +20 -0
- data/tasks/gem.rake +201 -0
- data/tasks/git.rake +40 -0
- data/tasks/notes.rake +27 -0
- data/tasks/post_load.rake +34 -0
- data/tasks/rdoc.rake +51 -0
- data/tasks/rubyforge.rake +55 -0
- data/tasks/setup.rb +292 -0
- data/tasks/spec.rake +54 -0
- data/tasks/test.rake +40 -0
- data/tasks/zentest.rake +36 -0
- metadata +222 -0
@@ -0,0 +1,31 @@
|
|
1
|
+
module WWMD
|
2
|
+
class VSStubs::VSTriplet
|
3
|
+
include VSStubHelpers
|
4
|
+
|
5
|
+
attr_accessor :value
|
6
|
+
|
7
|
+
def initialize(obj1,obj2,obj3)
|
8
|
+
@value = []
|
9
|
+
@value << obj1
|
10
|
+
@value << obj2
|
11
|
+
@value << obj3
|
12
|
+
end
|
13
|
+
|
14
|
+
def serialize
|
15
|
+
stack = super
|
16
|
+
self.value.each do |v|
|
17
|
+
stack << v.serialize
|
18
|
+
end
|
19
|
+
return stack
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_xml
|
23
|
+
xml = super
|
24
|
+
self.value.each do |v|
|
25
|
+
xml.add_element(v.to_xml)
|
26
|
+
end
|
27
|
+
xml
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module WWMD
|
2
|
+
class VSStubs::VSType
|
3
|
+
include VSStubHelpers
|
4
|
+
|
5
|
+
attr_accessor :value
|
6
|
+
attr_reader :typeref
|
7
|
+
attr_reader :typeval
|
8
|
+
|
9
|
+
def initialize(typeref,typeval)
|
10
|
+
@typeref = typeref
|
11
|
+
@typeval = typeval
|
12
|
+
end
|
13
|
+
|
14
|
+
def serialize
|
15
|
+
super # cheat opcode + typeref + typeval
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_xml
|
19
|
+
super
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module WWMD
|
2
|
+
class VSStubs::VSUnit
|
3
|
+
include VSStubHelpers
|
4
|
+
|
5
|
+
attr_reader :dword
|
6
|
+
attr_reader :word
|
7
|
+
attr_reader :value
|
8
|
+
|
9
|
+
def initialize(dword,word)
|
10
|
+
@dword = dword
|
11
|
+
@word = word
|
12
|
+
@value = ''
|
13
|
+
end
|
14
|
+
|
15
|
+
def serialize
|
16
|
+
stack = super
|
17
|
+
stack << write_double(self.dword)
|
18
|
+
stack << write_single(self.word)
|
19
|
+
return stack
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_xml
|
23
|
+
xml = super
|
24
|
+
xml.add_attribute("dword",self.dword.to_s)
|
25
|
+
xml.add_attribute("word",self.word.to_s)
|
26
|
+
xml
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module WWMD
|
2
|
+
class VSStubs::VSValue
|
3
|
+
include VSStubHelpers
|
4
|
+
|
5
|
+
attr_accessor :value
|
6
|
+
|
7
|
+
# gin up all the single byte values
|
8
|
+
def initialize(str)
|
9
|
+
@value = str
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
@value.hexify
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_sym
|
17
|
+
VIEWSTATE_TYPES[opcode].to_sym
|
18
|
+
end
|
19
|
+
|
20
|
+
def opcode
|
21
|
+
@value
|
22
|
+
end
|
23
|
+
|
24
|
+
def serialize
|
25
|
+
super # cheat... just return opcode
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_xml
|
29
|
+
xml = super
|
30
|
+
xml.add_text(self.to_sym.to_s)
|
31
|
+
xml
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module WWMD
|
2
|
+
|
3
|
+
class WWMDConfig#:nodoc:
|
4
|
+
# for backward compat
|
5
|
+
def self.load_config(file); WWMD::load_config(file); end
|
6
|
+
def self.parse_opts(args); WWMD::parse_opts(args); end
|
7
|
+
end
|
8
|
+
|
9
|
+
def load_config(file)
|
10
|
+
begin
|
11
|
+
config = YAML.load_file(file)
|
12
|
+
rescue => e
|
13
|
+
putw "config file not found #{file}"
|
14
|
+
putw e.inspect
|
15
|
+
exit
|
16
|
+
end
|
17
|
+
return config
|
18
|
+
end
|
19
|
+
|
20
|
+
def parse_opts(args)
|
21
|
+
inopts = Hash.new
|
22
|
+
inopts[:max_redirects] = 10
|
23
|
+
inopts[:timeout] = 30
|
24
|
+
inopts[:scrape_warn] = false
|
25
|
+
opts = OptionParser.new do |opts|
|
26
|
+
# set defaults
|
27
|
+
opts.on("-p", "--password PASSWORD", "Password") { |v| inopts[:password] = v }
|
28
|
+
opts.on("-u", "--username USERNAME", "Username") { |v| inopts[:username] = v }
|
29
|
+
opts.on("--header_file HEADER_FILE","Header file") { |v| inopts[:header_file] = v }
|
30
|
+
opts.on("--base_url BASE_URL","Base url") { |v| inopts[:base_url] = v }
|
31
|
+
opts.on("--use_proxy PROXY_URL", "Use proxy at url") do |v|
|
32
|
+
ENV['HTTP_PROXY'] = "http://" + v.to_s
|
33
|
+
inopts[:use_proxy] = true
|
34
|
+
inopts[:proxy_url] = v
|
35
|
+
end
|
36
|
+
opts.on("--no_proxy","do not use proxy") do |v|
|
37
|
+
inopts[:use_proxy] = false
|
38
|
+
inopts[:proxy_url] = nil
|
39
|
+
end
|
40
|
+
opts.on("--use_auth","login before getting url") { |v| inopts[:use_auth] = true }
|
41
|
+
opts.on("--no_auth","no login before getting url") { |v| inopts[:use_auth] = false }
|
42
|
+
opts.on("--debug","debugging really doesn't work") { |v| inopts[:debug] = true }
|
43
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
44
|
+
puts opts
|
45
|
+
exit
|
46
|
+
end
|
47
|
+
end
|
48
|
+
opts.parse!(args)
|
49
|
+
return inopts
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module WWMDUtils
|
2
|
+
|
3
|
+
def self.header_array_from_file(filename)
|
4
|
+
ret = Hash.new
|
5
|
+
File.readlines(filename).each do |line|
|
6
|
+
a = line.chomp.split(/\t/,2)
|
7
|
+
ret[a[0]] = a[1]
|
8
|
+
end
|
9
|
+
return ret
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.ranstr(len=8,digits=false)
|
13
|
+
chars = ("a".."z").to_a
|
14
|
+
chars += ("0".."9").to_a if digits
|
15
|
+
ret = ""
|
16
|
+
1.upto(len) { |i| ret << chars[rand(chars.size-1)] }
|
17
|
+
return ret
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.rannum(len=8,hex=false)
|
21
|
+
chars = ("0".."9").to_a
|
22
|
+
chars += ("A".."F").to_a if hex
|
23
|
+
ret = ""
|
24
|
+
1.upto(len) { |i| ret << chars[rand(chars.size-1)] }
|
25
|
+
return ret
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
data/spec/README
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'wwmd'
|
3
|
+
include WWMD
|
4
|
+
require 'spec'
|
5
|
+
|
6
|
+
describe FormArray do
|
7
|
+
before(:each) do
|
8
|
+
@form = FormArray.new
|
9
|
+
end
|
10
|
+
|
11
|
+
it "sets a value and reads a value" do
|
12
|
+
@form["foo"] = "bar"
|
13
|
+
@form["foo"].should == "bar"
|
14
|
+
end
|
15
|
+
|
16
|
+
it "reads from a string" do
|
17
|
+
@form = "foo=bar&baz=eep&argle=bargle".to_form
|
18
|
+
@form["foo"].should == "bar"
|
19
|
+
@form["baz"].should == "eep"
|
20
|
+
@form["argle"].should == "bargle"
|
21
|
+
end
|
22
|
+
|
23
|
+
it "to_get" do
|
24
|
+
str = "foo=bar&baz=eep&argle=bargle"
|
25
|
+
get = "?" + str
|
26
|
+
@form = str.to_form
|
27
|
+
@form.to_get.should == get
|
28
|
+
end
|
29
|
+
|
30
|
+
it "remove_nulls!" do
|
31
|
+
@form["var1"] = "not null"
|
32
|
+
@form["var2"] = ""
|
33
|
+
@form["var3"] = nil
|
34
|
+
@form.remove_nulls!
|
35
|
+
@form.size.should == 1
|
36
|
+
@form["var1"].should == "not null"
|
37
|
+
end
|
38
|
+
|
39
|
+
it "clones correctly" do
|
40
|
+
@form = "foo=bar&baz=eep&argle=bargle".to_form
|
41
|
+
lform = @form.clone
|
42
|
+
lform["foo"] = "test"
|
43
|
+
@form["foo"].should == "bar"
|
44
|
+
lform["foo"].should == "test"
|
45
|
+
end
|
46
|
+
|
47
|
+
it "escapes characters correctly"
|
48
|
+
it "unescapes characters correctly"
|
49
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'wwmd'
|
3
|
+
include WWMD
|
4
|
+
require 'spec'
|
5
|
+
|
6
|
+
describe Page do
|
7
|
+
before(:each) do
|
8
|
+
@page = Page.new({:base_url => "http://localhost"})
|
9
|
+
@spider = @page.spider
|
10
|
+
@spider.csrf_token = "CsRf"
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should remove csrf tokens from visited and queued" do
|
14
|
+
url = "http://localhost/foo.php?CsRf=something&bar=baz"
|
15
|
+
links = ["http://localhost/q1.php?CsRf=omg&first=FIRST"]
|
16
|
+
@spider.add(url,links)
|
17
|
+
@spider.visited.first.should == "http://localhost/foo.php?CsRf=&bar=baz"
|
18
|
+
@spider.queued.first.should == "http://localhost/q1.php?CsRf=&first=FIRST"
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should work normally" do
|
22
|
+
url = "http://localhost/foo.php?hithere=something&bar=baz"
|
23
|
+
links = ["http://localhost/q1.php?hithere=omg&first=FIRST"]
|
24
|
+
@spider.add(url,links)
|
25
|
+
@spider.visited.first.should == "http://localhost/foo.php?hithere=something&bar=baz"
|
26
|
+
@spider.queued.first.should == "http://localhost/q1.php?hithere=omg&first=FIRST"
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'wwmd/urlparse'
|
3
|
+
include WWMD
|
4
|
+
require 'spec'
|
5
|
+
|
6
|
+
describe URLParse do
|
7
|
+
before(:each) do
|
8
|
+
@base = "https://www.base.com"
|
9
|
+
@up = URLParse.new()
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should parse the basic case" do
|
13
|
+
@up.parse("https://www.location.com/","/path/path/path/script.scr").to_s.should \
|
14
|
+
== "https://www.location.com/path/path/path/script.scr"
|
15
|
+
@up.proto.should == "https"
|
16
|
+
@up.location.should == "www.location.com"
|
17
|
+
@up.path.should == "/path/path/path/"
|
18
|
+
@up.script.should == "script.scr"
|
19
|
+
@up.parse("https://www.location.com/","/path/path/path/script").to_s.should \
|
20
|
+
== "https://www.location.com/path/path/path/script"
|
21
|
+
@up.proto.should == "https"
|
22
|
+
@up.location.should == "www.location.com"
|
23
|
+
@up.path.should == "/path/path/path/script"
|
24
|
+
# @up.script.should == nil
|
25
|
+
@up.script.should == ""
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should parse when complete urls are passed" do
|
29
|
+
@up.parse(@base,"https://www.location.com/hithere/dirname/test.php").to_s.should \
|
30
|
+
== "https://www.location.com/hithere/dirname/test.php"
|
31
|
+
@up.proto.should == "https"
|
32
|
+
@up.location.should == "www.location.com"
|
33
|
+
@up.path.should == "/hithere/dirname/"
|
34
|
+
@up.script.should == "test.php"
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should parse GET params correctly" do
|
38
|
+
@up.parse(@base,"http://www.location.com/test.php?foo=bar&baz=eep").to_s.should \
|
39
|
+
== "http://www.location.com/test.php?foo=bar&baz=eep"
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should return the path if the path is fully qualified" do
|
43
|
+
@up.parse(@base,"http://www.location.com/").to_s.should == "http://www.location.com/"
|
44
|
+
@up.parse(@base,"http://www.location.com").to_s.should == "http://www.location.com/"
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should parse a location + relative link" do
|
48
|
+
@up.parse("https://www.location.com","relative/script.scr").to_s.should \
|
49
|
+
== "https://www.location.com/relative/script.scr"
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should parse base urls with scripts (page.cur) + relative link" do
|
53
|
+
@up.parse("https://www.location.com/path/to/a_script.php", "more/script.scr").to_s.should == "https://www.location.com/path/to/more/script.scr"
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should parse base urls without scripts + relative link" do
|
57
|
+
@up.parse("https://www.location.com/path/to/end", "but/more/script.scr").to_s.should \
|
58
|
+
== "https://www.location.com/path/to/end/but/more/script.scr"
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should handle trailing slashes correctly" do
|
62
|
+
@up.parse(@base + "/","/test.php").to_s.should == "#{@base}/test.php"
|
63
|
+
@up.parse(@base + "/","/test.php").to_s.should_not == "#{@base}//test.php"
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should parse dotdot correctly" do
|
67
|
+
@up.parse(@base + "/one/two/thee/four","../../foo.php").to_s.should \
|
68
|
+
== "#{@base}/one/two/foo.php"
|
69
|
+
@up.parse(("https://www.location.com/relative///path//deep/one"),"more/..//stuff/../foo.php").to_s.should \
|
70
|
+
== "https://www.location.com/relative/path/deep/one/foo.php"
|
71
|
+
@up.parse("https://www.location.com/rel/path/foo.php","../../../../../../bar.php").to_s.should \
|
72
|
+
== "https://www.location.com/bar.php"
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should parse dot correctly" do
|
76
|
+
@up.parse("https://www.location.com","base/./../foo/././bar/script.scr").to_s.should \
|
77
|
+
== "https://www.location.com/foo/bar/script.scr"
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should remove get params when posting to a form action with get params" do
|
81
|
+
@up.parse("https://www.location.com/mail/h/1nyas6k8hplt9/?s=t","?s=t&at=xn3j38mvpzxqd138zgwsooxvojvbvd").to_s.should \
|
82
|
+
== "https://www.location.com/mail/h/1nyas6k8hplt9/?s=t&at=xn3j38mvpzxqd138zgwsooxvojvbvd"
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should not remove directory traversal params" do
|
86
|
+
@up.parse("http://www.example.com/?file=../../../../../../etc/passwd¶m1=foobar.log¶m2=false").to_s.should \
|
87
|
+
== "http://www.example.com/?file=../../../../../../etc/passwd¶m1=foobar.log¶m2=false"
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should not remove directory traversal params 2" do
|
91
|
+
@up.parse("http://www.example.com:8888/foobar/barBaz.do?logFile=../../../../../../../../../../../../etc/passwd&foo=foobar.log&bazeep=false").to_s.should \
|
92
|
+
== "http://www.example.com:8888/foobar/barBaz.do?logFile=../../../../../../../../../../../../etc/passwd&foo=foobar.log&bazeep=false"
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should not remove directory traversal params 2" do
|
96
|
+
@up.parse("http://www.example.com:8888", "/foobar/barBaz.do?logFile=../../../../../../../../../../../../etc/passwd&foo=foobar.log&bazeep=false").to_s.should \
|
97
|
+
== "http://www.example.com:8888/foobar/barBaz.do?logFile=../../../../../../../../../../../../etc/passwd&foo=foobar.log&bazeep=false"
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
data/tasks/ann.rake
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
|
2
|
+
begin
|
3
|
+
require 'bones/smtp_tls'
|
4
|
+
rescue LoadError
|
5
|
+
require 'net/smtp'
|
6
|
+
end
|
7
|
+
require 'time'
|
8
|
+
|
9
|
+
namespace :ann do
|
10
|
+
|
11
|
+
# A prerequisites task that all other tasks depend upon
|
12
|
+
task :prereqs
|
13
|
+
|
14
|
+
file PROJ.ann.file do
|
15
|
+
ann = PROJ.ann
|
16
|
+
puts "Generating #{ann.file}"
|
17
|
+
File.open(ann.file,'w') do |fd|
|
18
|
+
fd.puts("#{PROJ.name} version #{PROJ.version}")
|
19
|
+
fd.puts(" by #{Array(PROJ.authors).first}") if PROJ.authors
|
20
|
+
fd.puts(" #{PROJ.url}") if PROJ.url.valid?
|
21
|
+
fd.puts(" (the \"#{PROJ.release_name}\" release)") if PROJ.release_name
|
22
|
+
fd.puts
|
23
|
+
fd.puts("== DESCRIPTION")
|
24
|
+
fd.puts
|
25
|
+
fd.puts(PROJ.description)
|
26
|
+
fd.puts
|
27
|
+
fd.puts(PROJ.changes.sub(%r/^.*$/, '== CHANGES'))
|
28
|
+
fd.puts
|
29
|
+
ann.paragraphs.each do |p|
|
30
|
+
fd.puts "== #{p.upcase}"
|
31
|
+
fd.puts
|
32
|
+
fd.puts paragraphs_of(PROJ.readme_file, p).join("\n\n")
|
33
|
+
fd.puts
|
34
|
+
end
|
35
|
+
fd.puts ann.text if ann.text
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
desc "Create an announcement file"
|
40
|
+
task :announcement => ['ann:prereqs', PROJ.ann.file]
|
41
|
+
|
42
|
+
desc "Send an email announcement"
|
43
|
+
task :email => ['ann:prereqs', PROJ.ann.file] do
|
44
|
+
ann = PROJ.ann
|
45
|
+
from = ann.email[:from] || Array(PROJ.authors).first || PROJ.email
|
46
|
+
to = Array(ann.email[:to])
|
47
|
+
|
48
|
+
### build a mail header for RFC 822
|
49
|
+
rfc822msg = "From: #{from}\n"
|
50
|
+
rfc822msg << "To: #{to.join(',')}\n"
|
51
|
+
rfc822msg << "Subject: [ANN] #{PROJ.name} #{PROJ.version}"
|
52
|
+
rfc822msg << " (#{PROJ.release_name})" if PROJ.release_name
|
53
|
+
rfc822msg << "\n"
|
54
|
+
rfc822msg << "Date: #{Time.new.rfc822}\n"
|
55
|
+
rfc822msg << "Message-Id: "
|
56
|
+
rfc822msg << "<#{"%.8f" % Time.now.to_f}@#{ann.email[:domain]}>\n\n"
|
57
|
+
rfc822msg << File.read(ann.file)
|
58
|
+
|
59
|
+
params = [:server, :port, :domain, :acct, :passwd, :authtype].map do |key|
|
60
|
+
ann.email[key]
|
61
|
+
end
|
62
|
+
|
63
|
+
params[3] = PROJ.email if params[3].nil?
|
64
|
+
|
65
|
+
if params[4].nil?
|
66
|
+
STDOUT.write "Please enter your e-mail password (#{params[3]}): "
|
67
|
+
params[4] = STDIN.gets.chomp
|
68
|
+
end
|
69
|
+
|
70
|
+
### send email
|
71
|
+
Net::SMTP.start(*params) {|smtp| smtp.sendmail(rfc822msg, from, to)}
|
72
|
+
end
|
73
|
+
end # namespace :ann
|
74
|
+
|
75
|
+
desc 'Alias to ann:announcement'
|
76
|
+
task :ann => 'ann:announcement'
|
77
|
+
|
78
|
+
CLOBBER << PROJ.ann.file
|
79
|
+
|
80
|
+
# EOF
|