added custom error messages (fixed existing implementation)
[iauth.git] / iauth_io.c
1 /*
2  * Written by David Herrmann.
3  * Dedicated to the Public Domain.
4  */
5
6 #include <stdlib.h>
7 #include <stdio.h>
8 #include <stdarg.h>
9 #include <string.h>
10 #include <errno.h>
11 #include <time.h>
12
13 #include <unistd.h>
14
15 #include "iauth.h"
16
17 /* /path/to/logfile.log or NULL. */
18 const char *iauth_logfile = NULL;
19 /* Current logfile or NULL. */
20 FILE *logfile = NULL;
21 /* 1 if debug mode is enabled, otherwise 0. */
22 unsigned int iauth_debug = 0;
23
24 static void iauth_error(const char *msg, size_t len) {
25     fwrite(msg, 1, len, stderr);
26     fwrite("\n", 1, 1, stderr);
27     /*abort();*/
28     exit(1);
29 }
30
31 void iauth_ferror(const char *format, ...) {
32     va_list arg;
33
34     va_start(arg, format);
35     iauth_verror(format, arg);
36     va_end(arg);
37 }
38
39 void iauth_verror(const char *format, va_list list) {
40     char buffer[IAUTH_LINE + 1];
41     size_t len;
42
43     memset(buffer, 0, IAUTH_LINE + 1);
44     len = vsnprintf(buffer, IAUTH_LINE, format, list);
45     if(!len) len = snprintf(buffer, IAUTH_LINE, "<no error message specified>");
46     iauth_error(buffer, len);
47 }
48
49 /* Checks whether we need to reopen the logfile.
50  * Aborts the application if the logfile cannot be
51  * opened.
52  */
53 static void iauth_log_reopen() {
54     if(iauth_logfile) {
55         if(logfile) fclose(logfile);
56         logfile = fopen(iauth_logfile, "a");
57         if(!logfile) iauth_ferror("Logfile '%s' could not be opened in mode 'a'; errno = '%d'.", iauth_logfile, errno);
58         iauth_logfile = NULL;
59     }
60     else if(!logfile) iauth_ferror("No logfile specified.");
61 }
62
63 static void iauth_log(unsigned int type, const char *msg, size_t len) {
64     struct tm *tstamp;
65     time_t curtime;
66     char buffer[IAUTH_LINE + 1];
67     size_t len2;
68
69     iauth_log_reopen();
70
71     curtime = time(NULL);
72     tstamp = localtime(&curtime);
73     memset(buffer, 0, IAUTH_LINE + 1);
74     len2 = snprintf(buffer, IAUTH_LINE + 1, "[%02d.%02d.%d %02d:%02d:%02d]", tstamp->tm_mday, tstamp->tm_mon + 1, tstamp->tm_year + 1900, 
75                                                                              tstamp->tm_hour, tstamp->tm_min, tstamp->tm_sec);
76     len2 += snprintf(&buffer[len2], IAUTH_LINE + 1 - len2, " (%s): ", (type == IAUTH_FATAL)?"FATAL":(type == IAUTH_WARNING)?"WARNING":
77                                                                       (type == IAUTH_INFO)?"INFO":(type == IAUTH_DEBUG)?"DEBUG":"UNKNOWN");
78
79     if(fwrite(buffer, 1, len2, logfile) != len2) iauth_ferror("Write operation on logfile failed; errno = '%d'.", errno);
80     if(fwrite(msg, 1, len, logfile) != len) iauth_ferror("Write operation on logfile failed; errno = '%d'.", errno);
81     fwrite("\n", 1, 1, logfile);
82     fflush(logfile);
83 }
84
85 void iauth_flog(unsigned int type, const char *format, ...) {
86     va_list arg;
87
88     va_start(arg, format);
89     iauth_vlog(type, format, arg);
90     va_end(arg);
91 }
92
93 void iauth_vlog(unsigned int type, const char *format, va_list list) {
94     char buffer[IAUTH_LINE + 1];
95     size_t len;
96
97     memset(buffer, 0, IAUTH_LINE + 1);
98     len = vsnprintf(buffer, IAUTH_LINE, format, list);
99     if(!len) len = snprintf(buffer, IAUTH_LINE, "<no message specified>");
100     iauth_log(type, buffer, len);
101 }
102
103 void iauth_eflog(const char *format, ...) {
104     va_list arg;
105
106     va_start(arg, format);
107     iauth_evlog(format, arg);
108     va_end(arg);
109 }
110
111 void iauth_evlog(const char *format, va_list list) {
112     char buffer[IAUTH_LINE + 1];
113     size_t len;
114
115     memset(buffer, 0, IAUTH_LINE + 1);
116     len = vsnprintf(buffer, IAUTH_LINE, format, list);
117     if(!len) len = snprintf(buffer, IAUTH_LINE, "<no message specified>");
118     iauth_log(IAUTH_FATAL, buffer, len);
119     iauth_error(buffer, len);
120 }
121
122 char *iauth_read() {
123     static char buffer[IAUTH_LINE + 1];
124     unsigned int i, ignore = 0;
125
126     next_line:
127
128     if(!fgets(buffer, IAUTH_LINE, stdin)) {
129         if(feof(stdin)) return NULL;
130         iauth_eflog("Reading on stdin failed; errno = '%d'.", errno);
131     }
132     buffer[IAUTH_LINE] = 0;
133
134     for(i = 0; i < IAUTH_LINE; ++i) {
135         if(buffer[i] == '\n') {
136             if(ignore) {
137                 ignore = 0;
138                 goto next_line;
139             }
140             if(i == 0) goto next_line;
141             if(buffer[i - 1] == '\r') {
142                 if(i == 1) goto next_line;
143                 buffer[i - 1] = 0;
144             }
145             else buffer[i] = 0;
146             if(iauth_debug) {
147                 iauth_flog(IAUTH_DEBUG, "IN -> %s", buffer);
148             }
149             return buffer;
150         }
151     }
152
153     /* Too long message. Discard it! */
154     iauth_flog(IAUTH_WARNING, "Message exceeded maximum length of '%d' bytes.", IAUTH_LINE);
155     ignore = 1;
156     goto next_line;
157 }
158
159 void iauth_fsend(const char *format, ...) {
160     va_list arg;
161
162     va_start(arg, format);
163     iauth_vsend(format, arg);
164     va_end(arg);
165 }
166
167 void iauth_vsend(const char *format, va_list list) {
168     char buffer[IAUTH_LINE + 1];
169     size_t len, len2;
170
171     memset(buffer, 0, IAUTH_LINE + 1);
172     if(iauth_debug) {
173         len2 = snprintf(buffer, IAUTH_LINE, "OUT <- ");
174         len = vsnprintf(&buffer[len2], IAUTH_LINE, format, list);
175         if(!len) return;
176         iauth_log(IAUTH_DEBUG, buffer, len + len2);
177         if(write(0, &buffer[len2], len) != len) iauth_eflog("IAuth write operation failed; errno = '%d'", errno);
178         write(0, "\r\n", 2);
179     }
180     else {
181         len = vsnprintf(buffer, IAUTH_LINE, format, list);
182         if(!len) return;
183         if(write(0, buffer, len) != len) iauth_eflog("IAuth write operation failed; errno = '%d'", errno);
184         write(0, "\r\n", 2);
185     }
186 }