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