You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

gopherlib.py 5.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. """Gopher protocol client interface."""
  2. __all__ = ["send_selector","send_query"]
  3. # Default selector, host and port
  4. DEF_SELECTOR = '1/'
  5. DEF_HOST = 'gopher.micro.umn.edu'
  6. DEF_PORT = 70
  7. # Recognized file types
  8. A_TEXT = '0'
  9. A_MENU = '1'
  10. A_CSO = '2'
  11. A_ERROR = '3'
  12. A_MACBINHEX = '4'
  13. A_PCBINHEX = '5'
  14. A_UUENCODED = '6'
  15. A_INDEX = '7'
  16. A_TELNET = '8'
  17. A_BINARY = '9'
  18. A_DUPLICATE = '+'
  19. A_SOUND = 's'
  20. A_EVENT = 'e'
  21. A_CALENDAR = 'c'
  22. A_HTML = 'h'
  23. A_TN3270 = 'T'
  24. A_MIME = 'M'
  25. A_IMAGE = 'I'
  26. A_WHOIS = 'w'
  27. A_QUERY = 'q'
  28. A_GIF = 'g'
  29. A_HTML = 'h' # HTML file
  30. A_WWW = 'w' # WWW address
  31. A_PLUS_IMAGE = ':'
  32. A_PLUS_MOVIE = ';'
  33. A_PLUS_SOUND = '<'
  34. _names = dir()
  35. _type_to_name_map = {}
  36. def type_to_name(gtype):
  37. """Map all file types to strings; unknown types become TYPE='x'."""
  38. global _type_to_name_map
  39. if _type_to_name_map=={}:
  40. for name in _names:
  41. if name[:2] == 'A_':
  42. _type_to_name_map[eval(name)] = name[2:]
  43. if _type_to_name_map.has_key(gtype):
  44. return _type_to_name_map[gtype]
  45. return 'TYPE=' + `gtype`
  46. # Names for characters and strings
  47. CRLF = '\r\n'
  48. TAB = '\t'
  49. def send_selector(selector, host, port = 0):
  50. """Send a selector to a given host and port, return a file with the reply."""
  51. import socket
  52. if not port:
  53. i = host.find(':')
  54. if i >= 0:
  55. host, port = host[:i], int(host[i+1:])
  56. if not port:
  57. port = DEF_PORT
  58. elif type(port) == type(''):
  59. port = int(port)
  60. s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  61. s.connect((host, port))
  62. s.send(selector + CRLF)
  63. s.shutdown(1)
  64. return s.makefile('rb')
  65. def send_query(selector, query, host, port = 0):
  66. """Send a selector and a query string."""
  67. return send_selector(selector + '\t' + query, host, port)
  68. def path_to_selector(path):
  69. """Takes a path as returned by urlparse and returns the appropriate selector."""
  70. if path=="/":
  71. return "/"
  72. else:
  73. return path[2:] # Cuts initial slash and data type identifier
  74. def path_to_datatype_name(path):
  75. """Takes a path as returned by urlparse and maps it to a string.
  76. See section 3.4 of RFC 1738 for details."""
  77. if path=="/":
  78. # No way to tell, although "INDEX" is likely
  79. return "TYPE='unknown'"
  80. else:
  81. return type_to_name(path[1])
  82. # The following functions interpret the data returned by the gopher
  83. # server according to the expected type, e.g. textfile or directory
  84. def get_directory(f):
  85. """Get a directory in the form of a list of entries."""
  86. list = []
  87. while 1:
  88. line = f.readline()
  89. if not line:
  90. print '(Unexpected EOF from server)'
  91. break
  92. if line[-2:] == CRLF:
  93. line = line[:-2]
  94. elif line[-1:] in CRLF:
  95. line = line[:-1]
  96. if line == '.':
  97. break
  98. if not line:
  99. print '(Empty line from server)'
  100. continue
  101. gtype = line[0]
  102. parts = line[1:].split(TAB)
  103. if len(parts) < 4:
  104. print '(Bad line from server:', `line`, ')'
  105. continue
  106. if len(parts) > 4:
  107. if parts[4:] != ['+']:
  108. print '(Extra info from server:',
  109. print parts[4:], ')'
  110. else:
  111. parts.append('')
  112. parts.insert(0, gtype)
  113. list.append(parts)
  114. return list
  115. def get_textfile(f):
  116. """Get a text file as a list of lines, with trailing CRLF stripped."""
  117. list = []
  118. get_alt_textfile(f, list.append)
  119. return list
  120. def get_alt_textfile(f, func):
  121. """Get a text file and pass each line to a function, with trailing CRLF stripped."""
  122. while 1:
  123. line = f.readline()
  124. if not line:
  125. print '(Unexpected EOF from server)'
  126. break
  127. if line[-2:] == CRLF:
  128. line = line[:-2]
  129. elif line[-1:] in CRLF:
  130. line = line[:-1]
  131. if line == '.':
  132. break
  133. if line[:2] == '..':
  134. line = line[1:]
  135. func(line)
  136. def get_binary(f):
  137. """Get a binary file as one solid data block."""
  138. data = f.read()
  139. return data
  140. def get_alt_binary(f, func, blocksize):
  141. """Get a binary file and pass each block to a function."""
  142. while 1:
  143. data = f.read(blocksize)
  144. if not data:
  145. break
  146. func(data)
  147. def test():
  148. """Trivial test program."""
  149. import sys
  150. import getopt
  151. opts, args = getopt.getopt(sys.argv[1:], '')
  152. selector = DEF_SELECTOR
  153. type = selector[0]
  154. host = DEF_HOST
  155. port = DEF_PORT
  156. if args:
  157. host = args[0]
  158. args = args[1:]
  159. if args:
  160. type = args[0]
  161. args = args[1:]
  162. if len(type) > 1:
  163. type, selector = type[0], type
  164. else:
  165. selector = ''
  166. if args:
  167. selector = args[0]
  168. args = args[1:]
  169. query = ''
  170. if args:
  171. query = args[0]
  172. args = args[1:]
  173. if type == A_INDEX:
  174. f = send_query(selector, query, host)
  175. else:
  176. f = send_selector(selector, host)
  177. if type == A_TEXT:
  178. list = get_textfile(f)
  179. for item in list: print item
  180. elif type in (A_MENU, A_INDEX):
  181. list = get_directory(f)
  182. for item in list: print item
  183. else:
  184. data = get_binary(f)
  185. print 'binary data:', len(data), 'bytes:', `data[:100]`[:40]
  186. # Run the test when run as script
  187. if __name__ == '__main__':
  188. test()