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
-
-}}}
-
-== 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%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:
- #
- #
- #
- #
- #
- #
- #