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