Compare commits
1624 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
01039d0b47 | ||
|
|
7c1e5a3cba | ||
|
|
038161527f | ||
|
|
b12b7a17e6 | ||
|
|
37cf23d5fe | ||
|
|
ed7fb4f6e3 | ||
|
|
5d4bd2fee6 | ||
|
|
aae5906311 | ||
|
|
4a7bafd011 | ||
|
|
626ebfe65d | ||
|
|
3cc75cd46d | ||
|
|
f809ee7b20 | ||
|
|
1295d8191c | ||
|
|
bc2adbfa77 | ||
|
|
9583dff176 | ||
|
|
5732b84a7b | ||
|
|
c0d27504ac | ||
|
|
3db1dd7bbb | ||
|
|
5392766c5e | ||
|
|
730b7dea37 | ||
|
|
02a13ce028 | ||
|
|
7884853098 | ||
|
|
e122ea8146 | ||
|
|
f500d2b9f4 | ||
|
|
79e53bf1f5 | ||
|
|
31de7ec094 | ||
|
|
2e4501187c | ||
|
|
4430c9bd74 | ||
|
|
cf8be85ff7 | ||
|
|
0759be1223 | ||
|
|
21f8ddcf9f | ||
|
|
37cba5ee34 | ||
|
|
ff642fd1ac | ||
|
|
647f1d9c8b | ||
|
|
d886195ee3 | ||
|
|
e2b8f4f89a | ||
|
|
1dcfe661e9 | ||
|
|
d6dd110781 | ||
|
|
a0f956c885 | ||
|
|
88bcafed97 | ||
|
|
6e4f15ab52 | ||
|
|
20cae1ff4d | ||
|
|
dcf621b822 | ||
|
|
eafa20032b | ||
|
|
7375e1a490 | ||
|
|
199d87ba84 | ||
|
|
b43975ebfc | ||
|
|
fe7314c978 | ||
|
|
d4eb8e59a6 | ||
|
|
8ee00907b7 | ||
|
|
25fddc3c71 | ||
|
|
c78b733850 | ||
|
|
a57c83125e | ||
|
|
247b59985f | ||
|
|
7470b8b6d3 | ||
|
|
776d37b7aa | ||
|
|
8f532f8468 | ||
|
|
472963fe2d | ||
|
|
6a9b62ab9a | ||
|
|
9ac8aa2969 | ||
|
|
838bd2c794 | ||
|
|
b2bbe432e0 | ||
|
|
b3d8042452 | ||
|
|
0c758a7fdc | ||
|
|
85cb1d1c92 | ||
|
|
2cd2e8894d | ||
|
|
abb58379b3 | ||
|
|
4f48e8b190 | ||
|
|
2b5cbb5e74 | ||
|
|
23dd140921 | ||
|
|
8c8d7bda64 | ||
|
|
a6e246948a | ||
|
|
c49ba735a0 | ||
|
|
1a33c598e8 | ||
|
|
a4bbdb49de | ||
|
|
cf3846fbfd | ||
|
|
d46943eedf | ||
|
|
3bc17bd50a | ||
|
|
48a159f4c8 | ||
|
|
3d1bcffdc5 | ||
|
|
45fa0f94d2 | ||
|
|
ed16b7de4a | ||
|
|
f2ec03c7ec | ||
|
|
11db87f1e6 | ||
|
|
a1feaf33e0 | ||
|
|
53a99e3f79 | ||
|
|
e7f04f55c2 | ||
|
|
6653ea12b7 | ||
|
|
a28cef5b98 | ||
|
|
78a28fbdb3 | ||
|
|
bf8bbbdcb0 | ||
|
|
83ad83b135 | ||
|
|
dbd4f55981 | ||
|
|
5f5d7172ee | ||
|
|
fbd4557b44 | ||
|
|
5d55a55754 | ||
|
|
84032aec33 | ||
|
|
a372d8902e | ||
|
|
c38c62e4c3 | ||
|
|
8aceff7480 | ||
|
|
736c450161 | ||
|
|
3b63a3d308 | ||
|
|
4f56174c8f | ||
|
|
4d2eb324f1 | ||
|
|
3f2ab8ddcb | ||
|
|
fd8f863c5b | ||
|
|
246f1d7df0 | ||
|
|
ccacda9bf5 | ||
|
|
5494d63878 | ||
|
|
c32b9812a7 | ||
|
|
9eb9898b61 | ||
|
|
834e05999f | ||
|
|
b4c37d9906 | ||
|
|
9326b450d7 | ||
|
|
019ee8b1ba | ||
|
|
f043645397 | ||
|
|
3b173f0b3e | ||
|
|
f36c06389d | ||
|
|
d3a0b44247 | ||
|
|
e4d3a98aa8 | ||
|
|
f685682214 | ||
|
|
1c6323315b | ||
|
|
1ff4839be1 | ||
|
|
558e5bb340 | ||
|
|
48a9d208e6 | ||
|
|
f29e1f8c45 | ||
|
|
5d2bc88bb9 | ||
|
|
298fdb1191 | ||
|
|
59b34688ea | ||
|
|
5ce0bb6e4a | ||
|
|
487b1ab182 | ||
|
|
5c144a8ba3 | ||
|
|
ad344356df | ||
|
|
a55c65374d | ||
|
|
28447a9d43 | ||
|
|
0c03550c62 | ||
|
|
6d9a84803f | ||
|
|
03b0e4e2bb | ||
|
|
a00e9a6f5e | ||
|
|
ba17f8fde9 | ||
|
|
01d35456bd | ||
|
|
672b8c48ac | ||
|
|
ac1a357740 | ||
|
|
504f8d09a6 | ||
|
|
89ce7c23c9 | ||
|
|
a5d99b1eb5 | ||
|
|
800d193acb | ||
|
|
7a1d12be76 | ||
|
|
1b9c95e801 | ||
|
|
9f44815470 | ||
|
|
ca38239bce | ||
|
|
d9c22de6b8 | ||
|
|
49a3c84fc5 | ||
|
|
5e0c28438b | ||
|
|
a6dc801bc4 | ||
|
|
22009d1b71 | ||
|
|
50f39dc40e | ||
|
|
05d446ed37 | ||
|
|
7a0b068642 | ||
|
|
c0ef8c09af | ||
|
|
a2db6dd468 | ||
|
|
471bc0f65d | ||
|
|
80f840a7c2 | ||
|
|
a2413fdf23 | ||
|
|
9cbe2b938c | ||
|
|
e915726c52 | ||
|
|
b11e68cfd8 | ||
|
|
bee66d06dd | ||
|
|
945a0add96 | ||
|
|
294cabcf05 | ||
|
|
22eb993ebf | ||
|
|
b5e1a297ae | ||
|
|
499a16feae | ||
|
|
1866a59d12 | ||
|
|
10513e0f3b | ||
|
|
2e32de2fbb | ||
|
|
30a838df77 | ||
|
|
7cf9b9f57e | ||
|
|
736d995d4c | ||
|
|
e335b2c0d6 | ||
|
|
96ae5517f6 | ||
|
|
fb4b8d923a | ||
|
|
2ade705e51 | ||
|
|
e0086b4b79 | ||
|
|
8f6d443104 | ||
|
|
5dbce16895 | ||
|
|
1016dcb3d1 | ||
|
|
ba5ad12e13 | ||
|
|
57f9c8158e | ||
|
|
b8a0293b52 | ||
|
|
59b7daaef5 | ||
|
|
d781c768e9 | ||
|
|
04efed2079 | ||
|
|
2de9e14c7d | ||
|
|
a849dec6c2 | ||
|
|
28019dc511 | ||
|
|
9d638968a9 | ||
|
|
f921ff7d77 | ||
|
|
e3ea329795 | ||
|
|
87f1c129b6 | ||
|
|
a1c8bc0e61 | ||
|
|
61e9101851 | ||
|
|
a6289347cc | ||
|
|
9e84de8e76 | ||
|
|
d04cc53152 | ||
|
|
1ff3c5fc07 | ||
|
|
3db047a12f | ||
|
|
bf4615f2f2 | ||
|
|
798c7fad9a | ||
|
|
5797b37262 | ||
|
|
ce22f34cd6 | ||
|
|
4c49e52e26 | ||
|
|
47318b5d70 | ||
|
|
d12297909c | ||
|
|
5fb4edae2d | ||
|
|
c4b490e46d | ||
|
|
45febe3fff | ||
|
|
689a81a985 | ||
|
|
28620b385a | ||
|
|
be13446e69 | ||
|
|
ee57d5b8e6 | ||
|
|
4eda3dd8fa | ||
|
|
911dc7f90e | ||
|
|
18005b96e8 | ||
|
|
a3e45d206e | ||
|
|
0accf262dc | ||
|
|
e0eb73bb0a | ||
|
|
258e822c13 | ||
|
|
4f05b93d63 | ||
|
|
bb661d4f50 | ||
|
|
201cfaa922 | ||
|
|
c339aa349c | ||
|
|
046421f345 | ||
|
|
3a2c9e7aaa | ||
|
|
0bb6e6bd86 | ||
|
|
693e8ab157 | ||
|
|
033c16c992 | ||
|
|
6ec61433fb | ||
|
|
072f773245 | ||
|
|
0086f65a96 | ||
|
|
281f14f47e | ||
|
|
9fd20ff001 | ||
|
|
7df90a6034 | ||
|
|
019869ec28 | ||
|
|
b7f4fd7469 | ||
|
|
1273d2aee1 | ||
|
|
88990b4828 | ||
|
|
2f02c2970c | ||
|
|
ade789c6d4 | ||
|
|
9738f90970 | ||
|
|
6ed0741339 | ||
|
|
e6b1e22245 | ||
|
|
6b922be0c6 | ||
|
|
6e35a260e8 | ||
|
|
1106fd8cf1 | ||
|
|
fb92b90d5c | ||
|
|
5effbee50b | ||
|
|
9bc50a9f34 | ||
|
|
2a5a339c27 | ||
|
|
78d182fff3 | ||
|
|
0efb0b5e3e | ||
|
|
a2e8755730 | ||
|
|
06ddedbc4c | ||
|
|
fa148cdf42 | ||
|
|
c1f71dee15 | ||
|
|
871cb81323 | ||
|
|
34f5c0f910 | ||
|
|
3fafc6de93 | ||
|
|
b013213745 | ||
|
|
40ea454a7c | ||
|
|
f0bac4b4c8 | ||
|
|
ffb38129e2 | ||
|
|
3c550c094a | ||
|
|
2d143687b8 | ||
|
|
1e7284f141 | ||
|
|
50bdf0927c | ||
|
|
10a60b26fa | ||
|
|
b3c2084b76 | ||
|
|
3bf2dc711d | ||
|
|
5a7836115e | ||
|
|
0da9cb45bd | ||
|
|
28aa954f8c | ||
|
|
4fc71fb77e | ||
|
|
5ea6b1e08a | ||
|
|
0cdfc97460 | ||
|
|
3dd54312e7 | ||
|
|
a866017b4c | ||
|
|
394b657fc9 | ||
|
|
50449df08d | ||
|
|
fe3836be14 | ||
|
|
055cd62dd8 | ||
|
|
4d1f7fa60c | ||
|
|
5c0fba8744 | ||
|
|
b74ddc0b43 | ||
|
|
d004c6860e | ||
|
|
fc3ba6c030 | ||
|
|
9c4dc185be | ||
|
|
2edbbc523a | ||
|
|
a5122b656d | ||
|
|
74f980aab1 | ||
|
|
35e5475255 | ||
|
|
fb649c04cf | ||
|
|
4ce7ca3c6f | ||
|
|
eebb6735aa | ||
|
|
e96c9abd69 | ||
|
|
fd7cf164ff | ||
|
|
24afe8bde4 | ||
|
|
9540669f56 | ||
|
|
c6caf29b5b | ||
|
|
2698137dea | ||
|
|
bdae08b13b | ||
|
|
019c69ecff | ||
|
|
6b99b7eec5 | ||
|
|
4eb443e547 | ||
|
|
e530789739 | ||
|
|
7618f9f7d4 | ||
|
|
d7bde77977 | ||
|
|
a0286ff810 | ||
|
|
b172b03927 | ||
|
|
a556bf9487 | ||
|
|
4f5362fdc4 | ||
|
|
176a91e7c5 | ||
|
|
3c45ef624a | ||
|
|
fea7c9fbd7 | ||
|
|
aa6b6e3849 | ||
|
|
f5956e7bf0 | ||
|
|
b2669103dc | ||
|
|
53e19ecb1d | ||
|
|
3e74bb65bd | ||
|
|
f60575b77c | ||
|
|
7a839063d0 | ||
|
|
4ccc7aa92d | ||
|
|
d5c6a42a9a | ||
|
|
3bdef4d6d8 | ||
|
|
f40eb724d7 | ||
|
|
4d84eede56 | ||
|
|
53a2fbd0ff | ||
|
|
409fe5290e | ||
|
|
75d86cf883 | ||
|
|
b33f536dbd | ||
|
|
1f77c56eaf | ||
|
|
fe895b1b27 | ||
|
|
20bb263b06 | ||
|
|
3ecbd3bc10 | ||
|
|
fe81b51dcb | ||
|
|
90ba80436d | ||
|
|
2bb5f6afc4 | ||
|
|
3971318ffb | ||
|
|
5fbeb4b0fb | ||
|
|
1b2b838bc0 | ||
|
|
8e28cc01b8 | ||
|
|
77a59e9107 | ||
|
|
1411643192 | ||
|
|
fde2a768cf | ||
|
|
b008a58cf8 | ||
|
|
8e02394ff4 | ||
|
|
60a75d9a31 | ||
|
|
24566901fb | ||
|
|
cc428618e5 | ||
|
|
4d330cedac | ||
|
|
2503583498 | ||
|
|
1b11916e1e | ||
|
|
3dfd557265 | ||
|
|
cd6bea28b6 | ||
|
|
054f0f2bc7 | ||
|
|
97347674e7 | ||
|
|
36d15852aa | ||
|
|
4813610492 | ||
|
|
243dbab06a | ||
|
|
073eaa9a26 | ||
|
|
cb1f936d05 | ||
|
|
a7f3a7b1a7 | ||
|
|
71eb5f0813 | ||
|
|
b4f50258a7 | ||
|
|
346a9c5fcc | ||
|
|
61635db5b5 | ||
|
|
4c0a59a715 | ||
|
|
aa829a66ea | ||
|
|
885f193a00 | ||
|
|
0a9bbf526d | ||
|
|
ff6716b39d | ||
|
|
8505f2db96 | ||
|
|
233d605256 | ||
|
|
c4e01d20a0 | ||
|
|
07156cb55c | ||
|
|
8361b4e4a0 | ||
|
|
bb90671979 | ||
|
|
beddc71ed8 | ||
|
|
d5f1cc99ac | ||
|
|
d03a86292e | ||
|
|
c061a24948 | ||
|
|
9c7446f820 | ||
|
|
8be1730719 | ||
|
|
7ceaf5c071 | ||
|
|
7901a59aa4 | ||
|
|
8343a1002f | ||
|
|
c83dce3cc0 | ||
|
|
14afeab2bb | ||
|
|
c22b57927c | ||
|
|
81b84d235c | ||
|
|
488e8aab00 | ||
|
|
fc43a9a726 | ||
|
|
31947fdcb3 | ||
|
|
34c7963d28 | ||
|
|
d91b0afb9a | ||
|
|
82eb3fd6bd | ||
|
|
3d3d3f83df | ||
|
|
6879c75bc8 | ||
|
|
b02ad6cdab | ||
|
|
f7b16952ea | ||
|
|
f0d05a7d4e | ||
|
|
d6ca317b20 | ||
|
|
2879fddfd9 | ||
|
|
b6c09470fc | ||
|
|
9fdf6c6c32 | ||
|
|
79085af994 | ||
|
|
d189f4b443 | ||
|
|
c9d65e5cd9 | ||
|
|
639d62588a | ||
|
|
6c9db51fd5 | ||
|
|
f0dbb6b22c | ||
|
|
f10f7b6268 | ||
|
|
48a5cbc552 | ||
|
|
6721d150e0 | ||
|
|
54c16cad7d | ||
|
|
ae3ab15245 | ||
|
|
d3c0f50fec | ||
|
|
43753b1b7a | ||
|
|
6f3e4b3682 | ||
|
|
b57cdd31bd | ||
|
|
b936c194e4 | ||
|
|
064431421a | ||
|
|
9d49c7aad0 | ||
|
|
a6f27e5071 | ||
|
|
8d1d10b783 | ||
|
|
61bea05f63 | ||
|
|
bbe7c7b884 | ||
|
|
a432852b78 | ||
|
|
770e8b8cfa | ||
|
|
7faabdc375 | ||
|
|
a9860418ba | ||
|
|
30ff9d0ea9 | ||
|
|
bbc2298939 | ||
|
|
3286e8e24d | ||
|
|
372f3991e1 | ||
|
|
9aa5c0d135 | ||
|
|
9e9808e489 | ||
|
|
3dd75b17cf | ||
|
|
2504b4737b | ||
|
|
8ff04dca0d | ||
|
|
d893ee4829 | ||
|
|
dfc5ec0705 | ||
|
|
8023eb74c9 | ||
|
|
bb4d3997ad | ||
|
|
7ede3af762 | ||
|
|
97ea1c7a9e | ||
|
|
43bb2c0fb8 | ||
|
|
6e7196bb27 | ||
|
|
87a71d58e8 | ||
|
|
32ffd43fe3 | ||
|
|
6500c8d85e | ||
|
|
9866d436da | ||
|
|
0f4884d9d8 | ||
|
|
5a81441351 | ||
|
|
e8721bfb6b | ||
|
|
1b09d95209 | ||
|
|
35f3b5a50e | ||
|
|
ff5203a561 | ||
|
|
c3c1ced309 | ||
|
|
08b1c8ec83 | ||
|
|
1dd3ee4e0a | ||
|
|
06d0c6517d | ||
|
|
4938ce6364 | ||
|
|
25f3fc354f | ||
|
|
ad1a1f8015 | ||
|
|
5c070e2ca8 | ||
|
|
70ea21fca2 | ||
|
|
bc3593871b | ||
|
|
355a424be2 | ||
|
|
0ffa9a0cc8 | ||
|
|
72fecb2b9a | ||
|
|
4e9dfe5478 | ||
|
|
c79e2e3ad4 | ||
|
|
7c33c1c322 | ||
|
|
ea32f75925 | ||
|
|
7eaed21b9a | ||
|
|
cfe8fcd28d | ||
|
|
9f4718af70 | ||
|
|
39faccfd0b | ||
|
|
7545763dae | ||
|
|
c2928be35d | ||
|
|
449bb40d48 | ||
|
|
4caf1a1e63 | ||
|
|
01b205e6f1 | ||
|
|
160d3843a6 | ||
|
|
5efb784115 | ||
|
|
acde5b8384 | ||
|
|
373ee6586a | ||
|
|
215308c329 | ||
|
|
478cadba5e | ||
|
|
f4af4da791 | ||
|
|
22d4f435de | ||
|
|
ccb8cd5c04 | ||
|
|
e963f9e349 | ||
|
|
e3c2a4b8da | ||
|
|
324f46cdad | ||
|
|
691b19b5fd | ||
|
|
63ed2311dc | ||
|
|
d59e59ca40 | ||
|
|
c9a278b4a2 | ||
|
|
8ee5304148 | ||
|
|
3beaa8145f | ||
|
|
e30c55a0af | ||
|
|
b583590a64 | ||
|
|
a28c63168a | ||
|
|
620422350f | ||
|
|
b77cc3c33b | ||
|
|
71bf9b4887 | ||
|
|
9039d401da | ||
|
|
74a0a93201 | ||
|
|
522571f0b3 | ||
|
|
123c49c22d | ||
|
|
1ff88d29be | ||
|
|
63d5a2a1be | ||
|
|
1e9a6cb06b | ||
|
|
31748aa660 | ||
|
|
d0c6ea6a63 | ||
|
|
7ffe286a56 | ||
|
|
b99b30163b | ||
|
|
83b4f1e660 | ||
|
|
b57ba6a98b | ||
|
|
d748e6eff4 | ||
|
|
315d4b75b2 | ||
|
|
31267cbc33 | ||
|
|
c23379b3b6 | ||
|
|
568144d6a2 | ||
|
|
5857042963 | ||
|
|
28e41dc621 | ||
|
|
baef3b364b | ||
|
|
4b9ddb803f | ||
|
|
c07c7ad82f | ||
|
|
e223b80b95 | ||
|
|
5fe468fa1b | ||
|
|
60068d8d16 | ||
|
|
b6c5b46afe | ||
|
|
124cbfadb4 | ||
|
|
8d21f9b900 | ||
|
|
398dbbd2e5 | ||
|
|
e9b392d1c0 | ||
|
|
9d7c7e3225 | ||
|
|
855fd4f0b7 | ||
|
|
807839929d | ||
|
|
870955fee1 | ||
|
|
e0cea929ea | ||
|
|
315f4c35f0 | ||
|
|
d961ea22a6 | ||
|
|
5c0c07c78f | ||
|
|
bba93a0fb7 | ||
|
|
5683df2fc0 | ||
|
|
b5cb9ce67e | ||
|
|
06a32dc895 | ||
|
|
dff12a8a3a | ||
|
|
dc3f07ee84 | ||
|
|
1aef49ee11 | ||
|
|
ee3159b00e | ||
|
|
86d2c307f1 | ||
|
|
7823a217b4 | ||
|
|
2004df8337 | ||
|
|
13bc2d0340 | ||
|
|
edbc850346 | ||
|
|
901d3c85ca | ||
|
|
abbe83f3c2 | ||
|
|
bb3a04a9c8 | ||
|
|
bac13e8b71 | ||
|
|
3871681de3 | ||
|
|
ae6f2b6df6 | ||
|
|
f59bbc9981 | ||
|
|
eac0c84e11 | ||
|
|
f814cc443d | ||
|
|
262466303b | ||
|
|
3b4cc2c5f2 | ||
|
|
023d3fbf6f | ||
|
|
fe01f290bc | ||
|
|
5805ac9f7f | ||
|
|
4e2f38a343 | ||
|
|
572c924e5c | ||
|
|
e90353e550 | ||
|
|
f8ae1b8c49 | ||
|
|
186d995919 | ||
|
|
70584aff9d | ||
|
|
dcdc63785a | ||
|
|
0e7bfa65de | ||
|
|
9427340ab7 | ||
|
|
63af5bae8a | ||
|
|
9232f1fa40 | ||
|
|
15bdb551f4 | ||
|
|
2cda2b53ed | ||
|
|
028c9ea0b5 | ||
|
|
315b51e7ca | ||
|
|
0185b3b145 | ||
|
|
afaad49879 | ||
|
|
4b2b45979b | ||
|
|
66e40edd0e | ||
|
|
3d5168885f | ||
|
|
6eee2c2342 | ||
|
|
11ec27147a | ||
|
|
a522a02ba2 | ||
|
|
5ff4d35a30 | ||
|
|
4c5546bf52 | ||
|
|
ab37a46e83 | ||
|
|
0b3635b5c5 | ||
|
|
2a338d9a83 | ||
|
|
7031a4c26c | ||
|
|
2c5ca97476 | ||
|
|
fc0c8f6bb1 | ||
|
|
643547704e | ||
|
|
4717b63775 | ||
|
|
4af148f480 | ||
|
|
a9c59693ee | ||
|
|
9b3ac159c1 | ||
|
|
c03c98157f | ||
|
|
74149b761f | ||
|
|
44cfa2d8dc | ||
|
|
b0fb00d597 | ||
|
|
af3c1dc039 | ||
|
|
1c94b5d5f0 | ||
|
|
fbb00c0f85 | ||
|
|
4a72e63d1c | ||
|
|
ce94e0ba4d | ||
|
|
e2c836794b | ||
|
|
05e424f805 | ||
|
|
bf30819a4a | ||
|
|
52e9d30b46 | ||
|
|
1fa2d55e1d | ||
|
|
4d0ee652d8 | ||
|
|
ff215bc9aa | ||
|
|
e1cadc878a | ||
|
|
57d9d8ddb9 | ||
|
|
f76b1a5363 | ||
|
|
9b579be2be | ||
|
|
9d43465eab | ||
|
|
fc52870431 | ||
|
|
c466ea100a | ||
|
|
4054369611 | ||
|
|
11106937a3 | ||
|
|
fdddf901f3 | ||
|
|
2e30c04238 | ||
|
|
bd838094cd | ||
|
|
6f22b74154 | ||
|
|
e71525db0e | ||
|
|
d7c1516ec2 | ||
|
|
ff91a5dad9 | ||
|
|
d76679c2b6 | ||
|
|
d7cb88f0a4 | ||
|
|
8a3eb41314 | ||
|
|
e2f9aac1c9 | ||
|
|
08c7396404 | ||
|
|
69cbdbd20e | ||
|
|
005f26ba7c | ||
|
|
a1dc0c8104 | ||
|
|
75435b8fe0 | ||
|
|
6d7385788e | ||
|
|
726d9d410f | ||
|
|
ee2a61bb61 | ||
|
|
40748d6e42 | ||
|
|
02225ad0b8 | ||
|
|
3c4703ad85 | ||
|
|
6762f35ade | ||
|
|
5135afcf8f | ||
|
|
4aae82e0cc | ||
|
|
ea95c6de79 | ||
|
|
4a67e963ed | ||
|
|
720f177d65 | ||
|
|
2b4a043a92 | ||
|
|
77b20bc562 | ||
|
|
063449448a | ||
|
|
559ffcbab5 | ||
|
|
80ed8346be | ||
|
|
a49455f330 | ||
|
|
c3de6f6d02 | ||
|
|
ee416b1bf2 | ||
|
|
784928ffc8 | ||
|
|
609360a1d0 | ||
|
|
18ce2f7b1c | ||
|
|
78625d82ae | ||
|
|
2ca34fb9ad | ||
|
|
f5f944aa50 | ||
|
|
83b4ab83d1 | ||
|
|
34f63f9006 | ||
|
|
b74188d904 | ||
|
|
ba246e99e8 | ||
|
|
51576b54c3 | ||
|
|
0d5a9fcf4a | ||
|
|
b0c5f74edb | ||
|
|
1ac7661593 | ||
|
|
caa2c523f4 | ||
|
|
7b9f1e6788 | ||
|
|
86f3ed29f9 | ||
|
|
655d411afe | ||
|
|
f5deb8e168 | ||
|
|
fcdb61bfbf | ||
|
|
bd9a4ca094 | ||
|
|
e2657e746d | ||
|
|
6f5a174231 | ||
|
|
91e9c0b02b | ||
|
|
658ea26ab7 | ||
|
|
77b736541e | ||
|
|
843b881da6 | ||
|
|
3ca8acc805 | ||
|
|
63809d4f16 | ||
|
|
557660b16b | ||
|
|
12be8bda52 | ||
|
|
81efd25e0a | ||
|
|
7037009ce4 | ||
|
|
4a0902d4c8 | ||
|
|
ed25ac1748 | ||
|
|
55e09ed062 | ||
|
|
babb548c22 | ||
|
|
1d0e8434ad | ||
|
|
e7b2deb941 | ||
|
|
2a37a6a28e | ||
|
|
735ebbe92a | ||
|
|
2031729656 | ||
|
|
f39fdc38cb | ||
|
|
6b62623615 | ||
|
|
f3f3dc1951 | ||
|
|
f4dd970f5e | ||
|
|
74332c58c5 | ||
|
|
f7ce49020f | ||
|
|
aaba1f7209 | ||
|
|
a9c7916b3d | ||
|
|
e3c8ccfafe | ||
|
|
2a22b65b2d | ||
|
|
ce5728e2df | ||
|
|
cba82e0e8e | ||
|
|
be30064f9a | ||
|
|
1798981380 | ||
|
|
06586d3405 | ||
|
|
31bcbd0e13 | ||
|
|
2652beed88 | ||
|
|
a2b87b862f | ||
|
|
85e8d9479e | ||
|
|
119508a4e3 | ||
|
|
3c2da0a225 | ||
|
|
ef79e6adf9 | ||
|
|
528400579a | ||
|
|
521ce1a487 | ||
|
|
014cc9bc16 | ||
|
|
34c6c953ac | ||
|
|
40fbbc7c58 | ||
|
|
534c329970 | ||
|
|
328d728257 | ||
|
|
fae6d42758 | ||
|
|
27693f6d23 | ||
|
|
89c4bab5b9 | ||
|
|
9d2ff04838 | ||
|
|
cf00243107 | ||
|
|
e8019ba7a5 | ||
|
|
4f30e3f0e3 | ||
|
|
047d08470f | ||
|
|
9643695389 | ||
|
|
5cabec86e7 | ||
|
|
f2d0e37255 | ||
|
|
9560851a3f | ||
|
|
4aeec1caa1 | ||
|
|
ae45b1ef44 | ||
|
|
aad1b087c9 | ||
|
|
b69c581fa1 | ||
|
|
9eb4d90cd6 | ||
|
|
958567c2c5 | ||
|
|
f1fc4583ed | ||
|
|
f7bfd5f766 | ||
|
|
aadc3c216a | ||
|
|
6fd2499daf | ||
|
|
f0405f1c5d | ||
|
|
1914cb3994 | ||
|
|
ab38fb572d | ||
|
|
46d7128a6e | ||
|
|
0821884ff3 | ||
|
|
62233281ef | ||
|
|
39504502af | ||
|
|
f97530a671 | ||
|
|
0fe7d5e598 | ||
|
|
527362dc9f | ||
|
|
1023426c8d | ||
|
|
4458fd7541 | ||
|
|
f0b03f59ff | ||
|
|
d5b17b3cfb | ||
|
|
9664d1d77f | ||
|
|
d329646a52 | ||
|
|
8eafe72bb0 | ||
|
|
c38a0bde65 | ||
|
|
eef5d3cc16 | ||
|
|
59ada594a5 | ||
|
|
5ec1d18143 | ||
|
|
fd1e1bb6bf | ||
|
|
5d545d8a85 | ||
|
|
e38bc45527 | ||
|
|
1b11425acd | ||
|
|
1e67850a80 | ||
|
|
975f89456f | ||
|
|
e267b4b379 | ||
|
|
2fd21690a5 | ||
|
|
b176ad03aa | ||
|
|
152c4802d0 | ||
|
|
8adbc57f23 | ||
|
|
d263a78db8 | ||
|
|
b94a065c06 | ||
|
|
bc957fea71 | ||
|
|
566f056149 | ||
|
|
abe484b0df | ||
|
|
982c865245 | ||
|
|
07d2a27b5f | ||
|
|
e4c65deda8 | ||
|
|
4930646e05 | ||
|
|
01dd1ff56f | ||
|
|
9433213fe5 | ||
|
|
82dea829f1 | ||
|
|
dafc83aa53 | ||
|
|
7ab1cd6612 | ||
|
|
d0e3b3ffbd | ||
|
|
50bcc88700 | ||
|
|
8fb8575f4b | ||
|
|
e63c113b8a | ||
|
|
43d892dbf7 | ||
|
|
d9bf31d4f0 | ||
|
|
0bde282448 | ||
|
|
6098132fdf | ||
|
|
a6bb8947f3 | ||
|
|
48880328b0 | ||
|
|
25776a788e | ||
|
|
f5e8f2831e | ||
|
|
b3fe13c97a | ||
|
|
7845569319 | ||
|
|
69b3178c13 | ||
|
|
1dce0f0366 | ||
|
|
db1b3fdaad | ||
|
|
032d12a592 | ||
|
|
94cc36a08f | ||
|
|
4fcae44fb7 | ||
|
|
83719dfe17 | ||
|
|
c8b01a5530 | ||
|
|
b469d73385 | ||
|
|
69468b1770 | ||
|
|
7e4f66d533 | ||
|
|
758d0d82d4 | ||
|
|
a01b934fdb | ||
|
|
e9b0372174 | ||
|
|
288c02e092 | ||
|
|
33322e8795 | ||
|
|
ec8f6478df | ||
|
|
59d397a3a0 | ||
|
|
b06540f2ec | ||
|
|
419f5458b4 | ||
|
|
ca4e8960d2 | ||
|
|
7b3bed015a | ||
|
|
c97fa3a767 | ||
|
|
42f7c7cb43 | ||
|
|
c8ae834f50 | ||
|
|
b8644268b3 | ||
|
|
75742d8a9e | ||
|
|
6b6169e248 | ||
|
|
3c4ae902fd | ||
|
|
bfc8bf91ee | ||
|
|
768d5cce27 | ||
|
|
d54a1c2754 | ||
|
|
282ad3ab2d | ||
|
|
c692dd3a66 | ||
|
|
e461fce2cb | ||
|
|
4d92caabf8 | ||
|
|
07c07ec60a | ||
|
|
4020213729 | ||
|
|
b9beb4be9d | ||
|
|
f747416d3b | ||
|
|
41241b694f | ||
|
|
26c5c1ac01 | ||
|
|
dedd5b197c | ||
|
|
6340764a6c | ||
|
|
a262c1e98d | ||
|
|
7b9c64c5a5 | ||
|
|
4283404f35 | ||
|
|
f612ce5db8 | ||
|
|
831a20ff91 | ||
|
|
38ae669070 | ||
|
|
fed7e7764e | ||
|
|
f3804fce40 | ||
|
|
88e7cd09d7 | ||
|
|
1ce5571ac6 | ||
|
|
355bf84af4 | ||
|
|
6e8a3a4867 | ||
|
|
bf6dcdf2bd | ||
|
|
165454dc57 | ||
|
|
34b4b9d099 | ||
|
|
0808ec1612 | ||
|
|
6b12549cd9 | ||
|
|
92baf9025a | ||
|
|
d58861ab88 | ||
|
|
142230e56a | ||
|
|
b8959c8f12 | ||
|
|
3fa22fdc55 | ||
|
|
a1c743e59c | ||
|
|
ebd0f45c10 | ||
|
|
fce6c46250 | ||
|
|
6114e4ff55 | ||
|
|
cc3d1ff7de | ||
|
|
2f721c8535 | ||
|
|
ddb790104f | ||
|
|
181dfd2f51 | ||
|
|
fca062f75a | ||
|
|
52b84c3201 | ||
|
|
e1021a6832 | ||
|
|
aa338d2da5 | ||
|
|
44471c4926 | ||
|
|
930c7e4ce5 | ||
|
|
2e64f23ef9 | ||
|
|
801d1fd320 | ||
|
|
cfffb5756e | ||
|
|
b30daf8e56 | ||
|
|
84eba671f0 | ||
|
|
f28b93daa0 | ||
|
|
dc16136c59 | ||
|
|
e47caf8c4f | ||
|
|
8ad80bb75d | ||
|
|
22475e459d | ||
|
|
73ad33dfc9 | ||
|
|
f1782c78f4 | ||
|
|
b544dde2c6 | ||
|
|
1a7dca8e58 | ||
|
|
23c7d7fb46 | ||
|
|
65fda407dc | ||
|
|
12ebad436a | ||
|
|
c7297dba7e | ||
|
|
e61368a26e | ||
|
|
b98da3a5dc | ||
|
|
12f3400894 | ||
|
|
4b5508fc3c | ||
|
|
5f40e6e0b7 | ||
|
|
080d660cfa | ||
|
|
790209efbc | ||
|
|
f1679e444c | ||
|
|
9dc6ba182a | ||
|
|
785a30e623 | ||
|
|
f09efdad66 | ||
|
|
4e73b3ae28 | ||
|
|
6213f86f81 | ||
|
|
0a6955cd59 | ||
|
|
6ee8f03ec0 | ||
|
|
b1a82d95c2 | ||
|
|
30bc9ded29 | ||
|
|
5a32892e94 | ||
|
|
e33de896b6 | ||
|
|
3ba92444a5 | ||
|
|
4f120e8eb4 | ||
|
|
08aebf5736 | ||
|
|
5a4966ba8d | ||
|
|
9ba963fc45 | ||
|
|
ccc10dbae4 | ||
|
|
d858342269 | ||
|
|
63f251d1fd | ||
|
|
83efe66f3e | ||
|
|
3f5729044f | ||
|
|
045af7e8df | ||
|
|
6f181053b2 | ||
|
|
4ff1dc2982 | ||
|
|
a883ba8808 | ||
|
|
961bd6140c | ||
|
|
5f364b48c9 | ||
|
|
5a04911c7c | ||
|
|
8ff248aa62 | ||
|
|
046ac95dc3 | ||
|
|
6fb17a4b74 | ||
|
|
f84397393d | ||
|
|
800c93e2aa | ||
|
|
df6179a1a8 | ||
|
|
3e3a079ba1 | ||
|
|
0bf4a43663 | ||
|
|
5e9f4ad926 | ||
|
|
69050bfe41 | ||
|
|
eebc16bcdd | ||
|
|
d4921535f2 | ||
|
|
b1eeb648a7 | ||
|
|
ba702ba041 | ||
|
|
c40d88d0b6 | ||
|
|
285f91e9e8 | ||
|
|
947a7aa7df | ||
|
|
0b37e36036 | ||
|
|
92320f5086 | ||
|
|
ae17e0c264 | ||
|
|
ec756ee943 | ||
|
|
b0ff814753 | ||
|
|
1f7eb2d48a | ||
|
|
eeab8e4a90 | ||
|
|
77fbddf488 | ||
|
|
bf396f8802 | ||
|
|
13cb8b84bf | ||
|
|
3ab992f5fb | ||
|
|
a386ecfc9c | ||
|
|
a71399c42c | ||
|
|
5a5d7e0981 | ||
|
|
b663a7f52a | ||
|
|
7390039086 | ||
|
|
12af02435f | ||
|
|
6f357828b7 | ||
|
|
09e1a4386d | ||
|
|
dcc8e6dc34 | ||
|
|
1b19ef54e4 | ||
|
|
8ce476caf1 | ||
|
|
dd2d9133eb | ||
|
|
2147d12ac8 | ||
|
|
22641a1da0 | ||
|
|
2bd4088d40 | ||
|
|
ee61363c31 | ||
|
|
8285688ec9 | ||
|
|
4eb076540e | ||
|
|
527bfec24c | ||
|
|
2fff7d7160 | ||
|
|
aac2b4aaf0 | ||
|
|
fe070b306f | ||
|
|
ecfadf8a23 | ||
|
|
653af71596 | ||
|
|
6a89be2e89 | ||
|
|
b84bad4e1a | ||
|
|
0f8d86f081 | ||
|
|
d44f311ba1 | ||
|
|
6f5428ca61 | ||
|
|
b0bee3ca1a | ||
|
|
7908194d27 | ||
|
|
b27c7fb2dd | ||
|
|
a23cb95a10 | ||
|
|
fc137f9b1c | ||
|
|
84780bf9ef | ||
|
|
1321037c52 | ||
|
|
de1da12d45 | ||
|
|
6d4cbacd50 | ||
|
|
bf9f7ca990 | ||
|
|
ca334104d7 | ||
|
|
c3e00ba31b | ||
|
|
ea3a9cc70e | ||
|
|
7ad22e0a73 | ||
|
|
ca1abb58eb | ||
|
|
3e353944b2 | ||
|
|
007a250f55 | ||
|
|
e9bb6a9951 | ||
|
|
82f236e07b | ||
|
|
17bfe74ecf | ||
|
|
b77827df90 | ||
|
|
a359a508ae | ||
|
|
3a740118f0 | ||
|
|
58d9bcbd14 | ||
|
|
24be7b2180 | ||
|
|
f4c9ca8dff | ||
|
|
9b8181b72b | ||
|
|
1ff1962425 | ||
|
|
9b05736746 | ||
|
|
dec722e693 | ||
|
|
49fa0a4c67 | ||
|
|
0cdc69e1e8 | ||
|
|
0494cc4ce7 | ||
|
|
30d82947d6 | ||
|
|
ec59249d79 | ||
|
|
7eb869ab1d | ||
|
|
d014724a2d | ||
|
|
dfb6cef364 | ||
|
|
0ebf8c9349 | ||
|
|
9dfd89c90d | ||
|
|
f6972125cd | ||
|
|
6e366bf55a | ||
|
|
5e2e45c673 | ||
|
|
f879235564 | ||
|
|
b00aee2ae7 | ||
|
|
28c2159ec3 | ||
|
|
33dcef2285 | ||
|
|
b9acd0ec28 | ||
|
|
7989d5180b | ||
|
|
238086942e | ||
|
|
a704a30242 | ||
|
|
26dd0d0ea5 | ||
|
|
e31a4bcaa9 | ||
|
|
1722dc570b | ||
|
|
3575e69b43 | ||
|
|
373d89874c | ||
|
|
976087ce97 | ||
|
|
69a45788ee | ||
|
|
92e4de12fb | ||
|
|
ea7fdb9b3d | ||
|
|
fe1c043b8e | ||
|
|
146f597a0b | ||
|
|
d2bef312ce | ||
|
|
61f297215d | ||
|
|
639a9fd540 | ||
|
|
3d0428c518 | ||
|
|
f40f926ce1 | ||
|
|
d6db4f0e4c | ||
|
|
fcbc9471aa | ||
|
|
b147e05794 | ||
|
|
47e6eb546d | ||
|
|
8ba05cb4ed | ||
|
|
84b91e9649 | ||
|
|
703e17478d | ||
|
|
8584e15c32 | ||
|
|
46be7aadab | ||
|
|
a5871f6cba | ||
|
|
917dc1803c | ||
|
|
c0c0961b2b | ||
|
|
0995ac6f9a | ||
|
|
b7f40e4cbf | ||
|
|
754cbb9eaa | ||
|
|
7052b56069 | ||
|
|
decdee825b | ||
|
|
5f66afc399 | ||
|
|
5ceb638edf | ||
|
|
688b9ef5ee | ||
|
|
4909a557d5 | ||
|
|
75f63afadc | ||
|
|
e90a624c9a | ||
|
|
dd85ccd3f8 | ||
|
|
fc54e19ce2 | ||
|
|
118a920e57 | ||
|
|
975a335538 | ||
|
|
c6ec4f38b0 | ||
|
|
5a5d686be1 | ||
|
|
8fc430d124 | ||
|
|
721eb40a8a | ||
|
|
f18103751f | ||
|
|
9d30bb669e | ||
|
|
ab6f5c21c8 | ||
|
|
4fc2ed32d2 | ||
|
|
00ab4f2a7d | ||
|
|
cb5d8b405b | ||
|
|
7b28aa8500 | ||
|
|
97a369df0a | ||
|
|
73a817c1cb | ||
|
|
c16053f0e5 | ||
|
|
3dfd108fc4 | ||
|
|
576696b25e | ||
|
|
781fab5aab | ||
|
|
0f099a19cd | ||
|
|
43b96ea4e5 | ||
|
|
8cbf6bfffa | ||
|
|
52debcdcb3 | ||
|
|
9d38d89c25 | ||
|
|
837f0ca393 | ||
|
|
42fd5b6eb2 | ||
|
|
7bdf76a665 | ||
|
|
784ef278ea | ||
|
|
9b4bf455b4 | ||
|
|
ddd3739ed9 | ||
|
|
1c95c5861c | ||
|
|
fd6fa1c622 | ||
|
|
6809a77a41 | ||
|
|
7a913ee38f | ||
|
|
cca330829a | ||
|
|
e2f5c76d91 | ||
|
|
26bf7c149c | ||
|
|
020df117c4 | ||
|
|
b48037b757 | ||
|
|
529b6613e9 | ||
|
|
8a5d8cdf8a | ||
|
|
cdf35740d9 | ||
|
|
76c40585fd | ||
|
|
2bf58288d3 | ||
|
|
ba2eac5290 | ||
|
|
23a4e4cc19 | ||
|
|
841a20cbd4 | ||
|
|
f70ca77913 | ||
|
|
c97df99c5c | ||
|
|
809e170018 | ||
|
|
d0c8f5eb45 | ||
|
|
d0f953f1d8 | ||
|
|
6d1636c540 | ||
|
|
b3292729b0 | ||
|
|
11840ffc8a | ||
|
|
9fd20d1dc8 | ||
|
|
ea6799d749 | ||
|
|
e23fe00a37 | ||
|
|
ccda957e8f | ||
|
|
cdaaf00509 | ||
|
|
4dfb0fa213 | ||
|
|
dcda8fc773 | ||
|
|
9c3fff8510 | ||
|
|
c3526ebfa3 | ||
|
|
28ba47a2f5 | ||
|
|
55a8b9085e | ||
|
|
2f250c5569 | ||
|
|
e9d0565e6a | ||
|
|
272de5f34d | ||
|
|
60a319a482 | ||
|
|
9e440fd5e6 | ||
|
|
235f20813d | ||
|
|
55ce8a3e97 | ||
|
|
b7ac15ad55 | ||
|
|
038b64a416 | ||
|
|
ec2fbca979 | ||
|
|
7f28b1d2c9 | ||
|
|
6a0ec7d294 | ||
|
|
217215b035 | ||
|
|
a6fefcacc9 | ||
|
|
03023c04fc | ||
|
|
8dadfe6579 | ||
|
|
6c06de63e4 | ||
|
|
54a122ae34 | ||
|
|
7a0daee443 | ||
|
|
88b5e29092 | ||
|
|
b3ea08dd99 | ||
|
|
95794bc5b2 | ||
|
|
3275454bd6 | ||
|
|
7f5bb86127 | ||
|
|
cf1ae8f240 | ||
|
|
9b2261918f | ||
|
|
f74071691f | ||
|
|
6d5e02860e | ||
|
|
ffa2acb2f5 | ||
|
|
25b4ff771e | ||
|
|
7a48617fea | ||
|
|
800807b47a | ||
|
|
eaf11c6998 | ||
|
|
e2053d5a77 | ||
|
|
ae6b3ef7fb | ||
|
|
359c6e0f02 | ||
|
|
9a8b4593e1 | ||
|
|
02fc631e3c | ||
|
|
19e03a1fdd | ||
|
|
5ea6279711 | ||
|
|
c44578c341 | ||
|
|
ab34d3fe30 | ||
|
|
5aa27c14c6 | ||
|
|
a5e9edb2e5 | ||
|
|
bbd8ef1f26 | ||
|
|
ad8afa0d3d | ||
|
|
5198f30363 | ||
|
|
50e55761ab | ||
|
|
7cb67aa49c | ||
|
|
1d4993180a | ||
|
|
73a36d5556 | ||
|
|
368e489190 | ||
|
|
d582d4846e | ||
|
|
87ffad398e | ||
|
|
439e7eaca0 | ||
|
|
90d703ae91 | ||
|
|
2d80c90f78 | ||
|
|
b54c8e97bd | ||
|
|
58e69a6644 | ||
|
|
f09a13259f | ||
|
|
8775c5e1f2 | ||
|
|
61b7997ae9 | ||
|
|
99fa98844c | ||
|
|
6e5781c633 | ||
|
|
b58e524d78 | ||
|
|
0d669af44e | ||
|
|
a331c70233 | ||
|
|
fd7d688e6b | ||
|
|
069d47f01b | ||
|
|
3079f1c651 | ||
|
|
f5ecda9255 | ||
|
|
d83ae315ed | ||
|
|
2b40e87eb9 | ||
|
|
f7e3eb180b | ||
|
|
209786cdb6 | ||
|
|
d418f79d8f | ||
|
|
eddd38ff03 | ||
|
|
e4fa729071 | ||
|
|
b3e7eac895 | ||
|
|
c43675d987 | ||
|
|
cdaff66126 | ||
|
|
cf45e89b16 | ||
|
|
aec8459761 | ||
|
|
de746375a9 | ||
|
|
25c2871888 | ||
|
|
28b2e1a405 | ||
|
|
2603132dae | ||
|
|
6f08cb0e88 | ||
|
|
dd65c3fcaa | ||
|
|
fec8641efe | ||
|
|
d5eaa56b60 | ||
|
|
db7e8dd6dc | ||
|
|
9415055e6f | ||
|
|
f597f09914 | ||
|
|
7903228c13 | ||
|
|
4c2388749d | ||
|
|
89da3823a4 | ||
|
|
3a9a49b163 | ||
|
|
277b21dc86 | ||
|
|
bc0e8c17ba | ||
|
|
51d3df69bb | ||
|
|
4913f6f37d | ||
|
|
ff622e2ef6 | ||
|
|
50ad643abe | ||
|
|
944849a381 | ||
|
|
6f485141f0 | ||
|
|
3575291119 | ||
|
|
e256ec5401 | ||
|
|
3defba6290 | ||
|
|
c201d986c5 | ||
|
|
36aad4424e | ||
|
|
b29fb1e2a3 | ||
|
|
d5aa307efb | ||
|
|
48928613bf | ||
|
|
52b8f480f6 | ||
|
|
bcda8bd602 | ||
|
|
2a11fe11e8 | ||
|
|
7ec0607fec | ||
|
|
aa41a8675e | ||
|
|
19f9bff6fe | ||
|
|
4e65732a4e | ||
|
|
47c843bf09 | ||
|
|
a7741a0b7d | ||
|
|
6f3fbdfe17 | ||
|
|
3be93df63f | ||
|
|
6259539c87 | ||
|
|
9654009650 | ||
|
|
62e796cf5a | ||
|
|
af820bb0f2 | ||
|
|
8f5bb3591b | ||
|
|
7eafae98d4 | ||
|
|
1d4e5baafb | ||
|
|
9894aa053f | ||
|
|
4ada8fea27 | ||
|
|
4d013a206b | ||
|
|
2d8a707bed | ||
|
|
0e55a859ea | ||
|
|
0b1ec520f4 | ||
|
|
fcc5f29882 | ||
|
|
d431c63245 | ||
|
|
15f8db64e7 | ||
|
|
c2ea14151e | ||
|
|
977f0428e2 | ||
|
|
f110446738 | ||
|
|
90289120d3 | ||
|
|
688f206555 | ||
|
|
38c739c45d | ||
|
|
73c79ca7bf | ||
|
|
978975ee2d | ||
|
|
d7dc0dff50 | ||
|
|
a3aa6c045f | ||
|
|
785ebc49fc | ||
|
|
fde1c98ddd | ||
|
|
024040163a | ||
|
|
e95b5e04c0 | ||
|
|
815826c856 | ||
|
|
1c5cc190c5 | ||
|
|
50b109ca71 | ||
|
|
3aa48a9a3e | ||
|
|
09702b9bde | ||
|
|
35f6cd3d9c | ||
|
|
b62f1786a9 | ||
|
|
b21ad28d5b | ||
|
|
8d66c0d180 | ||
|
|
ec8aa0df18 | ||
|
|
e824d8e91d | ||
|
|
b919422ec4 | ||
|
|
a8bc9ce605 | ||
|
|
e2f399eeb1 | ||
|
|
82924278b5 | ||
|
|
bb828a4e06 | ||
|
|
74933ac146 | ||
|
|
0d315f0dc3 | ||
|
|
372ee346dc | ||
|
|
cb462ab944 | ||
|
|
606da79372 | ||
|
|
0801c3db64 | ||
|
|
35deb0c915 | ||
|
|
fd4d712688 | ||
|
|
354fc618b0 | ||
|
|
82b6ee5ad2 | ||
|
|
645e6c4ec0 | ||
|
|
cdc83bc9d7 | ||
|
|
70feacd276 | ||
|
|
ece4572058 | ||
|
|
4d16a5e801 | ||
|
|
1493a8b03f | ||
|
|
d20791bf73 | ||
|
|
7bb91f57ac | ||
|
|
b7a6004830 | ||
|
|
dee5613f2f | ||
|
|
e58e0d6ac7 | ||
|
|
a2679e009d | ||
|
|
78d6bcd57a | ||
|
|
026936c92f | ||
|
|
5e5893362c | ||
|
|
5b8ce1836b | ||
|
|
d71f3fafcf | ||
|
|
828f93bc5c | ||
|
|
f947c541c9 | ||
|
|
539b6aafd9 | ||
|
|
c3971bda05 | ||
|
|
8f17331db2 | ||
|
|
583e824337 | ||
|
|
ac79466041 | ||
|
|
291fd491ff | ||
|
|
6408d51c85 | ||
|
|
b693873619 | ||
|
|
c2af982272 | ||
|
|
1f0a77bde2 | ||
|
|
0463c1a6e7 | ||
|
|
6517f66876 | ||
|
|
82682d4474 | ||
|
|
e030fae1c4 | ||
|
|
4d7cf140cd | ||
|
|
f6ccee1976 | ||
|
|
d42346d500 | ||
|
|
887dd8da3b | ||
|
|
731db2541d | ||
|
|
bfdc0a2e77 | ||
|
|
03ea24f29b | ||
|
|
61bd48fd1e | ||
|
|
f05b5d7fff | ||
|
|
0b8d66a87e | ||
|
|
c0b7a8c33e | ||
|
|
32a07bceb2 | ||
|
|
ba8f24d398 | ||
|
|
31b7857238 | ||
|
|
fada5f99bb | ||
|
|
734df5def5 | ||
|
|
03ad07733f | ||
|
|
b2d538d02a | ||
|
|
b9435a4b01 | ||
|
|
d27566686c | ||
|
|
ca883c164d | ||
|
|
0f6cd76f28 | ||
|
|
7778af65aa | ||
|
|
828246130b | ||
|
|
4eb1801f50 | ||
|
|
80b68dcf84 | ||
|
|
685baa576b | ||
|
|
7cdead8cb1 | ||
|
|
f220825b80 | ||
|
|
2b00490dd6 | ||
|
|
d361afa3f3 | ||
|
|
8d5bd5a80c | ||
|
|
002283899f | ||
|
|
206b440966 | ||
|
|
b5b5eb25a1 | ||
|
|
f84e538b6a | ||
|
|
5b9cdec156 | ||
|
|
cf0bd7f1e6 | ||
|
|
f3a86a2173 | ||
|
|
0a1d4d5193 | ||
|
|
88e2288298 | ||
|
|
fec47d4b72 | ||
|
|
f7f23e7980 | ||
|
|
32100db3fc | ||
|
|
dd2bed49f1 | ||
|
|
6a427a2158 | ||
|
|
716029def8 | ||
|
|
610a610ee0 | ||
|
|
4fc0738dad | ||
|
|
fe29771870 | ||
|
|
19be43d2d9 | ||
|
|
cb01809093 | ||
|
|
1138d660b2 | ||
|
|
c29ccf76d5 | ||
|
|
5907bf388c | ||
|
|
54adaffb92 | ||
|
|
3bc7812e1a | ||
|
|
16cd9706f9 | ||
|
|
839d07870c | ||
|
|
933d3c2678 | ||
|
|
a29b744526 | ||
|
|
c4070b26ff | ||
|
|
6d0b4e8c75 | ||
|
|
058e1aafd1 | ||
|
|
eac7361a0b | ||
|
|
64e6d6d038 | ||
|
|
d613fe79af | ||
|
|
565a7b1680 | ||
|
|
5076f45674 | ||
|
|
dc72c949c3 | ||
|
|
c85e418afa | ||
|
|
2fc3e846df | ||
|
|
a665dc9db7 | ||
|
|
672eb4b365 | ||
|
|
c9a150565d | ||
|
|
5b271108c0 | ||
|
|
8b63a19161 | ||
|
|
0d17a112a6 | ||
|
|
d997633810 | ||
|
|
446e981d0e | ||
|
|
dabca02b18 | ||
|
|
790bd1217c | ||
|
|
43d8515e23 | ||
|
|
bcd051e5a3 | ||
|
|
1a1e08753f | ||
|
|
d46838eb55 | ||
|
|
a70b5eeef5 | ||
|
|
7513ff860b | ||
|
|
4d872e9143 | ||
|
|
4408d67c49 | ||
|
|
545e1c5402 | ||
|
|
03dec5b604 | ||
|
|
777427787a | ||
|
|
f0c59e38cc | ||
|
|
35cbc54f99 | ||
|
|
bb78af8489 | ||
|
|
af000a27a6 | ||
|
|
b30191feb2 | ||
|
|
83804bd914 | ||
|
|
23b0f154f3 | ||
|
|
6084173257 | ||
|
|
7885893e9b | ||
|
|
53f9fee834 | ||
|
|
ae79d8c608 | ||
|
|
9c96feb4b5 | ||
|
|
597531b92b | ||
|
|
108ab7069b | ||
|
|
63411c5978 | ||
|
|
e6fc9e4783 | ||
|
|
57d5e02d6a | ||
|
|
e578c75545 | ||
|
|
77c5f6c583 | ||
|
|
d8ee3c3bba | ||
|
|
656451f604 | ||
|
|
9dc8cba3f0 | ||
|
|
03c4954c27 | ||
|
|
3c550e2803 | ||
|
|
52f1bdf834 | ||
|
|
c2c9c6a6db | ||
|
|
b390776219 | ||
|
|
47dce69aa4 | ||
|
|
3aeaadade6 | ||
|
|
162b3ab29c | ||
|
|
b8848823cd | ||
|
|
378f350c52 | ||
|
|
e7231d33b3 | ||
|
|
7c633374f6 | ||
|
|
1663c7d819 | ||
|
|
00982fd40b | ||
|
|
3697de973c | ||
|
|
3e575cc485 | ||
|
|
1675c67ddd | ||
|
|
b20c90acf0 | ||
|
|
a370f7bfbb | ||
|
|
e2de9b4703 | ||
|
|
b487dcf448 | ||
|
|
6c0a364d03 | ||
|
|
f892f0c533 | ||
|
|
0713c0c667 | ||
|
|
ab4d34b6ac | ||
|
|
3823e5ca91 | ||
|
|
c0430536a6 | ||
|
|
6f85b5318b | ||
|
|
76e67693fa | ||
|
|
478521d6d9 | ||
|
|
57299cf182 | ||
|
|
45738a7160 | ||
|
|
583bd22244 | ||
|
|
ba68f5008b | ||
|
|
2f3f52c5e0 | ||
|
|
1dcb72e9dc | ||
|
|
153c8a0a74 | ||
|
|
4e8ea374d8 | ||
|
|
e313e004eb | ||
|
|
93b8dec63e | ||
|
|
5c9c35c199 | ||
|
|
4a6b8968d5 | ||
|
|
088d2e6834 | ||
|
|
4f39f7a932 | ||
|
|
2615b72cee | ||
|
|
a799420d0f | ||
|
|
70069a05e6 | ||
|
|
d385b44a48 | ||
|
|
56c1557f4a | ||
|
|
737d563ebb | ||
|
|
81b70195cf | ||
|
|
e60851153b | ||
|
|
009a15f0d9 | ||
|
|
f234775bd5 | ||
|
|
9b8bd0fa8a | ||
|
|
f74191946c | ||
|
|
ffa8b6296d | ||
|
|
0b2a392de2 | ||
|
|
f6b044c10c | ||
|
|
88c87aec0e | ||
|
|
b632dee0ce | ||
|
|
0db772aaeb | ||
|
|
fedfed4028 | ||
|
|
3b27e7be36 | ||
|
|
78402118ef | ||
|
|
aa3094f796 | ||
|
|
20498ce453 | ||
|
|
56559e6509 | ||
|
|
9549d17b03 | ||
|
|
a822fa9766 | ||
|
|
dee4b19775 | ||
|
|
f4b885dce5 | ||
|
|
2c02285def | ||
|
|
be7d76947b | ||
|
|
a81254dfff | ||
|
|
cb24a909ae | ||
|
|
8a87318b68 | ||
|
|
219f544a43 | ||
|
|
46c887537e | ||
|
|
8cf66da07c | ||
|
|
57bcf8a1a7 | ||
|
|
05efff10da | ||
|
|
414ac3225a | ||
|
|
0a8e822f35 | ||
|
|
301d1dd998 | ||
|
|
621565f4a4 | ||
|
|
6f51d447c7 | ||
|
|
f9c7556c54 | ||
|
|
32b98524ff | ||
|
|
efaec3ee17 | ||
|
|
28826cad1b | ||
|
|
ca9840abf4 | ||
|
|
8813d66274 | ||
|
|
da2b0f7dd4 | ||
|
|
b1773dab90 | ||
|
|
9538a67be1 | ||
|
|
fbfbc811d4 | ||
|
|
1c04b752cd | ||
|
|
3b1a2287d5 | ||
|
|
f3e6bd2736 | ||
|
|
4e449811e7 | ||
|
|
910be7f471 | ||
|
|
1e088f851b | ||
|
|
a0b0ad53d2 | ||
|
|
5b3f09aaf7 | ||
|
|
f48468029e | ||
|
|
c837ba5482 | ||
|
|
2708ff7c1a | ||
|
|
1722e50402 | ||
|
|
244da05e70 | ||
|
|
0c26662602 | ||
|
|
2f65575fdf |
58
.github/ISSUE_TEMPLATE/01_bug_report.yml
vendored
Normal file
58
.github/ISSUE_TEMPLATE/01_bug_report.yml
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
name: Bug 报告
|
||||
description: 在提出问题前请先自行排除服务器端问题和升级到最新客户端,同时也请通过搜索确认是否有人提出过相同问题。
|
||||
title: "[Bug]: "
|
||||
labels: ["bug"]
|
||||
body:
|
||||
- type: input
|
||||
id: "expectation"
|
||||
attributes:
|
||||
label: "预期情况"
|
||||
description: "描述你认为应该发生什么"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: "describe-the-bug"
|
||||
attributes:
|
||||
label: "实际情况"
|
||||
description: "描述实际发生了什么"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: "reproduction-method"
|
||||
attributes:
|
||||
label: "复现方法"
|
||||
description: "在BUG出现前执行了哪些操作"
|
||||
placeholder: 标序号
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: "log"
|
||||
attributes:
|
||||
label: "日志信息"
|
||||
description: "位置在软件当前目录下的guiLogs"
|
||||
placeholder: 在日志开始和结束位置粘贴冒号后的内容:```
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: "more"
|
||||
attributes:
|
||||
label: "额外信息"
|
||||
description: "可选"
|
||||
validations:
|
||||
required: false
|
||||
- type: checkboxes
|
||||
id: "latest-version"
|
||||
attributes:
|
||||
label: "我确认已更新至最新版本"
|
||||
description: "否则请更新后尝试"
|
||||
options:
|
||||
- label: 是
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: "issues"
|
||||
attributes:
|
||||
label: "我确认已查询历史issues"
|
||||
description: "否则请查询后提出"
|
||||
options:
|
||||
- label: 是
|
||||
required: true
|
||||
34
.github/ISSUE_TEMPLATE/02_feature_request.yml
vendored
Normal file
34
.github/ISSUE_TEMPLATE/02_feature_request.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
name: Feature 请求
|
||||
description: "为这个项目提出一个建议"
|
||||
title: "[Feature request]: "
|
||||
labels: ['enhancement']
|
||||
body:
|
||||
- type: input
|
||||
id: problem
|
||||
attributes:
|
||||
label: 相关问题
|
||||
description: "清楚而简洁地描述问题是什么。"
|
||||
placeholder: "当我想要……时,软件不能……"
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: way-to-solve
|
||||
attributes:
|
||||
label: 描述你希望的解决方案
|
||||
description: "你希望发生什么"
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: instead
|
||||
attributes:
|
||||
label: 描述你所考虑的替代方案
|
||||
validations:
|
||||
required: false
|
||||
- type: checkboxes
|
||||
id: "issues"
|
||||
attributes:
|
||||
label: "我确认已查询历史issues"
|
||||
description: "否则请查询后提出"
|
||||
options:
|
||||
- label: 是
|
||||
required: true
|
||||
10
.github/dependabot.yml
vendored
Normal file
10
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# Set update schedule for GitHub Actions
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
# Check for updates to GitHub Actions every daily
|
||||
interval: "daily"
|
||||
25
.github/issue_template.md
vendored
25
.github/issue_template.md
vendored
@@ -1,25 +0,0 @@
|
||||
在提出问题前请先自行排除服务器端问题和升级到最新客户端,同时也请通过搜索确认是否有人提出过相同问题。
|
||||
|
||||
### 预期行为
|
||||
描述你认为应该发生什么
|
||||
|
||||
### 实际行为
|
||||
描述实际发生了什么
|
||||
|
||||
### 复现方法
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
### 日志信息,位置在当前目录下的guiLogs
|
||||
<details>
|
||||
|
||||
```
|
||||
在这里粘贴日志
|
||||
```
|
||||
</details>
|
||||
|
||||
### 环境信息(客户端请升级至最新正式版)
|
||||
|
||||
### 额外信息(可选)
|
||||
|
||||
77
.github/workflows/build-linux.yml
vendored
Normal file
77
.github/workflows/build-linux.yml
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
name: release Linux
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release_tag:
|
||||
required: false
|
||||
type: string
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
env:
|
||||
OutputArch: "linux-64"
|
||||
OutputArchArm: "linux-arm64"
|
||||
OutputPath64: "${{ github.workspace }}/v2rayN/Release/linux-64"
|
||||
OutputPathArm64: "${{ github.workspace }}/v2rayN/Release/linux-arm64"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
configuration: [Release]
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cd v2rayN
|
||||
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-x64 --self-contained true -p:PublishReadyToRun=false -p:PublishSingleFile=true -o $OutputPath64
|
||||
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-arm64 --self-contained true -p:PublishReadyToRun=false -p:PublishSingleFile=true -o $OutputPathArm64
|
||||
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-x64 --self-contained true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:PublishTrimmed=true -o $OutputPath64
|
||||
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-arm64 --self-contained true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:PublishTrimmed=true -o $OutputPathArm64
|
||||
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: v2rayN-linux
|
||||
path: |
|
||||
${{ github.workspace }}/v2rayN/Release/linux*
|
||||
|
||||
# release debian package
|
||||
- name: Package debian
|
||||
if: github.event.inputs.release_tag != ''
|
||||
run: |
|
||||
chmod 755 package-debian.sh
|
||||
./package-debian.sh $OutputArch $OutputPath64 ${{ github.event.inputs.release_tag }}
|
||||
./package-debian.sh $OutputArchArm $OutputPathArm64 ${{ github.event.inputs.release_tag }}
|
||||
|
||||
- name: Upload deb to release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
if: github.event.inputs.release_tag != ''
|
||||
with:
|
||||
file: ${{ github.workspace }}/v2rayN*.deb
|
||||
tag: ${{ github.event.inputs.release_tag }}
|
||||
file_glob: true
|
||||
|
||||
# release zip archive
|
||||
- name: Package release zip archive
|
||||
if: github.event.inputs.release_tag != ''
|
||||
run: |
|
||||
chmod 755 package-release-zip.sh
|
||||
./package-release-zip.sh $OutputArch $OutputPath64
|
||||
./package-release-zip.sh $OutputArchArm $OutputPathArm64
|
||||
|
||||
- name: Upload zip archive to release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
if: github.event.inputs.release_tag != ''
|
||||
with:
|
||||
file: ${{ github.workspace }}/v2rayN*.zip
|
||||
tag: ${{ github.event.inputs.release_tag }}
|
||||
file_glob: true
|
||||
prerelease: true
|
||||
78
.github/workflows/build-osx.yml
vendored
Normal file
78
.github/workflows/build-osx.yml
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
name: release macOS
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release_tag:
|
||||
required: false
|
||||
type: string
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
env:
|
||||
OutputArch: "macos-64"
|
||||
OutputArchArm: "macos-arm64"
|
||||
OutputPath64: "${{ github.workspace }}/v2rayN/Release/macos-64"
|
||||
OutputPathArm64: "${{ github.workspace }}/v2rayN/Release/macos-arm64"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
configuration: [Release]
|
||||
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cd v2rayN
|
||||
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r osx-x64 --self-contained true -p:PublishReadyToRun=false -p:PublishSingleFile=true -o $OutputPath64
|
||||
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r osx-arm64 --self-contained true -p:PublishReadyToRun=false -p:PublishSingleFile=true -o $OutputPathArm64
|
||||
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-x64 --self-contained true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:PublishTrimmed=true -o $OutputPath64
|
||||
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-arm64 --self-contained true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:PublishTrimmed=true -o $OutputPathArm64
|
||||
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: v2rayN-macos
|
||||
path: |
|
||||
${{ github.workspace }}/v2rayN/Release/macos*
|
||||
|
||||
# release osx package
|
||||
- name: Package osx
|
||||
if: github.event.inputs.release_tag != ''
|
||||
run: |
|
||||
brew install create-dmg
|
||||
chmod 755 package-osx.sh
|
||||
./package-osx.sh $OutputArch $OutputPath64 ${{ github.event.inputs.release_tag }}
|
||||
./package-osx.sh $OutputArchArm $OutputPathArm64 ${{ github.event.inputs.release_tag }}
|
||||
|
||||
- name: Upload dmg to release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
if: github.event.inputs.release_tag != ''
|
||||
with:
|
||||
file: ${{ github.workspace }}/v2rayN*.dmg
|
||||
tag: ${{ github.event.inputs.release_tag }}
|
||||
file_glob: true
|
||||
|
||||
# release zip archive
|
||||
- name: Package release zip archive
|
||||
if: github.event.inputs.release_tag != ''
|
||||
run: |
|
||||
chmod 755 package-release-zip.sh
|
||||
./package-release-zip.sh $OutputArch $OutputPath64
|
||||
./package-release-zip.sh $OutputArchArm $OutputPathArm64
|
||||
|
||||
- name: Upload zip archive to release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
if: github.event.inputs.release_tag != ''
|
||||
with:
|
||||
file: ${{ github.workspace }}/v2rayN*.zip
|
||||
tag: ${{ github.event.inputs.release_tag }}
|
||||
file_glob: true
|
||||
prerelease: true
|
||||
68
.github/workflows/build-windows.yml
vendored
Normal file
68
.github/workflows/build-windows.yml
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
name: release Windows
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release_tag:
|
||||
required: false
|
||||
type: string
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
env:
|
||||
OutputArch: "windows-64"
|
||||
OutputArchArm: "windows-arm64"
|
||||
OutputPath64: "${{ github.workspace }}/v2rayN/Release/windows-64"
|
||||
OutputPathArm64: "${{ github.workspace }}/v2rayN/Release/windows-arm64"
|
||||
OutputPath64Sc: "${{ github.workspace }}/v2rayN/Release/windows-64-SelfContained"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
configuration: [Release]
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cd v2rayN
|
||||
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-x64 --self-contained false -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:EnableWindowsTargeting=true -o $OutputPath64
|
||||
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-arm64 --self-contained false -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:EnableWindowsTargeting=true -o $OutputPathArm64
|
||||
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-x64 --self-contained true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:EnableWindowsTargeting=true -o $OutputPath64Sc
|
||||
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 --self-contained false -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:EnableWindowsTargeting=true -o $OutputPath64
|
||||
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 --self-contained false -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:EnableWindowsTargeting=true -o $OutputPathArm64
|
||||
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 --self-contained true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:PublishTrimmed=true -p:EnableWindowsTargeting=true -o $OutputPath64Sc
|
||||
|
||||
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: v2rayN-windows
|
||||
path: |
|
||||
${{ github.workspace }}/v2rayN/Release/windows*
|
||||
|
||||
# release zip archive
|
||||
- name: Package release zip archive
|
||||
if: github.event.inputs.release_tag != ''
|
||||
run: |
|
||||
chmod 755 package-release-zip.sh
|
||||
./package-release-zip.sh $OutputArch $OutputPath64
|
||||
./package-release-zip.sh "windows-64-With-Core" $OutputPath64
|
||||
./package-release-zip.sh $OutputArchArm $OutputPathArm64
|
||||
./package-release-zip.sh "windows-64-SelfContained" $OutputPath64Sc
|
||||
./package-release-zip.sh "windows-64-SelfContained-With-Core" $OutputPath64Sc
|
||||
|
||||
- name: Upload zip archive to release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
if: github.event.inputs.release_tag != ''
|
||||
with:
|
||||
file: ${{ github.workspace }}/v2rayN*.zip
|
||||
tag: ${{ github.event.inputs.release_tag }}
|
||||
file_glob: true
|
||||
prerelease: true
|
||||
31
.github/workflows/winget-publish.yml
vendored
Normal file
31
.github/workflows/winget-publish.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: WinGet submission on release
|
||||
# based off of https://github.com/nushell/nushell/blob/main/.github/workflows/winget-submission.yml
|
||||
# inspired by https://github.com/microsoft/PowerToys/blob/main/.github/workflows/package-submissions.yml
|
||||
# Modified by @MerrickZ https://github.com/anpho
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [released]
|
||||
|
||||
jobs:
|
||||
winget:
|
||||
name: Publish winget package
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Submit v2ray package to Windows Package Manager Community Repository
|
||||
run: |
|
||||
|
||||
$wingetPackage = "2dust.v2rayN"
|
||||
$gitToken = "${{ secrets.PT_WINGET }}"
|
||||
|
||||
$github = Invoke-RestMethod -uri "https://api.github.com/repos/2dust/v2rayN/releases"
|
||||
|
||||
$targetRelease = $github | Where-Object -Property prerelease -match 'False' | Select -First 1
|
||||
$installerUrl = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'v2rayN-windows-64-With-Core\.zip*' | Select -ExpandProperty browser_download_url
|
||||
|
||||
$ver = $targetRelease.tag_name
|
||||
|
||||
# getting latest wingetcreate file
|
||||
iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe
|
||||
.\wingetcreate.exe update $wingetPackage -s -v $ver -u "$installerUrl|x64" -t $gitToken
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -13,6 +13,7 @@
|
||||
.vs/slnx.sqlite
|
||||
.vs/VSWorkspaceState.json
|
||||
/v2rayN/v2rayUpgrade/bin/Debug
|
||||
/v2rayN/v2rayUpgrade/obj/Debug
|
||||
/v2rayN/v2rayUpgrade/bin/Release
|
||||
/v2rayN/v2rayUpgrade/obj/Release
|
||||
/v2rayN/v2rayUpgrade/obj/
|
||||
*.user
|
||||
/.vs/v2rayN
|
||||
|
||||
38
README.md
38
README.md
@@ -1,10 +1,34 @@
|
||||
# v2rayN
|
||||
A GUI client for Windows, Linux and macOS, support [Xray core](https://github.com/XTLS/Xray-core) and [sing-box-core](https://github.com/SagerNet/sing-box/releases) and [others](https://github.com/2dust/v2rayN/wiki/List-of-supported-cores)
|
||||
|
||||
### How to use
|
||||
- If you are newbie please download v2rayN-Core.zip from releases
|
||||
- Otherwise please download v2rayN.zip (Also need to download v2ray core in the same folder)
|
||||
- Run v2rayN.exe
|
||||
|
||||
### Requirements
|
||||
- Microsoft [.NET Framework 4.6](https://docs.microsoft.com/zh-cn/dotnet/framework/install/guide-for-developers) or higher
|
||||
- Project V core [https://github.com/v2ray/v2ray-core/releases](https://github.com/v2ray/v2ray-core/releases)
|
||||
[](https://github.com/2dust/v2rayN/commits/master)
|
||||
[](https://www.codefactor.io/repository/github/2dust/v2rayn)
|
||||
[](https://github.com/2dust/v2rayN/releases)
|
||||
[](https://t.me/v2rayn)
|
||||
|
||||
|
||||
## How to use
|
||||
Check [Release files introduction](https://github.com/2dust/v2rayN/wiki/Release-files-introduction) and select the version you need to download
|
||||
### Windows
|
||||
- Run `v2rayN.exe`
|
||||
### Linux
|
||||
- `chmod +x v2rayN` Run `./v2rayN` under user permissions
|
||||
```
|
||||
Debian 9+
|
||||
Ubuntu 16.04+
|
||||
Fedora 30+
|
||||
```
|
||||
### macOS
|
||||
- `chmod +x v2rayN` Run `./v2rayN` under user permissions
|
||||
```
|
||||
macOS 10.14+
|
||||
```
|
||||
|
||||
## Requirements
|
||||
- [Microsoft .NET 8.0 Desktop Runtime ](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)
|
||||
- [Supported cores](https://github.com/2dust/v2rayN/wiki/List-of-supported-cores)
|
||||
|
||||
|
||||
## Telegram Channel
|
||||
[github_2dust](https://t.me/github_2dust)
|
||||
|
||||
53
package-debian.sh
Normal file
53
package-debian.sh
Normal file
@@ -0,0 +1,53 @@
|
||||
#!/bin/bash
|
||||
|
||||
Arch="$1"
|
||||
OutputPath="$2"
|
||||
Version="$3"
|
||||
|
||||
PackagePath="v2rayN-Package-${Arch}"
|
||||
mkdir -p "${PackagePath}/DEBIAN"
|
||||
mkdir -p "${PackagePath}/opt"
|
||||
cp -rf $OutputPath "${PackagePath}/opt/v2rayN"
|
||||
echo "When this file exists, app will not store configs under this folder" > "${PackagePath}/opt/v2rayN/NotStoreConfigHere.txt"
|
||||
|
||||
if [ $Arch = "linux-64" ]; then
|
||||
Arch2="amd64"
|
||||
else
|
||||
Arch2="arm64"
|
||||
fi
|
||||
echo $Arch2
|
||||
|
||||
# basic
|
||||
cat >"${PackagePath}/DEBIAN/control" <<-EOF
|
||||
Package: v2rayN
|
||||
Version: $Version
|
||||
Architecture: $Arch2
|
||||
Maintainer: https://github.com/2dust/v2rayN
|
||||
Description: A GUI client for Windows and Linux, support Xray core and sing-box-core and others
|
||||
EOF
|
||||
|
||||
cat >"${PackagePath}/DEBIAN/postinst" <<-EOF
|
||||
if [ ! -s /usr/share/applications/v2rayN.desktop ]; then
|
||||
cat >/usr/share/applications/v2rayN.desktop<<-END
|
||||
[Desktop Entry]
|
||||
Name=v2rayN
|
||||
Comment=A GUI client for Windows and Linux, support Xray core and sing-box-core and others
|
||||
Exec=/opt/v2rayN/v2rayN
|
||||
Icon=/opt/v2rayN/v2rayN.png
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Network;Application;
|
||||
END
|
||||
fi
|
||||
|
||||
update-desktop-database
|
||||
EOF
|
||||
|
||||
sudo chmod 0755 "${PackagePath}/DEBIAN/postinst"
|
||||
sudo chmod 0755 "${PackagePath}/opt/v2rayN/v2rayN"
|
||||
sudo chmod 0755 "${PackagePath}/opt/v2rayN/AmazTool"
|
||||
|
||||
# desktop && PATH
|
||||
|
||||
sudo dpkg-deb -Zxz --build $PackagePath
|
||||
sudo mv "${PackagePath}.deb" "v2rayN-${Arch}.deb"
|
||||
65
package-osx.sh
Executable file
65
package-osx.sh
Executable file
@@ -0,0 +1,65 @@
|
||||
#!/bin/bash
|
||||
|
||||
Arch="$1"
|
||||
OutputPath="$2"
|
||||
Version="$3"
|
||||
|
||||
PackagePath="v2rayN-Package-${Arch}"
|
||||
mkdir -p "$PackagePath/v2rayN.app/Contents/Resources"
|
||||
cp -rf "$OutputPath" "$PackagePath/v2rayN.app/Contents/MacOS"
|
||||
echo "When this file exists, app will not store configs under this folder" > "$PackagePath/v2rayN.app/Contents/MacOS/NotStoreConfigHere.txt"
|
||||
chmod +x "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN"
|
||||
|
||||
mkdir -p "$PackagePath/icons.iconset"
|
||||
sips -z 16 16 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN2.png" --out "$PackagePath/icons.iconset/icon_16x16.png"
|
||||
sips -z 32 32 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN2.png" --out "$PackagePath/icons.iconset/icon_16x16@2x.png"
|
||||
sips -z 32 32 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN2.png" --out "$PackagePath/icons.iconset/icon_32x32.png"
|
||||
sips -z 64 64 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN2.png" --out "$PackagePath/icons.iconset/icon_32x32@2x.png"
|
||||
sips -z 128 128 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN2.png" --out "$PackagePath/icons.iconset/icon_128x128.png"
|
||||
sips -z 256 256 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN2.png" --out "$PackagePath/icons.iconset/icon_128x128@2x.png"
|
||||
sips -z 256 256 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN2.png" --out "$PackagePath/icons.iconset/icon_256x256.png"
|
||||
sips -z 512 512 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN2.png" --out "$PackagePath/icons.iconset/icon_256x256@2x.png"
|
||||
sips -z 512 512 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN2.png" --out "$PackagePath/icons.iconset/icon_512x512.png"
|
||||
sips -z 1024 1024 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN2.png" --out "$PackagePath/icons.iconset/icon_512x512@2x.png"
|
||||
iconutil -c icns "$PackagePath/icons.iconset" -o "$PackagePath/v2rayN.app/Contents/Resources/AppIcon.icns"
|
||||
|
||||
cat >"$PackagePath/v2rayN.app/Contents/Info.plist" <<-EOF
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>v2rayN</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>v2rayN</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>AppIcon</string>
|
||||
<key>CFBundleIconName</key>
|
||||
<string>AppIcon</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>2dust.v2rayN</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>v2rayN</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>${Version}</string>
|
||||
<key>CSResourcesFileMapped</key>
|
||||
<true/>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
EOF
|
||||
|
||||
create-dmg \
|
||||
--volname "v2rayN Installer" \
|
||||
--window-size 700 420 \
|
||||
--icon-size 100 \
|
||||
--icon "v2rayN.app" 160 185 \
|
||||
--hide-extension "v2rayN.app" \
|
||||
--app-drop-link 500 185 \
|
||||
"v2rayN-${Arch}.dmg" \
|
||||
"$PackagePath/v2rayN.app"
|
||||
15
package-release-zip.sh
Normal file
15
package-release-zip.sh
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
Arch="$1"
|
||||
OutputPath="$2"
|
||||
|
||||
OutputArch="v2rayN-${Arch}"
|
||||
FileName="v2rayN-${Arch}.zip"
|
||||
|
||||
wget -nv -O $FileName "https://github.com/2dust/v2rayN-core-bin/raw/refs/heads/master/$FileName"
|
||||
|
||||
ZipPath64="./$OutputArch"
|
||||
mkdir $ZipPath64
|
||||
|
||||
cp -rf $OutputPath "$ZipPath64/$OutputArch"
|
||||
7z a -tZip $FileName "$ZipPath64/$OutputArch" -mx1
|
||||
63
v2rayN/.gitattributes
vendored
Normal file
63
v2rayN/.gitattributes
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
###############################################################################
|
||||
# Set default behavior to automatically normalize line endings.
|
||||
###############################################################################
|
||||
* text=auto
|
||||
|
||||
###############################################################################
|
||||
# Set default behavior for command prompt diff.
|
||||
#
|
||||
# This is need for earlier builds of msysgit that does not have it on by
|
||||
# default for csharp files.
|
||||
# Note: This is only used by command line
|
||||
###############################################################################
|
||||
#*.cs diff=csharp
|
||||
|
||||
###############################################################################
|
||||
# Set the merge driver for project and solution files
|
||||
#
|
||||
# Merging from the command prompt will add diff markers to the files if there
|
||||
# are conflicts (Merging from VS is not affected by the settings below, in VS
|
||||
# the diff markers are never inserted). Diff markers may cause the following
|
||||
# file extensions to fail to load in VS. An alternative would be to treat
|
||||
# these files as binary and thus will always conflict and require user
|
||||
# intervention with every merge. To do so, just uncomment the entries below
|
||||
###############################################################################
|
||||
#*.sln merge=binary
|
||||
#*.csproj merge=binary
|
||||
#*.vbproj merge=binary
|
||||
#*.vcxproj merge=binary
|
||||
#*.vcproj merge=binary
|
||||
#*.dbproj merge=binary
|
||||
#*.fsproj merge=binary
|
||||
#*.lsproj merge=binary
|
||||
#*.wixproj merge=binary
|
||||
#*.modelproj merge=binary
|
||||
#*.sqlproj merge=binary
|
||||
#*.wwaproj merge=binary
|
||||
|
||||
###############################################################################
|
||||
# behavior for image files
|
||||
#
|
||||
# image files are treated as binary by default.
|
||||
###############################################################################
|
||||
#*.jpg binary
|
||||
#*.png binary
|
||||
#*.gif binary
|
||||
|
||||
###############################################################################
|
||||
# diff behavior for common document formats
|
||||
#
|
||||
# Convert binary document formats to text before diffing them. This feature
|
||||
# is only available from the command line. Turn it on by uncommenting the
|
||||
# entries below.
|
||||
###############################################################################
|
||||
#*.doc diff=astextplain
|
||||
#*.DOC diff=astextplain
|
||||
#*.docx diff=astextplain
|
||||
#*.DOCX diff=astextplain
|
||||
#*.dot diff=astextplain
|
||||
#*.DOT diff=astextplain
|
||||
#*.pdf diff=astextplain
|
||||
#*.PDF diff=astextplain
|
||||
#*.rtf diff=astextplain
|
||||
#*.RTF diff=astextplain
|
||||
363
v2rayN/.gitignore
vendored
Normal file
363
v2rayN/.gitignore
vendored
Normal file
@@ -0,0 +1,363 @@
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Ww][Ii][Nn]32/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Oo]ut/
|
||||
[Ll]og/
|
||||
[Ll]ogs/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUnit
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# ASP.NET Scaffolding
|
||||
ScaffoldingReadMe.txt
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Coverlet is a free, cross platform Code Coverage Tool
|
||||
coverage*.json
|
||||
coverage*.xml
|
||||
coverage*.info
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# NuGet Symbol Packages
|
||||
*.snupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
*.appxbundle
|
||||
*.appxupload
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- [Bb]ackup.rdl
|
||||
*- [Bb]ackup ([0-9]).rdl
|
||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
MigrationBackup/
|
||||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
.ionide/
|
||||
|
||||
# Fody - auto-generated XML schema
|
||||
FodyWeavers.xsd
|
||||
27
v2rayN/AmazTool/AmazTool.csproj
Normal file
27
v2rayN/AmazTool/AmazTool.csproj
Normal file
@@ -0,0 +1,27 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<Copyright>Copyright © 2017-2024 (GPLv3)</Copyright>
|
||||
<FileVersion>1.3.1</FileVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Resx\Resource.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resource.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Resx\Resource.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resource.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
29
v2rayN/AmazTool/Program.cs
Normal file
29
v2rayN/AmazTool/Program.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
namespace AmazTool
|
||||
{
|
||||
internal static class Program
|
||||
{
|
||||
/// <summary>
|
||||
/// 应用程序的主入口点。
|
||||
/// </summary>
|
||||
[STAThread]
|
||||
private static void Main(string[] args)
|
||||
{
|
||||
if (args.Length == 0)
|
||||
{
|
||||
Console.WriteLine(Resx.Resource.Guidelines);
|
||||
Thread.Sleep(5000);
|
||||
return;
|
||||
}
|
||||
|
||||
var argData = Uri.UnescapeDataString(string.Join(" ", args));
|
||||
if (argData.Equals("rebootas"))
|
||||
{
|
||||
Thread.Sleep(1000);
|
||||
Utils.StartV2RayN();
|
||||
return;
|
||||
}
|
||||
|
||||
UpgradeApp.Upgrade(argData);
|
||||
}
|
||||
}
|
||||
}
|
||||
171
v2rayN/AmazTool/Resx/Resource.Designer.cs
generated
Normal file
171
v2rayN/AmazTool/Resx/Resource.Designer.cs
generated
Normal file
@@ -0,0 +1,171 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// 此代码由工具生成。
|
||||
// 运行时版本:4.0.30319.42000
|
||||
//
|
||||
// 对此文件的更改可能会导致不正确的行为,并且如果
|
||||
// 重新生成代码,这些更改将会丢失。
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace AmazTool.Resx {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 一个强类型的资源类,用于查找本地化的字符串等。
|
||||
/// </summary>
|
||||
// 此类是由 StronglyTypedResourceBuilder
|
||||
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
|
||||
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
|
||||
// (以 /str 作为命令选项),或重新生成 VS 项目。
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resource {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resource() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回此类使用的缓存的 ResourceManager 实例。
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AmazTool.Resx.Resource", typeof(Resource).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重写当前线程的 CurrentUICulture 属性,对
|
||||
/// 使用此强类型资源类的所有资源查找执行重写。
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Failed to terminate the v2rayN.Close it manually,or the upgrade may fail. 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string FailedTerminateProcess {
|
||||
get {
|
||||
return ResourceManager.GetString("FailedTerminateProcess", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Failed to extract the update package. 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string FailedUnzipping {
|
||||
get {
|
||||
return ResourceManager.GetString("FailedUnzipping", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Upgrade failed. 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string FailedUpgrade {
|
||||
get {
|
||||
return ResourceManager.GetString("FailedUpgrade", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Please run it from the main application. 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string Guidelines {
|
||||
get {
|
||||
return ResourceManager.GetString("Guidelines", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Information 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string Information {
|
||||
get {
|
||||
return ResourceManager.GetString("Information", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 In progress, please wait... 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string InProgress {
|
||||
get {
|
||||
return ResourceManager.GetString("InProgress", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Start v2rayN, please wait... 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string Restartv2rayN {
|
||||
get {
|
||||
return ResourceManager.GetString("Restartv2rayN", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Start extracting the update package... 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string StartUnzipping {
|
||||
get {
|
||||
return ResourceManager.GetString("StartUnzipping", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Successfully extracted the update package. 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string SuccessUnzipping {
|
||||
get {
|
||||
return ResourceManager.GetString("SuccessUnzipping", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Upgrade success. 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string SuccessUpgrade {
|
||||
get {
|
||||
return ResourceManager.GetString("SuccessUpgrade", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Try to terminate the v2rayN process... 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string TryTerminateProcess {
|
||||
get {
|
||||
return ResourceManager.GetString("TryTerminateProcess", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Upgrade failed, file not found. 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string UpgradeFileNotFound {
|
||||
get {
|
||||
return ResourceManager.GetString("UpgradeFileNotFound", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,29 +117,40 @@
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<data name="btnRemove.Text" xml:space="preserve">
|
||||
<value>移除</value>
|
||||
<data name="Restartv2rayN" xml:space="preserve">
|
||||
<value>Start v2rayN, please wait...</value>
|
||||
</data>
|
||||
<data name="chkEnabled.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>48, 16</value>
|
||||
<data name="Guidelines" xml:space="preserve">
|
||||
<value>Please run it from the main application.</value>
|
||||
</data>
|
||||
<data name="chkEnabled.Text" xml:space="preserve">
|
||||
<value>启用</value>
|
||||
<data name="UpgradeFileNotFound" xml:space="preserve">
|
||||
<value>Upgrade failed, file not found.</value>
|
||||
</data>
|
||||
<data name="groupBox2.Text" xml:space="preserve">
|
||||
<value>订阅详情</value>
|
||||
<data name="InProgress" xml:space="preserve">
|
||||
<value>In progress, please wait...</value>
|
||||
</data>
|
||||
<data name="label2.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>29, 12</value>
|
||||
<data name="TryTerminateProcess" xml:space="preserve">
|
||||
<value>Try to terminate the v2rayN process...</value>
|
||||
</data>
|
||||
<data name="label2.Text" xml:space="preserve">
|
||||
<value>备注</value>
|
||||
<data name="FailedTerminateProcess" xml:space="preserve">
|
||||
<value>Failed to terminate the v2rayN.Close it manually,or the upgrade may fail.</value>
|
||||
</data>
|
||||
<data name="label3.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>65, 12</value>
|
||||
<data name="StartUnzipping" xml:space="preserve">
|
||||
<value>Start extracting the update package...</value>
|
||||
</data>
|
||||
<data name="label3.Text" xml:space="preserve">
|
||||
<value>地址 (url)</value>
|
||||
<data name="SuccessUnzipping" xml:space="preserve">
|
||||
<value>Successfully extracted the update package.</value>
|
||||
</data>
|
||||
<data name="FailedUnzipping" xml:space="preserve">
|
||||
<value>Failed to extract the update package.</value>
|
||||
</data>
|
||||
<data name="FailedUpgrade" xml:space="preserve">
|
||||
<value>Upgrade failed.</value>
|
||||
</data>
|
||||
<data name="SuccessUpgrade" xml:space="preserve">
|
||||
<value>Upgrade success.</value>
|
||||
</data>
|
||||
<data name="Information" xml:space="preserve">
|
||||
<value>Information</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
@@ -117,40 +117,40 @@
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="$this.Text" xml:space="preserve">
|
||||
<value>编辑或添加[Shadowsocks]服务器</value>
|
||||
<data name="Restartv2rayN" xml:space="preserve">
|
||||
<value>正在重启,请等待...</value>
|
||||
</data>
|
||||
<data name="btnClose.Text" xml:space="preserve">
|
||||
<value>取消(&C)</value>
|
||||
<data name="Guidelines" xml:space="preserve">
|
||||
<value>请从主应用运行。</value>
|
||||
</data>
|
||||
<data name="btnOK.Text" xml:space="preserve">
|
||||
<value>确定(&O)</value>
|
||||
<data name="UpgradeFileNotFound" xml:space="preserve">
|
||||
<value>升级失败,文件不存在。</value>
|
||||
</data>
|
||||
<data name="groupBox1.Text" xml:space="preserve">
|
||||
<value>服务器</value>
|
||||
<data name="InProgress" xml:space="preserve">
|
||||
<value>正在进行中,请等待...</value>
|
||||
</data>
|
||||
<data name="label1.Text" xml:space="preserve">
|
||||
<value>服务器地址</value>
|
||||
<data name="TryTerminateProcess" xml:space="preserve">
|
||||
<value>尝试结束 v2rayN 进程...</value>
|
||||
</data>
|
||||
<data name="label13.Text" xml:space="preserve">
|
||||
<value>*手填,方便识别管理</value>
|
||||
<data name="FailedTerminateProcess" xml:space="preserve">
|
||||
<value>请手动关闭正在运行的v2rayN,否则可能升级失败。</value>
|
||||
</data>
|
||||
<data name="label2.Text" xml:space="preserve">
|
||||
<value>服务器端口</value>
|
||||
<data name="StartUnzipping" xml:space="preserve">
|
||||
<value>开始解压缩更新包...</value>
|
||||
</data>
|
||||
<data name="label3.Text" xml:space="preserve">
|
||||
<value>密码</value>
|
||||
<data name="SuccessUnzipping" xml:space="preserve">
|
||||
<value>解压缩更新包成功。</value>
|
||||
</data>
|
||||
<data name="label5.Text" xml:space="preserve">
|
||||
<value>加密方式</value>
|
||||
<data name="FailedUnzipping" xml:space="preserve">
|
||||
<value>解压缩更新包失败。</value>
|
||||
</data>
|
||||
<data name="label6.Text" xml:space="preserve">
|
||||
<value>别名(remarks)</value>
|
||||
<data name="FailedUpgrade" xml:space="preserve">
|
||||
<value>升级失败。</value>
|
||||
</data>
|
||||
<data name="MenuItem1.Text" xml:space="preserve">
|
||||
<value>导入配置文件</value>
|
||||
<data name="SuccessUpgrade" xml:space="preserve">
|
||||
<value>升级成功。</value>
|
||||
</data>
|
||||
<data name="menuItemImportClipboard.Text" xml:space="preserve">
|
||||
<value>从剪贴板导入URL</value>
|
||||
<data name="Information" xml:space="preserve">
|
||||
<value>提示</value>
|
||||
</data>
|
||||
</root>
|
||||
108
v2rayN/AmazTool/UpgradeApp.cs
Normal file
108
v2rayN/AmazTool/UpgradeApp.cs
Normal file
@@ -0,0 +1,108 @@
|
||||
using System.Diagnostics;
|
||||
using System.IO.Compression;
|
||||
using System.Text;
|
||||
|
||||
namespace AmazTool
|
||||
{
|
||||
internal class UpgradeApp
|
||||
{
|
||||
public static void Upgrade(string fileName)
|
||||
{
|
||||
Console.WriteLine($"{Resx.Resource.StartUnzipping}\n{fileName}");
|
||||
|
||||
Waiting(3);
|
||||
|
||||
if (!File.Exists(fileName))
|
||||
{
|
||||
Console.WriteLine(Resx.Resource.UpgradeFileNotFound);
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine(Resx.Resource.TryTerminateProcess);
|
||||
try
|
||||
{
|
||||
var existing = Process.GetProcessesByName(Utils.V2rayN);
|
||||
foreach (var pp in existing)
|
||||
{
|
||||
var path = pp.MainModule?.FileName ?? "";
|
||||
if (path.StartsWith(Utils.GetPath(Utils.V2rayN)))
|
||||
{
|
||||
pp?.Kill();
|
||||
pp?.WaitForExit(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Access may be denied without admin right. The user may not be an administrator.
|
||||
Console.WriteLine(Resx.Resource.FailedTerminateProcess + ex.StackTrace);
|
||||
}
|
||||
|
||||
Console.WriteLine(Resx.Resource.StartUnzipping);
|
||||
StringBuilder sb = new();
|
||||
try
|
||||
{
|
||||
string thisAppOldFile = $"{Utils.GetExePath()}.tmp";
|
||||
File.Delete(thisAppOldFile);
|
||||
string splitKey = "/";
|
||||
|
||||
using ZipArchive archive = ZipFile.OpenRead(fileName);
|
||||
foreach (ZipArchiveEntry entry in archive.Entries)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (entry.Length == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Console.WriteLine(entry.FullName);
|
||||
|
||||
var lst = entry.FullName.Split(splitKey);
|
||||
if (lst.Length == 1) continue;
|
||||
string fullName = string.Join(splitKey, lst[1..lst.Length]);
|
||||
|
||||
if (string.Equals(Utils.GetExePath(), Utils.GetPath(fullName), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
File.Move(Utils.GetExePath(), thisAppOldFile);
|
||||
}
|
||||
|
||||
string entryOutputPath = Utils.GetPath(fullName);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(entryOutputPath)!);
|
||||
entry.ExtractToFile(entryOutputPath, true);
|
||||
|
||||
Console.WriteLine(entryOutputPath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
sb.Append(ex.StackTrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(Resx.Resource.FailedUpgrade + ex.StackTrace);
|
||||
//return;
|
||||
}
|
||||
if (sb.Length > 0)
|
||||
{
|
||||
Console.WriteLine(Resx.Resource.FailedUpgrade + sb.ToString());
|
||||
//return;
|
||||
}
|
||||
|
||||
Console.WriteLine(Resx.Resource.Restartv2rayN);
|
||||
Waiting(2);
|
||||
|
||||
Utils.StartV2RayN();
|
||||
}
|
||||
|
||||
public static void Waiting(int second)
|
||||
{
|
||||
for (var i = second; i > 0; i--)
|
||||
{
|
||||
Console.WriteLine(i);
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
43
v2rayN/AmazTool/Utils.cs
Normal file
43
v2rayN/AmazTool/Utils.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace AmazTool
|
||||
{
|
||||
internal class Utils
|
||||
{
|
||||
public static string GetExePath()
|
||||
{
|
||||
return Environment.ProcessPath ?? Process.GetCurrentProcess().MainModule?.FileName ?? string.Empty;
|
||||
}
|
||||
|
||||
public static string StartupPath()
|
||||
{
|
||||
return AppDomain.CurrentDomain.BaseDirectory;
|
||||
}
|
||||
|
||||
public static string GetPath(string fileName)
|
||||
{
|
||||
string startupPath = StartupPath();
|
||||
if (string.IsNullOrEmpty(fileName))
|
||||
{
|
||||
return startupPath;
|
||||
}
|
||||
return Path.Combine(startupPath, fileName);
|
||||
}
|
||||
|
||||
public static string V2rayN => "v2rayN";
|
||||
|
||||
public static void StartV2RayN()
|
||||
{
|
||||
Process process = new()
|
||||
{
|
||||
StartInfo = new()
|
||||
{
|
||||
UseShellExecute = true,
|
||||
FileName = V2rayN,
|
||||
WorkingDirectory = StartupPath()
|
||||
}
|
||||
};
|
||||
process.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
10
v2rayN/ServiceLib/Base/MyReactiveObject.cs
Normal file
10
v2rayN/ServiceLib/Base/MyReactiveObject.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using ReactiveUI;
|
||||
|
||||
namespace ServiceLib.Base
|
||||
{
|
||||
public class MyReactiveObject : ReactiveObject
|
||||
{
|
||||
protected static Config? _config;
|
||||
protected Func<EViewAction, object?, Task<bool>>? _updateView;
|
||||
}
|
||||
}
|
||||
101
v2rayN/ServiceLib/Common/AesUtils.cs
Normal file
101
v2rayN/ServiceLib/Common/AesUtils.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace ServiceLib.Common
|
||||
{
|
||||
public class AesUtils
|
||||
{
|
||||
private const int KeySize = 256; // AES-256
|
||||
private const int IvSize = 16; // AES block size
|
||||
private const int Iterations = 10000;
|
||||
private static readonly byte[] Salt = Encoding.ASCII.GetBytes("saltysalt".PadRight(16, ' ')); // google浏览器默认盐值
|
||||
private static readonly string DefaultPassword = Utils.GetMd5(Utils.GetHomePath() + "AesUtils");
|
||||
|
||||
/// <summary>
|
||||
/// Encrypt
|
||||
/// </summary>
|
||||
/// <param name="text">Plain text</param>
|
||||
/// <param name="password">Password for key derivation or direct key in ASCII bytes</param>
|
||||
/// <returns>Base64 encoded cipher text with IV</returns>
|
||||
public static string Encrypt(string text, string? password = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text))
|
||||
return string.Empty;
|
||||
|
||||
var plaintext = Encoding.UTF8.GetBytes(text);
|
||||
var key = GetKey(password);
|
||||
var iv = GenerateIv();
|
||||
|
||||
using var aes = Aes.Create();
|
||||
aes.Key = key;
|
||||
aes.IV = iv;
|
||||
|
||||
using var ms = new MemoryStream();
|
||||
ms.Write(iv, 0, iv.Length);
|
||||
|
||||
using (var cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
|
||||
{
|
||||
cs.Write(plaintext, 0, plaintext.Length);
|
||||
cs.FlushFinalBlock();
|
||||
}
|
||||
|
||||
var cipherTextWithIv = ms.ToArray();
|
||||
return Convert.ToBase64String(cipherTextWithIv);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypt
|
||||
/// </summary>
|
||||
/// <param name="cipherTextWithIv">Base64 encoded cipher text with IV</param>
|
||||
/// <param name="password">Password for key derivation or direct key in ASCII bytes</param>
|
||||
/// <returns>Plain text</returns>
|
||||
public static string Decrypt(string cipherTextWithIv, string? password = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(cipherTextWithIv))
|
||||
return string.Empty;
|
||||
|
||||
var cipherTextWithIvBytes = Convert.FromBase64String(cipherTextWithIv);
|
||||
var key = GetKey(password);
|
||||
|
||||
var iv = new byte[IvSize];
|
||||
Buffer.BlockCopy(cipherTextWithIvBytes, 0, iv, 0, IvSize);
|
||||
|
||||
var cipherText = new byte[cipherTextWithIvBytes.Length - IvSize];
|
||||
Buffer.BlockCopy(cipherTextWithIvBytes, IvSize, cipherText, 0, cipherText.Length);
|
||||
|
||||
using var aes = Aes.Create();
|
||||
aes.Key = key;
|
||||
aes.IV = iv;
|
||||
|
||||
using var ms = new MemoryStream();
|
||||
using (var cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write))
|
||||
{
|
||||
cs.Write(cipherText, 0, cipherText.Length);
|
||||
cs.FlushFinalBlock();
|
||||
}
|
||||
|
||||
var plainText = ms.ToArray();
|
||||
return Encoding.UTF8.GetString(plainText);
|
||||
}
|
||||
|
||||
private static byte[] GetKey(string? password)
|
||||
{
|
||||
if (password.IsNullOrEmpty())
|
||||
{
|
||||
password = DefaultPassword;
|
||||
}
|
||||
|
||||
using var pbkdf2 = new Rfc2898DeriveBytes(password, Salt, Iterations, HashAlgorithmName.SHA256);
|
||||
return pbkdf2.GetBytes(KeySize / 8);
|
||||
}
|
||||
|
||||
private static byte[] GenerateIv()
|
||||
{
|
||||
var randomNumber = new byte[IvSize];
|
||||
|
||||
using var rng = RandomNumberGenerator.Create();
|
||||
rng.GetBytes(randomNumber);
|
||||
return randomNumber;
|
||||
}
|
||||
}
|
||||
}
|
||||
75
v2rayN/ServiceLib/Common/DesUtils.cs
Normal file
75
v2rayN/ServiceLib/Common/DesUtils.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace ServiceLib.Common
|
||||
{
|
||||
public class DesUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Encrypt
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// /// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public static string Encrypt(string? text, string? key = null)
|
||||
{
|
||||
if (text.IsNullOrEmpty())
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
GetKeyIv(key ?? GetDefaultKey(), out var rgbKey, out var rgbIv);
|
||||
var dsp = DES.Create();
|
||||
using var memStream = new MemoryStream();
|
||||
using var cryStream = new CryptoStream(memStream, dsp.CreateEncryptor(rgbKey, rgbIv), CryptoStreamMode.Write);
|
||||
using var sWriter = new StreamWriter(cryStream);
|
||||
sWriter.Write(text);
|
||||
sWriter.Flush();
|
||||
cryStream.FlushFinalBlock();
|
||||
memStream.Flush();
|
||||
return Convert.ToBase64String(memStream.GetBuffer(), 0, (int)memStream.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypt
|
||||
/// </summary>
|
||||
/// <param name="encryptText"></param>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public static string Decrypt(string? encryptText, string? key = null)
|
||||
{
|
||||
if (encryptText.IsNullOrEmpty())
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
GetKeyIv(key ?? GetDefaultKey(), out var rgbKey, out var rgbIv);
|
||||
var dsp = DES.Create();
|
||||
var buffer = Convert.FromBase64String(encryptText);
|
||||
|
||||
using var memStream = new MemoryStream();
|
||||
using var cryStream = new CryptoStream(memStream, dsp.CreateDecryptor(rgbKey, rgbIv), CryptoStreamMode.Write);
|
||||
cryStream.Write(buffer, 0, buffer.Length);
|
||||
cryStream.FlushFinalBlock();
|
||||
return Encoding.UTF8.GetString(memStream.ToArray());
|
||||
}
|
||||
|
||||
private static void GetKeyIv(string key, out byte[] rgbKey, out byte[] rgbIv)
|
||||
{
|
||||
if (key.IsNullOrEmpty())
|
||||
{
|
||||
throw new ArgumentNullException("The key cannot be null");
|
||||
}
|
||||
if (key.Length <= 8)
|
||||
{
|
||||
throw new ArgumentNullException("The key length cannot be less than 8 characters.");
|
||||
}
|
||||
|
||||
rgbKey = Encoding.ASCII.GetBytes(key.Substring(0, 8));
|
||||
rgbIv = Encoding.ASCII.GetBytes(key.Insert(0, "w").Substring(0, 8));
|
||||
}
|
||||
|
||||
private static string GetDefaultKey()
|
||||
{
|
||||
return Utils.GetMd5(Utils.GetHomePath() + "DesUtils");
|
||||
}
|
||||
}
|
||||
}
|
||||
184
v2rayN/ServiceLib/Common/DownloaderHelper.cs
Normal file
184
v2rayN/ServiceLib/Common/DownloaderHelper.cs
Normal file
@@ -0,0 +1,184 @@
|
||||
using Downloader;
|
||||
using System.Net;
|
||||
|
||||
namespace ServiceLib.Common
|
||||
{
|
||||
public class DownloaderHelper
|
||||
{
|
||||
private static readonly Lazy<DownloaderHelper> _instance = new(() => new());
|
||||
public static DownloaderHelper Instance => _instance.Value;
|
||||
|
||||
public async Task<string?> DownloadStringAsync(IWebProxy? webProxy, string url, string? userAgent, int timeout)
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(url))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Uri uri = new(url);
|
||||
//Authorization Header
|
||||
var headers = new WebHeaderCollection();
|
||||
if (Utils.IsNotEmpty(uri.UserInfo))
|
||||
{
|
||||
headers.Add(HttpRequestHeader.Authorization, "Basic " + Utils.Base64Encode(uri.UserInfo));
|
||||
}
|
||||
|
||||
var downloadOpt = new DownloadConfiguration()
|
||||
{
|
||||
Timeout = timeout * 1000,
|
||||
MaxTryAgainOnFailover = 2,
|
||||
RequestConfiguration =
|
||||
{
|
||||
Headers = headers,
|
||||
UserAgent = userAgent,
|
||||
Timeout = timeout * 1000,
|
||||
Proxy = webProxy
|
||||
}
|
||||
};
|
||||
|
||||
await using var downloader = new Downloader.DownloadService(downloadOpt);
|
||||
downloader.DownloadFileCompleted += (sender, value) =>
|
||||
{
|
||||
if (value.Error != null)
|
||||
{
|
||||
throw value.Error;
|
||||
}
|
||||
};
|
||||
|
||||
using var cts = new CancellationTokenSource();
|
||||
await using var stream = await downloader.DownloadFileTaskAsync(address: url, cts.Token).WaitAsync(TimeSpan.FromSeconds(timeout), cts.Token);
|
||||
using StreamReader reader = new(stream);
|
||||
|
||||
downloadOpt = null;
|
||||
|
||||
return await reader.ReadToEndAsync(cts.Token);
|
||||
}
|
||||
|
||||
public async Task DownloadDataAsync4Speed(IWebProxy webProxy, string url, IProgress<string> progress, int timeout)
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(url))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(url));
|
||||
}
|
||||
|
||||
var downloadOpt = new DownloadConfiguration()
|
||||
{
|
||||
Timeout = timeout * 1000,
|
||||
MaxTryAgainOnFailover = 2,
|
||||
RequestConfiguration =
|
||||
{
|
||||
Timeout= timeout * 1000,
|
||||
Proxy = webProxy
|
||||
}
|
||||
};
|
||||
|
||||
var totalDatetime = DateTime.Now;
|
||||
var totalSecond = 0;
|
||||
var hasValue = false;
|
||||
double maxSpeed = 0;
|
||||
await using var downloader = new Downloader.DownloadService(downloadOpt);
|
||||
//downloader.DownloadStarted += (sender, value) =>
|
||||
//{
|
||||
// if (progress != null)
|
||||
// {
|
||||
// progress.Report("Start download data...");
|
||||
// }
|
||||
//};
|
||||
downloader.DownloadProgressChanged += (sender, value) =>
|
||||
{
|
||||
var ts = (DateTime.Now - totalDatetime);
|
||||
if (progress != null && ts.Seconds > totalSecond)
|
||||
{
|
||||
hasValue = true;
|
||||
totalSecond = ts.Seconds;
|
||||
if (value.BytesPerSecondSpeed > maxSpeed)
|
||||
{
|
||||
maxSpeed = value.BytesPerSecondSpeed;
|
||||
var speed = (maxSpeed / 1000 / 1000).ToString("#0.0");
|
||||
progress.Report(speed);
|
||||
}
|
||||
}
|
||||
};
|
||||
downloader.DownloadFileCompleted += (sender, value) =>
|
||||
{
|
||||
if (progress != null)
|
||||
{
|
||||
if (!hasValue && value.Error != null)
|
||||
{
|
||||
progress.Report(value.Error?.Message);
|
||||
}
|
||||
}
|
||||
};
|
||||
//progress.Report("......");
|
||||
using var cts = new CancellationTokenSource();
|
||||
cts.CancelAfter(timeout * 1000);
|
||||
await using var stream = await downloader.DownloadFileTaskAsync(address: url, cts.Token);
|
||||
|
||||
downloadOpt = null;
|
||||
}
|
||||
|
||||
public async Task DownloadFileAsync(IWebProxy? webProxy, string url, string fileName, IProgress<double> progress, int timeout)
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(url))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(url));
|
||||
}
|
||||
if (Utils.IsNullOrEmpty(fileName))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(fileName));
|
||||
}
|
||||
if (File.Exists(fileName))
|
||||
{
|
||||
File.Delete(fileName);
|
||||
}
|
||||
|
||||
var downloadOpt = new DownloadConfiguration()
|
||||
{
|
||||
Timeout = timeout * 1000,
|
||||
MaxTryAgainOnFailover = 2,
|
||||
RequestConfiguration =
|
||||
{
|
||||
Timeout= timeout * 1000,
|
||||
Proxy = webProxy
|
||||
}
|
||||
};
|
||||
|
||||
var progressPercentage = 0;
|
||||
var hasValue = false;
|
||||
await using var downloader = new Downloader.DownloadService(downloadOpt);
|
||||
downloader.DownloadStarted += (sender, value) =>
|
||||
{
|
||||
progress?.Report(0);
|
||||
};
|
||||
downloader.DownloadProgressChanged += (sender, value) =>
|
||||
{
|
||||
hasValue = true;
|
||||
var percent = (int)value.ProgressPercentage;// Convert.ToInt32((totalRead * 1d) / (total * 1d) * 100);
|
||||
if (progressPercentage != percent && percent % 10 == 0)
|
||||
{
|
||||
progressPercentage = percent;
|
||||
progress.Report(percent);
|
||||
}
|
||||
};
|
||||
downloader.DownloadFileCompleted += (sender, value) =>
|
||||
{
|
||||
if (progress != null)
|
||||
{
|
||||
if (hasValue && value.Error == null)
|
||||
{
|
||||
progress.Report(101);
|
||||
}
|
||||
else if (value.Error != null)
|
||||
{
|
||||
throw value.Error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
using var cts = new CancellationTokenSource();
|
||||
await downloader.DownloadFileTaskAsync(url, fileName, cts.Token);
|
||||
|
||||
downloadOpt = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
200
v2rayN/ServiceLib/Common/FileManager.cs
Normal file
200
v2rayN/ServiceLib/Common/FileManager.cs
Normal file
@@ -0,0 +1,200 @@
|
||||
using System.Formats.Tar;
|
||||
using System.IO.Compression;
|
||||
using System.Text;
|
||||
|
||||
namespace ServiceLib.Common
|
||||
{
|
||||
public static class FileManager
|
||||
{
|
||||
private static readonly string _tag = "FileManager";
|
||||
|
||||
public static bool ByteArrayToFile(string fileName, byte[] content)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.WriteAllBytes(fileName, content);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void DecompressFile(string fileName, byte[] content)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var fs = File.Create(fileName);
|
||||
using GZipStream input = new(new MemoryStream(content), CompressionMode.Decompress, false);
|
||||
input.CopyTo(fs);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DecompressFile(string fileName, string toPath, string? toName)
|
||||
{
|
||||
try
|
||||
{
|
||||
FileInfo fileInfo = new(fileName);
|
||||
using var originalFileStream = fileInfo.OpenRead();
|
||||
using var decompressedFileStream = File.Create(toName != null ? Path.Combine(toPath, toName) : toPath);
|
||||
using GZipStream decompressionStream = new(originalFileStream, CompressionMode.Decompress);
|
||||
decompressionStream.CopyTo(decompressedFileStream);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DecompressTarFile(string fileName, string toPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
|
||||
using var gz = new GZipStream(fs, CompressionMode.Decompress, leaveOpen: true);
|
||||
TarFile.ExtractToDirectory(gz, toPath, overwriteFiles: true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static string NonExclusiveReadAllText(string path)
|
||||
{
|
||||
return NonExclusiveReadAllText(path, Encoding.Default);
|
||||
}
|
||||
|
||||
private static string NonExclusiveReadAllText(string path, Encoding encoding)
|
||||
{
|
||||
try
|
||||
{
|
||||
using FileStream fs = new(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
using StreamReader sr = new(fs, encoding);
|
||||
return sr.ReadToEnd();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ZipExtractToFile(string fileName, string toPath, string ignoredName)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var archive = ZipFile.OpenRead(fileName);
|
||||
foreach (var entry in archive.Entries)
|
||||
{
|
||||
if (entry.Length == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
try
|
||||
{
|
||||
if (Utils.IsNotEmpty(ignoredName) && entry.Name.Contains(ignoredName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
entry.ExtractToFile(Path.Combine(toPath, entry.Name), true);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static List<string>? GetFilesFromZip(string fileName)
|
||||
{
|
||||
if (!File.Exists(fileName))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
try
|
||||
{
|
||||
using var archive = ZipFile.OpenRead(fileName);
|
||||
return archive.Entries.Select(entry => entry.FullName).ToList();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool CreateFromDirectory(string sourceDirectoryName, string destinationArchiveFileName)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(destinationArchiveFileName))
|
||||
{
|
||||
File.Delete(destinationArchiveFileName);
|
||||
}
|
||||
|
||||
ZipFile.CreateFromDirectory(sourceDirectoryName, destinationArchiveFileName, CompressionLevel.SmallestSize, true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void CopyDirectory(string sourceDir, string destinationDir, bool recursive, string? ignoredName)
|
||||
{
|
||||
// Get information about the source directory
|
||||
var dir = new DirectoryInfo(sourceDir);
|
||||
|
||||
// Check if the source directory exists
|
||||
if (!dir.Exists)
|
||||
throw new DirectoryNotFoundException($"Source directory not found: {dir.FullName}");
|
||||
|
||||
// Cache directories before we start copying
|
||||
var dirs = dir.GetDirectories();
|
||||
|
||||
// Create the destination directory
|
||||
Directory.CreateDirectory(destinationDir);
|
||||
|
||||
// Get the files in the source directory and copy to the destination directory
|
||||
foreach (var file in dir.GetFiles())
|
||||
{
|
||||
if (Utils.IsNotEmpty(ignoredName) && file.Name.Contains(ignoredName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (file.Extension == file.Name)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var targetFilePath = Path.Combine(destinationDir, file.Name);
|
||||
file.CopyTo(targetFilePath, true);
|
||||
}
|
||||
|
||||
// If recursive and copying subdirectories, recursively call this method
|
||||
if (recursive)
|
||||
{
|
||||
foreach (var subDir in dirs)
|
||||
{
|
||||
var newDestinationDir = Path.Combine(destinationDir, subDir.Name);
|
||||
CopyDirectory(subDir.FullName, newDestinationDir, true, ignoredName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
186
v2rayN/ServiceLib/Common/HttpClientHelper.cs
Normal file
186
v2rayN/ServiceLib/Common/HttpClientHelper.cs
Normal file
@@ -0,0 +1,186 @@
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Mime;
|
||||
using System.Text;
|
||||
|
||||
namespace ServiceLib.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
public class HttpClientHelper
|
||||
{
|
||||
private static readonly Lazy<HttpClientHelper> _instance = new(() =>
|
||||
{
|
||||
SocketsHttpHandler handler = new() { UseCookies = false };
|
||||
HttpClientHelper helper = new(new HttpClient(handler));
|
||||
return helper;
|
||||
});
|
||||
|
||||
public static HttpClientHelper Instance => _instance.Value;
|
||||
private readonly HttpClient httpClient;
|
||||
|
||||
private HttpClientHelper(HttpClient httpClient) => this.httpClient = httpClient;
|
||||
|
||||
public async Task<string?> TryGetAsync(string url)
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(url))
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
var response = await httpClient.GetAsync(url);
|
||||
return await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<string?> GetAsync(string url)
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(url)) return null;
|
||||
return await httpClient.GetStringAsync(url);
|
||||
}
|
||||
|
||||
public async Task<string?> GetAsync(HttpClient client, string url, CancellationToken token = default)
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(url)) return null;
|
||||
return await client.GetStringAsync(url, token);
|
||||
}
|
||||
|
||||
public async Task PutAsync(string url, Dictionary<string, string> headers)
|
||||
{
|
||||
var jsonContent = JsonUtils.Serialize(headers);
|
||||
var content = new StringContent(jsonContent, Encoding.UTF8, MediaTypeNames.Application.Json);
|
||||
|
||||
var result = await httpClient.PutAsync(url, content);
|
||||
}
|
||||
|
||||
public async Task PatchAsync(string url, Dictionary<string, string> headers)
|
||||
{
|
||||
var myContent = JsonUtils.Serialize(headers);
|
||||
var buffer = System.Text.Encoding.UTF8.GetBytes(myContent);
|
||||
var byteContent = new ByteArrayContent(buffer);
|
||||
byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
|
||||
|
||||
await httpClient.PatchAsync(url, byteContent);
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(string url)
|
||||
{
|
||||
await httpClient.DeleteAsync(url);
|
||||
}
|
||||
|
||||
public static async Task DownloadFileAsync(HttpClient client, string url, string fileName, IProgress<double>? progress, CancellationToken token = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(url);
|
||||
ArgumentNullException.ThrowIfNull(fileName);
|
||||
if (File.Exists(fileName)) File.Delete(fileName);
|
||||
|
||||
using var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token);
|
||||
|
||||
if (!response.IsSuccessStatusCode) throw new Exception(response.StatusCode.ToString());
|
||||
|
||||
var total = response.Content.Headers.ContentLength ?? -1L;
|
||||
var canReportProgress = total != -1 && progress != null;
|
||||
|
||||
await using var stream = await response.Content.ReadAsStreamAsync(token);
|
||||
await using var file = File.Create(fileName);
|
||||
var totalRead = 0L;
|
||||
var buffer = new byte[1024 * 1024];
|
||||
var progressPercentage = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
var read = await stream.ReadAsync(buffer, token);
|
||||
totalRead += read;
|
||||
|
||||
if (read == 0) break;
|
||||
await file.WriteAsync(buffer.AsMemory(0, read), token);
|
||||
|
||||
if (canReportProgress)
|
||||
{
|
||||
var percent = (int)(100.0 * totalRead / total);
|
||||
//if (progressPercentage != percent && percent % 10 == 0)
|
||||
{
|
||||
progressPercentage = percent;
|
||||
progress?.Report(percent);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (canReportProgress)
|
||||
{
|
||||
progress?.Report(101);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DownloadDataAsync4Speed(HttpClient client, string url, IProgress<string> progress, CancellationToken token = default)
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(url))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(url));
|
||||
}
|
||||
|
||||
var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
throw new Exception(response.StatusCode.ToString());
|
||||
}
|
||||
|
||||
//var total = response.Content.Headers.ContentLength.HasValue ? response.Content.Headers.ContentLength.Value : -1L;
|
||||
//var canReportProgress = total != -1 && progress != null;
|
||||
|
||||
await using var stream = await response.Content.ReadAsStreamAsync(token);
|
||||
var totalRead = 0L;
|
||||
var buffer = new byte[1024 * 64];
|
||||
var isMoreToRead = true;
|
||||
var progressSpeed = string.Empty;
|
||||
var totalDatetime = DateTime.Now;
|
||||
var totalSecond = 0;
|
||||
|
||||
do
|
||||
{
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
if (totalRead > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
token.ThrowIfCancellationRequested();
|
||||
}
|
||||
}
|
||||
|
||||
var read = await stream.ReadAsync(buffer, token);
|
||||
|
||||
if (read == 0)
|
||||
{
|
||||
isMoreToRead = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
var data = new byte[read];
|
||||
buffer.ToList().CopyTo(0, data, 0, read);
|
||||
|
||||
totalRead += read;
|
||||
|
||||
var ts = (DateTime.Now - totalDatetime);
|
||||
if (progress != null && ts.Seconds > totalSecond)
|
||||
{
|
||||
totalSecond = ts.Seconds;
|
||||
var speed = (totalRead * 1d / ts.TotalMilliseconds / 1000).ToString("#0.0");
|
||||
if (progressSpeed != speed)
|
||||
{
|
||||
progressSpeed = speed;
|
||||
progress.Report(speed);
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (isMoreToRead);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,14 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
|
||||
namespace v2rayN
|
||||
namespace ServiceLib.Common
|
||||
{
|
||||
/*
|
||||
* See:
|
||||
* http://stackoverflow.com/questions/6266820/working-example-of-createjobobject-setinformationjobobject-pinvoke-in-net
|
||||
*/
|
||||
public class Job : IDisposable
|
||||
|
||||
public sealed class Job : IDisposable
|
||||
{
|
||||
private IntPtr handle = IntPtr.Zero;
|
||||
|
||||
@@ -17,12 +16,12 @@ namespace v2rayN
|
||||
{
|
||||
handle = CreateJobObject(IntPtr.Zero, null);
|
||||
IntPtr extendedInfoPtr = IntPtr.Zero;
|
||||
JOBOBJECT_BASIC_LIMIT_INFORMATION info = new JOBOBJECT_BASIC_LIMIT_INFORMATION
|
||||
JOBOBJECT_BASIC_LIMIT_INFORMATION info = new()
|
||||
{
|
||||
LimitFlags = 0x2000
|
||||
};
|
||||
|
||||
JOBOBJECT_EXTENDED_LIMIT_INFORMATION extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION
|
||||
JOBOBJECT_EXTENDED_LIMIT_INFORMATION extendedInfo = new()
|
||||
{
|
||||
BasicLimitInformation = info
|
||||
};
|
||||
@@ -34,7 +33,7 @@ namespace v2rayN
|
||||
Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false);
|
||||
|
||||
if (!SetInformationJobObject(handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr,
|
||||
(uint) length))
|
||||
(uint)length))
|
||||
throw new Exception(string.Format("Unable to set information. Error: {0}",
|
||||
Marshal.GetLastWin32Error()));
|
||||
}
|
||||
@@ -53,7 +52,7 @@ namespace v2rayN
|
||||
|
||||
if (!succ)
|
||||
{
|
||||
Utils.SaveLog("Failed to call AssignProcessToJobObject! GetLastError=" + Marshal.GetLastWin32Error());
|
||||
Logging.SaveLog("Failed to call AssignProcessToJobObject! GetLastError=" + Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
return succ;
|
||||
@@ -74,7 +73,7 @@ namespace v2rayN
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (disposed) return;
|
||||
disposed = true;
|
||||
@@ -96,12 +95,12 @@ namespace v2rayN
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion IDisposable
|
||||
|
||||
#region Interop
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
|
||||
private static extern IntPtr CreateJobObject(IntPtr a, string lpName);
|
||||
private static extern IntPtr CreateJobObject(IntPtr a, string? lpName);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, UInt32 cbJobObjectInfoLength);
|
||||
@@ -113,13 +112,13 @@ namespace v2rayN
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool CloseHandle(IntPtr hObject);
|
||||
|
||||
#endregion
|
||||
#endregion Interop
|
||||
}
|
||||
|
||||
#region Helper classes
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct IO_COUNTERS
|
||||
internal struct IO_COUNTERS
|
||||
{
|
||||
public UInt64 ReadOperationCount;
|
||||
public UInt64 WriteOperationCount;
|
||||
@@ -129,9 +128,8 @@ namespace v2rayN
|
||||
public UInt64 OtherTransferCount;
|
||||
}
|
||||
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct JOBOBJECT_BASIC_LIMIT_INFORMATION
|
||||
internal struct JOBOBJECT_BASIC_LIMIT_INFORMATION
|
||||
{
|
||||
public Int64 PerProcessUserTimeLimit;
|
||||
public Int64 PerJobUserTimeLimit;
|
||||
@@ -153,7 +151,7 @@ namespace v2rayN
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION
|
||||
internal struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION
|
||||
{
|
||||
public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;
|
||||
public IO_COUNTERS IoInfo;
|
||||
@@ -174,5 +172,5 @@ namespace v2rayN
|
||||
GroupInformation = 11
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
#endregion Helper classes
|
||||
}
|
||||
107
v2rayN/ServiceLib/Common/JsonUtils.cs
Normal file
107
v2rayN/ServiceLib/Common/JsonUtils.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace ServiceLib.Common
|
||||
{
|
||||
public class JsonUtils
|
||||
{
|
||||
private static readonly string _tag = "JsonUtils";
|
||||
|
||||
/// <summary>
|
||||
/// DeepCopy
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="obj"></param>
|
||||
/// <returns></returns>
|
||||
public static T DeepCopy<T>(T obj)
|
||||
{
|
||||
return Deserialize<T>(Serialize(obj, false))!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserialize to object
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="strJson"></param>
|
||||
/// <returns></returns>
|
||||
public static T? Deserialize<T>(string? strJson)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(strJson))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
var options = new JsonSerializerOptions
|
||||
{
|
||||
PropertyNameCaseInsensitive = true
|
||||
};
|
||||
return JsonSerializer.Deserialize<T>(strJson, options);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// parse
|
||||
/// </summary>
|
||||
/// <param name="strJson"></param>
|
||||
/// <returns></returns>
|
||||
public static JsonNode? ParseJson(string strJson)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(strJson))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return JsonNode.Parse(strJson);
|
||||
}
|
||||
catch
|
||||
{
|
||||
//SaveLog(ex.Message, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serialize Object to Json string
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <param name="indented"></param>
|
||||
/// <param name="nullValue"></param>
|
||||
/// <returns></returns>
|
||||
public static string Serialize(object? obj, bool indented = true, bool nullValue = false)
|
||||
{
|
||||
var result = string.Empty;
|
||||
try
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
var options = new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = indented,
|
||||
DefaultIgnoreCondition = nullValue ? JsonIgnoreCondition.Never : JsonIgnoreCondition.WhenWritingNull
|
||||
};
|
||||
result = JsonSerializer.Serialize(obj, options);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SerializeToNode
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <returns></returns>
|
||||
public static JsonNode? SerializeToNode(object? obj) => JsonSerializer.SerializeToNode(obj);
|
||||
}
|
||||
}
|
||||
78
v2rayN/ServiceLib/Common/Logging.cs
Normal file
78
v2rayN/ServiceLib/Common/Logging.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
using NLog;
|
||||
using NLog.Config;
|
||||
using NLog.Targets;
|
||||
|
||||
namespace ServiceLib.Common
|
||||
{
|
||||
public class Logging
|
||||
{
|
||||
public static void Setup()
|
||||
{
|
||||
LoggingConfiguration config = new();
|
||||
FileTarget fileTarget = new();
|
||||
config.AddTarget("file", fileTarget);
|
||||
fileTarget.Layout = "${longdate}-${level:uppercase=true} ${message}";
|
||||
fileTarget.FileName = Utils.GetLogPath("${shortdate}.txt");
|
||||
config.LoggingRules.Add(new LoggingRule("*", LogLevel.Debug, fileTarget));
|
||||
LogManager.Configuration = config;
|
||||
}
|
||||
|
||||
public static void LoggingEnabled(bool enable)
|
||||
{
|
||||
if (!enable)
|
||||
{
|
||||
LogManager.SuspendLogging();
|
||||
}
|
||||
}
|
||||
|
||||
public static void ClearLogs()
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var now = DateTime.Now.AddMonths(-1);
|
||||
var dir = Utils.GetLogPath();
|
||||
var files = Directory.GetFiles(dir, "*.txt");
|
||||
foreach (var filePath in files)
|
||||
{
|
||||
var file = new FileInfo(filePath);
|
||||
if (file.CreationTime >= now) continue;
|
||||
try
|
||||
{
|
||||
file.Delete();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void SaveLog(string strContent)
|
||||
{
|
||||
if (!LogManager.IsLoggingEnabled()) return;
|
||||
|
||||
LogManager.GetLogger("Log1").Info(strContent);
|
||||
}
|
||||
|
||||
public static void SaveLog(string strTitle, Exception ex)
|
||||
{
|
||||
if (!LogManager.IsLoggingEnabled()) return;
|
||||
|
||||
var logger = LogManager.GetLogger("Log2");
|
||||
logger.Debug($"{strTitle},{ex.Message}");
|
||||
logger.Debug(ex.StackTrace);
|
||||
if (ex?.InnerException != null)
|
||||
{
|
||||
logger.Error(ex.InnerException);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
118
v2rayN/ServiceLib/Common/ProcUtils.cs
Normal file
118
v2rayN/ServiceLib/Common/ProcUtils.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace ServiceLib.Common;
|
||||
|
||||
public static class ProcUtils
|
||||
{
|
||||
private static readonly string _tag = "ProcUtils";
|
||||
|
||||
public static void ProcessStart(string? fileName, string arguments = "")
|
||||
{
|
||||
ProcessStart(fileName, arguments, null);
|
||||
}
|
||||
|
||||
public static int? ProcessStart(string? fileName, string arguments, string? dir)
|
||||
{
|
||||
if (fileName.IsNullOrEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
try
|
||||
{
|
||||
if (fileName.Contains(' ')) fileName = fileName.AppendQuotes();
|
||||
if (arguments.Contains(' ')) arguments = arguments.AppendQuotes();
|
||||
|
||||
Process process = new()
|
||||
{
|
||||
StartInfo = new ProcessStartInfo
|
||||
{
|
||||
UseShellExecute = true,
|
||||
FileName = fileName,
|
||||
Arguments = arguments,
|
||||
WorkingDirectory = dir
|
||||
}
|
||||
};
|
||||
process.Start();
|
||||
return process.Id;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void RebootAsAdmin(bool blAdmin = true)
|
||||
{
|
||||
try
|
||||
{
|
||||
ProcessStartInfo startInfo = new()
|
||||
{
|
||||
UseShellExecute = true,
|
||||
Arguments = Global.RebootAs,
|
||||
WorkingDirectory = Utils.StartupPath(),
|
||||
FileName = Utils.GetExePath().AppendQuotes(),
|
||||
Verb = blAdmin ? "runas" : null,
|
||||
};
|
||||
Process.Start(startInfo);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task ProcessKill(int pid)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ProcessKill(Process.GetProcessById(pid), false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task ProcessKill(Process? proc, bool review)
|
||||
{
|
||||
if (proc is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var procId = review ? proc?.Id : null;
|
||||
var fileName = review ? proc?.MainModule?.FileName : null;
|
||||
var processName = review ? proc?.ProcessName : null;
|
||||
|
||||
try { proc?.Kill(true); } catch (Exception ex) { Logging.SaveLog(_tag, ex); }
|
||||
try { proc?.Kill(); } catch (Exception ex) { Logging.SaveLog(_tag, ex); }
|
||||
try { proc?.Close(); } catch (Exception ex) { Logging.SaveLog(_tag, ex); }
|
||||
try { proc?.Dispose(); } catch (Exception ex) { Logging.SaveLog(_tag, ex); }
|
||||
|
||||
await Task.Delay(300);
|
||||
if (review && procId != null && fileName != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var lstProc = Process.GetProcessesByName(processName);
|
||||
foreach (var proc2 in lstProc)
|
||||
{
|
||||
if (proc2.Id == procId)
|
||||
{
|
||||
Logging.SaveLog($"{_tag}, KillProcess not completing the job, procId");
|
||||
await ProcessKill(proc2, false);
|
||||
}
|
||||
if (proc2.MainModule != null && proc2.MainModule?.FileName == fileName)
|
||||
{
|
||||
Logging.SaveLog($"{_tag}, KillProcess not completing the job, fileName");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
90
v2rayN/ServiceLib/Common/QRCodeHelper.cs
Normal file
90
v2rayN/ServiceLib/Common/QRCodeHelper.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
using QRCoder;
|
||||
using SkiaSharp;
|
||||
using ZXing.SkiaSharp;
|
||||
|
||||
namespace ServiceLib.Common
|
||||
{
|
||||
public class QRCodeHelper
|
||||
{
|
||||
public static byte[]? GenQRCode(string? url)
|
||||
{
|
||||
using QRCodeGenerator qrGenerator = new();
|
||||
using var qrCodeData = qrGenerator.CreateQrCode(url ?? string.Empty, QRCodeGenerator.ECCLevel.Q);
|
||||
using PngByteQRCode qrCode = new(qrCodeData);
|
||||
return qrCode.GetGraphic(20);
|
||||
}
|
||||
|
||||
public static string? ParseBarcode(string? fileName)
|
||||
{
|
||||
if (fileName == null || !File.Exists(fileName))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var image = SKImage.FromEncodedData(fileName);
|
||||
var bitmap = SKBitmap.FromImage(image);
|
||||
|
||||
return ReaderBarcode(bitmap);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string? ParseBarcode(byte[]? bytes)
|
||||
{
|
||||
try
|
||||
{
|
||||
var bitmap = SKBitmap.Decode(bytes);
|
||||
//using var stream = new FileStream("test2.png", FileMode.Create, FileAccess.Write);
|
||||
//using var image = SKImage.FromBitmap(bitmap);
|
||||
//using var encodedImage = image.Encode();
|
||||
//encodedImage.SaveTo(stream);
|
||||
return ReaderBarcode(bitmap);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string? ReaderBarcode(SKBitmap? bitmap)
|
||||
{
|
||||
var reader = new BarcodeReader();
|
||||
var result = reader.Decode(bitmap);
|
||||
|
||||
if (result != null && Utils.IsNotEmpty(result.Text))
|
||||
{
|
||||
return result.Text;
|
||||
}
|
||||
|
||||
//FlipBitmap
|
||||
var result2 = reader.Decode(FlipBitmap(bitmap));
|
||||
return result2?.Text;
|
||||
}
|
||||
|
||||
private static SKBitmap FlipBitmap(SKBitmap bmp)
|
||||
{
|
||||
// Create a bitmap (to return)
|
||||
var flipped = new SKBitmap(bmp.Width, bmp.Height, bmp.Info.ColorType, bmp.Info.AlphaType);
|
||||
|
||||
// Create a canvas to draw into the bitmap
|
||||
using var canvas = new SKCanvas(flipped);
|
||||
|
||||
// Set a transform matrix which moves the bitmap to the right,
|
||||
// and then "scales" it by -1, which just flips the pixels
|
||||
// horizontally
|
||||
canvas.Translate(bmp.Width, 0);
|
||||
canvas.Scale(-1, 1);
|
||||
canvas.DrawBitmap(bmp, 0, 0);
|
||||
return flipped;
|
||||
}
|
||||
}
|
||||
}
|
||||
187
v2rayN/ServiceLib/Common/SemanticVersion.cs
Normal file
187
v2rayN/ServiceLib/Common/SemanticVersion.cs
Normal file
@@ -0,0 +1,187 @@
|
||||
namespace ServiceLib.Common
|
||||
{
|
||||
public class SemanticVersion
|
||||
{
|
||||
private int major;
|
||||
private int minor;
|
||||
private int patch;
|
||||
private string version;
|
||||
|
||||
public SemanticVersion(int major, int minor, int patch)
|
||||
{
|
||||
this.major = major;
|
||||
this.minor = minor;
|
||||
this.patch = patch;
|
||||
this.version = $"{major}.{minor}.{patch}";
|
||||
}
|
||||
|
||||
public SemanticVersion(string? version)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (version.IsNullOrEmpty())
|
||||
{
|
||||
this.major = 0;
|
||||
this.minor = 0;
|
||||
this.patch = 0;
|
||||
return;
|
||||
}
|
||||
this.version = version.RemovePrefix('v');
|
||||
|
||||
var parts = this.version.Split('.');
|
||||
if (parts.Length == 2)
|
||||
{
|
||||
this.major = int.Parse(parts.First());
|
||||
this.minor = int.Parse(parts.Last());
|
||||
this.patch = 0;
|
||||
}
|
||||
else if (parts.Length is 3 or 4)
|
||||
{
|
||||
this.major = int.Parse(parts[0]);
|
||||
this.minor = int.Parse(parts[1]);
|
||||
this.patch = int.Parse(parts[2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("Invalid version string");
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
this.major = 0;
|
||||
this.minor = 0;
|
||||
this.patch = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (obj is SemanticVersion other)
|
||||
{
|
||||
return this.major == other.major && this.minor == other.minor && this.patch == other.patch;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return this.major.GetHashCode() ^ this.minor.GetHashCode() ^ this.patch.GetHashCode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use ToVersionString(string? prefix) instead if possible.
|
||||
/// </summary>
|
||||
/// <returns>major.minor.patch</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return this.version;
|
||||
}
|
||||
|
||||
public string ToVersionString(string? prefix = null)
|
||||
{
|
||||
if (prefix == null)
|
||||
{
|
||||
return this.version;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $"{prefix}{this.version}";
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(SemanticVersion v1, SemanticVersion v2)
|
||||
{ return v1.Equals(v2); }
|
||||
|
||||
public static bool operator !=(SemanticVersion v1, SemanticVersion v2)
|
||||
{ return !v1.Equals(v2); }
|
||||
|
||||
public static bool operator >=(SemanticVersion v1, SemanticVersion v2)
|
||||
{ return v1.GreaterEquals(v2); }
|
||||
|
||||
public static bool operator <=(SemanticVersion v1, SemanticVersion v2)
|
||||
{ return v1.LessEquals(v2); }
|
||||
|
||||
#region Private
|
||||
|
||||
private bool GreaterEquals(SemanticVersion other)
|
||||
{
|
||||
if (this.major < other.major)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (this.major > other.major)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.minor < other.minor)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (this.minor > other.minor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.patch < other.patch)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (this.patch > other.patch)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool LessEquals(SemanticVersion other)
|
||||
{
|
||||
if (this.major < other.major)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (this.major > other.major)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.minor < other.minor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (this.minor > other.minor)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.patch < other.patch)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (this.patch > other.patch)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Private
|
||||
}
|
||||
}
|
||||
91
v2rayN/ServiceLib/Common/SqliteHelper.cs
Normal file
91
v2rayN/ServiceLib/Common/SqliteHelper.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
using SQLite;
|
||||
using System.Collections;
|
||||
|
||||
namespace ServiceLib.Common
|
||||
{
|
||||
public sealed class SQLiteHelper
|
||||
{
|
||||
private static readonly Lazy<SQLiteHelper> _instance = new(() => new());
|
||||
public static SQLiteHelper Instance => _instance.Value;
|
||||
private string _connstr;
|
||||
private SQLiteConnection _db;
|
||||
private SQLiteAsyncConnection _dbAsync;
|
||||
private readonly string _configDB = "guiNDB.db";
|
||||
|
||||
public SQLiteHelper()
|
||||
{
|
||||
_connstr = Utils.GetConfigPath(_configDB);
|
||||
_db = new SQLiteConnection(_connstr, false);
|
||||
_dbAsync = new SQLiteAsyncConnection(_connstr, false);
|
||||
}
|
||||
|
||||
public CreateTableResult CreateTable<T>()
|
||||
{
|
||||
return _db.CreateTable<T>();
|
||||
}
|
||||
|
||||
public async Task<int> InsertAllAsync(IEnumerable models)
|
||||
{
|
||||
return await _dbAsync.InsertAllAsync(models);
|
||||
}
|
||||
|
||||
public async Task<int> InsertAsync(object model)
|
||||
{
|
||||
return await _dbAsync.InsertAsync(model);
|
||||
}
|
||||
|
||||
public async Task<int> ReplaceAsync(object model)
|
||||
{
|
||||
return await _dbAsync.InsertOrReplaceAsync(model);
|
||||
}
|
||||
|
||||
public async Task<int> UpdateAsync(object model)
|
||||
{
|
||||
return await _dbAsync.UpdateAsync(model);
|
||||
}
|
||||
|
||||
public async Task<int> UpdateAllAsync(IEnumerable models)
|
||||
{
|
||||
return await _dbAsync.UpdateAllAsync(models);
|
||||
}
|
||||
|
||||
public async Task<int> DeleteAsync(object model)
|
||||
{
|
||||
return await _dbAsync.DeleteAsync(model);
|
||||
}
|
||||
|
||||
public async Task<int> DeleteAllAsync<T>()
|
||||
{
|
||||
return await _dbAsync.DeleteAllAsync<T>();
|
||||
}
|
||||
|
||||
public async Task<int> ExecuteAsync(string sql)
|
||||
{
|
||||
return await _dbAsync.ExecuteAsync(sql);
|
||||
}
|
||||
|
||||
public async Task<List<T>> QueryAsync<T>(string sql) where T : new()
|
||||
{
|
||||
return await _dbAsync.QueryAsync<T>(sql);
|
||||
}
|
||||
|
||||
public AsyncTableQuery<T> TableAsync<T>() where T : new()
|
||||
{
|
||||
return _dbAsync.Table<T>();
|
||||
}
|
||||
|
||||
public async Task DisposeDbConnectionAsync()
|
||||
{
|
||||
await Task.Factory.StartNew(() =>
|
||||
{
|
||||
_db?.Close();
|
||||
_db?.Dispose();
|
||||
_db = null;
|
||||
|
||||
_dbAsync?.GetConnection()?.Close();
|
||||
_dbAsync?.GetConnection()?.Dispose();
|
||||
_dbAsync = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
72
v2rayN/ServiceLib/Common/StringEx.cs
Normal file
72
v2rayN/ServiceLib/Common/StringEx.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace ServiceLib.Common
|
||||
{
|
||||
public static class StringEx
|
||||
{
|
||||
public static bool IsNullOrEmpty([NotNullWhen(false)] this string? value)
|
||||
{
|
||||
return string.IsNullOrEmpty(value);
|
||||
}
|
||||
|
||||
public static bool IsNullOrWhiteSpace([NotNullWhen(false)] this string? value)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(value);
|
||||
}
|
||||
|
||||
public static bool IsNotEmpty([NotNullWhen(false)] this string? value)
|
||||
{
|
||||
return !string.IsNullOrEmpty(value);
|
||||
}
|
||||
|
||||
public static bool BeginWithAny(this string s, IEnumerable<char> chars)
|
||||
{
|
||||
if (s.IsNullOrEmpty()) return false;
|
||||
return chars.Contains(s.First());
|
||||
}
|
||||
|
||||
private static bool IsWhiteSpace(this string value)
|
||||
{
|
||||
return value.All(char.IsWhiteSpace);
|
||||
}
|
||||
|
||||
public static IEnumerable<string> NonWhiteSpaceLines(this TextReader reader)
|
||||
{
|
||||
while (reader.ReadLine() is { } line)
|
||||
{
|
||||
if (line.IsWhiteSpace()) continue;
|
||||
yield return line;
|
||||
}
|
||||
}
|
||||
|
||||
public static string TrimEx(this string? value)
|
||||
{
|
||||
return value == null ? string.Empty : value.Trim();
|
||||
}
|
||||
|
||||
public static string RemovePrefix(this string value, char prefix)
|
||||
{
|
||||
return value.StartsWith(prefix) ? value[1..] : value;
|
||||
}
|
||||
|
||||
public static string RemovePrefix(this string value, string prefix)
|
||||
{
|
||||
return value.StartsWith(prefix) ? value[prefix.Length..] : value;
|
||||
}
|
||||
|
||||
public static string UpperFirstChar(this string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return char.ToUpper(value.First()) + value[1..];
|
||||
}
|
||||
|
||||
public static string AppendQuotes(this string value)
|
||||
{
|
||||
return string.IsNullOrEmpty(value) ? string.Empty : $"\"{value}\"";
|
||||
}
|
||||
}
|
||||
}
|
||||
902
v2rayN/ServiceLib/Common/Utils.cs
Normal file
902
v2rayN/ServiceLib/Common/Utils.cs
Normal file
@@ -0,0 +1,902 @@
|
||||
using CliWrap;
|
||||
using CliWrap.Buffered;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
|
||||
namespace ServiceLib.Common
|
||||
{
|
||||
public class Utils
|
||||
{
|
||||
private static readonly string _tag = "Utils";
|
||||
|
||||
#region 资源操作
|
||||
|
||||
/// <summary>
|
||||
/// 获取嵌入文本资源
|
||||
/// </summary>
|
||||
/// <param name="res"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetEmbedText(string res)
|
||||
{
|
||||
var result = string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
using var stream = assembly.GetManifestResourceStream(res);
|
||||
ArgumentNullException.ThrowIfNull(stream);
|
||||
using StreamReader reader = new(stream);
|
||||
result = reader.ReadToEnd();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 取得存储资源
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string? LoadResource(string? res)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(res))
|
||||
{
|
||||
return File.ReadAllText(res);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion 资源操作
|
||||
|
||||
#region 转换函数
|
||||
|
||||
/// <summary>
|
||||
/// 转逗号分隔的字符串
|
||||
/// </summary>
|
||||
/// <param name="lst"></param>
|
||||
/// <param name="wrap"></param>
|
||||
/// <returns></returns>
|
||||
public static string List2String(List<string>? lst, bool wrap = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (lst == null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
if (wrap)
|
||||
{
|
||||
return string.Join("," + Environment.NewLine, lst);
|
||||
}
|
||||
else
|
||||
{
|
||||
return string.Join(",", lst);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 逗号分隔的字符串
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
public static List<string>? String2List(string? str)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (str == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
str = str.Replace(Environment.NewLine, "");
|
||||
return new List<string>(str.Split(',', StringSplitOptions.RemoveEmptyEntries));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 逗号分隔的字符串,先排序后转List
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
public static List<string>? String2ListSorted(string str)
|
||||
{
|
||||
try
|
||||
{
|
||||
str = str.Replace(Environment.NewLine, "");
|
||||
List<string> list = new(str.Split(',', StringSplitOptions.RemoveEmptyEntries));
|
||||
list.Sort();
|
||||
return list;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base64编码
|
||||
/// </summary>
|
||||
/// <param name="plainText"></param>
|
||||
/// <returns></returns>
|
||||
public static string Base64Encode(string plainText)
|
||||
{
|
||||
try
|
||||
{
|
||||
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
|
||||
return Convert.ToBase64String(plainTextBytes);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base64解码
|
||||
/// </summary>
|
||||
/// <param name="plainText"></param>
|
||||
/// <returns></returns>
|
||||
public static string Base64Decode(string? plainText)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (plainText.IsNullOrEmpty()) return "";
|
||||
plainText = plainText.Trim()
|
||||
.Replace(Environment.NewLine, "")
|
||||
.Replace("\n", "")
|
||||
.Replace("\r", "")
|
||||
.Replace('_', '/')
|
||||
.Replace('-', '+')
|
||||
.Replace(" ", "");
|
||||
|
||||
if (plainText.Length % 4 > 0)
|
||||
{
|
||||
plainText = plainText.PadRight(plainText.Length + 4 - (plainText.Length % 4), '=');
|
||||
}
|
||||
|
||||
var data = Convert.FromBase64String(plainText);
|
||||
return Encoding.UTF8.GetString(data);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public static int ToInt(object? obj)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Convert.ToInt32(obj ?? string.Empty);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ToBool(object obj)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Convert.ToBoolean(obj);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static string ToString(object? obj)
|
||||
{
|
||||
try
|
||||
{
|
||||
return obj?.ToString() ?? string.Empty;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ToHumanReadable(long amount, out double result, out string unit)
|
||||
{
|
||||
var factor = 1024u;
|
||||
//long KBs = amount / factor;
|
||||
var KBs = amount;
|
||||
if (KBs > 0)
|
||||
{
|
||||
// multi KB
|
||||
var MBs = KBs / factor;
|
||||
if (MBs > 0)
|
||||
{
|
||||
// multi MB
|
||||
var GBs = MBs / factor;
|
||||
if (GBs > 0)
|
||||
{
|
||||
// multi GB
|
||||
var TBs = GBs / factor;
|
||||
if (TBs > 0)
|
||||
{
|
||||
result = TBs + ((GBs % factor) / (factor + 0.0));
|
||||
unit = "TB";
|
||||
return;
|
||||
}
|
||||
|
||||
result = GBs + ((MBs % factor) / (factor + 0.0));
|
||||
unit = "GB";
|
||||
return;
|
||||
}
|
||||
|
||||
result = MBs + ((KBs % factor) / (factor + 0.0));
|
||||
unit = "MB";
|
||||
return;
|
||||
}
|
||||
|
||||
result = KBs + ((amount % factor) / (factor + 0.0));
|
||||
unit = "KB";
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = amount;
|
||||
unit = "B";
|
||||
}
|
||||
}
|
||||
|
||||
public static string HumanFy(long amount)
|
||||
{
|
||||
ToHumanReadable(amount, out var result, out var unit);
|
||||
return $"{result:f1} {unit}";
|
||||
}
|
||||
|
||||
public static string UrlEncode(string url)
|
||||
{
|
||||
return Uri.EscapeDataString(url);
|
||||
}
|
||||
|
||||
public static string UrlDecode(string url)
|
||||
{
|
||||
return Uri.UnescapeDataString(url);
|
||||
}
|
||||
|
||||
public static NameValueCollection ParseQueryString(string query)
|
||||
{
|
||||
var result = new NameValueCollection(StringComparer.OrdinalIgnoreCase);
|
||||
if (IsNullOrEmpty(query))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
var parts = query[1..].Split('&', StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (var part in parts)
|
||||
{
|
||||
var keyValue = part.Split('=');
|
||||
if (keyValue.Length != 2)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var key = Uri.UnescapeDataString(keyValue.First());
|
||||
var val = Uri.UnescapeDataString(keyValue.Last());
|
||||
|
||||
if (result[key] is null)
|
||||
{
|
||||
result.Add(key, val);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static string GetMd5(string str)
|
||||
{
|
||||
var byteOld = Encoding.UTF8.GetBytes(str);
|
||||
var byteNew = MD5.HashData(byteOld);
|
||||
StringBuilder sb = new(32);
|
||||
foreach (var b in byteNew)
|
||||
{
|
||||
sb.Append(b.ToString("x2"));
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// idn to idc
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetPunycode(string url)
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(url))
|
||||
{
|
||||
return url;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Uri uri = new(url);
|
||||
if (uri.Host == uri.IdnHost || uri.Host == $"[{uri.IdnHost}]")
|
||||
{
|
||||
return url;
|
||||
}
|
||||
else
|
||||
{
|
||||
return url.Replace(uri.Host, uri.IdnHost);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsBase64String(string? plainText)
|
||||
{
|
||||
if (plainText.IsNullOrEmpty()) return false;
|
||||
var buffer = new Span<byte>(new byte[plainText.Length]);
|
||||
return Convert.TryFromBase64String(plainText, buffer, out var _);
|
||||
}
|
||||
|
||||
public static string Convert2Comma(string text)
|
||||
{
|
||||
if (IsNullOrEmpty(text))
|
||||
{
|
||||
return text;
|
||||
}
|
||||
|
||||
return text.Replace(",", ",").Replace(Environment.NewLine, ",");
|
||||
}
|
||||
|
||||
#endregion 转换函数
|
||||
|
||||
#region 数据检查
|
||||
|
||||
/// <summary>
|
||||
/// 判断输入的是否是数字
|
||||
/// </summary>
|
||||
/// <param name="oText"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsNumeric(string oText)
|
||||
{
|
||||
return oText.All(char.IsNumber);
|
||||
}
|
||||
|
||||
public static bool IsNullOrEmpty(string? text)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return text == "null";
|
||||
}
|
||||
|
||||
public static bool IsNotEmpty(string? text)
|
||||
{
|
||||
return !string.IsNullOrEmpty(text);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证Domain地址是否合法
|
||||
/// </summary>
|
||||
/// <param name="domain"></param>
|
||||
public static bool IsDomain(string? domain)
|
||||
{
|
||||
if (IsNullOrEmpty(domain))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Uri.CheckHostName(domain) == UriHostNameType.Dns;
|
||||
}
|
||||
|
||||
public static bool IsIpv6(string ip)
|
||||
{
|
||||
if (IPAddress.TryParse(ip, out var address))
|
||||
{
|
||||
return address.AddressFamily switch
|
||||
{
|
||||
AddressFamily.InterNetwork => false,
|
||||
AddressFamily.InterNetworkV6 => true,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Uri? TryUri(string url)
|
||||
{
|
||||
try
|
||||
{
|
||||
return new Uri(url);
|
||||
}
|
||||
catch (UriFormatException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsPrivateNetwork(string ip)
|
||||
{
|
||||
if (IPAddress.TryParse(ip, out var address))
|
||||
{
|
||||
var ipBytes = address.GetAddressBytes();
|
||||
if (ipBytes[0] == 10) return true;
|
||||
if (ipBytes[0] == 172 && ipBytes[1] >= 16 && ipBytes[1] <= 31) return true;
|
||||
if (ipBytes[0] == 192 && ipBytes[1] == 168) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion 数据检查
|
||||
|
||||
#region 测速
|
||||
|
||||
private static bool PortInUse(int port)
|
||||
{
|
||||
try
|
||||
{
|
||||
var ipProperties = IPGlobalProperties.GetIPGlobalProperties();
|
||||
var ipEndPoints = ipProperties.GetActiveTcpListeners();
|
||||
//var lstIpEndPoints = new List<IPEndPoint>(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners());
|
||||
return ipEndPoints.Any(endPoint => endPoint.Port == port);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static int GetFreePort(int defaultPort = 9090)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!Utils.PortInUse(defaultPort))
|
||||
{
|
||||
return defaultPort;
|
||||
}
|
||||
|
||||
TcpListener l = new(IPAddress.Loopback, 0);
|
||||
l.Start();
|
||||
var port = ((IPEndPoint)l.LocalEndpoint).Port;
|
||||
l.Stop();
|
||||
return port;
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
return 59090;
|
||||
}
|
||||
|
||||
#endregion 测速
|
||||
|
||||
#region 杂项
|
||||
|
||||
public static bool UpgradeAppExists(out string upgradeFileName)
|
||||
{
|
||||
upgradeFileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, GetExeName("AmazTool"));
|
||||
return File.Exists(upgradeFileName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 取得版本
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string GetVersion(bool blFull = true)
|
||||
{
|
||||
try
|
||||
{
|
||||
return blFull
|
||||
? $"{Global.AppName} - V{GetVersionInfo()} - {RuntimeInformation.ProcessArchitecture}"
|
||||
: $"{Global.AppName}/{GetVersionInfo()}";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
|
||||
return Global.AppName;
|
||||
}
|
||||
|
||||
public static string GetVersionInfo()
|
||||
{
|
||||
try
|
||||
{
|
||||
return Assembly.GetExecutingAssembly()?.GetName()?.Version?.ToString(3) ?? "0.0";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
return "0.0";
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetRuntimeInfo()
|
||||
{
|
||||
return $"{Utils.GetVersion()} | {Utils.StartupPath()} | {Utils.GetExePath()} | {Environment.OSVersion} | {(Environment.Is64BitOperatingSystem ? 64 : 32)}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 取得GUID
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string GetGuid(bool full = true)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (full)
|
||||
{
|
||||
return Guid.NewGuid().ToString("D");
|
||||
}
|
||||
else
|
||||
{
|
||||
return BitConverter.ToInt64(Guid.NewGuid().ToByteArray(), 0).ToString();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public static bool IsGuidByParse(string strSrc)
|
||||
{
|
||||
return Guid.TryParse(strSrc, out _);
|
||||
}
|
||||
|
||||
public static Dictionary<string, string> GetSystemHosts()
|
||||
{
|
||||
var systemHosts = new Dictionary<string, string>();
|
||||
var hostFile = @"C:\Windows\System32\drivers\etc\hosts";
|
||||
try
|
||||
{
|
||||
if (File.Exists(hostFile))
|
||||
{
|
||||
var hosts = File.ReadAllText(hostFile).Replace("\r", "");
|
||||
var hostsList = hosts.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
foreach (var host in hostsList)
|
||||
{
|
||||
if (host.StartsWith("#")) continue;
|
||||
var hostItem = host.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (hostItem.Length != 2) continue;
|
||||
systemHosts.Add(hostItem.Last(), hostItem.First());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
|
||||
return systemHosts;
|
||||
}
|
||||
|
||||
public static async Task<string?> GetCliWrapOutput(string filePath, string? arg)
|
||||
{
|
||||
return await GetCliWrapOutput(filePath, arg != null ? new List<string>() { arg } : null);
|
||||
}
|
||||
|
||||
public static async Task<string?> GetCliWrapOutput(string filePath, IEnumerable<string>? args)
|
||||
{
|
||||
try
|
||||
{
|
||||
var cmd = Cli.Wrap(filePath);
|
||||
if (args != null)
|
||||
{
|
||||
if (args.Count() == 1)
|
||||
{
|
||||
cmd = cmd.WithArguments(args.First());
|
||||
}
|
||||
else
|
||||
{
|
||||
cmd = cmd.WithArguments(args);
|
||||
}
|
||||
}
|
||||
|
||||
var result = await cmd.ExecuteBufferedAsync();
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
return result.StandardOutput.ToString();
|
||||
}
|
||||
|
||||
Logging.SaveLog(result.ToString() ?? "");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog("GetCliWrapOutput", ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion 杂项
|
||||
|
||||
#region TempPath
|
||||
|
||||
public static bool HasWritePermission()
|
||||
{
|
||||
try
|
||||
{
|
||||
//When this file exists, it is equivalent to having no permission to read and write
|
||||
if (File.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "NotStoreConfigHere.txt")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var tempPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "guiTemps");
|
||||
if (!Directory.Exists(tempPath))
|
||||
{
|
||||
Directory.CreateDirectory(tempPath);
|
||||
}
|
||||
var fileName = Path.Combine(tempPath, GetGuid());
|
||||
File.Create(fileName).Close();
|
||||
File.Delete(fileName);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static string GetPath(string fileName)
|
||||
{
|
||||
var startupPath = StartupPath();
|
||||
if (IsNullOrEmpty(fileName))
|
||||
{
|
||||
return startupPath;
|
||||
}
|
||||
|
||||
return Path.Combine(startupPath, fileName);
|
||||
}
|
||||
|
||||
public static string GetExePath()
|
||||
{
|
||||
return Environment.ProcessPath ?? Process.GetCurrentProcess().MainModule?.FileName ?? string.Empty;
|
||||
}
|
||||
|
||||
public static string StartupPath()
|
||||
{
|
||||
if (Utils.IsNonWindows() && Environment.GetEnvironmentVariable("V2RAYN_LOCAL_APPLICATION_DATA") == "1")
|
||||
{
|
||||
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "v2rayN");
|
||||
}
|
||||
|
||||
return AppDomain.CurrentDomain.BaseDirectory;
|
||||
}
|
||||
|
||||
public static string GetTempPath(string filename = "")
|
||||
{
|
||||
var tempPath = Path.Combine(StartupPath(), "guiTemps");
|
||||
if (!Directory.Exists(tempPath))
|
||||
{
|
||||
Directory.CreateDirectory(tempPath);
|
||||
}
|
||||
|
||||
if (IsNullOrEmpty(filename))
|
||||
{
|
||||
return tempPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Path.Combine(tempPath, filename);
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetBackupPath(string filename)
|
||||
{
|
||||
var tempPath = Path.Combine(StartupPath(), "guiBackups");
|
||||
if (!Directory.Exists(tempPath))
|
||||
{
|
||||
Directory.CreateDirectory(tempPath);
|
||||
}
|
||||
|
||||
return Path.Combine(tempPath, filename);
|
||||
}
|
||||
|
||||
public static string GetConfigPath(string filename = "")
|
||||
{
|
||||
var tempPath = Path.Combine(StartupPath(), "guiConfigs");
|
||||
if (!Directory.Exists(tempPath))
|
||||
{
|
||||
Directory.CreateDirectory(tempPath);
|
||||
}
|
||||
|
||||
if (Utils.IsNullOrEmpty(filename))
|
||||
{
|
||||
return tempPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Path.Combine(tempPath, filename);
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetBinPath(string filename, string? coreType = null)
|
||||
{
|
||||
var tempPath = Path.Combine(StartupPath(), "bin");
|
||||
if (!Directory.Exists(tempPath))
|
||||
{
|
||||
Directory.CreateDirectory(tempPath);
|
||||
}
|
||||
|
||||
if (coreType != null)
|
||||
{
|
||||
tempPath = Path.Combine(tempPath, coreType.ToLower().ToString());
|
||||
if (!Directory.Exists(tempPath))
|
||||
{
|
||||
Directory.CreateDirectory(tempPath);
|
||||
}
|
||||
}
|
||||
|
||||
if (IsNullOrEmpty(filename))
|
||||
{
|
||||
return tempPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Path.Combine(tempPath, filename);
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetLogPath(string filename = "")
|
||||
{
|
||||
var tempPath = Path.Combine(StartupPath(), "guiLogs");
|
||||
if (!Directory.Exists(tempPath))
|
||||
{
|
||||
Directory.CreateDirectory(tempPath);
|
||||
}
|
||||
|
||||
if (Utils.IsNullOrEmpty(filename))
|
||||
{
|
||||
return tempPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Path.Combine(tempPath, filename);
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetFontsPath(string filename = "")
|
||||
{
|
||||
var tempPath = Path.Combine(StartupPath(), "guiFonts");
|
||||
if (!Directory.Exists(tempPath))
|
||||
{
|
||||
Directory.CreateDirectory(tempPath);
|
||||
}
|
||||
|
||||
if (Utils.IsNullOrEmpty(filename))
|
||||
{
|
||||
return tempPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Path.Combine(tempPath, filename);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion TempPath
|
||||
|
||||
#region Platform
|
||||
|
||||
public static bool IsWindows() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
||||
|
||||
public static bool IsLinux() => RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
|
||||
|
||||
public static bool IsOSX() => RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
|
||||
|
||||
public static bool IsNonWindows() => !RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
||||
|
||||
public static string GetExeName(string name)
|
||||
{
|
||||
return IsWindows() ? $"{name}.exe" : name;
|
||||
}
|
||||
|
||||
public static bool IsAdministrator()
|
||||
{
|
||||
if (IsWindows())
|
||||
{
|
||||
return new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
|
||||
}
|
||||
return false;
|
||||
//else
|
||||
//{
|
||||
// var id = GetLinuxUserId().Result ?? "1000";
|
||||
// if (int.TryParse(id, out var userId))
|
||||
// {
|
||||
// return userId == 0;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
private static async Task<string?> GetLinuxUserId()
|
||||
{
|
||||
var arg = new List<string>() { "-c", "id -u" };
|
||||
return await GetCliWrapOutput("/bin/bash", arg);
|
||||
}
|
||||
|
||||
public static async Task<string?> SetLinuxChmod(string? fileName)
|
||||
{
|
||||
if (fileName.IsNullOrEmpty()) return null;
|
||||
if (fileName.Contains(' ')) fileName = fileName.AppendQuotes();
|
||||
//File.SetUnixFileMode(fileName, UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute);
|
||||
var arg = new List<string>() { "-c", $"chmod +x {fileName}" };
|
||||
return await GetCliWrapOutput("/bin/bash", arg);
|
||||
}
|
||||
|
||||
public static async Task<string?> GetLinuxFontFamily(string lang)
|
||||
{
|
||||
// var arg = new List<string>() { "-c", $"fc-list :lang={lang} family" };
|
||||
var arg = new List<string>() { "-c", $"fc-list : family" };
|
||||
return await GetCliWrapOutput("/bin/bash", arg);
|
||||
}
|
||||
|
||||
public static string? GetHomePath()
|
||||
{
|
||||
return IsWindows()
|
||||
? Environment.ExpandEnvironmentVariables("%HOMEDRIVE%%HOMEPATH%")
|
||||
: Environment.GetEnvironmentVariable("HOME");
|
||||
}
|
||||
|
||||
public static async Task<string?> GetListNetworkServices()
|
||||
{
|
||||
var arg = new List<string>() { "-c", $"networksetup -listallnetworkservices" };
|
||||
return await GetCliWrapOutput("/bin/bash", arg);
|
||||
}
|
||||
|
||||
#endregion Platform
|
||||
}
|
||||
}
|
||||
54
v2rayN/ServiceLib/Common/WindowsUtils.cs
Normal file
54
v2rayN/ServiceLib/Common/WindowsUtils.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace ServiceLib.Common
|
||||
{
|
||||
internal static class WindowsUtils
|
||||
{
|
||||
private static readonly string _tag = "WindowsUtils";
|
||||
|
||||
public static string? RegReadValue(string path, string name, string def)
|
||||
{
|
||||
RegistryKey? regKey = null;
|
||||
try
|
||||
{
|
||||
regKey = Registry.CurrentUser.OpenSubKey(path, false);
|
||||
var value = regKey?.GetValue(name) as string;
|
||||
return Utils.IsNullOrEmpty(value) ? def : value;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
regKey?.Close();
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
public static void RegWriteValue(string path, string name, object value)
|
||||
{
|
||||
RegistryKey? regKey = null;
|
||||
try
|
||||
{
|
||||
regKey = Registry.CurrentUser.CreateSubKey(path);
|
||||
if (Utils.IsNullOrEmpty(value.ToString()))
|
||||
{
|
||||
regKey?.DeleteValue(name, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
regKey?.SetValue(name, value);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
regKey?.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
83
v2rayN/ServiceLib/Common/YamlUtils.cs
Normal file
83
v2rayN/ServiceLib/Common/YamlUtils.cs
Normal file
@@ -0,0 +1,83 @@
|
||||
using YamlDotNet.Core;
|
||||
using YamlDotNet.Serialization;
|
||||
using YamlDotNet.Serialization.NamingConventions;
|
||||
|
||||
namespace ServiceLib.Common
|
||||
{
|
||||
public class YamlUtils
|
||||
{
|
||||
private static readonly string _tag = "YamlUtils";
|
||||
|
||||
#region YAML
|
||||
|
||||
/// <summary>
|
||||
/// 反序列化成对象
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
public static T FromYaml<T>(string str)
|
||||
{
|
||||
var deserializer = new DeserializerBuilder()
|
||||
.WithNamingConvention(PascalCaseNamingConvention.Instance)
|
||||
.Build();
|
||||
try
|
||||
{
|
||||
var obj = deserializer.Deserialize<T>(str);
|
||||
return obj;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
return deserializer.Deserialize<T>("");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 序列化
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <returns></returns>
|
||||
public static string ToYaml(object? obj)
|
||||
{
|
||||
var result = string.Empty;
|
||||
if (obj == null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
var serializer = new SerializerBuilder()
|
||||
.WithNamingConvention(HyphenatedNamingConvention.Instance)
|
||||
.Build();
|
||||
|
||||
try
|
||||
{
|
||||
result = serializer.Serialize(obj);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static string? PreprocessYaml(string str)
|
||||
{
|
||||
var deserializer = new DeserializerBuilder()
|
||||
.WithNamingConvention(PascalCaseNamingConvention.Instance)
|
||||
.Build();
|
||||
try
|
||||
{
|
||||
var mergingParser = new MergingParser(new Parser(new StringReader(str)));
|
||||
var obj = new DeserializerBuilder().Build().Deserialize(mergingParser);
|
||||
return ToYaml(obj);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion YAML
|
||||
}
|
||||
}
|
||||
16
v2rayN/ServiceLib/Enums/EConfigType.cs
Normal file
16
v2rayN/ServiceLib/Enums/EConfigType.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum EConfigType
|
||||
{
|
||||
VMess = 1,
|
||||
Custom = 2,
|
||||
Shadowsocks = 3,
|
||||
SOCKS = 4,
|
||||
VLESS = 5,
|
||||
Trojan = 6,
|
||||
Hysteria2 = 7,
|
||||
TUIC = 8,
|
||||
WireGuard = 9,
|
||||
HTTP = 10
|
||||
}
|
||||
}
|
||||
17
v2rayN/ServiceLib/Enums/ECoreType.cs
Normal file
17
v2rayN/ServiceLib/Enums/ECoreType.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum ECoreType
|
||||
{
|
||||
v2fly = 1,
|
||||
Xray = 2,
|
||||
v2fly_v5 = 4,
|
||||
mihomo = 13,
|
||||
hysteria = 21,
|
||||
naiveproxy = 22,
|
||||
tuic = 23,
|
||||
sing_box = 24,
|
||||
juicity = 25,
|
||||
hysteria2 = 26,
|
||||
v2rayN = 99
|
||||
}
|
||||
}
|
||||
9
v2rayN/ServiceLib/Enums/EGirdOrientation.cs
Normal file
9
v2rayN/ServiceLib/Enums/EGirdOrientation.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum EGirdOrientation
|
||||
{
|
||||
Horizontal,
|
||||
Vertical,
|
||||
Tab,
|
||||
}
|
||||
}
|
||||
11
v2rayN/ServiceLib/Enums/EGlobalHotkey.cs
Normal file
11
v2rayN/ServiceLib/Enums/EGlobalHotkey.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum EGlobalHotkey
|
||||
{
|
||||
ShowForm = 0,
|
||||
SystemProxyClear = 1,
|
||||
SystemProxySet = 2,
|
||||
SystemProxyUnchanged = 3,
|
||||
SystemProxyPac = 4,
|
||||
}
|
||||
}
|
||||
14
v2rayN/ServiceLib/Enums/EInboundProtocol.cs
Normal file
14
v2rayN/ServiceLib/Enums/EInboundProtocol.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum EInboundProtocol
|
||||
{
|
||||
socks = 0,
|
||||
socks2,
|
||||
socks3,
|
||||
pac,
|
||||
api,
|
||||
api2,
|
||||
mixed,
|
||||
speedtest = 21
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
|
||||
namespace v2rayN.Mode
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum EMove
|
||||
{
|
||||
Top = 1,
|
||||
Up = 2,
|
||||
Down = 3,
|
||||
Bottom = 4
|
||||
Bottom = 4,
|
||||
Position = 5
|
||||
}
|
||||
}
|
||||
}
|
||||
12
v2rayN/ServiceLib/Enums/EMsgCommand.cs
Normal file
12
v2rayN/ServiceLib/Enums/EMsgCommand.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum EMsgCommand
|
||||
{
|
||||
ClearMsg,
|
||||
SendMsgView,
|
||||
SendSnackMsg,
|
||||
RefreshProfiles,
|
||||
StopSpeedtest,
|
||||
AppExit
|
||||
}
|
||||
}
|
||||
9
v2rayN/ServiceLib/Enums/EPresetType.cs
Normal file
9
v2rayN/ServiceLib/Enums/EPresetType.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum EPresetType
|
||||
{
|
||||
Default = 0,
|
||||
Russia = 1,
|
||||
Iran = 2,
|
||||
}
|
||||
}
|
||||
10
v2rayN/ServiceLib/Enums/ERuleMode.cs
Normal file
10
v2rayN/ServiceLib/Enums/ERuleMode.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum ERuleMode
|
||||
{
|
||||
Rule = 0,
|
||||
Global = 1,
|
||||
Direct = 2,
|
||||
Unchanged = 3
|
||||
}
|
||||
}
|
||||
21
v2rayN/ServiceLib/Enums/EServerColName.cs
Normal file
21
v2rayN/ServiceLib/Enums/EServerColName.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum EServerColName
|
||||
{
|
||||
Def = 0,
|
||||
ConfigType,
|
||||
Remarks,
|
||||
Address,
|
||||
Port,
|
||||
Network,
|
||||
StreamSecurity,
|
||||
SubRemarks,
|
||||
DelayVal,
|
||||
SpeedVal,
|
||||
|
||||
TodayDown,
|
||||
TodayUp,
|
||||
TotalDown,
|
||||
TotalUp
|
||||
}
|
||||
}
|
||||
10
v2rayN/ServiceLib/Enums/ESpeedActionType.cs
Normal file
10
v2rayN/ServiceLib/Enums/ESpeedActionType.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum ESpeedActionType
|
||||
{
|
||||
Tcping,
|
||||
Realping,
|
||||
Speedtest,
|
||||
Mixedtest
|
||||
}
|
||||
}
|
||||
10
v2rayN/ServiceLib/Enums/ESysProxyType.cs
Normal file
10
v2rayN/ServiceLib/Enums/ESysProxyType.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum ESysProxyType
|
||||
{
|
||||
ForcedClear = 0,
|
||||
ForcedChange = 1,
|
||||
Unchanged = 2,
|
||||
Pac = 3
|
||||
}
|
||||
}
|
||||
15
v2rayN/ServiceLib/Enums/ETransport.cs
Normal file
15
v2rayN/ServiceLib/Enums/ETransport.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum ETransport
|
||||
{
|
||||
tcp,
|
||||
kcp,
|
||||
ws,
|
||||
httpupgrade,
|
||||
xhttp,
|
||||
h2,
|
||||
http,
|
||||
quic,
|
||||
grpc
|
||||
}
|
||||
}
|
||||
46
v2rayN/ServiceLib/Enums/EViewAction.cs
Normal file
46
v2rayN/ServiceLib/Enums/EViewAction.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum EViewAction
|
||||
{
|
||||
CloseWindow,
|
||||
ShowYesNo,
|
||||
SaveFileDialog,
|
||||
AddBatchRoutingRulesYesNo,
|
||||
AdjustMainLvColWidth,
|
||||
SetClipboardData,
|
||||
AddServerViaClipboard,
|
||||
ImportRulesFromClipboard,
|
||||
ProfilesFocus,
|
||||
ShareSub,
|
||||
ShareServer,
|
||||
ShowHideWindow,
|
||||
ScanScreenTask,
|
||||
ScanImageTask,
|
||||
Shutdown,
|
||||
BrowseServer,
|
||||
ImportRulesFromFile,
|
||||
InitSettingFont,
|
||||
SubEditWindow,
|
||||
RoutingRuleSettingWindow,
|
||||
RoutingRuleDetailsWindow,
|
||||
AddServerWindow,
|
||||
AddServer2Window,
|
||||
DNSSettingWindow,
|
||||
RoutingSettingWindow,
|
||||
OptionSettingWindow,
|
||||
GlobalHotkeySettingWindow,
|
||||
SubSettingWindow,
|
||||
DispatcherSpeedTest,
|
||||
DispatcherRefreshConnections,
|
||||
DispatcherRefreshProxyGroups,
|
||||
DispatcherProxiesDelayTest,
|
||||
DispatcherStatistics,
|
||||
DispatcherServerAvailability,
|
||||
DispatcherReload,
|
||||
DispatcherRefreshServersBiz,
|
||||
DispatcherRefreshIcon,
|
||||
DispatcherCheckUpdate,
|
||||
DispatcherCheckUpdateFinished,
|
||||
DispatcherShowMsg,
|
||||
}
|
||||
}
|
||||
3
v2rayN/ServiceLib/FodyWeavers.xml
Normal file
3
v2rayN/ServiceLib/FodyWeavers.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
|
||||
<ReactiveUI />
|
||||
</Weavers>
|
||||
223
v2rayN/ServiceLib/Global.cs
Normal file
223
v2rayN/ServiceLib/Global.cs
Normal file
@@ -0,0 +1,223 @@
|
||||
namespace ServiceLib
|
||||
{
|
||||
public class Global
|
||||
{
|
||||
#region const
|
||||
|
||||
public const string AppName = "v2rayN";
|
||||
public const string GithubUrl = "https://github.com";
|
||||
public const string GithubApiUrl = "https://api.github.com/repos";
|
||||
public const string V2flyCoreUrl = "https://github.com/v2fly/v2ray-core/releases";
|
||||
public const string XrayCoreUrl = "https://github.com/XTLS/Xray-core/releases";
|
||||
public const string NUrl = @"https://github.com/2dust/v2rayN/releases";
|
||||
public const string MihomoCoreUrl = "https://github.com/MetaCubeX/mihomo/releases";
|
||||
public const string HysteriaCoreUrl = "https://github.com/apernet/hysteria/releases";
|
||||
public const string NaiveproxyCoreUrl = "https://github.com/klzgrad/naiveproxy/releases";
|
||||
public const string TuicCoreUrl = "https://github.com/EAimTY/tuic/releases";
|
||||
public const string SingboxCoreUrl = "https://github.com/SagerNet/sing-box/releases";
|
||||
public const string GeoUrl = "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/{0}.dat";
|
||||
public const string SpeedPingTestUrl = @"https://www.google.com/generate_204";
|
||||
public const string JuicityCoreUrl = "https://github.com/juicity/juicity/releases";
|
||||
public const string CustomRoutingListUrl = @"https://raw.githubusercontent.com/2dust/v2rayCustomRoutingList/master/";
|
||||
public const string SingboxRulesetUrl = @"https://raw.githubusercontent.com/2dust/sing-box-rules/rule-set-{0}/{1}.srs";
|
||||
public const string IPAPIUrl = "https://api.ip.sb/geoip";
|
||||
|
||||
public const string PromotionUrl = @"aHR0cHM6Ly85LjIzNDQ1Ni54eXovYWJjLmh0bWw=";
|
||||
public const string ConfigFileName = "guiNConfig.json";
|
||||
public const string CoreConfigFileName = "config.json";
|
||||
public const string CorePreConfigFileName = "configPre.json";
|
||||
public const string CoreSpeedtestConfigFileName = "configSpeedtest.json";
|
||||
public const string CoreMultipleLoadConfigFileName = "configMultipleLoad.json";
|
||||
public const string ClashMixinConfigFileName = "Mixin.yaml";
|
||||
|
||||
public const string NamespaceSample = "ServiceLib.Sample.";
|
||||
public const string V2raySampleClient = NamespaceSample + "SampleClientConfig";
|
||||
public const string SingboxSampleClient = NamespaceSample + "SingboxSampleClientConfig";
|
||||
public const string V2raySampleHttpRequestFileName = NamespaceSample + "SampleHttpRequest";
|
||||
public const string V2raySampleHttpResponseFileName = NamespaceSample + "SampleHttpResponse";
|
||||
public const string V2raySampleInbound = NamespaceSample + "SampleInbound";
|
||||
public const string V2raySampleOutbound = NamespaceSample + "SampleOutbound";
|
||||
public const string SingboxSampleOutbound = NamespaceSample + "SingboxSampleOutbound";
|
||||
public const string CustomRoutingFileName = NamespaceSample + "custom_routing_";
|
||||
public const string TunSingboxDNSFileName = NamespaceSample + "tun_singbox_dns";
|
||||
public const string TunSingboxInboundFileName = NamespaceSample + "tun_singbox_inbound";
|
||||
public const string TunSingboxRulesFileName = NamespaceSample + "tun_singbox_rules";
|
||||
public const string DNSV2rayNormalFileName = NamespaceSample + "dns_v2ray_normal";
|
||||
public const string DNSSingboxNormalFileName = NamespaceSample + "dns_singbox_normal";
|
||||
public const string ClashMixinYaml = NamespaceSample + "clash_mixin_yaml";
|
||||
public const string ClashTunYaml = NamespaceSample + "clash_tun_yaml";
|
||||
public const string LinuxAutostartConfig = NamespaceSample + "linux_autostart_config";
|
||||
public const string PacFileName = NamespaceSample + "pac";
|
||||
|
||||
public const string DefaultSecurity = "auto";
|
||||
public const string DefaultNetwork = "tcp";
|
||||
public const string TcpHeaderHttp = "http";
|
||||
public const string None = "none";
|
||||
public const string ProxyTag = "proxy";
|
||||
public const string DirectTag = "direct";
|
||||
public const string BlockTag = "block";
|
||||
public const string StreamSecurity = "tls";
|
||||
public const string StreamSecurityReality = "reality";
|
||||
public const string Loopback = "127.0.0.1";
|
||||
public const string InboundAPIProtocol = "dokodemo-door";
|
||||
public const string HttpProtocol = "http://";
|
||||
public const string HttpsProtocol = "https://";
|
||||
public const string SocksProtocol = "socks://";
|
||||
|
||||
public const string UserEMail = "t@t.tt";
|
||||
public const string AutoRunRegPath = @"Software\Microsoft\Windows\CurrentVersion\Run";
|
||||
public const string AutoRunName = "v2rayNAutoRun";
|
||||
public const string CustomIconName = "v2rayN.ico";
|
||||
public const string SystemProxyExceptionsWindows = "localhost;127.*;10.*;172.16.*;172.17.*;172.18.*;172.19.*;172.20.*;172.21.*;172.22.*;172.23.*;172.24.*;172.25.*;172.26.*;172.27.*;172.28.*;172.29.*;172.30.*;172.31.*;192.168.*";
|
||||
public const string SystemProxyExceptionsLinux = "localhost, 127.0.0.0/8, ::1";
|
||||
public const string RoutingRuleComma = "<COMMA>";
|
||||
public const string GrpcGunMode = "gun";
|
||||
public const string GrpcMultiMode = "multi";
|
||||
public const int MaxPort = 65536;
|
||||
public const string DelayUnit = "";
|
||||
public const string SpeedUnit = "";
|
||||
public const int MinFontSize = 8;
|
||||
public const string RebootAs = "rebootas";
|
||||
public const string AvaAssets = "avares://v2rayN/Assets/";
|
||||
|
||||
public static readonly List<string> IEProxyProtocols = new() {
|
||||
"{ip}:{http_port}",
|
||||
"socks={ip}:{socks_port}",
|
||||
"http={ip}:{http_port};https={ip}:{http_port};ftp={ip}:{http_port};socks={ip}:{socks_port}",
|
||||
"http=http://{ip}:{http_port};https=http://{ip}:{http_port}",
|
||||
""
|
||||
};
|
||||
|
||||
public static readonly List<string> SubConvertUrls = new List<string> {
|
||||
@"https://sub.xeton.dev/sub?url={0}",
|
||||
@"https://api.dler.io/sub?url={0}",
|
||||
@"http://127.0.0.1:25500/sub?url={0}",
|
||||
""
|
||||
};
|
||||
|
||||
public static readonly List<string> SubConvertConfig = new List<string> {
|
||||
@"https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/config/ACL4SSR_Online.ini"
|
||||
};
|
||||
|
||||
public static readonly List<string> SubConvertTargets = new List<string> {
|
||||
"",
|
||||
"mixed",
|
||||
"v2ray",
|
||||
"clash",
|
||||
"ss",
|
||||
};
|
||||
|
||||
public static readonly List<string> SpeedTestUrls = new() {
|
||||
@"https://speed.cloudflare.com/__down?bytes=100000000",
|
||||
@"https://speed.cloudflare.com/__down?bytes=50000000",
|
||||
@"https://speed.cloudflare.com/__down?bytes=10000000",
|
||||
@"https://cachefly.cachefly.net/50mb.test",
|
||||
};
|
||||
|
||||
public static readonly List<string> SpeedPingTestUrls = new() {
|
||||
@"https://www.google.com/generate_204",
|
||||
@"https://www.gstatic.com/generate_204",
|
||||
@"https://www.apple.com/library/test/success.html",
|
||||
@"http://www.msftconnecttest.com/connecttest.txt",
|
||||
};
|
||||
|
||||
public static readonly List<string> GeoFilesSources = new() {
|
||||
"",
|
||||
@"https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/{0}.dat",
|
||||
@"https://cdn.jsdelivr.net/gh/chocolate4u/Iran-v2ray-rules@release/{0}.dat",
|
||||
};
|
||||
|
||||
public static readonly List<string> SingboxRulesetSources = new() {
|
||||
"",
|
||||
@"https://cdn.jsdelivr.net/gh/runetfreedom/russia-v2ray-rules-dat@release/sing-box/rule-set-{0}/{1}.srs",
|
||||
@"https://cdn.jsdelivr.net/gh/chocolate4u/Iran-sing-box-rules@rule-set/{1}.srs",
|
||||
};
|
||||
|
||||
public static readonly List<string> RoutingRulesSources = new() {
|
||||
"",
|
||||
@"https://cdn.jsdelivr.net/gh/runetfreedom/russia-v2ray-custom-routing-list@main/v2rayN/template.json",
|
||||
@"https://cdn.jsdelivr.net/gh/Chocolate4U/Iran-v2ray-rules@main/v2rayN/template.json",
|
||||
};
|
||||
|
||||
public static readonly List<string> DNSTemplateSources = new() {
|
||||
"",
|
||||
@"https://cdn.jsdelivr.net/gh/runetfreedom/russia-v2ray-custom-routing-list@main/v2rayN/",
|
||||
@"https://cdn.jsdelivr.net/gh/Chocolate4U/Iran-v2ray-rules@main/v2rayN/",
|
||||
};
|
||||
|
||||
public static readonly Dictionary<string, string> UserAgentTexts = new()
|
||||
{
|
||||
{"chrome","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36" },
|
||||
{"firefox","Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0" },
|
||||
{"safari","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15" },
|
||||
{"edge","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.70" },
|
||||
{"none",""}
|
||||
};
|
||||
|
||||
public const string Hysteria2ProtocolShare = "hy2://";
|
||||
|
||||
public static readonly Dictionary<EConfigType, string> ProtocolShares = new()
|
||||
{
|
||||
{EConfigType.VMess,"vmess://"},
|
||||
{EConfigType.Shadowsocks,"ss://"},
|
||||
{EConfigType.SOCKS,"socks://"},
|
||||
{EConfigType.VLESS,"vless://"},
|
||||
{EConfigType.Trojan,"trojan://"},
|
||||
{EConfigType.Hysteria2,"hysteria2://"},
|
||||
{EConfigType.TUIC,"tuic://"},
|
||||
{EConfigType.WireGuard,"wireguard://"}
|
||||
};
|
||||
|
||||
public static readonly Dictionary<EConfigType, string> ProtocolTypes = new()
|
||||
{
|
||||
{EConfigType.VMess,"vmess"},
|
||||
{EConfigType.Shadowsocks,"shadowsocks"},
|
||||
{EConfigType.SOCKS,"socks"},
|
||||
{EConfigType.HTTP,"http"},
|
||||
{EConfigType.VLESS,"vless"},
|
||||
{EConfigType.Trojan,"trojan"},
|
||||
{EConfigType.Hysteria2,"hysteria2"},
|
||||
{EConfigType.TUIC,"tuic"},
|
||||
{EConfigType.WireGuard,"wireguard"}
|
||||
};
|
||||
|
||||
public static readonly List<string> VmessSecurities = new() { "aes-128-gcm", "chacha20-poly1305", "auto", "none", "zero" };
|
||||
public static readonly List<string> SsSecurities = new() { "aes-256-gcm", "aes-128-gcm", "chacha20-poly1305", "chacha20-ietf-poly1305", "none", "plain" };
|
||||
public static readonly List<string> SsSecuritiesInXray = new() { "aes-256-gcm", "aes-128-gcm", "chacha20-poly1305", "chacha20-ietf-poly1305", "xchacha20-poly1305", "xchacha20-ietf-poly1305", "none", "plain", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305" };
|
||||
public static readonly List<string> SsSecuritiesInSingbox = new() { "aes-256-gcm", "aes-192-gcm", "aes-128-gcm", "chacha20-ietf-poly1305", "xchacha20-ietf-poly1305", "none", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "rc4-md5", "chacha20-ietf", "xchacha20" };
|
||||
public static readonly List<string> Flows = new() { "", "xtls-rprx-vision", "xtls-rprx-vision-udp443" };
|
||||
public static readonly List<string> Networks = new() { "tcp", "kcp", "ws", "httpupgrade", "xhttp", "h2", "quic", "grpc" };
|
||||
public static readonly List<string> KcpHeaderTypes = new() { "srtp", "utp", "wechat-video", "dtls", "wireguard" };
|
||||
public static readonly List<string> CoreTypes = new() { "Xray", "sing_box" };
|
||||
public static readonly List<string> DomainStrategies = new() { "AsIs", "IPIfNonMatch", "IPOnDemand" };
|
||||
public static readonly List<string> DomainStrategies4Singbox = new() { "ipv4_only", "ipv6_only", "prefer_ipv4", "prefer_ipv6", "" };
|
||||
public static readonly List<string> DomainMatchers = new() { "linear", "mph", "" };
|
||||
public static readonly List<string> Fingerprints = new() { "chrome", "firefox", "safari", "ios", "android", "edge", "360", "qq", "random", "randomized", "" };
|
||||
public static readonly List<string> UserAgent = new() { "chrome", "firefox", "safari", "edge", "none" };
|
||||
public static readonly List<string> XhttpMode = new() { "auto", "packet-up", "stream-up", "stream-one" };
|
||||
|
||||
public static readonly List<string> AllowInsecure = new() { "true", "false", "" };
|
||||
public static readonly List<string> DomainStrategy4Freedoms = new() { "AsIs", "UseIP", "UseIPv4", "UseIPv6", "" };
|
||||
public static readonly List<string> SingboxDomainStrategy4Out = new() { "ipv4_only", "prefer_ipv4", "prefer_ipv6", "ipv6_only", "" };
|
||||
public static readonly List<string> DomainDNSAddress = ["223.5.5.5", "223.6.6.6", "localhost"];
|
||||
public static readonly List<string> SingboxDomainDNSAddress = ["223.5.5.5", "223.6.6.6", "dhcp://auto"];
|
||||
public static readonly List<string> Languages = new() { "zh-Hans", "zh-Hant", "en", "fa-Ir", "ru", "hu" };
|
||||
public static readonly List<string> Alpns = new() { "h3", "h2", "http/1.1", "h3,h2", "h2,http/1.1", "h3,h2,http/1.1", "" };
|
||||
public static readonly List<string> LogLevels = new() { "debug", "info", "warning", "error", "none" };
|
||||
public static readonly List<string> InboundTags = new() { "socks", "socks2", "socks3" };
|
||||
public static readonly List<string> RuleProtocols = new() { "http", "tls", "bittorrent" };
|
||||
public static readonly List<string> RuleNetworks = new() { "", "tcp", "udp", "tcp,udp" };
|
||||
public static readonly List<string> destOverrideProtocols = ["http", "tls", "quic", "fakedns", "fakedns+others"];
|
||||
public static readonly List<string> TunMtus = new() { "1280", "1408", "1500", "9000" };
|
||||
public static readonly List<string> TunStacks = new() { "gvisor", "system" };
|
||||
public static readonly List<string> PresetMsgFilters = new() { "proxy", "direct", "block", "" };
|
||||
public static readonly List<string> SingboxMuxs = new() { "h2mux", "smux", "yamux", "" };
|
||||
public static readonly List<string> TuicCongestionControls = new() { "cubic", "new_reno", "bbr" };
|
||||
|
||||
public static readonly List<string> allowSelectType = new() { "selector", "urltest", "loadbalance", "fallback" };
|
||||
public static readonly List<string> notAllowTestType = new() { "selector", "urltest", "direct", "reject", "compatible", "pass", "loadbalance", "fallback" };
|
||||
public static readonly List<string> proxyVehicleType = new() { "file", "http" };
|
||||
|
||||
#endregion const
|
||||
}
|
||||
}
|
||||
11
v2rayN/ServiceLib/GlobalUsings.cs
Normal file
11
v2rayN/ServiceLib/GlobalUsings.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
global using ServiceLib.Base;
|
||||
global using ServiceLib.Common;
|
||||
global using ServiceLib.Enums;
|
||||
global using ServiceLib.Handler;
|
||||
global using ServiceLib.Handler.Fmt;
|
||||
global using ServiceLib.Services;
|
||||
global using ServiceLib.Services.Statistics;
|
||||
global using ServiceLib.Services.CoreConfig;
|
||||
global using ServiceLib.Models;
|
||||
global using ServiceLib.Resx;
|
||||
global using ServiceLib.Handler.SysProxy;
|
||||
278
v2rayN/ServiceLib/Handler/AppHandler.cs
Normal file
278
v2rayN/ServiceLib/Handler/AppHandler.cs
Normal file
@@ -0,0 +1,278 @@
|
||||
namespace ServiceLib.Handler
|
||||
{
|
||||
public sealed class AppHandler
|
||||
{
|
||||
#region Property
|
||||
|
||||
private static readonly Lazy<AppHandler> _instance = new(() => new());
|
||||
private Config _config;
|
||||
private int? _statePort;
|
||||
private int? _statePort2;
|
||||
private Job? _processJob;
|
||||
private bool? _isAdministrator;
|
||||
public static AppHandler Instance => _instance.Value;
|
||||
public Config Config => _config;
|
||||
|
||||
public int StatePort
|
||||
{
|
||||
get
|
||||
{
|
||||
_statePort ??= Utils.GetFreePort(GetLocalPort(EInboundProtocol.api));
|
||||
return _statePort.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public int StatePort2
|
||||
{
|
||||
get
|
||||
{
|
||||
_statePort2 ??= Utils.GetFreePort(GetLocalPort(EInboundProtocol.api2));
|
||||
return _statePort2.Value + (_config.TunModeItem.EnableTun ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsAdministrator
|
||||
{
|
||||
get
|
||||
{
|
||||
_isAdministrator ??= Utils.IsAdministrator();
|
||||
return _isAdministrator.Value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Property
|
||||
|
||||
#region Init
|
||||
|
||||
public bool InitApp()
|
||||
{
|
||||
if (Utils.IsNonWindows() && Utils.HasWritePermission() == false)
|
||||
{
|
||||
Environment.SetEnvironmentVariable("V2RAYN_LOCAL_APPLICATION_DATA", "1", EnvironmentVariableTarget.Process);
|
||||
}
|
||||
|
||||
Logging.Setup();
|
||||
var config = ConfigHandler.LoadConfig();
|
||||
if (config == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_config = config;
|
||||
Thread.CurrentThread.CurrentUICulture = new(_config.UiItem.CurrentLanguage);
|
||||
|
||||
//Under Win10
|
||||
if (Utils.IsWindows() && Environment.OSVersion.Version.Major < 10)
|
||||
{
|
||||
Environment.SetEnvironmentVariable("DOTNET_EnableWriteXorExecute", "0", EnvironmentVariableTarget.User);
|
||||
}
|
||||
|
||||
SQLiteHelper.Instance.CreateTable<SubItem>();
|
||||
SQLiteHelper.Instance.CreateTable<ProfileItem>();
|
||||
SQLiteHelper.Instance.CreateTable<ServerStatItem>();
|
||||
SQLiteHelper.Instance.CreateTable<RoutingItem>();
|
||||
SQLiteHelper.Instance.CreateTable<ProfileExItem>();
|
||||
SQLiteHelper.Instance.CreateTable<DNSItem>();
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool InitComponents()
|
||||
{
|
||||
Logging.SaveLog($"v2rayN start up | {Utils.GetRuntimeInfo()}");
|
||||
Logging.LoggingEnabled(_config.GuiItem.EnableLog);
|
||||
Logging.ClearLogs();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Reset()
|
||||
{
|
||||
_statePort = null;
|
||||
_statePort2 = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion Init
|
||||
|
||||
#region Config
|
||||
|
||||
public int GetLocalPort(EInboundProtocol protocol)
|
||||
{
|
||||
var localPort = _config.Inbound.FirstOrDefault(t => t.Protocol == nameof(EInboundProtocol.socks))?.LocalPort ?? 10808;
|
||||
return localPort + (int)protocol;
|
||||
}
|
||||
|
||||
public void AddProcess(IntPtr processHandle)
|
||||
{
|
||||
if (Utils.IsWindows())
|
||||
{
|
||||
_processJob ??= new();
|
||||
_processJob?.AddProcess(processHandle);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Config
|
||||
|
||||
#region SqliteHelper
|
||||
|
||||
public async Task<List<SubItem>?> SubItems()
|
||||
{
|
||||
return await SQLiteHelper.Instance.TableAsync<SubItem>().OrderBy(t => t.Sort).ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<SubItem?> GetSubItem(string subid)
|
||||
{
|
||||
return await SQLiteHelper.Instance.TableAsync<SubItem>().FirstOrDefaultAsync(t => t.Id == subid);
|
||||
}
|
||||
|
||||
public async Task<List<ProfileItem>?> ProfileItems(string subid)
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(subid))
|
||||
{
|
||||
return await SQLiteHelper.Instance.TableAsync<ProfileItem>().ToListAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
return await SQLiteHelper.Instance.TableAsync<ProfileItem>().Where(t => t.Subid == subid).ToListAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<string>?> ProfileItemIndexes(string subid)
|
||||
{
|
||||
return (await ProfileItems(subid))?.Select(t => t.IndexId)?.ToList();
|
||||
}
|
||||
|
||||
public async Task<List<ProfileItemModel>?> ProfileItems(string subid, string filter)
|
||||
{
|
||||
var sql = @$"select a.*
|
||||
,b.remarks subRemarks
|
||||
from ProfileItem a
|
||||
left join SubItem b on a.subid = b.id
|
||||
where 1=1 ";
|
||||
if (Utils.IsNotEmpty(subid))
|
||||
{
|
||||
sql += $" and a.subid = '{subid}'";
|
||||
}
|
||||
if (Utils.IsNotEmpty(filter))
|
||||
{
|
||||
if (filter.Contains('\''))
|
||||
{
|
||||
filter = filter.Replace("'", "");
|
||||
}
|
||||
sql += string.Format(" and (a.remarks like '%{0}%' or a.address like '%{0}%') ", filter);
|
||||
}
|
||||
|
||||
return await SQLiteHelper.Instance.QueryAsync<ProfileItemModel>(sql);
|
||||
}
|
||||
|
||||
public async Task<List<ProfileItemModel>?> ProfileItemsEx(string subid, string filter)
|
||||
{
|
||||
var lstModel = await ProfileItems(_config.SubIndexId, filter);
|
||||
|
||||
await ConfigHandler.SetDefaultServer(_config, lstModel);
|
||||
|
||||
var lstServerStat = (_config.GuiItem.EnableStatistics ? StatisticsHandler.Instance.ServerStat : null) ?? [];
|
||||
var lstProfileExs = await ProfileExHandler.Instance.GetProfileExs();
|
||||
lstModel = (from t in lstModel
|
||||
join t2 in lstServerStat on t.IndexId equals t2.IndexId into t2b
|
||||
from t22 in t2b.DefaultIfEmpty()
|
||||
join t3 in lstProfileExs on t.IndexId equals t3.IndexId into t3b
|
||||
from t33 in t3b.DefaultIfEmpty()
|
||||
select new ProfileItemModel
|
||||
{
|
||||
IndexId = t.IndexId,
|
||||
ConfigType = t.ConfigType,
|
||||
Remarks = t.Remarks,
|
||||
Address = t.Address,
|
||||
Port = t.Port,
|
||||
Security = t.Security,
|
||||
Network = t.Network,
|
||||
StreamSecurity = t.StreamSecurity,
|
||||
Subid = t.Subid,
|
||||
SubRemarks = t.SubRemarks,
|
||||
IsActive = t.IndexId == _config.IndexId,
|
||||
Sort = t33 == null ? 0 : t33.Sort,
|
||||
Delay = t33 == null ? 0 : t33.Delay,
|
||||
DelayVal = t33?.Delay != 0 ? $"{t33?.Delay} {Global.DelayUnit}" : string.Empty,
|
||||
SpeedVal = t33?.Speed != 0 ? $"{t33?.Speed} {Global.SpeedUnit}" : string.Empty,
|
||||
TodayDown = t22 == null ? "" : Utils.HumanFy(t22.TodayDown),
|
||||
TodayUp = t22 == null ? "" : Utils.HumanFy(t22.TodayUp),
|
||||
TotalDown = t22 == null ? "" : Utils.HumanFy(t22.TotalDown),
|
||||
TotalUp = t22 == null ? "" : Utils.HumanFy(t22.TotalUp)
|
||||
}).OrderBy(t => t.Sort).ToList();
|
||||
|
||||
return lstModel;
|
||||
}
|
||||
|
||||
public async Task<ProfileItem?> GetProfileItem(string indexId)
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(indexId))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return await SQLiteHelper.Instance.TableAsync<ProfileItem>().FirstOrDefaultAsync(it => it.IndexId == indexId);
|
||||
}
|
||||
|
||||
public async Task<ProfileItem?> GetProfileItemViaRemarks(string? remarks)
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(remarks))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return await SQLiteHelper.Instance.TableAsync<ProfileItem>().FirstOrDefaultAsync(it => it.Remarks == remarks);
|
||||
}
|
||||
|
||||
public async Task<List<RoutingItem>?> RoutingItems()
|
||||
{
|
||||
return await SQLiteHelper.Instance.TableAsync<RoutingItem>().OrderBy(t => t.Sort).ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<RoutingItem?> GetRoutingItem(string id)
|
||||
{
|
||||
return await SQLiteHelper.Instance.TableAsync<RoutingItem>().FirstOrDefaultAsync(it => it.Id == id);
|
||||
}
|
||||
|
||||
public async Task<List<DNSItem>?> DNSItems()
|
||||
{
|
||||
return await SQLiteHelper.Instance.TableAsync<DNSItem>().ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<DNSItem?> GetDNSItem(ECoreType eCoreType)
|
||||
{
|
||||
return await SQLiteHelper.Instance.TableAsync<DNSItem>().FirstOrDefaultAsync(it => it.CoreType == eCoreType);
|
||||
}
|
||||
|
||||
#endregion SqliteHelper
|
||||
|
||||
#region Core Type
|
||||
|
||||
public List<string> GetShadowsocksSecurities(ProfileItem profileItem)
|
||||
{
|
||||
var coreType = GetCoreType(profileItem, EConfigType.Shadowsocks);
|
||||
switch (coreType)
|
||||
{
|
||||
case ECoreType.v2fly:
|
||||
return Global.SsSecurities;
|
||||
|
||||
case ECoreType.Xray:
|
||||
return Global.SsSecuritiesInXray;
|
||||
|
||||
case ECoreType.sing_box:
|
||||
return Global.SsSecuritiesInSingbox;
|
||||
}
|
||||
return Global.SsSecuritiesInSingbox;
|
||||
}
|
||||
|
||||
public ECoreType GetCoreType(ProfileItem profileItem, EConfigType eConfigType)
|
||||
{
|
||||
if (profileItem?.CoreType != null)
|
||||
{
|
||||
return (ECoreType)profileItem.CoreType;
|
||||
}
|
||||
|
||||
var item = _config.CoreTypeItem?.FirstOrDefault(it => it.ConfigType == eConfigType);
|
||||
return item?.CoreType ?? ECoreType.Xray;
|
||||
}
|
||||
|
||||
#endregion Core Type
|
||||
}
|
||||
}
|
||||
161
v2rayN/ServiceLib/Handler/AutoStartupHandler.cs
Normal file
161
v2rayN/ServiceLib/Handler/AutoStartupHandler.cs
Normal file
@@ -0,0 +1,161 @@
|
||||
using System.Security.Principal;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ServiceLib.Handler
|
||||
{
|
||||
public static class AutoStartupHandler
|
||||
{
|
||||
private static readonly string _tag = "AutoStartupHandler";
|
||||
|
||||
public static async Task<bool> UpdateTask(Config config)
|
||||
{
|
||||
if (Utils.IsWindows())
|
||||
{
|
||||
await ClearTaskWindows();
|
||||
|
||||
if (config.GuiItem.AutoRun)
|
||||
{
|
||||
await SetTaskWindows();
|
||||
}
|
||||
}
|
||||
else if (Utils.IsLinux())
|
||||
{
|
||||
await ClearTaskLinux();
|
||||
|
||||
if (config.GuiItem.AutoRun)
|
||||
{
|
||||
await SetTaskLinux();
|
||||
}
|
||||
}
|
||||
else if (Utils.IsOSX())
|
||||
{
|
||||
//TODO
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#region Windows
|
||||
|
||||
private static async Task ClearTaskWindows()
|
||||
{
|
||||
var autoRunName = GetAutoRunNameWindows();
|
||||
WindowsUtils.RegWriteValue(Global.AutoRunRegPath, autoRunName, "");
|
||||
if (Utils.IsAdministrator())
|
||||
{
|
||||
AutoStartTaskService(autoRunName, "", "");
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task SetTaskWindows()
|
||||
{
|
||||
try
|
||||
{
|
||||
var autoRunName = GetAutoRunNameWindows();
|
||||
var exePath = Utils.GetExePath();
|
||||
if (Utils.IsAdministrator())
|
||||
{
|
||||
AutoStartTaskService(autoRunName, exePath, "");
|
||||
}
|
||||
else
|
||||
{
|
||||
WindowsUtils.RegWriteValue(Global.AutoRunRegPath, autoRunName, exePath.AppendQuotes());
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Auto Start via TaskService
|
||||
/// </summary>
|
||||
/// <param name="taskName"></param>
|
||||
/// <param name="fileName"></param>
|
||||
/// <param name="description"></param>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
public static void AutoStartTaskService(string taskName, string fileName, string description)
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(taskName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var logonUser = WindowsIdentity.GetCurrent().Name;
|
||||
using var taskService = new Microsoft.Win32.TaskScheduler.TaskService();
|
||||
var tasks = taskService.RootFolder.GetTasks(new Regex(taskName));
|
||||
if (Utils.IsNullOrEmpty(fileName))
|
||||
{
|
||||
foreach (var t in tasks)
|
||||
{
|
||||
taskService.RootFolder.DeleteTask(t.Name);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var task = taskService.NewTask();
|
||||
task.RegistrationInfo.Description = description;
|
||||
task.Settings.DisallowStartIfOnBatteries = false;
|
||||
task.Settings.StopIfGoingOnBatteries = false;
|
||||
task.Settings.RunOnlyIfIdle = false;
|
||||
task.Settings.IdleSettings.StopOnIdleEnd = false;
|
||||
task.Settings.ExecutionTimeLimit = TimeSpan.Zero;
|
||||
task.Triggers.Add(new Microsoft.Win32.TaskScheduler.LogonTrigger { UserId = logonUser, Delay = TimeSpan.FromSeconds(10) });
|
||||
task.Principal.RunLevel = Microsoft.Win32.TaskScheduler.TaskRunLevel.Highest;
|
||||
task.Actions.Add(new Microsoft.Win32.TaskScheduler.ExecAction(fileName.AppendQuotes(), null, Path.GetDirectoryName(fileName)));
|
||||
|
||||
taskService.RootFolder.RegisterTaskDefinition(taskName, task);
|
||||
}
|
||||
|
||||
private static string GetAutoRunNameWindows()
|
||||
{
|
||||
return $"{Global.AutoRunName}_{Utils.GetMd5(Utils.StartupPath())}";
|
||||
}
|
||||
|
||||
#endregion Windows
|
||||
|
||||
#region Linux
|
||||
|
||||
private static async Task ClearTaskLinux()
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(GetHomePathLinux());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task SetTaskLinux()
|
||||
{
|
||||
try
|
||||
{
|
||||
var linuxConfig = Utils.GetEmbedText(Global.LinuxAutostartConfig);
|
||||
if (linuxConfig.IsNotEmpty())
|
||||
{
|
||||
linuxConfig = linuxConfig.Replace("$ExecPath$", Utils.GetExePath());
|
||||
Logging.SaveLog(linuxConfig);
|
||||
|
||||
var homePath = GetHomePathLinux();
|
||||
await File.WriteAllTextAsync(homePath, linuxConfig);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetHomePathLinux()
|
||||
{
|
||||
var homePath = Path.Combine(Utils.GetHomePath(), ".config", "autostart", $"{Global.AppName}.desktop");
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(homePath));
|
||||
return homePath;
|
||||
}
|
||||
|
||||
#endregion Linux
|
||||
}
|
||||
}
|
||||
197
v2rayN/ServiceLib/Handler/ClashApiHandler.cs
Normal file
197
v2rayN/ServiceLib/Handler/ClashApiHandler.cs
Normal file
@@ -0,0 +1,197 @@
|
||||
using static ServiceLib.Models.ClashProxies;
|
||||
|
||||
namespace ServiceLib.Handler
|
||||
{
|
||||
public sealed class ClashApiHandler
|
||||
{
|
||||
private static readonly Lazy<ClashApiHandler> instance = new(() => new());
|
||||
public static ClashApiHandler Instance => instance.Value;
|
||||
|
||||
private Dictionary<string, ProxiesItem>? _proxies;
|
||||
public Dictionary<string, object> ProfileContent { get; set; }
|
||||
private static readonly string _tag = "ClashApiHandler";
|
||||
|
||||
public async Task<Tuple<ClashProxies, ClashProviders>?> GetClashProxiesAsync(Config config)
|
||||
{
|
||||
for (var i = 0; i < 5; i++)
|
||||
{
|
||||
var url = $"{GetApiUrl()}/proxies";
|
||||
var result = await HttpClientHelper.Instance.TryGetAsync(url);
|
||||
var clashProxies = JsonUtils.Deserialize<ClashProxies>(result);
|
||||
|
||||
var url2 = $"{GetApiUrl()}/providers/proxies";
|
||||
var result2 = await HttpClientHelper.Instance.TryGetAsync(url2);
|
||||
var clashProviders = JsonUtils.Deserialize<ClashProviders>(result2);
|
||||
|
||||
if (clashProxies != null || clashProviders != null)
|
||||
{
|
||||
_proxies = clashProxies?.proxies;
|
||||
return new Tuple<ClashProxies, ClashProviders>(clashProxies, clashProviders);
|
||||
}
|
||||
|
||||
await Task.Delay(2000);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void ClashProxiesDelayTest(bool blAll, List<ClashProxyModel> lstProxy, Action<ClashProxyModel?, string> updateFunc)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
if (blAll)
|
||||
{
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
if (_proxies != null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
Task.Delay(5000).Wait();
|
||||
}
|
||||
if (_proxies == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
lstProxy = new List<ClashProxyModel>();
|
||||
foreach (KeyValuePair<string, ProxiesItem> kv in _proxies)
|
||||
{
|
||||
if (Global.notAllowTestType.Contains(kv.Value.type.ToLower()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
lstProxy.Add(new ClashProxyModel()
|
||||
{
|
||||
Name = kv.Value.name,
|
||||
Type = kv.Value.type.ToLower(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (lstProxy == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var urlBase = $"{GetApiUrl()}/proxies";
|
||||
urlBase += @"/{0}/delay?timeout=10000&url=" + AppHandler.Instance.Config.SpeedTestItem.SpeedPingTestUrl;
|
||||
|
||||
List<Task> tasks = new List<Task>();
|
||||
foreach (var it in lstProxy)
|
||||
{
|
||||
if (Global.notAllowTestType.Contains(it.Type.ToLower()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var name = it.Name;
|
||||
var url = string.Format(urlBase, name);
|
||||
tasks.Add(Task.Run(async () =>
|
||||
{
|
||||
var result = await HttpClientHelper.Instance.TryGetAsync(url);
|
||||
updateFunc?.Invoke(it, result);
|
||||
}));
|
||||
}
|
||||
Task.WaitAll(tasks.ToArray());
|
||||
|
||||
Task.Delay(1000).Wait();
|
||||
updateFunc?.Invoke(null, "");
|
||||
});
|
||||
}
|
||||
|
||||
public List<ProxiesItem>? GetClashProxyGroups()
|
||||
{
|
||||
try
|
||||
{
|
||||
var fileContent = ProfileContent;
|
||||
if (fileContent is null || fileContent?.ContainsKey("proxy-groups") == false)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return JsonUtils.Deserialize<List<ProxiesItem>>(JsonUtils.Serialize(fileContent["proxy-groups"]));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ClashSetActiveProxy(string name, string nameNode)
|
||||
{
|
||||
try
|
||||
{
|
||||
var url = $"{GetApiUrl()}/proxies/{name}";
|
||||
Dictionary<string, string> headers = new Dictionary<string, string>();
|
||||
headers.Add("name", nameNode);
|
||||
await HttpClientHelper.Instance.PutAsync(url, headers);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ClashConfigUpdate(Dictionary<string, string> headers)
|
||||
{
|
||||
if (_proxies == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var urlBase = $"{GetApiUrl()}/configs";
|
||||
|
||||
await HttpClientHelper.Instance.PatchAsync(urlBase, headers);
|
||||
}
|
||||
|
||||
public async Task ClashConfigReload(string filePath)
|
||||
{
|
||||
await ClashConnectionClose("");
|
||||
try
|
||||
{
|
||||
var url = $"{GetApiUrl()}/configs?force=true";
|
||||
Dictionary<string, string> headers = new Dictionary<string, string>();
|
||||
headers.Add("path", filePath);
|
||||
await HttpClientHelper.Instance.PutAsync(url, headers);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ClashConnections?> GetClashConnectionsAsync(Config config)
|
||||
{
|
||||
try
|
||||
{
|
||||
var url = $"{GetApiUrl()}/connections";
|
||||
var result = await HttpClientHelper.Instance.TryGetAsync(url);
|
||||
var clashConnections = JsonUtils.Deserialize<ClashConnections>(result);
|
||||
|
||||
return clashConnections;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task ClashConnectionClose(string id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var url = $"{GetApiUrl()}/connections/{id}";
|
||||
await HttpClientHelper.Instance.DeleteAsync(url);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetApiUrl()
|
||||
{
|
||||
return $"{Global.HttpProtocol}{Global.Loopback}:{AppHandler.Instance.StatePort2}";
|
||||
}
|
||||
}
|
||||
}
|
||||
1890
v2rayN/ServiceLib/Handler/ConfigHandler.cs
Normal file
1890
v2rayN/ServiceLib/Handler/ConfigHandler.cs
Normal file
File diff suppressed because it is too large
Load Diff
132
v2rayN/ServiceLib/Handler/CoreConfigHandler.cs
Normal file
132
v2rayN/ServiceLib/Handler/CoreConfigHandler.cs
Normal file
@@ -0,0 +1,132 @@
|
||||
namespace ServiceLib.Handler
|
||||
{
|
||||
/// <summary>
|
||||
/// Core configuration file processing class
|
||||
/// </summary>
|
||||
public class CoreConfigHandler
|
||||
{
|
||||
private static readonly string _tag = "CoreConfigHandler";
|
||||
|
||||
public static async Task<RetResult> GenerateClientConfig(ProfileItem node, string? fileName)
|
||||
{
|
||||
var config = AppHandler.Instance.Config;
|
||||
var result = new RetResult();
|
||||
|
||||
if (node.ConfigType == EConfigType.Custom)
|
||||
{
|
||||
result = node.CoreType switch
|
||||
{
|
||||
ECoreType.mihomo => await new CoreConfigClashService(config).GenerateClientCustomConfig(node, fileName),
|
||||
ECoreType.sing_box => await new CoreConfigSingboxService(config).GenerateClientCustomConfig(node, fileName),
|
||||
_ => await GenerateClientCustomConfig(node, fileName)
|
||||
};
|
||||
}
|
||||
else if (AppHandler.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box)
|
||||
{
|
||||
result = await new CoreConfigSingboxService(config).GenerateClientConfigContent(node);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = await new CoreConfigV2rayService(config).GenerateClientConfigContent(node);
|
||||
}
|
||||
if (result.Success != true)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (Utils.IsNotEmpty(fileName) && result.Data != null)
|
||||
{
|
||||
await File.WriteAllTextAsync(fileName, result.Data.ToString());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static async Task<RetResult> GenerateClientCustomConfig(ProfileItem node, string? fileName)
|
||||
{
|
||||
var ret = new RetResult();
|
||||
try
|
||||
{
|
||||
if (node == null || fileName is null)
|
||||
{
|
||||
ret.Msg = ResUI.CheckServerSettings;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (File.Exists(fileName))
|
||||
{
|
||||
File.SetAttributes(fileName, FileAttributes.Normal); //If the file has a read-only attribute, direct deletion will fail
|
||||
File.Delete(fileName);
|
||||
}
|
||||
|
||||
string addressFileName = node.Address;
|
||||
if (!File.Exists(addressFileName))
|
||||
{
|
||||
addressFileName = Utils.GetConfigPath(addressFileName);
|
||||
}
|
||||
if (!File.Exists(addressFileName))
|
||||
{
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
File.Copy(addressFileName, fileName);
|
||||
File.SetAttributes(fileName, FileAttributes.Normal); //Copy will keep the attributes of addressFileName, so we need to add write permissions to fileName just in case of addressFileName is a read-only file.
|
||||
|
||||
//check again
|
||||
if (!File.Exists(fileName))
|
||||
{
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
||||
ret.Success = true;
|
||||
return ret;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<RetResult> GenerateClientSpeedtestConfig(Config config, string fileName, List<ServerTestItem> selecteds, ECoreType coreType)
|
||||
{
|
||||
var result = new RetResult();
|
||||
if (coreType == ECoreType.sing_box)
|
||||
{
|
||||
result = await new CoreConfigSingboxService(config).GenerateClientSpeedtestConfig(selecteds);
|
||||
}
|
||||
else if (coreType == ECoreType.Xray)
|
||||
{
|
||||
result = await new CoreConfigV2rayService(config).GenerateClientSpeedtestConfig(selecteds);
|
||||
}
|
||||
if (result.Success != true)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
await File.WriteAllTextAsync(fileName, result.Data.ToString());
|
||||
return result;
|
||||
}
|
||||
|
||||
public static async Task<RetResult> GenerateClientMultipleLoadConfig(Config config, string fileName, List<ProfileItem> selecteds, ECoreType coreType)
|
||||
{
|
||||
var result = new RetResult();
|
||||
if (coreType == ECoreType.sing_box)
|
||||
{
|
||||
result = await new CoreConfigSingboxService(config).GenerateClientMultipleLoadConfig(selecteds);
|
||||
}
|
||||
else if (coreType == ECoreType.Xray)
|
||||
{
|
||||
result = await new CoreConfigV2rayService(config).GenerateClientMultipleLoadConfig(selecteds);
|
||||
}
|
||||
|
||||
if (result.Success != true)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
await File.WriteAllTextAsync(fileName, result.Data.ToString());
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
368
v2rayN/ServiceLib/Handler/CoreHandler.cs
Normal file
368
v2rayN/ServiceLib/Handler/CoreHandler.cs
Normal file
@@ -0,0 +1,368 @@
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
namespace ServiceLib.Handler
|
||||
{
|
||||
/// <summary>
|
||||
/// Core process processing class
|
||||
/// </summary>
|
||||
public class CoreHandler
|
||||
{
|
||||
private static readonly Lazy<CoreHandler> _instance = new(() => new());
|
||||
public static CoreHandler Instance => _instance.Value;
|
||||
private Config _config;
|
||||
private Process? _process;
|
||||
private Process? _processPre;
|
||||
private int _linuxSudoPid = -1;
|
||||
private Action<bool, string>? _updateFunc;
|
||||
private const string _tag = "CoreHandler";
|
||||
|
||||
public async Task Init(Config config, Action<bool, string> updateFunc)
|
||||
{
|
||||
_config = config;
|
||||
_updateFunc = updateFunc;
|
||||
|
||||
Environment.SetEnvironmentVariable("V2RAY_LOCATION_ASSET", Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
|
||||
Environment.SetEnvironmentVariable("XRAY_LOCATION_ASSET", Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
|
||||
|
||||
if (Utils.IsNonWindows())
|
||||
{
|
||||
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo();
|
||||
foreach (var it in coreInfo)
|
||||
{
|
||||
if (it.CoreType == ECoreType.v2rayN)
|
||||
{
|
||||
if (Utils.UpgradeAppExists(out var upgradeFileName))
|
||||
{
|
||||
await Utils.SetLinuxChmod(upgradeFileName);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var name in it.CoreExes)
|
||||
{
|
||||
var exe = Utils.GetBinPath(Utils.GetExeName(name), it.CoreType.ToString());
|
||||
if (File.Exists(exe))
|
||||
{
|
||||
await Utils.SetLinuxChmod(exe);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task LoadCore(ProfileItem? node)
|
||||
{
|
||||
if (node == null)
|
||||
{
|
||||
UpdateFunc(false, ResUI.CheckServerSettings);
|
||||
return;
|
||||
}
|
||||
|
||||
var fileName = Utils.GetConfigPath(Global.CoreConfigFileName);
|
||||
var result = await CoreConfigHandler.GenerateClientConfig(node, fileName);
|
||||
if (result.Success != true)
|
||||
{
|
||||
UpdateFunc(true, result.Msg);
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateFunc(true, $"{node.GetSummary()}");
|
||||
UpdateFunc(false, $"{Utils.GetRuntimeInfo()}");
|
||||
UpdateFunc(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
|
||||
await CoreStop();
|
||||
await Task.Delay(100);
|
||||
await CoreStart(node);
|
||||
await CoreStartPreService(node);
|
||||
}
|
||||
|
||||
public async Task<int> LoadCoreConfigSpeedtest(List<ServerTestItem> selecteds)
|
||||
{
|
||||
var coreType = selecteds.Exists(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard) ? ECoreType.sing_box : ECoreType.Xray;
|
||||
var configPath = Utils.GetConfigPath(Global.CoreSpeedtestConfigFileName);
|
||||
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType);
|
||||
UpdateFunc(false, result.Msg);
|
||||
if (result.Success != true)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
UpdateFunc(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
|
||||
UpdateFunc(false, configPath);
|
||||
|
||||
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
|
||||
var proc = await RunProcess(coreInfo, Global.CoreSpeedtestConfigFileName, true, false);
|
||||
if (proc is null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return proc.Id;
|
||||
}
|
||||
|
||||
public async Task CoreStop()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_process != null)
|
||||
{
|
||||
await ProcUtils.ProcessKill(_process, true);
|
||||
_process = null;
|
||||
}
|
||||
|
||||
if (_processPre != null)
|
||||
{
|
||||
await ProcUtils.ProcessKill(_processPre, true);
|
||||
_processPre = null;
|
||||
}
|
||||
|
||||
if (_linuxSudoPid > 0)
|
||||
{
|
||||
await KillProcessAsLinuxSudo();
|
||||
}
|
||||
_linuxSudoPid = -1;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
}
|
||||
|
||||
#region Private
|
||||
|
||||
private async Task CoreStart(ProfileItem node)
|
||||
{
|
||||
var coreType = _config.RunningCoreType = AppHandler.Instance.GetCoreType(node, node.ConfigType);
|
||||
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
|
||||
|
||||
var displayLog = node.ConfigType != EConfigType.Custom || node.DisplayLog;
|
||||
var proc = await RunProcess(coreInfo, Global.CoreConfigFileName, displayLog, true);
|
||||
if (proc is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_process = proc;
|
||||
}
|
||||
|
||||
private async Task CoreStartPreService(ProfileItem node)
|
||||
{
|
||||
if (_process != null && !_process.HasExited)
|
||||
{
|
||||
var coreType = AppHandler.Instance.GetCoreType(node, node.ConfigType);
|
||||
var itemSocks = await ConfigHandler.GetPreSocksItem(_config, node, coreType);
|
||||
if (itemSocks != null)
|
||||
{
|
||||
var preCoreType = itemSocks.CoreType ?? ECoreType.sing_box;
|
||||
var fileName = Utils.GetConfigPath(Global.CorePreConfigFileName);
|
||||
var result = await CoreConfigHandler.GenerateClientConfig(itemSocks, fileName);
|
||||
if (result.Success)
|
||||
{
|
||||
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(preCoreType);
|
||||
var proc = await RunProcess(coreInfo, Global.CorePreConfigFileName, true, true);
|
||||
if (proc is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_processPre = proc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateFunc(bool notify, string msg)
|
||||
{
|
||||
_updateFunc?.Invoke(notify, msg);
|
||||
}
|
||||
|
||||
private bool IsNeedSudo(ECoreType eCoreType)
|
||||
{
|
||||
return _config.TunModeItem.EnableTun
|
||||
&& eCoreType == ECoreType.sing_box
|
||||
&& (Utils.IsNonWindows())
|
||||
//&& _config.TunModeItem.LinuxSudoPwd.IsNotEmpty()
|
||||
;
|
||||
}
|
||||
|
||||
#endregion Private
|
||||
|
||||
#region Process
|
||||
|
||||
private async Task<Process?> RunProcess(CoreInfo? coreInfo, string configPath, bool displayLog, bool mayNeedSudo)
|
||||
{
|
||||
var fileName = CoreInfoHandler.Instance.GetCoreExecFile(coreInfo, out var msg);
|
||||
if (Utils.IsNullOrEmpty(fileName))
|
||||
{
|
||||
UpdateFunc(false, msg);
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Process proc = new()
|
||||
{
|
||||
StartInfo = new()
|
||||
{
|
||||
FileName = fileName,
|
||||
Arguments = string.Format(coreInfo.Arguments, configPath),
|
||||
WorkingDirectory = Utils.GetConfigPath(),
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = displayLog,
|
||||
RedirectStandardError = displayLog,
|
||||
CreateNoWindow = true,
|
||||
StandardOutputEncoding = displayLog ? Encoding.UTF8 : null,
|
||||
StandardErrorEncoding = displayLog ? Encoding.UTF8 : null,
|
||||
}
|
||||
};
|
||||
|
||||
var isNeedSudo = mayNeedSudo && IsNeedSudo(coreInfo.CoreType);
|
||||
if (isNeedSudo)
|
||||
{
|
||||
await RunProcessAsLinuxSudo(proc, fileName, coreInfo, configPath);
|
||||
}
|
||||
|
||||
var startUpErrorMessage = new StringBuilder();
|
||||
var startUpSuccessful = false;
|
||||
if (displayLog)
|
||||
{
|
||||
proc.OutputDataReceived += (sender, e) =>
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(e.Data)) return;
|
||||
UpdateFunc(false, e.Data + Environment.NewLine);
|
||||
};
|
||||
proc.ErrorDataReceived += (sender, e) =>
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(e.Data)) return;
|
||||
UpdateFunc(false, e.Data + Environment.NewLine);
|
||||
|
||||
if (!startUpSuccessful)
|
||||
{
|
||||
startUpErrorMessage.Append(e.Data + Environment.NewLine);
|
||||
}
|
||||
};
|
||||
}
|
||||
proc.Start();
|
||||
|
||||
if (isNeedSudo && _config.TunModeItem.LinuxSudoPwd.IsNotEmpty())
|
||||
{
|
||||
var pwd = DesUtils.Decrypt(_config.TunModeItem.LinuxSudoPwd);
|
||||
await Task.Delay(10);
|
||||
await proc.StandardInput.WriteLineAsync(pwd);
|
||||
await Task.Delay(10);
|
||||
await proc.StandardInput.WriteLineAsync(pwd);
|
||||
}
|
||||
if (isNeedSudo) _linuxSudoPid = proc.Id;
|
||||
|
||||
if (displayLog)
|
||||
{
|
||||
proc.BeginOutputReadLine();
|
||||
proc.BeginErrorReadLine();
|
||||
}
|
||||
|
||||
if (proc.WaitForExit(1000))
|
||||
{
|
||||
proc.CancelErrorRead();
|
||||
throw new Exception(displayLog ? startUpErrorMessage.ToString() : "启动进程失败并退出 (Failed to start the process and exited)");
|
||||
}
|
||||
else
|
||||
{
|
||||
startUpSuccessful = true;
|
||||
}
|
||||
|
||||
AppHandler.Instance.AddProcess(proc.Handle);
|
||||
return proc;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
UpdateFunc(true, ex.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Process
|
||||
|
||||
#region Linux
|
||||
|
||||
private async Task RunProcessAsLinuxSudo(Process proc, string fileName, CoreInfo coreInfo, string configPath)
|
||||
{
|
||||
var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetConfigPath(configPath).AppendQuotes())}";
|
||||
|
||||
var shFilePath = await CreateLinuxShellFile(cmdLine, "run_as_sudo.sh");
|
||||
proc.StartInfo.FileName = shFilePath;
|
||||
proc.StartInfo.Arguments = "";
|
||||
proc.StartInfo.WorkingDirectory = "";
|
||||
if (_config.TunModeItem.LinuxSudoPwd.IsNotEmpty())
|
||||
{
|
||||
proc.StartInfo.StandardInputEncoding = Encoding.UTF8;
|
||||
proc.StartInfo.RedirectStandardInput = true;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task KillProcessAsLinuxSudo()
|
||||
{
|
||||
var cmdLine = $"kill {_linuxSudoPid}";
|
||||
var shFilePath = await CreateLinuxShellFile(cmdLine, "kill_as_sudo.sh");
|
||||
Process proc = new()
|
||||
{
|
||||
StartInfo = new()
|
||||
{
|
||||
FileName = shFilePath,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
StandardInputEncoding = Encoding.UTF8,
|
||||
RedirectStandardInput = true
|
||||
}
|
||||
};
|
||||
proc.Start();
|
||||
|
||||
if (_config.TunModeItem.LinuxSudoPwd.IsNotEmpty())
|
||||
{
|
||||
try
|
||||
{
|
||||
var pwd = DesUtils.Decrypt(_config.TunModeItem.LinuxSudoPwd);
|
||||
await Task.Delay(10);
|
||||
await proc.StandardInput.WriteLineAsync(pwd);
|
||||
await Task.Delay(10);
|
||||
await proc.StandardInput.WriteLineAsync(pwd);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10));
|
||||
await proc.WaitForExitAsync(timeout.Token);
|
||||
await Task.Delay(3000);
|
||||
}
|
||||
|
||||
private async Task<string> CreateLinuxShellFile(string cmdLine, string fileName)
|
||||
{
|
||||
//Shell scripts
|
||||
var shFilePath = Utils.GetBinPath(AppHandler.Instance.IsAdministrator ? "root_" + fileName : fileName);
|
||||
File.Delete(shFilePath);
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("#!/bin/sh");
|
||||
if (AppHandler.Instance.IsAdministrator)
|
||||
{
|
||||
sb.AppendLine($"{cmdLine}");
|
||||
}
|
||||
else if (_config.TunModeItem.LinuxSudoPwd.IsNullOrEmpty())
|
||||
{
|
||||
sb.AppendLine($"pkexec {cmdLine}");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.AppendLine($"sudo -S {cmdLine}");
|
||||
}
|
||||
|
||||
await File.WriteAllTextAsync(shFilePath, sb.ToString());
|
||||
await Utils.SetLinuxChmod(shFilePath);
|
||||
Logging.SaveLog(shFilePath);
|
||||
|
||||
return shFilePath;
|
||||
}
|
||||
|
||||
#endregion Linux
|
||||
}
|
||||
}
|
||||
200
v2rayN/ServiceLib/Handler/CoreInfoHandler.cs
Normal file
200
v2rayN/ServiceLib/Handler/CoreInfoHandler.cs
Normal file
@@ -0,0 +1,200 @@
|
||||
namespace ServiceLib.Handler
|
||||
{
|
||||
public sealed class CoreInfoHandler
|
||||
{
|
||||
private static readonly Lazy<CoreInfoHandler> _instance = new(() => new());
|
||||
private List<CoreInfo>? _coreInfo;
|
||||
public static CoreInfoHandler Instance => _instance.Value;
|
||||
|
||||
public CoreInfoHandler()
|
||||
{
|
||||
InitCoreInfo();
|
||||
}
|
||||
|
||||
public CoreInfo? GetCoreInfo(ECoreType coreType)
|
||||
{
|
||||
if (_coreInfo == null)
|
||||
{
|
||||
InitCoreInfo();
|
||||
}
|
||||
return _coreInfo?.FirstOrDefault(t => t.CoreType == coreType);
|
||||
}
|
||||
|
||||
public List<CoreInfo> GetCoreInfo()
|
||||
{
|
||||
if (_coreInfo == null)
|
||||
{
|
||||
InitCoreInfo();
|
||||
}
|
||||
return _coreInfo ?? [];
|
||||
}
|
||||
|
||||
public string GetCoreExecFile(CoreInfo? coreInfo, out string msg)
|
||||
{
|
||||
var fileName = string.Empty;
|
||||
msg = string.Empty;
|
||||
foreach (var name in coreInfo?.CoreExes)
|
||||
{
|
||||
var vName = Utils.GetBinPath(Utils.GetExeName(name), coreInfo.CoreType.ToString());
|
||||
if (File.Exists(vName))
|
||||
{
|
||||
fileName = vName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fileName.IsNullOrEmpty())
|
||||
{
|
||||
msg = string.Format(ResUI.NotFoundCore, Utils.GetBinPath("", coreInfo.CoreType.ToString()), string.Join(", ", coreInfo.CoreExes.ToArray()), coreInfo.Url);
|
||||
Logging.SaveLog(msg);
|
||||
}
|
||||
return fileName;
|
||||
}
|
||||
|
||||
private void InitCoreInfo()
|
||||
{
|
||||
_coreInfo = [];
|
||||
|
||||
_coreInfo.Add(new CoreInfo
|
||||
{
|
||||
CoreType = ECoreType.v2rayN,
|
||||
Url = Global.NUrl,
|
||||
ReleaseApiUrl = Global.NUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
|
||||
DownloadUrlWin64 = Global.NUrl + "/download/{0}/v2rayN-windows-64.zip",
|
||||
DownloadUrlWinArm64 = Global.NUrl + "/download/{0}/v2rayN-windows-arm64.zip",
|
||||
DownloadUrlLinux64 = Global.NUrl + "/download/{0}/v2rayN-linux-64.zip",
|
||||
DownloadUrlLinuxArm64 = Global.NUrl + "/download/{0}/v2rayN-linux-arm64.zip",
|
||||
DownloadUrlOSX64 = Global.NUrl + "/download/{0}/v2rayN-macos-64.zip",
|
||||
DownloadUrlOSXArm64 = Global.NUrl + "/download/{0}/v2rayN-macos-arm64.zip",
|
||||
});
|
||||
|
||||
_coreInfo.Add(new CoreInfo
|
||||
{
|
||||
CoreType = ECoreType.v2fly,
|
||||
CoreExes = new List<string> { "wv2ray", "v2ray" },
|
||||
Arguments = "",
|
||||
Url = Global.V2flyCoreUrl,
|
||||
ReleaseApiUrl = Global.V2flyCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
|
||||
Match = "V2Ray",
|
||||
VersionArg = "-version",
|
||||
RedirectInfo = true,
|
||||
});
|
||||
|
||||
_coreInfo.Add(new CoreInfo
|
||||
{
|
||||
CoreType = ECoreType.v2fly_v5,
|
||||
CoreExes = new List<string> { "v2ray" },
|
||||
Arguments = "run -c config.json -format jsonv5",
|
||||
Url = Global.V2flyCoreUrl,
|
||||
ReleaseApiUrl = Global.V2flyCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
|
||||
Match = "V2Ray",
|
||||
VersionArg = "version",
|
||||
RedirectInfo = true,
|
||||
});
|
||||
|
||||
_coreInfo.Add(new CoreInfo
|
||||
{
|
||||
CoreType = ECoreType.Xray,
|
||||
CoreExes = new List<string> { "xray", "wxray" },
|
||||
Arguments = "run -c {0}",
|
||||
Url = Global.XrayCoreUrl,
|
||||
ReleaseApiUrl = Global.XrayCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
|
||||
DownloadUrlWin64 = Global.XrayCoreUrl + "/download/{0}/Xray-windows-64.zip",
|
||||
DownloadUrlWinArm64 = Global.XrayCoreUrl + "/download/{0}/Xray-windows-arm64-v8a.zip",
|
||||
DownloadUrlLinux64 = Global.XrayCoreUrl + "/download/{0}/Xray-linux-64.zip",
|
||||
DownloadUrlLinuxArm64 = Global.XrayCoreUrl + "/download/{0}/Xray-linux-arm64-v8a.zip",
|
||||
DownloadUrlOSX64 = Global.XrayCoreUrl + "/download/{0}/Xray-macos-64.zip",
|
||||
DownloadUrlOSXArm64 = Global.XrayCoreUrl + "/download/{0}/Xray-macos-arm64-v8a.zip",
|
||||
Match = "Xray",
|
||||
VersionArg = "-version",
|
||||
RedirectInfo = true,
|
||||
});
|
||||
|
||||
_coreInfo.Add(new CoreInfo
|
||||
{
|
||||
CoreType = ECoreType.mihomo,
|
||||
CoreExes = new List<string> { "mihomo-windows-amd64-compatible", "mihomo-windows-amd64", "mihomo-linux-amd64", "mihomo", "clash" },
|
||||
Arguments = "-f config.json" + PortableMode(),
|
||||
Url = Global.MihomoCoreUrl,
|
||||
ReleaseApiUrl = Global.MihomoCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
|
||||
DownloadUrlWin64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-windows-amd64-compatible-{0}.zip",
|
||||
DownloadUrlWinArm64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-windows-arm64-{0}.zip",
|
||||
DownloadUrlLinux64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-linux-amd64-compatible-{0}.gz",
|
||||
DownloadUrlLinuxArm64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-linux-arm64-{0}.gz",
|
||||
DownloadUrlOSX64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-darwin-amd64-compatible-{0}.gz",
|
||||
DownloadUrlOSXArm64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-darwin-arm64-{0}.gz",
|
||||
Match = "Mihomo",
|
||||
VersionArg = "-v",
|
||||
RedirectInfo = true,
|
||||
});
|
||||
|
||||
_coreInfo.Add(new CoreInfo
|
||||
{
|
||||
CoreType = ECoreType.hysteria,
|
||||
CoreExes = new List<string> { "hysteria-windows-amd64", "hysteria" },
|
||||
Arguments = "",
|
||||
Url = Global.HysteriaCoreUrl,
|
||||
ReleaseApiUrl = Global.HysteriaCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
|
||||
RedirectInfo = true,
|
||||
});
|
||||
|
||||
_coreInfo.Add(new CoreInfo
|
||||
{
|
||||
CoreType = ECoreType.naiveproxy,
|
||||
CoreExes = new List<string> { "naiveproxy", "naive" },
|
||||
Arguments = "config.json",
|
||||
Url = Global.NaiveproxyCoreUrl,
|
||||
RedirectInfo = false,
|
||||
});
|
||||
|
||||
_coreInfo.Add(new CoreInfo
|
||||
{
|
||||
CoreType = ECoreType.tuic,
|
||||
CoreExes = new List<string> { "tuic-client", "tuic" },
|
||||
Arguments = "-c config.json",
|
||||
Url = Global.TuicCoreUrl,
|
||||
RedirectInfo = true,
|
||||
});
|
||||
|
||||
_coreInfo.Add(new CoreInfo
|
||||
{
|
||||
CoreType = ECoreType.sing_box,
|
||||
CoreExes = new List<string> { "sing-box-client", "sing-box" },
|
||||
Arguments = "run -c {0} --disable-color",
|
||||
Url = Global.SingboxCoreUrl,
|
||||
RedirectInfo = true,
|
||||
ReleaseApiUrl = Global.SingboxCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
|
||||
DownloadUrlWin64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-windows-amd64.zip",
|
||||
DownloadUrlWinArm64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-windows-arm64.zip",
|
||||
DownloadUrlLinux64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-linux-amd64.tar.gz",
|
||||
DownloadUrlLinuxArm64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-linux-arm64.tar.gz",
|
||||
DownloadUrlOSX64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-darwin-amd64.tar.gz",
|
||||
DownloadUrlOSXArm64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-darwin-arm64.tar.gz",
|
||||
Match = "sing-box",
|
||||
VersionArg = "version",
|
||||
});
|
||||
|
||||
_coreInfo.Add(new CoreInfo
|
||||
{
|
||||
CoreType = ECoreType.juicity,
|
||||
CoreExes = new List<string> { "juicity-client", "juicity" },
|
||||
Arguments = "run -c config.json",
|
||||
Url = Global.JuicityCoreUrl
|
||||
});
|
||||
|
||||
_coreInfo.Add(new CoreInfo
|
||||
{
|
||||
CoreType = ECoreType.hysteria2,
|
||||
CoreExes = new List<string> { "hysteria-windows-amd64", "hysteria-linux-amd64", "hysteria" },
|
||||
Arguments = "",
|
||||
Url = Global.HysteriaCoreUrl,
|
||||
ReleaseApiUrl = Global.HysteriaCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
|
||||
RedirectInfo = true,
|
||||
});
|
||||
}
|
||||
|
||||
private string PortableMode()
|
||||
{
|
||||
return $" -d {Utils.GetBinPath("").AppendQuotes()}";
|
||||
}
|
||||
}
|
||||
}
|
||||
239
v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs
Normal file
239
v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs
Normal file
@@ -0,0 +1,239 @@
|
||||
using System.Collections.Specialized;
|
||||
|
||||
namespace ServiceLib.Handler.Fmt
|
||||
{
|
||||
public class BaseFmt
|
||||
{
|
||||
protected static string GetIpv6(string address)
|
||||
{
|
||||
if (Utils.IsIpv6(address))
|
||||
{
|
||||
// 检查地址是否已经被方括号包围,如果没有,则添加方括号
|
||||
return address.StartsWith('[') && address.EndsWith(']') ? address : $"[{address}]";
|
||||
}
|
||||
return address; // 如果不是IPv6地址,直接返回原地址
|
||||
}
|
||||
|
||||
protected static int GetStdTransport(ProfileItem item, string? securityDef, ref Dictionary<string, string> dicQuery)
|
||||
{
|
||||
if (Utils.IsNotEmpty(item.Flow))
|
||||
{
|
||||
dicQuery.Add("flow", item.Flow);
|
||||
}
|
||||
|
||||
if (Utils.IsNotEmpty(item.StreamSecurity))
|
||||
{
|
||||
dicQuery.Add("security", item.StreamSecurity);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (securityDef != null)
|
||||
{
|
||||
dicQuery.Add("security", securityDef);
|
||||
}
|
||||
}
|
||||
if (Utils.IsNotEmpty(item.Sni))
|
||||
{
|
||||
dicQuery.Add("sni", item.Sni);
|
||||
}
|
||||
if (Utils.IsNotEmpty(item.Alpn))
|
||||
{
|
||||
dicQuery.Add("alpn", Utils.UrlEncode(item.Alpn));
|
||||
}
|
||||
if (Utils.IsNotEmpty(item.Fingerprint))
|
||||
{
|
||||
dicQuery.Add("fp", Utils.UrlEncode(item.Fingerprint));
|
||||
}
|
||||
if (Utils.IsNotEmpty(item.PublicKey))
|
||||
{
|
||||
dicQuery.Add("pbk", Utils.UrlEncode(item.PublicKey));
|
||||
}
|
||||
if (Utils.IsNotEmpty(item.ShortId))
|
||||
{
|
||||
dicQuery.Add("sid", Utils.UrlEncode(item.ShortId));
|
||||
}
|
||||
if (Utils.IsNotEmpty(item.SpiderX))
|
||||
{
|
||||
dicQuery.Add("spx", Utils.UrlEncode(item.SpiderX));
|
||||
}
|
||||
if (item.AllowInsecure.Equals("true"))
|
||||
{
|
||||
dicQuery.Add("allowInsecure", "1");
|
||||
}
|
||||
|
||||
dicQuery.Add("type", Utils.IsNotEmpty(item.Network) ? item.Network : nameof(ETransport.tcp));
|
||||
|
||||
switch (item.Network)
|
||||
{
|
||||
case nameof(ETransport.tcp):
|
||||
dicQuery.Add("headerType", Utils.IsNotEmpty(item.HeaderType) ? item.HeaderType : Global.None);
|
||||
if (Utils.IsNotEmpty(item.RequestHost))
|
||||
{
|
||||
dicQuery.Add("host", Utils.UrlEncode(item.RequestHost));
|
||||
}
|
||||
break;
|
||||
|
||||
case nameof(ETransport.kcp):
|
||||
dicQuery.Add("headerType", Utils.IsNotEmpty(item.HeaderType) ? item.HeaderType : Global.None);
|
||||
if (Utils.IsNotEmpty(item.Path))
|
||||
{
|
||||
dicQuery.Add("seed", Utils.UrlEncode(item.Path));
|
||||
}
|
||||
break;
|
||||
|
||||
case nameof(ETransport.ws):
|
||||
case nameof(ETransport.httpupgrade):
|
||||
if (Utils.IsNotEmpty(item.RequestHost))
|
||||
{
|
||||
dicQuery.Add("host", Utils.UrlEncode(item.RequestHost));
|
||||
}
|
||||
if (Utils.IsNotEmpty(item.Path))
|
||||
{
|
||||
dicQuery.Add("path", Utils.UrlEncode(item.Path));
|
||||
}
|
||||
break;
|
||||
|
||||
case nameof(ETransport.xhttp):
|
||||
if (Utils.IsNotEmpty(item.RequestHost))
|
||||
{
|
||||
dicQuery.Add("host", Utils.UrlEncode(item.RequestHost));
|
||||
}
|
||||
if (Utils.IsNotEmpty(item.Path))
|
||||
{
|
||||
dicQuery.Add("path", Utils.UrlEncode(item.Path));
|
||||
}
|
||||
if (Utils.IsNotEmpty(item.HeaderType) && Global.XhttpMode.Contains(item.HeaderType))
|
||||
{
|
||||
dicQuery.Add("mode", Utils.UrlEncode(item.HeaderType));
|
||||
}
|
||||
if (Utils.IsNotEmpty(item.Extra))
|
||||
{
|
||||
dicQuery.Add("extra", Utils.UrlEncode(item.Extra));
|
||||
}
|
||||
break;
|
||||
|
||||
case nameof(ETransport.http):
|
||||
case nameof(ETransport.h2):
|
||||
dicQuery["type"] = nameof(ETransport.http);
|
||||
if (Utils.IsNotEmpty(item.RequestHost))
|
||||
{
|
||||
dicQuery.Add("host", Utils.UrlEncode(item.RequestHost));
|
||||
}
|
||||
if (Utils.IsNotEmpty(item.Path))
|
||||
{
|
||||
dicQuery.Add("path", Utils.UrlEncode(item.Path));
|
||||
}
|
||||
break;
|
||||
|
||||
case nameof(ETransport.quic):
|
||||
dicQuery.Add("headerType", Utils.IsNotEmpty(item.HeaderType) ? item.HeaderType : Global.None);
|
||||
dicQuery.Add("quicSecurity", Utils.UrlEncode(item.RequestHost));
|
||||
dicQuery.Add("key", Utils.UrlEncode(item.Path));
|
||||
break;
|
||||
|
||||
case nameof(ETransport.grpc):
|
||||
if (Utils.IsNotEmpty(item.Path))
|
||||
{
|
||||
dicQuery.Add("authority", Utils.UrlEncode(item.RequestHost));
|
||||
dicQuery.Add("serviceName", Utils.UrlEncode(item.Path));
|
||||
if (item.HeaderType is Global.GrpcGunMode or Global.GrpcMultiMode)
|
||||
{
|
||||
dicQuery.Add("mode", Utils.UrlEncode(item.HeaderType));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected static int ResolveStdTransport(NameValueCollection query, ref ProfileItem item)
|
||||
{
|
||||
item.Flow = query["flow"] ?? "";
|
||||
item.StreamSecurity = query["security"] ?? "";
|
||||
item.Sni = query["sni"] ?? "";
|
||||
item.Alpn = Utils.UrlDecode(query["alpn"] ?? "");
|
||||
item.Fingerprint = Utils.UrlDecode(query["fp"] ?? "");
|
||||
item.PublicKey = Utils.UrlDecode(query["pbk"] ?? "");
|
||||
item.ShortId = Utils.UrlDecode(query["sid"] ?? "");
|
||||
item.SpiderX = Utils.UrlDecode(query["spx"] ?? "");
|
||||
item.AllowInsecure = (query["allowInsecure"] ?? "") == "1" ? "true" : "";
|
||||
|
||||
item.Network = query["type"] ?? nameof(ETransport.tcp);
|
||||
switch (item.Network)
|
||||
{
|
||||
case nameof(ETransport.tcp):
|
||||
item.HeaderType = query["headerType"] ?? Global.None;
|
||||
item.RequestHost = Utils.UrlDecode(query["host"] ?? "");
|
||||
|
||||
break;
|
||||
|
||||
case nameof(ETransport.kcp):
|
||||
item.HeaderType = query["headerType"] ?? Global.None;
|
||||
item.Path = Utils.UrlDecode(query["seed"] ?? "");
|
||||
break;
|
||||
|
||||
case nameof(ETransport.ws):
|
||||
case nameof(ETransport.httpupgrade):
|
||||
item.RequestHost = Utils.UrlDecode(query["host"] ?? "");
|
||||
item.Path = Utils.UrlDecode(query["path"] ?? "/");
|
||||
break;
|
||||
|
||||
case nameof(ETransport.xhttp):
|
||||
item.RequestHost = Utils.UrlDecode(query["host"] ?? "");
|
||||
item.Path = Utils.UrlDecode(query["path"] ?? "/");
|
||||
item.HeaderType = Utils.UrlDecode(query["mode"] ?? "");
|
||||
item.Extra = Utils.UrlDecode(query["extra"] ?? "");
|
||||
break;
|
||||
|
||||
case nameof(ETransport.http):
|
||||
case nameof(ETransport.h2):
|
||||
item.Network = nameof(ETransport.h2);
|
||||
item.RequestHost = Utils.UrlDecode(query["host"] ?? "");
|
||||
item.Path = Utils.UrlDecode(query["path"] ?? "/");
|
||||
break;
|
||||
|
||||
case nameof(ETransport.quic):
|
||||
item.HeaderType = query["headerType"] ?? Global.None;
|
||||
item.RequestHost = query["quicSecurity"] ?? Global.None;
|
||||
item.Path = Utils.UrlDecode(query["key"] ?? "");
|
||||
break;
|
||||
|
||||
case nameof(ETransport.grpc):
|
||||
item.RequestHost = Utils.UrlDecode(query["authority"] ?? "");
|
||||
item.Path = Utils.UrlDecode(query["serviceName"] ?? "");
|
||||
item.HeaderType = Utils.UrlDecode(query["mode"] ?? Global.GrpcGunMode);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected static bool Contains(string str, params string[] s)
|
||||
{
|
||||
foreach (var item in s)
|
||||
{
|
||||
if (str.Contains(item, StringComparison.OrdinalIgnoreCase)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static string WriteAllText(string strData, string ext = "json")
|
||||
{
|
||||
var fileName = Utils.GetTempPath($"{Utils.GetGuid(false)}.{ext}");
|
||||
File.WriteAllText(fileName, strData);
|
||||
return fileName;
|
||||
}
|
||||
|
||||
protected static string ToUri(EConfigType eConfigType, string address, object port, string userInfo, Dictionary<string, string>? dicQuery, string? remark)
|
||||
{
|
||||
var query = dicQuery != null
|
||||
? ("?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray()))
|
||||
: string.Empty;
|
||||
|
||||
var url = $"{Utils.UrlEncode(userInfo)}@{GetIpv6(address)}:{port}";
|
||||
return $"{Global.ProtocolShares[eConfigType]}{url}{query}{remark}";
|
||||
}
|
||||
}
|
||||
}
|
||||
23
v2rayN/ServiceLib/Handler/Fmt/ClashFmt.cs
Normal file
23
v2rayN/ServiceLib/Handler/Fmt/ClashFmt.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
namespace ServiceLib.Handler.Fmt
|
||||
{
|
||||
public class ClashFmt : BaseFmt
|
||||
{
|
||||
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
|
||||
{
|
||||
if (Contains(strData, "port", "socks-port", "proxies"))
|
||||
{
|
||||
var fileName = WriteAllText(strData, "yaml");
|
||||
|
||||
var profileItem = new ProfileItem
|
||||
{
|
||||
CoreType = ECoreType.mihomo,
|
||||
Address = fileName,
|
||||
Remarks = subRemarks ?? "clash_custom"
|
||||
};
|
||||
return profileItem;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
92
v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs
Normal file
92
v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
namespace ServiceLib.Handler.Fmt
|
||||
{
|
||||
public class FmtHandler
|
||||
{
|
||||
private static readonly string _tag = "FmtHandler";
|
||||
|
||||
public static string? GetShareUri(ProfileItem item)
|
||||
{
|
||||
try
|
||||
{
|
||||
var url = item.ConfigType switch
|
||||
{
|
||||
EConfigType.VMess => VmessFmt.ToUri(item),
|
||||
EConfigType.Shadowsocks => ShadowsocksFmt.ToUri(item),
|
||||
EConfigType.SOCKS => SocksFmt.ToUri(item),
|
||||
EConfigType.Trojan => TrojanFmt.ToUri(item),
|
||||
EConfigType.VLESS => VLESSFmt.ToUri(item),
|
||||
EConfigType.Hysteria2 => Hysteria2Fmt.ToUri(item),
|
||||
EConfigType.TUIC => TuicFmt.ToUri(item),
|
||||
EConfigType.WireGuard => WireguardFmt.ToUri(item),
|
||||
_ => null,
|
||||
};
|
||||
|
||||
return url;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static ProfileItem? ResolveConfig(string config, out string msg)
|
||||
{
|
||||
msg = ResUI.ConfigurationFormatIncorrect;
|
||||
|
||||
try
|
||||
{
|
||||
string str = config.TrimEx();
|
||||
if (Utils.IsNullOrEmpty(str))
|
||||
{
|
||||
msg = ResUI.FailedReadConfiguration;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (str.StartsWith(Global.ProtocolShares[EConfigType.VMess]))
|
||||
{
|
||||
return VmessFmt.Resolve(str, out msg);
|
||||
}
|
||||
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Shadowsocks]))
|
||||
{
|
||||
return ShadowsocksFmt.Resolve(str, out msg);
|
||||
}
|
||||
else if (str.StartsWith(Global.ProtocolShares[EConfigType.SOCKS]))
|
||||
{
|
||||
return SocksFmt.Resolve(str, out msg);
|
||||
}
|
||||
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Trojan]))
|
||||
{
|
||||
return TrojanFmt.Resolve(str, out msg);
|
||||
}
|
||||
else if (str.StartsWith(Global.ProtocolShares[EConfigType.VLESS]))
|
||||
{
|
||||
return VLESSFmt.Resolve(str, out msg);
|
||||
}
|
||||
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Hysteria2]) || str.StartsWith(Global.Hysteria2ProtocolShare))
|
||||
{
|
||||
return Hysteria2Fmt.Resolve(str, out msg);
|
||||
}
|
||||
else if (str.StartsWith(Global.ProtocolShares[EConfigType.TUIC]))
|
||||
{
|
||||
return TuicFmt.Resolve(str, out msg);
|
||||
}
|
||||
else if (str.StartsWith(Global.ProtocolShares[EConfigType.WireGuard]))
|
||||
{
|
||||
return WireguardFmt.Resolve(str, out msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = ResUI.NonvmessOrssProtocol;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
msg = ResUI.Incorrectconfiguration;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
94
v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs
Normal file
94
v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs
Normal file
@@ -0,0 +1,94 @@
|
||||
namespace ServiceLib.Handler.Fmt
|
||||
{
|
||||
public class Hysteria2Fmt : BaseFmt
|
||||
{
|
||||
public static ProfileItem? Resolve(string str, out string msg)
|
||||
{
|
||||
msg = ResUI.ConfigurationFormatIncorrect;
|
||||
ProfileItem item = new()
|
||||
{
|
||||
ConfigType = EConfigType.Hysteria2
|
||||
};
|
||||
|
||||
var url = Utils.TryUri(str);
|
||||
if (url == null) return null;
|
||||
|
||||
item.Address = url.IdnHost;
|
||||
item.Port = url.Port;
|
||||
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
||||
item.Id = Utils.UrlDecode(url.UserInfo);
|
||||
|
||||
var query = Utils.ParseQueryString(url.Query);
|
||||
ResolveStdTransport(query, ref item);
|
||||
item.Path = Utils.UrlDecode(query["obfs-password"] ?? "");
|
||||
item.AllowInsecure = (query["insecure"] ?? "") == "1" ? "true" : "false";
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public static string? ToUri(ProfileItem? item)
|
||||
{
|
||||
if (item == null) return null;
|
||||
string url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
if (Utils.IsNotEmpty(item.Remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.Remarks);
|
||||
}
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
if (Utils.IsNotEmpty(item.Sni))
|
||||
{
|
||||
dicQuery.Add("sni", item.Sni);
|
||||
}
|
||||
if (Utils.IsNotEmpty(item.Alpn))
|
||||
{
|
||||
dicQuery.Add("alpn", Utils.UrlEncode(item.Alpn));
|
||||
}
|
||||
if (Utils.IsNotEmpty(item.Path))
|
||||
{
|
||||
dicQuery.Add("obfs", "salamander");
|
||||
dicQuery.Add("obfs-password", Utils.UrlEncode(item.Path));
|
||||
}
|
||||
dicQuery.Add("insecure", item.AllowInsecure.ToLower() == "true" ? "1" : "0");
|
||||
|
||||
return ToUri(EConfigType.Hysteria2, item.Address, item.Port, item.Id, dicQuery, remark);
|
||||
}
|
||||
|
||||
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
|
||||
{
|
||||
if (Contains(strData, "server", "up", "down", "listen", "<html>", "<body>"))
|
||||
{
|
||||
var fileName = WriteAllText(strData);
|
||||
|
||||
var profileItem = new ProfileItem
|
||||
{
|
||||
CoreType = ECoreType.hysteria,
|
||||
Address = fileName,
|
||||
Remarks = subRemarks ?? "hysteria_custom"
|
||||
};
|
||||
return profileItem;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ProfileItem? ResolveFull2(string strData, string? subRemarks)
|
||||
{
|
||||
if (Contains(strData, "server", "auth", "up", "down", "listen"))
|
||||
{
|
||||
var fileName = WriteAllText(strData);
|
||||
|
||||
var profileItem = new ProfileItem
|
||||
{
|
||||
CoreType = ECoreType.hysteria2,
|
||||
Address = fileName,
|
||||
Remarks = subRemarks ?? "hysteria2_custom"
|
||||
};
|
||||
return profileItem;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
23
v2rayN/ServiceLib/Handler/Fmt/NaiveproxyFmt.cs
Normal file
23
v2rayN/ServiceLib/Handler/Fmt/NaiveproxyFmt.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
namespace ServiceLib.Handler.Fmt
|
||||
{
|
||||
public class NaiveproxyFmt : BaseFmt
|
||||
{
|
||||
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
|
||||
{
|
||||
if (Contains(strData, "listen", "proxy", "<html>", "<body>"))
|
||||
{
|
||||
var fileName = WriteAllText(strData);
|
||||
|
||||
var profileItem = new ProfileItem
|
||||
{
|
||||
CoreType = ECoreType.naiveproxy,
|
||||
Address = fileName,
|
||||
Remarks = subRemarks ?? "naiveproxy_custom"
|
||||
};
|
||||
return profileItem;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
172
v2rayN/ServiceLib/Handler/Fmt/ShadowsocksFmt.cs
Normal file
172
v2rayN/ServiceLib/Handler/Fmt/ShadowsocksFmt.cs
Normal file
@@ -0,0 +1,172 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ServiceLib.Handler.Fmt
|
||||
{
|
||||
public class ShadowsocksFmt : BaseFmt
|
||||
{
|
||||
public static ProfileItem? Resolve(string str, out string msg)
|
||||
{
|
||||
msg = ResUI.ConfigurationFormatIncorrect;
|
||||
ProfileItem? item;
|
||||
|
||||
item = ResolveSSLegacy(str) ?? ResolveSip002(str);
|
||||
if (item == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (item.Address.Length == 0 || item.Port == 0 || item.Security.Length == 0 || item.Id.Length == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
item.ConfigType = EConfigType.Shadowsocks;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public static string? ToUri(ProfileItem? item)
|
||||
{
|
||||
if (item == null) return null;
|
||||
string url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
if (Utils.IsNotEmpty(item.Remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.Remarks);
|
||||
}
|
||||
//url = string.Format("{0}:{1}@{2}:{3}",
|
||||
// item.security,
|
||||
// item.id,
|
||||
// item.address,
|
||||
// item.port);
|
||||
//url = Utile.Base64Encode(url);
|
||||
//new Sip002
|
||||
var pw = Utils.Base64Encode($"{item.Security}:{item.Id}");
|
||||
return ToUri(EConfigType.Shadowsocks, item.Address, item.Port, pw, null, remark);
|
||||
}
|
||||
|
||||
private static readonly Regex UrlFinder = new(@"ss://(?<base64>[A-Za-z0-9+-/=_]+)(?:#(?<tag>\S+))?", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
private static readonly Regex DetailsParser = new(@"^((?<method>.+?):(?<password>.*)@(?<hostname>.+?):(?<port>\d+?))$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
private static ProfileItem? ResolveSSLegacy(string result)
|
||||
{
|
||||
var match = UrlFinder.Match(result);
|
||||
if (!match.Success)
|
||||
return null;
|
||||
|
||||
ProfileItem item = new();
|
||||
var base64 = match.Groups["base64"].Value.TrimEnd('/');
|
||||
var tag = match.Groups["tag"].Value;
|
||||
if (Utils.IsNotEmpty(tag))
|
||||
{
|
||||
item.Remarks = Utils.UrlDecode(tag);
|
||||
}
|
||||
Match details;
|
||||
try
|
||||
{
|
||||
details = DetailsParser.Match(Utils.Base64Decode(base64));
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (!details.Success)
|
||||
return null;
|
||||
item.Security = details.Groups["method"].Value;
|
||||
item.Id = details.Groups["password"].Value;
|
||||
item.Address = details.Groups["hostname"].Value;
|
||||
item.Port = Utils.ToInt(details.Groups["port"].Value);
|
||||
return item;
|
||||
}
|
||||
|
||||
private static ProfileItem? ResolveSip002(string result)
|
||||
{
|
||||
var parsedUrl = Utils.TryUri(result);
|
||||
if (parsedUrl == null) return null;
|
||||
|
||||
ProfileItem item = new()
|
||||
{
|
||||
Remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped),
|
||||
Address = parsedUrl.IdnHost,
|
||||
Port = parsedUrl.Port,
|
||||
};
|
||||
var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo);
|
||||
//2022-blake3
|
||||
if (rawUserInfo.Contains(':'))
|
||||
{
|
||||
string[] userInfoParts = rawUserInfo.Split(new[] { ':' }, 2);
|
||||
if (userInfoParts.Length != 2)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
item.Security = userInfoParts.First();
|
||||
item.Id = Utils.UrlDecode(userInfoParts.Last());
|
||||
}
|
||||
else
|
||||
{
|
||||
// parse base64 UserInfo
|
||||
string userInfo = Utils.Base64Decode(rawUserInfo);
|
||||
string[] userInfoParts = userInfo.Split(new[] { ':' }, 2);
|
||||
if (userInfoParts.Length != 2)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
item.Security = userInfoParts.First();
|
||||
item.Id = userInfoParts.Last();
|
||||
}
|
||||
|
||||
var queryParameters = Utils.ParseQueryString(parsedUrl.Query);
|
||||
if (queryParameters["plugin"] != null)
|
||||
{
|
||||
//obfs-host exists
|
||||
var obfsHost = queryParameters["plugin"]?.Split(';').FirstOrDefault(t => t.Contains("obfs-host"));
|
||||
if (queryParameters["plugin"].Contains("obfs=http") && Utils.IsNotEmpty(obfsHost))
|
||||
{
|
||||
obfsHost = obfsHost?.Replace("obfs-host=", "");
|
||||
item.Network = Global.DefaultNetwork;
|
||||
item.HeaderType = Global.TcpHeaderHttp;
|
||||
item.RequestHost = obfsHost ?? "";
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public static List<ProfileItem>? ResolveSip008(string result)
|
||||
{
|
||||
//SsSIP008
|
||||
var lstSsServer = JsonUtils.Deserialize<List<SsServer>>(result);
|
||||
if (lstSsServer?.Count <= 0)
|
||||
{
|
||||
var ssSIP008 = JsonUtils.Deserialize<SsSIP008>(result);
|
||||
if (ssSIP008?.servers?.Count > 0)
|
||||
{
|
||||
lstSsServer = ssSIP008.servers;
|
||||
}
|
||||
}
|
||||
|
||||
if (lstSsServer?.Count > 0)
|
||||
{
|
||||
List<ProfileItem> lst = [];
|
||||
foreach (var it in lstSsServer)
|
||||
{
|
||||
var ssItem = new ProfileItem()
|
||||
{
|
||||
Remarks = it.remarks,
|
||||
Security = it.method,
|
||||
Id = it.password,
|
||||
Address = it.server,
|
||||
Port = Utils.ToInt(it.server_port)
|
||||
};
|
||||
lst.Add(ssItem);
|
||||
}
|
||||
return lst;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
55
v2rayN/ServiceLib/Handler/Fmt/SingboxFmt.cs
Normal file
55
v2rayN/ServiceLib/Handler/Fmt/SingboxFmt.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
namespace ServiceLib.Handler.Fmt
|
||||
{
|
||||
public class SingboxFmt : BaseFmt
|
||||
{
|
||||
public static List<ProfileItem>? ResolveFullArray(string strData, string? subRemarks)
|
||||
{
|
||||
var configObjects = JsonUtils.Deserialize<Object[]>(strData);
|
||||
if (configObjects != null && configObjects.Length > 0)
|
||||
{
|
||||
List<ProfileItem> lstResult = [];
|
||||
foreach (var configObject in configObjects)
|
||||
{
|
||||
var objectString = JsonUtils.Serialize(configObject);
|
||||
var singboxCon = JsonUtils.Deserialize<SingboxConfig>(objectString);
|
||||
if (singboxCon?.inbounds?.Count > 0
|
||||
&& singboxCon.outbounds?.Count > 0
|
||||
&& singboxCon.route != null)
|
||||
{
|
||||
var fileName = WriteAllText(objectString);
|
||||
|
||||
var profileIt = new ProfileItem
|
||||
{
|
||||
CoreType = ECoreType.sing_box,
|
||||
Address = fileName,
|
||||
Remarks = subRemarks ?? "singbox_custom",
|
||||
};
|
||||
lstResult.Add(profileIt);
|
||||
}
|
||||
}
|
||||
return lstResult;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
|
||||
{
|
||||
var singboxConfig = JsonUtils.Deserialize<SingboxConfig>(strData);
|
||||
if (singboxConfig?.inbounds?.Count > 0
|
||||
&& singboxConfig.outbounds?.Count > 0
|
||||
&& singboxConfig.route != null)
|
||||
{
|
||||
var fileName = WriteAllText(strData);
|
||||
var profileItem = new ProfileItem
|
||||
{
|
||||
CoreType = ECoreType.sing_box,
|
||||
Address = fileName,
|
||||
Remarks = subRemarks ?? "singbox_custom"
|
||||
};
|
||||
|
||||
return profileItem;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
111
v2rayN/ServiceLib/Handler/Fmt/SocksFmt.cs
Normal file
111
v2rayN/ServiceLib/Handler/Fmt/SocksFmt.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
namespace ServiceLib.Handler.Fmt
|
||||
{
|
||||
public class SocksFmt : BaseFmt
|
||||
{
|
||||
public static ProfileItem? Resolve(string str, out string msg)
|
||||
{
|
||||
msg = ResUI.ConfigurationFormatIncorrect;
|
||||
|
||||
var item = ResolveSocksNew(str) ?? ResolveSocks(str);
|
||||
if (item == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (item.Address.Length == 0 || item.Port == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
item.ConfigType = EConfigType.SOCKS;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public static string? ToUri(ProfileItem? item)
|
||||
{
|
||||
if (item == null) return null;
|
||||
var url = string.Empty;
|
||||
|
||||
var remark = string.Empty;
|
||||
if (Utils.IsNotEmpty(item.Remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.Remarks);
|
||||
}
|
||||
//new
|
||||
var pw = Utils.Base64Encode($"{item.Security}:{item.Id}");
|
||||
return ToUri(EConfigType.SOCKS, item.Address, item.Port, pw, null, remark);
|
||||
}
|
||||
|
||||
private static ProfileItem? ResolveSocks(string result)
|
||||
{
|
||||
ProfileItem item = new()
|
||||
{
|
||||
ConfigType = EConfigType.SOCKS
|
||||
};
|
||||
result = result[Global.ProtocolShares[EConfigType.SOCKS].Length..];
|
||||
//remark
|
||||
var indexRemark = result.IndexOf("#");
|
||||
if (indexRemark > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
item.Remarks = Utils.UrlDecode(result.Substring(indexRemark + 1, result.Length - indexRemark - 1));
|
||||
}
|
||||
catch { }
|
||||
result = result[..indexRemark];
|
||||
}
|
||||
//part decode
|
||||
var indexS = result.IndexOf("@");
|
||||
if (indexS > 0)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
result = Utils.Base64Decode(result);
|
||||
}
|
||||
|
||||
var arr1 = result.Split('@');
|
||||
if (arr1.Length != 2)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var arr21 = arr1.First().Split(':');
|
||||
var indexPort = arr1.Last().LastIndexOf(":");
|
||||
if (arr21.Length != 2 || indexPort < 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
item.Address = arr1[1][..indexPort];
|
||||
item.Port = Utils.ToInt(arr1[1][(indexPort + 1)..]);
|
||||
item.Security = arr21.First();
|
||||
item.Id = arr21[1];
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private static ProfileItem? ResolveSocksNew(string result)
|
||||
{
|
||||
var parsedUrl = Utils.TryUri(result);
|
||||
if (parsedUrl == null) return null;
|
||||
|
||||
ProfileItem item = new()
|
||||
{
|
||||
Remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped),
|
||||
Address = parsedUrl.IdnHost,
|
||||
Port = parsedUrl.Port,
|
||||
};
|
||||
|
||||
// parse base64 UserInfo
|
||||
var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo);
|
||||
var userInfo = Utils.Base64Decode(rawUserInfo);
|
||||
var userInfoParts = userInfo.Split([':'], 2);
|
||||
if (userInfoParts.Length == 2)
|
||||
{
|
||||
item.Security = userInfoParts.First();
|
||||
item.Id = userInfoParts[1];
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
44
v2rayN/ServiceLib/Handler/Fmt/TrojanFmt.cs
Normal file
44
v2rayN/ServiceLib/Handler/Fmt/TrojanFmt.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
namespace ServiceLib.Handler.Fmt
|
||||
{
|
||||
public class TrojanFmt : BaseFmt
|
||||
{
|
||||
public static ProfileItem? Resolve(string str, out string msg)
|
||||
{
|
||||
msg = ResUI.ConfigurationFormatIncorrect;
|
||||
|
||||
ProfileItem item = new()
|
||||
{
|
||||
ConfigType = EConfigType.Trojan
|
||||
};
|
||||
|
||||
var url = Utils.TryUri(str);
|
||||
if (url == null) return null;
|
||||
|
||||
item.Address = url.IdnHost;
|
||||
item.Port = url.Port;
|
||||
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
||||
item.Id = Utils.UrlDecode(url.UserInfo);
|
||||
|
||||
var query = Utils.ParseQueryString(url.Query);
|
||||
ResolveStdTransport(query, ref item);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public static string? ToUri(ProfileItem? item)
|
||||
{
|
||||
if (item == null) return null;
|
||||
string url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
if (Utils.IsNotEmpty(item.Remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.Remarks);
|
||||
}
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
GetStdTransport(item, null, ref dicQuery);
|
||||
|
||||
return ToUri(EConfigType.Trojan, item.Address, item.Port, item.Id, dicQuery, remark);
|
||||
}
|
||||
}
|
||||
}
|
||||
59
v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs
Normal file
59
v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
namespace ServiceLib.Handler.Fmt
|
||||
{
|
||||
public class TuicFmt : BaseFmt
|
||||
{
|
||||
public static ProfileItem? Resolve(string str, out string msg)
|
||||
{
|
||||
msg = ResUI.ConfigurationFormatIncorrect;
|
||||
|
||||
ProfileItem item = new()
|
||||
{
|
||||
ConfigType = EConfigType.TUIC
|
||||
};
|
||||
|
||||
var url = Utils.TryUri(str);
|
||||
if (url == null) return null;
|
||||
|
||||
item.Address = url.IdnHost;
|
||||
item.Port = url.Port;
|
||||
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
||||
var rawUserInfo = Utils.UrlDecode(url.UserInfo);
|
||||
var userInfoParts = rawUserInfo.Split(new[] { ':' }, 2);
|
||||
if (userInfoParts.Length == 2)
|
||||
{
|
||||
item.Id = userInfoParts.First();
|
||||
item.Security = userInfoParts.Last();
|
||||
}
|
||||
|
||||
var query = Utils.ParseQueryString(url.Query);
|
||||
ResolveStdTransport(query, ref item);
|
||||
item.HeaderType = query["congestion_control"] ?? "";
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public static string? ToUri(ProfileItem? item)
|
||||
{
|
||||
if (item == null) return null;
|
||||
string url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
if (Utils.IsNotEmpty(item.Remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.Remarks);
|
||||
}
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
if (Utils.IsNotEmpty(item.Sni))
|
||||
{
|
||||
dicQuery.Add("sni", item.Sni);
|
||||
}
|
||||
if (Utils.IsNotEmpty(item.Alpn))
|
||||
{
|
||||
dicQuery.Add("alpn", Utils.UrlEncode(item.Alpn));
|
||||
}
|
||||
dicQuery.Add("congestion_control", item.HeaderType);
|
||||
|
||||
return ToUri(EConfigType.TUIC, item.Address, item.Port, $"{item.Id}:{item.Security}", dicQuery, remark);
|
||||
}
|
||||
}
|
||||
}
|
||||
56
v2rayN/ServiceLib/Handler/Fmt/V2rayFmt.cs
Normal file
56
v2rayN/ServiceLib/Handler/Fmt/V2rayFmt.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
namespace ServiceLib.Handler.Fmt
|
||||
{
|
||||
public class V2rayFmt : BaseFmt
|
||||
{
|
||||
public static List<ProfileItem>? ResolveFullArray(string strData, string? subRemarks)
|
||||
{
|
||||
var configObjects = JsonUtils.Deserialize<Object[]>(strData);
|
||||
if (configObjects != null && configObjects.Length > 0)
|
||||
{
|
||||
List<ProfileItem> lstResult = [];
|
||||
foreach (var configObject in configObjects)
|
||||
{
|
||||
var objectString = JsonUtils.Serialize(configObject);
|
||||
var v2rayCon = JsonUtils.Deserialize<V2rayConfig>(objectString);
|
||||
if (v2rayCon?.inbounds?.Count > 0
|
||||
&& v2rayCon.outbounds?.Count > 0
|
||||
&& v2rayCon.routing != null)
|
||||
{
|
||||
var fileName = WriteAllText(objectString);
|
||||
|
||||
var profileIt = new ProfileItem
|
||||
{
|
||||
CoreType = ECoreType.Xray,
|
||||
Address = fileName,
|
||||
Remarks = v2rayCon.remarks ?? subRemarks ?? "v2ray_custom",
|
||||
};
|
||||
lstResult.Add(profileIt);
|
||||
}
|
||||
}
|
||||
return lstResult;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
|
||||
{
|
||||
var v2rayConfig = JsonUtils.Deserialize<V2rayConfig>(strData);
|
||||
if (v2rayConfig?.inbounds?.Count > 0
|
||||
&& v2rayConfig.outbounds?.Count > 0
|
||||
&& v2rayConfig.routing != null)
|
||||
{
|
||||
var fileName = WriteAllText(strData);
|
||||
|
||||
var profileItem = new ProfileItem
|
||||
{
|
||||
CoreType = ECoreType.Xray,
|
||||
Address = fileName,
|
||||
Remarks = v2rayConfig.remarks ?? subRemarks ?? "v2ray_custom"
|
||||
};
|
||||
|
||||
return profileItem;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
55
v2rayN/ServiceLib/Handler/Fmt/VLESSFmt.cs
Normal file
55
v2rayN/ServiceLib/Handler/Fmt/VLESSFmt.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
namespace ServiceLib.Handler.Fmt
|
||||
{
|
||||
public class VLESSFmt : BaseFmt
|
||||
{
|
||||
public static ProfileItem? Resolve(string str, out string msg)
|
||||
{
|
||||
msg = ResUI.ConfigurationFormatIncorrect;
|
||||
|
||||
ProfileItem item = new()
|
||||
{
|
||||
ConfigType = EConfigType.VLESS,
|
||||
Security = Global.None
|
||||
};
|
||||
|
||||
var url = Utils.TryUri(str);
|
||||
if (url == null) return null;
|
||||
|
||||
item.Address = url.IdnHost;
|
||||
item.Port = url.Port;
|
||||
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
||||
item.Id = Utils.UrlDecode(url.UserInfo);
|
||||
|
||||
var query = Utils.ParseQueryString(url.Query);
|
||||
item.Security = query["encryption"] ?? Global.None;
|
||||
item.StreamSecurity = query["security"] ?? "";
|
||||
ResolveStdTransport(query, ref item);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public static string? ToUri(ProfileItem? item)
|
||||
{
|
||||
if (item == null) return null;
|
||||
string url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
if (Utils.IsNotEmpty(item.Remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.Remarks);
|
||||
}
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
if (Utils.IsNotEmpty(item.Security))
|
||||
{
|
||||
dicQuery.Add("encryption", item.Security);
|
||||
}
|
||||
else
|
||||
{
|
||||
dicQuery.Add("encryption", Global.None);
|
||||
}
|
||||
GetStdTransport(item, Global.None, ref dicQuery);
|
||||
|
||||
return ToUri(EConfigType.VLESS, item.Address, item.Port, item.Id, dicQuery, remark);
|
||||
}
|
||||
}
|
||||
}
|
||||
122
v2rayN/ServiceLib/Handler/Fmt/VmessFmt.cs
Normal file
122
v2rayN/ServiceLib/Handler/Fmt/VmessFmt.cs
Normal file
@@ -0,0 +1,122 @@
|
||||
namespace ServiceLib.Handler.Fmt
|
||||
{
|
||||
public class VmessFmt : BaseFmt
|
||||
{
|
||||
public static ProfileItem? Resolve(string str, out string msg)
|
||||
{
|
||||
msg = ResUI.ConfigurationFormatIncorrect;
|
||||
ProfileItem? item;
|
||||
if (str.IndexOf('?') > 0 && str.IndexOf('&') > 0)
|
||||
{
|
||||
item = ResolveStdVmess(str);
|
||||
}
|
||||
else
|
||||
{
|
||||
item = ResolveVmess(str, out msg);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
public static string? ToUri(ProfileItem? item)
|
||||
{
|
||||
if (item == null) return null;
|
||||
string url = string.Empty;
|
||||
|
||||
VmessQRCode vmessQRCode = new()
|
||||
{
|
||||
v = item.ConfigVersion,
|
||||
ps = item.Remarks.TrimEx(),
|
||||
add = item.Address,
|
||||
port = item.Port,
|
||||
id = item.Id,
|
||||
aid = item.AlterId,
|
||||
scy = item.Security,
|
||||
net = item.Network,
|
||||
type = item.HeaderType,
|
||||
host = item.RequestHost,
|
||||
path = item.Path,
|
||||
tls = item.StreamSecurity,
|
||||
sni = item.Sni,
|
||||
alpn = item.Alpn,
|
||||
fp = item.Fingerprint
|
||||
};
|
||||
|
||||
url = JsonUtils.Serialize(vmessQRCode);
|
||||
url = Utils.Base64Encode(url);
|
||||
url = $"{Global.ProtocolShares[EConfigType.VMess]}{url}";
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
private static ProfileItem? ResolveVmess(string result, out string msg)
|
||||
{
|
||||
msg = string.Empty;
|
||||
var item = new ProfileItem
|
||||
{
|
||||
ConfigType = EConfigType.VMess
|
||||
};
|
||||
|
||||
result = result[Global.ProtocolShares[EConfigType.VMess].Length..];
|
||||
result = Utils.Base64Decode(result);
|
||||
|
||||
VmessQRCode? vmessQRCode = JsonUtils.Deserialize<VmessQRCode>(result);
|
||||
if (vmessQRCode == null)
|
||||
{
|
||||
msg = ResUI.FailedConversionConfiguration;
|
||||
return null;
|
||||
}
|
||||
|
||||
item.Network = Global.DefaultNetwork;
|
||||
item.HeaderType = Global.None;
|
||||
|
||||
item.ConfigVersion = vmessQRCode.v;
|
||||
item.Remarks = Utils.ToString(vmessQRCode.ps);
|
||||
item.Address = Utils.ToString(vmessQRCode.add);
|
||||
item.Port = vmessQRCode.port;
|
||||
item.Id = Utils.ToString(vmessQRCode.id);
|
||||
item.AlterId = vmessQRCode.aid;
|
||||
item.Security = Utils.ToString(vmessQRCode.scy);
|
||||
|
||||
item.Security = Utils.IsNotEmpty(vmessQRCode.scy) ? vmessQRCode.scy : Global.DefaultSecurity;
|
||||
if (Utils.IsNotEmpty(vmessQRCode.net))
|
||||
{
|
||||
item.Network = vmessQRCode.net;
|
||||
}
|
||||
if (Utils.IsNotEmpty(vmessQRCode.type))
|
||||
{
|
||||
item.HeaderType = vmessQRCode.type;
|
||||
}
|
||||
|
||||
item.RequestHost = Utils.ToString(vmessQRCode.host);
|
||||
item.Path = Utils.ToString(vmessQRCode.path);
|
||||
item.StreamSecurity = Utils.ToString(vmessQRCode.tls);
|
||||
item.Sni = Utils.ToString(vmessQRCode.sni);
|
||||
item.Alpn = Utils.ToString(vmessQRCode.alpn);
|
||||
item.Fingerprint = Utils.ToString(vmessQRCode.fp);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public static ProfileItem? ResolveStdVmess(string str)
|
||||
{
|
||||
ProfileItem item = new()
|
||||
{
|
||||
ConfigType = EConfigType.VMess,
|
||||
Security = "auto"
|
||||
};
|
||||
|
||||
var url = Utils.TryUri(str);
|
||||
if (url == null) return null;
|
||||
|
||||
item.Address = url.IdnHost;
|
||||
item.Port = url.Port;
|
||||
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
||||
item.Id = Utils.UrlDecode(url.UserInfo);
|
||||
|
||||
var query = Utils.ParseQueryString(url.Query);
|
||||
ResolveStdTransport(query, ref item);
|
||||
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
63
v2rayN/ServiceLib/Handler/Fmt/WireguardFmt.cs
Normal file
63
v2rayN/ServiceLib/Handler/Fmt/WireguardFmt.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
namespace ServiceLib.Handler.Fmt
|
||||
{
|
||||
public class WireguardFmt : BaseFmt
|
||||
{
|
||||
public static ProfileItem? Resolve(string str, out string msg)
|
||||
{
|
||||
msg = ResUI.ConfigurationFormatIncorrect;
|
||||
|
||||
ProfileItem item = new()
|
||||
{
|
||||
ConfigType = EConfigType.WireGuard
|
||||
};
|
||||
|
||||
var url = Utils.TryUri(str);
|
||||
if (url == null) return null;
|
||||
|
||||
item.Address = url.IdnHost;
|
||||
item.Port = url.Port;
|
||||
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
||||
item.Id = Utils.UrlDecode(url.UserInfo);
|
||||
|
||||
var query = Utils.ParseQueryString(url.Query);
|
||||
|
||||
item.PublicKey = Utils.UrlDecode(query["publickey"] ?? "");
|
||||
item.Path = Utils.UrlDecode(query["reserved"] ?? "");
|
||||
item.RequestHost = Utils.UrlDecode(query["address"] ?? "");
|
||||
item.ShortId = Utils.UrlDecode(query["mtu"] ?? "");
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public static string? ToUri(ProfileItem? item)
|
||||
{
|
||||
if (item == null) return null;
|
||||
string url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
if (Utils.IsNotEmpty(item.Remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.Remarks);
|
||||
}
|
||||
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
if (Utils.IsNotEmpty(item.PublicKey))
|
||||
{
|
||||
dicQuery.Add("publickey", Utils.UrlEncode(item.PublicKey));
|
||||
}
|
||||
if (Utils.IsNotEmpty(item.Path))
|
||||
{
|
||||
dicQuery.Add("reserved", Utils.UrlEncode(item.Path));
|
||||
}
|
||||
if (Utils.IsNotEmpty(item.RequestHost))
|
||||
{
|
||||
dicQuery.Add("address", Utils.UrlEncode(item.RequestHost));
|
||||
}
|
||||
if (Utils.IsNotEmpty(item.ShortId))
|
||||
{
|
||||
dicQuery.Add("mtu", Utils.UrlEncode(item.ShortId));
|
||||
}
|
||||
return ToUri(EConfigType.WireGuard, item.Address, item.Port, item.Id, dicQuery, remark);
|
||||
}
|
||||
}
|
||||
}
|
||||
44
v2rayN/ServiceLib/Handler/NoticeHandler.cs
Normal file
44
v2rayN/ServiceLib/Handler/NoticeHandler.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using ReactiveUI;
|
||||
|
||||
namespace ServiceLib.Handler
|
||||
{
|
||||
public class NoticeHandler
|
||||
{
|
||||
private static readonly Lazy<NoticeHandler> _instance = new(() => new());
|
||||
public static NoticeHandler Instance => _instance.Value;
|
||||
|
||||
public void Enqueue(string? content)
|
||||
{
|
||||
if (content.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
MessageBus.Current.SendMessage(content, EMsgCommand.SendSnackMsg.ToString());
|
||||
}
|
||||
|
||||
public void SendMessage(string? content)
|
||||
{
|
||||
if (content.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
MessageBus.Current.SendMessage(content, EMsgCommand.SendMsgView.ToString());
|
||||
}
|
||||
|
||||
public void SendMessageEx(string? content)
|
||||
{
|
||||
if (content.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
content = $"{DateTime.Now:yyyy/MM/dd HH:mm:ss} {content}";
|
||||
SendMessage(content);
|
||||
}
|
||||
|
||||
public void SendMessageAndEnqueue(string? msg)
|
||||
{
|
||||
Enqueue(msg);
|
||||
SendMessage(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
105
v2rayN/ServiceLib/Handler/PacHandler.cs
Normal file
105
v2rayN/ServiceLib/Handler/PacHandler.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
|
||||
namespace ServiceLib.Handler
|
||||
{
|
||||
public class PacHandler
|
||||
{
|
||||
private static string _configPath;
|
||||
private static int _httpPort;
|
||||
private static int _pacPort;
|
||||
private static TcpListener? _tcpListener;
|
||||
private static byte[] _writeContent;
|
||||
private static bool _isRunning;
|
||||
private static bool _needRestart = true;
|
||||
|
||||
public static async Task Start(string configPath, int httpPort, int pacPort)
|
||||
{
|
||||
_needRestart = (configPath != _configPath || httpPort != _httpPort || pacPort != _pacPort || !_isRunning);
|
||||
|
||||
_configPath = configPath;
|
||||
_httpPort = httpPort;
|
||||
_pacPort = pacPort;
|
||||
|
||||
await InitText();
|
||||
|
||||
if (_needRestart)
|
||||
{
|
||||
Stop();
|
||||
RunListener();
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task InitText()
|
||||
{
|
||||
var path = Path.Combine(_configPath, "pac.txt");
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
var pac = Utils.GetEmbedText(Global.PacFileName);
|
||||
await File.AppendAllTextAsync(path, pac);
|
||||
}
|
||||
|
||||
var pacText =
|
||||
(await File.ReadAllTextAsync(path)).Replace("__PROXY__", $"PROXY 127.0.0.1:{_httpPort};DIRECT;");
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("HTTP/1.0 200 OK");
|
||||
sb.AppendLine("Content-type:application/x-ns-proxy-autoconfig");
|
||||
sb.AppendLine("Connection:close");
|
||||
sb.AppendLine("Content-Length:" + Encoding.UTF8.GetByteCount(pacText));
|
||||
sb.AppendLine();
|
||||
sb.Append(pacText);
|
||||
_writeContent = Encoding.UTF8.GetBytes(sb.ToString());
|
||||
}
|
||||
|
||||
private static void RunListener()
|
||||
{
|
||||
_tcpListener = TcpListener.Create(_pacPort);
|
||||
_isRunning = true;
|
||||
_tcpListener.Start();
|
||||
Task.Factory.StartNew(async () =>
|
||||
{
|
||||
while (_isRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!_tcpListener.Pending())
|
||||
{
|
||||
await Task.Delay(10);
|
||||
continue;
|
||||
}
|
||||
|
||||
var client = await _tcpListener.AcceptTcpClientAsync();
|
||||
await Task.Run(() => { WriteContent(client); });
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}, TaskCreationOptions.LongRunning);
|
||||
}
|
||||
|
||||
private static void WriteContent(TcpClient client)
|
||||
{
|
||||
var stream = client.GetStream();
|
||||
stream.Write(_writeContent, 0, _writeContent.Length);
|
||||
stream.Flush();
|
||||
}
|
||||
|
||||
public static void Stop()
|
||||
{
|
||||
if (_tcpListener == null) return;
|
||||
try
|
||||
{
|
||||
_isRunning = false;
|
||||
_tcpListener.Stop();
|
||||
_tcpListener = null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
183
v2rayN/ServiceLib/Handler/ProfileExHandler.cs
Normal file
183
v2rayN/ServiceLib/Handler/ProfileExHandler.cs
Normal file
@@ -0,0 +1,183 @@
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
//using System.Reactive.Linq;
|
||||
|
||||
namespace ServiceLib.Handler
|
||||
{
|
||||
public class ProfileExHandler
|
||||
{
|
||||
private static readonly Lazy<ProfileExHandler> _instance = new(() => new());
|
||||
private ConcurrentBag<ProfileExItem> _lstProfileEx = [];
|
||||
private Queue<string> _queIndexIds = new();
|
||||
public static ProfileExHandler Instance => _instance.Value;
|
||||
private static readonly string _tag = "ProfileExHandler";
|
||||
|
||||
public ProfileExHandler()
|
||||
{
|
||||
//Init();
|
||||
}
|
||||
|
||||
public async Task Init()
|
||||
{
|
||||
await InitData();
|
||||
Task.Run(async () =>
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
await Task.Delay(1000 * 600);
|
||||
await SaveQueueIndexIds();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<ConcurrentBag<ProfileExItem>> GetProfileExs()
|
||||
{
|
||||
return _lstProfileEx;
|
||||
}
|
||||
|
||||
private async Task InitData()
|
||||
{
|
||||
await SQLiteHelper.Instance.ExecuteAsync($"delete from ProfileExItem where indexId not in ( select indexId from ProfileItem )");
|
||||
|
||||
_lstProfileEx = new(await SQLiteHelper.Instance.TableAsync<ProfileExItem>().ToListAsync());
|
||||
}
|
||||
|
||||
private void IndexIdEnqueue(string indexId)
|
||||
{
|
||||
if (Utils.IsNotEmpty(indexId) && !_queIndexIds.Contains(indexId))
|
||||
{
|
||||
_queIndexIds.Enqueue(indexId);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SaveQueueIndexIds()
|
||||
{
|
||||
var cnt = _queIndexIds.Count;
|
||||
if (cnt > 0)
|
||||
{
|
||||
var lstExists = await SQLiteHelper.Instance.TableAsync<ProfileExItem>().ToListAsync();
|
||||
List<ProfileExItem> lstInserts = [];
|
||||
List<ProfileExItem> lstUpdates = [];
|
||||
|
||||
for (int i = 0; i < cnt; i++)
|
||||
{
|
||||
var id = _queIndexIds.Dequeue();
|
||||
var item = lstExists.FirstOrDefault(t => t.IndexId == id);
|
||||
var itemNew = _lstProfileEx?.FirstOrDefault(t => t.IndexId == id);
|
||||
if (itemNew is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item is not null)
|
||||
{
|
||||
lstUpdates.Add(itemNew);
|
||||
}
|
||||
else
|
||||
{
|
||||
lstInserts.Add(itemNew);
|
||||
}
|
||||
}
|
||||
try
|
||||
{
|
||||
if (lstInserts.Count() > 0)
|
||||
await SQLiteHelper.Instance.InsertAllAsync(lstInserts);
|
||||
|
||||
if (lstUpdates.Count() > 0)
|
||||
await SQLiteHelper.Instance.UpdateAllAsync(lstUpdates);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddProfileEx(string indexId, ref ProfileExItem? profileEx)
|
||||
{
|
||||
profileEx = new()
|
||||
{
|
||||
IndexId = indexId,
|
||||
Delay = 0,
|
||||
Speed = 0,
|
||||
Sort = 0
|
||||
};
|
||||
_lstProfileEx.Add(profileEx);
|
||||
IndexIdEnqueue(indexId);
|
||||
}
|
||||
|
||||
public async Task ClearAll()
|
||||
{
|
||||
await SQLiteHelper.Instance.ExecuteAsync($"delete from ProfileExItem ");
|
||||
_lstProfileEx = new();
|
||||
}
|
||||
|
||||
public async Task SaveTo()
|
||||
{
|
||||
try
|
||||
{
|
||||
await SaveQueueIndexIds();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetTestDelay(string indexId, string delayVal)
|
||||
{
|
||||
var profileEx = _lstProfileEx.FirstOrDefault(t => t.IndexId == indexId);
|
||||
if (profileEx == null)
|
||||
{
|
||||
AddProfileEx(indexId, ref profileEx);
|
||||
}
|
||||
|
||||
int.TryParse(delayVal, out int delay);
|
||||
profileEx.Delay = delay;
|
||||
IndexIdEnqueue(indexId);
|
||||
}
|
||||
|
||||
public void SetTestSpeed(string indexId, string speedVal)
|
||||
{
|
||||
var profileEx = _lstProfileEx.FirstOrDefault(t => t.IndexId == indexId);
|
||||
if (profileEx == null)
|
||||
{
|
||||
AddProfileEx(indexId, ref profileEx);
|
||||
}
|
||||
|
||||
decimal.TryParse(speedVal, out decimal speed);
|
||||
profileEx.Speed = speed;
|
||||
IndexIdEnqueue(indexId);
|
||||
}
|
||||
|
||||
public void SetSort(string indexId, int sort)
|
||||
{
|
||||
var profileEx = _lstProfileEx.FirstOrDefault(t => t.IndexId == indexId);
|
||||
if (profileEx == null)
|
||||
{
|
||||
AddProfileEx(indexId, ref profileEx);
|
||||
}
|
||||
profileEx.Sort = sort;
|
||||
IndexIdEnqueue(indexId);
|
||||
}
|
||||
|
||||
public int GetSort(string indexId)
|
||||
{
|
||||
var profileEx = _lstProfileEx.FirstOrDefault(t => t.IndexId == indexId);
|
||||
if (profileEx == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return profileEx.Sort;
|
||||
}
|
||||
|
||||
public int GetMaxSort()
|
||||
{
|
||||
if (_lstProfileEx.Count <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return _lstProfileEx.Max(t => t == null ? 0 : t.Sort);
|
||||
}
|
||||
}
|
||||
}
|
||||
161
v2rayN/ServiceLib/Handler/StatisticsHandler.cs
Normal file
161
v2rayN/ServiceLib/Handler/StatisticsHandler.cs
Normal file
@@ -0,0 +1,161 @@
|
||||
namespace ServiceLib.Handler
|
||||
{
|
||||
public class StatisticsHandler
|
||||
{
|
||||
private static readonly Lazy<StatisticsHandler> instance = new(() => new());
|
||||
public static StatisticsHandler Instance => instance.Value;
|
||||
|
||||
private Config _config;
|
||||
private ServerStatItem? _serverStatItem;
|
||||
private List<ServerStatItem> _lstServerStat;
|
||||
private Action<ServerSpeedItem>? _updateFunc;
|
||||
|
||||
private StatisticsXrayService? _statisticsXray;
|
||||
private StatisticsSingboxService? _statisticsSingbox;
|
||||
private static readonly string _tag = "StatisticsHandler";
|
||||
public List<ServerStatItem> ServerStat => _lstServerStat;
|
||||
|
||||
public async Task Init(Config config, Action<ServerSpeedItem> updateFunc)
|
||||
{
|
||||
_config = config;
|
||||
_updateFunc = updateFunc;
|
||||
if (!config.GuiItem.EnableStatistics)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await InitData();
|
||||
|
||||
_statisticsXray = new StatisticsXrayService(config, UpdateServerStatHandler);
|
||||
_statisticsSingbox = new StatisticsSingboxService(config, UpdateServerStatHandler);
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
try
|
||||
{
|
||||
_statisticsXray?.Close();
|
||||
_statisticsSingbox?.Close();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ClearAllServerStatistics()
|
||||
{
|
||||
await SQLiteHelper.Instance.ExecuteAsync($"delete from ServerStatItem ");
|
||||
_serverStatItem = null;
|
||||
_lstServerStat = new();
|
||||
}
|
||||
|
||||
public async Task SaveTo()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_lstServerStat != null)
|
||||
{
|
||||
await SQLiteHelper.Instance.UpdateAllAsync(_lstServerStat);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task CloneServerStatItem(string indexId, string toIndexId)
|
||||
{
|
||||
if (_lstServerStat == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var stat = _lstServerStat.FirstOrDefault(t => t.IndexId == indexId);
|
||||
if (stat == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var toStat = JsonUtils.DeepCopy(stat);
|
||||
toStat.IndexId = toIndexId;
|
||||
await SQLiteHelper.Instance.ReplaceAsync(toStat);
|
||||
_lstServerStat.Add(toStat);
|
||||
}
|
||||
|
||||
private async Task InitData()
|
||||
{
|
||||
await SQLiteHelper.Instance.ExecuteAsync($"delete from ServerStatItem where indexId not in ( select indexId from ProfileItem )");
|
||||
|
||||
long ticks = DateTime.Now.Date.Ticks;
|
||||
await SQLiteHelper.Instance.ExecuteAsync($"update ServerStatItem set todayUp = 0,todayDown=0,dateNow={ticks} where dateNow<>{ticks}");
|
||||
|
||||
_lstServerStat = await SQLiteHelper.Instance.TableAsync<ServerStatItem>().ToListAsync();
|
||||
}
|
||||
|
||||
private void UpdateServerStatHandler(ServerSpeedItem server)
|
||||
{
|
||||
UpdateServerStat(server);
|
||||
}
|
||||
|
||||
private async Task UpdateServerStat(ServerSpeedItem server)
|
||||
{
|
||||
await GetServerStatItem(_config.IndexId);
|
||||
|
||||
if (_serverStatItem is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (server.ProxyUp != 0 || server.ProxyDown != 0)
|
||||
{
|
||||
_serverStatItem.TodayUp += server.ProxyUp;
|
||||
_serverStatItem.TodayDown += server.ProxyDown;
|
||||
_serverStatItem.TotalUp += server.ProxyUp;
|
||||
_serverStatItem.TotalDown += server.ProxyDown;
|
||||
}
|
||||
|
||||
server.IndexId = _config.IndexId;
|
||||
server.TodayUp = _serverStatItem.TodayUp;
|
||||
server.TodayDown = _serverStatItem.TodayDown;
|
||||
server.TotalUp = _serverStatItem.TotalUp;
|
||||
server.TotalDown = _serverStatItem.TotalDown;
|
||||
_updateFunc?.Invoke(server);
|
||||
}
|
||||
|
||||
private async Task GetServerStatItem(string indexId)
|
||||
{
|
||||
long ticks = DateTime.Now.Date.Ticks;
|
||||
if (_serverStatItem != null && _serverStatItem.IndexId != indexId)
|
||||
{
|
||||
_serverStatItem = null;
|
||||
}
|
||||
|
||||
if (_serverStatItem == null)
|
||||
{
|
||||
_serverStatItem = _lstServerStat.FirstOrDefault(t => t.IndexId == indexId);
|
||||
if (_serverStatItem == null)
|
||||
{
|
||||
_serverStatItem = new ServerStatItem
|
||||
{
|
||||
IndexId = indexId,
|
||||
TotalUp = 0,
|
||||
TotalDown = 0,
|
||||
TodayUp = 0,
|
||||
TodayDown = 0,
|
||||
DateNow = ticks
|
||||
};
|
||||
await SQLiteHelper.Instance.ReplaceAsync(_serverStatItem);
|
||||
_lstServerStat.Add(_serverStatItem);
|
||||
}
|
||||
}
|
||||
|
||||
if (_serverStatItem.DateNow != ticks)
|
||||
{
|
||||
_serverStatItem.TodayUp = 0;
|
||||
_serverStatItem.TodayDown = 0;
|
||||
_serverStatItem.DateNow = ticks;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
202
v2rayN/ServiceLib/Handler/SysProxy/ProxySettingLinux.cs
Normal file
202
v2rayN/ServiceLib/Handler/SysProxy/ProxySettingLinux.cs
Normal file
@@ -0,0 +1,202 @@
|
||||
namespace ServiceLib.Handler.SysProxy
|
||||
{
|
||||
public class ProxySettingLinux
|
||||
{
|
||||
public static async Task SetProxy(string host, int port, string exceptions)
|
||||
{
|
||||
var lstCmd = GetSetCmds(host, port, exceptions);
|
||||
|
||||
await ExecCmd(lstCmd);
|
||||
}
|
||||
|
||||
public static async Task UnsetProxy()
|
||||
{
|
||||
var lstCmd = GetUnsetCmds();
|
||||
|
||||
await ExecCmd(lstCmd);
|
||||
}
|
||||
|
||||
private static async Task ExecCmd(List<CmdItem> lstCmd)
|
||||
{
|
||||
foreach (var cmd in lstCmd)
|
||||
{
|
||||
if (cmd is null || cmd.Cmd.IsNullOrEmpty() || cmd.Arguments is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
await Task.Delay(10);
|
||||
await Utils.GetCliWrapOutput(cmd.Cmd, cmd.Arguments);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<CmdItem> GetSetCmds(string host, int port, string exceptions)
|
||||
{
|
||||
var isKde = IsKde(out var configDir);
|
||||
List<string> lstType = ["", "http", "https", "socks", "ftp"];
|
||||
List<CmdItem> lstCmd = [];
|
||||
|
||||
//GNOME
|
||||
foreach (var type in lstType)
|
||||
{
|
||||
lstCmd.AddRange(GetSetCmd4Gnome(type, host, port));
|
||||
}
|
||||
if (exceptions.IsNotEmpty())
|
||||
{
|
||||
lstCmd.AddRange(GetSetCmd4Gnome("exceptions", exceptions, 0));
|
||||
}
|
||||
|
||||
if (isKde)
|
||||
{
|
||||
foreach (var type in lstType)
|
||||
{
|
||||
lstCmd.AddRange(GetSetCmd4Kde(type, host, port, configDir));
|
||||
}
|
||||
if (exceptions.IsNotEmpty())
|
||||
{
|
||||
lstCmd.AddRange(GetSetCmd4Kde("exceptions", exceptions, 0, configDir));
|
||||
}
|
||||
|
||||
// Notify system to reload
|
||||
lstCmd.Add(new CmdItem()
|
||||
{
|
||||
Cmd = "dbus-send",
|
||||
Arguments = ["--type=signal", "/KIO/Scheduler", "org.kde.KIO.Scheduler.reparseSlaveConfiguration", "string:''"]
|
||||
});
|
||||
}
|
||||
return lstCmd;
|
||||
}
|
||||
|
||||
private static List<CmdItem> GetUnsetCmds()
|
||||
{
|
||||
var isKde = IsKde(out var configDir);
|
||||
List<CmdItem> lstCmd = [];
|
||||
|
||||
//GNOME
|
||||
lstCmd.Add(new CmdItem()
|
||||
{
|
||||
Cmd = "gsettings",
|
||||
Arguments = ["set", "org.gnome.system.proxy", "mode", "none"]
|
||||
});
|
||||
|
||||
if (isKde)
|
||||
{
|
||||
lstCmd.Add(new CmdItem()
|
||||
{
|
||||
Cmd = GetKdeVersion(),
|
||||
Arguments = ["--file", $"{configDir}/kioslaverc", "--group", "Proxy Settings", "--key", "ProxyType", "0"]
|
||||
});
|
||||
|
||||
// Notify system to reload
|
||||
lstCmd.Add(new CmdItem()
|
||||
{
|
||||
Cmd = "dbus-send",
|
||||
Arguments = ["--type=signal", "/KIO/Scheduler", "org.kde.KIO.Scheduler.reparseSlaveConfiguration", "string:''"]
|
||||
});
|
||||
}
|
||||
return lstCmd;
|
||||
}
|
||||
|
||||
private static List<CmdItem> GetSetCmd4Kde(string type, string host, int port, string configDir)
|
||||
{
|
||||
List<CmdItem> lstCmd = [];
|
||||
var cmd = GetKdeVersion();
|
||||
|
||||
if (type.IsNullOrEmpty())
|
||||
{
|
||||
lstCmd.Add(new()
|
||||
{
|
||||
Cmd = cmd,
|
||||
Arguments = ["--file", $"{configDir}/kioslaverc", "--group", "Proxy Settings", "--key", "ProxyType", "1"]
|
||||
});
|
||||
}
|
||||
else if (type == "exceptions")
|
||||
{
|
||||
lstCmd.Add(new()
|
||||
{
|
||||
Cmd = cmd,
|
||||
Arguments = ["--file", $"{configDir}/kioslaverc", "--group", "Proxy Settings", "--key", "NoProxyFor", host]
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
var type2 = type.Equals("https") ? "http" : type;
|
||||
lstCmd.Add(new CmdItem()
|
||||
{
|
||||
Cmd = cmd,
|
||||
Arguments = ["--file", $"{configDir}/kioslaverc", "--group", "Proxy Settings", "--key", $"{type}Proxy", $"{type2}://{host}:{port}"]
|
||||
});
|
||||
}
|
||||
|
||||
return lstCmd;
|
||||
}
|
||||
|
||||
private static List<CmdItem> GetSetCmd4Gnome(string type, string host, int port)
|
||||
{
|
||||
List<CmdItem> lstCmd = [];
|
||||
|
||||
if (type.IsNullOrEmpty())
|
||||
{
|
||||
lstCmd.Add(new()
|
||||
{
|
||||
Cmd = "gsettings",
|
||||
Arguments = ["set", "org.gnome.system.proxy", "mode", "manual"]
|
||||
});
|
||||
}
|
||||
else if (type == "exceptions")
|
||||
{
|
||||
lstCmd.Add(new()
|
||||
{
|
||||
Cmd = "gsettings",
|
||||
Arguments = ["set", $"org.gnome.system.proxy", "ignore-hosts", JsonUtils.Serialize(host.Split(','), false)]
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
lstCmd.Add(new()
|
||||
{
|
||||
Cmd = "gsettings",
|
||||
Arguments = ["set", $"org.gnome.system.proxy.{type}", "host", host]
|
||||
});
|
||||
|
||||
lstCmd.Add(new()
|
||||
{
|
||||
Cmd = "gsettings",
|
||||
Arguments = ["set", $"org.gnome.system.proxy.{type}", "port", $"{port}"]
|
||||
});
|
||||
}
|
||||
|
||||
return lstCmd;
|
||||
}
|
||||
|
||||
private static bool IsKde(out string configDir)
|
||||
{
|
||||
configDir = "/home";
|
||||
var desktop = Environment.GetEnvironmentVariable("XDG_CURRENT_DESKTOP");
|
||||
var desktop2 = Environment.GetEnvironmentVariable("XDG_SESSION_DESKTOP");
|
||||
var isKde = string.Equals(desktop, "KDE", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(desktop, "plasma", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(desktop2, "KDE", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(desktop2, "plasma", StringComparison.OrdinalIgnoreCase);
|
||||
if (isKde)
|
||||
{
|
||||
var homeDir = Environment.GetEnvironmentVariable("HOME");
|
||||
if (homeDir != null)
|
||||
{
|
||||
configDir = Path.Combine(homeDir, ".config");
|
||||
}
|
||||
}
|
||||
|
||||
return isKde;
|
||||
}
|
||||
|
||||
private static string GetKdeVersion()
|
||||
{
|
||||
var ver = Environment.GetEnvironmentVariable("KDE_SESSION_VERSION") ?? "0";
|
||||
return ver switch
|
||||
{
|
||||
"6" => "kwriteconfig6",
|
||||
_ => "kwriteconfig5"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
101
v2rayN/ServiceLib/Handler/SysProxy/ProxySettingOSX.cs
Normal file
101
v2rayN/ServiceLib/Handler/SysProxy/ProxySettingOSX.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
namespace ServiceLib.Handler.SysProxy
|
||||
{
|
||||
public class ProxySettingOSX
|
||||
{
|
||||
/// <summary>
|
||||
/// 应用接口类型
|
||||
/// </summary>
|
||||
private static readonly List<string> LstInterface = ["Ethernet", "Wi-Fi", "Thunderbolt Bridge", "USB 10/100/1000 LAN"];
|
||||
|
||||
/// <summary>
|
||||
/// 代理类型,对应 http,https,socks
|
||||
/// </summary>
|
||||
private static readonly List<string> LstTypes = ["setwebproxy", "setsecurewebproxy", "setsocksfirewallproxy"];
|
||||
|
||||
public static async Task SetProxy(string host, int port, string exceptions)
|
||||
{
|
||||
var lstInterface = await GetListNetworkServices();
|
||||
var lstCmd = GetSetCmds(lstInterface, host, port, exceptions);
|
||||
await ExecCmd(lstCmd);
|
||||
}
|
||||
|
||||
public static async Task UnsetProxy()
|
||||
{
|
||||
var lstInterface = await GetListNetworkServices();
|
||||
var lstCmd = GetUnsetCmds(lstInterface);
|
||||
await ExecCmd(lstCmd);
|
||||
}
|
||||
|
||||
private static async Task ExecCmd(List<CmdItem> lstCmd)
|
||||
{
|
||||
foreach (var cmd in lstCmd)
|
||||
{
|
||||
if (cmd is null || cmd.Cmd.IsNullOrEmpty() || cmd.Arguments is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
await Task.Delay(10);
|
||||
await Utils.GetCliWrapOutput(cmd.Cmd, cmd.Arguments);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<CmdItem> GetSetCmds(List<string> lstInterface, string host, int port, string exceptions)
|
||||
{
|
||||
List<CmdItem> lstCmd = [];
|
||||
foreach (var interf in lstInterface)
|
||||
{
|
||||
foreach (var type in LstTypes)
|
||||
{
|
||||
lstCmd.Add(new CmdItem()
|
||||
{
|
||||
Cmd = "networksetup",
|
||||
Arguments = [$"-{type}", interf, host, port.ToString()]
|
||||
});
|
||||
}
|
||||
if (exceptions.IsNotEmpty())
|
||||
{
|
||||
List<string> args = [$"-setproxybypassdomains", interf];
|
||||
args.AddRange(exceptions.Split(','));
|
||||
lstCmd.Add(new CmdItem()
|
||||
{
|
||||
Cmd = "networksetup",
|
||||
Arguments = args
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return lstCmd;
|
||||
}
|
||||
|
||||
private static List<CmdItem> GetUnsetCmds(List<string> lstInterface)
|
||||
{
|
||||
List<CmdItem> lstCmd = [];
|
||||
foreach (var interf in lstInterface)
|
||||
{
|
||||
foreach (var type in LstTypes)
|
||||
{
|
||||
lstCmd.Add(new CmdItem()
|
||||
{
|
||||
Cmd = "networksetup",
|
||||
Arguments = [$"-{type}state", interf, "off"]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return lstCmd;
|
||||
}
|
||||
|
||||
public static async Task<List<string>> GetListNetworkServices()
|
||||
{
|
||||
var services = await Utils.GetListNetworkServices();
|
||||
if (services.IsNullOrEmpty())
|
||||
{
|
||||
return LstInterface;
|
||||
}
|
||||
|
||||
var lst = services.Split(Environment.NewLine).Where(t => t.Length > 0 && t.Contains('*') == false);
|
||||
return lst.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
359
v2rayN/ServiceLib/Handler/SysProxy/ProxySettingWindows.cs
Normal file
359
v2rayN/ServiceLib/Handler/SysProxy/ProxySettingWindows.cs
Normal file
@@ -0,0 +1,359 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using static ServiceLib.Handler.SysProxy.ProxySettingWindows.InternetConnectionOption;
|
||||
|
||||
namespace ServiceLib.Handler.SysProxy
|
||||
{
|
||||
public class ProxySettingWindows
|
||||
{
|
||||
private const string _regPath = @"Software\Microsoft\Windows\CurrentVersion\Internet Settings";
|
||||
|
||||
private static bool SetProxyFallback(string? strProxy, string? exceptions, int type)
|
||||
{
|
||||
if (type == 1)
|
||||
{
|
||||
WindowsUtils.RegWriteValue(_regPath, "ProxyEnable", 0);
|
||||
WindowsUtils.RegWriteValue(_regPath, "ProxyServer", string.Empty);
|
||||
WindowsUtils.RegWriteValue(_regPath, "ProxyOverride", string.Empty);
|
||||
WindowsUtils.RegWriteValue(_regPath, "AutoConfigURL", string.Empty);
|
||||
}
|
||||
if (type == 2)
|
||||
{
|
||||
WindowsUtils.RegWriteValue(_regPath, "ProxyEnable", 1);
|
||||
WindowsUtils.RegWriteValue(_regPath, "ProxyServer", strProxy ?? string.Empty);
|
||||
WindowsUtils.RegWriteValue(_regPath, "ProxyOverride", exceptions ?? string.Empty);
|
||||
WindowsUtils.RegWriteValue(_regPath, "AutoConfigURL", string.Empty);
|
||||
}
|
||||
else if (type == 4)
|
||||
{
|
||||
WindowsUtils.RegWriteValue(_regPath, "ProxyEnable", 0);
|
||||
WindowsUtils.RegWriteValue(_regPath, "ProxyServer", string.Empty);
|
||||
WindowsUtils.RegWriteValue(_regPath, "ProxyOverride", string.Empty);
|
||||
WindowsUtils.RegWriteValue(_regPath, "AutoConfigURL", strProxy ?? string.Empty);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
// set to use no proxy
|
||||
/// </summary>
|
||||
/// <exception cref="ApplicationException">Error message with win32 error code</exception>
|
||||
public static bool UnsetProxy()
|
||||
{
|
||||
return SetProxy(null, null, 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set system proxy settings
|
||||
/// </summary>
|
||||
/// <param name="strProxy"> proxy address</param>
|
||||
/// <param name="exceptions">exception addresses that do not use proxy</param>
|
||||
/// <param name="type">type of proxy defined in PerConnFlags
|
||||
/// PROXY_TYPE_DIRECT = 0x00000001, // direct connection (no proxy)
|
||||
/// PROXY_TYPE_PROXY = 0x00000002, // via named proxy
|
||||
/// PROXY_TYPE_AUTO_PROXY_URL = 0x00000004, // autoproxy script URL
|
||||
/// PROXY_TYPE_AUTO_DETECT = 0x00000008 // use autoproxy detection
|
||||
/// </param>
|
||||
/// <exception cref="ApplicationException">Error message with win32 error code</exception>
|
||||
/// <returns>true: one of connection is successfully updated proxy settings</returns>
|
||||
public static bool SetProxy(string? strProxy, string? exceptions, int type)
|
||||
{
|
||||
try
|
||||
{
|
||||
// set proxy for LAN
|
||||
bool result = SetConnectionProxy(null, strProxy, exceptions, type);
|
||||
// set proxy for dial up connections
|
||||
var connections = EnumerateRasEntries();
|
||||
foreach (var connection in connections)
|
||||
{
|
||||
result |= SetConnectionProxy(connection, strProxy, exceptions, type);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SetProxyFallback(strProxy, exceptions, type);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool SetConnectionProxy(string? connectionName, string? strProxy, string? exceptions, int type)
|
||||
{
|
||||
InternetPerConnOptionList list = new();
|
||||
|
||||
int optionCount = 1;
|
||||
if (type == 1) // No proxy
|
||||
{
|
||||
optionCount = 1;
|
||||
}
|
||||
else if (type is 2 or 4) // named proxy or autoproxy script URL
|
||||
{
|
||||
optionCount = string.IsNullOrEmpty(exceptions) ? 2 : 3;
|
||||
}
|
||||
|
||||
int m_Int = (int)PerConnFlags.PROXY_TYPE_DIRECT;
|
||||
PerConnOption m_Option = PerConnOption.INTERNET_PER_CONN_FLAGS;
|
||||
if (type == 2) // named proxy
|
||||
{
|
||||
m_Int = (int)(PerConnFlags.PROXY_TYPE_DIRECT | PerConnFlags.PROXY_TYPE_PROXY);
|
||||
m_Option = PerConnOption.INTERNET_PER_CONN_PROXY_SERVER;
|
||||
}
|
||||
else if (type == 4) // autoproxy script url
|
||||
{
|
||||
m_Int = (int)(PerConnFlags.PROXY_TYPE_DIRECT | PerConnFlags.PROXY_TYPE_AUTO_PROXY_URL);
|
||||
m_Option = PerConnOption.INTERNET_PER_CONN_AUTOCONFIG_URL;
|
||||
}
|
||||
|
||||
//int optionCount = Utile.IsNullOrEmpty(strProxy) ? 1 : (Utile.IsNullOrEmpty(exceptions) ? 2 : 3);
|
||||
InternetConnectionOption[] options = new InternetConnectionOption[optionCount];
|
||||
// USE a proxy server ...
|
||||
options[0].m_Option = PerConnOption.INTERNET_PER_CONN_FLAGS;
|
||||
//options[0].m_Value.m_Int = (int)((optionCount < 2) ? PerConnFlags.PROXY_TYPE_DIRECT : (PerConnFlags.PROXY_TYPE_DIRECT | PerConnFlags.PROXY_TYPE_PROXY));
|
||||
options[0].m_Value.m_Int = m_Int;
|
||||
// use THIS proxy server
|
||||
if (optionCount > 1)
|
||||
{
|
||||
options[1].m_Option = m_Option;
|
||||
options[1].m_Value.m_StringPtr = Marshal.StringToHGlobalAuto(strProxy); // !! remember to deallocate memory 1
|
||||
// except for these addresses ...
|
||||
if (optionCount > 2)
|
||||
{
|
||||
options[2].m_Option = PerConnOption.INTERNET_PER_CONN_PROXY_BYPASS;
|
||||
options[2].m_Value.m_StringPtr = Marshal.StringToHGlobalAuto(exceptions); // !! remember to deallocate memory 2
|
||||
}
|
||||
}
|
||||
|
||||
// default stuff
|
||||
list.dwSize = Marshal.SizeOf(list);
|
||||
if (connectionName != null)
|
||||
{
|
||||
list.szConnection = Marshal.StringToHGlobalAuto(connectionName); // !! remember to deallocate memory 3
|
||||
}
|
||||
else
|
||||
{
|
||||
list.szConnection = nint.Zero;
|
||||
}
|
||||
list.dwOptionCount = options.Length;
|
||||
list.dwOptionError = 0;
|
||||
|
||||
int optSize = Marshal.SizeOf(typeof(InternetConnectionOption));
|
||||
// make a pointer out of all that ...
|
||||
nint optionsPtr = Marshal.AllocCoTaskMem(optSize * options.Length); // !! remember to deallocate memory 4
|
||||
// copy the array over into that spot in memory ...
|
||||
for (int i = 0; i < options.Length; ++i)
|
||||
{
|
||||
if (Environment.Is64BitOperatingSystem)
|
||||
{
|
||||
nint opt = new(optionsPtr.ToInt64() + (i * optSize));
|
||||
Marshal.StructureToPtr(options[i], opt, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
nint opt = new(optionsPtr.ToInt32() + (i * optSize));
|
||||
Marshal.StructureToPtr(options[i], opt, false);
|
||||
}
|
||||
}
|
||||
|
||||
list.options = optionsPtr;
|
||||
|
||||
// and then make a pointer out of the whole list
|
||||
nint ipcoListPtr = Marshal.AllocCoTaskMem(list.dwSize); // !! remember to deallocate memory 5
|
||||
Marshal.StructureToPtr(list, ipcoListPtr, false);
|
||||
|
||||
// and finally, call the API method!
|
||||
bool isSuccess = NativeMethods.InternetSetOption(nint.Zero,
|
||||
InternetOption.INTERNET_OPTION_PER_CONNECTION_OPTION,
|
||||
ipcoListPtr, list.dwSize);
|
||||
int returnvalue = 0; // ERROR_SUCCESS
|
||||
if (!isSuccess)
|
||||
{ // get the error codes, they might be helpful
|
||||
returnvalue = Marshal.GetLastPInvokeError();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Notify the system that the registry settings have been changed and cause them to be refreshed
|
||||
NativeMethods.InternetSetOption(nint.Zero, InternetOption.INTERNET_OPTION_SETTINGS_CHANGED, nint.Zero, 0);
|
||||
NativeMethods.InternetSetOption(nint.Zero, InternetOption.INTERNET_OPTION_REFRESH, nint.Zero, 0);
|
||||
}
|
||||
|
||||
// FREE the data ASAP
|
||||
if (list.szConnection != nint.Zero) Marshal.FreeHGlobal(list.szConnection); // release mem 3
|
||||
if (optionCount > 1)
|
||||
{
|
||||
Marshal.FreeHGlobal(options[1].m_Value.m_StringPtr); // release mem 1
|
||||
if (optionCount > 2)
|
||||
{
|
||||
Marshal.FreeHGlobal(options[2].m_Value.m_StringPtr); // release mem 2
|
||||
}
|
||||
}
|
||||
Marshal.FreeCoTaskMem(optionsPtr); // release mem 4
|
||||
Marshal.FreeCoTaskMem(ipcoListPtr); // release mem 5
|
||||
if (returnvalue != 0)
|
||||
{
|
||||
// throw the error codes, they might be helpful
|
||||
throw new ApplicationException($"Set Internet Proxy failed with error code: {Marshal.GetLastWin32Error()}");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve list of connections including LAN and WAN to support PPPoE connection
|
||||
/// </summary>
|
||||
/// <returns>A list of RAS connection names. May be empty list if no dial up connection.</returns>
|
||||
/// <exception cref="ApplicationException">Error message with win32 error code</exception>
|
||||
private static IEnumerable<string> EnumerateRasEntries()
|
||||
{
|
||||
int entries = 0;
|
||||
// attempt to query with 1 entry buffer
|
||||
RASENTRYNAME[] rasEntryNames = new RASENTRYNAME[1];
|
||||
int bufferSize = Marshal.SizeOf(typeof(RASENTRYNAME));
|
||||
rasEntryNames[0].dwSize = Marshal.SizeOf(typeof(RASENTRYNAME));
|
||||
|
||||
uint result = NativeMethods.RasEnumEntries(null, null, rasEntryNames, ref bufferSize, ref entries);
|
||||
// increase buffer if the buffer is not large enough
|
||||
if (result == (uint)ErrorCode.ERROR_BUFFER_TOO_SMALL)
|
||||
{
|
||||
rasEntryNames = new RASENTRYNAME[bufferSize / Marshal.SizeOf(typeof(RASENTRYNAME))];
|
||||
for (int i = 0; i < rasEntryNames.Length; i++)
|
||||
{
|
||||
rasEntryNames[i].dwSize = Marshal.SizeOf(typeof(RASENTRYNAME));
|
||||
}
|
||||
|
||||
result = NativeMethods.RasEnumEntries(null, null, rasEntryNames, ref bufferSize, ref entries);
|
||||
}
|
||||
if (result == 0)
|
||||
{
|
||||
var entryNames = new List<string>();
|
||||
for (int i = 0; i < entries; i++)
|
||||
{
|
||||
entryNames.Add(rasEntryNames[i].szEntryName);
|
||||
}
|
||||
|
||||
return entryNames;
|
||||
}
|
||||
throw new ApplicationException($"RasEnumEntries failed with error code: {result}");
|
||||
}
|
||||
|
||||
#region WinInet structures
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
|
||||
public struct InternetPerConnOptionList
|
||||
{
|
||||
public int dwSize; // size of the INTERNET_PER_CONN_OPTION_LIST struct
|
||||
public nint szConnection; // connection name to set/query options
|
||||
public int dwOptionCount; // number of options to set/query
|
||||
public int dwOptionError; // on error, which option failed
|
||||
|
||||
//[MarshalAs(UnmanagedType.)]
|
||||
public nint options;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
|
||||
public struct InternetConnectionOption
|
||||
{
|
||||
private static readonly int Size;
|
||||
public PerConnOption m_Option;
|
||||
public InternetConnectionOptionValue m_Value;
|
||||
|
||||
static InternetConnectionOption()
|
||||
{
|
||||
Size = Marshal.SizeOf(typeof(InternetConnectionOption));
|
||||
}
|
||||
|
||||
// Nested Types
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct InternetConnectionOptionValue
|
||||
{
|
||||
// Fields
|
||||
[FieldOffset(0)]
|
||||
public System.Runtime.InteropServices.ComTypes.FILETIME m_FileTime;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public int m_Int;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public nint m_StringPtr;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
|
||||
public struct RASENTRYNAME
|
||||
{
|
||||
public int dwSize;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = RAS_MaxEntryName + 1)]
|
||||
public string szEntryName;
|
||||
|
||||
public int dwFlags;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH + 1)]
|
||||
public string szPhonebookPath;
|
||||
}
|
||||
|
||||
// Constants
|
||||
public const int RAS_MaxEntryName = 256;
|
||||
|
||||
public const int MAX_PATH = 260; // Standard MAX_PATH value in Windows
|
||||
}
|
||||
|
||||
#endregion WinInet structures
|
||||
|
||||
#region WinInet enums
|
||||
|
||||
//
|
||||
// options manifests for Internet{Query|Set}Option
|
||||
//
|
||||
public enum InternetOption : uint
|
||||
{
|
||||
INTERNET_OPTION_PER_CONNECTION_OPTION = 75,
|
||||
INTERNET_OPTION_REFRESH = 37,
|
||||
INTERNET_OPTION_SETTINGS_CHANGED = 39
|
||||
}
|
||||
|
||||
//
|
||||
// Options used in INTERNET_PER_CONN_OPTON struct
|
||||
//
|
||||
public enum PerConnOption
|
||||
{
|
||||
INTERNET_PER_CONN_FLAGS = 1, // Sets or retrieves the connection type. The Value member will contain one or more of the values from PerConnFlags
|
||||
INTERNET_PER_CONN_PROXY_SERVER = 2, // Sets or retrieves a string containing the proxy servers.
|
||||
INTERNET_PER_CONN_PROXY_BYPASS = 3, // Sets or retrieves a string containing the URLs that do not use the proxy server.
|
||||
INTERNET_PER_CONN_AUTOCONFIG_URL = 4//, // Sets or retrieves a string containing the URL to the automatic configuration script.
|
||||
}
|
||||
|
||||
//
|
||||
// PER_CONN_FLAGS
|
||||
//
|
||||
[Flags]
|
||||
public enum PerConnFlags
|
||||
{
|
||||
PROXY_TYPE_DIRECT = 0x00000001, // direct to net
|
||||
PROXY_TYPE_PROXY = 0x00000002, // via named proxy
|
||||
PROXY_TYPE_AUTO_PROXY_URL = 0x00000004, // autoproxy URL
|
||||
PROXY_TYPE_AUTO_DETECT = 0x00000008 // use autoproxy detection
|
||||
}
|
||||
|
||||
public enum ErrorCode : uint
|
||||
{
|
||||
ERROR_BUFFER_TOO_SMALL = 603,
|
||||
ERROR_INVALID_SIZE = 632
|
||||
}
|
||||
|
||||
#endregion WinInet enums
|
||||
|
||||
internal static class NativeMethods
|
||||
{
|
||||
[DllImport("WinInet.dll", SetLastError = true, CharSet = CharSet.Auto)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool InternetSetOption(nint hInternet, InternetOption dwOption, nint lpBuffer, int dwBufferLength);
|
||||
|
||||
[DllImport("Rasapi32.dll", CharSet = CharSet.Auto)]
|
||||
public static extern uint RasEnumEntries(
|
||||
string? reserved, // Reserved, must be null
|
||||
string? lpszPhonebook, // Pointer to full path and filename of phone-book file. If this parameter is NULL, the entries are enumerated from all the remote access phone-book files
|
||||
[In, Out] RASENTRYNAME[]? lprasentryname, // Buffer to receive RAS entry names
|
||||
ref int lpcb, // Size of the buffer
|
||||
ref int lpcEntries // Number of entries written to the buffer
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
99
v2rayN/ServiceLib/Handler/SysProxy/SysProxyHandler.cs
Normal file
99
v2rayN/ServiceLib/Handler/SysProxy/SysProxyHandler.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
namespace ServiceLib.Handler.SysProxy
|
||||
{
|
||||
public static class SysProxyHandler
|
||||
{
|
||||
private static readonly string _tag = "SysProxyHandler";
|
||||
|
||||
public static async Task<bool> UpdateSysProxy(Config config, bool forceDisable)
|
||||
{
|
||||
var type = config.SystemProxyItem.SysProxyType;
|
||||
|
||||
if (forceDisable && type != ESysProxyType.Unchanged)
|
||||
{
|
||||
type = ESysProxyType.ForcedClear;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
|
||||
var exceptions = config.SystemProxyItem.SystemProxyExceptions;
|
||||
if (port <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
switch (type)
|
||||
{
|
||||
case ESysProxyType.ForcedChange when Utils.IsWindows():
|
||||
{
|
||||
GetWindowsProxyString(config, port, out var strProxy, out var strExceptions);
|
||||
ProxySettingWindows.SetProxy(strProxy, strExceptions, 2);
|
||||
break;
|
||||
}
|
||||
case ESysProxyType.ForcedChange when Utils.IsLinux():
|
||||
await ProxySettingLinux.SetProxy(Global.Loopback, port, exceptions);
|
||||
break;
|
||||
|
||||
case ESysProxyType.ForcedChange when Utils.IsOSX():
|
||||
await ProxySettingOSX.SetProxy(Global.Loopback, port, exceptions);
|
||||
break;
|
||||
|
||||
case ESysProxyType.ForcedClear when Utils.IsWindows():
|
||||
ProxySettingWindows.UnsetProxy();
|
||||
break;
|
||||
|
||||
case ESysProxyType.ForcedClear when Utils.IsLinux():
|
||||
await ProxySettingLinux.UnsetProxy();
|
||||
break;
|
||||
|
||||
case ESysProxyType.ForcedClear when Utils.IsOSX():
|
||||
await ProxySettingOSX.UnsetProxy();
|
||||
break;
|
||||
|
||||
case ESysProxyType.Pac when Utils.IsWindows():
|
||||
await SetWindowsProxyPac(port);
|
||||
break;
|
||||
}
|
||||
|
||||
if (type != ESysProxyType.Pac && Utils.IsWindows())
|
||||
{
|
||||
PacHandler.Stop();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void GetWindowsProxyString(Config config, int port, out string strProxy, out string strExceptions)
|
||||
{
|
||||
strExceptions = config.SystemProxyItem.SystemProxyExceptions;
|
||||
if (config.SystemProxyItem.NotProxyLocalAddress)
|
||||
{
|
||||
strExceptions = $"<local>;{strExceptions}";
|
||||
}
|
||||
|
||||
strProxy = string.Empty;
|
||||
if (Utils.IsNullOrEmpty(config.SystemProxyItem.SystemProxyAdvancedProtocol))
|
||||
{
|
||||
strProxy = $"{Global.Loopback}:{port}";
|
||||
}
|
||||
else
|
||||
{
|
||||
strProxy = config.SystemProxyItem.SystemProxyAdvancedProtocol
|
||||
.Replace("{ip}", Global.Loopback)
|
||||
.Replace("{http_port}", port.ToString())
|
||||
.Replace("{socks_port}", port.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task SetWindowsProxyPac(int port)
|
||||
{
|
||||
var portPac = AppHandler.Instance.GetLocalPort(EInboundProtocol.pac);
|
||||
await PacHandler.Start(Utils.GetConfigPath(), port, portPac);
|
||||
var strProxy = $"{Global.HttpProtocol}{Global.Loopback}:{portPac}/pac?t={DateTime.Now.Ticks}";
|
||||
ProxySettingWindows.SetProxy(strProxy, "", 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
72
v2rayN/ServiceLib/Handler/TaskHandler.cs
Normal file
72
v2rayN/ServiceLib/Handler/TaskHandler.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
namespace ServiceLib.Handler
|
||||
{
|
||||
public class TaskHandler
|
||||
{
|
||||
private static readonly Lazy<TaskHandler> _instance = new(() => new());
|
||||
public static TaskHandler Instance => _instance.Value;
|
||||
|
||||
public void RegUpdateTask(Config config, Action<bool, string> updateFunc)
|
||||
{
|
||||
Task.Run(() => UpdateTaskRunSubscription(config, updateFunc));
|
||||
Task.Run(() => UpdateTaskRunGeo(config, updateFunc));
|
||||
}
|
||||
|
||||
private async Task UpdateTaskRunSubscription(Config config, Action<bool, string> updateFunc)
|
||||
{
|
||||
await Task.Delay(60000);
|
||||
Logging.SaveLog("UpdateTaskRunSubscription");
|
||||
|
||||
var updateHandle = new UpdateService();
|
||||
while (true)
|
||||
{
|
||||
var updateTime = ((DateTimeOffset)DateTime.Now).ToUnixTimeSeconds();
|
||||
var lstSubs = (await AppHandler.Instance.SubItems())
|
||||
.Where(t => t.AutoUpdateInterval > 0)
|
||||
.Where(t => updateTime - t.UpdateTime >= t.AutoUpdateInterval * 60)
|
||||
.ToList();
|
||||
|
||||
foreach (var item in lstSubs)
|
||||
{
|
||||
await updateHandle.UpdateSubscriptionProcess(config, item.Id, true, (bool success, string msg) =>
|
||||
{
|
||||
updateFunc?.Invoke(success, msg);
|
||||
if (success)
|
||||
Logging.SaveLog("subscription" + msg);
|
||||
});
|
||||
item.UpdateTime = updateTime;
|
||||
await ConfigHandler.AddSubItem(config, item);
|
||||
|
||||
await Task.Delay(5000);
|
||||
}
|
||||
await Task.Delay(60000);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpdateTaskRunGeo(Config config, Action<bool, string> updateFunc)
|
||||
{
|
||||
var autoUpdateGeoTime = DateTime.Now;
|
||||
|
||||
//await Task.Delay(1000 * 120);
|
||||
Logging.SaveLog("UpdateTaskRunGeo");
|
||||
|
||||
var updateHandle = new UpdateService();
|
||||
while (true)
|
||||
{
|
||||
await Task.Delay(1000 * 3600);
|
||||
|
||||
var dtNow = DateTime.Now;
|
||||
if (config.GuiItem.AutoUpdateInterval > 0)
|
||||
{
|
||||
if ((dtNow - autoUpdateGeoTime).Hours % config.GuiItem.AutoUpdateInterval == 0)
|
||||
{
|
||||
await updateHandle.UpdateGeoFileAll(config, (bool success, string msg) =>
|
||||
{
|
||||
updateFunc?.Invoke(false, msg);
|
||||
});
|
||||
autoUpdateGeoTime = dtNow;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
179
v2rayN/ServiceLib/Handler/WebDavHandler.cs
Normal file
179
v2rayN/ServiceLib/Handler/WebDavHandler.cs
Normal file
@@ -0,0 +1,179 @@
|
||||
using System.Net;
|
||||
using WebDav;
|
||||
|
||||
namespace ServiceLib.Handler
|
||||
{
|
||||
public sealed class WebDavHandler
|
||||
{
|
||||
private static readonly Lazy<WebDavHandler> _instance = new(() => new());
|
||||
public static WebDavHandler Instance => _instance.Value;
|
||||
|
||||
private Config? _config;
|
||||
private WebDavClient? _client;
|
||||
private string? _lastDescription;
|
||||
private string _webDir = Global.AppName + "_backup";
|
||||
private readonly string _webFileName = "backup.zip";
|
||||
private readonly string _tag = "WebDav--";
|
||||
|
||||
public WebDavHandler()
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
}
|
||||
|
||||
private async Task<bool> GetClient()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_config.WebDavItem.Url.IsNullOrEmpty()
|
||||
|| _config.WebDavItem.UserName.IsNullOrEmpty()
|
||||
|| _config.WebDavItem.Password.IsNullOrEmpty())
|
||||
{
|
||||
throw new ArgumentException("webdav parameter error or null");
|
||||
}
|
||||
if (_client != null)
|
||||
{
|
||||
_client?.Dispose();
|
||||
_client = null;
|
||||
}
|
||||
if (_config.WebDavItem.DirName.IsNullOrEmpty())
|
||||
{
|
||||
_webDir = Global.AppName + "_backup";
|
||||
}
|
||||
else
|
||||
{
|
||||
_webDir = _config.WebDavItem.DirName.TrimEx();
|
||||
}
|
||||
|
||||
var clientParams = new WebDavClientParams
|
||||
{
|
||||
BaseAddress = new Uri(_config.WebDavItem.Url),
|
||||
Credentials = new NetworkCredential(_config.WebDavItem.UserName, _config.WebDavItem.Password)
|
||||
};
|
||||
_client = new WebDavClient(clientParams);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SaveLog(ex);
|
||||
return false;
|
||||
}
|
||||
return await Task.FromResult(true);
|
||||
}
|
||||
|
||||
private async Task<bool> TryCreateDir()
|
||||
{
|
||||
if (_client is null) return false;
|
||||
try
|
||||
{
|
||||
var result2 = await _client.Mkcol(_webDir);
|
||||
if (result2.IsSuccessful)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
SaveLog(result2.Description);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SaveLog(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void SaveLog(string desc)
|
||||
{
|
||||
_lastDescription = desc;
|
||||
Logging.SaveLog(_tag + desc);
|
||||
}
|
||||
|
||||
private void SaveLog(Exception ex)
|
||||
{
|
||||
_lastDescription = ex.Message;
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
|
||||
public async Task<bool> CheckConnection()
|
||||
{
|
||||
if (await GetClient() == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
await TryCreateDir();
|
||||
|
||||
try
|
||||
{
|
||||
var testName = "readme_test";
|
||||
var myContent = new StringContent(testName);
|
||||
var result = await _client.PutFile($"{_webDir}/{testName}", myContent);
|
||||
if (result.IsSuccessful)
|
||||
{
|
||||
await _client.Delete($"{_webDir}/{testName}");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
SaveLog(result.Description);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SaveLog(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public async Task<bool> PutFile(string fileName)
|
||||
{
|
||||
if (await GetClient() == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
await TryCreateDir();
|
||||
|
||||
try
|
||||
{
|
||||
await using var fs = File.OpenRead(fileName);
|
||||
var result = await _client.PutFile($"{_webDir}/{_webFileName}", fs); // upload a resource
|
||||
if (result.IsSuccessful)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
SaveLog(result.Description);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SaveLog(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public async Task<bool> GetRawFile(string fileName)
|
||||
{
|
||||
if (await GetClient() == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
await TryCreateDir();
|
||||
|
||||
try
|
||||
{
|
||||
var response = await _client.GetRawFile($"{_webDir}/{_webFileName}");
|
||||
if (!response.IsSuccessful)
|
||||
{
|
||||
SaveLog(response.Description);
|
||||
return false;
|
||||
}
|
||||
|
||||
await using var outputFileStream = new FileStream(fileName, FileMode.Create);
|
||||
await response.Stream.CopyToAsync(outputFileStream);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SaveLog(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public string GetLastError() => _lastDescription ?? string.Empty;
|
||||
}
|
||||
}
|
||||
11
v2rayN/ServiceLib/Models/CheckUpdateModel.cs
Normal file
11
v2rayN/ServiceLib/Models/CheckUpdateModel.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
public class CheckUpdateModel
|
||||
{
|
||||
public bool? IsSelected { get; set; }
|
||||
public string? CoreType { get; set; }
|
||||
public string? Remarks { get; set; }
|
||||
public string? FileName { get; set; }
|
||||
public bool? IsFinished { get; set; }
|
||||
}
|
||||
}
|
||||
17
v2rayN/ServiceLib/Models/ClashConnectionModel.cs
Normal file
17
v2rayN/ServiceLib/Models/ClashConnectionModel.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
public class ClashConnectionModel
|
||||
{
|
||||
public string? Id { get; set; }
|
||||
public string? Network { get; set; }
|
||||
public string? Type { get; set; }
|
||||
public string? Host { get; set; }
|
||||
public ulong Upload { get; set; }
|
||||
public ulong Download { get; set; }
|
||||
public string? UploadTraffic { get; set; }
|
||||
public string? DownloadTraffic { get; set; }
|
||||
public double Time { get; set; }
|
||||
public string? Elapsed { get; set; }
|
||||
public string? Chain { get; set; }
|
||||
}
|
||||
}
|
||||
37
v2rayN/ServiceLib/Models/ClashConnections.cs
Normal file
37
v2rayN/ServiceLib/Models/ClashConnections.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
public class ClashConnections
|
||||
{
|
||||
public ulong downloadTotal { get; set; }
|
||||
public ulong uploadTotal { get; set; }
|
||||
public List<ConnectionItem>? connections { get; set; }
|
||||
}
|
||||
|
||||
public class ConnectionItem
|
||||
{
|
||||
public string? id { get; set; }
|
||||
public MetadataItem? metadata { get; set; }
|
||||
public ulong upload { get; set; }
|
||||
public ulong download { get; set; }
|
||||
public DateTime start { get; set; }
|
||||
public List<string>? chains { get; set; }
|
||||
public string? rule { get; set; }
|
||||
public string? rulePayload { get; set; }
|
||||
}
|
||||
|
||||
public class MetadataItem
|
||||
{
|
||||
public string? network { get; set; }
|
||||
public string? type { get; set; }
|
||||
public string? sourceIP { get; set; }
|
||||
public string? destinationIP { get; set; }
|
||||
public string? sourcePort { get; set; }
|
||||
public string? destinationPort { get; set; }
|
||||
public string? host { get; set; }
|
||||
public string? nsMode { get; set; }
|
||||
public object uid { get; set; }
|
||||
public string? process { get; set; }
|
||||
public string? processPath { get; set; }
|
||||
public string? remoteDestination { get; set; }
|
||||
}
|
||||
}
|
||||
17
v2rayN/ServiceLib/Models/ClashProviders.cs
Normal file
17
v2rayN/ServiceLib/Models/ClashProviders.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using static ServiceLib.Models.ClashProxies;
|
||||
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
public class ClashProviders
|
||||
{
|
||||
public Dictionary<string, ProvidersItem>? providers { get; set; }
|
||||
|
||||
public class ProvidersItem
|
||||
{
|
||||
public string? name { get; set; }
|
||||
public List<ProxiesItem>? proxies { get; set; }
|
||||
public string? type { get; set; }
|
||||
public string? vehicleType { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
24
v2rayN/ServiceLib/Models/ClashProxies.cs
Normal file
24
v2rayN/ServiceLib/Models/ClashProxies.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
public class ClashProxies
|
||||
{
|
||||
public Dictionary<string, ProxiesItem>? proxies { get; set; }
|
||||
|
||||
public class ProxiesItem
|
||||
{
|
||||
public List<string>? all { get; set; }
|
||||
public List<HistoryItem>? history { get; set; }
|
||||
public string? name { get; set; }
|
||||
public string? type { get; set; }
|
||||
public bool udp { get; set; }
|
||||
public string? now { get; set; }
|
||||
public int delay { get; set; }
|
||||
}
|
||||
|
||||
public class HistoryItem
|
||||
{
|
||||
public string? time { get; set; }
|
||||
public int delay { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
18
v2rayN/ServiceLib/Models/ClashProxyModel.cs
Normal file
18
v2rayN/ServiceLib/Models/ClashProxyModel.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class ClashProxyModel
|
||||
{
|
||||
public string? Name { get; set; }
|
||||
|
||||
public string? Type { get; set; }
|
||||
|
||||
public string? Now { get; set; }
|
||||
|
||||
public int Delay { get; set; }
|
||||
|
||||
public string? DelayName { get; set; }
|
||||
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
}
|
||||
8
v2rayN/ServiceLib/Models/CmdItem.cs
Normal file
8
v2rayN/ServiceLib/Models/CmdItem.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
public class CmdItem
|
||||
{
|
||||
public string? Cmd { get; set; }
|
||||
public List<string>? Arguments { get; set; }
|
||||
}
|
||||
}
|
||||
15
v2rayN/ServiceLib/Models/ComboItem.cs
Normal file
15
v2rayN/ServiceLib/Models/ComboItem.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
public class ComboItem
|
||||
{
|
||||
public string? ID
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public string? Text
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
}
|
||||
}
|
||||
56
v2rayN/ServiceLib/Models/Config.cs
Normal file
56
v2rayN/ServiceLib/Models/Config.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// 本软件配置文件实体类
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class Config
|
||||
{
|
||||
#region property
|
||||
|
||||
public string IndexId { get; set; }
|
||||
public string SubIndexId { get; set; }
|
||||
|
||||
public ECoreType RunningCoreType { get; set; }
|
||||
|
||||
public bool IsRunningCore(ECoreType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ECoreType.Xray when RunningCoreType is ECoreType.Xray or ECoreType.v2fly or ECoreType.v2fly_v5:
|
||||
case ECoreType.sing_box when RunningCoreType is ECoreType.sing_box or ECoreType.mihomo:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion property
|
||||
|
||||
#region other entities
|
||||
|
||||
public CoreBasicItem CoreBasicItem { get; set; }
|
||||
public TunModeItem TunModeItem { get; set; }
|
||||
public KcpItem KcpItem { get; set; }
|
||||
public GrpcItem GrpcItem { get; set; }
|
||||
public RoutingBasicItem RoutingBasicItem { get; set; }
|
||||
public GUIItem GuiItem { get; set; }
|
||||
public MsgUIItem MsgUIItem { get; set; }
|
||||
public UIItem UiItem { get; set; }
|
||||
public ConstItem ConstItem { get; set; }
|
||||
public SpeedTestItem SpeedTestItem { get; set; }
|
||||
public Mux4RayItem Mux4RayItem { get; set; }
|
||||
public Mux4SboxItem Mux4SboxItem { get; set; }
|
||||
public HysteriaItem HysteriaItem { get; set; }
|
||||
public ClashUIItem ClashUIItem { get; set; }
|
||||
public SystemProxyItem SystemProxyItem { get; set; }
|
||||
public WebDavItem WebDavItem { get; set; }
|
||||
public CheckUpdateItem CheckUpdateItem { get; set; }
|
||||
public List<InItem> Inbound { get; set; }
|
||||
public List<KeyEventItem> GlobalHotkeys { get; set; }
|
||||
public List<CoreTypeItem> CoreTypeItem { get; set; }
|
||||
|
||||
#endregion other entities
|
||||
}
|
||||
}
|
||||
249
v2rayN/ServiceLib/Models/ConfigItems.cs
Normal file
249
v2rayN/ServiceLib/Models/ConfigItems.cs
Normal file
@@ -0,0 +1,249 @@
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class CoreBasicItem
|
||||
{
|
||||
public bool LogEnabled { get; set; }
|
||||
|
||||
public string Loglevel { get; set; }
|
||||
|
||||
public bool MuxEnabled { get; set; }
|
||||
|
||||
public bool DefAllowInsecure { get; set; }
|
||||
|
||||
public string DefFingerprint { get; set; }
|
||||
|
||||
public string DefUserAgent { get; set; }
|
||||
|
||||
public bool EnableFragment { get; set; }
|
||||
|
||||
public bool EnableCacheFile4Sbox { get; set; } = true;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class InItem
|
||||
{
|
||||
public int LocalPort { get; set; }
|
||||
public string Protocol { get; set; }
|
||||
public bool UdpEnabled { get; set; }
|
||||
public bool SniffingEnabled { get; set; } = true;
|
||||
public List<string>? DestOverride { get; set; } = ["http", "tls"];
|
||||
public bool RouteOnly { get; set; }
|
||||
public bool AllowLANConn { get; set; }
|
||||
public bool NewPort4LAN { get; set; }
|
||||
public string User { get; set; }
|
||||
public string Pass { get; set; }
|
||||
public bool SecondLocalPortEnabled { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class KcpItem
|
||||
{
|
||||
public int Mtu { get; set; }
|
||||
|
||||
public int Tti { get; set; }
|
||||
|
||||
public int UplinkCapacity { get; set; }
|
||||
|
||||
public int DownlinkCapacity { get; set; }
|
||||
|
||||
public bool Congestion { get; set; }
|
||||
|
||||
public int ReadBufferSize { get; set; }
|
||||
|
||||
public int WriteBufferSize { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class GrpcItem
|
||||
{
|
||||
public int? IdleTimeout { get; set; }
|
||||
public int? HealthCheckTimeout { get; set; }
|
||||
public bool? PermitWithoutStream { get; set; }
|
||||
public int? InitialWindowsSize { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class GUIItem
|
||||
{
|
||||
public bool AutoRun { get; set; }
|
||||
|
||||
public bool EnableStatistics { get; set; }
|
||||
|
||||
public bool KeepOlderDedupl { get; set; }
|
||||
|
||||
public bool IgnoreGeoUpdateCore { get; set; } = true;
|
||||
|
||||
public int AutoUpdateInterval { get; set; }
|
||||
|
||||
public bool EnableSecurityProtocolTls13 { get; set; }
|
||||
|
||||
public int TrayMenuServersLimit { get; set; } = 20;
|
||||
|
||||
public bool EnableHWA { get; set; } = false;
|
||||
|
||||
public bool EnableLog { get; set; } = true;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class MsgUIItem
|
||||
{
|
||||
public string? MainMsgFilter { get; set; }
|
||||
public bool? AutoRefresh { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class UIItem
|
||||
{
|
||||
public bool EnableAutoAdjustMainLvColWidth { get; set; }
|
||||
public bool EnableUpdateSubOnlyRemarksExist { get; set; }
|
||||
public double MainWidth { get; set; }
|
||||
public double MainHeight { get; set; }
|
||||
public double MainGirdHeight1 { get; set; }
|
||||
public double MainGirdHeight2 { get; set; }
|
||||
public EGirdOrientation MainGirdOrientation { get; set; } = EGirdOrientation.Vertical;
|
||||
public bool ColorModeDark { get; set; }
|
||||
public bool FollowSystemTheme { get; set; }
|
||||
public string? ColorPrimaryName { get; set; }
|
||||
public string CurrentLanguage { get; set; }
|
||||
public string CurrentFontFamily { get; set; }
|
||||
public int CurrentFontSize { get; set; }
|
||||
public bool EnableDragDropSort { get; set; }
|
||||
public bool DoubleClick2Activate { get; set; }
|
||||
public bool AutoHideStartup { get; set; }
|
||||
public bool Hide2TrayWhenClose { get; set; }
|
||||
public List<ColumnItem> MainColumnItem { get; set; }
|
||||
public bool ShowInTaskbar { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ConstItem
|
||||
{
|
||||
public string DefIEProxyExceptions { get; set; }
|
||||
public string SubConvertUrl { get; set; } = string.Empty;
|
||||
public string? GeoSourceUrl { get; set; }
|
||||
public string? SrsSourceUrl { get; set; }
|
||||
public string? RouteRulesTemplateSourceUrl { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class KeyEventItem
|
||||
{
|
||||
public EGlobalHotkey EGlobalHotkey { get; set; }
|
||||
|
||||
public bool Alt { get; set; }
|
||||
|
||||
public bool Control { get; set; }
|
||||
|
||||
public bool Shift { get; set; }
|
||||
|
||||
public int? KeyCode { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class CoreTypeItem
|
||||
{
|
||||
public EConfigType ConfigType { get; set; }
|
||||
|
||||
public ECoreType CoreType { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class TunModeItem
|
||||
{
|
||||
public bool EnableTun { get; set; }
|
||||
public bool StrictRoute { get; set; } = true;
|
||||
public string Stack { get; set; }
|
||||
public int Mtu { get; set; }
|
||||
public bool EnableExInbound { get; set; }
|
||||
public bool EnableIPv6Address { get; set; }
|
||||
public string? LinuxSudoPwd { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class SpeedTestItem
|
||||
{
|
||||
public int SpeedTestTimeout { get; set; }
|
||||
public string SpeedTestUrl { get; set; }
|
||||
public string SpeedPingTestUrl { get; set; }
|
||||
public int SpeedTestPageSize { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class RoutingBasicItem
|
||||
{
|
||||
public string DomainStrategy { get; set; }
|
||||
public string DomainStrategy4Singbox { get; set; }
|
||||
public string DomainMatcher { get; set; }
|
||||
public string RoutingIndexId { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ColumnItem
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public int Width { get; set; }
|
||||
public int Index { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Mux4RayItem
|
||||
{
|
||||
public int? Concurrency { get; set; }
|
||||
public int? XudpConcurrency { get; set; }
|
||||
public string? XudpProxyUDP443 { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Mux4SboxItem
|
||||
{
|
||||
public string Protocol { get; set; }
|
||||
public int MaxConnections { get; set; }
|
||||
public bool? Padding { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class HysteriaItem
|
||||
{
|
||||
public int UpMbps { get; set; }
|
||||
public int DownMbps { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ClashUIItem
|
||||
{
|
||||
public ERuleMode RuleMode { get; set; }
|
||||
public bool EnableIPv6 { get; set; }
|
||||
public bool EnableMixinContent { get; set; }
|
||||
public int ProxiesSorting { get; set; }
|
||||
public bool ProxiesAutoRefresh { get; set; }
|
||||
public int ProxiesAutoDelayTestInterval { get; set; } = 10;
|
||||
public bool ConnectionsAutoRefresh { get; set; }
|
||||
public int ConnectionsRefreshInterval { get; set; } = 2;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class SystemProxyItem
|
||||
{
|
||||
public ESysProxyType SysProxyType { get; set; }
|
||||
public string SystemProxyExceptions { get; set; }
|
||||
public bool NotProxyLocalAddress { get; set; } = true;
|
||||
public string SystemProxyAdvancedProtocol { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class WebDavItem
|
||||
{
|
||||
public string? Url { get; set; }
|
||||
public string? UserName { get; set; }
|
||||
public string? Password { get; set; }
|
||||
public string? DirName { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class CheckUpdateItem
|
||||
{
|
||||
public bool CheckPreReleaseUpdate { get; set; }
|
||||
public List<string>? SelectedCoreTypes { get; set; }
|
||||
}
|
||||
}
|
||||
21
v2rayN/ServiceLib/Models/CoreInfo.cs
Normal file
21
v2rayN/ServiceLib/Models/CoreInfo.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class CoreInfo
|
||||
{
|
||||
public ECoreType CoreType { get; set; }
|
||||
public List<string>? CoreExes { get; set; }
|
||||
public string? Arguments { get; set; }
|
||||
public string? Url { get; set; }
|
||||
public string? ReleaseApiUrl { get; set; }
|
||||
public string? DownloadUrlWin64 { get; set; }
|
||||
public string? DownloadUrlWinArm64 { get; set; }
|
||||
public string? DownloadUrlLinux64 { get; set; }
|
||||
public string? DownloadUrlLinuxArm64 { get; set; }
|
||||
public string? DownloadUrlOSX64 { get; set; }
|
||||
public string? DownloadUrlOSXArm64 { get; set; }
|
||||
public string? Match { get; set; }
|
||||
public string? VersionArg { get; set; }
|
||||
public bool RedirectInfo { get; set; }
|
||||
}
|
||||
}
|
||||
20
v2rayN/ServiceLib/Models/DNSItem.cs
Normal file
20
v2rayN/ServiceLib/Models/DNSItem.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using SQLite;
|
||||
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class DNSItem
|
||||
{
|
||||
[PrimaryKey]
|
||||
public string Id { get; set; }
|
||||
|
||||
public string Remarks { get; set; }
|
||||
public bool Enabled { get; set; } = true;
|
||||
public ECoreType CoreType { get; set; }
|
||||
public bool UseSystemHosts { get; set; }
|
||||
public string? NormalDNS { get; set; }
|
||||
public string? TunDNS { get; set; }
|
||||
public string? DomainStrategy4Freedom { get; set; }
|
||||
public string? DomainDNSAddress { get; set; }
|
||||
}
|
||||
}
|
||||
68
v2rayN/ServiceLib/Models/GitHubRelease.cs
Normal file
68
v2rayN/ServiceLib/Models/GitHubRelease.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
public class GitHubReleaseAsset
|
||||
{
|
||||
[JsonPropertyName("url")] public string? Url { get; set; }
|
||||
|
||||
[JsonPropertyName("id")] public int Id { get; set; }
|
||||
|
||||
[JsonPropertyName("node_id")] public string? NodeId { get; set; }
|
||||
|
||||
[JsonPropertyName("name")] public string? Name { get; set; }
|
||||
|
||||
[JsonPropertyName("label")] public object Label { get; set; }
|
||||
|
||||
[JsonPropertyName("content_type")] public string? ContentType { get; set; }
|
||||
|
||||
[JsonPropertyName("state")] public string? State { get; set; }
|
||||
|
||||
[JsonPropertyName("size")] public int Size { get; set; }
|
||||
|
||||
[JsonPropertyName("download_count")] public int DownloadCount { get; set; }
|
||||
|
||||
[JsonPropertyName("created_at")] public DateTime CreatedAt { get; set; }
|
||||
|
||||
[JsonPropertyName("updated_at")] public DateTime UpdatedAt { get; set; }
|
||||
|
||||
[JsonPropertyName("browser_download_url")] public string? BrowserDownloadUrl { get; set; }
|
||||
}
|
||||
|
||||
public class GitHubRelease
|
||||
{
|
||||
[JsonPropertyName("url")] public string? Url { get; set; }
|
||||
|
||||
[JsonPropertyName("assets_url")] public string? AssetsUrl { get; set; }
|
||||
|
||||
[JsonPropertyName("upload_url")] public string? UploadUrl { get; set; }
|
||||
|
||||
[JsonPropertyName("html_url")] public string? HtmlUrl { get; set; }
|
||||
|
||||
[JsonPropertyName("id")] public int Id { get; set; }
|
||||
|
||||
[JsonPropertyName("node_id")] public string? NodeId { get; set; }
|
||||
|
||||
[JsonPropertyName("tag_name")] public string? TagName { get; set; }
|
||||
|
||||
[JsonPropertyName("target_commitish")] public string? TargetCommitish { get; set; }
|
||||
|
||||
[JsonPropertyName("name")] public string? Name { get; set; }
|
||||
|
||||
[JsonPropertyName("draft")] public bool Draft { get; set; }
|
||||
|
||||
[JsonPropertyName("prerelease")] public bool Prerelease { get; set; }
|
||||
|
||||
[JsonPropertyName("created_at")] public DateTime CreatedAt { get; set; }
|
||||
|
||||
[JsonPropertyName("published_at")] public DateTime PublishedAt { get; set; }
|
||||
|
||||
[JsonPropertyName("assets")] public List<GitHubReleaseAsset> Assets { get; set; }
|
||||
|
||||
[JsonPropertyName("tarball_url")] public string? TarballUrl { get; set; }
|
||||
|
||||
[JsonPropertyName("zipball_url")] public string? ZipballUrl { get; set; }
|
||||
|
||||
[JsonPropertyName("body")] public string? Body { get; set; }
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user