Branch data Line data Source code
1 : : /********************************************************************\
2 : : * TransLog.c -- the transaction logger *
3 : : * Copyright (C) 1998 Linas Vepstas *
4 : : * *
5 : : * This program is free software; you can redistribute it and/or *
6 : : * modify it under the terms of the GNU General Public License as *
7 : : * published by the Free Software Foundation; either version 2 of *
8 : : * the License, or (at your option) any later version. *
9 : : * *
10 : : * This program is distributed in the hope that it will be useful, *
11 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 : : * GNU General Public License for more details. *
14 : : * *
15 : : * You should have received a copy of the GNU General Public License*
16 : : * along with this program; if not, contact: *
17 : : * *
18 : : * Free Software Foundation Voice: +1-617-542-5942 *
19 : : * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
20 : : * Boston, MA 02110-1301, USA gnu@gnu.org *
21 : : * *
22 : : \********************************************************************/
23 : :
24 : : #include <config.h>
25 : : #ifdef __MINGW32__
26 : : #define __USE_MINGW_ANSI_STDIO 1
27 : : #endif
28 : : #include <errno.h>
29 : : #include <glib.h>
30 : : #include <glib/gstdio.h>
31 : : #include <string.h>
32 : :
33 : : #include "Account.h"
34 : : #include "Transaction.h"
35 : : #include "TransactionP.hpp"
36 : : #include "TransLog.h"
37 : : #include "qof.h"
38 : : #ifdef _MSC_VER
39 : : # define g_fopen fopen
40 : : #endif
41 : :
42 : : static QofLogModule log_module = "gnc.translog";
43 : :
44 : : /*
45 : : * Some design philosophy that I think would be good to keep in mind:
46 : : * (0) Simplicity and foolproofness are the over-riding design points.
47 : : * This is supposed to be a fail-safe safety net. We don't want
48 : : * our safety net to fail because of some whiz-bang shenanigans.
49 : : *
50 : : * (1) Try to keep the code simple. Want to make it simple and obvious
51 : : * that we are recording everything that we need to record.
52 : : *
53 : : * (2) Keep the printed format human readable, for the same reasons.
54 : : * (2.a) Keep the format, simple, flat, more or less unstructured,
55 : : * record oriented. This will help parsing by perl scripts.
56 : : * No, using a perl script to analyze a file that's supposed to
57 : : * be human readable is not a contradication in terms -- that's
58 : : * exactly the point.
59 : : * (2.b) Use tabs as a human friendly field separator; its also a
60 : : * character that does not (should not) appear naturally anywhere
61 : : * in the data, as it serves no formatting purpose in the current
62 : : * GUI design. (hack alert -- this is not currently tested for
63 : : * or enforced, so this is a very unsafe assumption. Maybe
64 : : * urlencoding should be used.)
65 : : * (2.c) Don't print redundant information in a single record. This
66 : : * would just confuse any potential user of this file.
67 : : * (2.d) Saving space, being compact is not a priority, I don't think.
68 : : *
69 : : * (3) There are no compatibility requirements from release to release.
70 : : * Sounds OK to me to change the format of the output when needed.
71 : : *
72 : : * (-) print transaction start and end delimiters
73 : : * (-) print a unique transaction id as a handy label for anyone
74 : : * who actually examines these logs.
75 : : * The C address pointer to the transaction struct should be fine,
76 : : * as it is simple and unique until the transaction is deleted ...
77 : : * and we log deletions, so that's OK. Just note that the id
78 : : * for a deleted transaction might be recycled.
79 : : * (-) print the current timestamp, so that if it is known that a bug
80 : : * occurred at a certain time, it can be located.
81 : : * (-) hack alert -- something better than just the account name
82 : : * is needed for identifying the account.
83 : : */
84 : : /* ------------------------------------------------------------------ */
85 : :
86 : :
87 : : static int gen_logs = 1;
88 : : static FILE * trans_log = nullptr; /**< current log file handle */
89 : : static char * trans_log_name = nullptr; /**< current log file name */
90 : : static char * log_base_name = nullptr;
91 : :
92 : : /********************************************************************\
93 : : \********************************************************************/
94 : :
95 : 142 : void xaccLogDisable (void)
96 : : {
97 : 142 : gen_logs = 0;
98 : 142 : }
99 : 129 : void xaccLogEnable (void)
100 : : {
101 : 129 : gen_logs = 1;
102 : 129 : }
103 : :
104 : : /********************************************************************\
105 : : \********************************************************************/
106 : :
107 : : void
108 : 0 : xaccReopenLog (void)
109 : : {
110 : 0 : if (trans_log)
111 : : {
112 : 0 : xaccCloseLog();
113 : 0 : xaccOpenLog();
114 : : }
115 : 0 : }
116 : :
117 : :
118 : : void
119 : 46 : xaccLogSetBaseName (const char *basepath)
120 : : {
121 : 46 : if (!basepath) return;
122 : :
123 : 37 : g_free (log_base_name);
124 : 37 : log_base_name = g_strdup (basepath);
125 : :
126 : 37 : if (trans_log)
127 : : {
128 : 10 : xaccCloseLog();
129 : 10 : xaccOpenLog();
130 : : }
131 : : }
132 : :
133 : :
134 : : /*
135 : : * See if the provided file name is that of the current log file.
136 : : * Since the filename is generated with a time-stamp we can ignore the
137 : : * directory path and avoid problems with worrying about any ".."
138 : : * components in the path.
139 : : */
140 : : gboolean
141 : 0 : xaccFileIsCurrentLog (const gchar *name)
142 : : {
143 : : gchar *base;
144 : : gint result;
145 : :
146 : 0 : if (!name || !trans_log_name)
147 : 0 : return FALSE;
148 : :
149 : 0 : base = g_path_get_basename(name);
150 : 0 : result = (strcmp(base, trans_log_name) == 0);
151 : 0 : g_free(base);
152 : 0 : return result;
153 : : }
154 : :
155 : : /********************************************************************\
156 : : \********************************************************************/
157 : :
158 : : void
159 : 4750 : xaccOpenLog (void)
160 : : {
161 : : char * filename;
162 : : char * timestamp;
163 : :
164 : 4750 : if (!gen_logs)
165 : : {
166 : 2435 : PINFO ("Attempt to open disabled transaction log");
167 : 2435 : return;
168 : : }
169 : 2315 : if (trans_log) return;
170 : :
171 : 75 : if (!log_base_name) log_base_name = g_strdup ("translog");
172 : :
173 : : /* tag each filename with a timestamp */
174 : 43 : timestamp = gnc_date_timestamp ();
175 : :
176 : 43 : filename = g_strconcat (log_base_name, ".", timestamp, ".log", nullptr);
177 : :
178 : 43 : trans_log = g_fopen (filename, "a");
179 : 43 : if (!trans_log)
180 : : {
181 : 0 : int norr = errno;
182 : 0 : printf ("Error: xaccOpenLog(): cannot open journal\n"
183 : 0 : "\t %d %s\n", norr, g_strerror (norr) ? g_strerror (norr) : "");
184 : :
185 : 0 : g_free (filename);
186 : 0 : g_free (timestamp);
187 : 0 : return;
188 : : }
189 : :
190 : : /* Save the log file name */
191 : 43 : if (trans_log_name)
192 : 10 : g_free (trans_log_name);
193 : 43 : trans_log_name = g_path_get_basename(filename);
194 : :
195 : 43 : g_free (filename);
196 : 43 : g_free (timestamp);
197 : :
198 : : /* Note: this must match src/import-export/log-replay/gnc-log-replay.c */
199 : 43 : fprintf (trans_log, "mod\ttrans_guid\tsplit_guid\ttime_now\t"
200 : : "date_entered\tdate_posted\t"
201 : : "acc_guid\tacc_name\tnum\tdescription\t"
202 : : "notes\tmemo\taction\treconciled\t"
203 : : "amount\tvalue\tdate_reconciled\n");
204 : 43 : fprintf (trans_log, "-----------------\n");
205 : : }
206 : :
207 : : /********************************************************************\
208 : : \********************************************************************/
209 : :
210 : : void
211 : 10 : xaccCloseLog (void)
212 : : {
213 : 10 : if (!trans_log) return;
214 : 10 : fflush (trans_log);
215 : 10 : fclose (trans_log);
216 : 10 : trans_log = nullptr;
217 : : }
218 : :
219 : : /********************************************************************\
220 : : \********************************************************************/
221 : :
222 : : void
223 : 9376 : xaccTransWriteLog (Transaction *trans, char flag)
224 : : {
225 : : GList *node;
226 : : char trans_guid_str[GUID_ENCODING_LENGTH + 1];
227 : : char split_guid_str[GUID_ENCODING_LENGTH + 1];
228 : : const char *trans_notes;
229 : : char dnow[100], dent[100], dpost[100], drecn[100];
230 : :
231 : 9376 : if (!gen_logs)
232 : : {
233 : 4766 : PINFO ("Attempt to write disabled transaction log");
234 : 4766 : return;
235 : : }
236 : 4610 : if (!trans_log) return;
237 : :
238 : 4610 : gnc_time64_to_iso8601_buff (gnc_time(nullptr), dnow);
239 : 4610 : gnc_time64_to_iso8601_buff (trans->date_entered, dent);
240 : 4610 : gnc_time64_to_iso8601_buff (trans->date_posted, dpost);
241 : 4610 : guid_to_string_buff (xaccTransGetGUID(trans), trans_guid_str);
242 : 4610 : trans_notes = xaccTransGetNotes(trans);
243 : 4610 : fprintf (trans_log, "===== START\n");
244 : :
245 : 9700 : for (node = trans->splits; node; node = node->next)
246 : : {
247 : 5090 : Split *split = GNC_SPLIT(node->data);
248 : 5090 : const char * accname = "";
249 : : char acc_guid_str[GUID_ENCODING_LENGTH + 1];
250 : : gnc_numeric amt, val;
251 : :
252 : 5090 : if (xaccSplitGetAccount(split))
253 : : {
254 : 5059 : accname = xaccAccountGetName (xaccSplitGetAccount(split));
255 : 5059 : guid_to_string_buff(xaccAccountGetGUID(xaccSplitGetAccount(split)),
256 : : acc_guid_str);
257 : : }
258 : : else
259 : : {
260 : 31 : acc_guid_str[0] = '\0';
261 : : }
262 : :
263 : 5090 : gnc_time64_to_iso8601_buff (split->date_reconciled, drecn);
264 : :
265 : 5090 : guid_to_string_buff (xaccSplitGetGUID(split), split_guid_str);
266 : 5090 : amt = xaccSplitGetAmount (split);
267 : 5090 : val = xaccSplitGetValue (split);
268 : :
269 : : /* use tab-separated fields */
270 : 25450 : fprintf (trans_log,
271 : : "%c\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t"
272 : : "%s\t%s\t%s\t%s\t%c\t%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT "\t%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT "\t%s\n",
273 : : flag,
274 : : trans_guid_str, split_guid_str, /* trans+split make up unique id */
275 : : /* Note that the next three strings always exist,
276 : : * so we don't need to test them. */
277 : : dnow,
278 : : dent,
279 : : dpost,
280 : : acc_guid_str,
281 : : accname ? accname : "",
282 : 5090 : trans->num ? trans->num : "",
283 : 5090 : trans->description ? trans->description : "",
284 : : trans_notes ? trans_notes : "",
285 : 5090 : split->memo ? split->memo : "",
286 : 5090 : split->action ? split->action : "",
287 : 5090 : split->reconciled,
288 : : gnc_numeric_num(amt),
289 : : gnc_numeric_denom(amt),
290 : : gnc_numeric_num(val),
291 : : gnc_numeric_denom(val),
292 : : /* The next string always exists. No need to test it. */
293 : : drecn);
294 : : }
295 : :
296 : 4610 : fprintf (trans_log, "===== END\n");
297 : :
298 : : /* get data out to the disk */
299 : 4610 : fflush (trans_log);
300 : : }
301 : :
302 : : /************************ END OF ************************************\
303 : : \************************* FILE *************************************/
|