1 /*****************************************************************************/
2 /* */
3 /* PASSWORD.CC */
4 /* */
5 /* (C) 1994-95 Ullrich von Bassewitz */
6 /* Zwehrenbuehlstrasse 33 */
7 /* D-72070 Tuebingen */
8 /* EMail: uz@ibb.schwaben.com */
9 /* */
10 /*****************************************************************************/
11
12
13
14 // $Id$
15 //
16 // $Log$
17 //
18 //
19
20
21
22 #include "password.h"
23 #include "streamid.h"
24 #include "msgid.h"
25 #include "listbox.h"
26 #include "menue.h"
27 #include "crcstrm.h"
28 #include "memstrm.h"
29 #include "progutil.h"
30 #include "stdmenue.h"
31 #include "stdmsg.h"
32 #include "strcvt.h"
33 #include "menuedit.h"
34
35
36
37 /*****************************************************************************/
38 /* Message constants */
39 /*****************************************************************************/
40
41
42
43 static const u16 msCannotOpenPasswordFile = MSGBASE_PASSWORD + 0;
44 static const u16 msCannotReadPasswordFile = MSGBASE_PASSWORD + 1;
45 static const u16 msCannotWritePasswordFile = MSGBASE_PASSWORD + 2;
46 static const u16 msUserIDEmpty = MSGBASE_PASSWORD + 3;
47 static const u16 msUserNameEmpty = MSGBASE_PASSWORD + 4;
48 static const u16 msPasswordEmpty = MSGBASE_PASSWORD + 5;
49 static const u16 msUserIDExists = MSGBASE_PASSWORD + 6;
50 static const u16 msInvalidLogin = MSGBASE_PASSWORD + 7;
51
52
53
54 /*****************************************************************************/
55 /* Global data */
56 /*****************************************************************************/
57
58
59
60 static String _CUN;
61 extern const String& CUN = _CUN;
62 static String _CUID;
63 extern const String& CUID = _CUID;
64 static u32 _CPL;
65 extern const u32& CPL = _CPL;
66
67
68
69 /*****************************************************************************/
70 /* Explicit template instantiation */
71 /*****************************************************************************/
72
73
74
75 #ifdef EXPLICIT_TEMPLATES
76 template class Collection<class PasswordEntry>;
77 template class SortedCollection<class PasswordEntry, String>;
78 template class ListBox<class PasswordEntry>;
79 #endif
80
81
82
83 /*****************************************************************************/
84 /* class PasswordEntry */
85 /*****************************************************************************/
86
87
88
89 class PasswordEntry: public Streamable {
90
91 friend class PasswordColl;
92 friend class PasswordListBox;
93 friend void Login (const String&, const String&);
94 friend inline void EntryEditor (class PasswordEntry*, int&, int&);
95 // EntryEditor is not inline but static, but this way gcc don't displays
96 // a warning
97
98 protected:
99 String UserName;
100 String UserID;
101 String Password;
102 u32 Level;
103
104 void Crypt ();
105 void Decrypt ();
106
107 PasswordEntry (StreamableInit);
108
109 public:
110 PasswordEntry ();
111 PasswordEntry (const String& Name, const String& ID, const String& PW, u32 aLevel);
112
113 // Derived from class Streamable
114 virtual void Load (Stream&);
115 virtual void Store (Stream&) const;
116 virtual u16 StreamableID () const;
117 static Streamable* Build ();
118
119 };
120
121
122
PasswordEntry()123 PasswordEntry::PasswordEntry ():
124 Level (0)
125 {
126 }
127
128
129
PasswordEntry(StreamableInit)130 inline PasswordEntry::PasswordEntry (StreamableInit):
131 UserName (Empty),
132 UserID (Empty),
133 Password (Empty)
134 {
135 }
136
137
138
PasswordEntry(const String & Name,const String & ID,const String & PW,u32 aLevel)139 PasswordEntry::PasswordEntry (const String& Name, const String& ID,
140 const String& PW, u32 aLevel):
141 UserName (Name),
142 UserID (ID),
143 Password (PW),
144 Level (aLevel)
145 {
146 }
147
148
149
Crypt()150 void PasswordEntry::Crypt ()
151 {
152 UserName.Crypt ();
153 UserID.Crypt ();
154 Password.Crypt ();
155 Level ^= Password.Len ();
156 }
157
158
159
Decrypt()160 void PasswordEntry::Decrypt ()
161 {
162 UserName.Decrypt ();
163 UserID.Decrypt ();
164 Password.Decrypt ();
165 Level ^= Password.Len ();
166 }
167
168
169
Load(Stream & S)170 void PasswordEntry::Load (Stream& S)
171 {
172 S >> UserName >> UserID >> Password >> Level;
173 Decrypt ();
174 }
175
176
177
Store(Stream & S) const178 void PasswordEntry::Store (Stream& S) const
179 {
180 // Because we crypt and later decrypt *this, we cast away constness...
181 ((PasswordEntry*) this)->Crypt ();
182 S << UserName << UserID << Password << Level;
183 ((PasswordEntry*) this)->Decrypt ();
184 }
185
186
187
StreamableID() const188 u16 PasswordEntry::StreamableID () const
189 {
190 return ID_PasswordEntry;
191 }
192
193
194
Build()195 Streamable* PasswordEntry::Build ()
196 {
197 return new PasswordEntry (Empty);
198 }
199
200
201
202 /*****************************************************************************/
203 /* class PWLogEntry */
204 /*****************************************************************************/
205
206
207
208 class PWLogEntry: public Streamable {
209
210 public:
211 enum _What { Login, Logout, Fail };
212
213 protected:
214 String UserName;
215 String UserID;
216 u32 Level;
217 Time Now;
218 _What What;
219
220 void Crypt ();
221 void Decrypt ();
222
223 PWLogEntry (StreamableInit);
224 // Build constructor
225
226 public:
227 PWLogEntry (const String& Name, const String& ID, u32 aLevel, _What Action);
228
229 // Derived from class Streamable
230 virtual void Load (Stream&);
231 virtual void Store (Stream&) const;
232 virtual u16 StreamableID () const;
233 static Streamable* Build ();
234
235 };
236
237
238
PWLogEntry(const String & Name,const String & ID,u32 aLevel,_What Action)239 PWLogEntry::PWLogEntry (const String& Name, const String& ID,
240 u32 aLevel, _What Action):
241 UserName (Name),
242 UserID (ID),
243 Level (aLevel),
244 What (Action)
245 {
246 }
247
248
249
PWLogEntry(StreamableInit)250 inline PWLogEntry::PWLogEntry (StreamableInit):
251 UserName (Empty),
252 UserID (Empty),
253 Now (Empty)
254 {
255 }
256
257
258
Crypt()259 void PWLogEntry::Crypt ()
260 {
261 UserName.Crypt ();
262 UserID.Crypt ();
263 Level ^= UserID.Len ();
264 }
265
266
267
Decrypt()268 void PWLogEntry::Decrypt ()
269 {
270 UserName.Decrypt ();
271 UserID.Decrypt ();
272 Level ^= UserID.Len ();
273 }
274
275
276
Load(Stream & S)277 void PWLogEntry::Load (Stream& S)
278 {
279 u32 Tmp;
280 S >> UserName >> UserID >> Level >> Now >> Tmp;
281 What = (_What) Tmp;
282 Decrypt ();
283 }
284
285
286
Store(Stream & S) const287 void PWLogEntry::Store (Stream& S) const
288 {
289 // Because we crypt and later decrypt *this, we cast away constness...
290 ((PWLogEntry*) this)->Crypt ();
291 u32 Tmp = What;
292 S << UserName << UserID << Level << Now << Tmp;
293 ((PWLogEntry*) this)->Decrypt ();
294 }
295
296
297
StreamableID() const298 u16 PWLogEntry::StreamableID () const
299 {
300 return ID_PWLogEntry;
301 }
302
303
304
Build()305 Streamable* PWLogEntry::Build ()
306 {
307 return new PWLogEntry (Empty);
308 }
309
310
311
312 /*****************************************************************************/
313 /* class PasswordColl */
314 /*****************************************************************************/
315
316
317
318 class PasswordColl: public SortedCollection<PasswordEntry, String> {
319
320 protected:
321 // Derived from class Collection
322 virtual void* GetItem (Stream& S);
323 virtual void PutItem (Stream& S, void* Item) const;
324
325 // Derived from class SortedCollection
326 virtual int Compare (const String* Key1, const String* Key2);
327 virtual const String* KeyOf (const PasswordEntry* Item);
328
329 PasswordColl (StreamableInit);
330 // Build constructor
331
332
333 public:
334 PasswordColl ();
335
336 // Derived from class Streamable
337 virtual u16 StreamableID () const;
338 static Streamable* Build ();
339
340 u32 GetLevel (const String& ID, const String& PW);
341 // Search for ID in the collection and compare the password. Return 0 if
342 // the ID is not found or the password does not match, return the user
343 // level otherwise
344
345 int UserIDExists (PasswordEntry* Entry);
346 // Check if the user id in Entry exists already in the collection
347
348 PasswordEntry* Extract (int Index);
349 // Get the entry from the collection and return it. Delete it from the
350 // collection
351 };
352
353
354
PasswordColl()355 PasswordColl::PasswordColl ():
356 SortedCollection<PasswordEntry, String> (50, 10)
357 {
358 ShouldDelete = 1;
359 }
360
361
362
PasswordColl(StreamableInit)363 PasswordColl::PasswordColl (StreamableInit):
364 SortedCollection<PasswordEntry, String> (Empty)
365 // Build constructor
366 {
367 }
368
369
370
Compare(const String * Key1,const String * Key2)371 int PasswordColl::Compare (const String* Key1, const String* Key2)
372 {
373 return ::Compare (*Key1, *Key2);
374 }
375
376
377
KeyOf(const PasswordEntry * Item)378 const String* PasswordColl::KeyOf (const PasswordEntry* Item)
379 {
380 return &Item->UserID;
381 }
382
383
384
StreamableID() const385 u16 PasswordColl::StreamableID () const
386 {
387 return ID_PasswordColl;
388 }
389
390
391
Build()392 Streamable* PasswordColl::Build ()
393 {
394 return new PasswordColl (Empty);
395 }
396
397
398
GetItem(Stream & S)399 void* PasswordColl::GetItem (Stream& S)
400 {
401 return (void*) S.Get ();
402 }
403
404
405
PutItem(Stream & S,void * Item) const406 void PasswordColl::PutItem (Stream& S, void* Item) const
407 {
408 S.Put ((PasswordEntry*) Item);
409 }
410
411
412
GetLevel(const String & ID,const String & PW)413 u32 PasswordColl::GetLevel (const String& ID, const String& PW)
414 {
415 int Index;
416 if (Search (&ID, Index) == 0) {
417 // Not found, level is zero
418 return 0;
419 }
420 PasswordEntry* Entry = At (Index);
421 return (Entry->Password == PW) ? Entry->Level : 0;
422 }
423
424
425
UserIDExists(PasswordEntry * Entry)426 int PasswordColl::UserIDExists (PasswordEntry* Entry)
427 // Check if the user id in Entry exists already in the collection
428 {
429 int Index;
430 return (Search (&Entry->UserID, Index));
431 }
432
433
434
Extract(int Index)435 PasswordEntry* PasswordColl::Extract (int Index)
436 // Get the entry from the collection and return it. Delete it from the
437 // collection
438 {
439 // Remember old ShouldDelete value and reset it
440 int OldShouldDelete = ShouldDelete;
441 ShouldDelete = 0;
442
443 // Get the entry
444 PasswordEntry* Entry = At (Index);
445
446 // Delete it from the collection
447 AtDelete (Index);
448
449 // Reset ShouldDelete value
450 ShouldDelete = OldShouldDelete;
451
452 // Return the extracted PasswordEntry
453 return Entry;
454 }
455
456
457
458 /*****************************************************************************/
459 /* class PasswordListBox */
460 /*****************************************************************************/
461
462
463
464 class PasswordListBox: public ListBox<PasswordEntry> {
465
466 protected:
467 virtual void Print (int Index, int X, int Y, u16 Attr);
468 // Display one of the listbox entries
469
470 public:
471 PasswordListBox (const String& aItemText, i16 aID, const Point& aSize,
472 WindowItem* NextItem);
473 };
474
475
476
PasswordListBox(const String & aItemText,i16 aID,const Point & aSize,WindowItem * NextItem)477 inline PasswordListBox::PasswordListBox (const String& aItemText,
478 i16 aID,
479 const Point& aSize,
480 WindowItem* NextItem):
481 ListBox<PasswordEntry> (aItemText, aID, aSize, atEditNormal,
482 atEditBar, atEditHigh, NextItem)
483 {
484 }
485
486
487
Print(int Index,int X,int Y,u16 Attr)488 void PasswordListBox::Print (int Index, int X, int Y, u16 Attr)
489 // Display one of the listbox entries
490 {
491 // Zeiger auf den Eintrag holen
492 PasswordEntry* E = Coll->At (Index);
493
494 // Strings bauen
495 String Password = E->Password;
496 String UserID = E->UserID;
497 String UserName = E->UserName;
498
499 String Line (" ");
500 Line += UserID.Trunc (14).Pad (String::Right, 17);
501 Line += UserName.Trunc (30).Pad (String::Right, 34);
502 Line += Password.Pad (String::Right, 18);
503 Line += U32Str (E->Level).Pad (String::Left, 4);
504
505 Owner->Write (X, Y, Line.Pad (String::Right, Size.X), Attr);
506 }
507
508
509
510 /*****************************************************************************/
511 /* Registering the classes */
512 /*****************************************************************************/
513
514
515
516 LINK (PasswordEntry, ID_PasswordEntry);
517 LINK (PWLogEntry, ID_PWLogEntry);
518 LINK (PasswordColl, ID_PasswordColl);
519
520
521
522 /*****************************************************************************/
523 /* PasswordEditor */
524 /*****************************************************************************/
525
526
527
EntryEditor(PasswordEntry * E,int & Abort,int & Changed)528 inline void EntryEditor (PasswordEntry* E, int& Abort, int& Changed)
529 // Allow editing of one password entry
530 {
531 // ID's of the menue items
532 const int miUserID = 1;
533 const int miUserName = 2;
534 const int miPassword = 3;
535 const int miLevel = 4;
536
537 // Remember the crc of the entry
538 u32 OldCRC = GetCRC (E);
539
540 // Store the old data in a memory stream
541 MemoryStream MS (256);
542 MS << *E;
543
544 // Load the editor window and register all accel keys
545 Menue* M = (Menue*) LoadResource ("PASSWORD.EntryEditwindow");
546 M->RegisterItemKeys ();
547
548 // Set the menue entries
549 M->SetStringValue (miUserID, E->UserID);
550 M->SetStringValue (miUserName, E->UserName);
551 M->SetStringValue (miPassword, E->Password);
552 M->SetLongValue (miLevel, E->Level);
553
554 // Set a new status line and activate the window
555 PushStatusLine (siAbort | siAccept | siUpDnCR_Select);
556 M->Activate ();
557
558 // Allow editing
559 int Done = 0;
560 Changed = 0;
561 while (!Done) {
562
563 switch (M->GetChoice ()) {
564
565 case 0:
566 // Accept or abort
567 if (M->GetAbortKey () == vkAbort) {
568 // Editing aborted
569 if (GetCRC (E) != OldCRC) {
570 // Entry has been changed
571 if (AskDiscardChanges () == 2) {
572 MS >> *E;
573 Changed = 0;
574 Abort = 1;
575 Done = 1;
576 }
577 } else {
578 Changed = 0;
579 Abort = 1;
580 Done = 1;
581 }
582 } else {
583 // Accept, check if all entries are valid
584 if (E->UserID.IsEmpty ()) {
585 ErrorMsg (msUserIDEmpty);
586 } else if (E->UserName.IsEmpty ()) {
587 ErrorMsg (msUserNameEmpty);
588 } else if (E->Password.IsEmpty ()) {
589 ErrorMsg (msPasswordEmpty);
590 } else {
591 // Entry is valid
592 Changed = (GetCRC (E) != OldCRC);
593 Abort = 0;
594 Done = 1;
595 }
596 }
597 break;
598
599 case miUserID:
600 E->UserID = M->GetStringValue (miUserID);
601 break;
602
603 case miUserName:
604 E->UserName = M->GetStringValue (miUserName);
605 break;
606
607 case miPassword:
608 E->Password = M->GetStringValue (miPassword);
609 break;
610
611 case miLevel:
612 E->Level = M->GetLongValue (miLevel);
613 break;
614
615
616 }
617
618 }
619
620 // Delete window and status line
621 M->UnregisterItemKeys ();
622 delete M;
623 PopStatusLine ();
624
625 }
626
627
628
PasswordEditor(PasswordColl * PC,int & Abort,int & Changed)629 static void PasswordEditor (PasswordColl* PC, int& Abort, int& Changed)
630 // Allow editing of the given password collection
631 {
632 // Create a memory stream and store the old password data
633 MemoryStream MS;
634 MS << *PC;
635
636 // Store the crc of the collection to determine if any data has changed
637 u32 OldCRC = GetCRC (PC);
638
639 // Load the editor window and adjust its size to the screen size
640 Menue* M = (Menue*) LoadResource ("PASSWORD.Editorwindow");
641 Rect WindowSize = M->OuterBounds ();
642 WindowSize.A.Y = 2;
643 WindowSize.B.Y = Background->IYSize () - 2;
644 M->Resize (WindowSize);
645
646 // Create a listbox and place it in the window
647 PasswordListBox* L = new PasswordListBox ("", 2,
648 Point (WindowSize.XSize (), WindowSize.YSize () - 1),
649 NULL);
650 L->SetColl (PC);
651 L->SetPos (0, 1);
652 M->AddItem (L);
653 L->Select ();
654
655 // Push a new statusline and activate (show) the window
656 PushStatusLine (siAbort | siAccept | siInsert | siDelete | siChange);
657 M->Activate ();
658
659 // User loop
660 int Done = 0;
661 Abort = 0;
662 while (!Done) {
663
664 // Get a key from the user
665 Key K = KbdGet ();
666
667 // Feed the key to the listbox
668 L->HandleKey (K);
669
670 // Get the current listbox entry
671 int Current = L->GetSelected ();
672 PasswordEntry* Entry;
673 PasswordEntry* NewEntry;
674 int EAbort;
675 int EChanged;
676
677 // Look if there's anything left
678 switch (K) {
679
680 case vkAbort:
681 // Ask if anything has changed
682 if (GetCRC (PC) != OldCRC) {
683 if (AskDiscardChanges () == 2) {
684 // Discard changes, end editing
685 PC->DeleteAll ();
686 MS >> *PC;
687 Changed = 0;
688 Abort = 1;
689 Done = 1;
690 }
691 } else {
692 Changed = 0;
693 Abort = 1;
694 Done = 1;
695 }
696 break;
697
698 case vkAccept:
699 Changed = (GetCRC (PC) != OldCRC);
700 Abort = 0;
701 Done = 1;
702 break;
703
704 case kbEnter:
705 // Change an entry
706 if (Current != -1) {
707
708 // Get the current entry and delete it from the collection
709 Entry = PC->Extract (Current);
710
711 // Create a duplicate of the entry
712 NewEntry = Duplicate (Entry);
713
714 EntryEditor (NewEntry, EAbort, EChanged);
715 if (!EAbort && EChanged) {
716 // Check if the user id already exists
717 if (PC->UserIDExists (NewEntry)) {
718 // The id exists, delete the entry
719 ErrorMsg (msUserIDExists);
720 delete NewEntry;
721 PC->Insert (Entry);
722 } else {
723 delete Entry;
724 L->Insert (NewEntry);
725 L->Reset ();
726 }
727 } else {
728 delete NewEntry;
729 PC->Insert (Entry);
730 }
731 }
732 break;
733
734 case vkIns:
735 // Insert a new entry
736 Entry = new PasswordEntry;
737 EntryEditor (Entry, EAbort, EChanged);
738 if (!EAbort && EChanged) {
739 // Check if the user id already exists
740 if (PC->UserIDExists (Entry)) {
741 // The id exists, delete the entry
742 ErrorMsg (msUserIDExists);
743 delete Entry;
744 } else {
745 // User id is unique, insert it
746 L->Insert (Entry);
747 }
748 } else {
749 // Editing was aborted, delete the entry
750 delete Entry;
751 }
752 break;
753
754 case vkDel:
755 // Delete the current entry
756 if (Current != -1 && AskAreYouShure () == 2) {
757 L->Delete (Current);
758 }
759 break;
760
761 }
762
763 }
764
765 // The listbox will delete the owned collection if we don't set it to NULL
766 // before deleting the window (including the listbox)
767 L->SetColl (NULL);
768 delete M;
769
770 // Restore the old status line
771 PopStatusLine ();
772
773 }
774
775
776
ReadPasswordFile(const String & Filename)777 static PasswordColl* ReadPasswordFile (const String& Filename)
778 // Read the password file and return the password collection. If there is no
779 // password file, return an empty collection
780 {
781 PasswordColl* PC;
782
783 // Try to open the file
784 FileStream S (Filename, "rb");
785 if (S.GetStatus () != stOk) {
786 // new file, create a collection
787 PC = new PasswordColl;
788 } else {
789 // Load a password collection from the stream and check for errors
790 PC = (PasswordColl*) S.Get ();
791 if (!PC || S.GetStatus () != stOk) {
792 ErrorMsg (msCannotReadPasswordFile, Filename.GetStr ());
793 }
794 }
795
796 return PC;
797 }
798
799
800
PasswordEditor(const String & Filename)801 void PasswordEditor (const String& Filename)
802 // Loads a password collection from the given file and allows editing users/
803 // passwords
804 {
805 // Load the password collection from the file
806 PasswordColl* PC = ReadPasswordFile (Filename);
807
808 // Allow editing
809 int Abort;
810 int Changed;
811 PasswordEditor (PC, Abort, Changed);
812
813 // If the collection has been changed, store it
814 if (!Abort && Changed) {
815 FileStream S (Filename, "wb");
816 if (S.GetStatus () != stOk) {
817 ErrorMsg (msCannotOpenPasswordFile, Filename.GetStr ());
818 return;
819 }
820 S.Truncate ();
821 S.Put (PC);
822 if (S.GetStatus () != stOk) {
823 ErrorMsg (msCannotWritePasswordFile, Filename.GetStr ());
824 return;
825 }
826 }
827
828 }
829
830
831
832 /*****************************************************************************/
833 /* Login/Logout */
834 /*****************************************************************************/
835
836
837
ResetLoginData()838 static void ResetLoginData ()
839 // Reset the login data
840 {
841 _CUN = "";
842 _CUID = "";
843 _CPL = 0;
844 }
845
846
847
Log(const String & Logname,const String & Name,const String & ID,u32 Level,PWLogEntry::_What What)848 static void Log (const String& Logname, const String& Name, const String& ID,
849 u32 Level, PWLogEntry::_What What)
850 {
851 if (Logname.IsEmpty ()) {
852 return;
853 }
854 FileStream S (Logname);
855 S.SeekToEnd ();
856 if (S.GetStatus () == stOk) {
857 PWLogEntry LE (Name, ID, Level, What);
858 S.Put (LE);
859 }
860 }
861
862
863
Login(const String & PWName,const String & Logname)864 void Login (const String& PWName, const String& Logname)
865 // Ask for user id and password and set the variables CUN CUID and CPL
866 // according to the users password entry.
867 // If Logname is not empty, a binary log of all login attempts is stored there.
868 {
869 int Abort;
870
871 // Read in the user id
872 Menue* M = (Menue*) LoadResource ("PASSWORD.UserID-Window");
873 TextEdit* TE = (TextEdit*) M->ForcedItemWithID (1);
874 TE->Edit (Abort);
875 String UserID = TE->GetValue ();
876 delete M;
877 if (Abort) {
878 return;
879 }
880
881 // Read in the password
882 M = (Menue*) LoadResource ("PASSWORD.Password-Window");
883 PasswordEdit* PE = (PasswordEdit*) M->ForcedItemWithID (1);
884 PE->Edit (Abort);
885 String Password = PE->GetValue ();
886 delete M;
887 if (Abort) {
888 return;
889 }
890
891 // Load the password collection from the file
892 PasswordColl* PC = ReadPasswordFile (PWName);
893
894 // Search for the user id
895 int Index;
896 if (PC->Search (&UserID, Index) == 0 || PC->At (Index)->Password != Password) {
897 // Not found, pop up an error message, reset data
898 ErrorMsg (msInvalidLogin);
899 Log (Logname, "", UserID, 0, PWLogEntry::Fail);
900 ResetLoginData ();
901 return;
902 } else {
903 // Found, set the login data, log the login
904 PasswordEntry* PE = PC->At (Index);
905 _CUN = PE->UserName;
906 _CUID = PE->UserID;
907 _CPL = PE->Level;
908 Log (Logname, CUN, CUID, CPL, PWLogEntry::Login);
909 }
910 }
911
912
913
Logout(const String & Logname)914 void Logout (const String& Logname)
915 // Reset the user data. If Logname is not empty, a binary log of the
916 // login/logout sequences is kept there.
917 {
918 // First, check if a logout is necessary
919 if (CPL == 0) {
920 return;
921 }
922
923 // Log the logout
924 if (!Logname.IsEmpty ()) {
925 PWLogEntry LE (CUN, CUID, CPL, PWLogEntry::Logout);
926 FileStream S (Logname);
927 S.Put (LE);
928 }
929
930 // Reset the data
931 ResetLoginData ();
932 }
933
934
935
936