/* ** LOCK requests */ PRIVATE BOOL lock_request (Cmdline * arg) { HTDAVHeaders * headers = HTDAVHeaders_new(); HTRequest * request = create_request (); HTAnchor * dst = HTAnchor_findAddress(arg->request_uri); HTParentAnchor * src = NULL; HTParentAnchor * base = NULL; BOOL status = NO; char * data = NULL; if (arg->I) { HTPrint ("Adding If header %s\n",arg->I); HTDAV_setIfHeader (headers,arg->I); } if (arg->arg1) { data = create_body (arg->arg1); HTPrint ("xml body %s\n",data); /* chose the func */ if (arg->func==1) { src = HTTmpAnchor(NULL); HTAnchor_setDocument(src, data); HTAnchor_setFormat(src, HTAtom_for ("text/xml")); HTAnchor_setLength(src, strlen(data)); } } if (arg->base_str && *(arg->base_str)) base = (HTParentAnchor *) HTAnchor_findAddress(arg->base_str); if (arg->D) HTDAV_setDepthHeader (headers,arg->D); if (arg->T) HTDAV_setTimeoutHeader (headers,arg->T); HTPrint ("function %d src? %s\n",arg->func,(src)?"yes":"no"); switch (arg->func) { case 1: status = HTLOCKDocumentAnchor (request,dst,src,headers); break; case 2: status = HTLOCKAnchor (request,dst,data,headers); break; case 3: status = HTLOCKAbsolute (request,arg->request_uri,data,headers); break; case 4: status = HTLOCKRelative (request,arg->request_uri,base,data,headers); break; } return status; }
/* ** MOVE requests */ PRIVATE BOOL move_request ( Cmdline * arg ) { HTDAVHeaders * headers = HTDAVHeaders_new(); HTRequest * request = create_request (); HTAnchor * src = HTAnchor_findAddress(arg->request_uri); HTParentAnchor * body = NULL; HTParentAnchor * base = NULL; BOOL status = NO; if (arg->arg1) { HTPrint ("Adding Destination header %s\n",arg->arg1); HTDAV_setDestinationHeader (headers,arg->arg1); } if (arg->I) { HTPrint ("Adding If header %s\n",arg->I); HTDAV_setIfHeader (headers,arg->I); } /* chose the func */ if (arg->func==2 && arg->arg2 ) { body = HTTmpAnchor(NULL); HTAnchor_setDocument(body, arg->arg2); HTAnchor_setFormat(body, HTAtom_for ("text/xml")); HTAnchor_setLength(body, strlen(arg->arg2)); } if (arg->base_str && *(arg->base_str)) base = (HTParentAnchor *) HTAnchor_findAddress(arg->base_str); if (arg->D) HTDAV_setDepthHeader (headers,arg->D); if (arg->O == 'T') HTDAV_setOverwriteHeader (headers,YES); else if (arg->O == 'F') HTDAV_setOverwriteHeader (headers,NO); switch (arg->func) { case 1: status = HTMOVEAnchor (request,src,arg->arg2,headers); break; case 2: status = HTMOVEDocumentAnchor (request,src,body,headers); break; case 3: status = HTMOVEAbsolute (request,arg->request_uri,arg->arg2, headers); break; case 4: status = HTMOVERelative (request,arg->request_uri,base,arg->arg2,headers); break; } return status; }
/* ** PROPPATCH requests */ PRIVATE BOOL proppatch_request (Cmdline * arg) { BOOL status = NO; HTDAVHeaders * headers = HTDAVHeaders_new(); HTRequest * request = create_request (); HTAnchor * dst = HTAnchor_findAddress(arg->request_uri); HTParentAnchor *base = NULL; HTParentAnchor *src = NULL; char * xmlbody = NULL; if (arg->arg1 && *(arg->arg1)) xmlbody = arg->arg1; else return NO; HTPrint ("xml body **%s**\n",xmlbody); if (arg->func==2) { src = HTTmpAnchor(NULL); HTAnchor_setDocument(src, xmlbody); HTAnchor_setFormat(src, HTAtom_for ("text/xml")); HTAnchor_setLength(src, strlen(xmlbody)); } if (arg->base_str && *(arg->base_str)) base = (HTParentAnchor *) HTAnchor_findAddress(arg->base_str); HTPrint ("setting headers\n"); if (arg->I && *(arg->I)) { HTPrint ("Adding If header %s\n",arg->I); HTDAV_setIfHeader (headers,arg->I); } HTPrint ("Chosing func\n"); switch (arg->func) { case 1: status = HTPROPPATCHAnchor (request,dst,xmlbody,headers); break; case 2: status = HTPROPPATCHDocumentAnchor (request,dst,src,headers); break; case 3: status = HTPROPPATCHAbsolute (request,arg->request_uri,xmlbody,headers); break; case 4: status = HTPROPPATCHRelative (request,arg->request_uri,base,xmlbody,headers); break; } return status; }
/* ** PROPFIND requests */ PRIVATE BOOL propfind_request (Cmdline * arg) { BOOL status = NO; HTDAVHeaders * headers = HTDAVHeaders_new(); HTRequest * request = create_request (); HTAnchor * dst = HTAnchor_findAddress(arg->request_uri); HTParentAnchor *base = NULL; HTParentAnchor *src = NULL; char * xmlbody = NULL; /* chose the func */ HTPrint ("should we set the xml body?\n"); if (arg->arg1 && *(arg->arg1)) { if (!strcasecomp (arg->arg1,"allprop") || !strcasecomp (arg->arg1,"propname")) xmlbody = create_propbody (arg->arg1); else xmlbody = arg->arg1; HTPrint ("xml body %s\n",xmlbody); } if (arg->func==2 && xmlbody && *xmlbody) { src = HTTmpAnchor(NULL); HTAnchor_setDocument(src, xmlbody); HTAnchor_setFormat(src, HTAtom_for ("text/xml")); HTAnchor_setLength(src, strlen(xmlbody)); } if (arg->base_str && *(arg->base_str)) base = (HTParentAnchor *) HTAnchor_findAddress(arg->base_str); HTPrint ("setting headers\n"); if (arg->D) HTDAV_setDepthHeader (headers,arg->D); switch (arg->func) { case 1: status = HTPROPFINDAnchor (request,dst,xmlbody,headers); break; case 2: status = HTPROPFINDDocumentAnchor (request,dst,src,headers); break; case 3: status = HTPROPFINDAbsolute (request,arg->request_uri,xmlbody,headers); break; case 4: status = HTPROPFINDRelative (request,arg->request_uri,base,xmlbody,headers); break; } return status; }
PRIVATE int buf_free (HTStream * me) { int status = HT_OK; /* ** If the buffer has not been flushed explicit and we are a pipe buffer ** then we don't free it. */ if (me->mode & HT_BM_PIPE && me->state != HT_BS_TRANSPARENT) { HTTRACE(STREAM_TRACE, "PipeBuffer Waiting to be flushed\n"); return HT_OK; } /* ** Should we count the content length and assign it to the ** anchor? */ if (me->mode & HT_BM_COUNT && me->request) { HTParentAnchor * anchor = HTRequest_anchor(me->request); HTTRACE(STREAM_TRACE, "Buffer........ Calculated content-length: %d\n" _ me->conlen); HTAnchor_setLength(anchor, me->conlen); } /* ** Flush the buffered data - even if we are paused. That is, we always ** flush - except if we have already flushed the buffer, of course. ** Also, we don't flush if we are a PIPE buffer. Then the flush MUST be ** called explicitly and the buffer can not be freed before this ** happens. */ if ((status = buf_flush(me)) != HT_OK) return status; if ((status = (*me->target->isa->_free)(me->target)) != HT_OK) return status; HT_FREE(me); return status; }
PRIVATE int FileEvent (SOCKET soc, void * pVoid, HTEventType type) { file_info *file = pVoid; /* Specific access information */ int status = HT_ERROR; HTNet * net = file->net; HTRequest * request = HTNet_request(net); HTParentAnchor * anchor = HTRequest_anchor(request); /* ** Initiate a new file structure and bind to request structure ** This is actually state FILE_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, "HTLoadFile"); FileCleanup(request, HT_INTERRUPTED); return HT_OK; } /* Now jump into the machine. We know the state from the previous run */ while (1) { switch (file->state) { case FS_BEGIN: /* We only support safe (GET, HEAD, etc) methods for the moment */ if (!HTMethod_isSafe(HTRequest_method(request))) { HTRequest_addError(request, ERR_FATAL, NO, HTERR_NOT_ALLOWED, NULL, 0, "HTLoadFile"); file->state = FS_ERROR; break; } /* Check whether we have access to local disk at all */ if (HTLib_secure()) { HTTRACE(PROT_TRACE, "LoadFile.... No access to local file system\n"); file->state = FS_TRY_FTP; break; } file->local = HTWWWToLocal(HTAnchor_physical(anchor), "", HTRequest_userProfile(request)); if (!file->local) { file->state = FS_TRY_FTP; break; } /* Create a new host object and link it to the net object */ { HTHost * host = NULL; if ((host = HTHost_new("localhost", 0)) == NULL) return HT_ERROR; HTNet_setHost(net, host); if (HTHost_addNet(host, net) == HT_PENDING) { HTTRACE(PROT_TRACE, "HTLoadFile.. Pending...\n"); /* move to the hack state */ file->state = FS_PENDING; return HT_OK; } } file->state = FS_DO_CN; break; case FS_PENDING: /* ** 2000/08/10 JK : This is a funny state. Because of the ** internal libwww stacks, when doing multiple local ** requests (e.g., while using the Robot), we need to ask ** again for the host object. If we had jumped directly to ** the FS_DO_CN state, libwww would have blocked because ** of socket starvation. ** This state is similar to FS_BEGINNING, but just requests ** the host object. ** YES. THIS IS AN UGLY HACK!! */ { HTHost * host = NULL; if ((host = HTHost_new("localhost", 0)) == NULL) return HT_ERROR; HTNet_setHost(net, host); if (HTHost_addNet(host, net) == HT_PENDING) { HTTRACE(PROT_TRACE, "HTLoadFile.. Pending...\n"); file->state = FS_PENDING; return HT_OK; } } file->state = FS_DO_CN; break; case FS_DO_CN: /* ** If we have to do content negotiation then find the object that ** fits best into either what the client has indicated in the ** accept headers or what the client has registered on its own. ** The object chosen can in fact be a directory! However, content ** negotiation only makes sense if we can read the directory! ** We stat the file in order to find the size and to see it if ** exists. */ if (HTRequest_negotiation(request) && HTMethod_isSafe(HTRequest_method(request))) { char * conneg = HTMulti(request, file->local,&file->stat_info); if (conneg) { HT_FREE(file->local); file->local = conneg; HTAnchor_setPhysical(anchor, conneg); HTTRACE(PROT_TRACE, "Load File... Found `%s\'\n" _ conneg); } else { HTTRACE(PROT_TRACE, "Load File... Not found - even tried content negotiation\n"); HTRequest_addError(request, ERR_FATAL, NO, HTERR_NOT_FOUND, NULL, 0, "HTLoadFile"); file->state = FS_ERROR; break; } } else { if (HT_STAT(file->local, &file->stat_info) == -1) { HTTRACE(PROT_TRACE, "Load File... Not found `%s\'\n" _ file->local); HTRequest_addError(request, ERR_FATAL, NO, HTERR_NOT_FOUND, NULL, 0, "HTLoadFile"); file->state = FS_ERROR; break; } } /* ** Check to see if the 'localname' is in fact a directory. ** Note that we can't do a HEAD on a directory */ if (((file->stat_info.st_mode) & S_IFMT) == S_IFDIR) { if (HTRequest_method(request) == METHOD_GET) file->state = FS_PARSE_DIR; else { HTRequest_addError(request, ERR_INFO, NO, HTERR_NO_CONTENT, NULL, 0, "HTLoadFile"); file->state = FS_NO_DATA; } break; } /* ** If empty file then only serve it if it is editable. We also get ** the bindings for the file suffixes in lack of better bindings */ { BOOL editable = HTEditable(file->local, &file->stat_info); if (file_suffix_binding) HTBind_getAnchorBindings(anchor); if (editable) HTAnchor_appendAllow(anchor, METHOD_PUT); /* Set the file size */ if (file->stat_info.st_size) HTAnchor_setLength(anchor, file->stat_info.st_size); /* Set the file last modified time stamp */ if (file->stat_info.st_mtime > 0) HTAnchor_setLastModified(anchor, file->stat_info.st_mtime); /* Check to see if we can edit it */ if (!editable && !file->stat_info.st_size) { HTRequest_addError(request, ERR_INFO, NO, HTERR_NO_CONTENT, NULL, 0, "HTLoadFile"); file->state = FS_NO_DATA; } else { file->state = (HTRequest_method(request)==METHOD_GET) ? FS_NEED_OPEN_FILE : FS_GOT_DATA; } } break; case FS_NEED_OPEN_FILE: status = HTFileOpen(net, file->local, HT_FB_RDONLY); if (status == HT_OK) { /* ** 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 = HTStreamStack(HTAnchor_format(anchor), HTRequest_outputFormat(request), HTRequest_outputStream(request), request, YES); 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); } /* ** Set up concurrent read/write if this request isn't the ** source for a PUT or POST. As source we don't start reading ** before all destinations are ready. If destination then ** register the input stream and get ready for read */ if (HTRequest_isSource(request) && !HTRequest_destinationsReady(request)) return HT_OK; HTRequest_addError(request, ERR_INFO, NO, HTERR_OK, NULL, 0, "HTLoadFile"); file->state = FS_NEED_BODY; /* If we are _not_ using preemptive mode and we are Unix fd's ** then return here to get the same effect as when we are ** connecting to a socket. That way, HTFile acts just like any ** other protocol module even though we are in fact doing ** blocking connect */ if (HTEvent_isCallbacksRegistered()) { if (!HTRequest_preemptive(request)) { if (!HTNet_preemptive(net)) { HTTRACE(PROT_TRACE, "HTLoadFile.. Returning\n"); HTHost_register(HTNet_host(net), net, HTEvent_READ); } else if (!file->timer) { HTTRACE(PROT_TRACE, "HTLoadFile.. Returning\n"); file->timer = HTTimer_new(NULL, ReturnEvent, file, 1, YES, NO); } return HT_OK; } } } else if (status == HT_WOULD_BLOCK || status == HT_PENDING) return HT_OK; else { HTRequest_addError(request, ERR_INFO, NO, HTERR_INTERNAL, NULL, 0, "HTLoadFile"); file->state = FS_ERROR; /* Error or interrupt */ } break; case FS_NEED_BODY: status = HTHost_read(HTNet_host(net), net); if (status == HT_WOULD_BLOCK) return HT_OK; else if (status == HT_LOADED || status == HT_CLOSED) { file->state = FS_GOT_DATA; } else { HTRequest_addError(request, ERR_INFO, NO, HTERR_FORBIDDEN, NULL, 0, "HTLoadFile"); file->state = FS_ERROR; } break; case FS_PARSE_DIR: status = HTFile_readDir(request, file); if (status == HT_LOADED) file->state = FS_GOT_DATA; else file->state = FS_ERROR; break; case FS_TRY_FTP: { char *url = HTAnchor_physical(anchor); HTAnchor *anchor; char *newname = NULL; StrAllocCopy(newname, "ftp:"); if (!strncmp(url, "file:", 5)) StrAllocCat(newname, url+5); else StrAllocCat(newname, url); anchor = HTAnchor_findAddress(newname); HTRequest_setAnchor(request, anchor); HT_FREE(newname); FileCleanup(request, HT_IGNORE); return HTLoad(request, YES); } break; case FS_GOT_DATA: FileCleanup(request, HT_LOADED); return HT_OK; break; case FS_NO_DATA: FileCleanup(request, HT_NO_DATA); return HT_OK; break; case FS_RETRY: FileCleanup(request, HT_RETRY); return HT_OK; break; case FS_ERROR: FileCleanup(request, HT_ERROR); return HT_OK; break; } } /* End of while(1) */ }
int main (int argc, char ** argv) { HTRequest * request = NULL; HTParentAnchor * src = NULL; HTAnchor * dst = NULL; char * dst_str = NULL; char * data = NULL; BOOL status = NO; /* Create a new premptive client */ HTProfile_newNoCacheClient("libwww-POST", "1.0"); /* Need our own trace and print functions */ HTPrint_setCallback(printer); HTTrace_setCallback(tracer); /* Add our own filter to update the history list */ HTNet_addAfter(terminate_handler, NULL, NULL, HT_ALL, HT_FILTER_LAST); /* Handle command line args */ if (argc >= 3) { dst_str = argv[1]; data = argv[2]; } else { HTPrint("Type the URI of the destination you want to POST to and the contents that you want to post.\n"); HTPrint("\t%s <destination> <data>\n", argv[0]); HTPrint("For example, %s http://myserver/destination.html \"This is some testdata\"\n", argv[0]); return -1; } if (data && *data && dst_str && *dst_str) { /* Make source relative to where we are */ char * cwd = HTGetCurrentDirectoryURL(); HTPrint("Posting to %s\n", dst_str); /* Create a request */ request = HTRequest_new(); /* Get an anchor object for the destination URI */ dst = HTAnchor_findAddress(dst_str); /* ** Dream up a source anchor (an editor can for example use this). ** After creation we associate the data that we want to post and ** set some metadata about what the data is. More formats can be found ** ../src/HTFormat.html */ src = HTTmpAnchor(NULL); HTAnchor_setDocument(src, data); HTAnchor_setFormat(src, WWW_PLAINTEXT); /* ** If not posting to an HTTP/1.1 server then content length MUST be ** there. If HTTP/1.1 then it doesn't matter as we just use chunked ** encoding under the covers */ HTAnchor_setLength(src, strlen(data)); /* POST the source to the dest */ status = HTPostAnchor(src, dst, request); /* We don't need these anymore */ HT_FREE(cwd); /* Go into the event loop... */ if (status == YES) HTEventList_loop(request); } return 0; }