Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement password update when .password file changes (fixes #2) #23

Merged
merged 2 commits into from
Aug 21, 2021
Merged
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
224 changes: 213 additions & 11 deletions monitor.c
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,196 @@ inotify_remove_watches(int fd)
}
#endif

static int
update_password(const char *password_path)
{
typedef struct _node
{
char path[PATH_MAX];
struct _node *next;
} node;

int error = 0;
node head = { { 0 }, NULL };
node *tail = &head;
node *current = &head;
char *password = NULL;

DPRINTF(E_INFO, L_PASSWORD, "Updating password file: %s.\n", password_path);

// Retrieve new password from file
if( access(password_path, 0) == 0 )
{
password = malloc(PASSWORD_ARRAY_LEN);
if( password == NULL )
{
error = -1;
goto clear;
}

readPassword(password_path, password, PASSWORD_ARRAY_LEN);

if ( password[0] == '\0' )
{
free(password);
password = NULL;
}
}

// Copy dirname of password_path
memcpy(head.path, password_path, strlen(password_path) - strlen(PASSWORD_FILE) - 1);

// Check if directory has an object representation
if ( sql_get_text_field(db,
"SELECT OBJECT_ID from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)"
" where d.PATH = '%q' and REF_ID is NULL", head.path) )
{
// Check if password changed
char *db_password = sql_get_text_field(db,
"SELECT PASSWORD from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)"
" where d.PATH = '%q' and REF_ID is NULL", head.path);
if ( db_password == NULL && password == NULL )
{
DPRINTF(E_DEBUG, L_PASSWORD, "Directory: %s, password is the same\n", head.path);
goto clear;
}
if ( db_password != NULL && password != NULL && strcmp(db_password, password) == 0 )
{
DPRINTF(E_DEBUG, L_PASSWORD, "Directory: %s, password is the same\n", head.path);
goto clear;
}
}
// Check if directory exists in db
else if (sql_get_text_field(db,
"SELECT ID from DETAILS where PATH = '%q'", head.path) == NULL )
{
DPRINTF(E_DEBUG, L_PASSWORD, "Directory: %s, isn't present in DB\n", head.path);
goto clear;
}

// When password is null look for a password file in an upper directory
if( password == NULL )
{
char path[PATH_MAX], *dirpath = path;
if((password = malloc(PASSWORD_ARRAY_LEN)) == NULL )
{
error = -1;
goto clear;
}

password[0] = '\0';
strcpy(dirpath, head.path);
while ( password[0] == '\0' &&
sql_get_text_field(db, "SELECT ID from DETAILS where PATH = '%q'",
(dirpath = dirname(dirpath))) != NULL )
{
char password_path[PATH_MAX];
int length = snprintf(password_path, PATH_MAX, "%s/"PASSWORD_FILE, dirpath);
if ( length > 0 && length < PATH_MAX && access(password_path, 0) == 0 )
readPassword(password_path, password, PASSWORD_ARRAY_LEN);

// Stop on root directory
if (dirpath[0] == '/' && dirpath[1] == '\0')
break;
}

if ( password[0] == '\0' )
{
free(password);
password = NULL;
}
}

DPRINTF(E_DEBUG, L_PASSWORD, "Directory: %s, new password: %s\n",
head.path, password == NULL ? "(null)" : password);

// Populate diretory list
while ( current != NULL )
{
DIR* srcdir = opendir(current->path);
struct dirent* dent;

if (srcdir == NULL)
continue;

while( (dent = readdir(srcdir)) != NULL )
{
int length;
char password_path[PATH_MAX];
struct stat st;

// Ignore current and previous directory entries
if( dent->d_name[0] == '\0' || (dent->d_name[0] == '.' && (
dent->d_name[1] == '\0' || (dent->d_name[1] == '.' && dent->d_name[2] == '\0'))) )
continue;

if ( fstatat(dirfd(srcdir), dent->d_name, &st, 0) < 0 )
continue;

if ( !S_ISDIR(st.st_mode) )
continue;

// Ignore subdirectory that have a password file
length = snprintf(password_path, PATH_MAX, "%s/%s/"PASSWORD_FILE, current->path, dent->d_name);
if (length <= 0 || PATH_MAX <= length || access(password_path, 0) == 0 )
continue;

// Add subdirectory to list
tail->next = malloc(sizeof(node));
if( tail->next == NULL )
{
error = -1;
goto clear;
}

length = snprintf(tail->next->path, PATH_MAX, "%s/%s", current->path, dent->d_name);
if (length <= 0 || PATH_MAX <= length)
{
free(tail->next);
tail->next = NULL;
continue;
}

tail->next->next = NULL;
tail = tail->next;
DPRINTF(E_DEBUG, L_PASSWORD, "Propagate password change to subdirectory: %s\n", tail->path);
}
closedir(srcdir);
current = current->next;
}

current = &head;
while( current != NULL )
{
DPRINTF(E_DEBUG, L_PASSWORD, "Updating DB record for: %s\n", current->path);
// Update password for current directory
if( sql_exec(db, "UPDATE OBJECTS set PASSWORD = '%s' where DETAIL_ID in "
"(SELECT ID from DETAILS where PATH = '%s')", password, current->path) != SQLITE_OK )
DPRINTF(E_ERROR, L_DB_SQL, "Failed to update password for directory: %s!\n", current->path);
// Update password for all files in directory
else if (sql_exec(db, "UPDATE OBJECTS set PASSWORD = '%s' where DETAIL_ID in "
"( SELECT I FROM ( SELECT ID as I, substr(PATH, length('%s/') + 1)"
" as P from DETAILS where PATH glob '%s/*' ) WHERE P NOT LIKE '%%/%%' ) "
"and CLASS != 'container.storageFolder'", password, current->path, current->path) != SQLITE_OK )
DPRINTF(E_ERROR, L_DB_SQL, "Failed to update password for files in directory: %s!\n", current->path);

current = current->next;
}

clear:
if ( password != NULL )
free(password);

while ( head.next != NULL )
{
current = head.next;
head.next = current->next;
free(current);
}

return error;
}

int
monitor_remove_file(const char * path)
{
Expand Down Expand Up @@ -357,9 +547,9 @@ monitor_insert_file(const char *name, const char *path)
char *id = NULL;
char video[PATH_MAX];
const char *tbl = "DETAILS";
char * dir_password = NULL;
char password_buf[11];
char file_password[11] = {0};
char *dir_password = NULL;
char password_buf[PASSWORD_ARRAY_LEN] = {0};
char file_password[PASSWORD_ARRAY_LEN] = {0};
char password_path[PATH_MAX];
int depth = 1;
int ts;
Expand Down Expand Up @@ -437,17 +627,25 @@ monitor_insert_file(const char *name, const char *path)
id = sql_get_text_field(db, "SELECT OBJECT_ID from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)"
" where d.PATH = '%q' and REF_ID is NULL", parent_buf);

snprintf(password_path, PATH_MAX, "%s/.password", parent_buf);
if( access(password_path, 0) == 0 )
if( id )
{
readPassword(password_path, password_buf, 11);
dir_password = password_buf;
}
else if( id )
dir_password = sql_get_text_field(db, "select PASSWORD from OBJECTS where OBJECT_ID='%s'", id);
if ( dir_password != NULL && dir_password[0] == '\0' )
dir_password = NULL;
}
else // Only relevant for root directory
{
snprintf(password_path, PATH_MAX, "%s/"PASSWORD_FILE, parent_buf);
if( access(password_path, 0) == 0 )
{
readPassword(password_path, password_buf, PASSWORD_ARRAY_LEN);
if ( password_buf[0] != '\0' )
dir_password = password_buf;
}
}

// Save first directory password seen as file password
if (dir_password != NULL && file_password[0] == '\0');
if ( dir_password != NULL && file_password[0] == '\0' )
strcpy(file_password, dir_password);

if( id )
Expand Down Expand Up @@ -709,13 +907,17 @@ start_inotify(void)
struct inotify_event * event = (struct inotify_event *) &buffer[i];
if( event->len )
{
snprintf(path_buf, sizeof(path_buf), "%s/%s", get_path_from_wd(event->wd), event->name);
if( *(event->name) == '.' )
{
// Handle password file changes
if ( strcmp(event->name, PASSWORD_FILE) == 0 )
if (update_password(path_buf) != 0)
DPRINTF(E_ERROR, L_PASSWORD, "Failed to update password.\n");
i += EVENT_SIZE + event->len;
continue;
}
esc_name = modifyString(strdup(event->name), "&", "&amp;amp;", 0);
snprintf(path_buf, sizeof(path_buf), "%s/%s", get_path_from_wd(event->wd), event->name);
if ( event->mask & IN_ISDIR && (event->mask & (IN_CREATE|IN_MOVED_TO)) )
{
DPRINTF(E_DEBUG, L_INOTIFY, "The directory %s was %s.\n",
Expand Down
5 changes: 5 additions & 0 deletions monitor_kqueue.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ dir_vnode_process(struct event *ev, u_int fflags)
wt = (struct watch *)ev->data;
path = wt->path;

// Handle password file changes
if ( strcmp(path + strlen(path) - strlen(PASSWORD_FILE), PASSWORD_FILE) == 0 )
if (update_password(path) != 0)
DPRINTF(E_ERROR, L_PASSWORD, "Failed to update password.\n");

if (fflags & NOTE_DELETE) {
DPRINTF(E_DEBUG, L_INOTIFY, "Path [%s] deleted.\n", path);
close(ev->fd);
Expand Down
6 changes: 3 additions & 3 deletions scanner.c
Original file line number Diff line number Diff line change
Expand Up @@ -769,7 +769,7 @@ ScanDirectory(const char *dir, const char *parent, media_types dir_types, const
int i, n, startID = 0;
char *full_path;
char *name = NULL;
char password[11];
char password[PASSWORD_ARRAY_LEN];
static long long unsigned int fileno = 0;
enum file_types type;

Expand Down Expand Up @@ -822,9 +822,9 @@ ScanDirectory(const char *dir, const char *parent, media_types dir_types, const
startID = get_next_available_id("OBJECTS", BROWSEDIR_ID);
}

snprintf(full_path, PATH_MAX, "%s/.password", dir);
snprintf(full_path, PATH_MAX, "%s/"PASSWORD_FILE, dir);
if (access(full_path, 0) == 0) {
readPassword(full_path, password, 11);
readPassword(full_path, password, PASSWORD_ARRAY_LEN);
} else {
strcpy(password, currentPassword);
}
Expand Down
4 changes: 4 additions & 0 deletions upnpglobalvars.h
Original file line number Diff line number Diff line change
Expand Up @@ -245,4 +245,8 @@ extern volatile short int quitting;
extern volatile uint32_t updateID;
extern const char *force_sort_criteria;

/* password */
#define PASSWORD_FILE ".password"
#define PASSWORD_ARRAY_LEN 11

#endif