/*

  This is a part of the Project Frontier's Source code.

  Copyright (C) 1997-98 Francis Gastellu
                    aka Lone Runner/Aegis

  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License
  as published by the Free Software Foundation; either version 2
  of the License, or (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

/*

USERS.SYS functions

*/

#include <malloc.h>
#include <share.h>
#include "..\lang\fortify.h"
#include "..\com\tpa.h"
#include "..\com\sharing.h"
#include "..\lang\usersys.h"

/*                              USERS.SYS FILE LAYOUT
                              ---------------------

  The structure of the USERS.SYS file is as follows:

  1) A header record describing what is contained in the file
  2) A fixed-size user record with PCBoard specific data fields
  3) Last Message Read pointers for conferences
  4) Bit Map fields for conferences (registered, expired, scanned, etc)
  5) The Third Party Application fixed record
  6) The Third Party Application conference records


  The following C structures are used to implement the above layout:

  USERS.SYS HEADER STRUCTURE
  --------------------------
  NOTE:  type "bool" is a character field (i.e. 1 byte in size) with a non-zero
  value meaning TRUE and zero meaning FALSE.

  typedef struct {
    unsigned Version;           PCBoard version number (i.e. 1500)
    long     RecNo;             Record number from USER's file
    unsigned SizeOfRec;         Size of "fixed" user record (current size)
    unsigned NumOfAreas;        Number of conference areas (Main=1 thru 65535)
    unsigned NumOfBitFields;    Number of Bit Map fields for conferences
    unsigned SizeOfBitFields;   Size of each Bit Map field
    char     AppName[15];       Name of the Third Party Application (if any)
    unsigned AppVersion;        Version number for the application (if any)
    unsigned AppSizeOfRec;      Size of a "fixed length" record (if any)
    unsigned AppSizeOfConfRec;  Size of each conference record (if any)
    long     AppRecOffset;      Offset of AppRec into USERS.INF record (if any)
    bool     Updated;           TRUE if the USERS.SYS file has been updated
  } syshdrtype;


  USERS.SYS FIXED USER RECORD STRUCTURES
  --------------------------------------
  typedef struct {               Bit packed flags in the users file
    int Dirty        :1;         Dirty Flag (meaning file has been updated)
    int MsgClear     :1;         User's choice for screen clear after messages
    int HasMail      :1;         Indicates if NEW mail has been left for user
    int DontAskFSE   :1;         Don't ask for if FSE should be used
    int FSEDefault   :1;         Default to FSE
    int ScrollMsgBody:1;         Scroll message body when display messages
    int ShortHeader  :1;         Display short message headers
    int WideEditor   :1;         Use wide (79-column) message editor
  } packedbyte;

  typedef struct {               Bit packed flags in the users file
    int UnAvailable:1;           Chat Status (Unavailable if bit is set)
    int Reserved:7;              RESERVED!  DO NO USE THESE BITS
  } packedbyte2;

  typedef struct {               DOS format for bit packed date fields
    int Day   :5;                5 bit integer representing the Day
    int Month :4;                4 bit integer representing the Month
    int Year  :7;                7 bit integer representing the Year MINUS 80
  } datetype;

  typedef struct {
    char  Street[2][51];         2 NULL-terminated strings for street address
    char  City[26];              A NULL-terminated string for city
    char  State[11];             A NULL-terminated string for state
    char  Zip[11];               A NULL-terminated string for zip
    char  Country[16];           A NULL-terminated string for country
  } addresstypez;

  typedef struct {
    char     Previous[3][13];    3 NULL-terminated strings for last 3 passwords
    unsigned LastChange;         Date of last password change
    unsigned TimesChanged;       Number of times password has been changed
    unsigned ExpireDate;         Expiration date of current password
  } passwordtypez;

  typedef struct {
    unsigned FirstDateOn;        First date on, in julian date format
    unsigned NumSysopPages;      Number of times caller paged the sysop
    unsigned NumGroupChats;      Number of times caller entered group chat
    unsigned NumComments;        Number of times caller left comment to sysop
    unsigned Num300;             Number of times caller was on a 300 bps
    unsigned Num1200;            Number of times caller was on a 1200 bps
    unsigned Num2400;            Number of times caller was on a 2400 bps
    unsigned Num9600;            Number of times caller was on a 9600 bps
    unsigned Num14400;           Number of times caller was on a 14400+ bps
    unsigned NumSecViol;         Number of security violations committed
    unsigned NumNotReg;          Number of attempts to join un-reg conference
    unsigned NumReachDnldLim;    Number of times download limit was reached
    unsigned NumFileNotFound;    Number of times download file was not found
    unsigned NumPwrdErrors;      Number of times entered password incorrectly
    unsigned NumVerifyErrors;    Number of times upload verification failed
  } callerstattype;

  typedef struct {
    char  Line[5][61];           5 NULL-terminated strings for notes on caller
  } notestypez;

| typedef struct {
|   double StartingBalance;      Starting Balance
|   double StartThisSession;     Balance at login of current session
|   double DebitCall;            Charges for calls made to the system
|   double DebitTime;            Charges for time spent online
|   double DebitMsgRead;         Charges for messages read
|   double DebitMsgReadCapture;  Charges for messages captured
|   double DebitMsgWrite;        Charges for messages written
|   double DebitMsgWriteEchoed;  Charges for messages written (echoed)
|   double DebitMsgWritePrivate; Charges for messages written (private)
|   double DebitDownloadFile;    Charges for files downloaded
|   double DebitDownloadBytes;   Charges for bytes downloaded
|   double DebitGroupChat;       Charges for time spent in group chat
|   double DebitTPU;             Charges for third party utility usage
|   double DebitSpecial;         Charges made via PPEs
|   double CreditUploadFile;     Payback for files uploaded
|   double CreditUploadBytes;    Payback for bytes uploaded
|   double CreditSpecial;        Packback made via PPEs
|   char DropSecLevel;           Security level on empty/negative balance
| } accounttype;
|
| typedef struct {
|   unsigned MaxMsgs;              Maximum messages desired in QWK packet
|   unsigned MaxMsgsPerConf;       Maximum messages per conference
|   long     PersonalAttachLimit;  Attach limit on personal messages
|   long     PublicAttachLimit;    Attach limit on public (non-personal) msgs
|   char     Reserved[18];
| } qwkconfigtype;

  typedef struct {
    char     Name[26];           Name (NULL terminated)
    char     City[25];           City (NULL terminated)
    char     Password[13];       Password (NULL terminated)
    char     BusDataPhone[14];   Business or Data Phone (NULL terminated)
    char     HomeVoicePhone[14]; Home or Voice Phone (NULL terminated)
    unsigned LastDateOn;         Julian date for the Last Date On
    char     LastTimeOn[6];      Last Time On (NULL Terminated)
    bool     ExpertMode;         1=Expert, 0=Novice
    char     Protocol;           Protocol (A thru Z)
    packedbyte PackedFlags;      Bit packed flags
    datetype DateLastDirRead;    Date for Last DIR Scan (most recent file)
    int      SecurityLevel;      Security Level
    unsigned NumTimesOn;         Number of times the caller has connected
    char     PageLen;            Page Length when display data on the screen
    unsigned NumUploads;         Total number of FILES uploaded
    unsigned NumDownloads;       Total number of FILES downloaded
    long     DailyDnldBytes;     Number of BYTES downloaded so far today
    char     UserComment[31];    Comment field #1 (NULL terminated)
    char     SysopComment[31];   Comment field #1 (NULL terminated)
    int      ElapsedTimeOn;      Number of minutes online
    unsigned RegExpDate;         Julian date for Registration Expiration Date
    int      ExpSecurityLevel;   Expired Security Level
    unsigned LastConference;     Number of the conference the caller was in
    long     TotDnldBytes;       Total number of BYTES downloaded
    long     TotUpldBytes;       Total number of BYTES uploaded
    bool     DeleteFlag;         1=delete this record, 0=keep
    long     RecNum;             Record Number in USERS.INF file
    packedbyte2  Flags;          More bit packed flags
    char     Reserved[8];        Bytes 390-397 from the USERS file
    long     MsgsRead;           Number of messages the user has read in PCB
    long     MsgsLeft;           Number of messages the user has left in PCB
    bool     AliasSupport;       TRUE if Alias PSA installed
    char     Alias[26];          Chosen Alias, if AliasSupport is TRUE
    bool     AddressSupport;     TRUE if Address PSA installed
    addresstypez Address;        Address information, if AddressSupport is TRUE
    bool     PasswordSupport;    TRUE if Password PSA installed
    passwordtypez PwrdHistory;   Password History, if PasswordSupport is TRUE
    bool     VerifySupport;      TRUE if Verify PSA installed
    char     Verify[26];         Verification Info, if VerifySupport is TRUE
    bool     StatsSupport;       TRUE if Caller Stats PSA installed
    callerstattype Stats;        Caller Stats, if StatsSupport is TRUE
    bool     NotesSupport;       TRUE if Notes PSA installed
    notestypez Notes;            Notes about caller, if NotesSupport is TRUE
|   bool      AccountSupport;    TRUE if Accounting PSA installed
|   accounttype Account;         Accounting values, if AccountSupport is TRUE
|   bool      QwkSupport;        TRUE if QWK/Net PSA installed
|   qwkconfigtype QwkConfig;     QWK/Net values, if QwkSupport is TRUE
  } userrectype;

  CHANGES FOR V15.0
  -----------------
  The fields near the end of the userrectype, from AliasSupport to Notes,
  are new for v15.0.  This means that the record size has GROWN.  Those
  applications which did not read the record size out of the header and jump
  over any growth in the record size will fail.

  However, version 15.0 allows you to set the USERS.SYS setting in DOORS.LST
  to either Y or O.  Setting it to Y tells PCBoard to create a native users.sys
| file.  Setting it to 1 tells PCBoard to create the 'old' format .. that which
  was used by PCBoard v14.5a.  This means that the additional information at
  the end of the record will not be included.

  We recommend that you update your applications to properly deal with the
  variable sized records so that they can be used with both v14.5a and v15.0
  users.sys files.

| CHANGES FOR V15.2
| -----------------
| Once again, the USERS.SYS structure has GROWN.  The four new fields for
| version 15.2 are AccountSupport, Account, QwkSupport and QwkConfig.
|
| To obtain a v15.0 format USERS.SYS file, use the number 2 inside DOORS.LST
| under the USERS.SYS header.  A Y means create a default format (v15.2),
| 1 means to create a v14.5a format, and 2 means to create a v15.0 format.
|
| To ensure compatibility with all versions of PCBoard, you should ensure that
| your software reads the header at the top of the USERS.INF file to determine
| how big the structure is, then only read as much as your program knows
| about, and also to determine how many bit fields there are since v15.2 has
| one more bit field than v14.5 and v15.0 had.


  LAST MESSAGE READ POINTERS
  --------------------------
  A LONG integer is used for each conference.  Therefore you must know the
  number of conference areas (syshdrtype.NumOfAreas) to determine how many
  long integers there are in the file (remember that the MAIN BOARD counts
  as one conference area).


  BIT MAPPED FIELDS
  -----------------
  A bit mapped field is nothing more than a string of bytes which when held
  together can be searched to see if a specific BIT is turned on or off.

  You should read the header record syshdrtype.NumOfBitFields to determine
  exactly how many bit fields there are in case a new release includes any
  new fields (which you may skip over if you don't need them).

| There are currently 8 of these fields and they are in the following order:

  1) Registered in Conference
  2) Expired Registered in Conference (same as above but used when expired)
  3) User Scan in Conference (user preference for scanning conferences)
  4) Conference Sysop (user gets Sysop Privileges while in conference)
  5) Conference Mail (user has mail waiting in conference)
  6) Conference Joined (user has already joined this conference today)
  7) Conference Scanned (user has already scanned this conference today)
| 8) Conferences in which Net Status is available to th caller

  The actual size of each of these fields is given to you in the header
  record syshdrtype.SizeOfBitFields.  You don't need to know this - but the
  actual calculation of the size by PCBoard is made using the following
  formula:

    ConfByteLen = (NumAreas / 8) + ((NumAreas % 8) != 0 ? 1 : 0);
    if (ConfByteLen < 5)
      ConfByteLen = 5;

  Basically, it allocates only the number of BYTES necessary to hold the
  number of bits required for all of the conferences defined.  A minimum of
  FIVE bytes are used due to PCBoard v14.0's conference definition of 40
  conference areas.

  In other words, if you have only one conference there will be FIVE bytes
  used due to v14.0's file layout requirements.  If you have 100 conferences
  then there will be 13 bytes used (with 4 bits going unused).

  As you can see - bit mapped fields become almost a necessity with the fact
  that there are currently 7 fields and a system may have as many as 65536
  conferences (which if it weren't for bit mapped fields could prove to be a
  very BIG file!).  With all 7 fields a system with 1024 conferences uses up
  only 896 bytes - which if one byte per flag were used could have taken up
  as much as 7168 bytes.


  CALCULATING THE JULIAN DATE VALUES
  ----------------------------------
  The following calculations are used to calculate the LAST DATE ON and the
  EXPIRATION DATE fields ..  it is a julian date calculation which is used so
  that arithmetic performed on the date value itself will result in another
  valid date.  In other words, "date + 365" equals a 365 days in the future.

    int Days[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};

    date = 36525L * Year;
    if ((date % 100) == 0 && Month < 3)
      date--;

    date = (date - (1900 * 36525)) / 100;
    date += Day + Days[Month-1];


  Converting the julian date back into month, day and year values is a bit
  trickier but is typically only done when displaying the date to the user or
  storing it in the USERS file in "mmddyy" format.  The following calculations
  can be used to convert the date back into the month, day and year components:

    int Days[2][12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
                       0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335};

    Year  = (100 * Date) / 36525;
    Temp  = Year * 36525L
    Date -= Temp / 100;

    if (Temp % 100 == 0) {
      Date++;
      Leap = 1;
    } else Leap = 0;

    for (Month = Counter = 0; Counter < 12; Counter++)
      if (Days[Leap][Counter] < Date)
        Month = Counter;

    Day = JD - Days[Leap][Month];


  THIRD PARTY APPLICATION RECORD
  ------------------------------
  The Third Party Application record is used to hold information about the
  USER which is specific to the application.  PCBoard copies this information
  out of the USERS.INF file into the USERS.SYS file and then copies it back
  upon reloading the software.  The advantage to letting PCBoard handle the
  application needs is that as new users are added to the USERS file the
  Third Party application records are created inside the USERS.INF file at
  the same time.  And when a user is removed from the USERS file they are
  also removed from the USERS.INF file at the same time (while third party
  records are also being removed).

  The Third Party Application record is written in the USERS.SYS file
  directly after the PCBoard Bit Map Fields.

  If a there are no installed Third Party Applications (or none which
  required a user record) then the USERS.SYS file will end with the Bit
  Mapped Fields described above.

  The way that PCBoard knows which Third Party Application record should be
  appended to the USERS.SYS file is by the KEYWORD used by the caller to
  invoke the application.


  HANDLING THE USERS.SYS FILE
  ---------------------------
  The following information is in reference to how PCBoard expects the
  USERS.SYS file to be handled by Third Party Applications.

  The USERS.SYS file is created as PCBoard is dropping to DOS and contains
  what is basically a 'memory dump' of the values that PCBoard had in memory
  at the time the caller dropped to DOS.

  On returning from DOS PCBoard will check to see if a USERS.SYS file is in the
  \PCB directory REGARDLESS of whether or not a PCBOARD.SYS file existed or
  indicated that a caller was online.

  If the Updated flag is set to TRUE (in this case the value must be a byte
  value of 1 - no other value will be used to indicate TRUE so as to avoid
  accidently specifying a TRUE response) then PCBoard will read in the rest
  of the USERS.SYS file and update the USERS and USERS.INF files based on the
  contents of the USERS.SYS file overwriting whatever previous values may have
  been there.

  If the Updated flag is set to FALSE (the default) then it is assumed that
  the TPA may have updated the USERS file directly (in the case of existing
  v14.0 compatible programs) or that no modification was required.

  If the TPA allows the caller to hang up then it should clear the PCBOARD.SYS
  file so that PCBoard will recycle.  PCBoard will still read the USERS.SYS
  file which will indicate that a caller was, in fact, online and if the
  Updated flag was set then it will still update the USERS and USERS.INF files.

  If the TPA updates the number of uploads, number of downloads or number of
  messages left fields in USERS.SYS then PCBoard will properly determine that
  such activity occured outside of the PCBoard environment and it will
  properly update these statistics on the CALLWAITING screen without the use
  of the PCBSTATS.EXE program.

*/

// --------------------------------------------------------------------------

void userSys_make(char *app)
{
userrectype *uRec;
syshdrtype *sHdr;
TPArec *tpa;
TPArec *tpaW;
FILE *uSys;
long a,b;
unsigned char c,d;
char *data;
int flag;
int f;

uRec = calloc(sizeof(userrectype), 1);
sHdr = calloc(sizeof(syshdrtype), 1);


tpa = openTPA(app);
if (tpa == NULL) app = NULL;


if ((uSys = fopen_share("USERS.SYS", "wb", SH_DENYRW)) == NULL)
	{
	fppe_runtimeError("Cannot create USER.SYS");
	closeTPA(tpa);
	free(sHdr);
	free(uRec);
	return;
    }

sHdr->Version = 1520;
sHdr->RecNo = user.Index;
sHdr->SizeOfRec = sizeof(userrectype);
sHdr->NumOfAreas = nConfs;
sHdr->NumOfBitFields = 7;
sHdr->SizeOfBitFields = nConfs / 8;
if (sHdr->SizeOfBitFields < 5)
	sHdr->SizeOfBitFields = 5;
if (app != NULL) strcpy(sHdr->AppName, app); else *sHdr->AppName = 0;
sHdr->AppVersion = app != NULL ? tpa->version : 0;
sHdr->AppSizeOfRec = app != NULL ? tpa->staticSize : 0;
sHdr->AppSizeOfConfRec = app != NULL ? tpa->dynamicSize : 0;
sHdr->AppRecOffset = 0;
sHdr->Updated = 0;

fwrite(sHdr, sizeof(syshdrtype), 1, uSys);

strnzcpy(uRec->Name, user.FullName, 25);
strnzcpy(uRec->City, user.City, 24);
strnzcpy(uRec->Password, user.Password, 12);
strncpy(uRec->BusDataPhone, user.DataPhone, 13);
strncpy(uRec->HomeVoicePhone, user.VoicePhone, 13);
strnzcpy(tmpStr, user.LastDateOn, 6);
uRec->LastDateOn = sysCharDateToPPLDate(tmpStr);
strnzcpy(uRec->LastTimeOn, user.LastTimeOn, 5);
uRec->ExpertMode = user.Expert;
uRec->Protocol = user.Protocol;
uRec->PackedFlags.Dirty = user.DirtyFlag;
uRec->PackedFlags.MsgClear = user.ClearScreen;
uRec->PackedFlags.HasMail = user.MailFlag;
uRec->PackedFlags.DontAskFSE = user.NoAskFSEditor;
uRec->PackedFlags.FSEDefault = user.FSEditor;
uRec->PackedFlags.ScrollMsgBody = user.ScrollMsgBody;
uRec->PackedFlags.ShortHeader  = user.ShortMsgHdr;
uRec->PackedFlags.WideEditor  = user.WideEditor;
strnzcpy(tmpStr, user.LastDir, 6);
sysCharDateToPPLDate(tmpStr);
uRec->DateLastDirRead.Day = sc_day;
uRec->DateLastDirRead.Month = sc_month;
uRec->DateLastDirRead.Year = sc_year - 1980;
uRec->SecurityLevel = user.Security;
uRec->NumTimesOn = user.TimesOn;
uRec->PageLen = user.PageLen;
uRec->NumUploads = user.FilesUploaded;
uRec->NumDownloads = user.FilesDownloaded;
strnzcpy(uRec->UserComment, user.Comment1, 30);
strnzcpy(uRec->SysopComment, user.Comment2, 30);
uRec->ElapsedTimeOn = user.TimeOn;
strnzcpy(tmpStr, user.RegExpDate, 6);
uRec->RegExpDate = sysCharDateToPPLDate(tmpStr);
uRec->ExpSecurityLevel = user.ExpSecurity;
uRec->LastConference = user.LastConfIn;
uRec->TotDnldBytes = BdReal2Double(user.BytesDownloaded);
uRec->TotUpldBytes = BdReal2Double(user.BytesUploaded);
uRec->DeleteFlag = user.DeletedFlag == 'Y' ? 1 : 0;
uRec->RecNum = user.Index;
uRec->Flags.UnAvailable = user.ChatStatus;
uRec->Flags.Reserved = 0;
memcpy(uRec->Reserved, user.Reserved8, 8);

// todo
uRec->MsgsRead = 0;
uRec->MsgsLeft = 0;
uRec->AliasSupport = 0;
*uRec->Alias = 0;
uRec->AddressSupport = 0;
*uRec->Address.Street[0] = 0;
*uRec->Address.Street[1] = 0;
*uRec->Address.City = 0;
*uRec->Address.State = 0;
*uRec->Address.Zip = 0;
*uRec->Address.Country = 0;
uRec->PasswordSupport = 0;
*uRec->PwrdHistory.Previous[0] = 0;
*uRec->PwrdHistory.Previous[1] = 0;
*uRec->PwrdHistory.Previous[2] = 0;
uRec->PwrdHistory.LastChange = 0;
uRec->PwrdHistory.TimesChanged = 0;
uRec->PwrdHistory.ExpireDate = 0;
uRec->VerifySupport = 0;
*uRec->Verify = 0;
uRec->StatsSupport = 0;
uRec->Stats.FirstDateOn = 0;
uRec->Stats.NumSysopPages = 0;
uRec->Stats.NumGroupChats = 0;
uRec->Stats.NumComments = 0;
uRec->Stats.Num300 = 0;
uRec->Stats.Num1200 = 0;
uRec->Stats.Num2400 = 0;
uRec->Stats.Num9600 = 0;
uRec->Stats.Num14400 = 0;
uRec->Stats.NumSecViol = 0;
uRec->Stats.NumNotReg = 0;
uRec->Stats.NumReachDnldLim = 0;
uRec->Stats.NumFileNotFound = 0;
uRec->Stats.NumPwrdErrors = 0;
uRec->Stats.NumVerifyErrors = 0;
uRec->NotesSupport = 0;
*uRec->Notes.Line[0] = 0;
*uRec->Notes.Line[1] = 0;
*uRec->Notes.Line[2] = 0;
*uRec->Notes.Line[3] = 0;
*uRec->Notes.Line[4] = 0;
uRec->AccountSupport = 0;
uRec->Account.StartingBalance=0;
uRec->Account.StartThisSession=0;
uRec->Account.DebitCall=0;
uRec->Account.DebitTime=0;
uRec->Account.DebitMsgRead=0;
uRec->Account.DebitMsgReadCapture=0;
uRec->Account.DebitMsgWrite=0;
uRec->Account.DebitMsgWriteEchoed=0;
uRec->Account.DebitMsgWritePrivate=0;
uRec->Account.DebitDownloadFile=0;
uRec->Account.DebitDownloadBytes=0;
uRec->Account.DebitGroupChat=0;
uRec->Account.DebitTPU=0;
uRec->Account.DebitSpecial=0;
uRec->Account.CreditUploadFile=0;
uRec->Account.CreditUploadBytes=0;
uRec->Account.CreditSpecial=0;
uRec->Account.DropSecLevel=0;
uRec->QwkSupport = 0;
uRec->QwkConfig.MaxMsgs=0;
uRec->QwkConfig.MaxMsgsPerConf=0;
uRec->QwkConfig.PersonalAttachLimit=0;
uRec->QwkConfig.PublicAttachLimit=0;
*uRec->QwkConfig.Reserved=0;

fwrite(uRec, sizeof(userrectype), 1, uSys);

tpaW = openTPA("LASTREAD");
for (b=0;b<nConfs;b++)
	{
	a = getTPAlong(tpaW, b, 1, userLoggedNum);
    fwrite(&a, 4, 1, uSys);
    }
closeTPA(tpaW);

tpaW = openTPA("UCNFFLAG");
for (flag=1;flag<8;flag++)
	{
    c=0;
    f=0;
	for (b=0;b<nConfs;b++)
		{
        if (b % 8 == 0 && f)
        	{
	    	fwrite(&c, 1, 1, uSys);
            c = 0;
            }
        f=1;
		getTPA(tpaW, &d, 1, b, 1, userLoggedNum);
        if (d & (1 << (flag-1)))
        	c |= 1 << (b % 8);
	    }
	for (;b<40;b++)
		{
        if (b % 8 == 0 && f)
        	{
	    	fwrite(&c, 1, 1, uSys);
            c = 0;
            }
        f=1;
	    }
    if ((b-1) % 8 != 0)
	 	fwrite(&c, 1, 1, uSys);
	}
closeTPA(tpaW);

data = calloc(tpa->staticSize, 1);
getTPA(tpa, data, tpa->staticSize, 0, 0, userLoggedNum);
fwrite(data, tpa->staticSize, 1, uSys);
free(data);

data = calloc(tpa->dynamicSize, 1);
for (b=0;b<nConfs;b++)
	{
	getTPA(tpa, data, tpa->dynamicSize, b, 1, userLoggedNum);
	fwrite(data, tpa->dynamicSize, 1, uSys);
    }
free(data);

closeTPA(tpa);

fclose(uSys);
free(sHdr);
free(uRec);
}

// --------------------------------------------------------------------------

void userSys_read(void)
{
userrectype *uRec;
syshdrtype *sHdr;
TPArec *tpa;
TPArec *tpaW;
FILE *uSys;
long a,b;
unsigned char c, d;
char *data;
userType u;
int flag;


sHdr = calloc(sizeof(syshdrtype), 1);


if ((uSys = fopen_share("USERS.SYS", "rb", SH_DENYWR)) == NULL)
	{
	free(sHdr);
	free(uRec);
	return;
    }

fread(sHdr, sizeof(syshdrtype), 1, uSys);
if (sHdr->Updated)
	{
	uRec = calloc(sizeof(userrectype), 1);
	fread(uRec, sizeof(userrectype), 1, uSys);

	getUser(sHdr->RecNo, &u);

	if (*sHdr->AppName != 0) tpa = openTPA(sHdr->AppName);

	strncpy(u.FullName, uRec->Name, 25);
	strncpy(u.City, uRec->City, 24);
	strcpy(u.Password, uRec->Password, 12);
	strncpy(u.DataPhone, uRec->BusDataPhone, 13);
	strncpy(u.VoicePhone, uRec->HomeVoicePhone, 13);
    PPLDateToSysCharDate(tmpStr, uRec->LastDateOn);
	strncpy(u.LastDateOn, tmpStr, 6);
	strncpy(u.LastTimeOn, uRec->LastTimeOn, 5);
	u.Expert = uRec->ExpertMode;
	u.Protocol = uRec->Protocol;
	u.DirtyFlag = uRec->PackedFlags.Dirty;
	u.ClearScreen = uRec->PackedFlags.MsgClear;
	u.MailFlag = uRec->PackedFlags.HasMail;
	u.NoAskFSEditor = uRec->PackedFlags.DontAskFSE;
	u.FSEditor = uRec->PackedFlags.FSEDefault;
	u.ScrollMsgBody = uRec->PackedFlags.ScrollMsgBody;
	u.ShortMsgHdr = uRec->PackedFlags.ShortHeader;
	u.WideEditor = uRec->PackedFlags.WideEditor;
    sprintf(tmpStr, "%02d%02d%02d", uRec->DateLastDirRead.Year, uRec->DateLastDirRead.Month = sc_month, uRec->DateLastDirRead.Year);
    strncpy(u.LastDir, tmpStr, 6);
	u.Security = uRec->SecurityLevel;
	u.TimesOn = uRec->NumTimesOn;
	u.PageLen = uRec->PageLen;
	u.FilesUploaded = uRec->NumUploads;
	u.FilesDownloaded = uRec->NumDownloads;
	strncpy(u.Comment1, uRec->UserComment, 30);
	strncpy(u.Comment2, uRec->SysopComment, 30);
	u.TimeOn = uRec->ElapsedTimeOn;
    PPLDateToSysCharDate(tmpStr, uRec->RegExpDate);
	strncpy(u.RegExpDate, tmpStr, 6);
	u.ExpSecurity = uRec->ExpSecurityLevel;
	u.LastConfIn = uRec->LastConference;
	Double2BdReal(u.BytesDownloaded, (double)uRec->TotDnldBytes);
    Double2BdReal(u.BytesUploaded, (double)uRec->TotUpldBytes);
	u.DeletedFlag = uRec->DeleteFlag;
//	u.Index = uRec->RecNum;
	u.ChatStatus = uRec->Flags.UnAvailable;
//	u.Reserved = uRec->Flags.Reserved;
	memcpy(u.Reserved8, uRec->Reserved, 8);

    if (sHdr->RecNo == userLoggedNum)
        user = u;
    else
    	putUser(&u);

// todo
/*uRec->MsgsRead = 0;
uRec->MsgsLeft = 0;
uRec->AliasSupport = 0;
*uRec->Alias = 0;
uRec->AddressSupport = 0;
*uRec->Address.Street[0] = 0;
*uRec->Address.Street[1] = 0;
*uRec->Address.City = 0;
*uRec->Address.State = 0;
*uRec->Address.Zip = 0;
*uRec->Address.Country = 0;
uRec->PasswordSupport = 0;
*uRec->PwrdHistory.Previous[0] = 0;
*uRec->PwrdHistory.Previous[1] = 0;
*uRec->PwrdHistory.Previous[2] = 0;
uRec->PwrdHistory.LastChange = 0;
uRec->PwrdHistory.TimesChanged = 0;
uRec->PwrdHistory.ExpireDate = 0;
uRec->VerifySupport = 0;
*uRec->Verify = 0;
uRec->StatsSupport = 0;
uRec->Stats.FirstDateOn = 0;
uRec->Stats.NumSysopPages = 0;
uRec->Stats.NumGroupChats = 0;
uRec->Stats.NumComments = 0;
uRec->Stats.Num300 = 0;
uRec->Stats.Num1200 = 0;
uRec->Stats.Num2400 = 0;
uRec->Stats.Num9600 = 0;
uRec->Stats.Num14400 = 0;
uRec->Stats.NumSecViol = 0;
uRec->Stats.NumNotReg = 0;
uRec->Stats.NumReachDnldLim = 0;
uRec->Stats.NumFileNotFound = 0;
uRec->Stats.NumPwrdErrors = 0;
uRec->Stats.NumVerifyErrors = 0;
uRec->NotesSupport = 0;
*uRec->Notes.Line[0] = 0;
*uRec->Notes.Line[1] = 0;
*uRec->Notes.Line[2] = 0;
*uRec->Notes.Line[3] = 0;
*uRec->Notes.Line[4] = 0;
uRec->AccountSupport = 0;
uRec->Account.StartingBalance=0;
uRec->Account.StartThisSession=0;
uRec->Account.DebitCall=0;
uRec->Account.DebitTime=0;
uRec->Account.DebitMsgRead=0;
uRec->Account.DebitMsgReadCapture=0;
uRec->Account.DebitMsgWrite=0;
uRec->Account.DebitMsgWriteEchoed=0;
uRec->Account.DebitMsgWritePrivate=0;
uRec->Account.DebitDownloadFile=0;
uRec->Account.DebitDownloadBytes=0;
uRec->Account.DebitGroupChat=0;
uRec->Account.DebitTPU=0;
uRec->Account.DebitSpecial=0;
uRec->Account.CreditUploadFile=0;
uRec->Account.CreditUploadBytes=0;
uRec->Account.CreditSpecial=0;
uRec->Account.DropSecLevel=0;
uRec->QwkSupport = 0;
uRec->QwkConfig.MaxMsgs=0;
uRec->QwkConfig.MaxMsgsPerConf=0;
uRec->QwkConfig.PersonalAttachLimit=0;
uRec->QwkConfig.PublicAttachLimit=0;
*uRec->QwkConfig.Reserved=0;
*/


	tpaW = openTPA("LASTREAD");
	for (b=0;b<nConfs;b++)
		{
	    fread(&a, 4, 1, uSys);
		putTPA(tpaW, &a, 4, b, 1, userLoggedNum);
	    }
	closeTPA(tpaW);

	tpaW = openTPA("UCNFFLAG");

    for (flag=1;flag<8;flag++)
	    {
	    for (b=0;b<nConfs;b++)
		    {
            if (b % 8 == 0)
	    	    fread(&c, 1, 1, uSys);
            if (c & (1 << b % 8))
            	{
			    getTPA(tpaW, &d, 1, b, 1, userLoggedNum);
                d &= 1 << (flag - 1);
			    putTPA(tpaW, &d, 1, b, 1, userLoggedNum);
                }
            }
	    for (;b<40;b++)
		    {
            if (b % 8 == 0)
	    	    fread(&c, 1, 1, uSys);
	        }
	    }

	closeTPA(tpaW);

    if (tpa != NULL)
    	{
	    data = calloc(tpa->staticSize, 1);
	    fread(data, tpa->staticSize, 1, uSys);
	    putTPA(tpa, data, tpa->staticSize, 0, 0, userLoggedNum);
	    free(data);

	    data = calloc(tpa->dynamicSize, 1);
	    for (b=0;b<nConfs;b++)
		    {
		    fread(data, tpa->dynamicSize, 1, uSys);
		    putTPA(tpa, data, tpa->dynamicSize, b, 1, userLoggedNum);
	        }
	    free(data);

	    closeTPA(tpa);
	    }
	free(uRec);
    }

fclose(uSys);
free(sHdr);
}
