"Fossies" - the Fresh Open Source Software Archive

Member "minidlna-1.3.0/upnpdescgen.c" (24 Nov 2020, 21083 Bytes) of package /linux/privat/minidlna-1.3.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "upnpdescgen.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.2.1_vs_1.3.0.

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