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