# Pantera - Web Pen-Test Proxy
#
# FILENAME      : pantera.py
# CODER         : Simon Roses Femerling
# DATE          : 9/23/2004
# LAST UPDATE   : 
# ABSTRACT      : Python Web Pen-Test Proxy :)
#                 Heavely base on SpikeProxy.
#
# - Roses Labs Innovations (RL+I)
# Roses Labs
# http://www.roseslabs.com
#
# Copyright (c) 2003-2006 Roses Labs.
#
# You may not distribute, transmit, repost this software for commercial 
# purposes without Roses Labs written permission. 
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, publish,
# distribute the Software, and to permit persons to whom the Software 
# is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#

'''
@author:       Simon Roses Femerling
@license:      GNU General Public License 2.0 or later
@contact:      pantera.proxy@gmail.com
@organization: OWASP / Roses Labs
'''

# Pantera Imports

import socket
import sys
from threading import Thread
import string
import os
from OpenSSL import SSL
import getopt
import base64

sys.path.append('ntlm')
from ntlmutils import str2unicode
import ntlm_messages
import ntlm_procs

import panteraLib
import panteraUI 
import panterautils
import time
import re

__version__ = "0.1.1"
__author__ = "Simon Roses Femerling"
__mail__ = "pantera.proxy@gmail.com"

#############################################################################################
# Defines
#############################################################################################

YES = 1
NO  = 0
    
HTTP_PORT  = 80
HTTPS_PORT = 443

TCP = 1
UDP = 2

denied1="<html><head><title>Error</title></head><body>You are not allowed to visit that page during this test, sorry. Try unsetting your proxy temporarily.</body></html>"
deniedstring="HTTP/1.1 404 Access Denied !\r\nContent-Length: %d"%len(denied1)+"\r\n\r\n"+denied1

#############################################################################################
# Our Functions
#############################################################################################

###############################################
#
# MyConnection class
#
###############################################

#############################################################################################
# FUNC     : class MyConnection
# PARAMS   : conn
# RETURN   : ...
# ABSTRACT : MyConnection class. Ripped from SpikeProxy.
class MyConnection:
    '''
    
    '''

    
    #############################################################################################
    def __init__(self,conn):
        '''
    
        '''
        self.doSSL=0
        self.mysocket=conn

    #############################################################################################
    def recv(self,size):
        '''
    
        '''
        #if self.doSSL:
        #    print "Reciving data as ssl!"
        #print "Recieving %d bytes" % size
        #if self.doSSL:
        #    print "Reading since we are SSL"
        #    return self.mysocket.read(size)
        result=self.mysocket.recv(size)
        #print "Returned from recv()"
        return result

    #############################################################################################
    #reliable send over socket
    def send(self,data):
        '''
    
        '''
        sizetosend=len(data)
        sentsize=0
        while sentsize<sizetosend:
            #print "sentsize="+str(sentsize)+"/"+str(sizetosend)
            try:
                #IF YOU ARE GETTING AN ERROR HERE, USE PYTHON VERSION 2.2!
                #FOR REDHAT 7.3 USERS, IT IS PROBABLY CALLED /usr/bin/python2 !
                sentsize+=self.mysocket.send(data[sentsize:])
            except (SSL.SysCallError,socket.error):
                #pass (this will cause it to loop forever, sucking CPU like a donkey)
                return sentsize
        return sentsize

    #############################################################################################
    #really never gets used
    def verify_cb(conn, cert, errnum, depth, ok):
        '''
    
        '''
        # This obviously has to be updated
        print 'Got certificate: %s' % cert.get_subject()
        return ok

    #############################################################################################
    #DOES get used. Needs pyOpenSSL 5.0 Pre or >
    def startSSLserver(self):
        '''
    
        '''
        debug_ssl=0
        
        dir = os.path.dirname(sys.argv[0])
        if dir == '':
            dir = os.curdir

        self.mysocket.send("HTTP/1.1 200 Connection established\r\n\r\n")
        ctx = SSL.Context(SSL.SSLv23_METHOD)
        ctx.set_timeout(5)
        ctx.set_verify(SSL.VERIFY_NONE, self.verify_cb) # Don't demand a certificate
        try:
            ctx.use_privatekey_file (os.path.join(dir, 'server.pkey'))
        except:
            print "Couldn't find file %s"%(os.path.join(dir, 'server.pkey'))
            
        ctx.use_certificate_file(os.path.join(dir, 'server.cert'))
        ctx.load_verify_locations(os.path.join(dir, 'CA.cert'))
        #normally would be SSL.connection, but we want to be threadsafe
        self.mysocket = SSL.Connection(ctx, self.mysocket)
        
        #only works with pyOpenSSL 5.0pre or >
        self.mysocket.set_accept_state()
        if debug_ssl:
            print "State="+self.mysocket.state_string()
        #done automatically
        #self.mysocket.do_handshake()
        self.doSSL=1
        if debug_ssl:
            print "Now using SSL to talk to client"
        
    #############################################################################################
    #wraps socket.close
    def close(self):
        '''
    
        '''
        #print "calling connection.close"
        self.mysocket.close()
        return

    #############################################################################################
    #we read 0 and on any exception return 1
    def gotclosed(self):
        '''
    
        '''
        #print "Checking if we got closed"
        try:
            data=self.mysocket.send("")
        except:
            #print "CAUGHT EXCEPTION CHECKING IF WE WERE CLOSED"
            return 1
        return 0

###############################################
#
# HTTPHeader class 
#
###############################################

#############################################################################################
# FUNC     : class HTTPHeader
# PARAMS   : ...
# RETURN   : ...
# ABSTRACT : HTTPHeader class. Ripped from SpikeProxy.
class HTTPHeader:
    '''
    
    '''
    
    #############################################################################################
    def __init__(self):
        '''
    
        '''
        self.clear()

    #############################################################################################
    #clears out the data structure - used for init
    def clear(self):
        '''
    
        '''
        self.data=[]
        self.done=0
        self.goodHeader=0
        self.clientisSSL=0
        self.verb=""
        #for the first request, we see a CONNECT verb
        self.sawCONNECT=0
        self.firstline="" #sheesh
        #1 if we are reading a response instead of a GET/POST, etc
        self.responseHeader=0
        self.wasChunked=0

        #here is basically what we return from parsing the headers
        self.URLargsDict={}
        self.headerValuesDict={}
        self.useSSL=0
        self.connectHost=""
        self.URL=""
        self.sawsslinit=0
        self.connectPort=0
        self.mybodysize=0
        self.useRawArguments=0
        self.allURLargs=""
        self.version=""
        #set this to not send a content-length
        self.doSurpressContentLength=0
        
        #variables for server response headers
        self.returncode=""
        self.returnmessage=""
        self.proxyHeader=""
        self.orderlist=[]
        self.replace_user_agent = "yes"
        self.replacer_list = []
        self.replace_mode = 0
        return

    #############################################################################################
    def SetReplaceData(self, u):
        '''
    
        '''
        self.replacer_list = u

    def SetReplaceMode(self, m):
        '''
    
        '''
        self.replace_mode = m

    def DoReplace(self, data):
        '''
    
        '''
        if self.replace_mode == 0:
            for i in self.replacer_list:
                if i['request'] == YES:
                    p = re.compile(i['regex'],re.IGNORECASE | re.DOTALL | re.MULTILINE)
                    data = p.sub(i['repla_str'], data)
                else:
                    continue
        if self.replace_mode == 1:
            for i in self.replacer_list:
                if i['reply_header'] == YES:
                    p = re.compile(i['regex'],re.IGNORECASE | re.DOTALL | re.MULTILINE)
                    data = p.sub(i['repla_str'], data)
                else:
                    continue
                
        return data
        
    #############################################################################################
    def SetReplaceUserAgent(self, ua):
        '''
    
        '''
        self.replace_user_agent = ua

    #############################################################################################
    def getProxyHeader(self):
        '''
    
        '''
        return self.proxyHeader
    
    #############################################################################################
    #fixes the URL to not have a ? in it if it happens to
    def normalize(self):
        '''
    
        '''
        if self.URL.count("?")>0 and self.URLargsDict=={} and self.useRawArguments==0:
            urlbit=self.URL[:]
            #if we have a url as well
            self.URL=urlbit.split("?")[0]
            #if we have arguments too
            if len(urlbit.split("?"))>1:
                self.allURLargs="?".join(urlbit.split("?")[1:])
                #print "SELF.allURLARGS=%s"%self.allURLargs
                #print "SELF.URL=%s"%self.URL
                self.URLargsDict=panterautils.splitargs(self.allURLargs,orderlist=self.orderlist)
                if self.URLargsDict==None:
                    self.URLargsDict={}
                    self.useRawArguments=1
                return
            else:
                self.URL+="?"
        return

    #############################################################################################
    #returns a site tuple (used for VulnXML)
    def getSiteTuple(self):
        '''
    
        '''
        result=(self.connectHost,self.connectPort,self.clientisSSL)
        return result

    #############################################################################################
    #sets us up from a site tuple
    def setSiteTuple(self,site):
        '''
    
        '''
        self.connectHost=site[0]
        self.connectPort=site[1]
        self.clientisSSL=site[2]
        return
        
    #############################################################################################
    #debug routine 
    def printme(self):
        '''
    
        '''
        #print "All my stuff:"
        result=""
        result+= "Host: "+self.connectHost + "\n"
        result+= "Port: "+str(self.connectPort) + "\n"
        result+= "SSL : "
        if self.clientisSSL:
            result+="Yes"
        else:
            result+="No"
        result+="\n\n"
        result+=self.verb
        for key in self.headerValuesDict.keys():
            for value in self.headerValuesDict[key]:
                result+=key+": "+value+"\n"
        return result

    #############################################################################################
    # reconstruct header
    def RecontructHeader(self,myheader):
        '''
    
        '''
        result = ""
        result += "%s " % myheader.verb 
        if myheader.clientisSSL:
            result += "https://"
        else:
            result += "http://"
        result += "%s%s %s" % (myheader.connectHost,myheader.URL,myheader.version)
        result += "\r\n"
        for key in myheader.headerValuesDict.keys():
            for value in myheader.headerValuesDict[key]:
                result+=key+": "+value+"\n"
        return result

    #############################################################################################
    def SetRewrite(self):
        '''
    
        '''
        #st = "http://pantera/rewrite?REALURL="
        #self.URL = "%s%s" % (st,self.URL)
        self.URLargsDict['PANTERA_TRIGGER'] = "rewrite"

    #############################################################################################
    #returns http://www.cnn.com from our header information
    def getSite(self):
        '''
    
        '''
        result=""
        if self.useSSL:
            result+="https://"
        else:
            result+="http://"
        result+=self.connectHost
        if self.connectPort!=80 and self.connectPort!=443:
            result+=":"+str(self.connectPort)
        return result
        
        
    #############################################################################################
    #returns 1 if 2 headers (self and other) are basically the same
    def issame(self,other):
        '''
    
        '''
        #we don't compare the header itself. That makes us
        #get false negatives with Date: headers and such
        #self.headerValuesDict==other.headerValuesDict and \\
        if cmp(self.URL,other.URL)==0 and \
           self.clientisSSL==other.clientisSSL and \
           self.firstline==other.firstline and \
           cmp(self.URLargsDict,other.URLargsDict)==0 and \
           self.connectPort==other.connectPort and \
           self.mybodysize==other.mybodysize and \
           panterautils.headerdictcmp(self.headerValuesDict,other.headerValuesDict) and \
           self.allURLargs==other.allURLargs:
            return 1
        return 0

    #############################################################################################
    #returns a string that is a "hash"
    def genhash(self):
        '''
    
        '''
        hash=""
        hash+=self.verb+self.returncode
        hash+=panterautils.hashstring(self.URL+self.allURLargs)
        #hash the cookies 
        if self.headerValuesDict.has_key("Cookie"):
            for key in self.headerValuesDict["Cookie"]:
                hash+=panterautils.hashstring(key)

        if self.headerValuesDict.has_key("Set-Cookie"):
            for key in self.headerValuesDict["Set-Cookie"]:
                hash+=panterautils.hashstring(key)

        #done!
        #return it encoded so we get rid of slashes
        return panterautils.strencode(hash,"A")

    #############################################################################################
    def setSurpressContentLength(self):
        '''
    
        '''
        self.doSurpressContentLength=1
        return

    #############################################################################################
    def surpressContentLength(self):
        '''
    
        '''
        return self.doSurpressContentLength
    
    #############################################################################################
    def setclientSSL(self):
        '''
    
        '''
        self.useSSL=1
        self.clientisSSL=1
        return
        
    #############################################################################################
    def addData(self,moredata):
        '''
    
        '''
        #print "addData "+moredata

        self.data.append(moredata)
        #print self.data[-4:]
        if self.data[-4:]==['\r', '\n', '\r', '\n']:
            #print "Got end of header!"
            self.done=1
            #print "All data="+"".join(self.data)
            self.verifyHeader()
        #we shouldn't NEED this, but economist.com has a misbehaving
        #IIS 5.0 server which does this!!!
        if self.data[-2:]==['\n','\n']:
            print "Weird \\n\\n in header!"
            self.done=1
            self.verifyHeader()
        return

    #############################################################################################
    #keys is a set of values for which we're going to look and
    #return an integer associated with them from the headers
    #we return the first value in the header list as an int
    def getIntValue(self,keys):
        '''
    
        '''
        #iterate over all the keys in the argument until we have a match
        #print "all header keys: "+str(self.headerValuesDict.keys())

        for akey in keys:
            if self.headerValuesDict.has_key(akey):
                #print "Int key: "+akey+" matched "+self.headerValuesDict[akey][0]
                #we just return the first one we encounter, sorry
                #so multiple headers will just be on a first come
                #first serve basis
                return int(self.headerValuesDict[akey][0])
        return 0

    #############################################################################################
    #we return the first value in the header list as a string
    #KEYS IS A LIST, NOT A STRING!
    def getStrValue(self,keys):
        '''
    
        '''
        #print "all header keys: "+str(self.headerValuesDict.keys())
        for akey in keys:
            #print "str: "+akey
            if self.headerValuesDict.has_key(akey):
                return str(self.headerValuesDict[akey][0])
        return "0"

    #############################################################################################
    def removeHeaders(self,hstring):
        '''
    
        '''
        if self.headerValuesDict.has_key(hstring):
            del self.headerValuesDict[hstring]
    
    #############################################################################################
    def addHeader(self,newheader,newheadervalue):
        '''
    
        '''
        #print "Adding header "+newheader+": "+newheadervalue
        #now we store it, at last
        if not self.headerValuesDict.has_key(newheader):
            #intialize it as a list
            self.headerValuesDict[newheader]=[]
        else:
            #print "Duplicate KEY: "+newheader
            pass

        #just separating them by commas doesn't work for hotmail.com 
        self.headerValuesDict[newheader].append(newheadervalue)        

    #############################################################################################
    def verifyHeader(self):
        '''
    
        '''
        #this little ditty returns a list of lines, without \r\n's
        #the -2 is because there were 2 null \r\n thingies on the end
        t = "".join(self.data)
        data = self.DoReplace(t)
        data = data.split("\r\n")
        self.allheaders=data
        #print "Self.allheaders="+str(self.allheaders)
        firstline=self.allheaders[0]
        self.allheaders=self.allheaders[:-2]
        #this will fail if we can't parse the first line
        if not self.parseFirstLine(firstline):
            print "Couldn't parse first line!"
            return 0

        #did we see a CONNECT?
        if self.sawCONNECT:
            #print "Saw SSL CONNECT request!"
            self.sawsslinit=1
            return 1

        
        for headerLine in self.allheaders[1:]:
            #print "Doing header line: "+headerLine
            tempvalues=headerLine.split(": ")
            if len(tempvalues)<2:
                #MS hotmail login is lame - uses this header, notice no space:
                #P3P:CP="BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo"
                #so we handle that condition now
                tempvalues=headerLine.split(":")
                if len(tempvalues)<2:
                    print "len(tempvalues)!=2 ="+str(len(tempvalues))+" in "+str(tempvalues)
                    return 0

            self.addHeader(tempvalues[0],":".join(tempvalues[1:]))

        #print "About to call massageHeaders"
        self.massageHeaders()
        #print "Headers="+str(self.headerValuesDict)

        #print "Got a good header."
        self.goodHeader=1
        return

    #############################################################################################
    #this function takes in 
    def massageHeaders(self):
        '''
    
        '''

        #print "Inside massageHeaders"

        #non-IE user Agent, for reference
        #User-Agent: Mozilla/5.0 Galeon/1.0.3 (X11; Linux i686; U;) Gecko/0
        #IE string
        IEstring="Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; Bob)"
        nonIEstring="Mozilla/5.0 Galeon/1.0.3 (X11; Linux i686; U;) Gecko/0"
        #always massage chunked out of the way
        #this will cause problems if someone sends over a gig of data
        #I doubt that will happen though
        if self.getStrValue(["Transfer-Encoding"])=="chunked":
            del self.headerValuesDict["Transfer-Encoding"]
            self.wasChunked=1
        
        #massage a response differently from a non-response
        #uncomment this for dave's cludgy early morning NTLM pass-through
        ## if self.responseHeader:
##             #print "Did not see connection"
##             #print "Auth: "+self.getStrValue(["WWW-Authenticate"])
##             authenticate=self.getStrValue(["WWW-Authenticate"])
##             if authenticate.count("NTLM")>0 or authenticate.count("Negotiate")>0:
##                 print "Doing band-aide for NTLM"
##                 self.addHeader("Proxy-Authenticate",authenticate)
##                 del self.headerValuesDict["WWW-Authenticate"]

##                 #must replace return code for some reason as well
##                 #see http://squid.sourceforge.net/ntlm/client_proxy_protocol.html
##                 self.firstline=self.firstline.replace("401","407")
                
##             return 

##         #here we replace Proxy-Authentication with Authentication for NTLM
##         if self.headerValuesDict.has_key("Proxy-Authorization"):
##             self.headerValuesDict["Authorization"]=self.headerValuesDict["Proxy-Authorization"][:]
##             del self.headerValuesDict["Proxy-Authorization"]


        #by default, use IE 5.0
        replaceUserAgent=1
        userAgent = panteraLib.PanteraDataDict["user_agent"]


            

        #change Proxy-Connection to Connection
        if self.headerValuesDict.has_key("Proxy-Connection"):
            #DEBUG
            #print "MassageHeaders: has key proxy-connection"
            self.headerValuesDict["Connection"]=self.headerValuesDict["Proxy-Connection"][:]
            #print "Connection is now: "+str(self.headerValuesDict["Connection"])
            del self.headerValuesDict["Proxy-Connection"]

        #replace the User-Agent
        #if replaceUserAgent:
        if self.replace_user_agent == 'yes':
            #just overwrite the damn thing
            if self.headerValuesDict.has_key("User-Agent"):
                del self.headerValuesDict["User-Agent"]
            #comment out the next line for NO user agent
                self.addHeader("User-Agent",userAgent)
            pass

        #save this off before we delete it
        self.mybodysize=self.getIntValue(["Content-length","Content-Length"])
        #get rid of Content-Length or Content-length - this is
        #a requirement since we recalcuate it later for fun!
        if self.headerValuesDict.has_key("Content-length"):
            del  self.headerValuesDict["Content-length"]
        if self.headerValuesDict.has_key("Content-Length"):
            del  self.headerValuesDict["Content-Length"]

        #no return value for massageHeaders
        return


    #############################################################################################
    def parseFirstLine(self,firstline):
        '''
    
        '''
        #print "firstline="+firstline
        templist=firstline.split(" ")
        if len(templist)<2:
            print "First line of header has less than 2 members!"
            return 0
        self.verb=templist[0]

        if self.verb in [ "HTTP/1.1", "HTTP/1.0" ]:
            #print "Response header - not verifying the first line of %s!" % (firstline)
            self.responseHeader=1
            if len(templist)>1:
                self.returncode=templist[1]
            if len(templist)>2:
                self.returnmessage=templist[2]
            self.firstline=firstline
            return 1
        
        #TODO: remove this code from the header class out into the spkProxy class
        #this is the only place we use self.connection!
        #SSL proxy check
        if self.verb=="CONNECT":
            #WE ARE SSL!
            #signifies we connect to server with ssl
            self.useSSL=1
            #signifies we connect to client with ssl
            self.clientisSSL=1
            self.sawCONNECT=1
            self.connectHost=templist[1].split(":")[0]

            #no port would be weird, but maybe it'll happen...
            if templist[1].split(":") < 2:
                self.connectPort=443
            else:
                self.connectPort=templist[1].split(":")[1]
            return 1
        
        if not self.processProxyUrl(templist[1]):
            return 0

        #HTTP/1.1 or HTTP/1.0
        self.version=templist[2]
        #print "VERB="+self.verb+" URL="+self.URL+" version="+self.version
        return 1
        
    #############################################################################################
    def processProxyUrl(self, proxyurl):
        '''
    
        '''

        #here is basically what we return
        self.URLargsDict={}
        self.useSSL=0
        self.connectHost=""
        #this might already be set if we got an SSL proxy request
        if not self.connectPort:
            self.connectPort=80
        self.URL=""

        #print "processProxyUrl: "+proxyurl
        #just in case we ARE doing ssl...
        urlbit=proxyurl
        #if we're not doing an SSL proxy
        if not self.clientisSSL:
            #print "proxyURL is not SSL"
            #rip the http:// off
            urltype=proxyurl.split("://")[0]
            if len(proxyurl.split("://")) < 2:
                print "Need something after the http:// - exiting this thread"
                return 0
            #else we are good to go...we reassign urlbit here
            #need to do join because of multiple :// in arguments and stuff
            #should fix bbc news error
            urlbit="://".join(proxyurl.split("://")[1:])
            if urltype=="https":
                #this is probably broken: REVISIT
                self.useSSL=1
            elif urltype!="http":
                print "unknown url type "+urltype
                return 0

            #must have http://something
            if len(proxyurl.split("://"))<2:
                print "must have http://something"
                return 0
            
            self.connectHost=urlbit.split("/")[0]

            #get rid of the host from urlbit
            if len(urlbit.split("/"))<2:
                urlbit="/"
            else:
                urlbit="/".join(urlbit.split("/")[1:])

            if urlbit=="":
                urlbit="/"

            #lame, but should work
            if urlbit[0]!="/":
                urlbit="/"+urlbit

                
            #print "connectHost="+self.connectHost
            if len(self.connectHost.split(":"))>1:
                #print "ConnectHost Split: "+str(self.connectHost.split(":"))
                self.connectPort=int(self.connectHost.split(":")[1])
                self.connectHost=self.connectHost.split(":")[0]
                #print "Set self.connectHost to "+self.connectHost

            if self.connectHost=="":
                print "Error: empty connect host!"
                return 0
            
        #end if self.clientisSSL==0:

        #TODO: Fix this to work on blah.ng/asdf=asdf&asdf2=asdf2
        #this should work, but there's no way for me, as the client
        #to really know
        if urlbit.count("?")==0 and urlbit.count("=")>0:
            indexequal=urlbit.find("=")
            if indexequal!=-1:
                indexfirstslash=urlbit.rfind("/",0,indexequal)
                if indexfirstslash!=-1:
                    #print "original = "+urlbit
                    #print "indexequal="+str(indexequal)
                    #print "indexfirstslash="+str(indexfirstslash)
                    urlbit=urlbit[:indexfirstslash]+"?"+urlbit[indexfirstslash+1:]
                    #print "new="+urlbit

        #if we have a url as well
        self.URL=urlbit.split("?")[0]
        #if we have arguments too
        if len(urlbit.split("?"))>1:
            self.allURLargs="?".join(urlbit.split("?")[1:])
            #print "SELF.allURLARGS=%s"%self.allURLargs
            #print "SELF.URL=%s"%self.URL
            self.URLargsDict=panterautils.splitargs(self.allURLargs,orderlist=self.orderlist)
            if self.URLargsDict==None:
                self.URLargsDict={}
                self.useRawArguments=1
                return 1

    
            
        #got here! success!
        #we now have URLargsDict
        return 1
        
    #############################################################################################
    def isdone(self):
        '''
    
        '''
        #print "self.isdone called "+str(self.done)
        if self.done==0:
            return 0
        return 1

    #############################################################################################
    def gotGoodHeader(self):
        '''
    
        '''
        return self.goodHeader

    #############################################################################################
    def bodySize(self):
        '''
    
        '''
        return self.mybodysize

    #############################################################################################
    def grabHeader(self,header):
        '''
    
        '''
        if self.headerValuesDict.has_key(header):
            returnstr=""
            #iterate over the list and add a line for each
            for value in self.headerValuesDict[header]:
                returnstr+=header+": "+value+"\r\n"
            return returnstr
        else:
            return ""

    #############################################################################################
    def igrabHeader(self,header):
        '''
    
        '''
        if self.headerValuesDict.has_key(header):
            returnstr=""
            #iterate over the list and add a line for each
            for value in self.headerValuesDict[header]:
                returnstr+=header+": "+value
            return returnstr
        else:
            return ""

    #############################################################################################
    def setProxyHeader(self,newheader):
        '''
    
        '''
        self.proxyHeader=newheader
        return

    def SetURL(self, url):
        '''
    
        '''
        self.URL = url
        
    def DelURL(self):
        '''
    
        '''
        del self.URL
        self.URL = ""

###############################################
#
# HTTPBody class 
#
###############################################

#############################################################################################
# FUNC     : class HTTPBody
# PARAMS   : ...
# RETURN   : ...
# ABSTRACT : HTTPBody class. Ripped from SpikeProxy.
class HTTPBody:
    '''
    
    '''
    
    #############################################################################################
    def __init__(self):
        '''
    
        '''
        self.mysize=0
        self.data=[]
        #self.data=""
        self.replacer_list = []
        self.replace_mode = 0
        
    #############################################################################################
    def SetReplaceData(self, u):
        '''
    
        '''
        self.replacer_list = u

    def DoReplace(self):
        '''
    
        '''
        dat = "".join(self.data)
        for i in self.replacer_list:
            if i['reply_body'] == YES:
                    p = re.compile(i['regex'],re.IGNORECASE | re.DOTALL | re.MULTILINE)
                    dat = p.sub(i['repla_str'], dat)
            else:
                continue
        self.data = [dat]        
        self.mysize = len(self.data)
                
    #############################################################################################
    def printme(self):
        '''
    
        '''
        result= "".join(self.data)
        result=panterautils.prettyprint(result)
        return result

    #############################################################################################
    def setSize(self,size):
        '''
    
        '''
        self.mysize=size
        return
    
    #############################################################################################
    #just compare sizes for speed.
    def issame(self,other):
        '''
    
        '''
        #and self.data==other.data:
        if self.mysize==other.mysize :
            return 1
        return 0

    #############################################################################################
    def genhash(self):
        '''
    
        '''
        hash=""
        hash+=panterautils.hashstring("".join(self.data))
        return hash
    
    #############################################################################################
    def getArgsDict(self):
        '''
    
        '''
        argsDict=panterautils.splitargs("".join(self.data))
        if argsDict==None:
            argsDict={}
        return argsDict
        
    #############################################################################################
    def readBlock(self,connection,size):
        '''
    
        '''
        targetsize=size
        tempdata=""
        while targetsize > len(tempdata):
            #read some data
            tempdata+=connection.recv(targetsize-len(tempdata))
            #print tempdata
            #print "Targetsize=%d, len(tempdata)=%d" % (targetsize,len(tempdata))
        #print "read "+str(len(tempdata))+" bytes of data in readblock, wanted "+str(size)
        self.data+=tempdata
        self.mysize+=targetsize
        #print "READ 3"
        #print self.data
        return size

    #############################################################################################
    #This handles chunked data cleanly - well, handles it anyways
    #this is the cruftiest function ever made.
    def read(self,connection,size,waschunked,readtillclosed):
        '''
    
        '''
        if not waschunked:
            if readtillclosed and size==0:
                #print "reading till closed"
                temp=""
                while 1:
                    #this is a lame way to do it, but hopefully it will work
                    try:
                        length=len(temp)
                        #print "len="+str(length)
                        temp+=connection.recv(1000)
                        #print "len2="+str(len(temp))
                        #WAY crufty here...
                        if (length==len(temp)):
                            break
                        if temp.count("</html>")>0:
                            #this is necessary because stupid hotmail will
                            #not send a fin after sending lots of data
                            #with connection: close!
                            #print "Noticed a </html> - breaking out of this"
                            #time.sleep(4)
                            #break
                            pass
                    except (SSL.SysCallError,socket.error), diag:
                        #print "Caught exception in recv - "+str(diag)
                        break
                    except:
                        #some sort of exception sending or recieving data
                        #print "Unknown exception occured"
                        break
                     
                #print "Read till close occured - "+str(len(temp))+" bytes read"
                self.data+=temp
                self.mysize+=len(temp)

                #print "READ 1"
                #print self.data
                return len(temp)
            else:
                return self.readBlock(connection,size)
        
        else:
            #print "Reading chunked data"
            while 1:
                #read in a chunked data stream and return the size
                linesize=[]
                while linesize[-2:]!=["\r","\n"]:
                    linesize+=connection.recv(1)
                #ok, now we have the size as a list, transform that to an int
                #base 16, of course
                #print "linesize in str = "+"".join(linesize)
                linesize=int("".join(linesize),16)
                #print "linesize="+str(linesize)
                if linesize==0:
                    #print "done with chunked transfer!"
                    #clear this out
                    linesize=[]
                    while linesize[-2:]!=["\r","\n"]:
                        linesize+=connection.recv(1)
                    return self.mysize
                #print "calling self.readBlock with size "+str(linesize)
                self.readBlock(connection,linesize)
                #clear this out
                linesize=[]
                #print "READ 2"
                #print self.data
                while linesize[-2:]!=["\r","\n"]:
                    linesize+=connection.recv(1)

    #############################################################################################
    def gotGoodBody(self):
        '''
    
        '''
        if self.mysize==len(self.data):
            return 1
        else:
            return 0

###############################################
#
# PanteraProxyConnection class 
#
###############################################

#############################################################################################
# FUNC     : class PanteraProxyConnection
# PARAMS   : ...
# RETURN   : ...
# ABSTRACT : PanteraProxyConnection class. Ripped from SpikeProxy.
class PanteraProxyConnection( Thread ):
    '''
    
    '''

    #############################################################################################
    def __init__(self,connection,myUI,proxy=None,ntlm=None):
        '''
    
        '''
        Thread.__init__(self)
        #client connection
        self.connection=connection
        self.clientisSSL=0
        self.currentHost=""
        self.currentPort=0
        self.haveSocket=0
        self.sslHost=""
        self.sslPort=""
        #serversion connection
        self.currentSocket=-1
        self.sawConnectionClose=0
        #new user interface
        self.myUI=myUI
        self.proxyHeader=""
        self.proxyHost=""
        self.proxyPort=0
        self.proxySSLHost=""
        self.proxySSLPort=0
        self.proxyUser=""
        self.proxyDomain=""
        self.proxyPassword=""
        self.NTLMUser=""
        self.NTLMDomain=""
        self.NTLMPassword=""
        self.NTLMAuthState=""
        
        if proxy!=None:
            (self.proxyHost,self.proxyPort,self.proxySSLHost,self.proxySSLPort, self.proxyUser, self.proxyPassword, self.proxyDomain)=proxy

        if ntlm!=None:
            (self.NTLMUser,self.NTLMPassword,self.NTLMDomain)=ntlm
            #print "Initializing NTLM: %s@%s with password %s"%(self.NTLMUser,self.NTLMDomain,self.NTLMPassword)

        self.my404List= self.myUI.getDefault404()
            
        return
    
    #############################################################################################
    def setProxyHeader(self,myheader):
        '''
    
        '''
        #print "spkProxyConnection: Proxy header set to %s"%myheader
        self.proxyHeader=myheader
        return
        
    #############################################################################################
    def run( self ):
        '''
    
        '''

        if panteraLib.PanteraDataDict["debug"] == "yes":
            print "[P_debug]> Handling new connection"
            
        while 1:
            if panteraLib.PanteraDataDict["debug"] == "yes":
                print "[P_debug]> entering while loop"
            myheader = HTTPHeader()
            myheader.SetReplaceMode(0)
            myheader.SetReplaceData(self.myUI.ReturnReplacerList())
            #myheader.setConnection(self.connection)
            myheader.SetReplaceUserAgent(panteraLib.PanteraDataDict['replace_user_agent'])
            if self.clientisSSL:
                myheader.setclientSSL()

            while myheader.isdone()==0:
                try:
                    data=self.connection.recv(1)
                except:
                    if panteraLib.PanteraDataDict["verbose"] == "yes":
                        print "[P]> Client closed connection"
                    self.cleanup()
                    return
                    
                if not data:
                    if panteraLib.PanteraDataDict["debug"] == "yes":
                        print "[P_debug]> End of data"
                    break
                #dat = self.myUI.DoReplacer(data)
                myheader.addData(data)

            if myheader.sawsslinit==1:
                if panteraLib.PanteraDataDict["debug"] == "yes":
                    print "[P_debug]> SSL init!"
                self.clientisSSL=1
                self.sslHost=myheader.connectHost
                self.sslPort=myheader.connectPort
                self.connection.startSSLserver()
                continue

            #print myheader.data
            #print myheader.headerValuesDict

            #print "Continuing on with while loop!"
            mybody = HTTPBody()
            if panteraLib.PanteraDataDict["debug"] == "yes":
                print "[P_debug]> Done with header"
            #read the body from the client now
            if myheader.gotGoodHeader():
                if panteraLib.PanteraDataDict["debug"] == "yes":
                    print "[P_debug]> Reading body"
                #print myheader.bodySize()
                #print myheader.wasChunked
                if myheader.bodySize()>0 or myheader.wasChunked:
                    if panteraLib.PanteraDataDict["debug"] == "yes":
                        print "[P_debug]> Reading the body!"
                    #readtillclosed always 0 on client
                    mybody.read(self.connection,myheader.bodySize(),myheader.wasChunked,0)
                else:
                    if panteraLib.PanteraDataDict["debug"] == "yes":
                        print "[P_debug]> No body needed"
                    pass

                #reset this to the truth
                myheader.mybodysize=mybody.mysize
                    
                if not mybody.gotGoodBody():
                    self.cleanup()
                    return
                if panteraLib.PanteraDataDict["debug"] == "yes":
                    print "[P_debug]> Done with body"
            else:
                if panteraLib.PanteraDataDict["debug"] == "yes":
                    print "[P_debug]> Failed to get a good header, cleaning up."
                    print "[P_debug]> Header we got: %s"%(str(myheader.data))
                self.cleanup()
                return
            #done with the body. So now we have a header and a body
            #print "header data="+str(myheader.data)
            #print "header data=" +"".join(myheader.data)
            #print "body data="+str(mybody.data)

            response = self.sendRequest(myheader,mybody)

            #print "Response : "+response
            sizetosend=len(response)
            sentsize=0

            sentsize+=self.connection.send(response[sentsize:])
            
            if panteraLib.PanteraDataDict["debug"] == "yes":
                print "[P_debug]> Sent data to client."
                #print "Header we sent: %s"%response.split("\r\n\r\n")[0]

            if self.connection.gotclosed():
                if panteraLib.PanteraDataDict["debug"] == "yes":
                    print "[P_debug]> self.connection.gotclosed() = true"
                self.sawConnectionClose=1

            if self.sawConnectionClose:
                if panteraLib.PanteraDataDict["debug"] == "yes":
                    print "[P_debug]> Closing connection!"
                self.connection.close()
                self.cleanup()
                return

            if panteraLib.PanteraDataDict["debug"] == "yes":
                print "[P_debug]> Continuing while loop"

            continue #while loop

    #############################################################################################
    #creates a string with the total reponse in it
    def constructResponse(self,myheader,mybody):
        '''
    
        '''
        return panterautils.constructResponse(myheader,mybody)

    #############################################################################################
    #connects to a remote proxy hopefully.
    #1 on success, 0 on failure
    def doProxyConnect(self,host,port):
        '''
    
        '''

        newsocket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        if self.clientisSSL:
            print "Connecting to SSL Proxy %s %s"%(self.proxySSLHost,self.proxySSLPort)
            newsocket.connect((self.proxySSLHost,int(self.proxyPort)))
            newsocket.send("CONNECT %s:%s\r\n\r\n"%(host,port))
            #get the response
            #print "Reading in until I get \\r\\n\\r\\n"
            data=panterautils.readuntil(newsocket,"\r\n\r\n")
            #print "Done reading in"
            if data.count("200")==0:
                print "Failed to connect to SSL Server via Proxy"
                return 0
            #SSL doesn't use proxy headers - just raw urls
            self.setProxyHeader("")

            
        else:
            if panteraLib.PanteraDataDict["verbose"] == "yes":
                print "[P]> Connecting to Proxy %s %s"%(self.proxyHost,self.proxyPort)
            newsocket.connect((self.proxyHost,int(self.proxyPort)))
            if newsocket==None:
                print "Couldn't connect to proxy host!"
                return 0
            #we don't print the port if it's port 80
            if str(port)!="80":
                self.setProxyHeader("http://"+host+":"+str(port))
            else:
                self.setProxyHeader("http://"+host)
        

        self.currentSocket=newsocket
        #success connecting to proxy
        return 1
        

#####FIXME
        

        
    #############################################################################################
    def closeServerSocket(self):
        '''
    
        '''
        if self.haveSocket:
            self.currentSocket.close()
        self.currentPort=0
        self.currentHost=""
        
        return
                          
    #############################################################################################
    #TODO: This needs to be modified to handle NTLM authentication!
    def connectToWebServer(self,myheader):
        '''
    
        '''
        #DEBUGSET
        debug_connectToWebServer=0

        if self.clientisSSL:
            #do we already have a socket connected to the web server?
            #we need to set these just for the record
            myheader.connectHost=self.sslHost
            myheader.connectPort=self.sslPort
            #now we do some actual work
            if not self.haveSocket:
                self.haveSocket=1
                self.currentHost=self.sslHost
                self.currentPort=self.sslPort
                self.currentSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                if panteraLib.PanteraDataDict["debug"] == "yes":
                    print "[P_debug]> Connecting to "+str(self.sslHost)+" "+str(self.sslPort)

                if self.proxyHost!="":
                    #DO PROXY CONNECT HERE
                    if self.doProxyConnect(myheader.connectHost,myheader.connectPort)!=1:
                        return 0
                else:
                    try:
                        self.currentSocket.connect((self.sslHost,int(self.sslPort)))
                    except:
                        print "Connection refused"
                        self.currentHost=""
                        self.currentPort=0
                        return 0
                    
                #HANDLE SSL HERE DO DO DO
                ctx = SSL.Context(SSL.SSLv23_METHOD)
                ctx.set_timeout(5)
                self.currentSocket = SSL.Connection(ctx,self.currentSocket)

                self.currentSocket.set_connect_state()
                #print "Set up SSL"
            else:
                if debug_connectToWebServer:
                    print "SSL connection not done since we already had a connection."
                
        else:
            #not SSL
            #do we already have a socket connected to the host
            if panteraLib.PanteraDataDict["debug"] == "yes":
                    print "[P_debug]> Connecting to "+str(myheader.connectHost)+" "+str(myheader.connectPort)
            if debug_connectToWebServer:
                print "currentSocket=%s" % str(self.currentSocket)
            #this -1 compare is because sometimes the socket gets closed on us and we don't find out about it, so
            #we check to make sure - currentSocket!=<socket object, fd=-1, family=2, type=1, protocol=0>
            if self.currentHost==myheader.connectHost and self.currentPort==myheader.connectPort and str(self.currentSocket).count("-1")==0:
                if debug_connectToWebServer:
                    print "passing because currentHost and currentPort are the same"
                #nothing really
                pass
            else:
                #handle the condition where we have a socket, but it is the wrong host...
                if self.haveSocket:
                    self.currentSocket.close()

                #if we don't have a socket, or we had the wrong socket, we now need a socket
                #TODO: add error checking...

                self.currentSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

                if self.proxyHost!="":
                    if self.doProxyConnect(myheader.connectHost,myheader.connectPort)!=1:
                        return 0
                    
                else:
                    if panteraLib.PanteraDataDict["debug"] == "yes":
                        print "[P_debug]> Trying to connect to %s:%s"%(myheader.connectHost,myheader.connectPort)
                    try:
                        self.currentSocket.connect((myheader.connectHost,int(myheader.connectPort)))
                    except:
                        self.currentHost=""
                        self.currentPort=0
                        print "Could not connect."
                        return 0
                        
                self.currentHost=myheader.connectHost
                self.currentPort=myheader.connectPort

        #return success!
        return 1

    #############################################################################################
    def massageResponse(self,serverheader,serverbody):
        '''
    
        '''
        """
        massageResponse() performs any additional changes on the response before we return it
        For example, this function handles false 200 responses that IIS misconfigurations do
        quite often
        """
        
        bodydata="".join(serverbody.data)
        #print bodydata
        #here we check for any misconfigured servers that return a 200 Success while
        #showing a lame "404 not found" page
        #404 string list is set up globally here
        for astring in self.my404List:
            if bodydata.count(astring):
                serverheader.returncode="404"
                
    #############################################################################################
    def getNTLMEnv(self):
        '''
    
        '''
        env={}
        env["LM"]=1
        env["NT"]=1
        env["UNICODE"]=1
        
        env["HOST"]=(self.currentHost.upper())
        env["DOMAIN"]=(self.NTLMDomain.upper())
        env["USER"]=(self.NTLMUser.upper())
        env["FLAGS"] = "06820000"
        env["NTLM_TO_BASIC"]=0
        env['LM_HASHED_PW'] = ntlm_procs.create_LM_hashed_password(self.NTLMPassword)
        env['NT_HASHED_PW'] = ntlm_procs.create_NT_hashed_password(self.NTLMPassword)
        #whatever this is
        env["NTLM_MODE"]=0

        return env

    #############################################################################################
    def getProxyNTLMEnv(self):
        '''
    
        '''
        env={}
        env["LM"]=1
        env["NT"]=1
        env["UNICODE"]=1
        
        env["HOST"]=(self.currentHost.upper())
        env["DOMAIN"]=(self.proxyDomain.upper())
        env["USER"]=(self.proxyUser.upper())
        env["FLAGS"] = "06820000"
        env["NTLM_TO_BASIC"]=0
        env['LM_HASHED_PW'] = ntlm_procs.create_LM_hashed_password(self.proxyPassword)
        env['NT_HASHED_PW'] = ntlm_procs.create_NT_hashed_password(self.proxyPassword)
        #whatever this is
        env["NTLM_MODE"]=0

        return env

    #############################################################################################
    def sendRequest(self,myheader,mybody):
        '''
    
        '''
        (response,serverheader,serverbody)=self.sendRequestRaw(myheader,mybody)
        #check to see if we have to do the NTLM stuff and if so
        #try to authenticate
        if serverheader!=None:
            #print "Auth: "+serverheader.getStrValue(["WWW-Authenticate"])
            #print "self.NTLMUser="+self.NTLMUser
            pass

        #
        # NTLM AUTH
        #
        if serverheader!=None and serverheader.getStrValue(["WWW-Authenticate"]) in ["NTLM","Negotiate"] and self.NTLMUser!="" and panteraLib.PanteraDataDict["force_auth"] == "yes":
            if panteraLib.PanteraDataDict["debug"] == "yes":
                print "Sending NTLM Authentication"
            #first, clean up our old socket...we're going to be reattempting
            #to open this socket and establish a connection
            #don't need this unless the socket is being kept open...
            #self.closeServerSocket()
            #send packet 1
            env=self.getNTLMEnv()
            ntlmstring1= ntlm_messages.create_message1(env)
            myheader.removeHeaders("Authorization")
            myheader.addHeader("Authorization","NTLM "+ntlmstring1)
            #must lock this to HTTP/1.0 for auth requests - why? Because
            #stupid IIS won't send Keep-Alive if it is 1.1. IE does this as well.
            myheader.version="HTTP/1.0"
            (response,serverheader,serverbody)=self.sendRequestRaw(myheader,mybody)
            ntlmchallenge=serverheader.getStrValue(["WWW-Authenticate"])
            #strip off the header, whatever it may be
            ntlmchallenge=ntlmchallenge.replace("NTLM ","")
            ntlmchallenge=ntlmchallenge.replace("NEGOTIATE ","")
            #create nonce
            nonce = ntlm_messages.parse_message2(ntlmchallenge)
            #print ntlm_messages.debug_message2(ntlmchallenge)
            #create new message
            NTLM_msg3 = ntlm_messages.create_message3(nonce, env)
            #print  ntlm_messages.debug_message3(NTLM_msg3)
            myheader.removeHeaders("Authorization")
            myheader.addHeader("Authorization","NTLM "+NTLM_msg3)
            #now read in from the new serverheader the new challenge
            #now construct a response
            #now send it, should get a 200 ok back!
            (response,serverheader,serverbody)=self.sendRequestRaw(myheader,mybody)
            #print "Sent last raw request"

        #
        # BASIC AUTH
        # 
        if serverheader!=None and serverheader.getStrValue(["WWW-Authenticate"]) in ["BASIC"] and panteraLib.PanteraDataDict["force_auth"] == "yes":
            if panteraLib.PanteraDataDict["debug"] == "yes":
                print "Sending BASIC Authentication"
            myheader.removeHeaders("Authorization")            
            base64string = base64.encodestring('%s:%s' % (panteraLib.PanteraDataDict["user"], panteraLib.PanteraDataDict["password"]))[:-1]
            myheader.addHeader("Authorization","BASIC "+base64string)
            myheader.version="HTTP/1.0"
            #now send it, should get a 200 ok back!
            (response,serverheader,serverbody)=self.sendRequestRaw(myheader,mybody)
        #
        # DIGEST AUTH (TODO)
        #
        if serverheader!=None and serverheader.getStrValue(["WWW-Authenticate"]) in ["DIGEST"]:
            pass

        #
        # Proxy Auth - NTLM
        # 
        if serverheader!=None and serverheader.getStrValue(["Proxy-Authenticate"]) in ["NTLM","Negotiate"] and self.proxyUser!="" and panteraLib.PanteraDataDict["force_proxy_auth"] == "yes":
            if panteraLib.PanteraDataDict["debug"] == "yes":
                print "Sending NTLM Authentication"
            #first, clean up our old socket...we're going to be reattempting
            #to open this socket and establish a connection
            #don't need this unless the socket is being kept open...
            #self.closeServerSocket()
            #send packet 1
            env=self.getProxyNTLMEnv()
            ntlmstring1= ntlm_messages.create_message1(env)
            myheader.removeHeaders("Proxy-Authorization")
            myheader.addHeader("Proxy-Authorization","NTLM "+ntlmstring1)
            #must lock this to HTTP/1.0 for auth requests - why? Because
            #stupid IIS won't send Keep-Alive if it is 1.1. IE does this as well.
            myheader.version="HTTP/1.0"
            #print myheader
            #print mybody
            #self.myUI.AddHashHeaderInterceptor(myheader)
            (response,serverheader,serverbody)=self.sendRequestRaw(myheader,mybody,1)
            #print serverheader
            ntlmchallenge=serverheader.getStrValue(["Proxy-Authenticate"])
            #strip off the header, whatever it may be
            ntlmchallenge=ntlmchallenge.replace("NTLM ","")
            ntlmchallenge=ntlmchallenge.replace("NEGOTIATE ","")
            #create nonce
            nonce = ntlm_messages.parse_message2(ntlmchallenge)
            #print ntlm_messages.debug_message2(ntlmchallenge)
            #create new message
            NTLM_msg3 = ntlm_messages.create_message3(nonce, env)
            #print  ntlm_messages.debug_message3(NTLM_msg3)
            myheader.removeHeaders("Proxy-Authorization")
            myheader.addHeader("Proxy-Authorization","NTLM "+NTLM_msg3)
            #now read in from the new serverheader the new challenge
            #now construct a response
            #now send it, should get a 200 ok back!
            (response,serverheader,serverbody)=self.sendRequestRaw(myheader,mybody,1)
            #print "Sent last raw request"
            if serverheader!=None and serverbody!=None:
                if myheader.URLargsDict.has_key('PANTERA_TRIGGER'):
                    del myheader.URLargsDict['PANTERA_TRIGGER']
                self.myUI.registerRequestandResponse(myheader,mybody,serverheader,serverbody)
            return response
        
        #
        # Proxy Auth - BASIC
        # 
        if serverheader!=None and serverheader.getStrValue(["Proxy-Authenticate"]) in ["BASIC"] and panteraLib.PanteraDataDict["force_proxy_auth"] == "yes":
            if panteraLib.PanteraDataDict["debug"] == "yes":
                print "Sending BASIC Proxy-Authentication"
            myheader.removeHeaders("Proxy-Authorization")            
            base64string = base64.encodestring('%s:%s' % (panteraLib.PanteraDataDict["user"], panteraLib.PanteraDataDict["password"]))[:-1]
            myheader.addHeader("Proxy-Authorization","BASIC "+base64string)
            myheader.version="HTTP/1.0"
            #now send it, should get a 200 ok back!
            (response,serverheader,serverbody)=self.sendRequestRaw(myheader,mybody)        
        
        if serverheader!=None and serverbody!=None:
            self.myUI.registerRequestandResponse(myheader,mybody,serverheader,serverbody)
        #print "Returning response: %s"%response
        
        return response    
        
    #############################################################################################
    #given a valid header and body, sends it off, and returns the result
    #also checks to see if the ui wants the requests, and can redirect it there
    #returns the response as a string
    #CALLED BY User Interface for rewrite support!!!
    def sendRequestRaw(self, myheader, mybody,mode=0,newserverheader=None, newserverbody=None):
        '''
    
        '''

        #supress request headers
        #print myheader.headerValuesDict
        myheader.headerValuesDict = self.myUI.DeleteRequestHeader(myheader.headerValuesDict)
        #print myheader.headerValuesDict

        #set this to 1 to enable this function's debugging messages
        #DEBUGSET
        debug_spkproxy=0
        myheader.normalize()
        if self.myUI.wantsRequest(myheader) and mode == 0:
            #print "Diverting request to the UI"
            #we force a closed connection for IE - it apparantly will not work otherwise
            if panteraLib.PanteraDataDict["debug"] == "yes":
                print "[P]> UI handling this request - sawConnectionClose==1"
            self.sawConnectionClose=1
            return (self.myUI.handleRequest(myheader,mybody),None,None)
        
        #print "MODE: "
        #print mode
        #if mode==0:
        # Check for Interceptor urls and delete
        self.myUI.DeleteInterceptorURL(myheader)
        
        #here we handle any restricted conditions
        restricted_host = []
        restricted_pages = []
        restricted_host = self.myUI.getRestrictedHost()
        restricted_pages = self.myUI.getRestrictedPages()
        
        #print restricted_host
        #print restricted_pages
        
        if restricted_pages != []:
            if panteraLib.PanteraDataDict["verbose"] == "yes":
                print "Checking restricted pages"
            if myheader.URL in restricted_pages:
                print "Page in restricted pages list"
                return (deniedstring, None, None)

        if restricted_host != []:
            if myheader.connectHost in restricted_host:
                return (deniedstring, None, None)
                
        byestring="<html><head><title>Error</title></head><body><h1>  No server there, sorry.</h1></body></html>"
        if not self.connectToWebServer(myheader):
            print "returning fake 501 page!"
            return ("HTTP/1.1 501 501 No Server There!\r\nContent-Length: "+str(len(byestring))+"\r\n\r\n"+byestring,None,None)

        #print "Setting proxy header to"+self.proxyHeader
        myheader.setProxyHeader(self.proxyHeader)

        #urg. I wish I could reference globals better
        myRequest=panterautils.constructRequest(myheader,mybody)
        
        self.myUI.AddCounter()
        
        #ok, now I have a socket connected to the host, send the data
        try:
            self.currentSocket.send(myRequest)
        except:
            return ("HTTP/1.1 501 No Server There!\r\nContent-Length: "+str(len(byestring))+"\r\n\r\n"+byestring,None,None)
        
        timeoflog=time.asctime()
        print "\r\n->->->->-> REQUEST %s - %s:%s [%s] ->->->->->\n\n" % (self.myUI.GetCounter(),myheader.connectHost,myheader.connectPort,timeoflog)    
        print myRequest

        t = "%s - %s:%s [%s] \n\n" % (self.myUI.GetCounter(),myheader.connectHost,myheader.connectPort,timeoflog)    
        t += str(myRequest)
        self.myUI.LogLine(t)

        returncode="100"
        #now read the response - we just ignore HTTP/1.1 100 Continue responses
        while returncode=="100":
            serverheader = HTTPHeader()
            serverheader.SetReplaceMode(1)
            serverheader.SetReplaceData(self.myUI.ReturnReplacerList())
            
            #commented out for testing
            #serverheader.setConnection(self.currentSocket)

            #print "Reading response now"
            while serverheader.isdone()==0:
                try:
                    #print "recieving"
                    data=self.currentSocket.recv(1)
                    #print "Read a byte: "+data
                except SSL.ZeroReturnError:
                    print "Server closed connection - weird"
                    data=""
                except socket.error:
                    print "Connection reset by peer"
                    data=""
                except SSL.SysCallError:
                    print "SSL recv error"
                    data=""
            
                if not data:
                    #print "end of data in response!"
                    break
                serverheader.addData(data)
            returncode=serverheader.returncode
            #print "Return code from server response="+returncode

            
        #print "end of header in response!"
        #+str(serverheader.data)
        #does a case insensitive match and returns us the content length
        #variable
        bodylength=serverheader.mybodysize
        #ok, now we're going to find out if we need to read-till-closed
        #or if we've got a 304 which naturally has no content-length
        if panteraLib.PanteraDataDict["debug"] == "yes":
            print "[P]> Server: "+serverheader.getStrValue(["Server"])
            print "[P]> Connection: "+serverheader.getStrValue(["Connection"]).lower()
            print "[P]> Returncode: "+serverheader.returncode
            print "[P]> Version: "+serverheader.version
            print "[P]> Bodylength: "+str(bodylength)
        readtillclosed=0
        
        #if you said to close, or you are redirecting AND you didn't bother to say,
        # then close the connection
        
        if serverheader.getStrValue(["Connection"]).lower() in ["close"] or (serverheader.returncode not in ["304","302","301"] and serverheader.getStrValue(["Connection"]).lower() in ["0"] ):
            if panteraLib.PanteraDataDict["debug"] == "yes":
                print "[P]> Connection: close detected"
            readtillclosed=1
        else:
            if panteraLib.PanteraDataDict["debug"] == "yes":
                print "[P]> Connection: close not detected or returncode of 304,302,301 reported "

        # Get target URLs
        u = ""
        if myheader.useRawArguments:
            if len(myheader.allURLargs) > 0:
                u+="?"+myheader.allURLargs
        else:
            if len(myheader.URLargsDict) > 0:
                u+="?"
                u+=panterautils.joinargs(myheader.URLargsDict,orderlist=myheader.orderlist)

        print "\r\n<-<-<-<-<- RESPONSE %s - %s:%s <-<-<-<-<-\n\n" % (self.myUI.GetCounter(),myheader.connectHost,myheader.connectPort)    
        #t = "".join(serverheader.data)
        #myReply = self.myUI.DoReplacer(t,1)
        myReply = "".join(serverheader.data)
        print myReply

        st = "%s - %s:%s \n\n" % (self.myUI.GetCounter(),myheader.connectHost,myheader.connectPort)    
        st += myReply
#        self.myUI.LogLine(st)
        
        serverbody=HTTPBody()
                   
        if bodylength>0 or readtillclosed or serverheader.wasChunked:
            if panteraLib.PanteraDataDict["debug"] == "yes":
                print "[P]> Reading a body of length "+str(bodylength)+" readtillclosed="+str(readtillclosed)+" chunked="+str(serverheader.wasChunked)
            serverbody.read(self.currentSocket,bodylength,serverheader.wasChunked,readtillclosed)

        # do body replacer
        if len(self.myUI.ReturnReplacerList())>0:
            serverbody.SetReplaceData(self.myUI.ReturnReplacerList())
            serverbody.DoReplace()

        if panteraLib.PanteraDataDict["debug"] == "yes":
            print "[P]> Body turned out to be "+str(serverbody.mysize)+" or "+str(len(serverbody.data))+" bytes."

        self.massageResponse(serverheader,serverbody)

        response=self.constructResponse(serverheader,serverbody)
        #response = self.myUI.DoReplacer(response,2)
        if (readtillclosed):
            if panteraLib.PanteraDataDict["debug"] == "yes":
                print "[P]> Saw Connection Close setting to 1"
            self.closeServerSocket()
            self.sawConnectionClose=1
            
        if newserverheader!=None:
            newserverheader=serverheader

        if newserverbody!=None:
            newserverbody=serverbody
        
        #delete response headers
        serverheader.headerValuesDict = self.myUI.DeleteResponseHeader(serverheader.headerValuesDict)

        st += "\n"
#        st += "".join(serverbody.data)
        #serverbody.printme()
        self.myUI.LogLine(st)
        
        return (response,serverheader,serverbody)

    #############################################################################################
    def cleanup(self):
        '''
    
        '''
        #needs to close socket and stuff
        self.connection.close()
        if self.haveSocket:
            self.currentSocket.close()
        self.haveSocket=0
        #print "CLEANING UP"
        #time.sleep(4)
        return
    
###############################################
#
# PanteraProxy class 
#
###############################################

#############################################################################################
# FUNC     : class PanteraProxy
# PARAMS   : ...
# RETURN   : ...
# ABSTRACT : PanteraProxy class. Ripped from SpikeProxy.
class PanteraProxy:
    '''
    
    '''
    
    #############################################################################################
    def __init__(self):
        '''
    
        '''
        self.myUI = panteraUI.PanteraProxyUI()
        self.myUI.setParent(self)
    
    #############################################################################################
    def addTo404List(self,newstring):
        '''
    
        '''
        panteraLib.Default404List.append(newstring)
        return

    #############################################################################################
    def removeFrom404List(self,oldstring):
        '''
    
        '''
        if oldstring in panteraLib.Default404List:
            panteraLib.Default404List.remove(oldstring)
        return

    #############################################################################################
    def get404List(self):
        '''
    
        '''
        return panteraLib.Default404List

    #############################################################################################
    def run(self):
        '''
    
        '''
        self.myUI.setNTLM((panteraLib.PanteraDataDict["user"],panteraLib.PanteraDataDict["password"],panteraLib.PanteraDataDict["domain"]))
        self.myUI.setProxy((panteraLib.PanteraDataDict["proxy_host"],panteraLib.PanteraDataDict["proxy_port"],panteraLib.PanteraDataDict["ssl_host"],panteraLib.PanteraDataDict["ssl_port"],panteraLib.PanteraDataDict["proxy_user"],panteraLib.PanteraDataDict["proxy_password"],panteraLib.PanteraDataDict["proxy_domain"]))

        #pass data to UI
        self.myUI.setConfigDataXML(panteraLib.PanteraDataDict)

        self.myUI.setDefault404(self.get404List())
        self.myUI.setRestrictedHost(panteraLib.RestrictedHostList)
        self.myUI.setRestrictedPages(panteraLib.RestrictedPagesList)
        self.myUI.setDontContentType(panteraLib.DontContentType)
        self.myUI.setTargetList(panteraLib.TargetList)        
        
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
        s.bind((panteraLib.PanteraDataDict["host"], panteraLib.PanteraDataDict["port"]))
        s.listen(5)
        while 1:
            s.settimeout(None)
            conn, addr = s.accept()
            s.settimeout(10)
            if panteraLib.PanteraDataDict["debug"] == "yes":
                print '[P]> Connected to by', addr
            connection=MyConnection(conn)
            self.handleConnection(connection)

    #############################################################################################
    def handleConnection(self,connection):
        '''
    
        '''
        #this needs to spawn a new thread!!
        connection = PanteraProxyConnection(connection,self.myUI,proxy=(panteraLib.PanteraDataDict["proxy_host"],panteraLib.PanteraDataDict["proxy_port"],panteraLib.PanteraDataDict["ssl_host"],panteraLib.PanteraDataDict["ssl_port"],panteraLib.PanteraDataDict["proxy_user"],panteraLib.PanteraDataDict["proxy_password"],panteraLib.PanteraDataDict["proxy_domain"]),ntlm=(panteraLib.PanteraDataDict["user"],panteraLib.PanteraDataDict["password"],panteraLib.PanteraDataDict["domain"]))
        connection.start()
        #done. :>
        
###############################################
#
# Program Entry Point
#
###############################################

#############################################################################################
# FUNC     : def Usage
# PARAMS   : ...
# RETURN   : ...
# ABSTRACT : Print Pantera usage.
def Usage():
    '''
    
    '''
    print "Pantera - Web Assessment Studio (WAS) V %s\n" % (__version__)
    print "Roses Labs Innovations (RL+I)\n"
    print "http://www.roseslabs.com\n\n"
    print "Usage : (Options)\n"
    print "Options:\n"
    print "-h : Print this help"
    print "-f : Set xml configure file. (default panteracfg.xml)\n"
# EOF: def Usage

#############################################################################################
if __name__ == '__main__':

    print ">>> Running Pantera - Web Assessment Studio (WAS) V %s" % (__version__)
    print ">>> Roses Labs Innovations (RL+I)"
    print ">>> http://www.roseslabs.com\n\n"
    
    #quit on control C and control break (win32)
    import signal
    signal.signal(signal.SIGINT,sys.exit)
    
    app = PanteraProxy()
    LoadConfig = panteraLib.DataParser()

    fil = ''

    try:
        (opts,args)=getopt.getopt(sys.argv[1:],"hf:")
    except getopt.GetoptError:
        Usage()
        sys.exit(2)
    for o,a in opts:
        if o in ["-h"]:
            Usage()
            sys.exit(2)
        if o in ["-f"]:
            fil = v

    if fil != '':
        LoadConfig.SetFilename(fil)
        
    # Begin config parser

    LoadConfig.BeginParse()
    
    panteraLib.PanteraDataDict = LoadConfig.GetConfigData()
    panteraLib.Default404List = LoadConfig.GetDefault404()
    panteraLib.RestrictedHostList = LoadConfig.GetRestrictedHost()
    panteraLib.RestrictedPagesList = LoadConfig.GetRestrictedPages()
    panteraLib.DontContentType = LoadConfig.GetDontContentType()
    panteraLib.TargetList = LoadConfig.GetTargetDomains()

    # Run Pantera!            
    app.run()

# RL+I EOF