imap.lua 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. ---
  2. -- Working Example: https://www.youtube.com/watch?v=PDxTR_KJLhc
  3. -- IMPORTANT: run node.compile("imap.lua") after uploading this script
  4. -- to create a compiled module. Then run file.remove("imap.lua")
  5. -- @name imap
  6. -- @description An IMAP 4rev1 module that can be used to read email.
  7. -- Tested on NodeMCU 0.9.5 build 20150213.
  8. -- @date March 12, 2015
  9. -- @author Miguel
  10. -- GitHub: https://github.com/AllAboutEE
  11. -- YouTube: https://www.youtube.com/user/AllAboutEE
  12. -- Website: http://AllAboutEE.com
  13. --
  14. -- Visit the following URLs to learn more about IMAP:
  15. -- "How to test an IMAP server by using telnet" http://www.anta.net/misc/telnet-troubleshooting/imap.shtml
  16. -- "RFC 2060 - Internet Message Access Protocol - Version 4rev1" http://www.faqs.org/rfcs/rfc2060.html
  17. -------------------------------------------------------------------------------------------------------------
  18. local moduleName = ...
  19. local M = {}
  20. _G[moduleName] = M
  21. local USERNAME = ""
  22. local PASSWORD = ""
  23. local TAG = ""
  24. local DEBUG = false
  25. local body = "" -- used to store an email's body / main text
  26. local header = "" -- used to store an email's last requested header field e.g. SUBJECT, FROM, DATA etc.
  27. local most_recent_num = 1 -- used to store the latest/newest email number/id
  28. local response_processed = false -- used to know if the last IMAP response has been processed
  29. ---
  30. -- @name response_processed
  31. -- @returns The response process status of the last IMAP command sent
  32. function M.response_processed()
  33. return response_processed
  34. end
  35. ---
  36. -- @name display
  37. -- @description A generic IMAP response processing function.
  38. -- Can display the IMAP response if DEBUG is set to true.
  39. -- Sets the response processed variable to true when the string "complete"
  40. -- is found in the IMAP reply/response
  41. local function display(socket, response) -- luacheck: no unused
  42. -- If debuggins is enabled print the IMAP response
  43. if(DEBUG) then
  44. print(response)
  45. end
  46. -- Some IMAP responses are long enough that they will cause the display
  47. -- function to be called several times. One thing is certain, IMAP will replay with
  48. -- "<tag> OK <command> complete" when it's done sending data back.
  49. if(string.match(response,'complete') ~= nil) then
  50. response_processed = true
  51. end
  52. end
  53. ---
  54. -- @name config
  55. -- @description Initiates the IMAP settings
  56. function M.config(username, password, tag, debug)
  57. USERNAME = username
  58. PASSWORD = password
  59. TAG = tag
  60. DEBUG = debug
  61. end
  62. ---
  63. -- @name login
  64. -- @descrpiton Logs into a new email session
  65. function M.login(socket)
  66. response_processed = false -- we are sending a new command
  67. -- which means that the response for it has not been processed
  68. socket:send(TAG .. " LOGIN " .. USERNAME .. " " .. PASSWORD .. "\r\n")
  69. socket:on("receive",display)
  70. end
  71. ---
  72. -- @name get_most_recent_num
  73. -- @returns The most recent email number. Should only be called after examine()
  74. function M.get_most_recent_num()
  75. return most_recent_num
  76. end
  77. ---
  78. -- @name set_most_recent_num
  79. -- @description Gets the most recent email number from the EXAMINE command.
  80. -- i.e. if EXAMINE returns "* 4 EXISTS" this means that there are 4 emails,
  81. -- so the latest/newest will be identified by the number 4
  82. local function set_most_recent_num(socket, response) -- luacheck: no unused
  83. if(DEBUG) then
  84. print(response)
  85. end
  86. local _, _, num = string.find(response,"([0-9]+) EXISTS") -- the _ and _ keep the index of the string found
  87. -- but we don't care about that.
  88. if(num~=nil) then
  89. most_recent_num = num
  90. end
  91. if(string.match(response,'complete') ~= nil) then
  92. response_processed = true
  93. end
  94. end
  95. ---
  96. -- @name examine
  97. -- @description IMAP examines the given mailbox/folder. Sends the IMAP EXAMINE command
  98. function M.examine(socket, mailbox)
  99. response_processed = false
  100. socket:send(TAG .. " EXAMINE " .. mailbox .. "\r\n")
  101. socket:on("receive",set_most_recent_num)
  102. end
  103. ---
  104. -- @name get_header
  105. -- @returns The last fetched header field
  106. function M.get_header()
  107. return header
  108. end
  109. ---
  110. -- @name set_header
  111. -- @description Records the IMAP header field response in a variable
  112. -- so that it may be read later
  113. local function set_header(socket, response) -- luacheck: no unused
  114. if(DEBUG) then
  115. print(response)
  116. end
  117. header = header .. response
  118. if(string.match(response,'complete') ~= nil) then
  119. response_processed = true
  120. end
  121. end
  122. ---
  123. -- @name fetch_header
  124. -- @description Fetches an emails header field e.g. SUBJECT, FROM, DATE
  125. -- @param socket The IMAP socket to use
  126. -- @param msg_number The email number to read e.g. 1 will read fetch the latest/newest email
  127. -- @param field A header field such as SUBJECT, FROM, or DATE
  128. function M.fetch_header(socket, msg_number, field)
  129. header = "" -- we are getting a new header so clear this variable
  130. response_processed = false
  131. socket:send(TAG .. " FETCH " .. msg_number .. " BODY[HEADER.FIELDS (" .. field .. ")]\r\n")
  132. socket:on("receive",set_header)
  133. end
  134. ---
  135. -- @name get_body
  136. -- @return The last email read's body
  137. function M.get_body()
  138. return body
  139. end
  140. ---
  141. -- @name set_body
  142. -- @description Records the IMAP body response in a variable
  143. -- so that it may be read later
  144. local function set_body(_, response)
  145. if(DEBUG) then
  146. print(response)
  147. end
  148. body = body .. response
  149. if(string.match(response,'complete') ~= nil) then
  150. response_processed = true
  151. end
  152. end
  153. ---
  154. -- @name fetch_body_plain_text
  155. -- @description Sends the IMAP command to fetch a plain text version of the email's body
  156. -- @param socket The IMAP socket to use
  157. -- @param msg_number The email number to obtain e.g. 1 will obtain the latest email
  158. function M.fetch_body_plain_text(socket, msg_number)
  159. response_processed = false
  160. body = "" -- clear the body variable since we'll be fetching a new email
  161. socket:send(TAG .. " FETCH " .. msg_number .. " BODY[1]\r\n")
  162. socket:on("receive",set_body)
  163. end
  164. ---
  165. -- @name logout
  166. -- @description Sends the IMAP command to logout of the email session
  167. function M.logout(socket)
  168. response_processed = false
  169. socket:send(TAG .. " LOGOUT\r\n")
  170. socket:on("receive",display)
  171. end