Compare commits
1019 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 |
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 }}
|
12
.gitignore
vendored
|
@ -103,5 +103,13 @@ dist
|
|||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# ChatGPT API Key
|
||||
data/chatgpt-apikey.txt
|
||||
# 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,8 +0,0 @@
|
|||
# These are supported funding model platforms
|
||||
|
||||
github: gnh1201
|
||||
open_collective: welsonjs
|
||||
liberapay: catswords
|
||||
custom: ['https://www.buymeacoffee.com/catswords']
|
||||
patreon: catswords # Replace with a single Patreon username
|
||||
ko_fi: catswords
|
217
README.md
|
@ -1,75 +1,84 @@
|
|||
# welsonjs
|
||||
WelsonJS - Build a Windows app on the Windows built-in JavaScript engine.
|
||||
|
||||

|
||||
[](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)
|
||||
|
||||
Now, You can build an Windows desktop app with JavaScript, TypeScript, ReScript, and HTML/CSS on Windows built-in ECMAScript engine.
|
||||
<img src="app/assets/img/logo.svg" height="32" alt=""/> WelsonJS - Build a Windows app on the Windows built-in JavaScript engine.
|
||||
|
||||
WelsonJS = ***W***indows + ***El***ectr***on***-like + ***Javascript(JS)*** + [Your contribution](FUNDING.yml)
|
||||

|
||||
|
||||
Dual license notice: 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.
|
||||
Now, You can build a Windows desktop app with JavaScript, TypeScript, CoffeeScript, ReScript, and HTML/CSS on Windows built-in ECMAScript engine.
|
||||
|
||||
I presented this project in [the open-source contest in the Republic of Korea in November 2023](https://www.slideshare.net/gnh1201/welsonjs-2023).
|
||||
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
|
||||
- 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 require additional software installation.
|
||||
- ES5(ECMAScript 5), XML, JSON, YAML compatibility
|
||||
- [github.com/zloirock/core-js](https://github.com/zloirock/core-js)
|
||||
- [github.com/douglascrockford/JSON-js](https://github.com/douglascrockford/JSON-js) (aka. JSON2.js)
|
||||
- [github.com/nodeca/js-yaml](https://github.com/nodeca/js-yaml)
|
||||
- HTML5, CSS3 compatibility
|
||||
- [github.com/aFarkas/html5shiv](https://github.com/aFarkas/html5shiv)
|
||||
- [github.com/parndt/jquery-html5-placeholder-shim](https://github.com/parndt/jquery-html5-placeholder-shim)
|
||||
- [github.com/scottjehl/Respond](https://github.com/scottjehl/Respond)
|
||||
- [github.com/keithclark/selectivizr](https://github.com/keithclark/selectivizr)
|
||||
- [github.com/arv/ExplorerCanvas](https://github.com/arv/ExplorerCanvas)
|
||||
- [github.com/etianen/html5media](https://github.com/etianen/html5media)
|
||||
- [github.com/Modernizr/Modernizr](https://github.com/Modernizr/Modernizr)
|
||||
- Default CSS Framework
|
||||
- [github.com/jslegers/cascadeframework](https://github.com/jslegers/cascadeframework)
|
||||
- WYSIWYG HTML Editor
|
||||
- [github.com/summernote/summernote](https://github.com/summernote/summernote)
|
||||
- Included libraries
|
||||
- [jQuery](https://jquery.com/)
|
||||
- [jQuery UI](https://jqueryui.com/)
|
||||
- [github.com/kamranahmedse/jquery-toast-plugin](https://github.com/kamranahmedse/jquery-toast-plugin)
|
||||
- [github.com/hiddentao/squel](https://github.com/hiddentao/squel)
|
||||
- [github.com/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
|
||||
- [gRPC](https://grpc.io/) protocol support (New feature in 2023) [see video demo](https://youtu.be/GqbU5JKuLac)
|
||||
* 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 interface)
|
||||
- lib/base64 (BASE64 encode and decode)
|
||||
- lib/file (File I/O interface)
|
||||
- lib/http (HTTP interface with MSXML and cURL)
|
||||
- lib/json (JSON library)
|
||||
- lib/registry (Windows Registry interface)
|
||||
- lib/security (Windows Security Policy interface)
|
||||
- lib/shell (Windows Shell (Command Prompt) interface)
|
||||
- lib/powershell (Windows Powershell interface)
|
||||
- lib/service (Windows Service interface)
|
||||
- lib/oldbrowser (ES5, HTML5/CSS3 compatibility layer)
|
||||
- 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/msoffice (Microsoft Office (e.g. Excel) 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)
|
||||
- lib/chatgpt ([ChatGPT](https://openai.com/chatgpt) integration)
|
||||
- Everything you can imagine.
|
||||
|
||||
## Make your own `sayhello` example
|
||||
## Quick start
|
||||
|
||||
### 1. Write a file `lib/sayhello.js`
|
||||
```js
|
||||
|
@ -80,8 +89,8 @@ function say() {
|
|||
|
||||
exports.say = say;
|
||||
|
||||
exports.VERSIONINFO = "SayHello Library (sayhello.js) version 0.1";
|
||||
exports.AUTHOR = "abuse@catswords.net"; // e.g. YOUR EMAIL ADDRESS
|
||||
exports.VERSIONINFO = "SayHello (sayhello.js) version 0.1";
|
||||
exports.AUTHOR = "abuse@catswords.net";
|
||||
exports.global = global;
|
||||
exports.require = global.require;
|
||||
```
|
||||
|
@ -100,45 +109,73 @@ function main() {
|
|||
exports.main = main;
|
||||
```
|
||||
|
||||
### 3. Execute file on the command prompt
|
||||
### 3. Execute script on the console
|
||||
```cmd
|
||||
C:\Users\oss\Documents\GitHub\welsonjs> cscript app.js sayhello
|
||||
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
|
||||
- [Paid technical support on Fiverr](https://www.fiverr.com/s/G42xRd)
|
||||
- abuse@catswords.net
|
||||
- ActivityPub [@catswords_oss@catswords.social](https://catswords.social/@catswords_oss)
|
||||
## 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.
|
73
SECURITY.MD
|
@ -1,46 +1,63 @@
|
|||
# Security Note for WelsonJS
|
||||
|
||||
## Caution
|
||||
This repository contains information on accessing Windows APIs and functions on 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 a DoS attack, may result in legal punishment in your country. We encourage you to use this project only for creating web technology-based applications, like Electron, or legally permitted testing tools.
|
||||
## 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.
|
||||
|
||||
## Known use cases
|
||||
## Caution
|
||||
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.
|
||||
|
||||
## Known Use Cases
|
||||
WelsonJS is typically used for the following purposes:
|
||||
|
||||
* 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, CD/CI (Continuous Integration/Continuous Delivery), DevOps, and SecOps.
|
||||
* Asset evaluation (e.g. Get a purchase history from online shopping and delivery websites)
|
||||
* Online video streaming quality testing and improvement.
|
||||
* Office Automation with AI(Artificial Intelligence) services.
|
||||
* 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.
|
||||
|
||||
## 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.
|
||||
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.
|
||||
|
||||
## Guidelines
|
||||
|
||||
### For the use of online shopping and delivery websites
|
||||
We are aware of cases where WelsonJS has been used for asset valuation to access websites of online shopping or delivery companies. 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.
|
||||
### 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.
|
||||
|
||||
### For the use of online video streaming quality testing and improvement
|
||||
We are aware of cases where WelsonJS is used for the purpose of 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 the region. The WelsonJS developers and maintainers take no responsibility for the use of videos containing illegal content.
|
||||
### 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.
|
||||
|
||||
### For the use of security testing
|
||||
### 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.
|
||||
|
||||
### For the use of cloud monitoring
|
||||
WelsonJS is a project initiated by a cloud service provider in response to a request to develop a lightweight software (e.g., agent) 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.
|
||||
### 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 name. This name is used solely for the purpose of identifying the work and does not impact the license:
|
||||
- "A0562"(2023) (2023 Open-source Development Contest, NIPA National IT Industry Promotion Agency, Republic of Korea)
|
||||
- "C-2021-000237"(2021) (Copyright Registration Online System, Korea Copyright Commission, Republic of Korea)
|
||||
- "Codename Macadamia"(2020) (Heavy industry specialized CSP in the Republic of Korea)
|
||||
## 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 discover any instances of this project being misused, please report them.
|
||||
* [GitHub Security Advisories](https://github.com/gnh1201/welsonjs/security)
|
||||
* [abuse@catswords.net](mailto:abuse@catswords.net)
|
||||
|
||||
* [Paid technical support on Fiverr](https://www.fiverr.com/s/G42xRd)
|
||||
* abuse@catswords.net
|
||||
* ActivityPub [@catswords_oss@catswords.social](https://catswords.social/@catswords_oss)
|
||||
## 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>
|
|
@ -5,26 +5,42 @@ 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
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
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|x64.ActiveCfg = Debug|x64
|
||||
{D6007282-B4F7-4694-AC67-BB838D91B77A}.Debug|x64.Build.0 = Debug|x64
|
||||
{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|x64.ActiveCfg = Release|x64
|
||||
{D6007282-B4F7-4694-AC67-BB838D91B77A}.Release|x64.Build.0 = Release|x64
|
||||
{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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@
|
|||
* website:
|
||||
* - https://github.com/gnh1201/welsonjs
|
||||
* - https://catswords.social/@catswords_oss
|
||||
* - https://teams.live.com/l/community/FEACHncAhq8ldnojAI
|
||||
*
|
||||
* author:
|
||||
* Namhyeon Go <abuse@catswords.net>
|
||||
|
|
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,33 +5,38 @@
|
|||
* HIGHT.cs
|
||||
*
|
||||
* description:
|
||||
* HIGHT(ISO/IEC 18033-3) cryptography algorithm (ECB mode) implementation for WelsonJS
|
||||
* 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
|
||||
*
|
||||
* author:
|
||||
* - Namhyeon Go <abuse@catswords.net>
|
||||
* - @chandong83 (github.com) - Original author of CryptoHIGHT.cs
|
||||
* - KISA(Korea Internet & Security Agency)
|
||||
* 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://seed.kisa.or.kr/kisa/algorithm/EgovHightInfo.do
|
||||
* - 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 static class HIGHT
|
||||
public class HIGHT
|
||||
{
|
||||
private static readonly byte[] hightDelta = {
|
||||
0x5A,0x6D,0x36,0x1B,0x0D,0x06,0x03,0x41,
|
||||
|
@ -234,24 +239,6 @@ namespace WelsonJS.Cryptography
|
|||
dataOut[6] = (byte)((xx[6] ^ scheduleKey[3]) & 0xFF);
|
||||
}
|
||||
|
||||
void debugPrintHex(byte[] dat)
|
||||
{
|
||||
for (int i = 0; i < dat.Length; i++)
|
||||
{
|
||||
Console.Write(string.Format("0x{0:x2}, ", dat[i]));
|
||||
if (((i + 1) % 16) == 0)
|
||||
{
|
||||
Console.WriteLine("");
|
||||
}
|
||||
}
|
||||
Console.WriteLine("");
|
||||
}
|
||||
|
||||
void debugPrintLine(string str)
|
||||
{
|
||||
Console.WriteLine(str);
|
||||
}
|
||||
|
||||
void EncryptBlock(byte[] dataIn, byte[] dataOut)
|
||||
{
|
||||
byte[] xx = new byte[8];
|
||||
|
@ -316,6 +303,7 @@ namespace WelsonJS.Cryptography
|
|||
dataOut[4] = (byte)((xx[5] + scheduleKey[6]) & 0xFF);
|
||||
dataOut[6] = (byte)((xx[7] ^ scheduleKey[7]) & 0xFF);
|
||||
}
|
||||
|
||||
public byte[] Encrypt(byte[] dataIn)
|
||||
{
|
||||
int length;
|
||||
|
@ -345,6 +333,7 @@ namespace WelsonJS.Cryptography
|
|||
}
|
||||
return dataOut;
|
||||
}
|
||||
|
||||
public byte[] Decrypt(byte[] dataIn)
|
||||
{
|
||||
int length;
|
||||
|
@ -374,7 +363,7 @@ namespace WelsonJS.Cryptography
|
|||
return dataOut;
|
||||
}
|
||||
|
||||
public void test()
|
||||
public void Test()
|
||||
{
|
||||
byte[] dataIn = new byte[15];
|
||||
byte[] dataOut = new byte[15];
|
||||
|
@ -383,17 +372,17 @@ namespace WelsonJS.Cryptography
|
|||
{
|
||||
dataIn[i] = (byte)i;
|
||||
}
|
||||
debugPrintLine("schedule key data");
|
||||
debugPrintHex(scheduleKey);
|
||||
Console.WriteLine("schedule key data");
|
||||
Console.WriteLine(string.Format("0x{0:x2}", scheduleKey));
|
||||
|
||||
dataOut = Encrypt(dataIn);
|
||||
dataOut2 = Decrypt(dataOut);
|
||||
debugPrintLine("origin data");
|
||||
debugPrintHex(dataIn);
|
||||
debugPrintLine("encryption data");
|
||||
debugPrintHex(dataOut);
|
||||
debugPrintLine("decryption data");
|
||||
debugPrintHex(dataOut2);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@
|
|||
* website:
|
||||
* - https://github.com/gnh1201/welsonjs
|
||||
* - https://catswords.social/@catswords_oss
|
||||
* - https://teams.live.com/l/community/FEACHncAhq8ldnojAI
|
||||
*
|
||||
* author:
|
||||
* Namhyeon Go <abuse@catswords.net>
|
||||
|
|
|
@ -1,4 +1,25 @@
|
|||
using System.Collections.Generic;
|
||||
/*
|
||||
* 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;
|
||||
|
||||
|
@ -6,7 +27,7 @@ namespace WelsonJS
|
|||
{
|
||||
public class ProcessUtils
|
||||
{
|
||||
public static List<int> ProcessIDs = new List<int>();
|
||||
public static List<Process> ProcessList = new List<Process>();
|
||||
|
||||
public static string OpenFileDialog()
|
||||
{
|
||||
|
@ -27,12 +48,14 @@ namespace WelsonJS
|
|||
|
||||
public static int Open(string filepath)
|
||||
{
|
||||
int processId = -1;
|
||||
|
||||
if (string.IsNullOrEmpty(filepath))
|
||||
{
|
||||
filepath = OpenFileDialog();
|
||||
if (string.IsNullOrEmpty(filepath))
|
||||
{
|
||||
return -1;
|
||||
return processId;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,16 +64,13 @@ namespace WelsonJS
|
|||
Process process = new Process();
|
||||
process.StartInfo.FileName = filepath;
|
||||
process.Start();
|
||||
|
||||
int processId = process.Id;
|
||||
ProcessIDs.Add(processId);
|
||||
|
||||
return processId;
|
||||
ProcessList.Add(process);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return -1;
|
||||
catch {
|
||||
processId = -1;
|
||||
}
|
||||
|
||||
return processId;
|
||||
}
|
||||
|
||||
public static bool Close(int processId)
|
||||
|
@ -58,12 +78,13 @@ namespace WelsonJS
|
|||
try
|
||||
{
|
||||
Process.GetProcessById(processId).CloseMainWindow();
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
* website:
|
||||
* - https://github.com/gnh1201/welsonjs
|
||||
* - https://catswords.social/@catswords_oss
|
||||
* - https://teams.live.com/l/community/FEACHncAhq8ldnojAI
|
||||
*
|
||||
* author:
|
||||
* Namhyeon Go <abuse@catswords.net>
|
||||
|
|
|
@ -6,11 +6,11 @@ using System.Runtime.InteropServices;
|
|||
// 제어됩니다. 어셈블리와 관련된 정보를 수정하려면
|
||||
// 이러한 특성 값을 변경하세요.
|
||||
[assembly: AssemblyTitle("WelsonJS.Toolkit")]
|
||||
[assembly: AssemblyDescription("WelsonJS dotNET native component")]
|
||||
[assembly: AssemblyDescription("Toolkit for WelsonJS framework based applications")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Catswords Research, Co.")]
|
||||
[assembly: AssemblyProduct("WelsonJS.Toolkit")]
|
||||
[assembly: AssemblyCopyright("Catswords Research, Co. (Licensed under GPLv3 or MS-RL)")]
|
||||
[assembly: AssemblyCompany("Catswords")]
|
||||
[assembly: AssemblyProduct("WelsonJS")]
|
||||
[assembly: AssemblyCopyright("Catswords OSS, GPLv3 or Ms-RL")]
|
||||
[assembly: AssemblyTrademark("WelsonJS")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@
|
|||
* website:
|
||||
* - https://github.com/gnh1201/welsonjs
|
||||
* - https://catswords.social/@catswords_oss
|
||||
* - https://teams.live.com/l/community/FEACHncAhq8ldnojAI
|
||||
*
|
||||
* author:
|
||||
* Namhyeon Go <abuse@catswords.net>
|
||||
|
@ -33,6 +34,7 @@ using System.Runtime.InteropServices;
|
|||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using WelsonJS.Cryptography;
|
||||
using WelsonJS.Serialization;
|
||||
|
||||
namespace WelsonJS
|
||||
{
|
||||
|
@ -223,7 +225,7 @@ namespace WelsonJS
|
|||
}
|
||||
|
||||
[ComVisible(true)]
|
||||
public string EncryptStringHIGHT(string key, string data)
|
||||
public string EncryptString(string key, string data)
|
||||
{
|
||||
byte[] userKey = Encoding.ASCII.GetBytes(key);
|
||||
byte[] dataIn = Encoding.UTF8.GetBytes(data);
|
||||
|
@ -233,7 +235,7 @@ namespace WelsonJS
|
|||
}
|
||||
|
||||
[ComVisible(true)]
|
||||
public string DecryptStringHIGHT(string key, string encryptedData)
|
||||
public string DecryptString(string key, string encryptedData)
|
||||
{
|
||||
byte[] userKey = Encoding.ASCII.GetBytes(key);
|
||||
byte[] dataIn = Convert.FromBase64String(encryptedData);
|
||||
|
@ -241,5 +243,42 @@ namespace WelsonJS
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,23 +59,58 @@
|
|||
<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>
|
|
@ -10,6 +10,7 @@
|
|||
* website:
|
||||
* - https://github.com/gnh1201/welsonjs
|
||||
* - https://catswords.social/@catswords_oss
|
||||
* - https://teams.live.com/l/community/FEACHncAhq8ldnojAI
|
||||
*
|
||||
* author:
|
||||
* Namhyeon Go <abuse@catswords.net>
|
||||
|
|
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
|
BIN
WelsonJS.Toolkit/WelsonJS.Toolkit/assets/img/logo.png
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
WelsonJS.Toolkit/WelsonJS.Toolkit/favicon.ico
Normal file
After Width: | Height: | Size: 56 KiB |
504
app.js
|
@ -1,52 +1,29 @@
|
|||
//////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// app.js
|
||||
//
|
||||
// Bootstrap code for running a javascript app in windows. Run as:
|
||||
//
|
||||
// cscript.js app.js <appname> <app arguments> ...
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//"use strict";
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// Bootstrap code, basic module loading functionality
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//
|
||||
// The module loaded is run inside a function, with one argument, global which
|
||||
// points to the global context. So global.FN is the same as FN (as long as a
|
||||
// version of FN does not exist in local scope).
|
||||
//
|
||||
// The module should return its interface at the end of the script. The basic
|
||||
// pattern for a module is:-
|
||||
//
|
||||
// var module = { ... };
|
||||
// return module;
|
||||
//
|
||||
// Or:-
|
||||
//
|
||||
// return function() {
|
||||
// }
|
||||
//
|
||||
// The appname argument causes <appname>.js to be loaded. The interface returned
|
||||
// must define main = function(args) {}, which is called once the module is
|
||||
// loaded.
|
||||
//
|
||||
// app.js
|
||||
// Namhyeon Go <abuse@catswords.net>
|
||||
// https://github.com/gnh1201/welsonjs
|
||||
// If you find an abuse case or a security issue, please feel free to contact me.
|
||||
//
|
||||
// app.js
|
||||
// Bootstrap code for running a javascript app in windows. Run as:
|
||||
// cscript.js app.js <appname> <app arguments> ...
|
||||
//
|
||||
// Author: Namhyeon Go <abuse@catswords.net>
|
||||
// Repository: https://github.com/gnh1201/welsonjs
|
||||
// License: GPLv3 or MS-RL (Opensource)
|
||||
// Report abuse: abuse@catswords.net
|
||||
// Latest news: ActivityPub @catswords_oss@catswords.social
|
||||
// Join our team: https://teams.live.com/l/community/FEACHncAhq8ldnojAI
|
||||
//
|
||||
"use strict";
|
||||
|
||||
var exit = function(status) {
|
||||
console.error("Exit", status, "caused");
|
||||
|
||||
if (typeof WScript !== "undefined") {
|
||||
WScript.Quit(status);
|
||||
return;
|
||||
} else if (typeof window !== "undefined") {
|
||||
window.close();
|
||||
return;
|
||||
}
|
||||
|
||||
// to exit completely
|
||||
throw new Error("Exit " + status + " caused");
|
||||
};
|
||||
|
||||
var console = {
|
||||
|
@ -62,60 +39,69 @@ var console = {
|
|||
}
|
||||
return res;
|
||||
},
|
||||
_echoCallback: null,
|
||||
_wshEcho: function(message) {
|
||||
_echoDefault: function(message) {
|
||||
if (typeof WScript !== "undefined") {
|
||||
WScript.Echo("[*] " + message)
|
||||
}
|
||||
},
|
||||
_echoCallback: null,
|
||||
_echo: function(args, type) {
|
||||
var message = "";
|
||||
var messages = [];
|
||||
var params = {
|
||||
type: type,
|
||||
channel: 'default',
|
||||
scope: [],
|
||||
message: '',
|
||||
datetime: new Date().toISOString()
|
||||
};
|
||||
|
||||
if (args.length > 0) {
|
||||
if (typeof args[0] === "string") {
|
||||
// if not type is "log", then "{type}: {message}"
|
||||
if (typeof type !== "undefined") {
|
||||
message += (type + ": " + this._join(args));
|
||||
} else {
|
||||
message += this._join(args);
|
||||
}
|
||||
this._wshEcho(message);
|
||||
this._messages.push(message);
|
||||
params.message = message;
|
||||
} else if (typeof args[0] === "object") {
|
||||
if ('message' in args[0]) {
|
||||
if (typeof type !== "undefined") {
|
||||
message += (type + ": " + args[0].message);
|
||||
var argl = args.length;
|
||||
for (var i = 0; i < argl; i++) {
|
||||
switch (typeof args[i]) {
|
||||
case "string":
|
||||
messages.push(args[i]);
|
||||
break;
|
||||
|
||||
case "number":
|
||||
case "boolean":
|
||||
messages.push(String(args[i]));
|
||||
break;
|
||||
|
||||
case "object":
|
||||
if ("message" in args[i]) {
|
||||
messages.push(args[i].message);
|
||||
for (var k in args[i]) {
|
||||
params[k] = args[i][k];
|
||||
}
|
||||
} else {
|
||||
message += args[0].message;
|
||||
messages.push("[object Object]");
|
||||
}
|
||||
}
|
||||
this._wshEcho(message);
|
||||
this._messages.push(args[0].message);
|
||||
for (var k in args[0]) {
|
||||
params[k] = args[0][k];
|
||||
}
|
||||
break;
|
||||
|
||||
case "unknown":
|
||||
messages.push("[unknown]");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var message = messages.join(' ');
|
||||
if (typeof type !== "undefined") {
|
||||
message = type + ": " + message;
|
||||
}
|
||||
this._echoDefault(message);
|
||||
this._messages.push(message);
|
||||
|
||||
if (params.channel != "default" && this._echoCallback != null) {
|
||||
if (params.scope.length > 0 && this._echoCallback != null) {
|
||||
try {
|
||||
this._echoCallback(params, type);
|
||||
} catch (e) {
|
||||
this._wshEcho("Exception on _echoCallback: " + e.message);
|
||||
this._echoDefault("Exception:" + e.message);
|
||||
}
|
||||
}
|
||||
},
|
||||
assert: function(assertion) {
|
||||
if (arguments.length > 1 && assertion === arguments[0]) {
|
||||
if(!assertion) {
|
||||
this.error("Assertion failed: " + this._join(arguments.slice(1)));
|
||||
this.error("Assertion failed:", this._join(arguments.slice(1)));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -194,9 +180,12 @@ if (typeof CreateObject === "undefined") {
|
|||
} else {
|
||||
console.warn("(Chakra) The standalone engine does not supported. Please use the built-in engine.");
|
||||
console.warn("(Chakra) hint:", "cscript //NoLogo //E:{1b7cd997-e5ff-4932-a7a6-2a9e636da385} app.js <filename> <...arguments>");
|
||||
throw new Error("Could not find a loader");
|
||||
}
|
||||
} else if (typeof ActiveXObject !== "undefined") {
|
||||
return new ActiveXObject(p);
|
||||
} else {
|
||||
throw new Error("Could not find a loader");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -204,76 +193,126 @@ if (typeof CreateObject === "undefined") {
|
|||
/**
|
||||
* @FN {string} The name of the file.
|
||||
*/
|
||||
function __include__(FN) {
|
||||
if (FN.substr(FN.length - 3) !== '.js') FN += ".js";
|
||||
return eval(require.__load__(FN));
|
||||
function __evalFile__(FN) {
|
||||
try {
|
||||
return eval(require._load(FN));
|
||||
} catch (e) {
|
||||
console.error(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @FN {string} The name of the file.
|
||||
*/
|
||||
function require(pathname) {
|
||||
var cache = require.__cache__ = require.__cache__ || {};
|
||||
var cache = require._cache = require._cache || {};
|
||||
var suffix = (function(pos, s) {
|
||||
return pos < 0 ? '.' : s.substr(pos);
|
||||
return pos < 0 ? '.' : s.substring(pos);
|
||||
})(pathname.lastIndexOf('.'), pathname);
|
||||
var FN = pathname;
|
||||
|
||||
if ('.js$.jse$.coffee$.ls$.ts$.re$.res$.enc$'.indexOf(suffix + '$') < 0) FN += ".js";
|
||||
if (cache[FN]) return cache[FN];
|
||||
|
||||
// get file and directory name
|
||||
var __filename__ = (function(fileExists, path) {
|
||||
var filepaths = [
|
||||
FN, // default
|
||||
path.join(pathname, "index.js"), // default
|
||||
path.join(FN + '.enc'), // default (encrypted)
|
||||
path.join(pathname, 'index.js.enc'), // default (encrypted)
|
||||
path.join("Scripts", FN), // NuGet
|
||||
path.join("Scripts", pathname, "index.js"), // NuGet
|
||||
path.join("bower_components", FN), // Bower
|
||||
path.join("bower_components", pathname, "index.js"), // Bower
|
||||
path.join("node_modules", FN), // NPM
|
||||
path.join("node_modules", pathname, "index.js"), // NPM
|
||||
];
|
||||
var filename = filepaths[0];
|
||||
var T = null;
|
||||
var sep = '://', pos = FN.indexOf(sep);
|
||||
if (pos > -1) {
|
||||
var scheme = FN.substring(0, pos);
|
||||
|
||||
if (!fileExists(filename)) {
|
||||
for (var i = 1; i < filepaths.length; i++) {
|
||||
if (fileExists(filepaths[i])) {
|
||||
filename = filepaths[i];
|
||||
break;
|
||||
// load script from a remote server
|
||||
if (["http", "https"].indexOf(scheme) > -1) {
|
||||
require._addScriptProvider(function(url) {
|
||||
try {
|
||||
return require("lib/http").get(url);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// load script from LIE(Language Inference Engine) service
|
||||
if (["ai"].indexOf(scheme) > -1) {
|
||||
require._addScriptProvider(function(url) {
|
||||
try {
|
||||
var text = url.substring(pos + sep.length);
|
||||
return require("lib/language-inference-engine")
|
||||
.create()
|
||||
.setProvider("openai")
|
||||
.inference(text, 0)
|
||||
.join(' ')
|
||||
;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// if exists the custom script providers
|
||||
if (require._scriptProviders.length > 0) {
|
||||
var i = 0;
|
||||
while (T == null && i < require._scriptProviders.length) {
|
||||
try {
|
||||
T = require._scriptProviders[i](FN) || null;
|
||||
break;
|
||||
} catch (e) {
|
||||
T = null;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// load script from a local server
|
||||
var _filename = (function(fs, path) {
|
||||
var filepaths = [
|
||||
FN, // default
|
||||
path.join(pathname, "index.js"), // default
|
||||
path.join(FN + '.enc'), // default (encrypted)
|
||||
path.join(pathname, 'index.js.enc'), // default (encrypted)
|
||||
path.join("Scripts", FN), // NuGet
|
||||
path.join("Scripts", pathname, "index.js"), // NuGet
|
||||
path.join("bower_components", FN), // Bower
|
||||
path.join("bower_components", pathname, "index.js"), // Bower
|
||||
path.join("node_modules", FN), // NPM
|
||||
path.join("node_modules", pathname, "index.js"), // NPM
|
||||
];
|
||||
var filename = filepaths[0];
|
||||
|
||||
return filename;
|
||||
})(function(filename) {
|
||||
return CreateObject("Scripting.FileSystemObject").FileExists(filename);
|
||||
}, {
|
||||
join: function() {
|
||||
var result = arguments[0];
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
result += "\\" + arguments[i];
|
||||
var i = 0;
|
||||
while (!fs.existsSync(filename) && i < filepaths.length) {
|
||||
filename = filepaths[i];
|
||||
i++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
});
|
||||
var __dirname__ = (function(dirname) {
|
||||
var currentScriptDirectory = require.__getCurrentScriptDirectory__();
|
||||
return dirname.length > 0 ? currentScriptDirectory + "\\" + dirname : currentScriptDirectory;
|
||||
})(require.__getDirName__(__filename__));
|
||||
var T = require.__load__(__filename__);
|
||||
|
||||
// check the suffix again
|
||||
suffix = (function(pos, s) {
|
||||
return pos < 0 ? '.' : s.substr(pos);
|
||||
})(__filename__.lastIndexOf('.'), __filename__);
|
||||
return filename;
|
||||
})({
|
||||
existsSync: function(filename) {
|
||||
return CreateObject("Scripting.FileSystemObject").FileExists(filename);
|
||||
}
|
||||
}, {
|
||||
join: function() {
|
||||
var result = arguments[0];
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
result += "\\" + arguments[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
});
|
||||
var _dirname = (function(dirname) {
|
||||
var currentScriptDirectory = require._getCurrentScriptDirectory();
|
||||
return dirname.length > 0 ? currentScriptDirectory + "\\" + dirname : currentScriptDirectory;
|
||||
})(require._getDirName(_filename));
|
||||
T = require._load(_filename);
|
||||
|
||||
// check the suffix again
|
||||
suffix = (function(pos, s) {
|
||||
return pos < 0 ? '.' : s.substring(pos);
|
||||
})(_filename.lastIndexOf('.'), _filename);
|
||||
}
|
||||
|
||||
// transpile
|
||||
switch (suffix) {
|
||||
case '.coffee': // CoffeeScript 2
|
||||
T = require.__msie9__("app/assets/js/coffeescript-legacy-2.7.0.min", [T], function(p, w, d, l) {
|
||||
T = require._msie9("app/assets/js/coffeescript-legacy-2.7.0.min", [T], function(p, w, d, l) {
|
||||
return w.CoffeeScript.compile(p[0], {
|
||||
"header": true,
|
||||
"sourceMap": false,
|
||||
|
@ -283,8 +322,8 @@ function require(pathname) {
|
|||
break;
|
||||
|
||||
case ".ls": // LiveScript
|
||||
T = require.__msie9__("app/assets/js/livescript-1.6.1.min", [T, "app/assets/ls/prelude.ls"], function(p, w, d, l) {
|
||||
return w.require("livescript").compile(require.__load__(p[1]) + "\n\n" + p[0], {
|
||||
T = require._msie9("app/assets/js/livescript-1.6.1.min", [T, "app/assets/ls/prelude.ls"], function(p, w, d, l) {
|
||||
return w.require("livescript").compile(require._load(p[1]) + "\n\n" + p[0], {
|
||||
"header": true,
|
||||
"bare": true
|
||||
});
|
||||
|
@ -292,28 +331,33 @@ function require(pathname) {
|
|||
break;
|
||||
|
||||
case ".ts": // TypeScript
|
||||
T = require.__modernie__("app/assets/js/typescript-4.9.4", [T], function(p, w, d, l) {
|
||||
T = require._modernie("app/assets/js/typescript-4.9.4", [T], function(p, w, d, l) {
|
||||
return w.ts.transpile(p[0]);
|
||||
});
|
||||
break;
|
||||
|
||||
case ".re": // Rescript (aka. BuckleScript, ReasonML)
|
||||
case ".res":
|
||||
T = require.__modernie__("app/assets/js/rescript-compiler-10.1.2", [T], function(p, w, d, l) {
|
||||
T = require._modernie("app/assets/js/rescript-compiler-10.1.2", [T], function(p, w, d, l) {
|
||||
var compiler = w.rescript_compiler.make();
|
||||
var result = compiler.rescript.compile(p[0]);
|
||||
return result.js_code;
|
||||
});
|
||||
break;
|
||||
|
||||
case ".enc": // HIGHT(ISO/IEC 18033-3) encrypted
|
||||
T = (function(encryptedData, ToolkitInterface) {
|
||||
case ".enc": // encrypted script (require WelsonJS.Toolkit)
|
||||
T = (function(data, o) {
|
||||
try {
|
||||
var userKey = '';
|
||||
while (userKey.length == 0 || userKey.length > 16) {
|
||||
userKey = ToolkitInterface.Prompt("This file has been encrypted. Please enter the password:");
|
||||
var s = '', i = 0, k = 6;
|
||||
while (i < k && (s.length == 0 || s.length > 16)) {
|
||||
if (i > 0) {
|
||||
console.error("Invalid key length");
|
||||
}
|
||||
s = o.Prompt("This file has been encrypted. Please enter the password:");
|
||||
i++;
|
||||
}
|
||||
return ToolkitInterface.DecryptStringHIGHT(userKey, encryptedData);
|
||||
if (i == k) return '';
|
||||
return o.DecryptString(s, data);
|
||||
} catch (e) {
|
||||
console.error("Failed to load the encrypted data:", e.message);
|
||||
return '';
|
||||
|
@ -323,10 +367,10 @@ function require(pathname) {
|
|||
}
|
||||
|
||||
// compile
|
||||
T = "(function(global){var module=new require.__ModulePrototype__();return(function(exports,require,module,__filename,__dirname){"
|
||||
T = "(function(global){var module=new require.__Module__();return(function(exports,require,module,__filename,__dirname){"
|
||||
+ '"use strict";'
|
||||
+ T
|
||||
+ "\n\nreturn module.exports})(module.exports,global.require,module,__filename__,__dirname__)})(require.__global__);\n\n////@ sourceURL="
|
||||
+ "\n\nreturn module.exports})(module.exports,global.require,module,_filename,_dirname)})(require._global);\n\n////@ sourceURL="
|
||||
+ FN
|
||||
;
|
||||
|
||||
|
@ -350,35 +394,39 @@ function require(pathname) {
|
|||
|
||||
return cache[FN];
|
||||
}
|
||||
require.__global__ = this;
|
||||
require.__ModulePrototype__ = function() {
|
||||
require.__Module__ = function() {
|
||||
this.exports = {};
|
||||
};
|
||||
require.__getDirName__ = function(path) {
|
||||
require._global = this;
|
||||
require._getDirName = function(path) {
|
||||
var pos = Math.max.apply(null, [path.lastIndexOf("\\"), path.lastIndexOf("/")]);
|
||||
return (pos > -1 ? path.substring(0, pos) : "");
|
||||
};
|
||||
require.__getCurrentScriptDirectory__ = function() {
|
||||
if (typeof WScript !== "undefined") {
|
||||
if ("ScriptFullName" in WScript) {
|
||||
return require.__getDirName__(WScript.ScriptFullName);
|
||||
require._getCurrentScriptDirectory = function() {
|
||||
try {
|
||||
if (typeof WScript !== "undefined") {
|
||||
if ("ScriptFullName" in WScript) {
|
||||
return require._getDirName(WScript.ScriptFullName);
|
||||
} else {
|
||||
throw new Error("No detected an absolute path.");
|
||||
}
|
||||
} else if (typeof document !== "undefined") {
|
||||
return require._getDirName(document.location.pathname);
|
||||
} else {
|
||||
console.warn("Could not resolve an absolute path. Use the relative path.");
|
||||
return ".";
|
||||
throw new Error("No detected an absolute path.");
|
||||
}
|
||||
} else if (typeof document !== "undefined") {
|
||||
return require.__getDirName__(document.location.pathname);
|
||||
} else {
|
||||
console.warn("Could not resolve an absolute path. Use the relative path.");
|
||||
return ".";
|
||||
} catch (e) {
|
||||
console.warn(e.message, "Use the relative path.");
|
||||
}
|
||||
|
||||
return ".";
|
||||
};
|
||||
require.__load__ = function(FN) {
|
||||
require._load = function(FN) {
|
||||
// if empty
|
||||
if (FN == '') return '';
|
||||
|
||||
// get filename
|
||||
var __filename__ = require.__getCurrentScriptDirectory__() + "\\" + FN;
|
||||
var _filename = require._getCurrentScriptDirectory() + "\\" + FN;
|
||||
|
||||
// load script file
|
||||
// use ADODB.Stream instead of Scripting.FileSystemObject, because of supporting UTF-8 (Unicode)
|
||||
|
@ -387,7 +435,7 @@ require.__load__ = function(FN) {
|
|||
try {
|
||||
objStream.charSet = "utf-8";
|
||||
objStream.open();
|
||||
objStream.loadFromFile(__filename__);
|
||||
objStream.loadFromFile(_filename);
|
||||
T = objStream.readText();
|
||||
objStream.close();
|
||||
} catch (e) {
|
||||
|
@ -397,16 +445,16 @@ require.__load__ = function(FN) {
|
|||
|
||||
return T;
|
||||
};
|
||||
require.__msie9__ = function(FN, params, callback) {
|
||||
require._msie9 = function(FN, params, callback) {
|
||||
if (typeof FN !== "string" || FN == null) FN = '';
|
||||
else if (FN.substr(FN.length - 3) !== '.js') FN += ".js";
|
||||
else if (FN.substring(FN.length - 3) !== '.js') FN += ".js";
|
||||
|
||||
var exports = null;
|
||||
try {
|
||||
var T = require.__load__("app/assets/js/core-js-3.26.1.minified.js")
|
||||
+ "\n\n" + require.__load__("app/assets/js/html5shiv-printshiv-3.7.3.min.js")
|
||||
+ "\n\n" + require.__load__("app/assets/js/modernizr-2.8.3.min.js")
|
||||
+ "\n\n" + require.__load__(FN);
|
||||
var T = require._load("app/assets/js/core-js-3.26.1.minified.js")
|
||||
+ "\n\n" + require._load("app/assets/js/html5shiv-printshiv-3.7.3.min.js")
|
||||
+ "\n\n" + require._load("app/assets/js/modernizr-2.8.3.min.js")
|
||||
+ "\n\n" + require._load(FN);
|
||||
var htmlfile = CreateObject("htmlfile");
|
||||
htmlfile.write('<meta http-equiv="X-UA-Compatible" content="IE=9">');
|
||||
htmlfile.write('<script type="text/javascript">//<!--<![CDATA[\n' + T + '\n//]]>--></script>');
|
||||
|
@ -415,7 +463,7 @@ require.__msie9__ = function(FN, params, callback) {
|
|||
if (FN.indexOf('://') > -1) {
|
||||
htmlfile.write('<script type="text/javascript" src="' + FN + '"></script>');
|
||||
} else {
|
||||
htmlfile.write('<script type="text/javascript">//<!--<![CDATA[\n' + require.__load__(FN) + '\n//]]>--></script>');
|
||||
htmlfile.write('<script type="text/javascript">//<!--<![CDATA[\n' + require._load(FN) + '\n//]]>--></script>');
|
||||
}
|
||||
};
|
||||
//console.log(htmlfile.parentWindow.navigator.userAgent);
|
||||
|
@ -428,28 +476,28 @@ require.__msie9__ = function(FN, params, callback) {
|
|||
|
||||
return exports;
|
||||
};
|
||||
require.__modernie__ = function(FN, params, callback) {
|
||||
require._modernie = function(FN, params, callback) {
|
||||
if (typeof FN !== "string" || FN == null) FN = '';
|
||||
else if (FN.substr(FN.length - 3) !== '.js') FN += ".js";
|
||||
else if (FN.substring(FN.length - 3) !== '.js') FN += ".js";
|
||||
|
||||
var exports = null;
|
||||
try {
|
||||
var ua = '', T = '', htmlfile = CreateObject("htmlfile");
|
||||
|
||||
htmlfile.write('<meta http-equiv="X-UA-Compatible" content="IE=edge">');
|
||||
htmlfile.write('<script type="text/javascript">//<!--<![CDATA[\n\nfunction __getUserAgent__(){return window.navigator.userAgent}\n\n//]]>--></script>');
|
||||
ua = htmlfile.parentWindow.__getUserAgent__();
|
||||
htmlfile.write('<script type="text/javascript">//<!--<![CDATA[\n\nfunction __getUserAgent(){return window.navigator.userAgent}\n\n//]]>--></script>');
|
||||
ua = htmlfile.parentWindow.__getUserAgent();
|
||||
|
||||
if (ua.indexOf('Trident/ ')) {
|
||||
T = require.__load__("app/assets/js/core-js-3.26.1.minified.js")
|
||||
+ "\n\n" + require.__load__("app/assets/js/modernizr-2.8.3.min.js")
|
||||
+ "\n\n" + require.__load__("app/assets/js/babel-standalone-7.20.6.min.js")
|
||||
+ "\n\n" + require.__load__(FN);
|
||||
T = require._load("app/assets/js/core-js-3.26.1.minified.js")
|
||||
+ "\n\n" + require._load("app/assets/js/modernizr-2.8.3.min.js")
|
||||
+ "\n\n" + require._load("app/assets/js/babel-standalone-7.20.6.min.js")
|
||||
+ "\n\n" + require._load(FN);
|
||||
} else {
|
||||
T = require.__load__("app/assets/js/core-js-3.26.1.minified.js")
|
||||
+ "\n\n" + require.__load__("app/assets/js/html5shiv-printshiv-3.7.3.min.js")
|
||||
+ "\n\n" + require.__load__("app/assets/js/modernizr-2.8.3.min.js")
|
||||
+ "\n\n" + require.__load__(FN);
|
||||
T = require._load("app/assets/js/core-js-3.26.1.minified.js")
|
||||
+ "\n\n" + require._load("app/assets/js/html5shiv-printshiv-3.7.3.min.js")
|
||||
+ "\n\n" + require._load("app/assets/js/modernizr-2.8.3.min.js")
|
||||
+ "\n\n" + require._load(FN);
|
||||
}
|
||||
htmlfile.write('<script type="text/javascript">//<!--<![CDATA[\n' + T + '\n//]]>--></script>');
|
||||
|
||||
|
@ -458,7 +506,7 @@ require.__modernie__ = function(FN, params, callback) {
|
|||
if (src.indexOf('://') > -1) {
|
||||
htmlfile.write('<script type="text/javascript" src="' + src + '"></script>');
|
||||
} else {
|
||||
htmlfile.write('<script type="text/javascript">//<!--<![CDATA[\n' + require.__load__(src) + '\n//]]>--></script>');
|
||||
htmlfile.write('<script type="text/javascript">//<!--<![CDATA[\n' + require._load(src) + '\n//]]>--></script>');
|
||||
}
|
||||
};
|
||||
//console.log(htmlfile.parentWindow.navigator.userAgent);
|
||||
|
@ -471,6 +519,14 @@ require.__modernie__ = function(FN, params, callback) {
|
|||
|
||||
return exports;
|
||||
};
|
||||
require._scriptProviders = [];
|
||||
require._addScriptProvider = function(f) {
|
||||
if (typeof f === "function") {
|
||||
require._scriptProviders.push(f);
|
||||
} else {
|
||||
console.error("This is not an function");
|
||||
}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// Load script, and call app.main()
|
||||
|
@ -478,8 +534,8 @@ require.__modernie__ = function(FN, params, callback) {
|
|||
|
||||
function initializeConsole() {
|
||||
if (typeof WScript === "undefined") {
|
||||
console.error("Error, WScript is not defined");
|
||||
exit(1);
|
||||
console.error("This is not a console application");
|
||||
return;
|
||||
}
|
||||
|
||||
var argl = WScript.arguments.length;
|
||||
|
@ -492,9 +548,9 @@ function initializeConsole() {
|
|||
var app = require(name);
|
||||
if (app) {
|
||||
if (app.main) {
|
||||
var exitStatus = app.main.call(this, args);
|
||||
if (typeof exitStatus !== "undefined") {
|
||||
exit(exitStatus);
|
||||
var status = app.main.call(this, args);
|
||||
if (typeof status !== "undefined") {
|
||||
exit(status);
|
||||
}
|
||||
} else {
|
||||
console.error("Error, missing main entry point in", name);
|
||||
|
@ -507,43 +563,107 @@ function initializeConsole() {
|
|||
|
||||
function initializeWindow(name, args, w, h) {
|
||||
if (typeof window === "undefined") {
|
||||
console.error("Error, window is not defined");
|
||||
exit(1);
|
||||
console.error("This is not a GUI application");
|
||||
return;
|
||||
}
|
||||
var app = require(name);
|
||||
|
||||
// "set default size of window";
|
||||
// set default size of window
|
||||
if (typeof w !== "undefined" && typeof h !== "undefined") {
|
||||
window.resizeTo(w, h);
|
||||
}
|
||||
|
||||
// "load app";
|
||||
// load the application
|
||||
if (app) {
|
||||
if (app.main) {
|
||||
var exitStatus = app.main.call(app, args);
|
||||
if (exitStatus > 0) {
|
||||
exit(exitStatus);
|
||||
var status = app.main.call(app, args);
|
||||
if (status > 0) {
|
||||
exit(status);
|
||||
}
|
||||
} else {
|
||||
console.error("Error, missing main entry point in", name + ".js");
|
||||
exit(1);
|
||||
console.error("Missing main entry point in", name + ".js");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
console.error("Error, cannot find", name + ".js");
|
||||
exit(1);
|
||||
console.error("Could not find", name + ".js");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function dispatchServiceEvent(name, eventType, w_args, argl) {
|
||||
var app = require(name);
|
||||
var args = [];
|
||||
|
||||
// convert the arguments to Array
|
||||
for (var i = 0; i < argl; i++) {
|
||||
args.push(w_args(i));
|
||||
}
|
||||
|
||||
// load the service
|
||||
if (app) {
|
||||
var bind = function(eventType) {
|
||||
var event_callback_name = "on" + eventType;
|
||||
|
||||
if (event_callback_name in app && typeof app[event_callback_name] === "function")
|
||||
return app[event_callback_name];
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
return (function(action) {
|
||||
if (eventType in action) {
|
||||
try {
|
||||
return (function(f) {
|
||||
return (typeof f !== "function" ? null : f(args));
|
||||
})(action[eventType]);
|
||||
} catch (e) {
|
||||
console.error("Exception:", e.message);
|
||||
}
|
||||
}
|
||||
})({
|
||||
start: bind("ServiceStart"),
|
||||
stop: bind("ServiceStop"),
|
||||
elapsedTime: bind("ServiceElapsedTime"),
|
||||
screenNextTemplate: bind("ScreenNextTemplate"),
|
||||
screenTemplateMatched: bind("ScreenTemplateMatched"),
|
||||
fileCreated: bind("FileCreated"),
|
||||
networkConnected: bind("NetworkConnected"),
|
||||
registryModified: bind("RegistryModified"),
|
||||
avScanResult: bind("AvScanResult")
|
||||
});
|
||||
} else {
|
||||
console.error("Could not find", name + ".js");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Date.prototype.toISOString() polyfill for MSScriptControl.ScriptControl
|
||||
if (!Date.prototype.toISOString) {
|
||||
Date.prototype.toISOString = function() {
|
||||
var pad = function(number) {
|
||||
return number < 10 ? ('0' + number) : number;
|
||||
};
|
||||
return this.getUTCFullYear() +
|
||||
'-' + pad(this.getUTCMonth() + 1) +
|
||||
'-' + pad(this.getUTCDate()) +
|
||||
'T' + pad(this.getUTCHours()) +
|
||||
':' + pad(this.getUTCMinutes()) +
|
||||
':' + pad(this.getUTCSeconds()) +
|
||||
'.' + (this.getUTCMilliseconds() / 1000).toFixed(3).slice(2, 5) +
|
||||
'Z';
|
||||
};
|
||||
}
|
||||
|
||||
// JSON 2
|
||||
if (typeof JSON === "undefined") {
|
||||
__include__("app/assets/js/json2");
|
||||
__evalFile__("app/assets/js/json2.js");
|
||||
}
|
||||
|
||||
// core-js (aka. babel-polyfill)
|
||||
require("app/assets/js/core-js-3.26.1.minified");
|
||||
// core-js (formerly, babel-polyfill)
|
||||
require("app/assets/js/core-js-3.38.0.minified");
|
||||
|
||||
// Squel.js SQL query string builder for Javascript
|
||||
var squel = require("app/assets/js/squel-basic-5.13.0.hiddentao-afa1cb5.wsh");
|
||||
var squel = require("app/assets/js/squel-basic-5.13.0-afa1cb5.wsh");
|
||||
|
||||
// JavaScript YAML parser and dumper.
|
||||
var yaml = require("app/assets/js/js-yaml-4.1.0.wsh");
|
||||
|
@ -555,6 +675,15 @@ var is = require("app/assets/js/is-0.9.0.min");
|
|||
//var Intl = require("app/assets/js/Intl-1.2.5-e93b114.min");
|
||||
//console.log(new Intl.NumberFormat().format(1234567890.123456));
|
||||
|
||||
// numbers.js - Advanced Mathematics Library for Node.js and JavaScript
|
||||
var numbers = require("app/assets/js/numbers-0.7.0.wsh");
|
||||
|
||||
// linq.js - LINQ for JavaScript
|
||||
var Enumerable = require("app/assets/js/linq-4.0.2.wsh")._default;
|
||||
|
||||
// PEG.js: Parser generator for JavaScript
|
||||
var PEG = require("app/assets/js/peg-0.10.0");
|
||||
|
||||
// Dive into entrypoint
|
||||
function __main__() {
|
||||
console.log("");
|
||||
|
@ -564,8 +693,11 @@ function __main__() {
|
|||
console.log(" \\ V V / __/ \\__ \\ (_) | | | | |_| |___) |");
|
||||
console.log(" \\_/\\_/ \\___|_|___/\\___/|_| |_|\\___/|____/ ");
|
||||
console.log("");
|
||||
console.log(" WelsonJS - Build a Windows app on the Windows built-in JavaScript engine");
|
||||
console.log(" https://github.com/gnh1201/welsonjs");
|
||||
console.log(" WelsonJS - Build a Windows app on the Windows built-in JavaScript engine");
|
||||
console.log(" C-2021-000237 (cros.or.kr), 10.5281/zenodo.11382385 (doi.org), 2023-A0562 (oss.kr), Codename Macadamia");
|
||||
console.log(" This software is distributed as open source under the GPL 3.0 or MS-RL licenses.");
|
||||
console.log(" Please support this project: https://gnh1201.link");
|
||||
console.log(" Source code available: https://github.com/gnh1201/welsonjs");
|
||||
console.log("");
|
||||
|
||||
if (typeof window === "undefined") {
|
||||
|
|