Support for EDG 2.1-compatible queries.
authorZdeněk Salvet <salvet@ics.muni.cz>
Thu, 22 Jul 2004 13:54:53 +0000 (13:54 +0000)
committerZdeněk Salvet <salvet@ics.muni.cz>
Thu, 22 Jul 2004 13:54:53 +0000 (13:54 +0000)
org.glite.lb.common/interface/context-int.h
org.glite.lb.common/interface/mini_http.h
org.glite.lb.common/interface/xml_conversions.h
org.glite.lb.common/src/xml_conversions.c
org.glite.lb.server/Makefile
org.glite.lb.server/src/lb_proto.c
org.glite.lb.server/src/lb_xml_parse.c.T
org.glite.lb.server/src/lb_xml_parse.h
org.glite.lb.server/src/lb_xml_parse_V21.c.T [new file with mode: 0644]
org.glite.lb.server/src/lb_xml_parse_V21.h [new file with mode: 0644]

index e7a2954..55d4a61 100644 (file)
@@ -59,6 +59,7 @@ struct _edg_wll_Context {
        int             rgma_export;
        int             strict_locking; /* lock jobs for storing event too */
 
+       int             is_V21;         /* true if old (V21) request arrived */
        
 /* server limits */
        int             softLimit;
index beea6e7..c3d7bb0 100644 (file)
@@ -8,10 +8,12 @@
 /* XXX: not a good place for the folowing #def's but we ain't got better currently */
 /** protocol version */
 #define PROTO_VERSION           "3.0"
+#define PROTO_VERSION_V21      "2.1"
 /** backward protocol compatibility     */
 /* version separated by comma           */
 /* e.g. "1.0,1.2,1.3"                   */
 #define COMP_PROTO              "3.0"
+#define COMP_PROTO_V21         "2.0,2.1"
 
 
 /* subset of HTTP codes we return */
index bb1d307..246c488 100644 (file)
@@ -43,6 +43,7 @@ typedef struct _edg_wll_XML_ctx {
        edg_wll_QueryRec        **job_conditions;       /* temporal results */
        edg_wll_QueryRec        **event_conditions;
        enum edg_wll_QueryType  type;   
+       edg_wll_QueryRec        **conditions;
        int                     flags;
         edg_wlc_JobId          *jobsOutGlobal;   
         edg_wll_Event          *eventsOutGlobal;
index a953fae..d9a6dce 100644 (file)
@@ -35,6 +35,7 @@ void edg_wll_initXMLCtx(edg_wll_XML_ctx *c) {
        c->job_conditions = NULL;
        c->event_conditions = NULL;
        c->type = EDG_WLL_QUERY_TYPE_UNDEF;
+       c->conditions = NULL;
        c->flags = 0;
         c->jobsOutGlobal = NULL;
         c->eventsOutGlobal = NULL;
index dff25cb..b2b2106 100644 (file)
@@ -70,6 +70,7 @@ HELPERS:= -L${stagedir}/lib -lglite_wms_tls_ssl_helpers
 
 SERVER_OBJS:= bkserverd.o get_events.o index.o jobstat.o jobstat_supp.o \
        write2rgma.o lbs_db.o lb_html.o lb_http.o lb_proto.o lb_xml_parse.o \
+       lb_xml_parse_V21.o \
        lock.o openserver.o query.o userjobs.o db_store.o request.o store.o \
        stored_master.o srv_purge.o server_state.o dump.o lb_authz.o load.o \
        notification.o il_notification.o notif_match.o
index 240f795..4ab0453 100644 (file)
 #include "get_events.h"
 #include "purge.h"
 #include "lb_xml_parse.h"
+#include "lb_xml_parse_V21.h"
 
 
 #define METHOD_GET      "GET "
 #define METHOD_POST     "POST "
 
-#define KEY_JOBS               "/userJobs/"
 #define KEY_QUERY_JOBS         "/queryJobs "
 #define KEY_QUERY_EVENTS       "/queryEvents "
 #define KEY_PURGE_REQUEST      "/purgeRequest "
@@ -85,6 +85,47 @@ char *edg_wll_HTTPErrorMessage(int errCode)
        return msg;
 }
 
+
+/* returns non-zero if old style (V21) protocols incompatible */
+static int is_protocol_incompatibleV21(char *user_agent)
+{
+        char *version, *comp_proto, *needle;
+        double  v, c, my_v = strtod(PROTO_VERSION_V21, (char **) NULL), my_c;
+
+
+       /* get version od the other side */
+        if ((version = strstr(user_agent,"/")) == NULL) return(-1);
+        else v = strtod(++version, &needle);
+
+       /* sent the other side list of compatible protocols? */
+       if ( needle[0] == '\0' ) return(-2);
+
+       /* test compatibility if server newer*/
+        if (my_v > v) {
+                comp_proto=COMP_PROTO_V21;
+                do {
+                        my_c = strtod(comp_proto, &needle);
+                        if (my_c == v) return(0);
+                        comp_proto = needle + 1;
+                } while (needle[0] != '\0');
+                return(1);
+        }
+
+       /* test compatibility if server is older */
+        else if (my_v < v) {
+                do {
+                        comp_proto = needle + 1;
+                        c = strtod(comp_proto, &needle);
+                        if (my_v == c) return(0);
+                } while (needle[0] != '\0');
+                return(1);
+        }
+
+       /* version match */
+        return(0);
+}
+
+
 /* returns non-zero if protocols incompatible */
 static int is_protocol_incompatible(char *user_agent)
 {
@@ -144,11 +185,199 @@ static int outputHTML(char **headers)
 
 
 
+
+edg_wll_ErrorCode edg_wll_ProtoV21(edg_wll_Context ctx,
+       char *request,char **headers,char *messageBody,
+       char **response,char ***headersOut,char **bodyOut)
+{
+       char *requestPTR, *message = NULL;
+       int     ret = HTTP_OK;
+       int     html = outputHTML(headers);
+       int     i;
+
+       edg_wll_ResetError(ctx);
+
+       for (i=0; headers[i]; i++) /* find line with version number in headers */
+               if ( strstr(headers[i], KEY_AGENT) ) break;
+  
+       if (headers[i] == NULL) { ret = HTTP_BADREQ; goto errV21; } /* if not present */
+       switch (is_protocol_incompatibleV21(headers[i])) { 
+               case 0  : /* protocols compatible */
+                         ctx->is_V21 = 1;
+                         break;
+               case -1 : /* malformed 'User Agent:' line */
+                         ret = HTTP_BADREQ;
+                         goto errV21;
+                         break;
+               case -2 : /* version of one protocol unknown */
+                         /* fallthrough */
+               case 1  : /* protocols incompatible */
+                         /* fallthrough */
+               default : ret = HTTP_UNSUPPORTED; 
+                         edg_wll_SetError(ctx,ENOTSUP,"Protocol versions are incompatible.");
+                         goto errV21; 
+                         break;
+       }
+
+
+/* GET */
+       if (!strncmp(request, METHOD_GET, sizeof(METHOD_GET)-1)) {
+               // Not supported
+               ret = HTTP_BADREQ;
+/* POST */
+       } else if (!strncmp(request,METHOD_POST,sizeof(METHOD_POST)-1)) {
+
+               requestPTR = request + sizeof(METHOD_POST)-1;
+       
+               if (!strncmp(requestPTR,KEY_QUERY_EVENTS,sizeof(KEY_QUERY_EVENTS)-1)) { 
+                       edg_wll_Event *eventsOut = NULL;
+                       edg_wll_QueryRec **job_conditions = NULL, **event_conditions = NULL;
+                       int i,j;
+
+                       if (parseQueryEventsRequestV21(ctx, messageBody, &job_conditions, &event_conditions)) 
+                               ret = HTTP_BADREQ;
+                               
+                       else {
+                               int     fatal = 0;
+
+                               switch (edg_wll_QueryEventsServer(ctx,ctx->noAuth,
+                                   (const edg_wll_QueryRec **)job_conditions, 
+                                   (const edg_wll_QueryRec **)event_conditions, &eventsOut)) {
+
+                                       case 0: if (html) ret = HTTP_NOTIMPL;
+                                               else      ret = HTTP_OK; 
+                                               break;
+                                       case ENOENT: ret = HTTP_NOTFOUND; break;
+                                       case EPERM : ret = HTTP_UNAUTH; break;
+                                       case EDG_WLL_ERROR_NOINDEX: ret = HTTP_UNAUTH; break;
+                                       case ENOMEM: fatal = 1; ret = HTTP_INTERNAL; break;
+                                       default: ret = HTTP_INTERNAL; break;
+                               }
+                               /* glue errors (if eny) to XML responce */ 
+                               if (!html && !fatal)
+                                       if (edg_wll_QueryEventsToXMLV21(ctx, eventsOut, &message))
+                                               ret = HTTP_INTERNAL;
+                       }
+
+                       if (job_conditions) {
+                               for (j = 0; job_conditions[j]; j++) {
+                                       for (i = 0 ; (job_conditions[j][i].attr != EDG_WLL_QUERY_ATTR_UNDEF); i++ )
+                                               edg_wll_QueryRecFree(&job_conditions[j][i]);
+                                       free(job_conditions[j]);
+                               }
+                               free(job_conditions);
+                       }
+
+                       if (event_conditions) {
+                               for (j = 0; event_conditions[j]; j++) {
+                                       for (i = 0 ; (event_conditions[j][i].attr != EDG_WLL_QUERY_ATTR_UNDEF); i++ )
+                                               edg_wll_QueryRecFree(&event_conditions[j][i]);
+                                       free(event_conditions[j]);
+                               }
+                               free(event_conditions);
+                       }
+       
+                       if (eventsOut != NULL) {
+                               for (i=0; eventsOut[i].type != EDG_WLL_EVENT_UNDEF; i++) 
+                                       edg_wll_FreeEvent(&(eventsOut[i]));
+                               edg_wll_FreeEvent(&(eventsOut[i])); /* free last line */
+                               free(eventsOut);
+                       }
+               }
+               else if (!strncmp(requestPTR,KEY_QUERY_JOBS,sizeof(KEY_QUERY_JOBS)-1)) { 
+                       edg_wlc_JobId *jobsOut = NULL;
+                       edg_wll_JobStat *statesOut = NULL;
+                       edg_wll_QueryRec **conditions = NULL;
+                       int i,j, flags = 0;
+
+                       if (parseQueryJobsRequestV21(ctx, messageBody, &conditions, &flags))
+                               ret = HTTP_BADREQ;
+
+                       else { 
+                               int             fatal = 0,
+                                               retCode;
+                               if (flags & EDG_WLL_STAT_NO_JOBS) { 
+                                       flags -= EDG_WLL_STAT_NO_JOBS;
+                                       jobsOut = NULL;
+                                       if (flags & EDG_WLL_STAT_NO_STATES) {
+                                               flags -= EDG_WLL_STAT_NO_STATES;
+                                               statesOut = NULL;
+                                               retCode = edg_wll_QueryJobsServer(ctx, (const edg_wll_QueryRec **)conditions, flags, NULL, NULL);
+                                       }
+                                       else
+                                               retCode = edg_wll_QueryJobsServer(ctx, (const edg_wll_QueryRec **)conditions, flags, NULL, &statesOut);
+                               }
+                               else {
+                                       if (flags & EDG_WLL_STAT_NO_STATES) {
+                                               flags -= EDG_WLL_STAT_NO_STATES;
+                                               statesOut = NULL;
+                                               retCode = edg_wll_QueryJobsServer(ctx, (const edg_wll_QueryRec **)conditions, flags, &jobsOut, NULL);
+                                       }
+                                       else
+                                               retCode = edg_wll_QueryJobsServer(ctx, (const edg_wll_QueryRec **)conditions, flags, &jobsOut, &statesOut);
+                               }
+                               
+                               switch ( retCode ) {
+                                       // case EPERM : ret = HTTP_UNAUTH;
+                                       //              /* soft-error fall through */
+                                       case 0: if (html) ret =  HTTP_NOTIMPL;
+                                               else ret = HTTP_OK;
+
+                                               break;
+                                       case ENOENT: ret = HTTP_NOTFOUND; break;
+                                       case EPERM: ret = HTTP_UNAUTH; break;
+                                       case EDG_WLL_ERROR_NOINDEX: ret = HTTP_UNAUTH; break;
+                                       case ENOMEM: fatal = 1; ret = HTTP_INTERNAL; break;
+                                       default: ret = HTTP_INTERNAL; break;
+                               }
+                               if (!html && !fatal)
+                                       if (edg_wll_QueryJobsToXMLV21(ctx, jobsOut, statesOut, &message))
+                                               ret = HTTP_INTERNAL;
+                       }
+
+                       if (conditions) {
+                               for (j = 0; conditions[j]; j++) {
+                                       for (i = 0; (conditions[j][i].attr != EDG_WLL_QUERY_ATTR_UNDEF); i++ )
+                                               edg_wll_QueryRecFree(&conditions[j][i]);
+                                       free(conditions[j]);
+                               }
+                               free(conditions);
+                       }
+
+                       if (jobsOut) {
+                               for (i=0; jobsOut[i]; i++) edg_wlc_JobIdFree(jobsOut[i]);
+                               free(jobsOut);
+                       }
+                       if (statesOut) {
+                               for (i=0; statesOut[i].state != EDG_WLL_JOB_UNDEF; i++) 
+                                       edg_wll_FreeStatus(&(statesOut[i]));
+                               edg_wll_FreeStatus(&(statesOut[i])); /* free last line */
+                               free(statesOut);
+                       }
+               }
+        /* POST [something else]: not understood */
+               else ret = HTTP_BADREQ;
+
+/* other HTTP methods */
+       } else ret = HTTP_NOTALLOWED;
+
+errV21:        asprintf(response,"HTTP/1.1 %d %s",ret,edg_wll_HTTPErrorMessage(ret));
+       *headersOut = (char **) response_headers;
+       if ((ret != HTTP_OK) && html)
+               *bodyOut = edg_wll_ErrorToHTML(ctx,ret);
+       else
+               *bodyOut = message;
+
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+
+
 edg_wll_ErrorCode edg_wll_Proto(edg_wll_Context ctx,
        char *request,char **headers,char *messageBody,
        char **response,char ***headersOut,char **bodyOut)
 {
-       //char *requestPTR, *pom, *message = NULL; Ely 16/10 unused variable `pom'
        char *requestPTR, *message = NULL;
        int     ret = HTTP_OK;
        int     html = outputHTML(headers);
@@ -162,14 +391,21 @@ edg_wll_ErrorCode edg_wll_Proto(edg_wll_Context ctx,
        if (headers[i] == NULL) { ret = HTTP_BADREQ; goto err; } /* if not present */
        switch (is_protocol_incompatible(headers[i])) { 
                case 0  : /* protocols compatible */
+                         ctx->is_V21 = 0;
                          break;
                case -1 : /* malformed 'User Agent:' line */
                          ret = HTTP_BADREQ;
                          goto err;
                          break;
-               case -2 : /* version of one protocol unknown */
-                         /* fallthrough */
                case 1  : /* protocols incompatible */
+                         /* try old (V21) version compatibility */
+                         edg_wll_ProtoV21(ctx, request, headers, messageBody, 
+                                         response, headersOut, bodyOut);
+                                         
+                         /* and propagate errors or results */
+                         return edg_wll_Error(ctx,NULL,NULL);
+                         break;
+               case -2 : /* version of one protocol unknown */
                          /* fallthrough */
                default : ret = HTTP_UNSUPPORTED; 
                          edg_wll_SetError(ctx,ENOTSUP,"Protocol versions are incompatible.");
@@ -552,9 +788,6 @@ edg_wll_ErrorCode edg_wll_Proto(edg_wll_Context ctx,
        } else ret = HTTP_NOTALLOWED;
 
 err:   asprintf(response,"HTTP/1.1 %d %s",ret,edg_wll_HTTPErrorMessage(ret));
-       //(*headersOut) = malloc(2*sizeof(**headersOut));
-       //(*headersOut)[0] = strdup("Cache-Control: no-cache");
-       //(*headersOut)[1] = NULL;
        *headersOut = (char **) response_headers;
        if ((ret != HTTP_OK) && html)
                *bodyOut = edg_wll_ErrorToHTML(ctx,ret);
index 92fcefd..3a42b20 100644 (file)
@@ -1459,7 +1459,7 @@ int edg_wll_UserJobsToXML(edg_wll_Context ctx, edg_wlc_JobId *jobsOut, char **me
         return 0;
 }
 
-void edg_wll_add_stslist_to_XMLBody(edg_wll_Context ctx, char **body, const edg_wll_JobStat *toAdd, const char *tag, const char *UNUSED_subTag, const int null)
+static void edg_wll_add_stslist_to_XMLBody(edg_wll_Context ctx, char **body, const edg_wll_JobStat *toAdd, const char *tag, const char *UNUSED_subTag, const int null)
 {
         char *pomA, *pomB, *newBody;
         char **list = NULL;
index 51db4cf..c602823 100644 (file)
@@ -27,6 +27,5 @@ int edg_wll_DumpResultToXML(edg_wll_Context ctx, edg_wll_DumpResult *result, cha
 int edg_wll_LoadResultToXML(edg_wll_Context ctx, edg_wll_LoadResult *result, char **message);
 int edg_wll_IndexedAttrsToXML(edg_wll_Context ctx, char **message);
 int edg_wll_NotifResultToXML(edg_wll_Context ctx, time_t validity, char **message);
-void edg_wll_ErrorToXML(edg_wll_Context, char **);
 
 #endif
diff --git a/org.glite.lb.server/src/lb_xml_parse_V21.c.T b/org.glite.lb.server/src/lb_xml_parse_V21.c.T
new file mode 100644 (file)
index 0000000..caec4f8
--- /dev/null
@@ -0,0 +1,1239 @@
+#ident "$Header$"
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <expat.h>
+
+#include "glite/wms/jobid/cjobid.h"
+
+#include "glite/lb/consumer.h"
+#include "glite/lb/escape.h"
+#include "glite/lb/context-int.h"
+#include "glite/lb/trio.h"
+#include "glite/lb/xml_conversions.h"
+
+#include "glite/lb/purge.h"
+#include "glite/lb/dump.h"
+
+#include "lb_xml_parse_V21.h"
+
+#ifdef __GNUC__
+#define UNUSED_VAR __attribute__((unused))
+#else
+#define UNUSED_VAR
+#endif
+
+#define QUERY_EVENTS_BEGIN     "<edg_wll_QueryEventsResult"
+#define QUERY_EVENTS_END       "</edg_wll_QueryEventsResult>\r\n"
+#define QUERY_JOBS_BEGIN       "<edg_wll_QueryJobsResult"
+#define QUERY_JOBS_END         "</edg_wll_QueryJobsResult>\r\n"
+#define PURGE_RESULT_BEGIN     "<edg_wll_PurgeResult"
+#define PURGE_RESULT_END       "</edg_wll_PurgeResult>\r\n"
+#define DUMP_RESULT_BEGIN      "<edg_wll_DumpResult"
+#define DUMP_RESULT_END                "</edg_wll_DumpResult>\r\n"
+
+// XXX will be redundant soon
+#define USERJOBS_BEGIN         "<edg_wll_UserJobs"
+#define USERJOBS_END           "</edg_wll_UserJobs>\r\n"
+
+
+static char    *ops[] = { "equal","less","greater","within" },
+               *attrs[] = {    "jobid","owner","status","location","destination",
+                               "donecode","usertag","time","level","host","source",
+                               "instance","type","chkpt_tag", "resubmitted", "parent_job", "exitcode" };
+
+static const struct timeval null_timeval = {0,0};
+
+#define unexp() {\
+       char    *e;\
+\
+       if (XMLCtx->errtxt) {\
+               asprintf(&e,"%s\nunexpected <%s> at line %d",XMLCtx->errtxt,\
+                       el,XML_GetCurrentLineNumber(XMLCtx->p));\
+               free(XMLCtx->errtxt);\
+       } else asprintf(&e,"unexpected <%s> at line %d",\
+               el,XML_GetCurrentLineNumber(XMLCtx->p));\
+       XMLCtx->errtxt = e;\
+}
+
+
+/* Dummy function, returns only const string "el"; just for compatibility */
+static char *return_string_el(edg_wll_JobStatCode statCode) {
+       return("el");
+}
+
+
+static void startQueryJobsRequest(void *data, const char *el, const char **attr)
+{
+       unsigned int    i;
+       edg_wll_XML_ctx *XMLCtx = data;
+
+       
+       strcpy(XMLCtx->element, el);
+
+       switch (XMLCtx->level) {
+               case 0: if (strcasecmp(el,"edg_wll_QueryJobsRequest")) { unexp() break; }
+                       for ( i = 0; attr[i] && attr[i+1]; i += 2 )
+                       {
+                               if ( !strcmp(attr[i],"softLimit") )
+                                       XMLCtx->ctx->softLimit = atoi(attr[i+1]);
+                               else if ( !strcmp(attr[i],"queryRes") )
+                                       XMLCtx->ctx->p_query_results = atoi(attr[i+1]);
+                               else { unexp() break; }
+                       }
+                       break;
+               case 1: if (strcasecmp(el,"and") && strcasecmp(el,"flags")) unexp()
+                       break;
+               case 2: if (strcasecmp(el,"or")) unexp()
+                       else {
+                               XMLCtx->position = -1;
+                               XMLCtx->conditions = realloc(XMLCtx->conditions,
+                                       (++XMLCtx->row+2)*sizeof(*XMLCtx->conditions));
+                               XMLCtx->conditions[XMLCtx->row] = NULL;
+                               XMLCtx->conditions[XMLCtx->row+1] = NULL;
+                       }
+                       break;
+               case 3:
+                       for (i=0; i<sizeof(ops)/sizeof(ops[0]); i++)
+                               if (!strcasecmp(el,ops[i])) break;
+                       if (i == sizeof(ops)/sizeof(ops[0])) unexp()
+                       else {
+                               /* malloc also terminator and set it to 0 (= EDG_WLL_QUERY_ATTR_UNDEF) */
+                               XMLCtx->conditions[XMLCtx->row] = realloc(XMLCtx->conditions[XMLCtx->row],
+                                       (++XMLCtx->position+2)*sizeof(**XMLCtx->conditions));
+                               memset(&XMLCtx->conditions[XMLCtx->row][XMLCtx->position],0,2*sizeof(**XMLCtx->conditions));
+                               XMLCtx->conditions[XMLCtx->row][XMLCtx->position].op = i;
+                               XMLCtx->bound = 0;
+                       }
+                       break;
+               case 4: for (i=0; i<sizeof(attrs)/sizeof(attrs[0]) && 
+                               strcasecmp(el,attrs[i]); i++);
+                       if (i == sizeof(attrs)/sizeof(attrs[0])) unexp()
+                       else {
+                               if ( (i+1) == EDG_WLL_QUERY_ATTR_USERTAG) {
+                                       if (!attr[0] || !attr[1]) { unexp() break;}
+                                       if (strcmp(attr[0],"name")) { unexp() break;}
+                                       XMLCtx->conditions[XMLCtx->row][XMLCtx->position].attr_id.tag = strdup(attr[1]);
+                               } 
+                               else if ( (i+1) == EDG_WLL_QUERY_ATTR_TIME) {
+                                       if (!attr[0] || !attr[1]) { unexp() break;}
+                                        if (attr[0] && strcmp(attr[0],"state")) { unexp() break;}
+                                       XMLCtx->conditions[XMLCtx->row][XMLCtx->position].attr_id.state = edg_wll_StringToStat(attr[1]);
+                               }
+                               XMLCtx->conditions[XMLCtx->row][XMLCtx->position].attr = i+1;
+                       }               
+                       break;
+               default: unexp(); break;
+       }
+       XMLCtx->level++;
+}
+
+
+static void startQueryEventsRequest(void *data, const char *el, const char **attr UNUSED_VAR)
+{
+       unsigned int    i;
+       edg_wll_XML_ctx *XMLCtx = data;
+
+
+       strcpy(XMLCtx->element, el);
+
+       switch (XMLCtx->level) {
+               case 0: if (strcasecmp(el,"edg_wll_QueryEventsRequest")) { unexp() break; }
+                       for ( i = 0; attr[i] && attr[i+1]; i += 2 )
+                       {
+                               if ( !strcmp(attr[i],"softLimit") )
+                                       XMLCtx->ctx->softLimit = atoi(attr[i+1]);
+                               else if ( !strcmp(attr[i],"queryRes") )
+                                       XMLCtx->ctx->p_query_results = atoi(attr[i+1]);
+                               else { unexp() break; }
+                       }
+                       break;
+               case 1: if (strcasecmp(el,"and")) unexp()
+                       break;
+               case 2: if (!strcasecmp(el,"orJobConditions")) {
+                               XMLCtx->type = EDG_WLL_QUERY_TYPE_JOB_CONDITION;
+                               XMLCtx->position = -1;
+                                XMLCtx->job_conditions = realloc(XMLCtx->job_conditions,
+                                        (++XMLCtx->row+2)*sizeof(*XMLCtx->job_conditions));
+                                XMLCtx->job_conditions[XMLCtx->row] = NULL;
+                                XMLCtx->job_conditions[XMLCtx->row+1] = NULL;
+
+                       }
+                       else if (!strcasecmp(el,"orEventConditions")) {
+                               XMLCtx->type = EDG_WLL_QUERY_TYPE_EVENT_CONDITION;
+                               XMLCtx->position2 = -1;
+                                XMLCtx->event_conditions = realloc(XMLCtx->event_conditions,
+                                        (++XMLCtx->row2+2)*sizeof(*XMLCtx->event_conditions));
+                                XMLCtx->event_conditions[XMLCtx->row2] = NULL;
+                                XMLCtx->event_conditions[XMLCtx->row2+1] = NULL;
+                       }
+                       else unexp()
+                       break;
+               case 3:
+                       for (i=0; i<sizeof(ops)/sizeof(ops[0]); i++)
+                               if (!strcasecmp(el,ops[i])) break;
+                       if (i == sizeof(ops)/sizeof(ops[0])) unexp()
+                       else if (XMLCtx->type == EDG_WLL_QUERY_TYPE_JOB_CONDITION) {
+                               /* malloc also terminator and set it to 0 (= EDG_WLL_QUERY_ATTR_UNDEF) */
+                               XMLCtx->job_conditions[XMLCtx->row] = realloc(XMLCtx->job_conditions[XMLCtx->row],
+                                       (++XMLCtx->position+2)*sizeof(**XMLCtx->job_conditions));
+                               memset(&XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position],0,2*sizeof(**XMLCtx->job_conditions));
+                               XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].op = i;
+                               XMLCtx->bound = 0;
+                       }
+                       else if (XMLCtx->type == EDG_WLL_QUERY_TYPE_EVENT_CONDITION) {
+                               /* malloc also terminator and set it to 0 (= EDG_WLL_QUERY_ATTR_UNDEF) */
+                               XMLCtx->event_conditions[XMLCtx->row2] = realloc(XMLCtx->event_conditions[XMLCtx->row2],
+                                       (++XMLCtx->position2+2)*sizeof(**XMLCtx->event_conditions));
+                               memset(&XMLCtx->event_conditions[XMLCtx->row2][XMLCtx->position2],0,2*sizeof(**XMLCtx->event_conditions));
+                               XMLCtx->event_conditions[XMLCtx->row2][XMLCtx->position2].op = i;
+                               XMLCtx->bound = 0;
+                       }
+                       
+                       break;
+               case 4: for (i=0; i<sizeof(attrs)/sizeof(attrs[0]) && 
+                               strcasecmp(el,attrs[i]); i++);
+                       if (i == sizeof(attrs)/sizeof(attrs[0])) unexp()
+                       else if (XMLCtx->type == EDG_WLL_QUERY_TYPE_JOB_CONDITION) { 
+                               if ( (i+1) == EDG_WLL_QUERY_ATTR_USERTAG) {
+                                       if (!attr[0] || !attr[1]) { unexp() break;}
+                                       if (attr[0] && strcmp(attr[0],"name")) { unexp() break;}
+                                       XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].attr_id.tag = strdup(attr[1]);
+                               } 
+                               else if ( (i+1) == EDG_WLL_QUERY_ATTR_TIME) {
+                                       if (!attr[0] || !attr[1]) { unexp() break;}
+                                        if (attr[0] && strcmp(attr[0],"state")) { unexp() break;}
+                                       XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].attr_id.state = edg_wll_StringToStat(attr[1]);
+                                       printf("\nchecking time attr\n%s = %s (%d)\n\n", attr[0], attr[1], edg_wll_StringToStat(attr[1]));
+                               }
+                               XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].attr = i+1;
+                       }
+                       else if (XMLCtx->type == EDG_WLL_QUERY_TYPE_EVENT_CONDITION) {
+                               if ( (i+1) == EDG_WLL_QUERY_ATTR_USERTAG) {
+                                       if (!attr[0] || !attr[1]) { unexp() break;}
+                                       if (attr[0] && strcmp(attr[0],"name")) { unexp() break;}
+                                       XMLCtx->event_conditions[XMLCtx->row2][XMLCtx->position].attr_id.tag = strdup(attr[1]);
+                               } 
+                               else if ( (i+1) == EDG_WLL_QUERY_ATTR_TIME) {
+                                       if (!attr[0] || !attr[1]) { unexp() break;}
+                                        if (attr[0] && strcmp(attr[0],"state")) { unexp() break;}
+                                       XMLCtx->event_conditions[XMLCtx->row2][XMLCtx->position].attr_id.state = edg_wll_StringToStat(attr[1]);
+                               }
+                               XMLCtx->event_conditions[XMLCtx->row2][XMLCtx->position2].attr = i+1;
+                       }
+                       break;
+               default: unexp(); break;
+       }
+       XMLCtx->level++;
+}
+
+
+
+static void startPurgeRequest(void *data, const char *el, const char **attr)
+{
+       edg_wll_XML_ctx *XMLCtx = data;
+
+       
+       strcpy(XMLCtx->element, el);
+
+       switch (XMLCtx->level) {
+               case 0: if (strcasecmp(el,"edg_wll_PurgeRequest")) unexp()
+                       break;
+               case 1: if (strcasecmp(el,"jobs") && strcasecmp(el,"timeout")
+                               && strcasecmp(el,"flags")) unexp()
+                       else 
+                               XMLCtx->position = 0;
+                       break;
+               case 2: if (!strcasecmp(el,"jobId")) {
+                               XMLCtx->purgeRequestGlobal.jobs = realloc(XMLCtx->purgeRequestGlobal.jobs,
+                                               (XMLCtx->position+2) * sizeof(XMLCtx->purgeRequestGlobal.jobs));
+
+                                if (!XMLCtx->purgeRequestGlobal.jobs) { 
+                                       edg_wll_SetError(XMLCtx->ctx, ENOMEM, NULL); 
+                                       unexp() return;
+                               }
+                                XMLCtx->purgeRequestGlobal.jobs[XMLCtx->position+1] = NULL;
+                       }
+                       else if (!strcasecmp(el,"time")) {
+                               /* static array, no need to initialize & allocate anything */
+                       }
+                       else 
+                               unexp()
+                       break;
+               default: unexp() 
+                        break;
+       }
+       XMLCtx->level++;
+}
+
+
+static void startDumpRequest(void *data, const char *el, const char **attr)
+{
+       edg_wll_XML_ctx *XMLCtx = data;
+
+       
+       strcpy(XMLCtx->element, el);
+
+       switch (XMLCtx->level) {
+               case 0: if (strcasecmp(el,"edg_wll_DumpRequest")) unexp()
+                       break;
+               case 1: if (strcasecmp(el,"from") && strcasecmp(el,"to")) unexp()
+                       break;
+               default: unexp() 
+                        break;
+       }
+       XMLCtx->level++;
+}
+
+#undef unexp
+
+
+static void char_handler(void *data, const char *s, int len)
+{
+        edg_wll_XML_ctx *XMLCtx = data;
+        int i, found = -1, temp_len1;
+       char *temp_s, *temp_s1;
+
+
+       /* if date are only spaces, t\, \r, \n ... don't bother with them */
+        for (i=0; i<len; i++)  
+                       if (!isspace(s[i])) { found = i; break; }
+        if (found == -1) return;
+
+       temp_s = malloc(len+1);
+
+       /* otherwise use them */
+       memcpy(temp_s,s,len);
+       temp_s[len] = 0;        
+       temp_s1 = edg_wll_UnescapeXML((const char *) temp_s);
+       temp_len1 = strlen(temp_s1);
+
+        if (XMLCtx->char_buf_len) XMLCtx->char_buf =
+               realloc(XMLCtx->char_buf,XMLCtx->char_buf_len+temp_len1 + 1);
+        else XMLCtx->char_buf = malloc(temp_len1 + 1);
+
+        memcpy(XMLCtx->char_buf+XMLCtx->char_buf_len,temp_s1,temp_len1 + 1);
+        XMLCtx->char_buf_len += temp_len1;
+       free(temp_s1);
+       free(temp_s);
+}
+
+
+
+static void endQueryJobsRequest(void *data, const char *el UNUSED_VAR)
+{
+       edg_wll_XML_ctx *XMLCtx = data;
+       char *e;
+
+
+       if (XMLCtx->level == 2) {
+               if (!strcmp(XMLCtx->element,"flags") && XMLCtx->char_buf) {
+// XXX: check if it works
+                        XMLCtx->flags = edg_wll_string_to_stat_flags(XMLCtx->char_buf);
+               }
+       }
+       else if (XMLCtx->level == 5) {
+               switch (XMLCtx->conditions[XMLCtx->row][XMLCtx->position].attr) {
+                       case EDG_WLL_QUERY_ATTR_JOBID:
+                       case EDG_WLL_QUERY_ATTR_PARENT:
+                               if ( (XMLCtx->conditions[XMLCtx->row][XMLCtx->position].value.j = 
+                                       edg_wll_from_string_to_jobid(XMLCtx)) == NULL )
+                               {
+                                       if (XMLCtx->errtxt) {
+                                               asprintf(&e,"%s\n%s: invalid JobId at line %d",
+                                                       XMLCtx->errtxt, XMLCtx->char_buf,
+                                                       XML_GetCurrentLineNumber(XMLCtx->p));
+                                               free(XMLCtx->errtxt);
+                                       } else asprintf(&e,"%s: invalid JobId at line %d",
+                                               XMLCtx->char_buf,XML_GetCurrentLineNumber(XMLCtx->p));
+                                       XMLCtx->errtxt = e;
+                               }
+                               break;
+                       case EDG_WLL_QUERY_ATTR_OWNER:
+                       // XXX - this is way how to pass NULL, user will be extracted from ssl partner later
+                               if (XMLCtx->char_buf != NULL && !strcmp(XMLCtx->char_buf,"NULL")) {  
+                                       XMLCtx->conditions[XMLCtx->row][XMLCtx->position].value.c = NULL; 
+                                       break; 
+                               }
+                               else /* fall through */
+                       case EDG_WLL_QUERY_ATTR_LOCATION:
+                       case EDG_WLL_QUERY_ATTR_DESTINATION:
+                       case EDG_WLL_QUERY_ATTR_USERTAG:
+                               XMLCtx->conditions[XMLCtx->row][XMLCtx->position].value.c = 
+                                       edg_wll_from_string_to_string(XMLCtx);
+                               break;
+                       case EDG_WLL_QUERY_ATTR_STATUS:
+                       case EDG_WLL_QUERY_ATTR_DONECODE:
+                       case EDG_WLL_QUERY_ATTR_EXITCODE:
+                       case EDG_WLL_QUERY_ATTR_RESUBMITTED:
+                               if ( !XMLCtx->bound )
+                               {
+                                       XMLCtx->conditions[XMLCtx->row][XMLCtx->position].value.i = 
+                                               edg_wll_from_string_to_int(XMLCtx);
+                                       XMLCtx->bound++;
+                               }
+                               else
+                                       XMLCtx->conditions[XMLCtx->row][XMLCtx->position].value2.i = 
+                                               edg_wll_from_string_to_int(XMLCtx);
+
+                               break;
+                       case EDG_WLL_QUERY_ATTR_TIME:
+                               if ( !XMLCtx->bound )
+                               {
+                                       XMLCtx->conditions[XMLCtx->row][XMLCtx->position].value.t.tv_sec = 
+                                               edg_wll_from_string_to_time_t(XMLCtx);
+                                       XMLCtx->bound++;
+                               }
+                               else
+                                       XMLCtx->conditions[XMLCtx->row][XMLCtx->position].value2.t.tv_sec =
+                                               edg_wll_from_string_to_time_t(XMLCtx);
+                               break;
+                       default: 
+                               edg_wll_freeBuf(XMLCtx);
+                               XMLCtx->level--;
+                               if (XMLCtx->errtxt) {
+                                       asprintf(&e,"%s\n%s: invalid attribute type at line %d",
+                                               XMLCtx->errtxt, XMLCtx->char_buf,
+                                               XML_GetCurrentLineNumber(XMLCtx->p));
+                                       free(XMLCtx->errtxt);
+                               } else asprintf(&e,"%s: invalid attribute type at line %d",
+                                       XMLCtx->char_buf,XML_GetCurrentLineNumber(XMLCtx->p));
+                               XMLCtx->errtxt = e;
+                               break;
+               }
+       }
+        XMLCtx->char_buf = NULL;
+        XMLCtx->char_buf_len = 0;
+       XMLCtx->level--;
+}
+
+
+static void endQueryEventsRequest(void *data, const char *el UNUSED_VAR)
+{
+       edg_wll_XML_ctx *XMLCtx = data;
+       char *e;
+
+        if (XMLCtx->level == 2) {
+                if (!strcmp(XMLCtx->element,"orJobConditions")) {
+                        /* make end-of-row */
+                        XMLCtx->job_conditions[XMLCtx->row] = realloc(XMLCtx->job_conditions[XMLCtx->row],
+                                (++XMLCtx->position+1)*sizeof(**XMLCtx->job_conditions));
+                        memset(&XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position],0,sizeof(**XMLCtx->job_conditions));
+                }
+               else if (!strcmp(XMLCtx->element,"orEventConditions")) {
+                        /* make end-of-row */
+                        XMLCtx->event_conditions[XMLCtx->row2] = realloc(XMLCtx->event_conditions[XMLCtx->row2],
+                                (++XMLCtx->position2+1)*sizeof(**XMLCtx->event_conditions));
+                        memset(&XMLCtx->event_conditions[XMLCtx->row2][XMLCtx->position2],0,sizeof(**XMLCtx->event_conditions));
+                }
+       }
+       else if (XMLCtx->level == 5) {
+               if (XMLCtx->type == EDG_WLL_QUERY_TYPE_JOB_CONDITION) {
+                       switch (XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].attr) {
+                               case EDG_WLL_QUERY_ATTR_JOBID:
+                               case EDG_WLL_QUERY_ATTR_PARENT:
+                                       if ( (XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].value.j = 
+                                               edg_wll_from_string_to_jobid(XMLCtx)) == NULL )
+                                       {
+                                               if (XMLCtx->errtxt) {
+                                                       asprintf(&e,"%s\n%s: invalid JobId at line %d",
+                                                               XMLCtx->errtxt, XMLCtx->char_buf,
+                                                               XML_GetCurrentLineNumber(XMLCtx->p));
+                                                       free(XMLCtx->errtxt);
+                                               } else asprintf(&e,"%s: invalid JobId at line %d",
+                                                       XMLCtx->char_buf,XML_GetCurrentLineNumber(XMLCtx->p));
+                                               XMLCtx->errtxt = e;
+                                       }
+                                       break;
+                               case EDG_WLL_QUERY_ATTR_OWNER:
+                               case EDG_WLL_QUERY_ATTR_LOCATION:
+                               case EDG_WLL_QUERY_ATTR_DESTINATION:
+                               case EDG_WLL_QUERY_ATTR_USERTAG:
+                                       XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].value.c = 
+                                               edg_wll_from_string_to_string(XMLCtx);
+                                       break;
+                               case EDG_WLL_QUERY_ATTR_STATUS:
+                               case EDG_WLL_QUERY_ATTR_DONECODE:
+                               case EDG_WLL_QUERY_ATTR_EXITCODE:
+                               case EDG_WLL_QUERY_ATTR_RESUBMITTED:
+                                       if ( !XMLCtx->bound )
+                                       {
+                                               XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].value.i = 
+                                                       edg_wll_from_string_to_int(XMLCtx);
+                                               XMLCtx->bound++;
+                                       }
+                                       else
+                                               XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].value2.i = 
+                                                       edg_wll_from_string_to_int(XMLCtx);
+                                       break;
+                               case EDG_WLL_QUERY_ATTR_TIME:
+                                       if ( !XMLCtx->bound )
+                                       {
+                                               XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].value.t.tv_sec = 
+                                                       edg_wll_from_string_to_time_t(XMLCtx);
+                                               XMLCtx->bound++;
+                                       }
+                                       else
+                                               XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].value2.t.tv_sec =
+                                                       edg_wll_from_string_to_time_t(XMLCtx);
+                                       break;
+                               default: 
+                                       edg_wll_freeBuf(XMLCtx);
+                                       XMLCtx->level--;
+                                       if (XMLCtx->errtxt) {
+                                               asprintf(&e,"%s\n%s: invalid attribute type at line %d",
+                                                       XMLCtx->errtxt, XMLCtx->char_buf,
+                                                       XML_GetCurrentLineNumber(XMLCtx->p));
+                                               free(XMLCtx->errtxt);
+                                       } else asprintf(&e,"%s: invalid attribute type at line %d",
+                                               XMLCtx->char_buf,XML_GetCurrentLineNumber(XMLCtx->p));
+                                       XMLCtx->errtxt = e;
+                                       break;
+                       }
+               }
+               else if (XMLCtx->type == EDG_WLL_QUERY_TYPE_EVENT_CONDITION) {
+                       switch (XMLCtx->event_conditions[XMLCtx->row2][XMLCtx->position2].attr) {
+                               case EDG_WLL_QUERY_ATTR_TIME:
+                                       if ( !XMLCtx->bound )
+                                       {
+                                               XMLCtx->event_conditions[XMLCtx->row2][XMLCtx->position2].value.t.tv_sec = 
+                                                       edg_wll_from_string_to_time_t(XMLCtx);
+                                               XMLCtx->bound++;
+                                       }
+                                       else
+                                               XMLCtx->event_conditions[XMLCtx->row2][XMLCtx->position2].value2.t.tv_sec =
+                                                       edg_wll_from_string_to_time_t(XMLCtx);
+                                       break;
+                               case EDG_WLL_QUERY_ATTR_LEVEL:
+                               case EDG_WLL_QUERY_ATTR_SOURCE:
+                               case EDG_WLL_QUERY_ATTR_EVENT_TYPE:
+                                       if ( !XMLCtx->bound )
+                                       {
+                                               XMLCtx->event_conditions[XMLCtx->row2][XMLCtx->position2].value.i =
+                                                       edg_wll_from_string_to_int(XMLCtx);
+                                               XMLCtx->bound++;
+                                       }
+                                       else
+                                               XMLCtx->event_conditions[XMLCtx->row2][XMLCtx->position2].value2.i =
+                                                       edg_wll_from_string_to_int(XMLCtx);
+                                       break;
+                               case EDG_WLL_QUERY_ATTR_HOST:
+                               case EDG_WLL_QUERY_ATTR_INSTANCE:
+                               case EDG_WLL_QUERY_ATTR_USERTAG:
+                                       XMLCtx->event_conditions[XMLCtx->row2][XMLCtx->position2].value.c =
+                                               edg_wll_from_string_to_string(XMLCtx);
+                                       break;
+                               default: 
+                                       edg_wll_freeBuf(XMLCtx);
+                                       XMLCtx->level--;
+                                       if (XMLCtx->errtxt) {
+                                               asprintf(&e,"%s\n%s: invalid attribute type at line %d",
+                                                       XMLCtx->errtxt, XMLCtx->char_buf,
+                                                       XML_GetCurrentLineNumber(XMLCtx->p));
+                                               free(XMLCtx->errtxt);
+                                       } else asprintf(&e,"%s: invalid attribute type at line %d",
+                                               XMLCtx->char_buf,XML_GetCurrentLineNumber(XMLCtx->p));
+                                       XMLCtx->errtxt = e;
+                                       break;
+                       }
+               } 
+       
+               XMLCtx->char_buf = NULL;
+               XMLCtx->char_buf_len = 0;
+       }
+       XMLCtx->level--;
+}
+
+
+static void endPurgeRequest(void *data, const char *el UNUSED_VAR)
+{
+        edg_wll_XML_ctx *XMLCtx = data;
+        char *e;
+
+        if (XMLCtx->level == 2) {
+                if (!strcmp(XMLCtx->element,"flags")) 
+                       XMLCtx->purgeRequestGlobal.flags = edg_wll_from_string_to_int(XMLCtx);
+       }
+       else if (XMLCtx->level == 3) {
+               if (!strcmp(XMLCtx->element,"jobId")) {
+                       if ( (XMLCtx->purgeRequestGlobal.jobs[XMLCtx->position++] = 
+                               edg_wll_from_string_to_string(XMLCtx)) == NULL )
+                       {
+                               if (XMLCtx->errtxt) {
+                                       asprintf(&e,"%s\n%s: invalid JobId at line %d",
+                                               XMLCtx->errtxt, XMLCtx->char_buf,
+                                               XML_GetCurrentLineNumber(XMLCtx->p));
+                                       free(XMLCtx->errtxt);
+                               } else asprintf(&e,"%s: invalid JobId at line %d",
+                                       XMLCtx->char_buf,XML_GetCurrentLineNumber(XMLCtx->p));
+                               XMLCtx->errtxt = e;
+                       }
+               }
+               else if (!strcmp(XMLCtx->element,"time") && XMLCtx->char_buf) {
+                       XMLCtx->purgeRequestGlobal.timeout[XMLCtx->position++] = 
+                               edg_wll_from_string_to_time_t(XMLCtx);
+               }
+       }
+
+       XMLCtx->char_buf = NULL;
+        XMLCtx->char_buf_len = 0;
+       XMLCtx->level--;
+}
+
+
+static void endDumpRequest(void *data, const char *el UNUSED_VAR)
+{
+        edg_wll_XML_ctx *XMLCtx = data;
+
+        if (XMLCtx->level == 2) {
+                if (!strcmp(XMLCtx->element,"from")) 
+                       XMLCtx->dumpRequestGlobal.from = edg_wll_from_string_to_time_t(XMLCtx);
+               else if (!strcmp(XMLCtx->element,"to")) 
+                        XMLCtx->dumpRequestGlobal.to = edg_wll_from_string_to_time_t(XMLCtx);
+       }
+
+       XMLCtx->char_buf = NULL;
+        XMLCtx->char_buf_len = 0;
+       XMLCtx->level--;
+}
+
+/* parse queryJobs request from client */
+int parseQueryJobsRequestV21(edg_wll_Context ctx, char *messageBody, edg_wll_QueryRec ***conditions, int *flags)
+{
+       int     ret;
+       edg_wll_XML_ctx         XMLCtx;
+       XML_Char *encoding = "ISO-8859-1";
+
+       errno = 0;
+       edg_wll_initXMLCtx(&XMLCtx);
+       XMLCtx.row = -1;
+       XMLCtx.ctx = ctx;
+       edg_wll_ResetError(ctx);
+
+
+        /* initialize parser */
+        XMLCtx.p = XML_ParserCreate(encoding);
+        XML_SetElementHandler(XMLCtx.p, startQueryJobsRequest, endQueryJobsRequest);
+        XML_SetCharacterDataHandler(XMLCtx.p, char_handler);
+        XML_SetUserData(XMLCtx.p, (void *) &XMLCtx);
+
+
+        if (! XML_Parse(XMLCtx.p, messageBody, strlen(messageBody), 1)) {
+                char *errorMessage;
+
+                asprintf(&errorMessage, "Parse error at line %d:\n%s\n",
+                        XML_GetCurrentLineNumber(XMLCtx.p),
+                        XML_ErrorString(XML_GetErrorCode(XMLCtx.p)));
+
+                edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, errorMessage);
+                free(errorMessage);
+        } else if (XMLCtx.errtxt) edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, XMLCtx.errtxt);
+
+
+       if ((ret = edg_wll_Error(ctx,NULL,NULL))) {
+               int i,j;
+
+               if (XMLCtx.conditions) {
+                       for (j = 0; XMLCtx.conditions[j]; j++) {
+                               for (i = 0; (XMLCtx.conditions[j][i].attr != EDG_WLL_QUERY_ATTR_UNDEF); i++ )
+                                       edg_wll_QueryRecFree(&XMLCtx.conditions[j][i]);
+                               free(XMLCtx.conditions[j]);
+                       }
+                       free(XMLCtx.conditions);
+               }
+
+               /* empty list terminators */
+               conditions[0] = NULL;
+               *flags = 0;
+       } else {
+               *conditions = XMLCtx.conditions;
+               *flags = XMLCtx.flags;
+       }
+
+       
+        XML_ParserFree(XMLCtx.p);
+       edg_wll_freeXMLCtx(&XMLCtx);
+       return ret;
+}
+
+
+/* parse queryEvents request from client */
+int parseQueryEventsRequestV21(edg_wll_Context ctx, char *messageBody, edg_wll_QueryRec ***job_conditions, edg_wll_QueryRec ***event_conditions)
+{
+       int     ret;
+       edg_wll_XML_ctx         XMLCtx;
+       XML_Char *encoding = "ISO-8859-1";
+
+       errno = 0;
+       edg_wll_initXMLCtx(&XMLCtx);
+       XMLCtx.row = -1;
+       XMLCtx.row2 = -1;
+       XMLCtx.ctx = ctx;
+       edg_wll_ResetError(ctx);
+
+
+        /* initialize parser */
+        XMLCtx.p = XML_ParserCreate(encoding);
+        XML_SetElementHandler(XMLCtx.p, startQueryEventsRequest, endQueryEventsRequest);
+        XML_SetCharacterDataHandler(XMLCtx.p, char_handler);
+        XML_SetUserData(XMLCtx.p, (void *) &XMLCtx);
+
+
+        if (! XML_Parse(XMLCtx.p, messageBody, strlen(messageBody), 1)) {
+                char *errorMessage;
+
+                asprintf(&errorMessage, "Parse error at line %d:\n%s\n",
+                        XML_GetCurrentLineNumber(XMLCtx.p),
+                        XML_ErrorString(XML_GetErrorCode(XMLCtx.p)));
+
+                edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, errorMessage);
+                free(errorMessage);
+        } else if (XMLCtx.errtxt) edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, XMLCtx.errtxt);
+
+
+       if ((ret = edg_wll_Error(ctx,NULL,NULL))) {
+               int i,j;
+
+               if (XMLCtx.job_conditions) {
+                       for (j = 0; XMLCtx.job_conditions[j]; j++) {
+                               for (i = 0 ; (XMLCtx.job_conditions[j][i].attr != EDG_WLL_QUERY_ATTR_UNDEF); i++ )
+                                       edg_wll_QueryRecFree(&XMLCtx.job_conditions[j][i]);
+                               free(XMLCtx.job_conditions[j]);
+                       }
+                       free(XMLCtx.job_conditions);
+               }
+               if (XMLCtx.event_conditions) {
+                       for (j = 0; XMLCtx.event_conditions[j]; j++) {
+                               for (i = 0 ; (XMLCtx.event_conditions[j][i].attr != EDG_WLL_QUERY_ATTR_UNDEF); i++ )
+                                       edg_wll_QueryRecFree(&XMLCtx.event_conditions[j][i]);
+                               free(XMLCtx.event_conditions[j]);
+                       }
+                       free(XMLCtx.event_conditions);
+               }
+
+               /* empty list terminators */
+               job_conditions[0] = NULL;
+               event_conditions[0] = NULL;
+       } else {
+               *job_conditions = XMLCtx.job_conditions;
+               *event_conditions = XMLCtx.event_conditions;
+       }
+
+       
+        XML_ParserFree(XMLCtx.p);
+       edg_wll_freeXMLCtx(&XMLCtx);
+       return ret;
+}
+
+
+
+/* parse purge request from client */
+int parsePurgeRequestV21(edg_wll_Context ctx, char *messageBody, edg_wll_PurgeRequest *request)
+{
+       int     ret;
+       edg_wll_XML_ctx         XMLCtx;
+       XML_Char *encoding = "ISO-8859-1";
+
+       errno = 0;
+       edg_wll_initXMLCtx(&XMLCtx);
+       XMLCtx.ctx = ctx;
+       edg_wll_ResetError(ctx);
+
+
+        /* initialize parser */
+        XMLCtx.p = XML_ParserCreate(encoding);
+        XML_SetElementHandler(XMLCtx.p, startPurgeRequest, endPurgeRequest);
+        XML_SetCharacterDataHandler(XMLCtx.p, char_handler);
+        XML_SetUserData(XMLCtx.p, (void *) &XMLCtx);
+
+
+        if (! XML_Parse(XMLCtx.p, messageBody, strlen(messageBody), 1)) {
+                char *errorMessage;
+
+                asprintf(&errorMessage, "Parse error at line %d:\n%s\n",
+                        XML_GetCurrentLineNumber(XMLCtx.p),
+                        XML_ErrorString(XML_GetErrorCode(XMLCtx.p)));
+
+                edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, errorMessage);
+                free(errorMessage);
+        } else if (XMLCtx.errtxt) edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, XMLCtx.errtxt);
+
+
+       if ((ret = edg_wll_Error(ctx,NULL,NULL))) {
+               int i;
+
+               if (XMLCtx.purgeRequestGlobal.jobs) {
+                       for (i=0; XMLCtx.purgeRequestGlobal.jobs[i]; i++) 
+                               free(XMLCtx.purgeRequestGlobal.jobs[i]);
+                       free(XMLCtx.purgeRequestGlobal.jobs);
+               }
+               memset(request,0,sizeof(*request));
+               
+       } else {
+               memcpy(request, &XMLCtx.purgeRequestGlobal, sizeof(XMLCtx.purgeRequestGlobal));
+       }
+
+       
+        XML_ParserFree(XMLCtx.p);
+       edg_wll_freeXMLCtx(&XMLCtx);
+       return ret;
+}
+
+
+
+/* parse dump request from client */
+int parseDumpRequestV21(edg_wll_Context ctx, char *messageBody, edg_wll_DumpRequest *request)
+{
+       int     ret;
+       edg_wll_XML_ctx         XMLCtx;
+       XML_Char *encoding = "ISO-8859-1";
+
+       errno = 0;
+       edg_wll_initXMLCtx(&XMLCtx);
+       XMLCtx.ctx = ctx;
+       edg_wll_ResetError(ctx);
+
+
+        /* initialize parser */
+        XMLCtx.p = XML_ParserCreate(encoding);
+        XML_SetElementHandler(XMLCtx.p, startDumpRequest, endDumpRequest);
+        XML_SetCharacterDataHandler(XMLCtx.p, char_handler);
+        XML_SetUserData(XMLCtx.p, (void *) &XMLCtx);
+
+
+        if (! XML_Parse(XMLCtx.p, messageBody, strlen(messageBody), 1)) {
+                char *errorMessage;
+
+                asprintf(&errorMessage, "Parse error at line %d:\n%s\n",
+                        XML_GetCurrentLineNumber(XMLCtx.p),
+                        XML_ErrorString(XML_GetErrorCode(XMLCtx.p)));
+
+                edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, errorMessage);
+                free(errorMessage);
+        } else if (XMLCtx.errtxt) edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, XMLCtx.errtxt);
+
+
+       if ((ret = edg_wll_Error(ctx,NULL,NULL))) 
+               memset(request,0,sizeof(*request));
+               
+       else {
+               memcpy(request, &XMLCtx.dumpRequestGlobal, sizeof(XMLCtx.dumpRequestGlobal));
+       }
+
+       
+        XML_ParserFree(XMLCtx.p);
+       edg_wll_freeXMLCtx(&XMLCtx);
+       return ret;
+}
+
+
+
+int edg_wll_QueryEventsToXMLV21(edg_wll_Context ctx UNUSED_VAR, edg_wll_Event *eventsOut, char **message)
+{
+        char *pomA, *pomB;
+       char **list = NULL;
+        int i = 0, len, tot_len = 0;
+       int *len_list = NULL;
+
+
+        while (eventsOut && eventsOut[i].any.type != EDG_WLL_EVENT_UNDEF) {
+               pomB = edg_wll_EventToString(eventsOut[i].any.type);
+                trio_asprintf(&pomA,"      <edg_wll_Event name=\"%|Xs\">\r\n", pomB);
+               free(pomB);
+               pomB = pomA;
+
+
+@@@{
+        selectType $event '_common_';
+        for (getFieldsOrdered $event) {
+                my $f = selectField $event $_;
+                my $ft = $f->{type};
+               my $n = $f->{null};
+                gen "\tedg_wll_add_$ft\_to_XMLBody(&pomB, eventsOut[i].any.$_, \"$_\", $n);\n";
+        }
+
+        gen "\tswitch (eventsOut[i].any.type) {\n";
+        for my $t (sort { $event->{order}->{$a} <=> $event->{order}->{$b} } getTypes $event) {
+                if ($t ne 'any') {
+                        selectType $event $t;
+                        my $u = uc $t;
+                        gen "\t    case EDG_WLL_EVENT_$u :\n";
+                        for (getFieldsOrdered $event) {
+                                my $f = selectField $event $_;
+                                my $ft = $f->{type};
+                               my $n = $f->{null};
+                               $t = lcfirst $t;
+                                gen "\t\tedg_wll_add_$ft\_to_XMLBody(&pomB, eventsOut[i].$t.$_, \"$_\", $n);\n";
+                        }
+                       gen "\t\tbreak;\n";
+               }
+        }
+        gen "\t    default : break;\n";
+        gen "\t}\n";
+@@@}
+
+
+
+                len = asprintf(&pomA,"%s      </edg_wll_Event>\r\n", pomB);
+                free(pomB);
+                i++;
+               tot_len += len;
+
+               list      = (char **) realloc(list, i * sizeof(*list));
+               list[i-1] = pomA;
+               len_list      = (int *) realloc(len_list, i * sizeof(*len_list));
+               len_list[i-1] = len;
+               
+        }
+
+       /* list termination */
+       list = (char **) realloc(list, (i+1) * sizeof(*list));
+        list[i] = NULL;
+
+       /* test errors */
+       if (ctx->errDesc || ctx->errCode)
+               len = trio_asprintf(&pomB," code=\"%d\" desc=\"%|Xs\">\r\n",ctx->errCode,ctx->errDesc);
+       else
+               len = asprintf(&pomB,">\r\n");
+
+       /* glueing all list fields together & freeing the list */
+       pomA = (char *) malloc(tot_len  * sizeof(char) + 
+                               sizeof(QUERY_EVENTS_BEGIN) + len + sizeof(QUERY_EVENTS_END) - 1);
+       
+       memcpy(pomA, QUERY_EVENTS_BEGIN, sizeof(QUERY_EVENTS_BEGIN));
+       memcpy(pomA + sizeof(QUERY_EVENTS_BEGIN) - 1, pomB, len);
+       free(pomB);
+       pomB = pomA + sizeof(QUERY_EVENTS_BEGIN) + len - 1;     
+
+       i = 0;
+       while (list[i]) {
+                memcpy(pomB, list[i], len_list[i] );
+               pomB += len_list[i];
+
+               /* freeing the list */
+               free(list[i]);
+
+                i++;
+        }
+
+       strcpy(pomB, QUERY_EVENTS_END);
+
+
+       free(list);
+       free(len_list);
+
+
+        *message = pomA;
+
+        return 0;
+}
+
+int edg_wll_QueryJobsToXMLV21(edg_wll_Context ctx, edg_wlc_JobId *jobsIn, edg_wll_JobStat *statesIn, char **message) 
+{
+        char *pomA, *pomB, *pomC;
+        char **list = NULL;
+        int i = 0, len, tot_len = 0, nres = 0;
+        int *len_list = NULL;
+
+       
+       if (jobsIn) for (nres = 0; jobsIn[nres]; nres++);
+       else if (statesIn) for (nres = 0; statesIn[nres].state; nres++);
+
+        while (i < nres) {
+                trio_asprintf(&pomA,"\t<edg_wll_Job>\r\n");
+                pomB = pomA;
+               
+               if (jobsIn) {
+                       trio_asprintf(&pomA,"%s\t\t<jobId>%|Xs</jobId>\r\n",
+                               pomB, pomC=edg_wlc_JobIdUnparse(jobsIn[i]));
+                       free(pomC);
+                       free(pomB);
+                       pomB = pomA;
+               }
+
+               if (statesIn) {
+                       edg_wll_JobStatusToXMLV21(ctx, statesIn[i], &pomC);     
+                       trio_asprintf(&pomA,"%s\t\t%s",pomB,pomC);
+               }
+               else {
+                       pomC = edg_wll_StatToString(EDG_WLL_JOB_UNKNOWN);
+                       trio_asprintf(&pomA,"%s\t\t<jobStat name=\"%|Xs\">\r\n\r\n\t\t</jobStat>\r\n",
+                               pomB, pomC);
+               }
+
+               free(pomB);
+               free(pomC);
+               pomB = pomA;
+       
+
+                len = asprintf(&pomA,"%s\t</edg_wll_Job>\r\n", pomB);
+                free(pomB);
+                i++;
+                tot_len += len;
+
+                list      = (char **) realloc(list, i * sizeof(*list));
+                list[i-1] = pomA;
+                len_list      = (int *) realloc(len_list, i * sizeof(*len_list));
+                len_list[i-1] = len;   
+       }
+       
+       /* list termination */
+       list = (char **) realloc(list, (i+1) * sizeof(*list));
+        list[i] = NULL;
+
+        /* test errors */
+        if (ctx->errDesc || ctx->errCode)
+                len = trio_asprintf(&pomB," code=\"%d\" desc=\"%|Xs\">\r\n",ctx->errCode,ctx->errDesc);
+        else
+                len = asprintf(&pomB,">\r\n");
+
+       /* glueing all list fields together & freeing the list */
+       pomA = (char *) malloc(tot_len  * sizeof(char) + 
+                               sizeof(QUERY_JOBS_BEGIN) + len + sizeof(QUERY_JOBS_END) - 1);
+       
+       memcpy(pomA, QUERY_JOBS_BEGIN, sizeof(QUERY_JOBS_BEGIN));       
+        memcpy((pomA + sizeof(QUERY_JOBS_BEGIN) - 1), pomB, len);
+        free(pomB);
+       pomB = pomA + sizeof(QUERY_JOBS_BEGIN) + len - 1;       
+
+       i = 0;
+       while (list[i]) {
+                memcpy(pomB, list[i], len_list[i] );
+               pomB += len_list[i];
+
+               /* freeing the list */
+               free(list[i]);
+
+                i++;
+        }
+
+       strcpy(pomB, QUERY_JOBS_END);
+
+
+       free(list);
+       free(len_list);
+
+
+        *message = pomA;
+
+       return 0;
+}
+
+/* construct Message-Body of Response-Line for edg_wll_UserJobs */
+int edg_wll_UserJobsToXMLV21(edg_wll_Context ctx UNUSED_VAR, edg_wlc_JobId *jobsOut, char **message)
+{
+        char *pomA, *pomB;
+        char **list = NULL;
+        int i = 0, len, tot_len = 0;
+        int *len_list = NULL;
+
+
+        while (jobsOut[i]) {
+               len = trio_asprintf(&pomA,"      <jobId>%|Xs</jobId>\r\n",
+                       pomB=edg_wlc_JobIdUnparse(jobsOut[i]));
+
+                free(pomB);
+
+                i++;
+                tot_len += len;
+
+                list      = (char **) realloc(list, i * sizeof(*list));
+                list[i-1] = pomA;
+               pomA = NULL;
+                len_list      = (int *) realloc(len_list, i * sizeof(*len_list));
+                len_list[i-1] = len;
+
+        }
+
+        /* list termination */
+        list = (char **) realloc(list, (i+1) * sizeof(*list));
+        list[i] = NULL;
+
+        /* test errors */
+        if (ctx->errDesc || ctx->errCode)
+                len = trio_asprintf(&pomB," code=\"%d\" desc=\"%|Xs\">\r\n",ctx->errCode,ctx->errDesc);
+        else
+                len = asprintf(&pomB,">\r\n");
+
+        /* glueing all list fields together & freeing the list */
+        pomA = (char *) malloc(tot_len  * sizeof(char) +
+                                sizeof(USERJOBS_BEGIN) + len + sizeof(USERJOBS_END) - 1);
+
+        memcpy(pomA, USERJOBS_BEGIN, sizeof(USERJOBS_BEGIN));
+        memcpy((pomA + sizeof(USERJOBS_BEGIN) - 1), pomB, len);
+        free(pomB);
+        pomB = pomA + sizeof(USERJOBS_BEGIN) + len - 1;
+
+        i = 0;
+        while (list[i]) {
+                memcpy(pomB, list[i], len_list[i] );
+                pomB += len_list[i];
+
+                /* freeing the list */
+                free(list[i]);
+
+                i++;
+        }
+
+        strcpy(pomB, USERJOBS_END);
+
+
+        free(list);
+        free(len_list);
+
+
+        *message = pomA;
+
+        return 0;
+}
+
+static void edg_wll_add_stslist_to_XMLBodyV21(edg_wll_Context ctx, char **body, const edg_wll_JobStat *toAdd, const char *tag, const char *UNUSED_subTag, const int null)
+{
+        char *pomA, *pomB, *newBody;
+        char **list = NULL;
+        int i = 0, len, tot_len = 0;
+        int *len_list = NULL;
+
+
+        while (toAdd[i].state != null) {
+                edg_wll_JobStatusToXMLV21(ctx, toAdd[i], &pomA);
+                len = strlen(pomA);
+
+                i++;
+                tot_len += len;
+
+                list      = (char **) realloc(list, i * sizeof(*list));
+                list[i-1] = pomA;
+                pomA = NULL;
+                len_list      = (int *) realloc(len_list, i * sizeof(*len_list));
+                len_list[i-1] = len;
+        }
+
+        /* list termination */
+        list = (char **) realloc(list, (i+1) * sizeof(*list));
+        list[i] = NULL;
+
+        /* glueing all list fields together & freeing the list */
+        pomA = (char *) malloc(tot_len  * sizeof(char) + 1);
+       pomB = pomA;
+
+        i = 0;
+        while (list[i]) {
+                memcpy(pomB, list[i], len_list[i] );
+                pomB += len_list[i];
+
+                /* freeing the list */
+                free(list[i]);
+
+                i++;
+        }
+       *pomB = '\0';
+        free(list);
+        free(len_list);
+
+        asprintf(&newBody,"%s\t\t\t<%s>\r\n%s\t\t\t</%s>\r\n", *body, tag, pomA, tag);
+        free(*body);
+        free(pomA);
+        *body = newBody;
+}
+
+
+/* construct Message-Body of Response-Line for edg_wll_JobStatus */
+int edg_wll_JobStatusToXMLV21(edg_wll_Context ctx, edg_wll_JobStat stat, char **message)
+{
+        char *pomA, *pomB, *pomC;
+
+       
+       pomB = strdup("");
+
+@@@{
+       selectType $status '_common_';
+        for (getFieldsOrdered $status) {
+                my $f = selectField $status $_;
+               next if defined($f->{special}) && $f->{special} eq 'XMLstructured';
+                my $ft = $f->{type};
+               my $n = $f->{null};
+                gen "edg_wll_add_$ft\_to_XMLBody(&pomB, stat.$_, \"$_\", $n);\n";
+        }
+@@@}
+       if (stat.children) edg_wll_add_strlist_to_XMLBody(&pomB, stat.children, "children", "jobId", "\t\t\t", NULL);
+       if (stat.children_hist) edg_wll_add_intlist_to_XMLBody(&pomB, stat.children_hist, "children_hist", return_string_el, "\t\t\t", 1, stat.children_hist[0]);
+       if (stat.children_states) edg_wll_add_stslist_to_XMLBodyV21(ctx, &pomB, stat.children_states, "children_states", "", EDG_WLL_JOB_UNDEF);
+       if (stat.user_tags) edg_wll_add_taglist_to_XMLBody(&pomB, stat.user_tags, "user_tags", "tag", "name", "\t\t\t", NULL);
+       if (stat.stateEnterTimes) edg_wll_add_intlist_to_XMLBody(&pomB, stat.stateEnterTimes, "stateEnterTimes", return_string_el, "\t\t\t",1, stat.stateEnterTimes[0]);
+
+       pomC = edg_wll_StatToString(stat.state);
+
+       if (ctx->errDesc || ctx->errCode)
+               trio_asprintf(&pomA,"<jobStat name=\"%|Xs\" code=\"%d\" desc=\"%|Xs\">\r\n%s</jobStat>",
+                       pomC, ctx->errCode,ctx->errDesc, pomB);
+       else
+               trio_asprintf(&pomA,"<jobStat name=\"%|Xs\">\r\n%s</jobStat>",
+                       pomC, pomB);
+
+        free(pomB);
+       free(pomC);
+
+        *message = pomA;
+
+        return 0;
+}
+
+
+
+/* construct Message-Body of Request-Line for edg_wll_Purge */
+int edg_wll_PurgeResultToXMLV21(
+                edg_wll_Context ctx,
+               edg_wll_PurgeResult *result,
+                char **message)
+{
+        char *pomA, *pomB;
+
+
+       if (!result) { *message = NULL; return(-1); }
+
+       pomA = strdup("");
+       edg_wll_add_strlist_to_XMLBody(&pomA, result->jobs, "jobs", "jobId", "\t", NULL);
+       edg_wll_add_string_to_XMLBody(&pomA, result->server_file, "server_file", NULL); 
+
+       if (ctx->errDesc || ctx->errCode)
+                trio_asprintf(&pomA,"%s code=\"%d\" desc=\"%|Xs\">\r\n%s%s",
+                       PURGE_RESULT_BEGIN, ctx->errCode, ctx->errDesc, pomA, PURGE_RESULT_END);
+       else
+               trio_asprintf(&pomB,"%s>\r\n%s%s", PURGE_RESULT_BEGIN, pomA, PURGE_RESULT_END);
+       free(pomA);
+
+        *message = pomB;
+        return 0;
+}
+
+
+
+/* construct Message-Body of Request-Line for edg_wll_Dump */
+int edg_wll_DumpResultToXMLV21(
+                edg_wll_Context ctx,
+               edg_wll_DumpResult *result,
+                char **message)
+{
+        char *pomA, *pomB;
+
+
+       if (!result) { *message = NULL; return(-1); }
+
+       pomA = strdup("");
+       edg_wll_add_string_to_XMLBody(&pomA, result->server_file, "server_file", NULL);
+       edg_wll_add_time_t_to_XMLBody(&pomA, result->from, "from", 0);
+       edg_wll_add_time_t_to_XMLBody(&pomA, result->to, "to", 0);
+
+       if (ctx->errDesc || ctx->errCode)
+               trio_asprintf(&pomB,"%s code=\"%d\" desc=\"%|Xs\">\r\n%s%s",
+                       DUMP_RESULT_BEGIN, ctx->errCode, ctx->errDesc, pomA, DUMP_RESULT_END);
+       else
+               trio_asprintf(&pomB,"%s>\r\n%s%s", DUMP_RESULT_BEGIN, pomA, DUMP_RESULT_END);
+       free(pomA);
+
+        *message = pomB;
+        return 0;
+}
diff --git a/org.glite.lb.server/src/lb_xml_parse_V21.h b/org.glite.lb.server/src/lb_xml_parse_V21.h
new file mode 100644 (file)
index 0000000..ea731dc
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef _LB_XML_PARSE_H_V21
+#define _LB_XML_PARSE_H_V21
+
+#ident "$Header$"
+
+#include "glite/lb/consumer.h"
+
+/* function for parsing/unparsing XML requests from client */
+
+int parseQueryJobsRequestV21(edg_wll_Context ctx, char *messageBody, edg_wll_QueryRec ***conditions, int *flags);
+int parseQueryEventsRequestV21(edg_wll_Context ctx, char *messageBody, edg_wll_QueryRec ***job_conditions, edg_wll_QueryRec ***event_conditions);
+int parsePurgeRequestV21(edg_wll_Context ctx, char *messageBody, edg_wll_PurgeRequest *request);
+int parseDumpRequestV21(edg_wll_Context ctx, char *messageBody, edg_wll_DumpRequest *request);
+int edg_wll_QueryEventsToXMLV21(edg_wll_Context, edg_wll_Event *, char **);
+int edg_wll_QueryJobsToXMLV21(edg_wll_Context, edg_wlc_JobId *, edg_wll_JobStat *, char **);
+int edg_wll_JobStatusToXMLV21(edg_wll_Context, edg_wll_JobStat, char **);
+int edg_wll_UserJobsToXMLV21(edg_wll_Context, edg_wlc_JobId *, char **);
+int edg_wll_PurgeResultToXMLV21(edg_wll_Context ctx, edg_wll_PurgeResult *result, char **message);
+int edg_wll_DumpResultToXMLV21(edg_wll_Context ctx, edg_wll_DumpResult *result, char **message);
+
+#endif