-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmailScript.sh
More file actions
393 lines (347 loc) · 13.4 KB
/
mailScript.sh
File metadata and controls
393 lines (347 loc) · 13.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
#!/bin/bash
#
# mailScript.sh - A script to setup and send messages through SMTP
#
# Changes:
#
# v1.0.0
# - Tested, working, and ready for release! Not happy about some of my choices, but damn it, it works now!
# - After functional testing, cleartext is the way to go. Just do your best to obfuscate the password
# - Forgot about local function variables, added shift outside the processArgs loop
# - Added some code for a "txt" mode, for sending to phones. Useless for now, will likely require a function in the future. Proof-read your messages for now!
#
# v0.2.2
# - After research and some considerations, encryption may not be the way to go.
# - Instead, warning user to use 2FA, or a dummy email account
#
# v0.2.1
# - Added correct number of spaces for output to config file
# - Other minor output changes
# - Fixed lines with tee to redirect stdout to /dev/null
# - Added tail to name calculation in createConfig()
# - Learned that it meant local used with gpg; can't get gpg to work though, so might find a different crypto program
#
# v0.2.0
# - Script needs to be tested, but everything should be ready for use now
# - msmtp cannot send files. So, until further notice, ATTACHMENTS WILL NOT WORK. Will be implemented soon (after release)
# - Added -g option for global config
#
# v0.1.0
# - Untested, but createConfig() is ready
#
# v0.0.2
# - More work on config
# - For now, config will now exit if not in linux-pref/
#
# v0.0.1
# - Initial version
# - Got displayHelp() and processArgs() ready for everything else
#
# TODO:
# - Include options to change defaults
# ~ e.g. Load default config, then overwrite SMTP address, or port, etc
# ~ Not too many use cases, and too much to code for now, so maybe in a later release
# - Accept piped input
# ~ Looked easy at first, but it's not. Save it for a later release.
# - Make attachments comma-delimited
# ~ Can't imagine sending more than one attachment at once, but that's not the point. Always be dynamic as possible!
# - High priotity email
# - Make config runnable from anywhere
# - Add default port for manual input
# - Add ability to send locally
# ~ Similar to sendmail for cron jobs, etc
# - Use mutt for attachments
# ~ Should be a way to convert necessary options from msmtp config to mutt config
#
# v1.0.0, 10 Mar. 2018, 21:47 PST
### Variables
longName="mailScript"
shortName="ms"
attachmentSize=0 # Total attachment size, in bytes
maxAttachmentSize=20000000 # Maximum size of all attachements, in bytes
configLocation="$HOME/.msConfig.conf" # Default location to check
#gpgFile="$HOME"/.ms-cred.gpg # Default location for password file
declare -a attachments # For when the user sends attachments
subject="Message from $(whoami 2>/dev/null)" # Subject used when sending emails
### Functions
if [[ -f commonFunctions.sh ]]; then
source commonFunctions.sh
elif [[ -f /usr/share/commonFunctions.sh ]]; then
source /usr/share/commonFunctions.sh
else
echo "commonFunctions.sh could not be located!"
# Comment/uncomment below depending on if script actually uses common functions
echo "Script will now exit, please put file in same directory as script, or link to /usr/share!"
exit 1
fi
function displayHelp() {
read -d '' helpVar <<"endHelp"
Usage: mailScript.sh [options] [emailSubject] <emailAddress> <messageContents OR message.txt>
Options:
-h | --help : Display this help message and exit
-v | --verbose : Prints verbose debug information. MUST be the first argument!
-s | --setup [file] : Initiates setup of mailScript, then exits. Will optionally write config to file
-c | --config <file> : Use given config file to run mailScript
-g | --global : Force use of the global config, if it exists.
-a | --attachment <file> : Attach a file to the email. Use as many times as necessary.
By default, script will use config file at $HOME/.msConfig.conf
If not found, will try /usr/share/.msConfig.conf
When using a file, the whole file will be sent as text, not as an attachment
Email subject not required, defaults to "Message from {username}"
endHelp
echo "$helpVar"
}
function processArgs() {
if [[ $# -lt 1 ]]; then
debug "l2" "ERROR: No arguments given! Please fix and re-run"
displayHelp
exit 1
fi
loopFlag=0
while [[ $loopFlag -eq 0 ]]; do
key="$1"
if [[ "$key" == *@* ]]; then
# Assume we have reached the email address; set and move on
sendToAddr="$key"
shift # Everything else is the message contents
message="$@"
return 0
elif [[ "$2" == *@* && "$key" != -* ]]; then
# Optional subject
subject="$key"
sendToAddr="$2"
shift
shift
message="$@"
debug "INFO: Subject given: $subject"
return 0
fi
case "$key" in
-h|--help)
displayHelp
exit 0
;;
-s|--setup)
debug "INFO: Generating config at user request!"
if [[ ! -z $2 ]]; then
configLocation="$2"
shift
fi
createConfig
exit "$?"
;;
-c|--config)
if [[ -z $2 ]]; then
debug "l2" "ERROR: No config file given with option $key! Please fix and re-run!"
displayHelp
exit 1
elif [[ ! -e "$2" ]]; then
debug "l2" "ERROR: Given file $2 is not readable, or does not exist! Please fix and re-run!"
displayHelp
exit 1
fi
configLocation="$2"
debug "INFO: Using user-specified config at $configLocation"
shift
;;
-g|--global)
if [[ ! -e /usr/share/.msConfig.conf ]]; then
debug "l2" "FATAL: No global configuration found! Please run setup and try again!"
exit 1
else
debug "WARN: Using global config!"
configLocation=/usr/share/.msConfig.conf
fi
;;
-a|--attachment)
debug "l3" "WARN: Attachments not supported, check for a future release!"
if [[ -z $2 ]]; then
debug "l2" "ERROR: No attachment given with option $key! Please fix and re-run"
displayHelp
exit 1
elif [[ ! -e "$2" || -d "$2" ]]; then
debug "l2" "ERROR: Attachment $2 either does not exist, or is not a file! Please fix and re-run!"
displayHelp
exit 1
fi
attachments+=("$2")
((attachmentSize+=$(stat --printf="%s" "$2"))) # Get size of file and add to attachmentSize
shift
;;
*)
debug "l2" "ERROR: Unknown option given: $key! Please fix and re-run"
displayHelp
exit 1
;;
esac
shift
done
}
# Creates config file at $configLocation
function createConfig() {
# Make sure folder is linux-pref/
if [[ "$(pwd)" != *linux-pref ]]; then
debug "l2" "FATAL: Please run $longName setup from linux-pref/ directory! Exiting..."
exit 1
fi
# First, check if config location is free
if [[ -e "$configLocation" ]]; then
debug "l3" "ERROR: Config file at $configLocation already exists!"
getUserAnswer "Would you like to overwrite this file?"
if [[ $? -eq 1 ]]; then
debug "WARN: User chose not to overwirte config, exiting..."
exit 1
fi
# else, move on. Backup config, just in case
debug "INFO: Backing up $configLocation to $configLocation.bak!"
mv "$configLocation" "$configLocation".bak
fi
debug "l2" "INFO: Creating config! Can be found later at $configLocation!"
#touch "$configLocation"
# Enter email, then search for settings
until [[ "$emailAddr" == *@*.* ]]; do
read -p "Please enter the email you would like to use: " emailAddr
emailAddr="$(echo "$emailAddr" | awk '{print tolower($0)}')" # Convert email address toLower, email is case-insensitive
done
domain="$(echo "$emailAddr" | cut -d'@' -f2)"
accountName="$(echo "$domain" | cut -d'.' -f1)" # Used for account info output
subCount="$(echo "$domain" | sed -e 's/\(.\)/\n/g' | grep . | wc -l)" # Gets number of dots in domain, indicating subdomains
found=0
# Check subdomains
while [[ "$subCount" -gt 1 && "$found" -eq 0 ]]; do
if [[ -f mailScriptSettings/"$domain" ]]; then
found=1
else
domain="$(echo "$domain" | cut -d'.' -f1 --complement)"
fi
done
# Assume domain is now <domain>.<tld>
if [[ $found -eq 0 ]]; then
if [[ -f mailScriptSettings/"$domain" ]]; then
found=1
fi
fi
# I hate the way I did these next few if statements, hopefully I can come back and fix them later
origDomain="$(echo "$emailAddr" | cut -d'@' -f2)"
origDomain="$(echo "$origDomain" | awk '{print tolower($0)}')"
if [[ $found -ne 0 && "$domain" != "$origDomain" ]]; then
debug "l2" "WARN: Settings for $origDomain not found, but exist for subdomain $domain!"
getUserAnswer "No gurantees it will work, but would you like to use subdomain settings?"
if [[ $? -eq 0 ]]; then
debug "WARN: Attempting to use subdomain settings!"
#debug "INFO: Settings found for domain $domain!"
cp mailScriptSettings/"$domain" "$configLocation"
fi
elif [[ $found -ne 0 ]]; then
debug "l2" "INFO: Settings found for domain $domain! Copying and continuing..."
cp mailScriptSettings/"$domain" "$configLocation"
else
domain="$origDomain"
debug "WARN: No settings found for $domain or subdomains, asking for manual input"
announce "No settings found for $domain domain!" "Please find them online, and enter settings into the following prompts."
# get SMTP address
read -p "Please enter the SMTP address: " smtpAddr # Just gonna have to assume this is correct
#getUserAnswer "Is this TLS or SSL? Answer [y]es for TLS, [n]o for SSL."
#enc="$?" # 0 for TLS, 1 for SSL
# get port number
portNum="abc" # In order for loop to work
until [[ $portNum -eq $portNum ]]; do # Just to make sure port is only numbers
read -p "What port number does this use? " portNum
done
cp mailScriptSettings/default "$configLocation"
printf "\# %s\naccount %s\nhost %s\nport %s\n" "$accountName" "$accountName" "$smtpAddr" "$portNum" | tee -a "$configLocation" 1>/dev/null
fi
# determine outgoing address
getUserAnswer "Would you like to use a different 'from' address than $emailAddr?"
if [[ $? -eq 0 ]]; then
until [[ $fromAddr == *@* ]]; do
read -p "Please enter the from address: " fromAddr
done
else
fromAddr="$emailAddr"
fi
printf "from %s\nuser %s\npassword %s\n" "$fromAddr" "$emailAddr" "$pass" | tee -a "$configLocation" 1>/dev/null
# Now, for the password
announce "Next, you will be asked to enter a password." "It is highly recommended to create an app-specific password for this!" "This is very necessary with systems using 2FA!" "Or, if possible, use a dummy account or an open relay!"
stty -echo # Trick I found to make stdin invisible
read -p "Please enter the password now: " pass
stty echo
#printf "%s" "$pass" > "$gpgFile"
# Set the account default
name="$(cat "$configLocation" | grep account | tail -n1 | rev | cut -d' ' -f1 | rev)"
printf "\naccount default : %s\n" "$name" | tee -a "$configLocation" 1>/dev/null
chmod 600 "$gpgFile" # Keeps file as hidden as possible
# Done at this point. But now ask user if you want to make this global
getUserAnswer "Would you like to make this configuration global? NOTE: This will require sudo"
if [[ $? -eq 0 ]]; then
debug "WARN: Making config global per user request"
sudo ln -s "$configLocation" /usr/share/.msConfig.conf
announce "NOTE: User config will always take priority over global config!" "You can override this with the -u option!"
fi
# Wrap things up and exit
debug "l2" "INFO: Done setting up config at $configLocation!"
getUserAnswer "n" "It is recommended to rebot to clear memory of cleartext password. Would you like to do this now?"
if [[ $? -eq 0 ]]; then
sudo reboot
fi
return 0
}
### Main Script
checkRequirements "msmtp" # The SMTP client I have chosen to use for this project. Mostly due to better documentation.
processArgs "$@"
# Make sure config exists, and use global if not found
if [[ ! -f "$configLocation" ]]; then
debug "l2" "ERROR: Given config at $configLocation not found! Trying global config..."
if [[ -f /usr/share/.msConfig.conf ]]; then
debug "l2" "WARN: Global config found, attempting to use!"
configLocation=/usr/share/.msConfig.conf
else
debug "l2" "FATAL: No config found or given! Please fix and re-run! Exiting..."
sleep 2
displayHelp
exit 1
fi
fi
# Make sure we somehow didn't get to this step without valid send-to addr
if [[ -z $sendToAddr ]]; then
debug "l2" "FATAL: Send-to address not found! Please fix call and re-run! Exiting..."
exit 1
elif [[ "$sendToAddr" != *@*.* ]]; then
debug "l2" "WARN: $sendToAddr does not follow standard convention of *@*.* . Email might not send, attempting anyways..."
fi
# Make sure message is set
if [[ -z $message ]]; then
debug "l2" "ERROR: Message is empty, cannot send! Exiting..."
exit 1
fi
# Almost ready to send. Check if "message" is a text file
if [[ -f "$1" ]]; then
tmp="$(file -i "$1" | grep text)"
if [[ -z $tmp ]]; then
# $1 is a file, but not a text file. Assume message?
debug "l2" "ERROR: $1 is a file, but not a text file... Assuming part of message, confinuing."
else
debug "INFO: Text file $1 given for message body, using it!"
mode="message"
fi
fi
# Finally, time to send a message
debug "INFO: Sending message with given options to $sendToAddr!"
if [[ "$mode" == message ]]; then
printf "%s\n" "$(cat "$1")" | msmtp -C "$configLocation" "$sendToAddr"
val=$?
elif [[ "$mode" == txt ]]; then
printf "%s\n" "$(echo "$message" | sed -e 's/\n/; /g')" | msmtp -C "$configLocation" "$sendToAddr" # Text messages look weird
val=$?
else
printf "%s\n" "$message" | msmtp -C "$configLocation" "$sendToAddr"
val=$?
fi
# Report error/success
if [[ $val -eq 0 ]]; then
debug "INFO: Message sent successfully!"
else
debug "l2" "ERROR: Message may not have sent, non-zero return value of $val!"
fi
debug "l3" "INFO: Done with script!"
#EOF