04cd48abbf963b6a4f5d18ce1c9b3d24a6dc4852
[citadel.git] / webcit / roomlist.c
1 /*
2  * $Id: roomlist.c 7751 2009-08-28 21:13:28Z dothebart $
3  * room listings and filters.
4  */
5
6 #include "webcit.h"
7 #include "webserver.h"
8
9
10 void DeleteFloor(void *vFloor)
11 {
12         floor *Floor;
13         Floor = (floor*) vFloor;
14         FreeStrBuf(&Floor->Name);
15         free(Floor);
16 }
17
18 int SortFloorsByNameOrder(const void *vfloor1, const void *vfloor2) 
19 {
20         floor *f1 = (floor*) GetSearchPayload(vfloor1);
21         floor *f2 = (floor*) GetSearchPayload(vfloor2);
22         
23         /* prefer My floor over alpabetical sort */
24         if (f1->ID == VIRTUAL_MY_FLOOR)
25                 return 1;
26         if (f2->ID == VIRTUAL_MY_FLOOR)
27                 return -1;
28
29         return strcmp(ChrPtr(f1->Name), ChrPtr(f2->Name));
30 }
31
32 HashList *GetFloorListHash(StrBuf *Target, WCTemplputParams *TP) 
33 {
34
35         const char *Err;
36         StrBuf *Buf;
37         HashList *floors;
38         HashPos *it;
39         floor *Floor;
40         void *vFloor;
41         const char *Pos;
42         int i;
43         wcsession *WCC = WC;
44         const char *HashKey;
45         long HKLen;
46
47
48         if (WCC->Floors != NULL)
49                 return WCC->Floors;
50         WCC->Floors = floors = NewHash(1, NULL);
51         Buf = NewStrBuf();
52
53         Floor = malloc(sizeof(floor));
54         Floor->ID = VIRTUAL_MY_FLOOR;
55         Floor->Name = NewStrBufPlain(_("My Folders"), -1);
56         Floor->NRooms = 0;
57         
58         Put(floors, IKEY(Floor->ID), Floor, DeleteFloor);
59
60         serv_puts("LFLR"); /* get floors */
61         StrBufTCP_read_line(Buf, &WC->serv_sock, 0, &Err); /* '100', we hope */
62         if (GetServerStatus(Buf, NULL) == 1) 
63         {
64                 while(StrBufTCP_read_line(Buf, &WC->serv_sock, 0, &Err), strcmp(ChrPtr(Buf), "000")) 
65                 {
66                         
67                         Pos = NULL;
68
69                         Floor = malloc(sizeof(floor));
70                         Floor->ID = StrBufExtractNext_long(Buf, &Pos, '|');
71                         Floor->Name = NewStrBufPlain(NULL, StrLength(Buf));
72                         StrBufExtract_NextToken(Floor->Name, Buf, &Pos, '|');
73                         Floor->NRooms = StrBufExtractNext_long(Buf, &Pos, '|');
74
75                         Put(floors, IKEY(Floor->ID), Floor, DeleteFloor);
76                 }
77         }
78         FreeStrBuf(&Buf);
79         
80         /* now lets pre-sort them alphabeticaly. */
81         i = 1;
82         SortByPayload(floors, SortFloorsByNameOrder);
83         it = GetNewHashPos(floors, 0);
84         while ( GetNextHashPos(floors, it, &HKLen, &HashKey, &vFloor)) 
85                 ((floor*) vFloor)->AlphaN = i++;
86         DeleteHashPos(&it);
87         SortByHashKeyStr(floors);
88
89         return floors;
90 }
91
92 void tmplput_FLOOR_ID(StrBuf *Target, WCTemplputParams *TP) 
93 {
94         floor *Floor = (floor *)(TP->Context);
95
96         StrBufAppendPrintf(Target, "%d", Floor->ID);
97 }
98
99 void tmplput_FLOOR_NAME(StrBuf *Target, WCTemplputParams *TP) 
100 {
101         floor *Floor = (floor *)(TP->Context);
102
103         StrBufAppendTemplate(Target, TP, Floor->Name, 0);
104 }
105
106 void tmplput_FLOOR_NROOMS(StrBuf *Target, WCTemplputParams *TP) 
107 {
108         floor *Floor = (floor *)(TP->Context);
109
110         StrBufAppendPrintf(Target, "%d", Floor->NRooms);
111 }
112 HashList *GetRoomListHashLKRA(StrBuf *Target, WCTemplputParams *TP) 
113 {
114         wcsession *WCC = WC;
115
116         if (WCC->Floors == NULL)
117                 GetFloorListHash(Target, TP);
118         serv_puts("LKRA");
119         return GetRoomListHash(Target, TP);
120 }
121
122 void DeleteFolder(void *vFolder)
123 {
124         int i;
125         folder *room;
126         room = (folder*) vFolder;
127
128         FreeStrBuf(&room->name);
129
130         if (room->RoomNameParts != NULL)
131         {
132                 for (i=0; i < room->nRoomNameParts; i++)
133                         FreeStrBuf(&room->RoomNameParts[i]);
134                 free(room->RoomNameParts);
135         }
136         free(room);
137 }
138
139
140 HashList *GetRoomListHash(StrBuf *Target, WCTemplputParams *TP) 
141 {
142         HashList *rooms;
143         folder *room;
144         StrBuf *Buf;
145         const char *Pos;
146         const char *Err;
147         void *vFloor;
148         wcsession *WCC = WC;
149         CompareFunc SortIt;
150         WCTemplputParams SubTP;
151
152         Buf = NewStrBuf();
153         rooms = NewHash(1, NULL);
154         StrBufTCP_read_line(Buf, &WC->serv_sock, 0, &Err);
155         if (GetServerStatus(Buf, NULL) == 1) 
156         {
157                 while(StrBufTCP_read_line(Buf, &WC->serv_sock, 0, &Err), 
158                       strcmp(ChrPtr(Buf), "000")) 
159                 {
160
161                         Pos = NULL;
162                         room = (folder*) malloc (sizeof(folder));
163                         memset(room, 0, sizeof(folder));
164
165                         /* Load the base data from the server reply */
166                         room->name = NewStrBufPlain(NULL, StrLength(Buf));
167                         StrBufExtract_NextToken(room->name, Buf, &Pos, '|');
168
169                         room->QRFlags = StrBufExtractNext_long(Buf, &Pos, '|');
170                         room->floorid = StrBufExtractNext_long(Buf, &Pos, '|');
171                         room->listorder = StrBufExtractNext_long(Buf, &Pos, '|');
172                         room->QRFlags2 = StrBufExtractNext_long(Buf, &Pos, '|');
173
174                         room->RAFlags = StrBufExtractNext_long(Buf, &Pos, '|');
175
176 /*
177  ACWHUT?
178                         room->ACL = NewStrBufPlain(NULL, StrLength(Buf));
179                         StrBufExtract_NextToken(room->ACL, Buf, &Pos, '|');
180 */
181
182                         room->view = StrBufExtractNext_long(Buf, &Pos, '|');
183                         room->defview = StrBufExtractNext_long(Buf, &Pos, '|');
184                         room->lastchange = StrBufExtractNext_long(Buf, &Pos, '|');
185
186                         /* Evaluate the Server sent data for later use */
187                         /* find out, whether we are in a sub-room */
188                         room->nRoomNameParts = StrBufNum_tokens(room->name, '\\');
189                         if (room->nRoomNameParts > 1)
190                         {
191                                 int i;
192
193                                 Pos = NULL;
194                                 room->RoomNameParts = malloc(sizeof(StrBuf*) * (room->nRoomNameParts + 1));
195                                 memset(room->RoomNameParts, 0, sizeof(StrBuf*) * (room->nRoomNameParts + 1));
196                                 for (i=0; i < room->nRoomNameParts; i++)
197                                 {
198                                         room->RoomNameParts[i] = NewStrBuf();
199                                         StrBufExtract_NextToken(room->RoomNameParts[i],
200                                                                 room->name, &Pos, '\\');
201                                 }
202                         }
203
204                         /* Private mailboxes on the main floor get remapped to the personal folder */
205                         if ((room->QRFlags & QR_MAILBOX) && 
206                             (room->floorid == 0))
207                         {
208                                 room->floorid = VIRTUAL_MY_FLOOR;
209                                 if ((room->nRoomNameParts == 1) && 
210                                     (StrLength(room->name) == 4) && 
211                                     (strcmp(ChrPtr(room->name), "Mail") == 0))
212                                 {
213                                         room->is_inbox = 1;
214                                 }
215
216                         }
217                         /* get a pointer to the floor we're on: */
218                         GetHash(WCC->Floors, IKEY(room->floorid), &vFloor);
219                         room->Floor = (const floor*) vFloor;
220
221
222
223                         /* now we know everything, remember it... */
224                         Put(rooms, SKEY(room->name), room, DeleteFolder);
225                 }
226         }
227
228         SubTP.Filter.ContextType = CTX_ROOMS;
229         SortIt = RetrieveSort(&SubTP, NULL, 0, HKEY("fileunsorted"), 0);
230         if (SortIt != NULL)
231                 SortByPayload(rooms, SortIt);
232         else 
233                 SortByPayload(rooms, SortRoomsByListOrder);
234         FreeStrBuf(&Buf);
235         return rooms;
236 }
237
238 /** Unused function that orders rooms by the listorder flag */
239 int SortRoomsByListOrder(const void *room1, const void *room2) 
240 {
241         folder *r1 = (folder*) GetSearchPayload(room1);
242         folder *r2 = (folder*) GetSearchPayload(room2);
243   
244         if (r1->listorder == r2->listorder) return 0;
245         if (r1->listorder > r2->listorder) return 1;
246         return -1;
247 }
248
249 int CompareRoomListByFloorRoomPrivFirst(const void *room1, const void *room2) 
250 {
251         folder *r1 = (folder*) GetSearchPayload(room1);
252         folder *r2 = (folder*) GetSearchPayload(room2);
253   
254         if ((r1->Floor == NULL)  ||
255             (r2->Floor == NULL))
256                 return 0;
257                 
258         /**
259          * are we on the same floor? else sort by floor.
260          */
261         if (r1->Floor != r2->Floor)
262         {
263                 /**
264                  * the private rooms are first in any case.
265                  */
266                 if (r1->Floor->ID == VIRTUAL_MY_FLOOR)
267                         return -1;
268                 if (r2->Floor->ID == VIRTUAL_MY_FLOOR)
269                         return 1;
270                 /**
271                  * else decide alpaheticaly by floorname
272                  */
273                 return (r1->Floor->AlphaN > r2->Floor->AlphaN)? 1 : -1;
274         }
275
276         /**
277          * if we have different levels of subdirectories, 
278          * we want the toplevel to be first, regardless of sort
279          * sequence.
280          */
281         if (((r1->nRoomNameParts > 1) || 
282             (r2->nRoomNameParts > 1)    )&&
283             (r1->nRoomNameParts != r2->nRoomNameParts))
284         {
285                 int i, ret;
286                 int nparts = (r1->nRoomNameParts > r2->nRoomNameParts)?
287                         r2->nRoomNameParts : r1->nRoomNameParts;
288
289                 for (i=0; i < nparts; i++)
290                 {
291                         ret = strcmp (ChrPtr(r1->name), 
292                                       ChrPtr(r2->name));
293                         /**
294                          * Deltas in common parts? exit here.
295                          */
296                         if (ret != 0) 
297                                 return ret;
298                 }
299
300                 /**
301                  * who's a subdirectory of whom?
302                  */
303                 if (r1->nRoomNameParts > r2->nRoomNameParts)
304                         return 1;
305                 else
306                         return -1;
307
308         }
309
310         /**
311          * else just sort alphabeticaly.
312          */
313         return strcmp (ChrPtr(r1->name), 
314                        ChrPtr(r2->name));
315 }
316
317 int CompareRoomListByFloorRoomPrivFirstRev(const void *room1, const void *room2) 
318 {
319         folder *r1 = (folder*) GetSearchPayload(room1);
320         folder *r2 = (folder*) GetSearchPayload(room2);
321   
322         if ((r1->Floor == NULL)  ||
323             (r2->Floor == NULL))
324                 return 0;
325                 
326         /**
327          * are we on the same floor? else sort by floor.
328          */
329         if (r2->Floor != r1->Floor)
330         {
331                 /**
332                  * the private rooms are first in any case.
333                  */
334                 if (r1->Floor->ID == VIRTUAL_MY_FLOOR)
335                         return -1;
336                 if (r2->Floor->ID == VIRTUAL_MY_FLOOR)
337                         return 1;
338                 /**
339                  * else decide alpaheticaly by floorname
340                  */
341
342                 return (r1->Floor->AlphaN < r2->Floor->AlphaN)? 1 : -1;
343         }
344
345         /**
346          * if we have different levels of subdirectories, 
347          * we want the toplevel to be first, regardless of sort
348          * sequence.
349          */
350         if (((r1->nRoomNameParts > 1) || 
351             (r2->nRoomNameParts > 1)    )&&
352             (r1->nRoomNameParts != r2->nRoomNameParts))
353         {
354                 int i, ret;
355                 int nparts = (r1->nRoomNameParts > r2->nRoomNameParts)?
356                         r2->nRoomNameParts : r1->nRoomNameParts;
357
358                 for (i=0; i < nparts; i++)
359                 {
360                         /**
361                          * special cases if one room is top-level...
362                          */
363                         if (r2->nRoomNameParts == 1)
364                                 ret = strcmp (ChrPtr(r2->name), 
365                                               ChrPtr(r1->RoomNameParts[i]));
366                         else if (r1->nRoomNameParts == 1)
367                                 ret = strcmp (ChrPtr(r2->RoomNameParts[i]),
368                                               ChrPtr(r1->name));
369                         else 
370                                 ret = strcmp (ChrPtr(r2->RoomNameParts[i]), 
371                                               ChrPtr(r1->RoomNameParts[i]));
372                         /**
373                          * Deltas in common parts? exit here.
374                          */
375                         if (ret != 0) 
376                                 return ret;
377                 }
378
379                 /**
380                  * who's a subdirectory of whom?
381                  */
382                 if (r1->nRoomNameParts > r2->nRoomNameParts)
383                         return 1;
384                 else
385                         return -1;
386         }
387
388         return strcmp (ChrPtr(r2->name), 
389                        ChrPtr(r1->name));
390 }
391
392 int GroupchangeRoomListByFloorRoomPrivFirst(const void *room1, const void *room2) 
393 {
394         folder *r1 = (folder*) room1;
395         folder *r2 = (folder*) room2;
396   
397
398         if ((r1->Floor == NULL)  ||
399             (r2->Floor == NULL))
400                 return 0;
401                 
402         if (r1->Floor == r2->Floor)
403                 return 0;
404         else 
405         {
406                 wcsession *WCC = WC;
407                 static int columns = 3;
408                 int boxes_per_column = 0;
409                 int nf;
410
411                 nf = GetCount(WCC->Floors);
412                 while (nf % columns != 0) ++nf;
413                 boxes_per_column = (nf / columns);
414                 if (boxes_per_column < 1)
415                         boxes_per_column = 1;
416                 if (r1->Floor->AlphaN % boxes_per_column == 0)
417                         return 2;
418                 else 
419                         return 1;
420         }
421 }
422
423
424
425
426
427
428 void tmplput_ROOM_NAME(StrBuf *Target, WCTemplputParams *TP) 
429 {
430         folder *Folder = (folder *)(TP->Context);
431
432         StrBufAppendTemplate(Target, TP, Folder->name, 0);
433 }
434 void tmplput_ROOM_BASENAME(StrBuf *Target, WCTemplputParams *TP) 
435 {
436         folder *room = (folder *)(TP->Context);
437
438         if (room->nRoomNameParts > 1)
439                 StrBufAppendTemplate(Target, TP, 
440                                       room->RoomNameParts[room->nRoomNameParts - 1], 0);
441         else 
442                 StrBufAppendTemplate(Target, TP, room->name, 0);
443 }
444 void tmplput_ROOM_LEVEL_N_TIMES(StrBuf *Target, WCTemplputParams *TP) 
445 {
446         folder *room = (folder *)(TP->Context);
447         int i;
448         const char *AppendMe;
449         long AppendMeLen;
450
451
452         if (room->nRoomNameParts > 1)
453         {
454                 GetTemplateTokenString(Target, TP, 0, &AppendMe, &AppendMeLen);
455                 for (i = 0; i < room->nRoomNameParts; i++)
456                         StrBufAppendBufPlain(Target, AppendMe, AppendMeLen, 0);
457         }
458 }
459
460 void tmplput_ROOM_ACL(StrBuf *Target, WCTemplputParams *TP) 
461 {
462         folder *Folder = (folder *)(TP->Context);
463
464         StrBufAppendPrintf(Target, "%ld", Folder->RAFlags, 0);
465 }
466
467
468 void tmplput_ROOM_QRFLAGS(StrBuf *Target, WCTemplputParams *TP) 
469 {
470         folder *Folder = (folder *)(TP->Context);
471         StrBufAppendPrintf(Target, "%d", Folder->QRFlags);
472 }
473
474
475
476 void tmplput_ROOM_FLOORID(StrBuf *Target, WCTemplputParams *TP) 
477 {
478         folder *Folder = (folder *)(TP->Context);
479         StrBufAppendPrintf(Target, "%d", Folder->floorid);
480 }
481
482 void tmplput_ROOM_LISTORDER(StrBuf *Target, WCTemplputParams *TP) 
483 {
484         folder *Folder = (folder *)(TP->Context);
485         StrBufAppendPrintf(Target, "%d", Folder->listorder);
486 }
487 void tmplput_ROOM_VIEW(StrBuf *Target, WCTemplputParams *TP) 
488 {
489         folder *Folder = (folder *)(TP->Context);
490         StrBufAppendPrintf(Target, "%d", Folder->view);
491 }
492 void tmplput_ROOM_DEFVIEW(StrBuf *Target, WCTemplputParams *TP) 
493 {
494         folder *Folder = (folder *)(TP->Context);
495         StrBufAppendPrintf(Target, "%d", Folder->defview);
496 }
497 void tmplput_ROOM_LASTCHANGE(StrBuf *Target, WCTemplputParams *TP) 
498 {
499         folder *Folder = (folder *)(TP->Context);
500         StrBufAppendPrintf(Target, "%d", Folder->lastchange);
501 }
502 void tmplput_ROOM_FLOOR_ID(StrBuf *Target, WCTemplputParams *TP) 
503 {
504         folder *Folder = (folder *)(TP->Context);
505         const floor *Floor = Folder->Floor;
506
507         if (Floor == NULL)
508                 return;
509
510         StrBufAppendPrintf(Target, "%d", Floor->ID);
511 }
512
513 void tmplput_ROOM_FLOOR_NAME(StrBuf *Target, WCTemplputParams *TP) 
514 {
515         folder *Folder = (folder *)(TP->Context);
516         const floor *Floor = Folder->Floor;
517
518         if (Floor == NULL)
519                 return;
520
521         StrBufAppendTemplate(Target, TP, Floor->Name, 0);
522 }
523
524 void tmplput_ROOM_FLOOR_NROOMS(StrBuf *Target, WCTemplputParams *TP) 
525 {
526         folder *Folder = (folder *)(TP->Context);
527         const floor *Floor = Folder->Floor;
528
529         if (Floor == NULL)
530                 return;
531         StrBufAppendPrintf(Target, "%d", Floor->NRooms);
532 }
533
534
535
536 int ConditionalRoomHas_UA_KNOWN(StrBuf *Target, WCTemplputParams *TP)
537 {
538         folder *Folder = (folder *)(TP->Context);
539         return (Folder->RAFlags & UA_KNOWN) != 0;
540 }
541
542 int ConditionalRoomHas_UA_GOTOALLOWED(StrBuf *Target, WCTemplputParams *TP)
543 {
544         folder *Folder = (folder *)(TP->Context);
545         return (Folder->RAFlags & UA_GOTOALLOWED) != 0;
546 }
547
548 int ConditionalRoomHas_UA_HASNEWMSGS(StrBuf *Target, WCTemplputParams *TP)
549 {
550         folder *Folder = (folder *)(TP->Context);
551         return (Folder->RAFlags & UA_HASNEWMSGS) != 0;
552 }
553
554 int ConditionalRoomHas_UA_ZAPPED(StrBuf *Target, WCTemplputParams *TP)
555 {
556         folder *Folder = (folder *)(TP->Context);
557         return (Folder->RAFlags & UA_ZAPPED) != 0;
558 }
559
560 int ConditionalRoomHas_UA_POSTALLOWED(StrBuf *Target, WCTemplputParams *TP)
561 {
562         folder *Folder = (folder *)(TP->Context);
563         return (Folder->RAFlags & UA_POSTALLOWED) != 0;
564 }
565
566 int ConditionalRoomHas_UA_ADMINALLOWED(StrBuf *Target, WCTemplputParams *TP)
567 {
568         folder *Folder = (folder *)(TP->Context);
569         return (Folder->RAFlags & UA_ADMINALLOWED) != 0;
570 }
571
572 int ConditionalRoomHas_UA_DELETEALLOWED(StrBuf *Target, WCTemplputParams *TP)
573 {
574         folder *Folder = (folder *)(TP->Context);
575         return (Folder->RAFlags & UA_DELETEALLOWED) != 0;
576 }
577
578
579 int ConditionalRoomIsInbox(StrBuf *Target, WCTemplputParams *TP)
580 {
581         folder *Folder = (folder *)(TP->Context);
582         return Folder->is_inbox;
583 }
584
585 void tmplput_ROOM_COLLECTIONTYPE(StrBuf *Target, WCTemplputParams *TP) 
586 {
587         folder *Folder = (folder *)(TP->Context);
588         
589         switch(Folder->view) {
590         case VIEW_CALENDAR:
591                 StrBufAppendBufPlain(Target, HKEY("vevent"), 0);
592                 break;
593         case VIEW_TASKS:
594                 StrBufAppendBufPlain(Target, HKEY("vtodo"), 0);
595                 break;
596         case VIEW_ADDRESSBOOK:
597                 StrBufAppendBufPlain(Target, HKEY("vcard"), 0);
598                 break;
599         case VIEW_NOTES:
600                 StrBufAppendBufPlain(Target, HKEY("vnotes"), 0);
601                 break;
602         case VIEW_JOURNAL:
603                 StrBufAppendBufPlain(Target, HKEY("vjournal"), 0);
604                 break;
605         }
606 }
607
608
609
610
611 int ConditionalRoomHasGroupdavContent(StrBuf *Target, WCTemplputParams *TP)
612 {
613         folder *Folder = (folder *)(TP->Context);
614
615         return ((Folder->view == VIEW_CALENDAR) || 
616                 (Folder->view == VIEW_TASKS) || 
617                 (Folder->view == VIEW_ADDRESSBOOK) ||
618                 (Folder->view == VIEW_NOTES) ||
619                 (Folder->view == VIEW_JOURNAL) );
620 }
621
622
623
624 int ConditionalFloorIsRESTSubFloor(StrBuf *Target, WCTemplputParams *TP)
625 {
626         wcsession  *WCC = WC;
627
628         /** If we have dav_depth the client just wants the _current_ room without subfloors */
629         if (WCC->Hdr->HR.dav_depth == 0)
630                 return 0;
631             
632         return 1;
633 }
634
635
636 int ConditionalRoomIsRESTSubRoom(StrBuf *Target, WCTemplputParams *TP)
637 {
638         wcsession  *WCC = WC;
639         folder     *Folder = (folder *)(TP->Context);
640         HashPos    *it;
641         StrBuf     * Dir;
642         void       *vDir;
643         long        len;
644         const char *Key;
645         int i;
646
647
648
649         if (Folder->Floor != WCC->CurrentFloor)
650                 return 0;
651
652         if (GetCount(WCC->Directory) != Folder->nRoomNameParts)
653                 return 0;
654
655         it = GetNewHashPos(WCC->Directory, 0);
656         for (i = 0; i < Folder->nRoomNameParts; i++)
657         {
658                 if (!GetNextHashPos(WCC->Directory, it, &len, &Key, &vDir) ||
659                     (vDir == NULL))
660                 {
661                         DeleteHashPos(&it);
662                         return 0;
663                 }
664                 Dir = (StrBuf*) vDir;
665                 if (strcmp(ChrPtr(Folder->RoomNameParts[i]), 
666                            ChrPtr(Dir)) != 0)
667                 {
668                         DeleteHashPos(&it);
669                         return 0;
670                 }
671         }
672         DeleteHashPos(&it);
673
674         /** If we have dav_depth the client just wants the _current_ room without subfloors */
675         if ((WCC->Hdr->HR.dav_depth == 0) &&
676             (i != Folder->nRoomNameParts))
677                 return 0;
678
679         return 1;
680 }
681
682
683 void jsonRoomFlr(void) 
684 {
685         /* Send as our own (application/json) content type */
686         hprintf("HTTP/1.1 200 OK\r\n");
687         hprintf("Content-type: application/json; charset=utf-8\r\n");
688         hprintf("Server: %s / %s\r\n", PACKAGE_STRING, ChrPtr(WC->serv_info->serv_software));
689         hprintf("Connection: close\r\n");
690         hprintf("Pragma: no-cache\r\nCache-Control: no-store\r\nExpires:-1\r\n");
691         begin_burst();
692         DoTemplate(HKEY("json_roomflr"),NULL,&NoCtx);
693         end_burst(); 
694 }
695
696
697
698 void 
699 InitModule_ROOMLIST
700 (void)
701 {
702         WebcitAddUrlHandler(HKEY("json_roomflr"), jsonRoomFlr, 0);
703
704
705         RegisterNamespace("FLOOR:ID", 0, 0, tmplput_FLOOR_ID, NULL, CTX_FLOORS);
706         RegisterNamespace("FLOOR:NAME", 0, 1, tmplput_FLOOR_NAME, NULL, CTX_FLOORS);
707         RegisterNamespace("FLOOR:NROOMS", 0, 0, tmplput_FLOOR_NROOMS, NULL, CTX_FLOORS);
708         RegisterConditional(HKEY("COND:ROOM:REST:ISSUBFLOOR"), 0, ConditionalFloorIsRESTSubFloor, CTX_FLOORS);
709
710         RegisterIterator("LFLR", 0, NULL, GetFloorListHash, NULL, NULL, CTX_FLOORS, CTX_NONE, IT_FLAG_DETECT_GROUPCHANGE);
711
712         RegisterIterator("LKRA", 0, NULL, GetRoomListHashLKRA, NULL, DeleteHash, CTX_ROOMS, CTX_NONE, IT_FLAG_DETECT_GROUPCHANGE);
713
714         RegisterNamespace("ROOM:INFO:FLOORID", 0, 1, tmplput_ROOM_FLOORID, NULL, CTX_ROOMS);
715         RegisterNamespace("ROOM:INFO:NAME", 0, 1, tmplput_ROOM_NAME, NULL, CTX_ROOMS);
716         RegisterNamespace("ROOM:INFO:PRINT_NAME", 0, 1, tmplput_ROOM_NAME, NULL, CTX_ROOMS);/// TODO!
717         RegisterNamespace("ROOM:INFO:BASENAME", 0, 1, tmplput_ROOM_BASENAME, NULL, CTX_ROOMS);
718         RegisterNamespace("ROOM:INFO:LEVELNTIMES", 1, 2, tmplput_ROOM_LEVEL_N_TIMES, NULL, CTX_ROOMS);
719
720         RegisterNamespace("ROOM:INFO:ACL", 0, 1, tmplput_ROOM_ACL, NULL, CTX_ROOMS);
721         RegisterNamespace("ROOM:INFO:QRFLAGS", 0, 1, tmplput_ROOM_QRFLAGS, NULL, CTX_ROOMS);
722         RegisterNamespace("ROOM:INFO:LISTORDER", 0, 1, tmplput_ROOM_LISTORDER, NULL, CTX_ROOMS);
723         RegisterNamespace("ROOM:INFO:VIEW", 0, 1, tmplput_ROOM_VIEW, NULL, CTX_ROOMS);
724         RegisterNamespace("ROOM:INFO:DEFVIEW", 0, 1, tmplput_ROOM_DEFVIEW, NULL, CTX_ROOMS);
725         RegisterNamespace("ROOM:INFO:LASTCHANGE", 0, 1, tmplput_ROOM_LASTCHANGE, NULL, CTX_ROOMS);
726         RegisterNamespace("ROOM:INFO:COLLECTIONTYPE", 0, 1, tmplput_ROOM_COLLECTIONTYPE, NULL, CTX_ROOMS);
727         RegisterNamespace("ROOM:INFO:FLOOR:ID", 0, 0, tmplput_ROOM_FLOOR_ID, NULL, CTX_ROOMS);
728         RegisterNamespace("ROOM:INFO:FLOOR:NAME", 0, 1, tmplput_ROOM_FLOOR_NAME, NULL, CTX_ROOMS);
729         RegisterNamespace("ROOM:INFO:FLOOR:NROOMS", 0, 0, tmplput_ROOM_FLOOR_NROOMS, NULL, CTX_ROOMS);
730
731         RegisterConditional(HKEY("COND:ROOM:REST:ISSUBROOM"), 0, ConditionalRoomIsRESTSubRoom, CTX_ROOMS);
732
733         RegisterConditional(HKEY("COND:ROOM:INFO:IS_INBOX"), 0, ConditionalRoomIsInbox, CTX_ROOMS);
734         RegisterConditional(HKEY("COND:ROOM:FLAGS:UA_KNOWN"), 0, ConditionalRoomHas_UA_KNOWN, CTX_ROOMS);
735         RegisterConditional(HKEY("COND:ROOM:FLAGS:UA_GOTOALLOWED"), 0, ConditionalRoomHas_UA_GOTOALLOWED, CTX_ROOMS);
736         RegisterConditional(HKEY("COND:ROOM:FLAGS:UA_HASNEWMSGS"), 0, ConditionalRoomHas_UA_HASNEWMSGS, CTX_ROOMS);
737         RegisterConditional(HKEY("COND:ROOM:FLAGS:UA_ZAPPED"), 0, ConditionalRoomHas_UA_ZAPPED, CTX_ROOMS);
738         RegisterConditional(HKEY("COND:ROOM:FLAGS:UA_POSTALLOWED"), 0, ConditionalRoomHas_UA_POSTALLOWED, CTX_ROOMS);
739         RegisterConditional(HKEY("COND:ROOM:FLAGS:UA_ADMINALLOWED"), 0, ConditionalRoomHas_UA_ADMINALLOWED, CTX_ROOMS);
740         RegisterConditional(HKEY("COND:ROOM:FLAGS:UA_DELETEALLOWED"), 0, ConditionalRoomHas_UA_DELETEALLOWED, CTX_ROOMS);
741         RegisterConditional(HKEY("COND:ROOM:GROUPDAV_CONTENT"), 0, ConditionalRoomHasGroupdavContent, CTX_ROOMS);
742
743
744
745         RegisterSortFunc(HKEY("byfloorroom"),
746                          NULL, 0,
747                          CompareRoomListByFloorRoomPrivFirst,
748                          CompareRoomListByFloorRoomPrivFirstRev,
749                          GroupchangeRoomListByFloorRoomPrivFirst,
750                          CTX_ROOMS);
751
752 }