Index: Makefile.in
===================================================================
--- Makefile.in	(revision 471)
+++ Makefile.in	(working copy)
@@ -37,7 +37,7 @@
 OBJ          = motion.o logger.o conf.o draw.o jpegutils.o vloopback_motion.o $(VIDEO_OBJ) \
 			   netcam.o netcam_ftp.o netcam_jpeg.o netcam_wget.o track.o \
 			   alg.o event.o picture.o rotate.o webhttpd.o \
-			   stream.o @FFMPEG_OBJ@
+			   stream.o md5.o @FFMPEG_OBJ@
 SRC          = $(OBJ:.o=.c)
 DOC          = CHANGELOG COPYING CREDITS INSTALL README motion_guide.html
 EXAMPLES     = *.conf motion.init-Debian motion.init-RH motion.init-FreeBSD.sh
Index: md5.c
===================================================================
--- md5.c	(revision 0)
+++ md5.c	(revision 0)
@@ -0,0 +1,343 @@
+/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
+   taken from RFC 1321
+ */
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+rights reserved.
+
+License to copy and use this software is granted provided that it
+is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+Algorithm" in all material mentioning or referencing this software
+or this function.
+
+License is also granted to make and use derivative works provided
+that such works are identified as "derived from the RSA Data
+Security, Inc. MD5 Message-Digest Algorithm" in all material
+mentioning or referencing the derived work.
+
+RSA Data Security, Inc. makes no representations concerning either
+the merchantability of this software or the suitability of this
+software for any particular purpose. It is provided "as is"
+without express or implied warranty of any kind.
+
+These notices must be retained in any copies of any part of this
+documentation and/or software.
+ */
+
+#include "md5.h"
+
+/* Constants for MD5Transform routine.
+ */
+
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+
+static void MD5Transform(UINT4 [4], unsigned char [64]);
+static void Encode(unsigned char *, UINT4 *, unsigned int);
+static void Decode(UINT4 *, unsigned char *, unsigned int);
+static void MD5_memcpy(POINTER, POINTER, unsigned int);
+static void MD5_memset(POINTER, int, unsigned int);
+
+static unsigned char PADDING[64] = {
+  0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* F, G, H and I are basic MD5 functions.
+ */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/* ROTATE_LEFT rotates x left n bits.
+ */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+Rotation is separate from addition to prevent recomputation.
+ */
+#define FF(a, b, c, d, x, s, ac) { \
+ (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+  }
+#define GG(a, b, c, d, x, s, ac) { \
+ (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+  }
+#define HH(a, b, c, d, x, s, ac) { \
+ (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+  }
+#define II(a, b, c, d, x, s, ac) { \
+ (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+  }
+
+/* MD5 initialization. Begins an MD5 operation, writing a new context.
+ */
+void MD5Init(MD5_CTX *context)
+{
+  context->count[0] = context->count[1] = 0;
+  /* Load magic initialization constants.
+*/
+  context->state[0] = 0x67452301;
+  context->state[1] = 0xefcdab89;
+  context->state[2] = 0x98badcfe;
+  context->state[3] = 0x10325476;
+}
+
+/* MD5 block update operation. Continues an MD5 message-digest
+  operation, processing another message block, and updating the
+  context.
+ */
+void MD5Update (
+    MD5_CTX *context,                                        /* context */
+    unsigned char *input,                                /* input block */
+    unsigned int inputLen)                     /* length of input block */
+{
+  unsigned int i, index, partLen;
+
+  /* Compute number of bytes mod 64 */
+  index = (unsigned int)((context->count[0] >> 3) & 0x3F);
+
+  /* Update number of bits */
+  if ((context->count[0] += ((UINT4)inputLen << 3))
+   < ((UINT4)inputLen << 3))
+ context->count[1]++;
+  context->count[1] += ((UINT4)inputLen >> 29);
+
+  partLen = 64 - index;
+
+  /* Transform as many times as possible.
+*/
+  if (inputLen >= partLen) {
+ MD5_memcpy
+   ((POINTER)&context->buffer[index], (POINTER)input, partLen);
+ MD5Transform (context->state, context->buffer);
+
+ for (i = partLen; i + 63 < inputLen; i += 64)
+   MD5Transform (context->state, &input[i]);
+
+ index = 0;
+  }
+  else
+ i = 0;
+
+  /* Buffer remaining input */
+  MD5_memcpy
+ ((POINTER)&context->buffer[index], (POINTER)&input[i],
+  inputLen-i);
+}
+
+/* MD5 finalization. Ends an MD5 message-digest operation, writing the
+  the message digest and zeroizing the context.
+ */
+void MD5Final (
+    unsigned char digest[16],                         /* message digest */
+    MD5_CTX *context)                                       /* context */
+{
+  unsigned char bits[8];
+  unsigned int index, padLen;
+
+  /* Save number of bits */
+  Encode (bits, context->count, 8);
+
+  /* Pad out to 56 mod 64.
+*/
+  index = (unsigned int)((context->count[0] >> 3) & 0x3f);
+  padLen = (index < 56) ? (56 - index) : (120 - index);
+  MD5Update (context, PADDING, padLen);
+
+  /* Append length (before padding) */
+  MD5Update (context, bits, 8);
+
+  /* Store state in digest */
+  Encode (digest, context->state, 16);
+
+  /* Zeroize sensitive information.
+*/
+  MD5_memset ((POINTER)context, 0, sizeof (*context));
+}
+
+/* MD5 basic transformation. Transforms state based on block.
+ */
+static void MD5Transform (state, block)
+UINT4 state[4];
+unsigned char block[64];
+{
+  UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+
+  Decode (x, block, 64);
+
+  /* Round 1 */
+  FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
+  FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
+  FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
+  FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
+  FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
+  FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
+  FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
+  FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
+  FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
+  FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
+  FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
+  FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
+  FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
+  FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
+  FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
+  FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
+
+ /* Round 2 */
+  GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
+  GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
+  GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
+  GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
+  GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
+  GG (d, a, b, c, x[10], S22,  0x2441453); /* 22 */
+  GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
+  GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
+  GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
+  GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
+  GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
+  GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
+  GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
+  GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
+  GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
+  GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
+
+  /* Round 3 */
+  HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
+  HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
+  HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
+  HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
+  HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
+  HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
+  HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
+  HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
+  HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
+  HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
+  HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
+  HH (b, c, d, a, x[ 6], S34,  0x4881d05); /* 44 */
+  HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
+  HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
+  HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
+  HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
+
+  /* Round 4 */
+  II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
+  II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
+  II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
+  II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
+  II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
+  II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
+  II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
+  II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
+  II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
+  II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
+  II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
+  II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
+  II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
+  II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
+  II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
+  II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
+
+  state[0] += a;
+  state[1] += b;
+  state[2] += c;
+  state[3] += d;
+
+  /* Zeroize sensitive information.
+ */
+  MD5_memset ((POINTER)x, 0, sizeof (x));
+}
+
+/* Encodes input (UINT4) into output (unsigned char). Assumes len is
+  a multiple of 4.
+ */
+static void Encode (output, input, len)
+unsigned char *output;
+UINT4 *input;
+unsigned int len;
+{
+  unsigned int i, j;
+
+  for (i = 0, j = 0; j < len; i++, j += 4) {
+ output[j] = (unsigned char)(input[i] & 0xff);
+ output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
+ output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
+ output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
+  }
+}
+
+/* Decodes input (unsigned char) into output (UINT4). Assumes len is
+  a multiple of 4.
+ */
+static void Decode (output, input, len)
+UINT4 *output;
+unsigned char *input;
+unsigned int len;
+{
+  unsigned int i, j;
+
+  for (i = 0, j = 0; j < len; i++, j += 4)
+ output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
+   (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
+}
+
+/* Note: Replace "for loop" with standard memcpy if possible.
+ */
+
+static void MD5_memcpy (output, input, len)
+POINTER output;
+POINTER input;
+unsigned int len;
+{
+  unsigned int i;
+
+  for (i = 0; i < len; i++)
+ output[i] = input[i];
+}
+
+/* Note: Replace "for loop" with standard memset if possible.
+ */
+static void MD5_memset (output, value, len)
+POINTER output;
+int value;
+unsigned int len;
+{
+  unsigned int i;
+
+  for (i = 0; i < len; i++)
+ ((char *)output)[i] = (char)value;
+}
+
+void MD5(unsigned char *message,unsigned long message_length,unsigned char *md)
+{
+  MD5_CTX state;
+
+  MD5Init(&state);
+  MD5Update(&state,message,message_length);
+  MD5Final(md,&state);
+
+  return;
+}
Index: conf.c
===================================================================
--- conf.c	(revision 471)
+++ conf.c	(working copy)
@@ -93,6 +93,8 @@
     stream_maxrate:                 1,
     stream_localhost:               1,
     stream_limit:                   0,
+    stream_auth_method:             0,
+    stream_authentication:          NULL,
     webcontrol_port:                0,
     webcontrol_localhost:           1,
     webcontrol_html_output:         1,
@@ -1004,6 +1006,26 @@
     print_int
     },
     {
+    "stream_auth_method",
+    "# Set the authentication method (default: 0)\n"
+    "# 0 = disabled \n"
+    "# 1 = Basic authentication\n"
+    "# 2 = MD5 digest (the safer authentication)\n",
+    0,
+    CONF_OFFSET(stream_auth_method),
+    copy_int,
+    print_int
+    },
+    {
+    "stream_authentication",
+    "# Authentication for the http based control. Syntax username:password\n"
+    "# Default: not defined (Disabled)",
+    1,
+    CONF_OFFSET(stream_authentication),
+    copy_string,
+    print_string
+    },
+    {
     "webcontrol_port",
     "\n############################################################\n"
     "# HTTP Based Control\n"
@@ -2179,7 +2201,7 @@
     printf("-c config\t\tFull path and filename of config file.\n");
     printf("-d level\t\tDebug mode.\n");
     printf("-p process_id_file\tFull path and filename of process id file (pid file).\n");
-    printf("-l log file \tFull path and filename of log file.\n");
+    printf("-l log file \t\tFull path and filename of log file.\n");
     printf("-h\t\t\tShow this screen.\n");
     printf("\n");
     printf("Motion is configured using a config file only. If none is supplied,\n");
Index: conf.h
===================================================================
--- conf.h	(revision 471)
+++ conf.h	(working copy)
@@ -69,6 +69,8 @@
     int stream_maxrate;
     int stream_localhost;
     int stream_limit;
+    int stream_auth_method;
+    const char *stream_authentication;
     int webcontrol_port;
     int webcontrol_localhost;
     int webcontrol_html_output;
Index: md5.h
===================================================================
--- md5.h	(revision 0)
+++ md5.h	(revision 0)
@@ -0,0 +1,74 @@
+/* MD5.H - header file for MD5C.C
+   taken from RFC 1321
+ */
+
+#ifndef MD5_H
+#define MD5_H
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+rights reserved.
+
+License to copy and use this software is granted provided that it
+is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+Algorithm" in all material mentioning or referencing this software
+or this function.
+
+License is also granted to make and use derivative works provided
+that such works are identified as "derived from the RSA Data
+Security, Inc. MD5 Message-Digest Algorithm" in all material
+mentioning or referencing the derived work.
+
+RSA Data Security, Inc. makes no representations concerning either
+the merchantability of this software or the suitability of this
+software for any particular purpose. It is provided "as is"
+without express or implied warranty of any kind.
+
+These notices must be retained in any copies of any part of this
+documentation and/or software.
+ */
+
+/* GLOBAL.H - RSAREF types and constants
+ */
+
+/* PROTOTYPES should be set to one if and only if the compiler supports
+  function argument prototyping.
+The following makes PROTOTYPES default to 0 if it has not already
+  been defined with C compiler flags.
+ */
+
+#ifndef PROTOTYPES
+#define PROTOTYPES 0
+#endif
+
+/* POINTER defines a generic pointer type */
+typedef unsigned char *POINTER;
+
+/* UINT2 defines a two byte word */
+typedef unsigned short int UINT2;
+
+/* UINT4 defines a four byte word */
+typedef unsigned int UINT4;
+
+/* PROTO_LIST is defined depending on how PROTOTYPES is defined above.
+If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it
+  returns an empty list.
+ */
+#if PROTOTYPES
+#define PROTO_LIST(list) list
+#else
+#define PROTO_LIST(list) ()
+#endif
+
+/* MD5 context. */
+typedef struct {
+  UINT4 state[4];                                   /* state (ABCD) */
+  UINT4 count[2];        /* number of bits, modulo 2^64 (lsb first) */
+  unsigned char buffer[64];                         /* input buffer */
+} MD5_CTX;
+
+void MD5Init(MD5_CTX *);
+void MD5Update(MD5_CTX *, unsigned char *, unsigned int);
+void MD5Final(unsigned char [16], MD5_CTX *);
+void MD5(unsigned char *message, unsigned long message_length, unsigned char *md);
+
+#endif // MD5_H
Index: motion-dist.conf.in
===================================================================
--- motion-dist.conf.in	(revision 471)
+++ motion-dist.conf.in	(working copy)
@@ -461,7 +461,17 @@
 # Actual stream rate is the smallest of the numbers framerate and stream_maxrate
 stream_limit 0
 
+# Set the authentication method (default: 0)
+# 0 = disabled 
+# 1 = Basic authentication
+# 2 = MD5 digest (the safer authentication)
+stream_auth_method 0
 
+# Authentication for the http based control. Syntax username:password
+# Default: not defined (Disabled)
+; stream_authentication username:password
+
+
 ############################################################
 # HTTP Based Control
 ############################################################
Index: configure
===================================================================
--- configure	(revision 471)
+++ configure	(working copy)
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.64 for motion trunk-r471.
+# Generated by GNU Autoconf 2.64 for motion trunk-r472.
 #
 # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
 # 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software
@@ -546,8 +546,8 @@
 # Identity of this package.
 PACKAGE_NAME='motion'
 PACKAGE_TARNAME='motion'
-PACKAGE_VERSION='trunk-r471'
-PACKAGE_STRING='motion trunk-r471'
+PACKAGE_VERSION='trunk-r472'
+PACKAGE_STRING='motion trunk-r472'
 PACKAGE_BUGREPORT=''
 PACKAGE_URL=''
 
@@ -1210,7 +1210,7 @@
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures motion trunk-r471 to adapt to many kinds of systems.
+\`configure' configures motion trunk-r472 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1271,7 +1271,7 @@
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of motion trunk-r471:";;
+     short | recursive ) echo "Configuration of motion trunk-r472:";;
    esac
   cat <<\_ACEOF
 
@@ -1412,7 +1412,7 @@
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-motion configure trunk-r471
+motion configure trunk-r472
 generated by GNU Autoconf 2.64
 
 Copyright (C) 2009 Free Software Foundation, Inc.
@@ -2009,7 +2009,7 @@
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by motion $as_me trunk-r471, which was
+It was created by motion $as_me trunk-r472, which was
 generated by GNU Autoconf 2.64.  Invocation command line was
 
   $ $0 $@
@@ -5790,7 +5790,7 @@
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by motion $as_me trunk-r471, which was
+This file was extended by motion $as_me trunk-r472, which was
 generated by GNU Autoconf 2.64.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -5850,7 +5850,7 @@
 _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_version="\\
-motion config.status trunk-r471
+motion config.status trunk-r472
 configured by $0, generated by GNU Autoconf 2.64,
   with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
 
Index: stream.c
===================================================================
--- stream.c	(revision 471)
+++ stream.c	(working copy)
@@ -18,6 +18,7 @@
  *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+#include "md5.h"
 #include "picture.h"
 #include <sys/socket.h>
 #include <netinet/in.h>
@@ -26,7 +27,643 @@
 #include <ctype.h>
 #include <sys/fcntl.h>
 
+#define STREAM_REALM       "Motion Stream Security Access"
+#define KEEP_ALIVE_TIMEOUT 100
 
+typedef void* (*auth_handler)(void*);
+struct auth_param {
+    struct context *cnt;
+    int sock;
+    int sock_flags;
+    int* thread_count;
+    struct config *conf;
+};
+
+pthread_mutex_t stream_auth_mutex;
+
+static int set_sock_timeout(int sock, int sec)
+{
+    struct timeval tv;
+
+    tv.tv_sec = sec;
+    tv.tv_usec = 0;
+
+    if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char*) &tv, sizeof(tv))) {
+        motion_log(LOG_ERR, 1, "%s: set socket timeout failed", __FUNCTION__);
+        return 1;
+    }
+    return 0;
+}
+
+static int read_http_request(int sock, char* buffer, int buflen, char* uri, int uri_len)
+{
+    int nread = 0;
+    int ret,readb = 1;
+    char method[10] = {'\0'};
+    char url[512] = {'\0'};
+    char protocol[10] = {'\0'};
+
+    static const char *bad_request_response_raw =
+        "HTTP/1.0 400 Bad Request\r\n"
+        "Content-type: text/plain\r\n\r\n"
+        "Bad Request\n";
+
+    static const char *bad_method_response_template_raw =
+        "HTTP/1.0 501 Method Not Implemented\r\n"
+        "Content-type: text/plain\r\n\r\n"
+        "Method Not Implemented\n";
+
+    static const char *timeout_response_template_raw =
+        "HTTP/1.0 408 Request Timeout\r\n"
+        "Content-type: text/plain\r\n\r\n"
+        "Request Timeout\n";
+
+    buffer[0] = '\0';
+  
+    while ((strstr(buffer, "\r\n\r\n") == NULL) && (readb != 0) && (nread < buflen)) {
+  
+        readb = read(sock, buffer+nread, buflen - nread);
+
+        if (readb == -1) { 
+            nread = -1;
+            break;
+        }
+
+        nread += readb;
+
+        if (nread > buflen) { 
+            motion_log(LOG_ERR, 1, "%s: motion-stream End buffer reached waiting "
+                      "for buffer ending", __FUNCTION__);
+            break;
+        }
+
+        buffer[nread] = '\0';
+    }
+
+    /* Make sure the last read didn't fail.  If it did, there's a
+    problem with the connection, so give up.  */
+    if (nread == -1) {
+        if(errno == EAGAIN) { // Timeout
+            ret = write(sock, timeout_response_template_raw, strlen(timeout_response_template_raw));
+	        return 1;
+        }
+    
+        motion_log(LOG_ERR, 1, "%s: motion-stream READ give up!", __FUNCTION__);
+        return 1;
+    }
+  
+    ret = sscanf(buffer, "%9s %511s %9s", method, url, protocol);
+    
+    if (ret != 3) { 
+        ret=write(sock, bad_request_response_raw, sizeof(bad_request_response_raw));
+        return 1;
+    }
+
+    /* Check Protocol */
+    if (strcmp(protocol, "HTTP/1.0") && strcmp (protocol, "HTTP/1.1")) { 
+        /* We don't understand this protocol.  Report a bad response.  */
+        ret = write(sock, bad_request_response_raw, sizeof(bad_request_response_raw));
+        return 1;
+    }
+
+    if (strcmp (method, "GET")) {
+        /* This server only implements the GET method.  If client
+        uses other method, report the failure.  */
+        char response[1024];
+        snprintf(response, sizeof(response), bad_method_response_template_raw, method);
+        ret = write(sock, response, strlen (response));
+
+        return 1;
+    }
+
+    if(uri)
+        strncpy(uri, url, uri_len);
+
+    return 0;
+}
+
+static void stream_add_client(struct stream *list, int sc);
+
+static void* handle_basic_auth(void* param)
+{
+    struct auth_param *p = (struct auth_param*)param;
+    char buffer[1024] = {'\0'};
+    ssize_t length = 1023;
+    char *auth, *h, *authentication;
+    int ret;
+    static const char *request_auth_response_template=
+        "HTTP/1.0 401 Authorization Required\r\n"
+        "Server: Motion/"VERSION"\r\n"
+        "Max-Age: 0\r\n"
+        "Expires: 0\r\n"
+        "Cache-Control: no-cache, private\r\n"
+        "Pragma: no-cache\r\n"
+        "WWW-Authenticate: Basic realm=\""STREAM_REALM"\"\r\n\r\n";
+  
+    pthread_mutex_lock(&stream_auth_mutex);
+    p->thread_count++;
+    pthread_mutex_unlock(&stream_auth_mutex);
+
+    if (read_http_request(p->sock,buffer, length, NULL, 0))
+        goto Invalid_Request;
+    
+
+    auth = strstr(buffer, "Authorization: Basic");
+    
+    if (!auth)
+        goto Error;
+
+    auth += sizeof("Authorization: Basic");
+    h = strstr(auth, "\r\n");
+  
+    if(!h)
+        goto Error;
+
+    *h='\0';
+
+    if (p->conf->stream_authentication != NULL) {
+  
+        char *userpass = NULL;
+        size_t auth_size = strlen(p->conf->stream_authentication);
+
+        authentication = (char *) mymalloc(BASE64_LENGTH(auth_size) + 1);
+        userpass = mymalloc(auth_size + 4);
+        /* base64_encode can read 3 bytes after the end of the string, initialize it */
+        memset(userpass, 0, auth_size + 4);
+        strcpy(userpass, p->conf->stream_authentication);
+        base64_encode(userpass, authentication, auth_size);
+        free(userpass);
+
+        if (strcmp(auth, authentication)) {
+            free(authentication);
+            goto Error;
+        }
+        free(authentication);
+    }
+
+    // OK - Access
+
+    /* Set socket to non blocking */
+    if (fcntl(p->sock, F_SETFL, p->sock_flags) < 0) {
+        motion_log(LOG_ERR, 1, "%s: fcntl", __FUNCTION__);
+        goto Error;
+    }
+
+    /* lock the mutex */
+    pthread_mutex_lock(&stream_auth_mutex);
+  
+    stream_add_client(&p->cnt->stream, p->sock);
+    p->cnt->stream_count++;
+    p->thread_count--;
+
+    /* unlock the mutex */
+    pthread_mutex_unlock(&stream_auth_mutex);
+
+    free(p);
+    pthread_exit(NULL);
+
+Error:
+    ret = write(p->sock, request_auth_response_template, strlen (request_auth_response_template));
+
+Invalid_Request:
+    close(p->sock);
+
+    pthread_mutex_lock(&stream_auth_mutex);
+    p->thread_count--;
+    pthread_mutex_unlock(&stream_auth_mutex);
+
+    free(p);
+    pthread_exit(NULL);
+}
+
+
+/* calculate H(A1) as per HTTP Digest spec -- taken from RFC 2617*/
+#define HASHLEN 16
+typedef char HASH[HASHLEN];
+#define HASHHEXLEN 32
+typedef char HASHHEX[HASHHEXLEN+1];
+#define IN
+#define OUT
+
+static void CvtHex(IN HASH Bin, OUT HASHHEX Hex)
+{
+    unsigned short i;
+    unsigned char j;
+
+    for (i = 0; i < HASHLEN; i++) {
+        j = (Bin[i] >> 4) & 0xf;
+        if (j <= 9)
+            Hex[i*2] = (j + '0');
+         else
+            Hex[i*2] = (j + 'a' - 10);
+        j = Bin[i] & 0xf;
+        if (j <= 9)
+            Hex[i*2+1] = (j + '0');
+         else
+            Hex[i*2+1] = (j + 'a' - 10);
+    };
+    Hex[HASHHEXLEN] = '\0';
+};
+
+/* calculate H(A1) as per spec */
+static void DigestCalcHA1(
+    IN char * pszAlg,
+    IN char * pszUserName,
+    IN char * pszRealm,
+    IN char * pszPassword,
+    IN char * pszNonce,
+    IN char * pszCNonce,
+    OUT HASHHEX SessionKey
+    )
+{
+    MD5_CTX Md5Ctx;
+    HASH HA1;
+
+    MD5Init(&Md5Ctx);
+    MD5Update(&Md5Ctx, (unsigned char *)pszUserName, strlen(pszUserName));
+    MD5Update(&Md5Ctx, (unsigned char *)":", 1);
+    MD5Update(&Md5Ctx, (unsigned char *)pszRealm, strlen(pszRealm));
+    MD5Update(&Md5Ctx, (unsigned char *)":", 1);
+    MD5Update(&Md5Ctx, (unsigned char *)pszPassword, strlen(pszPassword));
+    MD5Final((unsigned char *)HA1, &Md5Ctx);
+
+    if (strcmp(pszAlg, "md5-sess") == 0) {
+        MD5Init(&Md5Ctx);
+        MD5Update(&Md5Ctx, (unsigned char *)HA1, HASHLEN);
+        MD5Update(&Md5Ctx, (unsigned char *)":", 1);
+        MD5Update(&Md5Ctx, (unsigned char *)pszNonce, strlen(pszNonce));
+        MD5Update(&Md5Ctx, (unsigned char *)":", 1);
+        MD5Update(&Md5Ctx, (unsigned char *)pszCNonce, strlen(pszCNonce));
+        MD5Final((unsigned char *)HA1, &Md5Ctx);
+    };
+    CvtHex(HA1, SessionKey);
+};
+
+/* calculate request-digest/response-digest as per HTTP Digest spec */
+static void DigestCalcResponse(
+    IN HASHHEX HA1,           /* H(A1) */
+    IN char * pszNonce,       /* nonce from server */
+    IN char * pszNonceCount,  /* 8 hex digits */
+    IN char * pszCNonce,      /* client nonce */
+    IN char * pszQop,         /* qop-value: "", "auth", "auth-int" */
+    IN char * pszMethod,      /* method from the request */
+    IN char * pszDigestUri,   /* requested URL */
+    IN HASHHEX HEntity,       /* H(entity body) if qop="auth-int" */
+    OUT HASHHEX Response      /* request-digest or response-digest */
+    )
+{
+    MD5_CTX Md5Ctx;
+    HASH HA2;
+    HASH RespHash;
+    HASHHEX HA2Hex;
+    
+    // calculate H(A2)
+    MD5Init(&Md5Ctx);
+    MD5Update(&Md5Ctx, (unsigned char *)pszMethod, strlen(pszMethod));
+    MD5Update(&Md5Ctx, (unsigned char *)":", 1);
+    MD5Update(&Md5Ctx, (unsigned char *)pszDigestUri, strlen(pszDigestUri));
+  
+    if (strcmp(pszQop, "auth-int") == 0) {
+        MD5Update(&Md5Ctx, (unsigned char *)":", 1);
+        MD5Update(&Md5Ctx, (unsigned char *)HEntity, HASHHEXLEN);
+    }
+    MD5Final((unsigned char *)HA2, &Md5Ctx);
+    CvtHex(HA2, HA2Hex);
+
+    // calculate response
+    MD5Init(&Md5Ctx);
+    MD5Update(&Md5Ctx, (unsigned char *)HA1, HASHHEXLEN);
+    MD5Update(&Md5Ctx, (unsigned char *)":", 1);
+    MD5Update(&Md5Ctx, (unsigned char *)pszNonce, strlen(pszNonce));
+    MD5Update(&Md5Ctx, (unsigned char *)":", 1);
+
+    if (*pszQop) {
+        MD5Update(&Md5Ctx, (unsigned char *)pszNonceCount, strlen(pszNonceCount));
+        MD5Update(&Md5Ctx, (unsigned char *)":", 1);
+        MD5Update(&Md5Ctx, (unsigned char *)pszCNonce, strlen(pszCNonce));
+        MD5Update(&Md5Ctx, (unsigned char *)":", 1);
+        MD5Update(&Md5Ctx, (unsigned char *)pszQop, strlen(pszQop));
+        MD5Update(&Md5Ctx, (unsigned char *)":", 1);
+    }
+    MD5Update(&Md5Ctx, (unsigned char *)HA2Hex, HASHHEXLEN);
+    MD5Final((unsigned char *)RespHash, &Md5Ctx);
+    CvtHex(RespHash, Response);
+};
+
+
+static void* handle_md5_digest(void* param)
+{
+    struct auth_param *p = (struct auth_param*)param;
+    char buffer[1024] = {'\0'};
+    ssize_t length = 1023;
+    char *auth, *h, *username, *realm, *uri, *nonce, *response;
+    int username_len, realm_len, uri_len, nonce_len, response_len;
+#define SERVER_NONCE_LEN 17
+    char server_nonce[SERVER_NONCE_LEN];
+#define SERVER_URI_LEN 512
+    char server_uri[SERVER_URI_LEN];
+    char* server_user = NULL, *server_pass = NULL;
+    int ret;
+    unsigned int rand1,rand2;
+    HASHHEX HA1;
+    HASHHEX HA2 = "";
+    HASHHEX server_response;
+    static const char *request_auth_response_template=
+        "HTTP/1.0 401 Authorization Required\r\n"
+        "Server: Motion/"VERSION"\r\n"
+        "Max-Age: 0\r\n"
+        "Expires: 0\r\n"
+        "Cache-Control: no-cache, private\r\n"
+        "Pragma: no-cache\r\n"
+        "WWW-Authenticate: Digest";
+    static const char *auth_failed_html_template=
+        "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
+        "<HTML><HEAD>\r\n"
+        "<TITLE>401 Authorization Required</TITLE>\r\n"
+        "</HEAD><BODY>\r\n"
+        "<H1>Authorization Required</H1>\r\n"
+        "This server could not verify that you are authorized to access the document "
+        "requested.  Either you supplied the wrong credentials (e.g., bad password), "
+        "or your browser doesn't understand how to supply the credentials required.\r\n"
+        "</BODY></HTML>\r\n";
+    static const char *internal_error_template=
+        "HTTP/1.0 500 Internal Server Error\r\n"
+        "Server: Motion/"VERSION"\r\n"
+        "Content-Type: text/html\r\n"
+        "Connection: Close\r\n\r\n"
+        "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
+        "<HTML><HEAD>\r\n"
+        "<TITLE>500 Internal Server Error</TITLE>\r\n"
+        "</HEAD><BODY>\r\n"
+        "<H1>500 Internal Server Error</H1>\r\n"
+        "</BODY></HTML>\r\n";
+  
+    pthread_mutex_lock(&stream_auth_mutex);
+    p->thread_count++;
+    pthread_mutex_unlock(&stream_auth_mutex);
+
+    set_sock_timeout(p->sock, KEEP_ALIVE_TIMEOUT);
+    srand(time(NULL));
+    rand1 = (unsigned int)(42000000.0 * rand() / (RAND_MAX + 1.0));
+    rand2 = (unsigned int)(42000000.0 * rand() / (RAND_MAX + 1.0));
+    snprintf(server_nonce, SERVER_NONCE_LEN, "%08x%08x", rand1, rand2);
+  
+    if (!p->conf->stream_authentication) {
+        motion_log(LOG_ERR, 1, "%s: Error no authentication data", __FUNCTION__);
+        goto InternalError;
+    }
+    h = strstr(p->conf->stream_authentication, ":");
+  
+    if (!h) {
+        motion_log(LOG_ERR, 1, "%s: Error no authentication data (no ':' found)", __FUNCTION__);
+        goto InternalError;
+    }
+
+    server_user = (char*)malloc((h - p->conf->stream_authentication) + 1);
+    server_pass = (char*)malloc(strlen(h) + 1);
+  
+    if (!server_user || !server_pass) {
+        motion_log(LOG_ERR, 1, "%s: Error malloc failed", __FUNCTION__);
+        goto InternalError;
+    }
+
+    strncpy(server_user, p->conf->stream_authentication, h-p->conf->stream_authentication);
+    server_user[h - p->conf->stream_authentication] = '\0';
+    strncpy(server_pass, h + 1, strlen(h + 1));
+    server_pass[strlen(h + 1)] = '\0';
+
+    while(1) {
+        if(read_http_request(p->sock, buffer, length, server_uri, SERVER_URI_LEN - 1))
+            goto Invalid_Request;
+    
+
+        auth = strstr(buffer, "Authorization: Digest");
+        if(!auth)
+            goto Error;
+
+        auth += sizeof("Authorization: Digest");
+        h = strstr(auth, "\r\n");
+    
+        if (!h)
+            goto Error;
+        *h = '\0';
+
+        // Username
+        h=strstr(auth, "username=\"");
+        
+        if (!h)
+            goto Error;
+    
+        username = h + 10;
+        h = strstr(username + 1, "\"");
+   
+        if (!h)
+            goto Error;
+        
+        username_len = h - username;
+
+        // Realm
+        h = strstr(auth, "realm=\"");
+        if (!h)
+            goto Error;
+    
+        realm = h + 7;
+        h = strstr(realm + 1, "\"");
+    
+        if (!h)
+            goto Error;
+        
+        realm_len = h - realm;
+
+        // URI
+        h = strstr(auth, "uri=\"");
+        
+        if (!h)
+            goto Error;
+    
+        uri = h + 5;
+        h = strstr(uri + 1, "\"");
+    
+        if (!h)
+            goto Error;
+    
+        uri_len = h - uri;
+
+        // Nonce
+        h = strstr(auth, "nonce=\"");
+        
+        if (!h)
+            goto Error;
+        
+        nonce = h + 7;
+        h = strstr(nonce + 1, "\"");
+    
+        if (!h)
+            goto Error;
+    
+        nonce_len = h - nonce;
+
+        // Response
+        h = strstr(auth, "response=\"");
+    
+        if (!h)
+            goto Error;
+    
+        response = h + 10;
+        h = strstr(response + 1, "\"");
+    
+        if (!h)
+            goto Error;
+    
+        response_len = h - response;
+
+        username[username_len] = '\0';
+        realm[realm_len] = '\0';
+        uri[uri_len] = '\0';
+        nonce[nonce_len] = '\0';
+        response[response_len] = '\0';
+
+        DigestCalcHA1((char*)"md5", server_user, (char*)STREAM_REALM, server_pass, (char*)server_nonce, (char*)NULL, HA1);
+        DigestCalcResponse(HA1, server_nonce, NULL, NULL, (char*)"", (char*)"GET", server_uri, HA2, server_response);
+
+        if (strcmp(server_response, response) == 0)
+            break;
+Error:
+        rand1 = (unsigned int)(42000000.0 * rand() / (RAND_MAX + 1.0));
+        rand2 = (unsigned int)(42000000.0 * rand() / (RAND_MAX + 1.0));
+        snprintf(server_nonce, SERVER_NONCE_LEN, "%08x%08x", rand1, rand2);
+        snprintf(buffer, length, "%s realm=\""STREAM_REALM"\", nonce=\"%s\"\r\n"
+	            "Content-Type: text/html\r\n"
+	            "Keep-Alive: timeout=%i\r\n"
+	            "Connection: keep-alive\r\n"
+	            "Content-Length: %li\r\n\r\n",
+	            request_auth_response_template, server_nonce,
+	            KEEP_ALIVE_TIMEOUT, strlen(auth_failed_html_template));
+        ret = write(p->sock, buffer, strlen(buffer));
+        ret = write(p->sock, auth_failed_html_template, strlen(auth_failed_html_template));
+    }
+
+    // OK - Access
+
+    /* Set socket to non blocking */
+    if (fcntl(p->sock, F_SETFL, p->sock_flags) < 0) {
+        motion_log(LOG_ERR, 1, "%s: fcntl", __FUNCTION__);
+        goto Error;
+    }
+
+    if(server_user)
+        free(server_user);
+  
+    if(server_pass)
+        free(server_pass);
+
+    /* lock the mutex */
+    pthread_mutex_lock(&stream_auth_mutex);
+
+    stream_add_client(&p->cnt->stream, p->sock);
+    p->cnt->stream_count++;
+
+    p->thread_count--;
+    /* unlock the mutex */
+    pthread_mutex_unlock(&stream_auth_mutex);
+
+    free(p);
+    pthread_exit(NULL);
+  
+InternalError:
+    if(server_user)
+        free(server_user);
+  
+    if(server_pass)
+        free(server_pass);
+
+    ret = write(p->sock, internal_error_template, strlen(internal_error_template));
+
+Invalid_Request:
+    close(p->sock);
+
+    pthread_mutex_lock(&stream_auth_mutex);
+    p->thread_count--;
+    pthread_mutex_unlock(&stream_auth_mutex);
+
+    free(p);
+    pthread_exit(NULL);
+}
+
+
+static void do_client_auth(struct context *cnt, int sc)
+{
+    pthread_t thread_id;
+    pthread_attr_t attr;
+    auth_handler handle_func;
+    struct auth_param* handle_param = NULL;
+    int flags;
+    static int first_call = 0;
+    static int thread_count = 0;
+  
+    if(first_call == 0) {
+        first_call = 1;
+        /* Initialize the mutex */
+        pthread_mutex_init(&stream_auth_mutex, NULL);
+    }
+  
+    switch(cnt->conf.stream_auth_method)
+    {
+    case 1: // Basic
+	  handle_func = handle_basic_auth;
+	  break;
+    case 2: // MD5 Digest
+	  handle_func = handle_md5_digest;
+	  break;
+    default:
+	  motion_log(LOG_ERR, 1, "%s: Error unknown stream authentication method", __FUNCTION__);
+	  goto Error;
+	  break;
+    }
+  
+    handle_param = mymalloc(sizeof(struct auth_param));
+    handle_param->cnt = cnt;
+    handle_param->sock = sc;
+    handle_param->conf = &cnt->conf;
+    handle_param->thread_count = &thread_count;
+  
+    /* Set socket to blocking */
+    if ((flags = fcntl(sc, F_GETFL, 0)) < 0) {
+        motion_log(LOG_ERR, 1, "%s: fcntl", __FUNCTION__);
+        goto Error;
+    }
+    handle_param->sock_flags = flags;
+
+    if (fcntl(sc, F_SETFL, flags & (~O_NONBLOCK)) < 0) {
+        motion_log(LOG_ERR, 1, "%s: fcntl", __FUNCTION__);
+        goto Error;
+    }
+
+    if (thread_count >= DEF_MAXSTREAMS)
+        goto Error;
+    
+    if (pthread_attr_init(&attr)) {
+        motion_log(LOG_ERR, 1, "%s: Error pthread_attr_init", __FUNCTION__);
+        goto Error;
+    }
+
+    if (pthread_create(&thread_id, &attr, handle_func, handle_param)) {
+        motion_log(LOG_ERR, 1, "%s: Error pthread_create", __FUNCTION__);
+        goto Error;
+    }
+    pthread_detach(thread_id);
+
+    if (pthread_attr_destroy(&attr))
+        motion_log(LOG_ERR, 1, "%s: Error pthread_attr_destroy", __FUNCTION__);
+
+    return;
+  
+Error:
+    close(sc);
+    if(handle_param)
+        free(handle_param);
+}
+
 /* This function sets up a TCP/IP socket for incoming requests. It is called only during
  * initialisation of Motion from the function stream_init
  * The function sets up a a socket on the port number given by _port_.
@@ -39,10 +676,9 @@
     struct addrinfo hints, *res = NULL, *ressave = NULL;
     char portnumber[10], hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
 
-
     snprintf(portnumber, sizeof(portnumber), "%u", port);
+    memset(&hints, 0, sizeof(struct addrinfo));
 
-    memset(&hints, 0, sizeof(struct addrinfo));
     /* Use the AI_PASSIVE flag, which indicates we are using this address for a listen() */
     hints.ai_flags = AI_PASSIVE;
 #if defined(BSD)
@@ -419,12 +1055,21 @@
      * to each other.
      */
     if ((cnt->stream_count < DEF_MAXSTREAMS) &&
-        (select(sl+1, &fdread, NULL, NULL, &timeout)>0)) {
+        (select(sl + 1, &fdread, NULL, NULL, &timeout) > 0)) {
         sc = http_acceptsock(sl);
-        stream_add_client(&cnt->stream, sc);
-        cnt->stream_count++;
+	    if (cnt->conf.stream_auth_method == 0) {
+		    stream_add_client(&cnt->stream, sc);
+            cnt->stream_count++;
+	    } else 	{
+	        do_client_auth(cnt, sc);
+	    }
     }
     
+    /* lock the mutex */
+    if (cnt->conf.stream_auth_method != 0)
+        pthread_mutex_lock(&stream_auth_mutex);
+
+    
     /* call flush to send any previous partial-sends which are waiting */
     stream_flush(&cnt->stream, &cnt->stream_count, cnt->conf.stream_limit);
     
@@ -488,5 +1133,9 @@
      */
     stream_flush(&cnt->stream, &cnt->stream_count, cnt->conf.stream_limit);
     
+    /* unlock the mutex */
+    if (cnt->conf.stream_auth_method != 0)
+        pthread_mutex_unlock(&stream_auth_mutex);
+
     return;
 }
