blob: 9514832acf2d606a89cbcae9abc5df17999dde30 [file] [log] [blame]
Guido van Rossum54f22ed2000-02-04 15:10:34 +00001"""Gopher protocol client interface."""
Guido van Rossum23acc951994-02-21 16:36:04 +00002
3import string
4
Skip Montanaro2dd42762001-01-23 15:35:05 +00005__all__ = ["send_selector","send_query"]
6
Guido van Rossum23acc951994-02-21 16:36:04 +00007# Default selector, host and port
8DEF_SELECTOR = '1/'
9DEF_HOST = 'gopher.micro.umn.edu'
10DEF_PORT = 70
11
12# Recognized file types
13A_TEXT = '0'
14A_MENU = '1'
15A_CSO = '2'
16A_ERROR = '3'
17A_MACBINHEX = '4'
18A_PCBINHEX = '5'
19A_UUENCODED = '6'
20A_INDEX = '7'
21A_TELNET = '8'
22A_BINARY = '9'
23A_DUPLICATE = '+'
24A_SOUND = 's'
25A_EVENT = 'e'
26A_CALENDAR = 'c'
27A_HTML = 'h'
28A_TN3270 = 'T'
29A_MIME = 'M'
30A_IMAGE = 'I'
31A_WHOIS = 'w'
32A_QUERY = 'q'
33A_GIF = 'g'
Guido van Rossum54f22ed2000-02-04 15:10:34 +000034A_HTML = 'h' # HTML file
35A_WWW = 'w' # WWW address
Guido van Rossum23acc951994-02-21 16:36:04 +000036A_PLUS_IMAGE = ':'
37A_PLUS_MOVIE = ';'
38A_PLUS_SOUND = '<'
39
40
Guido van Rossum23acc951994-02-21 16:36:04 +000041_names = dir()
Guido van Rossumd2dd9a81998-01-19 21:59:48 +000042_type_to_name_map = {}
Guido van Rossum23acc951994-02-21 16:36:04 +000043def type_to_name(gtype):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000044 """Map all file types to strings; unknown types become TYPE='x'."""
45 global _type_to_name_map
46 if _type_to_name_map=={}:
47 for name in _names:
48 if name[:2] == 'A_':
49 _type_to_name_map[eval(name)] = name[2:]
50 if _type_to_name_map.has_key(gtype):
51 return _type_to_name_map[gtype]
52 return 'TYPE=' + `gtype`
Guido van Rossum23acc951994-02-21 16:36:04 +000053
54# Names for characters and strings
55CRLF = '\r\n'
56TAB = '\t'
57
Guido van Rossum2922c6d1994-05-06 14:28:19 +000058def send_selector(selector, host, port = 0):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000059 """Send a selector to a given host and port, return a file with the reply."""
60 import socket
61 import string
62 if not port:
63 i = string.find(host, ':')
64 if i >= 0:
65 host, port = host[:i], string.atoi(host[i+1:])
66 if not port:
67 port = DEF_PORT
68 elif type(port) == type(''):
69 port = string.atoi(port)
70 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Guido van Rossum93a7c0f2000-03-28 21:45:46 +000071 s.connect((host, port))
Guido van Rossum54f22ed2000-02-04 15:10:34 +000072 s.send(selector + CRLF)
73 s.shutdown(1)
74 return s.makefile('rb')
Guido van Rossum23acc951994-02-21 16:36:04 +000075
Guido van Rossum2922c6d1994-05-06 14:28:19 +000076def send_query(selector, query, host, port = 0):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000077 """Send a selector and a query string."""
78 return send_selector(selector + '\t' + query, host, port)
Guido van Rossum23acc951994-02-21 16:36:04 +000079
Guido van Rossumd2dd9a81998-01-19 21:59:48 +000080def path_to_selector(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000081 """Takes a path as returned by urlparse and returns the appropriate selector."""
82 if path=="/":
83 return "/"
84 else:
85 return path[2:] # Cuts initial slash and data type identifier
Guido van Rossumd2dd9a81998-01-19 21:59:48 +000086
Guido van Rossumd2dd9a81998-01-19 21:59:48 +000087def path_to_datatype_name(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000088 """Takes a path as returned by urlparse and maps it to a string.
89 See section 3.4 of RFC 1738 for details."""
90 if path=="/":
91 # No way to tell, although "INDEX" is likely
92 return "TYPE='unknown'"
93 else:
94 return type_to_name(path[1])
Guido van Rossum8ca84201998-03-26 20:56:10 +000095
Guido van Rossum23acc951994-02-21 16:36:04 +000096# The following functions interpret the data returned by the gopher
97# server according to the expected type, e.g. textfile or directory
98
Guido van Rossum23acc951994-02-21 16:36:04 +000099def get_directory(f):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000100 """Get a directory in the form of a list of entries."""
101 import string
102 list = []
103 while 1:
104 line = f.readline()
105 if not line:
106 print '(Unexpected EOF from server)'
107 break
108 if line[-2:] == CRLF:
109 line = line[:-2]
110 elif line[-1:] in CRLF:
111 line = line[:-1]
112 if line == '.':
113 break
114 if not line:
115 print '(Empty line from server)'
116 continue
117 gtype = line[0]
118 parts = string.splitfields(line[1:], TAB)
119 if len(parts) < 4:
120 print '(Bad line from server:', `line`, ')'
121 continue
122 if len(parts) > 4:
123 if parts[4:] != ['+']:
124 print '(Extra info from server:',
125 print parts[4:], ')'
126 else:
127 parts.append('')
128 parts.insert(0, gtype)
129 list.append(parts)
130 return list
Guido van Rossum23acc951994-02-21 16:36:04 +0000131
Guido van Rossum23acc951994-02-21 16:36:04 +0000132def get_textfile(f):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000133 """Get a text file as a list of lines, with trailing CRLF stripped."""
134 list = []
135 get_alt_textfile(f, list.append)
136 return list
Guido van Rossum23acc951994-02-21 16:36:04 +0000137
Guido van Rossum23acc951994-02-21 16:36:04 +0000138def get_alt_textfile(f, func):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000139 """Get a text file and pass each line to a function, with trailing CRLF stripped."""
140 while 1:
141 line = f.readline()
142 if not line:
143 print '(Unexpected EOF from server)'
144 break
145 if line[-2:] == CRLF:
146 line = line[:-2]
147 elif line[-1:] in CRLF:
148 line = line[:-1]
149 if line == '.':
150 break
151 if line[:2] == '..':
152 line = line[1:]
153 func(line)
Guido van Rossum23acc951994-02-21 16:36:04 +0000154
Guido van Rossum23acc951994-02-21 16:36:04 +0000155def get_binary(f):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000156 """Get a binary file as one solid data block."""
157 data = f.read()
158 return data
Guido van Rossum23acc951994-02-21 16:36:04 +0000159
Guido van Rossum23acc951994-02-21 16:36:04 +0000160def get_alt_binary(f, func, blocksize):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000161 """Get a binary file and pass each block to a function."""
162 while 1:
163 data = f.read(blocksize)
164 if not data:
165 break
166 func(data)
Guido van Rossum23acc951994-02-21 16:36:04 +0000167
Guido van Rossum23acc951994-02-21 16:36:04 +0000168def test():
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000169 """Trivial test program."""
170 import sys
171 import getopt
172 opts, args = getopt.getopt(sys.argv[1:], '')
173 selector = DEF_SELECTOR
174 type = selector[0]
175 host = DEF_HOST
176 port = DEF_PORT
177 if args:
178 host = args[0]
179 args = args[1:]
180 if args:
181 type = args[0]
182 args = args[1:]
183 if len(type) > 1:
184 type, selector = type[0], type
185 else:
186 selector = ''
187 if args:
188 selector = args[0]
189 args = args[1:]
190 query = ''
191 if args:
192 query = args[0]
193 args = args[1:]
194 if type == A_INDEX:
195 f = send_query(selector, query, host)
196 else:
197 f = send_selector(selector, host)
198 if type == A_TEXT:
199 list = get_textfile(f)
200 for item in list: print item
201 elif type in (A_MENU, A_INDEX):
202 list = get_directory(f)
203 for item in list: print item
204 else:
205 data = get_binary(f)
206 print 'binary data:', len(data), 'bytes:', `data[:100]`[:40]
Guido van Rossum23acc951994-02-21 16:36:04 +0000207
208# Run the test when run as script
209if __name__ == '__main__':
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000210 test()