impostor 0.2.1 → 1.0.0

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 (108) hide show
  1. data/.gemtest +0 -0
  2. data/.gitignore +1 -0
  3. data/.rspec +2 -0
  4. data/.rvmrc +1 -0
  5. data/Gemfile +17 -0
  6. data/Gemfile.lock +47 -0
  7. data/History.txt +8 -0
  8. data/Manifest.txt +87 -55
  9. data/README.txt +11 -11
  10. data/Rakefile +16 -20
  11. data/lib/impostor/auth.rb +103 -0
  12. data/lib/impostor/config.rb +171 -0
  13. data/lib/impostor/errors.rb +67 -0
  14. data/lib/impostor/phpbb2.rb +202 -0
  15. data/lib/impostor/phpbb3.rb +199 -0
  16. data/lib/impostor/post.rb +111 -0
  17. data/lib/impostor/topic.rb +115 -0
  18. data/lib/impostor/wwf79.rb +186 -0
  19. data/lib/impostor/wwf80.rb +190 -0
  20. data/lib/impostor.rb +108 -5
  21. data/spec/auth_spec.rb +148 -0
  22. data/spec/base_spec_helper.rb +12 -0
  23. data/{test/test_helper.rb → spec/caged_net_http.rb} +8 -17
  24. data/spec/config_spec.rb +136 -0
  25. data/spec/fixtures/junk.html +1 -0
  26. data/{test → spec}/fixtures/phpbb2-get-new_topic-form-good-response.html +0 -0
  27. data/{test → spec}/fixtures/phpbb2-get-viewtopic-for-new-topic-good-response.html +11 -11
  28. data/{test → spec}/fixtures/phpbb2-get-viewtopic-for-new-topic-malformed-response.html +0 -0
  29. data/{test → spec}/fixtures/phpbb2-index.html +0 -0
  30. data/{test → spec}/fixtures/phpbb2-logged-in.html +0 -0
  31. data/{test → spec}/fixtures/phpbb2-login.html +0 -0
  32. data/{test → spec}/fixtures/phpbb2-not-logged-in.html +0 -0
  33. data/{test → spec}/fixtures/phpbb2-post-new_topic-good-response.html +0 -0
  34. data/{test → spec}/fixtures/phpbb2-post-reply-good-response.html +0 -0
  35. data/{test → spec}/fixtures/phpbb2-post-reply-throttled-response.html +0 -0
  36. data/{test → spec}/fixtures/phpbb2-too-many-posts.html +0 -0
  37. data/{test → spec}/fixtures/phpbb3-get-new-topic-form-good-response.html +0 -0
  38. data/{test → spec}/fixtures/phpbb3-get-reply-form-good-response.html +0 -0
  39. data/{test → spec}/fixtures/phpbb3-logged-in.html +0 -0
  40. data/{test → spec}/fixtures/phpbb3-login.html +0 -0
  41. data/{test → spec}/fixtures/phpbb3-not-logged-in.html +0 -0
  42. data/{test → spec}/fixtures/phpbb3-post-new_topic-good-response.html +0 -0
  43. data/{test → spec}/fixtures/phpbb3-post-reply-good-response.html +0 -0
  44. data/spec/fixtures/vcr_cassettes/phpbb2-should-be-overlimit-creating-topic.yml +1308 -0
  45. data/spec/fixtures/vcr_cassettes/phpbb2-should-create-topic.yml +923 -0
  46. data/spec/fixtures/vcr_cassettes/phpbb2-should-login.yml +360 -0
  47. data/spec/fixtures/vcr_cassettes/phpbb2-should-not-create-new-topic.yml +497 -0
  48. data/spec/fixtures/vcr_cassettes/phpbb2-should-not-login.yml +287 -0
  49. data/spec/fixtures/vcr_cassettes/phpbb2-should-not-post.yml +497 -0
  50. data/spec/fixtures/vcr_cassettes/phpbb2-should-overlimit-error-post.yml +1140 -0
  51. data/spec/fixtures/vcr_cassettes/phpbb2-should-post.yml +751 -0
  52. data/spec/fixtures/vcr_cassettes/phpbb3-should-be-overlimit-creating-topic.yml +995 -0
  53. data/spec/fixtures/vcr_cassettes/phpbb3-should-create-topic.yml +675 -0
  54. data/spec/fixtures/vcr_cassettes/phpbb3-should-login.yml +245 -0
  55. data/spec/fixtures/vcr_cassettes/phpbb3-should-not-create-new-topic.yml +350 -0
  56. data/spec/fixtures/vcr_cassettes/phpbb3-should-not-login.yml +253 -0
  57. data/spec/fixtures/vcr_cassettes/phpbb3-should-not-post.yml +350 -0
  58. data/spec/fixtures/vcr_cassettes/phpbb3-should-overlimit-error-post.yml +1046 -0
  59. data/spec/fixtures/vcr_cassettes/phpbb3-should-post.yml +605 -0
  60. data/{test → spec}/fixtures/wwf79-forum_posts.html +0 -0
  61. data/{test → spec}/fixtures/wwf79-general-new-topic-error.html +0 -0
  62. data/{test → spec}/fixtures/wwf79-general-posting-error.html +0 -0
  63. data/{test → spec}/fixtures/wwf79-good-post-forum_posts.html +1 -1
  64. data/{test → spec}/fixtures/wwf79-index.html +0 -0
  65. data/{test → spec}/fixtures/wwf79-logged-in.html +0 -0
  66. data/{test → spec}/fixtures/wwf79-login.html +0 -0
  67. data/{test → spec}/fixtures/wwf79-new-topic-forum_posts-response.html +0 -0
  68. data/{test → spec}/fixtures/wwf79-new-topic-post_message_form.html +0 -0
  69. data/{test → spec}/fixtures/wwf79-not-logged-in.html +0 -0
  70. data/{test → spec}/fixtures/wwf79-too-many-posts.html +0 -0
  71. data/{test → spec}/fixtures/wwf79-too-many-topics.html +0 -0
  72. data/{test → spec}/fixtures/wwf80-general-posting-error.html +0 -0
  73. data/{test → spec}/fixtures/wwf80-get-new_topic-form-good-response.html +0 -0
  74. data/{test → spec}/fixtures/wwf80-get-viewtopic-for-new-topic-good-response.html +0 -0
  75. data/{test → spec}/fixtures/wwf80-index.html +0 -0
  76. data/{test → spec}/fixtures/wwf80-logged-in.html +0 -0
  77. data/{test → spec}/fixtures/wwf80-login.html +0 -0
  78. data/{test → spec}/fixtures/wwf80-new_reply_form.html +0 -0
  79. data/{test → spec}/fixtures/wwf80-not-logged-in.html +0 -0
  80. data/{test → spec}/fixtures/wwf80-post-new_topic-good-response.html +1 -1
  81. data/{test → spec}/fixtures/wwf80-post-reply-good-response.html +0 -0
  82. data/{test → spec}/fixtures/wwf80-too-many-posts.html +0 -0
  83. data/spec/impostor_spec_helper.rb +162 -0
  84. data/spec/integration/phpbb2_spec.rb +111 -0
  85. data/spec/integration/phpbb3_spec.rb +109 -0
  86. data/spec/integration_spec_helper.rb +7 -0
  87. data/spec/phpbb2_spec.rb +346 -0
  88. data/spec/phpbb3_spec.rb +332 -0
  89. data/spec/post_spec.rb +134 -0
  90. data/spec/spec_helper.rb +2 -0
  91. data/spec/test_impostor.rb +12 -0
  92. data/spec/topic_spec.rb +143 -0
  93. data/spec/wwf79_spec.rb +342 -0
  94. data/spec/wwf80_spec.rb +339 -0
  95. metadata +156 -87
  96. data/lib/www/impostor/phpbb2.rb +0 -258
  97. data/lib/www/impostor/phpbb3.rb +0 -236
  98. data/lib/www/impostor/wwf79.rb +0 -254
  99. data/lib/www/impostor/wwf80.rb +0 -264
  100. data/lib/www/impostor.rb +0 -269
  101. data/test/test_github.rb +0 -12
  102. data/test/test_www_impostor.rb +0 -165
  103. data/test/test_www_impostor_phpbb2.rb +0 -536
  104. data/test/test_www_impostor_phpbb3.rb +0 -483
  105. data/test/test_www_impostor_wwf79.rb +0 -535
  106. data/test/test_www_impostor_wwf80.rb +0 -535
  107. data/vendor/plugins/impostor/lib/autotest/discover.rb +0 -3
  108. data/vendor/plugins/impostor/lib/autotest/impostor.rb +0 -49
@@ -0,0 +1,67 @@
1
+ class Impostor
2
+
3
+ ##
4
+ # An application error
5
+
6
+ class ImpostorError < RuntimeError
7
+
8
+ ##
9
+ # The original exception
10
+
11
+ attr_accessor :original_exception
12
+
13
+ ##
14
+ # Creates a new ImpostorError with +message+ and +original_exception+
15
+
16
+ def initialize(e = nil)
17
+ exception = e.nil? || e.is_a?(String) ? StandardError.new(e) : e
18
+ @original_exception = exception
19
+ message = "Impostor error: #{exception.message} (#{exception.class})"
20
+ super message
21
+ end
22
+
23
+ end
24
+
25
+ ##
26
+ # An error for impostor login failure
27
+
28
+ class LoginError < ImpostorError; end
29
+
30
+ ##
31
+ # An error for impostor post failure
32
+
33
+ class PostError < ImpostorError; end
34
+
35
+ ##
36
+ # An error for impostor when a topic failure
37
+
38
+ class TopicError < ImpostorError; end
39
+
40
+ ##
41
+ # An error for impostor when the receiving forum rejects the post due to
42
+ # a throttling or spam error but which the user can re-attempt at a later
43
+ # time.
44
+
45
+ class ThrottledError < ImpostorError; end
46
+
47
+ ##
48
+ # An error for misconfiguration
49
+
50
+ class ConfigError < ImpostorError; end
51
+
52
+ ##
53
+ # An error for template methods that need to be implemented
54
+
55
+ class MissingTemplateMethodError < ImpostorError; end
56
+
57
+ ##
58
+ # An exception for when a new topic has been moderated
59
+
60
+ class TopicModerated < ImpostorError; end
61
+
62
+ ##
63
+ # An exception for when a new post has been moderated
64
+
65
+ class PostModerated < ImpostorError; end
66
+
67
+ end
@@ -0,0 +1,202 @@
1
+ ##
2
+ # phpBB2 version of the Impostor
3
+ #
4
+
5
+ class Impostor
6
+
7
+ module Phpbb2
8
+
9
+ ##
10
+ # Additional configuration parameters for a Phpbb2 compatible agent:
11
+ #
12
+ # :posting_page
13
+ #
14
+ # Typical configuration parameters
15
+ # { :type => :phpbb2,
16
+ # :app_root => 'http://example.com/forum/',
17
+ # :login_page => 'login.php',
18
+ # :posting_page => 'posting.php',
19
+ # :user_agent => 'Windows IE 7',
20
+ # :username => 'myuser',
21
+ # :password => 'mypasswd' }
22
+
23
+ module Auth
24
+
25
+ ##
26
+ # Checks if the agent is already logged by stored cookie
27
+
28
+ def logged_in?(page)
29
+ mm = page.search( "//a" ).detect{ | a| a.inner_html =~ /Log out \[ #{self.config.username} \]/ } ||
30
+ page.search( "//a" ).detect{ |a| a['href'] =~ /\/login\.php\?mode=logout/ }
31
+
32
+ not mm.nil?
33
+ end
34
+
35
+ ##
36
+ # returns the login form from the login page
37
+
38
+ def get_login_form(page)
39
+ form = page.forms.detect { |form| form.action =~ /login\.php/ }
40
+ raise Impostor::LoginError.new("unknown login page format") unless form
41
+ form
42
+ end
43
+
44
+ ##
45
+ # Sets the user name and pass word on the loing form.
46
+
47
+ def set_username_and_password(form)
48
+ form['username'] = self.config.username
49
+ form['password'] = self.config.password
50
+ form['autologin'] = 'on'
51
+ form['login'] = 'Log in'
52
+ form
53
+ end
54
+
55
+ end
56
+
57
+ module Post
58
+
59
+ ##
60
+ # return a uri used to fetch the reply page based on the forum, topic, and
61
+ # message
62
+
63
+ def get_reply_uri(forum, topic)
64
+ uri = URI.join(self.config.app_root, self.config.config(:posting_page))
65
+ uri.query = "mode=reply&t=#{topic}"
66
+ uri
67
+ end
68
+
69
+ ##
70
+ # return the form used for posting a message from the reply page
71
+
72
+ def get_post_form(page)
73
+ form = page.forms.detect { |form| form.action =~ /#{Regexp.escape(self.config.config(:posting_page))}/ }
74
+ raise Impostor::PostError.new("unknown reply page format#{page_message(page, ', ')}") unless form
75
+ form
76
+ end
77
+
78
+ ##
79
+ # set the message to reply with on the reply form
80
+
81
+ def set_message(form, message)
82
+ form.message = message
83
+ form["post"] = "Submit"
84
+ form
85
+ end
86
+
87
+ ##
88
+ # get post id from the result of posting the message form
89
+
90
+ def get_post_from_result(page)
91
+ message = page_message(page)
92
+ if message =~ /Your message has been entered successfully./
93
+ kv = page.links.collect{ |l| l.uri }.compact.
94
+ collect{ |l| l.query }.compact.
95
+ collect{ |q| q.split('&')}.flatten.
96
+ detect{|kv| kv =~ /^p=/ }
97
+ postid = URI.unescape(kv).split('#').first.split('=').last.to_i
98
+ raise Impostor::PostError.new("Message did not post.") if postid.zero?
99
+ return postid
100
+ end
101
+
102
+ too_many = message =~ /You cannot make another post so soon after your last/
103
+
104
+ if too_many
105
+ raise Impostor::ThrottledError.new("too many posts in too short amount of time")
106
+ else
107
+ raise Impostor::PostError.new("message did not post")
108
+ end
109
+ end
110
+
111
+ def page_message(page, prepend = '')
112
+ message = page.search("//span[@class='gen']").last || ''
113
+ message = message.text if message.respond_to?(:text)
114
+ prepend = '' if message.empty?
115
+ "#{prepend}#{message}"
116
+ end
117
+
118
+ end
119
+
120
+ module Topic
121
+
122
+ ##
123
+ # return a uri used to fetch the new topic page based on the forum, subject,
124
+ # and message
125
+
126
+ def get_new_topic_uri(forum, subject, message)
127
+ uri = URI.join(self.config.app_root, self.config.config(:posting_page))
128
+ uri.query = "mode=newtopic&f=#{forum}"
129
+ uri
130
+ end
131
+
132
+ ##
133
+ # Get the the new topic form on the page
134
+
135
+ def get_new_topic_form(page)
136
+ form = page.form('post')
137
+ raise Impostor::TopicError.new("unknown new topic page format") unless form
138
+ form
139
+ end
140
+
141
+ ##
142
+ # Set the subject and message on the new topic form
143
+
144
+ def set_subject_and_message(form, subject, message)
145
+ form.subject = subject
146
+ form.message = message
147
+ form["post"] = "Submit"
148
+ form
149
+ end
150
+
151
+ ##
152
+ # Validate the result of posting the new topic
153
+
154
+ def validate_new_topic_result(page)
155
+ message = page_message(page)
156
+ if message !~ /Your message has been entered successfully./
157
+ if message =~ /You cannot make another post so soon after your last/
158
+ raise Impostor::ThrottledError.new("too many new topics in too short amount of time")
159
+ else
160
+ raise Impostor::TopicError.new("Topic did not post.")
161
+ end
162
+ end
163
+
164
+ begin
165
+ # <td align="center"><span class="gen">Your message has been entered successfully.<br /><br />Click <a href="viewtopic.php?p=9#9">Here</a> to view your message<br /><br />Click <a href="viewforum.php?f=1">Here</a> to return to the forum</span></td>
166
+
167
+ # TODO the link has the postid specifically for the post, not all
168
+ # forums make it easy to deduce the post id
169
+ link = page.links.detect{ |l| l.href =~ /viewtopic\.php/ }
170
+ link.click
171
+ rescue StandardError => err
172
+ raise Impostor::TopicError.new(err)
173
+ end
174
+ end
175
+
176
+ ##
177
+ # Get the new topic identifier from the result page
178
+
179
+ def get_topic_from_result(page)
180
+ begin
181
+ link = page.links.detect{ |l| l.href =~ /viewtopic\.php/ }
182
+ kv = link.uri.query.split('&').detect{|kv| kv =~ /^t=/ }
183
+ topicid = URI.unescape(kv).split('#').first.split('=').last.to_i
184
+ rescue StandardError => err
185
+ raise Impostor::TopicError.new(err)
186
+ end
187
+ raise Impostor::TopicError.new("Failed to create topic.") if topicid.zero?
188
+
189
+ topicid
190
+ end
191
+
192
+ def page_message(page, prepend = '')
193
+ message = page.search("//span[@class='gen']").last || ''
194
+ message = message.text if message.respond_to?(:text)
195
+ prepend = '' if message.empty?
196
+ "#{prepend}#{message}"
197
+ end
198
+
199
+ end
200
+
201
+ end
202
+ end
@@ -0,0 +1,199 @@
1
+ ##
2
+ # phpBB3 version of the Impostor
3
+ #
4
+
5
+ class Impostor
6
+
7
+ module Phpbb3
8
+
9
+ ##
10
+ # Additional configuration parameters for a Phpbb3 compatible agent:
11
+ #
12
+ # :posting_page
13
+ #
14
+ # Typical configuration parameters
15
+ # { :type => :phpbb3,
16
+ # :app_root => 'http://example.com/forum/',
17
+ # :login_page => 'ucp.php?mode=login',
18
+ # :posting_page => 'posting.php',
19
+ # :user_agent => 'Windows IE 7',
20
+ # :username => 'myuser',
21
+ # :password => 'mypasswd' }
22
+
23
+ module Auth
24
+
25
+ ##
26
+ # Checks if the agent is already logged by stored cookie
27
+
28
+ def logged_in?(page)
29
+ mm = page.search( "//a" ).detect{ | a| a.inner_html =~ /Logout \[ #{self.config.username} \]/ } ||
30
+ page.search( "//a" ).detect{ |a| a['href'] =~ /\.\/ucp\.php\?mode=logout/ }
31
+
32
+ not mm.nil?
33
+ end
34
+
35
+ ##
36
+ # returns the login form from the login page
37
+
38
+ def get_login_form(page)
39
+ form = page.forms.detect { |form| form.action =~ /\/ucp\.php\?mode=login/ }
40
+ raise Impostor::LoginError.new("unknown login page format") unless form
41
+ form
42
+ end
43
+
44
+ ##
45
+ # Sets the user name and pass word on the loing form.
46
+
47
+ def set_username_and_password(form)
48
+ form['username'] = self.config.username
49
+ form['password'] = self.config.password
50
+ form['login'] = 'Login'
51
+ form['autologin'] = 'on'
52
+ form
53
+ end
54
+
55
+ end
56
+
57
+ module Post
58
+
59
+ ##
60
+ # return a uri used to fetch the reply page based on the forum, topic, and
61
+ # message
62
+
63
+ def get_reply_uri(forum, topic)
64
+ uri = URI.join(self.config.app_root, self.config.config(:posting_page))
65
+ uri.query = "mode=reply&f=#{forum}&t=#{topic}"
66
+ uri
67
+ end
68
+
69
+ ##
70
+ # return the form used for posting a message from the reply page
71
+
72
+ def get_post_form(page)
73
+ form = page.forms.detect { |form| form.action =~ /#{Regexp.escape(self.config.config(:posting_page))}/ }
74
+ raise Impostor::PostError.new("unknown reply page format") unless form
75
+ form
76
+ end
77
+
78
+ ##
79
+ # set the message to reply with on the reply form
80
+
81
+ def set_message(form, message)
82
+ form.message = message
83
+ form["post"] = "Submit"
84
+ form
85
+ end
86
+
87
+ ##
88
+ # get post id from the result of posting the message form
89
+
90
+ def get_post_from_result(page)
91
+ error_message = page_error_message(page)
92
+ if error_message =~ /You cannot make another post so soon after your last/
93
+ raise Impostor::ThrottledError.new("too many posts in too short amount of time, #{error_message}")
94
+ elsif !error_message.empty?
95
+ raise Impostor::PostError.new(error_message)
96
+ end
97
+
98
+ begin
99
+ kv = page.links.collect{ |l| l.uri }.compact.
100
+ collect{ |l| l.query }.compact.
101
+ collect{ |q| q.split('&')}.flatten.
102
+ detect { |p| p =~ /^p=/ }
103
+ raise StandardError.new("Message did not post.") if kv.nil?
104
+ postid = URI.unescape(kv).split('#').first.split('=').last.to_i
105
+ raise StandardError.new("Message did not post.") if postid.zero?
106
+ postid
107
+ rescue StandardError => err
108
+ raise Impostor::PostError.new(err)
109
+ end
110
+ end
111
+
112
+ ##
113
+ # Extract the error from a page
114
+
115
+ def page_error_message(page, prepend='')
116
+ message = page.search(".//p[@class='error']").last || ''
117
+ message = message.text if message.respond_to?(:text)
118
+ prepend = '' if message.empty?
119
+ "#{prepend}#{message}"
120
+ end
121
+
122
+ end
123
+
124
+ module Topic
125
+
126
+ ##
127
+ # return a uri used to fetch the new topic page based on the forum, subject,
128
+ # and message
129
+
130
+ def get_new_topic_uri(forum, subject, message)
131
+ uri = URI.join(self.config.app_root, self.config.config(:posting_page))
132
+ uri.query = "mode=post&f=#{forum}"
133
+ uri
134
+ end
135
+
136
+ ##
137
+ # Get the the new topic form on the page
138
+
139
+ def get_new_topic_form(page)
140
+ form = page.forms.detect { |form| form.action =~ /#{Regexp.escape(self.config.config(:posting_page))}/ }
141
+ raise Impostor::TopicError.new("unknown new topic page format") unless form
142
+ form
143
+ end
144
+
145
+ ##
146
+ # Set the subject and message on the new topic form
147
+
148
+ def set_subject_and_message(form, subject, message)
149
+ form.subject = subject
150
+ form.message = message
151
+ form["post"] = "Submit"
152
+ form
153
+ end
154
+
155
+ ##
156
+ # Validate the result of posting the new topic
157
+
158
+ def validate_new_topic_result(page)
159
+ error_message = page_error_message(page)
160
+ if error_message =~ /You cannot make another post so soon after your last/
161
+ raise Impostor::ThrottledError.new("too many posts in too short amount of time, #{error_message}")
162
+ elsif !error_message.empty?
163
+ raise Impostor::PostError.new(error_message)
164
+ end
165
+
166
+ page
167
+ end
168
+
169
+ ##
170
+ # Get the new topic identifier from the result page
171
+
172
+ def get_topic_from_result(page)
173
+ moderated = page.body =~ /it will need to be approved by a moderator/i
174
+ raise Impostor::TopicModerated.new("new topic has been moderated") if moderated
175
+
176
+ link = page.links.detect{ |l| l.text =~ /View your submitted message/i }
177
+ link ||= page.links.detect{ |l| l.href =~ /viewtopic\.php/ }
178
+ raise Impostor::TopicError.new("new topic did not post") unless link
179
+ topic = link.uri.query.split('&').detect{|a| a =~ /^t=/}
180
+ raise Impostor::TopicError.new("new topic did not post") unless topic
181
+ topic = topic.split('=').last.to_i
182
+ raise Impostor::TopicError.new("new topic did not post") if topic.zero?
183
+ topic
184
+ end
185
+
186
+ ##
187
+ # Extract the error from a page
188
+
189
+ def page_error_message(page, prepend='')
190
+ message = page.search(".//p[@class='error']").last || ''
191
+ message = message.text if message.respond_to?(:text)
192
+ prepend = '' if message.empty?
193
+ "#{prepend}#{message}"
194
+ end
195
+
196
+ end
197
+
198
+ end
199
+ end
@@ -0,0 +1,111 @@
1
+ class Impostor::Post
2
+
3
+ attr_reader :config
4
+ attr_reader :auth
5
+
6
+ ##
7
+ # Post is initialized with the auth of the impostor
8
+
9
+ def initialize(config, auth)
10
+ @config = config
11
+ @auth = auth
12
+ self.extend eval("Impostor::#{config.type.to_s.capitalize}::Post")
13
+ end
14
+
15
+ ##
16
+ # post a message to the forum and topic thread
17
+ # post is comprised of the following template methods to allow
18
+ # implementation for specific forum applications
19
+ #
20
+ # * validate_post_input(forum, topic, message)
21
+ # * get_reply_uri(forum, topic)
22
+ # * get_reply_page(uri)
23
+ # * get_post_form(page)
24
+ # * set_message(form, message)
25
+ # * post_message(form)
26
+ # * get_post_from_result(page)
27
+ #
28
+ # A hash of results is returned, having keys to the :forum, :topic, new :post
29
+ # id, :message, and :result
30
+
31
+ def post(forum, topic, message)
32
+ self.validate_post_input(forum, topic, message)
33
+ self.auth.login_with_raises
34
+ uri = self.get_reply_uri(forum, topic)
35
+ page = get_reply_page(uri)
36
+ form = get_post_form(page)
37
+ set_message(form, message)
38
+ page = post_message(form)
39
+ post = get_post_from_result(page)
40
+
41
+ { :forum => forum,
42
+ :topic => topic,
43
+ :post => post,
44
+ :message => message,
45
+ :result => true }
46
+ end
47
+
48
+ ##
49
+ # validate the inputs forum, topic, and message
50
+
51
+ def validate_post_input(forum, topic, message)
52
+ raise Impostor::PostError.new("forum not set") unless forum
53
+ raise Impostor::PostError.new("topic not set") unless topic
54
+ raise Impostor::PostError.new("message not set") unless message
55
+ true
56
+ end
57
+
58
+ ##
59
+ # return a uri used to fetch the reply page based on the forum, topic, and
60
+ # message
61
+
62
+ def get_reply_uri(forum, topic)
63
+ raise Impostor::MissingTemplateMethodError.new("get_reply_uri must be implemented")
64
+ end
65
+
66
+ ##
67
+ # return the reply page that is fetched with the reply uri
68
+
69
+ def get_reply_page(uri)
70
+ begin
71
+ page = self.config.agent.get(uri)
72
+ rescue StandardError => err
73
+ raise Impostor::PostError.new(err)
74
+ end
75
+ end
76
+
77
+ ##
78
+ # return the form used for posting a message from the reply page
79
+
80
+ def get_post_form(page)
81
+ raise Impostor::MissingTemplateMethodError.new("get_post_form must be implemented")
82
+ end
83
+
84
+ ##
85
+ # set the message to reply with on the reply form
86
+
87
+ def set_message(form, message)
88
+ form.message = message
89
+ form
90
+ end
91
+
92
+ ##
93
+ # post the message form
94
+
95
+ def post_message(form)
96
+ begin
97
+ config.sleep_before_post
98
+ form.submit
99
+ rescue StandardError => err
100
+ raise Impostor::PostError.new(err)
101
+ end
102
+ end
103
+
104
+ ##
105
+ # get post id from the result of posting the message form
106
+
107
+ def get_post_from_result(page)
108
+ raise Impostor::MissingTemplateMethodError.new("get_post_from_result must be implemented")
109
+ end
110
+
111
+ end
@@ -0,0 +1,115 @@
1
+ class Impostor::Topic
2
+
3
+ attr_reader :config
4
+ attr_reader :auth
5
+
6
+ ##
7
+ # Topic is initialized with the auth of the impostor
8
+
9
+ def initialize(config, auth)
10
+ @config = config
11
+ @auth = auth
12
+ self.extend eval("Impostor::#{config.type.to_s.capitalize}::Topic")
13
+ end
14
+
15
+ ##
16
+ # create a new topic in the forum with the subject title and initial message
17
+ #
18
+ # * validate_new_topic_input(forum, subject, message)
19
+ # * get_new_topic_uri(forum, subject, message)
20
+ # * get_new_topic_page(uri)
21
+ # * get_new_topic_form(page)
22
+ # * set_subject_and_message(form, subject, message)
23
+ # * post_new_topic(form)
24
+ # * validate_new_topic_result(page)
25
+ # * get_topic_from_result(page)
26
+
27
+ def new_topic(forum, subject, message)
28
+ self.validate_topic_input(forum, subject, message)
29
+ self.auth.login_with_raises
30
+ uri = self.get_new_topic_uri(forum, subject, message)
31
+ page = self.get_new_topic_page(uri)
32
+ form = self.get_new_topic_form(page)
33
+ self.set_subject_and_message(form, subject, message)
34
+ page = self.post_new_topic(form)
35
+ page = self.validate_new_topic_result(page)
36
+ topic = self.get_topic_from_result(page)
37
+
38
+ self.config.add_subject(forum, topic, subject)
39
+
40
+ { :forum => forum,
41
+ :topic => topic,
42
+ :subject => subject,
43
+ :message => message,
44
+ :result => true }
45
+ end
46
+
47
+ ##
48
+ # validate the inputs forum, topic, and message
49
+
50
+ def validate_topic_input(forum, subject, message)
51
+ raise Impostor::TopicError.new("forum not set") unless forum
52
+ raise Impostor::TopicError.new("subject not set") unless subject
53
+ raise Impostor::TopicError.new("message not set") unless message
54
+ true
55
+ end
56
+
57
+ ##
58
+ # return a uri used to fetch the new topic page based on the forum, subject,
59
+ # and message
60
+
61
+ def get_new_topic_uri(forum, subject, message)
62
+ raise Impostor::MissingTemplateMethodError.new("get_new_topic_uri must be implemented")
63
+ end
64
+
65
+ ##
66
+ # Get the page that has the form for new topics referenced by the uri
67
+
68
+ def get_new_topic_page(uri)
69
+ begin
70
+ self.config.agent.get(uri)
71
+ rescue StandardError => err
72
+ raise Impostor::TopicError.new(err)
73
+ end
74
+ end
75
+
76
+ ##
77
+ # Get the the new topic form on the page
78
+
79
+ def get_new_topic_form(page)
80
+ raise Impostor::MissingTemplateMethodError.new("get_new_topic_form must be implemented")
81
+ end
82
+
83
+ ##
84
+ # Set the subject and message on the new topic form
85
+
86
+ def set_subject_and_message(form, subject, message)
87
+ raise Impostor::MissingTemplateMethodError.new("set_subject_and_message must be implemented")
88
+ end
89
+
90
+ ##
91
+ # Post the new topic that is contained on the form
92
+
93
+ def post_new_topic(form)
94
+ begin
95
+ config.sleep_before_post
96
+ form.submit
97
+ rescue StandardError => err
98
+ raise Impostor::TopicError.new(err)
99
+ end
100
+ end
101
+
102
+ ##
103
+ # Validate the result of posting the new topic
104
+
105
+ def validate_new_topic_result(page)
106
+ raise Impostor::MissingTemplateMethodError.new("validate_new_topic_result must be implemented")
107
+ end
108
+
109
+ ##
110
+ # Get the new topic identifier from the result page
111
+
112
+ def get_topic_from_result(page)
113
+ raise Impostor::MissingTemplateMethodError.new("get_topic_from_result must be implemented")
114
+ end
115
+ end