From 41a52acef812eb13523cbf28b5d630baa38d3c26 Mon Sep 17 00:00:00 2001 From: Cyd Date: Wed, 1 Jul 2026 10:42:42 -0500 Subject: [PATCH] Deconstruct VncThumbnailViewer 1.4.2 and add self-contained build The shipped VncThumbnailViewerWin1.4.2.exe is a native launcher stub with a Java JAR appended. This commit adds: - src/: full Java source recovered from the bundled .class files (CFR), plus src/summary.txt with decompiler caveats - ANALYSIS.md: architecture, data flow, hosts-file format, and security notes - build-app-image.ps1: reproducible jpackage build that repackages the original classes and bundles a slim jlinked runtime (java.base + java.desktop) into a self-contained native app-image (no Java required on the target) The 74 MB app-image is shipped as a release asset rather than tracked. Co-Authored-By: Claude Opus 4.8 --- .gitignore | 4 + ANALYSIS.md | 88 ++ VncThumbnailViewerWin1.4.2.exe | Bin 0 -> 201858 bytes build-app-image.ps1 | 38 + src/AddHostDialog.java | 141 ++ src/AuthPanel.java | 76 + src/ButtonPanel.java | 107 ++ src/CapabilityInfo.java | 54 + src/CapsContainer.java | 73 + src/ClipboardFrame.java | 95 ++ src/DesCipher.java | 264 ++++ src/DiffieHellman.java | 146 ++ src/HTTPConnectSocket.java | 25 + src/HTTPConnectSocketFactory.java | 53 + src/HostsFilePasswordDialog.java | 99 ++ src/InStream.java | 148 ++ src/MemInStream.java | 20 + src/OptionsFrame.java | 267 ++++ src/RecordingFrame.java | 231 +++ src/ReloginPanel.java | 39 + src/RfbProto.java | 1027 +++++++++++++ src/SessionRecorder.java | 126 ++ src/SocketFactory.java | 13 + src/VncCanvas.java | 1352 +++++++++++++++++ src/VncCanvas2.java | 40 + src/VncThumbnailViewer.java | 368 +++++ src/VncViewer.java | 787 ++++++++++ src/VncViewersList.java | 315 ++++ src/ZlibInStream.java | 97 ++ src/net/n3/nanoxml/CDATAReader.java | 91 ++ src/net/n3/nanoxml/ContentReader.java | 111 ++ src/net/n3/nanoxml/IXMLBuilder.java | 28 + src/net/n3/nanoxml/IXMLElement.java | 112 ++ src/net/n3/nanoxml/IXMLEntityResolver.java | 24 + src/net/n3/nanoxml/IXMLParser.java | 39 + src/net/n3/nanoxml/IXMLReader.java | 41 + src/net/n3/nanoxml/IXMLValidator.java | 32 + src/net/n3/nanoxml/NonValidator.java | 354 +++++ src/net/n3/nanoxml/PIReader.java | 65 + src/net/n3/nanoxml/StdXMLBuilder.java | 126 ++ src/net/n3/nanoxml/StdXMLParser.java | 368 +++++ src/net/n3/nanoxml/StdXMLReader.java | 315 ++++ src/net/n3/nanoxml/ValidatorPlugin.java | 102 ++ src/net/n3/nanoxml/XMLAttribute.java | 48 + src/net/n3/nanoxml/XMLElement.java | 460 ++++++ src/net/n3/nanoxml/XMLEntityResolver.java | 76 + src/net/n3/nanoxml/XMLException.java | 108 ++ src/net/n3/nanoxml/XMLParseException.java | 22 + src/net/n3/nanoxml/XMLParserFactory.java | 45 + src/net/n3/nanoxml/XMLUtil.java | 259 ++++ .../n3/nanoxml/XMLValidationException.java | 53 + src/net/n3/nanoxml/XMLWriter.java | 173 +++ src/summary.txt | 11 + 53 files changed, 9156 insertions(+) create mode 100644 .gitignore create mode 100644 ANALYSIS.md create mode 100644 VncThumbnailViewerWin1.4.2.exe create mode 100644 build-app-image.ps1 create mode 100644 src/AddHostDialog.java create mode 100644 src/AuthPanel.java create mode 100644 src/ButtonPanel.java create mode 100644 src/CapabilityInfo.java create mode 100644 src/CapsContainer.java create mode 100644 src/ClipboardFrame.java create mode 100644 src/DesCipher.java create mode 100644 src/DiffieHellman.java create mode 100644 src/HTTPConnectSocket.java create mode 100644 src/HTTPConnectSocketFactory.java create mode 100644 src/HostsFilePasswordDialog.java create mode 100644 src/InStream.java create mode 100644 src/MemInStream.java create mode 100644 src/OptionsFrame.java create mode 100644 src/RecordingFrame.java create mode 100644 src/ReloginPanel.java create mode 100644 src/RfbProto.java create mode 100644 src/SessionRecorder.java create mode 100644 src/SocketFactory.java create mode 100644 src/VncCanvas.java create mode 100644 src/VncCanvas2.java create mode 100644 src/VncThumbnailViewer.java create mode 100644 src/VncViewer.java create mode 100644 src/VncViewersList.java create mode 100644 src/ZlibInStream.java create mode 100644 src/net/n3/nanoxml/CDATAReader.java create mode 100644 src/net/n3/nanoxml/ContentReader.java create mode 100644 src/net/n3/nanoxml/IXMLBuilder.java create mode 100644 src/net/n3/nanoxml/IXMLElement.java create mode 100644 src/net/n3/nanoxml/IXMLEntityResolver.java create mode 100644 src/net/n3/nanoxml/IXMLParser.java create mode 100644 src/net/n3/nanoxml/IXMLReader.java create mode 100644 src/net/n3/nanoxml/IXMLValidator.java create mode 100644 src/net/n3/nanoxml/NonValidator.java create mode 100644 src/net/n3/nanoxml/PIReader.java create mode 100644 src/net/n3/nanoxml/StdXMLBuilder.java create mode 100644 src/net/n3/nanoxml/StdXMLParser.java create mode 100644 src/net/n3/nanoxml/StdXMLReader.java create mode 100644 src/net/n3/nanoxml/ValidatorPlugin.java create mode 100644 src/net/n3/nanoxml/XMLAttribute.java create mode 100644 src/net/n3/nanoxml/XMLElement.java create mode 100644 src/net/n3/nanoxml/XMLEntityResolver.java create mode 100644 src/net/n3/nanoxml/XMLException.java create mode 100644 src/net/n3/nanoxml/XMLParseException.java create mode 100644 src/net/n3/nanoxml/XMLParserFactory.java create mode 100644 src/net/n3/nanoxml/XMLUtil.java create mode 100644 src/net/n3/nanoxml/XMLValidationException.java create mode 100644 src/net/n3/nanoxml/XMLWriter.java create mode 100644 src/summary.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5926745 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +# Build output (self-contained app-image is shipped as a release asset, not tracked) +/dist/ +/build-tmp/ +*.zip diff --git a/ANALYSIS.md b/ANALYSIS.md new file mode 100644 index 0000000..5c5f319 --- /dev/null +++ b/ANALYSIS.md @@ -0,0 +1,88 @@ +# VncThumbnailViewer 1.4.2 — Deconstruction + +## What it is +A Windows executable (`VncThumbnailViewerWin1.4.2.exe`) that is really a **self-running +Java application**: a native launcher stub with a JAR (ZIP) appended to it. It is a +multi-host **VNC viewer** that shows many remote desktops as a live grid of thumbnails, +and lets you double-click one to open it full size ("solo"). It is derived from the +**TightVNC Java viewer** (2008 vintage), with a custom thumbnail/grid front-end bolted on. + +- Built: 2008-05-19, `Created-By: 1.5.0_13 (Apple Computer)` — targets Java 1.5, AWT UI. +- `Main-Class: VncThumbnailViewer` +- Window title in code: **"DJC Thumbnail Viewer"**. + +## How it was packaged +`file` reports *"Zip archive, with extra data prepended"*. The `.exe` is a launcher stub +followed by a standard JAR. Extracting the ZIP yields 52 entries: the app classes (default +package), plus the bundled `net.n3.nanoxml` XML library. No source shipped — only `.class` +bytecode, which was decompiled with CFR into [src/](src/). + +## Component map (decompiled sources in `src/`) + +### Thumbnail front-end (the custom part) +- **VncThumbnailViewer** — `main()` + top-level `Frame`. Parses CLI args + (`host/port/password/username/encpassword`, repeatable), builds a `GridLayout` whose row + count is `sqrt(n)+1`, rescales all canvases to fit their cell, and manages the "solo" + full-screen frame (double-click a thumbnail → `soloHost`). Also owns the File menu + (Add Host / Load / Save / Exit). +- **VncViewersList** (`extends Vector`) — collection of live viewers + host-list + persistence to/from **XML** via nanoxml. Handles per-connection security types and + optional encryption of saved credentials. `launchViewer(...)` constructs each `VncViewer` + in embedded (non-applet, view-only, auto-scaled) mode. +- **AddHostDialog** — modal add-host dialog; auth choice of none / Password / + *VNC Enc. Password* / MS-Logon. Contains `readEncPassword()`. + +### VNC engine (inherited from TightVNC viewer) +- **VncViewer** — the viewer applet/app: connection setup, handshake, options. +- **RfbProto** (largest, ~1k LOC) — the RFB/VNC wire protocol: handshake, security + negotiation, framebuffer updates, encodings, session recording hooks. +- **VncCanvas** / **VncCanvas2** — draws the framebuffer, decodes encodings (Raw, CopyRect, + RRE, Hextile, ZRLE/Zlib, Tight), handles scaling and input. +- **OptionsFrame**, **ButtonPanel**, **AuthPanel**, **ClipboardFrame**, **ReloginPanel** — UI. +- **RecordingFrame** / **SessionRecorder** — record a session to an `.fbs` file. +- **InStream / MemInStream / ZlibInStream**, **CapabilityInfo / CapsContainer** — protocol + I/O and TightVNC capability negotiation. +- **DesCipher**, **DiffieHellman** — crypto (see below). +- **HTTPConnectSocket(Factory)**, **SocketFactory** — proxy/socket plumbing. +- **net.n3.nanoxml.\*** — third-party XML parser/writer for the hosts file. + +## Data / control flow +1. `main()` (or the Add-Host dialog, or "Load list of hosts") produces `(host, port, + password, username, userdomain)` tuples. +2. `VncViewersList.launchViewer` spins up a `VncViewer` per host, embedded in the grid, + `viewOnly=true`, `autoScale=true`, `scalingFactor=10`. +3. Each `VncViewer`/`RfbProto` connects, authenticates, and streams framebuffer updates + into its `VncCanvas`, which is scaled down to fit its thumbnail cell. +4. Double-click → `soloHost` moves that canvas to a full-screen frame and re-enables input. + +## Hosts file (persistence) format +XML `` containing `` elements with +`Host, Port, SecType, Password, Username`. `SecType`: `1`=none, `2`=VncAuth, +`-6`=MS-Logon; several TightVNC sec-types (5/6/16-19) are recognized but rejected as +"incomplete". If `Encrypted="1"`, the credential fields are DES-encrypted under a +user-supplied passphrase (`HostsFilePasswordDialog`). + +## Security notes (this is a 2008 codebase — expect weak crypto) +- **"VNC Enc. Password"** (`AddHostDialog.readEncPassword`) is the classic VNC password + obfuscation, **not real security**: it DES-*decrypts* the 16-hex-char stored value with + the well-known fixed key `{0x17,0x52,0x6B,0x06,0x23,0x4E,0x58,0x07}`. Anyone with this + key (it's public and right here in the binary) can recover the plaintext password. This + is the same fixed key TightVNC/RealVNC use for `.vnc` files. +- Encrypted hosts files use single **DES** (56-bit, broken) keyed off the user passphrase — + weak by modern standards. +- VncAuth itself is DES challenge-response, inherently weak. +- Passwords are held in plaintext `String`s in memory and passed on the command line + (visible in process listings). Treat any stored credentials as effectively public. + +## To rebuild / run from source +Needs a JDK 1.5–1.8 (uses removed AWT calls like `Component.enable(...)`, `Dialog.show()` +— will warn/fail on modern JDKs without `--release 8`-style tweaks). Roughly: +``` +javac -d out src/*.java src/net/n3/nanoxml/*.java +java -cp out VncThumbnailViewer host port 5900 +``` +Or just run the original: `java -cp VncThumbnailViewerWin1.4.2.exe VncThumbnailViewer` (the +exe is a valid JAR despite the prepended stub, though the stub also lets Windows run it +directly). +``` +``` diff --git a/VncThumbnailViewerWin1.4.2.exe b/VncThumbnailViewerWin1.4.2.exe new file mode 100644 index 0000000000000000000000000000000000000000..bfe079c1befd9f8287ff887d34efc09165a2778e GIT binary patch literal 201858 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 P0k2{phHflH0p9%|QuETp literal 0 HcmV?d00001 diff --git a/build-app-image.ps1 b/build-app-image.ps1 new file mode 100644 index 0000000..c215635 --- /dev/null +++ b/build-app-image.ps1 @@ -0,0 +1,38 @@ +# Builds a self-contained native app-image (bundled JRE, no Java required on target). +# Requires a JDK 17+ on PATH (needs jar, jdeps, jpackage). Tested with Temurin 21. +# Reuses the ORIGINAL compiled .class files from the shipped exe (identical behavior). +# +# Usage: powershell -ExecutionPolicy Bypass -File build-app-image.ps1 + +$ErrorActionPreference = "Stop" +$root = $PSScriptRoot +$work = Join-Path $root "build-tmp" +$classes = Join-Path $work "classes" +$dist = Join-Path $root "dist" + +Remove-Item $work, $dist -Recurse -Force -ErrorAction SilentlyContinue +New-Item -ItemType Directory -Force $classes | Out-Null + +# 1. Extract the class files (the .exe is a launcher stub + a ZIP/JAR). +Copy-Item (Join-Path $root "VncThumbnailViewerWin1.4.2.exe") (Join-Path $work "app.zip") +Expand-Archive -Path (Join-Path $work "app.zip") -DestinationPath $classes -Force + +# 2. Repackage into a runnable JAR, preserving the original manifest (Main-Class). +$jar = Join-Path $work "VncThumbnailViewer.jar" +$jarInput = Join-Path $work "jarinput" +New-Item -ItemType Directory -Force $jarInput | Out-Null +& jar --create --file $jar --manifest (Join-Path $classes "META-INF\MANIFEST.MF") -C $classes . + +# 3. Determine the minimal module set for a slim runtime. +$mods = (& jdeps --multi-release 21 --ignore-missing-deps --print-module-deps $jar).Trim() +Write-Host "Modules: $mods" + +# 4. Build the native app-image with a jlinked, bundled runtime. +Move-Item $jar $jarInput +& jpackage --type app-image ` + --name "VncThumbnailViewer" --app-version "1.4.2" --vendor "DJC" ` + --input $jarInput --main-jar "VncThumbnailViewer.jar" --main-class "VncThumbnailViewer" ` + --add-modules $mods --dest $dist + +Remove-Item $work -Recurse -Force -ErrorAction SilentlyContinue +Write-Host "Done -> $dist\VncThumbnailViewer\VncThumbnailViewer.exe" diff --git a/src/AddHostDialog.java b/src/AddHostDialog.java new file mode 100644 index 0000000..80869e9 --- /dev/null +++ b/src/AddHostDialog.java @@ -0,0 +1,141 @@ +/* + * Decompiled with CFR 0.152. + */ +import java.awt.Button; +import java.awt.Choice; +import java.awt.Component; +import java.awt.Dialog; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Frame; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Label; +import java.awt.Point; +import java.awt.TextField; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; + +class AddHostDialog +extends Dialog +implements ActionListener, +ItemListener { + VncThumbnailViewer tnviewer; + TextField hostField; + TextField portField; + TextField usernameField; + TextField passwordField; + Choice authChoice; + Button connectButton; + Button cancelButton; + + static String readEncPassword(String string) { + Object object; + if (string.length() != 16) { + System.out.println("VNC Enc. Passwords must be 16 chars"); + return string; + } + byte[] byArray = new byte[]{0, 0, 0, 0, 0, 0, 0, 0}; + int n = string.length() / 2; + if (n > 8) { + n = 8; + } + for (int i = 0; i < n; ++i) { + object = string.substring(i * 2, i * 2 + 2); + Integer n2 = new Integer(Integer.parseInt((String)object, 16)); + byArray[i] = n2.byteValue(); + } + byte[] byArray2 = new byte[]{23, 82, 107, 6, 35, 78, 88, 7}; + object = new DesCipher(byArray2); + ((DesCipher)object).decrypt(byArray, 0, byArray, 0); + return new String(byArray); + } + + public AddHostDialog(VncThumbnailViewer vncThumbnailViewer) { + super((Frame)vncThumbnailViewer, true); + this.tnviewer = vncThumbnailViewer; + this.setResizable(false); + GridBagLayout gridBagLayout = new GridBagLayout(); + GridBagConstraints gridBagConstraints = new GridBagConstraints(); + this.setLayout(gridBagLayout); + this.setFont(new Font("Helvetica", 0, 14)); + this.hostField = new TextField("", 10); + this.portField = new TextField("5900", 10); + this.usernameField = new TextField("", 10); + this.usernameField.enable(false); + this.passwordField = new TextField("", 10); + this.passwordField.setEchoChar('*'); + this.passwordField.enable(false); + this.authChoice = new Choice(); + this.authChoice.addItemListener(this); + this.authChoice.add("(none)"); + this.authChoice.add("Password"); + this.authChoice.add("VNC Enc. Password"); + this.authChoice.add("MS-Logon"); + this.connectButton = new Button("Connect..."); + this.cancelButton = new Button("Cancel"); + this.connectButton.addActionListener(this); + this.cancelButton.addActionListener(this); + gridBagConstraints.gridwidth = 0; + gridBagLayout.setConstraints(this.authChoice, gridBagConstraints); + gridBagLayout.setConstraints(this.hostField, gridBagConstraints); + gridBagLayout.setConstraints(this.portField, gridBagConstraints); + gridBagLayout.setConstraints(this.usernameField, gridBagConstraints); + gridBagLayout.setConstraints(this.passwordField, gridBagConstraints); + gridBagLayout.setConstraints(this.connectButton, gridBagConstraints); + this.add(new Label("Host", 2)); + this.add(this.hostField); + this.add(new Label("Port", 2)); + this.add(this.portField); + this.add(new Label("Authentication:", 2)); + this.add(this.authChoice); + this.add(new Label("Username", 2)); + this.add(this.usernameField); + this.add(new Label("Password", 2)); + this.add(this.passwordField); + this.add(this.cancelButton); + this.add(this.connectButton); + Point point = vncThumbnailViewer.getLocation(); + Dimension dimension = vncThumbnailViewer.getSize(); + point.x += dimension.width / 2 - 50; + point.y += dimension.height / 2 - 50; + ((Component)this).setLocation(point); + this.pack(); + this.validate(); + ((Component)this).setVisible(true); + } + + void callAddHost() { + String string = this.hostField.getText(); + int n = Integer.parseInt(this.portField.getText()); + String string2 = this.passwordField.getText(); + String string3 = this.usernameField.getText(); + if (this.authChoice.getSelectedItem() == "VNC Enc. Password") { + string2 = AddHostDialog.readEncPassword(string2); + } + this.tnviewer.launchViewer(string, n, string2, string3); + } + + public void actionPerformed(ActionEvent actionEvent) { + if (actionEvent.getSource() == this.connectButton) { + this.callAddHost(); + } + this.dispose(); + } + + public void itemStateChanged(ItemEvent itemEvent) { + if (this.authChoice.getSelectedItem() == "(none)") { + this.passwordField.enable(false); + } else { + this.passwordField.enable(true); + } + if (this.authChoice.getSelectedItem() == "MS-Logon") { + this.usernameField.enable(true); + } else { + this.usernameField.enable(false); + } + } +} + diff --git a/src/AuthPanel.java b/src/AuthPanel.java new file mode 100644 index 0000000..d46a122 --- /dev/null +++ b/src/AuthPanel.java @@ -0,0 +1,76 @@ +/* + * Decompiled with CFR 0.152. + */ +import java.awt.Button; +import java.awt.Color; +import java.awt.Font; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.Label; +import java.awt.Panel; +import java.awt.TextField; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +class AuthPanel +extends Panel +implements ActionListener { + TextField passwordField; + Button okButton; + + public AuthPanel(VncViewer vncViewer) { + Label label = new Label("VNC Authentication", 1); + label.setFont(new Font("Helvetica", 1, 18)); + Label label2 = new Label("Password:", 1); + this.passwordField = new TextField(10); + this.passwordField.setForeground(Color.black); + this.passwordField.setBackground(Color.white); + this.passwordField.setEchoChar('*'); + this.okButton = new Button("OK"); + GridBagLayout gridBagLayout = new GridBagLayout(); + GridBagConstraints gridBagConstraints = new GridBagConstraints(); + this.setLayout(gridBagLayout); + gridBagConstraints.gridwidth = 0; + gridBagConstraints.insets = new Insets(0, 0, 20, 0); + gridBagLayout.setConstraints(label, gridBagConstraints); + this.add(label); + gridBagConstraints.fill = 0; + gridBagConstraints.gridwidth = 1; + gridBagConstraints.insets = new Insets(0, 0, 0, 0); + gridBagLayout.setConstraints(label2, gridBagConstraints); + this.add(label2); + gridBagLayout.setConstraints(this.passwordField, gridBagConstraints); + this.add(this.passwordField); + this.passwordField.addActionListener(this); + gridBagConstraints.gridwidth = 0; + gridBagConstraints.fill = 1; + gridBagConstraints.insets = new Insets(0, 20, 0, 0); + gridBagConstraints.ipadx = 30; + gridBagLayout.setConstraints(this.okButton, gridBagConstraints); + this.add(this.okButton); + this.okButton.addActionListener(this); + } + + public void moveFocusToDefaultField() { + this.passwordField.requestFocus(); + } + + public synchronized void actionPerformed(ActionEvent actionEvent) { + if (actionEvent.getSource() == this.passwordField || actionEvent.getSource() == this.okButton) { + this.passwordField.setEnabled(false); + this.notify(); + } + } + + public synchronized String getPassword() throws Exception { + try { + this.wait(); + } + catch (InterruptedException interruptedException) { + // empty catch block + } + return this.passwordField.getText(); + } +} + diff --git a/src/ButtonPanel.java b/src/ButtonPanel.java new file mode 100644 index 0000000..4c55d0b --- /dev/null +++ b/src/ButtonPanel.java @@ -0,0 +1,107 @@ +/* + * Decompiled with CFR 0.152. + */ +import java.awt.Button; +import java.awt.Component; +import java.awt.FlowLayout; +import java.awt.Panel; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.io.IOException; + +class ButtonPanel +extends Panel +implements ActionListener { + VncViewer viewer; + Button disconnectButton; + Button optionsButton; + Button recordButton; + Button clipboardButton; + Button ctrlAltDelButton; + Button refreshButton; + + ButtonPanel(VncViewer vncViewer) { + this.viewer = vncViewer; + this.setLayout(new FlowLayout(0, 0, 0)); + this.disconnectButton = new Button("Disconnect"); + this.disconnectButton.setEnabled(false); + this.add(this.disconnectButton); + this.disconnectButton.addActionListener(this); + this.optionsButton = new Button("Options"); + this.add(this.optionsButton); + this.optionsButton.addActionListener(this); + this.clipboardButton = new Button("Clipboard"); + this.clipboardButton.setEnabled(false); + this.add(this.clipboardButton); + this.clipboardButton.addActionListener(this); + if (this.viewer.rec != null) { + this.recordButton = new Button("Record"); + this.add(this.recordButton); + this.recordButton.addActionListener(this); + } + this.ctrlAltDelButton = new Button("Send Ctrl-Alt-Del"); + this.ctrlAltDelButton.setEnabled(false); + this.add(this.ctrlAltDelButton); + this.ctrlAltDelButton.addActionListener(this); + this.refreshButton = new Button("Refresh"); + this.refreshButton.setEnabled(false); + this.add(this.refreshButton); + this.refreshButton.addActionListener(this); + } + + public void enableButtons() { + this.disconnectButton.setEnabled(true); + this.clipboardButton.setEnabled(true); + this.refreshButton.setEnabled(true); + } + + public void disableButtonsOnDisconnect() { + this.remove(this.disconnectButton); + this.disconnectButton = new Button("Hide desktop"); + this.disconnectButton.setEnabled(true); + this.add((Component)this.disconnectButton, 0); + this.disconnectButton.addActionListener(this); + this.optionsButton.setEnabled(false); + this.clipboardButton.setEnabled(false); + this.ctrlAltDelButton.setEnabled(false); + this.refreshButton.setEnabled(false); + this.validate(); + } + + public void enableRemoteAccessControls(boolean bl) { + this.ctrlAltDelButton.setEnabled(bl); + } + + public void actionPerformed(ActionEvent actionEvent) { + this.viewer.moveFocusToDesktop(); + if (actionEvent.getSource() == this.disconnectButton) { + this.viewer.disconnect(); + } else if (actionEvent.getSource() == this.optionsButton) { + ((Component)this.viewer.options).setVisible(!this.viewer.options.isVisible()); + } else if (actionEvent.getSource() == this.recordButton) { + ((Component)this.viewer.rec).setVisible(!this.viewer.rec.isVisible()); + } else if (actionEvent.getSource() == this.clipboardButton) { + ((Component)this.viewer.clipboard).setVisible(!this.viewer.clipboard.isVisible()); + } else if (actionEvent.getSource() == this.ctrlAltDelButton) { + try { + KeyEvent keyEvent = new KeyEvent(this, 401, 0L, 10, 127); + this.viewer.rfb.writeKeyEvent(keyEvent); + keyEvent = new KeyEvent(this, 402, 0L, 10, 127); + this.viewer.rfb.writeKeyEvent(keyEvent); + } + catch (IOException iOException) { + iOException.printStackTrace(); + } + } else if (actionEvent.getSource() == this.refreshButton) { + try { + RfbProto rfbProto = this.viewer.rfb; + rfbProto.writeFramebufferUpdateRequest(0, 0, rfbProto.framebufferWidth, rfbProto.framebufferHeight, false); + } + catch (IOException iOException) { + iOException.printStackTrace(); + } + } + } +} + diff --git a/src/CapabilityInfo.java b/src/CapabilityInfo.java new file mode 100644 index 0000000..685746c --- /dev/null +++ b/src/CapabilityInfo.java @@ -0,0 +1,54 @@ +/* + * Decompiled with CFR 0.152. + */ +class CapabilityInfo { + protected int code; + protected String vendorSignature; + protected String nameSignature; + protected String description; + protected boolean enabled; + + public CapabilityInfo(int n, String string, String string2, String string3) { + this.code = n; + this.vendorSignature = string; + this.nameSignature = string2; + this.description = string3; + this.enabled = false; + } + + public CapabilityInfo(int n, byte[] byArray, byte[] byArray2) { + this.code = n; + this.vendorSignature = new String(byArray); + this.nameSignature = new String(byArray2); + this.description = null; + this.enabled = false; + } + + public int getCode() { + return this.code; + } + + public String getDescription() { + return this.description; + } + + public boolean isEnabled() { + return this.enabled; + } + + public void enable() { + this.enabled = true; + } + + public boolean equals(CapabilityInfo capabilityInfo) { + return capabilityInfo != null && this.code == capabilityInfo.code && this.vendorSignature.equals(capabilityInfo.vendorSignature) && this.nameSignature.equals(capabilityInfo.nameSignature); + } + + public boolean enableIfEquals(CapabilityInfo capabilityInfo) { + if (this.equals(capabilityInfo)) { + this.enable(); + } + return this.isEnabled(); + } +} + diff --git a/src/CapsContainer.java b/src/CapsContainer.java new file mode 100644 index 0000000..0479323 --- /dev/null +++ b/src/CapsContainer.java @@ -0,0 +1,73 @@ +/* + * Decompiled with CFR 0.152. + */ +import java.util.Hashtable; +import java.util.Vector; + +class CapsContainer { + protected Hashtable infoMap = new Hashtable(64, 0.25f); + protected Vector orderedList = new Vector(32, 8); + + public void add(CapabilityInfo capabilityInfo) { + Integer n = new Integer(capabilityInfo.getCode()); + this.infoMap.put(n, capabilityInfo); + } + + public void add(int n, String string, String string2, String string3) { + Integer n2 = new Integer(n); + this.infoMap.put(n2, new CapabilityInfo(n, string, string2, string3)); + } + + public boolean isKnown(int n) { + return this.infoMap.containsKey(new Integer(n)); + } + + public CapabilityInfo getInfo(int n) { + return (CapabilityInfo)this.infoMap.get(new Integer(n)); + } + + public String getDescription(int n) { + CapabilityInfo capabilityInfo = (CapabilityInfo)this.infoMap.get(new Integer(n)); + if (capabilityInfo == null) { + return null; + } + return capabilityInfo.getDescription(); + } + + public boolean enable(CapabilityInfo capabilityInfo) { + Integer n = new Integer(capabilityInfo.getCode()); + CapabilityInfo capabilityInfo2 = (CapabilityInfo)this.infoMap.get(n); + if (capabilityInfo2 == null) { + return false; + } + boolean bl = capabilityInfo2.enableIfEquals(capabilityInfo); + if (bl) { + this.orderedList.addElement(n); + } + return bl; + } + + public boolean isEnabled(int n) { + CapabilityInfo capabilityInfo = (CapabilityInfo)this.infoMap.get(new Integer(n)); + if (capabilityInfo == null) { + return false; + } + return capabilityInfo.isEnabled(); + } + + public int numEnabled() { + return this.orderedList.size(); + } + + public int getByOrder(int n) { + int n2; + try { + n2 = (Integer)this.orderedList.elementAt(n); + } + catch (ArrayIndexOutOfBoundsException arrayIndexOutOfBoundsException) { + n2 = 0; + } + return n2; + } +} + diff --git a/src/ClipboardFrame.java b/src/ClipboardFrame.java new file mode 100644 index 0000000..95f9dbf --- /dev/null +++ b/src/ClipboardFrame.java @@ -0,0 +1,95 @@ +/* + * Decompiled with CFR 0.152. + */ +import java.awt.Button; +import java.awt.Component; +import java.awt.Frame; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.TextArea; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; + +class ClipboardFrame +extends Frame +implements WindowListener, +ActionListener { + TextArea textArea; + Button clearButton; + Button closeButton; + String selection; + VncViewer viewer; + + ClipboardFrame(VncViewer vncViewer) { + super("TightVNC Clipboard"); + this.viewer = vncViewer; + GridBagLayout gridBagLayout = new GridBagLayout(); + this.setLayout(gridBagLayout); + GridBagConstraints gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridwidth = 0; + gridBagConstraints.fill = 1; + gridBagConstraints.weighty = 1.0; + this.textArea = new TextArea(5, 40); + gridBagLayout.setConstraints(this.textArea, gridBagConstraints); + this.add(this.textArea); + gridBagConstraints.fill = 2; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 0.0; + gridBagConstraints.gridwidth = 1; + this.clearButton = new Button("Clear"); + gridBagLayout.setConstraints(this.clearButton, gridBagConstraints); + this.add(this.clearButton); + this.clearButton.addActionListener(this); + this.closeButton = new Button("Close"); + gridBagLayout.setConstraints(this.closeButton, gridBagConstraints); + this.add(this.closeButton); + this.closeButton.addActionListener(this); + this.pack(); + this.addWindowListener(this); + } + + void setCutText(String string) { + this.selection = string; + this.textArea.setText(string); + if (this.isVisible()) { + this.textArea.selectAll(); + } + } + + public void windowDeactivated(WindowEvent windowEvent) { + if (this.selection != null && !this.selection.equals(this.textArea.getText())) { + this.selection = this.textArea.getText(); + this.viewer.setCutText(this.selection); + } + } + + public void windowClosing(WindowEvent windowEvent) { + ((Component)this).setVisible(false); + } + + public void windowActivated(WindowEvent windowEvent) { + } + + public void windowOpened(WindowEvent windowEvent) { + } + + public void windowClosed(WindowEvent windowEvent) { + } + + public void windowIconified(WindowEvent windowEvent) { + } + + public void windowDeiconified(WindowEvent windowEvent) { + } + + public void actionPerformed(ActionEvent actionEvent) { + if (actionEvent.getSource() == this.clearButton) { + this.textArea.setText(""); + } else if (actionEvent.getSource() == this.closeButton) { + ((Component)this).setVisible(false); + } + } +} + diff --git a/src/DesCipher.java b/src/DesCipher.java new file mode 100644 index 0000000..b08bb35 --- /dev/null +++ b/src/DesCipher.java @@ -0,0 +1,264 @@ +/* + * Decompiled with CFR 0.152. + */ +public class DesCipher { + private int[] encryptKeys = new int[32]; + private int[] decryptKeys = new int[32]; + private int[] tempInts = new int[2]; + private static byte[] bytebit = new byte[]{1, 2, 4, 8, 16, 32, 64, -128}; + private static int[] bigbyte = new int[]{0x800000, 0x400000, 0x200000, 0x100000, 524288, 262144, 131072, 65536, 32768, 16384, 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1}; + private static byte[] pc1 = new byte[]{56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3}; + private static int[] totrot = new int[]{1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28}; + private static byte[] pc2 = new byte[]{13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9, 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1, 40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47, 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31}; + private static int[] SP1 = new int[]{0x1010400, 0, 65536, 0x1010404, 0x1010004, 66564, 4, 65536, 1024, 0x1010400, 0x1010404, 1024, 0x1000404, 0x1010004, 0x1000000, 4, 1028, 0x1000400, 0x1000400, 66560, 66560, 0x1010000, 0x1010000, 0x1000404, 65540, 0x1000004, 0x1000004, 65540, 0, 1028, 66564, 0x1000000, 65536, 0x1010404, 4, 0x1010000, 0x1010400, 0x1000000, 0x1000000, 1024, 0x1010004, 65536, 66560, 0x1000004, 1024, 4, 0x1000404, 66564, 0x1010404, 65540, 0x1010000, 0x1000404, 0x1000004, 1028, 66564, 0x1010400, 1028, 0x1000400, 0x1000400, 0, 65540, 66560, 0, 0x1010004}; + private static int[] SP2 = new int[]{-2146402272, -2147450880, 32768, 1081376, 0x100000, 32, -2146435040, -2147450848, -2147483616, -2146402272, -2146402304, Integer.MIN_VALUE, -2147450880, 0x100000, 32, -2146435040, 0x108000, 0x100020, -2147450848, 0, Integer.MIN_VALUE, 32768, 1081376, -2146435072, 0x100020, -2147483616, 0, 0x108000, 32800, -2146402304, -2146435072, 32800, 0, 1081376, -2146435040, 0x100000, -2147450848, -2146435072, -2146402304, 32768, -2146435072, -2147450880, 32, -2146402272, 1081376, 32, 32768, Integer.MIN_VALUE, 32800, -2146402304, 0x100000, -2147483616, 0x100020, -2147450848, -2147483616, 0x100020, 0x108000, 0, -2147450880, 32800, Integer.MIN_VALUE, -2146435040, -2146402272, 0x108000}; + private static int[] SP3 = new int[]{520, 0x8020200, 0, 0x8020008, 0x8000200, 0, 131592, 0x8000200, 131080, 0x8000008, 0x8000008, 131072, 0x8020208, 131080, 0x8020000, 520, 0x8000000, 8, 0x8020200, 512, 131584, 0x8020000, 0x8020008, 131592, 0x8000208, 131584, 131072, 0x8000208, 8, 0x8020208, 512, 0x8000000, 0x8020200, 0x8000000, 131080, 520, 131072, 0x8020200, 0x8000200, 0, 512, 131080, 0x8020208, 0x8000200, 0x8000008, 512, 0, 0x8020008, 0x8000208, 131072, 0x8000000, 0x8020208, 8, 131592, 131584, 0x8000008, 0x8020000, 0x8000208, 520, 0x8020000, 131592, 8, 0x8020008, 131584}; + private static int[] SP4 = new int[]{8396801, 8321, 8321, 128, 0x802080, 0x800081, 0x800001, 8193, 0, 0x802000, 0x802000, 8396929, 129, 0, 0x800080, 0x800001, 1, 8192, 0x800000, 8396801, 128, 0x800000, 8193, 8320, 0x800081, 1, 8320, 0x800080, 8192, 0x802080, 8396929, 129, 0x800080, 0x800001, 0x802000, 8396929, 129, 0, 0, 0x802000, 8320, 0x800080, 0x800081, 1, 8396801, 8321, 8321, 128, 8396929, 129, 1, 8192, 0x800001, 8193, 0x802080, 0x800081, 8193, 8320, 0x800000, 8396801, 128, 0x800000, 8192, 0x802080}; + private static int[] SP5 = new int[]{256, 34078976, 0x2080000, 1107296512, 524288, 256, 0x40000000, 0x2080000, 1074266368, 524288, 0x2000100, 1074266368, 1107296512, 1107820544, 524544, 0x40000000, 0x2000000, 0x40080000, 0x40080000, 0, 0x40000100, 1107820800, 1107820800, 0x2000100, 1107820544, 0x40000100, 0, 0x42000000, 34078976, 0x2000000, 0x42000000, 524544, 524288, 1107296512, 256, 0x2000000, 0x40000000, 0x2080000, 1107296512, 1074266368, 0x2000100, 0x40000000, 1107820544, 34078976, 1074266368, 256, 0x2000000, 1107820544, 1107820800, 524544, 0x42000000, 1107820800, 0x2080000, 0, 0x40080000, 0x42000000, 524544, 0x2000100, 0x40000100, 524288, 0, 0x40080000, 34078976, 0x40000100}; + private static int[] SP6 = new int[]{0x20000010, 0x20400000, 16384, 541081616, 0x20400000, 16, 541081616, 0x400000, 0x20004000, 0x404010, 0x400000, 0x20000010, 0x400010, 0x20004000, 0x20000000, 16400, 0, 0x400010, 536887312, 16384, 0x404000, 536887312, 16, 541065232, 541065232, 0, 0x404010, 0x20404000, 16400, 0x404000, 0x20404000, 0x20000000, 0x20004000, 16, 541065232, 0x404000, 541081616, 0x400000, 16400, 0x20000010, 0x400000, 0x20004000, 0x20000000, 16400, 0x20000010, 541081616, 0x404000, 0x20400000, 0x404010, 0x20404000, 0, 541065232, 16, 16384, 0x20400000, 0x404010, 16384, 0x400010, 536887312, 0, 0x20404000, 0x20000000, 0x400010, 536887312}; + private static int[] SP7 = new int[]{0x200000, 0x4200002, 67110914, 0, 2048, 67110914, 0x200802, 69208064, 69208066, 0x200000, 0, 0x4000002, 2, 0x4000000, 0x4200002, 2050, 0x4000800, 0x200802, 0x200002, 0x4000800, 0x4000002, 0x4200000, 69208064, 0x200002, 0x4200000, 2048, 2050, 69208066, 0x200800, 2, 0x4000000, 0x200800, 0x4000000, 0x200800, 0x200000, 67110914, 67110914, 0x4200002, 0x4200002, 2, 0x200002, 0x4000000, 0x4000800, 0x200000, 69208064, 2050, 0x200802, 69208064, 2050, 0x4000002, 69208066, 0x4200000, 0x200800, 0, 2, 69208066, 0, 0x200802, 0x4200000, 2048, 0x4000002, 0x4000800, 2048, 0x200002}; + private static int[] SP8 = new int[]{0x10001040, 4096, 262144, 0x10041040, 0x10000000, 0x10001040, 64, 0x10000000, 262208, 0x10040000, 0x10041040, 266240, 0x10041000, 266304, 4096, 64, 0x10040000, 0x10000040, 0x10001000, 4160, 266240, 262208, 0x10040040, 0x10041000, 4160, 0, 0, 0x10040040, 0x10000040, 0x10001000, 266304, 262144, 266304, 262144, 0x10041000, 4096, 64, 0x10040040, 4096, 266304, 0x10001000, 64, 0x10000040, 0x10040000, 0x10040040, 0x10000000, 262144, 0x10001040, 0, 0x10041040, 262208, 0x10000040, 0x10040000, 0x10001000, 0x10001040, 0, 0x10041040, 266240, 266240, 4160, 4160, 262208, 0x10000000, 0x10041000}; + + public DesCipher(byte[] byArray) { + this.setKey(byArray); + } + + public void setKey(byte[] byArray) { + this.deskey(byArray, true, this.encryptKeys); + this.deskey(byArray, false, this.decryptKeys); + } + + private void deskey(byte[] byArray, boolean bl, int[] nArray) { + int n; + int n2; + int n3; + int[] nArray2 = new int[56]; + int[] nArray3 = new int[56]; + int[] nArray4 = new int[32]; + for (n3 = 0; n3 < 56; ++n3) { + n2 = pc1[n3]; + n = n2 & 7; + nArray2[n3] = (byArray[n2 >>> 3] & bytebit[n]) != 0 ? 1 : 0; + } + for (int i = 0; i < 16; ++i) { + n = bl ? i << 1 : 15 - i << 1; + int n4 = n + 1; + nArray4[n4] = 0; + nArray4[n] = 0; + for (n3 = 0; n3 < 28; ++n3) { + n2 = n3 + totrot[i]; + nArray3[n3] = n2 < 28 ? nArray2[n2] : nArray2[n2 - 28]; + } + for (n3 = 28; n3 < 56; ++n3) { + n2 = n3 + totrot[i]; + nArray3[n3] = n2 < 56 ? nArray2[n2] : nArray2[n2 - 28]; + } + for (n3 = 0; n3 < 24; ++n3) { + if (nArray3[pc2[n3]] != 0) { + int n5 = n; + nArray4[n5] = nArray4[n5] | bigbyte[n3]; + } + if (nArray3[pc2[n3 + 24]] == 0) continue; + int n6 = n4; + nArray4[n6] = nArray4[n6] | bigbyte[n3]; + } + } + this.cookey(nArray4, nArray); + } + + private void cookey(int[] nArray, int[] nArray2) { + int n = 0; + int n2 = 0; + for (int i = 0; i < 16; ++i) { + int n3 = nArray[n++]; + int n4 = nArray[n++]; + nArray2[n2] = (n3 & 0xFC0000) << 6; + int n5 = n2; + nArray2[n5] = nArray2[n5] | (n3 & 0xFC0) << 10; + int n6 = n2; + nArray2[n6] = nArray2[n6] | (n4 & 0xFC0000) >>> 10; + int n7 = n2++; + nArray2[n7] = nArray2[n7] | (n4 & 0xFC0) >>> 6; + nArray2[n2] = (n3 & 0x3F000) << 12; + int n8 = n2; + nArray2[n8] = nArray2[n8] | (n3 & 0x3F) << 16; + int n9 = n2; + nArray2[n9] = nArray2[n9] | (n4 & 0x3F000) >>> 4; + int n10 = n2++; + nArray2[n10] = nArray2[n10] | n4 & 0x3F; + } + } + + public void encrypt(byte[] byArray, int n, byte[] byArray2, int n2) { + DesCipher.squashBytesToInts(byArray, n, this.tempInts, 0, 2); + this.des(this.tempInts, this.tempInts, this.encryptKeys); + DesCipher.spreadIntsToBytes(this.tempInts, 0, byArray2, n2, 2); + } + + public void decrypt(byte[] byArray, int n, byte[] byArray2, int n2) { + DesCipher.squashBytesToInts(byArray, n, this.tempInts, 0, 2); + this.des(this.tempInts, this.tempInts, this.decryptKeys); + DesCipher.spreadIntsToBytes(this.tempInts, 0, byArray2, n2, 2); + } + + public void encryptText(byte[] byArray, byte[] byArray2, byte[] byArray3) { + int n; + for (n = 0; n < 8; ++n) { + int n2 = n; + byArray[n2] = (byte)(byArray[n2] ^ byArray3[n]); + } + this.encrypt(byArray, 0, byArray2, 0); + for (n = 8; n < byArray.length; n += 8) { + for (int i = 0; i < 8; ++i) { + int n3 = n + i; + byArray[n3] = (byte)(byArray[n3] ^ byArray2[n + i - 8]); + } + this.encrypt(byArray, n, byArray2, n); + } + } + + public void decryptText(byte[] byArray, byte[] byArray2, byte[] byArray3) { + int n; + for (n = byArray.length - 8; n > 0; n -= 8) { + this.decrypt(byArray, n, byArray2, n); + for (int i = 0; i < 8; ++i) { + int n2 = n + i; + byArray2[n2] = (byte)(byArray2[n2] ^ byArray[n + i - 8]); + } + } + this.decrypt(byArray, 0, byArray2, 0); + for (n = 0; n < 8; ++n) { + int n3 = n; + byArray2[n3] = (byte)(byArray2[n3] ^ byArray3[n]); + } + } + + private static String bytesToHexString(byte[] byArray) { + String string = ""; + for (int i = 0; i < 8; ++i) { + int n = byArray[i] & 0xFF; + String string2 = Integer.toHexString(n); + if (string2.length() != 2) { + string2 = "0" + string2; + } + string = string + string2; + } + return string; + } + + public static String encryptData(String string, String string2) { + string2 = (string2 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000").substring(0, 8); + DesCipher desCipher = new DesCipher(string2.getBytes()); + String string3 = ""; + String string4 = string; + while (string4.length() != 0) { + for (int i = string4.length(); i < 8; ++i) { + string4 = string4 + '\u0000'; + } + byte[] byArray = new byte[8]; + desCipher.encrypt(string4.substring(0, 8).getBytes(), 0, byArray, 0); + string3 = string3 + DesCipher.bytesToHexString(byArray); + string4 = string4.substring(8); + } + return string3; + } + + public static String decryptData(String string, String string2) { + int n; + if (string.length() % 8 != 0) { + System.out.println("Error decrypting data, length not multiple of 8"); + return ""; + } + string2 = (string2 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000").substring(0, 8); + DesCipher desCipher = new DesCipher(string2.getBytes()); + int n2 = string.length() / 2; + byte[] byArray = new byte[n2]; + for (n = 0; n < n2; ++n) { + String string3 = string.substring(n * 2, n * 2 + 2); + Integer n3 = new Integer(Integer.parseInt(string3, 16)); + byArray[n] = n3.byteValue(); + } + for (n = 0; n < n2 / 8; ++n) { + desCipher.decrypt(byArray, n * 8, byArray, n * 8); + } + return new String(byArray); + } + + private void des(int[] nArray, int[] nArray2, int[] nArray3) { + int n = 0; + int n2 = nArray[0]; + int n3 = nArray[1]; + int n4 = (n2 >>> 4 ^ n3) & 0xF0F0F0F; + n3 ^= n4; + n2 ^= n4 << 4; + n4 = (n2 >>> 16 ^ n3) & 0xFFFF; + n3 ^= n4; + n2 ^= n4 << 16; + n4 = (n3 >>> 2 ^ n2) & 0x33333333; + n2 ^= n4; + n3 ^= n4 << 2; + n4 = (n3 >>> 8 ^ n2) & 0xFF00FF; + n2 ^= n4; + n3 ^= n4 << 8; + n3 = n3 << 1 | n3 >>> 31 & 1; + n4 = (n2 ^ n3) & 0xAAAAAAAA; + n2 ^= n4; + n3 ^= n4; + n2 = n2 << 1 | n2 >>> 31 & 1; + for (int i = 0; i < 8; ++i) { + n4 = n3 << 28 | n3 >>> 4; + int n5 = SP7[(n4 ^= nArray3[n++]) & 0x3F]; + n5 |= SP5[n4 >>> 8 & 0x3F]; + n5 |= SP3[n4 >>> 16 & 0x3F]; + n5 |= SP1[n4 >>> 24 & 0x3F]; + n4 = n3 ^ nArray3[n++]; + n5 |= SP8[n4 & 0x3F]; + n5 |= SP6[n4 >>> 8 & 0x3F]; + n5 |= SP4[n4 >>> 16 & 0x3F]; + n2 ^= (n5 |= SP2[n4 >>> 24 & 0x3F]); + n4 = n2 << 28 | n2 >>> 4; + n5 = SP7[(n4 ^= nArray3[n++]) & 0x3F]; + n5 |= SP5[n4 >>> 8 & 0x3F]; + n5 |= SP3[n4 >>> 16 & 0x3F]; + n5 |= SP1[n4 >>> 24 & 0x3F]; + n4 = n2 ^ nArray3[n++]; + n5 |= SP8[n4 & 0x3F]; + n5 |= SP6[n4 >>> 8 & 0x3F]; + n5 |= SP4[n4 >>> 16 & 0x3F]; + n3 ^= (n5 |= SP2[n4 >>> 24 & 0x3F]); + } + n3 = n3 << 31 | n3 >>> 1; + n4 = (n2 ^ n3) & 0xAAAAAAAA; + n2 ^= n4; + n3 ^= n4; + n2 = n2 << 31 | n2 >>> 1; + n4 = (n2 >>> 8 ^ n3) & 0xFF00FF; + n3 ^= n4; + n2 ^= n4 << 8; + n4 = (n2 >>> 2 ^ n3) & 0x33333333; + n3 ^= n4; + n2 ^= n4 << 2; + n4 = (n3 >>> 16 ^ n2) & 0xFFFF; + n2 ^= n4; + n3 ^= n4 << 16; + n4 = (n3 >>> 4 ^ n2) & 0xF0F0F0F; + nArray2[0] = n3 ^= n4 << 4; + nArray2[1] = n2 ^= n4; + } + + public static void squashBytesToInts(byte[] byArray, int n, int[] nArray, int n2, int n3) { + for (int i = 0; i < n3; ++i) { + nArray[n2 + i] = (byArray[n + i * 4] & 0xFF) << 24 | (byArray[n + i * 4 + 1] & 0xFF) << 16 | (byArray[n + i * 4 + 2] & 0xFF) << 8 | byArray[n + i * 4 + 3] & 0xFF; + } + } + + public static void spreadIntsToBytes(int[] nArray, int n, byte[] byArray, int n2, int n3) { + for (int i = 0; i < n3; ++i) { + byArray[n2 + i * 4] = (byte)(nArray[n + i] >>> 24); + byArray[n2 + i * 4 + 1] = (byte)(nArray[n + i] >>> 16); + byArray[n2 + i * 4 + 2] = (byte)(nArray[n + i] >>> 8); + byArray[n2 + i * 4 + 3] = (byte)nArray[n + i]; + } + } +} + diff --git a/src/DiffieHellman.java b/src/DiffieHellman.java new file mode 100644 index 0000000..01f847d --- /dev/null +++ b/src/DiffieHellman.java @@ -0,0 +1,146 @@ +/* + * Decompiled with CFR 0.152. + */ +public class DiffieHellman { + private long gen; + private long mod; + private long priv; + private long pub; + private long key; + private long maxNum = Integer.MAX_VALUE; + private static final int DH_MAX_BITS = 31; + private static final int DH_RANGE = 100; + private static final int DH_MOD = 1; + private static final int DH_GEN = 2; + private static final int DH_PRIV = 3; + private static final int DH_PUB = 4; + private static final int DH_KEY = 5; + + public DiffieHellman() { + } + + public DiffieHellman(long l, long l2) throws Exception { + if (l >= this.maxNum || l2 >= this.maxNum) { + throw new Exception("Modulus or generator too large."); + } + this.gen = l; + this.mod = l2; + } + + private long rng(long l) { + return (long)(Math.random() * (double)l); + } + + private boolean millerRabin(long l, int n) { + long l2 = 0L; + for (int i = 0; i < n; ++i) { + l2 = this.rng(l - 3L) + 2L; + if (this.XpowYmodN(l2, l - 1L, l) == 1L) continue; + return false; + } + return true; + } + + private long generatePrime() { + long l; + long l2 = 0L; + while ((l2 = this.tryToGeneratePrime(l = this.rng(this.maxNum))) == 0L) { + } + return l2; + } + + private long tryToGeneratePrime(long l) { + if ((l & 1L) == 0L) { + ++l; + } + long l2 = 0L; + while (!this.millerRabin(l, 25) && l2++ < 100L && l < this.maxNum) { + if ((l += 2L) % 3L != 0L) continue; + l += 2L; + } + return l2 >= 100L || l >= this.maxNum ? 0L : l; + } + + private long XpowYmodN(long l, long l2, long l3) { + long l4 = 1L; + for (int i = 0; i < 64; ++i) { + l4 = l4 * l4 % l3; + if ((l2 & Long.MIN_VALUE) != 0L) { + l4 = l4 * l % l3; + } + l2 <<= 1; + } + return l4; + } + + public void createKeys() { + this.gen = this.generatePrime(); + this.mod = this.generatePrime(); + if (this.gen > this.mod) { + long l = this.gen; + this.gen = this.mod; + this.mod = l; + } + } + + public long createInterKey() { + this.priv = this.rng(this.maxNum); + this.pub = this.XpowYmodN(this.gen, this.priv, this.mod); + return this.pub; + } + + public long createEncryptionKey(long l) throws Exception { + if (l >= this.maxNum) { + throw new Exception("interKey too large"); + } + this.key = this.XpowYmodN(l, this.priv, this.mod); + return this.key; + } + + public long getValue(int n) { + switch (n) { + case 1: { + return this.mod; + } + case 2: { + return this.gen; + } + case 3: { + return this.priv; + } + case 4: { + return this.pub; + } + case 5: { + return this.key; + } + } + return 0L; + } + + public int bits(long l) { + for (int i = 0; i < 64; ++i) { + if ((l /= 2L) >= 2L) continue; + return i; + } + return 0; + } + + public static byte[] longToBytes(long l) { + byte[] byArray = new byte[8]; + for (int i = 0; i < 8; ++i) { + byArray[i] = (byte)(0xFFL & l >> 8 * (7 - i)); + } + return byArray; + } + + public static long bytesToLong(byte[] byArray) { + long l = 0L; + for (int i = 0; i < 8; ++i) { + l <<= 8; + l += (long)byArray[i]; + } + return l; + } +} + diff --git a/src/HTTPConnectSocket.java b/src/HTTPConnectSocket.java new file mode 100644 index 0000000..6241f96 --- /dev/null +++ b/src/HTTPConnectSocket.java @@ -0,0 +1,25 @@ +/* + * Decompiled with CFR 0.152. + */ +import java.io.DataInputStream; +import java.io.IOException; +import java.net.Socket; + +class HTTPConnectSocket +extends Socket { + public HTTPConnectSocket(String string, int n, String string2, int n2) throws IOException { + super(string2, n2); + this.getOutputStream().write(("CONNECT " + string + ":" + n + " HTTP/1.0\r\n\r\n").getBytes()); + DataInputStream dataInputStream = new DataInputStream(this.getInputStream()); + String string3 = dataInputStream.readLine(); + if (!string3.startsWith("HTTP/1.0 200 ")) { + if (string3.startsWith("HTTP/1.0 ")) { + string3 = string3.substring(9); + } + throw new IOException("Proxy reports \"" + string3 + "\""); + } + while ((string3 = dataInputStream.readLine()).length() != 0) { + } + } +} + diff --git a/src/HTTPConnectSocketFactory.java b/src/HTTPConnectSocketFactory.java new file mode 100644 index 0000000..56ab010 --- /dev/null +++ b/src/HTTPConnectSocketFactory.java @@ -0,0 +1,53 @@ +/* + * Decompiled with CFR 0.152. + */ +import java.applet.Applet; +import java.io.IOException; +import java.net.Socket; + +class HTTPConnectSocketFactory +implements SocketFactory { + HTTPConnectSocketFactory() { + } + + public Socket createSocket(String string, int n, Applet applet) throws IOException { + return this.createSocket(string, n, applet.getParameter("PROXYHOST1"), applet.getParameter("PROXYPORT1")); + } + + public Socket createSocket(String string, int n, String[] stringArray) throws IOException { + return this.createSocket(string, n, this.readArg(stringArray, "PROXYHOST1"), this.readArg(stringArray, "PROXYPORT1")); + } + + public Socket createSocket(String string, int n, String string2, String string3) throws IOException { + int n2 = 0; + if (string3 != null) { + try { + n2 = Integer.parseInt(string3); + } + catch (NumberFormatException numberFormatException) { + // empty catch block + } + } + if (string2 == null || n2 == 0) { + System.out.println("Incomplete parameter list for HTTPConnectSocket"); + return new Socket(string, n); + } + System.out.println("HTTP CONNECT via proxy " + string2 + " port " + n2); + HTTPConnectSocket hTTPConnectSocket = new HTTPConnectSocket(string, n, string2, n2); + return hTTPConnectSocket; + } + + private String readArg(String[] stringArray, String string) { + for (int i = 0; i < stringArray.length; i += 2) { + if (!stringArray[i].equalsIgnoreCase(string)) continue; + try { + return stringArray[i + 1]; + } + catch (Exception exception) { + return null; + } + } + return null; + } +} + diff --git a/src/HostsFilePasswordDialog.java b/src/HostsFilePasswordDialog.java new file mode 100644 index 0000000..d3296d3 --- /dev/null +++ b/src/HostsFilePasswordDialog.java @@ -0,0 +1,99 @@ +/* + * Decompiled with CFR 0.152. + */ +import java.awt.Button; +import java.awt.Component; +import java.awt.Dialog; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Frame; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Label; +import java.awt.Point; +import java.awt.TextField; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class HostsFilePasswordDialog +extends Dialog +implements ActionListener { + private Button ok; + private Button cancel; + private TextField passField; + private String password = null; + private boolean result = false; + + public HostsFilePasswordDialog(Frame frame, boolean bl) { + super(frame); + GridBagLayout gridBagLayout = new GridBagLayout(); + GridBagConstraints gridBagConstraints = new GridBagConstraints(); + this.setLayout(gridBagLayout); + this.setModal(true); + this.setResizable(false); + Point point = frame.getLocation(); + Dimension dimension = frame.getSize(); + point.x += dimension.width / 2 - 50; + point.y += dimension.height / 2 - 50; + ((Component)this).setLocation(point); + Label label = new Label("Password:", 1); + label.setFont(new Font(null, 1, 24)); + gridBagConstraints.gridwidth = 0; + gridBagLayout.setConstraints(label, gridBagConstraints); + this.add(label); + if (bl) { + Label label2 = new Label("To protect login information", 1); + Label label3 = new Label("enter a password to encrypt", 1); + Label label4 = new Label("this file with. (To leave file", 1); + Label label5 = new Label("unencrypted, click NONE)", 1); + gridBagConstraints.gridwidth = 0; + gridBagLayout.setConstraints(label2, gridBagConstraints); + gridBagLayout.setConstraints(label3, gridBagConstraints); + gridBagLayout.setConstraints(label4, gridBagConstraints); + gridBagLayout.setConstraints(label5, gridBagConstraints); + this.add(label2); + this.add(label3); + this.add(label4); + this.add(label5); + } + this.passField = new TextField(20); + this.passField.setEchoChar('*'); + gridBagConstraints.gridwidth = 0; + gridBagLayout.setConstraints(this.passField, gridBagConstraints); + this.add(this.passField); + if (bl) { + this.cancel = new Button("None"); + this.cancel.addActionListener(this); + gridBagConstraints.anchor = 10; + gridBagConstraints.fill = 0; + gridBagConstraints.gridwidth = 1; + gridBagLayout.setConstraints(this.cancel, gridBagConstraints); + this.add(this.cancel); + } + this.ok = new Button("OK"); + this.ok.addActionListener(this); + gridBagConstraints.anchor = 10; + gridBagConstraints.fill = 0; + gridBagConstraints.gridwidth = 0; + gridBagLayout.setConstraints(this.ok, gridBagConstraints); + this.add(this.ok); + this.pack(); + ((Component)this).setVisible(true); + } + + public boolean getResult() { + return this.result; + } + + public String getPassword() { + return this.password; + } + + public void actionPerformed(ActionEvent actionEvent) { + this.result = actionEvent.getSource() == this.ok; + this.password = this.passField.getText(); + ((Component)this).setVisible(false); + this.dispose(); + } +} + diff --git a/src/InStream.java b/src/InStream.java new file mode 100644 index 0000000..62fb310 --- /dev/null +++ b/src/InStream.java @@ -0,0 +1,148 @@ +/* + * Decompiled with CFR 0.152. + */ +public abstract class InStream { + public static int maxStringLength = 65535; + protected byte[] b; + protected int ptr; + protected int end; + + public final int check(int n, int n2) throws Exception { + if (this.ptr + n * n2 > this.end) { + if (this.ptr + n > this.end) { + return this.overrun(n, n2); + } + n2 = (this.end - this.ptr) / n; + } + return n2; + } + + public final void check(int n) throws Exception { + if (this.ptr + n > this.end) { + this.overrun(n, 1); + } + } + + public final int readS8() throws Exception { + this.check(1); + return this.b[this.ptr++]; + } + + public final int readS16() throws Exception { + this.check(2); + byte by = this.b[this.ptr++]; + int n = this.b[this.ptr++] & 0xFF; + return by << 8 | n; + } + + public final int readS32() throws Exception { + this.check(4); + byte by = this.b[this.ptr++]; + int n = this.b[this.ptr++] & 0xFF; + int n2 = this.b[this.ptr++] & 0xFF; + int n3 = this.b[this.ptr++] & 0xFF; + return by << 24 | n << 16 | n2 << 8 | n3; + } + + public final int readU8() throws Exception { + return this.readS8() & 0xFF; + } + + public final int readU16() throws Exception { + return this.readS16() & 0xFFFF; + } + + public final int readU32() throws Exception { + return this.readS32() & 0xFFFFFFFF; + } + + public final String readString() throws Exception { + int n = this.readU32(); + if (n > maxStringLength) { + throw new Exception("InStream max string length exceeded"); + } + char[] cArray = new char[n]; + int n2 = 0; + while (n2 < n) { + int n3 = n2 + this.check(1, n - n2); + while (n2 < n3) { + cArray[n2++] = (char)this.b[this.ptr++]; + } + } + return new String(cArray); + } + + public final void skip(int n) throws Exception { + while (n > 0) { + int n2 = this.check(1, n); + this.ptr += n2; + n -= n2; + } + } + + public void readBytes(byte[] byArray, int n, int n2) throws Exception { + int n3 = n + n2; + while (n < n3) { + int n4 = this.check(1, n3 - n); + System.arraycopy(this.b, this.ptr, byArray, n, n4); + this.ptr += n4; + n += n4; + } + } + + public final int readOpaque8() throws Exception { + return this.readU8(); + } + + public final int readOpaque16() throws Exception { + return this.readU16(); + } + + public final int readOpaque32() throws Exception { + return this.readU32(); + } + + public final int readOpaque24A() throws Exception { + this.check(3); + byte by = this.b[this.ptr++]; + byte by2 = this.b[this.ptr++]; + byte by3 = this.b[this.ptr++]; + return by << 24 | by2 << 16 | by3 << 8; + } + + public final int readOpaque24B() throws Exception { + this.check(3); + byte by = this.b[this.ptr++]; + byte by2 = this.b[this.ptr++]; + byte by3 = this.b[this.ptr++]; + return by << 16 | by2 << 8 | by3; + } + + public abstract int pos(); + + public boolean bytesAvailable() { + return this.end != this.ptr; + } + + public final byte[] getbuf() { + return this.b; + } + + public final int getptr() { + return this.ptr; + } + + public final int getend() { + return this.end; + } + + public final void setptr(int n) { + this.ptr = n; + } + + protected abstract int overrun(int var1, int var2) throws Exception; + + protected InStream() { + } +} + diff --git a/src/MemInStream.java b/src/MemInStream.java new file mode 100644 index 0000000..75b0bde --- /dev/null +++ b/src/MemInStream.java @@ -0,0 +1,20 @@ +/* + * Decompiled with CFR 0.152. + */ +public class MemInStream +extends InStream { + public MemInStream(byte[] byArray, int n, int n2) { + this.b = byArray; + this.ptr = n; + this.end = n + n2; + } + + public int pos() { + return this.ptr; + } + + protected int overrun(int n, int n2) throws Exception { + throw new Exception("MemInStream overrun: end of stream"); + } +} + diff --git a/src/OptionsFrame.java b/src/OptionsFrame.java new file mode 100644 index 0000000..a711d50 --- /dev/null +++ b/src/OptionsFrame.java @@ -0,0 +1,267 @@ +/* + * Decompiled with CFR 0.152. + */ +import java.awt.Button; +import java.awt.Choice; +import java.awt.Component; +import java.awt.Frame; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Label; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; + +class OptionsFrame +extends Frame +implements WindowListener, +ActionListener, +ItemListener { + static String[] names = new String[]{"Encoding", "Compression level", "JPEG image quality", "Cursor shape updates", "Use CopyRect", "Restricted colors", "Mouse buttons 2 and 3", "View only", "Scale remote cursor", "Share desktop"}; + static String[][] values = new String[][]{{"Auto", "Raw", "RRE", "CoRRE", "Hextile", "Zlib", "Tight", "ZRLE"}, {"Default", "1", "2", "3", "4", "5", "6", "7", "8", "9"}, {"JPEG off", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}, {"Enable", "Ignore", "Disable"}, {"Yes", "No"}, {"Yes", "No"}, {"Normal", "Reversed"}, {"Yes", "No"}, {"No", "50%", "75%", "125%", "150%"}, {"Yes", "No"}}; + final int encodingIndex = 0; + final int compressLevelIndex = 1; + final int jpegQualityIndex = 2; + final int cursorUpdatesIndex = 3; + final int useCopyRectIndex = 4; + final int eightBitColorsIndex = 5; + final int mouseButtonIndex = 6; + final int viewOnlyIndex = 7; + final int scaleCursorIndex = 8; + final int shareDesktopIndex = 9; + Label[] labels = new Label[names.length]; + Choice[] choices = new Choice[names.length]; + Button closeButton; + VncViewer viewer; + int preferredEncoding; + int compressLevel; + int jpegQuality; + boolean useCopyRect; + boolean requestCursorUpdates; + boolean ignoreCursorUpdates; + boolean eightBitColors; + boolean reverseMouseButtons2And3; + boolean shareDesktop; + boolean viewOnly; + int scaleCursor; + boolean autoScale; + int scalingFactor; + + OptionsFrame(VncViewer vncViewer) { + super("TightVNC Options"); + int n; + this.viewer = vncViewer; + GridBagLayout gridBagLayout = new GridBagLayout(); + this.setLayout(gridBagLayout); + GridBagConstraints gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.fill = 1; + for (n = 0; n < names.length; ++n) { + this.labels[n] = new Label(names[n]); + gridBagConstraints.gridwidth = 1; + gridBagLayout.setConstraints(this.labels[n], gridBagConstraints); + this.add(this.labels[n]); + this.choices[n] = new Choice(); + gridBagConstraints.gridwidth = 0; + gridBagLayout.setConstraints(this.choices[n], gridBagConstraints); + this.add(this.choices[n]); + this.choices[n].addItemListener(this); + for (int i = 0; i < values[n].length; ++i) { + this.choices[n].addItem(values[n][i]); + } + } + this.closeButton = new Button("Close"); + gridBagConstraints.gridwidth = 0; + gridBagLayout.setConstraints(this.closeButton, gridBagConstraints); + this.add(this.closeButton); + this.closeButton.addActionListener(this); + this.pack(); + this.addWindowListener(this); + this.choices[0].select("Auto"); + this.choices[1].select("Default"); + this.choices[2].select("6"); + this.choices[3].select("Enable"); + this.choices[4].select("Yes"); + this.choices[5].select("No"); + this.choices[6].select("Normal"); + this.choices[7].select("No"); + this.choices[8].select("No"); + this.choices[9].select("Yes"); + for (n = 0; n < names.length; ++n) { + String string = this.viewer.readParameter(names[n], false); + if (string == null) continue; + for (int i = 0; i < values[n].length; ++i) { + if (!string.equalsIgnoreCase(values[n][i])) continue; + this.choices[n].select(i); + } + } + this.autoScale = false; + this.scalingFactor = 100; + String string = this.viewer.readParameter("Scaling Factor", false); + if (string != null) { + if (string.equalsIgnoreCase("Auto")) { + this.autoScale = true; + } else { + if (string.charAt(string.length() - 1) == '%') { + string = string.substring(0, string.length() - 1); + } + try { + this.scalingFactor = Integer.parseInt(string); + } + catch (NumberFormatException numberFormatException) { + this.scalingFactor = 100; + } + if (this.scalingFactor < 1) { + this.scalingFactor = 1; + } else if (this.scalingFactor > 1000) { + this.scalingFactor = 1000; + } + } + } + this.setEncodings(); + this.setColorFormat(); + this.setOtherOptions(); + } + + void disableShareDesktop() { + this.labels[9].setEnabled(false); + this.choices[9].setEnabled(false); + } + + void setEncodings() { + this.useCopyRect = this.choices[4].getSelectedItem().equals("Yes"); + this.preferredEncoding = 0; + boolean bl = false; + boolean bl2 = false; + if (this.choices[0].getSelectedItem().equals("RRE")) { + this.preferredEncoding = 2; + } else if (this.choices[0].getSelectedItem().equals("CoRRE")) { + this.preferredEncoding = 4; + } else if (this.choices[0].getSelectedItem().equals("Hextile")) { + this.preferredEncoding = 5; + } else if (this.choices[0].getSelectedItem().equals("ZRLE")) { + this.preferredEncoding = 16; + } else if (this.choices[0].getSelectedItem().equals("Zlib")) { + this.preferredEncoding = 6; + bl = true; + } else if (this.choices[0].getSelectedItem().equals("Tight")) { + this.preferredEncoding = 7; + bl = true; + bl2 = !this.eightBitColors; + } else if (this.choices[0].getSelectedItem().equals("Auto")) { + this.preferredEncoding = -1; + bl2 = !this.eightBitColors; + } + try { + this.compressLevel = Integer.parseInt(this.choices[1].getSelectedItem()); + } + catch (NumberFormatException numberFormatException) { + this.compressLevel = -1; + } + if (this.compressLevel < 1 || this.compressLevel > 9) { + this.compressLevel = -1; + } + this.labels[1].setEnabled(bl); + this.choices[1].setEnabled(bl); + try { + this.jpegQuality = Integer.parseInt(this.choices[2].getSelectedItem()); + } + catch (NumberFormatException numberFormatException) { + this.jpegQuality = -1; + } + if (this.jpegQuality < 0 || this.jpegQuality > 9) { + this.jpegQuality = -1; + } + this.labels[2].setEnabled(bl2); + this.choices[2].setEnabled(bl2); + boolean bl3 = this.requestCursorUpdates = !this.choices[3].getSelectedItem().equals("Disable"); + if (this.requestCursorUpdates) { + this.ignoreCursorUpdates = this.choices[3].getSelectedItem().equals("Ignore"); + } + this.viewer.setEncodings(); + } + + void setColorFormat() { + this.eightBitColors = this.choices[5].getSelectedItem().equals("Yes"); + boolean bl = !this.eightBitColors && (this.choices[0].getSelectedItem().equals("Tight") || this.choices[0].getSelectedItem().equals("Auto")); + this.labels[2].setEnabled(bl); + this.choices[2].setEnabled(bl); + } + + void setOtherOptions() { + this.reverseMouseButtons2And3 = this.choices[6].getSelectedItem().equals("Reversed"); + this.viewOnly = this.choices[7].getSelectedItem().equals("Yes"); + if (this.viewer.vc != null) { + this.viewer.vc.enableInput(!this.viewOnly); + } + this.shareDesktop = this.choices[9].getSelectedItem().equals("Yes"); + String string = this.choices[8].getSelectedItem(); + if (string.endsWith("%")) { + string = string.substring(0, string.length() - 1); + } + try { + this.scaleCursor = Integer.parseInt(string); + } + catch (NumberFormatException numberFormatException) { + this.scaleCursor = 0; + } + if (this.scaleCursor < 10 || this.scaleCursor > 500) { + this.scaleCursor = 0; + } + if (this.requestCursorUpdates && !this.ignoreCursorUpdates && !this.viewOnly) { + this.labels[8].setEnabled(true); + this.choices[8].setEnabled(true); + } else { + this.labels[8].setEnabled(false); + this.choices[8].setEnabled(false); + } + if (this.viewer.vc != null) { + this.viewer.vc.createSoftCursor(); + } + } + + public void itemStateChanged(ItemEvent itemEvent) { + Object object = itemEvent.getSource(); + if (object == this.choices[0] || object == this.choices[1] || object == this.choices[2] || object == this.choices[3] || object == this.choices[4]) { + this.setEncodings(); + if (object == this.choices[3]) { + this.setOtherOptions(); + } + } else if (object == this.choices[5]) { + this.setColorFormat(); + } else if (object == this.choices[6] || object == this.choices[9] || object == this.choices[7] || object == this.choices[8]) { + this.setOtherOptions(); + } + } + + public void actionPerformed(ActionEvent actionEvent) { + if (actionEvent.getSource() == this.closeButton) { + ((Component)this).setVisible(false); + } + } + + public void windowClosing(WindowEvent windowEvent) { + ((Component)this).setVisible(false); + } + + public void windowActivated(WindowEvent windowEvent) { + } + + public void windowDeactivated(WindowEvent windowEvent) { + } + + public void windowOpened(WindowEvent windowEvent) { + } + + public void windowClosed(WindowEvent windowEvent) { + } + + public void windowIconified(WindowEvent windowEvent) { + } + + public void windowDeiconified(WindowEvent windowEvent) { + } +} + diff --git a/src/RecordingFrame.java b/src/RecordingFrame.java new file mode 100644 index 0000000..17146c2 --- /dev/null +++ b/src/RecordingFrame.java @@ -0,0 +1,231 @@ +/* + * Decompiled with CFR 0.152. + */ +import java.awt.Button; +import java.awt.Color; +import java.awt.Component; +import java.awt.FileDialog; +import java.awt.Font; +import java.awt.Frame; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.Label; +import java.awt.Panel; +import java.awt.TextField; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; +import java.io.File; + +class RecordingFrame +extends Frame +implements WindowListener, +ActionListener { + boolean recording; + TextField fnameField; + Button browseButton; + Label statusLabel; + Button recordButton; + Button nextButton; + Button closeButton; + VncViewer viewer; + + public static boolean checkSecurity() { + SecurityManager securityManager = System.getSecurityManager(); + if (securityManager != null) { + try { + securityManager.checkPropertyAccess("user.dir"); + securityManager.checkPropertyAccess("file.separator"); + System.getProperty("user.dir"); + } + catch (SecurityException securityException) { + System.out.println("SecurityManager restricts session recording."); + return false; + } + } + return true; + } + + RecordingFrame(VncViewer vncViewer) { + super("TightVNC Session Recording"); + this.viewer = vncViewer; + String string = this.nextNewFilename(System.getProperty("user.dir") + System.getProperty("file.separator") + "vncsession.fbs"); + Panel panel = new Panel(); + GridBagLayout gridBagLayout = new GridBagLayout(); + panel.setLayout(gridBagLayout); + GridBagConstraints gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridwidth = -1; + gridBagConstraints.fill = 1; + gridBagConstraints.weightx = 4.0; + this.fnameField = new TextField(string, 64); + gridBagLayout.setConstraints(this.fnameField, gridBagConstraints); + panel.add(this.fnameField); + this.fnameField.addActionListener(this); + gridBagConstraints.gridwidth = 0; + gridBagConstraints.weightx = 1.0; + this.browseButton = new Button("Browse"); + gridBagLayout.setConstraints(this.browseButton, gridBagConstraints); + panel.add(this.browseButton); + this.browseButton.addActionListener(this); + GridBagLayout gridBagLayout2 = new GridBagLayout(); + this.setLayout(gridBagLayout2); + GridBagConstraints gridBagConstraints2 = new GridBagConstraints(); + gridBagConstraints2.gridwidth = 0; + gridBagConstraints2.fill = 1; + gridBagConstraints2.weighty = 1.0; + gridBagConstraints2.insets = new Insets(10, 0, 0, 0); + Label label = new Label("File name to save next recorded session in:", 1); + gridBagLayout2.setConstraints(label, gridBagConstraints2); + this.add(label); + gridBagConstraints2.fill = 2; + gridBagConstraints2.weighty = 0.0; + gridBagConstraints2.insets = new Insets(0, 0, 0, 0); + gridBagLayout2.setConstraints(panel, gridBagConstraints2); + this.add(panel); + gridBagConstraints2.fill = 1; + gridBagConstraints2.weighty = 1.0; + gridBagConstraints2.insets = new Insets(10, 0, 10, 0); + this.statusLabel = new Label("", 1); + gridBagLayout2.setConstraints(this.statusLabel, gridBagConstraints2); + this.add(this.statusLabel); + gridBagConstraints2.fill = 2; + gridBagConstraints2.weightx = 1.0; + gridBagConstraints2.weighty = 0.0; + gridBagConstraints2.gridwidth = 1; + gridBagConstraints2.insets = new Insets(0, 0, 0, 0); + this.recordButton = new Button("Record"); + gridBagLayout2.setConstraints(this.recordButton, gridBagConstraints2); + this.add(this.recordButton); + this.recordButton.addActionListener(this); + this.nextButton = new Button("Next file"); + gridBagLayout2.setConstraints(this.nextButton, gridBagConstraints2); + this.add(this.nextButton); + this.nextButton.addActionListener(this); + this.closeButton = new Button("Close"); + gridBagLayout2.setConstraints(this.closeButton, gridBagConstraints2); + this.add(this.closeButton); + this.closeButton.addActionListener(this); + this.stopRecording(); + this.pack(); + this.addWindowListener(this); + } + + protected String nextFilename(String string) { + int n; + int n2 = n = string.length(); + int n3 = 1; + if (n > 4 && string.charAt(n - 4) == '.') { + try { + n3 = Integer.parseInt(string.substring(n - 3, n)) + 1; + n2 = n - 4; + } + catch (NumberFormatException numberFormatException) { + // empty catch block + } + } + char[] cArray = new char[]{'0', '0', '0'}; + String string2 = String.valueOf(n3); + if (string2.length() < 3) { + string2 = new String(cArray, 0, 3 - string2.length()) + string2; + } + return string.substring(0, n2) + '.' + string2; + } + + protected String nextNewFilename(String string) { + String string2 = string; + try { + File file; + while ((file = new File(string2 = this.nextFilename(string2))).exists()) { + } + } + catch (SecurityException securityException) { + // empty catch block + } + return string2; + } + + protected boolean browseFile() { + File file = new File(this.fnameField.getText()); + FileDialog fileDialog = new FileDialog((Frame)this, "Save next session as...", 1); + fileDialog.setDirectory(file.getParent()); + ((Component)fileDialog).setVisible(true); + if (fileDialog.getFile() != null) { + String string; + String string2 = fileDialog.getDirectory(); + String string3 = System.getProperty("file.separator"); + if (string2.length() > 0 && !string3.equals(string2.substring(string2.length() - string3.length()))) { + string2 = string2 + string3; + } + if ((string = string2 + fileDialog.getFile()).equals(this.fnameField.getText())) { + this.fnameField.setText(string); + return true; + } + } + return false; + } + + public void startRecording() { + this.statusLabel.setText("Status: Recording..."); + this.statusLabel.setFont(new Font("Helvetica", 1, 12)); + this.statusLabel.setForeground(Color.red); + this.recordButton.setLabel("Stop recording"); + this.recording = true; + this.viewer.setRecordingStatus(this.fnameField.getText()); + } + + public void stopRecording() { + this.statusLabel.setText("Status: Not recording."); + this.statusLabel.setFont(new Font("Helvetica", 0, 12)); + this.statusLabel.setForeground(Color.black); + this.recordButton.setLabel("Record"); + this.recording = false; + this.viewer.setRecordingStatus(null); + } + + public void windowClosing(WindowEvent windowEvent) { + ((Component)this).setVisible(false); + } + + public void windowActivated(WindowEvent windowEvent) { + } + + public void windowDeactivated(WindowEvent windowEvent) { + } + + public void windowOpened(WindowEvent windowEvent) { + } + + public void windowClosed(WindowEvent windowEvent) { + } + + public void windowIconified(WindowEvent windowEvent) { + } + + public void windowDeiconified(WindowEvent windowEvent) { + } + + public void actionPerformed(ActionEvent actionEvent) { + if (actionEvent.getSource() == this.browseButton) { + if (this.browseFile() && this.recording) { + this.startRecording(); + } + } else if (actionEvent.getSource() == this.recordButton) { + if (!this.recording) { + this.startRecording(); + } else { + this.stopRecording(); + this.fnameField.setText(this.nextNewFilename(this.fnameField.getText())); + } + } else if (actionEvent.getSource() == this.nextButton) { + this.fnameField.setText(this.nextNewFilename(this.fnameField.getText())); + if (this.recording) { + this.startRecording(); + } + } else if (actionEvent.getSource() == this.closeButton) { + ((Component)this).setVisible(false); + } + } +} + diff --git a/src/ReloginPanel.java b/src/ReloginPanel.java new file mode 100644 index 0000000..b0afa06 --- /dev/null +++ b/src/ReloginPanel.java @@ -0,0 +1,39 @@ +/* + * Decompiled with CFR 0.152. + */ +import java.awt.Button; +import java.awt.FlowLayout; +import java.awt.Panel; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +class ReloginPanel +extends Panel +implements ActionListener { + Button reloginButton; + Button closeButton; + VncViewer viewer; + + public ReloginPanel(VncViewer vncViewer) { + this.viewer = vncViewer; + this.setLayout(new FlowLayout(1)); + this.reloginButton = new Button("Login again"); + this.add(this.reloginButton); + this.reloginButton.addActionListener(this); + if (this.viewer.inSeparateFrame) { + this.closeButton = new Button("Close window"); + this.add(this.closeButton); + this.closeButton.addActionListener(this); + } + } + + public synchronized void actionPerformed(ActionEvent actionEvent) { + if (this.viewer.inSeparateFrame) { + this.viewer.vncFrame.dispose(); + } + if (actionEvent.getSource() == this.reloginButton) { + this.viewer.getAppletContext().showDocument(this.viewer.getDocumentBase()); + } + } +} + diff --git a/src/RfbProto.java b/src/RfbProto.java new file mode 100644 index 0000000..c06b95d --- /dev/null +++ b/src/RfbProto.java @@ -0,0 +1,1027 @@ +/* + * Decompiled with CFR 0.152. + */ +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.io.BufferedInputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.Socket; +import java.util.zip.Deflater; + +class RfbProto { + static final String versionMsg_3_3 = "RFB 003.003\n"; + static final String versionMsg_3_4 = "RFB 003.004\n"; + static final String versionMsg_3_7 = "RFB 003.007\n"; + static final String versionMsg_3_8 = "RFB 003.008\n"; + static final String StandardVendor = "STDV"; + static final String TridiaVncVendor = "TRDV"; + static final String UltraVncVendor = "UVNC"; + static final String TightVncVendor = "TGHT"; + static final int SecTypeInvalid = 0; + static final int SecTypeNone = 1; + static final int SecTypeVncAuth = 2; + static final int SecTypeTight = 16; + static final int SecTypeMsLogon = -6; + static final int NoTunneling = 0; + static final String SigNoTunneling = "NOTUNNEL"; + static final int AuthNone = 1; + static final int AuthVNC = 2; + static final int AuthMSL = -6; + static final int AuthUnixLogin = 129; + static final String SigAuthNone = "NOAUTH__"; + static final String SigAuthVNC = "VNCAUTH_"; + static final String SigAuthMSLogon = "MSLOGON_"; + static final String SigAuthUnixLogin = "ULGNAUTH"; + static final int VncAuthOK = 0; + static final int VncAuthFailed = 1; + static final int VncAuthTooMany = 2; + static final int FramebufferUpdate = 0; + static final int SetColourMapEntries = 1; + static final int Bell = 2; + static final int ServerCutText = 3; + static final int SetPixelFormat = 0; + static final int FixColourMapEntries = 1; + static final int SetEncodings = 2; + static final int FramebufferUpdateRequest = 3; + static final int KeyboardEvent = 4; + static final int PointerEvent = 5; + static final int ClientCutText = 6; + static final int EncodingRaw = 0; + static final int EncodingCopyRect = 1; + static final int EncodingRRE = 2; + static final int EncodingCoRRE = 4; + static final int EncodingHextile = 5; + static final int EncodingZlib = 6; + static final int EncodingTight = 7; + static final int EncodingZRLE = 16; + static final int EncodingCompressLevel0 = -256; + static final int EncodingQualityLevel0 = -32; + static final int EncodingXCursor = -240; + static final int EncodingRichCursor = -239; + static final int EncodingPointerPos = -232; + static final int EncodingLastRect = -224; + static final int EncodingNewFBSize = -223; + static final String SigEncodingRaw = "RAW_____"; + static final String SigEncodingCopyRect = "COPYRECT"; + static final String SigEncodingRRE = "RRE_____"; + static final String SigEncodingCoRRE = "CORRE___"; + static final String SigEncodingHextile = "HEXTILE_"; + static final String SigEncodingZlib = "ZLIB____"; + static final String SigEncodingTight = "TIGHT___"; + static final String SigEncodingZRLE = "ZRLE____"; + static final String SigEncodingCompressLevel0 = "COMPRLVL"; + static final String SigEncodingQualityLevel0 = "JPEGQLVL"; + static final String SigEncodingXCursor = "X11CURSR"; + static final String SigEncodingRichCursor = "RCHCURSR"; + static final String SigEncodingPointerPos = "POINTPOS"; + static final String SigEncodingLastRect = "LASTRECT"; + static final String SigEncodingNewFBSize = "NEWFBSIZ"; + static final int MaxNormalEncoding = 255; + static final int HextileRaw = 1; + static final int HextileBackgroundSpecified = 2; + static final int HextileForegroundSpecified = 4; + static final int HextileAnySubrects = 8; + static final int HextileSubrectsColoured = 16; + static final int TightMinToCompress = 12; + static final int TightExplicitFilter = 4; + static final int TightFill = 8; + static final int TightJpeg = 9; + static final int TightMaxSubencoding = 9; + static final int TightFilterCopy = 0; + static final int TightFilterPalette = 1; + static final int TightFilterGradient = 2; + String host; + int port; + Socket sock; + DataInputStream is; + OutputStream os; + SessionRecorder rec; + boolean inNormalProtocol = false; + VncViewer viewer; + boolean brokenKeyPressed = false; + boolean wereZlibUpdates = false; + boolean recordFromBeginning = true; + boolean zlibWarningShown; + boolean tightWarningShown; + int numUpdatesInSession; + boolean timing; + long timeWaitedIn100us; + long timedKbits; + int serverMajor; + int serverMinor; + int clientMajor; + int clientMinor; + boolean protocolTightVNC; + CapsContainer tunnelCaps; + CapsContainer authCaps; + CapsContainer serverMsgCaps; + CapsContainer clientMsgCaps; + CapsContainer encodingCaps; + private boolean closed; + String desktopName; + int framebufferWidth; + int framebufferHeight; + int bitsPerPixel; + int depth; + boolean bigEndian; + boolean trueColour; + int redMax; + int greenMax; + int blueMax; + int redShift; + int greenShift; + int blueShift; + int updateNRects; + int updateRectX; + int updateRectY; + int updateRectW; + int updateRectH; + int updateRectEncoding; + int copyRectSrcX; + int copyRectSrcY; + byte[] eventBuf = new byte[72]; + int eventBufLen; + static final int CTRL_MASK = 2; + static final int SHIFT_MASK = 1; + static final int META_MASK = 4; + static final int ALT_MASK = 8; + int pointerMask = 0; + int oldModifiers = 0; + + RfbProto(String string, int n, VncViewer vncViewer) throws IOException { + this.viewer = vncViewer; + this.host = string; + this.port = n; + if (this.viewer.socketFactory == null) { + this.sock = new Socket(this.host, this.port); + } else { + try { + Class clazz = Class.forName(this.viewer.socketFactory); + SocketFactory socketFactory = (SocketFactory)clazz.newInstance(); + this.sock = this.viewer.inAnApplet ? socketFactory.createSocket(this.host, this.port, this.viewer) : socketFactory.createSocket(this.host, this.port, this.viewer.mainArgs); + } + catch (Exception exception) { + exception.printStackTrace(); + throw new IOException(exception.getMessage()); + } + } + this.is = new DataInputStream(new BufferedInputStream(this.sock.getInputStream(), 16384)); + this.os = this.sock.getOutputStream(); + this.timing = false; + this.timeWaitedIn100us = 5L; + this.timedKbits = 0L; + } + + synchronized void close() { + try { + this.sock.close(); + this.closed = true; + System.out.println("RFB socket closed"); + if (this.rec != null) { + this.rec.close(); + this.rec = null; + } + } + catch (Exception exception) { + exception.printStackTrace(); + } + } + + synchronized boolean closed() { + return this.closed; + } + + void readVersionMsg() throws Exception { + byte[] byArray = new byte[12]; + this.readFully(byArray); + if (byArray[0] != 82 || byArray[1] != 70 || byArray[2] != 66 || byArray[3] != 32 || byArray[4] < 48 || byArray[4] > 57 || byArray[5] < 48 || byArray[5] > 57 || byArray[6] < 48 || byArray[6] > 57 || byArray[7] != 46 || byArray[8] < 48 || byArray[8] > 57 || byArray[9] < 48 || byArray[9] > 57 || byArray[10] < 48 || byArray[10] > 57 || byArray[11] != 10) { + throw new Exception("Host " + this.host + " port " + this.port + " is not an RFB server"); + } + this.serverMajor = (byArray[4] - 48) * 100 + (byArray[5] - 48) * 10 + (byArray[6] - 48); + this.serverMinor = (byArray[8] - 48) * 100 + (byArray[9] - 48) * 10 + (byArray[10] - 48); + if (this.serverMajor < 3) { + throw new Exception("RFB server does not support protocol version 3"); + } + } + + void writeVersionMsg() throws IOException { + this.clientMajor = 3; + if (this.serverMajor > 3 || this.serverMinor >= 8) { + this.clientMinor = 8; + this.os.write(versionMsg_3_8.getBytes()); + } else if (this.serverMinor >= 7) { + this.clientMinor = 7; + this.os.write(versionMsg_3_7.getBytes()); + } else { + this.clientMinor = 3; + this.os.write(versionMsg_3_3.getBytes()); + } + this.protocolTightVNC = false; + } + + int negotiateSecurity() throws Exception { + return this.clientMinor >= 7 ? this.selectSecurityType() : this.readSecurityType(); + } + + int readSecurityType() throws Exception { + int n = this.is.readInt(); + switch (n) { + case 0: { + this.readConnFailedReason(); + return 0; + } + case -6: + case 1: + case 2: { + return n; + } + } + throw new Exception("Unknown security type from RFB server: " + n); + } + + int selectSecurityType() throws Exception { + int n; + int n2 = 0; + int n3 = this.is.readUnsignedByte(); + if (n3 == 0) { + this.readConnFailedReason(); + return 0; + } + byte[] byArray = new byte[n3]; + this.readFully(byArray); + for (n = 0; n < n3; ++n) { + if (byArray[n] != 16) continue; + this.protocolTightVNC = true; + this.os.write(16); + return 16; + } + for (n = 0; n < n3; ++n) { + if (byArray[n] != 1 && byArray[n] != 2 && byArray[n] != -6) continue; + n2 = byArray[n]; + break; + } + if (n2 == 0) { + throw new Exception("Server did not offer supported security type"); + } + this.os.write(n2); + return n2; + } + + void authenticateNone() throws Exception { + if (this.clientMinor >= 8) { + this.readSecurityResult("No authentication"); + } + } + + void authenticateVNC(String string) throws Exception { + int n; + byte[] byArray = new byte[16]; + this.readFully(byArray); + if (string.length() > 8) { + string = string.substring(0, 8); + } + if ((n = string.indexOf(0)) != -1) { + string = string.substring(0, n); + } + byte[] byArray2 = new byte[]{0, 0, 0, 0, 0, 0, 0, 0}; + System.arraycopy(string.getBytes(), 0, byArray2, 0, string.length()); + DesCipher desCipher = new DesCipher(byArray2); + desCipher.encrypt(byArray, 0, byArray, 0); + desCipher.encrypt(byArray, 8, byArray, 8); + this.os.write(byArray); + this.readSecurityResult("VNC authentication"); + } + + void authenticateMSLogonI(String string, String string2) throws Exception { + int n; + int n2; + byte[] byArray = new byte[256]; + byte[] byArray2 = new byte[256]; + byte[] byArray3 = new byte[256]; + byte[] byArray4 = new byte[64]; + byte[] byArray5 = new byte[16]; + System.arraycopy(string2.getBytes(), 0, byArray, 0, string2.length()); + if (string2.length() < 256) { + for (int i = string2.length(); i < 256; ++i) { + byArray[i] = 0; + } + } + String string3 = "."; + System.arraycopy(string3.getBytes(), 0, byArray2, 0, string3.length()); + if (string3.length() < 256) { + for (n2 = string3.length(); n2 < 256; ++n2) { + byArray2[n2] = 0; + } + } + System.arraycopy(string.getBytes(), 0, byArray3, 0, string.length()); + if (string.length() < 32) { + for (n2 = string.length(); n2 < 32; ++n2) { + byArray3[n2] = 0; + } + } + byte[] byArray6 = new byte[]{23, 82, 107, 6, 35, 78, 88, 7}; + DesCipher desCipher = new DesCipher(byArray6); + desCipher.encrypt(byArray3, 0, byArray3, 0); + this.is.readFully(byArray4); + this.is.readFully(byArray5); + if (string.length() > 8) { + string = string.substring(0, 8); + } + if ((n = string.indexOf(0)) != -1) { + string = string.substring(0, n); + } + for (int i = 0; i < 32; ++i) { + byArray4[i] = (byte)(byArray3[i] ^ byArray4[i]); + } + this.os.write(byArray); + this.os.write(byArray2); + this.os.write(byArray4); + byte[] byArray7 = new byte[]{0, 0, 0, 0, 0, 0, 0, 0}; + System.arraycopy(string.getBytes(), 0, byArray7, 0, string.length()); + DesCipher desCipher2 = new DesCipher(byArray7); + desCipher2.encrypt(byArray5, 0, byArray5, 0); + desCipher2.encrypt(byArray5, 8, byArray5, 8); + this.os.write(byArray5); + this.readSecurityResult("VNC+MS authentication"); + } + + void authenticateMSLogon(String string, String string2) throws Exception { + int n; + byte[] byArray = new byte[256]; + byte[] byArray2 = new byte[64]; + long l = this.is.readLong(); + long l2 = this.is.readLong(); + long l3 = this.is.readLong(); + DiffieHellman diffieHellman = new DiffieHellman(l, l2); + long l4 = diffieHellman.createInterKey(); + this.os.write(DiffieHellman.longToBytes(l4)); + long l5 = diffieHellman.createEncryptionKey(l3); + System.out.println("gen=" + l + ", mod=" + l2 + ", pub=" + l4 + ", key=" + l5); + DesCipher desCipher = new DesCipher(DiffieHellman.longToBytes(l5)); + System.arraycopy(string2.getBytes(), 0, byArray, 0, string2.length()); + if (string2.length() < 256) { + for (n = string2.length(); n < 256; ++n) { + byArray[n] = 0; + } + } + System.arraycopy(string.getBytes(), 0, byArray2, 0, string.length()); + if (string.length() < 32) { + for (n = string.length(); n < 32; ++n) { + byArray2[n] = 0; + } + } + desCipher.encryptText(byArray, byArray, DiffieHellman.longToBytes(l5)); + desCipher.encryptText(byArray2, byArray2, DiffieHellman.longToBytes(l5)); + this.os.write(byArray); + this.os.write(byArray2); + this.readSecurityResult("MS-Logon authentication"); + } + + void readSecurityResult(String string) throws Exception { + int n = this.is.readInt(); + switch (n) { + case 0: { + System.out.println(string + ": success"); + break; + } + case 1: { + if (this.clientMinor >= 8) { + this.readConnFailedReason(); + } + throw new Exception(string + ": failed"); + } + case 2: { + throw new Exception(string + ": failed, too many tries"); + } + default: { + throw new Exception(string + ": unknown result " + n); + } + } + } + + void readConnFailedReason() throws Exception { + int n = this.is.readInt(); + byte[] byArray = new byte[n]; + this.readFully(byArray); + throw new Exception(new String(byArray)); + } + + void initCapabilities() { + this.tunnelCaps = new CapsContainer(); + this.authCaps = new CapsContainer(); + this.serverMsgCaps = new CapsContainer(); + this.clientMsgCaps = new CapsContainer(); + this.encodingCaps = new CapsContainer(); + this.authCaps.add(1, StandardVendor, SigAuthNone, "No authentication"); + this.authCaps.add(2, StandardVendor, SigAuthVNC, "Standard VNC password authentication"); + this.authCaps.add(-6, UltraVncVendor, SigAuthMSLogon, "MS-Logon authentication"); + this.encodingCaps.add(1, StandardVendor, SigEncodingCopyRect, "Standard CopyRect encoding"); + this.encodingCaps.add(2, StandardVendor, SigEncodingRRE, "Standard RRE encoding"); + this.encodingCaps.add(4, StandardVendor, SigEncodingCoRRE, "Standard CoRRE encoding"); + this.encodingCaps.add(5, StandardVendor, SigEncodingHextile, "Standard Hextile encoding"); + this.encodingCaps.add(16, StandardVendor, SigEncodingZRLE, "Standard ZRLE encoding"); + this.encodingCaps.add(6, TridiaVncVendor, SigEncodingZlib, "Zlib encoding"); + this.encodingCaps.add(7, TightVncVendor, SigEncodingTight, "Tight encoding"); + this.encodingCaps.add(-256, TightVncVendor, SigEncodingCompressLevel0, "Compression level"); + this.encodingCaps.add(-32, TightVncVendor, SigEncodingQualityLevel0, "JPEG quality level"); + this.encodingCaps.add(-240, TightVncVendor, SigEncodingXCursor, "X-style cursor shape update"); + this.encodingCaps.add(-239, TightVncVendor, SigEncodingRichCursor, "Rich-color cursor shape update"); + this.encodingCaps.add(-232, TightVncVendor, SigEncodingPointerPos, "Pointer position update"); + this.encodingCaps.add(-224, TightVncVendor, SigEncodingLastRect, "LastRect protocol extension"); + this.encodingCaps.add(-223, TightVncVendor, SigEncodingNewFBSize, "Framebuffer size change"); + } + + void setupTunneling() throws IOException { + int n = this.is.readInt(); + if (n != 0) { + this.readCapabilityList(this.tunnelCaps, n); + this.writeInt(0); + } + } + + int negotiateAuthenticationTight() throws Exception { + int n = this.is.readInt(); + if (n == 0) { + return 1; + } + this.readCapabilityList(this.authCaps, n); + for (int i = 0; i < this.authCaps.numEnabled(); ++i) { + int n2 = this.authCaps.getByOrder(i); + if (n2 != 1 && n2 != 2 && n2 != -6) continue; + this.writeInt(n2); + return n2; + } + throw new Exception("No suitable authentication scheme found"); + } + + void readCapabilityList(CapsContainer capsContainer, int n) throws IOException { + byte[] byArray = new byte[4]; + byte[] byArray2 = new byte[8]; + for (int i = 0; i < n; ++i) { + int n2 = this.is.readInt(); + this.readFully(byArray); + this.readFully(byArray2); + capsContainer.enable(new CapabilityInfo(n2, byArray, byArray2)); + } + } + + void writeInt(int n) throws IOException { + byte[] byArray = new byte[]{(byte)(n >> 24 & 0xFF), (byte)(n >> 16 & 0xFF), (byte)(n >> 8 & 0xFF), (byte)(n & 0xFF)}; + this.os.write(byArray); + } + + void writeClientInit() throws IOException { + if (this.viewer.options.shareDesktop) { + this.os.write(1); + } else { + this.os.write(0); + } + this.viewer.options.disableShareDesktop(); + } + + void readServerInit() throws IOException { + this.framebufferWidth = this.is.readUnsignedShort(); + this.framebufferHeight = this.is.readUnsignedShort(); + this.bitsPerPixel = this.is.readUnsignedByte(); + this.depth = this.is.readUnsignedByte(); + this.bigEndian = this.is.readUnsignedByte() != 0; + this.trueColour = this.is.readUnsignedByte() != 0; + this.redMax = this.is.readUnsignedShort(); + this.greenMax = this.is.readUnsignedShort(); + this.blueMax = this.is.readUnsignedShort(); + this.redShift = this.is.readUnsignedByte(); + this.greenShift = this.is.readUnsignedByte(); + this.blueShift = this.is.readUnsignedByte(); + byte[] byArray = new byte[3]; + this.readFully(byArray); + int n = this.is.readInt(); + byte[] byArray2 = new byte[n]; + this.readFully(byArray2); + this.desktopName = new String(byArray2); + if (this.protocolTightVNC) { + int n2 = this.is.readUnsignedShort(); + int n3 = this.is.readUnsignedShort(); + int n4 = this.is.readUnsignedShort(); + this.is.readUnsignedShort(); + this.readCapabilityList(this.serverMsgCaps, n2); + this.readCapabilityList(this.clientMsgCaps, n3); + this.readCapabilityList(this.encodingCaps, n4); + } + this.inNormalProtocol = true; + } + + void startSession(String string) throws IOException { + this.rec = new SessionRecorder(string); + this.rec.writeHeader(); + this.rec.write(versionMsg_3_3.getBytes()); + this.rec.writeIntBE(1); + this.rec.writeShortBE(this.framebufferWidth); + this.rec.writeShortBE(this.framebufferHeight); + byte[] byArray = new byte[]{32, 24, 0, 1, 0, -1, 0, -1, 0, -1, 16, 8, 0, 0, 0, 0}; + this.rec.write(byArray); + this.rec.writeIntBE(this.desktopName.length()); + this.rec.write(this.desktopName.getBytes()); + this.numUpdatesInSession = 0; + if (this.wereZlibUpdates) { + this.recordFromBeginning = false; + } + this.zlibWarningShown = false; + this.tightWarningShown = false; + } + + void closeSession() throws IOException { + if (this.rec != null) { + this.rec.close(); + this.rec = null; + } + } + + void setFramebufferSize(int n, int n2) { + this.framebufferWidth = n; + this.framebufferHeight = n2; + } + + int readServerMessageType() throws IOException { + int n = this.is.readUnsignedByte(); + if (this.rec != null && n == 2) { + this.rec.writeByte(n); + if (this.numUpdatesInSession > 0) { + this.rec.flush(); + } + } + return n; + } + + void readFramebufferUpdate() throws IOException { + this.is.readByte(); + this.updateNRects = this.is.readUnsignedShort(); + if (this.rec != null) { + this.rec.writeByte(0); + this.rec.writeByte(0); + this.rec.writeShortBE(this.updateNRects); + } + ++this.numUpdatesInSession; + } + + void readFramebufferUpdateRectHdr() throws Exception { + this.updateRectX = this.is.readUnsignedShort(); + this.updateRectY = this.is.readUnsignedShort(); + this.updateRectW = this.is.readUnsignedShort(); + this.updateRectH = this.is.readUnsignedShort(); + this.updateRectEncoding = this.is.readInt(); + if (this.updateRectEncoding == 6 || this.updateRectEncoding == 16 || this.updateRectEncoding == 7) { + this.wereZlibUpdates = true; + } + if (this.rec != null) { + if (this.numUpdatesInSession > 1) { + this.rec.flush(); + } + this.rec.writeShortBE(this.updateRectX); + this.rec.writeShortBE(this.updateRectY); + this.rec.writeShortBE(this.updateRectW); + this.rec.writeShortBE(this.updateRectH); + if (this.updateRectEncoding == 6 && !this.recordFromBeginning) { + if (!this.zlibWarningShown) { + System.out.println("Warning: Raw encoding will be used instead of Zlib in recorded session."); + this.zlibWarningShown = true; + } + this.rec.writeIntBE(0); + } else { + this.rec.writeIntBE(this.updateRectEncoding); + if (this.updateRectEncoding == 7 && !this.recordFromBeginning && !this.tightWarningShown) { + System.out.println("Warning: Re-compressing Tight-encoded updates for session recording."); + this.tightWarningShown = true; + } + } + } + if (this.updateRectEncoding < 0 || this.updateRectEncoding > 255) { + return; + } + if (this.updateRectX + this.updateRectW > this.framebufferWidth || this.updateRectY + this.updateRectH > this.framebufferHeight) { + throw new Exception("Framebuffer update rectangle too large: " + this.updateRectW + "x" + this.updateRectH + " at (" + this.updateRectX + "," + this.updateRectY + ")"); + } + } + + void readCopyRect() throws IOException { + this.copyRectSrcX = this.is.readUnsignedShort(); + this.copyRectSrcY = this.is.readUnsignedShort(); + if (this.rec != null) { + this.rec.writeShortBE(this.copyRectSrcX); + this.rec.writeShortBE(this.copyRectSrcY); + } + } + + String readServerCutText() throws IOException { + byte[] byArray = new byte[3]; + this.readFully(byArray); + int n = this.is.readInt(); + byte[] byArray2 = new byte[n]; + this.readFully(byArray2); + return new String(byArray2); + } + + int readCompactLen() throws IOException { + int[] nArray = new int[3]; + nArray[0] = this.is.readUnsignedByte(); + int n = 1; + int n2 = nArray[0] & 0x7F; + if ((nArray[0] & 0x80) != 0) { + nArray[1] = this.is.readUnsignedByte(); + ++n; + n2 |= (nArray[1] & 0x7F) << 7; + if ((nArray[1] & 0x80) != 0) { + nArray[2] = this.is.readUnsignedByte(); + ++n; + n2 |= (nArray[2] & 0xFF) << 14; + } + } + if (this.rec != null && this.recordFromBeginning) { + for (int i = 0; i < n; ++i) { + this.rec.writeByte(nArray[i]); + } + } + return n2; + } + + void writeFramebufferUpdateRequest(int n, int n2, int n3, int n4, boolean bl) throws IOException { + byte[] byArray = new byte[]{3, (byte)(bl ? 1 : 0), (byte)(n >> 8 & 0xFF), (byte)(n & 0xFF), (byte)(n2 >> 8 & 0xFF), (byte)(n2 & 0xFF), (byte)(n3 >> 8 & 0xFF), (byte)(n3 & 0xFF), (byte)(n4 >> 8 & 0xFF), (byte)(n4 & 0xFF)}; + this.os.write(byArray); + } + + void writeSetPixelFormat(int n, int n2, boolean bl, boolean bl2, int n3, int n4, int n5, int n6, int n7, int n8) throws IOException { + byte[] byArray = new byte[20]; + byArray[0] = 0; + byArray[4] = (byte)n; + byArray[5] = (byte)n2; + byArray[6] = (byte)(bl ? 1 : 0); + byArray[7] = (byte)(bl2 ? 1 : 0); + byArray[8] = (byte)(n3 >> 8 & 0xFF); + byArray[9] = (byte)(n3 & 0xFF); + byArray[10] = (byte)(n4 >> 8 & 0xFF); + byArray[11] = (byte)(n4 & 0xFF); + byArray[12] = (byte)(n5 >> 8 & 0xFF); + byArray[13] = (byte)(n5 & 0xFF); + byArray[14] = (byte)n6; + byArray[15] = (byte)n7; + byArray[16] = (byte)n8; + this.os.write(byArray); + } + + void writeFixColourMapEntries(int n, int n2, int[] nArray, int[] nArray2, int[] nArray3) throws IOException { + byte[] byArray = new byte[6 + n2 * 6]; + byArray[0] = 1; + byArray[2] = (byte)(n >> 8 & 0xFF); + byArray[3] = (byte)(n & 0xFF); + byArray[4] = (byte)(n2 >> 8 & 0xFF); + byArray[5] = (byte)(n2 & 0xFF); + for (int i = 0; i < n2; ++i) { + byArray[6 + i * 6] = (byte)(nArray[i] >> 8 & 0xFF); + byArray[6 + i * 6 + 1] = (byte)(nArray[i] & 0xFF); + byArray[6 + i * 6 + 2] = (byte)(nArray2[i] >> 8 & 0xFF); + byArray[6 + i * 6 + 3] = (byte)(nArray2[i] & 0xFF); + byArray[6 + i * 6 + 4] = (byte)(nArray3[i] >> 8 & 0xFF); + byArray[6 + i * 6 + 5] = (byte)(nArray3[i] & 0xFF); + } + this.os.write(byArray); + } + + void writeSetEncodings(int[] nArray, int n) throws IOException { + byte[] byArray = new byte[4 + 4 * n]; + byArray[0] = 2; + byArray[2] = (byte)(n >> 8 & 0xFF); + byArray[3] = (byte)(n & 0xFF); + for (int i = 0; i < n; ++i) { + byArray[4 + 4 * i] = (byte)(nArray[i] >> 24 & 0xFF); + byArray[5 + 4 * i] = (byte)(nArray[i] >> 16 & 0xFF); + byArray[6 + 4 * i] = (byte)(nArray[i] >> 8 & 0xFF); + byArray[7 + 4 * i] = (byte)(nArray[i] & 0xFF); + } + this.os.write(byArray); + } + + void writeClientCutText(String string) throws IOException { + byte[] byArray = new byte[8 + string.length()]; + byArray[0] = 6; + byArray[4] = (byte)(string.length() >> 24 & 0xFF); + byArray[5] = (byte)(string.length() >> 16 & 0xFF); + byArray[6] = (byte)(string.length() >> 8 & 0xFF); + byArray[7] = (byte)(string.length() & 0xFF); + System.arraycopy(string.getBytes(), 0, byArray, 8, string.length()); + this.os.write(byArray); + } + + void writePointerEvent(MouseEvent mouseEvent) throws IOException { + int n = mouseEvent.getModifiers(); + int n2 = 2; + int n3 = 4; + if (this.viewer.options.reverseMouseButtons2And3) { + n2 = 4; + n3 = 2; + } + if (mouseEvent.getID() == 501) { + if ((n & 8) != 0) { + this.pointerMask = n2; + n &= 0xFFFFFFF7; + } else if ((n & 4) != 0) { + this.pointerMask = n3; + n &= 0xFFFFFFFB; + } else { + this.pointerMask = 1; + } + } else if (mouseEvent.getID() == 502) { + this.pointerMask = 0; + if ((n & 8) != 0) { + n &= 0xFFFFFFF7; + } else if ((n & 4) != 0) { + n &= 0xFFFFFFFB; + } + } + this.eventBufLen = 0; + this.writeModifierKeyEvents(n); + int n4 = mouseEvent.getX(); + int n5 = mouseEvent.getY(); + if (n4 < 0) { + n4 = 0; + } + if (n5 < 0) { + n5 = 0; + } + this.eventBuf[this.eventBufLen++] = 5; + this.eventBuf[this.eventBufLen++] = (byte)this.pointerMask; + this.eventBuf[this.eventBufLen++] = (byte)(n4 >> 8 & 0xFF); + this.eventBuf[this.eventBufLen++] = (byte)(n4 & 0xFF); + this.eventBuf[this.eventBufLen++] = (byte)(n5 >> 8 & 0xFF); + this.eventBuf[this.eventBufLen++] = (byte)(n5 & 0xFF); + if (this.pointerMask == 0) { + this.writeModifierKeyEvents(0); + } + this.os.write(this.eventBuf, 0, this.eventBufLen); + } + + void writeKeyEvent(KeyEvent keyEvent) throws IOException { + int n; + boolean bl; + block44: { + int n2; + block43: { + n2 = keyEvent.getKeyChar(); + if (n2 == 0) { + n2 = 65535; + } + if (n2 == 65535 && ((bl = keyEvent.getKeyCode()) || bl || bl || bl)) { + return; + } + boolean bl2 = bl = keyEvent.getID() == 401; + if (!keyEvent.isActionKey()) break block43; + switch (keyEvent.getKeyCode()) { + case 36: { + n = 65360; + break block44; + } + case 37: { + n = 65361; + break block44; + } + case 38: { + n = 65362; + break block44; + } + case 39: { + n = 65363; + break block44; + } + case 40: { + n = 65364; + break block44; + } + case 33: { + n = 65365; + break block44; + } + case 34: { + n = 65366; + break block44; + } + case 35: { + n = 65367; + break block44; + } + case 155: { + n = 65379; + break block44; + } + case 112: { + n = 65470; + break block44; + } + case 113: { + n = 65471; + break block44; + } + case 114: { + n = 65472; + break block44; + } + case 115: { + n = 65473; + break block44; + } + case 116: { + n = 65474; + break block44; + } + case 117: { + n = 65475; + break block44; + } + case 118: { + n = 65476; + break block44; + } + case 119: { + n = 65477; + break block44; + } + case 120: { + n = 65478; + break block44; + } + case 121: { + n = 65479; + break block44; + } + case 122: { + n = 65480; + break block44; + } + case 123: { + n = 65481; + break block44; + } + default: { + return; + } + } + } + n = n2; + if (n < 32) { + if (keyEvent.isControlDown()) { + n += 96; + } else { + switch (n) { + case 8: { + n = 65288; + break; + } + case 9: { + n = 65289; + break; + } + case 10: { + n = 65293; + break; + } + case 27: { + n = 65307; + } + } + } + } else if (n == 127) { + n = 65535; + } else if (!(n <= 255 || n >= 65280 && n <= 65535 || n >= 8352 && n <= 8367)) { + return; + } + } + if (n == 229 || n == 197 || n == 228 || n == 196 || n == 246 || n == 214 || n == 167 || n == 189 || n == 163) { + if (bl) { + this.brokenKeyPressed = true; + } + if (!bl && !this.brokenKeyPressed) { + this.eventBufLen = 0; + this.writeModifierKeyEvents(keyEvent.getModifiers()); + this.writeKeyEvent(n, true); + this.os.write(this.eventBuf, 0, this.eventBufLen); + } + if (!bl) { + this.brokenKeyPressed = false; + } + } + this.eventBufLen = 0; + this.writeModifierKeyEvents(keyEvent.getModifiers()); + this.writeKeyEvent(n, bl); + if (!bl) { + this.writeModifierKeyEvents(0); + } + this.os.write(this.eventBuf, 0, this.eventBufLen); + } + + void writeKeyEvent(int n, boolean bl) { + this.eventBuf[this.eventBufLen++] = 4; + this.eventBuf[this.eventBufLen++] = (byte)(bl ? 1 : 0); + this.eventBuf[this.eventBufLen++] = 0; + this.eventBuf[this.eventBufLen++] = 0; + this.eventBuf[this.eventBufLen++] = (byte)(n >> 24 & 0xFF); + this.eventBuf[this.eventBufLen++] = (byte)(n >> 16 & 0xFF); + this.eventBuf[this.eventBufLen++] = (byte)(n >> 8 & 0xFF); + this.eventBuf[this.eventBufLen++] = (byte)(n & 0xFF); + } + + void writeModifierKeyEvents(int n) { + if ((n & 2) != (this.oldModifiers & 2)) { + this.writeKeyEvent(65507, (n & 2) != 0); + } + if ((n & 1) != (this.oldModifiers & 1)) { + this.writeKeyEvent(65505, (n & 1) != 0); + } + if ((n & 4) != (this.oldModifiers & 4)) { + this.writeKeyEvent(65511, (n & 4) != 0); + } + if ((n & 8) != (this.oldModifiers & 8)) { + this.writeKeyEvent(65513, (n & 8) != 0); + } + this.oldModifiers = n; + } + + void recordCompressedData(byte[] byArray, int n, int n2) throws IOException { + Deflater deflater = new Deflater(); + deflater.setInput(byArray, n, n2); + int n3 = n2 + n2 / 100 + 12; + byte[] byArray2 = new byte[n3]; + deflater.finish(); + int n4 = deflater.deflate(byArray2); + this.recordCompactLen(n4); + this.rec.write(byArray2, 0, n4); + } + + void recordCompressedData(byte[] byArray) throws IOException { + this.recordCompressedData(byArray, 0, byArray.length); + } + + void recordCompactLen(int n) throws IOException { + byte[] byArray = new byte[3]; + int n2 = 0; + byArray[n2++] = (byte)(n & 0x7F); + if (n > 127) { + int n3 = n2 - 1; + byArray[n3] = (byte)(byArray[n3] | 0x80); + byArray[n2++] = (byte)(n >> 7 & 0x7F); + if (n > 16383) { + int n4 = n2 - 1; + byArray[n4] = (byte)(byArray[n4] | 0x80); + byArray[n2++] = (byte)(n >> 14 & 0xFF); + } + } + this.rec.write(byArray, 0, n2); + } + + public void startTiming() { + this.timing = true; + if (this.timeWaitedIn100us > 10000L) { + this.timedKbits = this.timedKbits * 10000L / this.timeWaitedIn100us; + this.timeWaitedIn100us = 10000L; + } + } + + public void stopTiming() { + this.timing = false; + if (this.timeWaitedIn100us < this.timedKbits / 2L) { + this.timeWaitedIn100us = this.timedKbits / 2L; + } + } + + public long kbitsPerSecond() { + return this.timedKbits * 10000L / this.timeWaitedIn100us; + } + + public long timeWaited() { + return this.timeWaitedIn100us; + } + + public void readFully(byte[] byArray) throws IOException { + this.readFully(byArray, 0, byArray.length); + } + + public void readFully(byte[] byArray, int n, int n2) throws IOException { + long l = 0L; + if (this.timing) { + l = System.currentTimeMillis(); + } + this.is.readFully(byArray, n, n2); + if (this.timing) { + int n3; + long l2 = System.currentTimeMillis(); + long l3 = (l2 - l) * 10L; + if (l3 > (long)((n3 = n2 * 8 / 1000) * 1000)) { + l3 = n3 * 1000; + } + if (l3 < (long)(n3 / 4)) { + l3 = n3 / 4; + } + this.timeWaitedIn100us += l3; + this.timedKbits += (long)n3; + } + } +} + diff --git a/src/SessionRecorder.java b/src/SessionRecorder.java new file mode 100644 index 0000000..a638658 --- /dev/null +++ b/src/SessionRecorder.java @@ -0,0 +1,126 @@ +/* + * Decompiled with CFR 0.152. + */ +import java.io.DataOutputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +class SessionRecorder { + protected FileOutputStream f; + protected DataOutputStream df; + protected long startTime; + protected long lastTimeOffset; + protected byte[] buffer; + protected int bufferSize; + protected int bufferBytes; + + public SessionRecorder(String string, int n) throws IOException { + this.f = new FileOutputStream(string); + this.df = new DataOutputStream(this.f); + this.startTime = System.currentTimeMillis(); + this.lastTimeOffset = 0L; + this.bufferSize = n; + this.bufferBytes = 0; + this.buffer = new byte[this.bufferSize]; + } + + public SessionRecorder(String string) throws IOException { + this(string, 65536); + } + + public void close() throws IOException { + try { + this.flush(); + } + catch (IOException iOException) { + // empty catch block + } + this.df = null; + this.f.close(); + this.f = null; + this.buffer = null; + } + + public void writeHeader() throws IOException { + this.df.write("FBS 001.000\n".getBytes()); + } + + public void writeByte(int n) throws IOException { + this.prepareWriting(); + this.buffer[this.bufferBytes++] = (byte)n; + } + + public void writeShortBE(int n) throws IOException { + this.prepareWriting(); + this.buffer[this.bufferBytes++] = (byte)(n >> 8); + this.buffer[this.bufferBytes++] = (byte)n; + } + + public void writeIntBE(int n) throws IOException { + this.prepareWriting(); + this.buffer[this.bufferBytes] = (byte)(n >> 24); + this.buffer[this.bufferBytes + 1] = (byte)(n >> 16); + this.buffer[this.bufferBytes + 2] = (byte)(n >> 8); + this.buffer[this.bufferBytes + 3] = (byte)n; + this.bufferBytes += 4; + } + + public void writeShortLE(int n) throws IOException { + this.prepareWriting(); + this.buffer[this.bufferBytes++] = (byte)n; + this.buffer[this.bufferBytes++] = (byte)(n >> 8); + } + + public void writeIntLE(int n) throws IOException { + this.prepareWriting(); + this.buffer[this.bufferBytes] = (byte)n; + this.buffer[this.bufferBytes + 1] = (byte)(n >> 8); + this.buffer[this.bufferBytes + 2] = (byte)(n >> 16); + this.buffer[this.bufferBytes + 3] = (byte)(n >> 24); + this.bufferBytes += 4; + } + + public void write(byte[] byArray, int n, int n2) throws IOException { + this.prepareWriting(); + while (n2 > 0) { + if (this.bufferBytes > this.bufferSize - 4) { + this.flush(false); + } + int n3 = this.bufferBytes + n2 > this.bufferSize ? this.bufferSize - this.bufferBytes : n2; + System.arraycopy(byArray, n, this.buffer, this.bufferBytes, n3); + this.bufferBytes += n3; + n += n3; + n2 -= n3; + } + } + + public void write(byte[] byArray) throws IOException { + this.write(byArray, 0, byArray.length); + } + + public void flush(boolean bl) throws IOException { + if (this.bufferBytes > 0) { + this.df.writeInt(this.bufferBytes); + this.df.write(this.buffer, 0, this.bufferBytes + 3 & 0x7FFFFFFC); + this.df.writeInt((int)this.lastTimeOffset); + this.bufferBytes = 0; + if (bl) { + this.lastTimeOffset = -1L; + } + } + } + + public void flush() throws IOException { + this.flush(true); + } + + protected void prepareWriting() throws IOException { + if (this.lastTimeOffset == -1L) { + this.lastTimeOffset = System.currentTimeMillis() - this.startTime; + } + if (this.bufferBytes > this.bufferSize - 4) { + this.flush(false); + } + } +} + diff --git a/src/SocketFactory.java b/src/SocketFactory.java new file mode 100644 index 0000000..81cddba --- /dev/null +++ b/src/SocketFactory.java @@ -0,0 +1,13 @@ +/* + * Decompiled with CFR 0.152. + */ +import java.applet.Applet; +import java.io.IOException; +import java.net.Socket; + +public interface SocketFactory { + public Socket createSocket(String var1, int var2, Applet var3) throws IOException; + + public Socket createSocket(String var1, int var2, String[] var3) throws IOException; +} + diff --git a/src/VncCanvas.java b/src/VncCanvas.java new file mode 100644 index 0000000..71241dd --- /dev/null +++ b/src/VncCanvas.java @@ -0,0 +1,1352 @@ +/* + * Decompiled with CFR 0.152. + */ +import java.awt.Canvas; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.Insets; +import java.awt.Rectangle; +import java.awt.Toolkit; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.image.ColorModel; +import java.awt.image.DirectColorModel; +import java.awt.image.MemoryImageSource; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.util.zip.Inflater; + +class VncCanvas +extends Canvas +implements KeyListener, +MouseListener, +MouseMotionListener { + VncViewer viewer; + RfbProto rfb; + ColorModel cm8; + ColorModel cm24; + Color[] colors; + int bytesPixel; + int maxWidth = 0; + int maxHeight = 0; + int scalingFactor; + int scaledWidth; + int scaledHeight; + Image memImage; + Graphics memGraphics; + Image rawPixelsImage; + MemoryImageSource pixelsSource; + byte[] pixels8; + int[] pixels24; + byte[] zrleBuf; + int zrleBufLen = 0; + byte[] zrleTilePixels8; + int[] zrleTilePixels24; + ZlibInStream zrleInStream; + boolean zrleRecWarningShown = false; + byte[] zlibBuf; + int zlibBufLen = 0; + Inflater zlibInflater; + static final int tightZlibBufferSize = 512; + Inflater[] tightInflaters; + Rectangle jpegRect; + boolean inputEnabled; + private Color hextile_bg; + private Color hextile_fg; + boolean showSoftCursor = false; + MemoryImageSource softCursorSource; + Image softCursor; + int cursorX = 0; + int cursorY = 0; + int cursorWidth; + int cursorHeight; + int origCursorWidth; + int origCursorHeight; + int hotX; + int hotY; + int origHotX; + int origHotY; + + public VncCanvas(VncViewer vncViewer, int n, int n2) throws IOException { + this.viewer = vncViewer; + this.maxWidth = n; + this.maxHeight = n2; + this.rfb = this.viewer.rfb; + this.scalingFactor = this.viewer.options.scalingFactor; + this.tightInflaters = new Inflater[4]; + this.cm8 = new DirectColorModel(8, 7, 56, 192); + this.cm24 = new DirectColorModel(24, 0xFF0000, 65280, 255); + this.colors = new Color[256]; + for (int i = 0; i < 256; ++i) { + this.colors[i] = new Color(this.cm8.getRGB(i)); + } + this.setPixelFormat(); + this.inputEnabled = false; + if (!this.viewer.options.viewOnly) { + this.enableInput(true); + } + this.addKeyListener(this); + } + + public VncCanvas(VncViewer vncViewer) throws IOException { + this(vncViewer, 0, 0); + } + + public Dimension getPreferredSize() { + return new Dimension(this.scaledWidth, this.scaledHeight); + } + + public Dimension getMinimumSize() { + return new Dimension(this.scaledWidth, this.scaledHeight); + } + + public Dimension getMaximumSize() { + return new Dimension(this.scaledWidth, this.scaledHeight); + } + + public void update(Graphics graphics) { + this.paint(graphics); + } + + /* + * WARNING - Removed try catching itself - possible behaviour change. + */ + public void paint(Graphics graphics) { + int n; + int n2; + Rectangle rectangle; + Image image = this.memImage; + synchronized (image) { + if (this.rfb.framebufferWidth == this.scaledWidth) { + graphics.drawImage(this.memImage, 0, 0, null); + } else { + this.paintScaledFrameBuffer(graphics); + } + } + if (this.showSoftCursor && (rectangle = new Rectangle(n2 = this.cursorX - this.hotX, n = this.cursorY - this.hotY, this.cursorWidth, this.cursorHeight)).intersects(graphics.getClipBounds())) { + graphics.drawImage(this.softCursor, n2, n, null); + } + } + + public void paintScaledFrameBuffer(Graphics graphics) { + graphics.drawImage(this.memImage, 0, 0, this.scaledWidth, this.scaledHeight, null); + } + + /* + * WARNING - Removed try catching itself - possible behaviour change. + */ + public boolean imageUpdate(Image image, int n, int n2, int n3, int n4, int n5) { + if ((n & 0xA0) == 0) { + return true; + } + if ((n & 0x20) != 0 && this.jpegRect != null) { + Rectangle rectangle = this.jpegRect; + synchronized (rectangle) { + this.memGraphics.drawImage(image, this.jpegRect.x, this.jpegRect.y, null); + this.scheduleRepaint(this.jpegRect.x, this.jpegRect.y, this.jpegRect.width, this.jpegRect.height); + this.jpegRect.notify(); + } + } + return false; + } + + public synchronized void enableInput(boolean bl) { + if (bl && !this.inputEnabled) { + this.inputEnabled = true; + this.addMouseListener(this); + this.addMouseMotionListener(this); + if (this.viewer.showControls) { + this.viewer.buttonPanel.enableRemoteAccessControls(true); + } + this.createSoftCursor(); + } else if (!bl && this.inputEnabled) { + this.inputEnabled = false; + this.removeMouseListener(this); + this.removeMouseMotionListener(this); + if (this.viewer.showControls) { + this.viewer.buttonPanel.enableRemoteAccessControls(false); + } + this.createSoftCursor(); + } + } + + public void setPixelFormat() throws IOException { + if (this.viewer.options.eightBitColors) { + this.rfb.writeSetPixelFormat(8, 8, false, true, 7, 7, 3, 0, 3, 6); + this.bytesPixel = 1; + } else { + this.rfb.writeSetPixelFormat(32, 24, false, true, 255, 255, 255, 16, 8, 0); + this.bytesPixel = 4; + } + this.updateFramebufferSize(); + } + + /* + * WARNING - Removed try catching itself - possible behaviour change. + */ + void updateFramebufferSize() { + int n = this.rfb.framebufferWidth; + int n2 = this.rfb.framebufferHeight; + if (this.maxWidth > 0 && this.maxHeight > 0) { + int n3 = this.maxWidth * 100 / n; + int n4 = this.maxHeight * 100 / n2; + this.scalingFactor = Math.min(n3, n4); + if (this.scalingFactor > 100) { + this.scalingFactor = 100; + } + System.out.println("Scaling desktop at " + this.scalingFactor + "%"); + } + this.scaledWidth = (n * this.scalingFactor + 50) / 100; + this.scaledHeight = (n2 * this.scalingFactor + 50) / 100; + if (this.memImage == null) { + this.memImage = this.viewer.vncContainer.createImage(n, n2); + this.memGraphics = this.memImage.getGraphics(); + } else if (this.memImage.getWidth(null) != n || this.memImage.getHeight(null) != n2) { + Image image = this.memImage; + synchronized (image) { + this.memImage = this.viewer.vncContainer.createImage(n, n2); + this.memGraphics = this.memImage.getGraphics(); + } + } + if (this.bytesPixel == 1) { + this.pixels24 = null; + this.pixels8 = new byte[n * n2]; + this.pixelsSource = new MemoryImageSource(n, n2, this.cm8, this.pixels8, 0, n); + this.zrleTilePixels24 = null; + this.zrleTilePixels8 = new byte[4096]; + } else { + this.pixels8 = null; + this.pixels24 = new int[n * n2]; + this.pixelsSource = new MemoryImageSource(n, n2, this.cm24, this.pixels24, 0, n); + this.zrleTilePixels8 = null; + this.zrleTilePixels24 = new int[4096]; + } + this.pixelsSource.setAnimated(true); + this.rawPixelsImage = Toolkit.getDefaultToolkit().createImage(this.pixelsSource); + if (this.viewer.inSeparateFrame) { + if (this.viewer.desktopScrollPane != null) { + this.resizeDesktopFrame(); + } + } else { + this.setSize(this.scaledWidth, this.scaledHeight); + } + this.viewer.moveFocusToDesktop(); + } + + void resizeDesktopFrame() { + Dimension dimension; + this.setSize(this.scaledWidth, this.scaledHeight); + Insets insets = this.viewer.desktopScrollPane.getInsets(); + this.viewer.desktopScrollPane.setSize(this.scaledWidth + 2 * Math.min(insets.left, insets.right), this.scaledHeight + 2 * Math.min(insets.top, insets.bottom)); + this.viewer.vncFrame.pack(); + Dimension dimension2 = this.viewer.vncFrame.getToolkit().getScreenSize(); + Dimension dimension3 = dimension = this.viewer.vncFrame.getSize(); + dimension2.height -= 30; + dimension2.width -= 30; + boolean bl = false; + if (dimension.height > dimension2.height) { + dimension3.height = dimension2.height; + bl = true; + } + if (dimension.width > dimension2.width) { + dimension3.width = dimension2.width; + bl = true; + } + if (bl) { + ((Component)this.viewer.vncFrame).setSize(dimension3); + } + this.viewer.desktopScrollPane.doLayout(); + } + + /* + * WARNING - Removed try catching itself - possible behaviour change. + * Unable to fully structure code + */ + public void processNormalProtocol() throws Exception { + this.viewer.checkRecordingStatus(); + this.rfb.writeFramebufferUpdateRequest(0, 0, this.rfb.framebufferWidth, this.rfb.framebufferHeight, false); + block21: while (true) { + var1_1 = this.rfb.readServerMessageType(); + switch (var1_1) { + case 0: { + this.rfb.readFramebufferUpdate(); + var2_2 = false; + for (var3_3 = 0; var3_3 < this.rfb.updateNRects; ++var3_3) { + this.rfb.readFramebufferUpdateRectHdr(); + var4_4 = this.rfb.updateRectX; + var5_7 = this.rfb.updateRectY; + var6_9 = this.rfb.updateRectW; + var7_10 = this.rfb.updateRectH; + if (this.rfb.updateRectEncoding == -224) break; + if (this.rfb.updateRectEncoding == -223) { + this.rfb.setFramebufferSize(var6_9, var7_10); + this.updateFramebufferSize(); + break; + } + if (this.rfb.updateRectEncoding == -240) ** GOTO lbl26 + if (this.rfb.updateRectEncoding != -239) ** GOTO lbl28 +lbl26: + // 2 sources + + this.handleCursorShapeUpdate(this.rfb.updateRectEncoding, var4_4, var5_7, var6_9, var7_10); + continue; +lbl28: + // 1 sources + + if (this.rfb.updateRectEncoding == -232) { + this.softCursorMove(var4_4, var5_7); + var2_2 = true; + continue; + } + this.rfb.startTiming(); + switch (this.rfb.updateRectEncoding) { + case 0: { + this.handleRawRect(var4_4, var5_7, var6_9, var7_10); + break; + } + case 1: { + this.handleCopyRect(var4_4, var5_7, var6_9, var7_10); + break; + } + case 2: { + this.handleRRERect(var4_4, var5_7, var6_9, var7_10); + break; + } + case 4: { + this.handleCoRRERect(var4_4, var5_7, var6_9, var7_10); + break; + } + case 5: { + this.handleHextileRect(var4_4, var5_7, var6_9, var7_10); + break; + } + case 16: { + this.handleZRLERect(var4_4, var5_7, var6_9, var7_10); + break; + } + case 6: { + this.handleZlibRect(var4_4, var5_7, var6_9, var7_10); + break; + } + case 7: { + this.handleTightRect(var4_4, var5_7, var6_9, var7_10); + break; + } + default: { + throw new Exception("Unknown RFB rectangle encoding " + this.rfb.updateRectEncoding); + } + } + this.rfb.stopTiming(); + } + var3_3 = 0; + if (this.viewer.checkRecordingStatus()) { + var3_3 = 1; + } + if (this.viewer.deferUpdateRequests > 0 && this.rfb.is.available() == 0 && !var2_2) { + var4_5 = this.rfb; + synchronized (var4_5) { + try { + this.rfb.wait(this.viewer.deferUpdateRequests); + } + catch (InterruptedException var5_8) { + // empty catch block + } + } + } + if (this.viewer.options.eightBitColors != (this.bytesPixel == 1)) { + this.setPixelFormat(); + var3_3 = 1; + } + this.viewer.autoSelectEncodings(); + this.rfb.writeFramebufferUpdateRequest(0, 0, this.rfb.framebufferWidth, this.rfb.framebufferHeight, var3_3 == 0); + continue block21; + } + case 1: { + throw new Exception("Can't handle SetColourMapEntries message"); + } + case 2: { + Toolkit.getDefaultToolkit().beep(); + continue block21; + } + case 3: { + var4_6 = this.rfb.readServerCutText(); + this.viewer.clipboard.setCutText(var4_6); + continue block21; + } + } + break; + } + throw new Exception("Unknown RFB message type " + var1_1); + } + + void handleRawRect(int n, int n2, int n3, int n4) throws IOException { + this.handleRawRect(n, n2, n3, n4, true); + } + + void handleRawRect(int n, int n2, int n3, int n4, boolean bl) throws IOException { + if (this.bytesPixel == 1) { + for (int i = n2; i < n2 + n4; ++i) { + this.rfb.readFully(this.pixels8, i * this.rfb.framebufferWidth + n, n3); + if (this.rfb.rec == null) continue; + this.rfb.rec.write(this.pixels8, i * this.rfb.framebufferWidth + n, n3); + } + } else { + byte[] byArray = new byte[n3 * 4]; + for (int i = n2; i < n2 + n4; ++i) { + this.rfb.readFully(byArray); + if (this.rfb.rec != null) { + this.rfb.rec.write(byArray); + } + int n5 = i * this.rfb.framebufferWidth + n; + for (int j = 0; j < n3; ++j) { + this.pixels24[n5 + j] = (byArray[j * 4 + 2] & 0xFF) << 16 | (byArray[j * 4 + 1] & 0xFF) << 8 | byArray[j * 4] & 0xFF; + } + } + } + this.handleUpdatedPixels(n, n2, n3, n4); + if (bl) { + this.scheduleRepaint(n, n2, n3, n4); + } + } + + void handleCopyRect(int n, int n2, int n3, int n4) throws IOException { + this.rfb.readCopyRect(); + this.memGraphics.copyArea(this.rfb.copyRectSrcX, this.rfb.copyRectSrcY, n3, n4, n - this.rfb.copyRectSrcX, n2 - this.rfb.copyRectSrcY); + this.scheduleRepaint(n, n2, n3, n4); + } + + void handleRRERect(int n, int n2, int n3, int n4) throws IOException { + int n5 = this.rfb.is.readInt(); + byte[] byArray = new byte[this.bytesPixel]; + this.rfb.readFully(byArray); + Color color = this.bytesPixel == 1 ? this.colors[byArray[0] & 0xFF] : new Color(byArray[2] & 0xFF, byArray[1] & 0xFF, byArray[0] & 0xFF); + this.memGraphics.setColor(color); + this.memGraphics.fillRect(n, n2, n3, n4); + byte[] byArray2 = new byte[n5 * (this.bytesPixel + 8)]; + this.rfb.readFully(byArray2); + DataInputStream dataInputStream = new DataInputStream(new ByteArrayInputStream(byArray2)); + if (this.rfb.rec != null) { + this.rfb.rec.writeIntBE(n5); + this.rfb.rec.write(byArray); + this.rfb.rec.write(byArray2); + } + for (int i = 0; i < n5; ++i) { + if (this.bytesPixel == 1) { + color = this.colors[dataInputStream.readUnsignedByte()]; + } else { + dataInputStream.skip(4L); + color = new Color(byArray2[i * 12 + 2] & 0xFF, byArray2[i * 12 + 1] & 0xFF, byArray2[i * 12] & 0xFF); + } + int n6 = n + dataInputStream.readUnsignedShort(); + int n7 = n2 + dataInputStream.readUnsignedShort(); + int n8 = dataInputStream.readUnsignedShort(); + int n9 = dataInputStream.readUnsignedShort(); + this.memGraphics.setColor(color); + this.memGraphics.fillRect(n6, n7, n8, n9); + } + this.scheduleRepaint(n, n2, n3, n4); + } + + void handleCoRRERect(int n, int n2, int n3, int n4) throws IOException { + int n5 = this.rfb.is.readInt(); + byte[] byArray = new byte[this.bytesPixel]; + this.rfb.readFully(byArray); + Color color = this.bytesPixel == 1 ? this.colors[byArray[0] & 0xFF] : new Color(byArray[2] & 0xFF, byArray[1] & 0xFF, byArray[0] & 0xFF); + this.memGraphics.setColor(color); + this.memGraphics.fillRect(n, n2, n3, n4); + byte[] byArray2 = new byte[n5 * (this.bytesPixel + 4)]; + this.rfb.readFully(byArray2); + if (this.rfb.rec != null) { + this.rfb.rec.writeIntBE(n5); + this.rfb.rec.write(byArray); + this.rfb.rec.write(byArray2); + } + int n6 = 0; + for (int i = 0; i < n5; ++i) { + if (this.bytesPixel == 1) { + color = this.colors[byArray2[n6++] & 0xFF]; + } else { + color = new Color(byArray2[n6 + 2] & 0xFF, byArray2[n6 + 1] & 0xFF, byArray2[n6] & 0xFF); + n6 += 4; + } + int n7 = n + (byArray2[n6++] & 0xFF); + int n8 = n2 + (byArray2[n6++] & 0xFF); + int n9 = byArray2[n6++] & 0xFF; + int n10 = byArray2[n6++] & 0xFF; + this.memGraphics.setColor(color); + this.memGraphics.fillRect(n7, n8, n9, n10); + } + this.scheduleRepaint(n, n2, n3, n4); + } + + void handleHextileRect(int n, int n2, int n3, int n4) throws IOException { + this.hextile_bg = new Color(0); + this.hextile_fg = new Color(0); + for (int i = n2; i < n2 + n4; i += 16) { + int n5 = 16; + if (n2 + n4 - i < 16) { + n5 = n2 + n4 - i; + } + for (int j = n; j < n + n3; j += 16) { + int n6 = 16; + if (n + n3 - j < 16) { + n6 = n + n3 - j; + } + this.handleHextileSubrect(j, i, n6, n5); + } + this.scheduleRepaint(n, n2, n3, n4); + } + } + + void handleHextileSubrect(int n, int n2, int n3, int n4) throws IOException { + int n5 = this.rfb.is.readUnsignedByte(); + if (this.rfb.rec != null) { + this.rfb.rec.writeByte(n5); + } + if ((n5 & 1) != 0) { + this.handleRawRect(n, n2, n3, n4, false); + return; + } + byte[] byArray = new byte[this.bytesPixel]; + if ((n5 & 2) != 0) { + this.rfb.readFully(byArray); + this.hextile_bg = this.bytesPixel == 1 ? this.colors[byArray[0] & 0xFF] : new Color(byArray[2] & 0xFF, byArray[1] & 0xFF, byArray[0] & 0xFF); + if (this.rfb.rec != null) { + this.rfb.rec.write(byArray); + } + } + this.memGraphics.setColor(this.hextile_bg); + this.memGraphics.fillRect(n, n2, n3, n4); + if ((n5 & 4) != 0) { + this.rfb.readFully(byArray); + this.hextile_fg = this.bytesPixel == 1 ? this.colors[byArray[0] & 0xFF] : new Color(byArray[2] & 0xFF, byArray[1] & 0xFF, byArray[0] & 0xFF); + if (this.rfb.rec != null) { + this.rfb.rec.write(byArray); + } + } + if ((n5 & 8) == 0) { + return; + } + int n6 = this.rfb.is.readUnsignedByte(); + int n7 = n6 * 2; + if ((n5 & 0x10) != 0) { + n7 += n6 * this.bytesPixel; + } + byte[] byArray2 = new byte[n7]; + this.rfb.readFully(byArray2); + if (this.rfb.rec != null) { + this.rfb.rec.writeByte(n6); + this.rfb.rec.write(byArray2); + } + int n8 = 0; + if ((n5 & 0x10) == 0) { + this.memGraphics.setColor(this.hextile_fg); + for (int i = 0; i < n6; ++i) { + int n9 = byArray2[n8++] & 0xFF; + int n10 = byArray2[n8++] & 0xFF; + int n11 = n + (n9 >> 4); + int n12 = n2 + (n9 & 0xF); + int n13 = (n10 >> 4) + 1; + int n14 = (n10 & 0xF) + 1; + this.memGraphics.fillRect(n11, n12, n13, n14); + } + } else if (this.bytesPixel == 1) { + for (int i = 0; i < n6; ++i) { + this.hextile_fg = this.colors[byArray2[n8++] & 0xFF]; + int n15 = byArray2[n8++] & 0xFF; + int n16 = byArray2[n8++] & 0xFF; + int n17 = n + (n15 >> 4); + int n18 = n2 + (n15 & 0xF); + int n19 = (n16 >> 4) + 1; + int n20 = (n16 & 0xF) + 1; + this.memGraphics.setColor(this.hextile_fg); + this.memGraphics.fillRect(n17, n18, n19, n20); + } + } else { + for (int i = 0; i < n6; ++i) { + this.hextile_fg = new Color(byArray2[n8 + 2] & 0xFF, byArray2[n8 + 1] & 0xFF, byArray2[n8] & 0xFF); + n8 += 4; + int n21 = byArray2[n8++] & 0xFF; + int n22 = byArray2[n8++] & 0xFF; + int n23 = n + (n21 >> 4); + int n24 = n2 + (n21 & 0xF); + int n25 = (n22 >> 4) + 1; + int n26 = (n22 & 0xF) + 1; + this.memGraphics.setColor(this.hextile_fg); + this.memGraphics.fillRect(n23, n24, n25, n26); + } + } + } + + void handleZRLERect(int n, int n2, int n3, int n4) throws Exception { + int n5; + if (this.zrleInStream == null) { + this.zrleInStream = new ZlibInStream(); + } + if ((n5 = this.rfb.is.readInt()) > 0x4000000) { + throw new Exception("ZRLE decoder: illegal compressed data size"); + } + if (this.zrleBuf == null || this.zrleBufLen < n5) { + this.zrleBufLen = n5 + 4096; + this.zrleBuf = new byte[this.zrleBufLen]; + } + this.rfb.readFully(this.zrleBuf, 0, n5); + if (this.rfb.rec != null) { + if (this.rfb.recordFromBeginning) { + this.rfb.rec.writeIntBE(n5); + this.rfb.rec.write(this.zrleBuf, 0, n5); + } else if (!this.zrleRecWarningShown) { + System.out.println("Warning: ZRLE session can be recorded only from the beginning"); + System.out.println("Warning: Recorded file may be corrupted"); + this.zrleRecWarningShown = true; + } + } + this.zrleInStream.setUnderlying(new MemInStream(this.zrleBuf, 0, n5), n5); + for (int i = n2; i < n2 + n4; i += 64) { + int n6 = Math.min(n2 + n4 - i, 64); + for (int j = n; j < n + n3; j += 64) { + int n7 = Math.min(n + n3 - j, 64); + int n8 = this.zrleInStream.readU8(); + boolean bl = (n8 & 0x80) != 0; + int n9 = n8 & 0x7F; + int[] nArray = new int[128]; + this.readZrlePalette(nArray, n9); + if (n9 == 1) { + int n10 = nArray[0]; + Color color = this.bytesPixel == 1 ? this.colors[n10] : new Color(0xFF000000 | n10); + this.memGraphics.setColor(color); + this.memGraphics.fillRect(j, i, n7, n6); + continue; + } + if (!bl) { + if (n9 == 0) { + this.readZrleRawPixels(n7, n6); + } else { + this.readZrlePackedPixels(n7, n6, nArray, n9); + } + } else if (n9 == 0) { + this.readZrlePlainRLEPixels(n7, n6); + } else { + this.readZrlePackedRLEPixels(n7, n6, nArray); + } + this.handleUpdatedZrleTile(j, i, n7, n6); + } + } + this.zrleInStream.reset(); + this.scheduleRepaint(n, n2, n3, n4); + } + + int readPixel(InStream inStream) throws Exception { + int n; + if (this.bytesPixel == 1) { + n = inStream.readU8(); + } else { + int n2 = inStream.readU8(); + int n3 = inStream.readU8(); + int n4 = inStream.readU8(); + n = (n4 & 0xFF) << 16 | (n3 & 0xFF) << 8 | n2 & 0xFF; + } + return n; + } + + void readPixels(InStream inStream, int[] nArray, int n) throws Exception { + if (this.bytesPixel == 1) { + byte[] byArray = new byte[n]; + inStream.readBytes(byArray, 0, n); + for (int i = 0; i < n; ++i) { + nArray[i] = byArray[i] & 0xFF; + } + } else { + byte[] byArray = new byte[n * 3]; + inStream.readBytes(byArray, 0, n * 3); + for (int i = 0; i < n; ++i) { + nArray[i] = (byArray[i * 3 + 2] & 0xFF) << 16 | (byArray[i * 3 + 1] & 0xFF) << 8 | byArray[i * 3] & 0xFF; + } + } + } + + void readZrlePalette(int[] nArray, int n) throws Exception { + this.readPixels(this.zrleInStream, nArray, n); + } + + void readZrleRawPixels(int n, int n2) throws Exception { + if (this.bytesPixel == 1) { + this.zrleInStream.readBytes(this.zrleTilePixels8, 0, n * n2); + } else { + this.readPixels(this.zrleInStream, this.zrleTilePixels24, n * n2); + } + } + + void readZrlePackedPixels(int n, int n2, int[] nArray, int n3) throws Exception { + int n4 = n3 > 16 ? 8 : (n3 > 4 ? 4 : (n3 > 2 ? 2 : 1)); + int n5 = 0; + for (int i = 0; i < n2; ++i) { + int n6 = n5 + n; + int n7 = 0; + int n8 = 0; + while (n5 < n6) { + if (n8 == 0) { + n7 = this.zrleInStream.readU8(); + n8 = 8; + } + int n9 = n7 >> (n8 -= n4) & (1 << n4) - 1 & 0x7F; + if (this.bytesPixel == 1) { + this.zrleTilePixels8[n5++] = (byte)nArray[n9]; + continue; + } + this.zrleTilePixels24[n5++] = nArray[n9]; + } + } + } + + void readZrlePlainRLEPixels(int n, int n2) throws Exception { + int n3 = 0; + int n4 = n3 + n * n2; + while (n3 < n4) { + int n5; + int n6 = this.readPixel(this.zrleInStream); + int n7 = 1; + do { + n5 = this.zrleInStream.readU8(); + n7 += n5; + } while (n5 == 255); + if (n7 > n4 - n3) { + throw new Exception("ZRLE decoder: assertion failed (len <= end-ptr)"); + } + if (this.bytesPixel == 1) { + while (n7-- > 0) { + this.zrleTilePixels8[n3++] = (byte)n6; + } + continue; + } + while (n7-- > 0) { + this.zrleTilePixels24[n3++] = n6; + } + } + } + + void readZrlePackedRLEPixels(int n, int n2, int[] nArray) throws Exception { + int n3 = 0; + int n4 = n3 + n * n2; + while (n3 < n4) { + int n5; + int n6 = this.zrleInStream.readU8(); + int n7 = 1; + if ((n6 & 0x80) != 0) { + do { + n5 = this.zrleInStream.readU8(); + n7 += n5; + } while (n5 == 255); + if (n7 > n4 - n3) { + throw new Exception("ZRLE decoder: assertion failed (len <= end - ptr)"); + } + } + n5 = nArray[n6 &= 0x7F]; + if (this.bytesPixel == 1) { + while (n7-- > 0) { + this.zrleTilePixels8[n3++] = (byte)n5; + } + continue; + } + while (n7-- > 0) { + this.zrleTilePixels24[n3++] = n5; + } + } + } + + void handleUpdatedZrleTile(int n, int n2, int n3, int n4) { + Object[] objectArray; + Object[] objectArray2; + if (this.bytesPixel == 1) { + objectArray2 = this.zrleTilePixels8; + objectArray = this.pixels8; + } else { + objectArray2 = this.zrleTilePixels24; + objectArray = this.pixels24; + } + int n5 = 0; + int n6 = n2 * this.rfb.framebufferWidth + n; + for (int i = 0; i < n4; ++i) { + System.arraycopy(objectArray2, n5, objectArray, n6, n3); + n5 += n3; + n6 += this.rfb.framebufferWidth; + } + this.handleUpdatedPixels(n, n2, n3, n4); + } + + void handleZlibRect(int n, int n2, int n3, int n4) throws Exception { + int n5 = this.rfb.is.readInt(); + if (this.zlibBuf == null || this.zlibBufLen < n5) { + this.zlibBufLen = n5 * 2; + this.zlibBuf = new byte[this.zlibBufLen]; + } + this.rfb.readFully(this.zlibBuf, 0, n5); + if (this.rfb.rec != null && this.rfb.recordFromBeginning) { + this.rfb.rec.writeIntBE(n5); + this.rfb.rec.write(this.zlibBuf, 0, n5); + } + if (this.zlibInflater == null) { + this.zlibInflater = new Inflater(); + } + this.zlibInflater.setInput(this.zlibBuf, 0, n5); + if (this.bytesPixel == 1) { + for (int i = n2; i < n2 + n4; ++i) { + this.zlibInflater.inflate(this.pixels8, i * this.rfb.framebufferWidth + n, n3); + if (this.rfb.rec == null || this.rfb.recordFromBeginning) continue; + this.rfb.rec.write(this.pixels8, i * this.rfb.framebufferWidth + n, n3); + } + } else { + byte[] byArray = new byte[n3 * 4]; + for (int i = n2; i < n2 + n4; ++i) { + this.zlibInflater.inflate(byArray); + int n6 = i * this.rfb.framebufferWidth + n; + for (int j = 0; j < n3; ++j) { + this.pixels24[n6 + j] = (byArray[j * 4 + 2] & 0xFF) << 16 | (byArray[j * 4 + 1] & 0xFF) << 8 | byArray[j * 4] & 0xFF; + } + if (this.rfb.rec == null || this.rfb.recordFromBeginning) continue; + this.rfb.rec.write(byArray); + } + } + this.handleUpdatedPixels(n, n2, n3, n4); + this.scheduleRepaint(n, n2, n3, n4); + } + + /* + * WARNING - Removed try catching itself - possible behaviour change. + * Unable to fully structure code + */ + void handleTightRect(int var1_1, int var2_2, int var3_3, int var4_4) throws Exception { + block72: { + var5_5 = this.rfb.is.readUnsignedByte(); + if (this.rfb.rec == null) break block72; + if (this.rfb.recordFromBeginning) ** GOTO lbl-1000 + if (var5_5 == 8 << 4) ** GOTO lbl-1000 + if (var5_5 == 9 << 4) lbl-1000: + // 3 sources + + { + this.rfb.rec.writeByte(var5_5); + } else { + this.rfb.rec.writeByte(var5_5 | 15); + } + } + for (var6_6 = 0; var6_6 < 4; ++var6_6) { + if ((var5_5 & 1) != 0 && this.tightInflaters[var6_6] != null) { + this.tightInflaters[var6_6] = null; + } + var5_5 >>= 1; + } + if (var5_5 > 9) { + throw new Exception("Incorrect tight subencoding: " + var5_5); + } + if (var5_5 == 8) { + if (this.bytesPixel == 1) { + var6_6 = this.rfb.is.readUnsignedByte(); + this.memGraphics.setColor(this.colors[var6_6]); + if (this.rfb.rec != null) { + this.rfb.rec.writeByte(var6_6); + } + } else { + var6_7 = new byte[3]; + this.rfb.readFully(var6_7); + if (this.rfb.rec != null) { + this.rfb.rec.write(var6_7); + } + var7_9 = new Color(-16777216 | (var6_7[0] & 255) << 16 | (var6_7[1] & 255) << 8 | var6_7[2] & 255); + this.memGraphics.setColor(var7_9); + } + this.memGraphics.fillRect(var1_1, var2_2, var3_3, var4_4); + this.scheduleRepaint(var1_1, var2_2, var3_3, var4_4); + return; + } + if (var5_5 == 9) { + var6_8 = new byte[this.rfb.readCompactLen()]; + this.rfb.readFully(var6_8); + if (this.rfb.rec != null) { + if (!this.rfb.recordFromBeginning) { + this.rfb.recordCompactLen(var6_8.length); + } + this.rfb.rec.write(var6_8); + } + var7_10 = Toolkit.getDefaultToolkit().createImage(var6_8); + var8_12 = this.jpegRect = new Rectangle(var1_1, var2_2, var3_3, var4_4); + synchronized (var8_12) { + Toolkit.getDefaultToolkit().prepareImage(var7_10, -1, -1, this); + try { + this.jpegRect.wait(3000L); + } + catch (InterruptedException var9_14) { + throw new Exception("Interrupted while decoding JPEG image"); + } + } + this.jpegRect = null; + return; + } + var6_6 = 0; + var7_11 = var3_3; + var8_13 = new byte[2]; + var9_15 = new int[256]; + var10_17 = false; + if ((var5_5 & 4) != 0) { + var11_18 = this.rfb.is.readUnsignedByte(); + if (this.rfb.rec != null) { + this.rfb.rec.writeByte(var11_18); + } + if (var11_18 == 1) { + var6_6 = this.rfb.is.readUnsignedByte() + 1; + if (this.rfb.rec != null) { + this.rfb.rec.writeByte(var6_6 - 1); + } + if (this.bytesPixel == 1) { + if (var6_6 != 2) { + throw new Exception("Incorrect tight palette size: " + var6_6); + } + this.rfb.readFully(var8_13); + if (this.rfb.rec != null) { + this.rfb.rec.write(var8_13); + } + } else { + var12_19 = new byte[var6_6 * 3]; + this.rfb.readFully(var12_19); + if (this.rfb.rec != null) { + this.rfb.rec.write(var12_19); + } + for (var13_22 = 0; var13_22 < var6_6; ++var13_22) { + var9_15[var13_22] = (var12_19[var13_22 * 3] & 255) << 16 | (var12_19[var13_22 * 3 + 1] & 255) << 8 | var12_19[var13_22 * 3 + 2] & 255; + } + } + if (var6_6 == 2) { + var7_11 = (var3_3 + 7) / 8; + } + } else if (var11_18 == 2) { + var10_17 = true; + } else if (var11_18 != 0) { + throw new Exception("Incorrect tight filter id: " + var11_18); + } + } + if (var6_6 == 0 && this.bytesPixel == 4) { + var7_11 *= 3; + } + var11_18 = var4_4 * var7_11; + if (var11_18 < 12) { + if (var6_6 != 0) { + var12_19 = new byte[var11_18]; + this.rfb.readFully(var12_19); + if (this.rfb.rec != null) { + this.rfb.rec.write(var12_19); + } + if (var6_6 == 2) { + if (this.bytesPixel == 1) { + this.decodeMonoData(var1_1, var2_2, var3_3, var4_4, var12_19, var8_13); + } else { + this.decodeMonoData(var1_1, var2_2, var3_3, var4_4, var12_19, var9_15); + } + } else { + var13_22 = 0; + for (var14_24 = var2_2; var14_24 < var2_2 + var4_4; ++var14_24) { + for (var15_27 = var1_1; var15_27 < var1_1 + var3_3; ++var15_27) { + this.pixels24[var14_24 * this.rfb.framebufferWidth + var15_27] = var9_15[var12_19[var13_22++] & 255]; + } + } + } + } else if (var10_17) { + var12_19 = new byte[var3_3 * var4_4 * 3]; + this.rfb.readFully(var12_19); + if (this.rfb.rec != null) { + this.rfb.rec.write(var12_19); + } + this.decodeGradientData(var1_1, var2_2, var3_3, var4_4, var12_19); + } else if (this.bytesPixel == 1) { + for (var12_20 = var2_2; var12_20 < var2_2 + var4_4; ++var12_20) { + this.rfb.readFully(this.pixels8, var12_20 * this.rfb.framebufferWidth + var1_1, var3_3); + if (this.rfb.rec == null) continue; + this.rfb.rec.write(this.pixels8, var12_20 * this.rfb.framebufferWidth + var1_1, var3_3); + } + } else { + var12_19 = new byte[var3_3 * 3]; + for (var15_28 = var2_2; var15_28 < var2_2 + var4_4; ++var15_28) { + this.rfb.readFully(var12_19); + if (this.rfb.rec != null) { + this.rfb.rec.write(var12_19); + } + var14_25 = var15_28 * this.rfb.framebufferWidth + var1_1; + for (var13_22 = 0; var13_22 < var3_3; ++var13_22) { + this.pixels24[var14_25 + var13_22] = (var12_19[var13_22 * 3] & 255) << 16 | (var12_19[var13_22 * 3 + 1] & 255) << 8 | var12_19[var13_22 * 3 + 2] & 255; + } + } + } + } else { + var12_21 = this.rfb.readCompactLen(); + var13_23 = new byte[var12_21]; + this.rfb.readFully(var13_23); + if (this.rfb.rec != null && this.rfb.recordFromBeginning) { + this.rfb.rec.write(var13_23); + } + if (this.tightInflaters[var14_26 = var5_5 & 3] == null) { + this.tightInflaters[var14_26] = new Inflater(); + } + var15_29 = this.tightInflaters[var14_26]; + var15_29.setInput(var13_23); + var16_30 = new byte[var11_18]; + var15_29.inflate(var16_30); + if (this.rfb.rec != null && !this.rfb.recordFromBeginning) { + this.rfb.recordCompressedData(var16_30); + } + if (var6_6 != 0) { + if (var6_6 == 2) { + if (this.bytesPixel == 1) { + this.decodeMonoData(var1_1, var2_2, var3_3, var4_4, var16_30, var8_13); + } else { + this.decodeMonoData(var1_1, var2_2, var3_3, var4_4, var16_30, var9_15); + } + } else { + var17_31 = 0; + for (var18_34 = var2_2; var18_34 < var2_2 + var4_4; ++var18_34) { + for (var19_37 = var1_1; var19_37 < var1_1 + var3_3; ++var19_37) { + this.pixels24[var18_34 * this.rfb.framebufferWidth + var19_37] = var9_15[var16_30[var17_31++] & 255]; + } + } + } + } else if (var10_17) { + this.decodeGradientData(var1_1, var2_2, var3_3, var4_4, var16_30); + } else if (this.bytesPixel == 1) { + var17_32 = var2_2 * this.rfb.framebufferWidth + var1_1; + for (var18_35 = 0; var18_35 < var4_4; ++var18_35) { + System.arraycopy(var16_30, var18_35 * var3_3, this.pixels8, var17_32, var3_3); + var17_32 += this.rfb.framebufferWidth; + } + } else { + var17_33 = 0; + for (var20_39 = 0; var20_39 < var4_4; ++var20_39) { + var15_29.inflate(var16_30); + var18_36 = (var2_2 + var20_39) * this.rfb.framebufferWidth + var1_1; + for (var19_38 = 0; var19_38 < var3_3; ++var19_38) { + this.pixels24[var18_36 + var19_38] = (var16_30[var17_33] & 255) << 16 | (var16_30[var17_33 + 1] & 255) << 8 | var16_30[var17_33 + 2] & 255; + var17_33 += 3; + } + } + } + } + this.handleUpdatedPixels(var1_1, var2_2, var3_3, var4_4); + this.scheduleRepaint(var1_1, var2_2, var3_3, var4_4); + } + + void decodeMonoData(int n, int n2, int n3, int n4, byte[] byArray, byte[] byArray2) { + int n5 = n2 * this.rfb.framebufferWidth + n; + int n6 = (n3 + 7) / 8; + for (int i = 0; i < n4; ++i) { + int n7; + int n8; + for (n8 = 0; n8 < n3 / 8; ++n8) { + byte by = byArray[i * n6 + n8]; + for (n7 = 7; n7 >= 0; --n7) { + this.pixels8[n5++] = byArray2[by >> n7 & 1]; + } + } + for (n7 = 7; n7 >= 8 - n3 % 8; --n7) { + this.pixels8[n5++] = byArray2[byArray[i * n6 + n8] >> n7 & 1]; + } + n5 += this.rfb.framebufferWidth - n3; + } + } + + void decodeMonoData(int n, int n2, int n3, int n4, byte[] byArray, int[] nArray) { + int n5 = n2 * this.rfb.framebufferWidth + n; + int n6 = (n3 + 7) / 8; + for (int i = 0; i < n4; ++i) { + int n7; + int n8; + for (n8 = 0; n8 < n3 / 8; ++n8) { + byte by = byArray[i * n6 + n8]; + for (n7 = 7; n7 >= 0; --n7) { + this.pixels24[n5++] = nArray[by >> n7 & 1]; + } + } + for (n7 = 7; n7 >= 8 - n3 % 8; --n7) { + this.pixels24[n5++] = nArray[byArray[i * n6 + n8] >> n7 & 1]; + } + n5 += this.rfb.framebufferWidth - n3; + } + } + + void decodeGradientData(int n, int n2, int n3, int n4, byte[] byArray) { + byte[] byArray2 = new byte[n3 * 3]; + byte[] byArray3 = new byte[n3 * 3]; + byte[] byArray4 = new byte[3]; + int[] nArray = new int[3]; + int n5 = n2 * this.rfb.framebufferWidth + n; + for (int i = 0; i < n4; ++i) { + int n6; + for (n6 = 0; n6 < 3; ++n6) { + byArray4[n6] = (byte)(byArray2[n6] + byArray[i * n3 * 3 + n6]); + byArray3[n6] = byArray4[n6]; + } + this.pixels24[n5++] = (byArray4[0] & 0xFF) << 16 | (byArray4[1] & 0xFF) << 8 | byArray4[2] & 0xFF; + for (int j = 1; j < n3; ++j) { + for (n6 = 0; n6 < 3; ++n6) { + nArray[n6] = (byArray2[j * 3 + n6] & 0xFF) + (byArray4[n6] & 0xFF) - (byArray2[(j - 1) * 3 + n6] & 0xFF); + if (nArray[n6] > 255) { + nArray[n6] = 255; + } else if (nArray[n6] < 0) { + nArray[n6] = 0; + } + byArray4[n6] = (byte)(nArray[n6] + byArray[(i * n3 + j) * 3 + n6]); + byArray3[j * 3 + n6] = byArray4[n6]; + } + this.pixels24[n5++] = (byArray4[0] & 0xFF) << 16 | (byArray4[1] & 0xFF) << 8 | byArray4[2] & 0xFF; + } + System.arraycopy(byArray3, 0, byArray2, 0, n3 * 3); + n5 += this.rfb.framebufferWidth - n3; + } + } + + void handleUpdatedPixels(int n, int n2, int n3, int n4) { + this.pixelsSource.newPixels(n, n2, n3, n4); + this.memGraphics.setClip(n, n2, n3, n4); + this.memGraphics.drawImage(this.rawPixelsImage, 0, 0, null); + this.memGraphics.setClip(0, 0, this.rfb.framebufferWidth, this.rfb.framebufferHeight); + } + + void scheduleRepaint(int n, int n2, int n3, int n4) { + if (this.rfb.framebufferWidth == this.scaledWidth) { + this.repaint(this.viewer.deferScreenUpdates, n, n2, n3, n4); + } else { + int n5 = n * this.scalingFactor / 100; + int n6 = n2 * this.scalingFactor / 100; + int n7 = ((n + n3) * this.scalingFactor + 49) / 100 - n5 + 1; + int n8 = ((n2 + n4) * this.scalingFactor + 49) / 100 - n6 + 1; + this.repaint(this.viewer.deferScreenUpdates, n5, n6, n7, n8); + } + } + + public void keyPressed(KeyEvent keyEvent) { + this.processLocalKeyEvent(keyEvent); + } + + public void keyReleased(KeyEvent keyEvent) { + this.processLocalKeyEvent(keyEvent); + } + + public void keyTyped(KeyEvent keyEvent) { + keyEvent.consume(); + } + + public void mousePressed(MouseEvent mouseEvent) { + this.processLocalMouseEvent(mouseEvent, false); + } + + public void mouseReleased(MouseEvent mouseEvent) { + this.processLocalMouseEvent(mouseEvent, false); + } + + public void mouseMoved(MouseEvent mouseEvent) { + this.processLocalMouseEvent(mouseEvent, true); + } + + public void mouseDragged(MouseEvent mouseEvent) { + this.processLocalMouseEvent(mouseEvent, true); + } + + /* + * WARNING - Removed try catching itself - possible behaviour change. + */ + public void processLocalKeyEvent(KeyEvent keyEvent) { + if (this.viewer.rfb != null && this.rfb.inNormalProtocol) { + if (!this.inputEnabled) { + if ((keyEvent.getKeyChar() == 'r' || keyEvent.getKeyChar() == 'R') && keyEvent.getID() == 401) { + try { + this.rfb.writeFramebufferUpdateRequest(0, 0, this.rfb.framebufferWidth, this.rfb.framebufferHeight, false); + } + catch (IOException iOException) { + iOException.printStackTrace(); + } + } + } else { + RfbProto rfbProto = this.rfb; + synchronized (rfbProto) { + try { + this.rfb.writeKeyEvent(keyEvent); + } + catch (Exception exception) { + exception.printStackTrace(); + } + this.rfb.notify(); + } + } + } + keyEvent.consume(); + } + + /* + * WARNING - Removed try catching itself - possible behaviour change. + */ + public void processLocalMouseEvent(MouseEvent mouseEvent, boolean bl) { + if (this.viewer.rfb != null && this.rfb.inNormalProtocol) { + if (bl) { + this.softCursorMove(mouseEvent.getX(), mouseEvent.getY()); + } + if (this.rfb.framebufferWidth != this.scaledWidth) { + int n = (mouseEvent.getX() * 100 + this.scalingFactor / 2) / this.scalingFactor; + int n2 = (mouseEvent.getY() * 100 + this.scalingFactor / 2) / this.scalingFactor; + mouseEvent.translatePoint(n - mouseEvent.getX(), n2 - mouseEvent.getY()); + } + RfbProto rfbProto = this.rfb; + synchronized (rfbProto) { + try { + this.rfb.writePointerEvent(mouseEvent); + } + catch (Exception exception) { + exception.printStackTrace(); + } + this.rfb.notify(); + } + } + } + + public void mouseClicked(MouseEvent mouseEvent) { + } + + public void mouseEntered(MouseEvent mouseEvent) { + } + + public void mouseExited(MouseEvent mouseEvent) { + } + + synchronized void handleCursorShapeUpdate(int n, int n2, int n3, int n4, int n5) throws IOException { + this.softCursorFree(); + if (n4 * n5 == 0) { + return; + } + if (this.viewer.options.ignoreCursorUpdates) { + int n6 = (n4 + 7) / 8; + int n7 = n6 * n5; + if (n == -240) { + this.rfb.is.skipBytes(6 + n7 * 2); + } else { + this.rfb.is.skipBytes(n4 * n5 + n7); + } + return; + } + this.softCursorSource = this.decodeCursorShape(n, n4, n5); + this.origCursorWidth = n4; + this.origCursorHeight = n5; + this.origHotX = n2; + this.origHotY = n3; + this.createSoftCursor(); + this.showSoftCursor = true; + this.repaint(this.viewer.deferCursorUpdates, this.cursorX - this.hotX, this.cursorY - this.hotY, this.cursorWidth, this.cursorHeight); + } + + synchronized MemoryImageSource decodeCursorShape(int n, int n2, int n3) throws IOException { + int n4 = (n2 + 7) / 8; + int n5 = n4 * n3; + int[] nArray = new int[n2 * n3]; + if (n == -240) { + byte[] byArray = new byte[6]; + this.rfb.readFully(byArray); + int[] nArray2 = new int[]{0xFF000000 | (byArray[3] & 0xFF) << 16 | (byArray[4] & 0xFF) << 8 | byArray[5] & 0xFF, 0xFF000000 | (byArray[0] & 0xFF) << 16 | (byArray[1] & 0xFF) << 8 | byArray[2] & 0xFF}; + byte[] byArray2 = new byte[n5]; + this.rfb.readFully(byArray2); + byte[] byArray3 = new byte[n5]; + this.rfb.readFully(byArray3); + int n6 = 0; + for (int i = 0; i < n3; ++i) { + int n7; + int n8; + int n9; + for (n9 = 0; n9 < n2 / 8; ++n9) { + byte by = byArray2[i * n4 + n9]; + byte by2 = byArray3[i * n4 + n9]; + for (n8 = 7; n8 >= 0; --n8) { + n7 = (by2 >> n8 & 1) != 0 ? nArray2[by >> n8 & 1] : 0; + nArray[n6++] = n7; + } + } + for (n8 = 7; n8 >= 8 - n2 % 8; --n8) { + n7 = (byArray3[i * n4 + n9] >> n8 & 1) != 0 ? nArray2[byArray2[i * n4 + n9] >> n8 & 1] : 0; + nArray[n6++] = n7; + } + } + } else { + byte[] byArray = new byte[n2 * n3 * this.bytesPixel]; + this.rfb.readFully(byArray); + byte[] byArray4 = new byte[n5]; + this.rfb.readFully(byArray4); + int n10 = 0; + for (int i = 0; i < n3; ++i) { + int n11; + int n12; + int n13; + for (n13 = 0; n13 < n2 / 8; ++n13) { + byte by = byArray4[i * n4 + n13]; + for (n12 = 7; n12 >= 0; --n12) { + n11 = (by >> n12 & 1) != 0 ? (this.bytesPixel == 1 ? this.cm8.getRGB(byArray[n10]) : 0xFF000000 | (byArray[n10 * 4 + 2] & 0xFF) << 16 | (byArray[n10 * 4 + 1] & 0xFF) << 8 | byArray[n10 * 4] & 0xFF) : 0; + nArray[n10++] = n11; + } + } + for (n12 = 7; n12 >= 8 - n2 % 8; --n12) { + n11 = (byArray4[i * n4 + n13] >> n12 & 1) != 0 ? (this.bytesPixel == 1 ? this.cm8.getRGB(byArray[n10]) : 0xFF000000 | (byArray[n10 * 4 + 2] & 0xFF) << 16 | (byArray[n10 * 4 + 1] & 0xFF) << 8 | byArray[n10 * 4] & 0xFF) : 0; + nArray[n10++] = n11; + } + } + } + return new MemoryImageSource(n2, n3, nArray, 0, n2); + } + + synchronized void createSoftCursor() { + if (this.softCursorSource == null) { + return; + } + int n = this.viewer.options.scaleCursor; + if (n == 0 || !this.inputEnabled) { + n = 100; + } + int n2 = this.cursorX - this.hotX; + int n3 = this.cursorY - this.hotY; + int n4 = this.cursorWidth; + int n5 = this.cursorHeight; + this.cursorWidth = (this.origCursorWidth * n + 50) / 100; + this.cursorHeight = (this.origCursorHeight * n + 50) / 100; + this.hotX = (this.origHotX * n + 50) / 100; + this.hotY = (this.origHotY * n + 50) / 100; + this.softCursor = Toolkit.getDefaultToolkit().createImage(this.softCursorSource); + if (n != 100) { + this.softCursor = this.softCursor.getScaledInstance(this.cursorWidth, this.cursorHeight, 4); + } + if (this.showSoftCursor) { + n2 = Math.min(n2, this.cursorX - this.hotX); + n3 = Math.min(n3, this.cursorY - this.hotY); + n4 = Math.max(n4, this.cursorWidth); + n5 = Math.max(n5, this.cursorHeight); + this.repaint(this.viewer.deferCursorUpdates, n2, n3, n4, n5); + } + } + + synchronized void softCursorMove(int n, int n2) { + int n3 = this.cursorX; + int n4 = this.cursorY; + this.cursorX = n; + this.cursorY = n2; + if (this.showSoftCursor) { + this.repaint(this.viewer.deferCursorUpdates, n3 - this.hotX, n4 - this.hotY, this.cursorWidth, this.cursorHeight); + this.repaint(this.viewer.deferCursorUpdates, this.cursorX - this.hotX, this.cursorY - this.hotY, this.cursorWidth, this.cursorHeight); + } + } + + synchronized void softCursorFree() { + if (this.showSoftCursor) { + this.showSoftCursor = false; + this.softCursor = null; + this.softCursorSource = null; + this.repaint(this.viewer.deferCursorUpdates, this.cursorX - this.hotX, this.cursorY - this.hotY, this.cursorWidth, this.cursorHeight); + } + } +} + diff --git a/src/VncCanvas2.java b/src/VncCanvas2.java new file mode 100644 index 0000000..866f8f0 --- /dev/null +++ b/src/VncCanvas2.java @@ -0,0 +1,40 @@ +/* + * Decompiled with CFR 0.152. + */ +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.io.IOException; +import java.lang.reflect.Method; + +class VncCanvas2 +extends VncCanvas { + public VncCanvas2(VncViewer vncViewer) throws IOException { + super(vncViewer); + this.disableFocusTraversalKeys(); + } + + public VncCanvas2(VncViewer vncViewer, int n, int n2) throws IOException { + super(vncViewer, n, n2); + this.disableFocusTraversalKeys(); + } + + public void paintScaledFrameBuffer(Graphics graphics) { + Graphics2D graphics2D = (Graphics2D)graphics; + graphics2D.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + graphics2D.drawImage(this.memImage, 0, 0, this.scaledWidth, this.scaledHeight, null); + } + + private void disableFocusTraversalKeys() { + try { + Class[] classArray = new Class[]{Boolean.TYPE}; + Method method = this.getClass().getMethod("setFocusTraversalKeysEnabled", classArray); + Object[] objectArray = new Object[]{new Boolean(false)}; + method.invoke(this, objectArray); + } + catch (Exception exception) { + // empty catch block + } + } +} + diff --git a/src/VncThumbnailViewer.java b/src/VncThumbnailViewer.java new file mode 100644 index 0000000..3724184 --- /dev/null +++ b/src/VncThumbnailViewer.java @@ -0,0 +1,368 @@ +/* + * Decompiled with CFR 0.152. + */ +import java.awt.Button; +import java.awt.Component; +import java.awt.FileDialog; +import java.awt.Frame; +import java.awt.GraphicsEnvironment; +import java.awt.GridLayout; +import java.awt.Insets; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.awt.event.ContainerEvent; +import java.awt.event.ContainerListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; +import java.util.AbstractList; +import java.util.ListIterator; + +public class VncThumbnailViewer +extends Frame +implements WindowListener, +ComponentListener, +ContainerListener, +MouseListener, +ActionListener { + static final float VERSION = 1.4f; + VncViewersList viewersList = new VncViewersList(this); + AddHostDialog hostDialog; + MenuItem newhostMenuItem; + MenuItem loadhostsMenuItem; + MenuItem savehostsMenuItem; + MenuItem exitMenuItem; + Frame soloViewer; + int widthPerThumbnail = 0; + int heightPerThumbnail = 0; + int thumbnailRowCount = 0; + + public static void main(String[] stringArray) { + VncThumbnailViewer vncThumbnailViewer = new VncThumbnailViewer(); + String string = new String(""); + String string2 = new String(""); + String string3 = new String(""); + int n = 0; + for (int i = 0; i < stringArray.length; i += 2) { + if (stringArray.length < i + 2) { + System.out.println("ERROR: No value found for parameter " + stringArray[i]); + break; + } + String string4 = stringArray[i]; + String string5 = stringArray[i + 1]; + if (string4.equalsIgnoreCase("host")) { + string = string5; + } + if (string4.equalsIgnoreCase("port")) { + n = Integer.parseInt(string5); + } + if (string4.equalsIgnoreCase("password")) { + string2 = string5; + } + if (string4.equalsIgnoreCase("username")) { + string3 = string5; + } + if (string4.equalsIgnoreCase("encpassword")) { + string2 = AddHostDialog.readEncPassword(string5); + } + if (i + 2 < stringArray.length && !stringArray[i + 2].equalsIgnoreCase("host")) continue; + if (string != "" && n != 0) { + System.out.println("Command-line: host " + string + " port " + n); + vncThumbnailViewer.launchViewer(string, n, string2, string3); + string = ""; + n = 0; + string2 = ""; + string3 = ""; + continue; + } + System.out.println("ERROR: No port specified for last host (" + string + ")"); + } + } + + VncThumbnailViewer() { + this.setTitle("DJC Thumbnail Viewer"); + this.addWindowListener(this); + this.addComponentListener(this); + this.addMouseListener(this); + GridLayout gridLayout = new GridLayout(); + this.setLayout(gridLayout); + ((Component)this).setSize(GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds().getSize()); + this.setMenuBar(new MenuBar()); + this.getMenuBar().add(this.createFileMenu()); + ((Component)this).setVisible(true); + this.soloViewer = new Frame(); + ((Component)this.soloViewer).setSize(GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds().getSize()); + this.soloViewer.addWindowListener(this); + this.soloViewer.addComponentListener(this); + this.soloViewer.validate(); + } + + public void launchViewer(String string, int n, String string2, String string3) { + this.launchViewer(string, n, string2, string3, ""); + } + + public void launchViewer(String string, int n, String string2, String string3, String string4) { + VncViewer vncViewer = this.viewersList.launchViewer(string, n, string2, string3, string4); + } + + void addViewer(VncViewer vncViewer) { + int n = (int)Math.sqrt(this.viewersList.size() - 1) + 1; + if (n != this.thumbnailRowCount) { + this.thumbnailRowCount = n; + ((GridLayout)this.getLayout()).setRows(this.thumbnailRowCount); + this.resizeThumbnails(); + } + this.add(vncViewer); + this.validate(); + } + + void removeViewer(VncViewer vncViewer) { + this.viewersList.remove(vncViewer); + this.remove(vncViewer); + this.validate(); + int n = (int)Math.sqrt(this.viewersList.size() - 1) + 1; + if (n != this.thumbnailRowCount) { + this.thumbnailRowCount = n; + ((GridLayout)this.getLayout()).setRows(this.thumbnailRowCount); + this.resizeThumbnails(); + } + } + + void soloHost(VncViewer vncViewer) { + if (vncViewer.vc == null) { + return; + } + if (this.soloViewer.getComponentCount() > 0) { + this.soloHostClose(); + } + ((Component)this.soloViewer).setVisible(true); + this.soloViewer.setTitle(vncViewer.host); + this.remove(vncViewer); + this.soloViewer.add(vncViewer); + vncViewer.vc.removeMouseListener(this); + this.validate(); + this.soloViewer.validate(); + if (!vncViewer.rfb.closed()) { + vncViewer.vc.enableInput(true); + } + this.updateCanvasScaling(vncViewer, VncThumbnailViewer.getWidthNoInsets(this.soloViewer), VncThumbnailViewer.getHeightNoInsets(this.soloViewer)); + } + + void soloHostClose() { + VncViewer vncViewer = (VncViewer)this.soloViewer.getComponent(0); + vncViewer.enableInput(false); + this.updateCanvasScaling(vncViewer, this.widthPerThumbnail, this.heightPerThumbnail); + this.soloViewer.removeAll(); + this.addViewer(vncViewer); + vncViewer.vc.addMouseListener(this); + ((Component)this.soloViewer).setVisible(false); + } + + private void updateCanvasScaling(VncViewer vncViewer, int n, int n2) { + int n3; + int n4 = vncViewer.vc.rfb.framebufferWidth; + int n5 = n * 100 / n4; + int n6 = (n2 -= vncViewer.buttonPanel.getHeight()) * 100 / (n3 = vncViewer.vc.rfb.framebufferHeight); + int n7 = Math.min(n5, n6); + if (n7 > 100) { + n7 = 100; + } + vncViewer.vc.maxWidth = n; + vncViewer.vc.maxHeight = n2; + vncViewer.vc.scalingFactor = n7; + vncViewer.vc.scaledWidth = (n4 * n7 + 50) / 100; + vncViewer.vc.scaledHeight = (n3 * n7 + 50) / 100; + } + + void resizeThumbnails() { + int n = VncThumbnailViewer.getWidthNoInsets(this) / this.thumbnailRowCount; + int n2 = VncThumbnailViewer.getHeightNoInsets(this) / this.thumbnailRowCount; + if (n != this.widthPerThumbnail || n2 != this.heightPerThumbnail) { + this.widthPerThumbnail = n; + this.heightPerThumbnail = n2; + ListIterator listIterator = ((AbstractList)this.viewersList).listIterator(); + while (listIterator.hasNext()) { + VncViewer vncViewer = (VncViewer)listIterator.next(); + if (this.soloViewer.isAncestorOf(vncViewer) || vncViewer.vc == null) continue; + this.updateCanvasScaling(vncViewer, this.widthPerThumbnail, this.heightPerThumbnail); + } + } + } + + private void loadsaveHosts(int n) { + FileDialog fileDialog = new FileDialog((Frame)this, "Load hosts file...", n); + if (n == 1) { + fileDialog.setTitle("Save hosts file..."); + } + fileDialog.show(); + String string = fileDialog.getFile(); + if (string != null) { + String string2 = fileDialog.getDirectory(); + if (n == 1) { + HostsFilePasswordDialog hostsFilePasswordDialog = new HostsFilePasswordDialog((Frame)this, true); + if (hostsFilePasswordDialog.getResult()) { + this.viewersList.saveToEncryptedFile(string2 + string, hostsFilePasswordDialog.getPassword()); + } else { + this.viewersList.saveToFile(string2 + string); + } + } else if (VncViewersList.isHostsFileEncrypted(string2 + string)) { + HostsFilePasswordDialog hostsFilePasswordDialog = new HostsFilePasswordDialog((Frame)this, false); + this.viewersList.loadHosts(string2 + string, hostsFilePasswordDialog.getPassword()); + } else { + this.viewersList.loadHosts(string2 + string, ""); + } + } + } + + private void quit() { + System.out.println("Closing window"); + ListIterator listIterator = ((AbstractList)this.viewersList).listIterator(); + while (listIterator.hasNext()) { + ((VncViewer)listIterator.next()).disconnect(); + } + this.dispose(); + System.exit(0); + } + + private static int getWidthNoInsets(Frame frame) { + Insets insets = frame.getInsets(); + int n = frame.getWidth() - (insets.left + insets.right); + return n; + } + + private static int getHeightNoInsets(Frame frame) { + Insets insets = frame.getInsets(); + int n = frame.getHeight() - (insets.top + insets.bottom); + return n; + } + + private Menu createFileMenu() { + Menu menu = new Menu("File"); + this.newhostMenuItem = new MenuItem("Add New Host"); + this.loadhostsMenuItem = new MenuItem("Load List of Hosts"); + this.savehostsMenuItem = new MenuItem("Save List of Hosts"); + this.exitMenuItem = new MenuItem("Exit"); + this.newhostMenuItem.addActionListener(this); + this.loadhostsMenuItem.addActionListener(this); + this.savehostsMenuItem.addActionListener(this); + this.exitMenuItem.addActionListener(this); + menu.add(this.newhostMenuItem); + menu.addSeparator(); + menu.add(this.loadhostsMenuItem); + menu.add(this.savehostsMenuItem); + menu.addSeparator(); + menu.add(this.exitMenuItem); + this.loadhostsMenuItem.enable(true); + this.savehostsMenuItem.enable(true); + return menu; + } + + public void windowClosing(WindowEvent windowEvent) { + if (this.soloViewer.isShowing()) { + this.soloHostClose(); + } + if (windowEvent.getComponent() == this) { + this.quit(); + } + } + + public void windowActivated(WindowEvent windowEvent) { + } + + public void windowDeactivated(WindowEvent windowEvent) { + } + + public void windowOpened(WindowEvent windowEvent) { + } + + public void windowClosed(WindowEvent windowEvent) { + } + + public void windowIconified(WindowEvent windowEvent) { + } + + public void windowDeiconified(WindowEvent windowEvent) { + } + + public void componentResized(ComponentEvent componentEvent) { + if (componentEvent.getComponent() == this) { + if (this.thumbnailRowCount > 0) { + this.resizeThumbnails(); + } + } else { + VncViewer vncViewer = (VncViewer)this.soloViewer.getComponent(0); + this.updateCanvasScaling(vncViewer, VncThumbnailViewer.getWidthNoInsets(this.soloViewer), VncThumbnailViewer.getHeightNoInsets(this.soloViewer)); + } + } + + public void componentHidden(ComponentEvent componentEvent) { + } + + public void componentMoved(ComponentEvent componentEvent) { + } + + public void componentShown(ComponentEvent componentEvent) { + } + + public void mouseClicked(MouseEvent mouseEvent) { + Component component; + if (mouseEvent.getClickCount() == 2 && (component = mouseEvent.getComponent()) instanceof VncCanvas) { + this.soloHost(((VncCanvas)component).viewer); + } + } + + public void mouseEntered(MouseEvent mouseEvent) { + } + + public void mouseExited(MouseEvent mouseEvent) { + } + + public void mousePressed(MouseEvent mouseEvent) { + } + + public void mouseReleased(MouseEvent mouseEvent) { + } + + public void componentAdded(ContainerEvent containerEvent) { + Button button; + if (containerEvent.getChild() instanceof VncCanvas) { + VncViewer vncViewer = (VncViewer)containerEvent.getContainer(); + vncViewer.vc.addMouseListener(this); + vncViewer.buttonPanel.addContainerListener(this); + vncViewer.buttonPanel.disconnectButton.addActionListener(this); + this.updateCanvasScaling(vncViewer, this.widthPerThumbnail, this.heightPerThumbnail); + } else if (containerEvent.getChild() instanceof Button && (button = (Button)containerEvent.getChild()).getLabel() == "Hide desktop") { + button.addActionListener(this); + } + } + + public void componentRemoved(ContainerEvent containerEvent) { + } + + public void actionPerformed(ActionEvent actionEvent) { + if (actionEvent.getSource() instanceof Button && ((Button)actionEvent.getSource()).getLabel() == "Hide desktop") { + VncViewer vncViewer = (VncViewer)((Component)actionEvent.getSource()).getParent().getParent(); + this.remove(vncViewer); + this.viewersList.remove(vncViewer); + } + if (actionEvent.getSource() == this.newhostMenuItem) { + this.hostDialog = new AddHostDialog(this); + } + if (actionEvent.getSource() == this.savehostsMenuItem) { + this.loadsaveHosts(1); + } + if (actionEvent.getSource() == this.loadhostsMenuItem) { + this.loadsaveHosts(0); + } + if (actionEvent.getSource() == this.exitMenuItem) { + this.quit(); + } + } +} + diff --git a/src/VncViewer.java b/src/VncViewer.java new file mode 100644 index 0000000..48db886 --- /dev/null +++ b/src/VncViewer.java @@ -0,0 +1,787 @@ +/* + * Decompiled with CFR 0.152. + */ +import java.applet.Applet; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.GridLayout; +import java.awt.Insets; +import java.awt.Label; +import java.awt.Panel; +import java.awt.ScrollPane; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; +import java.io.EOFException; +import java.io.IOException; +import java.io.Serializable; +import java.lang.reflect.Constructor; +import java.net.ConnectException; +import java.net.NoRouteToHostException; +import java.net.UnknownHostException; + +public class VncViewer +extends Applet +implements Runnable, +WindowListener { + boolean inAnApplet = true; + boolean inSeparateFrame = false; + String[] mainArgs; + RfbProto rfb; + Thread rfbThread; + Frame vncFrame; + Container vncContainer; + ScrollPane desktopScrollPane; + GridBagLayout gridbag; + ButtonPanel buttonPanel; + Label connStatusLabel; + VncCanvas vc; + OptionsFrame options; + ClipboardFrame clipboard; + RecordingFrame rec; + Object recordingSync; + String sessionFileName; + boolean recordingActive; + boolean recordingStatusChanged; + String cursorUpdatesDef; + String eightBitColorsDef; + String socketFactory; + String host; + int port; + String passwordParam; + String usernameParam; + boolean showControls; + boolean offerRelogin; + boolean showOfflineDesktop; + int deferScreenUpdates; + int deferCursorUpdates; + int deferUpdateRequests; + public static Applet refApplet; + int[] encodingsSaved; + int nEncodingsSaved; + + public static void main(String[] stringArray) { + VncViewer vncViewer = new VncViewer(); + vncViewer.mainArgs = stringArray; + vncViewer.inAnApplet = false; + vncViewer.inSeparateFrame = true; + vncViewer.init(); + vncViewer.start(); + } + + public void init() { + this.readParameters(); + refApplet = this; + if (this.inSeparateFrame) { + this.vncFrame = new Frame("TightVNC"); + if (!this.inAnApplet) { + this.vncFrame.add("Center", this); + } + this.vncContainer = this.vncFrame; + } else { + this.vncContainer = this; + } + this.recordingSync = new Object(); + this.options = new OptionsFrame(this); + this.clipboard = new ClipboardFrame(this); + if (RecordingFrame.checkSecurity()) { + this.rec = new RecordingFrame(this); + } + this.sessionFileName = null; + this.recordingActive = false; + this.recordingStatusChanged = false; + this.cursorUpdatesDef = null; + this.eightBitColorsDef = null; + if (this.inSeparateFrame) { + this.vncFrame.addWindowListener(this); + } + this.rfbThread = new Thread(this); + this.rfbThread.start(); + } + + public void update(Graphics graphics) { + } + + public void run() { + this.gridbag = new GridBagLayout(); + this.vncContainer.setLayout(this.gridbag); + GridBagConstraints gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridwidth = 0; + gridBagConstraints.anchor = 18; + gridBagConstraints.fill = 1; + if (this.showControls) { + this.buttonPanel = new ButtonPanel(this); + this.gridbag.setConstraints(this.buttonPanel, gridBagConstraints); + this.vncContainer.add(this.buttonPanel); + } + try { + Serializable serializable; + this.connectAndAuthenticate(); + this.doProtocolInitialisation(); + if (this.options.autoScale && this.inSeparateFrame) { + try { + serializable = this.vncContainer.getToolkit().getScreenSize(); + } + catch (Exception exception) { + serializable = new Dimension(0, 0); + } + this.createCanvas(((Dimension)serializable).width - 32, ((Dimension)serializable).height - 32); + } else { + this.createCanvas(0, 0); + } + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + if (this.inSeparateFrame) { + serializable = new Panel(); + ((Container)serializable).setLayout(new FlowLayout(0, 0, 0)); + ((Container)serializable).add(this.vc); + this.desktopScrollPane = new ScrollPane(0); + gridBagConstraints.fill = 1; + this.gridbag.setConstraints(this.desktopScrollPane, gridBagConstraints); + this.desktopScrollPane.add((Component)serializable); + this.vncFrame.add(this.desktopScrollPane); + this.vncFrame.setTitle(this.rfb.desktopName); + this.vncFrame.pack(); + this.vc.resizeDesktopFrame(); + } else { + this.gridbag.setConstraints(this.vc, gridBagConstraints); + this.add(this.vc); + this.validate(); + } + if (this.showControls) { + this.buttonPanel.enableButtons(); + } + this.moveFocusToDesktop(); + this.processNormalProtocol(); + } + catch (NoRouteToHostException noRouteToHostException) { + this.fatalError("Network error: no route to server: " + this.host, noRouteToHostException); + } + catch (UnknownHostException unknownHostException) { + this.fatalError("Network error: server name unknown: " + this.host, unknownHostException); + } + catch (ConnectException connectException) { + this.fatalError("Network error: could not connect to server: " + this.host + ":" + this.port, connectException); + } + catch (EOFException eOFException) { + if (this.showOfflineDesktop) { + eOFException.printStackTrace(); + System.out.println("Network error: remote side closed connection"); + if (this.vc != null) { + this.vc.enableInput(false); + } + if (this.inSeparateFrame) { + this.vncFrame.setTitle(this.rfb.desktopName + " [disconnected]"); + } + if (this.rfb != null && !this.rfb.closed()) { + this.rfb.close(); + } + if (this.showControls && this.buttonPanel != null) { + this.buttonPanel.disableButtonsOnDisconnect(); + if (this.inSeparateFrame) { + this.vncFrame.pack(); + } else { + this.validate(); + } + } + } else { + this.fatalError("Network error: remote side closed connection", eOFException); + } + } + catch (IOException iOException) { + String string = iOException.getMessage(); + if (string != null && string.length() != 0) { + this.fatalError("Network Error: " + string, iOException); + } else { + this.fatalError(iOException.toString(), iOException); + } + } + catch (Exception exception) { + String string = exception.getMessage(); + if (string != null && string.length() != 0) { + this.fatalError("Error: " + string, exception); + } + this.fatalError(exception.toString(), exception); + } + } + + void createCanvas(int n, int n2) throws IOException { + this.vc = null; + try { + Class clazz = Class.forName("java.awt.Graphics2D"); + clazz = Class.forName("VncCanvas2"); + Class[] classArray = new Class[]{this.getClass(), Integer.TYPE, Integer.TYPE}; + Constructor constructor = clazz.getConstructor(classArray); + Object[] objectArray = new Object[]{this, new Integer(n), new Integer(n2)}; + this.vc = (VncCanvas)constructor.newInstance(objectArray); + } + catch (Exception exception) { + System.out.println("Warning: Java 2D API is not available"); + } + if (this.vc == null) { + this.vc = new VncCanvas(this, n, n2); + } + } + + void processNormalProtocol() throws Exception { + try { + this.vc.processNormalProtocol(); + } + catch (Exception exception) { + if (this.rfbThread == null) { + System.out.println("Ignoring RFB socket exceptions because applet is stopping"); + } + throw exception; + } + } + + void connectAndAuthenticate() throws Exception { + int n; + this.showConnectionStatus("Initializing..."); + if (this.inSeparateFrame) { + this.vncFrame.pack(); + this.vncFrame.show(); + } else { + this.validate(); + } + this.showConnectionStatus("Connecting to " + this.host + ", port " + this.port + "..."); + this.rfb = new RfbProto(this.host, this.port, this); + this.showConnectionStatus("Connected to server"); + this.rfb.readVersionMsg(); + this.showConnectionStatus("RFB server supports protocol version " + this.rfb.serverMajor + "." + this.rfb.serverMinor); + this.rfb.writeVersionMsg(); + this.showConnectionStatus("Using RFB protocol version " + this.rfb.clientMajor + "." + this.rfb.clientMinor); + int n2 = this.rfb.negotiateSecurity(); + if (n2 == 16) { + this.showConnectionStatus("Enabling TightVNC protocol extensions"); + this.rfb.initCapabilities(); + this.rfb.setupTunneling(); + n = this.rfb.negotiateAuthenticationTight(); + } else { + n = n2; + } + switch (n) { + case 1: { + this.showConnectionStatus("No authentication needed"); + this.rfb.authenticateNone(); + break; + } + case 2: { + this.showConnectionStatus("Performing standard VNC authentication"); + String string = this.passwordParam; + if (string == null) { + string = this.askPassword(); + } + if (this.rfb.serverMinor == 4 && this.rfb.clientMinor == 4) { + this.showConnectionStatus("Performing VNC authentication... with Ultr@VNC MS-Logon I"); + String string2 = this.usernameParam; + if (string2 == null) { + throw new Exception("Username not specified, could not complete logon"); + } + this.rfb.authenticateMSLogonI(string, string2); + break; + } + this.rfb.authenticateVNC(string); + break; + } + case -6: { + this.showConnectionStatus("Performing MS-Logon authentication"); + if (this.passwordParam != null && this.usernameParam != null) { + this.rfb.authenticateMSLogon(this.passwordParam, this.usernameParam); + break; + } + throw new Exception("Username or Password not specified, could not complete MS-Logon"); + } + default: { + throw new Exception("Unknown authentication scheme " + n); + } + } + } + + void showConnectionStatus(String string) { + if (string == null) { + if (this.vncContainer.isAncestorOf(this.connStatusLabel)) { + this.vncContainer.remove(this.connStatusLabel); + } + return; + } + System.out.println(this.host + " Status: " + string); + if (this.connStatusLabel == null) { + this.connStatusLabel = new Label("Status: " + string); + this.connStatusLabel.setFont(new Font("Helvetica", 0, 12)); + } else { + this.connStatusLabel.setText("Status: " + string); + } + if (!this.vncContainer.isAncestorOf(this.connStatusLabel)) { + GridBagConstraints gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridwidth = 0; + gridBagConstraints.fill = 2; + gridBagConstraints.anchor = 18; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + gridBagConstraints.insets = new Insets(20, 30, 20, 30); + this.gridbag.setConstraints(this.connStatusLabel, gridBagConstraints); + this.vncContainer.add(this.connStatusLabel); + } + if (this.inSeparateFrame) { + this.vncFrame.pack(); + } else { + this.validate(); + } + } + + String askPassword() throws Exception { + this.showConnectionStatus(null); + AuthPanel authPanel = new AuthPanel(this); + GridBagConstraints gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridwidth = 0; + gridBagConstraints.anchor = 18; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + gridBagConstraints.ipadx = 100; + gridBagConstraints.ipady = 50; + this.gridbag.setConstraints(authPanel, gridBagConstraints); + this.vncContainer.add(authPanel); + if (this.inSeparateFrame) { + this.vncFrame.pack(); + } else { + this.validate(); + } + authPanel.moveFocusToDefaultField(); + String string = authPanel.getPassword(); + this.vncContainer.remove(authPanel); + return string; + } + + void doProtocolInitialisation() throws IOException { + this.rfb.writeClientInit(); + this.rfb.readServerInit(); + System.out.println("Desktop name is " + this.rfb.desktopName); + System.out.println("Desktop size is " + this.rfb.framebufferWidth + " x " + this.rfb.framebufferHeight); + this.setEncodings(); + this.showConnectionStatus(null); + } + + void setEncodings() { + this.setEncodings(false); + } + + void autoSelectEncodings() { + this.setEncodings(true); + } + + void setEncodings(boolean bl) { + if (this.options == null || this.rfb == null || !this.rfb.inNormalProtocol) { + return; + } + int n = this.options.preferredEncoding; + if (n == -1) { + long l = this.rfb.kbitsPerSecond(); + if (this.nEncodingsSaved < 1) { + System.out.println("Using Tight/ZRLE encodings"); + n = 7; + } else if (l > 2000L && this.encodingsSaved[0] != 5) { + System.out.println("Throughput " + l + " kbit/s - changing to Hextile encoding"); + n = 5; + } else if (l < 1000L && this.encodingsSaved[0] != 7) { + System.out.println("Throughput " + l + " kbit/s - changing to Tight/ZRLE encodings"); + n = 7; + } else { + if (bl) { + return; + } + n = this.encodingsSaved[0]; + } + } else if (bl) { + return; + } + int[] nArray = new int[20]; + int n2 = 0; + nArray[n2++] = n; + if (this.options.useCopyRect) { + nArray[n2++] = 1; + } + if (n != 7) { + nArray[n2++] = 7; + } + if (n != 16) { + nArray[n2++] = 16; + } + if (n != 5) { + nArray[n2++] = 5; + } + if (n != 6) { + nArray[n2++] = 6; + } + if (n != 4) { + nArray[n2++] = 4; + } + if (n != 2) { + nArray[n2++] = 2; + } + if (this.options.compressLevel >= 0 && this.options.compressLevel <= 9) { + nArray[n2++] = -256 + this.options.compressLevel; + } + if (this.options.jpegQuality >= 0 && this.options.jpegQuality <= 9) { + nArray[n2++] = -32 + this.options.jpegQuality; + } + if (this.options.requestCursorUpdates) { + nArray[n2++] = -240; + nArray[n2++] = -239; + if (!this.options.ignoreCursorUpdates) { + nArray[n2++] = -232; + } + } + nArray[n2++] = -224; + nArray[n2++] = -223; + boolean bl2 = false; + if (n2 != this.nEncodingsSaved) { + bl2 = true; + } else { + for (int i = 0; i < n2; ++i) { + if (nArray[i] == this.encodingsSaved[i]) continue; + bl2 = true; + break; + } + } + if (bl2) { + try { + this.rfb.writeSetEncodings(nArray, n2); + if (this.vc != null) { + this.vc.softCursorFree(); + } + } + catch (Exception exception) { + exception.printStackTrace(); + } + this.encodingsSaved = nArray; + this.nEncodingsSaved = n2; + } + } + + void setCutText(String string) { + try { + if (this.rfb != null && this.rfb.inNormalProtocol) { + this.rfb.writeClientCutText(string); + } + } + catch (Exception exception) { + exception.printStackTrace(); + } + } + + /* + * WARNING - Removed try catching itself - possible behaviour change. + */ + void setRecordingStatus(String string) { + Object object = this.recordingSync; + synchronized (object) { + this.sessionFileName = string; + this.recordingStatusChanged = true; + } + } + + /* + * WARNING - Removed try catching itself - possible behaviour change. + */ + boolean checkRecordingStatus() throws IOException { + Object object = this.recordingSync; + synchronized (object) { + if (this.recordingStatusChanged) { + this.recordingStatusChanged = false; + if (this.sessionFileName != null) { + this.startRecording(); + return true; + } + this.stopRecording(); + } + } + return false; + } + + /* + * WARNING - Removed try catching itself - possible behaviour change. + */ + protected void startRecording() throws IOException { + Object object = this.recordingSync; + synchronized (object) { + if (!this.recordingActive) { + this.cursorUpdatesDef = this.options.choices[this.options.cursorUpdatesIndex].getSelectedItem(); + this.eightBitColorsDef = this.options.choices[this.options.eightBitColorsIndex].getSelectedItem(); + this.options.choices[this.options.cursorUpdatesIndex].select("Disable"); + this.options.choices[this.options.cursorUpdatesIndex].setEnabled(false); + this.options.setEncodings(); + this.options.choices[this.options.eightBitColorsIndex].select("No"); + this.options.choices[this.options.eightBitColorsIndex].setEnabled(false); + this.options.setColorFormat(); + } else { + this.rfb.closeSession(); + } + System.out.println("Recording the session in " + this.sessionFileName); + this.rfb.startSession(this.sessionFileName); + this.recordingActive = true; + } + } + + /* + * WARNING - Removed try catching itself - possible behaviour change. + */ + protected void stopRecording() throws IOException { + Object object = this.recordingSync; + synchronized (object) { + if (this.recordingActive) { + this.options.choices[this.options.cursorUpdatesIndex].select(this.cursorUpdatesDef); + this.options.choices[this.options.cursorUpdatesIndex].setEnabled(true); + this.options.setEncodings(); + this.options.choices[this.options.eightBitColorsIndex].select(this.eightBitColorsDef); + this.options.choices[this.options.eightBitColorsIndex].setEnabled(true); + this.options.setColorFormat(); + this.rfb.closeSession(); + System.out.println("Session recording stopped."); + } + this.sessionFileName = null; + this.recordingActive = false; + } + } + + void readParameters() { + this.host = this.readParameter("HOST", !this.inAnApplet); + if (this.host == null) { + this.host = this.getCodeBase().getHost(); + if (this.host.equals("")) { + this.fatalError("HOST parameter not specified"); + } + } + String string = this.readParameter("PORT", true); + this.port = Integer.parseInt(string); + this.readPasswordParameters(); + this.usernameParam = this.readParameter("USERNAME", false); + if (this.inAnApplet && (string = this.readParameter("Open New Window", false)) != null && string.equalsIgnoreCase("Yes")) { + this.inSeparateFrame = true; + } + this.showControls = true; + string = this.readParameter("Show Controls", false); + if (string != null && string.equalsIgnoreCase("No")) { + this.showControls = false; + } + this.offerRelogin = true; + string = this.readParameter("Offer Relogin", false); + if (string != null && string.equalsIgnoreCase("No")) { + this.offerRelogin = false; + } + this.showOfflineDesktop = false; + string = this.readParameter("Show Offline Desktop", false); + if (string != null && string.equalsIgnoreCase("Yes")) { + this.showOfflineDesktop = true; + } + this.deferScreenUpdates = this.readIntParameter("Defer screen updates", 20); + this.deferCursorUpdates = this.readIntParameter("Defer cursor updates", 10); + this.deferUpdateRequests = this.readIntParameter("Defer update requests", 50); + this.socketFactory = this.readParameter("SocketFactory", false); + } + + private void readPasswordParameters() { + String string = this.readParameter("ENCPASSWORD", false); + if (string == null) { + this.passwordParam = this.readParameter("PASSWORD", false); + } else { + Object object; + byte[] byArray = new byte[]{0, 0, 0, 0, 0, 0, 0, 0}; + int n = string.length() / 2; + if (n > 8) { + n = 8; + } + for (int i = 0; i < n; ++i) { + object = string.substring(i * 2, i * 2 + 2); + Integer n2 = new Integer(Integer.parseInt((String)object, 16)); + byArray[i] = n2.byteValue(); + } + byte[] byArray2 = new byte[]{23, 82, 107, 6, 35, 78, 88, 7}; + object = new DesCipher(byArray2); + ((DesCipher)object).decrypt(byArray, 0, byArray, 0); + this.passwordParam = new String(byArray); + } + } + + public String readParameter(String string, boolean bl) { + if (this.inAnApplet) { + String string2 = this.getParameter(string); + if (string2 == null && bl) { + this.fatalError(string + " parameter not specified"); + } + return string2; + } + for (int i = 0; i < this.mainArgs.length; i += 2) { + if (!this.mainArgs[i].equalsIgnoreCase(string)) continue; + try { + return this.mainArgs[i + 1]; + } + catch (Exception exception) { + if (bl) { + this.fatalError(string + " parameter not specified"); + } + return null; + } + } + if (bl) { + this.fatalError(string + " parameter not specified"); + } + return null; + } + + int readIntParameter(String string, int n) { + String string2 = this.readParameter(string, false); + int n2 = n; + if (string2 != null) { + try { + n2 = Integer.parseInt(string2); + } + catch (NumberFormatException numberFormatException) { + // empty catch block + } + } + return n2; + } + + void moveFocusToDesktop() { + if (this.vncContainer != null && this.vc != null && this.vncContainer.isAncestorOf(this.vc)) { + this.vc.requestFocus(); + } + } + + public synchronized void disconnect() { + System.out.println("Disconnect"); + if (this.rfb != null && !this.rfb.closed()) { + this.rfb.close(); + } + this.options.dispose(); + this.clipboard.dispose(); + this.rfbThread = null; + if (this.rec != null) { + this.rec.dispose(); + } + if (this.inAnApplet) { + this.showMessage("Disconnected"); + } else if (this.inSeparateFrame) { + System.exit(0); + } else { + this.buttonPanel.disableButtonsOnDisconnect(); + } + } + + public synchronized void fatalError(String string) { + System.out.println(string); + if (this.inAnApplet) { + Thread.currentThread().stop(); + } else if (!this.inSeparateFrame) { + this.showConnectionStatus(string); + } else { + System.exit(1); + } + } + + public synchronized void fatalError(String string, Exception exception) { + if (this.rfb != null && this.rfb.closed()) { + System.out.println("RFB thread finished"); + return; + } + System.out.println(string); + exception.printStackTrace(); + if (this.rfb != null) { + this.rfb.close(); + } + if (this.inAnApplet) { + this.showMessage(string); + } else if (!this.inSeparateFrame) { + this.showConnectionStatus(string); + } else { + System.exit(1); + } + } + + void showMessage(String string) { + this.vncContainer.removeAll(); + Label label = new Label(string, 1); + label.setFont(new Font("Helvetica", 0, 12)); + if (this.offerRelogin) { + Panel panel = new Panel(new GridLayout(0, 1)); + Panel panel2 = new Panel(new FlowLayout(0)); + panel2.add(panel); + this.vncContainer.setLayout(new FlowLayout(0, 30, 16)); + this.vncContainer.add(panel2); + Panel panel3 = new Panel(new FlowLayout(1)); + panel3.add(label); + panel.add(panel3); + panel.add(new ReloginPanel(this)); + } else { + this.vncContainer.setLayout(new FlowLayout(0, 30, 30)); + this.vncContainer.add(label); + } + if (this.inSeparateFrame) { + this.vncFrame.pack(); + } else { + this.validate(); + } + } + + public void stop() { + System.out.println("Stopping applet"); + this.rfbThread = null; + } + + public void destroy() { + System.out.println("Destroying applet"); + this.vncContainer.removeAll(); + this.options.dispose(); + this.clipboard.dispose(); + if (this.rec != null) { + this.rec.dispose(); + } + if (this.rfb != null && !this.rfb.closed()) { + this.rfb.close(); + } + if (this.inSeparateFrame) { + this.vncFrame.dispose(); + } + } + + public void enableInput(boolean bl) { + this.vc.enableInput(bl); + } + + public void windowClosing(WindowEvent windowEvent) { + System.out.println("Closing window"); + if (this.rfb != null) { + this.disconnect(); + } + this.vncContainer.hide(); + if (!this.inAnApplet) { + System.exit(0); + } + } + + public void windowActivated(WindowEvent windowEvent) { + } + + public void windowDeactivated(WindowEvent windowEvent) { + } + + public void windowOpened(WindowEvent windowEvent) { + } + + public void windowClosed(WindowEvent windowEvent) { + } + + public void windowIconified(WindowEvent windowEvent) { + } + + public void windowDeiconified(WindowEvent windowEvent) { + } +} + diff --git a/src/VncViewersList.java b/src/VncViewersList.java new file mode 100644 index 0000000..9ada75e --- /dev/null +++ b/src/VncViewersList.java @@ -0,0 +1,315 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * net.n3.nanoxml.IXMLElement + * net.n3.nanoxml.IXMLParser + * net.n3.nanoxml.IXMLReader + * net.n3.nanoxml.StdXMLReader + * net.n3.nanoxml.XMLElement + * net.n3.nanoxml.XMLParserFactory + * net.n3.nanoxml.XMLWriter + */ +import java.awt.Button; +import java.awt.Container; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.Writer; +import java.net.URL; +import java.util.AbstractList; +import java.util.Enumeration; +import java.util.ListIterator; +import java.util.Vector; +import net.n3.nanoxml.IXMLElement; +import net.n3.nanoxml.IXMLParser; +import net.n3.nanoxml.IXMLReader; +import net.n3.nanoxml.StdXMLReader; +import net.n3.nanoxml.XMLElement; +import net.n3.nanoxml.XMLParserFactory; +import net.n3.nanoxml.XMLWriter; + +class VncViewersList +extends Vector { + private VncThumbnailViewer tnViewer; + + public VncViewersList(VncThumbnailViewer vncThumbnailViewer) { + this.tnViewer = vncThumbnailViewer; + } + + public static boolean isHostsFileEncrypted(String string) { + boolean bl = false; + try { + String string2; + File file = new File(string); + URL uRL = file.toURL(); + string = uRL.getPath(); + IXMLParser iXMLParser = XMLParserFactory.createDefaultXMLParser(); + IXMLReader iXMLReader = StdXMLReader.fileReader((String)string); + iXMLParser.setReader(iXMLReader); + IXMLElement iXMLElement = (IXMLElement)iXMLParser.parse(); + if (iXMLElement.getFullName().equalsIgnoreCase("Manifest") && Integer.parseInt(string2 = iXMLElement.getAttribute("Encrypted", "0")) == 1) { + bl = true; + } + } + catch (Exception exception) { + System.out.println("Error testing file for encryption."); + System.out.println(exception.getMessage()); + } + return bl; + } + + public void loadHosts(String string, String string2) { + try { + File file = new File(string); + URL uRL = file.toURL(); + string = uRL.getPath(); + IXMLParser iXMLParser = XMLParserFactory.createDefaultXMLParser(); + IXMLReader iXMLReader = StdXMLReader.fileReader((String)string); + iXMLParser.setReader(iXMLReader); + IXMLElement iXMLElement = (IXMLElement)iXMLParser.parse(); + if (iXMLElement.getFullName().equalsIgnoreCase("Manifest")) { + boolean bl = 1 == Integer.parseInt(iXMLElement.getAttribute("Encrypted", "0")); + float f = Float.parseFloat(iXMLElement.getAttribute("Version", "0.9")); + System.out.println("Loading file... file format version " + f + " encrypted(" + bl + ")"); + if (bl && string2 == null) { + System.out.println("ERROR: Password needed to properly read file."); + } + Enumeration enumeration = iXMLElement.enumerateChildren(); + while (enumeration.hasMoreElements()) { + IXMLElement iXMLElement2 = (IXMLElement)enumeration.nextElement(); + if (iXMLElement2.getFullName().equalsIgnoreCase("Connection")) { + boolean bl2 = this.parseConnection(iXMLElement2, bl, string2); + continue; + } + System.out.println("Load: Ignoring " + iXMLElement2.getFullName()); + } + } else { + System.out.println("Malformed file, missing manifest tag."); + System.out.println("Found " + iXMLElement.getFullName()); + } + } + catch (Exception exception) { + System.out.println("Error loading file.\n" + exception.getMessage()); + } + } + + private boolean parseConnection(IXMLElement iXMLElement, boolean bl, String string) { + String string2 = iXMLElement.getAttribute("Host", null); + String string3 = iXMLElement.getAttribute("Port", null); + boolean bl2 = true; + if (string3 == null || string2 == null) { + System.out.println("Missing Host or Port attribute"); + bl2 = false; + } else { + int n = Integer.parseInt(string3); + int n2 = Integer.parseInt(iXMLElement.getAttribute("SecType", "1")); + String string4 = iXMLElement.getAttribute("Password", null); + String string5 = iXMLElement.getAttribute("Username", null); + String string6 = iXMLElement.getAttribute("UserDomain", null); + String string7 = iXMLElement.getAttribute("CompName", null); + String string8 = iXMLElement.getAttribute("Comment", null); + if (bl) { + if (string4 != null) { + string4 = DesCipher.decryptData(string4, string); + } + if (string5 != null) { + string5 = DesCipher.decryptData(string5, string); + } + if (string6 != null) { + string6 = DesCipher.decryptData(string6, string); + } + if (string7 != null) { + string7 = DesCipher.decryptData(string7, string); + } + if (string8 != null) { + string8 = DesCipher.decryptData(string8, string); + } + } + switch (n2) { + case 1: { + if (string4 != null || string5 != null) { + System.out.println("WARNING: Password or Username specified for NoAuth"); + } + } + case 2: { + if (string4 == null) { + System.out.println("ERROR: Password missing for VncAuth"); + bl2 = false; + } + if (string5 == null) break; + System.out.println("WARNING: Username specified for VncAuth"); + break; + } + case -6: { + if (string4 != null && string5 == null) break; + System.out.println("ERROR: Password or Username missing for MsAuth"); + bl2 = false; + break; + } + case 5: + case 6: + case 16: + case 17: + case 18: + case 19: { + System.out.println("ERROR: Incomplete security type (" + n2 + ") for Host: " + string2 + " Port: " + n); + } + default: { + bl2 = false; + } + } + System.out.println("LOAD Host: " + string2 + " Port: " + n + " SecType: " + n2); + if (bl2 && this.getViewer(string2, n) == null) { + VncViewer vncViewer = this.launchViewer(string2, n, string4, string5, string6); + } + } + return bl2; + } + + public void saveToEncryptedFile(String string, String string2) { + if (string2 == null || string2.length() == 0) { + System.out.println("WARNING: Saving to encrypted file with empty passkey"); + } + this.saveHosts(true, string, string2); + } + + public void saveToFile(String string) { + this.saveHosts(false, string, null); + } + + private void saveHosts(boolean bl, String string, String string2) { + String string3; + Object object; + XMLElement xMLElement = new XMLElement("Manifest"); + xMLElement.setAttribute("Encrypted", bl ? "1" : "0"); + xMLElement.setAttribute("Version", Float.toString(1.4f)); + ListIterator listIterator = ((AbstractList)this).listIterator(); + while (listIterator.hasNext()) { + object = (VncViewer)listIterator.next(); + string3 = ((VncViewer)object).host; + String string4 = Integer.toString(((VncViewer)object).port); + String string5 = ((VncViewer)object).passwordParam; + String string6 = ((VncViewer)object).usernameParam; + String string7 = "1"; + if (string5 != null && string5.length() != 0) { + string7 = "2"; + if (string6 != null && string6.length() != 0) { + string7 = "-6"; + } + } + if (bl) { + if (string7 != "1") { + string5 = DesCipher.encryptData(string5, string2); + } + if (string7 == "-6") { + string6 = DesCipher.encryptData(string6, string2); + } + } + IXMLElement iXMLElement = xMLElement.createElement("Connection"); + xMLElement.addChild(iXMLElement); + iXMLElement.setAttribute("Host", string3); + iXMLElement.setAttribute("Port", string4); + iXMLElement.setAttribute("SecType", string7); + if (string7 != "2" && string7 != "-6") continue; + iXMLElement.setAttribute("Password", string5); + if (string7 != "-6") continue; + iXMLElement.setAttribute("Username", string6); + } + try { + object = new PrintWriter(new FileOutputStream(string)); + string3 = new XMLWriter((Writer)object); + ((PrintWriter)object).println(""); + string3.write((IXMLElement)xMLElement, true); + } + catch (IOException iOException) { + System.out.print("Error saving file.\n" + iOException.getMessage()); + } + } + + public VncViewer launchViewer(String string, int n, String string2, String string3, String string4) { + VncViewer vncViewer = VncViewersList.launchViewer(this.tnViewer, string, n, string2, string3, string4); + this.add(vncViewer); + this.tnViewer.addViewer(vncViewer); + return vncViewer; + } + + public static VncViewer launchViewer(VncThumbnailViewer vncThumbnailViewer, String string, int n, String string2, String string3, String string4) { + String[] stringArray; + int n2; + String[] stringArray2 = new String[]{"host", string, "port", Integer.toString(n)}; + if (string2 != null && string2.length() != 0) { + n2 = stringArray2.length + 2; + stringArray = new String[n2]; + System.arraycopy(stringArray2, 0, stringArray, 0, n2 - 2); + stringArray[n2 - 2] = "password"; + stringArray[n2 - 1] = string2; + stringArray2 = stringArray; + } + if (string3 != null && string3.length() != 0) { + n2 = stringArray2.length + 2; + stringArray = new String[n2]; + System.arraycopy(stringArray2, 0, stringArray, 0, n2 - 2); + stringArray[n2 - 2] = "username"; + stringArray[n2 - 1] = string3; + stringArray2 = stringArray; + } + if (string4 != null && string4.length() != 0) { + n2 = stringArray2.length + 2; + stringArray = new String[n2]; + System.arraycopy(stringArray2, 0, stringArray, 0, n2 - 2); + stringArray[n2 - 2] = "userdomain"; + stringArray[n2 - 1] = string4; + stringArray2 = stringArray; + } + System.out.println("Launch Host: " + string + ":" + n); + VncViewer vncViewer = new VncViewer(); + vncViewer.mainArgs = stringArray2; + vncViewer.inAnApplet = false; + vncViewer.inSeparateFrame = false; + vncViewer.showControls = true; + vncViewer.showOfflineDesktop = true; + vncViewer.vncFrame = vncThumbnailViewer; + vncViewer.init(); + vncViewer.options.viewOnly = true; + vncViewer.options.autoScale = true; + vncViewer.options.scalingFactor = 10; + vncViewer.addContainerListener(vncThumbnailViewer); + vncViewer.start(); + return vncViewer; + } + + public VncViewer getViewer(String string, int n) { + VncViewer vncViewer = null; + ListIterator listIterator = ((AbstractList)this).listIterator(); + while (listIterator.hasNext()) { + vncViewer = (VncViewer)listIterator.next(); + if (vncViewer.host != string || vncViewer.port != n) continue; + return vncViewer; + } + return null; + } + + public VncViewer getViewer(Container container) { + VncViewer vncViewer = null; + ListIterator listIterator = ((AbstractList)this).listIterator(); + while (listIterator.hasNext()) { + vncViewer = (VncViewer)listIterator.next(); + if (!container.isAncestorOf(vncViewer)) continue; + return vncViewer; + } + return null; + } + + public VncViewer getViewer(Button button) { + ListIterator listIterator = ((AbstractList)this).listIterator(); + while (listIterator.hasNext()) { + VncViewer vncViewer = (VncViewer)listIterator.next(); + if (!vncViewer.getParent().isAncestorOf(button)) continue; + return vncViewer; + } + return null; + } +} + diff --git a/src/ZlibInStream.java b/src/ZlibInStream.java new file mode 100644 index 0000000..8c2523d --- /dev/null +++ b/src/ZlibInStream.java @@ -0,0 +1,97 @@ +/* + * Decompiled with CFR 0.152. + */ +import java.util.zip.DataFormatException; +import java.util.zip.Inflater; + +public class ZlibInStream +extends InStream { + static final int defaultBufSize = 16384; + private InStream underlying; + private int bufSize; + private int ptrOffset; + private Inflater inflater; + private int bytesIn; + + public ZlibInStream(int n) { + this.bufSize = n; + this.b = new byte[this.bufSize]; + this.ptrOffset = 0; + this.end = 0; + this.ptr = 0; + this.inflater = new Inflater(); + } + + public ZlibInStream() { + this(16384); + } + + public void setUnderlying(InStream inStream, int n) { + this.underlying = inStream; + this.bytesIn = n; + this.end = 0; + this.ptr = 0; + } + + public void reset() throws Exception { + this.end = 0; + this.ptr = 0; + if (this.underlying == null) { + return; + } + while (this.bytesIn > 0) { + this.decompress(); + this.end = 0; + } + this.underlying = null; + } + + public int pos() { + return this.ptrOffset + this.ptr; + } + + protected int overrun(int n, int n2) throws Exception { + if (n > this.bufSize) { + throw new Exception("ZlibInStream overrun: max itemSize exceeded"); + } + if (this.underlying == null) { + throw new Exception("ZlibInStream overrun: no underlying stream"); + } + if (this.end - this.ptr != 0) { + System.arraycopy(this.b, this.ptr, this.b, 0, this.end - this.ptr); + } + this.ptrOffset += this.ptr; + this.end -= this.ptr; + this.ptr = 0; + while (this.end < n) { + this.decompress(); + } + if (n * n2 > this.end) { + n2 = this.end / n; + } + return n2; + } + + private void decompress() throws Exception { + try { + this.underlying.check(1); + int n = this.underlying.getend() - this.underlying.getptr(); + if (n > this.bytesIn) { + n = this.bytesIn; + } + if (this.inflater.needsInput()) { + this.inflater.setInput(this.underlying.getbuf(), this.underlying.getptr(), n); + } + int n2 = this.inflater.inflate(this.b, this.end, this.bufSize - this.end); + this.end += n2; + if (this.inflater.needsInput()) { + this.bytesIn -= n; + this.underlying.setptr(this.underlying.getptr() + n); + } + } + catch (DataFormatException dataFormatException) { + throw new Exception("ZlibInStream: inflate failed"); + } + } +} + diff --git a/src/net/n3/nanoxml/CDATAReader.java b/src/net/n3/nanoxml/CDATAReader.java new file mode 100644 index 0000000..dc7f21d --- /dev/null +++ b/src/net/n3/nanoxml/CDATAReader.java @@ -0,0 +1,91 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * net.n3.nanoxml.CDATAReader + * net.n3.nanoxml.IXMLReader + */ +package net.n3.nanoxml; + +import java.io.IOException; +import java.io.Reader; +import net.n3.nanoxml.IXMLReader; + +class CDATAReader +extends Reader { + private IXMLReader reader; + private char savedChar; + private boolean atEndOfData; + + CDATAReader(IXMLReader iXMLReader) { + this.reader = iXMLReader; + this.savedChar = '\u0000'; + this.atEndOfData = false; + } + + protected void finalize() throws Throwable { + this.reader = null; + super.finalize(); + } + + public int read(char[] cArray, int n, int n2) throws IOException { + int n3 = 0; + if (this.atEndOfData) { + return -1; + } + if (n + n2 > cArray.length) { + n2 = cArray.length - n; + } + while (n3 < n2) { + char c = this.savedChar; + if (c == '\u0000') { + c = this.reader.read(); + } else { + this.savedChar = '\u0000'; + } + if (c == ']') { + char c2 = this.reader.read(); + if (c2 == ']') { + char c3 = this.reader.read(); + if (c3 == '>') { + this.atEndOfData = true; + break; + } + this.savedChar = c2; + this.reader.unread(c3); + } else { + this.reader.unread(c2); + } + } + cArray[n3] = c; + ++n3; + } + if (n3 == 0) { + n3 = -1; + } + return n3; + } + + public void close() throws IOException { + while (!this.atEndOfData) { + char c = this.savedChar; + if (c == '\u0000') { + c = this.reader.read(); + } else { + this.savedChar = '\u0000'; + } + if (c != ']') continue; + char c2 = this.reader.read(); + if (c2 == ']') { + char c3 = this.reader.read(); + if (c3 == '>') break; + this.savedChar = c2; + this.reader.unread(c3); + continue; + } + this.reader.unread(c2); + } + this.atEndOfData = true; + } +} + diff --git a/src/net/n3/nanoxml/ContentReader.java b/src/net/n3/nanoxml/ContentReader.java new file mode 100644 index 0000000..a880c68 --- /dev/null +++ b/src/net/n3/nanoxml/ContentReader.java @@ -0,0 +1,111 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * net.n3.nanoxml.ContentReader + * net.n3.nanoxml.IXMLEntityResolver + * net.n3.nanoxml.IXMLReader + * net.n3.nanoxml.XMLParseException + * net.n3.nanoxml.XMLUtil + */ +package net.n3.nanoxml; + +import java.io.IOException; +import java.io.Reader; +import net.n3.nanoxml.IXMLEntityResolver; +import net.n3.nanoxml.IXMLReader; +import net.n3.nanoxml.XMLParseException; +import net.n3.nanoxml.XMLUtil; + +class ContentReader +extends Reader { + private IXMLReader reader; + private String buffer; + private int bufferIndex; + private IXMLEntityResolver resolver; + + ContentReader(IXMLReader iXMLReader, IXMLEntityResolver iXMLEntityResolver, String string) { + this.reader = iXMLReader; + this.resolver = iXMLEntityResolver; + this.buffer = string; + this.bufferIndex = 0; + } + + protected void finalize() throws Throwable { + this.reader = null; + this.resolver = null; + this.buffer = null; + super.finalize(); + } + + public int read(char[] cArray, int n, int n2) throws IOException { + try { + int n3 = 0; + int n4 = this.buffer.length(); + if (n + n2 > cArray.length) { + n2 = cArray.length - n; + } + while (n3 < n2) { + char c; + String string = ""; + if (this.bufferIndex < n4) { + c = this.buffer.charAt(this.bufferIndex); + ++this.bufferIndex; + cArray[n3] = c; + ++n3; + continue; + } + string = XMLUtil.read((IXMLReader)this.reader, (char)'&'); + c = string.charAt(0); + if (c == '<') { + this.reader.unread(c); + break; + } + if (c == '&' && string.length() > 1) { + if (string.charAt(1) == '#') { + c = XMLUtil.processCharLiteral((String)string); + } else { + XMLUtil.processEntity((String)string, (IXMLReader)this.reader, (IXMLEntityResolver)this.resolver); + continue; + } + } + cArray[n3] = c; + ++n3; + } + if (n3 == 0) { + n3 = -1; + } + return n3; + } + catch (XMLParseException xMLParseException) { + throw new IOException(xMLParseException.getMessage()); + } + } + + public void close() throws IOException { + try { + int n = this.buffer.length(); + while (true) { + char c; + String string = ""; + if (this.bufferIndex < n) { + c = this.buffer.charAt(this.bufferIndex); + ++this.bufferIndex; + continue; + } + string = XMLUtil.read((IXMLReader)this.reader, (char)'&'); + c = string.charAt(0); + if (c == '<') { + this.reader.unread(c); + break; + } + if (c != '&' || string.length() <= 1 || string.charAt(1) == '#') continue; + XMLUtil.processEntity((String)string, (IXMLReader)this.reader, (IXMLEntityResolver)this.resolver); + } + } + catch (XMLParseException xMLParseException) { + throw new IOException(xMLParseException.getMessage()); + } + } +} + diff --git a/src/net/n3/nanoxml/IXMLBuilder.java b/src/net/n3/nanoxml/IXMLBuilder.java new file mode 100644 index 0000000..8a7c504 --- /dev/null +++ b/src/net/n3/nanoxml/IXMLBuilder.java @@ -0,0 +1,28 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * net.n3.nanoxml.IXMLBuilder + */ +package net.n3.nanoxml; + +import java.io.Reader; + +public interface IXMLBuilder { + public void startBuilding(String var1, int var2) throws Exception; + + public void newProcessingInstruction(String var1, Reader var2) throws Exception; + + public void startElement(String var1, String var2, String var3, String var4, int var5) throws Exception; + + public void addAttribute(String var1, String var2, String var3, String var4, String var5) throws Exception; + + public void elementAttributesProcessed(String var1, String var2, String var3) throws Exception; + + public void endElement(String var1, String var2, String var3) throws Exception; + + public void addPCData(Reader var1, String var2, int var3) throws Exception; + + public Object getResult() throws Exception; +} + diff --git a/src/net/n3/nanoxml/IXMLElement.java b/src/net/n3/nanoxml/IXMLElement.java new file mode 100644 index 0000000..58bc50f --- /dev/null +++ b/src/net/n3/nanoxml/IXMLElement.java @@ -0,0 +1,112 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * net.n3.nanoxml.IXMLElement + */ +package net.n3.nanoxml; + +import java.util.Enumeration; +import java.util.Properties; +import java.util.Vector; + +public interface IXMLElement { + public static final int NO_LINE = -1; + + public IXMLElement createPCDataElement(); + + public IXMLElement createElement(String var1); + + public IXMLElement createElement(String var1, String var2, int var3); + + public IXMLElement createElement(String var1, String var2); + + public IXMLElement createElement(String var1, String var2, String var3, int var4); + + public IXMLElement getParent(); + + public String getFullName(); + + public String getName(); + + public String getNamespace(); + + public void setName(String var1); + + public void setName(String var1, String var2); + + public void addChild(IXMLElement var1); + + public void removeChild(IXMLElement var1); + + public void removeChildAtIndex(int var1); + + public Enumeration enumerateChildren(); + + public boolean isLeaf(); + + public boolean hasChildren(); + + public int getChildrenCount(); + + public Vector getChildren(); + + public IXMLElement getChildAtIndex(int var1) throws ArrayIndexOutOfBoundsException; + + public IXMLElement getFirstChildNamed(String var1); + + public IXMLElement getFirstChildNamed(String var1, String var2); + + public Vector getChildrenNamed(String var1); + + public Vector getChildrenNamed(String var1, String var2); + + public int getAttributeCount(); + + public String getAttribute(String var1); + + public String getAttribute(String var1, String var2); + + public String getAttribute(String var1, String var2, String var3); + + public int getAttribute(String var1, int var2); + + public int getAttribute(String var1, String var2, int var3); + + public String getAttributeType(String var1); + + public String getAttributeNamespace(String var1); + + public String getAttributeType(String var1, String var2); + + public void setAttribute(String var1, String var2); + + public void setAttribute(String var1, String var2, String var3); + + public void removeAttribute(String var1); + + public void removeAttribute(String var1, String var2); + + public Enumeration enumerateAttributeNames(); + + public boolean hasAttribute(String var1); + + public boolean hasAttribute(String var1, String var2); + + public Properties getAttributes(); + + public Properties getAttributesInNamespace(String var1); + + public String getSystemID(); + + public int getLineNr(); + + public String getContent(); + + public void setContent(String var1); + + public boolean equals(Object var1); + + public boolean equalsXMLElement(IXMLElement var1); +} + diff --git a/src/net/n3/nanoxml/IXMLEntityResolver.java b/src/net/n3/nanoxml/IXMLEntityResolver.java new file mode 100644 index 0000000..0123f3f --- /dev/null +++ b/src/net/n3/nanoxml/IXMLEntityResolver.java @@ -0,0 +1,24 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * net.n3.nanoxml.IXMLEntityResolver + * net.n3.nanoxml.IXMLReader + * net.n3.nanoxml.XMLParseException + */ +package net.n3.nanoxml; + +import java.io.Reader; +import net.n3.nanoxml.IXMLReader; +import net.n3.nanoxml.XMLParseException; + +public interface IXMLEntityResolver { + public void addInternalEntity(String var1, String var2); + + public void addExternalEntity(String var1, String var2, String var3); + + public Reader getEntity(IXMLReader var1, String var2) throws XMLParseException; + + public boolean isExternalEntity(String var1); +} + diff --git a/src/net/n3/nanoxml/IXMLParser.java b/src/net/n3/nanoxml/IXMLParser.java new file mode 100644 index 0000000..86b4ded --- /dev/null +++ b/src/net/n3/nanoxml/IXMLParser.java @@ -0,0 +1,39 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * net.n3.nanoxml.IXMLBuilder + * net.n3.nanoxml.IXMLEntityResolver + * net.n3.nanoxml.IXMLParser + * net.n3.nanoxml.IXMLReader + * net.n3.nanoxml.IXMLValidator + * net.n3.nanoxml.XMLException + */ +package net.n3.nanoxml; + +import net.n3.nanoxml.IXMLBuilder; +import net.n3.nanoxml.IXMLEntityResolver; +import net.n3.nanoxml.IXMLReader; +import net.n3.nanoxml.IXMLValidator; +import net.n3.nanoxml.XMLException; + +public interface IXMLParser { + public void setReader(IXMLReader var1); + + public IXMLReader getReader(); + + public void setBuilder(IXMLBuilder var1); + + public IXMLBuilder getBuilder(); + + public void setValidator(IXMLValidator var1); + + public IXMLValidator getValidator(); + + public void setResolver(IXMLEntityResolver var1); + + public IXMLEntityResolver getResolver(); + + public Object parse() throws XMLException; +} + diff --git a/src/net/n3/nanoxml/IXMLReader.java b/src/net/n3/nanoxml/IXMLReader.java new file mode 100644 index 0000000..f0c41b1 --- /dev/null +++ b/src/net/n3/nanoxml/IXMLReader.java @@ -0,0 +1,41 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * net.n3.nanoxml.IXMLReader + */ +package net.n3.nanoxml; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.Reader; +import java.net.MalformedURLException; + +public interface IXMLReader { + public char read() throws IOException; + + public boolean atEOFOfCurrentStream() throws IOException; + + public boolean atEOF() throws IOException; + + public void unread(char var1) throws IOException; + + public int getLineNr(); + + public Reader openStream(String var1, String var2) throws MalformedURLException, FileNotFoundException, IOException; + + public void startNewStream(Reader var1); + + public void startNewStream(Reader var1, boolean var2); + + public int getStreamLevel(); + + public void setSystemID(String var1) throws MalformedURLException; + + public void setPublicID(String var1); + + public String getSystemID(); + + public String getPublicID(); +} + diff --git a/src/net/n3/nanoxml/IXMLValidator.java b/src/net/n3/nanoxml/IXMLValidator.java new file mode 100644 index 0000000..7468f42 --- /dev/null +++ b/src/net/n3/nanoxml/IXMLValidator.java @@ -0,0 +1,32 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * net.n3.nanoxml.IXMLEntityResolver + * net.n3.nanoxml.IXMLReader + * net.n3.nanoxml.IXMLValidator + */ +package net.n3.nanoxml; + +import java.util.Properties; +import net.n3.nanoxml.IXMLEntityResolver; +import net.n3.nanoxml.IXMLReader; + +public interface IXMLValidator { + public void setParameterEntityResolver(IXMLEntityResolver var1); + + public IXMLEntityResolver getParameterEntityResolver(); + + public void parseDTD(String var1, IXMLReader var2, IXMLEntityResolver var3, boolean var4) throws Exception; + + public void elementStarted(String var1, String var2, int var3) throws Exception; + + public void elementEnded(String var1, String var2, int var3) throws Exception; + + public void attributeAdded(String var1, String var2, String var3, int var4) throws Exception; + + public void elementAttributesProcessed(String var1, Properties var2, String var3, int var4) throws Exception; + + public void PCDataAdded(String var1, int var2) throws Exception; +} + diff --git a/src/net/n3/nanoxml/NonValidator.java b/src/net/n3/nanoxml/NonValidator.java new file mode 100644 index 0000000..a38ede0 --- /dev/null +++ b/src/net/n3/nanoxml/NonValidator.java @@ -0,0 +1,354 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * net.n3.nanoxml.CDATAReader + * net.n3.nanoxml.IXMLEntityResolver + * net.n3.nanoxml.IXMLReader + * net.n3.nanoxml.IXMLValidator + * net.n3.nanoxml.NonValidator + * net.n3.nanoxml.XMLEntityResolver + * net.n3.nanoxml.XMLUtil + */ +package net.n3.nanoxml; + +import java.io.Reader; +import java.io.StringReader; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Properties; +import java.util.Stack; +import net.n3.nanoxml.CDATAReader; +import net.n3.nanoxml.IXMLEntityResolver; +import net.n3.nanoxml.IXMLReader; +import net.n3.nanoxml.IXMLValidator; +import net.n3.nanoxml.XMLEntityResolver; +import net.n3.nanoxml.XMLUtil; + +public class NonValidator +implements IXMLValidator { + protected IXMLEntityResolver parameterEntityResolver; + protected Hashtable attributeDefaultValues = new Hashtable(); + protected Stack currentElements = new Stack(); + + public NonValidator() { + this.parameterEntityResolver = new XMLEntityResolver(); + } + + protected void finalize() throws Throwable { + this.parameterEntityResolver = null; + this.attributeDefaultValues.clear(); + this.attributeDefaultValues = null; + this.currentElements.clear(); + this.currentElements = null; + super.finalize(); + } + + public void setParameterEntityResolver(IXMLEntityResolver iXMLEntityResolver) { + this.parameterEntityResolver = iXMLEntityResolver; + } + + public IXMLEntityResolver getParameterEntityResolver() { + return this.parameterEntityResolver; + } + + public void parseDTD(String string, IXMLReader iXMLReader, IXMLEntityResolver iXMLEntityResolver, boolean bl) throws Exception { + XMLUtil.skipWhitespace((IXMLReader)iXMLReader, null); + int n = iXMLReader.getStreamLevel(); + while (true) { + String string2; + char c; + if ((c = (string2 = XMLUtil.read((IXMLReader)iXMLReader, (char)'%')).charAt(0)) == '%') { + XMLUtil.processEntity((String)string2, (IXMLReader)iXMLReader, (IXMLEntityResolver)this.parameterEntityResolver); + continue; + } + if (c == '<') { + this.processElement(iXMLReader, iXMLEntityResolver); + } else { + if (c == ']') { + return; + } + XMLUtil.errorInvalidInput((String)iXMLReader.getSystemID(), (int)iXMLReader.getLineNr(), (String)string2); + } + do { + c = iXMLReader.read(); + if (!bl || iXMLReader.getStreamLevel() >= n) continue; + iXMLReader.unread(c); + return; + } while (c == ' ' || c == '\t' || c == '\n' || c == '\r'); + iXMLReader.unread(c); + } + } + + protected void processElement(IXMLReader iXMLReader, IXMLEntityResolver iXMLEntityResolver) throws Exception { + String string = XMLUtil.read((IXMLReader)iXMLReader, (char)'%'); + char c = string.charAt(0); + if (c != '!') { + XMLUtil.skipTag((IXMLReader)iXMLReader); + return; + } + string = XMLUtil.read((IXMLReader)iXMLReader, (char)'%'); + c = string.charAt(0); + switch (c) { + case '-': { + XMLUtil.skipComment((IXMLReader)iXMLReader); + break; + } + case '[': { + this.processConditionalSection(iXMLReader, iXMLEntityResolver); + break; + } + case 'E': { + this.processEntity(iXMLReader, iXMLEntityResolver); + break; + } + case 'A': { + this.processAttList(iXMLReader, iXMLEntityResolver); + break; + } + default: { + XMLUtil.skipTag((IXMLReader)iXMLReader); + } + } + } + + protected void processConditionalSection(IXMLReader iXMLReader, IXMLEntityResolver iXMLEntityResolver) throws Exception { + int n; + XMLUtil.skipWhitespace((IXMLReader)iXMLReader, null); + String string = XMLUtil.read((IXMLReader)iXMLReader, (char)'%'); + char c = string.charAt(0); + if (c != 'I') { + XMLUtil.skipTag((IXMLReader)iXMLReader); + return; + } + string = XMLUtil.read((IXMLReader)iXMLReader, (char)'%'); + c = string.charAt(0); + switch (c) { + case 'G': { + this.processIgnoreSection(iXMLReader, iXMLEntityResolver); + return; + } + case 'N': { + break; + } + default: { + XMLUtil.skipTag((IXMLReader)iXMLReader); + return; + } + } + if (!XMLUtil.checkLiteral((IXMLReader)iXMLReader, (String)"CLUDE")) { + XMLUtil.skipTag((IXMLReader)iXMLReader); + return; + } + XMLUtil.skipWhitespace((IXMLReader)iXMLReader, null); + string = XMLUtil.read((IXMLReader)iXMLReader, (char)'%'); + c = string.charAt(0); + if (c != '[') { + XMLUtil.skipTag((IXMLReader)iXMLReader); + return; + } + CDATAReader cDATAReader = new CDATAReader(iXMLReader); + StringBuffer stringBuffer = new StringBuffer(1024); + while ((n = cDATAReader.read()) >= 0) { + stringBuffer.append((char)n); + } + cDATAReader.close(); + iXMLReader.startNewStream((Reader)new StringReader(stringBuffer.toString())); + } + + protected void processIgnoreSection(IXMLReader iXMLReader, IXMLEntityResolver iXMLEntityResolver) throws Exception { + if (!XMLUtil.checkLiteral((IXMLReader)iXMLReader, (String)"NORE")) { + XMLUtil.skipTag((IXMLReader)iXMLReader); + return; + } + XMLUtil.skipWhitespace((IXMLReader)iXMLReader, null); + String string = XMLUtil.read((IXMLReader)iXMLReader, (char)'%'); + char c = string.charAt(0); + if (c != '[') { + XMLUtil.skipTag((IXMLReader)iXMLReader); + return; + } + CDATAReader cDATAReader = new CDATAReader(iXMLReader); + cDATAReader.close(); + } + + protected void processAttList(IXMLReader iXMLReader, IXMLEntityResolver iXMLEntityResolver) throws Exception { + if (!XMLUtil.checkLiteral((IXMLReader)iXMLReader, (String)"TTLIST")) { + XMLUtil.skipTag((IXMLReader)iXMLReader); + return; + } + XMLUtil.skipWhitespace((IXMLReader)iXMLReader, null); + String string = XMLUtil.read((IXMLReader)iXMLReader, (char)'%'); + char c = string.charAt(0); + while (c == '%') { + XMLUtil.processEntity((String)string, (IXMLReader)iXMLReader, (IXMLEntityResolver)this.parameterEntityResolver); + string = XMLUtil.read((IXMLReader)iXMLReader, (char)'%'); + c = string.charAt(0); + } + iXMLReader.unread(c); + String string2 = XMLUtil.scanIdentifier((IXMLReader)iXMLReader); + XMLUtil.skipWhitespace((IXMLReader)iXMLReader, null); + string = XMLUtil.read((IXMLReader)iXMLReader, (char)'%'); + c = string.charAt(0); + while (c == '%') { + XMLUtil.processEntity((String)string, (IXMLReader)iXMLReader, (IXMLEntityResolver)this.parameterEntityResolver); + string = XMLUtil.read((IXMLReader)iXMLReader, (char)'%'); + c = string.charAt(0); + } + Properties properties = new Properties(); + while (c != '>') { + iXMLReader.unread(c); + String string3 = XMLUtil.scanIdentifier((IXMLReader)iXMLReader); + XMLUtil.skipWhitespace((IXMLReader)iXMLReader, null); + string = XMLUtil.read((IXMLReader)iXMLReader, (char)'%'); + c = string.charAt(0); + while (c == '%') { + XMLUtil.processEntity((String)string, (IXMLReader)iXMLReader, (IXMLEntityResolver)this.parameterEntityResolver); + string = XMLUtil.read((IXMLReader)iXMLReader, (char)'%'); + c = string.charAt(0); + } + if (c == '(') { + while (c != ')') { + string = XMLUtil.read((IXMLReader)iXMLReader, (char)'%'); + c = string.charAt(0); + while (c == '%') { + XMLUtil.processEntity((String)string, (IXMLReader)iXMLReader, (IXMLEntityResolver)this.parameterEntityResolver); + string = XMLUtil.read((IXMLReader)iXMLReader, (char)'%'); + c = string.charAt(0); + } + } + } else { + iXMLReader.unread(c); + XMLUtil.scanIdentifier((IXMLReader)iXMLReader); + } + XMLUtil.skipWhitespace((IXMLReader)iXMLReader, null); + string = XMLUtil.read((IXMLReader)iXMLReader, (char)'%'); + c = string.charAt(0); + while (c == '%') { + XMLUtil.processEntity((String)string, (IXMLReader)iXMLReader, (IXMLEntityResolver)this.parameterEntityResolver); + string = XMLUtil.read((IXMLReader)iXMLReader, (char)'%'); + c = string.charAt(0); + } + if (c == '#') { + string = XMLUtil.scanIdentifier((IXMLReader)iXMLReader); + XMLUtil.skipWhitespace((IXMLReader)iXMLReader, null); + if (!string.equals("FIXED")) { + XMLUtil.skipWhitespace((IXMLReader)iXMLReader, null); + string = XMLUtil.read((IXMLReader)iXMLReader, (char)'%'); + c = string.charAt(0); + while (c == '%') { + XMLUtil.processEntity((String)string, (IXMLReader)iXMLReader, (IXMLEntityResolver)this.parameterEntityResolver); + string = XMLUtil.read((IXMLReader)iXMLReader, (char)'%'); + c = string.charAt(0); + } + continue; + } + } else { + iXMLReader.unread(c); + } + String string4 = XMLUtil.scanString((IXMLReader)iXMLReader, (char)'%', (IXMLEntityResolver)this.parameterEntityResolver); + properties.put(string3, string4); + XMLUtil.skipWhitespace((IXMLReader)iXMLReader, null); + string = XMLUtil.read((IXMLReader)iXMLReader, (char)'%'); + c = string.charAt(0); + while (c == '%') { + XMLUtil.processEntity((String)string, (IXMLReader)iXMLReader, (IXMLEntityResolver)this.parameterEntityResolver); + string = XMLUtil.read((IXMLReader)iXMLReader, (char)'%'); + c = string.charAt(0); + } + } + if (!properties.isEmpty()) { + this.attributeDefaultValues.put(string2, properties); + } + } + + protected void processEntity(IXMLReader iXMLReader, IXMLEntityResolver iXMLEntityResolver) throws Exception { + if (!XMLUtil.checkLiteral((IXMLReader)iXMLReader, (String)"NTITY")) { + XMLUtil.skipTag((IXMLReader)iXMLReader); + return; + } + XMLUtil.skipWhitespace((IXMLReader)iXMLReader, null); + char c = XMLUtil.readChar((IXMLReader)iXMLReader, (char)'\u0000'); + if (c == '%') { + XMLUtil.skipWhitespace((IXMLReader)iXMLReader, null); + iXMLEntityResolver = this.parameterEntityResolver; + } else { + iXMLReader.unread(c); + } + String string = XMLUtil.scanIdentifier((IXMLReader)iXMLReader); + XMLUtil.skipWhitespace((IXMLReader)iXMLReader, null); + c = XMLUtil.readChar((IXMLReader)iXMLReader, (char)'%'); + String string2 = null; + String string3 = null; + switch (c) { + case 'P': { + if (!XMLUtil.checkLiteral((IXMLReader)iXMLReader, (String)"UBLIC")) { + XMLUtil.skipTag((IXMLReader)iXMLReader); + return; + } + XMLUtil.skipWhitespace((IXMLReader)iXMLReader, null); + string3 = XMLUtil.scanString((IXMLReader)iXMLReader, (char)'%', (IXMLEntityResolver)this.parameterEntityResolver); + XMLUtil.skipWhitespace((IXMLReader)iXMLReader, null); + string2 = XMLUtil.scanString((IXMLReader)iXMLReader, (char)'%', (IXMLEntityResolver)this.parameterEntityResolver); + XMLUtil.skipWhitespace((IXMLReader)iXMLReader, null); + XMLUtil.readChar((IXMLReader)iXMLReader, (char)'%'); + break; + } + case 'S': { + if (!XMLUtil.checkLiteral((IXMLReader)iXMLReader, (String)"YSTEM")) { + XMLUtil.skipTag((IXMLReader)iXMLReader); + return; + } + XMLUtil.skipWhitespace((IXMLReader)iXMLReader, null); + string2 = XMLUtil.scanString((IXMLReader)iXMLReader, (char)'%', (IXMLEntityResolver)this.parameterEntityResolver); + XMLUtil.skipWhitespace((IXMLReader)iXMLReader, null); + XMLUtil.readChar((IXMLReader)iXMLReader, (char)'%'); + break; + } + case '\"': + case '\'': { + iXMLReader.unread(c); + String string4 = XMLUtil.scanString((IXMLReader)iXMLReader, (char)'%', (IXMLEntityResolver)this.parameterEntityResolver); + iXMLEntityResolver.addInternalEntity(string, string4); + XMLUtil.skipWhitespace((IXMLReader)iXMLReader, null); + XMLUtil.readChar((IXMLReader)iXMLReader, (char)'%'); + break; + } + default: { + XMLUtil.skipTag((IXMLReader)iXMLReader); + } + } + if (string2 != null) { + iXMLEntityResolver.addExternalEntity(string, string3, string2); + } + } + + public void elementStarted(String string, String string2, int n) { + Properties properties = (Properties)this.attributeDefaultValues.get(string); + properties = properties == null ? new Properties() : (Properties)properties.clone(); + this.currentElements.push(properties); + } + + public void elementEnded(String string, String string2, int n) { + } + + public void elementAttributesProcessed(String string, Properties properties, String string2, int n) { + Properties properties2 = (Properties)this.currentElements.pop(); + Enumeration enumeration = properties2.keys(); + while (enumeration.hasMoreElements()) { + String string3 = (String)enumeration.nextElement(); + properties.put(string3, properties2.get(string3)); + } + } + + public void attributeAdded(String string, String string2, String string3, int n) { + Properties properties = (Properties)this.currentElements.peek(); + if (properties.containsKey(string)) { + properties.remove(string); + } + } + + public void PCDataAdded(String string, int n) { + } +} + diff --git a/src/net/n3/nanoxml/PIReader.java b/src/net/n3/nanoxml/PIReader.java new file mode 100644 index 0000000..77c5135 --- /dev/null +++ b/src/net/n3/nanoxml/PIReader.java @@ -0,0 +1,65 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * net.n3.nanoxml.IXMLReader + * net.n3.nanoxml.PIReader + */ +package net.n3.nanoxml; + +import java.io.IOException; +import java.io.Reader; +import net.n3.nanoxml.IXMLReader; + +class PIReader +extends Reader { + private IXMLReader reader; + private boolean atEndOfData; + + PIReader(IXMLReader iXMLReader) { + this.reader = iXMLReader; + this.atEndOfData = false; + } + + protected void finalize() throws Throwable { + this.reader = null; + super.finalize(); + } + + public int read(char[] cArray, int n, int n2) throws IOException { + if (this.atEndOfData) { + return -1; + } + int n3 = 0; + if (n + n2 > cArray.length) { + n2 = cArray.length - n; + } + while (n3 < n2) { + char c = this.reader.read(); + if (c == '?') { + char c2 = this.reader.read(); + if (c2 == '>') { + this.atEndOfData = true; + break; + } + this.reader.unread(c2); + } + cArray[n3] = c; + ++n3; + } + if (n3 == 0) { + n3 = -1; + } + return n3; + } + + public void close() throws IOException { + while (!this.atEndOfData) { + char c; + char c2 = this.reader.read(); + if (c2 != '?' || (c = this.reader.read()) != '>') continue; + this.atEndOfData = true; + } + } +} + diff --git a/src/net/n3/nanoxml/StdXMLBuilder.java b/src/net/n3/nanoxml/StdXMLBuilder.java new file mode 100644 index 0000000..bc1efec --- /dev/null +++ b/src/net/n3/nanoxml/StdXMLBuilder.java @@ -0,0 +1,126 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * net.n3.nanoxml.IXMLBuilder + * net.n3.nanoxml.IXMLElement + * net.n3.nanoxml.StdXMLBuilder + * net.n3.nanoxml.XMLElement + * net.n3.nanoxml.XMLParseException + */ +package net.n3.nanoxml; + +import java.io.IOException; +import java.io.Reader; +import java.util.Stack; +import net.n3.nanoxml.IXMLBuilder; +import net.n3.nanoxml.IXMLElement; +import net.n3.nanoxml.XMLElement; +import net.n3.nanoxml.XMLParseException; + +public class StdXMLBuilder +implements IXMLBuilder { + private Stack stack = null; + private IXMLElement root = null; + private IXMLElement prototype; + + public StdXMLBuilder() { + this((IXMLElement)new XMLElement()); + } + + public StdXMLBuilder(IXMLElement iXMLElement) { + this.prototype = iXMLElement; + } + + protected void finalize() throws Throwable { + this.prototype = null; + this.root = null; + this.stack.clear(); + this.stack = null; + super.finalize(); + } + + public void startBuilding(String string, int n) { + this.stack = new Stack(); + this.root = null; + } + + public void newProcessingInstruction(String string, Reader reader) { + } + + public void startElement(String string, String string2, String string3, String string4, int n) { + String string5 = string; + if (string2 != null) { + string5 = string2 + ':' + string; + } + IXMLElement iXMLElement = this.prototype.createElement(string5, string3, string4, n); + if (this.stack.empty()) { + this.root = iXMLElement; + } else { + IXMLElement iXMLElement2 = (IXMLElement)this.stack.peek(); + iXMLElement2.addChild(iXMLElement); + } + this.stack.push(iXMLElement); + } + + public void elementAttributesProcessed(String string, String string2, String string3) { + } + + public void endElement(String string, String string2, String string3) { + IXMLElement iXMLElement; + IXMLElement iXMLElement2 = (IXMLElement)this.stack.pop(); + if (iXMLElement2.getChildrenCount() == 1 && (iXMLElement = iXMLElement2.getChildAtIndex(0)).getName() == null) { + iXMLElement2.setContent(iXMLElement.getContent()); + iXMLElement2.removeChildAtIndex(0); + } + } + + public void addAttribute(String string, String string2, String string3, String string4, String string5) throws Exception { + IXMLElement iXMLElement; + String string6 = string; + if (string2 != null) { + string6 = string2 + ':' + string; + } + if ((iXMLElement = (IXMLElement)this.stack.peek()).hasAttribute(string6)) { + throw new XMLParseException(iXMLElement.getSystemID(), iXMLElement.getLineNr(), "Duplicate attribute: " + string); + } + if (string2 != null) { + iXMLElement.setAttribute(string6, string3, string4); + } else { + iXMLElement.setAttribute(string6, string4); + } + } + + public void addPCData(Reader reader, String string, int n) { + int n2 = 2048; + int n3 = 0; + StringBuffer stringBuffer = new StringBuffer(n2); + char[] cArray = new char[n2]; + while (true) { + int n4; + if (n3 >= n2) { + stringBuffer.ensureCapacity(n2 *= 2); + } + try { + n4 = reader.read(cArray); + } + catch (IOException iOException) { + break; + } + if (n4 < 0) break; + stringBuffer.append(cArray, 0, n4); + n3 += n4; + } + IXMLElement iXMLElement = this.prototype.createElement(null, string, n); + iXMLElement.setContent(stringBuffer.toString()); + if (!this.stack.empty()) { + IXMLElement iXMLElement2 = (IXMLElement)this.stack.peek(); + iXMLElement2.addChild(iXMLElement); + } + } + + public Object getResult() { + return this.root; + } +} + diff --git a/src/net/n3/nanoxml/StdXMLParser.java b/src/net/n3/nanoxml/StdXMLParser.java new file mode 100644 index 0000000..856d558 --- /dev/null +++ b/src/net/n3/nanoxml/StdXMLParser.java @@ -0,0 +1,368 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * net.n3.nanoxml.CDATAReader + * net.n3.nanoxml.ContentReader + * net.n3.nanoxml.IXMLBuilder + * net.n3.nanoxml.IXMLEntityResolver + * net.n3.nanoxml.IXMLParser + * net.n3.nanoxml.IXMLReader + * net.n3.nanoxml.IXMLValidator + * net.n3.nanoxml.PIReader + * net.n3.nanoxml.StdXMLParser + * net.n3.nanoxml.XMLEntityResolver + * net.n3.nanoxml.XMLException + * net.n3.nanoxml.XMLUtil + */ +package net.n3.nanoxml; + +import java.io.Reader; +import java.util.Enumeration; +import java.util.Properties; +import java.util.Vector; +import net.n3.nanoxml.CDATAReader; +import net.n3.nanoxml.ContentReader; +import net.n3.nanoxml.IXMLBuilder; +import net.n3.nanoxml.IXMLEntityResolver; +import net.n3.nanoxml.IXMLParser; +import net.n3.nanoxml.IXMLReader; +import net.n3.nanoxml.IXMLValidator; +import net.n3.nanoxml.PIReader; +import net.n3.nanoxml.XMLEntityResolver; +import net.n3.nanoxml.XMLException; +import net.n3.nanoxml.XMLUtil; + +public class StdXMLParser +implements IXMLParser { + private IXMLBuilder builder = null; + private IXMLReader reader = null; + private IXMLEntityResolver entityResolver = new XMLEntityResolver(); + private IXMLValidator validator = null; + + protected void finalize() throws Throwable { + this.builder = null; + this.reader = null; + this.entityResolver = null; + this.validator = null; + super.finalize(); + } + + public void setBuilder(IXMLBuilder iXMLBuilder) { + this.builder = iXMLBuilder; + } + + public IXMLBuilder getBuilder() { + return this.builder; + } + + public void setValidator(IXMLValidator iXMLValidator) { + this.validator = iXMLValidator; + } + + public IXMLValidator getValidator() { + return this.validator; + } + + public void setResolver(IXMLEntityResolver iXMLEntityResolver) { + this.entityResolver = iXMLEntityResolver; + } + + public IXMLEntityResolver getResolver() { + return this.entityResolver; + } + + public void setReader(IXMLReader iXMLReader) { + this.reader = iXMLReader; + } + + public IXMLReader getReader() { + return this.reader; + } + + public Object parse() throws XMLException { + try { + this.builder.startBuilding(this.reader.getSystemID(), this.reader.getLineNr()); + this.scanData(); + return this.builder.getResult(); + } + catch (XMLException xMLException) { + throw xMLException; + } + catch (Exception exception) { + throw new XMLException(exception); + } + } + + protected void scanData() throws Exception { + block4: while (!this.reader.atEOF() && this.builder.getResult() == null) { + String string = XMLUtil.read((IXMLReader)this.reader, (char)'&'); + char c = string.charAt(0); + if (c == '&') { + XMLUtil.processEntity((String)string, (IXMLReader)this.reader, (IXMLEntityResolver)this.entityResolver); + continue; + } + switch (c) { + case '<': { + this.scanSomeTag(false, null, new Properties()); + continue block4; + } + case '\t': + case '\n': + case '\r': + case ' ': { + continue block4; + } + } + XMLUtil.errorInvalidInput((String)this.reader.getSystemID(), (int)this.reader.getLineNr(), (String)("`" + c + "' (0x" + Integer.toHexString(c) + ')')); + } + } + + protected void scanSomeTag(boolean bl, String string, Properties properties) throws Exception { + String string2 = XMLUtil.read((IXMLReader)this.reader, (char)'&'); + char c = string2.charAt(0); + if (c == '&') { + XMLUtil.errorUnexpectedEntity((String)this.reader.getSystemID(), (int)this.reader.getLineNr(), (String)string2); + } + switch (c) { + case '?': { + this.processPI(); + break; + } + case '!': { + this.processSpecialTag(bl); + break; + } + default: { + this.reader.unread(c); + this.processElement(string, properties); + } + } + } + + protected void processPI() throws Exception { + XMLUtil.skipWhitespace((IXMLReader)this.reader, null); + String string = XMLUtil.scanIdentifier((IXMLReader)this.reader); + XMLUtil.skipWhitespace((IXMLReader)this.reader, null); + PIReader pIReader = new PIReader(this.reader); + if (!string.equalsIgnoreCase("xml")) { + this.builder.newProcessingInstruction(string, (Reader)pIReader); + } + pIReader.close(); + } + + protected void processSpecialTag(boolean bl) throws Exception { + String string = XMLUtil.read((IXMLReader)this.reader, (char)'&'); + char c = string.charAt(0); + if (c == '&') { + XMLUtil.errorUnexpectedEntity((String)this.reader.getSystemID(), (int)this.reader.getLineNr(), (String)string); + } + switch (c) { + case '[': { + if (bl) { + this.processCDATA(); + } else { + XMLUtil.errorUnexpectedCDATA((String)this.reader.getSystemID(), (int)this.reader.getLineNr()); + } + return; + } + case 'D': { + this.processDocType(); + return; + } + case '-': { + XMLUtil.skipComment((IXMLReader)this.reader); + return; + } + } + } + + protected void processCDATA() throws Exception { + if (!XMLUtil.checkLiteral((IXMLReader)this.reader, (String)"CDATA[")) { + XMLUtil.errorExpectedInput((String)this.reader.getSystemID(), (int)this.reader.getLineNr(), (String)"') { + XMLUtil.errorExpectedInput((String)this.reader.getSystemID(), (int)this.reader.getLineNr(), (String)"`>'"); + } + if (string != null) { + Reader reader = this.reader.openStream(stringBuffer.toString(), string); + this.reader.startNewStream(reader); + this.reader.setSystemID(string); + this.reader.setPublicID(stringBuffer.toString()); + this.validator.parseDTD(stringBuffer.toString(), this.reader, this.entityResolver, true); + } + } + + protected void processElement(String string, Properties properties) throws Exception { + String string2; + String string3; + int n; + String string4; + char c; + String string5; + String string6 = string5 = XMLUtil.scanIdentifier((IXMLReader)this.reader); + XMLUtil.skipWhitespace((IXMLReader)this.reader, null); + String string7 = null; + int n2 = string6.indexOf(58); + if (n2 > 0) { + string7 = string6.substring(0, n2); + string6 = string6.substring(n2 + 1); + } + Vector vector = new Vector(); + Vector vector2 = new Vector(); + Vector vector3 = new Vector(); + this.validator.elementStarted(string5, this.reader.getSystemID(), this.reader.getLineNr()); + while ((c = this.reader.read()) != '/' && c != '>') { + this.reader.unread(c); + this.processAttribute(vector, vector2, vector3); + XMLUtil.skipWhitespace((IXMLReader)this.reader, null); + } + Properties properties2 = new Properties(); + this.validator.elementAttributesProcessed(string5, properties2, this.reader.getSystemID(), this.reader.getLineNr()); + Enumeration enumeration = properties2.keys(); + while (enumeration.hasMoreElements()) { + String string8 = (String)enumeration.nextElement(); + string4 = properties2.getProperty(string8); + vector.addElement(string8); + vector2.addElement(string4); + vector3.addElement("CDATA"); + } + for (n = 0; n < vector.size(); ++n) { + string4 = (String)vector.elementAt(n); + string3 = (String)vector2.elementAt(n); + string2 = (String)vector3.elementAt(n); + if (string4.equals("xmlns")) { + string = string3; + continue; + } + if (!string4.startsWith("xmlns:")) continue; + properties.put(string4.substring(6), string3); + } + if (string7 == null) { + this.builder.startElement(string6, string7, string, this.reader.getSystemID(), this.reader.getLineNr()); + } else { + this.builder.startElement(string6, string7, properties.getProperty(string7), this.reader.getSystemID(), this.reader.getLineNr()); + } + for (n = 0; n < vector.size(); ++n) { + string4 = (String)vector.elementAt(n); + if (string4.startsWith("xmlns")) continue; + string3 = (String)vector2.elementAt(n); + string2 = (String)vector3.elementAt(n); + n2 = string4.indexOf(58); + if (n2 > 0) { + String string9 = string4.substring(0, n2); + string4 = string4.substring(n2 + 1); + this.builder.addAttribute(string4, string9, properties.getProperty(string9), string3, string2); + continue; + } + this.builder.addAttribute(string4, null, null, string3, string2); + } + if (string7 == null) { + this.builder.elementAttributesProcessed(string6, string7, string); + } else { + this.builder.elementAttributesProcessed(string6, string7, properties.getProperty(string7)); + } + if (c == '/') { + if (this.reader.read() != '>') { + XMLUtil.errorExpectedInput((String)this.reader.getSystemID(), (int)this.reader.getLineNr(), (String)"`>'"); + } + this.validator.elementEnded(string6, this.reader.getSystemID(), this.reader.getLineNr()); + if (string7 == null) { + this.builder.endElement(string6, string7, string); + } else { + this.builder.endElement(string6, string7, properties.getProperty(string7)); + } + return; + } + StringBuffer stringBuffer = new StringBuffer(16); + while (true) { + stringBuffer.setLength(0); + while (true) { + XMLUtil.skipWhitespace((IXMLReader)this.reader, (StringBuffer)stringBuffer); + string4 = XMLUtil.read((IXMLReader)this.reader, (char)'&'); + if (string4.charAt(0) != '&' || string4.charAt(1) == '#') break; + XMLUtil.processEntity((String)string4, (IXMLReader)this.reader, (IXMLEntityResolver)this.entityResolver); + } + if (string4.charAt(0) == '<') { + string4 = XMLUtil.read((IXMLReader)this.reader, (char)'\u0000'); + if (string4.charAt(0) == '/') { + XMLUtil.skipWhitespace((IXMLReader)this.reader, null); + string4 = XMLUtil.scanIdentifier((IXMLReader)this.reader); + if (!string4.equals(string5)) { + XMLUtil.errorWrongClosingTag((String)this.reader.getSystemID(), (int)this.reader.getLineNr(), (String)string6, (String)string4); + } + XMLUtil.skipWhitespace((IXMLReader)this.reader, null); + if (this.reader.read() != '>') { + XMLUtil.errorClosingTagNotEmpty((String)this.reader.getSystemID(), (int)this.reader.getLineNr()); + } + this.validator.elementEnded(string5, this.reader.getSystemID(), this.reader.getLineNr()); + if (string7 == null) { + this.builder.endElement(string6, string7, string); + break; + } + this.builder.endElement(string6, string7, properties.getProperty(string7)); + break; + } + this.reader.unread(string4.charAt(0)); + this.scanSomeTag(true, string, (Properties)properties.clone()); + continue; + } + if (string4.charAt(0) == '&') { + c = XMLUtil.processCharLiteral((String)string4); + stringBuffer.append(c); + } else { + this.reader.unread(string4.charAt(0)); + } + this.validator.PCDataAdded(this.reader.getSystemID(), this.reader.getLineNr()); + string3 = new ContentReader(this.reader, this.entityResolver, stringBuffer.toString()); + this.builder.addPCData((Reader)((Object)string3), this.reader.getSystemID(), this.reader.getLineNr()); + ((Reader)((Object)string3)).close(); + } + } + + protected void processAttribute(Vector vector, Vector vector2, Vector vector3) throws Exception { + String string = XMLUtil.scanIdentifier((IXMLReader)this.reader); + XMLUtil.skipWhitespace((IXMLReader)this.reader, null); + if (!XMLUtil.read((IXMLReader)this.reader, (char)'&').equals("=")) { + XMLUtil.errorExpectedInput((String)this.reader.getSystemID(), (int)this.reader.getLineNr(), (String)"`='"); + } + XMLUtil.skipWhitespace((IXMLReader)this.reader, null); + String string2 = XMLUtil.scanString((IXMLReader)this.reader, (char)'&', (IXMLEntityResolver)this.entityResolver); + vector.addElement(string); + vector2.addElement(string2); + vector3.addElement("CDATA"); + this.validator.attributeAdded(string, string2, this.reader.getSystemID(), this.reader.getLineNr()); + } +} + diff --git a/src/net/n3/nanoxml/StdXMLReader.java b/src/net/n3/nanoxml/StdXMLReader.java new file mode 100644 index 0000000..e9bde3a --- /dev/null +++ b/src/net/n3/nanoxml/StdXMLReader.java @@ -0,0 +1,315 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * net.n3.nanoxml.IXMLReader + * net.n3.nanoxml.StdXMLReader + * net.n3.nanoxml.StdXMLReader$StackedReader + */ +package net.n3.nanoxml; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.LineNumberReader; +import java.io.PushbackInputStream; +import java.io.PushbackReader; +import java.io.Reader; +import java.io.StringReader; +import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Stack; +import net.n3.nanoxml.IXMLReader; +import net.n3.nanoxml.StdXMLReader; + +/* + * Exception performing whole class analysis ignored. + */ +public class StdXMLReader +implements IXMLReader { + private Stack readers; + private StackedReader currentReader; + static /* synthetic */ Class class$net$n3$nanoxml$StdXMLReader; + + public static IXMLReader stringReader(String string) { + return new StdXMLReader((Reader)new StringReader(string)); + } + + public static IXMLReader fileReader(String string) throws FileNotFoundException, IOException { + StdXMLReader stdXMLReader = new StdXMLReader((InputStream)new FileInputStream(string)); + stdXMLReader.setSystemID(string); + for (int i = 0; i < stdXMLReader.readers.size(); ++i) { + StackedReader stackedReader = (StackedReader)stdXMLReader.readers.elementAt(i); + stackedReader.systemId = stdXMLReader.currentReader.systemId; + } + return stdXMLReader; + } + + public StdXMLReader(String string, String string2) throws MalformedURLException, FileNotFoundException, IOException { + URL uRL = null; + try { + uRL = new URL(string2); + } + catch (MalformedURLException malformedURLException) { + string2 = "file:" + string2; + try { + uRL = new URL(string2); + } + catch (MalformedURLException malformedURLException2) { + throw malformedURLException; + } + } + this.currentReader = new StackedReader(this, null); + this.readers = new Stack(); + Reader reader = this.openStream(string, uRL.toString()); + this.currentReader.lineReader = new LineNumberReader(reader); + this.currentReader.pbReader = new PushbackReader(this.currentReader.lineReader, 2); + } + + public StdXMLReader(Reader reader) { + this.currentReader = new StackedReader(this, null); + this.readers = new Stack(); + this.currentReader.lineReader = new LineNumberReader(reader); + this.currentReader.pbReader = new PushbackReader(this.currentReader.lineReader, 2); + this.currentReader.publicId = ""; + try { + this.currentReader.systemId = new URL("file:."); + } + catch (MalformedURLException malformedURLException) { + // empty catch block + } + } + + protected void finalize() throws Throwable { + this.currentReader.lineReader = null; + this.currentReader.pbReader = null; + this.currentReader.systemId = null; + this.currentReader.publicId = null; + this.currentReader = null; + this.readers.clear(); + super.finalize(); + } + + protected String getEncoding(String string) { + if (!string.startsWith("= 'a' && string.charAt(n) <= 'z') { + stringBuffer.append(string.charAt(n)); + ++n; + } + while (n < string.length() && string.charAt(n) <= ' ') { + ++n; + } + if (n >= string.length() || string.charAt(n) != '=') break; + while (n < string.length() && string.charAt(n) != '\'' && string.charAt(n) != '\"') { + ++n; + } + if (n >= string.length() || (n2 = string.indexOf(c = string.charAt(n), ++n)) < 0) break; + if (stringBuffer.toString().equals("encoding")) { + return string.substring(n, n2); + } + n = n2 + 1; + } + return null; + } + + protected Reader stream2reader(InputStream inputStream, StringBuffer stringBuffer) throws IOException { + PushbackInputStream pushbackInputStream = new PushbackInputStream(inputStream); + int n = pushbackInputStream.read(); + switch (n) { + case 0: + case 254: + case 255: { + pushbackInputStream.unread(n); + return new InputStreamReader((InputStream)pushbackInputStream, "UTF-16"); + } + case 239: { + for (int i = 0; i < 2; ++i) { + pushbackInputStream.read(); + } + return new InputStreamReader((InputStream)pushbackInputStream, "UTF-8"); + } + case 60: { + String string; + n = pushbackInputStream.read(); + stringBuffer.append('<'); + while (n > 0 && n != 62) { + stringBuffer.append((char)n); + n = pushbackInputStream.read(); + } + if (n > 0) { + stringBuffer.append((char)n); + } + if ((string = this.getEncoding(stringBuffer.toString())) == null) { + return new InputStreamReader((InputStream)pushbackInputStream, "UTF-8"); + } + stringBuffer.setLength(0); + try { + return new InputStreamReader((InputStream)pushbackInputStream, string); + } + catch (UnsupportedEncodingException unsupportedEncodingException) { + return new InputStreamReader((InputStream)pushbackInputStream, "UTF-8"); + } + } + } + stringBuffer.append((char)n); + return new InputStreamReader((InputStream)pushbackInputStream, "UTF-8"); + } + + public StdXMLReader(InputStream inputStream) throws IOException { + PushbackInputStream pushbackInputStream = new PushbackInputStream(inputStream); + StringBuffer stringBuffer = new StringBuffer(); + Reader reader = this.stream2reader(inputStream, stringBuffer); + this.currentReader = new StackedReader(this, null); + this.readers = new Stack(); + this.currentReader.lineReader = new LineNumberReader(reader); + this.currentReader.pbReader = new PushbackReader(this.currentReader.lineReader, 2); + this.currentReader.publicId = ""; + try { + this.currentReader.systemId = new URL("file:."); + } + catch (MalformedURLException malformedURLException) { + // empty catch block + } + this.startNewStream((Reader)new StringReader(stringBuffer.toString())); + } + + public char read() throws IOException { + int n = this.currentReader.pbReader.read(); + while (n < 0) { + if (this.readers.empty()) { + throw new IOException("Unexpected EOF"); + } + this.currentReader.pbReader.close(); + this.currentReader = (StackedReader)this.readers.pop(); + n = this.currentReader.pbReader.read(); + } + return (char)n; + } + + public boolean atEOFOfCurrentStream() throws IOException { + int n = this.currentReader.pbReader.read(); + if (n < 0) { + return true; + } + this.currentReader.pbReader.unread(n); + return false; + } + + public boolean atEOF() throws IOException { + int n = this.currentReader.pbReader.read(); + while (n < 0) { + if (this.readers.empty()) { + return true; + } + this.currentReader.pbReader.close(); + this.currentReader = (StackedReader)this.readers.pop(); + n = this.currentReader.pbReader.read(); + } + this.currentReader.pbReader.unread(n); + return false; + } + + public void unread(char c) throws IOException { + this.currentReader.pbReader.unread(c); + } + + public Reader openStream(String string, String string2) throws MalformedURLException, FileNotFoundException, IOException { + CharSequence charSequence; + URL uRL = new URL(this.currentReader.systemId, string2); + if (uRL.getRef() != null) { + charSequence = uRL.getRef(); + if (uRL.getFile().length() > 0) { + uRL = new URL(uRL.getProtocol(), uRL.getHost(), uRL.getPort(), uRL.getFile()); + uRL = new URL("jar:" + uRL + '!' + (String)charSequence); + } else { + uRL = (class$net$n3$nanoxml$StdXMLReader == null ? (class$net$n3$nanoxml$StdXMLReader = StdXMLReader.class$((String)"net.n3.nanoxml.StdXMLReader")) : class$net$n3$nanoxml$StdXMLReader).getResource((String)charSequence); + } + } + this.currentReader.publicId = string; + this.currentReader.systemId = uRL; + charSequence = new StringBuffer(); + Reader reader = this.stream2reader(uRL.openStream(), (StringBuffer)charSequence); + if (((StringBuffer)charSequence).length() == 0) { + return reader; + } + String string3 = ((StringBuffer)charSequence).toString(); + PushbackReader pushbackReader = new PushbackReader(reader, string3.length()); + for (int i = string3.length() - 1; i >= 0; --i) { + pushbackReader.unread(string3.charAt(i)); + } + return pushbackReader; + } + + public void startNewStream(Reader reader) { + this.startNewStream(reader, false); + } + + public void startNewStream(Reader reader, boolean bl) { + StackedReader stackedReader = this.currentReader; + this.readers.push(this.currentReader); + this.currentReader = new StackedReader(this, null); + if (bl) { + this.currentReader.lineReader = null; + this.currentReader.pbReader = new PushbackReader(reader, 2); + } else { + this.currentReader.lineReader = new LineNumberReader(reader); + this.currentReader.pbReader = new PushbackReader(this.currentReader.lineReader, 2); + } + this.currentReader.systemId = stackedReader.systemId; + this.currentReader.publicId = stackedReader.publicId; + } + + public int getStreamLevel() { + return this.readers.size(); + } + + public int getLineNr() { + if (this.currentReader.lineReader == null) { + StackedReader stackedReader = (StackedReader)this.readers.peek(); + if (stackedReader.lineReader == null) { + return 0; + } + return stackedReader.lineReader.getLineNumber() + 1; + } + return this.currentReader.lineReader.getLineNumber() + 1; + } + + public void setSystemID(String string) throws MalformedURLException { + this.currentReader.systemId = new URL(this.currentReader.systemId, string); + } + + public void setPublicID(String string) { + this.currentReader.publicId = string; + } + + public String getSystemID() { + return this.currentReader.systemId.toString(); + } + + public String getPublicID() { + return this.currentReader.publicId; + } + + static /* synthetic */ Class class$(String string) { + try { + return Class.forName(string); + } + catch (ClassNotFoundException classNotFoundException) { + throw new NoClassDefFoundError(classNotFoundException.getMessage()); + } + } +} + diff --git a/src/net/n3/nanoxml/ValidatorPlugin.java b/src/net/n3/nanoxml/ValidatorPlugin.java new file mode 100644 index 0000000..486a454 --- /dev/null +++ b/src/net/n3/nanoxml/ValidatorPlugin.java @@ -0,0 +1,102 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * net.n3.nanoxml.IXMLEntityResolver + * net.n3.nanoxml.IXMLReader + * net.n3.nanoxml.IXMLValidator + * net.n3.nanoxml.ValidatorPlugin + * net.n3.nanoxml.XMLUtil + * net.n3.nanoxml.XMLValidationException + */ +package net.n3.nanoxml; + +import java.util.Properties; +import net.n3.nanoxml.IXMLEntityResolver; +import net.n3.nanoxml.IXMLReader; +import net.n3.nanoxml.IXMLValidator; +import net.n3.nanoxml.XMLUtil; +import net.n3.nanoxml.XMLValidationException; + +public class ValidatorPlugin +implements IXMLValidator { + private IXMLValidator delegate = null; + + protected void finalize() throws Throwable { + this.delegate = null; + super.finalize(); + } + + public IXMLValidator getDelegate() { + return this.delegate; + } + + public void setDelegate(IXMLValidator iXMLValidator) { + this.delegate = iXMLValidator; + } + + public void setParameterEntityResolver(IXMLEntityResolver iXMLEntityResolver) { + this.delegate.setParameterEntityResolver(iXMLEntityResolver); + } + + public IXMLEntityResolver getParameterEntityResolver() { + return this.delegate.getParameterEntityResolver(); + } + + public void parseDTD(String string, IXMLReader iXMLReader, IXMLEntityResolver iXMLEntityResolver, boolean bl) throws Exception { + this.delegate.parseDTD(string, iXMLReader, iXMLEntityResolver, bl); + } + + public void elementStarted(String string, String string2, int n) throws Exception { + this.delegate.elementStarted(string, string2, n); + } + + public void elementEnded(String string, String string2, int n) throws Exception { + this.delegate.elementEnded(string, string2, n); + } + + public void elementAttributesProcessed(String string, Properties properties, String string2, int n) throws Exception { + this.delegate.elementAttributesProcessed(string, properties, string2, n); + } + + public void attributeAdded(String string, String string2, String string3, int n) throws Exception { + this.delegate.attributeAdded(string, string2, string3, n); + } + + public void PCDataAdded(String string, int n) throws Exception { + this.delegate.PCDataAdded(string, n); + } + + public void missingElement(String string, int n, String string2, String string3) throws XMLValidationException { + XMLUtil.errorMissingElement((String)string, (int)n, (String)string2, (String)string3); + } + + public void unexpectedElement(String string, int n, String string2, String string3) throws XMLValidationException { + XMLUtil.errorUnexpectedElement((String)string, (int)n, (String)string2, (String)string3); + } + + public void missingAttribute(String string, int n, String string2, String string3) throws XMLValidationException { + XMLUtil.errorMissingAttribute((String)string, (int)n, (String)string2, (String)string3); + } + + public void unexpectedAttribute(String string, int n, String string2, String string3) throws XMLValidationException { + XMLUtil.errorUnexpectedAttribute((String)string, (int)n, (String)string2, (String)string3); + } + + public void invalidAttributeValue(String string, int n, String string2, String string3, String string4) throws XMLValidationException { + XMLUtil.errorInvalidAttributeValue((String)string, (int)n, (String)string2, (String)string3, (String)string4); + } + + public void missingPCData(String string, int n, String string2) throws XMLValidationException { + XMLUtil.errorMissingPCData((String)string, (int)n, (String)string2); + } + + public void unexpectedPCData(String string, int n, String string2) throws XMLValidationException { + XMLUtil.errorUnexpectedPCData((String)string, (int)n, (String)string2); + } + + public void validationError(String string, int n, String string2, String string3, String string4, String string5) throws XMLValidationException { + XMLUtil.validationError((String)string, (int)n, (String)string2, (String)string3, (String)string4, (String)string5); + } +} + diff --git a/src/net/n3/nanoxml/XMLAttribute.java b/src/net/n3/nanoxml/XMLAttribute.java new file mode 100644 index 0000000..edf3bfa --- /dev/null +++ b/src/net/n3/nanoxml/XMLAttribute.java @@ -0,0 +1,48 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * net.n3.nanoxml.XMLAttribute + */ +package net.n3.nanoxml; + +class XMLAttribute { + private String fullName; + private String name; + private String namespace; + private String value; + private String type; + + XMLAttribute(String string, String string2, String string3, String string4, String string5) { + this.fullName = string; + this.name = string2; + this.namespace = string3; + this.value = string4; + this.type = string5; + } + + String getFullName() { + return this.fullName; + } + + String getName() { + return this.name; + } + + String getNamespace() { + return this.namespace; + } + + String getValue() { + return this.value; + } + + void setValue(String string) { + this.value = string; + } + + String getType() { + return this.type; + } +} + diff --git a/src/net/n3/nanoxml/XMLElement.java b/src/net/n3/nanoxml/XMLElement.java new file mode 100644 index 0000000..3be67f4 --- /dev/null +++ b/src/net/n3/nanoxml/XMLElement.java @@ -0,0 +1,460 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * net.n3.nanoxml.IXMLElement + * net.n3.nanoxml.XMLAttribute + * net.n3.nanoxml.XMLElement + */ +package net.n3.nanoxml; + +import java.io.Serializable; +import java.util.Enumeration; +import java.util.Properties; +import java.util.Vector; +import net.n3.nanoxml.IXMLElement; +import net.n3.nanoxml.XMLAttribute; + +public class XMLElement +implements IXMLElement, +Serializable { + static final long serialVersionUID = -2383376380548624920L; + public static final int NO_LINE = -1; + private IXMLElement parent; + private Vector attributes = new Vector(); + private Vector children = new Vector(8); + private String name; + private String fullName; + private String namespace; + private String content; + private String systemID; + private int lineNr; + + public XMLElement() { + this(null, null, null, -1); + } + + public XMLElement(String string) { + this(string, null, null, -1); + } + + public XMLElement(String string, String string2, int n) { + this(string, null, string2, n); + } + + public XMLElement(String string, String string2) { + this(string, string2, null, -1); + } + + public XMLElement(String string, String string2, String string3, int n) { + int n2; + this.fullName = string; + this.name = string2 == null ? string : ((n2 = string.indexOf(58)) >= 0 ? string.substring(n2 + 1) : string); + this.namespace = string2; + this.content = null; + this.lineNr = n; + this.systemID = string3; + this.parent = null; + } + + public IXMLElement createPCDataElement() { + return new XMLElement(); + } + + public IXMLElement createElement(String string) { + return new XMLElement(string); + } + + public IXMLElement createElement(String string, String string2, int n) { + return new XMLElement(string, string2, n); + } + + public IXMLElement createElement(String string, String string2) { + return new XMLElement(string, string2); + } + + public IXMLElement createElement(String string, String string2, String string3, int n) { + return new XMLElement(string, string2, string3, n); + } + + protected void finalize() throws Throwable { + this.attributes.clear(); + this.attributes = null; + this.children = null; + this.fullName = null; + this.name = null; + this.namespace = null; + this.content = null; + this.systemID = null; + this.parent = null; + super.finalize(); + } + + public IXMLElement getParent() { + return this.parent; + } + + public String getFullName() { + return this.fullName; + } + + public String getName() { + return this.name; + } + + public String getNamespace() { + return this.namespace; + } + + public void setName(String string) { + this.name = string; + this.fullName = string; + this.namespace = null; + } + + public void setName(String string, String string2) { + int n = string.indexOf(58); + this.name = string2 == null || n < 0 ? string : string.substring(n + 1); + this.fullName = string; + this.namespace = string2; + } + + public void addChild(IXMLElement iXMLElement) { + IXMLElement iXMLElement2; + if (iXMLElement == null) { + throw new IllegalArgumentException("child must not be null"); + } + if (iXMLElement.getName() == null && !this.children.isEmpty() && (iXMLElement2 = (IXMLElement)this.children.lastElement()).getName() == null) { + iXMLElement2.setContent(iXMLElement2.getContent() + iXMLElement.getContent()); + return; + } + ((XMLElement)iXMLElement).parent = this; + this.children.addElement(iXMLElement); + } + + public void insertChild(IXMLElement iXMLElement, int n) { + IXMLElement iXMLElement2; + if (iXMLElement == null) { + throw new IllegalArgumentException("child must not be null"); + } + if (iXMLElement.getName() == null && !this.children.isEmpty() && (iXMLElement2 = (IXMLElement)this.children.lastElement()).getName() == null) { + iXMLElement2.setContent(iXMLElement2.getContent() + iXMLElement.getContent()); + return; + } + ((XMLElement)iXMLElement).parent = this; + this.children.insertElementAt(iXMLElement, n); + } + + public void removeChild(IXMLElement iXMLElement) { + if (iXMLElement == null) { + throw new IllegalArgumentException("child must not be null"); + } + this.children.removeElement(iXMLElement); + } + + public void removeChildAtIndex(int n) { + this.children.removeElementAt(n); + } + + public Enumeration enumerateChildren() { + return this.children.elements(); + } + + public boolean isLeaf() { + return this.children.isEmpty(); + } + + public boolean hasChildren() { + return !this.children.isEmpty(); + } + + public int getChildrenCount() { + return this.children.size(); + } + + public Vector getChildren() { + return this.children; + } + + public IXMLElement getChildAtIndex(int n) throws ArrayIndexOutOfBoundsException { + return (IXMLElement)this.children.elementAt(n); + } + + public IXMLElement getFirstChildNamed(String string) { + Enumeration enumeration = this.children.elements(); + while (enumeration.hasMoreElements()) { + IXMLElement iXMLElement = (IXMLElement)enumeration.nextElement(); + String string2 = iXMLElement.getFullName(); + if (string2 == null || !string2.equals(string)) continue; + return iXMLElement; + } + return null; + } + + public IXMLElement getFirstChildNamed(String string, String string2) { + Enumeration enumeration = this.children.elements(); + while (enumeration.hasMoreElements()) { + IXMLElement iXMLElement = (IXMLElement)enumeration.nextElement(); + String string3 = iXMLElement.getName(); + boolean bl = string3 != null && string3.equals(string); + string3 = iXMLElement.getNamespace(); + bl = string3 == null ? (bl &= string == null) : (bl &= string3.equals(string2)); + if (!bl) continue; + return iXMLElement; + } + return null; + } + + public Vector getChildrenNamed(String string) { + Vector vector = new Vector(this.children.size()); + Enumeration enumeration = this.children.elements(); + while (enumeration.hasMoreElements()) { + IXMLElement iXMLElement = (IXMLElement)enumeration.nextElement(); + String string2 = iXMLElement.getFullName(); + if (string2 == null || !string2.equals(string)) continue; + vector.addElement(iXMLElement); + } + return vector; + } + + public Vector getChildrenNamed(String string, String string2) { + Vector vector = new Vector(this.children.size()); + Enumeration enumeration = this.children.elements(); + while (enumeration.hasMoreElements()) { + IXMLElement iXMLElement = (IXMLElement)enumeration.nextElement(); + String string3 = iXMLElement.getName(); + boolean bl = string3 != null && string3.equals(string); + string3 = iXMLElement.getNamespace(); + bl = string3 == null ? (bl &= string == null) : (bl &= string3.equals(string2)); + if (!bl) continue; + vector.addElement(iXMLElement); + } + return vector; + } + + private XMLAttribute findAttribute(String string) { + Enumeration enumeration = this.attributes.elements(); + while (enumeration.hasMoreElements()) { + XMLAttribute xMLAttribute = (XMLAttribute)enumeration.nextElement(); + if (!xMLAttribute.getFullName().equalsIgnoreCase(string)) continue; + return xMLAttribute; + } + return null; + } + + private XMLAttribute findAttribute(String string, String string2) { + Enumeration enumeration = this.attributes.elements(); + while (enumeration.hasMoreElements()) { + XMLAttribute xMLAttribute = (XMLAttribute)enumeration.nextElement(); + boolean bl = xMLAttribute.getName().equals(string); + bl = string2 == null ? (bl &= xMLAttribute.getNamespace() == null) : (bl &= string2.equalsIgnoreCase(xMLAttribute.getNamespace())); + if (!bl) continue; + return xMLAttribute; + } + return null; + } + + public int getAttributeCount() { + return this.attributes.size(); + } + + public String getAttribute(String string) { + return this.getAttribute(string, null); + } + + public String getAttribute(String string, String string2) { + XMLAttribute xMLAttribute = this.findAttribute(string); + if (xMLAttribute == null) { + return string2; + } + return xMLAttribute.getValue(); + } + + public String getAttribute(String string, String string2, String string3) { + XMLAttribute xMLAttribute = this.findAttribute(string, string2); + if (xMLAttribute == null) { + return string3; + } + return xMLAttribute.getValue(); + } + + public int getAttribute(String string, int n) { + String string2 = this.getAttribute(string, Integer.toString(n)); + return Integer.parseInt(string2); + } + + public int getAttribute(String string, String string2, int n) { + String string3 = this.getAttribute(string, string2, Integer.toString(n)); + return Integer.parseInt(string3); + } + + public String getAttributeType(String string) { + XMLAttribute xMLAttribute = this.findAttribute(string); + if (xMLAttribute == null) { + return null; + } + return xMLAttribute.getType(); + } + + public String getAttributeNamespace(String string) { + XMLAttribute xMLAttribute = this.findAttribute(string); + if (xMLAttribute == null) { + return null; + } + return xMLAttribute.getNamespace(); + } + + public String getAttributeType(String string, String string2) { + XMLAttribute xMLAttribute = this.findAttribute(string, string2); + if (xMLAttribute == null) { + return null; + } + return xMLAttribute.getType(); + } + + public void setAttribute(String string, String string2) { + XMLAttribute xMLAttribute = this.findAttribute(string); + if (xMLAttribute == null) { + xMLAttribute = new XMLAttribute(string, string, null, string2, "CDATA"); + this.attributes.addElement(xMLAttribute); + } else { + xMLAttribute.setValue(string2); + } + } + + public void setAttribute(String string, String string2, String string3) { + int n = string.indexOf(58); + String string4 = string.substring(n + 1); + XMLAttribute xMLAttribute = this.findAttribute(string4, string2); + if (xMLAttribute == null) { + xMLAttribute = new XMLAttribute(string, string4, string2, string3, "CDATA"); + this.attributes.addElement(xMLAttribute); + } else { + xMLAttribute.setValue(string3); + } + } + + public void removeAttribute(String string) { + for (int i = 0; i < this.attributes.size(); ++i) { + XMLAttribute xMLAttribute = (XMLAttribute)this.attributes.elementAt(i); + if (!xMLAttribute.getFullName().equals(string)) continue; + this.attributes.removeElementAt(i); + return; + } + } + + public void removeAttribute(String string, String string2) { + for (int i = 0; i < this.attributes.size(); ++i) { + XMLAttribute xMLAttribute = (XMLAttribute)this.attributes.elementAt(i); + boolean bl = xMLAttribute.getName().equals(string); + bl = string2 == null ? (bl &= xMLAttribute.getNamespace() == null) : (bl &= xMLAttribute.getNamespace().equals(string2)); + if (!bl) continue; + this.attributes.removeElementAt(i); + return; + } + } + + public Enumeration enumerateAttributeNames() { + Vector vector = new Vector(); + Enumeration enumeration = this.attributes.elements(); + while (enumeration.hasMoreElements()) { + XMLAttribute xMLAttribute = (XMLAttribute)enumeration.nextElement(); + vector.addElement(xMLAttribute.getFullName()); + } + return vector.elements(); + } + + public boolean hasAttribute(String string) { + return this.findAttribute(string) != null; + } + + public boolean hasAttribute(String string, String string2) { + return this.findAttribute(string, string2) != null; + } + + public Properties getAttributes() { + Properties properties = new Properties(); + Enumeration enumeration = this.attributes.elements(); + while (enumeration.hasMoreElements()) { + XMLAttribute xMLAttribute = (XMLAttribute)enumeration.nextElement(); + properties.put(xMLAttribute.getFullName(), xMLAttribute.getValue()); + } + return properties; + } + + public Properties getAttributesInNamespace(String string) { + Properties properties = new Properties(); + Enumeration enumeration = this.attributes.elements(); + while (enumeration.hasMoreElements()) { + XMLAttribute xMLAttribute = (XMLAttribute)enumeration.nextElement(); + if (string == null) { + if (xMLAttribute.getNamespace() != null) continue; + properties.put(xMLAttribute.getName(), xMLAttribute.getValue()); + continue; + } + if (!string.equals(xMLAttribute.getNamespace())) continue; + properties.put(xMLAttribute.getName(), xMLAttribute.getValue()); + } + return properties; + } + + public String getSystemID() { + return this.systemID; + } + + public int getLineNr() { + return this.lineNr; + } + + public String getContent() { + return this.content; + } + + public void setContent(String string) { + this.content = string; + } + + public boolean equals(Object object) { + try { + return this.equalsXMLElement((IXMLElement)object); + } + catch (ClassCastException classCastException) { + return false; + } + } + + public boolean equalsXMLElement(IXMLElement iXMLElement) { + Object object; + Object object2; + if (!this.name.equals(iXMLElement.getName())) { + return false; + } + if (this.attributes.size() != iXMLElement.getAttributeCount()) { + return false; + } + Enumeration enumeration = this.attributes.elements(); + while (enumeration.hasMoreElements()) { + XMLAttribute xMLAttribute = (XMLAttribute)enumeration.nextElement(); + if (!iXMLElement.hasAttribute(xMLAttribute.getName(), xMLAttribute.getNamespace())) { + return false; + } + object2 = iXMLElement.getAttribute(xMLAttribute.getName(), xMLAttribute.getNamespace(), null); + if (!xMLAttribute.getValue().equals(object2)) { + return false; + } + object = iXMLElement.getAttributeType(xMLAttribute.getName(), xMLAttribute.getNamespace()); + if (xMLAttribute.getType().equals(object)) continue; + return false; + } + if (this.children.size() != iXMLElement.getChildrenCount()) { + return false; + } + for (int i = 0; i < this.children.size(); ++i) { + object2 = this.getChildAtIndex(i); + if (object2.equalsXMLElement(object = iXMLElement.getChildAtIndex(i))) continue; + return false; + } + return true; + } +} + diff --git a/src/net/n3/nanoxml/XMLEntityResolver.java b/src/net/n3/nanoxml/XMLEntityResolver.java new file mode 100644 index 0000000..7f1f235 --- /dev/null +++ b/src/net/n3/nanoxml/XMLEntityResolver.java @@ -0,0 +1,76 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * net.n3.nanoxml.IXMLEntityResolver + * net.n3.nanoxml.IXMLReader + * net.n3.nanoxml.XMLEntityResolver + * net.n3.nanoxml.XMLParseException + */ +package net.n3.nanoxml; + +import java.io.Reader; +import java.io.StringReader; +import java.util.Hashtable; +import net.n3.nanoxml.IXMLEntityResolver; +import net.n3.nanoxml.IXMLReader; +import net.n3.nanoxml.XMLParseException; + +public class XMLEntityResolver +implements IXMLEntityResolver { + private Hashtable entities = new Hashtable(); + + public XMLEntityResolver() { + this.entities.put("amp", "&"); + this.entities.put("quot", """); + this.entities.put("apos", "'"); + this.entities.put("lt", "<"); + this.entities.put("gt", ">"); + } + + protected void finalize() throws Throwable { + this.entities.clear(); + this.entities = null; + super.finalize(); + } + + public void addInternalEntity(String string, String string2) { + if (!this.entities.containsKey(string)) { + this.entities.put(string, string2); + } + } + + public void addExternalEntity(String string, String string2, String string3) { + if (!this.entities.containsKey(string)) { + this.entities.put(string, new String[]{string2, string3}); + } + } + + public Reader getEntity(IXMLReader iXMLReader, String string) throws XMLParseException { + Object v = this.entities.get(string); + if (v == null) { + return null; + } + if (v instanceof String) { + return new StringReader((String)v); + } + String[] stringArray = (String[])v; + return this.openExternalEntity(iXMLReader, stringArray[0], stringArray[1]); + } + + public boolean isExternalEntity(String string) { + Object v = this.entities.get(string); + return !(v instanceof String); + } + + protected Reader openExternalEntity(IXMLReader iXMLReader, String string, String string2) throws XMLParseException { + String string3 = iXMLReader.getSystemID(); + try { + return iXMLReader.openStream(string, string2); + } + catch (Exception exception) { + throw new XMLParseException(string3, iXMLReader.getLineNr(), "Could not open external entity at system ID: " + string2); + } + } +} + diff --git a/src/net/n3/nanoxml/XMLException.java b/src/net/n3/nanoxml/XMLException.java new file mode 100644 index 0000000..d797d65 --- /dev/null +++ b/src/net/n3/nanoxml/XMLException.java @@ -0,0 +1,108 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * net.n3.nanoxml.XMLException + */ +package net.n3.nanoxml; + +import java.io.PrintStream; +import java.io.PrintWriter; + +/* + * Exception performing whole class analysis ignored. + */ +public class XMLException +extends Exception { + private String msg; + private String systemID; + private int lineNr; + private Exception encapsulatedException; + + public XMLException(String string) { + this(null, -1, null, string, false); + } + + public XMLException(Exception exception) { + this(null, -1, exception, "Nested Exception", false); + } + + public XMLException(String string, int n, Exception exception) { + this(string, n, exception, "Nested Exception", true); + } + + public XMLException(String string, int n, String string2) { + this(string, n, null, string2, true); + } + + public XMLException(String string, int n, Exception exception, String string2, boolean bl) { + super(XMLException.buildMessage((String)string, (int)n, (Exception)exception, (String)string2, (boolean)bl)); + this.systemID = string; + this.lineNr = n; + this.encapsulatedException = exception; + this.msg = XMLException.buildMessage((String)string, (int)n, (Exception)exception, (String)string2, (boolean)bl); + } + + private static String buildMessage(String string, int n, Exception exception, String string2, boolean bl) { + String string3 = string2; + if (bl) { + if (string != null) { + string3 = string3 + ", SystemID='" + string + "'"; + } + if (n >= 0) { + string3 = string3 + ", Line=" + n; + } + if (exception != null) { + string3 = string3 + ", Exception: " + exception; + } + } + return string3; + } + + protected void finalize() throws Throwable { + this.systemID = null; + this.encapsulatedException = null; + super.finalize(); + } + + public String getSystemID() { + return this.systemID; + } + + public int getLineNr() { + return this.lineNr; + } + + public Exception getException() { + return this.encapsulatedException; + } + + public void printStackTrace(PrintWriter printWriter) { + super.printStackTrace(printWriter); + if (this.encapsulatedException != null) { + printWriter.println("*** Nested Exception:"); + this.encapsulatedException.printStackTrace(printWriter); + } + } + + public void printStackTrace(PrintStream printStream) { + super.printStackTrace(printStream); + if (this.encapsulatedException != null) { + printStream.println("*** Nested Exception:"); + this.encapsulatedException.printStackTrace(printStream); + } + } + + public void printStackTrace() { + super.printStackTrace(); + if (this.encapsulatedException != null) { + System.err.println("*** Nested Exception:"); + this.encapsulatedException.printStackTrace(); + } + } + + public String toString() { + return this.msg; + } +} + diff --git a/src/net/n3/nanoxml/XMLParseException.java b/src/net/n3/nanoxml/XMLParseException.java new file mode 100644 index 0000000..ae91ed3 --- /dev/null +++ b/src/net/n3/nanoxml/XMLParseException.java @@ -0,0 +1,22 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * net.n3.nanoxml.XMLException + * net.n3.nanoxml.XMLParseException + */ +package net.n3.nanoxml; + +import net.n3.nanoxml.XMLException; + +public class XMLParseException +extends XMLException { + public XMLParseException(String string) { + super(string); + } + + public XMLParseException(String string, int n, String string2) { + super(string, n, null, string2, true); + } +} + diff --git a/src/net/n3/nanoxml/XMLParserFactory.java b/src/net/n3/nanoxml/XMLParserFactory.java new file mode 100644 index 0000000..3ccc797 --- /dev/null +++ b/src/net/n3/nanoxml/XMLParserFactory.java @@ -0,0 +1,45 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * net.n3.nanoxml.IXMLBuilder + * net.n3.nanoxml.IXMLParser + * net.n3.nanoxml.IXMLValidator + * net.n3.nanoxml.NonValidator + * net.n3.nanoxml.StdXMLBuilder + * net.n3.nanoxml.XMLParserFactory + */ +package net.n3.nanoxml; + +import net.n3.nanoxml.IXMLBuilder; +import net.n3.nanoxml.IXMLParser; +import net.n3.nanoxml.IXMLValidator; +import net.n3.nanoxml.NonValidator; +import net.n3.nanoxml.StdXMLBuilder; + +/* + * Exception performing whole class analysis ignored. + */ +public class XMLParserFactory { + public static final String DEFAULT_CLASS = "net.n3.nanoxml.StdXMLParser"; + public static final String CLASS_KEY = "net.n3.nanoxml.XMLParser"; + + public static IXMLParser createDefaultXMLParser() throws ClassNotFoundException, InstantiationException, IllegalAccessException { + String string = System.getProperty("net.n3.nanoxml.XMLParser", "net.n3.nanoxml.StdXMLParser"); + return XMLParserFactory.createXMLParser((String)string, (IXMLBuilder)new StdXMLBuilder()); + } + + public static IXMLParser createDefaultXMLParser(IXMLBuilder iXMLBuilder) throws ClassNotFoundException, InstantiationException, IllegalAccessException { + String string = System.getProperty("net.n3.nanoxml.XMLParser", "net.n3.nanoxml.StdXMLParser"); + return XMLParserFactory.createXMLParser((String)string, (IXMLBuilder)iXMLBuilder); + } + + public static IXMLParser createXMLParser(String string, IXMLBuilder iXMLBuilder) throws ClassNotFoundException, InstantiationException, IllegalAccessException { + Class clazz = Class.forName(string); + IXMLParser iXMLParser = (IXMLParser)clazz.newInstance(); + iXMLParser.setBuilder(iXMLBuilder); + iXMLParser.setValidator((IXMLValidator)new NonValidator()); + return iXMLParser; + } +} + diff --git a/src/net/n3/nanoxml/XMLUtil.java b/src/net/n3/nanoxml/XMLUtil.java new file mode 100644 index 0000000..8fd072a --- /dev/null +++ b/src/net/n3/nanoxml/XMLUtil.java @@ -0,0 +1,259 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * net.n3.nanoxml.IXMLEntityResolver + * net.n3.nanoxml.IXMLReader + * net.n3.nanoxml.XMLParseException + * net.n3.nanoxml.XMLUtil + * net.n3.nanoxml.XMLValidationException + */ +package net.n3.nanoxml; + +import java.io.IOException; +import java.io.Reader; +import net.n3.nanoxml.IXMLEntityResolver; +import net.n3.nanoxml.IXMLReader; +import net.n3.nanoxml.XMLParseException; +import net.n3.nanoxml.XMLValidationException; + +/* + * Exception performing whole class analysis ignored. + */ +class XMLUtil { + XMLUtil() { + } + + static void skipComment(IXMLReader iXMLReader) throws IOException, XMLParseException { + if (iXMLReader.read() != '-') { + XMLUtil.errorExpectedInput((String)iXMLReader.getSystemID(), (int)iXMLReader.getLineNr(), (String)"