import logging
import threading
import string
import random
from os.path import expanduser
import os
from subprocess import *
import subprocess
from smb import *
from smb.SMBConnection import SMBConnection
from persistence import Database
from persistence import DatabaseSMBSR
import time
from smbsr import SMBSR
logger = logging.getLogger('rSMBi')


class rsmicore(object):

    def __init__(self, workername, options, db, targetobj, targetIPenum, wordlistDict):
        super(rsmicore, self).__init__()
        self.options = options
        self.workername = workername
        self.db = db
        self.targetobj = targetobj
        self.targetIPenum = targetIPenum
        self.wordlistDict = wordlistDict

    def runSMBSRbrain(self, file):

        logger.debug(f"[{self.workername}] | Working on file: " + file)
        # (self, workername, options, db):
        smbsrunner = SMBSR(self.workername, self.options, self.db["SMBSR"], file, list(
            self.targetobj.values())[0], list(self.targetobj.keys())[0], self.options.tag)
        # here i need to call the parse function
        # def parse(self, filename, to_match, options):
        smbsrunner.parse(file, self.wordlistDict, self.options)

    def checkWritingRights(self, filePath, sharePath):

        try:
            f = open(filePath, "a")
        except Exception:
            return
        logger.info(f"[{self.workername}] | " + " Writing permissions on: " +
                    filePath)
        # here there is a finding def insertFinding(self, filename, share, ip, tag):
        self.db["RSMBI"].insertFinding(filePath, list(self.targetobj.values())[0],
                                       list(self.targetobj.keys())[0], self.options.tag)
        f.close()

    def createFolder(self, options):
        localpath = options.local_path + "/" + \
            ''.join(random.choices(string.ascii_letters, k=7))
        try:
            os.mkdir(expanduser(localpath))
            logger.debug(f"[{self.workername}] | Created folder: " + localpath)
        except Exception as e:
            logger.error(f"[{self.workername}] |" + " Error while creating folder: " +
                         localpath + " with exception: " + str(e))

        return localpath

    def deleteFolder(self, path):
        logger.debug(f"[{self.workername}] |" + " Removing folder: " +
                     path)
        try:
            os.rmdir(path)
        except Exception as e:
            logger.error(f"[{self.workername}] |" + " Error removing " + path + " with exception " + str(e) +
                         ", keep in mind you might need to cleanup yourself")

    def listShares(self, serverName, options):
        connection = SMBConnection(options.username, options.password, options.fake_hostname,
                                   'netbios-server-name', options.domain, use_ntlm_v2=True, is_direct_tcp=True)
        try:
            connection.connect(serverName, 445)
        except Exception as e:
            logger.info(f"[{self.workername}] | " + "Error connecting to: " + serverName +
                        ", with exception: " + str(e))
        try:
            shares = connection.listShares()
        except Exception as e:
            logger.info(f"[{self.workername}] | " + "Error while listing shares from: " +
                        serverName + ", with exception: " + str(e))
            shares = []
        connection.close()
        return shares

    def mountShare(self, localPath, remoteShare, pathCredFile):
        logger.debug(f"[{self.workername}] | " + "Mounting share: " + remoteShare +
                     " in: " + localPath)
        try:
            check_call(['mount', '-t', 'cifs', remoteShare, '-o', 'credentials=' + pathCredFile,
                        expanduser(localPath)], stderr=subprocess.DEVNULL)
            return True
        except Exception as e:
            logger.error(f"[{self.workername}] | " + "Exception while trying to mount " +
                         remoteShare + " with exception: " + str(e))
            return False

    def umountShare(self, localPath):
        logger.debug(f"[{self.workername}] | " + "Unmounting share: " +
                     localPath)
        try:
            check_call(['umount', '-f', '-l', localPath],
                       stderr=subprocess.DEVNULL)
        except Exception as e:
            logger.error(f"[{self.workername}] | " + "Exception while trying to unmount " +
                         localPath + " with exception: " + str(e))

    def walkFolders(self, path):

        logger.debug(f"[{self.workername}] | " + "Walking folder in: " + path)

        try:
            for root, dirs, files in os.walk(path):
                for file in files:
                    if self.options.mode.upper() == "SMBSR":
                        self.runSMBSRbrain(root + "/" + file)
                    elif self.options.mode.upper() == "RSMBI":
                        self.checkWritingRights(root + "/" + file, path)
                    elif self.options.mode.upper() == "BOTH":
                        self.runSMBSRbrain(root + "/" + file)
                        self.checkWritingRights(root + "/" + file, path)

                        # here i need to check what i want to run: SMBSR, RSMBI, both

        except Exception as e:
            logger.error(f"[{self.workername}] | " + "Error while walking folders of path: " +
                         path + " with exception: " + str(e))
            if "Permission denied" not in str(e):
                self.umountShare(path)

    def analyzeTarget(self):
        logger.info(f"[{self.workername}] | " +
                    " working on: " + str(self.targetobj))
        localpath = ""
        localpath = self.createFolder(self.options)
        if localpath != "":
            try:

                if self.mountShare(localpath, "//" + list(self.targetobj.keys()
                                                          )[0] + "/" + list(self.targetobj.values())[0], self.options.smbcreds):
                    self.walkFolders(localpath)
                    self.umountShare(localpath)
                self.deleteFolder(localpath)

            except Exception as e:
                logger.error(f"[{self.workername}] | " + "Error while working on: //" + list(self.targetobj.keys())
                             [0] + "/" + list(self.targetobj.values())[0])

    def enumTargets(self):
        logger.info("I'm " + self.workername +
                    " enumerating targets")
        tempShares = []
        # need to check here if i get empty shares
        logger.debug(f"[{self.workername}] | " +
                     "Listing shares for: " + self.targetIPenum)
        for share in self.listShares(self.targetIPenum, self.options):
            if not share.isSpecial and share.name not in ['NETLOGON', 'IPC$'] and (share.name).lower() not in list(map(lambda x: x.lower(), self.options.share_black.split(','))):
                logger.debug(f"[{self.workername}] | " + "Found share: " + share.name +
                             " on host:" + self.targetIPenum)
                tempShares.append(share.name)

        return self.targetIPenum, tempShares


class rsmbiworker (threading.Thread):
    def __init__(self, workername, options, targetdict, db, lock, scope, targetsIPs, wordlistDict):
        threading.Thread.__init__(self)
        self.workername = workername
        self.options = options
        self.targetdict = targetdict
        self.db = db
        self.lock = lock
        self.scope = scope
        self.targetsIPs = targetsIPs
        self.wordlistDict = wordlistDict

    def run(self):
        logger.info("Starting " + self.workername)
        if self.scope == "Action":
            logger.info("My duty is to Find")
            while True:
                self.lock.acquire()
                if (len(list(self.targetdict.keys())) == 0):
                    logger.debug(f"[{self.workername}] | " +
                                 "No Targets left to analyze, Ciao Grande")
                    self.lock.release()
                    break
                key = list(self.targetdict.keys())[0]
                logger.info(f"[{self.workername}] | " + "Targets left to analyze: " +
                            str(len(list(self.targetdict.keys()))))
                try:
                    targetobj = {}
                    if len((self.targetdict[key])) > 0:
                        logger.debug(f"[{self.workername}] | " + "Shares to analyze left for: " +
                                     key + " are: " + str(len((self.targetdict[key]))))
                        targetobj[key] = (self.targetdict[key]).pop(0)
                    else:
                        logger.debug(f"[{self.workername}] | " + "No shares left for: " + key +
                                     ", I'm going to pop it out")
                        self.targetdict.pop(key)
                finally:
                    self.lock.release()
                    if bool(targetobj):
                        rsmbi = rsmicore(
                            self.workername, self.options, self.db, targetobj, None, self.wordlistDict)
                        rsmbi.analyzeTarget()
                        # self.targetdict[]
        else:
            logger.info("My duty is to enum")
            # workername, options, db, targetobj, targetIPenum
            while len(self.targetsIPs) > 0:
                ipToEnum = self.targetsIPs.pop(0)
                rsmbi = rsmicore(
                    self.workername, self.options, None, None, ipToEnum, None)
                toDict = rsmbi.enumTargets()
                self.targetdict[toDict[0]] = toDict[1]
        logger.info("Exiting " + self.workername)