diff -u -r libwww-current/Library/src/HTChannl.c libwww-5.3.1/Library/src/HTChannl.c --- libwww-current/Library/src/HTChannl.c Fri Jun 23 16:27:34 2000 +++ libwww-5.3.1/Library/src/HTChannl.c Tue Aug 29 08:41:05 2000 @@ -51,6 +51,7 @@ BOOL active; /* Active or passive channel */ int semaphore; /* On channel use */ HTHost * host; /* Zombie connections */ + void * protocolContext; /* protocol specific data, must be HT_FREE-able */ }; PRIVATE HTList ** channels = NULL; /* List of channels */ @@ -142,6 +143,13 @@ HTTRACE(PROT_TRACE, "Channel..... Deleted %p, file %p\n" _ ch _ ch->fp); ch->fp = NULL; } + + /* Free protocol context, if any */ + if (ch->protocolContext) { + HT_FREE(ch->protocolContext); + ch->protocolContext = NULL; + } + HT_FREE(ch); } } @@ -176,6 +184,7 @@ ch->channelOStream.isa = &ChannelOStreamIsa; ch->channelIStream.channel = ch; ch->channelOStream.channel = ch; + ch->protocolContext = NULL; HTList_addObject(list, (void *) ch); #ifdef HT_MUX @@ -488,3 +497,16 @@ return NULL; } +/* +** Protocol Context pointer to be used as a user defined context +*/ +PUBLIC void HTChannel_setProtocolContext (HTChannel * me, + void * protocolContext) +{ + if (me) me->protocolContext = protocolContext; +} + +PUBLIC void * HTChannel_protocolContext (HTChannel * me) +{ + return me ? me->protocolContext : NULL; +} diff -u -r libwww-current/Library/src/HTChannl.html libwww-5.3.1/Library/src/HTChannl.html --- libwww-current/Library/src/HTChannl.html Wed Jul 7 17:43:28 1999 +++ libwww-5.3.1/Library/src/HTChannl.html Mon Aug 28 23:40:55 2000 @@ -88,6 +88,17 @@ extern BOOL HTChannel_setFile (HTChannel * channel, FILE * fp);
+This can be used for anything that the application would like to keep +tabs on. NOTE: This information must be HT_FREE-able (ie. no "real C +object" or pointers pointing to "exclusive" memory) +
+extern void HTChannel_setProtocolContext (HTChannel * me, void * protocolContext); +extern void * HTChannel_protocolContext (HTChannel * me); ++
diff -u -r libwww-current/Library/src/HTFTP.c libwww-5.3.1/Library/src/HTFTP.c --- libwww-current/Library/src/HTFTP.c Wed Aug 9 12:43:08 2000 +++ libwww-5.3.1/Library/src/HTFTP.c Tue Aug 29 23:14:01 2000 @@ -126,6 +126,13 @@ BOOL stream_error; } ftp_data; +/* UGLY: Hardcoded max.lengths, but then... we can let the HTChannel object + delete the protocolContext member */ +typedef struct _ftp_protocol_context { + char uid[80]; + char passwd[80]; +} ftp_protocol_context; + struct _HTStream { const HTStreamClass * isa; HTStream * target; @@ -225,6 +232,21 @@ return NO; } +#if 0 +PRIVATE void DUMPBLOCK(const char *s, const char *c, int len) +{ + fprintf(stderr, "%s: ", s); + while (len--) { + if (isprint(*c)) + fprintf(stderr, "%c", *c); + else + fprintf(stderr, "[%d]", *c ); + c++; + } + fprintf(stderr, "\n"); +} +#endif + /* ScanResponse ** ------------ ** Analyzes the response from the FTP server. @@ -236,6 +258,7 @@ int reply = 0; char cont = '\0'; char *ptr = me->buffer+4; +/* DUMPBLOCK("ScanResponse", me->buffer, me->buflen); */ *(me->buffer+me->buflen) = '\0'; /* begin _GM_ */ /* Note: libwww bug ID: GM3 */ @@ -276,6 +299,22 @@ PRIVATE int FTPStatus_put_block (HTStream * me, const char * b, int l) { int status; +/* check to see if there are multiple lines */ + const char *c = b; + int found = 0; +/* DUMPBLOCK("FTPStatus_put_block", b, l); */ + for (c = b ; *c ; c++) { + if (*c == CR || *c == LF) { + found = 1; + continue; + } + if (found) { + l = c - b; +/* DUMPBLOCK("FTPStatus_put_block", b, l); */ + break; + } + } + HTHost_setConsumed(me->host, l); while (l-- > 0) { if (me->state == EOL_FCR) { @@ -491,6 +530,8 @@ if (HTHost_listen(NULL, dnet, "ftp://localhost:0") == HT_ERROR) return NO; + HTHost_setEventPriority(HTNet_host(dnet), HT_PRIORITY_MAX - 1); + /* ** Now we must find out who we are to tell the other guy ** We have to get the local IP interface from the control connection as @@ -1101,6 +1142,8 @@ case NEED_CONNECT: HTTRACE(PROT_TRACE, "FTP Get Data now in state NEED_CONNECT\n"); status = HTHost_connect(HTNet_host(dnet), dnet, data->host); + HTHost_setEventPriority(HTNet_host(dnet), HT_PRIORITY_MAX - 1); + if (status == HT_WOULD_BLOCK) return HT_WOULD_BLOCK; else if (status == HT_OK) { @@ -1278,7 +1321,7 @@ } continue; } else { - status = HTHost_read(HTNet_host(dnet), dnet); + status = HTHost_read(HTNet_host(dnet), dnet); } if (status == HT_WOULD_BLOCK) return HT_WOULD_BLOCK; @@ -1379,6 +1422,33 @@ return FTPEvent(soc, ctrl, HTEvent_BEGIN); } +PRIVATE int FTPControlCheck(HTHost *host, void *data) +{ + ftp_ctrl *ctrl = (ftp_ctrl *) data; + ftp_protocol_context *ctx = 0; + HTChannel *channel; + + if (!host || !data) return YES; + channel = HTHost_channel(host); + + if (channel == NULL) return YES; + ctx = HTChannel_protocolContext(channel); + + HTTRACE(PROT_TRACE, "FTP Check... channel: %s, wanted: %s\n" _ (ctx ? ctx->uid : "null") _ ctrl->uid ); + + if (ctx == NULL) return YES; + + if ((strcmp(ctx->uid, ctrl->uid) == 0) && + (strcmp(ctx->passwd, ctrl->passwd) == 0)) { + HTTRACE(PROT_TRACE, "FTP Check... compatible channel!\n"); + return 1; + } + + HTTRACE(PROT_TRACE, "FTP Check... incompatible channel!\n"); + + return 0; +} + PRIVATE int FTPEvent (SOCKET soc, void * pVoid, HTEventType type) { ftp_ctrl * ctrl = (ftp_ctrl *) pVoid; @@ -1471,13 +1541,17 @@ case FTP_NEED_CCON: HTTRACE(PROT_TRACE, "FTP Event... now in state FTP_NEED_CONN\n"); - status = HTHost_connect(host, cnet, url); + status = HTHost_connect_with_check(host, cnet, url, + FTPControlCheck, ctrl); host = HTNet_host(cnet); if (status == HT_OK) { + ftp_protocol_context *ctx; + HTChannel *channel; + int next_state = FTP_NEED_LOGIN; /* ** Check the protocol class to see if we have connected to a - ** the right class of server, in this case HTTP. + ** the right class of server, in this case FTP. */ { char * s_class = HTHost_class(host); @@ -1490,12 +1564,30 @@ HTHost_setClass(host, "ftp"); } + /* Record protocol context with the channel */ + channel = HTHost_channel(host); + if (HTChannel_protocolContext(channel) == NULL) { + ctx = (ftp_protocol_context *) HT_MALLOC(sizeof(ftp_protocol_context)); + if (ctx == NULL) { + HT_OUTOFMEM("ftp_protocol_context "); + } + ctx->uid[sizeof(ctx->uid)-1] = 0; + ctx->passwd[sizeof(ctx->passwd)-1] = 0; + + strncpy(ctx->uid, ctrl->uid, sizeof(ctx->uid)-1); + strncpy(ctx->passwd, ctrl->passwd, sizeof(ctx->passwd)-1); + + HTChannel_setProtocolContext(channel, ctx); + } + /* Check persistent connection */ if (HTNet_persistent(cnet)) { ctrl->server = HTHost_version(host); HTTRACE(PROT_TRACE, "FTP Server.. Cache says type %d server\n" _ ctrl->server); ctrl->reset = 1; + ctrl->substate = 0; + next_state = FTP_NEED_DCON; } else HTNet_setPersistent(cnet, YES, HT_TP_SINGLE); @@ -1530,7 +1622,7 @@ HTRequest_linkDestination(request); } - ctrl->state = FTP_NEED_LOGIN; + ctrl->state = next_state; } else if (status == HT_WOULD_BLOCK || status == HT_PENDING) return HT_OK; else diff -u -r libwww-current/Library/src/HTHost.c libwww-5.3.1/Library/src/HTHost.c --- libwww-current/Library/src/HTHost.c Fri Jul 28 15:56:08 2000 +++ libwww-5.3.1/Library/src/HTHost.c Tue Aug 29 23:18:04 2000 @@ -262,18 +262,16 @@ return HT_OK; } -/* -** Search the host info cache for a host object or create a new one -** and add it. Examples of host names are -** -** www.w3.org -** www.foo.com:8000 -** 18.52.0.18 -** -** Returns Host object or NULL if error. You may get back an already -** existing host object - you're not guaranteed a new one each time. -*/ -PUBLIC HTHost * HTHost_new (char * host, u_short u_port) + +/* +** Extended version of HTHost_new. This version provides for a +** "channel compatibility check" and is intended to be used for +** Non-stateless connections. A protocol handler can use this +** function to test +*/ +PUBLIC HTHost * HTHost_new_with_check (char * host, u_short u_port, + HTHost_new_with_check_callback *ccbf, + void * cbdata) { HTList * list = NULL; /* Current list in cache */ HTHost * pres = NULL; @@ -306,35 +304,58 @@ HTTRACE(CORE_TRACE, "Host info... Collecting host info %p\n" _ pres); delete_object(list, pres); pres = NULL; + } else { + if (pres->channel) { + time_t t = time(NULL); + /* + ** If we have a TTL for this TCP connection then + ** check that we haven't passed it. + */ + if (pres->expires > 0) { + if (HTHost_isIdle(pres) && pres->expires < t) { + HTTRACE(CORE_TRACE, "Host info... Persistent channel %p gotten cold\n" _ + pres->channel); + HTHost_clearChannel(pres, HT_OK); + } + } + if (pres && pres->channel) { + /* We might move this check to embrace + this if ... */ + + /* Check if the channel is compatible + with what the caller wants ... */ + int candidate_ok = 1; + + if (ccbf) { + candidate_ok = ccbf(pres, cbdata); + } + + if (candidate_ok) { + pres->expires = t + HTPassiveTimeout; + HTTRACE(CORE_TRACE, + "Host info... REUSING CHANNEL %p\n" + _ pres->channel); + } else { + /* pres is not wanted by the caller ... */ + HTTRACE(CORE_TRACE, "Host info... Candidate channel refused by checking callback %p\n" _ pres->channel); + pres = 0; + } + } + } else { + HTTRACE(CORE_TRACE, "Host info... Found Host %p with no active channel\n" _ pres); + } + } + if (pres) { + break; } - break; } } } - - /* If not found then create new Host object, else use existing one */ - if (pres) { - if (pres->channel) { - - /* - ** If we have a TTL for this TCP connection then - ** check that we haven't passed it. - */ - if (pres->expires > 0) { - time_t t = time(NULL); - if (HTHost_isIdle(pres) && pres->expires < t) { - HTTRACE(CORE_TRACE, "Host info... Persistent channel %p gotten cold\n" _ - pres->channel); - HTHost_clearChannel(pres, HT_OK); - } else { - pres->expires = t + HTPassiveTimeout; - HTTRACE(CORE_TRACE, "Host info... REUSING CHANNEL %p\n" _ pres->channel); - } - } - } else { - HTTRACE(CORE_TRACE, "Host info... Found Host %p with no active channel\n" _ pres); - } - } else { + + /* If not found then create new Host object, else + use existing one */ + + if (!pres) { if ((pres = (HTHost *) HT_CALLOC(1, sizeof(HTHost))) == NULL) HT_OUTOFMEM("HTHost_add"); pres->hash = hash; @@ -356,7 +377,31 @@ return pres; } -PUBLIC HTHost * HTHost_newWParse (HTRequest * request, char * url, u_short u_port) +/* +** Search the host info cache for a host object or create a new one +** and add it. Examples of host names are +** +** www.w3.org +** www.foo.com:8000 +** 18.52.0.18 +** +** Returns Host object or NULL if error. You may get back an already +** existing host object - you're not guaranteed a new one each time. +*/ +PUBLIC HTHost * HTHost_new (char * host, u_short u_port) +{ + return HTHost_new_with_check(host, u_port, NULL, NULL); +} + + + + + +PUBLIC HTHost *HTHost_newWParse_with_check(HTRequest * request, + char * url, + u_short u_port, + HTHost_new_with_check_callback *ccbf, + void * cbdata) { char * port; char * fullhost = NULL; @@ -390,9 +435,10 @@ HTTRACE(PROT_TRACE, "HTHost parse Looking up `%s\' on port %u\n" _ parsedHost _ u_port); /* Find information about this host */ - if ((me = HTHost_new(parsedHost, u_port)) == NULL) { + if ((me = HTHost_new_with_check(parsedHost, u_port, ccbf, cbdata)) == NULL) { HTTRACE(PROT_TRACE, "HTHost parse Can't get host info\n"); - me->tcpstate = TCP_ERROR; + /* Removed, because it would cause a SIGSEGV !!! */ + /* me->tcpstate = TCP_ERROR; */ return NULL; } sin = &me->sock_addr; @@ -408,6 +454,11 @@ return me; } +PUBLIC HTHost * HTHost_newWParse (HTRequest * request, char * url, u_short u_port) +{ + return HTHost_newWParse_with_check(request, url, u_port, NULL, NULL); +} + /* ** Search the host info cache for a host object. Examples of host names: ** @@ -1288,13 +1339,17 @@ ** multiple pipelined engines. It then registers its own engine ** (HostEvent) with the event manager. */ -PUBLIC int HTHost_connect (HTHost * host, HTNet * net, char * url) +PUBLIC int HTHost_connect_with_check (HTHost * host, HTNet * net, char * url, + HTHost_new_with_check_callback *ccbf, + void *cbdata) { HTRequest * request = HTNet_request(net); int status = HT_OK; if (!host) { HTProtocol * protocol = HTNet_protocol(net); - if ((host = HTHost_newWParse(request, url, HTProtocol_id(protocol))) == NULL) + if ((host = HTHost_newWParse_with_check(request, url, + HTProtocol_id(protocol), + ccbf, cbdata)) == NULL) return HT_ERROR; /* @@ -1344,6 +1399,10 @@ } return HT_ERROR; /* @@@ - some more deletion and stuff here? */ } +PUBLIC int HTHost_connect (HTHost * host, HTNet * net, char * url) +{ + return HTHost_connect_with_check(host, net, url, NULL, NULL); +} PUBLIC int HTHost_listen (HTHost * host, HTNet * net, char * url) { @@ -1793,3 +1852,15 @@ DoPendingReqLaunch = YES; } + +/* Change the priority of all the events registered for the host. This + was added by Peter Stamfest to fix a race condition / bug with FTP + persistent connections */ + +PUBLIC void HTHost_setEventPriority(HTHost *host, int prio) +{ + int i; + for (i = 0; i < HTEvent_TYPES; i++) { + HTEvent_setPriority(host->events[i], prio); + } +} diff -u -r libwww-current/Library/src/HTHost.html libwww-5.3.1/Library/src/HTHost.html --- libwww-current/Library/src/HTHost.html Wed Jul 7 17:43:28 1999 +++ libwww-5.3.1/Library/src/HTHost.html Tue Aug 29 23:16:37 2000 @@ -53,12 +53,27 @@
+Callback function type to be used with HTHost_new_with_check +
+typedef int HTHost_new_with_check_callback(HTHost *, void *data); +
extern HTHost * HTHost_new (char * host, u_short u_port); +extern HTHost * HTHost_new_with_check (char * host, u_short u_port, + HTHost_new_with_check_callback *ccbf, + void *cbdata); extern HTHost * HTHost_newWParse(HTRequest * request, char * url, u_short u_port); +extern HTHost * HTHost_newWParse_with_check(HTRequest * request, + char * url, u_short u_port, + HTHost_new_with_check_callback *ccbf, + void *cbdata); extern int HTHost_hash (HTHost * host);
As a Net Object doesn't necessarily know whether there is a channel up and running and whether that channel can be reused, -it must do an explicit connect the the host. +it must do an explicit connect to the host.
extern int HTHost_connect (HTHost * host, HTNet * net, char * url); +extern int HTHost_connect_with_check (HTHost * host, HTNet * net, char * url, + HTHost_new_with_check_callback *ccbf, + void *cbdata); extern int HTHost_accept (HTHost * host, HTNet * net, char * url); diff -u -r libwww-current/Library/src/HTHstMan.html libwww-5.3.1/Library/src/HTHstMan.html --- libwww-current/Library/src/HTHstMan.html Fri Jul 28 15:52:23 2000 +++ libwww-5.3.1/Library/src/HTHstMan.html Thu Aug 24 17:00:59 2000 @@ -102,6 +102,12 @@ int forceWriteFlush; int inFlush; /* Tells if we're currently processing a file flush */ + /* support for HTHost_new_with_check_callback functions */ + /* SHOULD WE USE *context instead of this one??? */ + /* NOTE: protocolContext is HT_FREE'd automatically when the HTHost + object gets deleted - this is crude! Maybe we should change it... + somehow? */ + void * protocolContext; /* Protocol Specific context */ }; #define HTHost_bytesRead(me) ((me) ? (me)->bytes_read : -1)