00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include "config.h"
00028
00029 #include <sys/time.h>
00030 #include <sys/types.h>
00031 #include <dirent.h>
00032 #include <sys/stat.h>
00033 #include <time.h>
00034 #include <gtk/gtk.h>
00035 #include <unistd.h>
00036 #include <stdlib.h>
00037
00038 #include "log.h"
00039 #include "filetypes.h"
00040 #include "index_db.h"
00041 #include "index.h"
00042 #include "tags.h"
00043 #include "index_ipc.h"
00044 #include "shortcut.h"
00045
00046 const GSList* g_iter;
00047 gchar startdir[256];
00048 int startdirlen = 0;
00049 gchar drz_dir[256];
00050 gchar shortcut_dir[256];
00051 gchar ade_thumbs_dir[256];
00052
00053 typedef struct
00054 {
00055 const gchar *filename;
00056 const gchar *filepath;
00057 gboolean is_dir;
00058 gint64 filesize;
00059 gint64 date_modified;
00060 } fs_entry_t;
00061
00062
00063 typedef unsigned long long u_int64_t;
00064 u_int64_t getCurrentTime()
00065 {
00066 struct timespec now;
00067
00068 clock_gettime(CLOCK_MONOTONIC, &now);
00069 u_int64_t now64 = now.tv_sec;
00070 now64 *= 1000000;
00071 now64 += (now.tv_nsec/1000);
00072 return now64;
00073 }
00074
00075
00076 static const char *HIDDEN_FILENAMES[] =
00077 {
00078 ERMETADB_GLOBAL_DATABASE_FILE,
00079 ERMETADB_LOCAL_DATABASE_FILE,
00080 ERMETADB_LOCAL_DATABASE_FILE "-journal",
00081 ERMETADB_LOCAL_DATABASE_FILE ".old",
00082 "autorun.inf",
00083 "Thumbs.db",
00084 NULL
00085 };
00086
00087
00088 static gboolean fs_ignore_filename (const gchar *filename)
00089 {
00090 int len = strlen(filename);
00091 if (len == 0) return TRUE;
00092
00093
00094 if (filename[0] == '.' || filename[0] == '_') return TRUE;
00095
00096
00097 const char** cpp = NULL;
00098 for (cpp = HIDDEN_FILENAMES ; *cpp; cpp++)
00099 {
00100 if ( strcmp(filename, *cpp) == 0 ) return TRUE;
00101 }
00102
00103 return FALSE;
00104 }
00105
00106
00107 static db_entry_t* create_db_entry(const fs_entry_t* fs_entry)
00108 {
00109 db_entry_t* entry = g_malloc(sizeof(db_entry_t));
00110 memset(entry, 0, sizeof(db_entry_t));
00111 entry->filename = g_strdup(fs_entry->filename);
00112 entry->filepath = g_strdup(fs_entry->filepath);
00113 entry->is_dir = fs_entry->is_dir;
00114 entry->filesize = fs_entry->filesize;
00115 entry->last_modified = fs_entry->date_modified;
00116 entry->state = NEW;
00117 return entry;
00118 }
00119
00120
00121 static db_entry_t* get_entry(const GSList* db_model, const char* filepath, const char* filename)
00122 {
00123
00124 const GSList* iter = db_model;
00125 while (iter) {
00126 db_entry_t* db_entry = iter->data;
00127 if ((strcmp(db_entry->filename, filename) == 0) &&
00128 strcmp(db_entry->filepath, filepath) == 0)
00129 {
00130 return db_entry;
00131 }
00132 iter = g_slist_next(iter);
00133 }
00134 return NULL;
00135 }
00136
00137
00138 static void compare_entry(GSList** db_model, fs_entry_t* fs_entry)
00139 {
00140 db_entry_t* db_entry = get_entry(*db_model, fs_entry->filepath, fs_entry->filename);
00141 if (db_entry) {
00142 if (db_entry->is_dir != fs_entry->is_dir) {
00143
00144 db_entry_t* new_entry = create_db_entry(fs_entry);
00145
00146 *db_model = g_slist_append(*db_model, new_entry);
00147 return;
00148 }
00149 if (!db_entry->is_dir && db_entry->filesize != fs_entry->filesize) {
00150 db_entry->filesize = fs_entry->filesize;
00151 db_entry->state = CHANGED;
00152 }
00153 if (db_entry->last_modified != fs_entry->date_modified) {
00154 db_entry->last_modified = fs_entry->date_modified;
00155 db_entry->state = CHANGED;
00156 }
00157 if (db_entry->state == UNKNOWN) db_entry->state = SAME;
00158 } else {
00159 db_entry_t* new_entry = create_db_entry(fs_entry);
00160 *db_model = g_slist_prepend(*db_model, new_entry);
00161
00162 }
00163 }
00164
00165
00166 static gboolean ignore_dir(const char* name)
00167 {
00168 if (strcmp(ade_thumbs_dir, name) == 0) return TRUE;
00169 return FALSE;
00170 }
00171
00172
00173 static gboolean ignore_file(const char* filename, const char* ext, gboolean is_dir)
00174 {
00175 if (fs_ignore_filename(filename)) return TRUE;
00176
00177
00178 if (is_dir) return FALSE;
00179
00180
00181 if (ext[0] == 0) return TRUE;
00182
00183
00184 if (is_shortcut_file_extension(ext)) return FALSE;
00185
00186 return (get_viewer_from_file_extension(ext) == NULL);
00187 }
00188
00189
00190 static gboolean ignore_thumbnail_generation(db_entry_t* db_entry)
00191 {
00192
00193 if (db_entry->state != CHANGED && db_entry->state != NEW)
00194 return TRUE;
00195
00196
00197 if (db_entry->is_dir)
00198 return TRUE;
00199
00200
00201 const char *ext = g_extension_pointer(db_entry->filename);
00202
00203
00204 if (get_viewer_from_file_extension(ext) == NULL)
00205 return TRUE;
00206
00207
00208 return (!get_generate_thumbnail(ext));
00209 }
00210
00211
00212 static void read_directory(const gchar* name, GSList** db_model)
00213 {
00214 DIR* dir = opendir(name);
00215 if (dir == NULL) {
00216 perror("opendir");
00217 return;
00218 }
00219 struct dirent* current = readdir(dir);
00220 while (current != 0)
00221 {
00222 if (current->d_name[0] == '.') goto next;
00223 char fullname[1024];
00224 snprintf(fullname, sizeof(fullname), "%s/%s", name, current->d_name);
00225 struct stat statbuf;
00226 stat(fullname, &statbuf);
00227
00228 gboolean is_dir = S_ISDIR(statbuf.st_mode);
00229 gboolean is_reg = S_ISREG(statbuf.st_mode);
00230 if (!is_dir && !is_reg) goto next;
00231
00232
00233 if (strcmp(current->d_name, ERMETADB_LOCAL_DATABASE_FILE) == 0) {
00234 LOGPRINTF("trying to open and close %s", fullname);
00235 erMetadb local_db = ermetadb_local_open(name, TRUE);
00236 if (local_db == NULL) {
00237 ERRORPRINTF("cannot open db %s", fullname);
00238 }
00239 ermetadb_close(local_db);
00240 goto next;
00241 }
00242
00243 const char *ext = g_extension_pointer(current->d_name);
00244
00245 if (is_drz_file_extension(ext))
00246 {
00247 if (strcmp(name, drz_dir) != 0) {
00248
00249 g_mkdir_with_parents(drz_dir, 644);
00250
00251
00252 char command[256];
00253 LOGPRINTF("force moving %s to %s/", fullname, drz_dir);
00254 sprintf(command, "mv -f %s %s/", fullname, drz_dir);
00255 system(command);
00256 }
00257 else
00258 {
00259 LOGPRINTF("ignoring %s", current->d_name);
00260 }
00261 goto next;
00262 }
00263
00264 if (ignore_file(current->d_name, ext, is_dir))
00265 {
00266 LOGPRINTF("ignoring %s", current->d_name);
00267 goto next;
00268 }
00269
00270 if (is_dir && ignore_dir(fullname))
00271 {
00272 LOGPRINTF("ignoring %s", current->d_name);
00273 goto next;
00274 }
00275
00276 fs_entry_t fs_entry;
00277 fs_entry.filename = current->d_name;
00278 fs_entry.filepath = name;
00279 fs_entry.is_dir = is_dir;
00280 fs_entry.filesize = is_dir ? 0 : statbuf.st_size;
00281 fs_entry.date_modified = statbuf.st_mtime;
00282 compare_entry(db_model, &fs_entry);
00283
00284 if (is_dir)
00285 {
00286 read_directory(fullname, db_model);
00287 }
00288 next:
00289 current = readdir(dir);
00290 }
00291 closedir(dir);
00292 }
00293
00294 #if (LOGGING_ON)
00295 static const char* state2str(DBState state)
00296 {
00297 static const char* states[] =
00298 {
00299 [UNKNOWN] = "UNKNOWN",
00300 [SAME] = "SAME",
00301 [CHANGED] = "CHANGED",
00302 [NEW] = "NEW",
00303 };
00304 return states[state];
00305 }
00306 #endif
00307
00308
00309 static gboolean send_store_metadata(db_entry_t* db_entry)
00310 {
00311 LOGPRINTF("Store metadata for [%s/%s]", db_entry->filepath, db_entry->filename);
00312 ipc_send_store_metadata(db_entry->filepath, db_entry->filename);
00313 return TRUE;
00314 }
00315
00316
00317
00318 static gboolean check_shortcut(const GSList* db_model,
00319 const char* filepath,
00320 const char* filename,
00321 char** display_name,
00322 char** author)
00323 {
00324 shortcut_t *shortcut = NULL;
00325 int ret = parse_shortcut_file(filepath, filename, &shortcut);
00326 if (ret != ER_OK) {
00327 LOGPRINTF("DENYING: %s/%s - invalid shortcut file", filepath, filename);
00328 return FALSE;
00329 }
00330 g_assert(shortcut);
00331
00332 if (shortcut->type == SHORTCUT_TO_APPLICATION) {
00333
00334
00335 *author = g_strdup("");
00336 *display_name = g_strdup("");
00337 goto allowed;
00338 }
00339
00340
00341 if (shortcut->type != SHORTCUT_TO_FILE &&
00342 shortcut->type != SHORTCUT_TO_FOLDER)
00343 {
00344 LOGPRINTF("DENYING: %s/%s - not shortcut to file/folder", filepath, filename);
00345 goto not_allowed;
00346 }
00347
00348
00349 if (is_shortcut_file(shortcut->details.file.filename)) {
00350 LOGPRINTF("DENYING: %s/%s - shortcut to shortcut", filepath, filename);
00351 goto not_allowed;
00352 }
00353
00354
00355 if (get_entry(db_model, shortcut->details.file.directory,
00356 shortcut->details.file.filename) == NULL)
00357 {
00358 LOGPRINTF("DENYING: %s/%s - target unknown", filepath, filename);
00359 goto not_allowed;
00360 }
00361
00362
00363 *display_name = shortcut->name;
00364 shortcut->name = NULL;
00365
00366
00367 const char* localdir = shortcut->details.file.directory + startdirlen;
00368 if (localdir[0] == '/') {
00369 *author = g_strdup_printf("%s/%s", localdir+1, shortcut->details.file.filename);
00370 } else {
00371 *author = g_strdup(shortcut->details.file.filename);
00372 }
00373
00374 allowed:
00375 shortcut_free(shortcut);
00376 return TRUE;
00377 not_allowed:
00378 shortcut_free(shortcut);
00379 return FALSE;
00380 }
00381
00382
00383 static gboolean handle_changes(const GSList* db_model, const char* dir, gboolean skip_thumbnails)
00384 {
00385 int removed = 0;
00386 int same = 0;
00387 int changed = 0;
00388 int new = 0;
00389
00390 db_state_t db_state;
00391 if (db_open_global(&db_state, dir) != ER_OK) {
00392 ERRORPRINTF("error opening db");
00393 return FALSE;
00394 }
00395
00396 int ret = db_start_transaction(&db_state);
00397 if (ret != ER_OK) {
00398 ERRORPRINTF("error starting transaction");
00399 goto error;
00400 }
00401
00402 const GSList* iter = db_model;
00403 while (iter) {
00404 db_entry_t* db_entry = iter->data;
00405 if (db_entry->state != SAME) {
00406 LOGPRINTF(" %s %4lld %s", state2str(db_entry->state), db_entry->file_id, db_entry->filename);
00407 }
00408
00409 gchar* display_name = NULL;
00410 gchar* author = NULL;
00411
00412 gboolean is_shortcut = is_shortcut_file(db_entry->filename) && !db_entry->is_dir;
00413
00414 if (is_shortcut && db_entry->state != UNKNOWN)
00415 {
00416 if (!check_shortcut(db_model, db_entry->filepath, db_entry->filename, &display_name, &author)) {
00417
00418 if (strcmp(db_entry->filepath, shortcut_dir) == 0) {
00419 char fullname[256];
00420 sprintf(fullname, "%s/%s", db_entry->filepath, db_entry->filename);
00421 LOGPRINTF("deleting %s", fullname);
00422 int err = unlink(fullname);
00423 if (err) {
00424 WARNPRINTF("cannot delete %s", fullname);
00425 }
00426 }
00427 switch (db_entry->state) {
00428 case UNKNOWN:
00429
00430 break;
00431 case SAME:
00432 case CHANGED:
00433
00434 db_entry->state = UNKNOWN;
00435 break;
00436 case NEW:
00437
00438 goto next;
00439 }
00440 }
00441 }
00442
00443 switch (db_entry->state) {
00444 case UNKNOWN:
00445 db_delete_entry(&db_state, db_entry);
00446 removed++;
00447 break;
00448 case SAME:
00449
00450 same++;
00451 break;
00452 case CHANGED:
00453 db_update_entry(&db_state, db_entry);
00454 if (is_shortcut) {
00455 db_update_shortcut_entry(&db_state, db_entry, display_name, author);
00456 }
00457 changed++;
00458 break;
00459 case NEW:
00460 {
00461 const gchar* tag = NULL;
00462 if (!db_entry->is_dir) tag = get_tag_for_file(db_entry->filename, db_entry->filepath, startdir);
00463 db_add_entry(&db_state, db_entry, tag, display_name, author);
00464 new++;
00465 break;
00466 }
00467 }
00468 next:
00469 g_free(display_name);
00470 g_free(author);
00471 iter = g_slist_next(iter);
00472 }
00473
00474 db_end_transaction (&db_state);
00475
00476 if (!skip_thumbnails) {
00477
00478
00479 g_iter = db_model;
00480 while (g_iter) {
00481 db_entry_t* db_entry = g_iter->data;
00482
00483 if (!ignore_thumbnail_generation(db_entry))
00484 {
00485 if (send_store_metadata(db_entry))
00486 {
00487 gtk_main();
00488 break;
00489 }
00490 }
00491 g_iter = g_slist_next(g_iter);
00492 }
00493 }
00494
00495 error:
00496 db_close(&db_state);
00497 printf("mdbindex: new %d removed %d same %d changed %d\n", new, removed, same, changed);
00498
00499 return (new + changed + removed) > 0;
00500 }
00501
00502
00503
00504
00505 void store_metadata_ready()
00506 {
00507 g_iter = g_slist_next(g_iter);
00508
00509 while (g_iter) {
00510 db_entry_t* db_entry = g_iter->data;
00511
00512 if (!ignore_thumbnail_generation(db_entry))
00513 {
00514 if (send_store_metadata(db_entry))
00515 {
00516 return;
00517 }
00518 }
00519 g_iter = g_slist_next(g_iter);
00520 }
00521 gtk_main_quit();
00522 }
00523
00524
00525 gboolean index_full(gchar *dir, gboolean skip_thumbnails)
00526 {
00527 int len = strlen(dir);
00528
00529 if (len>0 && dir[len-1] == '/') dir[len-1] = '\0';
00530 strncpy(startdir, dir, sizeof(startdir));
00531 startdir[sizeof(startdir)-1] = '\0';
00532 startdirlen = strlen(startdir);
00533
00534
00535 struct stat statbuf;
00536 int res = stat(dir, &statbuf);
00537 if (res == -1 || !S_ISDIR(statbuf.st_mode)) {
00538 fprintf(stderr, "mdbindex: directory '%s' doesn't exist or is not a dir\n", dir);
00539 return FALSE;
00540 }
00541
00542
00543 sprintf(drz_dir, "%s/%s", startdir, DIR_DRZ);
00544 sprintf(shortcut_dir, "%s/%s", startdir, DIR_SHORTCUTS);
00545 sprintf(ade_thumbs_dir, "%s/%s", startdir, DIR_ADE_THUMBS);
00546
00547 db_state_t db_state;
00548 if (db_open_global(&db_state, dir) != ER_OK)
00549 {
00550 ERRORPRINTF("Failed to open global db");
00551 return FALSE;
00552 }
00553 db_close(&db_state);
00554
00555 filetypes_init(FALSE);
00556
00557
00558 u_int64_t t1 = getCurrentTime();
00559 GSList* db_model = db_get_model(dir);
00560 if (db_model == NULL) WARNPRINTF("DB is empty");
00561 u_int64_t t2 = getCurrentTime();
00562 u_int64_t diff = t2 - t1;
00563 LOGPRINTF("DB DURATION = %lld ms", diff / 1000);
00564
00565
00566 t1 = getCurrentTime();
00567 read_directory(dir, &db_model);
00568 t2 = getCurrentTime();
00569 diff = t2 - t1;
00570 LOGPRINTF("FS/COMPARE DURATION = %lld ms", diff / 1000);
00571
00572
00573 t1 = getCurrentTime();
00574 gboolean changed = handle_changes(db_model, dir, skip_thumbnails);
00575 t2 = getCurrentTime();
00576 diff = t2 - t1;
00577 LOGPRINTF("DB UPDATE DURATION = %lld ms", diff / 1000);
00578
00579
00580 return changed;
00581 }
00582