(file) Return to wsmanparser.c CVS log (file) (dir) Up to [OMI] / omi / protocol

   1 mike  1.1 /*
   2           **==============================================================================
   3           **
   4           ** Open Management Infrastructure (OMI)
   5           **
   6           ** Copyright (c) Microsoft Corporation
   7           ** 
   8           ** Licensed under the Apache License, Version 2.0 (the "License"); you may not 
   9           ** use this file except in compliance with the License. You may obtain a copy 
  10           ** of the License at 
  11           **
  12           **     http://www.apache.org/licenses/LICENSE-2.0 
  13           **
  14           ** THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15           ** KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 
  16           ** WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
  17           ** MERCHANTABLITY OR NON-INFRINGEMENT. 
  18           **
  19           ** See the Apache 2 License for the specific language governing permissions 
  20           ** and limitations under the License.
  21           **
  22 mike  1.1 **==============================================================================
  23           */
  24           
  25           #include <assert.h>
  26 mike  1.2 #include <ctype.h>
  27 mike  1.1 #include "wsmanparser.h"
  28 mike  1.2 #include "wsbuf.h"
  29 mike  1.1 //#include "http.h"
  30           //#include "time.h"
  31           //#include <xml/xml.h>
  32           //#include <base/buf.h>
  33           #include <base/log.h>
  34           #include <base/result.h>
  35           #include <base/strings.h>
  36           
  37           #include "wstags.h"
  38           #include "wstags.inc"
  39           #include "uri.h"
  40           
  41           #define T MI_T
  42           
  43           #if 0
  44           #define ENABLE_TRACING
  45           #endif
  46           
  47           #ifdef  ENABLE_TRACING
  48           #define PRINTF(a)  printf a
  49           #else
  50 mike  1.1 #define PRINTF(a)
  51           #endif
  52           
  53           /*
  54           **==============================================================================
  55           **
  56           ** Local definitions:
  57           **
  58           **==============================================================================
  59           */
  60           static int _GetInstance(
  61               XML* xml, 
  62               XML_Elem *start,
  63               Batch*  dynamicBatch,
  64               MI_Instance** dynamicInstanceParams);
  65           
  66           /****************************************************************************************/
  67           /* soap processing */
  68           
  69           static int _MustUnderstandCanBeIgnored(
  70               const XML_Elem* e)
  71 mike  1.1 {
  72               size_t i;
  73               for (i = 0; i < e->attrsSize; i++)
  74               {
  75                   if (strcmp(e->attrs[i].name, "s:mustUnderstand") == 0 &&
  76                       strcmp(e->attrs[i].value, "true") == 0)
  77                   {
  78                       return -1;
  79                   }
  80               }
  81               return 0;
  82           }
  83           
  84 mike  1.2 #if 0
  85           static MI_Boolean IsValidClassname(const char* classname)
  86           {
  87               const char* p = classname;
  88           
  89               if (!isalpha((unsigned char)(*p)) && *p != '_')
  90                   return MI_FALSE;
  91           
  92               p++;
  93           
  94               for (; *p; p++)
  95               {
  96                   if (!isalnum((unsigned char)(*p)) && *p != '_')
  97                       return MI_FALSE;
  98               }
  99           
 100               return MI_TRUE;
 101           }
 102           #endif
 103           
 104 mike  1.1 static int _GetSelector(
 105               XML* xml,
 106               XML_Elem *e,
 107               const char** ns,
 108               const char* classname,
 109               MI_Instance** instance,
 110               Batch** batch)
 111           {
 112               size_t i;
 113           
 114               /* expecting namespace or key */
 115               for (i = 0; i < e->attrsSize; i++)
 116               {
 117 mike  1.2 #if 0
 118                   /* To support GetClass */
 119                   if (strcmp(e->attrs[i].name, "Name") == 0 &&
 120                       strcmp(e->attrs[i].value, "ClassName") == 0)
 121                   {
 122                       if (XML_Expect(xml, e, XML_CHARS, NULL) != 0)
 123                           return -1;
 124           
 125                       PRINTF(("a:Selector:ClassName{%s}\n", e->data));
 126           
 127                       if (!IsValidClassname(e->data))
 128                           return -1;
 129           
 130                       classname = e->data;
 131                   }
 132                   else 
 133           #endif
 134 mike  1.1         if (strcmp(e->attrs[i].name, "Name") == 0 &&
 135                       strcmp(e->attrs[i].value, "__cimnamespace") == 0)
 136                   {
 137                       /* namespace */
 138                       if (XML_Expect(xml, e, XML_CHARS, NULL) != 0)
 139                           return -1;
 140           
 141                       PRINTF(("a:Selector:namespace{%s}\n", e->data));
 142                       *ns = e->data;
 143                   }
 144                   else if (strcmp(e->attrs[i].name, "Name") == 0)
 145                   {
 146                       /* keys */
 147                       const char* propName = e->attrs[i].value;
 148           
 149                       if (XML_Expect(xml, e, XML_CHARS, NULL) != 0)
 150                           return -1;
 151           
 152                       PRINTF(("a:Selector:namespace{%s}\n", e->data));
 153           
 154                       /* allocate batch and instance if needed */
 155 mike  1.1             if (!(*instance))
 156                       {
 157                           MI_Result r;
 158                           MI_Char* cn;
 159           
 160                           /* verify that we already received classname */
 161                           if (!classname)
 162                           {
 163 mike  1.2                     XML_Raise(xml, "class-name was not found during selector parsing");
 164 mike  1.1                     return -1;
 165                           }
 166           
 167                           /* Destroy old batch if it exists (from previous operation) */
 168                           if (*batch)
 169                           {
 170                               Batch_Destroy(*batch);
 171                           }
 172           
 173                           *batch = Batch_New(BATCH_MAX_PAGES);
 174           
 175                           if (!(*batch))
 176                               return -1;
 177           
 178                           cn = Batch_Strdup2(*batch, classname);
 179           
 180                           r = Instance_NewDynamic(instance, cn, MI_FLAG_CLASS, *batch);
 181                           if (MI_RESULT_OK != r)
 182                               return -1;
 183                       }
 184           
 185 mike  1.1             /* add next property to the instance */
 186                       {
 187                           const MI_Char* wpropName = Batch_Strdup2(*batch, propName);
 188                           MI_Result r;
 189                           MI_Value value;
 190           
 191                           value.string = Batch_Strdup2(*batch, e->data);
 192           
 193                           // Set the property.
 194                           r = MI_Instance_AddElement(*instance, wpropName, &value, 
 195                               MI_STRING, MI_FLAG_BORROW | MI_FLAG_KEY);
 196           
 197                           if (MI_RESULT_OK != r)
 198                               return -1;
 199                       }
 200                   }
 201                   else
 202                       continue;
 203           
 204                   if (XML_Expect(xml, e, XML_END, "w:Selector") != 0)
 205                       return -1;
 206 mike  1.1 
 207                   break;
 208               }
 209           
 210               return 0;
 211           }
 212           
 213           static int _GetSelectorSet(
 214               XML* xml,
 215               WSMAN_WSHeader* wsheader)
 216           {
 217               XML_Elem e;
 218           
 219               if (XML_Expect(xml, &e, XML_START, "w:Selector") != 0)
 220                   return -1;
 221           
 222               /* iterate through all selector tags */
 223               for (;;)
 224               {
 225                   if (0 != _GetSelector(
 226                       xml, 
 227 mike  1.1             &e, 
 228                       &wsheader->rqtNamespace,
 229                       wsheader->rqtClassname,
 230                       &wsheader->instance,
 231                       &wsheader->instanceBatch))
 232                       return -1;
 233           
 234                   /**/
 235                   if (XML_Next(xml, &e) != 0)
 236                       return -1;
 237           
 238                   if (XML_END == e.type)
 239                       break;
 240           
 241               }
 242           
 243               return 0;
 244           }
 245           
 246           static int _GetReferenceParameters(
 247               XML* xml, 
 248 mike  1.1     Batch*  dynamicBatch,
 249               MI_Instance** dynamicInstanceParams)
 250           {
 251               XML_Elem e;
 252               const char* classname = 0;
 253               const char* nameSpace = NULL;
 254           
 255               /* extract ResourceURI and SelectorSet */
 256               for (;;)
 257               {
 258                   if (XML_Next(xml, &e) != 0)
 259                       return -1;
 260           
 261                   if (XML_END == e.type)
 262                       break;
 263           
 264                   if (0 == strcmp("w:ResourceURI", e.data))
 265                   {
 266                       if (XML_Expect(xml, &e, XML_CHARS, NULL) != 0)
 267                           return -1;
 268           
 269 mike  1.1             classname = strrchr(e.data, '/');
 270                       /* skip '/' */
 271                       if (classname) 
 272                           classname++;
 273           
 274                       if (XML_Expect(xml, &e, XML_END, "w:ResourceURI") != 0)
 275                           return -1;
 276           
 277                       continue;
 278                   }
 279           
 280                   if (0 == strcmp("w:SelectorSet", e.data))
 281                   {
 282                       /* Allocate an instance */
 283                       if (!*dynamicInstanceParams)
 284                       {
 285                           MI_Result r;
 286                           const MI_Char* cn;
 287           
 288                           if (!classname)
 289                           {
 290 mike  1.1                     XML_Raise(xml, "ResourceURI tag expected");
 291                               return -1;
 292                           }
 293           
 294                           cn = Batch_Strdup2(dynamicBatch, classname);
 295           
 296                           r = Instance_NewDynamic(dynamicInstanceParams, cn, MI_FLAG_CLASS, dynamicBatch);
 297                           if (MI_RESULT_OK != r)
 298                               return -1;
 299                       }
 300           
 301                       /* iterate through all selector tags */
 302                       for (;;)
 303                       {
 304                           const char* ns = NULL;
 305           
 306                           if (_GetSelector(
 307                               xml, 
 308                               &e, 
 309                               &ns, 
 310                               classname, 
 311 mike  1.1                     dynamicInstanceParams, 
 312                               &dynamicBatch) != 0)
 313                           {
 314                               return -1;
 315                           }
 316           
 317                           if (ns)
 318                           {
 319                               nameSpace = ns;
 320                           }
 321           
 322                           /**/
 323                           if (XML_Next(xml, &e) != 0)
 324                               return -1;
 325           
 326                           if (XML_END == e.type)
 327                               break;
 328           
 329                       }
 330                       continue;
 331                   }
 332 mike  1.1     }
 333           
 334               if (nameSpace)
 335                   (*dynamicInstanceParams)->nameSpace = nameSpace;
 336           
 337               return 0;
 338           }
 339           
 340           static int _GetReference(
 341               XML* xml, 
 342               XML_Elem *start,
 343               Batch*  dynamicBatch,
 344               MI_Instance** dynamicInstanceParams)
 345           {
 346               XML_Elem e;
 347           
 348               e = *start;
 349           
 350               /* extract all parameters */
 351               for (;;)
 352               {
 353 mike  1.1         if (0 != strcmp("a:ReferenceParameters", e.data))
 354                   {
 355                       if (XML_Skip(xml) != 0)
 356                           return -1;
 357           
 358                       if (XML_Next(xml, &e) != 0)
 359                           return -1;
 360           
 361                       if (e.type == XML_END)
 362                           break;
 363           
 364                       continue;
 365                   }
 366           
 367                   if (0 != _GetReferenceParameters(xml, dynamicBatch, dynamicInstanceParams))
 368                       return -1;
 369           
 370                   if (XML_Next(xml, &e) != 0)
 371                       return -1;
 372           
 373                   if (e.type == XML_END)
 374 mike  1.1             break;
 375               }
 376           
 377               return 0;
 378           }
 379           
 380           static int _GetSingleProperty(
 381               XML* xml,
 382               Batch*  dynamicBatch,
 383               const char* propNameChar,
 384               MI_Value* value,
 385               MI_Type* type)
 386           {
 387               XML_Elem e;
 388           
 389               if (XML_Next(xml, &e) != 0)
 390                   return -1;
 391           
 392               if (e.type == XML_CHARS)
 393               {
 394                   /* Plain string property */
 395 mike  1.1         value->string = Batch_Strdup2(dynamicBatch, e.data);
 396                   *type = MI_STRING;
 397           
 398                   if (XML_Expect(xml, &e, XML_END, propNameChar) != 0)
 399                       return -1;
 400               }
 401               else if ('a' == e.data[0])
 402               {
 403                   /* Reference as </adddress></ReferenceParameters>*/
 404                   value->instance = 0;
 405                   if (0 != _GetReference(xml, &e, dynamicBatch, &value->instance))
 406                       return -1;
 407           
 408                   *type = MI_REFERENCE;
 409               }
 410               else
 411               {
 412                   /* Embedded instance */
 413                   value->instance = 0;
 414                   if (0 != _GetInstance(xml, &e, dynamicBatch, &value->instance))
 415                       return -1;
 416 mike  1.1 
 417                   *type = MI_INSTANCE;
 418           
 419                   if (XML_Expect(xml, &e, XML_END, propNameChar) != 0)
 420                       return -1;
 421               }
 422           
 423           
 424               return 0;
 425           }
 426           
 427           static int _AddValueToArray(
 428               Batch*  dynamicBatch,
 429               MI_Value* valueA,
 430               MI_Type typeA,
 431               const MI_Value* value,
 432               MI_Type type)
 433           {
 434               /* does type match? */
 435               if ((type | MI_ARRAY_BIT) != typeA)
 436                   return -1;
 437 mike  1.1 
 438               /* do we need to realloc array? */
 439               if ((valueA->array.size % 16) == 0)
 440               {
 441                   void* newData = Batch_Get(dynamicBatch, (valueA->array.size + 16) * sizeof(void*));
 442           
 443                   if (!newData)
 444                       return -1;
 445           
 446                   if (valueA->array.size)
 447                       memcpy(newData, valueA->array.data, valueA->array.size * sizeof(void*));
 448           
 449                   valueA->array.data = newData;
 450               }
 451           
 452               if (type == MI_STRING)
 453                   valueA->stringa.data[valueA->stringa.size] = value->string;
 454               else
 455                   valueA->instancea.data[valueA->instancea.size] = value->instance;
 456           
 457               valueA->array.size++;
 458 mike  1.1     return 0;
 459           }
 460           
 461           static int _GetInstance(
 462               XML* xml, 
 463               XML_Elem *start,
 464               Batch*  dynamicBatch,
 465               MI_Instance** dynamicInstanceParams)
 466           {
 467               XML_Elem e;
 468               const MI_Char* propNameA = 0;
 469               MI_Value valueA;
 470               MI_Type typeA = MI_BOOLEAN;
 471               const MI_Char* propNamePrev = 0;
 472               MI_Value valuePrev;
 473               MI_Type typePrev = MI_BOOLEAN;
 474           
 475               memset(&valueA, 0, sizeof(valueA));
 476           
 477               /* extract all parameters */
 478               for (;;)
 479 mike  1.1     {
 480                   if (XML_Next(xml, &e) != 0)
 481                       return -1;
 482           
 483                   if (e.type == XML_END)
 484                       break;
 485           
 486                   /* skip possible comments */
 487                   if (e.type != XML_START)
 488                       continue;
 489           
 490                   /* allocate new instance if needed */
 491                   if (!*dynamicInstanceParams)
 492                   {
 493                       MI_Result r;
 494                       const MI_Char* cn = Batch_Strdup2(dynamicBatch, start->data + 2);
 495           
 496                       r = Instance_NewDynamic(dynamicInstanceParams, cn, MI_FLAG_CLASS, dynamicBatch);
 497                       if (MI_RESULT_OK != r)
 498                           return -1;
 499                   }
 500 mike  1.1 
 501                   /* add next property to the instance */
 502                   if (e.size > 2) /* ?:<name> */
 503                   {
 504                       MI_Result r;
 505                       MI_Value value;
 506                       MI_Type type = MI_BOOLEAN;
 507                       const char* propNameChar;
 508                       const MI_Char* propName;
 509           
 510                       propNameChar = e.data;
 511                       propName = Batch_Strdup2(dynamicBatch, propNameChar + 2);
 512           
 513                       type = MI_BOOLEAN;
 514           
 515                       if (0 != _GetSingleProperty(xml, dynamicBatch, propNameChar, &value, &type))
 516                           return -1;
 517           
 518                       /* Did we collect array's items? */
 519                       if (propNameA)
 520                       {
 521 mike  1.1                 /* if we have array and new property matches array - add new item to the array */
 522                           if (0 == Zcmp(propNameA, propName))
 523                           {
 524                               if (0 != _AddValueToArray(dynamicBatch, &valueA, typeA, &value, type))
 525                                   return -1;
 526                           }
 527                           else
 528                           {
 529                               r = MI_Instance_AddElement(*dynamicInstanceParams, propNameA, &valueA, 
 530                                   typeA, MI_FLAG_BORROW);
 531           
 532                               if (MI_RESULT_OK != r)
 533                                   return -1;
 534           
 535                               /* Clear array prop name */
 536                               propNameA = 0;
 537           
 538                               propNamePrev = propName;
 539                               valuePrev = value;
 540                               typePrev = type;
 541                           }
 542 mike  1.1             }
 543                       else if (propNamePrev)
 544                       {
 545                           /* Check if name is the same and we need to create an array */
 546                           if (0 == Zcmp(propNamePrev, propName))
 547                           {
 548                               /* create array */
 549                               valueA.array.size = 0;
 550                               valueA.array.data = 0;
 551           
 552                               typeA = type | MI_ARRAY_BIT;
 553                               propNameA = propName;
 554           
 555                               if (0 != _AddValueToArray(dynamicBatch, &valueA, typeA, &valuePrev, typePrev))
 556                                   return -1;
 557           
 558                               if (0 != _AddValueToArray(dynamicBatch, &valueA, typeA, &value, type))
 559                                   return -1;
 560           
 561                           }
 562                           else
 563 mike  1.1                 {
 564                               r = MI_Instance_AddElement(*dynamicInstanceParams, propNamePrev, &valuePrev, 
 565                                   typePrev, MI_FLAG_BORROW);
 566           
 567                               if (MI_RESULT_OK != r)
 568                                   return -1;
 569           
 570           
 571                               propNamePrev = propName;
 572                               valuePrev = value;
 573                               typePrev = type;
 574                          }
 575           
 576                       }
 577                       else
 578                       {
 579                           /* collecting first item */
 580                           propNamePrev = propName;
 581                           valuePrev = value;
 582                           typePrev = type;
 583                       }
 584 mike  1.1         }
 585               }
 586           
 587               /* if last property was array - add it */
 588               if (propNameA)
 589               {
 590                   MI_Result r;
 591           
 592                   r = MI_Instance_AddElement(*dynamicInstanceParams, propNameA, &valueA, 
 593                       typeA, MI_FLAG_BORROW);
 594           
 595                   if (MI_RESULT_OK != r)
 596                       return -1;
 597               }
 598               else if (propNamePrev)
 599               {
 600                   MI_Result r;
 601           
 602                   r = MI_Instance_AddElement(*dynamicInstanceParams, propNamePrev, &valuePrev, 
 603                       typePrev, MI_FLAG_BORROW);
 604           
 605 mike  1.1         if (MI_RESULT_OK != r)
 606                       return -1;
 607               }
 608           
 609               /* check closing tag */
 610               if (strcmp(e.data, start->data) != 0)
 611                   return -1;
 612           
 613               return 0;
 614           }
 615           
 616           int WS_ParseWSHeader(
 617               XML* xml,
 618               WSMAN_WSHeader* wsheader)
 619           {
 620               XML_Elem e;
 621           
 622               memset(wsheader, 0, sizeof(WSMAN_WSHeader));
 623           
 624               /* Expect <s:Header> */
 625               if (XML_Expect(xml, &e, XML_START, "s:Header") != 0)
 626 mike  1.1         return -1;
 627           
 628               for (;;)
 629               {
 630                   if (XML_Next(xml, &e) != 0)
 631                       return -1;
 632           
 633                   if (e.type == XML_END)// && strcmp(e.data, "s:Header") == 0)
 634                   {
 635                       int tag = HashStr(e.data, e.size);
 636           
 637                       if (WSMANTAG_HEADER != tag)
 638                       {
 639                           LOGW_CHAR(("wsman: unexpected close tag [%s] in incoming xml", e.data ));
 640                           return -1;
 641                       }
 642                       //printf("DONE\n");
 643                       break;
 644                   }
 645           
 646                   /* skip possible comments */
 647 mike  1.1         if (e.type != XML_START)
 648                       continue;
 649           
 650                   switch (HashStr(e.data, e.size))
 651                   {
 652                       case WSMANTAG_TO:
 653                       {
 654                           if (XML_Expect(xml, &e, XML_CHARS, NULL) != 0)
 655                               return -1;
 656           
 657                           PRINTF(("a:To{%s}\n", e.data));
 658           
 659                           if (XML_Expect(xml, &e, XML_END, "a:To") != 0)
 660                               return -1;
 661                       }
 662                       break;
 663           
 664                       case WSMANTAG_RESOURCE_URI:
 665                       {
 666                           if (XML_Expect(xml, &e, XML_CHARS, NULL) != 0)
 667                               return -1;
 668 mike  1.1 
 669                           PRINTF(("w:ResourceURI{%s}\n", e.data));
 670                           wsheader->rqtClassname = strrchr(e.data, '/');
 671                           /* skip '/' */
 672                           if (wsheader->rqtClassname) 
 673                               wsheader->rqtClassname++;
 674           
 675                           if (XML_Expect(xml, &e, XML_END, "w:ResourceURI") != 0)
 676                               return -1;
 677                       }
 678                       break;
 679           
 680                       case WSMANTAG_REPLY_TO:
 681                       {
 682                           if (XML_Expect(xml, &e, XML_START, "a:Address") != 0)
 683                               return -1;
 684           
 685                           if (XML_Expect(xml, &e, XML_CHARS, NULL) != 0)
 686                               return -1;
 687           
 688                           if (XML_Expect(xml, &e, XML_END, "a:Address") != 0)
 689 mike  1.1                     return -1;
 690           
 691                           if (XML_Expect(xml, &e, XML_END, "a:ReplyTo") != 0)
 692                               return -1;
 693                       }
 694                       break;
 695           
 696                       case WSMANTAG_ACTION:
 697                       {
 698                           wsheader->foundAction = MI_TRUE;
 699           
 700                           if (XML_Expect(xml, &e, XML_CHARS, NULL) != 0)
 701                               return -1;
 702           
 703                           wsheader->rqtAction = HashStr(e.data, e.size);
 704           
 705                           if (0 == wsheader->rqtAction)
 706                           {
 707                               char* s;
 708                               /* DSP0226; 9: Custom Actions (Methods) just need to have unique URI.
 709                                   We are assuming it has format like http://<server>/wbem/wscim/1/cim-schema/2/<class-name>/<method-name> */
 710 mike  1.1 
 711                               if (0 != strncmp(e.data, "http://", 7))
 712                                   return -1;
 713           
 714                               wsheader->rqtServer = e.data + 7;
 715           
 716                               s = strchr(wsheader->rqtServer, '/');
 717           
 718                               if (!s)
 719                                   return -1;
 720           
 721                               *s = 0;
 722                               s++;
 723           
 724                               if (0 != strncmp(s, "wbem/wscim/1/cim-schema/2/", 26))
 725                                   return -1;
 726           
 727                               s += 26;
 728           
 729                               wsheader->rqtClassname = s;
 730                               s = strchr(s, '/');
 731 mike  1.1 
 732                               if (!s)
 733                                   return -1;
 734           
 735                               *s = 0;
 736                               s++;
 737                               wsheader->rqtMethod = s;
 738                           }
 739           
 740                           if (XML_Expect(xml, &e, XML_END, "a:Action") != 0)
 741                               return -1;
 742                       }
 743                       break;
 744           
 745                       case WSMANTAG_SELECTOR_SET:
 746                       {
 747                           if (_GetSelectorSet(xml, wsheader) != 0)
 748                               return -1;
 749                       }
 750                       break;
 751           
 752 mike  1.1             case WSMANTAG_MESSAGE_ID:
 753                       {
 754                           if (XML_Expect(xml, &e, XML_CHARS, NULL) != 0)
 755                               return -1;
 756           
 757                           wsheader->rqtMessageID = e.data;
 758           
 759                           if (XML_Expect(xml, &e, XML_END, "a:MessageID") != 0)
 760                               return -1;
 761                       }
 762                       break;
 763           
 764                       case WSMANTAG_MAX_ENVELOPE_SIZE:
 765                       {
 766                           if (XML_Expect(xml, &e, XML_CHARS, NULL) != 0)
 767                               return -1;
 768           
 769                           wsheader->maxEnvelopeSize = (MI_Uint32)Strtoull(e.data, NULL, 10);
 770                           PRINTF(("maxEnvelopeSize{%d}\n", wsheader->maxEnvelopeSize));
 771           
 772                           if (XML_Expect(xml, &e, XML_END, "w:MaxEnvelopeSize") != 0)
 773 mike  1.1                     return -1;
 774                       }
 775                       break;
 776           
 777                       default:
 778                       {
 779                           if (_MustUnderstandCanBeIgnored(&e) != 0)
 780                           {
 781                               wsheader->unknownMandatoryTag = e.data;
 782                               LOGW_CHAR(("wsman: unknown mandatory tag [%s]; aborted", 
 783                                   e.data ));
 784                               /* validate header will send correct repsonse to the client */
 785                           }
 786           
 787                           if (XML_Skip(xml) != 0)
 788                               return -1;
 789                       }
 790                       break;
 791           
 792                   }
 793               }
 794 mike  1.1     return 0;
 795           }
 796           
 797           int WS_ParseSoapEnvelope(XML* xml)
 798           {
 799               XML_Elem e;
 800           
 801               /* Ignore the processing instruction (if any) */
 802               {
 803                   if (XML_Next(xml, &e) != 0)
 804                   {
 805                       XML_Raise(xml, "missing root element");
 806                       return -1;
 807                   }
 808           
 809                   if (e.type != XML_INSTRUCTION)
 810                   {
 811                       if (XML_PutBack(xml, &e) != 0)
 812                           return -1;
 813                   }
 814               }
 815 mike  1.1 
 816               /* Expect <s:Envelope> */
 817               if (XML_Expect(xml, &e, XML_START, "s:Envelope") != 0)
 818                   return -1;
 819           
 820               return 0;
 821           }
 822           
 823           static const char* _ExpectCharsAndEnd(
 824               XML* xml,
 825               const char* name)
 826           {
 827               XML_Elem e;
 828               const char* chars;
 829           
 830               if (XML_Expect(xml, &e, XML_CHARS, NULL) != 0)
 831                   return NULL;
 832           
 833               chars = e.data;
 834           
 835               if (XML_Expect(xml, &e, XML_END, name) != 0)
 836 mike  1.1         return NULL;
 837           
 838               return chars;
 839           }
 840           
 841           /*
 842           **==============================================================================
 843           **
 844           ** _ParseAssociationFilterObject()
 845           **
 846           ** Example:
 847           **   <b:Object>
 848           **     <a:Address>
 849           **       http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
 850           **     </a:Address>
 851           **     <a:ReferenceParameters>
 852           **       <w:ResourceURI>
 853           **         http://schemas.microsoft.com/wbem/wscim/1/cim-schema/2/ABC_Widget
 854           **       </w:ResourceURI>
 855           **       <w:SelectorSet>
 856           **         <w:Selector Name="__cimnamespace">
 857 mike  1.1 **           root/cimv2
 858           **         </w:Selector>
 859           **         <w:Selector Name="Key">
 860           **           1001
 861           **         </w:Selector>
 862           **       </w:SelectorSet>
 863           **     </a:ReferenceParameters>
 864           **   </b:Object>
 865           *
 866           **==============================================================================
 867           */
 868           
 869           static int _ParseAssociationFilterObject(
 870               XML* xml, 
 871               Batch* batch,
 872               WSMAN_AssociationFilter* filter)
 873           {
 874               XML_Elem e;
 875           
 876               /* Parse child elements */
 877           
 878 mike  1.1     for (;;)
 879               {
 880                   /* Get next element */
 881           
 882                   if (XML_Next(xml, &e) != 0)
 883                       return -1;
 884           
 885                   /* Put it back and break out if not a start tag */
 886           
 887                   if (e.type != XML_START)
 888                   {
 889                       if (XML_PutBack(xml, &e) != 0)
 890                           return -1;
 891           
 892                       break;
 893                   }
 894           
 895                   /* Handle "Object" tag */
 896           
 897                   if (strcmp(e.data, "a:ReferenceParameters") == 0)
 898                   {
 899 mike  1.1             if (_GetReferenceParameters(
 900                           xml, 
 901                           batch, 
 902                           &filter->referenceParameters) != 0)
 903                       {
 904                           return -1;
 905                       }
 906                   }
 907                   else if (strcmp(e.data, "a:Address") == 0)
 908                   {
 909                       filter->address = _ExpectCharsAndEnd(xml, "a:Address");
 910           
 911                       if (!filter->address)
 912                           return -1;
 913                   }
 914                   else
 915                   {
 916                       if (XML_Skip(xml) != 0)
 917                           return -1;
 918                   }
 919               }
 920 mike  1.1 
 921               /* Expect </Object> */
 922           
 923               if (XML_Expect(xml, &e, XML_END, "b:Object") != 0)
 924                   return -1;
 925           
 926               return 0;
 927           }
 928           
 929           /*
 930           **==============================================================================
 931           **
 932           ** _ParseAssociationFilter()
 933           **
 934           ** Example:
 935           **   <b:AssociatedInstances>
 936           **     <b:Object>
 937           **       <a:Address>
 938           **         http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
 939           **       </a:Address>
 940           **       <a:ReferenceParameters>
 941 mike  1.1 **         <w:ResourceURI>
 942           **           http://schemas.microsoft.com/wbem/wscim/1/cim-schema/2/ABC_Widget
 943           **         </w:ResourceURI>
 944           **         <w:SelectorSet>
 945           **           <w:Selector Name="__cimnamespace">
 946           **             root/cimv2
 947           **           </w:Selector>
 948           **           <w:Selector Name="Key">
 949           **             1001
 950           **           </w:Selector>
 951           **         </w:SelectorSet>
 952           **       </a:ReferenceParameters>
 953           **       </b:Object>
 954           **       <b:AssociationClassName>
 955           **           ABC_Connector
 956           **       </b:AssociationClassName>
 957           **       <b:Role>
 958           **           Component
 959           **       </b:Role>
 960           **     <b:ResultClassName>
 961           **       ABC_Gadget
 962 mike  1.1 **     </b:ResultClassName>
 963           **     <b:ResultRole>
 964           **       Part
 965           **     </b:ResultRole>
 966           **   </b:AssociatedInstances>
 967           **
 968           **==============================================================================
 969           */
 970           
 971           static int _ParseAssociationFilter(
 972               XML* xml, 
 973               Batch* batch,
 974               WSMAN_AssociationFilter* filter)
 975           {
 976               XML_Elem e;
 977           
 978               /* Expect <AssociatedInstances> */
 979           
 980               if (XML_Expect(xml, &e, XML_START, "b:AssociatedInstances") != 0)
 981                   return -1;
 982           
 983 mike  1.1     /* Parse child elements */
 984           
 985               for (;;)
 986               {
 987                   /* Get next element */
 988           
 989                   if (XML_Next(xml, &e) != 0)
 990                       return -1;
 991           
 992                   /* Put it back and break out if not a start tag */
 993           
 994                   if (e.type != XML_START)
 995                   {
 996                       if (XML_PutBack(xml, &e) != 0)
 997                           return -1;
 998           
 999                       break;
1000                   }
1001           
1002                   /* Handle "Object" tag */
1003           
1004 mike  1.1         if (strcmp(e.data, "b:Object") == 0)
1005                   {
1006                       if (_ParseAssociationFilterObject(xml, batch, filter) != 0)
1007                           return -1;
1008                   }
1009                   else if (strcmp(e.data, "b:AssociationClassName") == 0)
1010                   {
1011                       filter->associationClassName = _ExpectCharsAndEnd(
1012                           xml, "b:AssociationClassName");
1013           
1014                       if (!filter->associationClassName)
1015                           return -1;
1016                   }
1017                   else if (strcmp(e.data, "b:Role") == 0)
1018                   {
1019                       filter->role = _ExpectCharsAndEnd(xml, "b:Role");
1020           
1021                       if (!filter->role)
1022                           return -1;
1023                   }
1024                   else if (strcmp(e.data, "b:ResultClassName") == 0)
1025 mike  1.1         {
1026                       filter->resultClassName = _ExpectCharsAndEnd(
1027                           xml, 
1028                           "b:ResultClassName");
1029           
1030                       if (!filter->resultClassName)
1031                           return -1;
1032                   }
1033                   else if (strcmp(e.data, "b:ResultRole") == 0)
1034                   {
1035                       filter->resultRole = _ExpectCharsAndEnd(
1036                           xml, 
1037                           "b:ResultRole");
1038           
1039                       if (!filter->resultRole)
1040                           return -1;
1041                   }
1042                   else
1043                   {
1044                       if (XML_Skip(xml) != 0)
1045                           return -1;
1046 mike  1.1         }
1047               }
1048           
1049           #if 0
1050               printf("AssociationFilter\n");
1051               printf("{\n");
1052               Instance_Print(filter->referenceParameters, stdout, 1, MI_TRUE);
1053               printf("    nameSpace{%s}\n", filter->referenceParameters->nameSpace);
1054               printf("    address{%s}\n", filter->address);
1055               printf("    associationClassName{%s}\n", filter->associationClassName);
1056               printf("    resultClassName{%s}\n", filter->resultClassName);
1057               printf("    role{%s}\n", filter->role);
1058               printf("    resultRole{%s}\n", filter->resultRole);
1059               printf("}\n");
1060           #endif
1061           
1062               /* Expect </AssociatedInstances> */
1063           
1064               if (XML_Expect(xml, &e, XML_END, "b:AssociatedInstances") != 0)
1065                   return -1;
1066           
1067 mike  1.1     return 0;
1068           }
1069           
1070           int WS_ParseEnumerateBody(
1071               XML* xml, 
1072               Batch** batch,
1073               WSMAN_WSEnumeratePullBody* wsenumbody)
1074           {
1075               XML_Elem e;
1076           
1077               memset(wsenumbody, 0, sizeof(WSMAN_WSEnumeratePullBody));
1078           
1079               /* Allocate batch (owned by WSMAN_ConnectionData object */
1080           
1081               if (*batch == NULL)
1082               {
1083                   *batch = Batch_New(BATCH_MAX_PAGES);
1084           
1085                   if (!*batch)
1086                       return -1;
1087               }
1088 mike  1.1 
1089               /* Expect <s:Body> */
1090               if (XML_Expect(xml, &e, XML_START, "s:Body") != 0)
1091                   return -1;
1092           
1093               /* Expect <n:Enumerate> */
1094               if (XML_Expect(xml, &e, XML_START, "n:Enumerate") != 0)
1095                   return -1;
1096           
1097               for (;;)
1098               {
1099                   if (XML_Next(xml, &e) != 0)
1100                       return -1;
1101           
1102                   if (e.type == XML_END)
1103                   {
1104                       int tag = HashStr(e.data, e.size);
1105           
1106                       if (WSMANTAG_ENUM_ENUMERATE != tag)
1107                       {
1108                           LOGW_CHAR(("wsman: unexpected close tag [%s] in incoming xml", e.data ));
1109 mike  1.1                 return -1;
1110                       }
1111                       break;
1112                   }
1113           
1114                   /* skip possible comments */
1115                   if (e.type != XML_START)
1116                       continue;
1117           
1118                   switch (HashStr(e.data, e.size))
1119                   {
1120                   case WSMANTAG_ENUM_MAX_ELEMENTS:
1121                       {
1122                           if (XML_Expect(xml, &e, XML_CHARS, NULL) != 0)
1123                               return -1;
1124           
1125                           wsenumbody->maxElements = (MI_Uint32)Strtoull(e.data, NULL, 10);
1126                           PRINTF(("maxElements{%d}\n", wsenumbody->maxElements));
1127           
1128                           if (XML_Expect(xml, &e, XML_END, "w:MaxElements") != 0)
1129                               return -1;
1130 mike  1.1             }
1131                       break;
1132           
1133                   case WSMANTAG_ENUM_OPTIMIZE_ENUMERATION:
1134                       {
1135                           wsenumbody->allowOptimization = MI_TRUE;
1136           
1137                           if (XML_Skip(xml) != 0)
1138                               return -1;
1139                       }
1140                       break;
1141           
1142                   case WSMANTAG_ENUM_POLYMORPHISM_MODE:
1143                       {
1144                           if (XML_Expect(xml, &e, XML_CHARS, NULL) != 0)
1145                               return -1;
1146           
1147                           wsenumbody->polymorphismMode = HashStr(e.data, e.size);
1148           
1149                           if (XML_Expect(xml, &e, XML_END, "b:PolymorphismMode") != 0)
1150                               return -1;
1151 mike  1.1             }
1152                       break;
1153           
1154                   case WSMANTAG_ENUM_MODE:
1155                       {
1156                           if (XML_Expect(xml, &e, XML_CHARS, NULL) != 0)
1157                               return -1;
1158           
1159                           wsenumbody->enumerationMode = HashStr(e.data, e.size);
1160           
1161                           if (XML_Expect(xml, &e, XML_END, "w:EnumerationMode") != 0)
1162                               return -1;
1163                       }
1164                       break;
1165           
1166                   /* 
1167                    * Examples:
1168                    *   <w:Filter Dialect="http://microsoft.com/wbem/wsman/1/WQL">
1169                    *     SELECT Weight FROM Widget WHERE Key = 1001
1170                    *   </w:Filter>
1171                    *
1172 mike  1.1          *   <w:Filter Dialect="http://microsoft.com/wbem/wsman/1/WQL">
1173                    *     <b:AssociatedInstances>
1174                    *       ...
1175                    *     </b:AssociatedInstances>
1176                    *   </w:Filter>
1177                    */
1178                   case WSMANTAG_ENUM_FILTER:
1179                       {
1180                           const char* dialect;
1181                           const char* p;
1182           
1183                           /* Get 'Dialect' attribute? */
1184                           dialect = XML_Elem_GetAttr(&e, "Dialect");
1185                           if (!dialect)
1186                           {
1187                               LOGW_CHAR(("wsman: Filter tag missing Dialect attribute"));
1188                               return -1;
1189                           }
1190           
1191           
1192                           /* Reduce long dialect name to final component of path */
1193 mike  1.1                 p = strrchr(dialect, '/');
1194                           if (p)
1195                               wsenumbody->dialect = p + 1;
1196                           else
1197                               wsenumbody->dialect = dialect;
1198           
1199                           /* Parse the association filter */
1200                           if (strcmp(dialect, URI_CIMBINDING "/associationFilter") == 0)
1201                           {
1202                               wsenumbody->foundAssociationFilter = MI_TRUE;
1203           
1204                               if (_ParseAssociationFilter(xml, *batch,
1205                                   &wsenumbody->associationFilter) != 0)
1206                               {
1207                                   return -1;
1208                               }
1209                           }
1210                           else
1211                           {
1212                               /* Get the filter text */
1213                               if (XML_Expect(xml, &e, XML_CHARS, NULL) != 0)
1214 mike  1.1                         return -1;
1215           
1216                               wsenumbody->filter = e.data;
1217                           }
1218           
1219                           /* Check for closing </w:Filter> tag */
1220                           if (XML_Expect(xml, &e, XML_END, "w:Filter") != 0)
1221                               return -1;
1222                       }
1223                       break;
1224           
1225                   default:
1226                       {
1227                           if (_MustUnderstandCanBeIgnored(&e) != 0)
1228                           {
1229                               LOGW_CHAR(("wsman: unknown mandatory tag [%s]; aborted", e.data ));
1230                               return -1;
1231                           }
1232           
1233                           if (XML_Skip(xml) != 0)
1234                               return -1;
1235 mike  1.1             }
1236                       break;
1237           
1238                   }
1239               }
1240           
1241               /* Expect <s:Body> */
1242               if (XML_Expect(xml, &e, XML_END, "s:Body") != 0)
1243                   return -1;
1244           
1245               /* Expect </s:Envelope> */
1246               if (XML_Expect(xml, &e, XML_END, "s:Envelope") != 0)
1247                   return -1;
1248           
1249               return 0;
1250           }
1251           
1252           int WS_ParseInvokeBody(
1253               XML* xml, 
1254               Batch*  dynamicBatch,
1255               MI_Instance** dynamicInstanceParams)
1256 mike  1.1 {
1257               XML_Elem e;
1258           
1259               *dynamicInstanceParams = 0;
1260           
1261               /* Expect <s:Body> */
1262               if (XML_Expect(xml, &e, XML_START, "s:Body") != 0)
1263                   return -1;
1264           
1265               /* Expect <?:?> parameter's tag */
1266               for (;;)
1267               {
1268                   if (XML_Next(xml, &e) != 0)
1269                       return -1;
1270           
1271                   /* empty body? can be valid for methods without parameters */
1272                   if (e.type == XML_END)
1273                       return 0;
1274           
1275                   if (e.type == XML_START)
1276                       break;
1277 mike  1.1     }
1278           
1279               if (0 != _GetInstance(xml, &e, dynamicBatch, dynamicInstanceParams))
1280                   return -1;
1281           
1282           
1283               /* Expect <s:Body> */
1284               if (XML_Expect(xml, &e, XML_END, "s:Body") != 0)
1285                   return -1;
1286           
1287               /* Expect </s:Envelope> */
1288               if (XML_Expect(xml, &e, XML_END, "s:Envelope") != 0)
1289                   return -1;
1290           
1291               return 0;
1292           }
1293           
1294           int WS_ParseCreateBody(
1295               XML* xml, 
1296               Batch*  dynamicBatch,
1297               MI_Instance** dynamicInstanceParams)
1298 mike  1.1 {
1299               XML_Elem e;
1300           
1301               /* Expect <s:Body> */
1302               if (XML_Expect(xml, &e, XML_START, "s:Body") != 0)
1303                   return -1;
1304           
1305               /* Expect <?:?> parameter's tag */
1306               if (XML_Next(xml, &e) != 0)
1307                   return -1;
1308           
1309               if (0 != _GetInstance(xml, &e, dynamicBatch, dynamicInstanceParams))
1310                   return -1;
1311           
1312               /* Expect <s:Body> */
1313               if (XML_Expect(xml, &e, XML_END, "s:Body") != 0)
1314                   return -1;
1315           
1316               /* Expect </s:Envelope> */
1317               if (XML_Expect(xml, &e, XML_END, "s:Envelope") != 0)
1318                   return -1;
1319 mike  1.1 
1320               return 0;
1321           }
1322           
1323           int WS_ParsePullBody(
1324               XML* xml, 
1325               WSMAN_WSEnumeratePullBody* wsenumpullbody)
1326           {
1327               XML_Elem e;
1328           
1329               memset(wsenumpullbody, 0, sizeof(WSMAN_WSEnumeratePullBody));
1330           
1331               /* Expect <s:Body> */
1332               if (XML_Expect(xml, &e, XML_START, "s:Body") != 0)
1333                   return -1;
1334           
1335               /* Expect <n:Enumerate> */
1336               if (XML_Expect(xml, &e, XML_START, "n:Pull") != 0)
1337                   return -1;
1338           
1339               for (;;)
1340 mike  1.1     {
1341                   if (XML_Next(xml, &e) != 0)
1342                       return -1;
1343           
1344                   if (e.type == XML_END)
1345                   {
1346                       int tag = HashStr(e.data, e.size);
1347           
1348                       if (WSMANTAG_ENUM_PULL != tag)
1349                       {
1350                           LOGW_CHAR(("wsman: unexpected close tag [%s] in incoming xml", e.data ));
1351                           return -1;
1352                       }
1353                       break;
1354                   }
1355           
1356                   /* skip possible comments */
1357                   if (e.type != XML_START)
1358                       continue;
1359           
1360                   switch (HashStr(e.data, e.size))
1361 mike  1.1         {
1362                   case WSMANTAG_PULL_MAX_ELEMENTS:
1363                       {
1364                           if (XML_Expect(xml, &e, XML_CHARS, NULL) != 0)
1365                               return -1;
1366           
1367                           wsenumpullbody->maxElements = (MI_Uint32)Strtoull(e.data, NULL, 10);
1368                           PRINTF(("maxElements{%d}\n", wsenumpullbody->maxElements));
1369           
1370                           if (XML_Expect(xml, &e, XML_END, "n:MaxElements") != 0)
1371                               return -1;
1372                       }
1373                       break;
1374           
1375                   case WSMANTAG_PULL_ENUMERATION_CONTEXT:
1376                       {
1377                           if (XML_Expect(xml, &e, XML_CHARS, NULL) != 0)
1378                               return -1;
1379           
1380                           wsenumpullbody->enumerationContextID = (MI_Uint32)Strtoull(e.data, NULL, 10);
1381           
1382 mike  1.1                 if (XML_Expect(xml, &e, XML_END, "n:EnumerationContext") != 0)
1383                               return -1;
1384                       }
1385                       break;
1386           
1387                   default:
1388                       {
1389                           if (_MustUnderstandCanBeIgnored(&e) != 0)
1390                           {
1391                               LOGW_CHAR(("wsman: unknown mandatory tag [%s]; aborted", e.data ));
1392                               return -1;
1393                           }
1394           
1395                           if (XML_Skip(xml) != 0)
1396                               return -1;
1397                       }
1398                       break;
1399           
1400                   }
1401               }
1402           
1403 mike  1.1     /* Expect <s:Body> */
1404               if (XML_Expect(xml, &e, XML_END, "s:Body") != 0)
1405                   return -1;
1406           
1407               /* Expect </s:Envelope> */
1408               if (XML_Expect(xml, &e, XML_END, "s:Envelope") != 0)
1409                   return -1;
1410           
1411               return 0;
1412           }
1413           
1414           int WS_ParseReleaseBody(
1415               XML* xml, 
1416               WSMAN_WSEnumeratePullBody* wsenumpullbody)
1417           {
1418               XML_Elem e;
1419           
1420               memset(wsenumpullbody, 0, sizeof(WSMAN_WSEnumeratePullBody));
1421           
1422               /* Expect <s:Body> */
1423               if (XML_Expect(xml, &e, XML_START, "s:Body") != 0)
1424 mike  1.1         return -1;
1425           
1426               /* Expect <n:Release> */
1427               if (XML_Expect(xml, &e, XML_START, "n:Release") != 0)
1428                   return -1;
1429           
1430               for (;;)
1431               {
1432                   if (XML_Next(xml, &e) != 0)
1433                       return -1;
1434           
1435                   if (e.type == XML_END)
1436                   {
1437                       int tag = HashStr(e.data, e.size);
1438           
1439                       if (WSMANTAG_ENUM_RELEASE != tag)
1440                       {
1441                           LOGW_CHAR(("wsman: unexpected close tag [%s] in incoming xml", e.data ));
1442                           return -1;
1443                       }
1444                       break;
1445 mike  1.1         }
1446           
1447                   /* skip possible comments */
1448                   if (e.type != XML_START)
1449                       continue;
1450           
1451                   switch (HashStr(e.data, e.size))
1452                   {
1453                   case WSMANTAG_PULL_ENUMERATION_CONTEXT:
1454                       {
1455                           if (XML_Expect(xml, &e, XML_CHARS, NULL) != 0)
1456                               return -1;
1457           
1458                           wsenumpullbody->enumerationContextID = (MI_Uint32)Strtoull(e.data, NULL, 10);
1459           
1460                           if (XML_Expect(xml, &e, XML_END, "n:EnumerationContext") != 0)
1461                               return -1;
1462                       }
1463                       break;
1464           
1465                   default:
1466 mike  1.1             {
1467                           if (_MustUnderstandCanBeIgnored(&e) != 0)
1468                           {
1469                               LOGW_CHAR(("wsman: unknown mandatory tag [%s]; aborted", e.data ));
1470                               return -1;
1471                           }
1472           
1473                           if (XML_Skip(xml) != 0)
1474                               return -1;
1475                       }
1476                       break;
1477           
1478                   }
1479               }
1480           
1481               /* Expect <s:Body> */
1482               if (XML_Expect(xml, &e, XML_END, "s:Body") != 0)
1483                   return -1;
1484           
1485               /* Expect </s:Envelope> */
1486               if (XML_Expect(xml, &e, XML_END, "s:Envelope") != 0)
1487 mike  1.1         return -1;
1488           
1489               return 0;
1490           }
1491           
1492           int WS_ParseIdentifyBody(
1493               XML* xml)
1494           {
1495               XML_Elem e;
1496           
1497               if (XML_Expect(xml, &e, XML_START, "s:Body") != 0)
1498                   return -1;
1499           
1500               if (XML_Expect(xml, &e, XML_START, "i:Identify") != 0)
1501                   return -1;
1502           
1503               if (XML_Expect(xml, &e, XML_END, "i:Identify") != 0)
1504                   return -1;
1505           
1506               if (XML_Expect(xml, &e, XML_END, "s:Body") != 0)
1507                   return -1;
1508 mike  1.1 
1509               if (XML_Expect(xml, &e, XML_END, "s:Envelope") != 0)
1510                   return -1;
1511           
1512               return 0;
1513           }

ViewCVS 0.9.2