home *** CD-ROM | disk | FTP | other *** search
/ PC Extra 07 & 08 / pca1507.iso / Software / psp8 / Data1.cab / webbrowser.py < prev    next >
Encoding:
Python Source  |  2003-04-22  |  10.0 KB  |  326 lines

  1. """Interfaces for launching and remotely controlling Web browsers."""
  2.  
  3. import os
  4. import sys
  5.  
  6. __all__ = ["Error", "open", "get", "register"]
  7.  
  8. class Error(Exception):
  9.     pass
  10.  
  11. _browsers = {}          # Dictionary of available browser controllers
  12. _tryorder = []          # Preference order of available browsers
  13.  
  14. def register(name, klass, instance=None):
  15.     """Register a browser connector and, optionally, connection."""
  16.     _browsers[name.lower()] = [klass, instance]
  17.  
  18. def get(using=None):
  19.     """Return a browser launcher instance appropriate for the environment."""
  20.     if using:
  21.         alternatives = [using]
  22.     else:
  23.         alternatives = _tryorder
  24.     for browser in alternatives:
  25.         if browser.find('%s') > -1:
  26.             # User gave us a command line, don't mess with it.
  27.             return GenericBrowser(browser)
  28.         else:
  29.             # User gave us a browser name.
  30.             try:
  31.                 command = _browsers[browser.lower()]
  32.             except KeyError:
  33.                 command = _synthesize(browser)
  34.             if command[1] is None:
  35.                 return command[0]()
  36.             else:
  37.                 return command[1]
  38.     raise Error("could not locate runnable browser")
  39.  
  40. # Please note: the following definition hides a builtin function.
  41.  
  42. def open(url, new=0, autoraise=1):
  43.     get().open(url, new, autoraise)
  44.  
  45. def open_new(url):
  46.     get().open(url, 1)
  47.  
  48.  
  49. def _synthesize(browser):
  50.     """Attempt to synthesize a controller base on existing controllers.
  51.  
  52.     This is useful to create a controller when a user specifies a path to
  53.     an entry in the BROWSER environment variable -- we can copy a general
  54.     controller to operate using a specific installation of the desired
  55.     browser in this way.
  56.  
  57.     If we can't create a controller in this way, or if there is no
  58.     executable for the requested browser, return [None, None].
  59.  
  60.     """
  61.     if not os.path.exists(browser):
  62.         return [None, None]
  63.     name = os.path.basename(browser)
  64.     try:
  65.         command = _browsers[name.lower()]
  66.     except KeyError:
  67.         return [None, None]
  68.     # now attempt to clone to fit the new name:
  69.     controller = command[1]
  70.     if controller and name.lower() == controller.basename:
  71.         import copy
  72.         controller = copy.copy(controller)
  73.         controller.name = browser
  74.         controller.basename = os.path.basename(browser)
  75.         register(browser, None, controller)
  76.         return [None, controller]
  77.     return [None, None]
  78.  
  79.  
  80. def _iscommand(cmd):
  81.     """Return true if cmd can be found on the executable search path."""
  82.     path = os.environ.get("PATH")
  83.     if not path:
  84.         return 0
  85.     for d in path.split(os.pathsep):
  86.         exe = os.path.join(d, cmd)
  87.         if os.path.isfile(exe):
  88.             return 1
  89.     return 0
  90.  
  91.  
  92. PROCESS_CREATION_DELAY = 4
  93.  
  94.  
  95. class GenericBrowser:
  96.     def __init__(self, cmd):
  97.         self.name, self.args = cmd.split(None, 1)
  98.         self.basename = os.path.basename(self.name)
  99.  
  100.     def open(self, url, new=0, autoraise=1):
  101.         command = "%s %s" % (self.name, self.args)
  102.         os.system(command % url)
  103.  
  104.     def open_new(self, url):
  105.         self.open(url)
  106.  
  107.  
  108. class Netscape:
  109.     "Launcher class for Netscape browsers."
  110.     def __init__(self, name):
  111.         self.name = name
  112.         self.basename = os.path.basename(name)
  113.  
  114.     def _remote(self, action, autoraise):
  115.         raise_opt = ("-noraise", "-raise")[autoraise]
  116.         cmd = "%s %s -remote '%s' >/dev/null 2>&1" % (self.name,
  117.                                                       raise_opt,
  118.                                                       action)
  119.         rc = os.system(cmd)
  120.         if rc:
  121.             import time
  122.             os.system("%s &" % self.name)
  123.             time.sleep(PROCESS_CREATION_DELAY)
  124.             rc = os.system(cmd)
  125.         return not rc
  126.  
  127.     def open(self, url, new=0, autoraise=1):
  128.         if new:
  129.             self._remote("openURL(%s, new-window)"%url, autoraise)
  130.         else:
  131.             self._remote("openURL(%s)" % url, autoraise)
  132.  
  133.     def open_new(self, url):
  134.         self.open(url, 1)
  135.  
  136.  
  137. class Konqueror:
  138.     """Controller for the KDE File Manager (kfm, or Konqueror).
  139.  
  140.     See http://developer.kde.org/documentation/other/kfmclient.html
  141.     for more information on the Konqueror remote-control interface.
  142.  
  143.     """
  144.     def __init__(self):
  145.         if _iscommand("konqueror"):
  146.             self.name = self.basename = "konqueror"
  147.         else:
  148.             self.name = self.basename = "kfm"
  149.  
  150.     def _remote(self, action):
  151.         cmd = "kfmclient %s >/dev/null 2>&1" % action
  152.         rc = os.system(cmd)
  153.         if rc:
  154.             import time
  155.             if self.basename == "konqueror":
  156.                 os.system(self.name + " --silent &")
  157.             else:
  158.                 os.system(self.name + " -d &")
  159.             time.sleep(PROCESS_CREATION_DELAY)
  160.             rc = os.system(cmd)
  161.         return not rc
  162.  
  163.     def open(self, url, new=1, autoraise=1):
  164.         # XXX Currently I know no way to prevent KFM from
  165.         # opening a new win.
  166.         self._remote("openURL %s" % url)
  167.  
  168.     open_new = open
  169.  
  170.  
  171. class Grail:
  172.     # There should be a way to maintain a connection to Grail, but the
  173.     # Grail remote control protocol doesn't really allow that at this
  174.     # point.  It probably neverwill!
  175.     def _find_grail_rc(self):
  176.         import glob
  177.         import pwd
  178.         import socket
  179.         import tempfile
  180.         tempdir = os.path.join(tempfile.gettempdir(),
  181.                                ".grail-unix")
  182.         user = pwd.getpwuid(os.getuid())[0]
  183.         filename = os.path.join(tempdir, user + "-*")
  184.         maybes = glob.glob(filename)
  185.         if not maybes:
  186.             return None
  187.         s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
  188.         for fn in maybes:
  189.             # need to PING each one until we find one that's live
  190.             try:
  191.                 s.connect(fn)
  192.             except socket.error:
  193.                 # no good; attempt to clean it out, but don't fail:
  194.                 try:
  195.                     os.unlink(fn)
  196.                 except IOError:
  197.                     pass
  198.             else:
  199.                 return s
  200.  
  201.     def _remote(self, action):
  202.         s = self._find_grail_rc()
  203.         if not s:
  204.             return 0
  205.         s.send(action)
  206.         s.close()
  207.         return 1
  208.  
  209.     def open(self, url, new=0, autoraise=1):
  210.         if new:
  211.             self._remote("LOADNEW " + url)
  212.         else:
  213.             self._remote("LOAD " + url)
  214.  
  215.     def open_new(self, url):
  216.         self.open(url, 1)
  217.  
  218.  
  219. class WindowsDefault:
  220.     def open(self, url, new=0, autoraise=1):
  221.         os.startfile(url)
  222.  
  223.     def open_new(self, url):
  224.         self.open(url)
  225.  
  226. #
  227. # Platform support for Unix
  228. #
  229.  
  230. # This is the right test because all these Unix browsers require either
  231. # a console terminal of an X display to run.  Note that we cannot split
  232. # the TERM and DISPLAY cases, because we might be running Python from inside
  233. # an xterm.
  234. if os.environ.get("TERM") or os.environ.get("DISPLAY"):
  235.     _tryorder = ["mozilla","netscape","kfm","grail","links","lynx","w3m"]
  236.  
  237.     # Easy cases first -- register console browsers if we have them.
  238.     if os.environ.get("TERM"):
  239.         # The Links browser <http://artax.karlin.mff.cuni.cz/~mikulas/links/>
  240.         if _iscommand("links"):
  241.             register("links", None, GenericBrowser("links %s"))
  242.         # The Lynx browser <http://lynx.browser.org/>
  243.         if _iscommand("lynx"):
  244.             register("lynx", None, GenericBrowser("lynx %s"))
  245.         # The w3m browser <http://ei5nazha.yz.yamagata-u.ac.jp/~aito/w3m/eng/>
  246.         if _iscommand("w3m"):
  247.             register("w3m", None, GenericBrowser("w3m %s"))
  248.  
  249.     # X browsers have more in the way of options
  250.     if os.environ.get("DISPLAY"):
  251.         # First, the Netscape series
  252.         if _iscommand("netscape") or _iscommand("mozilla"):
  253.             if _iscommand("mozilla"):
  254.                 register("mozilla", None, Netscape("mozilla"))
  255.             if _iscommand("netscape"):
  256.                 register("netscape", None, Netscape("netscape"))
  257.  
  258.         # Next, Mosaic -- old but still in use.
  259.         if _iscommand("mosaic"):
  260.             register("mosaic", None, GenericBrowser("mosaic %s >/dev/null &"))
  261.  
  262.         # Konqueror/kfm, the KDE browser.
  263.         if _iscommand("kfm") or _iscommand("konqueror"):
  264.             register("kfm", Konqueror, Konqueror())
  265.  
  266.         # Grail, the Python browser.
  267.         if _iscommand("grail"):
  268.             register("grail", Grail, None)
  269.  
  270.  
  271. class InternetConfig:
  272.     def open(self, url, new=0, autoraise=1):
  273.         ic.launchurl(url)
  274.  
  275.     def open_new(self, url):
  276.         self.open(url)
  277.  
  278.  
  279. #
  280. # Platform support for Windows
  281. #
  282.  
  283. if sys.platform[:3] == "win":
  284.     _tryorder = ["netscape", "windows-default"]
  285.     register("windows-default", WindowsDefault)
  286.  
  287. #
  288. # Platform support for MacOS
  289. #
  290.  
  291. try:
  292.     import ic
  293. except ImportError:
  294.     pass
  295. else:
  296.     # internet-config is the only supported controller on MacOS,
  297.     # so don't mess with the default!
  298.     _tryorder = ["internet-config"]
  299.     register("internet-config", InternetConfig)
  300.  
  301. #
  302. # Platform support for OS/2
  303. #
  304.  
  305. if sys.platform[:3] == "os2" and _iscommand("netscape.exe"):
  306.     _tryorder = ["os2netscape"]
  307.     register("os2netscape", None,
  308.              GenericBrowser("start netscape.exe %s"))
  309.  
  310. # OK, now that we know what the default preference orders for each
  311. # platform are, allow user to override them with the BROWSER variable.
  312. #
  313. if os.environ.has_key("BROWSER"):
  314.     # It's the user's responsibility to register handlers for any unknown
  315.     # browser referenced by this value, before calling open().
  316.     _tryorder = os.environ["BROWSER"].split(os.pathsep)
  317.  
  318. for cmd in _tryorder:
  319.     if not _browsers.has_key(cmd.lower()):
  320.         if _iscommand(cmd.lower()):
  321.             register(cmd.lower(), None, GenericBrowser("%s %%s" % cmd.lower()))
  322.  
  323. _tryorder = filter(lambda x: _browsers.has_key(x.lower())
  324.                    or x.find("%s") > -1, _tryorder)
  325. # what to do if _tryorder is now empty?
  326.