Compare commits
1308 Commits
Author | SHA1 | Date | |
---|---|---|---|
fc31b528da | |||
718dd77c1b | |||
e1cb460649 | |||
![]() |
99dc2d321c | ||
0ccddd4e75 | |||
5087359ac3 | |||
1ab70d7cd7 | |||
0438ee9a50 | |||
![]() |
90ea515f41 | ||
![]() |
2bff4df4be | ||
![]() |
10606532cb | ||
0ad978bb6b | |||
![]() |
452675273b | ||
![]() |
d8a29eaeee | ||
![]() |
b1078cd36c | ||
a001471451 | |||
![]() |
bcdfef3f6f | ||
![]() |
ef23e41e3a | ||
5f10d50a3c | |||
aee5fb22d0 | |||
14c8b12df7 | |||
deed89560b | |||
eb32437f96 | |||
c95b7a373b | |||
302e7ce4fc | |||
a5fc5de78f | |||
d362d852ad | |||
2a3f9fb4fa | |||
944751121c | |||
fb420d1116 | |||
26af578178 | |||
245dd85341 | |||
fb6596fe64 | |||
a42180d244 | |||
73b24f9a56 | |||
eae2795bbf | |||
98e73b2372 | |||
2225ab9011 | |||
4c0895850b | |||
ea412908ef | |||
05ea48a0be | |||
243ff95198 | |||
8341394912 | |||
e5a89c9182 | |||
df5b8cd9f0 | |||
e7b6a87175 | |||
5b2863058a | |||
cd8844afef | |||
b26d979b8d | |||
5446a1bee0 | |||
10101986be | |||
47a5f50712 | |||
86517f313c | |||
b6d34404b5 | |||
88ac0cd31e | |||
3e0fd7c532 | |||
22d10cd28b | |||
0e95b72b21 | |||
426a9d7721 | |||
d42210f922 | |||
e853003bda | |||
8ca4faa88a | |||
f84f2195c3 | |||
1742ca385b | |||
2a9ebaff51 | |||
916154499c | |||
53a84b2349 | |||
6e3eb5c1d3 | |||
4b4754ae16 | |||
a273d6aac6 | |||
9e1878628b | |||
04ce7fdae5 | |||
f7de7bd430 | |||
ab8a864519 | |||
427f8dded0 | |||
a96764f725 | |||
46d0244c34 | |||
2cb3c1c331 | |||
231e6a56bd | |||
295ad2fca8 | |||
9ecc93a399 | |||
2896cc6bf1 | |||
be37951807 | |||
d38e26ba67 | |||
dd85e98c9a | |||
d8fa8779de | |||
f6fd43ae50 | |||
96dbe7b2cd | |||
c1fe39d3cc | |||
56ee12741c | |||
4372bdb09d | |||
b21f967a6a | |||
40b0b704e3 | |||
9a8ee4d7f9 | |||
4f319d474f | |||
93ed60bcf7 | |||
5d717505f4 | |||
b0e50d33bc | |||
c0f9816000 | |||
00ea473683 | |||
7e9054fb8f | |||
f29a63027f | |||
3419e62c6a | |||
e930cf86fd | |||
c0a27efd8d | |||
d42539c0a8 | |||
8389f755fd | |||
6d5af6d422 | |||
7738b3b898 | |||
c1052e0147 | |||
d3135bef8e | |||
54af38b6b6 | |||
9d4b7e535c | |||
8190c80f1a | |||
c887e1914d | |||
394ce1e903 | |||
d2cba59b6b | |||
7a23340623 | |||
5a2a48945b | |||
51da417506 | |||
a148319b44 | |||
340794d14c | |||
f6b222e469 | |||
fb17f37188 | |||
2450bc0b9c | |||
6737b4885b | |||
81d6e67f94 | |||
911be8135c | |||
e68c1e5e2f | |||
07e0665275 | |||
4e78905bbc | |||
e2c9169121 | |||
1f859c75c6 | |||
b2fdc38790 | |||
07991329fc | |||
5cea33ec20 | |||
01e94830e7 | |||
c8e757487e | |||
9cd4f2d744 | |||
2a2cdf8a4a | |||
4a46d6211c | |||
98b9019633 | |||
434f5d4a8d | |||
46a3049d62 | |||
be9ed37eea | |||
dd978ab952 | |||
15b2e606b1 | |||
0cbb22ac26 | |||
11afb64706 | |||
240a65e61c | |||
2da5051c5b | |||
1124dc5bf4 | |||
b93d16669f | |||
5ab1c97b79 | |||
0aceb0e86b | |||
dc06506276 | |||
7947d91c3a | |||
1f2a1e79b7 | |||
6722704859 | |||
96bd29c06a | |||
c6240fb793 | |||
74ca7dcd6c | |||
b1cabe9fbb | |||
6bb1dcc3f1 | |||
b3f75d3c53 | |||
4d26f504f8 | |||
4bc2a95c42 | |||
c382ffbf46 | |||
6fa4f64e0b | |||
6d601aa79f | |||
4105b567ee | |||
4fbd0970ed | |||
be782c1ebd | |||
1982b0fc6b | |||
ff88f484f6 | |||
e5c970bea1 | |||
653c44dbf6 | |||
9213d18c90 | |||
7c19dd366f | |||
39c1fbb29b | |||
53124007c2 | |||
fdc6062433 | |||
bb3cd40012 | |||
e9c6a493eb | |||
24ba77d1f6 | |||
a3c26a202d | |||
e9a6e414c1 | |||
aa5f3c9b6c | |||
1cf0a8920a | |||
ba35b022c6 | |||
f68c7fd7bd | |||
e942f0b9e8 | |||
ac6cdb0d76 | |||
16723acd97 | |||
b31a2d43bb | |||
be6f4b027f | |||
1e93dd22ce | |||
64b2eb0f1b | |||
a80c017206 | |||
913eb9f9bf | |||
d9175b83a2 | |||
f6d112e38f | |||
40129f87e3 | |||
e375ddf6f5 | |||
51110e54b7 | |||
7d2f76e2dd | |||
![]() |
5590c81be5 | ||
2c25eb2a43 | |||
ece0054da6 | |||
09e9cccc51 | |||
85a315eac7 | |||
3349255d9a | |||
c4be361f95 | |||
432fd39df2 | |||
b31add8d74 | |||
e659c8daf2 | |||
62ab354674 | |||
8285825c8b | |||
e1ebb0ac2a | |||
1aa575b13c | |||
0f2e874a4a | |||
c9578ae9f1 | |||
a4a7391de6 | |||
495944a3b6 | |||
477848b77f | |||
a3289cc81d | |||
4fd38e75c3 | |||
5a09d7a79a | |||
b912ae0b08 | |||
eacd68ce88 | |||
2251494a48 | |||
5b092c7f7a | |||
4d78bbf31e | |||
1c4e607d3d | |||
80de6da382 | |||
3edff1550c | |||
724b89f361 | |||
06a09fbc3e | |||
2c2696d75f | |||
36d51749b3 | |||
ac84bb222d | |||
7c87f25f25 | |||
a2123f8765 | |||
2078b45566 | |||
a7a00a38d8 | |||
a5275388e2 | |||
b5704d5746 | |||
ee038c6c20 | |||
827dea443c | |||
3cf7f6c07c | |||
408e98e46b | |||
2878eac81d | |||
60a26b3ad0 | |||
4c8a127ad3 | |||
3f993975eb | |||
68e7e4aec0 | |||
e7031baf0b | |||
7d3659ebce | |||
a1e254ceb0 | |||
0f1c2f3e85 | |||
6496da9b7e | |||
8699d89694 | |||
fb947cc777 | |||
fe5542a521 | |||
47bb893c49 | |||
0485412b52 | |||
ab2f3df4ea | |||
df92b4c58e | |||
88e6bd27b2 | |||
b640786f62 | |||
1c3a367b18 | |||
8a2a4c4760 | |||
e585b3e2d4 | |||
94601cbf4f | |||
905b56beb2 | |||
1337320803 | |||
1e3389acf4 | |||
74005364e2 | |||
1cea720a70 | |||
eeb0d0bc19 | |||
9d47a18325 | |||
81f196eed2 | |||
8adaf4bdb9 | |||
daf7db76b6 | |||
38b03fe026 | |||
e2582447cc | |||
6babec9d6b | |||
a5dce5b55d | |||
babfbe4e38 | |||
c9f03a5e23 | |||
f9f35da4d4 | |||
dd3ebc16df | |||
3120a5ea93 | |||
8571ec4d00 | |||
1c6d306760 | |||
9262d83fd5 | |||
b682f8fc34 | |||
e1720c5bcf | |||
a78581ee69 | |||
4eb2d4ae06 | |||
fdd5c74df5 | |||
1857712df4 | |||
c5ebf36274 | |||
8c2d403502 | |||
ec92b9605f | |||
c0e8f92e84 | |||
fe4d0d562e | |||
58e9bdc4ab | |||
638b172e77 | |||
79295de4ce | |||
37aa3ea406 | |||
1c9e77f890 | |||
da153a7900 | |||
7903a38f61 | |||
a99f1c396f | |||
5a5526df63 | |||
46c5a91032 | |||
e9ba3cff96 | |||
b4ace286e3 | |||
7aeb8edfc2 | |||
31792ebecd | |||
7dcd82e7b9 | |||
fc4d74b7ac | |||
98ff88ffff | |||
a3fa1a558e | |||
bf6fa1c044 | |||
8a185cb048 | |||
4d6239b606 | |||
78445aa4dc | |||
4a9f5a7fd8 | |||
e1d2d3e9ff | |||
7ee1e2b745 | |||
![]() |
442d081f9d | ||
![]() |
36da5ec3a1 | ||
![]() |
f49ba40cb9 | ||
a886363603 | |||
dee6ed1025 | |||
d8d67ac4c0 | |||
![]() |
b400c8d64d | ||
![]() |
98e2c6c837 | ||
a947432134 | |||
1d0f4172fb | |||
06972c2bd1 | |||
eccd60c340 | |||
c1f9249252 | |||
c14f4358f3 | |||
2dae3a3abb | |||
7d6046624c | |||
de4016100a | |||
3ed38eec6a | |||
7ef4437447 | |||
9c662177ec | |||
d6540f1351 | |||
1b31859786 | |||
ca857d70eb | |||
eaa52e40a6 | |||
5ba8744f6c | |||
92b3907a8e | |||
a95e7e3e0d | |||
7a8ef89f09 | |||
3b022d68a4 | |||
eaa33e3b7b | |||
ea78f08710 | |||
6fa089a854 | |||
ba73fddba5 | |||
ff0ab03630 | |||
7ba17435ac | |||
5852e987c6 | |||
9b4273e370 | |||
4e6b9ea086 | |||
caa56cedb8 | |||
701be4c351 | |||
2ba05aa633 | |||
f00e6eed1e | |||
81592beb64 | |||
ebdc212afb | |||
b880e7a13d | |||
ce10ac7f7b | |||
9012a161c3 | |||
9038829f24 | |||
116bd84394 | |||
69d7d3c632 | |||
400e904aa9 | |||
27f4dc2084 | |||
2ac75fd24e | |||
8ddff1cf94 | |||
b1428867dc | |||
f2044bcfbf | |||
b5bc2bd851 | |||
b45a7cdd6c | |||
ac31e5e884 | |||
43b5781cbd | |||
3fa72f86d6 | |||
8942a98431 | |||
8bb0f68049 | |||
d3743e206c | |||
4fe98d32a1 | |||
1fc0597f93 | |||
0f57fba805 | |||
2f74ceb013 | |||
009b171946 | |||
895bfe1ac8 | |||
3033382f64 | |||
fd21f2faae | |||
5e1f41b48a | |||
b98901c75a | |||
9b50e5b078 | |||
d9ac6d01d0 | |||
57fbb16bd8 | |||
13a8c94bcf | |||
0d6e2076d6 | |||
f6b04faea6 | |||
eedac13818 | |||
d45c21cf24 | |||
54a1515bcb | |||
4477ef84bf | |||
4370f40886 | |||
fe2466b49a | |||
a8b54493f7 | |||
2ee54f77ae | |||
187578861b | |||
a0420cd10f | |||
2f1a0f6e41 | |||
6d0a6c645f | |||
55d34f833d | |||
a0e9a83ddd | |||
9a35227728 | |||
af69d66ea9 | |||
2487e56847 | |||
5b264b64fd | |||
ce7a0034be | |||
a8a13b539c | |||
b2975e053c | |||
41fcb8706f | |||
3fc6f19227 | |||
3e78e0b5d2 | |||
020f99c5b2 | |||
3989eeba45 | |||
412c72f624 | |||
30c0e20eae | |||
53bc51a3af | |||
f95b769f11 | |||
5d8e195635 | |||
a60d0649c0 | |||
377d2bdd71 | |||
64fec9583d | |||
9b6637a697 | |||
af096d38b8 | |||
7eb960c711 | |||
a98c1cbc92 | |||
53970f97a1 | |||
9193df71b7 | |||
d180a01312 | |||
7f13b67f70 | |||
c9360c6651 | |||
489e10b76e | |||
28be18de01 | |||
c03a8011c2 | |||
06731f4d4b | |||
44de5bfd1a | |||
a00d43a59e | |||
9783e55069 | |||
3b101e177e | |||
08b10b4e4a | |||
65d5ee120b | |||
2fc9b11aa6 | |||
feb85625c5 | |||
e1d4df74b9 | |||
6eaeab5c24 | |||
b93c6cd48b | |||
94afeb406a | |||
9e623b645f | |||
4f92b782e9 | |||
16427b347f | |||
ea6add25ca | |||
89e3704f33 | |||
736356fe71 | |||
89dc45ac7c | |||
c3d4250094 | |||
5357ec167a | |||
7824de743f | |||
adce996aae | |||
2e334ec12e | |||
641c2070a4 | |||
86916d2ed9 | |||
c157116680 | |||
1a8c93627a | |||
ee5f503e42 | |||
c4c7d5480b | |||
06cb5095b2 | |||
f9ddd3b95e | |||
740f392c95 | |||
be336e6e06 | |||
2318092542 | |||
3964f081de | |||
ec451f633b | |||
5a7165d142 | |||
94183f1e60 | |||
8300e1dba4 | |||
d413cb49b4 | |||
7babc6bad7 | |||
7e3a6bdf45 | |||
bd51f1d8e1 | |||
fac76f9328 | |||
8285eee3c1 | |||
a36dd8e257 | |||
1e7061efae | |||
072110f282 | |||
3ecd35c4a6 | |||
b71366bac7 | |||
c6f614b106 | |||
a60e7e5d9e | |||
ad6cb7e430 | |||
e95275fa35 | |||
099e95bfd3 | |||
3c57828f3a | |||
6a4a94f486 | |||
27a0a0f5d7 | |||
dac6e7c5b1 | |||
80450467fc | |||
ff3cbd88dd | |||
c411f596f9 | |||
a0933153ba | |||
f03c0ef913 | |||
f3a79a3523 | |||
f18cf9b859 | |||
bf66ef6354 | |||
330323f0c2 | |||
b82f00748a | |||
0f7e74a5e2 | |||
4db50b12ca | |||
abe59228be | |||
3842d1bea8 | |||
4f1154f412 | |||
ce440ae7f7 | |||
124ea0ae30 | |||
4327651e1d | |||
f1e45f01bb | |||
5e6e3e30ba | |||
266971f5d3 | |||
ed3b631b30 | |||
7389131bdc | |||
f9302f50ee | |||
13bcbbbb15 | |||
27f4b24344 | |||
9a7cc6de38 | |||
15e31d60e6 | |||
528a9c93a4 | |||
7823a0a9b5 | |||
193fcb16c2 | |||
646c4babaa | |||
f2e62d6602 | |||
8d5f88a488 | |||
c7af90896b | |||
b3bb9ba28c | |||
54e7bb77f6 | |||
78a65f906e | |||
9f8e930a45 | |||
5f91e9be12 | |||
e575f41a19 | |||
13cb1da59d | |||
05874f90e5 | |||
25876bd81f | |||
96c57b0daa | |||
af395bf4ac | |||
611c70a1ff | |||
9730b639b9 | |||
de6b70a5b1 | |||
e7f65d50b0 | |||
855cf6adef | |||
5e82c58873 | |||
4b63596c3a | |||
8734f3f24a | |||
c0e42480e0 | |||
0742f17f2a | |||
12ce97c385 | |||
c36d7fb71f | |||
27e9a328d4 | |||
83efe8a156 | |||
ec278ade7b | |||
e38e3fb088 | |||
9cdd9e85ed | |||
775ffb924d | |||
efd35cd273 | |||
4144679db7 | |||
f7b1461cc5 | |||
93ea20cf25 | |||
13d7b821be | |||
8db76f49ae | |||
bc74515095 | |||
672a3637de | |||
91b73186d3 | |||
a18229afea | |||
c24166b36d | |||
40edee2b83 | |||
671007bb79 | |||
bdee21a66f | |||
6f3153a816 | |||
a69603a10c | |||
28435522de | |||
da08bb7bb9 | |||
![]() |
126169e37c | ||
0eb897b07f | |||
38288153ed | |||
9227518d1e | |||
bab0a3bba2 | |||
85ba0c14be | |||
ca7b7ad3d5 | |||
87d785df45 | |||
34115728fe | |||
7ed1cab718 | |||
ecf12aa83f | |||
ffbb560598 | |||
eb389d34c7 | |||
c6d55cd1fa | |||
db557015fd | |||
2f4b95313f | |||
e70c152494 | |||
9d7f14cddb | |||
dcdaaa11c2 | |||
3a1d2ab1ba | |||
ba654438ab | |||
4401ba6847 | |||
0777faaa5b | |||
577b3f018a | |||
![]() |
9b5fb5499c | ||
6ab285bc89 | |||
af30097c6e | |||
a0e1e34974 | |||
09a472b956 | |||
1fec9c1f2a | |||
1c4b94faaf | |||
3602cb4a00 | |||
e6fd3a800e | |||
a8ad7746ee | |||
4aace2a3ae | |||
222fe3c1a0 | |||
659fb12db1 | |||
8593240692 | |||
34e97347f2 | |||
7c0adf8f00 | |||
7b7a25f5e9 | |||
4e6b63df50 | |||
0df9166a13 | |||
2db49f85f0 | |||
4947151d00 | |||
44d7d431e3 | |||
72f70d990d | |||
1f2f940374 | |||
c208e6dc6c | |||
0eceab3d9b | |||
f4703ef7a0 | |||
50d1c93f94 | |||
ecfbf7032b | |||
d9bdd5c578 | |||
277eb08cba | |||
18e81f71ba | |||
82ac8e5d02 | |||
e4a8357715 | |||
3a890168df | |||
02681a135e | |||
b345d655f1 | |||
7f6e21c96e | |||
32975494f3 | |||
ad06da549f | |||
ba9088f43f | |||
016239b014 | |||
66d4926bba | |||
7b62e47fd2 | |||
96b91b2298 | |||
61ee6aeae0 | |||
bbefd3331a | |||
01477b97c5 | |||
1751f81cce | |||
cab0211e09 | |||
2d5b79ceb1 | |||
6b915a5b4c | |||
1f0cb81105 | |||
dbb008772e | |||
0fa8286d08 | |||
5146957ea1 | |||
b183e1925d | |||
158809732c | |||
7633675b33 | |||
f8e6340fda | |||
4a02689c71 | |||
7899efcb9e | |||
2c67b77156 | |||
dc7b5a9fb2 | |||
97a930ccb3 | |||
6ec6c07ad7 | |||
3c90c76796 | |||
0b0ca9912d | |||
fabcdf185a | |||
ad6eeb46d5 | |||
6d43cb47a7 | |||
ca9f0ba456 | |||
bc0d6646e4 | |||
8c59024b96 | |||
ecdc0767b8 | |||
f61dc98fa6 | |||
c985f1fa0a | |||
ce88ae603b | |||
1e82f18bc9 | |||
d8558a4398 | |||
7c77daabfa | |||
81a31c7b1f | |||
f3f2cc482c | |||
e8bc5348a8 | |||
fe31fd2c97 | |||
f6bbc31551 | |||
8821eb0ae0 | |||
c497c0fb26 | |||
ed9e1a6d05 | |||
1fa024546a | |||
b3548bc35f | |||
5de04579b7 | |||
80b3e49cb8 | |||
68d100458f | |||
3765d21648 | |||
e7ed4794e2 | |||
f181bf2696 | |||
1704575cfb | |||
905c85a99c | |||
c8b3ac5d99 | |||
5d9fb71753 | |||
1863a76a4c | |||
8865723dd4 | |||
eb05661e2b | |||
64e556f37a | |||
a20ce41f73 | |||
9a583d0284 | |||
5ec537e142 | |||
243db3edaa | |||
3723f775bd | |||
83ecd79ec0 | |||
9c77777577 | |||
832bb12829 | |||
d6012d88dd | |||
d01d28fbf3 | |||
f44f0b38b2 | |||
d04e03200b | |||
92bcb33171 | |||
41e542dc7a | |||
c4740f7d61 | |||
e8c69d1489 | |||
5062d774b7 | |||
7de923f90c | |||
7b03d9f020 | |||
4ff438655d | |||
4ece37fdf4 | |||
15bd7d40db | |||
cf34039f5a | |||
f0a7316eef | |||
ee454527af | |||
5af61c3e76 | |||
e8ba073de2 | |||
0c03995f43 | |||
5f0df92442 | |||
1e16ff9483 | |||
7c8fd0cd45 | |||
bb2db7b64a | |||
2631a8f577 | |||
58d1829c9c | |||
1a4931e283 | |||
b2433e9057 | |||
1b214a1604 | |||
b8d2e362bf | |||
9f4f240493 | |||
3aa5a140d1 | |||
00868b708b | |||
eead1f8775 | |||
cfadf1e369 | |||
fe31aa36bd | |||
d6cf95b345 | |||
3ff02c442e | |||
ca6f13196d | |||
69888245bf | |||
4a2fd7943a | |||
3e86686613 | |||
2bd104fc2f | |||
334ec48d3e | |||
ba5d038441 | |||
93aababfc4 | |||
daccf5e6de | |||
a6dbf1bb4b | |||
34a9a843a4 | |||
8e88761547 | |||
1bb01be01d | |||
dbae10a4e5 | |||
256ca14284 | |||
ac2e1117a7 | |||
1b8fbf78bf | |||
92e611f49a | |||
5ff6680cee | |||
aeef44d3d9 | |||
2e8c530ba9 | |||
c6cbe96e03 | |||
45a1938aea | |||
f55b70585a | |||
ff943d3fc2 | |||
a5672a16ff | |||
8df6270287 | |||
70bce6eb72 | |||
75e386d2a7 | |||
05c06bffd2 | |||
a7bc9fa21a | |||
8bd2382973 | |||
faf3d55d8d | |||
59d9d313f8 | |||
83e403cc54 | |||
a80fd75ec3 | |||
fc1694198b | |||
9ae992dc89 | |||
6297f9689d | |||
172a5109e9 | |||
e881ec98ec | |||
6c9f905c2b | |||
0105ef9010 | |||
fe568f032e | |||
2766b2dcfc | |||
2e37432990 | |||
38ad7ab669 | |||
27edbe4652 | |||
11abc2d1b3 | |||
119ad684ed | |||
accdf058c6 | |||
8d865b572d | |||
3762dac556 | |||
8f35bfb03b | |||
3b09a47fb8 | |||
b687cf7727 | |||
d5f55e0f28 | |||
95a7588726 | |||
f72db8bf2e | |||
02d45ede0f | |||
ffd6cde177 | |||
f73b180329 | |||
b5af648512 | |||
66879f229a | |||
dad5c06a1b | |||
0c5abb4658 | |||
![]() |
0c09c5bd3b | ||
4a33238d7e | |||
3b18896564 | |||
84e094fc19 | |||
639dfeeb68 | |||
f60f99fc73 | |||
95572825c3 | |||
7ef118955d | |||
c3114ab3f9 | |||
e25baeae9c | |||
4afc6deab8 | |||
d15f23a3c1 | |||
aa4ca1b212 | |||
d0fe3d6acb | |||
be16fbfd72 | |||
8c247adede | |||
def32aa559 | |||
1e9533a9a5 | |||
debc806516 | |||
91631c6c1d | |||
360d37cd36 | |||
d758957d68 | |||
7456bf903e | |||
239b039e2a | |||
7c6c633362 | |||
575e0cf6fc | |||
31fb594904 | |||
a592cd08cf | |||
ebb254061d | |||
f2239e75a9 | |||
0764d2793d | |||
4db58b0a3a | |||
ca2976f314 | |||
688d4963a5 | |||
84dc76800b | |||
190a5c81e3 | |||
b096dbe7c6 | |||
8b5abfe022 | |||
4db44324e4 | |||
1d21145d95 | |||
ac7169cfe3 | |||
ca3baeebdd | |||
b92f68e697 | |||
a6b77826d5 | |||
ce591bd9a7 | |||
d71ce372a3 | |||
89a2cd3684 | |||
f6f4bd673d | |||
2064d40bc9 | |||
ace15e3248 | |||
a586b7c23f | |||
8cdcff4660 | |||
a56d8eb11b | |||
0a35ade427 | |||
7a98e9c8bf | |||
aa489da1dc | |||
c49d2720f2 | |||
0f8557b09f | |||
c9bc48ad88 | |||
4c68780e87 | |||
b3a4125253 | |||
1309e2ab8f | |||
97f68c39bb | |||
3c375f6a7c | |||
92c9ae3063 | |||
be6937156f | |||
92fdc2ff57 | |||
98b49b80a1 | |||
840e3bfb4c | |||
4716a3a39e | |||
f4b2a68121 | |||
c773406464 | |||
25536a9b07 | |||
870ad0e52e | |||
dbe48f9624 | |||
fd18d9d150 | |||
83bc9a4b58 | |||
4b02143a44 | |||
9dda6f1e09 | |||
3e67209ae8 | |||
6cf7d3e223 | |||
7453b1f1ad | |||
8a935345c5 | |||
2e9143c220 | |||
5ba7476685 | |||
cde58c21a4 | |||
145e22de21 | |||
a20e0004d2 | |||
c0e2b7731a | |||
2f974727d6 | |||
c91e01e13d | |||
137793cd77 | |||
27636a4a72 | |||
d86c9728e3 | |||
7de4e4f1be | |||
3733eaea45 | |||
c9c70d9be6 | |||
54d421fb4f | |||
14e87fbe63 | |||
46eb8e6609 | |||
aa8f10b6a7 | |||
b391867f14 | |||
5f5386765d | |||
b193fadd72 | |||
b6bff48cd0 | |||
1fd9adc8be | |||
b74d64b7ad | |||
02764be097 | |||
6af7be9572 | |||
ed182b42f9 | |||
87d2a41193 | |||
ff7322a5de | |||
66d2f0fb7e | |||
062d00098e | |||
a96052ce27 | |||
0f07c61ad6 | |||
2157a23d9f | |||
3286121461 | |||
6361cb333e | |||
30ab79eae3 | |||
c9c6481b3f | |||
e3e68bf696 | |||
2edda2b710 | |||
24dcfdb2cc | |||
2408e89940 | |||
547da8ab60 | |||
c1cc02c217 | |||
0b4cacf417 | |||
9bab66ab53 | |||
02073e2247 | |||
4aff088de0 | |||
475739e9e9 | |||
a5f36ea692 | |||
6537f9a635 | |||
e332682fd2 | |||
9f24bf6384 | |||
b7ac95d8ea | |||
14145d6f70 | |||
db5c294ab9 | |||
6591ee7074 | |||
3f07d2355d | |||
b1a7a63987 | |||
7e75b5a812 | |||
d8a8b7e27c | |||
2d67c88fc8 | |||
87a16c69b6 | |||
![]() |
937d540db5 | ||
61de14a449 | |||
7f37d5e396 | |||
c7869d6819 | |||
ab6b28f220 | |||
7cc1dfd06a | |||
d4191917f6 | |||
c41bc5b791 | |||
853eabc898 | |||
8aa0005427 | |||
0b9c5f0c5e | |||
0e324a349d | |||
f7b4934f6b | |||
533521239f | |||
05df3518ac | |||
e0df993bad | |||
4c88348ddf | |||
1308d7a115 | |||
aca13b53eb | |||
f105bb2eb7 | |||
40972575b6 | |||
98fe04291b | |||
ddb9a47f59 | |||
36c5a8a586 | |||
c8b5e82b12 | |||
2fb4b62b8d | |||
51ef1508bd | |||
85fd0e2e01 | |||
94f11d73cd | |||
302b33d48b | |||
9cdfd205bf | |||
64d178a53a | |||
a5ba2423d5 | |||
bbd45ab9f9 | |||
ef755b3217 | |||
4bd770d078 | |||
4db4d98f0b | |||
453e63da2e | |||
01be6b6252 | |||
bafc0a085d | |||
79c64726e2 | |||
7d01adb54b | |||
436ce3d88d | |||
d0593958e7 | |||
0a069e3f3a | |||
83d90a8110 | |||
a268b5acc6 | |||
bcdf4f9165 | |||
8f733eba64 | |||
8515627f82 | |||
f4b86735fb | |||
166dd70ef9 | |||
541078bda8 | |||
cfc9501554 | |||
04c2325ade | |||
6a0491c419 | |||
60854fc9df | |||
df8b345ddf | |||
e650915bb9 | |||
ed2f98e558 | |||
1f7a4a0778 | |||
c179b2e478 | |||
c0c64de20d | |||
82deec7a81 | |||
1972c4f44a | |||
43a56c5360 | |||
604fc27a59 | |||
f0504e12ec | |||
7f9840f765 | |||
b1a0e91dd9 | |||
097f146499 | |||
11209fa8d7 | |||
bd5bf9ebe0 | |||
28fd89f22d | |||
dddfa3cc47 | |||
5ffdea15de | |||
6fe9d097a8 | |||
29a40ab83d | |||
fcd53969f1 | |||
1bd57f6fef | |||
7c06504281 | |||
c9dbcc24e3 | |||
83abad910a | |||
65abb93ee5 | |||
924e581fbb | |||
44573236f7 | |||
0d5660726d | |||
2775e277fd | |||
9c1250ac22 | |||
80dfe4c915 | |||
285e90494d | |||
3e19d03cd1 | |||
04e7dc82af | |||
e8e617658c | |||
df3ee0331c | |||
d3ae29ffdd | |||
5d1a539e9b | |||
30f60fd304 | |||
ca8a0af14e | |||
e0b915faec | |||
5aa9eb1392 | |||
320063ad1d | |||
ec70b2e648 | |||
084535597d | |||
5ef2cd92bf | |||
573d8c76d3 | |||
e7068f0f97 | |||
3cfc3fccf5 | |||
0d3052e659 | |||
31b28f2945 | |||
f6d756f92b | |||
92dedd182e | |||
5f487d9409 | |||
94a0b66c12 | |||
28edbcc135 | |||
f1982dd69e | |||
08e87edb71 | |||
ba47a7f62d | |||
9bc4621962 | |||
e74afcf112 | |||
44fa579390 | |||
c7682acbd2 | |||
821629eb5a | |||
32020e0d84 | |||
35eb144ce8 | |||
a000958f99 | |||
3fbb73e3e1 | |||
00f92f0eb9 | |||
bf796ed61c | |||
2b524dcb13 | |||
79bc3fe593 | |||
c4dfca7c62 | |||
ecbef69860 | |||
fffd5eb872 | |||
0283ad1bc2 | |||
871fe130af | |||
0266da168f | |||
7659507e9e | |||
4e3b7f7aee | |||
0eebb269f0 | |||
2596bef850 | |||
e502256bed | |||
99bdbc41ba | |||
4d19c93814 | |||
7e30b4ab1a | |||
349f3589ff | |||
049d1ee294 | |||
a3dcc8fbd7 | |||
f87ecc0e45 | |||
1fda56bd7e | |||
5858dff783 | |||
b87aad537a | |||
44c6e22dc8 | |||
ae04570d40 | |||
796918e07e | |||
848bded217 | |||
52888517ee | |||
4558db2d5e | |||
aecd998301 | |||
4bf9cb4c5c | |||
95a6324e18 | |||
22c140dcbd | |||
088e4c9010 | |||
9e6d39abb4 | |||
fc3697217a | |||
795d121422 | |||
543f7b61e7 | |||
f8ee2cf3e5 | |||
4fde95bbf6 | |||
4906799170 | |||
da36ad57be | |||
0efc455cf9 | |||
f393a99dc5 | |||
4ea98b1540 | |||
934009a6c0 | |||
141c7709b3 | |||
ceb7de458e | |||
0f4dfcef20 | |||
c3e9d1b179 | |||
79fb080e9f | |||
28474ad477 | |||
bfd826a0ea | |||
feef8e9f73 | |||
6e5e1b99e2 | |||
9ff1f617b8 | |||
1a460afaa2 | |||
130d3d1569 | |||
5d3d2a06d7 | |||
0fbd38db9f | |||
2e625a6392 | |||
ea5a70e03d | |||
20ac966a61 | |||
c3435f7aac | |||
c0dc768588 | |||
f904b43777 | |||
01b5e7d46f | |||
ae82ef5d95 | |||
9b0e67a12a | |||
d4214f7f34 | |||
6b55a2d0de | |||
c4ccdaf1aa | |||
2d72f6a87b | |||
34668ad54e | |||
88482f5ba8 | |||
2b27f369bd | |||
f22364b2f9 | |||
cbac701e7e | |||
2912ca8b4b | |||
7acb695877 | |||
c9c6d87803 | |||
59e76de85d | |||
44a9a32775 | |||
3999bcc171 | |||
30f16cf3e8 | |||
04ec2586a5 | |||
01cfdb0f72 | |||
b60ef15c91 | |||
a035304803 | |||
d05d4deb03 | |||
9080a177f1 | |||
f40a45c5f1 | |||
e28f346447 | |||
7a1086108c | |||
de20821b98 | |||
3baaf8dd2b | |||
4a3d044f02 | |||
145be2a894 | |||
26fbacf695 | |||
6ff0f3cbd3 | |||
2572cd5876 | |||
d8c21ac9cc | |||
5a25d0046b | |||
2467ee77d2 | |||
0c211f4055 | |||
bdcf7c8fe4 | |||
b81eae42d7 | |||
64d8850d03 | |||
cdd086bb98 | |||
8903a3ca8f | |||
e4e53b0dd3 | |||
921555438c | |||
847303b6ae | |||
f2caf930d2 | |||
b2165c9eff | |||
40774e5989 | |||
1eaae35649 | |||
0539e508ef | |||
47718ecfe9 | |||
3794bf3e18 | |||
97b9076745 | |||
a674a0f3b7 | |||
01f0ae6254 | |||
db55b30106 | |||
9c7ba07017 | |||
f83b39bc88 | |||
2c4ee3a912 | |||
a5ece6f187 | |||
629da37bec | |||
1ad2d1bac7 | |||
2049b14bda | |||
68b7cae0d5 | |||
12995a3982 | |||
4374a210d9 | |||
e1d3d3187a | |||
054715410d | |||
e25444c6f0 | |||
959e17f673 | |||
008f0044e3 | |||
424769d1d5 | |||
e693de128f | |||
4272b5a199 | |||
1a8d808a7b | |||
b894475d62 | |||
f71e4c16e0 | |||
905ad5b82f | |||
544cbd0bc6 | |||
164ef1d378 | |||
97cfbd3835 | |||
37ddae06d5 | |||
6cc413a529 | |||
4dda3c6d40 | |||
c24a04711f | |||
14f3d56441 | |||
6da822fc80 | |||
325247bd75 | |||
f7998c3f15 | |||
562456c59e | |||
57bed80d1c | |||
bb2151b999 | |||
cef1d89f8d | |||
ae2f87962e | |||
c9b5cc7227 | |||
c6c44cb981 | |||
dffa775658 | |||
d1d91b74e3 | |||
613f2d250f | |||
761148f350 | |||
a0f2d645e5 | |||
388c0ae3d0 | |||
17bbdadbb4 | |||
6ab0343834 | |||
6abe48ba5a | |||
401cb4296e | |||
aed2ab6e04 | |||
f5833dd103 | |||
3008ea8813 | |||
8b1722cedb | |||
4867faab16 | |||
42f971ab07 | |||
ba33419c67 | |||
e4d076bd47 | |||
27b324b3be | |||
83e4c02f16 | |||
f79e1e0e65 | |||
c187fa43a0 | |||
b3facdcaae | |||
0ab224b987 | |||
a1c45e9bd5 | |||
30a332c1c4 | |||
820d5dafc8 |
47
.appveyor.yml
Normal file
|
@ -0,0 +1,47 @@
|
|||
version: 0.2.7.build{build}
|
||||
|
||||
image: Visual Studio 2019
|
||||
|
||||
clone_folder: C:\projects\welsonjs
|
||||
|
||||
environment:
|
||||
ORGANIZATION_ID: 8ee5ec82-36dc-4619-ac33-5bc1117428fe
|
||||
PROJECT_SLUG: welsonjs
|
||||
ARTIFACT_CONFIGURATION_SLUG: initial
|
||||
matrix:
|
||||
- CONFIGURATION: Debug
|
||||
SIGNING_POLICY_SLUG: test-signing
|
||||
- CONFIGURATION: Release
|
||||
SIGNING_POLICY_SLUG: release-signing
|
||||
|
||||
before_build:
|
||||
- nuget restore WelsonJS.Toolkit
|
||||
|
||||
build_script:
|
||||
- msbuild "C:\projects\welsonjs\WelsonJS.Toolkit\WelsonJS.Toolkit.sln" /verbosity:minimal /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" /p:Configuration=%CONFIGURATION% /p:Platform="x86"
|
||||
|
||||
after_build:
|
||||
- cmd: mkdir artifacts
|
||||
#- cmd: xcopy /s /y WelsonJS.Toolkit\WelsonJS.Toolkit\bin\x86\%CONFIGURATION%\* artifacts\
|
||||
- cmd: xcopy /s /y WelsonJS.Toolkit\WelsonJS.Service\bin\x86\%CONFIGURATION%\* artifacts\
|
||||
- cmd: xcopy /s /y WelsonJS.Toolkit\WelsonJS.Launcher\bin\x86\%CONFIGURATION%\* artifacts\
|
||||
- cmd: nuget pack WelsonJS.Toolkit\WelsonJS.Toolkit\ -properties Configuration=%CONFIGURATION% -properties Platform=x86 -OutputDirectory artifacts\
|
||||
- cmd: 7z a artifacts.zip artifacts\*
|
||||
|
||||
artifacts:
|
||||
- path: artifacts.zip
|
||||
name: WelsonJS.Toolkit
|
||||
|
||||
deploy:
|
||||
- provider: Webhook
|
||||
url: https://app.signpath.io/API/v1/%ORGANIZATION_ID%/Integrations/AppVeyor?ProjectSlug=%PROJECT_SLUG%&SigningPolicySlug=%SIGNING_POLICY_SLUG%&ArtifactConfigurationSlug=%ARTIFACT_CONFIGURATION_SLUG%
|
||||
authorization:
|
||||
secure: nABfe+lksIRfS7lVnkq3EdIYNH8KOka+5ZFTUJqUW2nckBubhrlAXarbCPJHLp9FWWf3zxAWhNwT0It+qyqPRQ==
|
||||
|
||||
notifications:
|
||||
- provider: Email
|
||||
to:
|
||||
- abuse@catswords.net
|
||||
subject: 'Build #{{build}} {{status}}'
|
||||
on_build_success: false
|
||||
on_build_failure: true
|
2
.github/FUNDING.yml
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
github: gnh1201
|
||||
custom: ['https://gnh1201.link']
|
8
.github/workflows/codeql-analysis.yml
vendored
|
@ -32,7 +32,7 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'csharp', 'javascript', 'python' ]
|
||||
language: [ 'javascript' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Learn more about CodeQL language support at https://git.io/codeql-language-support
|
||||
|
||||
|
@ -42,7 +42,7 @@ jobs:
|
|||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
|
@ -53,7 +53,7 @@ jobs:
|
|||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
@ -67,4 +67,4 @@ jobs:
|
|||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
uses: github/codeql-action/analyze@v2
|
||||
|
|
23
.github/workflows/llm-code-review.yml
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
name: AI Code Review
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
issues:
|
||||
types: [opened, reopened]
|
||||
|
||||
jobs:
|
||||
repofix:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Run RepoFixAI
|
||||
uses: Manav916/llm-code-review@main
|
||||
with:
|
||||
groq_api_key: ${{ secrets.GROQ_API_KEY }}
|
||||
groq_model: 'llama-3.3-70b-versatile'
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# exclude_extensions: 'txt'
|
||||
repo_owner: ${{ github.repository_owner }}
|
||||
repo_name: ${{ github.event.repository.name }}
|
||||
event_number: ${{ github.event.number || github.event.issue.number }} # when listening for both pull requests and issues
|
||||
event_name: ${{ github.event_name }}
|
15
.gitignore
vendored
|
@ -103,8 +103,13 @@ dist
|
|||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Custom directories and files
|
||||
bin/
|
||||
packages/
|
||||
config.xml
|
||||
staticip.xml
|
||||
# user private assets
|
||||
bin
|
||||
data/*-apikey.txt
|
||||
data/*.nomedia.txt
|
||||
app/assets/img/_templates
|
||||
app/assets/img/_captured
|
||||
settings.ini
|
||||
defaultService.js
|
||||
lib/*.private.js
|
||||
data/python313.zip
|
||||
|
|
9
.gitmodules
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
[submodule "app/assets/tessdata"]
|
||||
path = app/assets/tessdata
|
||||
url = https://github.com/tesseract-ocr/tessdata
|
||||
[submodule "app/assets/tessdata_best"]
|
||||
path = app/assets/tessdata_best
|
||||
url = https://github.com/tesseract-ocr/tessdata_best
|
||||
[submodule "app/assets/tessdata_fast"]
|
||||
path = app/assets/tessdata_fast
|
||||
url = https://github.com/tesseract-ocr/tessdata_fast
|
3
.sonarcloud.properties
Normal file
|
@ -0,0 +1,3 @@
|
|||
sonar.issue.ignore.multicriteria=e1
|
||||
sonar.issue.ignore.multicriteria.e1.ruleKey=javascript:S3504
|
||||
sonar.issue.ignore.multicriteria.e1.resourceKey=**
|
11
CITATION.cff
Normal file
|
@ -0,0 +1,11 @@
|
|||
cff-version: 1.2.0
|
||||
message: "If you use this software, please cite it as below."
|
||||
authors:
|
||||
- family-names: "Go"
|
||||
given-names: "Namhyeon"
|
||||
orcid: "https://orcid.org/0009-0006-8421-0911"
|
||||
title: "WelsonJS"
|
||||
version: 0.2.7.12
|
||||
doi: 10.5281/zenodo.11382384
|
||||
date-released: 2024-05-29
|
||||
url: "https://github.com/gnh1201/welsonjs"
|
70
CODE_OF_CONDUCT.md
Normal file
|
@ -0,0 +1,70 @@
|
|||
# Code of Conduct - WelsonJS
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to make participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, or to ban
|
||||
temporarily or permanently any contributor for other behaviors that they deem
|
||||
inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at <abuse@catswords.net>.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant](https://contributor-covenant.org/), version
|
||||
[1.4](https://www.contributor-covenant.org/version/1/4/code-of-conduct/code_of_conduct.md) and
|
||||
[2.0](https://www.contributor-covenant.org/version/2/0/code_of_conduct/code_of_conduct.md),
|
||||
and was generated by [contributing-gen](https://github.com/bttger/contributing-gen).
|
147
CONTRIBUTING.md
Normal file
|
@ -0,0 +1,147 @@
|
|||
<!-- omit in toc -->
|
||||
# Contributing to WelsonJS
|
||||
|
||||
First off, thanks for taking the time to contribute! ❤️
|
||||
|
||||
All types of contributions are encouraged and valued. See the [Table of Contents](#table-of-contents) for different ways to help and details about how this project handles them. Please make sure to read the relevant section before making your contribution. It will make it a lot easier for us maintainers and smooth out the experience for all involved. The community looks forward to your contributions. 🎉
|
||||
|
||||
> And if you like the project, but just don't have time to contribute, that's fine. There are other easy ways to support the project and show your appreciation, which we would also be very happy about:
|
||||
> - Star the project
|
||||
> - Tweet about it
|
||||
> - Refer this project in your project's readme
|
||||
> - Mention the project at local meetups and tell your friends/colleagues
|
||||
|
||||
<!-- omit in toc -->
|
||||
## Table of Contents
|
||||
|
||||
- [Code of Conduct](#code-of-conduct)
|
||||
- [I Have a Question](#i-have-a-question)
|
||||
- [I Want To Contribute](#i-want-to-contribute)
|
||||
- [Reporting Bugs](#reporting-bugs)
|
||||
- [Suggesting Enhancements](#suggesting-enhancements)
|
||||
- [Your First Code Contribution](#your-first-code-contribution)
|
||||
- [Improving The Documentation](#improving-the-documentation)
|
||||
- [Styleguides](#styleguides)
|
||||
- [Commit Messages](#commit-messages)
|
||||
- [Join The Project Team](#join-the-project-team)
|
||||
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
This project and everyone participating in it is governed by the
|
||||
[WelsonJS Code of Conduct](https://github.com/gnh1201/welsonjs/blob/master/CODE_OF_CONDUCT.md).
|
||||
By participating, you are expected to uphold this code. Please report unacceptable behavior
|
||||
to <abuse@catswords.net>.
|
||||
|
||||
|
||||
## I Have a Question
|
||||
|
||||
> If you want to ask a question, we assume that you have read the available [Documentation](https://catswords.social/@catswords_oss).
|
||||
|
||||
Before you ask a question, it is best to search for existing [Issues](https://github.com/gnh1201/welsonjs//issues) that might help you. In case you have found a suitable issue and still need clarification, you can write your question in this issue. It is also advisable to search the internet for answers first.
|
||||
|
||||
If you then still feel the need to ask a question and need clarification, we recommend the following:
|
||||
|
||||
- Open an [Issue](https://github.com/gnh1201/welsonjs//issues/new).
|
||||
- Provide as much context as you can about what you're running into.
|
||||
- Provide project and platform versions (nodejs, npm, etc), depending on what seems relevant.
|
||||
|
||||
We will then take care of the issue as soon as possible.
|
||||
|
||||
You might want to create a separate issue tag for questions and include it in this description. People should then tag their issues accordingly.
|
||||
|
||||
Depending on how large the project is, you may want to outsource the questioning, e.g. to Stack Overflow. You may add additional contact and information possibilities:
|
||||
|
||||
- ActivityPub
|
||||
- Stack Overflow tag or Public Forums
|
||||
- E-Mail List
|
||||
|
||||
## I Want To Contribute
|
||||
|
||||
> ### Legal Notice <!-- omit in toc -->
|
||||
> When contributing to this project, you must agree that you have authored 100% of the content, that you have the necessary rights to the content and that the content you contribute may be provided under the project license.
|
||||
|
||||
### Reporting Bugs
|
||||
|
||||
<!-- omit in toc -->
|
||||
#### Before Submitting a Bug Report
|
||||
|
||||
A good bug report shouldn't leave others needing to chase you up for more information. Therefore, we ask you to investigate carefully, collect information and describe the issue in detail in your report. Please complete the following steps in advance to help us fix any potential bug as fast as possible.
|
||||
|
||||
- Make sure that you are using the latest version.
|
||||
- Determine if your bug is really a bug and not an error on your side e.g. using incompatible environment components/versions (Make sure that you have read the [documentation](https://catswords.social/@catswords_oss). If you are looking for support, you might want to check [this section](#i-have-a-question)).
|
||||
- To see if other users have experienced (and potentially already solved) the same issue you are having, check if there is not already a bug report existing for your bug or error in the [bug tracker](https://github.com/gnh1201/welsonjs/issues?q=label%3Abug).
|
||||
- Also make sure to search the internet (including Stack Overflow) to see if users outside of the GitHub community have discussed the issue.
|
||||
- Collect information about the bug:
|
||||
- Stack trace (Traceback)
|
||||
- OS, Platform and Version (Windows, Linux, macOS, x86, ARM)
|
||||
- Version of the interpreter, compiler, SDK, runtime environment, package manager, depending on what seems relevant.
|
||||
- Possibly your input and the output
|
||||
- Can you reliably reproduce the issue? And can you also reproduce it with older versions?
|
||||
|
||||
<!-- omit in toc -->
|
||||
#### How Do I Submit a Good Bug Report?
|
||||
|
||||
> You must never report security related issues, vulnerabilities or bugs including sensitive information to the issue tracker, or elsewhere in public. Instead sensitive bugs must be sent by email to <abuse@catswords.net>.
|
||||
<!-- You may add a PGP key to allow the messages to be sent encrypted as well. -->
|
||||
|
||||
We use GitHub issues to track bugs and errors. If you run into an issue with the project:
|
||||
|
||||
- Open an [Issue](https://github.com/gnh1201/welsonjs//issues/new). (Since we can't be sure at this point whether it is a bug or not, we ask you not to talk about a bug yet and not to label the issue.)
|
||||
- Explain the behavior you would expect and the actual behavior.
|
||||
- Please provide as much context as possible and describe the *reproduction steps* that someone else can follow to recreate the issue on their own. This usually includes your code. For good bug reports you should isolate the problem and create a reduced test case.
|
||||
- Provide the information you collected in the previous section.
|
||||
|
||||
Once it's filed:
|
||||
|
||||
- The project team will label the issue accordingly.
|
||||
- A team member will try to reproduce the issue with your provided steps. If there are no reproduction steps or no obvious way to reproduce the issue, the team will ask you for those steps and mark the issue as `needs-repro`. Bugs with the `needs-repro` tag will not be addressed until they are reproduced.
|
||||
- If the team is able to reproduce the issue, it will be marked `needs-fix`, as well as possibly other tags (such as `critical`), and the issue will be left to be [implemented by someone](#your-first-code-contribution).
|
||||
|
||||
<!-- You might want to create an issue template for bugs and errors that can be used as a guide and that defines the structure of the information to be included. If you do so, reference it here in the description. -->
|
||||
|
||||
|
||||
### Suggesting Enhancements
|
||||
|
||||
This section guides you through submitting an enhancement suggestion for WelsonJS, **including completely new features and minor improvements to existing functionality**. Following these guidelines will help maintainers and the community to understand your suggestion and find related suggestions.
|
||||
|
||||
<!-- omit in toc -->
|
||||
#### Before Submitting an Enhancement
|
||||
|
||||
- Make sure that you are using the latest version.
|
||||
- Read the [documentation](https://catswords.social/@catswords_oss) carefully and find out if the functionality is already covered, maybe by an individual configuration.
|
||||
- Perform a [search](https://github.com/gnh1201/welsonjs/issues) to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one.
|
||||
- Find out whether your idea fits with the scope and aims of the project. It's up to you to make a strong case to convince the project's developers of the merits of this feature. Keep in mind that we want features that will be useful to the majority of our users and not just a small subset. If you're just targeting a minority of users, consider writing an add-on/plugin library.
|
||||
|
||||
<!-- omit in toc -->
|
||||
#### How Do I Submit a Good Enhancement Suggestion?
|
||||
|
||||
Enhancement suggestions are tracked as [GitHub issues](https://github.com/gnh1201/welsonjs//issues).
|
||||
|
||||
- Use a **clear and descriptive title** for the issue to identify the suggestion.
|
||||
- Provide a **step-by-step description of the suggested enhancement** in as many details as possible.
|
||||
- **Describe the current behavior** and **explain which behavior you expected to see instead** and why. At this point you can also tell which alternatives do not work for you.
|
||||
- You may want to **include screenshots and animated GIFs** which help you demonstrate the steps or point out the part which the suggestion is related to. You can use [this tool](https://www.cockos.com/licecap/) to record GIFs on macOS and Windows, and [this tool](https://github.com/colinkeenan/silentcast) or [this tool](https://github.com/GNOME/byzanz) on Linux. <!-- this should only be included if the project has a GUI -->
|
||||
- **Explain why this enhancement would be useful** to most WelsonJS users. You may also want to point out the other projects that solved it better and which could serve as inspiration.
|
||||
|
||||
<!-- You might want to create an issue template for enhancement suggestions that can be used as a guide and that defines the structure of the information to be included. If you do so, reference it here in the description. -->
|
||||
|
||||
### Your First Code Contribution
|
||||
|
||||
Just Notepad, in Windows machine.
|
||||
|
||||
## Styleguides
|
||||
|
||||
### Commit Messages
|
||||
|
||||
Flexible. We will respect your style.
|
||||
|
||||
## Join The Project Team
|
||||
|
||||
Contact us:
|
||||
|
||||
* ActivityPub [@catswords_oss@catswords.social](https://catswords.social/@catswords_oss)
|
||||
* abuse@catswords.net
|
||||
|
||||
## Attribution
|
||||
This guide is based on the **contributing-gen**. [Make your own](https://github.com/bttger/contributing-gen)!
|
|
@ -1,4 +0,0 @@
|
|||
@echo off
|
||||
|
||||
reg add "HKCU\Software\Microsoft\Internet Explorer\Styles" /f
|
||||
reg add "HKCU\Software\Microsoft\Internet Explorer\Styles" /v "MaxScriptStatements" /t REG_DWORD /d 0xFFFFFFFF /f
|
683
LICENSE
|
@ -1,63 +1,674 @@
|
|||
Microsoft Reciprocal License (MS-RL)
|
||||
SPDX Short identifier: MS-RL
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
1. Definitions
|
||||
The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law.
|
||||
Preamble
|
||||
|
||||
A "contribution" is the original software, or any additions or changes to the software.
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
A "contributor" is any person that distributes its contribution under this license.
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
"Licensed patents" are a contributor's patent claims that read directly on its contribution.
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
2. Grant of Rights
|
||||
(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
3. Conditions and Limitations
|
||||
(A) Reciprocal Grants- For any file you distribute that contains code from the software (in source code or binary format), you must provide recipients the source code to that file along with a copy of this license, which license will govern that file. You may license other files that are entirely your own work and do not contain code from the software under any terms you choose.
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
(B) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
(C) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
(D) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
(E) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
(F) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
Microsoft 상호 라이선스(MS-RL)
|
||||
SPDX 짧은 식별자: MS-RL
|
||||
0. Definitions.
|
||||
|
||||
이 라이센스는 함께 제공되는 소프트웨어의 사용에 적용됩니다. 소프트웨어를 사용하는 경우 이 라이센스에 동의하는 것입니다. 라이센스에 동의하지 않으면 소프트웨어를 사용하지 마십시오.
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
1. 정의
|
||||
"복제", "복제물", "파생 저작물" 및 "배포"라는 용어는 여기에서 미국 저작권법과 동일한 의미를 갖습니다.
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"기여"은 원본 소프트웨어 또는 소프트웨어에 대한 추가 또는 변경 사항입니다.
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
"기여자"는 이 라이선스에 따라 기여를 배포하는 모든 사람입니다.
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
"라이선스 지적재산권"은 기여에 대해 직접 읽는 기여자의 지적재산권 주장입니다.
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
2. 권리 부여
|
||||
(A) 저작권 부여 - 섹션 3의 라이선스 조건 및 제한 사항을 포함하여 이 라이선스의 조건에 따라 각 기여자는 자신의 기여를 복제하고 파생 작업을 준비할 수 있는 비독점적이고 전세계적인 로열티 없는 저작권 라이선스를 부여합니다. 기여 및 귀하가 만든 기여 또는 파생 작업을 배포합니다.
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
(B) 지적재산권 부여 - 섹션 3의 라이선스 조건 및 제한 사항을 포함하여 이 라이선스의 조건에 따라 각 기여자는 라이선스가 부여된 지적재산권에 따라 귀하에게 비독점적이고 전 세계적으로 사용료가 없는 라이선스를 부여합니다. 여기에는 판매, 판매 제안, 수입 및 또는 소프트웨어에 대한 기여 또는 소프트웨어 기여의 파생물을 처분하는 행위를 포함합니다.
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
3. 조건 및 제한
|
||||
(A) 상호 부여 - 소프트웨어의 코드(소스코드 또는 바이너리 형식)가 포함된 배포하는 모든 파일의 경우 해당 파일에 적용되는 라이선스의 사본과 함께 해당 파일의 소스코드를 수신자에게 제공해야 합니다. . 귀하는 전적으로 귀하의 저작물이며 귀하가 선택한 조건에 따라 소프트웨어의 코드를 포함하지 않는 다른 파일에 라이선스를 부여할 수 있습니다.
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
(B) 상표 사용권 없음 - 이 사용권은 기여자의 이름, 로고 또는 상표를 사용할 수 있는 권한을 부여하지 않습니다.
|
||||
1. Source Code.
|
||||
|
||||
(C) 귀하가 소프트웨어에 의해 침해되었다고 주장하는 지적재산권에 대해 기여자에 대해 지적재산권 청구를 제기하는 경우 해당 기여자의 소프트웨어에 대한 귀하의 지적재산권 라이선스는 자동으로 종료됩니다.
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
(D) 소프트웨어의 일부를 배포하는 경우 소프트웨어에 있는 모든 저작권, 지적재산권, 상표 및 귀속 고지를 보유해야 합니다.
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
(E) 소프트웨어의 일부를 소스코드 형식으로 배포하는 경우 배포와 함께 이 라이센스의 전체 사본을 포함하여 이 라이센스 하에서만 그렇게 할 수 있습니다. 소프트웨어의 일부를 컴파일된 또는 개체 코드 형식으로 배포하는 경우 이 라이센스를 준수하는 라이센스 하에서만 배포할 수 있습니다.
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
(F) 소프트웨어는 "있는 그대로" 사용이 허가됩니다. 당신은 그것을 사용할 위험을 감수합니다. 기여자는 명시적 보증, 보증 또는 조건을 제공하지 않습니다. 귀하는 현지 법률에 따라 이 라이선스가 변경할 수 없는 추가적인 소비자 권리를 가질 수 있습니다. 현지 법률에서 허용하는 범위 내에서 기여자는 상품성, 특정 목적에의 적합성 및 비침해성에 대한 묵시적 보증을 배제합니다.
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
|
|
63
LICENSE_MSRL
Normal file
|
@ -0,0 +1,63 @@
|
|||
Microsoft Reciprocal License (MS-RL)
|
||||
SPDX Short identifier: MS-RL
|
||||
|
||||
This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.
|
||||
|
||||
1. Definitions
|
||||
The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law.
|
||||
|
||||
A "contribution" is the original software, or any additions or changes to the software.
|
||||
|
||||
A "contributor" is any person that distributes its contribution under this license.
|
||||
|
||||
"Licensed patents" are a contributor's patent claims that read directly on its contribution.
|
||||
|
||||
2. Grant of Rights
|
||||
(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
|
||||
|
||||
(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
|
||||
|
||||
3. Conditions and Limitations
|
||||
(A) Reciprocal Grants- For any file you distribute that contains code from the software (in source code or binary format), you must provide recipients the source code to that file along with a copy of this license, which license will govern that file. You may license other files that are entirely your own work and do not contain code from the software under any terms you choose.
|
||||
|
||||
(B) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
|
||||
|
||||
(C) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
|
||||
|
||||
(D) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
|
||||
|
||||
(E) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
|
||||
|
||||
(F) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
|
||||
|
||||
Microsoft 상호 라이선스(MS-RL)
|
||||
SPDX 짧은 식별자: MS-RL
|
||||
|
||||
이 라이센스는 함께 제공되는 소프트웨어의 사용에 적용됩니다. 소프트웨어를 사용하는 경우 이 라이센스에 동의하는 것입니다. 라이센스에 동의하지 않으면 소프트웨어를 사용하지 마십시오.
|
||||
|
||||
1. 정의
|
||||
"복제", "복제물", "파생 저작물" 및 "배포"라는 용어는 여기에서 미국 저작권법과 동일한 의미를 갖습니다.
|
||||
|
||||
"기여"은 원본 소프트웨어 또는 소프트웨어에 대한 추가 또는 변경 사항입니다.
|
||||
|
||||
"기여자"는 이 라이선스에 따라 기여를 배포하는 모든 사람입니다.
|
||||
|
||||
"라이선스 지적재산권"은 기여에 대해 직접 읽는 기여자의 지적재산권 주장입니다.
|
||||
|
||||
2. 권리 부여
|
||||
(A) 저작권 부여 - 섹션 3의 라이선스 조건 및 제한 사항을 포함하여 이 라이선스의 조건에 따라 각 기여자는 자신의 기여를 복제하고 파생 작업을 준비할 수 있는 비독점적이고 전세계적인 로열티 없는 저작권 라이선스를 부여합니다. 기여 및 귀하가 만든 기여 또는 파생 작업을 배포합니다.
|
||||
|
||||
(B) 지적재산권 부여 - 섹션 3의 라이선스 조건 및 제한 사항을 포함하여 이 라이선스의 조건에 따라 각 기여자는 라이선스가 부여된 지적재산권에 따라 귀하에게 비독점적이고 전 세계적으로 사용료가 없는 라이선스를 부여합니다. 여기에는 판매, 판매 제안, 수입 및 또는 소프트웨어에 대한 기여 또는 소프트웨어 기여의 파생물을 처분하는 행위를 포함합니다.
|
||||
|
||||
3. 조건 및 제한
|
||||
(A) 상호 부여 - 소프트웨어의 코드(소스코드 또는 바이너리 형식)가 포함된 배포하는 모든 파일의 경우 해당 파일에 적용되는 라이선스의 사본과 함께 해당 파일의 소스코드를 수신자에게 제공해야 합니다. . 귀하는 전적으로 귀하의 저작물이며 귀하가 선택한 조건에 따라 소프트웨어의 코드를 포함하지 않는 다른 파일에 라이선스를 부여할 수 있습니다.
|
||||
|
||||
(B) 상표 사용권 없음 - 이 사용권은 기여자의 이름, 로고 또는 상표를 사용할 수 있는 권한을 부여하지 않습니다.
|
||||
|
||||
(C) 귀하가 소프트웨어에 의해 침해되었다고 주장하는 지적재산권에 대해 기여자에 대해 지적재산권 청구를 제기하는 경우 해당 기여자의 소프트웨어에 대한 귀하의 지적재산권 라이선스는 자동으로 종료됩니다.
|
||||
|
||||
(D) 소프트웨어의 일부를 배포하는 경우 소프트웨어에 있는 모든 저작권, 지적재산권, 상표 및 귀속 고지를 보유해야 합니다.
|
||||
|
||||
(E) 소프트웨어의 일부를 소스코드 형식으로 배포하는 경우 배포와 함께 이 라이센스의 전체 사본을 포함하여 이 라이센스 하에서만 그렇게 할 수 있습니다. 소프트웨어의 일부를 컴파일된 또는 개체 코드 형식으로 배포하는 경우 이 라이센스를 준수하는 라이센스 하에서만 배포할 수 있습니다.
|
||||
|
||||
(F) 소프트웨어는 "있는 그대로" 사용이 허가됩니다. 당신은 그것을 사용할 위험을 감수합니다. 기여자는 명시적 보증, 보증 또는 조건을 제공하지 않습니다. 귀하는 현지 법률에 따라 이 라이선스가 변경할 수 없는 추가적인 소비자 권리를 가질 수 있습니다. 현지 법률에서 허용하는 범위 내에서 기여자는 상품성, 특정 목적에의 적합성 및 비침해성에 대한 묵시적 보증을 배제합니다.
|
236
README.md
|
@ -1,85 +1,103 @@
|
|||
# welsonjs
|
||||
WelsonJS - Build a Windows desktop apps with JavaScript, HTML, and CSS based on WSH/HTA or GTK.
|
||||
|
||||
[](https://www.buymeacoffee.com/catswords/thanks-welsonjs-users)
|
||||
[](https://app.fossa.com/projects/git%2Bgithub.com%2Fgnh1201%2Fwelsonjs?ref=badge_shield)
|
||||
[](https://ci.appveyor.com/project/gnh1201/welsonjs)
|
||||
[](https://doi.org/10.5281/zenodo.11382384)
|
||||
[](https://catswords-oss.rdbl.io/5719744820/5510319392)
|
||||
[](https://catswords-oss.rdbl.io/5719744820/5510319392)
|
||||
[](https://catswords-oss.rdbl.io/5719744820/5510319392)
|
||||
[](https://catswords-oss.rdbl.io/5719744820/5510319392)
|
||||
[](https://www.slideshare.net/slideshow/welsonjs-javascript-framework-presentation-2024/276005486)
|
||||
[](https://youtu.be/JavH7Dms8-U)
|
||||
[](https://discord.gg/XKG5CjtXEj)
|
||||
[](https://github.com/gnh1201/welsonjs/discussions/167)
|
||||
|
||||
<img src="app/assets/img/logo.svg" height="32" alt=""/> WelsonJS - Build a Windows app on the Windows built-in JavaScript engine.
|
||||
|
||||

|
||||
|
||||
Now, You can build a Windows desktop app with JavaScript, TypeScript, CoffeeScript, ReScript, and HTML/CSS on Windows built-in ECMAScript engine.
|
||||
|
||||
WelsonJS = ***W***indows + ***El***ectr***on***-like + ***Javascript(JS)*** + :heart:[Contributions](https://github.com/sponsors/gnh1201)
|
||||
|
||||
* :kissing_cat: [Download Latest WelsonJS Launcher (ics.catswords.net)](https://ics.catswords.net/welsonjs_launcher_latest.zip)
|
||||
* :rocket: [Launch the WelsonJS environment on Microsoft Azure (azuremarketplace.microsoft.com)](https://azuremarketplace.microsoft.com/en-us/marketplace/apps/catswords.catswords-welsonjs-feb2025-02?tab=Overview)
|
||||
|
||||
**Note**: The default license for this project is GPL 3.0. However, if the GPL 3.0 license is not compatible with Microsoft products, it is subject to the MS-RL license.
|
||||
|
||||
## Sponsors
|
||||
* :octocat: [GitHub Sponsors](https://github.com/sponsors/gnh1201), :coffee: [Buy me a coffee](https://buymeacoffee.com/catswords)
|
||||
* <img src="https://ics.catswords.net/logo_oss.gif" height="32" alt=""/> [Open SW Portal](https://oss.kr), NIPA National IT Industry Promotion Agency<sup>(정보통신산업진흥원)</sup>
|
||||
* <img src="https://ics.catswords.net/signpath_logo.png" height="32" alt=""/> Free code signing provided by [SignPath.io](https://signpath.io), certificate by [SignPath Foundation](https://signpath.org/)
|
||||
* <img src="https://ics.catswords.net/f1security_logo.png" height="32" alt=""/> [F1Security<sup>(에프원시큐리티)</sup>](https://f1security.co.kr/) provides [industry-leading](https://www.ksecurity.or.kr/kisis/subIndex/469.do) web security services.
|
||||
* <img src="https://ics.catswords.net/microsoft_logo.png" height="32" alt=""/> [Microsoft ISV Success Program](https://www.microsoft.com/en-us/isv/isv-success), Grow your business with powerful tools.
|
||||
* :zap: [Integrations](https://catswords-oss.rdbl.io/5719744820/8278298336) ([ScrapeOps](https://scrapeops.io?fpr=namhyeon75), [SearchApi](https://www.searchapi.io/?via=namhyeon), [AviationStack](https://aviationstack.com?utm_source=FirstPromoter&utm_medium=Affiliate&fpr=namhyeon71), [Coupang](https://link.coupang.com/a/b7HV3V)...)
|
||||
|
||||
## System Requirements
|
||||
* **Operating Systems**: Windows XP SP3 or later (Currently, Windows 11 24H2)
|
||||
* For systems running Windows 2000 or earlier versions (e.g., 95, 98, Me), please contact us separately.
|
||||
|
||||
## Why Choose WelsonJS?
|
||||
WelsonJS is an advanced JavaScript framework designed to operate in extreme conditions where conventional solutions may fail. Unlike traditional JavaScript frameworks, WelsonJS focuses on executing scripts in constrained environments, ensuring reliable performance even with minimal system resources.
|
||||
|
||||
### Key Features
|
||||
* **Lightweight and Efficient**: Optimized for environments with limited computing power, WelsonJS delivers efficient script execution without unnecessary overhead.
|
||||
* **Windows ECMAScript Compatibility**: Designed to run seamlessly on Windows-based systems, adhering to the ECMAScript standard while maintaining lightweight execution.
|
||||
* **Standalone Execution**: Unlike well-known JavaScript runtimes, which require external dependencies, a WelsonJS application can operate as a self-contained app, making it suitable for embedded systems and isolated environments.
|
||||
* **Security-Oriented**: Built with security in mind, ensuring controlled execution of scripts without compromising system integrity.
|
||||
* **Minimalist Design**: WelsonJS strips away unnecessary complexities, focusing on core functionalities that maximize performance and reliability.
|
||||
|
||||
### Use Cases
|
||||
* **Legacy System Integration**: WelsonJS provides a practical solution for running JavaScript on older Windows environments where modern frameworks are not feasible.
|
||||
* **Automation and Scripting**: Ideal for executing scripts in constrained environments, enabling automated workflows and system-level scripting.
|
||||
* **Embedded Applications**: Suitable for devices and systems with strict resource constraints, such as industrial controllers and embedded platforms.
|
||||
* **Security-Focused Environments**: Useful in security-sensitive applications where dependencies on external network connections are limited.
|
||||
* **Office Automation**: Suitable for office automation tasks using Microsoft Office and various LLM-based AI (such as ChatGPT).
|
||||
|
||||
WelsonJS is tailored for developers who need a reliable, lightweight JavaScript framework in environments where traditional solutions are impractical. Whether working with legacy systems, embedded devices, or security-critical applications, WelsonJS ensures that JavaScript remains a viable and efficient option.
|
||||
|
||||
## Structure
|
||||

|
||||

|
||||
|
||||
## Specifications
|
||||
- ES5(ECMAScript 5), XML, JSON, YAML compatibility
|
||||
- [github:zloirock/core-js](https://github.com/zloirock/core-js)
|
||||
- [github:douglascrockford/JSON-js](https://github.com/douglascrockford/JSON-js) (aka. JSON2.js)
|
||||
- [github:nodeca/js-yaml](https://github.com/nodeca/js-yaml)
|
||||
- HTML5, CSS3 compatibility
|
||||
- [github:aFarkas/html5shiv](https://github.com/aFarkas/html5shiv)
|
||||
- [github:parndt/jquery-html5-placeholder-shim](https://github.com/parndt/jquery-html5-placeholder-shim)
|
||||
- [github:scottjehl/Respond](https://github.com/scottjehl/Respond)
|
||||
- [github:keithclark/selectivizr](https://github.com/keithclark/selectivizr)
|
||||
- [github:arv/ExplorerCanvas](https://github.com/arv/ExplorerCanvas)
|
||||
- [github:etianen/html5media](https://github.com/etianen/html5media)
|
||||
- [github:Modernizr/Modernizr](https://github.com/Modernizr/Modernizr)
|
||||
- Default CSS Framework
|
||||
- [github:jslegers/cascadeframework](https://github.com/jslegers/cascadeframework)
|
||||
- Included libraries
|
||||
- [jQuery](https://jquery.com/)
|
||||
- [jQuery UI](https://jqueryui.com/)
|
||||
- [github:kamranahmedse/jquery-toast-plugin](https://github.com/kamranahmedse/jquery-toast-plugin)
|
||||
- [github:hiddentao/squel](https://github.com/hiddentao/squel)
|
||||
- [github:BorisMoore/jsrender](https://github.com/BorisMoore/jsrender)
|
||||
- [Includes binaries](https://github.com/gnh1201/welsonjs/blob/master/bin/README.MD)
|
||||
- [module.exports](https://nodejs.org/en/knowledge/getting-started/what-is-require/), CommonJS, UMD compatibility
|
||||
- [NPM](https://www.npmjs.com/) compatibility
|
||||
- [CoffeeScript 2](https://coffeescript.org/)/[LiveScript](https://livescript.net/) compatibility
|
||||
- [TypeScript](https://www.typescriptlang.org/) compatibility
|
||||
- Ready to use on Windows machine immediately. No require additional softwares installation.
|
||||
* Built-in transpilers: [TypeScript](https://www.typescriptlang.org/), [Rescript](https://rescript-lang.org/), [CoffeeScript 2](https://coffeescript.org/), [LiveScript](https://livescript.net/)
|
||||
* **Ready to use on Windows machine immediately. No additional software installation is required.**
|
||||
* **WelsonJS Launcher**: Manage instances (Like a container), User-defined variable editor, [Microsoft Monaco Editor](https://github.com/microsoft/monaco-editor) and [React](https://react.dev/) (Pre-embedded rich code editor), [Microsoft Copilot](https://copilot.microsoft.com), and [Azure AI Services](https://azure.microsoft.com/en-us/products/ai-services) on the code editor.
|
||||
* ES5(ECMAScript 5), XML, JSON, YAML compatibility: [core-js](https://github.com/zloirock/core-js), [JSON2.js](https://github.com/douglascrockford/JSON-js), [js-yaml](https://github.com/nodeca/js-yaml)
|
||||
* HTML5 compatibility on the built-in HTML rendering engine: [html5shiv](https://github.com/aFarkas/html5shiv), [jquery-html5-placeholder-shim](https://github.com/parndt/jquery-html5-placeholder-shim), [Respond](https://github.com/scottjehl/Respond), [selectivizr](https://github.com/keithclark/selectivizr), [ExplorerCanvas](https://github.com/arv/ExplorerCanvas), [Modernizr](https://github.com/Modernizr/Modernizr)
|
||||
* Classical CSS Frameworks: [cascadeframework](https://github.com/jslegers/cascadeframework), [golden-layout](https://github.com/golden-layout/golden-layout)
|
||||
* WYSIWYG HTML Editor: [summernote](https://github.com/summernote/summernote)
|
||||
* Included libraries: [jQuery](https://jquery.com/), [jQuery UI](https://jqueryui.com/), [jquery-toast-plugin](https://github.com/kamranahmedse/jquery-toast-plugin), [squel](https://github.com/hiddentao/squel), [jsrender](https://github.com/BorisMoore/jsrender), [linq](https://github.com/mihaifm/linq), [pegjs](https://github.com/pegjs/pegjs), [numbers.js](https://github.com/numbers/numbers.js)
|
||||
* Compatible with modern JavaScript specifications: [module.exports](https://nodejs.org/api/modules.html#moduleexports), CommonJS, UMD compatibility, [NPM(Node Package Manager)](https://www.npmjs.com/) compatibility
|
||||
* Support a device debugging protocol clients: [Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/), [ADB(Android Debug Bridge)](https://source.android.com/docs/setup/build/adb)
|
||||
* RPC(Remote Procedure Call) protocol clients: [gRPC](https://grpc.io/), [JSON-RPC 2.0](https://www.jsonrpc.org/specification)
|
||||
* Various types of HTTP clients: [XHR(MSXML)](https://developer.mozilla.org/docs/Glossary/XMLHttpRequest), [cURL](https://curl.se/), [BITS](https://en.m.wikipedia.org/w/index.php?title=Background_Intelligent_Transfer_Service), [CERT](https://github.com/MicrosoftDocs/windowsserverdocs/blob/main/WindowsServerDocs/administration/windows-commands/certutil.md), [Web Proxy, SEO/SERP](https://catswords-oss.rdbl.io/5719744820/1706431912)
|
||||
* The native toolkit for Windows environments: Write a Windows Service Application with JavaScript, Control a window handle, Cryptography (e.g., [ISO/IEC 18033-3:2010](https://www.iso.org/standard/54531.html) aka. [HIGHT](https://seed.kisa.or.kr/kisa/algorithm/EgovHightInfo.do)), [Named Shared Memory](https://learn.microsoft.com/en-us/windows/win32/memory/creating-named-shared-memory) based [IPC](https://qiita.com/gnh1201/items/4e70dccdb7adacf0ace5), [NuGet package](https://www.nuget.org/packages/WelsonJS.Toolkit)
|
||||
* Generative AI integrations: [Multiple LLM and sLLM](https://catswords-oss.rdbl.io/5719744820/5510319392) (e.g., ChatGPT, Claude, ...)
|
||||
* Aviation Data integrations: [AviationStack](https://aviationstack.com?utm_source=FirstPromoter&utm_medium=Affiliate&fpr=namhyeon71), [SearchApi Google Flight](https://www.searchapi.io/?via=namhyeon)
|
||||
* VM infrastructure tool integrations: [OVFTool for Broadcom/VMware infrastructures](https://developer.broadcom.com/tools/open-virtualization-format-ovf-tool/latest)
|
||||
* Everything you can imagine.
|
||||
|
||||
## Included libraries
|
||||
- lib/std (Standard library)
|
||||
- lib/system (System library)
|
||||
- lib/base64 (BASE64 Encode and Decode)
|
||||
- lib/db (Database interface)
|
||||
- lib/file (File I/O interface)
|
||||
- lib/http (HTTP interface)
|
||||
- lib/json (JSON Encode and Decode)
|
||||
- lib/registry (Windows Registry interface)
|
||||
- lib/security (Security Policy interface)
|
||||
- lib/sendmail (Sendmail interface with 3rdparty)
|
||||
- lib/shell (Command Prompt interface)
|
||||
- lib/timer (`setTimeout` polyfills)
|
||||
- lib/powershell (Windows Powershell interface)
|
||||
- lib/service (Windows Service interface)
|
||||
- lib/oldbrowser (ES5/ES6, HTML/JS/CSS compatibility)
|
||||
- lib/uri (URI scheme interface)
|
||||
- lib/winlibs (Windows DLL(Dynamic-link library) interface)
|
||||
- lib/autohotkey ([AutoHotkey](https://www.autohotkey.com/) interface)
|
||||
- lib/autoit ([AutoIt3/AutoItX](https://www.autoitscript.com/) interface)
|
||||
- lib/shadowsocks ([Shadowsocks](https://shadowsocks.org/) interface)
|
||||
- lib/excel (Microsoft Excel interface)
|
||||
- lib/vbscript (VBScript interface)
|
||||
- lib/wintap (Windows-TAP interface)
|
||||
- lib/tun2socks (TUN2SOCKS interface)
|
||||
- lib/hosts (Hosts file interface)
|
||||
- lib/gtk (GTK/GladeXML server GUI interface)
|
||||
- lib/chrome (Chrome Web Browser Debugging interface)
|
||||
- lib/toolkit (`WelsonJS.Toolkit` native component)
|
||||
- lib/pipe-ipc (PIPE-based IPC(Inter-Process Communication) implementation)
|
||||
|
||||
## Make your own `sayhello` example
|
||||
## Quick start
|
||||
|
||||
### 1. Write a file `lib/sayhello.js`
|
||||
```
|
||||
exports.VERSIONINFO = "SayHello Library (sayhello.js) version 0.1
|
||||
exports.global = global;
|
||||
exports.require = global.require;
|
||||
|
||||
exports.say = function() {
|
||||
```js
|
||||
// lib/sayhello.js
|
||||
function say() {
|
||||
console.log("hello");
|
||||
}
|
||||
|
||||
exports.say = say;
|
||||
|
||||
exports.VERSIONINFO = "SayHello (sayhello.js) version 0.1";
|
||||
exports.AUTHOR = "abuse@catswords.net";
|
||||
exports.global = global;
|
||||
exports.require = global.require;
|
||||
```
|
||||
|
||||
### 2. Write a file `sayhello.js`
|
||||
```
|
||||
```js
|
||||
// sayhello.js
|
||||
var SayHello = require("lib/sayhello");
|
||||
|
||||
function main() {
|
||||
|
@ -91,39 +109,73 @@ function main() {
|
|||
exports.main = main;
|
||||
```
|
||||
|
||||
### 3. Execute file on the command prompt
|
||||
```
|
||||
C:\Users\John\Documents\GitHub\welsonjs> cscript app.js sayhello
|
||||
### 3. Execute script on the console
|
||||
```cmd
|
||||
C:\Users\JohnDoe\Documents\GitHub\welsonjs> cscript app.js sayhello
|
||||
calling say()
|
||||
hello
|
||||
ended say()
|
||||
```
|
||||
|
||||
## How to make your own setup file
|
||||
- Please check `setup.iss` file it could be compile with [Inno Setup](https://jrsoftware.org/isinfo.php)
|
||||
## How to release my application?
|
||||
The WelsonJS framework suggests the following application release methods:
|
||||
|
||||
* **Compress to Zip, and use the launcher**: Compress the files and directories necessary for running the project into a Zip file, and distribute it along with the [WelsonJS Launcher](https://catswords-oss.rdbl.io/5719744820/4131485779).
|
||||
* **Build a setup file**: Use [Inno Setup](https://jrsoftware.org/isinfo.php). The setup profile (the `setup.iss` file) is already included.
|
||||
* **Copy all directories and files**: This is the simplest and most straightforward method.
|
||||
|
||||
## Screenshots
|
||||

|
||||

|
||||
|
||||
## Thanks!
|
||||

|
||||

|
||||
|
||||
- https://www.facebook.com/javascript4u/posts/1484014618472735
|
||||
- https://python5.com/q/xtbsqjxb
|
||||

|
||||
|
||||
## Related projects
|
||||
- [gnh1201/wsh-js-gtk](https://github.com/gnh1201/wsh-js-gtk) - GTK GUI ported to Windows Scripting Host - Javascript (Microsoft JScript) (wsh-js)
|
||||
- [gnh1201/wsh-json](https://github.com/gnh1201/wsh-json) - JSON stringify/parse (encode/decode) for Windows Scripting Host
|
||||
- [redskyit/wsh-appjs](https://github.com/redskyit/wsh-appjs) - require-js and app framework for Windows Scripting Host JavaScript
|
||||
- [JohnLaTwC's gist](https://gist.github.com/JohnLaTwC/4315bbbd89da0996f5c08c032b391799) - JavaScript RAT
|
||||
- [JSMan-/JS-Framework](https://github.com/JSMan-/JS-Framework) - No description
|
||||
- [iconjack/setTimeout-for-windows-script-host](https://github.com/iconjack/setTimeout-for-windows-script-host) - Replacement for the missing setTimeout and clearTimeout function in Windows Script Host
|
||||
- [johnjohnsp1/WindowsScriptHostExtension](https://github.com/johnjohnsp1/WindowsScriptHostExtension) - Inject DLL Prototype using Microsoft.Windows.ACTCTX COM Object
|
||||
- [kuntashov/jsunit](https://github.com/kuntashov/jsunit) - JSUnit port for Windows Scripting Host
|
||||
- [nickdoth/WSHHttpServer](https://github.com/nickdoth/WSHHttpServer) - HTTP server based on Windows Script Host
|
||||

|
||||
|
||||
## Image stocks
|
||||
- [7418_blocks_color_modules_rgb_square_icon](https://www.iconfinder.com/icons/7418/blocks_color_modules_rgb_square_icon) (Icon File) - Sergio Sanchesz Lopez, Futurosoft
|
||||

|
||||
|
||||
## Contact me
|
||||
- abuse@catswords.net
|
||||

|
||||
|
||||

|
||||
|
||||
## Thanks to
|
||||
* :heart: Artwork (Logo image): [@druidesse](https://github.com/druidesse)
|
||||
* :heart: Artwork (Cover image): [@_bag0@x.com](https://x.com/_bag0)
|
||||
* :heart: Special Contributors: [@hcho3](https://github.com/hcho3), :octocat: [GitHub Sponsors](https://github.com/sponsors/gnh1201)
|
||||
* :sunglasses: Heavy-industry specialized CSP(Cloud Service Provider) in Republic of Korea - Use case establishment
|
||||
* :sunglasses: Live-commerce specialized online advertisement companies in Republic of Korea - Use case establishment
|
||||
* :sunglasses: Information security companies in Republic of Korea - Use case establishment
|
||||
* :eyes: [Facebook Group "Javascript Programming"(javascript4u)](https://www.facebook.com/javascript4u/posts/build-a-windows-desktop-apps-with-javascript-html-and-cssmorioh-javascript-html-/1484014618472735/)
|
||||
* :eyes: [morioh.com](https://morioh.com/a/23c427a82bf1/build-a-windows-desktop-apps-with-javascript-html-and-css)
|
||||
* :eyes: CSDN
|
||||
* :eyes: Qiita - Knowledge-base about WSH environment
|
||||
* :sunglasses: Redsky Software - PoC(Proof of Concept) of the CommonJS on WSH environment
|
||||
* :sunglasses: Inspired by a small-sized JavaScript payload demonstrated by a cybersecurity related group.
|
||||
* :sunglasses: Inspired by the use of Named Shared Memory in a cross-runtime IPC implementation written by the unidentified developer.
|
||||
* :eyes: Fediverse
|
||||
* :eyes: [Hacker News](https://news.ycombinator.com/item?id=41316782)
|
||||
* :eyes: [WebToolsWeekly](https://webtoolsweekly.com/archives/issue-585/)
|
||||
* :eyes: [GeekNews](https://news.hada.io/weekly/202441) in GeekNews Weekly (2024-09-30 ~ 2024-10-06)
|
||||
* :eyes: [daily.dev](https://app.daily.dev/posts/js-libraries-svg-tools-json-databases-8quregz3a)
|
||||
* :eyes: [PitchHut](https://www.pitchhut.com/project/proj_Ya136OLSW5at)
|
||||
* :eyes: [Disquiet](https://dis.qa/nv6T6)
|
||||
|
||||
## Report abuse
|
||||
* [GitHub Security Advisories (gnh1201/welsonjs)](https://github.com/gnh1201/welsonjs/security)
|
||||
* [abuse@catswords.net](mailto:abuse@catswords.net)
|
||||
|
||||
## Join the community
|
||||
I am always open. Collaboration, opportunities, and community activities are all welcome.
|
||||
|
||||
* ActivityPub [@catswords_oss@catswords.social](https://catswords.social/@catswords_oss)
|
||||
* XMPP [catswords@conference.omemo.id](xmpp:catswords@conference.omemo.id?join)
|
||||
* [Join Catswords OSS on Microsoft Teams (teams.live.com)](https://teams.live.com/l/community/FEACHncAhq8ldnojAI)
|
||||
* [Join Catswords OSS #welsonjs on Discord (discord.gg)](https://discord.gg/XKG5CjtXEj)
|
||||
|
||||
## Special channels
|
||||
* [A paid consultation channel (m.expert.naver.com)](https://m.expert.naver.com/mobile/expert/product/detail?storeId=100051156&productId=100144540) is available for Korean<sup>(한국어)</sup> region.
|
||||
* [Join the private operations channel (forms.gle)](https://forms.gle/ZKAAaGTiGamksHoo8) is available for all regions.
|
||||
|
||||
## License
|
||||
[](https://app.fossa.com/projects/git%2Bgithub.com%2Fgnh1201%2Fwelsonjs?ref=badge_large)
|
||||
|
|
20
REGASM.MD
|
@ -1,20 +0,0 @@
|
|||
# REGASM for WelsonJS
|
||||
|
||||
In general, the location of REGASM is as follows.
|
||||
|
||||
```
|
||||
C:\Windows\Microsoft.NET\Framework\v2.0.50727\RegAsm.exe
|
||||
```
|
||||
|
||||
Or it may vary depending on the version.
|
||||
|
||||
## How to register .NET COM/DLL component
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
C:\Windows\Microsoft.NET\Framework\v2.0.50727\RegAsm.exe /codebase WelsonJS.Toolkit.dll
|
||||
C:\Windows\Microsoft.NET\Framework64\v2.0.50727\RegAsm.exe /codebase WelsonJS.Toolkit.dll
|
||||
```
|
||||
|
||||
Thank you.
|
64
SECURITY.MD
|
@ -1,27 +1,63 @@
|
|||
# Security Note for WelsonJS
|
||||
|
||||
## License
|
||||
The WelsonJS project is available under either the GPLv3 or MS-RL opensource licenses. If the GPLv3 license is not compatible with Microsoft products, the MS-RL license applies; otherwise, the GPLv3 license is used. Under these licenses, if you distribute modified versions of this project's source code to third parties, you may be required to disclose the source code. For more details, please refer to the `LICENSE` and `LICENSE_MSRL` files.
|
||||
|
||||
## Caution
|
||||
This repository includes recent cases on how to access Windows APIs and functions at the JavaScript level.
|
||||
This repository contains information on accessing Windows APIs and functions in the JavaScript runtime, along with recent case studies. While this can provide a flexible development environment for anyone, it can also be misused for malicious purposes. Please be aware that using this project to create abuse tools, such as for DoS attacks, may result in legal consequences in your country. We encourage you to use this project only for creating web technology-based applications, like Electron, or legally permitted testing tools.
|
||||
|
||||
This way can provide a flexible development environment for anyone, but it also applies to those with malicious purposes.
|
||||
## Known Use Cases
|
||||
WelsonJS is typically used for the following purposes:
|
||||
|
||||
Using this project to create an abuse tools (e.g. DoS attack) may be subject to punishment in your country.
|
||||
* Testing web accessibility and compliance, including adherence to W3C standards (WEB-ARIA, WCAG), national laws (ADA/DDA, GDPR), and other relevant regulations.
|
||||
* Exploring vulnerabilities of equipment within the local network.
|
||||
* Improving the availability of VPN or proxy clients.
|
||||
* Building automation, CI/CD (Continuous Integration/Continuous Delivery), DevOps, and SecOps.
|
||||
* Asset evaluation (e.g., obtaining purchase history from online shopping and delivery websites).
|
||||
* Online video streaming quality testing and improvement.
|
||||
* Office automation and integration with LLM-based AI (e.g., ChatGPT) services.
|
||||
|
||||
Please use it only to create a web technology based application similar to Electron or legally testing tool.
|
||||
## Notes
|
||||
1. If you plan to use WelsonJS for a purpose other than those mentioned above, please contact us beforehand.
|
||||
2. If you are looking for ways to use WelsonJS more efficiently, referencing the [LOLBAS (Living Off The Land Binaries and Scripts)](https://lolbas-project.github.io/) list can be helpful.
|
||||
|
||||
## Known use cases
|
||||
In general, WelsonJS is used in the following situations.
|
||||
## Guidelines
|
||||
|
||||
* Test web accessibility and compliance: e.g. W3C standards(WEB-ARIA, WCAG), The national laws(ADA/DDA, GDPR)
|
||||
* Explore vulnerabilities of equipment within the local network
|
||||
* Improve availablity of a clients of VPN or Proxy
|
||||
* Build automation, CD/CI(Continuous Integration/Continuous Delivery), DevOps, SecOps
|
||||
### Use of Online Shopping and Delivery Websites
|
||||
We are aware of cases where WelsonJS has been used to access the websites of online shopping or delivery companies for asset valuation. This is a good use case, but there have been reports of website downtime caused by excessive concurrent requests. Please exercise caution and avoid excessive simultaneous executions.
|
||||
|
||||
Note 1: If you deviate from the previous case mentioned above, please contact us before using it.
|
||||
### Use for Online Video Streaming Quality Testing and Improvement
|
||||
We are aware of cases where WelsonJS is used for video streaming quality testing and improvement. It should be used solely for expert-level streaming quality testing, often referred to by terms like 4K, 8K, HD, FHD, UHD, 720p, 1080p, etc. For such purposes, it is recommended to use videos provided by television manufacturers (e.g., LG, Samsung) or graphics card manufacturers (e.g., NVIDIA, AMD) specifically for testing purposes. It is essential to avoid using videos that contain content not legally permitted in your region. The WelsonJS developers and maintainers take no responsibility for the use of videos containing illegal content.
|
||||
|
||||
Note 2: A similar approach that WelsonJS uses has been called `LOLBins` in the cyber security community since 2018. This is not intended to be but you will get useful hints you need for extensions.
|
||||
### Use for Scientific Research Institutes
|
||||
WelsonJS is designed for flexible industrial facility control (a.k.a. Industrial Scripting) in environments where modifying compiled binaries is restricted. Parts or all of this project's source code may be found in use within scientific research institutes. In such cases, appropriate safety measures tailored to the specific application area may be required. If support is needed for these applications, please do not hesitate to contact us.
|
||||
|
||||
### Use for Security Testing
|
||||
We are aware of instances where WelsonJS has been used by legitimate cybersecurity firms to discover and test vulnerabilities (such as credential stuffing) in IoT devices. If you intend to use WelsonJS as a security testing tool, it should be done in a controlled environment that complies with legal regulations.
|
||||
|
||||
### Use for Cloud Monitoring
|
||||
WelsonJS is a project inspired by the requirements of a cloud service provider to develop lightweight software (e.g., agents) for collecting metrics on Windows systems. While using WelsonJS for this purpose is desirable, ensuring security in the server-client communication is entirely the responsibility of the user.
|
||||
|
||||
## Alternative Names
|
||||
This program is also known by the following names. These names are used solely for the purpose of identifying the work and do not impact the license:
|
||||
|
||||
* DOI [10.5281/zenodo.11382384](https://zenodo.org/doi/10.5281/zenodo.11382384)(2024) (CERN/OpenAIRE Zenodo)
|
||||
* ["284757291"](https://ics.catswords.net/1494315-Certificate%2BSoR-284757291.pdf)(2024) (Registered with the [UK Copyright Service](https://copyrightservice.co.uk/))
|
||||
* ["A0562"](https://www.oss.kr/dev_competition_activities/show/544723e6-850a-4956-9194-79640420c19a)(2023) (2023 Open-source Development Contest, NIPA National IT Industry Promotion Agency<sup>(정보통신산업진흥원)</sup>, Republic of Korea)
|
||||
* ["2025-02-08-1952"](https://ics.catswords.net/20250410092300005.pdf)(2025) (Technical Data Bailment System (Technology Escrow), "Korea Foundation for Cooperation of Large & Small Business, Rural Affairs"<sup>(대·중소기업·농어업협력재단)</sup>, Republic of Korea)
|
||||
* "C-2021-000237"(2021) (Copyright Registration Online System, Korea Copyright Commission<sup>(한국저작권위원회)</sup>, Republic of Korea)
|
||||
* "Codename Macadamia"(2020) (Heavy industry specialized CSP in the Republic of Korea)
|
||||
|
||||
## Report abuse
|
||||
If you find any abuse cases of this project, please report it.
|
||||
* [GitHub Security Advisories](https://github.com/gnh1201/welsonjs/security)
|
||||
* [abuse@catswords.net](mailto:abuse@catswords.net)
|
||||
|
||||
* abuse@catswords.net
|
||||
## Join the community
|
||||
* ActivityPub [@catswords_oss@catswords.social](https://catswords.social/@catswords_oss)
|
||||
* XMPP [catswords@conference.omemo.id](xmpp:catswords@conference.omemo.id?join)
|
||||
* [Join Catswords OSS on Microsoft Teams (teams.live.com)](https://teams.live.com/l/community/FEACHncAhq8ldnojAI)
|
||||
* [Join Catswords OSS #welsonjs on Discord (discord.gg)](https://discord.gg/XKG5CjtXEj)
|
||||
|
||||
## Special channels
|
||||
* [A paid consultation channel (m.expert.naver.com)](https://m.expert.naver.com/mobile/expert/product/detail?storeId=100051156&productId=100144540) is available for Korean<sup>(한국어)</sup> region.
|
||||
* [Join the private operations channel (forms.gle)](https://forms.gle/ZKAAaGTiGamksHoo8) is available for all regions.
|
||||
|
|
261
WelsonJS.Toolkit/WelsonJS.Launcher/EnvForm.Designer.cs
generated
Normal file
|
@ -0,0 +1,261 @@
|
|||
namespace WelsonJS.Launcher
|
||||
{
|
||||
partial class EnvForm
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.gbUserDefinedVariables = new System.Windows.Forms.GroupBox();
|
||||
this.lvUserDefinedVariables = new System.Windows.Forms.ListView();
|
||||
this.columnHeader1 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||
this.columnHeader2 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||
this.gbUpdateUserDefinedVariable = new System.Windows.Forms.GroupBox();
|
||||
this.checkDeleteVariable = new System.Windows.Forms.CheckBox();
|
||||
this.btnOk = new System.Windows.Forms.Button();
|
||||
this.btnOpenFile = new System.Windows.Forms.Button();
|
||||
this.btnOpenDirectory = new System.Windows.Forms.Button();
|
||||
this.textSetValue = new System.Windows.Forms.TextBox();
|
||||
this.textSetName = new System.Windows.Forms.TextBox();
|
||||
this.labelSetValue = new System.Windows.Forms.Label();
|
||||
this.labelSetName = new System.Windows.Forms.Label();
|
||||
this.gbImportAndExport = new System.Windows.Forms.GroupBox();
|
||||
this.btnExport = new System.Windows.Forms.Button();
|
||||
this.btnImport = new System.Windows.Forms.Button();
|
||||
this.gbUserDefinedVariables.SuspendLayout();
|
||||
this.gbUpdateUserDefinedVariable.SuspendLayout();
|
||||
this.gbImportAndExport.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// gbUserDefinedVariables
|
||||
//
|
||||
this.gbUserDefinedVariables.Controls.Add(this.lvUserDefinedVariables);
|
||||
this.gbUserDefinedVariables.Location = new System.Drawing.Point(12, 12);
|
||||
this.gbUserDefinedVariables.Name = "gbUserDefinedVariables";
|
||||
this.gbUserDefinedVariables.Size = new System.Drawing.Size(419, 170);
|
||||
this.gbUserDefinedVariables.TabIndex = 0;
|
||||
this.gbUserDefinedVariables.TabStop = false;
|
||||
this.gbUserDefinedVariables.Text = "User-defined variables";
|
||||
//
|
||||
// lvUserDefinedVariables
|
||||
//
|
||||
this.lvUserDefinedVariables.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
|
||||
this.columnHeader1,
|
||||
this.columnHeader2});
|
||||
this.lvUserDefinedVariables.HideSelection = false;
|
||||
this.lvUserDefinedVariables.Location = new System.Drawing.Point(16, 26);
|
||||
this.lvUserDefinedVariables.Name = "lvUserDefinedVariables";
|
||||
this.lvUserDefinedVariables.Size = new System.Drawing.Size(386, 129);
|
||||
this.lvUserDefinedVariables.TabIndex = 0;
|
||||
this.lvUserDefinedVariables.UseCompatibleStateImageBehavior = false;
|
||||
this.lvUserDefinedVariables.SelectedIndexChanged += new System.EventHandler(this.lvUserDefinedVariables_SelectedIndexChanged);
|
||||
//
|
||||
// columnHeader1
|
||||
//
|
||||
this.columnHeader1.Text = "Name";
|
||||
//
|
||||
// columnHeader2
|
||||
//
|
||||
this.columnHeader2.Text = "Value";
|
||||
//
|
||||
// gbUpdateUserDefinedVariable
|
||||
//
|
||||
this.gbUpdateUserDefinedVariable.Controls.Add(this.checkDeleteVariable);
|
||||
this.gbUpdateUserDefinedVariable.Controls.Add(this.btnOk);
|
||||
this.gbUpdateUserDefinedVariable.Controls.Add(this.btnOpenFile);
|
||||
this.gbUpdateUserDefinedVariable.Controls.Add(this.btnOpenDirectory);
|
||||
this.gbUpdateUserDefinedVariable.Controls.Add(this.textSetValue);
|
||||
this.gbUpdateUserDefinedVariable.Controls.Add(this.textSetName);
|
||||
this.gbUpdateUserDefinedVariable.Controls.Add(this.labelSetValue);
|
||||
this.gbUpdateUserDefinedVariable.Controls.Add(this.labelSetName);
|
||||
this.gbUpdateUserDefinedVariable.Location = new System.Drawing.Point(12, 188);
|
||||
this.gbUpdateUserDefinedVariable.Name = "gbUpdateUserDefinedVariable";
|
||||
this.gbUpdateUserDefinedVariable.Size = new System.Drawing.Size(419, 229);
|
||||
this.gbUpdateUserDefinedVariable.TabIndex = 1;
|
||||
this.gbUpdateUserDefinedVariable.TabStop = false;
|
||||
this.gbUpdateUserDefinedVariable.Text = "Update the user-defined variable";
|
||||
//
|
||||
// checkDeleteVariable
|
||||
//
|
||||
this.checkDeleteVariable.AutoSize = true;
|
||||
this.checkDeleteVariable.Location = new System.Drawing.Point(31, 88);
|
||||
this.checkDeleteVariable.Name = "checkDeleteVariable";
|
||||
this.checkDeleteVariable.Size = new System.Drawing.Size(131, 16);
|
||||
this.checkDeleteVariable.TabIndex = 7;
|
||||
this.checkDeleteVariable.Text = "Delete this variable";
|
||||
this.checkDeleteVariable.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// btnOk
|
||||
//
|
||||
this.btnOk.Image = global::WelsonJS.Launcher.Properties.Resources.icon_check_32;
|
||||
this.btnOk.Location = new System.Drawing.Point(303, 123);
|
||||
this.btnOk.Name = "btnOk";
|
||||
this.btnOk.Size = new System.Drawing.Size(86, 86);
|
||||
this.btnOk.TabIndex = 6;
|
||||
this.btnOk.Text = "Ok";
|
||||
this.btnOk.TextAlign = System.Drawing.ContentAlignment.BottomCenter;
|
||||
this.btnOk.UseVisualStyleBackColor = true;
|
||||
this.btnOk.Click += new System.EventHandler(this.btnOk_Click);
|
||||
//
|
||||
// btnOpenFile
|
||||
//
|
||||
this.btnOpenFile.Image = global::WelsonJS.Launcher.Properties.Resources.icon_file_32;
|
||||
this.btnOpenFile.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
||||
this.btnOpenFile.Location = new System.Drawing.Point(31, 169);
|
||||
this.btnOpenFile.Name = "btnOpenFile";
|
||||
this.btnOpenFile.Padding = new System.Windows.Forms.Padding(11, 0, 0, 0);
|
||||
this.btnOpenFile.Size = new System.Drawing.Size(201, 40);
|
||||
this.btnOpenFile.TabIndex = 5;
|
||||
this.btnOpenFile.Text = "Open the file...";
|
||||
this.btnOpenFile.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
||||
this.btnOpenFile.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText;
|
||||
this.btnOpenFile.UseVisualStyleBackColor = true;
|
||||
this.btnOpenFile.Click += new System.EventHandler(this.btnOpenFile_Click);
|
||||
//
|
||||
// btnOpenDirectory
|
||||
//
|
||||
this.btnOpenDirectory.Image = global::WelsonJS.Launcher.Properties.Resources.icon_directory_32;
|
||||
this.btnOpenDirectory.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
||||
this.btnOpenDirectory.Location = new System.Drawing.Point(31, 123);
|
||||
this.btnOpenDirectory.Name = "btnOpenDirectory";
|
||||
this.btnOpenDirectory.Padding = new System.Windows.Forms.Padding(11, 0, 0, 0);
|
||||
this.btnOpenDirectory.Size = new System.Drawing.Size(201, 40);
|
||||
this.btnOpenDirectory.TabIndex = 4;
|
||||
this.btnOpenDirectory.Text = "Open the directory...";
|
||||
this.btnOpenDirectory.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
||||
this.btnOpenDirectory.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText;
|
||||
this.btnOpenDirectory.UseVisualStyleBackColor = true;
|
||||
this.btnOpenDirectory.Click += new System.EventHandler(this.btnOpenDirectory_Click);
|
||||
//
|
||||
// textSetValue
|
||||
//
|
||||
this.textSetValue.Location = new System.Drawing.Point(98, 55);
|
||||
this.textSetValue.Name = "textSetValue";
|
||||
this.textSetValue.Size = new System.Drawing.Size(291, 21);
|
||||
this.textSetValue.TabIndex = 3;
|
||||
//
|
||||
// textSetName
|
||||
//
|
||||
this.textSetName.Location = new System.Drawing.Point(98, 28);
|
||||
this.textSetName.Name = "textSetName";
|
||||
this.textSetName.Size = new System.Drawing.Size(291, 21);
|
||||
this.textSetName.TabIndex = 2;
|
||||
//
|
||||
// labelSetValue
|
||||
//
|
||||
this.labelSetValue.AutoSize = true;
|
||||
this.labelSetValue.Location = new System.Drawing.Point(29, 59);
|
||||
this.labelSetValue.Name = "labelSetValue";
|
||||
this.labelSetValue.Size = new System.Drawing.Size(61, 12);
|
||||
this.labelSetValue.TabIndex = 1;
|
||||
this.labelSetValue.Text = "Set value:";
|
||||
//
|
||||
// labelSetName
|
||||
//
|
||||
this.labelSetName.AutoSize = true;
|
||||
this.labelSetName.Location = new System.Drawing.Point(29, 31);
|
||||
this.labelSetName.Name = "labelSetName";
|
||||
this.labelSetName.Size = new System.Drawing.Size(63, 12);
|
||||
this.labelSetName.TabIndex = 0;
|
||||
this.labelSetName.Text = "Set name:";
|
||||
//
|
||||
// gbImportAndExport
|
||||
//
|
||||
this.gbImportAndExport.Controls.Add(this.btnExport);
|
||||
this.gbImportAndExport.Controls.Add(this.btnImport);
|
||||
this.gbImportAndExport.Location = new System.Drawing.Point(12, 423);
|
||||
this.gbImportAndExport.Name = "gbImportAndExport";
|
||||
this.gbImportAndExport.Size = new System.Drawing.Size(419, 89);
|
||||
this.gbImportAndExport.TabIndex = 2;
|
||||
this.gbImportAndExport.TabStop = false;
|
||||
this.gbImportAndExport.Text = "Import and export";
|
||||
//
|
||||
// btnExport
|
||||
//
|
||||
this.btnExport.Image = global::WelsonJS.Launcher.Properties.Resources.icon_export_32;
|
||||
this.btnExport.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
||||
this.btnExport.Location = new System.Drawing.Point(213, 29);
|
||||
this.btnExport.Name = "btnExport";
|
||||
this.btnExport.Padding = new System.Windows.Forms.Padding(11, 0, 0, 0);
|
||||
this.btnExport.Size = new System.Drawing.Size(176, 40);
|
||||
this.btnExport.TabIndex = 6;
|
||||
this.btnExport.Text = "Export";
|
||||
this.btnExport.UseVisualStyleBackColor = true;
|
||||
this.btnExport.Click += new System.EventHandler(this.btnExport_Click);
|
||||
//
|
||||
// btnImport
|
||||
//
|
||||
this.btnImport.Image = global::WelsonJS.Launcher.Properties.Resources.icon_import_32;
|
||||
this.btnImport.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
||||
this.btnImport.Location = new System.Drawing.Point(31, 29);
|
||||
this.btnImport.Name = "btnImport";
|
||||
this.btnImport.Padding = new System.Windows.Forms.Padding(11, 0, 0, 0);
|
||||
this.btnImport.Size = new System.Drawing.Size(176, 40);
|
||||
this.btnImport.TabIndex = 5;
|
||||
this.btnImport.Text = "Import";
|
||||
this.btnImport.UseVisualStyleBackColor = true;
|
||||
this.btnImport.Click += new System.EventHandler(this.btnImport_Click);
|
||||
//
|
||||
// EnvForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(447, 529);
|
||||
this.Controls.Add(this.gbImportAndExport);
|
||||
this.Controls.Add(this.gbUpdateUserDefinedVariable);
|
||||
this.Controls.Add(this.gbUserDefinedVariables);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
|
||||
this.Icon = global::WelsonJS.Launcher.Properties.Resources.favicon;
|
||||
this.MaximizeBox = false;
|
||||
this.Name = "EnvForm";
|
||||
this.Text = "User-defined variables editor";
|
||||
this.gbUserDefinedVariables.ResumeLayout(false);
|
||||
this.gbUpdateUserDefinedVariable.ResumeLayout(false);
|
||||
this.gbUpdateUserDefinedVariable.PerformLayout();
|
||||
this.gbImportAndExport.ResumeLayout(false);
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.GroupBox gbUserDefinedVariables;
|
||||
private System.Windows.Forms.GroupBox gbUpdateUserDefinedVariable;
|
||||
private System.Windows.Forms.TextBox textSetValue;
|
||||
private System.Windows.Forms.TextBox textSetName;
|
||||
private System.Windows.Forms.Label labelSetValue;
|
||||
private System.Windows.Forms.Label labelSetName;
|
||||
private System.Windows.Forms.Button btnOk;
|
||||
private System.Windows.Forms.Button btnOpenFile;
|
||||
private System.Windows.Forms.Button btnOpenDirectory;
|
||||
private System.Windows.Forms.CheckBox checkDeleteVariable;
|
||||
private System.Windows.Forms.ListView lvUserDefinedVariables;
|
||||
private System.Windows.Forms.ColumnHeader columnHeader1;
|
||||
private System.Windows.Forms.ColumnHeader columnHeader2;
|
||||
private System.Windows.Forms.GroupBox gbImportAndExport;
|
||||
private System.Windows.Forms.Button btnImport;
|
||||
private System.Windows.Forms.Button btnExport;
|
||||
}
|
||||
}
|
295
WelsonJS.Toolkit/WelsonJS.Launcher/EnvForm.cs
Normal file
|
@ -0,0 +1,295 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace WelsonJS.Launcher
|
||||
{
|
||||
public partial class EnvForm : Form
|
||||
{
|
||||
private Dictionary<string, string> userVariables = new Dictionary<string, string>();
|
||||
private string tempFilePath;
|
||||
private readonly Encoding defaultEncoding = Encoding.UTF8;
|
||||
|
||||
public EnvForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
InitializeListView();
|
||||
|
||||
// Set the variable file path
|
||||
tempFilePath = Path.Combine(Program.GetAppDataPath(), "welsonjs_default.env");
|
||||
|
||||
LoadUserVariables(); // Load variables
|
||||
}
|
||||
|
||||
// Initialize ListView
|
||||
private void InitializeListView()
|
||||
{
|
||||
lvUserDefinedVariables.View = View.Details;
|
||||
lvUserDefinedVariables.FullRowSelect = true;
|
||||
lvUserDefinedVariables.Columns[0].Width = 150;
|
||||
lvUserDefinedVariables.Columns[1].Width = 220;
|
||||
lvUserDefinedVariables.SelectedIndexChanged += lvUserDefinedVariables_SelectedIndexChanged;
|
||||
}
|
||||
|
||||
// Load user-defined variables from the temporary folder in .env format
|
||||
private void LoadUserVariables()
|
||||
{
|
||||
if (File.Exists(tempFilePath))
|
||||
{
|
||||
try
|
||||
{
|
||||
string fileContent = File.ReadAllText(tempFilePath);
|
||||
// Split based on new line characters
|
||||
string[] keyValuePairs = fileContent.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
userVariables = new Dictionary<string, string>();
|
||||
|
||||
foreach (string pair in keyValuePairs)
|
||||
{
|
||||
// Split by the first occurrence of '='
|
||||
int indexOfEquals = pair.IndexOf('=');
|
||||
if (indexOfEquals != -1)
|
||||
{
|
||||
string key = pair.Substring(0, indexOfEquals).Trim();
|
||||
string value = pair.Substring(indexOfEquals + 1).Trim();
|
||||
|
||||
// Remove surrounding quotes if present
|
||||
if (value.StartsWith("\"") && value.EndsWith("\""))
|
||||
{
|
||||
value = value.Substring(1, value.Length - 2); // Remove the first and last character
|
||||
}
|
||||
|
||||
// Unescape double quotes in the value
|
||||
value = value.Replace("\\\"", "\"");
|
||||
|
||||
userVariables[key] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show($"Error parsing line: '{pair}'.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"Error loading variable file: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
userVariables = new Dictionary<string, string>();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
userVariables = new Dictionary<string, string>();
|
||||
}
|
||||
UpdateListView();
|
||||
}
|
||||
|
||||
// Update ListView with current variables
|
||||
private void UpdateListView()
|
||||
{
|
||||
lvUserDefinedVariables.Items.Clear();
|
||||
foreach (var variable in userVariables)
|
||||
{
|
||||
var item = new ListViewItem(variable.Key);
|
||||
item.SubItems.Add(variable.Value);
|
||||
lvUserDefinedVariables.Items.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle ListView selection change
|
||||
private void lvUserDefinedVariables_SelectedIndexChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (lvUserDefinedVariables.SelectedItems.Count > 0)
|
||||
{
|
||||
var selectedItem = lvUserDefinedVariables.SelectedItems[0];
|
||||
textSetName.Text = selectedItem.Text;
|
||||
textSetValue.Text = selectedItem.SubItems[1].Text;
|
||||
checkDeleteVariable.Checked = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle OK button click
|
||||
private void btnOk_Click(object sender, EventArgs e)
|
||||
{
|
||||
var name = textSetName.Text.Trim();
|
||||
var value = textSetValue.Text.Trim();
|
||||
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
MessageBox.Show("Please enter a variable name.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (checkDeleteVariable.Checked)
|
||||
{
|
||||
if (userVariables.ContainsKey(name))
|
||||
{
|
||||
userVariables.Remove(name);
|
||||
MessageBox.Show("Variable deleted.", "Info", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show("Variable not found.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
userVariables[name] = value;
|
||||
MessageBox.Show("Variable saved.", "Info", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
}
|
||||
|
||||
UpdateListView();
|
||||
SaveUserVariables(); // Save variables
|
||||
ClearInputFields();
|
||||
}
|
||||
|
||||
// Save user-defined variables to the temporary folder in .env format
|
||||
private void SaveUserVariables()
|
||||
{
|
||||
try
|
||||
{
|
||||
List<string> lines = new List<string>();
|
||||
|
||||
foreach (var variable in userVariables)
|
||||
{
|
||||
// Escape double quotes in the value
|
||||
string value = variable.Value.Replace("\"", "\\\"");
|
||||
|
||||
// Enclose the value in double quotes if it contains spaces
|
||||
if (value.Contains(" "))
|
||||
{
|
||||
value = $"\"{value}\"";
|
||||
}
|
||||
|
||||
// Create the line in the format: KEY=VALUE
|
||||
string line = $"{variable.Key}={value}";
|
||||
lines.Add(line);
|
||||
}
|
||||
|
||||
// Write lines to the file
|
||||
File.WriteAllLines(tempFilePath, lines, defaultEncoding);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"Error saving variable file: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear input fields
|
||||
private void ClearInputFields()
|
||||
{
|
||||
textSetName.Clear();
|
||||
textSetValue.Clear();
|
||||
checkDeleteVariable.Checked = false;
|
||||
}
|
||||
|
||||
// Handle "Open Directory" button click
|
||||
private void btnOpenDirectory_Click(object sender, EventArgs e)
|
||||
{
|
||||
using (var folderDialog = new FolderBrowserDialog())
|
||||
{
|
||||
if (folderDialog.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
textSetValue.Text = folderDialog.SelectedPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle "Open File" button click
|
||||
private void btnOpenFile_Click(object sender, EventArgs e)
|
||||
{
|
||||
using (var fileDialog = new OpenFileDialog())
|
||||
{
|
||||
if (fileDialog.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
textSetName.Text = Path.GetFileName(fileDialog.FileName);
|
||||
textSetValue.Text = fileDialog.FileName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void btnImport_Click(object sender, EventArgs e)
|
||||
{
|
||||
using (var openFileDialog = new OpenFileDialog())
|
||||
{
|
||||
openFileDialog.Filter = "Env files (*.env)|*.env|All files (*.*)|*.*";
|
||||
if (openFileDialog.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Load variables from the selected file
|
||||
string filePath = openFileDialog.FileName;
|
||||
string[] lines = File.ReadAllLines(filePath, defaultEncoding);
|
||||
|
||||
foreach (string line in lines)
|
||||
{
|
||||
// Skip empty lines
|
||||
if (string.IsNullOrWhiteSpace(line)) continue;
|
||||
|
||||
int indexOfEquals = line.IndexOf('=');
|
||||
if (indexOfEquals != -1)
|
||||
{
|
||||
string key = line.Substring(0, indexOfEquals).Trim();
|
||||
string value = line.Substring(indexOfEquals + 1).Trim();
|
||||
|
||||
// Remove surrounding quotes if present
|
||||
if (value.StartsWith("\"") && value.EndsWith("\""))
|
||||
{
|
||||
value = value.Substring(1, value.Length - 2);
|
||||
}
|
||||
|
||||
// Unescape double quotes in the value
|
||||
value = value.Replace("\\\"", "\"");
|
||||
|
||||
// Update or add the key-value pair
|
||||
userVariables[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Save the updated variables to the file
|
||||
SaveUserVariables();
|
||||
UpdateListView(); // Refresh the display
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"Error importing variable file: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void btnExport_Click(object sender, EventArgs e)
|
||||
{
|
||||
SaveFileDialog saveFileDialog = new SaveFileDialog();
|
||||
saveFileDialog.Filter = "Env files (*.env)|*.env|All files (*.*)|*.*";
|
||||
if (saveFileDialog.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Save the current variables to the selected file
|
||||
string filePath = saveFileDialog.FileName;
|
||||
List<string> lines = new List<string>();
|
||||
foreach (var variable in userVariables)
|
||||
{
|
||||
// Escape double quotes in the value
|
||||
string value = variable.Value.Replace("\"", "\\\"");
|
||||
|
||||
// Enclose the value in double quotes if it contains spaces
|
||||
if (value.Contains(" "))
|
||||
{
|
||||
value = $"\"{value}\"";
|
||||
}
|
||||
|
||||
lines.Add($"{variable.Key}={value}");
|
||||
}
|
||||
File.WriteAllLines(filePath, lines, defaultEncoding);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"Error exporting variable file: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
120
WelsonJS.Toolkit/WelsonJS.Launcher/EnvForm.resx
Normal file
|
@ -0,0 +1,120 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
89
WelsonJS.Toolkit/WelsonJS.Launcher/GlobalSettingsForm.Designer.cs
generated
Normal file
|
@ -0,0 +1,89 @@
|
|||
namespace WelsonJS.Launcher
|
||||
{
|
||||
partial class GlobalSettingsForm
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.gbMaxScriptStatements = new System.Windows.Forms.GroupBox();
|
||||
this.btnOkMaxScriptStatements = new System.Windows.Forms.Button();
|
||||
this.txtMaxScriptStatements = new System.Windows.Forms.TextBox();
|
||||
this.gbMaxScriptStatements.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// gbMaxScriptStatements
|
||||
//
|
||||
this.gbMaxScriptStatements.Controls.Add(this.btnOkMaxScriptStatements);
|
||||
this.gbMaxScriptStatements.Controls.Add(this.txtMaxScriptStatements);
|
||||
this.gbMaxScriptStatements.Location = new System.Drawing.Point(12, 12);
|
||||
this.gbMaxScriptStatements.Name = "gbMaxScriptStatements";
|
||||
this.gbMaxScriptStatements.Size = new System.Drawing.Size(290, 67);
|
||||
this.gbMaxScriptStatements.TabIndex = 0;
|
||||
this.gbMaxScriptStatements.TabStop = false;
|
||||
this.gbMaxScriptStatements.Text = "MaxScriptStatements (GUI only)";
|
||||
//
|
||||
// btnOkMaxScriptStatements
|
||||
//
|
||||
this.btnOkMaxScriptStatements.Location = new System.Drawing.Point(218, 30);
|
||||
this.btnOkMaxScriptStatements.Name = "btnOkMaxScriptStatements";
|
||||
this.btnOkMaxScriptStatements.Size = new System.Drawing.Size(57, 21);
|
||||
this.btnOkMaxScriptStatements.TabIndex = 1;
|
||||
this.btnOkMaxScriptStatements.Text = "Ok";
|
||||
this.btnOkMaxScriptStatements.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText;
|
||||
this.btnOkMaxScriptStatements.UseVisualStyleBackColor = true;
|
||||
this.btnOkMaxScriptStatements.Click += new System.EventHandler(this.btnOkMaxScriptStatements_Click);
|
||||
//
|
||||
// txtMaxScriptStatements
|
||||
//
|
||||
this.txtMaxScriptStatements.Location = new System.Drawing.Point(15, 30);
|
||||
this.txtMaxScriptStatements.Name = "txtMaxScriptStatements";
|
||||
this.txtMaxScriptStatements.Size = new System.Drawing.Size(197, 21);
|
||||
this.txtMaxScriptStatements.TabIndex = 1;
|
||||
//
|
||||
// GlobalSettingsForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(315, 92);
|
||||
this.Controls.Add(this.gbMaxScriptStatements);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
|
||||
this.Icon = global::WelsonJS.Launcher.Properties.Resources.favicon;
|
||||
this.MaximizeBox = false;
|
||||
this.Name = "GlobalSettingsForm";
|
||||
this.Text = "Global settings...";
|
||||
this.gbMaxScriptStatements.ResumeLayout(false);
|
||||
this.gbMaxScriptStatements.PerformLayout();
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.GroupBox gbMaxScriptStatements;
|
||||
private System.Windows.Forms.TextBox txtMaxScriptStatements;
|
||||
private System.Windows.Forms.Button btnOkMaxScriptStatements;
|
||||
}
|
||||
}
|
56
WelsonJS.Toolkit/WelsonJS.Launcher/GlobalSettingsForm.cs
Normal file
|
@ -0,0 +1,56 @@
|
|||
using Microsoft.Win32;
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace WelsonJS.Launcher
|
||||
{
|
||||
public partial class GlobalSettingsForm : Form
|
||||
{
|
||||
private const string RegistryPath = "Software\\Microsoft\\Internet Explorer\\Styles";
|
||||
private const string RegistryKey = "MaxScriptStatements";
|
||||
|
||||
public GlobalSettingsForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
LoadRegistryValue();
|
||||
}
|
||||
|
||||
private void LoadRegistryValue()
|
||||
{
|
||||
using (RegistryKey key = Registry.CurrentUser.OpenSubKey(RegistryPath))
|
||||
{
|
||||
if (key != null)
|
||||
{
|
||||
string value = key.GetValue(RegistryKey)?.ToString();
|
||||
if (value != null && int.TryParse(value, out int maxStatements))
|
||||
{
|
||||
txtMaxScriptStatements.Text = maxStatements.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void btnOkMaxScriptStatements_Click(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (int.TryParse(txtMaxScriptStatements.Text, out int maxStatements))
|
||||
{
|
||||
using (RegistryKey key = Registry.CurrentUser.CreateSubKey(RegistryPath))
|
||||
{
|
||||
key.SetValue(RegistryKey, maxStatements, RegistryValueKind.DWord);
|
||||
}
|
||||
MessageBox.Show($"MaxScriptStatements setting has been changed to {maxStatements}.", "Confirmation", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show("Please enter a valid number within the DWORD range.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"An error occurred while trying to change the MaxScriptStatements setting: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
120
WelsonJS.Toolkit/WelsonJS.Launcher/GlobalSettingsForm.resx
Normal file
|
@ -0,0 +1,120 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
25
WelsonJS.Toolkit/WelsonJS.Launcher/IResourceTool.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WelsonJS.Launcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a contract for resource tools that can handle specific HTTP requests.
|
||||
/// </summary>
|
||||
public interface IResourceTool
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether this tool can handle the specified path.
|
||||
/// </summary>
|
||||
/// <param name="path">The request path to check.</param>
|
||||
/// <returns>True if this tool can handle the request; otherwise, false.</returns>
|
||||
bool CanHandle(string path);
|
||||
/// <summary>
|
||||
/// Asynchronously processes the HTTP request for the specified path.
|
||||
/// </summary>
|
||||
/// <param name="context">The HTTP listener context containing request and response objects.</param>
|
||||
/// <param name="path">The request path to handle.</param>
|
||||
/// <returns>A task representing the asynchronous operation.</returns>
|
||||
Task HandleAsync(HttpListenerContext context, string path);
|
||||
}
|
||||
}
|
170
WelsonJS.Toolkit/WelsonJS.Launcher/InstancesForm.Designer.cs
generated
Normal file
|
@ -0,0 +1,170 @@
|
|||
namespace WelsonJS.Launcher
|
||||
{
|
||||
partial class InstancesForm
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.lvInstances = new System.Windows.Forms.ListView();
|
||||
this.chInstanceId = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||
this.chFirstDeployTime = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||
this.btnDelete = new System.Windows.Forms.Button();
|
||||
this.btnOpenWithExplorer = new System.Windows.Forms.Button();
|
||||
this.cbInteractiveServiceApp = new System.Windows.Forms.CheckBox();
|
||||
this.txtUseSpecificScript = new System.Windows.Forms.TextBox();
|
||||
this.cbUseSpecificScript = new System.Windows.Forms.CheckBox();
|
||||
this.btnStart = new System.Windows.Forms.Button();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// lvInstances
|
||||
//
|
||||
this.lvInstances.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
|
||||
this.chInstanceId,
|
||||
this.chFirstDeployTime});
|
||||
this.lvInstances.HideSelection = false;
|
||||
this.lvInstances.Location = new System.Drawing.Point(8, 8);
|
||||
this.lvInstances.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.lvInstances.Name = "lvInstances";
|
||||
this.lvInstances.Size = new System.Drawing.Size(384, 170);
|
||||
this.lvInstances.TabIndex = 0;
|
||||
this.lvInstances.UseCompatibleStateImageBehavior = false;
|
||||
this.lvInstances.View = System.Windows.Forms.View.Details;
|
||||
//
|
||||
// chInstanceId
|
||||
//
|
||||
this.chInstanceId.Text = "Instance ID";
|
||||
this.chInstanceId.Width = 220;
|
||||
//
|
||||
// chFirstDeployTime
|
||||
//
|
||||
this.chFirstDeployTime.Text = "First Deploy Time";
|
||||
this.chFirstDeployTime.Width = 160;
|
||||
//
|
||||
// btnDelete
|
||||
//
|
||||
this.btnDelete.Image = global::WelsonJS.Launcher.Properties.Resources.icon_delete_32;
|
||||
this.btnDelete.Location = new System.Drawing.Point(120, 243);
|
||||
this.btnDelete.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.btnDelete.Name = "btnDelete";
|
||||
this.btnDelete.Size = new System.Drawing.Size(105, 40);
|
||||
this.btnDelete.TabIndex = 2;
|
||||
this.btnDelete.Text = "Delete";
|
||||
this.btnDelete.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText;
|
||||
this.btnDelete.UseVisualStyleBackColor = true;
|
||||
this.btnDelete.Click += new System.EventHandler(this.btnDelete_Click);
|
||||
//
|
||||
// btnOpenWithExplorer
|
||||
//
|
||||
this.btnOpenWithExplorer.Image = global::WelsonJS.Launcher.Properties.Resources.icon_directory_32;
|
||||
this.btnOpenWithExplorer.Location = new System.Drawing.Point(229, 243);
|
||||
this.btnOpenWithExplorer.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.btnOpenWithExplorer.Name = "btnOpenWithExplorer";
|
||||
this.btnOpenWithExplorer.Size = new System.Drawing.Size(162, 40);
|
||||
this.btnOpenWithExplorer.TabIndex = 3;
|
||||
this.btnOpenWithExplorer.Text = "Open with Explorer";
|
||||
this.btnOpenWithExplorer.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText;
|
||||
this.btnOpenWithExplorer.UseVisualStyleBackColor = true;
|
||||
this.btnOpenWithExplorer.Click += new System.EventHandler(this.btnOpenWithExplorer_Click);
|
||||
//
|
||||
// cbInteractiveServiceApp
|
||||
//
|
||||
this.cbInteractiveServiceApp.AutoSize = true;
|
||||
this.cbInteractiveServiceApp.Location = new System.Drawing.Point(10, 215);
|
||||
this.cbInteractiveServiceApp.Name = "cbInteractiveServiceApp";
|
||||
this.cbInteractiveServiceApp.Size = new System.Drawing.Size(254, 16);
|
||||
this.cbInteractiveServiceApp.TabIndex = 9;
|
||||
this.cbInteractiveServiceApp.Text = "This is an Interactive Service Application";
|
||||
this.cbInteractiveServiceApp.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// txtUseSpecificScript
|
||||
//
|
||||
this.txtUseSpecificScript.Enabled = false;
|
||||
this.txtUseSpecificScript.Location = new System.Drawing.Point(199, 189);
|
||||
this.txtUseSpecificScript.Name = "txtUseSpecificScript";
|
||||
this.txtUseSpecificScript.Size = new System.Drawing.Size(110, 21);
|
||||
this.txtUseSpecificScript.TabIndex = 8;
|
||||
//
|
||||
// cbUseSpecificScript
|
||||
//
|
||||
this.cbUseSpecificScript.AutoSize = true;
|
||||
this.cbUseSpecificScript.Location = new System.Drawing.Point(10, 192);
|
||||
this.cbUseSpecificScript.Name = "cbUseSpecificScript";
|
||||
this.cbUseSpecificScript.Size = new System.Drawing.Size(184, 16);
|
||||
this.cbUseSpecificScript.TabIndex = 7;
|
||||
this.cbUseSpecificScript.Text = "I want to use specific script ";
|
||||
this.cbUseSpecificScript.UseVisualStyleBackColor = true;
|
||||
this.cbUseSpecificScript.CheckedChanged += new System.EventHandler(this.cbUseSpecificScript_CheckedChanged);
|
||||
//
|
||||
// btnStart
|
||||
//
|
||||
this.btnStart.Image = global::WelsonJS.Launcher.Properties.Resources.icon_start_32;
|
||||
this.btnStart.Location = new System.Drawing.Point(10, 243);
|
||||
this.btnStart.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.btnStart.Name = "btnStart";
|
||||
this.btnStart.Size = new System.Drawing.Size(105, 40);
|
||||
this.btnStart.TabIndex = 1;
|
||||
this.btnStart.Text = "Start";
|
||||
this.btnStart.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText;
|
||||
this.btnStart.UseVisualStyleBackColor = true;
|
||||
this.btnStart.Click += new System.EventHandler(this.btnStart_Click);
|
||||
//
|
||||
// InstancesForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(401, 296);
|
||||
this.Controls.Add(this.btnStart);
|
||||
this.Controls.Add(this.lvInstances);
|
||||
this.Controls.Add(this.btnDelete);
|
||||
this.Controls.Add(this.cbUseSpecificScript);
|
||||
this.Controls.Add(this.cbInteractiveServiceApp);
|
||||
this.Controls.Add(this.btnOpenWithExplorer);
|
||||
this.Controls.Add(this.txtUseSpecificScript);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
|
||||
this.Icon = global::WelsonJS.Launcher.Properties.Resources.favicon;
|
||||
this.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.MaximizeBox = false;
|
||||
this.Name = "InstancesForm";
|
||||
this.Text = "Instances";
|
||||
this.Load += new System.EventHandler(this.InstancesForm_Load);
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.ListView lvInstances;
|
||||
private System.Windows.Forms.Button btnDelete;
|
||||
private System.Windows.Forms.Button btnOpenWithExplorer;
|
||||
private System.Windows.Forms.CheckBox cbInteractiveServiceApp;
|
||||
private System.Windows.Forms.TextBox txtUseSpecificScript;
|
||||
private System.Windows.Forms.CheckBox cbUseSpecificScript;
|
||||
private System.Windows.Forms.ColumnHeader chInstanceId;
|
||||
private System.Windows.Forms.ColumnHeader chFirstDeployTime;
|
||||
private System.Windows.Forms.Button btnStart;
|
||||
}
|
||||
}
|
141
WelsonJS.Toolkit/WelsonJS.Launcher/InstancesForm.cs
Normal file
|
@ -0,0 +1,141 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace WelsonJS.Launcher
|
||||
{
|
||||
public partial class InstancesForm : Form
|
||||
{
|
||||
private string entryFileName;
|
||||
private string scriptName;
|
||||
private const string timestampFormat = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
public InstancesForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
entryFileName = "bootstrap.bat";
|
||||
}
|
||||
|
||||
private void InstancesForm_Load(object sender, EventArgs e)
|
||||
{
|
||||
lvInstances.Items.Clear();
|
||||
LoadInstances(Program.GetAppDataPath());
|
||||
LoadInstances(Path.GetTempPath());
|
||||
}
|
||||
|
||||
private void LoadInstances(string instancesRoot)
|
||||
{
|
||||
if (!Directory.Exists(instancesRoot))
|
||||
return;
|
||||
|
||||
foreach (string dir in Directory.GetDirectories(instancesRoot))
|
||||
{
|
||||
string timestampFile = Path.Combine(dir, ".welsonjs_first_deploy_time");
|
||||
string entryScriptFile = Path.Combine(dir, "app.js");
|
||||
string firstDeployTime = null;
|
||||
|
||||
if (File.Exists(timestampFile)
|
||||
&& DateTime.TryParse(File.ReadAllText(timestampFile).Trim(), out DateTime parsedTimestamp))
|
||||
{
|
||||
firstDeployTime = parsedTimestamp.ToString(timestampFormat);
|
||||
}
|
||||
else if (File.Exists(entryScriptFile))
|
||||
{
|
||||
firstDeployTime = File.GetCreationTime(entryScriptFile).ToString(timestampFormat);
|
||||
}
|
||||
|
||||
if (firstDeployTime != null)
|
||||
{
|
||||
lvInstances.Items.Add(new ListViewItem(new[]
|
||||
{
|
||||
Path.GetFileName(dir),
|
||||
firstDeployTime
|
||||
})
|
||||
{
|
||||
Tag = dir
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void btnStart_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (lvInstances.SelectedItems.Count > 0)
|
||||
{
|
||||
scriptName = txtUseSpecificScript.Text;
|
||||
|
||||
string instanceId = lvInstances.SelectedItems[0].Text;
|
||||
string workingDirectory = Program.GetWorkingDirectory(instanceId, true);
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// Run the appliction
|
||||
Program.RunCommandPrompt(workingDirectory, entryFileName, scriptName, cbUseSpecificScript.Checked, cbInteractiveServiceApp.Checked);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show(ex.Message);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show("No selected an instance");
|
||||
}
|
||||
}
|
||||
|
||||
private void btnDelete_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (lvInstances.SelectedItems.Count > 0)
|
||||
{
|
||||
string instanceId = lvInstances.SelectedItems[0].Text;
|
||||
string workingDirectory = Program.GetWorkingDirectory(instanceId, false);
|
||||
|
||||
if (!Directory.Exists(workingDirectory))
|
||||
{
|
||||
workingDirectory = Path.Combine(Path.GetTempPath(), instanceId);
|
||||
}
|
||||
|
||||
if (Directory.Exists(workingDirectory))
|
||||
{
|
||||
Directory.Delete(workingDirectory, true);
|
||||
|
||||
lvInstances.Items.Clear();
|
||||
LoadInstances(Program.GetAppDataPath());
|
||||
LoadInstances(Path.GetTempPath());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show("No selected an instance");
|
||||
}
|
||||
}
|
||||
|
||||
private void btnOpenWithExplorer_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (lvInstances.SelectedItems.Count > 0)
|
||||
{
|
||||
string instanceId = lvInstances.SelectedItems[0].Text;
|
||||
string workingDirectory = Program.GetWorkingDirectory(instanceId, true);
|
||||
|
||||
if (Directory.Exists(workingDirectory))
|
||||
{
|
||||
System.Diagnostics.Process.Start("explorer", workingDirectory);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show("No selected an instance");
|
||||
}
|
||||
}
|
||||
|
||||
private void cbUseSpecificScript_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
txtUseSpecificScript.Enabled = cbUseSpecificScript.Checked;
|
||||
}
|
||||
}
|
||||
}
|
120
WelsonJS.Toolkit/WelsonJS.Launcher/InstancesForm.resx
Normal file
|
@ -0,0 +1,120 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
279
WelsonJS.Toolkit/WelsonJS.Launcher/MainForm.Designer.cs
generated
Normal file
|
@ -0,0 +1,279 @@
|
|||
namespace WelsonJS.Launcher
|
||||
{
|
||||
partial class MainForm
|
||||
{
|
||||
/// <summary>
|
||||
/// 필수 디자이너 변수입니다.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// 사용 중인 모든 리소스를 정리합니다.
|
||||
/// </summary>
|
||||
/// <param name="disposing">관리되는 리소스를 삭제해야 하면 true이고, 그렇지 않으면 false입니다.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form 디자이너에서 생성한 코드
|
||||
|
||||
/// <summary>
|
||||
/// 디자이너 지원에 필요한 메서드입니다.
|
||||
/// 이 메서드의 내용을 코드 편집기로 수정하지 마세요.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.components = new System.ComponentModel.Container();
|
||||
this.btnRunFromZipFile = new System.Windows.Forms.Button();
|
||||
this.btnRunFromExternalLink = new System.Windows.Forms.Button();
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.linkLabel1 = new System.Windows.Forms.LinkLabel();
|
||||
this.cbUseSpecificScript = new System.Windows.Forms.CheckBox();
|
||||
this.txtUseSpecificScript = new System.Windows.Forms.TextBox();
|
||||
this.cbInteractiveServiceApp = new System.Windows.Forms.CheckBox();
|
||||
this.menuStrip1 = new System.Windows.Forms.MenuStrip();
|
||||
this.settingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.userdefinedVariablesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.instancesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.runAsAdministratorToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.globalSettingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.startCodeEditorToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.openCopilotToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.notifyIcon1 = new System.Windows.Forms.NotifyIcon(this.components);
|
||||
this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components);
|
||||
this.openLauncherToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.openCodeEditorToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.menuStrip1.SuspendLayout();
|
||||
this.contextMenuStrip1.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// btnRunFromZipFile
|
||||
//
|
||||
this.btnRunFromZipFile.Image = global::WelsonJS.Launcher.Properties.Resources.icon_zip_128;
|
||||
this.btnRunFromZipFile.Location = new System.Drawing.Point(24, 67);
|
||||
this.btnRunFromZipFile.Name = "btnRunFromZipFile";
|
||||
this.btnRunFromZipFile.Size = new System.Drawing.Size(200, 200);
|
||||
this.btnRunFromZipFile.TabIndex = 0;
|
||||
this.btnRunFromZipFile.Text = "From ZIP file...";
|
||||
this.btnRunFromZipFile.TextAlign = System.Drawing.ContentAlignment.BottomCenter;
|
||||
this.btnRunFromZipFile.UseVisualStyleBackColor = true;
|
||||
this.btnRunFromZipFile.Click += new System.EventHandler(this.btnRunFromZipFile_Click);
|
||||
//
|
||||
// btnRunFromExternalLink
|
||||
//
|
||||
this.btnRunFromExternalLink.Image = global::WelsonJS.Launcher.Properties.Resources.icon_link_128;
|
||||
this.btnRunFromExternalLink.Location = new System.Drawing.Point(230, 67);
|
||||
this.btnRunFromExternalLink.Name = "btnRunFromExternalLink";
|
||||
this.btnRunFromExternalLink.Size = new System.Drawing.Size(200, 200);
|
||||
this.btnRunFromExternalLink.TabIndex = 1;
|
||||
this.btnRunFromExternalLink.Text = "From external link...";
|
||||
this.btnRunFromExternalLink.TextAlign = System.Drawing.ContentAlignment.BottomCenter;
|
||||
this.btnRunFromExternalLink.UseVisualStyleBackColor = true;
|
||||
this.btnRunFromExternalLink.Click += new System.EventHandler(this.btnRunFromExternalLink_Click);
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.AutoSize = true;
|
||||
this.label1.Location = new System.Drawing.Point(24, 41);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(312, 12);
|
||||
this.label1.TabIndex = 2;
|
||||
this.label1.Text = "Choose the location of WelsonJS application package.";
|
||||
//
|
||||
// linkLabel1
|
||||
//
|
||||
this.linkLabel1.AutoSize = true;
|
||||
this.linkLabel1.Location = new System.Drawing.Point(24, 345);
|
||||
this.linkLabel1.Name = "linkLabel1";
|
||||
this.linkLabel1.Size = new System.Drawing.Size(219, 12);
|
||||
this.linkLabel1.TabIndex = 3;
|
||||
this.linkLabel1.TabStop = true;
|
||||
this.linkLabel1.Text = "https://github.com/gnh1201/welsonjs";
|
||||
this.linkLabel1.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel1_LinkClicked);
|
||||
//
|
||||
// cbUseSpecificScript
|
||||
//
|
||||
this.cbUseSpecificScript.AutoSize = true;
|
||||
this.cbUseSpecificScript.Location = new System.Drawing.Point(26, 281);
|
||||
this.cbUseSpecificScript.Name = "cbUseSpecificScript";
|
||||
this.cbUseSpecificScript.Size = new System.Drawing.Size(184, 16);
|
||||
this.cbUseSpecificScript.TabIndex = 4;
|
||||
this.cbUseSpecificScript.Text = "I want to use specific script ";
|
||||
this.cbUseSpecificScript.UseVisualStyleBackColor = true;
|
||||
this.cbUseSpecificScript.CheckedChanged += new System.EventHandler(this.cbUseSpecificScript_CheckedChanged);
|
||||
//
|
||||
// txtUseSpecificScript
|
||||
//
|
||||
this.txtUseSpecificScript.Enabled = false;
|
||||
this.txtUseSpecificScript.Location = new System.Drawing.Point(214, 278);
|
||||
this.txtUseSpecificScript.Name = "txtUseSpecificScript";
|
||||
this.txtUseSpecificScript.Size = new System.Drawing.Size(110, 21);
|
||||
this.txtUseSpecificScript.TabIndex = 5;
|
||||
//
|
||||
// cbInteractiveServiceApp
|
||||
//
|
||||
this.cbInteractiveServiceApp.AutoSize = true;
|
||||
this.cbInteractiveServiceApp.Location = new System.Drawing.Point(26, 305);
|
||||
this.cbInteractiveServiceApp.Name = "cbInteractiveServiceApp";
|
||||
this.cbInteractiveServiceApp.Size = new System.Drawing.Size(254, 16);
|
||||
this.cbInteractiveServiceApp.TabIndex = 6;
|
||||
this.cbInteractiveServiceApp.Text = "This is an Interactive Service Application";
|
||||
this.cbInteractiveServiceApp.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// menuStrip1
|
||||
//
|
||||
this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.settingsToolStripMenuItem});
|
||||
this.menuStrip1.Location = new System.Drawing.Point(0, 0);
|
||||
this.menuStrip1.Name = "menuStrip1";
|
||||
this.menuStrip1.Size = new System.Drawing.Size(461, 24);
|
||||
this.menuStrip1.TabIndex = 7;
|
||||
this.menuStrip1.Text = "menuStrip1";
|
||||
//
|
||||
// settingsToolStripMenuItem
|
||||
//
|
||||
this.settingsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.userdefinedVariablesToolStripMenuItem,
|
||||
this.instancesToolStripMenuItem,
|
||||
this.runAsAdministratorToolStripMenuItem,
|
||||
this.globalSettingsToolStripMenuItem,
|
||||
this.startCodeEditorToolStripMenuItem,
|
||||
this.openCopilotToolStripMenuItem});
|
||||
this.settingsToolStripMenuItem.Name = "settingsToolStripMenuItem";
|
||||
this.settingsToolStripMenuItem.Size = new System.Drawing.Size(62, 20);
|
||||
this.settingsToolStripMenuItem.Text = "Settings";
|
||||
//
|
||||
// userdefinedVariablesToolStripMenuItem
|
||||
//
|
||||
this.userdefinedVariablesToolStripMenuItem.Name = "userdefinedVariablesToolStripMenuItem";
|
||||
this.userdefinedVariablesToolStripMenuItem.Size = new System.Drawing.Size(196, 22);
|
||||
this.userdefinedVariablesToolStripMenuItem.Text = "User-defined variables";
|
||||
this.userdefinedVariablesToolStripMenuItem.Click += new System.EventHandler(this.userdefinedVariablesToolStripMenuItem_Click);
|
||||
//
|
||||
// instancesToolStripMenuItem
|
||||
//
|
||||
this.instancesToolStripMenuItem.Name = "instancesToolStripMenuItem";
|
||||
this.instancesToolStripMenuItem.Size = new System.Drawing.Size(196, 22);
|
||||
this.instancesToolStripMenuItem.Text = "Instances";
|
||||
this.instancesToolStripMenuItem.Click += new System.EventHandler(this.instancesToolStripMenuItem_Click);
|
||||
//
|
||||
// runAsAdministratorToolStripMenuItem
|
||||
//
|
||||
this.runAsAdministratorToolStripMenuItem.Name = "runAsAdministratorToolStripMenuItem";
|
||||
this.runAsAdministratorToolStripMenuItem.Size = new System.Drawing.Size(196, 22);
|
||||
this.runAsAdministratorToolStripMenuItem.Text = "Run as Administrator...";
|
||||
this.runAsAdministratorToolStripMenuItem.Click += new System.EventHandler(this.runAsAdministratorToolStripMenuItem_Click);
|
||||
//
|
||||
// globalSettingsToolStripMenuItem
|
||||
//
|
||||
this.globalSettingsToolStripMenuItem.Name = "globalSettingsToolStripMenuItem";
|
||||
this.globalSettingsToolStripMenuItem.Size = new System.Drawing.Size(196, 22);
|
||||
this.globalSettingsToolStripMenuItem.Text = "Global settings...";
|
||||
this.globalSettingsToolStripMenuItem.Click += new System.EventHandler(this.globalSettingsToolStripMenuItem_Click);
|
||||
//
|
||||
// startCodeEditorToolStripMenuItem
|
||||
//
|
||||
this.startCodeEditorToolStripMenuItem.Name = "startCodeEditorToolStripMenuItem";
|
||||
this.startCodeEditorToolStripMenuItem.Size = new System.Drawing.Size(196, 22);
|
||||
this.startCodeEditorToolStripMenuItem.Text = "Start the code editor...";
|
||||
this.startCodeEditorToolStripMenuItem.Click += new System.EventHandler(this.startCodeEditorToolStripMenuItem_Click);
|
||||
//
|
||||
// openCopilotToolStripMenuItem
|
||||
//
|
||||
this.openCopilotToolStripMenuItem.Name = "openCopilotToolStripMenuItem";
|
||||
this.openCopilotToolStripMenuItem.Size = new System.Drawing.Size(196, 22);
|
||||
this.openCopilotToolStripMenuItem.Text = "Open the Copilot...";
|
||||
this.openCopilotToolStripMenuItem.Click += new System.EventHandler(this.openCopilotToolStripMenuItem_Click);
|
||||
//
|
||||
// notifyIcon1
|
||||
//
|
||||
this.notifyIcon1.ContextMenuStrip = this.contextMenuStrip1;
|
||||
this.notifyIcon1.Icon = global::WelsonJS.Launcher.Properties.Resources.favicon;
|
||||
this.notifyIcon1.Text = "WelsonJS Launcher";
|
||||
//
|
||||
// contextMenuStrip1
|
||||
//
|
||||
this.contextMenuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.openLauncherToolStripMenuItem,
|
||||
this.openCodeEditorToolStripMenuItem,
|
||||
this.exitToolStripMenuItem});
|
||||
this.contextMenuStrip1.Name = "contextMenuStrip1";
|
||||
this.contextMenuStrip1.Size = new System.Drawing.Size(199, 70);
|
||||
//
|
||||
// openLauncherToolStripMenuItem
|
||||
//
|
||||
this.openLauncherToolStripMenuItem.Name = "openLauncherToolStripMenuItem";
|
||||
this.openLauncherToolStripMenuItem.Size = new System.Drawing.Size(198, 22);
|
||||
this.openLauncherToolStripMenuItem.Text = "Open the launcher...";
|
||||
//
|
||||
// openCodeEditorToolStripMenuItem
|
||||
//
|
||||
this.openCodeEditorToolStripMenuItem.Name = "openCodeEditorToolStripMenuItem";
|
||||
this.openCodeEditorToolStripMenuItem.Size = new System.Drawing.Size(198, 22);
|
||||
this.openCodeEditorToolStripMenuItem.Text = "Open the code editor...";
|
||||
this.openCodeEditorToolStripMenuItem.Click += new System.EventHandler(this.openCodeEditorToolStripMenuItem_Click);
|
||||
//
|
||||
// exitToolStripMenuItem
|
||||
//
|
||||
this.exitToolStripMenuItem.Name = "exitToolStripMenuItem";
|
||||
this.exitToolStripMenuItem.Size = new System.Drawing.Size(198, 22);
|
||||
this.exitToolStripMenuItem.Text = "Exit";
|
||||
//
|
||||
// MainForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(461, 382);
|
||||
this.Controls.Add(this.cbInteractiveServiceApp);
|
||||
this.Controls.Add(this.txtUseSpecificScript);
|
||||
this.Controls.Add(this.cbUseSpecificScript);
|
||||
this.Controls.Add(this.linkLabel1);
|
||||
this.Controls.Add(this.label1);
|
||||
this.Controls.Add(this.btnRunFromExternalLink);
|
||||
this.Controls.Add(this.btnRunFromZipFile);
|
||||
this.Controls.Add(this.menuStrip1);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
|
||||
this.Icon = global::WelsonJS.Launcher.Properties.Resources.favicon;
|
||||
this.MainMenuStrip = this.menuStrip1;
|
||||
this.MaximizeBox = false;
|
||||
this.Name = "MainForm";
|
||||
this.Text = "WelsonJS Application Launcher";
|
||||
this.menuStrip1.ResumeLayout(false);
|
||||
this.menuStrip1.PerformLayout();
|
||||
this.contextMenuStrip1.ResumeLayout(false);
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.Button btnRunFromZipFile;
|
||||
private System.Windows.Forms.Button btnRunFromExternalLink;
|
||||
private System.Windows.Forms.Label label1;
|
||||
private System.Windows.Forms.LinkLabel linkLabel1;
|
||||
private System.Windows.Forms.CheckBox cbUseSpecificScript;
|
||||
private System.Windows.Forms.TextBox txtUseSpecificScript;
|
||||
private System.Windows.Forms.CheckBox cbInteractiveServiceApp;
|
||||
private System.Windows.Forms.MenuStrip menuStrip1;
|
||||
private System.Windows.Forms.ToolStripMenuItem settingsToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem userdefinedVariablesToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem instancesToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem runAsAdministratorToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem globalSettingsToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem startCodeEditorToolStripMenuItem;
|
||||
private System.Windows.Forms.NotifyIcon notifyIcon1;
|
||||
private System.Windows.Forms.ContextMenuStrip contextMenuStrip1;
|
||||
private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem openCodeEditorToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem openLauncherToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem openCopilotToolStripMenuItem;
|
||||
}
|
||||
}
|
||||
|
275
WelsonJS.Toolkit/WelsonJS.Launcher/MainForm.cs
Normal file
|
@ -0,0 +1,275 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Security.Principal;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace WelsonJS.Launcher
|
||||
{
|
||||
public partial class MainForm : Form
|
||||
{
|
||||
private string workingDirectory;
|
||||
private string instanceId;
|
||||
private string entryFileName;
|
||||
private string scriptName;
|
||||
|
||||
public MainForm()
|
||||
{
|
||||
entryFileName = "bootstrap.bat";
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
if (IsInAdministrator())
|
||||
{
|
||||
Text = Text + " (Administrator)";
|
||||
}
|
||||
|
||||
notifyIcon1.DoubleClick += OnShow;
|
||||
openLauncherToolStripMenuItem.Click += OnShow;
|
||||
exitToolStripMenuItem.Click += OnExit;
|
||||
}
|
||||
|
||||
protected override void OnFormClosing(FormClosingEventArgs e)
|
||||
{
|
||||
if (e.CloseReason == CloseReason.UserClosing)
|
||||
{
|
||||
e.Cancel = true;
|
||||
this.Hide();
|
||||
this.ShowInTaskbar = false;
|
||||
notifyIcon1.Visible = true;
|
||||
}
|
||||
base.OnFormClosing(e);
|
||||
}
|
||||
|
||||
private void OnShow(object sender, EventArgs e)
|
||||
{
|
||||
this.Show();
|
||||
this.WindowState = FormWindowState.Normal;
|
||||
this.ShowInTaskbar = true;
|
||||
this.Focus();
|
||||
notifyIcon1.Visible = false;
|
||||
}
|
||||
|
||||
private void OnExit(object sender, EventArgs e)
|
||||
{
|
||||
notifyIcon1.Visible = false;
|
||||
Application.Exit();
|
||||
}
|
||||
|
||||
private void EnableUI()
|
||||
{
|
||||
label1.Text = "Choose the location of WelsonJS application package.";
|
||||
btnRunFromZipFile.Enabled = true;
|
||||
btnRunFromExternalLink.Enabled = true;
|
||||
cbUseSpecificScript.Enabled = true;
|
||||
cbInteractiveServiceApp.Enabled = true;
|
||||
if (cbUseSpecificScript.Checked)
|
||||
{
|
||||
txtUseSpecificScript.Enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void DisableUI()
|
||||
{
|
||||
label1.Text = "Please wait...";
|
||||
btnRunFromZipFile.Enabled = false;
|
||||
btnRunFromExternalLink.Enabled = false;
|
||||
cbUseSpecificScript.Enabled = false;
|
||||
cbInteractiveServiceApp.Enabled = false;
|
||||
txtUseSpecificScript.Enabled = false;
|
||||
}
|
||||
|
||||
private void SafeInvoke(Action action)
|
||||
{
|
||||
if (InvokeRequired)
|
||||
{
|
||||
Invoke(action);
|
||||
}
|
||||
else
|
||||
{
|
||||
action();
|
||||
}
|
||||
}
|
||||
|
||||
private void btnRunFromExternalLink_Click(object sender, EventArgs e)
|
||||
{
|
||||
MessageBox.Show("Coming soon...!");
|
||||
}
|
||||
|
||||
private void btnRunFromZipFile_Click(object sender, EventArgs e)
|
||||
{
|
||||
using (var openFileDialog = new OpenFileDialog())
|
||||
{
|
||||
openFileDialog.Filter = "zip files (*.zip)|*.zip|All files (*.*)|*.*";
|
||||
openFileDialog.FilterIndex = 2;
|
||||
openFileDialog.RestoreDirectory = true;
|
||||
|
||||
if (openFileDialog.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
string filePath = openFileDialog.FileName;
|
||||
ExtractAndRun(filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ExtractAndRun(string filePath)
|
||||
{
|
||||
instanceId = Guid.NewGuid().ToString();
|
||||
workingDirectory = Program.GetWorkingDirectory(instanceId);
|
||||
scriptName = txtUseSpecificScript.Text;
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// If exists, delete all
|
||||
if (Directory.Exists(workingDirectory))
|
||||
{
|
||||
Directory.Delete(workingDirectory, true);
|
||||
}
|
||||
|
||||
// try to extact ZIP file
|
||||
ZipFile.ExtractToDirectory(filePath, workingDirectory);
|
||||
|
||||
// record the first deploy time
|
||||
RecordFirstDeployTime(workingDirectory);
|
||||
|
||||
// follow the sub-directory
|
||||
workingDirectory = Program.GetWorkingDirectory(instanceId, true);
|
||||
|
||||
// Run the appliction
|
||||
Program.RunCommandPrompt(workingDirectory, entryFileName, scriptName, cbUseSpecificScript.Checked, cbInteractiveServiceApp.Checked);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SafeInvoke(() =>
|
||||
{
|
||||
MessageBox.Show("Error: " + ex.Message);
|
||||
});
|
||||
}
|
||||
|
||||
// Enable UI
|
||||
SafeInvoke(() => {
|
||||
EnableUI();
|
||||
});
|
||||
});
|
||||
|
||||
DisableUI();
|
||||
}
|
||||
|
||||
private void RecordFirstDeployTime(string directory)
|
||||
{
|
||||
try
|
||||
{
|
||||
string filePath = Path.Combine(directory, ".welsonjs_first_deploy_time");
|
||||
string text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
File.WriteAllText(filePath, text);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"Failed to record first deploy time: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsInAdministrator()
|
||||
{
|
||||
try
|
||||
{
|
||||
WindowsPrincipal wp = new WindowsPrincipal(WindowsIdentity.GetCurrent());
|
||||
return wp.IsInRole(WindowsBuiltInRole.Administrator);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void cbUseSpecificScript_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
txtUseSpecificScript.Enabled = cbUseSpecificScript.Checked;
|
||||
}
|
||||
|
||||
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
|
||||
{
|
||||
Program.OpenWebBrowser(Program.GetAppConfig("RepositoryUrl"));
|
||||
}
|
||||
|
||||
private void userdefinedVariablesToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
(new EnvForm()).Show();
|
||||
}
|
||||
|
||||
private void instancesToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
(new InstancesForm()).Show();
|
||||
}
|
||||
|
||||
private void runAsAdministratorToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (!IsInAdministrator())
|
||||
{
|
||||
ProcessStartInfo procInfo = new ProcessStartInfo
|
||||
{
|
||||
UseShellExecute = true,
|
||||
FileName = Application.ExecutablePath,
|
||||
WorkingDirectory = Environment.CurrentDirectory,
|
||||
Verb = "runas"
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
Process.Start(procInfo);
|
||||
Application.Exit();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show("Failed to run as administrator: " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show("Already running as Administrator.");
|
||||
}
|
||||
}
|
||||
|
||||
private void globalSettingsToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
(new GlobalSettingsForm()).Show();
|
||||
}
|
||||
|
||||
private void startCodeEditorToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
Program.StartResourceServer();
|
||||
|
||||
if (!Program.resourceServer.IsRunning())
|
||||
{
|
||||
Program.resourceServer.Start();
|
||||
((ToolStripMenuItem)sender).Text = "Open the code editor...";
|
||||
}
|
||||
else
|
||||
{
|
||||
Program.OpenWebBrowser(Program.resourceServer.GetPrefix());
|
||||
}
|
||||
}
|
||||
|
||||
private void openCodeEditorToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (Program.resourceServer == null)
|
||||
{
|
||||
MessageBox.Show("A resource server is not running.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Program.OpenWebBrowser(Program.resourceServer.GetPrefix());
|
||||
}
|
||||
}
|
||||
|
||||
private void openCopilotToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
Program.OpenWebBrowser(Program.GetAppConfig("CopilotUrl"));
|
||||
}
|
||||
}
|
||||
}
|
129
WelsonJS.Toolkit/WelsonJS.Launcher/MainForm.resx
Normal file
|
@ -0,0 +1,129 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<metadata name="menuStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
<metadata name="notifyIcon1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>134, 17</value>
|
||||
</metadata>
|
||||
<metadata name="contextMenuStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>249, 17</value>
|
||||
</metadata>
|
||||
</root>
|
191
WelsonJS.Toolkit/WelsonJS.Launcher/Program.cs
Normal file
|
@ -0,0 +1,191 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
using System.Configuration;
|
||||
|
||||
namespace WelsonJS.Launcher
|
||||
{
|
||||
internal static class Program
|
||||
{
|
||||
static Mutex mutex;
|
||||
public static ResourceServer resourceServer;
|
||||
|
||||
[STAThread]
|
||||
static void Main()
|
||||
{
|
||||
mutex = new Mutex(true, "WelsonJS.Launcher.Mutex", out bool isMutexNotExists);
|
||||
if (!isMutexNotExists)
|
||||
{
|
||||
MessageBox.Show("WelsonJS Launcher already running.");
|
||||
return;
|
||||
}
|
||||
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
Application.Run(new MainForm());
|
||||
|
||||
mutex.ReleaseMutex();
|
||||
mutex.Dispose();
|
||||
}
|
||||
|
||||
public static void RunCommandPrompt(string workingDirectory, string entryFileName, string scriptName, bool isConsoleApplication = false, bool isInteractiveServiceAapplication = false)
|
||||
{
|
||||
if (!isConsoleApplication)
|
||||
{
|
||||
if (!File.Exists(Path.Combine(workingDirectory, entryFileName)))
|
||||
{
|
||||
throw new Exception("Not Found: " + entryFileName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Directory.EnumerateFiles(workingDirectory, scriptName + ".*").Any())
|
||||
{
|
||||
throw new Exception("Not found matches file: " + scriptName);
|
||||
}
|
||||
}
|
||||
|
||||
Process process = new Process
|
||||
{
|
||||
StartInfo = new ProcessStartInfo("cmd")
|
||||
{
|
||||
UseShellExecute = false,
|
||||
RedirectStandardInput = true,
|
||||
RedirectStandardOutput = true,
|
||||
CreateNoWindow = true,
|
||||
Arguments = "/k",
|
||||
}
|
||||
};
|
||||
process.Start();
|
||||
|
||||
process.StandardInput.WriteLine("pushd " + workingDirectory);
|
||||
process.StandardInput.WriteLine();
|
||||
process.StandardInput.Flush();
|
||||
process.StandardOutput.ReadLine();
|
||||
|
||||
if (isInteractiveServiceAapplication)
|
||||
{
|
||||
process.StandardInput.WriteLine($"start cmd /c startInteractiveService.bat");
|
||||
process.StandardInput.WriteLine();
|
||||
process.StandardInput.Flush();
|
||||
process.StandardOutput.ReadLine();
|
||||
}
|
||||
else if (!isConsoleApplication)
|
||||
{
|
||||
process.StandardInput.WriteLine(entryFileName);
|
||||
process.StandardInput.WriteLine();
|
||||
process.StandardInput.Flush();
|
||||
process.StandardOutput.ReadLine();
|
||||
}
|
||||
else
|
||||
{
|
||||
process.StandardInput.WriteLine($"start cmd /c cscript app.js {scriptName}");
|
||||
process.StandardInput.WriteLine();
|
||||
process.StandardInput.Flush();
|
||||
process.StandardOutput.ReadLine();
|
||||
}
|
||||
process.StandardInput.Close();
|
||||
process.WaitForExit();
|
||||
}
|
||||
|
||||
public static string GetFinalDirectory(string path)
|
||||
{
|
||||
string[] directories = Directory.GetDirectories(path);
|
||||
|
||||
while (directories.Length == 1)
|
||||
{
|
||||
path = directories[0];
|
||||
directories = Directory.GetDirectories(path);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
public static string GetAppDataPath()
|
||||
{
|
||||
string path = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
"WelsonJS"
|
||||
);
|
||||
|
||||
Directory.CreateDirectory(path);
|
||||
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
throw new IOException("Failed to create directory: " + path);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
public static string GetWorkingDirectory(string instanceId, bool followSubDirectory = false)
|
||||
{
|
||||
string workingDirectory = Path.Combine(GetAppDataPath(), instanceId);
|
||||
|
||||
if (followSubDirectory)
|
||||
{
|
||||
if (!Directory.Exists(workingDirectory))
|
||||
{
|
||||
workingDirectory = Path.Combine(Path.GetTempPath(), instanceId);
|
||||
}
|
||||
|
||||
workingDirectory = GetFinalDirectory(workingDirectory);
|
||||
}
|
||||
|
||||
return workingDirectory;
|
||||
}
|
||||
|
||||
public static void StartResourceServer()
|
||||
{
|
||||
lock(typeof(Program))
|
||||
{
|
||||
if (resourceServer == null)
|
||||
{
|
||||
resourceServer = new ResourceServer(GetAppConfig("ResourceServerPrefix"), "editor.html");
|
||||
}
|
||||
}
|
||||
}
|
||||
public static void OpenWebBrowser(string url)
|
||||
{
|
||||
Uri resourceServerUri = new Uri(GetAppConfig("ResourceServerPrefix"));
|
||||
Uri devToolsUri = new Uri(GetAppConfig("DevToolsPrefix"));
|
||||
|
||||
string userDataDir = Path.Combine(GetAppDataPath(), "EdgeUserProfile");
|
||||
string remoteAllowOrigins = $"{resourceServerUri.Scheme}://{resourceServerUri.Host}:{resourceServerUri.Port}";
|
||||
int remoteDebuggingPort = devToolsUri.Port;
|
||||
string[] arguments = {
|
||||
$"\"{url}\"",
|
||||
$"--remote-debugging-port={remoteDebuggingPort}",
|
||||
$"--remote-allow-origins={remoteAllowOrigins}", // for security reason
|
||||
$"--user-data-dir=\"{userDataDir}\""
|
||||
};
|
||||
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = "msedge.exe",
|
||||
Arguments = string.Join(" ", arguments),
|
||||
UseShellExecute = true
|
||||
});
|
||||
}
|
||||
|
||||
public static string GetAppConfig(string key)
|
||||
{
|
||||
string value = ConfigurationManager.AppSettings[key];
|
||||
if (!string.IsNullOrEmpty(value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
value = Properties.Resources.ResourceManager.GetString(key);
|
||||
if (!string.IsNullOrEmpty(value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// 어셈블리에 대한 일반 정보는 다음 특성 집합을 통해
|
||||
// 제어됩니다. 어셈블리와 관련된 정보를 수정하려면
|
||||
// 이러한 특성 값을 변경하세요.
|
||||
[assembly: AssemblyTitle("WelsonJS.Launcher")]
|
||||
[assembly: AssemblyDescription("Launcher for WelsonJS Application Packages")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Catswords")]
|
||||
[assembly: AssemblyProduct("WelsonJS")]
|
||||
[assembly: AssemblyCopyright("Catswords OSS, GPLv3 or Ms-RL")]
|
||||
[assembly: AssemblyTrademark("WelsonJS")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// ComVisible을 false로 설정하면 이 어셈블리의 형식이 COM 구성 요소에
|
||||
// 표시되지 않습니다. COM에서 이 어셈블리의 형식에 액세스하려면
|
||||
// 해당 형식에 대해 ComVisible 특성을 true로 설정하세요.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// 이 프로젝트가 COM에 노출되는 경우 다음 GUID는 typelib의 ID를 나타냅니다.
|
||||
[assembly: Guid("4074d413-195c-45e9-9e63-0d07914187b8")]
|
||||
|
||||
// 어셈블리의 버전 정보는 다음 네 가지 값으로 구성됩니다.
|
||||
//
|
||||
// 주 버전
|
||||
// 부 버전
|
||||
// 빌드 번호
|
||||
// 수정 버전
|
||||
//
|
||||
// 모든 값을 지정하거나 아래와 같이 '*'를 사용하여 빌드 번호 및 수정 번호를
|
||||
// 기본값으로 할 수 있습니다.
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("0.2.7.54")]
|
||||
[assembly: AssemblyFileVersion("0.2.7.54")]
|
289
WelsonJS.Toolkit/WelsonJS.Launcher/Properties/Resources.Designer.cs
generated
Normal file
|
@ -0,0 +1,289 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// 이 코드는 도구를 사용하여 생성되었습니다.
|
||||
// 런타임 버전:4.0.30319.42000
|
||||
//
|
||||
// 파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면
|
||||
// 이러한 변경 내용이 손실됩니다.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace WelsonJS.Launcher.Properties {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 지역화된 문자열 등을 찾기 위한 강력한 형식의 리소스 클래스입니다.
|
||||
/// </summary>
|
||||
// 이 클래스는 ResGen 또는 Visual Studio와 같은 도구를 통해 StronglyTypedResourceBuilder
|
||||
// 클래스에서 자동으로 생성되었습니다.
|
||||
// 멤버를 추가하거나 제거하려면 .ResX 파일을 편집한 다음 /str 옵션을 사용하여 ResGen을
|
||||
// 다시 실행하거나 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 Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources() {
|
||||
}
|
||||
|
||||
/// <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("WelsonJS.Launcher.Properties.Resources", typeof(Resources).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>
|
||||
/// 과(와) 유사한 지역화된 문자열을 찾습니다.
|
||||
/// </summary>
|
||||
internal static string AzureAiServiceApiKey {
|
||||
get {
|
||||
return ResourceManager.GetString("AzureAiServiceApiKey", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 2024-05-01-preview과(와) 유사한 지역화된 문자열을 찾습니다.
|
||||
/// </summary>
|
||||
internal static string AzureAiServiceApiVersion {
|
||||
get {
|
||||
return ResourceManager.GetString("AzureAiServiceApiVersion", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// https://ai-catswords656881030318.services.ai.azure.com/과(와) 유사한 지역화된 문자열을 찾습니다.
|
||||
/// </summary>
|
||||
internal static string AzureAiServicePrefix {
|
||||
get {
|
||||
return ResourceManager.GetString("AzureAiServicePrefix", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// https://catswords.blob.core.windows.net/welsonjs/blob.config.xml과(와) 유사한 지역화된 문자열을 찾습니다.
|
||||
/// </summary>
|
||||
internal static string BlobConfigUrl {
|
||||
get {
|
||||
return ResourceManager.GetString("BlobConfigUrl", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// https://catswords.blob.core.windows.net/welsonjs/과(와) 유사한 지역화된 문자열을 찾습니다.
|
||||
/// </summary>
|
||||
internal static string BlobStoragePrefix {
|
||||
get {
|
||||
return ResourceManager.GetString("BlobStoragePrefix", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// https://copilot.microsoft.com/과(와) 유사한 지역화된 문자열을 찾습니다.
|
||||
/// </summary>
|
||||
internal static string CopilotUrl {
|
||||
get {
|
||||
return ResourceManager.GetString("CopilotUrl", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// http://localhost:9222/과(와) 유사한 지역화된 문자열을 찾습니다.
|
||||
/// </summary>
|
||||
internal static string DevToolsPrefix {
|
||||
get {
|
||||
return ResourceManager.GetString("DevToolsPrefix", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 1.1.1.1과(와) 유사한 지역화된 문자열을 찾습니다.
|
||||
/// </summary>
|
||||
internal static string DnsServerAddress {
|
||||
get {
|
||||
return ResourceManager.GetString("DnsServerAddress", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// (아이콘)과(와) 유사한 System.Drawing.Icon 형식의 지역화된 리소스를 찾습니다.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Icon favicon {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("favicon", resourceCulture);
|
||||
return ((System.Drawing.Icon)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 90과(와) 유사한 지역화된 문자열을 찾습니다.
|
||||
/// </summary>
|
||||
internal static string HttpClientTimeout {
|
||||
get {
|
||||
return ResourceManager.GetString("HttpClientTimeout", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// System.Drawing.Bitmap 형식의 지역화된 리소스를 찾습니다.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap icon_check_32 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("icon_check_32", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// System.Drawing.Bitmap 형식의 지역화된 리소스를 찾습니다.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap icon_delete_32 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("icon_delete_32", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// System.Drawing.Bitmap 형식의 지역화된 리소스를 찾습니다.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap icon_directory_32 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("icon_directory_32", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// System.Drawing.Bitmap 형식의 지역화된 리소스를 찾습니다.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap icon_export_32 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("icon_export_32", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// System.Drawing.Bitmap 형식의 지역화된 리소스를 찾습니다.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap icon_file_32 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("icon_file_32", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// System.Drawing.Bitmap 형식의 지역화된 리소스를 찾습니다.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap icon_import_32 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("icon_import_32", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// System.Drawing.Bitmap 형식의 지역화된 리소스를 찾습니다.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap icon_link_128 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("icon_link_128", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// System.Drawing.Bitmap 형식의 지역화된 리소스를 찾습니다.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap icon_start_32 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("icon_start_32", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// System.Drawing.Bitmap 형식의 지역화된 리소스를 찾습니다.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap icon_zip_128 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("icon_zip_128", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// https://github.com/gnh1201/welsonjs과(와) 유사한 지역화된 문자열을 찾습니다.
|
||||
/// </summary>
|
||||
internal static string RepositoryUrl {
|
||||
get {
|
||||
return ResourceManager.GetString("RepositoryUrl", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// http://localhost:3000/과(와) 유사한 지역화된 문자열을 찾습니다.
|
||||
/// </summary>
|
||||
internal static string ResourceServerPrefix {
|
||||
get {
|
||||
return ResourceManager.GetString("ResourceServerPrefix", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 141.101.82.1과(와) 유사한 지역화된 문자열을 찾습니다.
|
||||
/// </summary>
|
||||
internal static string WhoisClientAddress {
|
||||
get {
|
||||
return ResourceManager.GetString("WhoisClientAddress", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// https://xn--c79as89aj0e29b77z.xn--3e0b707e/kor/whois/whois.jsp과(와) 유사한 지역화된 문자열을 찾습니다.
|
||||
/// </summary>
|
||||
internal static string WhoisReferrerUrl {
|
||||
get {
|
||||
return ResourceManager.GetString("WhoisReferrerUrl", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// https://xn--c79as89aj0e29b77z.xn--3e0b707e/kor/whois.jsc과(와) 유사한 지역화된 문자열을 찾습니다.
|
||||
/// </summary>
|
||||
internal static string WhoisServerUrl {
|
||||
get {
|
||||
return ResourceManager.GetString("WhoisServerUrl", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
193
WelsonJS.Toolkit/WelsonJS.Launcher/Properties/Resources.resx
Normal file
|
@ -0,0 +1,193 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||
<data name="icon_link_128" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\icon_link_128.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="icon_zip_128" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\icon_zip_128.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="favicon" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\favicon.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="icon_directory_32" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\icon_directory_32.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="icon_file_32" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\icon_file_32.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="icon_check_32" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\icon_check_32.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="icon_export_32" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\icon_export_32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="icon_import_32" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\icon_import_32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="icon_delete_32" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\icon_delete_32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="icon_start_32" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\icon_start_32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="CopilotUrl" xml:space="preserve">
|
||||
<value>https://copilot.microsoft.com/</value>
|
||||
</data>
|
||||
<data name="RepositoryUrl" xml:space="preserve">
|
||||
<value>https://github.com/gnh1201/welsonjs</value>
|
||||
</data>
|
||||
<data name="ResourceServerPrefix" xml:space="preserve">
|
||||
<value>http://localhost:3000/</value>
|
||||
</data>
|
||||
<data name="DevToolsPrefix" xml:space="preserve">
|
||||
<value>http://localhost:9222/</value>
|
||||
</data>
|
||||
<data name="AzureAiServiceApiKey" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="AzureAiServicePrefix" xml:space="preserve">
|
||||
<value>https://ai-catswords656881030318.services.ai.azure.com/</value>
|
||||
</data>
|
||||
<data name="DnsServerAddress" xml:space="preserve">
|
||||
<value>1.1.1.1</value>
|
||||
</data>
|
||||
<data name="WhoisClientAddress" xml:space="preserve">
|
||||
<value>141.101.82.1</value>
|
||||
</data>
|
||||
<data name="WhoisReferrerUrl" xml:space="preserve">
|
||||
<value>https://xn--c79as89aj0e29b77z.xn--3e0b707e/kor/whois/whois.jsp</value>
|
||||
</data>
|
||||
<data name="WhoisServerUrl" xml:space="preserve">
|
||||
<value>https://xn--c79as89aj0e29b77z.xn--3e0b707e/kor/whois.jsc</value>
|
||||
</data>
|
||||
<data name="BlobStoragePrefix" xml:space="preserve">
|
||||
<value>https://catswords.blob.core.windows.net/welsonjs/</value>
|
||||
</data>
|
||||
<data name="AzureAiServiceApiVersion" xml:space="preserve">
|
||||
<value>2024-05-01-preview</value>
|
||||
</data>
|
||||
<data name="BlobConfigUrl" xml:space="preserve">
|
||||
<value>https://catswords.blob.core.windows.net/welsonjs/blob.config.xml</value>
|
||||
</data>
|
||||
<data name="HttpClientTimeout" xml:space="preserve">
|
||||
<value>90</value>
|
||||
</data>
|
||||
</root>
|
26
WelsonJS.Toolkit/WelsonJS.Launcher/Properties/Settings.Designer.cs
generated
Normal file
|
@ -0,0 +1,26 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// 이 코드는 도구를 사용하여 생성되었습니다.
|
||||
// 런타임 버전:4.0.30319.42000
|
||||
//
|
||||
// 파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면
|
||||
// 이러한 변경 내용이 손실됩니다.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace WelsonJS.Launcher.Properties {
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.10.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
|
||||
public static Settings Default {
|
||||
get {
|
||||
return defaultInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
|
||||
<Profiles>
|
||||
<Profile Name="(Default)" />
|
||||
</Profiles>
|
||||
<Settings />
|
||||
</SettingsFile>
|
500
WelsonJS.Toolkit/WelsonJS.Launcher/ResourceServer.cs
Normal file
|
@ -0,0 +1,500 @@
|
|||
// ResourceServer.cs
|
||||
// A resource server of WelsonJS Editor (WelsonJS.Launcher)
|
||||
// Namhyeon Go <abuse@catswords.net>
|
||||
// https://github.com/gnh1201/welsonjs
|
||||
//
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace WelsonJS.Launcher
|
||||
{
|
||||
public class ResourceServer
|
||||
{
|
||||
private readonly HttpListener _listener;
|
||||
private CancellationTokenSource _cts;
|
||||
private Task _serverTask;
|
||||
private bool _isRunning;
|
||||
private string _prefix;
|
||||
private string _resourceName;
|
||||
private List<IResourceTool> _tools = new List<IResourceTool>();
|
||||
private static readonly HttpClient _httpClient = new HttpClient();
|
||||
private static readonly string _defaultMimeType = "application/octet-stream";
|
||||
private static BlobConfig _blobConfig;
|
||||
|
||||
static ResourceServer()
|
||||
{
|
||||
// Set timeout
|
||||
int timeout = int.TryParse(Program.GetAppConfig("HttpClientTimeout"), out timeout) ? timeout : 90;
|
||||
_httpClient.Timeout = TimeSpan.FromSeconds(timeout);
|
||||
|
||||
// Fetch a blob config from Internet
|
||||
FetchBlobConfig();
|
||||
}
|
||||
|
||||
public ResourceServer(string prefix, string resourceName)
|
||||
{
|
||||
_prefix = prefix;
|
||||
_listener = new HttpListener();
|
||||
_listener.Prefixes.Add(prefix);
|
||||
_resourceName = resourceName;
|
||||
|
||||
// Add resource tools
|
||||
_tools.Add(new ResourceTools.Completion(this, _httpClient));
|
||||
_tools.Add(new ResourceTools.Settings(this, _httpClient));
|
||||
_tools.Add(new ResourceTools.DevTools(this, _httpClient));
|
||||
_tools.Add(new ResourceTools.DnsQuery(this, _httpClient));
|
||||
_tools.Add(new ResourceTools.Tfa(this, _httpClient));
|
||||
_tools.Add(new ResourceTools.Whois(this, _httpClient));
|
||||
}
|
||||
|
||||
public string GetPrefix()
|
||||
{
|
||||
return _prefix;
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if (_isRunning) return;
|
||||
|
||||
_isRunning = true;
|
||||
_cts = new CancellationTokenSource();
|
||||
_listener.Start();
|
||||
|
||||
// Open the web browser
|
||||
Program.OpenWebBrowser(_prefix);
|
||||
|
||||
// Run a task with cancellation token
|
||||
_serverTask = Task.Run(() => ListenLoop(_cts.Token));
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_isRunning = false;
|
||||
_cts.Cancel();
|
||||
_listener.Stop();
|
||||
|
||||
MessageBox.Show("Server stopped.");
|
||||
}
|
||||
|
||||
public bool IsRunning()
|
||||
{
|
||||
return _isRunning;
|
||||
}
|
||||
|
||||
private async Task ListenLoop(CancellationToken token)
|
||||
{
|
||||
while (!token.IsCancellationRequested && _isRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ProcessRequest(await _listener.GetContextAsync());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (token.IsCancellationRequested || !_isRunning) break;
|
||||
MessageBox.Show($"Error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ProcessRequest(HttpListenerContext context)
|
||||
{
|
||||
string path = context.Request.Url.AbsolutePath.TrimStart('/');
|
||||
|
||||
// Serve from a resource name
|
||||
if (String.IsNullOrEmpty(path))
|
||||
{
|
||||
ServeResource(context, GetResource(_resourceName), "text/html");
|
||||
return;
|
||||
}
|
||||
|
||||
// Serve the favicon.ico file
|
||||
if ("favicon.ico".Equals(path, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
ServeResource(context, GetResource("favicon"), "image/x-icon");
|
||||
return;
|
||||
}
|
||||
|
||||
// Serve from a resource tool
|
||||
foreach (var tool in _tools)
|
||||
{
|
||||
if (tool.CanHandle(path))
|
||||
{
|
||||
await tool.HandleAsync(context, path);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Serve from the blob server
|
||||
if (await ServeBlob(context, path)) return;
|
||||
|
||||
// Fallback to serve from a resource name
|
||||
ServeResource(context, GetResource(_resourceName), "text/html");
|
||||
}
|
||||
|
||||
private async Task<bool> ServeBlob(HttpListenerContext context, string path, string prefix = null)
|
||||
{
|
||||
byte[] data;
|
||||
string mimeType;
|
||||
|
||||
if (!String.IsNullOrEmpty(prefix))
|
||||
{
|
||||
string url = $"{prefix}{path}";
|
||||
|
||||
try
|
||||
{
|
||||
using (var client = new HttpClient())
|
||||
{
|
||||
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, url);
|
||||
request.Headers.UserAgent.ParseAdd(context.Request.UserAgent);
|
||||
HttpResponseMessage response = await client.SendAsync(request);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
Trace.TraceError($"Failed to serve blob. URL: {url}, Status: {response.StatusCode}");
|
||||
return false;
|
||||
}
|
||||
|
||||
data = await response.Content.ReadAsByteArrayAsync();
|
||||
mimeType = response.Content.Headers.ContentType?.MediaType ?? _defaultMimeType;
|
||||
|
||||
ServeResource(context, data, mimeType);
|
||||
_ = TrySaveCachedBlob(path, data, mimeType);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.TraceError($"Failed to serve blob. URL: {url}, Exception: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// use the cached data
|
||||
if (TryGetCachedBlob(path, out mimeType, true))
|
||||
{
|
||||
if (TryGetCachedBlob(path, out data))
|
||||
{
|
||||
if (String.IsNullOrEmpty(mimeType))
|
||||
{
|
||||
mimeType = _defaultMimeType;
|
||||
}
|
||||
|
||||
ServeResource(context, data, mimeType);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// use a blob source
|
||||
if (await TryServeFromBlob(context, path))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task<bool> TryServeFromBlob(HttpListenerContext context, string path)
|
||||
{
|
||||
if (_blobConfig != null)
|
||||
{
|
||||
foreach (var route in _blobConfig.Routes)
|
||||
{
|
||||
foreach (var (regex, index) in route.RegexConditions.Select((r, i) => (r, i)))
|
||||
{
|
||||
if (!regex.Compiled.IsMatch(path)) continue;
|
||||
|
||||
var match = (index < route.Matches.Count) ? route.Matches[index] : route.Matches.First();
|
||||
var _path = route.StripPrefix ? path.Substring(match.Length) : path;
|
||||
|
||||
foreach (var prefixUrl in route.PrefixUrls)
|
||||
{
|
||||
if (await ServeBlob(context, _path, prefixUrl))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fallback
|
||||
string prefix = Program.GetAppConfig("BlobStoragePrefix");
|
||||
if (await ServeBlob(context, path, prefix))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private string GetCachedBlobPath(string path)
|
||||
{
|
||||
// Get a hash from the path
|
||||
string hashedPath;
|
||||
using (MD5 md5 = MD5.Create())
|
||||
{
|
||||
byte[] bHashedPath = md5.ComputeHash(Encoding.UTF8.GetBytes(path));
|
||||
hashedPath = BitConverter.ToString(bHashedPath).Replace("-", "").ToLowerInvariant();
|
||||
}
|
||||
|
||||
// Get a sub-directory paths from the hashed path
|
||||
string[] subDirectoryPaths = new string[] {
|
||||
hashedPath.Substring(0, 2),
|
||||
hashedPath.Substring(2, 2),
|
||||
hashedPath.Substring(4, 2)
|
||||
};
|
||||
|
||||
// Return the cache path
|
||||
return Path.Combine(Program.GetAppDataPath(), "BlobCache", String.Join("\\", subDirectoryPaths), hashedPath);
|
||||
}
|
||||
|
||||
private bool TryGetCachedBlob(string path, out byte[] data, bool isMetadata = false)
|
||||
{
|
||||
string cachePath = GetCachedBlobPath(path);
|
||||
if (isMetadata)
|
||||
{
|
||||
cachePath = $"{cachePath}.meta";
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (File.Exists(cachePath))
|
||||
{
|
||||
data = File.ReadAllBytes(cachePath);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.TraceError($"Cache Read Error: {ex.Message}");
|
||||
}
|
||||
|
||||
data = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryGetCachedBlob(string path, out string data, bool isMetadata = false)
|
||||
{
|
||||
byte[] bData;
|
||||
if (TryGetCachedBlob(path, out bData, isMetadata))
|
||||
{
|
||||
data = Encoding.UTF8.GetString(bData);
|
||||
return true;
|
||||
}
|
||||
|
||||
data = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task<bool> TrySaveCachedBlob(string path, byte[] data, string mimeType)
|
||||
{
|
||||
await Task.Delay(0);
|
||||
|
||||
try
|
||||
{
|
||||
string cachePath = GetCachedBlobPath(path);
|
||||
string cacheDirectory = Path.GetDirectoryName(cachePath);
|
||||
|
||||
// Is exists the cached blob directory
|
||||
if (!Directory.Exists(cacheDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(cacheDirectory);
|
||||
}
|
||||
|
||||
// Save the cache
|
||||
File.WriteAllBytes(cachePath, data);
|
||||
|
||||
// Save the cache meta
|
||||
File.WriteAllBytes($"{cachePath}.meta", Encoding.UTF8.GetBytes(mimeType));
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.TraceError($"Error: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void ServeResource(HttpListenerContext context)
|
||||
{
|
||||
ServeResource(context, "<error>Not Found</error>", "application/xml", 404);
|
||||
}
|
||||
|
||||
public void ServeResource(HttpListenerContext context, byte[] data, string mimeType = "text/html", int statusCode = 200)
|
||||
{
|
||||
string xmlHeader = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
|
||||
|
||||
if (data == null)
|
||||
{
|
||||
data = Encoding.UTF8.GetBytes(xmlHeader + "\r\n<error>Could not find the resource.</error>");
|
||||
mimeType = "application/xml";
|
||||
statusCode = 404;
|
||||
}
|
||||
|
||||
context.Response.StatusCode = statusCode;
|
||||
context.Response.ContentType = mimeType;
|
||||
context.Response.ContentLength64 = data.Length;
|
||||
using (Stream outputStream = context.Response.OutputStream)
|
||||
{
|
||||
outputStream.Write(data, 0, data.Length);
|
||||
}
|
||||
}
|
||||
|
||||
public void ServeResource(HttpListenerContext context, string data, string mimeType = "text/html", int statusCode = 200)
|
||||
{
|
||||
string xmlHeader = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
|
||||
|
||||
if (data == null)
|
||||
{
|
||||
data = xmlHeader + "\r\n<error>Could not find the resource.</error>";
|
||||
mimeType = "application/xml";
|
||||
statusCode = 404;
|
||||
}
|
||||
else if (mimeType == "application/xml" && !data.StartsWith("<?xml"))
|
||||
{
|
||||
data = xmlHeader + "\r\n" + data;
|
||||
}
|
||||
|
||||
ServeResource(context, Encoding.UTF8.GetBytes(data), mimeType, statusCode);
|
||||
}
|
||||
|
||||
private byte[] GetResource(string resourceName)
|
||||
{
|
||||
// Try to fetch embedded resource.
|
||||
byte[] data = GetEmbeddedResource(typeof(Program).Namespace + "." + resourceName);
|
||||
if (data != null) return data;
|
||||
|
||||
// Fallback: Try to fetch resource from ResourceManager.
|
||||
return GetResourceFromManager(resourceName);
|
||||
}
|
||||
|
||||
private byte[] GetEmbeddedResource(string fullResourceName)
|
||||
{
|
||||
Assembly assembly = Assembly.GetExecutingAssembly();
|
||||
using (Stream stream = assembly.GetManifestResourceStream(fullResourceName))
|
||||
{
|
||||
if (stream != null)
|
||||
{
|
||||
using (MemoryStream memoryStream = new MemoryStream())
|
||||
{
|
||||
stream.CopyTo(memoryStream);
|
||||
return memoryStream.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private byte[] GetResourceFromManager(string resourceName)
|
||||
{
|
||||
object resourceObject = Properties.Resources.ResourceManager.GetObject(resourceName);
|
||||
switch (resourceObject)
|
||||
{
|
||||
case byte[] resourceBytes:
|
||||
return resourceBytes;
|
||||
case System.Drawing.Icon icon:
|
||||
return ConvertIconToBytes(icon);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] ConvertIconToBytes(System.Drawing.Icon icon)
|
||||
{
|
||||
using (MemoryStream memoryStream = new MemoryStream())
|
||||
{
|
||||
icon.Save(memoryStream);
|
||||
return memoryStream.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
private static async void FetchBlobConfig()
|
||||
{
|
||||
try
|
||||
{
|
||||
string url = Program.GetAppConfig("BlobConfigUrl");
|
||||
var response = await _httpClient.GetStreamAsync(url);
|
||||
|
||||
var serializer = new XmlSerializer(typeof(BlobConfig));
|
||||
using (var reader = new StreamReader(response))
|
||||
{
|
||||
_blobConfig = (BlobConfig)serializer.Deserialize(reader);
|
||||
}
|
||||
|
||||
_blobConfig?.Compile();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.TraceError($"Failed to fetch a blob config. Exception: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[XmlRoot("blobConfig")]
|
||||
public class BlobConfig
|
||||
{
|
||||
[XmlArray("routes")]
|
||||
[XmlArrayItem("route")]
|
||||
public List<BlobRoute> Routes { get; set; } = new List<BlobRoute>();
|
||||
|
||||
public void Compile()
|
||||
{
|
||||
foreach (var route in Routes)
|
||||
{
|
||||
if (route.Matches == null) continue;
|
||||
|
||||
route.RegexConditions = new List<RegexCondition>();
|
||||
foreach (var match in route.Matches)
|
||||
{
|
||||
route.RegexConditions.Add(new RegexCondition
|
||||
{
|
||||
Pattern = match,
|
||||
Compiled = new Regex(
|
||||
match.StartsWith("^") ? match : "^" + Regex.Escape(match),
|
||||
RegexOptions.Compiled)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class BlobRoute
|
||||
{
|
||||
[XmlArray("matches")]
|
||||
[XmlArrayItem("match")]
|
||||
public List<string> Matches { get; set; }
|
||||
|
||||
[XmlArray("prefixUrls")]
|
||||
[XmlArrayItem("url")]
|
||||
public List<string> PrefixUrls { get; set; }
|
||||
|
||||
[XmlAttribute("stripPrefix")]
|
||||
public bool StripPrefix { get; set; }
|
||||
|
||||
[XmlIgnore]
|
||||
public List<RegexCondition> RegexConditions { get; set; }
|
||||
}
|
||||
|
||||
public class RegexCondition
|
||||
{
|
||||
[XmlIgnore]
|
||||
public string Pattern { get; set; }
|
||||
|
||||
[XmlIgnore]
|
||||
public Regex Compiled { get; set; }
|
||||
}
|
||||
}
|
214
WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/Completion.cs
Normal file
|
@ -0,0 +1,214 @@
|
|||
using Microsoft.Win32;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace WelsonJS.Launcher.ResourceTools
|
||||
{
|
||||
public class Completion : IResourceTool
|
||||
{
|
||||
private readonly ResourceServer Server;
|
||||
private readonly HttpClient _httpClient;
|
||||
private const string Prefix = "completion/";
|
||||
private List<string> Executables = new List<string>();
|
||||
|
||||
public Completion(ResourceServer server, HttpClient httpClient)
|
||||
{
|
||||
Server = server;
|
||||
_httpClient = httpClient;
|
||||
|
||||
new Task(() =>
|
||||
{
|
||||
Executables.AddRange(GetInstalledSoftwareExecutables());
|
||||
Executables.AddRange(GetExecutablesFromPath());
|
||||
Executables.AddRange(GetExecutablesFromNetFx());
|
||||
}).Start();
|
||||
}
|
||||
|
||||
public bool CanHandle(string path)
|
||||
{
|
||||
return path.StartsWith(Prefix, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public async Task HandleAsync(HttpListenerContext context, string path)
|
||||
{
|
||||
await Task.Delay(0);
|
||||
|
||||
string word = path.Substring(Prefix.Length);
|
||||
|
||||
try
|
||||
{
|
||||
CompletionItem[] completionItems = Executables
|
||||
.Where(exec => exec.IndexOf(word, 0, StringComparison.OrdinalIgnoreCase) > -1)
|
||||
.Take(100) // Limit the number of results
|
||||
.Select(exec => new CompletionItem
|
||||
{
|
||||
Label = Path.GetFileName(exec),
|
||||
Kind = "Text",
|
||||
Documentation = $"An executable file: {exec}",
|
||||
InsertText = exec
|
||||
})
|
||||
.ToArray();
|
||||
|
||||
XElement response = new XElement("suggestions",
|
||||
completionItems.Select(item => new XElement("item",
|
||||
new XElement("label", item.Label),
|
||||
new XElement("kind", item.Kind),
|
||||
new XElement("documentation", item.Documentation),
|
||||
new XElement("insertText", item.InsertText)
|
||||
))
|
||||
);
|
||||
|
||||
Server.ServeResource(context, response.ToString(), "application/xml");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Server.ServeResource(context, $"<error>Failed to process completion request. {ex.Message}</error>", "application/xml", 500);
|
||||
}
|
||||
}
|
||||
|
||||
private List<string> GetInstalledSoftwareExecutables()
|
||||
{
|
||||
List<string> executables = new List<string>();
|
||||
string registryKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
|
||||
|
||||
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(registryKey))
|
||||
{
|
||||
if (key != null)
|
||||
{
|
||||
foreach (string subKeyName in key.GetSubKeyNames())
|
||||
{
|
||||
using (RegistryKey subKey = key.OpenSubKey(subKeyName))
|
||||
{
|
||||
string installLocation = subKey?.GetValue("InstallLocation") as string;
|
||||
string uninstallString = subKey?.GetValue("UninstallString") as string;
|
||||
|
||||
List<string> executablePaths = FindExecutables(installLocation, uninstallString);
|
||||
executables.AddRange(executablePaths);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return executables;
|
||||
}
|
||||
|
||||
private List<string> FindExecutables(string installLocation, string uninstallString)
|
||||
{
|
||||
List<string> executables = new List<string>();
|
||||
|
||||
if (!string.IsNullOrEmpty(installLocation) && Directory.Exists(installLocation))
|
||||
{
|
||||
try
|
||||
{
|
||||
List<string> executableFiles = Directory.GetFiles(installLocation, "*.exe", SearchOption.AllDirectories)
|
||||
.OrderByDescending(f => new FileInfo(f).Length)
|
||||
.ToList();
|
||||
executables.AddRange(executableFiles);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Error enumerating executables in '{installLocation}': {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(uninstallString))
|
||||
{
|
||||
if (TryParseExecutablePath(uninstallString, out string executablePath))
|
||||
{
|
||||
executables.Add(executablePath);
|
||||
}
|
||||
}
|
||||
|
||||
return executables;
|
||||
}
|
||||
|
||||
private static bool TryParseExecutablePath(string s, out string path)
|
||||
{
|
||||
Match match = Regex.Match(s, @"(?<=""|^)([a-zA-Z]:\\[^""]+\.exe)", RegexOptions.IgnoreCase);
|
||||
|
||||
if (match.Success)
|
||||
{
|
||||
path = match.Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
path = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private List<string> GetExecutablesFromPath()
|
||||
{
|
||||
List<string> executables = new List<string>();
|
||||
string[] paths = Environment.GetEnvironmentVariable("PATH")?.Split(';');
|
||||
|
||||
if (paths != null)
|
||||
{
|
||||
foreach (string path in paths)
|
||||
{
|
||||
if (Directory.Exists(path))
|
||||
{
|
||||
try
|
||||
{
|
||||
executables.AddRange(Directory.GetFiles(path, "*.exe", SearchOption.TopDirectoryOnly));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Error enumerating executables in '{path}': {ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return executables;
|
||||
}
|
||||
|
||||
private List<string> GetExecutablesFromNetFx()
|
||||
{
|
||||
List<string> executables = new List<string>();
|
||||
|
||||
string windir = Environment.GetEnvironmentVariable("WINDIR");
|
||||
|
||||
if (!string.IsNullOrEmpty(windir))
|
||||
{
|
||||
string[] paths = new string[]
|
||||
{
|
||||
Path.Combine(windir, "Microsoft.NET", "Framework"),
|
||||
Path.Combine(windir, "Microsoft.NET", "Framework64")
|
||||
};
|
||||
|
||||
foreach (string path in paths)
|
||||
{
|
||||
if (Directory.Exists(path))
|
||||
{
|
||||
try
|
||||
{
|
||||
executables.AddRange(Directory.GetFiles(path, "*.exe", SearchOption.AllDirectories));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Error enumerating executables in '{path}': {ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return executables;
|
||||
}
|
||||
}
|
||||
|
||||
public class CompletionItem
|
||||
{
|
||||
public string Label { get; set; }
|
||||
public string Kind { get; set; }
|
||||
public string Documentation { get; set; }
|
||||
public string InsertText { get; set; }
|
||||
}
|
||||
}
|
42
WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/DevTools.cs
Normal file
|
@ -0,0 +1,42 @@
|
|||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WelsonJS.Launcher.ResourceTools
|
||||
{
|
||||
public class DevTools : IResourceTool
|
||||
{
|
||||
private ResourceServer Server;
|
||||
private readonly HttpClient _httpClient;
|
||||
private const string Prefix = "devtools/";
|
||||
|
||||
public DevTools(ResourceServer server, HttpClient httpClient)
|
||||
{
|
||||
Server = server;
|
||||
_httpClient = httpClient;
|
||||
}
|
||||
|
||||
public bool CanHandle(string path)
|
||||
{
|
||||
return path.StartsWith(Prefix, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public async Task HandleAsync(HttpListenerContext context, string path)
|
||||
{
|
||||
string endpoint = path.Substring(Prefix.Length);
|
||||
|
||||
try
|
||||
{
|
||||
string url = Program.GetAppConfig("DevToolsPrefix") + endpoint;
|
||||
string data = await _httpClient.GetStringAsync(url);
|
||||
|
||||
Server.ServeResource(context, data, "application/json");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Server.ServeResource(context, $"<error>Failed to process DevTools request. {ex.Message}</error>", "application/xml", 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
343
WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/DnsQuery.cs
Normal file
|
@ -0,0 +1,343 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WelsonJS.Launcher.ResourceTools
|
||||
{
|
||||
public class DnsQuery : IResourceTool
|
||||
{
|
||||
private readonly ResourceServer Server;
|
||||
private readonly HttpClient _httpClient;
|
||||
private const string Prefix = "dns-query/";
|
||||
private string DnsServer;
|
||||
private const int DnsPort = 53;
|
||||
private const int Timeout = 5000;
|
||||
private static readonly Random _random = new Random();
|
||||
|
||||
public DnsQuery(ResourceServer server, HttpClient httpClient)
|
||||
{
|
||||
Server = server;
|
||||
_httpClient = httpClient;
|
||||
|
||||
DnsServer = Program.GetAppConfig("DnsServerAddress");
|
||||
}
|
||||
|
||||
public bool CanHandle(string path)
|
||||
{
|
||||
return path.StartsWith(Prefix, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public async Task HandleAsync(HttpListenerContext context, string path)
|
||||
{
|
||||
await Task.Delay(0);
|
||||
|
||||
string query = path.Substring(Prefix.Length);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(query) || query.Length > 255)
|
||||
{
|
||||
Server.ServeResource(context, "<error>Invalid query parameter</error>", "application/xml", 400);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Dictionary<string, List<string>> allRecords = QueryAll(query);
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
foreach (var recordType in allRecords.Keys)
|
||||
{
|
||||
result.AppendLine($"\n{recordType} Records:");
|
||||
foreach (var record in allRecords[recordType])
|
||||
{
|
||||
result.AppendLine(record);
|
||||
}
|
||||
}
|
||||
|
||||
string data = result.ToString();
|
||||
Server.ServeResource(context, data, "text/plain", 200);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Server.ServeResource(context, $"<error>Failed to process DNS query. {ex.Message}</error>", "application/xml", 500);
|
||||
}
|
||||
}
|
||||
|
||||
public List<string> QueryA(string domain) => QueryDns(domain, 1);
|
||||
public List<string> QueryNS(string domain) => QueryDns(domain, 2);
|
||||
public List<string> QueryCNAME(string domain) => QueryDns(domain, 5);
|
||||
public List<string> QuerySOA(string domain) => QueryDns(domain, 6);
|
||||
public List<string> QueryPTR(string domain) => QueryDns(domain, 12);
|
||||
public List<string> QueryMX(string domain) => QueryDns(domain, 15);
|
||||
public List<string> QueryTXT(string domain) => QueryDns(domain, 16);
|
||||
public List<string> QueryAAAA(string domain) => QueryDns(domain, 28);
|
||||
public List<string> QuerySRV(string domain) => QueryDns(domain, 33);
|
||||
public List<string> QueryNAPTR(string domain) => QueryDns(domain, 35);
|
||||
public List<string> QueryCAA(string domain) => QueryDns(domain, 257);
|
||||
|
||||
public Dictionary<string, List<string>> QueryAll(string domain)
|
||||
{
|
||||
var results = new Dictionary<string, List<string>>();
|
||||
|
||||
results["A"] = QueryA(domain);
|
||||
results["NS"] = QueryNS(domain);
|
||||
results["CNAME"] = QueryCNAME(domain);
|
||||
results["SOA"] = QuerySOA(domain);
|
||||
results["PTR"] = QueryPTR(domain);
|
||||
results["MX"] = QueryMX(domain);
|
||||
results["TXT"] = QueryTXT(domain);
|
||||
results["AAAA"] = QueryAAAA(domain);
|
||||
results["SRV"] = QuerySRV(domain);
|
||||
results["NAPTR"] = QueryNAPTR(domain);
|
||||
results["CAA"] = QueryCAA(domain);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private List<string> QueryDns(string domain, ushort type)
|
||||
{
|
||||
List<string> records = new List<string>();
|
||||
|
||||
// Validate domain format
|
||||
if (string.IsNullOrWhiteSpace(domain))
|
||||
{
|
||||
records.Add("Error: Domain cannot be empty");
|
||||
return records;
|
||||
}
|
||||
|
||||
// Basic domain format validation
|
||||
if (domain.Length > 255 ||
|
||||
!domain.Split('.').All(part => part.Length > 0 && part.Length <= 63))
|
||||
{
|
||||
records.Add("Error: Invalid domain format");
|
||||
return records;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using (UdpClient udpClient = new UdpClient(DnsServer, DnsPort))
|
||||
{
|
||||
udpClient.Client.ReceiveTimeout = Timeout;
|
||||
|
||||
byte[] request = CreateDnsQuery(domain, type);
|
||||
udpClient.Send(request, request.Length);
|
||||
|
||||
IPEndPoint remoteEP = new IPEndPoint(IPAddress.Any, DnsPort);
|
||||
byte[] response = udpClient.Receive(ref remoteEP);
|
||||
|
||||
records.AddRange(ParseDnsResponse(response, type));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
records.Add($"Error: {ex.Message}");
|
||||
}
|
||||
|
||||
return records;
|
||||
}
|
||||
|
||||
private byte[] CreateDnsQuery(string domain, ushort type)
|
||||
{
|
||||
byte[] query = new byte[512];
|
||||
|
||||
query[0] = (byte)_random.Next(0, 256);
|
||||
query[1] = (byte)_random.Next(0, 256);
|
||||
query[2] = 0x01;
|
||||
query[3] = 0x00;
|
||||
query[4] = 0x00;
|
||||
query[5] = 0x01;
|
||||
|
||||
for (int i = 6; i < 12; i++)
|
||||
query[i] = 0x00;
|
||||
|
||||
int position = 12;
|
||||
foreach (string part in domain.Split('.'))
|
||||
{
|
||||
query[position++] = (byte)part.Length;
|
||||
byte[] label = Encoding.ASCII.GetBytes(part);
|
||||
Array.Copy(label, 0, query, position, label.Length);
|
||||
position += label.Length;
|
||||
}
|
||||
query[position++] = 0x00;
|
||||
|
||||
query[position++] = (byte)(type >> 8);
|
||||
query[position++] = (byte)(type & 0xFF);
|
||||
query[position++] = 0x00;
|
||||
query[position++] = 0x01;
|
||||
|
||||
byte[] finalQuery = new byte[position];
|
||||
Array.Copy(query, finalQuery, position);
|
||||
|
||||
return finalQuery;
|
||||
}
|
||||
|
||||
private List<string> ParseDnsResponse(byte[] response, ushort queryType)
|
||||
{
|
||||
List<string> results = new List<string>();
|
||||
|
||||
// Check response code from DNS server
|
||||
int responseCode = response[3] & 0x0F;
|
||||
if (responseCode != 0)
|
||||
{
|
||||
string errorMessage = "DNS server returned error: ";
|
||||
switch (responseCode)
|
||||
{
|
||||
case 1: errorMessage += "Format Error"; break;
|
||||
case 2: errorMessage += "Server Failure"; break;
|
||||
case 3: errorMessage += "Name Error (Domain does not exist)"; break;
|
||||
case 4: errorMessage += "Not Implemented"; break;
|
||||
case 5: errorMessage += "Refused"; break;
|
||||
default: errorMessage += $"Unknown Error ({responseCode})"; break;
|
||||
}
|
||||
results.Add(errorMessage);
|
||||
return results;
|
||||
}
|
||||
|
||||
int answerCount = (response[6] << 8) | response[7];
|
||||
if (answerCount == 0)
|
||||
{
|
||||
results.Add("No records found.");
|
||||
return results;
|
||||
}
|
||||
|
||||
int position = 12; // Skip DNS header
|
||||
|
||||
while (response[position] != 0)
|
||||
position += response[position] + 1;
|
||||
position += 5; // End of Question section
|
||||
|
||||
for (int i = 0; i < answerCount; i++)
|
||||
{
|
||||
while (response[position] != 0 && (response[position] & 0xC0) == 0)
|
||||
position += response[position] + 1;
|
||||
position += 2; // Skip Type, Class fields
|
||||
|
||||
ushort recordType = (ushort)((response[position] << 8) | response[position + 1]);
|
||||
position += 8; // Skip TTL and Data length fields
|
||||
|
||||
ushort dataLength = (ushort)((response[position] << 8) | response[position + 1]);
|
||||
position += 2; // Read Data Length
|
||||
|
||||
byte[] data = new byte[dataLength];
|
||||
Array.Copy(response, position, data, 0, dataLength);
|
||||
position += dataLength;
|
||||
|
||||
switch (recordType)
|
||||
{
|
||||
case 1:
|
||||
results.Add($"A: {new IPAddress(data)}");
|
||||
break;
|
||||
case 2:
|
||||
results.Add($"NS: {DecodeDomainName(response, data, 0)}");
|
||||
break;
|
||||
case 5:
|
||||
results.Add($"CNAME: {DecodeDomainName(response, data, 0)}");
|
||||
break;
|
||||
case 6:
|
||||
results.Add($"SOA: {DecodeDomainName(response, data, 0)}");
|
||||
break;
|
||||
case 12:
|
||||
results.Add($"PTR: {DecodeDomainName(response, data, 0)}");
|
||||
break;
|
||||
case 15:
|
||||
// MX record processing (priority and exchange)
|
||||
if (data.Length >= 2)
|
||||
{
|
||||
ushort priority = (ushort)((data[0] << 8) | data[1]);
|
||||
string exchange = DecodeDomainName(response, data, 2); // Decode domain name after 2 bytes
|
||||
results.Add($"MX: Priority {priority}, Exchange {exchange}");
|
||||
}
|
||||
else
|
||||
{
|
||||
results.Add($"MX: Invalid data length.");
|
||||
}
|
||||
break;
|
||||
case 16:
|
||||
int txtPos = 0;
|
||||
while (txtPos < data.Length)
|
||||
{
|
||||
int txtLength = data[txtPos++];
|
||||
results.Add($"TXT: {Encoding.UTF8.GetString(data, txtPos, txtLength)}");
|
||||
txtPos += txtLength;
|
||||
}
|
||||
break;
|
||||
case 28:
|
||||
results.Add($"AAAA: {new IPAddress(data)}");
|
||||
break;
|
||||
case 33:
|
||||
ushort prioritySrv = (ushort)((data[0] << 8) | data[1]);
|
||||
ushort weight = (ushort)((data[2] << 8) | data[3]);
|
||||
ushort port = (ushort)((data[4] << 8) | data[5]);
|
||||
string target = DecodeDomainName(response, data, 6);
|
||||
results.Add($"SRV: Priority {prioritySrv}, Weight {weight}, Port {port}, Target {target}");
|
||||
break;
|
||||
case 35:
|
||||
if (data.Length >= 7)
|
||||
{
|
||||
ushort order = (ushort)((data[0] << 8) | data[1]);
|
||||
ushort preference = (ushort)((data[2] << 8) | data[3]);
|
||||
// Extract flags, services, regexp and replacement
|
||||
results.Add($"NAPTR: Order {order}, Preference {preference}");
|
||||
}
|
||||
else
|
||||
{
|
||||
results.Add($"NAPTR: Invalid data length: {BitConverter.ToString(data)}");
|
||||
}
|
||||
break;
|
||||
case 257:
|
||||
if (data.Length >= 2)
|
||||
{
|
||||
byte flags = data[0];
|
||||
int tagLen = data[1];
|
||||
if (data.Length >= 2 + tagLen)
|
||||
{
|
||||
string tag = Encoding.ASCII.GetString(data, 2, tagLen);
|
||||
string value = Encoding.ASCII.GetString(data, 2 + tagLen, data.Length - 2 - tagLen);
|
||||
results.Add($"CAA: Flags {flags}, Tag {tag}, Value {value}");
|
||||
}
|
||||
else
|
||||
{
|
||||
results.Add($"CAA: Invalid data length: {BitConverter.ToString(data)}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
results.Add($"CAA: Invalid data length: {BitConverter.ToString(data)}");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
results.Add($"Unknown Type {recordType}: {BitConverter.ToString(data)}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private string DecodeDomainName(byte[] response, byte[] data, int startIndex)
|
||||
{
|
||||
int position = startIndex;
|
||||
List<string> labels = new List<string>();
|
||||
|
||||
while (data[position] != 0)
|
||||
{
|
||||
// Handle 0xC0 pointer (compressed domain name handling)
|
||||
if ((data[position] & 0xC0) == 0xC0)
|
||||
{
|
||||
int pointer = ((data[position] & 0x3F) << 8) | data[position + 1];
|
||||
return DecodeDomainName(response, response, pointer); // Recursive call to decode from the pointer
|
||||
}
|
||||
|
||||
int labelLength = data[position++];
|
||||
labels.Add(Encoding.ASCII.GetString(data, position, labelLength));
|
||||
position += labelLength;
|
||||
}
|
||||
|
||||
return string.Join(".", labels);
|
||||
}
|
||||
}
|
||||
}
|
77
WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/Settings.cs
Normal file
|
@ -0,0 +1,77 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace WelsonJS.Launcher.ResourceTools
|
||||
{
|
||||
public class Settings : IResourceTool
|
||||
{
|
||||
private readonly ResourceServer Server;
|
||||
private readonly HttpClient _httpClient;
|
||||
private const string Prefix = "settings";
|
||||
|
||||
public Settings(ResourceServer server, HttpClient httpClient)
|
||||
{
|
||||
Server = server;
|
||||
_httpClient = httpClient;
|
||||
}
|
||||
|
||||
public bool CanHandle(string path)
|
||||
{
|
||||
return path.Equals(Prefix, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public async Task HandleAsync(HttpListenerContext context, string path)
|
||||
{
|
||||
await Task.Delay(0);
|
||||
|
||||
// Get current namespace (e.g., WelsonJS.Launcher)
|
||||
string ns = typeof(Program).Namespace;
|
||||
|
||||
// Build resource base name (e.g., WelsonJS.Launcher.Properties.Resources)
|
||||
string resourceBaseName = ns + ".Properties.Resources";
|
||||
|
||||
// Load resource strings using ResourceManager
|
||||
var resourceManager = new ResourceManager(resourceBaseName, Assembly.GetExecutingAssembly());
|
||||
var resourceSet = resourceManager.GetResourceSet(
|
||||
System.Globalization.CultureInfo.CurrentCulture,
|
||||
true,
|
||||
true
|
||||
);
|
||||
|
||||
var resourceStrings = new Dictionary<string, string>();
|
||||
foreach (System.Collections.DictionaryEntry entry in resourceSet)
|
||||
{
|
||||
if (entry.Value is string strValue)
|
||||
{
|
||||
resourceStrings[(string)entry.Key] = strValue;
|
||||
}
|
||||
}
|
||||
|
||||
// Load settings from app.config
|
||||
var appConfig = ConfigurationManager.AppSettings.AllKeys
|
||||
.ToDictionary(k => k, k => ConfigurationManager.AppSettings[k]);
|
||||
|
||||
// Merge by starting with resourceStrings and letting app.config override
|
||||
var finalConfig = new Dictionary<string, string>(resourceStrings);
|
||||
foreach (var kv in appConfig)
|
||||
{
|
||||
finalConfig[kv.Key] = kv.Value;
|
||||
}
|
||||
|
||||
// Generate XML using XElement
|
||||
XElement xml = new XElement("settings",
|
||||
finalConfig.Select(kv => new XElement(kv.Key, kv.Value))
|
||||
);
|
||||
|
||||
Server.ServeResource(context, xml.ToString(), "application/xml");
|
||||
}
|
||||
}
|
||||
}
|
103
WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/Tfa.cs
Normal file
|
@ -0,0 +1,103 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace WelsonJS.Launcher.ResourceTools
|
||||
{
|
||||
public class Tfa : IResourceTool
|
||||
{
|
||||
private readonly ResourceServer Server;
|
||||
private readonly HttpClient _httpClient;
|
||||
private const string Prefix = "tfa/";
|
||||
private const string Base32Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||||
|
||||
public Tfa(ResourceServer server, HttpClient httpClient)
|
||||
{
|
||||
Server = server;
|
||||
_httpClient = httpClient;
|
||||
}
|
||||
|
||||
public bool CanHandle(string path)
|
||||
{
|
||||
return path.StartsWith(Prefix, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public async Task HandleAsync(HttpListenerContext context, string path)
|
||||
{
|
||||
await Task.Delay(0);
|
||||
|
||||
string endpoint = path.Substring(Prefix.Length);
|
||||
|
||||
if (endpoint.Equals("pubkey"))
|
||||
{
|
||||
Server.ServeResource(context, GetPubKey(), "text/plain", 200);
|
||||
return;
|
||||
}
|
||||
|
||||
Server.ServeResource(context);
|
||||
}
|
||||
|
||||
public int GetOtp(string key)
|
||||
{
|
||||
byte[] binaryKey = DecodeBase32(key.Replace(" ", ""));
|
||||
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds() / 30;
|
||||
byte[] timestampBytes = BitConverter.GetBytes(timestamp);
|
||||
Array.Reverse(timestampBytes); // Ensure big-endian order
|
||||
|
||||
using (var hmac = new HMACSHA1(binaryKey))
|
||||
{
|
||||
byte[] hash = hmac.ComputeHash(timestampBytes);
|
||||
int offset = hash[hash.Length - 1] & 0xF;
|
||||
|
||||
int otp = ((hash[offset] & 0x7F) << 24) |
|
||||
((hash[offset + 1] & 0xFF) << 16) |
|
||||
((hash[offset + 2] & 0xFF) << 8) |
|
||||
(hash[offset + 3] & 0xFF);
|
||||
|
||||
return otp % 1000000; // Ensure 6-digit OTP
|
||||
}
|
||||
}
|
||||
|
||||
public string GetPubKey()
|
||||
{
|
||||
using (var rng = RandomNumberGenerator.Create())
|
||||
{
|
||||
var key = new char[16];
|
||||
var randomBytes = new byte[16];
|
||||
rng.GetBytes(randomBytes);
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
key[i] = Base32Chars[randomBytes[i] % Base32Chars.Length];
|
||||
}
|
||||
|
||||
return string.Join(" ", Enumerable.Range(0, 4).Select(i => new string(key, i * 4, 4)));
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] DecodeBase32(string key)
|
||||
{
|
||||
int buffer = 0, bitsLeft = 0;
|
||||
var binaryKey = new List<byte>();
|
||||
|
||||
foreach (char c in key)
|
||||
{
|
||||
int value = Base32Chars.IndexOf(c);
|
||||
if (value < 0) continue; // Ignore invalid characters
|
||||
|
||||
buffer = (buffer << 5) + value;
|
||||
bitsLeft += 5;
|
||||
if (bitsLeft >= 8)
|
||||
{
|
||||
bitsLeft -= 8;
|
||||
binaryKey.Add((byte)((buffer >> bitsLeft) & 0xFF));
|
||||
}
|
||||
}
|
||||
return binaryKey.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
60
WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/Whois.cs
Normal file
|
@ -0,0 +1,60 @@
|
|||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
|
||||
namespace WelsonJS.Launcher.ResourceTools
|
||||
{
|
||||
public class Whois : IResourceTool
|
||||
{
|
||||
private readonly ResourceServer Server;
|
||||
private readonly HttpClient _httpClient;
|
||||
private const string Prefix = "whois/";
|
||||
|
||||
public Whois(ResourceServer server, HttpClient httpClient)
|
||||
{
|
||||
Server = server;
|
||||
_httpClient = httpClient;
|
||||
}
|
||||
|
||||
public bool CanHandle(string path)
|
||||
{
|
||||
return path.StartsWith(Prefix, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public async Task HandleAsync(HttpListenerContext context, string path)
|
||||
{
|
||||
string query = path.Substring(Prefix.Length);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(query) || query.Length > 255)
|
||||
{
|
||||
Server.ServeResource(context, "<error>Invalid query parameter</error>", "application/xml", 400);
|
||||
return;
|
||||
}
|
||||
|
||||
string clientAddress = Program.GetAppConfig("WhoisClientAddress");
|
||||
|
||||
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, Program.GetAppConfig("WhoisServerUrl"))
|
||||
{
|
||||
Content = new StringContent($"query={Uri.EscapeDataString(query)}&ip={clientAddress}", Encoding.UTF8, "application/x-www-form-urlencoded")
|
||||
};
|
||||
|
||||
request.Headers.Add("Accept", "*/*");
|
||||
request.Headers.Add("User-Agent", context.Request.UserAgent);
|
||||
_httpClient.DefaultRequestHeaders.Referrer = new Uri(Program.GetAppConfig("WhoisReferrerUrl"));
|
||||
|
||||
try
|
||||
{
|
||||
HttpResponseMessage response = await _httpClient.SendAsync(request);
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
|
||||
Server.ServeResource(context, responseBody, "text/plain", (int)response.StatusCode);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Server.ServeResource(context, $"<error>Failed to process WHOIS request. {ex.Message}</error>", "application/xml", 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
BIN
WelsonJS.Toolkit/WelsonJS.Launcher/Resources/favicon.ico
Normal file
After Width: | Height: | Size: 56 KiB |
BIN
WelsonJS.Toolkit/WelsonJS.Launcher/Resources/icon_check_32.png
Normal file
After Width: | Height: | Size: 317 B |
BIN
WelsonJS.Toolkit/WelsonJS.Launcher/Resources/icon_delete_32.png
Normal file
After Width: | Height: | Size: 974 B |
After Width: | Height: | Size: 179 B |
BIN
WelsonJS.Toolkit/WelsonJS.Launcher/Resources/icon_export_32.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
WelsonJS.Toolkit/WelsonJS.Launcher/Resources/icon_file_32.png
Normal file
After Width: | Height: | Size: 230 B |
BIN
WelsonJS.Toolkit/WelsonJS.Launcher/Resources/icon_import_32.png
Normal file
After Width: | Height: | Size: 837 B |
BIN
WelsonJS.Toolkit/WelsonJS.Launcher/Resources/icon_link_128.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
WelsonJS.Toolkit/WelsonJS.Launcher/Resources/icon_start_32.png
Normal file
After Width: | Height: | Size: 796 B |
BIN
WelsonJS.Toolkit/WelsonJS.Launcher/Resources/icon_zip_128.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
169
WelsonJS.Toolkit/WelsonJS.Launcher/WelsonJS.Launcher.csproj
Normal file
|
@ -0,0 +1,169 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{4074D413-195C-45E9-9E63-0D07914187B8}</ProjectGuid>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<RootNamespace>WelsonJS.Launcher</RootNamespace>
|
||||
<AssemblyName>WelsonJS.Launcher</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<Deterministic>true</Deterministic>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<OutputPath>bin\x86\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>favicon.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Deployment" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.IO.Compression.FileSystem" />
|
||||
<Reference Include="System.IO.Compression.ZipFile, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.IO.Compression.ZipFile.4.3.0\lib\net46\System.IO.Compression.ZipFile.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="IResourceTool.cs" />
|
||||
<Compile Include="ResourceTools\Settings.cs" />
|
||||
<Compile Include="ResourceTools\Completion.cs" />
|
||||
<Compile Include="ResourceTools\DevTools.cs" />
|
||||
<Compile Include="ResourceTools\DnsQuery.cs" />
|
||||
<Compile Include="ResourceTools\Tfa.cs" />
|
||||
<Compile Include="ResourceTools\Whois.cs" />
|
||||
<Compile Include="EnvForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="EnvForm.Designer.cs">
|
||||
<DependentUpon>EnvForm.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="InstancesForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="InstancesForm.Designer.cs">
|
||||
<DependentUpon>InstancesForm.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="MainForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="MainForm.Designer.cs">
|
||||
<DependentUpon>MainForm.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="GlobalSettingsForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="GlobalSettingsForm.Designer.cs">
|
||||
<DependentUpon>GlobalSettingsForm.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="ResourceServer.cs" />
|
||||
<EmbeddedResource Include="EnvForm.resx">
|
||||
<DependentUpon>EnvForm.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="InstancesForm.resx">
|
||||
<DependentUpon>InstancesForm.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="MainForm.resx">
|
||||
<DependentUpon>MainForm.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="GlobalSettingsForm.resx">
|
||||
<DependentUpon>GlobalSettingsForm.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<None Include="app.config" />
|
||||
<None Include="packages.config" />
|
||||
<None Include="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Properties\Settings.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Resources\icon_link_128.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Resources\icon_zip_128.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="favicon.ico" />
|
||||
<None Include="Resources\favicon.ico" />
|
||||
<None Include="Resources\icon_check_32.png" />
|
||||
<None Include="Resources\icon_directory_32.png" />
|
||||
<None Include="Resources\icon_file_32.png" />
|
||||
<None Include="Resources\icon_export_32.png" />
|
||||
<None Include="Resources\icon_import_32.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Resources\icon_delete_32.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Resources\icon_start_32.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="editor.html" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
22
WelsonJS.Toolkit/WelsonJS.Launcher/app.config
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<appSettings>
|
||||
<add key="ResourceServerPrefix" value="http://localhost:3000/"/>
|
||||
<add key="RepositoryUrl" value="https://github.com/gnh1201/welsonjs"/>
|
||||
<add key="CopilotUrl" value="https://copilot.microsoft.com/"/>
|
||||
<add key="DevToolsPrefix" value="http://localhost:9222/"/>
|
||||
<add key="AzureAiServicePrefix" value="https://ai-catswords656881030318.services.ai.azure.com/"/>
|
||||
<add key="AzureAiServiceApiKey" value=""/>
|
||||
<add key="AzureAiServiceApiVersion" value="2024-05-01-preview"/>
|
||||
<add key="WhoisServerUrl" value="https://xn--c79as89aj0e29b77z.xn--3e0b707e/kor/whois.jsc"/>
|
||||
<add key="WhoisReferrerUrl" value="https://xn--c79as89aj0e29b77z.xn--3e0b707e/kor/whois/whois.jsp"/>
|
||||
<add key="WhoisClientAddress" value="141.101.82.1"/>
|
||||
<add key="DnsServerAddress" value="1.1.1.1"/>
|
||||
<add key="BlobStoragePrefix" value="https://catswords.blob.core.windows.net/welsonjs/"/>
|
||||
<add key="BlobConfigUrl" value="https://catswords.blob.core.windows.net/welsonjs/blob.config.xml"/>
|
||||
<add key="HttpClientTimeout" value="90"/>
|
||||
</appSettings>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2"/>
|
||||
</startup>
|
||||
</configuration>
|
597
WelsonJS.Toolkit/WelsonJS.Launcher/editor.html
Normal file
|
@ -0,0 +1,597 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>WelsonJS Editor</title>
|
||||
<meta name="title" content="WelsonJS Editor">
|
||||
<meta name="description" content="WelsonJS can build a Windows app on the Windows built-in JavaScript engine.">
|
||||
<meta name="keywords" content="ecmascript, javascript, windows">
|
||||
<meta name="robots" content="noindex, nofollow">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<meta name="language" content="English">
|
||||
<meta name="author" content="Namhyeon Go (abuse@catswords.net)">
|
||||
<style>
|
||||
html, body, #app, #app > .app {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#container {
|
||||
border: 1px solid grey;
|
||||
height: calc(100% - 167px);
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#editor {
|
||||
flex: 3;
|
||||
}
|
||||
|
||||
#promptEditor {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.banner {
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
background-color: #f1f1f1;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script>
|
||||
var require = {
|
||||
paths: {
|
||||
vs: 'http://localhost:3000/ajax/libs/monaco-editor/0.52.2/min/vs'
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<script src="http://localhost:3000/ajax/libs/lodash/4.17.21/lodash.min.js" integrity="sha384-H6KKS1H1WwuERMSm+54dYLzjg0fKqRK5ZRyASdbrI/lwrCc6bXEmtGYr5SwvP1pZ" crossorigin="anonymous"></script>
|
||||
<script src="http://localhost:3000/ajax/libs/jsoneditor/10.1.3/jsoneditor.min.js" integrity="sha384-NdVVc20Tze856ZAWEoJNCk0mL4zJrGztRwULc5Hz25HUXQQgYtmjFtgVAxR4p5dD" crossorigin="anonymous"></script>
|
||||
<script src="http://localhost:3000/ajax/libs/react/18.3.1/umd/react.production.min.js" integrity="sha384-DGyLxAyjq0f9SPpVevD6IgztCFlnMF6oW/XQGmfe+IsZ8TqEiDrcHkMLKI6fiB/Z" crossorigin="anonymous"></script>
|
||||
<script src="http://localhost:3000/ajax/libs/react-dom/18.3.1/umd/react-dom.production.min.js" integrity="sha384-gTGxhz21lVGYNMcdJOyq01Edg0jhn/c22nsx0kyqP0TxaV5WVdsSH1fSDUf5YJj1" crossorigin="anonymous"></script>
|
||||
<script src="http://localhost:3000/ajax/libs/axios/1.8.4/axios.min.js" integrity="sha384-06w+raHvkSL3+E7mbQ2X6DZwI5A3veU8Ba+NLrAPxxRGw4Xy78sihHDHQMustMM4" crossorigin="anonymous"></script>
|
||||
<script src="http://localhost:3000/ajax/libs/fast-xml-parser/4.5.1/fxparser.min.js" integrity="sha384-ae/HepOQ8hiJ/VA6yGwPMGXQXOkT/lJpjlcQ7EUgibUcfnBltuozgNj4IgOZ9QLc" crossorigin="anonymous"></script>
|
||||
<script src="http://localhost:3000/ajax/libs/dompurify/3.2.4/purify.min.js" integrity="sha384-eEu5CTj3qGvu9PdJuS+YlkNi7d2XxQROAFYOr59zgObtlcux1ae1Il3u7jvdCSWu" crossorigin="anonymous"></script>
|
||||
<script src="http://localhost:3000/ajax/libs/monaco-editor/0.52.2/min/vs/loader.js" integrity="sha384-pHG02SG8pId94Np3AbPmBEJ1yPqaH0IkJGLSNGXYmuGhkazT8Lr/57WYpbkGjJtu" crossorigin="anonymous"></script>
|
||||
<script src="http://localhost:3000/ajax/libs/monaco-editor/0.52.2/min/vs/editor/editor.main.js" integrity="sha384-fj9z+NUc93I3woCCy5IRQfrQ8Amu1E27tllwgb5gz3d9Vr1ymS13xcF6two3e4KH" crossorigin="anonymous"></script>
|
||||
<script>
|
||||
function loadResource(url, mimeType, integrity) {
|
||||
mimeType = mimeType || 'application/javascript';
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let el;
|
||||
|
||||
if (mimeType === 'text/css') {
|
||||
el = document.createElement('link');
|
||||
el.rel = 'stylesheet';
|
||||
el.type = mimeType;
|
||||
el.href = url;
|
||||
} else {
|
||||
el = document.createElement('script');
|
||||
el.type = mimeType;
|
||||
el.src = url;
|
||||
}
|
||||
|
||||
if (integrity) {
|
||||
el.integrity = integrity;
|
||||
el.crossOrigin = 'anonymous';
|
||||
}
|
||||
|
||||
el.onload = resolve;
|
||||
el.onerror = reject;
|
||||
|
||||
(mimeType === 'text/css' ? document.head : document.body).appendChild(el);
|
||||
});
|
||||
}
|
||||
|
||||
Promise.all([
|
||||
loadResource("http://localhost:3000/ajax/libs/metroui/dev/lib/metro.css", "text/css", "sha384-4XgOiXH2ZMaWt5s5B35yKi7EAOabhZvx7wO8Jr71q2vZ+uONdRza/6CsK2kpyocd"),
|
||||
loadResource("http://localhost:3000/ajax/libs/metroui/dev/lib/icons.css", "text/css", "sha384-FuLND994etg+RtnpPSPMyNBvL+fEz+xGhbN61WUWuDEeZ+wJzcQ8SGqAMuI5hWrt"),
|
||||
loadResource("http://localhost:3000/ajax/libs/monaco-editor/0.52.2/min/vs/editor/editor.main.css", "text/css", "sha384-06yHXpYRlHEPaR4AS0fB/W+lMN09Zh5e1XMtfkNQdHV38OlhfkOEW5M+pCj3QskC"),
|
||||
loadResource("http://localhost:3000/ajax/libs/jsoneditor/10.1.3/jsoneditor.min.css", "text/css", "sha384-cj1rYBc4/dVYAknZMTkVCDRL6Knzugf32igVqsuFW0iRWFHKH8Ci8+ekC8gNsFZ+")
|
||||
]).then(() => {
|
||||
const _e = React.createElement;
|
||||
|
||||
function Button({ id, icon, caption, onClick }) {
|
||||
return _e(
|
||||
'button',
|
||||
{ id, className: 'ribbon-button', onClick },
|
||||
_e('span', { className: `icon ${icon}` }),
|
||||
_e('span', { className: 'caption' }, caption)
|
||||
);
|
||||
}
|
||||
|
||||
function Group({ title, buttons }) {
|
||||
return _e(
|
||||
'div',
|
||||
{ className: 'group' },
|
||||
buttons.map((btn, index) =>
|
||||
_e(Button, { key: index, ...btn })
|
||||
),
|
||||
_e('span', { className: 'title' }, title)
|
||||
);
|
||||
}
|
||||
|
||||
function RibbonMenu({
|
||||
onOpenFileClick, onSaveFileClick, onCopliotClick, onAzureAiClick,
|
||||
onSavePromptClick, onLoadPromptClick, onQueryWhoisClick, onQueryDnsClick
|
||||
}) {
|
||||
const fileButtons = [
|
||||
{
|
||||
id: 'btnOpenFile',
|
||||
icon: 'mif-folder-open',
|
||||
caption: 'Open File',
|
||||
onClick: onOpenFileClick
|
||||
},
|
||||
{
|
||||
id: 'btnSaveFile',
|
||||
icon: 'mif-floppy-disks',
|
||||
caption: 'Save File',
|
||||
onClick: onSaveFileClick
|
||||
}
|
||||
];
|
||||
|
||||
const aiButtons = [
|
||||
{ id: 'btnCopilot', icon: 'mif-rocket', caption: 'Copilot', onClick: onCopliotClick },
|
||||
{ id: 'btnAzureAi', icon: 'mif-rocket', caption: 'Azure AI', onClick: onAzureAiClick },
|
||||
{ id: 'btnSavePrompt', icon: 'mif-floppy-disks', caption: 'Save', onClick: onSavePromptClick },
|
||||
{ id: 'btnLoadPrompt', icon: 'mif-file-upload', caption: 'Load', onClick: onLoadPromptClick }
|
||||
];
|
||||
|
||||
const networkToolsButtons = [
|
||||
{ id: 'btnWhois', icon: 'mif-earth', caption: 'Whois', onClick: onQueryWhoisClick },
|
||||
{ id: 'btnDnsQuery', icon: 'mif-earth', caption: 'DNS', onClick: onQueryDnsClick }
|
||||
];
|
||||
|
||||
return _e(
|
||||
'nav',
|
||||
{ 'className': 'ribbon-menu' },
|
||||
_e('ul', { className: 'tabs-holder' },
|
||||
_e('li', { className: 'static' }, _e('a', { href: '#heading-tab' }, 'WelsonJS')),
|
||||
_e('li', { className: 'active' }, _e('a', { href: '#editor-tab' }, 'Editor'))
|
||||
),
|
||||
_e('div', { className: 'content-holder' },
|
||||
_e('div', { className: 'section active', id: 'editor-tab' },
|
||||
_e(Group, { title: 'File', buttons: fileButtons }),
|
||||
_e(Group, { title: 'Generative AI', buttons: aiButtons }),
|
||||
_e(Group, { title: 'Network tools', buttons: networkToolsButtons })
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function Editor({ editorRef }) {
|
||||
const containerRef = React.useRef(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!containerRef.current)
|
||||
return;
|
||||
|
||||
require(['vs/editor/editor.main'], () => {
|
||||
const instance = monaco.editor.create(containerRef.current, {
|
||||
value: ['// lib/sayhello.js', 'function say() {', ' console.log("hello");', '}', '', 'exports.say = say;', '', 'exports.VERSIONINFO = "SayHello (sayhello.js) version 0.1";', 'exports.AUTHOR = "abuse@catswords.net";', 'exports.global = global;', 'exports.require = global.require;'].join('\n'),
|
||||
language: 'javascript'
|
||||
});
|
||||
|
||||
editorRef.current = instance;
|
||||
});
|
||||
}, []);
|
||||
|
||||
return _e('div', { id: 'editor', ref: containerRef });
|
||||
}
|
||||
|
||||
function PromptEditor({ promptEditorRef, promptMessagesRef }) {
|
||||
const containerRef = React.useRef(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!containerRef.current)
|
||||
return;
|
||||
|
||||
const invoke = () => {
|
||||
try {
|
||||
if (promptEditorRef.current) {
|
||||
const updated = promptEditorRef.current.get();
|
||||
promptMessagesRef.current = updated;
|
||||
} else {
|
||||
throw new Error("promptEditorRef.current is null");
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Invalid JSON structure", e);
|
||||
}
|
||||
};
|
||||
|
||||
const throttledInvoke = _.throttle(invoke, 300, {
|
||||
leading: true,
|
||||
trailing: true
|
||||
});
|
||||
|
||||
const options = {
|
||||
onChange: throttledInvoke
|
||||
};
|
||||
|
||||
const instance = new JSONEditor(containerRef.current, options);
|
||||
instance.set(promptMessagesRef.current);
|
||||
|
||||
promptEditorRef.current = instance;
|
||||
|
||||
return () => {
|
||||
if (instance) {
|
||||
throttledInvoke.flush();
|
||||
throttledInvoke.cancel();
|
||||
instance.destroy();
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
return _e('div', { id: 'promptEditor', ref: containerRef });
|
||||
}
|
||||
|
||||
function App() {
|
||||
const serverPrefix = "http://localhost:3000/";
|
||||
const editorRef = React.useRef(null);
|
||||
const promptEditorRef = React.useRef(null);
|
||||
const settingsRef = React.useRef({});
|
||||
const fileNameRef = React.useRef('sayhello.js');
|
||||
const promptMessagesRef = React.useRef([]);
|
||||
|
||||
const fetchSettings = () => axios.get(`${serverPrefix}settings`)
|
||||
.then(response => {
|
||||
const parser = new XMLParser();
|
||||
const result = parser.parse(response.data);
|
||||
settingsRef.current = result.settings;
|
||||
});
|
||||
|
||||
const resizeEditor = () => {
|
||||
if (editorRef.current) {
|
||||
const ribbon = document.querySelector('nav')?.offsetHeight || 0;
|
||||
const banner = document.querySelector('.banner')?.offsetHeight || 0;
|
||||
const h = document.documentElement.clientHeight - ribbon - banner;
|
||||
const editorDiv = document.getElementById('editor');
|
||||
if (editorDiv) editorDiv.style.height = h + 'px';
|
||||
if (editorRef.current) editorRef.current.layout();
|
||||
}
|
||||
};
|
||||
|
||||
const getSuggestions = (word) => axios.get(`${serverPrefix}completion/${encodeURIComponent(word)}`)
|
||||
.then(response => {
|
||||
const parser = new XMLParser();
|
||||
const result = parser.parse(response.data);
|
||||
|
||||
if (!result.suggestions || !result.suggestions.item) {
|
||||
return {
|
||||
suggestions: []
|
||||
};
|
||||
}
|
||||
|
||||
const items = Array.isArray(result.suggestions.item) ? result.suggestions.item : [result.suggestions.item];
|
||||
const suggestions = items.map(function (item) {
|
||||
return {
|
||||
label: item.label,
|
||||
kind: monaco.languages.CompletionItemKind.Text,
|
||||
documentation: item.documentation || "",
|
||||
insertText: '"' + item.insertText + '"',
|
||||
range: range
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
suggestions: suggestions
|
||||
};
|
||||
})
|
||||
.catch(function () {
|
||||
return {
|
||||
suggestions: []
|
||||
};
|
||||
});
|
||||
|
||||
const pushPromptMessage = (role, content) => {
|
||||
promptMessagesRef.current.push({
|
||||
role: role,
|
||||
content: content
|
||||
});
|
||||
promptEditorRef.current.set(promptMessagesRef.current);
|
||||
};
|
||||
|
||||
const navigate = (href) => {
|
||||
const a = document.createElement("a");
|
||||
a.href = href;
|
||||
a.target = "_blank";
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
};
|
||||
|
||||
const appendTextToEditor = (text) => {
|
||||
const editor = editorRef.current;
|
||||
const position = editor.getPosition();
|
||||
const range = new monaco.Range(position.lineNumber, position.column, position.lineNumber, position.column);
|
||||
editor.executeEdits('', [{
|
||||
range: range,
|
||||
text: "\n" + text,
|
||||
forceMoveMarkers: true
|
||||
}]);
|
||||
resizeEditor();
|
||||
};
|
||||
|
||||
const openFile = () => {
|
||||
const fileInput = document.createElement('input');
|
||||
fileInput.type = 'file';
|
||||
fileInput.onchange = () => {
|
||||
const file = fileInput.files[0];
|
||||
|
||||
if (!file)
|
||||
return;
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
const fileName = file.name;
|
||||
const ext = fileName.split('.').pop().toLowerCase();
|
||||
const langMap = {
|
||||
js: 'javascript', ts: 'typescript', html: 'html',
|
||||
css: 'css', json: 'json', py: 'python', java: 'java',
|
||||
c: 'c', cpp: 'cpp', cs: 'csharp', php: 'php',
|
||||
rb: 'ruby', go: 'go', rs: 'rust'
|
||||
};
|
||||
const lang = langMap[ext] || 'plaintext';
|
||||
|
||||
monaco.editor.setModelLanguage(editorRef.current.getModel(), lang);
|
||||
editorRef.current.setValue(e.target.result);
|
||||
|
||||
fileNameRef.current = fileName;
|
||||
};
|
||||
reader.readAsText(file);
|
||||
};
|
||||
fileInput.click();
|
||||
};
|
||||
|
||||
const saveFile = () => {
|
||||
const text = editorRef.current.getValue();
|
||||
const fileName = prompt("Enter file name:", fileNameRef.current);
|
||||
|
||||
if (!fileName)
|
||||
return;
|
||||
|
||||
const blob = new Blob([text], { type: 'text/plain' });
|
||||
const a = document.createElement('a');
|
||||
a.href = URL.createObjectURL(blob);
|
||||
a.download = fileNameRef.current;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
};
|
||||
|
||||
const sendMessageToCopilot = () => {
|
||||
const promptMessage = prompt("Enter a prompt message:", '');
|
||||
if (!promptMessage || promptMessage.trim() == '') {
|
||||
alert("A prompt message is required.");
|
||||
return;
|
||||
}
|
||||
|
||||
appendTextToEditor(`\n// ${promptMessage}... Generating text with Copilot...`);
|
||||
pushPromptMessage("user", promptMessage);
|
||||
|
||||
(async () => {
|
||||
const targetWsUrl = await getTargetByUrl('copilot.microsoft.com');
|
||||
if (targetWsUrl) {
|
||||
await _sendMessageToCopilot(targetWsUrl, promptMessage);
|
||||
} else {
|
||||
alert("Microsoft Copilot not running. Please visit copilot.microsoft.com first.");
|
||||
}
|
||||
})();
|
||||
};
|
||||
|
||||
const _sendMessageToCopilot = async (wsUrl, promptMessage) => {
|
||||
const socket = new WebSocket(wsUrl);
|
||||
const steps = [
|
||||
{
|
||||
id: 1,
|
||||
method: 'Input.insertText',
|
||||
params: {
|
||||
text: promptMessage
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
method: 'Input.dispatchKeyEvent',
|
||||
params: {
|
||||
type: 'keyDown',
|
||||
key: 'Enter',
|
||||
code: 'Enter'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
method: 'Runtime.evaluate',
|
||||
params: {
|
||||
expression: '((e)=>e[e.length-1].querySelector("code")?.innerText||e[e.length-1].innerText)(document.querySelectorAll("[data-content=ai-message]"))'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
socket.onopen = () => {
|
||||
steps.forEach((step) => {
|
||||
if (step.id == 3) {
|
||||
setTimeout(() => {
|
||||
socket.send(JSON.stringify(step));
|
||||
}, 9000);
|
||||
} else {
|
||||
socket.send(JSON.stringify(step));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
socket.onmessage = (event) => {
|
||||
const response = JSON.parse(event.data);
|
||||
console.log("Sent successfully:", response.result);
|
||||
|
||||
if (response.id == 3) {
|
||||
const responseContent = response.result.result.value;
|
||||
|
||||
appendTextToEditor("/*\n" + responseContent + "\n*/");
|
||||
pushPromptMessage("assistant", responseContent);
|
||||
|
||||
socket.close();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const getTargetByUrl = async (urlPart) => {
|
||||
const response = await fetch(`${serverPrefix}devtools/json`);
|
||||
const targets = await response.json();
|
||||
|
||||
const target = targets.find(target => target.url.includes(urlPart));
|
||||
|
||||
if (target) {
|
||||
console.log(`Found target: ${target.title} (${target.id})`);
|
||||
return target.webSocketDebuggerUrl;
|
||||
} else {
|
||||
console.log('Target not found');
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const sendMessageToAzureAi = () => {
|
||||
const promptMessage = prompt("Enter a prompt message:", '');
|
||||
if (!promptMessage || promptMessage.trim() == '') {
|
||||
alert("A prompt message is required.");
|
||||
return;
|
||||
}
|
||||
|
||||
appendTextToEditor(`\n// ${promptMessage}... Generating text with Azure AI...`);
|
||||
pushPromptMessage("user", promptMessage);
|
||||
|
||||
const apiKey = settingsRef.current.AzureAiServiceApiKey;
|
||||
const url = `${settingsRef.current.AzureAiServicePrefix}models/chat/completions?api-version=${settingsRef.current.AzureAiServiceApiVersion}`;
|
||||
|
||||
const data = {
|
||||
messages: promptMessagesRef.current,
|
||||
max_tokens: 2048,
|
||||
temperature: 0.8,
|
||||
top_p: 0.1,
|
||||
presence_penalty: 0,
|
||||
frequency_penalty: 0,
|
||||
model: 'Phi-4'
|
||||
};
|
||||
|
||||
axios.post(url, data, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'api-key': apiKey
|
||||
}
|
||||
}).then(response => {
|
||||
response.data.choices.forEach(x => {
|
||||
const responseContent = x.message.content;
|
||||
pushPromptMessage("assistant", responseContent);
|
||||
|
||||
const responseText = DOMPurify.sanitize(responseContent, { ALLOWED_TAGS: [], ALLOWED_ATTR: [] });
|
||||
appendTextToEditor(`/*\n${responseText}\n*/`);
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error.response?.data || error.message);
|
||||
});
|
||||
};
|
||||
|
||||
const savePromptMessages = () => {
|
||||
const text = JSON.stringify(promptMessagesRef.current, null, 4);
|
||||
const blob = new Blob([text], { type: 'text/plain' });
|
||||
const a = document.createElement('a');
|
||||
a.href = URL.createObjectURL(blob);
|
||||
a.download = "prompt.json";
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
};
|
||||
|
||||
const loadPromptMessages = () => {
|
||||
const fileInput = document.createElement('input');
|
||||
fileInput.type = 'file';
|
||||
fileInput.accept = '.json';
|
||||
fileInput.onchange = function (event) {
|
||||
const file = event.target.files[0];
|
||||
|
||||
if (!file)
|
||||
return;
|
||||
|
||||
let reader = new FileReader();
|
||||
reader.onload = function (e) {
|
||||
promptMessagesRef.current = JSON.parse(e.target.result);
|
||||
promptEditorRef.current.set(promptMessagesRef.current);
|
||||
appendTextToEditor("\n//Prompt loaded successfully.");
|
||||
};
|
||||
reader.readAsText(file);
|
||||
};
|
||||
fileInput.click();
|
||||
};
|
||||
|
||||
const queryWhois = () => {
|
||||
const hostname = prompt("Enter a hostname or IP address:", '');
|
||||
if (!hostname || hostname.trim() == '') {
|
||||
appendTextToEditor("\n// A hostname or IP address is required.");
|
||||
return;
|
||||
}
|
||||
|
||||
axios.get(`${serverPrefix}whois/${hostname}`).then(response => {
|
||||
const responseText = DOMPurify.sanitize(response.data, { ALLOWED_TAGS: [], ALLOWED_ATTR: [] });
|
||||
appendTextToEditor(`/*\n${responseText}\n*/`);
|
||||
pushPromptMessage("system", responseText);
|
||||
}).catch(error => {
|
||||
console.error(error);
|
||||
});
|
||||
};
|
||||
|
||||
const queryDns = () => {
|
||||
const hostname = prompt("Enter a hostname or IP address:", '');
|
||||
if (!hostname || hostname.trim() == '') {
|
||||
appendTextToEditor("\n// A hostname or IP address is required.");
|
||||
return;
|
||||
}
|
||||
|
||||
axios.get(`${serverPrefix}dns-query/${hostname}`).then(response => {
|
||||
const responseText = response.data;
|
||||
appendTextToEditor(`/*\n${responseText}\n*/`);
|
||||
pushPromptMessage("system", responseText);
|
||||
}).catch(error => {
|
||||
console.error(error);
|
||||
});
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
window.addEventListener('resize', () => {
|
||||
resizeEditor();
|
||||
});
|
||||
window.dispatchEvent(new Event('resize'));
|
||||
}, []);
|
||||
|
||||
fetchSettings();
|
||||
|
||||
return _e('div', { className: 'app' },
|
||||
_e(RibbonMenu, {
|
||||
onOpenFileClick: openFile,
|
||||
onSaveFileClick: saveFile,
|
||||
onCopliotClick: sendMessageToCopilot,
|
||||
onAzureAiClick: sendMessageToAzureAi,
|
||||
onSavePromptClick: savePromptMessages,
|
||||
onLoadPromptClick: loadPromptMessages,
|
||||
onQueryWhoisClick: queryWhois,
|
||||
onQueryDnsClick: queryDns
|
||||
}),
|
||||
_e('div', { id: 'container' },
|
||||
_e(Editor, { editorRef }),
|
||||
_e(PromptEditor, { promptEditorRef, promptMessagesRef })
|
||||
),
|
||||
_e('div', { className: 'banner' }, _e('a', { href: 'https://github.com/gnh1201/welsonjs' }, 'WelsonJS'), ' Editor powered by Metro UI, Monaco Editor, and JSONEditor.'),
|
||||
);
|
||||
}
|
||||
|
||||
const container = document.getElementById('app');
|
||||
const root = ReactDOM.createRoot(container);
|
||||
root.render(_e(App));
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
BIN
WelsonJS.Toolkit/WelsonJS.Launcher/favicon.ico
Normal file
After Width: | Height: | Size: 56 KiB |
4
WelsonJS.Toolkit/WelsonJS.Launcher/packages.config
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="System.IO.Compression.ZipFile" version="4.3.0" targetFramework="net472" />
|
||||
</packages>
|
30
WelsonJS.Toolkit/WelsonJS.Service/ElasticsearchClient.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
// ElasticsearchClient.cs
|
||||
// https://github.com/gnh1201/welsonjs
|
||||
using RestSharp.Authenticators;
|
||||
using RestSharp;
|
||||
|
||||
namespace WelsonJS.Service
|
||||
{
|
||||
public class ElasticsearchClient
|
||||
{
|
||||
private readonly RestClient client;
|
||||
|
||||
public ElasticsearchClient(string baseUrl, string username, string password)
|
||||
{
|
||||
var options = new RestClientOptions(baseUrl)
|
||||
{
|
||||
Authenticator = new HttpBasicAuthenticator(username, password)
|
||||
};
|
||||
client = new RestClient(options);
|
||||
}
|
||||
|
||||
public RestResponse IndexDocument(string indexName, object document)
|
||||
{
|
||||
var request = new RestRequest($"/{indexName}/_doc", Method.Post);
|
||||
request.AddHeader("Content-Type", "application/json");
|
||||
request.AddJsonBody(document);
|
||||
|
||||
return client.Execute(request);
|
||||
}
|
||||
}
|
||||
}
|
255
WelsonJS.Toolkit/WelsonJS.Service/FileEventMonitor.cs
Normal file
|
@ -0,0 +1,255 @@
|
|||
// FileEventMonitor.cs
|
||||
// Namhyeon Go <abuse@catswords.net>
|
||||
// https://github.com/gnh1201/welsonjs
|
||||
using ClamAV.Net.Client;
|
||||
using ClamAV.Net.Client.Results;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Diagnostics.Eventing.Reader;
|
||||
using System.ServiceProcess;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WelsonJS.Service
|
||||
{
|
||||
public class FileEventMonitor
|
||||
{
|
||||
private EventLogWatcher eventLogWatcher;
|
||||
private ServiceMain parent;
|
||||
private ILogger logger;
|
||||
private enum EventType: int
|
||||
{
|
||||
FileCreate = 11,
|
||||
NetworkConnection = 3,
|
||||
RegistryEvent_1 = 12,
|
||||
RegistryEvent_2 = 13,
|
||||
RegistryEvent_3 = 14
|
||||
};
|
||||
private enum FileCreateEvent: int {
|
||||
RuleName,
|
||||
UtcTime,
|
||||
ProcessGuid,
|
||||
ProcessId,
|
||||
Image,
|
||||
TargetFilename,
|
||||
CreationUtcTime,
|
||||
User
|
||||
};
|
||||
private enum NetworkConnectionEvent: int
|
||||
{
|
||||
RuleName,
|
||||
UtcTime,
|
||||
ProcessGuid,
|
||||
ProcessId,
|
||||
Image,
|
||||
User,
|
||||
Protocol,
|
||||
Initiated,
|
||||
SourceIsIpv6,
|
||||
SourceIp,
|
||||
SourceHostname,
|
||||
SourcePort,
|
||||
SourcePortName,
|
||||
DestinationIsIpv6,
|
||||
DestinationIp,
|
||||
DestinationHostname,
|
||||
DestinationPort,
|
||||
DestinationPortName,
|
||||
};
|
||||
private enum RegistryEvent: int
|
||||
{
|
||||
RuleName,
|
||||
EventType,
|
||||
UtcTime,
|
||||
ProcessGuid,
|
||||
ProcessId,
|
||||
Image,
|
||||
TargetObject,
|
||||
Details,
|
||||
User
|
||||
}
|
||||
private string clamAvConenctionString;
|
||||
private IClamAvClient clamAvClient;
|
||||
|
||||
public FileEventMonitor(ServiceBase _parent, string workingDirectory, ILogger _logger)
|
||||
{
|
||||
parent = (ServiceMain)_parent;
|
||||
logger = _logger;
|
||||
|
||||
try
|
||||
{
|
||||
clamAvConenctionString = parent.ReadSettingsValue("CLAMAV_HOST");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
clamAvConenctionString = "tcp://127.0.0.1:3310";
|
||||
logger.LogInformation($"Failed to read the address because of {ex.Message}. Set default: {clamAvConenctionString}");
|
||||
}
|
||||
ConnectToClamAv().Start();
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
try
|
||||
{
|
||||
string query = @"<QueryList>
|
||||
<Query Id='0' Path='Microsoft-Windows-Sysmon/Operational'>
|
||||
<Select Path='Microsoft-Windows-Sysmon/Operational'>*[System/EventID=11 or System/EventID=3 or System/EventID=12 or System/EventID=13 or System/EventID=14]</Select>
|
||||
</Query>
|
||||
</QueryList>";
|
||||
|
||||
EventLogQuery eventLogQuery = new EventLogQuery("Microsoft-Windows-Sysmon/Operational", PathType.LogName, query);
|
||||
eventLogWatcher = new EventLogWatcher(eventLogQuery);
|
||||
|
||||
eventLogWatcher.EventRecordWritten += new EventHandler<EventRecordWrittenEventArgs>(OnEventRecordWritten);
|
||||
eventLogWatcher.Enabled = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogInformation($"Could not reach to the Sysmon service: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (eventLogWatcher != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
eventLogWatcher.Dispose();
|
||||
eventLogWatcher = null;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
eventLogWatcher = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEventRecordWritten(object sender, EventRecordWrittenEventArgs e)
|
||||
{
|
||||
if (e.EventRecord != null)
|
||||
{
|
||||
int eventId = e.EventRecord.Id;
|
||||
|
||||
try
|
||||
{
|
||||
switch (eventId)
|
||||
{
|
||||
case (int)EventType.FileCreate:
|
||||
{
|
||||
string ruleName = e.EventRecord.Properties[(int)FileCreateEvent.RuleName]?.Value?.ToString();
|
||||
string processId = e.EventRecord.Properties[(int)FileCreateEvent.ProcessId]?.Value?.ToString();
|
||||
string image = e.EventRecord.Properties[(int)FileCreateEvent.Image]?.Value?.ToString();
|
||||
string fileName = e.EventRecord.Properties[(int)FileCreateEvent.TargetFilename]?.Value?.ToString();
|
||||
|
||||
logger.LogInformation($"> Detected the file creation: {fileName}");
|
||||
logger.LogInformation(parent.DispatchServiceEvent("fileCreated", new string[] {
|
||||
ruleName,
|
||||
processId,
|
||||
image,
|
||||
fileName
|
||||
}));
|
||||
|
||||
if (clamAvClient != null)
|
||||
{
|
||||
logger.LogInformation($"> Starting the ClamAV scan: {fileName}");
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await ScanWithClamAv(fileName);
|
||||
});
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case (int)EventType.NetworkConnection:
|
||||
{
|
||||
string ruleName = e.EventRecord.Properties[(int)NetworkConnectionEvent.RuleName]?.Value?.ToString();
|
||||
string processId = e.EventRecord.Properties[(int)NetworkConnectionEvent.ProcessId]?.Value?.ToString();
|
||||
string image = e.EventRecord.Properties[(int)NetworkConnectionEvent.Image]?.Value?.ToString();
|
||||
string protocol = e.EventRecord.Properties[(int)NetworkConnectionEvent.Protocol]?.Value?.ToString();
|
||||
string destinationIp = e.EventRecord.Properties[(int)NetworkConnectionEvent.DestinationIp]?.Value?.ToString();
|
||||
string desinationPort = e.EventRecord.Properties[(int)NetworkConnectionEvent.DestinationPort]?.Value?.ToString();
|
||||
string dstinationAddress = $"{protocol}://{destinationIp}:{desinationPort}";
|
||||
|
||||
logger.LogInformation($"> Detected the network connection: {dstinationAddress}");
|
||||
logger.LogInformation(parent.DispatchServiceEvent("networkConnected", new string[] {
|
||||
ruleName,
|
||||
processId,
|
||||
image,
|
||||
dstinationAddress
|
||||
}));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case (int)EventType.RegistryEvent_1:
|
||||
case (int)EventType.RegistryEvent_2:
|
||||
case (int)EventType.RegistryEvent_3:
|
||||
{
|
||||
string ruleName = e.EventRecord.Properties[(int)RegistryEvent.RuleName]?.Value?.ToString();
|
||||
string processId = e.EventRecord.Properties[(int)RegistryEvent.ProcessId]?.Value?.ToString();
|
||||
string image = e.EventRecord.Properties[(int)RegistryEvent.Image]?.Value?.ToString();
|
||||
string eventType = e.EventRecord.Properties[(int)RegistryEvent.EventType]?.Value?.ToString();
|
||||
string targetObject = e.EventRecord.Properties[(int)RegistryEvent.TargetObject]?.Value?.ToString();
|
||||
|
||||
logger.LogInformation($"> Detected the registry modification: {targetObject}");
|
||||
logger.LogInformation(parent.DispatchServiceEvent("registryModified", new string[] {
|
||||
ruleName,
|
||||
processId,
|
||||
image,
|
||||
eventType,
|
||||
targetObject
|
||||
}));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new ArgumentException("Not supported event type");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogInformation($"Failed to process the event bacause of {ex.Message}.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogInformation("The event instance was null.");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ConnectToClamAv()
|
||||
{
|
||||
try {
|
||||
// Create a client
|
||||
clamAvClient = ClamAvClient.Create(new Uri(clamAvConenctionString));
|
||||
|
||||
// Send PING command to ClamAV
|
||||
await clamAvClient.PingAsync().ConfigureAwait(false);
|
||||
|
||||
// Get ClamAV engine and virus database version
|
||||
VersionResult result = await clamAvClient.GetVersionAsync().ConfigureAwait(false);
|
||||
|
||||
logger.LogInformation($"ClamAV version {result.ProgramVersion}, Virus database version {result.VirusDbVersion}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogInformation($"Failed to read the address because of {ex.Message}. {clamAvConenctionString}");
|
||||
clamAvClient = null;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ScanWithClamAv(string remotePath)
|
||||
{
|
||||
ScanResult res = await clamAvClient.ScanRemotePathAsync(remotePath).ConfigureAwait(false);
|
||||
|
||||
logger.LogInformation($"> Scan result: Infected={res.Infected}, VirusName={res.VirusName}");
|
||||
logger.LogInformation(parent.DispatchServiceEvent("avScanResult", new string[] {
|
||||
res.Infected.ToString(),
|
||||
res.VirusName
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
160
WelsonJS.Toolkit/WelsonJS.Service/HeartbeatClient.cs
Normal file
|
@ -0,0 +1,160 @@
|
|||
// HeartbeatClient.cs
|
||||
// Namhyeon Go <abuse@catswords.net>
|
||||
// https://github.com/gnh1201/welsonjs
|
||||
using Grpc.Core;
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
using System.Management;
|
||||
using System.ServiceProcess;
|
||||
using Grpc.Net.Client;
|
||||
using Grpc.Net.Client.Web;
|
||||
using System.Net.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace WelsonJS.Service
|
||||
{
|
||||
public class HeartbeatClient
|
||||
{
|
||||
private readonly HeartbeatService.HeartbeatServiceClient _client;
|
||||
private ILogger logger;
|
||||
private readonly GrpcChannel _channel;
|
||||
private int HeartbeatInterval;
|
||||
private ServiceMain parent;
|
||||
private string clientId;
|
||||
private string serverAddress;
|
||||
|
||||
public HeartbeatClient(ServiceBase _parent, ILogger _logger)
|
||||
{
|
||||
parent = (ServiceMain)_parent;
|
||||
logger = _logger;
|
||||
|
||||
HeartbeatInterval = int.Parse(parent.ReadSettingsValue("HEARTBEAT_INTERVAL") ?? "2000");
|
||||
|
||||
try
|
||||
{
|
||||
serverAddress = parent.ReadSettingsValue("GRPC_HOST");
|
||||
if (String.IsNullOrEmpty(serverAddress))
|
||||
{
|
||||
throw new Exception("The server address could not be empty.");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
serverAddress = "http://localhost:50051";
|
||||
logger.LogInformation($"Failed to read the address because of {ex.Message}. Set default: {serverAddress}");
|
||||
}
|
||||
|
||||
var httpClientHandler = new HttpClientHandler();
|
||||
var grpcWebHandler = new GrpcWebHandler(GrpcWebMode.GrpcWebText, httpClientHandler);
|
||||
_channel = GrpcChannel.ForAddress(serverAddress, new GrpcChannelOptions
|
||||
{
|
||||
HttpHandler = grpcWebHandler,
|
||||
Credentials = ChannelCredentials.Insecure
|
||||
});
|
||||
_client = new HeartbeatService.HeartbeatServiceClient(_channel);
|
||||
|
||||
clientId = GetSystemUUID().ToLower();
|
||||
logger.LogInformation($"Use the client ID: {clientId}");
|
||||
}
|
||||
|
||||
public async Task StartHeartbeatAsync()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var call = _client.CheckHeartbeat();
|
||||
try
|
||||
{
|
||||
|
||||
var request = new HeartbeatRequest
|
||||
{
|
||||
IsAlive = true
|
||||
};
|
||||
|
||||
await call.RequestStream.WriteAsync(request);
|
||||
await call.RequestStream.CompleteAsync();
|
||||
logger.LogInformation("Sent heartbeat");
|
||||
|
||||
await Task.Delay(HeartbeatInterval); // Wait for HeartbeatInterval
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogInformation("Heartbeat request stream failed: " + ex.Message);
|
||||
}
|
||||
|
||||
// 서버 응답을 수신하는 작업
|
||||
try
|
||||
{
|
||||
while (await call.ResponseStream.MoveNext())
|
||||
{
|
||||
var response = call.ResponseStream.Current;
|
||||
logger.LogInformation("Heartbeat response received: " + response.IsAlive);
|
||||
}
|
||||
}
|
||||
catch (RpcException ex)
|
||||
{
|
||||
logger.LogInformation($"gRPC error: {ex.Status.Detail}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogInformation($"Unexpected error: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 잠시 대기 후 재연결
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(HeartbeatInterval));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task StartEventListenerAsync()
|
||||
{
|
||||
var eventRequest = new FetchEventsRequest
|
||||
{
|
||||
ClientId = clientId
|
||||
};
|
||||
while (true)
|
||||
{
|
||||
var eventCall = _client.FetchPendingEvents(eventRequest);
|
||||
try
|
||||
{
|
||||
while (await eventCall.ResponseStream.MoveNext())
|
||||
{
|
||||
var response = eventCall.ResponseStream.Current;
|
||||
logger.LogInformation($"Received event from server: {response.EventType} with args: {string.Join(", ", response.Args)}");
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(HeartbeatInterval));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public async Task ShutdownAsync()
|
||||
{
|
||||
await _channel.ShutdownAsync();
|
||||
}
|
||||
|
||||
private string GetSystemUUID()
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var searcher = new ManagementObjectSearcher("SELECT UUID FROM Win32_ComputerSystemProduct"))
|
||||
{
|
||||
foreach (var mo in searcher.Get())
|
||||
{
|
||||
return mo["UUID"].ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogInformation($"An error occurred while retrieving the system UUID: {ex.Message}");
|
||||
}
|
||||
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
}
|
62
WelsonJS.Toolkit/WelsonJS.Service/Logging/FileLogger.cs
Normal file
|
@ -0,0 +1,62 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
using System.IO;
|
||||
using System;
|
||||
|
||||
namespace WelsonJS.Service.Logging
|
||||
{
|
||||
public class FileLogger : ILogger
|
||||
{
|
||||
private string loggingDirectory;
|
||||
private string categoryName;
|
||||
private static object _lock = new object();
|
||||
|
||||
public FileLogger(string _loggingDirectory, string _categoryName = "welsonjs")
|
||||
{
|
||||
loggingDirectory = _loggingDirectory;
|
||||
categoryName = _categoryName;
|
||||
}
|
||||
|
||||
public IDisposable BeginScope<TState>(TState state)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool IsEnabled(LogLevel logLevel)
|
||||
{
|
||||
//return logLevel == LogLevel.Trace;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
|
||||
{
|
||||
if (formatter != null)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!Directory.Exists(loggingDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(loggingDirectory);
|
||||
}
|
||||
|
||||
string path = Path.Combine(loggingDirectory, $"{categoryName}_service.{DateTime.Now.ToString("yyyy-MM-dd")}.log");
|
||||
string nl = Environment.NewLine;
|
||||
string err = "";
|
||||
|
||||
if (exception != null)
|
||||
{
|
||||
err = nl + exception.GetType() + ": " + exception.Message + nl + exception.StackTrace + nl;
|
||||
}
|
||||
|
||||
File.AppendAllText(path, logLevel.ToString() + ": [" + DateTime.Now.ToString() + "] " + formatter(state, exception) + nl + err);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(LogLevel.Warning.ToString() + ": [" + DateTime.Now.ToString() + "] Failed to write a log file. " + ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace WelsonJS.Service.Logging
|
||||
{
|
||||
public static class FileLoggerExtensions
|
||||
{
|
||||
public static ILoggerFactory AddDirectory(this ILoggerFactory factory, string loggingDirectory)
|
||||
{
|
||||
factory.AddProvider(new FileLoggerProvider(loggingDirectory));
|
||||
return factory;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace WelsonJS.Service.Logging
|
||||
{
|
||||
public class FileLoggerProvider : ILoggerProvider
|
||||
{
|
||||
private string loggingDirectory;
|
||||
|
||||
public FileLoggerProvider(string _loggingDirectory)
|
||||
{
|
||||
loggingDirectory = _loggingDirectory;
|
||||
}
|
||||
|
||||
public ILogger CreateLogger(string categoryName)
|
||||
{
|
||||
return new FileLogger(loggingDirectory, categoryName);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Dispose
|
||||
}
|
||||
}
|
||||
}
|
13
WelsonJS.Toolkit/WelsonJS.Service/Model/FileMatchResult.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
|
||||
namespace WelsonJS.Service.Model
|
||||
{
|
||||
public class FileMatchResult
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string FilePath { get; set; }
|
||||
public ulong Offset { get; set; }
|
||||
public string RuleName { get; set; }
|
||||
public DateTime LastChecked { get; set; }
|
||||
}
|
||||
}
|
24
WelsonJS.Toolkit/WelsonJS.Service/Model/ScreenMatchResult.cs
Normal file
|
@ -0,0 +1,24 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace WelsonJS.Service
|
||||
{
|
||||
public class ScreenMatchResult
|
||||
{
|
||||
public string FileName { get; set; }
|
||||
public int ScreenNumber { get; set; }
|
||||
public IntPtr WindowHandle { get; set; }
|
||||
public string WindowTitle { get; set; }
|
||||
public string ProcessName { get; set; }
|
||||
public Point WindowPosition { get; set; }
|
||||
public Point Position { get; set; }
|
||||
public string Text { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Template: {FileName}, Screen Number: {ScreenNumber}, Window Title: {WindowTitle}, " +
|
||||
$"Process Name: {ProcessName}, Window Position: (x: {WindowPosition.X}, y: {WindowPosition.Y}), " +
|
||||
$"Location: (x: {Position.X}, y: {Position.Y})";
|
||||
}
|
||||
}
|
||||
}
|
63
WelsonJS.Toolkit/WelsonJS.Service/Program.cs
Normal file
|
@ -0,0 +1,63 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.ServiceProcess;
|
||||
using WelsonJS.Service.Logging;
|
||||
|
||||
namespace WelsonJS.Service
|
||||
{
|
||||
internal static class Program
|
||||
{
|
||||
private static ILogger logger;
|
||||
|
||||
/// <summary>
|
||||
/// 해당 애플리케이션의 주 진입점입니다.
|
||||
/// </summary>
|
||||
///
|
||||
static void Main(string[] args)
|
||||
{
|
||||
// create the logger
|
||||
ILoggerFactory factory = LoggerFactory.Create(builder => builder.AddConsole());
|
||||
factory.AddDirectory(Path.GetTempPath());
|
||||
logger = factory.CreateLogger("welsonjs");
|
||||
|
||||
// create the service
|
||||
if (Environment.UserInteractive)
|
||||
{
|
||||
Console.WriteLine("WelsonJS Service Application (User Interactive Mode)");
|
||||
Console.WriteLine("https://github.com/gnh1201/welsonjs");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Service is running...");
|
||||
|
||||
ServiceMain svc = new ServiceMain(args, logger);
|
||||
svc.TestStartupAndStop();
|
||||
}
|
||||
else
|
||||
{
|
||||
ServiceBase[] ServicesToRun;
|
||||
ServicesToRun = new ServiceBase[]
|
||||
{
|
||||
new ServiceMain(args, logger)
|
||||
};
|
||||
ServiceBase.Run(ServicesToRun);
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetAppDataPath()
|
||||
{
|
||||
string path = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
"WelsonJS"
|
||||
);
|
||||
|
||||
Directory.CreateDirectory(path);
|
||||
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
throw new IOException("Failed to create directory: " + path);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
61
WelsonJS.Toolkit/WelsonJS.Service/ProjectInstaller.Designer.cs
generated
Normal file
|
@ -0,0 +1,61 @@
|
|||
namespace WelsonJS.Service
|
||||
{
|
||||
partial class ProjectInstaller
|
||||
{
|
||||
/// <summary>
|
||||
/// 필수 디자이너 변수입니다.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// 사용 중인 모든 리소스를 정리합니다.
|
||||
/// </summary>
|
||||
/// <param name="disposing">관리되는 리소스를 삭제해야 하면 true이고, 그렇지 않으면 false입니다.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region 구성 요소 디자이너에서 생성한 코드
|
||||
|
||||
/// <summary>
|
||||
/// 디자이너 지원에 필요한 메서드입니다.
|
||||
/// 이 메서드의 내용을 코드 편집기로 수정하지 마세요.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.serviceProcessInstaller1 = new System.ServiceProcess.ServiceProcessInstaller();
|
||||
this.serviceInstaller1 = new System.ServiceProcess.ServiceInstaller();
|
||||
//
|
||||
// serviceProcessInstaller1
|
||||
//
|
||||
this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
|
||||
this.serviceProcessInstaller1.Password = null;
|
||||
this.serviceProcessInstaller1.Username = null;
|
||||
//
|
||||
// serviceInstaller1
|
||||
//
|
||||
this.serviceInstaller1.Description = "WelsonJS is the framework to build a Windows app on the Windows built-in JavaScri" +
|
||||
"pt engine.";
|
||||
this.serviceInstaller1.DisplayName = "WelsonJS.Service";
|
||||
this.serviceInstaller1.ServiceName = "WelsonJS.Service";
|
||||
this.serviceInstaller1.StartType = System.ServiceProcess.ServiceStartMode.Automatic;
|
||||
//
|
||||
// ProjectInstaller
|
||||
//
|
||||
this.Installers.AddRange(new System.Configuration.Install.Installer[] {
|
||||
this.serviceProcessInstaller1,
|
||||
this.serviceInstaller1});
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.ServiceProcess.ServiceProcessInstaller serviceProcessInstaller1;
|
||||
private System.ServiceProcess.ServiceInstaller serviceInstaller1;
|
||||
}
|
||||
}
|
17
WelsonJS.Toolkit/WelsonJS.Service/ProjectInstaller.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Configuration.Install;
|
||||
|
||||
namespace WelsonJS.Service
|
||||
{
|
||||
[RunInstaller(true)]
|
||||
public partial class ProjectInstaller : System.Configuration.Install.Installer
|
||||
{
|
||||
public ProjectInstaller()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
129
WelsonJS.Toolkit/WelsonJS.Service/ProjectInstaller.resx
Normal file
|
@ -0,0 +1,129 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<metadata name="serviceProcessInstaller1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
<metadata name="serviceInstaller1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>195, 17</value>
|
||||
</metadata>
|
||||
<metadata name="$this.TrayLargeIcon" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>False</value>
|
||||
</metadata>
|
||||
</root>
|
36
WelsonJS.Toolkit/WelsonJS.Service/Properties/AssemblyInfo.cs
Normal file
|
@ -0,0 +1,36 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// 어셈블리에 대한 일반 정보는 다음 특성 집합을 통해
|
||||
// 제어됩니다. 어셈블리와 관련된 정보를 수정하려면
|
||||
// 이러한 특성 값을 변경하세요.
|
||||
[assembly: AssemblyTitle("WelsonJS.Service")]
|
||||
[assembly: AssemblyDescription("Windows Service for WelsonJS framework based applications")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Catswords")]
|
||||
[assembly: AssemblyProduct("WelsonJS")]
|
||||
[assembly: AssemblyCopyright("Catswords OSS, GPLv3 or Ms-RL")]
|
||||
[assembly: AssemblyTrademark("WelsonJS")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// ComVisible을 false로 설정하면 이 어셈블리의 형식이 COM 구성 요소에
|
||||
// 표시되지 않습니다. COM에서 이 어셈블리의 형식에 액세스하려면
|
||||
// 해당 형식에 대해 ComVisible 특성을 true로 설정하세요.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// 이 프로젝트가 COM에 노출되는 경우 다음 GUID는 typelib의 ID를 나타냅니다.
|
||||
[assembly: Guid("09f295ee-5edb-4327-abbe-ddccdf5edd9e")]
|
||||
|
||||
// 어셈블리의 버전 정보는 다음 네 가지 값으로 구성됩니다.
|
||||
//
|
||||
// 주 버전
|
||||
// 부 버전
|
||||
// 빌드 번호
|
||||
// 수정 버전
|
||||
//
|
||||
// 모든 값을 지정하거나 아래와 같이 '*'를 사용하여 빌드 번호 및 수정 번호를
|
||||
// 기본값으로 할 수 있습니다.
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("0.2.7.54")]
|
||||
[assembly: AssemblyFileVersion("0.2.7.54")]
|
27
WelsonJS.Toolkit/WelsonJS.Service/Protos/heartbeat.proto
Normal file
|
@ -0,0 +1,27 @@
|
|||
syntax = "proto3";
|
||||
|
||||
option csharp_namespace = "WelsonJS.Service";
|
||||
|
||||
package heartbeat;
|
||||
|
||||
service HeartbeatService {
|
||||
rpc CheckHeartbeat (stream HeartbeatRequest) returns (stream HeartbeatResponse);
|
||||
rpc FetchPendingEvents (FetchEventsRequest) returns (stream FetchEventsResponse);
|
||||
}
|
||||
|
||||
message HeartbeatRequest {
|
||||
bool is_alive = 1;
|
||||
}
|
||||
|
||||
message HeartbeatResponse {
|
||||
bool is_alive = 1;
|
||||
}
|
||||
|
||||
message FetchEventsRequest {
|
||||
string client_id = 1;
|
||||
}
|
||||
|
||||
message FetchEventsResponse {
|
||||
string event_type = 1;
|
||||
repeated string args = 2;
|
||||
}
|
927
WelsonJS.Toolkit/WelsonJS.Service/ScreenMatch.cs
Normal file
|
@ -0,0 +1,927 @@
|
|||
// ScreenMatching.cs
|
||||
// https://github.com/gnh1201/welsonjs
|
||||
// https://catswords-oss.rdbl.io/5719744820/8803957194
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.IO.Hashing;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.ServiceProcess;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
using System.Linq;
|
||||
using Tesseract;
|
||||
using WelsonJS.Service;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
public class ScreenMatch
|
||||
{
|
||||
// User32.dll API 함수 선언
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool IsWindowVisible(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern int GetWindowRect(IntPtr hWnd, out RECT lpRect);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern IntPtr GetDC(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern int GetWindowTextLength(IntPtr hWnd);
|
||||
|
||||
[DllImport("gdi32.dll")]
|
||||
private static extern bool BitBlt(IntPtr hDestDC, int x, int y, int nWidth, int nHeight, IntPtr hSrcDC, int xSrc, int ySrc, int dwRop);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool EnumDisplaySettings(string lpszDeviceName, int iModeNum, ref DEVMODE lpDevMode);
|
||||
|
||||
// https://stackoverflow.com/questions/60872044/how-to-get-scaling-factor-for-each-monitor-e-g-1-1-25-1-5
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct DEVMODE
|
||||
{
|
||||
private const int CCHDEVICENAME = 0x20;
|
||||
private const int CCHFORMNAME = 0x20;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
|
||||
public string dmDeviceName;
|
||||
public short dmSpecVersion;
|
||||
public short dmDriverVersion;
|
||||
public short dmSize;
|
||||
public short dmDriverExtra;
|
||||
public int dmFields;
|
||||
public int dmPositionX;
|
||||
public int dmPositionY;
|
||||
public ScreenOrientation dmDisplayOrientation;
|
||||
public int dmDisplayFixedOutput;
|
||||
public short dmColor;
|
||||
public short dmDuplex;
|
||||
public short dmYResolution;
|
||||
public short dmTTOption;
|
||||
public short dmCollate;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
|
||||
public string dmFormName;
|
||||
public short dmLogPixels;
|
||||
public int dmBitsPerPel;
|
||||
public int dmPelsWidth;
|
||||
public int dmPelsHeight;
|
||||
public int dmDisplayFlags;
|
||||
public int dmDisplayFrequency;
|
||||
public int dmICMMethod;
|
||||
public int dmICMIntent;
|
||||
public int dmMediaType;
|
||||
public int dmDitherType;
|
||||
public int dmReserved1;
|
||||
public int dmReserved2;
|
||||
public int dmPanningWidth;
|
||||
public int dmPanningHeight;
|
||||
}
|
||||
|
||||
private const int SRCCOPY = 0x00CC0020;
|
||||
|
||||
// 델리게이트 선언
|
||||
public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
|
||||
|
||||
// RECT 구조체 선언
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct RECT
|
||||
{
|
||||
public int Left;
|
||||
public int Top;
|
||||
public int Right;
|
||||
public int Bottom;
|
||||
}
|
||||
|
||||
private ServiceMain parent;
|
||||
private ILogger logger;
|
||||
private List<Bitmap> templateImages;
|
||||
private string templateDirectoryPath;
|
||||
private string outputDirectoryPath;
|
||||
private int templateCurrentIndex = 0;
|
||||
private double threshold = 0.3;
|
||||
private string mode;
|
||||
private bool busy;
|
||||
private List<string> _params = new List<string>();
|
||||
private bool isSearchFromEnd = false;
|
||||
private bool isSaveToFile = false;
|
||||
private Size sampleSize;
|
||||
private int sampleAdjustX;
|
||||
private int sampleAdjustY;
|
||||
private List<string> sampleAny;
|
||||
private List<string> sampleClipboard;
|
||||
private List<string> sampleOcr;
|
||||
private List<string> sampleNodup;
|
||||
private Size sampleNodupSize;
|
||||
private Queue<Bitmap> outdatedSamples;
|
||||
private string tesseractDataPath;
|
||||
private string tesseractLanguage;
|
||||
|
||||
private void SetBusy(bool busy)
|
||||
{
|
||||
this.busy = busy;
|
||||
logger.LogInformation($"State changed: busy={busy}");
|
||||
}
|
||||
|
||||
private decimal GetScreenScalingFactor(Screen screen)
|
||||
{
|
||||
DEVMODE dm = new DEVMODE();
|
||||
dm.dmSize = (short)Marshal.SizeOf(typeof(DEVMODE));
|
||||
EnumDisplaySettings(screen.DeviceName, -1, ref dm);
|
||||
|
||||
decimal scalingFactor = Math.Round(Decimal.Divide(dm.dmPelsWidth, screen.Bounds.Width), 2);
|
||||
if (scalingFactor > 1)
|
||||
{
|
||||
logger.LogInformation($"Screen with scaling detected: {scalingFactor}x");
|
||||
logger.LogWarning("Please check the screen DPI.");
|
||||
}
|
||||
|
||||
return scalingFactor;
|
||||
}
|
||||
|
||||
public class TemplateInfo
|
||||
{
|
||||
public string FileName { get; set; }
|
||||
public int Index { get; set; }
|
||||
|
||||
public TemplateInfo(string fileName, int index)
|
||||
{
|
||||
FileName = fileName;
|
||||
Index = index;
|
||||
}
|
||||
}
|
||||
|
||||
public class SampleInfo
|
||||
{
|
||||
public string FileName { get; set; }
|
||||
public uint Crc32 { get; set; }
|
||||
|
||||
public SampleInfo(string fileName, uint crc32)
|
||||
{
|
||||
FileName = fileName;
|
||||
Crc32 = crc32;
|
||||
}
|
||||
}
|
||||
|
||||
public ScreenMatch(ServiceBase _parent, string workingDirectory, ILogger _logger)
|
||||
{
|
||||
parent = (ServiceMain)_parent;
|
||||
logger = _logger;
|
||||
|
||||
SetBusy(false);
|
||||
|
||||
templateDirectoryPath = Path.Combine(workingDirectory, "app/assets/img/_templates");
|
||||
outputDirectoryPath = Path.Combine(workingDirectory, "app/assets/img/_captured");
|
||||
templateImages = new List<Bitmap>();
|
||||
|
||||
// Initialize variables for sampling process
|
||||
sampleSize = new Size
|
||||
{
|
||||
Width = 128,
|
||||
Height = 128
|
||||
};
|
||||
sampleAdjustX = 0;
|
||||
sampleAdjustY = 0;
|
||||
sampleAny = new List<string>();
|
||||
sampleClipboard = new List<string>();
|
||||
sampleOcr = new List<string>();
|
||||
sampleNodup = new List<string>();
|
||||
sampleNodupSize = new Size
|
||||
{
|
||||
Width = 180,
|
||||
Height = 60
|
||||
};
|
||||
outdatedSamples = new Queue<Bitmap>();
|
||||
|
||||
// Read values from configration file
|
||||
string screen_time_mode;
|
||||
string screen_time_params;
|
||||
try
|
||||
{
|
||||
screen_time_mode = parent.ReadSettingsValue("SCREEN_TIME_MODE");
|
||||
screen_time_params = parent.ReadSettingsValue("SCREEN_TIME_PARAMS");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
screen_time_mode = null;
|
||||
screen_time_params = null;
|
||||
logger.LogInformation($"Failed to read from configration file: {ex.Message}");
|
||||
}
|
||||
|
||||
if (!String.IsNullOrEmpty(screen_time_params))
|
||||
{
|
||||
var screen_time_configs = screen_time_params
|
||||
.Split(',')
|
||||
.Select(pair => pair.Split('='))
|
||||
.ToDictionary(
|
||||
parts => parts[0],
|
||||
parts => parts.Length > 1 ? parts[1] : parts[0]
|
||||
);
|
||||
|
||||
var config_keys = new string[]
|
||||
{
|
||||
"process_name",
|
||||
"sample_width",
|
||||
"sample_height",
|
||||
"sample_adjust_x",
|
||||
"sample_adjust_y",
|
||||
"sample_any",
|
||||
"sample_nodup",
|
||||
"backward",
|
||||
"save",
|
||||
"sample_clipboard",
|
||||
"sample_ocr"
|
||||
};
|
||||
|
||||
foreach (var config_key in config_keys)
|
||||
{
|
||||
string config_value;
|
||||
screen_time_configs.TryGetValue(config_key, out config_value);
|
||||
|
||||
if (config_value != null)
|
||||
{
|
||||
switch (config_key)
|
||||
{
|
||||
case "backward":
|
||||
{
|
||||
isSearchFromEnd = true;
|
||||
logger.LogInformation("Use the backward search when screen time");
|
||||
break;
|
||||
}
|
||||
|
||||
case "save":
|
||||
{
|
||||
isSaveToFile = true;
|
||||
logger.LogInformation("Will be save an image file when capture the screens");
|
||||
break;
|
||||
}
|
||||
|
||||
case "threshold":
|
||||
{
|
||||
double.TryParse(config_value, out double t);
|
||||
threshold = t;
|
||||
break;
|
||||
}
|
||||
|
||||
case "sample_clipboard":
|
||||
{
|
||||
sampleClipboard = new List<string>(config_value.Split(':'));
|
||||
break;
|
||||
}
|
||||
|
||||
case "sample_ocr":
|
||||
{
|
||||
tesseractDataPath = Path.Combine(workingDirectory, "app/assets/tessdata_best");
|
||||
tesseractLanguage = "eng";
|
||||
sampleOcr = new List<string>(config_value.Split(':'));
|
||||
break;
|
||||
}
|
||||
|
||||
case "sample_width":
|
||||
{
|
||||
int.TryParse(config_value, out int w);
|
||||
sampleSize.Width = w;
|
||||
break;
|
||||
}
|
||||
|
||||
case "sample_height":
|
||||
{
|
||||
int.TryParse(config_value, out int h);
|
||||
sampleSize.Height = h;
|
||||
break;
|
||||
}
|
||||
|
||||
case "sample_nodup_width":
|
||||
{
|
||||
int.TryParse(config_value, out int w);
|
||||
sampleNodupSize.Width = w;
|
||||
break;
|
||||
}
|
||||
|
||||
case "sample_nodup_height":
|
||||
{
|
||||
int.TryParse(config_value, out int h);
|
||||
sampleNodupSize.Height = h;
|
||||
break;
|
||||
}
|
||||
|
||||
case "sample_adjust_x":
|
||||
{
|
||||
int.TryParse(config_value, out sampleAdjustX);
|
||||
break;
|
||||
}
|
||||
|
||||
case "sample_adjust_y":
|
||||
{
|
||||
int.TryParse(config_value, out sampleAdjustY);
|
||||
break;
|
||||
}
|
||||
|
||||
case "sample_any":
|
||||
{
|
||||
sampleAny = new List<string>(config_value.Split(':'));
|
||||
break;
|
||||
}
|
||||
|
||||
case "sample_nodup":
|
||||
{
|
||||
sampleNodup = new List<string>(config_value.Split(':'));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetMode(screen_time_mode);
|
||||
LoadTemplateImages();
|
||||
}
|
||||
|
||||
public void SetMode(string mode)
|
||||
{
|
||||
if (!String.IsNullOrEmpty(mode))
|
||||
{
|
||||
this.mode = mode;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.mode = "screen";
|
||||
}
|
||||
}
|
||||
|
||||
public void SetThreshold(double threshold)
|
||||
{
|
||||
this.threshold = threshold;
|
||||
}
|
||||
|
||||
public void LoadTemplateImages()
|
||||
{
|
||||
string[] files;
|
||||
|
||||
try
|
||||
{
|
||||
files = Directory.GetFiles(templateDirectoryPath, "*.png");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
files = new string[]{};
|
||||
logger.LogInformation($"Failed to read the directory structure: {ex.Message}");
|
||||
}
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
string filename = Path.GetFileName(file);
|
||||
|
||||
string realpath;
|
||||
string altpath = parent.GetUserVariablesHandler().GetValue(filename);
|
||||
if (!String.IsNullOrEmpty(altpath))
|
||||
{
|
||||
realpath = altpath;
|
||||
logger.LogInformation($"Use the alternative image: {realpath}");
|
||||
}
|
||||
else
|
||||
{
|
||||
realpath = file;
|
||||
logger.LogInformation($"Use the default image: {realpath}");
|
||||
}
|
||||
|
||||
Bitmap bitmap = new Bitmap(realpath)
|
||||
{
|
||||
Tag = filename
|
||||
};
|
||||
|
||||
if (!filename.StartsWith("no_"))
|
||||
{
|
||||
if (filename.StartsWith("binary_"))
|
||||
{
|
||||
templateImages.Add(ImageQuantize(bitmap));
|
||||
}
|
||||
else
|
||||
{
|
||||
templateImages.Add(bitmap);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 캡쳐 및 템플릿 매칭 진행
|
||||
public List<ScreenMatchResult> CaptureAndMatch()
|
||||
{
|
||||
List<ScreenMatchResult> results = new List<ScreenMatchResult>();
|
||||
|
||||
if (busy)
|
||||
{
|
||||
throw new Exception("Waiting done a previous job...");
|
||||
}
|
||||
|
||||
if (templateImages.Count > 0)
|
||||
{
|
||||
SetBusy(true);
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case "screen": // 화면 기준
|
||||
results = CaptureAndMatchAllScreens();
|
||||
SetBusy(false);
|
||||
break;
|
||||
|
||||
case "window": // 윈도우 핸들 기준
|
||||
results = CaptureAndMatchAllWindows();
|
||||
SetBusy(false);
|
||||
break;
|
||||
|
||||
default:
|
||||
SetBusy(false);
|
||||
throw new Exception($"Unknown capture mode {mode}");
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
// 화면을 기준으로 찾기
|
||||
public List<ScreenMatchResult> CaptureAndMatchAllScreens()
|
||||
{
|
||||
var results = new List<ScreenMatchResult>();
|
||||
|
||||
for (int i = 0; i < Screen.AllScreens.Length; i++)
|
||||
{
|
||||
Screen screen = Screen.AllScreens[i];
|
||||
Bitmap mainImage = CaptureScreen(screen);
|
||||
|
||||
Bitmap templateImage = templateImages[templateCurrentIndex];
|
||||
string templateName = templateImage.Tag as string;
|
||||
TemplateInfo nextTemplateInfo = parent.GetNextTemplateInfo();
|
||||
|
||||
Size templateSize = new Size
|
||||
{
|
||||
Width = templateImage.Width,
|
||||
Height = templateImage.Height
|
||||
};
|
||||
|
||||
logger.LogInformation($"Trying match the template {templateName} on the screen {i}...");
|
||||
|
||||
if (!String.IsNullOrEmpty(nextTemplateInfo.FileName) && templateName != nextTemplateInfo.FileName)
|
||||
{
|
||||
logger.LogInformation($"Ignored the template {templateName}");
|
||||
break;
|
||||
}
|
||||
|
||||
Bitmap out_mainImage;
|
||||
string out_filename;
|
||||
if (templateName.StartsWith("binary_"))
|
||||
{
|
||||
out_mainImage = ImageQuantize((Bitmap)mainImage.Clone());
|
||||
out_filename = $"{DateTime.Now:yyyy-MM-dd hh mm ss} binary.png";
|
||||
}
|
||||
else
|
||||
{
|
||||
out_mainImage = mainImage;
|
||||
out_filename = $"{DateTime.Now:yyyy-MM-dd hh mm ss}.png";
|
||||
}
|
||||
|
||||
if (isSaveToFile)
|
||||
{
|
||||
string out_filepath = Path.Combine(outputDirectoryPath, out_filename);
|
||||
((Bitmap)out_mainImage.Clone()).Save(out_filepath);
|
||||
logger.LogInformation($"Screenshot saved: {out_filepath}");
|
||||
}
|
||||
|
||||
// List to store the positions of matched templates in the main image
|
||||
List<Point> matchPositions;
|
||||
|
||||
// If the index value is negative, retrieve and use an outdated image from the queue
|
||||
if (nextTemplateInfo.Index < 0)
|
||||
{
|
||||
logger.LogInformation($"Finding a previous screen of {nextTemplateInfo.FileName}...");
|
||||
|
||||
Bitmap outdatedImage = null;
|
||||
try
|
||||
{
|
||||
// Since outdatedSamples is also used to detect duplicate work, we do not delete tasks with Dequeue.
|
||||
foreach (var image in outdatedSamples)
|
||||
{
|
||||
if (image.Tag != null &&
|
||||
((SampleInfo)image.Tag).FileName == nextTemplateInfo.FileName)
|
||||
{
|
||||
outdatedImage = image;
|
||||
logger.LogInformation($"Found the previous screen of {nextTemplateInfo.FileName}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogInformation($"Error finding a previous screen: {ex.Message}");
|
||||
}
|
||||
|
||||
// Find the matching positions of the outdated image in the main image
|
||||
if (outdatedImage != null) {
|
||||
matchPositions = FindTemplate(out_mainImage, outdatedImage);
|
||||
if (matchPositions.Count > 0)
|
||||
{
|
||||
logger.LogInformation("Match found with the outdated image");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogInformation("No match found with the outdated image");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogInformation("No match found an outdated image");
|
||||
matchPositions = new List<Point>();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the index is not negative, use the current image for template matching
|
||||
matchPositions = FindTemplate(out_mainImage, (Bitmap)templateImage.Clone());
|
||||
}
|
||||
|
||||
foreach (Point matchPosition in matchPositions)
|
||||
{
|
||||
try
|
||||
{
|
||||
string text = sampleAny.Contains(templateName) ?
|
||||
InspectSample((Bitmap)mainImage.Clone(), matchPosition, templateSize, templateName, sampleSize) : string.Empty;
|
||||
|
||||
results.Add(new ScreenMatchResult
|
||||
{
|
||||
FileName = templateName,
|
||||
ScreenNumber = i,
|
||||
Position = matchPosition,
|
||||
Text = text
|
||||
});
|
||||
|
||||
break; // Only one
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogInformation($"Ignore the match. {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (results.Count > 0)
|
||||
{
|
||||
logger.LogInformation("Match found");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogInformation($"No match found");
|
||||
}
|
||||
|
||||
templateCurrentIndex = ++templateCurrentIndex % templateImages.Count;
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public Bitmap CropBitmap(Bitmap bitmap, Point matchPosition, Size templateSize, Size sampleSize, int dx = 0, int dy = 0)
|
||||
{
|
||||
// Adjust coordinates to the center
|
||||
int x = matchPosition.X + (templateSize.Width / 2);
|
||||
int y = matchPosition.Y + (templateSize.Height / 2);
|
||||
|
||||
// Set range of crop image
|
||||
int cropX = Math.Max((x - sampleSize.Width / 2) + dx, 0);
|
||||
int cropY = Math.Max((y - sampleSize.Height / 2) + dy, 0);
|
||||
int cropWidth = Math.Min(sampleSize.Width, bitmap.Width - cropX);
|
||||
int cropHeight = Math.Min(sampleSize.Height, bitmap.Height - cropY);
|
||||
Rectangle cropArea = new Rectangle(cropX, cropY, cropWidth, cropHeight);
|
||||
|
||||
// Crop image
|
||||
return bitmap.Clone(cropArea, bitmap.PixelFormat);
|
||||
}
|
||||
|
||||
public string InspectSample(Bitmap bitmap, Point matchPosition, Size templateSize, string templateName, Size sampleSize)
|
||||
{
|
||||
if (bitmap == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(bitmap), "Bitmap cannot be null.");
|
||||
}
|
||||
|
||||
if (matchPosition == null || matchPosition == Point.Empty)
|
||||
{
|
||||
throw new ArgumentException("matchPosition cannot be empty.");
|
||||
}
|
||||
|
||||
// initialize the text
|
||||
string text = "";
|
||||
|
||||
// Crop image
|
||||
Bitmap croppedBitmap = CropBitmap(bitmap, matchPosition, templateSize, sampleSize, sampleAdjustX, sampleAdjustY);
|
||||
|
||||
// Save to the outdated samples
|
||||
if (sampleNodup.Contains(templateName))
|
||||
{
|
||||
Bitmap croppedNodupBitmap = CropBitmap(bitmap, matchPosition, templateSize, sampleNodupSize);
|
||||
uint bitmapCrc32 = ComputeBitmapCrc32(croppedNodupBitmap);
|
||||
croppedNodupBitmap.Tag = new SampleInfo(templateName, bitmapCrc32);
|
||||
|
||||
bool bitmapExists = outdatedSamples.Any(x => ((SampleInfo)x.Tag).Crc32 == bitmapCrc32);
|
||||
if (bitmapExists)
|
||||
{
|
||||
throw new InvalidOperationException($"This may be a duplicate request. {templateName}");
|
||||
}
|
||||
else
|
||||
{
|
||||
outdatedSamples.Enqueue(croppedNodupBitmap);
|
||||
logger.LogInformation($"Added to the image queue. {templateName}");
|
||||
}
|
||||
}
|
||||
|
||||
// if use Clipboard
|
||||
if (sampleClipboard.Contains(templateName))
|
||||
{
|
||||
logger.LogInformation($"Trying to use the clipboard... {templateName}");
|
||||
Thread th = new Thread(new ThreadStart(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
Clipboard.SetImage((Bitmap)croppedBitmap.Clone());
|
||||
logger.LogInformation($"Copied the image to Clipboard");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogInformation($"Failed to copy to the clipboard: {ex.Message}");
|
||||
}
|
||||
}));
|
||||
th.SetApartmentState(ApartmentState.STA);
|
||||
th.Start();
|
||||
}
|
||||
|
||||
// if use OCR
|
||||
if (sampleOcr.Contains(templateName))
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var engine = new TesseractEngine(tesseractDataPath, tesseractLanguage, EngineMode.Default))
|
||||
{
|
||||
using (var page = engine.Process(croppedBitmap))
|
||||
{
|
||||
text = page.GetText();
|
||||
|
||||
logger.LogInformation($"Mean confidence: {page.GetMeanConfidence()}");
|
||||
logger.LogInformation($"Text (GetText): {text}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogInformation($"Failed to OCR: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
public Bitmap CaptureScreen(Screen screen)
|
||||
{
|
||||
Rectangle screenSize = screen.Bounds;
|
||||
decimal scalingFactor = GetScreenScalingFactor(screen);
|
||||
|
||||
int adjustedWidth = (int)(screenSize.Width * scalingFactor);
|
||||
int adjustedHeight = (int)(screenSize.Height * scalingFactor);
|
||||
|
||||
Bitmap bitmap = new Bitmap(adjustedWidth, adjustedHeight);
|
||||
using (Graphics bitmapGraphics = Graphics.FromImage(bitmap))
|
||||
{
|
||||
bitmapGraphics.CopyFromScreen(screenSize.Left, screenSize.Top, 0, 0, new Size(adjustedWidth, adjustedHeight));
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
// 윈도우 핸들을 기준으로 찾기
|
||||
public List<ScreenMatchResult> CaptureAndMatchAllWindows()
|
||||
{
|
||||
var results = new List<ScreenMatchResult>();
|
||||
|
||||
// 모든 윈도우 핸들을 열거
|
||||
EnumWindows((hWnd, lParam) =>
|
||||
{
|
||||
if (IsWindowVisible(hWnd))
|
||||
{
|
||||
try
|
||||
{
|
||||
string windowTitle = GetWindowTitle(hWnd);
|
||||
string processName = GetProcessName(hWnd);
|
||||
GetWindowRect(hWnd, out RECT windowRect);
|
||||
Point windowPosition = new Point(windowRect.Left, windowRect.Top);
|
||||
Bitmap windowImage = CaptureWindow(hWnd);
|
||||
|
||||
if (windowImage != null)
|
||||
{
|
||||
Bitmap image = templateImages[templateCurrentIndex];
|
||||
string templateName = image.Tag as string;
|
||||
Size templateSize = new Size
|
||||
{
|
||||
Width = image.Width,
|
||||
Height = image.Height
|
||||
};
|
||||
|
||||
List<Point> matchPositions = FindTemplate(windowImage, image);
|
||||
matchPositions.ForEach((matchPosition) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
string text = sampleAny.Contains(templateName) ?
|
||||
InspectSample((Bitmap)windowImage.Clone(), matchPosition, templateSize, templateName, sampleSize) : string.Empty;
|
||||
|
||||
results.Add(new ScreenMatchResult
|
||||
{
|
||||
FileName = templateName,
|
||||
WindowHandle = hWnd,
|
||||
WindowTitle = windowTitle,
|
||||
ProcessName = processName,
|
||||
WindowPosition = windowPosition,
|
||||
Position = matchPosition,
|
||||
Text = text
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogInformation($"Ignore the match. {ex.Message}");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.LogInformation($"Error {ex.Message}");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}, IntPtr.Zero);
|
||||
|
||||
templateCurrentIndex = ++templateCurrentIndex % templateImages.Count;
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public string GetWindowTitle(IntPtr hWnd)
|
||||
{
|
||||
int length = GetWindowTextLength(hWnd);
|
||||
StringBuilder sb = new StringBuilder(length + 1);
|
||||
GetWindowText(hWnd, sb, sb.Capacity);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public string GetProcessName(IntPtr hWnd)
|
||||
{
|
||||
uint processId;
|
||||
GetWindowThreadProcessId(hWnd, out processId);
|
||||
Process process = Process.GetProcessById((int)processId);
|
||||
return process.ProcessName;
|
||||
}
|
||||
|
||||
public Bitmap CaptureWindow(IntPtr hWnd)
|
||||
{
|
||||
GetWindowRect(hWnd, out RECT rect);
|
||||
int width = rect.Right - rect.Left;
|
||||
int height = rect.Bottom - rect.Top;
|
||||
|
||||
if (width <= 0 || height <= 0)
|
||||
return null;
|
||||
|
||||
Bitmap bitmap = new Bitmap(width, height);
|
||||
Graphics graphics = Graphics.FromImage(bitmap);
|
||||
IntPtr hDC = graphics.GetHdc();
|
||||
IntPtr windowDC = GetDC(hWnd);
|
||||
|
||||
bool success = BitBlt(hDC, 0, 0, width, height, windowDC, 0, 0, SRCCOPY);
|
||||
ReleaseDC(hWnd, windowDC);
|
||||
graphics.ReleaseHdc(hDC);
|
||||
|
||||
return success ? bitmap : null;
|
||||
}
|
||||
|
||||
public List<Point> FindTemplate(Bitmap mainImage, Bitmap templateImage)
|
||||
{
|
||||
var matches = new List<Point>();
|
||||
|
||||
int mainWidth = mainImage.Width;
|
||||
int mainHeight = mainImage.Height;
|
||||
int templateWidth = templateImage.Width;
|
||||
int templateHeight = templateImage.Height;
|
||||
|
||||
int startX = isSearchFromEnd ? mainWidth - templateWidth : 0;
|
||||
int endX = isSearchFromEnd ? -1 : mainWidth - templateWidth + 1;
|
||||
int stepX = isSearchFromEnd ? -1 : 1;
|
||||
|
||||
int startY = isSearchFromEnd ? mainHeight - templateHeight : 0;
|
||||
int endY = isSearchFromEnd ? -1 : mainHeight - templateHeight + 1;
|
||||
int stepY = isSearchFromEnd ? -1 : 1;
|
||||
|
||||
for (int x = startX; x != endX; x += stepX)
|
||||
{
|
||||
for (int y = startY; y != endY; y += stepY)
|
||||
{
|
||||
if (IsTemplateMatch(mainImage, templateImage, x, y, threshold))
|
||||
{
|
||||
matches.Add(new Point(x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
private bool IsTemplateMatch(Bitmap mainImage, Bitmap templateImage, int offsetX, int offsetY, double threshold)
|
||||
{
|
||||
int templateWidth = templateImage.Width;
|
||||
int templateHeight = templateImage.Height;
|
||||
int totalPixels = templateWidth * templateHeight;
|
||||
int requiredMatches = (int)(totalPixels * threshold);
|
||||
|
||||
// When the square root of the canvas size of the image to be matched is less than 10, a complete match is applied.
|
||||
if (Math.Sqrt(templateWidth * templateHeight) < 10.0)
|
||||
{
|
||||
for (int y = 0; y < templateHeight; y++)
|
||||
{
|
||||
for (int x = 0; x < templateWidth; x++)
|
||||
{
|
||||
if (mainImage.GetPixel(x + offsetX, y + offsetY) != templateImage.GetPixel(x, y))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, randomness is used.
|
||||
int matchedCount = 0;
|
||||
Random rand = new Random();
|
||||
while (matchedCount < requiredMatches)
|
||||
{
|
||||
int x = rand.Next(templateWidth);
|
||||
int y = rand.Next(templateHeight);
|
||||
|
||||
if (mainImage.GetPixel(x + offsetX, y + offsetY) != templateImage.GetPixel(x, y))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
matchedCount++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private Bitmap ImageQuantize(Bitmap image, int levels = 4)
|
||||
{
|
||||
Bitmap quantizedImage = new Bitmap(image.Width, image.Height);
|
||||
if (image.Tag != null)
|
||||
{
|
||||
quantizedImage.Tag = image.Tag;
|
||||
}
|
||||
|
||||
int step = 255 / (levels - 1); // step by step..... ooh baby...(?)
|
||||
|
||||
for (int y = 0; y < image.Height; y++)
|
||||
{
|
||||
for (int x = 0; x < image.Width; x++)
|
||||
{
|
||||
// Convert the pixel to grayscale
|
||||
Color pixelColor = image.GetPixel(x, y);
|
||||
byte grayValue = (byte)((pixelColor.R + pixelColor.G + pixelColor.B) / 3);
|
||||
|
||||
// Convert the grayscale value to the quantize value
|
||||
byte quantizedValue = (byte)((grayValue / step) * step);
|
||||
|
||||
// Renew the colors
|
||||
Color quantizedColor = Color.FromArgb(quantizedValue, quantizedValue, quantizedValue);
|
||||
quantizedImage.SetPixel(x, y, quantizedColor);
|
||||
}
|
||||
}
|
||||
|
||||
return quantizedImage;
|
||||
}
|
||||
|
||||
private uint ComputeBitmapCrc32(Bitmap bitmap)
|
||||
{
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
{
|
||||
bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
|
||||
|
||||
byte[] bitmapBytes = ms.ToArray();
|
||||
Crc32 crc32 = new Crc32();
|
||||
crc32.Append(bitmapBytes);
|
||||
|
||||
return BitConverter.ToUInt32(crc32.GetCurrentHash(), 0);
|
||||
}
|
||||
}
|
||||
}
|
40
WelsonJS.Toolkit/WelsonJS.Service/ServiceMain.Designer.cs
generated
Normal file
|
@ -0,0 +1,40 @@
|
|||
namespace WelsonJS.Service
|
||||
{
|
||||
partial class ServiceMain
|
||||
{
|
||||
/// <summary>
|
||||
/// 필수 디자이너 변수입니다.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// 사용 중인 모든 리소스를 정리합니다.
|
||||
/// </summary>
|
||||
/// <param name="disposing">관리되는 리소스를 삭제해야 하면 true이고, 그렇지 않으면 false입니다.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region 구성 요소 디자이너에서 생성한 코드
|
||||
|
||||
/// <summary>
|
||||
/// 디자이너 지원에 필요한 메서드입니다.
|
||||
/// 이 메서드의 내용을 코드 편집기로 수정하지 마세요.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
//
|
||||
// ServiceMain
|
||||
//
|
||||
this.ServiceName = "ServiceMain";
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
496
WelsonJS.Toolkit/WelsonJS.Service/ServiceMain.cs
Normal file
|
@ -0,0 +1,496 @@
|
|||
/*
|
||||
* WelsonJS.Service
|
||||
*
|
||||
* filename:
|
||||
* ServiceMain.cs
|
||||
*
|
||||
* description:
|
||||
* WelsonJS - Build a Windows app on the Windows built-in JavaScript engine
|
||||
*
|
||||
* website:
|
||||
* - https://github.com/gnh1201/welsonjs
|
||||
* - https://catswords.social/@catswords_oss
|
||||
* - https://teams.live.com/l/community/FEACHncAhq8ldnojAI
|
||||
*
|
||||
* author:
|
||||
* Namhyeon Go <abuse@catswords.net>
|
||||
*
|
||||
* license:
|
||||
* GPLv3 or MS-RL(Microsoft Reciprocal License)
|
||||
*
|
||||
* references:
|
||||
* - https://learn.microsoft.com/en-us/dotnet/framework/windows-services/how-to-debug-windows-service-applications
|
||||
* - https://stackoverflow.com/questions/6490979/how-to-pass-parameters-to-windows-service
|
||||
* - https://stackoverflow.com/questions/42812333/pass-an-argument-to-a-windows-service-at-automatic-startup
|
||||
* - https://learn.microsoft.com/ko-kr/windows/win32/api/winuser/nf-winuser-getsystemmetrics
|
||||
*/
|
||||
using System;
|
||||
using System.ServiceProcess;
|
||||
using System.Timers;
|
||||
using System.Runtime.InteropServices;
|
||||
using MSScriptControl;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using WelsonJS.TinyINIController;
|
||||
using System.Collections;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace WelsonJS.Service
|
||||
{
|
||||
public partial class ServiceMain : ServiceBase
|
||||
{
|
||||
private readonly static string applicationName = "WelsonJS";
|
||||
private static List<Timer> timers;
|
||||
private ILogger logger;
|
||||
private string workingDirectory;
|
||||
private string scriptName;
|
||||
private string scriptFilePath;
|
||||
private string scriptText;
|
||||
private ScriptControl scriptControl;
|
||||
private string[] args;
|
||||
private bool disabledHeartbeat = false;
|
||||
private bool disabledScreenTime = false;
|
||||
private bool disabledFileMonitor = false;
|
||||
private ScreenMatch screenMatcher;
|
||||
private FileEventMonitor fileEventMonitor;
|
||||
private IniFile settingsFileHandler;
|
||||
private UserVariables userVariablesHandler;
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern int GetSystemMetrics(int nIndex);
|
||||
|
||||
private static int SM_REMOTESESSION = 0x1000;
|
||||
|
||||
public ServiceMain(string[] _args, ILogger _logger)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// set arguments and logger
|
||||
args = _args;
|
||||
logger = _logger;
|
||||
|
||||
// mapping arguments to each variables
|
||||
var arguments = ParseArguments(this.args);
|
||||
foreach (KeyValuePair<string, string> entry in arguments)
|
||||
{
|
||||
switch (entry.Key)
|
||||
{
|
||||
case "working-directory":
|
||||
workingDirectory = entry.Value;
|
||||
break;
|
||||
|
||||
case "script-name":
|
||||
scriptName = entry.Value;
|
||||
break;
|
||||
|
||||
case "disable-heartbeat":
|
||||
disabledHeartbeat = true;
|
||||
break;
|
||||
|
||||
case "disable-screen-time":
|
||||
disabledScreenTime = true;
|
||||
break;
|
||||
|
||||
case "disable-file-monitor":
|
||||
disabledFileMonitor = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// load the user variables
|
||||
userVariablesHandler = new UserVariables(this);
|
||||
userVariablesHandler.Load();
|
||||
|
||||
// set timers
|
||||
timers = new List<Timer>();
|
||||
|
||||
// set working directory
|
||||
if (string.IsNullOrEmpty(workingDirectory))
|
||||
{
|
||||
workingDirectory = Path.Combine(Path.GetTempPath(), applicationName);
|
||||
logger.LogInformation("Working directory not provided. Using default value: " + workingDirectory);
|
||||
|
||||
if (!Directory.Exists(workingDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(workingDirectory);
|
||||
logger.LogInformation("Directory created: " + workingDirectory);
|
||||
}
|
||||
}
|
||||
Directory.SetCurrentDirectory(workingDirectory);
|
||||
|
||||
// read settings.ini
|
||||
string settingsFilePath = Path.Combine(workingDirectory, "settings.ini");
|
||||
if (File.Exists(settingsFilePath))
|
||||
{
|
||||
try
|
||||
{
|
||||
settingsFileHandler = new IniFile(settingsFilePath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogWarning(ex.Message);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogInformation($"Configuration file not found: {settingsFilePath}");
|
||||
}
|
||||
|
||||
// read configrations from settings.ini
|
||||
if (settingsFileHandler != null)
|
||||
{
|
||||
string[] configNames = new string[]
|
||||
{
|
||||
"DISABLE_HEARTBEAT",
|
||||
"DISABLE_SCREEN_TIME",
|
||||
"DISABLE_FILE_MONITOR"
|
||||
};
|
||||
foreach (string configName in configNames)
|
||||
{
|
||||
try
|
||||
{
|
||||
if ("true" == ReadSettingsValue(configName))
|
||||
{
|
||||
switch (configName)
|
||||
{
|
||||
case "DISABLE_HEARTBEAT":
|
||||
disabledHeartbeat = true;
|
||||
break;
|
||||
|
||||
case "DISABLE_SCREEN_TIME":
|
||||
disabledScreenTime = true;
|
||||
break;
|
||||
|
||||
case "DISABLE_FILE_MONITOR":
|
||||
disabledFileMonitor = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogInformation($"{configName} is ignored: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set script name
|
||||
if (string.IsNullOrEmpty(scriptName))
|
||||
{
|
||||
scriptName = "defaultService";
|
||||
logger.LogInformation($"Script name not provided. Using default value: {scriptName}");
|
||||
}
|
||||
|
||||
// set path of the script
|
||||
scriptFilePath = Path.Combine(workingDirectory, "app.js");
|
||||
|
||||
// start the heartbeat
|
||||
if (!disabledHeartbeat)
|
||||
{
|
||||
HeartbeatClient heartbeatClient = new HeartbeatClient(this, logger);
|
||||
Task.Run(heartbeatClient.StartHeartbeatAsync);
|
||||
Task.Run(heartbeatClient.StartEventListenerAsync);
|
||||
}
|
||||
|
||||
// set default timer
|
||||
Timer defaultTimer = new Timer
|
||||
{
|
||||
Interval = 60000 // 1 minute
|
||||
};
|
||||
defaultTimer.Elapsed += OnElapsedTime;
|
||||
timers.Add(defaultTimer);
|
||||
|
||||
// check this session is the user interactive mode
|
||||
if (Environment.UserInteractive) {
|
||||
OnUserInteractiveEnvironment();
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogInformation("Disabled the User Interactive Mode. (e.g., OnScreenTime)");
|
||||
}
|
||||
|
||||
logger.LogInformation(applicationName + " Service Loaded");
|
||||
}
|
||||
|
||||
public string ReadSettingsValue(string key, string defaultValue = null)
|
||||
{
|
||||
if (settingsFileHandler != null)
|
||||
{
|
||||
return settingsFileHandler.Read(key, "Service") ?? defaultValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogWarning("Unable to read the value. It seems that settings.ini is not configured correctly.");
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public UserVariables GetUserVariablesHandler()
|
||||
{
|
||||
return userVariablesHandler;
|
||||
}
|
||||
|
||||
internal void TestStartupAndStop()
|
||||
{
|
||||
this.OnStart(this.args);
|
||||
Console.ReadLine();
|
||||
this.OnStop();
|
||||
}
|
||||
|
||||
protected override void OnStart(string[] args)
|
||||
{
|
||||
base.OnStart(args);
|
||||
|
||||
// Check exists the entry script file
|
||||
if (File.Exists(scriptFilePath))
|
||||
{
|
||||
logger.LogInformation($"Script file found: {scriptFilePath}");
|
||||
|
||||
try
|
||||
{
|
||||
// load the script
|
||||
scriptText = File.ReadAllText(scriptFilePath);
|
||||
scriptControl = new ScriptControl
|
||||
{
|
||||
Language = "JScript",
|
||||
AllowUI = false
|
||||
};
|
||||
scriptControl.Reset();
|
||||
scriptControl.AddCode(scriptText);
|
||||
|
||||
// make the start arguments
|
||||
string[] startArguments;
|
||||
string[] _args;
|
||||
if (Environment.UserInteractive)
|
||||
{
|
||||
_args = new string[]
|
||||
{
|
||||
$"--env-file={userVariablesHandler.GetEnvFilePath()}",
|
||||
"--user-interactive"
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
_args = new string[]
|
||||
{
|
||||
$"--env-file={userVariablesHandler.GetEnvFilePath()}"
|
||||
};
|
||||
}
|
||||
startArguments = new string[args.Length + _args.Length];
|
||||
args.CopyTo(startArguments, 0);
|
||||
for (int i = 0; i < _args.Length; i++)
|
||||
{
|
||||
startArguments[args.Length + i] = _args[i];
|
||||
}
|
||||
|
||||
// initialize
|
||||
logger.LogInformation(DispatchServiceEvent("start", startArguments));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogInformation($"Failed to start because of {ex.Message}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogInformation($"Script file not found: {scriptFilePath}");
|
||||
}
|
||||
|
||||
// Trace a Sysmon file events (If Sysinternals Sysmon installed)
|
||||
if (!disabledFileMonitor)
|
||||
{
|
||||
fileEventMonitor = new FileEventMonitor(this, workingDirectory, logger);
|
||||
fileEventMonitor.Start();
|
||||
|
||||
logger.LogInformation("File Event Monitor Started");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogInformation("File Event Monitor is Disabled");
|
||||
}
|
||||
|
||||
// Start all the registered timers
|
||||
timers.ForEach(timer => timer?.Start());
|
||||
|
||||
logger.LogInformation(applicationName + " Service Started");
|
||||
}
|
||||
|
||||
protected override void OnStop()
|
||||
{
|
||||
// stop timers
|
||||
timers.ForEach(timer => timer?.Stop());
|
||||
|
||||
// stop the File Event Monitor
|
||||
fileEventMonitor?.Stop();
|
||||
|
||||
// dispatch stop callback
|
||||
try
|
||||
{
|
||||
logger.LogInformation(DispatchServiceEvent("stop"));
|
||||
scriptControl?.Reset();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogInformation("Exception when stop: " + ex.Message);
|
||||
}
|
||||
scriptControl = null;
|
||||
|
||||
logger.LogInformation(applicationName + " Service Stopped");
|
||||
}
|
||||
|
||||
private void OnUserInteractiveEnvironment()
|
||||
{
|
||||
// check is it a remote desktop session
|
||||
if (GetSystemMetrics(SM_REMOTESESSION) > 0)
|
||||
{
|
||||
disabledScreenTime = true;
|
||||
logger.LogInformation("This application may not work correctly in a remote desktop session");
|
||||
}
|
||||
|
||||
// set screen timer
|
||||
if (!disabledScreenTime)
|
||||
{
|
||||
screenMatcher = new ScreenMatch(this, workingDirectory, logger);
|
||||
|
||||
Timer screenTimer = new Timer
|
||||
{
|
||||
Interval = 1000 // 1 seconds
|
||||
};
|
||||
screenTimer.Elapsed += OnScreenTime;
|
||||
timers.Add(screenTimer);
|
||||
|
||||
logger.LogInformation("Screen Time Event Enabled");
|
||||
}
|
||||
else
|
||||
{
|
||||
disabledScreenTime = true;
|
||||
|
||||
logger.LogInformation("Screen Time Event Disabled");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnElapsedTime(object source, ElapsedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
logger.LogInformation(DispatchServiceEvent("elapsedTime"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogInformation("Exception when elapsed time: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnScreenTime(object source, ElapsedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
List<ScreenMatchResult> matchedResults = screenMatcher.CaptureAndMatch();
|
||||
matchedResults.ForEach(result =>
|
||||
{
|
||||
logger.LogInformation(DispatchServiceEvent("screenTemplateMatched", new string[]
|
||||
{
|
||||
result.FileName,
|
||||
result.ScreenNumber.ToString(),
|
||||
result.Position.X.ToString(),
|
||||
result.Position.Y.ToString()
|
||||
}));
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogInformation($"Waiting a next screen time... {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private string InvokeScriptMethod(string methodName, string scriptName, string eventType, string[] args)
|
||||
{
|
||||
if (scriptControl != null)
|
||||
{
|
||||
object[] parameters = new object[] {
|
||||
scriptName,
|
||||
eventType,
|
||||
new ArrayList(args),
|
||||
args.Length
|
||||
};
|
||||
//scriptControl.AddObject("extern_arguments", new ArrayList(args), true);
|
||||
|
||||
return scriptControl.Run(methodName, parameters)?.ToString() ?? "void";
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogInformation("InvokeScriptMethod Ignored: " + methodName);
|
||||
}
|
||||
|
||||
return "void";
|
||||
}
|
||||
|
||||
private Dictionary<string, string> ParseArguments(string[] args)
|
||||
{
|
||||
var arguments = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (string arg in args)
|
||||
{
|
||||
if (arg.StartsWith("--"))
|
||||
{
|
||||
var index = arg.IndexOf('=');
|
||||
if (index > 2)
|
||||
{
|
||||
var key = arg.Substring(2, index - 2);
|
||||
var value = arg.Substring(index + 1).Trim('"');
|
||||
arguments[key] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
var key = arg.Substring(2, index - 2);
|
||||
arguments[key] = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return arguments;
|
||||
}
|
||||
|
||||
public ScreenMatch.TemplateInfo GetNextTemplateInfo()
|
||||
{
|
||||
string templateName = string.Empty;
|
||||
int index = 0;
|
||||
|
||||
try
|
||||
{
|
||||
templateName = DispatchServiceEvent("screenNextTemplate");
|
||||
|
||||
// Check if the received value contains an index
|
||||
string[] parts = templateName.Split(':');
|
||||
if (parts.Length > 1)
|
||||
{
|
||||
templateName = parts[0];
|
||||
int.TryParse(parts[1], out index);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogInformation($"Use all templates because of {ex.Message}");
|
||||
}
|
||||
|
||||
return new ScreenMatch.TemplateInfo(templateName, index);
|
||||
}
|
||||
|
||||
public string DispatchServiceEvent(string eventType, string[] args = null)
|
||||
{
|
||||
if (args == null)
|
||||
{
|
||||
return InvokeScriptMethod("dispatchServiceEvent", scriptName, eventType, new string[] { });
|
||||
}
|
||||
else
|
||||
{
|
||||
return InvokeScriptMethod("dispatchServiceEvent", scriptName, eventType, args);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
82
WelsonJS.Toolkit/WelsonJS.Service/UserVariables.cs
Normal file
|
@ -0,0 +1,82 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.ServiceProcess;
|
||||
|
||||
namespace WelsonJS.Service
|
||||
{
|
||||
public class UserVariables
|
||||
{
|
||||
private ServiceMain parent;
|
||||
private Dictionary<string, string> userVariables;
|
||||
private string envFilePath;
|
||||
|
||||
public UserVariables(ServiceBase parent)
|
||||
{
|
||||
envFilePath = Path.Combine(Program.GetAppDataPath(), "welsonjs_default.env");
|
||||
this.parent = (ServiceMain)parent;
|
||||
}
|
||||
|
||||
// Load user-defined variables from the temporary folder in .env format
|
||||
public void Load()
|
||||
{
|
||||
if (File.Exists(envFilePath))
|
||||
{
|
||||
try
|
||||
{
|
||||
string fileContent = File.ReadAllText(envFilePath);
|
||||
// Split based on new line characters
|
||||
string[] keyValuePairs = fileContent.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
userVariables = new Dictionary<string, string>();
|
||||
|
||||
foreach (string pair in keyValuePairs)
|
||||
{
|
||||
// Split by the first occurrence of '='
|
||||
int indexOfEquals = pair.IndexOf('=');
|
||||
if (indexOfEquals != -1)
|
||||
{
|
||||
string key = pair.Substring(0, indexOfEquals).Trim();
|
||||
string value = pair.Substring(indexOfEquals + 1).Trim();
|
||||
|
||||
// Remove surrounding quotes if present
|
||||
if (value.StartsWith("\"") && value.EndsWith("\""))
|
||||
{
|
||||
value = value.Substring(1, value.Length - 2); // Remove the first and last character
|
||||
}
|
||||
|
||||
// Unescape double quotes in the value
|
||||
value = value.Replace("\\\"", "\"");
|
||||
|
||||
userVariables[key] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Error parsing line: '{pair}'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error loading variable file: {ex.Message}");
|
||||
userVariables = new Dictionary<string, string>();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
userVariables = new Dictionary<string, string>();
|
||||
}
|
||||
}
|
||||
|
||||
public string GetValue(string name)
|
||||
{
|
||||
userVariables.TryGetValue(name, out string value);
|
||||
return value;
|
||||
}
|
||||
|
||||
public string GetEnvFilePath()
|
||||
{
|
||||
return envFilePath;
|
||||
}
|
||||
}
|
||||
}
|
300
WelsonJS.Toolkit/WelsonJS.Service/WelsonJS.Service.csproj
Normal file
|
@ -0,0 +1,300 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\packages\Grpc.Tools.2.67.0\build\Grpc.Tools.props" Condition="Exists('..\packages\Grpc.Tools.2.67.0\build\Grpc.Tools.props')" />
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{09F295EE-5EDB-4327-ABBE-DDCCDF5EDD9E}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>WelsonJS.Service</RootNamespace>
|
||||
<AssemblyName>WelsonJS.Service</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<Deterministic>true</Deterministic>
|
||||
<TargetFrameworkProfile />
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
<PublishUrl>게시\</PublishUrl>
|
||||
<Install>true</Install>
|
||||
<InstallFrom>Disk</InstallFrom>
|
||||
<UpdateEnabled>false</UpdateEnabled>
|
||||
<UpdateMode>Foreground</UpdateMode>
|
||||
<UpdateInterval>7</UpdateInterval>
|
||||
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
|
||||
<UpdatePeriodically>false</UpdatePeriodically>
|
||||
<UpdateRequired>false</UpdateRequired>
|
||||
<MapFileExtensions>true</MapFileExtensions>
|
||||
<ApplicationRevision>0</ApplicationRevision>
|
||||
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
|
||||
<IsWebBootstrapper>false</IsWebBootstrapper>
|
||||
<UseApplicationTrust>false</UseApplicationTrust>
|
||||
<BootstrapperEnabled>true</BootstrapperEnabled>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<OutputPath>bin\x86\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<StartupObject />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>favicon.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="ClamAV.Net, Version=0.1.166.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\ClamAV.Net.0.1.166\lib\netstandard2.0\ClamAV.Net.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Google.Protobuf, Version=3.28.2.0, Culture=neutral, PublicKeyToken=a7d26565bac4d604, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Google.Protobuf.3.28.2\lib\net45\Google.Protobuf.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Grpc.Core, Version=2.0.0.0, Culture=neutral, PublicKeyToken=d754f35622e28bad, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Grpc.Core.2.46.6\lib\net45\Grpc.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Grpc.Core.Api, Version=2.0.0.0, Culture=neutral, PublicKeyToken=d754f35622e28bad, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Grpc.Core.Api.2.66.0\lib\net462\Grpc.Core.Api.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Grpc.Net.Client, Version=2.0.0.0, Culture=neutral, PublicKeyToken=d754f35622e28bad, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Grpc.Net.Client.2.66.0\lib\net462\Grpc.Net.Client.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Grpc.Net.Client.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=d754f35622e28bad, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Grpc.Net.Client.Web.2.66.0\lib\netstandard2.0\Grpc.Net.Client.Web.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Grpc.Net.Common, Version=2.0.0.0, Culture=neutral, PublicKeyToken=d754f35622e28bad, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Grpc.Net.Common.2.66.0\lib\netstandard2.0\Grpc.Net.Common.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Bcl.AsyncInterfaces, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Bcl.AsyncInterfaces.8.0.0\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="Microsoft.Extensions.Configuration, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.Configuration.8.0.0\lib\net462\Microsoft.Extensions.Configuration.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.Configuration.Abstractions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.Configuration.Abstractions.8.0.0\lib\net462\Microsoft.Extensions.Configuration.Abstractions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.Configuration.Binder, Version=8.0.0.2, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.Configuration.Binder.8.0.2\lib\net462\Microsoft.Extensions.Configuration.Binder.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.DependencyInjection, Version=8.0.0.1, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.DependencyInjection.8.0.1\lib\net462\Microsoft.Extensions.DependencyInjection.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions, Version=8.0.0.2, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.8.0.2\lib\net462\Microsoft.Extensions.DependencyInjection.Abstractions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.Logging, Version=8.0.0.1, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.Logging.8.0.1\lib\net462\Microsoft.Extensions.Logging.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.Logging.Abstractions, Version=8.0.0.2, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.Logging.Abstractions.8.0.2\lib\net462\Microsoft.Extensions.Logging.Abstractions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.Logging.Configuration, Version=8.0.0.1, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.Logging.Configuration.8.0.1\lib\net462\Microsoft.Extensions.Logging.Configuration.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.Logging.Console, Version=8.0.0.1, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.Logging.Console.8.0.1\lib\net462\Microsoft.Extensions.Logging.Console.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.Options, Version=8.0.0.2, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.Options.8.0.2\lib\net462\Microsoft.Extensions.Options.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.Options.ConfigurationExtensions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.Options.ConfigurationExtensions.8.0.0\lib\net462\Microsoft.Extensions.Options.ConfigurationExtensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.Primitives, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.Primitives.8.0.0\lib\net462\Microsoft.Extensions.Primitives.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RestSharp, Version=112.1.0.0, Culture=neutral, PublicKeyToken=598062e77f915f75, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\RestSharp.112.1.0\lib\net48\RestSharp.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.ComponentModel.DataAnnotations" />
|
||||
<Reference Include="System.Configuration.Install" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Diagnostics.DiagnosticSource, Version=8.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Diagnostics.DiagnosticSource.8.0.1\lib\net462\System.Diagnostics.DiagnosticSource.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.IO.Hashing, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.IO.Hashing.8.0.0\lib\net462\System.IO.Hashing.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Management" />
|
||||
<Reference Include="System.Memory, Version=4.0.1.2, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Net.Http.WinHttpHandler, Version=8.0.0.2, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Net.Http.WinHttpHandler.8.0.2\lib\net462\System.Net.Http.WinHttpHandler.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Numerics.Vectors, Version=4.1.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime" />
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.ServiceProcess" />
|
||||
<Reference Include="System.Text.Encodings.Web, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Text.Encodings.Web.8.0.0\lib\net462\System.Text.Encodings.Web.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Text.Json, Version=8.0.0.5, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Text.Json.8.0.5\lib\net462\System.Text.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="Tesseract, Version=5.2.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Tesseract.5.2.0\lib\net48\Tesseract.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="FileEventMonitor.cs" />
|
||||
<Compile Include="HeartbeatClient.cs" />
|
||||
<Compile Include="Logging\FileLogger.cs" />
|
||||
<Compile Include="Logging\FileLoggerExtensions.cs" />
|
||||
<Compile Include="Logging\FileLoggerProvider.cs" />
|
||||
<Compile Include="Model\FileMatchResult.cs" />
|
||||
<Compile Include="ServiceMain.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="ServiceMain.Designer.cs">
|
||||
<DependentUpon>ServiceMain.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="ProjectInstaller.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="ProjectInstaller.Designer.cs">
|
||||
<DependentUpon>ProjectInstaller.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="ScreenMatch.cs" />
|
||||
<Compile Include="Model\ScreenMatchResult.cs" />
|
||||
<Compile Include="UserVariables.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<COMReference Include="MSScriptControl">
|
||||
<Guid>{0E59F1D2-1FBE-11D0-8FF2-00A0D10038BC}</Guid>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<VersionMinor>0</VersionMinor>
|
||||
<Lcid>0</Lcid>
|
||||
<WrapperTool>tlbimp</WrapperTool>
|
||||
<Isolated>False</Isolated>
|
||||
<EmbedInteropTypes>True</EmbedInteropTypes>
|
||||
<Private>True</Private>
|
||||
</COMReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="ProjectInstaller.resx">
|
||||
<DependentUpon>ProjectInstaller.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\WelsonJS.Toolkit\WelsonJS.Toolkit.csproj">
|
||||
<Project>{d6007282-b4f7-4694-ac67-bb838d91b77a}</Project>
|
||||
<Name>WelsonJS.Toolkit</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BootstrapperPackage Include=".NETFramework,Version=v4.8">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>Microsoft .NET Framework 4.8%28x86 및 x64%29</ProductName>
|
||||
<Install>true</Install>
|
||||
</BootstrapperPackage>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>.NET Framework 3.5 SP1</ProductName>
|
||||
<Install>false</Install>
|
||||
</BootstrapperPackage>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="favicon.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Protobuf Include="Protos\heartbeat.proto" GrpcServices="Client" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>이 프로젝트는 이 컴퓨터에 없는 NuGet 패키지를 참조합니다. 해당 패키지를 다운로드하려면 NuGet 패키지 복원을 사용하십시오. 자세한 내용은 http://go.microsoft.com/fwlink/?LinkID=322105를 참조하십시오. 누락된 파일은 {0}입니다.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\Grpc.Core.2.46.6\build\net45\Grpc.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Grpc.Core.2.46.6\build\net45\Grpc.Core.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Tesseract.5.2.0\build\Tesseract.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Tesseract.5.2.0\build\Tesseract.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Grpc.Tools.2.67.0\build\Grpc.Tools.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Grpc.Tools.2.67.0\build\Grpc.Tools.props'))" />
|
||||
<Error Condition="!Exists('..\packages\Grpc.Tools.2.67.0\build\Grpc.Tools.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Grpc.Tools.2.67.0\build\Grpc.Tools.targets'))" />
|
||||
</Target>
|
||||
<Import Project="..\packages\Grpc.Core.2.46.6\build\net45\Grpc.Core.targets" Condition="Exists('..\packages\Grpc.Core.2.46.6\build\net45\Grpc.Core.targets')" />
|
||||
<Import Project="..\packages\Tesseract.5.2.0\build\Tesseract.targets" Condition="Exists('..\packages\Tesseract.5.2.0\build\Tesseract.targets')" />
|
||||
<Import Project="..\packages\Grpc.Tools.2.67.0\build\Grpc.Tools.targets" Condition="Exists('..\packages\Grpc.Tools.2.67.0\build\Grpc.Tools.targets')" />
|
||||
</Project>
|
100
WelsonJS.Toolkit/WelsonJS.Service/app.config
Normal file
|
@ -0,0 +1,100 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" /></startup>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.1.2" newVersion="4.0.1.2" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.Bcl.AsyncInterfaces" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.Extensions.Logging.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-8.0.0.2" newVersion="8.0.0.2" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-8.0.0.1" newVersion="8.0.0.1" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Net.Http.WinHttpHandler" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-8.0.0.2" newVersion="8.0.0.2" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="DeviceId" publicKeyToken="f755c371b5c59c52" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-6.7.0.0" newVersion="6.7.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Google.Protobuf" publicKeyToken="a7d26565bac4d604" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-3.27.3.0" newVersion="3.27.3.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Grpc.Core" publicKeyToken="d754f35622e28bad" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Grpc.Core.Api" publicKeyToken="d754f35622e28bad" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Grpc.Net.Client" publicKeyToken="d754f35622e28bad" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Grpc.Net.Client.Web" publicKeyToken="d754f35622e28bad" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Grpc.Net.Common" publicKeyToken="d754f35622e28bad" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.Extensions.DependencyInjection.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-8.0.0.2" newVersion="8.0.0.2" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="RestSharp" publicKeyToken="598062e77f915f75" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-112.0.0.0" newVersion="112.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Numerics.Vectors" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.1.4.0" newVersion="4.1.4.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Text.Encodings.Web" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Text.Json" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-8.0.0.5" newVersion="8.0.0.5" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.2.0.1" newVersion="4.2.0.1" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.Extensions.Options" publicKeyToken="adb9793829ddae60" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-8.0.0.2" newVersion="8.0.0.2" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.Extensions.Configuration.Binder" publicKeyToken="adb9793829ddae60" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-8.0.0.2" newVersion="8.0.0.2" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
BIN
WelsonJS.Toolkit/WelsonJS.Service/favicon.ico
Normal file
After Width: | Height: | Size: 56 KiB |
38
WelsonJS.Toolkit/WelsonJS.Service/packages.config
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="ClamAV.Net" version="0.1.166" targetFramework="net48" />
|
||||
<package id="Google.Protobuf" version="3.28.2" targetFramework="net48" />
|
||||
<package id="Grpc" version="2.46.6" targetFramework="net48" />
|
||||
<package id="Grpc.Core" version="2.46.6" targetFramework="net48" />
|
||||
<package id="Grpc.Core.Api" version="2.66.0" targetFramework="net48" />
|
||||
<package id="Grpc.Net.Client" version="2.66.0" targetFramework="net48" />
|
||||
<package id="Grpc.Net.Client.Web" version="2.66.0" targetFramework="net48" />
|
||||
<package id="Grpc.Net.Common" version="2.66.0" targetFramework="net48" />
|
||||
<package id="Grpc.Tools" version="2.67.0" targetFramework="net48" developmentDependency="true" />
|
||||
<package id="Microsoft.Bcl.AsyncInterfaces" version="8.0.0" targetFramework="net48" />
|
||||
<package id="Microsoft.Extensions.Configuration" version="8.0.0" targetFramework="net48" />
|
||||
<package id="Microsoft.Extensions.Configuration.Abstractions" version="8.0.0" targetFramework="net48" />
|
||||
<package id="Microsoft.Extensions.Configuration.Binder" version="8.0.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Extensions.DependencyInjection" version="8.0.1" targetFramework="net48" />
|
||||
<package id="Microsoft.Extensions.DependencyInjection.Abstractions" version="8.0.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Extensions.Logging" version="8.0.1" targetFramework="net48" />
|
||||
<package id="Microsoft.Extensions.Logging.Abstractions" version="8.0.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Extensions.Logging.Configuration" version="8.0.1" targetFramework="net48" />
|
||||
<package id="Microsoft.Extensions.Logging.Console" version="8.0.1" targetFramework="net48" />
|
||||
<package id="Microsoft.Extensions.Options" version="8.0.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Extensions.Options.ConfigurationExtensions" version="8.0.0" targetFramework="net48" />
|
||||
<package id="Microsoft.Extensions.Primitives" version="8.0.0" targetFramework="net48" />
|
||||
<package id="RestSharp" version="112.1.0" targetFramework="net48" />
|
||||
<package id="System.Buffers" version="4.5.1" targetFramework="net48" />
|
||||
<package id="System.Diagnostics.DiagnosticSource" version="8.0.1" targetFramework="net48" />
|
||||
<package id="System.IO.Hashing" version="8.0.0" targetFramework="net48" />
|
||||
<package id="System.Memory" version="4.5.5" targetFramework="net48" />
|
||||
<package id="System.Net.Http.WinHttpHandler" version="8.0.2" targetFramework="net48" />
|
||||
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net48" />
|
||||
<package id="System.Runtime.CompilerServices.Unsafe" version="6.0.0" targetFramework="net48" />
|
||||
<package id="System.Text.Encodings.Web" version="8.0.0" targetFramework="net48" />
|
||||
<package id="System.Text.Json" version="8.0.5" targetFramework="net48" />
|
||||
<package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net48" />
|
||||
<package id="System.ValueTuple" version="4.5.0" targetFramework="net48" />
|
||||
<package id="Tesseract" version="5.2.0" targetFramework="net48" />
|
||||
</packages>
|
|
@ -1,10 +1,14 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.31515.178
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.8.34322.80
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WelsonJS.Toolkit", "WelsonJS.Toolkit\WelsonJS.Toolkit.csproj", "{D6007282-B4F7-4694-AC67-BB838D91B77A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WelsonJS.Service", "WelsonJS.Service\WelsonJS.Service.csproj", "{09F295EE-5EDB-4327-ABBE-DDCCDF5EDD9E}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WelsonJS.Launcher", "WelsonJS.Launcher\WelsonJS.Launcher.csproj", "{4074D413-195C-45E9-9E63-0D07914187B8}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -15,10 +19,28 @@ Global
|
|||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{D6007282-B4F7-4694-AC67-BB838D91B77A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D6007282-B4F7-4694-AC67-BB838D91B77A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D6007282-B4F7-4694-AC67-BB838D91B77A}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{D6007282-B4F7-4694-AC67-BB838D91B77A}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{D6007282-B4F7-4694-AC67-BB838D91B77A}.Debug|x86.Build.0 = Debug|x86
|
||||
{D6007282-B4F7-4694-AC67-BB838D91B77A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D6007282-B4F7-4694-AC67-BB838D91B77A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D6007282-B4F7-4694-AC67-BB838D91B77A}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{D6007282-B4F7-4694-AC67-BB838D91B77A}.Release|x86.ActiveCfg = Release|x86
|
||||
{D6007282-B4F7-4694-AC67-BB838D91B77A}.Release|x86.Build.0 = Release|x86
|
||||
{09F295EE-5EDB-4327-ABBE-DDCCDF5EDD9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{09F295EE-5EDB-4327-ABBE-DDCCDF5EDD9E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{09F295EE-5EDB-4327-ABBE-DDCCDF5EDD9E}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{09F295EE-5EDB-4327-ABBE-DDCCDF5EDD9E}.Debug|x86.Build.0 = Debug|x86
|
||||
{09F295EE-5EDB-4327-ABBE-DDCCDF5EDD9E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{09F295EE-5EDB-4327-ABBE-DDCCDF5EDD9E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{09F295EE-5EDB-4327-ABBE-DDCCDF5EDD9E}.Release|x86.ActiveCfg = Release|x86
|
||||
{09F295EE-5EDB-4327-ABBE-DDCCDF5EDD9E}.Release|x86.Build.0 = Release|x86
|
||||
{4074D413-195C-45E9-9E63-0D07914187B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4074D413-195C-45E9-9E63-0D07914187B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4074D413-195C-45E9-9E63-0D07914187B8}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{4074D413-195C-45E9-9E63-0D07914187B8}.Debug|x86.Build.0 = Debug|x86
|
||||
{4074D413-195C-45E9-9E63-0D07914187B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4074D413-195C-45E9-9E63-0D07914187B8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4074D413-195C-45E9-9E63-0D07914187B8}.Release|x86.ActiveCfg = Release|x86
|
||||
{4074D413-195C-45E9-9E63-0D07914187B8}.Release|x86.Build.0 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
114
WelsonJS.Toolkit/WelsonJS.Toolkit/BitmapUtils.cs
Normal file
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* WelsonJS.Toolkit: WelsonJS native component
|
||||
*
|
||||
* filename:
|
||||
* BitmapUtils.cs
|
||||
*
|
||||
* description:
|
||||
* WelsonJS - Build a Windows app on the Windows built-in JavaScript engine
|
||||
*
|
||||
* website:
|
||||
* - https://github.com/gnh1201/welsonjs
|
||||
* - https://catswords.social/@catswords_oss
|
||||
* - https://teams.live.com/l/community/FEACHncAhq8ldnojAI
|
||||
*
|
||||
* author:
|
||||
* Namhyeon Go <abuse@catswords.net>
|
||||
*
|
||||
* license:
|
||||
* GPLv3 or MS-RL(Microsoft Reciprocal License)
|
||||
*
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
|
||||
namespace WelsonJS
|
||||
{
|
||||
public class BitmapUtils
|
||||
{
|
||||
private static Bitmap Load(string filename)
|
||||
{
|
||||
return new Bitmap(filename);
|
||||
}
|
||||
|
||||
public static void Crop(string srcfile, string dstfile, int x, int y, int a, int b)
|
||||
{
|
||||
Bitmap originalBitmap = Load(srcfile);
|
||||
|
||||
Rectangle cropArea = new Rectangle(x, y, a, b);
|
||||
Bitmap croppedBitmap = originalBitmap.Clone(cropArea, originalBitmap.PixelFormat);
|
||||
|
||||
croppedBitmap.Save(dstfile);
|
||||
}
|
||||
|
||||
public static int[] GetSize(string srcfile)
|
||||
{
|
||||
Bitmap bitmap = Load(srcfile);
|
||||
|
||||
int width = bitmap.Width;
|
||||
int height = bitmap.Height;
|
||||
|
||||
bitmap.Dispose();
|
||||
|
||||
return new int[] { width, height };
|
||||
}
|
||||
|
||||
public static int[] GetPixel(string srcfile, int x, int y)
|
||||
{
|
||||
Bitmap bitmap = Load(srcfile);
|
||||
|
||||
Color pixelColor = bitmap.GetPixel(x, y);
|
||||
int red = pixelColor.R;
|
||||
int green = pixelColor.G;
|
||||
int blue = pixelColor.B;
|
||||
|
||||
bitmap.Dispose();
|
||||
|
||||
return new int[] { red, green, blue };
|
||||
}
|
||||
|
||||
public static string GetBase64(string srcfile)
|
||||
{
|
||||
Bitmap bitmap = Load(srcfile);
|
||||
MemoryStream memoryStream = new MemoryStream();
|
||||
|
||||
ImageFormat imageFormat;
|
||||
if (srcfile.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
imageFormat = ImageFormat.Bmp;
|
||||
}
|
||||
else if (srcfile.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) || srcfile.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
imageFormat = ImageFormat.Jpeg;
|
||||
}
|
||||
else if (srcfile.EndsWith(".png", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
imageFormat = ImageFormat.Png;
|
||||
}
|
||||
else if (srcfile.EndsWith(".tiff", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
imageFormat = ImageFormat.Tiff;
|
||||
}
|
||||
else if (srcfile.EndsWith(".gif", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
imageFormat = ImageFormat.Gif;
|
||||
}
|
||||
else
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
bitmap.Save(memoryStream, imageFormat);
|
||||
byte[] imageBytes = memoryStream.ToArray();
|
||||
string base64String = Convert.ToBase64String(imageBytes);
|
||||
|
||||
bitmap.Dispose();
|
||||
memoryStream.Dispose();
|
||||
|
||||
return base64String;
|
||||
}
|
||||
}
|
||||
}
|
104
WelsonJS.Toolkit/WelsonJS.Toolkit/Compression/LZ77.cs
Normal file
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* WelsonJS.Toolkit: WelsonJS native component
|
||||
*
|
||||
* filename:
|
||||
* LZ77.cs
|
||||
*
|
||||
* description:
|
||||
* MsCompress(LZ77) algorithm implementation for WelsonJS
|
||||
*
|
||||
* website:
|
||||
* - https://github.com/gnh1201/welsonjs
|
||||
* - https://catswords.social/@catswords_oss
|
||||
* - https://teams.live.com/l/community/FEACHncAhq8ldnojAI
|
||||
*
|
||||
* author:
|
||||
* Namhyeon Go <abuse@catswords.net>
|
||||
*
|
||||
* license:
|
||||
* GPLv3 or MS-RL(Microsoft Reciprocal License)
|
||||
*
|
||||
*/
|
||||
using System.Text;
|
||||
|
||||
namespace WelsonJS.Compression
|
||||
{
|
||||
public class LZ77
|
||||
{
|
||||
public static string Compress(string input)
|
||||
{
|
||||
StringBuilder compressed = new StringBuilder();
|
||||
int searchBufferIndex = 0;
|
||||
|
||||
while (searchBufferIndex < input.Length)
|
||||
{
|
||||
int longestMatchLength = 0;
|
||||
int longestMatchOffset = 0;
|
||||
|
||||
// Search for the longest match in the look-ahead buffer
|
||||
for (int i = 0; i < searchBufferIndex; i++)
|
||||
{
|
||||
int matchLength = 0;
|
||||
while (matchLength < input.Length - searchBufferIndex && input[i + matchLength] == input[searchBufferIndex + matchLength])
|
||||
{
|
||||
matchLength++;
|
||||
}
|
||||
|
||||
if (matchLength > longestMatchLength)
|
||||
{
|
||||
longestMatchLength = matchLength;
|
||||
longestMatchOffset = searchBufferIndex - i;
|
||||
}
|
||||
}
|
||||
|
||||
// Output the token (offset, length)
|
||||
if (longestMatchLength > 0)
|
||||
{
|
||||
compressed.Append($"({longestMatchOffset},{longestMatchLength})");
|
||||
searchBufferIndex += longestMatchLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
compressed.Append($"(0,{input[searchBufferIndex]})");
|
||||
searchBufferIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
return compressed.ToString();
|
||||
}
|
||||
|
||||
public static string Decompress(string compressedData)
|
||||
{
|
||||
StringBuilder decompressed = new StringBuilder();
|
||||
int currentIndex = 0;
|
||||
|
||||
while (currentIndex < compressedData.Length)
|
||||
{
|
||||
if (compressedData[currentIndex] == '(')
|
||||
{
|
||||
// Match case
|
||||
int commaIndex = compressedData.IndexOf(',', currentIndex);
|
||||
int offset = int.Parse(compressedData.Substring(currentIndex + 1, commaIndex - currentIndex - 1));
|
||||
int closingParenIndex = compressedData.IndexOf(')', commaIndex);
|
||||
int length = int.Parse(compressedData.Substring(commaIndex + 1, closingParenIndex - commaIndex - 1));
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
int copyIndex = decompressed.Length - offset;
|
||||
decompressed.Append(decompressed[copyIndex]);
|
||||
}
|
||||
|
||||
currentIndex = closingParenIndex + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Literal case
|
||||
decompressed.Append(compressedData[currentIndex]);
|
||||
currentIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
return decompressed.ToString();
|
||||
}
|
||||
}
|
||||
}
|
614
WelsonJS.Toolkit/WelsonJS.Toolkit/Cryptography/ARIA.cs
Normal file
|
@ -0,0 +1,614 @@
|
|||
/*
|
||||
* WelsonJS.Toolkit: WelsonJS native component
|
||||
*
|
||||
* filename:
|
||||
* ARIA.cs
|
||||
*
|
||||
* description:
|
||||
* ARIA(KS X 1213-1, RFC5794, RFC6209) cryptography algorithm implementation (Experimental)
|
||||
*
|
||||
* website:
|
||||
* - https://github.com/gnh1201/welsonjs
|
||||
* - https://catswords.social/@catswords_oss
|
||||
* - https://teams.live.com/l/community/FEACHncAhq8ldnojAI
|
||||
* - https://discord.gg/XKG5CjtXEj
|
||||
*
|
||||
* authors:
|
||||
* - Namhyeon Go (@gnh1201) <abuse@catswords.net>
|
||||
* - @angelkum (blog.naver.com)
|
||||
* - KISA(Korea Internet & Security Agency) (kisa.or.kr)
|
||||
* - National Security Research Institute (NSRI)
|
||||
* - National Intelligence Service (nis.go.kr)
|
||||
*
|
||||
* references:
|
||||
* - https://seed.kisa.or.kr/kisa/Board/19/detailView.do
|
||||
* - https://blog.naver.com/angelkum/130154153446
|
||||
* - https://www.ncsc.go.kr:4018/PageLink.do?link=forward:/PageContent.do&tempParam1=&menuNo=060000&subMenuNo=060200&thirdMenuNo=
|
||||
* - https://www.nis.go.kr/AF/1_7_3_1.do
|
||||
* - https://datatracker.ietf.org/doc/html/rfc5794
|
||||
* - https://datatracker.ietf.org/doc/html/rfc6209
|
||||
* - https://github.com/eGovFrame/egovframework.rte.root/blob/master/Foundation/egovframework.rte.fdl.crypto/src/main/java/egovframework/rte/fdl/cryptography/impl/aria/ARIAEngine.java
|
||||
* - https://ics.catswords.net/ARIA-specification.pdf
|
||||
* - https://ics.catswords.net/ARIA-testvector.pdf
|
||||
*
|
||||
* license:
|
||||
* GPLv3 or MS-RL(Microsoft Reciprocal License)
|
||||
*
|
||||
*/
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace WelsonJS.Cryptography
|
||||
{
|
||||
public class ARIA
|
||||
{
|
||||
private static readonly uint[,] KRK = new uint[,] {
|
||||
{0x517cc1b7, 0x27220a94, 0xfe13abe8, 0xfa9a6ee0},
|
||||
{0x6db14acc, 0x9e21c820, 0xff28b1d5, 0xef5de2b0},
|
||||
{0xdb92371d, 0x2126e970, 0x03249775, 0x04e8c90e}
|
||||
};
|
||||
|
||||
private static readonly byte[] S1 = new byte[256];
|
||||
private static readonly byte[] S2 = new byte[256];
|
||||
private static readonly byte[] X1 = new byte[256];
|
||||
private static readonly byte[] X2 = new byte[256];
|
||||
|
||||
private static readonly uint[] TS1 = new uint[256];
|
||||
private static readonly uint[] TS2 = new uint[256];
|
||||
private static readonly uint[] TX1 = new uint[256];
|
||||
private static readonly uint[] TX2 = new uint[256];
|
||||
|
||||
// Static initializer. For setting up the tables
|
||||
static ARIA()
|
||||
{
|
||||
uint[] exp = new uint[256];
|
||||
uint[] log = new uint[256];
|
||||
exp[0] = 1;
|
||||
for (int i = 1; i < 256; i++)
|
||||
{
|
||||
uint j = (exp[i - 1] << 1) ^ exp[i - 1];
|
||||
if ((j & 0x100) != 0) j ^= 0x11b;
|
||||
exp[i] = j;
|
||||
}
|
||||
for (int i = 1; i < 255; i++)
|
||||
log[exp[i]] = (uint)i;
|
||||
|
||||
uint[,] A = new uint[,] {
|
||||
{1, 0, 0, 0, 1, 1, 1, 1},
|
||||
{1, 1, 0, 0, 0, 1, 1, 1},
|
||||
{1, 1, 1, 0, 0, 0, 1, 1},
|
||||
{1, 1, 1, 1, 0, 0, 0, 1},
|
||||
{1, 1, 1, 1, 1, 0, 0, 0},
|
||||
{0, 1, 1, 1, 1, 1, 0, 0},
|
||||
{0, 0, 1, 1, 1, 1, 1, 0},
|
||||
{0, 0, 0, 1, 1, 1, 1, 1}
|
||||
};
|
||||
|
||||
uint[,] B = new uint[,] {
|
||||
{0, 1, 0, 1, 1, 1, 1, 0},
|
||||
{0, 0, 1, 1, 1, 1, 0, 1},
|
||||
{1, 1, 0, 1, 0, 1, 1, 1},
|
||||
{1, 0, 0, 1, 1, 1, 0, 1},
|
||||
{0, 0, 1, 0, 1, 1, 0, 0},
|
||||
{1, 0, 0, 0, 0, 0, 0, 1},
|
||||
{0, 1, 0, 1, 1, 1, 0, 1},
|
||||
{1, 1, 0, 1, 0, 0, 1, 1}
|
||||
};
|
||||
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
uint t = 0, p;
|
||||
if (i == 0)
|
||||
p = 0;
|
||||
else
|
||||
p = exp[255 - log[i]];
|
||||
for (int j = 0; j < 8; j++)
|
||||
{
|
||||
uint s = 0;
|
||||
for (int k = 0; k < 8; k++)
|
||||
{
|
||||
if (((p >> (7 - k)) & 0x01) != 0)
|
||||
s ^= A[k, j];
|
||||
}
|
||||
t = (t << 1) ^ s;
|
||||
}
|
||||
t ^= 0x63;
|
||||
S1[i] = (byte)t;
|
||||
X1[t] = (byte)i;
|
||||
}
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
uint t = 0, p;
|
||||
if (i == 0)
|
||||
p = 0;
|
||||
else
|
||||
p = exp[(247 * log[i]) % 255];
|
||||
for (int j = 0; j < 8; j++)
|
||||
{
|
||||
uint s = 0;
|
||||
for (int k = 0; k < 8; k++)
|
||||
{
|
||||
if (((p >> k) & 0x01) != 0)
|
||||
s ^= B[7 - j, k];
|
||||
}
|
||||
t = (t << 1) ^ s;
|
||||
}
|
||||
t ^= 0xe2;
|
||||
S2[i] = (byte)t;
|
||||
X2[t] = (byte)i;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
TS1[i] = (uint)0x00010101 * (S1[i] & (uint)0xff);
|
||||
TS2[i] = (uint)0x01000101 * (S2[i] & (uint)0xff);
|
||||
TX1[i] = (uint)0x01010001 * (X1[i] & (uint)0xff);
|
||||
TX2[i] = (uint)0x01010100 * (X2[i] & (uint)0xff);
|
||||
}
|
||||
}
|
||||
|
||||
private int keySize = 0;
|
||||
private int numberOfRounds = 0;
|
||||
private byte[] masterKey = null;
|
||||
private uint[] encRoundKeys = null, decRoundKeys = null;
|
||||
|
||||
public ARIA(int keySize)
|
||||
{
|
||||
SetKeySize(keySize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the class so that it can be reused for another master key.
|
||||
*/
|
||||
private void Reset()
|
||||
{
|
||||
keySize = 0;
|
||||
numberOfRounds = 0;
|
||||
masterKey = null;
|
||||
encRoundKeys = null;
|
||||
decRoundKeys = null;
|
||||
}
|
||||
|
||||
public int GetKeySize()
|
||||
{
|
||||
return keySize;
|
||||
}
|
||||
|
||||
private void SetKeySize(int _keySize)
|
||||
{
|
||||
Reset();
|
||||
|
||||
if (_keySize != 128 && _keySize != 192 && _keySize != 256)
|
||||
throw new CryptographicException("keySize=" + _keySize);
|
||||
|
||||
switch (_keySize)
|
||||
{
|
||||
case 128:
|
||||
numberOfRounds = 12;
|
||||
break;
|
||||
case 192:
|
||||
numberOfRounds = 14;
|
||||
break;
|
||||
case 256:
|
||||
numberOfRounds = 16;
|
||||
break;
|
||||
}
|
||||
|
||||
keySize = _keySize;
|
||||
}
|
||||
|
||||
private void SetKey(byte[] masterKey)
|
||||
{
|
||||
if (masterKey.Length * 8 < keySize)
|
||||
throw new CryptographicException("masterKey size=" + masterKey.Length);
|
||||
|
||||
decRoundKeys = null;
|
||||
encRoundKeys = null;
|
||||
masterKey = (byte[])masterKey.Clone();
|
||||
}
|
||||
|
||||
private void SetupEncRoundKeys()
|
||||
{
|
||||
if (keySize == 0)
|
||||
throw new CryptographicException("keySize");
|
||||
if (masterKey == null)
|
||||
throw new CryptographicException("masterKey");
|
||||
if (encRoundKeys == null)
|
||||
encRoundKeys = new uint[4 * (numberOfRounds + 1)];
|
||||
|
||||
decRoundKeys = null;
|
||||
DoEncKeySetup(masterKey, encRoundKeys, keySize);
|
||||
}
|
||||
|
||||
void SetupDecRoundKeys()
|
||||
{
|
||||
if (keySize == 0)
|
||||
throw new CryptographicException("keySize");
|
||||
|
||||
if (encRoundKeys == null)
|
||||
{
|
||||
if (masterKey == null)
|
||||
{
|
||||
throw new CryptographicException("masterKey");
|
||||
}
|
||||
else
|
||||
{
|
||||
SetupEncRoundKeys();
|
||||
}
|
||||
}
|
||||
|
||||
decRoundKeys = (uint[])encRoundKeys.Clone();
|
||||
DoDecKeySetup(masterKey, decRoundKeys, keySize);
|
||||
}
|
||||
|
||||
public void SetupRoundKeys()
|
||||
{
|
||||
SetupDecRoundKeys();
|
||||
}
|
||||
|
||||
private static void DoCrypt(byte[] i, int ioffset, uint[] rk, int nr, byte[] o, int ooffset)
|
||||
{
|
||||
uint t0, t1, t2, t3, j = 0;
|
||||
|
||||
t0 = ToInt(i[0 + ioffset], i[1 + ioffset], i[2 + ioffset], i[3 + ioffset]);
|
||||
t1 = ToInt(i[4 + ioffset], i[5 + ioffset], i[6 + ioffset], i[7 + ioffset]);
|
||||
t2 = ToInt(i[8 + ioffset], i[9 + ioffset], i[10 + ioffset], i[11 + ioffset]);
|
||||
t3 = ToInt(i[12 + ioffset], i[13 + ioffset], i[14 + ioffset], i[15 + ioffset]);
|
||||
|
||||
for (int r = 1; r < nr / 2; r++)
|
||||
{
|
||||
t0 ^= rk[j++]; t1 ^= rk[j++]; t2 ^= rk[j++]; t3 ^= rk[j++];
|
||||
t0 = TS1[(t0 >> 24) & 0xff] ^ TS2[(t0 >> 16) & 0xff] ^ TX1[(t0 >> 8) & 0xff] ^ TX2[t0 & 0xff];
|
||||
t1 = TS1[(t1 >> 24) & 0xff] ^ TS2[(t1 >> 16) & 0xff] ^ TX1[(t1 >> 8) & 0xff] ^ TX2[t1 & 0xff];
|
||||
t2 = TS1[(t2 >> 24) & 0xff] ^ TS2[(t2 >> 16) & 0xff] ^ TX1[(t2 >> 8) & 0xff] ^ TX2[t2 & 0xff];
|
||||
t3 = TS1[(t3 >> 24) & 0xff] ^ TS2[(t3 >> 16) & 0xff] ^ TX1[(t3 >> 8) & 0xff] ^ TX2[t3 & 0xff];
|
||||
t1 ^= t2; t2 ^= t3; t0 ^= t1; t3 ^= t1; t2 ^= t0; t1 ^= t2;
|
||||
t1 = Badc(t1); t2 = Cdab(t2); t3 = Dcba(t3);
|
||||
t1 ^= t2; t2 ^= t3; t0 ^= t1; t3 ^= t1; t2 ^= t0; t1 ^= t2;
|
||||
|
||||
t0 ^= rk[j++]; t1 ^= rk[j++]; t2 ^= rk[j++]; t3 ^= rk[j++];
|
||||
t0 = TX1[(t0 >> 24) & 0xff] ^ TX2[(t0 >> 16) & 0xff] ^ TS1[(t0 >> 8) & 0xff] ^ TS2[t0 & 0xff];
|
||||
t1 = TX1[(t1 >> 24) & 0xff] ^ TX2[(t1 >> 16) & 0xff] ^ TS1[(t1 >> 8) & 0xff] ^ TS2[t1 & 0xff];
|
||||
t2 = TX1[(t2 >> 24) & 0xff] ^ TX2[(t2 >> 16) & 0xff] ^ TS1[(t2 >> 8) & 0xff] ^ TS2[t2 & 0xff];
|
||||
t3 = TX1[(t3 >> 24) & 0xff] ^ TX2[(t3 >> 16) & 0xff] ^ TS1[(t3 >> 8) & 0xff] ^ TS2[t3 & 0xff];
|
||||
t1 ^= t2; t2 ^= t3; t0 ^= t1; t3 ^= t1; t2 ^= t0; t1 ^= t2;
|
||||
t3 = Badc(t3); t0 = Cdab(t0); t1 = Dcba(t1);
|
||||
t1 ^= t2; t2 ^= t3; t0 ^= t1; t3 ^= t1; t2 ^= t0; t1 ^= t2;
|
||||
}
|
||||
t0 ^= rk[j++]; t1 ^= rk[j++]; t2 ^= rk[j++]; t3 ^= rk[j++];
|
||||
t0 = TS1[(t0 >> 24) & 0xff] ^ TS2[(t0 >> 16) & 0xff] ^ TX1[(t0 >> 8) & 0xff] ^ TX2[t0 & 0xff];
|
||||
t1 = TS1[(t1 >> 24) & 0xff] ^ TS2[(t1 >> 16) & 0xff] ^ TX1[(t1 >> 8) & 0xff] ^ TX2[t1 & 0xff];
|
||||
t2 = TS1[(t2 >> 24) & 0xff] ^ TS2[(t2 >> 16) & 0xff] ^ TX1[(t2 >> 8) & 0xff] ^ TX2[t2 & 0xff];
|
||||
t3 = TS1[(t3 >> 24) & 0xff] ^ TS2[(t3 >> 16) & 0xff] ^ TX1[(t3 >> 8) & 0xff] ^ TX2[t3 & 0xff];
|
||||
t1 ^= t2; t2 ^= t3; t0 ^= t1; t3 ^= t1; t2 ^= t0; t1 ^= t2;
|
||||
t1 = Badc(t1); t2 = Cdab(t2); t3 = Dcba(t3);
|
||||
t1 ^= t2; t2 ^= t3; t0 ^= t1; t3 ^= t1; t2 ^= t0; t1 ^= t2;
|
||||
|
||||
t0 ^= rk[j++]; t1 ^= rk[j++]; t2 ^= rk[j++]; t3 ^= rk[j++];
|
||||
o[0 + ooffset] = (byte)(X1[0xff & (t0 >> 24)] ^ (rk[j] >> 24));
|
||||
o[1 + ooffset] = (byte)(X2[0xff & (t0 >> 16)] ^ (rk[j] >> 16));
|
||||
o[2 + ooffset] = (byte)(S1[0xff & (t0 >> 8)] ^ (rk[j] >> 8));
|
||||
o[3 + ooffset] = (byte)(S2[0xff & (t0)] ^ (rk[j]));
|
||||
o[4 + ooffset] = (byte)(X1[0xff & (t1 >> 24)] ^ (rk[j + 1] >> 24));
|
||||
o[5 + ooffset] = (byte)(X2[0xff & (t1 >> 16)] ^ (rk[j + 1] >> 16));
|
||||
o[6 + ooffset] = (byte)(S1[0xff & (t1 >> 8)] ^ (rk[j + 1] >> 8));
|
||||
o[7 + ooffset] = (byte)(S2[0xff & (t1)] ^ (rk[j + 1]));
|
||||
o[8 + ooffset] = (byte)(X1[0xff & (t2 >> 24)] ^ (rk[j + 2] >> 24));
|
||||
o[9 + ooffset] = (byte)(X2[0xff & (t2 >> 16)] ^ (rk[j + 2] >> 16));
|
||||
o[10 + ooffset] = (byte)(S1[0xff & (t2 >> 8)] ^ (rk[j + 2] >> 8));
|
||||
o[11 + ooffset] = (byte)(S2[0xff & (t2)] ^ (rk[j + 2]));
|
||||
o[12 + ooffset] = (byte)(X1[0xff & (t3 >> 24)] ^ (rk[j + 3] >> 24));
|
||||
o[13 + ooffset] = (byte)(X2[0xff & (t3 >> 16)] ^ (rk[j + 3] >> 16));
|
||||
o[14 + ooffset] = (byte)(S1[0xff & (t3 >> 8)] ^ (rk[j + 3] >> 8));
|
||||
o[15 + ooffset] = (byte)(S2[0xff & (t3)] ^ (rk[j + 3]));
|
||||
}
|
||||
|
||||
public void Encrypt(byte[] i, int ioffset, byte[] o, int ooffset)
|
||||
{
|
||||
if (keySize == 0)
|
||||
throw new CryptographicException("keySize");
|
||||
|
||||
if (encRoundKeys == null)
|
||||
{
|
||||
if (masterKey == null)
|
||||
{
|
||||
throw new CryptographicException("masterKey");
|
||||
}
|
||||
else
|
||||
{
|
||||
SetupEncRoundKeys();
|
||||
}
|
||||
}
|
||||
|
||||
DoCrypt(i, ioffset, encRoundKeys, numberOfRounds, o, ooffset);
|
||||
}
|
||||
|
||||
public byte[] Encrypt(byte[] i, int ioffset)
|
||||
{
|
||||
byte[] o = new byte[16];
|
||||
Encrypt(i, ioffset, o, 0);
|
||||
return o;
|
||||
}
|
||||
|
||||
public void Decrypt(byte[] i, int ioffset, byte[] o, int ooffset)
|
||||
{
|
||||
if (keySize == 0)
|
||||
throw new CryptographicException("keySize");
|
||||
|
||||
if (decRoundKeys == null)
|
||||
{
|
||||
if (masterKey == null)
|
||||
{
|
||||
throw new CryptographicException("masterKey");
|
||||
}
|
||||
else
|
||||
{
|
||||
SetupDecRoundKeys();
|
||||
}
|
||||
}
|
||||
|
||||
DoCrypt(i, ioffset, decRoundKeys, numberOfRounds, o, ooffset);
|
||||
}
|
||||
|
||||
public byte[] Decrypt(byte[] i, int ioffset)
|
||||
{
|
||||
byte[] o = new byte[16];
|
||||
Decrypt(i, ioffset, o, 0);
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
private static void DoEncKeySetup(byte[] mk, uint[] rk, int keyBits)
|
||||
{
|
||||
uint t0, t1, t2, t3;
|
||||
int q, j = 0;
|
||||
uint[] w0 = new uint[4];
|
||||
uint[] w1 = new uint[4];
|
||||
uint[] w2 = new uint[4];
|
||||
uint[] w3 = new uint[4];
|
||||
|
||||
w0[0] = ToInt(mk[0], mk[1], mk[2], mk[3]);
|
||||
w0[1] = ToInt(mk[4], mk[5], mk[6], mk[7]);
|
||||
w0[2] = ToInt(mk[8], mk[9], mk[10], mk[11]);
|
||||
w0[3] = ToInt(mk[12], mk[13], mk[14], mk[15]);
|
||||
|
||||
q = (keyBits - 128) / 64;
|
||||
t0 = w0[0] ^ KRK[q, 0]; t1 = w0[1] ^ KRK[q, 1];
|
||||
t2 = w0[2] ^ KRK[q, 2]; t3 = w0[3] ^ KRK[q, 3];
|
||||
t0 = TS1[(t0 >> 24) & 0xff] ^ TS2[(t0 >> 16) & 0xff] ^ TX1[(t0 >> 8) & 0xff] ^ TX2[t0 & 0xff];
|
||||
t1 = TS1[(t1 >> 24) & 0xff] ^ TS2[(t1 >> 16) & 0xff] ^ TX1[(t1 >> 8) & 0xff] ^ TX2[t1 & 0xff];
|
||||
t2 = TS1[(t2 >> 24) & 0xff] ^ TS2[(t2 >> 16) & 0xff] ^ TX1[(t2 >> 8) & 0xff] ^ TX2[t2 & 0xff];
|
||||
t3 = TS1[(t3 >> 24) & 0xff] ^ TS2[(t3 >> 16) & 0xff] ^ TX1[(t3 >> 8) & 0xff] ^ TX2[t3 & 0xff];
|
||||
t1 ^= t2; t2 ^= t3; t0 ^= t1; t3 ^= t1; t2 ^= t0; t1 ^= t2;
|
||||
t1 = Badc(t1); t2 = Cdab(t2); t3 = Dcba(t3);
|
||||
t1 ^= t2; t2 ^= t3; t0 ^= t1; t3 ^= t1; t2 ^= t0; t1 ^= t2;
|
||||
|
||||
if (keyBits > 128)
|
||||
{
|
||||
w1[0] = ToInt(mk[16], mk[17], mk[18], mk[19]);
|
||||
w1[1] = ToInt(mk[20], mk[21], mk[22], mk[23]);
|
||||
if (keyBits > 192)
|
||||
{
|
||||
w1[2] = ToInt(mk[24], mk[25], mk[26], mk[27]);
|
||||
w1[3] = ToInt(mk[28], mk[29], mk[30], mk[31]);
|
||||
}
|
||||
else
|
||||
{
|
||||
w1[2] = w1[3] = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
w1[0] = w1[1] = w1[2] = w1[3] = 0;
|
||||
}
|
||||
w1[0] ^= t0; w1[1] ^= t1; w1[2] ^= t2; w1[3] ^= t3;
|
||||
t0 = w1[0]; t1 = w1[1]; t2 = w1[2]; t3 = w1[3];
|
||||
|
||||
q = (q == 2) ? 0 : (q + 1);
|
||||
t0 ^= KRK[q, 0]; t1 ^= KRK[q, 1]; t2 ^= KRK[q, 2]; t3 ^= KRK[q, 3];
|
||||
t0 = TX1[(t0 >> 24) & 0xff] ^ TX2[(t0 >> 16) & 0xff] ^ TS1[(t0 >> 8) & 0xff] ^ TS2[t0 & 0xff];
|
||||
t1 = TX1[(t1 >> 24) & 0xff] ^ TX2[(t1 >> 16) & 0xff] ^ TS1[(t1 >> 8) & 0xff] ^ TS2[t1 & 0xff];
|
||||
t2 = TX1[(t2 >> 24) & 0xff] ^ TX2[(t2 >> 16) & 0xff] ^ TS1[(t2 >> 8) & 0xff] ^ TS2[t2 & 0xff];
|
||||
t3 = TX1[(t3 >> 24) & 0xff] ^ TX2[(t3 >> 16) & 0xff] ^ TS1[(t3 >> 8) & 0xff] ^ TS2[t3 & 0xff];
|
||||
t1 ^= t2; t2 ^= t3; t0 ^= t1; t3 ^= t1; t2 ^= t0; t1 ^= t2;
|
||||
t3 = Badc(t3); t0 = Cdab(t0); t1 = Dcba(t1);
|
||||
t1 ^= t2; t2 ^= t3; t0 ^= t1; t3 ^= t1; t2 ^= t0; t1 ^= t2;
|
||||
t0 ^= w0[0]; t1 ^= w0[1]; t2 ^= w0[2]; t3 ^= w0[3];
|
||||
w2[0] = t0; w2[1] = t1; w2[2] = t2; w2[3] = t3;
|
||||
|
||||
q = (q == 2) ? 0 : (q + 1);
|
||||
t0 ^= KRK[q, 0]; t1 ^= KRK[q, 1]; t2 ^= KRK[q, 2]; t3 ^= KRK[q, 3];
|
||||
t0 = TS1[(t0 >> 24) & 0xff] ^ TS2[(t0 >> 16) & 0xff] ^ TX1[(t0 >> 8) & 0xff] ^ TX2[t0 & 0xff];
|
||||
t1 = TS1[(t1 >> 24) & 0xff] ^ TS2[(t1 >> 16) & 0xff] ^ TX1[(t1 >> 8) & 0xff] ^ TX2[t1 & 0xff];
|
||||
t2 = TS1[(t2 >> 24) & 0xff] ^ TS2[(t2 >> 16) & 0xff] ^ TX1[(t2 >> 8) & 0xff] ^ TX2[t2 & 0xff];
|
||||
t3 = TS1[(t3 >> 24) & 0xff] ^ TS2[(t3 >> 16) & 0xff] ^ TX1[(t3 >> 8) & 0xff] ^ TX2[t3 & 0xff];
|
||||
t1 ^= t2; t2 ^= t3; t0 ^= t1; t3 ^= t1; t2 ^= t0; t1 ^= t2;
|
||||
t1 = Badc(t1); t2 = Cdab(t2); t3 = Dcba(t3);
|
||||
t1 ^= t2; t2 ^= t3; t0 ^= t1; t3 ^= t1; t2 ^= t0; t1 ^= t2;
|
||||
w3[0] = t0 ^ w1[0]; w3[1] = t1 ^ w1[1]; w3[2] = t2 ^ w1[2]; w3[3] = t3 ^ w1[3];
|
||||
|
||||
Gsrk(w0, w1, 19, rk, j); j += 4;
|
||||
Gsrk(w1, w2, 19, rk, j); j += 4;
|
||||
Gsrk(w2, w3, 19, rk, j); j += 4;
|
||||
Gsrk(w3, w0, 19, rk, j); j += 4;
|
||||
Gsrk(w0, w1, 31, rk, j); j += 4;
|
||||
Gsrk(w1, w2, 31, rk, j); j += 4;
|
||||
Gsrk(w2, w3, 31, rk, j); j += 4;
|
||||
Gsrk(w3, w0, 31, rk, j); j += 4;
|
||||
Gsrk(w0, w1, 67, rk, j); j += 4;
|
||||
Gsrk(w1, w2, 67, rk, j); j += 4;
|
||||
Gsrk(w2, w3, 67, rk, j); j += 4;
|
||||
Gsrk(w3, w0, 67, rk, j); j += 4;
|
||||
Gsrk(w0, w1, 97, rk, j); j += 4;
|
||||
if (keyBits > 128)
|
||||
{
|
||||
Gsrk(w1, w2, 97, rk, j); j += 4;
|
||||
Gsrk(w2, w3, 97, rk, j); j += 4;
|
||||
}
|
||||
if (keyBits > 192)
|
||||
{
|
||||
Gsrk(w3, w0, 97, rk, j); j += 4;
|
||||
Gsrk(w0, w1, 109, rk, j);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main bulk of the decryption key setup method. Here we assume that
|
||||
* the int array rk already contains the encryption round keys.
|
||||
* @param mk the master key
|
||||
* @param rk the array which contains the encryption round keys at the
|
||||
* beginning of the method execution. At the end of method execution
|
||||
* this will hold the decryption round keys.
|
||||
* @param keyBits the length of the master key
|
||||
* @return
|
||||
*/
|
||||
private static void DoDecKeySetup(byte[] mk, uint[] rk, int keyBits)
|
||||
{
|
||||
int a = 0, z;
|
||||
uint[] t = new uint[4];
|
||||
|
||||
z = 32 + keyBits / 8;
|
||||
SwapBlocks(rk, 0, z);
|
||||
a += 4; z -= 4;
|
||||
|
||||
for (; a < z; a += 4, z -= 4)
|
||||
SwapAndDiffuse(rk, a, z, t);
|
||||
Diff(rk, a, t, 0);
|
||||
rk[a] = t[0]; rk[a + 1] = t[1]; rk[a + 2] = t[2]; rk[a + 3] = t[3];
|
||||
}
|
||||
|
||||
private static uint ToInt(byte b0, byte b1, byte b2, byte b3)
|
||||
{
|
||||
return (uint)((b0 & 0xff) << 24 ^ (b1 & 0xff) << 16 ^ (b2 & 0xff) << 8 ^ b3 & 0xff);
|
||||
}
|
||||
|
||||
private static uint M(uint t)
|
||||
{
|
||||
return 0x00010101 * ((t >> 24) & 0xff) ^ 0x01000101 * ((t >> 16) & 0xff) ^
|
||||
0x01010001 * ((t >> 8) & 0xff) ^ 0x01010100 * (t & 0xff);
|
||||
}
|
||||
|
||||
// private static final int ms(int t) {
|
||||
// return TS1[(t>>>24)&0xff]^TS2[(t>>>16)&0xff]^TX1[(t>>>8)&0xff]^TX2[t&0xff];
|
||||
// }
|
||||
// private static final int mx(int t) {
|
||||
// return TX1[(t>>>24)&0xff]^TX2[(t>>>16)&0xff]^TS1[(t>>>8)&0xff]^TS2[t&0xff];
|
||||
// }
|
||||
private static uint Badc(uint t)
|
||||
{
|
||||
return ((t << 8) & 0xff00ff00) ^ ((t >> 8) & 0x00ff00ff);
|
||||
}
|
||||
|
||||
private static uint Cdab(uint t)
|
||||
{
|
||||
return ((t << 16) & 0xffff0000) ^ ((t >> 16) & 0x0000ffff);
|
||||
}
|
||||
|
||||
private static uint Dcba(uint t)
|
||||
{
|
||||
return (t & 0x000000ff) << 24 ^ (t & 0x0000ff00) << 8 ^ (t & 0x00ff0000) >> 8 ^ (t & 0xff000000) >> 24;
|
||||
}
|
||||
|
||||
private static void Gsrk(uint[] x, uint[] y, int rot, uint[] rk, int offset)
|
||||
{
|
||||
int q = 4 - (rot / 32), r = rot % 32, s = 32 - r;
|
||||
|
||||
rk[offset] = x[0] ^ y[(q) % 4] >> r ^ y[(q + 3) % 4] << s;
|
||||
rk[offset + 1] = x[1] ^ y[(q + 1) % 4] >> r ^ y[(q) % 4] << s;
|
||||
rk[offset + 2] = x[2] ^ y[(q + 2) % 4] >> r ^ y[(q + 1) % 4] << s;
|
||||
rk[offset + 3] = x[3] ^ y[(q + 3) % 4] >> r ^ y[(q + 2) % 4] << s;
|
||||
}
|
||||
|
||||
private static void Diff(uint[] i, int offset1, uint[] o, int offset2)
|
||||
{
|
||||
uint t0, t1, t2, t3;
|
||||
|
||||
t0 = M(i[offset1]); t1 = M(i[offset1 + 1]); t2 = M(i[offset1 + 2]); t3 = M(i[offset1 + 3]);
|
||||
t1 ^= t2; t2 ^= t3; t0 ^= t1; t3 ^= t1; t2 ^= t0; t1 ^= t2;
|
||||
t1 = Badc(t1); t2 = Cdab(t2); t3 = Dcba(t3);
|
||||
t1 ^= t2; t2 ^= t3; t0 ^= t1; t3 ^= t1; t2 ^= t0; t1 ^= t2;
|
||||
o[offset2] = t0; o[offset2 + 1] = t1; o[offset2 + 2] = t2; o[offset2 + 3] = t3;
|
||||
}
|
||||
|
||||
private static void SwapBlocks(uint[] arr, int offset1, int offset2)
|
||||
{
|
||||
uint t;
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
t = arr[offset1 + i];
|
||||
arr[offset1 + i] = arr[offset2 + i];
|
||||
arr[offset2 + i] = t;
|
||||
}
|
||||
}
|
||||
|
||||
private static void SwapAndDiffuse(uint[] arr, int offset1, int offset2, uint[] tmp)
|
||||
{
|
||||
Diff(arr, offset1, tmp, 0);
|
||||
Diff(arr, offset2, arr, offset1);
|
||||
arr[offset2] = tmp[0]; arr[offset2 + 1] = tmp[1];
|
||||
arr[offset2 + 2] = tmp[2]; arr[offset2 + 3] = tmp[3];
|
||||
}
|
||||
|
||||
public class ECB
|
||||
{
|
||||
private static readonly int BLOCK_SIZE = 16;
|
||||
private ARIA engine = null;
|
||||
|
||||
public ECB(byte[] key)
|
||||
{
|
||||
Init(key);
|
||||
}
|
||||
|
||||
public ECB(string key)
|
||||
{
|
||||
Init(CreateKey(key));
|
||||
}
|
||||
|
||||
private void Init(byte[] key)
|
||||
{
|
||||
engine = new ARIA(key.Length * 8);
|
||||
engine.SetKey(key);
|
||||
engine.SetupRoundKeys();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// allowed key size (bit): 128, 192, 256
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
private byte[] CreateKey(string key)
|
||||
{
|
||||
SHA256 hasher = SHA256.Create();
|
||||
byte[] hashData = hasher.ComputeHash(Encoding.Default.GetBytes(key));
|
||||
|
||||
return hashData;
|
||||
}
|
||||
|
||||
public byte[] Encrypt(byte[] data)
|
||||
{
|
||||
byte[] indata = AnsiX923Padding.AddPadding(data, BLOCK_SIZE);
|
||||
byte[] outdata = new byte[indata.Length];
|
||||
|
||||
for (int i = 0; i < indata.Length; i += BLOCK_SIZE)
|
||||
{
|
||||
engine.Encrypt(indata, i, outdata, i);
|
||||
}
|
||||
|
||||
return outdata;
|
||||
}
|
||||
|
||||
public byte[] Decrypt(byte[] data)
|
||||
{
|
||||
byte[] outdata = new byte[data.Length];
|
||||
|
||||
for (int i = 0; i < data.Length; i += BLOCK_SIZE)
|
||||
{
|
||||
engine.Decrypt(data, i, outdata, i);
|
||||
}
|
||||
|
||||
return AnsiX923Padding.RemovePadding(outdata, BLOCK_SIZE, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* WelsonJS.Toolkit: WelsonJS native component
|
||||
*
|
||||
* filename:
|
||||
* AnsiX923Padding.cs
|
||||
*
|
||||
* description:
|
||||
* AnsiX923Padding implementation
|
||||
*
|
||||
* website:
|
||||
* - https://github.com/gnh1201/welsonjs
|
||||
* - https://catswords.social/@catswords_oss
|
||||
* - https://teams.live.com/l/community/FEACHncAhq8ldnojAI
|
||||
* - https://discord.gg/XKG5CjtXEj
|
||||
*
|
||||
* authors:
|
||||
* - Namhyeon Go (@gnh1201) <abuse@catswords.net>
|
||||
*
|
||||
* references:
|
||||
* - https://github.com/eGovFrame/egovframework.rte.root/blob/master/Foundation/egovframework.rte.fdl.crypto/src/main/java/egovframework/rte/fdl/cryptography/impl/aria/AnsiX923Padding.java
|
||||
* - ChatGPT prompt "AnsiX923Padding with C#" (chatgpt.com)
|
||||
* - ChatGPT prompt "AnsiX923Padding with C#, Add a flag to decide how to handle possible errors when removing padding." (chatgpt.com)
|
||||
*
|
||||
* license:
|
||||
* GPLv3 or MS-RL(Microsoft Reciprocal License)
|
||||
*
|
||||
*/
|
||||
using System;
|
||||
|
||||
namespace WelsonJS.Cryptography
|
||||
{
|
||||
class AnsiX923Padding
|
||||
{
|
||||
/// <summary>
|
||||
/// Add ANSI X.923 padding to the input data to make it a multiple of the block size.
|
||||
/// </summary>
|
||||
/// <param name="data">The data to be padded.</param>
|
||||
/// <param name="blockSize">The block size to pad to.</param>
|
||||
/// <returns>Padded data with ANSI X.923 padding.</returns>
|
||||
public static byte[] AddPadding(byte[] data, int blockSize)
|
||||
{
|
||||
int paddingLength = blockSize - (data.Length % blockSize);
|
||||
|
||||
// If the data is already a multiple of the block size, no padding is needed
|
||||
if (paddingLength == blockSize)
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
byte[] paddedData = new byte[data.Length + paddingLength];
|
||||
|
||||
// Copy original data into the padded array
|
||||
Array.Copy(data, paddedData, data.Length);
|
||||
|
||||
// Fill with 0x00 bytes, and the last byte is the padding length
|
||||
for (int i = data.Length; i < paddedData.Length - 1; i++)
|
||||
{
|
||||
paddedData[i] = 0x00;
|
||||
}
|
||||
|
||||
// Last byte is the padding length
|
||||
paddedData[paddedData.Length - 1] = (byte)paddingLength;
|
||||
|
||||
return paddedData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes ANSI X.923 padding from the given data.
|
||||
/// </summary>
|
||||
/// <param name="data">The input data, including padding.</param>
|
||||
/// <param name="blockSize">The block size used for padding.</param>
|
||||
/// <param name="ignoreErrors">If true, ignores errors and attempts to process the input data as-is.</param>
|
||||
/// <returns>The unpadded data as a byte array.</returns>
|
||||
/// <exception cref="ArgumentException">Thrown if the input data or padding is invalid and ignoreErrors is false.</exception>
|
||||
public static byte[] RemovePadding(byte[] data, int blockSize, bool ignoreErrors = false)
|
||||
{
|
||||
// Check for null or empty data
|
||||
if (data == null || data.Length == 0)
|
||||
{
|
||||
if (ignoreErrors)
|
||||
{
|
||||
return new byte[] { };
|
||||
}
|
||||
throw new ArgumentException("Input data cannot be null or empty.");
|
||||
}
|
||||
|
||||
// Ensure the data length is a multiple of the block size
|
||||
if (data.Length % blockSize != 0)
|
||||
{
|
||||
if (ignoreErrors)
|
||||
{
|
||||
// Return the original data if errors are ignored
|
||||
return data;
|
||||
}
|
||||
throw new ArgumentException("Input data length must be a multiple of the block size.");
|
||||
}
|
||||
|
||||
// Retrieve the padding length from the last byte
|
||||
int paddingLength = data[data.Length - 1];
|
||||
|
||||
// Validate padding length
|
||||
if (paddingLength <= 0 || paddingLength > blockSize)
|
||||
{
|
||||
if (!ignoreErrors)
|
||||
{
|
||||
throw new ArgumentException($"Invalid padding length: {paddingLength}. Must be between 1 and {blockSize}.");
|
||||
}
|
||||
|
||||
// Treat padding length as 0 and return the full data
|
||||
return data;
|
||||
}
|
||||
|
||||
// Validate the padding region (last paddingLength - 1 bytes must be 0x00)
|
||||
for (int i = data.Length - paddingLength; i < data.Length - 1; i++)
|
||||
{
|
||||
if (data[i] != 0x00)
|
||||
{
|
||||
if (!ignoreErrors)
|
||||
{
|
||||
throw new ArgumentException("Invalid padding detected. Expected padding bytes to be 0x00.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extract unpadded data
|
||||
byte[] unpaddedData = new byte[data.Length - paddingLength];
|
||||
Array.Copy(data, unpaddedData, unpaddedData.Length);
|
||||
|
||||
return unpaddedData;
|
||||
}
|
||||
}
|
||||
}
|
389
WelsonJS.Toolkit/WelsonJS.Toolkit/Cryptography/HIGHT.cs
Normal file
|
@ -0,0 +1,389 @@
|
|||
/*
|
||||
* WelsonJS.Toolkit: WelsonJS native component
|
||||
*
|
||||
* filename:
|
||||
* HIGHT.cs
|
||||
*
|
||||
* description:
|
||||
* HIGHT(ISO/IEC 18033-3) cryptography algorithm implementation
|
||||
*
|
||||
* website:
|
||||
* - https://github.com/gnh1201/welsonjs
|
||||
* - https://catswords.social/@catswords_oss
|
||||
* - https://teams.live.com/l/community/FEACHncAhq8ldnojAI
|
||||
* - https://discord.gg/XKG5CjtXEj
|
||||
*
|
||||
* authors:
|
||||
* - Namhyeon Go (@gnh1201) <abuse@catswords.net>
|
||||
* - @chandong83 (github.com)
|
||||
* - KISA(Korea Internet & Security Agency) (kisa.or.kr)
|
||||
* - Korea Unversity (www.korea.ac.kr)
|
||||
*
|
||||
* references:
|
||||
* - https://seed.kisa.or.kr/kisa/algorithm/EgovHightInfo.do
|
||||
* - https://github.com/chandong83/csharp_crypto_hight_ecb_examples
|
||||
* - https://blog.naver.com/chandong83/222198351602
|
||||
* - https://www.iso.org/standard/54531.html
|
||||
* - https://ics.catswords.net/HIGHT-algorithm-specification-english.pdf
|
||||
* - https://ics.catswords.net/HIGHT-algorithm-specification-korean.pdf
|
||||
* - https://ics.catswords.net/HIGHT-sourcecode-explanation.pdf
|
||||
*
|
||||
* license:
|
||||
* GPLv3 or MS-RL(Microsoft Reciprocal License)
|
||||
*
|
||||
*/
|
||||
using System;
|
||||
|
||||
namespace WelsonJS.Cryptography
|
||||
{
|
||||
public class HIGHT
|
||||
{
|
||||
private static readonly byte[] hightDelta = {
|
||||
0x5A,0x6D,0x36,0x1B,0x0D,0x06,0x03,0x41,
|
||||
0x60,0x30,0x18,0x4C,0x66,0x33,0x59,0x2C,
|
||||
0x56,0x2B,0x15,0x4A,0x65,0x72,0x39,0x1C,
|
||||
0x4E,0x67,0x73,0x79,0x3C,0x5E,0x6F,0x37,
|
||||
0x5B,0x2D,0x16,0x0B,0x05,0x42,0x21,0x50,
|
||||
0x28,0x54,0x2A,0x55,0x6A,0x75,0x7A,0x7D,
|
||||
0x3E,0x5F,0x2F,0x17,0x4B,0x25,0x52,0x29,
|
||||
0x14,0x0A,0x45,0x62,0x31,0x58,0x6C,0x76,
|
||||
0x3B,0x1D,0x0E,0x47,0x63,0x71,0x78,0x7C,
|
||||
0x7E,0x7F,0x3F,0x1F,0x0F,0x07,0x43,0x61,
|
||||
0x70,0x38,0x5C,0x6E,0x77,0x7B,0x3D,0x1E,
|
||||
0x4F,0x27,0x53,0x69,0x34,0x1A,0x4D,0x26,
|
||||
0x13,0x49,0x24,0x12,0x09,0x04,0x02,0x01,
|
||||
0x40,0x20,0x10,0x08,0x44,0x22,0x11,0x48,
|
||||
0x64,0x32,0x19,0x0C,0x46,0x23,0x51,0x68,
|
||||
0x74,0x3A,0x5D,0x2E,0x57,0x6B,0x35,0x5A
|
||||
};
|
||||
|
||||
private static readonly byte[] hightF0 = {
|
||||
0x00,0x86,0x0D,0x8B,0x1A,0x9C,0x17,0x91,
|
||||
0x34,0xB2,0x39,0xBF,0x2E,0xA8,0x23,0xA5,
|
||||
0x68,0xEE,0x65,0xE3,0x72,0xF4,0x7F,0xF9,
|
||||
0x5C,0xDA,0x51,0xD7,0x46,0xC0,0x4B,0xCD,
|
||||
0xD0,0x56,0xDD,0x5B,0xCA,0x4C,0xC7,0x41,
|
||||
0xE4,0x62,0xE9,0x6F,0xFE,0x78,0xF3,0x75,
|
||||
0xB8,0x3E,0xB5,0x33,0xA2,0x24,0xAF,0x29,
|
||||
0x8C,0x0A,0x81,0x07,0x96,0x10,0x9B,0x1D,
|
||||
0xA1,0x27,0xAC,0x2A,0xBB,0x3D,0xB6,0x30,
|
||||
0x95,0x13,0x98,0x1E,0x8F,0x09,0x82,0x04,
|
||||
0xC9,0x4F,0xC4,0x42,0xD3,0x55,0xDE,0x58,
|
||||
0xFD,0x7B,0xF0,0x76,0xE7,0x61,0xEA,0x6C,
|
||||
0x71,0xF7,0x7C,0xFA,0x6B,0xED,0x66,0xE0,
|
||||
0x45,0xC3,0x48,0xCE,0x5F,0xD9,0x52,0xD4,
|
||||
0x19,0x9F,0x14,0x92,0x03,0x85,0x0E,0x88,
|
||||
0x2D,0xAB,0x20,0xA6,0x37,0xB1,0x3A,0xBC,
|
||||
0x43,0xC5,0x4E,0xC8,0x59,0xDF,0x54,0xD2,
|
||||
0x77,0xF1,0x7A,0xFC,0x6D,0xEB,0x60,0xE6,
|
||||
0x2B,0xAD,0x26,0xA0,0x31,0xB7,0x3C,0xBA,
|
||||
0x1F,0x99,0x12,0x94,0x05,0x83,0x08,0x8E,
|
||||
0x93,0x15,0x9E,0x18,0x89,0x0F,0x84,0x02,
|
||||
0xA7,0x21,0xAA,0x2C,0xBD,0x3B,0xB0,0x36,
|
||||
0xFB,0x7D,0xF6,0x70,0xE1,0x67,0xEC,0x6A,
|
||||
0xCF,0x49,0xC2,0x44,0xD5,0x53,0xD8,0x5E,
|
||||
0xE2,0x64,0xEF,0x69,0xF8,0x7E,0xF5,0x73,
|
||||
0xD6,0x50,0xDB,0x5D,0xCC,0x4A,0xC1,0x47,
|
||||
0x8A,0x0C,0x87,0x01,0x90,0x16,0x9D,0x1B,
|
||||
0xBE,0x38,0xB3,0x35,0xA4,0x22,0xA9,0x2F,
|
||||
0x32,0xB4,0x3F,0xB9,0x28,0xAE,0x25,0xA3,
|
||||
0x06,0x80,0x0B,0x8D,0x1C,0x9A,0x11,0x97,
|
||||
0x5A,0xDC,0x57,0xD1,0x40,0xC6,0x4D,0xCB,
|
||||
0x6E,0xE8,0x63,0xE5,0x74,0xF2,0x79,0xFF
|
||||
};
|
||||
|
||||
private static readonly byte[] hightF1 = {
|
||||
0x00,0x58,0xB0,0xE8,0x61,0x39,0xD1,0x89,
|
||||
0xC2,0x9A,0x72,0x2A,0xA3,0xFB,0x13,0x4B,
|
||||
0x85,0xDD,0x35,0x6D,0xE4,0xBC,0x54,0x0C,
|
||||
0x47,0x1F,0xF7,0xAF,0x26,0x7E,0x96,0xCE,
|
||||
0x0B,0x53,0xBB,0xE3,0x6A,0x32,0xDA,0x82,
|
||||
0xC9,0x91,0x79,0x21,0xA8,0xF0,0x18,0x40,
|
||||
0x8E,0xD6,0x3E,0x66,0xEF,0xB7,0x5F,0x07,
|
||||
0x4C,0x14,0xFC,0xA4,0x2D,0x75,0x9D,0xC5,
|
||||
0x16,0x4E,0xA6,0xFE,0x77,0x2F,0xC7,0x9F,
|
||||
0xD4,0x8C,0x64,0x3C,0xB5,0xED,0x05,0x5D,
|
||||
0x93,0xCB,0x23,0x7B,0xF2,0xAA,0x42,0x1A,
|
||||
0x51,0x09,0xE1,0xB9,0x30,0x68,0x80,0xD8,
|
||||
0x1D,0x45,0xAD,0xF5,0x7C,0x24,0xCC,0x94,
|
||||
0xDF,0x87,0x6F,0x37,0xBE,0xE6,0x0E,0x56,
|
||||
0x98,0xC0,0x28,0x70,0xF9,0xA1,0x49,0x11,
|
||||
0x5A,0x02,0xEA,0xB2,0x3B,0x63,0x8B,0xD3,
|
||||
0x2C,0x74,0x9C,0xC4,0x4D,0x15,0xFD,0xA5,
|
||||
0xEE,0xB6,0x5E,0x06,0x8F,0xD7,0x3F,0x67,
|
||||
0xA9,0xF1,0x19,0x41,0xC8,0x90,0x78,0x20,
|
||||
0x6B,0x33,0xDB,0x83,0x0A,0x52,0xBA,0xE2,
|
||||
0x27,0x7F,0x97,0xCF,0x46,0x1E,0xF6,0xAE,
|
||||
0xE5,0xBD,0x55,0x0D,0x84,0xDC,0x34,0x6C,
|
||||
0xA2,0xFA,0x12,0x4A,0xC3,0x9B,0x73,0x2B,
|
||||
0x60,0x38,0xD0,0x88,0x01,0x59,0xB1,0xE9,
|
||||
0x3A,0x62,0x8A,0xD2,0x5B,0x03,0xEB,0xB3,
|
||||
0xF8,0xA0,0x48,0x10,0x99,0xC1,0x29,0x71,
|
||||
0xBF,0xE7,0x0F,0x57,0xDE,0x86,0x6E,0x36,
|
||||
0x7D,0x25,0xCD,0x95,0x1C,0x44,0xAC,0xF4,
|
||||
0x31,0x69,0x81,0xD9,0x50,0x08,0xE0,0xB8,
|
||||
0xF3,0xAB,0x43,0x1B,0x92,0xCA,0x22,0x7A,
|
||||
0xB4,0xEC,0x04,0x5C,0xD5,0x8D,0x65,0x3D,
|
||||
0x76,0x2E,0xC6,0x9E,0x17,0x4F,0xA7,0xFF
|
||||
};
|
||||
|
||||
public class ECB
|
||||
{
|
||||
//whitening Key [8] + sub Key[128]
|
||||
byte[] scheduleKey = new byte[136];
|
||||
public ECB(byte[] userKey)
|
||||
{
|
||||
if (userKey.Length < 16)
|
||||
{
|
||||
// Pad the key with 0x00 if its length is less than 16 bytes
|
||||
byte[] paddedKey = new byte[16];
|
||||
Array.Copy(userKey, 0, paddedKey, 0, userKey.Length);
|
||||
userKey = paddedKey;
|
||||
}
|
||||
else if (userKey.Length > 16)
|
||||
{
|
||||
// If the key is longer than 16 bytes, truncate to 16 bytes
|
||||
byte[] truncatedKey = new byte[16];
|
||||
Array.Copy(userKey, truncatedKey, 16);
|
||||
userKey = truncatedKey;
|
||||
}
|
||||
|
||||
KeySched(userKey);
|
||||
}
|
||||
|
||||
void KeySched(byte[] userKey)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
scheduleKey[i] = userKey[i + 12];
|
||||
scheduleKey[i + 4] = userKey[i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
for (int j = 0; j < 8; j++)
|
||||
{
|
||||
scheduleKey[8 + 16 * i + j] = (byte)((userKey[(j - i) & 7] + hightDelta[(16 * i + j)]) & 0xFF);
|
||||
}
|
||||
for (int j = 0; j < 8; j++)
|
||||
{
|
||||
scheduleKey[8 + 16 * i + j + 8] = (byte)((userKey[((j - i) & 7) + 8] + hightDelta[16 * i + j + 8]) & 0xFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DecryptBlock(byte[] dataIn, byte[] dataOut)
|
||||
{
|
||||
byte[] xx = new byte[8];
|
||||
|
||||
xx[2] = dataIn[1];
|
||||
xx[4] = dataIn[3];
|
||||
xx[6] = dataIn[5];
|
||||
xx[0] = dataIn[7];
|
||||
|
||||
//void HIGHT_DEC(byte[] scheduleKey, byte[] xx, int k, int i0, int i1, int i2, int i3, int i4, int i5, int i6, int i7)
|
||||
void HIGHT_DEC(int k, int i0, int i1, int i2, int i3, int i4, int i5, int i6, int i7)
|
||||
{
|
||||
xx[i1] = (byte)((xx[i1] - (hightF1[xx[i2]] ^ scheduleKey[4 * k + 2])) & 0xFF);
|
||||
xx[i3] = (byte)((xx[i3] ^ (hightF0[xx[i4]] + scheduleKey[4 * k + 1])) & 0xFF);
|
||||
xx[i5] = (byte)((xx[i5] - (hightF1[xx[i6]] ^ scheduleKey[4 * k + 0])) & 0xFF);
|
||||
xx[i7] = (byte)((xx[i7] ^ (hightF0[xx[i0]] + scheduleKey[4 * k + 3])) & 0xFF);
|
||||
}
|
||||
|
||||
xx[1] = (byte)(dataIn[0] - scheduleKey[4]);
|
||||
xx[3] = (byte)(dataIn[2] ^ scheduleKey[5]);
|
||||
xx[5] = (byte)(dataIn[4] - scheduleKey[6]);
|
||||
xx[7] = (byte)(dataIn[6] ^ scheduleKey[7]);
|
||||
|
||||
HIGHT_DEC(33, 7, 6, 5, 4, 3, 2, 1, 0);
|
||||
HIGHT_DEC(32, 0, 7, 6, 5, 4, 3, 2, 1);
|
||||
HIGHT_DEC(31, 1, 0, 7, 6, 5, 4, 3, 2);
|
||||
HIGHT_DEC(30, 2, 1, 0, 7, 6, 5, 4, 3);
|
||||
HIGHT_DEC(29, 3, 2, 1, 0, 7, 6, 5, 4);
|
||||
HIGHT_DEC(28, 4, 3, 2, 1, 0, 7, 6, 5);
|
||||
HIGHT_DEC(27, 5, 4, 3, 2, 1, 0, 7, 6);
|
||||
HIGHT_DEC(26, 6, 5, 4, 3, 2, 1, 0, 7);
|
||||
HIGHT_DEC(25, 7, 6, 5, 4, 3, 2, 1, 0);
|
||||
HIGHT_DEC(24, 0, 7, 6, 5, 4, 3, 2, 1);
|
||||
HIGHT_DEC(23, 1, 0, 7, 6, 5, 4, 3, 2);
|
||||
HIGHT_DEC(22, 2, 1, 0, 7, 6, 5, 4, 3);
|
||||
HIGHT_DEC(21, 3, 2, 1, 0, 7, 6, 5, 4);
|
||||
HIGHT_DEC(20, 4, 3, 2, 1, 0, 7, 6, 5);
|
||||
HIGHT_DEC(19, 5, 4, 3, 2, 1, 0, 7, 6);
|
||||
HIGHT_DEC(18, 6, 5, 4, 3, 2, 1, 0, 7);
|
||||
HIGHT_DEC(17, 7, 6, 5, 4, 3, 2, 1, 0);
|
||||
HIGHT_DEC(16, 0, 7, 6, 5, 4, 3, 2, 1);
|
||||
HIGHT_DEC(15, 1, 0, 7, 6, 5, 4, 3, 2);
|
||||
HIGHT_DEC(14, 2, 1, 0, 7, 6, 5, 4, 3);
|
||||
HIGHT_DEC(13, 3, 2, 1, 0, 7, 6, 5, 4);
|
||||
HIGHT_DEC(12, 4, 3, 2, 1, 0, 7, 6, 5);
|
||||
HIGHT_DEC(11, 5, 4, 3, 2, 1, 0, 7, 6);
|
||||
HIGHT_DEC(10, 6, 5, 4, 3, 2, 1, 0, 7);
|
||||
HIGHT_DEC(9, 7, 6, 5, 4, 3, 2, 1, 0);
|
||||
HIGHT_DEC(8, 0, 7, 6, 5, 4, 3, 2, 1);
|
||||
HIGHT_DEC(7, 1, 0, 7, 6, 5, 4, 3, 2);
|
||||
HIGHT_DEC(6, 2, 1, 0, 7, 6, 5, 4, 3);
|
||||
HIGHT_DEC(5, 3, 2, 1, 0, 7, 6, 5, 4);
|
||||
HIGHT_DEC(4, 4, 3, 2, 1, 0, 7, 6, 5);
|
||||
HIGHT_DEC(3, 5, 4, 3, 2, 1, 0, 7, 6);
|
||||
HIGHT_DEC(2, 6, 5, 4, 3, 2, 1, 0, 7);
|
||||
|
||||
dataOut[1] = (byte)(xx[1] & 0xFF);
|
||||
dataOut[3] = (byte)(xx[3] & 0xFF);
|
||||
dataOut[5] = (byte)(xx[5] & 0xFF);
|
||||
dataOut[7] = (byte)(xx[7] & 0xFF);
|
||||
|
||||
dataOut[0] = (byte)((xx[0] - scheduleKey[0]) & 0xFF);
|
||||
dataOut[2] = (byte)((xx[2] ^ scheduleKey[1]) & 0xFF);
|
||||
dataOut[4] = (byte)((xx[4] - scheduleKey[2]) & 0xFF);
|
||||
dataOut[6] = (byte)((xx[6] ^ scheduleKey[3]) & 0xFF);
|
||||
}
|
||||
|
||||
void EncryptBlock(byte[] dataIn, byte[] dataOut)
|
||||
{
|
||||
byte[] xx = new byte[8];
|
||||
xx[1] = dataIn[1];
|
||||
xx[3] = dataIn[3];
|
||||
xx[5] = dataIn[5];
|
||||
xx[7] = dataIn[7];
|
||||
|
||||
xx[0] = (byte)((dataIn[0] + scheduleKey[0]) & 0xFF);
|
||||
xx[2] = (byte)((dataIn[2] ^ scheduleKey[1]));
|
||||
xx[4] = (byte)((dataIn[4] + scheduleKey[2]) & 0xFF);
|
||||
xx[6] = (byte)((dataIn[6] ^ scheduleKey[3]));
|
||||
|
||||
void HIGHT_ENC(int k, int i0, int i1, int i2, int i3, int i4, int i5, int i6, int i7)
|
||||
{
|
||||
xx[i0] = (byte)((xx[i0] ^ (hightF0[xx[i1]] + scheduleKey[4 * k + 3])) & 0xFF);
|
||||
xx[i2] = (byte)((xx[i2] + (hightF1[xx[i3]] ^ scheduleKey[4 * k + 2])) & 0xFF);
|
||||
xx[i4] = (byte)((xx[i4] ^ (hightF0[xx[i5]] + scheduleKey[4 * k + 1])) & 0xFF);
|
||||
xx[i6] = (byte)((xx[i6] + (hightF1[xx[i7]] ^ scheduleKey[4 * k + 0])) & 0xFF);
|
||||
}
|
||||
|
||||
HIGHT_ENC(2, 7, 6, 5, 4, 3, 2, 1, 0);
|
||||
HIGHT_ENC(3, 6, 5, 4, 3, 2, 1, 0, 7);
|
||||
HIGHT_ENC(4, 5, 4, 3, 2, 1, 0, 7, 6);
|
||||
HIGHT_ENC(5, 4, 3, 2, 1, 0, 7, 6, 5);
|
||||
HIGHT_ENC(6, 3, 2, 1, 0, 7, 6, 5, 4);
|
||||
HIGHT_ENC(7, 2, 1, 0, 7, 6, 5, 4, 3);
|
||||
HIGHT_ENC(8, 1, 0, 7, 6, 5, 4, 3, 2);
|
||||
HIGHT_ENC(9, 0, 7, 6, 5, 4, 3, 2, 1);
|
||||
HIGHT_ENC(10, 7, 6, 5, 4, 3, 2, 1, 0);
|
||||
HIGHT_ENC(11, 6, 5, 4, 3, 2, 1, 0, 7);
|
||||
HIGHT_ENC(12, 5, 4, 3, 2, 1, 0, 7, 6);
|
||||
HIGHT_ENC(13, 4, 3, 2, 1, 0, 7, 6, 5);
|
||||
HIGHT_ENC(14, 3, 2, 1, 0, 7, 6, 5, 4);
|
||||
HIGHT_ENC(15, 2, 1, 0, 7, 6, 5, 4, 3);
|
||||
HIGHT_ENC(16, 1, 0, 7, 6, 5, 4, 3, 2);
|
||||
HIGHT_ENC(17, 0, 7, 6, 5, 4, 3, 2, 1);
|
||||
HIGHT_ENC(18, 7, 6, 5, 4, 3, 2, 1, 0);
|
||||
HIGHT_ENC(19, 6, 5, 4, 3, 2, 1, 0, 7);
|
||||
HIGHT_ENC(20, 5, 4, 3, 2, 1, 0, 7, 6);
|
||||
HIGHT_ENC(21, 4, 3, 2, 1, 0, 7, 6, 5);
|
||||
HIGHT_ENC(22, 3, 2, 1, 0, 7, 6, 5, 4);
|
||||
HIGHT_ENC(23, 2, 1, 0, 7, 6, 5, 4, 3);
|
||||
HIGHT_ENC(24, 1, 0, 7, 6, 5, 4, 3, 2);
|
||||
HIGHT_ENC(25, 0, 7, 6, 5, 4, 3, 2, 1);
|
||||
HIGHT_ENC(26, 7, 6, 5, 4, 3, 2, 1, 0);
|
||||
HIGHT_ENC(27, 6, 5, 4, 3, 2, 1, 0, 7);
|
||||
HIGHT_ENC(28, 5, 4, 3, 2, 1, 0, 7, 6);
|
||||
HIGHT_ENC(29, 4, 3, 2, 1, 0, 7, 6, 5);
|
||||
HIGHT_ENC(30, 3, 2, 1, 0, 7, 6, 5, 4);
|
||||
HIGHT_ENC(31, 2, 1, 0, 7, 6, 5, 4, 3);
|
||||
HIGHT_ENC(32, 1, 0, 7, 6, 5, 4, 3, 2);
|
||||
HIGHT_ENC(33, 0, 7, 6, 5, 4, 3, 2, 1);
|
||||
|
||||
dataOut[1] = (byte)(xx[2] & 0xFF);
|
||||
dataOut[3] = (byte)(xx[4] & 0xFF);
|
||||
dataOut[5] = (byte)(xx[6] & 0xFF);
|
||||
dataOut[7] = (byte)(xx[0] & 0xFF);
|
||||
|
||||
dataOut[0] = (byte)((xx[1] + scheduleKey[4]) & 0xFF);
|
||||
dataOut[2] = (byte)((xx[3] ^ scheduleKey[5]) & 0xFF);
|
||||
dataOut[4] = (byte)((xx[5] + scheduleKey[6]) & 0xFF);
|
||||
dataOut[6] = (byte)((xx[7] ^ scheduleKey[7]) & 0xFF);
|
||||
}
|
||||
|
||||
public byte[] Encrypt(byte[] dataIn)
|
||||
{
|
||||
int length;
|
||||
if (dataIn.Length == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if ((dataIn.Length % 8) != 0)
|
||||
{
|
||||
length = dataIn.Length + (8 - (dataIn.Length % 8));
|
||||
}
|
||||
else
|
||||
{
|
||||
length = dataIn.Length;
|
||||
}
|
||||
byte[] dataOut = new byte[length];
|
||||
byte[] tempIn = new byte[8];
|
||||
byte[] tempOut = new byte[8];
|
||||
byte[] dataInWithPadding = new byte[length];
|
||||
Array.Copy(dataIn, dataInWithPadding, dataIn.Length);
|
||||
|
||||
for (int i = 0; i < (int)(length / 8); i++)
|
||||
{
|
||||
Array.Copy(dataInWithPadding, i * 8, tempIn, 0, 8);
|
||||
EncryptBlock(tempIn, tempOut);
|
||||
Array.Copy(tempOut, 0, dataOut, i * 8, 8);
|
||||
}
|
||||
return dataOut;
|
||||
}
|
||||
|
||||
public byte[] Decrypt(byte[] dataIn)
|
||||
{
|
||||
int length;
|
||||
if (dataIn.Length == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if ((dataIn.Length % 8) != 0)
|
||||
{
|
||||
length = dataIn.Length + (8 - (dataIn.Length % 8));
|
||||
}
|
||||
else
|
||||
{
|
||||
length = dataIn.Length;
|
||||
}
|
||||
byte[] dataOut = new byte[length];
|
||||
byte[] tempIn = new byte[8];
|
||||
byte[] tempOut = new byte[8];
|
||||
byte[] dataInWithPadding = new byte[length];
|
||||
Array.Copy(dataIn, dataInWithPadding, dataIn.Length);
|
||||
for (int i = 0; i < (int)(length / 8); i++)
|
||||
{
|
||||
Array.Copy(dataInWithPadding, i * 8, tempIn, 0, 8);
|
||||
DecryptBlock(tempIn, tempOut);
|
||||
Array.Copy(tempOut, 0, dataOut, i * 8, 8);
|
||||
}
|
||||
return dataOut;
|
||||
}
|
||||
|
||||
public void Test()
|
||||
{
|
||||
byte[] dataIn = new byte[15];
|
||||
byte[] dataOut = new byte[15];
|
||||
byte[] dataOut2 = new byte[15];
|
||||
for (int i = 0; i < 15; i++)
|
||||
{
|
||||
dataIn[i] = (byte)i;
|
||||
}
|
||||
Console.WriteLine("schedule key data");
|
||||
Console.WriteLine(string.Format("0x{0:x2}", scheduleKey));
|
||||
|
||||
dataOut = Encrypt(dataIn);
|
||||
dataOut2 = Decrypt(dataOut);
|
||||
Console.WriteLine("origin data");
|
||||
Console.WriteLine(string.Format("0x{0:x2}", dataIn));
|
||||
Console.WriteLine("encryption data");
|
||||
Console.WriteLine(string.Format("0x{0:x2}", dataOut));
|
||||
Console.WriteLine("decryption data");
|
||||
Console.WriteLine(string.Format("0x{0:x2}", dataOut2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
349
WelsonJS.Toolkit/WelsonJS.Toolkit/Cryptography/LEA.cs
Normal file
|
@ -0,0 +1,349 @@
|
|||
/*
|
||||
* WelsonJS.Toolkit: WelsonJS native component
|
||||
*
|
||||
* filename:
|
||||
* LEA.cs
|
||||
*
|
||||
* description:
|
||||
* LEA(KS X 3246:2016) cryptography algorithm implementation (Experimental)
|
||||
*
|
||||
* website:
|
||||
* - https://github.com/gnh1201/welsonjs
|
||||
* - https://catswords.social/@catswords_oss
|
||||
* - https://teams.live.com/l/community/FEACHncAhq8ldnojAI
|
||||
* - https://discord.gg/XKG5CjtXEj
|
||||
*
|
||||
* authors:
|
||||
* - Namhyeon Go (@gnh1201) <abuse@catswords.net>
|
||||
* - KISA(Korea Internet & Security Agency) (kisa.or.kr)
|
||||
* - National Security Research Institute (NSRI)
|
||||
*
|
||||
* references:
|
||||
* - https://seed.kisa.or.kr/kisa/Board/20/detailView.do
|
||||
* - https://committee.tta.or.kr/data/standard_view.jsp?order=t.kor_standard&by=asc&pk_num=TTAK.KO-12.0223&commit_code=TC5
|
||||
* - https://ics.catswords.net/LEA%20A%20128-Bit%20Block%20Cipher%20Datasheets-Korean.pdf
|
||||
* - https://ics.catswords.net/LEA%20A%20128-Bit%20Block%20Cipher%20for%20Fast%20Encryption%20on%20Common%20Processors-English.pdf
|
||||
* - https://ics.catswords.net/LEA-sourcecode-explanation.pdf
|
||||
*
|
||||
* license:
|
||||
* GPLv3 or MS-RL(Microsoft Reciprocal License)
|
||||
*
|
||||
*/
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace WelsonJS.Cryptography
|
||||
{
|
||||
public class LEA
|
||||
{
|
||||
private const int BLOCKSIZE = 16;
|
||||
private static readonly uint[] delta = {
|
||||
0xc3efe9db, 0x44626b02, 0x79e27c8a, 0x78df30ec,
|
||||
0x715ea49e, 0xc785da0a, 0xe04ef22a, 0xe5c40957
|
||||
};
|
||||
|
||||
private Mode mode;
|
||||
private int rounds;
|
||||
private uint[,] roundKeys;
|
||||
private uint[] block;
|
||||
|
||||
public LEA()
|
||||
{
|
||||
block = new uint[BLOCKSIZE / 4];
|
||||
}
|
||||
|
||||
public void Init(Mode mode, byte[] mk)
|
||||
{
|
||||
this.mode = mode;
|
||||
GenerateRoundKeys(mk);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
Array.Clear(block, 0, block.Length);
|
||||
}
|
||||
|
||||
public string GetAlgorithmName()
|
||||
{
|
||||
return "LEA";
|
||||
}
|
||||
|
||||
public int GetBlockSize()
|
||||
{
|
||||
return BLOCKSIZE;
|
||||
}
|
||||
|
||||
public int ProcessBlock(byte[] input, int inputOffset, byte[] output, int outputOffset)
|
||||
{
|
||||
if (input == null || output == null)
|
||||
throw new ArgumentNullException("Input and output buffers must not be null.");
|
||||
|
||||
if (input.Length - inputOffset < BLOCKSIZE)
|
||||
throw new InvalidOperationException("Input data is too short.");
|
||||
|
||||
if (output.Length - outputOffset < BLOCKSIZE)
|
||||
throw new InvalidOperationException("Output buffer is too short.");
|
||||
|
||||
return mode == Mode.ENCRYPT
|
||||
? EncryptBlock(input, inputOffset, output, outputOffset)
|
||||
: DecryptBlock(input, inputOffset, output, outputOffset);
|
||||
}
|
||||
|
||||
private int EncryptBlock(byte[] input, int inputOffset, byte[] output, int outputOffset)
|
||||
{
|
||||
Pack(input, inputOffset, ref block, 0, 16);
|
||||
|
||||
for (int i = 0; i < rounds; ++i)
|
||||
{
|
||||
block[3] = ROR((block[2] ^ roundKeys[i, 4]) + (block[3] ^ roundKeys[i, 5]), 3);
|
||||
block[2] = ROR((block[1] ^ roundKeys[i, 2]) + (block[2] ^ roundKeys[i, 3]), 5);
|
||||
block[1] = ROL((block[0] ^ roundKeys[i, 0]) + (block[1] ^ roundKeys[i, 1]), 9);
|
||||
++i;
|
||||
|
||||
block[0] = ROR((block[3] ^ roundKeys[i, 4]) + (block[0] ^ roundKeys[i, 5]), 3);
|
||||
block[3] = ROR((block[2] ^ roundKeys[i, 2]) + (block[3] ^ roundKeys[i, 3]), 5);
|
||||
block[2] = ROL((block[1] ^ roundKeys[i, 0]) + (block[2] ^ roundKeys[i, 1]), 9);
|
||||
|
||||
++i;
|
||||
block[1] = ROR((block[0] ^ roundKeys[i, 4]) + (block[1] ^ roundKeys[i, 5]), 3);
|
||||
block[0] = ROR((block[3] ^ roundKeys[i, 2]) + (block[0] ^ roundKeys[i, 3]), 5);
|
||||
block[3] = ROL((block[2] ^ roundKeys[i, 0]) + (block[3] ^ roundKeys[i, 1]), 9);
|
||||
|
||||
++i;
|
||||
block[2] = ROR((block[1] ^ roundKeys[i, 4]) + (block[2] ^ roundKeys[i, 5]), 3);
|
||||
block[1] = ROR((block[0] ^ roundKeys[i, 2]) + (block[1] ^ roundKeys[i, 3]), 5);
|
||||
block[0] = ROL((block[3] ^ roundKeys[i, 0]) + (block[0] ^ roundKeys[i, 1]), 9);
|
||||
}
|
||||
|
||||
Unpack(block, 0, ref output, outputOffset, 4);
|
||||
|
||||
return BLOCKSIZE;
|
||||
}
|
||||
|
||||
private int DecryptBlock(byte[] input, int inputOffset, byte[] output, int outputOffset)
|
||||
{
|
||||
Pack(input, inputOffset, ref block, 0, 16);
|
||||
|
||||
for (int i = rounds - 1; i >= 0; --i)
|
||||
{
|
||||
block[0] = (ROR(block[0], 9) - (block[3] ^ roundKeys[i, 0])) ^ roundKeys[i, 1];
|
||||
block[1] = (ROL(block[1], 5) - (block[0] ^ roundKeys[i, 2])) ^ roundKeys[i, 3];
|
||||
block[2] = (ROL(block[2], 3) - (block[1] ^ roundKeys[i, 4])) ^ roundKeys[i, 5];
|
||||
--i;
|
||||
|
||||
block[3] = (ROR(block[3], 9) - (block[2] ^ roundKeys[i, 0])) ^ roundKeys[i, 1];
|
||||
block[0] = (ROL(block[0], 5) - (block[3] ^ roundKeys[i, 2])) ^ roundKeys[i, 3];
|
||||
block[1] = (ROL(block[1], 3) - (block[0] ^ roundKeys[i, 4])) ^ roundKeys[i, 5];
|
||||
--i;
|
||||
|
||||
block[2] = (ROR(block[2], 9) - (block[1] ^ roundKeys[i, 0])) ^ roundKeys[i, 1];
|
||||
block[3] = (ROL(block[3], 5) - (block[2] ^ roundKeys[i, 2])) ^ roundKeys[i, 3];
|
||||
block[0] = (ROL(block[0], 3) - (block[3] ^ roundKeys[i, 4])) ^ roundKeys[i, 5];
|
||||
--i;
|
||||
|
||||
block[1] = (ROR(block[1], 9) - (block[0] ^ roundKeys[i, 0])) ^ roundKeys[i, 1];
|
||||
block[2] = (ROL(block[2], 5) - (block[1] ^ roundKeys[i, 2])) ^ roundKeys[i, 3];
|
||||
block[3] = (ROL(block[3], 3) - (block[2] ^ roundKeys[i, 4])) ^ roundKeys[i, 5];
|
||||
}
|
||||
|
||||
Unpack(block, 0, ref output, outputOffset, 4);
|
||||
|
||||
return BLOCKSIZE;
|
||||
}
|
||||
|
||||
private void GenerateRoundKeys(byte[] mk)
|
||||
{
|
||||
if (mk == null || (mk.Length != 16 && mk.Length != 24 && mk.Length != 32))
|
||||
throw new ArgumentException("Illegal key size");
|
||||
|
||||
uint[] T = new uint[8];
|
||||
rounds = (mk.Length >> 1) + 16;
|
||||
roundKeys = new uint[rounds, 6];
|
||||
|
||||
Pack(mk, 0, ref T, 0, 16);
|
||||
|
||||
if (mk.Length > 16)
|
||||
{
|
||||
Pack(mk, 16, ref T, 4, 8);
|
||||
}
|
||||
|
||||
if (mk.Length > 24)
|
||||
{
|
||||
Pack(mk, 24, ref T, 6, 8);
|
||||
}
|
||||
|
||||
if (mk.Length == 16)
|
||||
{
|
||||
for (int i = 0; i < 24; ++i)
|
||||
{
|
||||
uint temp = ROL(delta[i & 3], i);
|
||||
|
||||
roundKeys[i, 0] = T[0] = ROL(T[0] + ROL(temp, 0), 1);
|
||||
roundKeys[i, 1] = roundKeys[i, 3] = roundKeys[i, 5] = T[1] = ROL(T[1] + ROL(temp, 1), 3);
|
||||
roundKeys[i, 2] = T[2] = ROL(T[2] + ROL(temp, 2), 6);
|
||||
roundKeys[i, 4] = T[3] = ROL(T[3] + ROL(temp, 3), 11);
|
||||
}
|
||||
|
||||
}
|
||||
else if (mk.Length == 24)
|
||||
{
|
||||
for (int i = 0; i < 28; ++i)
|
||||
{
|
||||
uint temp = ROL(delta[i % 6], i);
|
||||
|
||||
roundKeys[i, 0] = T[0] = ROL(T[0] + ROL(temp, 0), 1);
|
||||
roundKeys[i, 1] = T[1] = ROL(T[1] + ROL(temp, 1), 3);
|
||||
roundKeys[i, 2] = T[2] = ROL(T[2] + ROL(temp, 2), 6);
|
||||
roundKeys[i, 3] = T[3] = ROL(T[3] + ROL(temp, 3), 11);
|
||||
roundKeys[i, 4] = T[4] = ROL(T[4] + ROL(temp, 4), 13);
|
||||
roundKeys[i, 5] = T[5] = ROL(T[5] + ROL(temp, 5), 17);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 32; ++i)
|
||||
{
|
||||
uint temp = ROL(delta[i & 7], i & 0x1f);
|
||||
|
||||
roundKeys[i, 0] = T[(6 * i + 0) & 7] = ROL(T[(6 * i + 0) & 7] + temp, 1);
|
||||
roundKeys[i, 1] = T[(6 * i + 1) & 7] = ROL(T[(6 * i + 1) & 7] + ROL(temp, 1), 3);
|
||||
roundKeys[i, 2] = T[(6 * i + 2) & 7] = ROL(T[(6 * i + 2) & 7] + ROL(temp, 2), 6);
|
||||
roundKeys[i, 3] = T[(6 * i + 3) & 7] = ROL(T[(6 * i + 3) & 7] + ROL(temp, 3), 11);
|
||||
roundKeys[i, 4] = T[(6 * i + 4) & 7] = ROL(T[(6 * i + 4) & 7] + ROL(temp, 4), 13);
|
||||
roundKeys[i, 5] = T[(6 * i + 5) & 7] = ROL(T[(6 * i + 5) & 7] + ROL(temp, 5), 17);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static uint ROL(uint state, int num)
|
||||
{
|
||||
return (state << num) | state >> (32 - num);
|
||||
}
|
||||
|
||||
private static uint ROR(uint state, int num)
|
||||
{
|
||||
return (state >> num) | state << (32 - num);
|
||||
}
|
||||
|
||||
public static void Pack(in byte[] input, int inputOffset, ref uint[] output, int outputOffset, int inputLength)
|
||||
{
|
||||
if (input == null || output == null)
|
||||
{
|
||||
throw new ArgumentNullException();
|
||||
}
|
||||
|
||||
if ((inputLength & 3) != 0)
|
||||
{
|
||||
throw new ArgumentException("Length should be a multiple of 4.");
|
||||
}
|
||||
|
||||
if (input.Length < inputOffset + inputLength || output.Length < outputOffset + inputLength / 4)
|
||||
{
|
||||
throw new IndexOutOfRangeException();
|
||||
}
|
||||
|
||||
int outputIndex = 0;
|
||||
for (int inputIdx = 0; inputIdx < input.Length; ++inputIdx, ++outputIndex) {
|
||||
output[outputIndex] = (uint)(input[inputIdx] & 0xff);
|
||||
output[outputIndex] |= (uint)((input[++inputIdx] & 0xff) << 8);
|
||||
output[outputIndex] |= (uint)((input[++inputIdx] & 0xff) << 16);
|
||||
output[outputIndex] |= (uint)((input[++inputIdx] & 0xff) << 24);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Unpack(in uint[] input, int inputOffset, ref byte[] output, int outputOffset, int inputLength)
|
||||
{
|
||||
if (input == null || output == null)
|
||||
{
|
||||
throw new ArgumentNullException();
|
||||
}
|
||||
|
||||
if (input.Length < inputOffset + inputLength || output.Length < outputOffset + inputLength * 4)
|
||||
{
|
||||
throw new IndexOutOfRangeException();
|
||||
}
|
||||
|
||||
int outputIdx = outputOffset;
|
||||
int endInIdx = inputOffset + inputLength;
|
||||
for (int inputIdx = inputOffset; inputIdx < endInIdx; ++inputIdx, ++outputIdx)
|
||||
{
|
||||
output[outputIdx] = (byte)input[inputIdx] ;
|
||||
output[++outputIdx] = (byte)(input[inputIdx] >> 8);
|
||||
output[++outputIdx] = (byte)(input[inputIdx] >> 16);
|
||||
output[++outputIdx] = (byte)(input[inputIdx] >> 24);
|
||||
}
|
||||
}
|
||||
|
||||
public enum Mode
|
||||
{
|
||||
ENCRYPT,
|
||||
DECRYPT
|
||||
}
|
||||
|
||||
public class ECB
|
||||
{
|
||||
private Mode mode;
|
||||
private LEA engine;
|
||||
private int blockSize;
|
||||
|
||||
ECB(Mode mode, string key)
|
||||
{
|
||||
engine = new LEA();
|
||||
Init(mode, CreateKey(key));
|
||||
blockSize = engine.GetBlockSize();
|
||||
}
|
||||
|
||||
public string GetAlgorithmName()
|
||||
{
|
||||
return engine.GetAlgorithmName() + "/ECB";
|
||||
}
|
||||
|
||||
public void Init(Mode mode, byte[] mk)
|
||||
{
|
||||
this.mode = mode;
|
||||
engine.Init(mode, mk);
|
||||
}
|
||||
|
||||
private byte[] CreateKey(string key)
|
||||
{
|
||||
SHA256 hasher = SHA256.Create();
|
||||
byte[] hashData = hasher.ComputeHash(Encoding.Default.GetBytes(key));
|
||||
|
||||
return hashData;
|
||||
}
|
||||
|
||||
public byte[] Encrypt(byte[] data)
|
||||
{
|
||||
if (this.mode != Mode.ENCRYPT)
|
||||
throw new InvalidOperationException("Not initialized for encryption mode.");
|
||||
|
||||
byte[] inputData = PKCS5Padding.AddPadding(data, blockSize);
|
||||
byte[] outputData = new byte[inputData.Length];
|
||||
|
||||
for (int i = 0; i < inputData.Length; i += blockSize)
|
||||
{
|
||||
engine.ProcessBlock(inputData, i, outputData, i);
|
||||
}
|
||||
|
||||
return outputData;
|
||||
}
|
||||
|
||||
public byte[] Decrypt(byte[] data)
|
||||
{
|
||||
if (this.mode != Mode.DECRYPT)
|
||||
throw new InvalidOperationException("Not initialized for decryption mode.");
|
||||
|
||||
byte[] outputData = new byte[data.Length];
|
||||
|
||||
for (int i = 0; i < data.Length; i += blockSize)
|
||||
{
|
||||
engine.ProcessBlock(data, i, outputData, i);
|
||||
}
|
||||
|
||||
return PKCS5Padding.RemovePadding(outputData, blockSize, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
118
WelsonJS.Toolkit/WelsonJS.Toolkit/Cryptography/PKCS5Padding.cs
Normal file
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* WelsonJS.Toolkit: WelsonJS native component
|
||||
*
|
||||
* filename:
|
||||
* PKCS5Padding.cs
|
||||
*
|
||||
* description:
|
||||
* PKCS5Padding implementation
|
||||
*
|
||||
* website:
|
||||
* - https://github.com/gnh1201/welsonjs
|
||||
* - https://catswords.social/@catswords_oss
|
||||
* - https://teams.live.com/l/community/FEACHncAhq8ldnojAI
|
||||
* - https://discord.gg/XKG5CjtXEj
|
||||
*
|
||||
* authors:
|
||||
* - Namhyeon Go (@gnh1201) <abuse@catswords.net>
|
||||
*
|
||||
* references:
|
||||
* - ChatGPT prompt "PKCS5Padding with C#" (chatgpt.com)
|
||||
* - ChatGPT prompt "PKCS5Padding with C#, Add a flag to decide how to handle possible errors when removing padding." (chatgpt.com)
|
||||
*
|
||||
* license:
|
||||
* GPLv3 or MS-RL(Microsoft Reciprocal License)
|
||||
*
|
||||
*/
|
||||
using System;
|
||||
|
||||
namespace WelsonJS.Cryptography
|
||||
{
|
||||
public class PKCS5Padding
|
||||
{
|
||||
/// <summary>
|
||||
/// Add PKCS#5 padding to the input data to make it a multiple of the block size.
|
||||
/// </summary>
|
||||
/// <param name="data">The data to be padded.</param>
|
||||
/// <param name="blockSize">The block size to pad to.</param>
|
||||
/// <returns>Padded data with PKCS#5 padding.</returns>
|
||||
public static byte[] AddPadding(byte[] data, int blockSize)
|
||||
{
|
||||
int paddingLength = blockSize - (data.Length % blockSize);
|
||||
|
||||
// If the data is already a multiple of the block size, no padding is needed
|
||||
if (paddingLength == blockSize)
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
byte[] paddedData = new byte[data.Length + paddingLength];
|
||||
|
||||
// Copy original data into the padded array
|
||||
Array.Copy(data, paddedData, data.Length);
|
||||
|
||||
// Fill padding with the padding length (PKCS5)
|
||||
for (int i = data.Length; i < paddedData.Length; i++)
|
||||
{
|
||||
paddedData[i] = (byte)paddingLength;
|
||||
}
|
||||
|
||||
return paddedData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes PKCS#5 padding from the given data.
|
||||
/// </summary>
|
||||
/// <param name="data">The input data, including padding.</param>
|
||||
/// <param name="blockSize">The block size used for padding.</param>
|
||||
/// <param name="ignoreErrors">If true, ignores errors and attempts to process the input data as-is.</param>
|
||||
/// <returns>The unpadded data as a byte array.</returns>
|
||||
/// <exception cref="ArgumentException">Thrown if the input data or padding is invalid and ignoreErrors is false.</exception>
|
||||
public static byte[] RemovePadding(byte[] data, int blockSize, bool ignoreErrors = false)
|
||||
{
|
||||
// If data length is 0, return empty array
|
||||
if (data.Length == 0)
|
||||
{
|
||||
return new byte[] { };
|
||||
}
|
||||
|
||||
// If data length is smaller than block size, treat it as unpadded
|
||||
if (data.Length < blockSize)
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
// Check if the last byte is valid padding (PKCS5)
|
||||
int paddingLength = data[data.Length - 1];
|
||||
|
||||
// Validate padding length
|
||||
if (paddingLength <= 0 || paddingLength > blockSize)
|
||||
{
|
||||
if (!ignoreErrors)
|
||||
{
|
||||
throw new ArgumentException($"Invalid padding length: {paddingLength}. Must be between 1 and {blockSize}.");
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// Check if the padding is correct (i.e., all padding bytes must be equal to paddingLength)
|
||||
for (int i = data.Length - paddingLength; i < data.Length; i++)
|
||||
{
|
||||
if (data[i] != paddingLength)
|
||||
{
|
||||
if (!ignoreErrors)
|
||||
{
|
||||
throw new ArgumentException("Invalid padding detected.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the padding
|
||||
byte[] unpaddedData = new byte[data.Length - paddingLength];
|
||||
Array.Copy(data, unpaddedData, unpaddedData.Length);
|
||||
|
||||
return unpaddedData;
|
||||
}
|
||||
}
|
||||
}
|
755
WelsonJS.Toolkit/WelsonJS.Toolkit/Cryptography/SEED.cs
Normal file
|
@ -0,0 +1,755 @@
|
|||
/*
|
||||
* WelsonJS.Toolkit: WelsonJS native component
|
||||
*
|
||||
* filename:
|
||||
* SEED.cs
|
||||
*
|
||||
* description:
|
||||
* SEED(ISO/IEC 18033-3) cryptography algorithm implementation (Experimental)
|
||||
*
|
||||
* website:
|
||||
* - https://github.com/gnh1201/welsonjs
|
||||
* - https://catswords.social/@catswords_oss
|
||||
* - https://teams.live.com/l/community/FEACHncAhq8ldnojAI
|
||||
* - https://discord.gg/XKG5CjtXEj
|
||||
*
|
||||
* author:
|
||||
* - Namhyeon Go (@gnh1201) <abuse@catswords.net>
|
||||
* - KISA(Korea Internet & Security Agency) (kisa.or.kr)
|
||||
*
|
||||
* references:
|
||||
* - https://seed.kisa.or.kr/kisa/Board/17/detailView.do
|
||||
* - https://www.iso.org/standard/54531.html
|
||||
* - https://ics.catswords.net/SEED%2B128_Specification_english_M.pdf
|
||||
* - https://ics.catswords.net/SEED_Algorithm_Specification_korean_M.pdf
|
||||
* - https://ics.catswords.net/SEED_sourcecode_explanation_korean.pdf
|
||||
*
|
||||
* license:
|
||||
* GPLv3 or MS-RL(Microsoft Reciprocal License)
|
||||
*
|
||||
*/
|
||||
using System;
|
||||
|
||||
namespace WelsonJS.Cryptography
|
||||
{
|
||||
public class SEED
|
||||
{
|
||||
public enum ENDIAN {
|
||||
BIG,
|
||||
LITTLE
|
||||
};
|
||||
private static ENDIAN DEFAULT_ENDIAN = ENDIAN.BIG;
|
||||
|
||||
// Constants for Key schedule
|
||||
private const uint KC0 = 0x9e3779b9;
|
||||
private const uint KC1 = 0x3c6ef373;
|
||||
private const uint KC2 = 0x78dde6e6;
|
||||
private const uint KC3 = 0xf1bbcdcc;
|
||||
private const uint KC4 = 0xe3779b99;
|
||||
private const uint KC5 = 0xc6ef3733;
|
||||
private const uint KC6 = 0x8dde6e67;
|
||||
private const uint KC7 = 0x1bbcdccf;
|
||||
private const uint KC8 = 0x3779b99e;
|
||||
private const uint KC9 = 0x6ef3733c;
|
||||
private const uint KC10 = 0xdde6e678;
|
||||
private const uint KC11 = 0xbbcdccf1;
|
||||
private const uint KC12 = 0x779b99e3;
|
||||
private const uint KC13 = 0xef3733c6;
|
||||
private const uint KC14 = 0xde6e678d;
|
||||
private const uint KC15 = 0xbcdccf1b;
|
||||
|
||||
private const int ABCD_A = 0;
|
||||
private const int ABCD_B = 1;
|
||||
private const int ABCD_C = 2;
|
||||
private const int ABCD_D = 3;
|
||||
|
||||
private const int LR_L0 = 0;
|
||||
private const int LR_L1 = 1;
|
||||
private const int LR_R0 = 2;
|
||||
private const int LR_R1 = 3;
|
||||
|
||||
private const int BLOCK_SIZE_SEED = 16;
|
||||
private const int BLOCK_SIZE_SEED_INT = 4;
|
||||
|
||||
// S-BOX
|
||||
private static readonly uint[] SS0 = new uint[] {
|
||||
0x2989a1a8, 0x05858184, 0x16c6d2d4, 0x13c3d3d0, 0x14445054, 0x1d0d111c, 0x2c8ca0ac, 0x25052124,
|
||||
0x1d4d515c, 0x03434340, 0x18081018, 0x1e0e121c, 0x11415150, 0x3cccf0fc, 0x0acac2c8, 0x23436360,
|
||||
0x28082028, 0x04444044, 0x20002020, 0x1d8d919c, 0x20c0e0e0, 0x22c2e2e0, 0x08c8c0c8, 0x17071314,
|
||||
0x2585a1a4, 0x0f8f838c, 0x03030300, 0x3b4b7378, 0x3b8bb3b8, 0x13031310, 0x12c2d2d0, 0x2ecee2ec,
|
||||
0x30407070, 0x0c8c808c, 0x3f0f333c, 0x2888a0a8, 0x32023230, 0x1dcdd1dc, 0x36c6f2f4, 0x34447074,
|
||||
0x2ccce0ec, 0x15859194, 0x0b0b0308, 0x17475354, 0x1c4c505c, 0x1b4b5358, 0x3d8db1bc, 0x01010100,
|
||||
0x24042024, 0x1c0c101c, 0x33437370, 0x18889098, 0x10001010, 0x0cccc0cc, 0x32c2f2f0, 0x19c9d1d8,
|
||||
0x2c0c202c, 0x27c7e3e4, 0x32427270, 0x03838380, 0x1b8b9398, 0x11c1d1d0, 0x06868284, 0x09c9c1c8,
|
||||
0x20406060, 0x10405050, 0x2383a3a0, 0x2bcbe3e8, 0x0d0d010c, 0x3686b2b4, 0x1e8e929c, 0x0f4f434c,
|
||||
0x3787b3b4, 0x1a4a5258, 0x06c6c2c4, 0x38487078, 0x2686a2a4, 0x12021210, 0x2f8fa3ac, 0x15c5d1d4,
|
||||
0x21416160, 0x03c3c3c0, 0x3484b0b4, 0x01414140, 0x12425250, 0x3d4d717c, 0x0d8d818c, 0x08080008,
|
||||
0x1f0f131c, 0x19899198, 0x00000000, 0x19091118, 0x04040004, 0x13435350, 0x37c7f3f4, 0x21c1e1e0,
|
||||
0x3dcdf1fc, 0x36467274, 0x2f0f232c, 0x27072324, 0x3080b0b0, 0x0b8b8388, 0x0e0e020c, 0x2b8ba3a8,
|
||||
0x2282a2a0, 0x2e4e626c, 0x13839390, 0x0d4d414c, 0x29496168, 0x3c4c707c, 0x09090108, 0x0a0a0208,
|
||||
0x3f8fb3bc, 0x2fcfe3ec, 0x33c3f3f0, 0x05c5c1c4, 0x07878384, 0x14041014, 0x3ecef2fc, 0x24446064,
|
||||
0x1eced2dc, 0x2e0e222c, 0x0b4b4348, 0x1a0a1218, 0x06060204, 0x21012120, 0x2b4b6368, 0x26466264,
|
||||
0x02020200, 0x35c5f1f4, 0x12829290, 0x0a8a8288, 0x0c0c000c, 0x3383b3b0, 0x3e4e727c, 0x10c0d0d0,
|
||||
0x3a4a7278, 0x07474344, 0x16869294, 0x25c5e1e4, 0x26062224, 0x00808080, 0x2d8da1ac, 0x1fcfd3dc,
|
||||
0x2181a1a0, 0x30003030, 0x37073334, 0x2e8ea2ac, 0x36063234, 0x15051114, 0x22022220, 0x38083038,
|
||||
0x34c4f0f4, 0x2787a3a4, 0x05454144, 0x0c4c404c, 0x01818180, 0x29c9e1e8, 0x04848084, 0x17879394,
|
||||
0x35053134, 0x0bcbc3c8, 0x0ecec2cc, 0x3c0c303c, 0x31417170, 0x11011110, 0x07c7c3c4, 0x09898188,
|
||||
0x35457174, 0x3bcbf3f8, 0x1acad2d8, 0x38c8f0f8, 0x14849094, 0x19495158, 0x02828280, 0x04c4c0c4,
|
||||
0x3fcff3fc, 0x09494148, 0x39093138, 0x27476364, 0x00c0c0c0, 0x0fcfc3cc, 0x17c7d3d4, 0x3888b0b8,
|
||||
0x0f0f030c, 0x0e8e828c, 0x02424240, 0x23032320, 0x11819190, 0x2c4c606c, 0x1bcbd3d8, 0x2484a0a4,
|
||||
0x34043034, 0x31c1f1f0, 0x08484048, 0x02c2c2c0, 0x2f4f636c, 0x3d0d313c, 0x2d0d212c, 0x00404040,
|
||||
0x3e8eb2bc, 0x3e0e323c, 0x3c8cb0bc, 0x01c1c1c0, 0x2a8aa2a8, 0x3a8ab2b8, 0x0e4e424c, 0x15455154,
|
||||
0x3b0b3338, 0x1cccd0dc, 0x28486068, 0x3f4f737c, 0x1c8c909c, 0x18c8d0d8, 0x0a4a4248, 0x16465254,
|
||||
0x37477374, 0x2080a0a0, 0x2dcde1ec, 0x06464244, 0x3585b1b4, 0x2b0b2328, 0x25456164, 0x3acaf2f8,
|
||||
0x23c3e3e0, 0x3989b1b8, 0x3181b1b0, 0x1f8f939c, 0x1e4e525c, 0x39c9f1f8, 0x26c6e2e4, 0x3282b2b0,
|
||||
0x31013130, 0x2acae2e8, 0x2d4d616c, 0x1f4f535c, 0x24c4e0e4, 0x30c0f0f0, 0x0dcdc1cc, 0x08888088,
|
||||
0x16061214, 0x3a0a3238, 0x18485058, 0x14c4d0d4, 0x22426260, 0x29092128, 0x07070304, 0x33033330,
|
||||
0x28c8e0e8, 0x1b0b1318, 0x05050104, 0x39497178, 0x10809090, 0x2a4a6268, 0x2a0a2228, 0x1a8a9298
|
||||
};
|
||||
|
||||
private static readonly uint[] SS1 = new uint[]
|
||||
{
|
||||
0x38380830, 0xe828c8e0, 0x2c2d0d21, 0xa42686a2, 0xcc0fcfc3, 0xdc1eced2, 0xb03383b3, 0xb83888b0,
|
||||
0xac2f8fa3, 0x60204060, 0x54154551, 0xc407c7c3, 0x44044440, 0x6c2f4f63, 0x682b4b63, 0x581b4b53,
|
||||
0xc003c3c3, 0x60224262, 0x30330333, 0xb43585b1, 0x28290921, 0xa02080a0, 0xe022c2e2, 0xa42787a3,
|
||||
0xd013c3d3, 0x90118191, 0x10110111, 0x04060602, 0x1c1c0c10, 0xbc3c8cb0, 0x34360632, 0x480b4b43,
|
||||
0xec2fcfe3, 0x88088880, 0x6c2c4c60, 0xa82888a0, 0x14170713, 0xc404c4c0, 0x14160612, 0xf434c4f0,
|
||||
0xc002c2c2, 0x44054541, 0xe021c1e1, 0xd416c6d2, 0x3c3f0f33, 0x3c3d0d31, 0x8c0e8e82, 0x98188890,
|
||||
0x28280820, 0x4c0e4e42, 0xf436c6f2, 0x3c3e0e32, 0xa42585a1, 0xf839c9f1, 0x0c0d0d01, 0xdc1fcfd3,
|
||||
0xd818c8d0, 0x282b0b23, 0x64264662, 0x783a4a72, 0x24270723, 0x2c2f0f23, 0xf031c1f1, 0x70324272,
|
||||
0x40024242, 0xd414c4d0, 0x40014141, 0xc000c0c0, 0x70334373, 0x64274763, 0xac2c8ca0, 0x880b8b83,
|
||||
0xf437c7f3, 0xac2d8da1, 0x80008080, 0x1c1f0f13, 0xc80acac2, 0x2c2c0c20, 0xa82a8aa2, 0x34340430,
|
||||
0xd012c2d2, 0x080b0b03, 0xec2ecee2, 0xe829c9e1, 0x5c1d4d51, 0x94148490, 0x18180810, 0xf838c8f0,
|
||||
0x54174753, 0xac2e8ea2, 0x08080800, 0xc405c5c1, 0x10130313, 0xcc0dcdc1, 0x84068682, 0xb83989b1,
|
||||
0xfc3fcff3, 0x7c3d4d71, 0xc001c1c1, 0x30310131, 0xf435c5f1, 0x880a8a82, 0x682a4a62, 0xb03181b1,
|
||||
0xd011c1d1, 0x20200020, 0xd417c7d3, 0x00020202, 0x20220222, 0x04040400, 0x68284860, 0x70314171,
|
||||
0x04070703, 0xd81bcbd3, 0x9c1d8d91, 0x98198991, 0x60214161, 0xbc3e8eb2, 0xe426c6e2, 0x58194951,
|
||||
0xdc1dcdd1, 0x50114151, 0x90108090, 0xdc1cccd0, 0x981a8a92, 0xa02383a3, 0xa82b8ba3, 0xd010c0d0,
|
||||
0x80018181, 0x0c0f0f03, 0x44074743, 0x181a0a12, 0xe023c3e3, 0xec2ccce0, 0x8c0d8d81, 0xbc3f8fb3,
|
||||
0x94168692, 0x783b4b73, 0x5c1c4c50, 0xa02282a2, 0xa02181a1, 0x60234363, 0x20230323, 0x4c0d4d41,
|
||||
0xc808c8c0, 0x9c1e8e92, 0x9c1c8c90, 0x383a0a32, 0x0c0c0c00, 0x2c2e0e22, 0xb83a8ab2, 0x6c2e4e62,
|
||||
0x9c1f8f93, 0x581a4a52, 0xf032c2f2, 0x90128292, 0xf033c3f3, 0x48094941, 0x78384870, 0xcc0cccc0,
|
||||
0x14150511, 0xf83bcbf3, 0x70304070, 0x74354571, 0x7c3f4f73, 0x34350531, 0x10100010, 0x00030303,
|
||||
0x64244460, 0x6c2d4d61, 0xc406c6c2, 0x74344470, 0xd415c5d1, 0xb43484b0, 0xe82acae2, 0x08090901,
|
||||
0x74364672, 0x18190911, 0xfc3ecef2, 0x40004040, 0x10120212, 0xe020c0e0, 0xbc3d8db1, 0x04050501,
|
||||
0xf83acaf2, 0x00010101, 0xf030c0f0, 0x282a0a22, 0x5c1e4e52, 0xa82989a1, 0x54164652, 0x40034343,
|
||||
0x84058581, 0x14140410, 0x88098981, 0x981b8b93, 0xb03080b0, 0xe425c5e1, 0x48084840, 0x78394971,
|
||||
0x94178793, 0xfc3cccf0, 0x1c1e0e12, 0x80028282, 0x20210121, 0x8c0c8c80, 0x181b0b13, 0x5c1f4f53,
|
||||
0x74374773, 0x54144450, 0xb03282b2, 0x1c1d0d11, 0x24250521, 0x4c0f4f43, 0x00000000, 0x44064642,
|
||||
0xec2dcde1, 0x58184850, 0x50124252, 0xe82bcbe3, 0x7c3e4e72, 0xd81acad2, 0xc809c9c1, 0xfc3dcdf1,
|
||||
0x30300030, 0x94158591, 0x64254561, 0x3c3c0c30, 0xb43686b2, 0xe424c4e0, 0xb83b8bb3, 0x7c3c4c70,
|
||||
0x0c0e0e02, 0x50104050, 0x38390931, 0x24260622, 0x30320232, 0x84048480, 0x68294961, 0x90138393,
|
||||
0x34370733, 0xe427c7e3, 0x24240420, 0xa42484a0, 0xc80bcbc3, 0x50134353, 0x080a0a02, 0x84078783,
|
||||
0xd819c9d1, 0x4c0c4c40, 0x80038383, 0x8c0f8f83, 0xcc0ecec2, 0x383b0b33, 0x480a4a42, 0xb43787b3
|
||||
};
|
||||
|
||||
private static readonly uint[] SS2 = new uint[]
|
||||
{
|
||||
0xa1a82989, 0x81840585, 0xd2d416c6, 0xd3d013c3, 0x50541444, 0x111c1d0d, 0xa0ac2c8c, 0x21242505,
|
||||
0x515c1d4d, 0x43400343, 0x10181808, 0x121c1e0e, 0x51501141, 0xf0fc3ccc, 0xc2c80aca, 0x63602343,
|
||||
0x20282808, 0x40440444, 0x20202000, 0x919c1d8d, 0xe0e020c0, 0xe2e022c2, 0xc0c808c8, 0x13141707,
|
||||
0xa1a42585, 0x838c0f8f, 0x03000303, 0x73783b4b, 0xb3b83b8b, 0x13101303, 0xd2d012c2, 0xe2ec2ece,
|
||||
0x70703040, 0x808c0c8c, 0x333c3f0f, 0xa0a82888, 0x32303202, 0xd1dc1dcd, 0xf2f436c6, 0x70743444,
|
||||
0xe0ec2ccc, 0x91941585, 0x03080b0b, 0x53541747, 0x505c1c4c, 0x53581b4b, 0xb1bc3d8d, 0x01000101,
|
||||
0x20242404, 0x101c1c0c, 0x73703343, 0x90981888, 0x10101000, 0xc0cc0ccc, 0xf2f032c2, 0xd1d819c9,
|
||||
0x202c2c0c, 0xe3e427c7, 0x72703242, 0x83800383, 0x93981b8b, 0xd1d011c1, 0x82840686, 0xc1c809c9,
|
||||
0x60602040, 0x50501040, 0xa3a02383, 0xe3e82bcb, 0x010c0d0d, 0xb2b43686, 0x929c1e8e, 0x434c0f4f,
|
||||
0xb3b43787, 0x52581a4a, 0xc2c406c6, 0x70783848, 0xa2a42686, 0x12101202, 0xa3ac2f8f, 0xd1d415c5,
|
||||
0x61602141, 0xc3c003c3, 0xb0b43484, 0x41400141, 0x52501242, 0x717c3d4d, 0x818c0d8d, 0x00080808,
|
||||
0x131c1f0f, 0x91981989, 0x00000000, 0x11181909, 0x00040404, 0x53501343, 0xf3f437c7, 0xe1e021c1,
|
||||
0xf1fc3dcd, 0x72743646, 0x232c2f0f, 0x23242707, 0xb0b03080, 0x83880b8b, 0x020c0e0e, 0xa3a82b8b,
|
||||
0xa2a02282, 0x626c2e4e, 0x93901383, 0x414c0d4d, 0x61682949, 0x707c3c4c, 0x01080909, 0x02080a0a,
|
||||
0xb3bc3f8f, 0xe3ec2fcf, 0xf3f033c3, 0xc1c405c5, 0x83840787, 0x10141404, 0xf2fc3ece, 0x60642444,
|
||||
0xd2dc1ece, 0x222c2e0e, 0x43480b4b, 0x12181a0a, 0x02040606, 0x21202101, 0x63682b4b, 0x62642646,
|
||||
0x02000202, 0xf1f435c5, 0x92901282, 0x82880a8a, 0x000c0c0c, 0xb3b03383, 0x727c3e4e, 0xd0d010c0,
|
||||
0x72783a4a, 0x43440747, 0x92941686, 0xe1e425c5, 0x22242606, 0x80800080, 0xa1ac2d8d, 0xd3dc1fcf,
|
||||
0xa1a02181, 0x30303000, 0x33343707, 0xa2ac2e8e, 0x32343606, 0x11141505, 0x22202202, 0x30383808,
|
||||
0xf0f434c4, 0xa3a42787, 0x41440545, 0x404c0c4c, 0x81800181, 0xe1e829c9, 0x80840484, 0x93941787,
|
||||
0x31343505, 0xc3c80bcb, 0xc2cc0ece, 0x303c3c0c, 0x71703141, 0x11101101, 0xc3c407c7, 0x81880989,
|
||||
0x71743545, 0xf3f83bcb, 0xd2d81aca, 0xf0f838c8, 0x90941484, 0x51581949, 0x82800282, 0xc0c404c4,
|
||||
0xf3fc3fcf, 0x41480949, 0x31383909, 0x63642747, 0xc0c000c0, 0xc3cc0fcf, 0xd3d417c7, 0xb0b83888,
|
||||
0x030c0f0f, 0x828c0e8e, 0x42400242, 0x23202303, 0x91901181, 0x606c2c4c, 0xd3d81bcb, 0xa0a42484,
|
||||
0x30343404, 0xf1f031c1, 0x40480848, 0xc2c002c2, 0x636c2f4f, 0x313c3d0d, 0x212c2d0d, 0x40400040,
|
||||
0xb2bc3e8e, 0x323c3e0e, 0xb0bc3c8c, 0xc1c001c1, 0xa2a82a8a, 0xb2b83a8a, 0x424c0e4e, 0x51541545,
|
||||
0x33383b0b, 0xd0dc1ccc, 0x60682848, 0x737c3f4f, 0x909c1c8c, 0xd0d818c8, 0x42480a4a, 0x52541646,
|
||||
0x73743747, 0xa0a02080, 0xe1ec2dcd, 0x42440646, 0xb1b43585, 0x23282b0b, 0x61642545, 0xf2f83aca,
|
||||
0xe3e023c3, 0xb1b83989, 0xb1b03181, 0x939c1f8f, 0x525c1e4e, 0xf1f839c9, 0xe2e426c6, 0xb2b03282,
|
||||
0x31303101, 0xe2e82aca, 0x616c2d4d, 0x535c1f4f, 0xe0e424c4, 0xf0f030c0, 0xc1cc0dcd, 0x80880888,
|
||||
0x12141606, 0x32383a0a, 0x50581848, 0xd0d414c4, 0x62602242, 0x21282909, 0x03040707, 0x33303303,
|
||||
0xe0e828c8, 0x13181b0b, 0x01040505, 0x71783949, 0x90901080, 0x62682a4a, 0x22282a0a, 0x92981a8a
|
||||
};
|
||||
|
||||
private static uint[] SS3 = new uint[]
|
||||
{
|
||||
0x08303838, 0xc8e0e828, 0x0d212c2d, 0x86a2a426, 0xcfc3cc0f, 0xced2dc1e, 0x83b3b033, 0x88b0b838,
|
||||
0x8fa3ac2f, 0x40606020, 0x45515415, 0xc7c3c407, 0x44404404, 0x4f636c2f, 0x4b63682b, 0x4b53581b,
|
||||
0xc3c3c003, 0x42626022, 0x03333033, 0x85b1b435, 0x09212829, 0x80a0a020, 0xc2e2e022, 0x87a3a427,
|
||||
0xc3d3d013, 0x81919011, 0x01111011, 0x06020406, 0x0c101c1c, 0x8cb0bc3c, 0x06323436, 0x4b43480b,
|
||||
0xcfe3ec2f, 0x88808808, 0x4c606c2c, 0x88a0a828, 0x07131417, 0xc4c0c404, 0x06121416, 0xc4f0f434,
|
||||
0xc2c2c002, 0x45414405, 0xc1e1e021, 0xc6d2d416, 0x0f333c3f, 0x0d313c3d, 0x8e828c0e, 0x88909818,
|
||||
0x08202828, 0x4e424c0e, 0xc6f2f436, 0x0e323c3e, 0x85a1a425, 0xc9f1f839, 0x0d010c0d, 0xcfd3dc1f,
|
||||
0xc8d0d818, 0x0b23282b, 0x46626426, 0x4a72783a, 0x07232427, 0x0f232c2f, 0xc1f1f031, 0x42727032,
|
||||
0x42424002, 0xc4d0d414, 0x41414001, 0xc0c0c000, 0x43737033, 0x47636427, 0x8ca0ac2c, 0x8b83880b,
|
||||
0xc7f3f437, 0x8da1ac2d, 0x80808000, 0x0f131c1f, 0xcac2c80a, 0x0c202c2c, 0x8aa2a82a, 0x04303434,
|
||||
0xc2d2d012, 0x0b03080b, 0xcee2ec2e, 0xc9e1e829, 0x4d515c1d, 0x84909414, 0x08101818, 0xc8f0f838,
|
||||
0x47535417, 0x8ea2ac2e, 0x08000808, 0xc5c1c405, 0x03131013, 0xcdc1cc0d, 0x86828406, 0x89b1b839,
|
||||
0xcff3fc3f, 0x4d717c3d, 0xc1c1c001, 0x01313031, 0xc5f1f435, 0x8a82880a, 0x4a62682a, 0x81b1b031,
|
||||
0xc1d1d011, 0x00202020, 0xc7d3d417, 0x02020002, 0x02222022, 0x04000404, 0x48606828, 0x41717031,
|
||||
0x07030407, 0xcbd3d81b, 0x8d919c1d, 0x89919819, 0x41616021, 0x8eb2bc3e, 0xc6e2e426, 0x49515819,
|
||||
0xcdd1dc1d, 0x41515011, 0x80909010, 0xccd0dc1c, 0x8a92981a, 0x83a3a023, 0x8ba3a82b, 0xc0d0d010,
|
||||
0x81818001, 0x0f030c0f, 0x47434407, 0x0a12181a, 0xc3e3e023, 0xcce0ec2c, 0x8d818c0d, 0x8fb3bc3f,
|
||||
0x86929416, 0x4b73783b, 0x4c505c1c, 0x82a2a022, 0x81a1a021, 0x43636023, 0x03232023, 0x4d414c0d,
|
||||
0xc8c0c808, 0x8e929c1e, 0x8c909c1c, 0x0a32383a, 0x0c000c0c, 0x0e222c2e, 0x8ab2b83a, 0x4e626c2e,
|
||||
0x8f939c1f, 0x4a52581a, 0xc2f2f032, 0x82929012, 0xc3f3f033, 0x49414809, 0x48707838, 0xccc0cc0c,
|
||||
0x05111415, 0xcbf3f83b, 0x40707030, 0x45717435, 0x4f737c3f, 0x05313435, 0x00101010, 0x03030003,
|
||||
0x44606424, 0x4d616c2d, 0xc6c2c406, 0x44707434, 0xc5d1d415, 0x84b0b434, 0xcae2e82a, 0x09010809,
|
||||
0x46727436, 0x09111819, 0xcef2fc3e, 0x40404000, 0x02121012, 0xc0e0e020, 0x8db1bc3d, 0x05010405,
|
||||
0xcaf2f83a, 0x01010001, 0xc0f0f030, 0x0a22282a, 0x4e525c1e, 0x89a1a829, 0x46525416, 0x43434003,
|
||||
0x85818405, 0x04101414, 0x89818809, 0x8b93981b, 0x80b0b030, 0xc5e1e425, 0x48404808, 0x49717839,
|
||||
0x87939417, 0xccf0fc3c, 0x0e121c1e, 0x82828002, 0x01212021, 0x8c808c0c, 0x0b13181b, 0x4f535c1f,
|
||||
0x47737437, 0x44505414, 0x82b2b032, 0x0d111c1d, 0x05212425, 0x4f434c0f, 0x00000000, 0x46424406,
|
||||
0xcde1ec2d, 0x48505818, 0x42525012, 0xcbe3e82b, 0x4e727c3e, 0xcad2d81a, 0xc9c1c809, 0xcdf1fc3d,
|
||||
0x00303030, 0x85919415, 0x45616425, 0x0c303c3c, 0x86b2b436, 0xc4e0e424, 0x8bb3b83b, 0x4c707c3c,
|
||||
0x0e020c0e, 0x40505010, 0x09313839, 0x06222426, 0x02323032, 0x84808404, 0x49616829, 0x83939013,
|
||||
0x07333437, 0xc7e3e427, 0x04202424, 0x84a0a424, 0xcbc3c80b, 0x43535013, 0x0a02080a, 0x87838407,
|
||||
0xc9d1d819, 0x4c404c0c, 0x83838003, 0x8f838c0f, 0xcec2cc0e, 0x0b33383b, 0x4a42480a, 0x87b3b437
|
||||
};
|
||||
|
||||
private static uint GetUIntFromByteArray(byte[] src, int srcOffset, ENDIAN endian = ENDIAN.BIG)
|
||||
{
|
||||
if (src == null || src.Length < srcOffset + 4)
|
||||
throw new ArgumentException("Invalid source array or offset.");
|
||||
|
||||
return (uint)(endian == ENDIAN.BIG
|
||||
? (src[srcOffset] & 0xFF) << 24 |
|
||||
(src[srcOffset + 1] & 0xFF) << 16 |
|
||||
(src[srcOffset + 2] & 0xFF) << 8 |
|
||||
(src[srcOffset + 3] & 0xFF)
|
||||
: (src[srcOffset] & 0xFF) |
|
||||
(src[srcOffset + 1] & 0xFF) << 8 |
|
||||
(src[srcOffset + 2] & 0xFF) << 16 |
|
||||
(src[srcOffset + 3] & 0xFF) << 24);
|
||||
}
|
||||
|
||||
private static byte GetByteFromUInt(uint value, int position)
|
||||
{
|
||||
if (position < 0 || position >= 4)
|
||||
throw new ArgumentException("Position must be between 0 and 3.");
|
||||
|
||||
return position > 0
|
||||
? (byte)((value >> (8 * position)) & 0xFF)
|
||||
: (byte)(value & 0xFF);
|
||||
}
|
||||
|
||||
public static void SetByteToUIntArray(ref uint[] dst, int byteOffset, byte value, ENDIAN endian)
|
||||
{
|
||||
if (dst == null || byteOffset < 0 || byteOffset >= dst.Length * 4)
|
||||
throw new ArgumentOutOfRangeException(nameof(byteOffset), "Invalid byte offset.");
|
||||
|
||||
int uintIndex = byteOffset / 4;
|
||||
int shiftValue = (endian == ENDIAN.BIG)
|
||||
? (3 - byteOffset % 4) * 8
|
||||
: (byteOffset % 4) * 8;
|
||||
|
||||
uint maskValue = (uint)(0xFF << shiftValue);
|
||||
uint maskValue2 = ~maskValue;
|
||||
uint valueToSet = (uint)(value & 0xFF) << shiftValue;
|
||||
|
||||
dst[uintIndex] = (dst[uintIndex] & maskValue2) | (valueToSet & maskValue);
|
||||
}
|
||||
|
||||
public static byte GetByteFromUIntArray(uint[] src, int byteOffset, ENDIAN endian)
|
||||
{
|
||||
if (src == null || byteOffset < 0 || byteOffset >= src.Length * 4)
|
||||
throw new ArgumentOutOfRangeException(nameof(byteOffset), "Invalid byte offset.");
|
||||
|
||||
int uintIndex = byteOffset / 4;
|
||||
int shiftValue = (endian == ENDIAN.BIG)
|
||||
? (3 - byteOffset % 4) * 8
|
||||
: (byteOffset % 4) * 8;
|
||||
|
||||
return (byte)((src[uintIndex] >> shiftValue) & 0xFF);
|
||||
}
|
||||
|
||||
private static uint[] ConvertToUIntArray(byte[] src, int inLen, ENDIAN endian = ENDIAN.BIG)
|
||||
{
|
||||
if (src == null)
|
||||
throw new ArgumentNullException(nameof(src), "source cannot be null.");
|
||||
|
||||
if (inLen <= 0 || inLen > src.Length)
|
||||
throw new ArgumentException("Invalid input length.", nameof(inLen));
|
||||
|
||||
int outLen = (inLen + 3) / 4;
|
||||
uint[] data = new uint[outLen];
|
||||
|
||||
for (int i = 0; i < outLen; i++)
|
||||
{
|
||||
data[i] = GetUIntFromByteArray(src, i * 4, endian);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private static byte[] ConvertToByteArray(uint[] src, int inLen, ENDIAN endian = ENDIAN.BIG)
|
||||
{
|
||||
if (src == null)
|
||||
throw new ArgumentNullException(nameof(src), "source cannot be null.");
|
||||
|
||||
if (inLen <= 0 || inLen > src.Length)
|
||||
throw new ArgumentException("Invalid input length.", nameof(inLen));
|
||||
|
||||
int outLen = inLen;
|
||||
byte[] data = new byte[outLen];
|
||||
|
||||
for (int i = 0; i < outLen; i++)
|
||||
{
|
||||
data[i] = GetByteFromUIntArray(src, i, endian);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private static uint Substitute(uint value)
|
||||
{
|
||||
return SS0[GetByteFromUInt(value, 0)] ^
|
||||
SS1[GetByteFromUInt(value, 1)] ^
|
||||
SS2[GetByteFromUInt(value, 2)] ^
|
||||
SS3[GetByteFromUInt(value, 3)];
|
||||
}
|
||||
|
||||
// Round function F and adding output of F to L.
|
||||
// L0, L1 : left input values at each round
|
||||
// R0, R1 : right input values at each round
|
||||
// K : round keys at each round
|
||||
private static void SeedRound(ref uint[] T, ref uint[] LR, int L0, int L1, int R0, int R1, uint[] K, int K_offset)
|
||||
{
|
||||
T[0] = LR[R0] ^ K[K_offset + 0];
|
||||
T[1] = LR[R1] ^ K[K_offset + 1];
|
||||
T[1] ^= T[0];
|
||||
T[1] = Substitute(T[1]);
|
||||
T[0] += T[1];
|
||||
T[0] = Substitute(T[0]);
|
||||
T[1] += T[0];
|
||||
T[1] = Substitute(T[1]);
|
||||
T[0] += T[1];
|
||||
LR[L0] ^= T[0]; LR[L1] ^= T[1];
|
||||
}
|
||||
|
||||
private static uint EndianChange(uint value)
|
||||
{
|
||||
return ((value & 0x000000FF) << 24) | // Move the first byte to the fourth byte position
|
||||
((value & 0x0000FF00) << 8) | // Move the second byte to the third byte position
|
||||
((value & 0x00FF0000) >> 8) | // Move the third byte to the second byte position
|
||||
((value & 0xFF000000) >> 24); // Move the fourth byte to the first byte position
|
||||
}
|
||||
|
||||
private static void RoundKeyUpdate0(ref uint[] T, ref uint[] K, int K_offset, ref uint[] ABCD, uint KC)
|
||||
{
|
||||
T[0] = ABCD[ABCD_A] + ABCD[ABCD_C] - KC;
|
||||
T[1] = ABCD[ABCD_B] + KC - ABCD[ABCD_D];
|
||||
K[K_offset + 0] = Substitute(T[0]);
|
||||
K[K_offset + 1] = Substitute(T[1]);
|
||||
T[0] = ABCD[ABCD_A];
|
||||
ABCD[ABCD_A] = ((ABCD[ABCD_A] >> 8) & 0x00ffffff) ^ (ABCD[ABCD_B] << 24);
|
||||
ABCD[ABCD_B] = ((ABCD[ABCD_B] >> 8) & 0x00ffffff) ^ (T[0] << 24);
|
||||
}
|
||||
|
||||
private static void RoundKeyUpdate1(ref uint[] T, ref uint[] K, int K_offset, ref uint[] ABCD, uint KC)
|
||||
{
|
||||
T[0] = ABCD[ABCD_A] + ABCD[ABCD_C] - KC;
|
||||
T[1] = ABCD[ABCD_B] + KC - ABCD[ABCD_D];
|
||||
K[K_offset + 0] = Substitute(T[0]);
|
||||
K[K_offset + 1] = Substitute(T[1]);
|
||||
T[0] = ABCD[ABCD_C];
|
||||
ABCD[ABCD_C] = (ABCD[ABCD_C] << 8) ^ ((ABCD[ABCD_D] >> 24) & 0x000000ff);
|
||||
ABCD[ABCD_D] = (ABCD[ABCD_D] << 8) ^ ((T[0] >> 24) & 0x000000ff);
|
||||
}
|
||||
|
||||
public class ECB
|
||||
{
|
||||
public void EncryptBlock(in uint[] _in, int in_offset, ref uint[] _out, int out_offset, KISA_SEED_KEY ks)
|
||||
{
|
||||
uint[] LR = new uint[4]; // Iuput/output values at each rounds
|
||||
uint[] T = new uint[2]; // Temporary variables for round function F
|
||||
uint[] K = ks.key_data; // Pointer of round keys
|
||||
|
||||
// Set up input values for first round
|
||||
LR[LR_L0] = _in[in_offset + 0];
|
||||
LR[LR_L1] = _in[in_offset + 1];
|
||||
LR[LR_R0] = _in[in_offset + 2];
|
||||
LR[LR_R1] = _in[in_offset + 3];
|
||||
|
||||
// Reorder for big endian
|
||||
// Because SEED use little endian order in default
|
||||
if (ENDIAN.BIG != DEFAULT_ENDIAN)
|
||||
{
|
||||
LR[LR_L0] = EndianChange(LR[LR_L0]);
|
||||
LR[LR_L1] = EndianChange(LR[LR_L1]);
|
||||
LR[LR_R0] = EndianChange(LR[LR_R0]);
|
||||
LR[LR_R1] = EndianChange(LR[LR_R1]);
|
||||
}
|
||||
|
||||
SeedRound(ref T, ref LR, LR_L0, LR_L1, LR_R0, LR_R1, K, 0); // Round 1
|
||||
SeedRound(ref T, ref LR, LR_R0, LR_R1, LR_L0, LR_L1, K, 2); // Round 2
|
||||
SeedRound(ref T, ref LR, LR_L0, LR_L1, LR_R0, LR_R1, K, 4); // Round 3
|
||||
SeedRound(ref T, ref LR, LR_R0, LR_R1, LR_L0, LR_L1, K, 6); // Round 4
|
||||
SeedRound(ref T, ref LR, LR_L0, LR_L1, LR_R0, LR_R1, K, 8); // Round 5
|
||||
SeedRound(ref T, ref LR, LR_R0, LR_R1, LR_L0, LR_L1, K, 10); // Round 6
|
||||
SeedRound(ref T, ref LR, LR_L0, LR_L1, LR_R0, LR_R1, K, 12); // Round 7
|
||||
SeedRound(ref T, ref LR, LR_R0, LR_R1, LR_L0, LR_L1, K, 14); // Round 8
|
||||
SeedRound(ref T, ref LR, LR_L0, LR_L1, LR_R0, LR_R1, K, 16); // Round 9
|
||||
SeedRound(ref T, ref LR, LR_R0, LR_R1, LR_L0, LR_L1, K, 18); // Round 10
|
||||
SeedRound(ref T, ref LR, LR_L0, LR_L1, LR_R0, LR_R1, K, 20); // Round 11
|
||||
SeedRound(ref T, ref LR, LR_R0, LR_R1, LR_L0, LR_L1, K, 22); // Round 12
|
||||
SeedRound(ref T, ref LR, LR_L0, LR_L1, LR_R0, LR_R1, K, 24); // Round 13
|
||||
SeedRound(ref T, ref LR, LR_R0, LR_R1, LR_L0, LR_L1, K, 26); // Round 14
|
||||
SeedRound(ref T, ref LR, LR_L0, LR_L1, LR_R0, LR_R1, K, 28); // Round 15
|
||||
SeedRound(ref T, ref LR, LR_R0, LR_R1, LR_L0, LR_L1, K, 30); // Round 16
|
||||
|
||||
if (ENDIAN.BIG != DEFAULT_ENDIAN)
|
||||
{
|
||||
LR[LR_L0] = EndianChange(LR[LR_L0]);
|
||||
LR[LR_L1] = EndianChange(LR[LR_L1]);
|
||||
LR[LR_R0] = EndianChange(LR[LR_R0]);
|
||||
LR[LR_R1] = EndianChange(LR[LR_R1]);
|
||||
}
|
||||
|
||||
// Copying output values from last round to pbData
|
||||
_out[out_offset + 0] = LR[LR_R0];
|
||||
_out[out_offset + 1] = LR[LR_R1];
|
||||
_out[out_offset + 2] = LR[LR_L0];
|
||||
_out[out_offset + 3] = LR[LR_L1];
|
||||
}
|
||||
|
||||
public void DecryptBlock(in uint[] _in, int in_offset, ref uint[] _out, int out_offset, KISA_SEED_KEY ks)
|
||||
{
|
||||
uint[] LR = new uint[4]; // Iuput/output values at each rounds
|
||||
uint[] T = new uint[2]; // Temporary variables for round function F
|
||||
uint[] K = ks.key_data; // Pointer of round keys
|
||||
|
||||
// Set up input values for first round
|
||||
LR[LR_L0] = _in[in_offset + 0];
|
||||
LR[LR_L1] = _in[in_offset + 1];
|
||||
LR[LR_R0] = _in[in_offset + 2];
|
||||
LR[LR_R1] = _in[in_offset + 3];
|
||||
|
||||
// Reorder for big endian
|
||||
if (ENDIAN.BIG != DEFAULT_ENDIAN)
|
||||
{
|
||||
LR[LR_L0] = EndianChange(LR[LR_L0]);
|
||||
LR[LR_L1] = EndianChange(LR[LR_L1]);
|
||||
LR[LR_R0] = EndianChange(LR[LR_R0]);
|
||||
LR[LR_R1] = EndianChange(LR[LR_R1]);
|
||||
}
|
||||
|
||||
SeedRound(ref T, ref LR, LR_L0, LR_L1, LR_R0, LR_R1, K, 30); // Round 1
|
||||
SeedRound(ref T, ref LR, LR_R0, LR_R1, LR_L0, LR_L1, K, 28); // Round 2
|
||||
SeedRound(ref T, ref LR, LR_L0, LR_L1, LR_R0, LR_R1, K, 26); // Round 3
|
||||
SeedRound(ref T, ref LR, LR_R0, LR_R1, LR_L0, LR_L1, K, 24); // Round 4
|
||||
SeedRound(ref T, ref LR, LR_L0, LR_L1, LR_R0, LR_R1, K, 22); // Round 5
|
||||
SeedRound(ref T, ref LR, LR_R0, LR_R1, LR_L0, LR_L1, K, 20); // Round 6
|
||||
SeedRound(ref T, ref LR, LR_L0, LR_L1, LR_R0, LR_R1, K, 18); // Round 7
|
||||
SeedRound(ref T, ref LR, LR_R0, LR_R1, LR_L0, LR_L1, K, 16); // Round 8
|
||||
SeedRound(ref T, ref LR, LR_L0, LR_L1, LR_R0, LR_R1, K, 14); // Round 9
|
||||
SeedRound(ref T, ref LR, LR_R0, LR_R1, LR_L0, LR_L1, K, 12); // Round 10
|
||||
SeedRound(ref T, ref LR, LR_L0, LR_L1, LR_R0, LR_R1, K, 10); // Round 11
|
||||
SeedRound(ref T, ref LR, LR_R0, LR_R1, LR_L0, LR_L1, K, 8); // Round 12
|
||||
SeedRound(ref T, ref LR, LR_L0, LR_L1, LR_R0, LR_R1, K, 6); // Round 13
|
||||
SeedRound(ref T, ref LR, LR_R0, LR_R1, LR_L0, LR_L1, K, 4); // Round 14
|
||||
SeedRound(ref T, ref LR, LR_L0, LR_L1, LR_R0, LR_R1, K, 2); // Round 15
|
||||
SeedRound(ref T, ref LR, LR_R0, LR_R1, LR_L0, LR_L1, K, 0); // Round 16
|
||||
|
||||
if (ENDIAN.BIG != DEFAULT_ENDIAN)
|
||||
{
|
||||
LR[LR_L0] = EndianChange(LR[LR_L0]);
|
||||
LR[LR_L1] = EndianChange(LR[LR_L1]);
|
||||
LR[LR_R0] = EndianChange(LR[LR_R0]);
|
||||
LR[LR_R1] = EndianChange(LR[LR_R1]);
|
||||
}
|
||||
|
||||
// Copy output values from last round to pbData
|
||||
_out[out_offset + 0] = LR[LR_R0];
|
||||
_out[out_offset + 1] = LR[LR_R1];
|
||||
_out[out_offset + 2] = LR[LR_L0];
|
||||
_out[out_offset + 3] = LR[LR_L1];
|
||||
}
|
||||
|
||||
public void Init(KISA_SEED_INFO pInfo, KISA_ENC_DEC enc, byte[] pbszUserKey)
|
||||
{
|
||||
uint[] ABCD = new uint[4]; // Iuput/output values at each rounds
|
||||
uint[] T = new uint[2]; // Temporary variable
|
||||
uint[] K;
|
||||
|
||||
if (pInfo == null || pbszUserKey == null)
|
||||
throw new ArgumentException("Invalid arguments");
|
||||
|
||||
K = pInfo.seed_key.key_data; // Pointer of round keys
|
||||
|
||||
pInfo.encrypt = enc.value;
|
||||
pInfo.last_block_flag = pInfo.buffer_length = 0;
|
||||
|
||||
// Set up input values for Key Schedule
|
||||
ABCD[ABCD_A] = GetUIntFromByteArray(pbszUserKey, 0 * 4, DEFAULT_ENDIAN);
|
||||
ABCD[ABCD_B] = GetUIntFromByteArray(pbszUserKey, 1 * 4, DEFAULT_ENDIAN);
|
||||
ABCD[ABCD_C] = GetUIntFromByteArray(pbszUserKey, 2 * 4, DEFAULT_ENDIAN);
|
||||
ABCD[ABCD_D] = GetUIntFromByteArray(pbszUserKey, 3 * 4, DEFAULT_ENDIAN);
|
||||
|
||||
// Reorder for big endian
|
||||
if (ENDIAN.BIG != DEFAULT_ENDIAN)
|
||||
{
|
||||
ABCD[ABCD_A] = EndianChange(ABCD[ABCD_A]);
|
||||
ABCD[ABCD_B] = EndianChange(ABCD[ABCD_B]);
|
||||
ABCD[ABCD_C] = EndianChange(ABCD[ABCD_C]);
|
||||
ABCD[ABCD_D] = EndianChange(ABCD[ABCD_D]);
|
||||
}
|
||||
|
||||
// i-th round keys( K_i,0 and K_i,1 ) are denoted as K[2*(i-1)] and K[2*i-1], respectively
|
||||
RoundKeyUpdate0(ref T, ref K, 0, ref ABCD, KC0); // K_1,0 and K_1,1
|
||||
RoundKeyUpdate1(ref T, ref K, 2, ref ABCD, KC1); // K_2,0 and K_2,1
|
||||
RoundKeyUpdate0(ref T, ref K, 4, ref ABCD, KC2); // K_3,0 and K_3,1
|
||||
RoundKeyUpdate1(ref T, ref K, 6, ref ABCD, KC3); // K_4,0 and K_4,1
|
||||
RoundKeyUpdate0(ref T, ref K, 8, ref ABCD, KC4); // K_5,0 and K_5,1
|
||||
RoundKeyUpdate1(ref T, ref K, 10, ref ABCD, KC5); // K_6,0 and K_6,1
|
||||
RoundKeyUpdate0(ref T, ref K, 12, ref ABCD, KC6); // K_7,0 and K_7,1
|
||||
RoundKeyUpdate1(ref T, ref K, 14, ref ABCD, KC7); // K_8,0 and K_8,1
|
||||
RoundKeyUpdate0(ref T, ref K, 16, ref ABCD, KC8); // K_9,0 and K_9,1
|
||||
RoundKeyUpdate1(ref T, ref K, 18, ref ABCD, KC9); // K_10,0 and K_10,1
|
||||
RoundKeyUpdate0(ref T, ref K, 20, ref ABCD, KC10); // K_11,0 and K_11,1
|
||||
RoundKeyUpdate1(ref T, ref K, 22, ref ABCD, KC11); // K_12,0 and K_12,1
|
||||
RoundKeyUpdate0(ref T, ref K, 24, ref ABCD, KC12); // K_13,0 and K_13,1
|
||||
RoundKeyUpdate1(ref T, ref K, 26, ref ABCD, KC13); // K_14,0 and K_14,1
|
||||
RoundKeyUpdate0(ref T, ref K, 28, ref ABCD, KC14); // K_15,0 and K_15,1
|
||||
|
||||
T[0] = ABCD[ABCD_A] + ABCD[ABCD_C] - KC15;
|
||||
T[1] = ABCD[ABCD_B] - ABCD[ABCD_D] + KC15;
|
||||
|
||||
K[30] = SS0[GetByteFromUInt(T[0], 0) & 0x0ff] ^ SS1[GetByteFromUInt(T[0], 1) & 0x0ff] ^ // K_16,0
|
||||
SS2[GetByteFromUInt(T[0], 2) & 0x0ff] ^ SS3[GetByteFromUInt(T[0], 3) & 0x0ff];
|
||||
K[31] = SS0[GetByteFromUInt(T[1], 0) & 0x0ff] ^ SS1[GetByteFromUInt(T[1], 1) & 0x0ff] ^ // K_16,1
|
||||
SS2[GetByteFromUInt(T[1], 2) & 0x0ff] ^ SS3[GetByteFromUInt(T[1], 3) & 0x0ff];
|
||||
}
|
||||
|
||||
public void Process(KISA_SEED_INFO pInfo, in uint[] _in, int inLen, ref uint[] _out, ref int[] outLen)
|
||||
{
|
||||
int nCurrentCount = BLOCK_SIZE_SEED;
|
||||
int in_offset = 0;
|
||||
int out_offset = 0;
|
||||
|
||||
if (pInfo == null || _in == null || _out == null || inLen <= 0)
|
||||
throw new ArgumentException("Invalid arguments.");
|
||||
|
||||
if (KISA_ENC_DEC._KISA_ENCRYPT == pInfo.encrypt)
|
||||
{
|
||||
while (nCurrentCount <= inLen)
|
||||
{
|
||||
EncryptBlock(_in, in_offset, ref _out, out_offset, pInfo.seed_key);
|
||||
nCurrentCount += BLOCK_SIZE_SEED;
|
||||
in_offset += BLOCK_SIZE_SEED_INT;
|
||||
out_offset += BLOCK_SIZE_SEED_INT;
|
||||
}
|
||||
|
||||
outLen[0] = nCurrentCount - BLOCK_SIZE_SEED;
|
||||
pInfo.buffer_length = inLen - outLen[0];
|
||||
Array.Copy(_in, in_offset, pInfo.ecb_buffer, 0, pInfo.buffer_length);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (nCurrentCount <= inLen)
|
||||
{
|
||||
DecryptBlock(_in, in_offset, ref _out, out_offset, pInfo.seed_key);
|
||||
nCurrentCount += BLOCK_SIZE_SEED;
|
||||
in_offset += BLOCK_SIZE_SEED_INT;
|
||||
out_offset += BLOCK_SIZE_SEED_INT;
|
||||
}
|
||||
outLen[0] = nCurrentCount - BLOCK_SIZE_SEED;
|
||||
Array.Copy(_out, out_offset - BLOCK_SIZE_SEED_INT, pInfo.ecb_last_block, 0, BLOCK_SIZE_SEED);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Close(KISA_SEED_INFO pInfo, uint[] _out, int out_offset, ref int[] outLen)
|
||||
{
|
||||
int nPaddngLen;
|
||||
|
||||
outLen[0] = 0;
|
||||
|
||||
if (_out == null)
|
||||
return false;
|
||||
|
||||
if (KISA_ENC_DEC._KISA_ENCRYPT == pInfo.encrypt)
|
||||
{
|
||||
nPaddngLen = BLOCK_SIZE_SEED - pInfo.buffer_length;
|
||||
for (int i = pInfo.buffer_length; i < BLOCK_SIZE_SEED; i++)
|
||||
{
|
||||
SetByteToUIntArray(ref pInfo.ecb_buffer, i, (byte)nPaddngLen, DEFAULT_ENDIAN);
|
||||
}
|
||||
EncryptBlock(pInfo.ecb_buffer, 0, ref _out, (out_offset) / 4, pInfo.seed_key);
|
||||
outLen[0] = BLOCK_SIZE_SEED;
|
||||
}
|
||||
else
|
||||
{
|
||||
nPaddngLen = GetByteFromUIntArray(pInfo.ecb_last_block, BLOCK_SIZE_SEED - 1, DEFAULT_ENDIAN);
|
||||
if (nPaddngLen > 0 && nPaddngLen <= BLOCK_SIZE_SEED)
|
||||
{
|
||||
for (int i = nPaddngLen; i > 0; i--)
|
||||
{
|
||||
SetByteToUIntArray(ref _out, out_offset - i, (byte)0x00, DEFAULT_ENDIAN);
|
||||
}
|
||||
outLen[0] = nPaddngLen;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public byte[] Encrypt(byte[] pbszUserKey, byte[] pbData, int offset, int length)
|
||||
{
|
||||
KISA_SEED_INFO info = new KISA_SEED_INFO();
|
||||
uint[] _out;
|
||||
uint[] data;
|
||||
byte[] cdata;
|
||||
int outLen;
|
||||
int[] nRetOutLen = new int[] { 0 };
|
||||
int[] nPaddingLen = new int[] { 0 };
|
||||
|
||||
byte[] pbszPlainText = new byte[length];
|
||||
Array.Copy(pbData, offset, pbszPlainText, 0, length);
|
||||
int nPlainTextLen = length;
|
||||
|
||||
int nPlainTextPadding = (BLOCK_SIZE_SEED - (nPlainTextLen) % BLOCK_SIZE_SEED);
|
||||
byte[] newpbszPlainText = new byte[nPlainTextLen + nPlainTextPadding];
|
||||
Array.Copy(pbszPlainText, newpbszPlainText, nPlainTextLen);
|
||||
|
||||
byte[] pbszCipherText = new byte[nPlainTextLen + nPlainTextPadding];
|
||||
|
||||
Init(info, KISA_ENC_DEC.KISA_ENCRYPT, pbszUserKey);
|
||||
|
||||
outLen = ((nPlainTextLen / 16) + 1) * 4;
|
||||
|
||||
_out = new uint[outLen];
|
||||
|
||||
data = ConvertToUIntArray(newpbszPlainText, nPlainTextLen);
|
||||
Process(info, data, nPlainTextLen, ref _out, ref nRetOutLen);
|
||||
Close(info, _out, nRetOutLen[0], ref nPaddingLen);
|
||||
|
||||
cdata = ConvertToByteArray(_out, nRetOutLen[0] + nPaddingLen[0]);
|
||||
Array.Copy(cdata, pbszCipherText, nRetOutLen[0] + nPaddingLen[0]);
|
||||
|
||||
data = null;
|
||||
cdata = null;
|
||||
_out = null;
|
||||
|
||||
return pbszCipherText;
|
||||
}
|
||||
|
||||
public byte[] Decrypt(byte[] pbszUserKey, byte[] pbData, int offset, int length)
|
||||
{
|
||||
byte[] result = new byte[] { };
|
||||
|
||||
KISA_SEED_INFO info = new KISA_SEED_INFO();
|
||||
uint[] _out;
|
||||
uint[] data;
|
||||
byte[] cdata;
|
||||
int outLen = 0;
|
||||
int[] nRetOutLen = new int[] { 0 };
|
||||
int[] nPaddingLen = new int[] { 0 };
|
||||
|
||||
byte[] pbszCipherText = pbData;
|
||||
int nCipherTextLen = length;
|
||||
|
||||
if (nCipherTextLen % BLOCK_SIZE_SEED > 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
byte[] newpbszCipherText = new byte[nCipherTextLen];
|
||||
Array.Copy(pbszCipherText, newpbszCipherText, nCipherTextLen);
|
||||
|
||||
byte[] pbszPlainText = new byte[nCipherTextLen];
|
||||
|
||||
Init(info, KISA_ENC_DEC.KISA_DECRYPT, pbszUserKey);
|
||||
|
||||
outLen = (nCipherTextLen / 16) * 4;
|
||||
_out = new uint[outLen];
|
||||
data = ConvertToUIntArray(newpbszCipherText, nCipherTextLen);
|
||||
Process(info, data, nCipherTextLen, ref _out, ref nRetOutLen);
|
||||
if (Close(info, _out, nRetOutLen[0], ref nPaddingLen))
|
||||
{
|
||||
cdata = ConvertToByteArray(_out, nRetOutLen[0] - nPaddingLen[0]);
|
||||
Array.Copy(cdata, pbszPlainText, nRetOutLen[0] - nPaddingLen[0]);
|
||||
int message_length = nRetOutLen[0] - nPaddingLen[0];
|
||||
|
||||
if (message_length < 0)
|
||||
{
|
||||
message_length = 0;
|
||||
}
|
||||
result = new byte[message_length];
|
||||
Array.Copy(pbszPlainText, 0, result, 0, message_length);
|
||||
|
||||
data = null;
|
||||
cdata = null;
|
||||
_out = null;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetDefaultEndian(ENDIAN endian)
|
||||
{
|
||||
DEFAULT_ENDIAN = endian;
|
||||
}
|
||||
|
||||
public class KISA_ENC_DEC
|
||||
{
|
||||
public const int _KISA_DECRYPT = 0;
|
||||
public const int _KISA_ENCRYPT = 1;
|
||||
|
||||
public int value;
|
||||
|
||||
public KISA_ENC_DEC(int value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static KISA_ENC_DEC KISA_ENCRYPT = new KISA_ENC_DEC(_KISA_ENCRYPT);
|
||||
public static KISA_ENC_DEC KISA_DECRYPT = new KISA_ENC_DEC(_KISA_DECRYPT);
|
||||
}
|
||||
|
||||
public class KISA_SEED_KEY
|
||||
{
|
||||
public uint[] key_data = new uint[32];
|
||||
|
||||
public void Init()
|
||||
{
|
||||
for (int i = 0; i < key_data.Length; i++)
|
||||
{
|
||||
key_data[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class KISA_SEED_INFO
|
||||
{
|
||||
public int encrypt;
|
||||
public KISA_SEED_KEY seed_key = new KISA_SEED_KEY();
|
||||
public uint[] ecb_buffer = new uint[4];
|
||||
public int buffer_length;
|
||||
public uint[] ecb_last_block = new uint[4];
|
||||
public int last_block_flag;
|
||||
|
||||
public KISA_SEED_INFO()
|
||||
{
|
||||
encrypt = 0;
|
||||
seed_key.Init();
|
||||
ecb_buffer[0] = ecb_buffer[1] = ecb_buffer[2] = ecb_buffer[3] = 0;
|
||||
buffer_length = 0;
|
||||
ecb_last_block[0] = ecb_last_block[1] = ecb_last_block[2] = ecb_last_block[3] = 0;
|
||||
last_block_flag = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
198
WelsonJS.Toolkit/WelsonJS.Toolkit/NamedSharedMemory.cs
Normal file
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* WelsonJS.Toolkit: WelsonJS native component
|
||||
*
|
||||
* filename:
|
||||
* NamedSharedMemory.cs
|
||||
*
|
||||
* description:
|
||||
* WelsonJS - Build a Windows app on the Windows built-in JavaScript engine
|
||||
*
|
||||
* website:
|
||||
* - https://github.com/gnh1201/welsonjs
|
||||
* - https://catswords.social/@catswords_oss
|
||||
* - https://teams.live.com/l/community/FEACHncAhq8ldnojAI
|
||||
*
|
||||
* author:
|
||||
* Namhyeon Go <abuse@catswords.net>
|
||||
*
|
||||
* license:
|
||||
* GPLv3 or MS-RL(Microsoft Reciprocal License)
|
||||
*
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace WelsonJS
|
||||
{
|
||||
public class NamedSharedMemory
|
||||
{
|
||||
private IntPtr hFile;
|
||||
private IntPtr hFileMappingObject;
|
||||
private string lpName;
|
||||
private static Dictionary<string, NamedSharedMemory> memoryMap = new Dictionary<string, NamedSharedMemory>();
|
||||
|
||||
[Flags]
|
||||
public enum FileProtection : uint
|
||||
{
|
||||
PAGE_NOACCESS = 1u,
|
||||
PAGE_READONLY = 2u,
|
||||
PAGE_READWRITE = 4u,
|
||||
PAGE_WRITECOPY = 8u,
|
||||
PAGE_EXECUTE = 0x10u,
|
||||
PAGE_EXECUTE_READ = 0x20u,
|
||||
PAGE_EXECUTE_READWRITE = 0x40u,
|
||||
PAGE_EXECUTE_WRITECOPY = 0x80u,
|
||||
PAGE_GUARD = 0x100u,
|
||||
PAGE_NOCACHE = 0x200u,
|
||||
PAGE_WRITECOMBINE = 0x400u,
|
||||
SEC_FILE = 0x800000u,
|
||||
SEC_IMAGE = 0x1000000u,
|
||||
SEC_RESERVE = 0x4000000u,
|
||||
SEC_COMMIT = 0x8000000u,
|
||||
SEC_NOCACHE = 0x10000000u
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum FileMapAccess
|
||||
{
|
||||
FILE_MAP_COPY = 1,
|
||||
FILE_MAP_WRITE = 2,
|
||||
FILE_MAP_READ = 4,
|
||||
FILE_MAP_ALL_ACCESS = 0xF001F
|
||||
}
|
||||
|
||||
public class FileMappingNative
|
||||
{
|
||||
public const int INVALID_HANDLE_VALUE = -1;
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern IntPtr CreateFileMapping(IntPtr hFile, IntPtr lpAttributes, FileProtection flProtect, uint dwMaximumSizeHigh, uint dwMaximumSizeLow, string lpName);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern IntPtr MapViewOfFile(IntPtr hFileMappingObject, FileMapAccess dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, uint dwNumberOfBytesToMap);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern IntPtr OpenFileMapping(FileMapAccess dwDesiredAccess, bool bInheritHandle, string lpName);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool UnmapViewOfFile(IntPtr lpBaseAddress);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool CloseHandle(IntPtr hHandle);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern uint GetLastError();
|
||||
}
|
||||
|
||||
public NamedSharedMemory(string lpName)
|
||||
{
|
||||
this.lpName = lpName;
|
||||
Open();
|
||||
}
|
||||
|
||||
public bool Open()
|
||||
{
|
||||
if (memoryMap.ContainsKey(lpName))
|
||||
{
|
||||
hFile = memoryMap[lpName].hFile;
|
||||
hFileMappingObject = memoryMap[lpName].hFileMappingObject;
|
||||
return true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
hFile = FileMappingNative.CreateFileMapping((IntPtr)(-1), IntPtr.Zero, FileProtection.PAGE_READWRITE, 0u, 1024u, lpName);
|
||||
hFileMappingObject = FileMappingNative.MapViewOfFile(hFile, FileMapAccess.FILE_MAP_ALL_ACCESS, 0u, 0u, 1024u);
|
||||
memoryMap.Add(lpName, this);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return IsInitialized();
|
||||
}
|
||||
|
||||
public bool IsInitialized()
|
||||
{
|
||||
return hFile != IntPtr.Zero;
|
||||
}
|
||||
|
||||
public string ReadText()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (hFile == IntPtr.Zero || hFileMappingObject == IntPtr.Zero)
|
||||
{
|
||||
throw new Exception("Could not access the shared memory");
|
||||
}
|
||||
return Marshal.PtrToStringAnsi(hFileMappingObject);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return "Exception: " + e.Message;
|
||||
}
|
||||
}
|
||||
|
||||
public bool WriteText(string text, int size = 1024)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (hFile == IntPtr.Zero || hFileMappingObject == IntPtr.Zero)
|
||||
{
|
||||
throw new Exception("Could not access the shared memory");
|
||||
}
|
||||
byte[] bytes = Encoding.ASCII.GetBytes(text);
|
||||
byte[] array = new byte[size + 1];
|
||||
Array.Copy(bytes, array, bytes.Length);
|
||||
Marshal.Copy(array, 0, hFileMappingObject, size);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Clear(int size = 1024)
|
||||
{
|
||||
try
|
||||
{
|
||||
Marshal.Copy(new byte[size + 1], 0, hFileMappingObject, size);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Close()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (hFile == IntPtr.Zero || hFileMappingObject == IntPtr.Zero)
|
||||
{
|
||||
throw new Exception("Could not access the shared memory");
|
||||
}
|
||||
|
||||
FileMappingNative.UnmapViewOfFile(hFileMappingObject);
|
||||
hFileMappingObject = IntPtr.Zero;
|
||||
FileMappingNative.CloseHandle(hFile);
|
||||
hFile = IntPtr.Zero;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
90
WelsonJS.Toolkit/WelsonJS.Toolkit/ProcessUtils.cs
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* WelsonJS.Toolkit: WelsonJS native component
|
||||
*
|
||||
* filename:
|
||||
* ProcessUtils.cs
|
||||
*
|
||||
* description:
|
||||
* WelsonJS - Build a Windows app on the Windows built-in JavaScript engine
|
||||
*
|
||||
* website:
|
||||
* - https://github.com/gnh1201/welsonjs
|
||||
* - https://catswords.social/@catswords_oss
|
||||
* - https://teams.live.com/l/community/FEACHncAhq8ldnojAI
|
||||
*
|
||||
* author:
|
||||
* Namhyeon Go <abuse@catswords.net>
|
||||
*
|
||||
* license:
|
||||
* GPLv3 or MS-RL(Microsoft Reciprocal License)
|
||||
*
|
||||
*/
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace WelsonJS
|
||||
{
|
||||
public class ProcessUtils
|
||||
{
|
||||
public static List<Process> ProcessList = new List<Process>();
|
||||
|
||||
public static string OpenFileDialog()
|
||||
{
|
||||
string filepath = string.Empty;
|
||||
|
||||
using (OpenFileDialog openFileDialog = new OpenFileDialog())
|
||||
{
|
||||
openFileDialog.Filter = "All files (*.*)|*.*";
|
||||
openFileDialog.RestoreDirectory = true;
|
||||
if (openFileDialog.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
filepath = openFileDialog.FileName;
|
||||
}
|
||||
}
|
||||
|
||||
return filepath;
|
||||
}
|
||||
|
||||
public static int Open(string filepath)
|
||||
{
|
||||
int processId = -1;
|
||||
|
||||
if (string.IsNullOrEmpty(filepath))
|
||||
{
|
||||
filepath = OpenFileDialog();
|
||||
if (string.IsNullOrEmpty(filepath))
|
||||
{
|
||||
return processId;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Process process = new Process();
|
||||
process.StartInfo.FileName = filepath;
|
||||
process.Start();
|
||||
ProcessList.Add(process);
|
||||
}
|
||||
catch {
|
||||
processId = -1;
|
||||
}
|
||||
|
||||
return processId;
|
||||
}
|
||||
|
||||
public static bool Close(int processId)
|
||||
{
|
||||
try
|
||||
{
|
||||
Process.GetProcessById(processId).CloseMainWindow();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,25 @@
|
|||
using System.Windows.Forms;
|
||||
/*
|
||||
* WelsonJS.Toolkit: WelsonJS native component
|
||||
*
|
||||
* filename:
|
||||
* Prompt.cs
|
||||
*
|
||||
* description:
|
||||
* WelsonJS - Build a Windows app on the Windows built-in JavaScript engine
|
||||
*
|
||||
* website:
|
||||
* - https://github.com/gnh1201/welsonjs
|
||||
* - https://catswords.social/@catswords_oss
|
||||
* - https://teams.live.com/l/community/FEACHncAhq8ldnojAI
|
||||
*
|
||||
* author:
|
||||
* Namhyeon Go <abuse@catswords.net>
|
||||
*
|
||||
* license:
|
||||
* GPLv3 or MS-RL(Microsoft Reciprocal License)
|
||||
*
|
||||
*/
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace WelsonJS
|
||||
{
|
||||
|
@ -14,7 +35,7 @@ namespace WelsonJS
|
|||
Text = caption,
|
||||
StartPosition = FormStartPosition.CenterScreen
|
||||
};
|
||||
Label textLabel = new Label() { Left = 50, Top = 20, Text = text };
|
||||
Label textLabel = new Label() { Left = 50, Top = 20, Width = 400, Text = text };
|
||||
TextBox textBox = new TextBox() { Left = 50, Top = 50, Width = 400 };
|
||||
Button confirmation = new Button() { Text = "Ok", Left = 350, Width = 100, Top = 70, DialogResult = DialogResult.OK };
|
||||
confirmation.Click += (sender, e) => { prompt.Close(); };
|
||||
|
|
|
@ -6,12 +6,12 @@ using System.Runtime.InteropServices;
|
|||
// 제어됩니다. 어셈블리와 관련된 정보를 수정하려면
|
||||
// 이러한 특성 값을 변경하세요.
|
||||
[assembly: AssemblyTitle("WelsonJS.Toolkit")]
|
||||
[assembly: AssemblyDescription("WelsonJS CSharp.NET native component")]
|
||||
[assembly: AssemblyDescription("Toolkit for WelsonJS framework based applications")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("gnh1201/welsonjs")]
|
||||
[assembly: AssemblyProduct("WelsonJS.Toolkit")]
|
||||
[assembly: AssemblyCopyright("gnh1201/welsonjs is licensed under the Microsoft Public License (Ms-PL)")]
|
||||
[assembly: AssemblyTrademark("WelsonJS.Toolkit")]
|
||||
[assembly: AssemblyCompany("Catswords")]
|
||||
[assembly: AssemblyProduct("WelsonJS")]
|
||||
[assembly: AssemblyCopyright("Catswords OSS, GPLv3 or Ms-RL")]
|
||||
[assembly: AssemblyTrademark("WelsonJS")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// ComVisible을 false로 설정하면 이 어셈블리의 형식이 COM 구성 요소에
|
||||
|
@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
|
|||
// 모든 값을 지정하거나 아래와 같이 '*'를 사용하여 빌드 번호 및 수정 번호를
|
||||
// 기본값으로 할 수 있습니다.
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
[assembly: AssemblyVersion("0.2.7.54")]
|
||||
[assembly: AssemblyFileVersion("0.2.7.54")]
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace WelsonJS.Serialization
|
||||
{
|
||||
public class KVSerializer
|
||||
{
|
||||
private static Dictionary<string, string> dict = new Dictionary<string, string>();
|
||||
|
||||
public void Add(string key, string value)
|
||||
{
|
||||
dict[key] = value;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
foreach (var x in dict)
|
||||
{
|
||||
sb.Append($"{x.Key}={x.Value}; ");
|
||||
}
|
||||
if (sb.Length > 0) sb.Length -= 2;
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
// TinyINIController
|
||||
// Original source code: https://github.com/niklyadov/tiny-ini-file-class
|
||||
|
||||
namespace WelsonJS.TinyINIController
|
||||
{
|
||||
public class IniFile
|
||||
{
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
|
||||
static extern long WritePrivateProfileString(string section, string key, string value, string FilePath);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
|
||||
static extern int GetPrivateProfileString(string section, string key, string Default, StringBuilder RetVal, int Size, string FilePath);
|
||||
|
||||
private readonly FileInfo FileInfo;
|
||||
|
||||
//private readonly string exe = Assembly.GetExecutingAssembly().GetName().Name;
|
||||
private readonly string defaultSection = "Default";
|
||||
|
||||
private readonly FileAccess fileAccess;
|
||||
|
||||
public IniFile(string path = null, FileAccess access = FileAccess.ReadWrite)
|
||||
{
|
||||
fileAccess = access;
|
||||
FileInfo = new FileInfo(path ?? defaultSection);
|
||||
}
|
||||
|
||||
public string Read(string key, string section = null)
|
||||
{
|
||||
var RetVal = new StringBuilder(65025);
|
||||
|
||||
if (fileAccess != FileAccess.Write)
|
||||
{
|
||||
GetPrivateProfileString(section ?? defaultSection, key, "", RetVal, 65025, FileInfo.FullName);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Can`t read file! No access!");
|
||||
}
|
||||
|
||||
return RetVal.ToString();
|
||||
}
|
||||
public void Write(string key, string value, string section = null)
|
||||
{
|
||||
if (fileAccess != FileAccess.Read)
|
||||
{
|
||||
WritePrivateProfileString(section ?? defaultSection, key, value, FileInfo.FullName);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Can`t write to file! No access!");
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteKey(string key, string section = null)
|
||||
{
|
||||
Write(key, null, section ?? defaultSection);
|
||||
}
|
||||
|
||||
public void DeleteSection(string section = null)
|
||||
{
|
||||
Write(null, null, section ?? defaultSection);
|
||||
}
|
||||
|
||||
public bool KeyExists(string key, string section = null)
|
||||
{
|
||||
return Read(key, section).Length > 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +1,22 @@
|
|||
/*
|
||||
* WelsonJS.Toolkit: WelsonJS C#.NET native component
|
||||
* WelsonJS.Toolkit: WelsonJS native component
|
||||
*
|
||||
* filename:
|
||||
* Toolkit.cs
|
||||
*
|
||||
* description:
|
||||
* WelsonJS - Build Windows desktop apps with JavaScript, HTML, and CSS based on WSH/HTA.
|
||||
* https://github.com/gnh1201/welsonjs
|
||||
* WelsonJS - Build a Windows app on the Windows built-in JavaScript engine
|
||||
*
|
||||
* website:
|
||||
* - https://github.com/gnh1201/welsonjs
|
||||
* - https://catswords.social/@catswords_oss
|
||||
* - https://teams.live.com/l/community/FEACHncAhq8ldnojAI
|
||||
*
|
||||
* author:
|
||||
* Namhyeon Go <abuse@catswords.net>
|
||||
*
|
||||
* license:
|
||||
* gnh1201/welsonjs is licensed under the Microsoft Reciprocal License (MS-RL)
|
||||
* GPLv3 or MS-RL(Microsoft Reciprocal License)
|
||||
*
|
||||
* references:
|
||||
* - https://stackoverflow.com/questions/9004352/call-a-function-in-a-console-app-from-vbscript
|
||||
|
@ -23,122 +30,55 @@
|
|||
*/
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using WelsonJS.Cryptography;
|
||||
using WelsonJS.Serialization;
|
||||
|
||||
namespace WelsonJS
|
||||
{
|
||||
[ComVisible(true)]
|
||||
public class Toolkit
|
||||
{
|
||||
private static string ApplicationName = "WelsonJS";
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern IntPtr FindWindowEx(IntPtr hWnd1, IntPtr hWnd2, string lpsz1, string lpsz2);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
public static extern bool PostMessage(IntPtr hWnd, uint msg, int wParam, IntPtr lParam);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern int SendMessage(IntPtr hWnd, uint msg, int wParam, IntPtr lParam);
|
||||
|
||||
public enum WMessages : int
|
||||
{
|
||||
WM_MOUSEMOVE = 0x200,
|
||||
WM_LBUTTONDOWN = 0x201, //Left mousebutton down
|
||||
WM_LBUTTONUP = 0x202, //Left mousebutton up
|
||||
WM_LBUTTONDBLCLK = 0x203, //Left mousebutton doubleclick
|
||||
WM_RBUTTONDOWN = 0x204, //Right mousebutton down
|
||||
WM_RBUTTONUP = 0x205, //Right mousebutton up
|
||||
WM_RBUTTONDBLCLK = 0x206, //Right mousebutton doubleclick
|
||||
WM_KEYDOWN = 0x100, //Key down
|
||||
WM_KEYUP = 0x101, //Key up
|
||||
WM_SYSKEYDOWN = 0x104,
|
||||
WM_SYSKEYUP = 0x105,
|
||||
WM_CHAR = 0x102, //char
|
||||
WM_COMMAND = 0x111
|
||||
}
|
||||
|
||||
public enum WVirtualKeys : int
|
||||
{
|
||||
VK_RETURN = 0x0D,
|
||||
VK_F1 = 0x70,
|
||||
VK_F2 = 0x71,
|
||||
VK_F3 = 0x72,
|
||||
VK_F4 = 0x73,
|
||||
VK_F5 = 0x74,
|
||||
VK_F6 = 0x75,
|
||||
VK_F7 = 0x76,
|
||||
VK_F8 = 0x77,
|
||||
VK_F9 = 0x78,
|
||||
VK_F10 = 0x79,
|
||||
VK_F11 = 0x7A,
|
||||
VK_F12 = 0x7B
|
||||
}
|
||||
|
||||
public IntPtr QueryHandleWindow(string wName)
|
||||
{
|
||||
IntPtr hWnd = IntPtr.Zero;
|
||||
|
||||
foreach (Process pList in Process.GetProcesses())
|
||||
{
|
||||
if (pList.MainWindowTitle.Contains(wName))
|
||||
{
|
||||
hWnd = pList.MainWindowHandle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return hWnd;
|
||||
}
|
||||
public static readonly string ApplicationName = "WelsonJS";
|
||||
|
||||
[ComVisible(true)]
|
||||
public bool SendClick(string wName, int X, int Y)
|
||||
public bool SendClick(string title, int x, int y)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
IntPtr hWnd = QueryHandleWindow(wName);
|
||||
IntPtr hWnd = Window.GetWindowByTitleContains(title);
|
||||
if (hWnd != IntPtr.Zero) {
|
||||
PostMessage(hWnd, (int)WMessages.WM_LBUTTONDOWN, 1, new IntPtr(Y * 0x10000 + X));
|
||||
PostMessage(hWnd, (int)WMessages.WM_LBUTTONUP, 0, new IntPtr(Y * 0x10000 + X));
|
||||
result = true;
|
||||
Window.PostMessage(hWnd, (int)Window.Message.WM_LBUTTONDOWN, 1, new IntPtr(y * 0x10000 + x));
|
||||
Window.PostMessage(hWnd, (int)Window.Message.WM_LBUTTONUP, 0, new IntPtr(y * 0x10000 + x));
|
||||
}
|
||||
|
||||
return result;
|
||||
return hWnd != IntPtr.Zero;
|
||||
}
|
||||
|
||||
[ComVisible(true)]
|
||||
public bool SendKey(string wName, char key)
|
||||
public bool SendKey(string title, char key)
|
||||
{
|
||||
IntPtr hWnd = QueryHandleWindow(wName);
|
||||
IntPtr hWnd = Window.GetWindowByTitleContains(title);
|
||||
return SendKey(hWnd, key);
|
||||
}
|
||||
|
||||
// [ComVisible(false)]
|
||||
public bool SendKey(IntPtr hWnd, char key)
|
||||
{
|
||||
return PostMessage(hWnd, (int)WMessages.WM_CHAR, key, IntPtr.Zero);
|
||||
return Window.PostMessage(hWnd, (int)Window.Message.WM_CHAR, key, IntPtr.Zero);
|
||||
}
|
||||
|
||||
[ComVisible(true)]
|
||||
public bool SendKeys(string wName, string str)
|
||||
public bool SendKeys(string title, string str)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
IntPtr hWnd = QueryHandleWindow(wName);
|
||||
IntPtr hWnd = Window.GetWindowByTitleContains(title);
|
||||
if (hWnd != IntPtr.Zero)
|
||||
{
|
||||
foreach (char i in str)
|
||||
{
|
||||
SendKey(hWnd, i);
|
||||
}
|
||||
result = true;
|
||||
foreach (char i in str) SendKey(hWnd, i);
|
||||
return true;
|
||||
}
|
||||
|
||||
return result;
|
||||
return false;
|
||||
}
|
||||
|
||||
[ComVisible(true)]
|
||||
|
@ -162,55 +102,183 @@ namespace WelsonJS
|
|||
}
|
||||
|
||||
[ComVisible(true)]
|
||||
public bool SendEnterKey(string wName)
|
||||
public bool SendEnterKey(string title)
|
||||
{
|
||||
IntPtr hWnd = QueryHandleWindow(wName);
|
||||
IntPtr hWnd = Window.GetWindowByTitleContains(title);
|
||||
|
||||
if (hWnd != IntPtr.Zero)
|
||||
{
|
||||
PostMessage(hWnd, (int)WMessages.WM_KEYDOWN, (char)WVirtualKeys.VK_RETURN, IntPtr.Zero);
|
||||
PostMessage(hWnd, (int)WMessages.WM_KEYUP, (char)WVirtualKeys.VK_RETURN, IntPtr.Zero);
|
||||
Window.PostMessage(hWnd, (int)Window.Message.WM_KEYDOWN, (char)Window.VirtualKey.VK_RETURN, IntPtr.Zero);
|
||||
Window.PostMessage(hWnd, (int)Window.Message.WM_KEYUP, (char)Window.VirtualKey.VK_RETURN, IntPtr.Zero);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[ComVisible(true)]
|
||||
public bool SendFnKey(string wName, int num) {
|
||||
IntPtr hWnd = QueryHandleWindow(wName);
|
||||
char cKey = (char)0x00;
|
||||
|
||||
if (hWnd != IntPtr.Zero)
|
||||
public bool SendFnKey(string title, int num) {
|
||||
char[] fnKeys = new char[]
|
||||
{
|
||||
switch (num) {
|
||||
case 1: cKey = (char)WVirtualKeys.VK_F1; break;
|
||||
case 2: cKey = (char)WVirtualKeys.VK_F2; break;
|
||||
case 3: cKey = (char)WVirtualKeys.VK_F3; break;
|
||||
case 4: cKey = (char)WVirtualKeys.VK_F4; break;
|
||||
case 5: cKey = (char)WVirtualKeys.VK_F5; break;
|
||||
case 6: cKey = (char)WVirtualKeys.VK_F6; break;
|
||||
case 7: cKey = (char)WVirtualKeys.VK_F7; break;
|
||||
case 8: cKey = (char)WVirtualKeys.VK_F8; break;
|
||||
case 9: cKey = (char)WVirtualKeys.VK_F9; break;
|
||||
case 10: cKey = (char)WVirtualKeys.VK_F10; break;
|
||||
case 11: cKey = (char)WVirtualKeys.VK_F11; break;
|
||||
case 12: cKey = (char)WVirtualKeys.VK_F12; break;
|
||||
}
|
||||
|
||||
if (cKey != 0x00) {
|
||||
PostMessage(hWnd, (int)WMessages.WM_KEYDOWN, cKey, IntPtr.Zero);
|
||||
PostMessage(hWnd, (int)WMessages.WM_KEYUP, cKey, IntPtr.Zero);
|
||||
}
|
||||
(char)0x00,
|
||||
(char)Window.VirtualKey.VK_F1,
|
||||
(char)Window.VirtualKey.VK_F2,
|
||||
(char)Window.VirtualKey.VK_F3,
|
||||
(char)Window.VirtualKey.VK_F4,
|
||||
(char)Window.VirtualKey.VK_F5,
|
||||
(char)Window.VirtualKey.VK_F6,
|
||||
(char)Window.VirtualKey.VK_F7,
|
||||
(char)Window.VirtualKey.VK_F8,
|
||||
(char)Window.VirtualKey.VK_F9,
|
||||
(char)Window.VirtualKey.VK_F10,
|
||||
(char)Window.VirtualKey.VK_F11,
|
||||
(char)Window.VirtualKey.VK_F12
|
||||
};
|
||||
IntPtr hWnd = Window.GetWindowByTitleContains(title);
|
||||
|
||||
if (hWnd != IntPtr.Zero && (fnKeys.Length + 1 < num))
|
||||
{
|
||||
Window.PostMessage(hWnd, (int)Window.Message.WM_KEYDOWN, fnKeys[num], IntPtr.Zero);
|
||||
Window.PostMessage(hWnd, (int)Window.Message.WM_KEYUP, fnKeys[num], IntPtr.Zero);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// [Toolkit] Access to a shared memory #96
|
||||
[ComVisible(true)]
|
||||
public bool WriteTextToSharedMemory(string lpName, string text)
|
||||
{
|
||||
NamedSharedMemory mem = new NamedSharedMemory(lpName);
|
||||
if (mem.IsInitialized())
|
||||
{
|
||||
return false;
|
||||
return mem.WriteText(text);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[ComVisible(true)]
|
||||
public string ReadTextFromSharedMemory(string lpName)
|
||||
{
|
||||
NamedSharedMemory mem = new NamedSharedMemory(lpName);
|
||||
if (mem.IsInitialized()) {
|
||||
return mem.ReadText();
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
[ComVisible(true)]
|
||||
public bool ClearSharedMemory(string lpName)
|
||||
{
|
||||
NamedSharedMemory mem = new NamedSharedMemory(lpName);
|
||||
if (mem.IsInitialized())
|
||||
{
|
||||
return mem.Clear();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[ComVisible(true)]
|
||||
public bool CloseSharedMemory(string lpName)
|
||||
{
|
||||
NamedSharedMemory mem = new NamedSharedMemory(lpName);
|
||||
if (mem.IsInitialized())
|
||||
{
|
||||
return mem.Close();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[ComVisible(true)]
|
||||
public string GetFilePathFromDialog()
|
||||
{
|
||||
return ProcessUtils.OpenFileDialog();
|
||||
}
|
||||
|
||||
[ComVisible(true)]
|
||||
public int OpenProcess(string filepath)
|
||||
{
|
||||
return ProcessUtils.Open(filepath);
|
||||
}
|
||||
|
||||
[ComVisible(true)]
|
||||
public bool CloseProcess(int processID)
|
||||
{
|
||||
return ProcessUtils.Close(processID);
|
||||
}
|
||||
|
||||
[ComVisible(true)]
|
||||
public string CompressLZ77(string data)
|
||||
{
|
||||
return Compression.LZ77.Compress(data);
|
||||
}
|
||||
|
||||
[ComVisible(true)]
|
||||
public string DecompressLZ77(string compressedData)
|
||||
{
|
||||
return Compression.LZ77.Decompress(compressedData);
|
||||
}
|
||||
|
||||
[ComVisible(true)]
|
||||
public string EncryptString(string key, string data)
|
||||
{
|
||||
byte[] userKey = Encoding.ASCII.GetBytes(key);
|
||||
byte[] dataIn = Encoding.UTF8.GetBytes(data);
|
||||
|
||||
HIGHT.ECB cipher = new HIGHT.ECB(userKey);
|
||||
return Convert.ToBase64String(cipher.Encrypt(dataIn));
|
||||
}
|
||||
|
||||
[ComVisible(true)]
|
||||
public string DecryptString(string key, string encryptedData)
|
||||
{
|
||||
byte[] userKey = Encoding.ASCII.GetBytes(key);
|
||||
byte[] dataIn = Convert.FromBase64String(encryptedData);
|
||||
|
||||
HIGHT.ECB cipher = new HIGHT.ECB(userKey);
|
||||
return Encoding.UTF8.GetString(cipher.Decrypt(dataIn)).Trim('\0');
|
||||
}
|
||||
|
||||
[ComVisible(true)]
|
||||
public string GetImageSize(string srcfile)
|
||||
{
|
||||
int[] result = BitmapUtils.GetSize(srcfile);
|
||||
|
||||
var serializer = new KVSerializer();
|
||||
serializer.Add("width", result[0].ToString());
|
||||
serializer.Add("height", result[1].ToString());
|
||||
|
||||
return serializer.ToString();
|
||||
}
|
||||
|
||||
[ComVisible(true)]
|
||||
public string GetImagePixel(string srcfile)
|
||||
{
|
||||
int[] result = BitmapUtils.GetSize(srcfile);
|
||||
|
||||
var serializer = new KVSerializer();
|
||||
serializer.Add("red", result[0].ToString());
|
||||
serializer.Add("green", result[1].ToString());
|
||||
serializer.Add("blue", result[2].ToString());
|
||||
|
||||
return serializer.ToString();
|
||||
}
|
||||
|
||||
[ComVisible(true)]
|
||||
public string GetImageBase64(string srcfile)
|
||||
{
|
||||
return BitmapUtils.GetBase64(srcfile);
|
||||
}
|
||||
|
||||
[ComVisible(true)]
|
||||
public void CropImage(string srcfile, string dstfile, int x, int y, int a, int b)
|
||||
{
|
||||
BitmapUtils.Crop(srcfile, dstfile, x, y, a, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,18 +41,76 @@
|
|||
<PropertyGroup>
|
||||
<AssemblyOriginatorKeyFile>welsonjs.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<OutputPath>bin\x86\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>favicon.ico</ApplicationIcon>
|
||||
<PackageIcon>icon.png</PackageIcon>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="BitmapUtils.cs" />
|
||||
<Compile Include="Compression\LZ77.cs" />
|
||||
<Compile Include="Cryptography\AnsiX923Padding.cs" />
|
||||
<Compile Include="Cryptography\ARIA.cs" />
|
||||
<Compile Include="Cryptography\HIGHT.cs" />
|
||||
<Compile Include="Cryptography\LEA.cs" />
|
||||
<Compile Include="Cryptography\PKCS5Padding.cs" />
|
||||
<Compile Include="Cryptography\SEED.cs" />
|
||||
<Compile Include="NamedSharedMemory.cs" />
|
||||
<Compile Include="ProcessUtils.cs" />
|
||||
<Compile Include="Prompt.cs" />
|
||||
<Compile Include="Serialization\KVSerializer.cs" />
|
||||
<Compile Include="TinyINIController\IniFile.cs" />
|
||||
<Compile Include="Toolkit.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Window.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="welsonjs.snk" />
|
||||
<None Include="WelsonJS.Toolkit.nuspec" />
|
||||
<None Include="assets/img/logo.png" Pack="true" PackagePath="" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="favicon.ico" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
22
WelsonJS.Toolkit/WelsonJS.Toolkit/WelsonJS.Toolkit.nuspec
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package >
|
||||
<metadata>
|
||||
<id>$id$</id>
|
||||
<version>0.2.7.49</version>
|
||||
<title>$title$</title>
|
||||
<authors>Namhyeon Go, Catswords OSS</authors>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">GPL-3.0-only</license>
|
||||
<icon>logo.png</icon>
|
||||
<readme>README.md</readme>
|
||||
<projectUrl>https://github.com/gnh1201/welsonjs</projectUrl>
|
||||
<description>Native toolkit for WelsonJS framework-based RPA applications.</description>
|
||||
<releaseNotes>Updated release</releaseNotes>
|
||||
<copyright>$copyright$</copyright>
|
||||
<tags>rpa com scripting library javascript ini window-handle named-shared-memory</tags>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="assets\img\logo.png" />
|
||||
<file src="assets\docs\README.md" />
|
||||
</files>
|
||||
</package>
|
92
WelsonJS.Toolkit/WelsonJS.Toolkit/Window.cs
Normal file
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* WelsonJS.Toolkit: WelsonJS native component
|
||||
*
|
||||
* filename:
|
||||
* Window.cs
|
||||
*
|
||||
* description:
|
||||
* WelsonJS - Build a Windows app on the Windows built-in JavaScript engine
|
||||
*
|
||||
* website:
|
||||
* - https://github.com/gnh1201/welsonjs
|
||||
* - https://catswords.social/@catswords_oss
|
||||
* - https://teams.live.com/l/community/FEACHncAhq8ldnojAI
|
||||
*
|
||||
* author:
|
||||
* Namhyeon Go <abuse@catswords.net>
|
||||
*
|
||||
* license:
|
||||
* GPLv3 or MS-RL(Microsoft Reciprocal License)
|
||||
*
|
||||
*/
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace WelsonJS
|
||||
{
|
||||
public class Window
|
||||
{
|
||||
[DllImport("user32.dll")]
|
||||
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern IntPtr FindWindowEx(IntPtr hWnd1, IntPtr hWnd2, string lpsz1, string lpsz2);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
public static extern bool PostMessage(IntPtr hWnd, uint msg, int wParam, IntPtr lParam);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern int SendMessage(IntPtr hWnd, uint msg, int wParam, IntPtr lParam);
|
||||
|
||||
public enum Message : int
|
||||
{
|
||||
WM_MOUSEMOVE = 0x200,
|
||||
WM_LBUTTONDOWN = 0x201, //Left mousebutton down
|
||||
WM_LBUTTONUP = 0x202, //Left mousebutton up
|
||||
WM_LBUTTONDBLCLK = 0x203, //Left mousebutton doubleclick
|
||||
WM_RBUTTONDOWN = 0x204, //Right mousebutton down
|
||||
WM_RBUTTONUP = 0x205, //Right mousebutton up
|
||||
WM_RBUTTONDBLCLK = 0x206, //Right mousebutton doubleclick
|
||||
WM_KEYDOWN = 0x100, //Key down
|
||||
WM_KEYUP = 0x101, //Key up
|
||||
WM_SYSKEYDOWN = 0x104,
|
||||
WM_SYSKEYUP = 0x105,
|
||||
WM_CHAR = 0x102, //char
|
||||
WM_COMMAND = 0x111
|
||||
}
|
||||
|
||||
public enum VirtualKey : int
|
||||
{
|
||||
VK_RETURN = 0x0D,
|
||||
VK_F1 = 0x70,
|
||||
VK_F2 = 0x71,
|
||||
VK_F3 = 0x72,
|
||||
VK_F4 = 0x73,
|
||||
VK_F5 = 0x74,
|
||||
VK_F6 = 0x75,
|
||||
VK_F7 = 0x76,
|
||||
VK_F8 = 0x77,
|
||||
VK_F9 = 0x78,
|
||||
VK_F10 = 0x79,
|
||||
VK_F11 = 0x7A,
|
||||
VK_F12 = 0x7B
|
||||
}
|
||||
|
||||
public static IntPtr GetWindowByTitleContains(string title)
|
||||
{
|
||||
IntPtr hWnd = IntPtr.Zero;
|
||||
|
||||
foreach (Process proc in Process.GetProcesses())
|
||||
{
|
||||
if (proc.MainWindowTitle.Contains(title))
|
||||
{
|
||||
hWnd = proc.MainWindowHandle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return hWnd;
|
||||
}
|
||||
}
|
||||
}
|
17
WelsonJS.Toolkit/WelsonJS.Toolkit/assets/docs/README.md
Normal file
|
@ -0,0 +1,17 @@
|
|||
# WelsonJS.Toolkit
|
||||
|
||||
WelsonJS.Toolkit is a native toolkit for WelsonJS. Of course, it can also be used in other projects.
|
||||
|
||||
This library supports COM and provides the following features:
|
||||
|
||||
* Access and control Windows handles
|
||||
* Send keyboard and mouse click commands to Windows handles
|
||||
* Use Named Shared Memory
|
||||
* Read and modify INI files
|
||||
* Encryption (HIGHT)
|
||||
* Compression (LZ77)
|
||||
* Bitmap handling
|
||||
|
||||
For more details, refer to the [WelsonJS Documentation](https://catswords-oss.rdbl.io/5719744820/5330609327).
|
||||
|
||||
Source code available: https://github.com/gnh1201/welsonjs
|