Skip to content

Commit 6dc0328

Browse files
committed
Fix compatibility
1 parent ecafc51 commit 6dc0328

File tree

8 files changed

+437
-364
lines changed

8 files changed

+437
-364
lines changed

doc/LoginHelp.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@
1414
</p>
1515

1616
## browser login
17-
This login requires additional download of dependent files. Download the compressed package with the version number ** jcef ** at the [releases](https://github.com/shuzijun/leetcode-editor/releases) address. After downloading, decompress it to the path of JCEFFilePath shown on the configuration page.
18-
If there is a resource file in the path, this login method will be used first, but this method is not compatible. If it cannot be loaded normally, you need to delete the contents of the folder and log in using other methods.
17+
~~This login requires additional download of dependent files. Download the compressed package with the version number ** jcef ** at the [releases](https://github.com/shuzijun/leetcode-editor/releases) address. After downloading, decompress it to the path of JCEFFilePath shown on the configuration page.~~
18+
~~If there is a resource file in the path, this login method will be used first, but this method is not compatible. If it cannot be loaded normally, you need to delete the contents of the folder and log in using other methods.~~
19+
Starting from version 6.8, external mounting of JCEF is no longer supported. Instead, JCEF provided by JetBrains is used. The supported version is 2020.2+. If you meet the conditions of use, you can check JCEF in the configuration item.

doc/LoginHelp_ZH.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
</p>
1515

1616
## 浏览器登录
17-
此登录需要额外下载依赖文件,在地址[releases](https://github.com/shuzijun/leetcode-editor/releases)下载版号带有**jcef**的压缩包,
18-
下载后,解压到配置页展示的JCEFFilePath的路径中。
19-
如路径中存在资源文件,将首先使用此登录方式,但此方式兼容性差,如不能正常加载,需删除文件夹下内容,使用其他方式登录。
17+
~~此登录需要额外下载依赖文件,在地址[releases](https://github.com/shuzijun/leetcode-editor/releases)下载版号带有**jcef**的压缩包,~~
18+
~~下载后,解压到配置页展示的JCEFFilePath的路径中。 ~~
19+
~~如路径中存在资源文件,将首先使用此登录方式,但此方式兼容性差,如不能正常加载,需删除文件夹下内容,使用其他方式登录。~~
20+
从版本6.8开始,不再支持外部挂载JCEF,改用JetBrains提供的JCEF,支持版本为2020.2+,如果满足使用条件,可以在配置项中勾选JCEF

src/main/java/com/shuzijun/leetcode/plugin/actions/toolbar/LoginAction.java

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@
77
import com.shuzijun.leetcode.plugin.model.Config;
88
import com.shuzijun.leetcode.plugin.setting.PersistentConfig;
99
import com.shuzijun.leetcode.plugin.utils.*;
10-
import com.shuzijun.leetcode.plugin.window.LoginFrame;
11-
import com.shuzijun.leetcode.plugin.window.WindowFactory;
10+
import com.shuzijun.leetcode.plugin.window.*;
1211
import org.apache.commons.lang.StringUtils;
1312

1413
import javax.swing.*;
@@ -20,8 +19,8 @@
2019
*/
2120
public class LoginAction extends AbstractAction {
2221

23-
@Override
24-
public void actionPerformed(AnActionEvent anActionEvent, Config config) {
22+
@Override
23+
public void actionPerformed(AnActionEvent anActionEvent, Config config) {
2524

2625
JTree tree = WindowFactory.getDataContext(anActionEvent.getProject()).getData(DataKeys.LEETCODE_PROJECTS_TREE);
2726

@@ -61,22 +60,16 @@ public void actionPerformed(AnActionEvent anActionEvent, Config config) {
6160
}
6261
}
6362

64-
65-
if (URLUtils.leetcodecn.equals(URLUtils.getLeetcodeHost())) {
66-
if (!LoginFrame.httpLogin.ajaxLogin(config, tree, anActionEvent.getProject())) {
67-
ApplicationManager.getApplication().invokeAndWait(new Runnable() {
68-
@Override
69-
public void run() {
70-
LoginFrame loginFrame = new LoginFrame(anActionEvent.getProject(), tree);
71-
loginFrame.loadComponent();
72-
}
73-
});
74-
}
75-
} else {
63+
if (!HttpLogin.ajaxLogin(config, tree, anActionEvent.getProject())) {
7664
ApplicationManager.getApplication().invokeAndWait(new Runnable() {
7765
@Override
7866
public void run() {
79-
LoginFrame loginFrame = new LoginFrame(anActionEvent.getProject(), tree);
67+
LoginFrame loginFrame;
68+
if (HttpLogin.isSupportedJcef()) {
69+
loginFrame = new JcefLogin(anActionEvent.getProject(), tree);
70+
} else {
71+
loginFrame = new CookieLogin(anActionEvent.getProject(), tree);
72+
}
8073
loginFrame.loadComponent();
8174
}
8275
});

src/main/java/com/shuzijun/leetcode/plugin/setting/SettingUI.java

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
import com.intellij.ui.components.JBPasswordField;
1414
import com.intellij.ui.components.JBScrollPane;
1515
import com.intellij.ui.components.JBTextField;
16-
import com.intellij.ui.jcef.JBCefApp;
1716
import com.intellij.util.net.HttpConfigurable;
1817
import com.shuzijun.leetcode.plugin.listener.ColorListener;
1918
import com.shuzijun.leetcode.plugin.listener.DonateListener;
@@ -25,6 +24,7 @@
2524
import com.shuzijun.leetcode.plugin.utils.MTAUtils;
2625
import com.shuzijun.leetcode.plugin.utils.PropertiesUtils;
2726
import com.shuzijun.leetcode.plugin.utils.URLUtils;
27+
import com.shuzijun.leetcode.plugin.window.HttpLogin;
2828
import org.apache.commons.lang.StringUtils;
2929

3030
import javax.swing.*;
@@ -92,15 +92,8 @@ public void mouseClicked(MouseEvent e) {
9292
}
9393
}
9494
});
95-
Boolean jcefSupported;
96-
try {
97-
jcefSupported = JBCefApp.isSupported();
98-
}catch (Throwable e){
99-
jcefSupported = false;
100-
}
101-
if(!jcefSupported){
102-
proxyCheckBox.setEnabled(true);
103-
}
95+
96+
jcefCheckBox.setEnabled(!HttpLogin.isSupportedJcef());
10497

10598
templateConfigHelp.addMouseListener(new MouseAdapter() {
10699
@Override
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package com.shuzijun.leetcode.plugin.window;
2+
3+
import com.intellij.ide.BrowserUtil;
4+
import com.intellij.openapi.progress.ProgressIndicator;
5+
import com.intellij.openapi.progress.ProgressManager;
6+
import com.intellij.openapi.progress.Task;
7+
import com.intellij.openapi.project.Project;
8+
import com.intellij.openapi.ui.DialogWrapper;
9+
import com.intellij.ui.components.JBPanel;
10+
import com.intellij.ui.components.JBScrollPane;
11+
import com.shuzijun.leetcode.plugin.utils.HttpRequestUtils;
12+
import com.shuzijun.leetcode.plugin.utils.PropertiesUtils;
13+
import com.shuzijun.leetcode.plugin.utils.URLUtils;
14+
import org.apache.commons.lang.StringUtils;
15+
import org.jetbrains.annotations.NotNull;
16+
import org.jetbrains.annotations.Nullable;
17+
18+
import javax.swing.*;
19+
import java.awt.*;
20+
import java.awt.event.ActionEvent;
21+
import java.net.HttpCookie;
22+
import java.util.ArrayList;
23+
import java.util.List;
24+
25+
/**
26+
* @author shuzijun
27+
*/
28+
public class CookieLogin implements LoginFrame {
29+
private JTree tree;
30+
private Project project;
31+
32+
33+
public CookieLogin(Project project, JTree tree) {
34+
this.tree = tree;
35+
this.project = project;
36+
}
37+
38+
@Override
39+
public void loadComponent() {
40+
CookiePanel cookiePanel = new CookiePanel(project);
41+
if (cookiePanel.showAndGet()) {
42+
String cookiesString = cookiePanel.cookieText();
43+
if (StringUtils.isBlank(cookiesString)) {
44+
JOptionPane.showMessageDialog(null, "cookie is null");
45+
return;
46+
}
47+
final List<HttpCookie> cookieList = new ArrayList<>();
48+
String[] cookies = cookiesString.split(";");
49+
for (String cookieString : cookies) {
50+
String[] cookie = cookieString.trim().split("=");
51+
if (cookie.length >= 2) {
52+
try {
53+
HttpCookie basicClientCookie = new HttpCookie(cookie[0], cookie[1]);
54+
basicClientCookie.setDomain("." + URLUtils.getLeetcodeHost());
55+
basicClientCookie.setPath("/");
56+
cookieList.add(basicClientCookie);
57+
} catch (IllegalArgumentException ignore) {
58+
59+
}
60+
}
61+
}
62+
HttpRequestUtils.setCookie(cookieList);
63+
64+
ProgressManager.getInstance().run(new Task.Backgroundable(project, "leetcode.loginSuccess", false) {
65+
@Override
66+
public void run(@NotNull ProgressIndicator progressIndicator) {
67+
if (HttpRequestUtils.isLogin()) {
68+
HttpLogin.loginSuccess(tree, project, cookieList);
69+
} else {
70+
JOptionPane.showMessageDialog(null, PropertiesUtils.getInfo("login.failed"));
71+
}
72+
73+
}
74+
});
75+
}
76+
}
77+
78+
class CookiePanel extends DialogWrapper {
79+
80+
private JPanel jpanel;
81+
private JTextArea caseText;
82+
83+
public CookiePanel(Project project) {
84+
super(project, Boolean.TRUE);
85+
86+
jpanel = new JBPanel();
87+
jpanel.setLayout(new BorderLayout());
88+
caseText = new JTextArea();
89+
caseText.setLineWrap(true);
90+
caseText.setMinimumSize(new Dimension(400, 200));
91+
caseText.setPreferredSize(new Dimension(400, 200));
92+
jpanel.add(new JBScrollPane(caseText, JBScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JBScrollPane.HORIZONTAL_SCROLLBAR_NEVER), BorderLayout.CENTER);
93+
setModal(true);
94+
init();
95+
setTitle("Cookie login");
96+
}
97+
98+
@NotNull
99+
@Override
100+
protected Action getOKAction() {
101+
Action action = super.getOKAction();
102+
action.putValue(Action.NAME, "login");
103+
return action;
104+
}
105+
106+
@NotNull
107+
@Override
108+
protected Action[] createActions() {
109+
Action helpAction = new AbstractAction() {
110+
@Override
111+
public void actionPerformed(ActionEvent e) {
112+
BrowserUtil.browse("https://github.com/shuzijun/leetcode-editor/blob/master/doc/LoginHelp.md");
113+
}
114+
115+
};
116+
helpAction.putValue(Action.NAME, "help");
117+
Action[] actions = new Action[]{helpAction, this.getOKAction(), this.getCancelAction()};
118+
return actions;
119+
}
120+
121+
@Nullable
122+
@Override
123+
protected JComponent createCenterPanel() {
124+
return jpanel;
125+
}
126+
127+
public String cookieText() {
128+
return caseText.getText();
129+
}
130+
}
131+
}
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
package com.shuzijun.leetcode.plugin.window;
2+
3+
import com.alibaba.fastjson.JSONArray;
4+
import com.alibaba.fastjson.JSONObject;
5+
import com.intellij.openapi.application.ApplicationManager;
6+
import com.intellij.openapi.progress.ProgressIndicator;
7+
import com.intellij.openapi.progress.ProgressManager;
8+
import com.intellij.openapi.progress.Task;
9+
import com.intellij.openapi.project.Project;
10+
import com.shuzijun.leetcode.plugin.manager.ViewManager;
11+
import com.shuzijun.leetcode.plugin.model.Config;
12+
import com.shuzijun.leetcode.plugin.setting.PersistentConfig;
13+
import com.shuzijun.leetcode.plugin.utils.*;
14+
import org.apache.commons.lang.StringUtils;
15+
import org.apache.http.HttpEntity;
16+
import org.apache.http.entity.mime.MultipartEntityBuilder;
17+
import org.jetbrains.annotations.NotNull;
18+
19+
import javax.swing.*;
20+
import java.lang.reflect.Method;
21+
import java.net.HttpCookie;
22+
import java.util.List;
23+
24+
/**
25+
* @author shuzijun
26+
*/
27+
public class HttpLogin {
28+
public static boolean ajaxLogin(Config config, JTree tree, Project project) {
29+
30+
if (URLUtils.leetcode.equals(URLUtils.getLeetcodeHost())) {
31+
return Boolean.FALSE;
32+
}
33+
34+
if (StringUtils.isBlank(PersistentConfig.getInstance().getPassword())) {
35+
return Boolean.FALSE;
36+
}
37+
38+
try {
39+
HttpEntity ent = MultipartEntityBuilder.create()
40+
.addTextBody("csrfmiddlewaretoken", HttpRequestUtils.getToken())
41+
.addTextBody("login", config.getLoginName())
42+
.addTextBody("password", PersistentConfig.getInstance().getPassword())
43+
.addTextBody("next", "/problems")
44+
.build();
45+
HttpRequest httpRequest = HttpRequest.post(URLUtils.getLeetcodeLogin(), ent.getContentType().getValue());
46+
httpRequest.setBody(IOUtils.toString(ent.getContent(), "UTF-8"));
47+
httpRequest.addHeader("x-requested-with", "XMLHttpRequest");
48+
httpRequest.addHeader("accept", "*/*");
49+
HttpResponse response = HttpRequestUtils.executePost(httpRequest);
50+
51+
if (response == null) {
52+
MessageUtils.getInstance(project).showWarnMsg("warning", PropertiesUtils.getInfo("request.failed"));
53+
return Boolean.FALSE;
54+
}
55+
56+
String body = response.getBody();
57+
58+
if ((response.getStatusCode() == 200 || response.getStatusCode() == 302)) {
59+
if (StringUtils.isNotBlank(body) && body.startsWith("{")) {
60+
JSONObject jsonObject = JSONObject.parseObject(body);
61+
JSONArray jsonArray = jsonObject.getJSONObject("form").getJSONArray("errors");
62+
if (jsonArray.isEmpty()) {
63+
MessageUtils.getInstance(project).showInfoMsg("info", PropertiesUtils.getInfo("login.success"));
64+
examineEmail(project);
65+
ViewManager.loadServiceData(tree, project);
66+
return Boolean.TRUE;
67+
} else {
68+
MessageUtils.getInstance(project).showInfoMsg("info", StringUtils.join(jsonArray, ","));
69+
return Boolean.FALSE;
70+
}
71+
} else if (StringUtils.isBlank(body)) {
72+
MessageUtils.getInstance(project).showInfoMsg("info", PropertiesUtils.getInfo("login.success"));
73+
examineEmail(project);
74+
ViewManager.loadServiceData(tree, project);
75+
return Boolean.TRUE;
76+
} else {
77+
HttpRequestUtils.resetHttpclient();
78+
MessageUtils.getInstance(project).showInfoMsg("info", PropertiesUtils.getInfo("login.unknown"));
79+
SentryUtils.submitErrorReport(null, String.format("login.unknown:\nStatusCode:%s\nbody:%s", response.getStatusCode(), body));
80+
return Boolean.FALSE;
81+
}
82+
} else if (response.getStatusCode() == 400) {
83+
JSONObject jsonObject = JSONObject.parseObject(body);
84+
MessageUtils.getInstance(project).showInfoMsg("info", StringUtils.join(jsonObject.getJSONObject("form").getJSONArray("errors"), ","));
85+
return Boolean.FALSE;
86+
} else {
87+
HttpRequestUtils.resetHttpclient();
88+
MessageUtils.getInstance(project).showInfoMsg("info", PropertiesUtils.getInfo("login.unknown"));
89+
SentryUtils.submitErrorReport(null, String.format("login.unknown:\nStatusCode:%s\nbody:%s", response.getStatusCode(), body));
90+
return Boolean.FALSE;
91+
}
92+
} catch (Exception e) {
93+
LogUtils.LOG.error("登陆错误", e);
94+
MessageUtils.getInstance(project).showInfoMsg("info", PropertiesUtils.getInfo("login.failed"));
95+
return Boolean.FALSE;
96+
}
97+
}
98+
99+
public static void examineEmail(Project project) {
100+
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
101+
@Override
102+
public void run() {
103+
HttpRequest httpRequest = HttpRequest.post(URLUtils.getLeetcodeGraphql(), "application/json");
104+
try {
105+
httpRequest.setBody("{\"operationName\":\"user\",\"variables\":{},\"query\":\"query user {\\n user {\\n socialAccounts\\n username\\n emails {\\n email\\n primary\\n verified\\n __typename\\n }\\n phone\\n profile {\\n rewardStats\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}");
106+
httpRequest.addHeader("Accept", "application/json");
107+
HttpResponse response = HttpRequestUtils.executePost(httpRequest);
108+
if (response != null && response.getStatusCode() == 200) {
109+
110+
String body = response.getBody();
111+
112+
JSONArray jsonArray = JSONObject.parseObject(body).getJSONObject("data").getJSONObject("user").getJSONArray("emails");
113+
if (jsonArray != null && jsonArray.size() > 0) {
114+
for (int i = 0; i < jsonArray.size(); i++) {
115+
JSONObject object = jsonArray.getJSONObject(i);
116+
if (object.getBoolean("verified")) {
117+
return;
118+
}
119+
}
120+
121+
}
122+
MessageUtils.getInstance(project).showWarnMsg("info", PropertiesUtils.getInfo("user.email"));
123+
}
124+
} catch (Exception i) {
125+
LogUtils.LOG.error("验证邮箱错误");
126+
}
127+
}
128+
});
129+
}
130+
131+
public static void loginSuccess(JTree tree, Project project, List<HttpCookie> cookieList) {
132+
ProgressManager.getInstance().run(new Task.Backgroundable(project, "leetcode.loginSuccess", false) {
133+
@Override
134+
public void run(@NotNull ProgressIndicator progressIndicator) {
135+
Config config = PersistentConfig.getInstance().getInitConfig();
136+
config.addCookie(config.getUrl() + config.getLoginName(), CookieUtils.httpCookieToJSONString(cookieList));
137+
PersistentConfig.getInstance().setInitConfig(config);
138+
MessageUtils.getInstance(project).showInfoMsg("info", PropertiesUtils.getInfo("login.success"));
139+
ViewManager.loadServiceData(tree, project);
140+
examineEmail(project);
141+
}
142+
});
143+
}
144+
145+
public static boolean isSupportedJcef() {
146+
try {
147+
Class<?> JBCefAppClass = Class.forName("com.intellij.ui.jcef.JBCefApp");
148+
Method method = JBCefAppClass.getMethod("isSupported");
149+
boolean supported = (boolean) method.invoke(null);
150+
151+
Config config = PersistentConfig.getInstance().getInitConfig();
152+
return config.getJcef() && supported;
153+
} catch (Throwable e) {
154+
return Boolean.FALSE;
155+
}
156+
}
157+
158+
}

0 commit comments

Comments
 (0)