Format project with clang-format
This commit is contained in:
parent
d7fe6cd476
commit
3efbddd3ee
4
.clang-format
Normal file
4
.clang-format
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
IndentWidth: 4
|
||||||
|
BreakBeforeBraces: Allman
|
||||||
|
ColumnLimit: 0
|
||||||
|
SortIncludes: false
|
@ -217,7 +217,8 @@ int process_md(article_info article, char **out)
|
|||||||
while (buff[i + 2 + n] != ']')
|
while (buff[i + 2 + n] != ']')
|
||||||
n++;
|
n++;
|
||||||
|
|
||||||
if (buff[i + 2 + n + 1] == '(') {
|
if (buff[i + 2 + n + 1] == '(')
|
||||||
|
{
|
||||||
int k = 0;
|
int k = 0;
|
||||||
while (buff[i + 2 + n + 2 + k] != ')')
|
while (buff[i + 2 + n + 2 + k] != ')')
|
||||||
k++;
|
k++;
|
||||||
@ -259,7 +260,8 @@ int process_md(article_info article, char **out)
|
|||||||
while (buff[i + 1 + n] != ']')
|
while (buff[i + 1 + n] != ']')
|
||||||
n++;
|
n++;
|
||||||
|
|
||||||
if (buff[i + 1 + n + 1] == '(') {
|
if (buff[i + 1 + n + 1] == '(')
|
||||||
|
{
|
||||||
int k = 0;
|
int k = 0;
|
||||||
while (buff[i + 1 + n + 2 + k] != ')')
|
while (buff[i + 1 + n + 2 + k] != ')')
|
||||||
k++;
|
k++;
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Generate file path from request path provided
|
* @brief Generate file path from request path provided
|
||||||
*
|
*
|
||||||
* @param req_path
|
* @param req_path
|
||||||
* @return char*
|
* @return char*
|
||||||
*/
|
*/
|
||||||
char *gen_file_path(char *req_path)
|
char *gen_file_path(char *req_path)
|
||||||
{
|
{
|
||||||
@ -27,7 +27,7 @@ char *gen_file_path(char *req_path)
|
|||||||
|
|
||||||
if (PATH_MAX < strlen(webroot) + strlen("/static"))
|
if (PATH_MAX < strlen(webroot) + strlen("/static"))
|
||||||
goto exit_error;
|
goto exit_error;
|
||||||
|
|
||||||
strcat(webroot, "/static");
|
strcat(webroot, "/static");
|
||||||
|
|
||||||
path = prepend(path, webroot);
|
path = prepend(path, webroot);
|
||||||
@ -36,7 +36,8 @@ char *gen_file_path(char *req_path)
|
|||||||
if (realpath(path, resolved_path) == NULL)
|
if (realpath(path, resolved_path) == NULL)
|
||||||
goto exit_error;
|
goto exit_error;
|
||||||
|
|
||||||
if (strncmp(resolved_path, webroot, strlen(webroot)) != 0) {
|
if (strncmp(resolved_path, webroot, strlen(webroot)) != 0)
|
||||||
|
{
|
||||||
goto exit_error;
|
goto exit_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,10 +50,10 @@ exit_error:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Send file to client
|
* @brief Send file to client
|
||||||
*
|
*
|
||||||
* @param fd
|
* @param fd
|
||||||
* @param file_path
|
* @param file_path
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
int send_file(int cli_fd, struct file_s *file)
|
int send_file(int cli_fd, struct file_s *file)
|
||||||
{
|
{
|
||||||
@ -68,9 +69,9 @@ int send_file(int cli_fd, struct file_s *file)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the file info
|
* @brief Get the file info
|
||||||
*
|
*
|
||||||
* @param file_path
|
* @param file_path
|
||||||
* @return struct file_s*
|
* @return struct file_s*
|
||||||
*/
|
*/
|
||||||
struct file_s *get_file_info(char *file_path)
|
struct file_s *get_file_info(char *file_path)
|
||||||
{
|
{
|
||||||
@ -98,9 +99,9 @@ struct file_s *get_file_info(char *file_path)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the file size
|
* @brief Get the file size
|
||||||
*
|
*
|
||||||
* @param file
|
* @param file
|
||||||
* @return size_t
|
* @return size_t
|
||||||
*/
|
*/
|
||||||
size_t get_file_size(FILE *file)
|
size_t get_file_size(FILE *file)
|
||||||
{
|
{
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the mime type of file
|
* @brief Get the mime type of file
|
||||||
*
|
*
|
||||||
* @param file_path
|
* @param file_path
|
||||||
* @return char*
|
* @return char*
|
||||||
*/
|
*/
|
||||||
char *get_mime_type(char *file_path)
|
char *get_mime_type(char *file_path)
|
||||||
{
|
{
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the list of albums on server
|
* @brief Get the list of albums on server
|
||||||
*
|
*
|
||||||
* @return gallery_t*
|
* @return gallery_t*
|
||||||
*/
|
*/
|
||||||
gallery_t *get_album_list()
|
gallery_t *get_album_list()
|
||||||
{
|
{
|
||||||
@ -45,7 +45,7 @@ gallery_t *get_album_list()
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Free gallery_t structure
|
* @brief Free gallery_t structure
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
void free_albums_list(gallery_t *albums_list)
|
void free_albums_list(gallery_t *albums_list)
|
||||||
@ -57,7 +57,7 @@ void free_albums_list(gallery_t *albums_list)
|
|||||||
for (int i = 0; i < albums_list->img_am; i++)
|
for (int i = 0; i < albums_list->img_am; i++)
|
||||||
free_img_item(albums_list->images[i]);
|
free_img_item(albums_list->images[i]);
|
||||||
free(albums_list->images);
|
free(albums_list->images);
|
||||||
|
|
||||||
gallery_t *next_item = albums_list->next;
|
gallery_t *next_item = albums_list->next;
|
||||||
free(albums_list);
|
free(albums_list);
|
||||||
albums_list = next_item;
|
albums_list = next_item;
|
||||||
@ -66,9 +66,9 @@ void free_albums_list(gallery_t *albums_list)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Generates new album item
|
* @brief Generates new album item
|
||||||
*
|
*
|
||||||
* @param title
|
* @param title
|
||||||
* @return gallery_t*
|
* @return gallery_t*
|
||||||
*/
|
*/
|
||||||
gallery_t *new_album_item(char *folder_name)
|
gallery_t *new_album_item(char *folder_name)
|
||||||
{
|
{
|
||||||
@ -90,9 +90,9 @@ gallery_t *new_album_item(char *folder_name)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Generates new image item
|
* @brief Generates new image item
|
||||||
*
|
*
|
||||||
* @param path
|
* @param path
|
||||||
* @return img_t
|
* @return img_t
|
||||||
*/
|
*/
|
||||||
img_t new_img_item(char *path)
|
img_t new_img_item(char *path)
|
||||||
{
|
{
|
||||||
@ -109,7 +109,7 @@ img_t new_img_item(char *path)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Free img_t structure
|
* @brief Free img_t structure
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
void free_img_item(img_t img)
|
void free_img_item(img_t img)
|
||||||
@ -154,12 +154,14 @@ int get_album_imgs(img_t **images_arr, int *size, char *title)
|
|||||||
char *gen_gallery_html()
|
char *gen_gallery_html()
|
||||||
{
|
{
|
||||||
FILE *album_template_fp = fopen("static/gallery/album.html", "r");
|
FILE *album_template_fp = fopen("static/gallery/album.html", "r");
|
||||||
if (album_template_fp == NULL) {
|
if (album_template_fp == NULL)
|
||||||
|
{
|
||||||
return "500 Internal Error\n";
|
return "500 Internal Error\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE *image_template_fp = fopen("static/gallery/image.html", "r");
|
FILE *image_template_fp = fopen("static/gallery/image.html", "r");
|
||||||
if (image_template_fp == NULL) {
|
if (image_template_fp == NULL)
|
||||||
|
{
|
||||||
fclose(album_template_fp);
|
fclose(album_template_fp);
|
||||||
return "500 Internal Error\n";
|
return "500 Internal Error\n";
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the listener socket object
|
* @brief Get the listener socket object
|
||||||
*
|
*
|
||||||
* @param {char *} port
|
* @param {char *} port
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
int get_listener_socket(char *port)
|
int get_listener_socket(char *port)
|
||||||
{
|
{
|
||||||
@ -67,17 +67,18 @@ int get_listener_socket(char *port)
|
|||||||
return sockfd;
|
return sockfd;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get address from sockaddr structure
|
* @brief Get address from sockaddr structure
|
||||||
*
|
*
|
||||||
* @param {struct sockaddr*} sa
|
* @param {struct sockaddr*} sa
|
||||||
* @return void*
|
* @return void*
|
||||||
*/
|
*/
|
||||||
void *get_in_addr(struct sockaddr *sa)
|
void *get_in_addr(struct sockaddr *sa)
|
||||||
{
|
{
|
||||||
if (sa->sa_family == AF_INET) {
|
if (sa->sa_family == AF_INET)
|
||||||
return &(((struct sockaddr_in*)sa)->sin_addr);
|
{
|
||||||
|
return &(((struct sockaddr_in *)sa)->sin_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
return &(((struct sockaddr_in6*)sa)->sin6_addr);
|
return &(((struct sockaddr_in6 *)sa)->sin6_addr);
|
||||||
}
|
}
|
@ -9,15 +9,16 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Send 404 response
|
* @brief Send 404 response
|
||||||
*
|
*
|
||||||
* @param fd
|
* @param fd
|
||||||
* @param path
|
* @param path
|
||||||
*/
|
*/
|
||||||
void res_404(int fd, char *path)
|
void res_404(int fd, char *path)
|
||||||
{
|
{
|
||||||
FILE *fp = fopen("static/404.html", "r");
|
FILE *fp = fopen("static/404.html", "r");
|
||||||
|
|
||||||
if (fp == NULL) {
|
if (fp == NULL)
|
||||||
|
{
|
||||||
res_500(fd);
|
res_500(fd);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -58,9 +59,9 @@ void res_500(int fd)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the path string
|
* @brief Get the path string
|
||||||
*
|
*
|
||||||
* @param request
|
* @param request
|
||||||
* @return char*
|
* @return char*
|
||||||
*/
|
*/
|
||||||
char *get_path(char *request)
|
char *get_path(char *request)
|
||||||
{
|
{
|
||||||
@ -71,11 +72,11 @@ char *get_path(char *request)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Generate header string
|
* @brief Generate header string
|
||||||
*
|
*
|
||||||
* @param status_code
|
* @param status_code
|
||||||
* @param file_size
|
* @param file_size
|
||||||
* @param mime_type
|
* @param mime_type
|
||||||
* @return char*
|
* @return char*
|
||||||
*/
|
*/
|
||||||
struct header_s *gen_header(int status_code, size_t file_size, char *mime_type)
|
struct header_s *gen_header(int status_code, size_t file_size, char *mime_type)
|
||||||
{
|
{
|
||||||
@ -99,16 +100,17 @@ struct header_s *gen_header(int status_code, size_t file_size, char *mime_type)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Send HTTP response
|
* @brief Send HTTP response
|
||||||
*
|
*
|
||||||
* @param fd
|
* @param fd
|
||||||
* @param req_path
|
* @param req_path
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
int send_response(int fd, char *req_path)
|
int send_response(int fd, char *req_path)
|
||||||
{
|
{
|
||||||
char *file_path = gen_file_path(req_path);
|
char *file_path = gen_file_path(req_path);
|
||||||
|
|
||||||
if (file_path == NULL) {
|
if (file_path == NULL)
|
||||||
|
{
|
||||||
res_404(fd, req_path);
|
res_404(fd, req_path);
|
||||||
free(file_path);
|
free(file_path);
|
||||||
return 0;
|
return 0;
|
||||||
@ -169,9 +171,9 @@ exit_code:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Handle POST request
|
* @brief Handle POST request
|
||||||
*
|
*
|
||||||
* @param fd
|
* @param fd
|
||||||
* @param request
|
* @param request
|
||||||
*/
|
*/
|
||||||
void handle_post_request(int fd, char *request)
|
void handle_post_request(int fd, char *request)
|
||||||
{
|
{
|
||||||
@ -182,9 +184,9 @@ void handle_post_request(int fd, char *request)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Handle get request
|
* @brief Handle get request
|
||||||
*
|
*
|
||||||
* @param fd
|
* @param fd
|
||||||
* @param request
|
* @param request
|
||||||
*/
|
*/
|
||||||
void handle_get_request(int fd, char *request)
|
void handle_get_request(int fd, char *request)
|
||||||
{
|
{
|
||||||
@ -205,7 +207,7 @@ void handle_get_request(int fd, char *request)
|
|||||||
|
|
||||||
fread(template, file_size, 1, fp);
|
fread(template, file_size, 1, fp);
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
template[file_size-1] = '\0';
|
template[file_size - 1] = '\0';
|
||||||
|
|
||||||
article_info *articles = NULL;
|
article_info *articles = NULL;
|
||||||
int amount = list_articles(&articles);
|
int amount = list_articles(&articles);
|
||||||
|
18
src/server.c
18
src/server.c
@ -16,8 +16,8 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Handle client connection
|
* @brief Handle client connection
|
||||||
*
|
*
|
||||||
* @param fd
|
* @param fd
|
||||||
*/
|
*/
|
||||||
void handle_connection(int fd)
|
void handle_connection(int fd)
|
||||||
{
|
{
|
||||||
@ -63,22 +63,22 @@ void handle_connection(int fd)
|
|||||||
/**
|
/**
|
||||||
* @brief Handles child process removal (to prevent zombies)
|
* @brief Handles child process removal (to prevent zombies)
|
||||||
*
|
*
|
||||||
* @param signum
|
* @param signum
|
||||||
*/
|
*/
|
||||||
void handle_process_termination(int signum)
|
void handle_process_termination(int signum)
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
|
||||||
do {
|
do
|
||||||
pid = waitpid(-1, &status, WNOHANG);
|
{
|
||||||
}
|
pid = waitpid(-1, &status, WNOHANG);
|
||||||
while (pid > 0);
|
} while (pid > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main
|
* Main
|
||||||
*/
|
*/
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int client_fd;
|
int client_fd;
|
||||||
|
@ -3,10 +3,10 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Insert string to the end of array of strings
|
* @brief Insert string to the end of array of strings
|
||||||
*
|
*
|
||||||
* @param arr
|
* @param arr
|
||||||
* @param length
|
* @param length
|
||||||
* @param value
|
* @param value
|
||||||
*/
|
*/
|
||||||
void insert_to_arr(char ***arr, size_t length, char *value)
|
void insert_to_arr(char ***arr, size_t length, char *value)
|
||||||
{
|
{
|
||||||
@ -18,9 +18,9 @@ void insert_to_arr(char ***arr, size_t length, char *value)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Free memory allocated for array of strings
|
* @brief Free memory allocated for array of strings
|
||||||
*
|
*
|
||||||
* @param arr
|
* @param arr
|
||||||
* @param length
|
* @param length
|
||||||
*/
|
*/
|
||||||
void free_arr(char **arr, int length)
|
void free_arr(char **arr, int length)
|
||||||
{
|
{
|
||||||
@ -32,11 +32,11 @@ void free_arr(char **arr, int length)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Check if array contains string
|
* @brief Check if array contains string
|
||||||
*
|
*
|
||||||
* @param arr
|
* @param arr
|
||||||
* @param length
|
* @param length
|
||||||
* @param str
|
* @param str
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
int check_if_contains(char **arr, size_t length, char *str)
|
int check_if_contains(char **arr, size_t length, char *str)
|
||||||
{
|
{
|
||||||
|
@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Find item by number
|
* @brief Find item by number
|
||||||
*
|
*
|
||||||
* @param list
|
* @param list
|
||||||
* @param n
|
* @param n
|
||||||
* @return llist_t*
|
* @return llist_t*
|
||||||
*/
|
*/
|
||||||
llist_t *find_item(llist_t *list, int n)
|
llist_t *find_item(llist_t *list, int n)
|
||||||
{
|
{
|
||||||
@ -16,10 +16,10 @@ llist_t *find_item(llist_t *list, int n)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Fill llist with array
|
* @brief Fill llist with array
|
||||||
*
|
*
|
||||||
* @param list
|
* @param list
|
||||||
* @param arr
|
* @param arr
|
||||||
* @param n
|
* @param n
|
||||||
*/
|
*/
|
||||||
void fill_with_arr(llist_t **list, char **arr, size_t n)
|
void fill_with_arr(llist_t **list, char **arr, size_t n)
|
||||||
{
|
{
|
||||||
@ -33,8 +33,8 @@ void fill_with_arr(llist_t **list, char **arr, size_t n)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Print whole list
|
* @brief Print whole list
|
||||||
*
|
*
|
||||||
* @param list
|
* @param list
|
||||||
*/
|
*/
|
||||||
void print_llist(llist_t *list)
|
void print_llist(llist_t *list)
|
||||||
{
|
{
|
||||||
@ -53,11 +53,11 @@ void print_llist(llist_t *list)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Add value to the specified pos of llist
|
* @brief Add value to the specified pos of llist
|
||||||
*
|
*
|
||||||
* @param head
|
* @param head
|
||||||
* @param pos
|
* @param pos
|
||||||
* @param value
|
* @param value
|
||||||
* @return llist_t*
|
* @return llist_t*
|
||||||
*/
|
*/
|
||||||
llist_t *add_to_list(llist_t **head, int pos, char *value)
|
llist_t *add_to_list(llist_t **head, int pos, char *value)
|
||||||
{
|
{
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
#include "../../include/utils_op/utils.h"
|
#include "../../include/utils_op/utils.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Prints error
|
* @brief Prints error
|
||||||
*
|
*
|
||||||
* @param {char *} msg
|
* @param {char *} msg
|
||||||
*/
|
*/
|
||||||
void err_msg(char *msg)
|
void err_msg(char *msg)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Error: %s\n", msg);
|
fprintf(stderr, "Error: %s\n", msg);
|
||||||
@ -12,10 +12,10 @@ void err_msg(char *msg)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Add str2 in front of str1
|
* @brief Add str2 in front of str1
|
||||||
*
|
*
|
||||||
* @param str1
|
* @param str1
|
||||||
* @param str2
|
* @param str2
|
||||||
* @return char*
|
* @return char*
|
||||||
*/
|
*/
|
||||||
char *prepend(char *dest, const char *prefix)
|
char *prepend(char *dest, const char *prefix)
|
||||||
{
|
{
|
||||||
@ -23,7 +23,7 @@ char *prepend(char *dest, const char *prefix)
|
|||||||
size_t prefix_len = strlen(prefix);
|
size_t prefix_len = strlen(prefix);
|
||||||
|
|
||||||
dest = realloc(dest, orig_len + prefix_len + 1);
|
dest = realloc(dest, orig_len + prefix_len + 1);
|
||||||
|
|
||||||
memmove(dest + prefix_len, dest, orig_len + 1);
|
memmove(dest + prefix_len, dest, orig_len + 1);
|
||||||
|
|
||||||
memcpy(dest, prefix, prefix_len);
|
memcpy(dest, prefix, prefix_len);
|
||||||
@ -33,9 +33,9 @@ char *prepend(char *dest, const char *prefix)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the status message object
|
* @brief Get the status message object
|
||||||
*
|
*
|
||||||
* @param status_code
|
* @param status_code
|
||||||
* @return char*
|
* @return char*
|
||||||
*/
|
*/
|
||||||
char *get_status_message(int status_code)
|
char *get_status_message(int status_code)
|
||||||
{
|
{
|
||||||
@ -73,9 +73,9 @@ char *to_lower(char *str)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Remove unneded spaces at the begining and ending of string
|
* @brief Remove unneded spaces at the begining and ending of string
|
||||||
*
|
*
|
||||||
* @param str
|
* @param str
|
||||||
* @return char*
|
* @return char*
|
||||||
*/
|
*/
|
||||||
char *trim(char *str)
|
char *trim(char *str)
|
||||||
{
|
{
|
||||||
@ -106,10 +106,10 @@ char *repair_spaces(char *str)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Gets the list of files and directories at the specified path
|
* @brief Gets the list of files and directories at the specified path
|
||||||
*
|
*
|
||||||
* @param dir_list
|
* @param dir_list
|
||||||
* @param path
|
* @param path
|
||||||
* @return ssize_t
|
* @return ssize_t
|
||||||
*/
|
*/
|
||||||
ssize_t get_dir_list(char ***dir_list, char *path)
|
ssize_t get_dir_list(char ***dir_list, char *path)
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user