├── .gitignore
├── jetbra-agent
├── src
│ └── main
│ │ ├── resources
│ │ └── jarLocation.txt
│ │ └── java
│ │ └── win
│ │ └── novice
│ │ └── li
│ │ ├── HttpClientAdvice.java
│ │ ├── PKIXBuilderParametersAdvice.java
│ │ ├── SystemAdvice.java
│ │ ├── AgentMain.java
│ │ └── TrustAnchorHolder.java
└── pom.xml
├── readme.md
├── jetbra-server
├── src
│ └── main
│ │ ├── java
│ │ └── win
│ │ │ └── novice
│ │ │ └── li
│ │ │ ├── JetbraServerApplication.java
│ │ │ ├── model
│ │ │ ├── Product.java
│ │ │ └── License.java
│ │ │ └── controller
│ │ │ └── LicenseController.java
│ │ └── resources
│ │ ├── jetbra.crt
│ │ └── jetbra.key
└── pom.xml
├── vmoptions
├── clion.vmoptions
├── goland.vmoptions
├── idea.vmoptions
├── rider.vmoptions
├── studio.vmoptions
├── webide.vmoptions
├── appcode.vmoptions
├── dataspell.vmoptions
├── gateway.vmoptions
├── phpstorm.vmoptions
├── pycharm.vmoptions
├── rubymine.vmoptions
├── webstorm.vmoptions
├── datagrip.vmoptions
├── devecostudio.vmoptions
├── jetbrains_client.vmoptions
└── jetbrainsclient.vmoptions
├── script
├── uninstall-current-user.vbs
├── uninstall-all-users.vbs
├── uninstall.sh
├── install-current-user.vbs
├── install-all-users.vbs
└── install.sh
├── pom.xml
├── LICENSE
├── trust-crt
├── janetfilter.crt
└── jetbra.crt
├── jetbra-dist
├── pom.xml
└── package.xml
├── crt_and_key_gen.py
└── jetbra.js
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .idea/
3 | target/
--------------------------------------------------------------------------------
/jetbra-agent/src/main/resources/jarLocation.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Jetbra
2 | 适用于jetbrains家族产品的一款工具,参照[热老的项目](https://jetbra.in/s)自己写了点代码 ,使用方式与之一致
3 | 在替换janetfilter前需要执行janetfilter的卸载脚本
4 |
5 |
6 |
7 | 新增[油猴脚本](https://greasyfork.org/zh-CN/scripts/480799-jetbra), 使用这个脚本可以生成插件的激活码,需配合该工具使用
8 |
--------------------------------------------------------------------------------
/jetbra-server/src/main/java/win/novice/li/JetbraServerApplication.java:
--------------------------------------------------------------------------------
1 | package win.novice.li;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class JetbraServerApplication {
8 | public static void main(String[] args) {
9 | SpringApplication.run(JetbraServerApplication.class,args);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/jetbra-agent/src/main/java/win/novice/li/HttpClientAdvice.java:
--------------------------------------------------------------------------------
1 | package win.novice.li;
2 |
3 | import net.bytebuddy.asm.Advice;
4 |
5 | import java.net.SocketTimeoutException;
6 |
7 | public class HttpClientAdvice {
8 | @Advice.OnMethodExit
9 | public static void intercept(@Advice.This Object x) throws Exception {
10 | if (x.toString().contains("validateKey.action")){
11 | throw new SocketTimeoutException();
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/jetbra-server/src/main/java/win/novice/li/model/Product.java:
--------------------------------------------------------------------------------
1 |
2 | package win.novice.li.model;
3 |
4 |
5 | import jakarta.validation.constraints.NotBlank;
6 | import jakarta.validation.constraints.NotNull;
7 | import lombok.Data;
8 |
9 | @Data
10 | public class Product {
11 | @NotBlank
12 | private String code;
13 | @NotBlank
14 | private String fallbackDate = "2025-12-31";
15 | @NotBlank
16 | private String paidUpTo = "2025-12-31";
17 | @NotNull
18 | private Boolean extended = false;
19 | }
--------------------------------------------------------------------------------
/vmoptions/clion.vmoptions:
--------------------------------------------------------------------------------
1 | -Xms128m
2 | -Xmx3992m
3 | -XX:ReservedCodeCacheSize=512m
4 | -XX:+IgnoreUnrecognizedVMOptions
5 | -XX:+UseG1GC
6 | -XX:SoftRefLRUPolicyMSPerMB=50
7 | -XX:CICompilerCount=2
8 | -XX:+HeapDumpOnOutOfMemoryError
9 | -XX:-OmitStackTraceInFastThrow
10 | -ea
11 | -Dsun.io.useCanonCaches=false
12 | -Djdk.http.auth.tunneling.disabledSchemes=""
13 | -Djdk.attach.allowAttachSelf=true
14 | -Djdk.module.illegalAccess.silent=true
15 | -Dkotlinx.coroutines.debug=off
16 | -XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log
17 | -XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof
18 |
19 | --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED
20 | --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED
21 |
22 |
--------------------------------------------------------------------------------
/vmoptions/goland.vmoptions:
--------------------------------------------------------------------------------
1 | -Xms128m
2 | -Xmx1024m
3 | -XX:ReservedCodeCacheSize=512m
4 | -XX:+IgnoreUnrecognizedVMOptions
5 | -XX:+UseG1GC
6 | -XX:SoftRefLRUPolicyMSPerMB=50
7 | -XX:CICompilerCount=2
8 | -XX:+HeapDumpOnOutOfMemoryError
9 | -XX:-OmitStackTraceInFastThrow
10 | -ea
11 | -Dsun.io.useCanonCaches=false
12 | -Djdk.http.auth.tunneling.disabledSchemes=""
13 | -Djdk.attach.allowAttachSelf=true
14 | -Djdk.module.illegalAccess.silent=true
15 | -Dkotlinx.coroutines.debug=off
16 | -XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log
17 | -XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof
18 |
19 | --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED
20 | --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED
21 |
22 |
--------------------------------------------------------------------------------
/vmoptions/idea.vmoptions:
--------------------------------------------------------------------------------
1 | -Xms128m
2 | -Xmx3992m
3 | -XX:ReservedCodeCacheSize=512m
4 | -XX:+IgnoreUnrecognizedVMOptions
5 | -XX:+UseG1GC
6 | -XX:SoftRefLRUPolicyMSPerMB=50
7 | -XX:CICompilerCount=2
8 | -XX:+HeapDumpOnOutOfMemoryError
9 | -XX:-OmitStackTraceInFastThrow
10 | -ea
11 | -Dsun.io.useCanonCaches=false
12 | -Djdk.http.auth.tunneling.disabledSchemes=""
13 | -Djdk.attach.allowAttachSelf=true
14 | -Djdk.module.illegalAccess.silent=true
15 | -Dkotlinx.coroutines.debug=off
16 | -XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log
17 | -XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof
18 |
19 | --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED
20 | --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED
21 |
22 |
--------------------------------------------------------------------------------
/vmoptions/rider.vmoptions:
--------------------------------------------------------------------------------
1 | -Xms128m
2 | -Xmx1024m
3 | -XX:ReservedCodeCacheSize=512m
4 | -XX:+IgnoreUnrecognizedVMOptions
5 | -XX:+UseG1GC
6 | -XX:SoftRefLRUPolicyMSPerMB=50
7 | -XX:CICompilerCount=2
8 | -XX:+HeapDumpOnOutOfMemoryError
9 | -XX:-OmitStackTraceInFastThrow
10 | -ea
11 | -Dsun.io.useCanonCaches=false
12 | -Djdk.http.auth.tunneling.disabledSchemes=""
13 | -Djdk.attach.allowAttachSelf=true
14 | -Djdk.module.illegalAccess.silent=true
15 | -Dkotlinx.coroutines.debug=off
16 | -XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log
17 | -XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof
18 |
19 | --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED
20 | --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED
21 |
22 |
--------------------------------------------------------------------------------
/vmoptions/studio.vmoptions:
--------------------------------------------------------------------------------
1 | -Xms128m
2 | -Xmx1024m
3 | -XX:ReservedCodeCacheSize=512m
4 | -XX:+IgnoreUnrecognizedVMOptions
5 | -XX:+UseG1GC
6 | -XX:SoftRefLRUPolicyMSPerMB=50
7 | -XX:CICompilerCount=2
8 | -XX:+HeapDumpOnOutOfMemoryError
9 | -XX:-OmitStackTraceInFastThrow
10 | -ea
11 | -Dsun.io.useCanonCaches=false
12 | -Djdk.http.auth.tunneling.disabledSchemes=""
13 | -Djdk.attach.allowAttachSelf=true
14 | -Djdk.module.illegalAccess.silent=true
15 | -Dkotlinx.coroutines.debug=off
16 | -XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log
17 | -XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof
18 |
19 | --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED
20 | --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED
21 |
22 |
--------------------------------------------------------------------------------
/vmoptions/webide.vmoptions:
--------------------------------------------------------------------------------
1 | -Xms128m
2 | -Xmx1024m
3 | -XX:ReservedCodeCacheSize=512m
4 | -XX:+IgnoreUnrecognizedVMOptions
5 | -XX:+UseG1GC
6 | -XX:SoftRefLRUPolicyMSPerMB=50
7 | -XX:CICompilerCount=2
8 | -XX:+HeapDumpOnOutOfMemoryError
9 | -XX:-OmitStackTraceInFastThrow
10 | -ea
11 | -Dsun.io.useCanonCaches=false
12 | -Djdk.http.auth.tunneling.disabledSchemes=""
13 | -Djdk.attach.allowAttachSelf=true
14 | -Djdk.module.illegalAccess.silent=true
15 | -Dkotlinx.coroutines.debug=off
16 | -XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log
17 | -XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof
18 |
19 | --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED
20 | --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED
21 |
22 |
--------------------------------------------------------------------------------
/vmoptions/appcode.vmoptions:
--------------------------------------------------------------------------------
1 | -Xms128m
2 | -Xmx1024m
3 | -XX:ReservedCodeCacheSize=512m
4 | -XX:+IgnoreUnrecognizedVMOptions
5 | -XX:+UseG1GC
6 | -XX:SoftRefLRUPolicyMSPerMB=50
7 | -XX:CICompilerCount=2
8 | -XX:+HeapDumpOnOutOfMemoryError
9 | -XX:-OmitStackTraceInFastThrow
10 | -ea
11 | -Dsun.io.useCanonCaches=false
12 | -Djdk.http.auth.tunneling.disabledSchemes=""
13 | -Djdk.attach.allowAttachSelf=true
14 | -Djdk.module.illegalAccess.silent=true
15 | -Dkotlinx.coroutines.debug=off
16 | -XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log
17 | -XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof
18 |
19 | --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED
20 | --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED
21 |
22 |
--------------------------------------------------------------------------------
/vmoptions/dataspell.vmoptions:
--------------------------------------------------------------------------------
1 | -Xms128m
2 | -Xmx1024m
3 | -XX:ReservedCodeCacheSize=512m
4 | -XX:+IgnoreUnrecognizedVMOptions
5 | -XX:+UseG1GC
6 | -XX:SoftRefLRUPolicyMSPerMB=50
7 | -XX:CICompilerCount=2
8 | -XX:+HeapDumpOnOutOfMemoryError
9 | -XX:-OmitStackTraceInFastThrow
10 | -ea
11 | -Dsun.io.useCanonCaches=false
12 | -Djdk.http.auth.tunneling.disabledSchemes=""
13 | -Djdk.attach.allowAttachSelf=true
14 | -Djdk.module.illegalAccess.silent=true
15 | -Dkotlinx.coroutines.debug=off
16 | -XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log
17 | -XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof
18 |
19 | --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED
20 | --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED
21 |
22 |
--------------------------------------------------------------------------------
/vmoptions/gateway.vmoptions:
--------------------------------------------------------------------------------
1 | -Xms128m
2 | -Xmx1024m
3 | -XX:ReservedCodeCacheSize=512m
4 | -XX:+IgnoreUnrecognizedVMOptions
5 | -XX:+UseG1GC
6 | -XX:SoftRefLRUPolicyMSPerMB=50
7 | -XX:CICompilerCount=2
8 | -XX:+HeapDumpOnOutOfMemoryError
9 | -XX:-OmitStackTraceInFastThrow
10 | -ea
11 | -Dsun.io.useCanonCaches=false
12 | -Djdk.http.auth.tunneling.disabledSchemes=""
13 | -Djdk.attach.allowAttachSelf=true
14 | -Djdk.module.illegalAccess.silent=true
15 | -Dkotlinx.coroutines.debug=off
16 | -XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log
17 | -XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof
18 |
19 | --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED
20 | --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED
21 |
22 |
--------------------------------------------------------------------------------
/vmoptions/phpstorm.vmoptions:
--------------------------------------------------------------------------------
1 | -Xms128m
2 | -Xmx1024m
3 | -XX:ReservedCodeCacheSize=512m
4 | -XX:+IgnoreUnrecognizedVMOptions
5 | -XX:+UseG1GC
6 | -XX:SoftRefLRUPolicyMSPerMB=50
7 | -XX:CICompilerCount=2
8 | -XX:+HeapDumpOnOutOfMemoryError
9 | -XX:-OmitStackTraceInFastThrow
10 | -ea
11 | -Dsun.io.useCanonCaches=false
12 | -Djdk.http.auth.tunneling.disabledSchemes=""
13 | -Djdk.attach.allowAttachSelf=true
14 | -Djdk.module.illegalAccess.silent=true
15 | -Dkotlinx.coroutines.debug=off
16 | -XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log
17 | -XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof
18 |
19 | --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED
20 | --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED
21 |
22 |
--------------------------------------------------------------------------------
/vmoptions/pycharm.vmoptions:
--------------------------------------------------------------------------------
1 | -Xms128m
2 | -Xmx1024m
3 | -XX:ReservedCodeCacheSize=512m
4 | -XX:+IgnoreUnrecognizedVMOptions
5 | -XX:+UseG1GC
6 | -XX:SoftRefLRUPolicyMSPerMB=50
7 | -XX:CICompilerCount=2
8 | -XX:+HeapDumpOnOutOfMemoryError
9 | -XX:-OmitStackTraceInFastThrow
10 | -ea
11 | -Dsun.io.useCanonCaches=false
12 | -Djdk.http.auth.tunneling.disabledSchemes=""
13 | -Djdk.attach.allowAttachSelf=true
14 | -Djdk.module.illegalAccess.silent=true
15 | -Dkotlinx.coroutines.debug=off
16 | -XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log
17 | -XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof
18 |
19 | --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED
20 | --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED
21 |
22 |
--------------------------------------------------------------------------------
/vmoptions/rubymine.vmoptions:
--------------------------------------------------------------------------------
1 | -Xms128m
2 | -Xmx1024m
3 | -XX:ReservedCodeCacheSize=512m
4 | -XX:+IgnoreUnrecognizedVMOptions
5 | -XX:+UseG1GC
6 | -XX:SoftRefLRUPolicyMSPerMB=50
7 | -XX:CICompilerCount=2
8 | -XX:+HeapDumpOnOutOfMemoryError
9 | -XX:-OmitStackTraceInFastThrow
10 | -ea
11 | -Dsun.io.useCanonCaches=false
12 | -Djdk.http.auth.tunneling.disabledSchemes=""
13 | -Djdk.attach.allowAttachSelf=true
14 | -Djdk.module.illegalAccess.silent=true
15 | -Dkotlinx.coroutines.debug=off
16 | -XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log
17 | -XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof
18 |
19 | --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED
20 | --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED
21 |
22 |
--------------------------------------------------------------------------------
/vmoptions/webstorm.vmoptions:
--------------------------------------------------------------------------------
1 | -Xms128m
2 | -Xmx1024m
3 | -XX:ReservedCodeCacheSize=512m
4 | -XX:+IgnoreUnrecognizedVMOptions
5 | -XX:+UseG1GC
6 | -XX:SoftRefLRUPolicyMSPerMB=50
7 | -XX:CICompilerCount=2
8 | -XX:+HeapDumpOnOutOfMemoryError
9 | -XX:-OmitStackTraceInFastThrow
10 | -ea
11 | -Dsun.io.useCanonCaches=false
12 | -Djdk.http.auth.tunneling.disabledSchemes=""
13 | -Djdk.attach.allowAttachSelf=true
14 | -Djdk.module.illegalAccess.silent=true
15 | -Dkotlinx.coroutines.debug=off
16 | -XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log
17 | -XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof
18 |
19 | --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED
20 | --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED
21 |
22 |
--------------------------------------------------------------------------------
/vmoptions/datagrip.vmoptions:
--------------------------------------------------------------------------------
1 | -Xms128m
2 | -Xmx1024m
3 | -XX:ReservedCodeCacheSize=512m
4 | -XX:+IgnoreUnrecognizedVMOptions
5 | -XX:+UseG1GC
6 | -XX:SoftRefLRUPolicyMSPerMB=50
7 | -XX:CICompilerCount=2
8 | -XX:+HeapDumpOnOutOfMemoryError
9 | -XX:-OmitStackTraceInFastThrow
10 | -ea
11 | -Dsun.io.useCanonCaches=false
12 | -Djdk.http.auth.tunneling.disabledSchemes=""
13 | -Djdk.attach.allowAttachSelf=true
14 | -Djdk.module.illegalAccess.silent=true
15 | -Dkotlinx.coroutines.debug=off
16 | -XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log
17 | -XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof
18 |
19 | --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED
20 | --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED
21 |
22 |
23 |
--------------------------------------------------------------------------------
/vmoptions/devecostudio.vmoptions:
--------------------------------------------------------------------------------
1 | -Xms128m
2 | -Xmx1024m
3 | -XX:ReservedCodeCacheSize=512m
4 | -XX:+IgnoreUnrecognizedVMOptions
5 | -XX:+UseG1GC
6 | -XX:SoftRefLRUPolicyMSPerMB=50
7 | -XX:CICompilerCount=2
8 | -XX:+HeapDumpOnOutOfMemoryError
9 | -XX:-OmitStackTraceInFastThrow
10 | -ea
11 | -Dsun.io.useCanonCaches=false
12 | -Djdk.http.auth.tunneling.disabledSchemes=""
13 | -Djdk.attach.allowAttachSelf=true
14 | -Djdk.module.illegalAccess.silent=true
15 | -Dkotlinx.coroutines.debug=off
16 | -XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log
17 | -XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof
18 |
19 | --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED
20 | --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED
21 |
22 |
--------------------------------------------------------------------------------
/vmoptions/jetbrains_client.vmoptions:
--------------------------------------------------------------------------------
1 | -Xms128m
2 | -Xmx1024m
3 | -XX:ReservedCodeCacheSize=512m
4 | -XX:+IgnoreUnrecognizedVMOptions
5 | -XX:+UseG1GC
6 | -XX:SoftRefLRUPolicyMSPerMB=50
7 | -XX:CICompilerCount=2
8 | -XX:+HeapDumpOnOutOfMemoryError
9 | -XX:-OmitStackTraceInFastThrow
10 | -ea
11 | -Dsun.io.useCanonCaches=false
12 | -Djdk.http.auth.tunneling.disabledSchemes=""
13 | -Djdk.attach.allowAttachSelf=true
14 | -Djdk.module.illegalAccess.silent=true
15 | -Dkotlinx.coroutines.debug=off
16 | -XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log
17 | -XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof
18 |
19 | --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED
20 | --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED
21 |
22 |
--------------------------------------------------------------------------------
/vmoptions/jetbrainsclient.vmoptions:
--------------------------------------------------------------------------------
1 | -Xms128m
2 | -Xmx1024m
3 | -XX:ReservedCodeCacheSize=512m
4 | -XX:+IgnoreUnrecognizedVMOptions
5 | -XX:+UseG1GC
6 | -XX:SoftRefLRUPolicyMSPerMB=50
7 | -XX:CICompilerCount=2
8 | -XX:+HeapDumpOnOutOfMemoryError
9 | -XX:-OmitStackTraceInFastThrow
10 | -ea
11 | -Dsun.io.useCanonCaches=false
12 | -Djdk.http.auth.tunneling.disabledSchemes=""
13 | -Djdk.attach.allowAttachSelf=true
14 | -Djdk.module.illegalAccess.silent=true
15 | -Dkotlinx.coroutines.debug=off
16 | -XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log
17 | -XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof
18 |
19 | --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED
20 | --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED
21 |
22 |
--------------------------------------------------------------------------------
/script/uninstall-current-user.vbs:
--------------------------------------------------------------------------------
1 | Set oShell = CreateObject("WScript.Shell")
2 | Set oEnv = oShell.Environment("USER")
3 |
4 | Dim sEnvKey, sEnvVal, aJBProducts
5 | aJBProducts = Array("idea", "clion", "phpstorm", "goland", "pycharm", "webstorm", "webide", "rider", "datagrip", "rubymine", "appcode", "dataspell", "gateway", "jetbrains_client", "jetbrainsclient", "studio", "devecostudio")
6 |
7 | MsgBox "It may take a few seconds to execute this script." & vbCrLf & vbCrLf & "Click 'OK' button and wait for the prompt of 'Done.' to pop up!"
8 |
9 | For Each sPrd in aJBProducts
10 | sEnvKey = UCase(sPrd) & "_VM_OPTIONS"
11 | sEnvVal = oShell.ExpandEnvironmentStrings("%" & sEnvKey & "%")
12 | If sEnvVal <> ("%" & sEnvKey & "%") Then
13 | oEnv.Remove(sEnvKey)
14 | End If
15 | Next
16 |
17 | MsgBox "Done."
18 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 3.2.0
9 |
10 |
11 | win.novice
12 | jetbra
13 | 0.0.1
14 | pom
15 | jetbra
16 | jetbra
17 |
18 | jetbra-agent
19 | jetbra-server
20 | jetbra-dist
21 |
22 |
23 | 17
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/jetbra-agent/src/main/java/win/novice/li/PKIXBuilderParametersAdvice.java:
--------------------------------------------------------------------------------
1 | package win.novice.li;
2 |
3 | import net.bytebuddy.asm.Advice;
4 |
5 | import java.lang.reflect.Method;
6 | import java.security.cert.TrustAnchor;
7 | import java.util.HashSet;
8 | import java.util.Set;
9 |
10 | public class PKIXBuilderParametersAdvice {
11 |
12 |
13 | @Advice.OnMethodEnter
14 | @SuppressWarnings("unchecked")
15 | public static void intercept(@Advice.Argument(value = 0, readOnly = false) Set trustAnchors) throws Exception {
16 | Class> clazz = Class.forName("win.novice.li.TrustAnchorHolder", true, ClassLoader.getSystemClassLoader());
17 | Method method = clazz.getDeclaredMethod("loadTrustAnchors");
18 | Set loadedTrustAnchors = (Set)method.invoke(null);
19 | HashSet newTrustAnchors = new HashSet<>(trustAnchors);
20 | newTrustAnchors.addAll(loadedTrustAnchors);
21 | trustAnchors = newTrustAnchors;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/jetbra-agent/src/main/java/win/novice/li/SystemAdvice.java:
--------------------------------------------------------------------------------
1 | package win.novice.li;
2 |
3 | import net.bytebuddy.asm.Advice;
4 |
5 | public class SystemAdvice {
6 |
7 |
8 | // System.getProperty
9 | @Advice.OnMethodExit
10 | public static void intercept(@Advice.Argument(0) Object x, @Advice.Return(readOnly = false) String r) throws Exception {
11 | if (x.toString().equals("jb.vmOptionsFile")) {
12 | RuntimeException exception = new RuntimeException();
13 | int nullCnt = 0;
14 | boolean hasReflect = false;
15 | for (StackTraceElement element : exception.getStackTrace()) {
16 | if (element.getFileName() == null) {
17 | nullCnt += 1;
18 | continue;
19 | }
20 | if (element.getFileName().equals("Method.java")) {
21 | hasReflect = true;
22 | }
23 | }
24 | if (nullCnt >= 3 && hasReflect) {
25 | r = null;
26 | }
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 kangwei Li
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/script/uninstall-all-users.vbs:
--------------------------------------------------------------------------------
1 | If Not WScript.Arguments.Named.Exists("elevate") Then
2 | CreateObject("Shell.Application").ShellExecute WScript.FullName, """" & WScript.ScriptFullName & """ /elevate", "", "runas", 10
3 | WScript.Quit
4 | End If
5 |
6 | MsgBox "It may take a few seconds to execute this script." & vbCrLf & vbCrLf & "Click 'OK' button and wait for the prompt of 'Done.' to pop up!"
7 |
8 | Sub RemoveEnv(env)
9 | On Error Resume Next
10 |
11 | Dim sEnvKey, sEnvVal, aJBProducts
12 | aJBProducts = Array("idea", "clion", "phpstorm", "goland", "pycharm", "webstorm", "webide", "rider", "datagrip", "rubymine", "appcode", "dataspell", "gateway", "jetbrains_client", "jetbrainsclient", "studio", "devecostudio")
13 |
14 | For Each sPrd in aJBProducts
15 | sEnvKey = UCase(sPrd) & "_VM_OPTIONS"
16 | sEnvVal = oShell.ExpandEnvironmentStrings("%" & sEnvKey & "%")
17 | If sEnvVal <> ("%" & sEnvKey & "%") Then
18 | env.Remove(sEnvKey)
19 | End If
20 | Next
21 | End Sub
22 |
23 | Set oShell = CreateObject("WScript.Shell")
24 |
25 | RemoveEnv oShell.Environment("USER")
26 | RemoveEnv oShell.Environment("SYSTEM")
27 |
28 | MsgBox "Done."
29 |
--------------------------------------------------------------------------------
/jetbra-server/src/main/java/win/novice/li/model/License.java:
--------------------------------------------------------------------------------
1 | package win.novice.li.model;
2 |
3 | import jakarta.validation.Valid;
4 | import jakarta.validation.constraints.Min;
5 | import jakarta.validation.constraints.NotBlank;
6 | import jakarta.validation.constraints.NotEmpty;
7 | import jakarta.validation.constraints.NotNull;
8 | import lombok.Data;
9 |
10 | import java.util.List;
11 |
12 | @Data
13 | public class License {
14 | private String licenseId;
15 | @NotBlank
16 | private String licenseeName = "Test";
17 | @NotBlank
18 | private String assigneeName = "novice.li";
19 | @NotNull
20 | private String assigneeEmail = "";
21 | @NotNull
22 | private String licenseRestriction = "";
23 | @NotNull
24 | private Boolean checkConcurrentUse = false;
25 |
26 | @NotEmpty
27 | private List<@Valid Product> products;
28 | @NotBlank
29 | private String metadata = "0120230102PPAA013009";
30 | @NotBlank
31 | private String hash = "41472961/0:1563609451";
32 |
33 | @NotNull
34 | @Min(1)
35 | private Integer gracePeriodDays = 7;
36 | @NotNull
37 | private Boolean autoProlongated = true;
38 | @NotNull
39 | private Boolean isAutoProlongated = true;
40 | }
41 |
--------------------------------------------------------------------------------
/trust-crt/janetfilter.crt:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIETDCCAjSgAwIBAgIBDTANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1KZXRQ
3 | cm9maWxlIENBMB4XDTIwMTAxOTA5MDU1M1oXDTIyMTAyMTA5MDU1M1owHzEdMBsG
4 | A1UEAwwUcHJvZDJ5LWZyb20tMjAyMDEwMTkwggEiMA0GCSqGSIb3DQEBAQUAA4IB
5 | DwAwggEKAoIBAQCUlaUFc1wf+CfY9wzFWEL2euKQ5nswqb57V8QZG7d7RoR6rwYU
6 | IXseTOAFq210oMEe++LCjzKDuqwDfsyhgDNTgZBPAaC4vUU2oy+XR+Fq8nBixWIs
7 | H668HeOnRK6RRhsr0rJzRB95aZ3EAPzBuQ2qPaNGm17pAX0Rd6MPRgjp75IWwI9e
8 | A6aMEdPQEVN7uyOtM5zSsjoj79Lbu1fjShOnQZuJcsV8tqnayeFkNzv2LTOlofU/
9 | Tbx502Ro073gGjoeRzNvrynAP03pL486P3KCAyiNPhDs2z8/COMrxRlZW5mfzo0x
10 | sK0dQGNH3UoG/9RVwHG4eS8LFpMTR9oetHZBAgMBAAGjgZkwgZYwCQYDVR0TBAIw
11 | ADAdBgNVHQ4EFgQUJNoRIpb1hUHAk0foMSNM9MCEAv8wSAYDVR0jBEEwP4AUo562
12 | SGdCEjZBvW3gubSgUouX8bOhHKQaMBgxFjAUBgNVBAMMDUpldFByb2ZpbGUgQ0GC
13 | CQDSbLGDsoN54TATBgNVHSUEDDAKBggrBgEFBQcDATALBgNVHQ8EBAMCBaAwDQYJ
14 | KoZIhvcNAQELBQADggIBABKaDfYJk51mtYwUFK8xqhiZaYPd30TlmCmSAaGJ0eBp
15 | vkVeqA2jGYhAQRqFiAlFC63JKvWvRZO1iRuWCEfUMkdqQ9VQPXziE/BlsOIgrL6R
16 | lJfuFcEZ8TK3syIfIGQZNCxYhLLUuet2HE6LJYPQ5c0jH4kDooRpcVZ4rBxNwddp
17 | ctUO2te9UU5/FjhioZQsPvd92qOTsV+8Cyl2fvNhNKD1Uu9ff5AkVIQn4JU23ozd
18 | B/R5oUlebwaTE6WZNBs+TA/qPj+5/we9NH71WRB0hqUoLI2AKKyiPw++FtN4Su1v
19 | sdDlrAzDj9ILjpjJKA1ImuVcG329/WTYIKysZ1CWK3zATg9BeCUPAV1pQy8ToXOq
20 | +RSYen6winZ2OO93eyHv2Iw5kbn1dqfBw1BuTE29V2FJKicJSu8iEOpfoafwJISX
21 | mz1wnnWL3V/0NxTulfWsXugOoLfv0ZIBP1xH9kmf22jjQ2JiHhQZP7ZDsreRrOeI
22 | Q/c4yR8IQvMLfC0WKQqrHu5ZzXTH4NO3CwGWSlTY74kE91zXB5mwWAx1jig+UXYc
23 | 2w4RkVhy0//lOmVya/PEepuuTTI4+UJwC7qbVlh5zfhj8oTNUXgN0AOc+Q0/WFPl
24 | 1aw5VV/VrO8FCoB15lFVlpKaQ1Yh+DVU8ke+rt9Th0BCHXe0uZOEmH0nOnH/0onD
25 | -----END CERTIFICATE-----
26 |
--------------------------------------------------------------------------------
/jetbra-dist/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | win.novice
8 | jetbra
9 | 0.0.1
10 |
11 |
12 | win.novice.li
13 | jetbra-dist
14 |
15 |
16 | 17
17 | 17
18 | UTF-8
19 |
20 |
21 |
22 |
23 | maven-assembly-plugin
24 |
25 | jetbra
26 |
27 | package.xml
28 |
29 |
30 |
31 |
32 | make-assembly
33 | package
34 |
35 | single
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/crt_and_key_gen.py:
--------------------------------------------------------------------------------
1 | import datetime
2 |
3 | from cryptography import x509
4 | from cryptography.hazmat.backends import default_backend
5 | from cryptography.hazmat.primitives import hashes, serialization
6 | from cryptography.hazmat.primitives.asymmetric import rsa
7 | from cryptography.x509.oid import NameOID
8 |
9 | one_day = datetime.timedelta(days=1)
10 | ten_day = datetime.timedelta(days=3650)
11 | today = datetime.datetime.today()
12 | yesterday = today - one_day
13 | tomorrow = today + ten_day
14 |
15 | private_key = rsa.generate_private_key(
16 | public_exponent=65537,
17 | key_size=4096,
18 | backend=default_backend()
19 | )
20 | public_key = private_key.public_key()
21 | builder = x509.CertificateBuilder()
22 |
23 | builder = builder.subject_name(x509.Name([
24 | x509.NameAttribute(NameOID.COMMON_NAME, 'Novice'),
25 | ]))
26 | builder = builder.issuer_name(x509.Name([
27 | x509.NameAttribute(NameOID.COMMON_NAME, 'JetProfile CA'),
28 | ]))
29 | builder = builder.not_valid_before(yesterday)
30 | builder = builder.not_valid_after(tomorrow)
31 | builder = builder.serial_number(x509.random_serial_number())
32 | builder = builder.public_key(public_key)
33 |
34 | certificate = builder.sign(
35 | private_key=private_key, algorithm=hashes.SHA256(),
36 | backend=default_backend()
37 | )
38 |
39 | private_bytes = private_key.private_bytes(
40 | encoding=serialization.Encoding.PEM,
41 | format=serialization.PrivateFormat.TraditionalOpenSSL,
42 | encryption_algorithm=serialization.NoEncryption())
43 | public_bytes = certificate.public_bytes(
44 | encoding=serialization.Encoding.PEM)
45 | with open("ca.key", "wb") as fout:
46 | fout.write(private_bytes)
47 | with open("ca.crt", "wb") as fout:
48 | fout.write(public_bytes)
49 |
--------------------------------------------------------------------------------
/trust-crt/jetbra.crt:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIEtTCCAp2gAwIBAgIUDyuccmylba71lZQAQic5TJiAhwwwDQYJKoZIhvcNAQEL
3 | BQAwGDEWMBQGA1UEAwwNSmV0UHJvZmlsZSBDQTAeFw0yMzA5MjkxNDA2MTJaFw0z
4 | MzA5MjcxNDA2MTJaMBExDzANBgNVBAMMBk5vdmljZTCCAiIwDQYJKoZIhvcNAQEB
5 | BQADggIPADCCAgoCggIBALenqcGP2ZxGkYqmKA9c4Hzf8+YD1smvmOxKjd+bmTLr
6 | utM/hXv1cj1rW3/lqyDtdDk7K6W8/TDq1CRrEt+Do6l30DxhAiC34aH8DmGwgq77
7 | xEoLimvH5LpePxflF+tbB1RZtFgFDOIYLdSQaKFH2JDgVKxhLiV3S6jniPhkCtWW
8 | rTs+E6vq4N15Bm3NnM5AJILqjtUbOjNfaxVq6RrOoTc0R3Fqqo6yvxo/+JYa2UnH
9 | IC+r2dbKuDLMUrtgnydEUdJNX0zH9FtcdELvr48uc9mY038TWUsZUK1pnQbxA2bP
10 | yA4qnYJ9IvUgO6LtLXvGFm137YQMS1N41AHDBOrwoNI8UoDX+qI3rM96biFOFvn7
11 | Edky7rByzybt3H+zxdojfjvpL1E0NO98BT9zfufHAaAxZtlmDOu5LDJe3CGurnyR
12 | MRExbtc+Qjl1mUh6tG4lakAwdsoxry0GdG72yaYyb9it53kaFks/T/s7Z7bRJzVF
13 | zQDV1Y4bzUtk43vKm2vztBVlQkBkZY5f2Jbe5Ig3b8swQzBnOT0mrL5SPUhwmQ6I
14 | xkEWztj55OEujBMmRr92oESuq9ZYMaeLidKWVR3/++HA8BRZaRGEKtSHZCbFEFdi
15 | hDxxJv9Xh6NuT/ewJ6HYp+0NQpFnUnJ72n8wV+tudpam7aKcdzVmz7cNwOhG2Ls7
16 | AgMBAAEwDQYJKoZIhvcNAQELBQADggIBAIdeaQfKni7tXtcywC3zJvGzaaj242pS
17 | WB1y40HW8jub0uHjTLsBPX27iA/5rb+rNXtUWX/f2K+DU4IgaIiiHhkDrMsw7piv
18 | azqwA9h7/uA0A5nepmTYf/HY4W6P2stbeqInNsFRZXS7Jg4Q5LgEtHKo/H8USjtV
19 | w9apmE3BCElkXRuelXMsSllpR/JEVv/8NPLmnHSY02q4KMVW2ozXtaAxSYQmZswy
20 | P1YnBcnRukoI4igobpcKQXwGoQCIUlec8LbFXYM9V2eNCwgABqd4r67m7QJq31Y/
21 | 1TJysQdMH+hoPFy9rqNCxSq3ptpuzcYAk6qVf58PrrYH/6bHwiYPAayvvdzNPOhM
22 | 9OCwomfcazhK3y7HyS8aBLntTQYFf7vYzZxPMDybYTvJM+ClCNnVD7Q9fttIJ6eM
23 | XFsXb8YK1uGNjQW8Y4WHk1MCHuD9ZumWu/CtAhBn6tllTQWwNMaPOQvKf1kr1Kt5
24 | etrONY+B6O+Oi75SZbDuGz7PIF9nMPy4WB/8XgKdVFtKJ7/zLIPHgY8IKgbx/VTz
25 | 6uBhYo8wOf3xzzweMnn06UcfV3JGNvtMuV4vlkZNNxXeifsgzHugCvJX0nybhfBh
26 | fIqVyfK6t0eKJqrvp54XFEtJGR+lf3pBfTdcOI6QFEPKGZKoQz8Ck+BC/WBDtbjc
27 | /uYKczZ8DKZu
28 | -----END CERTIFICATE-----
29 |
--------------------------------------------------------------------------------
/jetbra-server/src/main/resources/jetbra.crt:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIEtTCCAp2gAwIBAgIUDyuccmylba71lZQAQic5TJiAhwwwDQYJKoZIhvcNAQEL
3 | BQAwGDEWMBQGA1UEAwwNSmV0UHJvZmlsZSBDQTAeFw0yMzA5MjkxNDA2MTJaFw0z
4 | MzA5MjcxNDA2MTJaMBExDzANBgNVBAMMBk5vdmljZTCCAiIwDQYJKoZIhvcNAQEB
5 | BQADggIPADCCAgoCggIBALenqcGP2ZxGkYqmKA9c4Hzf8+YD1smvmOxKjd+bmTLr
6 | utM/hXv1cj1rW3/lqyDtdDk7K6W8/TDq1CRrEt+Do6l30DxhAiC34aH8DmGwgq77
7 | xEoLimvH5LpePxflF+tbB1RZtFgFDOIYLdSQaKFH2JDgVKxhLiV3S6jniPhkCtWW
8 | rTs+E6vq4N15Bm3NnM5AJILqjtUbOjNfaxVq6RrOoTc0R3Fqqo6yvxo/+JYa2UnH
9 | IC+r2dbKuDLMUrtgnydEUdJNX0zH9FtcdELvr48uc9mY038TWUsZUK1pnQbxA2bP
10 | yA4qnYJ9IvUgO6LtLXvGFm137YQMS1N41AHDBOrwoNI8UoDX+qI3rM96biFOFvn7
11 | Edky7rByzybt3H+zxdojfjvpL1E0NO98BT9zfufHAaAxZtlmDOu5LDJe3CGurnyR
12 | MRExbtc+Qjl1mUh6tG4lakAwdsoxry0GdG72yaYyb9it53kaFks/T/s7Z7bRJzVF
13 | zQDV1Y4bzUtk43vKm2vztBVlQkBkZY5f2Jbe5Ig3b8swQzBnOT0mrL5SPUhwmQ6I
14 | xkEWztj55OEujBMmRr92oESuq9ZYMaeLidKWVR3/++HA8BRZaRGEKtSHZCbFEFdi
15 | hDxxJv9Xh6NuT/ewJ6HYp+0NQpFnUnJ72n8wV+tudpam7aKcdzVmz7cNwOhG2Ls7
16 | AgMBAAEwDQYJKoZIhvcNAQELBQADggIBAIdeaQfKni7tXtcywC3zJvGzaaj242pS
17 | WB1y40HW8jub0uHjTLsBPX27iA/5rb+rNXtUWX/f2K+DU4IgaIiiHhkDrMsw7piv
18 | azqwA9h7/uA0A5nepmTYf/HY4W6P2stbeqInNsFRZXS7Jg4Q5LgEtHKo/H8USjtV
19 | w9apmE3BCElkXRuelXMsSllpR/JEVv/8NPLmnHSY02q4KMVW2ozXtaAxSYQmZswy
20 | P1YnBcnRukoI4igobpcKQXwGoQCIUlec8LbFXYM9V2eNCwgABqd4r67m7QJq31Y/
21 | 1TJysQdMH+hoPFy9rqNCxSq3ptpuzcYAk6qVf58PrrYH/6bHwiYPAayvvdzNPOhM
22 | 9OCwomfcazhK3y7HyS8aBLntTQYFf7vYzZxPMDybYTvJM+ClCNnVD7Q9fttIJ6eM
23 | XFsXb8YK1uGNjQW8Y4WHk1MCHuD9ZumWu/CtAhBn6tllTQWwNMaPOQvKf1kr1Kt5
24 | etrONY+B6O+Oi75SZbDuGz7PIF9nMPy4WB/8XgKdVFtKJ7/zLIPHgY8IKgbx/VTz
25 | 6uBhYo8wOf3xzzweMnn06UcfV3JGNvtMuV4vlkZNNxXeifsgzHugCvJX0nybhfBh
26 | fIqVyfK6t0eKJqrvp54XFEtJGR+lf3pBfTdcOI6QFEPKGZKoQz8Ck+BC/WBDtbjc
27 | /uYKczZ8DKZu
28 | -----END CERTIFICATE-----
29 |
--------------------------------------------------------------------------------
/jetbra-dist/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | all
5 |
6 | zip
7 |
8 | false
9 |
10 |
11 | ${project.parent.basedir}/script
12 | script
13 |
14 | *
15 |
16 |
17 |
18 | ${project.parent.basedir}/vmoptions
19 | vmoptions
20 |
21 | *
22 |
23 |
24 |
25 | ${project.parent.basedir}/trust-crt
26 | trust-crt
27 |
28 | *
29 |
30 |
31 |
32 | ${project.parent.basedir}/trust-crt
33 | trust-crt
34 |
35 | *
36 |
37 |
38 |
39 |
40 |
41 | ${project.parent.basedir}/jetbra-agent/target/jetbra-agent.jar
42 | jetbra-agent.jar
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/script/uninstall.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | OS_NAME=$(uname -s)
6 | JB_PRODUCTS="idea clion phpstorm goland pycharm webstorm webide rider datagrip rubymine appcode dataspell gateway jetbrains_client jetbrainsclient studio devecostudio"
7 |
8 | KDE_ENV_DIR="${HOME}/.config/plasma-workspace/env"
9 |
10 | PROFILE_PATH="${HOME}/.profile"
11 | ZSH_PROFILE_PATH="${HOME}/.zshrc"
12 | PLIST_PATH="${HOME}/Library/LaunchAgents/jetbrains.vmoptions.plist"
13 |
14 | if [ $OS_NAME = "Darwin" ]; then
15 | BASH_PROFILE_PATH="${HOME}/.bash_profile"
16 | else
17 | BASH_PROFILE_PATH="${HOME}/.bashrc"
18 | fi
19 |
20 | touch "${PROFILE_PATH}"
21 | touch "${BASH_PROFILE_PATH}"
22 | touch "${ZSH_PROFILE_PATH}"
23 |
24 | MY_VMOPTIONS_SHELL_NAME="jetbrains.vmoptions.sh"
25 | MY_VMOPTIONS_SHELL_FILE="${HOME}/.${MY_VMOPTIONS_SHELL_NAME}"
26 |
27 | rm -rf "${MY_VMOPTIONS_SHELL_FILE}"
28 |
29 | if [ $OS_NAME = "Darwin" ]; then
30 | for PRD in $JB_PRODUCTS; do
31 | ENV_NAME=$(echo $PRD | tr '[a-z]' '[A-Z]')"_VM_OPTIONS"
32 |
33 | launchctl unsetenv "${ENV_NAME}"
34 | done
35 |
36 | rm -rf "${PLIST_PATH}"
37 |
38 | sed -i '' '/___MY_VMOPTIONS_SHELL_FILE="${HOME}\/\.jetbrains\.vmoptions\.sh"; if /d' "${PROFILE_PATH}" >/dev/null 2>&1
39 | sed -i '' '/___MY_VMOPTIONS_SHELL_FILE="${HOME}\/\.jetbrains\.vmoptions\.sh"; if /d' "${BASH_PROFILE_PATH}" >/dev/null 2>&1
40 | sed -i '' '/___MY_VMOPTIONS_SHELL_FILE="${HOME}\/\.jetbrains\.vmoptions\.sh"; if /d' "${ZSH_PROFILE_PATH}" >/dev/null 2>&1
41 |
42 | echo 'done.'
43 | else
44 | sed -i '/___MY_VMOPTIONS_SHELL_FILE="${HOME}\/\.jetbrains\.vmoptions\.sh"; if /d' "${PROFILE_PATH}" >/dev/null 2>&1
45 | sed -i '/___MY_VMOPTIONS_SHELL_FILE="${HOME}\/\.jetbrains\.vmoptions\.sh"; if /d' "${BASH_PROFILE_PATH}" >/dev/null 2>&1
46 | sed -i '/___MY_VMOPTIONS_SHELL_FILE="${HOME}\/\.jetbrains\.vmoptions\.sh"; if /d' "${ZSH_PROFILE_PATH}" >/dev/null 2>&1
47 |
48 | rm -rf "${KDE_ENV_DIR}/${MY_VMOPTIONS_SHELL_NAME}"
49 | echo "done. you'd better log off first!"
50 | fi
51 |
--------------------------------------------------------------------------------
/script/install-current-user.vbs:
--------------------------------------------------------------------------------
1 | Set oShell = CreateObject("WScript.Shell")
2 | Set oEnv = oShell.Environment("USER")
3 | Set oFS = CreateObject("Scripting.FileSystemObject")
4 |
5 | Dim sEnvKey, sEnvVal, aJBProducts
6 | aJBProducts = Array("idea", "clion", "phpstorm", "goland", "pycharm", "webstorm", "webide", "rider", "datagrip", "rubymine", "appcode", "dataspell", "gateway", "jetbrains_client", "jetbrainsclient", "studio", "devecostudio")
7 |
8 | Set re = New RegExp
9 | re.Global = True
10 | re.IgnoreCase = True
11 | re.Pattern = "^\-javaagent:.*[\/\\]jetbra\-agent\.jar.*"
12 |
13 | Dim sBasePath, sJarFile
14 | sBasePath = oFS.GetParentFolderName(oShell.CurrentDirectory)
15 | sJarFile = sBasePath & "\jetbra-agent.jar"
16 |
17 | If Not oFS.FileExists(sJarFile) Then
18 | MsgBox "jetbra-agent.jar not found", vbOKOnly Or vbCritical
19 | WScript.Quit -1
20 | End If
21 |
22 | MsgBox "It may take a few seconds to execute this script." & vbCrLf & vbCrLf & "Click 'OK' button and wait for the prompt of 'Done.' to pop up!"
23 |
24 | Dim sVmOptionsFile
25 | For Each sPrd in aJBProducts
26 | sEnvKey = UCase(sPrd) & "_VM_OPTIONS"
27 | sVmOptionsFile = sBasePath & "\vmoptions\" & sPrd & ".vmoptions"
28 | If oFS.FileExists(sVmOptionsFile) Then
29 | ProcessVmOptions sVmOptionsFile
30 | oEnv(sEnvKey) = sVmOptionsFile
31 | End If
32 | Next
33 |
34 | Sub ProcessVmOptions(ByVal file)
35 | Dim sLine, sNewContent, bMatch
36 | Set oFile = oFS.OpenTextFile(file, 1, 0)
37 |
38 | sNewContent = ""
39 | Do Until oFile.AtEndOfStream
40 | sLine = oFile.ReadLine
41 | bMatch = re.Test(sLine)
42 | If Not bMatch Then
43 | sNewContent = sNewContent & sLine & vbLf
44 | End If
45 | Loop
46 | oFile.Close
47 |
48 | sNewContent = sNewContent & "-javaagent:" & sJarFile & "=jetbrains"
49 | Set oFile = oFS.OpenTextFile(file, 2, 0)
50 | oFile.Write sNewContent
51 | oFile.Close
52 | End Sub
53 |
54 | MsgBox "Done."
55 |
--------------------------------------------------------------------------------
/jetbra-agent/src/main/java/win/novice/li/AgentMain.java:
--------------------------------------------------------------------------------
1 | package win.novice.li;
2 |
3 | import net.bytebuddy.agent.builder.AgentBuilder;
4 | import net.bytebuddy.asm.Advice;
5 | import net.bytebuddy.matcher.ElementMatchers;
6 |
7 | import java.lang.instrument.Instrumentation;
8 | import java.util.Set;
9 |
10 | public class AgentMain {
11 | public static void premain(String agentArgs, Instrumentation inst) throws Exception {
12 | printLogo();
13 | AgentBuilder agentBuilder = newAgentBuilder();
14 | agentBuilder.type(ElementMatchers.named("java.security.cert.PKIXBuilderParameters"))
15 | .transform((builder, typeDescription, classLoader, module, protectionDomain) -> builder
16 | .visit(Advice.to(PKIXBuilderParametersAdvice.class)
17 | .on(ElementMatchers.isConstructor().and(ElementMatchers.takesArgument(0, Set.class)))))
18 | .asTerminalTransformation()
19 | .type(ElementMatchers.named("sun.net.www.http.HttpClient"))
20 | .transform((builder, typeDescription, classLoader, module, protectionDomain) -> builder
21 | .visit(Advice.to(HttpClientAdvice.class)
22 | .on(ElementMatchers.named("openServer"))))
23 | .asTerminalTransformation()
24 | .type(ElementMatchers.named("java.lang.System"))
25 | .transform((builder, typeDescription, classLoader, module, protectionDomain) -> builder
26 | .visit(Advice.to(SystemAdvice.class)
27 | .on(ElementMatchers.named("getProperty"))))
28 | .asTerminalTransformation()
29 | .installOn(inst);
30 |
31 | agentBuilder.installOn(inst);
32 | }
33 |
34 | static AgentBuilder newAgentBuilder() {
35 | return new AgentBuilder.Default()
36 | .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
37 | .with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)
38 | .with(AgentBuilder.TypeStrategy.Default.REDEFINE)
39 | .ignore(ElementMatchers.nameStartsWith("net.bytebuddy."));
40 | }
41 |
42 |
43 | static void printLogo() {
44 | System.out.println(" _ _ _ \n" +
45 | " | | ___| |_| |__ _ __ __ _ \n" +
46 | " _ | |/ _ \\ __| '_ \\| '__/ _` |\n" +
47 | "| |_| | __/ |_| |_) | | | (_| |\n" +
48 | " \\___/ \\___|\\__|_.__/|_| \\__,_|");
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/jetbra-agent/src/main/java/win/novice/li/TrustAnchorHolder.java:
--------------------------------------------------------------------------------
1 | package win.novice.li;
2 |
3 | import java.io.File;
4 | import java.io.FileInputStream;
5 | import java.net.URI;
6 | import java.net.URL;
7 | import java.nio.file.Paths;
8 | import java.security.cert.CertificateFactory;
9 | import java.security.cert.TrustAnchor;
10 | import java.security.cert.X509Certificate;
11 | import java.util.HashSet;
12 | import java.util.Set;
13 |
14 | public class TrustAnchorHolder {
15 | public static Set TRUST_ANCHORS;
16 |
17 |
18 | public static Set loadTrustAnchors() throws Exception {
19 | if (TRUST_ANCHORS != null) {
20 | return TRUST_ANCHORS;
21 | }
22 | TRUST_ANCHORS = new HashSet<>();
23 |
24 | String certDir;
25 | if (System.getenv("JB_HOME") != null) {
26 | certDir = System.getenv("JB_HOME");
27 | } else {
28 | URI jarURI = getJarURI();
29 | if (jarURI == null) {
30 | return TRUST_ANCHORS;
31 | }
32 | certDir = Paths.get(jarURI).getParent().resolve("trust-crt").toString();
33 | }
34 | System.out.println("load crt from " + certDir);
35 | File dir = new File(certDir);
36 | if (dir.exists() && dir.isDirectory()) {
37 | File[] files = dir.listFiles();
38 | if (files != null) {
39 | CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
40 | for (File item : files) {
41 | if (item.getName().endsWith(".crt")) {
42 | X509Certificate cert = (X509Certificate) certificateFactory.generateCertificate(new FileInputStream(item));
43 | TRUST_ANCHORS.add(new TrustAnchor(cert, null));
44 | }
45 | }
46 | }
47 | }
48 | System.out.println("loaded " + TRUST_ANCHORS.size() + " crts");
49 | return TRUST_ANCHORS;
50 | }
51 |
52 | public static URI getJarURI() throws Exception {
53 | URL url = TrustAnchorHolder.class.getProtectionDomain().getCodeSource().getLocation();
54 | if (null != url) {
55 | return url.toURI();
56 | }
57 | String resourcePath = "/jarLocation.txt";
58 | url = TrustAnchorHolder.class.getResource(resourcePath);
59 | if (null == url) {
60 | return null;
61 | }
62 | String path = url.getPath();
63 | path = path.substring(0, path.length() - resourcePath.length() - 1);
64 | return new URI(path);
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/jetbra-agent/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | win.novice
8 | jetbra
9 | 0.0.1
10 |
11 |
12 | win.novice.li
13 | jetbra-agent
14 |
15 |
16 | 17
17 | 17
18 | UTF-8
19 |
20 |
21 |
22 | net.bytebuddy
23 | byte-buddy
24 | 1.14.8
25 |
26 |
27 |
28 | net.bytebuddy
29 | byte-buddy-agent
30 | 1.14.8
31 |
32 |
33 |
34 |
35 |
36 |
37 | maven-assembly-plugin
38 |
39 | false
40 |
41 | jar-with-dependencies
42 |
43 |
44 |
45 | win.novice.li.AgentMain
46 | true
47 | true
48 |
49 |
50 | jetbra-agent
51 |
52 |
53 |
54 | make-assembly
55 | package
56 |
57 | single
58 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/script/install-all-users.vbs:
--------------------------------------------------------------------------------
1 | If Not WScript.Arguments.Named.Exists("elevate") Then
2 | CreateObject("Shell.Application").ShellExecute WScript.FullName, """" & WScript.ScriptFullName & """ /elevate", "", "runas", 10
3 | WScript.Quit
4 | End If
5 |
6 | Set oShell = CreateObject("WScript.Shell")
7 | Set oEnvSystem = oShell.Environment("SYSTEM")
8 | Set oFS = CreateObject("Scripting.FileSystemObject")
9 |
10 | Dim sBasePath, sJarFile
11 | sBasePath = oFS.GetParentFolderName(oFS.GetParentFolderName(WScript.ScriptFullName))
12 | sJarFile = sBasePath & "\jetbra-agent.jar"
13 |
14 | If Not oFS.FileExists(sJarFile) Then
15 | MsgBox "jetbra-agent.jar not found", vbOKOnly Or vbCritical
16 | WScript.Quit -1
17 | End If
18 |
19 | MsgBox "It may take a few seconds to execute this script." & vbCrLf & vbCrLf & "Click 'OK' button and wait for the prompt of 'Done.' to pop up!"
20 |
21 | Dim sEnvKey, sEnvVal, aJBProducts
22 | aJBProducts = Array("idea", "clion", "phpstorm", "goland", "pycharm", "webstorm", "webide", "rider", "datagrip", "rubymine", "appcode", "dataspell", "gateway", "jetbrains_client", "jetbrainsclient", "studio", "devecostudio")
23 |
24 | Set re = New RegExp
25 | re.Global = True
26 | re.IgnoreCase = True
27 | re.Pattern = "^\-javaagent:.*[\/\\]jetbra\-agent\.jar.*"
28 |
29 | Sub RemoveEnv(env)
30 | On Error Resume Next
31 |
32 | For Each sPrd in aJBProducts
33 | sEnvKey = UCase(sPrd) & "_VM_OPTIONS"
34 | sEnvVal = oShell.ExpandEnvironmentStrings("%" & sEnvKey & "%")
35 | If sEnvVal <> ("%" & sEnvKey & "%") Then
36 | env.Remove(sEnvKey)
37 | End If
38 | Next
39 | End Sub
40 |
41 | RemoveEnv oShell.Environment("USER")
42 |
43 | Dim sVmOptionsFile
44 | For Each sPrd in aJBProducts
45 | sEnvKey = UCase(sPrd) & "_VM_OPTIONS"
46 | sVmOptionsFile = sBasePath & "\vmoptions\" & sPrd & ".vmoptions"
47 | If oFS.FileExists(sVmOptionsFile) Then
48 | ProcessVmOptions sVmOptionsFile
49 | oEnvSystem(sEnvKey) = sVmOptionsFile
50 | End If
51 | Next
52 |
53 | Sub ProcessVmOptions(ByVal file)
54 | Dim sLine, sNewContent, bMatch
55 | Set oFile = oFS.OpenTextFile(file, 1, 0)
56 |
57 | sNewContent = ""
58 | Do Until oFile.AtEndOfStream
59 | sLine = oFile.ReadLine
60 | bMatch = re.Test(sLine)
61 | If Not bMatch Then
62 | sNewContent = sNewContent & sLine & vbLf
63 | End If
64 | Loop
65 | oFile.Close
66 |
67 | sNewContent = sNewContent & "-javaagent:" & sJarFile & "=jetbrains"
68 | Set oFile = oFS.OpenTextFile(file, 2, 0)
69 | oFile.Write sNewContent
70 | oFile.Close
71 | End Sub
72 |
73 | MsgBox "Done."
74 |
--------------------------------------------------------------------------------
/jetbra-server/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | win.novice
8 | jetbra
9 | 0.0.1
10 |
11 |
12 | win.novice.li
13 | jetbra-server
14 |
15 |
16 | 17
17 | 17
18 | UTF-8
19 |
20 |
21 |
22 |
23 | org.springframework.boot
24 | spring-boot-starter
25 |
26 |
27 | org.springframework.boot
28 | spring-boot-starter-web
29 |
30 |
31 | org.springframework.boot
32 | spring-boot-starter-validation
33 |
34 |
35 |
36 | org.springframework.boot
37 | spring-boot-starter-test
38 | test
39 |
40 |
41 | org.bouncycastle
42 | bcpkix-jdk18on
43 | 1.72
44 |
45 |
46 | org.bouncycastle
47 | bcprov-jdk18on
48 | 1.72
49 |
50 |
51 | org.projectlombok
52 | lombok
53 | true
54 |
55 |
56 |
57 |
58 |
59 |
60 | org.springframework.boot
61 | spring-boot-maven-plugin
62 |
63 |
64 |
65 | org.projectlombok
66 | lombok
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/jetbra-server/src/main/resources/jetbra.key:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | 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
51 | -----END RSA PRIVATE KEY-----
52 |
--------------------------------------------------------------------------------
/script/install.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | OS_NAME=$(uname -s)
6 | JB_PRODUCTS="idea clion phpstorm goland pycharm webstorm webide rider datagrip rubymine appcode dataspell gateway jetbrains_client jetbrainsclient studio devecostudio"
7 |
8 | BASE_PATH=$(dirname $(
9 | cd $(dirname "$0")
10 | pwd
11 | ))
12 |
13 | JAR_FILE_PATH="${BASE_PATH}/jetbra-agent.jar"
14 |
15 | if [ ! -f "${JAR_FILE_PATH}" ]; then
16 | echo 'jetbra-agent.jar not found'
17 | exit -1
18 | fi
19 |
20 | KDE_ENV_DIR="${HOME}/.config/plasma-workspace/env"
21 | LAUNCH_AGENTS_DIR="${HOME}/Library/LaunchAgents"
22 |
23 | PROFILE_PATH="${HOME}/.profile"
24 | ZSH_PROFILE_PATH="${HOME}/.zshrc"
25 | PLIST_PATH="${LAUNCH_AGENTS_DIR}/jetbrains.vmoptions.plist"
26 |
27 | if [ $OS_NAME = "Darwin" ]; then
28 | BASH_PROFILE_PATH="${HOME}/.bash_profile"
29 |
30 | mkdir -p "${LAUNCH_AGENTS_DIR}"
31 | echo 'Labeljetbrains.vmoptionsProgramArgumentssh-c' >"${PLIST_PATH}"
32 | else
33 | BASH_PROFILE_PATH="${HOME}/.bashrc"
34 | mkdir -p "${KDE_ENV_DIR}"
35 | fi
36 |
37 | touch "${PROFILE_PATH}"
38 | touch "${BASH_PROFILE_PATH}"
39 | touch "${ZSH_PROFILE_PATH}"
40 |
41 | MY_VMOPTIONS_SHELL_NAME="jetbrains.vmoptions.sh"
42 | MY_VMOPTIONS_SHELL_FILE="${HOME}/.${MY_VMOPTIONS_SHELL_NAME}"
43 | echo '#!/bin/sh' >"${MY_VMOPTIONS_SHELL_FILE}"
44 |
45 | EXEC_LINE='___MY_VMOPTIONS_SHELL_FILE="${HOME}/.jetbrains.vmoptions.sh"; if [ -f "${___MY_VMOPTIONS_SHELL_FILE}" ]; then . "${___MY_VMOPTIONS_SHELL_FILE}"; fi'
46 |
47 | for PRD in $JB_PRODUCTS; do
48 | VM_FILE_PATH="${BASE_PATH}/vmoptions/${PRD}.vmoptions"
49 | if [ ! -f "${VM_FILE_PATH}" ]; then
50 | continue
51 | fi
52 |
53 | if [ $OS_NAME = "Darwin" ]; then
54 | sed -i '' '/^\-javaagent:.*[\/\\]jetbra\-agent\.jar.*/d' "${VM_FILE_PATH}"
55 | else
56 | sed -i '/^\-javaagent:.*[\/\\]jetbra\-agent\.jar.*/d' "${VM_FILE_PATH}"
57 | fi
58 |
59 | echo "-javaagent:${JAR_FILE_PATH}=jetbrains" >>"${VM_FILE_PATH}"
60 |
61 | ENV_NAME=$(echo $PRD | tr '[a-z]' '[A-Z]')"_VM_OPTIONS"
62 | echo "export ${ENV_NAME}=\"${VM_FILE_PATH}\"" >>"${MY_VMOPTIONS_SHELL_FILE}"
63 |
64 | if [ $OS_NAME = "Darwin" ]; then
65 | launchctl setenv "${ENV_NAME}" "${VM_FILE_PATH}"
66 | echo "launchctl setenv \"${ENV_NAME}\" \"${VM_FILE_PATH}\"" >>"${PLIST_PATH}"
67 | fi
68 | done
69 |
70 | if [ $OS_NAME = "Darwin" ]; then
71 | sed -i '' '/___MY_VMOPTIONS_SHELL_FILE="${HOME}\/\.jetbrains\.vmoptions\.sh"; if /d' "${PROFILE_PATH}" >/dev/null 2>&1
72 | sed -i '' '/___MY_VMOPTIONS_SHELL_FILE="${HOME}\/\.jetbrains\.vmoptions\.sh"; if /d' "${BASH_PROFILE_PATH}" >/dev/null 2>&1
73 | sed -i '' '/___MY_VMOPTIONS_SHELL_FILE="${HOME}\/\.jetbrains\.vmoptions\.sh"; if /d' "${ZSH_PROFILE_PATH}" >/dev/null 2>&1
74 |
75 | echo 'RunAtLoad' >>"${PLIST_PATH}"
76 | else
77 | sed -i '/___MY_VMOPTIONS_SHELL_FILE="${HOME}\/\.jetbrains\.vmoptions\.sh"; if /d' "${PROFILE_PATH}" >/dev/null 2>&1
78 | sed -i '/___MY_VMOPTIONS_SHELL_FILE="${HOME}\/\.jetbrains\.vmoptions\.sh"; if /d' "${BASH_PROFILE_PATH}" >/dev/null 2>&1
79 | sed -i '/___MY_VMOPTIONS_SHELL_FILE="${HOME}\/\.jetbrains\.vmoptions\.sh"; if /d' "${ZSH_PROFILE_PATH}" >/dev/null 2>&1
80 | fi
81 |
82 | echo "${EXEC_LINE}" >>"${PROFILE_PATH}"
83 | echo "${EXEC_LINE}" >>"${BASH_PROFILE_PATH}"
84 | echo "${EXEC_LINE}" >>"${ZSH_PROFILE_PATH}"
85 |
86 | if [ $OS_NAME = "Darwin" ]; then
87 | echo 'done. the "kill Dock" command can fix the crash issue.'
88 | else
89 | ln -sf "${MY_VMOPTIONS_SHELL_FILE}" "${KDE_ENV_DIR}/${MY_VMOPTIONS_SHELL_NAME}"
90 | echo "done. you'd better log off first!"
91 | fi
92 |
--------------------------------------------------------------------------------
/jetbra-server/src/main/java/win/novice/li/controller/LicenseController.java:
--------------------------------------------------------------------------------
1 | package win.novice.li.controller;
2 |
3 | import com.fasterxml.jackson.databind.ObjectMapper;
4 | import lombok.SneakyThrows;
5 | import org.bouncycastle.jce.provider.BouncyCastleProvider;
6 | import org.bouncycastle.openssl.PEMKeyPair;
7 | import org.bouncycastle.openssl.PEMParser;
8 | import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
9 | import org.springframework.core.io.ClassPathResource;
10 | import org.springframework.validation.annotation.Validated;
11 | import org.springframework.web.bind.annotation.PostMapping;
12 | import org.springframework.web.bind.annotation.RequestBody;
13 | import org.springframework.web.bind.annotation.RestController;
14 | import win.novice.li.model.License;
15 |
16 | import java.io.FileInputStream;
17 | import java.io.InputStreamReader;
18 | import java.nio.charset.StandardCharsets;
19 | import java.security.*;
20 | import java.security.cert.CertificateFactory;
21 | import java.security.cert.X509Certificate;
22 | import java.util.Base64;
23 | import java.util.HashMap;
24 | import java.util.Map;
25 |
26 | @RestController
27 | public class LicenseController {
28 | private static final PrivateKey PRIVATE_KEY = getPrivateKey();
29 | private static final X509Certificate CRT = getCertificate();
30 |
31 | private static final ObjectMapper MAPPER = new ObjectMapper();
32 |
33 | @PostMapping("/generateLicense")
34 | @SneakyThrows
35 | public Map generateLicense(@RequestBody @Validated License license) {
36 | Map ans = new HashMap<>();
37 |
38 | String licenseId = generateLicenseId();
39 | license.setLicenseId(licenseId);
40 |
41 | String licensePart = MAPPER.writeValueAsString(license);
42 | byte[] licensePartBytes = licensePart.getBytes(StandardCharsets.UTF_8);
43 | String licensePartBase64 = Base64.getEncoder().encodeToString(licensePartBytes);
44 |
45 |
46 | Signature signature = Signature.getInstance("SHA1withRSA");
47 | signature.initSign(PRIVATE_KEY);
48 | signature.update(licensePartBytes);
49 | byte[] signatureBytes = signature.sign();
50 | String sigResultsBase64 = Base64.getEncoder().encodeToString(signatureBytes);
51 |
52 | String result = licenseId + "-" + licensePartBase64 + "-" + sigResultsBase64 + "-" + Base64.getEncoder().encodeToString(CRT.getEncoded());
53 |
54 | ans.put("license",result);
55 | return ans;
56 | }
57 |
58 |
59 | private static final String ALLOWED_CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
60 | private static final SecureRandom random = new SecureRandom();
61 |
62 | public static String generateLicenseId() {
63 | int licenseLength = 10;
64 | StringBuilder sb = new StringBuilder(licenseLength);
65 | for (int i = 0; i < licenseLength; i++) {
66 | int randomIndex = random.nextInt(ALLOWED_CHARACTERS.length());
67 | char randomChar = ALLOWED_CHARACTERS.charAt(randomIndex);
68 | sb.append(randomChar);
69 | }
70 | return sb.toString();
71 | }
72 |
73 |
74 | @SneakyThrows
75 | static PrivateKey getPrivateKey() {
76 | ClassPathResource licenseKeyResource = new ClassPathResource("jetbra.key");
77 | Security.addProvider(new BouncyCastleProvider());
78 | PEMParser pemParser = new PEMParser(new InputStreamReader(licenseKeyResource.getInputStream()));
79 | JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
80 | Object object = pemParser.readObject();
81 | KeyPair kp = converter.getKeyPair((PEMKeyPair) object);
82 | return kp.getPrivate();
83 | }
84 |
85 | @SneakyThrows
86 | static X509Certificate getCertificate() {
87 | CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
88 | ClassPathResource crtResource = new ClassPathResource("jetbra.crt");
89 | return (X509Certificate) certificateFactory.generateCertificate(crtResource.getInputStream());
90 | }
91 | }
92 |
93 |
--------------------------------------------------------------------------------
/jetbra.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name JetBra
3 | // @namespace https://github.com/novice88/jetbra
4 | // @version 1.1
5 | // @license MIT
6 | // @description 添加一个按钮,点击获取插件的激活码
7 | // @author novice.li
8 | // @match https://plugins.jetbrains.com/plugin/*
9 | // @grant GM_setClipboard
10 | // @grant GM_addStyle
11 | // @grant GM_xmlhttpRequest
12 | // @connect noviceli.win
13 | // @connect self
14 | // @connect localhost
15 | // ==/UserScript==
16 |
17 | var elmGetter = function() {
18 | const win = window.unsafeWindow || document.defaultView || window;
19 | const doc = win.document;
20 | const listeners = new WeakMap();
21 | let mode = 'css';
22 | let $;
23 | const elProto = win.Element.prototype;
24 | const matches = elProto.matches ||
25 | elProto.matchesSelector ||
26 | elProto.webkitMatchesSelector ||
27 | elProto.mozMatchesSelector ||
28 | elProto.oMatchesSelector;
29 | const MutationObs = win.MutationObserver ||
30 | win.WebkitMutationObserver ||
31 | win.MozMutationObserver;
32 | function addObserver(target, callback) {
33 | const observer = new MutationObs(mutations => {
34 | for (const mutation of mutations) {
35 | if (mutation.type === 'attributes') {
36 | callback(mutation.target);
37 | if (observer.canceled) return;
38 | }
39 | for (const node of mutation.addedNodes) {
40 | if (node instanceof Element) callback(node);
41 | if (observer.canceled) return;
42 | }
43 | }
44 | });
45 | observer.canceled = false;
46 | observer.observe(target, {childList: true, subtree: true, attributes: true});
47 | return () => {
48 | observer.canceled = true;
49 | observer.disconnect();
50 | };
51 | }
52 | function addFilter(target, filter) {
53 | let listener = listeners.get(target);
54 | if (!listener) {
55 | listener = {
56 | filters: new Set(),
57 | remove: addObserver(target, el => listener.filters.forEach(f => f(el)))
58 | };
59 | listeners.set(target, listener);
60 | }
61 | listener.filters.add(filter);
62 | }
63 | function removeFilter(target, filter) {
64 | const listener = listeners.get(target);
65 | if (!listener) return;
66 | listener.filters.delete(filter);
67 | if (!listener.filters.size) {
68 | listener.remove();
69 | listeners.delete(target);
70 | }
71 | }
72 | function query(all, selector, parent, includeParent, curMode) {
73 | switch (curMode) {
74 | case 'css':
75 | const checkParent = includeParent && matches.call(parent, selector);
76 | if (all) {
77 | const queryAll = parent.querySelectorAll(selector);
78 | return checkParent ? [parent, ...queryAll] : [...queryAll];
79 | }
80 | return checkParent ? parent : parent.querySelector(selector);
81 | case 'jquery':
82 | let jNodes = $(includeParent ? parent : []);
83 | jNodes = jNodes.add([...parent.querySelectorAll('*')]).filter(selector);
84 | if (all) return $.map(jNodes, el => $(el));
85 | return jNodes.length ? $(jNodes.get(0)) : null;
86 | case 'xpath':
87 | const ownerDoc = parent.ownerDocument || parent;
88 | selector += '/self::*';
89 | if (all) {
90 | const xPathResult = ownerDoc.evaluate(selector, parent, null, 7, null);
91 | const result = [];
92 | for (let i = 0; i < xPathResult.snapshotLength; i++) {
93 | result.push(xPathResult.snapshotItem(i));
94 | }
95 | return result;
96 | }
97 | return ownerDoc.evaluate(selector, parent, null, 9, null).singleNodeValue;
98 | }
99 | }
100 | function isJquery(jq) {
101 | return jq && jq.fn && typeof jq.fn.jquery === 'string';
102 | }
103 | function getOne(selector, parent, timeout) {
104 | const curMode = mode;
105 | return new Promise(resolve => {
106 | const node = query(false, selector, parent, false, curMode);
107 | if (node) return resolve(node);
108 | let timer;
109 | const filter = el => {
110 | const node = query(false, selector, el, true, curMode);
111 | if (node) {
112 | removeFilter(parent, filter);
113 | timer && clearTimeout(timer);
114 | resolve(node);
115 | }
116 | };
117 | addFilter(parent, filter);
118 | if (timeout > 0) {
119 | timer = setTimeout(() => {
120 | removeFilter(parent, filter);
121 | resolve(null);
122 | }, timeout);
123 | }
124 | });
125 | }
126 | return {
127 | get currentSelector() {
128 | return mode;
129 | },
130 | get(selector, ...args) {
131 | let parent = typeof args[0] !== 'number' && args.shift() || doc;
132 | if (mode === 'jquery' && parent instanceof $) parent = parent.get(0);
133 | const timeout = args[0] || 0;
134 | if (Array.isArray(selector)) {
135 | return Promise.all(selector.map(s => getOne(s, parent, timeout)));
136 | }
137 | return getOne(selector, parent, timeout);
138 | },
139 | each(selector, ...args) {
140 | let parent = typeof args[0] !== 'function' && args.shift() || doc;
141 | if (mode === 'jquery' && parent instanceof $) parent = parent.get(0);
142 | const callback = args[0];
143 | const curMode = mode;
144 | const refs = new WeakSet();
145 | for (const node of query(true, selector, parent, false, curMode)) {
146 | refs.add(curMode === 'jquery' ? node.get(0) : node);
147 | if (callback(node, false) === false) return;
148 | }
149 | const filter = el => {
150 | for (const node of query(true, selector, el, true, curMode)) {
151 | const _el = curMode === 'jquery' ? node.get(0) : node;
152 | if (refs.has(_el)) break;
153 | refs.add(_el);
154 | if (callback(node, true) === false) {
155 | return removeFilter(parent, filter);
156 | }
157 | }
158 | };
159 | addFilter(parent, filter);
160 | },
161 | create(domString, ...args) {
162 | const returnList = typeof args[0] === 'boolean' && args.shift();
163 | const parent = args[0];
164 | const template = doc.createElement('template');
165 | template.innerHTML = domString;
166 | const node = template.content.firstElementChild;
167 | if (!node) return null;
168 | parent ? parent.appendChild(node) : node.remove();
169 | if (returnList) {
170 | const list = {};
171 | node.querySelectorAll('[id]').forEach(el => list[el.id] = el);
172 | list[0] = node;
173 | return list;
174 | }
175 | return node;
176 | },
177 | selector(desc) {
178 | switch (true) {
179 | case isJquery(desc):
180 | $ = desc;
181 | return mode = 'jquery';
182 | case !desc || typeof desc.toLowerCase !== 'function':
183 | return mode = 'css';
184 | case desc.toLowerCase() === 'jquery':
185 | for (const jq of [window.jQuery, window.$, win.jQuery, win.$]) {
186 | if (isJquery(jq)) {
187 | $ = jq;
188 | break;
189 | };
190 | }
191 | return mode = $ ? 'jquery' : 'css';
192 | case desc.toLowerCase() === 'xpath':
193 | return mode = 'xpath';
194 | default:
195 | return mode = 'css';
196 | }
197 | }
198 | };
199 | }();
200 | (async function () {
201 | 'use strict';
202 | GM_addStyle(`
203 | .jetbra-button {
204 | background-color: #04AA6D;
205 | border: none;
206 | color: white;
207 | padding: 8px 24px;
208 | text-align: center;
209 | text-decoration: none;
210 | display: inline-block;
211 | border-radius: 16px;
212 | box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
213 | transition-duration: 0.4s;
214 | }
215 |
216 | .jetbra-button:hover {
217 | background-color: #057e47;
218 | color: white;
219 | }
220 | `);
221 | const backendBaseUrl = 'https://jetbra.noviceli.win'
222 | const metaTag = document.querySelector('meta[name="pluginId"]')
223 | if (!metaTag) {
224 | return
225 | }
226 | const pluginId = metaTag.getAttribute('content')
227 |
228 | let pluginDetail = await fetch('https://plugins.jetbrains.com/api/plugins/' + pluginId).then(r => r.json());
229 |
230 | const parentElement = await elmGetter.get('.plugin-header__controls-panel > div:first-child');
231 |
232 | let newElement = document.createElement('div');
233 | newElement.classList.toggle('wt-col-inline');
234 | newElement.innerHTML = ``;
235 | parentElement.appendChild(newElement)
236 |
237 |
238 | newElement.addEventListener('click', async () => {
239 | if (pluginDetail.purchaseInfo === undefined) {
240 | window.alert('此插件不是付费插件');
241 | return;
242 | }
243 | let data = {
244 | "licenseeName": "Test",
245 | "assigneeName": "novice.li",
246 | "assigneeEmail": "",
247 | "licenseRestriction": "",
248 | "checkConcurrentUse": false,
249 | "products": [{
250 | "code": pluginDetail.purchaseInfo.productCode,
251 | "fallbackDate": "2099-12-30",
252 | "paidUpTo": "2099-12-30",
253 | "extended": false
254 | }],
255 | "metadata": "0120230102PPAA013009",
256 | "hash": "41472961/0:1563609451",
257 | "gracePeriodDays": 7,
258 | "autoProlongated": true,
259 | "isAutoProlongated": true
260 | }
261 | GM_xmlhttpRequest({
262 | method: 'POST',
263 | url: backendBaseUrl + '/generateLicense',
264 | headers: {
265 | 'Content-Type': 'application/json'
266 | },
267 | data: JSON.stringify(data),
268 | onload: function (response) {
269 | let license = JSON.parse(response.responseText).license
270 | GM_setClipboard(license, 'text');
271 | window.alert('激活码已复制到剪切版');
272 | }
273 | });
274 | })
275 | })();
276 |
--------------------------------------------------------------------------------