614efd3d76c1389a47bfc1ced3110fe299e0d844
[citadel.git] / libcitadel / lib / vnote.c
1 /*
2  * vNote implementation for Citadel
3  *
4  * Copyright (C) 1999-2007 by the citadel.org development team.
5  * This code is freely redistributable under the terms of the GNU General
6  * Public License.  All other rights reserved.
7  */
8
9
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <stdio.h>
13 #include <fcntl.h>
14 #include <signal.h>
15
16 #if TIME_WITH_SYS_TIME
17 # include <sys/time.h>
18 # include <time.h>
19 #else
20 # if HAVE_SYS_TIME_H
21 #  include <sys/time.h>
22 # else
23 #  include <time.h>
24 # endif
25 #endif
26
27 #include <ctype.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <limits.h>
31 #include <string.h>
32 #include <libcitadel.h>
33
34
35 struct vnote *vnote_new(void) {
36         struct vnote *v;
37
38         v = (struct vnote *) malloc(sizeof(struct vnote));
39         if (v) {
40                 memset(v, 0, sizeof(struct vnote));
41                 v->magic = CTDL_VNOTE_MAGIC;
42                 v->pos_left = rand() % 256;
43                 v->pos_top = rand() % 256;
44                 v->pos_width = 200;
45                 v->pos_height = 150;
46                 v->color_red = 0xFF;
47                 v->color_green = 0xFF;
48                 v->color_blue = 0x00;
49         }
50         return v;
51 }
52
53 struct vnote *vnote_new_from_str(char *s) {
54         struct vnote *v;
55         char *ptr = s;
56         char *nexteol;
57         char *thisline;
58         int thisline_len;
59         char *encoded_value;
60         char *decoded_value;
61         int is_quoted_printable;
62         int is_base64;
63
64         v = vnote_new();
65         if (!v) return NULL;
66
67         while (*ptr) {          // keep going until we hit a null terminator
68                 thisline = NULL;
69                 nexteol = strchr(ptr, '\n');
70                 if (nexteol) {
71                         thisline = malloc((nexteol - ptr) + 2);
72                         strncpy(thisline, ptr, (nexteol-ptr));
73                         thisline_len = (nexteol-ptr);
74                         thisline[thisline_len] = 0;
75                         ptr = nexteol + 1;
76                 }
77                 else {
78                         thisline = strdup(ptr);
79                         thisline_len = strlen(thisline);
80                         ptr += thisline_len;
81                 }
82
83                 if (thisline) {
84                         if (thisline_len > 1) {
85                                 if (thisline[thisline_len - 1] == '\r') {
86                                         thisline[thisline_len - 1] = 0;
87                                         --thisline_len;
88                                 }
89                         }
90
91                         /* locate the colon separator */
92                         encoded_value = strchr(thisline, ':');
93                         if (encoded_value) {
94                                 *encoded_value++ = 0;
95
96                                 /* any qualifiers?  (look for a semicolon) */
97                                 is_base64 = bmstrcasestr(thisline, "encoding=base64") ? 1 : 0;
98                                 is_quoted_printable = bmstrcasestr(thisline, "encoding=quoted-printable") ? 1 : 0;
99
100                                 char *semicolon_pos = strchr(thisline, ';');
101                                 if (semicolon_pos) {
102                                         *semicolon_pos = 0;
103                                 }
104
105                                 decoded_value = malloc(thisline_len);
106                                 if (is_base64) {
107                                         CtdlDecodeBase64(decoded_value,
108                                                         encoded_value,
109                                                         strlen(encoded_value));
110                                 }
111                                 else if (is_quoted_printable) {
112                                         CtdlDecodeQuotedPrintable(decoded_value,
113                                                         encoded_value,
114                                                         strlen(encoded_value));
115                                 }
116                                 else {
117                                         strcpy(decoded_value, encoded_value);
118                                 }
119
120                                 if (!strcasecmp(thisline, "UID")) {
121                                         if (v->uid) free(v->uid);
122                                         v->uid = decoded_value;
123                                 }
124                                 else if (!strcasecmp(thisline, "SUMMARY")) {
125                                         if (v->summary) free(v->summary);
126                                         v->summary = decoded_value;
127                                 }
128                                 else if ( (!strcasecmp(thisline, "NOTE"))
129                                      || (!strcasecmp(thisline, "BODY")) ) {
130                                         if (v->body) free(v->body);
131                                         v->body = decoded_value;
132                                 }
133                                 else if (!strcasecmp(thisline, "X-OUTLOOK-WIDTH")) {
134                                         v->pos_width = atoi(decoded_value);
135                                         free(decoded_value);
136                                 }
137                                 else if (!strcasecmp(thisline, "X-OUTLOOK-HEIGHT")) {
138                                         v->pos_height = atoi(decoded_value);
139                                         free(decoded_value);
140                                 }
141                                 else if (!strcasecmp(thisline, "X-OUTLOOK-LEFT")) {
142                                         v->pos_left = atoi(decoded_value);
143                                         free(decoded_value);
144                                 }
145                                 else if (!strcasecmp(thisline, "X-OUTLOOK-TOP")) {
146                                         v->pos_top = atoi(decoded_value);
147                                         free(decoded_value);
148                                 }
149                                 else if ( (!strcasecmp(thisline, "X-OUTLOOK-COLOR"))
150                                      && (strlen(decoded_value) == 7)
151                                      && (decoded_value[0] == '#') ) {
152                                         sscanf(&decoded_value[1], "%2x%2x%2x",
153                                                 &v->color_red,
154                                                 &v->color_green,
155                                                 &v->color_blue);
156                                         free(decoded_value);
157                                 }
158                                 else {
159                                         free(decoded_value);    // throw it away
160                                 }
161
162                                 /* FIXME still need to handle these:
163                                  * X-OUTLOOK-CREATE-TIME:20070611T204615Z
164                                  * REV:20070611T204621Z
165                                  */
166                         }
167                         free(thisline);
168                 }
169         }
170
171         return(v);
172 }
173
174 void vnote_free(struct vnote *v) {
175         if (!v) return;
176         if (v->magic != CTDL_VNOTE_MAGIC) return;
177
178         if (v->uid) free(v->uid);
179         if (v->summary) free(v->summary);
180         if (v->body) free(v->body);
181         
182         memset(v, 0, sizeof(struct vnote));
183         free(v);
184 }
185
186
187 /* helper function for vnote_serialize() */
188 void vnote_serialize_output_field(char *append_to, char *field, char *label) {
189
190         char *mydup;
191         int output_len = 0;
192         int is_qp = 0;
193         char *ptr = field;
194         unsigned char ch;
195         int pos = 0;
196
197         if (!append_to) return;
198         if (!field) return;
199         if (!label) return;
200
201         mydup = malloc((strlen(field) * 3) + 1);
202         if (!mydup) return;
203         strcpy(mydup, "");
204
205         while (ptr[pos] != 0) {
206                 ch = (unsigned char)(ptr[pos++]);
207
208                 if (ch == 9) {
209                         mydup[output_len++] = ch;
210                 }
211                 else if ( (ch >= 32) && (ch <= 60) ) {
212                         mydup[output_len++] = ch;
213                 }
214                 else if ( (ch >= 62) && (ch <= 126) ) {
215                         mydup[output_len++] = ch;
216                 }
217                 else {
218                         sprintf((char *)&mydup[output_len], "=%02X", ch);
219                         output_len += 3;
220                         is_qp = 1;
221                 }
222         }
223         mydup[output_len] = 0;
224
225         sprintf(&append_to[strlen(append_to)], "%s%s:%s\r\n",
226                 label,
227                 (is_qp ? ";ENCODING=QUOTED-PRINTABLE" : ""),
228                 mydup);
229         free(mydup);
230 }
231
232
233 char *vnote_serialize(struct vnote *v) {
234         char *s;
235         int bytes_needed = 0;
236
237         if (!v) return NULL;
238         if (v->magic != CTDL_VNOTE_MAGIC) return NULL;
239
240         bytes_needed = 1024;
241         if (v->summary) bytes_needed += strlen(v->summary);
242         if (v->body) bytes_needed += strlen(v->body);
243         s = malloc(bytes_needed);
244         if (!s) return NULL;
245
246         strcpy(s, "");
247         vnote_serialize_output_field(s, "vnote", "BEGIN");
248         vnote_serialize_output_field(s, "//Citadel//vNote handler library//EN", "PRODID");
249         vnote_serialize_output_field(s, "1.1", "VERSION");
250         vnote_serialize_output_field(s, "PUBLIC", "CLASS");
251         vnote_serialize_output_field(s, v->uid, "UID");
252         vnote_serialize_output_field(s, v->summary, "SUMMARY");
253         vnote_serialize_output_field(s, v->body, "BODY");
254         vnote_serialize_output_field(s, v->body, "NOTE");
255         sprintf(&s[strlen(s)], "X-OUTLOOK-COLOR:#%02X%02X%02X\r\n",
256                 v->color_red, v->color_green, v->color_blue);
257         sprintf(&s[strlen(s)], "X-OUTLOOK-LEFT:%d\r\n", v->pos_left);
258         sprintf(&s[strlen(s)], "X-OUTLOOK-TOP:%d\r\n", v->pos_top);
259         sprintf(&s[strlen(s)], "X-OUTLOOK-WIDTH:%d\r\n", v->pos_width);
260         sprintf(&s[strlen(s)], "X-OUTLOOK-HEIGHT:%d\r\n", v->pos_height);
261         vnote_serialize_output_field(s, "vnote", "END");
262         return(s);
263 }