1/* $Id: upnpdescgen.c,v 1.15 2009/07/17 22:54:31 jmaggard Exp $ */ 2/* MiniUPnP project 3 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ 4 * (c) 2006-2008 Thomas Bernard 5 * This software is subject to the conditions detailed 6 * in the LICENCE file provided within the distribution */ 7 8#include <stdio.h> 9#include <stdlib.h> 10#include <string.h> 11 12#include "config.h" 13#include "getifaddr.h" 14#include "upnpdescgen.h" 15#include "minidlnapath.h" 16#include "upnpglobalvars.h" 17#include "upnpdescstrings.h" 18 19#undef DESC_DEBUG 20 21static const char * const upnptypes[] = 22{ 23 "string", 24 "boolean", 25 "ui2", 26 "ui4", 27 "i4", 28 "uri", 29 "int", 30 "bin.base64" 31}; 32 33static const char * const upnpdefaultvalues[] = 34{ 35 0, 36 "Unconfigured" 37}; 38 39static const char * const upnpallowedvalues[] = 40{ 41 0, /* 0 */ 42 "DSL", /* 1 */ 43 "POTS", 44 "Cable", 45 "Ethernet", 46 0, 47 "Up", /* 6 */ 48 "Down", 49 "Initializing", 50 "Unavailable", 51 0, 52 "TCP", /* 11 */ 53 "UDP", 54 0, 55 "Unconfigured", /* 14 */ 56 "IP_Routed", 57 "IP_Bridged", 58 0, 59 "Unconfigured", /* 18 */ 60 "Connecting", 61 "Connected", 62 "PendingDisconnect", 63 "Disconnecting", 64 "Disconnected", 65 0, 66 "ERROR_NONE", /* 25 */ 67 0, 68 "OK", /* 27 */ 69 "ContentFormatMismatch", 70 "InsufficientBandwidth", 71 "UnreliableChannel", 72 "Unknown", 73 0, 74 "Input", /* 33 */ 75 "Output", 76 0, 77 "BrowseMetadata", /* 36 */ 78 "BrowseDirectChildren", 79 0, 80 "COMPLETED", /* 39 */ 81 "ERROR", 82 "IN_PROGRESS", 83 "STOPPED", 84 0, 85 RESOURCE_PROTOCOL_INFO_VALUES, /* 44 */ 86 0, 87 "0", /* 46 */ 88 0, 89 "", /* 48 */ 90 0 91}; 92 93static const char xmlver[] = 94 "<?xml version=\"1.0\"?>\r\n"; 95static const char root_service[] = 96 "scpd xmlns=\"urn:schemas-upnp-org:service-1-0\""; 97static const char root_device[] = 98 "root xmlns=\"urn:schemas-upnp-org:device-1-0\""; 99 100/* root Description of the UPnP Device 101 * fixed to match UPnP_IGD_InternetGatewayDevice 1.0.pdf 102 * presentationURL is only "recommended" but the router doesn't appears 103 * in "Network connections" in Windows XP if it is not present. */ 104static const struct XMLElt rootDesc[] = 105{ 106 {root_device, INITHELPER(1,2)}, 107 {"specVersion", INITHELPER(3,2)}, 108 {"device", INITHELPER(5,14)}, 109 {"/major", "1"}, 110 {"/minor", "0"}, 111 {"/deviceType", "urn:schemas-upnp-org:device:MediaServer:1"}, 112 {"/friendlyName", friendly_name}, /* required */ 113 {"/manufacturer", ROOTDEV_MANUFACTURER}, /* required */ 114 {"/manufacturerURL", ROOTDEV_MANUFACTURERURL}, /* optional */ 115 {"/modelDescription", ROOTDEV_MODELDESCRIPTION}, /* recommended */ 116 {"/modelName", ROOTDEV_MODELNAME}, /* required */ 117 {"/modelNumber", modelnumber}, 118 {"/modelURL", ROOTDEV_MODELURL}, 119 {"/serialNumber", serialnumber}, 120 {"/UDN", uuidvalue}, /* required */ 121 {"/dlna:X_DLNADOC xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\"", "DMS-1.50"}, 122 {"/presentationURL", presentationurl}, /* recommended */ 123 {"iconList", INITHELPER(19,4)}, 124 {"serviceList", INITHELPER(43,3)}, 125 {"icon", INITHELPER(23,5)}, 126 {"icon", INITHELPER(28,5)}, 127 {"icon", INITHELPER(33,5)}, 128 {"icon", INITHELPER(38,5)}, 129 {"/mimetype", "image/png"}, 130 {"/width", "48"}, 131 {"/height", "48"}, 132 {"/depth", "24"}, 133 {"/url", "/icons/sm.png"}, 134 {"/mimetype", "image/png"}, 135 {"/width", "120"}, 136 {"/height", "120"}, 137 {"/depth", "24"}, 138 {"/url", "/icons/lrg.png"}, 139 {"/mimetype", "image/jpeg"}, 140 {"/width", "48"}, 141 {"/height", "48"}, 142 {"/depth", "24"}, 143 {"/url", "/icons/sm.jpg"}, 144 {"/mimetype", "image/jpeg"}, 145 {"/width", "120"}, 146 {"/height", "120"}, 147 {"/depth", "24"}, 148 {"/url", "/icons/lrg.jpg"}, 149 {"service", INITHELPER(46,5)}, 150 {"service", INITHELPER(51,5)}, 151 {"service", INITHELPER(56,5)}, 152 {"/serviceType", "urn:schemas-upnp-org:service:ContentDirectory:1"}, 153 {"/serviceId", "urn:upnp-org:serviceId:ContentDirectory"}, 154 {"/controlURL", CONTENTDIRECTORY_CONTROLURL}, 155 {"/eventSubURL", CONTENTDIRECTORY_EVENTURL}, 156 {"/SCPDURL", CONTENTDIRECTORY_PATH}, 157 {"/serviceType", "urn:schemas-upnp-org:service:ConnectionManager:1"}, 158 {"/serviceId", "urn:upnp-org:serviceId:ConnectionManager"}, 159 {"/controlURL", CONNECTIONMGR_CONTROLURL}, 160 {"/eventSubURL", CONNECTIONMGR_EVENTURL}, 161 {"/SCPDURL", CONNECTIONMGR_PATH}, 162 {"/serviceType", "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1"}, 163 {"/serviceId", "urn:microsoft.com:serviceId:X_MS_MediaReceiverRegistrar"}, 164 {"/controlURL", X_MS_MEDIARECEIVERREGISTRAR_CONTROLURL}, 165 {"/eventSubURL", X_MS_MEDIARECEIVERREGISTRAR_EVENTURL}, 166 {"/SCPDURL", X_MS_MEDIARECEIVERREGISTRAR_PATH}, 167 {0, 0} 168}; 169 170static const struct argument AddPortMappingArgs[] = 171{ 172 {NULL, 1, 11}, 173 {NULL, 1, 12}, 174 {NULL, 1, 14}, 175 {NULL, 1, 13}, 176 {NULL, 1, 15}, 177 {NULL, 1, 9}, 178 {NULL, 1, 16}, 179 {NULL, 1, 10}, 180 {NULL, 0, 0} 181}; 182 183static const struct argument DeletePortMappingArgs[] = 184{ 185 {NULL, 1, 11}, 186 {NULL, 1, 12}, 187 {NULL, 1, 14}, 188 {NULL, 0, 0} 189}; 190 191static const struct argument SetConnectionTypeArgs[] = 192{ 193 {NULL, 1, 0}, 194 {NULL, 0, 0} 195}; 196 197static const struct argument GetConnectionTypeInfoArgs[] = 198{ 199 {NULL, 2, 0}, 200 {NULL, 2, 1}, 201 {NULL, 0, 0} 202}; 203 204static const struct argument GetNATRSIPStatusArgs[] = 205{ 206 {NULL, 2, 5}, 207 {NULL, 2, 6}, 208 {NULL, 0, 0} 209}; 210 211static const struct argument GetGenericPortMappingEntryArgs[] = 212{ 213 {NULL, 1, 8}, 214 {NULL, 2, 11}, 215 {NULL, 2, 12}, 216 {NULL, 2, 14}, 217 {NULL, 2, 13}, 218 {NULL, 2, 15}, 219 {NULL, 2, 9}, 220 {NULL, 2, 16}, 221 {NULL, 2, 10}, 222 {NULL, 0, 0} 223}; 224 225static const struct argument GetSpecificPortMappingEntryArgs[] = 226{ 227 {NULL, 1, 11}, 228 {NULL, 1, 12}, 229 {NULL, 1, 14}, 230 {NULL, 2, 13}, 231 {NULL, 2, 15}, 232 {NULL, 2, 9}, 233 {NULL, 2, 16}, 234 {NULL, 2, 10}, 235 {NULL, 0, 0} 236}; 237 238/* For ConnectionManager */ 239static const struct argument GetProtocolInfoArgs[] = 240{ 241 {"Source", 2, 0}, 242 {"Sink", 2, 1}, 243 {NULL, 0, 0} 244}; 245 246static const struct argument PrepareForConnectionArgs[] = 247{ 248 {"RemoteProtocolInfo", 1, 6}, 249 {"PeerConnectionManager", 1, 4}, 250 {"PeerConnectionID", 1, 7}, 251 {"Direction", 1, 5}, 252 {"ConnectionID", 2, 7}, 253 {"AVTransportID", 2, 8}, 254 {"RcsID", 2, 9}, 255 {NULL, 0, 0} 256}; 257 258static const struct argument ConnectionCompleteArgs[] = 259{ 260 {"ConnectionID", 1, 7}, 261 {NULL, 0, 0} 262}; 263 264static const struct argument GetCurrentConnectionIDsArgs[] = 265{ 266 {"ConnectionIDs", 2, 2}, 267 {NULL, 0, 0} 268}; 269 270static const struct argument GetCurrentConnectionInfoArgs[] = 271{ 272 {"ConnectionID", 1, 7}, 273 {"RcsID", 2, 9}, 274 {"AVTransportID", 2, 8}, 275 {"ProtocolInfo", 2, 6}, 276 {"PeerConnectionManager", 2, 4}, 277 {"PeerConnectionID", 2, 7}, 278 {"Direction", 2, 5}, 279 {"Status", 2, 3}, 280 {NULL, 0, 0} 281}; 282 283static const struct action ConnectionManagerActions[] = 284{ 285 {"GetProtocolInfo", GetProtocolInfoArgs}, /* R */ 286 //OPTIONAL {"PrepareForConnection", PrepareForConnectionArgs}, /* R */ 287 //OPTIONAL {"ConnectionComplete", ConnectionCompleteArgs}, /* R */ 288 {"GetCurrentConnectionIDs", GetCurrentConnectionIDsArgs}, /* R */ 289 {"GetCurrentConnectionInfo", GetCurrentConnectionInfoArgs}, /* R */ 290 {0, 0} 291}; 292 293static const struct stateVar ConnectionManagerVars[] = 294{ 295 {"SourceProtocolInfo", 1<<7, 0, 0, 44}, /* required */ 296 {"SinkProtocolInfo", 1<<7, 0, 0, 48}, /* required */ 297 {"CurrentConnectionIDs", 1<<7, 0, 0, 46}, /* required */ 298 {"A_ARG_TYPE_ConnectionStatus", 0, 0, 27}, /* required */ 299 {"A_ARG_TYPE_ConnectionManager", 0, 0}, /* required */ 300 {"A_ARG_TYPE_Direction", 0, 0, 33}, /* required */ 301 {"A_ARG_TYPE_ProtocolInfo", 0, 0}, /* required */ 302 {"A_ARG_TYPE_ConnectionID", 4, 0}, /* required */ 303 {"A_ARG_TYPE_AVTransportID", 4, 0}, /* required */ 304 {"A_ARG_TYPE_RcsID", 4, 0}, /* required */ 305 {0, 0} 306}; 307 308static const struct argument GetSearchCapabilitiesArgs[] = 309{ 310 {"SearchCaps", 2, 10}, 311 {0, 0} 312}; 313 314static const struct argument GetSortCapabilitiesArgs[] = 315{ 316 {"SortCaps", 2, 11}, 317 {0, 0} 318}; 319 320static const struct argument GetSystemUpdateIDArgs[] = 321{ 322 {"Id", 2, 12}, 323 {0, 0} 324}; 325 326static const struct argument BrowseArgs[] = 327{ 328 {"ObjectID", 1, 1}, 329 {"BrowseFlag", 1, 4}, 330 {"Filter", 1, 5}, 331 {"StartingIndex", 1, 7}, 332 {"RequestedCount", 1, 8}, 333 {"SortCriteria", 1, 6}, 334 {"Result", 2, 2}, 335 {"NumberReturned", 2, 8}, 336 {"TotalMatches", 2, 8}, 337 {"UpdateID", 2, 9}, 338 {0, 0} 339}; 340 341static const struct argument SearchArgs[] = 342{ 343 {"ContainerID", 1, 1}, 344 {"SearchCriteria", 1, 3}, 345 {"Filter", 1, 5}, 346 {"StartingIndex", 1, 7}, 347 {"RequestedCount", 1, 8}, 348 {"SortCriteria", 1, 6}, 349 {"Result", 2, 2}, 350 {"NumberReturned", 2, 8}, 351 {"TotalMatches", 2, 8}, 352 {"UpdateID", 2, 9}, 353 {0, 0} 354}; 355 356static const struct action ContentDirectoryActions[] = 357{ 358 {"GetSearchCapabilities", GetSearchCapabilitiesArgs}, /* R */ 359 {"GetSortCapabilities", GetSortCapabilitiesArgs}, /* R */ 360 {"GetSystemUpdateID", GetSystemUpdateIDArgs}, /* R */ 361 {"Browse", BrowseArgs}, /* R */ 362 {"Search", SearchArgs}, /* O */ 363#if 0 // Not implementing optional features yet... 364 {"CreateObject", CreateObjectArgs}, /* O */ 365 {"DestroyObject", DestroyObjectArgs}, /* O */ 366 {"UpdateObject", UpdateObjectArgs}, /* O */ 367 {"ImportResource", ImportResourceArgs}, /* O */ 368 {"ExportResource", ExportResourceArgs}, /* O */ 369 {"StopTransferResource", StopTransferResourceArgs}, /* O */ 370 {"GetTransferProgress", GetTransferProgressArgs}, /* O */ 371 {"DeleteResource", DeleteResourceArgs}, /* O */ 372 {"CreateReference", CreateReferenceArgs}, /* O */ 373#endif 374 {0, 0} 375}; 376 377static const struct stateVar ContentDirectoryVars[] = 378{ 379 {"TransferIDs", 1<<7, 0, 0, 48}, /* 0 */ 380 {"A_ARG_TYPE_ObjectID", 0, 0}, 381 {"A_ARG_TYPE_Result", 0, 0}, 382 {"A_ARG_TYPE_SearchCriteria", 0, 0}, 383 {"A_ARG_TYPE_BrowseFlag", 0, 0, 36}, 384 /* Allowed Values : BrowseMetadata / BrowseDirectChildren */ 385 {"A_ARG_TYPE_Filter", 0, 0}, /* 5 */ 386 {"A_ARG_TYPE_SortCriteria", 0, 0}, 387 {"A_ARG_TYPE_Index", 3, 0}, 388 {"A_ARG_TYPE_Count", 3, 0}, 389 {"A_ARG_TYPE_UpdateID", 3, 0}, 390 //JM{"A_ARG_TYPE_TransferID", 3, 0}, /* 10 */ 391 //JM{"A_ARG_TYPE_TransferStatus", 0, 0, 39}, 392 /* Allowed Values : COMPLETED / ERROR / IN_PROGRESS / STOPPED */ 393 //JM{"A_ARG_TYPE_TransferLength", 0, 0}, 394 //JM{"A_ARG_TYPE_TransferTotal", 0, 0}, 395 //JM{"A_ARG_TYPE_TagValueList", 0, 0}, 396 //JM{"A_ARG_TYPE_URI", 5, 0}, /* 15 */ 397 {"SearchCapabilities", 0, 0}, 398 {"SortCapabilities", 0, 0}, 399 {"SystemUpdateID", 3|0x80, 0, 0, 255}, 400 //{"ContainerUpdateIDs", 0, 0}, 401 {0, 0} 402}; 403 404static const struct argument GetIsAuthorizedArgs[] = 405{ 406 {"DeviceID", 1, 0}, 407 {"Result", 2, 3}, 408 {NULL, 0, 0} 409}; 410 411static const struct argument GetIsValidatedArgs[] = 412{ 413 {"DeviceID", 1, 0}, 414 {"Result", 2, 3}, 415 {NULL, 0, 0} 416}; 417 418static const struct argument GetRegisterDeviceArgs[] = 419{ 420 {"RegistrationReqMsg", 1, 1}, 421 {"RegistrationRespMsg", 2, 2}, 422 {NULL, 0, 0} 423}; 424 425static const struct action X_MS_MediaReceiverRegistrarActions[] = 426{ 427 {"IsAuthorized", GetIsAuthorizedArgs}, /* R */ 428 {"IsValidated", GetIsValidatedArgs}, /* R */ 429#if 0 // Not needed? WMP12 still works. Need to check with 360 and WMP11. 430 {"RegisterDevice", GetRegisterDeviceArgs}, /* R */ 431#endif 432 {0, 0} 433}; 434 435static const struct stateVar X_MS_MediaReceiverRegistrarVars[] = 436{ 437 {"A_ARG_TYPE_DeviceID", 0, 0}, 438 {"A_ARG_TYPE_RegistrationReqMsg", 7, 0}, 439 {"A_ARG_TYPE_RegistrationRespMsg", 7, 0}, 440 {"A_ARG_TYPE_Result", 6, 0}, 441 {"AuthorizationDeniedUpdateID", 3, 0}, 442 {"AuthorizationGrantedUpdateID", 3, 0}, 443 {"ValidationRevokedUpdateID", 3, 0}, 444 {"ValidationSucceededUpdateID", 3, 0}, 445 {0, 0} 446}; 447 448/* WANCfg.xml */ 449/* See UPnP_IGD_WANCommonInterfaceConfig 1.0.pdf */ 450 451static const struct argument GetCommonLinkPropertiesArgs[] = 452{ 453 {NULL, 2, 0}, 454 {NULL, 2, 1}, 455 {NULL, 2, 2}, 456 {NULL, 2, 3}, 457 {NULL, 0, 0} 458}; 459 460static const struct argument GetTotalBytesSentArgs[] = 461{ 462 {NULL, 2, 4}, 463 {NULL, 0, 0} 464}; 465 466static const struct argument GetTotalBytesReceivedArgs[] = 467{ 468 {NULL, 2, 5}, 469 {NULL, 0, 0} 470}; 471 472static const struct argument GetTotalPacketsSentArgs[] = 473{ 474 {NULL, 2, 6}, 475 {NULL, 0, 0} 476}; 477 478static const struct argument GetTotalPacketsReceivedArgs[] = 479{ 480 {NULL, 2, 7}, 481 {NULL, 0, 0} 482}; 483 484static const struct serviceDesc scpdContentDirectory = 485{ ContentDirectoryActions, ContentDirectoryVars }; 486 487static const struct serviceDesc scpdConnectionManager = 488{ ConnectionManagerActions, ConnectionManagerVars }; 489 490static const struct serviceDesc scpdX_MS_MediaReceiverRegistrar = 491{ X_MS_MediaReceiverRegistrarActions, X_MS_MediaReceiverRegistrarVars }; 492 493/* strcat_str() 494 * concatenate the string and use realloc to increase the 495 * memory buffer if needed. */ 496static char * 497strcat_str(char * str, int * len, int * tmplen, const char * s2) 498{ 499 int s2len; 500 s2len = (int)strlen(s2); 501 if(*tmplen <= (*len + s2len)) 502 { 503 if(s2len < 256) 504 *tmplen += 256; 505 else 506 *tmplen += s2len + 1; 507 str = (char *)realloc(str, *tmplen); 508 } 509 /*strcpy(str + *len, s2); */ 510 memcpy(str + *len, s2, s2len + 1); 511 *len += s2len; 512 return str; 513} 514 515/* strcat_char() : 516 * concatenate a character and use realloc to increase the 517 * size of the memory buffer if needed */ 518static char * 519strcat_char(char * str, int * len, int * tmplen, char c) 520{ 521 if(*tmplen <= (*len + 1)) 522 { 523 *tmplen += 256; 524 str = (char *)realloc(str, *tmplen); 525 } 526 str[*len] = c; 527 (*len)++; 528 return str; 529} 530 531/* iterative subroutine using a small stack 532 * This way, the progam stack usage is kept low */ 533static char * 534genXML(char * str, int * len, int * tmplen, 535 const struct XMLElt * p) 536{ 537 u_int16_t i, j, k; 538 int top; 539 const char * eltname, *s; 540 char c; 541 char element[64]; 542 struct { 543 unsigned short i; 544 unsigned short j; 545 const char * eltname; 546 } pile[16]; /* stack */ 547 top = -1; 548 i = 0; /* current node */ 549 j = 1; /* i + number of nodes*/ 550 for(;;) 551 { 552 eltname = p[i].eltname; 553 if(!eltname) 554 return str; 555 if(eltname[0] == '/') 556 { 557 #ifdef DESC_DEBUG 558 printf("DBG: <%s>%s<%s>\n", eltname+1, p[i].data, eltname); 559 #endif 560 str = strcat_char(str, len, tmplen, '<'); 561 str = strcat_str(str, len, tmplen, eltname+1); 562 str = strcat_char(str, len, tmplen, '>'); 563 str = strcat_str(str, len, tmplen, p[i].data); 564 str = strcat_char(str, len, tmplen, '<'); 565 sscanf(eltname, "%s", element); 566 str = strcat_str(str, len, tmplen, element); 567 str = strcat_char(str, len, tmplen, '>'); 568 for(;;) 569 { 570 if(top < 0) 571 return str; 572 i = ++(pile[top].i); 573 j = pile[top].j; 574 #ifdef DESC_DEBUG 575 printf("DBG: pile[%d]\t%d %d\n", top, i, j); 576 #endif 577 if(i==j) 578 { 579 #ifdef DESC_DEBUG 580 printf("DBG: i==j, </%s>\n", pile[top].eltname); 581 #endif 582 str = strcat_char(str, len, tmplen, '<'); 583 str = strcat_char(str, len, tmplen, '/'); 584 s = pile[top].eltname; 585 for(c = *s; c > ' '; c = *(++s)) 586 str = strcat_char(str, len, tmplen, c); 587 str = strcat_char(str, len, tmplen, '>'); 588 top--; 589 } 590 else 591 break; 592 } 593 } 594 else 595 { 596 #ifdef DESC_DEBUG 597 printf("DBG: [%d] <%s>\n", i, eltname); 598 #endif 599 str = strcat_char(str, len, tmplen, '<'); 600 str = strcat_str(str, len, tmplen, eltname); 601 str = strcat_char(str, len, tmplen, '>'); 602 k = i; 603 /*i = p[k].index; */ 604 /*j = i + p[k].nchild; */ 605 i = (unsigned long)p[k].data & 0xffff; 606 j = i + ((unsigned long)p[k].data >> 16); 607 top++; 608 #ifdef DESC_DEBUG 609 printf("DBG: +pile[%d]\t%d %d\n", top, i, j); 610 #endif 611 pile[top].i = i; 612 pile[top].j = j; 613 pile[top].eltname = eltname; 614 } 615 } 616} 617 618/* genRootDesc() : 619 * - Generate the root description of the UPnP device. 620 * - the len argument is used to return the length of 621 * the returned string. 622 * - tmp_uuid argument is used to build the uuid string */ 623char * 624genRootDesc(int * len) 625{ 626 char * str; 627 int tmplen; 628 tmplen = 2048; 629 str = (char *)malloc(tmplen); 630 if(str == NULL) 631 return NULL; 632 * len = strlen(xmlver); 633 /*strcpy(str, xmlver); */ 634 memcpy(str, xmlver, *len + 1); 635 str = genXML(str, len, &tmplen, rootDesc); 636 str[*len] = '\0'; 637 return str; 638} 639 640/* genServiceDesc() : 641 * Generate service description with allowed methods and 642 * related variables. */ 643static char * 644genServiceDesc(int * len, const struct serviceDesc * s) 645{ 646 int i, j; 647 const struct action * acts; 648 const struct stateVar * vars; 649 const struct argument * args; 650 const char * p; 651 char * str; 652 int tmplen; 653 tmplen = 2048; 654 str = (char *)malloc(tmplen); 655 if(str == NULL) 656 return NULL; 657 /*strcpy(str, xmlver); */ 658 *len = strlen(xmlver); 659 memcpy(str, xmlver, *len + 1); 660 661 acts = s->actionList; 662 vars = s->serviceStateTable; 663 664 str = strcat_char(str, len, &tmplen, '<'); 665 str = strcat_str(str, len, &tmplen, root_service); 666 str = strcat_char(str, len, &tmplen, '>'); 667 668 str = strcat_str(str, len, &tmplen, 669 "<specVersion><major>1</major><minor>0</minor></specVersion>"); 670 671 i = 0; 672 str = strcat_str(str, len, &tmplen, "<actionList>"); 673 while(acts[i].name) 674 { 675 str = strcat_str(str, len, &tmplen, "<action><name>"); 676 str = strcat_str(str, len, &tmplen, acts[i].name); 677 str = strcat_str(str, len, &tmplen, "</name>"); 678 /* argument List */ 679 args = acts[i].args; 680 if(args) 681 { 682 str = strcat_str(str, len, &tmplen, "<argumentList>"); 683 j = 0; 684 while(args[j].dir) 685 { 686 str = strcat_str(str, len, &tmplen, "<argument><name>"); 687 p = vars[args[j].relatedVar].name; 688 str = strcat_str(str, len, &tmplen, (args[j].name ? args[j].name : p)); 689 str = strcat_str(str, len, &tmplen, "</name><direction>"); 690 str = strcat_str(str, len, &tmplen, args[j].dir==1?"in":"out"); 691 str = strcat_str(str, len, &tmplen, 692 "</direction><relatedStateVariable>"); 693 str = strcat_str(str, len, &tmplen, p); 694 str = strcat_str(str, len, &tmplen, 695 "</relatedStateVariable></argument>"); 696 j++; 697 } 698 str = strcat_str(str, len, &tmplen,"</argumentList>"); 699 } 700 str = strcat_str(str, len, &tmplen, "</action>"); 701 /*str = strcat_char(str, len, &tmplen, '\n'); // TEMP ! */ 702 i++; 703 } 704 str = strcat_str(str, len, &tmplen, "</actionList><serviceStateTable>"); 705 i = 0; 706 while(vars[i].name) 707 { 708 str = strcat_str(str, len, &tmplen, 709 "<stateVariable sendEvents=\""); 710 str = strcat_str(str, len, &tmplen, (vars[i].itype & 0x80)?"yes":"no"); 711 str = strcat_str(str, len, &tmplen, "\"><name>"); 712 str = strcat_str(str, len, &tmplen, vars[i].name); 713 str = strcat_str(str, len, &tmplen, "</name><dataType>"); 714 str = strcat_str(str, len, &tmplen, upnptypes[vars[i].itype & 0x0f]); 715 str = strcat_str(str, len, &tmplen, "</dataType>"); 716 if(vars[i].iallowedlist) 717 { 718 str = strcat_str(str, len, &tmplen, "<allowedValueList>"); 719 for(j=vars[i].iallowedlist; upnpallowedvalues[j]; j++) 720 { 721 str = strcat_str(str, len, &tmplen, "<allowedValue>"); 722 str = strcat_str(str, len, &tmplen, upnpallowedvalues[j]); 723 str = strcat_str(str, len, &tmplen, "</allowedValue>"); 724 } 725 str = strcat_str(str, len, &tmplen, "</allowedValueList>"); 726 } 727 /*if(vars[i].defaultValue) */ 728 if(vars[i].idefault) 729 { 730 str = strcat_str(str, len, &tmplen, "<defaultValue>"); 731 /*str = strcat_str(str, len, &tmplen, vars[i].defaultValue); */ 732 str = strcat_str(str, len, &tmplen, upnpdefaultvalues[vars[i].idefault]); 733 str = strcat_str(str, len, &tmplen, "</defaultValue>"); 734 } 735 str = strcat_str(str, len, &tmplen, "</stateVariable>"); 736 /*str = strcat_char(str, len, &tmplen, '\n'); // TEMP ! */ 737 i++; 738 } 739 str = strcat_str(str, len, &tmplen, "</serviceStateTable></scpd>"); 740 str[*len] = '\0'; 741 return str; 742} 743 744/* genContentDirectory() : 745 * Generate the ContentDirectory xml description */ 746char * 747genContentDirectory(int * len) 748{ 749 return genServiceDesc(len, &scpdContentDirectory); 750} 751 752/* genConnectionManager() : 753 * Generate the ConnectionManager xml description */ 754char * 755genConnectionManager(int * len) 756{ 757 return genServiceDesc(len, &scpdConnectionManager); 758} 759 760/* genX_MS_MediaReceiverRegistrar() : 761 * Generate the X_MS_MediaReceiverRegistrar xml description */ 762char * 763genX_MS_MediaReceiverRegistrar(int * len) 764{ 765 return genServiceDesc(len, &scpdX_MS_MediaReceiverRegistrar); 766} 767 768static char * 769genEventVars(int * len, const struct serviceDesc * s, const char * servns) 770{ 771 const struct stateVar * v; 772 char * str; 773 int tmplen; 774 char buf[512]; 775 tmplen = 512; 776 str = (char *)malloc(tmplen); 777 if(str == NULL) 778 return NULL; 779 *len = 0; 780 v = s->serviceStateTable; 781 snprintf(buf, sizeof(buf), "<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\" xmlns:s=\"%s\">", servns); 782 str = strcat_str(str, len, &tmplen, buf); 783 while(v->name) { 784 if(v->itype & 0x80) { 785 snprintf(buf, sizeof(buf), "<e:property><%s>", v->name); 786 str = strcat_str(str, len, &tmplen, buf); 787 //printf("<e:property><s:%s>", v->name); 788 switch(v->ieventvalue) { 789 case 0: 790 break; 791 case 255: /* Magical values should go around here */ 792 if( strcmp(v->name, "SystemUpdateID") == 0 ) 793 { 794 snprintf(buf, sizeof(buf), "%d", updateID); 795 str = strcat_str(str, len, &tmplen, buf); 796 } 797 break; 798 default: 799 str = strcat_str(str, len, &tmplen, upnpallowedvalues[v->ieventvalue]); 800 //printf("%s", upnpallowedvalues[v->ieventvalue]); 801 } 802 snprintf(buf, sizeof(buf), "</%s></e:property>", v->name); 803 str = strcat_str(str, len, &tmplen, buf); 804 //printf("</s:%s></e:property>\n", v->name); 805 } 806 v++; 807 } 808 str = strcat_str(str, len, &tmplen, "</e:propertyset>"); 809 //printf("</e:propertyset>\n"); 810 //printf("\n"); 811 //printf("%d\n", tmplen); 812 str[*len] = '\0'; 813 return str; 814} 815 816char * 817getVarsContentDirectory(int * l) 818{ 819 return genEventVars(l, 820 &scpdContentDirectory, 821 "urn:schemas-upnp-org:service:ContentDirectory:1"); 822} 823 824char * 825getVarsConnectionManager(int * l) 826{ 827 return genEventVars(l, 828 &scpdConnectionManager, 829 "urn:schemas-upnp-org:service:ConnectionManager:1"); 830} 831 832char * 833getVarsX_MS_MediaReceiverRegistrar(int * l) 834{ 835 return genEventVars(l, 836 &scpdX_MS_MediaReceiverRegistrar, 837 "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1"); 838} 839 840