/*to create API*/ #include /*to connect to Postgresql*/ #include /* to use JSON in response body*/ #include /*to use strcat*/ #include /*to use inet_ntoa*/ #include #include #include /*to use *print**/ #include /*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 \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