#!/usr/bin/env python
#
# How to cook a covert channel - v1.0 - http://gray-world.net
# Copyright (c) 2006, Gray World Team <team [at] gray-world.net>
#
# cook_cgi.py - v1.0
#

import sys, time, struct, binascii, os, cgi, Cookie
from os import R_OK, W_OK
from socket import inet_aton,inet_ntoa
from sha import sha

###############################################################################
### Globales
###############################################################################

PROGNAME="cook_cgi.py"
PROGVERSION="1.0"

ADMIN_PASS="grayworld"

KITCHEN="/var/www/kitchen/kitchen"

# Common values with clients
PADBYTE='\x42'              # default padding byte
COOKIE_NAME="PREF="         # default cookie name
COOKIE_SIZE=24              # default cookie size
MAX_COOKIE_SIZE=100         # Max cookie size
PADDING_ACT=0               # default : no padding

# Xoring the message
KEY="cooking covert channels"
RBYTES="Soon her eye fell on a little glass box that was lying under the table:"
RBYTES=RBYTES+" she opened it, and found in it a very small c...ookie"
RBYTES_POS=0
RBYTES_POSI=0

# Server commands
UPDATE_CTIME=ord('\x01')
UPDATE_RBYTES=ord('\x02')
UPDATE_SIZE=ord('\x03')

# Client commands
AM_UP='\x01'
REPEAT_AM_UP=0
LOST_SYNC=-1
CMD_SENT=42
ACK_UPDATE_CTIME=~UPDATE_CTIME & 0xff
ACK_UPDATE_SIZE=~UPDATE_SIZE & 0xff
ACK_UPDATE_RBYTES=~UPDATE_RBYTES & 0xff

# Data file

storage_info = { "cln": 0, "ctime": None, "lip": None, "stime": None, "period": None,
                 "rb_pos": None, "rb_posi": None, "cookie": None, "lsync": None, "lsync_time": None,
                 "cmd": None, "csize": None, "padding": None, "rbytes": None }

# Cgi parameters

REMOVE_QUIET=3600 # Remove quiet clients since 10 seconds
REFRESH_DELAY=10  # Refreshing admin panel when cmd isn't used

NOKITCHEN=0

NO_DBL_STOCK=1 # Cannot double stock a command

FAKE_COOKIE_NAME="ID="
FAKE_COOKIE_VAL=binascii.b2a_hex(sha(str(time.time())).digest())
#FAKE_COOKIE=FAKE_COOKIE_NAME+FAKE_COOKIE_VAL # Set here to None to disable
FAKE_COOKIE=None # Set here to None to disable

# Sending cookie to clients

COOKIE_DOMAIN=".grayworld.net"
COOKIE_EXPIRES="Fri, 01-Jan-2021 00:00:00 GMT"
COOKIE_PATH="/"

###############################################################################
### Templates
###############################################################################

head_tpl_vars={"prog":PROGNAME,"version":PROGVERSION, "refresh":""}

head_tpl="""<html>\n<head>\n
  <title>How to cook a covert channel</title>\n%(refresh)s<head>\n<body>\n
  <pre>$ ./cook_cgi\nHow to cook a covert channel - %(prog)s - v%(version)s</pre>"""

client_tpl="""\n  <pre>Visit the kitchen and ask Bryan...</pre>"""

tail_tpl="""\n  <pre>$ <b><a href="">_</a></b></pre>\n</body>\n</html>\n"""
tailp_tpl="""\n  <pre>$ <b><a href="?pass=%s">_</a></b></pre>\n</body>\n</html>\n"""

tpl_cl_info="""  <pre>Bryan says : Welcome in the kitchen, we have %d client(s) (%s)
  o Remove clients quiet for more than %d seconds.
  o Don't double stock idem command : %d
  o Fake cookie for standard clients : %s
  o Refresh delay : %d
  o <a href="?pass=%s&cmd=42">Burn the kitchen</a>\n\nClients list :</pre>\n"""

tpl_cl_list="""  <pre>\n  #%(cln)d - Public IP %(rip)s (last connection time: %(ctime)s)
       => Local IP %(lip)s (started the %(stime)s / contact period: %(period)s secs)
       => RBYTES_POS: %(rb_pos)d , RBYTES_POSI: %(rb_posi)d - %(remains)d/%(total)d bytes , %(remains2)d/%(total2)d rbytes available
       => RBYTES: %(rbytes)s
       => Cookie size is set to %(csize)d bytes and padding activation is set to %(padding)d
       => Last cookie: '%(cookie)s' / Lost sync: %(lsync)s (last: %(lsync_time)s)\n
    What you have ? 
      <a href="?pass=%(passwd)s&cmd=1&cln=%(cln)d&period=5">New contact period</a> , <a href="?pass=%(passwd)s&cmd=2&cln=%(cln)d&rbytes=priatnovoapetita">New rbytes</a> , <a href="?pass=%(passwd)s&cmd=3&cln=%(cln)d&csize=42&padding=1">Change cookie size</a> , <a href="?pass=%(passwd)s&cmd=3&cln=%(cln)d&csize=%(csize)d&padding=0">Disable</a> / <a href="?pass=%(passwd)s&cmd=3&cln=%(cln)d&csize=%(csize)d&padding=1">Enable</a> padding
      <a href="?pass=%(passwd)s&cmd=0&cln=%(cln)d">Remove commands</a>\n
    Stocked commands:</pre>\n"""

tpl_cl_no_list="""\n  <pre>Bryan says : <b>No clients</b></pre>\n"""

tpl_cl_no_kitchen="""\n  <pre>Bryan says : <b>No kitchen!</b></pre>\n"""

tpl_stocked_cmd="""  <pre>      o '%s'</pre>"""

tpl_update_cp="""\n  <pre>Bryan says : <b>Stocked a %s secs update contact period for client %s</b>.</pre>\n"""
tpl_update_rbytes="""\n  <pre>Bryan says : <b>Stocked %d rbytes for client %s : %s</b>.</pre>\n"""
tpl_update_csize="""\n  <pre>Bryan says : <b>Stocked cookie size update to %s with padding to %s for client %s</b>.</pre>\n"""

###############################################################################
### Generic
###############################################################################

def prepare_xor(lg) :
  global RBYTES_POS,RBYTES_POSI
  if RBYTES_POSI > 0 :
    toxor=sha(KEY+RBYTES[:RBYTES_POS]).digest()[20-RBYTES_POSI:] # 20 is sha size
    RBYTES_POSI=0
  RBYTES_POS += 1
  if RBYTES_POS > len(RBYTES)-1 : return ""
  toxor=sha(KEY+RBYTES[:RBYTES_POS]).digest()
  while lg > len(toxor) :
    RBYTES_POS += 1
    if RBYTES_POS > len(RBYTES)-1 : return ""
    toxor = toxor + sha(KEY+RBYTES[:RBYTES_POS]).digest()
  RBYTES_POSI = len(toxor)-lg
  return toxor[:len(toxor)-RBYTES_POSI]

def init_xor_parameters():
  global RBYTES_POS,RBYTES_POSI
  RBYTES_POS=0
  RBYTES_POSI=0

def set_xor_parameters(data):
  global RBYTES_POS,RBYTES_POSI,RBYTES
  if not data: return None
  if data.has_key(RADDR):
    RBYTES_POS=data[RADDR]["rb_pos"]
    RBYTES_POSI=data[RADDR]["rb_posi"]
    if data[RADDR].has_key("rbytes") and data[RADDR]["rbytes"] != None:
      RBYTES = data[RADDR]["rbytes"]
    debug("COOK_CGI: SETXORPARAM to %d/%d" % (RBYTES_POS,RBYTES_POSI))
    return 1
  return None

def checksum(str):
  return(binascii.crc32(str) & 0xffff)

def strxor(x,y):
    return "".join(map(lambda x,y:chr(ord(x)^ord(y)),x,y))

def debug(data):
  sys.stderr.write(data)

###############################################################################
### Data file (based on scapy session functions)
###############################################################################

import cPickle, gzip

def save_data(data=None,pickleProto=-1): # Dump data to file
  try:
    f=gzip.open(KITCHEN,"wb")
  except:
    return None
  cPickle.dump(data, f, pickleProto)
  f.close()

def load_data(): # Load data from file
  global NOKITCHEN
  if not os.access(KITCHEN,R_OK): 
    NOKITCHEN=1
    return None
  res=None
  try:
    res=cPickle.load(gzip.open(KITCHEN,"rb"))
  except:
    return None
  if res: update_data(res)
  return res

def update_data(data): # Remove quiet clients from data and save now
  cur_time = int(time.time())
  tmp=[]
  for key in data:
    if data[key]["ctime"]:
      if data[key]["ctime"] + REMOVE_QUIET < cur_time:
        tmp.append(key)
  for key in tmp: del data[key]
  save_data(data)

def get_new_cln(data,ip):
  if not data: return 1
  max=1
  for key in data:
    tmp = data[key]["cln"]
    if max <= tmp: max=tmp+1
  return (max)

def save_info(cooked,data,cookie): # Save info to data file with save_data()
  if not cooked: return None
  if not data:
    data={}
    cln=1
  else:
    if data.has_key(RADDR) and data[RADDR].has_key("cln") and data[RADDR]["cln"]:
      cln=data[RADDR]["cln"]
    else:
      cln = get_new_cln(data,RADDR)
  tmp=storage_info

  if (cooked[0] == AM_UP):
    tmp["cln"] = cln
    tmp["ctime"] = time.time()
    tmp["lip"] = cooked[1][0]
    tmp["stime"] = cooked[1][1]
    tmp["period"] = cooked[1][2]
    tmp["rb_pos"] = RBYTES_POS
    tmp["rb_posi"] = RBYTES_POSI
    tmp["cookie"] = cookie
    tmp["lsync"] = 0
    tmp["lsync_time"] = 0
    if data.has_key(RADDR) and data[RADDR].has_key("cmd")\
      and data[RADDR]["cmd"]: tmp["cmd"]=data[RADDR]["cmd"]
    else: tmp["cmd"]=None
    if data.has_key(RADDR) and data[RADDR].has_key("csize")\
      and data[RADDR]["csize"]: tmp["csize"]=data[RADDR]["csize"]
    else: tmp["csize"]=COOKIE_SIZE
    if data.has_key(RADDR) and data[RADDR].has_key("padding")\
      and ( data[RADDR]["padding"] or data[RADDR]["padding"] == 0): tmp["padding"]=data[RADDR]["padding"]
    else: tmp["padding"]=PADDING_ACT
    if data.has_key(RADDR) and data[RADDR].has_key("rbytes")\
      and data[RADDR]["rbytes"]: tmp["rbytes"]=data[RADDR]["rbytes"]
    else: tmp["rbytes"]=RBYTES
    data[RADDR] = tmp

  if (cooked[0] == REPEAT_AM_UP):
    if data.has_key(RADDR):
      data[RADDR]["ctime"] = time.time()
      data[RADDR]["lsync"] = 0
      data[RADDR]["lsync_time"] = 0

  if (cooked[0] == LOST_SYNC):
    if data.has_key(RADDR):
      data[RADDR]["lsync_time"] = time.time()
      data[RADDR]["lsync"] += 1

  if (cooked[0] == CMD_SENT):
    if data.has_key(RADDR):
      data[RADDR]["rb_pos"] = RBYTES_POS
      data[RADDR]["rb_posi"] = RBYTES_POSI
      debug("COOK_CGI: SAVEXORPARAM %d/%d" % (RBYTES_POS,RBYTES_POSI))

  if (cooked[0] == ACK_UPDATE_CTIME):
    data[RADDR]["rb_pos"] = RBYTES_POS
    data[RADDR]["rb_posi"] = RBYTES_POSI
    data[RADDR]["ctime"] = time.time()
    if data.has_key(RADDR) and data[RADDR].has_key("cmd") and data[RADDR]["cmd"]:
      cmd=None
      for i in data[RADDR]["cmd"]:
        if cmd == None and int(cooked[0]) == ~struct.unpack("!B",binascii.a2b_hex(i[4:6]))[0]&0xFF:
          cmd=i
      data[RADDR]["cmd"].remove(cmd)
      debug("COOK_CGI: ACK_TIME %s" % (repr(cmd)))

  if (cooked[0] == ACK_UPDATE_SIZE):
    data[RADDR]["rb_pos"] = RBYTES_POS
    data[RADDR]["rb_posi"] = RBYTES_POSI
    if data.has_key(RADDR) and data[RADDR].has_key("cmd") and data[RADDR]["cmd"]:
      cmd=None
      for i in data[RADDR]["cmd"]:
        if cmd == None and int(cooked[0]) == ~struct.unpack("!B",binascii.a2b_hex(i[4:6]))[0]&0xFF:
          cmd=i
      data[RADDR]["csize"] = struct.unpack("!H",binascii.a2b_hex(i[6:10]))[0]
      data[RADDR]["padding"] = struct.unpack("!B",binascii.a2b_hex(i[10:12]))[0]
      data[RADDR]["cmd"].remove(cmd)
      debug("COOK_CGI: ACK_SIZE (%d/%d) %s" % (data[RADDR]["csize"],data[RADDR]["padding"],repr(cmd)))

  if (cooked[0] == ACK_UPDATE_RBYTES):
    data[RADDR]["rb_pos"] = RBYTES_POS
    data[RADDR]["rb_posi"] = RBYTES_POSI
    if data.has_key(RADDR) and data[RADDR].has_key("cmd") and data[RADDR]["cmd"]:
      cmd=None
      for i in data[RADDR]["cmd"]:
        if cmd == None and int(cooked[0]) == ~struct.unpack("!B",binascii.a2b_hex(i[4:6]))[0]&0xFF:
          cmd=i
      tmp = binascii.a2b_hex(cmd)
      rbytes_lg = struct.unpack("!H",tmp[3:5])[0]
      rbytes = struct.unpack("!"+str(rbytes_lg)+"s",tmp[5:rbytes_lg+5])[0]
      data[RADDR]["rbytes"] = data[RADDR]["rbytes"]+rbytes
      data[RADDR]["cmd"].remove(cmd)
      debug("COOK_CGI: ACK_RBYTES (%d) '%s'" % (rbytes_lg,repr(rbytes)))

  save_data(data)
  if (cooked[0] == ACK_UPDATE_CTIME) or (cooked[0] == ACK_UPDATE_SIZE)\
  or (cooked[0] == ACK_UPDATE_RBYTES):
    return "" # We return nothing so that we are sure no cookie will be sent to client
  return(data)

###############################################################################
### HTTP
###############################################################################

def burn_cookie(name,value):
  cook = Cookie.SimpleCookie()
  cook[name[:len(name)-1]]=value
  cook[name[:len(name)-1]]['domain']=COOKIE_DOMAIN
  cook[name[:len(name)-1]]['expires']=COOKIE_EXPIRES
  cook[name[:len(name)-1]]['path']=COOKIE_PATH
  print cook

def display_list():
  data = load_data()
  if not data:
    if NOKITCHEN == 1:
      print tpl_cl_no_kitchen
    else:
      print tpl_cl_no_list
  else:
    print tpl_cl_info % (len(data), time.ctime(time.time()), REMOVE_QUIET, NO_DBL_STOCK, FAKE_COOKIE, REFRESH_DELAY, ADMIN_PASS)
    for i in data:
      tmp_rbytes=None
      tmp_len = len(data[i]["rbytes"])
      if tmp_len > 32 : tmp_rbytes = data[i]["rbytes"][0:16]+" [...] "+data[i]["rbytes"][tmp_len-16:tmp_len]
      else: tmp_rbytes = data[i]["rbytes"][0:tmp_len]
      tmp_lsync_time = data[i]["lsync_time"]
      if tmp_lsync_time == 0: tmp_lsync_time = ""
      else: tmp_lsync_time = time.ctime(int(tmp_lsync_time))

      tpl_vars = { "rip"    : i,
                   "cln"    : data[i]["cln"],
                   "ctime"  : time.ctime(int(data[i]["ctime"])),
                   "lip"    : inet_ntoa(data[i]["lip"]),
                   "stime"  : time.ctime(int(struct.unpack(">"+"I",data[i]["stime"])[0])),
                   "period" : struct.unpack(">"+"H",data[i]["period"])[0],
                   "rb_pos" : data[i]["rb_pos"],
                   "remains": len(data[i]["rbytes"])-data[i]["rb_pos"],
                   "remains2": (len(data[i]["rbytes"])-data[i]["rb_pos"])*20,
                   "total"  : len(data[i]["rbytes"]),
                   "total2" : len(data[i]["rbytes"])*20, # 20 is sha size
                   "rb_posi": data[i]["rb_posi"],
                   "rbytes" : repr(tmp_rbytes),
                   "cookie" : data[i]["cookie"],
                   "lsync"  : data[i]["lsync"],
                   "lsync_time"  : tmp_lsync_time,
                   "csize"  : data[i]["csize"],
                   "padding": data[i]["padding"],
                   "passwd" : ADMIN_PASS }
      print tpl_cl_list % tpl_vars

      if (data[i].has_key("cmd")) and data[i]["cmd"]:
        for cmd in data[i]["cmd"]: print tpl_stocked_cmd % cmd

def display_main():
  if FAKE_COOKIE:
    burn_cookie(FAKE_COOKIE_NAME,FAKE_COOKIE_VAL)
  print "Content-type: text/html\r\n"
  print head_tpl % head_tpl_vars
  print client_tpl
  print tail_tpl

def reply_to_client(cooked,data):
  if data and data.has_key(RADDR) and data[RADDR].has_key("cmd") and data[RADDR]["cmd"]:
    set_xor_parameters(data)
    for cmd in data[RADDR]["cmd"]:
      msg=binascii.a2b_hex(cmd)   # we reformat the msg 
      toxor=prepare_xor(len(msg)) # prepare the random bytes
      if toxor:
        xor=strxor(msg,toxor)       # xor the msg
        cmd=binascii.b2a_hex(xor)   # hexify it
        debug("COOK_CGI: Sending cookie to client (%d:%d): %s" % (RBYTES_POS,RBYTES_POSI,repr(msg)))
        burn_cookie(COOKIE_NAME,cmd)
      else:
        debug("COOK_CGI: No more rbytes available")
    save_info((CMD_SENT,None),data,None)

  print "Content-type: text/html\r\n"
  print head_tpl % head_tpl_vars
  print client_tpl
  print tail_tpl

###############################################################################
### Cookies
###############################################################################

def prepare_cmd_cookie(cmdinfo):
  if PADDING_ACT == 1:
    padding=PADBYTE*((COOKIE_SIZE-2)-(len(cmdinfo)))
  else:
    padding=""
  cksum=struct.pack("!H",checksum(cmdinfo))
  cooked=binascii.b2a_hex(cksum+cmdinfo+padding)
  return (cooked)

def manage_cookie(cookie,data):
  debug("COOK_CGI: manage (%d<%d<%d) (%d/%d) - /%s/" % (len(COOKIE_NAME),len(cookie),MAX_COOKIE_SIZE,RBYTES_POS,RBYTES_POSI,cookie))

  st=cookie.find(COOKIE_NAME)
  if st < 0 or len(cookie) < len(COOKIE_NAME)+1 or\
               len(cookie) > MAX_COOKIE_SIZE: return ("")

  # Is it the same cookie as previously ?
  if data and data.has_key(RADDR):
    info = data[RADDR]
    if info and info["cookie"] == cookie:
      debug("COOK_CGI: manage REPEAT_AM_UP")
      return (REPEAT_AM_UP, cookie)

  # we have a cookie we have to try to decode
  xor=binascii.a2b_hex(cookie[len(COOKIE_NAME):])
  set_xor_parameters(data)
  toxor=prepare_xor(len(xor))
  if not toxor:
    debug("COOK_CGI: No more rbytes...")
    return("")
  unxor=strxor(xor,toxor)

  debug("COOK_CGI: manage2 (%d<%d<%d) (%d/%d) - /%s/" % (len(COOKIE_NAME),len(cookie),MAX_COOKIE_SIZE,RBYTES_POS,RBYTES_POSI,repr(unxor)))

  if len(unxor) < 13: return ("") # Minimal is checksum and command
  (cksum,cmd) = struct.unpack(">"+"2s1s",unxor[:3])

  if (cmd == AM_UP):
    debug("COOK_CGI: manage AM_UP")
    if (len(unxor) < 13): return("")
    (ip,starttime,period) = struct.unpack(">"+"4s4s2s",unxor[3:13])
    info=ip+starttime+period
    verify=struct.pack("!H",checksum(cmd+info))
    if verify == cksum:
      return (cmd , ( ip , starttime , period ) )
    else:
      return None

  if (ord(cmd) == ACK_UPDATE_CTIME) or (ord(cmd) == ACK_UPDATE_RBYTES) \
  or (ord(cmd) == ACK_UPDATE_SIZE):
    debug("COOK_CGI: manage ACK")
    if (len(unxor) < 13): return("")
    (ip,starttime,period) = struct.unpack(">"+"4s4s2s",unxor[3:13])
    info=ip+starttime+period
    verify=struct.pack("!H",checksum(cmd+info))
    if verify == cksum:
      return (ord(cmd) , None )
    else:
      return None

  debug("COOK_CGI: Cannot interpret cookie")

  # Something wrong, if IP is already registered we update lost_sync
  if data and data.has_key(RADDR):
    return (LOST_SYNC, None)

  return(None)

###############################################################################
### CGI commands
###############################################################################

# Burn the kitchen

def burn_kitchen():
  if os.access(KITCHEN,W_OK):
    try:
      f = gzip.open(KITCHEN,"wb")
    except:
      return None
    cPickle.dump(None, f, -1) 
    f.close()
  return None

# Remove commands

def remove_commands():
  cln=form.getvalue("cln")
  if not cln or len(cln) > 3 or not cln.isdigit(): return ""
  data=load_data()
  if not data: return ""

  ref=None
  for key in data:
    if data[key]["cln"] == int(cln):
      ref=key
  if not ref == None and data[ref].has_key("cmd"):
    del data[ref]["cmd"]
    save_data(data)
    return 1

  return ""

# Update the contact period

def update_cp():
  cln=form.getvalue("cln")
  period=form.getvalue("period")
  if not cln or len(cln) > 3 or not cln.isdigit(): return ""
  if not period or len(period) > 5 or not period.isdigit(): return ""
  data=load_data()
  if not data: return ""

  for key in data:
    if data[key]["cln"] == int(cln):
      # Need to update cperiod for cln
      # 01 : Change contact period (2 bytes) : set a new 'contact period'
      cmdinfo=struct.pack("!bH",UPDATE_CTIME,int(period))
      cooked=prepare_cmd_cookie(cmdinfo)
      if data[key].has_key("cmd") and data[key]["cmd"]:
        if NO_DBL_STOCK == 1:
          for test in data[key]["cmd"]:
            if test == cooked: return 1 # already present
        data[key]["cmd"].append(cooked)
      else:
        data[key]["cmd"] = [cooked]
      save_data(data)
      print tpl_update_cp % (period,cln)
      return 1

  return ""

# Update random bytes

def update_rbytes():
  cln=form.getvalue("cln")
  rbytes=form.getvalue("rbytes")
  if not cln or len(cln) > 3 or not cln.isdigit(): return ""
  if not rbytes or len(rbytes) > COOKIE_SIZE-3: return ""
  data=load_data()
  if not data: return ""

  for key in data:
    if data[key]["cln"] == int(cln):
      cmdinfo=struct.pack("!bH"+str(len(rbytes))+"s",UPDATE_RBYTES,len(rbytes),rbytes)
      cooked=prepare_cmd_cookie(cmdinfo)
      if data[key].has_key("cmd") and data[key]["cmd"]:
        if NO_DBL_STOCK == 1:
          for test in data[key]["cmd"]:
            if test == cooked: return 1 # already present
        data[key]["cmd"].append(cooked)
      else:
        data[key]["cmd"] = [cooked]
      save_data(data)
      print tpl_update_rbytes % (len(rbytes),cln,repr(rbytes))
      return 1

  return ""

# Update cookie size and enable/disable padding

def update_csize():
  cln=form.getvalue("cln")
  csize=form.getvalue("csize")
  padding=form.getvalue("padding")
  if not cln or len(cln) > 3 or not cln.isdigit(): return ""
  if not csize or not padding: return ""
  if len(csize) > 4 or not csize.isdigit() or int(csize) > MAX_COOKIE_SIZE: return ""
  if len(padding) > 1 or not padding.isdigit(): return ""
  data=load_data()
  if not data: return ""

  for key in data:
    if data[key]["cln"] == int(cln):
      cmdinfo=struct.pack("!bHB",UPDATE_SIZE,int(csize),int(padding))
      debug("COOK_CGI: Preparing Update size command - size : %d bytes - padding %d" % (int(csize),int(padding)))
      cooked=prepare_cmd_cookie(cmdinfo)
      if data[key].has_key("cmd") and data[key]["cmd"]:
        if NO_DBL_STOCK == 1:
          for test in data[key]["cmd"]:
            if test == cooked: return 1 # already present
        data[key]["cmd"].append(cooked)
      else:
        data[key]["cmd"] = [cooked]
      save_data(data)
      print tpl_update_csize % (csize,padding,cln)
      return 1

  return ""

###############################################################################
### Main
###############################################################################

try:
  cookie = os.environ["HTTP_COOKIE"]
except:
  cookie = None

try:
  RADDR=os.environ["REMOTE_ADDR"]
except:
  RADDR = None

if cookie and RADDR:
  data=load_data()
  cooked = manage_cookie(cookie,data)
else:
  cooked = None

if not cooked: # Not a cooking client

  try:
    form=cgi.FieldStorage()
  except:
    display_main()

  if form.getvalue("pass") and form.getvalue("pass") == ADMIN_PASS:
    print "Content-type: text/html\r\n"
    if REFRESH_DELAY != 0:
      try:
        if form.getvalue("cmd"):
          head_tpl_vars["refresh"] = ""
        else:
          head_tpl_vars["refresh"] = '\n'
      except:
        head_tpl_vars["refresh"] = ""
    print head_tpl % head_tpl_vars
    if form.getvalue("cmd") == "0": remove_commands()
    if form.getvalue("cmd") == "42": burn_kitchen()
    if form.getvalue("cmd") == "1": update_cp()
    if form.getvalue("cmd") == "2": update_rbytes()
    if form.getvalue("cmd") == "3": update_csize()
    display_list()
    print tailp_tpl % (ADMIN_PASS)
  else:
    display_main()

else: # cookie available
  data = save_info(cooked,data,cookie)
  reply_to_client(cooked,data)

