notepad_filestore.cpp

Go to the documentation of this file.
00001 /*
00002  * File Name: notepad_filestore.cpp
00003  */
00004 
00005 /*
00006  * This file is part of notepad.
00007  *
00008  * notepad is free software: you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation, either version 2 of the License, or
00011  * (at your option) any later version.
00012  *
00013  * notepad is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program. If not, see <http://www.gnu.org/licenses/>.
00020  */
00021 
00022 /**
00023  * Copyright (C) 2010 IREX Technologies B.V.
00024  * All rights reserved.
00025  */
00026 
00027 #include "config.h"
00028 #include <sys/time.h>
00029 #include <cstring>
00030 #include <gtk/gtk.h>
00031 #include <glib.h>
00032 #include <glib/gstdio.h> // for rename and test
00033 #include <libermetadb/ermetadb.h>
00034 
00035 #include "log.h"
00036 #include "notepad_ipc.h"
00037 #include "notepad_utils.h"
00038 #include "notepad_filestore.h"
00039 
00040 #define NOTE_AUTHOR    "Notepad"
00041 #define NOTE_DIR       "Notes"
00042 #define NOTE_TAG       "note"
00043 #define NOTE_EXTENSION ".note"
00044 
00045 namespace notepad
00046 {
00047 
00048     static const char*  SCRIBBLE_LAST_READ_LOCATION  = "scribble_last_read_location";
00049 
00050     CFileStore::CFileStore()
00051         :
00052             m_db(0),
00053             m_dbfilename(0)
00054     {
00055         LOGPRINTF("entry");
00056         LOGPRINTF("exit");
00057     }
00058 
00059     CFileStore::~CFileStore()
00060     {
00061         LOGPRINTF("entry");
00062         closeDatabase();
00063         LOGPRINTF("exit");
00064     }
00065 
00066     np_result CFileStore::openDatabase(const gchar* dir, const gchar* filename)
00067     {
00068         LOGPRINTF("entry");
00069         g_assert(m_db == 0 );
00070         g_return_val_if_fail((dir !=0) && (dir[0] != '\0'), NP_NO_FILENAME);
00071         g_return_val_if_fail((filename !=0) && (filename[0] != '\0'), NP_NO_FILENAME);
00072 
00073         erMetadb db = ermetadb_local_open_custom(dir, filename);
00074         if (db == 0)
00075         {
00076             LOGPRINTF("opening file failed");
00077             return NP_CREATE_FAILED;
00078         }
00079         m_db = db;
00080 
00081         LOGPRINTF("exit");
00082         return NP_OK;
00083     }
00084 
00085     void CFileStore::closeDatabase(void)
00086     {
00087         LOGPRINTF("entry");
00088 
00089         if (m_dbfilename) g_free(m_dbfilename);
00090         m_dbfilename = 0;
00091 
00092         if (m_db) 
00093         {
00094             ermetadb_close(m_db);
00095             m_db = 0;
00096         }
00097 
00098         LOGPRINTF("exit"); 
00099     }
00100 
00101     np_result CFileStore::createDatabase(const gchar* dir, const gchar* file, const char* title)
00102     {
00103         LOGPRINTF("entry");
00104 
00105         // precondition: there is no db file
00106         g_assert(m_db == 0);
00107         // input check
00108         g_return_val_if_fail((dir != 0) && (dir[0] != '\0'), NP_NO_FILENAME);
00109         g_return_val_if_fail((file != 0) && (file[0] != '\0'), NP_NO_FILENAME);
00110         g_return_val_if_fail((title != 0) && (title[0] != '\0'), NP_NO_FILENAME);
00111 
00112         if ( ! g_file_test(dir, G_FILE_TEST_EXISTS) )
00113         {
00114             LOGPRINTF("dir %s did not exist, created", dir);
00115             // try to create directory
00116             if (g_mkdir(dir, 0775) != 0)
00117             {
00118                 WARNPRINTF("failed to create Notes directory.");
00119                 return NP_IS_NOT_DIR;
00120             }
00121         }
00122 
00123         if ( ! g_file_test(dir, G_FILE_TEST_IS_DIR))
00124         {
00125             LOGPRINTF("%s is no directory", dir);
00126             return NP_IS_NOT_DIR;
00127         }
00128 
00129         // precondition: directory Notes should exist
00130         // create database
00131         int ret = ermetadb_local_create_database(dir, file);
00132         if (ret != ER_OK) {
00133             WARNPRINTF("ermetadb_new failed");
00134             return NP_CREATE_FAILED;
00135         }
00136 
00137         // add new file to global
00138         const char* mountpoint = ipc_get_mountpoint();
00139         erMetadb global_db = ermetadb_global_open(mountpoint, FALSE);
00140         if (global_db == 0) {
00141             WARNPRINTF("failed to open globaldb.");
00142             return NP_OPEN_FAILED;
00143         }
00144 
00145         // date and size attributes
00146         struct stat statbuf;
00147         gchar* fname = g_strdup_printf("%s/%s", dir, file); 
00148         stat(fname, &statbuf);
00149         gint64 size = statbuf.st_size;
00150         gint64 mtime = statbuf.st_mtime;
00151         LOGPRINTF("%s size %lld, mtime %lld", fname, size, mtime);
00152         g_free(fname);
00153  
00154         ret = ermetadb_global_add_file(global_db, dir, file, size, mtime, title, NOTE_AUTHOR, NOTE_TAG);
00155         if (ret != ER_OK) {
00156             WARNPRINTF("ermetadb_add_file failed");
00157         }
00158 
00159         ermetadb_close(global_db);
00160 
00161         LOGPRINTF("exit");
00162         return openDatabase(dir, file);
00163     }
00164 
00165     np_result    CFileStore::renameDatabase(const gchar* dir, const gchar* srcfile, const gchar* destfile)
00166     {
00167         LOGPRINTF("entry");
00168         
00169         g_assert(m_db != 0);
00170         // input check
00171         g_return_val_if_fail((dir != 0) && (dir[0] != '\0'), NP_NO_FILENAME);
00172         g_return_val_if_fail((srcfile != 0) && (srcfile[0] != '\0'), NP_NO_FILENAME);
00173         g_return_val_if_fail((destfile != 0) && (destfile[0] != '\0'), NP_NO_FILENAME);
00174 
00175         LOGPRINTF("dir %s srcfile %s destfile %s", dir, srcfile, destfile);
00176 
00177         if (0 == g_strcmp0(srcfile, destfile))
00178         {
00179             LOGPRINTF("same name, do nothing");
00180             return NP_OK;
00181         }
00182 
00183         // rename on filesystem
00184         gchar *dst = g_strdup_printf("%s/%s", dir, destfile);
00185         gchar *src = g_strdup_printf("%s/%s", dir, srcfile);
00186         int err = g_rename(src, dst);
00187         g_free(src);
00188         g_free(dst);
00189         if (err) {
00190             WARNPRINTF("rename failed.");
00191             return NP_RENAME_FAILED;
00192         }
00193 
00194         // rename in global database
00195         const char* mountpoint = ipc_get_mountpoint();
00196         erMetadb global_db = ermetadb_global_open(mountpoint, FALSE);
00197         if (global_db == 0) {
00198             WARNPRINTF("rename failed.");
00199             return NP_OPEN_FAILED;
00200         }
00201         gchar* title = noExt(destfile); 
00202         int ret = ermetadb_global_rename_file(global_db, dir, srcfile, destfile, title);
00203         //TODO call global_change_file to update date and size attributes
00204         g_free(title);
00205         ermetadb_close(global_db);
00206         if (ret != ER_OK) {
00207             WARNPRINTF("rename failed.");
00208             return NP_RENAME_FAILED;
00209         }
00210 
00211         LOGPRINTF("exit success");
00212         return NP_OK;
00213     }
00214 
00215     np_result    CFileStore::createFile(const gchar* filename)
00216     {
00217         LOGPRINTF("filename='%s'", filename ? filename : "NULL");
00218 
00219         g_assert(m_dbfilename == 0);
00220         g_assert(m_db == 0);
00221 
00222         // determine dir + filename
00223         gchar* dir = 0;
00224         gchar* file = 0;
00225         if (  (filename != 0) && (filename[0] != '\0'))
00226         {
00227             dir = g_path_get_dirname(filename);
00228             file = g_path_get_basename(filename);
00229         }
00230         else 
00231         {
00232             dir = g_strdup_printf("%s/%s", ipc_get_mountpoint(), NOTE_DIR); 
00233             file = generateNewFilename();
00234         }
00235 
00236         gchar* title = 0;
00237         gchar* iter = g_strrstr(file, NOTE_EXTENSION);
00238         if ( iter == 0 ) // no needle found in the haystack
00239         {
00240             title =  g_strdup(file);
00241         }
00242         else
00243         {
00244             title =  g_strndup(file, (iter - file));
00245         }
00246 
00247         np_result result = createDatabase(dir, file, title);
00248         if (result == NP_OK)
00249         {
00250             m_dbfilename = g_strdup_printf("%s/%s", dir, file); 
00251         }
00252         else
00253         {
00254             WARNPRINTF("failed to create");
00255         }
00256 
00257         g_free(dir);
00258         g_free(file);
00259         g_free(title);
00260 
00261         LOGPRINTF("exit");
00262         return result;
00263     }
00264 
00265     np_result    CFileStore::openFile(const gchar* filename)
00266     {
00267         LOGPRINTF("entry");
00268 
00269         g_assert( m_dbfilename == 0);
00270         g_assert( m_db == 0);
00271 
00272         // input check
00273         g_return_val_if_fail((filename != 0) && (filename[0] != '\0'), NP_NO_FILENAME);
00274 
00275         gchar* directory = g_path_get_dirname(filename);
00276         gchar* file = g_path_get_basename(filename);
00277         np_result result = openDatabase(directory, file);
00278 
00279         if ( result == NP_OK)
00280         {
00281             m_dbfilename = g_strdup(filename);
00282         }
00283         else
00284         {
00285             WARNPRINTF("failed to open");
00286         }
00287 
00288         g_free(directory);
00289         g_free(file);
00290 
00291         LOGPRINTF("exit");
00292         return result;
00293     }
00294 
00295     void    CFileStore::closeFile()
00296     {
00297         return closeDatabase();
00298     }
00299 
00300     np_result    CFileStore::renameFile(const gchar* newshortfilename)
00301     {
00302         LOGPRINTF("entry");
00303         np_result result = NP_OK;
00304 
00305         g_assert( m_dbfilename != 0);
00306         g_assert( m_db != 0);
00307 
00308         // input check
00309         g_return_val_if_fail((newshortfilename != 0) && (newshortfilename[0] != '\0'), NP_NO_FILENAME);
00310 
00311         // assemble filenames 
00312         gchar* dir = getFileNameDirPart();
00313         gchar* newfilename = 0;
00314         int len = strlen(newshortfilename) - strlen(NOTE_EXTENSION);
00315         if (0 == g_strcmp0(newshortfilename + len, NOTE_EXTENSION))
00316         {
00317             g_assert(len > 0);
00318             newfilename = g_strdup_printf("%s/%s", dir, newshortfilename); 
00319         }
00320         else
00321         {
00322             newfilename = g_strdup_printf("%s/%s%s", dir, newshortfilename, NOTE_EXTENSION); 
00323         }
00324         LOGPRINTF("oldfilename %s, newfilename %s", m_dbfilename, newfilename);
00325 
00326         // test if new == old;
00327         if (0 == g_strcmp0(m_dbfilename, newfilename))
00328         {
00329             LOGPRINTF("save as same filename");
00330             result = NP_OK;
00331         }
00332         else
00333         {
00334             // test if file already exists
00335             if (g_file_test(newfilename, G_FILE_TEST_EXISTS))
00336                 result = NP_FILE_EXISTS;
00337 
00338             if (result != NP_FILE_EXISTS)
00339             {
00340                 gchar* srcfile  = getFileNameFilePart();
00341                 gchar* destfile = g_path_get_basename(newfilename);
00342 
00343                 // create local db with filename
00344                 result = renameDatabase(dir, srcfile, destfile); 
00345                 if (result == NP_OK)
00346                 {
00347                     // rename succeeded, store filename
00348                     g_free(m_dbfilename);
00349                     m_dbfilename = g_strdup(newfilename);
00350                 }
00351                 g_free(srcfile);
00352                 g_free(destfile);
00353             }
00354         }
00355 
00356         g_free(dir);
00357         g_free(newfilename);
00358 
00359         LOGPRINTF("FUNCTION EXIT");
00360         return result; 
00361     }
00362 
00363     gchar* CFileStore::getFilename(void)
00364     {
00365         // CRASHES if run after constructor!!
00366         return m_dbfilename;
00367     }
00368 
00369     erMetadb CFileStore::getFile()
00370     {
00371         return m_db;
00372     }
00373 
00374     // caller takes ownership of gchar object
00375     gchar* CFileStore::generateNewFilename(void)
00376     {  
00377         // format: Note_2010-02-19__10-07-05.note 
00378         time_t rawtime;
00379         time(&rawtime);
00380         struct tm * tms;
00381         tms = localtime(&rawtime);
00382 
00383         gchar* fname = g_strdup_printf("Note_%d-%02d-%02d__%02d-%02d-%02d%s", 
00384                                        1900+tms->tm_year, 
00385                                        1+tms->tm_mon, 
00386                                        tms->tm_mday, 
00387                                        tms->tm_hour, 
00388                                        tms->tm_min, 
00389                                        tms->tm_sec, 
00390                                        NOTE_EXTENSION);
00391         return fname;
00392     }
00393 
00394     gchar* CFileStore::noExt(const gchar* filename)
00395     {
00396         g_assert(filename != 0);
00397         // filename has shape: '/path/...morepathelements.../basename.extension'
00398         gchar* basename = g_path_get_basename(filename);
00399         gchar* iter = g_strrstr(basename, NOTE_EXTENSION);
00400         if (iter == 0) // no needle found in the haystack
00401         {
00402             return basename ;
00403         }
00404         gchar* basename_noext = g_strndup(basename, (iter - basename));
00405         g_free(basename);
00406         return basename_noext;
00407     }
00408 
00409     // caller takes ownership -> so caller must g_free() when not needed anymore
00410     gchar* CFileStore::getFileNameDirPart(void)
00411     {
00412         g_assert( m_dbfilename != 0);
00413         return g_path_get_dirname(m_dbfilename);
00414     }
00415 
00416     // caller takes ownership -> so caller must g_free() when not needed anymore
00417     gchar* CFileStore::getFileNameFilePart(void)
00418     {
00419         g_assert( m_dbfilename != 0);
00420         return g_path_get_basename(m_dbfilename);
00421     }
00422 
00423     // caller takes ownership -> so caller must g_free() when not needed anymore
00424     gchar* CFileStore::getFileNameFilePartNoExt(void)
00425     {
00426         g_assert( m_dbfilename != 0);
00427         return noExt(m_dbfilename);
00428     }
00429 
00430     // ----- Added metadata table mangling to store and load the last read page in a scribble document
00431 
00432     /// Save key and value pair into application data table.
00433     gboolean CFileStore::save_string(const char *key, const gchar* value)
00434     {
00435         // Create table.
00436         metadata_table *value_table = metadata_table_new();
00437         if (value_table == 0)
00438         {
00439             return false;
00440         }
00441 
00442         // Set key.
00443         metadata_table_add_column(value_table, key);
00444 
00445         // Assign value.
00446         int ret = -1;
00447         int index = metadata_table_find_column(value_table, key);
00448         if (index >= 0)
00449         {
00450             ret = metadata_table_set_text(value_table, index, value);
00451             if (ret != ER_OK)
00452             {
00453                 ERRORPRINTF("Cannot write value %s to table.", value);
00454             }
00455         }
00456 
00457         // TODO: we use "." for filename -- for we do not want to record the filemane into the db format.
00458         // If we did, then we would have to update the filename when the db is renamed.
00459         ret = ermetadb_local_set_application_data(m_db, ".", value_table);
00460         metadata_table_free(value_table);
00461         return static_cast<gboolean>(ret == ER_OK);
00462     }
00463 
00464 
00465     /// Load application data according to the key.
00466     gboolean CFileStore::load_string(const char *key, gchar** value)
00467     {
00468         LOGPRINTF("entry, key %s", key);
00469         // Create table.
00470         metadata_table *name_table = metadata_table_new();
00471         if (name_table == NULL)
00472         {
00473             return FALSE;
00474         }
00475 
00476         // Add column.
00477         metadata_table_add_column(name_table, key);
00478 
00479         // Query.
00480         metadata_table *results_table = NULL;
00481         int ret = ermetadb_local_get_application_data(m_db, ".", name_table, &results_table);
00482 
00483         // free the name table
00484         metadata_table_free(name_table);
00485 
00486         if (results_table == NULL)
00487         {
00488             LOGPRINTF("No result.");
00489             return FALSE;
00490         }
00491         if (ret != ER_OK)
00492         {
00493             ERRORPRINTF("Could not get application data.");
00494         }
00495 
00496         const metadata_cell *cell = NULL;
00497         int index = metadata_table_find_column(results_table, key);
00498 
00499         if (index >= 0)
00500         {   
00501             cell = metadata_table_get_cell(results_table, index);
00502             if (cell != 0) 
00503             {
00504                 if (cell->type == METADATA_TEXT)
00505                 {
00506                     *value = g_strdup(cell->value.v_text->str);
00507                 }
00508                 else
00509                 {
00510                     ERRORPRINTF("wrong cell type"); // Note: Pair of metadata where value is text. Not in lib. 
00511                     ret = ER_NOT_FOUND;
00512                 }
00513             }
00514             else
00515             {
00516                 ERRORPRINTF("Cannot find cell");
00517                 ret = ER_NOT_FOUND;
00518             }
00519         }
00520         else
00521         {
00522             ERRORPRINTF("Cannot find column");
00523             ret = ER_NOT_FOUND;
00524         }
00525 
00526         // free the results table
00527         metadata_table_free(results_table);
00528         return static_cast<gboolean>(ret == ER_OK);
00529     }
00530 
00531     /// Load last read location from meta database.
00532     gint CFileStore::get_last_read_location(void)
00533     {
00534         gchar* location = 0;
00535         gboolean result = load_string (SCRIBBLE_LAST_READ_LOCATION, &location);
00536         gint last_read = 0;
00537         if ( ! result )
00538         {
00539             last_read = -1;
00540         }
00541         else if (sscanf( location, "%d", &last_read ) < 0)
00542         {
00543             last_read = -1;
00544         }
00545         g_free(location);
00546         LOGPRINTF( "get_last_read_location -- last_read = %d", last_read);
00547         return last_read;
00548     }
00549 
00550     gboolean CFileStore::set_last_read_location(gint location)
00551     {
00552         LOGPRINTF( "set_last_read_location -- last_read = %d", location);
00553         gboolean result = FALSE;
00554         gchar* my_location = g_strdup_printf("%d", location);
00555         result = save_string( SCRIBBLE_LAST_READ_LOCATION, my_location);
00556         g_free(my_location);
00557         return result; 
00558     }
00559 
00560 }
00561 
00562 
Generated by  doxygen 1.6.2-20100208