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