RCube
Rcube Rest Server calculates sail routes based on Grib files and sailing boat polar files
Loading...
Searching...
No Matches
r3server.c
Go to the documentation of this file.
1
7#include <stdbool.h>
8#include <dirent.h> // DIR, opendir, readdir, closedir
9#include <errno.h>
10#include <stdarg.h>
11#include <time.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15#include <math.h>
16#include <unistd.h>
17#include <sys/socket.h>
18#include <netinet/in.h>
19#include <arpa/inet.h>
20#include <limits.h>
21#include <locale.h>
22#include "glibwrapper.h"
23#include "r3types.h"
24#include "r3util.h"
25#include "engine.h"
26#include "grib.h"
27#include "readgriball.h"
28#include "polar.h"
29#include "inline.h"
30#include "option.h"
31#include <sys/stat.h>
32#include <fcntl.h>
33#include <signal.h> // for signal(SIGPIPE, SIG_IGN)
34
35#define SYNOPSYS "<port> | -<option> [<parameter file>]"
36#define MAX_SIZE_REQUEST 2048 // Max size from request client
37#define MAX_SIZE_RESOURCE_NAME 256 // Max size polar or grib name
38#define PATTERN "GFS"
39#define MAX_SIZE_FEED_BACK 1024
40#define ADMIN_LEVEL 10 // level of authorization for administrator
41#define BIG_BUFFER_SIZE (300*MILLION)
42#define MAX_SIZE_MESS 100000
43#define GPX_ROUTE_FILE_NAME "gpxroute.tmp"
44
45char *bigBuffer = NULL;
46
47// global filter for REQ_DIR request
48const char *filter[] = {".csv", ".pol", ".grb", ".grb2", ".log", ".txt", ".par", ".yaml", ".json", NULL};
49
50enum {REQ_KILL = -1793, REQ_TEST = 0, REQ_ROUTING = 1, REQ_COORD = 2, REQ_FORBID = 3, REQ_POLAR = 4,
53 REQ_MARKS = 13, REQ_CHECK_GRIB = 14, REQ_GPX_ROUTE = 15, REQ_GRIB_DUMP = 16}; // type of request
54
55// level of authorization
56//const int typeLevel [16] = {0, 1, 0, 0, 0, 0, 0, 0, 0, ADMIN_LEVEL, 0, 0, 0, 0, 0, 0};
57const int typeLevel [16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // everybody access to all
58
60
62typedef struct {
63 int level; // level of authorization
64 int type; // type of request
65 int cogStep; // step of cog in degrees
66 int rangeCog; // range of cog from x - RANGE_GOG, x + RAGE_COG+1
67 int jFactor; // factor for target point distance used in sectorOptimize
68 int kFactor; // factor for target point distance used in sectorOptimize
69 int nSectors; // number of sector for optimization by sector
70 int penalty0; // penalty in seconds for tack
71 int penalty1; // penalty in seconds fot Gybe
72 int penalty2; // penalty in seconds for sail change
73 int initialAmure; // initial Amure of the routing calculation 0 = starboard = tribord, or 1 = port = babord
74 int timeStep; // isoc time step in seconds
75 time_t epochStart; // epoch time to start routing
76 bool isoc; // true if isochrones requested
77 bool isoDesc; // true if isochrone decriptor requested
78 bool sortByName; // true if directory should be sorted by name, false if sorted by modif date
79 bool forbid; // true if forbid zone (polygons or Earth) are considered
80 bool withWaves; // true if waves specified in wavePolName file are considered
81 bool withCurrent; // true if current specified in currentGribName is considered
82 double staminaVR; // Init stamina
83 double motorSpeed; // motor speed if used
84 double threshold; // threshold for motor use
85 double nightEfficiency; // efficiency of team at night
86 double dayEfficiency; // efficiency of team at day
87 double xWind; // multiply factor for wind
88 double maxWind; // max Wind supported
89 double constWindTws; // if not equal 0, constant wind used in place of grib file
90 double constWindTwd; // the direction of constant wind if used
91 double constWave; // constant wave height if used
92 double constCurrentS; // if not equal 0, contant current speed Knots
93 double constCurrentD; // the direction of constant current if used
94 int nBoats; // number of boats
95 struct {
96 char name [MAX_SIZE_NAME]; // name of the boat
97 double lat; // latitude
98 double lon; // longitude
99 } boats [MAX_N_COMPETITORS]; // boats
100 int nWp; // number of waypoints
101 struct {
102 double lat; // latitude
103 double lon; // longitude
104 } wp [MAX_N_WAY_POINT]; // way points
105 char model [MAX_SIZE_RESOURCE_NAME]; // grib model
106 char dirName [MAX_SIZE_RESOURCE_NAME]; // remote directory name
107 char wavePolName[MAX_SIZE_RESOURCE_NAME]; // polar file name
108 char polarName [MAX_SIZE_RESOURCE_NAME]; // polar file name
109 char gribName [MAX_SIZE_RESOURCE_NAME]; // grib file name
110 char fileName [MAX_SIZE_RESOURCE_NAME]; // grib file name
111 char currentGribName [MAX_SIZE_RESOURCE_NAME]; // grib file name
112 char feedback [MAX_SIZE_FEED_BACK]; // for feed back info
114
116
118typedef struct {
119 char *name;
120 off_t size;
121 time_t mtime;
122} FileInfo;
123
125static void polygonToJson (MyPolygon *po, char *str, size_t maxLen) {
126 const int n = po->n;
127 char temp [128];
128 double lat, lon;
129
130 strlcpy (str, " [", maxLen);
131 for (int k = 0; k < n; k++) {
132 lat = (po->points [k].lat);
133 lon = (po->points [k].lon);
134 snprintf (temp, sizeof temp, "[%.4lf, %.4lf]%s", lat, lon, (k < n-1) ? ", " : "");
135 strlcat (str,temp, maxLen);
136 }
137 strlcat (str, "]", maxLen);
138}
139
143static bool updateWindGrib (ClientRequest *clientReq, char *checkMessage, size_t maxLen) {
144 char strGrib [MAX_SIZE_FILE_NAME];
145 if (clientReq->gribName [0] == '\0') return false;
146 // change grib if requested MAY TAKE TIME !!!!
147 buildRootName (clientReq->gribName, strGrib, sizeof strGrib);
148 printf ("Grib Found: %s\n", strGrib);
149 if (strncmp (par.gribFileName, strGrib, strlen (strGrib)) != 0) {
150 if (readGribAll (strGrib, &zone, WIND)) {
151 g_strlcpy (par.gribFileName, strGrib, sizeof par.gribFileName);
152 printf ("Grib loaded : %s\n", strGrib);
153 }
154 else {
155 snprintf (checkMessage, maxLen, "3: Error reading Grib: %s", clientReq->gribName);
156 printf ("In updateWindGrib: Error reading Grib: %s\n", clientReq->gribName);
157 return false;
158 }
159 }
160 return true;
161}
162
166static bool updateCurrentGrib (ClientRequest *clientReq, char *checkMessage, size_t maxLen) {
167 char strGrib [MAX_SIZE_FILE_NAME];
168 if (clientReq->currentGribName [0] == '\0') return false;
169
170 buildRootName (clientReq->currentGribName, strGrib, sizeof strGrib);
171 printf ("Current Grib Found: %s\n", strGrib);
172 if (strncmp (par.currentGribFileName, strGrib, strlen (strGrib)) != 0) {
173 printf ("current readGrib: %s\n", strGrib);
174 if (readGribAll (strGrib, &currentZone, CURRENT)) {
176 printf ("Current Grib loaded : %s\n", strGrib);
177 }
178 else {
179 snprintf (checkMessage, maxLen, "4: Error reading Current Grib: %s", clientReq->currentGribName);
180 return false;
181 }
182 }
183 return true;
184}
185
198static double approxSegmentLengthNm(double lat0, double lon0, double lat1, double lon1) {
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);
204
205 return hypot(dlatNm, dlonNm);
206}
235static bool segmentOverSea(double lat0, double lon0, double lat1, double lon1) {
236 // 1. Defensive check on endpoints */
237 const double epsilon = 1e-12;
238 if (!isSea (tIsSea, lat0, lon0)) return false;
239 if (!isSea (tIsSea, lat1, lon1)) return false;
240
241 // 2. Degenerate segment (same point or extremely close) */
242 const double dlat = lat1 - lat0;
243 const double dlon = lon1 - lon0;
244 if (fabs(dlat) < epsilon && fabs(dlon) < epsilon) return true; // Same point, already tested */
245
246 // 3. Estimate segment length in NM */
247 double lengthNm = approxSegmentLengthNm(lat0, lon0, lat1, lon1);
248
249 // 4. Choose max spacing between checks.
250 const double stepNm = 1.0;
251
252 // 5. Compute how many intervals we need.
253 int steps = (int)ceil(lengthNm / stepNm);
254 steps = CLAMP (steps, 1, 2000); // hard cap to avoid crazy loops
255
256 //Sample intermediate points. We skip i=0 and i=steps because those are endpoints, already checked.
257 for (int i = 1; i < steps; i++) {
258 const double t = (double)i / (double)steps;
259 const double lat = lat0 + t * (lat1 - lat0); // Linear interpolation in lat/lon space.
260 const double lon = lon0 + t * (lon1 - lon0);
261 if (!isSea(tIsSea, lat, lon)) return false;
262 }
263 return true;
264}
265
277static int checkRoute(const SailRoute *route) {
278 if (!route || route->n < 3) return -1;
279 for (int i = 0; i < route->n - 2; i++) {
280 if (!segmentOverSea (route->t[i].lat, route->t[i].lon, route->t[i+1].lat, route->t[i+1].lon)) return i;
281 }
282 return -1;
283}
284
286static void forbidToJson (char *res, size_t maxLen) {
287 char str [1000];
288 strlcpy (res, "[\n", maxLen);
289 printf ("Number of polygon: %d\n", par.nForbidZone);
290
291 for (int i = 0; i < par.nForbidZone; i++) {
292 polygonToJson (&forbidZones [i], str, sizeof str);
293 strlcat (res, str, maxLen);
294 if (i < par.nForbidZone -1) strlcat (res, ",", maxLen);
295 strlcat (res, "\n", maxLen);
296 }
297
298 strlcat (res, "]\n", maxLen);
299}
300
302static char *isochronesToStrCatJson (char *res, size_t maxLen) {
303 Pp pt;
304 char str [10000];
305 int index;
306 char *savePt = res + strlen (res);
307 g_strlcat (res, ",\n\"_isoc\": \n[\n", maxLen);
308 Pp *newIsoc = NULL; // array of points
309 if ((newIsoc = malloc (MAX_SIZE_ISOC * sizeof(Pp))) == NULL) {
310 fprintf (stderr, "In isochronesToStrJson: error in memory newIsoc allocation\n");
311 return NULL;
312 }
313
314 for (int i = 0; i < nIsoc; i += 1) {
315 g_strlcat (res, " [\n", maxLen);
316 index = (isoDesc [i].size <= 1) ? 0 : isoDesc [i].first;
317 for (int j = 0; j < isoDesc [i].size; j++) {
318 newIsoc [j] = isocArray [i * MAX_SIZE_ISOC + index];
319 index += 1;
320 if (index == isoDesc [i].size) index = 0;
321 }
322 const int max = isoDesc [i].size;
323 for (int k = 0; k < max; k++) {
324 pt = newIsoc [k];
325 snprintf (str, sizeof str, " [%.6lf, %.6lf, %d, %d, %d]%s\n",
326 pt.lat, pt.lon, pt.id, pt.father, k, (k < max - 1) ? "," : "");
327 g_strlcat (res, str, maxLen);
328 }
329 snprintf (str, sizeof str, " ]%s\n", (i < nIsoc -1) ? "," : ""); // no comma for last value
330 g_strlcat (res, str, maxLen);
331 }
332 if (strlen (res) >= (maxLen - sizeof str - 2)) {// too big
333 res = savePt;
334 *res = '\0';
335 strlcat (res, ",\n\"_Warning_1\": \"No isochrone sent because too big!\"\n", maxLen); // no isochrone if too big !
336 }
337 else g_strlcat (res, "]\n", maxLen);
338 free (newIsoc);
339 return res;
340}
341
343static char *isoDescToStrCatJson (char *res, size_t maxLen) {
344 char str [10000];
345 char *savePt = res + strlen (res);
346 g_strlcat (res, ",\n\"_isodesc\": \n[\n", maxLen);
347
348 for (int i = 0; i < nIsoc; i++) {
349 //double distance = isoDesc [i].distance;
350 //if (distance >= DBL_MAX) distance = -1;
351 snprintf (str, sizeof str, " [%d, %d, %d, %d, %d, %.2lf, %.2lf, %.6lf, %.6lf]%s\n",
352 i, isoDesc [i].toIndexWp, isoDesc[i].size, isoDesc[i].first, isoDesc[i].closest,
353 isoDesc[i].bestVmc, isoDesc[i].biggestOrthoVmc,
354 isoDesc [i].focalLat,
355 isoDesc [i].focalLon,
356 (i < nIsoc - 1) ? "," : "");
357 g_strlcat (res, str, maxLen);
358 }
359 if (strlen (res) >= (maxLen - sizeof str - 2)) {// too big
360 res = savePt;
361 *res = '\0';
362 strlcat (res, ",\n\"_Warning_2\": \"No isoDesc sent because too big!\"\n", maxLen); // no isochrone if too big !
363 }
364 else g_strlcat (res, "]\n", maxLen);
365 return res;
366}
367
369static char *routeToJson (SailRoute *route, bool isoc, bool isoDesc, char *res, size_t maxLen) {
370 char str [MAX_SIZE_MESS] = "";
371 char strSail [MAX_SIZE_NAME] = "";
372 double twa = 0.0, hdg = 0.0, twd = 0.0;
373 int nSeg = -1; // for checkRoute
374 if (route->n <= 0) return NULL;
375
376 int iComp = (route->competitorIndex < 0) ? 0 : route->competitorIndex;
377 char *gribBaseName = g_path_get_basename (par.gribFileName);
378 char *gribCurrentBaseName = g_path_get_basename (par.currentGribFileName);
379 char *wavePolarBaseName = g_path_get_basename (par.wavePolFileName);
380
381 snprintf (res, maxLen,
382 "{\n"
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\": [",
391 competitors.t[iComp].name,
392 (int) (route->duration * 3600),
393 route->totDist,
394 route->ret,
395 route->isocTimeStep * 3600,
397 (route->destinationReached) ? "true" : "false",
399 );
400
401 for (int i = 0; i < route->nWayPoints; i += 1) {
402 snprintf (str, sizeof str, "%.4lf, ", route->lastStepWpDuration [i] * 3600.0);
403 g_strlcat (res, str, maxLen);
404 }
405 snprintf (str, sizeof str,
406 "%.4f],\n"
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"
414 "\"track\": [\n",
415 route->lastStepDuration * 3600.0,
419 route->polarFileName, wavePolarBaseName, gribBaseName, gribCurrentBaseName
420 );
421 g_strlcat (res, str, maxLen);
422 free (wavePolarBaseName);
423 free (gribBaseName);
424 free (gribCurrentBaseName);
425
426 for (int i = 0; i < route->n; i++) {
427 if (route->t[i].sog > LIMIT_SOG)
428 fprintf (stderr, "In routeToJson sog > LIMIT_SOG, lat = %.6lf, lon = %.6lf, sog = %.6lf, od = %.6lf; ld = %.6lf\n",
429 route->t[i].lat, route->t[i].lon, route->t[i].sog, route->t[i].od, route->t[i].ld);
430
431 twa = fTwa (route->t[i].lCap, route->t[i].twd);
432 hdg = route->t [i].oCap;
433 if (hdg < 0) hdg += 360;
434 twd = route->t[i].twd;
435 if (twd < 0) twd += 360;
436 fSailName (route->t[i].sail, strSail, sizeof strSail);
437 if ((route->t[i].toIndexWp < -1) || (route->t[i].toIndexWp >= route->nWayPoints)) {
438 printf ("In routeToJson, Error: toIndexWp outside range; %d\n", route->t[i].toIndexWp);
439 }
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",
442 route->t[i].toIndexWp,
443 route->t[i].lat, route->t[i].lon,
444 route->t[i].time * 3600.0,
445 route->t[i].od, route->t[i].sog,
446 twd ,route->t[i].tws,
447 hdg, twa,
448 route->t[i].g, route->t[i].w ,route->t[i].stamina,
449 strSail, (route->t[i].motor) ? "true" : "false",
450 route->t[i].id, route->t[i].father,
451 //(route->destinationReached || i < route-> n - 1) ? "," :"");
452 (i < route-> n - 1) ? "," :""
453 );
454 g_strlcat (res, str, maxLen);
455 }
456
457 g_strlcat (res, "]\n}\n", maxLen);
458
459 nSeg = checkRoute (route);
460 if (nSeg != -1) { // route is not OK
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",
463 nSeg, route->t[nSeg].lat, route->t[nSeg].lon, route->t[nSeg + 1].lat, route->t[nSeg + 1].lon);
464 g_strlcat (res, str, maxLen);
465 }
466 if (isoDesc) {
467 isoDescToStrCatJson (res, maxLen - 2); // concat in res des descriptors
468 }
469 printf ("Before isochronesToStrCatJson in routeToJson\n");
470 if (isoc) {
471 isochronesToStrCatJson (res, maxLen - 2); // concat in res the isochrones
472 }
473 g_strlcat (res, "}\n", maxLen);
474 return res;
475}
476
477static inline double round4(double x) {
478 return round(x * 10000.0) / 10000.0;
479}
480
485static void infoCoordToJson (double lat, double lon, ClientRequest *clientReq, char *res, size_t maxLen) {
486 char str [MAX_SIZE_MESS] = "";
487 const bool sea = isSea (tIsSea, lat, lon);
488 const bool seaTolerant = isSeaTolerant (tIsSea, lat, lon);
489 const bool wind = isInZone (lat, lon, &zone);
490 const bool current = isInZone (lat, lon, &currentZone);
491 time_t epochGribStart = gribDateTimeToEpoch (zone.dataDate [0], zone.dataTime [0]);
492 long tMax = zone.timeStamp [zone.nTimeStamp -1];
493 double u, v, g, w, twd, tws;
494 if (tMax <= 0) {
495 fprintf (stderr, "In infoCoordToJson: tMax should be strictly positive\n");
496 snprintf (str, sizeof str, "{\"_Error\": \"tMax should ne strictly positive\"\n");
497 return;
498 }
499 updateWindGrib (clientReq, str, sizeof str);
500 if (str [0] != '\0') {
501 snprintf (res, maxLen, "{\"_Error\": \"%s\"}\n", str);
502 return;
503 }
504
505 char *pFileName = strrchr (par.gribFileName, '/');
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 : "");
512
513 for (int i = 0; i < tMax; i += 1) {
514 findWindGrib (lat, lon, i, &u, &v, &g, &w, &twd, &tws);
515 // snprintf (str, sizeof str, " [%.4lf, %.4lf, %.4lf, %.4lf]%s\n", u, v, g, w, (i < tMax - 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);
518 }
519 strlcat (res, " ]\n}\n", maxLen);
520}
521
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);
533
534 if (headerStart) {
535 headerStart += strlen (headerName);
536 while (*headerStart == ' ') headerStart++; // Skip any whitepaces
537
538 const char* headerEnd = strstr(headerStart, "\r\n");
539 if (headerEnd) {
540 size_t ipLength = headerEnd - headerStart;
541 if (ipLength < bufferSize) {
542 g_strlcpy (clientAddress, headerStart, ipLength + 1);
543 g_strstrip (clientAddress);
544 return true;
545 } else {
546 fprintf (stderr, "In getRealIPAddress: IP address length exceeds buffer size.");
547 return false;
548 }
549 }
550 }
551 clientAddress [0] = '\0';
552 return false;
553}
554
556static char* extractUserAgent (const char* saveBuffer) {
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");
562 if (userAgentEnd) {
563 return strndup (userAgentStart, userAgentEnd - userAgentStart);
564 }
565 }
566 return NULL; // No user agent found
567}
568
570static int extractLevel (const char* buffer) {
571 if (!par.authent) return ADMIN_LEVEL; // No autentication, get highest level
572 const char* headerName = "X-User-Level:";
573 const char* levelStart = strstr (buffer, headerName);
574 if (levelStart) return atoi (levelStart + strlen (headerName));
575 return 0; // No X_User-Level found
576}
577
580 if ((clientReq->type == REQ_KILL) && (clientReq->level == ADMIN_LEVEL)) {
581 return true;
582 }
583 if ((clientReq->type == 1) && (strstr (clientReq->model, "GFS") != NULL)) {
584 printf ("GFS allowed whatever the level for type 1 request\n");
585 return true; // GFS allowed to anyone
586 }
587 if ((clientReq->level >= 0) && (clientReq->level <= ADMIN_LEVEL)) {
588 printf ("Allowed associated to typeLevel: %d\n", typeLevel [clientReq->type]);
589 return clientReq->level >= typeLevel [clientReq->type];
590 }
591 return false; // Level out of range
592}
593
595static int compareByName (const void *a, const void *b) {
596 const FileInfo *fa = (const FileInfo*) a, *fb = (const FileInfo*) b;
597 return strcmp (fa->name, fb->name);
598}
599
601static int compareByMtime (const void *a, const void *b) {
602 const FileInfo *fa = (const FileInfo*)a, *fb = (const FileInfo*)b;
603 if (fa->mtime > fb->mtime) return -1;
604 if (fa->mtime < fb->mtime) return 1;
605 return strcmp(fa->name, fb->name); // tie-breaker stable-ish
606}
607
609static bool matchFilter (const char *filename, const char **filter) {
610 if (filter == NULL) return true;
611 for (int i = 0; filter[i] != NULL; i++) {
612 if (g_str_has_suffix (filename, filter[i])) return true;
613 }
614 return false;
615}
616
620static char *nearestPortToStrJson (double lat, double lon, char *out, size_t maxLen) {
621 char selectedPort [MAX_SIZE_NAME];
622 int idPort = nearestPort (lat, lon, par.tidesFileName, selectedPort, sizeof selectedPort);
623 if (idPort != 0) {
624 snprintf (out, maxLen, "{\"nearestPort\": \"%s\", \"idPort\": %d}\n", selectedPort, idPort);
625 }
626 else snprintf (out, maxLen, "{\"error\": \"Tide File %s not found\"}\n", par.tidesFileName);
627 return out;
628}
629
631static char *jsonEscapeStrdup(const char *s) {
632 if (!s) return strdup("");
633 const size_t len = strlen(s);
634 // worst case: every byte become \u00XX (6 chars) */
635 const size_t cap = len * 6 + 1;
636 char *out = (char*)malloc(cap);
637 if (!out) return NULL;
638 char *p = out;
639
640 for (size_t i = 0; i < len; i++) {
641 unsigned char c = (unsigned char)s[i];
642 switch (c) {
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;
650 default:
651 if (c < 0x20) { // control -> \u00XX
652 static const char hex[] = "0123456789ABCDEF";
653 *p++='\\'; *p++='u'; *p++='0'; *p++='0';
654 *p++=hex[(c>>4)&0xF]; *p++=hex[c&0xF];
655 } else {
656 *p++=(char)c;
657 }
658 }
659 }
660 *p = '\0';
661 return out;
662}
663
675static char *listDirToStrJson (char *root, char *dir, bool sortByName, const char *pattern, const char **filter, char *out, size_t maxLen) {
676 char line [MAX_SIZE_LINE];
677 char fullPath [MAX_SIZE_LINE];
678 char filePath [MAX_SIZE_LINE * 2];
679 const char *sep = (dir && dir [0] != '/') ? "/" : "";
680 if (maxLen == 0) return out;
681 out[0] = '\0';
682
683 // Path directory
684 snprintf (fullPath, sizeof fullPath, "%s%s%s", root ? root : "", sep, dir ? dir : "");
685
686 DIR *d = opendir(fullPath);
687 if (!d) {
688 fprintf(stderr, "In listDirToStrJson Error opening directory '%s': %s\n", fullPath, strerror(errno));
689 snprintf (out, maxLen, "{\"error\":\"Error opening directory\"}");
690 return out;
691 }
692
693 // file Collect
694 FileInfo *arr = NULL;
695 size_t n = 0, cap = 0;
696
697 struct dirent *ent;
698 while ((ent = readdir(d)) != NULL) {
699 const char *fileName = ent->d_name;
700 if (strcmp(fileName, ".") == 0 || strcmp(fileName, "..") == 0) continue;
701
702 // suffix filter
703 if (!matchFilter(fileName, filter)) continue;
704 // prefix (pattern) filter
705 if (pattern && ! g_str_has_prefix(fileName, pattern)) continue;
706 snprintf (filePath, sizeof filePath, "%s/%s", fullPath, fileName);
707
708 struct stat st;
709 if (stat(filePath, &st) != 0) {
710 fprintf(stderr, "In listDirToStrJson Error retrieving info for '%s': %s\n", filePath, strerror(errno));
711 continue;
712 }
713
714 if (!S_ISREG(st.st_mode)) continue;
715
716 /* push_back */
717 if (n == cap) {
718 size_t newcap = cap ? cap * 2 : 32;
719 FileInfo *tmp = (FileInfo*)realloc(arr, newcap * sizeof(*arr));
720 if (!tmp) {
721 fprintf(stderr, "listDirToStrJson: realloc échouée\n");
722 break;
723 }
724 arr = tmp; cap = newcap;
725 }
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; }
730 n++;
731 }
732 closedir(d);
733
734 if (n > 1) qsort(arr, n, sizeof(*arr), sortByName ? compareByName : compareByMtime);
735
736 /* JSON */
737 snprintf (out, maxLen, "[\n");
738 for (size_t i = 0; i < n; i++) {
739 /* locale date "YYYY-MM-DD HH:MM:SS" */
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);
744
745 char *escaped = jsonEscapeStrdup(arr[i].name);
746 snprintf (line, sizeof line," [\"%s\", %lld, \"%s\"]%s\n",
747 escaped ? escaped : "", (long long)arr[i].size, time_str, (i + 1 < n) ? "," : "");
748 g_strlcat (out, line, maxLen);
749 free(escaped);
750 }
751 g_strlcat (out, "]\n", maxLen);
752
753 for (size_t i = 0; i < n; i++) free(arr[i].name);
754 free(arr);
755 return out;
756}
757
760static bool initContext (const char *parameterFileName, const char *pattern) {
761 char directory [MAX_SIZE_DIR_NAME];
762 char str [MAX_SIZE_LINE];
763 char errMessage [MAX_SIZE_TEXT] = "";
764 bool readGribRet;
765
766 if (! readParam (parameterFileName, false)) {
767 fprintf (stderr, "In initContext, Error readParam: %s\n", parameterFileName);
768 return false;
769 }
770 printf ("Parameters File: %s\n", parameterFileName);
771 if (par.mostRecentGrib) { // most recent grib will replace existing grib
772 snprintf (directory, sizeof directory, "%s%sgrib", par.workingDir, hasSlash (par.workingDir) ? "" : "/");
773 mostRecentFile (directory, ".gr", pattern, par.gribFileName, sizeof par.gribFileName);
774 }
775 if (par.gribFileName [0] != '\0') {
776 readGribRet = readGribAll (par.gribFileName, &zone, WIND);
777 if (! readGribRet) {
778 fprintf (stderr, "In initContext, Error: Unable to read grib file: %s\n ", par.gribFileName);
779 return false;
780 }
781 printf ("Grib loaded : %s\n", par.gribFileName);
782 printf ("Grib DateTime0 : %s\n", gribDateTimeToStr (zone.dataDate [0], zone.dataTime [0], str, sizeof str));
783 }
784
785 if (par.currentGribFileName [0] != '\0') {
786 readGribRet = readGribAll (par.currentGribFileName, &zone, WIND);
787 printf ("Cur grib loaded: %s\n", par.currentGribFileName);
788 printf ("Grib DateTime0 : %s\n", gribDateTimeToStr (currentZone.dataDate [0], currentZone.dataTime [0], str, sizeof str));
789 }
790 if (readPolar (par.polarFileName, &polMat, &sailPolMat, errMessage, sizeof errMessage)) {
791 printf ("Polar loaded : %s\n", par.polarFileName);
792 }
793 else {
794 fprintf (stderr, "In initContext, Error readPolar: %s\n", errMessage);
795 }
796 if (readPolar (par.wavePolFileName, &wavePolMat, NULL, errMessage, sizeof errMessage)) {
797 printf ("Polar loaded : %s\n", par.wavePolFileName);
798 }
799 else {
800 fprintf (stderr, "In initContext, Error readPolar: %s\n", errMessage);
801 }
802 printf ("par.web : %s\n", par.web);
803 nIsoc = 0;
804 route.n = 0;
806 if (par.isSeaFileName [0] != '\0')
809 return true;
810}
811
813static const char* getCurrentDate () {
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);
818 return dateBuffer;
819}
820
822static void handleFeedbackRequest (const char *fileName, const char *date, const char *clientIPAddress, const char *string) {
823 FILE *file = fopen (fileName, "a");
824 if (file == NULL) {
825 fprintf (stderr, "handleFeedbackRequest, Error opening file: %s\n", fileName);
826 return;
827 }
828 fprintf (file, "%s; %s; \n%s\n\n", date, clientIPAddress, string);
829 fclose (file);
830}
831
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) {
836
837 char newUserAgent [MAX_SIZE_LINE];
838 g_strlcpy (newUserAgent, userAgent, sizeof newUserAgent);
839 g_strdelimit (newUserAgent, ";", ':'); // to avoid ";" the CSV delimiter inside field
840 char *startAgent = strchr (newUserAgent, '('); // we delete what is before ( if exist
841 if (startAgent == NULL) startAgent = newUserAgent;
842
843 FILE *logFile = fopen (fileName, "a");
844 if (logFile == NULL) {
845 fprintf (stderr, "In logRequest, Error opening log file: %s\n", fileName);
846 return;
847 }
848 g_strstrip (dataReq);
849 g_strdelimit (dataReq, "\r\n", ' ');
850
851 fprintf (logFile, "%s; %d; %-16.16s; %-40.40s; %2d; %6.2lf; %.50s\n",
852 date, serverPort, remote_addr, startAgent, client->type, duration, dataReq);
853 fclose (logFile);
854}
855
858static bool decodeHttpReq (const char *req, ClientRequest *clientReq) {
859 memset (clientReq, 0, sizeof (ClientRequest));
860 clientReq->type = -1; // default unknown
861 clientReq->timeStep = 3600;
862 clientReq->cogStep = 5;
863 clientReq->rangeCog = 90;
864 clientReq->kFactor = 1;
865 clientReq->nSectors = 720;
866 clientReq->staminaVR = 100.0;
867 clientReq->motorSpeed = 6.0;
870 clientReq->xWind = 1.0;
871 clientReq->maxWind = 100.0;
872
873 char **parts = g_strsplit (req, "&", -1);
874 if (parts == NULL) return false;
875
876 for (int i = 0; parts[i]; i++) {
877 // printf ("part %d %s\n", i, parts [i]);
878 g_strstrip (parts [i]);
879 if (sscanf(parts[i], "type=%d", &clientReq->type) == 1);// type extraction
880 else if (g_str_has_prefix (parts[i], "boat=")) {
881 char *boatsPart = parts[i] + strlen ("boat="); // After boats=="
882 if (*boatsPart == '\0') {
883 g_strfreev(parts);
884 return false; // parsing error
885 }
886 // waypoints parsing
887 char **boatsCoords = g_strsplit (boatsPart, ";", -1);
888 for (int i = 0; boatsCoords[i] && clientReq->nBoats < MAX_N_COMPETITORS; i++) {
889 char name [MAX_SIZE_NAME];
890 double lat, lon;
891 if (sscanf(boatsCoords[i], "%63[^,], %lf, %lf", name, &lat, &lon) == 3) {
895 clientReq->nBoats += 1;
896 }
897 }
898 g_strfreev(boatsCoords);
899 }
900 else if (g_str_has_prefix (parts[i], "waypoints=")) {
901 char *wpPart = parts[i] + strlen ("waypoints="); // Partie après "waypoints="
902 if (*wpPart == '\0') {
903 g_strfreev(parts);
904 return false; // parsing error
905 }
906 // waypoints parsing
907 char **wpCoords = g_strsplit (wpPart, ";", -1);
908 for (int i = 0; wpCoords[i] && clientReq->nWp < MAX_N_WAY_POINT; i++) {
909 double lat, lon;
910 if (sscanf(wpCoords[i], "%lf,%lf", &lat, &lon) == 2) {
911 clientReq->wp [clientReq->nWp].lat = lat;
912 clientReq->wp [clientReq->nWp].lon = lon;
913 clientReq->nWp += 1;
914 }
915 }
916 g_strfreev(wpCoords);
917 }
918
919 else if (sscanf (parts[i], "timeStep=%d", &clientReq->timeStep) == 1); // time step extraction
920 else if (sscanf (parts[i], "cogStep=%d", &clientReq->cogStep) == 1); // cog step extraction
921 else if (sscanf (parts[i], "cogRange=%d", &clientReq->rangeCog) == 1); // range sog extraction
922 else if (sscanf (parts[i], "jFactor=%d", &clientReq->jFactor) == 1); // jFactor extraction
923 else if (sscanf (parts[i], "kFactor=%d", &clientReq->kFactor) == 1); // kFactor extraction
924 else if (sscanf (parts[i], "nSectors=%d", &clientReq->nSectors) == 1); // nSectors extraction
925 else if (sscanf (parts[i], "penalty0=%d", &clientReq->penalty0) == 1); // penalty0 extraction
926 else if (sscanf (parts[i], "penalty1=%d", &clientReq->penalty1) == 1); // penalty1 extraction
927 else if (sscanf (parts[i], "penalty2=%d", &clientReq->penalty2) == 1); // penalty2 (sail change) extraction
928 else if (sscanf (parts[i], "initialAmure=%d", &clientReq->initialAmure) == 1); // initial Amure extraction
929 else if (sscanf (parts[i], "epochStart=%ld", &clientReq->epochStart) == 1); // time startextraction
930 else if (sscanf (parts[i], "polar=%255s", clientReq->polarName) == 1); // polar name
931 else if (sscanf (parts[i], "wavePolar=%255s", clientReq->wavePolName) == 1); // wave polar name
932 else if (sscanf (parts[i], "file=%255s", clientReq->fileName) == 1); // file name
933 else if (sscanf (parts[i], "model=%255s", clientReq->model) == 1); // grib model
934 else if (sscanf (parts[i], "grib=%255s", clientReq->gribName) == 1); // grib name
935 else if (sscanf (parts[i], "currentGrib=%255s", clientReq->currentGribName) == 1); // current grib name
936 else if (sscanf (parts[i], "dir=%255s", clientReq->dirName) == 1); // directory name
937 else if (g_str_has_prefix (parts[i], "dir="))
938 g_strlcpy (clientReq->dirName, parts [i] + strlen ("dir="), sizeof clientReq->dirName); // defaulr empty works
939 else if (g_str_has_prefix (parts[i], "feedback="))
940 g_strlcpy (clientReq->feedback, parts [i] + strlen ("feedback="), sizeof clientReq->feedback);
941 else if (g_str_has_prefix (parts[i], "isoc=true")) clientReq->isoc = true; // Default false
942 else if (g_str_has_prefix (parts[i], "isoc=false")) clientReq->isoc = false; // Default false
943 else if (g_str_has_prefix (parts[i], "isodesc=true")) clientReq->isoDesc = true; // Default false
944 else if (g_str_has_prefix (parts[i], "isodesc=false")) clientReq->isoDesc = false; // Default false
945 else if (g_str_has_prefix (parts[i], "forbid=true")) clientReq->forbid = true; // Default false
946 else if (g_str_has_prefix (parts[i], "forbid=false")) clientReq->forbid = false; // Default false
947 else if (g_str_has_prefix (parts[i], "withWaves=true")) clientReq->withWaves = true; // Default false
948 else if (g_str_has_prefix (parts[i], "withWaves=false")) clientReq->withWaves = false; // Default false
949 else if (g_str_has_prefix (parts[i], "withCurrent=true")) clientReq->withCurrent = true; // Default false
950 else if (g_str_has_prefix (parts[i], "withCurrent=false")) clientReq->withCurrent = false;// Default false
951 else if (g_str_has_prefix (parts[i], "sortByName=true")) clientReq->sortByName = true; // Default false
952 else if (g_str_has_prefix (parts[i], "sortByName=false")) clientReq->sortByName = false; // Default false
953 else if (sscanf (parts[i], "staminaVR=%lf", &clientReq->staminaVR) == 1); // stamina Virtual Regatta
954 else if (sscanf (parts[i], "motorSpeed=%lf", &clientReq->motorSpeed) == 1); // motor speed
955 else if (sscanf (parts[i], "threshold=%lf", &clientReq->threshold) == 1); // threshold for motoe
956 else if (sscanf (parts[i], "nightEfficiency=%lf", &clientReq->nightEfficiency) == 1); // efficiency at night
957 else if (sscanf (parts[i], "dayEfficiency=%lf", &clientReq->dayEfficiency) == 1); // efficiency daylight
958 else if (sscanf (parts[i], "xWind=%lf", &clientReq->xWind) == 1); // xWind factor
959 else if (sscanf (parts[i], "maxWind=%lf", &clientReq->maxWind) == 1); // max Wind
960 else if (sscanf (parts[i], "constWindTws=%lf", &clientReq->constWindTws) == 1); // const Wind TWS
961 else if (sscanf (parts[i], "constWindTwd=%lf", &clientReq->constWindTwd) == 1); // const Wind TWD
962 else if (sscanf (parts[i], "constWave=%lf", &clientReq->constWave) == 1); // const Waves
963 else if (sscanf (parts[i], "constCurrentS=%lf", &clientReq->constCurrentS) == 1); // const current speed
964 else if (sscanf (parts[i], "constCurrentD=%lf", &clientReq->constCurrentD) == 1); // const current direction
965 else fprintf (stderr, "In decodeHttpReq Unknown value: %s\n", parts [i]);
966 }
967 g_strfreev (parts);
968
969 return clientReq->type != -1;
970}
971
973static bool checkParamAndUpdate (ClientRequest *clientReq, char *checkMessage, size_t maxLen) {
974 char strPolar [MAX_SIZE_FILE_NAME];
975 char directory [MAX_SIZE_DIR_NAME];
976 checkMessage [0] = '\0';
977 // printf ("startInfo after: %s, startTime: %lf\n", asctime (&startInfo), par.startTimeInHours);
978 if ((clientReq->nBoats == 0) || (clientReq->nWp == 0)) {
979 snprintf (checkMessage, maxLen, "1: No boats or no Waypoints");
980 return false;
981 }
988 par.penalty0 = clientReq->penalty0; // seconds
989 par.penalty1 = clientReq->penalty1; // seconds
990 par.penalty2 = clientReq->penalty2; // seconds
1005
1006 // change polar if requested
1007 if (clientReq->polarName [0] != '\0') {
1008 buildRootName (clientReq->polarName, strPolar, sizeof strPolar);
1009 printf ("polar found: %s\n", strPolar);
1010 if (strncmp (par.polarFileName, strPolar, strlen (strPolar)) != 0) {
1011 printf ("read polar: %s\n", strPolar);
1012 if (readPolar (strPolar, &polMat, &sailPolMat, checkMessage, maxLen)) {
1013 g_strlcpy (par.polarFileName, strPolar, sizeof par.polarFileName);
1014 printf ("Polar loaded : %s\n", strPolar);
1015 }
1016 else {
1017 snprintf (checkMessage, maxLen, "2: Error reading Polar: %s", clientReq->polarName);
1018 return false;
1019 }
1020 }
1021 }
1022 if (clientReq->wavePolName [0] != '\0') {
1023 buildRootName (clientReq->wavePolName, strPolar, sizeof strPolar);
1024 printf ("wave polar found: %s\n", strPolar);
1025 if (strncmp (par.wavePolFileName, strPolar, strlen (strPolar)) != 0) {
1026 printf ("read wave polar: %s\n", strPolar);
1027 if (readPolar (strPolar, &wavePolMat, NULL, checkMessage, maxLen)) {
1028 g_strlcpy (par.wavePolFileName, strPolar, sizeof par.wavePolFileName);
1029 printf ("Wave Polar loaded : %s\n", strPolar);
1030 }
1031 else {
1032 snprintf (checkMessage, maxLen, "2: Error reading Wave Polar: %s", clientReq->wavePolName);
1033 return false;
1034 }
1035 }
1036 }
1037 if (clientReq->model [0] != '\0' && clientReq->gribName [0] == '\0') { // there is a model specified but no grib file
1038 snprintf (directory, sizeof directory, "%s%sgrib", par.workingDir, hasSlash (par.workingDir) ? "" : "/");
1039 if (!mostRecentFile (directory, ".gr", clientReq->model, clientReq->gribName, sizeof clientReq->gribName)) {
1040 snprintf (checkMessage, maxLen, "0: No grib file with model: %s", clientReq->model);
1041 return false;
1042 };
1043 }
1044 updateWindGrib (clientReq, checkMessage, maxLen);
1045 if (checkMessage [0] != '\0') return false;
1046 updateCurrentGrib (clientReq, checkMessage, maxLen);
1047 if (checkMessage [0] != '\0') return false;
1048
1049 if (clientReq->epochStart <= 0)
1050 clientReq->epochStart = time (NULL); // default value if empty is now
1051 const time_t theTime0 = gribDateTimeToEpoch (zone.dataDate [0], zone.dataTime [0]);
1052 par.startTimeInHours = (clientReq->epochStart - theTime0) / 3600.0;
1053 printf ("Start Time Epoch: %ld, theTime0: %ld\n", clientReq->epochStart, theTime0);
1054 printf ("Start Time in Hours after Grib: %.2lf\n", par.startTimeInHours);
1055 char *gribBaseName = g_path_get_basename (par.gribFileName);
1056
1058 for (int i = 0; i < clientReq->nBoats; i += 1) {
1059 if (! par.allwaysSea && ! isSeaTolerant (tIsSea, clientReq -> boats [i].lat, clientReq -> boats [i].lon)) {
1060 snprintf (checkMessage, maxLen,
1061 "5: Competitor not in sea., name: %s, lat: %.6lf, lon: %.6lf",
1062 clientReq -> boats [i].name, clientReq -> boats [i].lat, clientReq -> boats [i].lon);
1063 return false;
1064 }
1065 if (! isInZone (clientReq -> boats [i].lat, clientReq -> boats [i].lon, &zone) && (par.constWindTws == 0)) {
1066 snprintf (checkMessage, maxLen,
1067 "6: Competitor not in Grib wind zone., grib: %s, bottomLat: %.2lf, leftLon: %.2lf, topLat: %.2lf, rightLon: %.2lf",
1068 gribBaseName, zone.latMin, zone.lonLeft, zone.latMax, zone.lonRight);
1069 free (gribBaseName);
1070 return false;
1071 }
1072 g_strlcpy (competitors.t [i].name, clientReq -> boats [i].name, MAX_SIZE_NAME);
1073 printf ("competitor name: %s\n", competitors.t [i].name);
1074 competitors.t [i].lat = clientReq -> boats [i].lat;
1075 competitors.t [i].lon = clientReq -> boats [i].lon;
1076 }
1077
1078 for (int i = 0; i < clientReq->nWp; i += 1) {
1079 if (! par.allwaysSea && ! isSeaTolerant (tIsSea, clientReq->wp [i].lat, clientReq->wp [i].lon)) {
1080 snprintf (checkMessage, maxLen,
1081 "7: WP or Dest. not in sea, lat: %.2lf, lon: %.2lf",
1082 clientReq->wp [i].lat, clientReq->wp [i].lon);
1083 return false;
1084 }
1085 if (! isInZone (clientReq->wp [i].lat , clientReq->wp [i].lon , &zone) && (par.constWindTws == 0)) {
1086 snprintf (checkMessage, maxLen,
1087 "8: WP or Dest. not in Grib wind zone: %s, bottomLat: %.2lf, leftLon: %.2lf, topLat: %.2lf, rightLon: %.2lf",
1088 gribBaseName, zone.latMin, zone.lonLeft, zone.latMax, zone.lonRight);
1089 free (gribBaseName);
1090 return false;
1091 }
1092 }
1093 for (int i = 0; i < clientReq->nWp -1; i += 1) {
1094 wayPoints.t[i].lat = clientReq->wp [i].lat;
1095 wayPoints.t[i].lon = clientReq->wp [i].lon;
1096 }
1097 wayPoints.n = clientReq->nWp - 1;
1098
1100 snprintf (checkMessage, maxLen, "9: start Time not in Grib time window");
1101 return false;
1102 }
1103
1104 par.tStep = clientReq->timeStep / 3600.0;
1105
1106 par.pOr.lat = clientReq->boats [0].lat;
1107 par.pOr.lon = clientReq->boats [0].lon;
1111 return true;
1112}
1113
1115static const char *getMimeType (const char *path) {
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";
1128}
1129
1131static void serveStaticFile (int client_socket, const char *requested_path) {
1132 char filepath [512];
1133 snprintf (filepath, sizeof filepath, "%s%s", par.web, requested_path);
1134
1135 char *q = strchr(filepath, '?'); // cut after ? to avoid issue with path/r3.js?v=0.2
1136 if (q) *q = '\0';
1137 printf ("File Path: %s\n", filepath);
1138
1139 // Check if file exist
1140 struct stat st;
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");
1145 return;
1146 }
1147
1148 // File open
1149 const int file = open (filepath, O_RDONLY);
1150 if (file == -1) {
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");
1154 return;
1155 }
1156
1157 // send HTTP header and MIME type
1158 char header [256];
1159 snprintf (header, sizeof header, "HTTP/1.1 200 OK\r\nContent-Type: %s\r\nContent-Length: %ld\r\n\r\n",
1160 getMimeType(filepath), st.st_size);
1161 send (client_socket, header, strlen(header), 0);
1162
1163 // Read end send file
1164 char buffer [1024];
1165 ssize_t bytesRead;
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);
1170 if (sent <= 0) {
1171 perror("send"); // we stop in a clean way if client disconnects
1172 close(file);
1173 fprintf(stderr, "In serveStaticFile, Client disconnected while sending file\n");
1174 return;
1175 }
1176 totalSent += sent;
1177 }
1178 }
1179 close (file);
1180 printf ("✅ serveStaticFile OK\n");
1181}
1182
1187int memoryUsage (void) {
1188 FILE *fp = fopen("/proc/self/status", "r");
1189 if (!fp) return -1;
1190 char line[256];
1191 int mem = -1;
1192
1193 while (fgets (line, sizeof line, fp)) {
1194 if (strncmp (line, "VmRSS:", 6) == 0) {
1195 // Example line: "VmRSS: 12345 kB"
1196 sscanf (line + 6, "%d", &mem); // skip "VmRSS:"
1197 break;
1198 }
1199 }
1200 fclose(fp);
1201 return mem; // in KB
1202}
1203
1205static char *testToJson (int serverPort, const char *clientIP, const char *userAgent, int level, char *out, size_t maxLen) {
1206 char strWind [MAX_SIZE_LINE] = "";
1207 char strCurrent [MAX_SIZE_LINE] = "";
1208 char strMem [MAX_SIZE_LINE] = "";
1209 char str [MAX_SIZE_TEXT_FILE] = "";
1210
1211 formatThousandSep (strWind, sizeof strWind, sizeof(FlowP) * (zone.nTimeStamp + 1) * zone.nbLat * zone.nbLon);
1212 formatThousandSep (strCurrent, sizeof strCurrent, sizeof(FlowP) * (currentZone.nTimeStamp + 1) * currentZone.nbLat * currentZone.nbLon);
1213 formatThousandSep (strMem, sizeof strMem, memoryUsage ()); // KB !
1214
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"
1222 " \"PID\": %d,\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",
1227 PROG_NAME, PROG_VERSION, PROG_AUTHOR, serverPort, gribReaderVersion (str, sizeof str),
1228 strWind, strCurrent, __DATE__, getpid (), strMem, clientIP, userAgent, level
1229 );
1230 return out;
1231}
1232
1234void sendBinaryResponse(int sock, const void *data, size_t len, const char *shortnames) {
1235 char header[512];
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"; // Important to read X-Shortnames
1241
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"
1246 "%s"
1247 "Content-Length: %zu\r\n"
1248 "Connection: close\r\n"
1249 "\r\n",
1250 shortnames, corsHeaders, len);
1251
1252 if ((n < 0) || (size_t)n >= sizeof header) {
1253 fprintf (stderr, "In sendBinaryResponse, Error n:%d\n", n);
1254 return;
1255 }
1256 send(sock, header, (size_t)n, 0);
1257 send(sock, data, len, 0);
1258}
1259
1262static void buildInitialOfShortNameList(const Zone *zone, char *str, size_t len) {
1263 int i = 0;
1264 str [0] = '\0';
1265 if (len < 5) return;
1266 if (! uvPresentGrib (zone)) return;
1267 str [i++] = 'u';
1268 str [i++] = 'v';
1269 if (isPresentGrib (zone, "gust")) str[i++] = 'g';
1270 if (isPresentGrib (zone, "swh")) str[i++] = 'w';
1271 str [i] = '\0';
1272}
1273
1275static bool launchAction (int serverPort, int sock, ClientRequest *clientReq,
1276 const char *date, const char *clientIPAddress, const char *userAgent, char *outBuffer, size_t maxLen) {
1277 char tempFileName [MAX_SIZE_FILE_NAME];
1278 char checkMessage [MAX_SIZE_TEXT];
1279 char errMessage [MAX_SIZE_TEXT] = "";
1280 char directory [MAX_SIZE_DIR_NAME];
1281 char str [MAX_SIZE_NAME];
1282 char *strTemp = NULL;
1283 int len;
1284 bool resp = true;
1285 bigBuffer [0] = '\0';
1286 // printf ("client.req = %d\n", clientReq->type);
1287 switch (clientReq->type) {
1288 case REQ_KILL:
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);
1291 break;
1292 case REQ_TEST:
1293 testToJson (serverPort, clientIPAddress, userAgent, clientReq->level, outBuffer, maxLen);
1294 break;
1295 case REQ_ROUTING:
1296 if (checkParamAndUpdate (clientReq, checkMessage, sizeof checkMessage)) {
1298 routingLaunch ();
1299 if (route.ret == ROUTING_ERROR) {
1300 snprintf (outBuffer, maxLen, "{\"_Error\": \"%s\"}\n", "Routing failed");
1301 }
1302 else routeToJson (&route, clientReq->isoc, clientReq->isoDesc, outBuffer, maxLen);
1303 }
1304 else {
1305 snprintf (outBuffer, maxLen, "{\"_Error\": \"%s\"}\n", checkMessage);
1306 }
1307 break;
1308 case REQ_COORD:
1309 if (clientReq->nBoats > 0) {
1310 infoCoordToJson (clientReq->boats [0].lat, clientReq->boats [0].lon, clientReq, outBuffer, maxLen);
1311 }
1312 else {
1313 snprintf (outBuffer, maxLen, "{\"_Error\": \"No Boat\"\n}\n");
1314 }
1315 break;
1316 case REQ_FORBID:
1317 forbidToJson (outBuffer, maxLen);
1318 break;
1319 case REQ_POLAR:
1320 if (clientReq->polarName [0] != '\0') {
1321 if (strstr (clientReq->polarName, "wavepol")) {
1322 polToStrJson (false, clientReq->polarName, "wavePolarName", outBuffer, maxLen);
1323 }
1324 else {
1325 polToStrJson (true, clientReq->polarName , "polarName", outBuffer, maxLen);
1326 }
1327 break;
1328 }
1329 polToStrJson (true, par.polarFileName, "polarName", outBuffer, maxLen);
1330 break;
1331 case REQ_GRIB:
1332 if (clientReq->model [0] != '\0' && clientReq->gribName [0] == '\0') { // there is a model specified but no grib file
1333 printf ("model: %s\n", clientReq->model);
1334 snprintf (directory, sizeof directory, "%s%sgrib", par.workingDir, hasSlash (par.workingDir) ? "" : "/");
1335 if (!mostRecentFile (directory, ".gr", clientReq->model, clientReq->gribName, sizeof clientReq->gribName)) {
1336 snprintf (outBuffer, maxLen, "{\"_Error\": \"No grib with model: %s\"}\n", clientReq->model);
1337 break;
1338 }
1339 }
1340 if (clientReq->gribName [0] != '\0') gribToStrJson (clientReq->gribName, outBuffer, maxLen);
1341 else snprintf (outBuffer, maxLen, "{\"_Error\": \"%s\"}\n", "No Grib");
1342 break;
1343 case REQ_DIR:
1345 break;
1346 case REQ_PAR_RAW:
1347 // yaml style if model = 1
1348 bool yaml = clientReq->model [0] == 'y';
1349 buildRootName (TEMP_FILE_NAME, tempFileName, sizeof tempFileName);
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);
1354 break;
1355 }
1356 strlcat (outBuffer, strTemp, maxLen);
1357 free (strTemp);
1358 if (yaml) normalizeSpaces (outBuffer);
1359 break;
1360 case REQ_PAR_JSON:
1361 paramToStrJson (&par, outBuffer, maxLen);
1362 break;
1363 case REQ_INIT:
1365 snprintf (outBuffer, maxLen, "{\n \"_Error\": \"Init failed\",\n \"serverPort\": %d\n}\n", serverPort);
1366 else
1367 snprintf (outBuffer, maxLen, "{\n \"message\": \"Init done\",\n \"serverPort\": %d\n}\n", serverPort);
1368 break;
1369 case REQ_FEEDBACK:
1370 handleFeedbackRequest (par.feedbackFileName, date, clientIPAddress, clientReq->feedback);
1371 snprintf (outBuffer, maxLen, "{\"_Feedback\": \"%s\"}\n", "OK");
1372 break;
1373 case REQ_DUMP_FILE: // raw dump
1374 buildRootName (clientReq->fileName, tempFileName, sizeof tempFileName);
1375 strTemp = readTextFile (tempFileName, errMessage, sizeof errMessage);
1376 if (strTemp == NULL) {
1377 snprintf (outBuffer, maxLen, "{\n \"_Error\": \"%s\"\n}\n", errMessage);
1378 break;
1379 }
1380 strlcat (outBuffer, strTemp, maxLen);
1381 free (strTemp);
1382 break;
1383 case REQ_MARKS:
1384 if (! readMarkCSVToJson (par.marksFileName, outBuffer, maxLen)) {
1385 snprintf (outBuffer, maxLen, "{\"_Error\": \"Reading Mark File %s\"}\n", par.marksFileName);
1386 }
1387 break;
1388 case REQ_NEAREST_PORT:
1389 if (clientReq->nWp > 0)
1390 nearestPortToStrJson (clientReq->wp[0].lat, clientReq->wp[0].lon, outBuffer, maxLen);
1391 else snprintf (outBuffer, maxLen, "{\"_Error\": \"%s\"}\n", "No coordinates found");
1392 break;
1393 case REQ_CHECK_GRIB:
1394 len = strlen (clientReq->gribName);
1395 bool hasGrib = len > 0 && clientReq->gribName [len - 1] != '/';
1396 len = strlen (clientReq->currentGribName);
1397 bool hasCurrentGrib = len > 0 && clientReq->currentGribName [len - 1] != '/';
1398 if (!hasGrib && !hasCurrentGrib) {
1399 snprintf (outBuffer, maxLen, "{\"_Error\": \"%s\"}\n", "Either wind grib and current grib required");
1400 break;
1401 }
1402 if (hasGrib && !(updateWindGrib (clientReq, outBuffer, maxLen))) break;
1403 if (hasCurrentGrib && !(updateCurrentGrib (clientReq, outBuffer, maxLen))) break;
1404
1405 checkGribToStr (hasCurrentGrib, outBuffer, maxLen);
1406 if (outBuffer [0] == '\0') g_strlcpy (outBuffer, "All is OK\n", maxLen);
1407 break;
1408 case REQ_GPX_ROUTE:
1409 buildRootName(GPX_ROUTE_FILE_NAME, tempFileName, sizeof tempFileName);
1410 if (route.n == 0 || !exportRouteToGpx (&route, tempFileName)) {
1411 snprintf (outBuffer, maxLen, "{\"_Error\": \"%s\"}\n", "No route");
1412 break;
1413 }
1414 strTemp = readTextFile (tempFileName, errMessage, sizeof errMessage);
1415 if (strTemp == NULL) {
1416 snprintf (outBuffer, maxLen, "{\n \"_Error\": \"%s\"\n}\n", errMessage);
1417 break;
1418 }
1419 strlcat (outBuffer, strTemp, maxLen);
1420 free (strTemp);
1421 break;
1422 case REQ_GRIB_DUMP:
1423 if (clientReq->model [0] != '\0' && clientReq->gribName [0] == '\0') { // there is a model specified but no grib file
1424 printf ("model: %s\n", clientReq->model);
1425 snprintf (directory, sizeof directory, "%s%sgrib", par.workingDir, hasSlash (par.workingDir) ? "" : "/");
1426 mostRecentFile (directory, ".gr", clientReq->model, clientReq->gribName, sizeof clientReq->gribName);
1427 }
1428 if (clientReq->gribName [0] == '\0') {
1429 snprintf (outBuffer, maxLen, "{\"_Error\": \"%s\"}\n", "No Grib");
1430 break;
1431 }
1432 updateWindGrib (clientReq, checkMessage, sizeof checkMessage);
1433 size_t dataLen = 0;
1434 buildInitialOfShortNameList(&zone, str, sizeof str);
1435 // strlcpy (str, "uvgw", 5); // ATT ecrase le précédent
1436 float *buf = buildUVGWarray(&zone, str, tGribData[WIND], &dataLen); // dataLen in number of float
1437 sendBinaryResponse(sock, buf, dataLen * sizeof(float), str);
1438 printf("✅ Send Binary float array with: Len=%zu, Bytes=%zu, Shortnames=%s\n\n",
1439 dataLen, dataLen * sizeof(float), str);
1440
1441 free(buf);
1442 resp = false;
1443 break;
1444 default:;
1445 }
1446 return resp;
1447}
1448
1450static int sendAll (int fd, const void *buf, size_t len) {
1451 const unsigned char *p = buf;
1452 while (len > 0) {
1453 const ssize_t n = send(fd, p, len, 0);
1454 if (n < 0) {
1455 if (errno == EINTR) continue;
1456 if (errno == EAGAIN || errno == EWOULDBLOCK) continue; // or poll/select
1457 return -1;
1458 }
1459 p += (size_t)n;
1460 len -= (size_t)n;
1461 }
1462 return 0;
1463}
1464
1466static bool handleClient (int serverPort, int clientFd, struct sockaddr_in *client_addr) {
1467 char saveBuffer [MAX_SIZE_REQUEST];
1468 char buffer [MAX_SIZE_REQUEST] = "";
1469 char clientIPAddress [MAX_SIZE_LINE];
1470 bool resp = true;
1471
1472 // read HTTP request
1473 const int bytes_read = recv (clientFd, buffer, sizeof buffer - 1, 0);
1474 if (bytes_read <= 0) {
1475 return false;
1476 }
1477 buffer [bytes_read] = '\0'; // terminate string
1478 // printf ("Client Request: %s\n", buffer);
1479 g_strlcpy (saveBuffer, buffer, sizeof buffer);
1480
1481 if (! getRealIPAddress (buffer, clientIPAddress, sizeof clientIPAddress)) { // try if proxy
1482 // Get client IP address if IP address not found with proxy
1483 char remoteAddr [INET_ADDRSTRLEN];
1484 inet_ntop (AF_INET, &(client_addr->sin_addr), remoteAddr, INET_ADDRSTRLEN); // not used
1485 g_strlcpy (clientIPAddress, remoteAddr, INET_ADDRSTRLEN);
1486 }
1487
1488 // Extract HTTP first line request
1489 char *requestLine = strtok (buffer, "\r\n");
1490 if (!requestLine) return false;
1491
1492 // check if Rest API (POST) or static file (GET)
1493 if (strncmp (requestLine, "POST", 4) != 0) {
1494 printf ("📥 GET Request, static file: %s\n", requestLine);
1495 // static file
1496 const char *requested_path = strchr (requestLine, ' '); // space after "GET"
1497 if (!requested_path) {
1498 return false;
1499 }
1500 requested_path++; // Pass space
1501
1502 char *end_path = strchr (requested_path, ' ');
1503 if (end_path) {
1504 *end_path = '\0'; // Terminate string
1505 }
1506
1507 if (strcmp(requested_path, "/") == 0) {
1508 requested_path = "/index.html"; // Default page
1509 }
1510
1511 serveStaticFile (clientFd, requested_path);
1512 return true; // stop
1513 }
1514
1515 // Extract request body
1516 char *postData = strstr (saveBuffer, "\r\n\r\n");
1517 if (postData == NULL) {
1518 return false;
1519 }
1520
1521 char* userAgent = extractUserAgent (saveBuffer);
1522 postData += 4; // Ignore HTTP request separators
1523 printf ("🟠 POST Request:\n%s\n", postData);
1524
1525 // data for log
1526 const double start = monotonic ();
1527 const char *date = getCurrentDate ();
1528
1529 if (! decodeHttpReq (postData, &clientReq)) {
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);
1533 return false;
1534 }
1535 clientReq.level = extractLevel (saveBuffer);
1536 printf ("user level: %d\n", clientReq.level);
1537
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";
1541
1542 if (! allowedLevel (&clientReq)) {
1543 g_strlcpy (bigBuffer, "{\"_Error\": \"Too low level of authorization\"}\n", BIG_BUFFER_SIZE);
1544 }
1545 else {
1546 resp = launchAction (serverPort, clientFd, &clientReq, date, clientIPAddress, userAgent, bigBuffer, BIG_BUFFER_SIZE);
1547 }
1548 if (resp) {
1549 char header [512];
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"
1554 "%s"
1555 "Content-Length: %zu\r\n"
1556 "\r\n",
1557 corsHeaders, bigBufferLen);
1558
1559 if (sendAll (clientFd, header, (size_t) headerLen) < 0) return false;
1560 if (sendAll (clientFd, bigBuffer, bigBufferLen) < 0) return false;
1561 printf ("✅ Response sent to client. Size: %zu\n\n", headerLen + bigBufferLen);
1562 }
1563
1564 const double duration = monotonic () - start;
1565 logRequest (par.logFileName, date, serverPort, clientIPAddress, postData, userAgent, &clientReq, duration);
1566 if (userAgent) free (userAgent);
1567 return true;
1568}
1569
1574int main (int argc, char *argv[]) {
1575 int serverFd, clientFd;
1576 struct sockaddr_in address;
1577 int addrlen = sizeof address;
1578 int serverPort, opt = 1;
1579 const double start = monotonic ();
1580 signal(SIGPIPE, SIG_IGN); // Ignore SIGPIPE globally
1581
1582 if ((bigBuffer =malloc (BIG_BUFFER_SIZE)) == NULL) {
1583 fprintf (stderr, "In main, Error: Malloc: %d,", BIG_BUFFER_SIZE);
1584 return EXIT_FAILURE;
1585 }
1586
1587 if (setlocale (LC_ALL, "C") == NULL) { // very important for printf decimal numbers
1588 fprintf (stderr, "In main, Error: setlocale failed");
1589 return EXIT_FAILURE;
1590 }
1591
1592 if (argc <= 1 || argc > 3) {
1593 fprintf (stderr, "Synopsys: %s %s\n", argv [0], SYNOPSYS);
1594 return EXIT_FAILURE;
1595 }
1596
1597 if (argc > 2)
1598 g_strlcpy (parameterFileName, argv [2], sizeof parameterFileName);
1599 else
1601
1602 if (! initContext (parameterFileName, ""))
1603 return EXIT_FAILURE;
1604
1605 // option case, launch optionManage
1606 if (argv[1][0] == '-') {
1607 optionManage (argv [1][1]);
1608 return EXIT_SUCCESS;
1609 }
1610
1611 // No option, normal case, launch of server
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;
1616 }
1617
1618 // Socket
1619 serverFd = socket (AF_INET, SOCK_STREAM, 0);
1620 if (serverFd < 0) {
1621 perror ("In main, Error: socket failed");
1622 return EXIT_FAILURE;
1623 }
1624
1625 // Allow adress reuse juste after closing
1626 if (setsockopt (serverFd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt) < 0) {
1627 perror ("In main, Error setsockopt");
1628 close (serverFd);
1629 return EXIT_FAILURE;
1630 }
1631
1632 // Define server parameters address port
1633 address.sin_family = AF_INET;
1634 address.sin_addr.s_addr = INADDR_ANY;
1635 address.sin_port = htons (serverPort);
1636
1637 // Bind socket with port
1638 if (bind (serverFd, (struct sockaddr *)&address, sizeof address) < 0) {
1639 perror ("In main, Error socket bind");
1640 close (serverFd);
1641 return EXIT_FAILURE;
1642 }
1643
1644 // Listen connexions
1645 if (listen (serverFd, 3) < 0) {
1646 perror ("In main: Error listening");
1647 close (serverFd);
1648 return EXIT_FAILURE;
1649 }
1650 const double elapsed = monotonic () - start;
1651 printf ("✅ Loaded in...: %.2lf seconds. Server listen on port: %d, Pid: %d\n", elapsed, serverPort, getpid ());
1652
1653 while (clientReq.type != REQ_KILL) {
1654 if ((clientFd = accept (serverFd, (struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0) {
1655 perror ("In main: Error accept");
1656 close (serverFd);
1657 return EXIT_FAILURE;
1658 }
1659 handleClient (serverPort, clientFd, &address);
1660 fflush (stdout);
1661 fflush (stderr);
1662 close (clientFd);
1663 }
1664 close (serverFd);
1665 free (tIsSea);
1666 free (isoDesc);
1667 free (isocArray);
1668 free (route.t);
1669 // freeHistoryRoute ();
1670 free (tGribData [WIND]);
1671 free (tGribData [CURRENT]);
1672 free (bigBuffer);
1673 return EXIT_SUCCESS;
1674}
1675
int nIsoc
Definition engine.c:35
void * routingLaunch()
launch routing with parameters
Definition engine.c:1192
SailRoute route
store sail route calculated in engine.c by routing
Definition engine.c:39
IsoDesc * isoDesc
Definition engine.c:33
bool exportRouteToGpx(const SailRoute *route, const char *fileName)
export route with GPX format
Definition engine.c:1420
Pp * isocArray
global variables
Definition engine.c:32
static void g_strdelimit(char *s, const char *delims, char repl)
#define MAX(i, j)
Definition glibwrapper.h:6
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)
Definition glibwrapper.h:10
static void g_strfreev(char **strv)
Free array with elements (like GLib g_strfreev)
Definition glibwrapper.h:30
#define CLAMP(x, lo, hi)
Definition glibwrapper.h:7
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)
Definition glibwrapper.h:9
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.
Definition glibwrapper.h:37
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
Definition r3grib.c:155
bool isPresentGrib(const Zone *zone, const char *name)
true if shortname in zone
Definition r3grib.c:169
bool checkGribToStr(bool hasCurrentGrib, char *buffer, size_t maxLen)
check Grib information and write report in the buffer return false if something wrong
Definition r3grib.c:316
char * gribToStrJson(const char *fileName, char *out, size_t maxLen)
write grib meta information in string
Definition r3grib.c:586
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,...
Definition r3grib.c:33
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
Definition r3grib.c:488
static bool isInZone(double lat, double lon, Zone *zone)
true if P (lat, lon) is within the zone
Definition inline.h:47
static double fTwa(double heading, double twd)
return TWA between -180 and 180 note : tribord amure if twa < 0
Definition inline.h:64
static bool isSea(char *isSeaArray, double lat, double lon)
this file contains small inlines functions to be included in source files
Definition inline.h:8
static bool isSeaTolerant(char *isSeaArray, double lat, double lon)
say if point is in sea
Definition inline.h:16
void optionManage(char option)
compilation: gcc -c option.c
Definition option.c:18
char * polToStrJson(bool report, const char *fileName, const char *objName, char *out, size_t maxLen)
write polar information in string Json format
Definition polar.c:419
bool readPolar(const char *fileName, PolMat *mat, PolMat *sailPolMat, char *errMessage, size_t maxLen)
Launch readPolarCsv or readPolarJson according to type.
Definition polar.c:388
static bool updateWindGrib(ClientRequest *clientReq, char *checkMessage, size_t maxLen)
update Grib file if required
Definition r3server.c:143
static char * nearestPortToStrJson(double lat, double lon, char *out, size_t maxLen)
return neareAst port to lat, lon
Definition r3server.c:620
static bool updateCurrentGrib(ClientRequest *clientReq, char *checkMessage, size_t maxLen)
update current file if required
Definition r3server.c:166
static char * isochronesToStrCatJson(char *res, size_t maxLen)
generate json description of isochrones.
Definition r3server.c:302
@ REQ_DIR
Definition r3server.c:51
@ REQ_KILL
Definition r3server.c:50
@ REQ_DUMP_FILE
Definition r3server.c:52
@ REQ_TEST
Definition r3server.c:50
@ REQ_COORD
Definition r3server.c:50
@ REQ_PAR_JSON
Definition r3server.c:51
@ REQ_GRIB_DUMP
Definition r3server.c:53
@ REQ_GPX_ROUTE
Definition r3server.c:53
@ REQ_CHECK_GRIB
Definition r3server.c:53
@ REQ_MARKS
Definition r3server.c:53
@ REQ_FORBID
Definition r3server.c:50
@ REQ_PAR_RAW
Definition r3server.c:51
@ REQ_POLAR
Definition r3server.c:50
@ REQ_NEAREST_PORT
Definition r3server.c:52
@ REQ_FEEDBACK
Definition r3server.c:52
@ REQ_INIT
Definition r3server.c:52
@ REQ_GRIB
Definition r3server.c:51
@ REQ_ROUTING
Definition r3server.c:50
int main(int argc, char *argv[])
main server first argument (mandatory): port number second argument (optionnal): parameter file
Definition r3server.c:1574
static bool checkParamAndUpdate(ClientRequest *clientReq, char *checkMessage, size_t maxLen)
check validity of parameters
Definition r3server.c:973
char * bigBuffer
Definition r3server.c:45
static char * jsonEscapeStrdup(const char *s)
Escape JSON string ; malloc(), to free()
Definition r3server.c:631
static bool handleClient(int serverPort, int clientFd, struct sockaddr_in *client_addr)
Handle client connection and launch actions.
Definition r3server.c:1466
static const char * getMimeType(const char *path)
Translate extension in MIME type.
Definition r3server.c:1115
#define GPX_ROUTE_FILE_NAME
Definition r3server.c:43
static void forbidToJson(char *res, size_t maxLen)
Exclusion zone is an array of polygons.
Definition r3server.c:286
ClientRequest clientReq
Definition r3server.c:115
#define MAX_SIZE_RESOURCE_NAME
Definition r3server.c:37
static void polygonToJson(MyPolygon *po, char *str, size_t maxLen)
Generate Json array for polygon.
Definition r3server.c:125
void sendBinaryResponse(int sock, const void *data, size_t len, const char *shortnames)
send over network binary data
Definition r3server.c:1234
static double round4(double x)
Definition r3server.c:477
static char * extractUserAgent(const char *saveBuffer)
extract user agent
Definition r3server.c:556
static bool initContext(const char *parameterFileName, const char *pattern)
Make initialization return false if readParam or readGribAll fail.
Definition r3server.c:760
#define SYNOPSYS
RCube is a routing software for sailing.
Definition r3server.c:35
static void buildInitialOfShortNameList(const Zone *zone, char *str, size_t len)
build list of short names.
Definition r3server.c:1262
const int typeLevel[16]
Definition r3server.c:57
const char * filter[]
Definition r3server.c:48
static int extractLevel(const char *buffer)
extract level
Definition r3server.c:570
static int compareByMtime(const void *a, const void *b)
Comparator to sort by modification date (most recent first)
Definition r3server.c:601
static const char * getCurrentDate()
date for logging
Definition r3server.c:813
#define MAX_SIZE_REQUEST
Definition r3server.c:36
static double approxSegmentLengthNm(double lat0, double lon0, double lat1, double lon1)
Approximate great-circle segment length in nautical miles.
Definition r3server.c:198
static char * testToJson(int serverPort, const char *clientIP, const char *userAgent, int level, char *out, size_t maxLen)
Provide system information.
Definition r3server.c:1205
#define ADMIN_LEVEL
Definition r3server.c:40
static char * listDirToStrJson(char *root, char *dir, bool sortByName, const char *pattern, const char **filter, char *out, size_t maxLen)
Definition r3server.c:675
static int checkRoute(const SailRoute *route)
Check that every consecutive segment of a route is fully over sea.
Definition r3server.c:277
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
Definition r3server.c:485
#define PATTERN
Definition r3server.c:38
static int sendAll(int fd, const void *buf, size_t len)
robust send
Definition r3server.c:1450
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
Definition r3server.c:1275
static char * isoDescToStrCatJson(char *res, size_t maxLen)
generate json description of isochrones decriptor.
Definition r3server.c:343
static bool getRealIPAddress(const char *headers, char *clientAddress, size_t bufferSize)
Retrieves the real client IP address from HTTP headers.
Definition r3server.c:530
static char * routeToJson(SailRoute *route, bool isoc, bool isoDesc, char *res, size_t maxLen)
generate json description of track boats
Definition r3server.c:369
static bool decodeHttpReq(const char *req, ClientRequest *clientReq)
decode request from client and fill ClientRequest structure return true if correct false if impossibl...
Definition r3server.c:858
static void serveStaticFile(int client_socket, const char *requested_path)
serve static file
Definition r3server.c:1131
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
Definition r3server.c:834
static bool allowedLevel(ClientRequest *clientReq)
compare level of authorization with request
Definition r3server.c:579
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...
Definition r3server.c:235
static bool matchFilter(const char *filename, const char **filter)
Checks if the filename matches one of the suffixes in the filter.
Definition r3server.c:609
#define BIG_BUFFER_SIZE
Definition r3server.c:41
static int compareByName(const void *a, const void *b)
Comparator to sort by name (ascending order)
Definition r3server.c:595
int memoryUsage(void)
Return the current memory usage (resident set size) in kilobytes.
Definition r3server.c:1187
static void handleFeedbackRequest(const char *fileName, const char *date, const char *clientIPAddress, const char *string)
store feedback information
Definition r3server.c:822
#define MAX_SIZE_FEED_BACK
Definition r3server.c:39
#define MAX_SIZE_MESS
Definition r3server.c:42
char parameterFileName[MAX_SIZE_FILE_NAME]
Definition r3server.c:59
#define MAX_N_COMPETITORS
Definition r3types.h:71
#define DEG_TO_RAD
Definition r3types.h:28
#define LIMIT_SOG
Definition r3types.h:74
#define PROG_NAME
Definition r3types.h:31
#define MAX_N_WAY_POINT
Definition r3types.h:30
#define PROG_VERSION
Definition r3types.h:32
#define PARAMETERS_FILE
Definition r3types.h:14
#define MAX_SIZE_DIR_NAME
Definition r3types.h:64
#define TEMP_FILE_NAME
Definition r3types.h:15
#define MAX_SIZE_NAME
Definition r3types.h:61
#define MAX_SIZE_FILE_NAME
Definition r3types.h:63
#define MAX_SIZE_LINE
Definition r3types.h:45
#define PROG_AUTHOR
Definition r3types.h:33
@ WIND
Definition r3types.h:77
@ CURRENT
Definition r3types.h:77
@ ROUTING_ERROR
Definition r3types.h:82
#define MAX_SIZE_TEXT
Definition r3types.h:48
#define MAX_SIZE_TEXT_FILE
Definition r3types.h:49
#define MAX_SIZE_ISOC
Definition r3types.h:39
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
Definition r3util.c:826
WayPointList wayPoints
list of wayPoint
Definition r3util.c:43
bool readParam(const char *fileName, bool initDisp)
read parameter file and build par struct
Definition r3util.c:573
void updateIsSeaWithForbiddenAreas(void)
complement according to forbidden areas
Definition r3util.c:534
Zone currentZone
Definition r3util.c:65
bool readMarkCSVToJson(const char *fileName, char *out, size_t maxLen)
read CSV file marks (Virtual Regatta) if check then polarCheck
Definition r3util.c:1144
double monotonic(void)
return seconds with decimals
Definition r3util.c:1099
PolMat wavePolMat
polar matrix for waves
Definition r3util.c:55
char * paramToStrJson(Par *par, char *out, size_t maxLen)
Return JSON formatted subset of parameters into 'out'.
Definition r3util.c:1023
MyPolygon forbidZones[MAX_N_FORBID_ZONE]
forbid ones is a set of polygons
Definition r3util.c:32
bool hasSlash(const char *name)
true if name terminates with slash
Definition r3util.c:130
char * buildRootName(const char *fileName, char *rootName, size_t maxLen)
Build root name if not already a root name.
Definition r3util.c:230
char * tIsSea
table describing if sea or earth
Definition r3util.c:61
PolMat polMat
polar matrix description
Definition r3util.c:49
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...
Definition r3util.c:138
bool readIsSea(const char *fileName)
read issea file and fill table tIsSea
Definition r3util.c:484
PolMat sailPolMat
polar matrix for sails
Definition r3util.c:52
char * readTextFile(const char *fileName, char *errMessage, size_t maxLen)
read all text file in buffer.
Definition r3util.c:1106
char * formatThousandSep(char *buffer, size_t maxLen, long value)
format big number with thousand sep.
Definition r3util.c:104
Par par
parameter
Definition r3util.c:58
char * fSailName(int val, char *str, size_t maxLen)
return the name of the sail
Definition r3util.c:68
Zone zone
geographic zone covered by grib file
Definition r3util.c:64
CompetitorsList competitors
list of competitors
Definition r3util.c:46
char * gribDateTimeToStr(long date, long time, char *str, size_t maxLen)
return str representing grib date
Definition r3util.c:276
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.
Definition r3util.c:1063
double tMax[3]
Definition r3util.c:25
char name[MAX_SIZE_NAME]
Definition r3util.c:22
time_t gribDateTimeToEpoch(long date, long hhmm)
convert long date/time from GRIB to time_t (UTC, via timegm)
Definition r3util.c:408
void normalizeSpaces(char *s)
replace multiple spaces by just one
Definition r3util.c:1196
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.
Definition r3server.c:62
char currentGribName[MAX_SIZE_RESOURCE_NAME]
Definition r3server.c:111
double threshold
Definition r3server.c:84
double lon
Definition r3server.c:98
bool sortByName
Definition r3server.c:78
bool withWaves
Definition r3server.c:80
double dayEfficiency
Definition r3server.c:86
double constWave
Definition r3server.c:91
int initialAmure
Definition r3server.c:73
char model[MAX_SIZE_RESOURCE_NAME]
Definition r3server.c:105
time_t epochStart
Definition r3server.c:75
double xWind
Definition r3server.c:87
char polarName[MAX_SIZE_RESOURCE_NAME]
Definition r3server.c:108
struct ClientRequest::@1 boats[MAX_N_COMPETITORS]
double lat
Definition r3server.c:97
double nightEfficiency
Definition r3server.c:85
double motorSpeed
Definition r3server.c:83
bool withCurrent
Definition r3server.c:81
double constWindTwd
Definition r3server.c:90
char fileName[MAX_SIZE_RESOURCE_NAME]
Definition r3server.c:110
double maxWind
Definition r3server.c:88
double constWindTws
Definition r3server.c:89
double constCurrentD
Definition r3server.c:93
double constCurrentS
Definition r3server.c:92
char dirName[MAX_SIZE_RESOURCE_NAME]
Definition r3server.c:106
double staminaVR
Definition r3server.c:82
char name[MAX_SIZE_NAME]
Definition r3server.c:96
char wavePolName[MAX_SIZE_RESOURCE_NAME]
Definition r3server.c:107
char feedback[MAX_SIZE_FEED_BACK]
Definition r3server.c:112
char gribName[MAX_SIZE_RESOURCE_NAME]
Definition r3server.c:109
struct ClientRequest::@2 wp[MAX_N_WAY_POINT]
double lon
Definition r3types.h:263
double lat
Definition r3types.h:262
char name[MAX_SIZE_NAME]
Definition r3types.h:267
Competitor t[MAX_N_COMPETITORS]
Definition r3types.h:275
Structure to store file information.
Definition r3server.c:118
off_t size
Definition r3server.c:120
time_t mtime
Definition r3server.c:121
char * name
Definition r3server.c:119
Wind point.
Definition r3types.h:128
int size
Definition r3types.h:195
int first
Definition r3types.h:194
Structure for polygon.
Definition r3types.h:94
int n
Definition r3types.h:95
Point * points
Definition r3types.h:96
double nightEfficiency
Definition r3types.h:394
int nForbidZone
Definition r3types.h:411
char polarFileName[MAX_SIZE_FILE_NAME]
Definition r3types.h:344
int jFactor
Definition r3types.h:333
int penalty1
Definition r3types.h:390
int withCurrent
Definition r3types.h:421
int penalty0
Definition r3types.h:389
Pp pDest
Definition r3types.h:367
double maxWind
Definition r3types.h:397
char gribFileName[MAX_SIZE_FILE_NAME]
Definition r3types.h:337
int kFactor
Definition r3types.h:334
char feedbackFileName[MAX_SIZE_FILE_NAME]
Definition r3types.h:358
double constWindTws
Definition r3types.h:328
char isSeaFileName[MAX_SIZE_FILE_NAME]
Definition r3types.h:350
int withWaves
Definition r3types.h:420
char marksFileName[MAX_SIZE_FILE_NAME]
Definition r3types.h:362
int authent
Definition r3types.h:319
char workingDir[MAX_SIZE_FILE_NAME]
Definition r3types.h:336
char web[MAX_SIZE_DIR_NAME]
Definition r3types.h:342
double constCurrentD
Definition r3types.h:332
double tStep
Definition r3types.h:324
double constWindTwd
Definition r3types.h:329
char wavePolFileName[MAX_SIZE_FILE_NAME]
Definition r3types.h:345
double dayEfficiency
Definition r3types.h:395
double threshold
Definition r3types.h:393
double motorSpeed
Definition r3types.h:392
int nSectors
Definition r3types.h:335
double constWave
Definition r3types.h:330
char logFileName[MAX_SIZE_FILE_NAME]
Definition r3types.h:359
int allwaysSea
Definition r3types.h:320
int penalty2
Definition r3types.h:391
Pp pOr
Definition r3types.h:366
char currentGribFileName[MAX_SIZE_FILE_NAME]
Definition r3types.h:343
double xWind
Definition r3types.h:396
char tidesFileName[MAX_SIZE_FILE_NAME]
Definition r3types.h:357
double staminaVR
Definition r3types.h:363
int rangeCog
Definition r3types.h:326
double constCurrentS
Definition r3types.h:331
double startTimeInHours
Definition r3types.h:365
int mostRecentGrib
Definition r3types.h:338
int cogStep
Definition r3types.h:325
double lat
Definition r3types.h:89
double lon
Definition r3types.h:90
Point in isochrone.
Definition r3types.h:172
int id
Definition r3types.h:173
int father
Definition r3types.h:174
double lat
Definition r3types.h:180
double lon
Definition r3types.h:181
int amure
Definition r3types.h:175
int sail
Definition r3types.h:235
double w
Definition r3types.h:245
double twd
Definition r3types.h:247
int toIndexWp
Definition r3types.h:234
int father
Definition r3types.h:232
double tws
Definition r3types.h:248
double oCap
Definition r3types.h:238
double time
Definition r3types.h:237
double ld
Definition r3types.h:241
double g
Definition r3types.h:246
double od
Definition r3types.h:239
double stamina
Definition r3types.h:228
double sog
Definition r3types.h:242
double lon
Definition r3types.h:230
double lat
Definition r3types.h:229
double lCap
Definition r3types.h:240
bool motor
Definition r3types.h:236
Route description
Definition r3types.h:279
double babordDist
Definition r3types.h:294
int nWayPoints
Definition r3types.h:289
double isocTimeStep
Definition r3types.h:284
int nSailChange
Definition r3types.h:305
bool destinationReached
Definition r3types.h:296
char polarFileName[MAX_SIZE_FILE_NAME]
Definition r3types.h:280
double totDist
Definition r3types.h:291
double lastStepDuration
Definition r3types.h:287
int nAmureChange
Definition r3types.h:306
double duration
Definition r3types.h:290
double tribordDist
Definition r3types.h:293
double motorDist
Definition r3types.h:292
double calculationTime
Definition r3types.h:286
double lastStepWpDuration[MAX_N_WAY_POINT]
Definition r3types.h:288
char lastPointInfo[MAX_SIZE_INFO]
Definition r3types.h:307
int competitorIndex
Definition r3types.h:304
int ret
Definition r3types.h:295
SailPoint * t
Definition r3types.h:308
WayPoint t[MAX_N_WAY_POINT]
Definition r3types.h:256
double lon
Definition r3types.h:219
double lat
Definition r3types.h:218
zone description
Definition r3types.h:140
double latMin
Definition r3types.h:149
double lonRight
Definition r3types.h:152
long dataTime[MAX_N_DATA_TIME]
Definition r3types.h:165
size_t nTimeStamp
Definition r3types.h:157
long nbLon
Definition r3types.h:156
long nbLat
Definition r3types.h:155
double latMax
Definition r3types.h:150
double lonLeft
Definition r3types.h:151
long timeStamp[MAX_N_TIME_STAMPS]
Definition r3types.h:163
long dataDate[MAX_N_DATA_DATE]
Definition r3types.h:164