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