PUBLIC int HTServHTTP (SOCKET soc, HTRequest * request) { HTNet * net = HTRequest_net(request); https_info * http; /* Specific protocol information */ /* ** Initiate a new https object and bind to request object ** This is actually state HTTPS_BEGIN, but it can't be in the state ** machine as we need the object first (chicken and egg problem). */ HTTRACE(PROT_TRACE, "Serv HTTP... on socket %d\n" _ soc); if ((http = (https_info *) HT_CALLOC(1, sizeof(https_info))) == NULL) HT_OUTOFMEM("HTServHTTP"); http->server = request; http->state = HTTPS_BEGIN; http->clients = HTList_new(); HTNet_setContext(net, http); /* ** Create the stream pipe FROM the channel to the server request. */ net->readStream = HTTPReceive_new(request, http); HTRequest_setOutputConnected(request, YES); http->state = HTTPS_BEGIN; HTNet_setEventCallback(net, ServEvent); HTNet_setEventParam(net, http); /* callbacks get http* */ return ServEvent(soc, http, HTEvent_BEGIN); /* get it started - ops is ignored */ }
/* FileCleanup ** ----------- ** This function closes the connection and frees memory. ** Returns YES on OK, else NO */ PRIVATE int FileCleanup (HTRequest *req, int status) { HTNet * net = HTRequest_net(req); file_info * file = (file_info *) HTNet_context(net); HTStream * input = HTRequest_inputStream(req); /* Free stream with data TO Local file system */ if (input) { if (status == HT_INTERRUPTED) (*input->isa->abort)(input, NULL); else (*input->isa->_free)(input); HTRequest_setInputStream(req, NULL); } /* ** Remove if we have registered a timer function as a callback */ if (file->timer) { HTTimer_delete(file->timer); file->timer = NULL; } if (file) { HT_FREE(file->local); HT_FREE(file); } HTNet_delete(net, status); return YES; }
/* HTNewsCleanup ** ------------- ** This function closes the connection and frees memory. ** Returns YES on OK, else NO */ PRIVATE int HTNewsCleanup (HTRequest * req, int status) { HTNet * net = HTRequest_net(req); news_info *news = (news_info *) HTNet_context(net); HTStream * input = HTRequest_inputStream(req); /* Free stream with data TO network */ if (!HTRequest_isDestination(req)) HTRequest_removeDestination(req); else if (input) { if (status == HT_INTERRUPTED) (*input->isa->abort)(input, NULL); else (*input->isa->_free)(input); HTRequest_setInputStream(req, NULL); } /* Remove the request object and our own context structure for nntp */ HTNet_delete(net, status); if (news) { HT_FREE(news->name); HTChunk_delete(news->cmd); HT_FREE(news); } return YES; }
PUBLIC int HTMIME_acceptRanges (HTRequest * request, HTResponse * response, char * token, char * value) { if (value) { HTNet * net = HTRequest_net(request); HTHost * host = HTNet_host(net); HTHost_setRangeUnits(host, value); } return HT_OK; }
PUBLIC int HTLoadFile (SOCKET soc, HTRequest * request) { file_info *file; /* Specific access information */ HTNet * net = HTRequest_net(request); HTParentAnchor * anchor = HTRequest_anchor(request); HTTRACE(PROT_TRACE, "HTLoadFile.. Looking for `%s\'\n" _ HTAnchor_physical(anchor)); if ((file = (file_info *) HT_CALLOC(1, sizeof(file_info))) == NULL) HT_OUTOFMEM("HTLoadFILE"); file->state = FS_BEGIN; file->net = net; HTNet_setContext(net, file); HTNet_setEventCallback(net, FileEvent); HTNet_setEventParam(net, file); /* callbacks get http* */ return FileEvent(soc, file, HTEvent_BEGIN); /* get it started - ops is ignored */ }
PUBLIC int HTLoadNews (SOCKET soc, HTRequest * request) { news_info *news; HTParentAnchor *anchor = HTRequest_anchor(request); HTNet * net = HTRequest_net(request); char * url = HTAnchor_physical(anchor); HTTRACE(PROT_TRACE, "NNTP........ Looking for `%s\'\n" _ url); if ((news = (news_info *) HT_CALLOC(1, sizeof(news_info))) == NULL) HT_OUTOFMEM("HTLoadNews"); news->cmd = HTChunk_new(128); news->state = NEWS_BEGIN; news->net = net; HTNet_setContext(net, news); HTNet_setEventCallback(net, NewsEvent); HTNet_setEventParam(net, news); /* callbacks get http* */ return NewsEvent(soc, news, HTEvent_BEGIN); /* get it started - ops is ignored */ }
PUBLIC int HTMIME_connection (HTRequest * request, HTResponse * response, char * token, char * value) { /* ** Walk through the set of connection directives and add them to the ** response association list for connection directives */ char * name_val; while ((name_val = HTNextPair(&value)) != NULL) { char * name = HTNextField(&name_val); char * val = HTNextField(&name_val); /* ** If we have a name then look if it is concerning persistent ** connections. If so, then we handle it here, otherwise we leave it ** to somebody else by simply adding it to the list of connection ** tokens. */ if (name) { HTNet * net = HTRequest_net(request); HTHost * host = HTNet_host(net); if (!strcasecomp(name, "close")) { /* HTTP/1.1 */ HTTRACE(STREAM_TRACE, "MIMEParser.. Close received...\n"); HTHost_setCloseNotification(host, YES); } else if (!strcasecomp(name, "keep-alive")) { /* HTTP/1.0 */ /* ** In case this is an HTTP/1.1 server sending keep-alive then ** ignore it. */ if (HTHost_version(host) < HTTP_11) { HTNet_setPersistent(net, YES, HT_TP_SINGLE); HTTRACE(STREAM_TRACE, "MIMEParser.. HTTP/1.0 Keep Alive\n"); } else HTTRACE(STREAM_TRACE, "MIMEParser.. HTTP/1.0 Keep Alive ignored\n"); } else HTResponse_addConnection(response, name, val ? val : ""); } } return HT_OK; }
PUBLIC int HTLoadSocket (SOCKET soc, HTRequest * request) { raw_info * raw; /* Specific protocol information */ HTNet * net = HTRequest_net(request); HTTRACE(PROT_TRACE, "Load socket. Setting up socket for accept\n"); if ((raw = (raw_info *) HT_CALLOC(1, sizeof(raw_info))) == NULL) HT_OUTOFMEM("HTLoadSocket"); raw->state = RAW_BEGIN; raw->net = net; raw->request = request; HTNet_setContext(net, raw); HTNet_setEventCallback(net, SocketEvent); HTNet_setEventParam(net, raw); /* Start listening on a socket */ if (HTHost_listen(NULL, net, HTAnchor_physical(HTRequest_anchor(request))) == HT_ERROR) return SocketEvent(soc, raw, HTEvent_CLOSE); /* Get it started - ops is ignored */ return SocketEvent(soc, raw, HTEvent_BEGIN); }
/* MIME header parser stream. ** ------------------------- ** This stream parses a complete MIME header and if a content type header ** is found then the stream stack is called. Any left over data is pumped ** right through the stream */ PUBLIC HTStream* HTMIMEConvert (HTRequest * request, void * param, HTFormat input_format, HTFormat output_format, HTStream * output_stream) { HTStream * me; if ((me = (HTStream *) HT_CALLOC(1, sizeof(* me))) == NULL) HT_OUTOFMEM("HTMIMEConvert"); me->isa = &HTMIME; me->request = request; me->response = HTRequest_response(request); me->net = HTRequest_net(request); me->target = output_stream; me->target_format = output_format; me->save_stream = LocalSaveStream ? LocalSaveStream : HTBlackHoleConverter; me->token = HTChunk_new(256); me->value = HTChunk_new(256); me->hash = 0; me->EOLstate = EOL_BEGIN; me->haveToken = NO; return me; }
PRIVATE int RawCleanup (HTRequest * request, int status) { HTNet * net = HTRequest_net(request); raw_info * raw = (raw_info *) HTNet_context(net); HTTRACE(PROT_TRACE, "Raw clean... Called with status %d, net %p\n" _ status _ net); if (status == HT_INTERRUPTED) { HTAlertCallback * cbf = HTAlert_find(HT_PROG_INTERRUPT); if (cbf) (*cbf)(request, HT_PROG_INTERRUPT, HT_MSG_NULL, NULL, NULL, NULL); } else if (status == HT_TIMEOUT) { HTAlertCallback * cbf = HTAlert_find(HT_PROG_TIMEOUT); if (cbf) (*cbf)(request, HT_PROG_TIMEOUT, HT_MSG_NULL, NULL, NULL, NULL); } /* Delete the Net object */ HTNet_delete(net, HT_ERROR); HT_FREE(raw); return YES; }
PRIVATE int NewsEvent (SOCKET soc, void * pVoid, HTEventType type) { news_info *news = (news_info *)pVoid; int status = HT_ERROR; HTNet * net = news->net; HTRequest * request = HTNet_request(net); HTParentAnchor * anchor = HTRequest_anchor(request); char * url = HTAnchor_physical(anchor); HTHost * host = HTNet_host(net); /* ** Initiate a new nntp structure and bind to request structure ** This is actually state NNTP_BEGIN, but it can't be in the state ** machine as we need the structure first. */ if (type == HTEvent_CLOSE) { /* Interrupted */ HTRequest_addError(request, ERR_FATAL, NO, HTERR_INTERRUPTED, NULL, 0, "HTLoadHTTP"); HTNewsCleanup(request, HT_INTERRUPTED); return HT_OK; } else news = (news_info *) HTNet_context(net); /* Get existing copy */ /* Now jump into the machine. We know the state from the previous run */ while (1) { switch (news->state) { case NEWS_BEGIN: news->state = (!strchr(url, '@') && strchr(url, '*')) ? NEWS_SEEK_CACHE : NEWS_NEED_CONNECTION; break; case NEWS_SEEK_CACHE: if (HTNewsCache_before(request, NULL, 0) == HT_LOADED) news->state = NEWS_SUCCESS; else news->state = NEWS_NEED_CONNECTION; break; case NEWS_NEED_CONNECTION: /* Let's set up a connection */ if (!strncasecomp(url, "news:", 5)) { HTUserProfile * up = HTRequest_userProfile(request); char * newshost = HTUserProfile_news(up); StrAllocCopy(news->name, url+5); if (newshost) { char *newshack = NULL; /* Then we can use HTParse :-) */ StrAllocCopy(newshack, "news://"); StrAllocCat(newshack, newshost); status = HTHost_connect(host, net, (char *) newshack); host = HTNet_host(net); HT_FREE(newshack); } else news->state = NEWS_ERROR; } else if (!strncasecomp(url, "nntp:", 5)) { news->name = HTParse(url, "", PARSE_PATH); status = HTHost_connect(host, net, url); host = HTNet_host(net); } else { HTTRACE(PROT_TRACE, "News........ Huh?"); news->state = NEWS_ERROR; } if (status == HT_OK) { BOOL greeting = NO; /* Set up the persistent connection */ if (!HTNet_persistent(net)) { HTNet_setPersistent(net, YES, HT_TP_SINGLE); greeting = YES; } /* ** Check the protocol class to see if we have connected to a ** the right class of server, in this case HTTP. */ { HTHost * host = HTNet_host(net); char * s_class = HTHost_class(host); if (s_class && strcasecomp(s_class, "nntp")) { HTRequest_addError(request, ERR_FATAL, NO, HTERR_CLASS, NULL, 0, "HTLoadNews"); news->state = NEWS_ERROR; break; } HTHost_setClass(host, "nntp"); } /* ** Create the stream pipe FROM the channel to the application. ** The target for the input stream pipe is set up using the ** stream stack. */ { HTStream * rstream = HTNewsStatus_new(request, news, host); HTNet_setReadStream(net, rstream); HTRequest_setOutputConnected(request, YES); } /* ** Create the stream pipe TO the channel from the application ** and hook it up to the request object */ { HTOutputStream * output = HTNet_getOutput(net, NULL, 0); HTRequest_setInputStream(request, (HTStream *) output); } news->state = greeting ? NEWS_NEED_GREETING : NEWS_NEED_SWITCH; } else if (status == HT_WOULD_BLOCK || status == HT_PENDING) return HT_OK; else news->state = NEWS_ERROR; break; case NEWS_NEED_GREETING: status = HTHost_read(HTNet_host(net), net); if (status == HT_WOULD_BLOCK) return HT_OK; else if (status == HT_LOADED) { if (news->repcode/100 == 2) news->state = NEWS_NEED_SWITCH; else news->state = NEWS_ERROR; } else news->state = NEWS_ERROR; break; case NEWS_NEED_SWITCH: { HTMethod method = HTRequest_method(request); /* ** Find out what to ask the news server. Syntax of address is ** xxx@yyy Article ** <xxx@yyy> Same article ** xxxxx News group (no "@") */ if (method == METHOD_GET) { if (strchr(url, '@')) { /* ARTICLE */ if (*(news->name) != '<') { /* Add '<' and '>' */ char *newart; if ((newart = (char *) HT_MALLOC(strlen(news->name)+3)) == NULL) HT_OUTOFMEM("HTLoadNews"); sprintf(newart, "<%s>", news->name); HT_FREE(news->name); news->name = newart; } news->state = NEWS_NEED_ARTICLE; } else if (strchr(url, '*')) news->state = NEWS_NEED_LIST; else news->state = NEWS_NEED_GROUP; } else if (method == METHOD_POST) news->state = NEWS_NEED_POST; else { HTRequest_addError(request, ERR_FATAL, NO, HTERR_NOT_IMPLEMENTED,NULL, 0,"HTLoadNews"); news->state = NEWS_ERROR; } HTUnEscape(news->name); HTCleanTelnetString(news->name); } break; case NEWS_NEED_ARTICLE: if (!news->sent) { status = SendCommand(request, news, "ARTICLE", news->name); if (status == HT_WOULD_BLOCK) return HT_OK; else if (status == HT_ERROR) news->state = NEWS_ERROR; news->format = WWW_MIME; /* ** Set the default content type to plain text as news servers ** almost never send any useful information about the length ** of the body or the type - the success of MIME! */ HTAnchor_setFormat(anchor, WWW_PLAINTEXT); news->sent = YES; } else { status = HTHost_read(HTNet_host(net), net); if (status == HT_WOULD_BLOCK) return HT_OK; else if (status == HT_OK) news->state = NEWS_NEED_BODY; else if (status == HT_LOADED) { news->state = (news->repcode/100 == 2) ? NEWS_SUCCESS : NEWS_ERROR; } else news->state = NEWS_ERROR; news->sent = NO; } break; #if HT_LISTGROUP case NEWS_NEED_LGRP: if (!news->sent) { status = SendCommand(request, news, "LIST", "NEWSGROUPS"); if (status == HT_WOULD_BLOCK) return HT_OK; else if (status == HT_ERROR) news->state = NEWS_ERROR; news->format = WWW_NNTP_LIST; news->sent = YES; } else { status = HTHost_read(HTNet_host(net), net); if (status == HT_WOULD_BLOCK) return HT_OK; else if (status == HT_OK) news->state = NEWS_NEED_BODY; else if (status == HT_LOADED) { news->state = (news->repcode/100 == 2) ? NEWS_SUCCESS : NEWS_NEED_LIST; } else news->state = NEWS_ERROR; news->sent = NO; } break; #endif /* HT_LISTGROUP */ case NEWS_NEED_LIST: if (!news->sent) { status = SendCommand(request, news, "LIST", NULL); if (status == HT_WOULD_BLOCK) return HT_OK; else if (status == HT_ERROR) news->state = NEWS_ERROR; news->format = WWW_NNTP_LIST; news->sent = YES; } else { status = HTHost_read(HTNet_host(net), net); if (status == HT_WOULD_BLOCK) return HT_OK; else if (status == HT_OK) news->state = NEWS_NEED_BODY; else if (status == HT_LOADED) { news->state = (news->repcode/100 == 2) ? NEWS_SUCCESS : NEWS_ERROR; } else news->state = NEWS_ERROR; news->sent = NO; } break; case NEWS_NEED_GROUP: if (!news->sent) { status = SendCommand(request, news, "GROUP", news->name); if (status == HT_WOULD_BLOCK) return HT_OK; else if (status == HT_ERROR) news->state = NEWS_ERROR; news->sent = YES; } else { status = HTHost_read(HTNet_host(net), net); if (status == HT_WOULD_BLOCK) return HT_OK; else if (status == HT_LOADED) { if (news->repcode/100 == 2) { if (sscanf(news->reply, "%d%d%d", &news->total, &news->first, &news->last) == 3) { if (MaxArt && news->total>MaxArt) news->last = news->first-MaxArt; news->current = news->first; /* If no content in this group */ if (news->first == news->last) { HTRequest_addError(request, ERR_FATAL, NO, HTERR_NO_CONTENT, NULL, 0, "HTLoadNews"); news->state = NEWS_NO_DATA; break; } news->state = NEWS_NEED_XOVER; } else news->state = NEWS_ERROR; } else news->state = NEWS_ERROR; } else news->state = NEWS_ERROR; news->sent = NO; } break; case NEWS_NEED_XOVER: if (!news->sent) { char buf[20]; sprintf(buf, "%d-%d", news->first, news->last); status = SendCommand(request, news, "XOVER", buf); if (status == HT_WOULD_BLOCK) return HT_OK; else if (status == HT_ERROR) news->state = NEWS_ERROR; news->format = WWW_NNTP_OVER; news->sent = YES; } else { status = HTHost_read(HTNet_host(net), net); if (status == HT_WOULD_BLOCK) return HT_OK; else if (status == HT_OK) news->state = NEWS_NEED_BODY; else if (status == HT_LOADED) { if (news->repcode/100 == 2) news->state = NEWS_SUCCESS; else { news->format = WWW_NNTP_HEAD; news->state = NEWS_NEED_HEAD; } } else news->state = NEWS_ERROR; news->sent = NO; } break; case NEWS_NEED_HEAD: if (!news->sent) { char buf[10]; sprintf(buf, "%d", news->current++); status = SendCommand(request, news, "HEAD", buf); if (status == HT_WOULD_BLOCK) return HT_OK; else if (status == HT_ERROR) news->state = NEWS_ERROR; news->sent = YES; } else { status = HTHost_read(HTNet_host(net), net); if (status == HT_WOULD_BLOCK) return HT_OK; else if (status == HT_LOADED) { if (news->repcode/100 == 2) { if (news->current > news->last) news->state = NEWS_SUCCESS; } else news->state = NEWS_ERROR; } else news->state = NEWS_ERROR; news->sent = NO; } break; case NEWS_NEED_POST: { HTStream * oldinput = HTRequest_inputStream(request); HTStream * newinput = HTNewsPost_new(request, HTBuffer_new(oldinput, request,512)); HTRequest_setInputStream(request, newinput); /* Remember to convert to CRLF */ } news->state = NEWS_NEED_BODY; break; case NEWS_NEED_BODY: if (type == HTEvent_WRITE || type == HTEvent_BEGIN) { if (HTRequest_isDestination(request)) { HTRequest * source = HTRequest_source(request); HTNet * srcnet = HTRequest_net(source); if (srcnet) { HTHost_register(HTNet_host(srcnet), srcnet, HTEvent_READ); HTHost_unregister(HTNet_host(srcnet), srcnet, HTEvent_WRITE); } return HT_OK; } /* ** Should we use the input stream directly or call the post ** callback function to send data down to the network? */ { HTStream * input = HTRequest_inputStream(request); HTPostCallback * pcbf = HTRequest_postCallback(request); if (pcbf) { status = pcbf(request, input); if (status == HT_PAUSE || status == HT_LOADED) type = HTEvent_READ; } else { status = (*input->isa->flush)(input); type = HTEvent_READ; } if (status == HT_WOULD_BLOCK) return HT_OK; } status = request->PostCallback ? request->PostCallback(request, request->input_stream) : (*request->input_stream->isa->flush)(request->input_stream); if (status == HT_WOULD_BLOCK) return HT_OK; else type = HTEvent_READ; /* Trick to ensure that we do READ */ } else if (type == HTEvent_READ) { status = HTHost_read(HTNet_host(net), net); if (status == HT_WOULD_BLOCK) return HT_OK; else if (status == HT_LOADED) news->state = NEWS_SUCCESS; else news->state = NEWS_ERROR; } else { news->state = NEWS_ERROR; } break; case NEWS_SUCCESS: HTNewsCleanup(request, HT_LOADED); return HT_OK; break; case NEWS_NO_DATA: HTNewsCleanup(request, HT_NO_DATA); return HT_OK; break; case NEWS_ERROR: HTNewsCleanup(request, HT_NOT_FOUND); return HT_OK; break; } } /* End of while(1) */ }