1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
|
# GopherResponse.py
# $Id: GopherResponse.py,v 1.15 2001/07/09 22:32:14 s2mdalle Exp $
# Contains GopherResource and GopherResponse objects
# Written by David Allen <mda@idatar.com>
# Released under the terms of the GNU General Public License
#
# This object holds the data corresponding to how a gopher server responded
# to a given request. It holds one of two things in general, either a pile
# of data corresponding to text, a gif file, whatever, or it holds a list of
# GopherResource objects. (This is when it's a directory entry that's being
# stored.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
##############################################################################
import re
from string import *
from urlparse import *
from gopher import *
import Connection
import GopherConnection
import GopherObject
import GopherResource
import ResourceInformation
import Options
GopherResponseException = "Gopher response error"
class GopherResponse(GopherObject.GopherObject):
verbose = None
def __init__(self, type=None, host=None, port=None, loc=None, name=None):
GopherObject.GopherObject.__init__(self, type, host, port, loc, name)
self.__class = "GopherResponse"
self.data = None
self.responses = []
return None
def toProtocolString(self):
if self.getData() == None:
def protString(item):
return item.toProtocolString()
return join(map(protString, self.getResponses()), "")
else:
return self.getData()
return None
def writeToFile(self, filename):
"""Writes the contents of this response to a disk file at filename
this may raise IOError which the caller should deal with"""
fp = open(filename, "w")
if self.getData() == None:
for resp in self.getResponses():
fp.write(resp.toProtocolString())
else:
fp.write(self.getData())
fp.flush()
fp.close()
return filename
def getResponses(self):
"""Return a list of responses. This is only really good when the
response was a directory and set of entries."""
if self.getData() != None:
raise GopherResponseException, "Get data instead."
return self.responses
def getData(self):
"""Return the data associated with the response. This is usually all
of the data off of the socket except the trailing closer."""
return self.data
def getDataLength(self):
return len(self.data)
def getDataChunk(self, startIndex, endIndex=-1):
"""This method provides a way to get chunks of data at a time,
rather than snarfing the entire ball."""
if endIndex == -1:
endIndex = len(self.data)
return self.data[startindex, endIndex]
def getError(self):
"""If there was an error message, return it."""
try:
return self.error
except:
return None
def setError(self, errorstr):
"""Modify the error message within this response."""
self.error = errorstr
return self.getError()
def setData(self, data):
"""Modify the data within the response. You probably don't want to
do this."""
self.data = data
return None
def looksLikeDir(self, data):
"""Takes a chunk of data and returns true if it looks like directory
data, and false otherwise. This is tricky, and is of course not 100%.
Basically, we look at each line, see if the first bit in the line is a
legal type, check to see that the line has at least 3 tabs in it. If
all of the first 20 lines of the data follow that rule, then it's good
enough to be used as directory data. If not, it gets chucked. Notice
that if this really is a directory but it's using file types we've
never heard of (see gopher.py) then it will still get thrown out.
Bummer. This should only be called anyway if the type indictator is
incorrect, so cope. :)"""
def linefn(l):
return replace(l, "\r", "")
# Some very strange non-standards compliant servers send \r on some
# lines and not on others. So split by newline and remove all
# carriage returns as they occur.
lines = map(linefn, split(data, "\n", 10))
for line in lines:
d = strip(line)
if not d or d == '' or d == '.':
continue
if count(line, "\t") < 2:
return None # Not enough tabs. Bummer.
isResponse = None
byte = line[0]
try:
resp = responses[byte]
isRespose = 1
except:
pass
if isResponse:
continue
try:
resp = errors[byte]
except:
return None
if len(lines) > 0:
return 1 # Matched all tests for max 20 lines. Looks OK.
else:
return 0 # Empty data isn't a directory.
def parseResponse(self, data):
"""Takes a lump of data, and tries to parse it as if it was a directory
response. It will set the responses array to the proper thing if the
result was good, so that you can use self.getRepsonses() to access
them. Otherwise it raises GopherResponseException"""
self.responses = []
if self.type == RESPONSE_DIR:
pass # Keep going
elif self.looksLikeDir(data):
self.type = RESPONSE_DIR
else:
raise GopherException, "This isn't a directory."
def stripCR(dat):
return replace(dat, "\r", "")
# This is done because \r may or may not be present, so we can't
# split by \r\n because it fails for some misbehaved gopher servers.
self.lines = map(stripCR, split(data, "\n"))
for line in self.lines:
if len(line) <= 1:
continue
# Type of the resource. See gopher.py for the possibilities.
stype = "%s" % line[0]
line = line[1:]
# Gopher protocol uses tab delimited fields...
linedata = split(line, "\t")
name = "Unknown" # Silly defaults
locator = "Unknown"
host = "Unknown"
port = 70
try:
name = linedata[0] # Assign the right things in the
except IndexError: pass # right places (maybe)
try: # Catch those errors coming from
locator = linedata[1] # the line not being split into
except IndexError: pass # enough tokens. Realistically,
try: # if those errors happen,
host = linedata[2] # something is really screwed up
except IndexError: pass # and there's not much we can do
try: # anyway.
port = linedata[3]
except IndexError: pass
try:
remainder = linedata[4:] # Extra Gopher+ fields.
except:
remainder = []
# UMN gopherd errors do this sometimes. It's quite annoying.
# they list the response type as 'directory' and then put the host
# as 'error.host' to flag errors
if host == 'error.host' and stype != RESPONSE_BLURB:
stype = RESPONSE_ERR
newresource = GopherResource.GopherResource(stype, host,
port, locator, name,
remainder)
# Are the options set to allow us to get info?
# THIS SHOULD BE USED WITH CAUTION since it can slow things down
# more than you might think.
if Options.program_options.getOption('grab_resource_info'):
if len(remainder) >= 1 and remainder[0] == '+':
try:
conn = GopherConnection.GopherConnection()
info = conn.getInfo(newresource)
newresource.setInfo(info)
except GopherConnectionException, estr:
print "***(GCE) can't get info: %s" % estr
except Exception, estr:
print "***(unknown) Couldn't %s %s" % (Exception,estr)
self.responses.append(newresource)
return None
# End GopherResponse
|