fzx2 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fzx2-1.0.0.dist-info/METADATA +82 -0
- fzx2-1.0.0.dist-info/RECORD +5 -0
- fzx2-1.0.0.dist-info/WHEEL +4 -0
- fzx2-1.0.0.dist-info/entry_points.txt +2 -0
- script/fzx2.py +208 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fzx2
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: 507EX executable format version 2.0
|
|
5
|
+
Author-email: Wyatt Brashear <admin@wyattb.dev>
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Requires-Dist: flask~=3.1.2
|
|
9
|
+
Requires-Dist: requests>=2.32.5
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# 507ex 2.0 (FZX2)
|
|
13
|
+
## What is 507ex, and why is there a 2.0?
|
|
14
|
+
A while back, I created a executable format called 507ex (507 Being my brand that I like to put things under). I made one
|
|
15
|
+
mistake on the first time. I used AI in my code. That completely ruined my code. So I decided it was time to rewrite it.
|
|
16
|
+
Introducing 507ex 2.0 (Abbreviated as FZX2).
|
|
17
|
+
### What's New?
|
|
18
|
+
- AI was not used to write code (yay)
|
|
19
|
+
- Enhanced metadata that stores more information
|
|
20
|
+
- Auto Dependency Manager. Uses a list embedded in the metadata to ensure the application has the correct dependencies.
|
|
21
|
+
- All operations are accessible through one module.
|
|
22
|
+
- CAR (Central Application Repository) now supports authentication.
|
|
23
|
+
## CAR (Central Application Repository)
|
|
24
|
+
CAR is a server system that allows seamless execution of applications from the command line.
|
|
25
|
+
Example:
|
|
26
|
+
~~~bash
|
|
27
|
+
python3 fzx2.py exec http://127.0.0.1:5000/pull/955d7c5f-5b21-428f-94e4-8e13f1e7223c
|
|
28
|
+
~~~
|
|
29
|
+
Easy as that! All CAR operations are authenticated by a random "secret code" generated by the server.
|
|
30
|
+
### Endpoint Documentation
|
|
31
|
+
POST: /push. Push an executable to the server. Accepts form data in the following form: {'file_id': STR}, and accepts file data in this form: {'file': FILE}.
|
|
32
|
+
Returns:
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"status": "success",
|
|
36
|
+
"secret_code": "secret_code",
|
|
37
|
+
"url": "request.url_root/pull/filename"
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
POST: /pull/<file_id>. Pull an executable from the server. Accepts form data in the following form: {'secret_code': STR}
|
|
41
|
+
Returns:
|
|
42
|
+
The file requested or an error message. Files are sent using flask's send_from_directory function.
|
|
43
|
+
### Running the server
|
|
44
|
+
Source Code Method:
|
|
45
|
+
~~~bash
|
|
46
|
+
python3 fzx2.py start_server
|
|
47
|
+
~~~
|
|
48
|
+
Compiled Executable Method:
|
|
49
|
+
~~~bash
|
|
50
|
+
./fzx2 start_server
|
|
51
|
+
~~~
|
|
52
|
+
## Installation
|
|
53
|
+
### Method 1 (Compiled Executable)
|
|
54
|
+
1. Download the latest release from the releases page.
|
|
55
|
+
2. Run the executable. (Recommended to be done in a terminal)
|
|
56
|
+
### Method 2 (Source Code)
|
|
57
|
+
1. Clone the Repository using `git clone https://github.com/WyattBrashear/507ex-utils2.git`
|
|
58
|
+
2. Run: `pip install -r requirements.txt` in order to install the dependencies.
|
|
59
|
+
3. Run the main script using `python3 fzx2.py`
|
|
60
|
+
## CLI Usage
|
|
61
|
+
`python3 fzx2.py <operation> <path>`
|
|
62
|
+
All operations are listed below:
|
|
63
|
+
1. exec: Executes an executable
|
|
64
|
+
2. start_server: Starts the CAR server
|
|
65
|
+
3. build: Builds an executable
|
|
66
|
+
4. unpack: Unpacks an executable
|
|
67
|
+
5. upload: Uploads an executable
|
|
68
|
+
## 507ex's required files
|
|
69
|
+
All directories that are going to be turned into a 507ex requires 2 files:
|
|
70
|
+
1. runfile (contains the command to run the script)
|
|
71
|
+
2. dependfile
|
|
72
|
+
Dependfile is in this format
|
|
73
|
+
~~~text
|
|
74
|
+
!{name}|{command}
|
|
75
|
+
!PLATFORM {platform}
|
|
76
|
+
{dependencies}
|
|
77
|
+
~~~
|
|
78
|
+
## Demo Video
|
|
79
|
+
https://drive.google.com/file/d/1QfDZL2Spc7WKqbfCU_m7xSnRLbJdnfNd/view?usp=sharing
|
|
80
|
+
## AI Usage
|
|
81
|
+
As per usual, I am against using AI in my code. All the code in this repository was human written. The instances of AI
|
|
82
|
+
usage was autocomplete and AI Assistant when I was stuck, pointing me in the right direction (fzx2.py).
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
script/fzx2.py,sha256=3tTazvcqDiRoAEGaX7Aml72m5xUMUSYVT2t1C449luY,8217
|
|
2
|
+
fzx2-1.0.0.dist-info/METADATA,sha256=NzalqP7Atu3gdTn_B10JwQ0_DOWnhXNYd8-KvAh_3ak,3251
|
|
3
|
+
fzx2-1.0.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
4
|
+
fzx2-1.0.0.dist-info/entry_points.txt,sha256=E891uRz1tI50-cK3XnuBs2lQBBps07GsNomHnMW-O1o,42
|
|
5
|
+
fzx2-1.0.0.dist-info/RECORD,,
|
script/fzx2.py
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import hashlib
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import random
|
|
6
|
+
import shutil
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
9
|
+
import uuid
|
|
10
|
+
import zipfile
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
|
|
13
|
+
import requests
|
|
14
|
+
from flask import Flask, request, send_from_directory
|
|
15
|
+
from werkzeug.utils import secure_filename
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
app = Flask(__name__)
|
|
19
|
+
@app.route('/push', methods=['POST'])
|
|
20
|
+
def push():
|
|
21
|
+
print(os.getcwd())
|
|
22
|
+
filename = secure_filename(request.form.get('file_id'))
|
|
23
|
+
with open(os.path.join("storage", f"{filename}.507ex"), 'wb') as f:
|
|
24
|
+
f.write(request.files['file'].read())
|
|
25
|
+
with open(os.path.join("storage", f"{filename}.json"), 'w') as f:
|
|
26
|
+
secret_code = random.randint(100000, 999999)
|
|
27
|
+
data = {
|
|
28
|
+
'secret_code': hashlib.sha256(str(secret_code).encode()).hexdigest(),
|
|
29
|
+
}
|
|
30
|
+
json.dump(data, f)
|
|
31
|
+
return {
|
|
32
|
+
'status': 'success',
|
|
33
|
+
'secret_code': str(secret_code),
|
|
34
|
+
'url': f'{request.url_root}pull/{filename}'
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@app.route('/pull/<file_id>', methods=['POST'])
|
|
38
|
+
def pull(file_id):
|
|
39
|
+
try:
|
|
40
|
+
with open(os.path.join("storage", f"{file_id}.json"), 'r') as json_file:
|
|
41
|
+
codefile = json.load(json_file)
|
|
42
|
+
secret_code = codefile['secret_code']
|
|
43
|
+
except FileNotFoundError:
|
|
44
|
+
return 'File not found', 404
|
|
45
|
+
if request.form.get('secret_code') == secret_code:
|
|
46
|
+
return send_from_directory('storage', f"{file_id}.507ex", as_attachment=True)
|
|
47
|
+
else:
|
|
48
|
+
return 'Invalid secret code', 401
|
|
49
|
+
#Alright. Lets rewrite this format!
|
|
50
|
+
def build(directory: str):
|
|
51
|
+
#Check for a runfile in the directory
|
|
52
|
+
if not os.path.exists(os.path.join(directory, 'runfile')):
|
|
53
|
+
raise FileNotFoundError('No Runfile Detected!')
|
|
54
|
+
depend_file = False
|
|
55
|
+
if os.path.exists(os.path.join(directory, 'dependfile')):
|
|
56
|
+
depend_file = True
|
|
57
|
+
with open(os.path.join(directory, 'dependfile'), 'r') as f:
|
|
58
|
+
dependencies = f.read()
|
|
59
|
+
shutil.make_archive(directory, 'zip', directory)
|
|
60
|
+
os.rename(f"{directory}.zip", os.path.join(f"{directory}.507ex"))
|
|
61
|
+
with open (os.path.join(f"{directory}.507ex"), 'rb') as f:
|
|
62
|
+
exec_contents = f.read()
|
|
63
|
+
#Calculate hash
|
|
64
|
+
hashfunc = hashlib.new('blake2s')
|
|
65
|
+
with open(os.path.join(f"{directory}.507ex"), 'rb') as f:
|
|
66
|
+
while chunk := f.read(8192):
|
|
67
|
+
hashfunc.update(chunk)
|
|
68
|
+
exec_hash = hashfunc.hexdigest()
|
|
69
|
+
|
|
70
|
+
with open(os.path.join(f"{directory}.507ex"), 'wb') as f:
|
|
71
|
+
f.write("FZX2".encode())
|
|
72
|
+
f.write("\n!507EX-METADATA".encode())
|
|
73
|
+
f.write(f"\n507ex-hash|{exec_hash}".encode())
|
|
74
|
+
f.write(f"\n507ex-hashmode|blake2s".encode())
|
|
75
|
+
f.write(f"\n507ex-id|{uuid.uuid4()}".encode())
|
|
76
|
+
#DTOC - Date/Time of Creation
|
|
77
|
+
f.write(f"\n507ex-dtoc|{datetime.now().now()}".encode())
|
|
78
|
+
f.write(f"\n507ex-depends|{depend_file}".encode())
|
|
79
|
+
f.write(f"\n!507EX-DEPENDENCIES\n{dependencies}".encode())
|
|
80
|
+
f.write("\n!507EX-END-META\n".encode())
|
|
81
|
+
f.write(exec_contents)
|
|
82
|
+
print(f"Successfully built {directory}.507ex")
|
|
83
|
+
|
|
84
|
+
def execute(path: str):
|
|
85
|
+
print(path)
|
|
86
|
+
current_path = os.getcwd()
|
|
87
|
+
reading_depends = False
|
|
88
|
+
has_depends = False
|
|
89
|
+
dependency_platform = ''
|
|
90
|
+
fromcar = False
|
|
91
|
+
#CAR Server logic
|
|
92
|
+
if path.startswith("http://") or path.startswith("https://"):
|
|
93
|
+
r = requests.post(path, data={'secret_code': hashlib.sha256(str(input("Please enter the secret code: \n")).encode()).hexdigest()})
|
|
94
|
+
if r.status_code == 401:
|
|
95
|
+
print("Your Secret Code is invalid!")
|
|
96
|
+
with open("tmp.507ex", 'wb') as f:
|
|
97
|
+
f.write(r.content)
|
|
98
|
+
fromcar = True
|
|
99
|
+
path = "tmp.507ex"
|
|
100
|
+
with open(path, 'rb') as f:
|
|
101
|
+
if f.readline() != b'FZX2\n':
|
|
102
|
+
raise ValueError('Invalid Executable!')
|
|
103
|
+
line_counter = 0
|
|
104
|
+
for line in f:
|
|
105
|
+
if line.startswith(b'!507EX-END-META'):
|
|
106
|
+
break
|
|
107
|
+
if line.startswith(b'507ex-hash|'):
|
|
108
|
+
exec_hash = line.split(b'|')[1].decode().replace('\n', '')
|
|
109
|
+
if line.startswith(b'507ex-hashmode'):
|
|
110
|
+
exec_hashmode = line.split(b'|')[1].decode().replace('\n', '')
|
|
111
|
+
if line.startswith(b'507ex-id'):
|
|
112
|
+
exec_id = line.split(b'|')[1].decode().replace('\n', '')
|
|
113
|
+
if line.startswith(b'507ex-depends'):
|
|
114
|
+
if line.split(b'|')[1].decode() == 'True\n':
|
|
115
|
+
if input("Executable has dependencies that it wants to install. Continue? (y/n)\n").lower() == 'y':
|
|
116
|
+
has_depends = True
|
|
117
|
+
else:
|
|
118
|
+
raise ValueError('Aborted!')
|
|
119
|
+
else:
|
|
120
|
+
has_depends = False
|
|
121
|
+
if reading_depends:
|
|
122
|
+
if line.startswith(b'!') and not line.startswith(b'!PLATFORM'):
|
|
123
|
+
command = line.split(b'|')[1].decode().replace('\n', '')
|
|
124
|
+
if dependency_platform == sys.platform or dependency_platform == '*':
|
|
125
|
+
arg = line.decode().replace('\n', '')
|
|
126
|
+
subprocess.run(f"{command} {arg}", shell=True, check=False)
|
|
127
|
+
if line.startswith(b'!PLATFORM'):
|
|
128
|
+
dependency_platform = line.decode().replace('\n', '').replace('!PLATFORM ', '')
|
|
129
|
+
if line.startswith(b'!507EX-DEPENDENCIES'):
|
|
130
|
+
if has_depends:
|
|
131
|
+
reading_depends = True
|
|
132
|
+
line_counter += 1
|
|
133
|
+
os.makedirs(os.path.join(current_path, '.fzx2-runtime', exec_id), exist_ok=True)
|
|
134
|
+
#Hash the executable
|
|
135
|
+
line_counter +=2
|
|
136
|
+
with open(path, 'rb') as f:
|
|
137
|
+
lines = b''.join(f.readlines()[line_counter:])
|
|
138
|
+
file_hash = hashlib.new(exec_hashmode, lines).hexdigest()
|
|
139
|
+
pass_hashcheck = False
|
|
140
|
+
if file_hash == exec_hash:
|
|
141
|
+
pass_hashcheck = True
|
|
142
|
+
else:
|
|
143
|
+
raise ValueError('Hash Verification Failed. Executable may have been damaged.')
|
|
144
|
+
os.chdir(os.path.join(current_path, '.fzx2-runtime', exec_id))
|
|
145
|
+
with zipfile.ZipFile(os.path.join(current_path, path), 'r') as zippy:
|
|
146
|
+
zippy.extractall()
|
|
147
|
+
with open("runfile", 'r') as runfile:
|
|
148
|
+
runfile_contents = runfile.read()
|
|
149
|
+
try:
|
|
150
|
+
subprocess.run(runfile_contents, shell=True, check=False)
|
|
151
|
+
except:
|
|
152
|
+
print("Exiting 507ex enviornment...")
|
|
153
|
+
#cleanup
|
|
154
|
+
os.chdir('..')
|
|
155
|
+
shutil.rmtree(os.path.join(current_path, '.fzx2-runtime', exec_id))
|
|
156
|
+
if fromcar:
|
|
157
|
+
os.remove(os.path.join(current_path, "tmp.507ex"))
|
|
158
|
+
|
|
159
|
+
def upload(path: str):
|
|
160
|
+
if not os.path.exists(path):
|
|
161
|
+
raise FileNotFoundError('File not found!')
|
|
162
|
+
url = input("Please enter the server address: \n")
|
|
163
|
+
with open(path, 'rb') as f:
|
|
164
|
+
for line in f:
|
|
165
|
+
if line.startswith(b'507ex-id|'):
|
|
166
|
+
exec_id = line.split(b'|')[1].decode()
|
|
167
|
+
break
|
|
168
|
+
with open(path, 'rb') as f:
|
|
169
|
+
r = requests.post(f"{url}/push", files={'file': f}, data={'file_id': exec_id})
|
|
170
|
+
json_data = r.json()
|
|
171
|
+
print('Upload Complete!')
|
|
172
|
+
print(f"Upload URL: {json_data['url']}")
|
|
173
|
+
print(f"Your Secret Code Is: {json_data['secret_code']}")
|
|
174
|
+
def unpack(path: str):
|
|
175
|
+
os.mkdir(path)
|
|
176
|
+
os.chdir(path)
|
|
177
|
+
with zipfile.ZipFile(path, 'r') as zippy:
|
|
178
|
+
zippy.extractall()
|
|
179
|
+
|
|
180
|
+
def main():
|
|
181
|
+
parser = argparse.ArgumentParser()
|
|
182
|
+
parser.add_argument('mode', choices=['build', 'upload', 'exec', 'unpack', 'start_server'],
|
|
183
|
+
help='The operation to perform.')
|
|
184
|
+
parser.add_argument('path', help='The path to the Executable or folder')
|
|
185
|
+
args = parser.parse_args(sys.argv[1:])
|
|
186
|
+
if args.mode == 'build':
|
|
187
|
+
build(args.path)
|
|
188
|
+
if args.mode == 'exec':
|
|
189
|
+
try:
|
|
190
|
+
execute(args.path)
|
|
191
|
+
except KeyboardInterrupt:
|
|
192
|
+
print("Exiting 507ex enviornment...")
|
|
193
|
+
if os.path.exists("tmp.507ex"):
|
|
194
|
+
os.remove(f"tmp.507ex")
|
|
195
|
+
except Exception as e:
|
|
196
|
+
print(f"An error occured while executing the executable: {e}")
|
|
197
|
+
if args.mode == 'upload':
|
|
198
|
+
upload(args.path)
|
|
199
|
+
if args.mode == 'unpack':
|
|
200
|
+
unpack(args.path)
|
|
201
|
+
if args.mode == 'start_server':
|
|
202
|
+
try:
|
|
203
|
+
os.mkdir(os.path.join('.', 'storage'))
|
|
204
|
+
except FileExistsError:
|
|
205
|
+
pass
|
|
206
|
+
app.run()
|
|
207
|
+
if __name__ == '__main__':
|
|
208
|
+
main()
|