1 /**
2  *      netcam.c
3  *
4  *      Module of common routines for handling network cameras.
5  *
6  */
7 
8 #include "translate.h"
9 #include "motion.h"
10 
11 #include <regex.h>                    /* For parsing of the URL */
12 
13 #include "netcam_http.h"
14 #include "netcam_ftp.h"
15 
16 /*
17  * The following three routines (netcam_url_match, netcam_url_parse and
18  * netcam_url_free are for 'parsing' (i.e. separating into the relevant
19  * components) the URL provided by the user.  They make use of regular
20  * expressions (which is outside the scope of this module, so detailed
21  * comments are not provided).  netcam_url_parse is called from netcam_start,
22  * and puts the "broken-up" components of the URL into the "url" element of
23  * the netcam_context structure.
24  *
25  * Note that the routines are not "very clever", but they work sufficiently
26  * well for the limited requirements of this module.  The expression:
27  *   (http)://(((.*):(.*))@)?([^/:]|[-.a-z0-9]+)(:([0-9]+))?($|(/[^:]*))
28  * requires
29  *   1) a string which begins with 'http', followed by '://'
30  *   2) optionally a '@' which is preceded by two strings
31  *      (with 0 or more characters each) separated by a ':'
32  *      [this is for an optional username:password]
33  *   3) a string comprising alpha-numerics, '-' and '.' characters
34  *      [this is for the hostname]
35  *   4) optionally a ':' followed by one or more numeric characters
36  *      [this is for an optional port number]
37  *   5) finally, either an end of line or a series of segments,
38  *      each of which begins with a '/', and contains anything
39  *      except a ':'
40  */
41 
42 /**
43  * netcam_url_match
44  *
45  *      Finds the matched part of a regular expression
46  *
47  * Parameters:
48  *
49  *      m          A structure containing the regular expression to be used
50  *      input      The input string
51  *
52  * Returns:        The string which was matched
53  *
54  */
netcam_url_match(regmatch_t m,const char * input)55 static char *netcam_url_match(regmatch_t m, const char *input)
56 {
57     char *match = NULL;
58     int len;
59 
60     if (m.rm_so != -1) {
61         len = m.rm_eo - m.rm_so;
62 
63         if ((match = mymalloc(len + 1)) != NULL) {
64             strncpy(match, input + m.rm_so, len);
65             match[len] = '\0';
66         }
67     }
68 
69     return match;
70 }
71 
netcam_url_invalid(struct url_t * parse_url)72 static void netcam_url_invalid(struct url_t *parse_url){
73 
74     MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO,_("Invalid URL.  Can not parse values."));
75 
76     parse_url->host = malloc(5);
77     parse_url->service = malloc(5);
78     parse_url->path = malloc(10);
79     parse_url->userpass = malloc(10);
80     parse_url->port = 0;
81     sprintf(parse_url->host, "%s","????");
82     sprintf(parse_url->service, "%s","????");
83     sprintf(parse_url->path, "%s","INVALID");
84     sprintf(parse_url->userpass, "%s","INVALID");
85 
86 }
87 /**
88  * netcam_url_parse
89  *
90  *      parses a string containing a URL into it's components
91  *
92  * Parameters:
93  *      parse_url          A structure which will receive the results
94  *                         of the parsing
95  *      text_url           The input string containing the URL
96  *
97  * Returns:                Nothing
98  *
99  */
netcam_url_parse(struct url_t * parse_url,const char * text_url)100 void netcam_url_parse(struct url_t *parse_url, const char *text_url)
101 {
102     char *s;
103     int i;
104 
105     const char *re = "(http|ftp|mjpg|mjpeg|rtsp|rtmp)://(((.*):(.*))@)?"
106                      "([^/:]|[-_.a-z0-9]+)(:([0-9]+))?($|(/[^*]*))";
107     regex_t pattbuf;
108     regmatch_t matches[10];
109 
110     if (!strncmp(text_url, "file", 4))
111         re = "(file)://(((.*):(.*))@)?([/:])?(:([0-9]+))?($|(/[^*]*))";
112 
113     if (!strncmp(text_url, "jpeg", 4))
114         re = "(jpeg)://(((.*):(.*))@)?([/:])?(:([0-9]+))?($|(/[^*]*))";
115 
116     if (!strncmp(text_url, "v4l2", 4))
117         re = "(v4l2)://(((.*):(.*))@)?([/:])?(:([0-9]+))?($|(/[^*]*))";
118 
119     /*  Note that log messages are commented out to avoid leaking info related
120      *  to user/host/pass etc.  Keeing them in the code for easier debugging if
121      *  it is needed
122      */
123 
124     //MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO, "Entry netcam_url_parse data %s",text_url);
125 
126     memset(parse_url, 0, sizeof(struct url_t));
127     /*
128      * regcomp compiles regular expressions into a form that is
129      * suitable for regexec searches
130      * regexec matches the URL string against the regular expression
131      * and returns an array of pointers to strings matching each match
132      * within (). The results that we need are finally placed in parse_url.
133      */
134     if (!regcomp(&pattbuf, re, REG_EXTENDED | REG_ICASE)) {
135         if (regexec(&pattbuf, text_url, 10, matches, 0) != REG_NOMATCH) {
136             for (i = 0; i < 10; i++) {
137                 if ((s = netcam_url_match(matches[i], text_url)) != NULL) {
138                     //MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "Parse case %d data %s", i, s);
139                     switch (i) {
140                     case 1:
141                         parse_url->service = s;
142                         break;
143                     case 3:
144                         parse_url->userpass = s;
145                         break;
146                     case 6:
147                         parse_url->host = s;
148                         break;
149                     case 8:
150                         parse_url->port = atoi(s);
151                         free(s);
152                         break;
153                     case 9:
154                         parse_url->path = s;
155                         break;
156                         /* Other components ignored */
157                     default:
158                         free(s);
159                         break;
160                     }
161                 }
162             }
163         } else {
164             netcam_url_invalid(parse_url);
165         }
166     } else {
167         netcam_url_invalid(parse_url);
168     }
169     if (((!parse_url->port) && (parse_url->service)) ||
170         ((parse_url->port > 65535) && (parse_url->service))) {
171         if (!strcmp(parse_url->service, "http"))
172             parse_url->port = 80;
173         else if (!strcmp(parse_url->service, "ftp"))
174             parse_url->port = 21;
175         else if (!strcmp(parse_url->service, "rtmp"))
176             parse_url->port = 1935;
177         else if (!strcmp(parse_url->service, "rtsp"))
178             parse_url->port = 554;
179         MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, _("Using port number %d"),parse_url->port);
180     }
181 
182     regfree(&pattbuf);
183 }
184 
185 /**
186  * netcam_url_free
187  *
188  *      General cleanup of the URL structure, called from netcam_cleanup.
189  *
190  * Parameters:
191  *
192  *      parse_url       Structure containing the parsed data.
193  *
194  * Returns:             Nothing
195  *
196  */
netcam_url_free(struct url_t * parse_url)197 void netcam_url_free(struct url_t *parse_url)
198 {
199     free(parse_url->service);
200     parse_url->service = NULL;
201 
202     free(parse_url->userpass);
203     parse_url->userpass = NULL;
204 
205     free(parse_url->host);
206     parse_url->host = NULL;
207 
208     free(parse_url->path);
209     parse_url->path = NULL;
210 }
211 
212 /**
213  * netcam_handler_loop
214  *      This is the "main loop" for the handler thread.  It is created
215  *      in netcam_start when a streaming camera is detected.
216  *
217  * Parameters
218  *
219  *      arg     Pointer to the motion context for this camera.
220  *
221  * Returns:     NULL pointer
222  *
223  */
netcam_handler_loop(void * arg)224 static void *netcam_handler_loop(void *arg)
225 {
226     int retval;
227     int open_error = 0;
228     netcam_context_ptr netcam = arg;
229     struct context *cnt = netcam->cnt; /* Needed for the SETUP macro :-( */
230 
231     netcam->handler_finished = FALSE;
232 
233     util_threadname_set("nc",netcam->threadnr,netcam->cnt->conf.camera_name);
234 
235     /* Store the corresponding motion thread number in TLS also for this
236      * thread (necessary for 'MOTION_LOG' to function properly).
237      */
238     pthread_setspecific(tls_key_threadnr, (void *)((unsigned long)cnt->threadnr));
239 
240     MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO
241         ,_("Camera handler thread [%d] started"), netcam->threadnr);
242     /*
243      * The logic of our loop is very simple.  If this is a non-
244      * streaming camera, we re-establish connection with the camera
245      * and read the header record.  If it's a streaming camera, we
246      * position to the next "boundary string" in the input stream.
247      * In either case, we then read the following JPEG image into the
248      * next available buffer, updating the "next" and "latest" indices
249      * in our netcam * structure.  The loop continues until netcam->finish
250      * or cnt->finish is set.
251      */
252 
253     while (!netcam->finish) {
254         if (netcam->response) {    /* If html input */
255             if (netcam->caps.streaming == NCS_UNSUPPORTED) {
256                 /* Non-streaming ie. jpeg */
257                 if (!netcam->connect_keepalive ||
258                     (netcam->connect_keepalive && netcam->keepalive_timeup)) {
259                     /* If keepalive flag set but time up, time to close this socket. */
260                     if (netcam->connect_keepalive && netcam->keepalive_timeup) {
261                         MOTION_LOG(WRN, TYPE_NETCAM, NO_ERRNO
262                             ,_("Closing netcam socket as Keep-Alive time is up "
263                             "(camera sent Close field). A reconnect should happen."));
264                         netcam_disconnect(netcam);
265                         netcam->keepalive_timeup = FALSE;
266                     }
267 
268                     /* And the netcam_connect call below will open a new one. */
269                     if (netcam_connect(netcam, open_error) < 0) {
270                         if (!open_error) { /* Log first error. */
271                             MOTION_LOG(WRN, TYPE_NETCAM, NO_ERRNO
272                                 ,_("re-opening camera (non-streaming)"));
273                             open_error = 1;
274                         }
275                         /* Need to have a dynamic delay here. */
276                         SLEEP(5, 0);
277                         continue;
278                     }
279 
280                     if (open_error) {          /* Log re-connection */
281                         MOTION_LOG(WRN, TYPE_NETCAM, NO_ERRNO
282                             ,_("camera re-connected"));
283                         open_error = 0;
284                     }
285                 }
286                 /* Send our request and look at the response. */
287                 if ((retval = netcam_read_first_header(netcam)) != 1) {
288                     if (retval > 0) {
289                         MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO
290                             ,_("Unrecognized image header (%d)"), retval);
291                     } else if (retval != -1) {
292                         MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO
293                             ,_("Error in header (%d)"), retval);
294                     }
295                     /* Need to have a dynamic delay here. */
296                     continue;
297                 }
298             } else if (netcam->caps.streaming == NCS_MULTIPART) {    /* Multipart Streaming */
299                 if (netcam_read_next_header(netcam) < 0) {
300                     if (netcam_connect(netcam, open_error) < 0) {
301                         if (!open_error) { /* Log first error */
302                             MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO
303                                 ,_("re-opening camera (streaming)"));
304                             open_error = 1;
305                         }
306                         SLEEP(5, 0);
307                         continue;
308                     }
309 
310                     if ((retval = netcam_read_first_header(netcam) != 2)) {
311                         if (retval > 0) {
312                             MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO
313                                 ,_("Unrecognized image header (%d)"), retval);
314                         } else if (retval != -1) {
315                             MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO
316                                 ,_("Error in header (%d)"), retval);
317                         }
318                         /* FIXME need some limit. */
319                         continue;
320                     }
321                 }
322                 if (open_error) {          /* Log re-connection */
323                     MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO
324                         ,_("camera re-connected"));
325                     open_error = 0;
326                 }
327             } else if (netcam->caps.streaming == NCS_BLOCK) { /* MJPG-Block streaming */
328                 /*
329                  * Since we cannot move in the stream here, because we will read past the
330                  * MJPG-block-header, error handling is done while reading MJPG blocks.
331                  */
332             }
333         }
334 
335 
336         if (netcam->get_image(netcam) < 0) {
337             MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO,_("Error getting jpeg image"));
338             /* If FTP connection, attempt to re-connect to server. */
339             if (netcam->ftp) {
340                 close(netcam->ftp->control_file_desc);
341                 if (ftp_connect(netcam) < 0)
342                     MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO,_("Trying to re-connect"));
343             }
344             continue;
345         }
346 
347 
348         /*
349          * FIXME
350          * Need to check whether the image was received / decoded
351          * satisfactorily.
352          */
353 
354         /*
355          * If non-streaming, want to synchronize our thread with the
356          * motion main-loop.
357          */
358         if (netcam->caps.streaming == NCS_UNSUPPORTED) {
359             pthread_mutex_lock(&netcam->mutex);
360 
361             /* Before anything else, check for system shutdown. */
362             if (netcam->finish) {
363                 pthread_mutex_unlock(&netcam->mutex);
364                 break;
365             }
366 
367             /*
368              * If our current loop has finished before the next
369              * request from the motion main-loop, we do a
370              * conditional wait (wait for signal).  On the other
371              * hand, if the motion main-loop has already signalled
372              * us, we just continue.  In either event, we clear
373              * the start_capture flag set by the main loop.
374              */
375             if (!netcam->start_capture)
376                 pthread_cond_wait(&netcam->cap_cond, &netcam->mutex);
377 
378             netcam->start_capture = 0;
379 
380             pthread_mutex_unlock(&netcam->mutex);
381         }
382     /* The loop continues forever, or until motion shutdown. */
383     }
384 
385     /* Our thread is finished - decrement motion's thread count. */
386     pthread_mutex_lock(&global_lock);
387     threads_running--;
388     pthread_mutex_unlock(&global_lock);
389 
390     /* Log out a termination message. */
391     MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO
392         ,_("netcam camera handler: finish set, exiting"));
393 
394     netcam->handler_finished = TRUE;
395 
396     /* Signal netcam_cleanup that we're all done. */
397     pthread_mutex_lock(&netcam->mutex);
398     pthread_cond_signal(&netcam->exiting);
399     pthread_mutex_unlock(&netcam->mutex);
400 
401     /* Goodbye..... */
402     pthread_exit(NULL);
403 }
404 
405 /**
406  * netcam_cleanup
407  *
408  *      This routine releases any allocated data within the netcam context,
409  *      then frees the context itself.  Extreme care must be taken to assure
410  *      that the multi-threading nature of the program is correctly
411  *      handled.
412  *      This function is also called from motion_init if first time connection
413  *      fails and we start retrying until we get a valid first frame from the
414  *      camera.
415  *
416  * Parameters:
417  *
418  *      netcam           Pointer to a netcam context
419  *      init_retry_flag  1 when the function is called because we are retrying
420  *                         making the initial connection with a netcam and we know
421  *                         we do not need to kill a netcam handler thread
422  *                       0 in any other case.
423  *
424  * Returns:              Nothing.
425  *
426  */
netcam_cleanup(netcam_context_ptr netcam,int init_retry_flag)427 void netcam_cleanup(netcam_context_ptr netcam, int init_retry_flag){
428     struct timespec waittime;
429 
430     if (!netcam) return;
431 
432     /*
433      * This 'lock' is just a bit of "defensive" programming.  It should
434      * only be necessary if the routine is being called from different
435      * threads, but in our Motion design, it should only be called from
436      * the motion main-loop.
437      */
438     pthread_mutex_lock(&netcam->mutex);
439 
440     if (netcam->cnt->netcam == NULL)
441         return;
442 
443     /*
444      * We set the netcam_context pointer in the motion main-loop context
445      * to be NULL, so that this routine won't be called a second time.
446      */
447     netcam->cnt->netcam = NULL;
448 
449     /*
450      * Next we set 'finish' in order to get the camera-handler thread
451      * to stop.
452      */
453     netcam->finish = 1;
454 
455     /*
456      * If the camera is non-streaming, the handler thread could be waiting
457      * for a signal, so we send it one.  If it's actually waiting on the
458      * condition, it won't actually start yet because we still have
459      * netcam->mutex locked.
460      */
461 
462     if (netcam->caps.streaming == NCS_UNSUPPORTED)
463         pthread_cond_signal(&netcam->cap_cond);
464 
465 
466     /*
467      * Once the camera-handler gets to the end of it's loop (probably as
468      * soon as we release netcam->mutex), because netcam->finish has been
469      * set it will exit it's loop, do anything it needs to do with the
470      * netcam context, and then send *us* as signal (netcam->exiting).
471      * Note that when we start our wait on netcam->exiting, our lock on
472      * netcam->mutex is automatically released, which will allow the
473      * handler to complete it's loop, notice that 'finish' is set and exit.
474      * This should always work, but again (defensive programming) we
475      * use pthread_cond_timedwait and, if our timeout (8 seconds) expires
476      * we just do the cleanup the handler would normally have done.  This
477      * assures that (even if there is a bug in our code) motion will still
478      * be able to exit.
479      * If the init_retry_flag is not set the netcam_cleanup code was
480      * called while retrying the initial connection to a netcam and then
481      * there is no camera-handler started yet and thread_running must
482      * not be decremented.
483      */
484     waittime.tv_sec = time(NULL) + 8;   /* Seems that 3 is too small */
485     waittime.tv_nsec = 0;
486 
487     if (!init_retry_flag &&
488         pthread_cond_timedwait(&netcam->exiting, &netcam->mutex, &waittime) != 0) {
489         /*
490          * Although this shouldn't happen, if it *does* happen we will
491          * log it (just for the programmer's information).
492          */
493         MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO
494             ,_("No response from camera handler - it must have already died"));
495         pthread_mutex_lock(&global_lock);
496         threads_running--;
497         pthread_mutex_unlock(&global_lock);
498     }
499 
500     /* We don't need any lock anymore, so release it. */
501     pthread_mutex_unlock(&netcam->mutex);
502 
503     /* and cleanup the rest of the netcam_context structure. */
504     free(netcam->connect_host);
505     free(netcam->connect_request);
506     free(netcam->boundary);
507 
508 
509     if (netcam->latest != NULL) {
510         free(netcam->latest->ptr);
511         free(netcam->latest);
512     }
513 
514     if (netcam->receiving != NULL) {
515         free(netcam->receiving->ptr);
516         free(netcam->receiving);
517     }
518 
519     if (netcam->jpegbuf != NULL) {
520         free(netcam->jpegbuf->ptr);
521         free(netcam->jpegbuf);
522     }
523 
524     if (netcam->ftp != NULL) {
525         ftp_free_context(netcam->ftp);
526         netcam->ftp = NULL;
527     } else {
528         netcam_disconnect(netcam);
529     }
530 
531     free(netcam->response);
532 
533     pthread_mutex_destroy(&netcam->mutex);
534     pthread_cond_destroy(&netcam->cap_cond);
535     pthread_cond_destroy(&netcam->pic_ready);
536     pthread_cond_destroy(&netcam->exiting);
537     free(netcam);
538 }
539 
540 /**
541  * netcam_next
542  *
543  *      This routine is called when the main 'motion' thread wants a new
544  *      frame of video.  It fetches the most recent frame available from
545  *      the netcam, converts it to YUV420P, and returns it to motion.
546  *
547  * Parameters:
548  *      cnt             Pointer to the context for this thread
549  *      image           Pointer to a buffer for the returned image
550  *
551  * Returns:             Error code
552  */
netcam_next(struct context * cnt,struct image_data * img_data)553 int netcam_next(struct context *cnt, struct image_data *img_data){
554 
555     netcam_context_ptr netcam;
556 
557     /*
558      * Here we have some more "defensive programming".  This check should
559      * never be true, but if it is just return with a "fatal error".
560      */
561     if ((!cnt) || (!cnt->netcam))
562         return NETCAM_FATAL_ERROR;
563 
564     netcam = cnt->netcam;
565 
566     if (!netcam->latest->used) {
567         MOTION_LOG(WRN, TYPE_NETCAM, NO_ERRNO,_("called with no data in buffer"));
568         return NETCAM_NOTHING_NEW_ERROR;
569     }
570 
571     /*
572      * If we are controlling a non-streaming camera, we synchronize the
573      * motion main-loop with the camera-handling thread through a signal,
574      * together with a flag to say "start your next capture".
575      */
576     if (netcam->caps.streaming == NCS_UNSUPPORTED) {
577         pthread_mutex_lock(&netcam->mutex);
578         netcam->start_capture = 1;
579         pthread_cond_signal(&netcam->cap_cond);
580         pthread_mutex_unlock(&netcam->mutex);
581     }
582 
583 
584     /*
585      * If an error occurs in the JPEG decompression which follows this,
586      * jpeglib will return to the code within this 'if'.  Basically, our
587      * approach is to just return a NULL (failed) to the caller (an
588      * error message has already been produced by the libjpeg routines).
589      */
590     if (setjmp(netcam->setjmp_buffer))
591         return NETCAM_GENERAL_ERROR | NETCAM_JPEG_CONV_ERROR;
592 
593     /* If there was no error, process the latest image buffer. */
594     return netcam_proc_jpeg(netcam, img_data);
595 }
596 
597 /**
598  * netcam_start
599  *
600  *      This routine is called from the main motion thread.  It's job is
601  *      to open up the requested camera device and do any required
602  *      initialization.  If the camera is a streaming type, then this
603  *      routine must also start up the camera-handling thread to take
604  *      care of it.
605  *
606  * Parameters:
607  *
608  *      cnt     Pointer to the motion context structure for this device.
609  *
610  * Returns:     0 on success
611  *              -1 on any failure
612  *              -2 image dimensions are not modulo 8
613  */
netcam_start(struct context * cnt)614 int netcam_start(struct context *cnt){
615 
616     netcam_context_ptr netcam;        /* Local pointer to our context. */
617     pthread_attr_t handler_attribute; /* Attributes of our handler thread. */
618     int retval;                       /* Working var. */
619     struct url_t url;                 /* For parsing netcam URL. */
620     char    err_service[6];
621 
622     memset(&url, 0, sizeof(url));
623 
624     cnt->netcam = mymalloc(sizeof(struct netcam_context));
625     netcam = cnt->netcam;           /* Just for clarity in remaining code. */
626     netcam->cnt = cnt;              /* Fill in the "parent" info. */
627 
628     /* Our image buffers */
629     netcam->receiving = mymalloc(sizeof(netcam_buff));
630     netcam->receiving->ptr = mymalloc(NETCAM_BUFFSIZE);
631 
632     netcam->latest = mymalloc(sizeof(netcam_buff));
633     netcam->latest->ptr = mymalloc(NETCAM_BUFFSIZE);
634 
635     netcam->jpegbuf = mymalloc(sizeof(netcam_buff));
636     netcam->jpegbuf->ptr = mymalloc(NETCAM_BUFFSIZE);
637 
638     /* Thread control structures */
639     pthread_mutex_init(&netcam->mutex, NULL);
640     pthread_cond_init(&netcam->cap_cond, NULL);
641     pthread_cond_init(&netcam->pic_ready, NULL);
642     pthread_cond_init(&netcam->exiting, NULL);
643 
644     /* Initialize the average frame time to the user's value. */
645     netcam->av_frame_time = 1000000.0 / cnt->conf.framerate;
646 
647     MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO
648         ,_("Network Camera starting for camera (%s)"), cnt->conf.camera_name);
649 
650     /* If a proxy has been specified, parse that URL. */
651     if (cnt->conf.netcam_proxy) {
652         netcam_url_parse(&url, cnt->conf.netcam_proxy);
653 
654         if (!url.host) {
655             MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO
656                 ,_("Invalid netcam_proxy (%s)"), cnt->conf.netcam_proxy);
657             netcam_url_free(&url);
658             return -1;
659         }
660 
661         if (url.userpass) {
662             MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO
663                 ,_("Username/password not allowed on a proxy URL"));
664             netcam_url_free(&url);
665             return -1;
666         }
667 
668         /*
669          * A 'proxy' means that our eventual 'connect' to our
670          * camera must be sent to the proxy, and that our 'GET' must
671          * include the full path to the camera host.
672          */
673         netcam->connect_host = url.host;
674         url.host = NULL;
675         netcam->connect_port = url.port;
676         netcam_url_free(&url);  /* Finished with proxy */
677     }
678 
679     /* Parse the URL from the configuration data */
680     netcam_url_parse(&url, cnt->conf.netcam_url);
681 
682     if (!url.service) {
683         snprintf(err_service,5,"%s",cnt->conf.netcam_url);
684         MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO
685             ,_("Invalid netcam service '%s' "), err_service);
686         netcam_url_free(&url);
687         return -1;
688     }
689 
690     if ((!url.host) && (strcmp(url.service, "jpeg"))) {
691         MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO
692             ,_("Invalid netcam_url for camera (%s)"), cnt->conf.camera_name);
693         netcam_url_free(&url);
694         return -1;
695     }
696 
697     if (cnt->conf.netcam_proxy == NULL) {
698         netcam->connect_host = url.host;
699         url.host = NULL;
700         netcam->connect_port = url.port;
701     }
702 
703     /* Get HTTP Mode (1.0 default, 1.0 Keep-Alive, 1.1) flag from config
704      * and report its stata for debug reasons.
705      * The flags in the conf structure is read only and cannot be
706      * unset if the Keep-Alive needs to be switched off (ie. netcam does
707      * not turn out to support it. That is handled by unsetting the flags
708      * in the context structures (cnt->...) only.
709      */
710 
711     if (!strcmp(cnt->conf.netcam_keepalive, "force")) {
712             netcam->connect_http_10   = TRUE;
713             netcam->connect_http_11   = FALSE;
714             netcam->connect_keepalive = TRUE;
715     } else if (!strcmp(cnt->conf.netcam_keepalive, "off")) {
716             netcam->connect_http_10   = TRUE;
717             netcam->connect_http_11   = FALSE;
718             netcam->connect_keepalive = FALSE;
719     } else if (!strcmp(cnt->conf.netcam_keepalive, "on")) {
720             netcam->connect_http_10   = FALSE;
721             netcam->connect_http_11   = TRUE;
722             netcam->connect_keepalive = TRUE; /* HTTP 1.1 has keepalive by default. */
723     }
724 
725     MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO
726         ,_("Netcam_http parameter '%s' converts to flags: HTTP/1.0: %s HTTP/1.1: %s Keep-Alive %s.")
727         ,cnt->conf.netcam_keepalive
728         ,netcam->connect_http_10 ? "1":"0", netcam->connect_http_11 ? "1":"0"
729         ,netcam->connect_keepalive ? "ON":"OFF");
730 
731 
732     /* Initialise the netcam socket to -1 to trigger a connection by the keep-alive logic. */
733     netcam->sock = -1;
734 
735     if ((url.service) && (!strcmp(url.service, "http"))) {
736         MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO,_("now calling netcam_setup_html()"));
737         retval = netcam_setup_html(netcam, &url);
738     } else if ((url.service) && (!strcmp(url.service, "ftp"))) {
739         MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO,_("now calling netcam_setup_ftp"));
740         retval = netcam_setup_ftp(netcam, &url);
741     } else if ((url.service) && (!strcmp(url.service, "jpeg"))) {
742         MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO,_("now calling netcam_setup_file()"));
743         retval = netcam_setup_file(netcam, &url);
744     } else if ((url.service) && (!strcmp(url.service, "mjpg"))) {
745         retval = netcam_setup_mjpg(netcam, &url);
746     } else {
747         MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO
748             ,_("Invalid netcam service '%s' - must be http, ftp, mjpg, mjpeg, v4l2 or jpeg.")
749             , url.service);
750         retval = -1;
751     }
752 
753     netcam_url_free(&url);
754     if (retval < 0) return -1;
755 
756     /*
757      * We expect that, at this point, we should be positioned to read
758      * he first image available from the camera (directly after the
759      * applicable header).  We want to decode the image in order to get
760      * the dimensions (width and height).  If successful, we will use
761      * these to set the required image buffer(s) in our netcam_struct.
762      */
763     if ((retval = netcam->get_image(netcam)) != 0) {
764         MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO
765             ,_("Failed trying to read first image - retval:%d"), retval);
766         return -1;
767     }
768 
769     /*
770     * If an error occurs in the JPEG decompression which follows this,
771     * jpeglib will return to the code within this 'if'.  If such an error
772     * occurs during startup, we will just abandon this attempt.
773     */
774     if (setjmp(netcam->setjmp_buffer)) {
775         MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO
776             ,_("libjpeg decompression failure on first frame - giving up!"));
777         return -1;
778     }
779 
780     netcam->netcam_tolerant_check = cnt->conf.netcam_tolerant_check;
781     netcam->JFIF_marker = 0;
782     netcam_get_dimensions(netcam);
783 
784     /* Validate image sizes are multiple of 8 */
785     if ((netcam->width % 8) || (netcam->height % 8) ) {
786         MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO
787             ,_("Width/height(%dx%d) must be multiples of 8")
788             ,netcam->width, netcam->height);
789         return -2;
790     }
791 
792     /* Fill in camera details into context structure. */
793     cnt->imgs.width = netcam->width;
794     cnt->imgs.height = netcam->height;
795     cnt->imgs.size_norm = (netcam->width * netcam->height * 3) / 2;
796     cnt->imgs.motionsize = netcam->width * netcam->height;
797 
798     cnt->imgs.width_high  = 0;
799     cnt->imgs.height_high = 0;
800     cnt->imgs.size_high   = 0;
801 
802     pthread_attr_init(&handler_attribute);
803     pthread_attr_setdetachstate(&handler_attribute, PTHREAD_CREATE_DETACHED);
804     pthread_mutex_lock(&global_lock);
805         netcam->threadnr = ++threads_running;
806     pthread_mutex_unlock(&global_lock);
807 
808     retval = pthread_create(&netcam->thread_id, &handler_attribute,&netcam_handler_loop, netcam);
809     if (retval < 0) {
810         MOTION_LOG(ALR, TYPE_NETCAM, SHOW_ERRNO
811             ,_("Error starting camera handler thread [%d]"), netcam->threadnr);
812         return -1;
813     }
814 
815     return 0;
816 }
817 
818