/* ** Libwww extension test file ** ** This program shows how to use the extension methods available in Libwww. ** Through these methods (METHOD_EXT_0 to METHOD_EXT_6), applications may ** register (by calling HTMethod_setExtensionMethod) new methods, that may be ** unknown for the Libwww, and use them for its request. See HTMethod.html for ** details. ** ** More libwww samples can be found at "http://www.w3.org/Library/Examples/" ** ** Copyright © 1995-1998 World Wide Web Consortium, (Massachusetts ** Institute of Technology, Institut National de Recherche en ** Informatique et en Automatique, Keio University). All Rights ** Reserved. This program is distributed under the W3C's Software ** Intellectual Property License. This program is distributed in the hope ** that it will be useful, but WITHOUT ANY WARRANTY; without even the ** implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR ** PURPOSE. See W3C License http://www.w3.org/Consortium/Legal/ for more ** details. ** ** ** Authors: ** MKP Manuele Kirsch Pinheiro (Manuele.Kirsch_Pinheiro@inrialpes.fr, ** manuele@inf.ufrgs.br) ** _ Project CEMT (INRIA Rhone-Alpes,France / UFRGS-II,Brazil) _ ** ** History: ** fev, 2002 created MKP ** ** $Id: myext2.c,v 1.2 2002/05/30 18:08:56 kirschpi Exp $ */ #include "WWWLib.h" #include "WWWInit.h" #include "WWWApp.h" #include "WWWXML.h" #include "WWWHTTP.h" #ifndef W3C_VERSION #define W3C_VERSION "Unspecified" #endif #define APP_NAME "MyExt" #define APP_VERSION "4.0" #if defined(__svr4__) #define CATCH_SIG #endif /* ** error codes for use in error_callback filter */ #define ERR_UNKNOWN 0x0 #define ERR_FATAL 0x1 #define ERR_NON_FATAL 0x2 #define ERR_WARN 0x4 #define ERR_INFO 0x8 /* ** Some compilers, like MSVC, doesn't know STDIN_FILENO */ #ifndef STDIN_FILENO #define STDIN_FILENO fileno(stdin) #endif /* commands */ #define VIEW 1 #define SET 2 #define DEL 3 #define PROPPATCH METHOD_EXT_0 #define PROPFIND METHOD_EXT_1 /* ** Our stream */ struct _HTStream { const HTStreamClass * isa; HTStream * target; HTRequest * request; int version; BOOL endHeader; BOOL transparent; }; /* ** Command line structure */ typedef struct _CmdLine { int cmd; /* command */ char *url; /* url */ char *prop; /* property name */ char *ns; /* property namespace */ char *val; /* property val */ } CmdLine; /* ** Request context */ typedef struct _Context { int depth; } Context; /* ** Application context */ typedef struct _App { HTRequest * console_request; HTEvent * console_event; HTList * active; /* List of active contexts */ } App; /* ** Callback functions */ PRIVATE HTEventCallback console_parser; PRIVATE HTNetAfter request_terminater; PRIVATE HTNetAfter error_callback; PRIVATE int printer (const char * fmt, va_list pArgs); PRIVATE int tracer (const char * fmt, va_list pArgs); /* ** Prototypes */ PRIVATE CmdLine * CmdLine_new (const char *buffer); PRIVATE BOOL CmdLine_delete (CmdLine * me); PRIVATE App * App_new (void); PRIVATE BOOL App_delete (App * me); PRIVATE void Context_new (HTRequest * request, App * app); PRIVATE void Context_delete (Context * ctx); PRIVATE HTRequest * Request_new (App * app,HTMethod method); PRIVATE BOOL Request_delete (App * app, HTRequest * request); PRIVATE int request_terminater (HTRequest * request, HTResponse * response, void * param, int status); PRIVATE int console_parser (SOCKET s, void * param, HTEventType type); PRIVATE int error_callback (HTRequest * request, HTResponse * response, void * param, int status); PRIVATE int mypcbf (HTRequest * request, HTStream * target); PRIVATE App * Init (void); PRIVATE void my_get_document (App *app, CmdLine * line); PRIVATE char * create_body (CmdLine *line); /* --------------------------------------------------------------------------*/ /* ------------------------------------------------------------------------- */ /* EXPAT HANDLERS */ /* ------------------------------------------------------------------------- */ PRIVATE void XML_startElement (void * userData, const XML_Char * name, const XML_Char ** atts) { int *depth = (int *)userData; int i=0; if (name) { HTPrint ("\n"); for (i=0;i<(*depth);i++) HTPrint (" "); HTPrint("%s ", name); for (i = 0; atts[i]; i += 2) HTPrint(" %s='%s'", atts[i], atts[i + 1]); } *depth += 1; } PRIVATE void XML_endElement (void * userData, const XML_Char * name) { int *depth = (int *)userData; *depth -= 1; } PRIVATE void XML_characterData (void * userData, const XML_Char * s, int len) { int i=0; if (len>1) { HTPrint (": "); for (i=0;idepth) ); } } PRIVATE void HTXML_newInstance (HTStream * me, HTRequest * request, HTFormat target_format, HTStream * target_stream, XML_Parser xmlparser, void * context) { /*HTPrint ("MyExt: HTXML_newInstance\n");*/ if (me && xmlparser) { if (request && HTRequest_method(request)==PROPFIND) HTXML_setHandlers(xmlparser,request); } } /* ------------------------------------------------------------------------- * ** New/Delete functions * ** ------------------------------------------------------------------------- */ /* ** CmdLine_new: creates a command line object ** A command line has this syntax : [propname:namespace][= value] ** A command is v (view), s (set) or d (delete) ** An example : s http://foo.bar/ name:http://host/mydtd.dtd = my_file */ PRIVATE CmdLine * CmdLine_new (const char *buffer) { char *line = (char *)buffer; char *url, *ns, *prop, *val; char *rurl, *rns, *rprop, *rval; int cmd; int i,j,len; CmdLine *res = NULL; BOOL status = YES; url = ns = prop = val = NULL; rurl = rns = rprop = rval = NULL; len = strlen (line); cmd = 0; i = j = 0; /*--- get command ---*/ while (line[i]==' ' && icmd = cmd; res->url = rurl; res->prop = rprop; res->ns = rns; res->val = rval; } else { if (rurl) HT_FREE(rurl); if (rprop) HT_FREE(rprop); if (rns) HT_FREE(rns); if (rval) HT_FREE(rval); res = NULL; } return res; } /* ** CmdLine_delete : removes a command line object */ PRIVATE BOOL CmdLine_delete (CmdLine * me) { if (me) { if (me->url) free (me->url); if (me->prop) free (me->prop); if (me->ns) free (me->ns); if (me->val) free (me->val); free (me); return YES; } return NO; } /* ** App_new : creates a new application context ** Returns : App * application context */ PRIVATE App * App_new (void) { App * me = NULL; if ((me = (App *) HT_CALLOC(1, sizeof(App))) == NULL) HT_OUTOFMEM("App_new"); /* setting everybody */ me->console_request = HTRequest_new(); me->console_event = HTEvent_new(console_parser, me, HT_PRIORITY_MAX, -1); me->active = HTList_new(); /* Register stdin as our console */ if (isatty(STDIN_FILENO)) HTEventList_register(STDIN_FILENO, HTEvent_READ, me->console_event); /* Add our own request terminate handler */ HTNet_addAfter(request_terminater, NULL, me, HT_ALL, HT_FILTER_LAST); HTPrint ("MyExt: Application context created\n"); /* Set Extension method 0 to PROPPATCH and 1 to PROPFIND */ HTPrint ("MyExt: Setting method %x to PROPPATCH\n",PROPPATCH); if (!HTMethod_setExtensionMethod (PROPPATCH, "PROPPATCH",YES)) { HTPrint ("MyExt: ERROR _ Unable to set extension method"); exit(1); } HTPrint ("MyExt: Setting method %x to PROPFIND\n",PROPFIND); if (!HTMethod_setExtensionMethod (PROPFIND, "PROPFIND",NO)) { HTPrint ("MyExt: ERROR _ Unable to set extension method"); exit(1); } HTPrint ("Enter: [propname:namespace][= valor]\n"); HTPrint (" or Q to exit \n"); HTPrint ("command := v (view), s (set) or d (delete)\n"); HTPrint ("examples : s http://foo.bar/ propertyname:http://foo.bar/foodtd.dtd = property_value\n"); HTPrint (" d http://foo.bar/ propertyname:http://foo.bar/foodtd.dtd \n"); HTPrint (" v http://foo.bar/\n"); HTPrint (">\n "); return me; } /* ** App_delete : removes an application context object ** Parameter : App * app application context to be removed ** Returns : BOOL YES - operation succed ** NO - operation failed */ PRIVATE BOOL App_delete (App * me) { HTRequest * req = NULL; HTPrint ("MyExt: Removing application context\n"); if (me) { /* killing any remaining active requests */ HTNet_killAll(); /* freeing all remaining request objects */ while (!HTList_isEmpty(me->active)) { req = (HTRequest *) HTList_nextObject (me->active); if (req) Request_delete (me,req); } /* clean up everything */ HTRequest_delete(me->console_request); HTEvent_delete (me->console_event); HTList_free (me->active); /* clean up extension method */ HTPrint ("Removing extension methods\n"); if (HTMethod_deleteExtensionMethod (PROPPATCH) && HTMethod_deleteExtensionMethod (PROPFIND)) HTPrint ("MyExt: Extension methods deleted \n"); HT_FREE(me); /* stopping event loop */ HTEventList_stopLoop(); /* Terminate libwww */ HTProfile_delete(); return YES; } return NO; } /* ** Context_new : creates a new request context ** Parameters : HTRequest * request ** App * app */ PRIVATE void Context_new (HTRequest * request, App * app) { Context * ctx = NULL; if ( (ctx = (Context *) HT_CALLOC(1, sizeof(Context))) == NULL ) { App_delete (app); HT_OUTOFMEM("Contect_new"); } /*HTPrint ("MyExt: Setting request context...\n");*/ ctx->depth = 0; HTRequest_setContext(request,ctx); } /* ** Context_delete : removes the request context ** Parameters : Context * ctx */ PRIVATE void Context_delete (Context * ctx) { if (ctx) { /*HTPrint ("MyExt: Removing request context...\n");*/ HT_FREE (ctx); } } /* ** Request_new : creates a new request ** Parameters : App * app application context ** Returns : HTRequest * */ PRIVATE HTRequest * Request_new (App * app, HTMethod method) { HTRequest * request = HTRequest_new(); HTPrint ("MyExt: creating a new request\n"); HTRequest_addConnection(request,"close",""); /* setting request format */ HTRequest_setOutputFormat(request, HTAtom_for ("text/xml")); HTRequest_setOutputFormat(request, WWW_SOURCE); /* method */ /* HTPrint ("MyExt: Setting method in the request\n"); */ HTRequest_setMethod (request,method); /* some headers */ HTRequest_addExtraHeader (request,"Depth","0"); HTRequest_addCacheControl (request, "no-cache",""); HTRequest_addGnHd (request,HT_G_PRAGMA_NO_CACHE); /* set local filters */ HTRequest_addAfter (request,error_callback, NULL, app, \ HT_ERROR, HT_FILTER_LAST, NO); if (!app->active) app->active = HTList_new(); HTList_addObject(app->active, request); HTRequest_setFlush(request, YES); /* register XML callback */ HTXMLCallback_registerNew (HTXML_newInstance, NULL); return request; } /* ** Request_delete : removes a request ** Parameters : App * app application context ** HTRequest * request ** Returns : BOOL YES if operation succeed ** NO if operation failed */ PRIVATE BOOL Request_delete (App * app, HTRequest * request) { /* HTPrint ("MyExt: removing request\n"); */ if (app && app->active && request) { HTPrint ("MyExt: Request deleted \n"); HTList_removeObject(app->active, request); HTRequest_delete(request); return YES; } return NO; } /* ------------------------------------------------------------------------- * ** Global/local Filters * ** ------------------------------------------------------------------------- */ /* ** printer : output messages function */ PRIVATE int printer (const char * fmt, va_list pArgs) { return (vfprintf(stderr, fmt, pArgs)); } /* ** trace : trace messages function */ PRIVATE int tracer (const char * fmt, va_list pArgs) { return (vfprintf(stderr, fmt, pArgs)); } /* ** console_parser : treats the user's interaction on the console ** Function's type : HTEventCallback */ PRIVATE int console_parser (SOCKET s, void * param, HTEventType type) { App * app = (App *) param; char buf[2048]; CmdLine * line; /* reading console */ if (!fgets(buf, sizeof(buf), stdin)) { HTPrint ("Error reading stdin\n"); return HT_ERROR; } if (toupper(buf[0]) == 'Q') { /* Quit the program */ App_delete(app); exit (0); } else { /* take the target address */ HTPrint ("MyExt: Console readed **%s**\n",buf); if ( (line = CmdLine_new (buf))==NULL ) { HTPrint ("Wrong command line\n"); return HT_ERROR; } my_get_document (app,line); } return HT_OK; } /* ** request_terminater : global filter to delete the requests ** Funtion's type : HTNetAfter */ PRIVATE int request_terminater (HTRequest * request, HTResponse * response, void * param, int status) { App * app = (App *) param; Context * ctx = NULL; /* HTPrint ("MyExt: GLOBAL FILTER (request_terminater)\n"); */ if (status/100 == 2) HTPrint ("MyExt: SUCCED!\n"); if (response) HTPrint ("\tReason %s\n",(HTResponse_reason(response))? \ HTResponse_reason(response):"NULL"); HTPrint ("\tStatus %d\n",status); ctx = (Context *) HTRequest_context(request); /*HTPrint ("MyExt: context object deleted\n");*/ Context_delete (ctx); Request_delete(app, request); HTPrint ("Ready!\n"); HTPrint ("Enter: [propname:namespace][= valor]\n"); HTPrint (" or Q to exit\n"); HTPrint ("> "); return HT_OK; } /* ** error_callback : global after filter to treat the request's errors ** Function's type : HTNetAfter */ PRIVATE int error_callback (HTRequest * request, HTResponse * response, void * param, int status) { App * app = (App *) param; HTList * error_list = NULL; HTError * error = NULL; HTPrint ("MyExt: ERROR CALLBACK\n"); HTPrint ("\tapp %s \n\trequest %s \n\tresponse %s \n\tstatus %d\n", \ (app)?"OK":"NULL",(request)?"OK":"NULL",\ (response)?"OK":"NULL",status); if (request) { error_list = HTRequest_error (request); while (error_list && (error = (HTError *) HTList_nextObject(error_list))) { HTPrint ("\tError location %s\n",HTError_location(error)); switch (HTError_severity(error)) { case ERR_UNKNOWN : HTPrint ("\tSeverity : UNKNOWN\n"); break; case ERR_FATAL : HTPrint ("\tSeverity : FATAL\n"); break; case ERR_NON_FATAL : HTPrint ("\tSeverity : NON FATAL\n"); break; case ERR_WARN : HTPrint ("\tSeverity : WARN\n"); break; case ERR_INFO : HTPrint ("\tSeverity : INFO\n"); break; default : HTPrint ("\tSeverity : %Xd\n",HTError_severity(error)); break; } } } return HT_OK; } /* --------------------------------------------------------------------------*/ /* ENTITY CALLBACK */ /* --------------------------------------------------------------------------*/ /* ** Entity Callback */ PRIVATE int mypcbf (HTRequest * request, HTStream * target) { HTParentAnchor * entity = HTRequest_entityAnchor(request); HTTRACE(APP_TRACE, "Posting Data from MY callback function\n"); if (!request || !entity || !target) return HT_ERROR; { BOOL chunking = NO; int status; char * document = (char *) HTAnchor_document(entity); int len = HTAnchor_length(entity); if (!document) { HTTRACE(PROT_TRACE, "Posting Data No document\n"); return HT_ERROR; } /* ** If the length is unknown (-1) then see if the document is a text ** type and in that case take the strlen. If not then we don't know ** how much data we can write and must stop */ if (len < 0) { HTFormat actual = HTAnchor_format(entity); HTFormat tmplate = HTAtom_for("text/*"); if (HTMIMEMatch(tmplate, actual)) { len = strlen(document); /* Naive! */ chunking = YES; } else { HTTRACE(PROT_TRACE, "Posting Data Must know the length of document %p\n" _ document); return HT_ERROR; } } /* Send the data down the pipe */ status = (*target->isa->put_block)(target, document, len); if (status == HT_WOULD_BLOCK) { HTTRACE(PROT_TRACE, "Posting Data Target WOULD BLOCK\n"); return HT_WOULD_BLOCK; } else if (status == HT_PAUSE) { HTTRACE(PROT_TRACE, "Posting Data Target PAUSED\n"); return HT_PAUSE; } else if (chunking && status == HT_OK) { HTTRACE(PROT_TRACE, "Posting Data Target is SAVED using chunked\n"); return (*target->isa->put_block)(target, "", 0); } else if (status == HT_LOADED || status == HT_OK) { HTTRACE(PROT_TRACE, "Posting Data Target is SAVED\n"); (*target->isa->flush)(target); return HT_LOADED; } else if (status > 0) { /* Stream specific return code */ HTTRACE(PROT_TRACE, "Posting Data. Target returns %d\n" _ status); return status; } else { /* we have a real error */ HTTRACE(PROT_TRACE, "Posting Data Target ERROR %d\n" _ status); return status; } } } /* ------------------------------------------------------------------------- * ** Other Functions * ** ------------------------------------------------------------------------- */ /* ** Init : application initialization ** Returns : App * application context */ PRIVATE App * Init (void) { App * app = NULL; HTPrint ("MyExt: starting application\n"); /* Initiate libwww */ HTProfile_newHTMLNoCacheClient(APP_NAME, APP_VERSION); HTTP_setConnectionMode(HTTP_11_NO_PIPELINING); app = App_new(); /* Gotta set up our own traces */ HTPrint_setCallback(printer); HTTrace_setCallback(tracer); #if 0 HTSetTraceMessageMask("sapol"); #endif HTEventList_newLoop(); return app; } /* ** my_get_document: retrives the document ** Parameters: App * app application context ** char * dst URL destiny */ PRIVATE void my_get_document (App *app, CmdLine * line) { HTRequest * request = NULL; HTAnchor * dst = NULL; char * full_dst; char * cwd; char * body = create_body(line); cwd = HTGetCurrentDirectoryURL(); full_dst = HTParse (line->url,cwd,PARSE_ALL); dst = HTAnchor_findAddress (full_dst); /* HTPrint ("MyExt: loading document\n");*/ request = Request_new (app, (line->cmd==VIEW)?PROPFIND:PROPPATCH); HTRequest_setAnchor (request,dst); /* set body */ if (body) { HTPrint ("MyExt: Setting entity message body to %s\n",body,strlen(body)); if (line->cmd==VIEW) { /* set message body */ HTRequest_setMessageBody (request,body); HTRequest_setMessageBodyLength (request,strlen(body)); HTRequest_setMessageBodyFormat (request,HTAtom_for("text/xml")); } else { HTParentAnchor *entity = HTTmpAnchor(NULL); HTAnchor_setDocument(entity, body); HTAnchor_setFormat(entity, HTAtom_for ("text/xml")); HTAnchor_setLength(entity, strlen(body)); HTRequest_setEntityAnchor (request,entity); /* HTPrint ("MyExt: Entity format %s length %d\n", \ HTAtom_name(HTAnchor_format(entity)), \ HTAnchor_length(entity)); */ /* if we do not register our own PostCallback, libwww will not * send the request entity body */ HTRequest_setPostCallback(request, mypcbf); } } if ( request && (HTLoad (request,NO))) { /* HTPrint ("MyExt: setting context\n");*/ Context_new (request,app); } else HTPrint ("MyExt: load failed\n"); /* HTParentAnchor entity keep using it - do not delete! */ /* HT_FREE (body); */ CmdLine_delete (line); } PRIVATE char * create_body (CmdLine * line) { char * body = NULL; char buf [2048]; if (line) { strcpy (buf,"\n"); if (line->cmd==VIEW) { strcat (buf," "); strcat (buf," "); } else { strcat (buf,"\n"); if (line->cmd==SET) strcat (buf," \n"); else if (line->cmd==DEL) strcat (buf," \n"); else return NULL; /* invalid command */ strcat (buf," \n"); strcat (buf," prop && line->ns) { strcat (buf,line->prop); strcat (buf," xmlns:ns=\""); strcat (buf,line->ns); strcat (buf,"\">"); } else return NULL; /* no property or property namespace */ if (line->cmd==SET) { strcat (buf,(line->val)?line->val:""); } strcat (buf,"prop); strcat (buf,">\n"); strcat (buf," \n"); strcat (buf,(line->cmd==SET)?" \n":" \n"); strcat (buf,""); } StrAllocCopy (body,buf); } return body; } /* ******************************************************************** */ int main (int argc, char ** argv) { App * app = NULL; app = Init(); return 0; }