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