blob: b0099b4c324a23fddfa8e53f9bc5e09ac4b03d3b (
plain)
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
|
#!/bin/bash
# Mounts Android Phones and USB drives (encrypted or not). This script will
# replace the older `dmenumount` which had extra steps and couldn't handle
# encrypted drives.
# TODO: Try decrypt for drives in crtypttab
# TODO: Add some support for connecting iPhones (although they are annoying).
# Set UTF-8 locale for proper emoji handling on macOS
export LC_ALL=en_US.UTF-8
export LANG=en_US.UTF-8
IFS='
'
# Function for escaping cell-phone names.
escape() { echo "$@" | iconv -cf UTF-8 -t ASCII//TRANSLIT | tr -d '[:punct:]' | tr '[:upper:]' '[:lower:]' | tr ' ' '-' | sed "s/-\+/-/g;s/\(^-\|-\$\)//g"; }
# Function for prompting user for a mountpoint.
getmount() {
if [ -z "$1" ]; then
echo "Available mount points:"
mountpoints="$(find "$HOME/Media" "$HOME/mnt" -mindepth 1 -maxdepth 1 -type d 2>/dev/null)"
if [ -n "$mountpoints" ]; then
echo "$mountpoints" | nl -nln
echo "Enter the number of the mount point, or type a custom path:"
read -r choice
if [[ "$choice" =~ ^[0-9]+$ ]]; then
mp="$(echo "$mountpoints" | sed -n "${choice}p")"
else
mp="$choice"
fi
else
echo "No existing mount points found. Enter a custom path:"
read -r mp
fi
else
mp="$1"
fi
test -n "$mp"
if [ ! -d "$mp" ]; then
echo "$mp does not exist. Create it? (y/n):"
read -r mkdiryn
[ "$mkdiryn" = "y" ] || [ "$mkdiryn" = "Y" ] && (mkdir -p "$mp" 2>/dev/null || sudo -A mkdir -p "$mp")
fi
}
attemptmount() {
# Attempt to mount using diskutil (macOS)
if command -v diskutil >/dev/null 2>&1; then
# Try to get volume name
mplabel=$(diskutil info "$chosen" 2>/dev/null | grep "Volume Name" | sed 's/.*: *//' | tr ' ' '-')
[ -z "$mplabel" ] && mplabel="disk-$(basename "$chosen")"
mp="$HOME/Media/$mplabel"
if [ ! -d "$mp" ] && [ ! -d "$HOME/mnt/$mplabel" ]; then
getmount "$mp" && diskutil mount -mountPoint "$mp" "$chosen" >/dev/null 2>&1 || return 1
elif [ -d "$mp" ] && [ ! -d "$HOME/mnt/$mplabel" ]; then
diskutil mount -mountPoint "$mp" "$chosen" >/dev/null 2>&1 || return 1
elif [ -d "$HOME/mnt/$mplabel" ]; then
getmount "$HOME/mnt/$mplabel" && diskutil mount -mountPoint "$HOME/mnt/$mplabel" "$chosen" >/dev/null 2>&1 || return 1
else
diskutil mount "$chosen" >/dev/null 2>&1 || return 1
fi
else
return 1
fi
}
osascript -e 'display notification "🔎 Searching drives to mount..." with title "Mounter"'
# Check for phones (Android MTP).
phones=""
if command -v simple-mtpfs >/dev/null 2>&1; then
phones="$(simple-mtpfs -l 2>/dev/null | awk '{print "📱" $0}')"
mountedphones="$(mount | grep "simple-mtpfs")"
# If there are already mounted phones, remove them from the list of mountables.
[ -n "$mountedphones" ] && phones="$(for phone in $phones; do
for mounted in $mountedphones; do
escphone="$(escape "$phone")"
[[ "$mounted" =~ $escphone ]] && break 1
done && continue 1
echo "$phone"
done)"
fi
iphones=""
if command -v ideviceinfo >/dev/null 2>&1; then
iphones="$(ideviceinfo 2>/dev/null | grep -E "^DeviceName" | awk -F': ' '{print "🍎" $2}')"
mountediphones="$(mount | grep "ifuse")"
[ -n "$mountediphones" ] && iphones="$(for iphone in $iphones; do
for mounted in $mountediphones; do
esciphone="$(escape "$iphone")"
[[ "$mounted" =~ $esciphone ]] && break 1
done && continue 1
echo "$iphone"
done)"
fi
# Check for drives using diskutil (macOS)
unmounteddrives=""
if command -v diskutil >/dev/null 2>&1; then
# Get all external disks
external_disks="$(diskutil list external | grep -E '^/dev/disk[0-9]+' | awk '{print $1}')"
for disk in $external_disks; do
# Check if disk is mounted
if ! diskutil info "$disk" 2>/dev/null | grep -q "Mounted.*Yes"; then
diskname="$(diskutil info "$disk" 2>/dev/null | grep "Device / Media Name" | sed 's/.*: *//')"
# Only include if it's truly removable/external
if diskutil info "$disk" 2>/dev/null | grep -qE "Protocol.*USB\|Protocol.*FireWire\|Protocol.*Thunderbolt\|Removable Media.*Yes"; then
[ -n "$diskname" ] && unmounteddrives="${unmounteddrives}💾 $disk ($diskname)\n"
fi
fi
done
# Also check for removable volumes that are unmounted
volumes="$(diskutil list | grep -E '^ [0-9]+:' | grep -v 'disk image' | awk '{print $NF}' | grep '^disk')"
for vol in $volumes; do
vol_path="/dev/$vol"
if ! mount | grep -q "$vol_path"; then
volname="$(diskutil info "$vol_path" 2>/dev/null | grep "Volume Name" | sed 's/.*: *//' | head -1)"
voltype="$(diskutil info "$vol_path" 2>/dev/null | grep "File System Personality" | sed 's/.*: *//')"
# Skip system partitions
case "$volname" in
"Recovery" | "EFI" | "Preboot" | "VM" | "Update" | "Data" | "" | *"Recovery"* | *"EFI"* | *"Preboot"* | *"VM"* | *"Update"* | *"Data"*)
continue
;;
esac
# Skip APFS system containers and snapshots
if diskutil info "$vol_path" 2>/dev/null | grep -q "APFS.*Container\|Snapshot"; then
continue
fi
# Only include external or removable drives
if diskutil info "$vol_path" 2>/dev/null | grep -qE "Protocol.*USB\|Protocol.*FireWire\|Protocol.*Thunderbolt\|Removable Media.*Yes\|External.*Yes"; then
[ -n "$volname" ] && [ "$voltype" != "" ] && unmounteddrives="${unmounteddrives}💾 $vol_path ($volname - $voltype)\n"
fi
fi
done
fi
normalparts="$(printf "%b" "$unmounteddrives" | grep -v '^$')"
# LUKS encryption is not common on macOS, so we'll skip it
unopenedluks=""
# Get network shares (simplified for macOS)
smbips=""
if [ -x "/opt/homebrew/bin/arp-scan" ]; then
# Get local network IPs from ARP table
smbips="$(sudo /opt/homebrew/bin/arp-scan --interface=en0 --localnet 2>/dev/null | awk '/^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/{print $1}' | sort -u)"
fi
# Get currently mounted SMB/CIFS shares
mountedsmbs="$(mount | grep 'smbfs\|cifs')"
smbshares=""
# Limit to first 3 IPs to avoid hanging
count=0
for smbip in $smbips; do
count=$((count + 1))
if [ $count -gt 3 ]; then
break
fi
# Try to discover shares using smbutil (macOS native) or smbclient
if command -v smbutil >/dev/null 2>&1; then
# Try with guest access first, then with current user if needed
shares=$(smbutil view "//$smbip" 2>/dev/null | awk '/Disk/ && !/\$/ && !/ADMIN/ && !/IPC/ {print $1}' | grep -v '^$' ||
smbutil view "//guest@$smbip" 2>/dev/null | awk '/Disk/ && !/\$/ && !/ADMIN/ && !/IPC/ {print $1}' | grep -v '^$' ||
smbutil view "//$USER@$smbip" 2>/dev/null | awk '/Disk/ && !/\$/ && !/ADMIN/ && !/IPC/ {print $1}' | grep -v '^$' || true)
for share in $shares; do
if [ -n "$share" ]; then
smbshares="${smbshares}//$smbip/$share"$'\n'
fi
done
elif command -v smbclient >/dev/null 2>&1; then
# Try smbclient if available
shares=$(smbclient -L "$smbip" -N 2>/dev/null | awk '/Disk/ && !/\$/ && !/ADMIN/ && !/IPC/ {print $1}' | grep -v '^$' || true)
for share in $shares; do
if [ -n "$share" ]; then
smbshares="${smbshares}//$smbip/$share"$'\n'
fi
done
fi
done
# Remove empty lines
smbshares="$(printf "%s" "$smbshares" | grep -v '^$')"
# Filter out already mounted shares and format for display
smbs=""
if [ -n "$smbshares" ]; then
smbs="$(printf "%s" "$smbshares" | while IFS= read -r smb; do
if [ -n "$smb" ] && [ "$smb" != "SMB_AVAILABLE" ]; then
# Check if already mounted
mounted=false
if [ -n "$mountedsmbs" ]; then
for mountedsmb in $mountedsmbs; do
mountedpath="${mountedsmb%% *}"
if [[ "$mountedpath" =~ $(printf "%s" "$smb" | sed 's/[[\.*^$()+?{|]/\\&/g') ]]; then
mounted=true
break
fi
done
fi
if [ "$mounted" = "false" ]; then
printf "📡 %s\n" "$smb"
fi
fi
done)"
fi
# No manual SMB option - only show discovered shares
# Add all to one variable. If no mountable drives found, exit.
alldrives="$(echo "$phones
$iphones
$unopenedluks
$normalparts
$smbs" | sed "/^$/d;s/ *$//")"
# Debug output
if [ "$1" = "-d" ] || [ "$1" = "--debug" ]; then
echo "Debug: phones='$phones'"
echo "Debug: iphones='$iphones'"
echo "Debug: normalparts='$normalparts'"
echo "Debug: smbs='$smbs'"
echo "Debug: alldrives='$alldrives'"
fi
# Quit the script if a sequential command fails.
set -e
test -n "$alldrives" || { echo "No mountable drives found. Use -d for debug info." && exit; }
# Feed all found drives to terminal menu and get user choice.
echo "Available drives to mount:"
echo "$alldrives" | nl -nln
echo "Enter the number of the drive you want to mount:"
read -r drivenum
chosen="$(echo "$alldrives" | sed -n "${drivenum}p")"
case "$chosen" in
💾*)
chosen="${chosen%% *}"
chosen="${chosen:1}" # This is a bashism.
attemptmount || {
getmount
# Use diskutil for mounting on macOS
if [ -n "$mp" ]; then
mkdir -p "$mp" 2>/dev/null
diskutil mount -mountPoint "$mp" "$chosen" 2>/dev/null || diskutil mount "$chosen"
else
diskutil mount "$chosen"
fi
}
osascript -e "display notification \"$chosen mounted to $mp.\" with title \"💾 Drive Mounted\""
;;
🔒*)
# LUKS encryption is not common on macOS, show message
osascript -e 'display notification "Encrypted drives not supported on macOS" with title "🔒 Encryption Not Supported"'
;;
📱*)
if command -v simple-mtpfs >/dev/null 2>&1; then
osascript -e 'display notification "Remember to allow file access on your phone now." with title "❗ Note"'
getmount
number="${chosen%%:*}"
number="${chosen:1}" # This is a bashism.
mkdir -p "$mp" 2>/dev/null
simple-mtpfs -o allow_other -o fsname="simple-mtpfs-$(escape "$chosen")" --device "$number" "$mp" 2>/dev/null
osascript -e "display notification \"Android device mounted to $mp.\" with title \"🤖 Android Mounted\""
else
osascript -e 'display notification "simple-mtpfs not installed. Install with: brew install simple-mtpfs" with title "❗ MTP Not Available"'
fi
;;
🍎*)
if command -v ifuse >/dev/null 2>&1; then
getmount
mkdir -p "$mp" 2>/dev/null
ifuse "$mp" 2>/dev/null
osascript -e "display notification \"$chosen mounted to $mp.\" with title \"🍎 iPhone Mounted\""
else
osascript -e 'display notification "ifuse not installed. Install with: brew install ifuse" with title "❗ iPhone Support Not Available"'
fi
;;
📡*)
# Extract the SMB path from the selected item
chosen="${chosen#📡 }"
# If it doesn't start with //, add it
if [[ "$chosen" != //* ]]; then
chosen="//$chosen"
fi
path="${chosen##*/}"
if getmount "$HOME/Media/$path"; then
mkdir -p "$mp" 2>/dev/null
# Use macOS mount_smbfs command
if command -v mount_smbfs >/dev/null 2>&1; then
echo "Attempting to mount $chosen to $mp"
mount_smbfs -o soft "$chosen" "$mp" 2>/dev/null
else
osascript -e 'display notification "SMB mounting not available" with title "❌ SMB Not Supported"'
exit
fi
else
osascript -e "display notification \"Failed to create mount point\" with title \"❌ Failed to mount samba\""
exit
fi
osascript -e "display notification \"$mp\" with title \"📡 Samba successfully mounted to\""
;;
esac
|