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