diff --git a/bin/nova-ajax-console-proxy b/bin/nova-ajax-console-proxy deleted file mode 100755 index 64e2b17..0000000 --- a/bin/nova-ajax-console-proxy +++ /dev/null @@ -1,143 +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 import rpc -from nova import service -from nova import utils -from nova import wsgi - -FLAGS = flags.FLAGS -flags.DEFINE_integer('ajax_console_idle_timeout', 300, - 'Seconds before idle connection destroyed') -flags.DEFINE_flag(flags.HelpFlag()) -flags.DEFINE_flag(flags.HelpshortFlag()) -flags.DEFINE_flag(flags.HelpXMLFlag()) - -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/api/ec2/cloud.py b/nova/api/ec2/cloud.py index eb115d8..dc840ac 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -818,15 +818,6 @@ class CloudController(object): "Timestamp": now, "output": base64.b64encode(output)} - def get_ajax_console(self, context, instance_id, **kwargs): - """Web based ajax terminal for vm. - - This is an extension to the normal ec2_api""" - ec2_id = instance_id[0] - instance_id = ec2utils.ec2_id_to_id(ec2_id) - instance = self.compute_api.get(context, instance_id) - return self.compute_api.get_ajax_console(context, instance) - def describe_volumes(self, context, volume_id=None, **kwargs): if volume_id: volumes = [] diff --git a/nova/compute/api.py b/nova/compute/api.py index ca28f38..e0759cd 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -1567,19 +1567,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 65985ca..a721db5 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -1492,15 +1492,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 4345b47..8a82757 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -282,14 +282,6 @@ DEFINE_string('scheduler_topic', 'scheduler', 'the topic scheduler nodes listen on') DEFINE_string('volume_topic', 'volume', 'the topic volume nodes listen on') DEFINE_string('network_topic', 'network', 'the topic network nodes listen on') -DEFINE_string('ajax_console_proxy_topic', 'ajax_proxy', - 'the topic ajax proxy nodes listen on') -DEFINE_string('ajax_console_proxy_url', - 'http://127.0.0.1:8000', - 'location of ajax console proxy, \ - in the form "http://127.0.0.1:8000"') -DEFINE_integer('ajax_console_proxy_port', - 8000, 'port that ajax_console_proxy binds') DEFINE_string('vsa_topic', 'vsa', 'the topic that nova-vsa service listens on') DEFINE_bool('verbose', False, 'show debug output') DEFINE_boolean('fake_rabbit', False, 'use a fake rabbit') diff --git a/nova/tests/api/ec2/test_cloud.py b/nova/tests/api/ec2/test_cloud.py index ffdb74c..e5d1cae 100644 --- a/nova/tests/api/ec2/test_cloud.py +++ b/nova/tests/api/ec2/test_cloud.py @@ -37,7 +37,6 @@ from nova import exception from nova import flags from nova.image import fake from nova import log as logging -from nova import manager from nova import rpc from nova import test from nova import utils @@ -47,17 +46,6 @@ FLAGS = flags.FLAGS LOG = logging.getLogger('nova.tests.cloud') -flags.DEFINE_string('ajax_proxy_manager', - 'nova.tests.api.ec2.test_cloud.AjaxProxyManager', '') - - -class AjaxProxyManager(manager.SchedulerDependentManager): - """Fake ajax proxy service, so that an 'rpc.call' will work.""" - @staticmethod - def authorize_ajax_console(context, **kwargs): - return None - - def get_fake_cache(): def _ip(ip, fixed=True, floats=None): ip_dict = {'address': ip, 'type': 'fixed'} @@ -111,7 +99,6 @@ class CloudTestCase(test.TestCase): self.scheduter = self.start_service('scheduler') self.network = self.start_service('network') self.volume = self.start_service('volume') - self.ajax_proxy = self.start_service('ajax_proxy') self.image_service = utils.import_object(FLAGS.image_service) self.user_id = 'fake' @@ -1179,16 +1166,6 @@ class CloudTestCase(test.TestCase): # for unit tests. rv = self.cloud.terminate_instances(self.context, [instance_id]) - def test_ajax_console(self): - instance_id = self._run_instance(image_id='ami-1') - output = self.cloud.get_ajax_console(context=self.context, - instance_id=[instance_id]) - self.assertEquals(output['url'], - '%s/?token=FAKETOKEN' % FLAGS.ajax_console_proxy_url) - # TODO(soren): We need this until we can stop polling in the rpc code - # for unit tests. - rv = self.cloud.terminate_instances(self.context, [instance_id]) - def test_key_generation(self): result = self._create_key('test') private_key = result['private_key'] diff --git a/nova/tests/fake_libvirt_utils.py b/nova/tests/fake_libvirt_utils.py index b05f111..ce37295 100644 --- a/nova/tests/fake_libvirt_utils.py +++ b/nova/tests/fake_libvirt_utils.py @@ -89,10 +89,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 807de69..1a521c6 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 83a4125..4d01a61 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -703,16 +703,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() @@ -2903,17 +2893,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 894ac58..745ce1c 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -1868,20 +1868,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 adf8f8e..57b8eae 100644 --- a/nova/tests/test_virt_drivers.py +++ b/nova/tests/test_virt_drivers.py @@ -283,14 +283,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 a1d0af4..bf8bcbe 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 7a93475..a7778b3 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 8c6d548..410daee 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 bf46c77..552d9e0 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -97,9 +97,6 @@ flags.DEFINE_string('libvirt_uri', flags.DEFINE_bool('use_cow_images', True, 'Whether to use cow images') -flags.DEFINE_string('ajaxterm_portrange', - '10000-12000', - 'Range of ports that ajaxterm should randomly try to bind') flags.DEFINE_string('cpuinfo_xml_template', utils.abspath('virt/cpuinfo.xml.template'), 'CpuInfo XML Template (Used only live migration now)') @@ -743,29 +740,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 c21b003..463aa85 100644 --- a/nova/virt/libvirt/utils.py +++ b/nova/virt/libvirt/utils.py @@ -218,18 +218,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 1a2d1dc..5056be6 100644 --- a/nova/virt/vmwareapi/vmops.py +++ b/nova/virt/vmwareapi/vmops.py @@ -714,10 +714,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 620407c..0a0681a 100644 --- a/nova/virt/vmwareapi_conn.py +++ b/nova/virt/vmwareapi_conn.py @@ -169,10 +169,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 attach_volume(self, connection_info, instance_name, mountpoint): """Attach volume storage to VM instance.""" pass diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 606da3b..e72e739 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -1387,11 +1387,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 1bd0b52..6f6bc12 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -334,12 +334,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) @staticmethod diff --git a/setup.py b/setup.py index b63bfde..353ff8d 100644 --- a/setup.py +++ b/setup.py @@ -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 4b0ae99..0000000 --- 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 -
ajaxterm screenshot
-}}} - -== 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 - - - ServerName localhost - SSLEngine On - SSLCertificateKeyFile ssl/apache.pem - SSLCertificateFile ssl/apache.pem - - ProxyRequests Off - - Order deny,allow - Allow from all - - ProxyPass /ajaxterm/ http://localhost:8022/ - ProxyPassReverse /ajaxterm/ http://localhost:8022/ - -}}} - - * 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 46f2acb..0000000 --- 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 - -This manual page was written for the Debian system by -Julien Valroff (but may be used by others). - -.SH "REPORTING BUGS" -Report any bugs to the author: Antony Lesuisse - -.SH COPYRIGHT -Copyright Antony Lesuisse - -.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 b9a5f87..0000000 --- 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 7fdef5e..0000000 --- a/tools/ajaxterm/ajaxterm.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - Ajaxterm - - - - - - - - -
- - diff --git a/tools/ajaxterm/ajaxterm.js b/tools/ajaxterm/ajaxterm.js deleted file mode 100644 index 32b4019..0000000 --- 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, more info'); - 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 bf27b26..0000000 --- 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.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+='%s'%(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='
%s
'%r - if self.last_html==r: - return '' - 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('') -# 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 45391f4..0000000 --- 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 4d1f5a9..0000000 --- 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 9010827..0000000 --- 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 ac28ef0..0000000 --- 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 5c97885..0000000 --- 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 6bd8085..0000000 --- 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 630325c..0000000 --- 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("%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([''%(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: - # - # - # Simple forms: - # - # - # - # - #