1 /* Gammu logging/debugging functions */
2 /* Copyright (c) 2008-2009 by Michal Cihar <michal@cihar.com> */
3 /* Licensed under GPL2+ */
4 
5 #include "debug.h"
6 
7 #include <string.h>
8 #include <ctype.h>
9 
10 /* Commit flag for opening files is MS extension, some other
11  * implementations (like BCC 5.5) don't like this flag at all */
12 #ifdef _MSC_VER
13 #  define COMMIT_FLAG "c"
14 #else
15 #  define COMMIT_FLAG ""
16 #endif
17 
18 #if defined _WIN64 || defined _WIN32
19 #define strcasecmp _stricmp
20 #endif
21 
22 GSM_Debug_Info GSM_none_debug = {
23 	0,
24 	NULL,
25 	FALSE,
26 	"",
27 	FALSE,
28 	FALSE,
29 	NULL,
30 	NULL
31 	};
32 
33 GSM_Debug_Info GSM_global_debug = {
34 	0,
35 	NULL,
36 	FALSE,
37 	"",
38 	FALSE,
39 	FALSE,
40 	NULL,
41 	NULL
42 	};
43 
44 /**
45  * Actually writes message to debuging file.
46  */
dbg_write(GSM_Debug_Info * d,const char * text)47 void dbg_write(GSM_Debug_Info *d, const char *text)
48 {
49 	if (d->log_function != NULL) {
50 		d->log_function(text, d->user_data);
51 	} else if (d->df != NULL) {
52 		fprintf(d->df, "%s", text);
53 	}
54 }
55 
56 PRINTF_STYLE(2, 0)
dbg_vprintf(GSM_Debug_Info * d,const char * format,va_list argp)57 int dbg_vprintf(GSM_Debug_Info *d, const char *format, va_list argp)
58 {
59 	int 			result=0;
60 	char			buffer[3000], timestamp[60];
61 	char			*pos, *end;
62 	char			save = 0;
63 	GSM_DateTime 		date_time;
64 	Debug_Level		l;
65 
66 	l = d->dl;
67 
68 	if (l == DL_NONE) return 0;
69 
70 	result = vsnprintf(buffer, sizeof(buffer) - 1, format, argp);
71 	pos = buffer;
72 
73 	while (*pos != 0) {
74 
75 		/* Find new line in string */
76 		end = strstr(pos, "\n");
77 
78 		/* Are we at start of line? */
79 		if (d->was_lf) {
80 			/* Show date? */
81 			if (l == DL_TEXTALLDATE || l == DL_TEXTERRORDATE || l == DL_TEXTDATE) {
82 				GSM_GetCurrentDateTime(&date_time);
83 				sprintf(timestamp, "%s %4d/%02d/%02d %02d:%02d:%02d: ",
84 					DayOfWeek(date_time.Year, date_time.Month, date_time.Day),
85 					date_time.Year, date_time.Month, date_time.Day,
86 					date_time.Hour, date_time.Minute, date_time.Second);
87 				dbg_write(d, timestamp);
88 			}
89 			d->was_lf = FALSE;
90 		}
91 
92 		/* Remember end char */
93 		if (end != NULL) {
94 			save = *end;
95 			*end = 0;
96 		}
97 
98 		/* Output */
99 		dbg_write(d, pos);
100 
101 		if (end != NULL) {
102 			/* We had new line */
103 			dbg_write(d, "\n");
104 			d->was_lf = TRUE;
105 
106 			/* Restore saved char */
107 			*end = save;
108 
109 			/* Advance to next line */
110 			pos = end + strlen("\n");
111 		} else {
112 			/* We hit end of string */
113 			break;
114 		}
115 	}
116 
117 	/* Flush buffers, this might be configurable, but it could cause drop of last log messages */
118 	if (d->df != NULL) {
119 		fflush(d->df);
120 	}
121 
122 	return result;
123 }
124 
GSM_SetDebugFileDescriptor(FILE * fd,gboolean closable,GSM_Debug_Info * privdi)125 GSM_Error GSM_SetDebugFileDescriptor(FILE *fd, gboolean closable, GSM_Debug_Info *privdi)
126 {
127 	privdi->was_lf = TRUE;
128 
129 	if (privdi->df != NULL
130 			&& fileno(privdi->df) != fileno(stderr)
131 			&& fileno(privdi->df) != fileno(stdout)
132 			&& privdi->closable) {
133 		fclose(privdi->df);
134 	}
135 
136 	privdi->df = fd;
137 	privdi->closable = closable;
138 
139 	return ERR_NONE;
140 }
141 
GSM_SetDebugFile(const char * info,GSM_Debug_Info * privdi)142 GSM_Error GSM_SetDebugFile(const char *info, GSM_Debug_Info *privdi)
143 {
144 	FILE *testfile;
145 
146 	if (info == NULL || strlen(info) == 0) {
147 		return GSM_SetDebugFileDescriptor(NULL, FALSE, privdi);
148 	}
149 
150 	switch (privdi->dl) {
151 		case DL_BINARY:
152 			testfile = fopen(info,"wb" COMMIT_FLAG);
153 			break;
154 		case DL_TEXTERROR:
155 		case DL_TEXTERRORDATE:
156 			testfile = fopen(info,"a" COMMIT_FLAG);
157 			if (testfile != NULL) {
158 				fseek(testfile, 0, SEEK_END);
159 				if (ftell(testfile) > 5000000) {
160 					fclose(testfile);
161 					testfile = fopen(info,"w" COMMIT_FLAG);
162 				}
163 			}
164 			break;
165 		default:
166 			testfile = fopen(info,"w" COMMIT_FLAG);
167 	}
168 
169 	if (testfile == NULL) {
170 		dbgprintf(privdi, "Can't open debug file\n");
171 		return ERR_CANTOPENFILE;
172 	} else {
173 		return GSM_SetDebugFileDescriptor(testfile, TRUE, privdi);
174 	}
175 }
176 
GSM_SetDebugFunction(GSM_Log_Function info,void * data,GSM_Debug_Info * privdi)177 GSM_Error GSM_SetDebugFunction(GSM_Log_Function info, void *data, GSM_Debug_Info * privdi)
178 {
179 	privdi->log_function = info;
180 	privdi->user_data = data;
181 	return ERR_NONE;
182 }
183 
GSM_SetDebugLevel(const char * info,GSM_Debug_Info * privdi)184 gboolean GSM_SetDebugLevel(const char *info, GSM_Debug_Info *privdi)
185 {
186 	if (info == NULL) {
187 		privdi->dl = DL_NONE;
188 		return TRUE;
189 	}
190 	if (strcasecmp(info, "nothing") == 0) {
191 		privdi->dl = DL_NONE;
192 		return TRUE;
193 	}
194 	if (strcasecmp(info, "text") == 0) {
195 		privdi->dl = DL_TEXT;
196 		return TRUE;
197 	}
198 	if (strcasecmp(info, "textall") == 0) {
199 		privdi->dl = DL_TEXTALL;
200 		return TRUE;
201 	}
202 	if (strcasecmp(info, "binary") == 0) {
203 		privdi->dl = DL_BINARY;
204 		return TRUE;
205 	}
206 	if (strcasecmp(info, "errors") == 0) {
207 		privdi->dl = DL_TEXTERROR;
208 		return TRUE;
209 	}
210 	if (strcasecmp(info, "textdate") == 0) {
211 		privdi->dl = DL_TEXTDATE;
212 		return TRUE;
213 	}
214 	if (strcasecmp(info, "textalldate") == 0) {
215 		privdi->dl = DL_TEXTALLDATE;
216 		return TRUE;
217 	}
218 	if (strcasecmp(info, "errorsdate") == 0) {
219 		privdi->dl = DL_TEXTERRORDATE;
220 		return TRUE;
221 	}
222 	return FALSE;
223 }
224 
GSM_SetDebugCoding(const char * info,GSM_Debug_Info * privdi)225 gboolean GSM_SetDebugCoding(const char *info, GSM_Debug_Info *privdi)
226 {
227 	privdi->coding = info;
228 	return TRUE;
229 }
230 
GSM_SetDebugGlobal(gboolean info,GSM_Debug_Info * privdi)231 gboolean GSM_SetDebugGlobal(gboolean info, GSM_Debug_Info *privdi)
232 {
233 	privdi->use_global = info;
234 	return TRUE;
235 }
236 
237 PRINTF_STYLE(2, 3)
smfprintf(GSM_Debug_Info * d,const char * format,...)238 int smfprintf(GSM_Debug_Info *d, const char *format, ...)
239 {
240 	va_list 		argp;
241 	int 			result;
242 	GSM_Debug_Info		*tmpdi;
243 
244 	if (d == NULL || d->use_global) {
245 		tmpdi = &GSM_global_debug;
246 	} else {
247 		tmpdi = d;
248 	}
249 
250 	va_start(argp, format);
251 	result = dbg_vprintf(tmpdi, format, argp);
252 	va_end(argp);
253 
254 	return result;
255 }
256 
257 
258 PRINTF_STYLE(2, 3)
smprintf(GSM_StateMachine * s,const char * format,...)259 int smprintf(GSM_StateMachine *s, const char *format, ...)
260 {
261 	va_list		argp;
262 	int 		result=0;
263 	GSM_Debug_Info *curdi;
264 
265 	curdi = GSM_GetDI(s);
266 
267 	va_start(argp, format);
268 
269 	result = dbg_vprintf(curdi, format, argp);
270 
271 	va_end(argp);
272 	return result;
273 }
274 
275 PRINTF_STYLE(3, 4)
smprintf_level(GSM_StateMachine * s,GSM_DebugSeverity severity,const char * format,...)276 int smprintf_level(GSM_StateMachine * s, GSM_DebugSeverity severity, const char *format, ...)
277 {
278 	va_list		argp;
279 	int 		result=0;
280 	GSM_Debug_Info *curdi;
281 
282 	curdi = GSM_GetDI(s);
283 
284 	if (severity == D_TEXT) {
285 		if (curdi->dl != DL_TEXT &&
286 				curdi->dl != DL_TEXTALL &&
287 				curdi->dl != DL_TEXTDATE &&
288 				curdi->dl != DL_TEXTALLDATE) {
289 			return 0;
290 		}
291 	} else if (severity == D_ERROR) {
292 		if (curdi->dl != DL_TEXT &&
293 				curdi->dl != DL_TEXTALL &&
294 				curdi->dl != DL_TEXTDATE &&
295 				curdi->dl != DL_TEXTALLDATE &&
296 				curdi->dl != DL_TEXTERROR &&
297 				curdi->dl != DL_TEXTERRORDATE) {
298 			return 0;
299 		}
300 	}
301 	va_start(argp, format);
302 
303 	result = dbg_vprintf(curdi, format, argp);
304 
305 	va_end(argp);
306 	return result;
307 }
308 
309 #define CHARS_PER_LINE (16)
310 
311 /* Dumps a message */
DumpMessage(GSM_Debug_Info * d,const unsigned char * message,const size_t messagesize)312 void DumpMessage(GSM_Debug_Info *d, const unsigned char *message, const size_t messagesize)
313 {
314 	size_t i, j = 0;
315 	char buffer[(CHARS_PER_LINE * 5) + 1];
316 
317 	smfprintf(d, "\n");
318 
319 	if (messagesize == 0) return;
320 
321 	memset(buffer, ' ', CHARS_PER_LINE * 5);
322 	buffer[CHARS_PER_LINE * 5] = 0;
323 
324 	for (i = 0; i < messagesize; i++) {
325 		/* Write hex number */
326 		snprintf(buffer + (j * 4), 3, "%02X", message[i]);
327 		buffer[(j * 4) + 2] = ' '; /* wipe snprintf's \0 */
328 
329 		/* Write char if possible */
330 		if (isprint(message[i])
331 				/* 0x09 = tab */
332 				&& message[i] != 0x09
333 				/* 0x01 = beep in windows xp */
334 				&& message[i] != 0x01
335 				/* these are empty in windows xp */
336 				&& message[i] != 0x85
337 				&& message[i] != 0x95
338 				&& message[i] != 0xA6
339 				&& message[i] != 0xB7) {
340 			buffer[(j * 4) + 2] = message[i];
341 			buffer[(CHARS_PER_LINE - 1) * 4 + j + 4] = message[i];
342 		} else {
343 			buffer[(CHARS_PER_LINE - 1) * 4 + j + 4] = '.';
344 		}
345 
346 		/* Write char separator */
347 		if (j != CHARS_PER_LINE - 1 && i != messagesize - 1) {
348 			buffer[j * 4 + 3] = '|';
349 		}
350 
351 		/* Print out buffer */
352 		if (j == CHARS_PER_LINE - 1) {
353 			smfprintf(d, "%s\n", buffer);
354 			memset(buffer, ' ', CHARS_PER_LINE * 5);
355 			j = 0;
356 		} else {
357 			j++;
358 		}
359 	}
360 
361 	/* Anything remains to be printed? */
362 	if (j != 0) {
363 		smfprintf(d, "%s\n", buffer);
364 	}
365 }
366 
367 #undef CHARS_PER_LINE
368 
DumpMessageText(GSM_Debug_Info * d,const unsigned char * message,const size_t messagesize)369 void DumpMessageText(GSM_Debug_Info *d, const unsigned char *message, const size_t messagesize)
370 {
371 	if (d == NULL || (d->dl != DL_TEXTALL && d->dl == DL_TEXTALLDATE)) return;
372 	DumpMessage(d, message, messagesize);
373 
374 }
375 /* How should editor hadle tabs in this file? Add editor commands here.
376  * vim: noexpandtab sw=8 ts=8 sts=8:
377  */
378