tmpmail 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. #!/usr/bin/env bash
  2. #
  3. # by Siddharth Dushantha 2020
  4. #
  5. # Dependencies: jq, curl, w3m
  6. #
  7. export LC_ALL=C
  8. export LC_CTYPE=C
  9. VERSION=1.0.8
  10. # By default 'tmpmail' uses 'w3m' as it's web browser to render
  11. # the HTML of the email
  12. BROWSER="w3m"
  13. # If the value is set to 'true' tmpmail will convert the HTML email
  14. # to raw text and send that to stdout
  15. RAW_TEXT=false
  16. # Everything related to 'tmpmail' will be stored in /tmp/tmpmail
  17. # so that the old emails and email addresses get cleared after
  18. # restarting the computer
  19. TMPMAIL_DIR="/tmp/tmpmail"
  20. # TMPMAIL_EMAIL_ADDRESS is where we store the temporary email address
  21. # that gets generated. This prevents the user from providing
  22. # the email address everytime they run tmpmail
  23. TMPMAIL_EMAIL_ADDRESS="$TMPMAIL_DIR/email_address"
  24. # tmpmail.html is where the email gets stored.
  25. # Even though the file ends with a .html extension, the raw text version of
  26. # the email will also be stored in this file so that w3m and other browsers
  27. # are able to open this file
  28. TMPMAIL_HTML_EMAIL="$TMPMAIL_DIR/tmpmail.html"
  29. usage() {
  30. # Using 'cat << EOF' we can easily output a multiline text. This is much
  31. # better than using 'echo' for each line or using '\n' to create a new line.
  32. cat <<EOF
  33. usage: tmpmail [-h] [--generate] [--browser BROWSER] [--recent] ID
  34. optional arguments:
  35. -h, --help Show this help message
  36. --version Print version
  37. -g, --generate Generate a new email address. You may aslo pass a custom username along with this flag
  38. -r, --recent View the most recent email
  39. -t, --text View the email as raw text, where all the HTML tags are removed
  40. -b, --browser Change the browser that is used to render the HTML of the email (default: w3m)
  41. EOF
  42. }
  43. has() {
  44. # Check if the user 'has' a command installed
  45. command -v "$1" >/dev/null 2>&1
  46. }
  47. generate_email_address() {
  48. # There are 2 ways which this function is called in this script.
  49. # [1] The user wants to generate a new email and runs 'tmpmail --generate'
  50. # [2] The user runs 'tmpmail' to check the inbox , but /tmp/tmpmail/email_address
  51. # is empty or nonexistant. Therefore a new email gets automatically
  52. # generated before showing the inbox. But of course the inbox will
  53. # be empty as the newly generated email address has not been
  54. # sent any emails.
  55. #
  56. # When the function 'generate_email()' is called with the arguement
  57. # 'true', it means that the function was called because the user
  58. # ran 'tmpmail --generate'.
  59. #
  60. # We need this variable so we can know whether or not we need to show the user
  61. # what the email was. <-- More about this can be found further down in this function.
  62. EXTERNALLY=${1:-false}
  63. CUSTOM=${2:-false}
  64. # This variable lets generate_email_address know if the user has provided a custom
  65. # email address which they want to use
  66. CUSTOM=${2:-false}
  67. # Generate a random email address.
  68. # This function is called whenever the user wants to generate a new email
  69. # address by running 'tmpmail --generate' or when the user runs 'tmpmail'
  70. # but /tmp/tmpmail/email_address is empty or nonexistent.
  71. #
  72. # We create a random username by taking the first 10 lines from /dev/random
  73. # and delete all the characters which are *not* lower case letters from A to Z.
  74. # So charcters such as dashes, periods, underscore, and numbers are all deleted,
  75. # giving us a text which only contains lower case letters form A to Z. We then take
  76. # the first 10 characters, which will be the username of the email address
  77. #
  78. # shellcheck disable=SC2018
  79. USERNAME=$(head /dev/urandom | tr -dc a-z | cut -c1-11)
  80. # This is an array of the valid TLDS which 1secmail provides.
  81. TLDS=(com net org)
  82. # Randomly pick one of the TLDS mentiond above.
  83. TLD=${TLDS[$RANDOM % ${#TLDS[@]}]}
  84. EMAIL_ADDRESS="$USERNAME@1secmail.$TLD"
  85. # If the user provided a custom email address then use that email address
  86. if [ "$CUSTOM" != false ]; then
  87. EMAIL_ADDRESS=$CUSTOM
  88. # Do a regex check to see if the email address provided by the user is a
  89. # valid email address
  90. echo "$EMAIL_ADDRESS" | grep -E "[a-z]+@1secmail\.(com|net|org)" >/dev/null
  91. # Get the exit status of the command above
  92. STATUS=$?
  93. if [ "$STATUS" -ne 0 ]; then
  94. echo "Error: Provided email is invalid. Must match [a-z]+@1secmail.(com|net|org)"
  95. exit 1
  96. fi
  97. fi
  98. # Save the generated email address to the $TMPMAIL_EMAIL_ADDRESS file
  99. # so that it can be whenever 'tmpmail' is run
  100. echo "$EMAIL_ADDRESS" >"$TMPMAIL_EMAIL_ADDRESS"
  101. # If this function was called because the user wanted to generate a new
  102. # email address, show them the email address
  103. [ "$EXTERNALLY" = true ] && cat "$TMPMAIL_EMAIL_ADDRESS"
  104. }
  105. get_email_address() {
  106. # This function is only called once and that is when this script
  107. # get executed. The output of this function gets stored in $EMAIL_ADDRESS
  108. #
  109. # If the file that contains the email address is empty,
  110. # that means we do not have an email address, so generate one.
  111. [ ! -s "$TMPMAIL_EMAIL_ADDRESS" ] && generate_email_address
  112. # Output the email address by getting the first line of $TMPMAIL_EMAIL
  113. head -n 1 "$TMPMAIL_EMAIL_ADDRESS"
  114. }
  115. list_emails() {
  116. # List all the received emails in a nicely formatted order
  117. #
  118. # Fetch the email data using 1secmail's API
  119. DATA=$(curl -sL "https://1secmail.com/api/v1/?action=getMessages&login=$USERNAME&domain=1secmail.$TLD")
  120. # Using 'jq' we get the length of the JSON data. From this we can determine whether or not
  121. # the email address has gotten any emails
  122. DATA_LENGTH=$(jq length <<<"$DATA")
  123. # We are showing what email address is currently being used
  124. # in case the user has forgotten what the email address was.
  125. echo -e "[ Inbox for $EMAIL_ADDRESS ]\n"
  126. # If the length of the data we got is 0, that means the email address
  127. # has not received any emails yet.
  128. [ "$DATA_LENGTH" -eq 0 ] && echo "No new mail" && exit
  129. # This is where we store all of our emails, which is then
  130. # displayed using 'column'
  131. INBOX=()
  132. # This for loop goes through each mail that have been received.
  133. #
  134. # Since we need to go through all the data, we need to tell our for loop
  135. # to loop from 1 to X, where X is legnth of the $DATA which contains all
  136. # the emails.
  137. #
  138. # Normally to loop from 1 to 5, we would use shell expansion and write:
  139. # for index in {1..5}; do
  140. # do_something
  141. # done
  142. #
  143. # But we a minor issue. We dont know what the final number is, and we are not allowed
  144. # use to variables in shell expansions like this:
  145. # {1..$X}
  146. #
  147. # where $X is the length of the $DATA.
  148. #
  149. # To fix this issue, we can use 'seq' which will allow us to create a sequence
  150. # from X to Y.
  151. # Example:
  152. # $ seq 1 5
  153. # 1
  154. # 2
  155. # 3
  156. # 4
  157. # 5
  158. #
  159. # We can then put those results into the foor loop
  160. for index in $(seq 1 "$DATA_LENGTH"); do
  161. # Since arrays in JSON data start at 0, we must subtract
  162. # the value of $index by 1 so that we dont miss one of the
  163. # emails in the array
  164. MAIL_DATA=$(jq -r ".[$index-1]" <<<"$DATA")
  165. ID=$(jq -r ".id" <<<"$MAIL_DATA")
  166. FROM=$(jq -r ".from" <<<"$MAIL_DATA")
  167. SUBJECT=$(jq -r ".subject" <<<"$MAIL_DATA")
  168. # The '||' are used as a divideder for 'column'. 'column' will use this divider as
  169. # a point of reference to create the division. By default 'column' uses a blank space
  170. # but that would not work in our case as the email subject could have multiple white spaces
  171. # and 'column' would split the words that are seperated by white space, in different columns.
  172. INBOX+=("$ID ||$FROM ||$SUBJECT")
  173. done
  174. # Show the emails cleanly
  175. column -t -s "||" < <(printf '%s\n' "${INBOX[@]}")
  176. }
  177. view_email() {
  178. # View an email by providing it's ID
  179. #
  180. # The first argument provided to this function will be the ID of the email
  181. # that has been received
  182. EMAIL_ID="$1"
  183. DATA=$(curl -sL "https://www.1secmail.com/api/v1/?action=readMessage&login=$USERNAME&domain=1secmail.$TLD&id=$EMAIL_ID")
  184. # After the data is retrieved using the API, we have to check if we got any emails.
  185. # Luckly 1secmail's API is not complicated and returns 'Message not found' as plain text
  186. # if our email address as not received any emails.
  187. # If we the error message from the API just quit because there is nothing to do
  188. [[ "$DATA" == "Message not found" ]] && echo "Message not found" && exit 1
  189. # We pass the $DATA to 'jq' which extracts the values
  190. FROM=$(jq -r ".from" <<<"$DATA")
  191. SUBJECT=$(jq -r ".subject" <<<"$DATA")
  192. HTML_BODY=$(jq -r ".htmlBody" <<<"$DATA")
  193. # If you get an email that is in pure text, the .htmlBody field will be empty and
  194. # we will need to get the content from .textBody instead
  195. [ -z "$HTML_BODY" ] && HTML_BODY="<pre>$(jq -r ".textBody" <<<"$DATA")</pre>"
  196. # Create the HTML with all the information that is relevant and then
  197. # assigning that HTML to the variable HTML_MAIL. This is the best method
  198. # to create a multiline variable
  199. read -r -d '' HTML_MAIL <<EOF
  200. <pre><b>To: </b>$EMAIL_ADDRESS
  201. <b>From: </b>$FROM
  202. <b>Subject: </b>$SUBJECT</pre>
  203. $HTML_BODY
  204. EOF
  205. # Save the $HTML_MAIL into $TMPMAIL_HTML_EMAIL
  206. echo "$HTML_MAIL" >"$TMPMAIL_HTML_EMAIL"
  207. # If the '--text' flag is used, then use 'w3m' to convert the HTML of
  208. # the email to pure text by removing all the HTML tags
  209. [ "$RAW_TEXT" = true ] && w3m -dump "$TMPMAIL_HTML_EMAIL" && exit
  210. # Open up the HTML file using $BROWSER. By default,
  211. # this will be 'w3m'.
  212. $BROWSER "$TMPMAIL_HTML_EMAIL"
  213. }
  214. view_recent_email() {
  215. # View the most recent email.
  216. #
  217. # This is done by listing all the received email like you
  218. # normally see on the terminal when running 'tmpmail'.
  219. # We then grab the ID of the most recent
  220. # email, which the first line.
  221. MAIL_ID=$(list_emails | head -3 | tail -1 | cut -d' ' -f 1)
  222. view_email "$MAIL_ID"
  223. }
  224. main() {
  225. # Iterate of the array of dependencies and check if the user has them installed
  226. dependencies=(jq w3m curl)
  227. for dependency in "${dependencies[@]}"; do
  228. if ! has "$dependency"; then
  229. echo "Error: Could not find '${dependency}', is it installed?" >&2
  230. exit 1
  231. fi
  232. done
  233. # Create the $TMPMAIL_DIR directory and dont throw any errors
  234. # if it already exists
  235. mkdir -p "$TMPMAIL_DIR"
  236. # Get the email address and save the value to the EMAIL_ADDRESS variable
  237. EMAIL_ADDRESS="$(get_email_address)"
  238. # ${VAR#PATTERN} Removes shortest match of pattern from start of a string.
  239. # In this case, it takes the EMAIL_ADDRESS and removed everything after
  240. # the '@' symbol which gives us the username.
  241. USERNAME=${EMAIL_ADDRESS%@*}
  242. # ${VAR%PATTERN} Remove shortest match of pattern from end of a string.
  243. # In this case, it takes the EMAIL_ADDRESS and removes everything until the
  244. # period '.' which gives us the TLD
  245. TLD=${EMAIL_ADDRESS#*.}
  246. # If no arguments are provided just the emails
  247. [[ $# -eq 0 ]] && list_emails && exit
  248. while [[ "$1" ]]; do
  249. case "$1" in
  250. --help | -h) usage && exit ;;
  251. --generate | -g) generate_email_address true "$2" && exit ;;
  252. --browser | -b) BROWSER="$2" ;;
  253. --text | -t) RAW_TEXT=true ;;
  254. --version) echo "$VERSION" && exit ;;
  255. --recent | -r) view_recent_email && exit ;;
  256. *[0-9]*)
  257. # If the user provides number as an argument,
  258. # assume its the ID of an email and try getting
  259. # the email that belongs to the ID
  260. view_email "$1" && exit
  261. ;;
  262. -*) echo "Error: option $1 does not exist" ;;
  263. esac
  264. shift
  265. done
  266. }
  267. main "$@"