Integrated the DKIM signer into serv_smtpclient, but disabled it
[citadel.git] / libcitadel / lib / wildfire.c
1 // This doesn't belong in the build but WebCit-classic depends on it.
2 // When we move to WebCit-NG we will axe this.
3
4 #include "sysdep.h"
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #include <unistd.h>
8 #include <dirent.h>
9 #include <errno.h>
10 #include <stdio.h>
11 #include <stdarg.h>
12 #include <string.h>
13
14 #if HAVE_BACKTRACE
15 #include <execinfo.h>
16 #endif
17
18 #include "libcitadel.h"
19 #include "libcitadellocal.h"
20
21
22 ConstStr WF_MsgStrs[] = {
23         {HKEY("LOG")},
24         {HKEY("INFO")},
25         {HKEY("WARN")},
26         {HKEY("ERROR")},
27         {HKEY("TRACE")},
28         {HKEY("EXCEPTION")}
29 };
30
31 static JsonValue *WFInfo(const char *Filename, long fnlen, long LineNo, WF_MessageType Type) {
32         JsonValue *Val;
33
34         Val = NewJsonObject(NULL, 0);
35         JsonObjectAppend(Val, NewJsonPlainString(HKEY("Type"), WF_MsgStrs[Type].Key, WF_MsgStrs[Type].len));
36         JsonObjectAppend(Val, NewJsonPlainString(HKEY("File"), Filename, fnlen));
37         JsonObjectAppend(Val, NewJsonNumber(HKEY("Line"), LineNo));
38         return Val;
39 }
40                             
41
42 JsonValue *WildFireMessage(const char *Filename, long fnlen, long LineNo, StrBuf *Msg, WF_MessageType Type) {
43         JsonValue *Ret;
44
45         Ret = NewJsonArray(NULL, 0);
46         JsonArrayAppend(Ret, WFInfo(Filename, fnlen, LineNo, Type));
47
48         JsonArrayAppend(Ret, NewJsonString(NULL, 0, Msg, NEWJSONSTRING_COPYBUF));
49         return Ret;
50 }
51
52 JsonValue *WildFireMessagePlain(const char *Filename, long fnlen, long LineNo, const char *Message, long len, WF_MessageType Type) {
53         JsonValue *Val;
54         Val = NewJsonArray(NULL, 0);
55
56         JsonArrayAppend(Val, WFInfo(Filename, fnlen, LineNo, Type));
57         JsonArrayAppend(Val, NewJsonPlainString(NULL, 0, Message, len));
58         return Val;
59 }
60
61 void WildFireAddArray(JsonValue *ReportBase, JsonValue *Array, WF_MessageType Type) {
62         JsonValue *Val;
63         Val = NewJsonArray(NULL, 0);
64         JsonArrayAppend(Val, NewJsonPlainString(NULL, 0, WF_MsgStrs[Type].Key, WF_MsgStrs[Type].len));
65         JsonArrayAppend(Val, Array);
66 }
67
68 int addr2line_write_pipe[2];
69 int addr2line_read_pipe[2];
70 pid_t addr2line_pid;
71
72 #ifdef HAVE_BACKTRACE
73 /* 
74  * Start up the addr2line daemon so we can decode function pointers
75  */
76 static void start_addr2line_daemon(const char *binary) 
77 {
78         struct stat filestats;
79         int i;
80         const char *addr2line = "/usr/bin/addr2line";
81         const char minuse[] = "-e";
82
83         printf("Starting addr2line daemon for decoding of backtraces\n");
84
85         if ((stat(addr2line, &filestats)==-1) ||
86             (filestats.st_size==0)){
87                 printf("didn't find addr2line daemon in %s: %s\n", addr2line, strerror(errno));
88                 abort();
89         }
90         if (pipe(addr2line_write_pipe) != 0) {
91                 printf("Unable to create pipe for addr2line daemon: %s\n", strerror(errno));
92                 abort();
93         }
94         if (pipe(addr2line_read_pipe) != 0) {
95                 printf("Unable to create pipe for addr2line daemon: %s\n", strerror(errno));
96                 abort();
97         }
98
99         addr2line_pid = fork();
100         if (addr2line_pid < 0) {
101                 printf("Unable to fork addr2line daemon: %s\n", strerror(errno));
102                 abort();
103         }
104         if (addr2line_pid == 0) {
105                 dup2(addr2line_write_pipe[0], 0);
106                 dup2(addr2line_read_pipe[1], 1);
107                 for (i=2; i<256; ++i) close(i);
108                 execl(addr2line, addr2line, minuse, binary, NULL);
109                 printf("Unable to exec addr2line daemon: %s\n", strerror(errno));
110                 abort();
111                 exit(errno);
112         }
113 }
114
115 static int addr2lineBacktrace(StrBuf *Function, StrBuf *FileName, StrBuf *Pointer, StrBuf *Buf, unsigned int *FunctionLine) {
116         const char *err;
117         const char *pch, *pche;
118
119         write(addr2line_write_pipe[1], SKEY(Pointer));
120         if (StrBufTCP_read_line(Buf, &addr2line_read_pipe[0], 0, &err) <= 0) {
121                 StrBufAppendBufPlain(Buf, err, -1, 0);
122                 return 0;
123         }
124         pch = ChrPtr(Buf);
125         pche = strchr(pch, ':');
126         FlushStrBuf(FileName);
127         StrBufAppendBufPlain(FileName, pch, pche - pch, 0);
128         if (pche != NULL) {
129                 pche++;
130                 *FunctionLine = atoi(pche);
131         }
132         else 
133                 *FunctionLine = 0;
134         return 1;
135 }
136
137 static int ParseBacktrace(char *Line, StrBuf *Function, StrBuf *FileName, unsigned int *FunctionLine) {
138         char *pch, *pche;
139
140         pch = Line;
141         pche = strchr(pch, '(');
142         if (pche == NULL) return 0;
143         StrBufAppendBufPlain(FileName, pch, pche - pch, 0);
144         pch = pche + 1;
145         pche = strchr(pch, '+');
146         if (pche == NULL) return 0;
147         StrBufAppendBufPlain(Function, pch, pche - pch, 0);
148         pch = pche + 1;
149         pche = strchr(pch, ')');
150         if (pche == NULL) return 0;
151         *pche = '\0';
152         sscanf(pch, "%x", FunctionLine);
153         StrBufAppendBufPlain(Function, pche + 1, -1, 0);
154         return 1;
155 }
156 #endif
157 long BaseFrames = 0;
158 StrBuf *FullBinaryName = NULL;
159
160 void WildFireShutdown(void) {
161         close(addr2line_write_pipe[0]);
162         close(addr2line_read_pipe[0]);
163
164         FreeStrBuf(&FullBinaryName);
165 }
166
167 void WildFireInitBacktrace(const char *argvNull, int AddBaseFrameSkip) {
168
169 #ifdef HAVE_BACKTRACE
170         void *stack_frames[100];
171         size_t size;
172         long i;
173         char **strings;
174         StrBuf *FileName;
175         StrBuf *Function;
176         StrBuf *Pointer;
177         StrBuf *Buf;
178         unsigned int FunctionLine;
179         struct stat filestats;
180
181         FileName = NewStrBuf();
182         Function = NewStrBuf();
183         Pointer = NewStrBuf();
184         Buf = NewStrBuf();
185
186         BaseFrames = size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
187         BaseFrames --;
188         BaseFrames += AddBaseFrameSkip;
189         strings = backtrace_symbols(stack_frames, size);
190         for (i = 1; i < size; i++) {
191                 if (strings != NULL){
192                         ParseBacktrace(strings[i], Function, FileName, &FunctionLine);
193                         FullBinaryName = NewStrBufDup(FileName);
194                         size = i;
195                 }
196                 else {
197                         char path[256];
198                         getcwd(path, sizeof(path));
199                         FullBinaryName = NewStrBufPlain(path, -1);
200                         StrBufAppendBufPlain(FullBinaryName, HKEY("/"), 0);
201                         StrBufAppendBufPlain(FullBinaryName, argvNull, -1, 0);
202                         i = size;
203                  }
204         }
205         if ((stat(ChrPtr(FullBinaryName), &filestats)==-1) || (filestats.st_size==0)) {
206                 FlushStrBuf(FullBinaryName);
207                 StrBufAppendBufPlain(FullBinaryName, argvNull, -1, 0);
208                 if ((stat(ChrPtr(FullBinaryName), &filestats)==-1) || (filestats.st_size==0)) {
209                         FlushStrBuf(FullBinaryName);
210                         fprintf(stderr, "unable to open my binary for addr2line checking, verbose backtraces won't work.\n");
211                 }
212         }
213         free(strings);
214         FreeStrBuf(&FileName);
215         FreeStrBuf(&Function);
216         FreeStrBuf(&Pointer);
217         FreeStrBuf(&Buf);
218         if (StrLength(FullBinaryName) > 0)
219                 start_addr2line_daemon(ChrPtr(FullBinaryName));
220 #endif
221 }
222
223
224 JsonValue *WildFireException(const char *Filename, long FileLen, long LineNo, StrBuf *Message, int StackOffset) {
225         JsonValue *ExcClass;
226         JsonValue *Val;
227         Val = NewJsonArray(NULL, 0);
228
229         JsonArrayAppend(Val, WFInfo(Filename, FileLen, LineNo, eEXCEPTION));
230         ExcClass = NewJsonObject(WF_MsgStrs[eTRACE].Key, WF_MsgStrs[eTRACE].len);
231         JsonArrayAppend(Val, ExcClass);
232         JsonObjectAppend(ExcClass, NewJsonPlainString(HKEY("Class"), HKEY("Exception")));
233         JsonObjectAppend(ExcClass, NewJsonString(HKEY("Message"), Message, NEWJSONSTRING_COPYBUF));
234         JsonObjectAppend(ExcClass, NewJsonPlainString(HKEY("File"), Filename, FileLen));
235 /*
236         JsonObjectAppend(ExcClass, NewJsonPlainString(HKEY("Type"), HKEY("throw")));
237 */
238         JsonObjectAppend(ExcClass, NewJsonNumber(HKEY("Line"), LineNo));
239
240 #ifdef HAVE_BACKTRACE
241         {
242                 void *stack_frames[100];
243                 size_t size;
244                 long i;
245                 char **strings;
246                 JsonValue *Trace;
247                 JsonValue *Frame;
248                 StrBuf *FileName;
249                 StrBuf *Function;
250                 StrBuf *Pointer;
251                 StrBuf *Buf;
252                 unsigned int FunctionLine;
253
254                 Trace = NewJsonArray(HKEY("Trace"));
255                 JsonObjectAppend(ExcClass, Trace);
256                 FileName = NewStrBuf();
257                 Function = NewStrBuf();
258                 Pointer = NewStrBuf();
259                 Buf = NewStrBuf();
260
261                 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
262                 strings = backtrace_symbols(stack_frames, size);
263                 for (i = StackOffset + 1; i < size; i++) {
264                         if (strings != NULL){
265                                 ParseBacktrace(strings[i], Function, FileName, &FunctionLine);
266                         }
267                         StrBufPrintf(Pointer, "%p\n", stack_frames[i]);
268                         addr2lineBacktrace(Function, FileName, Pointer, Buf, &FunctionLine);
269                         Frame = NewJsonObject(NULL, 0);
270                         JsonArrayAppend(Trace, Frame);
271                         JsonObjectAppend(Frame, NewJsonString(HKEY("function"), Function, NEWJSONSTRING_COPYBUF));
272                         JsonObjectAppend(Frame, NewJsonString(HKEY("file"), FileName, NEWJSONSTRING_COPYBUF));
273                         JsonObjectAppend(Frame, NewJsonNumber(HKEY("line"), FunctionLine));
274                         JsonObjectAppend(Frame, NewJsonArray(HKEY("args"))); // not supported
275                         FunctionLine = 0;
276                         FlushStrBuf(FileName);
277                         FlushStrBuf(Function);
278                         FlushStrBuf(Pointer);
279                 }
280                 free(strings);
281                 FreeStrBuf(&FileName);
282                 FreeStrBuf(&Function);
283                 FreeStrBuf(&Pointer);
284                 FreeStrBuf(&Buf);
285         }
286 #endif
287         return Val;
288 }
289
290 void WildFireSerializePayload(StrBuf *JsonBuffer, StrBuf *OutBuf, int *MsgCount, AddHeaderFunc AddHdr) {
291         int n = *MsgCount;
292         StrBuf *Buf;
293         StrBuf *HeaderName;
294         StrBuf *N; 
295         const char Concatenate[] = "\\";
296         const char empty[] = "";
297         const char *Cat;
298         StrBuf *Header;
299
300         if (OutBuf == NULL)
301                 Header = NewStrBuf();
302         if (*MsgCount == 0) {
303                 if (OutBuf != NULL) {
304                         StrBufAppendBufPlain(OutBuf, 
305                                              HKEY( 
306                                                      "X-Wf-Protocol-1" 
307                                                      ": "
308                                                      "http://meta.wildfirehq.org/Protocol/JsonStream/0.2\r\n"), 0);
309                         StrBufAppendBufPlain(OutBuf, 
310                                              HKEY( 
311                                                      "X-Wf-1-Plugin-1" 
312                                                      ": " 
313                                                      "http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.2.0\r\n"), 0);
314                         StrBufAppendBufPlain(OutBuf, 
315                                              HKEY(
316                                                      "X-Wf-1-Structure-1"
317                                                      ": "
318                                                      "http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1\r\n"), 0);
319                 }
320                 else {
321                         AddHdr("X-Wf-Protocol-1", 
322                                "http://meta.wildfirehq.org/Protocol/JsonStream/0.2");
323                         AddHdr("X-Wf-1-Plugin-1",
324                                "http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.2.0");
325                         AddHdr("X-Wf-1-Structure-1",
326                                "http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1");
327                 }
328         }
329
330         N = NewStrBuf();
331         StrBufPrintf(N, "%d", StrLength(JsonBuffer));
332         Buf = NewStrBufPlain(NULL, 1024);
333         HeaderName = NewStrBuf();
334
335         while (StrLength(JsonBuffer) > 0) {
336                 FlushStrBuf(Buf);
337                 StrBufPrintf(HeaderName, "X-Wf-"WF_MAJOR"-"WF_STRUCTINDEX"-"WF_SUB"-%d", n);
338                 if (StrLength(JsonBuffer) > 800) {
339                         StrBufAppendBufPlain(Buf, ChrPtr(JsonBuffer), 800, 0);
340                         StrBufCutLeft(JsonBuffer, 800);
341                         Cat = Concatenate;
342                 }
343                 else {
344                         StrBufAppendBuf(Buf, JsonBuffer, 0);
345                         FlushStrBuf(JsonBuffer);
346                         Cat = empty;
347                 }
348                 if (OutBuf != NULL) {
349                         StrBufAppendPrintf(OutBuf, "%s: %s|%s|%s\r\n", ChrPtr(HeaderName), ChrPtr(N), ChrPtr(Buf), Cat);
350                 }
351                 else {
352                         StrBufAppendPrintf(Header, "%s|%s|%s", ChrPtr(N), ChrPtr(Buf), Cat);
353                         AddHdr(ChrPtr(HeaderName), ChrPtr(Header));
354                 }
355
356                 FlushStrBuf(N);
357                 n++;
358         }
359         *MsgCount = n;
360         if (OutBuf == NULL) {
361                 FreeStrBuf(&Header);
362         }
363         FreeStrBuf(&N);
364         FreeStrBuf(&Buf);
365         FreeStrBuf(&HeaderName);
366 }