diff options
| author | Thierry Carrez <thierry@openstack.org> | 2012-02-07 16:37:34 +0100 |
|---|---|---|
| committer | Thierry Carrez <thierry@openstack.org> | 2012-02-07 21:14:31 +0100 |
| commit | 71410724cd1516608ee58c37077bf9080da38de2 (patch) | |
| tree | 89d1b1c2930cef01565da8e5436ad8c2edb8a1bf | |
| parent | ae377f42e95b18438f4855c13230e077a870c64b (diff) | |
| download | nova-71410724cd1516608ee58c37077bf9080da38de2.tar.gz nova-71410724cd1516608ee58c37077bf9080da38de2.tar.xz nova-71410724cd1516608ee58c37077bf9080da38de2.zip | |
Remove ajaxterm from Nova
Removes copy of ajaxterm code, nova-ajax-console-proxy,
and support for get_ajax_console from Nova proper.
Implements blueprint remove-ajaxterm
Fixes bug 917963
Change-Id: I2c0ff427c53c0f63a18b10475d6b4cbe9a085d83
36 files changed, 3 insertions, 3873 deletions
diff --git a/bin/nova-ajax-console-proxy b/bin/nova-ajax-console-proxy deleted file mode 100755 index 94d93cd0e..000000000 --- a/bin/nova-ajax-console-proxy +++ /dev/null @@ -1,146 +0,0 @@ -#!/usr/bin/env python -# pylint: disable=C0103 -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Ajax Console Proxy Server""" - -from eventlet import greenthread -from eventlet.green import urllib2 - -import exceptions -import os -import sys -import time -import urlparse - -# If ../nova/__init__.py exists, add ../ to Python search path, so that -# it will override what happens to be installed in /usr/(local/)lib/python... -possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), - os.pardir, - os.pardir)) -if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): - sys.path.insert(0, possible_topdir) - - -from nova import flags -from nova import log as logging -from nova.openstack.common import cfg -from nova import rpc -from nova import service -from nova import utils -from nova import wsgi - - -ajax_console_idle_timeout_opt = \ - cfg.IntOpt('ajax_console_idle_timeout', - default=300, - help='Seconds before idle connection destroyed') - -FLAGS = flags.FLAGS -FLAGS.add_option(ajax_console_idle_timeout_opt) - -LOG = logging.getLogger('nova.ajax_console_proxy') - - -class AjaxConsoleProxy(object): - tokens = {} - - def __call__(self, env, start_response): - try: - if 'QUERY_STRING' in env: - req_url = '%s://%s%s?%s' % (env['wsgi.url_scheme'], - env['HTTP_HOST'], - env['PATH_INFO'], - env['QUERY_STRING']) - else: - req_url = '%s://%s%s' % (env['wsgi.url_scheme'], - env['HTTP_HOST'], - env['PATH_INFO']) - - if 'HTTP_REFERER' in env: - auth_url = env['HTTP_REFERER'] - else: - auth_url = req_url - - auth_params = urlparse.parse_qs(urlparse.urlparse(auth_url).query) - parsed_url = urlparse.urlparse(req_url) - - auth_info = AjaxConsoleProxy.tokens[auth_params['token'][0]] - args = auth_info['args'] - auth_info['last_activity'] = time.time() - - remote_url = ("http://%s:%s%s?token=%s" % ( - str(args['host']), - str(args['port']), - parsed_url.path, - str(args['token']))) - - opener = urllib2.urlopen(remote_url, env['wsgi.input'].read()) - body = opener.read() - info = opener.info() - - start_response("200 OK", info.dict.items()) - return body - except (exceptions.KeyError): - if env['PATH_INFO'] != '/favicon.ico': - LOG.audit("Unauthorized request %s, %s" - % (req_url, str(env))) - start_response("401 NOT AUTHORIZED", []) - return "Not Authorized" - except Exception, exc: - LOG.exception(exc) - start_response("500 ERROR", []) - return "Server Error" - - def register_listeners(self): - class TopicProxy(): - @staticmethod - def authorize_ajax_console(context, **kwargs): - AjaxConsoleProxy.tokens[kwargs['token']] = \ - {'args': kwargs, 'last_activity': time.time()} - - self.conn = rpc.create_connection(new=True) - self.conn.create_consumer( - FLAGS.ajax_console_proxy_topic, - TopicProxy) - - def delete_expired_tokens(): - now = time.time() - to_delete = [] - for k, v in AjaxConsoleProxy.tokens.items(): - if now - v['last_activity'] > FLAGS.ajax_console_idle_timeout: - to_delete.append(k) - - for k in to_delete: - del AjaxConsoleProxy.tokens[k] - - self.conn.consume_in_thread() - utils.LoopingCall(delete_expired_tokens).start(1) - -if __name__ == '__main__': - utils.default_flagfile() - FLAGS(sys.argv) - logging.setup() - acp_port = FLAGS.ajax_console_proxy_port - acp = AjaxConsoleProxy() - acp.register_listeners() - server = wsgi.Server("AJAX Console Proxy", acp, port=acp_port) - service.serve(server) - service.wait() - acp.conn.close() diff --git a/nova/compute/api.py b/nova/compute/api.py index f54d184dd..2f4d3e200 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -1574,19 +1574,6 @@ class API(base.Base): instance, params=params) @wrap_check_policy - def get_ajax_console(self, context, instance): - """Get a url to an AJAX Console.""" - output = self._call_compute_message('get_ajax_console', - context, - instance) - rpc.cast(context, '%s' % FLAGS.ajax_console_proxy_topic, - {'method': 'authorize_ajax_console', - 'args': {'token': output['token'], 'host': output['host'], - 'port': output['port']}}) - return {'url': '%s/?token=%s' % (FLAGS.ajax_console_proxy_url, - output['token'])} - - @wrap_check_policy def get_vnc_console(self, context, instance, console_type): """Get a url to an instance Console.""" connect_info = self._call_compute_message('get_vnc_console', diff --git a/nova/compute/manager.py b/nova/compute/manager.py index e5600af32..c7cd1baaf 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -1553,15 +1553,6 @@ class ComputeManager(manager.SchedulerDependentManager): @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) @wrap_instance_fault - def get_ajax_console(self, context, instance_uuid): - """Return connection information for an ajax console.""" - context = context.elevated() - LOG.debug(_("instance %s: getting ajax console"), instance_uuid) - instance_ref = self.db.instance_get_by_uuid(context, instance_uuid) - return self.driver.get_ajax_console(instance_ref) - - @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) - @wrap_instance_fault def get_vnc_console(self, context, instance_uuid, console_type): """Return connection information for a vnc console.""" context = context.elevated() diff --git a/nova/flags.py b/nova/flags.py index 3f3560057..a76a2328c 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -250,15 +250,6 @@ global_opts = [ cfg.StrOpt('network_topic', default='network', help='the topic network nodes listen on'), - cfg.StrOpt('ajax_console_proxy_topic', - default='ajax_proxy', - help='the topic ajax proxy nodes listen on'), - cfg.StrOpt('ajax_console_proxy_url', - default='http://127.0.0.1:8000', - help='URL of ajax console proxy, in the form http://host:port'), - cfg.IntOpt('ajax_console_proxy_port', - default=8000, - help='port that ajax_console_proxy binds'), cfg.StrOpt('vsa_topic', default='vsa', help='the topic that nova-vsa service listens on'), diff --git a/nova/tests/fake_libvirt_utils.py b/nova/tests/fake_libvirt_utils.py index 27b753a64..726e28a1d 100644 --- a/nova/tests/fake_libvirt_utils.py +++ b/nova/tests/fake_libvirt_utils.py @@ -93,10 +93,6 @@ def get_open_port(start_port, end_port): return int((start_port + end_port) / 2) -def run_ajaxterm(cmd, token, port): - pass - - def get_fs_info(path): return {'total': 128 * (1024 ** 3), 'used': 44 * (1024 ** 3), diff --git a/nova/tests/policy.json b/nova/tests/policy.json index 4175d758e..99a97bba5 100644 --- a/nova/tests/policy.json +++ b/nova/tests/policy.json @@ -20,7 +20,6 @@ "compute:lock": [], "compute:unlock": [], - "compute:get_ajax_console": [], "compute:get_vnc_console": [], "compute:get_console_output": [], diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index d99aed310..221f86fe7 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -724,16 +724,6 @@ class ComputeTestCase(BaseTestCase): self.assertEqual(output, 'ANOTHER\nLAST LINE') self.compute.terminate_instance(self.context, instance['uuid']) - def test_ajax_console(self): - """Make sure we can get console output from instance""" - instance = self._create_fake_instance() - self.compute.run_instance(self.context, instance['uuid']) - - console = self.compute.get_ajax_console(self.context, - instance['uuid']) - self.assert_(set(['token', 'host', 'port']).issubset(console.keys())) - self.compute.terminate_instance(self.context, instance['uuid']) - def test_novnc_vnc_console(self): """Make sure we can a vnc console for an instance.""" instance = self._create_fake_instance() @@ -2990,17 +2980,6 @@ class ComputeAPITestCase(BaseTestCase): 'novnc') self.compute_api.delete(self.context, instance) - def test_ajax_console(self): - """Make sure we can an ajax console for an instance.""" - def ajax_rpc_call_wrapper(*args, **kwargs): - return {'token': 'asdf', 'host': '0.0.0.0', 'port': 8080} - - self.stubs.Set(rpc, 'call', ajax_rpc_call_wrapper) - - instance = self._create_fake_instance() - console = self.compute_api.get_ajax_console(self.context, instance) - self.compute_api.delete(self.context, instance) - def test_console_output(self): instance = self._create_fake_instance() console = self.compute_api.get_console_output(self.context, instance) diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index 72c9f8802..61d920ff1 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -1925,20 +1925,6 @@ disk size: 4.4M''', '')) finally: os.unlink(dst_path) - def test_run_ajaxterm(self): - self.mox.StubOutWithMock(utils, 'execute') - token = 's3cr3tt0ken' - shell_cmd = 'shell-cmd.py' - port = 2048 - utils.execute(mox.IgnoreArg(), - '--command', shell_cmd, - '-t', token, - '-p', port) - - # Start test - self.mox.ReplayAll() - libvirt_utils.run_ajaxterm(shell_cmd, token, port) - def test_get_fs_info(self): # Use a 1024-byte block size (df -k) because OS X does not support # the -B flag diff --git a/nova/tests/test_virt_drivers.py b/nova/tests/test_virt_drivers.py index 2cda7a61e..43dde697d 100644 --- a/nova/tests/test_virt_drivers.py +++ b/nova/tests/test_virt_drivers.py @@ -289,14 +289,6 @@ class _VirtDriverTestCase(test.TestCase): self.assertTrue(isinstance(console_output, basestring)) @catch_notimplementederror - def test_get_ajax_console(self): - instance_ref, network_info = self._get_running_instance() - ajax_console = self.connection.get_ajax_console(instance_ref) - self.assertIn('token', ajax_console) - self.assertIn('host', ajax_console) - self.assertIn('port', ajax_console) - - @catch_notimplementederror def test_get_vnc_console(self): instance_ref, network_info = self._get_running_instance() vnc_console = self.connection.get_vnc_console(instance_ref) diff --git a/nova/tests/test_vmwareapi.py b/nova/tests/test_vmwareapi.py index a1d0af440..bf8bcbe2f 100644 --- a/nova/tests/test_vmwareapi.py +++ b/nova/tests/test_vmwareapi.py @@ -259,6 +259,3 @@ class VMWareAPIVMTestCase(test.TestCase): def test_get_console_output(self): pass - - def test_get_ajax_console(self): - pass diff --git a/nova/virt/driver.py b/nova/virt/driver.py index d87a7286a..34ce9613b 100644 --- a/nova/virt/driver.py +++ b/nova/virt/driver.py @@ -191,10 +191,6 @@ class ComputeDriver(object): # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() - def get_ajax_console(self, instance): - # TODO(Vek): Need to pass context in for access to auth_token - raise NotImplementedError() - def get_vnc_console(self, instance): # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() diff --git a/nova/virt/fake.py b/nova/virt/fake.py index ff8da4724..ef3d392ba 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -219,11 +219,6 @@ class FakeConnection(driver.ComputeDriver): def get_console_output(self, instance): return 'FAKE CONSOLE OUTPUT\nANOTHER\nLAST LINE' - def get_ajax_console(self, instance): - return {'token': 'FAKETOKEN', - 'host': 'fakeajaxconsole.com', - 'port': 6969} - def get_vnc_console(self, instance): return {'internal_access_path': 'FAKE', 'host': 'fakevncconsole.com', diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index 37ad07736..ac3d1fb41 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -102,9 +102,6 @@ libvirt_opts = [ cfg.BoolOpt('use_cow_images', default=True, help='Whether to use cow images'), - cfg.StrOpt('ajaxterm_portrange', - default='10000-12000', - help='Range of ports that ajaxterm should try to bind'), cfg.StrOpt('cpuinfo_xml_template', default=utils.abspath('virt/cpuinfo.xml.template'), help='CpuInfo XML Template (Used only live migration now)'), @@ -777,29 +774,6 @@ class LibvirtConnection(driver.ComputeDriver): return libvirt_utils.load_file(fpath) - @exception.wrap_exception() - def get_ajax_console(self, instance): - def get_pty_for_instance(instance_name): - virt_dom = self._lookup_by_name(instance_name) - xml = virt_dom.XMLDesc(0) - dom = minidom.parseString(xml) - - for serial in dom.getElementsByTagName('serial'): - if serial.getAttribute('type') == 'pty': - source = serial.getElementsByTagName('source')[0] - return source.getAttribute('path') - - start_port, end_port = FLAGS.ajaxterm_portrange.split("-") - port = libvirt_utils.get_open_port(int(start_port), int(end_port)) - token = str(uuid.uuid4()) - host = instance['host'] - - ajaxterm_cmd = 'sudo netcat - %s' \ - % get_pty_for_instance(instance['name']) - - libvirt_utils.run_ajaxterm(ajaxterm_cmd, token, port) - return {'token': token, 'host': host, 'port': port} - @staticmethod def get_host_ip_addr(): return FLAGS.my_ip diff --git a/nova/virt/libvirt/utils.py b/nova/virt/libvirt/utils.py index f96128124..7b0e85f38 100644 --- a/nova/virt/libvirt/utils.py +++ b/nova/virt/libvirt/utils.py @@ -237,18 +237,6 @@ def get_open_port(start_port, end_port): raise Exception(_('Unable to find an open port')) -def run_ajaxterm(cmd, token, port): - """Run ajaxterm - - :param cmd: Command to connect to - :param token: Token to require for authentication - :param port: Port to run on - """ - cmd = ['%s/tools/ajaxterm/ajaxterm.py' % utils.novadir(), - '--command', cmd, '-t', token, '-p', port] - execute(*cmd) - - def get_fs_info(path): """Get free/used/total space info for a filesystem diff --git a/nova/virt/vmwareapi/vmops.py b/nova/virt/vmwareapi/vmops.py index af867dc6a..39d24479f 100644 --- a/nova/virt/vmwareapi/vmops.py +++ b/nova/virt/vmwareapi/vmops.py @@ -719,10 +719,6 @@ class VMWareVMOps(object): else: return "" - def get_ajax_console(self, instance): - """Return link to instance's ajax console.""" - return 'http://fakeajaxconsole/fake_url' - def _set_machine_id(self, client_factory, instance, network_info): """ Set the machine id of the VM for guest tools to pick up and reconfigure diff --git a/nova/virt/vmwareapi_conn.py b/nova/virt/vmwareapi_conn.py index 909969fac..7275b363e 100644 --- a/nova/virt/vmwareapi_conn.py +++ b/nova/virt/vmwareapi_conn.py @@ -174,10 +174,6 @@ class VMWareESXConnection(driver.ComputeDriver): """Return snapshot of console.""" return self._vmops.get_console_output(instance) - def get_ajax_console(self, instance): - """Return link to instance's ajax console.""" - return self._vmops.get_ajax_console(instance) - def get_volume_connector(self, _instance): """Return volume connector information""" # TODO(vish): When volume attaching is supported, return the diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index c2960b86a..30d77369b 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -1425,11 +1425,6 @@ class VMOps(object): # TODO: implement this to fix pylint! return 'FAKE CONSOLE OUTPUT of instance' - def get_ajax_console(self, instance): - """Return link to instance's ajax console.""" - # TODO: implement this! - return 'http://fakeajaxconsole/fake_url' - def get_vnc_console(self, instance): """Return connection info for a vnc console.""" vm_ref = self._get_vm_opaque_ref(instance) diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index e034cb3ca..bbbc62df7 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -337,12 +337,8 @@ class XenAPIConnection(driver.ComputeDriver): """Return snapshot of console""" return self._vmops.get_console_output(instance) - def get_ajax_console(self, instance): - """Return link to instance's ajax console""" - return self._vmops.get_ajax_console(instance) - def get_vnc_console(self, instance): - """Return link to instance's ajax console""" + """Return link to instance's VNC console""" return self._vmops.get_vnc_console(instance) def get_volume_connector(self, _instance): diff --git a/run_tests.sh b/run_tests.sh index e3cf69a36..070b89179 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -94,8 +94,7 @@ function run_pep8 { # Opt-out files from pep8 ignore_scripts="*.sh:*nova-debug:*clean-vlans" ignore_files="*eventlet-patch:*pip-requires" - ignore_dirs="*ajaxterm*" - GLOBIGNORE="$ignore_scripts:$ignore_files:$ignore_dirs" + GLOBIGNORE="$ignore_scripts:$ignore_files" srcfiles=`find bin -type f ! -name "nova.conf*"` srcfiles+=" `find tools/*`" srcfiles+=" nova setup.py plugins/xenserver/xenapi/etc/xapi.d/plugins/glance" @@ -124,8 +123,7 @@ function run_hacking { # Opt-out files from pep8 ignore_scripts="*.sh:*nova-debug:*clean-vlans:*.swp" ignore_files="*eventlet-patch:*pip-requires" - ignore_dirs="*ajaxterm*" - GLOBIGNORE="$ignore_scripts:$ignore_files:$ignore_dirs" + GLOBIGNORE="$ignore_scripts:$ignore_files" srcfiles=`find bin -type f ! -name "nova.conf*"` srcfiles+=" `find tools/*`" srcfiles+=" nova setup.py plugins/xenserver/xenapi/etc/xapi.d/plugins/glance" @@ -82,7 +82,6 @@ setup(name='nova', data_files=find_data_files('share/nova', 'tools'), scripts=['bin/clear_rabbit_queues', 'bin/instance-usage-audit', - 'bin/nova-ajax-console-proxy', 'bin/nova-all', 'bin/nova-api', 'bin/nova-api-ec2', diff --git a/tools/ajaxterm/README.txt b/tools/ajaxterm/README.txt deleted file mode 100644 index 4b0ae99af..000000000 --- a/tools/ajaxterm/README.txt +++ /dev/null @@ -1,120 +0,0 @@ -= [http://antony.lesuisse.org/qweb/trac/wiki/AjaxTerm Ajaxterm] =
-
-Ajaxterm is a web based terminal. It was totally inspired and works almost
-exactly like http://anyterm.org/ except it's much easier to install (see
-comparaison with anyterm below).
-
-Ajaxterm written in python (and some AJAX javascript for client side) and depends only on python2.3 or better.[[BR]]
-Ajaxterm is '''very simple to install''' on Linux, MacOS X, FreeBSD, Solaris, cygwin and any Unix that runs python2.3.[[BR]]
-Ajaxterm was written by Antony Lesuisse (email: al AT udev.org), License Public Domain.
-
-Use the [/qweb/forum/viewforum.php?id=2 Forum], if you have any question or remark.
-
-== News ==
-
- * 2006-10-29: v0.10 allow space in login, cgi launch fix, redhat init
- * 2006-07-12: v0.9 change uid, daemon fix (Daniel Fischer)
- * 2006-07-04: v0.8 add login support to ssh (Sven Geggus), change max width to 256
- * 2006-05-31: v0.7 minor fixes, daemon option
- * 2006-05-23: v0.6 Applied debian and gentoo patches, renamed to Ajaxterm, default port 8022
-
-== Download and Install ==
-
- * Release: [/qweb/files/Ajaxterm-0.10.tar.gz Ajaxterm-0.10.tar.gz]
- * Browse src: [/qweb/trac/browser/trunk/ajaxterm/ ajaxterm/]
-
-To install Ajaxterm issue the following commands:
-{{{
-wget http://antony.lesuisse.org/qweb/files/Ajaxterm-0.10.tar.gz
-tar zxvf Ajaxterm-0.10.tar.gz
-cd Ajaxterm-0.10
-./ajaxterm.py
-}}}
-Then point your browser to this URL : http://localhost:8022/
-
-== Screenshot ==
-
-{{{
-#!html
-<center><img src="/qweb/trac/attachment/wiki/AjaxTerm/scr.png?format=raw" alt="ajaxterm screenshot" style=""/></center>
-}}}
-
-== Documentation and Caveats ==
-
- * Ajaxterm only support latin1, if you use Ubuntu or any LANG==en_US.UTF-8 distribution don't forget to "unset LANG".
-
- * If run as root ajaxterm will run /bin/login, otherwise it will run ssh
- localhost. To use an other command use the -c option.
-
- * By default Ajaxterm only listen at 127.0.0.1:8022. For remote access, it is
- strongly recommended to use '''https SSL/TLS''', and that is simple to
- configure if you use the apache web server using mod_proxy.[[BR]][[BR]]
- Using ssl will also speed up ajaxterm (probably because of keepalive).[[BR]][[BR]]
- Here is an configuration example:
-
-{{{
- Listen 443
- NameVirtualHost *:443
-
- <VirtualHost *:443>
- ServerName localhost
- SSLEngine On
- SSLCertificateKeyFile ssl/apache.pem
- SSLCertificateFile ssl/apache.pem
-
- ProxyRequests Off
- <Proxy *>
- Order deny,allow
- Allow from all
- </Proxy>
- ProxyPass /ajaxterm/ http://localhost:8022/
- ProxyPassReverse /ajaxterm/ http://localhost:8022/
- </VirtualHost>
-}}}
-
- * Using GET HTTP request seems to speed up ajaxterm, just click on GET in the
- interface, but be warned that your keystrokes might be loggued (by apache or
- any proxy). I usually enable it after the login.
-
- * Ajaxterm commandline usage:
-
-{{{
-usage: ajaxterm.py [options]
-
-options:
- -h, --help show this help message and exit
- -pPORT, --port=PORT Set the TCP port (default: 8022)
- -cCMD, --command=CMD set the command (default: /bin/login or ssh localhost)
- -l, --log log requests to stderr (default: quiet mode)
- -d, --daemon run as daemon in the background
- -PPIDFILE, --pidfile=PIDFILE
- set the pidfile (default: /var/run/ajaxterm.pid)
- -iINDEX_FILE, --index=INDEX_FILE
- default index file (default: ajaxterm.html)
- -uUID, --uid=UID Set the daemon's user id
-}}}
-
- * Ajaxterm was first written as a demo for qweb (my web framework), but
- actually doesn't use many features of qweb.
-
- * Compared to anyterm:
- * There are no partial updates, ajaxterm updates either all the screen or
- nothing. That make the code simpler and I also think it's faster. HTTP
- replies are always gzencoded. When used in 80x25 mode, almost all of
- them are below the 1500 bytes (size of an ethernet frame) and we just
- replace the screen with the reply (no javascript string handling).
- * Ajaxterm polls the server for updates with an exponentially growing
- timeout when the screen hasn't changed. The timeout is also resetted as
- soon as a key is pressed. Anyterm blocks on a pending request and use a
- parallel connection for keypresses. The anyterm approch is better
- when there aren't any keypress.
-
- * Ajaxterm files are released in the Public Domain, (except [http://sarissa.sourceforge.net/doc/ sarissa*] which are LGPL).
-
-== TODO ==
-
- * insert mode ESC [ 4 h
- * change size x,y from gui (sending signal)
- * vt102 graphic codepage
- * use innerHTML or prototype instead of sarissa
-
diff --git a/tools/ajaxterm/ajaxterm.1 b/tools/ajaxterm/ajaxterm.1 deleted file mode 100644 index 46f2acb33..000000000 --- a/tools/ajaxterm/ajaxterm.1 +++ /dev/null @@ -1,35 +0,0 @@ -.TH ajaxterm "1" "May 2006" "ajaxterm 0.5" "User commands" -.SH NAME -ajaxterm \- Web based terminal written in python - -.SH DESCRITPION -\fBajaxterm\fR is a web based terminal written in python and some AJAX -javascript for client side. -It can use almost any web browser and even works through firewalls. - -.SH USAGE -\fBajaxterm.py\fR [options] - -.SH OPTIONS -A summary of the options supported by \fBajaxterm\fR is included below. - \fB-h, --help\fR show this help message and exit - \fB-pPORT, --port=PORT\fR Set the TCP port (default: 8022) - \fB-cCMD, --command=CMD\fR set the command (default: /bin/login or ssh localhost) - \fB-l, --log\fR log requests to stderr (default: quiet mode) - -.SH AUTHOR -Antony Lesuisse <al@udev.org> - -This manual page was written for the Debian system by -Julien Valroff <julien@kirya.net> (but may be used by others). - -.SH "REPORTING BUGS" -Report any bugs to the author: Antony Lesuisse <al@udev.org> - -.SH COPYRIGHT -Copyright Antony Lesuisse <al@udev.org> - -.SH SEE ALSO -- \fBajaxterm\fR wiki page: http://antony.lesuisse.org/qweb/trac/wiki/AjaxTerm -.br -- \fBajaxterm\fR forum: http://antony.lesuisse.org/qweb/forum/viewforum.php?id=2 diff --git a/tools/ajaxterm/ajaxterm.css b/tools/ajaxterm/ajaxterm.css deleted file mode 100644 index b9a5f8771..000000000 --- a/tools/ajaxterm/ajaxterm.css +++ /dev/null @@ -1,64 +0,0 @@ -pre.stat { - margin: 0px; - padding: 4px; - display: block; - font-family: monospace; - white-space: pre; - background-color: black; - border-top: 1px solid black; - color: white; -} -pre.stat span { - padding: 0px; -} -pre.stat .on { - background-color: #080; - font-weight: bold; - color: white; - cursor: pointer; -} -pre.stat .off { - background-color: #888; - font-weight: bold; - color: white; - cursor: pointer; -} -pre.term { - margin: 0px; - padding: 4px; - display: block; - font-family: monospace; - white-space: pre; - background-color: black; - border-top: 1px solid white; - color: #eee; -} -pre.term span.f0 { color: #000; } -pre.term span.f1 { color: #b00; } -pre.term span.f2 { color: #0b0; } -pre.term span.f3 { color: #bb0; } -pre.term span.f4 { color: #00b; } -pre.term span.f5 { color: #b0b; } -pre.term span.f6 { color: #0bb; } -pre.term span.f7 { color: #bbb; } -pre.term span.f8 { color: #666; } -pre.term span.f9 { color: #f00; } -pre.term span.f10 { color: #0f0; } -pre.term span.f11 { color: #ff0; } -pre.term span.f12 { color: #00f; } -pre.term span.f13 { color: #f0f; } -pre.term span.f14 { color: #0ff; } -pre.term span.f15 { color: #fff; } -pre.term span.b0 { background-color: #000; } -pre.term span.b1 { background-color: #b00; } -pre.term span.b2 { background-color: #0b0; } -pre.term span.b3 { background-color: #bb0; } -pre.term span.b4 { background-color: #00b; } -pre.term span.b5 { background-color: #b0b; } -pre.term span.b6 { background-color: #0bb; } -pre.term span.b7 { background-color: #bbb; } - -body { background-color: #888; } -#term { - float: left; -} diff --git a/tools/ajaxterm/ajaxterm.html b/tools/ajaxterm/ajaxterm.html deleted file mode 100644 index 7fdef5e94..000000000 --- a/tools/ajaxterm/ajaxterm.html +++ /dev/null @@ -1,25 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> -<html> -<head> - <title>Ajaxterm</title> - <meta http-equiv="content-type" content="text/html; charset=UTF-8"/> - <link rel="stylesheet" type="text/css" href="ajaxterm.css"/> - <script type="text/javascript" src="sarissa.js"></script> - <script type="text/javascript" src="sarissa_dhtml.js"></script> - <script type="text/javascript" src="ajaxterm.js"></script> - <script type="text/javascript"> - /* - ajaxterm.py creates a random session_id to demultiplex multiple connections, - and to add a layer of security - in its shipping form, ajaxterm accepted any session_id - and was susceptible to an easy exploit - */ - SESSION_ID = '$session_id'; - window.onload=function() { - t=ajaxterm.Terminal("term",80,25); - }; - </script> -</head> -<body> -<div id="term"></div> -</body> -</html> diff --git a/tools/ajaxterm/ajaxterm.js b/tools/ajaxterm/ajaxterm.js deleted file mode 100644 index 32b401930..000000000 --- a/tools/ajaxterm/ajaxterm.js +++ /dev/null @@ -1,279 +0,0 @@ -ajaxterm={}; -ajaxterm.Terminal_ctor=function(id,width,height) { - var ie=0; - if(window.ActiveXObject) - ie=1; - var sid=""+SESSION_ID; - var query0="s="+sid+"&w="+width+"&h="+height; - var query1=query0+"&c=1&k="; - var buf=""; - var timeout; - var error_timeout; - var keybuf=[]; - var sending=0; - var rmax=1; - - var div=document.getElementById(id); - var dstat=document.createElement('pre'); - var sled=document.createElement('span'); - var opt_get=document.createElement('a'); - var opt_color=document.createElement('a'); - var opt_paste=document.createElement('a'); - var sdebug=document.createElement('span'); - var dterm=document.createElement('div'); - - function debug(s) { - sdebug.innerHTML=s; - } - function error() { - sled.className='off'; - debug("Connection lost timeout ts:"+((new Date).getTime())); - } - function opt_add(opt,name) { - opt.className='off'; - opt.innerHTML=' '+name+' '; - dstat.appendChild(opt); - dstat.appendChild(document.createTextNode(' ')); - } - function do_get(event) { - opt_get.className=(opt_get.className=='off')?'on':'off'; - debug('GET '+opt_get.className); - } - function do_color(event) { - var o=opt_color.className=(opt_color.className=='off')?'on':'off'; - if(o=='on') - query1=query0+"&c=1&k="; - else - query1=query0+"&k="; - debug('Color '+opt_color.className); - } - function mozilla_clipboard() { - // mozilla sucks - try { - netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); - } catch (err) { - debug('Access denied, <a href="http://kb.mozillazine.org/Granting_JavaScript_access_to_the_clipboard" target="_blank">more info</a>'); - return undefined; - } - var clip = Components.classes["@mozilla.org/widget/clipboard;1"].createInstance(Components.interfaces.nsIClipboard); - var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable); - if (!clip || !trans) { - return undefined; - } - trans.addDataFlavor("text/unicode"); - clip.getData(trans,clip.kGlobalClipboard); - var str=new Object(); - var strLength=new Object(); - try { - trans.getTransferData("text/unicode",str,strLength); - } catch(err) { - return ""; - } - if (str) { - str=str.value.QueryInterface(Components.interfaces.nsISupportsString); - } - if (str) { - return str.data.substring(0,strLength.value / 2); - } else { - return ""; - } - } - function do_paste(event) { - var p=undefined; - if (window.clipboardData) { - p=window.clipboardData.getData("Text"); - } else if(window.netscape) { - p=mozilla_clipboard(); - } - if (p) { - debug('Pasted'); - queue(encodeURIComponent(p)); - } else { - } - } - function update() { -// debug("ts: "+((new Date).getTime())+" rmax:"+rmax); - if(sending==0) { - sending=1; - sled.className='on'; - var r=new XMLHttpRequest(); - var send=""; - while(keybuf.length>0) { - send+=keybuf.pop(); - } - var query=query1+send; - if(opt_get.className=='on') { - r.open("GET","u?"+query,true); - if(ie) { - r.setRequestHeader("If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT"); - } - } else { - r.open("POST","u",true); - } - r.setRequestHeader('Content-Type','application/x-www-form-urlencoded'); - r.onreadystatechange = function () { -// debug("xhr:"+((new Date).getTime())+" state:"+r.readyState+" status:"+r.status+" statusText:"+r.statusText); - if (r.readyState==4) { - if(r.status==200) { - window.clearTimeout(error_timeout); - de=r.responseXML.documentElement; - if(de.tagName=="pre") { - if(ie) { - Sarissa.updateContentFromNode(de, dterm); - } else { - Sarissa.updateContentFromNode(de, dterm); -// old=div.firstChild; -// div.replaceChild(de,old); - } - rmax=100; - } else { - rmax*=2; - if(rmax>2000) - rmax=2000; - } - sending=0; - sled.className='off'; - timeout=window.setTimeout(update,rmax); - } else { - debug("Connection error status:"+r.status); - } - } - } - error_timeout=window.setTimeout(error,5000); - if(opt_get.className=='on') { - r.send(null); - } else { - r.send(query); - } - } - } - function queue(s) { - keybuf.unshift(s); - if(sending==0) { - window.clearTimeout(timeout); - timeout=window.setTimeout(update,1); - } - } - function keypress(ev) { - if (!ev) var ev=window.event; -// s="kp keyCode="+ev.keyCode+" which="+ev.which+" shiftKey="+ev.shiftKey+" ctrlKey="+ev.ctrlKey+" altKey="+ev.altKey; -// debug(s); -// return false; -// else { if (!ev.ctrlKey || ev.keyCode==17) { return; } - var kc; - var k=""; - if (ev.keyCode) - kc=ev.keyCode; - if (ev.which) - kc=ev.which; - if (ev.altKey) { - if (kc>=65 && kc<=90) - kc+=32; - if (kc>=97 && kc<=122) { - k=String.fromCharCode(27)+String.fromCharCode(kc); - } - } else if (ev.ctrlKey) { - if (kc>=65 && kc<=90) k=String.fromCharCode(kc-64); // Ctrl-A..Z - else if (kc>=97 && kc<=122) k=String.fromCharCode(kc-96); // Ctrl-A..Z - else if (kc==54) k=String.fromCharCode(30); // Ctrl-^ - else if (kc==109) k=String.fromCharCode(31); // Ctrl-_ - else if (kc==219) k=String.fromCharCode(27); // Ctrl-[ - else if (kc==220) k=String.fromCharCode(28); // Ctrl-\ - else if (kc==221) k=String.fromCharCode(29); // Ctrl-] - else if (kc==219) k=String.fromCharCode(29); // Ctrl-] - else if (kc==219) k=String.fromCharCode(0); // Ctrl-@ - } else if (ev.which==0) { - if (kc==9) k=String.fromCharCode(9); // Tab - else if (kc==8) k=String.fromCharCode(127); // Backspace - else if (kc==27) k=String.fromCharCode(27); // Escape - else { - if (kc==33) k="[5~"; // PgUp - else if (kc==34) k="[6~"; // PgDn - else if (kc==35) k="[4~"; // End - else if (kc==36) k="[1~"; // Home - else if (kc==37) k="[D"; // Left - else if (kc==38) k="[A"; // Up - else if (kc==39) k="[C"; // Right - else if (kc==40) k="[B"; // Down - else if (kc==45) k="[2~"; // Ins - else if (kc==46) k="[3~"; // Del - else if (kc==112) k="[[A"; // F1 - else if (kc==113) k="[[B"; // F2 - else if (kc==114) k="[[C"; // F3 - else if (kc==115) k="[[D"; // F4 - else if (kc==116) k="[[E"; // F5 - else if (kc==117) k="[17~"; // F6 - else if (kc==118) k="[18~"; // F7 - else if (kc==119) k="[19~"; // F8 - else if (kc==120) k="[20~"; // F9 - else if (kc==121) k="[21~"; // F10 - else if (kc==122) k="[23~"; // F11 - else if (kc==123) k="[24~"; // F12 - if (k.length) { - k=String.fromCharCode(27)+k; - } - } - } else { - if (kc==8) - k=String.fromCharCode(127); // Backspace - else - k=String.fromCharCode(kc); - } - if(k.length) { -// queue(encodeURIComponent(k)); - if(k=="+") { - queue("%2B"); - } else { - queue(escape(k)); - } - } - ev.cancelBubble=true; - if (ev.stopPropagation) ev.stopPropagation(); - if (ev.preventDefault) ev.preventDefault(); - return false; - } - function keydown(ev) { - if (!ev) var ev=window.event; - if (ie) { -// s="kd keyCode="+ev.keyCode+" which="+ev.which+" shiftKey="+ev.shiftKey+" ctrlKey="+ev.ctrlKey+" altKey="+ev.altKey; -// debug(s); - o={9:1,8:1,27:1,33:1,34:1,35:1,36:1,37:1,38:1,39:1,40:1,45:1,46:1,112:1, - 113:1,114:1,115:1,116:1,117:1,118:1,119:1,120:1,121:1,122:1,123:1}; - if (o[ev.keyCode] || ev.ctrlKey || ev.altKey) { - ev.which=0; - return keypress(ev); - } - } - } - function init() { - sled.appendChild(document.createTextNode('\xb7')); - sled.className='off'; - dstat.appendChild(sled); - dstat.appendChild(document.createTextNode(' ')); - opt_add(opt_color,'Colors'); - opt_color.className='on'; - opt_add(opt_get,'GET'); - opt_add(opt_paste,'Paste'); - dstat.appendChild(sdebug); - dstat.className='stat'; - div.appendChild(dstat); - div.appendChild(dterm); - if(opt_color.addEventListener) { - opt_get.addEventListener('click',do_get,true); - opt_color.addEventListener('click',do_color,true); - opt_paste.addEventListener('click',do_paste,true); - } else { - opt_get.attachEvent("onclick", do_get); - opt_color.attachEvent("onclick", do_color); - opt_paste.attachEvent("onclick", do_paste); - } - document.onkeypress=keypress; - document.onkeydown=keydown; - timeout=window.setTimeout(update,100); - } - init(); -} -ajaxterm.Terminal=function(id,width,height) { - return new this.Terminal_ctor(id,width,height); -} - diff --git a/tools/ajaxterm/ajaxterm.py b/tools/ajaxterm/ajaxterm.py deleted file mode 100755 index bf27b264a..000000000 --- a/tools/ajaxterm/ajaxterm.py +++ /dev/null @@ -1,586 +0,0 @@ -#!/usr/bin/env python - -""" Ajaxterm """ - -import array,cgi,fcntl,glob,mimetypes,optparse,os,pty,random,re,signal,select,sys,threading,time,termios,struct,pwd - -os.chdir(os.path.normpath(os.path.dirname(__file__))) -# Optional: Add QWeb in sys path -sys.path[0:0]=glob.glob('../../python') - -import qweb -import string, subprocess, uuid - -global g_server -TIMEOUT=300 - -class Terminal: - def __init__(self,width=80,height=24): - self.width=width - self.height=height - self.init() - self.reset() - def init(self): - self.esc_seq={ - "\x00": None, - "\x05": self.esc_da, - "\x07": None, - "\x08": self.esc_0x08, - "\x09": self.esc_0x09, - "\x0a": self.esc_0x0a, - "\x0b": self.esc_0x0a, - "\x0c": self.esc_0x0a, - "\x0d": self.esc_0x0d, - "\x0e": None, - "\x0f": None, - "\x1b#8": None, - "\x1b=": None, - "\x1b>": None, - "\x1b(0": None, - "\x1b(A": None, - "\x1b(B": None, - "\x1b[c": self.esc_da, - "\x1b[0c": self.esc_da, - "\x1b]R": None, - "\x1b7": self.esc_save, - "\x1b8": self.esc_restore, - "\x1bD": None, - "\x1bE": None, - "\x1bH": None, - "\x1bM": self.esc_ri, - "\x1bN": None, - "\x1bO": None, - "\x1bZ": self.esc_da, - "\x1ba": None, - "\x1bc": self.reset, - "\x1bn": None, - "\x1bo": None, - } - for k,v in self.esc_seq.items(): - if v==None: - self.esc_seq[k]=self.esc_ignore - # regex - d={ - r'\[\??([0-9;]*)([@ABCDEFGHJKLMPXacdefghlmnqrstu`])' : self.csi_dispatch, - r'\]([^\x07]+)\x07' : self.esc_ignore, - } - self.esc_re=[] - for k,v in d.items(): - self.esc_re.append((re.compile('\x1b'+k),v)) - # define csi sequences - self.csi_seq={ - '@': (self.csi_at,[1]), - '`': (self.csi_G,[1]), - 'J': (self.csi_J,[0]), - 'K': (self.csi_K,[0]), - } - for i in [i[4] for i in dir(self) if i.startswith('csi_') and len(i)==5]: - if not self.csi_seq.has_key(i): - self.csi_seq[i]=(getattr(self,'csi_'+i),[1]) - # Init 0-256 to latin1 and html translation table - self.trl1="" - for i in range(256): - if i<32: - self.trl1+=" " - elif i<127 or i>160: - self.trl1+=chr(i) - else: - self.trl1+="?" - self.trhtml="" - for i in range(256): - if i==0x0a or (i>32 and i<127) or i>160: - self.trhtml+=chr(i) - elif i<=32: - self.trhtml+="\xa0" - else: - self.trhtml+="?" - def reset(self,s=""): - self.scr=array.array('i',[0x000700]*(self.width*self.height)) - self.st=0 - self.sb=self.height-1 - self.cx_bak=self.cx=0 - self.cy_bak=self.cy=0 - self.cl=0 - self.sgr=0x000700 - self.buf="" - self.outbuf="" - self.last_html="" - def peek(self,y1,x1,y2,x2): - return self.scr[self.width*y1+x1:self.width*y2+x2] - def poke(self,y,x,s): - pos=self.width*y+x - self.scr[pos:pos+len(s)]=s - def zero(self,y1,x1,y2,x2): - w=self.width*(y2-y1)+x2-x1+1 - z=array.array('i',[0x000700]*w) - self.scr[self.width*y1+x1:self.width*y2+x2+1]=z - def scroll_up(self,y1,y2): - self.poke(y1,0,self.peek(y1+1,0,y2,self.width)) - self.zero(y2,0,y2,self.width-1) - def scroll_down(self,y1,y2): - self.poke(y1+1,0,self.peek(y1,0,y2-1,self.width)) - self.zero(y1,0,y1,self.width-1) - def scroll_right(self,y,x): - self.poke(y,x+1,self.peek(y,x,y,self.width)) - self.zero(y,x,y,x) - def cursor_down(self): - if self.cy>=self.st and self.cy<=self.sb: - self.cl=0 - q,r=divmod(self.cy+1,self.sb+1) - if q: - self.scroll_up(self.st,self.sb) - self.cy=self.sb - else: - self.cy=r - def cursor_right(self): - q,r=divmod(self.cx+1,self.width) - if q: - self.cl=1 - else: - self.cx=r - def echo(self,c): - if self.cl: - self.cursor_down() - self.cx=0 - self.scr[(self.cy*self.width)+self.cx]=self.sgr|ord(c) - self.cursor_right() - def esc_0x08(self,s): - self.cx=max(0,self.cx-1) - def esc_0x09(self,s): - x=self.cx+8 - q,r=divmod(x,8) - self.cx=(q*8)%self.width - def esc_0x0a(self,s): - self.cursor_down() - def esc_0x0d(self,s): - self.cl=0 - self.cx=0 - def esc_save(self,s): - self.cx_bak=self.cx - self.cy_bak=self.cy - def esc_restore(self,s): - self.cx=self.cx_bak - self.cy=self.cy_bak - self.cl=0 - def esc_da(self,s): - self.outbuf="\x1b[?6c" - def esc_ri(self,s): - self.cy=max(self.st,self.cy-1) - if self.cy==self.st: - self.scroll_down(self.st,self.sb) - def esc_ignore(self,*s): - pass -# print "term:ignore: %s"%repr(s) - def csi_dispatch(self,seq,mo): - # CSI sequences - s=mo.group(1) - c=mo.group(2) - f=self.csi_seq.get(c,None) - if f: - try: - l=[min(int(i),1024) for i in s.split(';') if len(i)<4] - except ValueError: - l=[] - if len(l)==0: - l=f[1] - f[0](l) -# else: -# print 'csi ignore',c,l - def csi_at(self,l): - for i in range(l[0]): - self.scroll_right(self.cy,self.cx) - def csi_A(self,l): - self.cy=max(self.st,self.cy-l[0]) - def csi_B(self,l): - self.cy=min(self.sb,self.cy+l[0]) - def csi_C(self,l): - self.cx=min(self.width-1,self.cx+l[0]) - self.cl=0 - def csi_D(self,l): - self.cx=max(0,self.cx-l[0]) - self.cl=0 - def csi_E(self,l): - self.csi_B(l) - self.cx=0 - self.cl=0 - def csi_F(self,l): - self.csi_A(l) - self.cx=0 - self.cl=0 - def csi_G(self,l): - self.cx=min(self.width,l[0])-1 - def csi_H(self,l): - if len(l)<2: l=[1,1] - self.cx=min(self.width,l[1])-1 - self.cy=min(self.height,l[0])-1 - self.cl=0 - def csi_J(self,l): - if l[0]==0: - self.zero(self.cy,self.cx,self.height-1,self.width-1) - elif l[0]==1: - self.zero(0,0,self.cy,self.cx) - elif l[0]==2: - self.zero(0,0,self.height-1,self.width-1) - def csi_K(self,l): - if l[0]==0: - self.zero(self.cy,self.cx,self.cy,self.width-1) - elif l[0]==1: - self.zero(self.cy,0,self.cy,self.cx) - elif l[0]==2: - self.zero(self.cy,0,self.cy,self.width-1) - def csi_L(self,l): - for i in range(l[0]): - if self.cy<self.sb: - self.scroll_down(self.cy,self.sb) - def csi_M(self,l): - if self.cy>=self.st and self.cy<=self.sb: - for i in range(l[0]): - self.scroll_up(self.cy,self.sb) - def csi_P(self,l): - w,cx,cy=self.width,self.cx,self.cy - end=self.peek(cy,cx,cy,w) - self.csi_K([0]) - self.poke(cy,cx,end[l[0]:]) - def csi_X(self,l): - self.zero(self.cy,self.cx,self.cy,self.cx+l[0]) - def csi_a(self,l): - self.csi_C(l) - def csi_c(self,l): - #'\x1b[?0c' 0-8 cursor size - pass - def csi_d(self,l): - self.cy=min(self.height,l[0])-1 - def csi_e(self,l): - self.csi_B(l) - def csi_f(self,l): - self.csi_H(l) - def csi_h(self,l): - if l[0]==4: - pass -# print "insert on" - def csi_l(self,l): - if l[0]==4: - pass -# print "insert off" - def csi_m(self,l): - for i in l: - if i==0 or i==39 or i==49 or i==27: - self.sgr=0x000700 - elif i==1: - self.sgr=(self.sgr|0x000800) - elif i==7: - self.sgr=0x070000 - elif i>=30 and i<=37: - c=i-30 - self.sgr=(self.sgr&0xff08ff)|(c<<8) - elif i>=40 and i<=47: - c=i-40 - self.sgr=(self.sgr&0x00ffff)|(c<<16) -# else: -# print "CSI sgr ignore",l,i -# print 'sgr: %r %x'%(l,self.sgr) - def csi_r(self,l): - if len(l)<2: l=[0,self.height] - self.st=min(self.height-1,l[0]-1) - self.sb=min(self.height-1,l[1]-1) - self.sb=max(self.st,self.sb) - def csi_s(self,l): - self.esc_save(0) - def csi_u(self,l): - self.esc_restore(0) - def escape(self): - e=self.buf - if len(e)>32: -# print "error %r"%e - self.buf="" - elif e in self.esc_seq: - self.esc_seq[e](e) - self.buf="" - else: - for r,f in self.esc_re: - mo=r.match(e) - if mo: - f(e,mo) - self.buf="" - break -# if self.buf=='': print "ESC %r\n"%e - def write(self,s): - for i in s: - if len(self.buf) or (i in self.esc_seq): - self.buf+=i - self.escape() - elif i == '\x1b': - self.buf+=i - else: - self.echo(i) - def read(self): - b=self.outbuf - self.outbuf="" - return b - def dump(self): - r='' - for i in self.scr: - r+=chr(i&255) - return r - def dumplatin1(self): - return self.dump().translate(self.trl1) - def dumphtml(self,color=1): - h=self.height - w=self.width - r="" - span="" - span_bg,span_fg=-1,-1 - for i in range(h*w): - q,c=divmod(self.scr[i],256) - if color: - bg,fg=divmod(q,256) - else: - bg,fg=0,7 - if i==self.cy*w+self.cx: - bg,fg=1,7 - if (bg!=span_bg or fg!=span_fg or i==h*w-1): - if len(span): - r+='<span class="f%d b%d">%s</span>'%(span_fg,span_bg,cgi.escape(span.translate(self.trhtml))) - span="" - span_bg,span_fg=bg,fg - span+=chr(c) - if i%w==w-1: - span+='\n' - r='<?xml version="1.0" encoding="ISO-8859-1"?><pre class="term">%s</pre>'%r - if self.last_html==r: - return '<?xml version="1.0"?><idem></idem>' - else: - self.last_html=r -# print self - return r - def __repr__(self): - d=self.dumplatin1() - r="" - for i in range(self.height): - r+="|%s|\n"%d[self.width*i:self.width*(i+1)] - return r - -class SynchronizedMethod: - def __init__(self,lock,orig): - self.lock=lock - self.orig=orig - def __call__(self,*l): - self.lock.acquire() - r=self.orig(*l) - self.lock.release() - return r - -class Multiplex: - def __init__(self,cmd=None): - signal.signal(signal.SIGCHLD, signal.SIG_IGN) - self.cmd=cmd - self.proc={} - self.lock=threading.RLock() - self.thread=threading.Thread(target=self.loop) - self.alive=1 - self.lastActivity=time.time() - # synchronize methods - for name in ['create','fds','proc_read','proc_write','dump','die','run']: - orig=getattr(self,name) - setattr(self,name,SynchronizedMethod(self.lock,orig)) - self.thread.start() - def create(self,w=80,h=25): - pid,fd=pty.fork() - if pid==0: - try: - fdl=[int(i) for i in os.listdir('/proc/self/fd')] - except OSError: - fdl=range(256) - for i in [i for i in fdl if i>2]: - try: - os.close(i) - except OSError: - pass - if self.cmd: - cmd=['/bin/sh','-c',self.cmd] - elif os.getuid()==0: - cmd=['/bin/login'] - else: - sys.stdout.write("Login: ") - login=sys.stdin.readline().strip() - if re.match('^[0-9A-Za-z-_. ]+$',login): - cmd=['ssh'] - cmd+=['-oPreferredAuthentications=keyboard-interactive,password'] - cmd+=['-oNoHostAuthenticationForLocalhost=yes'] - cmd+=['-oLogLevel=FATAL'] - cmd+=['-F/dev/null','-l',login,'localhost'] - else: - os._exit(0) - env={} - env["COLUMNS"]=str(w) - env["LINES"]=str(h) - env["TERM"]="linux" - env["PATH"]=os.environ['PATH'] - os.execvpe(cmd[0],cmd,env) - else: - fcntl.fcntl(fd, fcntl.F_SETFL, os.O_NONBLOCK) - # python bug http://python.org/sf/1112949 on amd64 - fcntl.ioctl(fd, struct.unpack('i',struct.pack('I',termios.TIOCSWINSZ))[0], struct.pack("HHHH",h,w,0,0)) - self.proc[fd]={'pid':pid,'term':Terminal(w,h),'buf':'','time':time.time()} - return fd - def die(self): - self.alive=0 - def run(self): - return self.alive - def fds(self): - return self.proc.keys() - def proc_kill(self,fd): - if fd in self.proc: - self.proc[fd]['time']=0 - t=time.time() - for i in self.proc.keys(): - t0=self.proc[i]['time'] - if (t-t0)>TIMEOUT: - try: - os.close(i) - os.kill(self.proc[i]['pid'],signal.SIGTERM) - except (IOError,OSError): - pass - del self.proc[i] - def proc_read(self,fd): - try: - t=self.proc[fd]['term'] - t.write(os.read(fd,65536)) - reply=t.read() - if reply: - os.write(fd,reply) - self.proc[fd]['time']=time.time() - except (KeyError,IOError,OSError): - self.proc_kill(fd) - def proc_write(self,fd,s): - try: - os.write(fd,s) - except (IOError,OSError): - self.proc_kill(fd) - def dump(self,fd,color=1): - try: - return self.proc[fd]['term'].dumphtml(color) - except KeyError: - return False - def loop(self): - while self.run(): - fds=self.fds() - i,o,e=select.select(fds, [], [], 1.0) - if time.time() - self.lastActivity > TIMEOUT: - global g_server - g_server.shutdown() - for fd in i: - self.proc_read(fd) - if len(i): - time.sleep(0.002) - for i in self.proc.keys(): - try: - os.close(i) - os.kill(self.proc[i]['pid'],signal.SIGTERM) - except (IOError,OSError): - pass - -class AjaxTerm: - def __init__(self,cmd=None,index_file='ajaxterm.html',token=None): - self.files={} - self.token=token - for i in ['css','html','js']: - for j in glob.glob('*.%s'%i): - self.files[j]=file(j).read() - self.files['index']=file(index_file).read() - self.mime = mimetypes.types_map.copy() - self.mime['.html']= 'text/html; charset=UTF-8' - self.multi = Multiplex(cmd) - self.session = {} - def __call__(self, environ, start_response): - req = qweb.QWebRequest(environ, start_response,session=None) - if req.PATH_INFO.endswith('/u'): - s=req.REQUEST["s"] - k=req.REQUEST["k"] - c=req.REQUEST["c"] - w=req.REQUEST.int("w") - h=req.REQUEST.int("h") - if s in self.session: - term=self.session[s] - else: - raise Exception('Not Authorized') - # The original code below was insecure, because it allowed unauthorized sessions to be created - # if not (w>2 and w<256 and h>2 and h<100): - # w,h=80,25 - # term=self.session[s]=self.multi.create(w,h) - if k: - self.multi.proc_write(term,k) - time.sleep(0.002) - self.multi.lastActivity = time.time(); - dump=self.multi.dump(term,c) - req.response_headers['Content-Type']='text/xml' - if isinstance(dump,str): - req.write(dump) - req.response_gzencode=1 - else: - del self.session[s] - req.write('<?xml version="1.0"?><idem></idem>') -# print "sessions %r"%self.session - else: - n=os.path.basename(req.PATH_INFO) - if n in self.files: - req.response_headers['Content-Type'] = self.mime.get(os.path.splitext(n)[1].lower(), 'application/octet-stream') - req.write(self.files[n]) - elif req.REQUEST['token'] == self.token: - req.response_headers['Content-Type'] = 'text/html; charset=UTF-8' - session_id = str(uuid.uuid4()) - req.write(string.Template(self.files['index']).substitute(session_id=session_id)) - term=self.session[session_id]=self.multi.create(80,25) - else: - raise Exception("Not Authorized") - return req - -def main(): - parser = optparse.OptionParser() - parser.add_option("-p", "--port", dest="port", default="8022", help="Set the TCP port (default: 8022)") - parser.add_option("-c", "--command", dest="cmd", default=None,help="set the command (default: /bin/login or ssh 0.0.0.0)") - parser.add_option("-l", "--log", action="store_true", dest="log",default=0,help="log requests to stderr (default: quiet mode)") - parser.add_option("-d", "--daemon", action="store_true", dest="daemon", default=0, help="run as daemon in the background") - parser.add_option("-P", "--pidfile",dest="pidfile",default="/var/run/ajaxterm.pid",help="set the pidfile (default: /var/run/ajaxterm.pid)") - parser.add_option("-i", "--index", dest="index_file", default="ajaxterm.html",help="default index file (default: ajaxterm.html)") - parser.add_option("-u", "--uid", dest="uid", help="Set the daemon's user id") - parser.add_option("-t", "--token", dest="token", help="Set authorization token") - (o, a) = parser.parse_args() - if o.daemon: - pid=os.fork() - if pid == 0: - #os.setsid() ? - os.setpgrp() - nullin = file('/dev/null', 'r') - nullout = file('/dev/null', 'w') - os.dup2(nullin.fileno(), sys.stdin.fileno()) - os.dup2(nullout.fileno(), sys.stdout.fileno()) - os.dup2(nullout.fileno(), sys.stderr.fileno()) - if os.getuid()==0 and o.uid: - try: - os.setuid(int(o.uid)) - except: - os.setuid(pwd.getpwnam(o.uid).pw_uid) - else: - try: - file(o.pidfile,'w+').write(str(pid)+'\n') - except: - pass - print 'AjaxTerm at http://0.0.0.0:%s/ pid: %d' % (o.port,pid) - sys.exit(0) - else: - print 'AjaxTerm at http://0.0.0.0:%s/' % o.port - at=AjaxTerm(o.cmd,o.index_file,o.token) -# f=lambda:os.system('firefox http://localhost:%s/&'%o.port) -# qweb.qweb_wsgi_autorun(at,ip='localhost',port=int(o.port),threaded=0,log=o.log,callback_ready=None) - try: - global g_server - g_server = qweb.QWebWSGIServer(at,ip='0.0.0.0',port=int(o.port),threaded=0,log=o.log) - g_server.serve_forever() - except KeyboardInterrupt,e: - sys.excepthook(*sys.exc_info()) - at.multi.die() - -if __name__ == '__main__': - main() - diff --git a/tools/ajaxterm/configure b/tools/ajaxterm/configure deleted file mode 100755 index 45391f484..000000000 --- a/tools/ajaxterm/configure +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python - -import optparse,os - -parser = optparse.OptionParser() -parser.add_option("", "--prefix", dest="prefix",default="/usr/local",help="installation prefix (default: /usr/local)") -parser.add_option("", "--confdir", dest="confdir", default="/etc",help="configuration files directory prefix (default: /etc)") -parser.add_option("", "--port", dest="port", default="8022", help="set the listening TCP port (default: 8022)") -parser.add_option("", "--command", dest="cmd", default=None,help="set the command (default: /bin/login or ssh localhost)") -(o, a) = parser.parse_args() - -print "Configuring prefix=",o.prefix," port=",o.port - -etc=o.confdir -port=o.port -cmd=o.cmd -bin=os.path.join(o.prefix,"bin") -lib=os.path.join(o.prefix,"share/ajaxterm") -man=os.path.join(o.prefix,"share/man/man1") - -file("ajaxterm.bin","w").write(file("configure.ajaxterm.bin").read()%locals()) -file("Makefile","w").write(file("configure.makefile").read()%locals()) - -if os.path.isfile("/etc/gentoo-release"): - file("ajaxterm.initd","w").write(file("configure.initd.gentoo").read()%locals()) -elif os.path.isfile("/etc/fedora-release") or os.path.isfile("/etc/redhat-release"): - file("ajaxterm.initd","w").write(file("configure.initd.redhat").read()%locals()) -else: - file("ajaxterm.initd","w").write(file("configure.initd.debian").read()%locals()) - -os.system("chmod a+x ajaxterm.bin") -os.system("chmod a+x ajaxterm.initd") diff --git a/tools/ajaxterm/configure.ajaxterm.bin b/tools/ajaxterm/configure.ajaxterm.bin deleted file mode 100644 index 4d1f5a98f..000000000 --- a/tools/ajaxterm/configure.ajaxterm.bin +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -PYTHONPATH=%(lib)s exec %(lib)s/ajaxterm.py $@ diff --git a/tools/ajaxterm/configure.initd.debian b/tools/ajaxterm/configure.initd.debian deleted file mode 100644 index 901082707..000000000 --- a/tools/ajaxterm/configure.initd.debian +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/sh - -PATH=/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin -DAEMON=%(bin)s/ajaxterm -PORT=%(port)s -PIDFILE=/var/run/ajaxterm.pid - -[ -x "$DAEMON" ] || exit 0 - -#. /lib/lsb/init-functions - -case "$1" in - start) - echo "Starting ajaxterm on port $PORT" - start-stop-daemon --start --pidfile $PIDFILE --exec $DAEMON -- --daemon --port=$PORT --uid=nobody || return 2 - ;; - stop) - echo "Stopping ajaxterm" - start-stop-daemon --stop --pidfile $PIDFILE - rm -f $PIDFILE - ;; - restart|force-reload) - $0 stop - sleep 1 - $0 start - ;; - *) - echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2 - exit 3 - ;; -esac - -: diff --git a/tools/ajaxterm/configure.initd.gentoo b/tools/ajaxterm/configure.initd.gentoo deleted file mode 100644 index ac28ef0b6..000000000 --- a/tools/ajaxterm/configure.initd.gentoo +++ /dev/null @@ -1,27 +0,0 @@ -#!/sbin/runscript - -# AjaxTerm Gentoo script, 08 May 2006 Mark Gillespie - -DAEMON=%(bin)s/ajaxterm -PORT=%(port)s -PIDFILE=/var/run/ajaxterm.pid - -depend() -{ - need net -} - -start() -{ - ebegin "Starting AjaxTerm on port $PORT" - start-stop-daemon --start --pidfile $PIDFILE --exec $DAEMON -- --daemon --port=$PORT --uid=nobody - eend $? -} - -stop() -{ - ebegin "Stopping AjaxTerm" - start-stop-daemon --stop --pidfile $PIDFILE - rm -f $PIDFILE - eend $? -} diff --git a/tools/ajaxterm/configure.initd.redhat b/tools/ajaxterm/configure.initd.redhat deleted file mode 100644 index 5c9788574..000000000 --- a/tools/ajaxterm/configure.initd.redhat +++ /dev/null @@ -1,75 +0,0 @@ -# -# ajaxterm Startup script for ajaxterm -# -# chkconfig: - 99 99 -# description: Ajaxterm is a yadda yadda yadda -# processname: ajaxterm -# pidfile: /var/run/ajaxterm.pid -# version: 1.0 Kevin Reichhart - ajaxterminit at lastname dot org - -# Source function library. -. /etc/rc.d/init.d/functions - -if [ -f /etc/sysconfig/ajaxterm ]; then - . /etc/sysconfig/ajaxterm -fi - -ajaxterm=/usr/local/bin/ajaxterm -prog=ajaxterm -pidfile=${PIDFILE-/var/run/ajaxterm.pid} -lockfile=${LOCKFILE-/var/lock/subsys/ajaxterm} -port=${PORT-8022} -user=${xUSER-nobody} -RETVAL=0 - - -start() { - echo -n $"Starting $prog: " - daemon $ajaxterm --daemon --port=$port --uid=$user $OPTIONS - RETVAL=$? - echo - [ $RETVAL = 0 ] && touch ${lockfile} - return $RETVAL -} -stop() { - echo -n $"Stopping $prog: " - killproc $ajaxterm - RETVAL=$? - echo - [ $RETVAL = 0 ] && rm -f ${lockfile} ${pidfile} -} -reload() { - echo -n $"Reloading $prog: " - killproc $ajaxterm -HUP - RETVAL=$? - echo -} - -# See how we were called. -case "$1" in - start) - start - ;; - stop) - stop - ;; - status) - status python ajaxterm - RETVAL=$? - ;; - restart) - stop - start - ;; - condrestart) - if [ -f ${pidfile} ] ; then - stop - start - fi - ;; - *) - echo $"Usage: $prog {start|stop|restart|condrestart}" - exit 1 -esac - -exit $RETVAL diff --git a/tools/ajaxterm/configure.makefile b/tools/ajaxterm/configure.makefile deleted file mode 100644 index 6bd80853d..000000000 --- a/tools/ajaxterm/configure.makefile +++ /dev/null @@ -1,20 +0,0 @@ -build: - true - -install: - install -d "%(bin)s" - install -d "%(lib)s" - install ajaxterm.bin "%(bin)s/ajaxterm" - install ajaxterm.initd "%(etc)s/init.d/ajaxterm" - install -m 644 ajaxterm.css ajaxterm.html ajaxterm.js qweb.py sarissa.js sarissa_dhtml.js "%(lib)s" - install -m 755 ajaxterm.py "%(lib)s" - gzip --best -c ajaxterm.1 > ajaxterm.1.gz - install -d "%(man)s" - install ajaxterm.1.gz "%(man)s" - -clean: - rm ajaxterm.bin - rm ajaxterm.initd - rm ajaxterm.1.gz - rm Makefile - diff --git a/tools/ajaxterm/qweb.py b/tools/ajaxterm/qweb.py deleted file mode 100644 index 630325c95..000000000 --- a/tools/ajaxterm/qweb.py +++ /dev/null @@ -1,1356 +0,0 @@ -#!/usr/bin/python2.3 -# -# vim:set et ts=4 fdc=0 fdn=2 fdl=0: -# -# There are no blank lines between blocks beacause i use folding from: -# http://www.vim.org/scripts/script.php?script_id=515 -# - -"""= QWeb Framework = - -== What is QWeb ? == - -QWeb is a python based [http://www.python.org/doc/peps/pep-0333/ WSGI] -compatible web framework, it provides an infratructure to quickly build web -applications consisting of: - - * A lightweight request handler (QWebRequest) - * An xml templating engine (QWebXml and QWebHtml) - * A simple name based controler (qweb_control) - * A standalone WSGI Server (QWebWSGIServer) - * A cgi and fastcgi WSGI wrapper (taken from flup) - * A startup function that starts cgi, factgi or standalone according to the - evironement (qweb_autorun). - -QWeb applications are runnable in standalone mode (from commandline), via -FastCGI, Regular CGI or by any python WSGI compliant server. - -QWeb doesn't provide any database access but it integrates nicely with ORMs -such as SQLObject, SQLAlchemy or plain DB-API. - -Written by Antony Lesuisse (email al AT udev.org) - -Homepage: http://antony.lesuisse.org/qweb/trac/ - -Forum: [http://antony.lesuisse.org/qweb/forum/viewforum.php?id=1 Forum] - -== Quick Start (for Linux, MacOS X and cygwin) == - -Make sure you have at least python 2.3 installed and run the following commands: - -{{{ -$ wget http://antony.lesuisse.org/qweb/files/QWeb-0.7.tar.gz -$ tar zxvf QWeb-0.7.tar.gz -$ cd QWeb-0.7/examples/blog -$ ./blog.py -}}} - -And point your browser to http://localhost:8080/ - -You may also try AjaxTerm which uses qweb request handler. - -== Download == - - * Version 0.7: - * Source [/qweb/files/QWeb-0.7.tar.gz QWeb-0.7.tar.gz] - * Python 2.3 Egg [/qweb/files/QWeb-0.7-py2.3.egg QWeb-0.7-py2.3.egg] - * Python 2.4 Egg [/qweb/files/QWeb-0.7-py2.4.egg QWeb-0.7-py2.4.egg] - - * [/qweb/trac/browser Browse the source repository] - -== Documentation == - - * [/qweb/trac/browser/trunk/README.txt?format=raw Read the included documentation] - * QwebTemplating - -== Mailin-list == - - * Forum: [http://antony.lesuisse.org/qweb/forum/viewforum.php?id=1 Forum] - * No mailing-list exists yet, discussion should happen on: [http://mail.python.org/mailman/listinfo/web-sig web-sig] [http://mail.python.org/pipermail/web-sig/ archives] - -QWeb Components: ----------------- - -QWeb also feature a simple components api, that enables developers to easily -produces reusable components. - -Default qweb components: - - - qweb_static: - A qweb component to serve static content from the filesystem or from - zipfiles. - - - qweb_dbadmin: - scaffolding for sqlobject - -License -------- -qweb/fcgi.py wich is BSD-like from saddi.com. -Everything else is put in the public domain. - - -TODO ----- - Announce QWeb to python-announce-list@python.org web-sig@python.org - qweb_core - rename request methods into - request_save_files - response_404 - response_redirect - response_download - request callback_generator, callback_function ? - wsgi callback_server_local - xml tags explicitly call render_attributes(t_att)? - priority form-checkbox over t-value (for t-option) - -""" - -import BaseHTTPServer,SocketServer,Cookie -import cgi,datetime,email,email.Message,errno,gzip,os,random,re,socket,sys,tempfile,time,types,urllib,urlparse,xml.dom -try: - import cPickle as pickle -except ImportError: - import pickle -try: - import cStringIO as StringIO -except ImportError: - import StringIO - -#---------------------------------------------------------- -# Qweb Xml t-raw t-esc t-if t-foreach t-set t-call t-trim -#---------------------------------------------------------- -class QWebEval: - def __init__(self,data): - self.data=data - def __getitem__(self,expr): - if self.data.has_key(expr): - return self.data[expr] - r=None - try: - r=eval(expr,self.data) - except NameError,e: - pass - except AttributeError,e: - pass - except Exception,e: - print "qweb: expression error '%s' "%expr,e - if self.data.has_key("__builtins__"): - del self.data["__builtins__"] - return r - def eval_object(self,expr): - return self[expr] - def eval_str(self,expr): - if expr=="0": - return self.data[0] - if isinstance(self[expr],unicode): - return self[expr].encode("utf8") - return str(self[expr]) - def eval_format(self,expr): - try: - return str(expr%self) - except: - return "qweb: format error '%s' "%expr -# if isinstance(r,unicode): -# return r.encode("utf8") - def eval_bool(self,expr): - if self.eval_object(expr): - return 1 - else: - return 0 -class QWebXml: - """QWeb Xml templating engine - - The templating engine use a very simple syntax, "magic" xml attributes, to - produce any kind of texutal output (even non-xml). - - QWebXml: - the template engine core implements the basic magic attributes: - - t-att t-raw t-esc t-if t-foreach t-set t-call t-trim - - """ - def __init__(self,x=None,zipname=None): - self.node=xml.dom.Node - self._t={} - self._render_tag={} - prefix='render_tag_' - for i in [j for j in dir(self) if j.startswith(prefix)]: - name=i[len(prefix):].replace('_','-') - self._render_tag[name]=getattr(self.__class__,i) - - self._render_att={} - prefix='render_att_' - for i in [j for j in dir(self) if j.startswith(prefix)]: - name=i[len(prefix):].replace('_','-') - self._render_att[name]=getattr(self.__class__,i) - - if x!=None: - if zipname!=None: - import zipfile - zf=zipfile.ZipFile(zipname, 'r') - self.add_template(zf.read(x)) - else: - self.add_template(x) - def register_tag(self,tag,func): - self._render_tag[tag]=func - def add_template(self,x): - if hasattr(x,'documentElement'): - dom=x - elif x.startswith("<?xml"): - import xml.dom.minidom - dom=xml.dom.minidom.parseString(x) - else: - import xml.dom.minidom - dom=xml.dom.minidom.parse(x) - for n in dom.documentElement.childNodes: - if n.nodeName=="t": - self._t[str(n.getAttribute("t-name"))]=n - def get_template(self,name): - return self._t[name] - - def eval_object(self,expr,v): - return QWebEval(v).eval_object(expr) - def eval_str(self,expr,v): - return QWebEval(v).eval_str(expr) - def eval_format(self,expr,v): - return QWebEval(v).eval_format(expr) - def eval_bool(self,expr,v): - return QWebEval(v).eval_bool(expr) - - def render(self,tname,v={},out=None): - if self._t.has_key(tname): - return self.render_node(self._t[tname],v) - else: - return 'qweb: template "%s" not found'%tname - def render_node(self,e,v): - r="" - if e.nodeType==self.node.TEXT_NODE or e.nodeType==self.node.CDATA_SECTION_NODE: - r=e.data.encode("utf8") - elif e.nodeType==self.node.ELEMENT_NODE: - pre="" - g_att="" - t_render=None - t_att={} - for (an,av) in e.attributes.items(): - an=str(an) - if isinstance(av,types.UnicodeType): - av=av.encode("utf8") - else: - av=av.nodeValue.encode("utf8") - if an.startswith("t-"): - for i in self._render_att: - if an[2:].startswith(i): - g_att+=self._render_att[i](self,e,an,av,v) - break - else: - if self._render_tag.has_key(an[2:]): - t_render=an[2:] - t_att[an[2:]]=av - else: - g_att+=' %s="%s"'%(an,cgi.escape(av,1)); - if t_render: - if self._render_tag.has_key(t_render): - r=self._render_tag[t_render](self,e,t_att,g_att,v) - else: - r=self.render_element(e,g_att,v,pre,t_att.get("trim",0)) - return r - def render_element(self,e,g_att,v,pre="",trim=0): - g_inner=[] - for n in e.childNodes: - g_inner.append(self.render_node(n,v)) - name=str(e.nodeName) - inner="".join(g_inner) - if trim==0: - pass - elif trim=='left': - inner=inner.lstrip() - elif trim=='right': - inner=inner.rstrip() - elif trim=='both': - inner=inner.strip() - if name=="t": - return inner - elif len(inner): - return "<%s%s>%s%s</%s>"%(name,g_att,pre,inner,name) - else: - return "<%s%s/>"%(name,g_att) - - # Attributes - def render_att_att(self,e,an,av,v): - if an.startswith("t-attf-"): - att,val=an[7:],self.eval_format(av,v) - elif an.startswith("t-att-"): - att,val=(an[6:],self.eval_str(av,v)) - else: - att,val=self.eval_object(av,v) - return ' %s="%s"'%(att,cgi.escape(val,1)) - - # Tags - def render_tag_raw(self,e,t_att,g_att,v): - return self.eval_str(t_att["raw"],v) - def render_tag_rawf(self,e,t_att,g_att,v): - return self.eval_format(t_att["rawf"],v) - def render_tag_esc(self,e,t_att,g_att,v): - return cgi.escape(self.eval_str(t_att["esc"],v)) - def render_tag_escf(self,e,t_att,g_att,v): - return cgi.escape(self.eval_format(t_att["escf"],v)) - def render_tag_foreach(self,e,t_att,g_att,v): - expr=t_att["foreach"] - enum=self.eval_object(expr,v) - if enum!=None: - var=t_att.get('as',expr).replace('.','_') - d=v.copy() - size=-1 - if isinstance(enum,types.ListType): - size=len(enum) - elif isinstance(enum,types.TupleType): - size=len(enum) - elif hasattr(enum,'count'): - size=enum.count() - d["%s_size"%var]=size - d["%s_all"%var]=enum - index=0 - ru=[] - for i in enum: - d["%s_value"%var]=i - d["%s_index"%var]=index - d["%s_first"%var]=index==0 - d["%s_even"%var]=index%2 - d["%s_odd"%var]=(index+1)%2 - d["%s_last"%var]=index+1==size - if index%2: - d["%s_parity"%var]='odd' - else: - d["%s_parity"%var]='even' - if isinstance(i,types.DictType): - d.update(i) - else: - d[var]=i - ru.append(self.render_element(e,g_att,d)) - index+=1 - return "".join(ru) - else: - return "qweb: t-foreach %s not found."%expr - def render_tag_if(self,e,t_att,g_att,v): - if self.eval_bool(t_att["if"],v): - return self.render_element(e,g_att,v) - else: - return "" - def render_tag_call(self,e,t_att,g_att,v): - # TODO t-prefix - if t_att.has_key("import"): - d=v - else: - d=v.copy() - d[0]=self.render_element(e,g_att,d) - return self.render(t_att["call"],d) - def render_tag_set(self,e,t_att,g_att,v): - if t_att.has_key("eval"): - v[t_att["set"]]=self.eval_object(t_att["eval"],v) - else: - v[t_att["set"]]=self.render_element(e,g_att,v) - return "" - -#---------------------------------------------------------- -# QWeb HTML (+deprecated QWebFORM and QWebOLD) -#---------------------------------------------------------- -class QWebURL: - """ URL helper - assert req.PATH_INFO== "/site/admin/page_edit" - u = QWebURL(root_path="/site/",req_path=req.PATH_INFO) - s=u.url2_href("user/login",{'a':'1'}) - assert s=="../user/login?a=1" - - """ - def __init__(self, root_path="/", req_path="/",defpath="",defparam={}): - self.defpath=defpath - self.defparam=defparam - self.root_path=root_path - self.req_path=req_path - self.req_list=req_path.split("/")[:-1] - self.req_len=len(self.req_list) - def decode(self,s): - h={} - for k,v in cgi.parse_qsl(s,1): - h[k]=v - return h - def encode(self,h): - return urllib.urlencode(h.items()) - def request(self,req): - return req.REQUEST - def copy(self,path=None,param=None): - npath=self.defpath - if path: - npath=path - nparam=self.defparam.copy() - if param: - nparam.update(param) - return QWebURL(self.root_path,self.req_path,npath,nparam) - def path(self,path=''): - if not path: - path=self.defpath - pl=(self.root_path+path).split('/') - i=0 - for i in range(min(len(pl), self.req_len)): - if pl[i]!=self.req_list[i]: - break - else: - i+=1 - dd=self.req_len-i - if dd<0: - dd=0 - return '/'.join(['..']*dd+pl[i:]) - def href(self,path='',arg={}): - p=self.path(path) - tmp=self.defparam.copy() - tmp.update(arg) - s=self.encode(tmp) - if len(s): - return p+"?"+s - else: - return p - def form(self,path='',arg={}): - p=self.path(path) - tmp=self.defparam.copy() - tmp.update(arg) - r=''.join(['<input type="hidden" name="%s" value="%s"/>'%(k,cgi.escape(str(v),1)) for k,v in tmp.items()]) - return (p,r) -class QWebField: - def __init__(self,name=None,default="",check=None): - self.name=name - self.default=default - self.check=check - # optional attributes - self.type=None - self.trim=1 - self.required=1 - self.cssvalid="form_valid" - self.cssinvalid="form_invalid" - # set by addfield - self.form=None - # set by processing - self.input=None - self.css=None - self.value=None - self.valid=None - self.invalid=None - self.validate(1) - def validate(self,val=1,update=1): - if val: - self.valid=1 - self.invalid=0 - self.css=self.cssvalid - else: - self.valid=0 - self.invalid=1 - self.css=self.cssinvalid - if update and self.form: - self.form.update() - def invalidate(self,update=1): - self.validate(0,update) -class QWebForm: - class QWebFormF: - pass - def __init__(self,e=None,arg=None,default=None): - self.fields={} - # all fields have been submitted - self.submitted=False - self.missing=[] - # at least one field is invalid or missing - self.invalid=False - self.error=[] - # all fields have been submitted and are valid - self.valid=False - # fields under self.f for convenience - self.f=self.QWebFormF() - if e: - self.add_template(e) - # assume that the fields are done with the template - if default: - self.set_default(default,e==None) - if arg!=None: - self.process_input(arg) - def __getitem__(self,k): - return self.fields[k] - def set_default(self,default,add_missing=1): - for k,v in default.items(): - if self.fields.has_key(k): - self.fields[k].default=str(v) - elif add_missing: - self.add_field(QWebField(k,v)) - def add_field(self,f): - self.fields[f.name]=f - f.form=self - setattr(self.f,f.name,f) - def add_template(self,e): - att={} - for (an,av) in e.attributes.items(): - an=str(an) - if an.startswith("t-"): - att[an[2:]]=av.encode("utf8") - for i in ["form-text", "form-password", "form-radio", "form-checkbox", "form-select","form-textarea"]: - if att.has_key(i): - name=att[i].split(".")[-1] - default=att.get("default","") - check=att.get("check",None) - f=QWebField(name,default,check) - if i=="form-textarea": - f.type="textarea" - f.trim=0 - if i=="form-checkbox": - f.type="checkbox" - f.required=0 - self.add_field(f) - for n in e.childNodes: - if n.nodeType==n.ELEMENT_NODE: - self.add_template(n) - def process_input(self,arg): - for f in self.fields.values(): - if arg.has_key(f.name): - f.input=arg[f.name] - f.value=f.input - if f.trim: - f.input=f.input.strip() - f.validate(1,False) - if f.check==None: - continue - elif callable(f.check): - pass - elif isinstance(f.check,str): - v=f.check - if f.check=="email": - v=r"/^[^@#!& ]+@[A-Za-z0-9-][.A-Za-z0-9-]{0,64}\.[A-Za-z]{2,5}$/" - if f.check=="date": - v=r"/^(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$/" - if not re.match(v[1:-1],f.input): - f.validate(0,False) - else: - f.value=f.default - self.update() - def validate_all(self,val=1): - for f in self.fields.values(): - f.validate(val,0) - self.update() - def invalidate_all(self): - self.validate_all(0) - def update(self): - self.submitted=True - self.valid=True - self.errors=[] - for f in self.fields.values(): - if f.required and f.input==None: - self.submitted=False - self.valid=False - self.missing.append(f.name) - if f.invalid: - self.valid=False - self.error.append(f.name) - # invalid have been submitted and - self.invalid=self.submitted and self.valid==False - def collect(self): - d={} - for f in self.fields.values(): - d[f.name]=f.value - return d -class QWebURLEval(QWebEval): - def __init__(self,data): - QWebEval.__init__(self,data) - def __getitem__(self,expr): - r=QWebEval.__getitem__(self,expr) - if isinstance(r,str): - return urllib.quote_plus(r) - else: - return r -class QWebHtml(QWebXml): - """QWebHtml - QWebURL: - QWebField: - QWebForm: - QWebHtml: - an extended template engine, with a few utility class to easily produce - HTML, handle URLs and process forms, it adds the following magic attributes: - - t-href t-action t-form-text t-form-password t-form-textarea t-form-radio - t-form-checkbox t-form-select t-option t-selected t-checked t-pager - - # explication URL: - # v['tableurl']=QWebUrl({p=afdmin,saar=,orderby=,des=,mlink;meta_active=}) - # t-href="tableurl?desc=1" - # - # explication FORM: t-if="form.valid()" - # Foreach i - # email: <input type="text" t-esc-name="i" t-esc-value="form[i].value" t-esc-class="form[i].css"/> - # <input type="radio" name="spamtype" t-esc-value="i" t-selected="i==form.f.spamtype.value"/> - # <option t-esc-value="cc" t-selected="cc==form.f.country.value"><t t-esc="cname"></option> - # Simple forms: - # <input t-form-text="form.email" t-check="email"/> - # <input t-form-password="form.email" t-check="email"/> - # <input t-form-radio="form.email" /> - # <input t-form-checkbox="form.email" /> - # <textarea t-form-textarea="form.email" t-check="email"/> - # <select t-form-select="form.email"/> - # <option t-value="1"> - # <input t-form-radio="form.spamtype" t-value="1"/> Cars - # <input t-form-radio="form.spamtype" t-value="2"/> Sprt - """ - # QWebForm from a template - def form(self,tname,arg=None,default=None): - form=QWebForm(self._t[tname],arg,default) - return form - - # HTML Att - def eval_url(self,av,v): - s=QWebURLEval(v).eval_format(av) - a=s.split('?',1) - arg={} - if len(a)>1: - for k,v in cgi.parse_qsl(a[1],1): - arg[k]=v - b=a[0].split('/',1) - path='' - if len(b)>1: - path=b[1] - u=b[0] - return u,path,arg - def render_att_url_(self,e,an,av,v): - u,path,arg=self.eval_url(av,v) - if not isinstance(v.get(u,0),QWebURL): - out='qweb: missing url %r %r %r'%(u,path,arg) - else: - out=v[u].href(path,arg) - return ' %s="%s"'%(an[6:],cgi.escape(out,1)) - def render_att_href(self,e,an,av,v): - return self.render_att_url_(e,"t-url-href",av,v) - def render_att_checked(self,e,an,av,v): - if self.eval_bool(av,v): - return ' %s="%s"'%(an[2:],an[2:]) - else: - return '' - def render_att_selected(self,e,an,av,v): - return self.render_att_checked(e,an,av,v) - - # HTML Tags forms - def render_tag_rawurl(self,e,t_att,g_att,v): - u,path,arg=self.eval_url(t_att["rawurl"],v) - return v[u].href(path,arg) - def render_tag_escurl(self,e,t_att,g_att,v): - u,path,arg=self.eval_url(t_att["escurl"],v) - return cgi.escape(v[u].href(path,arg)) - def render_tag_action(self,e,t_att,g_att,v): - u,path,arg=self.eval_url(t_att["action"],v) - if not isinstance(v.get(u,0),QWebURL): - action,input=('qweb: missing url %r %r %r'%(u,path,arg),'') - else: - action,input=v[u].form(path,arg) - g_att+=' action="%s"'%action - return self.render_element(e,g_att,v,input) - def render_tag_form_text(self,e,t_att,g_att,v): - f=self.eval_object(t_att["form-text"],v) - g_att+=' type="text" name="%s" value="%s" class="%s"'%(f.name,cgi.escape(f.value,1),f.css) - return self.render_element(e,g_att,v) - def render_tag_form_password(self,e,t_att,g_att,v): - f=self.eval_object(t_att["form-password"],v) - g_att+=' type="password" name="%s" value="%s" class="%s"'%(f.name,cgi.escape(f.value,1),f.css) - return self.render_element(e,g_att,v) - def render_tag_form_textarea(self,e,t_att,g_att,v): - type="textarea" - f=self.eval_object(t_att["form-textarea"],v) - g_att+=' name="%s" class="%s"'%(f.name,f.css) - r="<%s%s>%s</%s>"%(type,g_att,cgi.escape(f.value,1),type) - return r - def render_tag_form_radio(self,e,t_att,g_att,v): - f=self.eval_object(t_att["form-radio"],v) - val=t_att["value"] - g_att+=' type="radio" name="%s" value="%s"'%(f.name,val) - if f.value==val: - g_att+=' checked="checked"' - return self.render_element(e,g_att,v) - def render_tag_form_checkbox(self,e,t_att,g_att,v): - f=self.eval_object(t_att["form-checkbox"],v) - val=t_att["value"] - g_att+=' type="checkbox" name="%s" value="%s"'%(f.name,val) - if f.value==val: - g_att+=' checked="checked"' - return self.render_element(e,g_att,v) - def render_tag_form_select(self,e,t_att,g_att,v): - f=self.eval_object(t_att["form-select"],v) - g_att+=' name="%s" class="%s"'%(f.name,f.css) - return self.render_element(e,g_att,v) - def render_tag_option(self,e,t_att,g_att,v): - f=self.eval_object(e.parentNode.getAttribute("t-form-select"),v) - val=t_att["option"] - g_att+=' value="%s"'%(val) - if f.value==val: - g_att+=' selected="selected"' - return self.render_element(e,g_att,v) - - # HTML Tags others - def render_tag_pager(self,e,t_att,g_att,v): - pre=t_att["pager"] - total=int(self.eval_str(t_att["total"],v)) - start=int(self.eval_str(t_att["start"],v)) - step=int(self.eval_str(t_att.get("step","100"),v)) - scope=int(self.eval_str(t_att.get("scope","5"),v)) - # Compute Pager - p=pre+"_" - d={} - d[p+"tot_size"]=total - d[p+"tot_page"]=tot_page=total/step - d[p+"win_start0"]=total and start - d[p+"win_start1"]=total and start+1 - d[p+"win_end0"]=max(0,min(start+step-1,total-1)) - d[p+"win_end1"]=min(start+step,total) - d[p+"win_page0"]=win_page=start/step - d[p+"win_page1"]=win_page+1 - d[p+"prev"]=(win_page!=0) - d[p+"prev_start"]=(win_page-1)*step - d[p+"next"]=(tot_page>=win_page+1) - d[p+"next_start"]=(win_page+1)*step - l=[] - begin=win_page-scope - end=win_page+scope - if begin<0: - end-=begin - if end>tot_page: - begin-=(end-tot_page) - i=max(0,begin) - while i<=min(end,tot_page) and total!=step: - l.append( { p+"page0":i, p+"page1":i+1, p+"start":i*step, p+"sel":(win_page==i) }) - i+=1 - d[p+"active"]=len(l)>1 - d[p+"list"]=l - # Update v - v.update(d) - return "" - -#---------------------------------------------------------- -# QWeb Simple Controller -#---------------------------------------------------------- -def qweb_control(self,jump='main',p=[]): - """ qweb_control(self,jump='main',p=[]): - A simple function to handle the controler part of your application. It - dispatch the control to the jump argument, while ensuring that prefix - function have been called. - - qweb_control replace '/' to '_' and strip '_' from the jump argument. - - name1 - name1_name2 - name1_name2_name3 - - """ - jump=jump.replace('/','_').strip('_') - if not hasattr(self,jump): - return 0 - done={} - todo=[] - while 1: - if jump!=None: - tmp="" - todo=[] - for i in jump.split("_"): - tmp+=i+"_"; - if not done.has_key(tmp[:-1]): - todo.append(tmp[:-1]) - jump=None - elif len(todo): - i=todo.pop(0) - done[i]=1 - if hasattr(self,i): - f=getattr(self,i) - r=f(*p) - if isinstance(r,types.StringType): - jump=r - else: - break - return 1 - -#---------------------------------------------------------- -# QWeb WSGI Request handler -#---------------------------------------------------------- -class QWebSession(dict): - def __init__(self,environ,**kw): - dict.__init__(self) - default={ - "path" : tempfile.gettempdir(), - "cookie_name" : "QWEBSID", - "cookie_lifetime" : 0, - "cookie_path" : '/', - "cookie_domain" : '', - "limit_cache" : 1, - "probability" : 0.01, - "maxlifetime" : 3600, - "disable" : 0, - } - for k,v in default.items(): - setattr(self,'session_%s'%k,kw.get(k,v)) - # Try to find session - self.session_found_cookie=0 - self.session_found_url=0 - self.session_found=0 - self.session_orig="" - # Try cookie - c=Cookie.SimpleCookie() - c.load(environ.get('HTTP_COOKIE', '')) - if c.has_key(self.session_cookie_name): - sid=c[self.session_cookie_name].value[:64] - if re.match('[a-f0-9]+$',sid) and self.session_load(sid): - self.session_id=sid - self.session_found_cookie=1 - self.session_found=1 - # Try URL - if not self.session_found_cookie: - mo=re.search('&%s=([a-f0-9]+)'%self.session_cookie_name,environ.get('QUERY_STRING','')) - if mo and self.session_load(mo.group(1)): - self.session_id=mo.group(1) - self.session_found_url=1 - self.session_found=1 - # New session - if not self.session_found: - self.session_id='%032x'%random.randint(1,2**128) - self.session_trans_sid="&%s=%s"%(self.session_cookie_name,self.session_id) - # Clean old session - if random.random() < self.session_probability: - self.session_clean() - def session_get_headers(self): - h=[] - if (not self.session_disable) and (len(self) or len(self.session_orig)): - self.session_save() - if not self.session_found_cookie: - c=Cookie.SimpleCookie() - c[self.session_cookie_name] = self.session_id - c[self.session_cookie_name]['path'] = self.session_cookie_path - if self.session_cookie_domain: - c[self.session_cookie_name]['domain'] = self.session_cookie_domain -# if self.session_cookie_lifetime: -# c[self.session_cookie_name]['expires'] = TODO date localtime or not, datetime.datetime(1970, 1, 1) - h.append(("Set-Cookie", c[self.session_cookie_name].OutputString())) - if self.session_limit_cache: - h.append(('Cache-Control','no-store, no-cache, must-revalidate, post-check=0, pre-check=0')) - h.append(('Expires','Thu, 19 Nov 1981 08:52:00 GMT')) - h.append(('Pragma','no-cache')) - return h - def session_load(self,sid): - fname=os.path.join(self.session_path,'qweb_sess_%s'%sid) - try: - orig=file(fname).read() - d=pickle.loads(orig) - except: - return - self.session_orig=orig - self.update(d) - return 1 - def session_save(self): - if not os.path.isdir(self.session_path): - os.makedirs(self.session_path) - fname=os.path.join(self.session_path,'qweb_sess_%s'%self.session_id) - try: - oldtime=os.path.getmtime(fname) - except OSError,IOError: - oldtime=0 - dump=pickle.dumps(self.copy()) - if (dump != self.session_orig) or (time.time() > oldtime+self.session_maxlifetime/4): - tmpname=os.path.join(self.session_path,'qweb_sess_%s_%x'%(self.session_id,random.randint(1,2**32))) - f=file(tmpname,'wb') - f.write(dump) - f.close() - if sys.platform=='win32' and os.path.isfile(fname): - os.remove(fname) - os.rename(tmpname,fname) - def session_clean(self): - t=time.time() - try: - for i in [os.path.join(self.session_path,i) for i in os.listdir(self.session_path) if i.startswith('qweb_sess_')]: - if (t > os.path.getmtime(i)+self.session_maxlifetime): - os.unlink(i) - except OSError,IOError: - pass -class QWebSessionMem(QWebSession): - def session_load(self,sid): - global _qweb_sessions - if not "_qweb_sessions" in globals(): - _qweb_sessions={} - if _qweb_sessions.has_key(sid): - self.session_orig=_qweb_sessions[sid] - self.update(self.session_orig) - return 1 - def session_save(self): - global _qweb_sessions - if not "_qweb_sessions" in globals(): - _qweb_sessions={} - _qweb_sessions[self.session_id]=self.copy() -class QWebSessionService: - def __init__(self, wsgiapp, url_rewrite=0): - self.wsgiapp=wsgiapp - self.url_rewrite_tags="a=href,area=href,frame=src,form=,fieldset=" - def __call__(self, environ, start_response): - # TODO - # use QWebSession to provide environ["qweb.session"] - return self.wsgiapp(environ,start_response) -class QWebDict(dict): - def __init__(self,*p): - dict.__init__(self,*p) - def __getitem__(self,key): - return self.get(key,"") - def int(self,key): - try: - return int(self.get(key,"0")) - except ValueError: - return 0 -class QWebListDict(dict): - def __init__(self,*p): - dict.__init__(self,*p) - def __getitem__(self,key): - return self.get(key,[]) - def appendlist(self,key,val): - if self.has_key(key): - self[key].append(val) - else: - self[key]=[val] - def get_qwebdict(self): - d=QWebDict() - for k,v in self.items(): - d[k]=v[-1] - return d -class QWebRequest: - """QWebRequest a WSGI request handler. - - QWebRequest is a WSGI request handler that feature GET, POST and POST - multipart methods, handles cookies and headers and provide a dict-like - SESSION Object (either on the filesystem or in memory). - - It is constructed with the environ and start_response WSGI arguments: - - req=qweb.QWebRequest(environ, start_response) - - req has the folowing attributes : - - req.environ standard WSGI dict (CGI and wsgi ones) - - Some CGI vars as attributes from environ for convenience: - - req.SCRIPT_NAME - req.PATH_INFO - req.REQUEST_URI - - Some computed value (also for convenience) - - req.FULL_URL full URL recontructed (http://host/query) - req.FULL_PATH (URL path before ?querystring) - - Dict constructed from querystring and POST datas, PHP-like. - - req.GET contains GET vars - req.POST contains POST vars - req.REQUEST contains merge of GET and POST - req.FILES contains uploaded files - req.GET_LIST req.POST_LIST req.REQUEST_LIST req.FILES_LIST multiple arguments versions - req.debug() returns an HTML dump of those vars - - A dict-like session object. - - req.SESSION the session start when the dict is not empty. - - Attribute for handling the response - - req.response_headers dict-like to set headers - req.response_cookies a SimpleCookie to set cookies - req.response_status a string to set the status like '200 OK' - - req.write() to write to the buffer - - req itselfs is an iterable object with the buffer, it will also also call - start_response automatically before returning anything via the iterator. - - To make it short, it means that you may use - - return req - - at the end of your request handling to return the reponse to any WSGI - application server. - """ - # - # This class contains part ripped from colubrid (with the permission of - # mitsuhiko) see http://wsgiarea.pocoo.org/colubrid/ - # - # - the class HttpHeaders - # - the method load_post_data (tuned version) - # - class HttpHeaders(object): - def __init__(self): - self.data = [('Content-Type', 'text/html')] - def __setitem__(self, key, value): - self.set(key, value) - def __delitem__(self, key): - self.remove(key) - def __contains__(self, key): - key = key.lower() - for k, v in self.data: - if k.lower() == key: - return True - return False - def add(self, key, value): - self.data.append((key, value)) - def remove(self, key, count=-1): - removed = 0 - data = [] - for _key, _value in self.data: - if _key.lower() != key.lower(): - if count > -1: - if removed >= count: - break - else: - removed += 1 - data.append((_key, _value)) - self.data = data - def clear(self): - self.data = [] - def set(self, key, value): - self.remove(key) - self.add(key, value) - def get(self, key=False, httpformat=False): - if not key: - result = self.data - else: - result = [] - for _key, _value in self.data: - if _key.lower() == key.lower(): - result.append((_key, _value)) - if httpformat: - return '\n'.join(['%s: %s' % item for item in result]) - return result - def load_post_data(self,environ,POST,FILES): - length = int(environ['CONTENT_LENGTH']) - DATA = environ['wsgi.input'].read(length) - if environ.get('CONTENT_TYPE', '').startswith('multipart'): - lines = ['Content-Type: %s' % environ.get('CONTENT_TYPE', '')] - for key, value in environ.items(): - if key.startswith('HTTP_'): - lines.append('%s: %s' % (key, value)) - raw = '\r\n'.join(lines) + '\r\n\r\n' + DATA - msg = email.message_from_string(raw) - for sub in msg.get_payload(): - if not isinstance(sub, email.Message.Message): - continue - name_dict = cgi.parse_header(sub['Content-Disposition'])[1] - if 'filename' in name_dict: - # Nested MIME Messages are not supported' - if isinstance(sub.get_payload(), list): - continue - if not name_dict['filename'].strip(): - continue - filename = name_dict['filename'] - # why not keep all the filename? because IE always send 'C:\documents and settings\blub\blub.png' - filename = filename[filename.rfind('\\') + 1:] - if 'Content-Type' in sub: - content_type = sub['Content-Type'] - else: - content_type = None - s = { "name":filename, "type":content_type, "data":sub.get_payload() } - FILES.appendlist(name_dict['name'], s) - else: - POST.appendlist(name_dict['name'], sub.get_payload()) - else: - POST.update(cgi.parse_qs(DATA,keep_blank_values=1)) - return DATA - - def __init__(self,environ,start_response,session=QWebSession): - self.environ=environ - self.start_response=start_response - self.buffer=[] - - self.SCRIPT_NAME = environ.get('SCRIPT_NAME', '') - self.PATH_INFO = environ.get('PATH_INFO', '') - # extensions: - self.FULL_URL = environ['FULL_URL'] = self.get_full_url(environ) - # REQUEST_URI is optional, fake it if absent - if not environ.has_key("REQUEST_URI"): - environ["REQUEST_URI"]=urllib.quote(self.SCRIPT_NAME+self.PATH_INFO) - if environ.get('QUERY_STRING'): - environ["REQUEST_URI"]+='?'+environ['QUERY_STRING'] - self.REQUEST_URI = environ["REQUEST_URI"] - # full quote url path before the ? - self.FULL_PATH = environ['FULL_PATH'] = self.REQUEST_URI.split('?')[0] - - self.request_cookies=Cookie.SimpleCookie() - self.request_cookies.load(environ.get('HTTP_COOKIE', '')) - - self.response_started=False - self.response_gzencode=False - self.response_cookies=Cookie.SimpleCookie() - # to delete a cookie use: c[key]['expires'] = datetime.datetime(1970, 1, 1) - self.response_headers=self.HttpHeaders() - self.response_status="200 OK" - - self.php=None - if self.environ.has_key("php"): - self.php=environ["php"] - self.SESSION=self.php._SESSION - self.GET=self.php._GET - self.POST=self.php._POST - self.REQUEST=self.php._ARG - self.FILES=self.php._FILES - else: - if isinstance(session,QWebSession): - self.SESSION=session - elif session: - self.SESSION=session(environ) - else: - self.SESSION=None - self.GET_LIST=QWebListDict(cgi.parse_qs(environ.get('QUERY_STRING', ''),keep_blank_values=1)) - self.POST_LIST=QWebListDict() - self.FILES_LIST=QWebListDict() - self.REQUEST_LIST=QWebListDict(self.GET_LIST) - if environ['REQUEST_METHOD'] == 'POST': - self.DATA=self.load_post_data(environ,self.POST_LIST,self.FILES_LIST) - self.REQUEST_LIST.update(self.POST_LIST) - self.GET=self.GET_LIST.get_qwebdict() - self.POST=self.POST_LIST.get_qwebdict() - self.FILES=self.FILES_LIST.get_qwebdict() - self.REQUEST=self.REQUEST_LIST.get_qwebdict() - def get_full_url(environ): - # taken from PEP 333 - if 'FULL_URL' in environ: - return environ['FULL_URL'] - url = environ['wsgi.url_scheme']+'://' - if environ.get('HTTP_HOST'): - url += environ['HTTP_HOST'] - else: - url += environ['SERVER_NAME'] - if environ['wsgi.url_scheme'] == 'https': - if environ['SERVER_PORT'] != '443': - url += ':' + environ['SERVER_PORT'] - else: - if environ['SERVER_PORT'] != '80': - url += ':' + environ['SERVER_PORT'] - if environ.has_key('REQUEST_URI'): - url += environ['REQUEST_URI'] - else: - url += urllib.quote(environ.get('SCRIPT_NAME', '')) - url += urllib.quote(environ.get('PATH_INFO', '')) - if environ.get('QUERY_STRING'): - url += '?' + environ['QUERY_STRING'] - return url - get_full_url=staticmethod(get_full_url) - def save_files(self): - for k,v in self.FILES.items(): - if not v.has_key("tmp_file"): - f=tempfile.NamedTemporaryFile() - f.write(v["data"]) - f.flush() - v["tmp_file"]=f - v["tmp_name"]=f.name - def debug(self): - body='' - for name,d in [ - ("GET",self.GET), ("POST",self.POST), ("REQUEST",self.REQUEST), ("FILES",self.FILES), - ("GET_LIST",self.GET_LIST), ("POST_LIST",self.POST_LIST), ("REQUEST_LIST",self.REQUEST_LIST), ("FILES_LIST",self.FILES_LIST), - ("SESSION",self.SESSION), ("environ",self.environ), - ]: - body+='<table border="1" width="100%" align="center">\n' - body+='<tr><th colspan="2" align="center">%s</th></tr>\n'%name - keys=d.keys() - keys.sort() - body+=''.join(['<tr><td>%s</td><td>%s</td></tr>\n'%(k,cgi.escape(repr(d[k]))) for k in keys]) - body+='</table><br><br>\n\n' - return body - def write(self,s): - self.buffer.append(s) - def echo(self,*s): - self.buffer.extend([str(i) for i in s]) - def response(self): - if not self.response_started: - if not self.php: - for k,v in self.FILES.items(): - if v.has_key("tmp_file"): - try: - v["tmp_file"].close() - except OSError: - pass - if self.response_gzencode and self.environ.get('HTTP_ACCEPT_ENCODING','').find('gzip')!=-1: - zbuf=StringIO.StringIO() - zfile=gzip.GzipFile(mode='wb', fileobj=zbuf) - zfile.write(''.join(self.buffer)) - zfile.close() - zbuf=zbuf.getvalue() - self.buffer=[zbuf] - self.response_headers['Content-Encoding']="gzip" - self.response_headers['Content-Length']=str(len(zbuf)) - headers = self.response_headers.get() - if isinstance(self.SESSION, QWebSession): - headers.extend(self.SESSION.session_get_headers()) - headers.extend([('Set-Cookie', self.response_cookies[i].OutputString()) for i in self.response_cookies]) - self.start_response(self.response_status, headers) - self.response_started=True - return self.buffer - def __iter__(self): - return self.response().__iter__() - def http_redirect(self,url,permanent=1): - if permanent: - self.response_status="301 Moved Permanently" - else: - self.response_status="302 Found" - self.response_headers["Location"]=url - def http_404(self,msg="<h1>404 Not Found</h1>"): - self.response_status="404 Not Found" - if msg: - self.write(msg) - def http_download(self,fname,fstr,partial=0): -# allow fstr to be a file-like object -# if parital: -# say accept ranages -# parse range headers... -# if range: -# header("HTTP/1.1 206 Partial Content"); -# header("Content-Range: bytes $offset-".($fsize-1)."/".$fsize); -# header("Content-Length: ".($fsize-$offset)); -# fseek($fd,$offset); -# else: - self.response_headers["Content-Type"]="application/octet-stream" - self.response_headers["Content-Disposition"]="attachment; filename=\"%s\""%fname - self.response_headers["Content-Transfer-Encoding"]="binary" - self.response_headers["Content-Length"]="%d"%len(fstr) - self.write(fstr) - -#---------------------------------------------------------- -# QWeb WSGI HTTP Server to run any WSGI app -# autorun, run an app as FCGI or CGI otherwise launch the server -#---------------------------------------------------------- -class QWebWSGIHandler(BaseHTTPServer.BaseHTTPRequestHandler): - def log_message(self,*p): - if self.server.log: - return BaseHTTPServer.BaseHTTPRequestHandler.log_message(self,*p) - def address_string(self): - return self.client_address[0] - def start_response(self,status,headers): - l=status.split(' ',1) - self.send_response(int(l[0]),l[1]) - ctype_sent=0 - for i in headers: - if i[0].lower()=="content-type": - ctype_sent=1 - self.send_header(*i) - if not ctype_sent: - self.send_header("Content-type", "text/html") - self.end_headers() - return self.write - def write(self,data): - try: - self.wfile.write(data) - except (socket.error, socket.timeout),e: - print e - def bufferon(self): - if not getattr(self,'wfile_buf',0): - self.wfile_buf=1 - self.wfile_bak=self.wfile - self.wfile=StringIO.StringIO() - def bufferoff(self): - if self.wfile_buf: - buf=self.wfile - self.wfile=self.wfile_bak - self.write(buf.getvalue()) - self.wfile_buf=0 - def serve(self,type): - path_info, parameters, query = urlparse.urlparse(self.path)[2:5] - environ = { - 'wsgi.version': (1,0), - 'wsgi.url_scheme': 'http', - 'wsgi.input': self.rfile, - 'wsgi.errors': sys.stderr, - 'wsgi.multithread': 0, - 'wsgi.multiprocess': 0, - 'wsgi.run_once': 0, - 'REQUEST_METHOD': self.command, - 'SCRIPT_NAME': '', - 'QUERY_STRING': query, - 'CONTENT_TYPE': self.headers.get('Content-Type', ''), - 'CONTENT_LENGTH': self.headers.get('Content-Length', ''), - 'REMOTE_ADDR': self.client_address[0], - 'REMOTE_PORT': str(self.client_address[1]), - 'SERVER_NAME': self.server.server_address[0], - 'SERVER_PORT': str(self.server.server_address[1]), - 'SERVER_PROTOCOL': self.request_version, - # extention - 'FULL_PATH': self.path, - 'qweb.mode': 'standalone', - } - if path_info: - environ['PATH_INFO'] = urllib.unquote(path_info) - for key, value in self.headers.items(): - environ['HTTP_' + key.upper().replace('-', '_')] = value - # Hack to avoid may TCP packets - self.bufferon() - appiter=self.server.wsgiapp(environ, self.start_response) - for data in appiter: - self.write(data) - self.bufferoff() - self.bufferoff() - def do_GET(self): - self.serve('GET') - def do_POST(self): - self.serve('GET') -class QWebWSGIServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer): - """ QWebWSGIServer - qweb_wsgi_autorun(wsgiapp,ip='127.0.0.1',port=8080,threaded=1) - A WSGI HTTP server threaded or not and a function to automatically run your - app according to the environement (either standalone, CGI or FastCGI). - - This feature is called QWeb autorun. If you want to To use it on your - application use the following lines at the end of the main application - python file: - - if __name__ == '__main__': - qweb.qweb_wsgi_autorun(your_wsgi_app) - - this function will select the approriate running mode according to the - calling environement (http-server, FastCGI or CGI). - """ - def __init__(self, wsgiapp, ip, port, threaded=1, log=1): - BaseHTTPServer.HTTPServer.__init__(self, (ip, port), QWebWSGIHandler) - self.wsgiapp = wsgiapp - self.threaded = threaded - self.log = log - def process_request(self,*p): - if self.threaded: - return SocketServer.ThreadingMixIn.process_request(self,*p) - else: - return BaseHTTPServer.HTTPServer.process_request(self,*p) -def qweb_wsgi_autorun(wsgiapp,ip='127.0.0.1',port=8080,threaded=1,log=1,callback_ready=None): - if sys.platform=='win32': - fcgi=0 - else: - fcgi=1 - sock = socket.fromfd(0, socket.AF_INET, socket.SOCK_STREAM) - try: - sock.getpeername() - except socket.error, e: - if e[0] == errno.ENOTSOCK: - fcgi=0 - if fcgi or os.environ.has_key('REQUEST_METHOD'): - import fcgi - fcgi.WSGIServer(wsgiapp,multithreaded=False).run() - else: - if log: - print 'Serving on %s:%d'%(ip,port) - s=QWebWSGIServer(wsgiapp,ip=ip,port=port,threaded=threaded,log=log) - if callback_ready: - callback_ready() - try: - s.serve_forever() - except KeyboardInterrupt,e: - sys.excepthook(*sys.exc_info()) - -#---------------------------------------------------------- -# Qweb Documentation -#---------------------------------------------------------- -def qweb_doc(): - body=__doc__ - for i in [QWebXml ,QWebHtml ,QWebForm ,QWebURL ,qweb_control ,QWebRequest ,QWebSession ,QWebWSGIServer ,qweb_wsgi_autorun]: - n=i.__name__ - d=i.__doc__ - body+='\n\n%s\n%s\n\n%s'%(n,'-'*len(n),d) - return body - - print qweb_doc() - -# diff --git a/tools/ajaxterm/sarissa.js b/tools/ajaxterm/sarissa.js deleted file mode 100644 index 6d13aa2e2..000000000 --- a/tools/ajaxterm/sarissa.js +++ /dev/null @@ -1,647 +0,0 @@ -/** - * ==================================================================== - * About - * ==================================================================== - * Sarissa is an ECMAScript library acting as a cross-browser wrapper for native XML APIs. - * The library supports Gecko based browsers like Mozilla and Firefox, - * Internet Explorer (5.5+ with MSXML3.0+), Konqueror, Safari and a little of Opera - * @version 0.9.6.1 - * @author: Manos Batsis, mailto: mbatsis at users full stop sourceforge full stop net - * ==================================================================== - * Licence - * ==================================================================== - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 or - * the GNU Lesser General Public License version 2.1 as published by - * the Free Software Foundation (your choice between the two). - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License or GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * or GNU Lesser General Public License along with this program; if not, - * write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * or visit http://www.gnu.org - * - */ -/** - * <p>Sarissa is a utility class. Provides "static" methods for DOMDocument and - * XMLHTTP objects, DOM Node serializatrion to XML strings and other goodies.</p> - * @constructor - */ -function Sarissa(){}; -/** @private */ -Sarissa.PARSED_OK = "Document contains no parsing errors"; -/** - * Tells you whether transformNode and transformNodeToObject are available. This functionality - * is contained in sarissa_ieemu_xslt.js and is deprecated. If you want to control XSLT transformations - * use the XSLTProcessor - * @deprecated - * @type boolean - */ -Sarissa.IS_ENABLED_TRANSFORM_NODE = false; -/** - * tells you whether XMLHttpRequest (or equivalent) is available - * @type boolean - */ -Sarissa.IS_ENABLED_XMLHTTP = false; -/** - * tells you whether selectNodes/selectSingleNode is available - * @type boolean - */ -Sarissa.IS_ENABLED_SELECT_NODES = false; -var _sarissa_iNsCounter = 0; -var _SARISSA_IEPREFIX4XSLPARAM = ""; -var _SARISSA_HAS_DOM_IMPLEMENTATION = document.implementation && true; -var _SARISSA_HAS_DOM_CREATE_DOCUMENT = _SARISSA_HAS_DOM_IMPLEMENTATION && document.implementation.createDocument; -var _SARISSA_HAS_DOM_FEATURE = _SARISSA_HAS_DOM_IMPLEMENTATION && document.implementation.hasFeature; -var _SARISSA_IS_MOZ = _SARISSA_HAS_DOM_CREATE_DOCUMENT && _SARISSA_HAS_DOM_FEATURE; -var _SARISSA_IS_SAFARI = (navigator.userAgent && navigator.vendor && (navigator.userAgent.toLowerCase().indexOf("applewebkit") != -1 || navigator.vendor.indexOf("Apple") != -1)); -var _SARISSA_IS_IE = document.all && window.ActiveXObject && navigator.userAgent.toLowerCase().indexOf("msie") > -1 && navigator.userAgent.toLowerCase().indexOf("opera") == -1; -if(!window.Node || !window.Node.ELEMENT_NODE){ - var Node = {ELEMENT_NODE: 1, ATTRIBUTE_NODE: 2, TEXT_NODE: 3, CDATA_SECTION_NODE: 4, ENTITY_REFERENCE_NODE: 5, ENTITY_NODE: 6, PROCESSING_INSTRUCTION_NODE: 7, COMMENT_NODE: 8, DOCUMENT_NODE: 9, DOCUMENT_TYPE_NODE: 10, DOCUMENT_FRAGMENT_NODE: 11, NOTATION_NODE: 12}; -}; - -// IE initialization -if(_SARISSA_IS_IE){ - // for XSLT parameter names, prefix needed by IE - _SARISSA_IEPREFIX4XSLPARAM = "xsl:"; - // used to store the most recent ProgID available out of the above - var _SARISSA_DOM_PROGID = ""; - var _SARISSA_XMLHTTP_PROGID = ""; - /** - * Called when the Sarissa_xx.js file is parsed, to pick most recent - * ProgIDs for IE, then gets destroyed. - * @param idList an array of MSXML PROGIDs from which the most recent will be picked for a given object - * @param enabledList an array of arrays where each array has two items; the index of the PROGID for which a certain feature is enabled - */ - pickRecentProgID = function (idList, enabledList){ - // found progID flag - var bFound = false; - for(var i=0; i < idList.length && !bFound; i++){ - try{ - var oDoc = new ActiveXObject(idList[i]); - o2Store = idList[i]; - bFound = true; - for(var j=0;j<enabledList.length;j++) - if(i <= enabledList[j][1]) - Sarissa["IS_ENABLED_"+enabledList[j][0]] = true; - }catch (objException){ - // trap; try next progID - }; - }; - if (!bFound) - throw "Could not retreive a valid progID of Class: " + idList[idList.length-1]+". (original exception: "+e+")"; - idList = null; - return o2Store; - }; - // pick best available MSXML progIDs - _SARISSA_DOM_PROGID = pickRecentProgID(["Msxml2.DOMDocument.5.0", "Msxml2.DOMDocument.4.0", "Msxml2.DOMDocument.3.0", "MSXML2.DOMDocument", "MSXML.DOMDocument", "Microsoft.XMLDOM"], [["SELECT_NODES", 2],["TRANSFORM_NODE", 2]]); - _SARISSA_XMLHTTP_PROGID = pickRecentProgID(["Msxml2.XMLHTTP.5.0", "Msxml2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"], [["XMLHTTP", 4]]); - _SARISSA_THREADEDDOM_PROGID = pickRecentProgID(["Msxml2.FreeThreadedDOMDocument.5.0", "MSXML2.FreeThreadedDOMDocument.4.0", "MSXML2.FreeThreadedDOMDocument.3.0"]); - _SARISSA_XSLTEMPLATE_PROGID = pickRecentProgID(["Msxml2.XSLTemplate.5.0", "Msxml2.XSLTemplate.4.0", "MSXML2.XSLTemplate.3.0"], [["XSLTPROC", 2]]); - // we dont need this anymore - pickRecentProgID = null; - //============================================ - // Factory methods (IE) - //============================================ - // see non-IE version - Sarissa.getDomDocument = function(sUri, sName){ - var oDoc = new ActiveXObject(_SARISSA_DOM_PROGID); - // if a root tag name was provided, we need to load it in the DOM - // object - if (sName){ - // if needed, create an artifical namespace prefix the way Moz - // does - if (sUri){ - oDoc.loadXML("<a" + _sarissa_iNsCounter + ":" + sName + " xmlns:a" + _sarissa_iNsCounter + "=\"" + sUri + "\" />"); - // don't use the same prefix again - ++_sarissa_iNsCounter; - } - else - oDoc.loadXML("<" + sName + "/>"); - }; - return oDoc; - }; - // see non-IE version - Sarissa.getParseErrorText = function (oDoc) { - var parseErrorText = Sarissa.PARSED_OK; - if(oDoc.parseError != 0){ - parseErrorText = "XML Parsing Error: " + oDoc.parseError.reason + - "\nLocation: " + oDoc.parseError.url + - "\nLine Number " + oDoc.parseError.line + ", Column " + - oDoc.parseError.linepos + - ":\n" + oDoc.parseError.srcText + - "\n"; - for(var i = 0; i < oDoc.parseError.linepos;i++){ - parseErrorText += "-"; - }; - parseErrorText += "^\n"; - }; - return parseErrorText; - }; - // see non-IE version - Sarissa.setXpathNamespaces = function(oDoc, sNsSet) { - oDoc.setProperty("SelectionLanguage", "XPath"); - oDoc.setProperty("SelectionNamespaces", sNsSet); - }; - /** - * Basic implementation of Mozilla's XSLTProcessor for IE. - * Reuses the same XSLT stylesheet for multiple transforms - * @constructor - */ - XSLTProcessor = function(){ - this.template = new ActiveXObject(_SARISSA_XSLTEMPLATE_PROGID); - this.processor = null; - }; - /** - * Impoprts the given XSLT DOM and compiles it to a reusable transform - * @argument xslDoc The XSLT DOMDocument to import - */ - XSLTProcessor.prototype.importStylesheet = function(xslDoc){ - // convert stylesheet to free threaded - var converted = new ActiveXObject(_SARISSA_THREADEDDOM_PROGID); - converted.loadXML(xslDoc.xml); - this.template.stylesheet = converted; - this.processor = this.template.createProcessor(); - // (re)set default param values - this.paramsSet = new Array(); - }; - /** - * Transform the given XML DOM - * @argument sourceDoc The XML DOMDocument to transform - * @return The transformation result as a DOM Document - */ - XSLTProcessor.prototype.transformToDocument = function(sourceDoc){ - this.processor.input = sourceDoc; - var outDoc = new ActiveXObject(_SARISSA_DOM_PROGID); - this.processor.output = outDoc; - this.processor.transform(); - return outDoc; - }; - /** - * Set global XSLT parameter of the imported stylesheet - * @argument nsURI The parameter namespace URI - * @argument name The parameter base name - * @argument value The new parameter value - */ - XSLTProcessor.prototype.setParameter = function(nsURI, name, value){ - /* nsURI is optional but cannot be null */ - if(nsURI){ - this.processor.addParameter(name, value, nsURI); - }else{ - this.processor.addParameter(name, value); - }; - /* update updated params for getParameter */ - if(!this.paramsSet[""+nsURI]){ - this.paramsSet[""+nsURI] = new Array(); - }; - this.paramsSet[""+nsURI][name] = value; - }; - /** - * Gets a parameter if previously set by setParameter. Returns null - * otherwise - * @argument name The parameter base name - * @argument value The new parameter value - * @return The parameter value if reviously set by setParameter, null otherwise - */ - XSLTProcessor.prototype.getParameter = function(nsURI, name){ - nsURI = nsURI || ""; - if(nsURI in this.paramsSet && name in this.paramsSet[nsURI]){ - return this.paramsSet[nsURI][name]; - }else{ - return null; - }; - }; -} -else{ /* end IE initialization, try to deal with real browsers now ;-) */ - if(_SARISSA_HAS_DOM_CREATE_DOCUMENT){ - /** - * <p>Ensures the document was loaded correctly, otherwise sets the - * parseError to -1 to indicate something went wrong. Internal use</p> - * @private - */ - Sarissa.__handleLoad__ = function(oDoc){ - if (!oDoc.documentElement || oDoc.documentElement.tagName == "parsererror") - oDoc.parseError = -1; - Sarissa.__setReadyState__(oDoc, 4); - }; - /** - * <p>Attached by an event handler to the load event. Internal use.</p> - * @private - */ - _sarissa_XMLDocument_onload = function(){ - Sarissa.__handleLoad__(this); - }; - /** - * <p>Sets the readyState property of the given DOM Document object. - * Internal use.</p> - * @private - * @argument oDoc the DOM Document object to fire the - * readystatechange event - * @argument iReadyState the number to change the readystate property to - */ - Sarissa.__setReadyState__ = function(oDoc, iReadyState){ - oDoc.readyState = iReadyState; - if (oDoc.onreadystatechange != null && typeof oDoc.onreadystatechange == "function") - oDoc.onreadystatechange(); - }; - Sarissa.getDomDocument = function(sUri, sName){ - var oDoc = document.implementation.createDocument(sUri?sUri:"", sName?sName:"", null); - oDoc.addEventListener("load", _sarissa_XMLDocument_onload, false); - return oDoc; - }; - if(false && window.XMLDocument){ - /** - * <p>Emulate IE's onreadystatechange attribute</p> - */ - XMLDocument.prototype.onreadystatechange = null; - /** - * <p>Emulates IE's readyState property, which always gives an integer from 0 to 4:</p> - * <ul><li>1 == LOADING,</li> - * <li>2 == LOADED,</li> - * <li>3 == INTERACTIVE,</li> - * <li>4 == COMPLETED</li></ul> - */ - XMLDocument.prototype.readyState = 0; - /** - * <p>Emulate IE's parseError attribute</p> - */ - XMLDocument.prototype.parseError = 0; - - // NOTE: setting async to false will only work with documents - // called over HTTP (meaning a server), not the local file system, - // unless you are using Moz 1.4+. - // BTW the try>catch block is for 1.4; I haven't found a way to check if - // the property is implemented without - // causing an error and I dont want to use user agent stuff for that... - var _SARISSA_SYNC_NON_IMPLEMENTED = false;// ("async" in XMLDocument.prototype) ? false: true; - /** - * <p>Keeps a handle to the original load() method. Internal use and only - * if Mozilla version is lower than 1.4</p> - * @private - */ - XMLDocument.prototype._sarissa_load = XMLDocument.prototype.load; - - /** - * <p>Overrides the original load method to provide synchronous loading for - * Mozilla versions prior to 1.4, using an XMLHttpRequest object (if - * async is set to false)</p> - * @returns the DOM Object as it was before the load() call (may be empty) - */ - XMLDocument.prototype.load = function(sURI) { - var oDoc = document.implementation.createDocument("", "", null); - Sarissa.copyChildNodes(this, oDoc); - this.parseError = 0; - Sarissa.__setReadyState__(this, 1); - try { - if(this.async == false && _SARISSA_SYNC_NON_IMPLEMENTED) { - var tmp = new XMLHttpRequest(); - tmp.open("GET", sURI, false); - tmp.send(null); - Sarissa.__setReadyState__(this, 2); - Sarissa.copyChildNodes(tmp.responseXML, this); - Sarissa.__setReadyState__(this, 3); - } - else { - this._sarissa_load(sURI); - }; - } - catch (objException) { - this.parseError = -1; - } - finally { - if(this.async == false){ - Sarissa.__handleLoad__(this); - }; - }; - return oDoc; - }; - - - }//if(window.XMLDocument) - else if(document.implementation && document.implementation.hasFeature && document.implementation.hasFeature('LS', '3.0')){ - Document.prototype.async = true; - Document.prototype.onreadystatechange = null; - Document.prototype.parseError = 0; - Document.prototype.load = function(sURI) { - var parser = document.implementation.createLSParser(this.async ? document.implementation.MODE_ASYNCHRONOUS : document.implementation.MODE_SYNCHRONOUS, null); - if(this.async){ - var self = this; - parser.addEventListener("load", - function(e) { - self.readyState = 4; - Sarissa.copyChildNodes(e.newDocument, self.documentElement, false); - self.onreadystatechange.call(); - }, - false); - }; - try { - var oDoc = parser.parseURI(sURI); - } - catch(e){ - this.parseError = -1; - }; - if(!this.async) - Sarissa.copyChildNodes(oDoc, this.documentElement, false); - return oDoc; - }; - /** - * <p>Factory method to obtain a new DOM Document object</p> - * @argument sUri the namespace of the root node (if any) - * @argument sUri the local name of the root node (if any) - * @returns a new DOM Document - */ - Sarissa.getDomDocument = function(sUri, sName){ - return document.implementation.createDocument(sUri?sUri:"", sName?sName:"", null); - }; - }; - };//if(_SARISSA_HAS_DOM_CREATE_DOCUMENT) -}; -//========================================== -// Common stuff -//========================================== -if(!window.DOMParser){ - /* - * DOMParser is a utility class, used to construct DOMDocuments from XML strings - * @constructor - */ - DOMParser = function() { - }; - if(_SARISSA_IS_SAFARI){ - /** - * Construct a new DOM Document from the given XMLstring - * @param sXml the given XML string - * @param contentType the content type of the document the given string represents (one of text/xml, application/xml, application/xhtml+xml). - * @return a new DOM Document from the given XML string - */ - DOMParser.prototype.parseFromString = function(sXml, contentType){ - if(contentType.toLowerCase() != "application/xml"){ - throw "Cannot handle content type: \"" + contentType + "\""; - }; - var xmlhttp = new XMLHttpRequest(); - xmlhttp.open("GET", "data:text/xml;charset=utf-8," + encodeURIComponent(str), false); - xmlhttp.send(null); - return xmlhttp.responseXML; - }; - }else if(Sarissa.getDomDocument && Sarissa.getDomDocument() && "loadXML" in Sarissa.getDomDocument()){ - DOMParser.prototype.parseFromString = function(sXml, contentType){ - var doc = Sarissa.getDomDocument(); - doc.loadXML(sXml); - return doc; - }; - }; -}; - -if(window.XMLHttpRequest){ - Sarissa.IS_ENABLED_XMLHTTP = true; -} -else if(_SARISSA_IS_IE){ - /** - * Emulate XMLHttpRequest - * @constructor - */ - XMLHttpRequest = function() { - return new ActiveXObject(_SARISSA_XMLHTTP_PROGID); - }; - Sarissa.IS_ENABLED_XMLHTTP = true; -}; - -if(!window.document.importNode && _SARISSA_IS_IE){ - try{ - /** - * Implements importNode for the current window document in IE using innerHTML. - * Testing showed that DOM was multiple times slower than innerHTML for this, - * sorry folks. If you encounter trouble (who knows what IE does behind innerHTML) - * please gimme a call. - * @param oNode the Node to import - * @param bChildren whether to include the children of oNode - * @returns the imported node for further use - */ - window.document.importNode = function(oNode, bChildren){ - var importNode = document.createElement("div"); - if(bChildren) - importNode.innerHTML = Sarissa.serialize(oNode); - else - importNode.innerHTML = Sarissa.serialize(oNode.cloneNode(false)); - return importNode.firstChild; - }; - }catch(e){}; -}; -if(!Sarissa.getParseErrorText){ - /** - * <p>Returns a human readable description of the parsing error. Usefull - * for debugging. Tip: append the returned error string in a <pre> - * element if you want to render it.</p> - * <p>Many thanks to Christian Stocker for the initial patch.</p> - * @argument oDoc The target DOM document - * @returns The parsing error description of the target Document in - * human readable form (preformated text) - */ - Sarissa.getParseErrorText = function (oDoc){ - var parseErrorText = Sarissa.PARSED_OK; - if(oDoc && oDoc.parseError && oDoc.parseError != 0){ - /*moz*/ - if(oDoc.documentElement.tagName == "parsererror"){ - parseErrorText = oDoc.documentElement.firstChild.data; - parseErrorText += "\n" + oDoc.documentElement.firstChild.nextSibling.firstChild.data; - }/*konq*/ - else{ - parseErrorText = Sarissa.getText(oDoc.documentElement);/*.getElementsByTagName("h1")[0], false) + "\n"; - parseErrorText += Sarissa.getText(oDoc.documentElement.getElementsByTagName("body")[0], false) + "\n"; - parseErrorText += Sarissa.getText(oDoc.documentElement.getElementsByTagName("pre")[0], false);*/ - }; - }; - return parseErrorText; - }; -}; -Sarissa.getText = function(oNode, deep){ - var s = ""; - var nodes = oNode.childNodes; - for(var i=0; i < nodes.length; i++){ - var node = nodes[i]; - var nodeType = node.nodeType; - if(nodeType == Node.TEXT_NODE || nodeType == Node.CDATA_SECTION_NODE){ - s += node.data; - }else if(deep == true - && (nodeType == Node.ELEMENT_NODE - || nodeType == Node.DOCUMENT_NODE - || nodeType == Node.DOCUMENT_FRAGMENT_NODE)){ - s += Sarissa.getText(node, true); - }; - }; - return s; -}; -if(window.XMLSerializer){ - /** - * <p>Factory method to obtain the serialization of a DOM Node</p> - * @returns the serialized Node as an XML string - */ - Sarissa.serialize = function(oDoc){ - var s = null; - if(oDoc){ - s = oDoc.innerHTML?oDoc.innerHTML:(new XMLSerializer()).serializeToString(oDoc); - }; - return s; - }; -}else{ - if(Sarissa.getDomDocument && (Sarissa.getDomDocument("","foo", null)).xml){ - // see non-IE version - Sarissa.serialize = function(oDoc) { - var s = null; - if(oDoc){ - s = oDoc.innerHTML?oDoc.innerHTML:oDoc.xml; - }; - return s; - }; - /** - * Utility class to serialize DOM Node objects to XML strings - * @constructor - */ - XMLSerializer = function(){}; - /** - * Serialize the given DOM Node to an XML string - * @param oNode the DOM Node to serialize - */ - XMLSerializer.prototype.serializeToString = function(oNode) { - return oNode.xml; - }; - }; -}; - -/** - * strips tags from a markup string - */ -Sarissa.stripTags = function (s) { - return s.replace(/<[^>]+>/g,""); -}; -/** - * <p>Deletes all child nodes of the given node</p> - * @argument oNode the Node to empty - */ -Sarissa.clearChildNodes = function(oNode) { - // need to check for firstChild due to opera 8 bug with hasChildNodes - while(oNode.firstChild){ - oNode.removeChild(oNode.firstChild); - }; -}; -/** - * <p> Copies the childNodes of nodeFrom to nodeTo</p> - * <p> <b>Note:</b> The second object's original content is deleted before - * the copy operation, unless you supply a true third parameter</p> - * @argument nodeFrom the Node to copy the childNodes from - * @argument nodeTo the Node to copy the childNodes to - * @argument bPreserveExisting whether to preserve the original content of nodeTo, default is false - */ -Sarissa.copyChildNodes = function(nodeFrom, nodeTo, bPreserveExisting) { - if((!nodeFrom) || (!nodeTo)){ - throw "Both source and destination nodes must be provided"; - }; - if(!bPreserveExisting){ - Sarissa.clearChildNodes(nodeTo); - }; - var ownerDoc = nodeTo.nodeType == Node.DOCUMENT_NODE ? nodeTo : nodeTo.ownerDocument; - var nodes = nodeFrom.childNodes; - if(ownerDoc.importNode && (!_SARISSA_IS_IE)) { - for(var i=0;i < nodes.length;i++) { - nodeTo.appendChild(ownerDoc.importNode(nodes[i], true)); - }; - } - else{ - for(var i=0;i < nodes.length;i++) { - nodeTo.appendChild(nodes[i].cloneNode(true)); - }; - }; -}; - -/** - * <p> Moves the childNodes of nodeFrom to nodeTo</p> - * <p> <b>Note:</b> The second object's original content is deleted before - * the move operation, unless you supply a true third parameter</p> - * @argument nodeFrom the Node to copy the childNodes from - * @argument nodeTo the Node to copy the childNodes to - * @argument bPreserveExisting whether to preserve the original content of nodeTo, default is - */ -Sarissa.moveChildNodes = function(nodeFrom, nodeTo, bPreserveExisting) { - if((!nodeFrom) || (!nodeTo)){ - throw "Both source and destination nodes must be provided"; - }; - if(!bPreserveExisting){ - Sarissa.clearChildNodes(nodeTo); - }; - var nodes = nodeFrom.childNodes; - // if within the same doc, just move, else copy and delete - if(nodeFrom.ownerDocument == nodeTo.ownerDocument){ - while(nodeFrom.firstChild){ - nodeTo.appendChild(nodeFrom.firstChild); - }; - }else{ - var ownerDoc = nodeTo.nodeType == Node.DOCUMENT_NODE ? nodeTo : nodeTo.ownerDocument; - if(ownerDoc.importNode && (!_SARISSA_IS_IE)) { - for(var i=0;i < nodes.length;i++) { - nodeTo.appendChild(ownerDoc.importNode(nodes[i], true)); - }; - }else{ - for(var i=0;i < nodes.length;i++) { - nodeTo.appendChild(nodes[i].cloneNode(true)); - }; - }; - Sarissa.clearChildNodes(nodeFrom); - }; -}; - -/** - * <p>Serialize any object to an XML string. All properties are serialized using the property name - * as the XML element name. Array elements are rendered as <code>array-item</code> elements, - * using their index/key as the value of the <code>key</code> attribute.</p> - * @argument anyObject the object to serialize - * @argument objectName a name for that object - * @return the XML serializationj of the given object as a string - */ -Sarissa.xmlize = function(anyObject, objectName, indentSpace){ - indentSpace = indentSpace?indentSpace:''; - var s = indentSpace + '<' + objectName + '>'; - var isLeaf = false; - if(!(anyObject instanceof Object) || anyObject instanceof Number || anyObject instanceof String - || anyObject instanceof Boolean || anyObject instanceof Date){ - s += Sarissa.escape(""+anyObject); - isLeaf = true; - }else{ - s += "\n"; - var itemKey = ''; - var isArrayItem = anyObject instanceof Array; - for(var name in anyObject){ - s += Sarissa.xmlize(anyObject[name], (isArrayItem?"array-item key=\""+name+"\"":name), indentSpace + " "); - }; - s += indentSpace; - }; - return s += (objectName.indexOf(' ')!=-1?"</array-item>\n":"</" + objectName + ">\n"); -}; - -/** - * Escape the given string chacters that correspond to the five predefined XML entities - * @param sXml the string to escape - */ -Sarissa.escape = function(sXml){ - return sXml.replace(/&/g, "&") - .replace(/</g, "<") - .replace(/>/g, ">") - .replace(/"/g, """) - .replace(/'/g, "'"); -}; - -/** - * Unescape the given string. This turns the occurences of the predefined XML - * entities to become the characters they represent correspond to the five predefined XML entities - * @param sXml the string to unescape - */ -Sarissa.unescape = function(sXml){ - return sXml.replace(/'/g,"'") - .replace(/"/g,"\"") - .replace(/>/g,">") - .replace(/</g,"<") - .replace(/&/g,"&"); -}; -// EOF diff --git a/tools/ajaxterm/sarissa_dhtml.js b/tools/ajaxterm/sarissa_dhtml.js deleted file mode 100644 index 2d85c817e..000000000 --- a/tools/ajaxterm/sarissa_dhtml.js +++ /dev/null @@ -1,105 +0,0 @@ -/** - * ==================================================================== - * About - * ==================================================================== - * Sarissa cross browser XML library - AJAX module - * @version 0.9.6.1 - * @author: Copyright Manos Batsis, mailto: mbatsis at users full stop sourceforge full stop net - * - * This module contains some convinient AJAX tricks based on Sarissa - * - * ==================================================================== - * Licence - * ==================================================================== - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 or - * the GNU Lesser General Public License version 2.1 as published by - * the Free Software Foundation (your choice between the two). - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License or GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * or GNU Lesser General Public License along with this program; if not, - * write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * or visit http://www.gnu.org - * - */ -/** - * Update an element with response of a GET request on the given URL. - * @addon - * @param sFromUrl the URL to make the request to - * @param oTargetElement the element to update - * @param xsltproc (optional) the transformer to use on the returned - * content before updating the target element with it - */ -Sarissa.updateContentFromURI = function(sFromUrl, oTargetElement, xsltproc) { - try{ - oTargetElement.style.cursor = "wait"; - var xmlhttp = new XMLHttpRequest(); - xmlhttp.open("GET", sFromUrl); - function sarissa_dhtml_loadHandler() { - if (xmlhttp.readyState == 4) { - oTargetElement.style.cursor = "auto"; - Sarissa.updateContentFromNode(xmlhttp.responseXML, oTargetElement, xsltproc); - }; - }; - xmlhttp.onreadystatechange = sarissa_dhtml_loadHandler; - xmlhttp.send(null); - oTargetElement.style.cursor = "auto"; - } - catch(e){ - oTargetElement.style.cursor = "auto"; - throw e; - }; -}; - -/** - * Update an element's content with the given DOM node. - * @addon - * @param sFromUrl the URL to make the request to - * @param oTargetElement the element to update - * @param xsltproc (optional) the transformer to use on the given - * DOM node before updating the target element with it - */ -Sarissa.updateContentFromNode = function(oNode, oTargetElement, xsltproc) { - try { - oTargetElement.style.cursor = "wait"; - Sarissa.clearChildNodes(oTargetElement); - // check for parsing errors - var ownerDoc = oNode.nodeType == Node.DOCUMENT_NODE?oNode:oNode.ownerDocument; - if(ownerDoc.parseError && ownerDoc.parseError != 0) { - var pre = document.createElement("pre"); - pre.appendChild(document.createTextNode(Sarissa.getParseErrorText(ownerDoc))); - oTargetElement.appendChild(pre); - } - else { - // transform if appropriate - if(xsltproc) { - oNode = xsltproc.transformToDocument(oNode); - }; - // be smart, maybe the user wants to display the source instead - if(oTargetElement.tagName.toLowerCase == "textarea" || oTargetElement.tagName.toLowerCase == "input") { - oTargetElement.value = Sarissa.serialize(oNode); - } - else { - // ok that was not smart; it was paranoid. Keep up the good work by trying to use DOM instead of innerHTML - if(oNode.nodeType == Node.DOCUMENT_NODE || oNode.ownerDocument.documentElement == oNode) { - oTargetElement.innerHTML = Sarissa.serialize(oNode); - } - else{ - oTargetElement.appendChild(oTargetElement.ownerDocument.importNode(oNode, true)); - }; - }; - }; - } - catch(e) { - throw e; - } - finally{ - oTargetElement.style.cursor = "auto"; - }; -}; - diff --git a/tools/euca-get-ajax-console b/tools/euca-get-ajax-console deleted file mode 100755 index 26236f1d9..000000000 --- a/tools/euca-get-ajax-console +++ /dev/null @@ -1,169 +0,0 @@ -#!/usr/bin/env python -# pylint: disable=C0103 -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Euca add-on to use ajax console""" - -import getopt -import os -import sys - -# If ../nova/__init__.py exists, add ../ to Python search path, so that -# it will override what happens to be installed in /usr/(local/)lib/python... -possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), - os.pardir, - os.pardir)) -if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): - sys.path.insert(0, possible_topdir) - -import boto -import nova -import boto.ec2.connection -import euca2ools - -usage_string = """ -Retrieves a url to an ajax console terminal - -euca-get-ajax-console [-h, --help] [--version] [--debug] instance_id - -REQUIRED PARAMETERS - -instance_id: unique identifier for the instance show the console output for. - -OPTIONAL PARAMETERS - -""" - - -# This class extends boto to add AjaxConsole functionality -class NovaEC2Connection(boto.ec2.connection.EC2Connection): - - def get_ajax_console(self, instance_id): - """ - Retrieves a console connection for the specified instance. - - :type instance_id: string - :param instance_id: The instance ID of a running instance on the cloud. - - :rtype: :class:`AjaxConsole` - """ - - class AjaxConsole: - def __init__(self, parent=None): - self.parent = parent - self.instance_id = None - self.url = None - - def startElement(self, name, attrs, connection): - return None - - def endElement(self, name, value, connection): - if name == 'instanceId': - self.instance_id = value - elif name == 'url': - self.url = value - else: - setattr(self, name, value) - - params = {} - self.build_list_params(params, [instance_id], 'InstanceId') - return self.get_object('GetAjaxConsole', params, AjaxConsole) - pass - - -def override_connect_ec2(aws_access_key_id=None, - aws_secret_access_key=None, **kwargs): - return NovaEC2Connection(aws_access_key_id, - aws_secret_access_key, **kwargs) - -# override boto's connect_ec2 method, so that we can use NovaEC2Connection -# (This is for Euca2ools 1.2) -boto.connect_ec2 = override_connect_ec2 - -# Override Euca2ools' EC2Connection class (which it gets from boto) -# (This is for Euca2ools 1.3) -euca2ools.EC2Connection = NovaEC2Connection - - -def usage(status=1): - print usage_string - euca2ools.Util().usage() - sys.exit(status) - - -def version(): - print euca2ools.Util().version() - sys.exit() - - -def display_console_output(console_output): - print console_output.instance_id - print console_output.timestamp - print console_output.output - - -def display_ajax_console_output(console_output): - print console_output.url - - -def main(): - try: - euca = euca2ools.Euca2ool() - except Exception, e: - print e - usage() - - instance_id = None - - for name, value in euca.opts: - if name in ('-h', '--help'): - usage(0) - elif name == '--version': - version() - elif name == '--debug': - debug = True - - for arg in euca.args: - instance_id = arg - break - - if instance_id: - try: - euca.validate_instance_id(instance_id) - except euca2ools.InstanceValidationError: - print 'Invalid instance id' - sys.exit(1) - - try: - euca_conn = euca.make_connection() - except Exception, e: - print e.message - sys.exit(1) - try: - console_output = euca_conn.get_ajax_console(instance_id) - except Exception, ex: - euca.display_error_and_exit('%s' % ex) - - display_ajax_console_output(console_output) - else: - print 'instance_id must be specified' - usage() - -if __name__ == "__main__": - main() |
