1 /***********************************************************
2 * netcam_http.c
3 * Process network camera images using http protocol
4 * This code was inspired by the original netcam.c module
5 * written by Jeroen Vreeken and enhanced by several Motion
6 * project contributors, particularly Angel Carpintero and
7 * Christopher Price.
8 *
9 * Copyright 2005, William M. Brack
10 * This software is distributed under the GNU Public license
11 * Version 2. See also the file 'COPYING'.
12 ***********************************************************/
13
14 #include "translate.h"
15 #include "motion.h" /* Needs to come first, because _GNU_SOURCE_ set there. */
16 #include "netcam_http.h"
17
18 #define CONNECT_TIMEOUT 10 /* Timeout on remote connection attempt */
19 #define READ_TIMEOUT 5 /* Default timeout on recv requests */
20 #define POLLING_TIMEOUT READ_TIMEOUT /* File polling timeout [s] */
21 #define POLLING_TIME 500*1000*1000 /* File polling time quantum [ns] (500ms) */
22 #define MAX_HEADER_RETRIES 5 /* Max tries to find a header record */
23 #define MINVAL(x, y) ((x) < (y) ? (x) : (y))
24
25 /* These strings are used for the HTTP connection. */
26 static const char *connect_req;
27
28 #define connect_req_http10 "GET %s HTTP/1.0\r\n" \
29 "Host: %s\r\n" \
30 "User-Agent: Motion-netcam/" VERSION "\r\n"
31
32 #define connect_req_http11 "GET %s HTTP/1.1\r\n" \
33 "Host: %s\r\n" \
34 "User-Agent: Motion-netcam/" VERSION "\r\n"
35
36 #define connect_req_close "Connection: close\r\n"
37
38 #define connect_req_keepalive "Connection: Keep-Alive\r\n"
39
40 #define connect_auth_req "Authorization: Basic %s\r\n"
41
42 tfile_context *file_new_context(void);
43 void file_free_context(tfile_context* ctxt);
44
45
46 /**
47 * check_quote
48 *
49 * Checks a string to see if it's quoted, and if so removes the
50 * quotes.
51 *
52 * Parameters:
53 *
54 * str Pointer to a string.
55 *
56 * Returns: Nothing, but updates the target if necessary.
57 *
58 */
check_quote(char * str)59 static void check_quote(char *str)
60 {
61 int len;
62 char ch;
63
64 ch = *str;
65
66 if ((ch == '"') || (ch == '\'')) {
67 len = strlen(str) - 1;
68 if (str[len] == ch) {
69 memmove(str, str+1, len-1);
70 str[len-1] = 0;
71 }
72 }
73 }
74
75 /**
76 * netcam_check_content_length
77 *
78 * Analyse an HTTP-header line to see if it is a Content-length.
79 *
80 * Parameters:
81 *
82 * header Pointer to a string containing the header line.
83 *
84 * Returns:
85 * -1 Not a Content-length line.
86 * >=0 Value of Content-length field.
87 *
88 */
netcam_check_content_length(char * header)89 static long netcam_check_content_length(char *header)
90 {
91 long length = -1; /* Note this is a long, not an int. */
92
93 if (!header_process(header, "Content-Length", header_extract_number, &length)) {
94 /*
95 * Some netcams deliver some bad-format data, but if
96 * we were able to recognize the header section and the
97 * number we might as well try to use it.
98 */
99 if (length > 0)
100 MOTION_LOG(WRN, TYPE_NETCAM, NO_ERRNO
101 ,_("malformed token Content-Length but value %ld"), length);
102 }
103
104 MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO,_("Content-Length %ld"), length);
105
106 return length;
107 }
108
109 /**
110 * netcam_check_keepalive
111 *
112 * Analyse an HTTP-header line to see if it is a Keep-Alive.
113 *
114 * Parameters:
115 *
116 * header Pointer to a string containing the header line.
117 *
118 * Returns:
119 * -1 Not a Keep-Alive line.
120 * 1 Is a Keep-Alive line.
121 *
122 */
netcam_check_keepalive(char * header)123 static int netcam_check_keepalive(char *header)
124 {
125 char *content_type = NULL;
126
127 if (!header_process(header, "Keep-Alive", http_process_type, &content_type))
128 return -1;
129
130 /* We do not detect the second field or other case mixes at present. */
131 free(content_type);
132
133 return 1;
134 }
135
136 /**
137 * netcam_check_close
138 *
139 * Analyse an HTTP-header line to see if it is a Connection: close.
140 *
141 * Parameters:
142 *
143 * header Pointer to a string containing the header line.
144 *
145 * Returns:
146 * -1 Not a Connection: close.
147 * 1 Is a Connection: close.
148 *
149 */
netcam_check_close(char * header)150 static int netcam_check_close(char *header)
151 {
152 char *type = NULL;
153 int ret = -1;
154
155 if (!header_process(header, "Connection", http_process_type, &type))
156 return -1;
157
158 if (!strcmp(type, "close")) /* strcmp returns 0 for match. */
159 ret = 1;
160
161 free(type);
162
163 return ret;
164 }
165
166 /**
167 * netcam_check_content_type
168 *
169 * Analyse an HTTP-header line to see if it is a Content-type.
170 *
171 * Parameters:
172 *
173 * header Pointer to a string containing the header line.
174 *
175 * Returns:
176 * -1 Not a Content-type line
177 * 0 Content-type not recognized
178 * 1 image/jpeg
179 * 2 multipart/x-mixed-replace or multipart/mixed
180 * 3 application/octet-stream (used by WVC200 Linksys IP Camera)
181 *
182 */
netcam_check_content_type(char * header)183 static int netcam_check_content_type(char *header)
184 {
185 char *content_type = NULL;
186 int ret;
187
188 if (!header_process(header, "Content-type", http_process_type, &content_type))
189 return -1;
190
191 MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO,_("Content-type %s"), content_type);
192
193 if (!strcmp(content_type, "image/jpeg")) {
194 ret = 1;
195 } else if (!strcmp(content_type, "multipart/x-mixed-replace") ||
196 !strcmp(content_type, "multipart/mixed")) {
197 ret = 2;
198 } else if (!strcmp(content_type, "application/octet-stream")) {
199 ret = 3;
200 } else {
201 ret = 0;
202 }
203
204 free(content_type);
205
206 return ret;
207 }
208
209 /**
210 * netcam_read_next_header
211 *
212 * Read the next header record from the camera.
213 *
214 * Parameters
215 *
216 * netcam pointer to a netcam_context.
217 *
218 * Returns: 0 for success, -1 if any error.
219 *
220 */
netcam_read_next_header(netcam_context_ptr netcam)221 int netcam_read_next_header(netcam_context_ptr netcam)
222 {
223 int retval;
224 char *header;
225
226 /* Return if not connected */
227 if (netcam->sock == -1)
228 return -1;
229 /*
230 * We are expecting a header which *must* contain a mime-type of
231 * image/jpeg, and *might* contain a Content-Length.
232 *
233 * If this is a "streaming" camera, the header *must* be preceded
234 * by a "boundary" string.
235 *
236 */
237 netcam->caps.content_length = 0;
238
239 /*
240 * If this is a "streaming" camera, the stream header must be
241 * preceded by a "boundary" string.
242 */
243 if (netcam->caps.streaming == NCS_MULTIPART) {
244 while (1) {
245 retval = header_get(netcam, &header, HG_NONE);
246
247 if (retval != HG_OK) {
248 /* Header reported as not-OK, check to see if it's null. */
249 if (strlen(header) == 0) {
250 MOTION_LOG(WRN, TYPE_NETCAM, NO_ERRNO
251 ,_("Error reading image header, streaming mode (1). Null header."));
252 } else {
253 /* Header is not null. Output it in case it's a new camera with unknown headers. */
254 MOTION_LOG(WRN, TYPE_NETCAM, NO_ERRNO
255 ,_("Error reading image header, streaming mode (1). Unknown header '%s'")
256 ,header);
257 }
258
259 free(header);
260 return -1;
261 }
262
263 retval = (strstr(header, netcam->boundary) == NULL);
264 free(header);
265
266 if (!retval)
267 break;
268 }
269 }
270
271 while (1) {
272 retval = header_get(netcam, &header, HG_NONE);
273
274 if (retval != HG_OK) {
275 MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO,_("Error reading image header (2)"));
276 free(header);
277 return -1;
278 }
279
280 if (*header == 0)
281 break;
282
283 if ((retval = netcam_check_content_type(header)) >= 0) {
284 if (retval != 1) {
285 MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO,_("Header not JPEG"));
286 free(header);
287 return -1;
288 }
289 }
290
291 if ((retval = (int) netcam_check_content_length(header)) >= 0) {
292 if (retval > 0) {
293 netcam->caps.content_length = 1; /* Set flag */
294 netcam->receiving->content_length = retval;
295 } else {
296 netcam->receiving->content_length = 0;
297 MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO,_("Content-Length 0"));
298 free(header);
299 return -1;
300 }
301 }
302
303 free(header);
304 }
305
306 MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO,_("Found image header record"));
307
308 free(header);
309 return 0;
310 }
311
312 /**
313 * netcam_read_first_header
314 *
315 * This routine attempts to read a header record from the netcam. If
316 * successful, it analyses the header to determine whether the camera is
317 * a "streaming" type. If it is, the routine looks for the Boundary-string;
318 * if found, it positions just past the string so that the image header can
319 * be read. It then reads the image header and continues processing that
320 * header as well.
321 *
322 * If the camera does not appear to be a streaming type, it is assumed that the
323 * header just read was the image header. It is processed to determine whether
324 * a Content-length is present.
325 *
326 * After this processing, the routine returns to the caller.
327 *
328 * Parameters:
329 * netcam Pointer to the netcam_context structure.
330 *
331 * Returns: Content-type code if successful, -1 if not
332 * -2 if Content-length = 0
333 */
netcam_read_first_header(netcam_context_ptr netcam)334 int netcam_read_first_header(netcam_context_ptr netcam)
335 {
336 int retval = -3; /* "Unknown err" */
337 int ret;
338 int firstflag = 1;
339 int aliveflag = 0; /* If we have seen a Keep-Alive header from cam. */
340 int closeflag = 0; /* If we have seen a Connection: close header from cam. */
341 char *header;
342 char *boundary;
343
344 /* Send the initial command to the camera. */
345 if (send(netcam->sock, netcam->connect_request,
346 strlen(netcam->connect_request), 0) < 0) {
347 MOTION_LOG(ERR, TYPE_NETCAM, SHOW_ERRNO
348 ,_("Error sending 'connect' request"));
349 return -1;
350 }
351
352 /*
353 * We expect to get back an HTTP header from the camera.
354 * Successive calls to header_get will return each line
355 * of the header received. We will continue reading until
356 * a blank line is received.
357 *
358 * As we process the header, we are looking for either of
359 * header lines Content-type or Content-length. Content-type
360 * is used to determine whether the camera is "streaming" or
361 * "non-streaming", and Content-length will be used to determine
362 * whether future reads of images will be controlled by the
363 * length specified before the image, or by a boundary string.
364 *
365 * The Content-length will only be present "just before" an
366 * image is sent (if it is present at all). That means that, if
367 * this is a "streaming" camera, it will not be present in the
368 * "first header", but will occur later (after a boundary-string).
369 * For a non-streaming camera, however, there is no boundary-string,
370 * and the first header is, in fact, the only header. In this case,
371 * there may be a Content-length.
372 *
373 */
374 while (1) { /* 'Do forever' */
375 ret = header_get(netcam, &header, HG_NONE);
376
377 MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO,_("Received first header ('%s')"), header);
378
379 if (ret != HG_OK) {
380 MOTION_LOG(WRN, TYPE_NETCAM, NO_ERRNO
381 ,_("Error reading first header (%s)"), header);
382 free(header);
383 return -1;
384 }
385
386 if (firstflag) {
387 if ((ret = http_result_code(header)) != 200) {
388 MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO,_("HTTP Result code %d"), ret);
389
390 free(header);
391 if (netcam->connect_keepalive) {
392 /*
393 * Cannot unset netcam->cnt->conf.netcam_keepalive as it is assigned const
394 * But we do unset the netcam keepalive flag which was set in netcam_start
395 * This message is logged as Information as it would be useful to know
396 * if your netcam often returns bad HTTP result codes.
397 */
398 netcam->connect_keepalive = FALSE;
399 free((void *)netcam->cnt->conf.netcam_keepalive);
400 netcam->cnt->conf.netcam_keepalive = strdup("off");
401 MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO
402 ,_("Removed netcam Keep-Alive flag "
403 "due to apparent closed HTTP connection."));
404 }
405 return ret;
406 }
407 firstflag = 0;
408 free(header);
409 continue;
410 }
411
412 if (*header == 0) /* Blank line received */
413 break;
414
415 /* Check if this line is the content type. */
416 if ((ret = netcam_check_content_type(header)) >= 0) {
417 retval = ret;
418 /*
419 * We are expecting to find one of three types:
420 * 'multipart/x-mixed-replace', 'multipart/mixed'
421 * or 'image/jpeg'. The first two will be received
422 * from a streaming camera, and the third from a
423 * camera which provides a single frame only.
424 */
425 switch (ret) {
426 case 1: /* Not streaming */
427 if (netcam->connect_keepalive)
428 MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO
429 ,_("Non-streaming camera (keep-alive set)"));
430 else
431 MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO
432 ,_("Non-streaming camera (keep-alive not set)"));
433
434 netcam->caps.streaming = NCS_UNSUPPORTED;
435 break;
436
437 case 2: /* Streaming */
438 MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO,_("Streaming camera"));
439
440 netcam->caps.streaming = NCS_MULTIPART;
441
442 if ((boundary = strstr(header, "boundary="))) {
443 /* On error recovery this may already be set. */
444 free(netcam->boundary);
445
446 netcam->boundary = mystrdup(boundary + 9);
447 /*
448 * HTTP protocol apparently permits the boundary string
449 * to be quoted (the Lumenera does this, which caused
450 * trouble) so we need to get rid of any surrounding
451 * quotes.
452 */
453 check_quote(netcam->boundary);
454 netcam->boundary_length = strlen(netcam->boundary);
455
456 MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO
457 ,_("Boundary string [%s]"), netcam->boundary);
458 } else {
459 MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO
460 ,_("Boundary string not found in header"));
461 free(header);
462 return -1;
463 }
464 break;
465 case 3: /* MJPG-Block style streaming. */
466 MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO
467 ,_("Streaming camera probably using MJPG-blocks,"
468 " consider using mjpg:// netcam_url."));
469 break;
470
471 default:
472 /* Error */
473 MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO,_("Unrecognized content type"));
474 free(header);
475 return -1;
476
477 }
478 } else if ((ret = (int) netcam_check_content_length(header)) >= 0) {
479 MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO,_("Content-length present"));
480
481 if (ret > 0) {
482 netcam->caps.content_length = 1; /* Set flag */
483 netcam->receiving->content_length = ret;
484 } else {
485 netcam->receiving->content_length = 0;
486 MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO,_("Content-length 0"));
487 retval = -2;
488 }
489 } else if (netcam_check_keepalive(header) == TRUE) {
490 /* Note that we have received a Keep-Alive header, and thus the socket can be left open. */
491 aliveflag = TRUE;
492 netcam->keepalive_thisconn = TRUE;
493 /*
494 * This flag will not be set when a Streaming cam is in use, but that
495 * does not matter as the test below looks at Streaming state also.
496 */
497 } else if (netcam_check_close(header) == TRUE) {
498 /* Note that we have received a Connection: close header. */
499 closeflag = TRUE;
500 /*
501 * This flag is acted upon below.
502 * Changed criterion and moved up from below to catch headers that cause returns.
503 */
504 MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO
505 ,_("Found Conn: close header ('%s')"), header);
506 }
507 free(header);
508 }
509 free(header);
510
511 if (netcam->caps.streaming == NCS_UNSUPPORTED && netcam->connect_keepalive) {
512
513 /* If we are a non-streaming (ie. Jpeg) netcam and keepalive is configured. */
514
515 if (aliveflag) {
516 if (closeflag) {
517 netcam->warning_count++;
518 if (netcam->warning_count > 3) {
519 netcam->warning_count = 0;
520 MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO
521 ,_("Both 'Connection: Keep-Alive' and "
522 "'Connection: close' header received. Motion removes keepalive."));
523 netcam->connect_keepalive = FALSE;
524 free((void *)netcam->cnt->conf.netcam_keepalive);
525 netcam->cnt->conf.netcam_keepalive = strdup("off");
526 } else {
527 /*
528 * If not a streaming cam, and keepalive is set, and the flag shows we
529 * did not see a Keep-Alive field returned from netcam and a Close field.
530 * Not quite sure what the correct course of action is here. In for testing.
531 */
532 MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO
533 ,_("Both 'Connection: Keep-Alive' and "
534 "'Connection: close' header received. Motion continues unchanged."));
535 }
536 } else {
537 /*
538 * aliveflag && !closeflag
539 *
540 * If not a streaming cam, and keepalive is set, and the flag shows we
541 * just got a Keep-Alive field returned from netcam and no Close field.
542 * No action, as this is the normal case. In debug we print a notification.
543 */
544
545 MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO
546 ,_("Received a Keep-Alive field in this set of headers."));
547 }
548 } else { /* !aliveflag */
549 if (!closeflag) {
550 netcam->warning_count++;
551
552 if (netcam->warning_count > 3) {
553 netcam->warning_count = 0;
554 MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO
555 ,_("No 'Connection: Keep-Alive' nor 'Connection: close'"
556 " header received.\n Motion removes keepalive."));
557 netcam->connect_keepalive = FALSE;
558 free((void *)netcam->cnt->conf.netcam_keepalive);
559 netcam->cnt->conf.netcam_keepalive = strdup("off");
560 } else {
561 /*
562 * If not a streaming cam, and keepalive is set, and the flag shows we
563 * did not see a Keep-Alive field returned from netcam nor a Close field.
564 * Not quite sure what the correct course of action is here. In for testing.
565 */
566 MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO
567 ,_("No 'Connection: Keep-Alive' nor 'Connection: close'"
568 " header received.\n Motion continues unchanged."));
569 }
570 } else {
571 /*
572 * !aliveflag & closeflag
573 * If not a streaming cam, and keepalive is set, and the flag shows we
574 * received a 'Connection: close' field returned from netcam. It is not likely
575 * we will get a Keep-Alive and Close header together - this is picked up by
576 * the test code above.
577 * If we receive a Close header, then we want to cease keep-alive for this cam.
578 * This situation will occur in 2 situations:
579 * (a) in HTTP 1.1 when the client wants to stop the keep-alive
580 * (and in this case it would be correct to close connection and then
581 * make a new one, with keep-alive set again).
582 * (b) in HTTP 1.0 with keepalive, when the client does not support it.
583 * In this case we should not attempt to re-start Keep-Alive.
584 * Due to that, we accept a Connection: close header in HTTP 1.0 & 1.1 modes
585 *
586 * To tell between the sitation where a camera has been in Keep-Alive mode and
587 * is now finishing (and will want to be re-started in Keep-Alive) and the other
588 * case when a cam does not support it, we have a flag which says if the netcam
589 * has returned a Keep-Alive flag during this connection. If that's set, we
590 * set ourselves up to re-connect with Keep-Alive after the socket is closed.
591 * If it's not set, then we will not try again to use Keep-Alive.
592 */
593 if (!netcam->keepalive_thisconn) {
594 netcam->connect_keepalive = FALSE; /* No further attempts at keep-alive */
595 free((void *)netcam->cnt->conf.netcam_keepalive);
596 netcam->cnt->conf.netcam_keepalive = strdup("off");
597 MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO
598 ,_("Removed netcam Keep-Alive flag because"
599 " 'Connection: close' header received.\n Netcam does not support "
600 "Keep-Alive. Motion continues in non-Keep-Alive."));
601 } else {
602 netcam->keepalive_timeup = TRUE; /* We will close and re-open keep-alive */
603 MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO
604 ,_("Keep-Alive has reached end of valid period.\n"
605 "Motion will close netcam, then resume Keep-Alive with a new socket."));
606 }
607 }
608 }
609 }
610
611 return retval;
612 }
613
614 /**
615 * netcam_disconnect
616 *
617 * Disconnect from the network camera.
618 *
619 * Parameters:
620 *
621 * netcam pointer to netcam context
622 *
623 * Returns: Nothing
624 *
625 */
netcam_disconnect(netcam_context_ptr netcam)626 void netcam_disconnect(netcam_context_ptr netcam)
627 {
628 if (netcam->sock > 0) {
629 if (close(netcam->sock) < 0)
630 MOTION_LOG(ERR, TYPE_NETCAM, SHOW_ERRNO, _("disconnect"));
631
632 netcam->sock = -1;
633 }
634 }
635
636 /**
637 * netcam_connect
638 *
639 * Attempt to open the network camera as a stream device.
640 * Keep-alive is supported, ie. if netcam->connect_keepalive is TRUE, we
641 * re-use netcam->sock unless it has value -1, meaning it is invalid.
642 *
643 * Parameters:
644 *
645 * netcam pointer to netcam_context structure
646 * err_flag flag to suppress error printout (1 => suppress)
647 * Note that errors which indicate something other than
648 * a network connection problem are not suppressed.
649 *
650 * Returns: 0 for success, -1 for error
651 *
652 */
netcam_connect(netcam_context_ptr netcam,int err_flag)653 int netcam_connect(netcam_context_ptr netcam, int err_flag)
654 {
655 struct addrinfo *ai;
656 int ret;
657 int saveflags;
658 int back_err;
659 int optval;
660 socklen_t optlen = sizeof(optval);
661 socklen_t len;
662 fd_set fd_w;
663 struct timeval selecttime;
664
665 char port[15];
666 sprintf(port,"%u",netcam->connect_port);
667
668 /* Lookup the hostname given in the netcam URL. */
669 if ((ret = getaddrinfo(netcam->connect_host, port, NULL, &ai)) != 0) {
670 if (!err_flag)
671 MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO
672 ,_("getaddrinfo() failed (%s): %s")
673 ,netcam->connect_host, gai_strerror(ret));
674
675 MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO,_("disconnecting netcam (1)"));
676
677 netcam_disconnect(netcam);
678 return -1;
679 }
680
681 /* Assure any previous connection has been closed - IF we are not in keepalive. */
682 if (!netcam->connect_keepalive) {
683 MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO
684 ,_("disconnecting netcam since keep-alive not set."));
685
686 netcam_disconnect(netcam);
687
688 /* Create a new socket. */
689 if ((netcam->sock = socket(ai->ai_family, SOCK_STREAM, 0)) < 0) {
690 MOTION_LOG(WRN, TYPE_NETCAM, SHOW_ERRNO
691 ,_("with no keepalive, attempt to create socket failed."));
692 return -1;
693 }
694
695 MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO
696 ,_("with no keepalive, new socket created fd %d"), netcam->sock);
697
698 } else if (netcam->sock == -1) { /* We are in keepalive mode, check for invalid socket. */
699 /* Must be first time, or closed, create a new socket. */
700 if ((netcam->sock = socket(ai->ai_family, SOCK_STREAM, 0)) < 0) {
701 MOTION_LOG(WRN, TYPE_NETCAM, SHOW_ERRNO
702 ,_("with keepalive set, invalid socket."
703 "This could be the first time. Creating a new one failed."));
704 return -1;
705 }
706
707 MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO
708 ,_("with keepalive set, invalid socket."
709 "This could be first time, created a new one with fd %d")
710 ,netcam->sock);
711
712 /* Record that this connection has not yet received a Keep-Alive header. */
713 netcam->keepalive_thisconn = FALSE;
714
715 /* Check the socket status for the keepalive option. */
716 if (getsockopt(netcam->sock, SOL_SOCKET, SO_KEEPALIVE, &optval, &optlen) < 0) {
717 MOTION_LOG(ERR, TYPE_NETCAM, SHOW_ERRNO, "getsockopt()");
718 return -1;
719 }
720
721 MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO
722 ,_("SO_KEEPALIVE is %s")
723 ,optval ? _("ON"):_("OFF"));
724
725 /* Set the option active. */
726 optval = 1;
727 optlen = sizeof(optval);
728
729 if (setsockopt(netcam->sock, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen) < 0) {
730 MOTION_LOG(ERR, TYPE_NETCAM, SHOW_ERRNO, "setsockopt()");
731 return -1;
732 }
733
734 MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, _("SO_KEEPALIVE set on socket."));
735 }
736
737 MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO
738 ,_("re-using socket %d since keepalive is set."), netcam->sock);
739
740 /*
741 * We set the socket non-blocking and then use a 'select'
742 * system call to control the timeout.
743 */
744
745 if ((saveflags = fcntl(netcam->sock, F_GETFL, 0)) < 0) {
746 MOTION_LOG(ERR, TYPE_NETCAM, SHOW_ERRNO, _("fcntl(1) on socket"));
747 netcam_disconnect(netcam);
748 return -1;
749 }
750
751 /* Set the socket non-blocking. */
752 if (fcntl(netcam->sock, F_SETFL, saveflags | O_NONBLOCK) < 0) {
753 MOTION_LOG(ERR, TYPE_NETCAM, SHOW_ERRNO,_("fcntl(2) on socket"));
754 netcam_disconnect(netcam);
755 return -1;
756 }
757
758 /* Now the connect call will return immediately. */
759 ret = connect(netcam->sock, ai->ai_addr, ai->ai_addrlen);
760 back_err = errno; /* Save the errno from connect */
761
762 freeaddrinfo(ai);
763
764 /* If the connect failed with anything except EINPROGRESS, error. */
765 if ((ret < 0) && (back_err != EINPROGRESS)) {
766 if (!err_flag)
767 MOTION_LOG(ERR, TYPE_NETCAM, SHOW_ERRNO
768 ,_("connect() failed (%d)"), back_err);
769
770 MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO,_("disconnecting netcam (4)"));
771
772 netcam_disconnect(netcam);
773 return -1;
774 }
775
776 /* Now we do a 'select' with timeout to wait for the connect. */
777 FD_ZERO(&fd_w);
778 FD_SET(netcam->sock, &fd_w);
779 selecttime.tv_sec = CONNECT_TIMEOUT;
780 selecttime.tv_usec = 0;
781 ret = select(FD_SETSIZE, NULL, &fd_w, NULL, &selecttime);
782
783 if (ret == 0) { /* 0 means timeout. */
784 if (!err_flag)
785 MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, _("timeout on connect()"));
786
787 MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO,_("disconnecting netcam (2)"));
788
789 netcam_disconnect(netcam);
790 return -1;
791 }
792
793 /*
794 * A +ve value returned from the select (actually, it must be a
795 * '1' showing 1 fd's changed) shows the select has completed.
796 * Now we must check the return code from the select.
797 */
798 len = sizeof(ret);
799
800 if (getsockopt(netcam->sock, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) {
801 MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, _("getsockopt after connect"));
802 netcam_disconnect(netcam);
803 return -1;
804 }
805
806 /* If the return code is anything except 0, error on connect. */
807 if (ret) {
808 if (!err_flag)
809 MOTION_LOG(ERR, TYPE_NETCAM, SHOW_ERRNO, _("connect returned error"));
810
811 MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO,_("disconnecting netcam (3)"));
812
813 netcam_disconnect(netcam);
814 return -1;
815 }
816
817 /* The socket info is stored in the rbuf structure of our context. */
818 rbuf_initialize(netcam);
819
820 return 0; /* Success */
821 }
822
netcam_check_buffsize(netcam_buff_ptr buff,size_t numbytes)823 void netcam_check_buffsize(netcam_buff_ptr buff, size_t numbytes)
824 {
825 int min_size_to_alloc;
826 int real_alloc;
827 int new_size;
828
829 if ((buff->size - buff->used) >= numbytes)
830 return;
831
832 min_size_to_alloc = numbytes - (buff->size - buff->used);
833 real_alloc = ((min_size_to_alloc / NETCAM_BUFFSIZE) * NETCAM_BUFFSIZE);
834
835 if ((min_size_to_alloc - real_alloc) > 0)
836 real_alloc += NETCAM_BUFFSIZE;
837
838 new_size = buff->size + real_alloc;
839
840 MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO
841 ,_("expanding buffer from [%d/%d] to [%d/%d] bytes.")
842 ,(int) buff->used, (int) buff->size
843 ,(int) buff->used, new_size);
844
845 buff->ptr = myrealloc(buff->ptr, new_size,
846 "netcam_check_buf_size");
847 buff->size = new_size;
848 }
849
850 /**
851 * Publish new image
852 *
853 * Moves the image in 'receiving' into 'latest' and updates last frame time
854 */
netcam_image_read_complete(netcam_context_ptr netcam)855 void netcam_image_read_complete(netcam_context_ptr netcam)
856 {
857 struct timeval curtime;
858 netcam_buff *xchg;
859
860 if (gettimeofday(&curtime, NULL) < 0)
861 MOTION_LOG(WRN, TYPE_NETCAM, SHOW_ERRNO, "gettimeofday");
862
863 netcam->receiving->image_time = curtime;
864 /*
865 * Calculate our "running average" time for this netcam's
866 * frame transmissions (except for the first time).
867 * Note that the average frame time is held in microseconds.
868 */
869 if (netcam->last_image.tv_sec) {
870 netcam->av_frame_time = ((9.0 * netcam->av_frame_time) + 1000000.0 *
871 (curtime.tv_sec - netcam->last_image.tv_sec) +
872 (curtime.tv_usec- netcam->last_image.tv_usec)) / 10.0;
873
874 /* The following floods the log. Comment out until it is needed. */
875 //MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO, "Calculated frame time %f", netcam->av_frame_time);
876 }
877
878 netcam->last_image = curtime;
879
880 /*
881 * read is complete - set the current 'receiving' buffer atomically
882 * as 'latest', and make the buffer previously in 'latest' become
883 * the new 'receiving'.
884 */
885 pthread_mutex_lock(&netcam->mutex);
886
887 xchg = netcam->latest;
888 netcam->latest = netcam->receiving;
889 netcam->receiving = xchg;
890 netcam->imgcnt++;
891
892 /*
893 * We have a new frame ready. We send a signal so that
894 * any thread (e.g. the motion main loop) waiting for the
895 * next frame to become available may proceed.
896 */
897 pthread_cond_signal(&netcam->pic_ready);
898 pthread_mutex_unlock(&netcam->mutex);
899 }
900
901 /**
902 * netcam_read_html_jpeg
903 *
904 * This routine reads a jpeg image from the netcam. When it is called,
905 * the stream is already positioned just after the image header.
906 *
907 * This routine is called under the four variations of two different
908 * conditions:
909 * 1) Streaming or non-streaming camera
910 * Note: Keep-Alive is supported for non-streaming cameras,
911 * if enabled in the netcam's config structure.
912 * 2) Header does or does not include Content-Length
913 * Additionally, if it is a streaming camera, there must always be a
914 * boundary-string.
915 *
916 * The routine will (attempt to) read the JPEG image. If a Content-Length
917 * is present, it will be used (this will result in more efficient code, and
918 * also code which should be better at detecting and recovering from possible
919 * error conditions).
920 *
921 * If a boundary-string is present (and, if the camera is streaming, this
922 * *must* be the case), the routine will assure that it is recognized and
923 * acted upon.
924 *
925 * Our algorithm for this will be as follows:
926 * 1) If a Content-Length is present, set the variable "remaining"
927 * to be equal to that value, else set it to a "very large"
928 * number.
929 * WARNING !!! Content-Length *must* to be greater than 0, even more
930 * a jpeg image cannot be less than 300 bytes or so.
931 * 2) While there is more data available from the camera:
932 * a) If there is a "boundary string" specified (from the initial
933 * header):
934 * i) If the amount of data in the input buffer is less than
935 * the length of the boundary string, get more data into
936 * the input buffer (error if failure).
937 * ii) If the boundary string is found, check how many
938 * characters remain in the input buffer before the start
939 * of the boundary string. If that is less than the
940 * variable "remaining", reset "remaining" to be equal
941 * to that number.
942 * b) Try to copy up to "remaining" characters from the input
943 * buffer into our destination buffer.
944 * c) If there are no more characters available from the camera,
945 * exit this loop, else subtract the number of characters
946 * actually copied from the variable "remaining".
947 * 3) If Content-Length was present, and "remaining" is not equal
948 * to zero, generate a warning message for logging.
949 *
950 *
951 * Parameters:
952 * netcam Pointer to netcam context
953 *
954 *
955 * Returns: 0 for success, -1 for error
956 *
957 */
netcam_read_html_jpeg(netcam_context_ptr netcam)958 static int netcam_read_html_jpeg(netcam_context_ptr netcam)
959 {
960 netcam_buff_ptr buffer;
961 size_t remaining; /* # characters to read */
962 size_t maxflush; /* # chars before boundary */
963 size_t rem, rlen, ix; /* Working vars */
964 int retval;
965 char *ptr, *bptr, *rptr;
966 /*
967 * Initialisation - set our local pointers to the context
968 * information.
969 */
970 buffer = netcam->receiving;
971 /* Assure the target buffer is empty. */
972 buffer->used = 0;
973 /* Prepare for read loop. */
974 if (buffer->content_length != 0)
975 remaining = buffer->content_length;
976 else
977 remaining = 9999999;
978
979 /* Now read in the data. */
980 while (remaining) {
981 /* Assure data in input buffer. */
982 if (netcam->response->buffer_left <= 0) {
983 retval = rbuf_read_bufferful(netcam);
984
985 if (retval <= 0)
986 break;
987
988 netcam->response->buffer_left = retval;
989 netcam->response->buffer_pos = netcam->response->buffer;
990 }
991
992 /* If a boundary string is present, take it into account. */
993 bptr = netcam->boundary;
994
995 if (bptr) {
996 rptr = netcam->response->buffer_pos;
997 rlen = netcam->response->buffer_left;
998
999 /* Loop through buffer looking for start of boundary. */
1000 while (1) {
1001 /*
1002 * Logic gets a little complicated here. The
1003 * problem is that we are reading in input
1004 * data in packets, and there is a (small)
1005 * chance that the boundary string could be
1006 * split across successive packets.
1007 * First a quick check if the string *might*
1008 * be in the current buffer.
1009 */
1010 if (rlen > remaining)
1011 rlen = remaining;
1012
1013 if (remaining < netcam->boundary_length)
1014 break;
1015
1016 if ((ptr = memchr(rptr, *bptr, rlen)) == NULL)
1017 /* Boundary not here (normal path) */
1018 break;
1019 /*
1020 * At least the first char was found in the
1021 * buffer - check for the rest.
1022 */
1023 rem = rlen - (ptr - rptr);
1024 for (ix = 1; (ix < rem) && (ix < netcam->boundary_length); ix++) {
1025 if (ptr[ix] != bptr[ix])
1026 break;
1027 }
1028
1029 if ((ix != netcam->boundary_length) && (ix != rem)) {
1030 /*
1031 * Not pointing at a boundary string -
1032 * step along input.
1033 */
1034 ix = ptr - rptr + 1;
1035 rptr += ix;
1036 rlen -= ix;
1037
1038 if (rlen <= 0)
1039 /* boundary not in buffer - go copy out */
1040 break;
1041 /*
1042 * Not yet decided - continue
1043 * through input.
1044 */
1045 continue;
1046 }
1047 /*
1048 * If we finish the 'for' with
1049 * ix == boundary_length, that means we found
1050 * the string, and should copy any data which
1051 * precedes it into the target buffer, then
1052 * exit the main loop.
1053 */
1054 if (ix == netcam->boundary_length) {
1055 if ((ptr - netcam->response->buffer) < (int) remaining)
1056 remaining = ptr - netcam->response->buffer;
1057
1058 /* Go copy everything up to boundary. */
1059 break;
1060 }
1061
1062 /*
1063 * If not, and ix == rem, that means we reached
1064 * the end of the input buffer in the middle of
1065 * our check, which is the (somewhat messy)
1066 * problem mentioned above.
1067 *
1068 * Assure there is data before potential
1069 * boundary string.
1070 */
1071 if (ptr != netcam->response->buffer) {
1072 /*
1073 * We have a boundary string crossing
1074 * packets :-(. We will copy all the
1075 * data up to the beginning of the
1076 * potential boundary, then re-position
1077 * the (partial) string to the
1078 * beginning and get some more input
1079 * data. First we flush the input
1080 * buffer up to the beginning of the
1081 * (potential) boundary string.
1082 */
1083 ix = ptr - netcam->response->buffer_pos;
1084 netcam_check_buffsize(buffer, ix);
1085 retval = rbuf_flush(netcam, buffer->ptr + buffer->used, ix);
1086 buffer->used += retval;
1087 remaining -= retval;
1088
1089 /*
1090 * Now move the boundary fragment to
1091 * the head of the input buffer.
1092 * This is really a "hack" - ideally,
1093 * we should have a function within the
1094 * module netcam_wget.c to do this job!
1095 */
1096
1097 MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO
1098 ,_("Potential split boundary - "
1099 "%d chars flushed, %d re-positioned")
1100 , ix, (int) netcam->response->buffer_left);
1101
1102 memmove(netcam->response->buffer, ptr,
1103 netcam->response->buffer_left);
1104 } /* End of boundary split over buffer. */
1105
1106 retval = netcam_recv(netcam, netcam->response->buffer +
1107 netcam->response->buffer_left,
1108 sizeof(netcam->response->buffer) -
1109 netcam->response->buffer_left);
1110
1111 if (retval <= 0) { /* This is a fatal error. */
1112 MOTION_LOG(ERR, TYPE_NETCAM, SHOW_ERRNO
1113 ,_("recv() fail after boundary string"));
1114 return -1;
1115 }
1116
1117 /* Reset the input buffer pointers. */
1118 netcam->response->buffer_left = retval + netcam->response->buffer_left;
1119 netcam->response->buffer_pos = netcam->response->buffer;
1120
1121 /* This will cause a 'continue' of the main loop. */
1122 bptr = NULL;
1123
1124 /* Return to do the boundary compare from the start. */
1125 break;
1126 } /* End of while(1) input buffer search. */
1127
1128 /* !bptr shows we're processing split boundary. */
1129 if (!bptr)
1130 continue;
1131 } /* end of if (bptr) */
1132
1133 /* boundary string not present, so just write out as much data as possible. */
1134 if (remaining) {
1135 maxflush = MINVAL(netcam->response->buffer_left, remaining);
1136 netcam_check_buffsize(buffer, maxflush);
1137 retval = rbuf_flush(netcam, buffer->ptr + buffer->used, maxflush);
1138 buffer->used += retval;
1139 remaining -= retval;
1140 }
1141 }
1142
1143 /* Fix starting of JPEG if needed , some cameras introduce thrash before
1144 * SOI 0xFFD8 Start Of Image
1145 */
1146 netcam_fix_jpeg_header(netcam);
1147
1148 netcam_image_read_complete(netcam);
1149
1150 if (netcam->caps.streaming == NCS_UNSUPPORTED) {
1151 if (!netcam->connect_keepalive) {
1152 MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO
1153 ,_("disconnecting netcam since keep-alive not set."));
1154 netcam_disconnect(netcam);
1155 return 0;
1156 }
1157 MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, _("leaving netcam connected."));
1158 }
1159
1160 return 0;
1161 }
1162
1163 /**
1164 * netcam_http_request
1165 *
1166 * This routine initiates a connection on the specified netcam,
1167 * for which every parameter has already been set (url, etc).
1168 * It uses the HTTP protocol, which is what many IP cameras use.
1169 * If this function succeeds, the HTTP response along with the
1170 * headers are already processed, and you can start reading contents
1171 * from here.
1172 *
1173 * Parameters:
1174 * netcam Pointer to a netcam_context structure
1175 *
1176 * Returns: 0 on success, -1 if an error occurs.
1177 */
netcam_http_request(netcam_context_ptr netcam)1178 static int netcam_http_request(netcam_context_ptr netcam)
1179 {
1180 int ix;
1181
1182 /*
1183 * Our basic initialisation has been completed. Now we will attempt
1184 * to connect with the camera so that we can then get a "header"
1185 * in order to find out what kind of camera we are dealing with,
1186 * as well as what are the picture dimensions. Note that for
1187 * this initial connection, any failure will cause an error
1188 * return from netcam_start (unlike later possible attempts at
1189 * re-connecting, if the network connection is later interrupted).
1190 */
1191 for (ix = 0; ix < MAX_HEADER_RETRIES; ix++) {
1192 /*
1193 * netcam_connect does an automatic netcam_close, so it's
1194 * safe to include it as part of this loop
1195 * (Not always true now Keep-Alive is implemented).
1196 */
1197 MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO
1198 ,_("about to try to connect, time #%d"), ix);
1199
1200 if (netcam_connect(netcam, 0) != 0) {
1201 MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO
1202 ,_("Failed to open camera - check your config and that netcamera is online"));
1203
1204 /* Fatal error on startup */
1205 ix = MAX_HEADER_RETRIES;
1206 break;;
1207 }
1208
1209 if (netcam_read_first_header(netcam) >= 0)
1210 break;
1211
1212 MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO,_("Error reading first header - re-trying"));
1213 }
1214
1215 if (ix == MAX_HEADER_RETRIES) {
1216 MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO
1217 ,_("Failed to read first camera header - giving up for now"));
1218 return -1;
1219 }
1220
1221 return 0;
1222 }
1223
1224 /**
1225 * netcam_http_build_url
1226 *
1227 * This routing takes care of the url-processing part of the http protocol.
1228 * This includes url scheme and parsing, proxy handling, http-authentication
1229 * preparation, response buffer allocation and so on. At the end of this
1230 * routine, we are ready to call netcam_http_request().
1231 *
1232 * Parameters:
1233 * netcam Pointer to a netcam_context structure
1234 * url Pointer to a netcam url structure
1235 *
1236 * Returns: 0 on success,
1237 * or -1 if an fatal error occurs.
1238 */
netcam_http_build_url(netcam_context_ptr netcam,struct url_t * url)1239 static int netcam_http_build_url(netcam_context_ptr netcam, struct url_t *url)
1240 {
1241 struct context *cnt = netcam->cnt;
1242 const char *ptr; /* Working var */
1243 char *userpass; /* Temp pointer to config value */
1244 char *encuserpass; /* Temp storage for encoded ver */
1245 char *request_pass = NULL; /* Temp storage for base64 conv */
1246 int ix;
1247
1248 /* First the http context structure. */
1249 netcam->response = mymalloc(sizeof(struct rbuf));
1250
1251 MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO
1252 ,_("Netcam has flags:"
1253 " HTTP/1.0: %s HTTP/1.1: %s Keep-Alive %s.")
1254 ,netcam->connect_http_10 ? "1":"0", netcam->connect_http_11 ? "1":"0"
1255 ,netcam->connect_keepalive ? "ON":"OFF");
1256
1257 /*
1258 * The network camera may require a username and password. If
1259 * so, the information can come from two different places in the
1260 * motion configuration file. Either it can be present in
1261 * the netcam_userpass, or it can be present as a part of the URL
1262 * for the camera. We assume the first of these has a higher
1263 * relevance.
1264 */
1265 if (cnt->conf.netcam_userpass)
1266 ptr = cnt->conf.netcam_userpass;
1267 else
1268 ptr = url->userpass;
1269
1270 /* motion_base64_encode needs up to 3 additional chars. */
1271 if (ptr) {
1272 userpass = mymalloc(strlen(ptr) + 3);
1273 strcpy(userpass, ptr);
1274 } else {
1275 userpass = NULL;
1276 }
1277
1278 /*
1279 * Now we want to create the actual string which will be used to
1280 * connect to the camera. It may or may not contain a username /
1281 * password. We first compose a basic connect message, then check
1282 * if a Keep-Alive header is to be included (or just 'close'), then
1283 * whether a username / password is required and, if so, just
1284 * concatenate it with the request.
1285 *
1286 */
1287
1288 /* Space for final \r\n plus string terminator. */
1289 ix = 3;
1290
1291 /* See if username / password is required. */
1292 if (userpass) { /* If either of the above are non-NULL. */
1293 /* Allocate space for the base64-encoded string. */
1294 encuserpass = mymalloc(BASE64_LENGTH(strlen(userpass)) + 1);
1295 /* Fill in the value. */
1296 motion_base64_encode(userpass, encuserpass, strlen(userpass));
1297 /* Now create the last part (authorization) of the request. */
1298 request_pass = mymalloc(strlen(connect_auth_req) +
1299 strlen(encuserpass) + 1);
1300 ix += sprintf(request_pass, connect_auth_req, encuserpass);
1301 /* Free the working variables. */
1302 free(encuserpass);
1303 }
1304
1305 /*
1306 * We are now ready to set up the netcam's "connect request". Most of
1307 * this comes from the (preset) string 'connect_req', but additional
1308 * characters are required if there is a proxy server, or if there is
1309 * a Keep-Alive connection rather than a close connection, or
1310 * a username / password for the camera. The variable 'ix' currently
1311 * has the number of characters required for username/password (which
1312 * could be zero) and for the \r\n and string terminator. We will also
1313 * always need space for the netcam path, and if a proxy is being used
1314 * we also need space for a preceding 'http://{hostname}' for the
1315 * netcam path.
1316 * Note: Keep-Alive (but not HTTP 1.1) is disabled if a proxy URL
1317 * is set, since HTTP 1.0 Keep-alive cannot be transferred through.
1318 */
1319 if (cnt->conf.netcam_proxy) {
1320 /*
1321 * Allocate space for a working string to contain the path.
1322 * The extra 4 is for "://" and string terminator.
1323 */
1324 if (url->port != 0) {
1325 ptr = mymalloc(strlen(url->service) + strlen(url->host) + 7 + strlen(url->path) + 4);
1326 sprintf((char *)ptr, "http://%s:%d%s", url->host, url->port , url->path);
1327 }else {
1328 ptr = mymalloc(strlen(url->service) + strlen(url->host) + strlen(url->path) + 4);
1329 sprintf((char *)ptr, "http://%s%s", url->host, url->path);
1330 }
1331
1332 netcam->connect_keepalive = FALSE; /* Disable Keepalive if proxy */
1333 free((void *)netcam->cnt->conf.netcam_keepalive);
1334 netcam->cnt->conf.netcam_keepalive = strdup("off");
1335
1336 MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO
1337 ,_("Removed netcam_keepalive flag due to proxy set."
1338 "Proxy is incompatible with Keep-Alive."));
1339 } else {
1340 /* If no proxy, set as netcam_url path. */
1341 ptr = url->path;
1342 /*
1343 * After generating the connect message the string
1344 * will be freed, so we don't want netcam_url_free
1345 * to free it as well.
1346 */
1347 url->path = NULL;
1348 }
1349
1350 ix += strlen(ptr);
1351
1352 /*
1353 * Now add the required number of characters for the close header
1354 * or Keep-Alive header. We test the flag which can be unset if
1355 * there is a problem (rather than the flag in the conf structure
1356 * which is read-only.
1357 */
1358
1359 if (netcam->connect_keepalive)
1360 ix += strlen(connect_req_keepalive);
1361 else
1362 ix += strlen(connect_req_close);
1363
1364
1365 /*
1366 * Point to either the HTTP 1.0 or 1.1 request header set
1367 * If the configuration is anything other than 1.1, use 1.0
1368 * as a default. This avoids a chance of being left with none.
1369 */
1370 if (netcam->connect_http_11 == TRUE)
1371 connect_req = connect_req_http11;
1372 else
1373 connect_req = connect_req_http10;
1374
1375 /*
1376 * Now that we know how much space we need, we can allocate space
1377 * for the connect-request string.
1378 */
1379 netcam->connect_request = mymalloc(strlen(connect_req) + ix +
1380 strlen(netcam->connect_host));
1381
1382 /* Now create the request string with an sprintf. */
1383 sprintf(netcam->connect_request, connect_req, ptr,
1384 netcam->connect_host);
1385
1386 if (netcam->connect_keepalive)
1387 strcat(netcam->connect_request, connect_req_keepalive);
1388 else
1389 strcat(netcam->connect_request, connect_req_close);
1390
1391
1392 if (userpass) {
1393 strcat(netcam->connect_request, request_pass);
1394 free(request_pass);
1395 free(userpass);
1396 }
1397
1398 /* Put on the final CRLF onto the request. */
1399 strcat(netcam->connect_request, "\r\n");
1400 free((void *)ptr);
1401 netcam_url_free(url); /* Cleanup the url data. */
1402
1403 /* Note that log messages are commented out to avoid leaking info related
1404 * to user/host/pass etc. Keeing them in the code for easier debugging if
1405 * it is needed
1406 */
1407 //MOTION_LOG(INF , TYPE_NETCAM, NO_ERRNO, "Camera connect"
1408 // " string is ''%s'' End of camera connect string.",
1409 // netcam->connect_request);
1410
1411 return 0;
1412 }
1413
1414 /**
1415 * netcam_setup_html
1416 * This function will parse the netcam url, connect to the camera,
1417 * set its type to jpeg-based, detect multipart and keep-alive,
1418 * and the get_image method accordingly. The cam can be non-streaming
1419 * or multipart-streaming.
1420 *
1421 * Parameters
1422 *
1423 * netcam Pointer to the netcam_context for the camera
1424 * url Pointer to the url of the camera
1425 *
1426 * Returns: 0 on success (camera link ok) or -1 if an error occurred.
1427 *
1428 */
netcam_setup_html(netcam_context_ptr netcam,struct url_t * url)1429 int netcam_setup_html(netcam_context_ptr netcam, struct url_t *url)
1430 {
1431 netcam->timeout.tv_sec = READ_TIMEOUT;
1432
1433 /*
1434 * This netcam is http-based, so build the required URL and
1435 * structures, like the connection-string and so on.
1436 */
1437 if (netcam_http_build_url(netcam, url) < 0)
1438 return -1;
1439
1440 /*
1441 * Then we will send our http request and get headers.
1442 */
1443 if (netcam_http_request(netcam) < 0)
1444 return -1;
1445
1446 /*
1447 * If this is a streaming camera, we need to position just
1448 * past the boundary string and read the image header.
1449 */
1450 if (netcam->caps.streaming == NCS_MULTIPART) {
1451 if (netcam_read_next_header(netcam) < 0) {
1452 MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO
1453 ,_("Failed to read first stream header - giving up for now"));
1454 return -1;
1455 }
1456 }
1457
1458 MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO
1459 ,_("connected, going on to read image."));
1460
1461 netcam->get_image = netcam_read_html_jpeg;
1462 return 0;
1463 }
1464
1465 /**
1466 * netcam_mjpg_buffer_refill
1467 *
1468 * This routing reads content from the MJPG-camera until the response
1469 * buffer of the specified netcam_context is full. If the connection is
1470 * lost during this operation, it tries to re-connect.
1471 *
1472 * Parameters:
1473 * netcam Pointer to a netcam_context structure
1474 *
1475 * Returns: The number of read bytes,
1476 * or -1 if an fatal connection error occurs.
1477 */
netcam_mjpg_buffer_refill(netcam_context_ptr netcam)1478 static int netcam_mjpg_buffer_refill(netcam_context_ptr netcam)
1479 {
1480 int retval;
1481
1482 if (netcam->response->buffer_left > 0)
1483 return netcam->response->buffer_left;
1484
1485 while (1) {
1486 retval = rbuf_read_bufferful(netcam);
1487 if (retval <= 0) { /* If we got 0, we timeoutted. */
1488 MOTION_LOG(ALR, TYPE_NETCAM, NO_ERRNO
1489 ,_("Read error, trying to reconnect.."));
1490 /* We may have lost the connexion */
1491 if (netcam_http_request(netcam) < 0) {
1492 MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO
1493 ,_("lost the cam."));
1494 return -1; /* We REALLY lost the cam... bail out for now. */
1495 }
1496 }
1497
1498 if (retval > 0)
1499 break;
1500 }
1501
1502 netcam->response->buffer_left = retval;
1503 netcam->response->buffer_pos = netcam->response->buffer;
1504
1505 MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO
1506 ,_("Refilled buffer with [%d] bytes from the network."), retval);
1507
1508 return retval;
1509 }
1510
1511
1512 /**
1513 * netcam_read_mjpg_jpeg
1514 *
1515 * This routine reads from a netcam using a MJPG-chunk based
1516 * protocol, used by Linksys WVC200 for example.
1517 * This implementation has been made by reverse-engineering
1518 * the protocol, so it may contain bugs and should be considered as
1519 * experimental.
1520 *
1521 * Protocol explanation:
1522 *
1523 * The stream consists of JPG pictures, spanned across multiple
1524 * MJPG chunks (in general 3 chunks, altough that's not guaranteed).
1525 *
1526 * Each data chunk can range from 1 to 65535 bytes + a header, altough
1527 * i have not seen anything bigger than 20000 bytes + a header.
1528 *
1529 * One MJPG chunk is constituted by a header plus the chunk data.
1530 * The chunk header is of fixed size, and the following data size
1531 * and position in the frame is specified in the chunk header.
1532 *
1533 * From what i have seen on WVC200 cameras, the stream always begins
1534 * on JPG frame boundary, so you don't have to worry about beginning
1535 * in the middle of a frame.
1536 *
1537 * See netcam.h for the mjpg_header structure and more details.
1538 *
1539 * Parameters:
1540 * netcam Pointer to a netcam_context structure
1541 *
1542 * Returns: 0 if an image was obtained from the camera,
1543 * or -1 if an error occurred.
1544 */
netcam_read_mjpg_jpeg(netcam_context_ptr netcam)1545 static int netcam_read_mjpg_jpeg(netcam_context_ptr netcam)
1546 {
1547 netcam_buff_ptr buffer;
1548 mjpg_header mh;
1549 size_t read_bytes;
1550 int retval;
1551
1552 /*
1553 * Initialisation - set our local pointers to the context
1554 * information.
1555 */
1556 buffer = netcam->receiving;
1557 /* Assure the target buffer is empty. */
1558 buffer->used = 0;
1559
1560 if (netcam_mjpg_buffer_refill(netcam) < 0)
1561 return -1;
1562
1563 /* Loop until we have a complete JPG. */
1564 while (1) {
1565 read_bytes = 0;
1566 while (read_bytes < sizeof(mh)) {
1567
1568 /* Transfer what we have in buffer in the header structure. */
1569 retval = rbuf_flush(netcam, ((char *)&mh) + read_bytes, sizeof(mh) - read_bytes);
1570
1571 read_bytes += retval;
1572
1573 MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO
1574 ,_("Read [%d/%d] header bytes."), read_bytes, sizeof(mh));
1575
1576 /* If we don't have received a full header, refill our buffer. */
1577 if (read_bytes < sizeof(mh)) {
1578 if (netcam_mjpg_buffer_refill(netcam) < 0)
1579 return -1;
1580 }
1581 }
1582
1583 /* Now check the validity of our header. */
1584 if (strncmp(mh.mh_magic, MJPG_MH_MAGIC, MJPG_MH_MAGIC_SIZE)) {
1585 MOTION_LOG(WRN, TYPE_NETCAM, NO_ERRNO
1586 ,_("Invalid header received, reconnecting"));
1587 /*
1588 * We shall reconnect to restart the stream, and get a chance
1589 * to resync.
1590 */
1591 if (netcam_http_request(netcam) < 0)
1592 return -1; /* We lost the cam... bail out. */
1593 /* Even there, we need to resync. */
1594 buffer->used = 0;
1595 continue ;
1596 }
1597
1598 /* Make room for the chunk. */
1599 netcam_check_buffsize(buffer, (int) mh.mh_chunksize);
1600
1601 read_bytes = 0;
1602 while (read_bytes < mh.mh_chunksize) {
1603 retval = rbuf_flush(netcam, buffer->ptr + buffer->used + read_bytes,
1604 mh.mh_chunksize - read_bytes);
1605 read_bytes += retval;
1606 MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO
1607 ,_("Read [%d/%d] chunk bytes, [%d/%d] total")
1608 ,read_bytes, mh.mh_chunksize
1609 ,buffer->used + read_bytes, mh.mh_framesize);
1610
1611 if (retval < (int) (mh.mh_chunksize - read_bytes)) {
1612 /* MOTION_LOG(EMG, TYPE_NETCAM, NO_ERRNO, "Chunk incomplete, going to refill."); */
1613 if (netcam_mjpg_buffer_refill(netcam) < 0)
1614 return -1;
1615
1616 }
1617 }
1618 buffer->used += read_bytes;
1619
1620 MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO
1621 ,_("Chunk complete, buffer used [%d] bytes."), buffer->used);
1622
1623 /* Is our JPG image complete ? */
1624 if (mh.mh_framesize == buffer->used) {
1625 MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO
1626 ,_("Image complete, buffer used [%d] bytes."), buffer->used);
1627 break;
1628 }
1629 /* MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO, "Rlen now at [%d] bytes", rlen); */
1630 }
1631
1632 netcam_image_read_complete(netcam);
1633
1634 return 0;
1635 }
1636
1637 /**
1638 * netcam_setup_mjpg
1639 * This function will parse the netcam url, connect to the camera,
1640 * set its type to MJPG-Streaming, and the get_image method accordingly.
1641 *
1642 * Parameters
1643 *
1644 * netcam Pointer to the netcam_context for the camera
1645 * url Pointer to the url of the camera
1646 *
1647 * Returns: 0 on success (camera link ok) or -1 if an error occurred.
1648 *
1649 */
netcam_setup_mjpg(netcam_context_ptr netcam,struct url_t * url)1650 int netcam_setup_mjpg(netcam_context_ptr netcam, struct url_t *url)
1651 {
1652 MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO,_("now calling netcam_setup_mjpg()"));
1653
1654 netcam->timeout.tv_sec = READ_TIMEOUT;
1655
1656 strcpy(url->service, "http"); /* Put back a real URL service. */
1657
1658 /*
1659 * This netcam is http-based, so build the required URL and
1660 * structures, like the connection-string and so on.
1661 */
1662 if (netcam_http_build_url(netcam, url) != 0)
1663 return -1;
1664
1665 /* Then we will send our http request and get headers. */
1666 if (netcam_http_request(netcam) < 0)
1667 return -1;
1668
1669 /* We have a special type of streaming camera. */
1670 netcam->caps.streaming = NCS_BLOCK;
1671
1672 /*
1673 * We are positionned right just at the start of the first MJPG
1674 * header, so don't move anymore, initialization complete.
1675 */
1676 MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO
1677 ,_("connected, going on to read and decode MJPG chunks."));
1678
1679 netcam->get_image = netcam_read_mjpg_jpeg;
1680
1681 return 0;
1682 }
1683
1684 /**
1685 * netcam_read_file_jpeg
1686 *
1687 * This routine reads local image jpeg. ( netcam_url jpeg:///path/image.jpg )
1688 * The current implementation is still a little experimental,
1689 * and needs some additional code for error detection and
1690 * recovery.
1691 */
netcam_read_file_jpeg(netcam_context_ptr netcam)1692 static int netcam_read_file_jpeg(netcam_context_ptr netcam)
1693 {
1694 int loop_counter = 0;
1695
1696 MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO,_("Begin"));
1697
1698 netcam_buff_ptr buffer;
1699 int len;
1700 struct stat statbuf;
1701
1702 /* Point to our working buffer. */
1703 buffer = netcam->receiving;
1704 buffer->used = 0;
1705
1706 /*int fstat(int filedes, struct stat *buf);*/
1707 do {
1708 if (stat(netcam->file->path, &statbuf)) {
1709 MOTION_LOG(CRT, TYPE_NETCAM, SHOW_ERRNO
1710 ,_("stat(%s) error"), netcam->file->path);
1711 return -1;
1712 }
1713
1714 MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO
1715 ,_("statbuf.st_mtime[%d] != last_st_mtime[%d]")
1716 , statbuf.st_mtime, netcam->file->last_st_mtime);
1717
1718 /* its waits POLLING_TIMEOUT */
1719 if (loop_counter>((POLLING_TIMEOUT*1000*1000)/(POLLING_TIME/1000))) {
1720 MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO
1721 ,_("waiting new file image timeout"));
1722 return -1;
1723 }
1724
1725 MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO
1726 ,_("delay waiting new file image "));
1727
1728 //its waits 5seconds - READ_TIMEOUT
1729 //SLEEP(netcam->timeout.tv_sec, netcam->timeout.tv_usec*1000);
1730 SLEEP(0, POLLING_TIME); // its waits 500ms
1731 /*return -1;*/
1732 loop_counter++;
1733
1734 } while (statbuf.st_mtime == netcam->file->last_st_mtime);
1735
1736 netcam->file->last_st_mtime = statbuf.st_mtime;
1737
1738 MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO
1739 ,_("processing new file image - st_mtime %d"), netcam->file->last_st_mtime);
1740
1741 /* Assure there's enough room in the buffer. */
1742 while (buffer->size < (size_t)statbuf.st_size)
1743 netcam_check_buffsize(buffer, statbuf.st_size);
1744
1745
1746 /* Do the read */
1747 netcam->file->control_file_desc = open(netcam->file->path, O_RDONLY|O_CLOEXEC);
1748 if (netcam->file->control_file_desc < 0) {
1749 MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO
1750 ,_("open(%s) error: %d")
1751 ,netcam->file->path, netcam->file->control_file_desc);
1752 return -1;
1753 }
1754 if ((len = read(netcam->file->control_file_desc,
1755 buffer->ptr + buffer->used, statbuf.st_size)) < 0) {
1756 MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO
1757 ,_("read(%s) error: %d"), netcam->file->control_file_desc, len);
1758 return -1;
1759 }
1760
1761 buffer->used += len;
1762 close(netcam->file->control_file_desc);
1763
1764 netcam_image_read_complete(netcam);
1765
1766 MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO,_("End"));
1767
1768 return 0;
1769 }
1770
file_new_context(void)1771 tfile_context *file_new_context(void)
1772 {
1773 /* Note that mymalloc will exit on any problem. */
1774 return mymalloc(sizeof(tfile_context));
1775 }
1776
file_free_context(tfile_context * ctxt)1777 void file_free_context(tfile_context* ctxt)
1778 {
1779 if (ctxt == NULL)
1780 return;
1781
1782 free(ctxt->path);
1783 free(ctxt);
1784 }
1785
netcam_setup_file(netcam_context_ptr netcam,struct url_t * url)1786 int netcam_setup_file(netcam_context_ptr netcam, struct url_t *url)
1787 {
1788
1789 if ((netcam->file = file_new_context()) == NULL)
1790 return -1;
1791
1792 /*
1793 * We copy the strings out of the url structure into the ftp_context
1794 * structure. By setting url->{string} to NULL we effectively "take
1795 * ownership" of the string away from the URL (i.e. it won't be freed
1796 * when we cleanup the url structure later).
1797 */
1798 netcam->file->path = url->path;
1799 url->path = NULL;
1800
1801 MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO
1802 ,_("netcam->file->path %s"), netcam->file->path);
1803
1804 netcam->get_image = netcam_read_file_jpeg;
1805
1806 return 0;
1807 }
1808
1809 /**
1810 * netcam_recv
1811 *
1812 * This routine receives the next block from the netcam. It takes care
1813 * of the potential timeouts and interrupt which may occur because of
1814 * the settings from setsockopt.
1815 *
1816 * Parameters:
1817 *
1818 * netcam Pointer to a netcam context
1819 * buffptr Pointer to the receive buffer
1820 * buffsize Length of the buffer
1821 *
1822 * Returns:
1823 * If successful, the length of the message received, otherwise the
1824 * error reply from the system call.
1825 *
1826 */
netcam_recv(netcam_context_ptr netcam,void * buffptr,size_t buffsize)1827 ssize_t netcam_recv(netcam_context_ptr netcam, void *buffptr, size_t buffsize)
1828 {
1829 ssize_t retval;
1830 fd_set fd_r;
1831 struct timeval selecttime;
1832
1833 if (netcam->sock < 0)
1834 return -1; /* We are not connected, it's impossible to receive data. */
1835
1836 FD_ZERO(&fd_r);
1837 FD_SET(netcam->sock, &fd_r);
1838 selecttime = netcam->timeout;
1839
1840 retval = select(FD_SETSIZE, &fd_r, NULL, NULL, &selecttime);
1841 if (retval == 0) /* 0 means timeout */
1842 return -1;
1843
1844 return recv(netcam->sock, buffptr, buffsize, 0);
1845 }
1846
1847
1848