#!/bin/bash
# # -*- coding: utf-8 -*-
#
# GUI Script to  search for ip's of shared folders and then connect to the
# selected on, using zzzfm (may be adapted to any file manager)
#
# By PPC, 8/3/2022, full GPL license
#
#
# 17/2/2024 update to ver. 2.14 by Robin for antiX 23.1 control centre and
# antiX SAMBA mount manager integration: Prevent self closing endless loop
# on startup, added forwarding of 252 exit code; use of /dev/shm for temp
# files in order to avoid timing issues on slow spinning hdds. Added cleanup
# on exit. Fixed mount command to work with Samba mounts (smb://). This prevents
# mounting of already mounted shares as well, and avoids the pop up of an
# error console window while program happily exits already propagating success,
# since login was never accepted by samba server with the former method.
# icons and buttons alligned with asm. Tooltips added where needed. Fixed
# for allowing special characters like ampersand in passwords.
# 19/2/24 ver 2.15/2.16 bugfixing, some code cleanup.
# 21/2/24 ver 2.17 prepared for gettext translation, further bugfixing.
#
# Dependencies: nmap, smbclient; yad, gksu, udevil,
# expects to be able to write to /dev/shm 
#
# Exit codes: 0 for successful mount of share
#             1 on error or abort,
#           252  for program exit per user request (X button or X in window border).
#
# Positionals: $1 is expected to either be missing/false (if called directly)
#              or true if called by antiX mount manager. Changes some button
#              lables and button tooltips.


TEXTDOMAINDIR=/usr/share/locale
TEXTDOMAIN=antiX-samba-manager
export TEXTDOMAINDIR TEXTDOMAIN
source gettext.sh

vesion=2.19


# declare variables
scriptname=$(basename "$0")
pid_of_script=$(pidof -x $scriptname)
acm=true
[ -z "${1}" ] && acm=false # set acm to false if running standalone.
install_warning="$(eval_gettext "Installation of the following new\npackages is mandatory to proceed:")"
exit252="/dev/shm/exit255"
err_gksu="/dev/shm/err_gksu"
if $acm; then
    btn01="$(eval_gettext "Back")"
    btn01_tt="$(eval_gettext "Brings you back to antiX Samba mount-manager main window.")"
else
    btn01="$(eval_gettext "Leave")"
    btn01_tt="$(eval_gettext "This exits antiX Samba-mount without further action.")"
fi
skip=false
flag_loop="/dev/shm/loop"

# Allow only one instance of the script at a time:
for pid in $(pidof -x $scriptname); do
    if [ $pid != $$ ]; then
        kill $pid_of_script
    fi 
done

# housekeeping when exiting
function cleanup() {
    rm -f '/dev/shm/range_of_available_ips' >/dev/null
    rm -f '/dev/shm/result' >/dev/null
    rm -f '/dev/shm/finished1' >/dev/null
    rm -f '/dev/shm/ips' >/dev/null
    rm -f '/dev/shm/ccc' >/dev/null
    rm -f "$exit252" >/dev/null
    rm -f "$flag_loop" >/dev/null
    [ -e "$err_gksu" ] && gksu --sudo-mode -- rm -f "$err_gksu" >/dev/null
}
trap cleanup EXIT
cleanup # make sure there are no leftovers from a former non-standard exit.

dlg_err() {
    yad --skip-taskbar --center --on-top \
        --close-on-unfocus --timeout=15 \
        --width=260 --height=210 --borders=5 \
        --no-buttons --undecorated \
        --text-info --wrap >/dev/null <<<"$1"
}

# Check for dependencies:
## testing for yad
if ! [ -x "$(command -v yad)" ]; then
    x-terminal-emulator -e whiptail \
    --title "$(eval_gettext "antiX Samba-mount")" \
    --msgbox "$(eval_gettext "You need to have yad, smclient and nmap installed. Please make sure that those packages are installed and re-run this script")" 8 78
    exit 1
fi

# Connectivity check:
while :; do
    ip=$(hostname -I)
    if [ -n "$ip" ]; then
        echo "$(eval_gettext "Connected to a network - this device's IP is \$ip")"
        break
    else
        echo "$(eval_gettext "Not connected to a network.")"
        yad --geometry=300x100-50-50 --borders=10 \
            --window-icon="antiX-smb-mgr05" \
            --title="$(eval_gettext "antiX Samba-mount")" \
            --text="<b>""$(eval_gettext "Not connected to a network.")""</b>\n""$(eval_gettext "Can’t proceed.")" \
            --picture --inc=256 \
            --filename=/usr/share/icons/Adwaita/48x48/legacy/network-error.png \
            --timeout-indicator=bottom \
            --button="$(eval_gettext "Retry")"!!"$(eval_gettext "You may proceed after having established a network connection.")":4 \
            --button="$btn01"!!"$btn01_tt":2 >/dev/null #leave or back
            case $? in
                2) exit 1;;
              252) exit 252;;
                4) :;;
            esac
    fi
done

## testing for smbclient and, in antiX, try to install it if user so selects
if ! [ -x "$(command -v smbclient)" ]; then
    app="smbclient"
    install_apps=1
fi
    
if ! [ -x "$(command -v nmap)" ]; then
    app=$(echo $app nmap)
    install_apps=1
fi
        
if [[ "$install_apps" -eq 1 ]]; then
    yad --center --borders=10 \
        --title="$(eval_gettext "antiX Samba-mount")" \
        --window-icon=antiX-smb-mgr05 \
        --text="\n${install_warning}\n\n""smbclient, nmap" \
        --button="$(eval_gettext "Install")"!!"$(eval_gettext "This installs smbclient and nmap on your System.")":1 \
        --button="$btn01"!!"$btn01_tt":2 >/dev/null #leave or back
    x=$?
    # This next line zeroes the file that is used to flag the end of the install process (in the end of the follwoing if statement, the "; echo 1 > /dev/shm/finished1" part changes that's file's content, signaling that the process is over and the main script can continue running:
    echo 0 > /dev/shm/finished1
    if [[ $x -eq 1 ]]; then
        x-terminal-emulator -e /bin/bash -c "gksu 'apt install -y $app'; echo 1 > /dev/shm/finished1"
    elif [ $x -eq 252 ]; then
        exit 252
    else
        exit 1
    fi
    # wait until install process is finished (this waits until the "sudo apt install" process running in paralel echoes the value 1 to the /dev/shm/finished1 file:
    finished=$(cat /dev/shm/finished1)
    until [ $finished -gt 0 ]; do
        finished=$(cat /dev/shm/finished1)
    done
fi

# test if install of each one of the dependencies was succefull, if not, exit:
if [ ! -x "$(command -v smbclient)" ] || [ ! -x "$(command -v nmap)" ]; then
    yad --center --fixed --borders=10 \
        --width=300 --height=220 \
        --title="$(eval_gettext "antiX Samba-mount")" \
        --window-icon=/usr/share/icons/numix-square-antix/48x48/places/gnome-mime-x-directory-smb-share.png \
        --image="vcs-conflicting" \
        --text="\n<big><b>""$(eval_gettext "Install has failed.")""</b></big>\n\n""$(eval_gettext "Dependencies are still not installed\non your system.")"" ""$(eval_gettext "Please make\nsure to have <b>nmap</b> and <b>smbclient</b> installed\nbefore trying to execute antiX-mount-samba-shares!")" \
        --button="$btn01"!!"$btn01_tt":2 >/dev/null #leave or back
        case $? in
            252) exit 252;;
              *) exit 1;;
        esac
fi
    
#Clean up temporary files:
>/dev/shm/range_of_available_ips
>/dev/shm/ccc
>/dev/shm/result

#Draw pulsating "wait" window, while searching for shared folders:
(>$flag_loop; while [ -e $flag_loop ]; do
    echo "#"
done | yad --on-top --center \
           --width 250 \
           --no-buttons --auto-close \
           --window-icon="antiX-smb-mgr06" \
           --title="$(eval_gettext "antiX Samba-mount")" \
           --text="$(eval_gettext "Searching...")" \
           --text-align center \
           --pulsate --progress
           [ $? -eq 252 ] && touch "$exit252") &

##Main part of the script:
#Create a list of all available ips for network shares:
ips=$(ip -o addr | sed '/: lo /d'|sed '/::/d')
newString="${ips#*inet}"
ip_range=$(echo $newString|cut -d ' ' -f 1)
echo "$(eval_gettext "The detected available IP range is: \$ip_range")"
[ -e "$exit252" ] && exit 252 # check whether user wants to quit prematurely

### Check what ips are on:
nmap -v -sn $ip_range > /dev/shm/ips
cat /dev/shm/ips | sed '/host down/d'  |grep -E -o "([0-9]{1,3}[\.]){3}[0-9]{1,3}" > /dev/shm/range_of_available_ips
echo "IPs that will be searched for shared folders:"
cat /dev/shm/range_of_available_ips
echo
echo "$(eval_gettext "Scanning for shared folders on each IP...")"
[ -e "$exit252" ] && exit 252 # check whether user wants to quit prematurely

# Remove own IP from result (only display remote shares in dialog):
echo "$(eval_gettext "Removing own IP \$ip from list.")"
sed -i '/^'"$(echo ${ip})"'/d' '/dev/shm/range_of_available_ips'

# Remove empty lines from the result:
sed -i '/^[[:space:]]*$/d' /dev/shm/range_of_available_ips

# Test every possible network share ip, and log the valid addresses:
while read line; do
    smbclient -N -L $line | sed '/Printer Drivers/d' | grep "Disk" | cut -d' ' -f1  >/dev/shm/ccc
    sleep 0.2
    cat /dev/shm/ccc
    while read line2; do
        sleep 0.2
        echo //$line/$line2 >>/dev/shm/result
        echo "Doubleclick entry to connect" >>/dev/shm/result
    done </dev/shm/ccc
done </dev/shm/range_of_available_ips
[ -e "$exit252" ] && exit 252 # check whether user wants to quit prematurely

# Remove empty lines from the result:
sed -i '/^[[:space:]]*$/d' /dev/shm/result

# Close pulsating "wait" window:
rm -f "$flag_loop" # stop pulsating loop
wmctrl -c "$(eval_gettext "antiX Samba-mount")"

# generic conversion algorithm to calculate network
# from a specific address within it.
# function expects two positionals: address netmask
# see https://de.wikipedia.org/wiki/Netzmaske#Bits
# and https://en.wikipedia.org/wiki/Arithmetic_shift
# for details on calculation algorithm.
# Example: 192.168.178.22 24 => 192.168.178.0
network() {
    { IFS=. read a b c d;  # separate values from string
    } <<<$1
    u=$(((((((a<<8)|b)<<8)|c)<<8)|d)) # convert address values to 32 digit binary
    v=$((0xffffffff<<(32-$2))) # convert mask value to 32 digit binary
    w=$((u&v))                 # bitwise add mask and address to get network
    for i in 1 2 3 4; do
        x=$((w&0xff))${x:+.}$x # convert result back to human readable IP format
        w=$((w>>8))
    done
    echo $x
}

subnet="$(network ${ip_range%%/*} ${ip_range##*/})/${ip_range##*/}"
selectedshare="$(cat /dev/shm/result | sed '/Doubleclick entry to connect/d')"

# Exit if no network shares were detected:
check=$(cat /dev/shm/result | sed '/Doubleclick entry to connect/d')
if [ -n "$check" ]; then
    echo
    echo "$(eval_gettext "...Done scanning. Detected Network shares:")"
    echo "$check"
else
    echo "$(eval_gettext "No network shares detected, exiting")"
    yad --undecorated --center --fixed \
        --width=260 --height=180 --borders=20 \
        --skip-taskbar --on-top \
        --timeout=3 \
        --title="$(eval_gettext "antiX Samba-mount")" \
        --text="<big>""$(eval_gettext "No shares detected\nin your LAN\n\t\${subnet}.")""</big>" \
        --button="$btn01"!!"$btn01_tt":2 >/dev/null #leave or back
        case $? in
            252) exit 252;;
              *) exit 1;;
        esac
fi

while :; do
    if ! $skip; then # we have the data already
        # If the list of available network shares has more than one line,
        # show window that allows user to select the share to connect to:
        if [[ $(wc -l </dev/shm/result) -ge 3 ]]; then
          if $acm; then
            selectedshare="$(yad --center --fixed \
                             --width=250 --height=260 \
                             --window-icon="antiX-smb-mgr06" \
                             --title="$(eval_gettext "antiX Samba-mount")" \
                             --separator=" " \
                             --list \
                             --print-column=1 \
                             --tooltip-column=2 --hide-column=2 \
                             --column="$(eval_gettext "Available in LAN (\$subnet):")" \
                             --column="Tip":TIP \
                             --button="$(eval_gettext "Back")"!!"$(eval_gettext "Brings you back to antiX Samba manager main window.")" </dev/shm/result)"
                             x=$?
          else
            selectedshare="$(yad --center --fixed \
                             --width=250 --height=250 \
                             --window-icon="antiX-smb-mgr06" \
                             --title="$(eval_gettext "antiX Samba-mount")" \
                             --no-buttons \
                             --separator=" " \
                             --list \
                             --print-column=1 \
                             --tooltip-column=2 --hide-column=2 \
                             --column="$(eval_gettext "Available in LAN (\$subnet):")" \
                             --column="Tip":TIP </dev/shm/result)"
                             x=$?
          fi
            # If nothing was selected or user wants to, exit:
            if [ $x -eq 252 ]; then
                exit 252
            elif [ $x -eq 4 ]; then
                exit 0
            elif [[ "$selectedshare" = "" ]]; then
                exit 1
            fi
        fi
        selectedshare="$(echo $selectedshare | sed -n 's/^[[:space:]]*\(..*\)$/\1/p' | rev | sed -n 's/^[[:space:]]*\(..*\)$/\1/p' | rev)"  # remove trailing and leading blanks; sed is greedy, so rev.
        # Ask for credentials:
        compat=false  # flag SMB protocol 1.0
        fmload=true   # flag mount File manager
        guest=false   # flag guest login
        folder=false  # flag userdefined mountpoint
        skip=false    # flag to skip over
        change=false  # flag target had to be changed
        share="$selectedshare"
    fi    
    skip=false   # reset skip flag for next loop 
    entry="$(yad --center --width=350 \
                     --window-icon="antiX-smb-mgr06" \
                     --title="$(eval_gettext "Credentials for \$share")" \
                     --form \
                     --field=" ""$(eval_gettext "Account")" $USER \
                     --field=" ""$(eval_gettext "Passphrase")":H ""\
                     --field=" ""$(eval_gettext "Compat. protocol v1.0")":CHK ${compat^^} \
                     --field=" ""$(eval_gettext "Load in FM")":CHK ${fmload^^} \
                     --field=" ""$(eval_gettext "Guest login")":CHK ${guest^^} \
                     --field=" ""$(eval_gettext "Targetfolder")":CHK ${folder^^} \
                     --button="$(eval_gettext "Return")"!!"$(eval_gettext "Returns to the share selection and lets you select another one.")":6 \
                     --button="$(eval_gettext "Abort")"!!"$(eval_gettext "Abstain from mounting.")":1 \
                     --button="$(eval_gettext "Mount")"!!"$(eval_gettext "Mounts remote share \$share with given credentials and settings.")"" ""$(eval_gettext "If ‘guest login’ is ticked, neither user nor password is expected, these fields are ignored.")"" ""$(eval_gettext "‘Compat. protocol v1.0’ mounts with insecure and deprecated SMB v1.0 transport protocol; not recommended if not really needed for hardware compatibility reasons. Consider upgrading the server instead.")"" ""$(eval_gettext "‘Load in FM’ opens the share after successfully mounting in your file manager.")"" ""$(eval_gettext "‘Targetfolder’ lets you select or create an arbitrary empty target folder on your system as mountpoint for the share.")":4)"
                     x=$?
                     if [ $x -eq 6 ]; then
                         continue
                     elif [ $x -ne 4 ]; then
                         exit 252   # exit code from yad ESC and X used for frowarding direct exit.
                     fi
    username="$(echo $entry |cut -d\| -f1)"
    password="$(echo $entry |cut -d\| -f2)"
    if [[ -z "${username// }" ]]; then
        echo "$(eval_gettext "Logging on as default samba user ›nobody‹")"
        username="nobody"   # use 'nobody' account on samba server, mapped to guest usually.
        # echo "need a user name"  #→ yad...
    fi
    compat=$(echo $entry |cut -d\| -f3)
    fmload=$(echo $entry |cut -d\| -f4)
    guest=$(echo $entry |cut -d\| -f5)
    folder=$(echo $entry |cut -d\| -f6)
    success=false
    if ${folder,,} || ${compat,,}; then # target checks only needed if not udevil can be used.
        user="$(whoami)"
        group="$user"
        o=${share:2}  # sharename without leading slashes
        p=${o%%/*}    # basename of folder; this can’t contain slashes already
        #p=${p//\//-} # convert slashes for file system # not needed, see above.
        q=${o##*/}    # sharename; this can’t contain slashes.
        target_d="/media/$user/asm-remote-$p/$q" # default target
        parent="/media/$user/asm-remote-$p"
        gksu --sudo-mode -- bash -c 'mkdir -p "'"/media/$user"'"' # create folder if still missing
        gksu --sudo-mode -- bash -c 'chmod 750 "'"/media/$user"'"' # set default permission for media folder
        while :; do  # break only if we have a target folder, valid, empty and not in use already.
            if ${folder,,}; then  # sub routine for folder selection by user
                target="/media/$user"
                while :; do  # break only on user selected target folder was verified.
                    # ask user for target
                    entry="$(yad --center --width=360 \
                             --window-icon="antiX-smb-mgr06" \
                             --title="$(eval_gettext "Mountpoint for \$share")" \
                             --Text="$(eval_gettext "Select empty target folder or let it create.")" \
                             --form \
                             --field=" ""$(eval_gettext "Targetfolder:")":DIR "$target" \
                             --button="$(eval_gettext "Skip")"!!"$(eval_gettext "Click this to use the default folder \$target_d, which will be created if not present.")":6 \
                             --button="$(eval_gettext "Accept")"!!"$(eval_gettext "Proceeds with mounting the share \$share to the above selected or created folder.")"" ""$(eval_gettext "You are not restricted to the /media folder, you may use any folder in your file system you have access to, e.g. an empty folder or subfolder somewhere on your hard disk (except for the default mountpoints’ base folders  /media  /media/‹user›  and  /mnt  themselves; create subfolders within instead).")"" ""$(eval_gettext "Better make sure not to mount something to folders which are not empty.")"" ""$(eval_gettext "‘Group identity’ determines whether the share is locally available for the group ›\$user‹ only or for the group ›users‹, ›sambashare‹, or key in any other group.")":4)"
                             case $? in
                                 252) exit 252;; # leave asm
                                   4) target="$(echo $entry |cut -d\| -f1)" # proceed with checking dlg results
                                      #group="$(echo $entry |cut -d\| -f2)"
                                      echo "$(eval_gettext "Setting sharefolder to user defined target: \$target")";;
                                   6) target="$target_d" # proceed with default target folder
                                      folder=false; continue 2;;   # switches to non-folder mode.
                             esac
                    # changed form cbe to dir, so no empty or nonexistent target can happen.
                    # 1st check whether there is something mounted already.
                    mount | grep -F 'on '"$target"' ' >/dev/null
                    if [ $? -eq 0 ]; then
                        dlg_err "$(eval_gettext "There is something mounted already to folder    \${target}.    Please unmount first or select another one.")"
                        continue
                    fi
                    # 2nd check whether folder is a base media folder.
                    sysusers="$(getent passwd | grep -v 'nologin\|sync\|root' | cut -d: -f1)"
                    mediausers+=( "$(sed -n 's/\(..*\)/\/\/media\/\1\/\//p'<<<"$sysusers")" ) # array of known /media/<user> subfolders + salt for greedy regex
                    if [[ "${mediausers[@]}" =~ "/$target//" ]] || [ "$target" == "/media" ] || [ "$target" == "/mnt" ] ; then
                        dlg_err "$(eval_gettext "Can’t use this folder \$target as mountpoint.")$(eval_gettext "It is meant to be a parent folder for multiple mountpoints.")$(eval_gettext "Please let create a subfolder within it instead.")"
                        continue
                    fi
                    # 3rd check whether user defined folder is empty
                    if [ ! -n "$(find "$target" -maxdepth 0 -type d -empty 2>/dev/null)" ]; then
                        # we know already it's a dir (since cbe was changed to dir), but it's not empty.
                        dlg_err "$(eval_gettext "Folder is not empty! Can’t use it. Please choose another one.")"
                        continue
                    fi
                    # find out whether this is a system folder; we need to give out a warning then.
                    sysfolder=false
                    if [[ "${target}" = '/' ]]; then
                        sysfolder=true
                    else
                        # match 1st level of target dir against whitelist. maybe change to blacklist?
                        if $(case "$(echo $target | awk -F/ '{print FS $2}')" in ('/media')false;;('/mnt')false;; ('/home')false;; esac); then
                            sysfolder=true
                        fi  # case returns inverted logic only, so false exit state means true!
                    fi                
                    if $sysfolder; then
                        yad --skip-taskbar --center --on-top \
                            --width=260 --height=210 --borders=5 \
                            --undecorated --back=darkred --fore=white \
                            --text-info --wrap >/dev/null <<<"$(eval_gettext "\$target seems to be a system folder. Are you sure what you are doing? Consider to choose another one, if not.")" \
                            --button="$(eval_gettext "I’m sure")"!!"$(eval_gettext "I’m fully aware of the side effects and risk of damage of my system. Do it anyway, I know what I’m doing.")":2 \
                            --button="$(eval_gettext "No, take me back.")"!!"$(eval_gettext "This will take you back to folder selection for choosing another folder.")":4
                            case $? in
                                2) :;;
                                4) continue 2;;
                                252) exit 252 # leave asm
                            esac
                    fi
    # todo:         #  detailed check whether user has access to the folder he has selected:
                    #   -check folder permissions: if at least read is allowed for "others" → OK
                    #   -check whether folder group matches with one of the groups user is member of → OK
                    #   -check whether folder group has at least read access → OK
                    #   -check whether user is folder owner → OK
                    #   -check whether folder user has at least read access → OK
                    #   -check whether user can traverse all parent directories.
                    #   else ...
                    # for now: Just a warning user must make sure himself to be able to access. We'll just mount.
                    dlg_err "$(eval_gettext "Please make sure you have sufficient permissions to access the selected folder locally.")"
                    break 2 # if we made it here, target folder exists and can be used as mount point. 
                done 
            else   # sub routine for checking of default folder (folder=false)
                target="$target_d" # use default target
                while :; do  # breaks only if we have a valid target.
                    if [ -e "$target" ]; then
                        if [ -d "$target" ]; then
                            n=1
                            target_tmp="$target"
                            while [ ! -n "$(find "$target" -maxdepth 0 -type d -empty 2>/dev/null)" ] || mount | grep -F "on $target" >/dev/null; do
                                #check whether empty, otherwise increment.
                                while [ -e "${target}-${n}" ]; do
                                    [ -n "$(find "${target_tmp}-${n}" -maxdepth 0 -type d -empty 2>/dev/null)" ] && break
                                    let n++
                                done
                                # if something is already mounted to this folder, add counter
                                # and increment further until we have got another possibly free parking slot.
                                while mount | grep -F "on ${target_tmp}-${n}"; do
                                    let n++
                                done
                                target="${target_tmp}-${n}"
                                if [ ! -e "$target" ]; then break; fi  #  if folder doesn't exist, nothing can be mounted...
                            done
                            break # found a parking slot finally.
    # todo:                 # since folder name is created from server and share names,
                            # this can only mean the very source is alredy mounted.
                            # make user aware of this.
                        else
                            # there lives something here not being a folder obviously, shouldn't be.
                            yad --skip-taskbar --center --on-top \
                                --width=260 --height=210 --borders=5 \
                                --undecorated \
                                --text-info --wrap \
                                --button="$(eval_gettext "Retry")"!!"$(eval_gettext "This repeats mountpoint path checking. (Implies you have removed the file meanwhile.)")":2 \
                                --button="$(eval_gettext "Abort")!!$(eval_gettext "Abstain from mounting the share.")":4 \
                                --button="$(eval_gettext "Manual selection")"!!"$(eval_gettext "You may want to select another target mountpoint on your own.")":6 >/dev/null <<<"$(eval_gettext "Error: there is a file  \$target  in folder  /media/\$user  blocking mounting the share. Please remove this file before retrying.")"
                                case $? in
                                    2) continue;;
                                    4) exit 1;;
                                    6) skip=true
                                       continue 2;;
                                       #break;;
                                  252) exit 252;;
                                esac
                        fi
                    else
                        break  # target folder doesn't exist still, no checks needed.
                    fi
                done # we have a valid target.
                # In compat mode we need to create a target folder since we can’t use udevil when specifying ver= statement.
                # mount.cifs doesn’t have a -m option. In folder mode dir field type makes sure already folder exists.
                # create folder and set permissions if folder doesn't exist.
                echo "$(eval_gettext "creating folder \$target for user \$user:\$group")"
                gksu --sudo-mode -- bash -c 'mkdir -p "'"$target"'"'
                gksu --sudo-mode -- bash -c 'chown "'"$user"'":"'"$group"'" "'"$target"'"'
                gksu --sudo-mode -- bash -c 'chown "'"$user"'":"'"$group"'" "'"$parent"'"'
                # check whether folder was actually created, or user aborted sudo credentials.
                if ! [ -d "$target" ]; then
                    dlg_err "$(eval_gettext "Can’t proceed. Needed mountpoint folder \$target wasn’t created.")"
                    exit 1
                fi
                break # if we made it here, target folder can be used as mount point. 
            fi
        done 
    fi
        # mount selected share
        # always mount rw, since ro restriction will be applied server side.
        if ${compat,,}; then
            echo "$(eval_gettext "mounting in SMB v1.0 compatibility mode.")"
            if ! ${guest,,}; then
                # mount type #1 (user, folder inclusive, 1.0 protocol)
                echo -e "$(eval_gettext "logging in to account \${username}\nmounting share \${share} to mointpoint \${target}\nlocally owned by user \$user:\$group\napplying mount type #1")"
                gksu --sudo-mode -- bash -c "mount.cifs -v -o vers=1.0,rw,gid=$group,uid=$user,username=$username,password="\""$password"\"" "\""${share}"\"" "\""${target}"\"" 2>"\""$err_gksu"\"""  # share is expected with leading slashes here. Tested on antiX 23.1 with a server accepting SMBv1.0, default user share.
                if [ $? -eq 0 ]; then
                    success=true
                    break
                fi
            else
                # mount type #2 (guest, folder inclusive, 1.0 protocol)
                echo -e "$(eval_gettext "logging in trying guest account\nmounting share \${share} to mointpoint \${target}\nlocally owned by user \$user:\$user\napplying mount type #2")"
                gksu --sudo-mode -- bash -c "mount.cifs -v -o vers=1.0,rw,gid=$group,uid=$user,guest "\""${share}"\"" "\""${target}"\"" 2>"\""$err_gksu"\"""  # share is expected with leading slashes here. Tested on antiX 23.1 with a server accepting SMBv1.0, share accepting guests.
                #err="$(udevil mount -t cifs smb:${share} -o guest,vers=1.0 2>&1)"
                # udevil: verweigert 90: Option 'vers=1.0' ist keine erlaubte Option
                if [ $? -eq 0 ]; then
                    success=true
                    break
                fi
            fi
            err="$(<$err_gksu)"
            gksu --sudo-mode -- rm -f "$err_gksu" >/dev/null
        else # we can use udevil service for default current SMB protocol mounts
            echo "$(eval_gettext "deploying max. SMB protocol version >=2.1 supported by server and client both.")"
            if ! ${folder,,}; then  # udevil won't accpet mountpoints outside 'mediafolders'
                echo "$(eval_gettext "mounting share in default mode by udevil")"
                if ! ${guest,,}; then
                    # mount type #3 (user, default protocol)
                    echo -e "$(eval_gettext "logging in to account \${username}\nmounting share \${share} to mointpoint ›unknown‹\nlocally owned by unknown:unknown (will be auto-set by udevil)\napplying mount type #3")"
                    #set -x
                    #udevil mount -t cifs -o username="$username",password="$password" "$share"
                    # the above line doesn’t work with samba shares. Password MUST be quoted
                    # to avoid bash errors on special characters.
                    err="$(udevil mount -t cifs smb://$username:"$password"@${share:2} 2>&1)"  # Share is expected NOT to have leading slashes here. Tested with default samba user shares (smb v3.1.1) on antiX 23.1.
                    if [ $? -eq 0 ]; then
                        success=true
                        break
                    fi
                    #set -x
                else
                    # mount type #4 (guest, default protocol)
                    echo -e "$(eval_gettext "logging in trying guest account\nmounting share \${share} to mointpoint ›unknown‹\nlocally owned by user ›unknown:unknown‹ (will be auto-set by udevil)\napplying mount type #4")"
                    err="$(udevil mount -t cifs smb://${share:2} -o guest 2>&1)" # Tested with a samba guest = ok share (smb v3.1.1) on antiX 23.1.
                    if [ $? -eq 0 ]; then
                        success=true
                        break
                    fi
                fi
            else
               echo "$(eval_gettext "mounting share in folder mode.")"
               # udevil refuses to mount something outside /media and /mnt.
               if ! ${guest,,}; then
                    # mount type #5 (user, folder, default protocol)
                    echo -e "$(eval_gettext "logging in to account \${username}\nmounting share \${share} to mointpoint \${target}, locally owned by user \$user:\$group\napplying mount type #5")"
                    gksu --sudo-mode -- bash -c "mount.cifs -v -o rw,gid=$group,uid=$user,username=$username,password="\""$password"\"" "\""${share}"\"" "\""${target}"\"" 2>"\""$err_gksu"\"""  # share is expected with leading slashes here. Tested with default samba user shares (smb v3.1.1) on antiX 23.1.
                    if [ $? -eq 0 ]; then
                        success=true
                        break
                    fi
                else
                    # mount type #6 (guest, folder, default protocol)
                    echo -e "$(eval_gettext "logging in trying guest account\nmounting share \${share} to mointpoint \${target}\nlocally owned by user \$user:\$group\napplying mount type #6")"
                    gksu --sudo-mode -- bash -c "mount.cifs -v -o rw,gid=$group,uid=$user,guest "\""${share}"\"" "\""${target}"\"" 2>"\""$err_gksu"\"""  # share is expected with leading slashes here. Tested with samba guest = ok shares (smb v3.1.1) on antiX 23.1.
                    if [ $? -eq 0 ]; then
                        success=true
                        break
                    fi
                fi
                err="$(<$err_gksu)"
                gksu --sudo-mode -- rm -f "$err_gksu" >/dev/null
            fi
        fi
    if ! $success; then
        yad --skip-taskbar --center --on-top \
            --close-on-unfocus --timeout=30 \
            --width=300 --height=300 --borders=5 \
            --no-buttons --undecorated \
            --text-info --wrap >/dev/null <<<"$(eval_gettext "Mount failed.")""    →    »$err«"
    fi
done

# aftermath: load zzzfm if user has reqested, give notice on fail.
if $success; then
    # inform user if mountpoint had to be changed.
    $change && dlg_err "$(eval_gettext "Please note!    →    To the target folder something was mounted already; hence an increment was appended to its name for creating the new mount. New mountpoint: \$target")"
    if ${fmload,,}; then
        # Try to mount share in the default File Manager:
        desktop-defaults-run -fm $share & sleep 1
    fi
    exit 0
fi
