0

I need to have an auth in a .csv file, mi users.csv file looks like this:

1,root,root,1
2,user,user,0

The columns represents [id,user,pass,isAdmin]

What I need is to search in the .csv rows in the columns [user, pass] to check if it's equal to the user and pass entered by the user, btw, I'm using an struct in this way

My code

struct User {
    int id;
    char user[20];
    char password[20];
    bool isAdmin;
};

And a function in this way:

void getUser (struct User *currentUser, FILE *users) {
    char allUsers[100][20];
    char user[20];
    char password[20];
    printf("Gimme your username: ");
    scanf("%s", user);
    printf("Gimme your password: ");
    scanf("%s", password);

    char line[MAX_USER_ROW_LENGTH];
    while (fgets(line, sizeof(line), users)) {
        char *token = strtok(line, DELIMITER);
        char *token2 = strtok(line, DELIMITER);
        token2 = strtok(NULL, DELIMITER);
        while (token != NULL) {
            if ((strcmp(user, token) == 0) && strcmp(password, token2) == 0) {
                printf("user an pass correct");
                
                /**
                 * here goes the code that initialize the user, i plan to do something like, if the user and pass 
                 * are correct i would do something like this:
                 *      currentUser->id = id;
                 *      currentUser->user = user;
                 *      currentUser->password = password;
                 *      currentUser->isAdmin = 1 or 0
                 *      
                 * I just relize that i have no idea how to initialize the user, sorry :c
                 * */
            } else {
                printf("user and pass not found");
                return;
            }
            token = strtok(NULL, DELIMITER);
            token2 = strtok(NULL, DELIMITER);
        }
    }
}

I've tried

I've used the function strtok() to separate each row between the commas, but, I can only compare the username or the password, not twice at each iteration, (sorry if the wile is empty, every ting i've tried, i did ctrl+z because i entered in panic).

Once i've checked if the user exists and can in, i should pass the values to the *currentUser, but i think i can do it.

Thank you if you can help me.

  • *I've used the function `strtok()`*. Please show the code that does that. The empty loop suggests you would like the code provided for you. A function argument is `struct User *currentUser` which isn't used. Please show how you use that too. – Weather Vane Jul 19 '23 at 19:52
  • I just edited the code, sorry if it doesn't make sense or something like that, it's a college homework assignment, its, just my professor just thow the "chamba", (we say chamba to work in mexico) with just a tiny explanation about files and structs and i have no idea wehre to start :c – Ricardo Roel Jul 19 '23 at 20:05
  • Hm, you can't apply `strtok(line, DELIMITER);` twice to the same string. It alters the string, and is not reentrant. So effectively the second one `char *token2 = strtok(line, DELIMITER);` is like doing `char *token2 = strtok(token, DELIMITER);` but then, subsequent calls which pass `NULL` aren't going to do what you think. I suggest finding examples of how `strtok()` should be used. Perhaps [this question](https://stackoverflow.com/questions/3889992/how-does-strtok-split-the-string-into-tokens-in-c) can help. – Weather Vane Jul 19 '23 at 20:46
  • Is there a better way to to the auth without using the `strtok()` function? – Ricardo Roel Jul 19 '23 at 22:56
  • Use `strchr()` to search for your delimiter. What is your question? – Allan Wind Jul 20 '23 at 04:45
  • Also you wan to supply us with a program not snippets that we have to assemble and write glue code to execute. In case there is no `main()` so we don't know how to handle errors. `MAX_USER_ROW_LENGTH` is missing. – Allan Wind Jul 20 '23 at 06:42

1 Answers1

0
  1. When you use struct User with an char user[20] attribute (i.e. duplication) it's a hint that your model is incorrect. In this case I renamed the attribute to name.

  2. Use symbolic constants instead of magic values in your struct definition. You can subsequently use these constants when defining related variables.

  3. The getUser() interface doesn't have a good way to tell caller if the lookup succeeded. On the other hand, I think it's a good choice to pass in a FILE *users instead of path name.

  4. Always use a maximum field width when reading strings with scanf() to avoid buffer overflows. The str() macro that relies on the str2() macro is handy for this.

  5. It's generally a good idea to separate user interaction and application logic. Moved the user prompts to main().

  6. (Not fixed) "Gimme" is not proper English. "User? " and "Password? " would be suitable prompts. I assume you are not a native speaker.

  7. You need MAX_USER_ROW_LENGTH because you use fget(). If you use getline() instead it will allocate a suitable string for you.

  8. strtok() modifies its input so you cannot call it twice on the same input as you do. Prefer strpbrk() or strchr() to strtok(). The scanf() family of functions is a concise way of parsing your file.

#define _POSIX_C_SOURCE 200809L
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define DELIMITER ','
#define FIELDS 4
#define NAME_LEN 19
#define PASSWORD_LEN 19
#define str(s) str2(s)
#define str2(s) #s

struct User {
    int id;
    char name[NAME_LEN+1];
    char password[PASSWORD_LEN+1];
    bool isAdmin;
};

struct User *getUser(FILE *users, const char *name, const char *password) {
    struct User *user = malloc(sizeof *user);
    if(!user) {
        perror("malloc");
        return NULL;
    }
    char *lineptr = NULL;
    size_t n;
    while(getline(&lineptr, &n, users) != -1) {
        int isAdmin;
        int rv = sscanf(lineptr,
            "%d,"
            "%" str(NAME_LEN) "[^,],"
            "%" str(PASSWORD_LEN) "[^,],"
            "%d",
            &user->id,
            user->name,
            user->password,
            &isAdmin
        );
        user->isAdmin = isAdmin;
        if(rv != FIELDS) {
            fprintf(stderr, "%d of %d fields read\n", rv, FIELDS);
            goto err;
        }
        if(
            !strcmp(name, user->name) &&
            !strcmp(password, user->password)
        ) {
            free(lineptr);
            return user;
        }
    }
err:
    free(lineptr);
    free(user);
    return NULL;
}

void printUser(struct User *user) {
    if(!user)
        return;
    printf(
        "id: %d, user: %s, password: %s, isAdmin: %d\n",
        user->id, user->name, user->password, user->isAdmin
    );
}

int main(void) {
    FILE *users = fopen("users.csv", "r");
    if(!users) {
        perror("fopen");
        return 1;
    }
    char name[NAME_LEN+1];
    printf("Gimme your username: ");
    scanf("%" str(NAME_LEN) "s", name);
    char password[PASSWORD_LEN+1];
    printf("Gimme your password: ");
    scanf("%" str(PASSWORD_LEN) "s", password);
    struct User *user = getUser(users, name, password);
    printUser(user);
    free(user);
    fclose(users);
}

and example sessions:

$ ./a.out
Gimme your username: root
Gimme your password: root
id: 1, user: root, password: root, isAdmin: 1
$ ./a.out
Gimme your username: root
Gimme your password: !root
Allan Wind
  • 23,068
  • 5
  • 28
  • 38