diff options
-rw-r--r-- | .cproject | 93 | ||||
-rw-r--r-- | .dependencies | 0 | ||||
-rw-r--r-- | .project | 27 | ||||
-rw-r--r-- | .settings/language.settings.xml | 11 | ||||
-rw-r--r-- | .settings/org.eclipse.cdt.core.prefs | 15 | ||||
-rw-r--r-- | COPYING | 340 | ||||
-rw-r--r-- | HISTORY | 6 | ||||
-rw-r--r-- | Makefile | 136 | ||||
-rw-r--r-- | README | 15 | ||||
-rw-r--r-- | common/config.cpp | 31 | ||||
-rw-r--r-- | common/config.o | bin | 0 -> 65336 bytes | |||
-rw-r--r-- | common/tools.cpp | 465 | ||||
-rw-r--r-- | common/tools.o | bin | 0 -> 579528 bytes | |||
-rw-r--r-- | httpdocs/images/upnpIconLrg.jpeg | bin | 0 -> 6620 bytes | |||
-rw-r--r-- | httpdocs/images/upnpIconLrg.png | bin | 0 -> 17309 bytes | |||
-rw-r--r-- | httpdocs/images/upnpIconSm.jpeg | bin | 0 -> 2416 bytes | |||
-rw-r--r-- | httpdocs/images/upnpIconSm.png | bin | 0 -> 4769 bytes | |||
-rw-r--r-- | httptnt/cds_scpd.ecpp | 152 | ||||
-rw-r--r-- | httptnt/cds_scpd.o | bin | 0 -> 206016 bytes | |||
-rw-r--r-- | httptnt/cms_scpd.ecpp | 140 | ||||
-rw-r--r-- | httptnt/cms_scpd.o | bin | 0 -> 207584 bytes | |||
-rw-r--r-- | httptnt/deviceDescription.ecpp | 88 | ||||
-rw-r--r-- | httptnt/deviceDescription.o | bin | 0 -> 295912 bytes | |||
-rw-r--r-- | include/config.h | 126 | ||||
-rw-r--r-- | include/connection.h | 84 | ||||
-rw-r--r-- | include/connectionManager.h | 70 | ||||
-rw-r--r-- | include/contentDirectory.h | 58 | ||||
-rw-r--r-- | include/media/mediaManager.h | 87 | ||||
-rw-r--r-- | include/media/profile.h | 50 | ||||
-rw-r--r-- | include/plugin.h | 484 | ||||
-rw-r--r-- | include/plugin.h.txt | 486 | ||||
-rw-r--r-- | include/pluginManager.h | 20 | ||||
-rw-r--r-- | include/server.h | 110 | ||||
-rw-r--r-- | include/service.h | 108 | ||||
-rw-r--r-- | include/tools.h | 162 | ||||
-rw-r--r-- | include/webserver.h | 66 | ||||
-rwxr-xr-x | libvdr-upnp.so | bin | 0 -> 1772649 bytes | |||
-rwxr-xr-x | libvdr-upnp.so.1.7.27 | bin | 0 -> 1772649 bytes | |||
-rw-r--r-- | media/mediaManager.cpp | 212 | ||||
-rw-r--r-- | media/mediaManager.o | bin | 0 -> 387416 bytes | |||
-rw-r--r-- | media/pluginManager.cpp | 262 | ||||
-rw-r--r-- | media/pluginManager.o | bin | 0 -> 269696 bytes | |||
-rw-r--r-- | media/profile.cpp | 18 | ||||
-rw-r--r-- | media/profile.o | bin | 0 -> 12960 bytes | |||
-rw-r--r-- | metadata.db | bin | 0 -> 7168 bytes | |||
-rw-r--r-- | po/upnp.pot | 21 | ||||
-rw-r--r-- | server/connection.cpp | 139 | ||||
-rw-r--r-- | server/connection.o | bin | 0 -> 57880 bytes | |||
-rw-r--r-- | server/connectionManager.cpp | 253 | ||||
-rw-r--r-- | server/connectionManager.o | bin | 0 -> 313200 bytes | |||
-rw-r--r-- | server/contentDirectory.cpp | 439 | ||||
-rw-r--r-- | server/contentDirectory.o | bin | 0 -> 232952 bytes | |||
-rw-r--r-- | server/server.cpp | 337 | ||||
-rw-r--r-- | server/server.o | bin | 0 -> 327792 bytes | |||
-rw-r--r-- | server/service.cpp | 117 | ||||
-rw-r--r-- | server/service.o | bin | 0 -> 84424 bytes | |||
-rw-r--r-- | server/webserver.cpp | 166 | ||||
-rw-r--r-- | server/webserver.o | bin | 0 -> 549288 bytes | |||
-rw-r--r-- | upnp.cpp | 90 | ||||
-rw-r--r-- | upnp.h | 41 | ||||
-rw-r--r-- | upnp.o | bin | 0 -> 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="upnp""/> + </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="upnp""/> + </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 @@ -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. @@ -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 @@ -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 Binary files differnew file mode 100644 index 0000000..d1fa07b --- /dev/null +++ b/common/config.o 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("€"); break; + case L'"': buffer.append("""); break; + case L'&': buffer.append("&"); break; + case L'<': buffer.append("<"); break; + case L'>': buffer.append(">"); break; + case L'¡': buffer.append("¡"); break; + case L'¢': buffer.append("¢"); break; + case L'£': buffer.append("£"); break; + case L'¤': buffer.append("¤"); break; + case L'¥': buffer.append("¥"); break; + case L'¦': buffer.append("¦"); break; + case L'§': buffer.append("§"); break; + case L'¨': buffer.append("¨"); break; + case L'©': buffer.append("©"); break; + case L'ª': buffer.append("ª"); break; + case L'¬': buffer.append("¬"); break; + case L'': buffer.append("­"); break; + case L'®': buffer.append("®"); break; + case L'¯': buffer.append("¯"); break; + case L'°': buffer.append("°"); break; + case L'±': buffer.append("±"); break; + case L'²': buffer.append("²"); break; + case L'³': buffer.append("³"); break; + case L'´': buffer.append("´"); break; + case L'µ': buffer.append("µ"); break; + case L'¶': buffer.append("¶"); break; + case L'·': buffer.append("·"); break; + case L'¸': buffer.append("¸"); break; + case L'¹': buffer.append("¹"); break; + case L'º': buffer.append("º"); break; + case L'»': buffer.append("»"); break; + case L'«': buffer.append("«"); break; + case L'¼': buffer.append("¼"); break; + case L'½': buffer.append("½"); break; + case L'¾': buffer.append("¾"); break; + case L'¿': buffer.append("¿"); break; + case L'À': buffer.append("À"); break; + case L'Á': buffer.append("Á"); break; + case L'Â': buffer.append("Â"); break; + case L'Ã': buffer.append("Ã"); break; + case L'Ä': buffer.append("Ä"); break; + case L'Å': buffer.append("Å"); break; + case L'Æ': buffer.append("Æ"); break; + case L'Ç': buffer.append("Ç"); break; + case L'È': buffer.append("È"); break; + case L'É': buffer.append("É"); break; + case L'Ê': buffer.append("Ê"); break; + case L'Ë': buffer.append("Ë"); break; + case L'Ì': buffer.append("Ì"); break; + case L'Í': buffer.append("Í"); break; + case L'Î': buffer.append("Î"); break; + case L'Ï': buffer.append("Ï"); break; + case L'Ð': buffer.append("Ð"); break; + case L'Ñ': buffer.append("Ñ"); break; + case L'Ò': buffer.append("Ò"); break; + case L'Ó': buffer.append("Ó"); break; + case L'Ô': buffer.append("Ô"); break; + case L'Õ': buffer.append("Õ"); break; + case L'Ö': buffer.append("Ö"); break; + case L'×': buffer.append("×"); break; + case L'Ø': buffer.append("Ø"); break; + case L'Ù': buffer.append("Ù"); break; + case L'Ú': buffer.append("Ú"); break; + case L'Û': buffer.append("Û"); break; + case L'Ü': buffer.append("Ü"); break; + case L'Ý': buffer.append("Ý"); break; + case L'Þ': buffer.append("Þ"); break; + case L'ß': buffer.append("ß"); break; + case L'à': buffer.append("à"); break; + case L'á': buffer.append("á"); break; + case L'â': buffer.append("â"); break; + case L'ã': buffer.append("ã"); break; + case L'ä': buffer.append("ä"); break; + case L'å': buffer.append("å"); break; + case L'æ': buffer.append("æ"); break; + case L'ç': buffer.append("ç"); break; + case L'è': buffer.append("è"); break; + case L'é': buffer.append("é"); break; + case L'ê': buffer.append("ê"); break; + case L'ë': buffer.append("ë"); break; + case L'ì': buffer.append("ì"); break; + case L'í': buffer.append("í"); break; + case L'î': buffer.append("î"); break; + case L'ï': buffer.append("ï"); break; + case L'ð': buffer.append("ð"); break; + case L'ñ': buffer.append("ñ"); break; + case L'ò': buffer.append("ò"); break; + case L'ó': buffer.append("ó"); break; + case L'ô': buffer.append("ô"); break; + case L'õ': buffer.append("õ"); break; + case L'ö': buffer.append("ö"); break; + case L'÷': buffer.append("÷"); break; + case L'ø': buffer.append("ø"); break; + case L'ù': buffer.append("ù"); break; + case L'ú': buffer.append("ú"); break; + case L'û': buffer.append("û"); break; + case L'ü': buffer.append("ü"); break; + case L'ý': buffer.append("ý"); break; + case L'þ': buffer.append("þ"); 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 Binary files differnew file mode 100644 index 0000000..d89989e --- /dev/null +++ b/common/tools.o diff --git a/httpdocs/images/upnpIconLrg.jpeg b/httpdocs/images/upnpIconLrg.jpeg Binary files differnew file mode 100644 index 0000000..6421267 --- /dev/null +++ b/httpdocs/images/upnpIconLrg.jpeg diff --git a/httpdocs/images/upnpIconLrg.png b/httpdocs/images/upnpIconLrg.png Binary files differnew file mode 100644 index 0000000..d93e7c0 --- /dev/null +++ b/httpdocs/images/upnpIconLrg.png diff --git a/httpdocs/images/upnpIconSm.jpeg b/httpdocs/images/upnpIconSm.jpeg Binary files differnew file mode 100644 index 0000000..b163f05 --- /dev/null +++ b/httpdocs/images/upnpIconSm.jpeg diff --git a/httpdocs/images/upnpIconSm.png b/httpdocs/images/upnpIconSm.png Binary files differnew file mode 100644 index 0000000..d4320f4 --- /dev/null +++ b/httpdocs/images/upnpIconSm.png 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 Binary files differnew file mode 100644 index 0000000..40f462c --- /dev/null +++ b/httptnt/cds_scpd.o 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 Binary files differnew file mode 100644 index 0000000..4110788 --- /dev/null +++ b/httptnt/cms_scpd.o 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 Binary files differnew file mode 100644 index 0000000..b14f268 --- /dev/null +++ b/httptnt/deviceDescription.o 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 Binary files differnew file mode 100755 index 0000000..da4f86a --- /dev/null +++ b/libvdr-upnp.so diff --git a/libvdr-upnp.so.1.7.27 b/libvdr-upnp.so.1.7.27 Binary files differnew file mode 100755 index 0000000..da4f86a --- /dev/null +++ b/libvdr-upnp.so.1.7.27 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 Binary files differnew file mode 100644 index 0000000..aae1479 --- /dev/null +++ b/media/mediaManager.o 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 Binary files differnew file mode 100644 index 0000000..ecfa185 --- /dev/null +++ b/media/pluginManager.o 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 Binary files differnew file mode 100644 index 0000000..0f9045f --- /dev/null +++ b/media/profile.o diff --git a/metadata.db b/metadata.db Binary files differnew file mode 100644 index 0000000..308999f --- /dev/null +++ b/metadata.db 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 Binary files differnew file mode 100644 index 0000000..03f6ec5 --- /dev/null +++ b/server/connection.o 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 Binary files differnew file mode 100644 index 0000000..e3b3e75 --- /dev/null +++ b/server/connectionManager.o 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 Binary files differnew file mode 100644 index 0000000..488881a --- /dev/null +++ b/server/contentDirectory.o 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 Binary files differnew file mode 100644 index 0000000..f603bbb --- /dev/null +++ b/server/server.o 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 Binary files differnew file mode 100644 index 0000000..a130cda --- /dev/null +++ b/server/service.o 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 Binary files differnew file mode 100644 index 0000000..c6789ea --- /dev/null +++ b/server/webserver.o 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! @@ -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_ */ Binary files differ |