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