shoesgem 0.1424.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 (808) hide show
  1. data/LICENSE +30 -0
  2. data/README.rdoc +31 -0
  3. data/lib/shoes.rb +4 -0
  4. data/shoes/CHANGELOG.txt +21 -0
  5. data/shoes/COPYING.txt +30 -0
  6. data/shoes/README.txt +117 -0
  7. data/shoes/VERSION.txt +1 -0
  8. data/shoes/fonts/Coolvetica.ttf +0 -0
  9. data/shoes/fonts/Lacuna.ttf +0 -0
  10. data/shoes/freetype6.dll +0 -0
  11. data/shoes/lib/shoes/cache.rb +54 -0
  12. data/shoes/lib/shoes/data.rb +39 -0
  13. data/shoes/lib/shoes/help.rb +421 -0
  14. data/shoes/lib/shoes/image.rb +25 -0
  15. data/shoes/lib/shoes/inspect.rb +128 -0
  16. data/shoes/lib/shoes/log.rb +48 -0
  17. data/shoes/lib/shoes/minitar.rb +986 -0
  18. data/shoes/lib/shoes/override.rb +38 -0
  19. data/shoes/lib/shoes/pack.rb +503 -0
  20. data/shoes/lib/shoes/search.rb +46 -0
  21. data/shoes/lib/shoes/setup.rb +329 -0
  22. data/shoes/lib/shoes/shy.rb +131 -0
  23. data/shoes/lib/shoes/shybuilder.rb +44 -0
  24. data/shoes/lib/shoes.rb +522 -0
  25. data/shoes/libcairo-2.dll +0 -0
  26. data/shoes/libeay32.dll +0 -0
  27. data/shoes/libexpat-1.dll +0 -0
  28. data/shoes/libfontconfig-1.dll +0 -0
  29. data/shoes/libgio-2.0-0.dll +0 -0
  30. data/shoes/libglib-2.0-0.dll +0 -0
  31. data/shoes/libgmodule-2.0-0.dll +0 -0
  32. data/shoes/libgobject-2.0-0.dll +0 -0
  33. data/shoes/libgthread-2.0-0.dll +0 -0
  34. data/shoes/libiconv2.dll +0 -0
  35. data/shoes/libjpeg-8.dll +0 -0
  36. data/shoes/libpango-1.0-0.dll +0 -0
  37. data/shoes/libpangocairo-1.0-0.dll +0 -0
  38. data/shoes/libpangoft2-1.0-0.dll +0 -0
  39. data/shoes/libpangowin32-1.0-0.dll +0 -0
  40. data/shoes/libpng14-14.dll +0 -0
  41. data/shoes/libportaudio-2.dll +0 -0
  42. data/shoes/libshoes.dll +0 -0
  43. data/shoes/libssl32.dll +0 -0
  44. data/shoes/libungif4.dll +0 -0
  45. data/shoes/msvcrt-ruby191.dll +0 -0
  46. data/shoes/readline5.dll +0 -0
  47. data/shoes/ruby/gems/1.9.1/gems/hpricot-0.8.1/lib/fast_xs.so +0 -0
  48. data/shoes/ruby/gems/1.9.1/gems/hpricot-0.8.1/lib/hpricot/blankslate.rb +63 -0
  49. data/shoes/ruby/gems/1.9.1/gems/hpricot-0.8.1/lib/hpricot/builder.rb +216 -0
  50. data/shoes/ruby/gems/1.9.1/gems/hpricot-0.8.1/lib/hpricot/elements.rb +510 -0
  51. data/shoes/ruby/gems/1.9.1/gems/hpricot-0.8.1/lib/hpricot/htmlinfo.rb +691 -0
  52. data/shoes/ruby/gems/1.9.1/gems/hpricot-0.8.1/lib/hpricot/inspect.rb +103 -0
  53. data/shoes/ruby/gems/1.9.1/gems/hpricot-0.8.1/lib/hpricot/modules.rb +40 -0
  54. data/shoes/ruby/gems/1.9.1/gems/hpricot-0.8.1/lib/hpricot/parse.rb +38 -0
  55. data/shoes/ruby/gems/1.9.1/gems/hpricot-0.8.1/lib/hpricot/tag.rb +202 -0
  56. data/shoes/ruby/gems/1.9.1/gems/hpricot-0.8.1/lib/hpricot/tags.rb +164 -0
  57. data/shoes/ruby/gems/1.9.1/gems/hpricot-0.8.1/lib/hpricot/traverse.rb +838 -0
  58. data/shoes/ruby/gems/1.9.1/gems/hpricot-0.8.1/lib/hpricot/xchar.rb +94 -0
  59. data/shoes/ruby/gems/1.9.1/gems/hpricot-0.8.1/lib/hpricot.rb +26 -0
  60. data/shoes/ruby/gems/1.9.1/gems/hpricot-0.8.1/lib/hpricot_scan.so +0 -0
  61. data/shoes/ruby/gems/1.9.1/gems/json-shoes-1.1.3/lib/json/add/core.rb +135 -0
  62. data/shoes/ruby/gems/1.9.1/gems/json-shoes-1.1.3/lib/json/add/rails.rb +58 -0
  63. data/shoes/ruby/gems/1.9.1/gems/json-shoes-1.1.3/lib/json/common.rb +354 -0
  64. data/shoes/ruby/gems/1.9.1/gems/json-shoes-1.1.3/lib/json/ext/generator.so +0 -0
  65. data/shoes/ruby/gems/1.9.1/gems/json-shoes-1.1.3/lib/json/ext/parser.so +0 -0
  66. data/shoes/ruby/gems/1.9.1/gems/json-shoes-1.1.3/lib/json/ext.rb +13 -0
  67. data/shoes/ruby/gems/1.9.1/gems/json-shoes-1.1.3/lib/json/version.rb +9 -0
  68. data/shoes/ruby/gems/1.9.1/gems/json-shoes-1.1.3/lib/json.rb +8 -0
  69. data/shoes/ruby/gems/1.9.1/gems/sqlite3-ruby-1.2.5-x86-mswin32/lib/sqlite3/constants.rb +49 -0
  70. data/shoes/ruby/gems/1.9.1/gems/sqlite3-ruby-1.2.5-x86-mswin32/lib/sqlite3/database.rb +721 -0
  71. data/shoes/ruby/gems/1.9.1/gems/sqlite3-ruby-1.2.5-x86-mswin32/lib/sqlite3/driver/dl/api.rb +152 -0
  72. data/shoes/ruby/gems/1.9.1/gems/sqlite3-ruby-1.2.5-x86-mswin32/lib/sqlite3/driver/dl/driver.rb +307 -0
  73. data/shoes/ruby/gems/1.9.1/gems/sqlite3-ruby-1.2.5-x86-mswin32/lib/sqlite3/driver/native/driver.rb +219 -0
  74. data/shoes/ruby/gems/1.9.1/gems/sqlite3-ruby-1.2.5-x86-mswin32/lib/sqlite3/errors.rb +68 -0
  75. data/shoes/ruby/gems/1.9.1/gems/sqlite3-ruby-1.2.5-x86-mswin32/lib/sqlite3/pragmas.rb +271 -0
  76. data/shoes/ruby/gems/1.9.1/gems/sqlite3-ruby-1.2.5-x86-mswin32/lib/sqlite3/resultset.rb +180 -0
  77. data/shoes/ruby/gems/1.9.1/gems/sqlite3-ruby-1.2.5-x86-mswin32/lib/sqlite3/statement.rb +231 -0
  78. data/shoes/ruby/gems/1.9.1/gems/sqlite3-ruby-1.2.5-x86-mswin32/lib/sqlite3/translator.rb +109 -0
  79. data/shoes/ruby/gems/1.9.1/gems/sqlite3-ruby-1.2.5-x86-mswin32/lib/sqlite3/value.rb +57 -0
  80. data/shoes/ruby/gems/1.9.1/gems/sqlite3-ruby-1.2.5-x86-mswin32/lib/sqlite3/version.rb +16 -0
  81. data/shoes/ruby/gems/1.9.1/gems/sqlite3-ruby-1.2.5-x86-mswin32/lib/sqlite3.rb +1 -0
  82. data/shoes/ruby/gems/1.9.1/gems/sqlite3-ruby-1.2.5-x86-mswin32/lib/sqlite3_api.so +0 -0
  83. data/shoes/ruby/gems/1.9.1/specifications/hpricot-0.8.1.gemspec +32 -0
  84. data/shoes/ruby/gems/1.9.1/specifications/json-shoes-1.1.3.gemspec +34 -0
  85. data/shoes/ruby/gems/1.9.1/specifications/sqlite3-ruby-1.2.5-x86-mswin32.gemspec +46 -0
  86. data/shoes/ruby/lib/English.rb +155 -0
  87. data/shoes/ruby/lib/Win32API.rb +28 -0
  88. data/shoes/ruby/lib/abbrev.rb +103 -0
  89. data/shoes/ruby/lib/base64.rb +91 -0
  90. data/shoes/ruby/lib/benchmark.rb +573 -0
  91. data/shoes/ruby/lib/bigdecimal/jacobian.rb +85 -0
  92. data/shoes/ruby/lib/bigdecimal/ludcmp.rb +86 -0
  93. data/shoes/ruby/lib/bigdecimal/math.rb +237 -0
  94. data/shoes/ruby/lib/bigdecimal/newton.rb +77 -0
  95. data/shoes/ruby/lib/bigdecimal/util.rb +53 -0
  96. data/shoes/ruby/lib/cgi/cookie.rb +144 -0
  97. data/shoes/ruby/lib/cgi/core.rb +786 -0
  98. data/shoes/ruby/lib/cgi/html.rb +1021 -0
  99. data/shoes/ruby/lib/cgi/session/pstore.rb +111 -0
  100. data/shoes/ruby/lib/cgi/session.rb +537 -0
  101. data/shoes/ruby/lib/cgi/util.rb +181 -0
  102. data/shoes/ruby/lib/cgi.rb +274 -0
  103. data/shoes/ruby/lib/cmath.rb +233 -0
  104. data/shoes/ruby/lib/complex.rb +24 -0
  105. data/shoes/ruby/lib/csv.rb +2320 -0
  106. data/shoes/ruby/lib/date/format.rb +1313 -0
  107. data/shoes/ruby/lib/date.rb +1834 -0
  108. data/shoes/ruby/lib/debug.rb +907 -0
  109. data/shoes/ruby/lib/delegate.rb +311 -0
  110. data/shoes/ruby/lib/digest/hmac.rb +274 -0
  111. data/shoes/ruby/lib/digest/sha2.rb +74 -0
  112. data/shoes/ruby/lib/digest.rb +50 -0
  113. data/shoes/ruby/lib/dl/callback.rb +69 -0
  114. data/shoes/ruby/lib/dl/cparser.rb +109 -0
  115. data/shoes/ruby/lib/dl/func.rb +153 -0
  116. data/shoes/ruby/lib/dl/import.rb +215 -0
  117. data/shoes/ruby/lib/dl/pack.rb +173 -0
  118. data/shoes/ruby/lib/dl/stack.rb +146 -0
  119. data/shoes/ruby/lib/dl/struct.rb +213 -0
  120. data/shoes/ruby/lib/dl/types.rb +40 -0
  121. data/shoes/ruby/lib/dl/value.rb +112 -0
  122. data/shoes/ruby/lib/drb/acl.rb +146 -0
  123. data/shoes/ruby/lib/drb/drb.rb +1778 -0
  124. data/shoes/ruby/lib/drb/eq.rb +16 -0
  125. data/shoes/ruby/lib/drb/extserv.rb +71 -0
  126. data/shoes/ruby/lib/drb/extservm.rb +85 -0
  127. data/shoes/ruby/lib/drb/gw.rb +122 -0
  128. data/shoes/ruby/lib/drb/invokemethod.rb +34 -0
  129. data/shoes/ruby/lib/drb/observer.rb +22 -0
  130. data/shoes/ruby/lib/drb/ssl.rb +190 -0
  131. data/shoes/ruby/lib/drb/timeridconv.rb +91 -0
  132. data/shoes/ruby/lib/drb/unix.rb +108 -0
  133. data/shoes/ruby/lib/drb.rb +2 -0
  134. data/shoes/ruby/lib/e2mmap.rb +172 -0
  135. data/shoes/ruby/lib/erb.rb +902 -0
  136. data/shoes/ruby/lib/fileutils.rb +1592 -0
  137. data/shoes/ruby/lib/find.rb +81 -0
  138. data/shoes/ruby/lib/forwardable.rb +270 -0
  139. data/shoes/ruby/lib/ftsearch/analysis/analyzer.rb +16 -0
  140. data/shoes/ruby/lib/ftsearch/analysis/simple_identifier_analyzer.rb +23 -0
  141. data/shoes/ruby/lib/ftsearch/analysis/whitespace_analyzer.rb +22 -0
  142. data/shoes/ruby/lib/ftsearch/document_map_reader.rb +106 -0
  143. data/shoes/ruby/lib/ftsearch/document_map_writer.rb +46 -0
  144. data/shoes/ruby/lib/ftsearch/field_infos.rb +46 -0
  145. data/shoes/ruby/lib/ftsearch/fragment_writer.rb +114 -0
  146. data/shoes/ruby/lib/ftsearch/fulltext_reader.rb +52 -0
  147. data/shoes/ruby/lib/ftsearch/fulltext_writer.rb +75 -0
  148. data/shoes/ruby/lib/ftsearch/suffix_array_reader.rb +275 -0
  149. data/shoes/ruby/lib/ftsearch/suffix_array_writer.rb +99 -0
  150. data/shoes/ruby/lib/ftsearch/util.rb +21 -0
  151. data/shoes/ruby/lib/getoptlong.rb +610 -0
  152. data/shoes/ruby/lib/gserver.rb +253 -0
  153. data/shoes/ruby/lib/i386-mingw32/bigdecimal.so +0 -0
  154. data/shoes/ruby/lib/i386-mingw32/binject.so +0 -0
  155. data/shoes/ruby/lib/i386-mingw32/bloops.so +0 -0
  156. data/shoes/ruby/lib/i386-mingw32/continuation.so +0 -0
  157. data/shoes/ruby/lib/i386-mingw32/coverage.so +0 -0
  158. data/shoes/ruby/lib/i386-mingw32/curses.so +0 -0
  159. data/shoes/ruby/lib/i386-mingw32/digest/bubblebabble.so +0 -0
  160. data/shoes/ruby/lib/i386-mingw32/digest/md5.so +0 -0
  161. data/shoes/ruby/lib/i386-mingw32/digest/rmd160.so +0 -0
  162. data/shoes/ruby/lib/i386-mingw32/digest/sha1.so +0 -0
  163. data/shoes/ruby/lib/i386-mingw32/digest/sha2.so +0 -0
  164. data/shoes/ruby/lib/i386-mingw32/digest.so +0 -0
  165. data/shoes/ruby/lib/i386-mingw32/dl.so +0 -0
  166. data/shoes/ruby/lib/i386-mingw32/enc/big5.so +0 -0
  167. data/shoes/ruby/lib/i386-mingw32/enc/cp949.so +0 -0
  168. data/shoes/ruby/lib/i386-mingw32/enc/emacs_mule.so +0 -0
  169. data/shoes/ruby/lib/i386-mingw32/enc/encdb.so +0 -0
  170. data/shoes/ruby/lib/i386-mingw32/enc/euc_jp.so +0 -0
  171. data/shoes/ruby/lib/i386-mingw32/enc/euc_kr.so +0 -0
  172. data/shoes/ruby/lib/i386-mingw32/enc/euc_tw.so +0 -0
  173. data/shoes/ruby/lib/i386-mingw32/enc/gb18030.so +0 -0
  174. data/shoes/ruby/lib/i386-mingw32/enc/gb2312.so +0 -0
  175. data/shoes/ruby/lib/i386-mingw32/enc/gbk.so +0 -0
  176. data/shoes/ruby/lib/i386-mingw32/enc/iso_8859_1.so +0 -0
  177. data/shoes/ruby/lib/i386-mingw32/enc/iso_8859_10.so +0 -0
  178. data/shoes/ruby/lib/i386-mingw32/enc/iso_8859_11.so +0 -0
  179. data/shoes/ruby/lib/i386-mingw32/enc/iso_8859_13.so +0 -0
  180. data/shoes/ruby/lib/i386-mingw32/enc/iso_8859_14.so +0 -0
  181. data/shoes/ruby/lib/i386-mingw32/enc/iso_8859_15.so +0 -0
  182. data/shoes/ruby/lib/i386-mingw32/enc/iso_8859_16.so +0 -0
  183. data/shoes/ruby/lib/i386-mingw32/enc/iso_8859_2.so +0 -0
  184. data/shoes/ruby/lib/i386-mingw32/enc/iso_8859_3.so +0 -0
  185. data/shoes/ruby/lib/i386-mingw32/enc/iso_8859_4.so +0 -0
  186. data/shoes/ruby/lib/i386-mingw32/enc/iso_8859_5.so +0 -0
  187. data/shoes/ruby/lib/i386-mingw32/enc/iso_8859_6.so +0 -0
  188. data/shoes/ruby/lib/i386-mingw32/enc/iso_8859_7.so +0 -0
  189. data/shoes/ruby/lib/i386-mingw32/enc/iso_8859_8.so +0 -0
  190. data/shoes/ruby/lib/i386-mingw32/enc/iso_8859_9.so +0 -0
  191. data/shoes/ruby/lib/i386-mingw32/enc/koi8_r.so +0 -0
  192. data/shoes/ruby/lib/i386-mingw32/enc/koi8_u.so +0 -0
  193. data/shoes/ruby/lib/i386-mingw32/enc/shift_jis.so +0 -0
  194. data/shoes/ruby/lib/i386-mingw32/enc/trans/big5.so +0 -0
  195. data/shoes/ruby/lib/i386-mingw32/enc/trans/chinese.so +0 -0
  196. data/shoes/ruby/lib/i386-mingw32/enc/trans/escape.so +0 -0
  197. data/shoes/ruby/lib/i386-mingw32/enc/trans/gb18030.so +0 -0
  198. data/shoes/ruby/lib/i386-mingw32/enc/trans/gbk.so +0 -0
  199. data/shoes/ruby/lib/i386-mingw32/enc/trans/iso2022.so +0 -0
  200. data/shoes/ruby/lib/i386-mingw32/enc/trans/japanese.so +0 -0
  201. data/shoes/ruby/lib/i386-mingw32/enc/trans/japanese_euc.so +0 -0
  202. data/shoes/ruby/lib/i386-mingw32/enc/trans/japanese_sjis.so +0 -0
  203. data/shoes/ruby/lib/i386-mingw32/enc/trans/korean.so +0 -0
  204. data/shoes/ruby/lib/i386-mingw32/enc/trans/single_byte.so +0 -0
  205. data/shoes/ruby/lib/i386-mingw32/enc/trans/transdb.so +0 -0
  206. data/shoes/ruby/lib/i386-mingw32/enc/trans/utf_16_32.so +0 -0
  207. data/shoes/ruby/lib/i386-mingw32/enc/utf_16be.so +0 -0
  208. data/shoes/ruby/lib/i386-mingw32/enc/utf_16le.so +0 -0
  209. data/shoes/ruby/lib/i386-mingw32/enc/utf_32be.so +0 -0
  210. data/shoes/ruby/lib/i386-mingw32/enc/utf_32le.so +0 -0
  211. data/shoes/ruby/lib/i386-mingw32/enc/windows_1251.so +0 -0
  212. data/shoes/ruby/lib/i386-mingw32/etc.so +0 -0
  213. data/shoes/ruby/lib/i386-mingw32/fcntl.so +0 -0
  214. data/shoes/ruby/lib/i386-mingw32/fiber.so +0 -0
  215. data/shoes/ruby/lib/i386-mingw32/ftsearchrt.so +0 -0
  216. data/shoes/ruby/lib/i386-mingw32/gdbm.so +0 -0
  217. data/shoes/ruby/lib/i386-mingw32/iconv.so +0 -0
  218. data/shoes/ruby/lib/i386-mingw32/io/wait.so +0 -0
  219. data/shoes/ruby/lib/i386-mingw32/json/ext/generator.so +0 -0
  220. data/shoes/ruby/lib/i386-mingw32/json/ext/parser.so +0 -0
  221. data/shoes/ruby/lib/i386-mingw32/mathn/complex.so +0 -0
  222. data/shoes/ruby/lib/i386-mingw32/mathn/rational.so +0 -0
  223. data/shoes/ruby/lib/i386-mingw32/nkf.so +0 -0
  224. data/shoes/ruby/lib/i386-mingw32/openssl.so +0 -0
  225. data/shoes/ruby/lib/i386-mingw32/racc/cparse.so +0 -0
  226. data/shoes/ruby/lib/i386-mingw32/rbconfig.rb +196 -0
  227. data/shoes/ruby/lib/i386-mingw32/readline.so +0 -0
  228. data/shoes/ruby/lib/i386-mingw32/ripper.so +0 -0
  229. data/shoes/ruby/lib/i386-mingw32/sdbm.so +0 -0
  230. data/shoes/ruby/lib/i386-mingw32/socket.so +0 -0
  231. data/shoes/ruby/lib/i386-mingw32/stringio.so +0 -0
  232. data/shoes/ruby/lib/i386-mingw32/strscan.so +0 -0
  233. data/shoes/ruby/lib/i386-mingw32/syck.so +0 -0
  234. data/shoes/ruby/lib/i386-mingw32/win32ole.so +0 -0
  235. data/shoes/ruby/lib/i386-mingw32/zlib.so +0 -0
  236. data/shoes/ruby/lib/io/nonblock.rb +23 -0
  237. data/shoes/ruby/lib/ipaddr.rb +813 -0
  238. data/shoes/ruby/lib/irb/cmd/chws.rb +32 -0
  239. data/shoes/ruby/lib/irb/cmd/fork.rb +38 -0
  240. data/shoes/ruby/lib/irb/cmd/help.rb +36 -0
  241. data/shoes/ruby/lib/irb/cmd/load.rb +66 -0
  242. data/shoes/ruby/lib/irb/cmd/nop.rb +38 -0
  243. data/shoes/ruby/lib/irb/cmd/pushws.rb +38 -0
  244. data/shoes/ruby/lib/irb/cmd/subirb.rb +42 -0
  245. data/shoes/ruby/lib/irb/completion.rb +207 -0
  246. data/shoes/ruby/lib/irb/context.rb +255 -0
  247. data/shoes/ruby/lib/irb/ext/change-ws.rb +61 -0
  248. data/shoes/ruby/lib/irb/ext/history.rb +109 -0
  249. data/shoes/ruby/lib/irb/ext/loader.rb +119 -0
  250. data/shoes/ruby/lib/irb/ext/math-mode.rb +36 -0
  251. data/shoes/ruby/lib/irb/ext/multi-irb.rb +240 -0
  252. data/shoes/ruby/lib/irb/ext/save-history.rb +100 -0
  253. data/shoes/ruby/lib/irb/ext/tracer.rb +60 -0
  254. data/shoes/ruby/lib/irb/ext/use-loader.rb +64 -0
  255. data/shoes/ruby/lib/irb/ext/workspaces.rb +55 -0
  256. data/shoes/ruby/lib/irb/extend-command.rb +272 -0
  257. data/shoes/ruby/lib/irb/frame.rb +66 -0
  258. data/shoes/ruby/lib/irb/help.rb +35 -0
  259. data/shoes/ruby/lib/irb/init.rb +288 -0
  260. data/shoes/ruby/lib/irb/input-method.rb +142 -0
  261. data/shoes/ruby/lib/irb/lc/error.rb +29 -0
  262. data/shoes/ruby/lib/irb/lc/help-message +38 -0
  263. data/shoes/ruby/lib/irb/lc/ja/encoding_aliases.rb +8 -0
  264. data/shoes/ruby/lib/irb/lc/ja/error.rb +27 -0
  265. data/shoes/ruby/lib/irb/lc/ja/help-message +39 -0
  266. data/shoes/ruby/lib/irb/locale.rb +195 -0
  267. data/shoes/ruby/lib/irb/magic-file.rb +36 -0
  268. data/shoes/ruby/lib/irb/notifier.rb +144 -0
  269. data/shoes/ruby/lib/irb/output-method.rb +69 -0
  270. data/shoes/ruby/lib/irb/ruby-lex.rb +1188 -0
  271. data/shoes/ruby/lib/irb/ruby-token.rb +270 -0
  272. data/shoes/ruby/lib/irb/slex.rb +282 -0
  273. data/shoes/ruby/lib/irb/src_encoding.rb +4 -0
  274. data/shoes/ruby/lib/irb/version.rb +15 -0
  275. data/shoes/ruby/lib/irb/workspace.rb +108 -0
  276. data/shoes/ruby/lib/irb/ws-for-case-2.rb +14 -0
  277. data/shoes/ruby/lib/irb/xmp.rb +97 -0
  278. data/shoes/ruby/lib/irb.rb +354 -0
  279. data/shoes/ruby/lib/json/add/core.rb +135 -0
  280. data/shoes/ruby/lib/json/add/rails.rb +58 -0
  281. data/shoes/ruby/lib/json/common.rb +354 -0
  282. data/shoes/ruby/lib/json/editor.rb +1371 -0
  283. data/shoes/ruby/lib/json/ext.rb +15 -0
  284. data/shoes/ruby/lib/json/version.rb +9 -0
  285. data/shoes/ruby/lib/json.rb +297 -0
  286. data/shoes/ruby/lib/kconv.rb +282 -0
  287. data/shoes/ruby/lib/logger.rb +732 -0
  288. data/shoes/ruby/lib/mathn.rb +206 -0
  289. data/shoes/ruby/lib/matrix.rb +1381 -0
  290. data/shoes/ruby/lib/minitest/autorun.rb +9 -0
  291. data/shoes/ruby/lib/minitest/mock.rb +37 -0
  292. data/shoes/ruby/lib/minitest/spec.rb +89 -0
  293. data/shoes/ruby/lib/minitest/unit.rb +497 -0
  294. data/shoes/ruby/lib/mkmf.rb +1958 -0
  295. data/shoes/ruby/lib/monitor.rb +265 -0
  296. data/shoes/ruby/lib/mutex_m.rb +91 -0
  297. data/shoes/ruby/lib/net/ftp.rb +981 -0
  298. data/shoes/ruby/lib/net/http.rb +2399 -0
  299. data/shoes/ruby/lib/net/https.rb +136 -0
  300. data/shoes/ruby/lib/net/imap.rb +3500 -0
  301. data/shoes/ruby/lib/net/pop.rb +1000 -0
  302. data/shoes/ruby/lib/net/protocol.rb +382 -0
  303. data/shoes/ruby/lib/net/smtp.rb +1014 -0
  304. data/shoes/ruby/lib/net/telnet.rb +759 -0
  305. data/shoes/ruby/lib/observer.rb +193 -0
  306. data/shoes/ruby/lib/open-uri.rb +832 -0
  307. data/shoes/ruby/lib/open3.rb +98 -0
  308. data/shoes/ruby/lib/openssl/bn.rb +35 -0
  309. data/shoes/ruby/lib/openssl/buffering.rb +242 -0
  310. data/shoes/ruby/lib/openssl/cipher.rb +65 -0
  311. data/shoes/ruby/lib/openssl/digest.rb +61 -0
  312. data/shoes/ruby/lib/openssl/ssl.rb +178 -0
  313. data/shoes/ruby/lib/openssl/x509.rb +155 -0
  314. data/shoes/ruby/lib/openssl.rb +24 -0
  315. data/shoes/ruby/lib/optparse/date.rb +17 -0
  316. data/shoes/ruby/lib/optparse/shellwords.rb +6 -0
  317. data/shoes/ruby/lib/optparse/time.rb +10 -0
  318. data/shoes/ruby/lib/optparse/uri.rb +6 -0
  319. data/shoes/ruby/lib/optparse/version.rb +70 -0
  320. data/shoes/ruby/lib/optparse.rb +1810 -0
  321. data/shoes/ruby/lib/ostruct.rb +145 -0
  322. data/shoes/ruby/lib/pathname.rb +1099 -0
  323. data/shoes/ruby/lib/pp.rb +532 -0
  324. data/shoes/ruby/lib/prettyprint.rb +896 -0
  325. data/shoes/ruby/lib/prime.rb +471 -0
  326. data/shoes/ruby/lib/profile.rb +10 -0
  327. data/shoes/ruby/lib/profiler.rb +59 -0
  328. data/shoes/ruby/lib/pstore.rb +543 -0
  329. data/shoes/ruby/lib/racc/parser.rb +441 -0
  330. data/shoes/ruby/lib/rake/classic_namespace.rb +8 -0
  331. data/shoes/ruby/lib/rake/clean.rb +33 -0
  332. data/shoes/ruby/lib/rake/gempackagetask.rb +97 -0
  333. data/shoes/ruby/lib/rake/loaders/makefile.rb +35 -0
  334. data/shoes/ruby/lib/rake/packagetask.rb +185 -0
  335. data/shoes/ruby/lib/rake/rake_test_loader.rb +5 -0
  336. data/shoes/ruby/lib/rake/rdoctask.rb +147 -0
  337. data/shoes/ruby/lib/rake/runtest.rb +23 -0
  338. data/shoes/ruby/lib/rake/tasklib.rb +23 -0
  339. data/shoes/ruby/lib/rake/testtask.rb +161 -0
  340. data/shoes/ruby/lib/rake/win32.rb +34 -0
  341. data/shoes/ruby/lib/rake.rb +2465 -0
  342. data/shoes/ruby/lib/rational.rb +19 -0
  343. data/shoes/ruby/lib/rbconfig/datadir.rb +24 -0
  344. data/shoes/ruby/lib/rdoc/code_objects.rb +1061 -0
  345. data/shoes/ruby/lib/rdoc/diagram.rb +340 -0
  346. data/shoes/ruby/lib/rdoc/dot.rb +249 -0
  347. data/shoes/ruby/lib/rdoc/generator/chm/chm.rb +100 -0
  348. data/shoes/ruby/lib/rdoc/generator/chm.rb +113 -0
  349. data/shoes/ruby/lib/rdoc/generator/html/common.rb +24 -0
  350. data/shoes/ruby/lib/rdoc/generator/html/frameless.rb +92 -0
  351. data/shoes/ruby/lib/rdoc/generator/html/hefss.rb +150 -0
  352. data/shoes/ruby/lib/rdoc/generator/html/html.rb +769 -0
  353. data/shoes/ruby/lib/rdoc/generator/html/kilmer.rb +151 -0
  354. data/shoes/ruby/lib/rdoc/generator/html/kilmerfactory.rb +427 -0
  355. data/shoes/ruby/lib/rdoc/generator/html/one_page_html.rb +122 -0
  356. data/shoes/ruby/lib/rdoc/generator/html.rb +445 -0
  357. data/shoes/ruby/lib/rdoc/generator/ri.rb +226 -0
  358. data/shoes/ruby/lib/rdoc/generator/texinfo/class.texinfo.erb +44 -0
  359. data/shoes/ruby/lib/rdoc/generator/texinfo/file.texinfo.erb +6 -0
  360. data/shoes/ruby/lib/rdoc/generator/texinfo/method.texinfo.erb +6 -0
  361. data/shoes/ruby/lib/rdoc/generator/texinfo/texinfo.erb +28 -0
  362. data/shoes/ruby/lib/rdoc/generator/texinfo.rb +81 -0
  363. data/shoes/ruby/lib/rdoc/generator/xml/rdf.rb +113 -0
  364. data/shoes/ruby/lib/rdoc/generator/xml/xml.rb +123 -0
  365. data/shoes/ruby/lib/rdoc/generator/xml.rb +117 -0
  366. data/shoes/ruby/lib/rdoc/generator.rb +1082 -0
  367. data/shoes/ruby/lib/rdoc/known_classes.rb +68 -0
  368. data/shoes/ruby/lib/rdoc/markup/attribute_manager.rb +265 -0
  369. data/shoes/ruby/lib/rdoc/markup/formatter.rb +14 -0
  370. data/shoes/ruby/lib/rdoc/markup/fragments.rb +337 -0
  371. data/shoes/ruby/lib/rdoc/markup/inline.rb +101 -0
  372. data/shoes/ruby/lib/rdoc/markup/lines.rb +152 -0
  373. data/shoes/ruby/lib/rdoc/markup/preprocess.rb +75 -0
  374. data/shoes/ruby/lib/rdoc/markup/to_flow.rb +185 -0
  375. data/shoes/ruby/lib/rdoc/markup/to_html.rb +403 -0
  376. data/shoes/ruby/lib/rdoc/markup/to_html_crossref.rb +148 -0
  377. data/shoes/ruby/lib/rdoc/markup/to_latex.rb +328 -0
  378. data/shoes/ruby/lib/rdoc/markup/to_test.rb +50 -0
  379. data/shoes/ruby/lib/rdoc/markup/to_texinfo.rb +69 -0
  380. data/shoes/ruby/lib/rdoc/markup.rb +378 -0
  381. data/shoes/ruby/lib/rdoc/options.rb +638 -0
  382. data/shoes/ruby/lib/rdoc/parser/c.rb +661 -0
  383. data/shoes/ruby/lib/rdoc/parser/f95.rb +1835 -0
  384. data/shoes/ruby/lib/rdoc/parser/perl.rb +165 -0
  385. data/shoes/ruby/lib/rdoc/parser/ruby.rb +2829 -0
  386. data/shoes/ruby/lib/rdoc/parser/simple.rb +38 -0
  387. data/shoes/ruby/lib/rdoc/parser.rb +142 -0
  388. data/shoes/ruby/lib/rdoc/rdoc.rb +293 -0
  389. data/shoes/ruby/lib/rdoc/ri/cache.rb +187 -0
  390. data/shoes/ruby/lib/rdoc/ri/descriptions.rb +156 -0
  391. data/shoes/ruby/lib/rdoc/ri/display.rb +392 -0
  392. data/shoes/ruby/lib/rdoc/ri/driver.rb +669 -0
  393. data/shoes/ruby/lib/rdoc/ri/formatter.rb +616 -0
  394. data/shoes/ruby/lib/rdoc/ri/paths.rb +99 -0
  395. data/shoes/ruby/lib/rdoc/ri/reader.rb +106 -0
  396. data/shoes/ruby/lib/rdoc/ri/util.rb +79 -0
  397. data/shoes/ruby/lib/rdoc/ri/writer.rb +68 -0
  398. data/shoes/ruby/lib/rdoc/ri.rb +8 -0
  399. data/shoes/ruby/lib/rdoc/stats.rb +115 -0
  400. data/shoes/ruby/lib/rdoc/template.rb +64 -0
  401. data/shoes/ruby/lib/rdoc/tokenstream.rb +33 -0
  402. data/shoes/ruby/lib/rdoc.rb +395 -0
  403. data/shoes/ruby/lib/resolv-replace.rb +63 -0
  404. data/shoes/ruby/lib/resolv.rb +2262 -0
  405. data/shoes/ruby/lib/rexml/attlistdecl.rb +62 -0
  406. data/shoes/ruby/lib/rexml/attribute.rb +188 -0
  407. data/shoes/ruby/lib/rexml/cdata.rb +67 -0
  408. data/shoes/ruby/lib/rexml/child.rb +96 -0
  409. data/shoes/ruby/lib/rexml/comment.rb +80 -0
  410. data/shoes/ruby/lib/rexml/doctype.rb +270 -0
  411. data/shoes/ruby/lib/rexml/document.rb +231 -0
  412. data/shoes/ruby/lib/rexml/dtd/attlistdecl.rb +10 -0
  413. data/shoes/ruby/lib/rexml/dtd/dtd.rb +51 -0
  414. data/shoes/ruby/lib/rexml/dtd/elementdecl.rb +17 -0
  415. data/shoes/ruby/lib/rexml/dtd/entitydecl.rb +56 -0
  416. data/shoes/ruby/lib/rexml/dtd/notationdecl.rb +39 -0
  417. data/shoes/ruby/lib/rexml/element.rb +1246 -0
  418. data/shoes/ruby/lib/rexml/encoding.rb +71 -0
  419. data/shoes/ruby/lib/rexml/encodings/CP-1252.rb +103 -0
  420. data/shoes/ruby/lib/rexml/encodings/EUC-JP.rb +35 -0
  421. data/shoes/ruby/lib/rexml/encodings/ICONV.rb +22 -0
  422. data/shoes/ruby/lib/rexml/encodings/ISO-8859-1.rb +7 -0
  423. data/shoes/ruby/lib/rexml/encodings/ISO-8859-15.rb +72 -0
  424. data/shoes/ruby/lib/rexml/encodings/SHIFT-JIS.rb +37 -0
  425. data/shoes/ruby/lib/rexml/encodings/SHIFT_JIS.rb +1 -0
  426. data/shoes/ruby/lib/rexml/encodings/UNILE.rb +34 -0
  427. data/shoes/ruby/lib/rexml/encodings/US-ASCII.rb +30 -0
  428. data/shoes/ruby/lib/rexml/encodings/UTF-16.rb +35 -0
  429. data/shoes/ruby/lib/rexml/encodings/UTF-8.rb +18 -0
  430. data/shoes/ruby/lib/rexml/entity.rb +166 -0
  431. data/shoes/ruby/lib/rexml/formatters/default.rb +109 -0
  432. data/shoes/ruby/lib/rexml/formatters/pretty.rb +139 -0
  433. data/shoes/ruby/lib/rexml/formatters/transitive.rb +58 -0
  434. data/shoes/ruby/lib/rexml/functions.rb +388 -0
  435. data/shoes/ruby/lib/rexml/instruction.rb +70 -0
  436. data/shoes/ruby/lib/rexml/light/node.rb +196 -0
  437. data/shoes/ruby/lib/rexml/namespace.rb +47 -0
  438. data/shoes/ruby/lib/rexml/node.rb +75 -0
  439. data/shoes/ruby/lib/rexml/output.rb +24 -0
  440. data/shoes/ruby/lib/rexml/parent.rb +166 -0
  441. data/shoes/ruby/lib/rexml/parseexception.rb +51 -0
  442. data/shoes/ruby/lib/rexml/parsers/baseparser.rb +530 -0
  443. data/shoes/ruby/lib/rexml/parsers/lightparser.rb +58 -0
  444. data/shoes/ruby/lib/rexml/parsers/pullparser.rb +196 -0
  445. data/shoes/ruby/lib/rexml/parsers/sax2parser.rb +247 -0
  446. data/shoes/ruby/lib/rexml/parsers/streamparser.rb +46 -0
  447. data/shoes/ruby/lib/rexml/parsers/treeparser.rb +100 -0
  448. data/shoes/ruby/lib/rexml/parsers/ultralightparser.rb +56 -0
  449. data/shoes/ruby/lib/rexml/parsers/xpathparser.rb +698 -0
  450. data/shoes/ruby/lib/rexml/quickpath.rb +263 -0
  451. data/shoes/ruby/lib/rexml/rexml.rb +31 -0
  452. data/shoes/ruby/lib/rexml/sax2listener.rb +97 -0
  453. data/shoes/ruby/lib/rexml/source.rb +258 -0
  454. data/shoes/ruby/lib/rexml/streamlistener.rb +92 -0
  455. data/shoes/ruby/lib/rexml/syncenumerator.rb +32 -0
  456. data/shoes/ruby/lib/rexml/text.rb +404 -0
  457. data/shoes/ruby/lib/rexml/undefinednamespaceexception.rb +8 -0
  458. data/shoes/ruby/lib/rexml/validation/relaxng.rb +559 -0
  459. data/shoes/ruby/lib/rexml/validation/validation.rb +155 -0
  460. data/shoes/ruby/lib/rexml/validation/validationexception.rb +9 -0
  461. data/shoes/ruby/lib/rexml/xmldecl.rb +119 -0
  462. data/shoes/ruby/lib/rexml/xmltokens.rb +18 -0
  463. data/shoes/ruby/lib/rexml/xpath.rb +77 -0
  464. data/shoes/ruby/lib/rexml/xpath_parser.rb +792 -0
  465. data/shoes/ruby/lib/rinda/rinda.rb +283 -0
  466. data/shoes/ruby/lib/rinda/ring.rb +271 -0
  467. data/shoes/ruby/lib/rinda/tuplespace.rb +642 -0
  468. data/shoes/ruby/lib/ripper/core.rb +70 -0
  469. data/shoes/ruby/lib/ripper/filter.rb +70 -0
  470. data/shoes/ruby/lib/ripper/lexer.rb +179 -0
  471. data/shoes/ruby/lib/ripper/sexp.rb +99 -0
  472. data/shoes/ruby/lib/ripper.rb +4 -0
  473. data/shoes/ruby/lib/rss/0.9.rb +427 -0
  474. data/shoes/ruby/lib/rss/1.0.rb +452 -0
  475. data/shoes/ruby/lib/rss/2.0.rb +111 -0
  476. data/shoes/ruby/lib/rss/atom.rb +748 -0
  477. data/shoes/ruby/lib/rss/content/1.0.rb +10 -0
  478. data/shoes/ruby/lib/rss/content/2.0.rb +12 -0
  479. data/shoes/ruby/lib/rss/content.rb +31 -0
  480. data/shoes/ruby/lib/rss/converter.rb +170 -0
  481. data/shoes/ruby/lib/rss/dublincore/1.0.rb +13 -0
  482. data/shoes/ruby/lib/rss/dublincore/2.0.rb +13 -0
  483. data/shoes/ruby/lib/rss/dublincore/atom.rb +17 -0
  484. data/shoes/ruby/lib/rss/dublincore.rb +161 -0
  485. data/shoes/ruby/lib/rss/image.rb +193 -0
  486. data/shoes/ruby/lib/rss/itunes.rb +410 -0
  487. data/shoes/ruby/lib/rss/maker/0.9.rb +467 -0
  488. data/shoes/ruby/lib/rss/maker/1.0.rb +434 -0
  489. data/shoes/ruby/lib/rss/maker/2.0.rb +223 -0
  490. data/shoes/ruby/lib/rss/maker/atom.rb +172 -0
  491. data/shoes/ruby/lib/rss/maker/base.rb +880 -0
  492. data/shoes/ruby/lib/rss/maker/content.rb +21 -0
  493. data/shoes/ruby/lib/rss/maker/dublincore.rb +124 -0
  494. data/shoes/ruby/lib/rss/maker/entry.rb +163 -0
  495. data/shoes/ruby/lib/rss/maker/feed.rb +430 -0
  496. data/shoes/ruby/lib/rss/maker/image.rb +111 -0
  497. data/shoes/ruby/lib/rss/maker/itunes.rb +242 -0
  498. data/shoes/ruby/lib/rss/maker/slash.rb +33 -0
  499. data/shoes/ruby/lib/rss/maker/syndication.rb +18 -0
  500. data/shoes/ruby/lib/rss/maker/taxonomy.rb +118 -0
  501. data/shoes/ruby/lib/rss/maker/trackback.rb +61 -0
  502. data/shoes/ruby/lib/rss/maker.rb +44 -0
  503. data/shoes/ruby/lib/rss/parser.rb +568 -0
  504. data/shoes/ruby/lib/rss/rexmlparser.rb +54 -0
  505. data/shoes/ruby/lib/rss/rss.rb +1313 -0
  506. data/shoes/ruby/lib/rss/slash.rb +49 -0
  507. data/shoes/ruby/lib/rss/syndication.rb +67 -0
  508. data/shoes/ruby/lib/rss/taxonomy.rb +145 -0
  509. data/shoes/ruby/lib/rss/trackback.rb +288 -0
  510. data/shoes/ruby/lib/rss/utils.rb +111 -0
  511. data/shoes/ruby/lib/rss/xml-stylesheet.rb +105 -0
  512. data/shoes/ruby/lib/rss/xml.rb +71 -0
  513. data/shoes/ruby/lib/rss/xmlparser.rb +93 -0
  514. data/shoes/ruby/lib/rss/xmlscanner.rb +121 -0
  515. data/shoes/ruby/lib/rss.rb +19 -0
  516. data/shoes/ruby/lib/rubygems/builder.rb +88 -0
  517. data/shoes/ruby/lib/rubygems/command.rb +406 -0
  518. data/shoes/ruby/lib/rubygems/command_manager.rb +146 -0
  519. data/shoes/ruby/lib/rubygems/commands/build_command.rb +53 -0
  520. data/shoes/ruby/lib/rubygems/commands/cert_command.rb +86 -0
  521. data/shoes/ruby/lib/rubygems/commands/check_command.rb +75 -0
  522. data/shoes/ruby/lib/rubygems/commands/cleanup_command.rb +91 -0
  523. data/shoes/ruby/lib/rubygems/commands/contents_command.rb +74 -0
  524. data/shoes/ruby/lib/rubygems/commands/dependency_command.rb +188 -0
  525. data/shoes/ruby/lib/rubygems/commands/environment_command.rb +128 -0
  526. data/shoes/ruby/lib/rubygems/commands/fetch_command.rb +62 -0
  527. data/shoes/ruby/lib/rubygems/commands/generate_index_command.rb +57 -0
  528. data/shoes/ruby/lib/rubygems/commands/help_command.rb +172 -0
  529. data/shoes/ruby/lib/rubygems/commands/install_command.rb +148 -0
  530. data/shoes/ruby/lib/rubygems/commands/list_command.rb +35 -0
  531. data/shoes/ruby/lib/rubygems/commands/lock_command.rb +110 -0
  532. data/shoes/ruby/lib/rubygems/commands/mirror_command.rb +111 -0
  533. data/shoes/ruby/lib/rubygems/commands/outdated_command.rb +33 -0
  534. data/shoes/ruby/lib/rubygems/commands/pristine_command.rb +93 -0
  535. data/shoes/ruby/lib/rubygems/commands/query_command.rb +233 -0
  536. data/shoes/ruby/lib/rubygems/commands/rdoc_command.rb +82 -0
  537. data/shoes/ruby/lib/rubygems/commands/search_command.rb +37 -0
  538. data/shoes/ruby/lib/rubygems/commands/server_command.rb +48 -0
  539. data/shoes/ruby/lib/rubygems/commands/sources_command.rb +152 -0
  540. data/shoes/ruby/lib/rubygems/commands/specification_command.rb +77 -0
  541. data/shoes/ruby/lib/rubygems/commands/stale_command.rb +27 -0
  542. data/shoes/ruby/lib/rubygems/commands/uninstall_command.rb +73 -0
  543. data/shoes/ruby/lib/rubygems/commands/unpack_command.rb +95 -0
  544. data/shoes/ruby/lib/rubygems/commands/update_command.rb +181 -0
  545. data/shoes/ruby/lib/rubygems/commands/which_command.rb +87 -0
  546. data/shoes/ruby/lib/rubygems/config_file.rb +266 -0
  547. data/shoes/ruby/lib/rubygems/custom_require.rb +46 -0
  548. data/shoes/ruby/lib/rubygems/defaults.rb +89 -0
  549. data/shoes/ruby/lib/rubygems/dependency.rb +119 -0
  550. data/shoes/ruby/lib/rubygems/dependency_installer.rb +258 -0
  551. data/shoes/ruby/lib/rubygems/dependency_list.rb +165 -0
  552. data/shoes/ruby/lib/rubygems/digest/digest_adapter.rb +40 -0
  553. data/shoes/ruby/lib/rubygems/digest/md5.rb +23 -0
  554. data/shoes/ruby/lib/rubygems/digest/sha1.rb +17 -0
  555. data/shoes/ruby/lib/rubygems/digest/sha2.rb +17 -0
  556. data/shoes/ruby/lib/rubygems/doc_manager.rb +214 -0
  557. data/shoes/ruby/lib/rubygems/exceptions.rb +84 -0
  558. data/shoes/ruby/lib/rubygems/ext/builder.rb +56 -0
  559. data/shoes/ruby/lib/rubygems/ext/configure_builder.rb +24 -0
  560. data/shoes/ruby/lib/rubygems/ext/ext_conf_builder.rb +23 -0
  561. data/shoes/ruby/lib/rubygems/ext/rake_builder.rb +27 -0
  562. data/shoes/ruby/lib/rubygems/ext.rb +18 -0
  563. data/shoes/ruby/lib/rubygems/format.rb +87 -0
  564. data/shoes/ruby/lib/rubygems/gem_openssl.rb +83 -0
  565. data/shoes/ruby/lib/rubygems/gem_path_searcher.rb +100 -0
  566. data/shoes/ruby/lib/rubygems/gem_runner.rb +58 -0
  567. data/shoes/ruby/lib/rubygems/indexer/abstract_index_builder.rb +88 -0
  568. data/shoes/ruby/lib/rubygems/indexer/latest_index_builder.rb +35 -0
  569. data/shoes/ruby/lib/rubygems/indexer/marshal_index_builder.rb +17 -0
  570. data/shoes/ruby/lib/rubygems/indexer/master_index_builder.rb +54 -0
  571. data/shoes/ruby/lib/rubygems/indexer/quick_index_builder.rb +50 -0
  572. data/shoes/ruby/lib/rubygems/indexer.rb +370 -0
  573. data/shoes/ruby/lib/rubygems/install_update_options.rb +113 -0
  574. data/shoes/ruby/lib/rubygems/installer.rb +578 -0
  575. data/shoes/ruby/lib/rubygems/local_remote_options.rb +134 -0
  576. data/shoes/ruby/lib/rubygems/old_format.rb +148 -0
  577. data/shoes/ruby/lib/rubygems/package/f_sync_dir.rb +24 -0
  578. data/shoes/ruby/lib/rubygems/package/tar_header.rb +245 -0
  579. data/shoes/ruby/lib/rubygems/package/tar_input.rb +219 -0
  580. data/shoes/ruby/lib/rubygems/package/tar_output.rb +143 -0
  581. data/shoes/ruby/lib/rubygems/package/tar_reader/entry.rb +99 -0
  582. data/shoes/ruby/lib/rubygems/package/tar_reader.rb +86 -0
  583. data/shoes/ruby/lib/rubygems/package/tar_writer.rb +180 -0
  584. data/shoes/ruby/lib/rubygems/package.rb +95 -0
  585. data/shoes/ruby/lib/rubygems/platform.rb +178 -0
  586. data/shoes/ruby/lib/rubygems/remote_fetcher.rb +344 -0
  587. data/shoes/ruby/lib/rubygems/require_paths_builder.rb +15 -0
  588. data/shoes/ruby/lib/rubygems/requirement.rb +163 -0
  589. data/shoes/ruby/lib/rubygems/rubygems_version.rb +6 -0
  590. data/shoes/ruby/lib/rubygems/security.rb +786 -0
  591. data/shoes/ruby/lib/rubygems/server.rb +629 -0
  592. data/shoes/ruby/lib/rubygems/source_index.rb +559 -0
  593. data/shoes/ruby/lib/rubygems/source_info_cache.rb +393 -0
  594. data/shoes/ruby/lib/rubygems/source_info_cache_entry.rb +56 -0
  595. data/shoes/ruby/lib/rubygems/spec_fetcher.rb +249 -0
  596. data/shoes/ruby/lib/rubygems/specification.rb +1262 -0
  597. data/shoes/ruby/lib/rubygems/test_utilities.rb +131 -0
  598. data/shoes/ruby/lib/rubygems/timer.rb +25 -0
  599. data/shoes/ruby/lib/rubygems/uninstaller.rb +242 -0
  600. data/shoes/ruby/lib/rubygems/user_interaction.rb +360 -0
  601. data/shoes/ruby/lib/rubygems/validator.rb +208 -0
  602. data/shoes/ruby/lib/rubygems/version.rb +167 -0
  603. data/shoes/ruby/lib/rubygems/version_option.rb +48 -0
  604. data/shoes/ruby/lib/rubygems.rb +888 -0
  605. data/shoes/ruby/lib/scanf.rb +703 -0
  606. data/shoes/ruby/lib/securerandom.rb +182 -0
  607. data/shoes/ruby/lib/set.rb +1274 -0
  608. data/shoes/ruby/lib/shell/builtin-command.rb +160 -0
  609. data/shoes/ruby/lib/shell/command-processor.rb +593 -0
  610. data/shoes/ruby/lib/shell/error.rb +25 -0
  611. data/shoes/ruby/lib/shell/filter.rb +109 -0
  612. data/shoes/ruby/lib/shell/process-controller.rb +319 -0
  613. data/shoes/ruby/lib/shell/system-command.rb +159 -0
  614. data/shoes/ruby/lib/shell/version.rb +15 -0
  615. data/shoes/ruby/lib/shell.rb +300 -0
  616. data/shoes/ruby/lib/shellwords.rb +156 -0
  617. data/shoes/ruby/lib/singleton.rb +313 -0
  618. data/shoes/ruby/lib/sync.rb +307 -0
  619. data/shoes/ruby/lib/tempfile.rb +218 -0
  620. data/shoes/ruby/lib/test/unit/assertions.rb +122 -0
  621. data/shoes/ruby/lib/test/unit/testcase.rb +12 -0
  622. data/shoes/ruby/lib/test/unit.rb +66 -0
  623. data/shoes/ruby/lib/thread.rb +367 -0
  624. data/shoes/ruby/lib/thwait.rb +168 -0
  625. data/shoes/ruby/lib/time.rb +869 -0
  626. data/shoes/ruby/lib/timeout.rb +108 -0
  627. data/shoes/ruby/lib/tmpdir.rb +138 -0
  628. data/shoes/ruby/lib/tracer.rb +166 -0
  629. data/shoes/ruby/lib/tsort.rb +290 -0
  630. data/shoes/ruby/lib/ubygems.rb +10 -0
  631. data/shoes/ruby/lib/un.rb +304 -0
  632. data/shoes/ruby/lib/uri/common.rb +727 -0
  633. data/shoes/ruby/lib/uri/ftp.rb +198 -0
  634. data/shoes/ruby/lib/uri/generic.rb +1128 -0
  635. data/shoes/ruby/lib/uri/http.rb +100 -0
  636. data/shoes/ruby/lib/uri/https.rb +20 -0
  637. data/shoes/ruby/lib/uri/ldap.rb +190 -0
  638. data/shoes/ruby/lib/uri/ldaps.rb +12 -0
  639. data/shoes/ruby/lib/uri/mailto.rb +266 -0
  640. data/shoes/ruby/lib/uri.rb +29 -0
  641. data/shoes/ruby/lib/weakref.rb +80 -0
  642. data/shoes/ruby/lib/webrick/accesslog.rb +75 -0
  643. data/shoes/ruby/lib/webrick/cgi.rb +260 -0
  644. data/shoes/ruby/lib/webrick/compat.rb +15 -0
  645. data/shoes/ruby/lib/webrick/config.rb +100 -0
  646. data/shoes/ruby/lib/webrick/cookie.rb +110 -0
  647. data/shoes/ruby/lib/webrick/htmlutils.rb +25 -0
  648. data/shoes/ruby/lib/webrick/httpauth/authenticator.rb +79 -0
  649. data/shoes/ruby/lib/webrick/httpauth/basicauth.rb +65 -0
  650. data/shoes/ruby/lib/webrick/httpauth/digestauth.rb +344 -0
  651. data/shoes/ruby/lib/webrick/httpauth/htdigest.rb +91 -0
  652. data/shoes/ruby/lib/webrick/httpauth/htgroup.rb +61 -0
  653. data/shoes/ruby/lib/webrick/httpauth/htpasswd.rb +83 -0
  654. data/shoes/ruby/lib/webrick/httpauth/userdb.rb +29 -0
  655. data/shoes/ruby/lib/webrick/httpauth.rb +45 -0
  656. data/shoes/ruby/lib/webrick/httpproxy.rb +288 -0
  657. data/shoes/ruby/lib/webrick/httprequest.rb +402 -0
  658. data/shoes/ruby/lib/webrick/httpresponse.rb +326 -0
  659. data/shoes/ruby/lib/webrick/https.rb +63 -0
  660. data/shoes/ruby/lib/webrick/httpserver.rb +217 -0
  661. data/shoes/ruby/lib/webrick/httpservlet/abstract.rb +70 -0
  662. data/shoes/ruby/lib/webrick/httpservlet/cgi_runner.rb +47 -0
  663. data/shoes/ruby/lib/webrick/httpservlet/cgihandler.rb +110 -0
  664. data/shoes/ruby/lib/webrick/httpservlet/erbhandler.rb +54 -0
  665. data/shoes/ruby/lib/webrick/httpservlet/filehandler.rb +435 -0
  666. data/shoes/ruby/lib/webrick/httpservlet/prochandler.rb +33 -0
  667. data/shoes/ruby/lib/webrick/httpservlet.rb +22 -0
  668. data/shoes/ruby/lib/webrick/httpstatus.rb +132 -0
  669. data/shoes/ruby/lib/webrick/httputils.rb +392 -0
  670. data/shoes/ruby/lib/webrick/httpversion.rb +49 -0
  671. data/shoes/ruby/lib/webrick/log.rb +88 -0
  672. data/shoes/ruby/lib/webrick/server.rb +210 -0
  673. data/shoes/ruby/lib/webrick/ssl.rb +126 -0
  674. data/shoes/ruby/lib/webrick/utils.rb +175 -0
  675. data/shoes/ruby/lib/webrick/version.rb +13 -0
  676. data/shoes/ruby/lib/webrick.rb +29 -0
  677. data/shoes/ruby/lib/win32/registry.rb +832 -0
  678. data/shoes/ruby/lib/win32/resolv.rb +370 -0
  679. data/shoes/ruby/lib/win32/sspi.rb +330 -0
  680. data/shoes/ruby/lib/win32ole/property.rb +16 -0
  681. data/shoes/ruby/lib/xmlrpc/base64.rb +81 -0
  682. data/shoes/ruby/lib/xmlrpc/client.rb +625 -0
  683. data/shoes/ruby/lib/xmlrpc/config.rb +40 -0
  684. data/shoes/ruby/lib/xmlrpc/create.rb +290 -0
  685. data/shoes/ruby/lib/xmlrpc/datetime.rb +142 -0
  686. data/shoes/ruby/lib/xmlrpc/httpserver.rb +178 -0
  687. data/shoes/ruby/lib/xmlrpc/marshal.rb +76 -0
  688. data/shoes/ruby/lib/xmlrpc/parser.rb +813 -0
  689. data/shoes/ruby/lib/xmlrpc/server.rb +778 -0
  690. data/shoes/ruby/lib/xmlrpc/utils.rb +165 -0
  691. data/shoes/ruby/lib/yaml/baseemitter.rb +242 -0
  692. data/shoes/ruby/lib/yaml/basenode.rb +216 -0
  693. data/shoes/ruby/lib/yaml/constants.rb +45 -0
  694. data/shoes/ruby/lib/yaml/dbm.rb +111 -0
  695. data/shoes/ruby/lib/yaml/encoding.rb +33 -0
  696. data/shoes/ruby/lib/yaml/error.rb +34 -0
  697. data/shoes/ruby/lib/yaml/loader.rb +14 -0
  698. data/shoes/ruby/lib/yaml/rubytypes.rb +446 -0
  699. data/shoes/ruby/lib/yaml/store.rb +43 -0
  700. data/shoes/ruby/lib/yaml/stream.rb +40 -0
  701. data/shoes/ruby/lib/yaml/stringio.rb +83 -0
  702. data/shoes/ruby/lib/yaml/syck.rb +19 -0
  703. data/shoes/ruby/lib/yaml/tag.rb +91 -0
  704. data/shoes/ruby/lib/yaml/types.rb +192 -0
  705. data/shoes/ruby/lib/yaml/yamlnode.rb +54 -0
  706. data/shoes/ruby/lib/yaml/ypath.rb +52 -0
  707. data/shoes/ruby/lib/yaml.rb +440 -0
  708. data/shoes/samples/class-book.rb +43 -0
  709. data/shoes/samples/class-book.yaml +387 -0
  710. data/shoes/samples/expert-definr.rb +23 -0
  711. data/shoes/samples/expert-funnies.rb +51 -0
  712. data/shoes/samples/expert-irb.rb +112 -0
  713. data/shoes/samples/expert-minesweeper.rb +267 -0
  714. data/shoes/samples/expert-othello.rb +319 -0
  715. data/shoes/samples/expert-pong.rb +62 -0
  716. data/shoes/samples/expert-tankspank.rb +385 -0
  717. data/shoes/samples/good-arc.rb +37 -0
  718. data/shoes/samples/good-clock.rb +51 -0
  719. data/shoes/samples/good-follow.rb +26 -0
  720. data/shoes/samples/good-reminder.rb +174 -0
  721. data/shoes/samples/good-vjot.rb +56 -0
  722. data/shoes/samples/simple-accordion.rb +75 -0
  723. data/shoes/samples/simple-anim-shapes.rb +17 -0
  724. data/shoes/samples/simple-anim-text.rb +13 -0
  725. data/shoes/samples/simple-arc.rb +23 -0
  726. data/shoes/samples/simple-bounce.rb +24 -0
  727. data/shoes/samples/simple-calc.rb +70 -0
  728. data/shoes/samples/simple-control-sizes.rb +24 -0
  729. data/shoes/samples/simple-curve.rb +26 -0
  730. data/shoes/samples/simple-dialogs.rb +29 -0
  731. data/shoes/samples/simple-downloader.rb +27 -0
  732. data/shoes/samples/simple-draw.rb +13 -0
  733. data/shoes/samples/simple-editor.rb +28 -0
  734. data/shoes/samples/simple-form.rb +28 -0
  735. data/shoes/samples/simple-form.shy +0 -0
  736. data/shoes/samples/simple-mask.rb +21 -0
  737. data/shoes/samples/simple-menu.rb +31 -0
  738. data/shoes/samples/simple-menu1.rb +35 -0
  739. data/shoes/samples/simple-rubygems.rb +29 -0
  740. data/shoes/samples/simple-slide.rb +45 -0
  741. data/shoes/samples/simple-sphere.rb +28 -0
  742. data/shoes/samples/simple-timer.rb +13 -0
  743. data/shoes/samples/simple-video.rb +13 -0
  744. data/shoes/shoes +21 -0
  745. data/shoes/shoes.exe +0 -0
  746. data/shoes/shoes.exe.manifest +17 -0
  747. data/shoes/sqlite3.dll +0 -0
  748. data/shoes/static/Shoes.icns +0 -0
  749. data/shoes/static/Thumbs.db +0 -0
  750. data/shoes/static/app-icon.png +0 -0
  751. data/shoes/static/avatar.png +0 -0
  752. data/shoes/static/code_highlighter.js +188 -0
  753. data/shoes/static/code_highlighter_ruby.js +26 -0
  754. data/shoes/static/icon-debug.png +0 -0
  755. data/shoes/static/icon-error.png +0 -0
  756. data/shoes/static/icon-info.png +0 -0
  757. data/shoes/static/icon-warn.png +0 -0
  758. data/shoes/static/listbox_button1.png +0 -0
  759. data/shoes/static/listbox_button2.png +0 -0
  760. data/shoes/static/man-app.png +0 -0
  761. data/shoes/static/man-builds.png +0 -0
  762. data/shoes/static/man-editor-notepad.png +0 -0
  763. data/shoes/static/man-editor-osx.png +0 -0
  764. data/shoes/static/man-ele-background.png +0 -0
  765. data/shoes/static/man-ele-border.png +0 -0
  766. data/shoes/static/man-ele-button.png +0 -0
  767. data/shoes/static/man-ele-check.png +0 -0
  768. data/shoes/static/man-ele-editbox.png +0 -0
  769. data/shoes/static/man-ele-editline.png +0 -0
  770. data/shoes/static/man-ele-image.png +0 -0
  771. data/shoes/static/man-ele-listbox.png +0 -0
  772. data/shoes/static/man-ele-progress.png +0 -0
  773. data/shoes/static/man-ele-radio.png +0 -0
  774. data/shoes/static/man-ele-shape.png +0 -0
  775. data/shoes/static/man-ele-textblock.png +0 -0
  776. data/shoes/static/man-ele-video.png +0 -0
  777. data/shoes/static/man-intro-dmg.png +0 -0
  778. data/shoes/static/man-intro-exe.png +0 -0
  779. data/shoes/static/man-look-tiger.png +0 -0
  780. data/shoes/static/man-look-ubuntu.png +0 -0
  781. data/shoes/static/man-look-vista.png +0 -0
  782. data/shoes/static/man-run-osx.png +0 -0
  783. data/shoes/static/man-run-vista.png +0 -0
  784. data/shoes/static/man-run-xp.png +0 -0
  785. data/shoes/static/man-shot1.png +0 -0
  786. data/shoes/static/manual-en.txt +2783 -0
  787. data/shoes/static/manual-ja.txt +2780 -0
  788. data/shoes/static/manual.css +167 -0
  789. data/shoes/static/menu-corner1.png +0 -0
  790. data/shoes/static/menu-corner2.png +0 -0
  791. data/shoes/static/menu-gray.png +0 -0
  792. data/shoes/static/menu-left.png +0 -0
  793. data/shoes/static/menu-right.png +0 -0
  794. data/shoes/static/menu-top.png +0 -0
  795. data/shoes/static/shoes-dmg.jpg +0 -0
  796. data/shoes/static/shoes-icon-blue.png +0 -0
  797. data/shoes/static/shoes-icon.png +0 -0
  798. data/shoes/static/shoes-manual-apps.gif +0 -0
  799. data/shoes/static/stripe.png +0 -0
  800. data/shoes/static/stubs/blank.exe +0 -0
  801. data/shoes/static/stubs/blank.hfz +0 -0
  802. data/shoes/static/stubs/blank.run +375 -0
  803. data/shoes/static/stubs/cocoa-install +0 -0
  804. data/shoes/static/stubs/sh-install +48 -0
  805. data/shoes/static/tutor-back.png +0 -0
  806. data/shoes/zlib.dll +0 -0
  807. data/shoes/zlib1.dll +0 -0
  808. metadata +887 -0
@@ -0,0 +1,3500 @@
1
+ #
2
+ # = net/imap.rb
3
+ #
4
+ # Copyright (C) 2000 Shugo Maeda <shugo@ruby-lang.org>
5
+ #
6
+ # This library is distributed under the terms of the Ruby license.
7
+ # You can freely distribute/modify this library.
8
+ #
9
+ # Documentation: Shugo Maeda, with RDoc conversion and overview by William
10
+ # Webber.
11
+ #
12
+ # See Net::IMAP for documentation.
13
+ #
14
+
15
+
16
+ require "socket"
17
+ require "monitor"
18
+ require "digest/md5"
19
+ require "strscan"
20
+ begin
21
+ require "openssl/ssl"
22
+ rescue LoadError
23
+ end
24
+
25
+ module Net
26
+
27
+ #
28
+ # Net::IMAP implements Internet Message Access Protocol (IMAP) client
29
+ # functionality. The protocol is described in [IMAP].
30
+ #
31
+ # == IMAP Overview
32
+ #
33
+ # An IMAP client connects to a server, and then authenticates
34
+ # itself using either #authenticate() or #login(). Having
35
+ # authenticated itself, there is a range of commands
36
+ # available to it. Most work with mailboxes, which may be
37
+ # arranged in an hierarchical namespace, and each of which
38
+ # contains zero or more messages. How this is implemented on
39
+ # the server is implementation-dependent; on a UNIX server, it
40
+ # will frequently be implemented as a files in mailbox format
41
+ # within a hierarchy of directories.
42
+ #
43
+ # To work on the messages within a mailbox, the client must
44
+ # first select that mailbox, using either #select() or (for
45
+ # read-only access) #examine(). Once the client has successfully
46
+ # selected a mailbox, they enter _selected_ state, and that
47
+ # mailbox becomes the _current_ mailbox, on which mail-item
48
+ # related commands implicitly operate.
49
+ #
50
+ # Messages have two sorts of identifiers: message sequence
51
+ # numbers, and UIDs.
52
+ #
53
+ # Message sequence numbers number messages within a mail box
54
+ # from 1 up to the number of items in the mail box. If new
55
+ # message arrives during a session, it receives a sequence
56
+ # number equal to the new size of the mail box. If messages
57
+ # are expunged from the mailbox, remaining messages have their
58
+ # sequence numbers "shuffled down" to fill the gaps.
59
+ #
60
+ # UIDs, on the other hand, are permanently guaranteed not to
61
+ # identify another message within the same mailbox, even if
62
+ # the existing message is deleted. UIDs are required to
63
+ # be assigned in ascending (but not necessarily sequential)
64
+ # order within a mailbox; this means that if a non-IMAP client
65
+ # rearranges the order of mailitems within a mailbox, the
66
+ # UIDs have to be reassigned. An IMAP client cannot thus
67
+ # rearrange message orders.
68
+ #
69
+ # == Examples of Usage
70
+ #
71
+ # === List sender and subject of all recent messages in the default mailbox
72
+ #
73
+ # imap = Net::IMAP.new('mail.example.com')
74
+ # imap.authenticate('LOGIN', 'joe_user', 'joes_password')
75
+ # imap.examine('INBOX')
76
+ # imap.search(["RECENT"]).each do |message_id|
77
+ # envelope = imap.fetch(message_id, "ENVELOPE")[0].attr["ENVELOPE"]
78
+ # puts "#{envelope.from[0].name}: \t#{envelope.subject}"
79
+ # end
80
+ #
81
+ # === Move all messages from April 2003 from "Mail/sent-mail" to "Mail/sent-apr03"
82
+ #
83
+ # imap = Net::IMAP.new('mail.example.com')
84
+ # imap.authenticate('LOGIN', 'joe_user', 'joes_password')
85
+ # imap.select('Mail/sent-mail')
86
+ # if not imap.list('Mail/', 'sent-apr03')
87
+ # imap.create('Mail/sent-apr03')
88
+ # end
89
+ # imap.search(["BEFORE", "30-Apr-2003", "SINCE", "1-Apr-2003"]).each do |message_id|
90
+ # imap.copy(message_id, "Mail/sent-apr03")
91
+ # imap.store(message_id, "+FLAGS", [:Deleted])
92
+ # end
93
+ # imap.expunge
94
+ #
95
+ # == Thread Safety
96
+ #
97
+ # Net::IMAP supports concurrent threads. For example,
98
+ #
99
+ # imap = Net::IMAP.new("imap.foo.net", "imap2")
100
+ # imap.authenticate("cram-md5", "bar", "password")
101
+ # imap.select("inbox")
102
+ # fetch_thread = Thread.start { imap.fetch(1..-1, "UID") }
103
+ # search_result = imap.search(["BODY", "hello"])
104
+ # fetch_result = fetch_thread.value
105
+ # imap.disconnect
106
+ #
107
+ # This script invokes the FETCH command and the SEARCH command concurrently.
108
+ #
109
+ # == Errors
110
+ #
111
+ # An IMAP server can send three different types of responses to indicate
112
+ # failure:
113
+ #
114
+ # NO:: the attempted command could not be successfully completed. For
115
+ # instance, the username/password used for logging in are incorrect;
116
+ # the selected mailbox does not exists; etc.
117
+ #
118
+ # BAD:: the request from the client does not follow the server's
119
+ # understanding of the IMAP protocol. This includes attempting
120
+ # commands from the wrong client state; for instance, attempting
121
+ # to perform a SEARCH command without having SELECTed a current
122
+ # mailbox. It can also signal an internal server
123
+ # failure (such as a disk crash) has occurred.
124
+ #
125
+ # BYE:: the server is saying goodbye. This can be part of a normal
126
+ # logout sequence, and can be used as part of a login sequence
127
+ # to indicate that the server is (for some reason) unwilling
128
+ # to accept our connection. As a response to any other command,
129
+ # it indicates either that the server is shutting down, or that
130
+ # the server is timing out the client connection due to inactivity.
131
+ #
132
+ # These three error response are represented by the errors
133
+ # Net::IMAP::NoResponseError, Net::IMAP::BadResponseError, and
134
+ # Net::IMAP::ByeResponseError, all of which are subclasses of
135
+ # Net::IMAP::ResponseError. Essentially, all methods that involve
136
+ # sending a request to the server can generate one of these errors.
137
+ # Only the most pertinent instances have been documented below.
138
+ #
139
+ # Because the IMAP class uses Sockets for communication, its methods
140
+ # are also susceptible to the various errors that can occur when
141
+ # working with sockets. These are generally represented as
142
+ # Errno errors. For instance, any method that involves sending a
143
+ # request to the server and/or receiving a response from it could
144
+ # raise an Errno::EPIPE error if the network connection unexpectedly
145
+ # goes down. See the socket(7), ip(7), tcp(7), socket(2), connect(2),
146
+ # and associated man pages.
147
+ #
148
+ # Finally, a Net::IMAP::DataFormatError is thrown if low-level data
149
+ # is found to be in an incorrect format (for instance, when converting
150
+ # between UTF-8 and UTF-16), and Net::IMAP::ResponseParseError is
151
+ # thrown if a server response is non-parseable.
152
+ #
153
+ #
154
+ # == References
155
+ #
156
+ # [[IMAP]]
157
+ # M. Crispin, "INTERNET MESSAGE ACCESS PROTOCOL - VERSION 4rev1",
158
+ # RFC 2060, December 1996. (Note: since obsoleted by RFC 3501)
159
+ #
160
+ # [[LANGUAGE-TAGS]]
161
+ # Alvestrand, H., "Tags for the Identification of
162
+ # Languages", RFC 1766, March 1995.
163
+ #
164
+ # [[MD5]]
165
+ # Myers, J., and M. Rose, "The Content-MD5 Header Field", RFC
166
+ # 1864, October 1995.
167
+ #
168
+ # [[MIME-IMB]]
169
+ # Freed, N., and N. Borenstein, "MIME (Multipurpose Internet
170
+ # Mail Extensions) Part One: Format of Internet Message Bodies", RFC
171
+ # 2045, November 1996.
172
+ #
173
+ # [[RFC-822]]
174
+ # Crocker, D., "Standard for the Format of ARPA Internet Text
175
+ # Messages", STD 11, RFC 822, University of Delaware, August 1982.
176
+ #
177
+ # [[RFC-2087]]
178
+ # Myers, J., "IMAP4 QUOTA extension", RFC 2087, January 1997.
179
+ #
180
+ # [[RFC-2086]]
181
+ # Myers, J., "IMAP4 ACL extension", RFC 2086, January 1997.
182
+ #
183
+ # [[RFC-2195]]
184
+ # Klensin, J., Catoe, R., and Krumviede, P., "IMAP/POP AUTHorize Extension
185
+ # for Simple Challenge/Response", RFC 2195, September 1997.
186
+ #
187
+ # [[SORT-THREAD-EXT]]
188
+ # Crispin, M., "INTERNET MESSAGE ACCESS PROTOCOL - SORT and THREAD
189
+ # Extensions", draft-ietf-imapext-sort, May 2003.
190
+ #
191
+ # [[OSSL]]
192
+ # http://www.openssl.org
193
+ #
194
+ # [[RSSL]]
195
+ # http://savannah.gnu.org/projects/rubypki
196
+ #
197
+ # [[UTF7]]
198
+ # Goldsmith, D. and Davis, M., "UTF-7: A Mail-Safe Transformation Format of
199
+ # Unicode", RFC 2152, May 1997.
200
+ #
201
+ class IMAP
202
+ include MonitorMixin
203
+ if defined?(OpenSSL)
204
+ include OpenSSL
205
+ include SSL
206
+ end
207
+
208
+ # Returns an initial greeting response from the server.
209
+ attr_reader :greeting
210
+
211
+ # Returns recorded untagged responses. For example:
212
+ #
213
+ # imap.select("inbox")
214
+ # p imap.responses["EXISTS"][-1]
215
+ # #=> 2
216
+ # p imap.responses["UIDVALIDITY"][-1]
217
+ # #=> 968263756
218
+ attr_reader :responses
219
+
220
+ # Returns all response handlers.
221
+ attr_reader :response_handlers
222
+
223
+ # The thread to receive exceptions.
224
+ attr_accessor :client_thread
225
+
226
+ # Flag indicating a message has been seen
227
+ SEEN = :Seen
228
+
229
+ # Flag indicating a message has been answered
230
+ ANSWERED = :Answered
231
+
232
+ # Flag indicating a message has been flagged for special or urgent
233
+ # attention
234
+ FLAGGED = :Flagged
235
+
236
+ # Flag indicating a message has been marked for deletion. This
237
+ # will occur when the mailbox is closed or expunged.
238
+ DELETED = :Deleted
239
+
240
+ # Flag indicating a message is only a draft or work-in-progress version.
241
+ DRAFT = :Draft
242
+
243
+ # Flag indicating that the message is "recent", meaning that this
244
+ # session is the first session in which the client has been notified
245
+ # of this message.
246
+ RECENT = :Recent
247
+
248
+ # Flag indicating that a mailbox context name cannot contain
249
+ # children.
250
+ NOINFERIORS = :Noinferiors
251
+
252
+ # Flag indicating that a mailbox is not selected.
253
+ NOSELECT = :Noselect
254
+
255
+ # Flag indicating that a mailbox has been marked "interesting" by
256
+ # the server; this commonly indicates that the mailbox contains
257
+ # new messages.
258
+ MARKED = :Marked
259
+
260
+ # Flag indicating that the mailbox does not contains new messages.
261
+ UNMARKED = :Unmarked
262
+
263
+ # Returns the debug mode.
264
+ def self.debug
265
+ return @@debug
266
+ end
267
+
268
+ # Sets the debug mode.
269
+ def self.debug=(val)
270
+ return @@debug = val
271
+ end
272
+
273
+ # Adds an authenticator for Net::IMAP#authenticate. +auth_type+
274
+ # is the type of authentication this authenticator supports
275
+ # (for instance, "LOGIN"). The +authenticator+ is an object
276
+ # which defines a process() method to handle authentication with
277
+ # the server. See Net::IMAP::LoginAuthenticator,
278
+ # Net::IMAP::CramMD5Authenticator, and Net::IMAP::DigestMD5Authenticator
279
+ # for examples.
280
+ #
281
+ #
282
+ # If +auth_type+ refers to an existing authenticator, it will be
283
+ # replaced by the new one.
284
+ def self.add_authenticator(auth_type, authenticator)
285
+ @@authenticators[auth_type] = authenticator
286
+ end
287
+
288
+ # Disconnects from the server.
289
+ def disconnect
290
+ begin
291
+ begin
292
+ # try to call SSL::SSLSocket#io.
293
+ @sock.io.shutdown
294
+ rescue NoMethodError
295
+ # @sock is not an SSL::SSLSocket.
296
+ @sock.shutdown
297
+ end
298
+ rescue Errno::ENOTCONN
299
+ # ignore `Errno::ENOTCONN: Socket is not connected' on some platforms.
300
+ end
301
+ @receiver_thread.join
302
+ @sock.close
303
+ end
304
+
305
+ # Returns true if disconnected from the server.
306
+ def disconnected?
307
+ return @sock.closed?
308
+ end
309
+
310
+ # Sends a CAPABILITY command, and returns an array of
311
+ # capabilities that the server supports. Each capability
312
+ # is a string. See [IMAP] for a list of possible
313
+ # capabilities.
314
+ #
315
+ # Note that the Net::IMAP class does not modify its
316
+ # behaviour according to the capabilities of the server;
317
+ # it is up to the user of the class to ensure that
318
+ # a certain capability is supported by a server before
319
+ # using it.
320
+ def capability
321
+ synchronize do
322
+ send_command("CAPABILITY")
323
+ return @responses.delete("CAPABILITY")[-1]
324
+ end
325
+ end
326
+
327
+ # Sends a NOOP command to the server. It does nothing.
328
+ def noop
329
+ send_command("NOOP")
330
+ end
331
+
332
+ # Sends a LOGOUT command to inform the server that the client is
333
+ # done with the connection.
334
+ def logout
335
+ send_command("LOGOUT")
336
+ end
337
+
338
+ # Sends a STARTTLS command to start TLS session.
339
+ def starttls(options = {}, verify = true)
340
+ send_command("STARTTLS") do |resp|
341
+ if resp.kind_of?(TaggedResponse) && resp.name == "OK"
342
+ begin
343
+ # for backward compatibility
344
+ certs = options.to_str
345
+ options = create_ssl_params(certs, verify)
346
+ rescue NoMethodError
347
+ end
348
+ start_tls_session(options)
349
+ end
350
+ end
351
+ end
352
+
353
+ # Sends an AUTHENTICATE command to authenticate the client.
354
+ # The +auth_type+ parameter is a string that represents
355
+ # the authentication mechanism to be used. Currently Net::IMAP
356
+ # supports authentication mechanisms:
357
+ #
358
+ # LOGIN:: login using cleartext user and password.
359
+ # CRAM-MD5:: login with cleartext user and encrypted password
360
+ # (see [RFC-2195] for a full description). This
361
+ # mechanism requires that the server have the user's
362
+ # password stored in clear-text password.
363
+ #
364
+ # For both these mechanisms, there should be two +args+: username
365
+ # and (cleartext) password. A server may not support one or other
366
+ # of these mechanisms; check #capability() for a capability of
367
+ # the form "AUTH=LOGIN" or "AUTH=CRAM-MD5".
368
+ #
369
+ # Authentication is done using the appropriate authenticator object:
370
+ # see @@authenticators for more information on plugging in your own
371
+ # authenticator.
372
+ #
373
+ # For example:
374
+ #
375
+ # imap.authenticate('LOGIN', user, password)
376
+ #
377
+ # A Net::IMAP::NoResponseError is raised if authentication fails.
378
+ def authenticate(auth_type, *args)
379
+ auth_type = auth_type.upcase
380
+ unless @@authenticators.has_key?(auth_type)
381
+ raise ArgumentError,
382
+ format('unknown auth type - "%s"', auth_type)
383
+ end
384
+ authenticator = @@authenticators[auth_type].new(*args)
385
+ send_command("AUTHENTICATE", auth_type) do |resp|
386
+ if resp.instance_of?(ContinuationRequest)
387
+ data = authenticator.process(resp.data.text.unpack("m")[0])
388
+ s = [data].pack("m").gsub(/\n/, "")
389
+ send_string_data(s)
390
+ put_string(CRLF)
391
+ end
392
+ end
393
+ end
394
+
395
+ # Sends a LOGIN command to identify the client and carries
396
+ # the plaintext +password+ authenticating this +user+. Note
397
+ # that, unlike calling #authenticate() with an +auth_type+
398
+ # of "LOGIN", #login() does *not* use the login authenticator.
399
+ #
400
+ # A Net::IMAP::NoResponseError is raised if authentication fails.
401
+ def login(user, password)
402
+ send_command("LOGIN", user, password)
403
+ end
404
+
405
+ # Sends a SELECT command to select a +mailbox+ so that messages
406
+ # in the +mailbox+ can be accessed.
407
+ #
408
+ # After you have selected a mailbox, you may retrieve the
409
+ # number of items in that mailbox from @responses["EXISTS"][-1],
410
+ # and the number of recent messages from @responses["RECENT"][-1].
411
+ # Note that these values can change if new messages arrive
412
+ # during a session; see #add_response_handler() for a way of
413
+ # detecting this event.
414
+ #
415
+ # A Net::IMAP::NoResponseError is raised if the mailbox does not
416
+ # exist or is for some reason non-selectable.
417
+ def select(mailbox)
418
+ synchronize do
419
+ @responses.clear
420
+ send_command("SELECT", mailbox)
421
+ end
422
+ end
423
+
424
+ # Sends a EXAMINE command to select a +mailbox+ so that messages
425
+ # in the +mailbox+ can be accessed. Behaves the same as #select(),
426
+ # except that the selected +mailbox+ is identified as read-only.
427
+ #
428
+ # A Net::IMAP::NoResponseError is raised if the mailbox does not
429
+ # exist or is for some reason non-examinable.
430
+ def examine(mailbox)
431
+ synchronize do
432
+ @responses.clear
433
+ send_command("EXAMINE", mailbox)
434
+ end
435
+ end
436
+
437
+ # Sends a CREATE command to create a new +mailbox+.
438
+ #
439
+ # A Net::IMAP::NoResponseError is raised if a mailbox with that name
440
+ # cannot be created.
441
+ def create(mailbox)
442
+ send_command("CREATE", mailbox)
443
+ end
444
+
445
+ # Sends a DELETE command to remove the +mailbox+.
446
+ #
447
+ # A Net::IMAP::NoResponseError is raised if a mailbox with that name
448
+ # cannot be deleted, either because it does not exist or because the
449
+ # client does not have permission to delete it.
450
+ def delete(mailbox)
451
+ send_command("DELETE", mailbox)
452
+ end
453
+
454
+ # Sends a RENAME command to change the name of the +mailbox+ to
455
+ # +newname+.
456
+ #
457
+ # A Net::IMAP::NoResponseError is raised if a mailbox with the
458
+ # name +mailbox+ cannot be renamed to +newname+ for whatever
459
+ # reason; for instance, because +mailbox+ does not exist, or
460
+ # because there is already a mailbox with the name +newname+.
461
+ def rename(mailbox, newname)
462
+ send_command("RENAME", mailbox, newname)
463
+ end
464
+
465
+ # Sends a SUBSCRIBE command to add the specified +mailbox+ name to
466
+ # the server's set of "active" or "subscribed" mailboxes as returned
467
+ # by #lsub().
468
+ #
469
+ # A Net::IMAP::NoResponseError is raised if +mailbox+ cannot be
470
+ # subscribed to, for instance because it does not exist.
471
+ def subscribe(mailbox)
472
+ send_command("SUBSCRIBE", mailbox)
473
+ end
474
+
475
+ # Sends a UNSUBSCRIBE command to remove the specified +mailbox+ name
476
+ # from the server's set of "active" or "subscribed" mailboxes.
477
+ #
478
+ # A Net::IMAP::NoResponseError is raised if +mailbox+ cannot be
479
+ # unsubscribed from, for instance because the client is not currently
480
+ # subscribed to it.
481
+ def unsubscribe(mailbox)
482
+ send_command("UNSUBSCRIBE", mailbox)
483
+ end
484
+
485
+ # Sends a LIST command, and returns a subset of names from
486
+ # the complete set of all names available to the client.
487
+ # +refname+ provides a context (for instance, a base directory
488
+ # in a directory-based mailbox hierarchy). +mailbox+ specifies
489
+ # a mailbox or (via wildcards) mailboxes under that context.
490
+ # Two wildcards may be used in +mailbox+: '*', which matches
491
+ # all characters *including* the hierarchy delimiter (for instance,
492
+ # '/' on a UNIX-hosted directory-based mailbox hierarchy); and '%',
493
+ # which matches all characters *except* the hierarchy delimiter.
494
+ #
495
+ # If +refname+ is empty, +mailbox+ is used directly to determine
496
+ # which mailboxes to match. If +mailbox+ is empty, the root
497
+ # name of +refname+ and the hierarchy delimiter are returned.
498
+ #
499
+ # The return value is an array of +Net::IMAP::MailboxList+. For example:
500
+ #
501
+ # imap.create("foo/bar")
502
+ # imap.create("foo/baz")
503
+ # p imap.list("", "foo/%")
504
+ # #=> [#<Net::IMAP::MailboxList attr=[:Noselect], delim="/", name="foo/">, \\
505
+ # #<Net::IMAP::MailboxList attr=[:Noinferiors, :Marked], delim="/", name="foo/bar">, \\
506
+ # #<Net::IMAP::MailboxList attr=[:Noinferiors], delim="/", name="foo/baz">]
507
+ def list(refname, mailbox)
508
+ synchronize do
509
+ send_command("LIST", refname, mailbox)
510
+ return @responses.delete("LIST")
511
+ end
512
+ end
513
+
514
+ # Sends the GETQUOTAROOT command along with specified +mailbox+.
515
+ # This command is generally available to both admin and user.
516
+ # If mailbox exists, returns an array containing objects of
517
+ # Net::IMAP::MailboxQuotaRoot and Net::IMAP::MailboxQuota.
518
+ def getquotaroot(mailbox)
519
+ synchronize do
520
+ send_command("GETQUOTAROOT", mailbox)
521
+ result = []
522
+ result.concat(@responses.delete("QUOTAROOT"))
523
+ result.concat(@responses.delete("QUOTA"))
524
+ return result
525
+ end
526
+ end
527
+
528
+ # Sends the GETQUOTA command along with specified +mailbox+.
529
+ # If this mailbox exists, then an array containing a
530
+ # Net::IMAP::MailboxQuota object is returned. This
531
+ # command generally is only available to server admin.
532
+ def getquota(mailbox)
533
+ synchronize do
534
+ send_command("GETQUOTA", mailbox)
535
+ return @responses.delete("QUOTA")
536
+ end
537
+ end
538
+
539
+ # Sends a SETQUOTA command along with the specified +mailbox+ and
540
+ # +quota+. If +quota+ is nil, then quota will be unset for that
541
+ # mailbox. Typically one needs to be logged in as server admin
542
+ # for this to work. The IMAP quota commands are described in
543
+ # [RFC-2087].
544
+ def setquota(mailbox, quota)
545
+ if quota.nil?
546
+ data = '()'
547
+ else
548
+ data = '(STORAGE ' + quota.to_s + ')'
549
+ end
550
+ send_command("SETQUOTA", mailbox, RawData.new(data))
551
+ end
552
+
553
+ # Sends the SETACL command along with +mailbox+, +user+ and the
554
+ # +rights+ that user is to have on that mailbox. If +rights+ is nil,
555
+ # then that user will be stripped of any rights to that mailbox.
556
+ # The IMAP ACL commands are described in [RFC-2086].
557
+ def setacl(mailbox, user, rights)
558
+ if rights.nil?
559
+ send_command("SETACL", mailbox, user, "")
560
+ else
561
+ send_command("SETACL", mailbox, user, rights)
562
+ end
563
+ end
564
+
565
+ # Send the GETACL command along with specified +mailbox+.
566
+ # If this mailbox exists, an array containing objects of
567
+ # Net::IMAP::MailboxACLItem will be returned.
568
+ def getacl(mailbox)
569
+ synchronize do
570
+ send_command("GETACL", mailbox)
571
+ return @responses.delete("ACL")[-1]
572
+ end
573
+ end
574
+
575
+ # Sends a LSUB command, and returns a subset of names from the set
576
+ # of names that the user has declared as being "active" or
577
+ # "subscribed". +refname+ and +mailbox+ are interpreted as
578
+ # for #list().
579
+ # The return value is an array of +Net::IMAP::MailboxList+.
580
+ def lsub(refname, mailbox)
581
+ synchronize do
582
+ send_command("LSUB", refname, mailbox)
583
+ return @responses.delete("LSUB")
584
+ end
585
+ end
586
+
587
+ # Sends a STATUS command, and returns the status of the indicated
588
+ # +mailbox+. +attr+ is a list of one or more attributes that
589
+ # we are request the status of. Supported attributes include:
590
+ #
591
+ # MESSAGES:: the number of messages in the mailbox.
592
+ # RECENT:: the number of recent messages in the mailbox.
593
+ # UNSEEN:: the number of unseen messages in the mailbox.
594
+ #
595
+ # The return value is a hash of attributes. For example:
596
+ #
597
+ # p imap.status("inbox", ["MESSAGES", "RECENT"])
598
+ # #=> {"RECENT"=>0, "MESSAGES"=>44}
599
+ #
600
+ # A Net::IMAP::NoResponseError is raised if status values
601
+ # for +mailbox+ cannot be returned, for instance because it
602
+ # does not exist.
603
+ def status(mailbox, attr)
604
+ synchronize do
605
+ send_command("STATUS", mailbox, attr)
606
+ return @responses.delete("STATUS")[-1].attr
607
+ end
608
+ end
609
+
610
+ # Sends a APPEND command to append the +message+ to the end of
611
+ # the +mailbox+. The optional +flags+ argument is an array of
612
+ # flags to initially passing to the new message. The optional
613
+ # +date_time+ argument specifies the creation time to assign to the
614
+ # new message; it defaults to the current time.
615
+ # For example:
616
+ #
617
+ # imap.append("inbox", <<EOF.gsub(/\n/, "\r\n"), [:Seen], Time.now)
618
+ # Subject: hello
619
+ # From: shugo@ruby-lang.org
620
+ # To: shugo@ruby-lang.org
621
+ #
622
+ # hello world
623
+ # EOF
624
+ #
625
+ # A Net::IMAP::NoResponseError is raised if the mailbox does
626
+ # not exist (it is not created automatically), or if the flags,
627
+ # date_time, or message arguments contain errors.
628
+ def append(mailbox, message, flags = nil, date_time = nil)
629
+ args = []
630
+ if flags
631
+ args.push(flags)
632
+ end
633
+ args.push(date_time) if date_time
634
+ args.push(Literal.new(message))
635
+ send_command("APPEND", mailbox, *args)
636
+ end
637
+
638
+ # Sends a CHECK command to request a checkpoint of the currently
639
+ # selected mailbox. This performs implementation-specific
640
+ # housekeeping, for instance, reconciling the mailbox's
641
+ # in-memory and on-disk state.
642
+ def check
643
+ send_command("CHECK")
644
+ end
645
+
646
+ # Sends a CLOSE command to close the currently selected mailbox.
647
+ # The CLOSE command permanently removes from the mailbox all
648
+ # messages that have the \Deleted flag set.
649
+ def close
650
+ send_command("CLOSE")
651
+ end
652
+
653
+ # Sends a EXPUNGE command to permanently remove from the currently
654
+ # selected mailbox all messages that have the \Deleted flag set.
655
+ def expunge
656
+ synchronize do
657
+ send_command("EXPUNGE")
658
+ return @responses.delete("EXPUNGE")
659
+ end
660
+ end
661
+
662
+ # Sends a SEARCH command to search the mailbox for messages that
663
+ # match the given searching criteria, and returns message sequence
664
+ # numbers. +keys+ can either be a string holding the entire
665
+ # search string, or a single-dimension array of search keywords and
666
+ # arguments. The following are some common search criteria;
667
+ # see [IMAP] section 6.4.4 for a full list.
668
+ #
669
+ # <message set>:: a set of message sequence numbers. ',' indicates
670
+ # an interval, ':' indicates a range. For instance,
671
+ # '2,10:12,15' means "2,10,11,12,15".
672
+ #
673
+ # BEFORE <date>:: messages with an internal date strictly before
674
+ # <date>. The date argument has a format similar
675
+ # to 8-Aug-2002.
676
+ #
677
+ # BODY <string>:: messages that contain <string> within their body.
678
+ #
679
+ # CC <string>:: messages containing <string> in their CC field.
680
+ #
681
+ # FROM <string>:: messages that contain <string> in their FROM field.
682
+ #
683
+ # NEW:: messages with the \Recent, but not the \Seen, flag set.
684
+ #
685
+ # NOT <search-key>:: negate the following search key.
686
+ #
687
+ # OR <search-key> <search-key>:: "or" two search keys together.
688
+ #
689
+ # ON <date>:: messages with an internal date exactly equal to <date>,
690
+ # which has a format similar to 8-Aug-2002.
691
+ #
692
+ # SINCE <date>:: messages with an internal date on or after <date>.
693
+ #
694
+ # SUBJECT <string>:: messages with <string> in their subject.
695
+ #
696
+ # TO <string>:: messages with <string> in their TO field.
697
+ #
698
+ # For example:
699
+ #
700
+ # p imap.search(["SUBJECT", "hello", "NOT", "NEW"])
701
+ # #=> [1, 6, 7, 8]
702
+ def search(keys, charset = nil)
703
+ return search_internal("SEARCH", keys, charset)
704
+ end
705
+
706
+ # As for #search(), but returns unique identifiers.
707
+ def uid_search(keys, charset = nil)
708
+ return search_internal("UID SEARCH", keys, charset)
709
+ end
710
+
711
+ # Sends a FETCH command to retrieve data associated with a message
712
+ # in the mailbox. The +set+ parameter is a number or an array of
713
+ # numbers or a Range object. The number is a message sequence
714
+ # number. +attr+ is a list of attributes to fetch; see the
715
+ # documentation for Net::IMAP::FetchData for a list of valid
716
+ # attributes.
717
+ # The return value is an array of Net::IMAP::FetchData. For example:
718
+ #
719
+ # p imap.fetch(6..8, "UID")
720
+ # #=> [#<Net::IMAP::FetchData seqno=6, attr={"UID"=>98}>, \\
721
+ # #<Net::IMAP::FetchData seqno=7, attr={"UID"=>99}>, \\
722
+ # #<Net::IMAP::FetchData seqno=8, attr={"UID"=>100}>]
723
+ # p imap.fetch(6, "BODY[HEADER.FIELDS (SUBJECT)]")
724
+ # #=> [#<Net::IMAP::FetchData seqno=6, attr={"BODY[HEADER.FIELDS (SUBJECT)]"=>"Subject: test\r\n\r\n"}>]
725
+ # data = imap.uid_fetch(98, ["RFC822.SIZE", "INTERNALDATE"])[0]
726
+ # p data.seqno
727
+ # #=> 6
728
+ # p data.attr["RFC822.SIZE"]
729
+ # #=> 611
730
+ # p data.attr["INTERNALDATE"]
731
+ # #=> "12-Oct-2000 22:40:59 +0900"
732
+ # p data.attr["UID"]
733
+ # #=> 98
734
+ def fetch(set, attr)
735
+ return fetch_internal("FETCH", set, attr)
736
+ end
737
+
738
+ # As for #fetch(), but +set+ contains unique identifiers.
739
+ def uid_fetch(set, attr)
740
+ return fetch_internal("UID FETCH", set, attr)
741
+ end
742
+
743
+ # Sends a STORE command to alter data associated with messages
744
+ # in the mailbox, in particular their flags. The +set+ parameter
745
+ # is a number or an array of numbers or a Range object. Each number
746
+ # is a message sequence number. +attr+ is the name of a data item
747
+ # to store: 'FLAGS' means to replace the message's flag list
748
+ # with the provided one; '+FLAGS' means to add the provided flags;
749
+ # and '-FLAGS' means to remove them. +flags+ is a list of flags.
750
+ #
751
+ # The return value is an array of Net::IMAP::FetchData. For example:
752
+ #
753
+ # p imap.store(6..8, "+FLAGS", [:Deleted])
754
+ # #=> [#<Net::IMAP::FetchData seqno=6, attr={"FLAGS"=>[:Seen, :Deleted]}>, \\
755
+ # #<Net::IMAP::FetchData seqno=7, attr={"FLAGS"=>[:Seen, :Deleted]}>, \\
756
+ # #<Net::IMAP::FetchData seqno=8, attr={"FLAGS"=>[:Seen, :Deleted]}>]
757
+ def store(set, attr, flags)
758
+ return store_internal("STORE", set, attr, flags)
759
+ end
760
+
761
+ # As for #store(), but +set+ contains unique identifiers.
762
+ def uid_store(set, attr, flags)
763
+ return store_internal("UID STORE", set, attr, flags)
764
+ end
765
+
766
+ # Sends a COPY command to copy the specified message(s) to the end
767
+ # of the specified destination +mailbox+. The +set+ parameter is
768
+ # a number or an array of numbers or a Range object. The number is
769
+ # a message sequence number.
770
+ def copy(set, mailbox)
771
+ copy_internal("COPY", set, mailbox)
772
+ end
773
+
774
+ # As for #copy(), but +set+ contains unique identifiers.
775
+ def uid_copy(set, mailbox)
776
+ copy_internal("UID COPY", set, mailbox)
777
+ end
778
+
779
+ # Sends a SORT command to sort messages in the mailbox.
780
+ # Returns an array of message sequence numbers. For example:
781
+ #
782
+ # p imap.sort(["FROM"], ["ALL"], "US-ASCII")
783
+ # #=> [1, 2, 3, 5, 6, 7, 8, 4, 9]
784
+ # p imap.sort(["DATE"], ["SUBJECT", "hello"], "US-ASCII")
785
+ # #=> [6, 7, 8, 1]
786
+ #
787
+ # See [SORT-THREAD-EXT] for more details.
788
+ def sort(sort_keys, search_keys, charset)
789
+ return sort_internal("SORT", sort_keys, search_keys, charset)
790
+ end
791
+
792
+ # As for #sort(), but returns an array of unique identifiers.
793
+ def uid_sort(sort_keys, search_keys, charset)
794
+ return sort_internal("UID SORT", sort_keys, search_keys, charset)
795
+ end
796
+
797
+ # Adds a response handler. For example, to detect when
798
+ # the server sends us a new EXISTS response (which normally
799
+ # indicates new messages being added to the mail box),
800
+ # you could add the following handler after selecting the
801
+ # mailbox.
802
+ #
803
+ # imap.add_response_handler { |resp|
804
+ # if resp.kind_of?(Net::IMAP::UntaggedResponse) and resp.name == "EXISTS"
805
+ # puts "Mailbox now has #{resp.data} messages"
806
+ # end
807
+ # }
808
+ #
809
+ def add_response_handler(handler = Proc.new)
810
+ @response_handlers.push(handler)
811
+ end
812
+
813
+ # Removes the response handler.
814
+ def remove_response_handler(handler)
815
+ @response_handlers.delete(handler)
816
+ end
817
+
818
+ # As for #search(), but returns message sequence numbers in threaded
819
+ # format, as a Net::IMAP::ThreadMember tree. The supported algorithms
820
+ # are:
821
+ #
822
+ # ORDEREDSUBJECT:: split into single-level threads according to subject,
823
+ # ordered by date.
824
+ # REFERENCES:: split into threads by parent/child relationships determined
825
+ # by which message is a reply to which.
826
+ #
827
+ # Unlike #search(), +charset+ is a required argument. US-ASCII
828
+ # and UTF-8 are sample values.
829
+ #
830
+ # See [SORT-THREAD-EXT] for more details.
831
+ def thread(algorithm, search_keys, charset)
832
+ return thread_internal("THREAD", algorithm, search_keys, charset)
833
+ end
834
+
835
+ # As for #thread(), but returns unique identifiers instead of
836
+ # message sequence numbers.
837
+ def uid_thread(algorithm, search_keys, charset)
838
+ return thread_internal("UID THREAD", algorithm, search_keys, charset)
839
+ end
840
+
841
+ # Decode a string from modified UTF-7 format to UTF-8.
842
+ #
843
+ # UTF-7 is a 7-bit encoding of Unicode [UTF7]. IMAP uses a
844
+ # slightly modified version of this to encode mailbox names
845
+ # containing non-ASCII characters; see [IMAP] section 5.1.3.
846
+ #
847
+ # Net::IMAP does _not_ automatically encode and decode
848
+ # mailbox names to and from utf7.
849
+ def self.decode_utf7(s)
850
+ return s.gsub(/&(.*?)-/n) {
851
+ if $1.empty?
852
+ "&"
853
+ else
854
+ base64 = $1.tr(",", "/")
855
+ x = base64.length % 4
856
+ if x > 0
857
+ base64.concat("=" * (4 - x))
858
+ end
859
+ base64.unpack("m")[0].unpack("n*").pack("U*")
860
+ end
861
+ }.force_encoding("UTF-8")
862
+ end
863
+
864
+ # Encode a string from UTF-8 format to modified UTF-7.
865
+ def self.encode_utf7(s)
866
+ return s.gsub(/(&)|([^\x20-\x25\x27-\x7e]+)/u) {
867
+ if $1
868
+ "&-"
869
+ else
870
+ base64 = [$&.unpack("U*").pack("n*")].pack("m")
871
+ "&" + base64.delete("=\n").tr("/", ",") + "-"
872
+ end
873
+ }.force_encoding("ASCII-8BIT")
874
+ end
875
+
876
+ private
877
+
878
+ CRLF = "\r\n" # :nodoc:
879
+ PORT = 143 # :nodoc:
880
+ SSL_PORT = 993 # :nodoc:
881
+
882
+ @@debug = false
883
+ @@authenticators = {}
884
+
885
+ # call-seq:
886
+ # Net::IMAP.new(host, options = {})
887
+ #
888
+ # Creates a new Net::IMAP object and connects it to the specified
889
+ # +host+.
890
+ #
891
+ # +options+ is an option hash, each key of which is a symbol.
892
+ #
893
+ # The available options are:
894
+ #
895
+ # port:: port number (default value is 143 for imap, or 993 for imaps)
896
+ # ssl:: if options[:ssl] is true, then an attempt will be made
897
+ # to use SSL (now TLS) to connect to the server. For this to work
898
+ # OpenSSL [OSSL] and the Ruby OpenSSL [RSSL] extensions need to
899
+ # be installed.
900
+ # if options[:ssl] is a hash, it's passed to
901
+ # OpenSSL::SSL::SSLContext#set_params as parameters.
902
+ #
903
+ # The most common errors are:
904
+ #
905
+ # Errno::ECONNREFUSED:: connection refused by +host+ or an intervening
906
+ # firewall.
907
+ # Errno::ETIMEDOUT:: connection timed out (possibly due to packets
908
+ # being dropped by an intervening firewall).
909
+ # Errno::ENETUNREACH:: there is no route to that network.
910
+ # SocketError:: hostname not known or other socket error.
911
+ # Net::IMAP::ByeResponseError:: we connected to the host, but they
912
+ # immediately said goodbye to us.
913
+ def initialize(host, port_or_options = {},
914
+ usessl = false, certs = nil, verify = true)
915
+ super()
916
+ @host = host
917
+ begin
918
+ options = port_or_options.to_hash
919
+ rescue NoMethodError
920
+ # for backward compatibility
921
+ options = {}
922
+ options[:port] = port_or_options
923
+ if usessl
924
+ options[:ssl] = create_ssl_params(certs, verify)
925
+ end
926
+ end
927
+ @port = options[:port] || (options[:ssl] ? SSL_PORT : PORT)
928
+ @tag_prefix = "RUBY"
929
+ @tagno = 0
930
+ @parser = ResponseParser.new
931
+ @sock = TCPSocket.open(@host, @port)
932
+ if options[:ssl]
933
+ start_tls_session(options[:ssl])
934
+ @usessl = true
935
+ else
936
+ @usessl = false
937
+ end
938
+ @responses = Hash.new([].freeze)
939
+ @tagged_responses = {}
940
+ @response_handlers = []
941
+ @tagged_response_arrival = new_cond
942
+ @continuation_request_arrival = new_cond
943
+ @logout_command_tag = nil
944
+ @debug_output_bol = true
945
+ @exception = nil
946
+
947
+ @greeting = get_response
948
+ if @greeting.name == "BYE"
949
+ @sock.close
950
+ raise ByeResponseError, @greeting.raw_data
951
+ end
952
+
953
+ @client_thread = Thread.current
954
+ @receiver_thread = Thread.start {
955
+ receive_responses
956
+ }
957
+ end
958
+
959
+ def receive_responses
960
+ while true
961
+ synchronize do
962
+ @exception = nil
963
+ end
964
+ begin
965
+ resp = get_response
966
+ rescue Exception => e
967
+ synchronize do
968
+ @sock.close
969
+ @exception = e
970
+ end
971
+ break
972
+ end
973
+ unless resp
974
+ synchronize do
975
+ @exception = EOFError.new("end of file reached")
976
+ end
977
+ break
978
+ end
979
+ begin
980
+ synchronize do
981
+ case resp
982
+ when TaggedResponse
983
+ @tagged_responses[resp.tag] = resp
984
+ @tagged_response_arrival.broadcast
985
+ if resp.tag == @logout_command_tag
986
+ return
987
+ end
988
+ when UntaggedResponse
989
+ record_response(resp.name, resp.data)
990
+ if resp.data.instance_of?(ResponseText) &&
991
+ (code = resp.data.code)
992
+ record_response(code.name, code.data)
993
+ end
994
+ if resp.name == "BYE" && @logout_command_tag.nil?
995
+ @sock.close
996
+ @exception = ByeResponseError.new(resp.raw_data)
997
+ break
998
+ end
999
+ when ContinuationRequest
1000
+ @continuation_request_arrival.signal
1001
+ end
1002
+ @response_handlers.each do |handler|
1003
+ handler.call(resp)
1004
+ end
1005
+ end
1006
+ rescue Exception => e
1007
+ @exception = e
1008
+ synchronize do
1009
+ @tagged_response_arrival.broadcast
1010
+ @continuation_request_arrival.broadcast
1011
+ end
1012
+ end
1013
+ end
1014
+ synchronize do
1015
+ @tagged_response_arrival.broadcast
1016
+ @continuation_request_arrival.broadcast
1017
+ end
1018
+ end
1019
+
1020
+ def get_tagged_response(tag, cmd)
1021
+ until @tagged_responses.key?(tag)
1022
+ raise @exception if @exception
1023
+ @tagged_response_arrival.wait
1024
+ end
1025
+ resp = @tagged_responses.delete(tag)
1026
+ case resp.name
1027
+ when /\A(?:NO)\z/ni
1028
+ raise NoResponseError, resp.data.text
1029
+ when /\A(?:BAD)\z/ni
1030
+ raise BadResponseError, resp.data.text
1031
+ else
1032
+ return resp
1033
+ end
1034
+ end
1035
+
1036
+ def get_response
1037
+ buff = ""
1038
+ while true
1039
+ s = @sock.gets(CRLF)
1040
+ break unless s
1041
+ buff.concat(s)
1042
+ if /\{(\d+)\}\r\n/n =~ s
1043
+ s = @sock.read($1.to_i)
1044
+ buff.concat(s)
1045
+ else
1046
+ break
1047
+ end
1048
+ end
1049
+ return nil if buff.length == 0
1050
+ if @@debug
1051
+ $stderr.print(buff.gsub(/^/n, "S: "))
1052
+ end
1053
+ return @parser.parse(buff)
1054
+ end
1055
+
1056
+ def record_response(name, data)
1057
+ unless @responses.has_key?(name)
1058
+ @responses[name] = []
1059
+ end
1060
+ @responses[name].push(data)
1061
+ end
1062
+
1063
+ def send_command(cmd, *args, &block)
1064
+ synchronize do
1065
+ args.each do |i|
1066
+ validate_data(i)
1067
+ end
1068
+ tag = generate_tag
1069
+ put_string(tag + " " + cmd)
1070
+ args.each do |i|
1071
+ put_string(" ")
1072
+ send_data(i)
1073
+ end
1074
+ put_string(CRLF)
1075
+ if cmd == "LOGOUT"
1076
+ @logout_command_tag = tag
1077
+ end
1078
+ if block
1079
+ add_response_handler(block)
1080
+ end
1081
+ begin
1082
+ return get_tagged_response(tag, cmd)
1083
+ ensure
1084
+ if block
1085
+ remove_response_handler(block)
1086
+ end
1087
+ end
1088
+ end
1089
+ end
1090
+
1091
+ def generate_tag
1092
+ @tagno += 1
1093
+ return format("%s%04d", @tag_prefix, @tagno)
1094
+ end
1095
+
1096
+ def put_string(str)
1097
+ @sock.print(str)
1098
+ if @@debug
1099
+ if @debug_output_bol
1100
+ $stderr.print("C: ")
1101
+ end
1102
+ $stderr.print(str.gsub(/\n(?!\z)/n, "\nC: "))
1103
+ if /\r\n\z/n.match(str)
1104
+ @debug_output_bol = true
1105
+ else
1106
+ @debug_output_bol = false
1107
+ end
1108
+ end
1109
+ end
1110
+
1111
+ def validate_data(data)
1112
+ case data
1113
+ when nil
1114
+ when String
1115
+ when Integer
1116
+ if data < 0 || data >= 4294967296
1117
+ raise DataFormatError, num.to_s
1118
+ end
1119
+ when Array
1120
+ data.each do |i|
1121
+ validate_data(i)
1122
+ end
1123
+ when Time
1124
+ when Symbol
1125
+ else
1126
+ data.validate
1127
+ end
1128
+ end
1129
+
1130
+ def send_data(data)
1131
+ case data
1132
+ when nil
1133
+ put_string("NIL")
1134
+ when String
1135
+ send_string_data(data)
1136
+ when Integer
1137
+ send_number_data(data)
1138
+ when Array
1139
+ send_list_data(data)
1140
+ when Time
1141
+ send_time_data(data)
1142
+ when Symbol
1143
+ send_symbol_data(data)
1144
+ else
1145
+ data.send_data(self)
1146
+ end
1147
+ end
1148
+
1149
+ def send_string_data(str)
1150
+ case str
1151
+ when ""
1152
+ put_string('""')
1153
+ when /[\x80-\xff\r\n]/n
1154
+ # literal
1155
+ send_literal(str)
1156
+ when /[(){ \x00-\x1f\x7f%*"\\]/n
1157
+ # quoted string
1158
+ send_quoted_string(str)
1159
+ else
1160
+ put_string(str)
1161
+ end
1162
+ end
1163
+
1164
+ def send_quoted_string(str)
1165
+ put_string('"' + str.gsub(/["\\]/n, "\\\\\\&") + '"')
1166
+ end
1167
+
1168
+ def send_literal(str)
1169
+ put_string("{" + str.length.to_s + "}" + CRLF)
1170
+ @continuation_request_arrival.wait
1171
+ raise @exception if @exception
1172
+ put_string(str)
1173
+ end
1174
+
1175
+ def send_number_data(num)
1176
+ put_string(num.to_s)
1177
+ end
1178
+
1179
+ def send_list_data(list)
1180
+ put_string("(")
1181
+ first = true
1182
+ list.each do |i|
1183
+ if first
1184
+ first = false
1185
+ else
1186
+ put_string(" ")
1187
+ end
1188
+ send_data(i)
1189
+ end
1190
+ put_string(")")
1191
+ end
1192
+
1193
+ DATE_MONTH = %w(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)
1194
+
1195
+ def send_time_data(time)
1196
+ t = time.dup.gmtime
1197
+ s = format('"%2d-%3s-%4d %02d:%02d:%02d +0000"',
1198
+ t.day, DATE_MONTH[t.month - 1], t.year,
1199
+ t.hour, t.min, t.sec)
1200
+ put_string(s)
1201
+ end
1202
+
1203
+ def send_symbol_data(symbol)
1204
+ put_string("\\" + symbol.to_s)
1205
+ end
1206
+
1207
+ def search_internal(cmd, keys, charset)
1208
+ if keys.instance_of?(String)
1209
+ keys = [RawData.new(keys)]
1210
+ else
1211
+ normalize_searching_criteria(keys)
1212
+ end
1213
+ synchronize do
1214
+ if charset
1215
+ send_command(cmd, "CHARSET", charset, *keys)
1216
+ else
1217
+ send_command(cmd, *keys)
1218
+ end
1219
+ return @responses.delete("SEARCH")[-1]
1220
+ end
1221
+ end
1222
+
1223
+ def fetch_internal(cmd, set, attr)
1224
+ if attr.instance_of?(String)
1225
+ attr = RawData.new(attr)
1226
+ end
1227
+ synchronize do
1228
+ @responses.delete("FETCH")
1229
+ send_command(cmd, MessageSet.new(set), attr)
1230
+ return @responses.delete("FETCH")
1231
+ end
1232
+ end
1233
+
1234
+ def store_internal(cmd, set, attr, flags)
1235
+ if attr.instance_of?(String)
1236
+ attr = RawData.new(attr)
1237
+ end
1238
+ synchronize do
1239
+ @responses.delete("FETCH")
1240
+ send_command(cmd, MessageSet.new(set), attr, flags)
1241
+ return @responses.delete("FETCH")
1242
+ end
1243
+ end
1244
+
1245
+ def copy_internal(cmd, set, mailbox)
1246
+ send_command(cmd, MessageSet.new(set), mailbox)
1247
+ end
1248
+
1249
+ def sort_internal(cmd, sort_keys, search_keys, charset)
1250
+ if search_keys.instance_of?(String)
1251
+ search_keys = [RawData.new(search_keys)]
1252
+ else
1253
+ normalize_searching_criteria(search_keys)
1254
+ end
1255
+ normalize_searching_criteria(search_keys)
1256
+ synchronize do
1257
+ send_command(cmd, sort_keys, charset, *search_keys)
1258
+ return @responses.delete("SORT")[-1]
1259
+ end
1260
+ end
1261
+
1262
+ def thread_internal(cmd, algorithm, search_keys, charset)
1263
+ if search_keys.instance_of?(String)
1264
+ search_keys = [RawData.new(search_keys)]
1265
+ else
1266
+ normalize_searching_criteria(search_keys)
1267
+ end
1268
+ normalize_searching_criteria(search_keys)
1269
+ send_command(cmd, algorithm, charset, *search_keys)
1270
+ return @responses.delete("THREAD")[-1]
1271
+ end
1272
+
1273
+ def normalize_searching_criteria(keys)
1274
+ keys.collect! do |i|
1275
+ case i
1276
+ when -1, Range, Array
1277
+ MessageSet.new(i)
1278
+ else
1279
+ i
1280
+ end
1281
+ end
1282
+ end
1283
+
1284
+ def create_ssl_params(certs = nil, verify = true)
1285
+ params = {}
1286
+ if certs
1287
+ if File.file?(certs)
1288
+ params[:ca_file] = certs
1289
+ elsif File.directory?(certs)
1290
+ params[:ca_path] = certs
1291
+ end
1292
+ end
1293
+ if verify
1294
+ params[:verify_mode] = VERIFY_PEER
1295
+ else
1296
+ params[:verify_mode] = VERIFY_NONE
1297
+ end
1298
+ return params
1299
+ end
1300
+
1301
+ def start_tls_session(params = {})
1302
+ unless defined?(OpenSSL)
1303
+ raise "SSL extension not installed"
1304
+ end
1305
+ if @sock.kind_of?(OpenSSL::SSL::SSLSocket)
1306
+ raise RuntimeError, "already using SSL"
1307
+ end
1308
+ begin
1309
+ params = params.to_hash
1310
+ rescue NoMethodError
1311
+ params = {}
1312
+ end
1313
+ context = SSLContext.new
1314
+ context.set_params(params)
1315
+ if defined?(VerifyCallbackProc)
1316
+ context.verify_callback = VerifyCallbackProc
1317
+ end
1318
+ @sock = SSLSocket.new(@sock, context)
1319
+ @sock.sync_close = true
1320
+ @sock.connect
1321
+ if context.verify_mode != VERIFY_NONE
1322
+ @sock.post_connection_check(@host)
1323
+ end
1324
+ end
1325
+
1326
+ class RawData # :nodoc:
1327
+ def send_data(imap)
1328
+ imap.send(:put_string, @data)
1329
+ end
1330
+
1331
+ def validate
1332
+ end
1333
+
1334
+ private
1335
+
1336
+ def initialize(data)
1337
+ @data = data
1338
+ end
1339
+ end
1340
+
1341
+ class Atom # :nodoc:
1342
+ def send_data(imap)
1343
+ imap.send(:put_string, @data)
1344
+ end
1345
+
1346
+ def validate
1347
+ end
1348
+
1349
+ private
1350
+
1351
+ def initialize(data)
1352
+ @data = data
1353
+ end
1354
+ end
1355
+
1356
+ class QuotedString # :nodoc:
1357
+ def send_data(imap)
1358
+ imap.send(:send_quoted_string, @data)
1359
+ end
1360
+
1361
+ def validate
1362
+ end
1363
+
1364
+ private
1365
+
1366
+ def initialize(data)
1367
+ @data = data
1368
+ end
1369
+ end
1370
+
1371
+ class Literal # :nodoc:
1372
+ def send_data(imap)
1373
+ imap.send(:send_literal, @data)
1374
+ end
1375
+
1376
+ def validate
1377
+ end
1378
+
1379
+ private
1380
+
1381
+ def initialize(data)
1382
+ @data = data
1383
+ end
1384
+ end
1385
+
1386
+ class MessageSet # :nodoc:
1387
+ def send_data(imap)
1388
+ imap.send(:put_string, format_internal(@data))
1389
+ end
1390
+
1391
+ def validate
1392
+ validate_internal(@data)
1393
+ end
1394
+
1395
+ private
1396
+
1397
+ def initialize(data)
1398
+ @data = data
1399
+ end
1400
+
1401
+ def format_internal(data)
1402
+ case data
1403
+ when "*"
1404
+ return data
1405
+ when Integer
1406
+ if data == -1
1407
+ return "*"
1408
+ else
1409
+ return data.to_s
1410
+ end
1411
+ when Range
1412
+ return format_internal(data.first) +
1413
+ ":" + format_internal(data.last)
1414
+ when Array
1415
+ return data.collect {|i| format_internal(i)}.join(",")
1416
+ when ThreadMember
1417
+ return data.seqno.to_s +
1418
+ ":" + data.children.collect {|i| format_internal(i).join(",")}
1419
+ end
1420
+ end
1421
+
1422
+ def validate_internal(data)
1423
+ case data
1424
+ when "*"
1425
+ when Integer
1426
+ ensure_nz_number(data)
1427
+ when Range
1428
+ when Array
1429
+ data.each do |i|
1430
+ validate_internal(i)
1431
+ end
1432
+ when ThreadMember
1433
+ data.children.each do |i|
1434
+ validate_internal(i)
1435
+ end
1436
+ else
1437
+ raise DataFormatError, data.inspect
1438
+ end
1439
+ end
1440
+
1441
+ def ensure_nz_number(num)
1442
+ if num < -1 || num == 0 || num >= 4294967296
1443
+ msg = "nz_number must be non-zero unsigned 32-bit integer: " +
1444
+ num.inspect
1445
+ raise DataFormatError, msg
1446
+ end
1447
+ end
1448
+ end
1449
+
1450
+ # Net::IMAP::ContinuationRequest represents command continuation requests.
1451
+ #
1452
+ # The command continuation request response is indicated by a "+" token
1453
+ # instead of a tag. This form of response indicates that the server is
1454
+ # ready to accept the continuation of a command from the client. The
1455
+ # remainder of this response is a line of text.
1456
+ #
1457
+ # continue_req ::= "+" SPACE (resp_text / base64)
1458
+ #
1459
+ # ==== Fields:
1460
+ #
1461
+ # data:: Returns the data (Net::IMAP::ResponseText).
1462
+ #
1463
+ # raw_data:: Returns the raw data string.
1464
+ ContinuationRequest = Struct.new(:data, :raw_data)
1465
+
1466
+ # Net::IMAP::UntaggedResponse represents untagged responses.
1467
+ #
1468
+ # Data transmitted by the server to the client and status responses
1469
+ # that do not indicate command completion are prefixed with the token
1470
+ # "*", and are called untagged responses.
1471
+ #
1472
+ # response_data ::= "*" SPACE (resp_cond_state / resp_cond_bye /
1473
+ # mailbox_data / message_data / capability_data)
1474
+ #
1475
+ # ==== Fields:
1476
+ #
1477
+ # name:: Returns the name such as "FLAGS", "LIST", "FETCH"....
1478
+ #
1479
+ # data:: Returns the data such as an array of flag symbols,
1480
+ # a ((<Net::IMAP::MailboxList>)) object....
1481
+ #
1482
+ # raw_data:: Returns the raw data string.
1483
+ UntaggedResponse = Struct.new(:name, :data, :raw_data)
1484
+
1485
+ # Net::IMAP::TaggedResponse represents tagged responses.
1486
+ #
1487
+ # The server completion result response indicates the success or
1488
+ # failure of the operation. It is tagged with the same tag as the
1489
+ # client command which began the operation.
1490
+ #
1491
+ # response_tagged ::= tag SPACE resp_cond_state CRLF
1492
+ #
1493
+ # tag ::= 1*<any ATOM_CHAR except "+">
1494
+ #
1495
+ # resp_cond_state ::= ("OK" / "NO" / "BAD") SPACE resp_text
1496
+ #
1497
+ # ==== Fields:
1498
+ #
1499
+ # tag:: Returns the tag.
1500
+ #
1501
+ # name:: Returns the name. the name is one of "OK", "NO", "BAD".
1502
+ #
1503
+ # data:: Returns the data. See ((<Net::IMAP::ResponseText>)).
1504
+ #
1505
+ # raw_data:: Returns the raw data string.
1506
+ #
1507
+ TaggedResponse = Struct.new(:tag, :name, :data, :raw_data)
1508
+
1509
+ # Net::IMAP::ResponseText represents texts of responses.
1510
+ # The text may be prefixed by the response code.
1511
+ #
1512
+ # resp_text ::= ["[" resp_text_code "]" SPACE] (text_mime2 / text)
1513
+ # ;; text SHOULD NOT begin with "[" or "="
1514
+ #
1515
+ # ==== Fields:
1516
+ #
1517
+ # code:: Returns the response code. See ((<Net::IMAP::ResponseCode>)).
1518
+ #
1519
+ # text:: Returns the text.
1520
+ #
1521
+ ResponseText = Struct.new(:code, :text)
1522
+
1523
+ #
1524
+ # Net::IMAP::ResponseCode represents response codes.
1525
+ #
1526
+ # resp_text_code ::= "ALERT" / "PARSE" /
1527
+ # "PERMANENTFLAGS" SPACE "(" #(flag / "\*") ")" /
1528
+ # "READ-ONLY" / "READ-WRITE" / "TRYCREATE" /
1529
+ # "UIDVALIDITY" SPACE nz_number /
1530
+ # "UNSEEN" SPACE nz_number /
1531
+ # atom [SPACE 1*<any TEXT_CHAR except "]">]
1532
+ #
1533
+ # ==== Fields:
1534
+ #
1535
+ # name:: Returns the name such as "ALERT", "PERMANENTFLAGS", "UIDVALIDITY"....
1536
+ #
1537
+ # data:: Returns the data if it exists.
1538
+ #
1539
+ ResponseCode = Struct.new(:name, :data)
1540
+
1541
+ # Net::IMAP::MailboxList represents contents of the LIST response.
1542
+ #
1543
+ # mailbox_list ::= "(" #("\Marked" / "\Noinferiors" /
1544
+ # "\Noselect" / "\Unmarked" / flag_extension) ")"
1545
+ # SPACE (<"> QUOTED_CHAR <"> / nil) SPACE mailbox
1546
+ #
1547
+ # ==== Fields:
1548
+ #
1549
+ # attr:: Returns the name attributes. Each name attribute is a symbol
1550
+ # capitalized by String#capitalize, such as :Noselect (not :NoSelect).
1551
+ #
1552
+ # delim:: Returns the hierarchy delimiter
1553
+ #
1554
+ # name:: Returns the mailbox name.
1555
+ #
1556
+ MailboxList = Struct.new(:attr, :delim, :name)
1557
+
1558
+ # Net::IMAP::MailboxQuota represents contents of GETQUOTA response.
1559
+ # This object can also be a response to GETQUOTAROOT. In the syntax
1560
+ # specification below, the delimiter used with the "#" construct is a
1561
+ # single space (SPACE).
1562
+ #
1563
+ # quota_list ::= "(" #quota_resource ")"
1564
+ #
1565
+ # quota_resource ::= atom SPACE number SPACE number
1566
+ #
1567
+ # quota_response ::= "QUOTA" SPACE astring SPACE quota_list
1568
+ #
1569
+ # ==== Fields:
1570
+ #
1571
+ # mailbox:: The mailbox with the associated quota.
1572
+ #
1573
+ # usage:: Current storage usage of mailbox.
1574
+ #
1575
+ # quota:: Quota limit imposed on mailbox.
1576
+ #
1577
+ MailboxQuota = Struct.new(:mailbox, :usage, :quota)
1578
+
1579
+ # Net::IMAP::MailboxQuotaRoot represents part of the GETQUOTAROOT
1580
+ # response. (GETQUOTAROOT can also return Net::IMAP::MailboxQuota.)
1581
+ #
1582
+ # quotaroot_response ::= "QUOTAROOT" SPACE astring *(SPACE astring)
1583
+ #
1584
+ # ==== Fields:
1585
+ #
1586
+ # mailbox:: The mailbox with the associated quota.
1587
+ #
1588
+ # quotaroots:: Zero or more quotaroots that effect the quota on the
1589
+ # specified mailbox.
1590
+ #
1591
+ MailboxQuotaRoot = Struct.new(:mailbox, :quotaroots)
1592
+
1593
+ # Net::IMAP::MailboxACLItem represents response from GETACL.
1594
+ #
1595
+ # acl_data ::= "ACL" SPACE mailbox *(SPACE identifier SPACE rights)
1596
+ #
1597
+ # identifier ::= astring
1598
+ #
1599
+ # rights ::= astring
1600
+ #
1601
+ # ==== Fields:
1602
+ #
1603
+ # user:: Login name that has certain rights to the mailbox
1604
+ # that was specified with the getacl command.
1605
+ #
1606
+ # rights:: The access rights the indicated user has to the
1607
+ # mailbox.
1608
+ #
1609
+ MailboxACLItem = Struct.new(:user, :rights)
1610
+
1611
+ # Net::IMAP::StatusData represents contents of the STATUS response.
1612
+ #
1613
+ # ==== Fields:
1614
+ #
1615
+ # mailbox:: Returns the mailbox name.
1616
+ #
1617
+ # attr:: Returns a hash. Each key is one of "MESSAGES", "RECENT", "UIDNEXT",
1618
+ # "UIDVALIDITY", "UNSEEN". Each value is a number.
1619
+ #
1620
+ StatusData = Struct.new(:mailbox, :attr)
1621
+
1622
+ # Net::IMAP::FetchData represents contents of the FETCH response.
1623
+ #
1624
+ # ==== Fields:
1625
+ #
1626
+ # seqno:: Returns the message sequence number.
1627
+ # (Note: not the unique identifier, even for the UID command response.)
1628
+ #
1629
+ # attr:: Returns a hash. Each key is a data item name, and each value is
1630
+ # its value.
1631
+ #
1632
+ # The current data items are:
1633
+ #
1634
+ # [BODY]
1635
+ # A form of BODYSTRUCTURE without extension data.
1636
+ # [BODY[<section>]<<origin_octet>>]
1637
+ # A string expressing the body contents of the specified section.
1638
+ # [BODYSTRUCTURE]
1639
+ # An object that describes the [MIME-IMB] body structure of a message.
1640
+ # See Net::IMAP::BodyTypeBasic, Net::IMAP::BodyTypeText,
1641
+ # Net::IMAP::BodyTypeMessage, Net::IMAP::BodyTypeMultipart.
1642
+ # [ENVELOPE]
1643
+ # A Net::IMAP::Envelope object that describes the envelope
1644
+ # structure of a message.
1645
+ # [FLAGS]
1646
+ # A array of flag symbols that are set for this message. flag symbols
1647
+ # are capitalized by String#capitalize.
1648
+ # [INTERNALDATE]
1649
+ # A string representing the internal date of the message.
1650
+ # [RFC822]
1651
+ # Equivalent to BODY[].
1652
+ # [RFC822.HEADER]
1653
+ # Equivalent to BODY.PEEK[HEADER].
1654
+ # [RFC822.SIZE]
1655
+ # A number expressing the [RFC-822] size of the message.
1656
+ # [RFC822.TEXT]
1657
+ # Equivalent to BODY[TEXT].
1658
+ # [UID]
1659
+ # A number expressing the unique identifier of the message.
1660
+ #
1661
+ FetchData = Struct.new(:seqno, :attr)
1662
+
1663
+ # Net::IMAP::Envelope represents envelope structures of messages.
1664
+ #
1665
+ # ==== Fields:
1666
+ #
1667
+ # date:: Returns a string that represents the date.
1668
+ #
1669
+ # subject:: Returns a string that represents the subject.
1670
+ #
1671
+ # from:: Returns an array of Net::IMAP::Address that represents the from.
1672
+ #
1673
+ # sender:: Returns an array of Net::IMAP::Address that represents the sender.
1674
+ #
1675
+ # reply_to:: Returns an array of Net::IMAP::Address that represents the reply-to.
1676
+ #
1677
+ # to:: Returns an array of Net::IMAP::Address that represents the to.
1678
+ #
1679
+ # cc:: Returns an array of Net::IMAP::Address that represents the cc.
1680
+ #
1681
+ # bcc:: Returns an array of Net::IMAP::Address that represents the bcc.
1682
+ #
1683
+ # in_reply_to:: Returns a string that represents the in-reply-to.
1684
+ #
1685
+ # message_id:: Returns a string that represents the message-id.
1686
+ #
1687
+ Envelope = Struct.new(:date, :subject, :from, :sender, :reply_to,
1688
+ :to, :cc, :bcc, :in_reply_to, :message_id)
1689
+
1690
+ #
1691
+ # Net::IMAP::Address represents electronic mail addresses.
1692
+ #
1693
+ # ==== Fields:
1694
+ #
1695
+ # name:: Returns the phrase from [RFC-822] mailbox.
1696
+ #
1697
+ # route:: Returns the route from [RFC-822] route-addr.
1698
+ #
1699
+ # mailbox:: nil indicates end of [RFC-822] group.
1700
+ # If non-nil and host is nil, returns [RFC-822] group name.
1701
+ # Otherwise, returns [RFC-822] local-part
1702
+ #
1703
+ # host:: nil indicates [RFC-822] group syntax.
1704
+ # Otherwise, returns [RFC-822] domain name.
1705
+ #
1706
+ Address = Struct.new(:name, :route, :mailbox, :host)
1707
+
1708
+ #
1709
+ # Net::IMAP::ContentDisposition represents Content-Disposition fields.
1710
+ #
1711
+ # ==== Fields:
1712
+ #
1713
+ # dsp_type:: Returns the disposition type.
1714
+ #
1715
+ # param:: Returns a hash that represents parameters of the Content-Disposition
1716
+ # field.
1717
+ #
1718
+ ContentDisposition = Struct.new(:dsp_type, :param)
1719
+
1720
+ # Net::IMAP::ThreadMember represents a thread-node returned
1721
+ # by Net::IMAP#thread
1722
+ #
1723
+ # ==== Fields:
1724
+ #
1725
+ # seqno:: The sequence number of this message.
1726
+ #
1727
+ # children:: an array of Net::IMAP::ThreadMember objects for mail
1728
+ # items that are children of this in the thread.
1729
+ #
1730
+ ThreadMember = Struct.new(:seqno, :children)
1731
+
1732
+ # Net::IMAP::BodyTypeBasic represents basic body structures of messages.
1733
+ #
1734
+ # ==== Fields:
1735
+ #
1736
+ # media_type:: Returns the content media type name as defined in [MIME-IMB].
1737
+ #
1738
+ # subtype:: Returns the content subtype name as defined in [MIME-IMB].
1739
+ #
1740
+ # param:: Returns a hash that represents parameters as defined in [MIME-IMB].
1741
+ #
1742
+ # content_id:: Returns a string giving the content id as defined in [MIME-IMB].
1743
+ #
1744
+ # description:: Returns a string giving the content description as defined in
1745
+ # [MIME-IMB].
1746
+ #
1747
+ # encoding:: Returns a string giving the content transfer encoding as defined in
1748
+ # [MIME-IMB].
1749
+ #
1750
+ # size:: Returns a number giving the size of the body in octets.
1751
+ #
1752
+ # md5:: Returns a string giving the body MD5 value as defined in [MD5].
1753
+ #
1754
+ # disposition:: Returns a Net::IMAP::ContentDisposition object giving
1755
+ # the content disposition.
1756
+ #
1757
+ # language:: Returns a string or an array of strings giving the body
1758
+ # language value as defined in [LANGUAGE-TAGS].
1759
+ #
1760
+ # extension:: Returns extension data.
1761
+ #
1762
+ # multipart?:: Returns false.
1763
+ #
1764
+ class BodyTypeBasic < Struct.new(:media_type, :subtype,
1765
+ :param, :content_id,
1766
+ :description, :encoding, :size,
1767
+ :md5, :disposition, :language,
1768
+ :extension)
1769
+ def multipart?
1770
+ return false
1771
+ end
1772
+
1773
+ # Obsolete: use +subtype+ instead. Calling this will
1774
+ # generate a warning message to +stderr+, then return
1775
+ # the value of +subtype+.
1776
+ def media_subtype
1777
+ $stderr.printf("warning: media_subtype is obsolete.\n")
1778
+ $stderr.printf(" use subtype instead.\n")
1779
+ return subtype
1780
+ end
1781
+ end
1782
+
1783
+ # Net::IMAP::BodyTypeText represents TEXT body structures of messages.
1784
+ #
1785
+ # ==== Fields:
1786
+ #
1787
+ # lines:: Returns the size of the body in text lines.
1788
+ #
1789
+ # And Net::IMAP::BodyTypeText has all fields of Net::IMAP::BodyTypeBasic.
1790
+ #
1791
+ class BodyTypeText < Struct.new(:media_type, :subtype,
1792
+ :param, :content_id,
1793
+ :description, :encoding, :size,
1794
+ :lines,
1795
+ :md5, :disposition, :language,
1796
+ :extension)
1797
+ def multipart?
1798
+ return false
1799
+ end
1800
+
1801
+ # Obsolete: use +subtype+ instead. Calling this will
1802
+ # generate a warning message to +stderr+, then return
1803
+ # the value of +subtype+.
1804
+ def media_subtype
1805
+ $stderr.printf("warning: media_subtype is obsolete.\n")
1806
+ $stderr.printf(" use subtype instead.\n")
1807
+ return subtype
1808
+ end
1809
+ end
1810
+
1811
+ # Net::IMAP::BodyTypeMessage represents MESSAGE/RFC822 body structures of messages.
1812
+ #
1813
+ # ==== Fields:
1814
+ #
1815
+ # envelope:: Returns a Net::IMAP::Envelope giving the envelope structure.
1816
+ #
1817
+ # body:: Returns an object giving the body structure.
1818
+ #
1819
+ # And Net::IMAP::BodyTypeMessage has all methods of Net::IMAP::BodyTypeText.
1820
+ #
1821
+ class BodyTypeMessage < Struct.new(:media_type, :subtype,
1822
+ :param, :content_id,
1823
+ :description, :encoding, :size,
1824
+ :envelope, :body, :lines,
1825
+ :md5, :disposition, :language,
1826
+ :extension)
1827
+ def multipart?
1828
+ return false
1829
+ end
1830
+
1831
+ # Obsolete: use +subtype+ instead. Calling this will
1832
+ # generate a warning message to +stderr+, then return
1833
+ # the value of +subtype+.
1834
+ def media_subtype
1835
+ $stderr.printf("warning: media_subtype is obsolete.\n")
1836
+ $stderr.printf(" use subtype instead.\n")
1837
+ return subtype
1838
+ end
1839
+ end
1840
+
1841
+ # Net::IMAP::BodyTypeMultipart represents multipart body structures
1842
+ # of messages.
1843
+ #
1844
+ # ==== Fields:
1845
+ #
1846
+ # media_type:: Returns the content media type name as defined in [MIME-IMB].
1847
+ #
1848
+ # subtype:: Returns the content subtype name as defined in [MIME-IMB].
1849
+ #
1850
+ # parts:: Returns multiple parts.
1851
+ #
1852
+ # param:: Returns a hash that represents parameters as defined in [MIME-IMB].
1853
+ #
1854
+ # disposition:: Returns a Net::IMAP::ContentDisposition object giving
1855
+ # the content disposition.
1856
+ #
1857
+ # language:: Returns a string or an array of strings giving the body
1858
+ # language value as defined in [LANGUAGE-TAGS].
1859
+ #
1860
+ # extension:: Returns extension data.
1861
+ #
1862
+ # multipart?:: Returns true.
1863
+ #
1864
+ class BodyTypeMultipart < Struct.new(:media_type, :subtype,
1865
+ :parts,
1866
+ :param, :disposition, :language,
1867
+ :extension)
1868
+ def multipart?
1869
+ return true
1870
+ end
1871
+
1872
+ # Obsolete: use +subtype+ instead. Calling this will
1873
+ # generate a warning message to +stderr+, then return
1874
+ # the value of +subtype+.
1875
+ def media_subtype
1876
+ $stderr.printf("warning: media_subtype is obsolete.\n")
1877
+ $stderr.printf(" use subtype instead.\n")
1878
+ return subtype
1879
+ end
1880
+ end
1881
+
1882
+ class ResponseParser # :nodoc:
1883
+ def parse(str)
1884
+ @str = str
1885
+ @pos = 0
1886
+ @lex_state = EXPR_BEG
1887
+ @token = nil
1888
+ return response
1889
+ end
1890
+
1891
+ private
1892
+
1893
+ EXPR_BEG = :EXPR_BEG
1894
+ EXPR_DATA = :EXPR_DATA
1895
+ EXPR_TEXT = :EXPR_TEXT
1896
+ EXPR_RTEXT = :EXPR_RTEXT
1897
+ EXPR_CTEXT = :EXPR_CTEXT
1898
+
1899
+ T_SPACE = :SPACE
1900
+ T_NIL = :NIL
1901
+ T_NUMBER = :NUMBER
1902
+ T_ATOM = :ATOM
1903
+ T_QUOTED = :QUOTED
1904
+ T_LPAR = :LPAR
1905
+ T_RPAR = :RPAR
1906
+ T_BSLASH = :BSLASH
1907
+ T_STAR = :STAR
1908
+ T_LBRA = :LBRA
1909
+ T_RBRA = :RBRA
1910
+ T_LITERAL = :LITERAL
1911
+ T_PLUS = :PLUS
1912
+ T_PERCENT = :PERCENT
1913
+ T_CRLF = :CRLF
1914
+ T_EOF = :EOF
1915
+ T_TEXT = :TEXT
1916
+
1917
+ BEG_REGEXP = /\G(?:\
1918
+ (?# 1: SPACE )( +)|\
1919
+ (?# 2: NIL )(NIL)(?=[\x80-\xff(){ \x00-\x1f\x7f%*"\\\[\]+])|\
1920
+ (?# 3: NUMBER )(\d+)(?=[\x80-\xff(){ \x00-\x1f\x7f%*"\\\[\]+])|\
1921
+ (?# 4: ATOM )([^\x80-\xff(){ \x00-\x1f\x7f%*"\\\[\]+]+)|\
1922
+ (?# 5: QUOTED )"((?:[^\x00\r\n"\\]|\\["\\])*)"|\
1923
+ (?# 6: LPAR )(\()|\
1924
+ (?# 7: RPAR )(\))|\
1925
+ (?# 8: BSLASH )(\\)|\
1926
+ (?# 9: STAR )(\*)|\
1927
+ (?# 10: LBRA )(\[)|\
1928
+ (?# 11: RBRA )(\])|\
1929
+ (?# 12: LITERAL )\{(\d+)\}\r\n|\
1930
+ (?# 13: PLUS )(\+)|\
1931
+ (?# 14: PERCENT )(%)|\
1932
+ (?# 15: CRLF )(\r\n)|\
1933
+ (?# 16: EOF )(\z))/ni
1934
+
1935
+ DATA_REGEXP = /\G(?:\
1936
+ (?# 1: SPACE )( )|\
1937
+ (?# 2: NIL )(NIL)|\
1938
+ (?# 3: NUMBER )(\d+)|\
1939
+ (?# 4: QUOTED )"((?:[^\x00\r\n"\\]|\\["\\])*)"|\
1940
+ (?# 5: LITERAL )\{(\d+)\}\r\n|\
1941
+ (?# 6: LPAR )(\()|\
1942
+ (?# 7: RPAR )(\)))/ni
1943
+
1944
+ TEXT_REGEXP = /\G(?:\
1945
+ (?# 1: TEXT )([^\x00\r\n]*))/ni
1946
+
1947
+ RTEXT_REGEXP = /\G(?:\
1948
+ (?# 1: LBRA )(\[)|\
1949
+ (?# 2: TEXT )([^\x00\r\n]*))/ni
1950
+
1951
+ CTEXT_REGEXP = /\G(?:\
1952
+ (?# 1: TEXT )([^\x00\r\n\]]*))/ni
1953
+
1954
+ Token = Struct.new(:symbol, :value)
1955
+
1956
+ def response
1957
+ token = lookahead
1958
+ case token.symbol
1959
+ when T_PLUS
1960
+ result = continue_req
1961
+ when T_STAR
1962
+ result = response_untagged
1963
+ else
1964
+ result = response_tagged
1965
+ end
1966
+ match(T_CRLF)
1967
+ match(T_EOF)
1968
+ return result
1969
+ end
1970
+
1971
+ def continue_req
1972
+ match(T_PLUS)
1973
+ match(T_SPACE)
1974
+ return ContinuationRequest.new(resp_text, @str)
1975
+ end
1976
+
1977
+ def response_untagged
1978
+ match(T_STAR)
1979
+ match(T_SPACE)
1980
+ token = lookahead
1981
+ if token.symbol == T_NUMBER
1982
+ return numeric_response
1983
+ elsif token.symbol == T_ATOM
1984
+ case token.value
1985
+ when /\A(?:OK|NO|BAD|BYE|PREAUTH)\z/ni
1986
+ return response_cond
1987
+ when /\A(?:FLAGS)\z/ni
1988
+ return flags_response
1989
+ when /\A(?:LIST|LSUB)\z/ni
1990
+ return list_response
1991
+ when /\A(?:QUOTA)\z/ni
1992
+ return getquota_response
1993
+ when /\A(?:QUOTAROOT)\z/ni
1994
+ return getquotaroot_response
1995
+ when /\A(?:ACL)\z/ni
1996
+ return getacl_response
1997
+ when /\A(?:SEARCH|SORT)\z/ni
1998
+ return search_response
1999
+ when /\A(?:THREAD)\z/ni
2000
+ return thread_response
2001
+ when /\A(?:STATUS)\z/ni
2002
+ return status_response
2003
+ when /\A(?:CAPABILITY)\z/ni
2004
+ return capability_response
2005
+ else
2006
+ return text_response
2007
+ end
2008
+ else
2009
+ parse_error("unexpected token %s", token.symbol)
2010
+ end
2011
+ end
2012
+
2013
+ def response_tagged
2014
+ tag = atom
2015
+ match(T_SPACE)
2016
+ token = match(T_ATOM)
2017
+ name = token.value.upcase
2018
+ match(T_SPACE)
2019
+ return TaggedResponse.new(tag, name, resp_text, @str)
2020
+ end
2021
+
2022
+ def response_cond
2023
+ token = match(T_ATOM)
2024
+ name = token.value.upcase
2025
+ match(T_SPACE)
2026
+ return UntaggedResponse.new(name, resp_text, @str)
2027
+ end
2028
+
2029
+ def numeric_response
2030
+ n = number
2031
+ match(T_SPACE)
2032
+ token = match(T_ATOM)
2033
+ name = token.value.upcase
2034
+ case name
2035
+ when "EXISTS", "RECENT", "EXPUNGE"
2036
+ return UntaggedResponse.new(name, n, @str)
2037
+ when "FETCH"
2038
+ shift_token
2039
+ match(T_SPACE)
2040
+ data = FetchData.new(n, msg_att)
2041
+ return UntaggedResponse.new(name, data, @str)
2042
+ end
2043
+ end
2044
+
2045
+ def msg_att
2046
+ match(T_LPAR)
2047
+ attr = {}
2048
+ while true
2049
+ token = lookahead
2050
+ case token.symbol
2051
+ when T_RPAR
2052
+ shift_token
2053
+ break
2054
+ when T_SPACE
2055
+ shift_token
2056
+ token = lookahead
2057
+ end
2058
+ case token.value
2059
+ when /\A(?:ENVELOPE)\z/ni
2060
+ name, val = envelope_data
2061
+ when /\A(?:FLAGS)\z/ni
2062
+ name, val = flags_data
2063
+ when /\A(?:INTERNALDATE)\z/ni
2064
+ name, val = internaldate_data
2065
+ when /\A(?:RFC822(?:\.HEADER|\.TEXT)?)\z/ni
2066
+ name, val = rfc822_text
2067
+ when /\A(?:RFC822\.SIZE)\z/ni
2068
+ name, val = rfc822_size
2069
+ when /\A(?:BODY(?:STRUCTURE)?)\z/ni
2070
+ name, val = body_data
2071
+ when /\A(?:UID)\z/ni
2072
+ name, val = uid_data
2073
+ else
2074
+ parse_error("unknown attribute `%s'", token.value)
2075
+ end
2076
+ attr[name] = val
2077
+ end
2078
+ return attr
2079
+ end
2080
+
2081
+ def envelope_data
2082
+ token = match(T_ATOM)
2083
+ name = token.value.upcase
2084
+ match(T_SPACE)
2085
+ return name, envelope
2086
+ end
2087
+
2088
+ def envelope
2089
+ @lex_state = EXPR_DATA
2090
+ token = lookahead
2091
+ if token.symbol == T_NIL
2092
+ shift_token
2093
+ result = nil
2094
+ else
2095
+ match(T_LPAR)
2096
+ date = nstring
2097
+ match(T_SPACE)
2098
+ subject = nstring
2099
+ match(T_SPACE)
2100
+ from = address_list
2101
+ match(T_SPACE)
2102
+ sender = address_list
2103
+ match(T_SPACE)
2104
+ reply_to = address_list
2105
+ match(T_SPACE)
2106
+ to = address_list
2107
+ match(T_SPACE)
2108
+ cc = address_list
2109
+ match(T_SPACE)
2110
+ bcc = address_list
2111
+ match(T_SPACE)
2112
+ in_reply_to = nstring
2113
+ match(T_SPACE)
2114
+ message_id = nstring
2115
+ match(T_RPAR)
2116
+ result = Envelope.new(date, subject, from, sender, reply_to,
2117
+ to, cc, bcc, in_reply_to, message_id)
2118
+ end
2119
+ @lex_state = EXPR_BEG
2120
+ return result
2121
+ end
2122
+
2123
+ def flags_data
2124
+ token = match(T_ATOM)
2125
+ name = token.value.upcase
2126
+ match(T_SPACE)
2127
+ return name, flag_list
2128
+ end
2129
+
2130
+ def internaldate_data
2131
+ token = match(T_ATOM)
2132
+ name = token.value.upcase
2133
+ match(T_SPACE)
2134
+ token = match(T_QUOTED)
2135
+ return name, token.value
2136
+ end
2137
+
2138
+ def rfc822_text
2139
+ token = match(T_ATOM)
2140
+ name = token.value.upcase
2141
+ match(T_SPACE)
2142
+ return name, nstring
2143
+ end
2144
+
2145
+ def rfc822_size
2146
+ token = match(T_ATOM)
2147
+ name = token.value.upcase
2148
+ match(T_SPACE)
2149
+ return name, number
2150
+ end
2151
+
2152
+ def body_data
2153
+ token = match(T_ATOM)
2154
+ name = token.value.upcase
2155
+ token = lookahead
2156
+ if token.symbol == T_SPACE
2157
+ shift_token
2158
+ return name, body
2159
+ end
2160
+ name.concat(section)
2161
+ token = lookahead
2162
+ if token.symbol == T_ATOM
2163
+ name.concat(token.value)
2164
+ shift_token
2165
+ end
2166
+ match(T_SPACE)
2167
+ data = nstring
2168
+ return name, data
2169
+ end
2170
+
2171
+ def body
2172
+ @lex_state = EXPR_DATA
2173
+ token = lookahead
2174
+ if token.symbol == T_NIL
2175
+ shift_token
2176
+ result = nil
2177
+ else
2178
+ match(T_LPAR)
2179
+ token = lookahead
2180
+ if token.symbol == T_LPAR
2181
+ result = body_type_mpart
2182
+ else
2183
+ result = body_type_1part
2184
+ end
2185
+ match(T_RPAR)
2186
+ end
2187
+ @lex_state = EXPR_BEG
2188
+ return result
2189
+ end
2190
+
2191
+ def body_type_1part
2192
+ token = lookahead
2193
+ case token.value
2194
+ when /\A(?:TEXT)\z/ni
2195
+ return body_type_text
2196
+ when /\A(?:MESSAGE)\z/ni
2197
+ return body_type_msg
2198
+ else
2199
+ return body_type_basic
2200
+ end
2201
+ end
2202
+
2203
+ def body_type_basic
2204
+ mtype, msubtype = media_type
2205
+ token = lookahead
2206
+ if token.symbol == T_RPAR
2207
+ return BodyTypeBasic.new(mtype, msubtype)
2208
+ end
2209
+ match(T_SPACE)
2210
+ param, content_id, desc, enc, size = body_fields
2211
+ md5, disposition, language, extension = body_ext_1part
2212
+ return BodyTypeBasic.new(mtype, msubtype,
2213
+ param, content_id,
2214
+ desc, enc, size,
2215
+ md5, disposition, language, extension)
2216
+ end
2217
+
2218
+ def body_type_text
2219
+ mtype, msubtype = media_type
2220
+ match(T_SPACE)
2221
+ param, content_id, desc, enc, size = body_fields
2222
+ match(T_SPACE)
2223
+ lines = number
2224
+ md5, disposition, language, extension = body_ext_1part
2225
+ return BodyTypeText.new(mtype, msubtype,
2226
+ param, content_id,
2227
+ desc, enc, size,
2228
+ lines,
2229
+ md5, disposition, language, extension)
2230
+ end
2231
+
2232
+ def body_type_msg
2233
+ mtype, msubtype = media_type
2234
+ match(T_SPACE)
2235
+ param, content_id, desc, enc, size = body_fields
2236
+ match(T_SPACE)
2237
+ env = envelope
2238
+ match(T_SPACE)
2239
+ b = body
2240
+ match(T_SPACE)
2241
+ lines = number
2242
+ md5, disposition, language, extension = body_ext_1part
2243
+ return BodyTypeMessage.new(mtype, msubtype,
2244
+ param, content_id,
2245
+ desc, enc, size,
2246
+ env, b, lines,
2247
+ md5, disposition, language, extension)
2248
+ end
2249
+
2250
+ def body_type_mpart
2251
+ parts = []
2252
+ while true
2253
+ token = lookahead
2254
+ if token.symbol == T_SPACE
2255
+ shift_token
2256
+ break
2257
+ end
2258
+ parts.push(body)
2259
+ end
2260
+ mtype = "MULTIPART"
2261
+ msubtype = case_insensitive_string
2262
+ param, disposition, language, extension = body_ext_mpart
2263
+ return BodyTypeMultipart.new(mtype, msubtype, parts,
2264
+ param, disposition, language,
2265
+ extension)
2266
+ end
2267
+
2268
+ def media_type
2269
+ mtype = case_insensitive_string
2270
+ match(T_SPACE)
2271
+ msubtype = case_insensitive_string
2272
+ return mtype, msubtype
2273
+ end
2274
+
2275
+ def body_fields
2276
+ param = body_fld_param
2277
+ match(T_SPACE)
2278
+ content_id = nstring
2279
+ match(T_SPACE)
2280
+ desc = nstring
2281
+ match(T_SPACE)
2282
+ enc = case_insensitive_string
2283
+ match(T_SPACE)
2284
+ size = number
2285
+ return param, content_id, desc, enc, size
2286
+ end
2287
+
2288
+ def body_fld_param
2289
+ token = lookahead
2290
+ if token.symbol == T_NIL
2291
+ shift_token
2292
+ return nil
2293
+ end
2294
+ match(T_LPAR)
2295
+ param = {}
2296
+ while true
2297
+ token = lookahead
2298
+ case token.symbol
2299
+ when T_RPAR
2300
+ shift_token
2301
+ break
2302
+ when T_SPACE
2303
+ shift_token
2304
+ end
2305
+ name = case_insensitive_string
2306
+ match(T_SPACE)
2307
+ val = string
2308
+ param[name] = val
2309
+ end
2310
+ return param
2311
+ end
2312
+
2313
+ def body_ext_1part
2314
+ token = lookahead
2315
+ if token.symbol == T_SPACE
2316
+ shift_token
2317
+ else
2318
+ return nil
2319
+ end
2320
+ md5 = nstring
2321
+
2322
+ token = lookahead
2323
+ if token.symbol == T_SPACE
2324
+ shift_token
2325
+ else
2326
+ return md5
2327
+ end
2328
+ disposition = body_fld_dsp
2329
+
2330
+ token = lookahead
2331
+ if token.symbol == T_SPACE
2332
+ shift_token
2333
+ else
2334
+ return md5, disposition
2335
+ end
2336
+ language = body_fld_lang
2337
+
2338
+ token = lookahead
2339
+ if token.symbol == T_SPACE
2340
+ shift_token
2341
+ else
2342
+ return md5, disposition, language
2343
+ end
2344
+
2345
+ extension = body_extensions
2346
+ return md5, disposition, language, extension
2347
+ end
2348
+
2349
+ def body_ext_mpart
2350
+ token = lookahead
2351
+ if token.symbol == T_SPACE
2352
+ shift_token
2353
+ else
2354
+ return nil
2355
+ end
2356
+ param = body_fld_param
2357
+
2358
+ token = lookahead
2359
+ if token.symbol == T_SPACE
2360
+ shift_token
2361
+ else
2362
+ return param
2363
+ end
2364
+ disposition = body_fld_dsp
2365
+ match(T_SPACE)
2366
+ language = body_fld_lang
2367
+
2368
+ token = lookahead
2369
+ if token.symbol == T_SPACE
2370
+ shift_token
2371
+ else
2372
+ return param, disposition, language
2373
+ end
2374
+
2375
+ extension = body_extensions
2376
+ return param, disposition, language, extension
2377
+ end
2378
+
2379
+ def body_fld_dsp
2380
+ token = lookahead
2381
+ if token.symbol == T_NIL
2382
+ shift_token
2383
+ return nil
2384
+ end
2385
+ match(T_LPAR)
2386
+ dsp_type = case_insensitive_string
2387
+ match(T_SPACE)
2388
+ param = body_fld_param
2389
+ match(T_RPAR)
2390
+ return ContentDisposition.new(dsp_type, param)
2391
+ end
2392
+
2393
+ def body_fld_lang
2394
+ token = lookahead
2395
+ if token.symbol == T_LPAR
2396
+ shift_token
2397
+ result = []
2398
+ while true
2399
+ token = lookahead
2400
+ case token.symbol
2401
+ when T_RPAR
2402
+ shift_token
2403
+ return result
2404
+ when T_SPACE
2405
+ shift_token
2406
+ end
2407
+ result.push(case_insensitive_string)
2408
+ end
2409
+ else
2410
+ lang = nstring
2411
+ if lang
2412
+ return lang.upcase
2413
+ else
2414
+ return lang
2415
+ end
2416
+ end
2417
+ end
2418
+
2419
+ def body_extensions
2420
+ result = []
2421
+ while true
2422
+ token = lookahead
2423
+ case token.symbol
2424
+ when T_RPAR
2425
+ return result
2426
+ when T_SPACE
2427
+ shift_token
2428
+ end
2429
+ result.push(body_extension)
2430
+ end
2431
+ end
2432
+
2433
+ def body_extension
2434
+ token = lookahead
2435
+ case token.symbol
2436
+ when T_LPAR
2437
+ shift_token
2438
+ result = body_extensions
2439
+ match(T_RPAR)
2440
+ return result
2441
+ when T_NUMBER
2442
+ return number
2443
+ else
2444
+ return nstring
2445
+ end
2446
+ end
2447
+
2448
+ def section
2449
+ str = ""
2450
+ token = match(T_LBRA)
2451
+ str.concat(token.value)
2452
+ token = match(T_ATOM, T_NUMBER, T_RBRA)
2453
+ if token.symbol == T_RBRA
2454
+ str.concat(token.value)
2455
+ return str
2456
+ end
2457
+ str.concat(token.value)
2458
+ token = lookahead
2459
+ if token.symbol == T_SPACE
2460
+ shift_token
2461
+ str.concat(token.value)
2462
+ token = match(T_LPAR)
2463
+ str.concat(token.value)
2464
+ while true
2465
+ token = lookahead
2466
+ case token.symbol
2467
+ when T_RPAR
2468
+ str.concat(token.value)
2469
+ shift_token
2470
+ break
2471
+ when T_SPACE
2472
+ shift_token
2473
+ str.concat(token.value)
2474
+ end
2475
+ str.concat(format_string(astring))
2476
+ end
2477
+ end
2478
+ token = match(T_RBRA)
2479
+ str.concat(token.value)
2480
+ return str
2481
+ end
2482
+
2483
+ def format_string(str)
2484
+ case str
2485
+ when ""
2486
+ return '""'
2487
+ when /[\x80-\xff\r\n]/n
2488
+ # literal
2489
+ return "{" + str.length.to_s + "}" + CRLF + str
2490
+ when /[(){ \x00-\x1f\x7f%*"\\]/n
2491
+ # quoted string
2492
+ return '"' + str.gsub(/["\\]/n, "\\\\\\&") + '"'
2493
+ else
2494
+ # atom
2495
+ return str
2496
+ end
2497
+ end
2498
+
2499
+ def uid_data
2500
+ token = match(T_ATOM)
2501
+ name = token.value.upcase
2502
+ match(T_SPACE)
2503
+ return name, number
2504
+ end
2505
+
2506
+ def text_response
2507
+ token = match(T_ATOM)
2508
+ name = token.value.upcase
2509
+ match(T_SPACE)
2510
+ @lex_state = EXPR_TEXT
2511
+ token = match(T_TEXT)
2512
+ @lex_state = EXPR_BEG
2513
+ return UntaggedResponse.new(name, token.value)
2514
+ end
2515
+
2516
+ def flags_response
2517
+ token = match(T_ATOM)
2518
+ name = token.value.upcase
2519
+ match(T_SPACE)
2520
+ return UntaggedResponse.new(name, flag_list, @str)
2521
+ end
2522
+
2523
+ def list_response
2524
+ token = match(T_ATOM)
2525
+ name = token.value.upcase
2526
+ match(T_SPACE)
2527
+ return UntaggedResponse.new(name, mailbox_list, @str)
2528
+ end
2529
+
2530
+ def mailbox_list
2531
+ attr = flag_list
2532
+ match(T_SPACE)
2533
+ token = match(T_QUOTED, T_NIL)
2534
+ if token.symbol == T_NIL
2535
+ delim = nil
2536
+ else
2537
+ delim = token.value
2538
+ end
2539
+ match(T_SPACE)
2540
+ name = astring
2541
+ return MailboxList.new(attr, delim, name)
2542
+ end
2543
+
2544
+ def getquota_response
2545
+ # If quota never established, get back
2546
+ # `NO Quota root does not exist'.
2547
+ # If quota removed, get `()' after the
2548
+ # folder spec with no mention of `STORAGE'.
2549
+ token = match(T_ATOM)
2550
+ name = token.value.upcase
2551
+ match(T_SPACE)
2552
+ mailbox = astring
2553
+ match(T_SPACE)
2554
+ match(T_LPAR)
2555
+ token = lookahead
2556
+ case token.symbol
2557
+ when T_RPAR
2558
+ shift_token
2559
+ data = MailboxQuota.new(mailbox, nil, nil)
2560
+ return UntaggedResponse.new(name, data, @str)
2561
+ when T_ATOM
2562
+ shift_token
2563
+ match(T_SPACE)
2564
+ token = match(T_NUMBER)
2565
+ usage = token.value
2566
+ match(T_SPACE)
2567
+ token = match(T_NUMBER)
2568
+ quota = token.value
2569
+ match(T_RPAR)
2570
+ data = MailboxQuota.new(mailbox, usage, quota)
2571
+ return UntaggedResponse.new(name, data, @str)
2572
+ else
2573
+ parse_error("unexpected token %s", token.symbol)
2574
+ end
2575
+ end
2576
+
2577
+ def getquotaroot_response
2578
+ # Similar to getquota, but only admin can use getquota.
2579
+ token = match(T_ATOM)
2580
+ name = token.value.upcase
2581
+ match(T_SPACE)
2582
+ mailbox = astring
2583
+ quotaroots = []
2584
+ while true
2585
+ token = lookahead
2586
+ break unless token.symbol == T_SPACE
2587
+ shift_token
2588
+ quotaroots.push(astring)
2589
+ end
2590
+ data = MailboxQuotaRoot.new(mailbox, quotaroots)
2591
+ return UntaggedResponse.new(name, data, @str)
2592
+ end
2593
+
2594
+ def getacl_response
2595
+ token = match(T_ATOM)
2596
+ name = token.value.upcase
2597
+ match(T_SPACE)
2598
+ mailbox = astring
2599
+ data = []
2600
+ token = lookahead
2601
+ if token.symbol == T_SPACE
2602
+ shift_token
2603
+ while true
2604
+ token = lookahead
2605
+ case token.symbol
2606
+ when T_CRLF
2607
+ break
2608
+ when T_SPACE
2609
+ shift_token
2610
+ end
2611
+ user = astring
2612
+ match(T_SPACE)
2613
+ rights = astring
2614
+ ##XXX data.push([user, rights])
2615
+ data.push(MailboxACLItem.new(user, rights))
2616
+ end
2617
+ end
2618
+ return UntaggedResponse.new(name, data, @str)
2619
+ end
2620
+
2621
+ def search_response
2622
+ token = match(T_ATOM)
2623
+ name = token.value.upcase
2624
+ token = lookahead
2625
+ if token.symbol == T_SPACE
2626
+ shift_token
2627
+ data = []
2628
+ while true
2629
+ token = lookahead
2630
+ case token.symbol
2631
+ when T_CRLF
2632
+ break
2633
+ when T_SPACE
2634
+ shift_token
2635
+ end
2636
+ data.push(number)
2637
+ end
2638
+ else
2639
+ data = []
2640
+ end
2641
+ return UntaggedResponse.new(name, data, @str)
2642
+ end
2643
+
2644
+ def thread_response
2645
+ token = match(T_ATOM)
2646
+ name = token.value.upcase
2647
+ token = lookahead
2648
+
2649
+ if token.symbol == T_SPACE
2650
+ threads = []
2651
+
2652
+ while true
2653
+ shift_token
2654
+ token = lookahead
2655
+
2656
+ case token.symbol
2657
+ when T_LPAR
2658
+ threads << thread_branch(token)
2659
+ when T_CRLF
2660
+ break
2661
+ end
2662
+ end
2663
+ else
2664
+ # no member
2665
+ threads = []
2666
+ end
2667
+
2668
+ return UntaggedResponse.new(name, threads, @str)
2669
+ end
2670
+
2671
+ def thread_branch(token)
2672
+ rootmember = nil
2673
+ lastmember = nil
2674
+
2675
+ while true
2676
+ shift_token # ignore first T_LPAR
2677
+ token = lookahead
2678
+
2679
+ case token.symbol
2680
+ when T_NUMBER
2681
+ # new member
2682
+ newmember = ThreadMember.new(number, [])
2683
+ if rootmember.nil?
2684
+ rootmember = newmember
2685
+ else
2686
+ lastmember.children << newmember
2687
+ end
2688
+ lastmember = newmember
2689
+ when T_SPACE
2690
+ # do nothing
2691
+ when T_LPAR
2692
+ if rootmember.nil?
2693
+ # dummy member
2694
+ lastmember = rootmember = ThreadMember.new(nil, [])
2695
+ end
2696
+
2697
+ lastmember.children << thread_branch(token)
2698
+ when T_RPAR
2699
+ break
2700
+ end
2701
+ end
2702
+
2703
+ return rootmember
2704
+ end
2705
+
2706
+ def status_response
2707
+ token = match(T_ATOM)
2708
+ name = token.value.upcase
2709
+ match(T_SPACE)
2710
+ mailbox = astring
2711
+ match(T_SPACE)
2712
+ match(T_LPAR)
2713
+ attr = {}
2714
+ while true
2715
+ token = lookahead
2716
+ case token.symbol
2717
+ when T_RPAR
2718
+ shift_token
2719
+ break
2720
+ when T_SPACE
2721
+ shift_token
2722
+ end
2723
+ token = match(T_ATOM)
2724
+ key = token.value.upcase
2725
+ match(T_SPACE)
2726
+ val = number
2727
+ attr[key] = val
2728
+ end
2729
+ data = StatusData.new(mailbox, attr)
2730
+ return UntaggedResponse.new(name, data, @str)
2731
+ end
2732
+
2733
+ def capability_response
2734
+ token = match(T_ATOM)
2735
+ name = token.value.upcase
2736
+ match(T_SPACE)
2737
+ data = []
2738
+ while true
2739
+ token = lookahead
2740
+ case token.symbol
2741
+ when T_CRLF
2742
+ break
2743
+ when T_SPACE
2744
+ shift_token
2745
+ end
2746
+ data.push(atom.upcase)
2747
+ end
2748
+ return UntaggedResponse.new(name, data, @str)
2749
+ end
2750
+
2751
+ def resp_text
2752
+ @lex_state = EXPR_RTEXT
2753
+ token = lookahead
2754
+ if token.symbol == T_LBRA
2755
+ code = resp_text_code
2756
+ else
2757
+ code = nil
2758
+ end
2759
+ token = match(T_TEXT)
2760
+ @lex_state = EXPR_BEG
2761
+ return ResponseText.new(code, token.value)
2762
+ end
2763
+
2764
+ def resp_text_code
2765
+ @lex_state = EXPR_BEG
2766
+ match(T_LBRA)
2767
+ token = match(T_ATOM)
2768
+ name = token.value.upcase
2769
+ case name
2770
+ when /\A(?:ALERT|PARSE|READ-ONLY|READ-WRITE|TRYCREATE|NOMODSEQ)\z/n
2771
+ result = ResponseCode.new(name, nil)
2772
+ when /\A(?:PERMANENTFLAGS)\z/n
2773
+ match(T_SPACE)
2774
+ result = ResponseCode.new(name, flag_list)
2775
+ when /\A(?:UIDVALIDITY|UIDNEXT|UNSEEN)\z/n
2776
+ match(T_SPACE)
2777
+ result = ResponseCode.new(name, number)
2778
+ else
2779
+ match(T_SPACE)
2780
+ @lex_state = EXPR_CTEXT
2781
+ token = match(T_TEXT)
2782
+ @lex_state = EXPR_BEG
2783
+ result = ResponseCode.new(name, token.value)
2784
+ end
2785
+ match(T_RBRA)
2786
+ @lex_state = EXPR_RTEXT
2787
+ return result
2788
+ end
2789
+
2790
+ def address_list
2791
+ token = lookahead
2792
+ if token.symbol == T_NIL
2793
+ shift_token
2794
+ return nil
2795
+ else
2796
+ result = []
2797
+ match(T_LPAR)
2798
+ while true
2799
+ token = lookahead
2800
+ case token.symbol
2801
+ when T_RPAR
2802
+ shift_token
2803
+ break
2804
+ when T_SPACE
2805
+ shift_token
2806
+ end
2807
+ result.push(address)
2808
+ end
2809
+ return result
2810
+ end
2811
+ end
2812
+
2813
+ ADDRESS_REGEXP = /\G\
2814
+ (?# 1: NAME )(?:NIL|"((?:[^\x80-\xff\x00\r\n"\\]|\\["\\])*)") \
2815
+ (?# 2: ROUTE )(?:NIL|"((?:[^\x80-\xff\x00\r\n"\\]|\\["\\])*)") \
2816
+ (?# 3: MAILBOX )(?:NIL|"((?:[^\x80-\xff\x00\r\n"\\]|\\["\\])*)") \
2817
+ (?# 4: HOST )(?:NIL|"((?:[^\x80-\xff\x00\r\n"\\]|\\["\\])*)")\
2818
+ \)/ni
2819
+
2820
+ def address
2821
+ match(T_LPAR)
2822
+ if @str.index(ADDRESS_REGEXP, @pos)
2823
+ # address does not include literal.
2824
+ @pos = $~.end(0)
2825
+ name = $1
2826
+ route = $2
2827
+ mailbox = $3
2828
+ host = $4
2829
+ for s in [name, route, mailbox, host]
2830
+ if s
2831
+ s.gsub!(/\\(["\\])/n, "\\1")
2832
+ end
2833
+ end
2834
+ else
2835
+ name = nstring
2836
+ match(T_SPACE)
2837
+ route = nstring
2838
+ match(T_SPACE)
2839
+ mailbox = nstring
2840
+ match(T_SPACE)
2841
+ host = nstring
2842
+ match(T_RPAR)
2843
+ end
2844
+ return Address.new(name, route, mailbox, host)
2845
+ end
2846
+
2847
+ # def flag_list
2848
+ # result = []
2849
+ # match(T_LPAR)
2850
+ # while true
2851
+ # token = lookahead
2852
+ # case token.symbol
2853
+ # when T_RPAR
2854
+ # shift_token
2855
+ # break
2856
+ # when T_SPACE
2857
+ # shift_token
2858
+ # end
2859
+ # result.push(flag)
2860
+ # end
2861
+ # return result
2862
+ # end
2863
+
2864
+ # def flag
2865
+ # token = lookahead
2866
+ # if token.symbol == T_BSLASH
2867
+ # shift_token
2868
+ # token = lookahead
2869
+ # if token.symbol == T_STAR
2870
+ # shift_token
2871
+ # return token.value.intern
2872
+ # else
2873
+ # return atom.intern
2874
+ # end
2875
+ # else
2876
+ # return atom
2877
+ # end
2878
+ # end
2879
+
2880
+ FLAG_REGEXP = /\
2881
+ (?# FLAG )\\([^\x80-\xff(){ \x00-\x1f\x7f%"\\]+)|\
2882
+ (?# ATOM )([^\x80-\xff(){ \x00-\x1f\x7f%*"\\]+)/n
2883
+
2884
+ def flag_list
2885
+ if @str.index(/\(([^)]*)\)/ni, @pos)
2886
+ @pos = $~.end(0)
2887
+ return $1.scan(FLAG_REGEXP).collect { |flag, atom|
2888
+ atom || flag.capitalize.intern
2889
+ }
2890
+ else
2891
+ parse_error("invalid flag list")
2892
+ end
2893
+ end
2894
+
2895
+ def nstring
2896
+ token = lookahead
2897
+ if token.symbol == T_NIL
2898
+ shift_token
2899
+ return nil
2900
+ else
2901
+ return string
2902
+ end
2903
+ end
2904
+
2905
+ def astring
2906
+ token = lookahead
2907
+ if string_token?(token)
2908
+ return string
2909
+ else
2910
+ return atom
2911
+ end
2912
+ end
2913
+
2914
+ def string
2915
+ token = lookahead
2916
+ if token.symbol == T_NIL
2917
+ shift_token
2918
+ return nil
2919
+ end
2920
+ token = match(T_QUOTED, T_LITERAL)
2921
+ return token.value
2922
+ end
2923
+
2924
+ STRING_TOKENS = [T_QUOTED, T_LITERAL, T_NIL]
2925
+
2926
+ def string_token?(token)
2927
+ return STRING_TOKENS.include?(token.symbol)
2928
+ end
2929
+
2930
+ def case_insensitive_string
2931
+ token = lookahead
2932
+ if token.symbol == T_NIL
2933
+ shift_token
2934
+ return nil
2935
+ end
2936
+ token = match(T_QUOTED, T_LITERAL)
2937
+ return token.value.upcase
2938
+ end
2939
+
2940
+ def atom
2941
+ result = ""
2942
+ while true
2943
+ token = lookahead
2944
+ if atom_token?(token)
2945
+ result.concat(token.value)
2946
+ shift_token
2947
+ else
2948
+ if result.empty?
2949
+ parse_error("unexpected token %s", token.symbol)
2950
+ else
2951
+ return result
2952
+ end
2953
+ end
2954
+ end
2955
+ end
2956
+
2957
+ ATOM_TOKENS = [
2958
+ T_ATOM,
2959
+ T_NUMBER,
2960
+ T_NIL,
2961
+ T_LBRA,
2962
+ T_RBRA,
2963
+ T_PLUS
2964
+ ]
2965
+
2966
+ def atom_token?(token)
2967
+ return ATOM_TOKENS.include?(token.symbol)
2968
+ end
2969
+
2970
+ def number
2971
+ token = lookahead
2972
+ if token.symbol == T_NIL
2973
+ shift_token
2974
+ return nil
2975
+ end
2976
+ token = match(T_NUMBER)
2977
+ return token.value.to_i
2978
+ end
2979
+
2980
+ def nil_atom
2981
+ match(T_NIL)
2982
+ return nil
2983
+ end
2984
+
2985
+ def match(*args)
2986
+ token = lookahead
2987
+ unless args.include?(token.symbol)
2988
+ parse_error('unexpected token %s (expected %s)',
2989
+ token.symbol.id2name,
2990
+ args.collect {|i| i.id2name}.join(" or "))
2991
+ end
2992
+ shift_token
2993
+ return token
2994
+ end
2995
+
2996
+ def lookahead
2997
+ unless @token
2998
+ @token = next_token
2999
+ end
3000
+ return @token
3001
+ end
3002
+
3003
+ def shift_token
3004
+ @token = nil
3005
+ end
3006
+
3007
+ def next_token
3008
+ case @lex_state
3009
+ when EXPR_BEG
3010
+ if @str.index(BEG_REGEXP, @pos)
3011
+ @pos = $~.end(0)
3012
+ if $1
3013
+ return Token.new(T_SPACE, $+)
3014
+ elsif $2
3015
+ return Token.new(T_NIL, $+)
3016
+ elsif $3
3017
+ return Token.new(T_NUMBER, $+)
3018
+ elsif $4
3019
+ return Token.new(T_ATOM, $+)
3020
+ elsif $5
3021
+ return Token.new(T_QUOTED,
3022
+ $+.gsub(/\\(["\\])/n, "\\1"))
3023
+ elsif $6
3024
+ return Token.new(T_LPAR, $+)
3025
+ elsif $7
3026
+ return Token.new(T_RPAR, $+)
3027
+ elsif $8
3028
+ return Token.new(T_BSLASH, $+)
3029
+ elsif $9
3030
+ return Token.new(T_STAR, $+)
3031
+ elsif $10
3032
+ return Token.new(T_LBRA, $+)
3033
+ elsif $11
3034
+ return Token.new(T_RBRA, $+)
3035
+ elsif $12
3036
+ len = $+.to_i
3037
+ val = @str[@pos, len]
3038
+ @pos += len
3039
+ return Token.new(T_LITERAL, val)
3040
+ elsif $13
3041
+ return Token.new(T_PLUS, $+)
3042
+ elsif $14
3043
+ return Token.new(T_PERCENT, $+)
3044
+ elsif $15
3045
+ return Token.new(T_CRLF, $+)
3046
+ elsif $16
3047
+ return Token.new(T_EOF, $+)
3048
+ else
3049
+ parse_error("[Net::IMAP BUG] BEG_REGEXP is invalid")
3050
+ end
3051
+ else
3052
+ @str.index(/\S*/n, @pos)
3053
+ parse_error("unknown token - %s", $&.dump)
3054
+ end
3055
+ when EXPR_DATA
3056
+ if @str.index(DATA_REGEXP, @pos)
3057
+ @pos = $~.end(0)
3058
+ if $1
3059
+ return Token.new(T_SPACE, $+)
3060
+ elsif $2
3061
+ return Token.new(T_NIL, $+)
3062
+ elsif $3
3063
+ return Token.new(T_NUMBER, $+)
3064
+ elsif $4
3065
+ return Token.new(T_QUOTED,
3066
+ $+.gsub(/\\(["\\])/n, "\\1"))
3067
+ elsif $5
3068
+ len = $+.to_i
3069
+ val = @str[@pos, len]
3070
+ @pos += len
3071
+ return Token.new(T_LITERAL, val)
3072
+ elsif $6
3073
+ return Token.new(T_LPAR, $+)
3074
+ elsif $7
3075
+ return Token.new(T_RPAR, $+)
3076
+ else
3077
+ parse_error("[Net::IMAP BUG] DATA_REGEXP is invalid")
3078
+ end
3079
+ else
3080
+ @str.index(/\S*/n, @pos)
3081
+ parse_error("unknown token - %s", $&.dump)
3082
+ end
3083
+ when EXPR_TEXT
3084
+ if @str.index(TEXT_REGEXP, @pos)
3085
+ @pos = $~.end(0)
3086
+ if $1
3087
+ return Token.new(T_TEXT, $+)
3088
+ else
3089
+ parse_error("[Net::IMAP BUG] TEXT_REGEXP is invalid")
3090
+ end
3091
+ else
3092
+ @str.index(/\S*/n, @pos)
3093
+ parse_error("unknown token - %s", $&.dump)
3094
+ end
3095
+ when EXPR_RTEXT
3096
+ if @str.index(RTEXT_REGEXP, @pos)
3097
+ @pos = $~.end(0)
3098
+ if $1
3099
+ return Token.new(T_LBRA, $+)
3100
+ elsif $2
3101
+ return Token.new(T_TEXT, $+)
3102
+ else
3103
+ parse_error("[Net::IMAP BUG] RTEXT_REGEXP is invalid")
3104
+ end
3105
+ else
3106
+ @str.index(/\S*/n, @pos)
3107
+ parse_error("unknown token - %s", $&.dump)
3108
+ end
3109
+ when EXPR_CTEXT
3110
+ if @str.index(CTEXT_REGEXP, @pos)
3111
+ @pos = $~.end(0)
3112
+ if $1
3113
+ return Token.new(T_TEXT, $+)
3114
+ else
3115
+ parse_error("[Net::IMAP BUG] CTEXT_REGEXP is invalid")
3116
+ end
3117
+ else
3118
+ @str.index(/\S*/n, @pos) #/
3119
+ parse_error("unknown token - %s", $&.dump)
3120
+ end
3121
+ else
3122
+ parse_error("invalid @lex_state - %s", @lex_state.inspect)
3123
+ end
3124
+ end
3125
+
3126
+ def parse_error(fmt, *args)
3127
+ if IMAP.debug
3128
+ $stderr.printf("@str: %s\n", @str.dump)
3129
+ $stderr.printf("@pos: %d\n", @pos)
3130
+ $stderr.printf("@lex_state: %s\n", @lex_state)
3131
+ if @token
3132
+ $stderr.printf("@token.symbol: %s\n", @token.symbol)
3133
+ $stderr.printf("@token.value: %s\n", @token.value.inspect)
3134
+ end
3135
+ end
3136
+ raise ResponseParseError, format(fmt, *args)
3137
+ end
3138
+ end
3139
+
3140
+ # Authenticator for the "LOGIN" authentication type. See
3141
+ # #authenticate().
3142
+ class LoginAuthenticator
3143
+ def process(data)
3144
+ case @state
3145
+ when STATE_USER
3146
+ @state = STATE_PASSWORD
3147
+ return @user
3148
+ when STATE_PASSWORD
3149
+ return @password
3150
+ end
3151
+ end
3152
+
3153
+ private
3154
+
3155
+ STATE_USER = :USER
3156
+ STATE_PASSWORD = :PASSWORD
3157
+
3158
+ def initialize(user, password)
3159
+ @user = user
3160
+ @password = password
3161
+ @state = STATE_USER
3162
+ end
3163
+ end
3164
+ add_authenticator "LOGIN", LoginAuthenticator
3165
+
3166
+ # Authenticator for the "PLAIN" authentication type. See
3167
+ # #authenticate().
3168
+ class PlainAuthenticator
3169
+ def process(data)
3170
+ return "\0#{@user}\0#{@password}"
3171
+ end
3172
+
3173
+ private
3174
+
3175
+ def initialize(user, password)
3176
+ @user = user
3177
+ @password = password
3178
+ end
3179
+ end
3180
+ add_authenticator "PLAIN", PlainAuthenticator
3181
+
3182
+ # Authenticator for the "CRAM-MD5" authentication type. See
3183
+ # #authenticate().
3184
+ class CramMD5Authenticator
3185
+ def process(challenge)
3186
+ digest = hmac_md5(challenge, @password)
3187
+ return @user + " " + digest
3188
+ end
3189
+
3190
+ private
3191
+
3192
+ def initialize(user, password)
3193
+ @user = user
3194
+ @password = password
3195
+ end
3196
+
3197
+ def hmac_md5(text, key)
3198
+ if key.length > 64
3199
+ key = Digest::MD5.digest(key)
3200
+ end
3201
+
3202
+ k_ipad = key + "\0" * (64 - key.length)
3203
+ k_opad = key + "\0" * (64 - key.length)
3204
+ for i in 0..63
3205
+ k_ipad[i] = (k_ipad[i].ord ^ 0x36).chr
3206
+ k_opad[i] = (k_opad[i].ord ^ 0x5c).chr
3207
+ end
3208
+
3209
+ digest = Digest::MD5.digest(k_ipad + text)
3210
+
3211
+ return Digest::MD5.hexdigest(k_opad + digest)
3212
+ end
3213
+ end
3214
+ add_authenticator "CRAM-MD5", CramMD5Authenticator
3215
+
3216
+ # Authenticator for the "DIGEST-MD5" authentication type. See
3217
+ # #authenticate().
3218
+ class DigestMD5Authenticator
3219
+ def process(challenge)
3220
+ case @stage
3221
+ when STAGE_ONE
3222
+ @stage = STAGE_TWO
3223
+ sparams = {}
3224
+ c = StringScanner.new(challenge)
3225
+ while c.scan(/(?:\s*,)?\s*(\w+)=("(?:[^\\"]+|\\.)*"|[^,]+)\s*/)
3226
+ k, v = c[1], c[2]
3227
+ if v =~ /^"(.*)"$/
3228
+ v = $1
3229
+ if v =~ /,/
3230
+ v = v.split(',')
3231
+ end
3232
+ end
3233
+ sparams[k] = v
3234
+ end
3235
+
3236
+ raise DataFormatError, "Bad Challenge: '#{challenge}'" unless c.rest.size == 0
3237
+ raise Error, "Server does not support auth (qop = #{sparams['qop'].join(',')})" unless sparams['qop'].include?("auth")
3238
+
3239
+ response = {
3240
+ :nonce => sparams['nonce'],
3241
+ :username => @user,
3242
+ :realm => sparams['realm'],
3243
+ :cnonce => Digest::MD5.hexdigest("%.15f:%.15f:%d" % [Time.now.to_f, rand, Process.pid.to_s]),
3244
+ :'digest-uri' => 'imap/' + sparams['realm'],
3245
+ :qop => 'auth',
3246
+ :maxbuf => 65535,
3247
+ :nc => "%08d" % nc(sparams['nonce']),
3248
+ :charset => sparams['charset'],
3249
+ }
3250
+
3251
+ response[:authzid] = @authname unless @authname.nil?
3252
+
3253
+ # now, the real thing
3254
+ a0 = Digest::MD5.digest( [ response.values_at(:username, :realm), @password ].join(':') )
3255
+
3256
+ a1 = [ a0, response.values_at(:nonce,:cnonce) ].join(':')
3257
+ a1 << ':' + response[:authzid] unless response[:authzid].nil?
3258
+
3259
+ a2 = "AUTHENTICATE:" + response[:'digest-uri']
3260
+ a2 << ":00000000000000000000000000000000" if response[:qop] and response[:qop] =~ /^auth-(?:conf|int)$/
3261
+
3262
+ response[:response] = Digest::MD5.hexdigest(
3263
+ [
3264
+ Digest::MD5.hexdigest(a1),
3265
+ response.values_at(:nonce, :nc, :cnonce, :qop),
3266
+ Digest::MD5.hexdigest(a2)
3267
+ ].join(':')
3268
+ )
3269
+
3270
+ return response.keys.map {|key| qdval(key.to_s, response[key]) }.join(',')
3271
+ when STAGE_TWO
3272
+ @stage = nil
3273
+ # if at the second stage, return an empty string
3274
+ if challenge =~ /rspauth=/
3275
+ return ''
3276
+ else
3277
+ raise ResponseParseError, challenge
3278
+ end
3279
+ else
3280
+ raise ResponseParseError, challenge
3281
+ end
3282
+ end
3283
+
3284
+ def initialize(user, password, authname = nil)
3285
+ @user, @password, @authname = user, password, authname
3286
+ @nc, @stage = {}, STAGE_ONE
3287
+ end
3288
+
3289
+ private
3290
+
3291
+ STAGE_ONE = :stage_one
3292
+ STAGE_TWO = :stage_two
3293
+
3294
+ def nc(nonce)
3295
+ if @nc.has_key? nonce
3296
+ @nc[nonce] = @nc[nonce] + 1
3297
+ else
3298
+ @nc[nonce] = 1
3299
+ end
3300
+ return @nc[nonce]
3301
+ end
3302
+
3303
+ # some responses need quoting
3304
+ def qdval(k, v)
3305
+ return if k.nil? or v.nil?
3306
+ if %w"username authzid realm nonce cnonce digest-uri qop".include? k
3307
+ v.gsub!(/([\\"])/, "\\\1")
3308
+ return '%s="%s"' % [k, v]
3309
+ else
3310
+ return '%s=%s' % [k, v]
3311
+ end
3312
+ end
3313
+ end
3314
+ add_authenticator "DIGEST-MD5", DigestMD5Authenticator
3315
+
3316
+ # Superclass of IMAP errors.
3317
+ class Error < StandardError
3318
+ end
3319
+
3320
+ # Error raised when data is in the incorrect format.
3321
+ class DataFormatError < Error
3322
+ end
3323
+
3324
+ # Error raised when a response from the server is non-parseable.
3325
+ class ResponseParseError < Error
3326
+ end
3327
+
3328
+ # Superclass of all errors used to encapsulate "fail" responses
3329
+ # from the server.
3330
+ class ResponseError < Error
3331
+ end
3332
+
3333
+ # Error raised upon a "NO" response from the server, indicating
3334
+ # that the client command could not be completed successfully.
3335
+ class NoResponseError < ResponseError
3336
+ end
3337
+
3338
+ # Error raised upon a "BAD" response from the server, indicating
3339
+ # that the client command violated the IMAP protocol, or an internal
3340
+ # server failure has occurred.
3341
+ class BadResponseError < ResponseError
3342
+ end
3343
+
3344
+ # Error raised upon a "BYE" response from the server, indicating
3345
+ # that the client is not being allowed to login, or has been timed
3346
+ # out due to inactivity.
3347
+ class ByeResponseError < ResponseError
3348
+ end
3349
+ end
3350
+ end
3351
+
3352
+ if __FILE__ == $0
3353
+ # :enddoc:
3354
+ require "getoptlong"
3355
+
3356
+ $stdout.sync = true
3357
+ $port = nil
3358
+ $user = ENV["USER"] || ENV["LOGNAME"]
3359
+ $auth = "login"
3360
+ $ssl = false
3361
+
3362
+ def usage
3363
+ $stderr.print <<EOF
3364
+ usage: #{$0} [options] <host>
3365
+
3366
+ --help print this message
3367
+ --port=PORT specifies port
3368
+ --user=USER specifies user
3369
+ --auth=AUTH specifies auth type
3370
+ --ssl use ssl
3371
+ EOF
3372
+ end
3373
+
3374
+ def get_password
3375
+ print "password: "
3376
+ system("stty", "-echo")
3377
+ begin
3378
+ return gets.chop
3379
+ ensure
3380
+ system("stty", "echo")
3381
+ print "\n"
3382
+ end
3383
+ end
3384
+
3385
+ def get_command
3386
+ printf("%s@%s> ", $user, $host)
3387
+ if line = gets
3388
+ return line.strip.split(/\s+/)
3389
+ else
3390
+ return nil
3391
+ end
3392
+ end
3393
+
3394
+ parser = GetoptLong.new
3395
+ parser.set_options(['--debug', GetoptLong::NO_ARGUMENT],
3396
+ ['--help', GetoptLong::NO_ARGUMENT],
3397
+ ['--port', GetoptLong::REQUIRED_ARGUMENT],
3398
+ ['--user', GetoptLong::REQUIRED_ARGUMENT],
3399
+ ['--auth', GetoptLong::REQUIRED_ARGUMENT],
3400
+ ['--ssl', GetoptLong::NO_ARGUMENT])
3401
+ begin
3402
+ parser.each_option do |name, arg|
3403
+ case name
3404
+ when "--port"
3405
+ $port = arg
3406
+ when "--user"
3407
+ $user = arg
3408
+ when "--auth"
3409
+ $auth = arg
3410
+ when "--ssl"
3411
+ $ssl = true
3412
+ when "--debug"
3413
+ Net::IMAP.debug = true
3414
+ when "--help"
3415
+ usage
3416
+ exit(1)
3417
+ end
3418
+ end
3419
+ rescue
3420
+ usage
3421
+ exit(1)
3422
+ end
3423
+
3424
+ $host = ARGV.shift
3425
+ unless $host
3426
+ usage
3427
+ exit(1)
3428
+ end
3429
+
3430
+ imap = Net::IMAP.new($host, :port => $port, :ssl => $ssl)
3431
+ begin
3432
+ password = get_password
3433
+ imap.authenticate($auth, $user, password)
3434
+ while true
3435
+ cmd, *args = get_command
3436
+ break unless cmd
3437
+ begin
3438
+ case cmd
3439
+ when "list"
3440
+ for mbox in imap.list("", args[0] || "*")
3441
+ if mbox.attr.include?(Net::IMAP::NOSELECT)
3442
+ prefix = "!"
3443
+ elsif mbox.attr.include?(Net::IMAP::MARKED)
3444
+ prefix = "*"
3445
+ else
3446
+ prefix = " "
3447
+ end
3448
+ print prefix, mbox.name, "\n"
3449
+ end
3450
+ when "select"
3451
+ imap.select(args[0] || "inbox")
3452
+ print "ok\n"
3453
+ when "close"
3454
+ imap.close
3455
+ print "ok\n"
3456
+ when "summary"
3457
+ unless messages = imap.responses["EXISTS"][-1]
3458
+ puts "not selected"
3459
+ next
3460
+ end
3461
+ if messages > 0
3462
+ for data in imap.fetch(1..-1, ["ENVELOPE"])
3463
+ print data.seqno, ": ", data.attr["ENVELOPE"].subject, "\n"
3464
+ end
3465
+ else
3466
+ puts "no message"
3467
+ end
3468
+ when "fetch"
3469
+ if args[0]
3470
+ data = imap.fetch(args[0].to_i, ["RFC822.HEADER", "RFC822.TEXT"])[0]
3471
+ puts data.attr["RFC822.HEADER"]
3472
+ puts data.attr["RFC822.TEXT"]
3473
+ else
3474
+ puts "missing argument"
3475
+ end
3476
+ when "logout", "exit", "quit"
3477
+ break
3478
+ when "help", "?"
3479
+ print <<EOF
3480
+ list [pattern] list mailboxes
3481
+ select [mailbox] select mailbox
3482
+ close close mailbox
3483
+ summary display summary
3484
+ fetch [msgno] display message
3485
+ logout logout
3486
+ help, ? display help message
3487
+ EOF
3488
+ else
3489
+ print "unknown command: ", cmd, "\n"
3490
+ end
3491
+ rescue Net::IMAP::Error
3492
+ puts $!
3493
+ end
3494
+ end
3495
+ ensure
3496
+ imap.logout
3497
+ imap.disconnect
3498
+ end
3499
+ end
3500
+