qwikdoc 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.
Files changed (335) hide show
  1. data/.gitignore +5 -0
  2. data/.rspec +2 -0
  3. data/CHANGES +3 -0
  4. data/COPYING +340 -0
  5. data/Gemfile +3 -0
  6. data/README.md +10 -0
  7. data/Rakefile +10 -0
  8. data/TextFormattingRules +137 -0
  9. data/TextFormattingRules.ja +143 -0
  10. data/bin/qwikdoc +34 -0
  11. data/lib/qwik_doc.rb +15 -0
  12. data/lib/qwik_doc/hack.rb +184 -0
  13. data/qwikdoc.gemspec +26 -0
  14. data/spec/qwik_doc_spec.rb +305 -0
  15. data/spec/spec_helper.rb +12 -0
  16. data/vendor/qwik/Makefile +63 -0
  17. data/vendor/qwik/Rakefile +17 -0
  18. data/vendor/qwik/act-aa.rb +83 -0
  19. data/vendor/qwik/act-archive.rb +386 -0
  20. data/vendor/qwik/act-attach.rb +304 -0
  21. data/vendor/qwik/act-backup.rb +236 -0
  22. data/vendor/qwik/act-basic.rb +325 -0
  23. data/vendor/qwik/act-book.rb +161 -0
  24. data/vendor/qwik/act-calc.rb +371 -0
  25. data/vendor/qwik/act-charset.rb +251 -0
  26. data/vendor/qwik/act-christel.rb +112 -0
  27. data/vendor/qwik/act-chronology.rb +149 -0
  28. data/vendor/qwik/act-code.rb +155 -0
  29. data/vendor/qwik/act-comment.rb +554 -0
  30. data/vendor/qwik/act-config.rb +49 -0
  31. data/vendor/qwik/act-counter.rb +125 -0
  32. data/vendor/qwik/act-day.rb +96 -0
  33. data/vendor/qwik/act-describe.rb +135 -0
  34. data/vendor/qwik/act-diary.rb +109 -0
  35. data/vendor/qwik/act-edit.rb +317 -0
  36. data/vendor/qwik/act-embed-html.rb +296 -0
  37. data/vendor/qwik/act-emode.rb +150 -0
  38. data/vendor/qwik/act-event.rb +187 -0
  39. data/vendor/qwik/act-file.rb +303 -0
  40. data/vendor/qwik/act-files.rb +771 -0
  41. data/vendor/qwik/act-getpass.rb +295 -0
  42. data/vendor/qwik/act-history.rb +136 -0
  43. data/vendor/qwik/act-html.rb +204 -0
  44. data/vendor/qwik/act-httpauth.rb +147 -0
  45. data/vendor/qwik/act-include.rb +142 -0
  46. data/vendor/qwik/act-interwiki.rb +189 -0
  47. data/vendor/qwik/act-isearch.rb +109 -0
  48. data/vendor/qwik/act-keywords.rb +137 -0
  49. data/vendor/qwik/act-lang.rb +81 -0
  50. data/vendor/qwik/act-license.rb +63 -0
  51. data/vendor/qwik/act-list.rb +287 -0
  52. data/vendor/qwik/act-login.rb +410 -0
  53. data/vendor/qwik/act-logout.rb +60 -0
  54. data/vendor/qwik/act-mail.rb +159 -0
  55. data/vendor/qwik/act-map.rb +292 -0
  56. data/vendor/qwik/act-md5.rb +42 -0
  57. data/vendor/qwik/act-mdlb.rb +741 -0
  58. data/vendor/qwik/act-media.rb +416 -0
  59. data/vendor/qwik/act-member.rb +157 -0
  60. data/vendor/qwik/act-menu.rb +142 -0
  61. data/vendor/qwik/act-metadata.rb +323 -0
  62. data/vendor/qwik/act-mlsubmitform.rb +49 -0
  63. data/vendor/qwik/act-monitor.rb +167 -0
  64. data/vendor/qwik/act-new.rb +418 -0
  65. data/vendor/qwik/act-pagelist.rb +248 -0
  66. data/vendor/qwik/act-password.rb +145 -0
  67. data/vendor/qwik/act-plan.rb +210 -0
  68. data/vendor/qwik/act-povray.rb +230 -0
  69. data/vendor/qwik/act-presen.rb +380 -0
  70. data/vendor/qwik/act-redirect.rb +133 -0
  71. data/vendor/qwik/act-ring.rb +1071 -0
  72. data/vendor/qwik/act-rrefs.rb +255 -0
  73. data/vendor/qwik/act-ruby.rb +116 -0
  74. data/vendor/qwik/act-sample.rb +170 -0
  75. data/vendor/qwik/act-save.rb +295 -0
  76. data/vendor/qwik/act-schedule.rb +374 -0
  77. data/vendor/qwik/act-search.rb +180 -0
  78. data/vendor/qwik/act-searchwords.rb +51 -0
  79. data/vendor/qwik/act-sendpass.rb +83 -0
  80. data/vendor/qwik/act-site.rb +89 -0
  81. data/vendor/qwik/act-sitebackup.rb +419 -0
  82. data/vendor/qwik/act-sitelog.rb +118 -0
  83. data/vendor/qwik/act-smil.rb +299 -0
  84. data/vendor/qwik/act-status.rb +220 -0
  85. data/vendor/qwik/act-style.rb +608 -0
  86. data/vendor/qwik/act-table.rb +263 -0
  87. data/vendor/qwik/act-tableform.rb +241 -0
  88. data/vendor/qwik/act-tag.rb +103 -0
  89. data/vendor/qwik/act-text.rb +70 -0
  90. data/vendor/qwik/act-textarea.rb +164 -0
  91. data/vendor/qwik/act-theme.rb +346 -0
  92. data/vendor/qwik/act-toc.rb +112 -0
  93. data/vendor/qwik/act-typekey.rb +198 -0
  94. data/vendor/qwik/act-under-const.rb +94 -0
  95. data/vendor/qwik/act-webservice.rb +321 -0
  96. data/vendor/qwik/act-wema.rb +558 -0
  97. data/vendor/qwik/act-wysiwyg.rb +238 -0
  98. data/vendor/qwik/action.rb +325 -0
  99. data/vendor/qwik/autoreload.rb +98 -0
  100. data/vendor/qwik/bench-bench.rb +28 -0
  101. data/vendor/qwik/bench-emode.rb +47 -0
  102. data/vendor/qwik/bench-format-xml.rb +70 -0
  103. data/vendor/qwik/bench-large-table.rb +62 -0
  104. data/vendor/qwik/bench-md5.rb +14 -0
  105. data/vendor/qwik/bench-module-session.rb +19 -0
  106. data/vendor/qwik/bench-page.rb +50 -0
  107. data/vendor/qwik/bench-s-md5.rb +28 -0
  108. data/vendor/qwik/bench-session.rb +25 -0
  109. data/vendor/qwik/bench-textformat.rb +31 -0
  110. data/vendor/qwik/catalog-factory.rb +91 -0
  111. data/vendor/qwik/catalog-ja.rb +479 -0
  112. data/vendor/qwik/catalog-ml-ja.rb +145 -0
  113. data/vendor/qwik/check-act-monitor.rb +112 -0
  114. data/vendor/qwik/check-act-monitor2.rb +46 -0
  115. data/vendor/qwik/check-act-theme.rb +27 -0
  116. data/vendor/qwik/check-event.rb +112 -0
  117. data/vendor/qwik/check-event2.rb +55 -0
  118. data/vendor/qwik/check-htree-rexml.rb +77 -0
  119. data/vendor/qwik/check-htree.rb +51 -0
  120. data/vendor/qwik/check-libxml.rb +135 -0
  121. data/vendor/qwik/check-many-pages.rb +47 -0
  122. data/vendor/qwik/check-monitor.rb +33 -0
  123. data/vendor/qwik/check-observer.rb +67 -0
  124. data/vendor/qwik/check-server.rb +61 -0
  125. data/vendor/qwik/check-uuencode.rb +33 -0
  126. data/vendor/qwik/check-webdav.rb +65 -0
  127. data/vendor/qwik/check-xml-xpath.rb +27 -0
  128. data/vendor/qwik/common-backtrace.rb +94 -0
  129. data/vendor/qwik/common-basic.rb +111 -0
  130. data/vendor/qwik/common-condition.rb +142 -0
  131. data/vendor/qwik/common-error.rb +48 -0
  132. data/vendor/qwik/common-gettext.rb +50 -0
  133. data/vendor/qwik/common-javascript.rb +45 -0
  134. data/vendor/qwik/common-notice.rb +132 -0
  135. data/vendor/qwik/common-plain.rb +105 -0
  136. data/vendor/qwik/common-plugin.rb +111 -0
  137. data/vendor/qwik/common-res.rb +76 -0
  138. data/vendor/qwik/common-resolve.rb +92 -0
  139. data/vendor/qwik/common-send.rb +171 -0
  140. data/vendor/qwik/common-session.rb +139 -0
  141. data/vendor/qwik/common-surface.rb +261 -0
  142. data/vendor/qwik/common-url.rb +90 -0
  143. data/vendor/qwik/compat/Makefile +12 -0
  144. data/vendor/qwik/compat/add_load_path.rb +1 -0
  145. data/vendor/qwik/config.rb +299 -0
  146. data/vendor/qwik/db-b-backup.rb +142 -0
  147. data/vendor/qwik/db-backup.rb +184 -0
  148. data/vendor/qwik/db-berkeley.rb +306 -0
  149. data/vendor/qwik/db-filesystem.rb +305 -0
  150. data/vendor/qwik/db-indexer.rb +154 -0
  151. data/vendor/qwik/db-searchwords.rb +238 -0
  152. data/vendor/qwik/description-ja.rb +267 -0
  153. data/vendor/qwik/description.rb +256 -0
  154. data/vendor/qwik/dev-add-copyright.rb +23 -0
  155. data/vendor/qwik/dev-backup.rb +24 -0
  156. data/vendor/qwik/dev-bench.rb +175 -0
  157. data/vendor/qwik/dev-calctime.rb +52 -0
  158. data/vendor/qwik/dev-css-compressor.rb +60 -0
  159. data/vendor/qwik/dev-extract-catalog.rb +102 -0
  160. data/vendor/qwik/dev-js-compressor.rb +73 -0
  161. data/vendor/qwik/dev-release.rb +235 -0
  162. data/vendor/qwik/dev-replace.rb +353 -0
  163. data/vendor/qwik/dev-sendmail.rb +25 -0
  164. data/vendor/qwik/farm.rb +283 -0
  165. data/vendor/qwik/gettext.rb +56 -0
  166. data/vendor/qwik/group-cond.rb +157 -0
  167. data/vendor/qwik/group-config.rb +247 -0
  168. data/vendor/qwik/group-confirm.rb +147 -0
  169. data/vendor/qwik/group-db.rb +326 -0
  170. data/vendor/qwik/group-mail.rb +195 -0
  171. data/vendor/qwik/group-member.rb +307 -0
  172. data/vendor/qwik/group-sendmail.rb +231 -0
  173. data/vendor/qwik/group-site.rb +374 -0
  174. data/vendor/qwik/group-sweep.rb +63 -0
  175. data/vendor/qwik/group.rb +157 -0
  176. data/vendor/qwik/helper.rb +6 -0
  177. data/vendor/qwik/html-generator.rb +104 -0
  178. data/vendor/qwik/html-to-wabisabi.rb +149 -0
  179. data/vendor/qwik/htree-diff.rb +87 -0
  180. data/vendor/qwik/htree-format-xml.rb +57 -0
  181. data/vendor/qwik/htree-generator.rb +144 -0
  182. data/vendor/qwik/htree-get.rb +251 -0
  183. data/vendor/qwik/htree-html-generator.rb +179 -0
  184. data/vendor/qwik/htree-template.rb +263 -0
  185. data/vendor/qwik/htree-to-wabisabi.rb +171 -0
  186. data/vendor/qwik/loadlib.rb +79 -0
  187. data/vendor/qwik/logger.rb +115 -0
  188. data/vendor/qwik/mail-body.rb +171 -0
  189. data/vendor/qwik/mail-header.rb +474 -0
  190. data/vendor/qwik/mail-multipart.rb +193 -0
  191. data/vendor/qwik/mail-parse.rb +147 -0
  192. data/vendor/qwik/mail.rb +341 -0
  193. data/vendor/qwik/mailaddress.rb +83 -0
  194. data/vendor/qwik/mdlb-model.rb +81 -0
  195. data/vendor/qwik/mdlb-sample.rb +324 -0
  196. data/vendor/qwik/ml-catalog-factory.rb +27 -0
  197. data/vendor/qwik/ml-exception.rb +15 -0
  198. data/vendor/qwik/ml-generator.rb +28 -0
  199. data/vendor/qwik/ml-gettext.rb +137 -0
  200. data/vendor/qwik/ml-logger.rb +82 -0
  201. data/vendor/qwik/ml-memory.rb +65 -0
  202. data/vendor/qwik/ml-msg-validator.rb +86 -0
  203. data/vendor/qwik/ml-processor.rb +609 -0
  204. data/vendor/qwik/ml-quickml-server.rb +122 -0
  205. data/vendor/qwik/ml-sendmail.rb +0 -0
  206. data/vendor/qwik/ml-server.rb +107 -0
  207. data/vendor/qwik/ml-session.rb +348 -0
  208. data/vendor/qwik/ml-sweeper.rb +101 -0
  209. data/vendor/qwik/mock-logger.rb +65 -0
  210. data/vendor/qwik/mock-sendmail.rb +78 -0
  211. data/vendor/qwik/mock-socket.rb +82 -0
  212. data/vendor/qwik/modulobe.rb +406 -0
  213. data/vendor/qwik/mongrel-server.rb +72 -0
  214. data/vendor/qwik/page-files.rb +249 -0
  215. data/vendor/qwik/page-generate.rb +82 -0
  216. data/vendor/qwik/page-get.rb +262 -0
  217. data/vendor/qwik/page-images.rb +168 -0
  218. data/vendor/qwik/page-put.rb +128 -0
  219. data/vendor/qwik/page-rrefs.rb +154 -0
  220. data/vendor/qwik/page-title.rb +0 -0
  221. data/vendor/qwik/page-wikidb.rb +234 -0
  222. data/vendor/qwik/page.rb +67 -0
  223. data/vendor/qwik/pages.rb +444 -0
  224. data/vendor/qwik/parse-plugin.rb +249 -0
  225. data/vendor/qwik/parser-emode.rb +116 -0
  226. data/vendor/qwik/parser-inline.rb +273 -0
  227. data/vendor/qwik/parser.rb +350 -0
  228. data/vendor/qwik/password.rb +183 -0
  229. data/vendor/qwik/periodic.rb +79 -0
  230. data/vendor/qwik/qp.rb +56 -0
  231. data/vendor/qwik/qwik-service.rb +341 -0
  232. data/vendor/qwik/qwik.rb +5 -0
  233. data/vendor/qwik/qwikweb-adduser.rb +34 -0
  234. data/vendor/qwik/qwikweb-catalog-validator.rb +110 -0
  235. data/vendor/qwik/qwikweb-incgen.rb +28 -0
  236. data/vendor/qwik/qwikweb-makesite.rb +41 -0
  237. data/vendor/qwik/qwikweb-server.rb +44 -0
  238. data/vendor/qwik/qwikweb-showpassword.rb +26 -0
  239. data/vendor/qwik/qwikweb-watchlog.rb +67 -0
  240. data/vendor/qwik/request-path.rb +304 -0
  241. data/vendor/qwik/request-ua.rb +76 -0
  242. data/vendor/qwik/request-webrick.rb +81 -0
  243. data/vendor/qwik/request.rb +91 -0
  244. data/vendor/qwik/resolve-tdiary.rb +292 -0
  245. data/vendor/qwik/resolve.rb +121 -0
  246. data/vendor/qwik/response.rb +228 -0
  247. data/vendor/qwik/server-memory.rb +57 -0
  248. data/vendor/qwik/server-webrick.rb +102 -0
  249. data/vendor/qwik/server.rb +252 -0
  250. data/vendor/qwik/site-config.rb +149 -0
  251. data/vendor/qwik/site-db.rb +136 -0
  252. data/vendor/qwik/site-event.rb +162 -0
  253. data/vendor/qwik/site-files.rb +86 -0
  254. data/vendor/qwik/site-group.rb +103 -0
  255. data/vendor/qwik/site-index.rb +47 -0
  256. data/vendor/qwik/site-log.rb +64 -0
  257. data/vendor/qwik/site-member.rb +230 -0
  258. data/vendor/qwik/site-monitor.rb +97 -0
  259. data/vendor/qwik/site-pages.rb +62 -0
  260. data/vendor/qwik/site-plan.rb +139 -0
  261. data/vendor/qwik/site-report.rb +282 -0
  262. data/vendor/qwik/site-resolve.rb +230 -0
  263. data/vendor/qwik/site-rrefs.rb +18 -0
  264. data/vendor/qwik/site-search.rb +180 -0
  265. data/vendor/qwik/site-timeline.rb +153 -0
  266. data/vendor/qwik/site-url.rb +189 -0
  267. data/vendor/qwik/site.rb +76 -0
  268. data/vendor/qwik/smil-time.rb +105 -0
  269. data/vendor/qwik/smtp-session.rb +331 -0
  270. data/vendor/qwik/template-generator.rb +149 -0
  271. data/vendor/qwik/template.rb +85 -0
  272. data/vendor/qwik/test-common.rb +10 -0
  273. data/vendor/qwik/test-ml-info.rb +225 -0
  274. data/vendor/qwik/test-module-ml.rb +142 -0
  275. data/vendor/qwik/test-module-path.rb +110 -0
  276. data/vendor/qwik/test-module-public.rb +70 -0
  277. data/vendor/qwik/test-module-server.rb +98 -0
  278. data/vendor/qwik/test-module-session.rb +264 -0
  279. data/vendor/qwik/test-module-suite.rb +91 -0
  280. data/vendor/qwik/test-ms-applemail.rb +59 -0
  281. data/vendor/qwik/test-ms-attach.rb +174 -0
  282. data/vendor/qwik/test-ms-basic.rb +40 -0
  283. data/vendor/qwik/test-ms-close.rb +35 -0
  284. data/vendor/qwik/test-ms-confirm.rb +75 -0
  285. data/vendor/qwik/test-ms-ignore-spam.rb +57 -0
  286. data/vendor/qwik/test-ms-japanese.rb +97 -0
  287. data/vendor/qwik/test-ms-jfilename.rb +82 -0
  288. data/vendor/qwik/test-ms-large.rb +58 -0
  289. data/vendor/qwik/test-ms-member.rb +103 -0
  290. data/vendor/qwik/test-ms-multipart.rb +84 -0
  291. data/vendor/qwik/test-ms-org.rb +129 -0
  292. data/vendor/qwik/test-ms-plan.rb +130 -0
  293. data/vendor/qwik/test-ms-post.rb +53 -0
  294. data/vendor/qwik/test-ms-quopri.rb +76 -0
  295. data/vendor/qwik/test-sub-quopri.rb +45 -0
  296. data/vendor/qwik/test-submit-base64.rb +59 -0
  297. data/vendor/qwik/test-submit-forward.rb +61 -0
  298. data/vendor/qwik/test-submit-html-mail.rb +52 -0
  299. data/vendor/qwik/test-submit-japanese.rb +135 -0
  300. data/vendor/qwik/test-submit-jfilename.rb +144 -0
  301. data/vendor/qwik/test-submit-uuencode.rb +71 -0
  302. data/vendor/qwik/test-suite-all.rb +8 -0
  303. data/vendor/qwik/test-suite-basic.rb +9 -0
  304. data/vendor/qwik/test-suite-ml.rb +9 -0
  305. data/vendor/qwik/test-typekey.rb +64 -0
  306. data/vendor/qwik/testunit.rb +45 -0
  307. data/vendor/qwik/tokenizer-inline.rb +294 -0
  308. data/vendor/qwik/tokenizer.rb +332 -0
  309. data/vendor/qwik/typekey.rb +160 -0
  310. data/vendor/qwik/util-basic.rb +135 -0
  311. data/vendor/qwik/util-charset.rb +332 -0
  312. data/vendor/qwik/util-css.rb +56 -0
  313. data/vendor/qwik/util-csv-tokenizer.rb +113 -0
  314. data/vendor/qwik/util-filename.rb +112 -0
  315. data/vendor/qwik/util-pathname.rb +202 -0
  316. data/vendor/qwik/util-pid.rb +58 -0
  317. data/vendor/qwik/util-safe.rb +67 -0
  318. data/vendor/qwik/util-sendmail.rb +273 -0
  319. data/vendor/qwik/util-string.rb +203 -0
  320. data/vendor/qwik/util-time.rb +125 -0
  321. data/vendor/qwik/util-webrick.rb +176 -0
  322. data/vendor/qwik/version.rb +11 -0
  323. data/vendor/qwik/wabisabi-basic.rb +163 -0
  324. data/vendor/qwik/wabisabi-diff.rb +130 -0
  325. data/vendor/qwik/wabisabi-format-xml.rb +310 -0
  326. data/vendor/qwik/wabisabi-generator.rb +262 -0
  327. data/vendor/qwik/wabisabi-index.rb +174 -0
  328. data/vendor/qwik/wabisabi-table.rb +212 -0
  329. data/vendor/qwik/wabisabi-template.rb +295 -0
  330. data/vendor/qwik/wabisabi-to-htree.rb +111 -0
  331. data/vendor/qwik/wabisabi-to-wiki.rb +690 -0
  332. data/vendor/qwik/wabisabi-traverse.rb +207 -0
  333. data/vendor/qwik/wabisabi-validator.rb +116 -0
  334. data/vendor/qwik/wabisabi.rb +5 -0
  335. metadata +416 -0
@@ -0,0 +1,609 @@
1
+ # -*- coding: shift_jis -*-
2
+ #
3
+ # Copyright (C) 2002-2004 Satoru Takabayashi <satoru@namazu.org>
4
+ # Copyright (C) 2003-2006 Kouichirou Eto
5
+ # All rights reserved.
6
+ # This is free software with ABSOLUTELY NO WARRANTY.
7
+ #
8
+ # You can redistribute it and/or modify it under the terms of
9
+ # the GNU General Public License version 2.
10
+ #
11
+
12
+ $LOAD_PATH.unshift '..' unless $LOAD_PATH.include? '..'
13
+ require 'qwik/ml-gettext'
14
+ require 'qwik/ml-exception'
15
+ require 'qwik/group'
16
+ require 'qwik/mail'
17
+ require 'qwik/util-charset'
18
+
19
+ module QuickML
20
+ class Processor
21
+ include GetText
22
+
23
+ def initialize (config, mail)
24
+ @config = config
25
+ @mail = mail
26
+ @logger = @config.logger
27
+ @catalog = @config.catalog
28
+ if mail.multipart?
29
+ sub_mail = Mail.new
30
+ sub_mail.read(mail.parts.first)
31
+ @message_charset = sub_mail.charset
32
+ else
33
+ @message_charset = mail.charset
34
+ end
35
+
36
+ # FIXME: too ad-hoc
37
+ @rejection_ignore_list = %w(info ajax study test)
38
+ if $test
39
+ @rejection_ignore_list = []
40
+ if $test_rejection_ignore_list
41
+ @rejection_ignore_list = $test_rejection_ignore_list
42
+ end
43
+ end
44
+ end
45
+
46
+ def process
47
+ mail_log
48
+ if @mail.looping?
49
+ @logger.log "Looping Mail: from #{@mail.from}"
50
+ return
51
+ end
52
+
53
+ @mail.recipients.each {|recipient|
54
+ process_recipient(recipient)
55
+ }
56
+ end
57
+
58
+ private
59
+
60
+ def mail_log
61
+ @logger.vlog "MAIL FROM:<#{@mail.mail_from}>"
62
+ @mail.recipients.each {|recipient|
63
+ @logger.vlog "RCPT TO:<#{recipient}>"
64
+ }
65
+ @logger.vlog 'From: ' + @mail.from
66
+ @logger.vlog 'Cc: ' + @mail.collect_cc.join(', ')
67
+ @logger.vlog 'bare From: ' + @mail['From']
68
+ @logger.vlog 'bare Cc: ' + @mail['Cc']
69
+ end
70
+
71
+ def process_recipient (recipient)
72
+ mladdress = recipient
73
+
74
+ # Error mail handling.
75
+ if to_return_address?(mladdress)
76
+ handler = ErrorMailHandler.new(@config, @message_charset)
77
+ handler.handle(@mail)
78
+ return
79
+ end
80
+
81
+ # Confirm to create a new mailing list.
82
+ if @config.confirm_ml_creation && to_confirmation_address?(mladdress)
83
+ validate_confirmation(mladdress)
84
+ return
85
+ end
86
+
87
+ begin
88
+ ServerMemory.ml_mutex(@config, mladdress).synchronize {
89
+ ml = Group.new(@config, mladdress, @mail.from, @message_charset)
90
+ @message_charset ||= ml.charset
91
+
92
+ #qp @mail.body
93
+ if Processor.unsubscribe_requested?(@mail.body)
94
+ unsubscribe(ml)
95
+ return
96
+ end
97
+
98
+ submit(ml)
99
+ }
100
+
101
+ rescue InvalidMLName
102
+ report_invalid_mladdress(mladdress)
103
+ end
104
+ end
105
+
106
+ def to_return_address? (recipient)
107
+ # "return=" for XVERP, 'return@' for without XVERP.
108
+ return /^[^=]*=return[=@]/ =~ recipient
109
+ end
110
+
111
+ def to_confirmation_address? (address)
112
+ return /\Aconfirm\+/.match(address)
113
+ end
114
+
115
+ def validate_confirmation (confirmation_address)
116
+ m = /\Aconfirm\+(\d+)\+(.*)/.match(confirmation_address)
117
+ return if m.nil?
118
+ time = m[1]
119
+ mladdress = m[2]
120
+ ml = Group.new(@config, mladdress)
121
+ if ml.validate_confirmation(time)
122
+ ml.accept_confirmation
123
+ end
124
+ end
125
+
126
+ UNSUBSCRIBE_THRESHOLD = 500
127
+ UNSUBSCRIBE_RE = /\A\s*(unsubscribe|bye|#\s*bye|quit|�މ�|�E��)\s*$/s
128
+ def self.unsubscribe_requested?(body)
129
+ return true if body.empty?
130
+ return true if Mail.empty_body?(body)
131
+ return false if UNSUBSCRIBE_THRESHOLD <= body.length
132
+ return true if UNSUBSCRIBE_RE.match(body.tosjis)
133
+ return false
134
+ end
135
+
136
+ def submit (ml)
137
+ if Group.exclude?(@mail.from, @config.ml_domain)
138
+ @logger.log "Invalid From Address: #{@mail.from}"
139
+ return
140
+ end
141
+
142
+ if ml.forward?
143
+ @logger.log "Forward Address: #{ml.address}"
144
+ ml.submit(@mail)
145
+ return
146
+ end
147
+
148
+ if confirmation_required?(ml)
149
+ ml.prepare_confirmation(@mail)
150
+ return
151
+ end
152
+
153
+ if acceptable_submission?(ml)
154
+ submit_article(ml)
155
+ return
156
+ end
157
+
158
+ report_rejection(ml)
159
+ end
160
+
161
+ def report_invalid_mladdress (mladdress)
162
+ mail = {
163
+ :mail_from => '',
164
+ :recipient => @mail.from,
165
+ :header => [
166
+ ['To', @mail.from],
167
+ ['From', @config.ml_postmaster],
168
+ ['Subject',
169
+ Mail.encode_field(_("[QuickML] Error: %s", @mail['Subject']))],
170
+ ['Content-Type', content_type]
171
+ ],
172
+ :body => _("Invalid mailing list name: <%s>\n", mladdress) +
173
+ _("You can only use 0-9, a-z, A-Z, `-' for mailing list name\n") +
174
+ generate_footer,
175
+ }
176
+ Sendmail.send_mail(@config.smtp_host, @config.smtp_port, @logger, mail)
177
+ @logger.log "Invalid ML Address: #{mladdress}"
178
+ end
179
+
180
+ def content_type
181
+ return Mail.content_type(@config.content_type, @message_charset)
182
+ end
183
+
184
+ def generate_footer
185
+ return "\n-- \n" + _("Info: %s\n", @config.public_url)
186
+ end
187
+
188
+ def report_rejection (ml)
189
+ # FIXME: too ad-hoc
190
+ if @rejection_ignore_list.include?(ml.name)
191
+ @logger.log "[#{ml.name}]: Reject quietly: #{@mail.from}"
192
+
193
+ # do nothing
194
+ return
195
+ end
196
+
197
+ header = []
198
+ subject = Mail.encode_field(_("[QuickML] Error: %s", @mail['Subject']))
199
+ header.push(['To', @mail.from],
200
+ ['From', ml.address],
201
+ ['Subject', subject])
202
+
203
+ body = _("You are not a member of the mailing list:\n<%s>\n",
204
+ ml.address)
205
+ body << "\n"
206
+ body << _("Did you send a mail with a different address from the address registered in the mailing list?\n")
207
+ body << _("Please check your 'From:' address.\n")
208
+ body << generate_footer
209
+ body << "\n"
210
+
211
+ body << _("----- Original Message -----\n")
212
+ orig_subject = codeconv(Mail.decode_subject(@mail['Subject']))
213
+ body << "Subject: #{orig_subject}\n"
214
+ body << "To: #{@mail['To']}\n"
215
+ body << "From: #{@mail['From']}\n"
216
+ body << "Date: #{@mail['Date']}\n"
217
+ body << "\n"
218
+
219
+ =begin
220
+ if @mail.multipart?
221
+ ['Content-Type', 'Mime-Version',
222
+ 'Content-Transfer-Encoding'].each {|key|
223
+ header.push([key, @mail[key]]) unless @mail[key].empty?
224
+ }
225
+ sub_mail = Mail.new
226
+ parts = @mail.parts
227
+ sub_mail.read(parts.first)
228
+ body << sub_mail.body
229
+ sub_mail.body = body
230
+ parts[0] = sub_mail.to_s
231
+ body = Mail.join_parts(parts, @mail.boundary)
232
+ else
233
+ unless @mail['Content-Type'].empty?
234
+ header.push(['Content-Type', @mail['Content-Type']])
235
+ end
236
+ body << @mail.body
237
+ end
238
+ =end
239
+
240
+ body << _("The original body is omitted to avoid spam trouble.\n")
241
+
242
+ mail = {
243
+ :mail_from => '',
244
+ :recipient => @mail.from,
245
+ :header => header,
246
+ :body => body,
247
+ }
248
+ Sendmail.send_mail(@config.smtp_host, @config.smtp_port, @logger, mail)
249
+ @logger.log "[#{ml.name}]: Reject: #{@mail.from}"
250
+ end
251
+
252
+ def report_unsubscription (ml, member, requested_by = nil)
253
+ header = []
254
+ subject = Mail.encode_field(_("[%s] Unsubscribe: %s",
255
+ ml.name, ml.address))
256
+ header.push(['To', member],
257
+ ['From', ml.address],
258
+ ['Subject', subject],
259
+ ['Content-Type', content_type])
260
+
261
+ if requested_by
262
+ body = _("You are removed from the mailing list:\n<%s>\n",
263
+ ml.address)
264
+ body << _("by the request of <%s>.\n", requested_by)
265
+ else
266
+ body = _("You have unsubscribed from the mailing list:\n<%s>.\n",
267
+ ml.address)
268
+ end
269
+ body << generate_footer
270
+
271
+ mail = {
272
+ :mail_from => '',
273
+ :recipient => member,
274
+ :header => header,
275
+ :body => body,
276
+ }
277
+ Sendmail.send_mail(@config.smtp_host, @config.smtp_port, @logger, mail)
278
+ @logger.log "[#{ml.name}]: Unsubscribe: #{member}"
279
+ end
280
+
281
+ def report_too_many_members (ml, unadded_addresses)
282
+ header = [
283
+ ['To', @mail.from],
284
+ ['From', ml.address],
285
+ ['Subject',
286
+ Mail.encode_field(_("[QuickML] Error: %s", @mail['Subject']))],
287
+ ['Content-Type', content_type]
288
+ ]
289
+ body = _("The following addresses cannot be added because <%s> mailing list reaches the maximum number of members (%d persons)\n\n",
290
+ ml.address,
291
+ ml.get_max_members)
292
+ unadded_addresses.each {|address|
293
+ body << sprintf("<%s>\n", address)
294
+ }
295
+
296
+ body << generate_footer
297
+
298
+ mail = {
299
+ :mail_from => '',
300
+ :recipient => @mail.from,
301
+ :header => header,
302
+ :body => body,
303
+ }
304
+ Sendmail(@config.smtp_host, @config.smtp_port, @logger, mail)
305
+
306
+ str = unadded_addresses.join(',')
307
+ @logger.log "[#{ml.name}]: Too Many Members: #{str}"
308
+ end
309
+
310
+ def sender_knows_an_active_member? (ml)
311
+ return @mail.collect_cc.find {|address|
312
+ ml.active_members_include?(address)
313
+ }
314
+ end
315
+
316
+ def add_member (ml, address)
317
+ begin
318
+ ml.add_member(address)
319
+ rescue TooManyMembers
320
+ @unadded_addresses.push(address)
321
+ end
322
+ end
323
+
324
+ def ml_address_in_to? (ml)
325
+ return @mail.collect_to.find {|address|
326
+ address == ml.address
327
+ }
328
+ end
329
+
330
+ def submit_article (ml)
331
+ @unadded_addresses = []
332
+
333
+ if ml_address_in_to?(ml)
334
+ add_member(ml, @mail.from)
335
+ @mail.collect_cc.each {|address|
336
+ add_member(ml, address)
337
+ }
338
+ end
339
+
340
+ if ! @unadded_addresses.empty?
341
+ report_too_many_members(ml, @unadded_addresses)
342
+ end
343
+
344
+ ml.submit(@mail)
345
+ end
346
+
347
+ def unsubscribe_self (ml)
348
+ if ml.active_members_include?(@mail.from)
349
+ ml.remove_member(@mail.from)
350
+ report_unsubscription(ml, @mail.from)
351
+ else
352
+ report_rejection(ml)
353
+ end
354
+ end
355
+
356
+ def unsubscribe_other (ml, cc)
357
+ if ml.active_members_include?(@mail.from)
358
+ cc.each {|other|
359
+ if ml.active_members_include?(other)
360
+ ml.remove_member(other)
361
+ report_unsubscription(ml, other, @mail.from)
362
+ end
363
+ }
364
+ else
365
+ @logger.vlog 'rejected'
366
+ end
367
+ end
368
+
369
+ def unsubscribe (ml)
370
+ cc = @mail.collect_cc
371
+ if cc.empty?
372
+ unsubscribe_self(ml)
373
+ else
374
+ unsubscribe_other(ml, cc)
375
+ end
376
+ end
377
+
378
+ def acceptable_submission? (ml)
379
+ ml.newly_created? ||
380
+ ml.active_members_include?(@mail.from) ||
381
+ ml.former_members_include?(@mail.from) ||
382
+ sender_knows_an_active_member?(ml)
383
+ end
384
+
385
+ def confirmation_required? (ml)
386
+ @config.confirm_ml_creation and ml.newly_created?
387
+ end
388
+ end
389
+
390
+ class ErrorMailHandler
391
+ def initialize (config, message_charset)
392
+ @config = config
393
+ @logger = config.logger
394
+ @message_charset = message_charset
395
+ end
396
+
397
+ def handle (mail)
398
+ if /\A(.*)=return=(.*?)@(.*?)\z/ =~ mail.recipients.first
399
+ mladdress = $1 + '@' + $3
400
+ error_address = $2.sub(/=/, '@')
401
+
402
+ ServerMemory.ml_mutex(@config, mladdress).synchronize {
403
+ ml = Group.new(@config, mladdress, nil, @message_charset)
404
+ handle_error(ml, error_address)
405
+ }
406
+
407
+ else
408
+ @logger.vlog "Error: Use Postfix with XVERP to handle an error mail!"
409
+ end
410
+ end
411
+
412
+ private
413
+
414
+ def handle_error (ml, error_address)
415
+ @logger.log "ErrorMail: [#{ml.name}] #{error_address}"
416
+ ml.add_error_member(error_address)
417
+ end
418
+ end
419
+ end
420
+
421
+ if $0 == __FILE__
422
+ require 'qwik/testunit'
423
+ require 'qwik/test-module-ml'
424
+ require 'qwik/config'
425
+ require 'qwik/mail'
426
+ $test = true
427
+ end
428
+
429
+ if defined?($test) && $test
430
+ class TestMLProcessor < Test::Unit::TestCase
431
+ include TestModuleML
432
+
433
+ def test_class_method
434
+ c = QuickML::Processor
435
+ eq true, c.unsubscribe_requested?('')
436
+ eq false, c.unsubscribe_requested?('unsubscribe'+' '*489)
437
+ eq false, c.unsubscribe_requested?(' '*499)
438
+ eq false, c.unsubscribe_requested?(' '*500)
439
+ eq true, c.unsubscribe_requested?(' ')
440
+ eq true, c.unsubscribe_requested?("\n")
441
+ eq true, c.unsubscribe_requested?('unsubscribe')
442
+ eq true, c.unsubscribe_requested?(' unsubscribe')
443
+ eq true, c.unsubscribe_requested?('bye')
444
+ eq true, c.unsubscribe_requested?('#bye')
445
+ eq true, c.unsubscribe_requested?('# bye')
446
+ eq true, c.unsubscribe_requested?('�މ�')
447
+ eq true, c.unsubscribe_requested?('unsubscribe'+' '*488)
448
+ eq false, c.unsubscribe_requested?('unsubscribe desu.')
449
+ eq false, c.unsubscribe_requested?('I want to unsubscribe.')
450
+ end
451
+
452
+ def test_instance_method
453
+ mail = QuickML::Mail.generate {
454
+ 'From: "Test User" <user@e.com>
455
+ To: "Test Mailing List" <test@example.com>
456
+ Subject: Test Mail
457
+ Date: Mon, 3 Feb 2001 12:34:56 +0900
458
+
459
+ This is a test.
460
+ '
461
+ }
462
+ pro = QuickML::Processor.new(@ml_config, mail)
463
+
464
+ # test_to_return_address
465
+ t_make_public(QuickML::Processor, :to_return_address?)
466
+ eq nil, pro.to_return_address?('t@example.com')
467
+ assert pro.to_return_address?('t=return@example.com')
468
+
469
+ # test_to_confirmation_address
470
+ t_make_public(QuickML::Processor, :to_confirmation_address?)
471
+ eq nil, pro.to_confirmation_address?('t@example.com')
472
+ assert pro.to_confirmation_address?('confirm+t@example.com')
473
+ end
474
+
475
+ def ok_file(e, file)
476
+ dir = @config.sites_dir.path
477
+ str = (dir + "test/#{file}").path.read
478
+ ok_eq(e, str)
479
+ end
480
+
481
+ def ok_config(e)
482
+ dir = @config.sites_dir.path
483
+ str = (dir + 'test/_GroupConfig.txt').path.read
484
+ hash = QuickML::GroupConfig.parse_hash(str)
485
+ ok_eq(e, hash)
486
+ end
487
+
488
+ def test_all
489
+ mail = QuickML::Mail.generate {
490
+ 'From: "Test User" <user@e.com>
491
+ To: "Test Mailing List" <test@example.com>
492
+ Subject: Test Mail
493
+ Date: Mon, 3 Feb 2001 12:34:56 +0900
494
+
495
+ This is a test.
496
+ '
497
+ }
498
+ processor = QuickML::Processor.new(@ml_config, mail)
499
+ processor.process
500
+
501
+ ok_file("user@e.com\n", '_GroupMembers.txt')
502
+ ok_config({
503
+ :auto_unsubscribe_count=>5,
504
+ :max_mail_length=>102400,
505
+ :max_members=>100,
506
+ :ml_alert_time=>2073600,
507
+ :ml_life_time=>2678400,
508
+ :forward=>false,
509
+ :permanent=>false,
510
+ :unlimited=>false,
511
+ })
512
+ end
513
+
514
+ def test_with_confirm
515
+ message = 'From: "Test User" <user@e.com>
516
+ To: "Test Mailing List" <test@example.com>
517
+ Subject: Test Mail
518
+ Date: Mon, 3 Feb 2001 12:34:56 +0900
519
+
520
+ This is a test.
521
+ '
522
+ mail = QuickML::Mail.generate { message }
523
+ org_confirm_ml_creation = @ml_config[:confirm_ml_creation]
524
+ @ml_config[:confirm_ml_creation] = true
525
+
526
+ ".test/data/test".path.rmtree
527
+
528
+ processor = QuickML::Processor.new(@ml_config, mail)
529
+ processor.process
530
+
531
+ eq "ply to this mail to create ML <test@example.com>.\n",
532
+ $quickml_sendmail[4][-50..9999]
533
+
534
+ ok_file('', '_GroupMembers.txt')
535
+ ok_file("user@e.com\n", '_GroupWaitingMembers.txt')
536
+ ok_file(message, '_GroupWaitingMessage.txt')
537
+ h = {
538
+ :auto_unsubscribe_count=>5,
539
+ :max_mail_length=>102400,
540
+ :max_members=>100,
541
+ :ml_alert_time=>2073600,
542
+ :ml_life_time=>2678400,
543
+ :forward=>false,
544
+ :permanent=>false,
545
+ :unlimited=>false,
546
+ }
547
+ ok_config(h)
548
+ @ml_config[:confirm_ml_creation] = org_confirm_ml_creation
549
+ end
550
+
551
+ def test_invalid_mlname
552
+ message = 'From: user@e.com
553
+ To: invalid_mlname@example.com
554
+ Subject: Test Mail
555
+ Date: Mon, 3 Feb 2001 12:34:56 +0900
556
+
557
+ This is a test.
558
+ '
559
+ mail = QuickML::Mail.generate { message }
560
+ processor = QuickML::Processor.new(@ml_config, mail)
561
+ processor.process
562
+ eq "To: user@e.com\nFrom: postmaster@q.example.com\nSubject: [QuickML] Error: Test Mail\nContent-Type: text/plain\n\nInvalid mailing list name: <invalid_mlname@example.com>\nYou can only use 0-9, a-z, A-Z, `-' for mailing list name\n\n-- \nInfo: http://example.com/\n", $quickml_sendmail[4]
563
+ end
564
+
565
+ def test_ignore_list
566
+ $test_rejection_ignore_list = ["test"]
567
+
568
+ #
569
+ # normal case
570
+ #
571
+ send_normal_mail('bob@example.net') # Bob creates a new ML.
572
+
573
+ sendmail('bob@example.net', 'test@q.example.com', 'test mail') {
574
+ "This is a test."
575
+ }
576
+ eq true, @site.exist?('1')
577
+ eq 'test mail', @site['1'].get_title
578
+ eq "* test mail\n{{mail(bob@example.net,0)\nThis is a test.\n}}\n",
579
+ @site['1'].load
580
+
581
+ #
582
+ # sent from alien
583
+ #
584
+
585
+ # clear probe
586
+ $quickml_sendmail = nil
587
+
588
+ # rejection message should be null
589
+ expected = nil
590
+
591
+ input = []
592
+ input << 'alice@example.net' # from
593
+ input << 'test@q.example.com' # to
594
+ input << 'spam mail' # subject
595
+ inputBody = 'This is spam.'
596
+
597
+ sendmail(*input) {
598
+ inputBody
599
+ }
600
+ actual = $quickml_sendmail
601
+
602
+ ok_eq(expected, actual)
603
+
604
+ # clean up for test suite
605
+ $test_rejection_ignore_list = nil
606
+ end
607
+
608
+ end
609
+ end