MRTKLIB 0.7.14
Modernized GNSS Positioning Library
Loading...
Searching...
No Matches
mrtk_stream.c
Go to the documentation of this file.
1/*------------------------------------------------------------------------------
2 * mrtk_stream.c : stream I/O functions
3 *
4 * Copyright (C) 2026 H.SHIONO (MRTKLIB Project)
5 * Copyright (C) 2023-2025 Cabinet Office, Japan
6 * Copyright (C) 2024-2025 Lighthouse Technology & Consulting Co. Ltd.
7 * Copyright (C) 2023-2025 Japan Aerospace Exploration Agency
8 * Copyright (C) 2023-2025 TOSHIBA ELECTRONIC TECHNOLOGIES CORPORATION
9 * Copyright (C) 2015- Mitsubishi Electric Corp.
10 * Copyright (C) 2014 Geospatial Information Authority of Japan
11 * Copyright (C) 2014 T.SUZUKI
12 * Copyright (C) 2007-2023 T.TAKASU
13 *
14 * SPDX-License-Identifier: BSD-2-Clause
15 *----------------------------------------------------------------------------*/
16#include "mrtklib/mrtk_stream.h"
17
18#include <ctype.h>
19#include <math.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23
24#include "mrtklib/mrtk_rcvraw.h"
25#include "mrtklib/mrtk_sol.h"
26#include "mrtklib/mrtk_sys.h"
27#include "mrtklib/mrtk_time.h"
28#include "mrtklib/mrtk_version.h"
29
30/* Forward declarations for functions in rtklib (resolved at link time) */
31#include <fcntl.h>
32#include <sys/stat.h>
33#include <sys/time.h>
34#include <unistd.h>
35#define __USE_MISC
36#ifndef CRTSCTS
37#define CRTSCTS 020000000000
38#endif
39#include <arpa/inet.h>
40#include <errno.h>
41#include <netdb.h>
42#include <netinet/in.h>
43#include <netinet/tcp.h>
44#include <sys/socket.h>
45#include <termios.h>
46
47#include "mrtklib/mrtk_trace.h"
48#include "ntrip_chunk.h"
49
50/* constants -----------------------------------------------------------------*/
51
52#define TINTACT 200 /* period for stream active (ms) */
53#define SERIBUFFSIZE 4096 /* serial buffer size (bytes) */
54#define TIMETAGH_LEN 64 /* time tag file header length */
55#define MAXCLI 32 /* max client connection for tcp svr */
56#define MAXSTATMSG 32 /* max length of status message */
57#define DEFAULT_MEMBUF_SIZE 4096 /* default memory buffer size (bytes) */
58
59#define NTRIP_AGENT MRTKLIB_SOFTNAME "/" MRTKLIB_VERSION_STRING
60#define NTRIP_CLI_PORT 2101 /* default ntrip-client connection port */
61#define NTRIP_SVR_PORT 80 /* default ntrip-server connection port */
62#define NTRIP_MAXRSP 32768 /* max size of ntrip response */
63#define NTRIP_MAXSTR 256 /* max length of mountpoint string */
64#define NTRIP_RSP_OK_CLI "ICY 200 OK\r\n" /* ntrip response: client */
65#define NTRIP_RSP_OK_SVR "OK\r\n" /* ntrip response: server */
66#define NTRIP_RSP_SRCTBL "SOURCETABLE 200 OK\r\n" /* ntrip response: source table */
67#define NTRIP_RSP_TBLEND "ENDSOURCETABLE"
68#define NTRIP_RSP_HTTP "HTTP/" /* ntrip response: http */
69#define NTRIP_RSP_ERROR "ERROR" /* ntrip response: error */
70#define NTRIP_RSP_UNAUTH "HTTP/1.0 401 Unauthorized\r\n"
71#define NTRIP_RSP_ERR_PWD "ERROR - Bad Password\r\n"
72#define NTRIP_RSP_ERR_MNTP "ERROR - Bad Mountpoint\r\n"
73
74#define FTP_CMD "wget" /* ftp/http command */
75#define FTP_TIMEOUT 30 /* ftp/http timeout (s) */
76
77#define MIN(x, y) ((x) < (y) ? (x) : (y))
78
79/* stream format strings (moved from rtkcmn.c) */
80const char* formatstrs[32] = { /* stream format strings */
81 "RTCM 2", /* 0 */
82 "RTCM 3", /* 1 */
83 "NovAtel OEM7", /* 2 */
84 "NovAtel OEM3", /* 3 */
85 "u-blox UBX", /* 4 */
86 "Superstar II", /* 5 */
87 "Hemisphere", /* 6 */
88 "SkyTraq", /* 7 */
89 "Javad GREIS", /* 8 */
90 "NVS BINR", /* 9 */
91 "BINEX", /* 10 */
92 "Trimble RT17", /* 11 */
93 "Septentrio SBF", /* 12 */
94 "RINEX", /* 13 */
95 "SP3", /* 14 */
96 "RINEX CLK", /* 15 */
97 "SBAS", /* 16 */
98 "NMEA 0183", /* 17 */
99 NULL};
100
101/* macros --------------------------------------------------------------------*/
102
103#define dev_t int
104#define socket_t int
105#define closesocket close
106
107/* type definition -----------------------------------------------------------*/
108
109typedef struct { /* file control type */
110 FILE* fp; /* file pointer */
111 FILE* fp_tag; /* file pointer of tag file */
112 FILE* fp_tmp; /* temporary file pointer for swap */
113 FILE* fp_tag_tmp; /* temporary file pointer of tag file for swap */
114 char path[MAXSTRPATH]; /* file path */
115 char openpath[MAXSTRPATH]; /* open file path */
116 int mode; /* file mode */
117 int timetag; /* time tag flag (0:off,1:on) */
118 int repmode; /* replay mode (0:master,1:slave) */
119 int offset; /* time offset (ms) for slave */
120 int size_fpos; /* file position size (bytes) */
121 gtime_t time; /* start time */
122 gtime_t wtime; /* write time */
123 uint32_t tick; /* start tick */
124 uint32_t tick_f; /* start tick in file */
125 long fpos_n; /* next file position */
126 uint32_t tick_n; /* next tick */
127 double start; /* start offset (s) */
128 double speed; /* replay speed (time factor) */
129 double swapintv; /* swap interval (hr) (0: no swap) */
130 rtk_lock_t lock; /* lock flag */
131} file_t;
132
133typedef struct { /* tcp control type */
134 int state; /* state (0:close,1:wait,2:connect) */
135 char saddr[256]; /* address string */
136 int port; /* port */
137 struct sockaddr_in addr; /* address resolved */
138 socket_t sock; /* socket descriptor */
139 int tcon; /* reconnect time (ms) (-1:never,0:now) */
140 uint32_t tact; /* data active tick */
141 uint32_t tdis; /* disconnect tick */
142} tcp_t;
143
144typedef struct tcpsvr_tag { /* tcp server type */
145 tcp_t svr; /* tcp server control */
146 tcp_t cli[MAXCLI]; /* tcp client controls */
148
149typedef struct { /* tcp cilent type */
150 tcp_t svr; /* tcp server control */
151 int toinact; /* inactive timeout (ms) (0:no timeout) */
152 int tirecon; /* reconnect interval (ms) (0:no reconnect) */
153} tcpcli_t;
154
155typedef struct { /* serial control type */
156 dev_t dev; /* serial device */
157 int error; /* error state */
158 tcpsvr_t* tcpsvr; /* tcp server for received stream */
159} serial_t;
160
161typedef struct { /* ntrip control type */
162 int state; /* state (0:close,1:wait,2:connect) */
163 int type; /* type (0:server,1:client) */
164 int ver; /* requested version (NTRIP_VER_AUTO/1/2) */
165 int ver_neg; /* negotiated version after handshake */
166 int chunked; /* chunked transfer encoding flag */
167 int v2_tried; /* v2 attempt flag for AUTO fallback */
168 int nb; /* response buffer size */
169 char url[MAXSTRPATH]; /* url for proxy */
170 char host[256]; /* host header value (addr:port) */
171 char mntpnt[256]; /* mountpoint */
172 char user[256]; /* user */
173 char passwd[256]; /* password */
174 char str[NTRIP_MAXSTR]; /* mountpoint string for server */
175 uint8_t buff[NTRIP_MAXRSP]; /* response buffer */
176 tcpcli_t* tcp; /* tcp client */
177 chunk_dec_t cdec; /* chunked transfer decoder */
178} ntrip_t;
179
180typedef struct { /* ntrip client/server connection type */
181 int state; /* state (0:close,1:connect) */
182 int ver; /* detected NTRIP version */
183 char mntpnt[256]; /* mountpoint */
184 char str[NTRIP_MAXSTR]; /* mountpoint string for server */
185 int nb; /* request buffer size */
186 uint8_t buff[NTRIP_MAXRSP]; /* request buffer */
188
189typedef struct { /* ntrip caster control type */
190 int state; /* state (0:close,1:wait,2:connect) */
191 int type; /* type (0:server,1:client) */
192 char mntpnt[256]; /* mountpoint */
193 char user[256]; /* user */
194 char passwd[256]; /* password */
195 char srctbl[NTRIP_MAXSTR]; /* source table */
196 tcpsvr_t* tcp; /* tcp server */
197 ntripc_con_t con[MAXCLI]; /* ntrip client/server connections */
198} ntripc_t;
199
200typedef struct { /* udp type */
201 int state; /* state (0:close,1:open) */
202 int type; /* type (0:server,1:client) */
203 int port; /* port */
204 char saddr[256]; /* address (server:filter,client:server) */
205 struct sockaddr_in addr; /* address resolved */
206 socket_t sock; /* socket descriptor */
207} udp_t;
208
209typedef struct { /* ftp download control type */
210 int state; /* state (0:close,1:download,2:complete,3:error) */
211 int proto; /* protocol (0:ftp,1:http) */
212 int error; /* error code (0:no error,1-10:wget error, */
213 /* 11:no temp dir,12:uncompact error) */
214 char addr[1024]; /* download address */
215 char file[1024]; /* download file path */
216 char user[256]; /* user for ftp */
217 char passwd[256]; /* password for ftp */
218 char local[1024]; /* local file path */
219 int topts[4]; /* time options {poff,tint,toff,tretry} (s) */
220 gtime_t tnext; /* next retry time (gpst) */
221 rtk_thread_t thread; /* download thread */
222} ftp_t;
223
224typedef struct { /* memory buffer type */
225 int state, wp, rp; /* state,write/read pointer */
226 int bufsize; /* buffer size (bytes) */
227 rtk_lock_t lock; /* lock flag */
228 uint8_t* buf; /* write buffer */
229} membuf_t;
230
231/* proto types for static functions ------------------------------------------*/
232
233static tcpsvr_t* opentcpsvr(const char* path, char* msg);
234static void closetcpsvr(tcpsvr_t* tcpsvr);
235static int writetcpsvr(tcpsvr_t* tcpsvr, uint8_t* buff, int n, char* msg);
236
237/* global options ------------------------------------------------------------*/
238
239static int toinact = 10000; /* inactive timeout (ms) */
240static int ticonnect = 10000; /* interval to re-connect (ms) */
241static int tirate = 1000; /* averaging time for data rate (ms) */
242static int buffsize = 32768; /* receive/send buffer size (bytes) */
243static char localdir[1024] = ""; /* local directory for ftp/http */
244static char proxyaddr[256] = ""; /* http/ntrip/ftp proxy address */
245static int ntrip_ver_default = NTRIP_VER_AUTO; /* default ntrip version */
246static uint32_t tick_master = 0; /* time tick master for replay */
247static int fswapmargin = 30; /* file swap margin (s) */
248
249/* open serial ---------------------------------------------------------------*/
250static serial_t* openserial(const char* path, int mode, char* msg) {
251 const int br[] = {300,
252 600,
253 1200,
254 2400,
255 4800,
256 9600,
257 19200,
258 38400,
259 57600,
260 115200,
261 230400
262#ifdef B460800
263 ,
264 460800
265#endif
266#ifdef B921600
267 ,
268 921600
269#endif
270 };
271 const int nbr = (int)(sizeof(br) / sizeof(br[0]));
272 serial_t* serial;
273 int i, brate = 9600, bsize = 8, stopb = 1, tcp_port = 0;
274 char *p, parity = 'N', dev[128], port[128], fctr[64] = "", path_tcp[32], msg_tcp[128];
275 const speed_t bs[] = {B300,
276 B600,
277 B1200,
278 B2400,
279 B4800,
280 B9600,
281 B19200,
282 B38400,
283 B57600,
284 B115200,
285 B230400
286#ifdef B460800
287 ,
288 B460800
289#endif
290#ifdef B921600
291 ,
292 B921600
293#endif
294 };
295 struct termios ios = {0};
296 int rw = 0;
297 tracet(NULL, 3, "openserial: path=%s mode=%d\n", path, mode);
298
299 if (!(serial = (serial_t*)calloc(1, sizeof(serial_t)))) {
300 return NULL;
301 }
302
303 if ((p = strchr(path, ':'))) {
304 strncpy(port, path, p - path);
305 port[p - path] = '\0';
306 sscanf(p, ":%d:%d:%c:%d:%s", &brate, &bsize, &parity, &stopb, fctr);
307 } else {
308 strcpy(port, path);
309 }
310
311 if ((p = strchr(path, '#'))) {
312 sscanf(p, "#%d", &tcp_port);
313 }
314 for (i = 0; i < nbr; i++) {
315 if (br[i] == brate) {
316 break;
317 }
318 }
319 if (i >= nbr) {
320 sprintf(msg, "bitrate error (%d)", brate);
321 tracet(NULL, 1, "openserial: %s path=%s\n", msg, path);
322 free(serial);
323 return NULL;
324 }
325 parity = (char)toupper((int)parity);
326
327 sprintf(dev, "/dev/%.*s", (int)sizeof(port) - 6, port);
328
329 if ((mode & STR_MODE_R) && (mode & STR_MODE_W)) {
330 rw = O_RDWR;
331 } else if (mode & STR_MODE_R) {
332 rw = O_RDONLY;
333 } else if (mode & STR_MODE_W) {
334 rw = O_WRONLY;
335 }
336
337 if ((serial->dev = open(dev, rw | O_NOCTTY | O_NONBLOCK)) < 0) {
338 sprintf(msg, "%s open error (%d)", dev, errno);
339 tracet(NULL, 1, "openserial: %s dev=%s\n", msg, dev);
340 free(serial);
341 return NULL;
342 }
343 tcgetattr(serial->dev, &ios);
344 ios.c_iflag = 0;
345 ios.c_oflag = 0;
346 ios.c_lflag = 0; /* non-canonical */
347 ios.c_cc[VMIN] = 0; /* non-block-mode */
348 ios.c_cc[VTIME] = 0;
349 cfsetospeed(&ios, bs[i]);
350 cfsetispeed(&ios, bs[i]);
351 ios.c_cflag |= bsize == 7 ? CS7 : CS8;
352 if (parity == 'O') {
353 ios.c_cflag |= (PARENB | PARODD);
354 } else if (parity == 'E') {
355 ios.c_cflag |= PARENB;
356 }
357 ios.c_cflag |= stopb == 2 ? CSTOPB : 0;
358 ios.c_cflag |= !strcmp(fctr, "rts") ? CRTSCTS : 0;
359 tcsetattr(serial->dev, TCSANOW, &ios);
360 tcflush(serial->dev, TCIOFLUSH);
361 sprintf(msg, "%s", dev);
362 serial->tcpsvr = NULL;
363
364 /* open tcp sever to output received stream */
365 if (tcp_port > 0) {
366 sprintf(path_tcp, ":%d", tcp_port);
367 serial->tcpsvr = opentcpsvr(path_tcp, msg_tcp);
368 }
369 tracet(NULL, 3, "openserial: dev=%d\n", serial->dev);
370 return serial;
371}
372/* close serial --------------------------------------------------------------*/
373static void closeserial(serial_t* serial) {
374 tracet(NULL, 3, "closeserial: dev=%d\n", serial->dev);
375
376 if (!serial) {
377 return;
378 }
379 close(serial->dev);
380 if (serial->tcpsvr) {
381 closetcpsvr(serial->tcpsvr);
382 }
383 free(serial);
384}
385/* read serial ---------------------------------------------------------------*/
386static int readserial(serial_t* serial, uint8_t* buff, int n, char* msg) {
387 char msg_tcp[128];
388 int nr;
389 tracet(NULL, 4, "readserial: dev=%d n=%d\n", serial->dev, n);
390 if (!serial) {
391 return 0;
392 }
393 if ((nr = read(serial->dev, buff, n)) < 0) {
394 return 0;
395 }
396 tracet(NULL, 5, "readserial: exit dev=%d nr=%d\n", serial->dev, nr);
397
398 /* write received stream to tcp server port */
399 if (serial->tcpsvr && nr > 0) {
400 writetcpsvr(serial->tcpsvr, buff, (int)nr, msg_tcp);
401 }
402 return nr;
403}
404/* write serial --------------------------------------------------------------*/
405static int writeserial(serial_t* serial, uint8_t* buff, int n, char* msg) {
406 int ns;
407
408 if (!serial) {
409 return 0;
410 }
411 tracet(NULL, 3, "writeserial: dev=%d n=%d\n", serial->dev, n);
412
413 /* non-blocking port: a full output buffer (EAGAIN) or a short write is not
414 * a device error; the unsent part is not resent */
415 if ((ns = (int)write(serial->dev, buff, n)) < 0) {
416 if (errno != EAGAIN && errno != EWOULDBLOCK) {
417 serial->error = 1;
418 }
419 ns = 0;
420 }
421 tracet(NULL, 5, "writeserial: exit dev=%d ns=%d\n", serial->dev, ns);
422 return ns;
423}
424/* get state serial ----------------------------------------------------------*/
425static int stateserial(serial_t* serial) { return !serial ? 0 : (serial->error ? -1 : 2); }
426/* get extended state serial -------------------------------------------------*/
427static int statexserial(serial_t* serial, char* msg) {
428 char* p = msg;
429 int state = !serial ? 0 : (serial->error ? -1 : 2);
430
431 p += sprintf(p, "serial:\n");
432 p += sprintf(p, " state = %d\n", state);
433 if (!state) {
434 return 0;
435 }
436 p += sprintf(p, " dev = %d\n", serial->dev);
437 p += sprintf(p, " error = %d\n", serial->error);
438 return state;
439}
440/* open file -----------------------------------------------------------------*/
441static int openfile_(file_t* file, gtime_t time, char* msg) {
442 FILE* fp;
443 double time_sec;
444 uint32_t time_time;
445 char *rw, tagpath[MAXSTRPATH + 4] = "";
446 char tagh[TIMETAGH_LEN + 1] = "";
447
448 tracet(NULL, 3, "openfile_: path=%s time=%s\n", file->path, time_str(time, 0));
449
450 file->time = utc2gpst(timeget());
451 file->tick = file->tick_f = tickget();
452 file->fpos_n = 0;
453 file->tick_n = 0;
454
455 /* use stdin or stdout if file path is null */
456 if (!*file->path) {
457 file->fp = file->mode & STR_MODE_R ? stdin : stdout;
458 return 1;
459 }
460 /* replace keywords */
461 reppath(file->path, file->openpath, time, "", "");
462
463 /* create directory */
464 if ((file->mode & STR_MODE_W) && !(file->mode & STR_MODE_R)) {
465 createdir(file->openpath);
466 }
467 if (file->mode & STR_MODE_R) {
468 rw = "rb";
469 } else {
470 rw = "wb";
471 }
472
473 if (!(file->fp = fopen(file->openpath, rw))) {
474 sprintf(msg, "file open error: %s", file->openpath);
475 tracet(NULL, 1, "openfile: %s\n", msg);
476 return 0;
477 }
478 tracet(NULL, 4, "openfile_: open file %s (%s)\n", file->openpath, rw);
479
480 sprintf(tagpath, "%s.tag", file->openpath);
481
482 if (file->timetag) { /* output/sync time-tag */
483
484 if (!(file->fp_tag = fopen(tagpath, rw))) {
485 sprintf(msg, "tag open error: %s", tagpath);
486 tracet(NULL, 1, "openfile: %s\n", msg);
487 fclose(file->fp);
488 return 0;
489 }
490 tracet(NULL, 4, "openfile_: open tag file %s (%s)\n", tagpath, rw);
491
492 if (file->mode & STR_MODE_R) {
493 if (fread(&tagh, TIMETAGH_LEN, 1, file->fp_tag) == 1 &&
494 fread(&time_time, sizeof(time_time), 1, file->fp_tag) == 1 &&
495 fread(&time_sec, sizeof(time_sec), 1, file->fp_tag) == 1) {
496 memcpy(&file->tick_f, tagh + TIMETAGH_LEN - 4, sizeof(file->tick_f));
497 file->time.time = (time_t)time_time;
498 file->time.sec = time_sec;
499 file->wtime = file->time;
500 } else {
501 file->tick_f = 0;
502 }
503 /* adust time to read playback file */
504 timeset(gpst2utc(file->time));
505 } else {
506 sprintf(tagh, "TIMETAG %s %s", MRTKLIB_SOFTNAME, MRTKLIB_VERSION_STRING);
507 memcpy(tagh + TIMETAGH_LEN - 4, &file->tick_f, sizeof(file->tick_f));
508 time_time = (uint32_t)file->time.time;
509 time_sec = file->time.sec;
510 fwrite(&tagh, 1, TIMETAGH_LEN, file->fp_tag);
511 fwrite(&time_time, 1, sizeof(time_time), file->fp_tag);
512 fwrite(&time_sec, 1, sizeof(time_sec), file->fp_tag);
513 /* time tag file structure */
514 /* HEADER(60)+TICK(4)+TIME(4+8)+ */
515 /* TICK0(4)+FPOS0(4/8)+ */
516 /* TICK1(4)+FPOS1(4/8)+... */
517 }
518 } else if (file->mode & STR_MODE_W) { /* remove time-tag */
519 if ((fp = fopen(tagpath, "rb"))) {
520 fclose(fp);
521 remove(tagpath);
522 }
523 }
524 return 1;
525}
526/* close file ----------------------------------------------------------------*/
527static void closefile_(file_t* file) {
528 tracet(NULL, 3, "closefile_: path=%s\n", file->path);
529
530 if (file->fp) {
531 fclose(file->fp);
532 }
533 if (file->fp_tag) {
534 fclose(file->fp_tag);
535 }
536 if (file->fp_tmp) {
537 fclose(file->fp_tmp);
538 }
539 if (file->fp_tag_tmp) {
540 fclose(file->fp_tag_tmp);
541 }
542 file->fp = file->fp_tag = file->fp_tmp = file->fp_tag_tmp = NULL;
543
544 /* reset time offset */
545 timereset();
546}
547/* open file (path=filepath[::T[::+<off>][::x<speed>]][::S=swapintv][::P={4|8}] */
548static file_t* openfile(const char* path, int mode, char* msg) {
549 file_t* file;
550 gtime_t time, time0 = {0};
551 double speed = 1.0, start = 0.0, swapintv = 0.0;
552 char* p;
553 int timetag = 0, size_fpos = 4; /* default 4B */
554
555 tracet(NULL, 3, "openfile: path=%s mode=%d\n", path, mode);
556
557 if (!(mode & (STR_MODE_R | STR_MODE_W))) {
558 return NULL;
559 }
560
561 /* file options */
562 for (p = (char*)path; (p = strstr(p, "::")); p += 2) { /* file options */
563 if (*(p + 2) == 'T') {
564 timetag = 1;
565 } else if (*(p + 2) == '+') {
566 sscanf(p + 2, "+%lf", &start);
567 } else if (*(p + 2) == 'x') {
568 sscanf(p + 2, "x%lf", &speed);
569 } else if (*(p + 2) == 'S') {
570 sscanf(p + 2, "S=%lf", &swapintv);
571 } else if (*(p + 2) == 'P') {
572 sscanf(p + 2, "P=%d", &size_fpos);
573 }
574 }
575 if (start <= 0.0) {
576 start = 0.0;
577 }
578 if (swapintv <= 0.0) {
579 swapintv = 0.0;
580 }
581
582 if (!(file = (file_t*)malloc(sizeof(file_t)))) {
583 return NULL;
584 }
585
586 file->fp = file->fp_tag = file->fp_tmp = file->fp_tag_tmp = NULL;
587 strcpy(file->path, path);
588 if ((p = strstr(file->path, "::"))) {
589 *p = '\0';
590 }
591 file->openpath[0] = '\0';
592 file->mode = mode;
593 file->timetag = timetag;
594 file->repmode = 0;
595 file->offset = 0;
596 file->size_fpos = size_fpos;
597 file->time = file->wtime = time0;
598 file->tick = file->tick_f = file->tick_n = file->fpos_n = 0;
599 file->start = start;
600 file->speed = speed;
601 file->swapintv = swapintv;
602 rtk_initlock(&file->lock);
603
604 time = utc2gpst(timeget());
605
606 /* open new file */
607 if (!openfile_(file, time, msg)) {
608 free(file);
609 return NULL;
610 }
611 return file;
612}
613/* close file ----------------------------------------------------------------*/
614static void closefile(file_t* file) {
615 tracet(NULL, 3, "closefile: fp=%d\n", file->fp);
616
617 if (!file) {
618 return;
619 }
620 closefile_(file);
621 free(file);
622}
623/* open new swap file --------------------------------------------------------*/
624static void swapfile(file_t* file, gtime_t time, char* msg) {
625 char openpath[MAXSTRPATH];
626
627 tracet(NULL, 3, "swapfile: fp=%d time=%s\n", file->fp, time_str(time, 0));
628
629 /* return if old swap file open */
630 if (file->fp_tmp || file->fp_tag_tmp) {
631 return;
632 }
633
634 /* check path of new swap file */
635 reppath(file->path, openpath, time, "", "");
636
637 if (!strcmp(openpath, file->openpath)) {
638 tracet(NULL, 2, "swapfile: no need to swap %s\n", openpath);
639 return;
640 }
641 /* save file pointer to temporary pointer */
642 file->fp_tmp = file->fp;
643 file->fp_tag_tmp = file->fp_tag;
644
645 /* open new swap file */
646 openfile_(file, time, msg);
647}
648/* close old swap file -------------------------------------------------------*/
649static void swapclose(file_t* file) {
650 tracet(NULL, 3, "swapclose: fp_tmp=%d\n", file->fp_tmp);
651
652 if (file->fp_tmp) {
653 fclose(file->fp_tmp);
654 }
655 if (file->fp_tag_tmp) {
656 fclose(file->fp_tag_tmp);
657 }
658 file->fp_tmp = file->fp_tag_tmp = NULL;
659}
660/* get state file ------------------------------------------------------------*/
661static int statefile(file_t* file) { return file ? 2 : 0; }
662/* get extended state file ---------------------------------------------------*/
663static int statexfile(file_t* file, char* msg) {
664 char *p = msg, tstr1[32], tstr2[32];
665 int state = file ? 2 : 0;
666
667 p += sprintf(p, "file:\n");
668 p += sprintf(p, " state = %d\n", state);
669 if (!state) {
670 return 0;
671 }
672 time2str(file->time, tstr1, 3);
673 time2str(file->wtime, tstr2, 3);
674 p += sprintf(p, " path = %s\n", file->path);
675 p += sprintf(p, " openpath= %s\n", file->openpath);
676 p += sprintf(p, " mode = %d\n", file->mode);
677 p += sprintf(p, " timetag = %d\n", file->timetag);
678 p += sprintf(p, " repmode = %d\n", file->repmode);
679 p += sprintf(p, " offsete = %d\n", file->offset);
680 p += sprintf(p, " time = %s\n", tstr1);
681 p += sprintf(p, " wtime = %s\n", tstr2);
682 p += sprintf(p, " tick = %u\n", file->tick);
683 p += sprintf(p, " tick_f = %u\n", file->tick_f);
684 p += sprintf(p, " start = %.3f\n", file->start);
685 p += sprintf(p, " speed = %.3f\n", file->speed);
686 p += sprintf(p, " swapintv= %.3f\n", file->swapintv);
687 return state;
688}
689/* read file -----------------------------------------------------------------*/
690static int readfile(file_t* file, uint8_t* buff, int nmax, char* msg) {
691 struct timeval tv = {0};
692 fd_set rs;
693 uint64_t fpos_8B;
694 uint32_t t, tick, fpos_4B;
695 long pos, n;
696 int nr = 0;
697
698 tracet(NULL, 4, "readfile: fp=%d nmax=%d\n", file->fp, nmax);
699
700 if (!file) {
701 return 0;
702 }
703
704 if (file->fp == stdin) {
705 /* input from stdin */
706 FD_ZERO(&rs);
707 FD_SET(0, &rs);
708 if (!select(1, &rs, NULL, NULL, &tv)) {
709 return 0;
710 }
711 if ((nr = (int)read(0, buff, nmax)) < 0) {
712 return 0;
713 }
714 return nr;
715 }
716 if (file->fp_tag) {
717 /* target tick */
718 if (file->repmode) { /* slave */
719 t = (uint32_t)(tick_master + file->offset);
720 } else { /* master */
721 t = (uint32_t)((tickget() - file->tick) * file->speed + file->start * 1000.0);
722 tick_master = t;
723 }
724 /* seek time-tag file to get next tick and file position */
725 while ((int)(file->tick_n - t) <= 0) {
726 if (fread(&file->tick_n, sizeof(tick), 1, file->fp_tag) < 1 ||
727 fread((file->size_fpos == 4) ? (void*)&fpos_4B : (void*)&fpos_8B, file->size_fpos, 1, file->fp_tag) <
728 1) {
729 file->tick_n = (uint32_t)(-1);
730 pos = ftell(file->fp);
731 fseek(file->fp, 0L, SEEK_END);
732 file->fpos_n = ftell(file->fp);
733 fseek(file->fp, pos, SEEK_SET);
734 break;
735 }
736 file->fpos_n = (long)((file->size_fpos == 4) ? fpos_4B : fpos_8B);
737 }
738 if (file->tick_n == (uint32_t)(-1)) {
739 sprintf(msg, "end");
740 } else {
741 sprintf(msg, "T%+.1fs", (int)t * 0.001);
742 file->wtime = timeadd(file->time, (int)t * 0.001);
743 timeset(timeadd(gpst2utc(file->time), (int)file->tick_n * 0.001));
744 }
745 if ((n = file->fpos_n - ftell(file->fp)) < nmax) {
746 nmax = n;
747 }
748 }
749 if (nmax > 0) {
750 nr = (int)fread(buff, 1, nmax, file->fp);
751 }
752 if (feof(file->fp)) {
753 sprintf(msg, "end");
754 }
755 tracet(NULL, 5, "readfile: fp=%d nr=%d\n", file->fp, nr);
756 return nr;
757}
758/* write file ----------------------------------------------------------------*/
759static int writefile(file_t* file, uint8_t* buff, int n, char* msg) {
760 gtime_t wtime;
761 uint64_t fpos_8B;
762 uint32_t tick = tickget(), fpos_4B;
763 int week1, week2, ns;
764 double tow1, tow2, intv;
765 long fpos, fpos_tmp = 0;
766
767 tracet(NULL, 4, "writefile: fp=%d n=%d\n", file->fp, n);
768
769 if (!file) {
770 return 0;
771 }
772
773 wtime = utc2gpst(timeget()); /* write time in gpst */
774
775 /* swap writing file */
776 if (file->swapintv > 0.0 && file->wtime.time != 0) {
777 intv = file->swapintv * 3600.0;
778 tow1 = time2gpst(file->wtime, &week1);
779 tow2 = time2gpst(wtime, &week2);
780 tow2 += 604800.0 * (week2 - week1);
781
782 /* open new swap file */
783 if (floor((tow1 + fswapmargin) / intv) < floor((tow2 + fswapmargin) / intv)) {
784 swapfile(file, timeadd(wtime, fswapmargin), msg);
785 }
786 /* close old swap file */
787 if (floor((tow1 - fswapmargin) / intv) < floor((tow2 - fswapmargin) / intv)) {
788 swapclose(file);
789 }
790 }
791 if (!file->fp) {
792 return 0;
793 }
794
795 ns = (int)fwrite(buff, 1, n, file->fp);
796 fpos = ftell(file->fp);
797 fflush(file->fp);
798 file->wtime = wtime;
799
800 if (file->fp_tmp) {
801 fwrite(buff, 1, n, file->fp_tmp);
802 fpos_tmp = ftell(file->fp_tmp);
803 fflush(file->fp_tmp);
804 }
805 if (file->fp_tag) {
806 tick -= file->tick;
807 fwrite(&tick, 1, sizeof(tick), file->fp_tag);
808 if (file->size_fpos == 4) {
809 fpos_4B = (uint32_t)fpos;
810 fwrite(&fpos_4B, 1, sizeof(fpos_4B), file->fp_tag);
811 } else {
812 fpos_8B = (uint64_t)fpos;
813 fwrite(&fpos_8B, 1, sizeof(fpos_8B), file->fp_tag);
814 }
815 fflush(file->fp_tag);
816
817 if (file->fp_tag_tmp) {
818 fwrite(&tick, 1, sizeof(tick), file->fp_tag_tmp);
819 if (file->size_fpos == 4) {
820 fpos_4B = (uint32_t)fpos_tmp;
821 fwrite(&fpos_4B, 1, sizeof(fpos_4B), file->fp_tag_tmp);
822 } else {
823 fpos_8B = (uint64_t)fpos_tmp;
824 fwrite(&fpos_8B, 1, sizeof(fpos_8B), file->fp_tag_tmp);
825 }
826 fflush(file->fp_tag_tmp);
827 }
828 }
829 tracet(NULL, 5, "writefile: fp=%d ns=%d tick=%5d fpos=%d\n", file->fp, ns, tick, fpos);
830
831 return ns;
832}
833/* sync files by time-tag ----------------------------------------------------*/
834static void syncfile(file_t* file1, file_t* file2) {
835 if (!file1->fp_tag || !file2->fp_tag) {
836 return;
837 }
838 file1->repmode = 0;
839 file2->repmode = 1;
840 file2->offset = (int)(file1->tick_f - file2->tick_f);
841}
842/* decode percent-encoded characters in-place (%XX -> char) -----------------*/
843static void urldecode(char* str) {
844 char *src = str, *dst = str;
845 while (*src) {
846 if (*src == '%' && src[1] && src[2]) {
847 int hi = src[1], lo = src[2];
848 hi = (hi >= '0' && hi <= '9') ? hi - '0'
849 : (hi >= 'A' && hi <= 'F') ? hi - 'A' + 10
850 : (hi >= 'a' && hi <= 'f') ? hi - 'a' + 10
851 : -1;
852 lo = (lo >= '0' && lo <= '9') ? lo - '0'
853 : (lo >= 'A' && lo <= 'F') ? lo - 'A' + 10
854 : (lo >= 'a' && lo <= 'f') ? lo - 'a' + 10
855 : -1;
856 if (hi >= 0 && lo >= 0) {
857 char ch = (char)((hi << 4) | lo);
858 if (ch == '\0' || ch == '\r' || ch == '\n' || ch == ' ') {
859 /* reject NUL, CR, LF, space to prevent string truncation,
860 * request injection, and v1 SOURCE command delimiter issues */
861 src += 3;
862 continue;
863 }
864 *dst++ = ch;
865 src += 3;
866 continue;
867 }
868 }
869 *dst++ = *src++;
870 }
871 *dst = '\0';
872}
873/* decode tcp/ntrip path (path=[user[:passwd]@]addr[:port][/mntpnt[:str]]) ---*/
874static void decodetcppath(const char* path, char* addr, char* port, char* user, char* passwd, char* mntpnt, char* str) {
875 char buff[MAXSTRPATH], *p, *q;
876
877 tracet(NULL, 4, "decodetcpepath: path=%s\n", path);
878
879 if (port) {
880 *port = '\0';
881 }
882 if (user) {
883 *user = '\0';
884 }
885 if (passwd) {
886 *passwd = '\0';
887 }
888 if (mntpnt) {
889 *mntpnt = '\0';
890 }
891 if (str) {
892 *str = '\0';
893 }
894
895 strcpy(buff, path);
896
897 if (!(p = strrchr(buff, '@'))) {
898 p = buff;
899 }
900
901 if ((p = strchr(p, '/'))) {
902 if ((q = strchr(p + 1, ':'))) {
903 *q = '\0';
904 if (str) {
905 sprintf(str, "%.*s", NTRIP_MAXSTR - 1, q + 1);
906 }
907 }
908 *p = '\0';
909 if (mntpnt) {
910 sprintf(mntpnt, "%.255s", p + 1);
911 }
912 }
913 if ((p = strrchr(buff, '@'))) {
914 *p++ = '\0';
915 if ((q = strchr(buff, ':'))) {
916 *q = '\0';
917 if (passwd) {
918 sprintf(passwd, "%.255s", q + 1);
919 }
920 }
921 if (user) {
922 sprintf(user, "%.255s", buff);
923 urldecode(user);
924 }
925 if (passwd) {
926 urldecode(passwd);
927 }
928 } else {
929 p = buff;
930 }
931
932 if ((q = strchr(p, ':'))) {
933 *q = '\0';
934 if (port) {
935 sprintf(port, "%.255s", q + 1);
936 }
937 }
938 if (addr) {
939 sprintf(addr, "%.255s", p);
940 }
941}
942/* get socket error ----------------------------------------------------------*/
943static int errsock(void) { return errno; }
944
945/* set socket option ---------------------------------------------------------*/
946static int setsock(socket_t sock, char* msg) {
947 int bs = buffsize, mode = 1;
948 struct timeval tv = {0};
949 tracet(NULL, 3, "setsock: sock=%d\n", sock);
950
951 if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv)) == -1 ||
952 setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv)) == -1) {
953 sprintf(msg, "sockopt error: notimeo");
954 tracet(NULL, 1, "setsock: setsockopt error 1 sock=%d err=%d\n", sock, errsock());
955 closesocket(sock);
956 return 0;
957 }
958 if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (const char*)&bs, sizeof(bs)) == -1 ||
959 setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (const char*)&bs, sizeof(bs)) == -1) {
960 tracet(NULL, 1, "setsock: setsockopt error 2 sock=%d err=%d bs=%d\n", sock, errsock(), bs);
961 sprintf(msg, "sockopt error: bufsiz");
962 }
963 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (const char*)&mode, sizeof(mode)) == -1) {
964 tracet(NULL, 1, "setsock: setsockopt error 3 sock=%d err=%d\n", sock, errsock());
965 sprintf(msg, "sockopt error: nodelay");
966 }
967 return 1;
968}
969/* non-block accept ----------------------------------------------------------*/
970static socket_t accept_nb(socket_t sock, struct sockaddr* addr, socklen_t* len) {
971 struct timeval tv = {0};
972 fd_set rs;
973 int ret;
974
975 FD_ZERO(&rs);
976 FD_SET(sock, &rs);
977 ret = select(sock + 1, &rs, NULL, NULL, &tv);
978 if (ret <= 0) {
979 return (socket_t)ret;
980 }
981 return accept(sock, addr, len);
982}
983/* non-block connect ---------------------------------------------------------*/
984static int connect_nb(socket_t sock, struct sockaddr* addr, socklen_t len) {
985 struct timeval tv = {0};
986 fd_set rs, ws;
987 int err, flag;
988
989 flag = fcntl(sock, F_GETFL, 0);
990 fcntl(sock, F_SETFL, flag | O_NONBLOCK);
991 if (connect(sock, addr, len) == -1) {
992 err = errsock();
993 if (err != EISCONN && err != EINPROGRESS && err != EALREADY) {
994 return -1;
995 }
996 FD_ZERO(&rs);
997 FD_SET(sock, &rs);
998 ws = rs;
999 if (select(sock + 1, &rs, &ws, NULL, &tv) == 0) {
1000 return 0;
1001 }
1002 }
1003 return 1;
1004}
1005/* non-block receive ---------------------------------------------------------*/
1006static int recv_nb(socket_t sock, uint8_t* buff, int n) {
1007 struct timeval tv = {0};
1008 fd_set rs;
1009 int ret, nr;
1010
1011 FD_ZERO(&rs);
1012 FD_SET(sock, &rs);
1013 ret = select(sock + 1, &rs, NULL, NULL, &tv);
1014 if (ret <= 0) {
1015 return ret;
1016 }
1017 nr = recv(sock, (char*)buff, n, 0);
1018 return nr <= 0 ? -1 : nr;
1019}
1020/* non-block send ------------------------------------------------------------*/
1021static int send_nb(socket_t sock, uint8_t* buff, int n) {
1022 struct timeval tv = {0};
1023 fd_set ws;
1024 int ret, ns;
1025
1026 FD_ZERO(&ws);
1027 FD_SET(sock, &ws);
1028 ret = select(sock + 1, NULL, &ws, NULL, &tv);
1029 if (ret <= 0) {
1030 return ret;
1031 }
1032 ns = send(sock, (char*)buff, n, 0);
1033 return ns < n ? -1 : ns;
1034}
1035/* generate tcp socket -------------------------------------------------------*/
1036static int gentcp(tcp_t* tcp, int type, char* msg) {
1037 struct hostent* hp;
1038#ifdef SVR_REUSEADDR
1039 int opt = 1;
1040#endif
1041
1042 tracet(NULL, 3, "gentcp: type=%d\n", type);
1043
1044 /* generate socket */
1045 if ((tcp->sock = socket(AF_INET, SOCK_STREAM, 0)) == (socket_t)-1) {
1046 sprintf(msg, "socket error (%d)", errsock());
1047 tracet(NULL, 1, "gentcp: socket error err=%d\n", errsock());
1048 tcp->state = -1;
1049 return 0;
1050 }
1051 if (!setsock(tcp->sock, msg)) {
1052 tcp->state = -1;
1053 return 0;
1054 }
1055 memset(&tcp->addr, 0, sizeof(tcp->addr));
1056 tcp->addr.sin_family = AF_INET;
1057 tcp->addr.sin_port = htons(tcp->port);
1058
1059 if (type == 0) { /* server socket */
1060
1061#ifdef SVR_REUSEADDR
1062 /* multiple-use of server socket */
1063 setsockopt(tcp->sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
1064#endif
1065 if (bind(tcp->sock, (struct sockaddr*)&tcp->addr, sizeof(tcp->addr)) == -1) {
1066 sprintf(msg, "bind error (%d) : %d", errsock(), tcp->port);
1067 tracet(NULL, 1, "gentcp: bind error port=%d err=%d\n", tcp->port, errsock());
1068 closesocket(tcp->sock);
1069 tcp->state = -1;
1070 return 0;
1071 }
1072 listen(tcp->sock, 5);
1073 } else { /* client socket */
1074 if (!(hp = gethostbyname(tcp->saddr))) {
1075 sprintf(msg, "address error (%s)", tcp->saddr);
1076 tracet(NULL, 1, "gentcp: gethostbyname error addr=%s err=%d\n", tcp->saddr, errsock());
1077 closesocket(tcp->sock);
1078 tcp->state = 0;
1079 tcp->tcon = ticonnect;
1080 tcp->tdis = tickget();
1081 return 0;
1082 }
1083 memcpy(&tcp->addr.sin_addr, hp->h_addr_list[0], hp->h_length);
1084 }
1085 tcp->state = 1;
1086 tcp->tact = tickget();
1087 tracet(NULL, 5, "gentcp: exit sock=%d\n", tcp->sock);
1088 return 1;
1089}
1090/* disconnect tcp ------------------------------------------------------------*/
1091static void discontcp(tcp_t* tcp, int tcon) {
1092 tracet(NULL, 3, "discontcp: sock=%d tcon=%d\n", tcp->sock, tcon);
1093
1094 closesocket(tcp->sock);
1095 tcp->state = 0;
1096 tcp->tcon = tcon;
1097 tcp->tdis = tickget();
1098}
1099/* open tcp server -----------------------------------------------------------*/
1100static tcpsvr_t* opentcpsvr(const char* path, char* msg) {
1101 tcpsvr_t *tcpsvr, tcpsvr0 = {{0}};
1102 char port[256] = "";
1103
1104 tracet(NULL, 3, "opentcpsvr: path=%s\n", path);
1105
1106 if (!(tcpsvr = (tcpsvr_t*)malloc(sizeof(tcpsvr_t)))) {
1107 return NULL;
1108 }
1109 *tcpsvr = tcpsvr0;
1110 decodetcppath(path, tcpsvr->svr.saddr, port, NULL, NULL, NULL, NULL);
1111 if (sscanf(port, "%d", &tcpsvr->svr.port) < 1) {
1112 sprintf(msg, "port error: %s", port);
1113 tracet(NULL, 1, "opentcpsvr: port error port=%s\n", port);
1114 free(tcpsvr);
1115 return NULL;
1116 }
1117 if (!gentcp(&tcpsvr->svr, 0, msg)) {
1118 free(tcpsvr);
1119 return NULL;
1120 }
1121 tcpsvr->svr.tcon = 0;
1122 return tcpsvr;
1123}
1124/* close tcp server ----------------------------------------------------------*/
1125static void closetcpsvr(tcpsvr_t* tcpsvr) {
1126 int i;
1127
1128 tracet(NULL, 3, "closetcpsvr:\n");
1129
1130 for (i = 0; i < MAXCLI; i++) {
1131 if (tcpsvr->cli[i].state) {
1132 closesocket(tcpsvr->cli[i].sock);
1133 }
1134 }
1135 closesocket(tcpsvr->svr.sock);
1136 free(tcpsvr);
1137}
1138/* update tcp server ---------------------------------------------------------*/
1139static void updatetcpsvr(tcpsvr_t* tcpsvr, char* msg) {
1140 char saddr[256] = "";
1141 int i, n = 0;
1142
1143 tracet(NULL, 4, "updatetcpsvr: state=%d\n", tcpsvr->svr.state);
1144
1145 if (tcpsvr->svr.state == 0) {
1146 return;
1147 }
1148
1149 for (i = 0; i < MAXCLI; i++) {
1150 if (!tcpsvr->cli[i].state) {
1151 continue;
1152 }
1153 strcpy(saddr, tcpsvr->cli[i].saddr);
1154 n++;
1155 }
1156 if (n == 0) {
1157 tcpsvr->svr.state = 1;
1158 sprintf(msg, "waiting...");
1159 return;
1160 }
1161 tcpsvr->svr.state = 2;
1162 if (n == 1) {
1163 sprintf(msg, "%s", saddr);
1164 } else {
1165 sprintf(msg, "%d clients", n);
1166 }
1167}
1168/* accept client connection --------------------------------------------------*/
1169static int accsock(tcpsvr_t* tcpsvr, char* msg) {
1170 struct sockaddr_in addr;
1171 socket_t sock;
1172 socklen_t len = sizeof(addr);
1173 int i, err;
1174
1175 tracet(NULL, 4, "accsock: sock=%d\n", tcpsvr->svr.sock);
1176
1177 for (i = 0; i < MAXCLI; i++) {
1178 if (tcpsvr->cli[i].state == 0) {
1179 break;
1180 }
1181 }
1182 if (i >= MAXCLI) {
1183 tracet(NULL, 2, "accsock: too many clients sock=%d\n", tcpsvr->svr.sock);
1184 return 0;
1185 }
1186 if ((sock = accept_nb(tcpsvr->svr.sock, (struct sockaddr*)&addr, &len)) == (socket_t)-1) {
1187 err = errsock();
1188 sprintf(msg, "accept error (%d)", err);
1189 tracet(NULL, 1, "accsock: accept error sock=%d err=%d\n", tcpsvr->svr.sock, err);
1190 closesocket(tcpsvr->svr.sock);
1191 tcpsvr->svr.state = 0;
1192 return 0;
1193 }
1194 if (sock == 0) {
1195 return 0;
1196 }
1197 if (!setsock(sock, msg)) {
1198 return 0;
1199 }
1200
1201 tcpsvr->cli[i].sock = sock;
1202 memcpy(&tcpsvr->cli[i].addr, &addr, sizeof(addr));
1203 strcpy(tcpsvr->cli[i].saddr, inet_ntoa(addr.sin_addr));
1204 sprintf(msg, "%s", tcpsvr->cli[i].saddr);
1205 tracet(NULL, 3, "accsock: connected sock=%d addr=%s i=%d\n", tcpsvr->cli[i].sock, tcpsvr->cli[i].saddr, i);
1206 tcpsvr->cli[i].state = 2;
1207 tcpsvr->cli[i].tact = tickget();
1208 return 1;
1209}
1210/* wait socket accept --------------------------------------------------------*/
1211static int waittcpsvr(tcpsvr_t* tcpsvr, char* msg) {
1212 tracet(NULL, 4, "waittcpsvr: sock=%d state=%d\n", tcpsvr->svr.sock, tcpsvr->svr.state);
1213
1214 if (tcpsvr->svr.state <= 0) {
1215 return 0;
1216 }
1217
1218 while (accsock(tcpsvr, msg)) {
1219 /* accept all pending connections */
1220 }
1221
1222 updatetcpsvr(tcpsvr, msg);
1223 return tcpsvr->svr.state == 2;
1224}
1225/* read tcp server -----------------------------------------------------------*/
1226static int readtcpsvr(tcpsvr_t* tcpsvr, uint8_t* buff, int n, char* msg) {
1227 int i, nr, err;
1228
1229 tracet(NULL, 4, "readtcpsvr: state=%d\n", tcpsvr->svr.state);
1230
1231 if (!waittcpsvr(tcpsvr, msg)) {
1232 return 0;
1233 }
1234
1235 for (i = 0; i < MAXCLI; i++) {
1236 if (tcpsvr->cli[i].state != 2) {
1237 continue;
1238 }
1239
1240 if ((nr = recv_nb(tcpsvr->cli[i].sock, buff, n)) == -1) {
1241 if ((err = errsock())) {
1242 tracet(NULL, 2, "readtcpsvr: recv error sock=%d err=%d\n", tcpsvr->cli[i].sock, err);
1243 }
1244 discontcp(&tcpsvr->cli[i], ticonnect);
1245 updatetcpsvr(tcpsvr, msg);
1246 }
1247 if (nr > 0) {
1248 tcpsvr->cli[i].tact = tickget();
1249 return nr;
1250 }
1251 }
1252 return 0;
1253}
1254/* write tcp server ----------------------------------------------------------*/
1255static int writetcpsvr(tcpsvr_t* tcpsvr, uint8_t* buff, int n, char* msg) {
1256 int i, ns = 0, nmax = 0, err;
1257
1258 tracet(NULL, 4, "writetcpsvr: state=%d n=%d\n", tcpsvr->svr.state, n);
1259
1260 if (!waittcpsvr(tcpsvr, msg)) {
1261 return 0;
1262 }
1263
1264 for (i = 0; i < MAXCLI; i++) {
1265 if (tcpsvr->cli[i].state != 2) {
1266 continue;
1267 }
1268
1269 if ((ns = send_nb(tcpsvr->cli[i].sock, buff, n)) == -1) {
1270 if ((err = errsock())) {
1271 tracet(NULL, 2, "writetcpsvr: send error i=%d sock=%d err=%d\n", i, tcpsvr->cli[i].sock, err);
1272 }
1273 discontcp(&tcpsvr->cli[i], ticonnect);
1274 updatetcpsvr(tcpsvr, msg);
1275 } else {
1276 if (ns > nmax) {
1277 nmax = ns;
1278 }
1279 if (ns > 0) {
1280 tcpsvr->cli[i].tact = tickget();
1281 }
1282 }
1283 }
1284 return nmax;
1285}
1286/* get state tcp server ------------------------------------------------------*/
1287static int statetcpsvr(tcpsvr_t* tcpsvr) { return tcpsvr ? tcpsvr->svr.state : 0; }
1288/* print extended state tcp --------------------------------------------------*/
1289static int statextcp(tcp_t* tcp, char* msg) {
1290 char* p = msg;
1291
1292 p += sprintf(p, " state = %d\n", tcp->state);
1293 p += sprintf(p, " saddr = %s\n", tcp->saddr);
1294 p += sprintf(p, " port = %d\n", tcp->port);
1295 p += sprintf(p, " sock = %d\n", (int)tcp->sock);
1296#if 0 /* for debug */
1297 p+=sprintf(p," tcon = %d\n",tcp->tcon);
1298 p+=sprintf(p," tact = %u\n",tcp->tact);
1299 p+=sprintf(p," tdis = %u\n",tcp->tdis);
1300#endif
1301 return (int)(p - msg);
1302}
1303/* get extended state tcp server ---------------------------------------------*/
1304static int statextcpsvr(tcpsvr_t* tcpsvr, char* msg) {
1305 char* p = msg;
1306 int i, state = tcpsvr ? tcpsvr->svr.state : 0;
1307
1308 p += sprintf(p, "tcpsvr:\n");
1309 p += sprintf(p, " state = %d\n", state);
1310 if (!state) {
1311 return 0;
1312 }
1313 p += sprintf(p, " svr:\n");
1314 p += statextcp(&tcpsvr->svr, p);
1315 for (i = 0; i < MAXCLI; i++) {
1316 if (!tcpsvr->cli[i].state) {
1317 continue;
1318 }
1319 p += sprintf(p, " cli#%d:\n", i);
1320 p += statextcp(tcpsvr->cli + i, p);
1321 }
1322 return state;
1323}
1324/* connect server ------------------------------------------------------------*/
1325static int consock(tcpcli_t* tcpcli, char* msg) {
1326 int stat, err;
1327
1328 tracet(NULL, 4, "consock: sock=%d\n", tcpcli->svr.sock);
1329
1330 /* wait re-connect */
1331 if (tcpcli->svr.tcon < 0 || (tcpcli->svr.tcon > 0 && (int)(tickget() - tcpcli->svr.tdis) < tcpcli->svr.tcon)) {
1332 return 0;
1333 }
1334 /* non-block connect */
1335 if ((stat = connect_nb(tcpcli->svr.sock, (struct sockaddr*)&tcpcli->svr.addr, sizeof(tcpcli->svr.addr))) == -1) {
1336 err = errsock();
1337 sprintf(msg, "connect error (%d)", err);
1338 tracet(NULL, 2, "consock: connect error sock=%d err=%d\n", tcpcli->svr.sock, err);
1339 closesocket(tcpcli->svr.sock);
1340 tcpcli->svr.state = 0;
1341 return 0;
1342 }
1343 if (!stat) { /* not connect */
1344 sprintf(msg, "connecting...");
1345 return 0;
1346 }
1347 sprintf(msg, "%s", tcpcli->svr.saddr);
1348 tracet(NULL, 3, "consock: connected sock=%d addr=%s\n", tcpcli->svr.sock, tcpcli->svr.saddr);
1349 tcpcli->svr.state = 2;
1350 tcpcli->svr.tact = tickget();
1351 return 1;
1352}
1353/* open tcp client -----------------------------------------------------------*/
1354static tcpcli_t* opentcpcli(const char* path, char* msg) {
1355 tcpcli_t *tcpcli, tcpcli0 = {{0}};
1356 char port[256] = "";
1357
1358 tracet(NULL, 3, "opentcpcli: path=%s\n", path);
1359
1360 if (!(tcpcli = (tcpcli_t*)malloc(sizeof(tcpcli_t)))) {
1361 return NULL;
1362 }
1363 *tcpcli = tcpcli0;
1364 decodetcppath(path, tcpcli->svr.saddr, port, NULL, NULL, NULL, NULL);
1365 if (sscanf(port, "%d", &tcpcli->svr.port) < 1) {
1366 sprintf(msg, "port error: %s", port);
1367 tracet(NULL, 2, "opentcp: port error port=%s\n", port);
1368 free(tcpcli);
1369 return NULL;
1370 }
1371 tcpcli->svr.tcon = 0;
1372 tcpcli->toinact = toinact;
1373 tcpcli->tirecon = ticonnect;
1374 return tcpcli;
1375}
1376/* close tcp client ----------------------------------------------------------*/
1377static void closetcpcli(tcpcli_t* tcpcli) {
1378 tracet(NULL, 3, "closetcpcli: sock=%d\n", tcpcli->svr.sock);
1379
1380 closesocket(tcpcli->svr.sock);
1381 free(tcpcli);
1382}
1383/* wait socket connect -------------------------------------------------------*/
1384static int waittcpcli(tcpcli_t* tcpcli, char* msg) {
1385 tracet(NULL, 4, "waittcpcli: sock=%d state=%d\n", tcpcli->svr.sock, tcpcli->svr.state);
1386
1387 if (tcpcli->svr.state < 0) {
1388 return 0;
1389 }
1390
1391 if (tcpcli->svr.state == 0) { /* close */
1392 if (!gentcp(&tcpcli->svr, 1, msg)) {
1393 return 0;
1394 }
1395 }
1396 if (tcpcli->svr.state == 1) { /* wait */
1397 if (!consock(tcpcli, msg)) {
1398 return 0;
1399 }
1400 }
1401 if (tcpcli->svr.state == 2) { /* connect */
1402 if (tcpcli->toinact > 0 && (int)(tickget() - tcpcli->svr.tact) > tcpcli->toinact) {
1403 sprintf(msg, "timeout");
1404 tracet(NULL, 2, "waittcpcli: inactive timeout sock=%d\n", tcpcli->svr.sock);
1405 discontcp(&tcpcli->svr, tcpcli->tirecon);
1406 return 0;
1407 }
1408 }
1409 return 1;
1410}
1411/* read tcp client -----------------------------------------------------------*/
1412static int readtcpcli(tcpcli_t* tcpcli, uint8_t* buff, int n, char* msg) {
1413 int nr, err;
1414
1415 tracet(NULL, 4, "readtcpcli: sock=%d\n", tcpcli->svr.sock);
1416
1417 if (!waittcpcli(tcpcli, msg)) {
1418 return 0;
1419 }
1420
1421 if ((nr = recv_nb(tcpcli->svr.sock, buff, n)) == -1) {
1422 if ((err = errsock())) {
1423 tracet(NULL, 2, "readtcpcli: recv error sock=%d err=%d\n", tcpcli->svr.sock, err);
1424 sprintf(msg, "recv error (%d)", err);
1425 } else {
1426 sprintf(msg, "disconnected");
1427 }
1428 discontcp(&tcpcli->svr, tcpcli->tirecon);
1429 return 0;
1430 }
1431 if (nr > 0) {
1432 tcpcli->svr.tact = tickget();
1433 }
1434 tracet(NULL, 5, "readtcpcli: exit sock=%d nr=%d\n", tcpcli->svr.sock, nr);
1435 return nr;
1436}
1437/* write tcp client ----------------------------------------------------------*/
1438static int writetcpcli(tcpcli_t* tcpcli, uint8_t* buff, int n, char* msg) {
1439 int ns, err;
1440
1441 tracet(NULL, 3, "writetcpcli: sock=%d state=%d n=%d\n", tcpcli->svr.sock, tcpcli->svr.state, n);
1442
1443 if (!waittcpcli(tcpcli, msg)) {
1444 return 0;
1445 }
1446
1447 if ((ns = send_nb(tcpcli->svr.sock, buff, n)) == -1) {
1448 if ((err = errsock())) {
1449 tracet(NULL, 2, "writetcp: send error sock=%d err=%d\n", tcpcli->svr.sock, err);
1450 sprintf(msg, "send error (%d)", err);
1451 }
1452 discontcp(&tcpcli->svr, tcpcli->tirecon);
1453 return 0;
1454 }
1455 if (ns > 0) {
1456 tcpcli->svr.tact = tickget();
1457 }
1458 tracet(NULL, 5, "writetcpcli: exit sock=%d ns=%d\n", tcpcli->svr.sock, ns);
1459 return ns;
1460}
1461/* get state tcp client ------------------------------------------------------*/
1462static int statetcpcli(tcpcli_t* tcpcli) { return tcpcli ? tcpcli->svr.state : 0; }
1463/* get extended state tcp client ---------------------------------------------*/
1464static int statextcpcli(tcpcli_t* tcpcli, char* msg) { return tcpcli ? tcpcli->svr.state : 0; }
1465/* base64 encoder ------------------------------------------------------------*/
1466static int encbase64(char* str, const uint8_t* byte, int n) {
1467 const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1468 int i, j, k, b;
1469
1470 tracet(NULL, 4, "encbase64: n=%d\n", n);
1471
1472 for (i = j = 0; i / 8 < n;) {
1473 for (k = b = 0; k < 6; k++, i++) {
1474 b <<= 1;
1475 if (i / 8 < n) {
1476 b |= (byte[i / 8] >> (7 - i % 8)) & 0x1;
1477 }
1478 }
1479 str[j++] = table[b];
1480 }
1481 while (j & 0x3) {
1482 str[j++] = '=';
1483 }
1484 str[j] = '\0';
1485 tracet(NULL, 5, "encbase64: str=%s\n", str);
1486 return j;
1487}
1488/* safe append to buffer with overflow tracking */
1489#define BUFADD(p, rem, ...) \
1490 do { \
1491 int _n = snprintf(p, rem, __VA_ARGS__); \
1492 if (_n > 0 && _n < (rem)) { \
1493 (p) += _n; \
1494 (rem) -= _n; \
1495 } else { \
1496 (rem) = 0; \
1497 } \
1498 } while (0)
1499/* send ntrip server request -------------------------------------------------*/
1500static int reqntrip_s(ntrip_t* ntrip, char* msg) {
1501 char buff[2048], user[514], *p = buff;
1502 int rem = (int)sizeof(buff);
1503 int use_v2 = (ntrip->ver == NTRIP_VER_2 ||
1504 (ntrip->ver == NTRIP_VER_AUTO && (!ntrip->v2_tried || ntrip->ver_neg == NTRIP_VER_2)));
1505
1506 tracet(NULL, 3, "reqntrip_s: state=%d ver=%d\n", ntrip->state, ntrip->ver);
1507
1508 if (use_v2) {
1509 /* NTRIP v2: POST with HTTP/1.1 (use absolute URL for proxy) */
1510 if (*ntrip->url) {
1511 BUFADD(p, rem, "POST %s/%s HTTP/1.1\r\n", ntrip->url, ntrip->mntpnt);
1512 } else {
1513 BUFADD(p, rem, "POST /%s HTTP/1.1\r\n", ntrip->mntpnt);
1514 }
1515 BUFADD(p, rem, "Host: %s\r\n", ntrip->host);
1516 BUFADD(p, rem, "Ntrip-Version: Ntrip/2.0\r\n");
1517 BUFADD(p, rem, "User-Agent: NTRIP %s\r\n", NTRIP_AGENT);
1518 if (*ntrip->user) {
1519 snprintf(user, sizeof(user), "%s:%s", ntrip->user, ntrip->passwd);
1520 BUFADD(p, rem, "Authorization: Basic ");
1521 if (rem > 0) {
1522 int b64_need = (int)(((strlen(user) + 2) / 3) * 4 + 1);
1523 if (b64_need > rem) {
1524 sprintf(msg, "request buffer overflow");
1525 return 0;
1526 }
1527 int n = encbase64(p, (uint8_t*)user, strlen(user));
1528 p += n;
1529 rem -= n;
1530 }
1531 BUFADD(p, rem, "\r\n");
1532 }
1533 BUFADD(p, rem, "Transfer-Encoding: chunked\r\n");
1534 BUFADD(p, rem, "\r\n");
1535 } else {
1536 /* NTRIP v1: SOURCE command */
1537 BUFADD(p, rem, "SOURCE %s %s\r\n", ntrip->passwd, ntrip->mntpnt);
1538 BUFADD(p, rem, "Source-Agent: NTRIP %s\r\n", NTRIP_AGENT);
1539 BUFADD(p, rem, "STR: %s\r\n", ntrip->str);
1540 BUFADD(p, rem, "\r\n");
1541 }
1542
1543 if (rem <= 0) {
1544 sprintf(msg, "request buffer overflow");
1545 return 0;
1546 }
1547 if (writetcpcli(ntrip->tcp, (uint8_t*)buff, (int)(p - buff), msg) != (int)(p - buff)) {
1548 return 0;
1549 }
1550
1551 tracet(NULL, 3, "reqntrip_s: send request state=%d ns=%d v2=%d\n", ntrip->state, (int)(p - buff), use_v2);
1552 tracet(NULL, 5, "reqntrip_s: n=%d buff=\n%s\n", (int)(p - buff), buff);
1553 ntrip->state = 1;
1554 return 1;
1555}
1556/* send ntrip client request -------------------------------------------------*/
1557static int reqntrip_c(ntrip_t* ntrip, char* msg) {
1558 char buff[2048], user[514], *p = buff;
1559 int rem = (int)sizeof(buff);
1560 int use_v2 = (ntrip->ver == NTRIP_VER_2 ||
1561 (ntrip->ver == NTRIP_VER_AUTO && (!ntrip->v2_tried || ntrip->ver_neg == NTRIP_VER_2)));
1562
1563 tracet(NULL, 3, "reqntrip_c: state=%d ver=%d\n", ntrip->state, ntrip->ver);
1564
1565 if (use_v2) {
1566 /* NTRIP v2: HTTP/1.1 with Host and Ntrip-Version headers */
1567 BUFADD(p, rem, "GET %s/%s HTTP/1.1\r\n", ntrip->url, ntrip->mntpnt);
1568 BUFADD(p, rem, "Host: %s\r\n", ntrip->host);
1569 BUFADD(p, rem, "Ntrip-Version: Ntrip/2.0\r\n");
1570 BUFADD(p, rem, "User-Agent: NTRIP %s\r\n", NTRIP_AGENT);
1571
1572 if (*ntrip->user) {
1573 snprintf(user, sizeof(user), "%s:%s", ntrip->user, ntrip->passwd);
1574 BUFADD(p, rem, "Authorization: Basic ");
1575 if (rem > 0) {
1576 int b64_need = (int)(((strlen(user) + 2) / 3) * 4 + 1);
1577 if (b64_need > rem) {
1578 sprintf(msg, "request buffer overflow");
1579 return 0;
1580 }
1581 int n = encbase64(p, (uint8_t*)user, strlen(user));
1582 p += n;
1583 rem -= n;
1584 }
1585 BUFADD(p, rem, "\r\n");
1586 }
1587 BUFADD(p, rem, "Connection: close\r\n");
1588 BUFADD(p, rem, "\r\n");
1589 } else {
1590 /* NTRIP v1: HTTP/1.0 (original) */
1591 BUFADD(p, rem, "GET %s/%s HTTP/1.0\r\n", ntrip->url, ntrip->mntpnt);
1592 BUFADD(p, rem, "User-Agent: NTRIP %s\r\n", NTRIP_AGENT);
1593
1594 if (!*ntrip->user) {
1595 BUFADD(p, rem, "Accept: */*\r\n");
1596 BUFADD(p, rem, "Connection: close\r\n");
1597 } else {
1598 snprintf(user, sizeof(user), "%s:%s", ntrip->user, ntrip->passwd);
1599 BUFADD(p, rem, "Authorization: Basic ");
1600 if (rem > 0) {
1601 int b64_need = (int)(((strlen(user) + 2) / 3) * 4 + 1);
1602 if (b64_need > rem) {
1603 sprintf(msg, "request buffer overflow");
1604 return 0;
1605 }
1606 int n = encbase64(p, (uint8_t*)user, strlen(user));
1607 p += n;
1608 rem -= n;
1609 }
1610 BUFADD(p, rem, "\r\n");
1611 }
1612 BUFADD(p, rem, "\r\n");
1613 }
1614
1615 if (rem <= 0) {
1616 sprintf(msg, "request buffer overflow");
1617 return 0;
1618 }
1619 if (writetcpcli(ntrip->tcp, (uint8_t*)buff, (int)(p - buff), msg) != (int)(p - buff)) {
1620 return 0;
1621 }
1622
1623 tracet(NULL, 3, "reqntrip_c: send request state=%d ns=%d v2=%d\n", ntrip->state, (int)(p - buff), use_v2);
1624 tracet(NULL, 5, "reqntrip_c: n=%d buff=\n%s\n", (int)(p - buff), buff);
1625 ntrip->state = 1;
1626 return 1;
1627}
1628/* test ntrip server response ------------------------------------------------*/
1629static int rspntrip_s(ntrip_t* ntrip, char* msg) {
1630 int i, nb, body_off, status;
1631 char *p, *q;
1632
1633 tracet(NULL, 3, "rspntrip_s: state=%d nb=%d\n", ntrip->state, ntrip->nb);
1634 ntrip->buff[ntrip->nb] = '\0';
1635 tracet(NULL, 5, "rspntrip_s: n=%d buff=\n%s\n", ntrip->nb, ntrip->buff);
1636
1637 if ((p = strstr((char*)ntrip->buff, NTRIP_RSP_OK_SVR))) { /* v1 ok */
1638 q = (char*)ntrip->buff;
1639 p += strlen(NTRIP_RSP_OK_SVR);
1640 ntrip->nb -= (int)(p - q);
1641 for (i = 0; i < ntrip->nb; i++) {
1642 *q++ = *p++;
1643 }
1644 ntrip->state = 2;
1645 ntrip->ver_neg = NTRIP_VER_1;
1646 sprintf(msg, "%s/%s", ntrip->tcp->svr.saddr, ntrip->mntpnt);
1647 tracet(NULL, 3, "rspntrip_s: v1 response ok nb=%d\n", ntrip->nb);
1648 return 1;
1649 }
1650 /* check for HTTP/1.1 response (NTRIP v2 caster) */
1651 if ((body_off = http_header_end(ntrip->buff, ntrip->nb)) > 0) {
1652 status = http_status_code(ntrip->buff, ntrip->nb);
1653
1654 if (status == 200) {
1655 ntrip->state = 2;
1656 ntrip->ver_neg = NTRIP_VER_2;
1657 /* shift remaining data to start of buffer */
1658 ntrip->nb -= body_off;
1659 if (ntrip->nb > 0) {
1660 memmove(ntrip->buff, ntrip->buff + body_off, ntrip->nb);
1661 }
1662 sprintf(msg, "%s/%s", ntrip->tcp->svr.saddr, ntrip->mntpnt);
1663 tracet(NULL, 3, "rspntrip_s: v2 response ok nb=%d\n", ntrip->nb);
1664 return 1;
1665 }
1666 /* HTTP error response */
1667 snprintf(msg, MAXSTATMSG, "%s", (char*)ntrip->buff);
1668 tracet(NULL, 3, "rspntrip_s: http error %s nb=%d\n", msg, ntrip->nb);
1669 ntrip->nb = 0;
1670 ntrip->buff[0] = '\0';
1671 ntrip->state = 0;
1672 discontcp(&ntrip->tcp->svr, ntrip->tcp->tirecon);
1673 return 0;
1674 }
1675 if ((p = strstr((char*)ntrip->buff, NTRIP_RSP_ERROR))) { /* v1 error */
1676 nb = ntrip->nb < MAXSTATMSG ? ntrip->nb : MAXSTATMSG;
1677 sprintf(msg, "%.*s", nb, (char*)ntrip->buff);
1678 if ((p = strchr(msg, '\r'))) {
1679 *p = '\0';
1680 }
1681 tracet(NULL, 3, "rspntrip_s: %s nb=%d\n", msg, ntrip->nb);
1682 ntrip->nb = 0;
1683 ntrip->buff[0] = '\0';
1684 ntrip->state = 0;
1685 discontcp(&ntrip->tcp->svr, ntrip->tcp->tirecon);
1686 } else if (ntrip->nb >= NTRIP_MAXRSP) { /* buffer overflow */
1687 sprintf(msg, "response overflow");
1688 tracet(NULL, 3, "rspntrip_s: response overflow nb=%d\n", ntrip->nb);
1689 ntrip->nb = 0;
1690 ntrip->buff[0] = '\0';
1691 ntrip->state = 0;
1692 discontcp(&ntrip->tcp->svr, ntrip->tcp->tirecon);
1693 }
1694 tracet(NULL, 5, "rspntrip_s: exit state=%d nb=%d\n", ntrip->state, ntrip->nb);
1695 return 0;
1696}
1697/* test ntrip client response ------------------------------------------------*/
1698static int rspntrip_c(ntrip_t* ntrip, char* msg) {
1699 int i, body_off, status;
1700 char *p, *q, val[256];
1701
1702 tracet(NULL, 3, "rspntrip_c: state=%d nb=%d\n", ntrip->state, ntrip->nb);
1703 ntrip->buff[ntrip->nb] = '\0';
1704 tracet(NULL, 5, "rspntrip_c: n=%d buff=\n%s\n", ntrip->nb, ntrip->buff);
1705
1706 if ((p = strstr((char*)ntrip->buff, NTRIP_RSP_OK_CLI))) { /* v1 ok: ICY 200 OK */
1707 q = (char*)ntrip->buff;
1708 p += strlen(NTRIP_RSP_OK_CLI);
1709 ntrip->nb -= (int)(p - q);
1710 for (i = 0; i < ntrip->nb; i++) {
1711 *q++ = *p++;
1712 }
1713 ntrip->state = 2;
1714 ntrip->ver_neg = NTRIP_VER_1;
1715 ntrip->chunked = 0;
1716 if (ntrip->ver == NTRIP_VER_AUTO) {
1717 ntrip->v2_tried = 1; /* accepted v2 request with v1 response */
1718 }
1719 sprintf(msg, "%s/%s", ntrip->tcp->svr.saddr, ntrip->mntpnt);
1720 tracet(NULL, 3, "rspntrip_c: v1 response ok nb=%d\n", ntrip->nb);
1721 return 1;
1722 }
1723 /* check for HTTP/1.1 response (NTRIP v2) */
1724 if ((body_off = http_header_end(ntrip->buff, ntrip->nb)) > 0) {
1725 status = http_status_code(ntrip->buff, ntrip->nb);
1726
1727 if (status == 200) {
1728 /* check for NTRIP v2 source table */
1729 if (http_find_header(ntrip->buff, body_off, "Content-Type", val, sizeof(val)) &&
1730 strstr(val, "sourcetable")) {
1731 if (!*ntrip->mntpnt) { /* source table request */
1732 int is_chunked = 0;
1733 ntrip->state = 2;
1734 ntrip->ver_neg = NTRIP_VER_2;
1735 /* check for chunked encoding before shifting headers away */
1736 if (http_find_header(ntrip->buff, body_off, "Transfer-Encoding", val, sizeof(val)) &&
1737 stristr(val, "chunked")) {
1738 is_chunked = 1;
1739 }
1740 /* shift body to start of buffer */
1741 ntrip->nb -= body_off;
1742 memmove(ntrip->buff, ntrip->buff + body_off, ntrip->nb);
1743 /* enable persistent chunked decoding for incremental reads.
1744 * ntrip->buff remains encoded; readntrip() decodes on demand */
1745 if (is_chunked) {
1746 ntrip->chunked = 1;
1747 chunk_dec_init(&ntrip->cdec);
1748 }
1749 sprintf(msg, "source table received");
1750 tracet(NULL, 3, "rspntrip_c: v2 source table nb=%d\n", ntrip->nb);
1751 return 1;
1752 }
1753 sprintf(msg, "no mountp. reconnect...");
1754 tracet(NULL, 2, "rspntrip_c: v2 no mount point nb=%d\n", ntrip->nb);
1755 ntrip->nb = 0;
1756 ntrip->buff[0] = '\0';
1757 ntrip->state = 0;
1758 discontcp(&ntrip->tcp->svr, ntrip->tcp->tirecon);
1759 return 0;
1760 }
1761 /* NTRIP v2 data stream OK */
1762 ntrip->state = 2;
1763 ntrip->ver_neg = NTRIP_VER_2;
1764
1765 /* detect chunked transfer encoding */
1766 if (http_find_header(ntrip->buff, body_off, "Transfer-Encoding", val, sizeof(val)) &&
1767 stristr(val, "chunked")) {
1768 ntrip->chunked = 1;
1769 chunk_dec_init(&ntrip->cdec);
1770 }
1771 /* shift remaining body data to start of buffer */
1772 ntrip->nb -= body_off;
1773 if (ntrip->nb > 0) {
1774 memmove(ntrip->buff, ntrip->buff + body_off, ntrip->nb);
1775 }
1776 sprintf(msg, "%s/%s", ntrip->tcp->svr.saddr, ntrip->mntpnt);
1777 tracet(NULL, 3, "rspntrip_c: v2 response ok chunked=%d nb=%d\n", ntrip->chunked, ntrip->nb);
1778 return 1;
1779 }
1780 /* HTTP error response */
1781 snprintf(msg, MAXSTATMSG, "%s", (char*)ntrip->buff);
1782 tracet(NULL, 3, "rspntrip_c: http error %s nb=%d\n", msg, ntrip->nb);
1783 ntrip->nb = 0;
1784 ntrip->buff[0] = '\0';
1785 ntrip->state = 0;
1786 discontcp(&ntrip->tcp->svr, ntrip->tcp->tirecon);
1787 return 0;
1788 }
1789 if ((p = strstr((char*)ntrip->buff, NTRIP_RSP_SRCTBL))) { /* v1 source table */
1790 if (!*ntrip->mntpnt) { /* source table request */
1791 ntrip->state = 2;
1792 ntrip->ver_neg = NTRIP_VER_1;
1793 sprintf(msg, "source table received");
1794 tracet(NULL, 3, "rspntrip_c: receive source table nb=%d\n", ntrip->nb);
1795 return 1;
1796 }
1797 sprintf(msg, "no mountp. reconnect...");
1798 tracet(NULL, 2, "rspntrip_c: no mount point nb=%d\n", ntrip->nb);
1799 ntrip->nb = 0;
1800 ntrip->buff[0] = '\0';
1801 ntrip->state = 0;
1802 discontcp(&ntrip->tcp->svr, ntrip->tcp->tirecon);
1803 } else if (ntrip->nb >= NTRIP_MAXRSP) { /* buffer overflow */
1804 sprintf(msg, "response overflow");
1805 tracet(NULL, 2, "rspntrip_c: response overflow nb=%d\n", ntrip->nb);
1806 ntrip->nb = 0;
1807 ntrip->buff[0] = '\0';
1808 ntrip->state = 0;
1809 discontcp(&ntrip->tcp->svr, ntrip->tcp->tirecon);
1810 }
1811 tracet(NULL, 5, "rspntrip_c: exit state=%d nb=%d\n", ntrip->state, ntrip->nb);
1812 return 0;
1813}
1814/* wait ntrip request/response -----------------------------------------------*/
1815static int waitntrip(ntrip_t* ntrip, char* msg) {
1816 int n;
1817 char* p;
1818
1819 tracet(NULL, 4, "waitntrip: state=%d nb=%d\n", ntrip->state, ntrip->nb);
1820
1821 if (ntrip->state < 0) {
1822 return 0; /* error */
1823 }
1824
1825 if (ntrip->tcp->svr.state < 2) {
1826 ntrip->state = 0; /* tcp disconnected */
1827
1828 /* AUTO fallback: only switch to v1 if connection dropped before any
1829 * response was received. If an HTTP response was seen (nb>0), the
1830 * caster supports v2 and the error is auth/mountpoint, not protocol */
1831 if (ntrip->ver == NTRIP_VER_AUTO && ntrip->v2_tried && ntrip->ver_neg == 0 && ntrip->nb == 0) {
1832 tracet(NULL, 3, "waitntrip: v2 failed before response, falling back to v1\n");
1833 ntrip->v2_tried = 2; /* mark as fallen back */
1834 }
1835 }
1836
1837 if (ntrip->state == 0) { /* send request */
1838 int req_ok;
1839 if (ntrip->type == 0) {
1840 req_ok = reqntrip_s(ntrip, msg);
1841 } else {
1842 req_ok = reqntrip_c(ntrip, msg);
1843 }
1844 if (!req_ok) {
1845 return 0;
1846 }
1847 /* mark v2 as tried for AUTO mode */
1848 if (ntrip->ver == NTRIP_VER_AUTO && !ntrip->v2_tried) {
1849 ntrip->v2_tried = 1;
1850 }
1851 tracet(NULL, 3, "waitntrip: state=%d nb=%d\n", ntrip->state, ntrip->nb);
1852 }
1853 if (ntrip->state == 1) { /* read response */
1854 p = (char*)ntrip->buff + ntrip->nb;
1855 if ((n = readtcpcli(ntrip->tcp, (uint8_t*)p, NTRIP_MAXRSP - ntrip->nb - 1, msg)) == 0) {
1856 tracet(NULL, 5, "waitntrip: readtcp n=%d\n", n);
1857 return 0;
1858 }
1859 ntrip->nb += n;
1860 ntrip->buff[ntrip->nb] = '\0';
1861
1862 /* wait response */
1863 if (ntrip->type == 0) {
1864 return rspntrip_s(ntrip, msg);
1865 } else {
1866 return rspntrip_c(ntrip, msg);
1867 }
1868 }
1869 return 1;
1870}
1871/* parse query parameters from mountpoint string
1872 * supported: ?ver=N (NTRIP version), &host=HOSTNAME (Host header override)
1873 * strips the query string from mntpnt
1874 * query string is copied to a local buffer to avoid modifying mntpnt beyond '?' */
1875static int parse_ntrip_query(char* mntpnt, char* host_override, int host_size) {
1876 char params[256], *p, *next;
1877 char* q;
1878 int ver = ntrip_ver_default;
1879
1880 host_override[0] = '\0';
1881
1882 q = strchr(mntpnt, '?');
1883 if (!q) {
1884 return ver;
1885 }
1886
1887 /* copy query string to local buffer before truncating mntpnt */
1888 snprintf(params, sizeof(params), "%s", q + 1);
1889 *q = '\0'; /* strip query string from mountpoint */
1890
1891 tracet(NULL, 3, "parse_ntrip_query: mntpnt=%s params=%s\n", mntpnt, params);
1892
1893 /* parse &-separated parameters manually (no strtok) */
1894 p = params;
1895 while (*p) {
1896 /* find end of current parameter */
1897 next = strchr(p, '&');
1898 if (next) {
1899 *next = '\0';
1900 }
1901
1902 if (strncmp(p, "ver=", 4) == 0) {
1903 int v = atoi(p + 4);
1904 if (v == 1) {
1905 ver = NTRIP_VER_1;
1906 } else if (v == 2) {
1907 ver = NTRIP_VER_2;
1908 }
1909 } else if (strncmp(p, "host=", 5) == 0) {
1910 const char* h = p + 5;
1911 int valid = 1, hlen = 0;
1912 /* reject control characters and whitespace to prevent header injection */
1913 while (h[hlen]) {
1914 if ((unsigned char)h[hlen] <= 0x20 || h[hlen] == 0x7F) {
1915 valid = 0;
1916 break;
1917 }
1918 hlen++;
1919 }
1920 if (valid && hlen > 0) {
1921 snprintf(host_override, host_size, "%s", h);
1922 }
1923 }
1924
1925 if (next) {
1926 p = next + 1;
1927 } else {
1928 break;
1929 }
1930 }
1931 tracet(NULL, 3, "parse_ntrip_query: ver=%d host_override=%s\n", ver, host_override);
1932 return ver;
1933}
1934/* open ntrip ----------------------------------------------------------------*/
1935static ntrip_t* openntrip(const char* path, int type, char* msg) {
1936 ntrip_t* ntrip;
1937 int i;
1938 char addr[256] = "", port[256] = "", tpath[MAXSTRPATH];
1939
1940 tracet(NULL, 3, "openntrip: path=%s type=%d\n", path, type);
1941
1942 if (!(ntrip = (ntrip_t*)malloc(sizeof(ntrip_t)))) {
1943 return NULL;
1944 }
1945
1946 ntrip->state = 0;
1947 ntrip->type = type; /* 0:server,1:client */
1948 ntrip->nb = 0;
1949 ntrip->ver = NTRIP_VER_AUTO;
1950 ntrip->ver_neg = 0;
1951 ntrip->chunked = 0;
1952 ntrip->v2_tried = 0;
1953 ntrip->url[0] = '\0';
1954 ntrip->host[0] = '\0';
1955 ntrip->mntpnt[0] = ntrip->user[0] = ntrip->passwd[0] = ntrip->str[0] = '\0';
1956 for (i = 0; i < NTRIP_MAXRSP; i++) {
1957 ntrip->buff[i] = 0;
1958 }
1959 chunk_dec_init(&ntrip->cdec);
1960
1961 /* decode tcp/ntrip path */
1962 decodetcppath(path, addr, port, ntrip->user, ntrip->passwd, ntrip->mntpnt, ntrip->str);
1963
1964 /* parse query parameters (?ver=N&host=HOSTNAME) from mountpoint */
1965 {
1966 char host_override[256] = "";
1967 ntrip->ver = parse_ntrip_query(ntrip->mntpnt, host_override, sizeof(host_override));
1968
1969 /* use default port if no port specified */
1970 if (!*port) {
1971 sprintf(port, "%d", type ? NTRIP_CLI_PORT : NTRIP_SVR_PORT);
1972 }
1973 sprintf(tpath, "%s:%s", addr, port);
1974
1975 /* save host header value (host= override takes priority) */
1976 if (*host_override) {
1977 snprintf(ntrip->host, sizeof(ntrip->host), "%s", host_override);
1978 } else {
1979 snprintf(ntrip->host, sizeof(ntrip->host), "%s:%s", addr, port);
1980 }
1981 }
1982
1983 /* ntrip access via proxy server */
1984 if (*proxyaddr) {
1985 sprintf(ntrip->url, "http://%.*s", MAXSTRPATH - 8, tpath);
1986 sprintf(tpath, "%.*s", MAXSTRPATH - 1, proxyaddr);
1987 }
1988 /* open tcp client stream */
1989 if (!(ntrip->tcp = opentcpcli(tpath, msg))) {
1990 tracet(NULL, 2, "openntrip: opentcp error\n");
1991 free(ntrip);
1992 return NULL;
1993 }
1994 tracet(NULL, 3, "openntrip: ver=%d host=%s mntpnt=%s\n", ntrip->ver, ntrip->host, ntrip->mntpnt);
1995 return ntrip;
1996}
1997/* close ntrip ---------------------------------------------------------------*/
1998static void closentrip(ntrip_t* ntrip) {
1999 tracet(NULL, 3, "closentrip: state=%d\n", ntrip->state);
2000
2001 /* send chunked terminator for v2 server before closing */
2002 if (ntrip->ver_neg == NTRIP_VER_2 && ntrip->type == 0 && ntrip->state == 2) {
2003 char msg_tmp[MAXSTRMSG] = "";
2004 uint8_t term[] = "0\r\n\r\n";
2005 writetcpcli(ntrip->tcp, term, 5, msg_tmp);
2006 }
2007 closetcpcli(ntrip->tcp);
2008 free(ntrip);
2009}
2010/* read ntrip ----------------------------------------------------------------*/
2011static int readntrip(ntrip_t* ntrip, uint8_t* buff, int n, char* msg) {
2012 int nb;
2013
2014 tracet(NULL, 4, "readntrip:\n");
2015
2016 if (!waitntrip(ntrip, msg)) {
2017 return 0;
2018 }
2019
2020 if (ntrip->nb > 0) { /* read response buffer first */
2021 if (ntrip->chunked) {
2022 /* decode directly from ntrip->buff into caller's buff */
2023 const uint8_t* in = ntrip->buff;
2024 int nin = ntrip->nb;
2025 int nd = chunk_decode(&ntrip->cdec, &in, &nin, buff, n);
2026 if (nd < 0) {
2027 tracet(NULL, 2, "readntrip: chunk decode error, resetting\n");
2028 ntrip->nb = 0;
2029 chunk_dec_init(&ntrip->cdec);
2030 return 0;
2031 }
2032 /* compact: remove consumed bytes from ntrip->buff */
2033 if (nin > 0) {
2034 memmove(ntrip->buff, in, nin);
2035 }
2036 ntrip->nb = nin;
2037 return nd;
2038 }
2039 nb = ntrip->nb <= n ? ntrip->nb : n;
2040 memcpy(buff, ntrip->buff, nb);
2041 if (nb < ntrip->nb) {
2042 memmove(ntrip->buff, ntrip->buff + nb, ntrip->nb - nb);
2043 }
2044 ntrip->nb -= nb;
2045 return nb;
2046 }
2047 if (ntrip->chunked) {
2048 /* read raw TCP data and decode chunks */
2049 uint8_t raw[4096];
2050 int nr = readtcpcli(ntrip->tcp, raw, sizeof(raw), msg);
2051 if (nr <= 0) {
2052 return 0;
2053 }
2054 const uint8_t* in = raw;
2055 int nin = nr;
2056 int nd = chunk_decode(&ntrip->cdec, &in, &nin, buff, n);
2057 if (nd < 0) {
2058 tracet(NULL, 2, "readntrip: chunk decode error, resetting\n");
2059 ntrip->nb = 0;
2060 chunk_dec_init(&ntrip->cdec);
2061 return 0;
2062 }
2063 /* preserve unconsumed encoded bytes to response buffer */
2064 if (nin > 0) {
2065 if (ntrip->nb + nin <= NTRIP_MAXRSP) {
2066 memcpy(ntrip->buff + ntrip->nb, in, nin);
2067 ntrip->nb += nin;
2068 } else {
2069 tracet(NULL, 2, "readntrip: chunk buffer overflow, resetting\n");
2070 ntrip->nb = 0;
2071 chunk_dec_init(&ntrip->cdec);
2072 }
2073 }
2074 return nd;
2075 }
2076 return readtcpcli(ntrip->tcp, buff, n, msg);
2077}
2078/* write ntrip ---------------------------------------------------------------*/
2079static int writentrip(ntrip_t* ntrip, uint8_t* buff, int n, char* msg) {
2080 tracet(NULL, 3, "writentrip: n=%d\n", n);
2081
2082 if (!waitntrip(ntrip, msg)) {
2083 return 0;
2084 }
2085
2086 /* v2 server: use chunked transfer encoding */
2087 if (ntrip->ver_neg == NTRIP_VER_2 && ntrip->type == 0) {
2088 uint8_t cbuf[4096 + 20];
2089 int remaining = n, sent = 0;
2090
2091 while (remaining > 0) {
2092 int chunk = remaining < 4096 ? remaining : 4096;
2093 int cn = chunk_encode(cbuf, sizeof(cbuf), buff + sent, chunk);
2094 if (cn < 0 || writetcpcli(ntrip->tcp, cbuf, cn, msg) != cn) {
2095 return sent;
2096 }
2097 sent += chunk;
2098 remaining -= chunk;
2099 }
2100 return sent;
2101 }
2102 return writetcpcli(ntrip->tcp, buff, n, msg);
2103}
2104/* get state ntrip -----------------------------------------------------------*/
2105static int statentrip(ntrip_t* ntrip) {
2106 return !ntrip ? 0 : (ntrip->state == 0 ? ntrip->tcp->svr.state : ntrip->state);
2107}
2108/* get extended state ntrip --------------------------------------------------*/
2109static int statexntrip(ntrip_t* ntrip, char* msg) {
2110 char* p = msg;
2111 int state = !ntrip ? 0 : (ntrip->state == 0 ? ntrip->tcp->svr.state : ntrip->state);
2112
2113 p += sprintf(p, "ntrip:\n");
2114 p += sprintf(p, " state = %d\n", state);
2115 if (!state) {
2116 return 0;
2117 }
2118 p += sprintf(p, " state = %d\n", state);
2119 p += sprintf(p, " type = %d\n", ntrip->type);
2120 p += sprintf(p, " ver = %d\n", ntrip->ver);
2121 p += sprintf(p, " ver_neg = %d\n", ntrip->ver_neg);
2122 p += sprintf(p, " chunked = %d\n", ntrip->chunked);
2123 p += sprintf(p, " nb = %d\n", ntrip->nb);
2124 p += sprintf(p, " url = %s\n", ntrip->url);
2125 p += sprintf(p, " host = %s\n", ntrip->host);
2126 p += sprintf(p, " mntpnt = %s\n", ntrip->mntpnt);
2127 p += sprintf(p, " user = %s\n", ntrip->user);
2128 p += sprintf(p, " passwd = %s\n", ntrip->passwd);
2129 p += sprintf(p, " str = %s\n", ntrip->str);
2130 p += sprintf(p, " svr:\n");
2131 p += statextcp(&ntrip->tcp->svr, p);
2132 return state;
2133}
2134/* open ntrip-caster ---------------------------------------------------------*/
2135static ntripc_t* openntripc(const char* path, char* msg) {
2136 ntripc_t* ntripc;
2137 int i;
2138 char port[256] = "", tpath[MAXSTRPATH];
2139
2140 tracet(NULL, 3, "openntripc: path=%s\n", path);
2141
2142 if (!(ntripc = (ntripc_t*)malloc(sizeof(ntripc_t)))) {
2143 return NULL;
2144 }
2145
2146 ntripc->state = 0;
2147 ntripc->mntpnt[0] = ntripc->user[0] = ntripc->passwd[0] = ntripc->srctbl[0] = '\0';
2148 for (i = 0; i < MAXCLI; i++) {
2149 ntripc->con[i].state = 0;
2150 ntripc->con[i].ver = 1;
2151 ntripc->con[i].nb = 0;
2152 memset(ntripc->con[i].buff, 0, NTRIP_MAXRSP);
2153 }
2154 /* decode tcp/ntrip path */
2155 decodetcppath(path, NULL, port, ntripc->user, ntripc->passwd, ntripc->mntpnt, ntripc->srctbl);
2156
2157 if (!*ntripc->mntpnt) {
2158 tracet(NULL, 2, "openntripc: no mountpoint path=%s\n", path);
2159 free(ntripc);
2160 return NULL;
2161 }
2162 /* use default port if no port specified */
2163 if (!*port) {
2164 sprintf(port, "%d", NTRIP_CLI_PORT);
2165 }
2166 sprintf(tpath, ":%s", port);
2167
2168 /* open tcp server stream */
2169 if (!(ntripc->tcp = opentcpsvr(tpath, msg))) {
2170 tracet(NULL, 2, "openntripc: opentcpsvr error port=%d\n", port);
2171 free(ntripc);
2172 return NULL;
2173 }
2174 return ntripc;
2175}
2176/* close ntrip-caster --------------------------------------------------------*/
2177static void closentripc(ntripc_t* ntripc) {
2178 int i;
2179 tracet(NULL, 3, "closentripc: state=%d\n", ntripc->state);
2180
2181 /* send chunked terminator for all connected v2 clients */
2182 for (i = 0; i < MAXCLI; i++) {
2183 if (ntripc->con[i].state && ntripc->con[i].ver >= 2) {
2184 uint8_t term[] = "0\r\n\r\n";
2185 send_nb(ntripc->tcp->cli[i].sock, term, 5);
2186 }
2187 }
2188 closetcpsvr(ntripc->tcp);
2189 free(ntripc);
2190}
2191/* disconnect ntrip-caster connection ----------------------------------------*/
2192static void discon_ntripc(ntripc_t* ntripc, int i) {
2193 tracet(NULL, 3, "discon_ntripc: i=%d\n", i);
2194
2195 /* send chunked terminator for v2 clients before disconnecting */
2196 if (ntripc->con[i].ver >= 2 && ntripc->con[i].state) {
2197 uint8_t term[] = "0\r\n\r\n";
2198 send_nb(ntripc->tcp->cli[i].sock, term, 5);
2199 }
2200 discontcp(&ntripc->tcp->cli[i], ticonnect);
2201 ntripc->con[i].nb = 0;
2202 ntripc->con[i].buff[0] = '\0';
2203 ntripc->con[i].ver = 1;
2204 ntripc->con[i].state = 0;
2205}
2206/* http date-time string -----------------------------------------------------*/
2207static void time_str_http(char* buff) {
2208 static const char* month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
2209 static const char* wkday[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
2210 time_t tt = time(NULL);
2211 struct tm* tm = gmtime(&tt);
2212
2213 sprintf(buff, "%s, %02d %s %04d %02d:%02d:%02d GMT", wkday[tm->tm_wday], tm->tm_mday, month[tm->tm_mon],
2214 1900 + tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec);
2215}
2216/* send NTRIP source table ---------------------------------------------------*/
2217static void send_srctbl(ntripc_t* ntripc, int ver, socket_t sock) {
2218 char srctbl[512 + NTRIP_MAXSTR], buff[256], tstr[64], *p = buff;
2219
2220 sprintf(srctbl, "STR;%s;%s\r\n%s\r\n", ntripc->mntpnt, ntripc->srctbl, NTRIP_RSP_TBLEND);
2221
2222 if (ver == 1) { /* NTRIP ver.1 */
2223 p += sprintf(p, "%s", NTRIP_RSP_SRCTBL);
2224 } else { /* NTRIP ver.2 */
2225 p += sprintf(p, "HTTP/1.1 200 OK\r\n");
2226 p += sprintf(p, "Ntrip-Version: Ntrip/2.0\r\n");
2227 p += sprintf(p, "Ntrip-Flags: \r\n");
2228 }
2229 time_str_http(tstr);
2230 p += sprintf(p, "Server: NTRIP %s/%s\r\n", MRTKLIB_SOFTNAME, MRTKLIB_VERSION_STRING);
2231 p += sprintf(p, "Date: %s\r\n", tstr);
2232 p += sprintf(p, "Connection: close\r\n");
2233 p += sprintf(p, "Content-Type: %s\r\n", (ver == 1) ? "text/plain" : "gnss/sourcetable");
2234 p += sprintf(p, "Content-Length: %d\r\n\r\n", (int)strlen(srctbl));
2235 send_nb(sock, (uint8_t*)buff, (int)(p - buff));
2236 send_nb(sock, (uint8_t*)srctbl, (int)strlen(srctbl));
2237}
2238/* send OK response ----------------------------------------------------------*/
2239static void send_rsp_ok(int ver, socket_t sock) {
2240 char buff[512], tstr[64], *p = buff;
2241
2242 if (ver == 1) { /* NTRIP ver.1 */
2243 p += sprintf(p, "%s", NTRIP_RSP_OK_CLI);
2244 } else { /* NTRIP ver.2 */
2245 time_str_http(tstr);
2246 p += sprintf(p, "HTTP/1.1 200 OK\r\n");
2247 p += sprintf(p, "Ntrip-Version: Ntrip/2.0\r\n");
2248 p += sprintf(p, "Server: NTRIP %s/%s\r\n", MRTKLIB_SOFTNAME, MRTKLIB_VERSION_STRING);
2249 p += sprintf(p, "Date: %s\r\n", tstr);
2250 p += sprintf(p, "Cache-Control: no-store, no-cache, max-age=0\r\n");
2251 p += sprintf(p, "Pragma: no-cache\r\n");
2252 p += sprintf(p, "Transfer-Encoding: chunked\r\n");
2253 p += sprintf(p, "Connection: close\r\n");
2254 p += sprintf(p, "Content-Type: gnss/data\r\n\r\n");
2255 }
2256 send_nb(sock, (uint8_t*)buff, (int)(p - buff));
2257}
2258/* send error response -------------------------------------------------------*/
2259static void send_rsp_err(ntripc_t* ntripc, int ver, int err, socket_t sock) {
2260 char buff[512], tstr[64], *p = buff;
2261
2262 if (ver == 1) { /* NTRIP ver.1 */
2263 if (err == 404) {
2264 send_srctbl(ntripc, ver, sock); /* send source table if no mp */
2265 return;
2266 }
2267 p += sprintf(p, "%s", NTRIP_RSP_UNAUTH);
2268 } else { /* NTRIP ver.2 */
2269 time_str_http(tstr);
2270 p += sprintf(p, "HTTP/1.1 %d %s\r\n", err, (err == 404) ? "Not Found" : "Unauthorized");
2271 p += sprintf(p, "Ntrip-Version: Ntrip/2.0\r\n");
2272 p += sprintf(p, "Server: NTRIP %s/%s\r\n", MRTKLIB_SOFTNAME, MRTKLIB_VERSION_STRING);
2273 p += sprintf(p, "Date: %s\r\n", tstr);
2274 p += sprintf(p, "Content-Type: text/html\r\n");
2275 p += sprintf(p, "Connection: close\r\n\r\n");
2276 p += sprintf(p, "%s\r\n", (err == 404) ? "Mountpoint of request not found" : "No or wrong authorization");
2277 }
2278 send_nb(sock, (uint8_t*)buff, (int)(p - buff));
2279}
2280/* test ntrip client request -------------------------------------------------*/
2281static void rsp_ntripc(ntripc_t* ntripc, int i) {
2282 ntripc_con_t* con = ntripc->con + i;
2283 char url[256] = "", mntpnt[256] = "", proto[256] = "", user[513], user_pwd[256], *p, *q;
2284 int ver = 1;
2285
2286 tracet(NULL, 3, "rsp_ntripc=%d\n", i);
2287 con->buff[con->nb] = '\0';
2288 tracet(NULL, 5, "rsp_ntripc: n=%d,buff=\n%s\n", con->nb, con->buff);
2289
2290 if (con->nb >= NTRIP_MAXRSP - 1) { /* buffer overflow */
2291 tracet(NULL, 2, "rsp_ntripc: request buffer overflow\n");
2292 discon_ntripc(ntripc, i);
2293 return;
2294 }
2295 /* test GET and User-Agent */
2296 if (!(p = strstr((char*)con->buff, "GET")) || !(q = strstr(p, "\r\n")) || !(q = strstr(q, "User-Agent:")) ||
2297 !strstr(q, "\r\n")) {
2298 tracet(NULL, 2, "rsp_ntripc: NTRIP request error\n");
2299 discon_ntripc(ntripc, i);
2300 return;
2301 }
2302 /* test protocol */
2303 if (sscanf(p, "GET %255s %255s", url, proto) < 2 || (strcmp(proto, "HTTP/1.0") && strcmp(proto, "HTTP/1.1"))) {
2304 tracet(NULL, 2, "rsp_ntripc: NTRIP request error proto=%s\n", proto);
2305 discon_ntripc(ntripc, i);
2306 return;
2307 }
2308 /* test NTRIP version (clamp to 1 or 2) */
2309 if ((p = strstr((char*)con->buff, "Ntrip-Version:"))) {
2310 sscanf(p + 14, " Ntrip/%d", &ver);
2311 if (ver < 1 || ver > 2) {
2312 ver = 1; /* treat unsupported versions as v1 */
2313 }
2314 }
2315 if ((p = strchr(url, '/'))) {
2316 strcpy(mntpnt, p + 1);
2317 }
2318
2319 /* source table request */
2320 if (!*mntpnt) {
2321 send_srctbl(ntripc, ver, ntripc->tcp->cli[i].sock);
2322 discon_ntripc(ntripc, i);
2323 return;
2324 }
2325 /* test mountpoint */
2326 if (strcmp(mntpnt, ntripc->mntpnt)) {
2327 send_rsp_err(ntripc, ver, 404, ntripc->tcp->cli[i].sock);
2328 discon_ntripc(ntripc, i);
2329 tracet(NULL, 2, "rsp_ntripc: no mountpoint %s\n", mntpnt);
2330 return;
2331 }
2332 /* test authentication */
2333 if (*ntripc->passwd) {
2334 sprintf(user, "%s:%s", ntripc->user, ntripc->passwd);
2335 q = user_pwd;
2336 q += sprintf(q, "Authorization: Basic ");
2337 q += encbase64(q, (uint8_t*)user, strlen(user));
2338 if (!(p = strstr((char*)con->buff, "Authorization:")) || strncmp(p, user_pwd, strlen(user_pwd))) {
2339 send_rsp_err(ntripc, ver, 401, ntripc->tcp->cli[i].sock);
2340 discon_ntripc(ntripc, i);
2341 tracet(NULL, 2, "rsp_ntripc: authorization error\n");
2342 return;
2343 }
2344 }
2345 /* send OK response */
2346 send_rsp_ok(ver, ntripc->tcp->cli[i].sock);
2347
2348 con->state = 1;
2349 con->ver = ver;
2350 strcpy(con->mntpnt, mntpnt);
2351}
2352/* handle ntrip client connect request ---------------------------------------*/
2353static void wait_ntripc(ntripc_t* ntripc, char* msg) {
2354 uint8_t* buff;
2355 int i, n, nmax, err;
2356
2357 tracet(NULL, 4, "wait_ntripc\n");
2358
2359 ntripc->state = ntripc->tcp->svr.state;
2360
2361 if (!waittcpsvr(ntripc->tcp, msg)) {
2362 return;
2363 }
2364
2365 for (i = 0; i < MAXCLI; i++) {
2366 if (ntripc->tcp->cli[i].state != 2 || ntripc->con[i].state) {
2367 continue;
2368 }
2369
2370 /* receive ntrip client request */
2371 buff = ntripc->con[i].buff + ntripc->con[i].nb;
2372 nmax = NTRIP_MAXRSP - ntripc->con[i].nb - 1;
2373
2374 if ((n = recv_nb(ntripc->tcp->cli[i].sock, buff, nmax)) == -1) {
2375 if ((err = errsock())) {
2376 tracet(NULL, 2, "wait_ntripc: recv error sock=%d err=%d\n", ntripc->tcp->cli[i].sock, err);
2377 }
2378 discon_ntripc(ntripc, i);
2379 continue;
2380 }
2381 if (n <= 0) {
2382 continue;
2383 }
2384
2385 /* test ntrip client request */
2386 ntripc->con[i].nb += n;
2387 rsp_ntripc(ntripc, i);
2388 }
2389}
2390/* read ntrip-caster ---------------------------------------------------------*/
2391static int readntripc(ntripc_t* ntripc, uint8_t* buff, int n, char* msg) {
2392 int i, nr, err;
2393
2394 tracet(NULL, 4, "readntripc:\n");
2395
2396 wait_ntripc(ntripc, msg);
2397
2398 for (i = 0; i < MAXCLI; i++) {
2399 if (!ntripc->con[i].state) {
2400 continue;
2401 }
2402
2403 nr = recv_nb(ntripc->tcp->cli[i].sock, buff, n);
2404
2405 if (nr < 0) {
2406 if ((err = errsock())) {
2407 tracet(NULL, 2, "readntripc: recv error i=%d sock=%d err=%d\n", i, ntripc->tcp->cli[i].sock, err);
2408 }
2409 discon_ntripc(ntripc, i);
2410 } else if (nr > 0) {
2411 ntripc->tcp->cli[i].tact = tickget();
2412 return nr;
2413 }
2414 }
2415 return 0;
2416}
2417/* write ntrip-caster --------------------------------------------------------*/
2418static int writentripc(ntripc_t* ntripc, uint8_t* buff, int n, char* msg) {
2419 int i, ns = 0, err;
2420
2421 tracet(NULL, 4, "writentripc: n=%d\n", n);
2422
2423 wait_ntripc(ntripc, msg);
2424
2425 for (i = 0; i < MAXCLI; i++) {
2426 if (!ntripc->con[i].state) {
2427 continue;
2428 }
2429
2430 if (ntripc->con[i].ver >= 2) {
2431 /* v2 client: use chunked transfer encoding */
2432 uint8_t cbuf[4096 + 20];
2433 int remaining = n, sent = 0;
2434
2435 while (remaining > 0) {
2436 int chunk = remaining < 4096 ? remaining : 4096;
2437 int cn = chunk_encode(cbuf, sizeof(cbuf), buff + sent, chunk);
2438 if (cn < 0) {
2439 break;
2440 }
2441 if (send_nb(ntripc->tcp->cli[i].sock, cbuf, cn) < cn) {
2442 if ((err = errsock())) {
2443 tracet(NULL, 2, "writentripc: send error i=%d err=%d\n", i, err);
2444 }
2445 discon_ntripc(ntripc, i);
2446 break;
2447 }
2448 sent += chunk;
2449 remaining -= chunk;
2450 }
2451 if (remaining <= 0) {
2452 ntripc->tcp->cli[i].tact = tickget();
2453 ns = n; /* return payload bytes, not encoded bytes */
2454 }
2455 } else {
2456 /* v1 client: raw data */
2457 ns = send_nb(ntripc->tcp->cli[i].sock, buff, n);
2458
2459 if (ns < n) {
2460 if ((err = errsock())) {
2461 tracet(NULL, 2, "writentripc: send error i=%d sock=%d err=%d\n", i, ntripc->tcp->cli[i].sock, err);
2462 }
2463 discon_ntripc(ntripc, i);
2464 } else {
2465 ntripc->tcp->cli[i].tact = tickget();
2466 }
2467 }
2468 }
2469 return ns;
2470}
2471/* get state ntrip-caster ----------------------------------------------------*/
2472static int statentripc(ntripc_t* ntripc) { return !ntripc ? 0 : ntripc->state; }
2473/* get extended state ntrip-caster -------------------------------------------*/
2474static int statexntripc(ntripc_t* ntripc, char* msg) {
2475 char* p = msg;
2476 int i, state = !ntripc ? 0 : ntripc->state;
2477
2478 p += sprintf(p, "ntripc:\n");
2479 p += sprintf(p, " state = %d\n", ntripc->state);
2480 if (!state) {
2481 return 0;
2482 }
2483 p += sprintf(p, " type = %d\n", ntripc->type);
2484 p += sprintf(p, " mntpnt = %s\n", ntripc->mntpnt);
2485 p += sprintf(p, " user = %s\n", ntripc->user);
2486 p += sprintf(p, " passwd = %s\n", ntripc->passwd);
2487 p += sprintf(p, " srctbl = %s\n", ntripc->srctbl);
2488 p += sprintf(p, " svr:\n");
2489 p += statextcp(&ntripc->tcp->svr, p);
2490 for (i = 0; i < MAXCLI; i++) {
2491 if (!ntripc->tcp->cli[i].state) {
2492 continue;
2493 }
2494 p += sprintf(p, " cli#%d:\n", i);
2495 p += statextcp(ntripc->tcp->cli + i, p);
2496 p += sprintf(p, " mntpnt= %s\n", ntripc->con[i].mntpnt);
2497 p += sprintf(p, " nb = %d\n", ntripc->con[i].nb);
2498 }
2499 return state;
2500}
2501/* generate udp socket -------------------------------------------------------*/
2502static udp_t* genudp(int type, int port, const char* saddr, char* msg) {
2503 udp_t* udp;
2504 struct hostent* hp;
2505 int bs = buffsize, opt = 1;
2506
2507 tracet(NULL, 3, "genudp: type=%d\n", type);
2508
2509 if (!(udp = (udp_t*)malloc(sizeof(udp_t)))) {
2510 return NULL;
2511 }
2512 udp->state = 2;
2513 udp->type = type;
2514 udp->port = port;
2515 strcpy(udp->saddr, saddr);
2516
2517 if ((udp->sock = socket(AF_INET, SOCK_DGRAM, 0)) == (socket_t)-1) {
2518 sprintf(msg, "socket error (%d)", errsock());
2519 return NULL;
2520 }
2521 if (setsockopt(udp->sock, SOL_SOCKET, SO_RCVBUF, (const char*)&bs, sizeof(bs)) == -1 ||
2522 setsockopt(udp->sock, SOL_SOCKET, SO_SNDBUF, (const char*)&bs, sizeof(bs)) == -1) {
2523 tracet(NULL, 2, "genudp: setsockopt error sock=%d err=%d bs=%d\n", udp->sock, errsock(), bs);
2524 sprintf(msg, "sockopt error: bufsiz");
2525 }
2526 memset(&udp->addr, 0, sizeof(udp->addr));
2527 udp->addr.sin_family = AF_INET;
2528 udp->addr.sin_port = htons(port);
2529
2530 if (!udp->type) { /* udp server */
2531 udp->addr.sin_addr.s_addr = htonl(INADDR_ANY);
2532#ifdef SVR_REUSEADDR
2533 setsockopt(udp->sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
2534#endif
2535 if (bind(udp->sock, (struct sockaddr*)&udp->addr, sizeof(udp->addr)) == -1) {
2536 tracet(NULL, 2, "genudp: bind error sock=%d port=%d err=%d\n", udp->sock, port, errsock());
2537 sprintf(msg, "bind error (%d): %d", errsock(), port);
2538 closesocket(udp->sock);
2539 free(udp);
2540 return NULL;
2541 }
2542 } else { /* udp client */
2543 if (!strcmp(saddr, "255.255.255.255") &&
2544 setsockopt(udp->sock, SOL_SOCKET, SO_BROADCAST, (const char*)&opt, sizeof(opt)) == -1) {
2545 tracet(NULL, 2, "genudp: setsockopt error sock=%d err=%d\n", udp->sock, errsock());
2546 sprintf(msg, "sockopt error: broadcast");
2547 }
2548 if (!(hp = gethostbyname(saddr))) {
2549 sprintf(msg, "address error (%s)", saddr);
2550 closesocket(udp->sock);
2551 free(udp);
2552 return NULL;
2553 }
2554 memcpy(&udp->addr.sin_addr, hp->h_addr_list[0], hp->h_length);
2555 }
2556 return udp;
2557}
2558/* open udp server -----------------------------------------------------------*/
2559static udp_t* openudpsvr(const char* path, char* msg) {
2560 char sport[256] = "";
2561 int port;
2562
2563 tracet(NULL, 3, "openudpsvr: path=%s\n", path);
2564
2565 decodetcppath(path, NULL, sport, NULL, NULL, NULL, NULL);
2566
2567 if (sscanf(sport, "%d", &port) < 1) {
2568 sprintf(msg, "port error: %s", sport);
2569 tracet(NULL, 2, "openudpsvr: port error port=%s\n", port);
2570 return NULL;
2571 }
2572 return genudp(0, port, "", msg);
2573}
2574/* close udp server ----------------------------------------------------------*/
2575static void closeudpsvr(udp_t* udpsvr) {
2576 tracet(NULL, 3, "closeudpsvr: sock=%d\n", udpsvr->sock);
2577
2578 closesocket(udpsvr->sock);
2579 free(udpsvr);
2580}
2581/* read udp server -----------------------------------------------------------*/
2582static int readudpsvr(udp_t* udpsvr, uint8_t* buff, int n, char* msg) {
2583 struct timeval tv = {0};
2584 fd_set rs;
2585 int ret, nr;
2586
2587 tracet(NULL, 4, "readudpsvr: sock=%d n=%d\n", udpsvr->sock, n);
2588
2589 FD_ZERO(&rs);
2590 FD_SET(udpsvr->sock, &rs);
2591 ret = select(udpsvr->sock + 1, &rs, NULL, NULL, &tv);
2592 if (ret <= 0) {
2593 return ret;
2594 }
2595 nr = recvfrom(udpsvr->sock, (char*)buff, n, 0, NULL, NULL);
2596 return nr <= 0 ? -1 : nr;
2597}
2598/* get state udp server ------------------------------------------------------*/
2599static int stateudpsvr(udp_t* udpsvr) { return udpsvr ? udpsvr->state : 0; }
2600/* get extended state udp server ---------------------------------------------*/
2601static int statexudpsvr(udp_t* udpsvr, char* msg) {
2602 char* p = msg;
2603 int state = udpsvr ? udpsvr->state : 0;
2604
2605 p += sprintf(p, "udpsvr:\n");
2606 p += sprintf(p, " state = %d\n", state);
2607 if (!state) {
2608 return 0;
2609 }
2610 p += sprintf(p, " type = %d\n", udpsvr->type);
2611 p += sprintf(p, " sock = %d\n", (int)udpsvr->sock);
2612 p += sprintf(p, " port = %d\n", udpsvr->port);
2613 return state;
2614}
2615/* open udp client -----------------------------------------------------------*/
2616static udp_t* openudpcli(const char* path, char* msg) {
2617 char sport[256] = "", saddr[256] = "";
2618 int port;
2619
2620 tracet(NULL, 3, "openudpsvr: path=%s\n", path);
2621
2622 decodetcppath(path, saddr, sport, NULL, NULL, NULL, NULL);
2623
2624 if (sscanf(sport, "%d", &port) < 1) {
2625 sprintf(msg, "port error: %s", sport);
2626 tracet(NULL, 2, "openudpcli: port error port=%s\n", sport);
2627 return NULL;
2628 }
2629 return genudp(1, port, saddr, msg);
2630}
2631/* close udp client ----------------------------------------------------------*/
2632static void closeudpcli(udp_t* udpcli) {
2633 tracet(NULL, 3, "closeudpcli: sock=%d\n", udpcli->sock);
2634
2635 closesocket(udpcli->sock);
2636 free(udpcli);
2637}
2638/* write udp client -----------------------------------------------------------*/
2639static int writeudpcli(udp_t* udpcli, uint8_t* buff, int n, char* msg) {
2640 tracet(NULL, 4, "writeudpcli: sock=%d n=%d\n", udpcli->sock, n);
2641
2642 return (int)sendto(udpcli->sock, (char*)buff, n, 0, (struct sockaddr*)&udpcli->addr, sizeof(udpcli->addr));
2643}
2644/* get state udp client ------------------------------------------------------*/
2645static int stateudpcli(udp_t* udpcli) { return udpcli ? udpcli->state : 0; }
2646/* get extended state udp client ---------------------------------------------*/
2647static int statexudpcli(udp_t* udpcli, char* msg) {
2648 char* p = msg;
2649 int state = udpcli ? udpcli->state : 0;
2650
2651 p += sprintf(p, "udpsvr:\n");
2652 p += sprintf(p, " state = %d\n", state);
2653 if (!state) {
2654 return 0;
2655 }
2656 p += sprintf(p, " type = %d\n", udpcli->type);
2657 p += sprintf(p, " sock = %d\n", (int)udpcli->sock);
2658 p += sprintf(p, " addr = %s\n", udpcli->saddr);
2659 p += sprintf(p, " port = %d\n", udpcli->port);
2660 return state;
2661}
2662/* decode ftp path -----------------------------------------------------------*/
2663static void decodeftppath(const char* path, char* addr, char* file, char* user, char* passwd, int* topts) {
2664 char buff[MAXSTRPATH], *p, *q;
2665
2666 tracet(NULL, 4, "decodeftpath: path=%s\n", path);
2667
2668 if (user) {
2669 *user = '\0';
2670 }
2671 if (passwd) {
2672 *passwd = '\0';
2673 }
2674 if (topts) {
2675 topts[0] = 0; /* time offset in path (s) */
2676 topts[1] = 3600; /* download interval (s) */
2677 topts[2] = 0; /* download time offset (s) */
2678 topts[3] = 0; /* retry interval (s) (0: no retry) */
2679 }
2680 strcpy(buff, path);
2681
2682 if ((p = strchr(buff, '/'))) {
2683 if ((q = strstr(p + 1, "::"))) {
2684 *q = '\0';
2685 if (topts) {
2686 sscanf(q + 2, "T=%d,%d,%d,%d", topts, topts + 1, topts + 2, topts + 3);
2687 }
2688 }
2689 strcpy(file, p + 1);
2690 *p = '\0';
2691 } else {
2692 file[0] = '\0';
2693 }
2694
2695 if ((p = strrchr(buff, '@'))) {
2696 *p++ = '\0';
2697 if ((q = strchr(buff, ':'))) {
2698 *q = '\0';
2699 if (passwd) {
2700 strcpy(passwd, q + 1);
2701 }
2702 }
2703 *q = '\0';
2704 if (user) {
2705 strcpy(user, buff);
2706 }
2707 } else {
2708 p = buff;
2709 }
2710
2711 strcpy(addr, p);
2712}
2713/* next download time --------------------------------------------------------*/
2714static gtime_t nextdltime(const int* topts, int stat) {
2715 gtime_t time;
2716 double tow;
2717 int week, tint;
2718
2719 tracet(NULL, 3, "nextdltime: topts=%d %d %d %d stat=%d\n", topts[0], topts[1], topts[2], topts[3], stat);
2720
2721 /* current time (gpst) */
2722 time = utc2gpst(timeget());
2723 tow = time2gpst(time, &week);
2724
2725 /* next retry time */
2726 if (stat == 0 && topts[3] > 0) {
2727 tow = (floor((tow - topts[2]) / topts[3]) + 1.0) * topts[3] + topts[2];
2728 return gpst2time(week, tow);
2729 }
2730 /* next interval time */
2731 tint = topts[1] <= 0 ? 3600 : topts[1];
2732 tow = (floor((tow - topts[2]) / tint) + 1.0) * tint + topts[2];
2733 time = gpst2time(week, tow);
2734
2735 return time;
2736}
2737/* ftp thread ----------------------------------------------------------------*/
2738static void* ftpthread(void* arg) {
2739 ftp_t* ftp = (ftp_t*)arg;
2740 FILE* fp;
2741 gtime_t time;
2742 char remote[1024], local[1024], tmpfile[1024], errfile[1024], *p;
2743 char cmd[5120], env[1024] = "", opt[1024], *proxyopt = "", *proto;
2744 int ret;
2745
2746 tracet(NULL, 3, "ftpthread:\n");
2747
2748 if (!*localdir) {
2749 tracet(NULL, 2, "no local directory\n");
2750 ftp->error = 11;
2751 ftp->state = 3;
2752 return 0;
2753 }
2754 /* replace keyword in file path and local path */
2755 time = timeadd(utc2gpst(timeget()), ftp->topts[0]);
2756 reppath(ftp->file, remote, time, "", "");
2757
2758 if ((p = strrchr(remote, '/'))) {
2759 p++;
2760 } else {
2761 p = remote;
2762 }
2763 sprintf(local, "%.768s%c%.254s", localdir, FILEPATHSEP, p);
2764 sprintf(errfile, "%.1019s.err", local);
2765
2766 /* if local file exist, skip download */
2767 strcpy(tmpfile, local);
2768 if ((p = strrchr(tmpfile, '.')) && (!strcmp(p, ".z") || !strcmp(p, ".gz") || !strcmp(p, ".zip") ||
2769 !strcmp(p, ".Z") || !strcmp(p, ".GZ") || !strcmp(p, ".ZIP"))) {
2770 *p = '\0';
2771 }
2772 if ((fp = fopen(tmpfile, "rb"))) {
2773 fclose(fp);
2774 sprintf(ftp->local, "%.1023s", tmpfile);
2775 tracet(NULL, 3, "ftpthread: file exists %s\n", ftp->local);
2776 ftp->state = 2;
2777 return 0;
2778 }
2779 /* proxy settings for wget (ref [2]) */
2780 if (*proxyaddr) {
2781 proto = ftp->proto ? "http" : "ftp";
2782 sprintf(env, "set %.4s_proxy=http://%.998s & ", proto, proxyaddr);
2783 proxyopt = "--proxy=on ";
2784 }
2785 /* download command (ref [2]) */
2786 if (ftp->proto == 0) { /* ftp */
2787 sprintf(opt,
2788 "--ftp-user=%.32s --ftp-password=%.32s --glob=off "
2789 "--passive-ftp %.32s -t 1 -T %d -O \"%.768s\"",
2790 ftp->user, ftp->passwd, proxyopt, FTP_TIMEOUT, local);
2791 sprintf(cmd, "%s%s %s \"ftp://%s/%s\" 2> \"%.768s\"\n", env, FTP_CMD, opt, ftp->addr, remote, errfile);
2792 } else { /* http */
2793 sprintf(opt, "%.32s -t 1 -T %d -O \"%.768s\"", proxyopt, FTP_TIMEOUT, local);
2794 sprintf(cmd, "%s%s %s \"http://%s/%s\" 2> \"%.768s\"\n", env, FTP_CMD, opt, ftp->addr, remote, errfile);
2795 }
2796 /* execute download command */
2797 if ((ret = execcmd(cmd))) {
2798 remove(local);
2799 tracet(NULL, 2, "execcmd error: cmd=%s ret=%d\n", cmd, ret);
2800 ftp->error = ret;
2801 ftp->state = 3;
2802 return 0;
2803 }
2804 remove(errfile);
2805
2806 /* uncompress downloaded file */
2807 if ((p = strrchr(local, '.')) && (!strcmp(p, ".z") || !strcmp(p, ".gz") || !strcmp(p, ".zip") || !strcmp(p, ".Z") ||
2808 !strcmp(p, ".GZ") || !strcmp(p, ".ZIP"))) {
2809 if (rtk_uncompress(local, tmpfile)) {
2810 remove(local);
2811 strcpy(local, tmpfile);
2812 } else {
2813 tracet(NULL, 2, "file uncompact error: %s\n", local);
2814 ftp->error = 12;
2815 ftp->state = 3;
2816 return 0;
2817 }
2818 }
2819 strcpy(ftp->local, local);
2820 ftp->state = 2; /* ftp completed */
2821
2822 tracet(NULL, 3, "ftpthread: complete cmd=%s\n", cmd);
2823 return 0;
2824}
2825/* open ftp ------------------------------------------------------------------*/
2826static ftp_t* openftp(const char* path, int type, char* msg) {
2827 ftp_t* ftp;
2828
2829 tracet(NULL, 3, "openftp: path=%s type=%d\n", path, type);
2830
2831 msg[0] = '\0';
2832
2833 if (!(ftp = (ftp_t*)malloc(sizeof(ftp_t)))) {
2834 return NULL;
2835 }
2836
2837 ftp->state = 0;
2838 ftp->proto = type;
2839 ftp->error = 0;
2840 ftp->thread = 0;
2841 ftp->local[0] = '\0';
2842
2843 /* decode ftp path */
2844 decodeftppath(path, ftp->addr, ftp->file, ftp->user, ftp->passwd, ftp->topts);
2845
2846 /* set first download time */
2847 ftp->tnext = timeadd(timeget(), 10.0);
2848
2849 return ftp;
2850}
2851/* close ftp -----------------------------------------------------------------*/
2852static void closeftp(ftp_t* ftp) {
2853 tracet(NULL, 3, "closeftp: state=%d\n", ftp->state);
2854
2855 if (ftp->state != 1) {
2856 free(ftp);
2857 }
2858}
2859/* read ftp ------------------------------------------------------------------*/
2860static int readftp(ftp_t* ftp, uint8_t* buff, int n, char* msg) {
2861 gtime_t time;
2862 uint8_t *p, *q;
2863
2864 tracet(NULL, 4, "readftp: n=%d\n", n);
2865
2866 time = utc2gpst(timeget());
2867
2868 if (timediff(time, ftp->tnext) < 0.0) { /* until download time? */
2869 return 0;
2870 }
2871 if (ftp->state <= 0) { /* ftp/http not executed? */
2872 ftp->state = 1;
2873 sprintf(msg, "%s://%s", ftp->proto ? "http" : "ftp", ftp->addr);
2874
2875 if (pthread_create(&ftp->thread, NULL, ftpthread, ftp)) {
2876 tracet(NULL, 2, "readftp: ftp thread create error\n");
2877 ftp->state = 3;
2878 strcpy(msg, "ftp thread error");
2879 return 0;
2880 }
2881 }
2882 if (ftp->state <= 1) {
2883 return 0; /* ftp/http on going? */
2884 }
2885
2886 if (ftp->state == 3) { /* ftp error */
2887 sprintf(msg, "%s error (%d)", ftp->proto ? "http" : "ftp", ftp->error);
2888
2889 /* set next retry time */
2890 ftp->tnext = nextdltime(ftp->topts, 0);
2891 ftp->state = 0;
2892 return 0;
2893 }
2894 /* return local file path if ftp completed */
2895 p = buff;
2896 q = (uint8_t*)ftp->local;
2897 while (*q && (int)(p - buff) < n) {
2898 *p++ = *q++;
2899 }
2900 p += sprintf((char*)p, "\r\n");
2901
2902 /* set next download time */
2903 ftp->tnext = nextdltime(ftp->topts, 1);
2904 ftp->state = 0;
2905
2906 strcpy(msg, "");
2907
2908 return (int)(p - buff);
2909}
2910/* get state ftp -------------------------------------------------------------*/
2911static int stateftp(ftp_t* ftp) {
2912 if (!ftp) {
2913 return 0;
2914 } else if (ftp->state == 0) {
2915 return 2;
2916 } else if (ftp->state <= 2) {
2917 return 3;
2918 } else {
2919 return -1;
2920 }
2921}
2922/* get extended state ftp ----------------------------------------------------*/
2923static int statexftp(ftp_t* ftp, char* msg) {
2924 if (!ftp) {
2925 return 0;
2926 } else if (ftp->state == 0) {
2927 return 2;
2928 } else if (ftp->state <= 2) {
2929 return 3;
2930 } else {
2931 return -1;
2932 }
2933}
2934/* open memory buffer --------------------------------------------------------*/
2935static membuf_t* openmembuf(const char* path, char* msg) {
2936 membuf_t* membuf;
2937 int bufsize = DEFAULT_MEMBUF_SIZE;
2938
2939 tracet(NULL, 3, "openmembuf: path=%s\n", path);
2940
2941 msg[0] = '\0';
2942
2943 sscanf(path, "%d", &bufsize);
2944
2945 if (!(membuf = (membuf_t*)malloc(sizeof(membuf_t)))) {
2946 return NULL;
2947 }
2948 membuf->state = 1;
2949 membuf->rp = 0;
2950 membuf->wp = 0;
2951 if (!(membuf->buf = (uint8_t*)malloc(bufsize))) {
2952 free(membuf);
2953 return NULL;
2954 }
2955 membuf->bufsize = bufsize;
2956 rtk_initlock(&membuf->lock);
2957
2958 sprintf(msg, "membuf sizebuf=%d", bufsize);
2959
2960 return membuf;
2961}
2962/* close memory buffer -------------------------------------------------------*/
2963static void closemembuf(membuf_t* membuf) {
2964 tracet(NULL, 3, "closemembufp\n");
2965
2966 free(membuf->buf);
2967 free(membuf);
2968}
2969/* read memory buffer --------------------------------------------------------*/
2970static int readmembuf(membuf_t* membuf, uint8_t* buff, int n, char* msg) {
2971 int i, nr = 0;
2972
2973 tracet(NULL, 4, "readmembuf: n=%d\n", n);
2974
2975 if (!membuf) {
2976 return 0;
2977 }
2978
2979 rtk_lock(&membuf->lock);
2980
2981 for (i = membuf->rp; i != membuf->wp && nr < n; i++) {
2982 if (i >= membuf->bufsize) {
2983 i = 0;
2984 if (i == membuf->wp) {
2985 break;
2986 }
2987 }
2988 buff[nr++] = membuf->buf[i];
2989 }
2990 membuf->rp = i;
2991 rtk_unlock(&membuf->lock);
2992 return nr;
2993}
2994/* write memory buffer -------------------------------------------------------*/
2995static int writemembuf(membuf_t* membuf, uint8_t* buff, int n, char* msg) {
2996 int i;
2997
2998 tracet(NULL, 3, "writemembuf: n=%d\n", n);
2999
3000 if (!membuf) {
3001 return 0;
3002 }
3003
3004 rtk_lock(&membuf->lock);
3005
3006 for (i = 0; i < n; i++) {
3007 membuf->buf[membuf->wp++] = buff[i];
3008 if (membuf->wp >= membuf->bufsize) {
3009 membuf->wp = 0;
3010 }
3011 if (membuf->wp == membuf->rp) {
3012 strcpy(msg, "mem-buffer overflow");
3013 membuf->state = -1;
3014 rtk_unlock(&membuf->lock);
3015 return i + 1;
3016 }
3017 }
3018 rtk_unlock(&membuf->lock);
3019 return i;
3020}
3021/* get state memory buffer ---------------------------------------------------*/
3022static int statemembuf(membuf_t* membuf) { return !membuf ? 0 : membuf->state; }
3023/* get extended state memory buffer ------------------------------------------*/
3024static int statexmembuf(membuf_t* membuf, char* msg) {
3025 char* p = msg;
3026 int state = !membuf ? 0 : membuf->state;
3027
3028 p += sprintf(p, "membuf:\n");
3029 p += sprintf(p, " state = %d\n", state);
3030 if (!state) {
3031 return 0;
3032 }
3033 p += sprintf(p, " buffsize= %d\n", membuf->bufsize);
3034 p += sprintf(p, " wp = %d\n", membuf->wp);
3035 p += sprintf(p, " rp = %d\n", membuf->rp);
3036 return state;
3037}
3038/* initialize stream environment -----------------------------------------------
3039 * initialize stream environment
3040 * args : none
3041 * return : none
3042 *-----------------------------------------------------------------------------*/
3043extern void strinitcom(void) { tracet(NULL, 3, "strinitcom:\n"); }
3044/* initialize stream -----------------------------------------------------------
3045 * initialize stream struct
3046 * args : stream_t *stream IO stream
3047 * return : none
3048 *-----------------------------------------------------------------------------*/
3049extern void strinit(stream_t* stream) {
3050 tracet(NULL, 3, "strinit:\n");
3051
3052 stream->type = 0;
3053 stream->mode = 0;
3054 stream->state = 0;
3055 stream->inb = stream->inr = stream->outb = stream->outr = 0;
3056 stream->tick_i = stream->tick_o = stream->tact = stream->inbt = stream->outbt = 0;
3057 rtk_initlock(&stream->lock);
3058 stream->port = NULL;
3059 stream->path[0] = '\0';
3060 stream->msg[0] = '\0';
3061}
3062/* open stream -----------------------------------------------------------------
3063 *
3064 * open stream to read or write data from or to virtual devices.
3065 *
3066 * args : stream_t *stream IO stream
3067 * int type I stream type
3068 * STR_SERIAL = serial device
3069 * STR_FILE = file (record and playback)
3070 * STR_TCPSVR = TCP server
3071 * STR_TCPCLI = TCP client
3072 * STR_NTRIPSVR = NTRIP server
3073 * STR_NTRIPCLI = NTRIP client
3074 * STR_NTRIPC_S = NTRIP caster server
3075 * STR_NTRIPC_C = NTRIP caster client
3076 * STR_UDPSVR = UDP server (read only)
3077 * STR_UDPCLI = UDP client (write only)
3078 * STR_MEMBUF = memory buffer (FIFO)
3079 * STR_FTP = download by FTP (raed only)
3080 * STR_HTTP = download by HTTP (raed only)
3081 * int mode I stream mode (STR_MODE_???)
3082 * STR_MODE_R = read only
3083 * STR_MODE_W = write only
3084 * STR_MODE_RW = read and write
3085 * char *path I stream path (see below)
3086 *
3087 * return : status (0:error,1:ok)
3088 *
3089 * notes : see reference [1] for NTRIP
3090 * STR_FTP/HTTP needs "wget" in command search paths
3091 *
3092 * stream path ([] options):
3093 *
3094 * STR_SERIAL port[:brate[:bsize[:parity[:stopb[:fctr[#port]]]]]]
3095 * port = COM?? (windows)
3096 * tty??? (linuex, omit /dev/)
3097 * brate = bit rate (bps)
3098 * bsize = bit size (7|8)
3099 * parity= parity (n|o|e)
3100 * stopb = stop bits (1|2)
3101 * fctr = flow control (off|rts)
3102 * port = tcp server port to output received stream
3103 *
3104 * STR_FILE path[::T][::+start][::xseppd][::S=swap][::P={4|8}]
3105 * path = file path
3106 * (can include keywords defined by )
3107 * ::T = enable time tag
3108 * start = replay start offset (s)
3109 * speed = replay speed factor
3110 * swap = output swap interval (hr) (0: no swap)
3111 * ::P={4|8} = file pointer size (4:32bit,8:64bit)
3112 *
3113 * STR_TCPSVR :port
3114 * port = TCP server port to accept
3115 *
3116 * STR_TCPCLI addr:port
3117 * addr = TCP server address to connect
3118 * port = TCP server port to connect
3119 *
3120 * STR_NTRIPSVR [:passwd@]addr[:port]/mponit[:string]
3121 * addr = NTRIP caster address to connect
3122 * port = NTRIP caster server port to connect
3123 * passwd= NTRIP caster server password to connect
3124 * mpoint= NTRIP mountpoint
3125 * string= NTRIP server string
3126 *
3127 * STR_NTRIPCLI [user[:passwd]@]addr[:port]/mpoint
3128 * addr = NTRIP caster address to connect
3129 * port = NTRIP caster client port to connect
3130 * user = NTRIP caster client user to connect
3131 * passwd= NTRIP caster client password to connect
3132 * mpoint= NTRIP mountpoint
3133 *
3134 * STR_NTRIPCAS [user[:passwd]@][:port]/mpoint[:srctbl]
3135 * port = NTRIP caster client port to accept connection
3136 * user = NTRIP caster client user to accept connection
3137 * passwd= NTRIP caster client password to accept connection
3138 * mpoint= NTRIP mountpoint
3139 * srctbl= NTRIP source table entry (STR) (ref [3] 6.3)
3140 * (ID;format;format-details;carrier;nav-system;network;
3141 * country;latitude;longitude;nmea;solution;generator;
3142 * compr-encrp;autentication;fee;bitrate;...;misc)
3143 *
3144 * STR_UDPSVR :port
3145 * port = UDP server port to receive
3146 *
3147 * STR_UDPCLI addr:port
3148 * addr = UDP server or broadcast address to send
3149 * port = UDP server or broadcast port to send
3150 *
3151 * STR_MEMBUF [size]
3152 * size = FIFO size (bytes) ("":4096)
3153 *
3154 * STR_FTP [user[:passwd]@]addr/path[::T=poff[,tint[,toff,tret]]]]
3155 * user = FTP server user
3156 * passwd= FTP server password
3157 * addr = FTP server address
3158 * path = FTP server file path
3159 * poff = time offset for path extension (s)
3160 * tint = download interval (s)
3161 * toff = download time offset (s)
3162 * tret = download retry interval (s) (0:no retry)
3163 *
3164 * STR_HTTP addr/path[::T=poff[,tint[,toff,tret]]]]
3165 * addr = HTTP server address
3166 * path = HTTP server file path
3167 * poff = time offset for path extension (s)
3168 * tint = download interval (s)
3169 * toff = download time offset (s)
3170 * tret = download retry interval (s) (0:no retry)
3171 *
3172 *-----------------------------------------------------------------------------*/
3173extern int stropen(stream_t* stream, int type, int mode, const char* path) {
3174 tracet(NULL, 3, "stropen: type=%d mode=%d path=%s\n", type, mode, path);
3175
3176 stream->type = type;
3177 stream->mode = mode;
3178 strcpy(stream->path, path);
3179 stream->inb = stream->inr = stream->outb = stream->outr = 0;
3180 stream->tick_i = stream->tick_o = tickget();
3181 stream->inbt = stream->outbt = 0;
3182 stream->msg[0] = '\0';
3183 stream->port = NULL;
3184 switch (type) {
3185 case STR_SERIAL:
3186 stream->port = openserial(path, mode, stream->msg);
3187 break;
3188 case STR_FILE:
3189 stream->port = openfile(path, mode, stream->msg);
3190 break;
3191 case STR_TCPSVR:
3192 stream->port = opentcpsvr(path, stream->msg);
3193 break;
3194 case STR_TCPCLI:
3195 stream->port = opentcpcli(path, stream->msg);
3196 break;
3197 case STR_NTRIPSVR:
3198 stream->port = openntrip(path, 0, stream->msg);
3199 break;
3200 case STR_NTRIPCLI:
3201 stream->port = openntrip(path, 1, stream->msg);
3202 break;
3203 case STR_NTRIPCAS:
3204 stream->port = openntripc(path, stream->msg);
3205 break;
3206 case STR_UDPSVR:
3207 stream->port = openudpsvr(path, stream->msg);
3208 break;
3209 case STR_UDPCLI:
3210 stream->port = openudpcli(path, stream->msg);
3211 break;
3212 case STR_MEMBUF:
3213 stream->port = openmembuf(path, stream->msg);
3214 break;
3215 case STR_FTP:
3216 stream->port = openftp(path, 0, stream->msg);
3217 break;
3218 case STR_HTTP:
3219 stream->port = openftp(path, 1, stream->msg);
3220 break;
3221 default:
3222 stream->state = 0;
3223 return 1;
3224 }
3225 stream->state = !stream->port ? -1 : 1;
3226 return stream->port != NULL;
3227}
3228/* close stream ----------------------------------------------------------------
3229 * close stream
3230 * args : stream_t *stream IO stream
3231 * return : none
3232 *-----------------------------------------------------------------------------*/
3233extern void strclose(stream_t* stream) {
3234 tracet(NULL, 3, "strclose: type=%d mode=%d\n", stream->type, stream->mode);
3235
3236 strlock(stream);
3237
3238 if (stream->port) {
3239 switch (stream->type) {
3240 case STR_SERIAL:
3241 closeserial((serial_t*)stream->port);
3242 break;
3243 case STR_FILE:
3244 closefile((file_t*)stream->port);
3245 break;
3246 case STR_TCPSVR:
3247 closetcpsvr((tcpsvr_t*)stream->port);
3248 break;
3249 case STR_TCPCLI:
3250 closetcpcli((tcpcli_t*)stream->port);
3251 break;
3252 case STR_NTRIPSVR:
3253 closentrip((ntrip_t*)stream->port);
3254 break;
3255 case STR_NTRIPCLI:
3256 closentrip((ntrip_t*)stream->port);
3257 break;
3258 case STR_NTRIPCAS:
3259 closentripc((ntripc_t*)stream->port);
3260 break;
3261 case STR_UDPSVR:
3262 closeudpsvr((udp_t*)stream->port);
3263 break;
3264 case STR_UDPCLI:
3265 closeudpcli((udp_t*)stream->port);
3266 break;
3267 case STR_MEMBUF:
3268 closemembuf((membuf_t*)stream->port);
3269 break;
3270 case STR_FTP:
3271 closeftp((ftp_t*)stream->port);
3272 break;
3273 case STR_HTTP:
3274 closeftp((ftp_t*)stream->port);
3275 break;
3276 }
3277 } else {
3278 trace(NULL, 3, "no port to close stream: type=%d\n", stream->type);
3279 }
3280 stream->type = 0;
3281 stream->mode = 0;
3282 stream->state = 0;
3283 stream->inr = stream->outr = 0;
3284 stream->path[0] = '\0';
3285 stream->msg[0] = '\0';
3286 stream->port = NULL;
3287
3288 strunlock(stream);
3289}
3290/* sync streams ----------------------------------------------------------------
3291 * sync time for streams
3292 * args : stream_t *stream1 IO stream 1
3293 * stream_t *stream2 IO stream 2
3294 * return : none
3295 * notes : for replay files with time tags
3296 *-----------------------------------------------------------------------------*/
3297extern void strsync(stream_t* stream1, stream_t* stream2) {
3298 file_t *file1, *file2;
3299 if (stream1->type != STR_FILE || stream2->type != STR_FILE) {
3300 return;
3301 }
3302 file1 = (file_t*)stream1->port;
3303 file2 = (file_t*)stream2->port;
3304 if (file1 && file2) {
3305 syncfile(file1, file2);
3306 }
3307}
3308/* lock/unlock stream ----------------------------------------------------------
3309 * lock/unlock stream
3310 * args : stream_t *stream I stream
3311 * return : none
3312 *-----------------------------------------------------------------------------*/
3313extern void strlock(stream_t* stream) { rtk_lock(&stream->lock); }
3314extern void strunlock(stream_t* stream) { rtk_unlock(&stream->lock); }
3315
3316/* read stream -----------------------------------------------------------------
3317 * read data from stream (unblocked)
3318 * args : stream_t *stream I stream
3319 * unsinged char *buff O data buffer
3320 * int n I maximum data length
3321 * return : read data length
3322 * notes : if no data, return immediately with no data
3323 *-----------------------------------------------------------------------------*/
3324extern int strread(stream_t* stream, uint8_t* buff, int n) {
3325 uint32_t tick = tickget();
3326 char* msg = stream->msg;
3327 int nr = 0, tt;
3328
3329 tracet(NULL, 4, "strread: n=%d\n", n);
3330
3331 if (!(stream->mode & STR_MODE_R) || !stream->port) {
3332 return 0;
3333 }
3334
3335 strlock(stream);
3336
3337 switch (stream->type) {
3338 case STR_SERIAL:
3339 nr = readserial((serial_t*)stream->port, buff, n, msg);
3340 break;
3341 case STR_FILE:
3342 nr = readfile((file_t*)stream->port, buff, n, msg);
3343 break;
3344 case STR_TCPSVR:
3345 nr = readtcpsvr((tcpsvr_t*)stream->port, buff, n, msg);
3346 break;
3347 case STR_TCPCLI:
3348 nr = readtcpcli((tcpcli_t*)stream->port, buff, n, msg);
3349 break;
3350 case STR_NTRIPSVR:
3351 case STR_NTRIPCLI:
3352 nr = readntrip((ntrip_t*)stream->port, buff, n, msg);
3353 break;
3354 case STR_NTRIPCAS:
3355 nr = readntripc((ntripc_t*)stream->port, buff, n, msg);
3356 break;
3357 case STR_UDPSVR:
3358 nr = readudpsvr((udp_t*)stream->port, buff, n, msg);
3359 break;
3360 case STR_MEMBUF:
3361 nr = readmembuf((membuf_t*)stream->port, buff, n, msg);
3362 break;
3363 case STR_FTP:
3364 nr = readftp((ftp_t*)stream->port, buff, n, msg);
3365 break;
3366 case STR_HTTP:
3367 nr = readftp((ftp_t*)stream->port, buff, n, msg);
3368 break;
3369 default:
3370 strunlock(stream);
3371 return 0;
3372 }
3373 if (nr > 0) {
3374 stream->inb += nr;
3375 stream->tact = tick;
3376 }
3377 tt = (int)(tick - stream->tick_i);
3378 if (tt >= tirate) {
3379 stream->inr = (uint32_t)((double)((stream->inb - stream->inbt) * 8) / (tt * 0.001));
3380 stream->tick_i = tick;
3381 stream->inbt = stream->inb;
3382 }
3383 strunlock(stream);
3384 return nr;
3385}
3386/* write stream ----------------------------------------------------------------
3387 * write data to stream (unblocked)
3388 * args : stream_t *stream I stream
3389 * unsinged char *buff I data buffer
3390 * int n I data length
3391 * return : bytes written (0: nothing written or error; -1: error for some types)
3392 * notes : write data to buffer and return immediately
3393 *-----------------------------------------------------------------------------*/
3394extern int strwrite(stream_t* stream, uint8_t* buff, int n) {
3395 uint32_t tick = tickget();
3396 char* msg = stream->msg;
3397 int ns, tt;
3398
3399 tracet(NULL, 4, "strwrite: n=%d\n", n);
3400
3401 if (!(stream->mode & STR_MODE_W) || !stream->port) {
3402 return 0;
3403 }
3404
3405 strlock(stream);
3406
3407 switch (stream->type) {
3408 case STR_SERIAL:
3409 ns = writeserial((serial_t*)stream->port, buff, n, msg);
3410 break;
3411 case STR_FILE:
3412 ns = writefile((file_t*)stream->port, buff, n, msg);
3413 break;
3414 case STR_TCPSVR:
3415 ns = writetcpsvr((tcpsvr_t*)stream->port, buff, n, msg);
3416 break;
3417 case STR_TCPCLI:
3418 ns = writetcpcli((tcpcli_t*)stream->port, buff, n, msg);
3419 break;
3420 case STR_NTRIPSVR:
3421 case STR_NTRIPCLI:
3422 ns = writentrip((ntrip_t*)stream->port, buff, n, msg);
3423 break;
3424 case STR_NTRIPCAS:
3425 ns = writentripc((ntripc_t*)stream->port, buff, n, msg);
3426 break;
3427 case STR_UDPCLI:
3428 ns = writeudpcli((udp_t*)stream->port, buff, n, msg);
3429 break;
3430 case STR_MEMBUF:
3431 ns = writemembuf((membuf_t*)stream->port, buff, n, msg);
3432 break;
3433 case STR_FTP:
3434 case STR_HTTP:
3435 default:
3436 strunlock(stream);
3437 return 0;
3438 }
3439 if (ns > 0) {
3440 stream->outb += ns;
3441 stream->tact = tick;
3442 }
3443 tt = (int)(tick - stream->tick_o);
3444 if (tt > tirate) {
3445 stream->outr = (uint32_t)((double)((stream->outb - stream->outbt) * 8) / (tt * 0.001));
3446 stream->tick_o = tick;
3447 stream->outbt = stream->outb;
3448 }
3449 strunlock(stream);
3450 return ns;
3451}
3452/* get stream status -----------------------------------------------------------
3453 * get stream status
3454 * args : stream_t *stream I stream
3455 * char *msg IO status message (NULL: no output)
3456 * return : status (-1:error,0:close,1:wait,2:connect,3:active)
3457 *-----------------------------------------------------------------------------*/
3458extern int strstat(stream_t* stream, char* msg) {
3459 int state = 0;
3460
3461 tracet(NULL, 4, "strstat:\n");
3462
3463 strlock(stream);
3464 if (msg) {
3465 strncpy(msg, stream->msg, MAXSTRMSG - 1);
3466 msg[MAXSTRMSG - 1] = '\0';
3467 }
3468 if (!stream->port) {
3469 strunlock(stream);
3470 return stream->state;
3471 }
3472 switch (stream->type) {
3473 case STR_SERIAL:
3474 state = stateserial((serial_t*)stream->port);
3475 break;
3476 case STR_FILE:
3477 state = statefile((file_t*)stream->port);
3478 break;
3479 case STR_TCPSVR:
3480 state = statetcpsvr((tcpsvr_t*)stream->port);
3481 break;
3482 case STR_TCPCLI:
3483 state = statetcpcli((tcpcli_t*)stream->port);
3484 break;
3485 case STR_NTRIPSVR:
3486 case STR_NTRIPCLI:
3487 state = statentrip((ntrip_t*)stream->port);
3488 break;
3489 case STR_NTRIPCAS:
3490 state = statentripc((ntripc_t*)stream->port);
3491 break;
3492 case STR_UDPSVR:
3493 state = stateudpsvr((udp_t*)stream->port);
3494 break;
3495 case STR_UDPCLI:
3496 state = stateudpcli((udp_t*)stream->port);
3497 break;
3498 case STR_MEMBUF:
3499 state = statemembuf((membuf_t*)stream->port);
3500 break;
3501 case STR_FTP:
3502 state = stateftp((ftp_t*)stream->port);
3503 break;
3504 case STR_HTTP:
3505 state = stateftp((ftp_t*)stream->port);
3506 break;
3507 default:
3508 strunlock(stream);
3509 return 0;
3510 }
3511 if (state == 2 && (int)(tickget() - stream->tact) <= TINTACT) {
3512 state = 3;
3513 }
3514 strunlock(stream);
3515 return state;
3516}
3517/* get extended stream status ---------------------------------------------------
3518 * get extended stream status
3519 * args : stream_t *stream I stream
3520 * char *msg IO extended status message
3521 * return : status (-1:error,0:close,1:wait,2:connect,3:active)
3522 *-----------------------------------------------------------------------------*/
3523extern int strstatx(stream_t* stream, char* msg) {
3524 int state = 0;
3525
3526 tracet(NULL, 4, "strstatx:\n");
3527
3528 strlock(stream);
3529
3530 if (!stream->port) {
3531 strunlock(stream);
3532 return stream->state;
3533 }
3534 switch (stream->type) {
3535 case STR_SERIAL:
3536 state = statexserial((serial_t*)stream->port, msg);
3537 break;
3538 case STR_FILE:
3539 state = statexfile((file_t*)stream->port, msg);
3540 break;
3541 case STR_TCPSVR:
3542 state = statextcpsvr((tcpsvr_t*)stream->port, msg);
3543 break;
3544 case STR_TCPCLI:
3545 state = statextcpcli((tcpcli_t*)stream->port, msg);
3546 break;
3547 case STR_NTRIPSVR:
3548 case STR_NTRIPCLI:
3549 state = statexntrip((ntrip_t*)stream->port, msg);
3550 break;
3551 case STR_NTRIPCAS:
3552 state = statexntripc((ntripc_t*)stream->port, msg);
3553 break;
3554 case STR_UDPSVR:
3555 state = statexudpsvr((udp_t*)stream->port, msg);
3556 break;
3557 case STR_UDPCLI:
3558 state = statexudpcli((udp_t*)stream->port, msg);
3559 break;
3560 case STR_MEMBUF:
3561 state = statexmembuf((membuf_t*)stream->port, msg);
3562 break;
3563 case STR_FTP:
3564 state = statexftp((ftp_t*)stream->port, msg);
3565 break;
3566 case STR_HTTP:
3567 state = statexftp((ftp_t*)stream->port, msg);
3568 break;
3569 default:
3570 msg[0] = '\0';
3571 strunlock(stream);
3572 return 0;
3573 }
3574 if (state == 2 && (int)(tickget() - stream->tact) <= TINTACT) {
3575 state = 3;
3576 }
3577 strunlock(stream);
3578 return state;
3579}
3580/* get stream statistics summary -----------------------------------------------
3581 * get stream statistics summary
3582 * args : stream_t *stream I stream
3583 * uint64_t *inb IO bytes of input (NULL: no output)
3584 * int *inr IO bps of input (NULL: no output)
3585 * uint64_t *outb IO bytes of output (NULL: no output)
3586 * int *outr IO bps of output (NULL: no output)
3587 * return : none
3588 *-----------------------------------------------------------------------------*/
3589extern void strsum(stream_t* stream, uint64_t* inb, int* inr, uint64_t* outb, int* outr) {
3590 tracet(NULL, 4, "strsum:\n");
3591
3592 strlock(stream);
3593 if (inb) {
3594 *inb = stream->inb;
3595 }
3596 if (inr) {
3597 *inr = stream->inr;
3598 }
3599 if (outb) {
3600 *outb = stream->outb;
3601 }
3602 if (outr) {
3603 *outr = stream->outr;
3604 }
3605 strunlock(stream);
3606}
3607/* set global stream options ---------------------------------------------------
3608 * set global stream options
3609 * args : int *opt I options
3610 * opt[0]= inactive timeout (ms) (0: no timeout)
3611 * opt[1]= interval to reconnect (ms)
3612 * opt[2]= averaging time of data rate (ms)
3613 * opt[3]= receive/send buffer size (bytes);
3614 * opt[4]= file swap margin (s)
3615 * opt[5]= reserved
3616 * opt[6]= reserved
3617 * opt[7]= reserved
3618 * return : none
3619 *-----------------------------------------------------------------------------*/
3620extern void strsetopt(const int* opt) {
3621 tracet(NULL, 3, "strsetopt: opt=%d %d %d %d %d %d %d %d\n", opt[0], opt[1], opt[2], opt[3], opt[4], opt[5], opt[6],
3622 opt[7]);
3623
3624 toinact = 0 < opt[0] && opt[0] < 1000 ? 1000 : opt[0]; /* >=1s */
3625 ticonnect = opt[1] < 1000 ? 1000 : opt[1]; /* >=1s */
3626 tirate = opt[2] < 100 ? 100 : opt[2]; /* >=0.1s */
3627 buffsize = opt[3] < 4096 ? 4096 : opt[3]; /* >=4096byte */
3628 fswapmargin = opt[4] < 0 ? 0 : opt[4];
3629}
3630/* set timeout time ------------------------------------------------------------
3631 * set timeout time
3632 * args : stream_t *stream I stream (STR_TCPCLI,STR_NTRIPCLI,STR_NTRIPSVR)
3633 * int toinact I inactive timeout (ms) (0: no timeout)
3634 * int tirecon I reconnect interval (ms) (0: no reconnect)
3635 * return : none
3636 *-----------------------------------------------------------------------------*/
3637extern void strsettimeout(stream_t* stream, int toinact, int tirecon) {
3638 tcpcli_t* tcpcli;
3639
3640 tracet(NULL, 3, "strsettimeout: toinact=%d tirecon=%d\n", toinact, tirecon);
3641
3642 if (stream->type == STR_TCPCLI) {
3643 tcpcli = (tcpcli_t*)stream->port;
3644 } else if (stream->type == STR_NTRIPCLI || stream->type == STR_NTRIPSVR) {
3645 tcpcli = ((ntrip_t*)stream->port)->tcp;
3646 } else {
3647 return;
3648 }
3649
3650 tcpcli->toinact = toinact;
3651 tcpcli->tirecon = tirecon;
3652}
3653/* set local directory ---------------------------------------------------------
3654 * set local directory path for ftp/http download
3655 * args : char *dir I directory for download files
3656 * return : none
3657 *-----------------------------------------------------------------------------*/
3658extern void strsetdir(const char* dir) {
3659 tracet(NULL, 3, "strsetdir: dir=%s\n", dir);
3660
3661 strcpy(localdir, dir);
3662}
3663/* set http/ntrip proxy address ------------------------------------------------
3664 * set http/ntrip proxy address
3665 * args : char *addr I http/ntrip proxy address <address>:<port>
3666 * return : none
3667 *-----------------------------------------------------------------------------*/
3668extern void strsetproxy(const char* addr) {
3669 tracet(NULL, 3, "strsetproxy: addr=%s\n", addr);
3670
3671 strcpy(proxyaddr, addr);
3672}
3673/* set default ntrip version ---------------------------------------------------
3674 * set default NTRIP version for new connections
3675 * args : int ver I NTRIP version (0:auto, 1:v1, 2:v2)
3676 * return : none
3677 *-----------------------------------------------------------------------------*/
3678extern void strsetntripver(int ver) {
3679 tracet(NULL, 3, "strsetntripver: ver=%d\n", ver);
3680
3681 if (ver >= 0 && ver <= 2) {
3682 ntrip_ver_default = ver;
3683 }
3684}
3685/* get stream time -------------------------------------------------------------
3686 * get stream time
3687 * args : stream_t *stream I stream
3688 * return : current time or replay time for playback file
3689 *-----------------------------------------------------------------------------*/
3690extern gtime_t strgettime(stream_t* stream) {
3691 file_t* file;
3692 if (stream->type == STR_FILE && (stream->mode & STR_MODE_R) && (file = (file_t*)stream->port)) {
3693 return timeadd(file->time, file->start); /* replay start time */
3694 }
3695 return utc2gpst(timeget());
3696}
3697/* send nmea request -----------------------------------------------------------
3698 * send nmea gpgga message to stream
3699 * args : stream_t *stream I stream
3700 * sol_t *sol I solution
3701 * return : none
3702 *-----------------------------------------------------------------------------*/
3703extern void strsendnmea(stream_t* stream, const sol_t* sol) {
3704 uint8_t buff[1024];
3705 int n;
3706
3707 tracet(NULL, 3, "strsendnmea: rr=%.3f %.3f %.3f\n", sol->rr[0], sol->rr[1], sol->rr[2]);
3708
3709 n = outnmea_gga(buff, sol);
3710 strwrite(stream, buff, n);
3711}
3712/* generate general hex message ----------------------------------------------*/
3713static int gen_hex(const char* msg, uint8_t* buff) {
3714 uint8_t* q = buff;
3715 char mbuff[1024] = "", *args[256], *p;
3716 uint32_t byte;
3717 int i, narg = 0;
3718
3719 trace(NULL, 4, "gen_hex: msg=%s\n", msg);
3720
3721 strncpy(mbuff, msg, 1023);
3722 for (p = strtok(mbuff, " "); p && narg < 256; p = strtok(NULL, " ")) {
3723 args[narg++] = p;
3724 }
3725 for (i = 0; i < narg; i++) {
3726 if (sscanf(args[i], "%x", &byte)) {
3727 *q++ = (uint8_t)byte;
3728 }
3729 }
3730 return (int)(q - buff);
3731}
3732/* set bitrate ---------------------------------------------------------------*/
3733static int set_brate(stream_t* str, int brate) {
3734 char path[1024], buff[1024] = "", *p, *q;
3735 int type = str->type, mode = str->mode;
3736
3737 if (type != STR_SERIAL) {
3738 return 0;
3739 }
3740
3741 strcpy(path, str->path);
3742
3743 if (!(p = strchr(path, ':'))) {
3744 sprintf(path + strlen(path), ":%d", brate);
3745 } else {
3746 if ((q = strchr(p + 1, ':'))) {
3747 strcpy(buff, q);
3748 }
3749 sprintf(p, ":%d%s", brate, buff);
3750 }
3751 strclose(str);
3752 return stropen(str, type, mode, path);
3753}
3754/* send receiver command -------------------------------------------------------
3755 * send receiver commands to stream
3756 * args : stream_t *stream I stream
3757 * char *cmd I receiver command strings
3758 * return : none
3759 *-----------------------------------------------------------------------------*/
3760extern void strsendcmd(stream_t* str, const char* cmd) {
3761 uint8_t buff[1024];
3762 const char *p = cmd, *q;
3763 char msg[1024], cmdend[] = "\r\n";
3764 int n, m, ms, brate;
3765
3766 tracet(NULL, 3, "strsendcmd: cmd=%s\n", cmd);
3767
3768 while (1) {
3769 for (q = p;; q++) {
3770 if (*q == '\r' || *q == '\n' || *q == '\0') {
3771 break;
3772 }
3773 }
3774 n = (int)(q - p);
3775 strncpy(msg, p, n);
3776 msg[n] = '\0';
3777
3778 if (!*msg || *msg == '#') { /* null or comment */
3779 /* skip */
3780 } else if (*msg == '!') { /* binary escape */
3781
3782 if (!strncmp(msg + 1, "WAIT", 4)) { /* wait */
3783 if (sscanf(msg + 5, "%d", &ms) < 1) {
3784 ms = 100;
3785 }
3786 if (ms > 3000) {
3787 ms = 3000; /* max 3 s */
3788 }
3789 sleepms(ms);
3790 } else if (!strncmp(msg + 1, "BRATE", 5)) { /* set bitrate */
3791 if (sscanf(msg + 6, "%d", &brate) < 1) {
3792 brate = 9600;
3793 }
3794 set_brate(str, brate);
3795 sleepms(500);
3796 } else if (!strncmp(msg + 1, "UBX", 3)) { /* ublox */
3797 if ((m = gen_ubx(msg + 4, buff)) > 0) {
3798 strwrite(str, buff, m);
3799 }
3800 } else if (!strncmp(msg + 1, "STQ", 3)) { /* skytraq */
3801 if ((m = gen_stq(msg + 4, buff)) > 0) {
3802 strwrite(str, buff, m);
3803 }
3804 } else if (!strncmp(msg + 1, "NVS", 3)) { /* nvs */
3805 if ((m = gen_nvs(msg + 4, buff)) > 0) {
3806 strwrite(str, buff, m);
3807 }
3808 } else if (!strncmp(msg + 1, "HEX", 3)) { /* general hex message */
3809 if ((m = gen_hex(msg + 4, buff)) > 0) {
3810 strwrite(str, buff, m);
3811 }
3812 }
3813 } else {
3814 strcat(msg, cmdend);
3815 strwrite(str, (uint8_t*)msg, n + 2);
3816 }
3817 if (*q == '\0') {
3818 break;
3819 } else {
3820 p = q + 1;
3821 }
3822 }
3823}
#define MAXSTRPATH
#define rtk_unlock(f)
#define rtk_thread_t
#define rtk_initlock(f)
#define MAXSTRMSG
#define rtk_lock_t
#define rtk_lock(f)
int gen_nvs(const char *msg, uint8_t *buff)
int gen_stq(const char *msg, uint8_t *buff)
int gen_ubx(const char *msg, uint8_t *buff)
MRTKLIB Receiver Raw Data Module — Receiver raw data types and decoder functions for various GNSS rec...
int outnmea_gga(uint8_t *buff, const sol_t *sol)
Output NMEA GGA sentence.
Definition mrtk_sol.c:1447
MRTKLIB Solution Module — Solution data types and I/O functions.
static uint32_t tick_master
static void closemembuf(membuf_t *membuf)
static int statemembuf(membuf_t *membuf)
static int statextcpcli(tcpcli_t *tcpcli, char *msg)
static void decodeftppath(const char *path, char *addr, char *file, char *user, char *passwd, int *topts)
#define NTRIP_SVR_PORT
Definition mrtk_stream.c:61
static int writetcpsvr(tcpsvr_t *tcpsvr, uint8_t *buff, int n, char *msg)
#define NTRIP_AGENT
Definition mrtk_stream.c:59
#define socket_t
static file_t * openfile(const char *path, int mode, char *msg)
#define MAXSTATMSG
Definition mrtk_stream.c:56
static int stateudpsvr(udp_t *udpsvr)
void strinit(stream_t *stream)
Initialize stream structure.
static int readntripc(ntripc_t *ntripc, uint8_t *buff, int n, char *msg)
static int readudpsvr(udp_t *udpsvr, uint8_t *buff, int n, char *msg)
static int consock(tcpcli_t *tcpcli, char *msg)
#define NTRIP_RSP_OK_CLI
Definition mrtk_stream.c:64
static udp_t * openudpsvr(const char *path, char *msg)
static int writefile(file_t *file, uint8_t *buff, int n, char *msg)
#define TINTACT
Definition mrtk_stream.c:52
static void send_srctbl(ntripc_t *ntripc, int ver, int sock)
#define NTRIP_RSP_UNAUTH
Definition mrtk_stream.c:70
static void syncfile(file_t *file1, file_t *file2)
int strstat(stream_t *stream, char *msg)
Get stream status.
static int fswapmargin
static int readntrip(ntrip_t *ntrip, uint8_t *buff, int n, char *msg)
static ntrip_t * openntrip(const char *path, int type, char *msg)
static void closeudpsvr(udp_t *udpsvr)
static void closeftp(ftp_t *ftp)
#define FTP_CMD
Definition mrtk_stream.c:74
struct tcpsvr_tag tcpsvr_t
#define NTRIP_RSP_SRCTBL
Definition mrtk_stream.c:66
static ntripc_t * openntripc(const char *path, char *msg)
static int statentripc(ntripc_t *ntripc)
gtime_t strgettime(stream_t *stream)
Get stream time.
static void send_rsp_err(ntripc_t *ntripc, int ver, int err, int sock)
static int readserial(serial_t *serial, uint8_t *buff, int n, char *msg)
#define TIMETAGH_LEN
Definition mrtk_stream.c:54
void strsettimeout(stream_t *stream, int toinact, int tirecon)
Set stream timeout.
static int statetcpsvr(tcpsvr_t *tcpsvr)
static int openfile_(file_t *file, gtime_t time, char *msg)
static int ntrip_ver_default
static int readtcpsvr(tcpsvr_t *tcpsvr, uint8_t *buff, int n, char *msg)
static int statexntripc(ntripc_t *ntripc, char *msg)
static int statexmembuf(membuf_t *membuf, char *msg)
static char proxyaddr[256]
static int statetcpcli(tcpcli_t *tcpcli)
static int readfile(file_t *file, uint8_t *buff, int nmax, char *msg)
int strstatx(stream_t *stream, char *msg)
Get stream extended status.
static int set_brate(stream_t *str, int brate)
#define FTP_TIMEOUT
Definition mrtk_stream.c:75
int strread(stream_t *stream, uint8_t *buff, int n)
Read data from stream.
void strsendnmea(stream_t *stream, const sol_t *sol)
Send NMEA request to stream.
static tcpcli_t * opentcpcli(const char *path, char *msg)
static int waittcpcli(tcpcli_t *tcpcli, char *msg)
static int statexserial(serial_t *serial, char *msg)
static void closetcpsvr(tcpsvr_t *tcpsvr)
static int statexudpcli(udp_t *udpcli, char *msg)
static void closefile(file_t *file)
static void wait_ntripc(ntripc_t *ntripc, char *msg)
static int recv_nb(int sock, uint8_t *buff, int n)
static void discon_ntripc(ntripc_t *ntripc, int i)
static void urldecode(char *str)
static void closetcpcli(tcpcli_t *tcpcli)
#define CRTSCTS
Definition mrtk_stream.c:37
static int statexntrip(ntrip_t *ntrip, char *msg)
static int reqntrip_s(ntrip_t *ntrip, char *msg)
static void swapfile(file_t *file, gtime_t time, char *msg)
static int statextcp(tcp_t *tcp, char *msg)
static int readmembuf(membuf_t *membuf, uint8_t *buff, int n, char *msg)
static int accept_nb(int sock, struct sockaddr *addr, socklen_t *len)
static int readftp(ftp_t *ftp, uint8_t *buff, int n, char *msg)
void strsetopt(const int *opt)
Set stream options.
static int waittcpsvr(tcpsvr_t *tcpsvr, char *msg)
static void decodetcppath(const char *path, char *addr, char *port, char *user, char *passwd, char *mntpnt, char *str)
static void closefile_(file_t *file)
static int encbase64(char *str, const uint8_t *byte, int n)
static int buffsize
static void closeserial(serial_t *serial)
static int writentripc(ntripc_t *ntripc, uint8_t *buff, int n, char *msg)
static int statextcpsvr(tcpsvr_t *tcpsvr, char *msg)
void strlock(stream_t *stream)
Lock stream.
#define dev_t
#define NTRIP_MAXSTR
Definition mrtk_stream.c:63
static void closeudpcli(udp_t *udpcli)
void strsetdir(const char *dir)
Set FTP/HTTP local directory.
void strinitcom(void)
Initialize stream communication system.
#define NTRIP_CLI_PORT
Definition mrtk_stream.c:60
static tcpsvr_t * opentcpsvr(const char *path, char *msg)
static void swapclose(file_t *file)
static int readtcpcli(tcpcli_t *tcpcli, uint8_t *buff, int n, char *msg)
static membuf_t * openmembuf(const char *path, char *msg)
static void closentrip(ntrip_t *ntrip)
static int connect_nb(int sock, struct sockaddr *addr, socklen_t len)
static void * ftpthread(void *arg)
const char * formatstrs[32]
Definition mrtk_stream.c:80
static void discontcp(tcp_t *tcp, int tcon)
#define NTRIP_RSP_TBLEND
Definition mrtk_stream.c:67
static int statexudpsvr(udp_t *udpsvr, char *msg)
static int rspntrip_c(ntrip_t *ntrip, char *msg)
static ftp_t * openftp(const char *path, int type, char *msg)
static void closentripc(ntripc_t *ntripc)
static udp_t * openudpcli(const char *path, char *msg)
static int writetcpcli(tcpcli_t *tcpcli, uint8_t *buff, int n, char *msg)
#define BUFADD(p, rem,...)
static int stateftp(ftp_t *ftp)
#define NTRIP_RSP_OK_SVR
Definition mrtk_stream.c:65
static int statentrip(ntrip_t *ntrip)
static int statefile(file_t *file)
static gtime_t nextdltime(const int *topts, int stat)
static int stateserial(serial_t *serial)
static int stateudpcli(udp_t *udpcli)
static void time_str_http(char *buff)
static int waitntrip(ntrip_t *ntrip, char *msg)
static int setsock(int sock, char *msg)
static int gentcp(tcp_t *tcp, int type, char *msg)
#define NTRIP_RSP_ERROR
Definition mrtk_stream.c:69
static int reqntrip_c(ntrip_t *ntrip, char *msg)
static int accsock(tcpsvr_t *tcpsvr, char *msg)
static void send_rsp_ok(int ver, int sock)
void strclose(stream_t *stream)
Close stream.
static int tirate
static int gen_hex(const char *msg, uint8_t *buff)
int strwrite(stream_t *stream, uint8_t *buff, int n)
Write data to stream.
static int send_nb(int sock, uint8_t *buff, int n)
void strsetntripver(int ver)
Set default NTRIP version for new connections.
static udp_t * genudp(int type, int port, const char *saddr, char *msg)
static int parse_ntrip_query(char *mntpnt, char *host_override, int host_size)
static void rsp_ntripc(ntripc_t *ntripc, int i)
static int writeserial(serial_t *serial, uint8_t *buff, int n, char *msg)
static int statexfile(file_t *file, char *msg)
static int writentrip(ntrip_t *ntrip, uint8_t *buff, int n, char *msg)
void strsetproxy(const char *addr)
Set HTTP/NTRIP proxy address.
static int rspntrip_s(ntrip_t *ntrip, char *msg)
int stropen(stream_t *stream, int type, int mode, const char *path)
Open stream.
static int writemembuf(membuf_t *membuf, uint8_t *buff, int n, char *msg)
void strsync(stream_t *stream1, stream_t *stream2)
Sync streams.
#define NTRIP_MAXRSP
Definition mrtk_stream.c:62
void strunlock(stream_t *stream)
Unlock stream.
static int statexftp(ftp_t *ftp, char *msg)
static void updatetcpsvr(tcpsvr_t *tcpsvr, char *msg)
#define MAXCLI
Definition mrtk_stream.c:55
static int errsock(void)
static int writeudpcli(udp_t *udpcli, uint8_t *buff, int n, char *msg)
static int ticonnect
#define DEFAULT_MEMBUF_SIZE
Definition mrtk_stream.c:57
void strsum(stream_t *stream, uint64_t *inb, int *inr, uint64_t *outb, int *outr)
Get stream statistics summary.
static serial_t * openserial(const char *path, int mode, char *msg)
static char localdir[1024]
static int toinact
#define closesocket
void strsendcmd(stream_t *str, const char *cmd)
Send receiver command to stream.
MRTKLIB Stream Module — stream I/O abstraction layer.
#define STR_FILE
Definition mrtk_stream.h:45
#define STR_MODE_W
Definition mrtk_stream.h:60
#define STR_UDPCLI
Definition mrtk_stream.h:54
#define STR_NTRIPCLI
Definition mrtk_stream.h:49
#define STR_SERIAL
Definition mrtk_stream.h:44
#define STR_TCPCLI
Definition mrtk_stream.h:47
#define STR_MEMBUF
Definition mrtk_stream.h:55
#define STR_MODE_R
Definition mrtk_stream.h:59
#define STR_HTTP
Definition mrtk_stream.h:51
#define STR_NTRIPCAS
Definition mrtk_stream.h:52
#define STR_FTP
Definition mrtk_stream.h:50
#define STR_NTRIPSVR
Definition mrtk_stream.h:48
struct stream_tag stream_t
#define STR_UDPSVR
Definition mrtk_stream.h:53
#define STR_TCPSVR
Definition mrtk_stream.h:46
#define FILEPATHSEP
Definition mrtk_sys.c:48
int rtk_uncompress(const char *file, char *uncfile)
Uncompress file (gzip/zip/tar/Hatanaka).
Definition mrtk_sys.c:367
int execcmd(const char *cmd)
Execute command line by operating system shell.
Definition mrtk_sys.c:83
void createdir(const char *path)
Create directory if not exists (recursively).
Definition mrtk_sys.c:187
int reppath(const char *path, char *rpath, gtime_t time, const char *rov, const char *base)
Replace keywords in file path with date, time, rover and base station id.
Definition mrtk_sys.c:248
MRTKLIB System Module — OS/filesystem utility functions.
void sleepms(int ms)
Sleep for specified milliseconds.
Definition mrtk_time.c:588
gtime_t gpst2time(int week, double sec)
Convert GPS week/tow to gtime_t.
Definition mrtk_time.c:165
void time2str(gtime_t t, char *s, int n)
Convert gtime_t struct to string.
Definition mrtk_time.c:507
gtime_t gpst2utc(gtime_t t)
Convert GPS time to UTC considering leap seconds.
Definition mrtk_time.c:422
double timediff(gtime_t t1, gtime_t t2)
Compute time difference (t1 - t2).
Definition mrtk_time.c:279
double time2gpst(gtime_t t, int *week)
Convert gtime_t to GPS week/tow.
Definition mrtk_time.c:181
gtime_t timeadd(gtime_t t, double sec)
Add seconds to gtime_t.
Definition mrtk_time.c:265
char * time_str(gtime_t t, int n)
Get time string (not reentrant).
Definition mrtk_time.c:530
void timereset(void)
Reset time offset to zero.
Definition mrtk_time.c:324
void timeset(gtime_t t)
Set current time offset in UTC.
Definition mrtk_time.c:318
gtime_t utc2gpst(gtime_t t)
Convert UTC to GPS time considering leap seconds.
Definition mrtk_time.c:440
gtime_t timeget(void)
Get current time in UTC.
Definition mrtk_time.c:289
uint32_t tickget(void)
Get current tick in milliseconds.
Definition mrtk_time.c:566
MRTKLIB Time Module — GNSS time types and conversion functions.
void tracet(mrtk_ctx_t *ctx, int level, const char *format,...)
Output trace message with elapsed time.
Definition mrtk_trace.c:91
void trace(mrtk_ctx_t *ctx, int level, const char *format,...)
Output trace message.
Definition mrtk_trace.c:78
MRTKLIB Trace Module — debug trace and logging functions.
static int chunk_decode(chunk_dec_t *dec, const uint8_t **pin, int *pnin, uint8_t *out, int nout)
Definition ntrip_chunk.h:74
#define NTRIP_VER_1
Definition ntrip_chunk.h:32
static int http_find_header(const uint8_t *buff, int nb, const char *name, char *val, int vmax)
static const char * stristr(const char *haystack, const char *needle)
static int http_header_end(const uint8_t *buff, int nb)
#define NTRIP_VER_AUTO
Definition ntrip_chunk.h:31
static int chunk_encode(uint8_t *out, int nout, const uint8_t *data, int ndata)
static void chunk_dec_init(chunk_dec_t *dec)
Definition ntrip_chunk.h:55
#define NTRIP_VER_2
Definition ntrip_chunk.h:33
static int http_status_code(const uint8_t *buff, int nb)
FILE * fp_tmp
int repmode
int offset
FILE * fp
double start
char openpath[MAXSTRPATH]
char path[MAXSTRPATH]
FILE * fp_tag
uint32_t tick
rtk_lock_t lock
uint32_t tick_n
int timetag
int size_fpos
FILE * fp_tag_tmp
double swapintv
uint32_t tick_f
double speed
long fpos_n
gtime_t time
gtime_t wtime
char local[1024]
gtime_t tnext
rtk_thread_t thread
int topts[4]
char file[1024]
int state
char user[256]
char addr[1024]
int proto
char passwd[256]
int error
GNSS time representation.
Definition mrtk_time.h:49
double sec
Definition mrtk_time.h:51
time_t time
Definition mrtk_time.h:50
uint8_t * buf
rtk_lock_t lock
char url[MAXSTRPATH]
char user[256]
int v2_tried
char host[256]
int chunked
int ver_neg
char mntpnt[256]
tcpcli_t * tcp
char passwd[256]
uint8_t buff[32768]
char str[256]
chunk_dec_t cdec
char str[256]
char mntpnt[256]
uint8_t buff[32768]
char passwd[256]
tcpsvr_t * tcp
char srctbl[256]
char user[256]
ntripc_con_t con[32]
char mntpnt[256]
tcpsvr_t * tcpsvr
double rr[6]
Definition mrtk_sol.h:46
char msg[MAXSTRMSG]
Definition mrtk_stream.h:84
void * port
Definition mrtk_stream.h:82
uint64_t outb
Definition mrtk_stream.h:75
uint32_t inr
Definition mrtk_stream.h:74
uint64_t inbt
Definition mrtk_stream.h:80
uint32_t outr
Definition mrtk_stream.h:76
uint32_t tick_i
Definition mrtk_stream.h:77
uint64_t inb
Definition mrtk_stream.h:73
rtk_lock_t lock
Definition mrtk_stream.h:81
char path[MAXSTRPATH]
Definition mrtk_stream.h:83
uint32_t tick_o
Definition mrtk_stream.h:78
uint32_t tact
Definition mrtk_stream.h:79
uint64_t outbt
Definition mrtk_stream.h:80
int sock
uint32_t tact
int port
struct sockaddr_in addr
int tcon
uint32_t tdis
int state
char saddr[256]
tcp_t svr
tcp_t cli[32]
int state
int port
int sock
struct sockaddr_in addr
char saddr[256]
int type
#define malloc(x)
Definition toml.c:52
#define calloc(x, y)
Definition toml.c:54
#define free(x)
Definition toml.c:53