diff --exclude=.svn -Naur motion/conf.c motion_new/conf.c
--- motion/conf.c	2009-10-26 19:09:29.000000000 +0100
+++ motion_new/conf.c	2009-10-23 19:14:11.000000000 +0200
@@ -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");
diff --exclude=.svn -Naur motion/conf.h motion_new/conf.h
--- motion/conf.h	2009-10-26 19:09:29.000000000 +0100
+++ motion_new/conf.h	2009-10-20 17:38:00.000000000 +0200
@@ -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;
diff --exclude=.svn -Naur motion/Makefile.in motion_new/Makefile.in
--- motion/Makefile.in	2009-10-26 19:09:29.000000000 +0100
+++ motion_new/Makefile.in	2009-10-21 20:21:31.000000000 +0200
@@ -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
diff --exclude=.svn -Naur motion/md5.c motion_new/md5.c
--- motion/md5.c	1970-01-01 01:00:00.000000000 +0100
+++ motion_new/md5.c	2009-10-21 20:23:56.000000000 +0200
@@ -0,0 +1,346 @@
+/* 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 PROTO_LIST ((UINT4 [4], unsigned char [64]));
+static void Encode PROTO_LIST
+  ((unsigned char *, UINT4 *, unsigned int));
+static void Decode PROTO_LIST
+  ((UINT4 *, unsigned char *, unsigned int));
+static void MD5_memcpy PROTO_LIST ((POINTER, POINTER, unsigned int));
+static void MD5_memset PROTO_LIST ((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 (context)
+MD5_CTX *context;                                        /* 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 (context, input, inputLen)
+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 (digest, context)
+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;
+}
diff --exclude=.svn -Naur motion/md5.h motion_new/md5.h
--- motion/md5.h	1970-01-01 01:00:00.000000000 +0100
+++ motion_new/md5.h	2009-10-24 11:32:25.000000000 +0200
@@ -0,0 +1,77 @@
+/* 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 PROTO_LIST ((MD5_CTX *));
+void MD5Update PROTO_LIST
+  ((MD5_CTX *, unsigned char *, unsigned int));
+void MD5Final PROTO_LIST ((unsigned char [16], MD5_CTX *));
+
+void MD5(unsigned char *message, unsigned long message_length, unsigned char *md);
+
+#endif // MD5_H
diff --exclude=.svn -Naur motion/motion-dist.conf.in motion_new/motion-dist.conf.in
--- motion/motion-dist.conf.in	2009-10-26 19:09:29.000000000 +0100
+++ motion_new/motion-dist.conf.in	2009-10-23 19:19:41.000000000 +0200
@@ -461,6 +461,16 @@
 # 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
diff --exclude=.svn -Naur motion/stream.c motion_new/stream.c
--- motion/stream.c	2009-10-26 19:09:29.000000000 +0100
+++ motion_new/stream.c	2009-10-26 19:16:35.000000000 +0100
@@ -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,6 +27,632 @@
 #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, pszUserName, strlen(pszUserName));
+      MD5Update(&Md5Ctx, ":", 1);
+      MD5Update(&Md5Ctx, pszRealm, strlen(pszRealm));
+      MD5Update(&Md5Ctx, ":", 1);
+      MD5Update(&Md5Ctx, pszPassword, strlen(pszPassword));
+      MD5Final(HA1, &Md5Ctx);
+      if (strcmp(pszAlg, "md5-sess") == 0) {
+
+            MD5Init(&Md5Ctx);
+            MD5Update(&Md5Ctx, HA1, HASHLEN);
+            MD5Update(&Md5Ctx, ":", 1);
+            MD5Update(&Md5Ctx, pszNonce, strlen(pszNonce));
+            MD5Update(&Md5Ctx, ":", 1);
+            MD5Update(&Md5Ctx, pszCNonce, strlen(pszCNonce));
+            MD5Final(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, pszMethod, strlen(pszMethod));
+      MD5Update(&Md5Ctx, ":", 1);
+      MD5Update(&Md5Ctx, pszDigestUri, strlen(pszDigestUri));
+      if (strcmp(pszQop, "auth-int") == 0) {
+            MD5Update(&Md5Ctx, ":", 1);
+            MD5Update(&Md5Ctx, HEntity, HASHHEXLEN);
+      };
+      MD5Final(HA2, &Md5Ctx);
+       CvtHex(HA2, HA2Hex);
+
+      // calculate response
+      MD5Init(&Md5Ctx);
+      MD5Update(&Md5Ctx, HA1, HASHHEXLEN);
+      MD5Update(&Md5Ctx, ":", 1);
+      MD5Update(&Md5Ctx, pszNonce, strlen(pszNonce));
+      MD5Update(&Md5Ctx, ":", 1);
+
+      if (*pszQop) {
+          MD5Update(&Md5Ctx, pszNonceCount, strlen(pszNonceCount));
+          MD5Update(&Md5Ctx, ":", 1);
+          MD5Update(&Md5Ctx, pszCNonce, strlen(pszCNonce));
+          MD5Update(&Md5Ctx, ":", 1);
+          MD5Update(&Md5Ctx, pszQop, strlen(pszQop));
+          MD5Update(&Md5Ctx, ":", 1);
+      };
+      MD5Update(&Md5Ctx, HA2Hex, HASHHEXLEN);
+      MD5Final(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
@@ -421,10 +1048,22 @@
     if ((cnt->stream_count < DEF_MAXSTREAMS) &&
         (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 +1127,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;
 }
