17#include <sys/socket.h>
18#include <netinet/in.h>
35#define SYNOPSYS "<port> | -<option> [<parameter file>]"
36#define MAX_SIZE_REQUEST 2048
37#define MAX_SIZE_RESOURCE_NAME 256
39#define MAX_SIZE_FEED_BACK 1024
41#define BIG_BUFFER_SIZE (300*MILLION)
42#define MAX_SIZE_MESS 100000
43#define GPX_ROUTE_FILE_NAME "gpxroute.tmp"
48const char *
filter[] = {
".csv",
".pol",
".grb",
".grb2",
".log",
".txt",
".par",
".yaml",
".json", NULL};
57const int typeLevel [16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
130 strlcpy (str,
" [", maxLen);
131 for (
int k = 0; k < n; k++) {
134 snprintf (temp,
sizeof temp,
"[%.4lf, %.4lf]%s", lat, lon, (k < n-1) ?
", " :
"");
135 strlcat (str,temp, maxLen);
137 strlcat (str,
"]", maxLen);
148 printf (
"Grib Found: %s\n", strGrib);
152 printf (
"Grib loaded : %s\n", strGrib);
155 snprintf (checkMessage, maxLen,
"3: Error reading Grib: %s",
clientReq->
gribName);
171 printf (
"Current Grib Found: %s\n", strGrib);
173 printf (
"current readGrib: %s\n", strGrib);
176 printf (
"Current Grib loaded : %s\n", strGrib);
199 const double dlatDeg = lat1 - lat0;
200 const double dlonDeg = lon1 - lon0;
201 const double latMidRad = ((lat0 + lat1) * 0.5) *
DEG_TO_RAD;
202 const double dlatNm = dlatDeg * 60.0;
203 const double dlonNm = dlonDeg * 60.0 * cos(latMidRad);
205 return hypot(dlatNm, dlonNm);
237 const double epsilon = 1e-12;
242 const double dlat = lat1 - lat0;
243 const double dlon = lon1 - lon0;
244 if (fabs(dlat) < epsilon && fabs(dlon) < epsilon)
return true;
250 const double stepNm = 1.0;
253 int steps = (int)ceil(lengthNm / stepNm);
254 steps =
CLAMP (steps, 1, 2000);
257 for (
int i = 1; i < steps; i++) {
258 const double t = (double)i / (
double)steps;
259 const double lat = lat0 + t * (lat1 - lat0);
260 const double lon = lon0 + t * (lon1 - lon0);
279 for (
int i = 0; i <
route->
n - 2; i++) {
288 strlcpy (res,
"[\n", maxLen);
293 strlcat (res, str, maxLen);
295 strlcat (res,
"\n", maxLen);
298 strlcat (res,
"]\n", maxLen);
306 char *savePt = res + strlen (res);
307 g_strlcat (res,
",\n\"_isoc\": \n[\n", maxLen);
310 fprintf (stderr,
"In isochronesToStrJson: error in memory newIsoc allocation\n");
314 for (
int i = 0; i <
nIsoc; i += 1) {
320 if (index ==
isoDesc [i].size) index = 0;
323 for (
int k = 0; k < max; k++) {
325 snprintf (str,
sizeof str,
" [%.6lf, %.6lf, %d, %d, %d]%s\n",
329 snprintf (str,
sizeof str,
" ]%s\n", (i <
nIsoc -1) ?
"," :
"");
332 if (strlen (res) >= (maxLen -
sizeof str - 2)) {
335 strlcat (res,
",\n\"_Warning_1\": \"No isochrone sent because too big!\"\n", maxLen);
345 char *savePt = res + strlen (res);
346 g_strlcat (res,
",\n\"_isodesc\": \n[\n", maxLen);
348 for (
int i = 0; i <
nIsoc; i++) {
351 snprintf (str,
sizeof str,
" [%d, %d, %d, %d, %d, %.2lf, %.2lf, %.6lf, %.6lf]%s\n",
356 (i <
nIsoc - 1) ?
"," :
"");
359 if (strlen (res) >= (maxLen -
sizeof str - 2)) {
362 strlcat (res,
",\n\"_Warning_2\": \"No isoDesc sent because too big!\"\n", maxLen);
372 double twa = 0.0, hdg = 0.0, twd = 0.0;
374 if (
route->
n <= 0)
return NULL;
381 snprintf (res, maxLen,
383 "\"%s\": {\n\"duration\": %d,\n"
384 "\"totDist\": %.2lf,\n"
385 "\"routingRet\": %d,\n"
386 "\"isocTimeStep\": %.2lf,\n"
387 "\"calculationTime\": %.4lf,\n"
388 "\"destinationReached\": %s,\n"
389 "\"lastPointInfo\": %s,\n"
390 "\"lastStepDuration\": [",
405 snprintf (str,
sizeof str,
407 "\"motorDist\": %.2lf, \"starboardDist\": %.2lf, \"portDist\": %.2lf,\n"
408 "\"nSailChange\": %d, \"nAmureChange\": %d,\n"
409 "\"bottomLat\": %.2lf, \"leftLon\": %.2lf, \"topLat\": %.2lf, \"rightLon\": %.2lf,\n"
410 "\"polar\": \"%s\",\n"
411 "\"wavePolar\": \"%s\",\n"
412 "\"grib\": \"%s\",\n"
413 "\"currentGrib\": \"%s\",\n"
422 free (wavePolarBaseName);
424 free (gribCurrentBaseName);
426 for (
int i = 0; i <
route->
n; i++) {
428 fprintf (stderr,
"In routeToJson sog > LIMIT_SOG, lat = %.6lf, lon = %.6lf, sog = %.6lf, od = %.6lf; ld = %.6lf\n",
433 if (hdg < 0) hdg += 360;
435 if (twd < 0) twd += 360;
438 printf (
"In routeToJson, Error: toIndexWp outside range; %d\n",
route->
t[i].
toIndexWp);
440 snprintf (str,
sizeof str,
441 " [%d, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, \"%s\", %s, %d, %d]%s\n",
452 (i <
route-> n - 1) ?
"," :
""
461 fprintf (stderr,
"In routeToSrJson, checkRoute warns bad segment (no sea): %d\n", nSeg);
462 snprintf (str,
sizeof str,
",\n\"_Warning_0\": \"route over sea or forbidden zone on segment: %d (%.2lf, %.2lf) to (%.2lf, %.2lf)\"\n",
469 printf (
"Before isochronesToStrCatJson in routeToJson\n");
478 return round(x * 10000.0) / 10000.0;
493 double u, v, g, w, twd, tws;
495 fprintf (stderr,
"In infoCoordToJson: tMax should be strictly positive\n");
496 snprintf (str,
sizeof str,
"{\"_Error\": \"tMax should ne strictly positive\"\n");
500 if (str [0] !=
'\0') {
501 snprintf (res, maxLen,
"{\"_Error\": \"%s\"}\n", str);
506 if (pFileName != NULL) pFileName += 1;
507 snprintf (res, maxLen,
"{\n"
508 " \"isSea\": %s,\n \"isSeaTolerant\": %s,\n \"inWind\": %s,\n \"inCurrent\": %s,\n"
509 " \"epochGribStart\": %ld,\n \"grib\": \"%s\",\n \"meteoArray\":\n [\n",
510 sea ?
"true" :
"false", seaTolerant ?
"true":
"false", wind ?
"true":
"false", current ?
"true":
"false",
511 epochGribStart, pFileName ? pFileName :
"");
513 for (
int i = 0; i <
tMax; i += 1) {
516 snprintf(str,
sizeof str,
" [%g, %g, %g, %g]%s\n",
round4(u),
round4(v),
round4(g),
round4(w), (i <
tMax - 1) ?
"," :
"");
517 strlcat (res, str, maxLen);
519 strlcat (res,
" ]\n}\n", maxLen);
530static bool getRealIPAddress (
const char* headers,
char* clientAddress,
size_t bufferSize) {
531 const char* headerName =
"X-Real-IP: ";
532 const char* headerStart = strstr (headers, headerName);
535 headerStart += strlen (headerName);
536 while (*headerStart ==
' ') headerStart++;
538 const char* headerEnd = strstr(headerStart,
"\r\n");
540 size_t ipLength = headerEnd - headerStart;
541 if (ipLength < bufferSize) {
542 g_strlcpy (clientAddress, headerStart, ipLength + 1);
546 fprintf (stderr,
"In getRealIPAddress: IP address length exceeds buffer size.");
551 clientAddress [0] =
'\0';
557 const char* headerName =
"User-Agent: ";
558 const char* userAgentStart = strstr (saveBuffer, headerName);
559 if (userAgentStart) {
560 userAgentStart += strlen (headerName);
561 const char* userAgentEnd = strstr (userAgentStart,
"\r\n");
563 return strndup (userAgentStart, userAgentEnd - userAgentStart);
572 const char* headerName =
"X-User-Level:";
573 const char* levelStart = strstr (buffer, headerName);
574 if (levelStart)
return atoi (levelStart + strlen (headerName));
584 printf (
"GFS allowed whatever the level for type 1 request\n");
597 return strcmp (fa->
name, fb->name);
603 if (fa->
mtime > fb->mtime)
return -1;
604 if (fa->
mtime < fb->mtime)
return 1;
605 return strcmp(fa->
name, fb->name);
610 if (
filter == NULL)
return true;
611 for (
int i = 0;
filter[i] != NULL; i++) {
624 snprintf (out, maxLen,
"{\"nearestPort\": \"%s\", \"idPort\": %d}\n", selectedPort, idPort);
626 else snprintf (out, maxLen,
"{\"error\": \"Tide File %s not found\"}\n",
par.
tidesFileName);
632 if (!s)
return strdup(
"");
633 const size_t len = strlen(s);
635 const size_t cap = len * 6 + 1;
636 char *out = (
char*)malloc(cap);
637 if (!out)
return NULL;
640 for (
size_t i = 0; i < len; i++) {
641 unsigned char c = (
unsigned char)s[i];
643 case '\"': *p++=
'\\'; *p++=
'\"';
break;
644 case '\\': *p++=
'\\'; *p++=
'\\';
break;
645 case '\b': *p++=
'\\'; *p++=
'b';
break;
646 case '\f': *p++=
'\\'; *p++=
'f';
break;
647 case '\n': *p++=
'\\'; *p++=
'n';
break;
648 case '\r': *p++=
'\\'; *p++=
'r';
break;
649 case '\t': *p++=
'\\'; *p++=
't';
break;
652 static const char hex[] =
"0123456789ABCDEF";
653 *p++=
'\\'; *p++=
'u'; *p++=
'0'; *p++=
'0';
654 *p++=hex[(c>>4)&0xF]; *p++=hex[c&0xF];
675static char *
listDirToStrJson (
char *root,
char *dir,
bool sortByName,
const char *pattern,
const char **
filter,
char *out,
size_t maxLen) {
679 const char *sep = (dir && dir [0] !=
'/') ?
"/" :
"";
680 if (maxLen == 0)
return out;
684 snprintf (fullPath,
sizeof fullPath,
"%s%s%s", root ? root :
"", sep, dir ? dir :
"");
686 DIR *d = opendir(fullPath);
688 fprintf(stderr,
"In listDirToStrJson Error opening directory '%s': %s\n", fullPath, strerror(errno));
689 snprintf (out, maxLen,
"{\"error\":\"Error opening directory\"}");
695 size_t n = 0, cap = 0;
698 while ((ent = readdir(d)) != NULL) {
699 const char *fileName = ent->d_name;
700 if (strcmp(fileName,
".") == 0 || strcmp(fileName,
"..") == 0)
continue;
706 snprintf (filePath,
sizeof filePath,
"%s/%s", fullPath, fileName);
709 if (stat(filePath, &st) != 0) {
710 fprintf(stderr,
"In listDirToStrJson Error retrieving info for '%s': %s\n", filePath, strerror(errno));
714 if (!S_ISREG(st.st_mode))
continue;
718 size_t newcap = cap ? cap * 2 : 32;
721 fprintf(stderr,
"listDirToStrJson: realloc échouée\n");
724 arr = tmp; cap = newcap;
726 arr[n].
name = strdup(fileName);
727 arr[n].
size = st.st_size;
728 arr[n].
mtime = st.st_mtime;
729 if (!arr[n].
name) { fprintf(stderr,
"OOM on strdup\n");
continue; }
737 snprintf (out, maxLen,
"[\n");
738 for (
size_t i = 0; i < n; i++) {
740 struct tm tm_buf, *tm_info;
741 tm_info = localtime_r(&arr[i].mtime, &tm_buf);
742 char time_str[20] =
"1970-01-01 00:00:00";
743 if (tm_info) strftime(time_str,
sizeof time_str,
"%Y-%m-%d %H:%M:%S", tm_info);
746 snprintf (line,
sizeof line,
" [\"%s\", %lld, \"%s\"]%s\n",
747 escaped ? escaped :
"", (
long long)arr[i].size, time_str, (i + 1 < n) ?
"," :
"");
753 for (
size_t i = 0; i < n; i++) free(arr[i].
name);
778 fprintf (stderr,
"In initContext, Error: Unable to read grib file: %s\n ",
par.
gribFileName);
794 fprintf (stderr,
"In initContext, Error readPolar: %s\n", errMessage);
800 fprintf (stderr,
"In initContext, Error readPolar: %s\n", errMessage);
802 printf (
"par.web : %s\n",
par.
web);
814 static char dateBuffer [100];
815 const time_t now = time (NULL);
816 struct tm *tm_info = gmtime(&now);
817 strftime (dateBuffer,
sizeof dateBuffer,
"%Y-%m-%d %H:%M:%S UTC", tm_info);
822static void handleFeedbackRequest (
const char *fileName,
const char *date,
const char *clientIPAddress,
const char *
string) {
823 FILE *file = fopen (fileName,
"a");
825 fprintf (stderr,
"handleFeedbackRequest, Error opening file: %s\n", fileName);
828 fprintf (file,
"%s; %s; \n%s\n\n", date, clientIPAddress,
string);
834static void logRequest (
const char* fileName,
const char *date,
int serverPort,
const char *remote_addr, \
835 char *dataReq,
const char *userAgent,
ClientRequest *client,
double duration) {
838 g_strlcpy (newUserAgent, userAgent,
sizeof newUserAgent);
840 char *startAgent = strchr (newUserAgent,
'(');
841 if (startAgent == NULL) startAgent = newUserAgent;
843 FILE *logFile = fopen (fileName,
"a");
844 if (logFile == NULL) {
845 fprintf (stderr,
"In logRequest, Error opening log file: %s\n", fileName);
851 fprintf (logFile,
"%s; %d; %-16.16s; %-40.40s; %2d; %6.2lf; %.50s\n",
852 date, serverPort, remote_addr, startAgent, client->
type, duration, dataReq);
874 if (parts == NULL)
return false;
876 for (
int i = 0; parts[i]; i++) {
881 char *boatsPart = parts[i] + strlen (
"boat=");
882 if (*boatsPart ==
'\0') {
887 char **boatsCoords =
g_strsplit (boatsPart,
";", -1);
891 if (sscanf(boatsCoords[i],
"%63[^,], %lf, %lf",
name, &lat, &lon) == 3) {
901 char *wpPart = parts[i] + strlen (
"waypoints=");
902 if (*wpPart ==
'\0') {
907 char **wpCoords =
g_strsplit (wpPart,
";", -1);
910 if (sscanf(wpCoords[i],
"%lf,%lf", &lat, &lon) == 2) {
933 else if (sscanf (parts[i],
"model=%255s",
clientReq->
model) == 1);
965 else fprintf (stderr,
"In decodeHttpReq Unknown value: %s\n", parts [i]);
976 checkMessage [0] =
'\0';
979 snprintf (checkMessage, maxLen,
"1: No boats or no Waypoints");
1009 printf (
"polar found: %s\n", strPolar);
1011 printf (
"read polar: %s\n", strPolar);
1014 printf (
"Polar loaded : %s\n", strPolar);
1024 printf (
"wave polar found: %s\n", strPolar);
1026 printf (
"read wave polar: %s\n", strPolar);
1029 printf (
"Wave Polar loaded : %s\n", strPolar);
1040 snprintf (checkMessage, maxLen,
"0: No grib file with model: %s",
clientReq->
model);
1045 if (checkMessage [0] !=
'\0')
return false;
1047 if (checkMessage [0] !=
'\0')
return false;
1060 snprintf (checkMessage, maxLen,
1061 "5: Competitor not in sea., name: %s, lat: %.6lf, lon: %.6lf",
1066 snprintf (checkMessage, maxLen,
1067 "6: Competitor not in Grib wind zone., grib: %s, bottomLat: %.2lf, leftLon: %.2lf, topLat: %.2lf, rightLon: %.2lf",
1069 free (gribBaseName);
1080 snprintf (checkMessage, maxLen,
1081 "7: WP or Dest. not in sea, lat: %.2lf, lon: %.2lf",
1086 snprintf (checkMessage, maxLen,
1087 "8: WP or Dest. not in Grib wind zone: %s, bottomLat: %.2lf, leftLon: %.2lf, topLat: %.2lf, rightLon: %.2lf",
1089 free (gribBaseName);
1100 snprintf (checkMessage, maxLen,
"9: start Time not in Grib time window");
1116 const char *ext = strrchr(path,
'.');
1117 if (!ext)
return "application/octet-stream";
1118 if (strcmp(ext,
".html") == 0)
return "text/html";
1119 if (strcmp(ext,
".css") == 0)
return "text/css";
1120 if (strcmp(ext,
".js") == 0)
return "application/javascript";
1121 if (strcmp(ext,
".png") == 0)
return "image/png";
1122 if (strcmp(ext,
".jpg") == 0)
return "image/jpeg";
1123 if (strcmp(ext,
".gif") == 0)
return "image/gif";
1124 if (strcmp(ext,
".txt") == 0)
return "text/plain";
1125 if (strcmp(ext,
".par") == 0)
return "text/plain";
1126 if (strcmp(ext,
"geojson") == 0)
return "application/geo+json; charset=utf-8";
1127 return "application/octet-stream";
1132 char filepath [512];
1133 snprintf (filepath,
sizeof filepath,
"%s%s",
par.
web, requested_path);
1135 char *q = strchr(filepath,
'?');
1137 printf (
"File Path: %s\n", filepath);
1141 if (stat (filepath, &st) == -1 || S_ISDIR(st.st_mode)) {
1142 const char *not_found =
"HTTP/1.1 404 Not Found\r\nContent-Length: 13\r\n\r\n404 Not Found";
1143 send (client_socket, not_found, strlen(not_found), 0);
1144 fprintf (stderr,
"In serveStaticFile, Error 404\n");
1149 const int file = open (filepath, O_RDONLY);
1151 const char *error =
"HTTP/1.1 500 Internal Server Error\r\nContent-Length: 21\r\n\r\n500 Internal Server Error";
1152 send (client_socket, error, strlen(error), 0);
1153 fprintf (stderr,
"In serveStaticFile, Error 500\n");
1159 snprintf (header,
sizeof header,
"HTTP/1.1 200 OK\r\nContent-Type: %s\r\nContent-Length: %ld\r\n\r\n",
1161 send (client_socket, header, strlen(header), 0);
1166 while ((bytesRead = read(file, buffer,
sizeof buffer)) > 0) {
1167 ssize_t totalSent = 0;
1168 while (totalSent < bytesRead) {
1169 ssize_t sent = send(client_socket, buffer + totalSent, bytesRead - totalSent, 0);
1173 fprintf(stderr,
"In serveStaticFile, Client disconnected while sending file\n");
1180 printf (
"✅ serveStaticFile OK\n");
1188 FILE *fp = fopen(
"/proc/self/status",
"r");
1193 while (fgets (line,
sizeof line, fp)) {
1194 if (strncmp (line,
"VmRSS:", 6) == 0) {
1196 sscanf (line + 6,
"%d", &mem);
1205static char *
testToJson (
int serverPort,
const char *clientIP,
const char *userAgent,
int level,
char *out,
size_t maxLen) {
1215 snprintf (out, maxLen,
1216 "{\n \"Prog-version\": \"%s, %s, %s\",\n"
1217 " \"API server port\": %d,\n"
1218 " \"Grib Reader\": \"%s\",\n"
1219 " \"Memory for Grib Wind\": \"%s\",\n"
1220 " \"Memory for Grib Current\": \"%s\",\n"
1221 " \"Compilation-date\": \"%s\",\n"
1223 " \"Memory usage in KB\": \"%s\",\n"
1224 " \"Client IP Address\": \"%s\",\n"
1225 " \"User Agent\": \"%s\",\n"
1226 " \"Authorization-Level\": %d\n}\n",
1228 strWind, strCurrent, __DATE__, getpid (), strMem, clientIP, userAgent, level
1236 const char *corsHeaders =
1237 "Access-Control-Allow-Origin: *\r\n"
1238 "Access-Control-Allow-Methods: POST, GET, OPTIONS\r\n"
1239 "Access-Control-Allow-Headers: Content-Type\r\n"
1240 "Access-Control-Expose-Headers: X-Shortnames\r\n";
1242 int n = snprintf(header,
sizeof header,
1243 "HTTP/1.1 200 OK\r\n"
1244 "Content-Type: application/octet-stream\r\n"
1245 "X-Shortnames: %s\r\n"
1247 "Content-Length: %zu\r\n"
1248 "Connection: close\r\n"
1250 shortnames, corsHeaders, len);
1252 if ((n < 0) || (
size_t)n >=
sizeof header) {
1253 fprintf (stderr,
"In sendBinaryResponse, Error n:%d\n", n);
1256 send(sock, header, (
size_t)n, 0);
1257 send(sock, data, len, 0);
1265 if (len < 5)
return;
1276 const char *date,
const char *clientIPAddress,
const char *userAgent,
char *outBuffer,
size_t maxLen) {
1282 char *strTemp = NULL;
1289 printf (
"Killed on port: %d, At: %s, By: %s\n", serverPort, date, clientIPAddress);
1290 snprintf (outBuffer, maxLen,
"{\n \"killed_on_port\": %d,\n \"date\": \"%s\",\n \"by\": \"%s\"\n}\n", serverPort, date, clientIPAddress);
1300 snprintf (outBuffer, maxLen,
"{\"_Error\": \"%s\"}\n",
"Routing failed");
1305 snprintf (outBuffer, maxLen,
"{\"_Error\": \"%s\"}\n", checkMessage);
1313 snprintf (outBuffer, maxLen,
"{\"_Error\": \"No Boat\"\n}\n");
1336 snprintf (outBuffer, maxLen,
"{\"_Error\": \"No grib with model: %s\"}\n",
clientReq->
model);
1341 else snprintf (outBuffer, maxLen,
"{\"_Error\": \"%s\"}\n",
"No Grib");
1350 writeParam (tempFileName,
false,
false, yaml);
1351 strTemp =
readTextFile (tempFileName, errMessage,
sizeof errMessage);
1352 if (strTemp == NULL) {
1353 snprintf (outBuffer, maxLen,
"{\n \"_Error\": \"%s\"\n}\n", errMessage);
1356 strlcat (outBuffer, strTemp, maxLen);
1365 snprintf (outBuffer, maxLen,
"{\n \"_Error\": \"Init failed\",\n \"serverPort\": %d\n}\n", serverPort);
1367 snprintf (outBuffer, maxLen,
"{\n \"message\": \"Init done\",\n \"serverPort\": %d\n}\n", serverPort);
1371 snprintf (outBuffer, maxLen,
"{\"_Feedback\": \"%s\"}\n",
"OK");
1375 strTemp =
readTextFile (tempFileName, errMessage,
sizeof errMessage);
1376 if (strTemp == NULL) {
1377 snprintf (outBuffer, maxLen,
"{\n \"_Error\": \"%s\"\n}\n", errMessage);
1380 strlcat (outBuffer, strTemp, maxLen);
1385 snprintf (outBuffer, maxLen,
"{\"_Error\": \"Reading Mark File %s\"}\n",
par.
marksFileName);
1391 else snprintf (outBuffer, maxLen,
"{\"_Error\": \"%s\"}\n",
"No coordinates found");
1398 if (!hasGrib && !hasCurrentGrib) {
1399 snprintf (outBuffer, maxLen,
"{\"_Error\": \"%s\"}\n",
"Either wind grib and current grib required");
1406 if (outBuffer [0] ==
'\0')
g_strlcpy (outBuffer,
"All is OK\n", maxLen);
1411 snprintf (outBuffer, maxLen,
"{\"_Error\": \"%s\"}\n",
"No route");
1414 strTemp =
readTextFile (tempFileName, errMessage,
sizeof errMessage);
1415 if (strTemp == NULL) {
1416 snprintf (outBuffer, maxLen,
"{\n \"_Error\": \"%s\"\n}\n", errMessage);
1419 strlcat (outBuffer, strTemp, maxLen);
1429 snprintf (outBuffer, maxLen,
"{\"_Error\": \"%s\"}\n",
"No Grib");
1438 printf(
"✅ Send Binary float array with: Len=%zu, Bytes=%zu, Shortnames=%s\n\n",
1439 dataLen, dataLen *
sizeof(
float), str);
1450static int sendAll (
int fd,
const void *buf,
size_t len) {
1451 const unsigned char *p = buf;
1453 const ssize_t n = send(fd, p, len, 0);
1455 if (errno == EINTR)
continue;
1456 if (errno == EAGAIN || errno == EWOULDBLOCK)
continue;
1466static bool handleClient (
int serverPort,
int clientFd,
struct sockaddr_in *client_addr) {
1473 const int bytes_read = recv (clientFd, buffer,
sizeof buffer - 1, 0);
1474 if (bytes_read <= 0) {
1477 buffer [bytes_read] =
'\0';
1479 g_strlcpy (saveBuffer, buffer,
sizeof buffer);
1481 if (!
getRealIPAddress (buffer, clientIPAddress,
sizeof clientIPAddress)) {
1483 char remoteAddr [INET_ADDRSTRLEN];
1484 inet_ntop (AF_INET, &(client_addr->sin_addr), remoteAddr, INET_ADDRSTRLEN);
1485 g_strlcpy (clientIPAddress, remoteAddr, INET_ADDRSTRLEN);
1489 char *requestLine = strtok (buffer,
"\r\n");
1490 if (!requestLine)
return false;
1493 if (strncmp (requestLine,
"POST", 4) != 0) {
1494 printf (
"📥 GET Request, static file: %s\n", requestLine);
1496 const char *requested_path = strchr (requestLine,
' ');
1497 if (!requested_path) {
1502 char *end_path = strchr (requested_path,
' ');
1507 if (strcmp(requested_path,
"/") == 0) {
1508 requested_path =
"/index.html";
1516 char *postData = strstr (saveBuffer,
"\r\n\r\n");
1517 if (postData == NULL) {
1523 printf (
"🟠POST Request:\n%s\n", postData);
1530 const char *errorResponse =
"HTTP/1.1 400 Bad Request\r\nContent-Type: text/plain\r\n\r\nError";
1531 fprintf (stderr,
"In handleClient, Error: %s\n", errorResponse);
1532 send (clientFd, errorResponse, strlen(errorResponse), 0);
1538 const char *corsHeaders =
"Access-Control-Allow-Origin: *\r\n"
1539 "Access-Control-Allow-Methods: POST, GET, OPTIONS\r\n"
1540 "Access-Control-Allow-Headers: Content-Type\r\n";
1550 const size_t bigBufferLen = strlen (
bigBuffer);
1551 const int headerLen = snprintf (header,
sizeof header,
1552 "HTTP/1.1 200 OK\r\n"
1553 "Content-Type: application/json\r\n"
1555 "Content-Length: %zu\r\n"
1557 corsHeaders, bigBufferLen);
1559 if (
sendAll (clientFd, header, (
size_t) headerLen) < 0)
return false;
1561 printf (
"✅ Response sent to client. Size: %zu\n\n", headerLen + bigBufferLen);
1564 const double duration =
monotonic () - start;
1566 if (userAgent) free (userAgent);
1575 int serverFd, clientFd;
1576 struct sockaddr_in address;
1577 int addrlen =
sizeof address;
1578 int serverPort, opt = 1;
1580 signal(SIGPIPE, SIG_IGN);
1584 return EXIT_FAILURE;
1587 if (setlocale (LC_ALL,
"C") == NULL) {
1588 fprintf (stderr,
"In main, Error: setlocale failed");
1589 return EXIT_FAILURE;
1592 if (argc <= 1 || argc > 3) {
1593 fprintf (stderr,
"Synopsys: %s %s\n", argv [0],
SYNOPSYS);
1594 return EXIT_FAILURE;
1603 return EXIT_FAILURE;
1606 if (argv[1][0] ==
'-') {
1608 return EXIT_SUCCESS;
1612 serverPort = atoi (argv [1]);
1613 if (serverPort < 80 || serverPort > 9000) {
1614 fprintf (stderr,
"In main, Error: port server not in range\n");
1615 return EXIT_FAILURE;
1619 serverFd = socket (AF_INET, SOCK_STREAM, 0);
1621 perror (
"In main, Error: socket failed");
1622 return EXIT_FAILURE;
1626 if (setsockopt (serverFd, SOL_SOCKET, SO_REUSEADDR, &opt,
sizeof opt) < 0) {
1627 perror (
"In main, Error setsockopt");
1629 return EXIT_FAILURE;
1633 address.sin_family = AF_INET;
1634 address.sin_addr.s_addr = INADDR_ANY;
1635 address.sin_port = htons (serverPort);
1638 if (bind (serverFd, (
struct sockaddr *)&address,
sizeof address) < 0) {
1639 perror (
"In main, Error socket bind");
1641 return EXIT_FAILURE;
1645 if (listen (serverFd, 3) < 0) {
1646 perror (
"In main: Error listening");
1648 return EXIT_FAILURE;
1650 const double elapsed =
monotonic () - start;
1651 printf (
"✅ Loaded in...: %.2lf seconds. Server listen on port: %d, Pid: %d\n", elapsed, serverPort, getpid ());
1654 if ((clientFd = accept (serverFd, (
struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0) {
1655 perror (
"In main: Error accept");
1657 return EXIT_FAILURE;
1673 return EXIT_SUCCESS;
void * routingLaunch()
launch routing with parameters
SailRoute route
store sail route calculated in engine.c by routing
bool exportRouteToGpx(const SailRoute *route, const char *fileName)
export route with GPX format
Pp * isocArray
global variables
static void g_strdelimit(char *s, const char *delims, char repl)
static bool g_str_has_prefix(const char *s, const char *prefix)
Equivalent to Glib g_str_has_prefix.
#define g_strlcat(dest, src, size)
static void g_strfreev(char **strv)
Free array with elements (like GLib g_strfreev)
static bool g_str_has_suffix(const char *s, const char *suffix)
Equivalent to Glib g_str_has_suffix.
#define g_strlcpy(dest, src, size)
static char * g_path_get_basename(const char *path)
close to Glib g_path_get_basename
static char ** g_strsplit(const char *string, const char *delimiter, int max_tokens)
Clone of GLib g_strsplit.
static char * g_strstrip(char *s)
Équivalent to GLIB g_strstrip: trim in place, return s.
bool uvPresentGrib(const Zone *zone)
true if u and v (or uCurr, vCurr) are in zone
bool isPresentGrib(const Zone *zone, const char *name)
true if shortname in zone
bool checkGribToStr(bool hasCurrentGrib, char *buffer, size_t maxLen)
check Grib information and write report in the buffer return false if something wrong
char * gribToStrJson(const char *fileName, char *out, size_t maxLen)
write grib meta information in string
float * buildUVGWarray(const Zone *zone, const char *initialOfNames, const FlowP *gribData, size_t *outNValues)
return array of outNvalues floats n = nTimeStamp * nbLat * nbLon * nShortNames values = [u1,...
void findWindGrib(double lat, double lon, double t, double *u, double *v, double *gust, double *w, double *twd, double *tws)
use findflow to get wind and waves
static bool isInZone(double lat, double lon, Zone *zone)
true if P (lat, lon) is within the zone
static double fTwa(double heading, double twd)
return TWA between -180 and 180 note : tribord amure if twa < 0
static bool isSea(char *isSeaArray, double lat, double lon)
this file contains small inlines functions to be included in source files
static bool isSeaTolerant(char *isSeaArray, double lat, double lon)
say if point is in sea
void optionManage(char option)
compilation: gcc -c option.c
char * polToStrJson(bool report, const char *fileName, const char *objName, char *out, size_t maxLen)
write polar information in string Json format
bool readPolar(const char *fileName, PolMat *mat, PolMat *sailPolMat, char *errMessage, size_t maxLen)
Launch readPolarCsv or readPolarJson according to type.
static bool updateWindGrib(ClientRequest *clientReq, char *checkMessage, size_t maxLen)
update Grib file if required
static char * nearestPortToStrJson(double lat, double lon, char *out, size_t maxLen)
return neareAst port to lat, lon
static bool updateCurrentGrib(ClientRequest *clientReq, char *checkMessage, size_t maxLen)
update current file if required
static char * isochronesToStrCatJson(char *res, size_t maxLen)
generate json description of isochrones.
int main(int argc, char *argv[])
main server first argument (mandatory): port number second argument (optionnal): parameter file
static bool checkParamAndUpdate(ClientRequest *clientReq, char *checkMessage, size_t maxLen)
check validity of parameters
static char * jsonEscapeStrdup(const char *s)
Escape JSON string ; malloc(), to free()
static bool handleClient(int serverPort, int clientFd, struct sockaddr_in *client_addr)
Handle client connection and launch actions.
static const char * getMimeType(const char *path)
Translate extension in MIME type.
#define GPX_ROUTE_FILE_NAME
static void forbidToJson(char *res, size_t maxLen)
Exclusion zone is an array of polygons.
#define MAX_SIZE_RESOURCE_NAME
static void polygonToJson(MyPolygon *po, char *str, size_t maxLen)
Generate Json array for polygon.
void sendBinaryResponse(int sock, const void *data, size_t len, const char *shortnames)
send over network binary data
static double round4(double x)
static char * extractUserAgent(const char *saveBuffer)
extract user agent
static bool initContext(const char *parameterFileName, const char *pattern)
Make initialization return false if readParam or readGribAll fail.
#define SYNOPSYS
RCube is a routing software for sailing.
static void buildInitialOfShortNameList(const Zone *zone, char *str, size_t len)
build list of short names.
static int extractLevel(const char *buffer)
extract level
static int compareByMtime(const void *a, const void *b)
Comparator to sort by modification date (most recent first)
static const char * getCurrentDate()
date for logging
static double approxSegmentLengthNm(double lat0, double lon0, double lat1, double lon1)
Approximate great-circle segment length in nautical miles.
static char * testToJson(int serverPort, const char *clientIP, const char *userAgent, int level, char *out, size_t maxLen)
Provide system information.
static char * listDirToStrJson(char *root, char *dir, bool sortByName, const char *pattern, const char **filter, char *out, size_t maxLen)
static int checkRoute(const SailRoute *route)
Check that every consecutive segment of a route is fully over sea.
static void infoCoordToJson(double lat, double lon, ClientRequest *clientReq, char *res, size_t maxLen)
information associated to coord (lat lon) isSea, isSeaTolerant, grib wind and current
static int sendAll(int fd, const void *buf, size_t len)
robust send
static bool launchAction(int serverPort, int sock, ClientRequest *clientReq, const char *date, const char *clientIPAddress, const char *userAgent, char *outBuffer, size_t maxLen)
launch action and returns outBuffer after execution
static char * isoDescToStrCatJson(char *res, size_t maxLen)
generate json description of isochrones decriptor.
static bool getRealIPAddress(const char *headers, char *clientAddress, size_t bufferSize)
Retrieves the real client IP address from HTTP headers.
static char * routeToJson(SailRoute *route, bool isoc, bool isoDesc, char *res, size_t maxLen)
generate json description of track boats
static bool decodeHttpReq(const char *req, ClientRequest *clientReq)
decode request from client and fill ClientRequest structure return true if correct false if impossibl...
static void serveStaticFile(int client_socket, const char *requested_path)
serve static file
static void logRequest(const char *fileName, const char *date, int serverPort, const char *remote_addr, char *dataReq, const char *userAgent, ClientRequest *client, double duration)
log client Request side effect: dataReq is modified
static bool allowedLevel(ClientRequest *clientReq)
compare level of authorization with request
static bool segmentOverSea(double lat0, double lon0, double lat1, double lon1)
Check if the straight segment between (lat0, lon0) and (lat1, lon1) stays entirely over allowed sea a...
static bool matchFilter(const char *filename, const char **filter)
Checks if the filename matches one of the suffixes in the filter.
static int compareByName(const void *a, const void *b)
Comparator to sort by name (ascending order)
int memoryUsage(void)
Return the current memory usage (resident set size) in kilobytes.
static void handleFeedbackRequest(const char *fileName, const char *date, const char *clientIPAddress, const char *string)
store feedback information
#define MAX_SIZE_FEED_BACK
char parameterFileName[MAX_SIZE_FILE_NAME]
#define MAX_N_COMPETITORS
#define MAX_SIZE_DIR_NAME
#define MAX_SIZE_FILE_NAME
#define MAX_SIZE_TEXT_FILE
bool writeParam(const char *fileName, bool header, bool password, bool yaml)
write parameter file from struct par header or not, password or not yaml style or not
WayPointList wayPoints
list of wayPoint
bool readParam(const char *fileName, bool initDisp)
read parameter file and build par struct
void updateIsSeaWithForbiddenAreas(void)
complement according to forbidden areas
bool readMarkCSVToJson(const char *fileName, char *out, size_t maxLen)
read CSV file marks (Virtual Regatta) if check then polarCheck
double monotonic(void)
return seconds with decimals
PolMat wavePolMat
polar matrix for waves
char * paramToStrJson(Par *par, char *out, size_t maxLen)
Return JSON formatted subset of parameters into 'out'.
MyPolygon forbidZones[MAX_N_FORBID_ZONE]
forbid ones is a set of polygons
bool hasSlash(const char *name)
true if name terminates with slash
char * buildRootName(const char *fileName, char *rootName, size_t maxLen)
Build root name if not already a root name.
char * tIsSea
table describing if sea or earth
PolMat polMat
polar matrix description
bool mostRecentFile(const char *directory, const char *pattern0, const char *pattern1, char *name, size_t maxLen)
select most recent file in "directory" that contains "pattern0" and "pattern1" in name return true if...
bool readIsSea(const char *fileName)
read issea file and fill table tIsSea
PolMat sailPolMat
polar matrix for sails
char * readTextFile(const char *fileName, char *errMessage, size_t maxLen)
read all text file in buffer.
char * formatThousandSep(char *buffer, size_t maxLen, long value)
format big number with thousand sep.
char * fSailName(int val, char *str, size_t maxLen)
return the name of the sail
Zone zone
geographic zone covered by grib file
CompetitorsList competitors
list of competitors
char * gribDateTimeToStr(long date, long time, char *str, size_t maxLen)
return str representing grib date
int nearestPort(double lat, double lon, const char *fileName, char *res, size_t maxLen)
return id and name of nearest port found in file fileName from lat, lon.
time_t gribDateTimeToEpoch(long date, long hhmm)
convert long date/time from GRIB to time_t (UTC, via timegm)
void normalizeSpaces(char *s)
replace multiple spaces by just one
char * gribReaderVersion(char *str, size_t maxLen)
return version of ECCODE API
FlowP * tGribData[]
grib data description
bool readGribAll(const char *fileName, Zone *zone, int iFlow)
read grib file using eccodes C API return true if OK
Client Request description.
char currentGribName[MAX_SIZE_RESOURCE_NAME]
char model[MAX_SIZE_RESOURCE_NAME]
char polarName[MAX_SIZE_RESOURCE_NAME]
struct ClientRequest::@1 boats[MAX_N_COMPETITORS]
char fileName[MAX_SIZE_RESOURCE_NAME]
char dirName[MAX_SIZE_RESOURCE_NAME]
char wavePolName[MAX_SIZE_RESOURCE_NAME]
char feedback[MAX_SIZE_FEED_BACK]
char gribName[MAX_SIZE_RESOURCE_NAME]
struct ClientRequest::@2 wp[MAX_N_WAY_POINT]
Competitor t[MAX_N_COMPETITORS]
Structure to store file information.
char polarFileName[MAX_SIZE_FILE_NAME]
char gribFileName[MAX_SIZE_FILE_NAME]
char feedbackFileName[MAX_SIZE_FILE_NAME]
char isSeaFileName[MAX_SIZE_FILE_NAME]
char marksFileName[MAX_SIZE_FILE_NAME]
char workingDir[MAX_SIZE_FILE_NAME]
char web[MAX_SIZE_DIR_NAME]
char wavePolFileName[MAX_SIZE_FILE_NAME]
char logFileName[MAX_SIZE_FILE_NAME]
char currentGribFileName[MAX_SIZE_FILE_NAME]
char tidesFileName[MAX_SIZE_FILE_NAME]
char polarFileName[MAX_SIZE_FILE_NAME]
double lastStepWpDuration[MAX_N_WAY_POINT]
char lastPointInfo[MAX_SIZE_INFO]
WayPoint t[MAX_N_WAY_POINT]
long dataTime[MAX_N_DATA_TIME]
long timeStamp[MAX_N_TIME_STAMPS]
long dataDate[MAX_N_DATA_DATE]