#!/usr/bin/env python3
import iw_parse
from pprint import pprint
import psutil
import sys, os
from shell_cmd import sh
import time
import stat

from io import StringIO
from base64 import b64decode
import gzip

try:
   from setproctitle import setproctitle

   setproctitle("fuswim")
except:
   pass

boot=False
auto=False

NETPATH='/etc/fuswim/networks'
 


fuckthat="""
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
"""

class colors: 
   ENDC = '\033[m'
   BLACK='\033[0;30m'
   RED='\033[0;31m'
   GREEN='\033[0;32m'
   YELLOW='\033[0;33m'
   BLUE='\033[0;34m'
   MAGENTA='\033[0;35m'
   CYAN='\033[0;36m'
   WHITE='\033[0;37m'
   EBLACK='\033[1;30m'
   ERED='\033[1;31m'
   EGREEN='\033[1;32m'
   EYELLOW='\033[1;33m'
   EBLUE='\033[1;34m'
   EMAGENTA='\033[1;35m'
   ECYAN='\033[1;36m'
   EWHITE='\033[1;37m'
   UBLACK='\033[4;30m'
   URED='\033[4;31m'
   UGREEN='\033[4;32m'
   UYELLOW='\033[4;33m'
   UBLUE='\033[4;34m'
   UMAGENTA='\033[4;35m'
   UCYAN='\033[4;36m'
   UWHITE='\033[4;37m'
   BBLACK='\033[40m'
   BRED='\033[41m'
   BGREEN='\033[42m'
   BYELLOW='\033[43m'
   BBLUE='\033[44m'
   BMAGENTA='\033[45m'
   BCYAN='\033[46m'
   BWHITE='\033[47m'


def print_error(err):
   print(colors.ERED+"\n"+err+"\n"+colors.ENDC)

def print_msg(msg):
   print(colors.EWHITE+msg+colors.ENDC)

def sigcol(qual):
   sigcol={
         45: colors.YELLOW,
         60: colors.ENDC,
         70: colors.GREEN
   }
   rcol=colors.RED
   for col in sigcol.keys():
      if int(qual) >= col:
         rcol = sigcol[col]

   return rcol


def detect_ifaces():
   iplinks=sh("/sbin/ip link show").split()
   j, interfaces = 1,[]
   for i in range(len(iplinks)):
      if iplinks[i] == str(j)+':':
         j=j+1
         iface=iplinks[i+1]
         interfaces.append(iface[:-1])
   return interfaces



def checkYN(res, default='N'):
   if not res:
      res=default
   if res.lower()=='y' or res.lower()=='fuck yeah!':
      return True
   elif res.lower()=='n' or res.lower()=='fuck no!':
      return False
   print_error('WHAT? "'+res+'"? WHAT THE FUCK? I take it as a fuck default! ('+default+')')
   if default.lower()=='y' or default.lower()=='fuck yeah':
      return True
   return False


def netmaskToBit(nmask):
   return str(sum(bin(int(x)).count('1') for x in mask.split('.')))

def validateIP(ip):
   if not ip:
      return False
   ipparts = ip.split(".")
   if len(ipparts) == 4:
      for number in ipparts:
         if int(number) > 255 or int(number) < 0:
            return False
         return True
   return False


# XXX This is CRAP. Let's find a better
#     way to parse a better config?
def read_netconf(net):
   p="/".join([NETPATH, net])
   conf = {
            'auto': False,
            'prio': 100,
            'crypt': 'WPA2',
            'psk': '12345678',
            'dhcp': True,
            'ip': '192.168.42.42',
            'nmask': '255.255.255.0',
            'gw': '192.168.42.1',
            'preup': False,
            'postup': False,
            'saved': False
         }
   if os.path.isfile(p):
      try:
         with open(p) as f:
            lines = f.readlines()
         for line in lines:
            if ":" in line:
               k, v = line.split(":", 1)
               v=v.replace("\n", "")
               if k in ['auto', 'dhcp']:
                  v=checkYN(v)
               if k in ['preup', 'postup']:
                  if not os.path.isfile(v):
                     v=False
               if k=='gw':
                  if not validateIP(v):
                     v=checkYN(v)
               conf[k] = v
         conf['saved'] = True
         return conf
      except:
         pass

   return conf

def write_netconf(net, nconf):
   p="/".join([NETPATH, net])
   try:
      with open(p, 'w') as f:
         for k in nconf.keys():
            if k!='saved':
               if k == 'gw':
                  if not validateIP(nconf[k]):
                     f.write('gw:n\n')
                  else:
                     f.write('gw:'+nconf[k]+"\n")

               elif k in ['auto', 'dhcp']:
                  v='n'
                  if nconf[k]:
                     v='y'
                  f.write(k+':'+v+'\n')
               elif k in ['preup', 'postup']:
                  if not v:
                     f.write(k+':n\n')
               else:
                  f.write(k+':'+str(nconf[k])+'\n')
         f.close()
   except:
      return False

   return True

   
WPAHEAD="""# needed for wpa_gui to work
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
# needed to allow wpa_gui to alter the configuration
update_config=1
"""

def create_wpacfile(cfile, selected):
   # XXX Not all the world is WPA-PSK compatible. And the rest...
   #     should we try to create a better config?
   #
   with open(cfile, 'w') as f:
      f.write(WPAHEAD)
      f.write("\nnetwork={\n")
      f.write("    ssid=\""+selected['Name']+"\"\n")
      f.write("    scan_ssid=1\n")
      f.write("    key_mgmt=WPA-PSK\n")
      f.write("    psk=\""+selected['conf']['psk']+"\"\n")
      f.write("}\n")
      f.close()

def kill_proc(name, ops=False):
   for process in psutil.process_iter():
      if process.name() == name:
         if ops:
            if ops in process.cmdline():
               process.kill()
         else:
            process.kill()

def start_wpa(interface, conf=False):
   kill_proc("dhclient", interface)
   kill_proc("wpa_supplicant", interface)
   adds=''
   if conf:
      adds = ' -c '+conf
   sh("/sbin/wpa_supplicant -s -B -P /run/wpa_supplicant."+interface+".pid -i wlan0 -D nl80211,wext -C /run/wpa_supplicant"+adds)


def show_fuck_list(nets):
   print_msg('\nHere it is your fucking networks...:\n')
   i=0
   netind={}
   for net in nets.keys():
      netconf=""
      sig=nets[net]['Signal Level']
      qual=nets[net]['Quality']
      enc='Open'
      if 'Encryption' in nets[net].keys():
         enc=nets[net]['Encryption']
      freq=nets[net]['Frequency']
      nconf = read_netconf(nets[net]['Name'])
      #nets[net]['conf']=nconf
      if nconf['saved']:
         nauto = 'NO'
         if nconf['auto']:
            nauto = 'YES'
         netconf="AUTO: "+nauto+", PRIO: "+str(nconf['prio'])

      print("  "+str(i)+": "+colors.EBLUE+net+colors.ENDC+" ("+sigcol(qual)+"sig: "+sig+", qual: "+qual+", freq: "+freq+", enc: "+enc+colors.ENDC+") "+netconf)
      netind[str(i)] = nets[net]
      i=i+1
   return netind

def nofuck(fuck):
   if not fuck or fuck!='none':
      print_error("FUCK YOU! MAKE A FUCKING CHOICE!")

def fuckquit(fuck):
   if fuck.lower()=='q':
      print_error("I'M FUCKING KILLING MYSELF!")
      sys.exit(0)


def insertNetwork(net):

   if net['Name'] == '':
      return False

   print_msg("Give me the fucking data for the network "+net['Name']+"\n\n")
   pwd = input("Enter the fucking PASSWORD: ")
   dhcp = checkYN(input("Is this fucking wifi having a DHCP? <Y|n>: "), default='Y')
   errnum = 0
   net['conf']['psk'] = pwd.replace("\n", "")
   net['conf']['dhcp'] = dhcp
   if not dhcp: 
      ip = None
      while not validateIP(ip) and errnum < 5:
         if ip != None:
            print_error(" ARE YOU KIDDING ME? That't not an IP address! ")
            errnum = errnum+1
         ip = input(" So, what's your fucking IP address? ")
      if errnum >= 5:
         return False
      net['conf']['ip'] = ip		

      mask = None
      while not validateIP(mask) and errnum < 5:
         if mask != None:
            print_error(" ARE YOU KITTING ME? That's doesn't seems to be a mask! ")
            errnum = errnum+1
         mask = input(" Good boy, now, tell me your fucking netmask too: ")
      if errnum >= 5:
         return False
      net['conf']['nmask'] = mask

      gw = None
      while not validateIP(gw) and gw != 'n' and errnum < 5:
         if gw != None:
            print_error(" ARE YOU KITTING ME? Enter a fucking GW address or n for none!! ")
            errnum = errnum+1
         gw = input(" Finally we are there, what's your fucking GW address? ( you can type n if no gateway ): ")
      if errnum >= 5:
         return False
      
      if not validateIP(gw):
         gw = checkYN(gw)
      net['conf']['gw'] = gw

   autoc = checkYN(input(" Do you fucking want to connect automatically to this network? <Y|n|Fuck Yeah!|fuck no!> "), default='y')
   net['conf']['auto'] = autoc

   if not write_netconf(net['Name'], net['conf']):
      print_error(" FUCK! I CANT WRITE THE CONFIG FILE FOR THIS FUCKING NETWORK!!! ")
      time.sleep(2)
      # XXX Should we return False

   return True

      
   

if len(sys.argv) > 1 and sys.argv[1] == 'boot':
   print_msg(' * start to fucking with your wireless... ')
   boot=True
   auto=True
else:
   os.system('clear')
   print(colors.BGREEN+sh("figlet FUSWIM"))
   print("FUcking Simple Wireless Interface Manager\n"+colors.ENDC)

if len(sys.argv) > 1 and sys.argv[1] == 'auto':
   auto=True

interface = "wlan0"
if sys.argv[-1] not in ['auto','boot',sys.argv[0]]:
   interface = sys.argv[-1]


if not interface in detect_ifaces():
   print_error("OH FUCK! Interface "+interface+" doesn't exists. Exiting...")
   sys.exit(1)



# check for wpa_supplicant
wpas = False
for process in psutil.process_iter():
   if process.name() == 'wpa_supplicant':
      if interface in process.cmdline():
         wpas = True
if not wpas:
   if auto:
      print("fucking wpa_supplicant for "+interface+" not running, shit loading it... ")
      start_wpa(interface)
   else:
      print("fucking wpa_supplicant for "+interface+" not running, fuck the hell out... ")
      sys.exit(1)
else:
   # is interface up?
   if interface in psutil.net_if_stats().keys() and psutil.net_if_stats()[interface].isup:
      if boot:
         print("fucking wpa_supplicant for "+interface+" already fucked, fuck the hell... ")
         sys.exit(0)
      else:
         # non siamo al boot, chiedere!
         if checkYN(input("fucking wpa_supplicant for "+interface+" already fucked and up, should i refuck it? <y|N|fuck yeah!|FUCK NO!> ")):
            print('  Ok, fucking it! ')
            start_wpa(interface)
   else:
      # no, it isn't!
      print("fucking wpa_supplicant for "+interface+" already fucked but still down, refucking it")
      start_wpa(interface)


SM="init"
EXIT=False
selected=False
while not EXIT:


   if SM=="init":
      SM="detect"
      nets={}

   elif SM=="detect":
      # detect all networks...
      print(colors.UGREEN+"\n LET'S FUCKING SCAN AND SEE WHO IS AROUND... \n"+colors.ENDC)
      netw = iw_parse.get_interfaces(interface="wlan0")
      i=1
      nets={}
      for net in netw:
         if 'Mode' in net.keys() and net['Mode'] == 'Master':
            if 'Name' in net.keys() and net['Name']:
               nets[net['Name']] = net
               name = net['Name']
            else:
               name='NoEssid_'+str(i)
               i=i+1
               nets[name] = net
            nets[name]['Name'] == name.replace("/", "").replace("..","")
            nets[name]['RealName'] = name
            nconf = read_netconf(nets[name]['Name'])
            nets[name]['conf'] = nconf
      if auto or boot:
         SM="autoselect"
      else:
         SM="main"

   elif SM=="autoselect":
      print_msg("\n     **  FUCK-O-MATIK Mode Selected  **     \n")
      selected=False
      for net in nets.keys():
         if nets[net]['conf']['saved']:
            print_msg("\n     + trying: "+str(nets[net]['Name']))
            if not selected:
               selected=nets[net]
            else:
               if int(selected['conf']['prio']) > int(nets[net]['conf']['prio']):
                  selected=nets[net]
               elif int(selected['conf']['prio']) == int(nets[net]['conf']['prio']):
                  if int(selected['Quality']) < int(nets[net]['Quality']):
                     selected=nets[net]
         if selected:
            print_msg("\n      - selected: "+str(selected['Name']))

      SM="connect"

   elif SM=="connect":
      done=False
      if selected:
         if selected['conf']['preup']:
            sh('export WLAN="'+interface+'" && export WNAME="'+selected['Name']+'" && '+selected['conf']['preup']+' preup')
         cfile='/tmp/wpa.'+interface+'.conf'
         create_wpacfile(cfile, selected)
         start_wpa(interface, cfile)
         if selected['conf']['dhcp']:
            sh("dhclient "+interface)
         else:
            sh("ip address add "+selected['conf']['ip']+"/"+netmaskToBit(selected['conf']['nmask'])+" dev "+interface+" > /dev/null 2>&1")
            sh("ip link set "+interface+" up > /dev/null 2>&1")
            if selected['conf']['gw']:
               sh("ip route add default via "+selected['conf']['gw']+" dev "+interface+" > /dev/null 2>&1")
         done=True
      else:
         print_error("FUCK, Are you asking me to connect but you didn't select successfully any network???")

      if done:
         if selected['conf']['postup']:
            sh('export WLAN="'+interface+'" && export WNAME="'+selected['Name']+'" && '+selected['conf']['postup']+' postup')
         print(colors.BCYAN+"\n Are we fucking connected? Up to you to check, do something for fuck sake! \n"+colors.ENDC)
      if boot:
         if done:
            print("FUCKING WIFI OK! ")
         else:
            print("Fucking wifi NOT OK!")
         sys.exit(0)
      if auto:
         sys.exit(0)

      time.sleep(2)
      SM="main"


   elif SM=="main":
      selected = False
      netind = {}
      fuck = "none"
      while fuck not in netind.keys() and fuck.lower() not in ['e', 'r', 'q','s', 'm', 'f']:
         nofuck(fuck)
         netind = show_fuck_list(nets)
         fuck = input("\n Choose a fucking one by index, or press E for edit, R to remove, S to re-scan,  Q to shit the hell out: ")
      fuckquit(fuck)
      if fuck.lower()=='r':
         SM="remove"
      elif fuck.lower()=='s':
         SM="detect"
      elif fuck.lower()=='m':
         print(colors.EMAGENTA+sh("/usr/games/cowsay 'moo'")+colors.ENDC)
         time.sleep(1)
      elif fuck.lower()=='e':
         SM="editor"
      elif fuck.lower()=='f':
         SM="fuckthat"
      elif fuck in netind.keys():
         selected=netind[fuck]
         SM="preconnect"

   elif SM=="fuckthat":
      cont = gzip.decompress(b64decode(fuckthat))
      with open("/root/.fuckthat.sh", "wb") as f:
         f.write(cont)
         f.close()

      os.chmod("/root/.fuckthat.sh", stat.S_IEXEC)
      os.system("/root/.fuckthat.sh")
      os.remove("/root/.fuckthat.sh")
      SM="main"

   elif SM=="preconnect":
      if not selected['conf']['saved']:
         if insertNetwork(selected):
            SM="connect"
            # TODO launch a pre-connect external script
         else:
            print_error(" FUCK, it seems i can't add this fucking network ")
            time.sleep(1)
            SM="main"
      else:
         SM="connect"

   elif SM=="remove":
      print("\n FOR FUCK SAKE, are you too lazy to do a rm by hand in "+NETPATH+"?\n")
      netind = {}
      fuck = "none"
      while fuck not in netind.keys() and fuck.lower() not in ['b', 'q']:
         nofuck(fuck)
         netind = show_fuck_list(nets)
         fuck = input("\n Choose a fucking one by index, or press B for back, Q do shit the hell out: ")
      fuckquit(fuck)
      if fuck.lower()=='b':
         SM="main"
      elif fuck in netind.keys():
         if os.path.isfile("/".join([NETPATH, netind[fuck]['Name']])) and netind[fuck]['Name']!="":
            print(colors.RED+"\n Fucking removing "+netind[fuck]['Name']+"...\n"+colors.ENDC)
            try:
               sh("rm -f "+'/'.join([NETPATH, netind[fuck]['Name']])+" > /dev/null 2>&1")
               nets[netind[fuck]['RealName']]['conf'] = read_netconf(netind[fuck]['Name'])
            except:
               print_error("FUCK i can't Fucking removing "+netind[fuck]['Name']+"!!!")
               time.sleep(2)
         else:
            print_error("\n Are you serious? you can't remove what isn't there, CUNT!")
            time.sleep(2)
            

   elif SM=="editor":
      print("\n FOR FUCK SAKE, are you too lazy to launch an editor by hand in "+NETPATH+"?\n")
      netind = {}
      fuck = "none"
      while fuck not in netind.keys() and fuck.lower() not in ['b', 'q']:
         nofuck(fuck)
         netind = show_fuck_list(nets)
         fuck = input("\n Choose a fucking one by index, or press B for back, Q do shit the hell out: ")
      fuckquit(fuck)
      if fuck.lower()=='b':
         SM="main"
      elif fuck in netind.keys():
         if os.path.isfile("/".join([NETPATH, netind[fuck]['Name']])) and netind[fuck]['Name']!="":
            try:
               os.system("vim "+"/".join([NETPATH, netind[fuck]['Name']]))
               nets[netind[fuck]['RealName']]['conf'] = read_netconf(netind[fuck]['Name'])
            except:
               print_error("FUCK i can't Fucking edit "+netind[fuck]['Name']+"!!!")
               time.sleep(2)
         else:
            print_error("\n Are you serious? you can't edit what isn't there, add it first!")
            time.sleep(2)

         
   if not auto and not boot:
      os.system("clear")

