0

So my assignment is to create a login system. Username and passwords will be checked with content in the “account.txt” file. The content is that file have structure look like this:

Account ID: 1
Name: John Lee
Pass: 7uf
Role: student

Account ID: 2
Name: Park Lee
Pass: 42h
Role: Lecturer

Here what i got so far:

struct Account {
    char name[20];
    char pass[20];
};

void Login (char name[], char pass[]){

    FILE *sc;
    struct Account acc;

    sc = fopen("Account.txt","r");

    fscanf(sc,"\nName: %s",acc.name);
    fscanf(sc,"\nPass: %s",acc.pass);



    if(strcmp(name,acc.name) == 0 && strcmp(pass,acc.pass)) {
        printf("Login successful");
    }
    else {
        printf("Name or Pass incorrect");
    }

    fclose(sc);
}


 int main () {

    struct Account log[20];

        fflush(stdin);
        printf("\n\t\tEnter your name: ");
        gets(log[20].name);

        printf("\t\tEnter your password: ");
        gets(log[20].pass);

        Login(log[20].name,log[20].pass);   
    }

    return 0; }

What do you guys think i should do ?

Minh Khôi
  • 3
  • 1
  • 2
  • 3
    See [Why gets() is so dangerous it should never be used!](https://stackoverflow.com/questions/1694036/why-is-the-gets-function-dangerous-why-should-it-not-be-used) (especially for a login system...). You would normally read the entire accounts file with the hashed password into either an dynamically allocated array of struct, or list to have in memory to quickly iterate over for each login attempt. (at least for this exercise) – David C. Rankin May 15 '19 at 04:37
  • @user3629249 - you might as well collect your comments and provide them as an answer, you have good points to make. Also while you are correct on `fflush(stdin)` from a C-Standard standpoint, if he is on windows -- of course they allow the non-standard use as an implementation feature... – David C. Rankin May 15 '19 at 04:39
  • @DavidC.Rankin, ok, moved my comments to an answer – user3629249 May 15 '19 at 04:40
  • What you should do is send the password to a good hash function and stop storing passwords as plain text in a file. – Brendan May 15 '19 at 05:20
  • All password should be hashed and I would use a packed struct and store the data in a binary file or perhaps a database. – Cyclonecode May 15 '19 at 15:58
  • 1. `fopen`, `fscanf` have return values - Please check out the manual pages for these functions. 2. The format %s can lead to buffer overruns. Try %19s.l 3. `fflush(stdin);` does not make sense. 4. `gets` is wrong. 5. `strcmp(pass,acc.pass)` should do a comparison with 0 – Ed Heal May 15 '19 at 19:17

1 Answers1

0

in the function: login() the code needs to check every entry in the file before declaring a failure. After all, the first entry might not be for the person trying to login

regarding:

sc = fopen("Account.txt","r"); 
fscanf(sc,"\nName: %s",acc.name); 

1) always check (!=NULL) the returned value from fopen() to assure the operation was successful.

2) need to move past the first line of each entry in the input file before trying to read the name

3) when using the input format specifiers '%s' and/or '%[...]' always include a MAX CHARACTERS modifier that is 1 less than the length of the input buffer because those specifiers always append a NUL byte to the input. This avoids a buffer overflow and the resulting undefined behavior.

I.E.

if( !sc ) 
{ 
    perror( "fopen failed" ); 
    exit( EXIT_FAILURE ); 
} 

{input first line of acct and discard} 

if( fscanf(sc,"\nName: %19s",acc.name) != 1 ) 
{ 
    // handle error 
} 

However, if those lines in the input file contains those labels, like Name: Then the code needs to also input and discard those labels, as in the above example.

This seems to be homework, so I'm very reluctant to just 'give' you appropriate code. I would expect your instructor or TA would be able to help you with the details of what the code should be doing.

regarding statements like:

gets(log[20].name);

1) gets() is no longer part of the C language, Your compiler should have told you this.

2) the valid index into an array has the range: 0...(number of entries in array -1). So index 20 is beyond the end of the range. Suggest just using a pointer to the array.

3) Suggest using `fgets() to input each line from the file.

4) the struct you have declared will not work well with the actual data from the input file.

Suggest using:

#define MAX_LOG_ENTRIES 20

int main( void )
{
    struct Account acc[ MAX_LOG_ENTRIES ] = { "","" };
    char dummy[128];
    size_t i;
    for( i = 0; i<MAX_LOG_ENTRIES; i++ )
    {
        if( i< MAX_LOG_ENTRIES && fgets( dummy, sizeof( dummy ), sc ) )
        {  // then successfully read 'account' line
            if( fgets( dummy, sizeof( dummy ), sc ) )
            {  // then successfully read 'Name:` line
                // remove trailing newline
                dummy[ strcspn( dummy, "\n" )] = '\0';
                // skip past Name: ' label
                char * namePtr = strchr( dummy, ':' );
                if( namePtr )
                { // then found the ':'
                     // step by ': '
                     namePtr += 2;
                }
                // extract name
                strcpy( log[i].name, namePtr );

            if( fgets( dummy, sizeof( dummy ), sc ) )
            {  // then successfully read 'Pswd:` line
                // remove trailing newline
                dummy[ strcspn( dummy, "\n" )] = '\0';
                // skip past Pswd: ' label
                char * pswdPtr = strchr( dummy, ':' );
                if( pswdPtr )
                { // then found the ':'
                     // step by ': '
                     pswdPtr += 2;
                }
                // extract password
                strcpy( log[i].pswd, pswdPtr );

                // read/discard unused data line
                fgets( dummy, sizeof( dummy ), sc );
                // read/discard unused blank line
                fgets( dummy, sizeof( dummy ), sc );          
         }

When the above for() loop exits, all the records are read into the array named log[] and the variable 'i' contains the number of entries in the array 'log[]' that are actually used

now the code needs to input the two fields from the user (name and pswd)

Then loop through the array log[] to see if there is a 'name+pswd' match.

if fgets( dummy, sizeof( dummy ), sc );a match is found, then success, otherwise the user failed to enter valid data.

Note: The above code fails to check for errors and similar problems, including if the input file contains less than 20 entries. You should be able to add the error (and EOF) checking

user3629249
  • 16,402
  • 1
  • 16
  • 17
  • 1) i use `fflush(stdin)` because the program skip `gets(log[20].name)`. I looked back to another problem and look like it's the only solution. 2) i try to use `gets(...)` in order to get input from the user, If it unsafe what do you think i should use ? – Minh Khôi May 15 '19 at 04:56
  • strongly suggest using `fgets()` (see the MAN page for details of the parameters and note that it also inputs the trailing newline, Suggest using: `strcspn()` to remove the newline – user3629249 May 15 '19 at 04:58
  • I still dont know how to check **name** and **pass** in the account 2 and other account forward. Can you help me ? – Minh Khôi May 15 '19 at 15:27