impostor 0.0.1
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 +4 -0
- data/Manifest.txt +50 -0
- data/README.txt +65 -0
- data/Rakefile +40 -0
- data/lib/impostor.rb +6 -0
- data/lib/www/impostor/phpbb2.rb +260 -0
- data/lib/www/impostor/wwf79.rb +254 -0
- data/lib/www/impostor/wwf80.rb +264 -0
- data/lib/www/impostor.rb +269 -0
- data/test/fixtures/phpbb2-get-new_topic-form-good-response.html +725 -0
- data/test/fixtures/phpbb2-get-viewtopic-for-new-topic-good-response.html +364 -0
- data/test/fixtures/phpbb2-get-viewtopic-for-new-topic-malformed-response.html +364 -0
- data/test/fixtures/phpbb2-index.html +361 -0
- data/test/fixtures/phpbb2-logged-in.html +349 -0
- data/test/fixtures/phpbb2-login.html +306 -0
- data/test/fixtures/phpbb2-not-logged-in.html +349 -0
- data/test/fixtures/phpbb2-post-new_topic-good-response.html +290 -0
- data/test/fixtures/phpbb2-post-reply-good-response.html +290 -0
- data/test/fixtures/phpbb2-post-reply-throttled-response.html +290 -0
- data/test/fixtures/phpbb2-too-many-posts.html +290 -0
- data/test/fixtures/wwf79-forum_posts.html +422 -0
- data/test/fixtures/wwf79-general-new-topic-error.html +46 -0
- data/test/fixtures/wwf79-general-posting-error.html +46 -0
- data/test/fixtures/wwf79-good-post-forum_posts.html +462 -0
- data/test/fixtures/wwf79-index.html +137 -0
- data/test/fixtures/wwf79-logged-in.html +46 -0
- data/test/fixtures/wwf79-login.html +123 -0
- data/test/fixtures/wwf79-new-topic-forum_posts-response.html +305 -0
- data/test/fixtures/wwf79-new-topic-post_message_form.html +212 -0
- data/test/fixtures/wwf79-not-logged-in.html +128 -0
- data/test/fixtures/wwf79-too-many-posts.html +46 -0
- data/test/fixtures/wwf79-too-many-topics.html +46 -0
- data/test/fixtures/wwf80-general-posting-error.html +54 -0
- data/test/fixtures/wwf80-get-new_topic-form-good-response.html +217 -0
- data/test/fixtures/wwf80-get-viewtopic-for-new-topic-good-response.html +290 -0
- data/test/fixtures/wwf80-index.html +371 -0
- data/test/fixtures/wwf80-logged-in.html +52 -0
- data/test/fixtures/wwf80-login.html +125 -0
- data/test/fixtures/wwf80-new_reply_form.html +204 -0
- data/test/fixtures/wwf80-not-logged-in.html +136 -0
- data/test/fixtures/wwf80-post-new_topic-good-response.html +293 -0
- data/test/fixtures/wwf80-post-reply-good-response.html +331 -0
- data/test/fixtures/wwf80-too-many-posts.html +54 -0
- data/test/test_helper.rb +38 -0
- data/test/test_www_impostor.rb +165 -0
- data/test/test_www_impostor_phpbb2.rb +536 -0
- data/test/test_www_impostor_wwf79.rb +535 -0
- data/test/test_www_impostor_wwf80.rb +535 -0
- data/vendor/plugins/impostor/lib/autotest/discover.rb +3 -0
- data/vendor/plugins/impostor/lib/autotest/impostor.rb +49 -0
- data.tar.gz.sig +3 -0
- metadata +156 -0
- metadata.gz.sig +3 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.txt
|
4
|
+
Rakefile
|
5
|
+
lib/impostor.rb
|
6
|
+
lib/www/impostor.rb
|
7
|
+
lib/www/impostor/phpbb2.rb
|
8
|
+
lib/www/impostor/wwf79.rb
|
9
|
+
lib/www/impostor/wwf80.rb
|
10
|
+
test/fixtures/phpbb2-get-new_topic-form-good-response.html
|
11
|
+
test/fixtures/phpbb2-get-viewtopic-for-new-topic-good-response.html
|
12
|
+
test/fixtures/phpbb2-get-viewtopic-for-new-topic-malformed-response.html
|
13
|
+
test/fixtures/phpbb2-index.html
|
14
|
+
test/fixtures/phpbb2-logged-in.html
|
15
|
+
test/fixtures/phpbb2-login.html
|
16
|
+
test/fixtures/phpbb2-not-logged-in.html
|
17
|
+
test/fixtures/phpbb2-post-new_topic-good-response.html
|
18
|
+
test/fixtures/phpbb2-post-reply-good-response.html
|
19
|
+
test/fixtures/phpbb2-post-reply-throttled-response.html
|
20
|
+
test/fixtures/phpbb2-too-many-posts.html
|
21
|
+
test/fixtures/wwf79-forum_posts.html
|
22
|
+
test/fixtures/wwf79-general-new-topic-error.html
|
23
|
+
test/fixtures/wwf79-general-posting-error.html
|
24
|
+
test/fixtures/wwf79-good-post-forum_posts.html
|
25
|
+
test/fixtures/wwf79-index.html
|
26
|
+
test/fixtures/wwf79-logged-in.html
|
27
|
+
test/fixtures/wwf79-login.html
|
28
|
+
test/fixtures/wwf79-new-topic-forum_posts-response.html
|
29
|
+
test/fixtures/wwf79-new-topic-post_message_form.html
|
30
|
+
test/fixtures/wwf79-not-logged-in.html
|
31
|
+
test/fixtures/wwf79-too-many-posts.html
|
32
|
+
test/fixtures/wwf79-too-many-topics.html
|
33
|
+
test/fixtures/wwf80-general-posting-error.html
|
34
|
+
test/fixtures/wwf80-get-new_topic-form-good-response.html
|
35
|
+
test/fixtures/wwf80-get-viewtopic-for-new-topic-good-response.html
|
36
|
+
test/fixtures/wwf80-index.html
|
37
|
+
test/fixtures/wwf80-logged-in.html
|
38
|
+
test/fixtures/wwf80-login.html
|
39
|
+
test/fixtures/wwf80-new_reply_form.html
|
40
|
+
test/fixtures/wwf80-not-logged-in.html
|
41
|
+
test/fixtures/wwf80-post-new_topic-good-response.html
|
42
|
+
test/fixtures/wwf80-post-reply-good-response.html
|
43
|
+
test/fixtures/wwf80-too-many-posts.html
|
44
|
+
test/test_helper.rb
|
45
|
+
test/test_www_impostor.rb
|
46
|
+
test/test_www_impostor_phpbb2.rb
|
47
|
+
test/test_www_impostor_wwf79.rb
|
48
|
+
test/test_www_impostor_wwf80.rb
|
49
|
+
vendor/plugins/impostor/lib/autotest/discover.rb
|
50
|
+
vendor/plugins/impostor/lib/autotest/impostor.rb
|
data/README.txt
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
impostor
|
2
|
+
by Mike Mondragon
|
3
|
+
http://impostor.rubyforge.org/
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
imPOSTor posts messages to forums
|
8
|
+
|
9
|
+
== FEATURES/PROBLEMS:
|
10
|
+
|
11
|
+
Makes automatic posts to the following forum applications:
|
12
|
+
|
13
|
+
* Web Wiz Forums (WWF) 7.9
|
14
|
+
* Web Wiz Forums (WWF) 8.0
|
15
|
+
* PHP Bullitin Board (phpBB) 2.0 (2.0.22)
|
16
|
+
|
17
|
+
== SYNOPSIS:
|
18
|
+
|
19
|
+
# config yaml has options specefic to wwf79, wwf80, phpbb2, etc.
|
20
|
+
# read the impostor docs for options to the kind of forum in use
|
21
|
+
# config can be keyed by symbols or strings
|
22
|
+
config = YAML::load_file('conf/impostor.yml')
|
23
|
+
post = WWW::Impostor.new(config)
|
24
|
+
message = %q{hello world is to application
|
25
|
+
programmers as tea pots are to graphics programmers}
|
26
|
+
# your application stores forum and topic ids
|
27
|
+
post.post(forum=5,topic=10,message)
|
28
|
+
# make a new topic
|
29
|
+
subject = "about programmers..."
|
30
|
+
post.new_topic(forum=7,subject,message)
|
31
|
+
post.logout
|
32
|
+
|
33
|
+
== REQUIREMENTS:
|
34
|
+
|
35
|
+
* mechanize
|
36
|
+
* hpricot
|
37
|
+
|
38
|
+
== INSTALL:
|
39
|
+
|
40
|
+
* sudo gem install impostor
|
41
|
+
|
42
|
+
== LICENSE:
|
43
|
+
|
44
|
+
(The MIT License)
|
45
|
+
|
46
|
+
Copyright (c) 2008 Mike Mondragon
|
47
|
+
|
48
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
49
|
+
a copy of this software and associated documentation files (the
|
50
|
+
'Software'), to deal in the Software without restriction, including
|
51
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
52
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
53
|
+
permit persons to whom the Software is furnished to do so, subject to
|
54
|
+
the following conditions:
|
55
|
+
|
56
|
+
The above copyright notice and this permission notice shall be
|
57
|
+
included in all copies or substantial portions of the Software.
|
58
|
+
|
59
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
60
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
61
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
62
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
63
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
64
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
65
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
begin
|
6
|
+
require 'rcov/rcovtask'
|
7
|
+
rescue LoadError
|
8
|
+
end
|
9
|
+
require 'mechanize'
|
10
|
+
$LOAD_PATH.unshift 'lib'
|
11
|
+
require 'impostor'
|
12
|
+
|
13
|
+
Hoe.new('impostor', WWW::Impostor::VERSION) do |p|
|
14
|
+
p.rubyforge_name = 'impostor'
|
15
|
+
p.author = 'Mike Mondragon'
|
16
|
+
p.email = 'mikemondragon@gmail.com'
|
17
|
+
p.summary = 'imPOSTor posts messages to non-RESTful forums and blogs'
|
18
|
+
p.description = p.paragraphs_of('README.txt', 3..6).join("\n\n")
|
19
|
+
p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
|
20
|
+
p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
|
21
|
+
p.extra_deps << ['hpricot', '>= 0.5.0']
|
22
|
+
p.extra_deps << ['mechanize', '>= 0.7.0']
|
23
|
+
p.clean_globs << 'coverage'
|
24
|
+
end
|
25
|
+
|
26
|
+
begin
|
27
|
+
Rcov::RcovTask.new do |t|
|
28
|
+
t.test_files = FileList['test/test_www_impostor*.rb']
|
29
|
+
t.verbose = true
|
30
|
+
end
|
31
|
+
rescue NameError
|
32
|
+
end
|
33
|
+
|
34
|
+
Rake::TestTask.new do |t|
|
35
|
+
t.libs << "test"
|
36
|
+
t.test_files = FileList['test/test_www_impostor*.rb']
|
37
|
+
t.verbose = true
|
38
|
+
end
|
39
|
+
|
40
|
+
# vim: syntax=Ruby
|
data/lib/impostor.rb
ADDED
@@ -0,0 +1,260 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'hpricot'
|
3
|
+
gem 'mechanize', '>= 0.7.0'
|
4
|
+
require 'mechanize'
|
5
|
+
require 'cgi'
|
6
|
+
|
7
|
+
##
|
8
|
+
# phpBB version of the Impostor
|
9
|
+
#
|
10
|
+
|
11
|
+
class WWW::Impostor
|
12
|
+
|
13
|
+
class Phpbb2 < WWW::Impostor
|
14
|
+
|
15
|
+
##
|
16
|
+
# After initializing the parent a mechanize agent is created
|
17
|
+
#
|
18
|
+
# Additional configuration parameters:
|
19
|
+
#
|
20
|
+
# :posting_page
|
21
|
+
#
|
22
|
+
# Typical configuration parameters
|
23
|
+
# { :type => :phpbb2,
|
24
|
+
# :app_root => 'http://example.com/forum/',
|
25
|
+
# :login_page => 'login.php',
|
26
|
+
# :posting_page => 'posting.php',
|
27
|
+
# :user_agent => 'Windows IE 7',
|
28
|
+
# :username => 'myuser',
|
29
|
+
# :password => 'mypasswd' }
|
30
|
+
|
31
|
+
def initialize(config={})
|
32
|
+
super(config)
|
33
|
+
@agent = WWW::Mechanize.new
|
34
|
+
@agent.user_agent_alias = user_agent
|
35
|
+
# jar is a yaml file
|
36
|
+
@agent.cookie_jar.load(cookie_jar) if cookie_jar && File.exist?(cookie_jar)
|
37
|
+
@message = nil
|
38
|
+
@loggedin = false
|
39
|
+
end
|
40
|
+
|
41
|
+
##
|
42
|
+
# clean up the state of the library and log out
|
43
|
+
|
44
|
+
def logout
|
45
|
+
return false unless @loggedin
|
46
|
+
|
47
|
+
@agent.cookie_jar.save_as(cookie_jar) if cookie_jar
|
48
|
+
save_topics
|
49
|
+
|
50
|
+
@forum = nil
|
51
|
+
@topic = nil
|
52
|
+
@message = nil
|
53
|
+
|
54
|
+
@loggedin = false
|
55
|
+
true
|
56
|
+
end
|
57
|
+
|
58
|
+
##
|
59
|
+
# make a new topic
|
60
|
+
|
61
|
+
def new_topic(forum=@forum, subject=@subject, message=@message)
|
62
|
+
raise PostError.new("forum not set") unless forum
|
63
|
+
raise PostError.new("topic name not given") unless subject
|
64
|
+
raise PostError.new("message not set") unless message
|
65
|
+
|
66
|
+
login
|
67
|
+
raise PostError.new("not logged in") unless @loggedin
|
68
|
+
|
69
|
+
uri = posting_page
|
70
|
+
uri.query = "mode=newtopic&f=#{forum}"
|
71
|
+
|
72
|
+
# get the submit form
|
73
|
+
begin
|
74
|
+
page = @agent.get(uri)
|
75
|
+
rescue StandardError => err
|
76
|
+
raise PostError.new(err)
|
77
|
+
end
|
78
|
+
|
79
|
+
form = page.form('post') rescue nil
|
80
|
+
button = form.buttons.with.name('post').first rescue nil
|
81
|
+
raise PostError.new("post form not found") unless button && form
|
82
|
+
|
83
|
+
# set up the form and submit it
|
84
|
+
form.subject = subject
|
85
|
+
form.message = message
|
86
|
+
form['disable_html'] = nil
|
87
|
+
form['disable_bbcode'] = 'on'
|
88
|
+
form['disable_smilies'] = 'on'
|
89
|
+
begin
|
90
|
+
page = @agent.submit(form, button)
|
91
|
+
rescue StandardError => err
|
92
|
+
raise PostError.new(err)
|
93
|
+
end
|
94
|
+
|
95
|
+
# if the response is correct there will be a meta link that looks something like
|
96
|
+
# <meta http-equiv="refresh" content="3;url=viewtopic.php?p=29#29">
|
97
|
+
#
|
98
|
+
# this link needs to be followed, the page it leads to will give us
|
99
|
+
# the topic id that was created for the topic name that we created
|
100
|
+
a = (page.search("//meta[@http-equiv='refresh']").attr('content') rescue nil)
|
101
|
+
a = (/url=(.*)/.match(a)[1] rescue nil)
|
102
|
+
raise PostError.new('unexpected new topic response from refresh') unless a
|
103
|
+
|
104
|
+
a = URI.join(app_root, a)
|
105
|
+
page = @agent.get(a)
|
106
|
+
link = (page.search("//link[@rel='prev']").first['href'] rescue nil)
|
107
|
+
raise PostError.new('unexpected new topic response from link prev') unless link
|
108
|
+
|
109
|
+
# t=XXX will be our new topic id, i.e.
|
110
|
+
# <link rel="prev" href="http://localhost/phpBB2/viewtopic.php?t=5&view=previous" title="View previous topic"
|
111
|
+
u = (URI.parse(link) rescue nil)
|
112
|
+
topic = (CGI::parse(u.query)['t'][0] rescue nil)
|
113
|
+
topic = topic.to_i
|
114
|
+
raise PostError.new('unexpected new topic ID') unless topic > 0
|
115
|
+
|
116
|
+
# save new topic id and topic name
|
117
|
+
add_subject(forum, topic, subject)
|
118
|
+
@forum=forum; @topic=topic; @subject=subject; @message=message
|
119
|
+
true
|
120
|
+
end
|
121
|
+
|
122
|
+
##
|
123
|
+
# Attempt to post to the forum
|
124
|
+
|
125
|
+
def post(forum = @forum, topic = @topic, message = @message)
|
126
|
+
raise PostError.new("forum not set") unless forum
|
127
|
+
raise PostError.new("topic not set") unless topic
|
128
|
+
raise PostError.new("message not set") unless message
|
129
|
+
|
130
|
+
login
|
131
|
+
raise PostError.new("not logged in") unless @loggedin
|
132
|
+
|
133
|
+
uri = posting_page
|
134
|
+
uri.query = "mode=reply&t=#{topic}"
|
135
|
+
|
136
|
+
# get the submit form
|
137
|
+
begin
|
138
|
+
page = @agent.get(uri)
|
139
|
+
rescue StandardError => err
|
140
|
+
raise PostError.new(err)
|
141
|
+
end
|
142
|
+
|
143
|
+
form = page.form('post') rescue nil
|
144
|
+
button = form.buttons.with.name('post').first rescue nil
|
145
|
+
raise PostError.new("post form not found") unless button && form
|
146
|
+
|
147
|
+
# set up the form and submit it
|
148
|
+
form.message = message
|
149
|
+
form['disable_html'] = nil
|
150
|
+
form['disable_bbcode'] = 'on'
|
151
|
+
form['disable_smilies'] = 'on'
|
152
|
+
begin
|
153
|
+
page = @agent.submit(form, button)
|
154
|
+
rescue StandardError => err
|
155
|
+
raise PostError.new(err)
|
156
|
+
end
|
157
|
+
|
158
|
+
mes = page.search("//span[@class='gen']").last
|
159
|
+
posted = mes.innerText =~ /Your message has been entered successfully./ rescue false
|
160
|
+
if posted
|
161
|
+
@forum=forum; @topic=topic; @subject=get_subject(forum,topic); @message=message
|
162
|
+
return true
|
163
|
+
end
|
164
|
+
|
165
|
+
too_many = (mes.innerText =~
|
166
|
+
/You cannot make another post so soon after your last; please try again in a short while./ rescue
|
167
|
+
false)
|
168
|
+
raise ThrottledError.new("too many posts in too short amount of time") if too_many
|
169
|
+
|
170
|
+
# false otherwise, should we raise an exception instead?
|
171
|
+
false
|
172
|
+
end
|
173
|
+
|
174
|
+
##
|
175
|
+
# Get the posting page for the application (specific to phpBB2)
|
176
|
+
|
177
|
+
def posting_page
|
178
|
+
URI.join(app_root, config[:posting_page])
|
179
|
+
end
|
180
|
+
|
181
|
+
##
|
182
|
+
# does the work of logging into phpbb
|
183
|
+
|
184
|
+
def login
|
185
|
+
return true if @loggedin
|
186
|
+
|
187
|
+
# get the login page
|
188
|
+
page = fetch_login_page
|
189
|
+
|
190
|
+
# return if we are already logged in from a cookie state
|
191
|
+
return true if logged_in?(page)
|
192
|
+
|
193
|
+
# setup the form and submit
|
194
|
+
form, button = login_form_and_button(page)
|
195
|
+
page = post_login(form, button)
|
196
|
+
|
197
|
+
# set up the rest of the state if we are logged in
|
198
|
+
@loggedin = logged_in?(page)
|
199
|
+
load_topics if @loggedin
|
200
|
+
|
201
|
+
@loggedin
|
202
|
+
end
|
203
|
+
|
204
|
+
def version
|
205
|
+
@version ||= self.class.to_s
|
206
|
+
end
|
207
|
+
|
208
|
+
protected
|
209
|
+
|
210
|
+
##
|
211
|
+
# does the work of posting the login form
|
212
|
+
|
213
|
+
def post_login(form, button)
|
214
|
+
begin
|
215
|
+
page = @agent.submit(form, button)
|
216
|
+
rescue StandardError => err
|
217
|
+
raise LoginError.new(err)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
##
|
222
|
+
# returns the login form and its button from the login page
|
223
|
+
|
224
|
+
def login_form_and_button(page)
|
225
|
+
form = page.forms.first rescue nil
|
226
|
+
raise LoginError.new("unknown login page format") unless form
|
227
|
+
|
228
|
+
button = page.forms.first.buttons.with.name('login').first
|
229
|
+
form['username'] = username
|
230
|
+
form['password'] = password
|
231
|
+
form['autologin'] = 'on'
|
232
|
+
|
233
|
+
return form, button
|
234
|
+
end
|
235
|
+
|
236
|
+
##
|
237
|
+
# fetches the login page
|
238
|
+
|
239
|
+
def fetch_login_page
|
240
|
+
begin
|
241
|
+
page = @agent.get(login_page)
|
242
|
+
rescue StandardError => err
|
243
|
+
raise LoginError.new(err)
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
##
|
248
|
+
# Checks if the agent is already logged by stored cookie
|
249
|
+
|
250
|
+
def logged_in?(page)
|
251
|
+
mm = page.search("//a[@class='mainmenu']")
|
252
|
+
return false unless mm
|
253
|
+
mm.each do |m|
|
254
|
+
return true if (m.innerText =~ /Log out \[ #{username} \]/ rescue false)
|
255
|
+
end
|
256
|
+
false
|
257
|
+
end
|
258
|
+
|
259
|
+
end
|
260
|
+
end
|
@@ -0,0 +1,254 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'hpricot'
|
3
|
+
gem 'mechanize', '>= 0.7.0'
|
4
|
+
require 'mechanize'
|
5
|
+
require 'cgi'
|
6
|
+
|
7
|
+
##
|
8
|
+
# Web Wiz Forums version 7.9 of the Impostor
|
9
|
+
#
|
10
|
+
|
11
|
+
class WWW::Impostor
|
12
|
+
|
13
|
+
class Wwf79 < WWW::Impostor
|
14
|
+
|
15
|
+
##
|
16
|
+
# After initializing the parent a mechanize agent is created
|
17
|
+
#
|
18
|
+
# Additional configuration parameters:
|
19
|
+
#
|
20
|
+
# :forum_posts_page
|
21
|
+
# :post_message_page
|
22
|
+
#
|
23
|
+
# Typical configuration parameters
|
24
|
+
# { :type => :wwf79,
|
25
|
+
# :app_root => 'http://example.com/forum/',
|
26
|
+
# :login_page => 'login_user.asp',
|
27
|
+
# :forum_posts_page => 'forum_posts.asp',
|
28
|
+
# :post_message_page => 'post_message_form.asp'
|
29
|
+
# :user_agent => 'Windows IE 7',
|
30
|
+
# :username => 'myuser',
|
31
|
+
# :password => 'mypasswd' }
|
32
|
+
|
33
|
+
def initialize(config={})
|
34
|
+
super(config)
|
35
|
+
@agent = WWW::Mechanize.new
|
36
|
+
@agent.user_agent_alias = user_agent
|
37
|
+
@agent.cookie_jar.load(cookie_jar) if cookie_jar && File.exist?(cookie_jar)
|
38
|
+
@message = nil
|
39
|
+
@loggedin = false
|
40
|
+
end
|
41
|
+
|
42
|
+
##
|
43
|
+
# clean up the state of the library and log out
|
44
|
+
|
45
|
+
def logout
|
46
|
+
return false unless @loggedin
|
47
|
+
|
48
|
+
@agent.cookie_jar.save_as(cookie_jar) if cookie_jar
|
49
|
+
save_topics
|
50
|
+
|
51
|
+
@forum = nil
|
52
|
+
@topic = nil
|
53
|
+
@message = nil
|
54
|
+
|
55
|
+
@loggedin = false
|
56
|
+
true
|
57
|
+
end
|
58
|
+
|
59
|
+
##
|
60
|
+
# create a new topic
|
61
|
+
|
62
|
+
def new_topic(forum=@forum, subject=@subject, message=@message)
|
63
|
+
raise PostError.new("forum not set") unless forum
|
64
|
+
raise PostError.new("topic name not given") unless subject
|
65
|
+
raise PostError.new("message not set") unless message
|
66
|
+
|
67
|
+
login
|
68
|
+
raise PostError.new("not logged in") unless @loggedin
|
69
|
+
|
70
|
+
uri = post_message_page
|
71
|
+
uri.query = "FID=#{forum}"
|
72
|
+
|
73
|
+
# get the submit form
|
74
|
+
begin
|
75
|
+
page = @agent.get(uri)
|
76
|
+
rescue StandardError => err
|
77
|
+
raise PostError.new(err)
|
78
|
+
end
|
79
|
+
form = page.form('frmAddMessage') rescue nil
|
80
|
+
button = form.buttons.with.name('Submit').first rescue nil
|
81
|
+
raise PostError.new("post form not found") unless button && form
|
82
|
+
|
83
|
+
# set up the form and submit it
|
84
|
+
form.subject = subject
|
85
|
+
form.message = message
|
86
|
+
begin
|
87
|
+
page = @agent.submit(form, button)
|
88
|
+
rescue StandardError => err
|
89
|
+
raise PostError.new(err)
|
90
|
+
end
|
91
|
+
|
92
|
+
error = page.body =~ /Message Not Posted/
|
93
|
+
if error
|
94
|
+
|
95
|
+
# throttled
|
96
|
+
throttled = "You have exceeded the number of posts permitted in the time span"
|
97
|
+
too_many = page.body =~ /#{throttled}/
|
98
|
+
raise ThrottledError.new(throttled) if too_many
|
99
|
+
|
100
|
+
# general error
|
101
|
+
raise PostError.new("There was an error creating the topic")
|
102
|
+
end
|
103
|
+
|
104
|
+
# look up the new topic id
|
105
|
+
form = page.form('frmAddMessage') rescue nil
|
106
|
+
topic = form['TID'].to_i rescue 0
|
107
|
+
raise PostError.new('unexpected new topic ID') if topic < 1
|
108
|
+
|
109
|
+
# save new topic id and topic name
|
110
|
+
add_subject(forum, topic, subject)
|
111
|
+
@forum=forum; @topic=topic; @subject=subject; @message=message
|
112
|
+
return true
|
113
|
+
end
|
114
|
+
|
115
|
+
##
|
116
|
+
# Attempt to post to the forum
|
117
|
+
|
118
|
+
def post(forum = @forum, topic = @topic, message = @message)
|
119
|
+
raise PostError.new("forum not set") unless forum
|
120
|
+
raise PostError.new("topic not set") unless topic
|
121
|
+
raise PostError.new("message not set") unless message
|
122
|
+
|
123
|
+
login
|
124
|
+
raise PostError.new("not logged in") unless @loggedin
|
125
|
+
|
126
|
+
uri = forum_posts_page
|
127
|
+
uri.query = "TID=#{topic}&TPN=10000"
|
128
|
+
|
129
|
+
# get the submit form
|
130
|
+
begin
|
131
|
+
page = @agent.get(uri)
|
132
|
+
rescue StandardError => err
|
133
|
+
raise PostError.new(err)
|
134
|
+
end
|
135
|
+
form = page.form('frmAddMessage') rescue nil
|
136
|
+
button = form.buttons.with.name('Submit').first rescue nil
|
137
|
+
raise PostError.new("post form not found") unless button && form
|
138
|
+
|
139
|
+
# set up the form and submit it
|
140
|
+
form.message = message
|
141
|
+
begin
|
142
|
+
page = @agent.submit(form, button)
|
143
|
+
rescue StandardError => err
|
144
|
+
raise PostError.new(err)
|
145
|
+
end
|
146
|
+
|
147
|
+
error = page.body =~ /Message Not Posted/
|
148
|
+
if error
|
149
|
+
|
150
|
+
# throttled
|
151
|
+
throttled = "You have exceeded the number of posts permitted in the time span"
|
152
|
+
too_many = page.body =~ /#{throttled}/
|
153
|
+
raise ThrottledError.new(throttled) if too_many
|
154
|
+
|
155
|
+
# general error
|
156
|
+
raise PostError.new("There was an error making the post")
|
157
|
+
end
|
158
|
+
|
159
|
+
@forum=forum; @topic=topic; @subject=get_subject(forum,topic); @message=message
|
160
|
+
return true
|
161
|
+
end
|
162
|
+
|
163
|
+
##
|
164
|
+
# Get the new posts page for the application (specific to WWF7.9)
|
165
|
+
|
166
|
+
def forum_posts_page
|
167
|
+
URI.join(app_root, config[:forum_posts_page])
|
168
|
+
end
|
169
|
+
|
170
|
+
##
|
171
|
+
# Get the new topic page for the application (specific to WWF7.9)
|
172
|
+
|
173
|
+
def post_message_page
|
174
|
+
URI.join(app_root, config[:post_message_page])
|
175
|
+
end
|
176
|
+
|
177
|
+
##
|
178
|
+
# does the work of logging into WWF 7.9
|
179
|
+
|
180
|
+
def login
|
181
|
+
return true if @loggedin
|
182
|
+
|
183
|
+
# get the login page
|
184
|
+
page = fetch_login_page
|
185
|
+
|
186
|
+
# return if we are already logged in from a cookie state
|
187
|
+
return true if logged_in?(page)
|
188
|
+
|
189
|
+
# setup the form and submit
|
190
|
+
form, button = login_form_and_button(page)
|
191
|
+
page = post_login(form, button)
|
192
|
+
|
193
|
+
# set up the rest of the state if we are logged in
|
194
|
+
@loggedin = logged_in?(page)
|
195
|
+
load_topics if @loggedin
|
196
|
+
|
197
|
+
@loggedin
|
198
|
+
end
|
199
|
+
|
200
|
+
def version
|
201
|
+
@version ||= self.class.to_s
|
202
|
+
end
|
203
|
+
|
204
|
+
protected
|
205
|
+
|
206
|
+
##
|
207
|
+
# does the work of posting the login form
|
208
|
+
|
209
|
+
def post_login(form, button)
|
210
|
+
begin
|
211
|
+
page = @agent.submit(form, button)
|
212
|
+
rescue StandardError => err
|
213
|
+
raise LoginError.new(err)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
##
|
218
|
+
# returns the login form and its button from the login page
|
219
|
+
|
220
|
+
def login_form_and_button(page)
|
221
|
+
form = page.forms.with.name('frmLogin').first rescue nil
|
222
|
+
raise LoginError.new("unknown login page format") unless form
|
223
|
+
|
224
|
+
button = WWW::Mechanize::Form::Button.new('Submit', 'Forum Login')
|
225
|
+
form.add_button_to_query(button)
|
226
|
+
form['name'] = username
|
227
|
+
form['password'] = password
|
228
|
+
return form, button
|
229
|
+
end
|
230
|
+
|
231
|
+
##
|
232
|
+
# fetches the login page
|
233
|
+
|
234
|
+
def fetch_login_page
|
235
|
+
begin
|
236
|
+
page = @agent.get(login_page)
|
237
|
+
rescue StandardError => err
|
238
|
+
raise LoginError.new(err)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
##
|
243
|
+
# Checks if the agent is already logged by stored cookie
|
244
|
+
|
245
|
+
def logged_in?(page)
|
246
|
+
mm = page.search("//a[@class='nav']")
|
247
|
+
return false unless mm
|
248
|
+
mm.each do |m|
|
249
|
+
return true if (m.innerText =~ /Logout \[#{username}\]/ rescue false)
|
250
|
+
end
|
251
|
+
false
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|