Integrated the DKIM signer into serv_smtpclient, but disabled it
[citadel.git] / libcitadel / lib / json.c
1 //
2 // JSON data type and serializer for Citadel
3 //
4 // Copyright (c) 1987-2023 by the citadel.org team
5 //
6 // This program is open source software.  Use, duplication, or disclosure
7 // is subject to the terms of the GNU General Public License, version 3.
8
9 #include "sysdep.h"
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #include <unistd.h>
13 #include <dirent.h>
14 #include <errno.h>
15 #include <stdio.h>
16 #include <stdarg.h>
17 #include <string.h>
18 #include "libcitadel.h"
19
20 #define JSON_STRING 0
21 #define JSON_NUM 1
22 #define JSON_NULL 2
23 #define JSON_BOOL 3
24 #define JSON_ARRAY 4
25 #define JSON_OBJECT 7
26
27 struct JsonValue {
28         int Type;
29         StrBuf *Name;
30         StrBuf *Value;
31         HashList *SubValues;
32 };
33
34
35 void DeleteJSONValue(void *vJsonValue) {
36         JsonValue *Val = (JsonValue*) vJsonValue;
37         FreeStrBuf(&Val->Name);
38         FreeStrBuf(&Val->Value);
39         DeleteHash(&Val->SubValues);
40         free(Val);
41 }
42
43
44 JsonValue *NewJsonObject(const char *Key, long keylen) {
45         JsonValue *Ret;
46
47         Ret = (JsonValue*) malloc(sizeof(JsonValue));
48         memset(Ret, 0, sizeof(JsonValue));
49         Ret->Type = JSON_OBJECT;
50         if (Key != NULL) {
51                 Ret->Name = NewStrBufPlain(Key, keylen);
52         }
53         Ret->SubValues = NewHash(1, NULL);
54         return Ret;
55 }
56
57
58 JsonValue *NewJsonArray(const char *Key, long keylen) {
59         JsonValue *Ret;
60
61         Ret = (JsonValue*) malloc(sizeof(JsonValue));
62         memset(Ret, 0, sizeof(JsonValue));
63         Ret->Type = JSON_ARRAY;
64         if (Key != NULL) {
65                 Ret->Name = NewStrBufPlain(Key, keylen);
66         }
67         Ret->SubValues = NewHash(1, lFlathash);
68         return Ret;
69 }
70
71
72 JsonValue *NewJsonNumber(const char *Key, long keylen, long Number) {
73         JsonValue *Ret;
74
75         Ret = (JsonValue*) malloc(sizeof(JsonValue));
76         memset(Ret, 0, sizeof(JsonValue));
77         Ret->Type = JSON_NUM;
78         if (Key != NULL) {
79                 Ret->Name = NewStrBufPlain(Key, keylen);
80         }
81         Ret->Value = NewStrBufPlain(NULL, 64);
82         StrBufPrintf(Ret->Value, "%ld", Number);
83         return Ret;
84 }
85
86
87 JsonValue *NewJsonBigNumber(const char *Key, long keylen, double Number) {
88         JsonValue *Ret;
89
90         Ret = (JsonValue*) malloc(sizeof(JsonValue));
91         memset(Ret, 0, sizeof(JsonValue));
92         Ret->Type = JSON_NUM;
93         if (Key != NULL) {
94                 Ret->Name = NewStrBufPlain(Key, keylen);
95         }
96         Ret->Value = NewStrBufPlain(NULL, 128);
97         StrBufPrintf(Ret->Value, "%f", Number);
98         return Ret;
99 }
100
101
102 JsonValue *NewJsonString(const char *Key, long keylen, StrBuf *CopyMe, int copy_or_smash) {
103         JsonValue *Ret;
104
105         Ret = (JsonValue*) malloc(sizeof(JsonValue));
106         memset(Ret, 0, sizeof(JsonValue));
107         Ret->Type = JSON_STRING;
108         if (Key != NULL) {
109                 Ret->Name = NewStrBufPlain(Key, keylen);
110         }
111         if (copy_or_smash == NEWJSONSTRING_COPYBUF) {
112                 Ret->Value = NewStrBufDup(CopyMe);
113         }
114         else if (copy_or_smash == NEWJSONSTRING_SMASHBUF) {
115                 Ret->Value = CopyMe;
116         }
117         else {
118                 Ret->Value = NULL;              // error condition
119         }
120         return Ret;
121 }
122
123
124 JsonValue *NewJsonPlainString(const char *Key, long keylen, const char *CopyMe, long len) {
125         JsonValue *Ret;
126
127         Ret = (JsonValue*) malloc(sizeof(JsonValue));
128         memset(Ret, 0, sizeof(JsonValue));
129         Ret->Type = JSON_STRING;
130         if (Key != NULL) {
131                 Ret->Name = NewStrBufPlain(Key, keylen);
132         }
133         Ret->Value = NewStrBufPlain(CopyMe, len);
134         return Ret;
135 }
136
137
138 JsonValue *NewJsonNull(const char *Key, long keylen) {
139         JsonValue *Ret;
140
141         Ret = (JsonValue*) malloc(sizeof(JsonValue));
142         memset(Ret, 0, sizeof(JsonValue));
143         Ret->Type = JSON_NULL;
144         if (Key != NULL) {
145                 Ret->Name = NewStrBufPlain(Key, keylen);
146         }
147         Ret->Value = NewStrBufPlain(HKEY("nulll"));
148         return Ret;
149 }
150
151
152 JsonValue *NewJsonBool(const char *Key, long keylen, int value) {
153         JsonValue *Ret;
154
155         Ret = (JsonValue*) malloc(sizeof(JsonValue));
156         memset(Ret, 0, sizeof(JsonValue));
157         Ret->Type = JSON_BOOL;
158         if (Key != NULL) {
159                 Ret->Name = NewStrBufPlain(Key, keylen);
160         }
161         if (value) {
162                 Ret->Value = NewStrBufPlain(HKEY("true"));
163         }
164         else {
165                 Ret->Value = NewStrBufPlain(HKEY("false"));
166         }
167         return Ret;
168 }
169
170
171 void JsonArrayAppend(JsonValue *Array, JsonValue *Val) {
172         long n;
173         if (Array->Type != JSON_ARRAY) {
174                 return;
175         }
176
177         n = GetCount(Array->SubValues);
178         Put(Array->SubValues, LKEY(n), Val, DeleteJSONValue);
179 }
180
181
182 void JsonObjectAppend(JsonValue *Array, JsonValue *Val) {
183         if ((Array->Type != JSON_OBJECT) || (Val->Name == NULL)) {
184                 return;
185         }
186         Put(Array->SubValues, SKEY(Val->Name), Val, DeleteJSONValue);
187 }
188
189
190 void SerializeJson(StrBuf *Target, JsonValue *Val, int FreeVal) {
191         void *vValue, *vPrevious;
192         JsonValue *SubVal;
193         HashPos *It;
194         const char *Key;
195         long keylen;
196
197         switch (Val->Type) {
198         case JSON_STRING:
199                 StrBufAppendBufPlain(Target, HKEY("\""), 0);
200                 int i;
201                 char *plain = (char *)ChrPtr(Val->Value);
202                 if (!IsEmptyStr(plain)) {
203                         int len = strlen(plain);
204                         for (i=0; i<len; ++i) {
205                                 // JSON escaped strings as per RFC 7159 section 7
206                                 if (plain[i] == '\r') {
207                                         StrBufAppendBufPlain(Target, HKEY("\\r"), 0);
208                                 }
209                                 else if (plain[i] == '\n') {
210                                         StrBufAppendBufPlain(Target, HKEY("\\n"), 0);
211                                 }
212                                 else if (plain[i] == '\"') {
213                                         StrBufAppendBufPlain(Target, HKEY("\\\""), 0);
214                                 }
215                                 else if (plain[i] == '\\') {
216                                         StrBufAppendBufPlain(Target, HKEY("\\\\"), 0);
217                                 }
218                                 else if (plain[i] < 32) {
219                                         StrBufAppendPrintf(Target, "\\u%04x", plain[i]);
220                                 }
221                                 else {
222                                         StrBufAppendBufPlain(Target, &plain[i], 1, 0);
223                                 }
224                         }
225                 }
226                 StrBufAppendBufPlain(Target, HKEY("\""), 0);
227                 break;
228         case JSON_NUM:
229                 StrBufAppendBuf(Target, Val->Value, 0);
230                 break;
231         case JSON_BOOL:
232                 StrBufAppendBuf(Target, Val->Value, 0);
233                 break;
234         case JSON_NULL:
235                 StrBufAppendBuf(Target, Val->Value, 0);
236                 break;
237         case JSON_ARRAY:
238                 vPrevious = NULL;
239                 StrBufAppendBufPlain(Target, HKEY("["), 0);
240                 It = GetNewHashPos(Val->SubValues, 0);
241                 while (GetNextHashPos(Val->SubValues, It, &keylen, &Key, &vValue)) {
242                         if (vPrevious != NULL) {
243                                 StrBufAppendBufPlain(Target, HKEY(","), 0);
244                         }
245                         SubVal = (JsonValue*) vValue;
246                         SerializeJson(Target, SubVal, 0);
247                         vPrevious = vValue;
248                 }
249                 StrBufAppendBufPlain(Target, HKEY("]"), 0);
250                 DeleteHashPos(&It);
251                 break;
252         case JSON_OBJECT:
253                 vPrevious = NULL;
254                 StrBufAppendBufPlain(Target, HKEY("{"), 0);
255                 It = GetNewHashPos(Val->SubValues, 0);
256                 while (GetNextHashPos(Val->SubValues, It, &keylen, &Key, &vValue)) {
257                         SubVal = (JsonValue*) vValue;
258
259                         if (vPrevious != NULL) {
260                                 StrBufAppendBufPlain(Target, HKEY(","), 0);
261                         }
262                         StrBufAppendBufPlain(Target, HKEY("\""), 0);
263                         StrBufAppendBuf(Target, SubVal->Name, 0);
264                         StrBufAppendBufPlain(Target, HKEY("\":"), 0);
265
266                         SerializeJson(Target, SubVal, 0);
267                         vPrevious = vValue;
268                 }
269                 StrBufAppendBufPlain(Target, HKEY("}"), 0);
270                 DeleteHashPos(&It);
271                 break;
272         }
273         if (FreeVal) {
274                 DeleteJSONValue(Val);
275         }
276 }