266bd1430f1d572e80b6e2dd72b68aba57f0271d
[citadel.git] / webcit / groupdav_propfind.c
1 /*
2  * $Id$
3  */
4 /**
5  * \defgroup GroupdavPropfind Handles GroupDAV PROPFIND requests.
6  *
7  * A few notes about our XML output:
8  *
9  * --> Yes, we are spewing tags directly instead of using an XML library.
10  *     If you would like to rewrite this using libxml2, code it up and submit
11  *     a patch.  Whining will be summarily ignored.
12  *
13  * --> XML is deliberately output with no whitespace/newlines between tags.
14  *     This makes it difficult to read, but we have discovered clients which
15  *     crash when you try to pretty it up.
16  *
17  */
18
19 #include "webcit.h"
20 #include "webserver.h"
21 #include "groupdav.h"
22
23
24 /**
25  * \brief get all messages of this user
26  * Given an encoded UID, translate that to an unencoded Citadel EUID and
27  * then search for it in the current room.  Return a message number or -1
28  * if not found.
29  *
30  * NOTE: this function relies on the Citadel server's brute-force search.
31  * There's got to be a way to optimize this better.
32  * \param uid the user to get the data for...
33  */
34 long locate_message_by_uid(char *uid) {
35         char buf[SIZ];
36         char decoded_uid[SIZ];
37         long retval = (-1L);
38
39         /* Decode the uid */
40         euid_unescapize(decoded_uid, uid);
41
42         serv_printf("EUID %s", decoded_uid);
43         serv_getln(buf, sizeof buf);
44         if (buf[0] == '2') {
45                 retval = extract_long(&buf[4], 0);
46         }
47         return(retval);
48 }
49
50
51 /**
52  * \brief List folders containing interesting groupware objects
53  */
54 void groupdav_folder_list(void) {
55         char buf[SIZ];
56         char roomname[SIZ];
57         int view;
58         char datestring[SIZ];
59         time_t now;
60
61         now = time(NULL);
62         http_datestring(datestring, sizeof datestring, now);
63
64         /**
65          * Be rude.  Completely ignore the XML request and simply send them
66          * everything we know about.  Let the client sort it out.
67          */
68         wprintf("HTTP/1.1 207 Multi-Status\r\n");
69         groupdav_common_headers();
70         wprintf("Date: %s\r\n", datestring);
71         wprintf("Content-type: text/xml\r\n");
72         wprintf("Content-encoding: identity\r\n");
73
74         begin_burst();
75
76         wprintf("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
77                 "<D:multistatus xmlns:D=\"DAV:\" xmlns:G=\"http://groupdav.org/\">"
78         );
79
80         serv_puts("LKRA");
81         serv_getln(buf, sizeof buf);
82         if (buf[0] == '1') while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
83
84                 extract_token(roomname, buf, 0, '|', sizeof roomname);
85                 view = extract_int(buf, 6);
86
87                 /**
88                  * For now, only list rooms that we know a GroupDAV client
89                  * might be interested in.  In the future we may add
90                  * the rest.
91                  */
92                 if ((view == VIEW_CALENDAR)
93                    || (view == VIEW_TASKS)
94                    || (view == VIEW_ADDRESSBOOK) ) {
95
96                         wprintf("<D:response>");
97
98                         wprintf("<D:href>");
99                         if (strlen(WC->http_host) > 0) {
100                                 wprintf("%s://%s",
101                                         (is_https ? "https" : "http"),
102                                         WC->http_host);
103                         }
104                         wprintf("/groupdav/");
105                         urlescputs(roomname);
106                         wprintf("/</D:href>");
107
108                         wprintf("<D:propstat>");
109                         wprintf("<D:status>HTTP/1.1 200 OK</D:status>");
110                         wprintf("<D:prop>");
111                         wprintf("<D:fullname>");
112                         escputs(roomname);
113                         wprintf("</D:fullname>");
114                         wprintf("<D:resourcetype><D:collection/>");
115
116                         switch(view) {
117                                 case VIEW_CALENDAR:
118                                         wprintf("<G:vevent-collection />");
119                                         break;
120                                 case VIEW_TASKS:
121                                         wprintf("<G:vtodo-collection />");
122                                         break;
123                                 case VIEW_ADDRESSBOOK:
124                                         wprintf("<G:vcard-collection />");
125                                         break;
126                         }
127
128                         wprintf("</D:resourcetype>");
129                         wprintf("</D:prop>");
130                         wprintf("</D:propstat>");
131                         wprintf("</D:response>");
132                 }
133         }
134         wprintf("</D:multistatus>\n");
135
136         end_burst();
137 }
138
139
140
141 /**
142  * \brief Search though a davname
143  * \param dav_pathname The pathname is always going to be /groupdav/room_name/msg_num
144  */
145 void groupdav_propfind(char *dav_pathname) {
146         char dav_roomname[256];
147         char dav_uid[256];
148         char msgnum[256];
149         long dav_msgnum = (-1);
150         char buf[256];
151         char uid[256];
152         char encoded_uid[256];
153         long *msgs = NULL;
154         int num_msgs = 0;
155         int i;
156         char datestring[256];
157         time_t now;
158
159         now = time(NULL);
160         http_datestring(datestring, sizeof datestring, now);
161
162         extract_token(dav_roomname, dav_pathname, 2, '/', sizeof dav_roomname);
163         extract_token(dav_uid, dav_pathname, 3, '/', sizeof dav_uid);
164
165         /*
166         lprintf(9, "dav_pathname: %s\n", dav_pathname);
167         lprintf(9, "dav_roomname: %s\n", dav_roomname);
168         lprintf(9, "     dav_uid: %s\n", dav_uid);
169         */
170
171         /**
172          * If the room name is blank, the client is requesting a
173          * folder list.
174          */
175         if (strlen(dav_roomname) == 0) {
176                 groupdav_folder_list();
177                 return;
178         }
179
180         /* Go to the correct room. */
181         if (strcasecmp(WC->wc_roomname, dav_roomname)) {
182                 gotoroom(dav_roomname);
183         }
184         if (strcasecmp(WC->wc_roomname, dav_roomname)) {
185                 wprintf("HTTP/1.1 404 not found\r\n");
186                 groupdav_common_headers();
187                 wprintf("Date: %s\r\n", datestring);
188                 wprintf(
189                         "Content-Type: text/plain\r\n"
190                         "\r\n"
191                         "There is no folder called \"%s\" on this server.\r\n",
192                         dav_roomname
193                 );
194                 return;
195         }
196
197         /**
198          * If dav_uid is non-empty, client is requesting a PROPFIND on
199          * a specific item in the room.  This is not valid GroupDAV, but
200          * we try to honor it anyway because some clients are expecting
201          * it to work...
202          */
203         if (strlen(dav_uid) > 0) {
204
205                 dav_msgnum = locate_message_by_uid(dav_uid);
206                 if (dav_msgnum < 0) {
207                         wprintf("HTTP/1.1 404 not found\r\n");
208                         groupdav_common_headers();
209                         wprintf(
210                                 "Content-Type: text/plain\r\n"
211                                 "\r\n"
212                                 "Object \"%s\" was not found in the \"%s\" folder.\r\n",
213                                 dav_uid,
214                                 dav_roomname
215                         );
216                         return;
217                 }
218
219                 /**
220                  * Be rude.  Completely ignore the XML request and simply send them
221                  * everything we know about (which is going to simply be the ETag and
222                  * nothing else).  Let the client-side parser sort it out.
223                  */
224                 wprintf("HTTP/1.1 207 Multi-Status\r\n");
225                 groupdav_common_headers();
226                 wprintf("Date: %s\r\n", datestring);
227                 wprintf("Content-type: text/xml\r\n");
228                 wprintf("Content-encoding: identity\r\n");
229         
230                 begin_burst();
231         
232                 wprintf("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
233                         "<D:multistatus xmlns:D=\"DAV:\" xmlns:G=\"http://groupdav.org/\">"
234                 );
235
236                 wprintf("<D:response>");
237
238                 wprintf("<D:href>");
239                 if (strlen(WC->http_host) > 0) {
240                         wprintf("%s://%s",
241                                 (is_https ? "https" : "http"),
242                                 WC->http_host);
243                 }
244                 wprintf("/groupdav/");
245                 urlescputs(WC->wc_roomname);
246                 euid_escapize(encoded_uid, dav_uid);
247                 wprintf("/%s", encoded_uid);
248                 wprintf("</D:href>");
249                 wprintf("<D:propstat>");
250                 wprintf("<D:status>HTTP/1.1 200 OK</D:status>");
251                 wprintf("<D:prop><D:getetag>\"%ld\"</D:getetag></D:prop>", dav_msgnum);
252                 wprintf("</D:propstat>");
253
254                 wprintf("</D:response>\n");
255                 wprintf("</D:multistatus>\n");
256                 end_burst();
257                 return;
258         }
259
260
261         /**
262          * We got to this point, which means that the client is requesting
263          * a 'collection' (i.e. a list of all items in the room).
264          *
265          * Be rude.  Completely ignore the XML request and simply send them
266          * everything we know about (which is going to simply be the ETag and
267          * nothing else).  Let the client-side parser sort it out.
268          */
269         wprintf("HTTP/1.1 207 Multi-Status\r\n");
270         groupdav_common_headers();
271         wprintf("Date: %s\r\n", datestring);
272         wprintf("Content-type: text/xml\r\n");
273         wprintf("Content-encoding: identity\r\n");
274
275         begin_burst();
276
277         wprintf("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
278                 "<D:multistatus xmlns:D=\"DAV:\" xmlns:G=\"http://groupdav.org/\">"
279         );
280
281         serv_puts("MSGS ALL");
282         serv_getln(buf, sizeof buf);
283         if (buf[0] == '1') while (serv_getln(msgnum, sizeof msgnum), strcmp(msgnum, "000")) {
284                 msgs = realloc(msgs, ++num_msgs * sizeof(long));
285                 msgs[num_msgs-1] = atol(msgnum);
286         }
287
288         if (num_msgs > 0) for (i=0; i<num_msgs; ++i) {
289
290                 strcpy(uid, "");
291                 serv_printf("MSG0 %ld|3", msgs[i]);
292                 serv_getln(buf, sizeof buf);
293                 if (buf[0] == '1') while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
294                         if (!strncasecmp(buf, "exti=", 5)) {
295                                 strcpy(uid, &buf[5]);
296                         }
297                 }
298
299                 if (strlen(uid) > 0) {
300                         wprintf("<D:response>");
301                         wprintf("<D:href>");
302                         if (strlen(WC->http_host) > 0) {
303                                 wprintf("%s://%s",
304                                         (is_https ? "https" : "http"),
305                                         WC->http_host);
306                         }
307                         wprintf("/groupdav/");
308                         urlescputs(WC->wc_roomname);
309                         euid_escapize(encoded_uid, uid);
310                         wprintf("/%s", encoded_uid);
311                         wprintf("</D:href>");
312                         wprintf("<D:propstat>");
313                         wprintf("<D:status>HTTP/1.1 200 OK</D:status>");
314                         wprintf("<D:prop><D:getetag>\"%ld\"</D:getetag></D:prop>", msgs[i]);
315                         wprintf("</D:propstat>");
316                         wprintf("</D:response>");
317                 }
318         }
319
320         wprintf("</D:multistatus>\n");
321         end_burst();
322
323         if (msgs != NULL) {
324                 free(msgs);
325         }
326 }
327
328 /*@}*/