diff --git a/console-proxy/.classpath b/console-proxy/.classpath index 1a61b023647..eeed44e009b 100644 --- a/console-proxy/.classpath +++ b/console-proxy/.classpath @@ -1,25 +1,25 @@ - - - - - - - - + + + + + + + + diff --git a/console-proxy/.project b/console-proxy/.project index d3efcb5b448..5fae244dad4 100644 --- a/console-proxy/.project +++ b/console-proxy/.project @@ -1,41 +1,41 @@ - - - - console-proxy - - - - - - org.eclipse.wst.jsdt.core.javascriptValidator - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - org.eclipse.wst.jsdt.core.jsNature - - + + + + console-proxy + + + + + + org.eclipse.wst.jsdt.core.javascriptValidator + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.wst.jsdt.core.jsNature + + diff --git a/console-proxy/conf.dom0/agent.properties.in b/console-proxy/conf.dom0/agent.properties.in index 285587a44cb..1920481c03f 100644 --- a/console-proxy/conf.dom0/agent.properties.in +++ b/console-proxy/conf.dom0/agent.properties.in @@ -1,46 +1,46 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you 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. - -# Sample configuration file for VMOPS console proxy - -instance=ConsoleProxy -consoleproxy.httpListenPort=8002 - -#resource= the java class, which agent load to execute -resource=com.cloud.agent.resource.consoleproxy.ConsoleProxyResource - -#host= The IP address of management server -host=localhost - -#port = The port management server listening on, default is 8250 -port=8250 - -#pod= The pod, which agent belonged to -pod=default - -#zone= The zone, which agent belonged to -zone=default - -#private.network.device= the private nic device -# if this is commented, it is autodetected on service startup -# private.network.device=cloudbr0 - -#public.network.device= the public nic device -# if this is commented, it is autodetected on service startup -# public.network.device=cloudbr0 - -#guid= a GUID to identify the agent +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +# Sample configuration file for VMOPS console proxy + +instance=ConsoleProxy +consoleproxy.httpListenPort=8002 + +#resource= the java class, which agent load to execute +resource=com.cloud.agent.resource.consoleproxy.ConsoleProxyResource + +#host= The IP address of management server +host=localhost + +#port = The port management server listening on, default is 8250 +port=8250 + +#pod= The pod, which agent belonged to +pod=default + +#zone= The zone, which agent belonged to +zone=default + +#private.network.device= the private nic device +# if this is commented, it is autodetected on service startup +# private.network.device=cloudbr0 + +#public.network.device= the public nic device +# if this is commented, it is autodetected on service startup +# public.network.device=cloudbr0 + +#guid= a GUID to identify the agent diff --git a/console-proxy/conf.dom0/consoleproxy.properties.in b/console-proxy/conf.dom0/consoleproxy.properties.in index 858ef444cd3..a3cddbcab96 100644 --- a/console-proxy/conf.dom0/consoleproxy.properties.in +++ b/console-proxy/conf.dom0/consoleproxy.properties.in @@ -1,23 +1,23 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you 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. - -consoleproxy.tcpListenPort=0 -consoleproxy.httpListenPort=80 -consoleproxy.httpCmdListenPort=8001 -consoleproxy.jarDir=./applet/ -consoleproxy.viewerLinger=180 -consoleproxy.reconnectMaxRetry=5 +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +consoleproxy.tcpListenPort=0 +consoleproxy.httpListenPort=80 +consoleproxy.httpCmdListenPort=8001 +consoleproxy.jarDir=./applet/ +consoleproxy.viewerLinger=180 +consoleproxy.reconnectMaxRetry=5 diff --git a/console-proxy/conf/agent.properties b/console-proxy/conf/agent.properties index 11853ba6ca9..4e217f21100 100644 --- a/console-proxy/conf/agent.properties +++ b/console-proxy/conf/agent.properties @@ -1,19 +1,19 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you 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. - -instance=ConsoleProxy -resource=com.cloud.agent.resource.consoleproxy.ConsoleProxyResource +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +instance=ConsoleProxy +resource=com.cloud.agent.resource.consoleproxy.ConsoleProxyResource diff --git a/console-proxy/conf/consoleproxy.properties b/console-proxy/conf/consoleproxy.properties index 3e5f69ab241..bb452f5823c 100644 --- a/console-proxy/conf/consoleproxy.properties +++ b/console-proxy/conf/consoleproxy.properties @@ -1,23 +1,23 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you 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. - -consoleproxy.tcpListenPort=0 -consoleproxy.httpListenPort=8088 -consoleproxy.httpCmdListenPort=8001 -consoleproxy.jarDir=./applet/ -consoleproxy.viewerLinger=180 -consoleproxy.reconnectMaxRetry=5 +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +consoleproxy.tcpListenPort=0 +consoleproxy.httpListenPort=8088 +consoleproxy.httpCmdListenPort=8001 +consoleproxy.jarDir=./applet/ +consoleproxy.viewerLinger=180 +consoleproxy.reconnectMaxRetry=5 diff --git a/console-proxy/css/ajaxviewer.css b/console-proxy/css/ajaxviewer.css index 90c1a63bb29..5ea552b176f 100644 --- a/console-proxy/css/ajaxviewer.css +++ b/console-proxy/css/ajaxviewer.css @@ -1,144 +1,144 @@ -/* -Licensed to the Apache Software Foundation (ASF) under one -or more contributor license agreements. See the NOTICE file -distributed with this work for additional information -regarding copyright ownership. The ASF licenses this file -to you 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. -*/ - -body { - margin:0 0; - text-align: center; -} - -#main_panel { - clear:both; - margin: 0 auto; - text-align: left; -} - -.canvas_tile { - cursor:crosshair; -} - -#toolbar { - font:normal 12px 'Trebuchet MS','Arial'; - margin:0 auto; - text-align: left; - padding:0 0; - height:32px; - background-image:url(/resource/images/back.gif); - background-repeat:repeat-x; -} - -#toolbar ul { - margin:0 0; - padding:0 10px 0 10px; - float:left; - display:block; - line-height:32px; - list-style:none; -} - -#toolbar li { - float:left; - display:inline; - padding:0; - height:32px; -} - -#toolbar a { - color:white; - float:left; - display:block; - padding:0 3px 0 3px; - text-decoration:none; - line-height:32px; -} - -#toolbar a span { - display:block; - float:none; - padding:0 10px 0 7px; -} - -#toolbar a span img { - border:none; - margin:8px 4px 0 0; -} - -#toolbar a:hover { - background: url(/resource/images/left.png) no-repeat left center; -} - -#toolbar a:hover span { - background:url(/resource/images/right.png) no-repeat right center; -} - - -#toolbar ul li ul { - position: absolute; - top:32; - width: 260; - height: 65; - display: block; - display: none; - border-top: 1px solid black; - background-image:url(/resource/images/back.gif); - background-repeat:repeat-x repeat-y; -} - -#toolbar ul li ul li { - display: list-item; - float:none; - padding-left: 20; -} - -#toolbar ul li ul li.current { - background: url(/resource/images/cad.gif) no-repeat left center; -} - -#toolbar ul li ul li a { - display:block; - padding:0 3px 0 3px; - text-decoration:none; - line-height:32px; - vertical-align: bottom; /* this is to fix the list gap in IE */ -} - -#toolbar ul li ul li a:hover { - background: url(/resource/images/left.png) no-repeat left center; -} - -#toolbar ul li ul li a:hover span { - background: url(/resource/images/right2.png) no-repeat right center; -} - -span.dark { - margin-right:20px; - float:right; - display:block; - width:32px; - height:30px; - background:url(/resource/images/gray-green.png) no-repeat center center; -} - -span.bright { - margin-right:20px; - float:right; - display:block; - width:32px; - height:30px; - background:url(/resource/images/bright-green.png) no-repeat center center; -} +/* +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you 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. +*/ + +body { + margin:0 0; + text-align: center; +} + +#main_panel { + clear:both; + margin: 0 auto; + text-align: left; +} + +.canvas_tile { + cursor:crosshair; +} + +#toolbar { + font:normal 12px 'Trebuchet MS','Arial'; + margin:0 auto; + text-align: left; + padding:0 0; + height:32px; + background-image:url(/resource/images/back.gif); + background-repeat:repeat-x; +} + +#toolbar ul { + margin:0 0; + padding:0 10px 0 10px; + float:left; + display:block; + line-height:32px; + list-style:none; +} + +#toolbar li { + float:left; + display:inline; + padding:0; + height:32px; +} + +#toolbar a { + color:white; + float:left; + display:block; + padding:0 3px 0 3px; + text-decoration:none; + line-height:32px; +} + +#toolbar a span { + display:block; + float:none; + padding:0 10px 0 7px; +} + +#toolbar a span img { + border:none; + margin:8px 4px 0 0; +} + +#toolbar a:hover { + background: url(/resource/images/left.png) no-repeat left center; +} + +#toolbar a:hover span { + background:url(/resource/images/right.png) no-repeat right center; +} + + +#toolbar ul li ul { + position: absolute; + top:32; + width: 260; + height: 65; + display: block; + display: none; + border-top: 1px solid black; + background-image:url(/resource/images/back.gif); + background-repeat:repeat-x repeat-y; +} + +#toolbar ul li ul li { + display: list-item; + float:none; + padding-left: 20; +} + +#toolbar ul li ul li.current { + background: url(/resource/images/cad.gif) no-repeat left center; +} + +#toolbar ul li ul li a { + display:block; + padding:0 3px 0 3px; + text-decoration:none; + line-height:32px; + vertical-align: bottom; /* this is to fix the list gap in IE */ +} + +#toolbar ul li ul li a:hover { + background: url(/resource/images/left.png) no-repeat left center; +} + +#toolbar ul li ul li a:hover span { + background: url(/resource/images/right2.png) no-repeat right center; +} + +span.dark { + margin-right:20px; + float:right; + display:block; + width:32px; + height:30px; + background:url(/resource/images/gray-green.png) no-repeat center center; +} + +span.bright { + margin-right:20px; + float:right; + display:block; + width:32px; + height:30px; + background:url(/resource/images/bright-green.png) no-repeat center center; +} diff --git a/console-proxy/js/ajaxviewer.js b/console-proxy/js/ajaxviewer.js index b15642a1da1..355cc827ba2 100644 --- a/console-proxy/js/ajaxviewer.js +++ b/console-proxy/js/ajaxviewer.js @@ -22,27 +22,27 @@ var g_logger; ///////////////////////////////////////////////////////////////////////////// // class StringBuilder // -function StringBuilder(initStr) { - this.strings = new Array(""); - this.append(initStr); -} - -StringBuilder.prototype = { - append : function (str) { - if (str) { - this.strings.push(str); - } - return this; - }, - - clear : function() { - this.strings.length = 1; - return this; - }, - - toString: function() { - return this.strings.join(""); - } +function StringBuilder(initStr) { + this.strings = new Array(""); + this.append(initStr); +} + +StringBuilder.prototype = { + append : function (str) { + if (str) { + this.strings.push(str); + } + return this; + }, + + clear : function() { + this.strings.length = 1; + return this; + }, + + toString: function() { + return this.strings.join(""); + } }; @@ -335,36 +335,36 @@ function AjaxViewer(panelId, imageUrl, updateUrl, tileMap, width, height, tileWi // g_logger.enable(true); // g_logger.open(); - var ajaxViewer = this; - this.imageLoaded = false; - this.fullImage = true; - this.imgUrl = imageUrl; - this.img = new Image(); - $(this.img).attr('src', imageUrl).load(function() { - ajaxViewer.imageLoaded = true; - }); - - this.updateUrl = updateUrl; - this.tileMap = tileMap; - this.dirty = true; - this.width = width; - this.height = height; - this.tileWidth = tileWidth; + var ajaxViewer = this; + this.imageLoaded = false; + this.fullImage = true; + this.imgUrl = imageUrl; + this.img = new Image(); + $(this.img).attr('src', imageUrl).load(function() { + ajaxViewer.imageLoaded = true; + }); + + this.updateUrl = updateUrl; + this.tileMap = tileMap; + this.dirty = true; + this.width = width; + this.height = height; + this.tileWidth = tileWidth; this.tileHeight = tileHeight; this.maxTileZIndex = 1; this.currentKeyboard = AjaxViewer.KEYBOARD_TYPE_ENGLISH; this.keyboardMappers = []; - this.timer = 0; - this.eventQueue = []; - this.sendingEventInProgress = false; - - this.lastClickEvent = { x: 0, y: 0, button: 0, modifiers: 0, time: new Date().getTime() }; - - if(window.onStatusNotify == undefined) - window.onStatusNotify = function(status) {}; - + this.timer = 0; + this.eventQueue = []; + this.sendingEventInProgress = false; + + this.lastClickEvent = { x: 0, y: 0, button: 0, modifiers: 0, time: new Date().getTime() }; + + if(window.onStatusNotify == undefined) + window.onStatusNotify = function(status) {}; + this.panel = this.generateCanvas(panelId, width, height, tileWidth, tileHeight); this.setupKeyboardTranslationTable(); @@ -569,86 +569,86 @@ AjaxViewer.getEventName = function(type) { return "N/A"; }; - -AjaxViewer.prototype = { - setDirty: function(value) { - this.dirty = value; - }, - - isDirty: function() { - return this.dirty; - }, - - isImageLoaded: function() { - return this.imageLoaded; - }, - - refresh: function(imageUrl, tileMap, fullImage) { - var ajaxViewer = this; - var img = $(this.img); - this.fullImage = fullImage; - this.imgUrl=imageUrl; - - img.attr('src', imageUrl).load(function() { - ajaxViewer.imageLoaded = true; - }); - this.tileMap = tileMap; - }, - - resize: function(panelId, width, height, tileWidth, tileHeight) { - $(".canvas_tile", document.body).each(function() { - $(this).remove(); - }); - $("table", $("#" + panelId)).remove(); - - this.width = width; - this.height = height; - this.tileWidth = tileWidth; - this.tileHeight = tileHeight; - this.panel = this.generateCanvas(panelId, width, height, tileWidth, tileHeight); - }, - - start: function() { - var ajaxViewer = this; - this.timer = setInterval(function() { ajaxViewer.heartbeat(); }, 50); - - $(document).bind("ajaxError", function(event, XMLHttpRequest, ajaxOptions, thrownError) { - ajaxViewer.onAjaxError(event, XMLHttpRequest, ajaxOptions, thrownError); - }); - - this.eventQueue = []; // reset event queue - this.sendingEventInProgress = false; - ajaxViewer.installMouseHook(); - ajaxViewer.installKeyboardHook(); - - $(window).bind("resize", function() { - ajaxViewer.onWindowResize(); - }); - }, - - stop: function() { - clearInterval(this.timer); - this.deleteCanvas(); - - this.uninstallMouseHook(); - this.uninstallKeyboardHook(); - this.eventQueue = []; - this.sendingEventInProgress = false; - - $(document).unbind("ajaxError"); - $(window).unbind("resize"); - }, - - sendMouseEvent: function(event, x, y, whichButton, modifiers) { - this.eventQueue.push({ - type: AjaxViewer.EVENT_QUEUE_MOUSE_EVENT, - event: event, - x: x, - y: y, - code: whichButton, - modifiers: modifiers - }); - this.checkEventQueue(); + +AjaxViewer.prototype = { + setDirty: function(value) { + this.dirty = value; + }, + + isDirty: function() { + return this.dirty; + }, + + isImageLoaded: function() { + return this.imageLoaded; + }, + + refresh: function(imageUrl, tileMap, fullImage) { + var ajaxViewer = this; + var img = $(this.img); + this.fullImage = fullImage; + this.imgUrl=imageUrl; + + img.attr('src', imageUrl).load(function() { + ajaxViewer.imageLoaded = true; + }); + this.tileMap = tileMap; + }, + + resize: function(panelId, width, height, tileWidth, tileHeight) { + $(".canvas_tile", document.body).each(function() { + $(this).remove(); + }); + $("table", $("#" + panelId)).remove(); + + this.width = width; + this.height = height; + this.tileWidth = tileWidth; + this.tileHeight = tileHeight; + this.panel = this.generateCanvas(panelId, width, height, tileWidth, tileHeight); + }, + + start: function() { + var ajaxViewer = this; + this.timer = setInterval(function() { ajaxViewer.heartbeat(); }, 50); + + $(document).bind("ajaxError", function(event, XMLHttpRequest, ajaxOptions, thrownError) { + ajaxViewer.onAjaxError(event, XMLHttpRequest, ajaxOptions, thrownError); + }); + + this.eventQueue = []; // reset event queue + this.sendingEventInProgress = false; + ajaxViewer.installMouseHook(); + ajaxViewer.installKeyboardHook(); + + $(window).bind("resize", function() { + ajaxViewer.onWindowResize(); + }); + }, + + stop: function() { + clearInterval(this.timer); + this.deleteCanvas(); + + this.uninstallMouseHook(); + this.uninstallKeyboardHook(); + this.eventQueue = []; + this.sendingEventInProgress = false; + + $(document).unbind("ajaxError"); + $(window).unbind("resize"); + }, + + sendMouseEvent: function(event, x, y, whichButton, modifiers) { + this.eventQueue.push({ + type: AjaxViewer.EVENT_QUEUE_MOUSE_EVENT, + event: event, + x: x, + y: y, + code: whichButton, + modifiers: modifiers + }); + this.checkEventQueue(); }, setupKeyboardTranslationTable : function() { @@ -799,487 +799,487 @@ AjaxViewer.prototype = { } } - var len; + var len; g_logger.log(Logger.LEVEL_INFO, "Keyboard event: " + AjaxViewer.getEventName(event) + ", code: " + code + ", modifiers: " + modifiers + ', char: ' + String.fromCharCode(code)); - this.eventQueue.push({ - type: AjaxViewer.EVENT_QUEUE_KEYBOARD_EVENT, - event: event, - code: code, - modifiers: modifiers + this.eventQueue.push({ + type: AjaxViewer.EVENT_QUEUE_KEYBOARD_EVENT, + event: event, + code: code, + modifiers: modifiers }); - if(event != AjaxViewer.KEY_DOWN) - this.checkEventQueue(); - }, - - aggregateEvents: function() { - var ajaxViewer = this; - var aggratedQueue = []; - - var aggregating = false; - var mouseX; - var mouseY; - $.each(ajaxViewer.eventQueue, function(index, item) { - if(item.type != AjaxViewer.EVENT_QUEUE_MOUSE_EVENT) { - aggratedQueue.push(item); - } else { - if(!aggregating) { - if(item.event == AjaxViewer.MOUSE_MOVE) { - aggregating = true; - mouseX = item.x; - mouseY = item.y; - } else { - aggratedQueue.push(item); - } - } else { - if(item.event == AjaxViewer.MOUSE_MOVE) { - // continue to aggregate mouse move event - mouseX = item.x; - mouseY = item.y; - } else { - aggratedQueue.push({ - type: AjaxViewer.EVENT_QUEUE_MOUSE_EVENT, - event: AjaxViewer.MOUSE_MOVE, - x: mouseX, - y: mouseY, - code: 0, - modifiers: 0 - }); - aggregating = false; - - aggratedQueue.push(item); - } - } - } - }); - - if(aggregating) { - aggratedQueue.push({ - type: AjaxViewer.EVENT_QUEUE_MOUSE_EVENT, - event: AjaxViewer.MOUSE_MOVE, - x: mouseX, - y: mouseY, - code: 0, - modifiers: 0 - }); - } - - this.eventQueue = aggratedQueue; - }, - - checkEventQueue: function() { + if(event != AjaxViewer.KEY_DOWN) + this.checkEventQueue(); + }, + + aggregateEvents: function() { var ajaxViewer = this; - + var aggratedQueue = []; + + var aggregating = false; + var mouseX; + var mouseY; + $.each(ajaxViewer.eventQueue, function(index, item) { + if(item.type != AjaxViewer.EVENT_QUEUE_MOUSE_EVENT) { + aggratedQueue.push(item); + } else { + if(!aggregating) { + if(item.event == AjaxViewer.MOUSE_MOVE) { + aggregating = true; + mouseX = item.x; + mouseY = item.y; + } else { + aggratedQueue.push(item); + } + } else { + if(item.event == AjaxViewer.MOUSE_MOVE) { + // continue to aggregate mouse move event + mouseX = item.x; + mouseY = item.y; + } else { + aggratedQueue.push({ + type: AjaxViewer.EVENT_QUEUE_MOUSE_EVENT, + event: AjaxViewer.MOUSE_MOVE, + x: mouseX, + y: mouseY, + code: 0, + modifiers: 0 + }); + aggregating = false; + + aggratedQueue.push(item); + } + } + } + }); + + if(aggregating) { + aggratedQueue.push({ + type: AjaxViewer.EVENT_QUEUE_MOUSE_EVENT, + event: AjaxViewer.MOUSE_MOVE, + x: mouseX, + y: mouseY, + code: 0, + modifiers: 0 + }); + } + + this.eventQueue = aggratedQueue; + }, + + checkEventQueue: function() { + var ajaxViewer = this; + if(!this.sendingEventInProgress && this.eventQueue.length > 0) { - var sb = new StringBuilder(); - sb.append(""+this.eventQueue.length).append("|"); - $.each(this.eventQueue, function() { - var item = this; - if(item.type == AjaxViewer.EVENT_QUEUE_MOUSE_EVENT) { - sb.append(""+item.type).append("|"); - sb.append(""+item.event).append("|"); - sb.append(""+item.x).append("|"); - sb.append(""+item.y).append("|"); - sb.append(""+item.code).append("|"); - sb.append(""+item.modifiers).append("|"); - } else { - sb.append(""+item.type).append("|"); - sb.append(""+item.event).append("|"); - sb.append(""+item.code).append("|"); - sb.append(""+item.modifiers).append("|"); - } - }); - this.eventQueue.length = 0; - + var sb = new StringBuilder(); + sb.append(""+this.eventQueue.length).append("|"); + $.each(this.eventQueue, function() { + var item = this; + if(item.type == AjaxViewer.EVENT_QUEUE_MOUSE_EVENT) { + sb.append(""+item.type).append("|"); + sb.append(""+item.event).append("|"); + sb.append(""+item.x).append("|"); + sb.append(""+item.y).append("|"); + sb.append(""+item.code).append("|"); + sb.append(""+item.modifiers).append("|"); + } else { + sb.append(""+item.type).append("|"); + sb.append(""+item.event).append("|"); + sb.append(""+item.code).append("|"); + sb.append(""+item.modifiers).append("|"); + } + }); + this.eventQueue.length = 0; + var url = ajaxViewer.updateUrl + "&event=" + AjaxViewer.EVENT_BAG; g_logger.log(Logger.LEVEL_TRACE, "Posting client event " + sb.toString() + "..."); - - ajaxViewer.sendingEventInProgress = true; - window.onStatusNotify(AjaxViewer.STATUS_SENDING); + + ajaxViewer.sendingEventInProgress = true; + window.onStatusNotify(AjaxViewer.STATUS_SENDING); $.post(url, {data: sb.toString()}, function(data, textStatus) { g_logger.log(Logger.LEVEL_TRACE, "Client event " + sb.toString() + " is posted"); - - ajaxViewer.sendingEventInProgress = false; - window.onStatusNotify(AjaxViewer.STATUS_SENT); - - ajaxViewer.checkUpdate(); - }, 'html'); - } - }, - - onAjaxError: function(event, XMLHttpRequest, ajaxOptions, thrownError) { - if(window.onClientError != undefined && jQuery.isFunction(window.onClientError)) { - window.onClientError(); - } - }, - - onWindowResize: function() { - var offset = this.panel.offset(); - - var row = $('tr:first', this.panel); - var cell = $('td:first', row); - var tile = this.getTile(cell, 'tile'); - - var tileOffset = tile.offset(); - var deltaX = offset.left - tileOffset.left; - var deltaY = offset.top - tileOffset.top; - - if(deltaX != 0 || deltaY != 0) { - $(".canvas_tile").each(function() { - var offsetFrom = $(this).offset(); - $(this).css('left', offsetFrom.left + deltaX).css('top', offsetFrom.top + deltaY); - }); - } - }, - - deleteCanvas: function() { - $('.canvas_tile', $(document.body)).each(function() { - $(this).remove(); - }); - }, - - generateCanvas: function(wrapperDivId, width, height, tileWidth, tileHeight) { - var canvasParent = $('#' + wrapperDivId); - canvasParent.width(width); - canvasParent.height(height); - - if(window.onCanvasSizeChange != undefined && jQuery.isFunction(window.onCanvasSizeChange)) - window.onCanvasSizeChange(width, height); - - var tableDef = '\r\n'; - var i = 0; - var j = 0; - for(i = 0; i < Math.ceil((height + tileHeight - 1) / tileHeight); i++) { - var rowHeight = Math.min(height - i*tileHeight, tileHeight); - tableDef += '\r\n'; - - for(j = 0; j < Math.ceil((width + tileWidth - 1) / tileWidth); j++) { - var colWidth = Math.min(width - j*tileWidth, tileWidth); - tableDef += '\r\n'; - } - tableDef += '\r\n'; - } - tableDef += '
\r\n'; - - return $(tableDef).appendTo(canvasParent); - }, - - getTile: function(cell, name) { - var clonedDiv = cell.data(name); - if(!clonedDiv) { - var offset = cell.offset(); - var divDef = "
"; - - clonedDiv = $(divDef).appendTo($(document.body)); - cell.data(name, clonedDiv); - } - - return clonedDiv; - }, - - initCell: function(cell) { - if(!cell.data("init")) { - cell.data("init", true); - - cell.data("current", 0); - this.getTile(cell, "tile2"); - this.getTile(cell, "tile"); - } - }, - - displayCell: function(cell, bg) { - var div; - var divPrev; - if(!cell.data("current")) { - cell.data("current", 1); - - divPrev = this.getTile(cell, "tile"); - div = this.getTile(cell, "tile2"); - } else { - cell.data("current", 0); - divPrev = this.getTile(cell, "tile2"); - div = this.getTile(cell, "tile"); - } + + ajaxViewer.sendingEventInProgress = false; + window.onStatusNotify(AjaxViewer.STATUS_SENT); + + ajaxViewer.checkUpdate(); + }, 'html'); + } + }, + + onAjaxError: function(event, XMLHttpRequest, ajaxOptions, thrownError) { + if(window.onClientError != undefined && jQuery.isFunction(window.onClientError)) { + window.onClientError(); + } + }, + + onWindowResize: function() { + var offset = this.panel.offset(); + + var row = $('tr:first', this.panel); + var cell = $('td:first', row); + var tile = this.getTile(cell, 'tile'); + + var tileOffset = tile.offset(); + var deltaX = offset.left - tileOffset.left; + var deltaY = offset.top - tileOffset.top; + + if(deltaX != 0 || deltaY != 0) { + $(".canvas_tile").each(function() { + var offsetFrom = $(this).offset(); + $(this).css('left', offsetFrom.left + deltaX).css('top', offsetFrom.top + deltaY); + }); + } + }, + + deleteCanvas: function() { + $('.canvas_tile', $(document.body)).each(function() { + $(this).remove(); + }); + }, + + generateCanvas: function(wrapperDivId, width, height, tileWidth, tileHeight) { + var canvasParent = $('#' + wrapperDivId); + canvasParent.width(width); + canvasParent.height(height); + + if(window.onCanvasSizeChange != undefined && jQuery.isFunction(window.onCanvasSizeChange)) + window.onCanvasSizeChange(width, height); + + var tableDef = '\r\n'; + var i = 0; + var j = 0; + for(i = 0; i < Math.ceil((height + tileHeight - 1) / tileHeight); i++) { + var rowHeight = Math.min(height - i*tileHeight, tileHeight); + tableDef += '\r\n'; + + for(j = 0; j < Math.ceil((width + tileWidth - 1) / tileWidth); j++) { + var colWidth = Math.min(width - j*tileWidth, tileWidth); + tableDef += '\r\n'; + } + tableDef += '\r\n'; + } + tableDef += '
\r\n'; + + return $(tableDef).appendTo(canvasParent); + }, + + getTile: function(cell, name) { + var clonedDiv = cell.data(name); + if(!clonedDiv) { + var offset = cell.offset(); + var divDef = "
"; + + clonedDiv = $(divDef).appendTo($(document.body)); + cell.data(name, clonedDiv); + } + + return clonedDiv; + }, + + initCell: function(cell) { + if(!cell.data("init")) { + cell.data("init", true); + + cell.data("current", 0); + this.getTile(cell, "tile2"); + this.getTile(cell, "tile"); + } + }, + + displayCell: function(cell, bg) { + var div; + var divPrev; + if(!cell.data("current")) { + cell.data("current", 1); + + divPrev = this.getTile(cell, "tile"); + div = this.getTile(cell, "tile2"); + } else { + cell.data("current", 0); + divPrev = this.getTile(cell, "tile2"); + div = this.getTile(cell, "tile"); + } var zIndex = parseInt(divPrev.css("z-index")) + 1; - this.maxTileZIndex = Math.max(this.maxTileZIndex, zIndex); - div.css("z-index", zIndex); - div.css("background", bg); - }, - - updateTile: function() { - if(this.dirty) { - var ajaxViewer = this; - var tileWidth = this.tileWidth; - var tileHeight = this.tileHeight; - var imgUrl = this.imgUrl; - var panel = this.panel; - - if(this.fullImage) { - $.each(this.tileMap, function() { - var i = $(this)[0]; - var j = $(this)[1]; - var row = $("TR:eq("+i+")", panel); - var cell = $("TD:eq("+j+")", row); - var attr = "url(" + imgUrl + ") -"+j*tileWidth+"px -"+i*tileHeight + "px"; - - ajaxViewer.initCell(cell); - ajaxViewer.displayCell(cell, attr); - }); - } else { - $.each(this.tileMap, function(index) { - var i = $(this)[0]; - var j = $(this)[1]; - var offset = index*tileWidth; - var attr = "url(" + imgUrl + ") no-repeat -"+offset+"px 0px"; - var row = $("TR:eq("+i+")", panel); - var cell = $("TD:eq("+j+")", row); - - ajaxViewer.initCell(cell); - ajaxViewer.displayCell(cell, attr); - }); - } - - this.dirty = false; - } - }, - - heartbeat: function() { - this.checkEventQueue(); - this.checkUpdate(); - }, - - checkUpdate: function() { - if(!this.isDirty()) - return; - - if(this.isImageLoaded()) { - this.updateTile(); - var url = this.updateUrl; - var ajaxViewer = this; - - window.onStatusNotify(AjaxViewer.STATUS_RECEIVING); - $.getScript(url, function(data, textStatus) { - if(/^/.test(data)) { - ajaxViewer.stop(); - $(document.body).html(data); - } else { - eval(data); - ajaxViewer.setDirty(true); - window.onStatusNotify(AjaxViewer.STATUS_RECEIVED); - - ajaxViewer.checkUpdate(); - } - }); - } - }, - - ptInPanel: function(pageX, pageY) { - var mainPanel = this.panel; - - var offset = mainPanel.offset(); - var x = pageX - offset.left; - var y = pageY - offset.top; - - if(x < 0 || y < 0 || x > mainPanel.width() - 1 || y > mainPanel.height() - 1) - return false; - return true; - }, - - pageToPanel: function(pageX, pageY) { - var mainPanel = this.panel; - - var offset = mainPanel.offset(); - var x = pageX - offset.left; - var y = pageY - offset.top; - - if(x < 0) - x = 0; - if(x > mainPanel.width() - 1) - x = mainPanel.width() - 1; - - if(y < 0) - y = 0; - if(y > mainPanel.height() - 1) - y = mainPanel.height() - 1; - - return { x: Math.ceil(x), y: Math.ceil(y) }; - }, - - installMouseHook: function() { - var ajaxViewer = this; - var target = $(document.body); - - target.mousemove(function(e) { - if(!ajaxViewer.ptInPanel(e.pageX, e.pageY)) - return true; - - var pt = ajaxViewer.pageToPanel(e.pageX, e.pageY); - ajaxViewer.onMouseMove(pt.x, pt.y); - - e.stopPropagation(); - return false; - }); - - target.mousedown(function(e) { - ajaxViewer.panel.parent().focus(); - - if(!ajaxViewer.ptInPanel(e.pageX, e.pageY)) - return true; + this.maxTileZIndex = Math.max(this.maxTileZIndex, zIndex); + div.css("z-index", zIndex); + div.css("background", bg); + }, + + updateTile: function() { + if(this.dirty) { + var ajaxViewer = this; + var tileWidth = this.tileWidth; + var tileHeight = this.tileHeight; + var imgUrl = this.imgUrl; + var panel = this.panel; - var modifiers = ajaxViewer.getKeyModifiers(e); - var whichButton = e.button; - - var pt = ajaxViewer.pageToPanel(e.pageX, e.pageY); - ajaxViewer.onMouseDown(pt.x, pt.y, whichButton, modifiers); - - e.stopPropagation(); - return false; - }); - - target.mouseup(function(e) { - if(!ajaxViewer.ptInPanel(e.pageX, e.pageY)) - return true; - - var modifiers = ajaxViewer.getKeyModifiers(e); - var whichButton = e.button; - - var pt = ajaxViewer.pageToPanel(e.pageX, e.pageY); + if(this.fullImage) { + $.each(this.tileMap, function() { + var i = $(this)[0]; + var j = $(this)[1]; + var row = $("TR:eq("+i+")", panel); + var cell = $("TD:eq("+j+")", row); + var attr = "url(" + imgUrl + ") -"+j*tileWidth+"px -"+i*tileHeight + "px"; + + ajaxViewer.initCell(cell); + ajaxViewer.displayCell(cell, attr); + }); + } else { + $.each(this.tileMap, function(index) { + var i = $(this)[0]; + var j = $(this)[1]; + var offset = index*tileWidth; + var attr = "url(" + imgUrl + ") no-repeat -"+offset+"px 0px"; + var row = $("TR:eq("+i+")", panel); + var cell = $("TD:eq("+j+")", row); + + ajaxViewer.initCell(cell); + ajaxViewer.displayCell(cell, attr); + }); + } + + this.dirty = false; + } + }, + + heartbeat: function() { + this.checkEventQueue(); + this.checkUpdate(); + }, + + checkUpdate: function() { + if(!this.isDirty()) + return; + + if(this.isImageLoaded()) { + this.updateTile(); + var url = this.updateUrl; + var ajaxViewer = this; + + window.onStatusNotify(AjaxViewer.STATUS_RECEIVING); + $.getScript(url, function(data, textStatus) { + if(/^/.test(data)) { + ajaxViewer.stop(); + $(document.body).html(data); + } else { + eval(data); + ajaxViewer.setDirty(true); + window.onStatusNotify(AjaxViewer.STATUS_RECEIVED); + + ajaxViewer.checkUpdate(); + } + }); + } + }, + + ptInPanel: function(pageX, pageY) { + var mainPanel = this.panel; + + var offset = mainPanel.offset(); + var x = pageX - offset.left; + var y = pageY - offset.top; + + if(x < 0 || y < 0 || x > mainPanel.width() - 1 || y > mainPanel.height() - 1) + return false; + return true; + }, + + pageToPanel: function(pageX, pageY) { + var mainPanel = this.panel; + + var offset = mainPanel.offset(); + var x = pageX - offset.left; + var y = pageY - offset.top; + + if(x < 0) + x = 0; + if(x > mainPanel.width() - 1) + x = mainPanel.width() - 1; + + if(y < 0) + y = 0; + if(y > mainPanel.height() - 1) + y = mainPanel.height() - 1; + + return { x: Math.ceil(x), y: Math.ceil(y) }; + }, + + installMouseHook: function() { + var ajaxViewer = this; + var target = $(document.body); + + target.mousemove(function(e) { + if(!ajaxViewer.ptInPanel(e.pageX, e.pageY)) + return true; + + var pt = ajaxViewer.pageToPanel(e.pageX, e.pageY); + ajaxViewer.onMouseMove(pt.x, pt.y); + + e.stopPropagation(); + return false; + }); + + target.mousedown(function(e) { + ajaxViewer.panel.parent().focus(); + + if(!ajaxViewer.ptInPanel(e.pageX, e.pageY)) + return true; + + var modifiers = ajaxViewer.getKeyModifiers(e); + var whichButton = e.button; + + var pt = ajaxViewer.pageToPanel(e.pageX, e.pageY); + ajaxViewer.onMouseDown(pt.x, pt.y, whichButton, modifiers); + + e.stopPropagation(); + return false; + }); + + target.mouseup(function(e) { + if(!ajaxViewer.ptInPanel(e.pageX, e.pageY)) + return true; + + var modifiers = ajaxViewer.getKeyModifiers(e); + var whichButton = e.button; + + var pt = ajaxViewer.pageToPanel(e.pageX, e.pageY); + + ajaxViewer.onMouseUp(pt.x, pt.y, whichButton, modifiers); + e.stopPropagation(); + return false; + }); + + // disable browser right-click context menu + target.bind("contextmenu", function() { return false; }); + }, + + uninstallMouseHook : function() { + var target = $(document); + target.unbind("mousemove"); + target.unbind("mousedown"); + target.unbind("mouseup"); + target.unbind("contextmenu"); + }, + + requiresDefaultKeyProcess : function(e) { + switch(e.which) { + case 8 : // backspace + case 9 : // TAB + case 19 : // PAUSE/BREAK + case 20 : // CAPSLOCK + case 27 : // ESCAPE + case 16 : // SHIFT key + case 17 : // CTRL key + case 18 : // ALT key + case 33 : // PGUP + case 34 : // PGDN + case 35 : // END + case 36 : // HOME + case 37 : // LEFT + case 38 : // UP + case 39 : // RIGHT + case 40 : // DOWN + return false; + } + + if(this.getKeyModifiers(e) == AjaxViewer.SHIFT_KEY_MASK) + return true; + + if(this.getKeyModifiers(e) != 0) + return false; + + return true; + }, + + installKeyboardHook: function() { + var ajaxViewer = this; + var target = $(document); - ajaxViewer.onMouseUp(pt.x, pt.y, whichButton, modifiers); - e.stopPropagation(); - return false; - }); - - // disable browser right-click context menu - target.bind("contextmenu", function() { return false; }); - }, - - uninstallMouseHook : function() { - var target = $(document); - target.unbind("mousemove"); - target.unbind("mousedown"); - target.unbind("mouseup"); - target.unbind("contextmenu"); - }, - - requiresDefaultKeyProcess : function(e) { - switch(e.which) { - case 8 : // backspace - case 9 : // TAB - case 19 : // PAUSE/BREAK - case 20 : // CAPSLOCK - case 27 : // ESCAPE - case 16 : // SHIFT key - case 17 : // CTRL key - case 18 : // ALT key - case 33 : // PGUP - case 34 : // PGDN - case 35 : // END - case 36 : // HOME - case 37 : // LEFT - case 38 : // UP - case 39 : // RIGHT - case 40 : // DOWN - return false; - } - - if(this.getKeyModifiers(e) == AjaxViewer.SHIFT_KEY_MASK) - return true; - - if(this.getKeyModifiers(e) != 0) - return false; - - return true; - }, - - installKeyboardHook: function() { - var ajaxViewer = this; - var target = $(document); - target.keypress(function(e) { - ajaxViewer.onKeyPress(e.which, ajaxViewer.getKeyModifiers(e)); - - e.stopPropagation(); - if(ajaxViewer.requiresDefaultKeyProcess(e)) - return true; - - e.preventDefault(); - return false; - }); - - target.keydown(function(e) { + ajaxViewer.onKeyPress(e.which, ajaxViewer.getKeyModifiers(e)); + + e.stopPropagation(); + if(ajaxViewer.requiresDefaultKeyProcess(e)) + return true; + + e.preventDefault(); + return false; + }); + + target.keydown(function(e) { ajaxViewer.onKeyDown(e.which, ajaxViewer.getKeyModifiers(e)); - - e.stopPropagation(); - if(ajaxViewer.requiresDefaultKeyProcess(e)) - return true; - - e.preventDefault(); - return false; - }); - - target.keyup(function(e) { - ajaxViewer.onKeyUp(e.which, ajaxViewer.getKeyModifiers(e)); - - e.stopPropagation(); - if(ajaxViewer.requiresDefaultKeyProcess(e)) - return true; - - e.preventDefault(); - return false; - }); - }, - - uninstallKeyboardHook : function() { - var target = $(document); - target.unbind("keypress"); - target.unbind("keydown"); - target.unbind("keyup"); - }, - - onMouseMove: function(x, y) { - this.sendMouseEvent(AjaxViewer.MOUSE_MOVE, x, y, 0, 0); - }, - - onMouseDown: function(x, y, whichButton, modifiers) { - this.sendMouseEvent(AjaxViewer.MOUSE_DOWN, x, y, whichButton, modifiers); - }, - - onMouseUp: function(x, y, whichButton, modifiers) { - this.sendMouseEvent(AjaxViewer.MOUSE_UP, x, y, whichButton, modifiers); - - var curTick = new Date().getTime(); - if(this.lastClickEvent.time && (curTick - this.lastClickEvent.time < 300)) { - this.onMouseDblClick(this.lastClickEvent.x, this.lastClickEvent.y, - this.lastClickEvent.button, this.lastClickEvent.modifiers); - } - - this.lastClickEvent.x = x; - this.lastClickEvent.y = y; - this.lastClickEvent.button = whichButton; - this.lastClickEvent.modifiers = modifiers; - this.lastClickEvent.time = curTick; - }, - - onMouseDblClick: function(x, y, whichButton, modifiers) { - this.sendMouseEvent(AjaxViewer.MOUSE_DBLCLK, x, y, whichButton, modifiers); - }, - + + e.stopPropagation(); + if(ajaxViewer.requiresDefaultKeyProcess(e)) + return true; + + e.preventDefault(); + return false; + }); + + target.keyup(function(e) { + ajaxViewer.onKeyUp(e.which, ajaxViewer.getKeyModifiers(e)); + + e.stopPropagation(); + if(ajaxViewer.requiresDefaultKeyProcess(e)) + return true; + + e.preventDefault(); + return false; + }); + }, + + uninstallKeyboardHook : function() { + var target = $(document); + target.unbind("keypress"); + target.unbind("keydown"); + target.unbind("keyup"); + }, + + onMouseMove: function(x, y) { + this.sendMouseEvent(AjaxViewer.MOUSE_MOVE, x, y, 0, 0); + }, + + onMouseDown: function(x, y, whichButton, modifiers) { + this.sendMouseEvent(AjaxViewer.MOUSE_DOWN, x, y, whichButton, modifiers); + }, + + onMouseUp: function(x, y, whichButton, modifiers) { + this.sendMouseEvent(AjaxViewer.MOUSE_UP, x, y, whichButton, modifiers); + + var curTick = new Date().getTime(); + if(this.lastClickEvent.time && (curTick - this.lastClickEvent.time < 300)) { + this.onMouseDblClick(this.lastClickEvent.x, this.lastClickEvent.y, + this.lastClickEvent.button, this.lastClickEvent.modifiers); + } + + this.lastClickEvent.x = x; + this.lastClickEvent.y = y; + this.lastClickEvent.button = whichButton; + this.lastClickEvent.modifiers = modifiers; + this.lastClickEvent.time = curTick; + }, + + onMouseDblClick: function(x, y, whichButton, modifiers) { + this.sendMouseEvent(AjaxViewer.MOUSE_DBLCLK, x, y, whichButton, modifiers); + }, + onKeyPress: function(code, modifiers) { g_logger.log(Logger.LEVEL_WARN, "RAW KEYBOARD EVENT. KEY-PRESS: " + code + ", modifers: " + modifiers); this.dispatchKeyboardInput(AjaxViewer.KEY_PRESS, code, modifiers); - }, - + }, + onKeyDown: function(code, modifiers) { g_logger.log(Logger.LEVEL_WARN, "RAW KEYBOARD EVENT. KEY-DOWN: " + code + ", modifers: " + modifiers); this.dispatchKeyboardInput(AjaxViewer.KEY_DOWN, code, modifiers); - }, - + }, + onKeyUp: function(code, modifiers) { g_logger.log(Logger.LEVEL_WARN, "RAW KEYBOARD EVENT. KEY-UP: " + code + ", modifers: " + modifiers); @@ -1308,32 +1308,32 @@ AjaxViewer.prototype = { break; } } - }, - - getKeyModifiers: function(e) { - var modifiers = 0; - if(e.altKey) - modifiers |= AjaxViewer.ALT_KEY_MASK; - - if(e.altLeft) - modifiers |= AjaxViewer.LEFT_ALT_MASK; - - if(e.ctrlKey) - modifiers |= AjaxViewer.CTRL_KEY_MASK; - - if(e.ctrlLeft) - modifiers |= AjaxViewer.LEFT_CTRL_MASK; - - if(e.shiftKey) - modifiers |= AjaxViewer.SHIFT_KEY_MASK; - - if(e.shiftLeft) - modifiers |= AjaxViewer.LEFT_SHIFT_MASK; - - if(e.metaKey) - modifiers |= AjaxViewer.META_KEY_MASK; - - return modifiers; - } -}; + }, + + getKeyModifiers: function(e) { + var modifiers = 0; + if(e.altKey) + modifiers |= AjaxViewer.ALT_KEY_MASK; + + if(e.altLeft) + modifiers |= AjaxViewer.LEFT_ALT_MASK; + + if(e.ctrlKey) + modifiers |= AjaxViewer.CTRL_KEY_MASK; + + if(e.ctrlLeft) + modifiers |= AjaxViewer.LEFT_CTRL_MASK; + + if(e.shiftKey) + modifiers |= AjaxViewer.SHIFT_KEY_MASK; + + if(e.shiftLeft) + modifiers |= AjaxViewer.LEFT_SHIFT_MASK; + + if(e.metaKey) + modifiers |= AjaxViewer.META_KEY_MASK; + + return modifiers; + } +}; diff --git a/console-proxy/scripts/run.bat b/console-proxy/scripts/run.bat index 39655f9f09f..ce6dc404574 100644 --- a/console-proxy/scripts/run.bat +++ b/console-proxy/scripts/run.bat @@ -1,18 +1,18 @@ -rem Licensed to the Apache Software Foundation (ASF) under one -rem or more contributor license agreements. See the NOTICE file -rem distributed with this work for additional information -rem regarding copyright ownership. The ASF licenses this file -rem to you under the Apache License, Version 2.0 (the -rem "License"); you may not use this file except in compliance -rem with the License. You may obtain a copy of the License at -rem -rem http://www.apache.org/licenses/LICENSE-2.0 -rem -rem Unless required by applicable law or agreed to in writing, -rem software distributed under the License is distributed on an -rem "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -rem KIND, either express or implied. See the License for the -rem specific language governing permissions and limitations -rem under the License. - -java -mx700m -cp cloud-console-proxy.jar;;cloud-console-common.jar;log4j-1.2.15.jar;apache-log4j-extras-1.0.jar;gson-1.3.jar;commons-logging-1.1.1.jar;.;.\conf; com.cloud.consoleproxy.ConsoleProxy %* +rem Licensed to the Apache Software Foundation (ASF) under one +rem or more contributor license agreements. See the NOTICE file +rem distributed with this work for additional information +rem regarding copyright ownership. The ASF licenses this file +rem to you under the Apache License, Version 2.0 (the +rem "License"); you may not use this file except in compliance +rem with the License. You may obtain a copy of the License at +rem +rem http://www.apache.org/licenses/LICENSE-2.0 +rem +rem Unless required by applicable law or agreed to in writing, +rem software distributed under the License is distributed on an +rem "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +rem KIND, either express or implied. See the License for the +rem specific language governing permissions and limitations +rem under the License. + +java -mx700m -cp cloud-console-proxy.jar;;cloud-console-common.jar;log4j-1.2.15.jar;apache-log4j-extras-1.0.jar;gson-1.3.jar;commons-logging-1.1.1.jar;.;.\conf; com.cloud.consoleproxy.ConsoleProxy %* diff --git a/console-proxy/src/com/cloud/consoleproxy/AjaxFIFOImageCache.java b/console-proxy/src/com/cloud/consoleproxy/AjaxFIFOImageCache.java index a745d0d9e81..cff00b3317d 100644 --- a/console-proxy/src/com/cloud/consoleproxy/AjaxFIFOImageCache.java +++ b/console-proxy/src/com/cloud/consoleproxy/AjaxFIFOImageCache.java @@ -14,53 +14,53 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.consoleproxy; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +package com.cloud.consoleproxy; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import com.cloud.consoleproxy.util.Logger; - -public class AjaxFIFOImageCache { - private static final Logger s_logger = Logger.getLogger(AjaxFIFOImageCache.class); - - private List fifoQueue; - private Map cache; - private int cacheSize; + +public class AjaxFIFOImageCache { + private static final Logger s_logger = Logger.getLogger(AjaxFIFOImageCache.class); + + private List fifoQueue; + private Map cache; + private int cacheSize; private int nextKey = 0; - - public AjaxFIFOImageCache(int cacheSize) { - this.cacheSize = cacheSize; - fifoQueue = new ArrayList(); - cache = new HashMap(); - } - - public synchronized void clear() { - fifoQueue.clear(); - cache.clear(); - } - - public synchronized int putImage(byte[] image) { - while(cache.size() >= cacheSize) { - Integer keyToRemove = fifoQueue.remove(0); - cache.remove(keyToRemove); - - if(s_logger.isTraceEnabled()) - s_logger.trace("Remove image from cache, key: " + keyToRemove); - } - - int key = getNextKey(); - - if(s_logger.isTraceEnabled()) - s_logger.trace("Add image to cache, key: " + key); - - cache.put(key, image); - fifoQueue.add(key); - return key; - } - + + public AjaxFIFOImageCache(int cacheSize) { + this.cacheSize = cacheSize; + fifoQueue = new ArrayList(); + cache = new HashMap(); + } + + public synchronized void clear() { + fifoQueue.clear(); + cache.clear(); + } + + public synchronized int putImage(byte[] image) { + while(cache.size() >= cacheSize) { + Integer keyToRemove = fifoQueue.remove(0); + cache.remove(keyToRemove); + + if(s_logger.isTraceEnabled()) + s_logger.trace("Remove image from cache, key: " + keyToRemove); + } + + int key = getNextKey(); + + if(s_logger.isTraceEnabled()) + s_logger.trace("Add image to cache, key: " + key); + + cache.put(key, image); + fifoQueue.add(key); + return key; + } + public synchronized byte[] getImage(int key) { if (key == 0) { key = nextKey; diff --git a/console-proxy/src/com/cloud/consoleproxy/AuthenticationException.java b/console-proxy/src/com/cloud/consoleproxy/AuthenticationException.java index aaf8e44eb0a..3fa266792ae 100644 --- a/console-proxy/src/com/cloud/consoleproxy/AuthenticationException.java +++ b/console-proxy/src/com/cloud/consoleproxy/AuthenticationException.java @@ -14,20 +14,20 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.consoleproxy; - -public class AuthenticationException extends Exception { - private static final long serialVersionUID = -393139302884898842L; - public AuthenticationException() { - super(); - } - public AuthenticationException(String s) { - super(s); - } - public AuthenticationException(String message, Throwable cause) { - super(message, cause); - } - public AuthenticationException(Throwable cause) { - super(cause); - } +package com.cloud.consoleproxy; + +public class AuthenticationException extends Exception { + private static final long serialVersionUID = -393139302884898842L; + public AuthenticationException() { + super(); + } + public AuthenticationException(String s) { + super(s); + } + public AuthenticationException(String message, Throwable cause) { + super(message, cause); + } + public AuthenticationException(Throwable cause) { + super(cause); + } } \ No newline at end of file diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java index d91a198a103..6cadeca1f4a 100644 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java +++ b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java @@ -14,8 +14,8 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.consoleproxy; - +package com.cloud.consoleproxy; + import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -30,377 +30,377 @@ import com.cloud.consoleproxy.util.Logger; import com.sun.net.httpserver.Headers; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; - -public class ConsoleProxyAjaxHandler implements HttpHandler { - private static final Logger s_logger = Logger.getLogger(ConsoleProxyAjaxHandler.class); - - public ConsoleProxyAjaxHandler() { - } - - public void handle(HttpExchange t) throws IOException { - try { - if(s_logger.isTraceEnabled()) - s_logger.trace("AjaxHandler " + t.getRequestURI()); - - long startTick = System.currentTimeMillis(); - - doHandle(t); - - if(s_logger.isTraceEnabled()) - s_logger.trace(t.getRequestURI() + " process time " + (System.currentTimeMillis() - startTick) + " ms"); - } catch (IOException e) { - throw e; - } catch (IllegalArgumentException e) { - s_logger.warn("Exception, ", e); - t.sendResponseHeaders(400, -1); // bad request - } catch(Throwable e) { - s_logger.error("Unexpected exception, ", e); - t.sendResponseHeaders(500, -1); // server error - } finally { - t.close(); - } - } - - private void doHandle(HttpExchange t) throws Exception, IllegalArgumentException { - String queries = t.getRequestURI().getQuery(); - if(s_logger.isTraceEnabled()) - s_logger.trace("Handle AJAX request: " + queries); - - Map queryMap = ConsoleProxyHttpHandlerHelper.getQueryMap(queries); - - String host = queryMap.get("host"); - String portStr = queryMap.get("port"); - String sid = queryMap.get("sid"); - String tag = queryMap.get("tag"); - String ticket = queryMap.get("ticket"); - String ajaxSessionIdStr = queryMap.get("sess"); - String eventStr = queryMap.get("event"); - String console_url = queryMap.get("consoleurl"); - String console_host_session = queryMap.get("sessionref"); - - if(tag == null) - tag = ""; - - long ajaxSessionId = 0; - int event = 0; - - int port; - - if(host == null || portStr == null || sid == null) - throw new IllegalArgumentException(); - - try { - port = Integer.parseInt(portStr); - } catch (NumberFormatException e) { - s_logger.warn("Invalid number parameter in query string: " + portStr); - throw new IllegalArgumentException(e); - } - - if(ajaxSessionIdStr != null) { - try { - ajaxSessionId = Long.parseLong(ajaxSessionIdStr); - } catch (NumberFormatException e) { - s_logger.warn("Invalid number parameter in query string: " + ajaxSessionIdStr); - throw new IllegalArgumentException(e); - } - } - - if(eventStr != null) { - try { - event = Integer.parseInt(eventStr); - } catch (NumberFormatException e) { - s_logger.warn("Invalid number parameter in query string: " + eventStr); - throw new IllegalArgumentException(e); - } - } - - ConsoleProxyClient viewer = null; - try { - ConsoleProxyClientParam param = new ConsoleProxyClientParam(); - param.setClientHostAddress(host); - param.setClientHostPort(port); - param.setClientHostPassword(sid); - param.setClientTag(tag); - param.setTicket(ticket); - param.setClientTunnelUrl(console_url); - param.setClientTunnelSession(console_host_session); - - viewer = ConsoleProxy.getAjaxVncViewer(param, ajaxSessionIdStr); - } catch(Exception e) { - - s_logger.warn("Failed to create viewer due to " + e.getMessage(), e); - - String[] content = new String[] { - "", - "
", - "

Access is denied for the console session. Please close the window and retry again

", - "
" - }; - - StringBuffer sb = new StringBuffer(); - for(int i = 0; i < content.length; i++) - sb.append(content[i]); - - sendResponse(t, "text/html", sb.toString()); - return; - } - - if(event != 0) { - if(ajaxSessionId != 0 && ajaxSessionId == viewer.getAjaxSessionId()) { - if(event == 7) { - // client send over an event bag - InputStream is = t.getRequestBody(); - handleClientEventBag(viewer, convertStreamToString(is, true)); - } else { - handleClientEvent(viewer, event, queryMap); - } - sendResponse(t, "text/html", "OK"); - } else { - if(s_logger.isDebugEnabled()) - s_logger.debug("Ajax request comes from a different session, id in request: " + ajaxSessionId + ", id in viewer: " + viewer.getAjaxSessionId()); - - sendResponse(t, "text/html", "Invalid ajax client session id"); - } - } else { - if(ajaxSessionId != 0 && ajaxSessionId != viewer.getAjaxSessionId()) { - s_logger.info("Ajax request comes from a different session, id in request: " + ajaxSessionId + ", id in viewer: " + viewer.getAjaxSessionId()); - handleClientKickoff(t, viewer); - } else if(ajaxSessionId == 0) { - if(s_logger.isDebugEnabled()) - s_logger.debug("Ajax request indicates a fresh client start"); - - String title = queryMap.get("t"); - String guest = queryMap.get("guest"); - handleClientStart(t, viewer, title != null ? title : "", guest); - } else { - - if(s_logger.isTraceEnabled()) - s_logger.trace("Ajax request indicates client update"); - - handleClientUpdate(t, viewer); - } - } - } - - private static String convertStreamToString(InputStream is, boolean closeStreamAfterRead) { - BufferedReader reader = new BufferedReader(new InputStreamReader(is)); - StringBuilder sb = new StringBuilder(); - String line = null; - try { - while ((line = reader.readLine()) != null) { - sb.append(line + "\n"); - } - } catch (IOException e) { - s_logger.warn("Exception while reading request body: ", e); - } finally { - if(closeStreamAfterRead) { - try { - is.close(); - } catch (IOException e) { - } - } - } - return sb.toString(); - } - - private void sendResponse(HttpExchange t, String contentType, String response) throws IOException { - Headers hds = t.getResponseHeaders(); - hds.set("Content-Type", contentType); - - t.sendResponseHeaders(200, response.length()); - OutputStream os = t.getResponseBody(); - try { - os.write(response.getBytes()); - } finally { - os.close(); - } - } - - @SuppressWarnings("deprecation") - private void handleClientEventBag(ConsoleProxyClient viewer, String requestData) { - if(s_logger.isTraceEnabled()) - s_logger.trace("Handle event bag, event bag: " + requestData); - - int start = requestData.indexOf("="); - if(start < 0) - start = 0; - else if(start > 0) - start++; - String data = URLDecoder.decode(requestData.substring(start)); - String[] tokens = data.split("\\|"); - if(tokens != null && tokens.length > 0) { - int count = 0; - try { - count = Integer.parseInt(tokens[0]); - int parsePos = 1; - int type, event, x, y, code, modifiers; - for(int i = 0; i < count; i++) { - type = Integer.parseInt(tokens[parsePos++]); - if(type == 1) { - // mouse event - event = Integer.parseInt(tokens[parsePos++]); - x = Integer.parseInt(tokens[parsePos++]); - y = Integer.parseInt(tokens[parsePos++]); - code = Integer.parseInt(tokens[parsePos++]); - modifiers = Integer.parseInt(tokens[parsePos++]); - - Map queryMap = new HashMap(); - queryMap.put("event", String.valueOf(event)); - queryMap.put("x", String.valueOf(x)); - queryMap.put("y", String.valueOf(y)); - queryMap.put("code", String.valueOf(code)); - queryMap.put("modifier", String.valueOf(modifiers)); - handleClientEvent(viewer, event, queryMap); - } else { - // keyboard event - event = Integer.parseInt(tokens[parsePos++]); - code = Integer.parseInt(tokens[parsePos++]); - modifiers = Integer.parseInt(tokens[parsePos++]); - - Map queryMap = new HashMap(); - queryMap.put("event", String.valueOf(event)); - queryMap.put("code", String.valueOf(code)); - queryMap.put("modifier", String.valueOf(modifiers)); - handleClientEvent(viewer, event, queryMap); - } - } - } catch(NumberFormatException e) { - s_logger.warn("Exception in handle client event bag: " + data + ", ", e); - } catch(Exception e) { - s_logger.warn("Exception in handle client event bag: " + data + ", ", e); - } catch(OutOfMemoryError e) { - s_logger.error("Unrecoverable OutOfMemory Error, exit and let it be re-launched"); - System.exit(1); - } - } - } - - private void handleClientEvent(ConsoleProxyClient viewer, int event, Map queryMap) { - int code = 0; - int x = 0, y = 0; - int modifiers = 0; - - String str; - switch(event) { - case 1: // mouse move - case 2: // mouse down - case 3: // mouse up - case 8: // mouse double click - str = queryMap.get("x"); - if(str != null) { - try { - x = Integer.parseInt(str); - } catch (NumberFormatException e) { - s_logger.warn("Invalid number parameter in query string: " + str); - throw new IllegalArgumentException(e); - } - } - str = queryMap.get("y"); - if(str != null) { - try { - y = Integer.parseInt(str); - } catch (NumberFormatException e) { - s_logger.warn("Invalid number parameter in query string: " + str); - throw new IllegalArgumentException(e); - } - } - - if(event != 1) { - str = queryMap.get("code"); - try { - code = Integer.parseInt(str); - } catch (NumberFormatException e) { - s_logger.warn("Invalid number parameter in query string: " + str); - throw new IllegalArgumentException(e); - } - - str = queryMap.get("modifier"); - try { - modifiers = Integer.parseInt(str); - } catch (NumberFormatException e) { - s_logger.warn("Invalid number parameter in query string: " + str); - throw new IllegalArgumentException(e); - } - - if(s_logger.isTraceEnabled()) - s_logger.trace("Handle client mouse event. event: " + event + ", x: " + x + ", y: " + y + ", button: " + code + ", modifier: " + modifiers); - } else { - if(s_logger.isTraceEnabled()) - s_logger.trace("Handle client mouse move event. x: " + x + ", y: " + y); - } - viewer.sendClientMouseEvent(InputEventType.fromEventCode(event), x, y, code, modifiers); - break; - - case 4: // key press - case 5: // key down - case 6: // key up - str = queryMap.get("code"); - try { - code = Integer.parseInt(str); - } catch (NumberFormatException e) { - s_logger.warn("Invalid number parameter in query string: " + str); - throw new IllegalArgumentException(e); - } - - str = queryMap.get("modifier"); - try { - modifiers = Integer.parseInt(str); - } catch (NumberFormatException e) { - s_logger.warn("Invalid number parameter in query string: " + str); - throw new IllegalArgumentException(e); - } - - if(s_logger.isDebugEnabled()) - s_logger.debug("Handle client keyboard event. event: " + event + ", code: " + code + ", modifier: " + modifiers); - viewer.sendClientRawKeyboardEvent(InputEventType.fromEventCode(event), code, modifiers); - break; - - default : - break; - } - } - - private void handleClientKickoff(HttpExchange t, ConsoleProxyClient viewer) throws IOException { - String response = viewer.onAjaxClientKickoff(); - t.sendResponseHeaders(200, response.length()); - OutputStream os = t.getResponseBody(); - try { - os.write(response.getBytes()); - } finally { - os.close(); - } - } - - private void handleClientStart(HttpExchange t, ConsoleProxyClient viewer, String title, String guest) throws IOException { - List languages = t.getRequestHeaders().get("Accept-Language"); - String response = viewer.onAjaxClientStart(title, languages, guest); - - Headers hds = t.getResponseHeaders(); - hds.set("Content-Type", "text/html"); - hds.set("Cache-Control", "no-cache"); - hds.set("Cache-Control", "no-store"); - t.sendResponseHeaders(200, response.length()); - - OutputStream os = t.getResponseBody(); - try { - os.write(response.getBytes()); - } finally { - os.close(); - } - } - - private void handleClientUpdate(HttpExchange t, ConsoleProxyClient viewer) throws IOException { - String response = viewer.onAjaxClientUpdate(); - - Headers hds = t.getResponseHeaders(); - hds.set("Content-Type", "text/javascript"); - t.sendResponseHeaders(200, response.length()); - - OutputStream os = t.getResponseBody(); - try { - os.write(response.getBytes()); - } finally { - os.close(); - } - } -} + +public class ConsoleProxyAjaxHandler implements HttpHandler { + private static final Logger s_logger = Logger.getLogger(ConsoleProxyAjaxHandler.class); + + public ConsoleProxyAjaxHandler() { + } + + public void handle(HttpExchange t) throws IOException { + try { + if(s_logger.isTraceEnabled()) + s_logger.trace("AjaxHandler " + t.getRequestURI()); + + long startTick = System.currentTimeMillis(); + + doHandle(t); + + if(s_logger.isTraceEnabled()) + s_logger.trace(t.getRequestURI() + " process time " + (System.currentTimeMillis() - startTick) + " ms"); + } catch (IOException e) { + throw e; + } catch (IllegalArgumentException e) { + s_logger.warn("Exception, ", e); + t.sendResponseHeaders(400, -1); // bad request + } catch(Throwable e) { + s_logger.error("Unexpected exception, ", e); + t.sendResponseHeaders(500, -1); // server error + } finally { + t.close(); + } + } + + private void doHandle(HttpExchange t) throws Exception, IllegalArgumentException { + String queries = t.getRequestURI().getQuery(); + if(s_logger.isTraceEnabled()) + s_logger.trace("Handle AJAX request: " + queries); + + Map queryMap = ConsoleProxyHttpHandlerHelper.getQueryMap(queries); + + String host = queryMap.get("host"); + String portStr = queryMap.get("port"); + String sid = queryMap.get("sid"); + String tag = queryMap.get("tag"); + String ticket = queryMap.get("ticket"); + String ajaxSessionIdStr = queryMap.get("sess"); + String eventStr = queryMap.get("event"); + String console_url = queryMap.get("consoleurl"); + String console_host_session = queryMap.get("sessionref"); + + if(tag == null) + tag = ""; + + long ajaxSessionId = 0; + int event = 0; + + int port; + + if(host == null || portStr == null || sid == null) + throw new IllegalArgumentException(); + + try { + port = Integer.parseInt(portStr); + } catch (NumberFormatException e) { + s_logger.warn("Invalid number parameter in query string: " + portStr); + throw new IllegalArgumentException(e); + } + + if(ajaxSessionIdStr != null) { + try { + ajaxSessionId = Long.parseLong(ajaxSessionIdStr); + } catch (NumberFormatException e) { + s_logger.warn("Invalid number parameter in query string: " + ajaxSessionIdStr); + throw new IllegalArgumentException(e); + } + } + + if(eventStr != null) { + try { + event = Integer.parseInt(eventStr); + } catch (NumberFormatException e) { + s_logger.warn("Invalid number parameter in query string: " + eventStr); + throw new IllegalArgumentException(e); + } + } + + ConsoleProxyClient viewer = null; + try { + ConsoleProxyClientParam param = new ConsoleProxyClientParam(); + param.setClientHostAddress(host); + param.setClientHostPort(port); + param.setClientHostPassword(sid); + param.setClientTag(tag); + param.setTicket(ticket); + param.setClientTunnelUrl(console_url); + param.setClientTunnelSession(console_host_session); + + viewer = ConsoleProxy.getAjaxVncViewer(param, ajaxSessionIdStr); + } catch(Exception e) { + + s_logger.warn("Failed to create viewer due to " + e.getMessage(), e); + + String[] content = new String[] { + "", + "
", + "

Access is denied for the console session. Please close the window and retry again

", + "
" + }; + + StringBuffer sb = new StringBuffer(); + for(int i = 0; i < content.length; i++) + sb.append(content[i]); + + sendResponse(t, "text/html", sb.toString()); + return; + } + + if(event != 0) { + if(ajaxSessionId != 0 && ajaxSessionId == viewer.getAjaxSessionId()) { + if(event == 7) { + // client send over an event bag + InputStream is = t.getRequestBody(); + handleClientEventBag(viewer, convertStreamToString(is, true)); + } else { + handleClientEvent(viewer, event, queryMap); + } + sendResponse(t, "text/html", "OK"); + } else { + if(s_logger.isDebugEnabled()) + s_logger.debug("Ajax request comes from a different session, id in request: " + ajaxSessionId + ", id in viewer: " + viewer.getAjaxSessionId()); + + sendResponse(t, "text/html", "Invalid ajax client session id"); + } + } else { + if(ajaxSessionId != 0 && ajaxSessionId != viewer.getAjaxSessionId()) { + s_logger.info("Ajax request comes from a different session, id in request: " + ajaxSessionId + ", id in viewer: " + viewer.getAjaxSessionId()); + handleClientKickoff(t, viewer); + } else if(ajaxSessionId == 0) { + if(s_logger.isDebugEnabled()) + s_logger.debug("Ajax request indicates a fresh client start"); + + String title = queryMap.get("t"); + String guest = queryMap.get("guest"); + handleClientStart(t, viewer, title != null ? title : "", guest); + } else { + + if(s_logger.isTraceEnabled()) + s_logger.trace("Ajax request indicates client update"); + + handleClientUpdate(t, viewer); + } + } + } + + private static String convertStreamToString(InputStream is, boolean closeStreamAfterRead) { + BufferedReader reader = new BufferedReader(new InputStreamReader(is)); + StringBuilder sb = new StringBuilder(); + String line = null; + try { + while ((line = reader.readLine()) != null) { + sb.append(line + "\n"); + } + } catch (IOException e) { + s_logger.warn("Exception while reading request body: ", e); + } finally { + if(closeStreamAfterRead) { + try { + is.close(); + } catch (IOException e) { + } + } + } + return sb.toString(); + } + + private void sendResponse(HttpExchange t, String contentType, String response) throws IOException { + Headers hds = t.getResponseHeaders(); + hds.set("Content-Type", contentType); + + t.sendResponseHeaders(200, response.length()); + OutputStream os = t.getResponseBody(); + try { + os.write(response.getBytes()); + } finally { + os.close(); + } + } + + @SuppressWarnings("deprecation") + private void handleClientEventBag(ConsoleProxyClient viewer, String requestData) { + if(s_logger.isTraceEnabled()) + s_logger.trace("Handle event bag, event bag: " + requestData); + + int start = requestData.indexOf("="); + if(start < 0) + start = 0; + else if(start > 0) + start++; + String data = URLDecoder.decode(requestData.substring(start)); + String[] tokens = data.split("\\|"); + if(tokens != null && tokens.length > 0) { + int count = 0; + try { + count = Integer.parseInt(tokens[0]); + int parsePos = 1; + int type, event, x, y, code, modifiers; + for(int i = 0; i < count; i++) { + type = Integer.parseInt(tokens[parsePos++]); + if(type == 1) { + // mouse event + event = Integer.parseInt(tokens[parsePos++]); + x = Integer.parseInt(tokens[parsePos++]); + y = Integer.parseInt(tokens[parsePos++]); + code = Integer.parseInt(tokens[parsePos++]); + modifiers = Integer.parseInt(tokens[parsePos++]); + + Map queryMap = new HashMap(); + queryMap.put("event", String.valueOf(event)); + queryMap.put("x", String.valueOf(x)); + queryMap.put("y", String.valueOf(y)); + queryMap.put("code", String.valueOf(code)); + queryMap.put("modifier", String.valueOf(modifiers)); + handleClientEvent(viewer, event, queryMap); + } else { + // keyboard event + event = Integer.parseInt(tokens[parsePos++]); + code = Integer.parseInt(tokens[parsePos++]); + modifiers = Integer.parseInt(tokens[parsePos++]); + + Map queryMap = new HashMap(); + queryMap.put("event", String.valueOf(event)); + queryMap.put("code", String.valueOf(code)); + queryMap.put("modifier", String.valueOf(modifiers)); + handleClientEvent(viewer, event, queryMap); + } + } + } catch(NumberFormatException e) { + s_logger.warn("Exception in handle client event bag: " + data + ", ", e); + } catch(Exception e) { + s_logger.warn("Exception in handle client event bag: " + data + ", ", e); + } catch(OutOfMemoryError e) { + s_logger.error("Unrecoverable OutOfMemory Error, exit and let it be re-launched"); + System.exit(1); + } + } + } + + private void handleClientEvent(ConsoleProxyClient viewer, int event, Map queryMap) { + int code = 0; + int x = 0, y = 0; + int modifiers = 0; + + String str; + switch(event) { + case 1: // mouse move + case 2: // mouse down + case 3: // mouse up + case 8: // mouse double click + str = queryMap.get("x"); + if(str != null) { + try { + x = Integer.parseInt(str); + } catch (NumberFormatException e) { + s_logger.warn("Invalid number parameter in query string: " + str); + throw new IllegalArgumentException(e); + } + } + str = queryMap.get("y"); + if(str != null) { + try { + y = Integer.parseInt(str); + } catch (NumberFormatException e) { + s_logger.warn("Invalid number parameter in query string: " + str); + throw new IllegalArgumentException(e); + } + } + + if(event != 1) { + str = queryMap.get("code"); + try { + code = Integer.parseInt(str); + } catch (NumberFormatException e) { + s_logger.warn("Invalid number parameter in query string: " + str); + throw new IllegalArgumentException(e); + } + + str = queryMap.get("modifier"); + try { + modifiers = Integer.parseInt(str); + } catch (NumberFormatException e) { + s_logger.warn("Invalid number parameter in query string: " + str); + throw new IllegalArgumentException(e); + } + + if(s_logger.isTraceEnabled()) + s_logger.trace("Handle client mouse event. event: " + event + ", x: " + x + ", y: " + y + ", button: " + code + ", modifier: " + modifiers); + } else { + if(s_logger.isTraceEnabled()) + s_logger.trace("Handle client mouse move event. x: " + x + ", y: " + y); + } + viewer.sendClientMouseEvent(InputEventType.fromEventCode(event), x, y, code, modifiers); + break; + + case 4: // key press + case 5: // key down + case 6: // key up + str = queryMap.get("code"); + try { + code = Integer.parseInt(str); + } catch (NumberFormatException e) { + s_logger.warn("Invalid number parameter in query string: " + str); + throw new IllegalArgumentException(e); + } + + str = queryMap.get("modifier"); + try { + modifiers = Integer.parseInt(str); + } catch (NumberFormatException e) { + s_logger.warn("Invalid number parameter in query string: " + str); + throw new IllegalArgumentException(e); + } + + if(s_logger.isDebugEnabled()) + s_logger.debug("Handle client keyboard event. event: " + event + ", code: " + code + ", modifier: " + modifiers); + viewer.sendClientRawKeyboardEvent(InputEventType.fromEventCode(event), code, modifiers); + break; + + default : + break; + } + } + + private void handleClientKickoff(HttpExchange t, ConsoleProxyClient viewer) throws IOException { + String response = viewer.onAjaxClientKickoff(); + t.sendResponseHeaders(200, response.length()); + OutputStream os = t.getResponseBody(); + try { + os.write(response.getBytes()); + } finally { + os.close(); + } + } + + private void handleClientStart(HttpExchange t, ConsoleProxyClient viewer, String title, String guest) throws IOException { + List languages = t.getRequestHeaders().get("Accept-Language"); + String response = viewer.onAjaxClientStart(title, languages, guest); + + Headers hds = t.getResponseHeaders(); + hds.set("Content-Type", "text/html"); + hds.set("Cache-Control", "no-cache"); + hds.set("Cache-Control", "no-store"); + t.sendResponseHeaders(200, response.length()); + + OutputStream os = t.getResponseBody(); + try { + os.write(response.getBytes()); + } finally { + os.close(); + } + } + + private void handleClientUpdate(HttpExchange t, ConsoleProxyClient viewer) throws IOException { + String response = viewer.onAjaxClientUpdate(); + + Headers hds = t.getResponseHeaders(); + hds.set("Content-Type", "text/javascript"); + t.sendResponseHeaders(200, response.length()); + + OutputStream os = t.getResponseBody(); + try { + os.write(response.getBytes()); + } finally { + os.close(); + } + } +} diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAjaxImageHandler.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAjaxImageHandler.java index 9c0094839cf..5e1014963f3 100644 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAjaxImageHandler.java +++ b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAjaxImageHandler.java @@ -14,87 +14,87 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.consoleproxy; - +package com.cloud.consoleproxy; + import java.awt.Graphics2D; import java.awt.Image; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.Map; - +import java.io.IOException; +import java.io.OutputStream; +import java.util.Map; + import com.cloud.consoleproxy.util.Logger; -import com.sun.net.httpserver.Headers; -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpHandler; - -public class ConsoleProxyAjaxImageHandler implements HttpHandler { - private static final Logger s_logger = Logger.getLogger(ConsoleProxyAjaxImageHandler.class); - - public void handle(HttpExchange t) throws IOException { - try { - if(s_logger.isDebugEnabled()) - s_logger.debug("AjaxImageHandler " + t.getRequestURI()); - - long startTick = System.currentTimeMillis(); - - doHandle(t); - - if(s_logger.isDebugEnabled()) - s_logger.debug(t.getRequestURI() + "Process time " + (System.currentTimeMillis() - startTick) + " ms"); - } catch (IOException e) { - throw e; - } catch (IllegalArgumentException e) { - s_logger.warn("Exception, ", e); - t.sendResponseHeaders(400, -1); // bad request - } catch(OutOfMemoryError e) { - s_logger.error("Unrecoverable OutOfMemory Error, exit and let it be re-launched"); - System.exit(1); - } catch(Throwable e) { - s_logger.error("Unexpected exception, ", e); - t.sendResponseHeaders(500, -1); // server error - } finally { - t.close(); - } - } - - private void doHandle(HttpExchange t) throws Exception, IllegalArgumentException { - String queries = t.getRequestURI().getQuery(); - Map queryMap = ConsoleProxyHttpHandlerHelper.getQueryMap(queries); - - String host = queryMap.get("host"); - String portStr = queryMap.get("port"); - String sid = queryMap.get("sid"); - String tag = queryMap.get("tag"); - String ticket = queryMap.get("ticket"); - String keyStr = queryMap.get("key"); - String console_url = queryMap.get("consoleurl"); - String console_host_session = queryMap.get("sessionref"); - String w = queryMap.get("w"); +import com.sun.net.httpserver.Headers; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; + +public class ConsoleProxyAjaxImageHandler implements HttpHandler { + private static final Logger s_logger = Logger.getLogger(ConsoleProxyAjaxImageHandler.class); + + public void handle(HttpExchange t) throws IOException { + try { + if(s_logger.isDebugEnabled()) + s_logger.debug("AjaxImageHandler " + t.getRequestURI()); + + long startTick = System.currentTimeMillis(); + + doHandle(t); + + if(s_logger.isDebugEnabled()) + s_logger.debug(t.getRequestURI() + "Process time " + (System.currentTimeMillis() - startTick) + " ms"); + } catch (IOException e) { + throw e; + } catch (IllegalArgumentException e) { + s_logger.warn("Exception, ", e); + t.sendResponseHeaders(400, -1); // bad request + } catch(OutOfMemoryError e) { + s_logger.error("Unrecoverable OutOfMemory Error, exit and let it be re-launched"); + System.exit(1); + } catch(Throwable e) { + s_logger.error("Unexpected exception, ", e); + t.sendResponseHeaders(500, -1); // server error + } finally { + t.close(); + } + } + + private void doHandle(HttpExchange t) throws Exception, IllegalArgumentException { + String queries = t.getRequestURI().getQuery(); + Map queryMap = ConsoleProxyHttpHandlerHelper.getQueryMap(queries); + + String host = queryMap.get("host"); + String portStr = queryMap.get("port"); + String sid = queryMap.get("sid"); + String tag = queryMap.get("tag"); + String ticket = queryMap.get("ticket"); + String keyStr = queryMap.get("key"); + String console_url = queryMap.get("consoleurl"); + String console_host_session = queryMap.get("sessionref"); + String w = queryMap.get("w"); String h = queryMap.get("h"); - + int key = 0; int width = 144; int height = 110; - - if(tag == null) - tag = ""; - - int port; - if(host == null || portStr == null || sid == null) - throw new IllegalArgumentException(); - - try { - port = Integer.parseInt(portStr); - } catch (NumberFormatException e) { - s_logger.warn("Invalid numeric parameter in query string: " + portStr); - throw new IllegalArgumentException(e); - } - - try { + + if(tag == null) + tag = ""; + + int port; + if(host == null || portStr == null || sid == null) + throw new IllegalArgumentException(); + + try { + port = Integer.parseInt(portStr); + } catch (NumberFormatException e) { + s_logger.warn("Invalid numeric parameter in query string: " + portStr); + throw new IllegalArgumentException(e); + } + + try { if (keyStr != null) - key = Integer.parseInt(keyStr); + key = Integer.parseInt(keyStr); if(null != w) width = Integer.parseInt(w); @@ -102,58 +102,58 @@ public class ConsoleProxyAjaxImageHandler implements HttpHandler { height = Integer.parseInt(h); } catch (NumberFormatException e) { - s_logger.warn("Invalid numeric parameter in query string: " + keyStr); - throw new IllegalArgumentException(e); - } + s_logger.warn("Invalid numeric parameter in query string: " + keyStr); + throw new IllegalArgumentException(e); + } - ConsoleProxyClientParam param = new ConsoleProxyClientParam(); - param.setClientHostAddress(host); - param.setClientHostPort(port); - param.setClientHostPassword(sid); - param.setClientTag(tag); - param.setTicket(ticket); - param.setClientTunnelUrl(console_url); - param.setClientTunnelSession(console_host_session); + ConsoleProxyClientParam param = new ConsoleProxyClientParam(); + param.setClientHostAddress(host); + param.setClientHostPort(port); + param.setClientHostPassword(sid); + param.setClientTag(tag); + param.setTicket(ticket); + param.setClientTunnelUrl(console_url); + param.setClientTunnelSession(console_host_session); - ConsoleProxyClient viewer = ConsoleProxy.getVncViewer(param); + ConsoleProxyClient viewer = ConsoleProxy.getVncViewer(param); - if (key == 0) { - Image scaledImage = viewer.getClientScaledImage(width, height); - BufferedImage bufferedImage = new BufferedImage(width, height, - BufferedImage.TYPE_3BYTE_BGR); - Graphics2D bufImageGraphics = bufferedImage.createGraphics(); - bufImageGraphics.drawImage(scaledImage, 0, 0, null); - ByteArrayOutputStream bos = new ByteArrayOutputStream(8196); - javax.imageio.ImageIO.write(bufferedImage, "jpg", bos); - byte[] bs = bos.toByteArray(); - Headers hds = t.getResponseHeaders(); - hds.set("Content-Type", "image/jpeg"); - hds.set("Cache-Control", "no-cache"); - hds.set("Cache-Control", "no-store"); - t.sendResponseHeaders(200, bs.length); - OutputStream os = t.getResponseBody(); - os.write(bs); - os.close(); - } else { - AjaxFIFOImageCache imageCache = viewer.getAjaxImageCache(); - byte[] img = imageCache.getImage(key); - - if(img != null) { - Headers hds = t.getResponseHeaders(); - hds.set("Content-Type", "image/jpeg"); - t.sendResponseHeaders(200, img.length); - - OutputStream os = t.getResponseBody(); - try { - os.write(img, 0, img.length); - } finally { - os.close(); - } - } else { - if(s_logger.isInfoEnabled()) - s_logger.info("Image has already been swept out, key: " + key); - t.sendResponseHeaders(404, -1); - } - } - } -} + if (key == 0) { + Image scaledImage = viewer.getClientScaledImage(width, height); + BufferedImage bufferedImage = new BufferedImage(width, height, + BufferedImage.TYPE_3BYTE_BGR); + Graphics2D bufImageGraphics = bufferedImage.createGraphics(); + bufImageGraphics.drawImage(scaledImage, 0, 0, null); + ByteArrayOutputStream bos = new ByteArrayOutputStream(8196); + javax.imageio.ImageIO.write(bufferedImage, "jpg", bos); + byte[] bs = bos.toByteArray(); + Headers hds = t.getResponseHeaders(); + hds.set("Content-Type", "image/jpeg"); + hds.set("Cache-Control", "no-cache"); + hds.set("Cache-Control", "no-store"); + t.sendResponseHeaders(200, bs.length); + OutputStream os = t.getResponseBody(); + os.write(bs); + os.close(); + } else { + AjaxFIFOImageCache imageCache = viewer.getAjaxImageCache(); + byte[] img = imageCache.getImage(key); + + if(img != null) { + Headers hds = t.getResponseHeaders(); + hds.set("Content-Type", "image/jpeg"); + t.sendResponseHeaders(200, img.length); + + OutputStream os = t.getResponseBody(); + try { + os.write(img, 0, img.length); + } finally { + os.close(); + } + } else { + if(s_logger.isInfoEnabled()) + s_logger.info("Image has already been swept out, key: " + key); + t.sendResponseHeaders(404, -1); + } + } + } +} diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAuthenticationResult.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAuthenticationResult.java index d9563b94d97..26ee9b33155 100644 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAuthenticationResult.java +++ b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAuthenticationResult.java @@ -14,68 +14,68 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.consoleproxy; - -// duplicated class -public class ConsoleProxyAuthenticationResult { - private boolean success; - private boolean isReauthentication; - private String host; - private int port; - private String tunnelUrl; - private String tunnelSession; - - public ConsoleProxyAuthenticationResult() { - success = false; - isReauthentication = false; - port = 0; - } - - public boolean isSuccess() { - return success; - } - - public void setSuccess(boolean success) { - this.success = success; - } - - public boolean isReauthentication() { - return isReauthentication; - } - - public void setReauthentication(boolean isReauthentication) { - this.isReauthentication = isReauthentication; - } - - public String getHost() { - return host; - } - - public void setHost(String host) { - this.host = host; - } - - public int getPort() { - return port; - } - - public void setPort(int port) { - this.port = port; - } - - public String getTunnelUrl() { - return tunnelUrl; - } - - public void setTunnelUrl(String tunnelUrl) { - this.tunnelUrl = tunnelUrl; - } - - public String getTunnelSession() { - return tunnelSession; - } - - public void setTunnelSession(String tunnelSession) { - this.tunnelSession = tunnelSession; - } -} +package com.cloud.consoleproxy; + +// duplicated class +public class ConsoleProxyAuthenticationResult { + private boolean success; + private boolean isReauthentication; + private String host; + private int port; + private String tunnelUrl; + private String tunnelSession; + + public ConsoleProxyAuthenticationResult() { + success = false; + isReauthentication = false; + port = 0; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public boolean isReauthentication() { + return isReauthentication; + } + + public void setReauthentication(boolean isReauthentication) { + this.isReauthentication = isReauthentication; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getTunnelUrl() { + return tunnelUrl; + } + + public void setTunnelUrl(String tunnelUrl) { + this.tunnelUrl = tunnelUrl; + } + + public String getTunnelSession() { + return tunnelSession; + } + + public void setTunnelSession(String tunnelSession) { + this.tunnelSession = tunnelSession; + } +} diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyBaseServerFactoryImpl.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyBaseServerFactoryImpl.java index 2706184a72e..c9ad8ab82fa 100644 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyBaseServerFactoryImpl.java +++ b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyBaseServerFactoryImpl.java @@ -14,8 +14,8 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.consoleproxy; - +package com.cloud.consoleproxy; + import java.io.IOException; import java.net.InetSocketAddress; @@ -23,26 +23,26 @@ import javax.net.ssl.SSLServerSocket; import com.cloud.consoleproxy.util.Logger; import com.sun.net.httpserver.HttpServer; - -public class ConsoleProxyBaseServerFactoryImpl implements ConsoleProxyServerFactory { - private static final Logger s_logger = Logger.getLogger(ConsoleProxyBaseServerFactoryImpl.class); - - @Override - public void init(byte[] ksBits, String ksPassword) { - } - - @Override - public HttpServer createHttpServerInstance(int port) throws IOException { - if(s_logger.isInfoEnabled()) - s_logger.info("create HTTP server instance at port: " + port); - return HttpServer.create(new InetSocketAddress(port), 5); - } - - @Override - public SSLServerSocket createSSLServerSocket(int port) throws IOException { - if(s_logger.isInfoEnabled()) - s_logger.info("SSL server socket is not supported in ConsoleProxyBaseServerFactoryImpl"); - - return null; - } -} + +public class ConsoleProxyBaseServerFactoryImpl implements ConsoleProxyServerFactory { + private static final Logger s_logger = Logger.getLogger(ConsoleProxyBaseServerFactoryImpl.class); + + @Override + public void init(byte[] ksBits, String ksPassword) { + } + + @Override + public HttpServer createHttpServerInstance(int port) throws IOException { + if(s_logger.isInfoEnabled()) + s_logger.info("create HTTP server instance at port: " + port); + return HttpServer.create(new InetSocketAddress(port), 5); + } + + @Override + public SSLServerSocket createSSLServerSocket(int port) throws IOException { + if(s_logger.isInfoEnabled()) + s_logger.info("SSL server socket is not supported in ConsoleProxyBaseServerFactoryImpl"); + + return null; + } +} diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyClientListener.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyClientListener.java index 89097d84af7..43a0bab8bed 100644 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyClientListener.java +++ b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyClientListener.java @@ -14,12 +14,12 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.consoleproxy; - -public interface ConsoleProxyClientListener { - void onFramebufferSizeChange(int w, int h); - void onFramebufferUpdate(int x, int y, int w, int h); - - void onClientConnected(); - void onClientClose(); -} +package com.cloud.consoleproxy; + +public interface ConsoleProxyClientListener { + void onFramebufferSizeChange(int w, int h); + void onFramebufferUpdate(int x, int y, int w, int h); + + void onClientConnected(); + void onClientClose(); +} diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyCmdHandler.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyCmdHandler.java index 5471dea148f..408eb0419d7 100644 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyCmdHandler.java +++ b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyCmdHandler.java @@ -14,57 +14,57 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.consoleproxy; - -import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; - +package com.cloud.consoleproxy; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; + import com.cloud.consoleproxy.util.Logger; -import com.sun.net.httpserver.Headers; -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpHandler; - -public class ConsoleProxyCmdHandler implements HttpHandler { - private static final Logger s_logger = Logger.getLogger(ConsoleProxyCmdHandler.class); - - public void handle(HttpExchange t) throws IOException { - try { - Thread.currentThread().setName("Cmd Thread " + - Thread.currentThread().getId() + " " + t.getRemoteAddress()); - s_logger.info("CmdHandler " + t.getRequestURI()); - doHandle(t); - } catch (Exception e) { - s_logger.error(e.toString(), e); - String response = "Not found"; - t.sendResponseHeaders(404, response.length()); - OutputStream os = t.getResponseBody(); - os.write(response.getBytes()); - os.close(); - } catch(OutOfMemoryError e) { - s_logger.error("Unrecoverable OutOfMemory Error, exit and let it be re-launched"); - System.exit(1); - } catch (Throwable e) { - s_logger.error(e.toString(), e); - } finally { - t.close(); - } - } - - public void doHandle(HttpExchange t) throws Exception { - String path = t.getRequestURI().getPath(); - int i = path.indexOf("/", 1); - String cmd = path.substring(i + 1); - s_logger.info("Get CMD request for " + cmd); - if (cmd.equals("getstatus")) { - ConsoleProxyClientStatsCollector statsCollector = ConsoleProxy.getStatsCollector(); - - Headers hds = t.getResponseHeaders(); - hds.set("Content-Type", "text/plain"); - t.sendResponseHeaders(200, 0); - OutputStreamWriter os = new OutputStreamWriter(t.getResponseBody()); - statsCollector.getStatsReport(os); - os.close(); - } - } -} +import com.sun.net.httpserver.Headers; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; + +public class ConsoleProxyCmdHandler implements HttpHandler { + private static final Logger s_logger = Logger.getLogger(ConsoleProxyCmdHandler.class); + + public void handle(HttpExchange t) throws IOException { + try { + Thread.currentThread().setName("Cmd Thread " + + Thread.currentThread().getId() + " " + t.getRemoteAddress()); + s_logger.info("CmdHandler " + t.getRequestURI()); + doHandle(t); + } catch (Exception e) { + s_logger.error(e.toString(), e); + String response = "Not found"; + t.sendResponseHeaders(404, response.length()); + OutputStream os = t.getResponseBody(); + os.write(response.getBytes()); + os.close(); + } catch(OutOfMemoryError e) { + s_logger.error("Unrecoverable OutOfMemory Error, exit and let it be re-launched"); + System.exit(1); + } catch (Throwable e) { + s_logger.error(e.toString(), e); + } finally { + t.close(); + } + } + + public void doHandle(HttpExchange t) throws Exception { + String path = t.getRequestURI().getPath(); + int i = path.indexOf("/", 1); + String cmd = path.substring(i + 1); + s_logger.info("Get CMD request for " + cmd); + if (cmd.equals("getstatus")) { + ConsoleProxyClientStatsCollector statsCollector = ConsoleProxy.getStatsCollector(); + + Headers hds = t.getResponseHeaders(); + hds.set("Content-Type", "text/plain"); + t.sendResponseHeaders(200, 0); + OutputStreamWriter os = new OutputStreamWriter(t.getResponseBody()); + statsCollector.getStatsReport(os); + os.close(); + } + } +} diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyHttpHandlerHelper.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyHttpHandlerHelper.java index cf2027b86ec..7756d01cd7f 100644 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyHttpHandlerHelper.java +++ b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyHttpHandlerHelper.java @@ -14,61 +14,61 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.consoleproxy; - -import java.util.HashMap; -import java.util.Map; - -import com.cloud.consoleproxy.util.Logger; - -public class ConsoleProxyHttpHandlerHelper { - private static final Logger s_logger = Logger.getLogger(ConsoleProxyHttpHandlerHelper.class); - - public static Map getQueryMap(String query) { - String[] params = query.split("&"); - Map map = new HashMap(); - for (String param : params) { - String[] paramTokens = param.split("="); - if(paramTokens != null && paramTokens.length == 2) { - String name = param.split("=")[0]; - String value = param.split("=")[1]; - map.put(name, value); - } else if (paramTokens.length == 3) { - // very ugly, added for Xen tunneling url - String name = paramTokens[0]; - String value = paramTokens[1] + "=" + paramTokens[2]; - map.put(name, value); - } else { - if(s_logger.isDebugEnabled()) - s_logger.debug("Invalid paramemter in URL found. param: " + param); - } - } - - // This is a ugly solution for now. We will do encryption/decryption translation - // here to make it transparent to rest of the code. - if(map.get("token") != null) { - ConsoleProxyPasswordBasedEncryptor encryptor = new ConsoleProxyPasswordBasedEncryptor( - ConsoleProxy.getEncryptorPassword()); - - ConsoleProxyClientParam param = encryptor.decryptObject(ConsoleProxyClientParam.class, map.get("token")); - if(param != null) { - if(param.getClientHostAddress() != null) - map.put("host", param.getClientHostAddress()); - if(param.getClientHostPort() != 0) - map.put("port", String.valueOf(param.getClientHostPort())); - if(param.getClientTag() != null) - map.put("tag", param.getClientTag()); - if(param.getClientHostPassword() != null) - map.put("sid", param.getClientHostPassword()); - if(param.getClientTunnelUrl() != null) - map.put("consoleurl", param.getClientTunnelUrl()); - if(param.getClientTunnelSession() != null) - map.put("sessionref", param.getClientTunnelSession()); - if(param.getTicket() != null) - map.put("ticket", param.getTicket()); - } - } - - return map; - } -} +package com.cloud.consoleproxy; + +import java.util.HashMap; +import java.util.Map; + +import com.cloud.consoleproxy.util.Logger; + +public class ConsoleProxyHttpHandlerHelper { + private static final Logger s_logger = Logger.getLogger(ConsoleProxyHttpHandlerHelper.class); + + public static Map getQueryMap(String query) { + String[] params = query.split("&"); + Map map = new HashMap(); + for (String param : params) { + String[] paramTokens = param.split("="); + if(paramTokens != null && paramTokens.length == 2) { + String name = param.split("=")[0]; + String value = param.split("=")[1]; + map.put(name, value); + } else if (paramTokens.length == 3) { + // very ugly, added for Xen tunneling url + String name = paramTokens[0]; + String value = paramTokens[1] + "=" + paramTokens[2]; + map.put(name, value); + } else { + if(s_logger.isDebugEnabled()) + s_logger.debug("Invalid paramemter in URL found. param: " + param); + } + } + + // This is a ugly solution for now. We will do encryption/decryption translation + // here to make it transparent to rest of the code. + if(map.get("token") != null) { + ConsoleProxyPasswordBasedEncryptor encryptor = new ConsoleProxyPasswordBasedEncryptor( + ConsoleProxy.getEncryptorPassword()); + + ConsoleProxyClientParam param = encryptor.decryptObject(ConsoleProxyClientParam.class, map.get("token")); + if(param != null) { + if(param.getClientHostAddress() != null) + map.put("host", param.getClientHostAddress()); + if(param.getClientHostPort() != 0) + map.put("port", String.valueOf(param.getClientHostPort())); + if(param.getClientTag() != null) + map.put("tag", param.getClientTag()); + if(param.getClientHostPassword() != null) + map.put("sid", param.getClientHostPassword()); + if(param.getClientTunnelUrl() != null) + map.put("consoleurl", param.getClientTunnelUrl()); + if(param.getClientTunnelSession() != null) + map.put("sessionref", param.getClientTunnelSession()); + if(param.getTicket() != null) + map.put("ticket", param.getTicket()); + } + } + + return map; + } +} diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyLoggerFactory.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyLoggerFactory.java index 701ad7a277d..ff66de3bcc4 100644 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyLoggerFactory.java +++ b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyLoggerFactory.java @@ -14,76 +14,76 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.consoleproxy; +package com.cloud.consoleproxy; import com.cloud.consoleproxy.util.Logger; import com.cloud.consoleproxy.util.LoggerFactory; - -public class ConsoleProxyLoggerFactory implements LoggerFactory { - public ConsoleProxyLoggerFactory() { - } - - public Logger getLogger(Class clazz) { - return new Log4jLogger(org.apache.log4j.Logger.getLogger(clazz)); - } - - public static class Log4jLogger extends Logger { - private org.apache.log4j.Logger logger; - - public Log4jLogger(org.apache.log4j.Logger logger) { - this.logger = logger; - } - - public boolean isTraceEnabled() { - return logger.isTraceEnabled(); - } - - public boolean isDebugEnabled() { - return logger.isDebugEnabled(); - } - - public boolean isInfoEnabled() { - return logger.isInfoEnabled(); - } - - public void trace(Object message) { - logger.trace(message); - } - - public void trace(Object message, Throwable exception) { - logger.trace(message, exception); - } - - public void info(Object message) { - logger.info(message); - } - - public void info(Object message, Throwable exception) { - logger.info(message, exception); - } - - public void debug(Object message) { - logger.debug(message); - } - - public void debug(Object message, Throwable exception) { - logger.debug(message, exception); - } - - public void warn(Object message) { - logger.warn(message); - } - - public void warn(Object message, Throwable exception) { - logger.warn(message, exception); - } - - public void error(Object message) { - logger.error(message); - } - - public void error(Object message, Throwable exception) { - logger.error(message, exception); - } - } -} + +public class ConsoleProxyLoggerFactory implements LoggerFactory { + public ConsoleProxyLoggerFactory() { + } + + public Logger getLogger(Class clazz) { + return new Log4jLogger(org.apache.log4j.Logger.getLogger(clazz)); + } + + public static class Log4jLogger extends Logger { + private org.apache.log4j.Logger logger; + + public Log4jLogger(org.apache.log4j.Logger logger) { + this.logger = logger; + } + + public boolean isTraceEnabled() { + return logger.isTraceEnabled(); + } + + public boolean isDebugEnabled() { + return logger.isDebugEnabled(); + } + + public boolean isInfoEnabled() { + return logger.isInfoEnabled(); + } + + public void trace(Object message) { + logger.trace(message); + } + + public void trace(Object message, Throwable exception) { + logger.trace(message, exception); + } + + public void info(Object message) { + logger.info(message); + } + + public void info(Object message, Throwable exception) { + logger.info(message, exception); + } + + public void debug(Object message) { + logger.debug(message); + } + + public void debug(Object message, Throwable exception) { + logger.debug(message, exception); + } + + public void warn(Object message) { + logger.warn(message); + } + + public void warn(Object message, Throwable exception) { + logger.warn(message, exception); + } + + public void error(Object message) { + logger.error(message); + } + + public void error(Object message, Throwable exception) { + logger.error(message, exception); + } + } +} diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyMonitor.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyMonitor.java index be5df6efa59..030b2f452eb 100644 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyMonitor.java +++ b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyMonitor.java @@ -34,120 +34,120 @@ import com.cloud.consoleproxy.util.Logger; // itself and the shell script will re-launch console proxy // public class ConsoleProxyMonitor { - private static final Logger s_logger = Logger.getLogger(ConsoleProxyMonitor.class); - - private String[] _argv; - private Map _argMap = new HashMap(); - - private volatile Process _process; - private boolean _quit = false; - - public ConsoleProxyMonitor(String[] argv) { - _argv = argv; - - for(String arg : _argv) { - String[] tokens = arg.split("="); - if(tokens.length == 2) { - s_logger.info("Add argument " + tokens[0] + "=" + tokens[1] + " to the argument map"); + private static final Logger s_logger = Logger.getLogger(ConsoleProxyMonitor.class); + + private String[] _argv; + private Map _argMap = new HashMap(); + + private volatile Process _process; + private boolean _quit = false; + + public ConsoleProxyMonitor(String[] argv) { + _argv = argv; + + for(String arg : _argv) { + String[] tokens = arg.split("="); + if(tokens.length == 2) { + s_logger.info("Add argument " + tokens[0] + "=" + tokens[1] + " to the argument map"); - _argMap.put(tokens[0].trim(), tokens[1].trim()); - } else { - s_logger.warn("unrecognized argument, skip adding it to argument map"); - } - } - } - - private void run() { - Runtime.getRuntime().addShutdownHook(new Thread() { - @Override - public void run() { - _quit = true; - onShutdown(); - } - }); - - while(!_quit) { - String cmdLine = getLaunchCommandLine(); - - s_logger.info("Launch console proxy process with command line: " + cmdLine); - - try { - _process = Runtime.getRuntime().exec(cmdLine); - } catch (IOException e) { - s_logger.error("Unexpected exception ", e); - System.exit(1); - } - - boolean waitSucceeded = false; - int exitCode = 0; - while(!waitSucceeded) { - try { - exitCode = _process.waitFor(); - waitSucceeded = true; - - if(s_logger.isInfoEnabled()) - s_logger.info("Console proxy process exits with code: " + exitCode); - } catch (InterruptedException e) { - if(s_logger.isInfoEnabled()) - s_logger.info("InterruptedException while waiting for termination of console proxy, will retry"); - } - } - } - } - - private String getLaunchCommandLine() { - StringBuffer sb = new StringBuffer("java "); - String jvmOptions = _argMap.get("jvmoptions"); - - if(jvmOptions != null) - sb.append(jvmOptions); - - for(Map.Entry entry : _argMap.entrySet()) { - if(!"jvmoptions".equalsIgnoreCase(entry.getKey())) - sb.append(" ").append(entry.getKey()).append("=").append(entry.getValue()); - } - - return sb.toString(); - } - - private void onShutdown() { - if(_process != null) { - if(s_logger.isInfoEnabled()) - s_logger.info("Console proxy monitor shuts dwon, terminate console proxy process"); - _process.destroy(); - } - } + _argMap.put(tokens[0].trim(), tokens[1].trim()); + } else { + s_logger.warn("unrecognized argument, skip adding it to argument map"); + } + } + } + + private void run() { + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + _quit = true; + onShutdown(); + } + }); + + while(!_quit) { + String cmdLine = getLaunchCommandLine(); + + s_logger.info("Launch console proxy process with command line: " + cmdLine); + + try { + _process = Runtime.getRuntime().exec(cmdLine); + } catch (IOException e) { + s_logger.error("Unexpected exception ", e); + System.exit(1); + } + + boolean waitSucceeded = false; + int exitCode = 0; + while(!waitSucceeded) { + try { + exitCode = _process.waitFor(); + waitSucceeded = true; + + if(s_logger.isInfoEnabled()) + s_logger.info("Console proxy process exits with code: " + exitCode); + } catch (InterruptedException e) { + if(s_logger.isInfoEnabled()) + s_logger.info("InterruptedException while waiting for termination of console proxy, will retry"); + } + } + } + } + + private String getLaunchCommandLine() { + StringBuffer sb = new StringBuffer("java "); + String jvmOptions = _argMap.get("jvmoptions"); + + if(jvmOptions != null) + sb.append(jvmOptions); + + for(Map.Entry entry : _argMap.entrySet()) { + if(!"jvmoptions".equalsIgnoreCase(entry.getKey())) + sb.append(" ").append(entry.getKey()).append("=").append(entry.getValue()); + } + + return sb.toString(); + } + + private void onShutdown() { + if(_process != null) { + if(s_logger.isInfoEnabled()) + s_logger.info("Console proxy monitor shuts dwon, terminate console proxy process"); + _process.destroy(); + } + } - private static void configLog4j() { - URL configUrl = System.class.getResource("/conf/log4j-cloud.xml"); - if(configUrl == null) - configUrl = ClassLoader.getSystemResource("log4j-cloud.xml"); - - if(configUrl == null) - configUrl = ClassLoader.getSystemResource("conf/log4j-cloud.xml"); - - if(configUrl != null) { - try { - System.out.println("Configure log4j using " + configUrl.toURI().toString()); - } catch (URISyntaxException e1) { - e1.printStackTrace(); - } + private static void configLog4j() { + URL configUrl = System.class.getResource("/conf/log4j-cloud.xml"); + if(configUrl == null) + configUrl = ClassLoader.getSystemResource("log4j-cloud.xml"); + + if(configUrl == null) + configUrl = ClassLoader.getSystemResource("conf/log4j-cloud.xml"); + + if(configUrl != null) { + try { + System.out.println("Configure log4j using " + configUrl.toURI().toString()); + } catch (URISyntaxException e1) { + e1.printStackTrace(); + } - try { - File file = new File(configUrl.toURI()); - - System.out.println("Log4j configuration from : " + file.getAbsolutePath()); - DOMConfigurator.configureAndWatch(file.getAbsolutePath(), 10000); - } catch (URISyntaxException e) { - System.out.println("Unable to convert log4j configuration Url to URI"); - } - } else { - System.out.println("Configure log4j with default properties"); - } - } - - public static void main(String[] argv) { - configLog4j(); - (new ConsoleProxyMonitor(argv)).run(); - } + try { + File file = new File(configUrl.toURI()); + + System.out.println("Log4j configuration from : " + file.getAbsolutePath()); + DOMConfigurator.configureAndWatch(file.getAbsolutePath(), 10000); + } catch (URISyntaxException e) { + System.out.println("Unable to convert log4j configuration Url to URI"); + } + } else { + System.out.println("Configure log4j with default properties"); + } + } + + public static void main(String[] argv) { + configLog4j(); + (new ConsoleProxyMonitor(argv)).run(); + } } diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyResourceHandler.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyResourceHandler.java index f9dc21e1575..7d160472e8a 100644 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyResourceHandler.java +++ b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyResourceHandler.java @@ -14,168 +14,168 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.consoleproxy; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; - +package com.cloud.consoleproxy; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + import com.cloud.consoleproxy.util.Logger; -import com.sun.net.httpserver.Headers; -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpHandler; - -public class ConsoleProxyResourceHandler implements HttpHandler { - private static final Logger s_logger = Logger.getLogger(ConsoleProxyResourceHandler.class); - - static Map s_mimeTypes; - static { - s_mimeTypes = new HashMap(); - s_mimeTypes.put("jar", "application/java-archive"); - s_mimeTypes.put("js", "text/javascript"); - s_mimeTypes.put("css", "text/css"); - s_mimeTypes.put("jpg", "image/jpeg"); - s_mimeTypes.put("html", "text/html"); - s_mimeTypes.put("htm", "text/html"); - s_mimeTypes.put("log", "text/plain"); - } - - static Map s_validResourceFolders; - static { - s_validResourceFolders = new HashMap(); - s_validResourceFolders.put("applet", ""); - s_validResourceFolders.put("logs", ""); - s_validResourceFolders.put("images", ""); - s_validResourceFolders.put("js", ""); - s_validResourceFolders.put("css", ""); - s_validResourceFolders.put("html", ""); - } - - public ConsoleProxyResourceHandler() { - } - - public void handle(HttpExchange t) throws IOException { - try { - if(s_logger.isDebugEnabled()) - s_logger.debug("Resource Handler " + t.getRequestURI()); - - long startTick = System.currentTimeMillis(); - - doHandle(t); - - if(s_logger.isDebugEnabled()) - s_logger.debug(t.getRequestURI() + " Process time " + (System.currentTimeMillis() - startTick) + " ms"); - } catch (IOException e) { - throw e; - } catch(Throwable e) { - s_logger.error("Unexpected exception, ", e); - t.sendResponseHeaders(500, -1); // server error - } finally { - t.close(); - } - } - - @SuppressWarnings("deprecation") - private void doHandle(HttpExchange t) throws Exception { - String path = t.getRequestURI().getPath(); - - if(s_logger.isInfoEnabled()) - s_logger.info("Get resource request for " + path); - - int i = path.indexOf("/", 1); - String filepath = path.substring(i + 1); - i = path.lastIndexOf("."); - String extension = (i == -1) ? "" : path.substring(i + 1); - String contentType = getContentType(extension); - - if(!validatePath(filepath)) { - if(s_logger.isInfoEnabled()) - s_logger.info("Resource access is forbidden, uri: " + path); - - t.sendResponseHeaders(403, -1); // forbidden - return; - } - - File f = new File ("./" + filepath); - if(f.exists()) { - long lastModified = f.lastModified(); - String ifModifiedSince = t.getRequestHeaders().getFirst("If-Modified-Since"); - if (ifModifiedSince != null) { - long d = Date.parse(ifModifiedSince); - if (d + 1000 >= lastModified) { - Headers hds = t.getResponseHeaders(); - hds.set("Content-Type", contentType); - t.sendResponseHeaders(304, -1); - - if(s_logger.isInfoEnabled()) - s_logger.info("Sent 304 file has not been " + - "modified since " + ifModifiedSince); - return; - } - } - - long length = f.length(); - Headers hds = t.getResponseHeaders(); - hds.set("Content-Type", contentType); - hds.set("Last-Modified", new Date(lastModified).toGMTString()); - t.sendResponseHeaders(200, length); - responseFileContent(t, f); - - if(s_logger.isInfoEnabled()) - s_logger.info("Sent file " + path + " with content type " + contentType); - } else { - if(s_logger.isInfoEnabled()) - s_logger.info("file does not exist" + path); - t.sendResponseHeaders(404, -1); - } - } - - private static String getContentType(String extension) { - String key = extension.toLowerCase(); - if(s_mimeTypes.containsKey(key)) { - return s_mimeTypes.get(key); - } - return "application/octet-stream"; - } - - private static void responseFileContent(HttpExchange t, File f) throws Exception { - OutputStream os = t.getResponseBody(); - FileInputStream fis = new FileInputStream(f); - while (true) { - byte[] b = new byte[8192]; - int n = fis.read(b); - if (n < 0) { - break; - } - os.write(b, 0, n); - } - fis.close(); - os.close(); - } - - private static boolean validatePath(String path) { - int i = path.indexOf("/"); - if(i == -1) { - if(s_logger.isInfoEnabled()) - s_logger.info("Invalid resource path: can not start at resource root"); - return false; - } - - if(path.contains("..")) { - if(s_logger.isInfoEnabled()) - s_logger.info("Invalid resource path: contains relative up-level navigation"); - - return false; - } - - return isValidResourceFolder(path.substring(0, i)); - } - - private static boolean isValidResourceFolder(String name) { - return s_validResourceFolders.containsKey(name); - } -} +import com.sun.net.httpserver.Headers; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; + +public class ConsoleProxyResourceHandler implements HttpHandler { + private static final Logger s_logger = Logger.getLogger(ConsoleProxyResourceHandler.class); + + static Map s_mimeTypes; + static { + s_mimeTypes = new HashMap(); + s_mimeTypes.put("jar", "application/java-archive"); + s_mimeTypes.put("js", "text/javascript"); + s_mimeTypes.put("css", "text/css"); + s_mimeTypes.put("jpg", "image/jpeg"); + s_mimeTypes.put("html", "text/html"); + s_mimeTypes.put("htm", "text/html"); + s_mimeTypes.put("log", "text/plain"); + } + + static Map s_validResourceFolders; + static { + s_validResourceFolders = new HashMap(); + s_validResourceFolders.put("applet", ""); + s_validResourceFolders.put("logs", ""); + s_validResourceFolders.put("images", ""); + s_validResourceFolders.put("js", ""); + s_validResourceFolders.put("css", ""); + s_validResourceFolders.put("html", ""); + } + + public ConsoleProxyResourceHandler() { + } + + public void handle(HttpExchange t) throws IOException { + try { + if(s_logger.isDebugEnabled()) + s_logger.debug("Resource Handler " + t.getRequestURI()); + + long startTick = System.currentTimeMillis(); + + doHandle(t); + + if(s_logger.isDebugEnabled()) + s_logger.debug(t.getRequestURI() + " Process time " + (System.currentTimeMillis() - startTick) + " ms"); + } catch (IOException e) { + throw e; + } catch(Throwable e) { + s_logger.error("Unexpected exception, ", e); + t.sendResponseHeaders(500, -1); // server error + } finally { + t.close(); + } + } + + @SuppressWarnings("deprecation") + private void doHandle(HttpExchange t) throws Exception { + String path = t.getRequestURI().getPath(); + + if(s_logger.isInfoEnabled()) + s_logger.info("Get resource request for " + path); + + int i = path.indexOf("/", 1); + String filepath = path.substring(i + 1); + i = path.lastIndexOf("."); + String extension = (i == -1) ? "" : path.substring(i + 1); + String contentType = getContentType(extension); + + if(!validatePath(filepath)) { + if(s_logger.isInfoEnabled()) + s_logger.info("Resource access is forbidden, uri: " + path); + + t.sendResponseHeaders(403, -1); // forbidden + return; + } + + File f = new File ("./" + filepath); + if(f.exists()) { + long lastModified = f.lastModified(); + String ifModifiedSince = t.getRequestHeaders().getFirst("If-Modified-Since"); + if (ifModifiedSince != null) { + long d = Date.parse(ifModifiedSince); + if (d + 1000 >= lastModified) { + Headers hds = t.getResponseHeaders(); + hds.set("Content-Type", contentType); + t.sendResponseHeaders(304, -1); + + if(s_logger.isInfoEnabled()) + s_logger.info("Sent 304 file has not been " + + "modified since " + ifModifiedSince); + return; + } + } + + long length = f.length(); + Headers hds = t.getResponseHeaders(); + hds.set("Content-Type", contentType); + hds.set("Last-Modified", new Date(lastModified).toGMTString()); + t.sendResponseHeaders(200, length); + responseFileContent(t, f); + + if(s_logger.isInfoEnabled()) + s_logger.info("Sent file " + path + " with content type " + contentType); + } else { + if(s_logger.isInfoEnabled()) + s_logger.info("file does not exist" + path); + t.sendResponseHeaders(404, -1); + } + } + + private static String getContentType(String extension) { + String key = extension.toLowerCase(); + if(s_mimeTypes.containsKey(key)) { + return s_mimeTypes.get(key); + } + return "application/octet-stream"; + } + + private static void responseFileContent(HttpExchange t, File f) throws Exception { + OutputStream os = t.getResponseBody(); + FileInputStream fis = new FileInputStream(f); + while (true) { + byte[] b = new byte[8192]; + int n = fis.read(b); + if (n < 0) { + break; + } + os.write(b, 0, n); + } + fis.close(); + os.close(); + } + + private static boolean validatePath(String path) { + int i = path.indexOf("/"); + if(i == -1) { + if(s_logger.isInfoEnabled()) + s_logger.info("Invalid resource path: can not start at resource root"); + return false; + } + + if(path.contains("..")) { + if(s_logger.isInfoEnabled()) + s_logger.info("Invalid resource path: contains relative up-level navigation"); + + return false; + } + + return isValidResourceFolder(path.substring(0, i)); + } + + private static boolean isValidResourceFolder(String name) { + return s_validResourceFolders.containsKey(name); + } +} diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxySecureServerFactoryImpl.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxySecureServerFactoryImpl.java index 42d069f9331..ee0ee13fa92 100644 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxySecureServerFactoryImpl.java +++ b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxySecureServerFactoryImpl.java @@ -14,8 +14,8 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.consoleproxy; - +package com.cloud.consoleproxy; + import java.io.ByteArrayInputStream; import java.io.FileInputStream; import java.io.IOException; @@ -35,111 +35,111 @@ import com.sun.net.httpserver.HttpServer; import com.sun.net.httpserver.HttpsConfigurator; import com.sun.net.httpserver.HttpsParameters; import com.sun.net.httpserver.HttpsServer; - -public class ConsoleProxySecureServerFactoryImpl implements ConsoleProxyServerFactory { - private static final Logger s_logger = Logger.getLogger(ConsoleProxySecureServerFactoryImpl.class); - - private SSLContext sslContext = null; - - public ConsoleProxySecureServerFactoryImpl() { - } - - @Override - public void init(byte[] ksBits, String ksPassword) { - s_logger.info("Start initializing SSL"); - if(ksBits == null) { - try { - s_logger.info("Initializing SSL from built-in default certificate"); - - char[] passphrase = "vmops.com".toCharArray(); - KeyStore ks = KeyStore.getInstance("JKS"); - - ks.load(new FileInputStream("certs/realhostip.keystore"), passphrase); - // ks.load(ConsoleProxy.class.getResourceAsStream("/realhostip.keystore"), passphrase); - - s_logger.info("SSL certificate loaded"); - - KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); - kmf.init(ks, passphrase); - s_logger.info("Key manager factory is initialized"); +public class ConsoleProxySecureServerFactoryImpl implements ConsoleProxyServerFactory { + private static final Logger s_logger = Logger.getLogger(ConsoleProxySecureServerFactoryImpl.class); + + private SSLContext sslContext = null; + + public ConsoleProxySecureServerFactoryImpl() { + } + + @Override + public void init(byte[] ksBits, String ksPassword) { + s_logger.info("Start initializing SSL"); - TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); - tmf.init(ks); - s_logger.info("Trust manager factory is initialized"); + if(ksBits == null) { + try { + s_logger.info("Initializing SSL from built-in default certificate"); + + char[] passphrase = "vmops.com".toCharArray(); + KeyStore ks = KeyStore.getInstance("JKS"); + + ks.load(new FileInputStream("certs/realhostip.keystore"), passphrase); + // ks.load(ConsoleProxy.class.getResourceAsStream("/realhostip.keystore"), passphrase); + + s_logger.info("SSL certificate loaded"); + + KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); + kmf.init(ks, passphrase); + s_logger.info("Key manager factory is initialized"); - sslContext = SSLContext.getInstance("TLS"); - sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); - s_logger.info("SSL context is initialized"); - } catch (Exception ioe) { - s_logger.error(ioe.toString(), ioe); - } - - } else { - char[] passphrase = ksPassword != null ? ksPassword.toCharArray() : null; - try { - s_logger.info("Initializing SSL from passed-in certificate"); - - KeyStore ks = KeyStore.getInstance("JKS"); - ks.load(new ByteArrayInputStream(ksBits), passphrase); - - KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); - kmf.init(ks, passphrase); - s_logger.info("Key manager factory is initialized"); - - TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); - tmf.init(ks); - s_logger.info("Trust manager factory is initialized"); - - sslContext = SSLContext.getInstance("TLS"); - sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); - s_logger.info("SSL context is initialized"); - } catch(Exception e) { - s_logger.error("Unable to init factory due to exception ", e); - } - } - - } - - public HttpServer createHttpServerInstance(int port) throws IOException { - try { - HttpsServer server = HttpsServer.create(new InetSocketAddress(port), 5); - server.setHttpsConfigurator (new HttpsConfigurator(sslContext) { - @Override - public void configure (HttpsParameters params) { - - // get the remote address if needed - InetSocketAddress remote = params.getClientAddress(); - SSLContext c = getSSLContext(); - - // get the default parameters - SSLParameters sslparams = c.getDefaultSSLParameters(); - - params.setSSLParameters(sslparams); - // statement above could throw IAE if any params invalid. - // eg. if app has a UI and parameters supplied by a user. - } - }); - - s_logger.info("create HTTPS server instance on port: " + port); - return server; - } catch (Exception ioe) { - s_logger.error(ioe.toString(), ioe); - } - return null; - } - - public SSLServerSocket createSSLServerSocket(int port) throws IOException { - try { - SSLServerSocket srvSock = null; - SSLServerSocketFactory ssf = sslContext.getServerSocketFactory(); - srvSock = (SSLServerSocket) ssf.createServerSocket(port); - - s_logger.info("create SSL server socket on port: " + port); - return srvSock; - } catch (Exception ioe) { - s_logger.error(ioe.toString(), ioe); - } - return null; - } -} + TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); + tmf.init(ks); + s_logger.info("Trust manager factory is initialized"); + + sslContext = SSLContext.getInstance("TLS"); + sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + s_logger.info("SSL context is initialized"); + } catch (Exception ioe) { + s_logger.error(ioe.toString(), ioe); + } + + } else { + char[] passphrase = ksPassword != null ? ksPassword.toCharArray() : null; + try { + s_logger.info("Initializing SSL from passed-in certificate"); + + KeyStore ks = KeyStore.getInstance("JKS"); + ks.load(new ByteArrayInputStream(ksBits), passphrase); + + KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); + kmf.init(ks, passphrase); + s_logger.info("Key manager factory is initialized"); + + TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); + tmf.init(ks); + s_logger.info("Trust manager factory is initialized"); + + sslContext = SSLContext.getInstance("TLS"); + sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + s_logger.info("SSL context is initialized"); + } catch(Exception e) { + s_logger.error("Unable to init factory due to exception ", e); + } + } + + } + + public HttpServer createHttpServerInstance(int port) throws IOException { + try { + HttpsServer server = HttpsServer.create(new InetSocketAddress(port), 5); + server.setHttpsConfigurator (new HttpsConfigurator(sslContext) { + @Override + public void configure (HttpsParameters params) { + + // get the remote address if needed + InetSocketAddress remote = params.getClientAddress(); + SSLContext c = getSSLContext(); + + // get the default parameters + SSLParameters sslparams = c.getDefaultSSLParameters(); + + params.setSSLParameters(sslparams); + // statement above could throw IAE if any params invalid. + // eg. if app has a UI and parameters supplied by a user. + } + }); + + s_logger.info("create HTTPS server instance on port: " + port); + return server; + } catch (Exception ioe) { + s_logger.error(ioe.toString(), ioe); + } + return null; + } + + public SSLServerSocket createSSLServerSocket(int port) throws IOException { + try { + SSLServerSocket srvSock = null; + SSLServerSocketFactory ssf = sslContext.getServerSocketFactory(); + srvSock = (SSLServerSocket) ssf.createServerSocket(port); + + s_logger.info("create SSL server socket on port: " + port); + return srvSock; + } catch (Exception ioe) { + s_logger.error(ioe.toString(), ioe); + } + return null; + } +} diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyServerFactory.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyServerFactory.java index 9d65c00d096..7e0e5c77bf4 100644 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyServerFactory.java +++ b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyServerFactory.java @@ -14,16 +14,16 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.consoleproxy; - +package com.cloud.consoleproxy; + import java.io.IOException; import javax.net.ssl.SSLServerSocket; import com.sun.net.httpserver.HttpServer; - + public interface ConsoleProxyServerFactory { - void init(byte[] ksBits, String ksPassword); - HttpServer createHttpServerInstance(int port) throws IOException; - SSLServerSocket createSSLServerSocket(int port) throws IOException; -} + void init(byte[] ksBits, String ksPassword); + HttpServer createHttpServerInstance(int port) throws IOException; + SSLServerSocket createSSLServerSocket(int port) throws IOException; +} diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyThumbnailHandler.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyThumbnailHandler.java index 82d45b77e74..6d34d3b9162 100644 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyThumbnailHandler.java +++ b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyThumbnailHandler.java @@ -14,199 +14,199 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.consoleproxy; - -import java.awt.Color; -import java.awt.Font; -import java.awt.FontMetrics; -import java.awt.Graphics2D; -import java.awt.Image; -import java.awt.image.BufferedImage; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.HashMap; -import java.util.Map; - +package com.cloud.consoleproxy; + +import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.Map; + import com.cloud.consoleproxy.util.Logger; -import com.sun.net.httpserver.Headers; -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpHandler; - -public class ConsoleProxyThumbnailHandler implements HttpHandler { - private static final Logger s_logger = Logger.getLogger(ConsoleProxyThumbnailHandler.class); - - public ConsoleProxyThumbnailHandler() { - } - - public void handle(HttpExchange t) throws IOException { - try { - Thread.currentThread().setName("JPG Thread " + - Thread.currentThread().getId() + " " + t.getRemoteAddress()); - - if(s_logger.isDebugEnabled()) - s_logger.debug("ScreenHandler " + t.getRequestURI()); - - long startTick = System.currentTimeMillis(); - doHandle(t); - - if(s_logger.isDebugEnabled()) - s_logger.debug(t.getRequestURI() + "Process time " + (System.currentTimeMillis() - startTick) + " ms"); - } catch (IllegalArgumentException e) { - String response = "Bad query string"; - s_logger.error(response + ", request URI : " + t.getRequestURI()); - t.sendResponseHeaders(200, response.length()); - OutputStream os = t.getResponseBody(); - os.write(response.getBytes()); - os.close(); - } catch(OutOfMemoryError e) { - s_logger.error("Unrecoverable OutOfMemory Error, exit and let it be re-launched"); - System.exit(1); - } catch (Throwable e) { - s_logger.error("Unexpected exception while handing thumbnail request, ", e); - - String queries = t.getRequestURI().getQuery(); - Map queryMap = getQueryMap(queries); - int width = 0; - int height = 0; - String ws = queryMap.get("w"); - String hs = queryMap.get("h"); - try { - width = Integer.parseInt(ws); - height = Integer.parseInt(hs); - } catch (NumberFormatException ex) { - } - width = Math.min(width, 800); - height = Math.min(height, 600); - - BufferedImage img = generateTextImage(width, height, "Cannot Connect"); - ByteArrayOutputStream bos = new ByteArrayOutputStream(8196); - javax.imageio.ImageIO.write(img, "jpg", bos); - byte[] bs = bos.toByteArray(); - Headers hds = t.getResponseHeaders(); - hds.set("Content-Type", "image/jpeg"); - hds.set("Cache-Control", "no-cache"); - hds.set("Cache-Control", "no-store"); - t.sendResponseHeaders(200, bs.length); - OutputStream os = t.getResponseBody(); - os.write(bs); - os.close(); - s_logger.error("Cannot get console, sent error JPG response for " + t.getRequestURI()); - return; - } finally { - t.close(); - } - } - - private void doHandle(HttpExchange t) throws Exception, IllegalArgumentException { - String queries = t.getRequestURI().getQuery(); - Map queryMap = getQueryMap(queries); - int width = 0; - int height = 0; - int port = 0; - String ws = queryMap.get("w"); - String hs = queryMap.get("h"); - String host = queryMap.get("host"); - String portStr = queryMap.get("port"); - String sid = queryMap.get("sid"); - String tag = queryMap.get("tag"); - String ticket = queryMap.get("ticket"); - String console_url = queryMap.get("consoleurl"); - String console_host_session = queryMap.get("sessionref"); +import com.sun.net.httpserver.Headers; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; - if(tag == null) - tag = ""; - - if (ws == null || hs == null || host == null || portStr == null || sid == null ) { - throw new IllegalArgumentException(); - } - try { - width = Integer.parseInt(ws); - height = Integer.parseInt(hs); - port = Integer.parseInt(portStr); - } catch (NumberFormatException e) { - throw new IllegalArgumentException(e); - } +public class ConsoleProxyThumbnailHandler implements HttpHandler { + private static final Logger s_logger = Logger.getLogger(ConsoleProxyThumbnailHandler.class); + + public ConsoleProxyThumbnailHandler() { + } - ConsoleProxyClientParam param = new ConsoleProxyClientParam(); - param.setClientHostAddress(host); - param.setClientHostPort(port); - param.setClientHostPassword(sid); - param.setClientTag(tag); - param.setTicket(ticket); - param.setClientTunnelUrl(console_url); - param.setClientTunnelSession(console_host_session); - - ConsoleProxyClient viewer = ConsoleProxy.getVncViewer(param); - - if (!viewer.isHostConnected()) { - // use generated image instead of static - BufferedImage img = generateTextImage(width, height, "Connecting"); - ByteArrayOutputStream bos = new ByteArrayOutputStream(8196); - javax.imageio.ImageIO.write(img, "jpg", bos); - byte[] bs = bos.toByteArray(); - Headers hds = t.getResponseHeaders(); - hds.set("Content-Type", "image/jpeg"); - hds.set("Cache-Control", "no-cache"); - hds.set("Cache-Control", "no-store"); - t.sendResponseHeaders(200, bs.length); - OutputStream os = t.getResponseBody(); - os.write(bs); - os.close(); - - if(s_logger.isInfoEnabled()) - s_logger.info("Console not ready, sent dummy JPG response"); - return; - } - - { - Image scaledImage = viewer.getClientScaledImage(width, height); - BufferedImage bufferedImage = new BufferedImage(width, height, - BufferedImage.TYPE_3BYTE_BGR); - Graphics2D bufImageGraphics = bufferedImage.createGraphics(); - bufImageGraphics.drawImage(scaledImage, 0, 0, null); - ByteArrayOutputStream bos = new ByteArrayOutputStream(8196); - javax.imageio.ImageIO.write(bufferedImage, "jpg", bos); - byte[] bs = bos.toByteArray(); - Headers hds = t.getResponseHeaders(); - hds.set("Content-Type", "image/jpeg"); - hds.set("Cache-Control", "no-cache"); - hds.set("Cache-Control", "no-store"); - t.sendResponseHeaders(200, bs.length); - OutputStream os = t.getResponseBody(); - os.write(bs); - os.close(); - } - } - - public static BufferedImage generateTextImage(int w, int h, String text) { - BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_3BYTE_BGR); - Graphics2D g = img.createGraphics(); - g.setColor(Color.BLACK); - g.fillRect(0, 0, w, h); - g.setColor(Color.WHITE); - try { - g.setFont(new Font(null, Font.PLAIN, 12)); - FontMetrics fm = g.getFontMetrics(); - int textWidth = fm.stringWidth(text); - int startx = (w-textWidth) / 2; - if(startx < 0) - startx = 0; - g.drawString(text, startx, h/2); - } catch (Throwable e) { - s_logger.warn("Problem in generating text to thumnail image, return blank image"); - } - return img; - } - - public static Map getQueryMap(String query) { - String[] params = query.split("&"); - Map map = new HashMap(); - for (String param : params) { - String name = param.split("=")[0]; - String value = param.split("=")[1]; - map.put(name, value); - } - return map; - } -} + public void handle(HttpExchange t) throws IOException { + try { + Thread.currentThread().setName("JPG Thread " + + Thread.currentThread().getId() + " " + t.getRemoteAddress()); + + if(s_logger.isDebugEnabled()) + s_logger.debug("ScreenHandler " + t.getRequestURI()); + + long startTick = System.currentTimeMillis(); + doHandle(t); + + if(s_logger.isDebugEnabled()) + s_logger.debug(t.getRequestURI() + "Process time " + (System.currentTimeMillis() - startTick) + " ms"); + } catch (IllegalArgumentException e) { + String response = "Bad query string"; + s_logger.error(response + ", request URI : " + t.getRequestURI()); + t.sendResponseHeaders(200, response.length()); + OutputStream os = t.getResponseBody(); + os.write(response.getBytes()); + os.close(); + } catch(OutOfMemoryError e) { + s_logger.error("Unrecoverable OutOfMemory Error, exit and let it be re-launched"); + System.exit(1); + } catch (Throwable e) { + s_logger.error("Unexpected exception while handing thumbnail request, ", e); + + String queries = t.getRequestURI().getQuery(); + Map queryMap = getQueryMap(queries); + int width = 0; + int height = 0; + String ws = queryMap.get("w"); + String hs = queryMap.get("h"); + try { + width = Integer.parseInt(ws); + height = Integer.parseInt(hs); + } catch (NumberFormatException ex) { + } + width = Math.min(width, 800); + height = Math.min(height, 600); + + BufferedImage img = generateTextImage(width, height, "Cannot Connect"); + ByteArrayOutputStream bos = new ByteArrayOutputStream(8196); + javax.imageio.ImageIO.write(img, "jpg", bos); + byte[] bs = bos.toByteArray(); + Headers hds = t.getResponseHeaders(); + hds.set("Content-Type", "image/jpeg"); + hds.set("Cache-Control", "no-cache"); + hds.set("Cache-Control", "no-store"); + t.sendResponseHeaders(200, bs.length); + OutputStream os = t.getResponseBody(); + os.write(bs); + os.close(); + s_logger.error("Cannot get console, sent error JPG response for " + t.getRequestURI()); + return; + } finally { + t.close(); + } + } + + private void doHandle(HttpExchange t) throws Exception, IllegalArgumentException { + String queries = t.getRequestURI().getQuery(); + Map queryMap = getQueryMap(queries); + int width = 0; + int height = 0; + int port = 0; + String ws = queryMap.get("w"); + String hs = queryMap.get("h"); + String host = queryMap.get("host"); + String portStr = queryMap.get("port"); + String sid = queryMap.get("sid"); + String tag = queryMap.get("tag"); + String ticket = queryMap.get("ticket"); + String console_url = queryMap.get("consoleurl"); + String console_host_session = queryMap.get("sessionref"); + + if(tag == null) + tag = ""; + + if (ws == null || hs == null || host == null || portStr == null || sid == null ) { + throw new IllegalArgumentException(); + } + try { + width = Integer.parseInt(ws); + height = Integer.parseInt(hs); + port = Integer.parseInt(portStr); + } catch (NumberFormatException e) { + throw new IllegalArgumentException(e); + } + + ConsoleProxyClientParam param = new ConsoleProxyClientParam(); + param.setClientHostAddress(host); + param.setClientHostPort(port); + param.setClientHostPassword(sid); + param.setClientTag(tag); + param.setTicket(ticket); + param.setClientTunnelUrl(console_url); + param.setClientTunnelSession(console_host_session); + + ConsoleProxyClient viewer = ConsoleProxy.getVncViewer(param); + + if (!viewer.isHostConnected()) { + // use generated image instead of static + BufferedImage img = generateTextImage(width, height, "Connecting"); + ByteArrayOutputStream bos = new ByteArrayOutputStream(8196); + javax.imageio.ImageIO.write(img, "jpg", bos); + byte[] bs = bos.toByteArray(); + Headers hds = t.getResponseHeaders(); + hds.set("Content-Type", "image/jpeg"); + hds.set("Cache-Control", "no-cache"); + hds.set("Cache-Control", "no-store"); + t.sendResponseHeaders(200, bs.length); + OutputStream os = t.getResponseBody(); + os.write(bs); + os.close(); + + if(s_logger.isInfoEnabled()) + s_logger.info("Console not ready, sent dummy JPG response"); + return; + } + + { + Image scaledImage = viewer.getClientScaledImage(width, height); + BufferedImage bufferedImage = new BufferedImage(width, height, + BufferedImage.TYPE_3BYTE_BGR); + Graphics2D bufImageGraphics = bufferedImage.createGraphics(); + bufImageGraphics.drawImage(scaledImage, 0, 0, null); + ByteArrayOutputStream bos = new ByteArrayOutputStream(8196); + javax.imageio.ImageIO.write(bufferedImage, "jpg", bos); + byte[] bs = bos.toByteArray(); + Headers hds = t.getResponseHeaders(); + hds.set("Content-Type", "image/jpeg"); + hds.set("Cache-Control", "no-cache"); + hds.set("Cache-Control", "no-store"); + t.sendResponseHeaders(200, bs.length); + OutputStream os = t.getResponseBody(); + os.write(bs); + os.close(); + } + } + + public static BufferedImage generateTextImage(int w, int h, String text) { + BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_3BYTE_BGR); + Graphics2D g = img.createGraphics(); + g.setColor(Color.BLACK); + g.fillRect(0, 0, w, h); + g.setColor(Color.WHITE); + try { + g.setFont(new Font(null, Font.PLAIN, 12)); + FontMetrics fm = g.getFontMetrics(); + int textWidth = fm.stringWidth(text); + int startx = (w-textWidth) / 2; + if(startx < 0) + startx = 0; + g.drawString(text, startx, h/2); + } catch (Throwable e) { + s_logger.warn("Problem in generating text to thumnail image, return blank image"); + } + return img; + } + + public static Map getQueryMap(String query) { + String[] params = query.split("&"); + Map map = new HashMap(); + for (String param : params) { + String name = param.split("=")[0]; + String value = param.split("=")[1]; + map.put(name, value); + } + return map; + } +} diff --git a/console-proxy/src/com/cloud/consoleproxy/InputEventType.java b/console-proxy/src/com/cloud/consoleproxy/InputEventType.java index b0bf8ebc381..4a5aff16bcf 100644 --- a/console-proxy/src/com/cloud/consoleproxy/InputEventType.java +++ b/console-proxy/src/com/cloud/consoleproxy/InputEventType.java @@ -14,45 +14,45 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.consoleproxy; - -public enum InputEventType { - MOUSE_MOVE(1), - MOUSE_DOWN(2), - MOUSE_UP(3), - KEY_PRESS(4), - KEY_DOWN(5), - KEY_UP(6), - MOUSE_DBLCLICK(8); - - int eventCode; - private InputEventType(int eventCode) { - this.eventCode = eventCode; - } - - public int getEventCode() { - return eventCode; - } - - public static InputEventType fromEventCode(int eventCode) { - switch(eventCode) { - case 1 : - return MOUSE_MOVE; - case 2 : - return MOUSE_DOWN; - case 3 : - return MOUSE_UP; - case 4 : - return KEY_PRESS; - case 5 : - return KEY_DOWN; - case 6 : - return KEY_UP; - case 8 : - return MOUSE_DBLCLICK; - default : - break; - } - throw new IllegalArgumentException("Unsupport event code: " + eventCode); - } -} +package com.cloud.consoleproxy; + +public enum InputEventType { + MOUSE_MOVE(1), + MOUSE_DOWN(2), + MOUSE_UP(3), + KEY_PRESS(4), + KEY_DOWN(5), + KEY_UP(6), + MOUSE_DBLCLICK(8); + + int eventCode; + private InputEventType(int eventCode) { + this.eventCode = eventCode; + } + + public int getEventCode() { + return eventCode; + } + + public static InputEventType fromEventCode(int eventCode) { + switch(eventCode) { + case 1 : + return MOUSE_MOVE; + case 2 : + return MOUSE_DOWN; + case 3 : + return MOUSE_UP; + case 4 : + return KEY_PRESS; + case 5 : + return KEY_DOWN; + case 6 : + return KEY_UP; + case 8 : + return MOUSE_DBLCLICK; + default : + break; + } + throw new IllegalArgumentException("Unsupport event code: " + eventCode); + } +} diff --git a/console-proxy/src/com/cloud/consoleproxy/util/ITileScanListener.java b/console-proxy/src/com/cloud/consoleproxy/util/ITileScanListener.java index 547b13cd60a..2ff82d7242f 100644 --- a/console-proxy/src/com/cloud/consoleproxy/util/ITileScanListener.java +++ b/console-proxy/src/com/cloud/consoleproxy/util/ITileScanListener.java @@ -14,12 +14,12 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.consoleproxy.util; - -import java.awt.Rectangle; -import java.util.List; - -public interface ITileScanListener { - boolean onTileChange(Rectangle rowMergedRect, int row, int col); - void onRegionChange(List regionList); -} +package com.cloud.consoleproxy.util; + +import java.awt.Rectangle; +import java.util.List; + +public interface ITileScanListener { + boolean onTileChange(Rectangle rowMergedRect, int row, int col); + void onRegionChange(List regionList); +} diff --git a/console-proxy/src/com/cloud/consoleproxy/util/ImageHelper.java b/console-proxy/src/com/cloud/consoleproxy/util/ImageHelper.java index 42fb59e48a7..bb7373e78b2 100644 --- a/console-proxy/src/com/cloud/consoleproxy/util/ImageHelper.java +++ b/console-proxy/src/com/cloud/consoleproxy/util/ImageHelper.java @@ -14,19 +14,19 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.consoleproxy.util; - -import java.awt.image.BufferedImage; -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -public class ImageHelper { - public static byte[] jpegFromImage(BufferedImage image) throws IOException { - ByteArrayOutputStream bos = new ByteArrayOutputStream(128000); - javax.imageio.ImageIO.write(image, "jpg", bos); - - byte[] jpegBits = bos.toByteArray(); - bos.close(); - return jpegBits; - } -} +package com.cloud.consoleproxy.util; + +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +public class ImageHelper { + public static byte[] jpegFromImage(BufferedImage image) throws IOException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(128000); + javax.imageio.ImageIO.write(image, "jpg", bos); + + byte[] jpegBits = bos.toByteArray(); + bos.close(); + return jpegBits; + } +} diff --git a/console-proxy/src/com/cloud/consoleproxy/util/Logger.java b/console-proxy/src/com/cloud/consoleproxy/util/Logger.java index 2392a98751b..f4357bd4bc4 100644 --- a/console-proxy/src/com/cloud/consoleproxy/util/Logger.java +++ b/console-proxy/src/com/cloud/consoleproxy/util/Logger.java @@ -14,210 +14,210 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.consoleproxy.util; - -// logger facility for dynamic switch between console logger used in Applet and log4j based logger -public class Logger { - private static LoggerFactory factory = null; - - public static final int LEVEL_TRACE = 1; - public static final int LEVEL_DEBUG = 2; - public static final int LEVEL_INFO = 3; - public static final int LEVEL_WARN = 4; - public static final int LEVEL_ERROR = 5; - - private Class clazz; - private Logger logger; - - private static int level = LEVEL_INFO; - - public static Logger getLogger(Class clazz) { - return new Logger(clazz); - } - - public static void setFactory(LoggerFactory f) { - factory = f; - } - - public static void setLevel(int l) { - level = l; - } - - public Logger(Class clazz) { - this.clazz = clazz; - } - - protected Logger() { - } - - public boolean isTraceEnabled() { - if(factory != null) { - if(logger == null) - logger = factory.getLogger(clazz); - - return logger.isTraceEnabled(); - } - return level <= LEVEL_TRACE; - } - - public boolean isDebugEnabled() { - if(factory != null) { - if(logger == null) - logger = factory.getLogger(clazz); - - return logger.isDebugEnabled(); - } - return level <= LEVEL_DEBUG; - } - - public boolean isInfoEnabled() { - if(factory != null) { - if(logger == null) - logger = factory.getLogger(clazz); - - return logger.isInfoEnabled(); - } - return level <= LEVEL_INFO; - } - - public void trace(Object message) { - - if(factory != null) { - if(logger == null) - logger = factory.getLogger(clazz); - - logger.trace(message); - } else { - if(level <= LEVEL_TRACE) - System.out.println(message); - } - } - - public void trace(Object message, Throwable exception) { - if(factory != null) { - if(logger == null) - logger = factory.getLogger(clazz); - - logger.trace(message, exception); - } else { - if(level <= LEVEL_TRACE) { - System.out.println(message); - if (exception != null) { - exception.printStackTrace(System.out); - } - } - } - } - - public void info(Object message) { - if(factory != null) { - if(logger == null) - logger = factory.getLogger(clazz); - - logger.info(message); - } else { - if(level <= LEVEL_INFO) - System.out.println(message); - } - } - - public void info(Object message, Throwable exception) { - if(factory != null) { - if(logger == null) - logger = factory.getLogger(clazz); - - logger.info(message, exception); - } else { - if(level <= LEVEL_INFO) { - System.out.println(message); - if (exception != null) { - exception.printStackTrace(System.out); - } - } - } - } - - public void debug(Object message) { - if(factory != null) { - if(logger == null) - logger = factory.getLogger(clazz); - - logger.debug(message); - } else { - if(level <= LEVEL_DEBUG) - System.out.println(message); - } - } - - public void debug(Object message, Throwable exception) { - if(factory != null) { - if(logger == null) - logger = factory.getLogger(clazz); - - logger.debug(message, exception); - } else { - if(level <= LEVEL_DEBUG) { - System.out.println(message); - if (exception != null) { - exception.printStackTrace(System.out); - } - } - } - } - - public void warn(Object message) { - if(factory != null) { - if(logger == null) - logger = factory.getLogger(clazz); - - logger.warn(message); - } else { - if(level <= LEVEL_WARN) - System.out.println(message); - } - } - - public void warn(Object message, Throwable exception) { - if(factory != null) { - if(logger == null) - logger = factory.getLogger(clazz); - - logger.warn(message, exception); - } else { - if(level <= LEVEL_WARN) { - System.out.println(message); - if (exception != null) { - exception.printStackTrace(System.out); - } - } - } - } - - public void error(Object message) { - if(factory != null) { - if(logger == null) - logger = factory.getLogger(clazz); - - logger.error(message); - } else { - if(level <= LEVEL_ERROR) - System.out.println(message); - } - } - - public void error(Object message, Throwable exception) { - if(factory != null) { - if(logger == null) - logger = factory.getLogger(clazz); - - logger.error(message, exception); - } else { - if(level <= LEVEL_ERROR) { - System.out.println(message); - if (exception != null) { - exception.printStackTrace(System.out); - } - } - } - } -} +package com.cloud.consoleproxy.util; + +// logger facility for dynamic switch between console logger used in Applet and log4j based logger +public class Logger { + private static LoggerFactory factory = null; + + public static final int LEVEL_TRACE = 1; + public static final int LEVEL_DEBUG = 2; + public static final int LEVEL_INFO = 3; + public static final int LEVEL_WARN = 4; + public static final int LEVEL_ERROR = 5; + + private Class clazz; + private Logger logger; + + private static int level = LEVEL_INFO; + + public static Logger getLogger(Class clazz) { + return new Logger(clazz); + } + + public static void setFactory(LoggerFactory f) { + factory = f; + } + + public static void setLevel(int l) { + level = l; + } + + public Logger(Class clazz) { + this.clazz = clazz; + } + + protected Logger() { + } + + public boolean isTraceEnabled() { + if(factory != null) { + if(logger == null) + logger = factory.getLogger(clazz); + + return logger.isTraceEnabled(); + } + return level <= LEVEL_TRACE; + } + + public boolean isDebugEnabled() { + if(factory != null) { + if(logger == null) + logger = factory.getLogger(clazz); + + return logger.isDebugEnabled(); + } + return level <= LEVEL_DEBUG; + } + + public boolean isInfoEnabled() { + if(factory != null) { + if(logger == null) + logger = factory.getLogger(clazz); + + return logger.isInfoEnabled(); + } + return level <= LEVEL_INFO; + } + + public void trace(Object message) { + + if(factory != null) { + if(logger == null) + logger = factory.getLogger(clazz); + + logger.trace(message); + } else { + if(level <= LEVEL_TRACE) + System.out.println(message); + } + } + + public void trace(Object message, Throwable exception) { + if(factory != null) { + if(logger == null) + logger = factory.getLogger(clazz); + + logger.trace(message, exception); + } else { + if(level <= LEVEL_TRACE) { + System.out.println(message); + if (exception != null) { + exception.printStackTrace(System.out); + } + } + } + } + + public void info(Object message) { + if(factory != null) { + if(logger == null) + logger = factory.getLogger(clazz); + + logger.info(message); + } else { + if(level <= LEVEL_INFO) + System.out.println(message); + } + } + + public void info(Object message, Throwable exception) { + if(factory != null) { + if(logger == null) + logger = factory.getLogger(clazz); + + logger.info(message, exception); + } else { + if(level <= LEVEL_INFO) { + System.out.println(message); + if (exception != null) { + exception.printStackTrace(System.out); + } + } + } + } + + public void debug(Object message) { + if(factory != null) { + if(logger == null) + logger = factory.getLogger(clazz); + + logger.debug(message); + } else { + if(level <= LEVEL_DEBUG) + System.out.println(message); + } + } + + public void debug(Object message, Throwable exception) { + if(factory != null) { + if(logger == null) + logger = factory.getLogger(clazz); + + logger.debug(message, exception); + } else { + if(level <= LEVEL_DEBUG) { + System.out.println(message); + if (exception != null) { + exception.printStackTrace(System.out); + } + } + } + } + + public void warn(Object message) { + if(factory != null) { + if(logger == null) + logger = factory.getLogger(clazz); + + logger.warn(message); + } else { + if(level <= LEVEL_WARN) + System.out.println(message); + } + } + + public void warn(Object message, Throwable exception) { + if(factory != null) { + if(logger == null) + logger = factory.getLogger(clazz); + + logger.warn(message, exception); + } else { + if(level <= LEVEL_WARN) { + System.out.println(message); + if (exception != null) { + exception.printStackTrace(System.out); + } + } + } + } + + public void error(Object message) { + if(factory != null) { + if(logger == null) + logger = factory.getLogger(clazz); + + logger.error(message); + } else { + if(level <= LEVEL_ERROR) + System.out.println(message); + } + } + + public void error(Object message, Throwable exception) { + if(factory != null) { + if(logger == null) + logger = factory.getLogger(clazz); + + logger.error(message, exception); + } else { + if(level <= LEVEL_ERROR) { + System.out.println(message); + if (exception != null) { + exception.printStackTrace(System.out); + } + } + } + } +} diff --git a/console-proxy/src/com/cloud/consoleproxy/util/LoggerFactory.java b/console-proxy/src/com/cloud/consoleproxy/util/LoggerFactory.java index fa0d3cfcb31..121411adf16 100644 --- a/console-proxy/src/com/cloud/consoleproxy/util/LoggerFactory.java +++ b/console-proxy/src/com/cloud/consoleproxy/util/LoggerFactory.java @@ -14,8 +14,8 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.consoleproxy.util; - -public interface LoggerFactory { - Logger getLogger(Class clazz); -} +package com.cloud.consoleproxy.util; + +public interface LoggerFactory { + Logger getLogger(Class clazz); +} diff --git a/console-proxy/src/com/cloud/consoleproxy/util/RawHTTP.java b/console-proxy/src/com/cloud/consoleproxy/util/RawHTTP.java index eeba34532de..c77b5511270 100644 --- a/console-proxy/src/com/cloud/consoleproxy/util/RawHTTP.java +++ b/console-proxy/src/com/cloud/consoleproxy/util/RawHTTP.java @@ -14,236 +14,236 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.consoleproxy.util; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.Socket; -import java.security.KeyManagementException; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.SecureRandom; -import java.security.cert.X509Certificate; -import java.util.HashMap; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.net.SocketFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSocket; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; - -// -// This file is originally from XenConsole with modifications -// - -/** - * Send an HTTP CONNECT or PUT request to a XenAPI host with a Session ID, - * return the connected socket and the Task ID. Used for tunnelling VNC - * connections and import/export operations. - */ -public final class RawHTTP { - private static final Logger s_logger = Logger.getLogger(RawHTTP.class); - - private static final Pattern END_PATTERN = Pattern.compile("^\r\n$"); - private static final Pattern HEADER_PATTERN = Pattern - .compile("^([A-Z_a-z0-9-]+):\\s*(.*)\r\n$"); - private static final Pattern HTTP_PATTERN = Pattern - .compile("^HTTP/\\d+\\.\\d+ (\\d*) (.*)\r\n$"); - - /** - * @uml.property name="command" - */ - private final String command; - /** - * @uml.property name="host" - */ - private final String host; - /** - * @uml.property name="port" - */ - private final int port; - /** - * @uml.property name="path" - */ - private final String path; - /** - * @uml.property name="session" - */ - private final String session; - /** - * @uml.property name="useSSL" - */ - private final boolean useSSL; - - /** - * @uml.property name="responseHeaders" - * @uml.associationEnd qualifier="group:java.lang.String java.lang.String" - */ - private final Map responseHeaders = new HashMap(); - - /** - * @uml.property name="ic" - */ - private InputStream ic; - /** - * @uml.property name="oc" - */ - private OutputStream oc; - /** - * @uml.property name="s" - */ - private Socket s; - - public InputStream getInputStream() { - return ic; - } - - public OutputStream getOutputStream() { - return oc; - } - - public Socket getSocket() { - return s; - } - - public RawHTTP(String command, String host, int port, String path, - String session, boolean useSSL) { - this.command = command; - this.host = host; - this.port = port; - this.path = path; - this.session = session; - this.useSSL = useSSL; - } - - private static final TrustManager[] trustAllCerts = new TrustManager[] { - new X509TrustManager() { - public X509Certificate[] getAcceptedIssuers() { - return null; - } - - public void checkClientTrusted(X509Certificate[] certs, String authType) { - } - - public void checkServerTrusted(X509Certificate[] certs, String authType) { - } - } - }; - - private Socket _getSocket() throws IOException { - if (useSSL) { - SSLContext context = getClientSSLContext(); - if(context == null) - throw new IOException("Unable to setup SSL context"); - - SSLSocket ssl = null; - try { - context.init(null, trustAllCerts, new SecureRandom()); - SocketFactory factory = context.getSocketFactory(); - ssl = (SSLSocket) factory.createSocket(host, port); - /* ssl.setSSLParameters(context.getDefaultSSLParameters()); */ - } catch (IOException e) { - s_logger.error("IOException: " + e.getMessage(), e); - throw e; - } catch (KeyManagementException e) { - s_logger.error("KeyManagementException: " + e.getMessage(), e); - } - return ssl; - } else { - return new Socket(host, port); - } - } - - public Socket connect() throws IOException { - String[] headers = makeHeaders(); - s = _getSocket(); - try { - oc = s.getOutputStream(); - for (String header : headers) { - oc.write(header.getBytes()); - oc.write("\r\n".getBytes()); - } - oc.flush(); - ic = s.getInputStream(); - while (true) { - String line = readline(ic); - - Matcher m = END_PATTERN.matcher(line); - if (m.matches()) { - return s; - } - - m = HEADER_PATTERN.matcher(line); - if (m.matches()) { - responseHeaders.put(m.group(1), m.group(2)); - continue; - } - - m = HTTP_PATTERN.matcher(line); - if (m.matches()) { - String status_code = m.group(1); - String reason_phrase = m.group(2); - if (!"200".equals(status_code)) { - throw new IOException("HTTP status " + status_code - + " " + reason_phrase); - } - } else { - throw new IOException("Unknown HTTP line " + line); - } - } - } catch (IOException exn) { - s.close(); - throw exn; - } catch (RuntimeException exn) { - s.close(); - throw exn; - } - } - - public Map getResponseHeaders() { - return responseHeaders; - } - - private String[] makeHeaders() { - String[] headers = { String.format("%s %s HTTP/1.0", command, path), - String.format("Host: %s", host), - String.format("Cookie: session_id=%s", session), "" }; - return headers; - } - - private static String readline(InputStream ic) throws IOException { - String result = ""; - while (true) { - try { - int c = ic.read(); - - if (c == -1) { - return result; - } - result = result + (char) c; - if (c == 0x0a /* LF */) { - return result; - } - } catch (IOException e) { - ic.close(); - throw e; - } - } - } - - private SSLContext getClientSSLContext() { - SSLContext sslContext = null; - try { - sslContext = SSLContext.getInstance("SSL", "SunJSSE"); - } catch (NoSuchAlgorithmException e) { - s_logger.error("Unexpected exception ", e); - } catch (NoSuchProviderException e) { - s_logger.error("Unexpected exception ", e); - } - return sslContext; - } -} +package com.cloud.consoleproxy.util; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SecureRandom; +import java.security.cert.X509Certificate; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.net.SocketFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +// +// This file is originally from XenConsole with modifications +// + +/** + * Send an HTTP CONNECT or PUT request to a XenAPI host with a Session ID, + * return the connected socket and the Task ID. Used for tunnelling VNC + * connections and import/export operations. + */ +public final class RawHTTP { + private static final Logger s_logger = Logger.getLogger(RawHTTP.class); + + private static final Pattern END_PATTERN = Pattern.compile("^\r\n$"); + private static final Pattern HEADER_PATTERN = Pattern + .compile("^([A-Z_a-z0-9-]+):\\s*(.*)\r\n$"); + private static final Pattern HTTP_PATTERN = Pattern + .compile("^HTTP/\\d+\\.\\d+ (\\d*) (.*)\r\n$"); + + /** + * @uml.property name="command" + */ + private final String command; + /** + * @uml.property name="host" + */ + private final String host; + /** + * @uml.property name="port" + */ + private final int port; + /** + * @uml.property name="path" + */ + private final String path; + /** + * @uml.property name="session" + */ + private final String session; + /** + * @uml.property name="useSSL" + */ + private final boolean useSSL; + + /** + * @uml.property name="responseHeaders" + * @uml.associationEnd qualifier="group:java.lang.String java.lang.String" + */ + private final Map responseHeaders = new HashMap(); + + /** + * @uml.property name="ic" + */ + private InputStream ic; + /** + * @uml.property name="oc" + */ + private OutputStream oc; + /** + * @uml.property name="s" + */ + private Socket s; + + public InputStream getInputStream() { + return ic; + } + + public OutputStream getOutputStream() { + return oc; + } + + public Socket getSocket() { + return s; + } + + public RawHTTP(String command, String host, int port, String path, + String session, boolean useSSL) { + this.command = command; + this.host = host; + this.port = port; + this.path = path; + this.session = session; + this.useSSL = useSSL; + } + + private static final TrustManager[] trustAllCerts = new TrustManager[] { + new X509TrustManager() { + public X509Certificate[] getAcceptedIssuers() { + return null; + } + + public void checkClientTrusted(X509Certificate[] certs, String authType) { + } + + public void checkServerTrusted(X509Certificate[] certs, String authType) { + } + } + }; + + private Socket _getSocket() throws IOException { + if (useSSL) { + SSLContext context = getClientSSLContext(); + if(context == null) + throw new IOException("Unable to setup SSL context"); + + SSLSocket ssl = null; + try { + context.init(null, trustAllCerts, new SecureRandom()); + SocketFactory factory = context.getSocketFactory(); + ssl = (SSLSocket) factory.createSocket(host, port); + /* ssl.setSSLParameters(context.getDefaultSSLParameters()); */ + } catch (IOException e) { + s_logger.error("IOException: " + e.getMessage(), e); + throw e; + } catch (KeyManagementException e) { + s_logger.error("KeyManagementException: " + e.getMessage(), e); + } + return ssl; + } else { + return new Socket(host, port); + } + } + + public Socket connect() throws IOException { + String[] headers = makeHeaders(); + s = _getSocket(); + try { + oc = s.getOutputStream(); + for (String header : headers) { + oc.write(header.getBytes()); + oc.write("\r\n".getBytes()); + } + oc.flush(); + ic = s.getInputStream(); + while (true) { + String line = readline(ic); + + Matcher m = END_PATTERN.matcher(line); + if (m.matches()) { + return s; + } + + m = HEADER_PATTERN.matcher(line); + if (m.matches()) { + responseHeaders.put(m.group(1), m.group(2)); + continue; + } + + m = HTTP_PATTERN.matcher(line); + if (m.matches()) { + String status_code = m.group(1); + String reason_phrase = m.group(2); + if (!"200".equals(status_code)) { + throw new IOException("HTTP status " + status_code + + " " + reason_phrase); + } + } else { + throw new IOException("Unknown HTTP line " + line); + } + } + } catch (IOException exn) { + s.close(); + throw exn; + } catch (RuntimeException exn) { + s.close(); + throw exn; + } + } + + public Map getResponseHeaders() { + return responseHeaders; + } + + private String[] makeHeaders() { + String[] headers = { String.format("%s %s HTTP/1.0", command, path), + String.format("Host: %s", host), + String.format("Cookie: session_id=%s", session), "" }; + return headers; + } + + private static String readline(InputStream ic) throws IOException { + String result = ""; + while (true) { + try { + int c = ic.read(); + + if (c == -1) { + return result; + } + result = result + (char) c; + if (c == 0x0a /* LF */) { + return result; + } + } catch (IOException e) { + ic.close(); + throw e; + } + } + } + + private SSLContext getClientSSLContext() { + SSLContext sslContext = null; + try { + sslContext = SSLContext.getInstance("SSL", "SunJSSE"); + } catch (NoSuchAlgorithmException e) { + s_logger.error("Unexpected exception ", e); + } catch (NoSuchProviderException e) { + s_logger.error("Unexpected exception ", e); + } + return sslContext; + } +} diff --git a/console-proxy/src/com/cloud/consoleproxy/util/Region.java b/console-proxy/src/com/cloud/consoleproxy/util/Region.java index a5860f361b5..a5d1751d63b 100644 --- a/console-proxy/src/com/cloud/consoleproxy/util/Region.java +++ b/console-proxy/src/com/cloud/consoleproxy/util/Region.java @@ -14,77 +14,77 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.consoleproxy.util; - -import java.awt.Rectangle; -import java.util.ArrayList; -import java.util.List; - -public class Region { - private Rectangle bound; - private List rectList; - - public Region() { - bound = new Rectangle(0, 0, 0, 0); - rectList = new ArrayList(); - } - - public Region(Rectangle rect) { - bound = new Rectangle(rect.x, rect.y, rect.width, rect.height); - rectList = new ArrayList(); - rectList.add(rect); - } - - public Rectangle getBound() { - return bound; - } - - public void clearBound() { - assert(rectList.size() == 0); - bound.x = bound.y = bound.width = bound.height = 0; - } - - public List getRectangles() { - return rectList; - } - - public boolean add(Rectangle rect) { - if(bound.isEmpty()) { - assert(rectList.size() == 0); - bound.x = rect.x; - bound.y = rect.y; - bound.width = rect.width; - bound.height = rect.height; - - rectList.add(rect); - return true; - } - - Rectangle rcInflated = new Rectangle(rect.x - 1, rect.y- 1, rect.width + 2, rect.height + 2); - if(!bound.intersects(rcInflated)) - return false; - - for(Rectangle r : rectList) { - if(r.intersects(rcInflated)) { - if(!r.contains(rect)) { - enlargeBound(rect); - rectList.add(rect); - return true; - } - } - } - return false; - } - - private void enlargeBound(Rectangle rect) { - int boundLeft = Math.min(bound.x, rect.x); - int boundTop = Math.min(bound.y, rect.y); - int boundRight = Math.max(bound.x + bound.width, rect.x + rect.width); - int boundBottom = Math.max(bound.y + bound.height, rect.y + rect.height); - - bound.x = boundLeft; - bound.y = boundTop; - bound.width = boundRight - boundLeft; - bound.height = boundBottom - boundTop; - } -} +package com.cloud.consoleproxy.util; + +import java.awt.Rectangle; +import java.util.ArrayList; +import java.util.List; + +public class Region { + private Rectangle bound; + private List rectList; + + public Region() { + bound = new Rectangle(0, 0, 0, 0); + rectList = new ArrayList(); + } + + public Region(Rectangle rect) { + bound = new Rectangle(rect.x, rect.y, rect.width, rect.height); + rectList = new ArrayList(); + rectList.add(rect); + } + + public Rectangle getBound() { + return bound; + } + + public void clearBound() { + assert(rectList.size() == 0); + bound.x = bound.y = bound.width = bound.height = 0; + } + + public List getRectangles() { + return rectList; + } + + public boolean add(Rectangle rect) { + if(bound.isEmpty()) { + assert(rectList.size() == 0); + bound.x = rect.x; + bound.y = rect.y; + bound.width = rect.width; + bound.height = rect.height; + + rectList.add(rect); + return true; + } + + Rectangle rcInflated = new Rectangle(rect.x - 1, rect.y- 1, rect.width + 2, rect.height + 2); + if(!bound.intersects(rcInflated)) + return false; + + for(Rectangle r : rectList) { + if(r.intersects(rcInflated)) { + if(!r.contains(rect)) { + enlargeBound(rect); + rectList.add(rect); + return true; + } + } + } + return false; + } + + private void enlargeBound(Rectangle rect) { + int boundLeft = Math.min(bound.x, rect.x); + int boundTop = Math.min(bound.y, rect.y); + int boundRight = Math.max(bound.x + bound.width, rect.x + rect.width); + int boundBottom = Math.max(bound.y + bound.height, rect.y + rect.height); + + bound.x = boundLeft; + bound.y = boundTop; + bound.width = boundRight - boundLeft; + bound.height = boundBottom - boundTop; + } +} diff --git a/console-proxy/src/com/cloud/consoleproxy/util/RegionClassifier.java b/console-proxy/src/com/cloud/consoleproxy/util/RegionClassifier.java index c4d9b82eb4e..9faa04ea9eb 100644 --- a/console-proxy/src/com/cloud/consoleproxy/util/RegionClassifier.java +++ b/console-proxy/src/com/cloud/consoleproxy/util/RegionClassifier.java @@ -14,45 +14,45 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.consoleproxy.util; - -import java.awt.Rectangle; -import java.util.ArrayList; -import java.util.List; - -public class RegionClassifier { - private List regionList; - - public RegionClassifier() { - regionList = new ArrayList(); - } - - public void add(Rectangle rect) { - boolean newRegion = true; - Rectangle rcInflated = new Rectangle(rect.x - 1, rect.y - 1, rect.width + 2, rect.height + 2); - for(Region region : regionList) { - if(region.getBound().intersects(rcInflated)) { - newRegion = false; - break; - } - } - - if(newRegion) { - regionList.add(new Region(rect)); - } else { - for(Region region : regionList) { - if(region.add(rect)) - return; - } - regionList.add(new Region(rect)); - } - } - - public List getRegionList() { - return regionList; - } - - public void clear() { - regionList.clear(); - } -} +package com.cloud.consoleproxy.util; + +import java.awt.Rectangle; +import java.util.ArrayList; +import java.util.List; + +public class RegionClassifier { + private List regionList; + + public RegionClassifier() { + regionList = new ArrayList(); + } + + public void add(Rectangle rect) { + boolean newRegion = true; + Rectangle rcInflated = new Rectangle(rect.x - 1, rect.y - 1, rect.width + 2, rect.height + 2); + for(Region region : regionList) { + if(region.getBound().intersects(rcInflated)) { + newRegion = false; + break; + } + } + + if(newRegion) { + regionList.add(new Region(rect)); + } else { + for(Region region : regionList) { + if(region.add(rect)) + return; + } + regionList.add(new Region(rect)); + } + } + + public List getRegionList() { + return regionList; + } + + public void clear() { + regionList.clear(); + } +} diff --git a/console-proxy/src/com/cloud/consoleproxy/util/TileInfo.java b/console-proxy/src/com/cloud/consoleproxy/util/TileInfo.java index 728f07a700d..933a55a55b0 100644 --- a/console-proxy/src/com/cloud/consoleproxy/util/TileInfo.java +++ b/console-proxy/src/com/cloud/consoleproxy/util/TileInfo.java @@ -14,42 +14,42 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.consoleproxy.util; - -import java.awt.Rectangle; - -public class TileInfo { - private int row; - private int col; - private Rectangle tileRect; - - public TileInfo(int row, int col, Rectangle tileRect) { - this.row = row; - this.col = col; - this.tileRect = tileRect; - } - - public int getRow() { - return row; - } - - public void setRow(int row) { - this.row = row; - } - - public int getCol() { - return col; - } - - public void setCol(int col) { - this.col = col; - } - - public Rectangle getTileRect() { - return tileRect; - } - - public void setTileRect(Rectangle tileRect) { - this.tileRect = tileRect; - } -} +package com.cloud.consoleproxy.util; + +import java.awt.Rectangle; + +public class TileInfo { + private int row; + private int col; + private Rectangle tileRect; + + public TileInfo(int row, int col, Rectangle tileRect) { + this.row = row; + this.col = col; + this.tileRect = tileRect; + } + + public int getRow() { + return row; + } + + public void setRow(int row) { + this.row = row; + } + + public int getCol() { + return col; + } + + public void setCol(int col) { + this.col = col; + } + + public Rectangle getTileRect() { + return tileRect; + } + + public void setTileRect(Rectangle tileRect) { + this.tileRect = tileRect; + } +} diff --git a/console-proxy/src/com/cloud/consoleproxy/util/TileTracker.java b/console-proxy/src/com/cloud/consoleproxy/util/TileTracker.java index 350eebf26a2..b3facb2f88f 100644 --- a/console-proxy/src/com/cloud/consoleproxy/util/TileTracker.java +++ b/console-proxy/src/com/cloud/consoleproxy/util/TileTracker.java @@ -14,256 +14,256 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.consoleproxy.util; - -import java.awt.Rectangle; -import java.util.ArrayList; -import java.util.List; - -public class TileTracker { - - // 2 dimension tile status snapshot, a true value means the corresponding tile has been invalidated - private boolean[][] snapshot; - - private int tileWidth = 0; - private int tileHeight = 0; - private int trackWidth = 0; - private int trackHeight = 0; - - public TileTracker() { - } - - public int getTileWidth() { - return tileWidth; - } - - public void setTileWidth(int tileWidth) { - this.tileWidth = tileWidth; - } - - public int getTileHeight() { - return tileHeight; - } - - public void setTileHeight(int tileHeight) { - this.tileHeight = tileHeight; - } - - public int getTrackWidth() { - return trackWidth; - } - - public void setTrackWidth(int trackWidth) { - this.trackWidth = trackWidth; - } - - public int getTrackHeight() { - return trackHeight; - } - - public void setTrackHeight(int trackHeight) { - this.trackHeight = trackHeight; - } - - public void initTracking(int tileWidth, int tileHeight, int trackWidth, int trackHeight) { - assert(tileWidth > 0); - assert(tileHeight > 0); - assert(trackWidth > 0); - assert(trackHeight > 0); - assert(tileWidth <= trackWidth); - assert(tileHeight <= trackHeight); - - this.tileWidth = tileWidth; - this.tileHeight = tileHeight; - this.trackWidth = trackWidth; - this.trackHeight = trackHeight; - - int cols = getTileCols(); - int rows = getTileRows(); - snapshot = new boolean[rows][cols]; - for(int i = 0; i < rows; i++) - for(int j = 0; j < cols; j++) - snapshot[i][j] = false; - } - - public synchronized void resize(int trackWidth, int trackHeight) { - assert(tileWidth > 0); - assert(tileHeight > 0); - assert(trackWidth > 0); - assert(trackHeight > 0); - - this.trackWidth = trackWidth; - this.trackHeight = trackHeight; - - int cols = getTileCols(); - int rows = getTileRows(); - snapshot = new boolean[rows][cols]; - for(int i = 0; i < rows; i++) - for(int j = 0; j < cols; j++) - snapshot[i][j] = true; - } - - public void invalidate(Rectangle rect) { - setTileFlag(rect, true); - } - - public void validate(Rectangle rect) { - setTileFlag(rect, false); - } - - public List scan(boolean init) { - List l = new ArrayList(); - - synchronized(this) { - for(int i = 0; i < getTileRows(); i++) { - for(int j = 0; j < getTileCols(); j++) { - if(init || snapshot[i][j]) { - Rectangle rect = new Rectangle(); - rect.y = i*tileHeight; - rect.x = j*tileWidth; - rect.width = Math.min(trackWidth - rect.x, tileWidth); - rect.height = Math.min(trackHeight - rect.y, tileHeight); - - l.add(new TileInfo(i, j, rect)); - snapshot[i][j] = false; - } - } - } - - return l; - } - } - - public boolean hasFullCoverage() { - synchronized(this) { - for(int i = 0; i < getTileRows(); i++) { - for(int j = 0; j < getTileCols(); j++) { - if(!snapshot[i][j]) - return false; - } - } - } - return true; - } - - - - public void initCoverageTest() { - synchronized(this) { - for(int i = 0; i < getTileRows(); i++) { - for(int j = 0; j < getTileCols(); j++) { - snapshot[i][j] = false; - } - } - } - } - - // listener will be called while holding the object lock, use it - // with care to avoid deadlock condition being formed - public synchronized void scan(int nStartRow, int nStartCol, ITileScanListener listener) { - assert(listener != null); - - int cols = getTileCols(); - int rows = getTileRows(); - - nStartRow = nStartRow % rows; - nStartCol = nStartCol % cols; - - int nPos = nStartRow*cols + nStartCol; - int nUnits = rows*cols; - int nStartPos = nPos; - int nRow; - int nCol; - do { - nRow = nPos / cols; - nCol = nPos % cols; - - if(snapshot[nRow][nCol]) { - int nEndCol = nCol; - for(; nEndCol < cols && snapshot[nRow][nEndCol]; nEndCol++) { - snapshot[nRow][nEndCol] = false; - } - - Rectangle rect = new Rectangle(); - rect.y = nRow*tileHeight; - rect.height = tileHeight; - rect.x = nCol*tileWidth; - rect.width = (nEndCol - nCol)*tileWidth; - - if(!listener.onTileChange(rect, nRow, nEndCol)) - break; - } - - nPos = (nPos + 1) % nUnits; - } while(nPos != nStartPos); - } - - public void capture(ITileScanListener listener) { - assert(listener != null); - - int cols = getTileCols(); - int rows = getTileRows(); - - RegionClassifier classifier = new RegionClassifier(); - int left, top, right, bottom; - - synchronized(this) { - for(int i = 0; i < rows; i++) { - top = i*tileHeight; - bottom = Math.min(top + tileHeight, trackHeight); - for(int j = 0; j < cols; j++) { - left = j*tileWidth; - right = Math.min(left + tileWidth, trackWidth); - - if(snapshot[i][j]) { - snapshot[i][j] = false; - classifier.add(new Rectangle(left, top, right - left, bottom - top)); - } - } - } - } - listener.onRegionChange(classifier.getRegionList()); - } - - private synchronized void setTileFlag(Rectangle rect, boolean flag) { - int nStartTileRow; - int nStartTileCol; - int nEndTileRow; - int nEndTileCol; - - int cols = getTileCols(); - int rows = getTileRows(); - - if(rect != null) { - nStartTileRow = Math.min(getTileYPos(rect.y), rows - 1); - nStartTileCol = Math.min(getTileXPos(rect.x), cols - 1); - nEndTileRow = Math.min(getTileYPos(rect.y + rect.height - 1), rows -1); - nEndTileCol = Math.min(getTileXPos(rect.x + rect.width - 1), cols -1); - } else { - nStartTileRow = 0; - nStartTileCol = 0; - nEndTileRow = rows - 1; - nEndTileCol = cols - 1; - } - - for(int i = nStartTileRow; i <= nEndTileRow; i++) - for(int j = nStartTileCol; j <= nEndTileCol; j++) - snapshot[i][j] = flag; - } - - private int getTileRows() { - return (trackHeight + tileHeight - 1) / tileHeight; - } - - private int getTileCols() { - return (trackWidth + tileWidth - 1) / tileWidth; - } - - private int getTileXPos(int x) { - return x / tileWidth; - } - - public int getTileYPos(int y) { - return y / tileHeight; - } -} +package com.cloud.consoleproxy.util; + +import java.awt.Rectangle; +import java.util.ArrayList; +import java.util.List; + +public class TileTracker { + + // 2 dimension tile status snapshot, a true value means the corresponding tile has been invalidated + private boolean[][] snapshot; + + private int tileWidth = 0; + private int tileHeight = 0; + private int trackWidth = 0; + private int trackHeight = 0; + + public TileTracker() { + } + + public int getTileWidth() { + return tileWidth; + } + + public void setTileWidth(int tileWidth) { + this.tileWidth = tileWidth; + } + + public int getTileHeight() { + return tileHeight; + } + + public void setTileHeight(int tileHeight) { + this.tileHeight = tileHeight; + } + + public int getTrackWidth() { + return trackWidth; + } + + public void setTrackWidth(int trackWidth) { + this.trackWidth = trackWidth; + } + + public int getTrackHeight() { + return trackHeight; + } + + public void setTrackHeight(int trackHeight) { + this.trackHeight = trackHeight; + } + + public void initTracking(int tileWidth, int tileHeight, int trackWidth, int trackHeight) { + assert(tileWidth > 0); + assert(tileHeight > 0); + assert(trackWidth > 0); + assert(trackHeight > 0); + assert(tileWidth <= trackWidth); + assert(tileHeight <= trackHeight); + + this.tileWidth = tileWidth; + this.tileHeight = tileHeight; + this.trackWidth = trackWidth; + this.trackHeight = trackHeight; + + int cols = getTileCols(); + int rows = getTileRows(); + snapshot = new boolean[rows][cols]; + for(int i = 0; i < rows; i++) + for(int j = 0; j < cols; j++) + snapshot[i][j] = false; + } + + public synchronized void resize(int trackWidth, int trackHeight) { + assert(tileWidth > 0); + assert(tileHeight > 0); + assert(trackWidth > 0); + assert(trackHeight > 0); + + this.trackWidth = trackWidth; + this.trackHeight = trackHeight; + + int cols = getTileCols(); + int rows = getTileRows(); + snapshot = new boolean[rows][cols]; + for(int i = 0; i < rows; i++) + for(int j = 0; j < cols; j++) + snapshot[i][j] = true; + } + + public void invalidate(Rectangle rect) { + setTileFlag(rect, true); + } + + public void validate(Rectangle rect) { + setTileFlag(rect, false); + } + + public List scan(boolean init) { + List l = new ArrayList(); + + synchronized(this) { + for(int i = 0; i < getTileRows(); i++) { + for(int j = 0; j < getTileCols(); j++) { + if(init || snapshot[i][j]) { + Rectangle rect = new Rectangle(); + rect.y = i*tileHeight; + rect.x = j*tileWidth; + rect.width = Math.min(trackWidth - rect.x, tileWidth); + rect.height = Math.min(trackHeight - rect.y, tileHeight); + + l.add(new TileInfo(i, j, rect)); + snapshot[i][j] = false; + } + } + } + + return l; + } + } + + public boolean hasFullCoverage() { + synchronized(this) { + for(int i = 0; i < getTileRows(); i++) { + for(int j = 0; j < getTileCols(); j++) { + if(!snapshot[i][j]) + return false; + } + } + } + return true; + } + + + + public void initCoverageTest() { + synchronized(this) { + for(int i = 0; i < getTileRows(); i++) { + for(int j = 0; j < getTileCols(); j++) { + snapshot[i][j] = false; + } + } + } + } + + // listener will be called while holding the object lock, use it + // with care to avoid deadlock condition being formed + public synchronized void scan(int nStartRow, int nStartCol, ITileScanListener listener) { + assert(listener != null); + + int cols = getTileCols(); + int rows = getTileRows(); + + nStartRow = nStartRow % rows; + nStartCol = nStartCol % cols; + + int nPos = nStartRow*cols + nStartCol; + int nUnits = rows*cols; + int nStartPos = nPos; + int nRow; + int nCol; + do { + nRow = nPos / cols; + nCol = nPos % cols; + + if(snapshot[nRow][nCol]) { + int nEndCol = nCol; + for(; nEndCol < cols && snapshot[nRow][nEndCol]; nEndCol++) { + snapshot[nRow][nEndCol] = false; + } + + Rectangle rect = new Rectangle(); + rect.y = nRow*tileHeight; + rect.height = tileHeight; + rect.x = nCol*tileWidth; + rect.width = (nEndCol - nCol)*tileWidth; + + if(!listener.onTileChange(rect, nRow, nEndCol)) + break; + } + + nPos = (nPos + 1) % nUnits; + } while(nPos != nStartPos); + } + + public void capture(ITileScanListener listener) { + assert(listener != null); + + int cols = getTileCols(); + int rows = getTileRows(); + + RegionClassifier classifier = new RegionClassifier(); + int left, top, right, bottom; + + synchronized(this) { + for(int i = 0; i < rows; i++) { + top = i*tileHeight; + bottom = Math.min(top + tileHeight, trackHeight); + for(int j = 0; j < cols; j++) { + left = j*tileWidth; + right = Math.min(left + tileWidth, trackWidth); + + if(snapshot[i][j]) { + snapshot[i][j] = false; + classifier.add(new Rectangle(left, top, right - left, bottom - top)); + } + } + } + } + listener.onRegionChange(classifier.getRegionList()); + } + + private synchronized void setTileFlag(Rectangle rect, boolean flag) { + int nStartTileRow; + int nStartTileCol; + int nEndTileRow; + int nEndTileCol; + + int cols = getTileCols(); + int rows = getTileRows(); + + if(rect != null) { + nStartTileRow = Math.min(getTileYPos(rect.y), rows - 1); + nStartTileCol = Math.min(getTileXPos(rect.x), cols - 1); + nEndTileRow = Math.min(getTileYPos(rect.y + rect.height - 1), rows -1); + nEndTileCol = Math.min(getTileXPos(rect.x + rect.width - 1), cols -1); + } else { + nStartTileRow = 0; + nStartTileCol = 0; + nEndTileRow = rows - 1; + nEndTileCol = cols - 1; + } + + for(int i = nStartTileRow; i <= nEndTileRow; i++) + for(int j = nStartTileCol; j <= nEndTileCol; j++) + snapshot[i][j] = flag; + } + + private int getTileRows() { + return (trackHeight + tileHeight - 1) / tileHeight; + } + + private int getTileCols() { + return (trackWidth + tileWidth - 1) / tileWidth; + } + + private int getTileXPos(int x) { + return x / tileWidth; + } + + public int getTileYPos(int y) { + return y / tileHeight; + } +} diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/BufferedImageCanvas.java b/console-proxy/src/com/cloud/consoleproxy/vnc/BufferedImageCanvas.java index a92131fc98d..f2fb4bb6b69 100644 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/BufferedImageCanvas.java +++ b/console-proxy/src/com/cloud/consoleproxy/vnc/BufferedImageCanvas.java @@ -30,125 +30,121 @@ import com.cloud.consoleproxy.util.ImageHelper; import com.cloud.consoleproxy.util.TileInfo; /** - * A BuffereImageCanvas component represents frame buffer image on the - * screen. It also notifies its subscribers when screen is repainted. + * A BuffereImageCanvas component represents frame buffer image on + * the screen. It also notifies its subscribers when screen is repainted. */ public class BufferedImageCanvas extends Canvas implements FrameBufferCanvas { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - // Offline screen buffer - private BufferedImage offlineImage; - - // Cached Graphics2D object for offline screen buffer - private Graphics2D graphics; + // Offline screen buffer + private BufferedImage offlineImage; - private PaintNotificationListener listener; + // Cached Graphics2D object for offline screen buffer + private Graphics2D graphics; - public BufferedImageCanvas(PaintNotificationListener listener, int width, int height) { - super(); - this.listener = listener; + private PaintNotificationListener listener; - setBackground(Color.black); - - setFocusable(true); + public BufferedImageCanvas(PaintNotificationListener listener, int width, int height) { + super(); + this.listener = listener; - // Don't intercept TAB key - setFocusTraversalKeysEnabled(false); + setBackground(Color.black); - setCanvasSize(width, height); - } + setFocusable(true); - public void setCanvasSize(int width, int height) { - this.offlineImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); - graphics = offlineImage.createGraphics(); + // Don't intercept TAB key + setFocusTraversalKeysEnabled(false); - setSize(offlineImage.getWidth(), offlineImage.getHeight()); - } - - @Override - public void update(Graphics g) { - // Call paint() directly, without clearing screen first - paint(g); - } - - @Override - public void paint(Graphics g) { - // Only part of image, requested with repaint(Rectangle), will be - // painted on screen. - synchronized(offlineImage) { - g.drawImage(offlineImage, 0, 0, this); + setCanvasSize(width, height); } - // Notify server that update is painted on screen - listener.imagePaintedOnScreen(); - } - public BufferedImage getOfflineImage() { - return offlineImage; - } + public void setCanvasSize(int width, int height) { + this.offlineImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + graphics = offlineImage.createGraphics(); - public Graphics2D getOfflineGraphics() { - return graphics; - } - - public void copyTile(Graphics2D g, int x, int y, Rectangle rc) { - synchronized(offlineImage) { - g.drawImage(offlineImage, x, y, x + rc.width, y + rc.height, - rc.x, rc.y, rc.x + rc.width, rc.y + rc.height, null); - } - } - - @Override - public Image getFrameBufferScaledImage(int width, int height) { - if(offlineImage != null) - return offlineImage.getScaledInstance(width, height, Image.SCALE_DEFAULT); - return null; - } - - @Override - public byte[] getFrameBufferJpeg() { - int width = 800; - int height = 600; - - width = offlineImage.getWidth(); - height = offlineImage.getHeight(); - - BufferedImage bufferedImage = new BufferedImage(width, height, - BufferedImage.TYPE_3BYTE_BGR); - Graphics2D g = bufferedImage.createGraphics(); - synchronized(offlineImage) { - g.drawImage(offlineImage, 0, 0, width, height, 0, 0, width, height, null); - } - - byte[] imgBits = null; - try { - imgBits = ImageHelper.jpegFromImage(bufferedImage); - } catch (IOException e) { - } - return imgBits; - } - - @Override - public byte[] getTilesMergedJpeg(List tileList, int tileWidth, int tileHeight) { - int width = Math.max(tileWidth, tileWidth*tileList.size()); - BufferedImage bufferedImage = new BufferedImage(width, tileHeight, - BufferedImage.TYPE_3BYTE_BGR); - Graphics2D g = bufferedImage.createGraphics(); - - synchronized(offlineImage) { - int i = 0; - for(TileInfo tile : tileList) { - Rectangle rc = tile.getTileRect(); - g.drawImage(offlineImage, i*tileWidth, 0, i*tileWidth + rc.width, rc.height, - rc.x, rc.y, rc.x + rc.width, rc.y + rc.height, null); - i++; - } - } - - byte[] imgBits = null; - try { - imgBits = ImageHelper.jpegFromImage(bufferedImage); - } catch (IOException e) { - } - return imgBits; - } + setSize(offlineImage.getWidth(), offlineImage.getHeight()); + } + + @Override + public void update(Graphics g) { + // Call paint() directly, without clearing screen first + paint(g); + } + + @Override + public void paint(Graphics g) { + // Only part of image, requested with repaint(Rectangle), will be + // painted on screen. + synchronized (offlineImage) { + g.drawImage(offlineImage, 0, 0, this); + } + // Notify server that update is painted on screen + listener.imagePaintedOnScreen(); + } + + public BufferedImage getOfflineImage() { + return offlineImage; + } + + public Graphics2D getOfflineGraphics() { + return graphics; + } + + public void copyTile(Graphics2D g, int x, int y, Rectangle rc) { + synchronized (offlineImage) { + g.drawImage(offlineImage, x, y, x + rc.width, y + rc.height, rc.x, rc.y, rc.x + rc.width, rc.y + rc.height, null); + } + } + + @Override + public Image getFrameBufferScaledImage(int width, int height) { + if (offlineImage != null) + return offlineImage.getScaledInstance(width, height, Image.SCALE_DEFAULT); + return null; + } + + @Override + public byte[] getFrameBufferJpeg() { + int width = 800; + int height = 600; + + width = offlineImage.getWidth(); + height = offlineImage.getHeight(); + + BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR); + Graphics2D g = bufferedImage.createGraphics(); + synchronized (offlineImage) { + g.drawImage(offlineImage, 0, 0, width, height, 0, 0, width, height, null); + } + + byte[] imgBits = null; + try { + imgBits = ImageHelper.jpegFromImage(bufferedImage); + } catch (IOException e) { + } + return imgBits; + } + + @Override + public byte[] getTilesMergedJpeg(List tileList, int tileWidth, int tileHeight) { + int width = Math.max(tileWidth, tileWidth * tileList.size()); + BufferedImage bufferedImage = new BufferedImage(width, tileHeight, BufferedImage.TYPE_3BYTE_BGR); + Graphics2D g = bufferedImage.createGraphics(); + + synchronized (offlineImage) { + int i = 0; + for (TileInfo tile : tileList) { + Rectangle rc = tile.getTileRect(); + g.drawImage(offlineImage, i * tileWidth, 0, i * tileWidth + rc.width, rc.height, rc.x, rc.y, rc.x + rc.width, rc.y + rc.height, null); + i++; + } + } + + byte[] imgBits = null; + try { + imgBits = ImageHelper.jpegFromImage(bufferedImage); + } catch (IOException e) { + } + return imgBits; + } } \ No newline at end of file diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/FrameBufferCanvas.java b/console-proxy/src/com/cloud/consoleproxy/vnc/FrameBufferCanvas.java index 3b72f442a06..dcf11461360 100644 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/FrameBufferCanvas.java +++ b/console-proxy/src/com/cloud/consoleproxy/vnc/FrameBufferCanvas.java @@ -14,15 +14,17 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.consoleproxy.vnc; - -import java.awt.Image; -import java.util.List; - -import com.cloud.consoleproxy.util.TileInfo; - -public interface FrameBufferCanvas { - Image getFrameBufferScaledImage(int width, int height); - public byte[] getFrameBufferJpeg(); - public byte[] getTilesMergedJpeg(List tileList, int tileWidth, int tileHeight); -} +package com.cloud.consoleproxy.vnc; + +import java.awt.Image; +import java.util.List; + +import com.cloud.consoleproxy.util.TileInfo; + +public interface FrameBufferCanvas { + Image getFrameBufferScaledImage(int width, int height); + + public byte[] getFrameBufferJpeg(); + + public byte[] getTilesMergedJpeg(List tileList, int tileWidth, int tileHeight); +} diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/FrameBufferUpdateListener.java b/console-proxy/src/com/cloud/consoleproxy/vnc/FrameBufferUpdateListener.java index 8dbbe993b14..b8527c5261a 100644 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/FrameBufferUpdateListener.java +++ b/console-proxy/src/com/cloud/consoleproxy/vnc/FrameBufferUpdateListener.java @@ -18,9 +18,9 @@ package com.cloud.consoleproxy.vnc; public interface FrameBufferUpdateListener { - /** - * Notify listener, that frame buffer update packet is received, so client is - * permitted (but not obligated) to ask server to send another update. - */ - void frameBufferPacketReceived(); + /** + * Notify listener, that frame buffer update packet is received, so client + * is permitted (but not obligated) to ask server to send another update. + */ + void frameBufferPacketReceived(); } diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/PaintNotificationListener.java b/console-proxy/src/com/cloud/consoleproxy/vnc/PaintNotificationListener.java index e3bd4af17eb..aaefacb99f2 100644 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/PaintNotificationListener.java +++ b/console-proxy/src/com/cloud/consoleproxy/vnc/PaintNotificationListener.java @@ -18,10 +18,10 @@ package com.cloud.consoleproxy.vnc; public interface PaintNotificationListener { - /** - * Notify subscriber that screen is updated, so client can send another frame - * buffer update request to server. - */ - void imagePaintedOnScreen(); + /** + * Notify subscriber that screen is updated, so client can send another + * frame buffer update request to server. + */ + void imagePaintedOnScreen(); } diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/RfbConstants.java b/console-proxy/src/com/cloud/consoleproxy/vnc/RfbConstants.java index 75499b862ac..18bf47ec1d4 100644 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/RfbConstants.java +++ b/console-proxy/src/com/cloud/consoleproxy/vnc/RfbConstants.java @@ -20,62 +20,63 @@ import java.nio.charset.Charset; public interface RfbConstants { - public static final String RFB_PROTOCOL_VERSION_MAJOR = "RFB 003."; -// public static final String VNC_PROTOCOL_VERSION_MINOR = "003"; - public static final String VNC_PROTOCOL_VERSION_MINOR = "003"; - public static final String RFB_PROTOCOL_VERSION = RFB_PROTOCOL_VERSION_MAJOR + VNC_PROTOCOL_VERSION_MINOR; + public static final String RFB_PROTOCOL_VERSION_MAJOR = "RFB 003."; + // public static final String VNC_PROTOCOL_VERSION_MINOR = "003"; + public static final String VNC_PROTOCOL_VERSION_MINOR = "003"; + public static final String RFB_PROTOCOL_VERSION = RFB_PROTOCOL_VERSION_MAJOR + VNC_PROTOCOL_VERSION_MINOR; - /** - * Server message types. - */ - final static int SERVER_FRAMEBUFFER_UPDATE = 0, SERVER_SET_COLOURMAP_ENTRIES = 1, SERVER_BELL = 2, SERVER_CUT_TEXT = 3; + /** + * Server message types. + */ + final static int SERVER_FRAMEBUFFER_UPDATE = 0, SERVER_SET_COLOURMAP_ENTRIES = 1, SERVER_BELL = 2, SERVER_CUT_TEXT = 3; - /** - * Client message types. - */ - public static final int CLIENT_SET_PIXEL_FORMAT = 0, CLIENT_FIX_COLOURMAP_ENTRIES = 1, CLIENT_SET_ENCODINGS = 2, CLIENT_FRAMEBUFFER_UPDATE_REQUEST = 3, - CLIENT_KEYBOARD_EVENT = 4, CLIENT_POINTER_EVENT = 5, CLIENT_CUT_TEXT = 6; + /** + * Client message types. + */ + public static final int CLIENT_SET_PIXEL_FORMAT = 0, CLIENT_FIX_COLOURMAP_ENTRIES = 1, CLIENT_SET_ENCODINGS = 2, CLIENT_FRAMEBUFFER_UPDATE_REQUEST = 3, CLIENT_KEYBOARD_EVENT = 4, + CLIENT_POINTER_EVENT = 5, CLIENT_CUT_TEXT = 6; - /** - * Server authorization type - */ - public final static int CONNECTION_FAILED = 0, NO_AUTH = 1, VNC_AUTH = 2; + /** + * Server authorization type + */ + public final static int CONNECTION_FAILED = 0, NO_AUTH = 1, VNC_AUTH = 2; - /** - * Server authorization reply. - */ - public final static int VNC_AUTH_OK = 0, VNC_AUTH_FAILED = 1, VNC_AUTH_TOO_MANY = 2; + /** + * Server authorization reply. + */ + public final static int VNC_AUTH_OK = 0, VNC_AUTH_FAILED = 1, VNC_AUTH_TOO_MANY = 2; - /** - * Encodings. - */ - public final static int ENCODING_RAW = 0, ENCODING_COPY_RECT = 1, ENCODING_RRE = 2, ENCODING_CO_RRE = 4, ENCODING_HEXTILE = 5, ENCODING_ZRLE = 16; + /** + * Encodings. + */ + public final static int ENCODING_RAW = 0, ENCODING_COPY_RECT = 1, ENCODING_RRE = 2, ENCODING_CO_RRE = 4, ENCODING_HEXTILE = 5, ENCODING_ZRLE = 16; - /** - * Pseudo-encodings. - */ - public final static int ENCODING_CURSOR = -239 /*0xFFFFFF11*/, ENCODING_DESKTOP_SIZE = -223 /*0xFFFFFF21*/; + /** + * Pseudo-encodings. + */ + public final static int ENCODING_CURSOR = -239 /* 0xFFFFFF11 */, ENCODING_DESKTOP_SIZE = -223 /* 0xFFFFFF21 */; - /** - * Encodings, which we support. - */ - public final static int[] SUPPORTED_ENCODINGS_ARRAY = { ENCODING_RAW, ENCODING_COPY_RECT, ENCODING_DESKTOP_SIZE }; + /** + * Encodings, which we support. + */ + public final static int[] SUPPORTED_ENCODINGS_ARRAY = { ENCODING_RAW, ENCODING_COPY_RECT, ENCODING_DESKTOP_SIZE }; - /** - * Frame buffer update request type: update of whole screen or partial update. - */ - public static final int FRAMEBUFFER_FULL_UPDATE_REQUEST = 0, FRAMEBUFFER_INCREMENTAL_UPDATE_REQUEST = 1; + /** + * Frame buffer update request type: update of whole screen or partial + * update. + */ + public static final int FRAMEBUFFER_FULL_UPDATE_REQUEST = 0, FRAMEBUFFER_INCREMENTAL_UPDATE_REQUEST = 1; - public static final int KEY_UP = 0, KEY_DOWN = 1; + public static final int KEY_UP = 0, KEY_DOWN = 1; - public static final int LITTLE_ENDIAN = 0, BIG_ENDIAN = 1; + public static final int LITTLE_ENDIAN = 0, BIG_ENDIAN = 1; - public static final int EXCLUSIVE_ACCESS = 0, SHARED_ACCESS = 1; + public static final int EXCLUSIVE_ACCESS = 0, SHARED_ACCESS = 1; - public static final int PALETTE = 0, TRUE_COLOR = 1; + public static final int PALETTE = 0, TRUE_COLOR = 1; - /** - * Default charset to use when communicating with server. - */ - public static final Charset CHARSET = Charset.availableCharsets().get("US-ASCII"); + /** + * Default charset to use when communicating with server. + */ + public static final Charset CHARSET = Charset.availableCharsets().get("US-ASCII"); } diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/VncClient.java b/console-proxy/src/com/cloud/consoleproxy/vnc/VncClient.java index abc9ffaeced..194eec17414 100644 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/VncClient.java +++ b/console-proxy/src/com/cloud/consoleproxy/vnc/VncClient.java @@ -39,414 +39,413 @@ import com.cloud.consoleproxy.vnc.packet.client.KeyboardEventPacket; import com.cloud.consoleproxy.vnc.packet.client.MouseEventPacket; public class VncClient { - private static final Logger s_logger = Logger.getLogger(VncClient.class); + private static final Logger s_logger = Logger.getLogger(VncClient.class); - private Socket socket; - private DataInputStream is; - private DataOutputStream os; + private Socket socket; + private DataInputStream is; + private DataOutputStream os; - private VncScreenDescription screen = new VncScreenDescription(); + private VncScreenDescription screen = new VncScreenDescription(); - private VncClientPacketSender sender; - private VncServerPacketReceiver receiver; - - private boolean noUI = false; - private ConsoleProxyClientListener clientListener = null; + private VncClientPacketSender sender; + private VncServerPacketReceiver receiver; - public static void main(String args[]) { - if (args.length < 3) { - printHelpMessage(); - System.exit(1); + private boolean noUI = false; + private ConsoleProxyClientListener clientListener = null; + + public static void main(String args[]) { + if (args.length < 3) { + printHelpMessage(); + System.exit(1); + } + + String host = args[0]; + String port = args[1]; + String password = args[2]; + + try { + new VncClient(host, Integer.parseInt(port), password, false, null); + } catch (NumberFormatException e) { + s_logger.error("Incorrect VNC server port number: " + port + "."); + System.exit(1); + } catch (UnknownHostException e) { + s_logger.error("Incorrect VNC server host name: " + host + "."); + System.exit(1); + } catch (IOException e) { + s_logger.error("Cannot communicate with VNC server: " + e.getMessage()); + System.exit(1); + } catch (Throwable e) { + s_logger.error("An error happened: " + e.getMessage()); + System.exit(1); + } + System.exit(0); } - String host = args[0]; - String port = args[1]; - String password = args[2]; - - try { - new VncClient(host, Integer.parseInt(port), password, false, null); - } catch (NumberFormatException e) { - s_logger.error("Incorrect VNC server port number: " + port + "."); - System.exit(1); - } catch (UnknownHostException e) { - s_logger.error("Incorrect VNC server host name: " + host + "."); - System.exit(1); - } catch (IOException e) { - s_logger.error("Cannot communicate with VNC server: " + e.getMessage()); - System.exit(1); - } catch (Throwable e) { - s_logger.error("An error happened: " + e.getMessage()); - System.exit(1); - } - System.exit(0); - } - - private static void printHelpMessage() { - /* LOG */s_logger.info("Usage: HOST PORT PASSWORD."); - } - - public VncClient(ConsoleProxyClientListener clientListener) { - this.noUI = true; - this.clientListener = clientListener; - } - - public VncClient(String host, int port, String password, boolean noUI, ConsoleProxyClientListener clientListener) - throws UnknownHostException, IOException { - - this.noUI = noUI; - this.clientListener = clientListener; - connectTo(host, port, password); - } - - public void shutdown() { - if(sender != null) - sender.closeConnection(); - - if(receiver != null) - receiver.closeConnection(); - - if(is != null) { - try { - is.close(); - } catch (Throwable e) { - } - } - - if(os != null) { - try { - os.close(); - } catch (Throwable e) { - } - } - - if(socket != null) { - try { - socket.close(); - } catch (Throwable e) { - } - } - } - - public ConsoleProxyClientListener getClientListener() { - return clientListener; - } - - public void connectTo(String host, int port, String path, - String session, boolean useSSL, String sid) throws UnknownHostException, IOException { - if(port < 0) { - if(useSSL) - port = 443; - else - port = 80; - } - - RawHTTP tunnel = new RawHTTP("CONNECT", host, port, path, session, useSSL); - this.socket = tunnel.connect(); - doConnect(sid); - } - - public void connectTo(String host, int port, String password) throws UnknownHostException, IOException { - // Connect to server - s_logger.info("Connecting to VNC server " + host + ":" + port + "..."); - this.socket = new Socket(host, port); - doConnect(password); - } - - private void doConnect(String password) throws IOException { - is = new DataInputStream(socket.getInputStream()); - os = new DataOutputStream(socket.getOutputStream()); - - // Initialize connection - handshake(); - authenticate(password); - initialize(); - - s_logger.info("Connecting to VNC server succeeded, start session"); - - // Run client-to-server packet sender - sender = new VncClientPacketSender(os, screen, this); - - // Create buffered image canvas - BufferedImageCanvas canvas = new BufferedImageCanvas(sender, screen.getFramebufferWidth(), screen.getFramebufferHeight()); - - // Subscribe packet sender to various events - canvas.addMouseListener(sender); - canvas.addMouseMotionListener(sender); - canvas.addKeyListener(sender); - - Frame frame = null; - if(!noUI) - frame = createVncClientMainWindow(canvas, screen.getDesktopName()); - - new Thread(sender).start(); - - // Run server-to-client packet receiver - receiver = new VncServerPacketReceiver(is, canvas, screen, this, sender, clientListener); - try { - receiver.run(); - } finally { - if(frame != null) { - frame.setVisible(false); - frame.dispose(); - } - this.shutdown(); - } - } - - private Frame createVncClientMainWindow(BufferedImageCanvas canvas, String title) { - // Create AWT windows - final Frame frame = new Frame(title + " - VNCle"); - - // Use scrolling pane to support screens, which are larger than ours - ScrollPane scroller = new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED); - scroller.add(canvas); - scroller.setSize(screen.getFramebufferWidth(), screen.getFramebufferHeight()); - - frame.add(scroller); - frame.pack(); - frame.setVisible(true); - - frame.addWindowListener(new WindowAdapter() { - public void windowClosing(WindowEvent evt) { - frame.setVisible(false); - shutdown(); - } - }); - - return frame; - } - - /** - * Handshake with VNC server. - */ - private void handshake() throws IOException { - - // Read protocol version - byte[] buf = new byte[12]; - is.readFully(buf); - String rfbProtocol = new String(buf); - - // Server should use RFB protocol 3.x - if (!rfbProtocol.contains(RfbConstants.RFB_PROTOCOL_VERSION_MAJOR)) { - s_logger.error("Cannot handshake with VNC server. Unsupported protocol version: \"" + rfbProtocol + "\"."); - throw new RuntimeException("Cannot handshake with VNC server. Unsupported protocol version: \"" + rfbProtocol + "\"."); + private static void printHelpMessage() { + /* LOG */s_logger.info("Usage: HOST PORT PASSWORD."); } - // Send response: we support RFB 3.3 only - String ourProtocolString = RfbConstants.RFB_PROTOCOL_VERSION + "\n"; - os.write(ourProtocolString.getBytes()); - os.flush(); - } - - /** - * VNC authentication. - */ - private void authenticate(String password) throws IOException { - // Read security type - int authType = is.readInt(); - - switch (authType) { - case RfbConstants.CONNECTION_FAILED: { - // Server forbids to connect. Read reason and throw exception - - int length = is.readInt(); - byte[] buf = new byte[length]; - is.readFully(buf); - String reason = new String(buf, RfbConstants.CHARSET); - - s_logger.error("Authentication to VNC server is failed. Reason: " + reason); - throw new RuntimeException("Authentication to VNC server is failed. Reason: " + reason); + public VncClient(ConsoleProxyClientListener clientListener) { + this.noUI = true; + this.clientListener = clientListener; } - case RfbConstants.NO_AUTH: { - // Client can connect without authorization. Nothing to do. - break; + public VncClient(String host, int port, String password, boolean noUI, ConsoleProxyClientListener clientListener) throws UnknownHostException, IOException { + + this.noUI = noUI; + this.clientListener = clientListener; + connectTo(host, port, password); } - case RfbConstants.VNC_AUTH: { - s_logger.info("VNC server requires password authentication"); - doVncAuth(password); - break; + public void shutdown() { + if (sender != null) + sender.closeConnection(); + + if (receiver != null) + receiver.closeConnection(); + + if (is != null) { + try { + is.close(); + } catch (Throwable e) { + } + } + + if (os != null) { + try { + os.close(); + } catch (Throwable e) { + } + } + + if (socket != null) { + try { + socket.close(); + } catch (Throwable e) { + } + } } - default: - s_logger.error("Unsupported VNC protocol authorization scheme, scheme code: " + authType + "."); - throw new RuntimeException("Unsupported VNC protocol authorization scheme, scheme code: " + authType + "."); - } - } - - /** - * Encode client password and send it to server. - */ - private void doVncAuth(String password) throws IOException { - - // Read challenge - byte[] challenge = new byte[16]; - is.readFully(challenge); - - // Encode challenge with password - byte[] response; - try { - response = encodePassword(challenge, password); - } catch (Exception e) { - s_logger.error("Cannot encrypt client password to send to server: " + e.getMessage()); - throw new RuntimeException("Cannot encrypt client password to send to server: " + e.getMessage()); + public ConsoleProxyClientListener getClientListener() { + return clientListener; } - // Send encoded challenge - os.write(response); - os.flush(); + public void connectTo(String host, int port, String path, String session, boolean useSSL, String sid) throws UnknownHostException, IOException { + if (port < 0) { + if (useSSL) + port = 443; + else + port = 80; + } - // Read security result - int authResult = is.readInt(); - - switch (authResult) { - case RfbConstants.VNC_AUTH_OK: { - // Nothing to do - break; + RawHTTP tunnel = new RawHTTP("CONNECT", host, port, path, session, useSSL); + this.socket = tunnel.connect(); + doConnect(sid); } - case RfbConstants.VNC_AUTH_TOO_MANY: - s_logger.error("Connection to VNC server failed: too many wrong attempts."); - throw new RuntimeException("Connection to VNC server failed: too many wrong attempts."); - - case RfbConstants.VNC_AUTH_FAILED: - s_logger.error("Connection to VNC server failed: wrong password."); - throw new RuntimeException("Connection to VNC server failed: wrong password."); - - default: - s_logger.error("Connection to VNC server failed, reason code: " + authResult); - throw new RuntimeException("Connection to VNC server failed, reason code: " + authResult); - } - } - - /** - * Encode password using DES encryption with given challenge. - * - * @param challenge - * a random set of bytes. - * @param password - * a password - * @return DES hash of password and challenge - */ - public byte[] encodePassword(byte[] challenge, String password) throws Exception { - // VNC password consist of up to eight ASCII characters. - byte[] key = { 0, 0, 0, 0, 0, 0, 0, 0 }; // Padding - byte[] passwordAsciiBytes = password.getBytes(RfbConstants.CHARSET); - System.arraycopy(passwordAsciiBytes, 0, key, 0, Math.min(password.length(), 8)); - - // Flip bytes (reverse bits) in key - for (int i = 0; i < key.length; i++) { - key[i] = flipByte(key[i]); + public void connectTo(String host, int port, String password) throws UnknownHostException, IOException { + // Connect to server + s_logger.info("Connecting to VNC server " + host + ":" + port + "..."); + this.socket = new Socket(host, port); + doConnect(password); } - KeySpec desKeySpec = new DESKeySpec(key); - SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DES"); - SecretKey secretKey = secretKeyFactory.generateSecret(desKeySpec); - Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding"); - cipher.init(Cipher.ENCRYPT_MODE, secretKey); + private void doConnect(String password) throws IOException { + is = new DataInputStream(socket.getInputStream()); + os = new DataOutputStream(socket.getOutputStream()); - byte[] response = cipher.doFinal(challenge); - return response; - } + // Initialize connection + handshake(); + authenticate(password); + initialize(); - /** - * Reverse bits in byte, so least significant bit will be most significant - * bit. E.g. 01001100 will become 00110010. - * - * See also: http://www.vidarholen.net/contents/junk/vnc.html , - * http://bytecrafter .blogspot.com/2010/09/des-encryption-as-used-in-vnc.html - * - * @param b - * a byte - * @return byte in reverse order - */ - private static byte flipByte(byte b) { - int b1_8 = (b & 0x1) << 7; - int b2_7 = (b & 0x2) << 5; - int b3_6 = (b & 0x4) << 3; - int b4_5 = (b & 0x8) << 1; - int b5_4 = (b & 0x10) >>> 1; - int b6_3 = (b & 0x20) >>> 3; - int b7_2 = (b & 0x40) >>> 5; - int b8_1 = (b & 0x80) >>> 7; - byte c = (byte) (b1_8 | b2_7 | b3_6 | b4_5 | b5_4 | b6_3 | b7_2 | b8_1); - return c; - } + s_logger.info("Connecting to VNC server succeeded, start session"); - private void initialize() throws IOException { - // Send client initialization message - { - // Send shared flag - os.writeByte(RfbConstants.EXCLUSIVE_ACCESS); - os.flush(); + // Run client-to-server packet sender + sender = new VncClientPacketSender(os, screen, this); + + // Create buffered image canvas + BufferedImageCanvas canvas = new BufferedImageCanvas(sender, screen.getFramebufferWidth(), screen.getFramebufferHeight()); + + // Subscribe packet sender to various events + canvas.addMouseListener(sender); + canvas.addMouseMotionListener(sender); + canvas.addKeyListener(sender); + + Frame frame = null; + if (!noUI) + frame = createVncClientMainWindow(canvas, screen.getDesktopName()); + + new Thread(sender).start(); + + // Run server-to-client packet receiver + receiver = new VncServerPacketReceiver(is, canvas, screen, this, sender, clientListener); + try { + receiver.run(); + } finally { + if (frame != null) { + frame.setVisible(false); + frame.dispose(); + } + this.shutdown(); + } } - // Read server initialization message - { - // Read frame buffer size - int framebufferWidth = is.readUnsignedShort(); - int framebufferHeight = is.readUnsignedShort(); - screen.setFramebufferSize(framebufferWidth, framebufferHeight); - if(clientListener != null) - clientListener.onFramebufferSizeChange(framebufferWidth, framebufferHeight); + private Frame createVncClientMainWindow(BufferedImageCanvas canvas, String title) { + // Create AWT windows + final Frame frame = new Frame(title + " - VNCle"); + + // Use scrolling pane to support screens, which are larger than ours + ScrollPane scroller = new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED); + scroller.add(canvas); + scroller.setSize(screen.getFramebufferWidth(), screen.getFramebufferHeight()); + + frame.add(scroller); + frame.pack(); + frame.setVisible(true); + + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent evt) { + frame.setVisible(false); + shutdown(); + } + }); + + return frame; } - // Read pixel format - { - int bitsPerPixel = is.readUnsignedByte(); - int depth = is.readUnsignedByte(); + /** + * Handshake with VNC server. + */ + private void handshake() throws IOException { - int bigEndianFlag = is.readUnsignedByte(); - int trueColorFlag = is.readUnsignedByte(); + // Read protocol version + byte[] buf = new byte[12]; + is.readFully(buf); + String rfbProtocol = new String(buf); - int redMax = is.readUnsignedShort(); - int greenMax = is.readUnsignedShort(); - int blueMax = is.readUnsignedShort(); + // Server should use RFB protocol 3.x + if (!rfbProtocol.contains(RfbConstants.RFB_PROTOCOL_VERSION_MAJOR)) { + s_logger.error("Cannot handshake with VNC server. Unsupported protocol version: \"" + rfbProtocol + "\"."); + throw new RuntimeException("Cannot handshake with VNC server. Unsupported protocol version: \"" + rfbProtocol + "\"."); + } - int redShift = is.readUnsignedByte(); - int greenShift = is.readUnsignedByte(); - int blueShift = is.readUnsignedByte(); - - // Skip padding - is.skipBytes(3); - - screen.setPixelFormat(bitsPerPixel, depth, bigEndianFlag, trueColorFlag, redMax, greenMax, blueMax, redShift, greenShift, blueShift); + // Send response: we support RFB 3.3 only + String ourProtocolString = RfbConstants.RFB_PROTOCOL_VERSION + "\n"; + os.write(ourProtocolString.getBytes()); + os.flush(); } - // Read desktop name - { - int length = is.readInt(); - byte buf[] = new byte[length]; - is.readFully(buf); - String desktopName = new String(buf, RfbConstants.CHARSET); - screen.setDesktopName(desktopName); + /** + * VNC authentication. + */ + private void authenticate(String password) throws IOException { + // Read security type + int authType = is.readInt(); + + switch (authType) { + case RfbConstants.CONNECTION_FAILED: { + // Server forbids to connect. Read reason and throw exception + + int length = is.readInt(); + byte[] buf = new byte[length]; + is.readFully(buf); + String reason = new String(buf, RfbConstants.CHARSET); + + s_logger.error("Authentication to VNC server is failed. Reason: " + reason); + throw new RuntimeException("Authentication to VNC server is failed. Reason: " + reason); + } + + case RfbConstants.NO_AUTH: { + // Client can connect without authorization. Nothing to do. + break; + } + + case RfbConstants.VNC_AUTH: { + s_logger.info("VNC server requires password authentication"); + doVncAuth(password); + break; + } + + default: + s_logger.error("Unsupported VNC protocol authorization scheme, scheme code: " + authType + "."); + throw new RuntimeException("Unsupported VNC protocol authorization scheme, scheme code: " + authType + "."); + } + } + + /** + * Encode client password and send it to server. + */ + private void doVncAuth(String password) throws IOException { + + // Read challenge + byte[] challenge = new byte[16]; + is.readFully(challenge); + + // Encode challenge with password + byte[] response; + try { + response = encodePassword(challenge, password); + } catch (Exception e) { + s_logger.error("Cannot encrypt client password to send to server: " + e.getMessage()); + throw new RuntimeException("Cannot encrypt client password to send to server: " + e.getMessage()); + } + + // Send encoded challenge + os.write(response); + os.flush(); + + // Read security result + int authResult = is.readInt(); + + switch (authResult) { + case RfbConstants.VNC_AUTH_OK: { + // Nothing to do + break; + } + + case RfbConstants.VNC_AUTH_TOO_MANY: + s_logger.error("Connection to VNC server failed: too many wrong attempts."); + throw new RuntimeException("Connection to VNC server failed: too many wrong attempts."); + + case RfbConstants.VNC_AUTH_FAILED: + s_logger.error("Connection to VNC server failed: wrong password."); + throw new RuntimeException("Connection to VNC server failed: wrong password."); + + default: + s_logger.error("Connection to VNC server failed, reason code: " + authResult); + throw new RuntimeException("Connection to VNC server failed, reason code: " + authResult); + } + } + + /** + * Encode password using DES encryption with given challenge. + * + * @param challenge + * a random set of bytes. + * @param password + * a password + * @return DES hash of password and challenge + */ + public byte[] encodePassword(byte[] challenge, String password) throws Exception { + // VNC password consist of up to eight ASCII characters. + byte[] key = { 0, 0, 0, 0, 0, 0, 0, 0 }; // Padding + byte[] passwordAsciiBytes = password.getBytes(RfbConstants.CHARSET); + System.arraycopy(passwordAsciiBytes, 0, key, 0, Math.min(password.length(), 8)); + + // Flip bytes (reverse bits) in key + for (int i = 0; i < key.length; i++) { + key[i] = flipByte(key[i]); + } + + KeySpec desKeySpec = new DESKeySpec(key); + SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DES"); + SecretKey secretKey = secretKeyFactory.generateSecret(desKeySpec); + Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding"); + cipher.init(Cipher.ENCRYPT_MODE, secretKey); + + byte[] response = cipher.doFinal(challenge); + return response; + } + + /** + * Reverse bits in byte, so least significant bit will be most significant + * bit. E.g. 01001100 will become 00110010. + * + * See also: http://www.vidarholen.net/contents/junk/vnc.html , + * http://bytecrafter + * .blogspot.com/2010/09/des-encryption-as-used-in-vnc.html + * + * @param b + * a byte + * @return byte in reverse order + */ + private static byte flipByte(byte b) { + int b1_8 = (b & 0x1) << 7; + int b2_7 = (b & 0x2) << 5; + int b3_6 = (b & 0x4) << 3; + int b4_5 = (b & 0x8) << 1; + int b5_4 = (b & 0x10) >>> 1; + int b6_3 = (b & 0x20) >>> 3; + int b7_2 = (b & 0x40) >>> 5; + int b8_1 = (b & 0x80) >>> 7; + byte c = (byte) (b1_8 | b2_7 | b3_6 | b4_5 | b5_4 | b6_3 | b7_2 | b8_1); + return c; + } + + private void initialize() throws IOException { + // Send client initialization message + { + // Send shared flag + os.writeByte(RfbConstants.EXCLUSIVE_ACCESS); + os.flush(); + } + + // Read server initialization message + { + // Read frame buffer size + int framebufferWidth = is.readUnsignedShort(); + int framebufferHeight = is.readUnsignedShort(); + screen.setFramebufferSize(framebufferWidth, framebufferHeight); + if (clientListener != null) + clientListener.onFramebufferSizeChange(framebufferWidth, framebufferHeight); + } + + // Read pixel format + { + int bitsPerPixel = is.readUnsignedByte(); + int depth = is.readUnsignedByte(); + + int bigEndianFlag = is.readUnsignedByte(); + int trueColorFlag = is.readUnsignedByte(); + + int redMax = is.readUnsignedShort(); + int greenMax = is.readUnsignedShort(); + int blueMax = is.readUnsignedShort(); + + int redShift = is.readUnsignedByte(); + int greenShift = is.readUnsignedByte(); + int blueShift = is.readUnsignedByte(); + + // Skip padding + is.skipBytes(3); + + screen.setPixelFormat(bitsPerPixel, depth, bigEndianFlag, trueColorFlag, redMax, greenMax, blueMax, redShift, greenShift, blueShift); + } + + // Read desktop name + { + int length = is.readInt(); + byte buf[] = new byte[length]; + is.readFully(buf); + String desktopName = new String(buf, RfbConstants.CHARSET); + screen.setDesktopName(desktopName); + } + } + + public FrameBufferCanvas getFrameBufferCanvas() { + if (receiver != null) + return receiver.getCanvas(); + + return null; + } + + public void requestUpdate(boolean fullUpdate) { + if (fullUpdate) + sender.requestFullScreenUpdate(); + else + sender.imagePaintedOnScreen(); + } + + public void sendClientKeyboardEvent(int event, int code, int modifiers) { + sender.sendClientPacket(new KeyboardEventPacket(event, code)); + } + + public void sendClientMouseEvent(int event, int x, int y, int code, int modifiers) { + sender.sendClientPacket(new MouseEventPacket(event, x, y)); + } + + public boolean isHostConnected() { + return receiver != null && receiver.isConnectionAlive(); } - } - - public FrameBufferCanvas getFrameBufferCanvas() { - if(receiver != null) - return receiver.getCanvas(); - - return null; - } - - public void requestUpdate(boolean fullUpdate) { - if(fullUpdate) - sender.requestFullScreenUpdate(); - else - sender.imagePaintedOnScreen(); - } - - public void sendClientKeyboardEvent(int event, int code, int modifiers) { - sender.sendClientPacket(new KeyboardEventPacket(event, code)); - } - - public void sendClientMouseEvent(int event, int x, int y, int code, int modifiers) { - sender.sendClientPacket(new MouseEventPacket(event, x, y)); - } - - public boolean isHostConnected() { - return receiver != null && receiver.isConnectionAlive(); - } } diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/VncClientPacketSender.java b/console-proxy/src/com/cloud/consoleproxy/vnc/VncClientPacketSender.java index cf15f885e5f..d27b76d3468 100644 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/VncClientPacketSender.java +++ b/console-proxy/src/com/cloud/consoleproxy/vnc/VncClientPacketSender.java @@ -35,226 +35,224 @@ import com.cloud.consoleproxy.vnc.packet.client.SetEncodingsPacket; import com.cloud.consoleproxy.vnc.packet.client.SetPixelFormatPacket; public class VncClientPacketSender implements Runnable, PaintNotificationListener, KeyListener, MouseListener, MouseMotionListener, FrameBufferUpdateListener { - private static final Logger s_logger = Logger.getLogger(VncClientPacketSender.class); + private static final Logger s_logger = Logger.getLogger(VncClientPacketSender.class); - // Queue for outgoing packets - private final BlockingQueue queue = new ArrayBlockingQueue(30); + // Queue for outgoing packets + private final BlockingQueue queue = new ArrayBlockingQueue(30); - private final DataOutputStream os; - private final VncScreenDescription screen; - private final VncClient vncConnection; + private final DataOutputStream os; + private final VncScreenDescription screen; + private final VncClient vncConnection; - private boolean connectionAlive = true; + private boolean connectionAlive = true; - // Don't send update request again until we receive next frame buffer update - private boolean updateRequestSent = false; + // Don't send update request again until we receive next frame buffer update + private boolean updateRequestSent = false; - public VncClientPacketSender(DataOutputStream os, VncScreenDescription screen, VncClient vncConnection) { - this.os = os; - this.screen = screen; - this.vncConnection = vncConnection; + public VncClientPacketSender(DataOutputStream os, VncScreenDescription screen, VncClient vncConnection) { + this.os = os; + this.screen = screen; + this.vncConnection = vncConnection; - sendSetPixelFormat(); - sendSetEncodings(); - requestFullScreenUpdate(); - } - - public void sendClientPacket(ClientPacket packet) { - queue.add(packet); - } + sendSetPixelFormat(); + sendSetEncodings(); + requestFullScreenUpdate(); + } - @Override - public void run() { - try { - while (connectionAlive) { - ClientPacket packet = queue.poll(1, TimeUnit.SECONDS); - if (packet != null) { - packet.write(os); - os.flush(); + public void sendClientPacket(ClientPacket packet) { + queue.add(packet); + } + + @Override + public void run() { + try { + while (connectionAlive) { + ClientPacket packet = queue.poll(1, TimeUnit.SECONDS); + if (packet != null) { + packet.write(os); + os.flush(); + } + } + } catch (Throwable e) { + s_logger.error("Unexpected exception: ", e); + if (connectionAlive) { + closeConnection(); + vncConnection.shutdown(); + } } - } - } catch (Throwable e) { - s_logger.error("Unexpected exception: ", e); - if (connectionAlive) { - closeConnection(); - vncConnection.shutdown(); - } - } - } - - private void sendSetEncodings() { - queue.add(new SetEncodingsPacket(RfbConstants.SUPPORTED_ENCODINGS_ARRAY)); - } - - private void sendSetPixelFormat() { - if (!screen.isRGB888_32_LE()) { - queue.add(new SetPixelFormatPacket(screen, 32, 24, RfbConstants.LITTLE_ENDIAN, RfbConstants.TRUE_COLOR, 255, 255, 255, 16, 8, 0)); - } - } - - public void closeConnection() { - connectionAlive = false; - } - - public void requestFullScreenUpdate() { - queue.add(new FramebufferUpdateRequestPacket(RfbConstants.FRAMEBUFFER_FULL_UPDATE_REQUEST, 0, 0, screen.getFramebufferWidth(), screen - .getFramebufferHeight())); - updateRequestSent = true; - } - - @Override - public void imagePaintedOnScreen() { - if (!updateRequestSent) { - queue.add(new FramebufferUpdateRequestPacket(RfbConstants.FRAMEBUFFER_INCREMENTAL_UPDATE_REQUEST, 0, 0, screen.getFramebufferWidth(), screen - .getFramebufferHeight())); - updateRequestSent = true; - } - } - - @Override - public void frameBufferPacketReceived() { - updateRequestSent = false; - } - - @Override - public void mouseDragged(MouseEvent e) { - queue.add(new MouseEventPacket(mapAwtModifiersToVncButtonMask(e.getModifiersEx()), e.getX(), e.getY())); - } - - @Override - public void mouseMoved(MouseEvent e) { - queue.add(new MouseEventPacket(mapAwtModifiersToVncButtonMask(e.getModifiersEx()), e.getX(), e.getY())); - } - - @Override - public void mouseClicked(MouseEvent e) { - // Nothing to do - } - - @Override - public void mousePressed(MouseEvent e) { - queue.add(new MouseEventPacket(mapAwtModifiersToVncButtonMask(e.getModifiersEx()), e.getX(), e.getY())); - } - - @Override - public void mouseReleased(MouseEvent e) { - queue.add(new MouseEventPacket(mapAwtModifiersToVncButtonMask(e.getModifiersEx()), e.getX(), e.getY())); - } - - @Override - public void mouseEntered(MouseEvent e) { - // Nothing to do - } - - @Override - public void mouseExited(MouseEvent e) { - // Nothing to do - } - - /** - * Current state of buttons 1 to 8 are represented by bits 0 to 7 of - * button-mask respectively, 0 meaning up, 1 meaning down (pressed). On a - * conventional mouse, buttons 1, 2 and 3 correspond to the left, middle and - * right buttons on the mouse. On a wheel mouse, each step of the wheel - * upwards is represented by a press and release of button 4, and each step - * downwards is represented by a press and release of button 5. - * - * @param modifiers - * extended modifiers from AWT mouse event - * @return VNC mouse button mask - */ - public static int mapAwtModifiersToVncButtonMask(int modifiers) { - int mask = (((modifiers & MouseEvent.BUTTON1_DOWN_MASK) != 0) ? 0x1 : 0) | (((modifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0) ? 0x2 : 0) - | (((modifiers & MouseEvent.BUTTON3_DOWN_MASK) != 0) ? 0x4 : 0); - return mask; - } - - @Override - public void keyTyped(KeyEvent e) { - // Do nothing - } - - @Override - public void keyPressed(KeyEvent e) { - ClientPacket request = new KeyboardEventPacket(RfbConstants.KEY_DOWN, mapAwtKeyToVncKey(e.getKeyCode())); - queue.add(request); - } - - @Override - public void keyReleased(KeyEvent e) { - ClientPacket request = new KeyboardEventPacket(RfbConstants.KEY_UP, mapAwtKeyToVncKey(e.getKeyCode())); - queue.add(request); - } - - private int mapAwtKeyToVncKey(int key) { - switch (key) { - case KeyEvent.VK_BACK_SPACE: - return 0xff08; - case KeyEvent.VK_TAB: - return 0xff09; - case KeyEvent.VK_ENTER: - return 0xff0d; - case KeyEvent.VK_ESCAPE: - return 0xff1b; - case KeyEvent.VK_INSERT: - return 0xff63; - case KeyEvent.VK_DELETE: - return 0xffff; - case KeyEvent.VK_HOME: - return 0xff50; - case KeyEvent.VK_END: - return 0xff57; - case KeyEvent.VK_PAGE_UP: - return 0xff55; - case KeyEvent.VK_PAGE_DOWN: - return 0xff56; - case KeyEvent.VK_LEFT: - return 0xff51; - case KeyEvent.VK_UP: - return 0xff52; - case KeyEvent.VK_RIGHT: - return 0xff53; - case KeyEvent.VK_DOWN: - return 0xff54; - case KeyEvent.VK_F1: - return 0xffbe; - case KeyEvent.VK_F2: - return 0xffbf; - case KeyEvent.VK_F3: - return 0xffc0; - case KeyEvent.VK_F4: - return 0xffc1; - case KeyEvent.VK_F5: - return 0xffc2; - case KeyEvent.VK_F6: - return 0xffc3; - case KeyEvent.VK_F7: - return 0xffc4; - case KeyEvent.VK_F8: - return 0xffc5; - case KeyEvent.VK_F9: - return 0xffc6; - case KeyEvent.VK_F10: - return 0xffc7; - case KeyEvent.VK_F11: - return 0xffc8; - case KeyEvent.VK_F12: - return 0xffc9; - case KeyEvent.VK_SHIFT: - return 0xffe1; - case KeyEvent.VK_CONTROL: - return 0xffe3; - case KeyEvent.VK_META: - return 0xffe7; - case KeyEvent.VK_ALT: - return 0xffe9; - case KeyEvent.VK_ALT_GRAPH: - return 0xffea; - case KeyEvent.VK_BACK_QUOTE: - return 0x0060; } - return key; - } + private void sendSetEncodings() { + queue.add(new SetEncodingsPacket(RfbConstants.SUPPORTED_ENCODINGS_ARRAY)); + } + + private void sendSetPixelFormat() { + if (!screen.isRGB888_32_LE()) { + queue.add(new SetPixelFormatPacket(screen, 32, 24, RfbConstants.LITTLE_ENDIAN, RfbConstants.TRUE_COLOR, 255, 255, 255, 16, 8, 0)); + } + } + + public void closeConnection() { + connectionAlive = false; + } + + public void requestFullScreenUpdate() { + queue.add(new FramebufferUpdateRequestPacket(RfbConstants.FRAMEBUFFER_FULL_UPDATE_REQUEST, 0, 0, screen.getFramebufferWidth(), screen.getFramebufferHeight())); + updateRequestSent = true; + } + + @Override + public void imagePaintedOnScreen() { + if (!updateRequestSent) { + queue.add(new FramebufferUpdateRequestPacket(RfbConstants.FRAMEBUFFER_INCREMENTAL_UPDATE_REQUEST, 0, 0, screen.getFramebufferWidth(), screen.getFramebufferHeight())); + updateRequestSent = true; + } + } + + @Override + public void frameBufferPacketReceived() { + updateRequestSent = false; + } + + @Override + public void mouseDragged(MouseEvent e) { + queue.add(new MouseEventPacket(mapAwtModifiersToVncButtonMask(e.getModifiersEx()), e.getX(), e.getY())); + } + + @Override + public void mouseMoved(MouseEvent e) { + queue.add(new MouseEventPacket(mapAwtModifiersToVncButtonMask(e.getModifiersEx()), e.getX(), e.getY())); + } + + @Override + public void mouseClicked(MouseEvent e) { + // Nothing to do + } + + @Override + public void mousePressed(MouseEvent e) { + queue.add(new MouseEventPacket(mapAwtModifiersToVncButtonMask(e.getModifiersEx()), e.getX(), e.getY())); + } + + @Override + public void mouseReleased(MouseEvent e) { + queue.add(new MouseEventPacket(mapAwtModifiersToVncButtonMask(e.getModifiersEx()), e.getX(), e.getY())); + } + + @Override + public void mouseEntered(MouseEvent e) { + // Nothing to do + } + + @Override + public void mouseExited(MouseEvent e) { + // Nothing to do + } + + /** + * Current state of buttons 1 to 8 are represented by bits 0 to 7 of + * button-mask respectively, 0 meaning up, 1 meaning down (pressed). On a + * conventional mouse, buttons 1, 2 and 3 correspond to the left, middle and + * right buttons on the mouse. On a wheel mouse, each step of the wheel + * upwards is represented by a press and release of button 4, and each step + * downwards is represented by a press and release of button 5. + * + * @param modifiers + * extended modifiers from AWT mouse event + * @return VNC mouse button mask + */ + public static int mapAwtModifiersToVncButtonMask(int modifiers) { + int mask = (((modifiers & MouseEvent.BUTTON1_DOWN_MASK) != 0) ? 0x1 : 0) | (((modifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0) ? 0x2 : 0) + | (((modifiers & MouseEvent.BUTTON3_DOWN_MASK) != 0) ? 0x4 : 0); + return mask; + } + + @Override + public void keyTyped(KeyEvent e) { + // Do nothing + } + + @Override + public void keyPressed(KeyEvent e) { + ClientPacket request = new KeyboardEventPacket(RfbConstants.KEY_DOWN, mapAwtKeyToVncKey(e.getKeyCode())); + queue.add(request); + } + + @Override + public void keyReleased(KeyEvent e) { + ClientPacket request = new KeyboardEventPacket(RfbConstants.KEY_UP, mapAwtKeyToVncKey(e.getKeyCode())); + queue.add(request); + } + + private int mapAwtKeyToVncKey(int key) { + switch (key) { + case KeyEvent.VK_BACK_SPACE: + return 0xff08; + case KeyEvent.VK_TAB: + return 0xff09; + case KeyEvent.VK_ENTER: + return 0xff0d; + case KeyEvent.VK_ESCAPE: + return 0xff1b; + case KeyEvent.VK_INSERT: + return 0xff63; + case KeyEvent.VK_DELETE: + return 0xffff; + case KeyEvent.VK_HOME: + return 0xff50; + case KeyEvent.VK_END: + return 0xff57; + case KeyEvent.VK_PAGE_UP: + return 0xff55; + case KeyEvent.VK_PAGE_DOWN: + return 0xff56; + case KeyEvent.VK_LEFT: + return 0xff51; + case KeyEvent.VK_UP: + return 0xff52; + case KeyEvent.VK_RIGHT: + return 0xff53; + case KeyEvent.VK_DOWN: + return 0xff54; + case KeyEvent.VK_F1: + return 0xffbe; + case KeyEvent.VK_F2: + return 0xffbf; + case KeyEvent.VK_F3: + return 0xffc0; + case KeyEvent.VK_F4: + return 0xffc1; + case KeyEvent.VK_F5: + return 0xffc2; + case KeyEvent.VK_F6: + return 0xffc3; + case KeyEvent.VK_F7: + return 0xffc4; + case KeyEvent.VK_F8: + return 0xffc5; + case KeyEvent.VK_F9: + return 0xffc6; + case KeyEvent.VK_F10: + return 0xffc7; + case KeyEvent.VK_F11: + return 0xffc8; + case KeyEvent.VK_F12: + return 0xffc9; + case KeyEvent.VK_SHIFT: + return 0xffe1; + case KeyEvent.VK_CONTROL: + return 0xffe3; + case KeyEvent.VK_META: + return 0xffe7; + case KeyEvent.VK_ALT: + return 0xffe9; + case KeyEvent.VK_ALT_GRAPH: + return 0xffea; + case KeyEvent.VK_BACK_QUOTE: + return 0x0060; + } + + return key; + } } diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/VncScreenDescription.java b/console-proxy/src/com/cloud/consoleproxy/vnc/VncScreenDescription.java index c9e135ecd34..44b2a34769b 100644 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/VncScreenDescription.java +++ b/console-proxy/src/com/cloud/consoleproxy/vnc/VncScreenDescription.java @@ -21,70 +21,69 @@ package com.cloud.consoleproxy.vnc; */ public class VncScreenDescription { - // Frame buffer size - private int framebufferWidth = -1; - private int framebufferHeight = -1; + // Frame buffer size + private int framebufferWidth = -1; + private int framebufferHeight = -1; - // Desktop name - private String desktopName; + // Desktop name + private String desktopName; - // Bytes per pixel - private int bytesPerPixel; + // Bytes per pixel + private int bytesPerPixel; - // Indicates that screen uses format which we want to use: - // RGB 24bit packed into 32bit little-endian int. - private boolean rgb888_32_le = false; + // Indicates that screen uses format which we want to use: + // RGB 24bit packed into 32bit little-endian int. + private boolean rgb888_32_le = false; - public VncScreenDescription() { - } + public VncScreenDescription() { + } - /** - * Store information about server pixel format. - */ - public void setPixelFormat(int bitsPerPixel, int depth, int bigEndianFlag, int trueColorFlag, int redMax, int greenMax, int blueMax, int redShift, - int greenShift, int blueShift) { + /** + * Store information about server pixel format. + */ + public void setPixelFormat(int bitsPerPixel, int depth, int bigEndianFlag, int trueColorFlag, int redMax, int greenMax, int blueMax, int redShift, int greenShift, int blueShift) { - bytesPerPixel = (bitsPerPixel + 7) / 8; + bytesPerPixel = (bitsPerPixel + 7) / 8; - rgb888_32_le = (depth == 24 && bitsPerPixel == 32 && redShift == 16 && greenShift == 8 && blueShift == 0 && redMax == 255 && greenMax == 255 - && blueMax == 255 && bigEndianFlag == RfbConstants.LITTLE_ENDIAN && trueColorFlag == RfbConstants.TRUE_COLOR); - } + rgb888_32_le = (depth == 24 && bitsPerPixel == 32 && redShift == 16 && greenShift == 8 && blueShift == 0 && redMax == 255 && greenMax == 255 && blueMax == 255 + && bigEndianFlag == RfbConstants.LITTLE_ENDIAN && trueColorFlag == RfbConstants.TRUE_COLOR); + } - /** - * Store information about server screen size. - */ - public void setFramebufferSize(int framebufferWidth, int framebufferHeight) { - this.framebufferWidth = framebufferWidth; - this.framebufferHeight = framebufferHeight; - } + /** + * Store information about server screen size. + */ + public void setFramebufferSize(int framebufferWidth, int framebufferHeight) { + this.framebufferWidth = framebufferWidth; + this.framebufferHeight = framebufferHeight; + } - /** - * Store server desktop name. - */ - public void setDesktopName(String desktopName) { - this.desktopName = desktopName; - } + /** + * Store server desktop name. + */ + public void setDesktopName(String desktopName) { + this.desktopName = desktopName; + } - // Getters for variables, as usual + // Getters for variables, as usual - public String getDesktopName() { - return desktopName; - } + public String getDesktopName() { + return desktopName; + } - public int getBytesPerPixel() { - return bytesPerPixel; - } + public int getBytesPerPixel() { + return bytesPerPixel; + } - public int getFramebufferHeight() { - return framebufferHeight; - } + public int getFramebufferHeight() { + return framebufferHeight; + } - public int getFramebufferWidth() { - return framebufferWidth; - } + public int getFramebufferWidth() { + return framebufferWidth; + } - public boolean isRGB888_32_LE() { - return rgb888_32_le; - } + public boolean isRGB888_32_LE() { + return rgb888_32_le; + } } diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/VncServerPacketReceiver.java b/console-proxy/src/com/cloud/consoleproxy/vnc/VncServerPacketReceiver.java index 06a4f0332e3..57c8ff8efe2 100644 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/VncServerPacketReceiver.java +++ b/console-proxy/src/com/cloud/consoleproxy/vnc/VncServerPacketReceiver.java @@ -27,97 +27,97 @@ import com.cloud.consoleproxy.vnc.packet.server.FramebufferUpdatePacket; import com.cloud.consoleproxy.vnc.packet.server.ServerCutText; public class VncServerPacketReceiver implements Runnable { - private static final Logger s_logger = Logger.getLogger(VncServerPacketReceiver.class); + private static final Logger s_logger = Logger.getLogger(VncServerPacketReceiver.class); - private final VncScreenDescription screen; - private BufferedImageCanvas canvas; - private DataInputStream is; + private final VncScreenDescription screen; + private BufferedImageCanvas canvas; + private DataInputStream is; - private boolean connectionAlive = true; - private VncClient vncConnection; - private final FrameBufferUpdateListener fburListener; - private final ConsoleProxyClientListener clientListener; + private boolean connectionAlive = true; + private VncClient vncConnection; + private final FrameBufferUpdateListener fburListener; + private final ConsoleProxyClientListener clientListener; - public VncServerPacketReceiver(DataInputStream is, BufferedImageCanvas canvas, VncScreenDescription screen, VncClient vncConnection, - FrameBufferUpdateListener fburListener, ConsoleProxyClientListener clientListener) { - this.screen = screen; - this.canvas = canvas; - this.is = is; - this.vncConnection = vncConnection; - this.fburListener = fburListener; - this.clientListener = clientListener; - } - - public BufferedImageCanvas getCanvas() { - return canvas; - } - - @Override - public void run() { - try { - while (connectionAlive) { - - // Read server message type - int messageType = is.readUnsignedByte(); - - // Invoke packet handler by packet type. - switch (messageType) { - - case RfbConstants.SERVER_FRAMEBUFFER_UPDATE: { - // Notify sender that frame buffer update is received, - // so it can send another frame buffer update request - fburListener.frameBufferPacketReceived(); - // Handle frame buffer update - new FramebufferUpdatePacket(canvas, screen, is, clientListener); - break; - } - - case RfbConstants.SERVER_BELL: { - serverBell(); - break; - } - - case RfbConstants.SERVER_CUT_TEXT: { - serverCutText(is); - break; - } - - default: - throw new RuntimeException("Unknown server packet type: " + messageType + "."); - } - } - } catch (Throwable e) { - s_logger.error("Unexpected exception: ", e); - if (connectionAlive) { - closeConnection(); - vncConnection.shutdown(); - } + public VncServerPacketReceiver(DataInputStream is, BufferedImageCanvas canvas, VncScreenDescription screen, VncClient vncConnection, FrameBufferUpdateListener fburListener, + ConsoleProxyClientListener clientListener) { + this.screen = screen; + this.canvas = canvas; + this.is = is; + this.vncConnection = vncConnection; + this.fburListener = fburListener; + this.clientListener = clientListener; } - } - public void closeConnection() { - connectionAlive = false; - } - - public boolean isConnectionAlive() { - return connectionAlive; - } + public BufferedImageCanvas getCanvas() { + return canvas; + } - /** - * Handle server bell packet. - */ - private void serverBell() { - Toolkit.getDefaultToolkit().beep(); - } + @Override + public void run() { + try { + while (connectionAlive) { - /** - * Handle packet with server clip-board. - */ - private void serverCutText(DataInputStream is) throws IOException { - ServerCutText clipboardContent = new ServerCutText(is); - StringSelection contents = new StringSelection(clipboardContent.getContent()); - Toolkit.getDefaultToolkit().getSystemClipboard().setContents(contents, null); - - s_logger.info("Server clipboard buffer: "+clipboardContent.getContent()); - } + // Read server message type + int messageType = is.readUnsignedByte(); + + // Invoke packet handler by packet type. + switch (messageType) { + + case RfbConstants.SERVER_FRAMEBUFFER_UPDATE: { + // Notify sender that frame buffer update is received, + // so it can send another frame buffer update request + fburListener.frameBufferPacketReceived(); + // Handle frame buffer update + new FramebufferUpdatePacket(canvas, screen, is, clientListener); + break; + } + + case RfbConstants.SERVER_BELL: { + serverBell(); + break; + } + + case RfbConstants.SERVER_CUT_TEXT: { + serverCutText(is); + break; + } + + default: + throw new RuntimeException("Unknown server packet type: " + messageType + "."); + } + } + } catch (Throwable e) { + s_logger.error("Unexpected exception: ", e); + if (connectionAlive) { + closeConnection(); + vncConnection.shutdown(); + } + } + } + + public void closeConnection() { + connectionAlive = false; + } + + public boolean isConnectionAlive() { + return connectionAlive; + } + + /** + * Handle server bell packet. + */ + private void serverBell() { + Toolkit.getDefaultToolkit().beep(); + } + + /** + * Handle packet with server clip-board. + */ + private void serverCutText(DataInputStream is) throws IOException { + ServerCutText clipboardContent = new ServerCutText(is); + StringSelection contents = new StringSelection(clipboardContent.getContent()); + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(contents, null); + + s_logger.info("Server clipboard buffer: " + clipboardContent.getContent()); + } } diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/ClientPacket.java b/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/ClientPacket.java index 10e7c021cb1..873b8c0825e 100644 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/ClientPacket.java +++ b/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/ClientPacket.java @@ -21,6 +21,6 @@ import java.io.IOException; public interface ClientPacket { - void write(DataOutputStream os) throws IOException; + void write(DataOutputStream os) throws IOException; } diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/FramebufferUpdateRequestPacket.java b/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/FramebufferUpdateRequestPacket.java index b5e87a88f57..d3a6e40e961 100644 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/FramebufferUpdateRequestPacket.java +++ b/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/FramebufferUpdateRequestPacket.java @@ -28,27 +28,26 @@ import com.cloud.consoleproxy.vnc.RfbConstants; */ public class FramebufferUpdateRequestPacket implements ClientPacket { - private final int incremental; - private final int x, y, width, height; + private final int incremental; + private final int x, y, width, height; - public FramebufferUpdateRequestPacket(int incremental, int x, int y, int width, int height) { - this.incremental = incremental; - this.x = x; - this.y = y; - this.width = width; - this.height = height; - } + public FramebufferUpdateRequestPacket(int incremental, int x, int y, int width, int height) { + this.incremental = incremental; + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + @Override + public void write(DataOutputStream os) throws IOException { + os.writeByte(RfbConstants.CLIENT_FRAMEBUFFER_UPDATE_REQUEST); - @Override - public void write(DataOutputStream os) throws IOException { - os.writeByte(RfbConstants.CLIENT_FRAMEBUFFER_UPDATE_REQUEST); - - os.writeByte(incremental); - os.writeShort(x); - os.writeShort(y); - os.writeShort(width); - os.writeShort(height); - } + os.writeByte(incremental); + os.writeShort(x); + os.writeShort(y); + os.writeShort(width); + os.writeShort(height); + } } diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/KeyboardEventPacket.java b/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/KeyboardEventPacket.java index 335fee6b176..8efbab123a5 100644 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/KeyboardEventPacket.java +++ b/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/KeyboardEventPacket.java @@ -23,20 +23,20 @@ import com.cloud.consoleproxy.vnc.RfbConstants; public class KeyboardEventPacket implements ClientPacket { - private final int downFlag, key; - - public KeyboardEventPacket(int downFlag, int key) { - this.downFlag = downFlag; - this.key = key; - } + private final int downFlag, key; - @Override - public void write(DataOutputStream os) throws IOException { - os.writeByte(RfbConstants.CLIENT_KEYBOARD_EVENT); + public KeyboardEventPacket(int downFlag, int key) { + this.downFlag = downFlag; + this.key = key; + } - os.writeByte(downFlag); - os.writeShort(0); // padding - os.writeInt(key); - } + @Override + public void write(DataOutputStream os) throws IOException { + os.writeByte(RfbConstants.CLIENT_KEYBOARD_EVENT); + + os.writeByte(downFlag); + os.writeShort(0); // padding + os.writeInt(key); + } } diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/MouseEventPacket.java b/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/MouseEventPacket.java index 4b799f6f2f7..b42191cbadc 100644 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/MouseEventPacket.java +++ b/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/MouseEventPacket.java @@ -23,21 +23,21 @@ import com.cloud.consoleproxy.vnc.RfbConstants; public class MouseEventPacket implements ClientPacket { - private final int buttonMask, x, y; + private final int buttonMask, x, y; - public MouseEventPacket(int buttonMask, int x, int y) { - this.buttonMask = buttonMask; - this.x = x; - this.y = y; - } + public MouseEventPacket(int buttonMask, int x, int y) { + this.buttonMask = buttonMask; + this.x = x; + this.y = y; + } - @Override - public void write(DataOutputStream os) throws IOException { - os.writeByte(RfbConstants.CLIENT_POINTER_EVENT); + @Override + public void write(DataOutputStream os) throws IOException { + os.writeByte(RfbConstants.CLIENT_POINTER_EVENT); - os.writeByte(buttonMask); - os.writeShort(x); - os.writeShort(y); - } + os.writeByte(buttonMask); + os.writeShort(x); + os.writeShort(y); + } } diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/SetEncodingsPacket.java b/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/SetEncodingsPacket.java index f34576f23f0..3d8cfcb09a6 100644 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/SetEncodingsPacket.java +++ b/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/SetEncodingsPacket.java @@ -23,26 +23,23 @@ import com.cloud.consoleproxy.vnc.RfbConstants; public class SetEncodingsPacket implements ClientPacket { - private final int[] encodings; + private final int[] encodings; - public SetEncodingsPacket(int[] encodings) - { - this.encodings = encodings; - } - - @Override - public void write(DataOutputStream os) throws IOException - { - os.writeByte(RfbConstants.CLIENT_SET_ENCODINGS); - - os.writeByte(0);//padding - - os.writeShort(encodings.length); - - for(int i=0;i -tileMap = [ ${tileSequence} ]; -<#if resized == true> - ajaxViewer.resize('main_panel', ${width}, ${height}, ${tileWidth}, ${tileHeight}); - -ajaxViewer.refresh('${imgUrl}', tileMap, false); - +<#-- +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you 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. +--> +tileMap = [ ${tileSequence} ]; +<#if resized == true> + ajaxViewer.resize('main_panel', ${width}, ${height}, ${tileWidth}, ${tileHeight}); + +ajaxViewer.refresh('${imgUrl}', tileMap, false); +