diff --git a/src/sbbs3/userdat.c b/src/sbbs3/userdat.c
index 44f6079cef58e0b753a7dc5541d7e7c10506ab84..e3c7513cea5ce8af89ade7987df20dca17c00ca3 100644
--- a/src/sbbs3/userdat.c
+++ b/src/sbbs3/userdat.c
@@ -2981,6 +2981,75 @@ BOOL can_user_post(scfg_t* cfg, uint subnum, user_t* user, client_t* client, uin
 	return TRUE;
 }
 
+/****************************************************************************/
+/* Determine if the specified user can or cannot access the specified dir	*/
+/****************************************************************************/
+BOOL can_user_access_dir(scfg_t* cfg, uint dirnum, user_t* user, client_t* client)
+{
+	if(!VALID_CFG(cfg))
+		return FALSE;
+	if(dirnum>=cfg->total_dirs)
+		return FALSE;
+	if(!chk_ar(cfg,cfg->lib[cfg->dir[dirnum]->lib]->ar,user,client))
+		return FALSE;
+	if(!chk_ar(cfg,cfg->dir[dirnum]->ar,user,client))
+		return FALSE;
+
+	return TRUE;
+}
+
+/****************************************************************************/
+/* Determine if the specified user can or cannot upload files to the dirnum	*/
+/* 'reason' is an (optional) pointer to a text.dat item number, indicating	*/
+/* the reason the user cannot post, when returning FALSE.					*/
+/****************************************************************************/
+BOOL can_user_upload(scfg_t* cfg, uint dirnum, user_t* user, client_t* client, uint* reason)
+{
+	if(reason!=NULL)
+		*reason=NoAccessDir;
+	if(!can_user_access_dir(cfg, dirnum, user, client))
+		return FALSE;
+	if(reason!=NULL)
+		*reason=R_Upload;
+	if(user->rest&FLAG('U'))			/* upload restriction? */
+		return FALSE;
+	if(user->rest&FLAG('T'))			/* transfer restriction? */
+		return FALSE;
+	if(!(user->exempt&FLAG('U'))		/* upload exemption */
+		&& !is_user_dirop(cfg, dirnum, user, client)) {
+		if(reason!=NULL)
+			*reason=CantUploadHere;
+		if(!chk_ar(cfg, cfg->dir[dirnum]->ul_ar, user, client))
+			return FALSE;
+	}
+	return TRUE;
+}
+
+/****************************************************************************/
+/* Determine if the specified user can or cannot download files from dirnum	*/
+/* 'reason' is an (optional) pointer to a text.dat item number, indicating	*/
+/* the reason the user cannot post, when returning FALSE.					*/
+/****************************************************************************/
+BOOL can_user_download(scfg_t* cfg, uint dirnum, user_t* user, client_t* client, uint* reason)
+{
+	if(reason!=NULL)
+		*reason=NoAccessDir;
+	if(!can_user_access_dir(cfg, dirnum, user, client))
+		return FALSE;
+	if(reason!=NULL)
+		*reason=CantDownloadFromDir;
+	if(!chk_ar(cfg,cfg->dir[dirnum]->dl_ar,user,client))
+		return FALSE;
+	if(reason!=NULL)
+		*reason=R_Download;
+	if(user->rest&FLAG('D'))			/* download restriction? */
+		return FALSE;
+	if(user->rest&FLAG('T'))			/* transfer restriction? */
+		return FALSE;
+
+	return TRUE;
+}
+
 /****************************************************************************/
 /* Determine if the specified user can or cannot send email					*/
 /* 'reason' is an (optional) pointer to a text.dat item number				*/
@@ -3029,6 +3098,21 @@ BOOL is_user_subop(scfg_t* cfg, uint subnum, user_t* user, client_t* client)
 	return cfg->sub[subnum]->op_ar!=NULL && cfg->sub[subnum]->op_ar[0]!=0 && chk_ar(cfg,cfg->sub[subnum]->op_ar,user,client);
 }
 
+/****************************************************************************/
+/* Determine if the specified user is a directory operator					*/
+/****************************************************************************/
+BOOL is_user_dirop(scfg_t* cfg, uint dirnum, user_t* user, client_t* client)
+{
+	if(user==NULL)
+		return FALSE;
+	if(!can_user_access_dir(cfg, dirnum, user, client))
+		return FALSE;
+	if(user->level >= SYSOP_LEVEL)
+		return TRUE;
+
+	return cfg->dir[dirnum]->op_ar!=NULL && cfg->dir[dirnum]->op_ar[0]!=0 && chk_ar(cfg,cfg->dir[dirnum]->op_ar,user,client);
+}
+
 /****************************************************************************/
 /* Determine if downloads from the specified directory are free for the		*/
 /* specified user															*/
diff --git a/src/sbbs3/userdat.h b/src/sbbs3/userdat.h
index 3d78be70fee126a278b332544473294eda98c73f..4014af73a015c1d588a3bf4656625f1d6e6a4aca 100644
--- a/src/sbbs3/userdat.h
+++ b/src/sbbs3/userdat.h
@@ -90,11 +90,15 @@ DLLEXPORT BOOL	logoutuserdat(scfg_t*, user_t*, time_t now, time_t logontime);
 DLLEXPORT void	resetdailyuserdat(scfg_t*, user_t*, BOOL write);
 DLLEXPORT void	subtract_cdt(scfg_t*, user_t*, long amt);
 DLLEXPORT int	user_rec_len(int offset);
+DLLEXPORT BOOL	can_user_access_dir(scfg_t*, uint dirnum, user_t*, client_t* client);
 DLLEXPORT BOOL	can_user_access_sub(scfg_t*, uint subnum, user_t*, client_t* client);
 DLLEXPORT BOOL	can_user_read_sub(scfg_t*, uint subnum, user_t*, client_t* client);
 DLLEXPORT BOOL	can_user_post(scfg_t*, uint subnum, user_t*, client_t* client, uint* reason);
+DLLEXPORT BOOL	can_user_upload(scfg_t*, uint dirnum, user_t*, client_t* client, uint* reason);
+DLLEXPORT BOOL	can_user_download(scfg_t*, uint dirnum, user_t*, client_t* client, uint* reason);
 DLLEXPORT BOOL	can_user_send_mail(scfg_t*, enum smb_net_type, uint usernumber, user_t*, uint* reason);
 DLLEXPORT BOOL	is_user_subop(scfg_t*, uint subnum, user_t*, client_t* client);
+DLLEXPORT BOOL	is_user_dirop(scfg_t*, uint dirnum, user_t*, client_t* client);
 DLLEXPORT BOOL	is_download_free(scfg_t*, uint dirnum, user_t*, client_t* client);
 DLLEXPORT BOOL	is_host_exempt(scfg_t*, const char* ip_addr, const char* host_name);
 DLLEXPORT BOOL	filter_ip(scfg_t*, const char* prot, const char* reason, const char* host