Added save directory option to rncp

This commit is contained in:
Mark Qvist 2024-10-10 00:41:57 +02:00
parent 715f4d9fcb
commit 6fec76205c
1 changed files with 55 additions and 16 deletions

View File

@ -35,6 +35,7 @@ APP_NAME = "rncp"
allow_all = False allow_all = False
allow_fetch = False allow_fetch = False
fetch_jail = None fetch_jail = None
save_path = None
show_phy_rates = False show_phy_rates = False
allowed_identity_hashes = [] allowed_identity_hashes = []
@ -44,8 +45,8 @@ es = " "
erase_str = "\33[2K\r" erase_str = "\33[2K\r"
def listen(configdir, verbosity = 0, quietness = 0, allowed = [], display_identity = False, def listen(configdir, verbosity = 0, quietness = 0, allowed = [], display_identity = False,
limit = None, disable_auth = None, fetch_allowed = False, jail = None, announce = False): limit = None, disable_auth = None, fetch_allowed = False, jail = None, save = None, announce = False):
global allow_all, allow_fetch, allowed_identity_hashes, fetch_jail global allow_all, allow_fetch, allowed_identity_hashes, fetch_jail, save_path
from tempfile import TemporaryFile from tempfile import TemporaryFile
allow_fetch = fetch_allowed allow_fetch = fetch_allowed
@ -60,6 +61,20 @@ def listen(configdir, verbosity = 0, quietness = 0, allowed = [], display_identi
fetch_jail = os.path.abspath(os.path.expanduser(jail)) fetch_jail = os.path.abspath(os.path.expanduser(jail))
RNS.log("Restricting fetch requests to paths under \""+fetch_jail+"\"", RNS.LOG_VERBOSE) RNS.log("Restricting fetch requests to paths under \""+fetch_jail+"\"", RNS.LOG_VERBOSE)
if save != None:
sp = os.path.abspath(os.path.expanduser(save))
if os.path.isdir(sp):
if os.access(sp, os.W_OK):
save_path = sp
else:
RNS.log("Output directory not writable", RNS.LOG_ERROR)
exit(4)
else:
RNS.log("Output directory not found", RNS.LOG_ERROR)
exit(3)
RNS.log("Saving received files in \""+save_path+"\"", RNS.LOG_VERBOSE)
identity_path = RNS.Reticulum.identitypath+"/"+APP_NAME identity_path = RNS.Reticulum.identitypath+"/"+APP_NAME
if os.path.isfile(identity_path): if os.path.isfile(identity_path):
identity = RNS.Identity.from_file(identity_path) identity = RNS.Identity.from_file(identity_path)
@ -135,9 +150,11 @@ def listen(configdir, verbosity = 0, quietness = 0, allowed = [], display_identi
return REQ_FETCH_NOT_ALLOWED return REQ_FETCH_NOT_ALLOWED
if fetch_jail: if fetch_jail:
if data.startswith(fetch_jail+"/"):
data = data.replace(fetch_jail+"/", "")
file_path = os.path.abspath(os.path.expanduser(f"{fetch_jail}/{data}")) file_path = os.path.abspath(os.path.expanduser(f"{fetch_jail}/{data}"))
if not file_path.startswith(fetch_jail+"/"):
RNS.log(f"Disallowing fetch request for {file_path} outside of fetch jail {fetch_jail}", RNS.LOG_WARNING) RNS.log(f"Disallowing fetch request for {file_path} outside of fetch jail {fetch_jail}", RNS.LOG_WARNING)
if not file_path.startswith(fetch_jail):
return REQ_FETCH_NOT_ALLOWED return REQ_FETCH_NOT_ALLOWED
else: else:
file_path = os.path.abspath(os.path.expanduser(f"{data}")) file_path = os.path.abspath(os.path.expanduser(f"{data}"))
@ -240,7 +257,7 @@ def receive_resource_started(resource):
print("Starting resource transfer "+RNS.prettyhexrep(resource.hash)+id_str) print("Starting resource transfer "+RNS.prettyhexrep(resource.hash)+id_str)
def receive_resource_concluded(resource): def receive_resource_concluded(resource):
global fetch_jail global save_path
if resource.status == RNS.Resource.COMPLETE: if resource.status == RNS.Resource.COMPLETE:
print(str(resource)+" completed") print(str(resource)+" completed")
@ -249,9 +266,9 @@ def receive_resource_concluded(resource):
filename = resource.data.read(filename_len).decode("utf-8") filename = resource.data.read(filename_len).decode("utf-8")
counter = 0 counter = 0
if fetch_jail: if save_path:
saved_filename = os.path.abspath(os.path.expanduser(fetch_jail+"/"+filename)) saved_filename = os.path.abspath(os.path.expanduser(save_path+"/"+filename))
if not saved_filename.startswith(fetch_jail): if not saved_filename.startswith(save_path+"/"):
RNS.log(f"Invalid save path {saved_filename}, ignoring", RNS.LOG_ERROR) RNS.log(f"Invalid save path {saved_filename}, ignoring", RNS.LOG_ERROR)
return return
else: else:
@ -311,11 +328,23 @@ def sender_progress(resource):
resource_done = True resource_done = True
link = None link = None
def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = None, timeout = RNS.Transport.PATH_REQUEST_TIMEOUT, silent=False, phy_rates=False): def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = None, timeout = RNS.Transport.PATH_REQUEST_TIMEOUT, silent=False, phy_rates=False, save=None):
global current_resource, resource_done, link, speed, show_phy_rates global current_resource, resource_done, link, speed, show_phy_rates, save_path
targetloglevel = 3+verbosity-quietness targetloglevel = 3+verbosity-quietness
show_phy_rates = phy_rates show_phy_rates = phy_rates
if save:
sp = os.path.abspath(os.path.expanduser(save))
if os.path.isdir(sp):
if os.access(sp, os.W_OK):
save_path = sp
else:
RNS.log("Output directory not writable", RNS.LOG_ERROR)
exit(4)
else:
RNS.log("Output directory not found", RNS.LOG_ERROR)
exit(3)
try: try:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2 dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination) != dest_len: if len(destination) != dest_len:
@ -436,18 +465,25 @@ def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = No
def fetch_resource_concluded(resource): def fetch_resource_concluded(resource):
nonlocal resource_resolved, resource_status nonlocal resource_resolved, resource_status
global save_path
if resource.status == RNS.Resource.COMPLETE: if resource.status == RNS.Resource.COMPLETE:
if resource.total_size > 4: if resource.total_size > 4:
filename_len = int.from_bytes(resource.data.read(2), "big") filename_len = int.from_bytes(resource.data.read(2), "big")
filename = resource.data.read(filename_len).decode("utf-8") filename = resource.data.read(filename_len).decode("utf-8")
counter = 0 counter = 0
saved_filename = filename if save_path:
while os.path.isfile(saved_filename): saved_filename = os.path.abspath(os.path.expanduser(save_path+"/"+filename))
counter += 1
saved_filename = filename+"."+str(counter)
file = open(saved_filename, "wb") else:
saved_filename = filename
full_save_path = saved_filename
while os.path.isfile(full_save_path):
counter += 1
full_save_path = saved_filename+"."+str(counter)
file = open(full_save_path, "wb")
file.write(resource.data.read()) file.write(resource.data.read())
file.close() file.close()
resource_status = "completed" resource_status = "completed"
@ -751,8 +787,9 @@ def main():
parser.add_argument("-F", '--allow-fetch', action='store_true', default=False, help="allow authenticated clients to fetch files") parser.add_argument("-F", '--allow-fetch', action='store_true', default=False, help="allow authenticated clients to fetch files")
parser.add_argument("-f", '--fetch', action='store_true', default=False, help="fetch file from remote listener instead of sending") parser.add_argument("-f", '--fetch', action='store_true', default=False, help="fetch file from remote listener instead of sending")
parser.add_argument("-j", "--jail", metavar="path", action="store", default=None, help="restrict fetch requests to specified path", type=str) parser.add_argument("-j", "--jail", metavar="path", action="store", default=None, help="restrict fetch requests to specified path", type=str)
parser.add_argument("-s", "--save", metavar="path", action="store", default=None, help="save received files in specified path", type=str)
parser.add_argument("-b", action='store', metavar="seconds", default=-1, help="announce interval, 0 to only announce at startup", type=int) parser.add_argument("-b", action='store', metavar="seconds", default=-1, help="announce interval, 0 to only announce at startup", type=int)
parser.add_argument('-a', metavar="allowed_hash", dest="allowed", action='append', help="allow this identity", type=str) parser.add_argument('-a', metavar="allowed_hash", dest="allowed", action='append', help="allow this identity (or add in ~/.rncp/allowed_identities)", type=str)
parser.add_argument('-n', '--no-auth', action='store_true', default=False, help="accept requests from anyone") parser.add_argument('-n', '--no-auth', action='store_true', default=False, help="accept requests from anyone")
parser.add_argument('-p', '--print-identity', action='store_true', default=False, help="print identity and destination info and exit") parser.add_argument('-p', '--print-identity', action='store_true', default=False, help="print identity and destination info and exit")
parser.add_argument("-w", action="store", metavar="seconds", type=float, help="sender timeout before giving up", default=RNS.Transport.PATH_REQUEST_TIMEOUT) parser.add_argument("-w", action="store", metavar="seconds", type=float, help="sender timeout before giving up", default=RNS.Transport.PATH_REQUEST_TIMEOUT)
@ -770,6 +807,7 @@ def main():
allowed = args.allowed, allowed = args.allowed,
fetch_allowed = args.allow_fetch, fetch_allowed = args.allow_fetch,
jail = args.jail, jail = args.jail,
save = args.save,
display_identity=args.print_identity, display_identity=args.print_identity,
# limit=args.limit, # limit=args.limit,
disable_auth=args.no_auth, disable_auth=args.no_auth,
@ -787,6 +825,7 @@ def main():
timeout = args.w, timeout = args.w,
silent = args.silent, silent = args.silent,
phy_rates = args.phy_rates, phy_rates = args.phy_rates,
save = args.save,
) )
else: else:
print("") print("")