1 /****************************************************************************
2 
3 NAME
4    ais_json.c - deserialize AIS JSON
5 
6 DESCRIPTION
7    This module uses the generic JSON parser to get data from AIS
8 representations to libgps structures.
9 
10 This file is Copyright (c)2010-2018 by the GPSD project
11 SPDX-License-Identifier: BSD-2-clause
12 ***************************************************************************/
13 
14 #include "gpsd_config.h"  /* must be before all includes */
15 
16 #include <stdio.h>
17 #include <string.h>
18 #include <stdbool.h>
19 #include <stdlib.h>
20 #include <stddef.h>
21 #include <time.h>
22 
23 #include "gps.h"
24 #include "json.h"
25 #ifdef SOCKET_EXPORT_ENABLE
26 #include "libgps.h"
27 
28 /* kluge because we don't want to include gpsd.h here */
29 extern int gpsd_hexpack(const char *, char *, size_t);
30 
lenhex_unpack(const char * from,size_t * plen,char * to,size_t maxlen)31 static void lenhex_unpack(const char *from,
32 			  size_t * plen, char *to, size_t maxlen)
33 {
34     char *colon = strchr(from, ':');
35 
36     *plen = (size_t) atoi(from);
37     if (colon != NULL)
38 	(void)gpsd_hexpack(colon + 1, to, maxlen);
39 }
40 
41 
json_ais_read(const char * buf,char * path,size_t pathlen,struct ais_t * ais,const char ** endptr)42 int json_ais_read(const char *buf,
43 		  char *path, size_t pathlen, struct ais_t *ais,
44 		  const char **endptr)
45 {
46     /* collected but not actually used yet */
47     bool scaled;
48 
49 #define AIS_HEADER \
50 	{"class",          t_check,    .dflt.check = "AIS"}, \
51 	{"type",           t_uinteger, .addr.uinteger = &ais->type}, \
52 	{"device",         t_string,   .addr.string = path, \
53 	                                  .len = pathlen}, \
54 	{"repeat",         t_uinteger, .addr.uinteger = &ais->repeat}, \
55 	{"scaled",         t_boolean,  .addr.boolean = &scaled, \
56 		                          .dflt.boolean = false}, \
57 	{"mmsi",           t_uinteger, .addr.uinteger = &ais->mmsi},
58 
59 #define AIS_TYPE6 \
60 	{"seqno",         t_uinteger,  .addr.uinteger = &ais->type6.seqno,\
61                                        .dflt.uinteger = 0},\
62 	{"dest_mmsi",     t_uinteger,  .addr.uinteger = &ais->type6.dest_mmsi,\
63                                        .dflt.uinteger = 0},\
64 	{"retransmit",    t_boolean,   .addr.boolean = &ais->type6.retransmit,\
65                                        .dflt.boolean = false},\
66 	{"dac",           t_uinteger,  .addr.uinteger = &ais->type6.dac,\
67                                        .dflt.uinteger = 0},\
68 	{"fid",           t_uinteger,  .addr.uinteger = &ais->type6.fid,\
69                                        .dflt.uinteger = 0},
70 #define AIS_TYPE8 \
71 	{"dac",           t_uinteger,  .addr.uinteger = &ais->type8.dac,\
72                                        .dflt.uinteger = 0},\
73 	{"fid",           t_uinteger,  .addr.uinteger = &ais->type8.fid,\
74                                        .dflt.uinteger = 0},
75 
76     int status;
77 
78 #include "ais_json.i"		/* JSON parser template structures */
79 
80 #undef AIS_HEADER
81 
82     memset(ais, '\0', sizeof(struct ais_t));
83 
84     if (strstr(buf, "\"type\":1,") != NULL
85 	|| strstr(buf, "\"type\":2,") != NULL
86 	|| strstr(buf, "\"type\":3,") != NULL) {
87 	status = json_read_object(buf, json_ais1, endptr);
88     } else if (strstr(buf, "\"type\":4,") != NULL
89 	       || strstr(buf, "\"type\":11,") != NULL) {
90 	status = json_read_object(buf, json_ais4, endptr);
91 	if (status == 0) {
92 	    ais->type4.year = AIS_YEAR_NOT_AVAILABLE;
93 	    ais->type4.month = AIS_MONTH_NOT_AVAILABLE;
94 	    ais->type4.day = AIS_DAY_NOT_AVAILABLE;
95 	    ais->type4.hour = AIS_HOUR_NOT_AVAILABLE;
96 	    ais->type4.minute = AIS_MINUTE_NOT_AVAILABLE;
97 	    ais->type4.second = AIS_SECOND_NOT_AVAILABLE;
98 	    // We use %09u for the date to allow for dodgy years (>9999) to go through
99 	    // cppcheck-suppress uninitvar
100 	    (void)sscanf(timestamp, "%09u-%02u-%02uT%02u:%02u:%02uZ",
101 			 &ais->type4.year,
102 			 &ais->type4.month,
103 			 &ais->type4.day,
104 			 &ais->type4.hour,
105 			 &ais->type4.minute,
106 			 &ais->type4.second);
107 	}
108     } else if (strstr(buf, "\"type\":5,") != NULL) {
109 	status = json_read_object(buf, json_ais5, endptr);
110 	if (status == 0) {
111 	    ais->type5.month = AIS_MONTH_NOT_AVAILABLE;
112 	    ais->type5.day = AIS_DAY_NOT_AVAILABLE;
113 	    ais->type5.hour = AIS_HOUR_NOT_AVAILABLE;
114 	    ais->type5.minute = AIS_MINUTE_NOT_AVAILABLE;
115 	    // cppcheck-suppress uninitvar
116 	    (void)sscanf(eta, "%02u-%02uT%02u:%02uZ",
117 			 &ais->type5.month,
118 			 &ais->type5.day,
119 			 &ais->type5.hour,
120 			 &ais->type5.minute);
121 	}
122     } else if (strstr(buf, "\"type\":6,") != NULL) {
123 	bool structured = false;
124 	if (strstr(buf, "\"dac\":1,") != NULL) {
125 	    if (strstr(buf, "\"fid\":12,") != NULL) {
126 		status = json_read_object(buf, json_ais6_fid12, endptr);
127 		if (status == 0) {
128 		    ais->type6.dac1fid12.lmonth = AIS_MONTH_NOT_AVAILABLE;
129 		    ais->type6.dac1fid12.lday = AIS_DAY_NOT_AVAILABLE;
130 		    ais->type6.dac1fid12.lhour = AIS_HOUR_NOT_AVAILABLE;
131 		    ais->type6.dac1fid12.lminute = AIS_MINUTE_NOT_AVAILABLE;
132 		    // cppcheck-suppress uninitvar
133 		    (void)sscanf(departure, "%02u-%02uT%02u:%02uZ",
134 				 &ais->type6.dac1fid12.lmonth,
135 				 &ais->type6.dac1fid12.lday,
136 				 &ais->type6.dac1fid12.lhour,
137 				 &ais->type6.dac1fid12.lminute);
138 		    ais->type6.dac1fid12.nmonth = AIS_MONTH_NOT_AVAILABLE;
139 		    ais->type6.dac1fid12.nday = AIS_DAY_NOT_AVAILABLE;
140 		    ais->type6.dac1fid12.nhour = AIS_HOUR_NOT_AVAILABLE;
141 		    ais->type6.dac1fid12.nminute = AIS_MINUTE_NOT_AVAILABLE;
142 		    // cppcheck-suppress uninitvar
143 		    (void)sscanf(eta, "%02u-%02uT%02u:%02uZ",
144 				 &ais->type6.dac1fid12.nmonth,
145 				 &ais->type6.dac1fid12.nday,
146 				 &ais->type6.dac1fid12.nhour,
147 				 &ais->type6.dac1fid12.nminute);
148 		}
149 		structured = true;
150 	    }
151 	    else if (strstr(buf, "\"fid\":15,") != NULL) {
152 		status = json_read_object(buf, json_ais6_fid15, endptr);
153 		structured = true;
154 	    }
155 	    else if (strstr(buf, "\"fid\":16,") != NULL) {
156 		status = json_read_object(buf, json_ais6_fid16, endptr);
157 		structured = true;
158 	    }
159 	    else if (strstr(buf, "\"fid\":18,") != NULL) {
160 		status = json_read_object(buf, json_ais6_fid18, endptr);
161 		if (status == 0) {
162 		    ais->type6.dac1fid18.day = AIS_DAY_NOT_AVAILABLE;
163 		    ais->type6.dac1fid18.hour = AIS_HOUR_NOT_AVAILABLE;
164 		    ais->type6.dac1fid18.minute = AIS_MINUTE_NOT_AVAILABLE;
165 		    // cppcheck-suppress uninitvar
166 		    (void)sscanf(arrival, "%02u-%02uT%02u:%02uZ",
167 				 &ais->type6.dac1fid18.month,
168 				 &ais->type6.dac1fid18.day,
169 				 &ais->type6.dac1fid18.hour,
170 				 &ais->type6.dac1fid18.minute);
171 		}
172 		structured = true;
173 	    }
174 	    else if (strstr(buf, "\"fid\":20,") != NULL) {
175 		status = json_read_object(buf, json_ais6_fid20, endptr);
176 		if (status == 0) {
177 		    ais->type6.dac1fid20.month = AIS_MONTH_NOT_AVAILABLE;
178 		    ais->type6.dac1fid20.day = AIS_DAY_NOT_AVAILABLE;
179 		    ais->type6.dac1fid20.hour = AIS_HOUR_NOT_AVAILABLE;
180 		    ais->type6.dac1fid20.minute = AIS_MINUTE_NOT_AVAILABLE;
181 		    // cppcheck-suppress uninitvar
182 		    (void)sscanf(arrival, "%02u-%02uT%02u:%02uZ",
183 				 &ais->type6.dac1fid20.month,
184 				 &ais->type6.dac1fid20.day,
185 				 &ais->type6.dac1fid20.hour,
186 				 &ais->type6.dac1fid20.minute);
187 		}
188 		structured = true;
189 	    }
190 	    else if (strstr(buf, "\"fid\":25,") != NULL) {
191 		status = json_read_object(buf, json_ais6_fid25, endptr);
192 		structured = true;
193 	    }
194 	    else if (strstr(buf, "\"fid\":28,") != NULL) {
195 		status = json_read_object(buf, json_ais6_fid28, endptr);
196 		if (status == 0) {
197 		    ais->type6.dac1fid28.month = AIS_MONTH_NOT_AVAILABLE;
198 		    ais->type6.dac1fid28.day = AIS_DAY_NOT_AVAILABLE;
199 		    ais->type6.dac1fid28.hour = AIS_HOUR_NOT_AVAILABLE;
200 		    ais->type6.dac1fid28.minute = AIS_MINUTE_NOT_AVAILABLE;
201 		    // cppcheck-suppress uninitvar
202 		    (void)sscanf(start, "%02u-%02uT%02u:%02uZ",
203 				 &ais->type6.dac1fid28.month,
204 				 &ais->type6.dac1fid28.day,
205 				 &ais->type6.dac1fid28.hour,
206 				 &ais->type6.dac1fid28.minute);
207 		}
208 		structured = true;
209 	    }
210 	    else if (strstr(buf, "\"fid\":30,") != NULL) {
211 		status = json_read_object(buf, json_ais6_fid30, endptr);
212 		structured = true;
213 	    }
214 	    else if (strstr(buf, "\"fid\":32,") != NULL || strstr(buf, "\"fid\":14,") != NULL) {
215 		status = json_read_object(buf, json_ais6_fid32, endptr);
216 		structured = true;
217 	    }
218 	}
219 	else if (strstr(buf, "\"dac\":235,") != NULL || strstr(buf, "\"dac\":250,") != NULL) {
220 	    if (strstr(buf, "\"fid\":10,") != NULL) {
221 		status = json_read_object(buf, json_ais6_fid10, endptr);
222 		structured = true;
223 	    }
224 	}
225 	else if (strstr(buf, "\"dac\":200,") != NULL) {
226 	    if (strstr(buf, "\"fid\":21,") != NULL) {
227 		status = json_read_object(buf, json_ais6_fid21, endptr);
228 		structured = true;
229 		if (status == 0) {
230 		    ais->type6.dac200fid21.month = AIS_MONTH_NOT_AVAILABLE;
231 		    ais->type6.dac200fid21.day = AIS_DAY_NOT_AVAILABLE;
232 		    ais->type6.dac200fid21.hour = AIS_HOUR_NOT_AVAILABLE;
233 		    ais->type6.dac200fid21.minute = AIS_MINUTE_NOT_AVAILABLE;
234 		    // cppcheck-suppress uninitvar
235 		    (void)sscanf(eta, "%02u-%02uT%02u:%02u",
236 				 &ais->type6.dac200fid21.month,
237 				 &ais->type6.dac200fid21.day,
238 				 &ais->type6.dac200fid21.hour,
239 				 &ais->type6.dac200fid21.minute);
240 		}
241 	    }
242 	    else if (strstr(buf, "\"fid\":22,") != NULL) {
243 		status = json_read_object(buf, json_ais6_fid22, endptr);
244 		structured = true;
245 		if (status == 0) {
246 		    ais->type6.dac200fid22.month = AIS_MONTH_NOT_AVAILABLE;
247 		    ais->type6.dac200fid22.day = AIS_DAY_NOT_AVAILABLE;
248 		    ais->type6.dac200fid22.hour = AIS_HOUR_NOT_AVAILABLE;
249 		    ais->type6.dac200fid22.minute = AIS_MINUTE_NOT_AVAILABLE;
250 		    // cppcheck-suppress uninitvar
251 		    (void)sscanf(rta, "%02u-%02uT%02u:%02u",
252 				 &ais->type6.dac200fid22.month,
253 				 &ais->type6.dac200fid22.day,
254 				 &ais->type6.dac200fid22.hour,
255 				 &ais->type6.dac200fid22.minute);
256 		}
257 	    }
258 	    else if (strstr(buf, "\"fid\":55,") != NULL) {
259 		status = json_read_object(buf, json_ais6_fid55, endptr);
260 		structured = true;
261 	    }
262 	}
263 	if (!structured) {
264 	    status = json_read_object(buf, json_ais6, endptr);
265 	    if (status == 0)
266 		lenhex_unpack(data, &ais->type6.bitcount,
267 			      ais->type6.bitdata, sizeof(ais->type6.bitdata));
268 	}
269 	ais->type6.structured = structured;
270     } else if (strstr(buf, "\"type\":7,") != NULL
271 	       || strstr(buf, "\"type\":13,") != NULL) {
272 	status = json_read_object(buf, json_ais7, endptr);
273     } else if (strstr(buf, "\"type\":8,") != NULL) {
274 	bool structured = false;
275 	if (strstr(buf, "\"dac\":1,") != NULL) {
276 	    if (strstr(buf, "\"fid\":11,") != NULL) {
277 		status = json_read_object(buf, json_ais8_fid11, endptr);
278 		if (status == 0) {
279 		    ais->type8.dac1fid11.day = AIS_DAY_NOT_AVAILABLE;
280 		    ais->type8.dac1fid11.hour = AIS_HOUR_NOT_AVAILABLE;
281 		    ais->type8.dac1fid11.minute = AIS_MINUTE_NOT_AVAILABLE;
282 		    // cppcheck-suppress uninitvar
283 		    (void)sscanf(timestamp, "%02uT%02u:%02uZ",
284 				 &ais->type8.dac1fid11.day,
285 				 &ais->type8.dac1fid11.hour,
286 				 &ais->type8.dac1fid11.minute);
287 		}
288 		structured = true;
289 	    }
290 	    else if (strstr(buf, "\"fid\":13,") != NULL) {
291 		status = json_read_object(buf, json_ais8_fid13, endptr);
292 		if (status == 0) {
293 		    ais->type8.dac1fid13.fmonth = AIS_MONTH_NOT_AVAILABLE;
294 		    ais->type8.dac1fid13.fday = AIS_DAY_NOT_AVAILABLE;
295 		    ais->type8.dac1fid13.fhour = AIS_HOUR_NOT_AVAILABLE;
296 		    ais->type8.dac1fid13.fminute = AIS_MINUTE_NOT_AVAILABLE;
297 		    // cppcheck-suppress uninitvar
298 		    (void)sscanf(departure, "%02u-%02uT%02u:%02uZ",
299 				 &ais->type8.dac1fid13.fmonth,
300 				 &ais->type8.dac1fid13.fday,
301 				 &ais->type8.dac1fid13.fhour,
302 				 &ais->type8.dac1fid13.fminute);
303 		    ais->type8.dac1fid13.tmonth = AIS_MONTH_NOT_AVAILABLE;
304 		    ais->type8.dac1fid13.tday = AIS_DAY_NOT_AVAILABLE;
305 		    ais->type8.dac1fid13.thour = AIS_HOUR_NOT_AVAILABLE;
306 		    ais->type8.dac1fid13.tminute = AIS_MINUTE_NOT_AVAILABLE;
307 		    // cppcheck-suppress uninitvar
308 		    (void)sscanf(eta, "%02u-%02uT%02u:%02uZ",
309 				 &ais->type8.dac1fid13.tmonth,
310 				 &ais->type8.dac1fid13.tday,
311 				 &ais->type8.dac1fid13.thour,
312 				 &ais->type8.dac1fid13.tminute);
313 		}
314 		structured = true;
315 	    }
316 	    else if (strstr(buf, "\"fid\":15,") != NULL) {
317 		status = json_read_object(buf, json_ais8_fid15, endptr);
318 		structured = true;
319 	    }
320 	    else if (strstr(buf, "\"fid\":16,") != NULL) {
321 		status = json_read_object(buf, json_ais8_fid16, endptr);
322 		if (status == 0) {
323 			structured = true;
324 		}
325 	    }
326 	    else if (strstr(buf, "\"fid\":17,") != NULL) {
327 		status = json_read_object(buf, json_ais8_fid17, endptr);
328 		structured = true;
329 	    }
330 	    else if (strstr(buf, "\"fid\":19,") != NULL) {
331 		status = json_read_object(buf, json_ais8_fid19, endptr);
332 		structured = true;
333 	    }
334 	    else if (strstr(buf, "\"fid\":23,") != NULL) {
335 		status = json_read_object(buf, json_ais8_fid23, endptr);
336 		ais->type8.dac200fid23.start_year = AIS_YEAR_NOT_AVAILABLE;
337 		ais->type8.dac200fid23.start_month = AIS_MONTH_NOT_AVAILABLE;
338 		ais->type8.dac200fid23.start_day = AIS_DAY_NOT_AVAILABLE;
339 		ais->type8.dac200fid23.start_hour = AIS_HOUR_NOT_AVAILABLE;
340 		ais->type8.dac200fid23.start_minute = AIS_MINUTE_NOT_AVAILABLE;
341 		ais->type8.dac200fid23.end_year = AIS_YEAR_NOT_AVAILABLE;
342 		ais->type8.dac200fid23.end_month = AIS_MONTH_NOT_AVAILABLE;
343 		ais->type8.dac200fid23.end_day = AIS_DAY_NOT_AVAILABLE;
344 		ais->type8.dac200fid23.end_hour = AIS_HOUR_NOT_AVAILABLE;
345 		ais->type8.dac200fid23.end_minute = AIS_MINUTE_NOT_AVAILABLE;
346 		// cppcheck-suppress uninitvar
347 		(void)sscanf(start, "%09u-%02u-%02uT%02u:%02u",
348 			 &ais->type8.dac200fid23.start_year,
349 			 &ais->type8.dac200fid23.start_month,
350 			 &ais->type8.dac200fid23.start_day,
351 			 &ais->type8.dac200fid23.start_hour,
352 			 &ais->type8.dac200fid23.start_minute);
353 		// cppcheck-suppress uninitvar
354 		(void)sscanf(end, "%09u-%02u-%02uT%02u:%02u",
355 			 &ais->type8.dac200fid23.end_year,
356 			 &ais->type8.dac200fid23.end_month,
357 			 &ais->type8.dac200fid23.end_day,
358 			 &ais->type8.dac200fid23.end_hour,
359 			 &ais->type8.dac200fid23.end_minute);
360 		structured = true;
361 	    }
362 	    else if (strstr(buf, "\"fid\":24,") != NULL) {
363 		status = json_read_object(buf, json_ais8_fid24, endptr);
364 		structured = true;
365 	    }
366 	    else if (strstr(buf, "\"fid\":27,") != NULL) {
367 		status = json_read_object(buf, json_ais8_fid27, endptr);
368 		if (status == 0) {
369 		    ais->type8.dac1fid27.month = AIS_MONTH_NOT_AVAILABLE;
370 		    ais->type8.dac1fid27.day = AIS_DAY_NOT_AVAILABLE;
371 		    ais->type8.dac1fid27.hour = AIS_HOUR_NOT_AVAILABLE;
372 		    ais->type8.dac1fid27.minute = AIS_MINUTE_NOT_AVAILABLE;
373 		    // cppcheck-suppress uninitvar
374 		    (void)sscanf(start, "%02u-%02uT%02u:%02uZ",
375 				 &ais->type8.dac1fid27.month,
376 				 &ais->type8.dac1fid27.day,
377 				 &ais->type8.dac1fid27.hour,
378 				 &ais->type8.dac1fid27.minute);
379 		}
380 		structured = true;
381 	    }
382 	    else if (strstr(buf, "\"fid\":29,") != NULL) {
383 		status = json_read_object(buf, json_ais8_fid29, endptr);
384 		structured = true;
385 	    }
386 	    else if (strstr(buf, "\"fid\":31,") != NULL) {
387 		status = json_read_object(buf, json_ais8_fid31, endptr);
388 		if (status == 0) {
389 		    ais->type8.dac1fid31.day = AIS_DAY_NOT_AVAILABLE;
390 		    ais->type8.dac1fid31.hour = AIS_HOUR_NOT_AVAILABLE;
391 		    ais->type8.dac1fid31.minute = AIS_MINUTE_NOT_AVAILABLE;
392 		    // cppcheck-suppress uninitvar
393 		    (void)sscanf(timestamp, "%02uT%02u:%02uZ",
394 				 &ais->type8.dac1fid31.day,
395 				 &ais->type8.dac1fid31.hour,
396 				 &ais->type8.dac1fid31.minute);
397 		}
398 		structured = true;
399 	    }
400 	}
401 	else if (strstr(buf, "\"dac\":200,") != NULL && strstr(buf,"data")==NULL) {
402 	    if (strstr(buf, "\"fid\":10,") != NULL) {
403 		status = json_read_object(buf, json_ais8_fid10, endptr);
404 		structured = true;
405 	    }
406 	    if (strstr(buf, "\"fid\":40,") != NULL) {
407 		status = json_read_object(buf, json_ais8_fid40, endptr);
408 		structured = true;
409 	    }
410 	}
411 	if (!structured) {
412 	    status = json_read_object(buf, json_ais8, endptr);
413 	    if (status == 0)
414 		lenhex_unpack(data, &ais->type8.bitcount,
415 			      ais->type8.bitdata, sizeof(ais->type8.bitdata));
416 	}
417 	ais->type8.structured = structured;
418     } else if (strstr(buf, "\"type\":9,") != NULL) {
419 	status = json_read_object(buf, json_ais9, endptr);
420     } else if (strstr(buf, "\"type\":10,") != NULL) {
421 	status = json_read_object(buf, json_ais10, endptr);
422     } else if (strstr(buf, "\"type\":12,") != NULL) {
423 	status = json_read_object(buf, json_ais12, endptr);
424     } else if (strstr(buf, "\"type\":14,") != NULL) {
425 	status = json_read_object(buf, json_ais14, endptr);
426     } else if (strstr(buf, "\"type\":15,") != NULL) {
427 	status = json_read_object(buf, json_ais15, endptr);
428     } else if (strstr(buf, "\"type\":16,") != NULL) {
429 	status = json_read_object(buf, json_ais16, endptr);
430     } else if (strstr(buf, "\"type\":17,") != NULL) {
431 	status = json_read_object(buf, json_ais17, endptr);
432 	if (status == 0)
433 	    lenhex_unpack(data, &ais->type17.bitcount,
434 			  ais->type17.bitdata, sizeof(ais->type17.bitdata));
435     } else if (strstr(buf, "\"type\":18,") != NULL) {
436 	status = json_read_object(buf, json_ais18, endptr);
437     } else if (strstr(buf, "\"type\":19,") != NULL) {
438 	status = json_read_object(buf, json_ais19, endptr);
439     } else if (strstr(buf, "\"type\":20,") != NULL) {
440 	status = json_read_object(buf, json_ais20, endptr);
441     } else if (strstr(buf, "\"type\":21,") != NULL) {
442 	status = json_read_object(buf, json_ais21, endptr);
443     } else if (strstr(buf, "\"type\":22,") != NULL) {
444 	status = json_read_object(buf, json_ais22, endptr);
445     } else if (strstr(buf, "\"type\":23,") != NULL) {
446 	status = json_read_object(buf, json_ais23, endptr);
447     } else if (strstr(buf, "\"type\":24,") != NULL) {
448 	status = json_read_object(buf, json_ais24, endptr);
449     } else if (strstr(buf, "\"type\":25,") != NULL) {
450 	status = json_read_object(buf, json_ais25, endptr);
451 	if (status == 0)
452 	    lenhex_unpack(data, &ais->type25.bitcount,
453 			  ais->type25.bitdata, sizeof(ais->type25.bitdata));
454     } else if (strstr(buf, "\"type\":26,") != NULL) {
455 	status = json_read_object(buf, json_ais26, endptr);
456 	if (status == 0)
457 	    lenhex_unpack(data, &ais->type26.bitcount,
458 			  ais->type26.bitdata, sizeof(ais->type26.bitdata));
459     } else if (strstr(buf, "\"type\":27,") != NULL) {
460 	status = json_read_object(buf, json_ais27, endptr);
461     } else {
462 	if (endptr != NULL)
463 	    *endptr = NULL;
464 	return JSON_ERR_MISC;
465     }
466     return status;
467 }
468 #endif /* SOCKET_EXPORT_ENABLE */
469 
470 /* ais_json.c ends here */
471