Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@ This extension requires Python3.8 and above to be installed on your system.
## AccessOwnership
This extension modifies access permissions & ownership on downloaded files.<br />
Supports multiple categories and to configure access permissions & ownership separately for each category.<br />
Built in testfunction to test entered values.
Built in function to test entered values.<br />
Built in function to get UID and GID for NZBGet instance.


## OS Support (so far)
Linux / Debian12
Linux / Debian13<br />
MacOS / 14<br />
Docker

## Installation
For manual installation, unpack 'Extension-AccessOwnership-1.4.zip' into NZBget's scripts folder.<br />
For manual installation, unpack 'Extension-AccessOwnership-1.6.zip' into NZBget's scripts folder.<br />
Follow instructions here:<br />
https://nzbget.com/documentation/extension-scripts
212 changes: 130 additions & 82 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,112 +9,155 @@
import grp

# Exit codes used by NZBGet
POSTPROCESS_SUCCESS=93
POSTPROCESS_ERROR=94
SCRIPT_SUCCESS=93
SCRIPT_ERROR=94

# Debug messages from Extension
#check_OS=
check_SETTINGS="Check instructions in the extension settings"
CHECK_SETTINGS="Check instructions in the extension settings"

# Check if all required script config options are present in config file
required_options = ('NZBPO_DESTDIR', 'NZBPO_ACCESS', 'NZBPO_OWNER', 'NZBPO_GROUP')
required_options = ('NZBPO_DESTDIR', 'NZBPO_ACCESS', 'NZBPO_OWNER', 'NZBPO_GROUP', 'NZBPO_OVERRIDEID')
for optname in required_options:
if (not optname in os.environ):
print('[ERROR] Option %s is missing in configuration file. Please check script settings' % optname[6:])
sys.exit(POSTPROCESS_ERROR)
if (optname not in os.environ):
print(f"[ERROR] Option {optname[6:]} is missing in configuration file. Please check script settings")
sys.exit(SCRIPT_ERROR)

# Check if the script is executed from settings page with a custom command
command = os.environ.get("NZBCP_COMMAND")
detect_mode = command == "Detect"
test_mode = command == "Test"
if command != None and not test_mode:
if command is not None and not any((detect_mode,test_mode)):
print('[ERROR] Invalid command ' + command)
sys.exit(COMMAND_ERROR)
sys.exit(SCRIPT_ERROR)

# Override and debugging
overrideid = os.environ['NZBPO_OVERRIDEID'];

# Detecting users Owner[UID] and Group[GID], executed from settings page
if detect_mode:
try:
if pwd.getpwuid(os.getuid())[2] is not None:
print("User UID is detected:",[pwd.getpwuid(os.getuid())[2]],)
else:
print("Cannot detect logged in users UID")
check = SCRIPT_ERROR

if pwd.getpwuid(os.getuid())[3] is not None:
print("Group GID is detected:",[pwd.getpwuid(os.getuid())[3]],)
else:
print("Cannot detect logged in users GID")
check = SCRIPT_ERROR

try:
check
except NameError:
sys.exit(SCRIPT_SUCCESS)
else:
sys.exit(check)
except KeyError:
print("The requested key was not found.")

# Counters for added Categories
countCategory = 1
countcategory = 1
for i in range(1, 100):
if os.environ.get("NZBOP_Category" + str(i) + ".Name") is not None:
countCategory +=1
countCategoryExt = 1
countcategory +=1
countcategoryext = 1
for i in range(1, 100):
if os.environ.get("NZBPO_CategoryExt" + str(i) + ".Name") is not None:
countCategoryExt +=1

countcategoryext +=1

# Testing the validity of settings, executed from settings page
if test_mode:
if not os.environ.get("NZBPO_DestDir") == os.environ.get("NZBOP_DestDir"):
print("Default Category: Invalid Path:",[os.environ.get("NZBPO_DestDir")],check_SETTINGS)
check = POSTPROCESS_ERROR
if not re.match('^[0-7]{3}+$', os.environ.get("NZBPO_Access")):
print("Default Category: Invalid Access[mask]:",[os.environ.get("NZBPO_Access")],check_SETTINGS)
check = POSTPROCESS_ERROR

if not re.match('^[0-9]*$', os.environ.get("NZBPO_Owner")):
print("Default Category: Invalid Owner[UID]:",[os.environ.get("NZBPO_Owner")],check_SETTINGS)
check = POSTPROCESS_ERROR
print("Default Category: Invalid Path:",[os.environ.get("NZBPO_DestDir")],CHECK_SETTINGS)
check = SCRIPT_ERROR
if not re.match('^[0-7]{3}$', os.environ.get("NZBPO_Access")):
print("Default Category: Invalid Access[mask]:",[os.environ.get("NZBPO_Access")],CHECK_SETTINGS)
check = SCRIPT_ERROR

if not re.match('^[0-9]{1,}$', os.environ.get("NZBPO_Owner")):
print("Default Category: Invalid Owner[UID]:",[os.environ.get("NZBPO_Owner")],CHECK_SETTINGS)
check = SCRIPT_ERROR
else:
try:
pwd.getpwuid(int(os.environ.get("NZBPO_Owner")))
except KeyError:
print("Default Category: Invalid Owner[UID]:",[os.environ.get("NZBPO_Owner")],"User not in system.",check_SETTINGS)
check = POSTPROCESS_ERROR
if overrideid == "yes":
print("Default Category: Invalid Owner[UID]:",[os.environ.get("NZBPO_Owner")],"<- ENABLED!!")
else:
print("Default Category: Invalid Owner[UID]:",[os.environ.get("NZBPO_Owner")],"User not in system.",CHECK_SETTINGS)
check = SCRIPT_ERROR

if not re.match('^[0-9]*$', os.environ.get("NZBPO_Group")):
print("Default Category: Invalid Group[GID]:",[os.environ.get("NZBPO_Group")],check_SETTINGS)
check = POSTPROCESS_ERROR
if not re.match('^[0-9]{1,}$', os.environ.get("NZBPO_Group")):
print("Default Category: Invalid Group[GID]:",[os.environ.get("NZBPO_Group")],CHECK_SETTINGS)
check = SCRIPT_ERROR
else:
try:
grp.getgrgid(int(os.environ.get("NZBPO_Group")))
except KeyError:
print("Default Category: Invalid Group[UID]: Group[UID]",[os.environ.get("NZBPO_Group")],"Group not in system.",check_SETTINGS)
check = POSTPROCESS_ERROR
if overrideid == "yes":
print("Default Category: Invalid Group[GID]:",[os.environ.get("NZBPO_Group")],"<- ENABLED!!")
else:
print("Default Category: Invalid Group[GID]:",[os.environ.get("NZBPO_Group")],"Group not in system.",CHECK_SETTINGS)
check = SCRIPT_ERROR

for i in range(1, countCategoryExt):
for i in range(1, countcategoryext):
if os.environ.get("NZBPO_CategoryExt" + str(i) + ".Name") is not None:
catextname = os.environ["NZBPO_CategoryExt" + str(i) + ".Name"];
catextdestdir = os.environ["NZBPO_CategoryExt" + str(i) + ".DestDir"];
catextaccess = os.environ["NZBPO_CategoryExt" + str(i) + ".Access"];
catextowner = os.environ["NZBPO_CategoryExt" + str(i) + ".Owner"];
catextgroup = os.environ["NZBPO_CategoryExt" + str(i) + ".Group"];
if not re.match('^[0-7]{3}+$', catextaccess):
print(catextname,"Category: Invalid Access[mask]:",[catextaccess],check_SETTINGS)
check = POSTPROCESS_ERROR

if not re.match('^[0-9]*$', catextowner):
print(catextname,"Category: Invalid Owner[UID]:",[catextowner],check_SETTINGS)
check = POSTPROCESS_ERROR
if not re.match('^[0-7]{3}$', catextaccess):
print(catextname,"Category: Invalid Access[mask]:",[catextaccess],CHECK_SETTINGS)
check = SCRIPT_ERROR

if not re.match('^[0-9]{1,}$', catextowner):
print(catextname,"Category: Invalid Owner[UID]:",[catextowner],CHECK_SETTINGS)
check = SCRIPT_ERROR
else:
try:
pwd.getpwuid(int(catextowner))
except KeyError:
print(catextname,"Category: Invalid Owner[UID]:",[catextowner],"User not in system.",check_SETTINGS)
check = POSTPROCESS_ERROR

if not re.match('^[0-9]*$', catextgroup):
print(catextname,"Category: Invalid Group[UID]:",[catextgroup],check_SETTINGS)
check = POSTPROCESS_ERROR
if overrideid == "yes":
print(catextname,"Category: Invalid Owner[UID]:",[catextowner],"User not in system.","<- ENABLED!!")
else:
print(catextname,"Category: Invalid Owner[UID]:",[catextowner],"User not in system.",CHECK_SETTINGS)
check = SCRIPT_ERROR

if not re.match('^[0-9]{1,}$', catextgroup):
print(catextname,"Category: Invalid Group[UID]:",[catextgroup],CHECK_SETTINGS)
check = SCRIPT_ERROR
else:
try:
grp.getgrgid(int(catextgroup))
except KeyError:
print(catextname,"Category: Invalid Group[UID]:",[catextgroup],"Group not in system.",check_SETTINGS)
check = POSTPROCESS_ERROR

countCategoryName = 1
for i in range(1, countCategory):
if catextname == os.environ.get("NZBOP_Category" + str(i) + ".Name") is not None:
if overrideid == "yes":
print(catextname,"Category: Invalid Group[UID]:",[catextgroup],"Group not in system.","<- ENABLED!!")
else:
print(catextname,"Category: Invalid Group[UID]:",[catextgroup],"Group not in system.",CHECK_SETTINGS)
check = SCRIPT_ERROR

for i in range(1, countcategory):
if catextname == os.environ.get("NZBOP_Category" + str(i) + ".Name"):
if not catextdestdir == os.environ.get("NZBOP_Category" + str(i) + ".DestDir"):
print(catextname,"Category: Invalid Path:",[catextdestdir],check_SETTINGS)
check = POSTPROCESS_ERROR
else:
countCategoryName +=1
if countCategoryName >= countCategoryExt:
print(catextname,"Category: Invalid CategoryExt Name:",[catextname],"Name not same as in CATEGORIES",check_SETTINGS)
check = POSTPROCESS_ERROR
print(catextname,"Category: Invalid Path:",[catextdestdir],CHECK_SETTINGS)
check = SCRIPT_ERROR

catnames = []
for i in range(1, countcategory):
catname = os.environ.get("NZBOP_Category" + str(i) + ".Name")
catnames.append(catname)
if catextname not in catnames:
print(catextname,"Category: Invalid CategoryExt Name:",[catextname],"Name not same as in CATEGORIES.",CHECK_SETTINGS)
check = SCRIPT_ERROR

try:
check
except NameError:
sys.exit(POSTPROCESS_SUCCESS)
sys.exit(SCRIPT_SUCCESS)
else:
sys.exit(check)

Expand All @@ -127,7 +170,7 @@
catextaccess = os.environ.get("NZBPO_CATEGORYEXT" + str(i) + "_ACCESS")
catextowner = os.environ.get("NZBPO_CATEGORYEXT" + str(i) + "_OWNER")
catextgroup = os.environ.get("NZBPO_CATEGORYEXT" + str(i) + "_GROUP")
if catextname == None or catextdestdir == None:
if catextname is None or catextdestdir is None:
break
categories.append({catextname, catextdestdir, catextaccess, catextowner, catextgroup})

Expand All @@ -141,40 +184,45 @@
group = os.environ['NZBPO_GROUP'];


# If download is an added category, set the related destination dir
# If download is an added category, read in the related values and dir
if not category == "":
for i in range(1, countCategoryExt):
if category == os.environ.get("NZBPO_CategoryExt" + str(i) + ".Name"):
destdir = os.environ["NZBPO_CategoryExt" + str(i) + ".DestDir"];
access = os.environ["NZBPO_CategoryExt" + str(i) + ".Access"];
owner = os.environ["NZBPO_CategoryExt" + str(i) + ".Owner"];
group = os.environ["NZBPO_CategoryExt" + str(i) + ".Group"];

for i in range(1, countcategoryext):
if category == os.environ.get("NZBPO_CategoryExt" + str(i) + ".Name"):
destdir = os.environ["NZBPO_CategoryExt" + str(i) + ".DestDir"];
access = os.environ["NZBPO_CategoryExt" + str(i) + ".Access"];
owner = os.environ["NZBPO_CategoryExt" + str(i) + ".Owner"];
group = os.environ["NZBPO_CategoryExt" + str(i) + ".Group"];

# Check if input values are valid
if not re.match('^[0-7]{3}+$', access):
print(category,"Category: Invalid Access[mask]:",[access],check_SETTINGS)
sys.exit(POSTPROCESS_ERROR)
if not re.match('^[0-7]{3}$', access):
print(category,"Category: Invalid Access[mask]:",[access],CHECK_SETTINGS)
sys.exit(SCRIPT_ERROR)

if not re.match('^[0-9]*$', owner):
print(category,"Category: Invalid Owner[UID]:",[owner],check_SETTINGS)
sys.exit(POSTPROCESS_ERROR)
if not re.match('^[0-9]{1,}$', owner):
print(category,"Category: Invalid Owner[UID]:",[owner],CHECK_SETTINGS)
sys.exit(SCRIPT_ERROR)
else:
try:
pwd.getpwuid(int(owner))
except KeyError:
print(category,"Category: Invalid Owner[UID]:",[owner],"User not in system.",check_SETTINGS)
sys.exit(POSTPROCESS_ERROR)

if not re.match('^[0-9]*$', group):
print(category,"Category: Invalid Group[UID]:",[group],check_SETTINGS)
sys.exit(POSTPROCESS_ERROR)
if overrideid == "yes":
print(category,"Category: Invalid Owner[UID]:",[owner],"User not in system.","<- ENABLED!!")
else:
print(category,"Category: Invalid Owner[UID]:",[owner],"User not in system.",CHECK_SETTINGS)
sys.exit(SCRIPT_ERROR)

if not re.match('^[0-9]{1,}$', group):
print(category,"Category: Invalid Group[UID]:",[group],CHECK_SETTINGS)
sys.exit(SCRIPT_ERROR)
else:
try:
grp.getgrgid(int(group))
except KeyError:
print(category,"Category: Invalid Group[UID]:",[group],"Group not in system.",check_SETTINGS)
sys.exit(POSTPROCESS_ERROR)
if overrideid == "yes":
print(category,"Category: Invalid Group[UID]:",[group],"Group not in system.","<- ENABLED!!")
else:
print(category,"Category: Invalid Group[UID]:",[group],"Group not in system.",CHECK_SETTINGS)
sys.exit(SCRIPT_ERROR)


# Apply chmod & chown to files in destination dir
Expand All @@ -193,4 +241,4 @@
for f in files :
os.chown(os.path.join(root,f), int(owner), int(group))

sys.exit(POSTPROCESS_SUCCESS)
sys.exit(SCRIPT_SUCCESS)
Loading