2cf1885235aca9c7468d0c9c533917b372e2eeda
[citadel.git] / webcit / groupdav_get.c
1 /*
2  * $Id$
3  *
4  * Handles GroupDAV GET requests.
5  *
6  */
7
8 #include "webcit.h"
9 #include "webserver.h"
10 #include "groupdav.h"
11 #include "mime_parser.h"
12
13
14 /*
15  * Fetch the entire contents of the room as one big ics file.
16  * This is for "webcal://" type access.
17  */     
18 void groupdav_get_big_ics(void) {
19         char buf[1024];
20
21         serv_puts("ICAL getics");
22         serv_getln(buf, sizeof buf);
23         if (buf[0] != '1') {
24                 wprintf("HTTP/1.1 404 not found\r\n");
25                 groupdav_common_headers();
26                 wprintf(
27                         "Content-Type: text/plain\r\n"
28                         "\r\n"
29                         "%s\r\n",
30                         &buf[4]
31                 );
32                 return;
33         }
34
35         wprintf("HTTP/1.1 200 OK\r\n");
36         groupdav_common_headers();
37         wprintf("Content-type: text/calendar; charset=UTF-8\r\n");
38         begin_burst();
39         while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
40                 wprintf("%s\r\n", buf);
41         }
42         end_burst();
43 }
44
45
46 /* 
47  * MIME parser callback function for groupdav_get()
48  * Helps identify the relevant section of a multipart message
49  */
50 void extract_preferred(char *name, char *filename, char *partnum, char *disp,
51                         void *content, char *cbtype, char *cbcharset,
52                         size_t length, char *encoding, void *userdata)
53 {
54         struct epdata *epdata = (struct epdata *)userdata;
55         int hit = 0;
56
57         /* We only want the first one that we found */
58         if (!IsEmptyStr(epdata->found_section)) return;
59
60         /* Check for a content type match */
61         if (strlen(epdata->desired_content_type_1) > 0) {
62                 if (!strcasecmp(epdata->desired_content_type_1, cbtype)) {
63                         hit = 1;
64                 }
65         }
66         if (!IsEmptyStr(epdata->desired_content_type_2)) {
67                 if (!strcasecmp(epdata->desired_content_type_2, cbtype)) {
68                         hit = 1;
69                 }
70         }
71
72         /* Is this the one?  If so, output it. */
73         if (hit) {
74                 safestrncpy(epdata->found_section, partnum, sizeof epdata->found_section);
75                 if (!IsEmptyStr(cbcharset)) {
76                         safestrncpy(epdata->charset, cbcharset, sizeof epdata->charset);
77                 }
78                 wprintf("Content-type: %s; charset=%s\r\n", cbtype, epdata->charset);
79                 begin_burst();
80                 client_write(content, length);
81                 end_burst();
82         }
83 }
84
85
86
87 /*
88  * The pathname is always going to take one of two formats:
89  * /groupdav/room_name/euid     (GroupDAV)
90  * /groupdav/room_name          (webcal)
91  */
92 void groupdav_get(char *dav_pathname) {
93         char dav_roomname[1024];
94         char dav_uid[1024];
95         long dav_msgnum = (-1);
96         char buf[1024];
97         int in_body = 0;
98         int found_content_type = 0;
99         char *ptr;
100         char *endptr;
101         char *msgtext = NULL;
102         size_t msglen = 0;
103         size_t msgalloc = 0;
104         int linelen;
105         char content_type[128];
106         char charset[128];
107         char date[128];
108         struct epdata epdata;
109
110         if (num_tokens(dav_pathname, '/') < 3) {
111                 wprintf("HTTP/1.1 404 not found\r\n");
112                 groupdav_common_headers();
113                 wprintf(
114                         "Content-Type: text/plain\r\n"
115                         "\r\n"
116                         "The object you requested was not found.\r\n"
117                 );
118                 return;
119         }
120
121         extract_token(dav_roomname, dav_pathname, 2, '/', sizeof dav_roomname);
122         extract_token(dav_uid, dav_pathname, 3, '/', sizeof dav_uid);
123         if ((!strcasecmp(dav_uid, "ics")) || (!strcasecmp(dav_uid, "calendar.ics"))) {
124                 strcpy(dav_uid, "");
125         }
126
127         /* Go to the correct room. */
128         if (strcasecmp(WC->wc_roomname, dav_roomname)) {
129                 gotoroom(dav_roomname);
130         }
131         if (strcasecmp(WC->wc_roomname, dav_roomname)) {
132                 wprintf("HTTP/1.1 404 not found\r\n");
133                 groupdav_common_headers();
134                 wprintf(
135                         "Content-Type: text/plain\r\n"
136                         "\r\n"
137                         "There is no folder called \"%s\" on this server.\r\n",
138                         dav_roomname
139                 );
140                 return;
141         }
142
143         /** GET on the collection itself returns an ICS of the entire collection.
144          */
145         if (!strcasecmp(dav_uid, "")) {
146                 groupdav_get_big_ics();
147                 return;
148         }
149
150         dav_msgnum = locate_message_by_uid(dav_uid);
151         serv_printf("MSG2 %ld", dav_msgnum);
152         serv_getln(buf, sizeof buf);
153         if (buf[0] != '1') {
154                 wprintf("HTTP/1.1 404 not found\r\n");
155                 groupdav_common_headers();
156                 wprintf(
157                         "Content-Type: text/plain\r\n"
158                         "\r\n"
159                         "Object \"%s\" was not found in the \"%s\" folder.\r\n",
160                         dav_uid,
161                         dav_roomname
162                 );
163                 return;
164         }
165
166         /* We got it; a message is now arriving from the server.  Read it in. */
167
168         in_body = 0;
169         found_content_type = 0;
170         strcpy(charset, "UTF-8");
171         strcpy(content_type, "text/plain");
172         strcpy(date, "");
173         while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
174                 linelen = strlen(buf);
175
176                 /* Append it to the buffer */
177                 if ((msglen + linelen + 3) > msgalloc) {
178                         msgalloc = ( (msgalloc > 0) ? (msgalloc * 2) : 1024 );
179                         msgtext = realloc(msgtext, msgalloc);
180                 }
181                 strcpy(&msgtext[msglen], buf);
182                 msglen += linelen;
183                 strcpy(&msgtext[msglen], "\n");
184                 msglen += 1;
185
186                 /* Also learn some things about the message */
187                 if (linelen == 0) {
188                         in_body = 1;
189                 }
190                 if (!in_body) {
191                         if (!strncasecmp(buf, "Date:", 5)) {
192                                 safestrncpy(date, &buf[5], sizeof date);
193                                 striplt(date);
194                         }
195                         if (!strncasecmp(buf, "Content-type:", 13)) {
196                                 safestrncpy(content_type, &buf[13], sizeof content_type);
197                                 striplt(content_type);
198                                 ptr = bmstrcasestr(&buf[13], "charset=");
199                                 if (ptr) {
200                                         safestrncpy(charset, ptr+8, sizeof charset);
201                                         striplt(charset);
202                                         endptr = strchr(charset, ';');
203                                         if (endptr != NULL) strcpy(endptr, "");
204                                 }
205                                 endptr = strchr(content_type, ';');
206                                 if (endptr != NULL) strcpy(endptr, "");
207                         }
208                 }
209         }
210         msgtext[msglen] = 0;
211
212         /* Output headers common to single or multi part messages */
213
214         wprintf("HTTP/1.1 200 OK\r\n");
215         groupdav_common_headers();
216         wprintf("etag: \"%ld\"\r\n", dav_msgnum);
217         wprintf("Date: %s\r\n", date);
218
219         memset(&epdata, 0, sizeof(struct epdata));
220         safestrncpy(epdata.charset, charset, sizeof epdata.charset);
221
222         /* If we have a multipart message on our hands, and we are in a groupware room,
223          * strip it down to only the relevant part.
224          */
225         if (!strncasecmp(content_type, "multipart/", 10)) {
226
227                 if ( (WC->wc_default_view == VIEW_CALENDAR) || (WC->wc_default_view == VIEW_TASKS) ) {
228                         strcpy(epdata.desired_content_type_1, "text/calendar");
229                 }
230
231                 else if (WC->wc_default_view == VIEW_ADDRESSBOOK) {
232                         strcpy(epdata.desired_content_type_1, "text/vcard");
233                         strcpy(epdata.desired_content_type_2, "text/x-vcard");
234                 }
235
236                 mime_parser(msgtext, &msgtext[msglen], extract_preferred, NULL, NULL, (void *)&epdata, 0);
237         }
238
239         /* If epdata.found_section is empty, we haven't output anything yet, so output the whole thing */
240
241         if (IsEmptyStr(epdata.found_section)) {
242                 ptr = msgtext;
243                 endptr = &msgtext[msglen];
244         
245                 wprintf("Content-type: %s; charset=%s\r\n", content_type, charset);
246         
247                 in_body = 0;
248                 do {
249                         ptr = memreadline(ptr, buf, sizeof buf);
250         
251                         if (in_body) {
252                                 wprintf("%s\r\n", buf);
253                         }
254                         else if ((buf[0] == 0) && (in_body == 0)) {
255                                 in_body = 1;
256                                 begin_burst();
257                         }
258                 } while (ptr < endptr);
259         
260                 end_burst();
261         }
262
263         free(msgtext);
264 }