Skip to content

Commit 6426d43

Browse files
authored
Merge pull request #21 from Bolado/http-proxy-args
Update Proxy Handling
2 parents 8b22106 + 5e47cdd commit 6426d43

3 files changed

Lines changed: 170 additions & 28 deletions

File tree

libs/jar-executor.js

Lines changed: 116 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ module.exports = async function (deps) {
66
const jarPath = path.join(microbotDir, `microbot-${version}.jar`);
77
const commandArgs = ['-jar', jarPath];
88

9-
if (proxy && proxy.proxyIp && proxy.proxyType) {
10-
commandArgs.push(`-proxy=${proxy.proxyIp}`);
11-
commandArgs.push(`-proxy-type=${proxy.proxyType}`);
9+
// apply proxy args (done differently depending on client version)
10+
const err = addProxyArgs(commandArgs, version, proxy);
11+
if (err) {
12+
log.error(err.message);
13+
return { error: err.message };
1214
}
1315

1416
if (account && account.profile) {
@@ -36,9 +38,11 @@ module.exports = async function (deps) {
3638
const jarPath = path.join(microbotDir, `microbot-${version}.jar`);
3739
const commandArgs = ['-jar', jarPath, '-clean-jagex-launcher'];
3840

39-
if (proxy && proxy.proxyIp && proxy.proxyType) {
40-
commandArgs.push(`-proxy=${proxy.proxyIp}`);
41-
commandArgs.push(`-proxy-type=${proxy.proxyType}`);
41+
// apply proxy args (done differently depending on client version)
42+
const err = addProxyArgs(commandArgs, version, proxy);
43+
if (err) {
44+
log.error(err.message);
45+
return { error: err.message };
4246
}
4347

4448
if (
@@ -119,8 +123,111 @@ module.exports = async function (deps) {
119123
}
120124
}
121125

126+
/**
127+
* Adds proxy arguments to the commandArgs array.
128+
* For versions < 1.9.9.2 we keep the old behavior: -proxy=<ip[:port]> -proxy-type=<type>
129+
* For versions >= 1.9.9.2 we only support SOCKS proxies in the following form: scheme://[user:pass@]host:port
130+
* Accepted legacy input formats (proxy.proxyIp):
131+
* ip:port
132+
* ip:port:user:pass (password may contain colons; extra segments are joined back for password)
133+
* scheme://user:pass@host:port (already formatted; passed through unchanged)
134+
* We do not push -proxy-type for new versions.
135+
*
136+
* @param {string[]} commandArgs - The command arguments array.
137+
* @param {string} version - The client version (e.g. "1.9.9.1").
138+
* @param {Object} proxy - The proxy configuration object.
139+
* @returns {Error|null} - Returns an error if the proxy configuration is invalid, otherwise null.
140+
*/
141+
function addProxyArgs(commandArgs, version, proxy) {
142+
if (!proxy || !proxy.proxyIp) return null;
143+
if (typeof proxy.proxyIp !== 'string') return null;
144+
if (proxy.proxyIp.trim() === '') return null;
145+
146+
const isNewFormat =
147+
version.localeCompare('1.9.9.2', undefined, { numeric: true }) >= 0;
148+
149+
if (!isNewFormat) {
150+
// Backwards compatibility: keep existing arguments exactly as before
151+
if (proxy.proxyType) {
152+
commandArgs.push(`-proxy=${proxy.proxyIp}`);
153+
commandArgs.push(`-proxy-type=${proxy.proxyType}`);
154+
}
155+
return null;
156+
}
157+
158+
// If <proxy.proxyType> is set and it's not socks we error and return
159+
if (proxy.proxyType && proxy.proxyType !== 'socks') {
160+
return new Error(
161+
'Only SOCKS proxies are supported since client version 1.9.9.2. Found: ' +
162+
proxy.proxyType
163+
);
164+
}
165+
166+
// New format (>= 1.9.9.2) – only SOCKS proxies supported.
167+
// As the UI may be changed in the future, we need to be flexible with input formats.
168+
try {
169+
let raw = proxy.proxyIp.trim();
170+
// if user already supplied in URI format, just use it.
171+
if (raw.includes('://')) {
172+
commandArgs.push(`-proxy=${raw}`);
173+
return null;
174+
}
175+
176+
const DEFAULT_SCHEME = 'socks5';
177+
const parts = raw.split(':');
178+
179+
if (parts.length === 2) {
180+
const [host, port] = parts;
181+
commandArgs.push(`-proxy=${DEFAULT_SCHEME}://${host}:${port}`);
182+
} else if (parts.length >= 4) {
183+
const host = parts[0];
184+
const port = parts[1];
185+
const user = parts[2];
186+
const pass = parts.slice(3).join(':'); // allow colons in password
187+
// encode user and pass (without encoding things may break)
188+
const encUser = encodeURIComponent(user);
189+
const encPass = encodeURIComponent(pass);
190+
commandArgs.push(
191+
`-proxy=${DEFAULT_SCHEME}://${encUser}:${encPass}@${host}:${port}`
192+
);
193+
} else {
194+
// fallback: just attach whatever (may be just host)
195+
commandArgs.push(`-proxy=${DEFAULT_SCHEME}://${raw}`);
196+
}
197+
} catch (err) {
198+
return new Error(
199+
'Failed to construct new proxy URI: ' + err.message
200+
);
201+
}
202+
return null;
203+
}
204+
205+
/**
206+
* Redacts sensitive information from command line arguments.
207+
* @param {string[]} args - The command line arguments.
208+
* @returns {string[]} - The redacted command line arguments.
209+
*/
210+
function redactCommandArgs(args) {
211+
return args.map((a) => {
212+
if (!a.startsWith('-proxy=')) return a;
213+
const value = a.slice('-proxy='.length);
214+
try {
215+
const u = new URL(value);
216+
if (u.username || u.password) {
217+
if (u.username) u.username = '***';
218+
if (u.password) u.password = '***';
219+
return `-proxy=${u.toString()}`;
220+
}
221+
} catch (_) {
222+
// fallback: strip credentials if present
223+
return `-proxy=${value.replace(/\/\/[^@]*@/, '//***@')}`;
224+
}
225+
return a;
226+
});
227+
}
228+
122229
function executeJar(commandArgs, dialog) {
123-
log.info(`java ${commandArgs.join(' ')}`);
230+
log.info(`java ${redactCommandArgs(commandArgs).join(' ')}`);
124231

125232
/**
126233
* Additional arguments for spawn library.
@@ -191,7 +298,8 @@ module.exports = async function (deps) {
191298
}
192299

193300
function checkJavaAndRunJar(commandArgs, dialog, shell) {
194-
log.info(`java ${commandArgs.join(' ')}`);
301+
log.info(`java ${redactCommandArgs(commandArgs).join(' ')}`);
302+
195303
isJavaInstalled((isInstalled, error) => {
196304
if (isInstalled) {
197305
log.info('Java is installed, running the JAR...');

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "microbot-launcher",
3-
"version": "3.2.1",
3+
"version": "3.2.2",
44
"description": "Launcher for the microbot client",
55
"main": "main.js",
66
"scripts": {

renderer.js

Lines changed: 53 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,21 @@ async function openClient() {
3838
(x) => x.accountId === selectedValue
3939
);
4040
if (selectedAccount) {
41-
await window.electron.overwriteCredentialProperties(selectedAccount);
42-
await window.electron.openClient(version, proxy, selectedAccount);
41+
try {
42+
await window.electron.overwriteCredentialProperties(
43+
selectedAccount
44+
);
45+
const launchResult = await window.electron.openClient(
46+
version,
47+
proxy,
48+
selectedAccount
49+
);
50+
if (launchResult?.error) {
51+
window.electron.errorAlert(launchResult.error);
52+
}
53+
} catch (err) {
54+
window.electron.errorAlert(err?.message || String(err));
55+
}
4356
} else {
4457
alert('Account not found. Please restart your client.');
4558
}
@@ -74,19 +87,25 @@ function getSelectedClientVersion() {
7487
*/
7588
async function playButtonClickHandler() {
7689
await checkForOutdatedLaunch();
90+
const playBtn = document.getElementById('play');
7791

7892
if (
79-
document.getElementById('play')?.innerText.toLowerCase() ===
93+
playBtn?.innerText.toLowerCase() ===
8094
'Play With Jagex Account'.toLowerCase()
8195
) {
8296
await openClient();
8397
} else {
84-
document.getElementById('play').classList.add('disabled');
85-
const result = await window.electron.startAuthFlow();
86-
if (result.error) {
87-
window.electron.errorAlert(result.error);
98+
playBtn?.classList.add('disabled');
99+
try {
100+
const authResult = await window.electron.startAuthFlow();
101+
if (authResult?.error) {
102+
window.electron.errorAlert(authResult.error);
103+
}
104+
} catch (err) {
105+
window.electron.errorAlert(err?.message || String(err));
106+
} finally {
107+
playBtn?.classList.remove('disabled');
88108
}
89-
document.getElementById('play').classList.remove('disabled');
90109
}
91110
}
92111

@@ -203,7 +222,7 @@ window.addEventListener('load', async () => {
203222
if (properties['client'] === '0.0.0') {
204223
document.getElementById('loader-container').style.display = 'block';
205224
const result = await window.electron.downloadClient(clientVersion);
206-
if (result.error) {
225+
if (result?.error) {
207226
window.electron.errorAlert(result.error);
208227
properties['client'] = '0.0.0';
209228
} else {
@@ -428,11 +447,16 @@ async function setupSidebarLayout(amountOfAccounts) {
428447
async function addAccountsHandler() {
429448
const addAccountsButton = document.getElementById('add-accounts');
430449
addAccountsButton.classList.add('disabled');
431-
const result = await window.electron.startAuthFlow();
432-
if (result.error) {
433-
window.electron.errorAlert(result.error);
450+
try {
451+
const authResult = await window.electron.startAuthFlow();
452+
if (authResult?.error) {
453+
window.electron.errorAlert(authResult.error);
454+
}
455+
} catch (err) {
456+
window.electron.errorAlert(err?.message || String(err));
457+
} finally {
458+
document.getElementById('add-accounts').classList.remove('disabled');
434459
}
435-
document.getElementById('add-accounts').classList.remove('disabled');
436460
}
437461

438462
function setupAddAccountsButton() {
@@ -448,7 +472,8 @@ function setupAddAccountsButton() {
448472
* @returns {string} The extracted version number.
449473
*/
450474
function extractVersion(versionString) {
451-
return versionString?.replace(/^microbot-/, '').replace(/\.jar$/, '');
475+
const s = String(versionString ?? '');
476+
return s.replace(/^microbot-/, '').replace(/\.jar$/, '');
452477
}
453478

454479
/**
@@ -481,10 +506,19 @@ function playNoJagexAccount() {
481506
const selectedProfile =
482507
document.getElementById('profile').value || 'default';
483508
await window.electron.setProfileNoJagexAccount(selectedProfile);
484-
485509
const result = await downloadClientIfNotExist(version);
486-
if (result.exists) {
487-
await window.electron.playNoJagexAccount(version, proxy);
510+
if (result?.exists) {
511+
try {
512+
const playResult = await window.electron.playNoJagexAccount(
513+
version,
514+
proxy
515+
);
516+
if (playResult?.error) {
517+
window.electron.errorAlert(playResult.error);
518+
}
519+
} catch (err) {
520+
window.electron.errorAlert(err?.message || String(err));
521+
}
488522
}
489523
});
490524
}
@@ -505,7 +539,7 @@ async function downloadClientIfNotExist(version) {
505539

506540
/** @type {{success: boolean, error?: string, path?: string}} */
507541
const result = await window.electron.downloadClient(version);
508-
if (result.error) {
542+
if (result?.error) {
509543
window.electron.errorAlert(result.error);
510544
return { exists: false };
511545
}
@@ -524,7 +558,7 @@ function updateNowBtn() {
524558
document.getElementById('loader-container').style.display = 'block';
525559
const clientVersion = await window.electron.fetchClientVersion();
526560
const result = await window.electron.downloadClient(clientVersion);
527-
if (result.error) {
561+
if (result?.error) {
528562
window.electron.errorAlert(result.error);
529563
document.getElementById('loader-container').style.display =
530564
'none';

0 commit comments

Comments
 (0)