* move some more vars from the session context to strbuf (the use of StrBufAppendTemp...
[citadel.git] / webcit / useredit.c
1 /*
2  * $Id$
3  */
4
5 #include "webcit.h"
6 #include "webserver.h"
7
8
9 /**
10  *  show a list of available users to edit them
11  *  message the header message???
12  *  preselect which user should be selected in the browser
13  */
14 void select_user_to_edit(char *message, char *preselect)
15 {
16         output_headers(1, 0, 0, 0, 1, 0);
17         do_template("edituser_select", NULL);
18         end_burst();
19 }
20
21
22 typedef struct _UserListEntry {
23         int UID;
24         int AccessLevel;
25         int nLogons;
26         int nPosts;
27
28         StrBuf *UserName;
29         StrBuf *Passvoid;
30         time_t LastLogonT;
31         /* Just available for Single users to view: */
32         unsigned int Flags;
33         int DaysTillPurge;
34 } UserListEntry;
35
36
37 UserListEntry* NewUserListOneEntry(StrBuf *SerializedUser)
38 {
39         UserListEntry *ul;
40
41         if (StrLength(SerializedUser) < 8) 
42                 return NULL;
43
44         ul = (UserListEntry*) malloc(sizeof(UserListEntry));
45         ul->UserName = NewStrBuf();
46         ul->Passvoid = NewStrBuf();
47
48         StrBufExtract_token(ul->UserName, SerializedUser, 0, '|');
49         StrBufExtract_token(ul->Passvoid, SerializedUser, 1, '|');
50         ul->Flags = (unsigned int)StrBufExtract_long(SerializedUser, 2, '|');
51         ul->nLogons = StrBufExtract_int(SerializedUser, 3, '|');
52         ul->nPosts = StrBufExtract_int(SerializedUser, 4, '|');
53         ul->AccessLevel = StrBufExtract_int(SerializedUser, 5, '|');
54         ul->UID = StrBufExtract_int(SerializedUser, 6, '|');
55         ul->LastLogonT = StrBufExtract_long(SerializedUser, 7, '|');
56         ul->DaysTillPurge = StrBufExtract_int(SerializedUser, 8, '|');
57         return ul;
58 }
59
60 void DeleteUserListEntry(void *vUserList)
61 {
62         UserListEntry *ul = (UserListEntry*) vUserList;
63         FreeStrBuf(&ul->UserName);
64         FreeStrBuf(&ul->Passvoid);
65         free(ul);
66 }
67
68 UserListEntry* NewUserListEntry(StrBuf *SerializedUserList)
69 {
70         UserListEntry *ul;
71
72         if (StrLength(SerializedUserList) < 8) 
73                 return NULL;
74
75         ul = (UserListEntry*) malloc(sizeof(UserListEntry));
76         ul->UserName = NewStrBuf();
77         ul->Passvoid = NewStrBuf();
78
79         StrBufExtract_token(ul->UserName, SerializedUserList, 0, '|');
80         ul->AccessLevel = StrBufExtract_int(SerializedUserList, 1, '|');
81         ul->UID = StrBufExtract_int(SerializedUserList, 2, '|');
82         ul->LastLogonT = StrBufExtract_long(SerializedUserList, 3, '|');
83         ul->nLogons = StrBufExtract_int(SerializedUserList, 4, '|');
84         ul->nPosts = StrBufExtract_int(SerializedUserList, 5, '|');
85         StrBufExtract_token(ul->Passvoid, SerializedUserList, 6, '|');
86         ul->Flags = 0;
87         ul->DaysTillPurge = -1;
88         return ul;
89 }
90
91 /*
92  * Sort by Username
93  */
94 int CompareUserListName(const void *vUser1, const void *vUser2)
95 {
96         UserListEntry *u1 = (UserListEntry*) vUser1;
97         UserListEntry *u2 = (UserListEntry*) vUser2;
98
99         return strcmp(ChrPtr(u1->UserName), ChrPtr(u2->UserName));
100 }
101 int CompareUserListNameRev(const void *vUser1, const void *vUser2)
102 {
103         UserListEntry *u1 = (UserListEntry*) vUser1;
104         UserListEntry *u2 = (UserListEntry*) vUser2;
105         return strcmp(ChrPtr(u2->UserName), ChrPtr(u1->UserName));
106 }
107
108 /*
109  * Sort by AccessLevel
110  */
111 int CompareAccessLevel(const void *vUser1, const void *vUser2)
112 {
113         UserListEntry *u1 = (UserListEntry*) vUser1;
114         UserListEntry *u2 = (UserListEntry*) vUser2;
115
116         return (u1->AccessLevel > u2->AccessLevel);
117 }
118 int CompareAccessLevelRev(const void *vUser1, const void *vUser2)
119 {
120         UserListEntry *u1 = (UserListEntry*) vUser1;
121         UserListEntry *u2 = (UserListEntry*) vUser2;
122
123         return (u2->AccessLevel > u1->AccessLevel);
124 }
125
126
127 /*
128  * Sort by UID
129  */
130 int CompareUID(const void *vUser1, const void *vUser2)
131 {
132         UserListEntry *u1 = (UserListEntry*) vUser1;
133         UserListEntry *u2 = (UserListEntry*) vUser2;
134
135         return (u1->UID > u2->UID);
136 }
137 int CompareUIDRev(const void *vUser1, const void *vUser2)
138 {
139         UserListEntry *u1 = (UserListEntry*) vUser1;
140         UserListEntry *u2 = (UserListEntry*) vUser2;
141
142         return (u2->UID > u1->UID);
143 }
144
145 /*
146  * Sort By Date /// TODO!
147  */
148 int CompareLastLogon(const void *vUser1, const void *vUser2)
149 {
150         UserListEntry *u1 = (UserListEntry*) vUser1;
151         UserListEntry *u2 = (UserListEntry*) vUser2;
152
153         return (u1->LastLogonT > u2->LastLogonT);
154 }
155 int CompareLastLogonRev(const void *vUser1, const void *vUser2)
156 {
157         UserListEntry *u1 = (UserListEntry*) vUser1;
158         UserListEntry *u2 = (UserListEntry*) vUser2;
159
160         return (u2->LastLogonT > u1->LastLogonT);
161 }
162
163 /*
164  * Sort By Number of Logons
165  */
166 int ComparenLogons(const void *vUser1, const void *vUser2)
167 {
168         UserListEntry *u1 = (UserListEntry*) vUser1;
169         UserListEntry *u2 = (UserListEntry*) vUser2;
170
171         return (u1->nLogons > u2->nLogons);
172 }
173 int ComparenLogonsRev(const void *vUser1, const void *vUser2)
174 {
175         UserListEntry *u1 = (UserListEntry*) vUser1;
176         UserListEntry *u2 = (UserListEntry*) vUser2;
177
178         return (u2->nLogons > u1->nLogons);
179 }
180
181 /*
182  * Sort By Number of Posts
183  */
184 int ComparenPosts(const void *vUser1, const void *vUser2)
185 {
186         UserListEntry *u1 = (UserListEntry*) vUser1;
187         UserListEntry *u2 = (UserListEntry*) vUser2;
188
189         return (u1->nPosts > u2->nPosts);
190 }
191 int ComparenPostsRev(const void *vUser1, const void *vUser2)
192 {
193         UserListEntry *u1 = (UserListEntry*) vUser1;
194         UserListEntry *u2 = (UserListEntry*) vUser2;
195
196         return (u2->nPosts > u1->nPosts);
197 }
198
199
200 HashList *iterate_load_userlist(StrBuf *Target, WCTemplputParams *TP)
201 {
202         HashList *Hash;
203         char buf[SIZ];
204         StrBuf *Buf;
205         UserListEntry* ul;
206         char nnn[64];
207         int nUsed;
208         int Order;
209         int len;
210         
211         serv_puts("LIST");
212         serv_getln(buf, sizeof buf);
213         if (buf[0] == '1') {
214                 Hash = NewHash(1, NULL);
215
216                 Buf = NewStrBuf();
217                 while ((len = StrBuf_ServGetln(Buf),
218                         strcmp(ChrPtr(Buf), "000"))) {
219                         ul = NewUserListEntry(Buf);
220                         if (ul == NULL)
221                                 continue;
222                         nUsed = GetCount(Hash);
223                         nUsed = snprintf(nnn, sizeof(nnn), "%d", nUsed+1);
224                         Put(Hash, nnn, nUsed, ul, DeleteUserListEntry); 
225                 }
226                 FreeStrBuf(&Buf);
227                 Order = ibstr("SortOrder");
228                 switch (ibstr("SortBy")){
229                 case 1: /*NAME*/
230                         SortByPayload(Hash, (Order)? 
231                                       CompareUserListName:
232                                       CompareUserListNameRev);
233                         break;
234                 case 2: /*AccessLevel*/
235                         SortByPayload(Hash, (Order)? 
236                                       CompareAccessLevel:
237                                       CompareAccessLevelRev);
238                         break;
239                 case 3: /*nLogons*/
240                         SortByPayload(Hash, (Order)? 
241                                       ComparenLogons:
242                                       ComparenLogonsRev);
243                         break;
244                 case 4: /*UID*/
245                         SortByPayload(Hash, (Order)? 
246                                       CompareUID:
247                                       CompareUIDRev);
248                         break;
249                 case 5: /*LastLogon*/
250                         SortByPayload(Hash, (Order)? 
251                                       CompareLastLogon:
252                                       CompareLastLogonRev);
253                         break;
254                 case 6: /* nLogons */
255                         SortByPayload(Hash, (Order)? 
256                                       ComparenLogons:
257                                       ComparenLogonsRev);
258                         break;
259                 case 7: /* Posts */
260                         SortByPayload(Hash, (Order)? 
261                                       ComparenPosts:
262                                       ComparenPostsRev);
263                         break;
264                 }
265                 return Hash;
266         }
267         return NULL;
268 }
269
270
271 void tmplput_USERLIST_UserName(StrBuf *Target, WCTemplputParams *TP)
272 {
273         UserListEntry *ul = (UserListEntry*) CTX;
274         StrBufAppendTemplate(Target, TP, ul->UserName, 0);
275 }
276
277 void tmplput_USERLIST_AccessLevelNo(StrBuf *Target, WCTemplputParams *TP)
278 {
279         UserListEntry *ul = (UserListEntry*) CTX;
280
281         StrBufAppendPrintf(Target, "%d", ul->AccessLevel, 0);
282 }
283
284 void tmplput_USERLIST_AccessLevelStr(StrBuf *Target, WCTemplputParams *TP)
285 {
286         UserListEntry *ul = (UserListEntry*) CTX;
287         
288         StrBufAppendBufPlain(Target, _(axdefs[ul->AccessLevel]), -1, 0);
289 }
290
291 void tmplput_USERLIST_UID(StrBuf *Target, WCTemplputParams *TP)
292 {
293         UserListEntry *ul = (UserListEntry*) CTX;
294
295         StrBufAppendPrintf(Target, "%d", ul->UID, 0);
296 }
297
298 void tmplput_USERLIST_LastLogonNo(StrBuf *Target, WCTemplputParams *TP)
299 {
300         UserListEntry *ul = (UserListEntry*) CTX;
301
302         StrBufAppendPrintf(Target,"%ld", ul->LastLogonT, 0);
303 }
304 void tmplput_USERLIST_LastLogonStr(StrBuf *Target, WCTemplputParams *TP)
305 {
306         UserListEntry *ul = (UserListEntry*) CTX;
307         StrEscAppend(Target, NULL, asctime(localtime(&ul->LastLogonT)), 0, 0);
308 }
309
310 void tmplput_USERLIST_nLogons(StrBuf *Target, WCTemplputParams *TP)
311 {
312         UserListEntry *ul = (UserListEntry*) CTX;
313
314         StrBufAppendPrintf(Target, "%d", ul->nLogons, 0);
315 }
316
317 void tmplput_USERLIST_nPosts(StrBuf *Target, WCTemplputParams *TP)
318 {
319         UserListEntry *ul = (UserListEntry*) CTX;
320
321         StrBufAppendPrintf(Target, "%d", ul->nPosts, 0);
322 }
323
324 void tmplput_USERLIST_Flags(StrBuf *Target, WCTemplputParams *TP)
325 {
326         UserListEntry *ul = (UserListEntry*) CTX;
327
328         StrBufAppendPrintf(Target, "%d", ul->Flags, 0);
329 }
330
331 void tmplput_USERLIST_DaysTillPurge(StrBuf *Target, WCTemplputParams *TP)
332 {
333         UserListEntry *ul = (UserListEntry*) CTX;
334
335         StrBufAppendPrintf(Target, "%d", ul->DaysTillPurge, 0);
336 }
337
338 int ConditionalUser(StrBuf *Target, WCTemplputParams *TP)
339 {
340         UserListEntry *ul = (UserListEntry*) CTX;
341         if (havebstr("usernum")) {
342                 return ibstr("usernum") == ul->UID;
343         }
344         else if (havebstr("username")) {
345                 return strcmp(bstr("username"), ChrPtr(ul->UserName)) == 0;
346         }
347         else 
348                 return 0;
349 }
350
351 int ConditionalFlagINetEmail(StrBuf *Target, WCTemplputParams *TP)
352 {
353         UserListEntry *ul = (UserListEntry*) CTX;
354         return (ul->Flags & US_INTERNET) != 0;
355 }
356
357 int ConditionalUserAccess(StrBuf *Target, WCTemplputParams *TP)
358 {
359         UserListEntry *ul = (UserListEntry*) CTX;
360
361         if (TP->Tokens->Params[3]->Type == TYPE_LONG)
362                 return (TP->Tokens->Params[3]->lvalue == ul->AccessLevel);
363         else
364                 return 0;
365 }
366
367 /*
368  *  Locate the message number of a user's vCard in the current room
369  *  Returns the message id of his vcard
370  */
371 long locate_user_vcard_in_this_room() {
372         char buf[SIZ];
373         long vcard_msgnum = (-1L);
374         char content_type[SIZ];
375         char partnum[SIZ];
376         int already_tried_creating_one = 0;
377
378         struct stuff_t {
379                 struct stuff_t *next;
380                 long msgnum;
381         };
382
383         struct stuff_t *stuff = NULL;
384         struct stuff_t *ptr;
385
386 TRYAGAIN:
387         /** Search for the user's vCard */
388         serv_puts("MSGS ALL");
389         serv_getln(buf, sizeof buf);
390         if (buf[0] == '1') while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
391                 ptr = malloc(sizeof(struct stuff_t));
392                 ptr->msgnum = atol(buf);
393                 ptr->next = stuff;
394                 stuff = ptr;
395         }
396
397         /** Iterate through the message list looking for vCards */
398         while (stuff != NULL) {
399                 serv_printf("MSG0 %ld|2", stuff->msgnum);
400                 serv_getln(buf, sizeof buf);
401                 if (buf[0]=='1') {
402                         while(serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
403                                 if (!strncasecmp(buf, "part=", 5)) {
404                                         extract_token(partnum, &buf[5], 2, '|', sizeof partnum);
405                                         extract_token(content_type, &buf[5], 4, '|', sizeof content_type);
406                                         if (  (!strcasecmp(content_type, "text/x-vcard"))
407                                            || (!strcasecmp(content_type, "text/vcard")) ) {
408                                                 vcard_msgnum = stuff->msgnum;
409                                         }
410                                 }
411                         }
412                 }
413
414                 ptr = stuff->next;
415                 free(stuff);
416                 stuff = ptr;
417         }
418
419         /** If there's no vcard, create one */
420         if ((vcard_msgnum < 0) && (already_tried_creating_one == 0)) {
421                 already_tried_creating_one = 1;
422                 serv_puts("ENT0 1|||4");
423                 serv_getln(buf, sizeof buf);
424                 if (buf[0] == '4') {
425                         serv_puts("Content-type: text/x-vcard");
426                         serv_puts("");
427                         serv_puts("begin:vcard");
428                         serv_puts("end:vcard");
429                         serv_puts("000");
430                 }
431                 goto TRYAGAIN;
432         }
433
434         return(vcard_msgnum);
435 }
436
437
438 /**
439  *  Display the form for editing a user's address book entry
440  *  username the name of the user
441  *  usernum the citadel-uid of the user
442  */
443 void display_edit_address_book_entry(char *username, long usernum) {
444         char roomname[SIZ];
445         char buf[SIZ];
446         char error_message[SIZ];
447         long vcard_msgnum = (-1L);
448
449         /** Locate the user's config room, creating it if necessary */
450         sprintf(roomname, "%010ld.%s", usernum, USERCONFIGROOM);
451         serv_printf("GOTO %s||1", roomname);
452         serv_getln(buf, sizeof buf);
453         if (buf[0] != '2') {
454                 serv_printf("CRE8 1|%s|5|||1|", roomname);
455                 serv_getln(buf, sizeof buf);
456                 serv_printf("GOTO %s||1", roomname);
457                 serv_getln(buf, sizeof buf);
458                 if (buf[0] != '2') {
459                         sprintf(error_message,
460                                 "<img src=\"static/error.gif\" align=center>"
461                                 "%s<br /><br />\n", &buf[4]);
462                         select_user_to_edit(error_message, username);
463                         return;
464                 }
465         }
466
467         vcard_msgnum = locate_user_vcard_in_this_room();
468
469         if (vcard_msgnum < 0) {
470                 sprintf(error_message,
471                         "<img src=\"static/error.gif\" align=center>%s<br /><br />\n",
472                         _("An error occurred while trying to create or edit this address book entry.")
473                 );
474                 select_user_to_edit(error_message, username);
475                 return;
476         }
477
478         do_edit_vcard(vcard_msgnum, "1", "select_user_to_edit", roomname);
479 }
480
481
482 void display_edituser(char *supplied_username, int is_new) {
483         UserListEntry* UL;
484         StrBuf *Buf;
485         char error_message[1024];
486         char MajorStatus;
487         char username[256];
488
489         if (supplied_username != NULL) {
490                 safestrncpy(username, supplied_username, sizeof username);
491         }
492         else {
493                 safestrncpy(username, bstr("username"), sizeof username);
494         }
495
496         Buf = NewStrBuf();
497         serv_printf("AGUP %s", username);
498         StrBuf_ServGetln(Buf);
499         MajorStatus = ChrPtr(Buf)[0];
500         StrBufCutLeft(Buf, 4);
501         if (MajorStatus != '2') {
502                 /*TODO ImportantMessage */
503                 sprintf(error_message,
504                         "<img src=\"static/error.gif\" align=center>"
505                         "%s<br /><br />\n", ChrPtr(Buf));
506                 select_user_to_edit(error_message, username);
507                 FreeStrBuf(&Buf);
508                 return;
509         }
510         else {
511                 UL = NewUserListOneEntry(Buf);
512                 if (havebstr("edit_abe_button")) {
513                         display_edit_address_book_entry(username, UL->UID);
514                 }
515                 else if (havebstr("delete_button")) {
516                         delete_user(username);
517                 }
518                 else {
519                         WCTemplputParams SubTP;
520                         memset(&SubTP, 0, sizeof(WCTemplputParams));
521                         SubTP.ContextType = CTX_USERLIST;
522                         SubTP.Context = UL;
523                         output_headers(1, 0, 0, 0, 1, 0);
524                         DoTemplate(HKEY("userlist_detailview"), NULL, &SubTP);
525                         end_burst();
526                 }
527                 DeleteUserListEntry(UL);
528                 
529         }
530         FreeStrBuf(&Buf);
531 }
532
533 /**
534  *  do the backend operation of the user edit on the server
535  */
536 void edituser(void) {
537         char message[SIZ];
538         char buf[SIZ];
539         int is_new = 0;
540         unsigned int flags = 0;
541         char *username;
542
543         is_new = ibstr("is_new");
544         safestrncpy(message, "", sizeof message);
545         username = bstr("username");
546
547         if (!havebstr("ok_button")) {
548                 safestrncpy(message, _("Changes were not saved."), sizeof message);
549         }
550         
551         else {
552                 flags = ibstr("flags");
553                 if (yesbstr("inetmail")) {
554                         flags |= US_INTERNET;
555                 }
556                 else {
557                         flags &= ~US_INTERNET ;
558                 }
559
560                 if ((havebstr("newname")) && (strcasecmp(bstr("username"), bstr("newname")))) {
561                         serv_printf("RENU %s|%s", bstr("username"), bstr("newname"));
562                         serv_getln(buf, sizeof buf);
563                         if (buf[0] != '2') {
564                                 sprintf(&message[strlen(message)],
565                                         "<img src=\"static/error.gif\" align=center>"
566                                         "%s<br /><br />\n", &buf[4]);
567                         }
568                         else {
569                                 username = bstr("newname");
570                         }
571                 }
572
573                 serv_printf("ASUP %s|%s|%d|%s|%s|%s|%s|%s|%s|",
574                         username,
575                         bstr("password"),
576                         flags,
577                         bstr("timescalled"),
578                         bstr("msgsposted"),
579                         bstr("axlevel"),
580                         bstr("usernum"),
581                         bstr("lastcall"),
582                         bstr("purgedays")
583                 );
584                 serv_getln(buf, sizeof buf);
585                 if (buf[0] != '2') {
586                         sprintf(&message[strlen(message)],
587                                 "<img src=\"static/error.gif\" align=center>"
588                                 "%s<br /><br />\n", &buf[4]);
589                 }
590         }
591
592         /**
593          * If we are in the middle of creating a new user, move on to
594          * the vCard edit screen.
595          */
596         if (is_new) {
597                 display_edit_address_book_entry(username, lbstr("usernum") );
598         }
599         else {
600                 select_user_to_edit(message, username);
601         }
602 }
603
604 /*
605  *  burge a user 
606  *  username the name of the user to remove
607  */
608 void delete_user(char *username) {
609         char buf[SIZ];
610         char message[SIZ];
611
612         serv_printf("ASUP %s|0|0|0|0|0|", username);
613         serv_getln(buf, sizeof buf);
614         if (buf[0] != '2') {
615                 sprintf(message,
616                         "<img src=\"static/error.gif\" align=center>"
617                         "%s<br /><br />\n", &buf[4]);
618         }
619         else {
620                 safestrncpy(message, "", sizeof message);
621         }
622         select_user_to_edit(message, bstr("username"));
623 }
624                 
625
626
627 /**
628  *  create a new user
629  * take the web environment username and create it on the citadel server
630  */
631 void create_user(void) {
632         char buf[SIZ];
633         char error_message[SIZ];
634         char username[SIZ];
635
636         safestrncpy(username, bstr("username"), sizeof username);
637
638         serv_printf("CREU %s", username);
639         serv_getln(buf, sizeof buf);
640
641         if (buf[0] == '2') {
642                 sprintf(WC->ImportantMessage, _("A new user has been created."));
643                 display_edituser(username, 1);
644         }
645         else if (!strncmp(buf, "570", 3)) {
646                 sprintf(error_message,
647                         "<img src=\"static/error.gif\" align=center>"
648                         "%s<br /><br />\n",
649                         _("You are attempting to create a new user from within Citadel "
650                         "while running in host based authentication mode.  In this mode, "
651                         "you must create new users on the host system, not within Citadel.")
652                 );
653                 select_user_to_edit(error_message, NULL);
654         }
655         else {
656                 sprintf(error_message,
657                         "<img src=\"static/error.gif\" align=center>"
658                         "%s<br /><br />\n", &buf[4]);
659                 select_user_to_edit(error_message, NULL);
660         }
661
662 }
663
664
665 void _select_user_to_edit(void){select_user_to_edit(NULL, NULL);}
666 void _display_edituser(void) {display_edituser(NULL, 0);}
667
668 void 
669 InitModule_USEREDIT
670 (void)
671 {
672         WebcitAddUrlHandler(HKEY("select_user_to_edit"), _select_user_to_edit, 0);
673         WebcitAddUrlHandler(HKEY("display_edituser"), _display_edituser, 0);
674         WebcitAddUrlHandler(HKEY("edituser"), edituser, 0);
675         WebcitAddUrlHandler(HKEY("create_user"), create_user, 0);
676
677         RegisterNamespace("USERLIST:USERNAME",      0, 1, tmplput_USERLIST_UserName, CTX_USERLIST);
678         RegisterNamespace("USERLIST:ACCLVLNO",      0, 0, tmplput_USERLIST_AccessLevelNo, CTX_USERLIST);
679         RegisterNamespace("USERLIST:ACCLVLSTR",     0, 0, tmplput_USERLIST_AccessLevelStr, CTX_USERLIST);
680         RegisterNamespace("USERLIST:UID",           0, 0, tmplput_USERLIST_UID, CTX_USERLIST);
681         RegisterNamespace("USERLIST:LASTLOGON:STR", 0, 0, tmplput_USERLIST_LastLogonStr, CTX_USERLIST);
682         RegisterNamespace("USERLIST:LASTLOGON:NO",  0, 0, tmplput_USERLIST_LastLogonNo, CTX_USERLIST);
683         RegisterNamespace("USERLIST:NLOGONS",       0, 0, tmplput_USERLIST_nLogons, CTX_USERLIST);
684         RegisterNamespace("USERLIST:NPOSTS",        0, 0, tmplput_USERLIST_nPosts, CTX_USERLIST);
685                                                     
686         RegisterNamespace("USERLIST:FLAGS",         0, 0, tmplput_USERLIST_Flags, CTX_USERLIST);
687         RegisterNamespace("USERLIST:DAYSTILLPURGE", 0, 0, tmplput_USERLIST_DaysTillPurge, CTX_USERLIST);
688
689         RegisterConditional(HKEY("COND:USERNAME"),  0,    ConditionalUser, CTX_USERLIST);
690         RegisterConditional(HKEY("COND:USERACCESS"), 0,   ConditionalUserAccess, CTX_USERLIST);
691         RegisterConditional(HKEY("COND:USERLIST:FLAG:USE_INTERNET"), 0, ConditionalFlagINetEmail, CTX_USERLIST);
692
693         RegisterIterator("USERLIST", 0, NULL, iterate_load_userlist, NULL, DeleteHash, CTX_USERLIST, CTX_NONE, IT_NOFLAG);
694 }