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