1 /*
2     OWFS -- One-Wire filesystem
3     OWHTTPD -- One-Wire Web Server
4     Written 2003 Paul H Alfille
5     email: paul.alfille@gmail.com
6     Released under the GPL
7     See the header file: ow.h for full attribution
8     1wire/iButton system from Dallas Semiconductor
9 $ID: $
10 */
11 
12 #include <config.h>
13 #include "owfs_config.h"
14 #include "ow.h"
15 #include "ow_counters.h"
16 #include "ow_connection.h"
17 
18 /* ------- Prototypes ----------- */
19 static SIZE_OR_ERROR OWQ_parse_output_integer(struct one_wire_query *owq);
20 static SIZE_OR_ERROR OWQ_parse_output_unsigned(struct one_wire_query *owq);
21 static SIZE_OR_ERROR OWQ_parse_output_float(struct one_wire_query *owq);
22 static SIZE_OR_ERROR OWQ_parse_output_date(struct one_wire_query *owq);
23 static SIZE_OR_ERROR OWQ_parse_output_yesno(struct one_wire_query *owq);
24 static SIZE_OR_ERROR OWQ_parse_output_ascii(struct one_wire_query *owq);
25 static SIZE_OR_ERROR OWQ_parse_output_array_with_commas(struct one_wire_query *owq);
26 static SIZE_OR_ERROR OWQ_parse_output_array_no_commas(struct one_wire_query *owq);
27 static SIZE_OR_ERROR OWQ_parse_output_ascii_array(struct one_wire_query *owq);
28 static SIZE_OR_ERROR OWQ_parse_output_offset_and_size_z(const char *string, struct one_wire_query *owq) ;
29 static SIZE_OR_ERROR OWQ_parse_output_offset_and_size(const char *string, size_t length, struct one_wire_query *owq) ;
30 
31 /*
32 Change in strategy 6/2006:
33 Now use CheckPresence as primary method of finding correct bus
34 Can break down cases into:
35 1. bad ParsedName -- no read possible
36 2. structure -- read from 1st bus (local)
37 3. specified bus (picked up in ParsedName) -- use that
38 4. statistics, settings, Simultaneous, Thermostat -- use first or specified
39 5. real -- use caced, if error, delete cache entry and try twice more.
40 */
41 
42 /* ---------------------------------------------- */
43 /* Filesystem callback functions                  */
44 /* ---------------------------------------------- */
45 
OWQ_parse_output(struct one_wire_query * owq)46 SIZE_OR_ERROR OWQ_parse_output(struct one_wire_query *owq)
47 {
48 	// have to check if offset is beyond the filesize.
49 	if (OWQ_offset(owq)) {
50 		size_t file_length = 0;
51 		file_length = FileLength(PN(owq));
52 		LEVEL_DEBUG("file_length=%lu offset=%lu size=%lu",
53 					(unsigned long) file_length, (unsigned long) OWQ_offset(owq), (unsigned long) OWQ_size(owq));
54 		if ((unsigned long) OWQ_offset(owq) >= (unsigned long) file_length) {
55 			return 0;			// This is data-length
56 		}
57 	}
58 
59 	/* Special case, structure is always ascii */
60 	if (OWQ_pn(owq).type == ePN_structure) {
61 		return OWQ_parse_output_ascii(owq);
62 	}
63 
64 	switch (OWQ_pn(owq).extension) {
65 	case EXTENSION_BYTE:
66 		return OWQ_parse_output_unsigned(owq);
67 	case EXTENSION_ALL:
68 		switch (OWQ_pn(owq).selected_filetype->format) {
69 		case ft_ascii:
70 		case ft_vascii:
71 		case ft_alias:
72 			return OWQ_parse_output_ascii_array(owq);
73 		case ft_binary:
74 			return OWQ_parse_output_array_no_commas(owq);
75 		default:
76 			return OWQ_parse_output_array_with_commas(owq);
77 		}
78 	default:
79 		switch (OWQ_pn(owq).selected_filetype->format) {
80 		case ft_integer:
81 			return OWQ_parse_output_integer(owq);
82 		case ft_yesno:
83 		case ft_bitfield:
84 			return OWQ_parse_output_yesno(owq);
85 		case ft_unsigned:
86 			return OWQ_parse_output_unsigned(owq);
87 		case ft_pressure:
88 		case ft_temperature:
89 		case ft_tempgap:
90 		case ft_float:
91 			return OWQ_parse_output_float(owq);
92 		case ft_date:
93 			return OWQ_parse_output_date(owq);
94 		case ft_vascii:
95 		case ft_alias:
96 		case ft_ascii:
97 		case ft_binary:
98 			return OWQ_parse_output_ascii(owq);
99 		case ft_directory:
100 		case ft_subdir:
101 		case ft_unknown:
102 			return -ENOENT;
103 		}
104 	}
105 	return -EINVAL;				// should never be reached if all the cases are truly covered
106 }
107 
OWQ_parse_output_integer(struct one_wire_query * owq)108 static SIZE_OR_ERROR OWQ_parse_output_integer(struct one_wire_query *owq)
109 {
110 	/* should only need suglen+1, but uClibc's snprintf()
111 	   seem to trash 'len' if not increased */
112 	int len;
113 	char c[PROPERTY_LENGTH_INTEGER + 2];
114 
115 	UCLIBCLOCK;
116 	if ( ShouldTrim(PN(owq)) ) {
117 		len = snprintf(c, PROPERTY_LENGTH_INTEGER + 1, "%1d", OWQ_I(owq));
118 	} else {
119 		len = snprintf(c, PROPERTY_LENGTH_INTEGER + 1, "%*d", PROPERTY_LENGTH_INTEGER, OWQ_I(owq));
120 	}
121 	UCLIBCUNLOCK;
122 	if ((len < 0) || ((size_t) len > PROPERTY_LENGTH_INTEGER)) {
123 		return -EMSGSIZE;
124 	}
125 	return OWQ_parse_output_offset_and_size(c, len, owq);
126 }
127 
OWQ_parse_output_unsigned(struct one_wire_query * owq)128 static SIZE_OR_ERROR OWQ_parse_output_unsigned(struct one_wire_query *owq)
129 {
130 	/* should only need suglen+1, but uClibc's snprintf()
131 	   seem to trash 'len' if not increased */
132 	int len;
133 	char c[PROPERTY_LENGTH_UNSIGNED + 2];
134 
135 	UCLIBCLOCK;
136 	if ( ShouldTrim(PN(owq)) ) {
137 		len = snprintf(c, PROPERTY_LENGTH_UNSIGNED + 1, "%1u", OWQ_U(owq));
138 	} else {
139 		len = snprintf(c, PROPERTY_LENGTH_UNSIGNED + 1, "%*u", PROPERTY_LENGTH_UNSIGNED, OWQ_U(owq));
140 	}
141 	UCLIBCUNLOCK;
142 	if ((len < 0) || ((size_t) len > PROPERTY_LENGTH_UNSIGNED)) {
143 		return -EMSGSIZE;
144 	}
145 	return OWQ_parse_output_offset_and_size(c, len, owq);
146 }
147 
OWQ_parse_output_float(struct one_wire_query * owq)148 static SIZE_OR_ERROR OWQ_parse_output_float(struct one_wire_query *owq)
149 {
150 	/* should only need suglen+1, but uClibc's snprintf()
151 	   seem to trash 'len' if not increased */
152 	int len;
153 	char c[PROPERTY_LENGTH_FLOAT + 2];
154 	_FLOAT F;
155 
156 	switch (OWQ_pn(owq).selected_filetype->format) {
157 	case ft_pressure:
158 		F = Pressure(OWQ_F(owq), PN(owq));
159 		break;
160 	case ft_temperature:
161 		F = Temperature(OWQ_F(owq), PN(owq));
162 		break;
163 	case ft_tempgap:
164 		F = TemperatureGap(OWQ_F(owq), PN(owq));
165 		break;
166 	default:
167 		F = OWQ_F(owq);
168 		break;
169 	}
170 
171 	UCLIBCLOCK;
172 	if ( ShouldTrim(PN(owq)) ) {
173 		len = snprintf(c, PROPERTY_LENGTH_FLOAT + 1, "%1G", F);
174 	} else {
175 		len = snprintf(c, PROPERTY_LENGTH_FLOAT + 1, "%*G", PROPERTY_LENGTH_FLOAT, F);
176 	}
177 	UCLIBCUNLOCK;
178 	if ((len < 0) || ((size_t) len > PROPERTY_LENGTH_FLOAT)) {
179 		return -EMSGSIZE;
180 	}
181 	return OWQ_parse_output_offset_and_size(c, len, owq);
182 }
183 
OWQ_parse_output_date(struct one_wire_query * owq)184 static SIZE_OR_ERROR OWQ_parse_output_date(struct one_wire_query *owq)
185 {
186 	char c[PROPERTY_LENGTH_DATE + 2];
187 	if (OWQ_size(owq) < PROPERTY_LENGTH_DATE) {
188 		return -EMSGSIZE;
189 	}
190 	ctime_r(&OWQ_D(owq), c);
191 	return OWQ_parse_output_offset_and_size(c, PROPERTY_LENGTH_DATE, owq);
192 }
193 
OWQ_parse_output_yesno(struct one_wire_query * owq)194 static SIZE_OR_ERROR OWQ_parse_output_yesno(struct one_wire_query *owq)
195 {
196 	if (OWQ_size(owq) < PROPERTY_LENGTH_YESNO) {
197 		return -EMSGSIZE;
198 	}
199 	OWQ_buffer(owq)[0] = ((OWQ_Y(owq) & 0x1) == 0) ? '0' : '1';
200 	return ShouldTrim(PN(owq))? 1 : PROPERTY_LENGTH_YESNO;
201 }
202 
OWQ_format_output_offset_and_size_z(const char * string,struct one_wire_query * owq)203 ZERO_OR_ERROR OWQ_format_output_offset_and_size_z(const char *string, struct one_wire_query *owq)
204 {
205 	SIZE_OR_ERROR ret = OWQ_parse_output_offset_and_size_z(string,owq) ;
206 	if ( ret > 0 ) {
207 		return 0 ;
208 	}
209 	return ret ;
210 }
211 
OWQ_format_output_offset_and_size(const char * string,size_t length,struct one_wire_query * owq)212 ZERO_OR_ERROR OWQ_format_output_offset_and_size(const char *string, size_t length, struct one_wire_query *owq)
213 {
214 	SIZE_OR_ERROR ret = OWQ_parse_output_offset_and_size(string,length,owq) ;
215 	if ( ret > 0 ) {
216 		return 0 ;
217 	}
218 	return ret ;
219 }
220 
OWQ_parse_output_offset_and_size_z(const char * string,struct one_wire_query * owq)221 static SIZE_OR_ERROR OWQ_parse_output_offset_and_size_z(const char *string, struct one_wire_query *owq)
222 {
223 	return OWQ_parse_output_offset_and_size(string, strlen(string), owq);
224 }
225 
226 /* Put a string ionto the OWQ structure and return the length
227    check lengths and offsets as part of the process */
OWQ_parse_output_offset_and_size(const char * string,size_t length,struct one_wire_query * owq)228 static SIZE_OR_ERROR OWQ_parse_output_offset_and_size(const char *string, size_t length, struct one_wire_query *owq)
229 {
230 	size_t copy_length = length;
231 	off_t offset = OWQ_offset(owq);
232 	Debug_Bytes("OWQ_parse_output_offset_and_size", (const BYTE *) string, length);
233 
234 	/* offset is after the length of the string -- return 0 since
235 	   some conditions a read after the end is done automatically */
236 	if (offset > (off_t) length) {
237 		return 0;
238 	}
239 
240 	/* correct length for offset (cannot be negative because of previous check) */
241 	copy_length -= offset;
242 
243 	/* correct length for buffer space */
244 	if (copy_length > OWQ_size(owq)) {
245 		copy_length = OWQ_size(owq);
246 	}
247 
248 	/* and copy */
249 	memcpy(OWQ_buffer(owq), &string[offset], copy_length);
250 
251 	// Warning, this will overwrite the I U or DATA value,
252 	// but that shouldn't matter since it's only called on ascii values
253 	// and all structure values
254 	OWQ_length(owq) = copy_length;
255 
256 	return copy_length;
257 }
258 
OWQ_parse_output_ascii(struct one_wire_query * owq)259 static SIZE_OR_ERROR OWQ_parse_output_ascii(struct one_wire_query *owq)
260 {
261 	Debug_OWQ(owq);
262 	return OWQ_length(owq);
263 }
264 
OWQ_parse_output_array_with_commas(struct one_wire_query * owq)265 static SIZE_OR_ERROR OWQ_parse_output_array_with_commas(struct one_wire_query *owq)
266 {
267 	struct one_wire_query owq_single;
268 	size_t extension;
269 	int len;
270 	size_t used_size = 0;
271 	size_t remaining_size = OWQ_size(owq);
272 	size_t elements = OWQ_pn(owq).selected_filetype->ag->elements;
273 
274 	// loop though all array elements
275 	for (extension = 0; extension < elements; ++extension) {
276 		//printf("OWQ_parse_output_array_with_commas element=%d, size_used=%d, remaining=%d\n",(int)extension,(int)used_size,(int)remaining_size) ;
277 		// Prepare a copy of owq that only points to a single element
278 		memcpy(&owq_single, owq, sizeof(owq_single));
279 		OWQ_pn(&owq_single).extension = extension;
280 		memcpy(&OWQ_val(&owq_single), &OWQ_array(owq)[extension], sizeof(union value_object));
281 		// add the comma first (if not the first element and enough room)
282 		if (used_size > 0) {
283 			if (remaining_size == 0) {
284 				return -EFAULT;
285 			}
286 			OWQ_buffer(owq)[used_size] = ',';
287 			++used_size;
288 			--remaining_size;
289 		}
290 		// Now process the single element
291 		OWQ_buffer(&owq_single) = &OWQ_buffer(owq)[used_size];
292 		OWQ_size(&owq_single) = remaining_size;
293 		len = OWQ_parse_output(&owq_single);
294 		// any error aborts
295 		if (len < 0) {
296 			return len;
297 		}
298 		remaining_size -= len;
299 		used_size += len;
300 	}
301 	return used_size;
302 }
303 
OWQ_parse_output_ascii_array(struct one_wire_query * owq)304 static SIZE_OR_ERROR OWQ_parse_output_ascii_array(struct one_wire_query *owq)
305 {
306 	size_t extension;
307 	size_t elements = OWQ_pn(owq).selected_filetype->ag->elements;
308 	size_t total_length = 0;
309 	size_t current_offset = OWQ_array_length(owq, 0);
310 
311 	for (extension = 0; extension < elements; ++extension) {
312 		total_length += OWQ_array_length(owq, extension);
313 	}
314 
315 	for (extension = 1; extension < elements; ++extension) {
316 		memmove(&OWQ_buffer(owq)[current_offset + 1], &OWQ_buffer(owq)[current_offset], total_length - current_offset);
317 		OWQ_buffer(owq)[current_offset] = ',';
318 		++total_length;
319 		current_offset += 1 + OWQ_array_length(owq, extension);
320 	}
321 
322 	return total_length;
323 }
324 
OWQ_parse_output_array_no_commas(struct one_wire_query * owq)325 static SIZE_OR_ERROR OWQ_parse_output_array_no_commas(struct one_wire_query *owq)
326 {
327 	size_t extension;
328 	size_t total_length = 0;
329 	size_t elements = OWQ_pn(owq).selected_filetype->ag->elements;
330 
331 	for (extension = 0; extension < elements; ++extension) {
332 		total_length += OWQ_array_length(owq, extension);
333 	}
334 	return total_length;
335 }
336