rgncycle-libulfius/http-get-param/main.c

406 lines
12 KiB
C

/*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"
#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);
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);
int callback_set_entity (const struct _u_request * request, struct _u_response * response, void * user_data);
/**
* 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);
//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
ulfius_add_endpoint_by_val(&instance, "GET", ROUTE_ENTITIES, "/update", 0, &callback_set_entity, (void *)conn);
// 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);
ulfius_set_string_body_response(response, 200, "This is the /entity/:id/info route: Param error!");
}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);
ulfius_set_string_body_response(response, 200, "This is the /entity/:id/info route: Dba error!");
}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 update an existing entity
*/
int callback_set_entity (const struct _u_request * request, struct _u_response * response, void * user_data) {
/*declarations*/
//TODO
}
/**
* 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);
ulfius_set_string_body_response(response, 200, "This is the /entities route: Dba error!");
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;
}