0001 """Gopher protocol client interface.""" 0002 0003 __all__ = ["send_selector","send_query"] 0004 0005 # Default selector, host and port 0006 DEF_SELECTOR = '1/' 0007 DEF_HOST = 'gopher.micro.umn.edu' 0008 DEF_PORT = 70 0009 0010 # Recognized file types 0011 A_TEXT = '0' 0012 A_MENU = '1' 0013 A_CSO = '2' 0014 A_ERROR = '3' 0015 A_MACBINHEX = '4' 0016 A_PCBINHEX = '5' 0017 A_UUENCODED = '6' 0018 A_INDEX = '7' 0019 A_TELNET = '8' 0020 A_BINARY = '9' 0021 A_DUPLICATE = '+' 0022 A_SOUND = 's' 0023 A_EVENT = 'e' 0024 A_CALENDAR = 'c' 0025 A_HTML = 'h' 0026 A_TN3270 = 'T' 0027 A_MIME = 'M' 0028 A_IMAGE = 'I' 0029 A_WHOIS = 'w' 0030 A_QUERY = 'q' 0031 A_GIF = 'g' 0032 A_HTML = 'h' # HTML file 0033 A_WWW = 'w' # WWW address 0034 A_PLUS_IMAGE = ':' 0035 A_PLUS_MOVIE = ';' 0036 A_PLUS_SOUND = '<' 0037 0038 0039 _names = dir() 0040 _type_to_name_map = {} 0041 def type_to_name(gtype): 0042 """Map all file types to strings; unknown types become TYPE='x'.""" 0043 global _type_to_name_map 0044 if _type_to_name_map=={}: 0045 for name in _names: 0046 if name[:2] == 'A_': 0047 _type_to_name_map[eval(name)] = name[2:] 0048 if gtype in _type_to_name_map: 0049 return _type_to_name_map[gtype] 0050 return 'TYPE=%r' % (gtype,) 0051 0052 # Names for characters and strings 0053 CRLF = '\r\n' 0054 TAB = '\t' 0055 0056 def send_selector(selector, host, port = 0): 0057 """Send a selector to a given host and port, return a file with the reply.""" 0058 import socket 0059 if not port: 0060 i = host.find(':') 0061 if i >= 0: 0062 host, port = host[:i], int(host[i+1:]) 0063 if not port: 0064 port = DEF_PORT 0065 elif type(port) == type(''): 0066 port = int(port) 0067 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 0068 s.connect((host, port)) 0069 s.sendall(selector + CRLF) 0070 s.shutdown(1) 0071 return s.makefile('rb') 0072 0073 def send_query(selector, query, host, port = 0): 0074 """Send a selector and a query string.""" 0075 return send_selector(selector + '\t' + query, host, port) 0076 0077 def path_to_selector(path): 0078 """Takes a path as returned by urlparse and returns the appropriate selector.""" 0079 if path=="/": 0080 return "/" 0081 else: 0082 return path[2:] # Cuts initial slash and data type identifier 0083 0084 def path_to_datatype_name(path): 0085 """Takes a path as returned by urlparse and maps it to a string. 0086 See section 3.4 of RFC 1738 for details.""" 0087 if path=="/": 0088 # No way to tell, although "INDEX" is likely 0089 return "TYPE='unknown'" 0090 else: 0091 return type_to_name(path[1]) 0092 0093 # The following functions interpret the data returned by the gopher 0094 # server according to the expected type, e.g. textfile or directory 0095 0096 def get_directory(f): 0097 """Get a directory in the form of a list of entries.""" 0098 entries = [] 0099 while 1: 0100 line = f.readline() 0101 if not line: 0102 print '(Unexpected EOF from server)' 0103 break 0104 if line[-2:] == CRLF: 0105 line = line[:-2] 0106 elif line[-1:] in CRLF: 0107 line = line[:-1] 0108 if line == '.': 0109 break 0110 if not line: 0111 print '(Empty line from server)' 0112 continue 0113 gtype = line[0] 0114 parts = line[1:].split(TAB) 0115 if len(parts) < 4: 0116 print '(Bad line from server: %r)' % (line,) 0117 continue 0118 if len(parts) > 4: 0119 if parts[4:] != ['+']: 0120 print '(Extra info from server:', 0121 print parts[4:], ')' 0122 else: 0123 parts.append('') 0124 parts.insert(0, gtype) 0125 entries.append(parts) 0126 return entries 0127 0128 def get_textfile(f): 0129 """Get a text file as a list of lines, with trailing CRLF stripped.""" 0130 lines = [] 0131 get_alt_textfile(f, lines.append) 0132 return lines 0133 0134 def get_alt_textfile(f, func): 0135 """Get a text file and pass each line to a function, with trailing CRLF stripped.""" 0136 while 1: 0137 line = f.readline() 0138 if not line: 0139 print '(Unexpected EOF from server)' 0140 break 0141 if line[-2:] == CRLF: 0142 line = line[:-2] 0143 elif line[-1:] in CRLF: 0144 line = line[:-1] 0145 if line == '.': 0146 break 0147 if line[:2] == '..': 0148 line = line[1:] 0149 func(line) 0150 0151 def get_binary(f): 0152 """Get a binary file as one solid data block.""" 0153 data = f.read() 0154 return data 0155 0156 def get_alt_binary(f, func, blocksize): 0157 """Get a binary file and pass each block to a function.""" 0158 while 1: 0159 data = f.read(blocksize) 0160 if not data: 0161 break 0162 func(data) 0163 0164 def test(): 0165 """Trivial test program.""" 0166 import sys 0167 import getopt 0168 opts, args = getopt.getopt(sys.argv[1:], '') 0169 selector = DEF_SELECTOR 0170 type = selector[0] 0171 host = DEF_HOST 0172 if args: 0173 host = args[0] 0174 args = args[1:] 0175 if args: 0176 type = args[0] 0177 args = args[1:] 0178 if len(type) > 1: 0179 type, selector = type[0], type 0180 else: 0181 selector = '' 0182 if args: 0183 selector = args[0] 0184 args = args[1:] 0185 query = '' 0186 if args: 0187 query = args[0] 0188 args = args[1:] 0189 if type == A_INDEX: 0190 f = send_query(selector, query, host) 0191 else: 0192 f = send_selector(selector, host) 0193 if type == A_TEXT: 0194 lines = get_textfile(f) 0195 for item in lines: print item 0196 elif type in (A_MENU, A_INDEX): 0197 entries = get_directory(f) 0198 for item in entries: print item 0199 else: 0200 data = get_binary(f) 0201 print 'binary data:', len(data), 'bytes:', repr(data[:100])[:40] 0202 0203 # Run the test when run as script 0204 if __name__ == '__main__': 0205 test() 0206
Generated by PyXR 0.9.4