Skip to content

Instantly share code, notes, and snippets.

@wwmoraes
Last active November 21, 2023 02:11
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wwmoraes/3288859d4b466f530706aa556347de9f to your computer and use it in GitHub Desktop.
Save wwmoraes/3288859d4b466f530706aa556347de9f to your computer and use it in GitHub Desktop.
StarUML patch

StarUML 5.0.x patch

About

Inspired on @msterhuj's [patch][original-patch]. This patch keeps the application mechanisms as vanilla as possible.

Here the validation logic has changes to avoid a round trip to the StarUML server, instead of patching the final check like the referred patch. The license file structure and data validation remains intact.

Users can create a license directly on the interface. The Enter License Key... dialog now accepts a name instead of a key to generate a personal license data and file.

The unlicensed dialog has a Enter License button that opens the enter license key dialog instead of the Buy Now one that opens the shop page.

Usage

You can fully patch StarUML on MacOS using:

make extract # extracts the asar
make app # applies the patch
make pack # packs it back into the asar

On Windows or Linux, you can export the full path to the asar file before the commands, for instance:

export ASAR_PATH=/c/Program Files/StarUML/resources/app.asar # cygwin

The patch sources aren't versioned to prevent issues with stale code patching. You can regenerate them using:

make extract
make patch-sources

If there's no conflicts, you should get a patch directory with a structure containing the files with changes.

Requirements

To patch your StarUML:

To change the patch diff you'll also need:

/app
/original
/patch
ASAR_PATH ?= $(shell mdfind kMDItemCFBundleIdentifier = "io.staruml.staruml")/Contents/Resources/app.asar
PATCH_FILES = $(shell find patch -type f 2> /dev/null)
SOURCE_DIR = original
PATCH_DIR = patch
TARGET_DIR = app
DIFF_FILE = patch.diff
extract:
@$(info extracting ${ASAR_PATH} into ${SOURCE_DIR})
@asar extract ${ASAR_PATH} ${SOURCE_DIR}
pack:
@$(info packing ${TARGET_DIR} into ${ASAR_PATH})
@asar pack ${TARGET_DIR} ${ASAR_PATH}
patch-sources:
@$(info recreating patch sources)
@cat ${DIFF_FILE} | grep -E "^diff" | awk '{print $$5}' | cut -d/ -f2- | while read -r file; do mkdir -p ${PATCH_DIR}/$$(dirname $$file); cp ${SOURCE_DIR}/$$file ${PATCH_DIR}/$$file; done
@patch -p1 --directory ${PATCH_DIR} < ${DIFF_FILE}
patch: ${DIFF_FILE}
show-diff: ${DIFF_FILE}
@grcat conf.diff < $<
${DIFF_FILE}: ${PATCH_FILES}
@$(info generating $@)
@diff --unified --recursive ${SOURCE_DIR} ${PATCH_DIR} | grep -vE "^Only in" > $@
${TARGET_DIR}: ${DIFF_FILE} ${SOURCE_DIR}
@$(info generating app files)
@${RM} -rf $@
@cp -r ${SOURCE_DIR} $@
@patch -p1 --directory $@ < $<
diff --unified --recursive original/src/dialogs/enter-license-dialog.js patch/src/dialogs/enter-license-dialog.js
--- original/src/dialogs/enter-license-dialog.js 2022-12-17 20:26:37
+++ patch/src/dialogs/enter-license-dialog.js 2022-12-17 22:05:51
@@ -33,12 +33,7 @@
var dialog = app.dialogs.showModalDialogUsingTemplate(Mustache.render(enterLicenseDialogTemplate, context))
var $dlg = dialog.getElement()
- var $buyNow = $dlg.find('.buy-now')
var $licenseKey = $dlg.find('.license-key')
-
- $buyNow.click(function () {
- shell.openExternal(app.config.purchase_url)
- })
dialog.then(function ({buttonId}) {
if (buttonId === 'ok') {
diff --unified --recursive original/src/dialogs/unregistered-dialog.js patch/src/dialogs/unregistered-dialog.js
--- original/src/dialogs/unregistered-dialog.js 2022-12-17 20:26:37
+++ patch/src/dialogs/unregistered-dialog.js 2022-12-17 21:35:41
@@ -18,6 +18,8 @@
const path = require('path')
const Strings = require('../strings')
+const EnterLicenseDialog = require('./enter-license-dialog')
+
const unregisteredDialogTemplate = fs.readFileSync(path.join(__dirname, '../static/html-contents/unregistered-dialog.html'), 'utf8')
/**
@@ -32,10 +34,10 @@
}
var dialog = app.dialogs.showModalDialogUsingTemplate(Mustache.render(unregisteredDialogTemplate, context))
var $dlg = dialog.getElement()
- var $buyNow = $dlg.find('.buy-now')
+ var $enterLicense = $dlg.find('.enter-license')
- $buyNow.click(function () {
- shell.openExternal(app.config.purchase_url)
+ $enterLicense.click(function () {
+ EnterLicenseDialog.showDialog()
})
dialog.then(({buttonId}) => {
diff --unified --recursive original/src/engine/license-manager.js patch/src/engine/license-manager.js
--- original/src/engine/license-manager.js 2022-12-17 20:26:37
+++ patch/src/engine/license-manager.js 2022-12-17 21:40:36
@@ -106,19 +106,7 @@
if (_key !== licenseInfo.licenseKey) {
reject('Invalid license key')
} else {
- // Server check
- $.post(app.config.validation_url, {licenseKey: licenseInfo.licenseKey})
- .done(data => {
- resolve(data)
- })
- .fail(err => {
- if (err && err.status === 499) { /* License key not exists */
- reject(err)
- } else {
- // If server is not available, assume that license key is valid
- resolve(licenseInfo)
- }
- })
+ resolve(licenseInfo)
}
}
}
@@ -148,27 +136,29 @@
*/
register (licenseKey) {
return new Promise((resolve, reject) => {
- $.post(app.config.validation_url, {licenseKey: licenseKey})
- .done(data => {
- if (data.product === packageJSON.config.product_id) {
- var file = path.join(app.getUserPath(), '/license.key')
- fs.writeFileSync(file, JSON.stringify(data, 2))
- licenseInfo = data
- setStatus(this, true)
- resolve(data)
- } else {
- setStatus(this, false)
- reject('unmatched') /* License is for old version */
- }
- })
- .fail(err => {
- setStatus(this, false)
- if (err.status === 499) { /* License key not exists */
- reject('invalid')
- } else {
- reject()
- }
- })
+ if (typeof licenseKey === 'undefined' || licenseKey.length == 0) {
+ reject('invalid')
+ }
+
+ let licenseInfo = {
+ name: licenseKey,
+ product: packageJSON.config.product_id,
+ licenseType: "PS",
+ quantity: 1,
+ timestamp: (new Date()).getTime(),
+ }
+
+ var base = SK + licenseInfo.name +
+ SK + licenseInfo.product + '-' + licenseInfo.licenseType +
+ SK + licenseInfo.quantity +
+ SK + licenseInfo.timestamp + SK
+
+ licenseInfo.licenseKey = crypto.createHash('sha1').update(base).digest('hex').toUpperCase()
+
+ var file = path.join(app.getUserPath(), '/license.key')
+ fs.writeFileSync(file, JSON.stringify(licenseInfo, 2))
+ setStatus(this, true)
+ resolve(licenseInfo)
})
}
diff --unified --recursive original/src/static/html-contents/enter-license-dialog.html patch/src/static/html-contents/enter-license-dialog.html
--- original/src/static/html-contents/enter-license-dialog.html 2022-12-17 20:26:37
+++ patch/src/static/html-contents/enter-license-dialog.html 2022-12-17 21:18:49
@@ -1,10 +1,9 @@
-<div class="enter-license-dialog dialog modal" data-title="Enter License Key">
+<div class="enter-license-dialog dialog modal" data-title="Create License Key">
<div class="dialog-body">
- <div>License Key</div>
+ <div>Name</div>
<div><input type='text' class='k-input k-textbox license-key'></div>
</div>
<div class="dialog-footer">
- <button class="k-button dialog-button outline left buy-now">Buy Now</button>
<button class="k-button dialog-button outline" data-button-id="cancel">{{Strings.CANCEL}}</button>
<button class="k-button dialog-button primary" data-button-id="ok">{{Strings.OK}}</button>
</div>
diff --unified --recursive original/src/static/html-contents/unregistered-dialog.html patch/src/static/html-contents/unregistered-dialog.html
--- original/src/static/html-contents/unregistered-dialog.html 2022-12-17 20:26:37
+++ patch/src/static/html-contents/unregistered-dialog.html 2022-12-17 21:31:54
@@ -1,10 +1,10 @@
<div class="unregistered-dialog dialog modal" data-title="Unregistered Version">
<div class="dialog-body">
<div class="ad"></div>
- <div>Thank you for evaluating StarUML. This is unregistered evaluation version. Although no time limit for evaluation, a license should be purchased for continued use. If you want to purchase a license, please click "Buy Now" button.</div>
+ <div>Thank you for evaluating StarUML. This is unregistered evaluation version. Although no time limit for evaluation, a license should be entered for continued use. If you want to enter a license, please click "Enter License" button.</div>
</div>
<div class="dialog-footer">
- <button class="k-button dialog-button left buy-now primary">Buy Now</button>
+ <button class="k-button dialog-button left enter-license primary">Enter License</button>
<button class="k-button dialog-button outline" data-button-id="cancel">Evaluate</button>
</div>
</div>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment