"Fossies" - the Fresh Open Source Software Archive

Member "minidlna-1.3.0/upnpevents.c" (24 Nov 2020, 12772 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 "upnpevents.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 /* MiniDLNA project
    2  * http://minidlna.sourceforge.net/
    3  *
    4  * MiniDLNA media server
    5  * Copyright (C) 2008-2009  Justin Maggard
    6  *
    7  * This file is part of MiniDLNA.
    8  *
    9  * MiniDLNA is free software; you can redistribute it and/or modify
   10  * it under the terms of the GNU General Public License version 2 as
   11  * published by the Free Software Foundation.
   12  *
   13  * MiniDLNA is distributed in the hope that it will be useful,
   14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   16  * GNU General Public License for more details.
   17  *
   18  * You should have received a copy of the GNU General Public License
   19  * along with MiniDLNA. If not, see <http://www.gnu.org/licenses/>.
   20  *
   21  * Portions of the code from the MiniUPnP project:
   22  *
   23  * Copyright (c) 2006-2007, Thomas Bernard
   24  * All rights reserved.
   25  *
   26  * Redistribution and use in source and binary forms, with or without
   27  * modification, are permitted provided that the following conditions are met:
   28  *     * Redistributions of source code must retain the above copyright
   29  *       notice, this list of conditions and the following disclaimer.
   30  *     * Redistributions in binary form must reproduce the above copyright
   31  *       notice, this list of conditions and the following disclaimer in the
   32  *       documentation and/or other materials provided with the distribution.
   33  *     * The name of the author may not be used to endorse or promote products
   34  *       derived from this software without specific prior written permission.
   35  *
   36  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   37  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   38  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   39  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
   40  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   41  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   42  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   43  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   44  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   45  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   46  * POSSIBILITY OF SUCH DAMAGE.
   47  */
   48 #include "config.h"
   49 
   50 #include <stdio.h>
   51 #include <string.h>
   52 #include <errno.h>
   53 #include <sys/queue.h>
   54 #include <stdlib.h>
   55 #include <unistd.h>
   56 #include <time.h>
   57 #include <sys/types.h>
   58 #include <sys/socket.h>
   59 #include <sys/param.h>
   60 #include <netinet/in.h>
   61 #include <arpa/inet.h>
   62 #include <assert.h>
   63 #include <fcntl.h>
   64 #include <errno.h>
   65 
   66 #include "event.h"
   67 #include "upnpevents.h"
   68 #include "minidlnapath.h"
   69 #include "upnpglobalvars.h"
   70 #include "upnpdescgen.h"
   71 #include "uuid.h"
   72 #include "utils.h"
   73 #include "log.h"
   74 
   75 /* stuctures definitions */
   76 struct subscriber {
   77     LIST_ENTRY(subscriber) entries;
   78     struct upnp_event_notify * notify;
   79     time_t timeout;
   80     uint32_t seq;
   81     enum subscriber_service_enum service;
   82     char uuid[42];
   83     char callback[];
   84 };
   85 
   86 struct upnp_event_notify {
   87     struct event ev;
   88     LIST_ENTRY(upnp_event_notify) entries;
   89     enum { EConnecting,
   90            ESending,
   91            EWaitingForResponse,
   92            EFinished,
   93            EError } state;
   94     struct subscriber * sub;
   95     char * buffer;
   96     int buffersize;
   97     int tosend;
   98     int sent;
   99     const char * path;
  100     char addrstr[16];
  101     char portstr[8];
  102 };
  103 
  104 /* prototypes */
  105 static void upnp_event_create_notify(struct subscriber * sub);
  106 static void upnp_event_process_notify(struct event *ev);
  107 
  108 /* Subscriber list */
  109 LIST_HEAD(listhead, subscriber) subscriberlist = { NULL };
  110 
  111 /* notify list */
  112 LIST_HEAD(listheadnotif, upnp_event_notify) notifylist = { NULL };
  113 
  114 #define MAX_SUBSCRIBERS 500
  115 static uint16_t nsubscribers = 0;
  116 
  117 /* create a new subscriber */
  118 static struct subscriber *
  119 newSubscriber(const char * eventurl, const char * callback, int callbacklen)
  120 {
  121     struct subscriber * tmp;
  122     if(!eventurl || !callback || !callbacklen)
  123         return NULL;
  124     tmp = calloc(1, sizeof(struct subscriber)+callbacklen+1);
  125     if(strcmp(eventurl, CONTENTDIRECTORY_EVENTURL)==0)
  126         tmp->service = EContentDirectory;
  127     else if(strcmp(eventurl, CONNECTIONMGR_EVENTURL)==0)
  128         tmp->service = EConnectionManager;
  129     else if(strcmp(eventurl, X_MS_MEDIARECEIVERREGISTRAR_EVENTURL)==0)
  130         tmp->service = EMSMediaReceiverRegistrar;
  131     else {
  132         free(tmp);
  133         return NULL;
  134     }
  135     memcpy(tmp->callback, callback, callbacklen);
  136     tmp->callback[callbacklen] = '\0';
  137     /* make a dummy uuid */
  138     strncpyt(tmp->uuid, uuidvalue, sizeof(tmp->uuid));
  139     if( get_uuid_string(tmp->uuid+5) != 0 )
  140     {
  141         tmp->uuid[sizeof(tmp->uuid)-1] = '\0';
  142         snprintf(tmp->uuid+37, 5, "%04lx", random() & 0xffff);
  143     }
  144 
  145     return tmp;
  146 }
  147 
  148 /* creates a new subscriber and adds it to the subscriber list
  149  * also initiate 1st notify */
  150 const char *
  151 upnpevents_addSubscriber(const char * eventurl,
  152                          const char * callback, int callbacklen,
  153                          int timeout)
  154 {
  155     struct subscriber * tmp;
  156     DPRINTF(E_DEBUG, L_HTTP, "addSubscriber(%s, %.*s, %d)\n",
  157            eventurl, callbacklen, callback, timeout);
  158     if (nsubscribers >= MAX_SUBSCRIBERS)
  159         return NULL;
  160     tmp = newSubscriber(eventurl, callback, callbacklen);
  161     if(!tmp)
  162         return NULL;
  163     if(timeout)
  164         tmp->timeout = time(NULL) + timeout;
  165     LIST_INSERT_HEAD(&subscriberlist, tmp, entries);
  166     nsubscribers++;
  167     upnp_event_create_notify(tmp);
  168     return tmp->uuid;
  169 }
  170 
  171 /* renew a subscription (update the timeout) */
  172 int
  173 renewSubscription(const char * sid, int sidlen, int timeout)
  174 {
  175     struct subscriber * sub;
  176     for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) {
  177         if(memcmp(sid, sub->uuid, 41) == 0) {
  178             sub->timeout = (timeout ? time(NULL) + timeout : 0);
  179             return 0;
  180         }
  181     }
  182     return -1;
  183 }
  184 
  185 int
  186 upnpevents_removeSubscriber(const char * sid, int sidlen)
  187 {
  188     struct subscriber * sub;
  189     if(!sid)
  190         return -1;
  191     DPRINTF(E_DEBUG, L_HTTP, "removeSubscriber(%.*s)\n",
  192            sidlen, sid);
  193     for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) {
  194         if(memcmp(sid, sub->uuid, 41) == 0) {
  195             if(sub->notify) {
  196                 sub->notify->sub = NULL;
  197             }
  198             LIST_REMOVE(sub, entries);
  199             nsubscribers--;
  200             free(sub);
  201             return 0;
  202         }
  203     }
  204     return -1;
  205 }
  206 
  207 void
  208 upnpevents_removeSubscribers(void)
  209 {
  210     struct subscriber * sub;
  211 
  212     for(sub = subscriberlist.lh_first; sub != NULL; sub = subscriberlist.lh_first) {
  213         upnpevents_removeSubscriber(sub->uuid, sizeof(sub->uuid));
  214     }
  215 }
  216 
  217 /* notifies all subscribers of a SystemUpdateID change */
  218 void
  219 upnp_event_var_change_notify(enum subscriber_service_enum service)
  220 {
  221     struct subscriber * sub;
  222     for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) {
  223         if(sub->service == service && sub->notify == NULL)
  224             upnp_event_create_notify(sub);
  225     }
  226 }
  227 
  228 /* create and add the notify object to the list, start connecting */
  229 static void
  230 upnp_event_create_notify(struct subscriber *sub)
  231 {
  232     struct upnp_event_notify * obj;
  233     int flags, s, i;
  234     const char *p;
  235     unsigned short port;
  236     struct sockaddr_in addr;
  237 
  238     assert(sub);
  239 
  240     obj = calloc(1, sizeof(struct upnp_event_notify));
  241     if(!obj) {
  242         DPRINTF(E_ERROR, L_HTTP, "%s: calloc(): %s\n", "upnp_event_create_notify", strerror(errno));
  243         return;
  244     }
  245     obj->sub = sub;
  246     s = socket(PF_INET, SOCK_STREAM, 0);
  247     if(s < 0) {
  248         DPRINTF(E_ERROR, L_HTTP, "%s: socket(): %s\n", "upnp_event_create_notify", strerror(errno));
  249         goto error;
  250     }
  251     if((flags = fcntl(s, F_GETFL, 0)) < 0) {
  252         DPRINTF(E_ERROR, L_HTTP, "%s: fcntl(..F_GETFL..): %s\n",
  253                "upnp_event_create_notify", strerror(errno));
  254         goto error;
  255     }
  256     if(fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0) {
  257         DPRINTF(E_ERROR, L_HTTP, "%s: fcntl(..F_SETFL..): %s\n",
  258                "upnp_event_create_notify", strerror(errno));
  259         goto error;
  260     }
  261     if(sub)
  262         sub->notify = obj;
  263     LIST_INSERT_HEAD(&notifylist, obj, entries);
  264 
  265     memset(&addr, 0, sizeof(addr));
  266     i = 0;
  267     p = obj->sub->callback;
  268     p += 7; /* http:// */
  269     while(*p != '/' && *p != ':' && i < (sizeof(obj->addrstr)-1))
  270         obj->addrstr[i++] = *(p++);
  271     obj->addrstr[i] = '\0';
  272     if(*p == ':') {
  273         obj->portstr[0] = *p;
  274         i = 1;
  275         p++;
  276         port = (unsigned short)atoi(p);
  277         while(*p != '/' && *p != '\0') {
  278             if(i<7) obj->portstr[i++] = *p;
  279             p++;
  280         }
  281         obj->portstr[i] = 0;
  282     } else {
  283         port = 80;
  284         obj->portstr[0] = '\0';
  285     }
  286     if( *p )
  287         obj->path = p;
  288     else
  289         obj->path = "/";
  290     addr.sin_family = AF_INET;
  291     inet_aton(obj->addrstr, &addr.sin_addr);
  292     addr.sin_port = htons(port);
  293     DPRINTF(E_DEBUG, L_HTTP, "%s: '%s' %hu '%s'\n", "upnp_event_notify_connect",
  294            obj->addrstr, port, obj->path);
  295     obj->state = EConnecting;
  296     if(connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
  297         if(errno != EINPROGRESS && errno != EWOULDBLOCK) {
  298             DPRINTF(E_ERROR, L_HTTP, "%s: connect(): %s\n", "upnp_event_notify_connect", strerror(errno));
  299             obj->state = EError;
  300         }
  301     } else {
  302         obj->ev = (struct event ){ .fd = s, .rdwr = EVENT_WRITE,
  303             .process = upnp_event_process_notify, .data = obj };
  304         event_module.add(&obj->ev);
  305     }
  306 
  307     return;
  308 
  309 error:
  310     if(s >= 0)
  311         close(s);
  312     free(obj);
  313 }
  314 
  315 static void upnp_event_prepare(struct upnp_event_notify * obj)
  316 {
  317     static const char notifymsg[] = 
  318         "NOTIFY %s HTTP/1.1\r\n"
  319         "Host: %s%s\r\n"
  320         "Content-Type: text/xml; charset=\"utf-8\"\r\n"
  321         "Content-Length: %d\r\n"
  322         "NT: upnp:event\r\n"
  323         "NTS: upnp:propchange\r\n"
  324         "SID: %s\r\n"
  325         "SEQ: %u\r\n"
  326         "Connection: close\r\n"
  327         "Cache-Control: no-cache\r\n"
  328         "\r\n"
  329         "%.*s\r\n";
  330     char * xml;
  331     int l;
  332 
  333     assert(obj->sub);
  334 
  335     switch(obj->sub->service) {
  336     case EContentDirectory:
  337         xml = getVarsContentDirectory(&l);
  338         break;
  339     case EConnectionManager:
  340         xml = getVarsConnectionManager(&l);
  341         break;
  342     case EMSMediaReceiverRegistrar:
  343         xml = getVarsX_MS_MediaReceiverRegistrar(&l);
  344         break;
  345     default:
  346         xml = NULL;
  347         l = 0;
  348     }
  349     obj->tosend = asprintf(&(obj->buffer), notifymsg,
  350                            obj->path, obj->addrstr, obj->portstr, l+2,
  351                            obj->sub->uuid, obj->sub->seq,
  352                            l, xml);
  353     obj->buffersize = obj->tosend;
  354     free(xml);
  355     DPRINTF(E_DEBUG, L_HTTP, "Sending UPnP Event response:\n%s\n", obj->buffer);
  356     obj->state = ESending;
  357 }
  358 
  359 static void upnp_event_send(struct upnp_event_notify * obj)
  360 {
  361     int i;
  362     //DEBUG DPRINTF(E_DEBUG, L_HTTP, "Sending UPnP Event:\n%s", obj->buffer+obj->sent);
  363     while( obj->sent < obj->tosend ) {
  364         i = send(obj->ev.fd, obj->buffer + obj->sent, obj->tosend - obj->sent, 0);
  365         if(i<0) {
  366             DPRINTF(E_WARN, L_HTTP, "%s: send(): %s\n", "upnp_event_send", strerror(errno));
  367             obj->state = EError;
  368             event_module.del(&obj->ev, 0);
  369             return;
  370         }
  371         obj->sent += i;
  372     }
  373     if(obj->sent == obj->tosend) {
  374         obj->state = EWaitingForResponse;
  375         event_module.del(&obj->ev, 0);
  376         obj->ev.rdwr = EVENT_READ;
  377         event_module.add(&obj->ev);
  378     }
  379 }
  380 
  381 static void upnp_event_recv(struct upnp_event_notify * obj)
  382 {
  383     int n;
  384     n = recv(obj->ev.fd, obj->buffer, obj->buffersize, 0);
  385     if(n<0) {
  386         DPRINTF(E_ERROR, L_HTTP, "%s: recv(): %s\n", "upnp_event_recv", strerror(errno));
  387         obj->state = EError;
  388         event_module.del(&obj->ev, 0);
  389         return;
  390     }
  391     DPRINTF(E_DEBUG, L_HTTP, "%s: (%dbytes) %.*s\n", "upnp_event_recv",
  392            n, n, obj->buffer);
  393     obj->state = EFinished;
  394     event_module.del(&obj->ev, 0);
  395     if(obj->sub)
  396     {
  397         obj->sub->seq++;
  398         if (!obj->sub->seq)
  399             obj->sub->seq++;
  400     }
  401 }
  402 
  403 static void
  404 upnp_event_process_notify(struct event *ev)
  405 {
  406     struct upnp_event_notify *obj = ev->data;
  407 
  408     switch(obj->state) {
  409     case EConnecting:
  410         /* now connected or failed to connect */
  411         upnp_event_prepare(obj);
  412         upnp_event_send(obj);
  413         break;
  414     case ESending:
  415         upnp_event_send(obj);
  416         break;
  417     case EWaitingForResponse:
  418         upnp_event_recv(obj);
  419         break;
  420     case EFinished:
  421         close(obj->ev.fd);
  422         obj->ev.fd = -1;
  423         break;
  424     default:
  425         DPRINTF(E_ERROR, L_HTTP, "upnp_event_process_notify: unknown state\n");
  426     }
  427 }
  428 
  429 void upnpevents_gc(void)
  430 {
  431     struct upnp_event_notify * obj;
  432     struct upnp_event_notify * next;
  433     struct subscriber * sub;
  434     struct subscriber * subnext;
  435     time_t curtime;
  436 
  437     obj = notifylist.lh_first;
  438     while(obj != NULL) {
  439         next = obj->entries.le_next;
  440         if(obj->state == EError || obj->state == EFinished) {
  441             if(obj->ev.fd >= 0) {
  442                 close(obj->ev.fd);
  443             }
  444             if(obj->sub)
  445                 obj->sub->notify = NULL;
  446             /* remove also the subscriber from the list if there was an error */
  447             if(obj->state == EError && obj->sub) {
  448                 LIST_REMOVE(obj->sub, entries);
  449                 nsubscribers--;
  450                 free(obj->sub);
  451             }
  452             free(obj->buffer);
  453             LIST_REMOVE(obj, entries);
  454             free(obj);
  455         }
  456         obj = next;
  457     }
  458     /* remove timed-out subscribers */
  459     curtime = time(NULL);
  460     for(sub = subscriberlist.lh_first; sub != NULL; ) {
  461         subnext = sub->entries.le_next;
  462         if(sub->timeout && curtime > sub->timeout && sub->notify == NULL) {
  463             LIST_REMOVE(sub, entries);
  464             nsubscribers--;
  465             free(sub);
  466         }
  467         sub = subnext;
  468     }
  469 }