On 5/10/07, Robert van der Boon <[EMAIL PROTECTED]> wrote:
Anatoly,
this is a known problem with the neon library. Please see my mail on this
list from 2007-03-28 for a patch for neon 0.26.3 to fix your problem:
http://mailman.webdav.org/pipermail/neon/2007-March/002421.html
Note that this patch has only been tested on my own systems. I do not
know if or when it will be incorporated into the neon library, as that is
upto the maintainer. If you use this patch could you report on your
results?
I can test this patch, but only in a form of SVN binaries. It is not
possible for me to gather all required stuff to compile patched
version myself. I wish I could review the patch, but I do not have
much experience with that stuff and is full of questions.
Concerning your initial mail [1] about the problem - can you explain
what a "non-idempotent message" is? I can see that in my case SVN
session is broken after keep-alive requests limit expires (as you
described) and connection breaks as a result.
You said that Neon doesn't clear SSPI token when connection expires.
From my logs it seems the same. However, I would like to understand
who is guilty - client or server side. From logs it is obvious that
when limit for keep-alive requests is reached server stops to accept
authorization token sending 401 header instead. Now client need to
react to this 401 header properly. Seems easy - just intercept 401,
recalculate token and give another response. But skipping client
reaction for now I wonder if SSPI protocol is either connection based
(token is valid until connection expires) or server could ask (by
giving out 401 response) to refresh the session at random time or even
per request? Possible solution depends on this fact.
If SSPI protocol is connection based the solution could be to ask for
new token every time a connection is made (so no dis-connection hook
is needed). If SSPI protocol allows to reauthenticate several times
during one keep-alive requests then there should by a way to intercept
401 requests and handle them transparently.
There is one more strange thing. During initial negotiation server sends headers
WWW-Authenticate: NTLM
together with 401 status, but when subsequent request fails (due to
keep-alive limit) and 401 header is sent to client - there is no such
header. So to proceed client seems to need to either retry request
without credentials to enter negotiation stage again or explicitly ask
server for a new token somehow.
In Neon code there are two concepts - SSPI session and SSPI token.
What is the difference? What is the difference between clearing SSPI
session and attaining new token? What could go wrong if sspi session
is dropped and recreated anew? As far as I can see it doesn't have any
negative effect on WebDAV session (which can exists across sspi
session), doesn't it?
P.S. Neon code contains many tabs and spaces at lineends, that's why
your patch is unusable in its current form. I've restored and
reattached it for future reference.
P.P.S. I wish there would be more convenient interface to this
mailling list. I gathered a list of links to keep the thread more
accessible for everyone.
:
[1] http://mailman.webdav.org/pipermail/neon/2007-January/002376.html
[2] http://mailman.webdav.org/pipermail/neon/2007-February/002385.html
[3] http://mailman.webdav.org/pipermail/neon/2007-February/002390.html
. one letter from Joe Orton is missing .
[4] http://mailman.webdav.org/pipermail/neon/2007-March/002421.html
. my report
[5] http://mailman.webdav.org/pipermail/neon/2007-April/002426.html
--
--anatoly t.
diff -cr neon-0.26.3\src\ne_auth.c neon-0.26.3.rjvdboon\src\ne_auth.c
*** neon-0.26.3\src\ne_auth.c Mon Jan 22 17:12:24 2007
--- neon-0.26.3.rjvdboon\src\ne_auth.c Thu Feb 15 12:58:13 2007
***************
*** 238,243 ****
--- 238,253 ----
int flags; /* AUTH_FLAG_* flags */
};
+ #ifdef HAVE_SSPI
+ static void clean_session_sspi(auth_session *sess)
+ {
+ if (sess->sspi_token) ne_free(sess->sspi_token);
+ sess->sspi_token = NULL;
+ ne_sspi_destroy_context(sess->sspi_context);
+ sess->sspi_context = NULL;
+ }
+ #endif
+
static void clean_session(auth_session *sess)
{
if (sess->basic) ne_free(sess->basic);
***************
*** 263,272 ****
sess->gssapi_token = NULL;
#endif
#ifdef HAVE_SSPI
! if (sess->sspi_token) ne_free(sess->sspi_token);
! sess->sspi_token = NULL;
! ne_sspi_destroy_context(sess->sspi_context);
! sess->sspi_context = NULL;
#endif
}
--- 273,279 ----
sess->gssapi_token = NULL;
#endif
#ifdef HAVE_SSPI
! clean_session_sspi(sess);
#endif
}
***************
*** 551,557 ****
#ifdef HAVE_SSPI
static char *request_sspi(auth_session *sess, struct auth_request *request)
{
! return ne_concat(sess->protocol->name, " ", sess->sspi_token, "\r\n",
NULL);
}
static int sspi_challenge(auth_session *sess, int attempt,
--- 558,566 ----
#ifdef HAVE_SSPI
static char *request_sspi(auth_session *sess, struct auth_request *request)
{
! if (sess->sspi_token == NULL)
! return NULL;
! return ne_concat(sess->protocol->name, " ", sess->sspi_token, "\r\n",
NULL);
}
static int sspi_challenge(auth_session *sess, int attempt,
***************
*** 1211,1217 ****
&& (sess->protocol->flags & AUTH_FLAG_VERIFY_NON40x) == 0) {
ret = sess->protocol->verify(areq, sess, auth_info_hdr);
}
! else if (sess->protocol
&& sess->protocol->flags && AUTH_FLAG_VERIFY_NON40x
&& (status->klass == 2 || status->klass == 3)
&& auth_hdr) {
--- 1220,1226 ----
&& (sess->protocol->flags & AUTH_FLAG_VERIFY_NON40x) == 0) {
ret = sess->protocol->verify(areq, sess, auth_info_hdr);
}
! else if (sess->protocol && sess->protocol->verify
&& sess->protocol->flags && AUTH_FLAG_VERIFY_NON40x
&& (status->klass == 2 || status->klass == 3)
&& auth_hdr) {
***************
*** 1234,1239 ****
--- 1243,1251 ----
else if (sess->sspi_context) {
ne_sspi_clear_context(sess->sspi_context);
}
+ if (sess->protocol && sess->protocol->id == NE_AUTH_NEGOTIATE &&
sess->sspi_context) {
+ ne_set_request_flag(req, NE_REQFLAG_IN_AUTH_NEGOTIATION,
ne_sspi_is_in_negotiation(sess->sspi_context));
+ }
#endif
return ret;
***************
*** 1269,1274 ****
--- 1281,1292 ----
clean_session(sess);
ne_free(sess);
}
+ static void ah_close_conn(void *cookie)
+ {
+ #ifdef HAVE_SSPI
+ clean_session_sspi((auth_session*)cookie);
+ #endif
+ }
static void auth_register(ne_session *sess, int isproxy, unsigned protomask,
const struct auth_class *ahc, const char *id,
***************
*** 1296,1302 ****
ne_hook_post_send(sess, ah_post_send, ahs);
ne_hook_destroy_request(sess, ah_destroy, ahs);
ne_hook_destroy_session(sess, free_auth, ahs);
!
ne_set_session_private(sess, id, ahs);
}
--- 1314,1322 ----
ne_hook_post_send(sess, ah_post_send, ahs);
ne_hook_destroy_request(sess, ah_destroy, ahs);
ne_hook_destroy_session(sess, free_auth, ahs);
! #ifdef HAVE_SSPI
! ne_hook_connection_close(sess, ah_close_conn, ahs);
! #endif
ne_set_session_private(sess, id, ahs);
}
diff -cr neon-0.26.3\src\ne_private.h neon-0.26.3.rjvdboon\src\ne_private.h
*** neon-0.26.3\src\ne_private.h Tue Mar 07 22:38:22 2006
--- neon-0.26.3.rjvdboon\src\ne_private.h Wed Feb 14 10:00:46 2007
***************
*** 87,93 ****
int rdtimeout; /* read timeout. */
struct hook *create_req_hooks, *pre_send_hooks, *post_send_hooks;
! struct hook *destroy_req_hooks, *destroy_sess_hooks, *private;
char *user_agent; /* full User-Agent: header field */
--- 87,93 ----
int rdtimeout; /* read timeout. */
struct hook *create_req_hooks, *pre_send_hooks, *post_send_hooks;
! struct hook *destroy_req_hooks, *destroy_sess_hooks, *conn_close_hooks,
*private;
char *user_agent; /* full User-Agent: header field */
diff -cr neon-0.26.3\src\ne_request.c neon-0.26.3.rjvdboon\src\ne_request.c
*** neon-0.26.3\src\ne_request.c Tue Mar 07 22:38:22 2006
--- neon-0.26.3.rjvdboon\src\ne_request.c Wed Feb 14 10:00:46 2007
***************
*** 1210,1216 ****
* then it is impossible to distinguish between a server failure
* and a connection timeout if an EOF/RST is received. So don't
* do that. */
! if (!req->flags[NE_REQFLAG_IDEMPOTENT] && req->session->persisted) {
NE_DEBUG(NE_DBG_HTTP, "req: Closing connection for non-idempotent "
"request.\n");
ne_close_connection(req->session);
--- 1210,1217 ----
* then it is impossible to distinguish between a server failure
* and a connection timeout if an EOF/RST is received. So don't
* do that. */
! if (!req->flags[NE_REQFLAG_IDEMPOTENT] && req->session->persisted &&
! !req->flags[NE_REQFLAG_IN_AUTH_NEGOTIATION]) {
NE_DEBUG(NE_DBG_HTTP, "req: Closing connection for non-idempotent "
"request.\n");
ne_close_connection(req->session);
diff -cr neon-0.26.3\src\ne_request.h neon-0.26.3.rjvdboon\src\ne_request.h
*** neon-0.26.3\src\ne_request.h Sun Feb 26 00:16:12 2006
--- neon-0.26.3.rjvdboon\src\ne_request.h Wed Feb 14 10:00:46 2007
***************
*** 231,236 ****
--- 231,239 ----
NE_REQFLAG_IDEMPOTENT, /* disable this flag if the request uses a
* non-idempotent method such as POST. */
+ NE_REQFLAG_IN_AUTH_NEGOTIATION, /* enable this flag if the request is
part of auth
+ * negotiation. i.e. please do not close
the connection
+ * or all my work was in vain... */
NE_REQFLAG_LAST /* enum sentinel value */
} ne_request_flag;
***************
*** 284,289 ****
--- 287,297 ----
void ne_hook_destroy_session(ne_session *sess,
ne_destroy_sess_fn fn, void *userdata);
+ typedef void (*ne_connection_close_fn)(void *userdata);
+ /* Hook called when the connection is closed. */
+ void ne_hook_connection_close(ne_session *sess,
+ ne_connection_close_fn fn, void *userdata);
+
/* The ne_unhook_* functions remove a hook registered with the given
* session. If a hook is found which was registered with a given
* function 'fn', and userdata pointer 'userdata', then it will be
***************
*** 299,304 ****
--- 307,314 ----
ne_destroy_req_fn fn, void *userdata);
void ne_unhook_destroy_session(ne_session *sess,
ne_destroy_sess_fn fn, void *userdata);
+ void ne_unhook_connection_close(ne_session *sess,
+ ne_connection_close_fn fn, void *userdata);
/* Store an opaque context for the request, 'priv' is returned by a
* call to ne_request_get_private with the same ID. */
diff -cr neon-0.26.3\src\ne_session.c neon-0.26.3.rjvdboon\src\ne_session.c
*** neon-0.26.3\src\ne_session.c Wed Mar 01 18:45:02 2006
--- neon-0.26.3.rjvdboon\src\ne_session.c Thu Feb 15 12:57:20 2007
***************
*** 71,76 ****
--- 71,78 ----
destroy_hooks(sess->post_send_hooks);
destroy_hooks(sess->destroy_req_hooks);
destroy_hooks(sess->destroy_sess_hooks);
+ destroy_hooks(sess->conn_close_hooks);
+ sess->conn_close_hooks = NULL; // Set to NULL explicitly, else would be
called by the close_connection below.
destroy_hooks(sess->private);
ne_free(sess->scheme);
***************
*** 256,263 ****
void ne_close_connection(ne_session *sess)
{
! if (sess->connected) {
NE_DEBUG(NE_DBG_SOCKET, "Closing connection.\n");
ne_sock_close(sess->socket);
sess->socket = NULL;
NE_DEBUG(NE_DBG_SOCKET, "Connection closed.\n");
--- 258,272 ----
void ne_close_connection(ne_session *sess)
{
! struct hook *hk, *next_hk;
NE_DEBUG(NE_DBG_SOCKET, "Closing connection.\n");
+ if (sess->connected) {
+ NE_DEBUG(NE_DBG_HTTP, "Running connection close hooks.\n");
+ for (hk = sess->conn_close_hooks; hk; hk = next_hk) {
+ ne_connection_close_fn fn = (ne_connection_close_fn)hk->fn;
+ next_hk = hk->next;
+ fn(hk->userdata);
+ }
ne_sock_close(sess->socket);
sess->socket = NULL;
NE_DEBUG(NE_DBG_SOCKET, "Connection closed.\n");
***************
*** 396,401 ****
--- 405,416 ----
ADD_HOOK(sess->destroy_sess_hooks, fn, userdata);
}
+ void ne_hook_connection_close(ne_session *sess,
+ ne_connection_close_fn fn, void *userdata)
+ {
+ ADD_HOOK(sess->conn_close_hooks, fn, userdata);
+ }
+
void ne_set_session_private(ne_session *sess, const char *id, void *userdata)
{
add_hook(&sess->private, id, NULL, userdata);
***************
*** 444,447 ****
--- 459,468 ----
ne_destroy_sess_fn fn, void *userdata)
{
REMOVE_HOOK(sess->destroy_sess_hooks, fn, userdata);
+ }
+
+ void ne_unhook_connection_close(ne_session *sess,
+ ne_connection_close_fn fn, void *userdata)
+ {
+ REMOVE_HOOK(sess->conn_close_hooks, fn, userdata);
}
diff -cr neon-0.26.3\src\ne_sspi.c neon-0.26.3.rjvdboon\src\ne_sspi.c
*** neon-0.26.3\src\ne_sspi.c Tue Sep 05 16:43:48 2006
--- neon-0.26.3.rjvdboon\src\ne_sspi.c Wed Feb 14 10:00:46 2007
***************
*** 29,39 ****
#define SEC_SUCCESS(Status) ((Status) >= 0)
struct SSPIContextStruct {
CtxtHandle context;
char *serverName;
CredHandle credentials;
! int continueNeeded;
int authfinished;
char *mechanism;
int ntlm;
--- 29,46 ----
#define SEC_SUCCESS(Status) ((Status) >= 0)
+ /* Defined session flags: */
+ typedef enum {
+ ne_sspistatus_unknown = 0, /* unknown state, sspi unused */
+ ne_sspistatus_continueneeded, /* authentication in progress */
+ ne_sspistatus_authfinishedthisrequest /* authentication succeeded in last
request */
+ } ne_sspi_status;
+
struct SSPIContextStruct {
CtxtHandle context;
char *serverName;
CredHandle credentials;
! ne_sspi_status authState;
int authfinished;
char *mechanism;
int ntlm;
***************
*** 338,344 ****
}
sspiContext = ne_calloc(sizeof(SSPIContext));
- sspiContext->continueNeeded = 0;
if (ntlm) {
sspiContext->mechanism = "NTLM";
--- 345,350 ----
***************
*** 352,357 ****
--- 358,364 ----
sspiContext->ntlm = ntlm;
sspiContext->authfinished = 0;
+ sspiContext->authState = ne_sspistatus_unknown;
*context = sspiContext;
return 0;
}
***************
*** 367,373 ****
#else
pSFT->FreeCredentialsHandle(&(sspiContext->credentials));
#endif
! sspiContext->continueNeeded = 0;
}
/*
--- 374,380 ----
#else
pSFT->FreeCredentialsHandle(&(sspiContext->credentials));
#endif
! sspiContext->authState = ne_sspistatus_unknown;
}
/*
***************
*** 428,433 ****
--- 435,441 ----
return status;
}
sspiContext->authfinished = 0;
+ sspiContext->authState = ne_sspistatus_unknown;
return 0;
}
/*
***************
*** 441,446 ****
--- 449,455 ----
int status;
SECURITY_STATUS securityStatus;
ULONG contextFlags;
+ int continueNeeded = 0;
SSPIContext *sspiContext;
if (initialized <= 0) {
***************
*** 463,473 ****
return status;
}
if (base64Token) {
SecBufferDesc inBufferDesc;
SecBuffer inBuffer;
! if (!sspiContext->continueNeeded) {
freeBuffer(&outBufferDesc);
NE_DEBUG(NE_DBG_HTTPAUTH, "sspi: Got an unexpected token.\n");
return -1;
--- 472,485 ----
return status;
}
+ if (sspiContext->authState == ne_sspistatus_continueneeded)
+ continueNeeded = 1;
+ sspiContext->authState = ne_sspistatus_unknown;
if (base64Token) {
SecBufferDesc inBufferDesc;
SecBuffer inBuffer;
! if (!continueNeeded) {
freeBuffer(&outBufferDesc);
NE_DEBUG(NE_DBG_HTTPAUTH, "sspi: Got an unexpected token.\n");
return -1;
***************
*** 490,499 ****
if (securityStatus == SEC_E_OK)
{
sspiContext->authfinished = 1;
}
freeBuffer(&inBufferDesc);
} else {
! if (sspiContext->continueNeeded) {
freeBuffer(&outBufferDesc);
NE_DEBUG(NE_DBG_HTTPAUTH, "sspi: Expected a token from
server.\n");
return -1;
--- 502,512 ----
if (securityStatus == SEC_E_OK)
{
sspiContext->authfinished = 1;
+ sspiContext->authState = ne_sspistatus_authfinishedthisrequest;
}
freeBuffer(&inBufferDesc);
} else {
! if (continueNeeded) {
freeBuffer(&outBufferDesc);
NE_DEBUG(NE_DBG_HTTPAUTH, "sspi: Expected a token from
server.\n");
return -1;
***************
*** 541,549 ****
if (securityStatus == SEC_I_COMPLETE_AND_CONTINUE
|| securityStatus == SEC_I_CONTINUE_NEEDED) {
! sspiContext->continueNeeded = 1;
} else {
! sspiContext->continueNeeded = 0;
}
if (!(securityStatus == SEC_I_COMPLETE_AND_CONTINUE
--- 554,563 ----
if (securityStatus == SEC_I_COMPLETE_AND_CONTINUE
|| securityStatus == SEC_I_CONTINUE_NEEDED) {
! sspiContext->authState = ne_sspistatus_continueneeded;
} else {
! if (securityStatus == SEC_I_COMPLETE_NEEDED || securityStatus ==
SEC_E_OK)
! sspiContext->authState = ne_sspistatus_authfinishedthisrequest;
}
if (!(securityStatus == SEC_I_COMPLETE_AND_CONTINUE
***************
*** 562,566 ****
--- 576,601 ----
freeBuffer(&outBufferDesc);
return 0;
+ }
+
+ int ne_sspi_is_in_negotiation(void *context)
+ {
+ int status;
+ SSPIContext *sspiContext;
+ if (initialized <= 0) {
+ return 0;
+ }
+
+ status = getContext(context, &sspiContext);
+ if (status) {
+ return 0;
+ }
+
+ NE_DEBUG(NE_DBG_HTTPAUTH, "sspi: ne_sspi_is_in_negotiation. (%d)\n",
sspiContext->authState);
+
+ if (sspiContext->authState == ne_sspistatus_continueneeded ||
+ sspiContext->authState == ne_sspistatus_authfinishedthisrequest)
+ return 1;
+ return 0;
}
#endif /* HAVE_SSPI */
diff -cr neon-0.26.3\src\ne_sspi.h neon-0.26.3.rjvdboon\src\ne_sspi.h
*** neon-0.26.3\src\ne_sspi.h Sun Feb 12 13:05:14 2006
--- neon-0.26.3.rjvdboon\src\ne_sspi.h Wed Feb 14 10:00:46 2007
***************
*** 43,48 ****
--- 43,50 ----
int ne_sspi_authenticate(void *context, const char *base64Token,
char **responseToken);
+ int ne_sspi_is_in_negotiation(void *context);
+
#endif /* HAVE_SSPI */
#endif /* NE_SSPI_H */
[status-line] < HTTP/1.1 200 OK
[status-line] < HTTP/1.1 401 Authorization Required
[status-line] < HTTP/1.1 401 Authorization Required
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 207 Multi-Status
[status-line] < HTTP/1.1 207 Multi-Status
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 207 Multi-Status
[status-line] < HTTP/1.1 207 Multi-Status
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 207 Multi-Status
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 207 Multi-Status
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 207 Multi-Status
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 207 Multi-Status
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 207 Multi-Status
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 207 Multi-Status
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 207 Multi-Status
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 207 Multi-Status
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 207 Multi-Status
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 207 Multi-Status
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 201 Created
[status-line] < HTTP/1.1 401 Authorization Required
[status-line] < HTTP/1.1 401 Authorization Required
_______________________________________________
neon mailing list
[email protected]
http://mailman.webdav.org/mailman/listinfo/neon