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