Initial commit

This commit is contained in:
Miguel Villegas
2025-11-19 13:00:37 -05:00
commit 5d3a2fc0a6
13 changed files with 2218 additions and 0 deletions
+4
View File
@@ -0,0 +1,4 @@
# These are supported funding model platforms
github: [zarfadev]
patreon: zarfala
+143
View File
@@ -0,0 +1,143 @@
# MobaXterm Keygen & Customizer 🚀
![License](https://img.shields.io/badge/License-MIT-blue.svg)
![Version](https://img.shields.io/badge/Version-v2.4-green.svg)
![Compatibility](https://img.shields.io/badge/Compatible-v20.X%20|%20v25.1%20|%20v25.2-orange)
> ⭐ **Please star this repository if you find it helpful!** ⭐
## Overview
A modern, user-friendly web tool for generating activation keys and customizing MobaXterm, the ultimate terminal emulator for Windows with an integrated X11 server, SSH client, and comprehensive network toolset.
## Features
- **✨ Clean, Modern UI** - Responsive design optimized for both desktop and mobile
- **🌓 Dark/Light Mode** - Toggle between themes with a click
- **🔒 Multiple Editions Support** - Generate keys for different MobaXterm editions
- **👥 Custom User Count** - Specify the number of concurrent users
- **💾 One-Click Downloads** - Instantly download your custom activation file
- **🔧 Complete Customizer** - Create personalized settings without the official customizer tool
- **🖼️ Custom Logo Support** - Replace the default MobaXterm logo with your own
- **📑 Bookmarks Manager** - Pre-define SSH, FTP, and other connection profiles
- **🔌 Plugin Integration** - Include plugins in your custom configuration
## Compatibility
- Works with **MobaXterm versions 20.X, 25.2, and 25.3**
- Supports both portable and installer editions
## How to Use
### Key Generator
1. **Visit the Generator**: Go to the [MobaXterm Key Generator](https://moba-xterm-keygen.vercel.app/)
2. **Fill in the Form**:
- Select the desired MobaXterm edition
- Enter a username (letters only)
- Choose your MobaXterm version (20.X or 25.X)
- Specify the number of users
3. **Download**: Click "Download Key" to get your Custom.mxtpro file
4. **Activate**: Place the generated file in your MobaXterm installation directory:
```
C:\Program Files (x86)\Mobatek\MobaXterm
```
### Customizer
1. **Go to the Customizer Tab** in the web interface
2. **Configure Your Settings**:
- **Banner**: Create a personalized welcome message
- **Profile**: Configure a custom bash profile (similar to /etc/profile in Linux)
- **Settings**: Toggle MobaXterm features on/off
- **Logo**: Upload your custom logo image
- **Bookmarks**: Create predefined connections for your users
- **Plugins**: Add optional MobaXterm plugins (.mxt3 files)
3. **Generate Settings**: Click "Generate Configuration File" to download your settings
4. **Apply Settings**: Use either method below to apply your customization
### Settings Merger
1. **Go to the Custom Settings Merger Tab**
2. **Upload Files**:
- Your license key file (Custom.mxtpro)
- Your customization file (MobaXterm customization.custom)
3. **Merge**: Create a single file with both your license and custom settings
4. **Deploy**: Copy the merged Custom.mxtpro to your MobaXterm installation directory
## Installation Guide
### Step 1: Download MobaXterm
Download the latest version from the [official MobaXterm website](https://mobaxterm.mobatek.net/download-home-edition.html).
### Step 2: Generate Your Key & Customization
Use our tool to:
1. Create your activation key
2. Customize your settings (optional)
3. Merge your license with your customization (optional)
### Step 3: Activate MobaXterm
1. Locate your MobaXterm installation folder:
- **Default Path**: `C:\Program Files (x86)\Mobatek\MobaXterm`
- Or right-click the MobaXterm shortcut and select "Open file location"
2. Copy the `Custom.mxtpro` file to this directory
3. Start MobaXterm - your license and customizations should now be applied!
### Alternative Customization Method
MobaXterm.exe has a customizer mode that you can access by adding the parameter "-customizer":
```
.\MobaXterm.exe -customizer
```
This official tool also allows you to export your settings to a file named "MobaXterm customization.custom". Our web-based customizer provides similar functionality but is accessible directly in your browser.
## Troubleshooting
1. **Activation Not Working?**
- Verify you're using a compatible version (20.X, 25.2, or 25.3)
- Make sure the file is in the correct location
- Check that the file is named exactly `Custom.mxtpro`
2. **Error During Key Generation?**
- Ensure all form fields are filled correctly
- Username must contain only letters (no spaces, numbers, or special characters)
- Try refreshing the page and generating again
3. **Customization Not Applying?**
- Verify that you've merged the customization with your license file
- Make sure the merged file is named exactly `Custom.mxtpro`
- Try restarting MobaXterm after placing the file in the installation directory
## Technical Details
This tool uses client-side processing to generate valid license keys and customization files based on your inputs. Everything is generated directly in your browser - no data is sent to any server, ensuring your privacy and security.
## Disclaimer
This tool is provided for educational purposes only. Please support the developers by purchasing a license for commercial use.
## Development
### Built With
- Vue.js
- Tailwind CSS
- Modern JavaScript
### Version History
- **v2.5** - Added support for version 25.3
- **v2.4** - Added full MobaXterm customizer functionality
- **v2.3** - Added custom settings merger
- **v2.2** - Dark mode and mobile optimization
- **v2.1** - Added support for version 25.2
- **v2.0** - Major UI redesign
- **v1.0** - Initial release
---
Created with ❤️ by Zarfala
---
⭐ [Star this repository on GitHub!](https://github.com/zarfadev/MobaXterm-Keygen) ⭐
+98
View File
@@ -0,0 +1,98 @@
/*
NOTE: This is just an experiment and
could use some changes to improve performance:
https://x.com/actualTimWilson/status/1730753841795158313
See a production optimized version here: https://charmui.com
*/
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap');
body {
font-family: Inter, sans-serif;
}
@keyframes jumbo {
from {
background-position: 50% 50%, 50% 50%;
}
to {
background-position: 350% 50%, 350% 50%;
}
}
/* New Styles for Form Isolation */
.box-main {
position: relative;
z-index: 1;
}
.jumbo {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 0;
}
.jumbo {
--stripes: repeating-linear-gradient(
100deg,
#fff 0%,
#fff 7%,
transparent 10%,
transparent 12%,
#fff 16%
);
--stripesDark: repeating-linear-gradient(
100deg,
#000 0%,
#000 7%,
transparent 10%,
transparent 12%,
#000 16%
);
--rainbow: repeating-linear-gradient(
100deg,
#60a5fa 10%,
#e879f9 15%,
#60a5fa 20%,
#5eead4 25%,
#60a5fa 30%
);
background-image: var(--stripes), var(--rainbow);
background-size: 300%, 200%;
background-position: 50% 50%, 50% 50%;
filter: blur(10px) invert(100%);
mask-image: radial-gradient(ellipse at 100% 0%, black 40%, transparent 70%);
pointer-events: none;
}
.jumbo::after {
content: "";
position: absolute;
inset: 0;
background-image: var(--stripes), var(--rainbow);
background-size: 200%, 100%;
animation: jumbo 60s linear infinite;
background-attachment: fixed;
mix-blend-mode: difference;
}
.dark .jumbo {
background-image: var(--stripesDark), var(--rainbow);
filter: blur(10px) opacity(50%) saturate(200%);
}
.dark .jumbo::after {
background-image: var(--stripesDark), var(--rainbow);
}
.bg-white {
--tw-bg-opacity: 1;
background-color: rgb(255 255 255);
}
+905
View File
@@ -0,0 +1,905 @@
/*!
Pure v2.0.3
Copyright 2013 Yahoo!
Licensed under the BSD License.
https://github.com/pure-css/pure/blob/master/LICENSE.md
*/
/*!
normalize.css v | MIT License | git.io/normalize
Copyright (c) Nicolas Gallagher and Jonathan Neal
*/
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
html {
line-height: 1.15;
-webkit-text-size-adjust: 100%
}
body {
margin: 0
}
main {
display: block
}
h1 {
font-size: 2em;
margin: .67em 0
}
hr {
-webkit-box-sizing: content-box;
box-sizing: content-box;
height: 0;
overflow: visible
}
pre {
font-family: monospace, monospace;
font-size: 1em
}
a {
background-color: transparent
}
abbr[title] {
border-bottom: none;
text-decoration: underline;
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted
}
b, strong {
font-weight: bolder
}
code, kbd, samp {
font-family: monospace, monospace;
font-size: 1em
}
small {
font-size: 80%
}
sub, sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline
}
sub {
bottom: -.25em
}
sup {
top: -.5em
}
img {
border-style: none
}
button, input, optgroup, select, textarea {
font-family: inherit;
font-size: 100%;
line-height: 1.15;
margin: 0
}
button, input {
overflow: visible
}
button, select {
text-transform: none
}
[type=button], [type=reset], [type=submit], button {
-webkit-appearance: button
}
[type=button]::-moz-focus-inner, [type=reset]::-moz-focus-inner, [type=submit]::-moz-focus-inner, button::-moz-focus-inner {
border-style: none;
padding: 0
}
[type=button]:-moz-focusring, [type=reset]:-moz-focusring, [type=submit]:-moz-focusring, button:-moz-focusring {
outline: 1px dotted ButtonText
}
fieldset {
padding: .35em .75em .625em
}
legend {
-webkit-box-sizing: border-box;
box-sizing: border-box;
color: inherit;
display: table;
max-width: 100%;
padding: 0;
white-space: normal
}
progress {
vertical-align: baseline
}
textarea {
overflow: auto
}
[type=checkbox], [type=radio] {
-webkit-box-sizing: border-box;
box-sizing: border-box;
padding: 0
}
[type=number]::-webkit-inner-spin-button, [type=number]::-webkit-outer-spin-button {
height: auto
}
[type=search] {
-webkit-appearance: textfield;
outline-offset: -2px
}
[type=search]::-webkit-search-decoration {
-webkit-appearance: none
}
::-webkit-file-upload-button {
-webkit-appearance: button;
font: inherit
}
details {
display: block
}
summary {
display: list-item
}
template {
display: none
}
[hidden] {
display: none
}
html {
font-family: sans-serif
}
.hidden, [hidden] {
display: none !important
}
.pure-img {
max-width: 100%;
height: auto;
display: block
}
.pure-g {
letter-spacing: -.31em;
text-rendering: optimizespeed;
font-family: FreeSans, Arimo, "Droid Sans", Helvetica, Arial, sans-serif;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: horizontal;
-webkit-box-direction: normal;
-ms-flex-flow: row wrap;
flex-flow: row wrap;
-ms-flex-line-pack: start;
align-content: flex-start
}
@media all and (-ms-high-contrast:none), (-ms-high-contrast:active) {
table .pure-g {
display: block
}
}
.opera-only :-o-prefocus, .pure-g {
word-spacing: -.43em
}
.pure-u {
display: inline-block;
letter-spacing: normal;
word-spacing: normal;
vertical-align: top;
text-rendering: auto
}
.pure-g [class*=pure-u] {
font-family: sans-serif
}
.pure-u-1, .pure-u-1-1, .pure-u-1-12, .pure-u-1-2, .pure-u-1-24, .pure-u-1-3, .pure-u-1-4, .pure-u-1-5, .pure-u-1-6, .pure-u-1-8, .pure-u-10-24, .pure-u-11-12, .pure-u-11-24, .pure-u-12-24, .pure-u-13-24, .pure-u-14-24, .pure-u-15-24, .pure-u-16-24, .pure-u-17-24, .pure-u-18-24, .pure-u-19-24, .pure-u-2-24, .pure-u-2-3, .pure-u-2-5, .pure-u-20-24, .pure-u-21-24, .pure-u-22-24, .pure-u-23-24, .pure-u-24-24, .pure-u-3-24, .pure-u-3-4, .pure-u-3-5, .pure-u-3-8, .pure-u-4-24, .pure-u-4-5, .pure-u-5-12, .pure-u-5-24, .pure-u-5-5, .pure-u-5-6, .pure-u-5-8, .pure-u-6-24, .pure-u-7-12, .pure-u-7-24, .pure-u-7-8, .pure-u-8-24, .pure-u-9-24 {
display: flex;
letter-spacing: normal;
word-spacing: normal;
vertical-align: top;
text-rendering: auto
}
.pure-u-1-24 {
width: 4.1667%
}
.pure-u-1-12, .pure-u-2-24 {
width: 8.3333%
}
.pure-u-1-8, .pure-u-3-24 {
width: 12.5%
}
.pure-u-1-6, .pure-u-4-24 {
width: 16.6667%
}
.pure-u-1-5 {
width: 20%
}
.pure-u-5-24 {
width: 20.8333%
}
.pure-u-1-4, .pure-u-6-24 {
width: 25%
}
.pure-u-7-24 {
width: 29.1667%
}
.pure-u-1-3, .pure-u-8-24 {
width: 33.3333%
}
.pure-u-3-8, .pure-u-9-24 {
width: 37.5%
}
.pure-u-2-5 {
width: 40%
}
.pure-u-10-24, .pure-u-5-12 {
width: 41.6667%
}
.pure-u-11-24 {
width: 45.8333%
}
.pure-u-1-2, .pure-u-12-24 {
width: 50%
}
.pure-u-13-24 {
width: 54.1667%
}
.pure-u-14-24, .pure-u-7-12 {
width: 58.3333%
}
.pure-u-3-5 {
width: 60%
}
.pure-u-15-24, .pure-u-5-8 {
width: 62.5%
}
.pure-u-16-24, .pure-u-2-3 {
width: 66.6667%
}
.pure-u-17-24 {
width: 70.8333%
}
.pure-u-18-24, .pure-u-3-4 {
width: 75%
}
.pure-u-19-24 {
width: 79.1667%
}
.pure-u-4-5 {
width: 80%
}
.pure-u-20-24, .pure-u-5-6 {
width: 83.3333%
}
.pure-u-21-24, .pure-u-7-8 {
width: 87.5%
}
.pure-u-11-12, .pure-u-22-24 {
width: 91.6667%
}
.pure-u-23-24 {
width: 95.8333%
}
.pure-u-1, .pure-u-1-1, .pure-u-24-24, .pure-u-5-5 {
width: 100%
}
.pure-button {
display: inline-block;
line-height: normal;
white-space: nowrap;
vertical-align: middle;
text-align: center;
cursor: pointer;
-webkit-user-drag: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-box-sizing: border-box;
box-sizing: border-box
}
.pure-button::-moz-focus-inner {
padding: 0;
border: 0
}
.pure-button-group {
letter-spacing: -.31em;
text-rendering: optimizespeed
}
.opera-only :-o-prefocus, .pure-button-group {
word-spacing: -.43em
}
.pure-button-group .pure-button {
letter-spacing: normal;
word-spacing: normal;
vertical-align: top;
text-rendering: auto
}
.pure-button {
font-family: inherit;
font-size: 100%;
padding: .5em 1em;
color: rgba(0, 0, 0, .8);
border: none transparent;
background-color: #e6e6e6;
text-decoration: none;
border-radius: 2px
}
.pure-button-hover, .pure-button:focus, .pure-button:hover {
background-image: -webkit-gradient(linear, left top, left bottom, from(transparent), color-stop(40%, rgba(0, 0, 0, .05)), to(rgba(0, 0, 0, .1)));
background-image: linear-gradient(transparent, rgba(0, 0, 0, .05) 40%, rgba(0, 0, 0, .1))
}
.pure-button:focus {
outline: 0
}
.pure-button-active, .pure-button:active {
-webkit-box-shadow: 0 0 0 1px rgba(0, 0, 0, .15) inset, 0 0 6px rgba(0, 0, 0, .2) inset;
box-shadow: 0 0 0 1px rgba(0, 0, 0, .15) inset, 0 0 6px rgba(0, 0, 0, .2) inset;
border-color: #000
}
.pure-button-disabled, .pure-button-disabled:active, .pure-button-disabled:focus, .pure-button-disabled:hover, .pure-button[disabled] {
border: none;
background-image: none;
opacity: .4;
cursor: not-allowed;
-webkit-box-shadow: none;
box-shadow: none;
pointer-events: none
}
.pure-button-hidden {
display: none
}
.pure-button-primary, .pure-button-selected, a.pure-button-primary, a.pure-button-selected {
background-color: #0078e7;
color: #fff
}
.pure-button-group .pure-button {
margin: 0;
border-radius: 0;
border-right: 1px solid rgba(0, 0, 0, .2)
}
.pure-button-group .pure-button:first-child {
border-top-left-radius: 2px;
border-bottom-left-radius: 2px
}
.pure-button-group .pure-button:last-child {
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
border-right: none
}
.pure-form input[type=color], .pure-form input[type=date], .pure-form input[type=datetime-local], .pure-form input[type=datetime], .pure-form input[type=email], .pure-form input[type=month], .pure-form input[type=number], .pure-form input[type=password], .pure-form input[type=search], .pure-form input[type=tel], .pure-form input[type=text], .pure-form input[type=time], .pure-form input[type=url], .pure-form input[type=week], .pure-form select, .pure-form textarea {
padding: .5em .6em;
display: inline-block;
border-width: 1px;
border-radius: .5rem;
box-sizing: border-box;
border-color: rgb(209 213 219 / 1);
background-color: #f9fafb;
}
.pure-form input:not([type]) {
padding: .5em .6em;
display: inline-block;
line-height: normal;
border-width: 1px;
border-radius: .5rem;
box-sizing: border-box;
border-color: #ccc;
background-color: #fff;
}
.pure-form input[type=color] {
padding: .2em .5em
}
.pure-form input[type=color]:focus, .pure-form input[type=date]:focus, .pure-form input[type=datetime-local]:focus, .pure-form input[type=datetime]:focus, .pure-form input[type=email]:focus, .pure-form input[type=month]:focus, .pure-form input[type=number]:focus, .pure-form input[type=password]:focus, .pure-form input[type=search]:focus, .pure-form input[type=tel]:focus, .pure-form input[type=text]:focus, .pure-form input[type=time]:focus, .pure-form input[type=url]:focus, .pure-form input[type=week]:focus, .pure-form select:focus, .pure-form textarea:focus {
outline: 0;
border-color: #129fea
}
.pure-form input:not([type]):focus {
outline: 0;
border-color: #129fea
}
.pure-form input[type=checkbox]:focus, .pure-form input[type=file]:focus, .pure-form input[type=radio]:focus {
outline: thin solid #129fea;
outline: 1px auto #129fea
}
.pure-form .pure-checkbox, .pure-form .pure-radio {
margin: .5em 0;
display: block
}
.pure-form input[type=color][disabled], .pure-form input[type=date][disabled], .pure-form input[type=datetime-local][disabled], .pure-form input[type=datetime][disabled], .pure-form input[type=email][disabled], .pure-form input[type=month][disabled], .pure-form input[type=number][disabled], .pure-form input[type=password][disabled], .pure-form input[type=search][disabled], .pure-form input[type=tel][disabled], .pure-form input[type=text][disabled], .pure-form input[type=time][disabled], .pure-form input[type=url][disabled], .pure-form input[type=week][disabled], .pure-form select[disabled], .pure-form textarea[disabled] {
cursor: not-allowed;
background-color: #eaeded;
color: #cad2d3
}
.pure-form input:not([type])[disabled] {
cursor: not-allowed;
background-color: #eaeded;
color: #cad2d3
}
.pure-form input[readonly], .pure-form select[readonly], .pure-form textarea[readonly] {
background-color: #eee;
color: #777;
border-color: #ccc
}
.pure-form input:focus:invalid, .pure-form select:focus:invalid, .pure-form textarea:focus:invalid {
color: #b94a48;
border-color: #e9322d
}
.pure-form input[type=checkbox]:focus:invalid:focus, .pure-form input[type=file]:focus:invalid:focus, .pure-form input[type=radio]:focus:invalid:focus {
outline-color: #e9322d
}
.pure-form select {
height: 2.3em !important;
border: 1px solid #ccc;
background-color: #fff
}
.pure-form select[multiple] {
height: auto
}
.pure-form label {
margin: .5em 0 .2em
}
.pure-form fieldset {
margin: 0;
padding: .35em 0 .75em;
border: 0
}
.pure-form legend {
display: block;
padding: .3em 0;
margin-bottom: .3em;
color: #333;
/* border-bottom: 1px solid #e5e5e5 */
}
.pure-form-stacked input[type=color], .pure-form-stacked input[type=date], .pure-form-stacked input[type=datetime-local], .pure-form-stacked input[type=datetime], .pure-form-stacked input[type=email], .pure-form-stacked input[type=file], .pure-form-stacked input[type=month], .pure-form-stacked input[type=number], .pure-form-stacked input[type=password], .pure-form-stacked input[type=search], .pure-form-stacked input[type=tel], .pure-form-stacked input[type=text], .pure-form-stacked input[type=time], .pure-form-stacked input[type=url], .pure-form-stacked input[type=week], .pure-form-stacked label, .pure-form-stacked select, .pure-form-stacked textarea {
display: block;
margin: .25em 0
}
.pure-form-stacked input:not([type]) {
display: block;
margin: .25em 0
}
.pure-form-aligned input, .pure-form-aligned select, .pure-form-aligned textarea, .pure-form-message-inline {
display: inline-block;
vertical-align: middle
}
.pure-form-aligned textarea {
vertical-align: top
}
.pure-form-aligned .pure-control-group {
margin-bottom: .5em
}
.pure-form-aligned .pure-control-group label {
text-align: right;
display: inline;
padding: 0.3em;
vertical-align: middle;
width: 10em;
margin: 0 1em 0 0;
}
.pure-form-aligned .pure-controls {
margin: 1.5em 0 0 11em
}
.pure-form .pure-input-rounded, .pure-form input.pure-input-rounded {
border-radius: 2em;
padding: .5em 1em
}
.pure-form .pure-group fieldset {
margin-bottom: 10px
}
.pure-form .pure-group input, .pure-form .pure-group textarea {
display: block;
padding: 10px;
margin: 0 0 -1px;
border-radius: 0;
position: relative;
top: -1px
}
.pure-form .pure-group input:focus, .pure-form .pure-group textarea:focus {
z-index: 3
}
.pure-form .pure-group input:first-child, .pure-form .pure-group textarea:first-child {
top: 1px;
border-radius: 4px 4px 0 0;
margin: 0
}
.pure-form .pure-group input:first-child:last-child, .pure-form .pure-group textarea:first-child:last-child {
top: 1px;
border-radius: 4px;
margin: 0
}
.pure-form .pure-group input:last-child, .pure-form .pure-group textarea:last-child {
top: -2px;
border-radius: 0 0 4px 4px;
margin: 0
}
.pure-form .pure-group button {
margin: .35em 0
}
.pure-form .pure-input-1 {
width: 100%
}
.pure-form .pure-input-3-4 {
width: 75%
}
.pure-form .pure-input-2-3 {
width: 66%
}
.pure-form .pure-input-1-2 {
width: 50%
}
.pure-form .pure-input-1-3 {
width: 33%
}
.pure-form .pure-input-1-4 {
width: 25%
}
.pure-form-message-inline {
display: inline-block;
padding-left: .3em;
color: #666;
vertical-align: middle;
font-size: .875em
}
.pure-form-message {
display: block;
color: #666;
font-size: .875em
}
@media only screen and (max-width :480px) {
.pure-form button[type=submit] {
margin: .7em 0 0
}
.pure-form input:not([type]), .pure-form input[type=color], .pure-form input[type=date], .pure-form input[type=datetime-local], .pure-form input[type=datetime], .pure-form input[type=email], .pure-form input[type=month], .pure-form input[type=number], .pure-form input[type=password], .pure-form input[type=search], .pure-form input[type=tel], .pure-form input[type=text], .pure-form input[type=time], .pure-form input[type=url], .pure-form input[type=week], .pure-form label {
margin-bottom: .3em;
display: block
}
.pure-group input:not([type]), .pure-group input[type=color], .pure-group input[type=date], .pure-group input[type=datetime-local], .pure-group input[type=datetime], .pure-group input[type=email], .pure-group input[type=month], .pure-group input[type=number], .pure-group input[type=password], .pure-group input[type=search], .pure-group input[type=tel], .pure-group input[type=text], .pure-group input[type=time], .pure-group input[type=url], .pure-group input[type=week] {
margin-bottom: 0
}
.pure-form-aligned .pure-control-group label {
margin-bottom: .3em;
text-align: left;
display: block;
width: 100%
}
.pure-form-aligned .pure-controls {
margin: 1.5em 0 0 0
}
.pure-form-message, .pure-form-message-inline {
display: block;
font-size: .75em;
padding: .2em 0 .8em
}
}
.pure-menu {
-webkit-box-sizing: border-box;
box-sizing: border-box
}
.pure-menu-fixed {
position: fixed;
left: 0;
top: 0;
z-index: 3
}
.pure-menu-item, .pure-menu-list {
position: relative
}
.pure-menu-list {
list-style: none;
margin: 0;
padding: 0
}
.pure-menu-item {
padding: 0;
margin: 0;
height: 100%
}
.pure-menu-heading, .pure-menu-link {
display: block;
text-decoration: none;
white-space: nowrap
}
.pure-menu-horizontal {
width: 100%;
white-space: nowrap
}
.pure-menu-horizontal .pure-menu-list {
display: inline-block
}
.pure-menu-horizontal .pure-menu-heading, .pure-menu-horizontal .pure-menu-item, .pure-menu-horizontal .pure-menu-separator {
display: inline-block;
vertical-align: middle
}
.pure-menu-item .pure-menu-item {
display: block
}
.pure-menu-children {
display: none;
position: absolute;
left: 100%;
top: 0;
margin: 0;
padding: 0;
z-index: 3
}
.pure-menu-horizontal .pure-menu-children {
left: 0;
top: auto;
width: inherit
}
.pure-menu-active>.pure-menu-children, .pure-menu-allow-hover:hover>.pure-menu-children {
display: block;
position: absolute
}
.pure-menu-has-children>.pure-menu-link:after {
padding-left: .5em;
content: "\25B8";
font-size: small
}
.pure-menu-horizontal .pure-menu-has-children>.pure-menu-link:after {
content: "\25BE"
}
.pure-menu-scrollable {
overflow-y: scroll;
overflow-x: hidden
}
.pure-menu-scrollable .pure-menu-list {
display: block
}
.pure-menu-horizontal.pure-menu-scrollable .pure-menu-list {
display: inline-block
}
.pure-menu-horizontal.pure-menu-scrollable {
white-space: nowrap;
overflow-y: hidden;
overflow-x: auto;
padding: .5em 0
}
.pure-menu-horizontal .pure-menu-children .pure-menu-separator, .pure-menu-separator {
background-color: #ccc;
height: 1px;
margin: .3em 0
}
.pure-menu-horizontal .pure-menu-separator {
width: 1px;
height: 1.3em;
margin: 0 .3em
}
.pure-menu-horizontal .pure-menu-children .pure-menu-separator {
display: block;
width: auto
}
.pure-menu-heading {
text-transform: uppercase;
color: #565d64
}
.pure-menu-link {
color: #777
}
.pure-menu-children {
background-color: #fff
}
.pure-menu-disabled, .pure-menu-heading, .pure-menu-link {
padding: .5em 1em
}
.pure-menu-disabled {
opacity: .5
}
.pure-menu-disabled .pure-menu-link:hover {
background-color: transparent
}
.pure-menu-active>.pure-menu-link, .pure-menu-link:focus, .pure-menu-link:hover {
background-color: #eee
}
.pure-menu-selected>.pure-menu-link, .pure-menu-selected>.pure-menu-link:visited {
color: #000
}
.pure-table {
border-collapse: collapse;
border-spacing: 0;
empty-cells: show;
border: 1px solid #cbcbcb
}
.pure-table caption {
color: #000;
font: italic 85%/1 arial, sans-serif;
padding: 1em 0;
text-align: center
}
.pure-table td, .pure-table th {
border-left: 1px solid #cbcbcb;
border-width: 0 0 0 1px;
font-size: inherit;
margin: 0;
overflow: visible;
padding: .5em 1em
}
.pure-table thead {
background-color: #e0e0e0;
color: #000;
text-align: left;
vertical-align: bottom
}
.pure-table td {
background-color: transparent
}
.pure-table-odd td {
background-color: #f2f2f2
}
.pure-table-striped tr:nth-child(2n-1) td {
background-color: #f2f2f2
}
.pure-table-bordered td {
border-bottom: 1px solid #cbcbcb
}
.pure-table-bordered tbody>tr:last-child>td {
border-bottom-width: 0
}
.pure-table-horizontal td, .pure-table-horizontal th {
border-width: 0 0 1px 0;
border-bottom: 1px solid #cbcbcb
}
.pure-table-horizontal tbody>tr:last-child>td {
border-bottom-width: 0
}
+71
View File
@@ -0,0 +1,71 @@
.box-main {
margin: 0 auto;
width: 500px;
}
.auto-center {
margin: 0 auto;
}
.text-center {
text-align: center;
}
.content-main {
padding-right: 30px;
}
.my-input {
position: relative;
display: inline-block;
}
.my-input-tips-wrap {
color: rgb(255, 52, 52);
position: absolute;
left: 10px;
bottom: 50px;
box-sizing: border-box;
border: 1px solid #ccc;
box-shadow: inset 0 1px 3px #ddd;
background-color: white;
width: auto;
height: 50px;
}
.my-input-tips {
position: relative;
top: 10px;
margin: 0 20px;
height: 30px;
line-height: 30px;
font-size: large;
white-space: nowrap;
}
.my-input-arrow {
position: absolute;
left: 5px;
top: 49px;
width: 0;
height: 0;
border: 10px solid;
border-color: gray transparent transparent transparent;
}
.hide {
display: none;
}
input {
line-height: normal;
}
select {
height: auto !important;
}
.quote {
margin-top: 30px;
text-align: center;
}
+506
View File
@@ -0,0 +1,506 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>MobaXterm Keygen</title>
<meta name="description" content="Website created with love by Zarfala" />
<link rel="icon" href="https://i.imgur.com/FtcF9ZP.png" />
<link href="./css/pure-min.css?=2" rel="stylesheet" />
<link href="./css/style.css" rel="stylesheet" />
<link href="./css/fire.css" rel="stylesheet" />
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
darkMode: "class",
theme: {
extend: {
screens: {
'xs': '475px',
},
colors: {
primary: {
50: '#eef2ff',
100: '#e0e7ff',
500: '#6366f1',
600: '#4f46e5',
700: '#4338ca',
}
},
animation: {
'pulse-slow': 'pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite',
}
}
}
}
</script>
<!-- Preloader style -->
<style>
#preloader {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #111;
z-index: 9999;
display: flex;
justify-content: center;
align-items: center;
transition: opacity 0.5s ease-in-out, visibility 0.5s ease-in-out;
}
#preloader.fade-out {
opacity: 0;
visibility: hidden;
}
.spinner {
width: 60px;
height: 60px;
border: 5px solid rgba(255, 255, 255, 0.3);
border-radius: 50%;
border-top-color: #6366f1;
animation: spin 1s ease-in-out infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.content-hidden {
visibility: hidden;
opacity: 0;
transition: opacity 0.5s ease-in-out, visibility 0.5s ease-in-out;
}
.content-visible {
visibility: visible;
opacity: 1;
}
</style>
<!-- Meta tags for better SEO and sharing -->
<meta property="og:title" content="MobaXterm Keygen" />
<meta property="og:type" content="website" />
<meta property="og:description" content="Website created with love by Zarfala" />
<meta property="og:image" content="https://i.imgur.com/FtcF9ZP.png" />
<meta property="og:site_name" content="MobaXterm Keygen" />
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="MobaXterm Keygen">
<meta name="twitter:description" content="Website created with love by Zarfala">
<meta name="twitter:image" content="https://i.imgur.com/FtcF9ZP.png">
</head>
<body class="transition-colors duration-300">
<!-- Preloader -->
<div id="preloader">
<div class="spinner"></div>
</div>
<main id="main-content" class="content-hidden">
<div class="relative flex min-h-screen flex-col items-center justify-center bg-white dark:bg-black transition-colors duration-300 overflow-hidden px-4">
<!-- Background effect -->
<div class="flex inset-0 overflow-hidden">
<div class="jumbo absolute opacity-60"></div>
</div>
<div class="box-main w-full max-w-xl mx-auto">
<div id="app" class="relative z-10 p-6 sm:p-10 w-full text-left shadow-lg rounded-xl bg-white dark:bg-slate-900/80 dark:backdrop-blur dark:ring-1 dark:ring-inset dark:ring-white/10 transition-all duration-300">
<!-- Header Section -->
<header class="mb-8 text-center">
<h1 class="relative flex flex-col xs:flex-row items-center justify-center text-4xl sm:text-5xl font-bold text-gray-800 dark:text-white dark:opacity-95 transition-colors">
<span>MobaXterm</span>
<span class="mt-2 xs:mt-0 xs:ml-2 rounded-xl bg-current p-2 text-[0.7em] leading-none">
<span class="text-white dark:text-black">Keygen</span>
</span>
</h1>
<!-- Version Badge -->
<div class="mt-4 inline-flex items-center px-3 py-1 rounded-full bg-gradient-to-r from-indigo-500 via-purple-500 to-pink-500 text-sm font-medium text-white"> Compatible with versions 20.X, 25.2 and 25.3 </div>
<!-- Theme Toggle -->
<div class="mt-4">
<button onclick="toggleTheme()" class="px-3 py-1 border border-stone-200 rounded-full drop-shadow-sm text-sm text-stone-800 dark:text-white bg-white/40 dark:bg-black/40 backdrop-blur-lg hover:border-stone-300 transition-colors dark:border-stone-500 dark:hover:border-stone-400"> Toggle Theme </button>
</div>
<!-- Tab Navigation -->
<div class="mt-6 border-b border-gray-200 dark:border-gray-700">
<nav class="flex space-x-4" aria-label="Tabs">
<button @click="activeTab = 'keyGenerator'"
:class="[activeTab === 'keyGenerator' ? 'border-primary-500 text-primary-600 dark:text-primary-400' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 dark:text-gray-400 dark:hover:text-gray-300', 'px-1 pb-4 text-sm font-medium border-b-2']">
Key Generator
</button>
<button @click="activeTab = 'customMerger'"
:class="[activeTab === 'customMerger' ? 'border-primary-500 text-primary-600 dark:text-primary-400' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 dark:text-gray-400 dark:hover:text-gray-300', 'px-1 pb-4 text-sm font-medium border-b-2']">
Custom Settings Merger
</button>
</nav>
</div>
</header>
<!-- Key Generator Tab -->
<div v-if="activeTab === 'keyGenerator'">
<form @submit.prevent="validateAndGenerate">
<div class="space-y-6">
<div>
<label for="licenseType" class="block text-lg text-gray-700 dark:text-gray-300">Edition:</label>
<div class="relative">
<select name="licenseType" id="licenseType" v-model="licenseType" class="w-full mt-1 p-2 pr-8 rounded-lg border border-gray-300 dark:border-gray-600 dark:bg-slate-800 dark:text-white focus:ring-2 focus:ring-primary-500 focus:border-transparent outline-none transition-colors appearance-none">
<option value="" disabled>Select an option</option>
<option v-for="(val, key) in LICENSE_TYPES" :value="val">{{key}}</option>
</select>
<div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 mt-1 text-gray-700 dark:text-gray-300">
<svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
<path d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"></path>
</svg>
</div>
</div>
<p v-if="errors.licenseType" class="mt-1 text-red-500 text-sm">{{ errors.licenseType }}</p>
</div>
<div>
<label for="userName" class="block text-lg text-gray-700 dark:text-gray-300">Username:</label>
<div class="relative">
<input type="text" id="userName" v-model="userName" @input="validateUserName" placeholder="Example defaultUser" class="w-full mt-1 p-2 rounded-lg border border-gray-300 dark:border-gray-600 dark:bg-slate-800 dark:text-white focus:ring-2 focus:ring-primary-500 focus:border-transparent outline-none transition-colors" />
<p v-if="errors.userName" class="mt-1 text-red-500 text-sm">{{ errors.userName }}</p>
</div>
</div>
<div>
<label for="versionName" class="block text-lg text-gray-700 dark:text-gray-300">Version:</label>
<div class="relative">
<input type="text" id="versionName" v-model="versionName" @input="validateVersion" placeholder="Example 25.3" class="w-full mt-1 p-2 rounded-lg border border-gray-300 dark:border-gray-600 dark:bg-slate-800 dark:text-white focus:ring-2 focus:ring-primary-500 focus:border-transparent outline-none transition-colors" />
<p v-if="errors.versionName" class="mt-1 text-red-500 text-sm">{{ errors.versionName }}</p>
</div>
</div>
<div>
<label for="userNum" class="block text-lg text-gray-700 dark:text-gray-300">Number of users:</label>
<div class="relative">
<input type="number" id="userNum" v-model="userNum" @input="validateUserNum" placeholder="Example 1" min="1" class="w-full mt-1 p-2 rounded-lg border border-gray-300 dark:border-gray-600 dark:bg-slate-800 dark:text-white focus:ring-2 focus:ring-primary-500 focus:border-transparent outline-none transition-colors" />
<p v-if="errors.userNum" class="mt-1 text-red-500 text-sm">{{ errors.userNum }}</p>
</div>
</div>
<!-- Download Button -->
<div class="text-center pt-4">
<button type="submit" class="w-full sm:w-auto px-6 py-3 bg-primary-600 text-white rounded-lg shadow-md hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-primary-500 transition-colors duration-200"> Download Key </button>
</div>
</div>
</form>
</div>
<!-- Custom Settings Merger Tab -->
<div v-if="activeTab === 'customMerger'" class="space-y-6">
<div class="bg-slate-100 dark:bg-slate-800 p-4 rounded-lg mb-4">
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-2">How to Use Custom Settings Merger</h3>
<ol class="list-decimal list-inside space-y-2 text-sm text-gray-700 dark:text-gray-300">
<li>Generate a license key file using the Key Generator tab</li>
<li>Run MobaXterm with the <code class="bg-slate-200 dark:bg-slate-700 px-1 py-0.5 rounded">-customizer</code> parameter: <br>
<code class="bg-slate-200 dark:bg-slate-700 px-2 py-1 rounded block mt-1">.\MobaXterm.exe -customizer</code>
</li>
<li>Export your custom settings to a file named <code class="bg-slate-200 dark:bg-slate-700 px-1 py-0.5 rounded">MobaXterm customization.custom</code></li>
<li>Upload both your license key file and custom settings file below</li>
<li>Click "Merge Files" to create a combined activation file</li>
</ol>
</div>
<div>
<label for="licenseFileInput" class="block text-lg font-medium text-gray-700 dark:text-gray-300 mb-2">
License Key File (Custom.mxtpro):
</label>
<div class="relative">
<input
type="file"
id="licenseFileInput"
@change="handleLicenseFileUpload"
accept=".mxtpro"
class="block w-full text-sm text-gray-700 dark:text-gray-300
file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0
file:text-sm file:font-semibold
file:bg-primary-50 file:text-primary-700
hover:file:bg-primary-100 hover:file:text-primary-800
focus:outline-none focus:border-primary-500 focus:ring-2 focus:ring-primary-200
transition-colors duration-200
dark:file:bg-slate-700 dark:file:text-slate-100
dark:hover:file:bg-slate-600 dark:hover:file:text-white"
/>
<p v-if="errors.licenseFile" class="mt-1 text-red-500 text-sm">{{ errors.licenseFile }}</p>
</div>
</div>
<div>
<label for="customSettingsFileInput" class="block text-lg text-gray-700 dark:text-gray-300">Custom Settings File (.custom):</label>
<input type="file" id="customSettingsFileInput" @change="handleCustomSettingsFileUpload" accept=".custom" class="block w-full text-sm text-gray-700 dark:text-gray-300
file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0
file:text-sm file:font-semibold
file:bg-primary-50 file:text-primary-700
hover:file:bg-primary-100 hover:file:text-primary-800
focus:outline-none focus:border-primary-500 focus:ring-2 focus:ring-primary-200
transition-colors duration-200
dark:file:bg-slate-700 dark:file:text-slate-100
dark:hover:file:bg-slate-600 dark:hover:file:text-white" />
<p v-if="errors.customSettingsFile" class="mt-1 text-red-500 text-sm">{{ errors.customSettingsFile }}</p>
</div>
<div class="text-center pt-4">
<button @click="mergeFiles" :disabled="!licenseFile || !customSettingsFile" class="w-full sm:w-auto px-6 py-3 bg-primary-600 text-white rounded-lg shadow-md hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-primary-500 transition-colors duration-200 disabled:bg-gray-400 disabled:cursor-not-allowed"> Merge Files </button>
</div>
</div>
<!-- Footer -->
<footer class="mt-8 pt-6 border-t border-slate-200 dark:border-slate-700">
<div class="flex flex-col items-center space-y-4">
<div class="flex items-center space-x-2">
<span class="text-sm text-slate-600 dark:text-slate-400">Created with</span>
<span class="animate-pulse text-red-500"></span>
<span class="text-sm text-slate-600 dark:text-slate-400">by</span>
<span class="text-sm font-semibold bg-clip-text text-transparent bg-gradient-to-r from-purple-500 to-pink-500"> Zarfala </span>
</div>
<p class="text-sm text-slate-600 dark:text-slate-400" style="margin-top: 0;"> UI/UX Design optimized for <span class="inline-block">
<span class="text-blue-500 font-semibold">Computer</span>
<span class="text-green-500 font-semibold">& Mobile</span>
</span>
</p>
<div class="text-xs text-slate-500 dark:text-slate-500" style="margin-top: 5px;"> v2.5 © 2025 All rights reserved </div>
</div>
</footer>
</div>
</div>
</div>
<!-- Toast notification -->
<div id="toast" class="fixed bottom-4 right-4 px-4 py-2 rounded-lg bg-green-500 text-white transform translate-y-16 opacity-0 transition-all duration-300 z-50 hidden">
License generated successfully!
</div>
<!-- Scripts -->
<script src="./js/vue.min-2.6.12.js"></script>
<script src="./js/FileSaver.js"></script>
<script src="./js/jszip.js"></script>
<script src="./js/fire.js"></script>
<script src="./js/alert.js"></script>
<script>
// Preloader functionality
window.addEventListener('load', function() {
// Hide preloader
setTimeout(function() {
const preloader = document.getElementById('preloader');
const mainContent = document.getElementById('main-content');
preloader.classList.add('fade-out');
mainContent.classList.remove('content-hidden');
mainContent.classList.add('content-visible');
// Remove preloader from DOM after transition completes
setTimeout(function() {
preloader.style.display = 'none';
}, 500);
}, 300);
});
// Theme toggle functionality
function toggleTheme() {
if (document.documentElement.classList.contains('dark')) {
document.documentElement.classList.remove('dark');
localStorage.theme = 'light';
} else {
document.documentElement.classList.add('dark');
localStorage.theme = 'dark';
}
}
// Check for saved theme preference or prefer-color-scheme
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
// Toast notification function
function showToast(message, type = 'success') {
const toast = document.getElementById('toast');
toast.textContent = message;
toast.classList.remove('hidden');
if (type === 'success') {
toast.classList.add('bg-green-500');
toast.classList.remove('bg-red-500');
} else {
toast.classList.add('bg-red-500');
toast.classList.remove('bg-green-500');
}
setTimeout(() => {
toast.classList.remove('translate-y-16', 'opacity-0');
}, 100);
setTimeout(() => {
toast.classList.add('translate-y-16', 'opacity-0');
setTimeout(() => {
toast.classList.add('hidden');
}, 300);
}, 3000);
}
</script>
<script type="module">
import {
LicenseType,
generateLicense
} from './js/mobaXtermGenerater.js'
import {
mergeZipFiles
} from './js/mergeZipFiles.js'
new Vue({
el: '#app',
data() {
return {
LICENSE_TYPES: LicenseType,
licenseType: '',
userName: '',
versionName: '',
userNum: '',
activeTab: 'keyGenerator',
licenseFile: null,
customSettingsFile: null,
errors: {
licenseType: '',
userName: '',
versionName: '',
userNum: '',
licenseFile: '',
customSettingsFile: ''
}
}
},
methods: {
validateLicenseType() {
if (!this.licenseType) {
this.errors.licenseType = 'Please select an edition';
return false;
}
this.errors.licenseType = '';
return true;
},
validateUserName() {
if (!this.userName) {
this.errors.userName = 'Username is required';
return false;
}
const regex = /^[a-zA-Z]+$/;
if (!regex.test(this.userName)) {
this.errors.userName = 'Only letters are allowed (no spaces, numbers or special characters)';
return false;
}
this.errors.userName = '';
return true;
},
validateVersion() {
if (!this.versionName) {
this.errors.versionName = 'Version is required';
return false;
}
const regex = /^[1-9][0-9]*\.?\d{0,1}$/;
if (!regex.test(this.versionName)) {
this.errors.versionName = 'Enter a valid version format (e.g., 25.3)';
return false;
}
// Check if version is supported
const majorVersion = parseInt(this.versionName.split('.')[0]);
if (majorVersion !== 20 && majorVersion !== 25) {
this.errors.versionName = 'Currently only versions 20.X and 25.X are supported';
return false;
}
this.errors.versionName = '';
return true;
},
validateUserNum() {
if (!this.userNum) {
this.errors.userNum = 'Number of users is required';
return false;
}
const regex = /^[1-9]+\d*$/;
if (!regex.test(this.userNum)) {
this.errors.userNum = 'Enter a valid number greater than 0';
return false;
}
this.errors.userNum = '';
return true;
},
validateAndGenerate() {
// Validate all fields
const isLicenseTypeValid = this.validateLicenseType();
const isUserNameValid = this.validateUserName();
const isVersionValid = this.validateVersion();
const isUserNumValid = this.validateUserNum();
// Only generate if all validations pass
if (isLicenseTypeValid && isUserNameValid && isVersionValid && isUserNumValid) {
try {
let versionNameArr = this.versionName.split('.');
const majorVersion = parseInt(versionNameArr[0]);
const minorVersion = versionNameArr.length === 2 ? (parseInt(versionNameArr[1]) || 0) : 0;
let licenseStr = generateLicense(this.licenseType, this.userName, this.userNum, majorVersion, minorVersion);
this.generateLicenseFile(licenseStr);
showToast('License generated successfully!', 'success');
} catch (error) {
console.error('Error generating license:', error);
showToast('Error generating license. Please try again.', 'error');
}
}
},
generateLicenseFile(licenseStr) {
let zip = new JSZip();
zip.file("Pro.key", licenseStr);
zip.generateAsync({
type: "blob"
}).then(function(content) {
saveAs(content, "Custom.mxtpro");
}).catch(function(error) {
console.error('Error creating zip file:', error);
showToast('Error creating license file. Please try again.', 'error');
});
},
handleLicenseFileUpload(event) {
const file = event.target.files[0];
if (file) {
if (file.name.toLowerCase().endsWith('.mxtpro')) {
this.licenseFile = file;
this.errors.licenseFile = '';
} else {
this.licenseFile = null;
this.errors.licenseFile = 'Please select a valid .mxtpro file';
}
}
},
handleCustomSettingsFileUpload(event) {
const file = event.target.files[0];
if (file) {
if (file.name.toLowerCase().endsWith('.custom')) {
this.customSettingsFile = file;
this.errors.customSettingsFile = '';
} else {
this.customSettingsFile = null;
this.errors.customSettingsFile = 'Please select a valid .custom file';
}
}
},
mergeFiles() {
if (!this.licenseFile || !this.customSettingsFile) {
showToast('Please upload both required files', 'error');
return;
}
mergeZipFiles(this.licenseFile, this.customSettingsFile)
.then((mergedContent) => {
saveAs(mergedContent, "Custom.mxtpro");
showToast('Files merged successfully!', 'success');
})
.catch((error) => {
console.error('Error merging files:', error);
showToast('Error merging files. Please try again.', 'error');
});
}
},
mounted() {
// Set default version to 25.3
this.versionName = '25.3';
}
});
</script>
</body>
</html>
+188
View File
@@ -0,0 +1,188 @@
(function (global, factory) {
if (typeof define === "function" && define.amd) {
define([], factory);
} else if (typeof exports !== "undefined") {
factory();
} else {
var mod = {
exports: {}
};
factory();
global.FileSaver = mod.exports;
}
})(this, function () {
"use strict";
/*
* FileSaver.js
* A saveAs() FileSaver implementation.
*
* By Eli Grey, http://eligrey.com
*
* License : https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md (MIT)
* source : http://purl.eligrey.com/github/FileSaver.js
*/
// The one and only way of getting global scope in all environments
// https://stackoverflow.com/q/3277182/1008999
var _global = typeof window === 'object' && window.window === window ? window : typeof self === 'object' && self.self === self ? self : typeof global === 'object' && global.global === global ? global : void 0;
function bom(blob, opts) {
if (typeof opts === 'undefined') opts = {
autoBom: false
};else if (typeof opts !== 'object') {
console.warn('Deprecated: Expected third argument to be a object');
opts = {
autoBom: !opts
};
} // prepend BOM for UTF-8 XML and text/* types (including HTML)
// note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
if (opts.autoBom && /^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
return new Blob([String.fromCharCode(0xFEFF), blob], {
type: blob.type
});
}
return blob;
}
function download(url, name, opts) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.responseType = 'blob';
xhr.onload = function () {
saveAs(xhr.response, name, opts);
};
xhr.onerror = function () {
console.error('could not download file');
};
xhr.send();
}
function corsEnabled(url) {
var xhr = new XMLHttpRequest(); // use sync to avoid popup blocker
xhr.open('HEAD', url, false);
try {
xhr.send();
} catch (e) {}
return xhr.status >= 200 && xhr.status <= 299;
} // `a.click()` doesn't work for all browsers (#465)
function click(node) {
try {
node.dispatchEvent(new MouseEvent('click'));
} catch (e) {
var evt = document.createEvent('MouseEvents');
evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80, 20, false, false, false, false, 0, null);
node.dispatchEvent(evt);
}
} // Detect WebView inside a native macOS app by ruling out all browsers
// We just need to check for 'Safari' because all other browsers (besides Firefox) include that too
// https://www.whatismybrowser.com/guides/the-latest-user-agent/macos
var isMacOSWebView = /Macintosh/.test(navigator.userAgent) && /AppleWebKit/.test(navigator.userAgent) && !/Safari/.test(navigator.userAgent);
var saveAs = _global.saveAs || ( // probably in some web worker
typeof window !== 'object' || window !== _global ? function saveAs() {}
/* noop */
// Use download attribute first if possible (#193 Lumia mobile) unless this is a macOS WebView
: 'download' in HTMLAnchorElement.prototype && !isMacOSWebView ? function saveAs(blob, name, opts) {
var URL = _global.URL || _global.webkitURL;
var a = document.createElement('a');
name = name || blob.name || 'download';
a.download = name;
a.rel = 'noopener'; // tabnabbing
// TODO: detect chrome extensions & packaged apps
// a.target = '_blank'
if (typeof blob === 'string') {
// Support regular links
a.href = blob;
if (a.origin !== location.origin) {
corsEnabled(a.href) ? download(blob, name, opts) : click(a, a.target = '_blank');
} else {
click(a);
}
} else {
// Support blobs
a.href = URL.createObjectURL(blob);
setTimeout(function () {
URL.revokeObjectURL(a.href);
}, 4E4); // 40s
setTimeout(function () {
click(a);
}, 0);
}
} // Use msSaveOrOpenBlob as a second approach
: 'msSaveOrOpenBlob' in navigator ? function saveAs(blob, name, opts) {
name = name || blob.name || 'download';
if (typeof blob === 'string') {
if (corsEnabled(blob)) {
download(blob, name, opts);
} else {
var a = document.createElement('a');
a.href = blob;
a.target = '_blank';
setTimeout(function () {
click(a);
});
}
} else {
navigator.msSaveOrOpenBlob(bom(blob, opts), name);
}
} // Fallback to using FileReader and a popup
: function saveAs(blob, name, opts, popup) {
// Open a popup immediately do go around popup blocker
// Mostly only available on user interaction and the fileReader is async so...
popup = popup || open('', '_blank');
if (popup) {
popup.document.title = popup.document.body.innerText = 'downloading...';
}
if (typeof blob === 'string') return download(blob, name, opts);
var force = blob.type === 'application/octet-stream';
var isSafari = /constructor/i.test(_global.HTMLElement) || _global.safari;
var isChromeIOS = /CriOS\/[\d]+/.test(navigator.userAgent);
if ((isChromeIOS || force && isSafari || isMacOSWebView) && typeof FileReader !== 'undefined') {
// Safari doesn't allow downloading of blob URLs
var reader = new FileReader();
reader.onloadend = function () {
var url = reader.result;
url = isChromeIOS ? url : url.replace(/^data:[^;]*;/, 'data:attachment/file;');
if (popup) popup.location.href = url;else location = url;
popup = null; // reverse-tabnabbing #460
};
reader.readAsDataURL(blob);
} else {
var URL = _global.URL || _global.webkitURL;
var url = URL.createObjectURL(blob);
if (popup) popup.location = url;else location.href = url;
popup = null; // reverse-tabnabbing #460
setTimeout(function () {
URL.revokeObjectURL(url);
}, 4E4); // 40s
}
});
_global.saveAs = saveAs.saveAs = saveAs;
if (typeof module !== 'undefined') {
module.exports = saveAs;
}
});
+40
View File
@@ -0,0 +1,40 @@
var estiloConsola = `
font-size: 16px;
font-family: "Courier New", monospace;
color: red;
white-space: pre;
`;
var estiloZarfala = `
color: red;
white-space: pre;
`;
var mensajeZarfala = `
@@@@@@@@ @@@@@@ @@@@@@@ @@@@@@@@ @@@@@@ @@@ @@@@@@
@@@@@@@@ @@@@@@@@ @@@@@@@@ @@@@@@@@ @@@@@@@@ @@@ @@@@@@@@
@@! @@! @@@ @@! @@@ @@! @@! @@@ @@! @@! @@@
!@! !@! @!@ !@! @!@ !@! !@! @!@ !@! !@! @!@
@!! @!@!@!@! @!@!!@! @!!!:! @!@!@!@! @!! @!@!@!@!
!!! !!!@!!!! !!@!@! !!!!!: !!!@!!!! !!! !!!@!!!!
!!: !!: !!! !!: :!! !!: !!: !!! !!: !!: !!!
:!: :!: !:! :!: !:! :!: :!: !:! :!: :!: !:!
:: :::: :: ::: :: ::: :: :: ::: :: :::: :: :::
: :: : : : : : : : : : : : : : :: : : : : :
`;
function mostrarMensaje() {
console.clear();
console.log('%c' + mensajeZarfala, estiloZarfala);
console.log("%cSTOP!", "color:red;font-size:xx-large;font-weight:bold;"),
console.log(`%cThis is a browser feature intended for developers. Do not enter or paste code which you don't understand. It may allow attackers to steal your information or impersonate you. See https://en.wikipedia.org/wiki/Self-XSS for more details`, "font-size:large;"),
localStorage.do_NOT_share_jwt || localStorage.setItem("do_NOT_share_jwt", "do NOT share the JWT value. someone could steal your account");
}
var intermitente = true;
setInterval(function () {
mostrarMensaje();
intermitente = !intermitente;
}, 1000);
mostrarMensaje();
+6
View File
@@ -0,0 +1,6 @@
function toggleTheme() {
if (document.body.classList.contains("dark"))
document.body.classList.remove("dark");
else
document.body.classList.add("dark");
}
+30
View File
File diff suppressed because one or more lines are too long
+58
View File
@@ -0,0 +1,58 @@
/**
* Utility to merge MobaXterm key file with custom settings file
*/
/**
* Merges a custom settings file with the license file
* @param {Blob} licenseFile - The Custom.mxtpro license file (which is a ZIP containing Pro.key)
* @param {Blob} customSettingsFile - The MobaXterm customization.custom file (which is a ZIP with settings)
* @returns {Promise<Blob>} - A promise that resolves to the merged ZIP file
*/
function mergeZipFiles(licenseFile, customSettingsFile) {
return new Promise((resolve, reject) => {
// Create a new JSZip instance for the merged result
const mergedZip = new JSZip();
const licenseZip = new JSZip();
const settingsZip = new JSZip();
// First load the license file
licenseZip.loadAsync(licenseFile)
.then((licenseContents) => {
// Then load the custom settings file
return settingsZip.loadAsync(customSettingsFile)
.then((settingsContents) => {
// Get the Pro.key file from license ZIP
return licenseContents.file("Pro.key").async("string")
.then((proKeyContent) => {
// Add the Pro.key to the merged zip
mergedZip.file("Pro.key", proKeyContent);
// Add all files from the custom settings ZIP
const promises = [];
settingsContents.forEach((relativePath, file) => {
if (!file.dir) {
const promise = file.async("blob").then((content) => {
mergedZip.file(relativePath, content);
});
promises.push(promise);
}
});
return Promise.all(promises).then(() => {
// Generate the final merged ZIP file
return mergedZip.generateAsync({type: "blob"});
});
});
});
})
.then((mergedContent) => {
resolve(mergedContent);
})
.catch((error) => {
console.error("Error merging ZIP files:", error);
reject(error);
});
});
}
export { mergeZipFiles };
+163
View File
@@ -0,0 +1,163 @@
const VariantBase64Table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split('');
const VariantBase64Dict = {};
VariantBase64Table.forEach((val, i) => VariantBase64Dict[i] = val);
const VariantBase64ReverseDict = {};
VariantBase64Table.forEach((val, i) => VariantBase64ReverseDict[val] = i);
/** license type */
const LicenseType = {
Professional: 1,
Educational: 3,
Personal: 4
}
/**
* byte[] is base64 encoded
* @param {array[Number]} bs
* @returns
*/
function VariantBase64Encode(bs) {
let result = [];
let blocks_count = Math.floor(bs.length / 3);
let left_bytes = bs.length % 3;
let coding_int, block;
for (let i = 0; i < blocks_count; i++) {
coding_int = bs.slice(3 * i, 3 * i + 3).toInt();
//coding_int = int.from_bytes(bs[3 * i: 3 * i + 3], 'little');
block = VariantBase64Dict[coding_int & 0x3f];
block += VariantBase64Dict[(coding_int >> 6) & 0x3f];
block += VariantBase64Dict[(coding_int >> 12) & 0x3f];
block += VariantBase64Dict[(coding_int >> 18) & 0x3f];
result = result.concat(block.toBytes());
}
switch (left_bytes) {
case 0:
return result;
case 1:
coding_int = bs.slice(3 * blocks_count).toInt();
block = VariantBase64Dict[coding_int & 0x3f];
block += VariantBase64Dict[(coding_int >> 6) & 0x3f];
result = result.concat(block.toBytes());
return result;
default:
coding_int = bs.slice(3 * blocks_count).toInt();
block = VariantBase64Dict[coding_int & 0x3f];
block += VariantBase64Dict[(coding_int >> 6) & 0x3f];
block += VariantBase64Dict[(coding_int >> 12) & 0x3f];
result = result.concat(block.toBytes());
return result;
}
}
function EncryptBytes(key, bs) {
let result = [];
bs.forEach(val => {
result.push(val ^ ((key >> 8) & 0xff))
key = result[-1] & key | 0x482D
});
return result;
}
function DecryptBytes(key, bs) {
let result = [];
bs.forEach(val => {
result.push(val ^ ((key >> 8) & 0xff));
key = val & key | 0x482D;
});
return result;
}
/**
* byte[] to int for low before high byte[] Python3 in.from_bytes(byte[], 'little')
* @param {Number} offset Offset, default 0
* @returns byte[] converted to init
*/
Array.prototype.toInt = function (offset = 0) {
let value = ((this[offset] & 0xFF)
| ((this[offset + 1] & 0xFF) << 8)
| ((this[offset + 2] & 0xFF) << 16)
| ((this[offset + 3] & 0xFF) << 24)
);
return value;
}
/**
* byte[] to string
*/
Array.prototype.bytesToStr = function () {
var str = '',
_arr = this;
for (var i = 0; i < _arr.length; i++) {
var one = _arr[i].toString(2),
v = one.match(/^1+?(?=0)/);
if (v && one.length == 8) {
var bytesLength = v[0].length;
var store = _arr[i].toString(2).slice(7 - bytesLength);
for (var st = 1; st < bytesLength; st++) {
store += _arr[st + i].toString(2).slice(2);
}
str += String.fromCharCode(parseInt(store, 2));
i += bytesLength - 1;
} else {
str += String.fromCharCode(_arr[i]);
}
}
return str;
}
/**
* String to btye[]
*/
String.prototype.toBytes = function () {
var bytes = new Array();
var len, char;
len = this.length;
for (var i = 0; i < len; i++) {
char = this.charCodeAt(i);
if (char >= 0x010000 && char <= 0x10FFFF) {
bytes.push(((char >> 18) & 0x07) | 0xF0);
bytes.push(((char >> 12) & 0x3F) | 0x80);
bytes.push(((char >> 6) & 0x3F) | 0x80);
bytes.push((char & 0x3F) | 0x80);
} else if (char >= 0x000800 && char <= 0x00FFFF) {
bytes.push(((char >> 12) & 0x0F) | 0xE0);
bytes.push(((char >> 6) & 0x3F) | 0x80);
bytes.push((char & 0x3F) | 0x80);
} else if (char >= 0x000080 && char <= 0x0007FF) {
bytes.push(((char >> 6) & 0x1F) | 0xC0);
bytes.push((char & 0x3F) | 0x80);
} else {
bytes.push(char & 0xFF);
}
}
return bytes;
}
/**
* @param {Number} type License Type
* @param {string} userName user ID
* @param {Number} count Number of users supported by license
* @param {Number} majorVersion Major version number e.g. 21.0 is 21
* @param {Number} minorVersion Minor version number e.g. 21.0 is 0.
*/
function generateLicense(type, userName, count, majorVersion, minorVersion) {
/*
LicenseString = '%d#%s|%d%d#%d#%d3%d6%d#%d#%d#%d#' % (
Type,
UserName,
MajorVersion, MinorVersion,
Count,
MajorVersion, MinorVersion, MinorVersion,
0, # Unknown
0, # No Games flag. 0 means "NoGames = false". But it does not work.
0) # No Plugins flag. 0 means "NoPlugins = false". But it does not work.
*/
let licenseSourceStr = `${type}#${userName}|${majorVersion}${minorVersion}#${count}#${majorVersion}3${minorVersion}6${minorVersion}#0#0#0#`;
return VariantBase64Encode(EncryptBytes(0x787, licenseSourceStr.toBytes())).bytesToStr();
}
export { LicenseType, generateLicense }
File diff suppressed because one or more lines are too long