34 #include <glib/gi18n.h>
35 #include <glib/gstdio.h>
41 #include <sys/types.h>
59 # define mktemp _mktemp
65 # define g_fopen fopen
80 #include "gnc-address-xml-v2.h"
81 #include "gnc-bill-term-xml-v2.h"
82 #include "gnc-customer-xml-v2.h"
83 #include "gnc-employee-xml-v2.h"
84 #include "gnc-entry-xml-v2.h"
85 #include "gnc-invoice-xml-v2.h"
86 #include "gnc-job-xml-v2.h"
87 #include "gnc-order-xml-v2.h"
88 #include "gnc-owner-xml-v2.h"
89 #include "gnc-tax-table-xml-v2.h"
90 #include "gnc-vendor-xml-v2.h"
93 # include "strptime.h"
98 static gboolean save_may_clobber_data (
QofBackend *bend);
107 char *pathbuf = NULL, *path = NULL, *tmpbuf = NULL;
108 size_t pathbuf_size = 0;
113 rc = g_stat (be->lockfile, &statbuf);
121 be->lockfd = g_open (be->lockfile, O_RDWR | O_CREAT | O_EXCL , S_IRUSR | S_IWUSR);
130 PWARN(
"Unable to create the lockfile %s; may not have write priv",
159 pathbuf_size = strlen (be->lockfile) + 100;
160 pathbuf = (
char *) malloc (pathbuf_size);
161 if (pathbuf == NULL) {
164 strcpy (pathbuf, be->lockfile);
165 path = strrchr (pathbuf,
'.');
166 while (snprintf (path, pathbuf_size - (path - pathbuf),
".%lx.%d.LNK", gethostid(), getpid())
167 >= pathbuf_size - (path - pathbuf))
170 tmpbuf = (
char *) realloc (pathbuf, pathbuf_size);
171 if (tmpbuf == NULL) {
179 rc = link (be->lockfile, pathbuf);
183 if (errno == EPERM || errno == ENOSYS
185 || errno == EOPNOTSUPP
202 g_unlink (be->lockfile);
206 rc = g_stat (be->lockfile, &statbuf);
216 g_unlink (be->lockfile);
220 if (statbuf.st_nlink != 2)
226 g_unlink (be->lockfile);
230 be->linkfile = g_strdup (pathbuf);
244 #define XML_URI_PREFIX "xml://"
245 #define FILE_URI_PREFIX "file://"
249 const char *book_id, gboolean ignore_lock,
250 gboolean create, gboolean force)
259 if (NULL == be->fullpath)
266 if (create && !force && save_may_clobber_data( be_start ) )
269 LEAVE(
"Might clobber, no force");
274 be->dirname = g_path_get_dirname (be->fullpath);
281 rc = g_stat (be->dirname, &statbuf);
284 || (statbuf.st_mode & _S_IFDIR) == 0
286 || !S_ISDIR(statbuf.st_mode)
294 PWARN (
"Couldn't find directory for %s", be->fullpath);
295 g_free (be->fullpath);
297 g_free (be->dirname);
304 rc = g_stat (be->fullpath, &statbuf);
305 if ((rc != 0) && (!create))
310 PWARN (
"Couldn't find %s", be->fullpath);
311 g_free (be->fullpath);
313 g_free (be->dirname);
320 && (statbuf.st_mode & _S_IFDIR) != 0
322 && S_ISDIR(statbuf.st_mode)
329 PWARN(
"Path %s is a directory", be->fullpath);
330 g_free (be->fullpath);
332 g_free (be->dirname);
344 PINFO (
"logpath=%s", be->fullpath ? be->fullpath :
"(null)");
347 be->lockfile = g_strconcat(be->fullpath,
".LCK", NULL);
349 if (!ignore_lock && !gnc_xml_be_get_file_lock (be))
354 g_free (be->lockfile);
396 g_unlink (be->linkfile);
407 rv = g_chmod (be->lockfile, S_IWRITE | S_IREAD);
409 rv = g_unlink (be->lockfile);
412 PWARN(
"Error on g_unlink(%s): %d: %s", be->lockfile,
413 errno, g_strerror(errno) ? g_strerror(errno) :
"");
417 g_free (be->dirname);
420 g_free (be->fullpath);
423 g_free (be->lockfile);
426 g_free (be->linkfile);
437 qof_backend_destroy(be);
453 #define buf_size 1024
456 copy_file(
const char *orig,
const char *bkup)
469 orig_fd = g_open(orig, O_RDONLY | flags, 0);
474 bkup_fd = g_open(bkup, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL | flags, 0600);
483 count_read = read(orig_fd, buf, buf_size);
484 if (count_read == -1 && errno != EINTR)
493 count_write = write(bkup_fd, buf, count_read);
494 if (count_write == -1)
502 while (count_read > 0);
513 gnc_int_link_or_make_backup(
FileBackend *be,
const char *orig,
const char *bkup)
515 gboolean copy_success = FALSE;
526 if (errno == EPERM || errno == ENOSYS
528 || errno == EOPNOTSUPP
539 copy_success = copy_file(orig, bkup);
545 PWARN (
"unable to make file backup from %s to %s: %s",
546 orig, bkup, g_strerror(errno) ? g_strerror(errno) :
"");
556 static QofBookFileType
557 gnc_xml_be_determine_file_type(
const char *path)
559 gboolean with_encoding;
560 QofBookFileType v2type;
563 if (v2type == GNC_BOOK_XML2_FILE)
567 return GNC_BOOK_XML2_FILE;
571 return GNC_BOOK_XML2_FILE_NO_ENCODING;
574 else if (v2type == GNC_BOOK_POST_XML2_0_0_FILE)
576 return GNC_BOOK_POST_XML2_0_0_FILE;
578 else if (v2type == GNC_BOOK_XML1_FILE)
580 return GNC_BOOK_XML1_FILE;
582 return GNC_BOOK_NOT_OURS;
586 gnc_determine_file_type (
const char *uri)
592 QofBookFileType xml_type;
606 t = g_fopen( filename,
"r" );
614 rc = g_stat(filename, &sbuf);
620 if (sbuf.st_size == 0)
622 PINFO (
" empty file");
627 if ((xml_type == GNC_BOOK_XML2_FILE) ||
628 (xml_type == GNC_BOOK_XML1_FILE) ||
629 (xml_type == GNC_BOOK_POST_XML2_0_0_FILE))
634 PINFO (
" %s is not a gnc XML file", filename);
648 const char *datafile;
652 datafile = be->fullpath;
654 rc = g_stat (datafile, &statbuf);
656 return (errno == ENOENT);
658 if (gnc_xml_be_determine_file_type(datafile) == GNC_BOOK_BIN_FILE)
661 const char *back =
"-binfmt.bkup";
662 char *bin_bkup = g_new(
char, strlen(datafile) + strlen(back) + 1);
663 strcpy(bin_bkup, datafile);
664 strcat(bin_bkup, back);
665 bkup_ret = gnc_int_link_or_make_backup(be, datafile, bin_bkup);
674 backup = g_strconcat( datafile,
".", timestamp, GNC_DATAFILE_EXT, NULL );
677 bkup_ret = gnc_int_link_or_make_backup(be, datafile, backup);
688 const gchar *datafile,
689 gboolean make_backup)
697 ENTER (
" book=%p file=%s", book, datafile);
711 tmp_name = g_new(
char, strlen(datafile) + 12);
712 strcpy(tmp_name, datafile);
713 strcat(tmp_name,
".tmp-XXXXXX");
715 if (!mktemp(tmp_name))
725 if (!gnc_xml_be_backup_file(fbe))
732 if (gnc_book_write_to_xml_file_v2(book, tmp_name, gnc_prefs_get_file_save_compressed()))
735 rc = g_stat(datafile, &statbuf);
739 g_assert(g_strcmp0(tmp_name,
"/dev/null") != 0);
742 if (g_chmod(tmp_name, statbuf.st_mode) != 0)
751 PWARN(
"unable to chmod filename %s: %s",
752 tmp_name ? tmp_name :
"(null)",
753 g_strerror(errno) ? g_strerror(errno) :
"");
762 if (chown(tmp_name, -1, statbuf.st_gid) != 0)
768 PWARN(
"unable to chown filename %s: %s",
769 tmp_name ? tmp_name :
"(null)",
770 strerror(errno) ? strerror(errno) :
"");
778 if (g_unlink(datafile) != 0 && errno != ENOENT)
781 PWARN(
"unable to unlink filename %s: %s",
782 datafile ? datafile :
"(null)",
783 g_strerror(errno) ? g_strerror(errno) :
"");
788 if (!gnc_int_link_or_make_backup(fbe, tmp_name, datafile))
792 datafile ? datafile :
"NULL" );
797 if (g_unlink(tmp_name) != 0)
800 PWARN(
"unable to unlink temp filename %s: %s",
801 tmp_name ? tmp_name :
"(null)",
802 g_strerror(errno) ? g_strerror(errno) :
"");
812 LEAVE (
" successful save of book=%p to file=%s", book, datafile);
817 if (g_unlink(tmp_name) != 0)
833 PWARN(
"unable to unlink temp_filename %s: %s",
834 tmp_name ? tmp_name :
"(null)",
835 g_strerror(errno) ? g_strerror(errno) :
"");
843 tmp_name ? tmp_name :
"NULL" );
865 struct stat lockstatbuf, statbuf;
868 if (g_stat (be->lockfile, &lockstatbuf) != 0)
871 dir = g_dir_open (be->dirname, 0, NULL);
876 while ((dent = g_dir_read_name(dir)) != NULL)
881 if ( !(g_str_has_suffix(dent,
".LNK") ||
882 g_str_has_suffix(dent,
".xac") ||
883 g_str_has_suffix(dent, GNC_DATAFILE_EXT) ||
884 g_str_has_suffix(dent, GNC_LOGFILE_EXT)) )
887 name = g_build_filename(be->dirname, dent, (gchar*)NULL);
890 if (!g_str_has_prefix(name, be->fullpath))
897 if (g_strcmp0(name, be->fullpath) == 0)
904 if (g_str_has_suffix(name,
".LNK"))
907 if ((g_strcmp0(name, be->linkfile) != 0) &&
909 (g_stat(name, &statbuf) == 0) &&
910 (statbuf.st_mtime < lockstatbuf.st_mtime))
912 PINFO (
"remove stale lock file: %s", name);
934 gchar *stamp_start = name + strlen(be->fullpath);
935 gchar *expression = g_strdup_printf (
"^\\.[[:digit:]]{14}(\\%s|\\%s|\\.xac)$",
936 GNC_DATAFILE_EXT, GNC_LOGFILE_EXT);
937 gboolean got_date_stamp = FALSE;
939 if (regcomp(&pattern, expression, REG_EXTENDED | REG_ICASE) != 0)
940 PWARN(
"Cannot compile regex for date stamp");
941 else if (regexec(&pattern, stamp_start, 0, NULL, 0) == 0)
942 got_date_stamp = TRUE;
957 if (gnc_prefs_get_file_retention_policy() == XML_RETAIN_NONE)
959 PINFO (
"remove stale file: %s - reason: preference XML_RETAIN_NONE", name);
962 else if ((gnc_prefs_get_file_retention_policy() == XML_RETAIN_DAYS) &&
963 (gnc_prefs_get_file_retention_days() > 0))
968 if (g_stat(name, &statbuf) != 0)
973 days = (int)(difftime(now, statbuf.st_mtime) / 86400);
975 PINFO (
"file retention = %d days", gnc_prefs_get_file_retention_days());
976 if (days >= gnc_prefs_get_file_retention_days())
978 PINFO (
"remove stale file: %s - reason: more than %d days old", name, days);
991 ENTER (
"book=%p, fbe->book=%p", book, fbe->book);
999 if (NULL == fbe->book) fbe->book = book;
1000 if (book != fbe->book)
return;
1009 gnc_xml_be_write_to_file (fbe, book, fbe->fullpath, TRUE);
1010 gnc_xml_be_remove_old_files (fbe);
1011 LEAVE (
"book=%p", book);
1029 str = g_new (
char, len);
1030 strcpy (str, fbe->fullpath);
1034 p = strrchr (str, G_DIR_SEPARATOR);
1036 p = stpcpy (p,
"book-");
1038 p = stpcpy (p,
"-");
1039 q = strrchr (fbe->fullpath, G_DIR_SEPARATOR);
1042 p = stpcpy (p,
".gml");
1050 if (0) build_period_filepath(0, 0);
1054 const char * filepath;
1057 if (strcmp (GNC_ID_PERIOD, typ))
return;
1058 filepath = build_period_filepath(fbe, book);
1059 PINFO (
" ====================== book=%p filepath=%s\n", book, filepath);
1061 if (NULL == fbe->primary_book)
1063 PERR (
"You should have saved the data "
1064 "at least once before closing the books!\n");
1081 if (strcmp (GNC_ID_PERIOD, typ))
return;
1082 PINFO (
"book=%p", book);
1098 const char * filepath;
1100 if (strcmp (GNC_ID_PERIOD, typ))
return;
1101 filepath = build_period_filepath(fbe, book);
1102 PINFO (
" ====================== book=%p filepath=%s\n", book, filepath);
1103 gnc_xml_be_write_to_file(fbe, book, filepath, FALSE);
1109 gnc_xml_be_write_to_file (fbe, fbe->primary_book, fbe->fullpath, TRUE);
1123 gnc_xml_be_load_from_file (
QofBackend *bend,
QofBook *book, QofBackendLoadType loadType)
1129 if (loadType != LOAD_TYPE_INITIAL_LOAD)
return;
1131 error = ERR_BACKEND_NO_ERR;
1134 switch (gnc_xml_be_determine_file_type(be->fullpath))
1136 case GNC_BOOK_XML2_FILE:
1140 PWARN(
"Syntax error in Xml File %s", be->fullpath );
1145 case GNC_BOOK_XML2_FILE_NO_ENCODING:
1147 PWARN(
"No character encoding in Xml File %s", be->fullpath );
1149 case GNC_BOOK_XML1_FILE:
1153 PWARN(
"Syntax error in Xml File %s", be->fullpath );
1157 case GNC_BOOK_POST_XML2_0_0_FILE:
1159 PWARN(
"Version of Xml file %s is newer than what we can read", be->fullpath );
1168 PWARN(
"No read permission to file");
1172 PWARN(
"Filename is a directory");
1176 PWARN(
"File not any known type");
1183 if (error != ERR_BACKEND_NO_ERR)
1197 struct stat statbuf;
1202 if (g_stat(bend->
fullpath, &statbuf) == 0)
return TRUE;
1210 const gchar *datafile;
1213 gnc_book_write_accounts_to_xml_file_v2(be, book, datafile);
1219 gnc_backend_new(
void)
1226 qof_backend_init(be);
1228 be->session_begin = xml_session_begin;
1229 be->session_end = xml_session_end;
1230 be->destroy_backend = xml_destroy_backend;
1232 be->load = gnc_xml_be_load_from_file;
1235 be->begin = xml_begin_edit;
1236 be->commit = xml_commit_edit;
1237 be->rollback = xml_rollback_edit;
1240 be->compile_query = NULL;
1241 be->free_query = NULL;
1242 be->run_query = NULL;
1245 be->events_pending = NULL;
1246 be->process_events = NULL;
1248 be->sync = xml_sync_all;
1249 be->load_config = NULL;
1250 be->get_config = NULL;
1252 be->
export_fn = gnc_xml_be_write_accounts_to_file;
1254 gnc_be->dirname = NULL;
1255 gnc_be->fullpath = NULL;
1256 gnc_be->lockfile = NULL;
1257 gnc_be->linkfile = NULL;
1258 gnc_be->lockfd = -1;
1260 gnc_be->book = NULL;
1266 business_core_xml_init(
void)
1269 gnc_address_xml_initialize ();
1270 gnc_billterm_xml_initialize ();
1271 gnc_customer_xml_initialize ();
1272 gnc_employee_xml_initialize ();
1273 gnc_entry_xml_initialize ();
1274 gnc_invoice_xml_initialize ();
1275 gnc_job_xml_initialize ();
1276 gnc_order_xml_initialize ();
1277 gnc_owner_xml_initialize ();
1278 gnc_taxtable_xml_initialize ();
1279 gnc_vendor_xml_initialize ();
1290 #ifndef GNC_NO_LOADABLE_MODULES
1291 G_MODULE_EXPORT
void
1319 business_core_xml_init();
gboolean qof_session_load_from_xml_file_v2(FileBackend *, QofBook *, QofBookFileType)
QofBackend *(* backend_new)(void)
void qof_backend_set_error(QofBackend *be, QofBackendError err)
void(* provider_free)(QofBackendProvider *)
QofBook * qof_instance_get_book(gconstpointer)
void qof_backend_register_provider(QofBackendProvider *)
#define PINFO(format, args...)
void gnc_module_init_backend_xml(void)
const gchar * QofIdTypeConst
QofBackendError
The errors that can be reported to the GUI & other front-end users.
gboolean qof_instance_get_destroying(gconstpointer ptr)
#define QOF_STDOUT
Allow session data to be printed to stdout.
gchar * gnc_uri_get_path(const gchar *uri)
void(* export_fn)(QofBackend *, QofBook *)
QofCollection * qof_instance_get_collection(gconstpointer inst)
gchar * guid_to_string_buff(const GncGUID *guid, gchar *buff)
#define PERR(format, args...)
#define ENTER(format, args...)
api for Version 1 XML-based file format
#define PWARN(format, args...)
void qof_backend_set_message(QofBackend *be, const char *format,...)
void qof_book_mark_session_saved(QofBook *book)
api for GnuCash version 2 XML-based file format
#define GUID_ENCODING_LENGTH
G_MODULE_EXPORT void qof_backend_module_init(void)
QofBookFileType gnc_is_xml_data_file_v2(const gchar *name, gboolean *with_encoding)
gboolean(* check_data_type)(const char *)
Distinguish two providers with same access method.
QofBackendError qof_backend_get_error(QofBackend *be)
All type declarations for the whole Gnucash engine.
#define qof_book_get_guid(X)
void qof_book_mark_session_dirty(QofBook *book)
Generic api to store and retrieve preferences.
API for the transaction logger.
const char * provider_name
gboolean qof_book_is_readonly(const QofBook *book)
void xaccLogSetBaseName(const char *basepath)
const char * access_method
gboolean qof_session_load_from_xml_file(QofBook *, const char *filename)
#define LEAVE(format, args...)
time64 gnc_time(time64 *tbuf)
get the current local time
Utility functions for convert uri in separate components and back.
load and save data to files
gboolean qof_get_alt_dirty_mode(void)
const gchar * QofLogModule
char * gnc_date_timestamp(void)
Make a timestamp in YYYYMMDDHHMMSS format.