summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cproject93
-rw-r--r--.dependencies0
-rw-r--r--.project27
-rw-r--r--.settings/language.settings.xml11
-rw-r--r--.settings/org.eclipse.cdt.core.prefs15
-rw-r--r--COPYING340
-rw-r--r--HISTORY6
-rw-r--r--Makefile136
-rw-r--r--README15
-rw-r--r--common/config.cpp31
-rw-r--r--common/config.obin0 -> 65336 bytes
-rw-r--r--common/tools.cpp465
-rw-r--r--common/tools.obin0 -> 579528 bytes
-rw-r--r--httpdocs/images/upnpIconLrg.jpegbin0 -> 6620 bytes
-rw-r--r--httpdocs/images/upnpIconLrg.pngbin0 -> 17309 bytes
-rw-r--r--httpdocs/images/upnpIconSm.jpegbin0 -> 2416 bytes
-rw-r--r--httpdocs/images/upnpIconSm.pngbin0 -> 4769 bytes
-rw-r--r--httptnt/cds_scpd.ecpp152
-rw-r--r--httptnt/cds_scpd.obin0 -> 206016 bytes
-rw-r--r--httptnt/cms_scpd.ecpp140
-rw-r--r--httptnt/cms_scpd.obin0 -> 207584 bytes
-rw-r--r--httptnt/deviceDescription.ecpp88
-rw-r--r--httptnt/deviceDescription.obin0 -> 295912 bytes
-rw-r--r--include/config.h126
-rw-r--r--include/connection.h84
-rw-r--r--include/connectionManager.h70
-rw-r--r--include/contentDirectory.h58
-rw-r--r--include/media/mediaManager.h87
-rw-r--r--include/media/profile.h50
-rw-r--r--include/plugin.h484
-rw-r--r--include/plugin.h.txt486
-rw-r--r--include/pluginManager.h20
-rw-r--r--include/server.h110
-rw-r--r--include/service.h108
-rw-r--r--include/tools.h162
-rw-r--r--include/webserver.h66
-rwxr-xr-xlibvdr-upnp.sobin0 -> 1772649 bytes
-rwxr-xr-xlibvdr-upnp.so.1.7.27bin0 -> 1772649 bytes
-rw-r--r--media/mediaManager.cpp212
-rw-r--r--media/mediaManager.obin0 -> 387416 bytes
-rw-r--r--media/pluginManager.cpp262
-rw-r--r--media/pluginManager.obin0 -> 269696 bytes
-rw-r--r--media/profile.cpp18
-rw-r--r--media/profile.obin0 -> 12960 bytes
-rw-r--r--metadata.dbbin0 -> 7168 bytes
-rw-r--r--po/upnp.pot21
-rw-r--r--server/connection.cpp139
-rw-r--r--server/connection.obin0 -> 57880 bytes
-rw-r--r--server/connectionManager.cpp253
-rw-r--r--server/connectionManager.obin0 -> 313200 bytes
-rw-r--r--server/contentDirectory.cpp439
-rw-r--r--server/contentDirectory.obin0 -> 232952 bytes
-rw-r--r--server/server.cpp337
-rw-r--r--server/server.obin0 -> 327792 bytes
-rw-r--r--server/service.cpp117
-rw-r--r--server/service.obin0 -> 84424 bytes
-rw-r--r--server/webserver.cpp166
-rw-r--r--server/webserver.obin0 -> 549288 bytes
-rw-r--r--upnp.cpp90
-rw-r--r--upnp.h41
-rw-r--r--upnp.obin0 -> 52272 bytes
61 files changed, 5525 insertions, 0 deletions
diff --git a/.cproject b/.cproject
new file mode 100644
index 0000000..5dc98fb
--- /dev/null
+++ b/.cproject
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?fileVersion 4.0.0?>
+
+<cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
+ <storageModule moduleId="org.eclipse.cdt.core.settings">
+ <cconfiguration id="cdt.managedbuild.toolchain.gnu.base.1272503944">
+ <storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.toolchain.gnu.base.1272503944" moduleId="org.eclipse.cdt.core.settings" name="Default">
+ <macros>
+ <stringMacro name="LIBDIR" type="VALUE_TEXT" value="."/>
+ <stringMacro name="DVBDIR" type="VALUE_TEXT" value="/usr"/>
+ <stringMacro name="VDRDIR" type="VALUE_TEXT" value="/usr/include/vdr"/>
+ </macros>
+ <externalSettings/>
+ <extensions>
+ <extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
+ <extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+ <extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
+ <extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+ <extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+ <extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+ </extensions>
+ </storageModule>
+ <storageModule moduleId="cdtBuildSystem" version="4.0.0">
+ <configuration artifactName="upnp-0.0.1" buildProperties="" description="" id="cdt.managedbuild.toolchain.gnu.base.1272503944" name="Default" parent="org.eclipse.cdt.build.core.emptycfg">
+ <folderInfo id="cdt.managedbuild.toolchain.gnu.base.1272503944.1521558368" name="/" resourcePath="">
+ <toolChain id="cdt.managedbuild.toolchain.gnu.base.1418950310" name="cdt.managedbuild.toolchain.gnu.base" superClass="cdt.managedbuild.toolchain.gnu.base">
+ <targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF" id="cdt.managedbuild.target.gnu.platform.base.1111565743" name="Debug Platform" osList="linux,hpux,aix,qnx" superClass="cdt.managedbuild.target.gnu.platform.base"/>
+ <builder id="cdt.managedbuild.target.gnu.builder.base.1244499817" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" superClass="cdt.managedbuild.target.gnu.builder.base">
+ <outputEntries>
+ <entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="outputPath" name=""/>
+ </outputEntries>
+ </builder>
+ <tool id="cdt.managedbuild.tool.gnu.archiver.base.951465774" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.base"/>
+ <tool id="cdt.managedbuild.tool.gnu.cpp.compiler.base.233431571" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.base">
+ <option id="gnu.cpp.compiler.option.include.paths.132379647" name="Include paths (-I)" superClass="gnu.cpp.compiler.option.include.paths" valueType="includePath">
+ <listOptionValue builtIn="false" value="/usr/include/vdr"/>
+ </option>
+ <option id="gnu.cpp.compiler.option.preprocessor.def.2084380070" name="Defined symbols (-D)" superClass="gnu.cpp.compiler.option.preprocessor.def" valueType="definedSymbols">
+ <listOptionValue builtIn="false" value="PLUGIN_NAME_I18N=&quot;upnp&quot;"/>
+ </option>
+ <inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.149069592" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
+ </tool>
+ <tool id="cdt.managedbuild.tool.gnu.c.compiler.base.1832963059" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.base">
+ <option id="gnu.c.compiler.option.include.paths.2058507031" superClass="gnu.c.compiler.option.include.paths" valueType="includePath">
+ <listOptionValue builtIn="false" value="/usr/include/vdr"/>
+ </option>
+ <option id="gnu.c.compiler.option.preprocessor.def.symbols.88995935" superClass="gnu.c.compiler.option.preprocessor.def.symbols" valueType="definedSymbols">
+ <listOptionValue builtIn="false" value="PLUGIN_NAME_I18N=&quot;upnp&quot;"/>
+ </option>
+ <inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.2101471239" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
+ </tool>
+ <tool id="cdt.managedbuild.tool.gnu.c.linker.base.712127718" name="GCC C Linker" superClass="cdt.managedbuild.tool.gnu.c.linker.base"/>
+ <tool id="cdt.managedbuild.tool.gnu.cpp.linker.base.574765582" name="GCC C++ Linker" superClass="cdt.managedbuild.tool.gnu.cpp.linker.base">
+ <inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.1854191500" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input">
+ <additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
+ <additionalInput kind="additionalinput" paths="$(LIBS)"/>
+ </inputType>
+ </tool>
+ <tool id="cdt.managedbuild.tool.gnu.assembler.base.1697949684" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.base">
+ <option id="gnu.both.asm.option.include.paths.177029986" superClass="gnu.both.asm.option.include.paths" valueType="includePath">
+ <listOptionValue builtIn="false" value="/usr/include/vdr"/>
+ </option>
+ <inputType id="cdt.managedbuild.tool.gnu.assembler.input.846983754" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
+ </tool>
+ </toolChain>
+ </folderInfo>
+ <sourceEntries>
+ <entry excluding="httpdocs" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/>
+ </sourceEntries>
+ </configuration>
+ </storageModule>
+ <storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
+ </cconfiguration>
+ </storageModule>
+ <storageModule moduleId="cdtBuildSystem" version="4.0.0">
+ <project id="upnp-0.0.1.null.716162678" name="upnp-0.0.1"/>
+ </storageModule>
+ <storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
+ <storageModule moduleId="refreshScope" versionNumber="2">
+ <configuration configurationName="Default">
+ <resource resourceType="PROJECT" workspacePath="/upnp-0.0.1"/>
+ </configuration>
+ </storageModule>
+ <storageModule moduleId="scannerConfiguration">
+ <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+ <scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.base.1272503944;cdt.managedbuild.toolchain.gnu.base.1272503944.1521558368;cdt.managedbuild.tool.gnu.c.compiler.base.1832963059;cdt.managedbuild.tool.gnu.c.compiler.input.2101471239">
+ <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+ </scannerConfigBuildInfo>
+ <scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.base.1272503944;cdt.managedbuild.toolchain.gnu.base.1272503944.1521558368;cdt.managedbuild.tool.gnu.cpp.compiler.base.233431571;cdt.managedbuild.tool.gnu.cpp.compiler.input.149069592">
+ <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+ </scannerConfigBuildInfo>
+ </storageModule>
+</cproject>
diff --git a/.dependencies b/.dependencies
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/.dependencies
diff --git a/.project b/.project
new file mode 100644
index 0000000..150bd2c
--- /dev/null
+++ b/.project
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>upnp-0.0.1</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
+ <triggers>clean,full,incremental,</triggers>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
+ <triggers>full,incremental,</triggers>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.cdt.core.cnature</nature>
+ <nature>org.eclipse.cdt.core.ccnature</nature>
+ <nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
+ <nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
+ </natures>
+</projectDescription>
diff --git a/.settings/language.settings.xml b/.settings/language.settings.xml
new file mode 100644
index 0000000..94880b1
--- /dev/null
+++ b/.settings/language.settings.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project>
+ <configuration id="cdt.managedbuild.toolchain.gnu.base.1272503944" name="Default">
+ <extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
+ <provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
+ <provider class="org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuildCommandParser" id="org.eclipse.cdt.managedbuilder.core.GCCBuildCommandParser" keep-relative-paths="false" name="CDT GCC Build Output Parser" parameter="(gcc)|([gc]\+\+)|(clang)" prefer-non-shared="true"/>
+ <provider-reference id="org.eclipse.cdt.managedbuilder.core.GCCBuiltinSpecsDetector" ref="shared-provider"/>
+ <provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
+ </extension>
+ </configuration>
+</project>
diff --git a/.settings/org.eclipse.cdt.core.prefs b/.settings/org.eclipse.cdt.core.prefs
new file mode 100644
index 0000000..0b971db
--- /dev/null
+++ b/.settings/org.eclipse.cdt.core.prefs
@@ -0,0 +1,15 @@
+eclipse.preferences.version=1
+environment/project/cdt.managedbuild.toolchain.gnu.base.1272503944/DEB_BUILD_OPTIONS/delimiter=\:
+environment/project/cdt.managedbuild.toolchain.gnu.base.1272503944/DEB_BUILD_OPTIONS/operation=append
+environment/project/cdt.managedbuild.toolchain.gnu.base.1272503944/DEB_BUILD_OPTIONS/value=noopt
+environment/project/cdt.managedbuild.toolchain.gnu.base.1272503944/DVBDIR/delimiter=\:
+environment/project/cdt.managedbuild.toolchain.gnu.base.1272503944/DVBDIR/operation=append
+environment/project/cdt.managedbuild.toolchain.gnu.base.1272503944/DVBDIR/value=/usr
+environment/project/cdt.managedbuild.toolchain.gnu.base.1272503944/LIBDIR/delimiter=\:
+environment/project/cdt.managedbuild.toolchain.gnu.base.1272503944/LIBDIR/operation=append
+environment/project/cdt.managedbuild.toolchain.gnu.base.1272503944/LIBDIR/value=.
+environment/project/cdt.managedbuild.toolchain.gnu.base.1272503944/VDRDIR/delimiter=\:
+environment/project/cdt.managedbuild.toolchain.gnu.base.1272503944/VDRDIR/operation=replace
+environment/project/cdt.managedbuild.toolchain.gnu.base.1272503944/VDRDIR/value=/usr/include/vdr
+environment/project/cdt.managedbuild.toolchain.gnu.base.1272503944/append=true
+environment/project/cdt.managedbuild.toolchain.gnu.base.1272503944/appendContributed=true
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..f90922e
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/HISTORY b/HISTORY
new file mode 100644
index 0000000..11f4945
--- /dev/null
+++ b/HISTORY
@@ -0,0 +1,6 @@
+VDR Plugin 'upnp' Revision History
+----------------------------------
+
+2012-07-30: Version 0.0.1
+
+- Initial revision.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..12a5ba1
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,136 @@
+#
+# Makefile for a Video Disk Recorder plugin
+#
+# $Id$
+
+# The official name of this plugin.
+# This name will be used in the '-P...' option of VDR to load the plugin.
+# By default the main source file also carries this name.
+# IMPORTANT: the presence of this macro is important for the Make.config
+# file. So it must be defined, even if it is not used here!
+#
+PLUGIN = upnp
+
+### The version number of this plugin (taken from the main source file):
+
+VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).h | awk '{ print $$6 }' | sed -e 's/[";]//g')
+
+### The C++ compiler and options:
+
+CXX ?= gcc
+ECPPC ?= ecppc
+CXXFLAGS ?= -g -O3 -Wall -Werror=overloaded-virtual -Wno-parentheses
+
+### The directory environment:
+
+VDRDIR ?= ../../..
+LIBDIR ?= ../../lib
+TMPDIR ?= /tmp
+
+### Make sure that necessary options are included:
+
+include $(VDRDIR)/Make.global
+
+### Allow user defined options to overwrite defaults:
+
+-include $(VDRDIR)/Make.config
+
+### The version number of VDR's plugin API (taken from VDR's "config.h"):
+
+APIVERSION = $(shell sed -ne '/define APIVERSION/s/^.*"\(.*\)".*$$/\1/p' $(VDRDIR)/config.h)
+
+### The name of the distribution archive:
+
+ARCHIVE = $(PLUGIN)-$(VERSION)
+PACKAGE = vdr-$(ARCHIVE)
+
+### Includes and Defines (add further entries here):
+
+INCLUDES += -I$(VDRDIR)/include
+
+DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
+
+### The object files (add further files here):
+
+TNTOBJ = httptnt/deviceDescription.o \
+ httptnt/cds_scpd.o \
+ httptnt/cms_scpd.o
+
+OBJS = $(PLUGIN).o \
+ server/server.o \
+ server/connection.o \
+ server/webserver.o \
+ server/service.o \
+ server/connectionManager.o \
+ server/contentDirectory.o \
+ common/config.o \
+ common/tools.o \
+ media/profile.o \
+ media/mediaManager.o \
+ media/pluginManager.o \
+ $(TNTOBJ)
+
+LIBS += -lupnp -lcxxtools -ltntnet -ltntdb
+
+### The main target:
+
+all: libvdr-$(PLUGIN).so i18n
+
+### Implicit rules:
+
+%.o: %.cpp
+ $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $<
+
+%.cpp: %.ecpp
+ $(ECPPC) $(ECPPFLAGS) $(ECPPFLAGS_CPP) $<
+
+### Dependencies:
+
+MAKEDEP = $(CXX) -MM -MG
+DEPFILE = .dependencies
+$(DEPFILE): Makefile
+ @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.cpp) > $@
+
+-include $(DEPFILE)
+
+### Internationalization (I18N):
+
+PODIR = po
+LOCALEDIR = $(VDRDIR)/locale
+I18Npo = $(wildcard $(PODIR)/*.po)
+I18Nmsgs = $(addprefix $(LOCALEDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file))))))
+I18Npot = $(PODIR)/$(PLUGIN).pot
+
+%.mo: %.po
+ msgfmt -c -o $@ $<
+
+$(I18Npot): $(wildcard *.cpp)
+ xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --package-name=vdr-$(PLUGIN) --package-version=$(VERSION) --msgid-bugs-address='<see README>' -o $@ $^
+
+%.po: $(I18Npot)
+ msgmerge -U --no-wrap --no-location --backup=none -q $@ $<
+ @touch $@
+
+$(I18Nmsgs): $(LOCALEDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo
+ @mkdir -p $(dir $@)
+ cp $< $@
+
+.PHONY: i18n
+i18n: $(I18Nmsgs) $(I18Npot)
+
+### Targets:
+
+libvdr-$(PLUGIN).so: $(OBJS)
+ $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(OBJS) $(LIBS) -o $@
+ @cp --remove-destination $@ $(LIBDIR)/$@.$(APIVERSION)
+
+dist: $(I18Npo) clean
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @mkdir $(TMPDIR)/$(ARCHIVE)
+ @cp -a * $(TMPDIR)/$(ARCHIVE)
+ @tar czf $(PACKAGE).tgz -C $(TMPDIR) --exclude debian --exclude CVS --exclude .svn $(ARCHIVE)
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @echo Distribution package created as $(PACKAGE).tgz
+
+clean:
+ @-rm -f $(OBJS) $(DEPFILE) *.so *.so.$(APIVERSION) *.tgz core* *~ $(PODIR)/*.mo $(PODIR)/*.pot
diff --git a/README b/README
new file mode 100644
index 0000000..241fe06
--- /dev/null
+++ b/README
@@ -0,0 +1,15 @@
+This is a "plugin" for the Video Disk Recorder (VDR).
+
+Written by: Your Name <email@host.dom>
+
+Project's homepage: URL
+
+Latest version available at: URL
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+See the file COPYING for more information.
+
+Description:
diff --git a/common/config.cpp b/common/config.cpp
new file mode 100644
index 0000000..ff24976
--- /dev/null
+++ b/common/config.cpp
@@ -0,0 +1,31 @@
+/*
+ * config.cpp
+ *
+ * Created on: 05.08.2012
+ * Author: savop
+ */
+
+
+#include "../include/config.h"
+#include "../include/tools.h"
+#include "../upnp.h"
+
+using namespace upnp;
+
+upnp::cConfig::cConfig()
+: enabled(true)
+, expertSettings(false)
+, useInternalWebserver(false)
+, webServerPort(0)
+, presentationURL("index.html")
+, maxContentLength(KB(20))
+, announceMaxAge(1800)
+, deviceUUID(tools::GenerateUUIDRandomly())
+, serviceURL("services/")
+, staticContentURL("http/")
+, bindToAddress(true)
+, address("0.0.0.0")
+, port(0)
+, databaseFile("metadata.db")
+{
+}
diff --git a/common/config.o b/common/config.o
new file mode 100644
index 0000000..d1fa07b
--- /dev/null
+++ b/common/config.o
Binary files differ
diff --git a/common/tools.cpp b/common/tools.cpp
new file mode 100644
index 0000000..f519276
--- /dev/null
+++ b/common/tools.cpp
@@ -0,0 +1,465 @@
+/*
+ * tools.cpp
+ *
+ * Created on: 05.08.2012
+ * Author: savop
+ */
+
+#include "../include/tools.h"
+#include <sstream>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/ioctl.h>
+#include <boost/uuid/uuid_generators.hpp>
+#include <boost/uuid/uuid.hpp>
+#include <boost/uuid/uuid_io.hpp>
+
+using namespace std;
+
+char* substr(const char* str, unsigned int offset, unsigned int length){
+ if(offset > strlen(str)) return NULL;
+ if(length > strlen(str+offset)) length = strlen(str+offset);
+ char* substring = (char*)malloc(sizeof(substring)*length+1);
+ strncpy(substring, str+offset, length);
+ substring[length] = '\0';
+ return substring;
+}
+
+namespace upnp {
+
+namespace tools {
+
+string GetAddressByInterface(string Interface){
+ string address;
+
+ if(!Interface.empty()){
+ int fd;
+ struct ifreq ifr;
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ /* I want to get an IPv4 IP address */
+ ifr.ifr_addr.sa_family = AF_INET;
+ /* I want IP address attached to "eth0" */
+ strncpy(ifr.ifr_name, Interface.c_str(), IFNAMSIZ-1);
+ int ret = ioctl(fd, SIOCGIFADDR, &ifr);
+ close(fd);
+ if(ret==0){
+ sockaddr_in* inAddr = (sockaddr_in*)&ifr.ifr_addr;
+ address = inet_ntoa(inAddr->sin_addr);
+ }
+ }
+
+ return address;
+}
+string GetNetworkInterfaceByIndex(int Index, bool skipLoop){
+ return GetNetworkInterfaces(skipLoop)[Index];
+}
+
+vector<string> GetNetworkInterfaces(bool skipLoop){
+ vector<string> interfaces;
+
+ int fd;
+ struct ifconf ifc;
+ struct ifreq ifr[10];
+ int nifaces, i;
+
+ memset(&ifc,0,sizeof(ifc));
+ ifc.ifc_buf = (char*) (ifr);
+ ifc.ifc_len = sizeof(ifr);
+
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ int ret = ioctl(fd, SIOCGIFCONF, &ifc);
+ close(fd);
+ if(ret==0){
+ nifaces = ifc.ifc_len/sizeof(struct ifreq);
+ for(i = 0; i < nifaces; i++){
+ if(skipLoop && strcmp("lo", ifr[i].ifr_name)==0)
+ continue;
+ else
+ interfaces.push_back(ifr[i].ifr_name);
+ }
+ }
+
+ return interfaces;
+}
+
+string ToString(long number){
+ stringstream ss;
+ ss << number;
+ return ss.str();
+}
+
+string StringListToCSV(StringList list){
+ stringstream ss;
+
+ if(list.empty()) return string();
+
+ StringList::iterator it = list.begin();
+
+ ss << (*it);
+ for(++it; it != list.end(); ++it){
+ ss << "," << (*it);
+ }
+
+ return ss.str();
+}
+
+string IdListToCSV(IdList list){
+ stringstream ss;
+
+ if(list.empty()) return string();
+
+ IdList::iterator it = list.begin();
+
+ ss << (*it).first << "," << (*it).second;
+ for(++it; it != list.end(); ++it){
+ ss << "," << (*it).first << "," << (*it).second;
+ }
+
+ return ss.str();
+}
+
+string GenerateUUIDFromURL(string url){
+ boost::uuids::string_generator gen;
+ boost::uuids::uuid urlNamespace = gen(L"6ba7b811-9dad-11d1-80b4-00c04fd430c8");
+ stringstream uuid;
+
+ uuid << boost::uuids::name_generator(urlNamespace)(url);
+
+ return uuid.str();
+}
+
+string GenerateUUIDRandomly(){
+ stringstream uuid;
+
+ uuid << boost::uuids::random_generator()();
+
+ return uuid.str();
+}
+
+} // namespace tools
+
+namespace ixml {
+
+void XmlEscapeSpecialChars(string& doc){
+ std::string buffer;
+
+ buffer.reserve(doc.size()*1.1);
+ for(unsigned int i = 0; i < doc.size(); i++){
+ switch((long)doc[i]){
+ case L'€': buffer.append("&euro;"); break;
+ case L'"': buffer.append("&quot;"); break;
+ case L'&': buffer.append("&amp;"); break;
+ case L'<': buffer.append("&lt;"); break;
+ case L'>': buffer.append("&gt;"); break;
+ case L'¡': buffer.append("&iexcl;"); break;
+ case L'¢': buffer.append("&cent;"); break;
+ case L'£': buffer.append("&pound;"); break;
+ case L'¤': buffer.append("&curren;"); break;
+ case L'¥': buffer.append("&yen;"); break;
+ case L'¦': buffer.append("&brvbar;"); break;
+ case L'§': buffer.append("&sect;"); break;
+ case L'¨': buffer.append("&uml;"); break;
+ case L'©': buffer.append("&copy;"); break;
+ case L'ª': buffer.append("&ordf;"); break;
+ case L'¬': buffer.append("&not;"); break;
+ case L'­': buffer.append("&shy;"); break;
+ case L'®': buffer.append("&reg;"); break;
+ case L'¯': buffer.append("&macr;"); break;
+ case L'°': buffer.append("&deg;"); break;
+ case L'±': buffer.append("&plusmn;"); break;
+ case L'²': buffer.append("&sup2;"); break;
+ case L'³': buffer.append("&sup3;"); break;
+ case L'´': buffer.append("&acute;"); break;
+ case L'µ': buffer.append("&micro;"); break;
+ case L'¶': buffer.append("&para;"); break;
+ case L'·': buffer.append("&middot;"); break;
+ case L'¸': buffer.append("&cedil;"); break;
+ case L'¹': buffer.append("&sup1;"); break;
+ case L'º': buffer.append("&ordm;"); break;
+ case L'»': buffer.append("&raquo;"); break;
+ case L'«': buffer.append("&laquo;"); break;
+ case L'¼': buffer.append("&frac14;"); break;
+ case L'½': buffer.append("&frac12;"); break;
+ case L'¾': buffer.append("&frac34;"); break;
+ case L'¿': buffer.append("&iquest;"); break;
+ case L'À': buffer.append("&Agrave;"); break;
+ case L'Á': buffer.append("&Aacute;"); break;
+ case L'Â': buffer.append("&Acirc;"); break;
+ case L'Ã': buffer.append("&Atilde;"); break;
+ case L'Ä': buffer.append("&Auml;"); break;
+ case L'Å': buffer.append("&Aring;"); break;
+ case L'Æ': buffer.append("&AElig;"); break;
+ case L'Ç': buffer.append("&Ccedil;"); break;
+ case L'È': buffer.append("&Egrave;"); break;
+ case L'É': buffer.append("&Eacute;"); break;
+ case L'Ê': buffer.append("&Ecirc;"); break;
+ case L'Ë': buffer.append("&Euml;"); break;
+ case L'Ì': buffer.append("&Igrave;"); break;
+ case L'Í': buffer.append("&Iacute;"); break;
+ case L'Î': buffer.append("&Icirc;"); break;
+ case L'Ï': buffer.append("&Iuml;"); break;
+ case L'Ð': buffer.append("&ETH;"); break;
+ case L'Ñ': buffer.append("&Ntilde;"); break;
+ case L'Ò': buffer.append("&Ograve;"); break;
+ case L'Ó': buffer.append("&Oacute;"); break;
+ case L'Ô': buffer.append("&Ocirc;"); break;
+ case L'Õ': buffer.append("&Otilde;"); break;
+ case L'Ö': buffer.append("&Ouml;"); break;
+ case L'×': buffer.append("&times;"); break;
+ case L'Ø': buffer.append("&Oslash;"); break;
+ case L'Ù': buffer.append("&Ugrave;"); break;
+ case L'Ú': buffer.append("&Uacute;"); break;
+ case L'Û': buffer.append("&Ucirc;"); break;
+ case L'Ü': buffer.append("&Uuml;"); break;
+ case L'Ý': buffer.append("&Yacute;"); break;
+ case L'Þ': buffer.append("&THORN;"); break;
+ case L'ß': buffer.append("&szlig;"); break;
+ case L'à': buffer.append("&agrave;"); break;
+ case L'á': buffer.append("&aacute;"); break;
+ case L'â': buffer.append("&acirc;"); break;
+ case L'ã': buffer.append("&atilde;"); break;
+ case L'ä': buffer.append("&auml;"); break;
+ case L'å': buffer.append("&aring;"); break;
+ case L'æ': buffer.append("&aelig;"); break;
+ case L'ç': buffer.append("&ccedil;"); break;
+ case L'è': buffer.append("&egrave;"); break;
+ case L'é': buffer.append("&eacute;"); break;
+ case L'ê': buffer.append("&ecirc;"); break;
+ case L'ë': buffer.append("&euml;"); break;
+ case L'ì': buffer.append("&igrave;"); break;
+ case L'í': buffer.append("&iacute;"); break;
+ case L'î': buffer.append("&icirc;"); break;
+ case L'ï': buffer.append("&iuml;"); break;
+ case L'ð': buffer.append("&eth;"); break;
+ case L'ñ': buffer.append("&ntilde;"); break;
+ case L'ò': buffer.append("&ograve;"); break;
+ case L'ó': buffer.append("&oacute;"); break;
+ case L'ô': buffer.append("&ocirc;"); break;
+ case L'õ': buffer.append("&otilde;"); break;
+ case L'ö': buffer.append("&ouml;"); break;
+ case L'÷': buffer.append("&divide;"); break;
+ case L'ø': buffer.append("&oslash;"); break;
+ case L'ù': buffer.append("&ugrave;"); break;
+ case L'ú': buffer.append("&uacute;"); break;
+ case L'û': buffer.append("&ucirc;"); break;
+ case L'ü': buffer.append("&uuml;"); break;
+ case L'ý': buffer.append("&yacute;"); break;
+ case L'þ': buffer.append("&thorn;"); break;
+ default: buffer.append(1, doc[i]); break;
+ }
+ }
+
+ doc.swap(buffer);
+}
+
+//Function copied from Intel SDK
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2000-2003 Intel Corporation
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+// * Neither name of Intel Corporation nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+///////////////////////////////////////////////////////////////////////////
+/********************************************************************************
+ * SampleUtil_GetFirstDocumentItem
+ *
+ * Description:
+ * Given a document node, this routine searches for the first element
+ * named by the input string item, and returns its value as a string.
+ * String must be freed by caller using free.
+ * Parameters:
+ * doc -- The DOM document from which to extract the value
+ * item -- The item to search for
+ *
+ *
+ ********************************************************************************/
+
+int IxmlGetFirstDocumentItem( IN IXML_Document * doc, IN std::string item, std::string& value ) {
+ IXML_NodeList *nodeList = NULL;
+ IXML_Node *textNode = NULL;
+ IXML_Node *tmpNode = NULL;
+
+ int error = 0;
+
+ nodeList = ixmlDocument_getElementsByTagName( doc, item.c_str() );
+
+ if( nodeList != NULL ) {
+ if( ( tmpNode = ixmlNodeList_item( nodeList, 0 ) ) ) {
+
+ textNode = ixmlNode_getFirstChild( tmpNode );
+
+ if(textNode != NULL){
+ value = ixmlNode_getNodeValue( textNode );
+ }
+ }
+ } else {
+ error = -1;
+ }
+
+ if( nodeList != NULL) {
+ ixmlNodeList_free( nodeList );
+ }
+
+
+ return error;
+}
+
+//TODO const char* mit std::string ersetzen
+IXML_Element* IxmlAddProperty(IXML_Document* document, IXML_Element* node, const char* upnpproperty, const char* value){
+ if(!node) return NULL;
+ IXML_Element* PropertyNode = NULL;
+
+ //TODO Bug #887
+ char tvalue[MAX_METADATA_LENGTH];
+ // trim the value to max metadata size
+ if(value){
+ strncpy(tvalue, value, MAX_METADATA_LENGTH);
+ }
+
+ const char* attribute = att(upnpproperty);
+ const char* property = prop(upnpproperty);
+ if(attribute){
+ if(!strcmp(property,"")){
+ if(ixmlElement_setAttribute(node, attribute, tvalue)!=IXML_SUCCESS){
+ return NULL;
+ }
+ }
+ else {
+ IXML_NodeList* NodeList = ixmlElement_getElementsByTagName(node, property);
+ if(NodeList!=NULL){
+ PropertyNode = (IXML_Element*) ixmlNodeList_item(NodeList, 0);
+ if(PropertyNode){
+ if(ixmlElement_setAttribute(PropertyNode, attribute, tvalue)!=IXML_SUCCESS){
+ return NULL;
+ }
+ }
+ else {
+ ixmlNodeList_free(NodeList);
+ return NULL;
+ }
+ }
+ else {
+ return NULL;
+ }
+ }
+ }
+ else {
+ PropertyNode = ixmlDocument_createElement(document, property);
+ IXML_Node* PropertyText = ixmlDocument_createTextNode(document, tvalue);
+ ixmlNode_appendChild((IXML_Node*) PropertyNode, PropertyText);
+ ixmlNode_appendChild((IXML_Node*) node, (IXML_Node*) PropertyNode);
+ }
+ return PropertyNode;
+}
+
+//TODO const char* mit std::string ersetzen
+IXML_Element* IxmlAddFilteredProperty(cStringList* Filter, IXML_Document* document, IXML_Element* node, const char* upnpproperty, const char* value){
+ // leave out empty values.
+ if(!value || !strcmp(value, "") || !strcmp(value, "0")){
+ return NULL;
+ }
+
+ if(!Filter || Filter->Find(upnpproperty))
+ return IxmlAddProperty(document, node, upnpproperty, value);
+ else
+ return NULL;
+}
+
+//TODO const char* mit std::string ersetzen
+IXML_Element* IxmlReplaceProperty(IXML_Document* document, IXML_Element* node, const char* upnpproperty, const char* newValue){
+ if(!node) return NULL;
+ IXML_Element* PropertyNode = NULL;
+
+ //TODO Bug #887
+ char tvalue[MAX_METADATA_LENGTH];
+ // trim the value to max metadata size
+ if(newValue){
+ strncpy(tvalue, newValue, MAX_METADATA_LENGTH);
+ }
+
+ const char* attribute = att(upnpproperty);
+ const char* property = prop(upnpproperty);
+ if(attribute){
+ if(!strcmp(property,"")){
+ if(newValue){
+ if(ixmlElement_setAttribute(node, attribute, tvalue)!=IXML_SUCCESS){
+ return NULL;
+ }
+ }
+ else {
+ ixmlElement_removeAttribute(node, attribute);
+ }
+ }
+ else {
+ IXML_NodeList* NodeList = ixmlElement_getElementsByTagName(node, property);
+ if(NodeList!=NULL){
+ PropertyNode = (IXML_Element*) ixmlNodeList_item(NodeList, 0);
+ if(PropertyNode){
+ if(newValue){
+ if(ixmlElement_setAttribute(PropertyNode, attribute, tvalue)!=IXML_SUCCESS){
+ return NULL;
+ }
+ }
+ else {
+ ixmlElement_removeAttribute(PropertyNode, attribute);
+ }
+ }
+ else {
+ ixmlNodeList_free(NodeList);
+ return NULL;
+ }
+ }
+ else {
+ return NULL;
+ }
+ }
+ }
+ else {
+ IXML_NodeList* NodeList = ixmlElement_getElementsByTagName(node, property);
+ if(NodeList!=NULL){
+ PropertyNode = (IXML_Element*) ixmlNodeList_item(NodeList, 0);
+ IXML_Node* PropertyText = ixmlNode_getFirstChild((IXML_Node*) PropertyNode);
+
+ if(ixmlNode_removeChild((IXML_Node*) PropertyNode, PropertyText, NULL)!=IXML_SUCCESS){
+ return NULL;
+ }
+ if(newValue){
+ PropertyText = ixmlDocument_createTextNode(document, tvalue);
+ }
+ ixmlNode_appendChild((IXML_Node*) PropertyNode, PropertyText);
+ }
+ else {
+ ixmlNodeList_free(NodeList);
+ return NULL;
+ }
+ }
+ return PropertyNode;
+}
+
+} // namespace ixml
+
+} // namespace upnp
diff --git a/common/tools.o b/common/tools.o
new file mode 100644
index 0000000..d89989e
--- /dev/null
+++ b/common/tools.o
Binary files differ
diff --git a/httpdocs/images/upnpIconLrg.jpeg b/httpdocs/images/upnpIconLrg.jpeg
new file mode 100644
index 0000000..6421267
--- /dev/null
+++ b/httpdocs/images/upnpIconLrg.jpeg
Binary files differ
diff --git a/httpdocs/images/upnpIconLrg.png b/httpdocs/images/upnpIconLrg.png
new file mode 100644
index 0000000..d93e7c0
--- /dev/null
+++ b/httpdocs/images/upnpIconLrg.png
Binary files differ
diff --git a/httpdocs/images/upnpIconSm.jpeg b/httpdocs/images/upnpIconSm.jpeg
new file mode 100644
index 0000000..b163f05
--- /dev/null
+++ b/httpdocs/images/upnpIconSm.jpeg
Binary files differ
diff --git a/httpdocs/images/upnpIconSm.png b/httpdocs/images/upnpIconSm.png
new file mode 100644
index 0000000..d4320f4
--- /dev/null
+++ b/httpdocs/images/upnpIconSm.png
Binary files differ
diff --git a/httptnt/cds_scpd.ecpp b/httptnt/cds_scpd.ecpp
new file mode 100644
index 0000000..73f0baf
--- /dev/null
+++ b/httptnt/cds_scpd.ecpp
@@ -0,0 +1,152 @@
+<?xml version = "1.0" encoding = "utf-8"?>
+<#
+
+This is the Connection Manager Service Description File
+
+It contains the service description for the connection manager of the media server.
+
+#>
+<{ reply.setContentType("application/xml"); }>
+<scpd xmlns="urn:schemas-upnp-org:service-1-0">
+ <specVersion>
+ <major>1</major>
+ <minor>0</minor>
+ </specVersion>
+ <actionList>
+ <action>
+ <name>GetSearchCapabilities</name>
+ <argumentList>
+ <argument>
+ <name>SearchCaps</name>
+ <direction>out</direction>
+ <relatedStateVariable>SearchCapabilities</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>GetSortCapabilities</name>
+ <argumentList>
+ <argument>
+ <name>SortCaps</name>
+ <direction>out</direction>
+ <relatedStateVariable>SortCapabilities</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>GetSystemUpdateID</name>
+ <argumentList>
+ <argument>
+ <name>Id</name>
+ <direction>out</direction>
+ <relatedStateVariable>SystemUpdateID</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>Browse</name>
+ <argumentList>
+ <argument>
+ <name>ObjectID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_ObjectID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>BrowseFlag</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_BrowseFlag</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Filter</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_Filter</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>StartingIndex</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_Index</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>RequestedCount</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_Count</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>SortCriteria</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_SortCriteria</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Result</name>
+ <direction>out</direction>
+ <relatedStateVariable>A_ARG_TYPE_Result</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>NumberReturned</name>
+ <direction>out</direction>
+ <relatedStateVariable>A_ARG_TYPE_Count</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>TotalMatches</name>
+ <direction>out</direction>
+ <relatedStateVariable>A_ARG_TYPE_Count</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>UpdateID</name>
+ <direction>out</direction>
+ <relatedStateVariable>A_ARG_TYPE_UpdateID</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ </actionList>
+ <serviceStateTable>
+ <stateVariable sendEvents="no">
+ <name>A_ARG_TYPE_ObjectID</name>
+ <dataType>string</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>A_ARG_TYPE_Result</name>
+ <dataType>string</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>A_ARG_TYPE_BrowseFlag</name>
+ <dataType>string</dataType>
+ <allowedValueList>
+ <allowedValue>BrowseMetadata</allowedValue>
+ <allowedValue>BrowseDirectChildren</allowedValue>
+ </allowedValueList>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>A_ARG_TYPE_Filter</name>
+ <dataType>string</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>A_ARG_TYPE_SortCriteria</name>
+ <dataType>string</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>A_ARG_TYPE_Index</name>
+ <dataType>ui4</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>A_ARG_TYPE_Count</name>
+ <dataType>ui4</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>A_ARG_TYPE_UpdateID</name>
+ <dataType>ui4</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>SearchCapabilities</name>
+ <dataType>string</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>SortCapabilities</name>
+ <dataType>string</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="yes">
+ <name>SystemUpdateID</name>
+ <dataType>ui4</dataType>
+ </stateVariable>
+ </serviceStateTable>
+</scpd>
diff --git a/httptnt/cds_scpd.o b/httptnt/cds_scpd.o
new file mode 100644
index 0000000..40f462c
--- /dev/null
+++ b/httptnt/cds_scpd.o
Binary files differ
diff --git a/httptnt/cms_scpd.ecpp b/httptnt/cms_scpd.ecpp
new file mode 100644
index 0000000..766e0a6
--- /dev/null
+++ b/httptnt/cms_scpd.ecpp
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<#
+
+This is the Connection Manager Service Description File
+
+It contains the service description for the connection manager of the media server.
+
+#>
+<{ reply.setContentType("application/xml"); }>
+<scpd xmlns="urn:schemas-upnp-org:service-1-0">
+ <specVersion>
+ <major>1</major>
+ <minor>0</minor>
+ </specVersion>
+ <actionList>
+ <action>
+ <name>GetProtocolInfo</name>
+ <argumentList>
+ <argument>
+ <name>Source</name>
+ <direction>out</direction>
+ <relatedStateVariable>SourceProtocolInfo</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Sink</name>
+ <direction>out</direction>
+ <relatedStateVariable>SinkProtocolInfo</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>GetCurrentConnectionIDs</name>
+ <argumentList>
+ <argument>
+ <name>ConnectionIDs</name>
+ <direction>out</direction>
+ <relatedStateVariable>CurrentConnectionIDs</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>GetCurrentConnectionInfo</name>
+ <argumentList>
+ <argument>
+ <name>ConnectionID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_ConnectionID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>RcsID</name>
+ <direction>out</direction>
+ <relatedStateVariable>A_ARG_TYPE_RcsID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>AVTransportID</name>
+ <direction>out</direction>
+ <relatedStateVariable>A_ARG_TYPE_AVTransportID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>ProtocolInfo</name>
+ <direction>out</direction>
+ <relatedStateVariable>A_ARG_TYPE_ProtocolInfo</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>PeerConnectionManager</name>
+ <direction>out</direction>
+ <relatedStateVariable>A_ARG_TYPE_ConnectionManager</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>PeerConnectionID</name>
+ <direction>out</direction>
+ <relatedStateVariable>A_ARG_TYPE_ConnectionID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Direction</name>
+ <direction>out</direction>
+ <relatedStateVariable>A_ARG_TYPE_Direction</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Status</name>
+ <direction>out</direction>
+ <relatedStateVariable>A_ARG_TYPE_ConnectionStatus</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ </actionList>
+ <serviceStateTable>
+ <stateVariable sendEvents="yes">
+ <name>SourceProtocolInfo</name>
+ <dataType>string</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="yes">
+ <name>SinkProtocolInfo</name>
+ <dataType>string</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="yes">
+ <name>CurrentConnectionIDs</name>
+ <dataType>string</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>A_ARG_TYPE_ConnectionStatus</name>
+ <dataType>string</dataType>
+ <allowedValueList>
+ <allowedValue>OK</allowedValue>
+ <allowedValue>ContentFormatMismatch</allowedValue>
+ <allowedValue>InsufficientBandwidth</allowedValue>
+ <allowedValue>UnreliableChannel</allowedValue>
+ <allowedValue>Unknown</allowedValue>
+ </allowedValueList>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>A_ARG_TYPE_ConnectionManager</name>
+ <dataType>string</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>A_ARG_TYPE_Direction</name>
+ <dataType>string</dataType>
+ <allowedValueList>
+ <allowedValue>Input</allowedValue>
+ <allowedValue>Output</allowedValue>
+ </allowedValueList>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>A_ARG_TYPE_ProtocolInfo</name>
+ <dataType>string</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>A_ARG_TYPE_ConnectionID</name>
+ <dataType>i4</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>A_ARG_TYPE_AVTransportID</name>
+ <dataType>i4</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>A_ARG_TYPE_RcsID</name>
+ <dataType>i4</dataType>
+ </stateVariable>
+ </serviceStateTable>
+</scpd>
diff --git a/httptnt/cms_scpd.o b/httptnt/cms_scpd.o
new file mode 100644
index 0000000..4110788
--- /dev/null
+++ b/httptnt/cms_scpd.o
Binary files differ
diff --git a/httptnt/deviceDescription.ecpp b/httptnt/deviceDescription.ecpp
new file mode 100644
index 0000000..f43ff8c
--- /dev/null
+++ b/httptnt/deviceDescription.ecpp
@@ -0,0 +1,88 @@
+<?xml version = "1.0" encoding = "utf-8"?>
+<#
+
+This is the Device Description File
+
+It contains the device and service descriptions of the media server.
+
+#>
+<%pre>
+#include <string>
+#include <stdint.h>
+#include "../include/tools.h"
+#include "../include/server.h"
+#include "../include/service.h"
+#include "../include/webserver.h"
+#include "../include/config.h"
+
+using namespace upnp;
+</%pre>
+<{
+ upnp::cMediaServer* server = cMediaServer::GetInstance();
+ const upnp::cWebserver& webserver = server->GetWebserver();
+
+ const upnp::cMediaServer::Description& serverDescription = server->GetServerDescription();
+ std::string deviceUUID = server->GetDeviceUUID();
+ std::string urlBase = webserver.GetBaseUrl();
+ std::string presentationUrl = webserver.GetPresentationUrl();
+ std::string staticContentUrl = webserver.GetStaticContentUrl();
+
+ std::string serviceUrl = webserver.GetServiceUrl();
+ std::string controlUrl = webserver.GetControlUrl();
+
+ reply.setContentType("application/xml");
+
+}>
+<root xmlns="urn:schemas-upnp-org:device-1-0" xmlns:dlna="urn:schemas-dlna-org:device-1-0">
+ <specVersion>
+ <major>1</major>
+ <minor>0</minor>
+ </specVersion>
+ <URLBase><$ urlBase $></URLBase>
+ <device>
+ <deviceType>urn:schemas-upnp-org:device:MediaServer:1</deviceType>
+ <friendlyName><$ serverDescription.friendlyName $></friendlyName>
+ <manufacturer><$ serverDescription.manufacturer $></manufacturer>
+ <manufacturerURL><$ serverDescription.manufacturerURL $></manufacturerURL>
+ <modelDescription><$ serverDescription.modelDescription $></modelDescription>
+ <modelName><$ serverDescription.modelName $></modelName>
+ <modelNumber><$ serverDescription.modelNumber $></modelNumber>
+ <modelURL><$ serverDescription.modelURL $></modelURL>
+ <serialNumber><$ serverDescription.serialNumber $></serialNumber>
+ <UDN><$ deviceUUID $></UDN>
+ <presentationURL><$ presentationUrl $></presentationURL>
+ <dlna:X_DLNADOC>DMS-1.50</dlna:X_DLNADOC>
+ <serviceList>
+<{
+ const cMediaServer::serviceMap& services = server->GetServices();
+
+ for(cMediaServer::serviceMap::const_iterator it = services.begin(); it != services.end(); ++it){
+ const cUPnPService::Description& serviceDescription = (*it).second->GetServiceDescription();
+}>
+
+ <service>
+ <serviceType><$ serviceDescription.serviceType $></serviceType>
+ <serviceId><$ serviceDescription.serviceID $></serviceId>
+ <SCPDURL><$ serviceUrl $><$ serviceDescription.SCPDXML $></SCPDURL>
+ <controlURL><$ controlUrl $><$ serviceDescription.controlDescriptor $></controlURL>
+ <eventSubURL><$ controlUrl $><$ serviceDescription.eventSubscriberDescriptor $></eventSubURL>
+ </service>
+% };
+ </serviceList>
+ <iconList>
+<{
+ const cMediaServer::iconList& icons = server->GetServerIcons();
+
+ for(cMediaServer::iconList::const_iterator it = icons.begin(); it != icons.end(); ++it){
+}>
+ <icon>
+ <mimetype><$ (*it).profile.mime $></mimetype>
+ <width><$ (*it).profile.width $></width>
+ <height><$ (*it).profile.height $></height>
+ <depth><$ (int)(*it).profile.bitDepth $></depth>
+ <url><$ staticContentUrl $><$ (*it).filename $></url>
+ </icon>
+% };
+ </iconList>
+ </device>
+</root> \ No newline at end of file
diff --git a/httptnt/deviceDescription.o b/httptnt/deviceDescription.o
new file mode 100644
index 0000000..b14f268
--- /dev/null
+++ b/httptnt/deviceDescription.o
Binary files differ
diff --git a/include/config.h b/include/config.h
new file mode 100644
index 0000000..b149316
--- /dev/null
+++ b/include/config.h
@@ -0,0 +1,126 @@
+/*
+ * config.h
+ *
+ * Created on: 03.08.2012
+ * Author: savop
+ */
+
+#ifndef CONFIG_H_
+#define CONFIG_H_
+
+#include <string>
+#include <stdint.h>
+#include "media/profile.h"
+
+using namespace std;
+
+namespace upnp {
+
+struct cConfig {
+ cConfig();
+ /**
+ * Enable the media server
+ *
+ * If this is true, the media server is running on startup.
+ */
+ bool enabled;
+ /**
+ * Enable expert settings
+ *
+ * If this is true, the user may customize additional settings, which should
+ * only be done, if the user is very experienced or want to use that for
+ * debugging.
+ */
+ bool expertSettings;
+ /**
+ * Use the internal web server
+ *
+ * If this is true, the internal web server is used for streaming of files.
+ * Otherwise an external URL is used.
+ */
+ bool useInternalWebserver;
+ /**
+ * Web server root directory
+ *
+ * This is the directory, where the internal web server is looking for files. If it
+ * is empty, the default directory (plugins configuration folder) is used.
+ */
+ string webServerRoot;
+ /**
+ * Web server port
+ *
+ * This is the port where the web server is listening on.
+ */
+ uint16_t webServerPort;
+ /**
+ * External web server URL
+ *
+ * This is the URL, which is used instead of the internal web server URL. If it is
+ * empty, the default presentation URL, which is /index.html is used.
+ */
+ string presentationURL;
+ /**
+ * Maximum size of SOAP messages
+ *
+ * This is the maximum size in bytes of soap messages received by the UPnP library.
+ */
+ size_t maxContentLength;
+
+ /**
+ * Maximum age of upnp announcements
+ *
+ * This is the number of seconds an announcement is valid until the device is declared
+ * as out-dated and has to re-new its announcement.
+ */
+ int announceMaxAge;
+
+ /**
+ * DeviceUUID
+ *
+ * This is the unique identifier for this media server device. If this is empty
+ * it will be generated.
+ */
+ string deviceUUID;
+
+ /**
+ * Service URL
+ *
+ * This is the URL to the services and service descriptors of that services.
+ */
+ string serviceURL;
+
+ /**
+ * Static content URL
+ *
+ * This is the URL to the static content of the server. They must be located within the
+ * network of the server.
+ */
+ string staticContentURL;
+ /**
+ * Bind the server to an IP address
+ *
+ * If this is true, the media server is bound to the specified IP address,
+ * otherwise the specified interface will be used.
+ */
+ bool bindToAddress;
+
+ string address;
+ string interface;
+ /**
+ * The media server listening port
+ *
+ * This is the port which the media server is listening for incoming connections
+ */
+ uint16_t port;
+
+ /**
+ * The sqlite database file
+ *
+ * This is the path to the database file.
+ */
+ string databaseFile;
+};
+
+}
+
+#endif /* CONFIG_H_ */
diff --git a/include/connection.h b/include/connection.h
new file mode 100644
index 0000000..ebd1f4f
--- /dev/null
+++ b/include/connection.h
@@ -0,0 +1,84 @@
+/*
+ * connection.h
+ *
+ * Created on: 31.07.2012
+ * Author: savop
+ */
+
+#ifndef CONNECTION_H_
+#define CONNECTION_H_
+
+#include <vdr/tools.h>
+#include <string>
+
+namespace upnp {
+
+class cVirtualConnection {
+public:
+ enum Direction {
+ VC_OUTPUT,
+ VC_INPUT,
+ NumDirections
+ };
+
+ enum Status {
+ VC_OKAY,
+ VC_CONTENT_FORMAT_MISMATCH,
+ VC_INSUFFICIENT_BANDWIDTH,
+ VC_UNRELIABLE_CHANNEL,
+ VC_UNKNOWN,
+ NumStatus
+ };
+
+ static cVirtualConnection* GenerateVirtualConnection(
+ const std::string& pemoteProtocolInfo,
+ const std::string& peerConnectionManager,
+ int32_t peerConnectionID,
+ Direction direction);
+
+ static cVirtualConnection* GenerateVirtualConnection(
+ const std::string& pemoteProtocolInfo,
+ const std::string& peerConnectionManager,
+ int32_t peerConnectionID,
+ const std::string& direction);
+
+ static void DestroyVirtualConnection(cVirtualConnection* connection);
+
+ const std::string& GetRemoteProtocolInfo() const { return mRemoteProtocolInfo; }
+ const std::string& GetPeerConnectionManager() const { return mPeerConnectionManager; }
+ int32_t GetPeerConnectionID() const { return mPeerConnectionID; }
+ int32_t GetConnectionID() const { return mConnectionID; }
+ int32_t GetAVTransportID() const { return mAVTransportID; }
+ int32_t GetRcsID() const { return mRcsID; }
+ Direction GetDirection() const { return mDirection; }
+ std::string GetDirectionString() const;
+ Status GetStatus() const { return mStatus; }
+ std::string GetStatusString() const;
+
+private:
+ cVirtualConnection(
+ int32_t connectionID,
+ const std::string & remoteProtocolInfo,
+ const std::string & peerConnectionManager,
+ int32_t peerConnectionID,
+ Direction direction);
+ cVirtualConnection(const cVirtualConnection & other);
+ cVirtualConnection& operator=(const cVirtualConnection & other);
+
+ virtual ~cVirtualConnection(){}
+
+ static int32_t NextConnectionID();
+
+ std::string mRemoteProtocolInfo;
+ std::string mPeerConnectionManager;
+ int32_t mPeerConnectionID;
+ int32_t mConnectionID;
+ int32_t mAVTransportID;
+ int32_t mRcsID;
+ Direction mDirection;
+ Status mStatus;
+};
+
+};
+
+#endif /* CONNECTION_H_ */
diff --git a/include/connectionManager.h b/include/connectionManager.h
new file mode 100644
index 0000000..9680891
--- /dev/null
+++ b/include/connectionManager.h
@@ -0,0 +1,70 @@
+/*
+ * connectionManager.h
+ *
+ * Created on: 27.08.2012
+ * Author: savop
+ */
+
+#ifndef CONNECTIONMANAGER_H_
+#define CONNECTIONMANAGER_H_
+
+#include "../include/service.h"
+#include "../include/connection.h"
+#include "../include/tools.h"
+#include <string>
+#include <list>
+#include <map>
+
+namespace upnp {
+
+/**
+ * The connection manager service
+ *
+ * This is the connection manager service which handles all incoming connection,
+ * creates and destroys connections to clients.
+ */
+class cConnectionManager : public cUPnPService {
+public:
+
+ typedef std::map<int32_t, cVirtualConnection*> ConnectionList;
+ /**
+ * Constructor of a Connection manager
+ *
+ * This creates an instance of a <em>Connection Manager Service</em> and provides
+ * interfaces for executing actions and subscribing on events.
+ */
+ cConnectionManager();
+ virtual ~cConnectionManager();
+ /*! @copydoc cUpnpService::subscribe(Upnp_Subscription_Request* Request) */
+ virtual int Subscribe(Upnp_Subscription_Request* request);
+ /*! @copydoc cUpnpService::execute(Upnp_Action_Request* Request) */
+ virtual int Execute(Upnp_Action_Request* request);
+ /*! @copydoc cUpnpService::setError(Upnp_Action_Request* Request, int Error) */
+ virtual void SetError(Upnp_Action_Request* request, int error);
+private:
+
+ int GetProtocolInfo(Upnp_Action_Request* request);
+ int GetCurrentConnectionIDs(Upnp_Action_Request* request);
+ int GetCurrentConnectionInfo(Upnp_Action_Request* request);
+ int PrepareForConnection(Upnp_Action_Request* request);
+ int ConnectionComplete(Upnp_Action_Request* request);
+ const std::string GetConnectionIDsCVS();
+
+ void DeleteConnections();
+ bool DeleteConnection(int32_t connectionID);
+ bool CreateConnection(
+ const std::string & remoteProtocolInfo,
+ const std::string & peerConnectionManager,
+ int32_t peerConnectionID,
+ const std::string & direction
+ );
+
+ bool OnConnectionChange();
+
+ ConnectionList mVirtualConnections;
+ StringList mSupportedProtocols;
+} ConnectionManager;
+
+} // namespace upnp
+
+#endif /* CONNECTIONMANAGER_H_ */
diff --git a/include/contentDirectory.h b/include/contentDirectory.h
new file mode 100644
index 0000000..c2edffc
--- /dev/null
+++ b/include/contentDirectory.h
@@ -0,0 +1,58 @@
+/*
+ * contentDirectory.h
+ *
+ * Created on: 27.08.2012
+ * Author: savop
+ */
+
+#ifndef CONTENTDIRECTORY_H_
+#define CONTENTDIRECTORY_H_
+
+#include "../include/service.h"
+#include "../include/tools.h"
+#include <vdr/thread.h>
+
+namespace upnp {
+
+class cContentDirectory : public cUPnPService, public cThread {
+public:
+
+ /**
+ * Constructor of a Connection manager
+ *
+ * This creates an instance of a <em>Connection Manager Service</em> and provides
+ * interfaces for executing actions and subscribing on events.
+ */
+ cContentDirectory();
+ virtual ~cContentDirectory();
+ /*! @copydoc cUpnpService::subscribe(Upnp_Subscription_Request* Request) */
+ virtual int Subscribe(Upnp_Subscription_Request* request);
+ /*! @copydoc cUpnpService::execute(Upnp_Action_Request* Request) */
+ virtual int Execute(Upnp_Action_Request* request);
+ /*! @copydoc cUpnpService::setError(Upnp_Action_Request* Request, int Error) */
+ virtual void SetError(Upnp_Action_Request* request, int error);
+
+ virtual void Init(cMediaServer* server, UpnpDevice_Handle deviceHandle);
+private:
+ int GetSearchCapabilities(Upnp_Action_Request* Request);
+ int GetSortCapabilities(Upnp_Action_Request* Request);
+ int GetSystemUpdateID(Upnp_Action_Request* Request);
+ int Browse(Upnp_Action_Request* Request);
+ int Search(Upnp_Action_Request* Request);
+ int CreateObject(Upnp_Action_Request* Request);
+ int DestroyObject(Upnp_Action_Request* Request);
+ int UpdateObject(Upnp_Action_Request* Request);
+ int DeleteResource(Upnp_Action_Request* Request);
+ int CreateReference(Upnp_Action_Request* Request);
+ int StopTransferResource(Upnp_Action_Request* Request);
+ int GetTransferProgress(Upnp_Action_Request* Request);
+ int ExportResource(Upnp_Action_Request* Request);
+ int ImportResource(Upnp_Action_Request* Request);
+
+ void Action();
+
+} ContentDirectory;
+
+} // namespace upnp
+
+#endif /* CONTENTDIRECTORY_H_ */
diff --git a/include/media/mediaManager.h b/include/media/mediaManager.h
new file mode 100644
index 0000000..7b3bf83
--- /dev/null
+++ b/include/media/mediaManager.h
@@ -0,0 +1,87 @@
+/*
+ * mediaManager.h
+ *
+ * Created on: 31.08.2012
+ * Author: savop
+ */
+
+#ifndef MEDIAMANAGER_H_
+#define MEDIAMANAGER_H_
+
+#include <vdr/thread.h>
+#include <list>
+#include <string>
+#include <stdint.h>
+#include <tntdb/connection.h>
+#include <tntdb/connect.h>
+#include "../../include/tools.h"
+
+namespace upnp {
+
+class cMediaManager : public cThread {
+private:
+
+ struct MediaRequest {
+ int64_t objectID;
+ std::string filter;
+ uint32_t startIndex;
+ uint32_t requestCount;
+ std::string sortCriteria;
+ std::string result;
+ uint32_t numberReturned;
+ uint32_t totalMatches;
+ uint32_t updateID;
+ };
+
+public:
+
+ enum BrowseFlag {
+ CD_BROWSE_METADATA,
+ CD_BROWSE_DIRECT_CHILDREN,
+ NumBrowseFlags
+ };
+
+ struct BrowseRequest : public MediaRequest {
+ BrowseFlag browseMetadata;
+ };
+
+ struct SearchRequest : public MediaRequest {
+ std::string searchCriteria;
+ };
+
+ cMediaManager();
+ virtual ~cMediaManager();
+
+ void SetDatabaseFile(string file);
+
+ bool Initialise();
+
+ uint32_t GetSystemUpdateID() const;
+ IdList GetContainerUpdateIDs(bool unevented = false);
+ StringList GetSearchCapabilities() const;
+ StringList GetSortCapabilities() const;
+ StringList GetSupportedProtocolInfos() const;
+
+ int Browse(BrowseRequest& request);
+ int Search(SearchRequest& request);
+
+ static BrowseFlag ToBrowseFlag(std::string browseFlag);
+
+private:
+
+ void Action();
+ bool CheckIntegrity();
+
+ void OnContainerUpdate(string containerID, long updateID);
+
+ uint32_t mSystemUpdateID;
+ IdList mEventedContainerUpdateIDs;
+ string mDatabaseFile;
+ tntdb::Connection mConnection;
+
+};
+
+} // namespace upnp
+
+
+#endif /* MEDIAMANAGER_H_ */
diff --git a/include/media/profile.h b/include/media/profile.h
new file mode 100644
index 0000000..867de66
--- /dev/null
+++ b/include/media/profile.h
@@ -0,0 +1,50 @@
+/*
+ * profile.h
+ *
+ * Created on: 05.08.2012
+ * Author: savop
+ */
+
+#ifndef PROFILE_H_
+#define PROFILE_H_
+
+#include <string>
+#include <stdint.h>
+
+using namespace std;
+
+namespace upnp {
+
+namespace video {
+
+}
+
+namespace audio {
+
+}
+
+namespace image {
+
+struct cIcon {
+ const char* mime; ///< the mime type of the image
+ uint16_t width; ///< image width in pixel
+ uint16_t height; ///< image height in pixel
+ uint8_t bitDepth; ///< bit depth in bits per pixel
+};
+
+/* JPEG Icons */
+//extern cIcon DLNA_ICON_JEPG_TN; ///< DLNA jpeg thumbnail profile of images
+extern cIcon DLNA_ICON_JPEG_SM_24; ///< DLNA icon profile of small jpeg images
+extern cIcon DLNA_ICON_JPEG_LRG_24; ///< DLNA icon profile of large jpeg images
+
+/* PNG Icons */
+extern cIcon DLNA_ICON_PNG_SM_24A; ///< DLNA icon profile of small png images
+extern cIcon DLNA_ICON_PNG_LRG_24A; ///< DLNA icon profile of large png images
+//extern cIcon DLNA_ICON_PNG_TN; ///< DLNA png thumbnail profile of images
+
+} /* NS:image */
+
+} /* NS:upnp */
+
+
+#endif /* PROFILE_H_ */
diff --git a/include/plugin.h b/include/plugin.h
new file mode 100644
index 0000000..94a60da
--- /dev/null
+++ b/include/plugin.h
@@ -0,0 +1,484 @@
+/*
+ * plugin.h
+ *
+ * Created on: 31.08.2012
+ * Author: savop
+ */
+
+#ifndef PLUGIN_H_
+#define PLUGIN_H_
+
+#include <string>
+#include <map>
+#include <list>
+#include <stdint.h>
+
+using namespace std;
+
+namespace upnp {
+
+/**
+ * Metadata class
+ *
+ * This class describes the content of a multimedia item like a video or audio file.
+ *
+ * It contains some general information like the title or a detailed description of
+ * the content and one or more resources to the file.
+ *
+ * Why are multiple resources possible, though I just have that one file? Lets say,
+ * you want to display images. It is very useful if there are thumbnail pictures
+ * available, which may speed up the display process as the UPnP media renderer may
+ * first display those thumbnails rather than the huge image itself. Those images
+ * both refer to the same meta information. However, they have different resource
+ * information. This may also apply to videos (different resolution, bit rate,
+ * whatever...)
+ *
+ * Every plugin implementor is requested to add as much as available information about
+ * a resource, because the media renderer may display them in their user interface,
+ * which in turn makes the navigation and identifying the contents much easier.
+ *
+ * The metadata class itself offers some major properties, which are very common in
+ * the VDRs context. However, the implementor may add additional custom information
+ * to the list of properties.
+ *
+ * A Property with the same name but different value may exist multiple times. It is
+ * added multiple times to the output. Property attributes are NOT supported. However,
+ * there only a handful of attributes, which are likely to be unimportant.
+ *
+ */
+class cMetadata {
+public:
+
+ /**
+ * Property class
+ *
+ * This is a basic key-value class, which holds a property (the key)
+ * and a value. The value is kept in a string representation. There
+ * is a set of getter methods to convert the string in to the corresponding
+ * data type. However, there is no type safety given. The implementor
+ * must know what the contents of the properties are.
+ */
+ class Property {
+ public:
+ Property(){};
+ Property(string key, string value);
+ Property(string key, long value);
+ Property(string key, bool value);
+
+ string GetKey() const;
+ string GetString() const;
+ long GetInteger() const;
+ bool GetBoolean() const;
+
+ private:
+ string key;
+ string value;
+ };
+
+ /**
+ * Resource class
+ *
+ * The resource class contains information about a specific resource.
+ *
+ * Most of the information are optional except the protocolInfo and the
+ * resourceUri.
+ *
+ * The resourceUri is the locator to the file or stream or whatever. The uri
+ * is provided by the resourceProvider plugins and must be understood by the
+ * mediaProfiler plugins.
+ */
+ class Resource {
+ public:
+
+ bool SetResourceUri(string resourceUri);
+ bool SetProtocolInfo(string protocolInfo);
+ bool SetDuration(string duration);
+ bool SetResolution(string resolution);
+ bool SetBitrate(uint32_t bitrate);
+ bool SetSize(uint32_t size);
+ bool SetSampleFrequency(uint32_t sampleFrequency);
+ bool SetBitsPerSample(uint32_t bitsPerSample);
+ bool SetNrAudioChannels(uint32_t nrAudioChannels);
+ bool SetColorDepth(uint32_t colorDepth);
+
+ string GetResourceUri() const { return resourceUri; }
+ string GetProtocolInfo() const { return protocolInfo; }
+ string GetDuration() const { return duration; }
+ string GetResolution() const { return resolution; }
+ uint32_t GetBitrate() const { return bitrate; }
+ uint32_t GetSize() const { return size; }
+ uint32_t GetSampleFrequency() const { return sampleFrequency; }
+ uint32_t GetBitsPerSample() const { return bitsPerSample; }
+ uint32_t GetNrAudioChannels() const { return nrAudioChannels; }
+ uint32_t GetColorDepth() const { return colorDepth; }
+
+ private:
+ string resourceUri;
+ string protocolInfo;
+ string duration;
+ string resolution;
+ uint32_t bitrate;
+ uint32_t size;
+ uint32_t sampleFrequency;
+ uint32_t bitsPerSample;
+ uint32_t nrAudioChannels;
+ uint32_t colorDepth;
+ };
+
+ typedef multimap<string, Property> PropertyMap;
+ typedef pair<PropertyMap::iterator,PropertyMap::iterator> PropertyRange;
+ typedef list<Resource> ResourceList;
+
+ bool AddProperty(Property property);
+ bool SetProperty(Property property, int index = 0);
+
+ Property& GetPropertyByKey(string property);
+ PropertyRange GetPropertiesByKey(string property);
+ PropertyRange GetAllProperties();
+
+ bool SetObjectID(string objectID);
+ bool SetObjectIDByUri(string uri);
+ string GetObjectID() const { return objectID; }
+ bool SetParentID(string parentID);
+ bool SetParentIDByUri(string uri);
+ string GetParentID() const { return parentID; }
+ bool SetTitle(string title);
+ string GetTitle() const { return title; }
+ bool SetUpnpClass(string upnpClass);
+ string GetUpnpClass() const { return upnpClass; }
+ bool SetRestricted(bool restricted);
+ bool GetRestricted() const { return restricted; }
+ bool SetDescription(string description);
+ string GetDescription() const { return description; }
+ bool SetLongDescription(string longDescription);
+ string GetLongDescription() const { return longDescription; }
+ bool SetDate(string date);
+ string GetDate() const { return date; }
+ bool SetLanguage(string language);
+ string GetLanguage() const { return language; }
+ bool SetChannelNr(int channelNr);
+ int GetChannelNr() const { return channelNr; }
+ bool SetChannelName(string channelName);
+ string GetChannelName() const { return channelName; }
+ bool SetScheduledStart(string scheduledStart);
+ string GetScheduledStart() const { return scheduledStart; }
+ bool SetScheduledEnd(string scheduledEnd);
+ string GetScheduledEnd() const { return scheduledEnd; }
+
+private:
+
+ PropertyMap properties;
+ ResourceList resources;
+
+ string objectID;
+ string parentID;
+ string title;
+ string upnpClass;
+ bool restricted;
+ string description;
+ string longDescription;
+ string date;
+ string language;
+ int channelNr;
+ string channelName;
+ string scheduledStart;
+ string scheduledEnd;
+
+};
+
+#define UPNP_REGISTER_RESOURCE_PROVIDER(cls) extern "C" void *UPnPCreateResourceProvider(void) { return new cls; }
+
+class cUPnPResourceProvider {
+public:
+
+ typedef list<string> EntryList;
+
+ virtual ~cUPnPResourceProvider(){};
+
+ /**
+ * Returns the schema of this provider
+ *
+ * Each provider provides a specific uri schema. This
+ * is required to distinguish the different kind of file
+ * types and resource types.
+ *
+ * This can be for instance (without ://):
+ *
+ * Schema Description
+ * vdr:// external http stream
+ * rec:// recording
+ * file:// any other file, which is not managed by the vdr or the upnp plugin
+ *
+ */
+ virtual string ProvidesSchema() = 0;
+
+ /**
+ * Returns the root container of the provider.
+ *
+ * This function is used to determine the root of the provider.
+ * The returned string is prepended to every uri.
+ *
+ * This results in the following uri:
+ *
+ * <schema>://<rootContainer>/<path>...
+ */
+ virtual string GetRootContainer() = 0;
+
+ /**
+ * Return a list of the elements in a container.
+ *
+ * This function returns a list of elements in a container.
+ * Each element will be checked by the plugin manager, if it is
+ * an item or another container.
+ *
+ * If an element is a container, the manager will append the
+ * element to the URI and creates a new call to this function
+ * to recurse through the directory tree.
+ *
+ * The implementor MUST skip entries, which redirects to the
+ * parent container or itself, i.e. containers like "." and "..".
+ *
+ * Additionally, link loops MUST be avoided as much as possible.
+ * The plugin manager will try to detect a loop. However, you
+ * should not use links.
+ *
+ * The given URI is an absolute URI.
+ */
+ virtual EntryList GetContainerEntries(string uri) = 0;
+
+ /**
+ * Checks if the given URI is a container.
+ *
+ * This function is used by the plugin manager to check if
+ * the given absolute URI is a container or not.
+ *
+ * If it is a container, the plugin manager will try to recurse
+ * into that directory and get the elements of it.
+ */
+ virtual bool IsContainer(string uri) = 0;
+
+ /**
+ * Checks if the given URI is a link to another element.
+ *
+ * The plugin manager uses this function in conjuction with
+ * the IsContainer() function to iterate and recurse through
+ * the tree. If a link directs to a container (IsContainer()
+ * return true), then the plugin manager tries to detect
+ * loops.
+ *
+ * If target is not empty, the target is evaluated and
+ * registered as a reference to the original element. Otherwise
+ * the element is skipped.
+ */
+ virtual bool IsLink(string uri, string& target) = 0;
+
+ /**
+ * Returns the containerUpdateId of the given container
+ *
+ * This function called every time a container gets evaluated.
+ *
+ * This happens at the start of the media server and on every
+ * change to that container.
+ *
+ * This function MUST return an identifier, which enables the
+ * plugin manager to distinguish a change of a container.
+ *
+ * The identifier MUST overcome restarts. If the container was
+ * not changes after a restart of the server, the identifier
+ * must be equal to the one returned before the server was
+ * restarted. If the container was changed, the identifier must
+ * be greater than the previous one. The identifier must not
+ * decrease in the scope of an unsigned integer, as this may
+ * cause clients which were offline for some time to get
+ * confused, if the elements differ though the updateId remains
+ * the same.
+ */
+ virtual long GetContainerUpdateId(string uri) = 0;
+
+ /**
+ * Returns the meta data of a specific container.
+ *
+ * This function is used to retrieve meta informatio about a
+ * container. It is NOT used to get information about files or
+ * resources, as this is done by the media plugins.
+ *
+ * However, a resource provider may override this function
+ * to add more detailed information about a specific container,
+ * for instance if it is a special container like a photo album.
+ *
+ * The implementor is requested to return meta information about
+ * the resource provider like title or description when called
+ * with the root container uri.
+ *
+ * The default implementation will fill the required meta data
+ * properties with the best matching information, i.e:
+ *
+ * - Class: object.container
+ * - Title: the element name
+ * - Restricted: true
+ *
+ */
+ virtual cMetadata* GetMetadata(string uri);
+
+ /**
+ * Get the HTTP Uri.
+ *
+ * This function returns the HTTP Uri if the stream is accessible
+ * from an external server via HTTP.
+ *
+ * The resource MUST be located in the same network than the server.
+ *
+ * This function returns an empty string by default, which means
+ * that the resource provider uses internal access via the file
+ * access function Open(), Read(), Close().
+ *
+ * The implementor of a resource provider MUST either implement
+ * this function or the file access methods below.
+ */
+ virtual string GetHTTPUri(string uri);
+
+ /**
+ * Opens a resource
+ *
+ * If the resource provider uses internal file access it MUST
+ * implement the file access methods Open(), i.e. this one, Read()
+ * and Close().
+ *
+ * This opens the given uri and prepares the provider to read
+ * from the file. If there is an error while opening the resource,
+ * it must return false.
+ *
+ * The default implementation return false.
+ */
+ virtual bool Open(string uri);
+
+ /**
+ * Reads data from the stream
+ *
+ * This functions reads up to "buflen" number the number of bytes
+ * into the buffer and returns the actual number of bytes read.
+ *
+ * If there was an error while reading the function MUST return -1.
+ * This also applies to when there was no Open() call before reading.
+ *
+ * If the resource provider reached end of file, it MUST return 0.
+ *
+ * The default implementation return 0.
+ */
+ virtual size_t Read(char* buf, size_t bufLen);
+
+ /**
+ * Closes the file
+ *
+ * This function closes the file opened by Open(). It MUST free all
+ * acquired memory resources.
+ *
+ */
+ virtual void Close();
+
+protected:
+
+ /**
+ * Called whenever a container was changed.
+ *
+ * This function MUST be called by a resource provider whenever
+ * a container was changed during the runtime of the server.
+ *
+ * The containerUpdateId must equal to the one returned by
+ * GetContainerUpdateId() with the same URI. This means,
+ * if the provider detected a change of a container, this function
+ * is called. Then, the plugin manager might call
+ * GetContainerUpdateId() with the URI to retrieve the Id again.
+ *
+ * The following rules MUST be applied:
+ *
+ * - A change must be signaled if a file or directory was
+ * - created
+ * - modified
+ * - deleted
+ * - moved
+ * - If a file or directory was created, the parent container
+ * URI was changed. Therefore, this URI is signaled with
+ * an appropriate updateID.
+ * - If a file was modified, the parent container must be
+ * signaled.
+ * - A file is modified, when its contents or metadata have
+ * changed.
+ * - If a container was modified, the container itself must be
+ * signaled. The parent container is kept untouched.
+ * - If a file or container was deleted, the parent container
+ * must be signaled.
+ * - If a file or container is moved, two changes must be
+ * signaled:
+ * - change of the parent container, where the file or container
+ * was moved from
+ * - change of the parent container, where the file or container
+ * was moved to
+ */
+ void OnContainerUpdate(string uri, long containerUpdateId);
+
+};
+
+#define UPNP_REGISTER_MEDIA_PLUGIN(cls) extern "C" void *UPnPCreateMediaProfiler(void) { return new cls; }
+
+class cMediaProfiler {
+public:
+
+ virtual ~cMediaProfiler(){};
+
+ /**
+ * Tell the manager if this plugin can handle a specific uri schema
+ *
+ * This function gets called by the plugin manager multiple times to check
+ * if the plugin can handle a specific uri schema.
+ *
+ * This can be for instance (without ://):
+ *
+ * Schema Description
+ * vdr:// live-tv stream
+ * rec:// recording
+ * file:// any other file, which is not managed by the vdr or the upnp plugin
+ *
+ * The plugin must reject all but the schemas it is capable of.
+ *
+ * @return \b true if the plugin can handle the uri schema, \b false if not.
+ */
+ virtual bool CanHandleSchema(string schema) = 0;
+
+ /**
+ * Get the metadata from a specific resource.
+ *
+ * This function is called by the plugin manager when the manager scans for
+ * files in a container. The implementor must create and fill a mediaResource
+ * object which contains all information about the resource.
+ *
+ * If the plugin is not able to fill the required information about the resource,
+ * it MUST return NULL. The plugin manager will then rotate through all the
+ * registered plugins until it finds a suitable one.
+ *
+ * The required information MUST be set in the cMediaResource::Create function
+ * correctly. Otherwise the object creation might fail and a NULL pointer will
+ * be returned.
+ *
+ * Additionally, a plugin implementor is advised to add as many as possible
+ * metadata about a resource. Especially, information about the contents might
+ * help the user.
+ *
+ * The URI is a absolute path to the resource.
+ *
+ * The plugin manager tries to reduce the calls of the resources as much as
+ * possible. It will only requests new information, if the resource was
+ * changed.
+ *
+ * A change of a resource is indicated by the resource provider.
+ *
+ * @param uri the absolute path to the resource.
+ * @return the media resource information.
+ */
+ virtual cMetadata* GetMetadata(string uri) = 0;
+protected:
+};
+
+} // namespace upnp
+
+#endif /* PLUGIN_H_ */
diff --git a/include/plugin.h.txt b/include/plugin.h.txt
new file mode 100644
index 0000000..d4bb970
--- /dev/null
+++ b/include/plugin.h.txt
@@ -0,0 +1,486 @@
+/*
+ * plugin.h
+ *
+ * Created on: 31.08.2012
+ * Author: savop
+ */
+
+#ifndef PLUGIN_H_
+#define PLUGIN_H_
+
+#include <string>
+#include <map>
+#include <list>
+
+using namespace std;
+
+namespace upnp {
+
+/**
+ * Metadata class
+ *
+ * This class describes the content of a multimedia item like a video or audio file.
+ *
+ * It contains some general information like the title or a detailed description of
+ * the content and one or more resources to the file.
+ *
+ * Why are multiple resources possible, though I just have that one file? Lets say,
+ * you want to display images. It is very useful if there are thumbnail pictures
+ * available, which may speed up the display process as the UPnP media renderer may
+ * first display those thumbnails rather than the huge image itself. Those images
+ * both refer to the same meta information. However, they have different resource
+ * information. This may also apply to videos (different resolution, bit rate,
+ * whatever...)
+ *
+ * Every plugin implementor is requested to add as much as available information about
+ * a resource, because the media renderer may display them in their user interface,
+ * which in turn makes the navigation and identifying the contents much easier.
+ *
+ * The metadata class itself offers some major properties, which are very common in
+ * the VDRs context. However, the implementor may add additional custom information
+ * to the list of properties.
+ *
+ * A Property with the same name but different value may exist multiple times. It is
+ * added multiple times to the output. Property attributes are NOT supported. However,
+ * there only a handful of attributes, which are likely to be unimportant.
+ *
+ */
+class cMetadata {
+public:
+
+ /**
+ * Property class
+ *
+ * This is a basic key-value class, which holds a property (the key)
+ * and a value. The value is kept in a string representation. There
+ * is a set of getter methods to convert the string in to the corresponding
+ * data type. However, there is no type safety given. The implementor
+ * must know what the contents of the properties are.
+ */
+ class Property {
+ public:
+ Property(){};
+ Property(string key, string value);
+ Property(string key, long value);
+ Property(string key, bool value);
+
+ /**
+ * Returns the string representation of the value.
+ */
+ string operator()() const;
+
+ string GetKey() const;
+ string GetString() const;
+ long GetInteger() const;
+ bool GetBoolean() const;
+
+ private:
+ string key;
+ string value;
+ };
+
+ /**
+ * Resource class
+ *
+ * The resource class contains information about a specific resource.
+ *
+ * Most of the information are optional except the protocolInfo and the
+ * resourceUri.
+ *
+ * The resourceUri is the locator to the file or stream or whatever. The uri
+ * is provided by the resourceProvider plugins and must be understood by the
+ * mediaProfiler plugins.
+ */
+ class Resource {
+ public:
+
+ bool SetResourceUri(string resourceUri);
+ bool SetProtocolInfo(string protocolInfo);
+ bool SetDuration(string duration);
+ bool SetResolution(string resolution);
+ bool SetBitrate(uint32_t bitrate);
+ bool SetSize(uint32_t size);
+ bool SetSampleFrequency(uint32_t sampleFrequency);
+ bool SetBitsPerSample(uint32_t bitsPerSample);
+ bool SetNrAudioChannels(uint32_t nrAudioChannels);
+ bool SetColorDepth(uint32_t colorDepth);
+
+ string GetResourceUri() const { return resourceUri; }
+ string GetProtocolInfo() const { return protocolInfo; }
+ string GetDuration() const { return duration; }
+ string GetResolution() const { return resolution; }
+ uint32_t GetBitrate() const { return bitrate; }
+ uint32_t GetSize() const { return size; }
+ uint32_t GetSampleFrequency() const { return sampleFrequency; }
+ uint32_t GetBitsPerSample() const { return bitsPerSample; }
+ uint32_t GetNrAudioChannels() const { return nrAudioChannels; }
+ uint32_t GetColorDepth() const { return colorDepth; }
+
+ private:
+ string resourceUri;
+ string protocolInfo;
+ string duration;
+ string resolution;
+ uint32_t bitrate;
+ uint32_t size;
+ uint32_t sampleFrequency;
+ uint32_t bitsPerSample;
+ uint32_t nrAudioChannels;
+ uint32_t colorDepth;
+ };
+
+ typedef multimap<string, Property> PropertyMap;
+ typedef pair<PropertyMap::iterator,PropertyMap::iterator> PropertyRange;
+ typedef list<Resource> ResourceList;
+
+ bool AddProperty(Property property);
+ bool SetProperty(Property property, int index = 0);
+
+ Property& GetPropertyByKey(string property);
+ PropertyRange GetPropertiesByKey(string property);
+ PropertyRange GetAllProperties();
+
+ bool SetObjectID(string objectID);
+ string GetObjectID() const { return objectID; }
+ bool SetParentID(string parentID);
+ string GetParentID() const { return parentID; }
+ bool SetTitle(string title);
+ string GetTitle() const { return title; }
+ bool SetUpnpClass(string upnpClass);
+ string GetUpnpClass() const { return upnpClass; }
+ bool SetRestricted(bool restricted);
+ bool GetRestricted() const { return restricted; }
+ bool SetDescription(string description);
+ string GetDescription() const { return description; }
+ bool SetLongDescription(string longDescription);
+ string GetLongDescription() const { return longDescription; }
+ bool SetDate(string date);
+ string GetDate() const { return date; }
+ bool SetLanguage(string language);
+ string GetLanguage() const { return language; }
+ bool SetChannelNr(int channelNr);
+ int GetChannelNr() const { return channelNr; }
+ bool SetChannelName(string channelName);
+ string GetChannelName() const { return channelName; }
+ bool SetScheduledStart(string scheduledStart);
+ string GetScheduledStart() const { return scheduledStart; }
+ bool SetScheduledEnd(string scheduledEnd);
+ string GetScheduledEnd() const { return scheduledEnd; }
+
+private:
+
+ PropertyMap properties;
+ ResourceList resources;
+
+ string objectID;
+ string parentID;
+ string title;
+ string upnpClass;
+ bool restricted;
+ string description;
+ string longDescription;
+ string date;
+ string language;
+ int channelNr;
+ string channelName;
+ string scheduledStart;
+ string scheduledEnd;
+
+};
+
+#define UPNP_REGISTER_RESOURCE_PROVIDER(cls) extern "C" void *UPnPCreateResourceProvider(void) { return new cls; }
+
+class cUPnPResourceProvider {
+public:
+
+ typedef list<string> EntryList;
+
+ virtual ~cUPnPResourceProvider(){};
+
+ /**
+ * Returns the schema of this provider
+ *
+ * Each provider provides a specific uri schema. This
+ * is required to distinguish the different kind of file
+ * types and resource types.
+ *
+ * This can be for instance (without ://):
+ *
+ * Schema Description
+ * vdr:// external http stream
+ * rec:// recording
+ * file:// any other file, which is not managed by the vdr or the upnp plugin
+ *
+ */
+ virtual string ProvidesSchema() = 0;
+
+ /**
+ * Returns the root container of the provider.
+ *
+ * This function is used to determine the root of the provider.
+ * The returned string is prepended to every uri.
+ *
+ * This results in the following uri:
+ *
+ * <schema>://<rootContainer>/<path>...
+ */
+ virtual string GetRootContainer() = 0;
+
+ /**
+ * Return a list of the elements in a container.
+ *
+ * This function returns a list of elements in a container.
+ * Each element will be checked by the plugin manager, if it is
+ * an item or another container.
+ *
+ * If an element is a container, the manager will append the
+ * element to the URI and creates a new call to this function
+ * to recurse through the directory tree.
+ *
+ * The implementor MUST skip entries, which redirects to the
+ * parent container or itself, i.e. containers like "." and "..".
+ *
+ * Additionally, link loops MUST be avoided as much as possible.
+ * The plugin manager will try to detect a loop. However, you
+ * should not use links.
+ *
+ * The given URI is an absolute URI.
+ */
+ virtual EntryList GetContainerEntries(string uri) = 0;
+
+ /**
+ * Checks if the given URI is a container.
+ *
+ * This function is used by the plugin manager to check if
+ * the given absolute URI is a container or not.
+ *
+ * If it is a container, the plugin manager will try to recurse
+ * into that directory and get the elements of it.
+ */
+ virtual bool IsContainer(string uri) = 0;
+
+ /**
+ * Checks if the given URI is a link to another element.
+ *
+ * The plugin manager uses this function in conjuction with
+ * the IsContainer() function to iterate and recurse through
+ * the tree. If a link directs to a container (IsContainer()
+ * return true), then the plugin manager tries to detect
+ * loops.
+ *
+ * If target is not empty, the target is evaluated and
+ * registered as a reference to the original element. Otherwise
+ * the element is skipped.
+ */
+ virtual bool IsLink(string uri, string& target) = 0;
+
+ /**
+ * Returns the containerUpdateId of the given container
+ *
+ * This function called every time a container gets evaluated.
+ *
+ * This happens at the start of the media server and on every
+ * change to that container.
+ *
+ * This function MUST return an identifier, which enables the
+ * plugin manager to distinguish a change of a container.
+ *
+ * The identifier MUST overcome restarts. If the container was
+ * not changes after a restart of the server, the identifier
+ * must be equal to the one returned before the server was
+ * restarted. If the container was changed, the identifier must
+ * be greater than the previous one. The identifier must not
+ * decrease in the scope of an unsigned integer, as this may
+ * cause clients which were offline for some time to get
+ * confused, if the elements differ though the updateId remains
+ * the same.
+ */
+ virtual long GetContainerUpdateId(string uri) = 0;
+
+ /**
+ * Returns the meta data of a specific container.
+ *
+ * This function is used to retrieve meta informatio about a
+ * container. It is NOT used to get information about files or
+ * resources, as this is done by the media plugins.
+ *
+ * However, a resource provider may override this function
+ * to add more detailed information about a specific container,
+ * for instance if it is a special container like a photo album.
+ *
+ * The implementor is requested to return meta information about
+ * the resource provider like title or description when called
+ * with the root container uri.
+ *
+ * The default implementation will fill the required meta data
+ * properties with the best matching information, i.e:
+ *
+ * - Class: object.container
+ * - Title: the element name
+ * - Restricted: true
+ *
+ */
+ virtual cMetadata* GetMetadata(string uri);
+
+ /**
+ * Get the HTTP Uri.
+ *
+ * This function returns the HTTP Uri if the stream is accessible
+ * from an external server via HTTP.
+ *
+ * The resource MUST be located in the same network than the server.
+ *
+ * This function returns an empty string by default, which means
+ * that the resource provider uses internal access via the file
+ * access function Open(), Read(), Close().
+ *
+ * The implementor of a resource provider MUST either implement
+ * this function or the file access methods below.
+ */
+ virtual string GetHTTPUri(string uri);
+
+ /**
+ * Opens a resource
+ *
+ * If the resource provider uses internal file access it MUST
+ * implement the file access methods Open(), i.e. this one, Read()
+ * and Close().
+ *
+ * This opens the given uri and prepares the provider to read
+ * from the file. If there is an error while opening the resource,
+ * it must return false.
+ *
+ * The default implementation return false.
+ */
+ virtual bool Open(string uri);
+
+ /**
+ * Reads data from the stream
+ *
+ * This functions reads up to "buflen" number the number of bytes
+ * into the buffer and returns the actual number of bytes read.
+ *
+ * If there was an error while reading the function MUST return -1.
+ * This also applies to when there was no Open() call before reading.
+ *
+ * If the resource provider reached end of file, it MUST return 0.
+ *
+ * The default implementation return 0.
+ */
+ virtual size_t Read(char* buf, size_t bufLen);
+
+ /**
+ * Closes the file
+ *
+ * This function closes the file opened by Open(). It MUST free all
+ * acquired memory resources.
+ *
+ */
+ virtual void Close();
+
+protected:
+
+ /**
+ * Called whenever a container was changed.
+ *
+ * This function MUST be called by a resource provider whenever
+ * a container was changed during the runtime of the server.
+ *
+ * The containerUpdateId must equal to the one returned by
+ * GetContainerUpdateId() with the same URI. This means,
+ * if the provider detected a change of a container, this function
+ * is called. Then, the plugin manager might call
+ * GetContainerUpdateId() with the URI to retrieve the Id again.
+ *
+ * The following rules MUST be applied:
+ *
+ * - A change must be signaled if a file or directory was
+ * - created
+ * - modified
+ * - deleted
+ * - moved
+ * - If a file or directory was created, the parent container
+ * URI was changed. Therefore, this URI is signaled with
+ * an appropriate updateID.
+ * - If a file was modified, the parent container must be
+ * signaled.
+ * - A file is modified, when its contents or metadata have
+ * changed.
+ * - If a container was modified, the container itself must be
+ * signaled. The parent container is kept untouched.
+ * - If a file or container was deleted, the parent container
+ * must be signaled.
+ * - If a file or container is moved, two changes must be
+ * signaled:
+ * - change of the parent container, where the file or container
+ * was moved from
+ * - change of the parent container, where the file or container
+ * was moved to
+ */
+ void OnContainerUpdate(string uri, long containerUpdateId);
+
+};
+
+#define UPNP_REGISTER_MEDIA_PLUGIN(cls) extern "C" void *UPnPCreateMediaProfiler(void) { return new cls; }
+
+class cMediaProfiler {
+public:
+
+ virtual ~cMediaProfiler(){};
+
+ /**
+ * Tell the manager if this plugin can handle a specific uri schema
+ *
+ * This function gets called by the plugin manager multiple times to check
+ * if the plugin can handle a specific uri schema.
+ *
+ * This can be for instance (without ://):
+ *
+ * Schema Description
+ * vdr:// live-tv stream
+ * rec:// recording
+ * file:// any other file, which is not managed by the vdr or the upnp plugin
+ *
+ * The plugin must reject all but the schemas it is capable of.
+ *
+ * @return \b true if the plugin can handle the uri schema, \b false if not.
+ */
+ virtual bool CanHandleSchema(string schema) = 0;
+
+ /**
+ * Get the metadata from a specific resource.
+ *
+ * This function is called by the plugin manager when the manager scans for
+ * files in a container. The implementor must create and fill a mediaResource
+ * object which contains all information about the resource.
+ *
+ * If the plugin is not able to fill the required information about the resource,
+ * it MUST return NULL. The plugin manager will then rotate through all the
+ * registered plugins until it finds a suitable one.
+ *
+ * The required information MUST be set in the cMediaResource::Create function
+ * correctly. Otherwise the object creation might fail and a NULL pointer will
+ * be returned.
+ *
+ * Additionally, a plugin implementor is advised to add as many as possible
+ * metadata about a resource. Especially, information about the contents might
+ * help the user.
+ *
+ * The URI is a absolute path to the resource.
+ *
+ * The plugin manager tries to reduce the calls of the resources as much as
+ * possible. It will only requests new information, if the resource was
+ * changed.
+ *
+ * A change of a resource is indicated by the resource provider.
+ *
+ * @param uri the absolute path to the resource.
+ * @return the media resource information.
+ */
+ virtual cMetadata* GetMetadata(string uri) = 0;
+protected:
+};
+
+} // namespace upnp
+
+#endif /* PLUGIN_H_ */
diff --git a/include/pluginManager.h b/include/pluginManager.h
new file mode 100644
index 0000000..90ee456
--- /dev/null
+++ b/include/pluginManager.h
@@ -0,0 +1,20 @@
+/*
+ * pluginManager.h
+ *
+ * Created on: 05.09.2012
+ * Author: savop
+ */
+
+#ifndef PLUGINMANAGER_H_
+#define PLUGINMANAGER_H_
+
+namespace upnp {
+
+class cPluginManager {
+public:
+private:
+};
+
+} // namespace upnp
+
+#endif /* PLUGINMANAGER_H_ */
diff --git a/include/server.h b/include/server.h
new file mode 100644
index 0000000..03a7524
--- /dev/null
+++ b/include/server.h
@@ -0,0 +1,110 @@
+/*
+ * server.h
+ *
+ * Created on: 31.07.2012
+ * Author: savop
+ */
+
+#ifndef SERVER_H_
+#define SERVER_H_
+
+#include <string>
+#include <upnp/upnp.h>
+#include "../include/webserver.h"
+#include "../include/config.h"
+
+using namespace std;
+
+namespace upnp {
+
+class cUPnPService;
+class cMediaManager;
+
+class cMediaServer {
+public:
+
+ struct Description {
+ Description(string, string, string , string, string, string , string, string);
+ string friendlyName;
+ string manufacturer;
+ string manufacturerURL;
+ string modelDescription;
+ string modelName;
+ string modelNumber;
+ string modelURL;
+ string serialNumber;
+ };
+
+ struct ServerIcon {
+ ServerIcon(image::cIcon, string);
+ image::cIcon profile;
+ string filename;
+ };
+
+ typedef map<string, cUPnPService*> serviceMap;
+ typedef list<ServerIcon> iconList;
+
+ virtual ~cMediaServer();
+
+ bool Initialize();
+ bool Start();
+ bool Stop();
+
+ bool IsRunning() const;
+
+ static cMediaServer* GetInstance();
+
+ void SetConfiguration(upnp::cConfig newConfig);
+ upnp::cConfig GetConfiguration() const;
+
+ const char* GetServerIPAddress() const;
+ uint16_t GetServerPort() const;
+
+ int GetAnnounceMaxAge() const { return mAnnounceMaxAge; }
+ size_t GetMaxContentLength() const { return mMaxContentLength; }
+
+ const cWebserver& GetWebserver() const { return *mWebserver; }
+ cMediaManager& GetManager() const { return *mMediaManager; }
+
+ const string GetDeviceUUID() const { return mCurrentConfiguration.deviceUUID; }
+
+ const Description& GetServerDescription() const { return mServerDescription; }
+ const iconList& GetServerIcons() const { return mServerIcons; }
+ const serviceMap& GetServices() const { return mServices; }
+
+ static void RegisterService(cUPnPService* service);
+
+private:
+ class RuntimeException : public std::exception {
+ public:
+ virtual const char* what() const throw();
+ };
+
+ cMediaServer();
+
+ string GetDeviceDescriptionUrl() const;
+ void SetAnnounceMaxAge(int announceMaxAge);
+ void SetMaxContentLength(size_t maxContentLength);
+ bool CheckDeviceUUID(string deviceUUID) const;
+
+ static int ActionCallback(Upnp_EventType eventtype, void *event, void *cookie);
+
+ Description mServerDescription;
+ iconList mServerIcons;
+ upnp::cConfig mCurrentConfiguration;
+ UpnpDevice_Handle mDeviceHandle;
+ int mAnnounceMaxAge;
+ size_t mMaxContentLength;
+ bool mIsRunning;
+
+ cWebserver* mWebserver;
+ cMediaManager* mMediaManager;
+
+ static serviceMap mServices;
+
+};
+
+}
+
+
+#endif /* SERVER_H_ */
diff --git a/include/service.h b/include/service.h
new file mode 100644
index 0000000..ddcf614
--- /dev/null
+++ b/include/service.h
@@ -0,0 +1,108 @@
+/*
+ * service.h
+ *
+ * Created on: 27.08.2012
+ * Author: savop
+ */
+
+#ifndef SERVICE_H_
+#define SERVICE_H_
+
+#include <upnp/upnp.h>
+#include <string>
+
+namespace upnp {
+
+class cMediaServer;
+
+class cUPnPService {
+public:
+
+ struct Description {
+ Description(std::string, std::string, std::string, std::string, std::string);
+ std::string serviceType;
+ std::string serviceID;
+ std::string SCPDXML;
+ std::string controlDescriptor;
+ std::string eventSubscriberDescriptor;
+ };
+
+ cUPnPService(Description serviceDescription);
+
+ virtual ~cUPnPService();
+
+ virtual void Init(cMediaServer* server, UpnpDevice_Handle deviceHandle);
+
+ virtual int Subscribe(
+ Upnp_Subscription_Request* Request ///< Information about the subscription
+ ) = 0;
+
+ virtual int Execute(
+ Upnp_Action_Request* Request ///< Input and output parameters of an action
+ ) = 0;
+
+ const Description& GetServiceDescription() const { return mServiceDescription; };
+
+protected:
+ /**
+ * Sets an error on an action request
+ *
+ * This function puts a error message into the action request structure
+ * according to its error code
+ *
+ * @param Request the action request, to set the error for
+ * @param Error the error code of which the message should be obtained
+ */
+ virtual void SetError(
+ Upnp_Action_Request* Request, ///< the action request, to set the error for
+ int Error ///< the error code of which the message should be obtained
+ );
+ /**
+ * Parses an integer value
+ *
+ * This tool function parses an integer value from a given \em IXML document. It is searching
+ * for the very first occurance of the demanded item.
+ *
+ * @return Returns
+ * - \bc 0, if parsing was successful
+ * - \bc <0, if an error occured
+ *
+ * @param Document the document, which is parsed
+ * @param Item the demanded item
+ * @param Value the value of the item
+ */
+ int ParseIntegerValue(
+ IXML_Document* Document, ///< the document, which is parsed
+ std::string Item, ///< the demanded item
+ long& Value ///< the value of the item
+ );
+ /**
+ * Parses a string value
+ *
+ * This tool function parses a string value from a given \em IXML document. It is searching
+ * for the very first occurance of the demanded item.
+ *
+ * @return Returns
+ * - \bc 0, if parsing was successful
+ * - \bc <0, if an error occured
+ *
+ * @param Document the document, which is parsed
+ * @param Item the demanded item
+ * @param Value the value of the item
+ */
+ int ParseStringValue(
+ IXML_Document* Document, ///< the document, which is parsed
+ std::string Item, ///< the demanded item
+ std::string& Value ///< the value of the item
+ );
+
+ cMediaServer* mMediaServer;
+ UpnpDevice_Handle mDeviceHandle; ///< the UPnP device handle of the root device
+ Description mServiceDescription;
+
+};
+
+} // namespace upnp
+
+
+#endif /* SERVICE_H_ */
diff --git a/include/tools.h b/include/tools.h
new file mode 100644
index 0000000..b12f6be
--- /dev/null
+++ b/include/tools.h
@@ -0,0 +1,162 @@
+/*
+ * tools.h
+ *
+ * Created on: 03.08.2012
+ * Author: savop
+ */
+
+#ifndef TOOLS_H_
+#define TOOLS_H_
+
+#include <vector>
+#include <string>
+#include <iostream>
+#include <upnp/upnp.h>
+#include <string.h>
+#include <vdr/tools.h>
+#include <map>
+#include <list>
+
+using namespace std;
+
+#define KB(s) (s * 1024)
+#define MB(s) (s * 1024 * 1024)
+
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+
+#define CRLF "\r\n"
+
+#define NL "\n"
+
+#define att(s) strchr(s,'@')!=NULL?strchr(s,'@')+1:NULL
+#define prop(s) substr(s, 0, strchr(s,'@')-s)
+
+#define MAX_METADATA_LENGTH 1024
+
+/**
+ * creates a part of a string
+ *
+ * This creates a substring of a string which begins at the given offset and has the
+ * specified lenght.
+ *
+ * @return the new string
+ * @param str the full string
+ * @param offset the starting index
+ * @param length the length of the new string
+ */
+char* substr(const char* str, unsigned int offset, unsigned int length);
+
+/****************************************************
+ *
+ * Known Errors
+ *
+ ****************************************************/
+
+/* Errors 401-404, 501 are already defined in
+ * Intel SDK, however 403 MUST NOT USED.
+ */
+
+/****** 600 Common Action Errors ******/
+
+#define UPNP_SOAP_E_ARGUMENT_INVALID 600
+#define UPNP_SOAP_E_ARGUMENT_OUT_OF_RANGE 601
+#define UPNP_SOAP_E_ACTION_NOT_IMPLEMENTED 602
+#define UPNP_SOAP_E_OUT_OF_MEMORY 603
+#define UPNP_SOAP_E_HUMAN_INTERVENTION 604
+#define UPNP_SOAP_E_STRING_TO_LONG 605
+#define UPNP_SOAP_E_NOT_AUTHORIZED 606
+#define UPNP_SOAP_E_SIGNATURE_FAILURE 607
+#define UPNP_SOAP_E_SIGNATURE_MISSING 608
+#define UPNP_SOAP_E_NOT_ENCRYPTED 609
+#define UPNP_SOAP_E_INVALID_SEQUENCE 610
+#define UPNP_SOAP_E_INVALID_CONTROL_URL 611
+#define UPNP_SOAP_E_NO_SUCH_SESSION 612
+
+/****** 700 Action specific Errors ******/
+
+#define UPNP_CDS_E_NO_SUCH_OBJECT 701
+#define UPNP_CDS_E_INVALID_CURRENT_TAG 702
+#define UPNP_CDS_E_INVALID_NEW_TAG 703
+#define UPNP_CDS_E_REQUIRED_TAG 704
+#define UPNP_CDS_E_READ_ONLY_TAG 705
+#define UPNP_CDS_E_PARAMETER_MISMATCH 706
+#define UPNP_CDS_E_INVALID_SEARCH_CRITERIA 708
+#define UPNP_CDS_E_INVALID_SORT_CRITERIA 709
+#define UPNP_CDS_E_NO_SUCH_CONTAINER 710
+#define UPNP_CDS_E_RESTRICTED_OBJECT 711
+#define UPNP_CDS_E_BAD_METADATA 712
+#define UPNP_CDS_E_RESTRICTED_PARENT 713
+#define UPNP_CDS_E_NO_SUCH_SOURCE_RESOURCE 714
+#define UPNP_CDS_E_RESOURCE_ACCESS_DENIED 715
+#define UPNP_CDS_E_TRANSFER_BUSY 716
+#define UPNP_CDS_E_NO_SUCH_FILE_TRANSFER 717
+#define UPNP_CDS_E_NO_SUCH_DESTINATION_RESOURCE 718
+#define UPNP_CDS_E_DEST_RESOURCE_ACCESS_DENIED 719
+#define UPNP_CDS_E_CANT_PROCESS_REQUEST 720
+
+#define UPNP_CMS_E_INCOMPATIBLE_PROTOCOL_INFO 701
+#define UPNP_CMS_E_INCOMPATIBLE_DIRECTIONS 702
+#define UPNP_CMS_E_INSUFFICIENT_RESOURCES 703
+#define UPNP_CMS_E_LOCAL_RESTRICTIONS 704
+#define UPNP_CMS_E_ACCESS_DENIED 705
+#define UPNP_CMS_E_INVALID_CONNECTION_REFERENCE 706
+#define UPNP_CMS_E_NOT_IN_NETWORK 707
+
+namespace upnp {
+
+ typedef std::list<std::string> StringList;
+ typedef std::map<std::string, uint32_t> IdList;
+
+namespace tools {
+ string GetAddressByInterface(string Interface);
+ string GetNetworkInterfaceByIndex(int Index, bool skipLoop);
+ vector<string> GetNetworkInterfaces(bool skipLoop);
+
+ string ToString(long number);
+
+ string StringListToCSV(StringList list);
+ string IdListToCSV(IdList list);
+
+ string GenerateUUIDFromURL(string url);
+ string GenerateUUIDRandomly();
+}
+
+namespace ixml {
+ void XmlEscapeSpecialChars(string& doc);
+ /**
+ * First occurance of item
+ *
+ * Finds the first occurance of a specified item in a given \bc IXML document and returns its value.
+ * If an error occures, its code is stored in the last parameter \c 'error'.
+ *
+ * @return error the error code in case of an error
+ * @param doc the \c IXML document to be parsed
+ * @param item the item which shall be found
+ * @param the value of the item
+ */
+ int IxmlGetFirstDocumentItem( IN IXML_Document * doc, IN const string item, string& value );
+ /**
+ * Adds a property
+ *
+ * This adds a UPnP property to an \bc IXML document.
+ * The property must have the pattern "namespace:property@attribute".
+ *
+ * @return returns
+ * - \bc NULL, in case of an error
+ * - \bc the newly created property node or the node at which the attribute was
+ * appended to
+ * @param document the \c IXML document to put the parameter in
+ * @param node the specific node where to put the parameter
+ * @param upnpproperty the upnp property
+ * @param value the value of the upnp property
+ */
+ IXML_Element* IxmlAddProperty(IN IXML_Document* document, IN IXML_Element* node, IN const char* upnpproperty, IN const char* value );
+
+ IXML_Element* IxmlAddFilteredProperty(IN cStringList* Filter, IN IXML_Document* document, IN IXML_Element* node, IN const char* upnpproperty, IN const char* value );
+
+ IXML_Element* IxmlReplaceProperty(IN IXML_Document* document, IN IXML_Element* node, IN const char* upnpproperty, IN const char* newValue );
+}
+
+}
+
+#endif /* TOOLS_H_ */
diff --git a/include/webserver.h b/include/webserver.h
new file mode 100644
index 0000000..240d4d6
--- /dev/null
+++ b/include/webserver.h
@@ -0,0 +1,66 @@
+/*
+ * webserver.h
+ *
+ * Created on: 06.08.2012
+ * Author: savop
+ */
+
+#ifndef WEBSERVER_H_
+#define WEBSERVER_H_
+
+#include <tnt/tntnet.h>
+#include <stdint.h>
+#include <string>
+#include <vdr/thread.h>
+
+namespace upnp {
+
+ class cMediaServer;
+
+ class cWebserver {
+ public:
+ cWebserver(std::string address);
+ virtual ~cWebserver();
+
+ void SetWebserverRootDir(std::string rootDirectory, std::string staticContentUrl, std::string presentationUrl);
+ void SetServiceUrl(std::string descriptionUrl, std::string controlUrl);
+
+ void SetListenerPort(uint16_t port);
+
+ bool Initialise();
+
+ bool Start();
+ void Stop();
+
+ const std::string GetBaseUrl() const;
+ const std::string GetServiceUrl() const;
+ const std::string GetControlUrl() const;
+ const std::string GetStaticContentUrl() const;
+ const std::string GetPresentationUrl() const;
+
+ std::string GetListenerAddress() const { return mListenerAddress; }
+ uint16_t GetListenerPort() const { return mListenerPort; }
+
+ private:
+ tnt::Tntnet mApplication;
+ std::string mWebserverRootDir;
+ std::string mListenerAddress;
+ uint16_t mListenerPort;
+
+ std::string mServiceUrl;
+ std::string mControlUrl;
+ std::string mStaticContentUrl;
+ std::string mPresentationUrl;
+
+ class cWSThread : public cThread {
+ public:
+ cWSThread(cWebserver& webServer);
+ virtual void Action(void);
+ private:
+ cWebserver& mWebserver;
+ } mWebserverThread;
+ };
+
+};
+
+#endif /* WEBSERVER_H_ */
diff --git a/libvdr-upnp.so b/libvdr-upnp.so
new file mode 100755
index 0000000..da4f86a
--- /dev/null
+++ b/libvdr-upnp.so
Binary files differ
diff --git a/libvdr-upnp.so.1.7.27 b/libvdr-upnp.so.1.7.27
new file mode 100755
index 0000000..da4f86a
--- /dev/null
+++ b/libvdr-upnp.so.1.7.27
Binary files differ
diff --git a/media/mediaManager.cpp b/media/mediaManager.cpp
new file mode 100644
index 0000000..9740a0b
--- /dev/null
+++ b/media/mediaManager.cpp
@@ -0,0 +1,212 @@
+/*
+ * mediaManager.cpp
+ *
+ * Created on: 01.09.2012
+ * Author: savop
+ */
+
+#include "../include/plugin.h"
+#include "../include/media/mediaManager.h"
+#include "../include/server.h"
+#include <upnp/upnp.h>
+#include <sstream>
+#include <tntdb/result.h>
+
+namespace upnp {
+
+cMediaManager::cMediaManager()
+: mSystemUpdateID(0)
+, mDatabaseFile("metadata.db")
+{
+}
+
+cMediaManager::~cMediaManager(){
+}
+
+uint32_t cMediaManager::GetSystemUpdateID() const {
+ return mSystemUpdateID;
+}
+
+IdList cMediaManager::GetContainerUpdateIDs(bool unevented){
+ IdList list = mEventedContainerUpdateIDs;
+
+ if(!unevented)
+ mEventedContainerUpdateIDs.clear();
+
+ return list;
+}
+
+void cMediaManager::OnContainerUpdate(string containerID, long updateID){
+ ++mSystemUpdateID;
+
+ mEventedContainerUpdateIDs[containerID] = updateID;
+}
+
+StringList cMediaManager::GetSearchCapabilities() const {
+ // TODO: SearchCapabilities ermitteln und zurückgeben.
+ StringList list;
+
+ return list;
+}
+
+StringList cMediaManager::GetSortCapabilities() const {
+ // TODO: SortCapabilities ermitteln und zurückgeben.
+ StringList list;
+
+ return list;
+}
+
+StringList cMediaManager::GetSupportedProtocolInfos() const {
+ // TODO: ProtocolInfos ermitteln und zurückgeben.
+ StringList list;
+
+ return list;
+}
+
+int cMediaManager::Browse(BrowseRequest& request){
+ request.numberReturned = 0;
+ request.totalMatches = 0;
+ request.updateID = 0;
+
+ return UPNP_E_SUCCESS;
+}
+
+int cMediaManager::Search(SearchRequest& request){
+ request.numberReturned = 0;
+ request.totalMatches = 0;
+ request.updateID = 0;
+
+ return UPNP_E_SUCCESS;
+}
+
+cMediaManager::BrowseFlag cMediaManager::ToBrowseFlag(std::string browseFlag) {
+ if (browseFlag.compare("BrowseMetadata") == 0)
+ return CD_BROWSE_METADATA;
+ else if (browseFlag.compare("BrowseDirectChildren") == 0)
+ return CD_BROWSE_DIRECT_CHILDREN;
+ else
+ return NumBrowseFlags;
+}
+
+bool cMediaManager::Initialise(){
+ try {
+ stringstream ss;
+ ss << "sqlite:" << mDatabaseFile;
+
+ mConnection = tntdb::connect(ss.str());
+
+ dsyslog("UPNP\tPreparing database structure...");
+
+ if(CheckIntegrity()) return true;
+
+ mConnection.beginTransaction();
+
+ tntdb::Statement objectTable = mConnection.prepare(
+ "CREATE TABLE metadata"
+ "("
+ " objectID TEXT PRIMARY KEY,"
+ " parentID TEXT NOT NULL,"
+ " title TEXT NOT NULL,"
+ " class TEXT NOT NULL,"
+ " restricted INTEGER NOT NULL,"
+ " description TEXT,"
+ " ldescription TEXT,"
+ " date TEXT,"
+ " language TEXT,"
+ " channelNr INTEGER,"
+ " channelName TEXT,"
+ " scheduledStart TEXT,"
+ " scheduledEnd TEXT"
+ ")");
+
+ objectTable.execute();
+
+ tntdb::Statement detailsTable = mConnection.prepare(
+ "CREATE TABLE details"
+ "("
+ " propertyID INTEGER PRIMARY KEY,"
+ " objectID TEXT REFERENCES metadata (objectID) ON DELETE CASCADE ON UPDATE CASCADE,"
+ " property TEXT,"
+ " value TEXT"
+ ")");
+
+ detailsTable.execute();
+
+ tntdb::Statement resourcesTable = mConnection.prepare(
+ "CREATE TABLE resources"
+ "("
+ " resourceID INTEGER PRIMARY KEY,"
+ " objectID TEXT REFERENCES metadata (objectID) ON DELETE CASCADE ON UPDATE CASCADE,"
+ " resourceUri TEXT NOT NULL,"
+ " protocolInfo TEXT NOT NULL,"
+ " size INTEGER,"
+ " duration TEXT,"
+ " resolution TEXT,"
+ " bitrate INTEGER,"
+ " sampleFreq INTEGER,"
+ " bitsPerSample INTEGER,"
+ " nrAudioChannels INTEGER,"
+ " colorDepth INTEGER"
+ ")");
+
+ resourcesTable.execute();
+
+ tntdb::Statement rootContainer = mConnection.prepare(
+ "INSERT INTO metadata (objectID, parentID, title, class, restricted, description)"
+ " VALUES (:objectID, :parentID, :title, :class, :restricted, :description)"
+ );
+
+ rootContainer.setString("objectID", "0")
+ .setString("parentID", "-1")
+ .setString("title", cMediaServer::GetInstance()->GetServerDescription().friendlyName)
+ .setString("class", "object.container")
+ .setBool("restricted", true)
+ .setString("description", cMediaServer::GetInstance()->GetServerDescription().modelDescription)
+ .execute();
+
+ mConnection.commitTransaction();
+
+ return true;
+
+ } catch (const std::exception& e) {
+ esyslog("UPnP\tException occurred while initializing database: %s", e.what());
+
+ mConnection.rollbackTransaction();
+
+ return false;
+ }
+
+ return false;
+}
+
+bool cMediaManager::CheckIntegrity(){
+
+ tntdb::Statement checkTable = mConnection.prepare(
+ "SELECT name FROM sqlite_master WHERE type='table' AND name=:table;"
+ );
+
+ if( checkTable.setString("table", "metadata").select().empty() ) return false;
+ if( checkTable.setString("table", "details").select().empty() ) return false;
+ if( checkTable.setString("table", "resources").select().empty() ) return false;
+
+ tntdb::Statement checkObject = mConnection.prepare(
+ "SELECT objectID FROM metadata WHERE objectID='0' AND parentID='-1';"
+ );
+
+ if( checkObject.select().size() != 1 ) return false;
+
+ return true;
+}
+
+void cMediaManager::SetDatabaseFile(string file){
+ if(file.empty()) mDatabaseFile = "metadata.db";
+ else mDatabaseFile = file;
+}
+
+void cMediaManager::Action(){
+
+}
+
+} // namespace upnp
+
+
diff --git a/media/mediaManager.o b/media/mediaManager.o
new file mode 100644
index 0000000..aae1479
--- /dev/null
+++ b/media/mediaManager.o
Binary files differ
diff --git a/media/pluginManager.cpp b/media/pluginManager.cpp
new file mode 100644
index 0000000..232a9fe
--- /dev/null
+++ b/media/pluginManager.cpp
@@ -0,0 +1,262 @@
+/*
+ * pluginManager.cpp
+ *
+ * Created on: 05.09.2012
+ * Author: savop
+ */
+
+#include "../include/plugin.h"
+#include "../include/tools.h"
+#include <string>
+
+using namespace std;
+
+namespace upnp {
+
+bool cMetadata::SetObjectIDByUri(string uri){
+ return SetObjectID(tools::GenerateUUIDFromURL(uri));
+}
+
+bool cMetadata::SetParentIDByUri(string uri){
+ return SetParentID(tools::GenerateUUIDFromURL(uri));
+}
+
+bool cMetadata::SetObjectID(string objectID){
+ if(objectID.empty() || objectID.compare("0") == 0) return false;
+
+ this->objectID = objectID;
+
+ return true;
+};
+
+bool cMetadata::SetParentID(string parentID){
+ if(objectID.compare("-1") == 0) return false;
+
+ this->parentID = parentID;
+
+ return true;
+};
+
+bool cMetadata::SetTitle(string title){
+ this->title = title;
+
+ return true;
+};
+
+bool cMetadata::SetUpnpClass(string upnpClass){
+ //TODO: Validiere upnpClass.
+ this->upnpClass = upnpClass;
+
+ return true;
+};
+
+bool cMetadata::SetRestricted(bool restricted){
+ this->restricted = restricted;
+
+ return true;
+};
+
+bool cMetadata::SetDescription(string description){
+ this->description = description;
+
+ return true;
+};
+
+bool cMetadata::SetLongDescription(string longDescription){
+ this->longDescription = longDescription;
+
+ return true;
+};
+
+bool cMetadata::SetDate(string date){
+ this->date = date;
+
+ return true;
+};
+
+bool cMetadata::SetLanguage(string language){
+ this->language = language;
+
+ return true;
+};
+
+bool cMetadata::SetChannelNr(int channelNr){
+ this->channelNr = channelNr;
+
+ return true;
+};
+
+bool cMetadata::SetChannelName(string channelName){
+ this->channelName = channelName;
+
+ return true;
+};
+
+bool cMetadata::SetScheduledStart(string scheduledStart){
+ this->scheduledStart = scheduledStart;
+
+ return true;
+};
+
+bool cMetadata::SetScheduledEnd(string scheduledEnd){
+ this->scheduledEnd = scheduledEnd;
+
+ return true;
+};
+
+
+bool cMetadata::AddProperty(Property property){
+ properties.insert(pair<string, Property>(property.GetKey(), property));
+ return true;
+}
+
+bool cMetadata::SetProperty(Property property, int index){
+ PropertyRange ret = properties.equal_range(property.GetKey());
+
+ // No property with the given name found. Let's add it to the map.
+ if(ret.first == ret.second) return AddProperty(property);
+
+ int i = 0;
+ for(PropertyMap::iterator it = ret.first; it != ret.second; ++it){
+ if(i == index){
+ (*it).second = property;
+ return true;
+ }
+ ++i;
+ }
+
+ return false;
+
+}
+
+cMetadata::PropertyRange cMetadata::GetPropertiesByKey(string property) {
+ return properties.equal_range(property);
+}
+
+cMetadata::PropertyRange cMetadata::GetAllProperties() {
+ PropertyRange range(properties.begin(), properties.end());
+ return range;
+}
+
+cMetadata::Property& cMetadata::GetPropertyByKey(string property) {
+ return (*properties.find(property)).second;
+}
+
+cMetadata::Property::Property(string key, bool val)
+: key(key)
+{
+ value = val ? "true" : "false";
+}
+
+cMetadata::Property::Property(string key, string value)
+: key(key)
+, value(value)
+{
+}
+
+cMetadata::Property::Property(string key, long val)
+: key(key)
+{
+ value = tools::ToString(val);
+}
+
+string cMetadata::Property::GetString() const {
+ return value;
+}
+
+bool cMetadata::Property::GetBoolean() const {
+ return value.compare("true") == 0 ? true : false;
+}
+
+long cMetadata::Property::GetInteger() const {
+ return atol(value.c_str());
+}
+
+string cMetadata::Property::GetKey() const {
+ return key;
+}
+
+bool cMetadata::Resource::SetResourceUri(string resourceUri){
+ this->resourceUri = resourceUri;
+ return true;
+}
+
+bool cMetadata::Resource::SetProtocolInfo(string protocolInfo){
+ //TODO validiere protocolInfo.
+
+ this->protocolInfo = protocolInfo;
+ return true;
+}
+
+bool cMetadata::Resource::SetDuration(string duration){
+ this->duration = duration;
+ return true;
+}
+
+bool cMetadata::Resource::SetResolution(string resolution){
+ this->resolution = resolution;
+ return true;
+}
+
+bool cMetadata::Resource::SetBitrate(uint32_t bitrate){
+ this->bitrate = bitrate;
+ return true;
+}
+
+bool cMetadata::Resource::SetSize(uint32_t size){
+ this->size = size;
+ return true;
+}
+
+bool cMetadata::Resource::SetSampleFrequency(uint32_t sampleFrequency){
+ this->sampleFrequency = sampleFrequency;
+ return true;
+}
+
+bool cMetadata::Resource::SetBitsPerSample(uint32_t bitsPerSample){
+ this->bitsPerSample = bitsPerSample;
+ return true;
+}
+
+bool cMetadata::Resource::SetNrAudioChannels(uint32_t nrAudioChannels){
+ this->nrAudioChannels = nrAudioChannels;
+ return true;
+}
+
+bool cMetadata::Resource::SetColorDepth(uint32_t colorDepth){
+ this->colorDepth = colorDepth;
+ return true;
+}
+
+cMetadata* cUPnPResourceProvider::GetMetadata(string uri){
+
+ cMetadata* metadata = new cMetadata;
+
+ metadata->SetTitle(uri.substr(uri.find_last_of("/")+1));
+ metadata->SetUpnpClass("object.container");
+ metadata->SetObjectIDByUri(uri);
+ metadata->SetParentIDByUri(uri.substr(0,uri.find_last_of("/")));
+ metadata->SetRestricted(true);
+
+ return metadata;
+
+}
+
+string cUPnPResourceProvider::GetHTTPUri(string uri){
+ return string();
+}
+
+bool cUPnPResourceProvider::Open(string uri){
+ return false;
+}
+
+size_t cUPnPResourceProvider::Read(char* buf, size_t bufLen){
+ return -1;
+}
+
+void cUPnPResourceProvider::Close(){
+}
+
+} // namespace uünü
+
+
diff --git a/media/pluginManager.o b/media/pluginManager.o
new file mode 100644
index 0000000..ecfa185
--- /dev/null
+++ b/media/pluginManager.o
Binary files differ
diff --git a/media/profile.cpp b/media/profile.cpp
new file mode 100644
index 0000000..1b7b81c
--- /dev/null
+++ b/media/profile.cpp
@@ -0,0 +1,18 @@
+/*
+ * profile.cpp
+ *
+ * Created on: 05.08.2012
+ * Author: savop
+ */
+
+#include "../include/media/profile.h"
+
+using namespace upnp;
+
+image::cIcon image::DLNA_ICON_PNG_SM_24A = { "image/png", 48, 48, 24 };
+image::cIcon image::DLNA_ICON_PNG_LRG_24A = { "image/png", 120, 120, 24 };
+
+image::cIcon image::DLNA_ICON_JPEG_SM_24 = { "image/jpeg", 48, 48, 24 };
+image::cIcon image::DLNA_ICON_JPEG_LRG_24 = { "image/jpeg", 120, 120, 24 };
+
+
diff --git a/media/profile.o b/media/profile.o
new file mode 100644
index 0000000..0f9045f
--- /dev/null
+++ b/media/profile.o
Binary files differ
diff --git a/metadata.db b/metadata.db
new file mode 100644
index 0000000..308999f
--- /dev/null
+++ b/metadata.db
Binary files differ
diff --git a/po/upnp.pot b/po/upnp.pot
new file mode 100644
index 0000000..12fddb8
--- /dev/null
+++ b/po/upnp.pot
@@ -0,0 +1,21 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: vdr-upnp 0.0.1\n"
+"Report-Msgid-Bugs-To: <see README>\n"
+"POT-Creation-Date: 2012-09-13 19:11+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "The UPnP server is still running."
+msgstr ""
diff --git a/server/connection.cpp b/server/connection.cpp
new file mode 100644
index 0000000..bd2d78c
--- /dev/null
+++ b/server/connection.cpp
@@ -0,0 +1,139 @@
+/*
+ * connection.cpp
+ *
+ * Created on: 31.07.2012
+ * Author: savop
+ */
+
+#ifndef CONNECTION_CPP_
+#define CONNECTION_CPP_
+
+#include <stdlib.h>
+#include <limits.h>
+#include <time.h>
+#include "../include/connection.h"
+
+using namespace upnp;
+
+cVirtualConnection::cVirtualConnection(
+ int32_t connectionID,
+ const std::string& remoteProtocolInfo,
+ const std::string& peerConnectionManager,
+ int32_t peerConnectionID,
+ Direction direction) :
+ mRemoteProtocolInfo(remoteProtocolInfo),
+ mPeerConnectionManager(peerConnectionManager),
+ mPeerConnectionID(peerConnectionID),
+ mConnectionID(connectionID),
+ mAVTransportID(-1),
+ mRcsID(-1),
+ mDirection(direction),
+ mStatus(VC_OKAY)
+{
+}
+
+cVirtualConnection::cVirtualConnection(const cVirtualConnection & other) :
+ mRemoteProtocolInfo(other.mRemoteProtocolInfo),
+ mPeerConnectionManager(other.mPeerConnectionManager),
+ mPeerConnectionID(other.mPeerConnectionID),
+ mConnectionID(other.mConnectionID),
+ mAVTransportID(other.mAVTransportID),
+ mRcsID(other.mRcsID),
+ mDirection(other.mDirection),
+ mStatus(other.mStatus)
+{
+}
+
+cVirtualConnection & cVirtualConnection::operator =(const cVirtualConnection & other)
+{
+ mConnectionID = other.mConnectionID;
+ mAVTransportID = other.mAVTransportID;
+ mPeerConnectionID = other.mPeerConnectionID;
+ mRcsID = other.mRcsID;
+ mDirection = other.mDirection;
+ mStatus = other.mStatus;
+ mPeerConnectionManager = other.mPeerConnectionManager;
+ mRemoteProtocolInfo = other.mRemoteProtocolInfo;
+
+ return *this;
+}
+
+cVirtualConnection* cVirtualConnection::GenerateVirtualConnection(
+ const std::string & remoteProtocolInfo,
+ const std::string & peerConnectionManager,
+ int32_t peerConnectionID,
+ const std::string & direction)
+{
+ Direction eDirection;
+
+ if (direction.compare("Input")==0){
+ eDirection = VC_INPUT;
+ } else if (direction.compare("Output")==0){
+ eDirection = VC_OUTPUT;
+ } else {
+ return NULL;
+ }
+
+ cVirtualConnection* connnection = new cVirtualConnection(
+ cVirtualConnection::NextConnectionID(),
+ remoteProtocolInfo, peerConnectionManager,
+ peerConnectionID, eDirection );
+
+ return connnection;
+}
+
+cVirtualConnection* cVirtualConnection::GenerateVirtualConnection(
+ const std::string & remoteProtocolInfo,
+ const std::string & peerConnectionManager,
+ int32_t peerConnectionID,
+ Direction direction)
+{
+ cVirtualConnection* connnection = new cVirtualConnection(
+ cVirtualConnection::NextConnectionID(),
+ remoteProtocolInfo, peerConnectionManager,
+ peerConnectionID, direction );
+
+ return connnection;
+}
+
+void cVirtualConnection::DestroyVirtualConnection(cVirtualConnection* connection){
+ delete connection;
+}
+
+int32_t cVirtualConnection::NextConnectionID(){
+ static int32_t lastConnectionID = 0;
+
+ if(lastConnectionID == INT_MAX) lastConnectionID = 1;
+
+ return lastConnectionID++;
+}
+
+std::string cVirtualConnection::GetDirectionString() const {
+ switch (mDirection){
+ case VC_INPUT:
+ return "Input";
+ case VC_OUTPUT:
+ return "Output";
+ default:
+ return NULL;
+ }
+}
+
+std::string cVirtualConnection::GetStatusString() const {
+ switch (mStatus){
+ case VC_CONTENT_FORMAT_MISMATCH:
+ return "ContentFormatMismatch";
+ case VC_INSUFFICIENT_BANDWIDTH:
+ return "InsufficientBandwidth";
+ case VC_OKAY:
+ return "OK";
+ case VC_UNKNOWN:
+ return "Unknown";
+ case VC_UNRELIABLE_CHANNEL:
+ return "UnreliableChannel";
+ default:
+ return NULL;
+ }
+}
+
+#endif /* CONNECTION_CPP_ */
diff --git a/server/connection.o b/server/connection.o
new file mode 100644
index 0000000..03f6ec5
--- /dev/null
+++ b/server/connection.o
Binary files differ
diff --git a/server/connectionManager.cpp b/server/connectionManager.cpp
new file mode 100644
index 0000000..fd16d97
--- /dev/null
+++ b/server/connectionManager.cpp
@@ -0,0 +1,253 @@
+/*
+ * connectionManager.cpp
+ *
+ * Created on: 27.08.2012
+ * Author: savop
+ */
+
+#include "../include/connectionManager.h"
+#include "../include/tools.h"
+#include "../include/server.h"
+#include "../include/media/mediaManager.h"
+#include <vdr/i18n.h>
+#include <upnp/upnptools.h>
+#include <string>
+#include <sstream>
+
+namespace upnp {
+
+#define UPNP_CMS_ACTION_GETPROTOCOLINFO "GetProtocolInfo"
+#define UPNP_CMS_ACTION_GETCURRENTCONNECTIONIDS "GetCurrentConnectionIDs"
+#define UPNP_CMS_ACTION_GETCURRENTCONNECTIONINFO "GetCurrentConnectionInfo"
+#define UPNP_CMS_ACTION_PREPAREFORCONNECTION "PrepareForConnection"
+#define UPNP_CMS_ACTION_CONNECTIONCOMPLETE "ConnectionComplete"
+
+cConnectionManager::cConnectionManager()
+: cUPnPService(
+ cUPnPService::Description(
+ "urn:schemas-upnp-org:service:ConnectionManager:1",
+ "urn:upnp-org:serviceId:ConnectionManager",
+ "cms_scpd.xml",
+ "cms_control",
+ "cms_event"
+ ))
+{
+
+ cVirtualConnection* connection = cVirtualConnection::GenerateVirtualConnection(std::string(), std::string(),
+ -1, cVirtualConnection::VC_OUTPUT);
+ mVirtualConnections[connection->GetConnectionID()] = connection;
+}
+
+cConnectionManager::~cConnectionManager(){
+ DeleteConnections();
+}
+
+int cConnectionManager::Subscribe(Upnp_Subscription_Request* request){
+ IXML_Document* PropertySet = NULL;
+
+ std::string protocolInfo = tools::StringListToCSV(mMediaServer->GetManager().GetSupportedProtocolInfos());
+
+ /* The protocol infos which this server supports */
+ UpnpAddToPropertySet(&PropertySet, "SourceProtocolInfo", protocolInfo.c_str());
+ /* Not set, this field is only used by Media Renderers */
+ UpnpAddToPropertySet(&PropertySet, "SinkProtocolInfo", "");
+ /* The current connection IDs of all virtual connections */
+ UpnpAddToPropertySet(&PropertySet, "CurrentConnectionIDs", GetConnectionIDsCVS().c_str());
+ // Accept subscription
+ int ret = UpnpAcceptSubscriptionExt(this->mDeviceHandle, request->UDN, request->ServiceId, PropertySet, request->Sid);
+
+ if(ret != UPNP_E_SUCCESS){
+ esyslog("UPnP\tSubscription failed (Error code: %d)", ret);
+ }
+
+ ixmlDocument_free(PropertySet);
+ return ret;
+}
+
+int cConnectionManager::Execute(Upnp_Action_Request* request){
+ if (request == NULL) {
+ esyslog("UPnP\tCMS Action Handler - request is null");
+ return UPNP_E_BAD_REQUEST;
+ }
+
+ if(!strcmp(request->ActionName, UPNP_CMS_ACTION_GETPROTOCOLINFO))
+ return this->GetProtocolInfo(request);
+ if(!strcmp(request->ActionName, UPNP_CMS_ACTION_GETCURRENTCONNECTIONIDS))
+ return this->GetCurrentConnectionIDs(request);
+ if(!strcmp(request->ActionName, UPNP_CMS_ACTION_GETCURRENTCONNECTIONINFO))
+ return this->GetCurrentConnectionInfo(request);
+ if(!strcmp(request->ActionName, UPNP_CMS_ACTION_PREPAREFORCONNECTION))
+ return this->PrepareForConnection(request);
+ if(!strcmp(request->ActionName, UPNP_CMS_ACTION_CONNECTIONCOMPLETE))
+ return this->ConnectionComplete(request);
+
+ return UPNP_E_BAD_REQUEST;
+}
+
+int cConnectionManager::GetCurrentConnectionIDs(Upnp_Action_Request* request){
+ std::string IDs = this->GetConnectionIDsCVS();
+ if(IDs.empty()){
+ SetError(request, UPNP_E_INTERNAL_ERROR);
+ return request->ErrCode;
+ }
+
+ std::stringstream ss;
+
+ ss << "<u:" << request->ActionName << "Response xmlns:u=\"" << GetServiceDescription().serviceType << "\">"
+ << " <ConnectionIDs>" << IDs << "</ConnectionIDs>"
+ << "</u:" << request->ActionName << "Response>";
+
+ request->ActionResult = ixmlParseBuffer(ss.str().c_str());
+ request->ErrCode = UPNP_E_SUCCESS;
+ return request->ErrCode;
+}
+
+int cConnectionManager::GetCurrentConnectionInfo(Upnp_Action_Request* request){
+
+ long id;
+ if(ParseIntegerValue(request->ActionRequest, "ConnectionID", id) != UPNP_E_SUCCESS){
+ esyslog("UPnP\tInvalid arguments. ConnectionID missing or wrong");
+ SetError(request, UPNP_SOAP_E_INVALID_ARGS);
+ return request->ErrCode;
+ }
+ int32_t connectionID = id;
+
+ cVirtualConnection* connection = mVirtualConnections[connectionID];
+
+ if(connection == NULL){
+ esyslog("UPnP\tNo valid connection found with given ID=%d!", connectionID);
+ SetError(request, UPNP_CMS_E_INVALID_CONNECTION_REFERENCE);
+ return request->ErrCode;
+ }
+
+ std::stringstream ss;
+ ss << "<u:" << request->ActionName << "Response xmlns:u=\"" << GetServiceDescription().serviceType << "\">"
+ << " <ProtocolInfo>" << connection->GetRemoteProtocolInfo() << "</ProtocolInfo>"
+ << " <PeerConnectionManager>" << connection->GetPeerConnectionManager() << "</PeerConnectionManager>"
+ << " <PeerConnectionID>" << connection->GetPeerConnectionID() << "</PeerConnectionID>"
+ << " <Direction>" << connection->GetDirectionString() << "</Direction>"
+ << " <RcsID>" << connection->GetRcsID() << "</RcsID>"
+ << " <AVTransportID>" << connection->GetAVTransportID() << "</AVTransportID>"
+ << " <Status>" << connection->GetStatusString() << "</Status>"
+ << "</u:" << request->ActionName << "Response>";
+
+ request->ActionResult = ixmlParseBuffer(ss.str().c_str());
+ request->ErrCode = UPNP_E_SUCCESS;
+ return request->ErrCode;
+}
+
+int cConnectionManager::GetProtocolInfo(Upnp_Action_Request* request){
+ std::stringstream ss;
+
+ std::string protocolInfo = tools::StringListToCSV(mMediaServer->GetManager().GetSupportedProtocolInfos());
+
+ ss << "<u:" << request->ActionName << "Response xmlns:u=\"" << GetServiceDescription().serviceType << "\">"
+ << " <Source>" << protocolInfo.c_str() << "</Source>"
+ << " <Sink></Sink>"
+ << "</u:" << request->ActionName << "Response>";
+
+ request->ActionResult = ixmlParseBuffer(ss.str().c_str());
+ request->ErrCode = UPNP_E_SUCCESS;
+ return request->ErrCode;
+}
+
+int cConnectionManager::ConnectionComplete(Upnp_Action_Request* request){
+ SetError(request, UPNP_SOAP_E_ACTION_NOT_IMPLEMENTED);
+ return request->ErrCode;
+}
+
+int cConnectionManager::PrepareForConnection(Upnp_Action_Request* request){
+ SetError(request, UPNP_SOAP_E_ACTION_NOT_IMPLEMENTED);
+ return request->ErrCode;
+}
+
+const std::string cConnectionManager::GetConnectionIDsCVS(){
+ std::stringstream ss;
+
+ ConnectionList::iterator it = mVirtualConnections.begin();
+ ss << (*it).second->GetConnectionID();
+ for(++it; it != mVirtualConnections.end(); ++it){
+ ss << "," << (*it).second->GetConnectionID();
+ }
+
+ return ss.str();
+}
+
+void cConnectionManager::DeleteConnections(){
+ for(ConnectionList::iterator it = mVirtualConnections.begin(); it != mVirtualConnections.end();++it){
+ cVirtualConnection::DestroyVirtualConnection((*it).second);
+ }
+ mVirtualConnections.clear();
+}
+
+bool cConnectionManager::DeleteConnection(int32_t connectionID){
+ if(connectionID == 0){
+ esyslog("UPnP\tCannot delete default connection with connectionID = 0");
+ return false;
+ }
+
+ cVirtualConnection::DestroyVirtualConnection(mVirtualConnections[connectionID]);
+ mVirtualConnections.erase(connectionID);
+
+ return OnConnectionChange();
+}
+
+bool cConnectionManager::CreateConnection(const std::string & remoteProtocolInfo,
+ const std::string & peerConnectionManager,
+ int32_t peerConnectionID,
+ const std::string & direction)
+{
+ cVirtualConnection* connection = cVirtualConnection::GenerateVirtualConnection(
+ remoteProtocolInfo, peerConnectionManager,
+ peerConnectionID, direction);
+
+ mVirtualConnections[connection->GetConnectionID()] = connection;
+
+ return OnConnectionChange();
+}
+
+bool cConnectionManager::OnConnectionChange(){
+ IXML_Document* PropertySet = NULL;
+ UpnpAddToPropertySet(&PropertySet, "CurrentConnectionIDs", GetConnectionIDsCVS().c_str());
+ int ret = UpnpNotifyExt(mDeviceHandle, mMediaServer->GetDeviceUUID().c_str(), mServiceDescription.serviceID.c_str(), PropertySet);
+ ixmlDocument_free(PropertySet);
+
+ if(ret != UPNP_E_SUCCESS){
+ esyslog("UPnP\tState change notification failed (Error code: %d)",ret);
+ return false;
+ }
+
+ return true;
+}
+
+void cConnectionManager::SetError(Upnp_Action_Request* request, int error){
+ request->ErrCode = error;
+ switch(error){
+ case UPNP_CMS_E_INCOMPATIBLE_PROTOCOL_INFO:
+ strn0cpy(request->ErrStr,tr("Incompatible protocol info"),LINE_SIZE);
+ break;
+ case UPNP_CMS_E_INCOMPATIBLE_DIRECTIONS:
+ strn0cpy(request->ErrStr,tr("Incompatible directions"),LINE_SIZE);
+ break;
+ case UPNP_CMS_E_INSUFFICIENT_RESOURCES:
+ strn0cpy(request->ErrStr,tr("Insufficient network resources"),LINE_SIZE);
+ break;
+ case UPNP_CMS_E_LOCAL_RESTRICTIONS:
+ strn0cpy(request->ErrStr,tr("Local restrictions"),LINE_SIZE);
+ break;
+ case UPNP_CMS_E_ACCESS_DENIED:
+ strn0cpy(request->ErrStr,tr("Access denied"),LINE_SIZE);
+ break;
+ case UPNP_CMS_E_INVALID_CONNECTION_REFERENCE:
+ strn0cpy(request->ErrStr,tr("Invalid connection reference"),LINE_SIZE);
+ break;
+ case UPNP_CMS_E_NOT_IN_NETWORK:
+ strn0cpy(request->ErrStr,tr("Not in network"),LINE_SIZE);
+ break;
+ default:
+ cUPnPService::SetError(request, error);
+ break;
+ }
+}
+
+} // namespace upnp
diff --git a/server/connectionManager.o b/server/connectionManager.o
new file mode 100644
index 0000000..e3b3e75
--- /dev/null
+++ b/server/connectionManager.o
Binary files differ
diff --git a/server/contentDirectory.cpp b/server/contentDirectory.cpp
new file mode 100644
index 0000000..4855715
--- /dev/null
+++ b/server/contentDirectory.cpp
@@ -0,0 +1,439 @@
+/*
+ * contentDirectory.cpp
+ *
+ * Created on: 27.08.2012
+ * Author: savop
+ */
+
+#include "../include/contentDirectory.h"
+#include "../include/media/mediaManager.h"
+#include "../include/server.h"
+#include <vdr/i18n.h>
+#include <upnp/upnptools.h>
+#include <sstream>
+
+namespace upnp {
+
+#define UPNP_CDS_ACTION_SEARCHCAPABILITIES "GetSearchCapabilities"
+#define UPNP_CDS_ACTION_SORTCAPABILITIES "GetSortCapabilities"
+#define UPNP_CDS_ACTION_SYSTEMUPDATEID "GetSystemUpdateID"
+#define UPNP_CDS_ACTION_BROWSE "Browse"
+#define UPNP_CDS_ACTION_SEARCH "Search"
+#define UPNP_CDS_ACTION_CREATEOBJECT "CreateObject"
+#define UPNP_CDS_ACTION_DESTROYOBJECT "DestroyObject"
+#define UPNP_CDS_ACTION_UPDATEOBJECT "UpdateObject"
+#define UPNP_CDS_ACTION_IMPORTRESOURCE "ImportResource"
+#define UPNP_CDS_ACTION_EXPORTRESOURCE "ExportResource"
+#define UPNP_CDS_ACTION_STOPTRANSFERRES "StopTransferResource"
+#define UPNP_CDS_ACTION_TRANSFERPROGRESS "GetTransferProgress"
+#define UPNP_CDS_ACTION_DELETERESOURCE "DeleteResource"
+#define UPNP_CDS_ACTION_CREATEREFERENCE "CreateReference"
+
+cContentDirectory::cContentDirectory()
+: cUPnPService(
+ cUPnPService::Description(
+ "urn:schemas-upnp-org:service:ContentDirectory:1",
+ "urn:upnp-org:serviceId:ContentDirectory",
+ "cds_scpd.xml",
+ "cds_control",
+ "cds_event"
+ ))
+{
+
+}
+
+cContentDirectory::~cContentDirectory(){
+ Cancel(1);
+}
+
+void cContentDirectory::Init(cMediaServer* server, UpnpDevice_Handle deviceHandle){
+ cUPnPService::Init(server, deviceHandle);
+
+ Start();
+}
+
+int cContentDirectory::Subscribe(Upnp_Subscription_Request* Request){
+ IXML_Document* PropertySet = NULL;
+
+ /* The system update ID */
+ UpnpAddToPropertySet(&PropertySet, "SystemUpdateID",
+ tools::ToString(mMediaServer->GetManager().GetSystemUpdateID()).c_str());
+ /* The container update IDs as CSV list */
+ UpnpAddToPropertySet(&PropertySet, "ContainerUpdateIDs",
+ tools::IdListToCSV(mMediaServer->GetManager().GetContainerUpdateIDs()).c_str());
+ /* The transfer IDs, which are not supported, i.e. empty */
+ UpnpAddToPropertySet(&PropertySet, "TransferIDs", "");
+ // Accept subscription
+ int ret = UpnpAcceptSubscriptionExt(this->mDeviceHandle, Request->UDN, Request->ServiceId, PropertySet, Request->Sid);
+
+ if(ret != UPNP_E_SUCCESS){
+ esyslog("UPnP\tSubscription failed (Error code: %d)", ret);
+ }
+
+ ixmlDocument_free(PropertySet);
+ return ret;
+}
+
+void cContentDirectory::Action(){
+ int Retry = 5;
+ dsyslog("UPnP\tStart Content directory thread");
+ while(this->Running()){
+ IXML_Document* PropertySet = NULL;
+
+ /* The system update ID */
+ UpnpAddToPropertySet(&PropertySet, "SystemUpdateID",
+ tools::ToString(mMediaServer->GetManager().GetSystemUpdateID()).c_str());
+ /* The container update IDs as CSV list */
+ UpnpAddToPropertySet(&PropertySet, "ContainerUpdateIDs",
+ tools::IdListToCSV(mMediaServer->GetManager().GetContainerUpdateIDs()).c_str());
+ int ret = UpnpNotifyExt(this->mDeviceHandle, this->mMediaServer->GetDeviceUUID().c_str(),
+ this->mServiceDescription.serviceID.c_str(), PropertySet);
+ ixmlDocument_free(PropertySet);
+
+ if(ret != UPNP_E_SUCCESS){
+ Retry--;
+ esyslog("UPnP\tState change notification failed (Error code: %d)",ret);
+ esyslog("UPnP\t%d of %d notifications failed", (5-Retry), 5);
+ }
+ else {
+ Retry = 5;
+ }
+ if (!Retry){
+ esyslog("UPnP\tMaximum retries of notifications reached. Stopping...");
+ this->Cancel();
+ }
+ // Sleep 2 seconds
+ cCondWait::SleepMs(2000);
+ }
+}
+
+int cContentDirectory::Execute(Upnp_Action_Request* request){
+ if (request == NULL) {
+ esyslog("UPnP\tCMS Action Handler - request is null");
+ return UPNP_E_BAD_REQUEST;
+ }
+
+ if(!strcmp(request->ActionName, UPNP_CDS_ACTION_SEARCHCAPABILITIES))
+ return this->GetSearchCapabilities(request);
+ if(!strcmp(request->ActionName, UPNP_CDS_ACTION_SORTCAPABILITIES))
+ return this->GetSortCapabilities(request);
+ if(!strcmp(request->ActionName, UPNP_CDS_ACTION_SYSTEMUPDATEID))
+ return this->GetSystemUpdateID(request);
+ if(!strcmp(request->ActionName, UPNP_CDS_ACTION_BROWSE))
+ return this->Browse(request);
+ if(!strcmp(request->ActionName, UPNP_CDS_ACTION_SEARCH))
+ return this->Search(request);
+ if(!strcmp(request->ActionName, UPNP_CDS_ACTION_CREATEOBJECT))
+ return this->CreateObject(request);
+ if(!strcmp(request->ActionName, UPNP_CDS_ACTION_DESTROYOBJECT))
+ return this->DestroyObject(request);
+ if(!strcmp(request->ActionName, UPNP_CDS_ACTION_UPDATEOBJECT))
+ return this->UpdateObject(request);
+ if(!strcmp(request->ActionName, UPNP_CDS_ACTION_IMPORTRESOURCE))
+ return this->ImportResource(request);
+ if(!strcmp(request->ActionName, UPNP_CDS_ACTION_EXPORTRESOURCE))
+ return this->ExportResource(request);
+ if(!strcmp(request->ActionName, UPNP_CDS_ACTION_STOPTRANSFERRES))
+ return this->StopTransferResource(request);
+ if(!strcmp(request->ActionName, UPNP_CDS_ACTION_TRANSFERPROGRESS))
+ return this->GetTransferProgress(request);
+ if(!strcmp(request->ActionName, UPNP_CDS_ACTION_DELETERESOURCE))
+ return this->DeleteResource(request);
+ if(!strcmp(request->ActionName, UPNP_CDS_ACTION_CREATEREFERENCE))
+ return this->CreateReference(request);
+
+ return UPNP_E_BAD_REQUEST;
+}
+
+int cContentDirectory::CreateReference(Upnp_Action_Request* request){
+ SetError(request, UPNP_SOAP_E_ACTION_NOT_IMPLEMENTED);
+ return request->ErrCode;
+}
+
+int cContentDirectory::DeleteResource(Upnp_Action_Request* request){
+ SetError(request, UPNP_SOAP_E_ACTION_NOT_IMPLEMENTED);
+ return request->ErrCode;
+}
+
+int cContentDirectory::GetTransferProgress(Upnp_Action_Request* request){
+ SetError(request, UPNP_SOAP_E_ACTION_NOT_IMPLEMENTED);
+ return request->ErrCode;
+}
+
+int cContentDirectory::StopTransferResource(Upnp_Action_Request* request){
+ SetError(request, UPNP_SOAP_E_ACTION_NOT_IMPLEMENTED);
+ return request->ErrCode;
+}
+
+int cContentDirectory::ExportResource(Upnp_Action_Request* request){
+ SetError(request, UPNP_SOAP_E_ACTION_NOT_IMPLEMENTED);
+ return request->ErrCode;
+}
+
+int cContentDirectory::ImportResource(Upnp_Action_Request* request){
+ SetError(request, UPNP_SOAP_E_ACTION_NOT_IMPLEMENTED);
+ return request->ErrCode;
+}
+
+int cContentDirectory::UpdateObject(Upnp_Action_Request* request){
+ SetError(request, UPNP_SOAP_E_ACTION_NOT_IMPLEMENTED);
+ return request->ErrCode;
+}
+
+int cContentDirectory::DestroyObject(Upnp_Action_Request* request){
+ SetError(request, UPNP_SOAP_E_ACTION_NOT_IMPLEMENTED);
+ return request->ErrCode;
+}
+
+int cContentDirectory::CreateObject(Upnp_Action_Request* request){
+ SetError(request, UPNP_SOAP_E_ACTION_NOT_IMPLEMENTED);
+ return request->ErrCode;
+}
+
+int cContentDirectory::Search(Upnp_Action_Request* request){
+ cMediaManager::SearchRequest searchRequest;
+
+ if(this->ParseIntegerValue(request->ActionRequest, "ContainerID", searchRequest.objectID)){
+ esyslog("UPnP\tInvalid arguments. ObjectID missing or wrong");
+ this->SetError(request, UPNP_SOAP_E_INVALID_ARGS);
+ return request->ErrCode;
+ }
+
+ if(this->ParseStringValue(request->ActionRequest, "BrowseFlag", searchRequest.searchCriteria)){
+ esyslog("UPnP\tInvalid arguments. Search criteria missing or wrong");
+ this->SetError(request, UPNP_SOAP_E_INVALID_ARGS);
+ return request->ErrCode;
+ }
+
+ if(this->ParseStringValue(request->ActionRequest, "Filter", searchRequest.filter)){
+ esyslog("UPnP\tInvalid arguments. Filter missing or wrong");
+ this->SetError(request, UPNP_SOAP_E_INVALID_ARGS);
+ return request->ErrCode;
+ }
+
+ long startIndex;
+ if(this->ParseIntegerValue(request->ActionRequest, "StartingIndex", startIndex)){
+ esyslog("UPnP\tInvalid arguments. Starting index missing or wrong");
+ this->SetError(request, UPNP_SOAP_E_INVALID_ARGS);
+ return request->ErrCode;
+ }
+ searchRequest.startIndex = startIndex;
+
+ long requestCount;
+ if(this->ParseIntegerValue(request->ActionRequest, "RequestedCount", requestCount)){
+ esyslog("UPnP\tInvalid arguments. Requested count missing or wrong");
+ this->SetError(request, UPNP_SOAP_E_INVALID_ARGS);
+ return request->ErrCode;
+ }
+ searchRequest.requestCount = requestCount;
+
+ if(this->ParseStringValue(request->ActionRequest, "SortCriteria", searchRequest.sortCriteria)){
+ esyslog("UPnP\tInvalid arguments. Sort criteria missing or wrong");
+ this->SetError(request, UPNP_SOAP_E_INVALID_ARGS);
+ return request->ErrCode;
+ }
+
+ int ret = mMediaServer->GetManager().Search(searchRequest);
+ if(ret!=UPNP_E_SUCCESS){
+ esyslog("UPnP\tError while browsing. Code: %d", ret);
+ this->SetError(request, ret);
+ return request->ErrCode;
+ }
+
+ ixml::XmlEscapeSpecialChars(searchRequest.result);
+
+ std::stringstream ss;
+
+ ss << "<u:" << request->ActionName << "Response xmlns:u=\"" << GetServiceDescription().serviceType << "\">"
+ << " <Result>" << searchRequest.result << "</Result>"
+ << " <NumberReturned>" << searchRequest.numberReturned << "</NumberReturned>"
+ << " <TotalMatches>" << searchRequest.totalMatches << "</TotalMatches>"
+ << " <UpdateID>" << searchRequest.updateID << "</UpdateID>"
+ << "</u:" << request->ActionName << "Response>";
+
+ request->ActionResult = ixmlParseBuffer(ss.str().c_str());
+ request->ErrCode = UPNP_E_SUCCESS;
+
+ return request->ErrCode;
+}
+
+int cContentDirectory::Browse(Upnp_Action_Request* request){
+ cMediaManager::BrowseRequest browseRequest;
+
+ if(this->ParseIntegerValue(request->ActionRequest, "ObjectID", browseRequest.objectID)){
+ esyslog("UPnP\tInvalid arguments. ObjectID missing or wrong");
+ this->SetError(request, UPNP_SOAP_E_INVALID_ARGS);
+ return request->ErrCode;
+ }
+
+ std::string browseFlag;
+ if(this->ParseStringValue(request->ActionRequest, "BrowseFlag", browseFlag)){
+ esyslog("UPnP\tInvalid arguments. Browse flag missing or wrong");
+ this->SetError(request, UPNP_SOAP_E_INVALID_ARGS);
+ return request->ErrCode;
+ }
+ if((browseRequest.browseMetadata = cMediaManager::ToBrowseFlag(browseFlag)) == cMediaManager::NumBrowseFlags){
+ esyslog("UPnP\tInvalid arguments. Browse flag invalid");
+ this->SetError(request, UPNP_SOAP_E_INVALID_ARGS);
+ return request->ErrCode;
+ }
+
+ if(this->ParseStringValue(request->ActionRequest, "Filter", browseRequest.filter)){
+ esyslog("UPnP\tInvalid arguments. Filter missing or wrong");
+ this->SetError(request, UPNP_SOAP_E_INVALID_ARGS);
+ return request->ErrCode;
+ }
+
+ long startIndex;
+ if(this->ParseIntegerValue(request->ActionRequest, "StartingIndex", startIndex)){
+ esyslog("UPnP\tInvalid arguments. Starting index missing or wrong");
+ this->SetError(request, UPNP_SOAP_E_INVALID_ARGS);
+ return request->ErrCode;
+ }
+ browseRequest.startIndex = startIndex;
+
+ long requestCount;
+ if(this->ParseIntegerValue(request->ActionRequest, "RequestedCount", requestCount)){
+ esyslog("UPnP\tInvalid arguments. Requested count missing or wrong");
+ this->SetError(request, UPNP_SOAP_E_INVALID_ARGS);
+ return request->ErrCode;
+ }
+ browseRequest.requestCount = requestCount;
+
+ if(this->ParseStringValue(request->ActionRequest, "SortCriteria", browseRequest.sortCriteria)){
+ esyslog("UPnP\tInvalid arguments. Sort criteria missing or wrong");
+ this->SetError(request, UPNP_SOAP_E_INVALID_ARGS);
+ return request->ErrCode;
+ }
+
+ int ret = mMediaServer->GetManager().Browse(browseRequest);
+ if(ret!=UPNP_E_SUCCESS){
+ esyslog("UPnP\tError while browsing. Code: %d", ret);
+ this->SetError(request, ret);
+ return request->ErrCode;
+ }
+
+ ixml::XmlEscapeSpecialChars(browseRequest.result);
+
+ std::stringstream ss;
+
+ ss << "<u:" << request->ActionName << "Response xmlns:u=\"" << GetServiceDescription().serviceType << "\">"
+ << " <Result>" << browseRequest.result << "</Result>"
+ << " <NumberReturned>" << browseRequest.numberReturned << "</NumberReturned>"
+ << " <TotalMatches>" << browseRequest.totalMatches << "</TotalMatches>"
+ << " <UpdateID>" << browseRequest.updateID << "</UpdateID>"
+ << "</u:" << request->ActionName << "Response>";
+
+ request->ActionResult = ixmlParseBuffer(ss.str().c_str());
+ request->ErrCode = UPNP_E_SUCCESS;
+
+ return request->ErrCode;
+}
+
+int cContentDirectory::GetSystemUpdateID(Upnp_Action_Request* request){
+ std::stringstream ss;
+
+ ss << "<u:" << request->ActionName << "Response xmlns:u=\"" << GetServiceDescription().serviceType << "\">"
+ << " <Id>" << mMediaServer->GetManager().GetSystemUpdateID() << "</Id>"
+ << "</u:" << request->ActionName << "Response>";
+
+ request->ActionResult = ixmlParseBuffer(ss.str().c_str());
+ request->ErrCode = UPNP_E_SUCCESS;
+
+ return request->ErrCode;
+}
+
+int cContentDirectory::GetSortCapabilities(Upnp_Action_Request* request){
+ std::stringstream ss;
+
+ ss << "<u:" << request->ActionName << "Response xmlns:u=\"" << GetServiceDescription().serviceType << "\">"
+ << " <SortCaps>" << tools::StringListToCSV(mMediaServer->GetManager().GetSortCapabilities()) << "</SortCaps>"
+ << "</u:" << request->ActionName << "Response>";
+
+ request->ActionResult = ixmlParseBuffer(ss.str().c_str());
+ request->ErrCode = UPNP_E_SUCCESS;
+
+ return request->ErrCode;
+}
+
+int cContentDirectory::GetSearchCapabilities(Upnp_Action_Request* request){
+ std::stringstream ss;
+
+ ss << "<u:" << request->ActionName << "Response xmlns:u=\"" << GetServiceDescription().serviceType << "\">"
+ << " <SearchCaps>" << tools::StringListToCSV(mMediaServer->GetManager().GetSearchCapabilities()) << "</SearchCaps>"
+ << "</u:" << request->ActionName << "Response>";
+
+ request->ActionResult = ixmlParseBuffer(ss.str().c_str());
+ request->ErrCode = UPNP_E_SUCCESS;
+
+ return request->ErrCode;
+}
+
+void cContentDirectory::SetError(Upnp_Action_Request* request, int error){
+ request->ErrCode = error;
+ switch(error){
+ case UPNP_CDS_E_BAD_METADATA:
+ strn0cpy(request->ErrStr,tr("Bad metadata"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_CANT_PROCESS_REQUEST:
+ strn0cpy(request->ErrStr,tr("Cannot process the request"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_DEST_RESOURCE_ACCESS_DENIED:
+ strn0cpy(request->ErrStr,tr("Destination resource access denied"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_INVALID_CURRENT_TAG:
+ strn0cpy(request->ErrStr,tr("Invalid current tag"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_INVALID_NEW_TAG:
+ strn0cpy(request->ErrStr,tr("Invalid new tag"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_INVALID_SEARCH_CRITERIA:
+ strn0cpy(request->ErrStr,tr("Invalid or unsupported search criteria"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_INVALID_SORT_CRITERIA:
+ strn0cpy(request->ErrStr,tr("Invalid or unsupported sort criteria"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_NO_SUCH_CONTAINER:
+ strn0cpy(request->ErrStr,tr("No such container"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_NO_SUCH_DESTINATION_RESOURCE:
+ strn0cpy(request->ErrStr,tr("No such destination resource"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_NO_SUCH_FILE_TRANSFER:
+ strn0cpy(request->ErrStr,tr("No such file transfer"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_NO_SUCH_OBJECT:
+ strn0cpy(request->ErrStr,tr("No such objectID"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_NO_SUCH_SOURCE_RESOURCE:
+ strn0cpy(request->ErrStr,tr("No such source resource"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_PARAMETER_MISMATCH:
+ strn0cpy(request->ErrStr,tr("Parameter mismatch"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_READ_ONLY_TAG:
+ strn0cpy(request->ErrStr,tr("Read only tag"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_REQUIRED_TAG:
+ strn0cpy(request->ErrStr,tr("Required tag"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_RESOURCE_ACCESS_DENIED:
+ strn0cpy(request->ErrStr,tr("Resource access denied"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_RESTRICTED_OBJECT:
+ strn0cpy(request->ErrStr,tr("Restricted object"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_RESTRICTED_PARENT:
+ strn0cpy(request->ErrStr,tr("Restricted parent"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_TRANSFER_BUSY:
+ strn0cpy(request->ErrStr,tr("Transfer busy"),LINE_SIZE);
+ break;
+ default:
+ cUPnPService::SetError(request, error);
+ break;
+ }
+ return;
+}
+
+} // namespace upnp
diff --git a/server/contentDirectory.o b/server/contentDirectory.o
new file mode 100644
index 0000000..488881a
--- /dev/null
+++ b/server/contentDirectory.o
Binary files differ
diff --git a/server/server.cpp b/server/server.cpp
new file mode 100644
index 0000000..a211c1c
--- /dev/null
+++ b/server/server.cpp
@@ -0,0 +1,337 @@
+/*
+ * server.cpp
+ *
+ * Created on: 31.07.2012
+ * Author: savop
+ */
+
+#include <vdr/tools.h>
+#include <string>
+#include <sstream>
+#include "../include/server.h"
+#include "../include/service.h"
+#include "../include/tools.h"
+#include "../include/media/mediaManager.h"
+#include "../upnp.h"
+
+using namespace upnp;
+using namespace std;
+
+cMediaServer* cMediaServer::GetInstance(){
+ static cMediaServer server;
+ return &server;
+}
+
+cMediaServer::serviceMap cMediaServer::mServices;
+
+cMediaServer::cMediaServer()
+: mServerDescription("VDR UPnP/DLNA MS", "Denis Loh", "http://upnp.vdr-developer.org",
+ DESCRIPTION, "VDR UPnP/DLNA MS", VERSION,
+ "http://projects.vdr-developer.org/projects/plg-upnp/files", VERSION)
+, mDeviceHandle(0)
+, mAnnounceMaxAge(1800)
+, mMaxContentLength(KB(20))
+, mIsRunning(false)
+, mWebserver(NULL)
+, mMediaManager(NULL)
+{
+ mServerIcons.push_back(ServerIcon(image::DLNA_ICON_PNG_SM_24A, "images/upnpIconSm.png"));
+ mServerIcons.push_back(ServerIcon(image::DLNA_ICON_PNG_LRG_24A, "images/upnpIconLrg.png"));
+ mServerIcons.push_back(ServerIcon(image::DLNA_ICON_JPEG_SM_24, "images/upnpIconSm.jpeg"));
+ mServerIcons.push_back(ServerIcon(image::DLNA_ICON_JPEG_LRG_24, "images/upnpIconLrg.jpeg"));
+}
+
+cMediaServer::~cMediaServer(){
+ delete mWebserver;
+ delete mMediaManager;
+}
+
+bool cMediaServer::IsRunning() const {
+ return mIsRunning;
+}
+
+bool cMediaServer::Start(){
+
+ isyslog("UPnP\tStarting UPnP media server");
+
+ int ret;
+
+ // Disable internal webserver, we don't need it.
+ UpnpEnableWebserver(false);
+
+ if(!mWebserver->Start()){
+ esyslog("UPnP\tFailed to start the web server");
+ return false;
+ }
+
+ isyslog("UPnP\tRegistering UPnP media server");
+
+ string description = GetDeviceDescriptionUrl();
+
+ ret = UpnpRegisterRootDevice2(UPNPREG_URL_DESC,
+ description.c_str(),
+ description.size(),
+ 0,
+ &cMediaServer::ActionCallback,
+ this,
+ &mDeviceHandle);
+
+ if(ret != UPNP_E_SUCCESS){
+ esyslog("UPnP\tFailed to register the device. Error code: %d", ret);
+ return false;
+ }
+
+ ret = UpnpUnRegisterRootDevice(mDeviceHandle);
+ if (ret != UPNP_E_SUCCESS) {
+ esyslog("UPnP\tUnregistering old devices failed");
+ return false;
+ }
+
+ ret = UpnpRegisterRootDevice2(UPNPREG_URL_DESC,
+ description.c_str(),
+ description.size(),
+ 0,
+ &cMediaServer::ActionCallback,
+ this,
+ &mDeviceHandle);
+
+ if(ret != UPNP_E_SUCCESS){
+ esyslog("UPnP\tFailed to register the device. Error code: %d", ret);
+ return false;
+ }
+
+ isyslog("UPnP\tInitialising services...");
+ for(serviceMap::iterator it = cMediaServer::mServices.begin(); it != cMediaServer::mServices.end(); ++it){
+ isyslog("UPnP\t...%s", (*it).second->GetServiceDescription().serviceType.c_str());
+ (*it).second->Init(this, mDeviceHandle);
+ }
+
+ //send first advertisments
+ isyslog("UPnP\tSend first advertisements to publish start in network");
+ ret = UpnpSendAdvertisement(mDeviceHandle, GetAnnounceMaxAge());
+ if (ret != UPNP_E_SUCCESS) {
+ esyslog("UPnP\tError while sending first advertisments - Errorcode: %d", ret);
+ return false;
+ }
+
+ mIsRunning = true;
+
+ return IsRunning();
+}
+
+bool cMediaServer::Stop(){
+
+ isyslog("UPnP\tStopping UPnP media server");
+
+ int ret = 0;
+
+ UpnpUnRegisterRootDevice(mDeviceHandle);
+ if (ret != UPNP_E_SUCCESS) {
+ esyslog("UPnP\tError while sending first advertisments - Errorcode: %d", ret);
+ return false;
+ }
+
+ UpnpFinish();
+
+ if(mWebserver){
+ mWebserver->Stop();
+
+ delete mWebserver;
+ mWebserver = NULL;
+ }
+
+ if(mMediaManager){
+ delete mMediaManager;
+ mMediaManager = NULL;
+ }
+
+ mIsRunning = false;
+
+ return !IsRunning();
+}
+
+bool cMediaServer::Initialize(){
+ isyslog("UPnP\tInitializing UPnP media server");
+
+ string address = mCurrentConfiguration.bindToAddress
+ ? mCurrentConfiguration.address
+ : tools::GetAddressByInterface(mCurrentConfiguration.interface);
+
+ if(!address.compare("0.0.0.0"))
+ address = tools::GetAddressByInterface(tools::GetNetworkInterfaceByIndex(0, true));
+
+ int ret = 0;
+
+ ret = UpnpInit(address.c_str(), mCurrentConfiguration.port);
+
+ if(ret != UPNP_E_SUCCESS && ret != UPNP_E_INIT){
+ esyslog("UPnP\tFailed to initialise UPnP media server. Error code: %d", ret);
+ return false;
+ }
+
+ mWebserver = new cWebserver(GetServerIPAddress());
+
+ if(mCurrentConfiguration.expertSettings){
+
+ if(mCurrentConfiguration.maxContentLength)
+ SetMaxContentLength(mCurrentConfiguration.maxContentLength);
+
+ if(mCurrentConfiguration.announceMaxAge)
+ SetAnnounceMaxAge(mCurrentConfiguration.announceMaxAge);
+
+ if(!mCurrentConfiguration.webServerRoot.empty())
+ mWebserver->SetWebserverRootDir(mCurrentConfiguration.webServerRoot,
+ mCurrentConfiguration.staticContentURL,
+ mCurrentConfiguration.presentationURL);
+
+ if(mCurrentConfiguration.webServerPort)
+ mWebserver->SetListenerPort(mCurrentConfiguration.webServerPort);
+ }
+
+ mMediaManager = new cMediaManager();
+
+ ret = UpnpSetMaxContentLength(GetMaxContentLength());
+
+ if(ret != UPNP_E_SUCCESS){
+ esyslog("UPnP\tFailed to set max. content length of SOAP messages. Error code: %d", ret);
+ return false;
+ }
+
+ isyslog("UPnP\tInitialising webserver");
+ if(!mWebserver->Initialise()){
+ esyslog("UPnP\tFailed to initialise the web server.");
+ return false;
+ }
+
+ isyslog("UPnP\tInitialising media manager");
+ if(!mMediaManager->Initialise()){
+ esyslog("UPnP\tFailed to initialise the media manager.");
+ return false;
+ }
+
+ return true;
+}
+
+void cMediaServer::SetAnnounceMaxAge(int announceMaxAge){
+ mAnnounceMaxAge = (announceMaxAge != 0) ? announceMaxAge : 1800;
+}
+
+void cMediaServer::SetMaxContentLength(size_t maxContentLength){
+ mMaxContentLength = (maxContentLength != 0) ? maxContentLength : KB(20);
+}
+
+void cMediaServer::SetConfiguration(upnp::cConfig newConfig){
+ mCurrentConfiguration = newConfig;
+}
+
+upnp::cConfig cMediaServer::GetConfiguration() const {
+ return mCurrentConfiguration;
+}
+
+const char* cMediaServer::GetServerIPAddress() const {
+ return UpnpGetServerIpAddress();
+}
+
+uint16_t cMediaServer::GetServerPort() const {
+ return UpnpGetServerPort();
+}
+
+string cMediaServer::GetDeviceDescriptionUrl() const {
+ return mWebserver->GetBaseUrl() + mCurrentConfiguration.serviceURL + "deviceDescription.xml";
+}
+
+void cMediaServer::RegisterService(cUPnPService* service){
+ if(service != NULL){
+ cout << "Registered service: " << service->GetServiceDescription().serviceType << endl;
+ mServices[service->GetServiceDescription().serviceID] = service;
+ }
+}
+
+int cMediaServer::ActionCallback(Upnp_EventType eventtype, void *event, void *cookie){
+ Upnp_Subscription_Request* eventRequest = NULL;
+ Upnp_Action_Request* actionRequest = NULL;
+
+ cMediaServer* mediaServer = (cMediaServer*)cookie;
+
+ //check committed event variable
+ if (event == NULL) {
+ esyslog("UPnP\tUPnP Callback - NULL request");
+ return UPNP_E_BAD_REQUEST;
+ }
+
+ cUPnPService* service;
+
+ switch(eventtype){
+ case UPNP_CONTROL_ACTION_REQUEST:
+ actionRequest = (Upnp_Action_Request*) event;
+
+ dsyslog("UPnP\tAction request: %s", actionRequest->ActionName);
+
+ if(!mediaServer->CheckDeviceUUID(actionRequest->DevUDN)){
+ esyslog("UPnP\tUPnP Callback - action request not for this device");
+ return UPNP_E_BAD_REQUEST;
+ }
+
+ service = cMediaServer::mServices[actionRequest->ServiceID];
+
+ if(service == NULL){
+ esyslog("UPnP\tCallback - unsupported service called for control");
+ return UPNP_E_BAD_REQUEST;
+ }
+
+ return service->Execute(actionRequest);
+
+ case UPNP_EVENT_SUBSCRIPTION_REQUEST:
+ eventRequest = (Upnp_Subscription_Request*) event;
+
+ dsyslog("UPnP\tSubscription request: %s", eventRequest->ServiceId);
+
+ if(!mediaServer->CheckDeviceUUID(eventRequest->UDN)){
+ esyslog("UPnP\tUPnP Callback - event request not for this device");
+ return UPNP_E_BAD_REQUEST;
+ }
+
+ service = cMediaServer::mServices[eventRequest->ServiceId];
+
+ if(service == NULL){
+ esyslog("UPnP\tCallback - unsupported service called for eventing");
+ return UPNP_E_BAD_REQUEST;
+ }
+
+ return service->Subscribe(eventRequest);
+
+ default:
+ esyslog("UPnP\tUPnP Action Callback - Unsupported Event");
+ return UPNP_E_BAD_REQUEST;
+ }
+
+}
+
+const char* cMediaServer::RuntimeException::what() const throw() {
+ return "Runtime error: media server is not running";
+}
+
+bool cMediaServer::CheckDeviceUUID(string deviceUUID) const {
+ return deviceUUID.compare(mCurrentConfiguration.deviceUUID) == 0;
+}
+
+cMediaServer::Description::Description(
+ string fn, string m, string murl,
+ string mod, string mon, string mono,
+ string mourl, string sno)
+: friendlyName(fn)
+, manufacturer(m)
+, manufacturerURL(murl)
+, modelDescription(mod)
+, modelName(mon)
+, modelNumber(mono)
+, modelURL(mourl)
+, serialNumber(sno)
+{
+}
+
+cMediaServer::ServerIcon::ServerIcon(image::cIcon profile, string filename)
+: profile(profile)
+, filename(filename)
+{
+}
diff --git a/server/server.o b/server/server.o
new file mode 100644
index 0000000..f603bbb
--- /dev/null
+++ b/server/server.o
Binary files differ
diff --git a/server/service.cpp b/server/service.cpp
new file mode 100644
index 0000000..7185170
--- /dev/null
+++ b/server/service.cpp
@@ -0,0 +1,117 @@
+/*
+ * service.cpp
+ *
+ * Created on: 27.08.2012
+ * Author: savop
+ */
+
+#include "../include/server.h"
+#include "../include/service.h"
+#include "../include/tools.h"
+#include <vdr/i18n.h>
+
+namespace upnp {
+
+cUPnPService::cUPnPService(Description serviceDescription)
+: mMediaServer(NULL)
+, mDeviceHandle(0)
+, mServiceDescription(serviceDescription)
+{
+ cMediaServer::RegisterService(this);
+}
+
+cUPnPService::~cUPnPService(){}
+
+void cUPnPService::Init(cMediaServer* server, UpnpDevice_Handle deviceHandle){
+ mMediaServer = server;
+ mDeviceHandle = deviceHandle;
+}
+
+void cUPnPService::SetError(Upnp_Action_Request* request, int error){
+ request->ErrCode = error;
+ switch(error){
+ case UPNP_SOAP_E_INVALID_ACTION:
+ strn0cpy(request->ErrStr,tr("Invalid action"),LINE_SIZE);
+ break;
+ case UPNP_SOAP_E_INVALID_ARGS:
+ strn0cpy(request->ErrStr,tr("Invalid args"),LINE_SIZE);
+ break;
+ case UPNP_SOAP_E_INVALID_VAR:
+ strn0cpy(request->ErrStr,tr("Invalid var"),LINE_SIZE);
+ break;
+ case UPNP_SOAP_E_ACTION_FAILED:
+ strn0cpy(request->ErrStr,tr("Action failed"),LINE_SIZE);
+ break;
+ case UPNP_SOAP_E_ARGUMENT_INVALID:
+ strn0cpy(request->ErrStr,tr("Argument value invalid"),LINE_SIZE);
+ break;
+ case UPNP_SOAP_E_ARGUMENT_OUT_OF_RANGE:
+ strn0cpy(request->ErrStr,tr("Argument value out of range"),LINE_SIZE);
+ break;
+ case UPNP_SOAP_E_ACTION_NOT_IMPLEMENTED:
+ strn0cpy(request->ErrStr,tr("Optional action not implemented"),LINE_SIZE);
+ break;
+ case UPNP_SOAP_E_OUT_OF_MEMORY:
+ strn0cpy(request->ErrStr,tr("Out of memory"),LINE_SIZE);
+ break;
+ case UPNP_SOAP_E_HUMAN_INTERVENTION:
+ strn0cpy(request->ErrStr,tr("Human intervention required"),LINE_SIZE);
+ break;
+ case UPNP_SOAP_E_STRING_TO_LONG:
+ strn0cpy(request->ErrStr,tr("String argument to long"),LINE_SIZE);
+ break;
+ case UPNP_SOAP_E_NOT_AUTHORIZED:
+ strn0cpy(request->ErrStr,tr("Action not authorized"),LINE_SIZE);
+ break;
+ case UPNP_SOAP_E_SIGNATURE_FAILURE:
+ strn0cpy(request->ErrStr,tr("Signature failure"),LINE_SIZE);
+ break;
+ case UPNP_SOAP_E_SIGNATURE_MISSING:
+ strn0cpy(request->ErrStr,tr("Signature missing"),LINE_SIZE);
+ break;
+ case UPNP_SOAP_E_NOT_ENCRYPTED:
+ strn0cpy(request->ErrStr,tr("Not encrypted"),LINE_SIZE);
+ break;
+ case UPNP_SOAP_E_INVALID_SEQUENCE:
+ strn0cpy(request->ErrStr,tr("Invalid sequence"),LINE_SIZE);
+ break;
+ case UPNP_SOAP_E_INVALID_CONTROL_URL:
+ strn0cpy(request->ErrStr,tr("Invalid control URL"),LINE_SIZE);
+ break;
+ case UPNP_SOAP_E_NO_SUCH_SESSION:
+ strn0cpy(request->ErrStr,tr("No such session"),LINE_SIZE);
+ break;
+ case UPNP_SOAP_E_OUT_OF_SYNC:
+ default:
+ strn0cpy(request->ErrStr,tr("Unknown error code. Contact the device manufacturer"),LINE_SIZE);
+ break;
+ }
+}
+
+int cUPnPService::ParseIntegerValue(IN IXML_Document* Document, IN std::string Item, OUT long& Value){
+ std::string Val;
+ int error = ixml::IxmlGetFirstDocumentItem(Document, Item, Val);
+
+ if(error) return error;
+ else {
+ Value = atol(Val.c_str());
+ return 0;
+ }
+}
+
+int cUPnPService::ParseStringValue(IN IXML_Document* Document, IN std::string Item, OUT std::string& Value){
+ return ixml::IxmlGetFirstDocumentItem(Document, Item, Value);
+}
+
+cUPnPService::Description::Description(string type, string id, string scpd, string control, string event)
+: serviceType(type)
+, serviceID(id)
+, SCPDXML(scpd)
+, controlDescriptor(control)
+, eventSubscriberDescriptor(event)
+{
+}
+
+} // namespace upnp
+
+
diff --git a/server/service.o b/server/service.o
new file mode 100644
index 0000000..a130cda
--- /dev/null
+++ b/server/service.o
Binary files differ
diff --git a/server/webserver.cpp b/server/webserver.cpp
new file mode 100644
index 0000000..ce8acda
--- /dev/null
+++ b/server/webserver.cpp
@@ -0,0 +1,166 @@
+/*
+ * webserver.cpp
+ *
+ * Created on: 06.08.2012
+ * Author: savop
+ */
+
+#include "../include/webserver.h"
+#include "../upnp.h"
+#include <sstream>
+
+namespace upnp {
+
+cWebserver::cWebserver(std::string address)
+: mListenerAddress(address)
+, mListenerPort(7649)
+, mWebserverThread(*this)
+{
+ SetWebserverRootDir(string(), string(), string());
+ SetServiceUrl("services/", string());
+}
+
+cWebserver::~cWebserver(){
+
+}
+
+bool cWebserver::Start(){
+ return mWebserverThread.Start();
+}
+
+void cWebserver::Stop(){
+ try {
+ mApplication.shutdown();
+ } catch (const std::exception& e){
+ esyslog("UPnP\tError while stopping web server: %s", e.what());
+ }
+}
+
+bool cWebserver::Initialise(){
+
+ try {
+ // Map static contents
+ stringstream ss1, ss2;
+
+ mApplication.listen(mListenerAddress.c_str(), mListenerPort);
+
+ mApplication.mapUrl("^/$", "index");
+
+ mApplication.mapUrl("^/index.html", "index");
+
+ ss1.clear(); ss1.str(string());
+ ss1 << "^/" << mServiceUrl << "([^/.]+).xml$";
+
+ mApplication.mapUrl(ss1.str(), "$1");
+
+ // Map static contents
+ ss1.clear(); ss1.str(string());
+ ss1 << "^/" << mStaticContentUrl << "(.*)";
+
+ ss2.clear(); ss2.str(string());
+ ss2 << mWebserverRootDir << "$1";
+
+ mApplication.mapUrl(ss1.str(), ss2.str(), "static@tntnet");
+
+ // Map static contents
+ ss1.clear(); ss1.str(string());
+ ss1 << "^/" << mServiceUrl << "([^/.]+)";
+
+ ss2.clear(); ss2.str(string());
+ ss2 << mServiceUrl << "$1";
+
+ mApplication.mapUrl(ss1.str(), ss2.str(), "serviceRedirect");
+
+ isyslog("UPnP\tUsing %s for static content delivery.", mWebserverRootDir.c_str());
+
+ } catch (const std::exception& e){
+ esyslog("UPnP\tError while initialising web server: %s", e.what());
+ return false;
+ }
+
+ return true;
+}
+
+void cWebserver::SetListenerPort(uint16_t port){
+ if(mWebserverThread.Active()) return;
+
+ mListenerPort = port ? port : 7649;
+}
+
+void cWebserver::SetWebserverRootDir(std::string rootDirectory, std::string staticContentUrl, std::string presentationUrl){
+ if(mWebserverThread.Active()) return;
+
+ if(rootDirectory.empty())
+ mWebserverRootDir = std::string(cPluginUpnp::ConfigDirectory(PLUGIN_NAME_I18N)) + "/httpdocs/";
+ else
+ mWebserverRootDir = rootDirectory;
+
+ if(staticContentUrl.empty())
+ mStaticContentUrl = "http/";
+ else
+ mStaticContentUrl = staticContentUrl;
+
+ if(presentationUrl.empty())
+ mPresentationUrl = "index.html";
+ else
+ mPresentationUrl = presentationUrl;
+}
+
+void cWebserver::SetServiceUrl(std::string descriptionUrl, std::string controlUrl){
+ if(mWebserverThread.Active()) return;
+
+ if(descriptionUrl.empty())
+ mServiceUrl = "services/";
+ else
+ mServiceUrl = descriptionUrl;
+
+ if(controlUrl.empty()){
+ stringstream s;
+ s << "http://" << UpnpGetServerIpAddress() << ":" << UpnpGetServerPort() << "/" << mServiceUrl;
+
+ mControlUrl = s.str();
+ } else {
+ mControlUrl = controlUrl;
+ }
+}
+
+const std::string cWebserver::GetBaseUrl() const {
+ stringstream s;
+ s << "http://" << mListenerAddress << ":" << mListenerPort << "/";
+
+ return s.str();
+}
+
+const std::string cWebserver::GetServiceUrl() const {
+ return mServiceUrl;
+}
+
+const std::string cWebserver::GetControlUrl() const {
+ return mControlUrl;
+}
+
+const std::string cWebserver::GetPresentationUrl() const {
+ return mPresentationUrl;
+}
+
+const std::string cWebserver::GetStaticContentUrl() const {
+ return mStaticContentUrl;
+}
+
+cWebserver::cWSThread::cWSThread(cWebserver& webServer)
+: mWebserver(webServer)
+{
+
+}
+
+void cWebserver::cWSThread::Action(){
+ try {
+ mWebserver.mApplication.run();
+ } catch (const std::exception& e){
+ esyslog("UPnP\tError while starting web server: %s", e.what());
+ }
+}
+
+} // namespace upnp
+
+
diff --git a/server/webserver.o b/server/webserver.o
new file mode 100644
index 0000000..c6789ea
--- /dev/null
+++ b/server/webserver.o
Binary files differ
diff --git a/upnp.cpp b/upnp.cpp
new file mode 100644
index 0000000..63064f4
--- /dev/null
+++ b/upnp.cpp
@@ -0,0 +1,90 @@
+/*
+ * upnp.c: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#include <iostream>
+#include "upnp.h"
+#include "include/connection.h"
+
+using namespace std;
+using namespace upnp;
+
+cPluginUpnp::cPluginUpnp(void)
+{
+ mMediaServer = cMediaServer::GetInstance();
+}
+
+cPluginUpnp::~cPluginUpnp()
+{
+ // Clean up after yourself!
+}
+
+const char *cPluginUpnp::CommandLineHelp(void)
+{
+ // Return a string that describes all known command line options.
+ return NULL;
+}
+
+bool cPluginUpnp::ProcessArgs(int argc, char *argv[])
+{
+ // Implement command line argument processing here if applicable.
+ return true;
+}
+
+bool cPluginUpnp::Initialize(void)
+{
+ // Initialize any background activities the plugin shall perform.
+ return mMediaServer->Initialize();
+}
+
+bool cPluginUpnp::Start(void)
+{
+ // Start any background activities the plugin shall perform.
+ return mMediaServer->Start();
+}
+
+void cPluginUpnp::Stop(void)
+{
+ // Stop any background activities the plugin is performing.
+ mMediaServer->Stop();
+}
+
+void cPluginUpnp::Housekeeping(void)
+{
+ // Perform any cleanup or other regular tasks.
+}
+
+void cPluginUpnp::MainThreadHook(void)
+{
+ // Perform actions in the context of the main program thread.
+ // WARNING: Use with great care - see PLUGINS.html!
+}
+
+cString cPluginUpnp::Active(void)
+{
+ // Return a message string if shutdown should be postponed
+ if(mMediaServer->IsRunning()){
+ isyslog("UPnP\tPlugin is still active. Shutdown is postponed.");
+ return tr("The UPnP server is still running.");
+ }
+
+ return NULL;
+}
+
+cMenuSetupPage *cPluginUpnp::SetupMenu(void)
+{
+ // Return a setup menu in case the plugin supports one.
+ return NULL;
+}
+
+bool cPluginUpnp::SetupParse(const char *Name, const char *Value)
+{
+ // Parse your own setup parameters and store their values.
+ return false;
+}
+
+VDRPLUGINCREATOR(cPluginUpnp); // Don't touch this!
diff --git a/upnp.h b/upnp.h
new file mode 100644
index 0000000..a2d54de
--- /dev/null
+++ b/upnp.h
@@ -0,0 +1,41 @@
+/*
+ * upnp.h
+ *
+ * Created on: 31.07.2012
+ * Author: savop
+ */
+
+#ifndef UPNP_H_
+#define UPNP_H_
+
+#include <vdr/plugin.h>
+#include "include/server.h"
+
+using namespace upnp;
+
+static const char *VERSION = "0.0.1";
+static const char *DESCRIPTION = "UPnP/DLNA compliant Media Server functionality for VDR";
+
+class cPluginUpnp : public cPlugin {
+private:
+ // Add any member variables or functions you may need here.
+ cMediaServer* mMediaServer;
+public:
+ cPluginUpnp(void);
+ virtual ~cPluginUpnp();
+ virtual const char *Version(void) { return VERSION; }
+ virtual const char *Description(void) { return DESCRIPTION; }
+ virtual const char *CommandLineHelp(void);
+ virtual bool ProcessArgs(int argc, char *argv[]);
+ virtual bool Initialize(void);
+ virtual bool Start(void);
+ virtual void Stop(void);
+ virtual void Housekeeping(void);
+ virtual void MainThreadHook(void);
+ virtual cString Active(void);
+ virtual cMenuSetupPage *SetupMenu(void);
+ virtual bool SetupParse(const char *Name, const char *Value);
+};
+
+
+#endif /* UPNP_H_ */
diff --git a/upnp.o b/upnp.o
new file mode 100644
index 0000000..94c58e0
--- /dev/null
+++ b/upnp.o
Binary files differ