2023-03-17 15:23:33 +01:00
/*to create API*/
# include <ulfius.h>
/*to connect to Postgresql*/
# include <libpq-fe.h>
/* to use JSON in response body*/
# include <jansson.h>
/*to use strcat*/
# include <string.h>
/*to use inet_ntoa*/
# include <sys/socket.h>
# include <netinet/in.h>
# include <arpa/inet.h>
/*to use *print**/
# include <stdio.h>
/*to use struct pq_confo*/
# include "pqconfo.h"
/*to use file2char*/
# include "file2char.h"
2023-03-20 15:34:10 +01:00
# define PREFIX " / test"
2023-03-17 15:23:33 +01:00
# define ROUTE_HELLO " / hello"
# define ROUTE_ENTITIES " / entities"
# define PREFIX " / test"
/**
* callback functions declaration
*/
int callback_get_hello ( const struct _u_request * request , struct _u_response * response , void * user_data ) ;
int callback_default ( const struct _u_request * request , struct _u_response * response , void * user_data ) ;
2023-03-20 15:34:10 +01:00
int callback_create_entity ( const struct _u_request * request , struct _u_response * response , void * user_data ) ;
2023-03-17 15:23:33 +01:00
int callback_get_entities ( const struct _u_request * request , struct _u_response * response , void * user_data ) ;
int callback_get_entity ( const struct _u_request * request , struct _u_response * response , void * user_data ) ;
2023-03-20 15:34:10 +01:00
int callback_update_entity ( const struct _u_request * request , struct _u_response * response , void * user_data ) ;
int callback_delete_entity ( const struct _u_request * request , struct _u_response * response , void * user_data ) ;
2023-03-17 15:23:33 +01:00
2023-03-20 15:34:10 +01:00
int callback_all_test_foo ( const struct _u_request * request , struct _u_response * response , void * user_data ) ;
2023-03-17 15:23:33 +01:00
/**
* decode a u_map into a string
*/
char * print_map ( const struct _u_map * map ) {
/*declaration*/
char * line , * to_return = NULL ;
const char * * keys , * value ;
int len , i ;
if ( map ! = NULL ) {
keys = u_map_enum_keys ( map ) ;
for ( i = 0 ; keys [ i ] ! = NULL ; i + + ) {
value = u_map_get ( map , keys [ i ] ) ;
len = snprintf ( NULL , 0 , " key is %s, value is %s " , keys [ i ] , value ) ;
line = malloc ( ( size_t ) ( len + 1 ) ) ;
snprintf ( line , ( size_t ) ( len + 1 ) , " key is %s, value is %s " , keys [ i ] , value ) ;
if ( to_return ! = NULL ) {
len = ( int ) ( strlen ( to_return ) + strlen ( line ) + 1 ) ;
to_return = realloc ( to_return , ( size_t ) ( len + 1 ) ) ;
if ( strlen ( to_return ) > 0 ) {
//concatenate strings
strcat ( to_return , " \n " ) ;
}
} else {
to_return = malloc ( ( strlen ( line ) + 1 ) ) ;
to_return [ 0 ] = 0 ;
}
strcat ( to_return , line ) ;
free ( line ) ;
}
return to_return ;
} else {
return NULL ;
}
}
int main ( int argc , char * argv [ ] ) {
/*declaration*/
struct _u_instance instance ;
int frameworkRet , apiPort , apiPortRet , cx ;
/*TODO How is const defined?*/
const int pqConInfoSize = 100 ;
char pqConInfo [ pqConInfoSize ] ;
int pqConfoRet ;
const struct pq_confo * pqConfo ;
printf ( " main() Started... \n " ) ;
if ( argc ! = 3 ) {
fprintf ( stderr , " Usage: ./main <API port> <path to config file> \n " ) ;
return 1 ;
}
printf ( " main() argv[1]: %s \n " , argv [ 1 ] ) ;
printf ( " main() argv[2]: %s \n " , argv [ 2 ] ) ;
/*get Postgresql connection info from config*/
/*TODO Validate argv!*/
pqConfoRet = get_pq_confo ( argv [ 2 ] , & pqConfo ) ;
if ( pqConfoRet ) {
fprintf ( stderr , " Postgresql connection information are NOT valid \n " ) ;
return 2 ;
} else {
fprintf ( stdout , " pqConfoRet: %d \n " , pqConfoRet ) ;
}
/*create pq connection info*/
cx = snprintf ( pqConInfo , pqConInfoSize , " postgresql://%s:%s@%s:%d/%s " , pqConfo - > user , pqConfo - > secret , pqConfo - > host , pqConfo - > port , pqConfo - > db ) ;
if ( 0 > cx & & cx > = pqConInfoSize ) {
fprintf ( stderr , " main() error creating pqConInfo \n " ) ;
/*free Postgresql connection info*/
pqConfoRet = free_pq_confo ( & pqConfo ) ;
if ( pqConfoRet ) {
fprintf ( stderr , " Postgresql connection information are NOT free \n " ) ;
return 3 ;
}
return 4 ;
}
/*free Postgresql connection info*/
pqConfoRet = free_pq_confo ( & pqConfo ) ;
if ( pqConfoRet ) {
fprintf ( stderr , " Postgresql connection information are NOT free \n " ) ;
return 5 ;
}
/*store command line argument in variable*/
/*validate user input*/
/*omit injection*/
/*stream:argv*/
apiPortRet = sscanf ( argv [ 1 ] , " %d " , & apiPort ) ;
/*valid user input:1 successfully filled item*/
if ( apiPortRet ! = 1 ) {
fprintf ( stderr , " The argument must be an integer \n " ) ;
return 6 ;
}
if ( apiPort < 0 ) {
fprintf ( stderr , " Error passing a negative port \n " ) ;
exit ( 1 ) ;
}
printf ( " main() port: %d \n " , apiPort ) ;
/*TODO Is mem leaked when connection to pq failes?*/
if ( ulfius_init_instance ( & instance , apiPort , NULL , NULL ) ! = U_OK ) {
printf ( " main() Error ulfius_init_instance, abort " ) ;
return 7 ;
}
/*connect to database*/
//TODO This is a synchronous call! Shall we block the rest?
PGconn * conn = PQconnectdb ( pqConInfo ) ;
if ( PQstatus ( conn ) = = CONNECTION_BAD ) {
fprintf ( stderr , " Connection to database failed: %s \n " ,
PQerrorMessage ( conn ) ) ;
PQfinish ( conn ) ;
return 8 ;
} else if ( PQstatus ( conn ) = = CONNECTION_OK ) {
printf ( " main() connected to database \n " ) ;
} else {
printf ( " main() connection status NOT known \n " ) ;
}
//TODO Why?
u_map_put ( instance . default_headers , " Access-Control-Allow-Origin " , " * " ) ;
// Maximum body size sent by the client is 1 Kb
instance . max_post_body_size = 1024 ;
// Endpoint list declaration
ulfius_add_endpoint_by_val ( & instance , " GET " , ROUTE_HELLO , NULL , 0 , & callback_get_hello , NULL ) ;
2023-03-20 15:34:10 +01:00
//user_data: pass pq connection information to callback
ulfius_add_endpoint_by_val ( & instance , " POST " , ROUTE_ENTITIES , " /create " , 0 , & callback_create_entity , ( void * ) conn ) ;
2023-03-17 15:23:33 +01:00
//user_data: pass pq connection information to callback
ulfius_add_endpoint_by_val ( & instance , " GET " , ROUTE_ENTITIES , " /info " , 0 , & callback_get_entities , ( void * ) conn ) ;
//user_data: pass pq connection information to callback
ulfius_add_endpoint_by_val ( & instance , " GET " , ROUTE_ENTITIES , " /:id/info " , 0 , & callback_get_entity , ( void * ) conn ) ;
//user_data: pass pq connection information to callback
2023-03-20 15:34:10 +01:00
ulfius_add_endpoint_by_val ( & instance , " POST " , ROUTE_ENTITIES , " /:id/update " , 0 , & callback_update_entity , ( void * ) conn ) ;
//user_data: pass pq connection information to callback
ulfius_add_endpoint_by_val ( & instance , " DELETE " , ROUTE_ENTITIES , " /:id/delete " , 0 , & callback_delete_entity , ( void * ) conn ) ;
ulfius_add_endpoint_by_val ( & instance , " GET " , PREFIX , " /param/:foo " , 0 , & callback_all_test_foo , " user data 1 " ) ;
ulfius_add_endpoint_by_val ( & instance , " POST " , PREFIX , " /param/:foo " , 0 , & callback_all_test_foo , " user data 2 " ) ;
ulfius_add_endpoint_by_val ( & instance , " PUT " , PREFIX , " /param/:foo " , 0 , & callback_all_test_foo , " user data 3 " ) ;
ulfius_add_endpoint_by_val ( & instance , " DELETE " , PREFIX , " /param/:foo " , 0 , & callback_all_test_foo , " user data 4 " ) ;
2023-03-17 15:23:33 +01:00
// default_endpoint declaration
ulfius_set_default_endpoint ( & instance , & callback_default , NULL ) ;
// Start the framework
// Open an http connection
frameworkRet = ulfius_start_framework ( & instance ) ;
if ( frameworkRet = = U_OK ) {
printf ( " main() Start framework on port %d \n " , instance . port ) ;
/*wait*/
while ( 1 ) {
sleep ( 3600 ) ;
}
} else {
printf ( " main() Error starting framework; frameworkRet: %d \n " , frameworkRet ) ;
}
printf ( " main() End framework \n " ) ;
/*clean up API*/
ulfius_stop_framework ( & instance ) ;
ulfius_clean_instance ( & instance ) ;
/*clean up Postgresql connection*/
PQfinish ( conn ) ;
printf ( " main() Done. \n " ) ;
return 0 ;
}
/**
* Callback function that put a " Hello World! " string in the response
*/
int callback_get_hello ( const struct _u_request * request , struct _u_response * response , void * user_data ) {
ulfius_set_string_body_response ( response , 200 , " Hello World! " ) ;
return U_CALLBACK_CONTINUE ;
}
/**
* Default callback function called if no endpoint has a match
*/
int callback_default ( const struct _u_request * request , struct _u_response * response , void * user_data ) {
ulfius_set_string_body_response ( response , 404 , " Page not found, do what you want " ) ;
return U_CALLBACK_CONTINUE ;
}
/**
* Callback function for route / entities to reply the entity at question
*/
int callback_get_entity ( const struct _u_request * request , struct _u_response * response , void * user_data ) {
/*declarations*/
char * url_params ;
char * response_body ;
int r ;
const char * * keys ;
const char * value ;
char * line ;
int len ;
int i ;
PGconn * conn ;
char * prepStmEntities ;
PGresult * pqRes ;
int nbRecords , nbFields ;
json_t * arrayRecords , * arrayFields , * stringField ;
printf ( " callback_get_entity() Started... \n " ) ;
url_params = print_map ( request - > map_url ) ;
r = asprintf (
& response_body ,
" parameters from the url are \n %s \n \n " ,
url_params ) ;
//printf("callback_get_entity() %d characters generated\n",r);
if ( request - > map_url ! = NULL ) {
keys = u_map_enum_keys ( request - > map_url ) ;
if ( keys [ 0 ] = = NULL ) {
//TODO error
printf ( " callback_get_entity() param NOT available \n " ) ;
//TODO Why this casting?
( void ) ( request ) ;
( void ) ( user_data ) ;
2023-03-20 15:34:10 +01:00
ulfius_set_string_body_response ( response , 500 , " This is the /entity/:id/info route: Param error! " ) ;
2023-03-17 15:23:33 +01:00
} else {
value = u_map_get ( request - > map_url , keys [ 0 ] ) ;
//printf("callback_get_entity() key is %s, value is %s\n", keys[0], value);
conn = ( PGconn * ) user_data ;
/*define prepared statement*/
len = snprintf ( NULL , 0 , " SELECT * FROM entities WHERE id=%s; " , value ) ;
prepStmEntities = malloc ( ( size_t ) ( len + 1 ) ) ;
//TODO Release prepStmEntities!
snprintf ( prepStmEntities , ( size_t ) ( len + 1 ) , " SELECT * FROM entities WHERE id=%s; " , value ) ;
//printf("callback_get_entity() prepStmEntities: %s\n",prepStmEntities);
/*create prepared statement*/
/*conn:connection*/
/*stm:statement*/
/*0:number of passed parameters*/
/*NULL: server figures out parameter type*/
/*paramValues:pointer of an array of strings containing parameters*/
/*NULL:relevant for binary parameters*/
/*NULL:relevant for binary parameters*/
/*0:obtain result in text format*/
//TODO How to pass params?
pqRes = PQexecParams ( conn , prepStmEntities , 0 , NULL , NULL , NULL , NULL , 0 ) ;
if ( PQresultStatus ( pqRes ) ! = PGRES_TUPLES_OK ) {
/*handle error*/
printf ( " callback_get_entity() No data retrieved \n " ) ;
/*clean up result*/
PQclear ( pqRes ) ;
//TODO Why this casting?
( void ) ( request ) ;
( void ) ( user_data ) ;
2023-03-20 15:34:10 +01:00
ulfius_set_string_body_response ( response , 500 , " This is the /entity/:id/info route: Dba error! " ) ;
2023-03-17 15:23:33 +01:00
} else {
/*evaluate result*/
nbRecords = PQntuples ( pqRes ) ;
//printf("callback_get_entity() nbRecords: %d\n",nbRecords);
nbFields = PQnfields ( pqRes ) ;
arrayRecords = json_array ( ) ;
for ( int i = 0 ; i < nbRecords ; i + + ) {
arrayFields = json_array ( ) ;
for ( int j = 0 ; j < nbFields ; j + + ) {
stringField = json_string ( PQgetvalue ( pqRes , i , j ) ) ;
json_array_append ( arrayFields , stringField ) ;
json_decref ( stringField ) ;
}
json_array_append ( arrayRecords , arrayFields ) ;
json_decref ( arrayFields ) ;
}
/*clean up result*/
PQclear ( pqRes ) ;
/*set response body*/
ulfius_set_json_body_response ( response , 200 , arrayRecords ) ;
/*clean up JSON*/
json_decref ( arrayRecords ) ;
//TODO Why this casting?
( void ) ( request ) ;
( void ) ( user_data ) ;
}
}
}
/*clean up remainings*/
free ( url_params ) ;
free ( response_body ) ;
printf ( " callback_get_entity() Done. \n " ) ;
return U_CALLBACK_CONTINUE ;
}
/**
* Callback function for route / entities to reply all entities
*/
int callback_get_entities ( const struct _u_request * request , struct _u_response * response , void * user_data ) {
/*declarations*/
PGconn * conn ;
char * prepStmEntities ;
PGresult * pqRes ;
int nbRecords , nbFields ;
json_t * arrayRecords , * arrayFields , * stringField ;
printf ( " callback_get_entities() Started... \n " ) ;
conn = ( PGconn * ) user_data ;
/*define prepared statement*/
prepStmEntities = " SELECT * FROM entities; " ;
/*create prepared statement*/
/*conn:connection*/
/*stm:statement*/
/*1:number of passed parameters*/
/*NULL: server figures out parameter type*/
/*paramValues:pointer of an array of strings containing parameters*/
/*NULL:relevant for binary parameters*/
/*NULL:relevant for binary parameters*/
/*0:obtain result in text format*/
pqRes = PQexecParams ( conn , prepStmEntities , 0 , NULL , NULL , NULL , NULL , 0 ) ;
if ( PQresultStatus ( pqRes ) ! = PGRES_TUPLES_OK ) {
printf ( " callback_get_entities() No data retrieved \n " ) ;
/*clean up result*/
PQclear ( pqRes ) ;
//TODO Why this casting?
( void ) ( request ) ;
( void ) ( user_data ) ;
2023-03-20 15:34:10 +01:00
ulfius_set_string_body_response ( response , 500 , " This is the /entities route: Dba error! " ) ;
2023-03-17 15:23:33 +01:00
printf ( " callback_get_entities() Done. \n " ) ;
return U_CALLBACK_CONTINUE ;
}
/*evaluate result*/
nbRecords = PQntuples ( pqRes ) ;
nbFields = PQnfields ( pqRes ) ;
arrayRecords = json_array ( ) ;
for ( int i = 0 ; i < nbRecords ; i + + ) {
arrayFields = json_array ( ) ;
for ( int j = 0 ; j < nbFields ; j + + ) {
/*not password, verification_token_id, created_account*/
if ( j ! = 6 & & j ! = 8 & & j ! = 10 ) {
stringField = json_string ( PQgetvalue ( pqRes , i , j ) ) ;
json_array_append ( arrayFields , stringField ) ;
json_decref ( stringField ) ;
}
}
json_array_append ( arrayRecords , arrayFields ) ;
json_decref ( arrayFields ) ;
}
/*clean up result*/
PQclear ( pqRes ) ;
/*set response body*/
ulfius_set_json_body_response ( response , 200 , arrayRecords ) ;
/*clean up JSON*/
json_decref ( arrayRecords ) ;
//TODO Why this casting?
( void ) ( request ) ;
( void ) ( user_data ) ;
printf ( " callback_get_entities() Done. \n " ) ;
return U_CALLBACK_CONTINUE ;
}
2023-03-20 15:34:10 +01:00
int callback_delete_entity ( const struct _u_request * request , struct _u_response * response , void * user_data ) { } ;
int callback_update_entity ( const struct _u_request * request , struct _u_response * response , void * user_data ) { } ;
int callback_create_entity ( const struct _u_request * request , struct _u_response * response , void * user_data ) {
/*declarations*/
int r ;
2023-03-24 14:50:27 +01:00
const char * * keys = NULL ;
const char * value = NULL ;
char * line = NULL ;
2023-03-20 15:34:10 +01:00
int len ;
int i ;
2023-03-24 14:50:27 +01:00
PGconn * conn = NULL ;
char * prepStmEntities = NULL ;
PGresult * pqRes = NULL ;
2023-03-20 15:34:10 +01:00
printf ( " callback_create_entity() Started... \n " ) ;
if ( request - > map_post_body ! = NULL ) {
keys = u_map_enum_keys ( request - > map_post_body ) ;
if ( keys [ 0 ] = = NULL ) {
//TODO error
printf ( " callback_create_entity() param NOT available \n " ) ;
//TODO Why this casting?
( void ) ( request ) ;
( void ) ( user_data ) ;
ulfius_set_string_body_response ( response , 500 , " This is the /entity/create route: HTTP POST data error! " ) ;
} else {
value = u_map_get ( request - > map_post_body , keys [ 0 ] ) ;
2023-03-24 14:50:27 +01:00
if ( value = = NULL ) {
//TODO error
printf ( " callback_create_entity() key NOT available in request map \n " ) ;
//TODO Why this casting?
( void ) ( request ) ;
( void ) ( user_data ) ;
ulfius_set_string_body_response ( response , 500 , " This is the /entity/create route: HTTP POST key error! " ) ;
} else {
printf ( " callback_create_entity() key is %s, value is %s \n " , keys [ 0 ] , value ) ;
conn = ( PGconn * ) user_data ;
/*define prepared statement*/
len = snprintf ( NULL , 0 ,
2023-03-20 15:34:10 +01:00
" INSERT INTO entities (name) VALUES ('%s') RETURNING *; "
, value ) ;
2023-03-24 14:50:27 +01:00
prepStmEntities = malloc ( ( size_t ) ( len + 1 ) ) ;
//TODO Release prepStmEntities!
snprintf ( prepStmEntities , ( size_t ) ( len + 1 ) ,
2023-03-20 15:34:10 +01:00
" INSERT INTO entities (name) VALUES ('%s') RETURNING *; "
, value ) ;
2023-03-24 14:50:27 +01:00
printf ( " callback_create_entity() prepStmEntities: %s \n " , prepStmEntities ) ;
/*create prepared statement*/
2023-03-20 15:34:10 +01:00
/*conn:connection*/
/*stm:statement*/
/*0:number of passed parameters*/
/*NULL: server figures out parameter type*/
/*paramValues:pointer of an array of strings containing parameters*/
/*NULL:relevant for binary parameters*/
/*NULL:relevant for binary parameters*/
/*0:obtain result in text format*/
//TODO How to pass params?
2023-03-24 14:50:27 +01:00
pqRes = PQexecParams ( conn , prepStmEntities , 0 , NULL , NULL , NULL , NULL , 0 ) ;
if ( PQresultStatus ( pqRes ) ! = PGRES_TUPLES_OK ) {
2023-03-20 15:34:10 +01:00
/*handle error*/
printf ( " callback_create_entity() No data retrieved \n " ) ;
ulfius_set_string_body_response ( response , 500 , " This is the /entity/create route: Dba error! " ) ;
/*clean up result*/
PQclear ( pqRes ) ;
//TODO Why this casting?
2023-03-24 14:50:27 +01:00
} else {
/*evaluate result*/
int nbRecords = PQntuples ( pqRes ) ;
printf ( " callback_get_entity() nbRecords: %d \n " , nbRecords ) ;
/*clean up result*/
PQclear ( pqRes ) ;
}
2023-03-20 15:34:10 +01:00
}
}
} else {
printf ( " callback_create_entity() HTTP POST request error! \n " ) ;
ulfius_set_string_body_response ( response , 400 , " This is the /entity/create route: HTTP POST request error! " ) ;
}
/*clean up remainings*/
printf ( " callback_create_entity() Done. \n " ) ;
//TODO Why this casting?
( void ) ( request ) ;
( void ) ( user_data ) ;
ulfius_set_string_body_response ( response , 200 , " Done. \n " ) ;
return U_CALLBACK_CONTINUE ;
}
/**
* Callback function that put " Hello World! " and all the data sent by the client in the response as string ( http method , url , params , cookies , headers , post , json , and user specific data in the response
*/
int callback_all_test_foo ( const struct _u_request * request , struct _u_response * response , void * user_data ) {
char * url_params = print_map ( request - > map_url ) , * headers = print_map ( request - > map_header ) , * cookies = print_map ( request - > map_cookie ) ;
char * post_params = print_map ( request - > map_post_body ) ;
char * response_body ;
int r = asprintf (
& response_body ,
" Hello World! \n \n method is %s \n url is %s \n \n parameters from the url are \n %s \n \n cookies are \n %s \n \n headers are \n %s \n \n post parameters are \n %s \n \n user data is %s \n \n client address is %s \n \n " ,
request - > http_verb , request - > http_url , post_params , cookies , headers , post_params , ( char * ) user_data , inet_ntoa ( ( ( struct sockaddr_in * ) request - > client_address ) - > sin_addr ) ) ;
ulfius_set_string_body_response ( response , 200 , response_body ) ;
free ( post_params ) ;
free ( headers ) ;
free ( cookies ) ;
free ( post_params ) ;
free ( response_body ) ;
return U_CALLBACK_CONTINUE ;
}