shoesgem 0.1424.0

Sign up to get free protection for your applications and to get access to all the features.
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
+