mirror of
				https://github.com/simon987/sist2.git
				synced 2025-11-04 01:36:51 +00:00 
			
		
		
		
	Manual tagging
This commit is contained in:
		
							parent
							
								
									8127745f2b
								
							
						
					
					
						commit
						1d9fcf7105
					
				@ -18,7 +18,7 @@ sist2 (Simple incremental search tool)
 | 
				
			|||||||
* Extracts text and metadata from common file types \*
 | 
					* Extracts text and metadata from common file types \*
 | 
				
			||||||
* Generates thumbnails \*
 | 
					* Generates thumbnails \*
 | 
				
			||||||
* Incremental scanning
 | 
					* Incremental scanning
 | 
				
			||||||
* Automatic tagging from file attributes via [user scripts](docs/scripting.md)
 | 
					* Manual tagging from the UI and automatic tagging based on file attributes via [user scripts](docs/scripting.md)
 | 
				
			||||||
* Recursive scan inside archive files \*\*
 | 
					* Recursive scan inside archive files \*\*
 | 
				
			||||||
* OCR support with tesseract \*\*\*
 | 
					* OCR support with tesseract \*\*\*
 | 
				
			||||||
* Stats page & disk utilisation visualization
 | 
					* Stats page & disk utilisation visualization
 | 
				
			||||||
 | 
				
			|||||||
@ -15,6 +15,7 @@
 | 
				
			|||||||
    * [rewrite_url](#rewrite_url)
 | 
					    * [rewrite_url](#rewrite_url)
 | 
				
			||||||
    * [link to specific indices](#link-to-specific-indices)
 | 
					    * [link to specific indices](#link-to-specific-indices)
 | 
				
			||||||
* [exec-script](#exec-script)
 | 
					* [exec-script](#exec-script)
 | 
				
			||||||
 | 
					* [tagging](#tagging)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
Usage: sist2 scan [OPTION]... PATH
 | 
					Usage: sist2 scan [OPTION]... PATH
 | 
				
			||||||
@ -57,6 +58,7 @@ Web options
 | 
				
			|||||||
    --es-url=<str>                Elasticsearch url. DEFAULT=http://localhost:9200
 | 
					    --es-url=<str>                Elasticsearch url. DEFAULT=http://localhost:9200
 | 
				
			||||||
    --bind=<str>                  Listen on this address. DEFAULT=localhost:4090
 | 
					    --bind=<str>                  Listen on this address. DEFAULT=localhost:4090
 | 
				
			||||||
    --auth=<str>                  Basic auth in user:password format
 | 
					    --auth=<str>                  Basic auth in user:password format
 | 
				
			||||||
 | 
					    --tag-auth=<str>              Basic auth in user:password format for tagging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Exec-script options
 | 
					Exec-script options
 | 
				
			||||||
    --script-file=<str>           Path to user script.
 | 
					    --script-file=<str>           Path to user script.
 | 
				
			||||||
@ -145,7 +147,10 @@ documents.idx/
 | 
				
			|||||||
├── agg_mime.csv
 | 
					├── agg_mime.csv
 | 
				
			||||||
├── agg_date.csv
 | 
					├── agg_date.csv
 | 
				
			||||||
├── add_size.csv
 | 
					├── add_size.csv
 | 
				
			||||||
└── thumbs
 | 
					├── thumbs
 | 
				
			||||||
 | 
					|   ├── data.mdb
 | 
				
			||||||
 | 
					|   └── lock.mdb
 | 
				
			||||||
 | 
					└── tags
 | 
				
			||||||
    ├── data.mdb
 | 
					    ├── data.mdb
 | 
				
			||||||
    └── lock.mdb
 | 
					    └── lock.mdb
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
@ -270,6 +275,8 @@ sist2 index --print ./my_index/ | jq | less
 | 
				
			|||||||
 * `--es-url=<str>` Elasticsearch url.
 | 
					 * `--es-url=<str>` Elasticsearch url.
 | 
				
			||||||
 * `--bind=<str>` Listen on this address.
 | 
					 * `--bind=<str>` Listen on this address.
 | 
				
			||||||
 * `--auth=<str>` Basic auth in user:password format
 | 
					 * `--auth=<str>` Basic auth in user:password format
 | 
				
			||||||
 | 
					 * `--tag-auth=<str>` Basic auth in user:password format. Works the same way as the 
 | 
				
			||||||
 | 
					    `--auth` argument, but authentication is only applied the `/tag/` endpoint.
 | 
				
			||||||
 
 | 
					 
 | 
				
			||||||
### Web examples
 | 
					### Web examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -301,3 +308,31 @@ not displayed.
 | 
				
			|||||||
## exec-script
 | 
					## exec-script
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The `exec-script` command is used to execute a user script for an index that has already been imported to Elasticsearch with the `index` command. Note that the documents will not be reset to their default state before each execution as the `index` command does: if you make undesired changes to the documents by accident, you will need to run `index` again to revert to the original state.
 | 
					The `exec-script` command is used to execute a user script for an index that has already been imported to Elasticsearch with the `index` command. Note that the documents will not be reset to their default state before each execution as the `index` command does: if you make undesired changes to the documents by accident, you will need to run `index` again to revert to the original state.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Tagging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Manual tagging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You can modify tags of individual documents directly from the 
 | 
				
			||||||
 | 
					 `web` interface. Note that you can setup authentication for this feature
 | 
				
			||||||
 | 
					 with the `--tag-auth` option (See [web options](#web-options))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Tags that are manually added are saved both in the 
 | 
				
			||||||
 | 
					 index folder (in `/tags/`) and in Elasticsearch*. When re-`index`ing, 
 | 
				
			||||||
 | 
					 they are read from the index and automatically applied.
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					You can safely copy the `/tags/` database to another index.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					See [Automatic tagging](#automatic-tagging) for information about tag 
 | 
				
			||||||
 | 
					 hierarchies and tag colors.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					\* *It can take a few seconds to take effect in new search queries, and the page needs 
 | 
				
			||||||
 | 
					    to be reloaded for the tag tab to update*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Automatic tagging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					See [scripting](docs/scripting.md) documentation.
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								docs/manual_tag.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/manual_tag.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 3.9 KiB  | 
							
								
								
									
										28
									
								
								src/cli.c
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								src/cli.c
									
									
									
									
									
								
							@ -326,7 +326,7 @@ int web_args_validate(web_args_t *args, int argc, const char **argv) {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        strncpy(args->auth_user, args->credentials, (ptr - args->credentials));
 | 
					        strncpy(args->auth_user, args->credentials, (ptr - args->credentials));
 | 
				
			||||||
        strcpy(args->auth_pass, ptr);
 | 
					        strcpy(args->auth_pass, ptr + 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (strlen(args->auth_user) == 0) {
 | 
					        if (strlen(args->auth_user) == 0) {
 | 
				
			||||||
            fprintf(stderr, "--auth username must be at least one character long");
 | 
					            fprintf(stderr, "--auth username must be at least one character long");
 | 
				
			||||||
@ -338,6 +338,31 @@ int web_args_validate(web_args_t *args, int argc, const char **argv) {
 | 
				
			|||||||
        args->auth_enabled = FALSE;
 | 
					        args->auth_enabled = FALSE;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (args->tag_credentials != NULL && args->credentials != NULL) {
 | 
				
			||||||
 | 
					        fprintf(stderr, "--auth and --tag-auth are mutually exclusive");
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (args->tag_credentials != NULL) {
 | 
				
			||||||
 | 
					        char *ptr = strstr(args->tag_credentials, ":");
 | 
				
			||||||
 | 
					        if (ptr == NULL) {
 | 
				
			||||||
 | 
					            fprintf(stderr, "Invalid --tag-auth format, see usage\n");
 | 
				
			||||||
 | 
					            return 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        strncpy(args->auth_user, args->tag_credentials, (ptr - args->tag_credentials));
 | 
				
			||||||
 | 
					        strcpy(args->auth_pass, ptr + 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (strlen(args->auth_user) == 0) {
 | 
				
			||||||
 | 
					            fprintf(stderr, "--tag-auth username must be at least one character long");
 | 
				
			||||||
 | 
					            return 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        args->tag_auth_enabled = TRUE;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        args->tag_auth_enabled = FALSE;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    args->index_count = argc - 1;
 | 
					    args->index_count = argc - 1;
 | 
				
			||||||
    args->indices = argv + 1;
 | 
					    args->indices = argv + 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -352,6 +377,7 @@ int web_args_validate(web_args_t *args, int argc, const char **argv) {
 | 
				
			|||||||
    LOG_DEBUGF("cli.c", "arg es_url=%s", args->es_url)
 | 
					    LOG_DEBUGF("cli.c", "arg es_url=%s", args->es_url)
 | 
				
			||||||
    LOG_DEBUGF("cli.c", "arg listen=%s", args->listen_address)
 | 
					    LOG_DEBUGF("cli.c", "arg listen=%s", args->listen_address)
 | 
				
			||||||
    LOG_DEBUGF("cli.c", "arg credentials=%s", args->credentials)
 | 
					    LOG_DEBUGF("cli.c", "arg credentials=%s", args->credentials)
 | 
				
			||||||
 | 
					    LOG_DEBUGF("cli.c", "arg tag_credentials=%s", args->tag_credentials)
 | 
				
			||||||
    LOG_DEBUGF("cli.c", "arg auth_user=%s", args->auth_user)
 | 
					    LOG_DEBUGF("cli.c", "arg auth_user=%s", args->auth_user)
 | 
				
			||||||
    LOG_DEBUGF("cli.c", "arg auth_pass=%s", args->auth_pass)
 | 
					    LOG_DEBUGF("cli.c", "arg auth_pass=%s", args->auth_pass)
 | 
				
			||||||
    LOG_DEBUGF("cli.c", "arg index_count=%d", args->index_count)
 | 
					    LOG_DEBUGF("cli.c", "arg index_count=%d", args->index_count)
 | 
				
			||||||
 | 
				
			|||||||
@ -48,9 +48,11 @@ typedef struct web_args {
 | 
				
			|||||||
    char *es_url;
 | 
					    char *es_url;
 | 
				
			||||||
    char *listen_address;
 | 
					    char *listen_address;
 | 
				
			||||||
    char *credentials;
 | 
					    char *credentials;
 | 
				
			||||||
 | 
					    char *tag_credentials;
 | 
				
			||||||
    char auth_user[256];
 | 
					    char auth_user[256];
 | 
				
			||||||
    char auth_pass[256];
 | 
					    char auth_pass[256];
 | 
				
			||||||
    int auth_enabled;
 | 
					    int auth_enabled;
 | 
				
			||||||
 | 
					    int tag_auth_enabled;
 | 
				
			||||||
    int index_count;
 | 
					    int index_count;
 | 
				
			||||||
    const char **indices;
 | 
					    const char **indices;
 | 
				
			||||||
} web_args_t;
 | 
					} web_args_t;
 | 
				
			||||||
 | 
				
			|||||||
@ -60,6 +60,8 @@ typedef struct {
 | 
				
			|||||||
    char *es_url;
 | 
					    char *es_url;
 | 
				
			||||||
    int batch_size;
 | 
					    int batch_size;
 | 
				
			||||||
    tpool_t *pool;
 | 
					    tpool_t *pool;
 | 
				
			||||||
 | 
					    store_t *tag_store;
 | 
				
			||||||
 | 
					    GHashTable *tags;
 | 
				
			||||||
} IndexCtx_t;
 | 
					} IndexCtx_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct {
 | 
					typedef struct {
 | 
				
			||||||
@ -68,6 +70,7 @@ typedef struct {
 | 
				
			|||||||
    char *auth_user;
 | 
					    char *auth_user;
 | 
				
			||||||
    char *auth_pass;
 | 
					    char *auth_pass;
 | 
				
			||||||
    int auth_enabled;
 | 
					    int auth_enabled;
 | 
				
			||||||
 | 
					    int tag_auth_enabled;
 | 
				
			||||||
    struct index_t indices[64];
 | 
					    struct index_t indices[64];
 | 
				
			||||||
} WebCtx_t;
 | 
					} WebCtx_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -62,7 +62,7 @@ index_descriptor_t read_index_descriptor(char *path) {
 | 
				
			|||||||
    int fd = open(path, O_RDONLY);
 | 
					    int fd = open(path, O_RDONLY);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (fd == -1) {
 | 
					    if (fd == -1) {
 | 
				
			||||||
        LOG_FATALF("serialize.c", "Invalid/corrupt index (Could not find descriptor): %s: %s\n", path ,strerror(errno))
 | 
					        LOG_FATALF("serialize.c", "Invalid/corrupt index (Could not find descriptor): %s: %s\n", path, strerror(errno))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    char *buf = malloc(info.st_size + 1);
 | 
					    char *buf = malloc(info.st_size + 1);
 | 
				
			||||||
@ -172,8 +172,8 @@ void write_document(document_t *doc) {
 | 
				
			|||||||
    dyn_buffer_t buf = dyn_buffer_create();
 | 
					    dyn_buffer_t buf = dyn_buffer_create();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Ignore root directory in the file path
 | 
					    // Ignore root directory in the file path
 | 
				
			||||||
    doc->ext = doc->ext - ScanCtx.index.desc.root_len;
 | 
					    doc->ext = (short) (doc->ext - ScanCtx.index.desc.root_len);
 | 
				
			||||||
    doc->base = doc->base - ScanCtx.index.desc.root_len;
 | 
					    doc->base = (short) (doc->base - ScanCtx.index.desc.root_len);
 | 
				
			||||||
    doc->filepath += ScanCtx.index.desc.root_len;
 | 
					    doc->filepath += ScanCtx.index.desc.root_len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    dyn_buffer_write(&buf, doc, sizeof(line_t));
 | 
					    dyn_buffer_write(&buf, doc, sizeof(line_t));
 | 
				
			||||||
@ -230,7 +230,7 @@ void read_index_bin(const char *path, const char *index_id, index_func func) {
 | 
				
			|||||||
        char uuid_str[UUID_STR_LEN];
 | 
					        char uuid_str[UUID_STR_LEN];
 | 
				
			||||||
        uuid_unparse(line.uuid, uuid_str);
 | 
					        uuid_unparse(line.uuid, uuid_str);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const char* mime_text = mime_get_mime_text(line.mime);
 | 
					        const char *mime_text = mime_get_mime_text(line.mime);
 | 
				
			||||||
        if (mime_text == NULL) {
 | 
					        if (mime_text == NULL) {
 | 
				
			||||||
            cJSON_AddNullToObject(document, "mime");
 | 
					            cJSON_AddNullToObject(document, "mime");
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
@ -239,12 +239,18 @@ void read_index_bin(const char *path, const char *index_id, index_func func) {
 | 
				
			|||||||
        cJSON_AddNumberToObject(document, "size", (double) line.size);
 | 
					        cJSON_AddNumberToObject(document, "size", (double) line.size);
 | 
				
			||||||
        cJSON_AddNumberToObject(document, "mtime", line.mtime);
 | 
					        cJSON_AddNumberToObject(document, "mtime", line.mtime);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        int c;
 | 
					        int c = 0;
 | 
				
			||||||
        while ((c = getc(file)) != 0) {
 | 
					        while ((c = getc(file)) != 0) {
 | 
				
			||||||
            dyn_buffer_write_char(&buf, (char) c);
 | 
					            dyn_buffer_write_char(&buf, (char) c);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        dyn_buffer_write_char(&buf, '\0');
 | 
					        dyn_buffer_write_char(&buf, '\0');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const char *tags_string = g_hash_table_lookup(IndexCtx.tags, buf.buf);
 | 
				
			||||||
 | 
					        if (tags_string != NULL) {
 | 
				
			||||||
 | 
					            cJSON *tags_arr = cJSON_Parse(tags_string);
 | 
				
			||||||
 | 
					            cJSON_AddItemToObject(document, "tag", tags_arr);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        cJSON_AddStringToObject(document, "extension", buf.buf + line.ext);
 | 
					        cJSON_AddStringToObject(document, "extension", buf.buf + line.ext);
 | 
				
			||||||
        if (*(buf.buf + line.ext - 1) == '.') {
 | 
					        if (*(buf.buf + line.ext - 1) == '.') {
 | 
				
			||||||
            *(buf.buf + line.ext - 1) = '\0';
 | 
					            *(buf.buf + line.ext - 1) = '\0';
 | 
				
			||||||
 | 
				
			|||||||
@ -111,3 +111,35 @@ char *store_read(store_t *store, char *key, size_t key_len, size_t *ret_vallen)
 | 
				
			|||||||
    return buf;
 | 
					    return buf;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					GHashTable *store_read_all(store_t *store) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int count = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    GHashTable *table = g_hash_table_new_full(g_str_hash, g_str_equal, free, free);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    MDB_txn *txn = NULL;
 | 
				
			||||||
 | 
					    mdb_txn_begin(store->env, NULL, MDB_RDONLY, &txn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    MDB_cursor *cur = NULL;
 | 
				
			||||||
 | 
					    mdb_cursor_open(txn, store->dbi, &cur);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    MDB_val key;
 | 
				
			||||||
 | 
					    MDB_val value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while (mdb_cursor_get(cur, &key, &value, MDB_NEXT) == 0) {
 | 
				
			||||||
 | 
					        char *key_str = malloc(key.mv_size);
 | 
				
			||||||
 | 
					        memcpy(key_str, key.mv_data, key.mv_size);
 | 
				
			||||||
 | 
					        char *val_str = malloc(value.mv_size);
 | 
				
			||||||
 | 
					        memcpy(val_str, value.mv_data, value.mv_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        g_hash_table_insert(table, key_str, val_str);
 | 
				
			||||||
 | 
					        count += 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    LOG_DEBUGF("store.c", "Read tags for %d documents", count);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mdb_cursor_close(cur);
 | 
				
			||||||
 | 
					    mdb_txn_abort(txn);
 | 
				
			||||||
 | 
					    return table;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -4,6 +4,8 @@
 | 
				
			|||||||
#include <pthread.h>
 | 
					#include <pthread.h>
 | 
				
			||||||
#include <lmdb.h>
 | 
					#include <lmdb.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <glib.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define STORE_SIZE_TN 1024 * 1024 * 5
 | 
					#define STORE_SIZE_TN 1024 * 1024 * 5
 | 
				
			||||||
#define STORE_SIZE_TAG 1024 * 16
 | 
					#define STORE_SIZE_TAG 1024 * 16
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -23,4 +25,6 @@ void store_write(store_t *store, char *key, size_t key_len, char *buf, size_t bu
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
char *store_read(store_t *store, char *key, size_t key_len, size_t *ret_vallen);
 | 
					char *store_read(store_t *store, char *key, size_t key_len, size_t *ret_vallen);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					GHashTable *store_read_all(store_t *store);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										15
									
								
								src/main.c
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								src/main.c
									
									
									
									
									
								
							@ -21,7 +21,7 @@
 | 
				
			|||||||
#define EPILOG "Made by simon987 <me@simon987.net>. Released under GPL-3.0"
 | 
					#define EPILOG "Made by simon987 <me@simon987.net>. Released under GPL-3.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const char *const Version = "2.5.2";
 | 
					static const char *const Version = "2.6.0";
 | 
				
			||||||
static const char *const usage[] = {
 | 
					static const char *const usage[] = {
 | 
				
			||||||
        "sist2 scan [OPTION]... PATH",
 | 
					        "sist2 scan [OPTION]... PATH",
 | 
				
			||||||
        "sist2 index [OPTION]... INDEX",
 | 
					        "sist2 index [OPTION]... INDEX",
 | 
				
			||||||
@ -271,6 +271,12 @@ void sist2_index(index_args_t *args) {
 | 
				
			|||||||
        LOG_FATALF("main.c", "Could not open index %s: %s", args->index_path, strerror(errno))
 | 
					        LOG_FATALF("main.c", "Could not open index %s: %s", args->index_path, strerror(errno))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    char path_tmp[PATH_MAX];
 | 
				
			||||||
 | 
					    snprintf(path_tmp, sizeof(path_tmp), "%s/tags", args->index_path);
 | 
				
			||||||
 | 
					    mkdir(path_tmp, S_IWUSR | S_IRUSR | S_IXUSR);
 | 
				
			||||||
 | 
					    IndexCtx.tag_store = store_create(path_tmp, STORE_SIZE_TAG);
 | 
				
			||||||
 | 
					    IndexCtx.tags = store_read_all(IndexCtx.tag_store);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    index_func f;
 | 
					    index_func f;
 | 
				
			||||||
    if (args->print) {
 | 
					    if (args->print) {
 | 
				
			||||||
        f = print_json;
 | 
					        f = print_json;
 | 
				
			||||||
@ -303,8 +309,11 @@ void sist2_index(index_args_t *args) {
 | 
				
			|||||||
    if (!args->print) {
 | 
					    if (!args->print) {
 | 
				
			||||||
        finish_indexer(args->script, desc.uuid);
 | 
					        finish_indexer(args->script, desc.uuid);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    tpool_destroy(IndexCtx.pool);
 | 
					    tpool_destroy(IndexCtx.pool);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    store_destroy(IndexCtx.tag_store);
 | 
				
			||||||
 | 
					    g_hash_table_remove_all(IndexCtx.tags);
 | 
				
			||||||
 | 
					    g_hash_table_destroy(IndexCtx.tags);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void sist2_exec_script(exec_args_t *args) {
 | 
					void sist2_exec_script(exec_args_t *args) {
 | 
				
			||||||
@ -330,6 +339,7 @@ void sist2_web(web_args_t *args) {
 | 
				
			|||||||
    WebCtx.auth_user = args->auth_user;
 | 
					    WebCtx.auth_user = args->auth_user;
 | 
				
			||||||
    WebCtx.auth_pass = args->auth_pass;
 | 
					    WebCtx.auth_pass = args->auth_pass;
 | 
				
			||||||
    WebCtx.auth_enabled = args->auth_enabled;
 | 
					    WebCtx.auth_enabled = args->auth_enabled;
 | 
				
			||||||
 | 
					    WebCtx.tag_auth_enabled = args->tag_auth_enabled;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (int i = 0; i < args->index_count; i++) {
 | 
					    for (int i = 0; i < args->index_count; i++) {
 | 
				
			||||||
        char *abs_path = abspath(args->indices[i]);
 | 
					        char *abs_path = abspath(args->indices[i]);
 | 
				
			||||||
@ -419,6 +429,7 @@ int main(int argc, const char *argv[]) {
 | 
				
			|||||||
            OPT_STRING(0, "es-url", &common_es_url, "Elasticsearch url. DEFAULT=http://localhost:9200"),
 | 
					            OPT_STRING(0, "es-url", &common_es_url, "Elasticsearch url. DEFAULT=http://localhost:9200"),
 | 
				
			||||||
            OPT_STRING(0, "bind", &web_args->listen_address, "Listen on this address. DEFAULT=localhost:4090"),
 | 
					            OPT_STRING(0, "bind", &web_args->listen_address, "Listen on this address. DEFAULT=localhost:4090"),
 | 
				
			||||||
            OPT_STRING(0, "auth", &web_args->credentials, "Basic auth in user:password format"),
 | 
					            OPT_STRING(0, "auth", &web_args->credentials, "Basic auth in user:password format"),
 | 
				
			||||||
 | 
					            OPT_STRING(0, "tag-auth", &web_args->tag_credentials, "Basic auth in user:password format for tagging"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            OPT_GROUP("Exec-script options"),
 | 
					            OPT_GROUP("Exec-script options"),
 | 
				
			||||||
            OPT_STRING(0, "script-file", &common_script_path, "Path to user script."),
 | 
					            OPT_STRING(0, "script-file", &common_script_path, "Path to user script."),
 | 
				
			||||||
 | 
				
			|||||||
@ -197,6 +197,18 @@ a:hover,.btn:hover {
 | 
				
			|||||||
    margin-right: 3px;
 | 
					    margin-right: 3px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.badge-delete {
 | 
				
			||||||
 | 
					    margin-right: -2px;
 | 
				
			||||||
 | 
					    margin-left: 2px;
 | 
				
			||||||
 | 
					    margin-top: -1px;
 | 
				
			||||||
 | 
					    font-family: monospace;
 | 
				
			||||||
 | 
					    font-size: 90%;
 | 
				
			||||||
 | 
					    background: rgba(0,0,0,0.2);
 | 
				
			||||||
 | 
					    padding: 0.1em 0.4em;
 | 
				
			||||||
 | 
					    color: white;
 | 
				
			||||||
 | 
					    cursor: pointer;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.badge-user {
 | 
					.badge-user {
 | 
				
			||||||
    color: #212529;
 | 
					    color: #212529;
 | 
				
			||||||
    background-color: #e0e0e0;
 | 
					    background-color: #e0e0e0;
 | 
				
			||||||
 | 
				
			|||||||
@ -113,6 +113,7 @@ body {
 | 
				
			|||||||
.badge-delete {
 | 
					.badge-delete {
 | 
				
			||||||
    margin-right: -2px;
 | 
					    margin-right: -2px;
 | 
				
			||||||
    margin-left: 2px;
 | 
					    margin-left: 2px;
 | 
				
			||||||
 | 
					    margin-top: -1px;
 | 
				
			||||||
    font-family: monospace;
 | 
					    font-family: monospace;
 | 
				
			||||||
    font-size: 90%;
 | 
					    font-size: 90%;
 | 
				
			||||||
    background: rgba(0,0,0,0.2);
 | 
					    background: rgba(0,0,0,0.2);
 | 
				
			||||||
 | 
				
			|||||||
@ -117,7 +117,7 @@ window.onload = () => {
 | 
				
			|||||||
        minChars: 1,
 | 
					        minChars: 1,
 | 
				
			||||||
        delay: 200,
 | 
					        delay: 200,
 | 
				
			||||||
        renderItem: function (item) {
 | 
					        renderItem: function (item) {
 | 
				
			||||||
            return '<div class="autocomplete-suggestion" data-val="' + item + '">' + item + '</div>';
 | 
					            return '<div class="autocomplete-suggestion" data-val="' + item + '">' + item.split("#")[0] + '</div>';
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        source: async function (term, suggest) {
 | 
					        source: async function (term, suggest) {
 | 
				
			||||||
            term = term.toLowerCase();
 | 
					            term = term.toLowerCase();
 | 
				
			||||||
@ -132,12 +132,18 @@ window.onload = () => {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            suggest(matches.sort());
 | 
					            suggest(matches.sort());
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        onSelect: function () {
 | 
					        onSelect: function (e, item) {
 | 
				
			||||||
 | 
					            const name = item.split("#")[0];
 | 
				
			||||||
 | 
					            const color = "#" + item.split("#")[1];
 | 
				
			||||||
 | 
					            $("#tag-color").val(color);
 | 
				
			||||||
 | 
					            $("#tag-color").trigger("keyup", color);
 | 
				
			||||||
 | 
					            tagBar.value = name;
 | 
				
			||||||
 | 
					            e.preventDefault();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    [tagBar, document.getElementById("tag-color")].forEach(elem => {
 | 
					    [tagBar, document.getElementById("tag-color")].forEach(elem => {
 | 
				
			||||||
        elem.addEventListener("keyup", e => {
 | 
					        elem.addEventListener("keyup", e => {
 | 
				
			||||||
            if (e.key === "Enter") {
 | 
					            if (e.key === "Enter" && tagBar.value.length > 0) {
 | 
				
			||||||
                const tag = tagBar.value + document.getElementById("tag-color").value;
 | 
					                const tag = tagBar.value + document.getElementById("tag-color").value;
 | 
				
			||||||
                saveTag(tag, currentDocToTag).then(() => currentTagCallback(tag));
 | 
					                saveTag(tag, currentDocToTag).then(() => currentTagCallback(tag));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -162,7 +168,7 @@ window.onload = () => {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function saveTag(tag, hit) {
 | 
					function saveTag(tag, hit) {
 | 
				
			||||||
    const relPath = hit["_source"]["path"] + "/" + hit["_source"] + ext(hit);
 | 
					    const relPath = hit["_source"]["path"] + "/" + hit["_source"]["name"] + ext(hit);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return $.jsonPost("/tag/" + hit["_source"]["index"], {
 | 
					    return $.jsonPost("/tag/" + hit["_source"]["index"], {
 | 
				
			||||||
        delete: false,
 | 
					        delete: false,
 | 
				
			||||||
@ -186,7 +192,7 @@ function saveTag(tag, hit) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function deleteTag(tag, hit) {
 | 
					function deleteTag(tag, hit) {
 | 
				
			||||||
    const relPath = hit["_source"]["path"] + "/" + hit["_source"] + ext(hit);
 | 
					    const relPath = hit["_source"]["path"] + "/" + hit["_source"]["name"] + ext(hit);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return $.jsonPost("/tag/" + hit["_source"]["index"], {
 | 
					    return $.jsonPost("/tag/" + hit["_source"]["index"], {
 | 
				
			||||||
        delete: true,
 | 
					        delete: true,
 | 
				
			||||||
@ -344,11 +350,14 @@ $.jsonPost("es", {
 | 
				
			|||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function addTag(map, tag, id, count) {
 | 
					function addTag(map, tag, id, count) {
 | 
				
			||||||
    let tags = tag.split("#")[0].split(".");
 | 
					    // let tags = tag.split("#")[0].split(".");
 | 
				
			||||||
 | 
					    let tags = tag.split(".");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let child = {
 | 
					    let child = {
 | 
				
			||||||
        id: id,
 | 
					        id: id,
 | 
				
			||||||
        text: tags.length !== 1 ? tags[0] : `${tags[0]} (${count})`,
 | 
					        values: [id],
 | 
				
			||||||
 | 
					        count: count,
 | 
				
			||||||
 | 
					        text: tags.length !== 1 ? tags[0] : `${tags[0].split("#")[0]} (${count})`,
 | 
				
			||||||
        name: tags[0],
 | 
					        name: tags[0],
 | 
				
			||||||
        children: [],
 | 
					        children: [],
 | 
				
			||||||
        isLeaf: tags.length === 1,
 | 
					        isLeaf: tags.length === 1,
 | 
				
			||||||
@ -396,10 +405,15 @@ function addTag(map, tag, id, count) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    let found = false;
 | 
					    let found = false;
 | 
				
			||||||
    map.forEach(node => {
 | 
					    map.forEach(node => {
 | 
				
			||||||
        if (node.name === child.name) {
 | 
					        if (node.name.split("#")[0] === child.name.split("#")[0]) {
 | 
				
			||||||
            found = true;
 | 
					            found = true;
 | 
				
			||||||
            if (tags.length !== 1) {
 | 
					            if (tags.length !== 1) {
 | 
				
			||||||
                addTag(node.children, tags.slice(1).join("."), id, count);
 | 
					                addTag(node.children, tags.slice(1).join("."), id, count);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // Same name, different color
 | 
				
			||||||
 | 
					                node.count += count;
 | 
				
			||||||
 | 
					                node.text = `${tags[0].split("#")[0]} (${node.count})`;
 | 
				
			||||||
 | 
					                node.values.push(id);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
@ -451,7 +465,11 @@ function getSelectedNodes(tree) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        //Only get children
 | 
					        //Only get children
 | 
				
			||||||
        if (selected[i].text.indexOf("(") !== -1) {
 | 
					        if (selected[i].text.indexOf("(") !== -1) {
 | 
				
			||||||
            selectedNodes.push(selected[i].id);
 | 
					            if (selected[i].values) {
 | 
				
			||||||
 | 
					                selectedNodes.push(selected[i].values);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                selectedNodes.push(selected[i].id);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -514,7 +532,9 @@ function search(after = null) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    let tags = getSelectedNodes(tagTree);
 | 
					    let tags = getSelectedNodes(tagTree);
 | 
				
			||||||
    if (!tags.includes("any")) {
 | 
					    if (!tags.includes("any")) {
 | 
				
			||||||
        tags.forEach(term => filters.push({term: {"tag": term}}))
 | 
					        tags.forEach(tagGroup => {
 | 
				
			||||||
 | 
					            filters.push({terms: {"tag": tagGroup}})
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (date_min && date_max) {
 | 
					    if (date_min && date_max) {
 | 
				
			||||||
@ -758,6 +778,7 @@ function getNextDepth(node) {
 | 
				
			|||||||
                    text: `${name}/ (${bucket.doc_count})`,
 | 
					                    text: `${name}/ (${bucket.doc_count})`,
 | 
				
			||||||
                    depth: node.depth + 1,
 | 
					                    depth: node.depth + 1,
 | 
				
			||||||
                    index: node.index,
 | 
					                    index: node.index,
 | 
				
			||||||
 | 
					                    values: [bucket.key],
 | 
				
			||||||
                    children: true,
 | 
					                    children: true,
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }).filter(x => x !== null)
 | 
					            }).filter(x => x !== null)
 | 
				
			||||||
@ -788,6 +809,7 @@ function createPathTree(target) {
 | 
				
			|||||||
    selectedIndices.forEach(index => {
 | 
					    selectedIndices.forEach(index => {
 | 
				
			||||||
        pathTree.addNode({
 | 
					        pathTree.addNode({
 | 
				
			||||||
            id: "/" + index,
 | 
					            id: "/" + index,
 | 
				
			||||||
 | 
					            values: ["/" + index],
 | 
				
			||||||
            text: `/[${indexMap[index]}]`,
 | 
					            text: `/[${indexMap[index]}]`,
 | 
				
			||||||
            index: index,
 | 
					            index: index,
 | 
				
			||||||
            depth: 0,
 | 
					            depth: 0,
 | 
				
			||||||
@ -838,8 +860,8 @@ function getTagChoices() {
 | 
				
			|||||||
            resp["suggest"]["tag"][0]["options"].map(opt => opt["_source"]["tag"]).forEach(tags => {
 | 
					            resp["suggest"]["tag"][0]["options"].map(opt => opt["_source"]["tag"]).forEach(tags => {
 | 
				
			||||||
                tags.forEach(tag => {
 | 
					                tags.forEach(tag => {
 | 
				
			||||||
                    const t = tag.split("#")[0];
 | 
					                    const t = tag.split("#")[0];
 | 
				
			||||||
                    if (result.indexOf(t) === -1) {
 | 
					                    if (!result.find(x => x.split("#")[0] === t)) {
 | 
				
			||||||
                        result.push(t);
 | 
					                        result.push(tag);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
				
			|||||||
@ -11,7 +11,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
<nav class="navbar navbar-expand-lg">
 | 
					<nav class="navbar navbar-expand-lg">
 | 
				
			||||||
    <a class="navbar-brand" href="/">sist2</a>
 | 
					    <a class="navbar-brand" href="/">sist2</a>
 | 
				
			||||||
    <span class="badge badge-pill version">2.5.2</span>
 | 
					    <span class="badge badge-pill version">2.6.0</span>
 | 
				
			||||||
    <span class="tagline">Lightning-fast file system indexer and search tool </span>
 | 
					    <span class="tagline">Lightning-fast file system indexer and search tool </span>
 | 
				
			||||||
    <a class="btn ml-auto" href="/stats">Stats</a>
 | 
					    <a class="btn ml-auto" href="/stats">Stats</a>
 | 
				
			||||||
    <button class="btn" type="button" data-toggle="modal" data-target="#settings" onclick="loadSettings()">Settings
 | 
					    <button class="btn" type="button" data-toggle="modal" data-target="#settings" onclick="loadSettings()">Settings
 | 
				
			||||||
 | 
				
			|||||||
@ -10,7 +10,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
<nav class="navbar navbar-expand-lg">
 | 
					<nav class="navbar navbar-expand-lg">
 | 
				
			||||||
    <a class="navbar-brand" href="/">sist2</a>
 | 
					    <a class="navbar-brand" href="/">sist2</a>
 | 
				
			||||||
    <span class="badge badge-pill version">2.5.2</span>
 | 
					    <span class="badge badge-pill version">2.6.0</span>
 | 
				
			||||||
    <span class="tagline">Lightning-fast file system indexer and search tool </span>
 | 
					    <span class="tagline">Lightning-fast file system indexer and search tool </span>
 | 
				
			||||||
    <a style="margin-left: auto" class="btn" href="/">Back</a>
 | 
					    <a style="margin-left: auto" class="btn" href="/">Back</a>
 | 
				
			||||||
    <button class="btn" type="button" data-toggle="modal" data-target="#settings"
 | 
					    <button class="btn" type="button" data-toggle="modal" data-target="#settings"
 | 
				
			||||||
 | 
				
			|||||||
@ -562,7 +562,7 @@ void tag(struct mg_connection *nc, struct http_message *hm, struct mg_str *path)
 | 
				
			|||||||
        snprintf(buf, sizeof(buf),
 | 
					        snprintf(buf, sizeof(buf),
 | 
				
			||||||
                 "{"
 | 
					                 "{"
 | 
				
			||||||
                 "    \"script\" : {"
 | 
					                 "    \"script\" : {"
 | 
				
			||||||
                 "        \"source\": \"ctx._source.tag.add(params.tag)\","
 | 
					                 "        \"source\": \"if(ctx._source.tag == null) {ctx._source.tag = new ArrayList()} ctx._source.tag.add(params.tag)\","
 | 
				
			||||||
                 "        \"lang\": \"painless\","
 | 
					                 "        \"lang\": \"painless\","
 | 
				
			||||||
                 "        \"params\" : {"
 | 
					                 "        \"params\" : {"
 | 
				
			||||||
                 "            \"tag\" : \"%s\""
 | 
					                 "            \"tag\" : \"%s\""
 | 
				
			||||||
@ -577,7 +577,7 @@ void tag(struct mg_connection *nc, struct http_message *hm, struct mg_str *path)
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    char *json_str = cJSON_PrintUnformatted(arr);
 | 
					    char *json_str = cJSON_PrintUnformatted(arr);
 | 
				
			||||||
    store_write(store, arg_req->relpath, strlen(arg_req->relpath), json_str, strlen(json_str) + 1);
 | 
					    store_write(store, arg_req->relpath, strlen(arg_req->relpath) + 1, json_str, strlen(json_str) + 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    free(arg_req);
 | 
					    free(arg_req);
 | 
				
			||||||
    free(json_str);
 | 
					    free(json_str);
 | 
				
			||||||
@ -586,6 +586,21 @@ void tag(struct mg_connection *nc, struct http_message *hm, struct mg_str *path)
 | 
				
			|||||||
    free(body);
 | 
					    free(body);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int validate_auth(struct mg_connection *nc, struct http_message *hm) {
 | 
				
			||||||
 | 
					    char user[256] = {0,};
 | 
				
			||||||
 | 
					    char pass[256] = {0,};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int ret = mg_get_http_basic_auth(hm, user, sizeof(user), pass, sizeof(pass));
 | 
				
			||||||
 | 
					    if (ret == -1 || strcmp(user, WebCtx.auth_user) != 0 || strcmp(pass, WebCtx.auth_pass) != 0) {
 | 
				
			||||||
 | 
					        mg_printf(nc, "HTTP/1.1 401 Unauthorized\r\n"
 | 
				
			||||||
 | 
					                      "WWW-Authenticate: Basic realm=\"sist2\"\r\n"
 | 
				
			||||||
 | 
					                      "Content-Length: 0\r\n\r\n");
 | 
				
			||||||
 | 
					        nc->flags |= MG_F_SEND_AND_CLOSE;
 | 
				
			||||||
 | 
					        return FALSE;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return TRUE;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void ev_router(struct mg_connection *nc, int ev, void *p) {
 | 
					static void ev_router(struct mg_connection *nc, int ev, void *p) {
 | 
				
			||||||
    struct mg_str scheme;
 | 
					    struct mg_str scheme;
 | 
				
			||||||
    struct mg_str user_info;
 | 
					    struct mg_str user_info;
 | 
				
			||||||
@ -606,15 +621,7 @@ static void ev_router(struct mg_connection *nc, int ev, void *p) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (WebCtx.auth_enabled == TRUE) {
 | 
					        if (WebCtx.auth_enabled == TRUE) {
 | 
				
			||||||
            char user[256] = {0,};
 | 
					            if (!validate_auth(nc, hm)) {
 | 
				
			||||||
            char pass[256] = {0,};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            int ret = mg_get_http_basic_auth(hm, user, sizeof(user), pass, sizeof(pass));
 | 
					 | 
				
			||||||
            if (ret == -1 || strcmp(user, WebCtx.auth_user) != 0 || strcmp(pass, WebCtx.auth_pass) != 0) {
 | 
					 | 
				
			||||||
                mg_printf(nc, "HTTP/1.1 401 Unauthorized\r\n"
 | 
					 | 
				
			||||||
                              "WWW-Authenticate: Basic realm=\"sist2\"\r\n"
 | 
					 | 
				
			||||||
                              "Content-Length: 0\r\n\r\n");
 | 
					 | 
				
			||||||
                nc->flags |= MG_F_SEND_AND_CLOSE;
 | 
					 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -644,6 +651,11 @@ static void ev_router(struct mg_connection *nc, int ev, void *p) {
 | 
				
			|||||||
        } else if (has_prefix(&path, &((struct mg_str) MG_MK_STR("/s/")))) {
 | 
					        } else if (has_prefix(&path, &((struct mg_str) MG_MK_STR("/s/")))) {
 | 
				
			||||||
            stats_files(nc, hm, &path);
 | 
					            stats_files(nc, hm, &path);
 | 
				
			||||||
        } else if (has_prefix(&path, &((struct mg_str) MG_MK_STR("/tag/")))) {
 | 
					        } else if (has_prefix(&path, &((struct mg_str) MG_MK_STR("/tag/")))) {
 | 
				
			||||||
 | 
					            if (WebCtx.tag_auth_enabled == TRUE) {
 | 
				
			||||||
 | 
					                if (!validate_auth(nc, hm)) {
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            tag(nc, hm, &path);
 | 
					            tag(nc, hm, &path);
 | 
				
			||||||
        } else if (has_prefix(&path, &((struct mg_str) MG_MK_STR("/d/")))) {
 | 
					        } else if (has_prefix(&path, &((struct mg_str) MG_MK_STR("/d/")))) {
 | 
				
			||||||
            document_info(nc, hm, &path);
 | 
					            document_info(nc, hm, &path);
 | 
				
			||||||
 | 
				
			|||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user