summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhorchi <vdr@jwendel.de>2017-03-05 16:48:30 +0100
committerhorchi <vdr@jwendel.de>2017-03-05 16:48:30 +0100
commitbf558fd824c7ab8c794448f718b364ad403a706a (patch)
treee0ba2269c08ccc9c9bd9c336df06b1fa6fce5ec6
downloadvdr-plugin-pin-0.1.16.tar.gz
vdr-plugin-pin-0.1.16.tar.bz2
git init0.1.16
-rw-r--r--.gitignore8
-rw-r--r--COPYING340
-rw-r--r--HISTORY130
l---------Makefile1
-rw-r--r--Makefile.pre-1.7.35125
-rw-r--r--Makefile.since-1.7.35131
-rw-r--r--README62
-rw-r--r--brdclocks.c125
-rw-r--r--def.c41
-rw-r--r--def.h178
-rw-r--r--fskcheck.c172
-rw-r--r--locks.c274
-rw-r--r--locks.h182
-rw-r--r--menu.c518
-rw-r--r--menu.h151
-rw-r--r--msgreceiver.c141
-rw-r--r--patches/epgsearch-0.9.19.diff85
-rw-r--r--patches/epgsearch-0.9.20.diff221
-rw-r--r--patches/epgsearch-0.9.21.diff194
-rw-r--r--patches/vdr-1.4.5.diff454
-rw-r--r--patches/vdr-1.7.17.diff416
-rw-r--r--patches/vdr-1.7.32.diff426
-rw-r--r--patches/vdr-1.7.33.diff427
-rw-r--r--patches/vdr-2.0.2.diff420
-rw-r--r--patches/vdr-2.3.1.diff432
-rw-r--r--pin.c882
-rw-r--r--pin.h237
-rw-r--r--po/de_DE.po170
-rw-r--r--po/fr_FR.po173
-rw-r--r--po/hu_HU.po171
-rw-r--r--po/it_IT.po172
-rw-r--r--po/ru_RU.po170
-rw-r--r--rep.c77
-rw-r--r--rep.h31
-rwxr-xr-xscripts/cut.sh14
-rwxr-xr-xscripts/fskcheck-demo.sh23
-rwxr-xr-xscripts/fskprotect.sh34
-rwxr-xr-xscripts/getSetupMenues8
-rw-r--r--setupmenu.c139
-rw-r--r--setupmenu.h42
-rw-r--r--talk.c198
-rw-r--r--talk.h97
-rw-r--r--test.cc13
43 files changed, 8305 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..f0cc9a1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+.dependencies
+po/*~
+po/*.mo
+po/*.pot
+*.so
+*.o
+*~
+fskcheck \ No newline at end of file
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..5b6e7c6
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library General
+Public License instead of this License.
diff --git a/HISTORY b/HISTORY
new file mode 100644
index 0000000..2bbdaf4
--- /dev/null
+++ b/HISTORY
@@ -0,0 +1,130 @@
+ * ---------------------------------------------------------------------
+ * Version - History
+ * ---------------------------------------------------------------------
+ *
+ * build version date comment
+ *
+
+ * #41 0.1.16 01.11.15, added: fixed backward compatibility
+ * #40 0.1.15 09.10.15, added: ported to vdr 2.3.1
+ * #39 0.1.14 22.05.13, added: patch for vdr 2.0.2
+ added: Makefile for vdr >= 1.7.35
+ * #38 0.1.13 10.12.12, added: patch for vdr 1.7.33
+ * #37 0.1.12 07.12.12, added: patch for vdr 1.7.32
+ * #36 0.1.11 31.03.11, added: patch for vdr 1.7.17
+ * #35 0.1.10 20.07.08, added: italian translations (thanks to Gringo)
+ * #34 0.1.9 19.05.07, added: setup option for log-level
+ More frensh translations from pat, thanks!
+ 'no translation found' message now appear
+ only once for each text
+ (added internal cache for menu item translations)
+ * #33 0.1.8 12.02.07, added: Autofill of list with menu-entry
+ * #32 0.1.7 31.01.07, added: Hiding protected plugins.
+ bugfix: Fixed problem with hotkey handling in pin menu
+ added: Protection of menu entries.
+ Hiding protected menu entries.
+ New search mode 'start with'.
+ Extended search modes (exact, regular expression, includes, start with)
+ now available for all items (channels, plugins, menuitems).
+ Hiding protected items now configuable.
+ added patch for epgsearch 0.9.20
+ * #31 0.1.6a 29.01.07, bugfix: Fixed syntax error in vdr patch (reported by zulu)
+ * #30 0.1.6 28.01.07, added: Hiding protected recorings in recordings menu
+ * #29 0.1.5b 12.01.07, bugfix: Fixed error message when closing setup.
+ Fixed translation in timer menue, thanks to Marc (zulu)
+ added: Patch for vdr 1.4.5
+ change: Patch for epgsearch changed
+ * #28 0.1.5a 07.01.07, change: pin in setup dialog now hidden again.
+ * #27 0.1.5 06.01.07, bugfix: Fixed protection leak when replaying via
+ the 'Play' key, reported by Christian Reiser.
+ change: Edit of timers fsk setting only protected if the
+ timer 'not' already locked
+ * #26 0.1.4 23.11.06, added: Frensh translations from pat, thanks!
+ * #25 0.1.3 23.11.06, change: Pin input dialog now calls status class
+ (thereby it's shown on graphTFT, ...)
+ added: Edit of timers fsk setting only when unlocked ;)
+ bugfix: Some small fixes
+ * #24 0.1.2 09.11.06, change: limited pin to 5 digits
+ change: many namings and translations
+ bugfix: fixed handling of daylightsavingtime.
+ change: ask for inital pincode setup when entering menue
+ added: EXCLUDEPLUGINS to makefile, here can defined which
+ plugins can't protected
+ added: setup-plugin lock to pin menu (for fast and easy access)
+ added: button blue now toggle protection in list
+ added: patch for the timer menu of epgsearch
+ the changes of thes release mostly suggested by Andreas Ritter
+ * #23 0.1.1 11.09.06, added: Redesign of fskcheck communication (some code rework)
+ bugfix: Fixed translation bug in patch (Danke Frank!)
+ bugfix: Take care of daylightsavingtime.
+ added: Changed patch due to epgsearch plugin requirements.
+ * #22 0.1.0 09.09.06, change: No protection until a pin code was configured
+ (requested by Andreas Ritter)
+ change: Enter pin menu directly if pin was
+ accepted (configurable) (requested by Andreas Ritter)
+ added: Translation of script messages by
+ plugin (as far as found in translation list)
+ Frensh translations from pat, thanks!
+ bugix: Fixed calculation of 'inactivity' time
+ change: Some code cleanup
+ * #21 0.0.21 10.06.06, change: Removed green and yellow button in plugin-lock menu
+ * #20 0.0.20 30.05.06, bugfix: Fixed crash in empty lock menus (reported by Andreas Ritter)
+ Fixed permissions of config path (reported by wilderigel)
+ * #19 0.0.19 20.05.06, change: Using skin for pin request dialog
+ * #18 0.0.18 04.05.06, change: Code redesign, pooled similar classes, ..
+ * added: Paragraph-Sign to mark activated locks in pin menu lists
+ * #17 0.0.17 04.05.06, added: plugin protection
+ * bugfix: fixed problem with recording subdirectories
+ * (bug was reported by Viking, thanks!)
+ * #16 0.0.16 01.05.06, bugfix: fixed permissions ot system message queues.
+ * added: num keys navigation for pin menu
+ * added: Missing translation for 'Recording
+ * protected, enter pin code first'.
+ * All this detected and reported by Michael Nausch, many thanks!
+ * #15 0.0.15 29.04.06, added: french translations (thanks to Pat!)
+ * change: Makefile due to VDR's new APIVERSION feature
+ * #14 0.0.14 08.04.06, added: protection feature for scripting
+ * #13 0.0.13 26.03.06, bugfix: autoProtectionMode now respect start time
+ * of event (needed since channels not
+ * protected every time)
+ * #12 0.0.12 20.03.06, change: now using aux field in timer config patch
+ * bugfix: autoProtectionMode will now restored from config
+ * (Bug reported by rookie1, thanks!)
+ * bugfix: autoProtectionMode now working even with vdr 1.3.44
+ * (Bug reported by rookie1, thanks!)
+ * #11 0.0.11 26.02.06, bugfix: create missing config directory at startup.
+ * bugfix: keys 1-5 in pin menu now working.
+ * added: hungary translations (thanks to Albert Papp!)
+ * change: silend skip of channels works now again
+ * (fixed problems due to changes of VDR 1.3.40)
+ * #10 0.0.10 28.01.06, added: regular expressions for brodcasts locks.
+ * added: russian translations (appropriated by Monchenko Vladimir, thanks!)
+ * bugfix: only 3 chars of pin in setup menu (reported by Monchenko Vladimir, thanks!)
+ * #9 0.0.9 26.01.06, added: lock for brodcasts.
+ * #8 0.0.8 17.01.06, added: channel lock time range.
+ * #7 0.0.7 15.01.06, added: channel lock list and improved configuration
+ * #6 0.0.6f 13.01.06, change: removed pin request on demand
+ * 0.0.6e 23.12.05, bugfix: syslog message at channel check
+ * 0.0.6d 18.12.05, added: dynamic handling of menu entries
+ * 0.0.6c 18.12.05, added: internationalisation
+ * 0.0.6b 18.12.05, change: reset time now in minutes
+ * 0.0.6a 17.12.05, added: reset time for pin code
+ * 0.0.6 17.12.05, added: protection flag to timer config
+ * #5 0.0.5b 11.12.05, bugfix: some bugfixes, added handling for recording
+ * 0.0.5a 10.12.05, added: pin request on demand, even for replay of recorings
+ * 0.0.5 10.12.05, added: pin request on demand
+ * #4 0.0.4 08.12.05, added: channel protection
+ * #3 0.0.3 06.12.05, added: protection of recording sub-directorys
+ * #2 0.0.2 01.12.05, bugfixes
+ * #1 0.0.1 11.04.05, initial release
+ *
+ *
+
+ * ---------------------------------------------------------------------
+ * Todos
+ * ---------------------------------------------------------------------
+ *
+ * - broadcast locks via event description and/or epg text
+ *
+ * ---------------------------------------------------------------------
+
diff --git a/Makefile b/Makefile
new file mode 120000
index 0000000..b3f546d
--- /dev/null
+++ b/Makefile
@@ -0,0 +1 @@
+Makefile.since-1.7.35 \ No newline at end of file
diff --git a/Makefile.pre-1.7.35 b/Makefile.pre-1.7.35
new file mode 100644
index 0000000..84d629d
--- /dev/null
+++ b/Makefile.pre-1.7.35
@@ -0,0 +1,125 @@
+#
+# Makefile for a Video Disk Recorder plugin
+#
+# $Id: Makefile,v 1.8 2011-03-31 08:59:31 root Exp $
+
+# 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.
+#
+PLUGIN = pin
+
+### User settings, list of plugins which can not protected
+
+EXCLUDEPLUGINS = "femon,text2skin,span,remote,premiereepg"
+
+### 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 ?= g++
+#CXXFLAGS ?= -ggdb -fPIC -O2 -Wall -Wno-unused-result -Woverloaded-virtual
+CXXFLAGS ?= -fPIC -O2 -Wall -Wno-unused-result -Woverloaded-virtual
+
+### The directory environment:
+
+DVBDIR = ../../../../DVB
+VDRDIR = ../../..
+LIBDIR = ../../lib
+TMPDIR = /tmp
+FSKCHKDIR = /usr/bin
+
+### Allow user defined options to overwrite defaults:
+
+-include $(VDRDIR)/Make.config
+
+### The version number of VDR (taken from VDR's "config.h"):
+
+APIVERSION = $(shell grep 'define APIVERSION ' $(VDRDIR)/config.h | awk '{ print $$3 }' | sed -e 's/"//g')
+
+### The name of the distribution archive:
+
+ARCHIVE = $(PLUGIN)-$(VERSION)
+PACKAGE = vdr-$(ARCHIVE)
+
+### Includes and Defines (add further entries here):
+
+INCLUDES += -I$(VDRDIR)/include -I$(DVBDIR)/include
+
+DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"' -D__EXCL_PLUGINS='""$(EXCLUDEPLUGINS)""'
+
+### The object files (add further files here):
+
+OBJS = $(PLUGIN).o setupmenu.o def.o menu.o locks.o brdclocks.o rep.o msgreceiver.o talk.o
+CMDOBJS = fskcheck.o talk.o def.o
+
+### Implicit rules:
+
+%.o: %.c
+ $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $<
+
+# Dependencies:
+
+MAKEDEP = g++ -MM -MG
+DEPFILE = .dependencies
+$(DEPFILE): Makefile
+ @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
+
+-include $(DEPFILE)
+
+### Internationalization (I18N):
+
+PODIR = po
+LOCALEDIR = $(VDRDIR)/locale
+I18Npo = $(wildcard $(PODIR)/*.po)
+I18Nmo = $(addsuffix .mo, $(foreach file, $(I18Npo), $(basename $(file))))
+I18Ndirs = $(notdir $(foreach file, $(I18Npo), $(basename $(file))))
+I18Npot = $(PODIR)/$(PLUGIN).pot
+
+%.mo: %.po
+ msgfmt -c -o $@ $<
+
+$(I18Npot): $(wildcard *.c)
+ xgettext -C -cTRANSLATORS --no-wrap -F -k -ktr -ktrNOOP --msgid-bugs-address='<vdr@jwendel.de>' -o $@ $(wildcard *.c)
+
+$(I18Npo): $(I18Npot)
+ msgmerge -U --no-wrap -F --backup=none -q $@ $<
+
+i18n: $(I18Nmo)
+ @mkdir -p $(LOCALEDIR)
+ for i in $(I18Ndirs); do\
+ mkdir -p $(LOCALEDIR)/$$i/LC_MESSAGES;\
+ cp $(PODIR)/$$i.mo $(LOCALEDIR)/$$i/LC_MESSAGES/vdr-$(PLUGIN).mo;\
+ done
+
+### Targets:
+
+all: i18n
+ @make fskcheck
+ @make plugin
+
+plugin: libvdr-$(PLUGIN).so
+
+fskcheck: $(CMDOBJS)
+ $(CXX) $(CXXFLAGS) $(CMDOBJS) -o $@
+ @strip $@
+# @cp $@ $(FSKCHKDIR)
+
+libvdr-$(PLUGIN).so: $(OBJS)
+ $(CXX) $(CXXFLAGS) -shared $(OBJS) -o $@
+ @cp $@ $(LIBDIR)/$@.$(APIVERSION)
+
+dist: clean
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @mkdir $(TMPDIR)/$(ARCHIVE)
+ @cp -a * $(TMPDIR)/$(ARCHIVE)
+ @tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE)
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @echo Distribution package created as $(PACKAGE).tgz
+
+clean:
+ @-rm -f $(PODIR)/*.mo $(PODIR)/*.pot $(PODIR)/*~
+ @-rm -f $(OBJS) $(CMDOBJS) $(DEPFILE) *.so *.tgz core* *~ fskcheck
+
diff --git a/Makefile.since-1.7.35 b/Makefile.since-1.7.35
new file mode 100644
index 0000000..fa84228
--- /dev/null
+++ b/Makefile.since-1.7.35
@@ -0,0 +1,131 @@
+#
+# 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.
+
+PLUGIN = pin
+
+### 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')
+
+### User settings, list of plugins which can not protected
+
+EXCLUDEPLUGINS = "femon,text2skin,span,remote,premiereepg"
+
+### The directory environment:
+
+# Use package data if installed...otherwise assume we're under the VDR source directory:
+PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell pkg-config --variable=$(1) vdr || pkg-config --variable=$(1) ../../../vdr.pc))
+LIBDIR = $(call PKGCFG,libdir)
+LOCDIR = $(call PKGCFG,locdir)
+PLGCFG = $(call PKGCFG,plgcfg)
+#
+TMPDIR ?= /tmp
+
+### The compiler options:
+
+export CFLAGS = $(call PKGCFG,cflags)
+export CXXFLAGS = $(call PKGCFG,cxxflags)
+
+### The version number of VDR's plugin API:
+
+APIVERSION = $(call PKGCFG,apiversion)
+
+### Allow user defined options to overwrite defaults:
+
+-include $(PLGCFG)
+
+### The name of the distribution archive:
+
+ARCHIVE = $(PLUGIN)-$(VERSION)
+PACKAGE = vdr-$(ARCHIVE)
+
+### The name of the shared object file:
+
+SOFILE = libvdr-$(PLUGIN).so
+
+### Includes and Defines (add further entries here):
+
+INCLUDES += -I$(VDRDIR)/include
+
+DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' -D__EXCL_PLUGINS='""$(EXCLUDEPLUGINS)""'
+
+### The object files (add further files here):
+
+OBJS = $(PLUGIN).o setupmenu.o def.o menu.o locks.o brdclocks.o rep.o msgreceiver.o talk.o
+CMDOBJS = fskcheck.o talk.o def.o
+
+### The main target:
+
+all: $(SOFILE) i18n fskcheck
+
+### Implicit rules:
+
+%.o: %.c
+ $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $<
+
+### Dependencies:
+
+MAKEDEP = $(CXX) -MM -MG
+DEPFILE = .dependencies
+$(DEPFILE): Makefile
+ @$(MAKEDEP) $(CXXFLAGS) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
+
+-include $(DEPFILE)
+
+### Internationalization (I18N):
+
+PODIR = po
+I18Npo = $(wildcard $(PODIR)/*.po)
+I18Nmo = $(addsuffix .mo, $(foreach file, $(I18Npo), $(basename $(file))))
+I18Nmsgs = $(addprefix $(DESTDIR)$(LOCDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file))))))
+I18Npot = $(PODIR)/$(PLUGIN).pot
+
+%.mo: %.po
+ msgfmt -c -o $@ $<
+
+$(I18Npot): $(wildcard *.c)
+ xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --package-name=vdr-$(PLUGIN) --package-version=$(VERSION) --msgid-bugs-address='<see README>' -o $@ `ls $^`
+
+%.po: $(I18Npot)
+ msgmerge -U --no-wrap --no-location --backup=none -q -N $@ $<
+ @touch $@
+
+$(I18Nmsgs): $(DESTDIR)$(LOCDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo
+ install -D -m644 $< $@
+
+.PHONY: i18n
+i18n: $(I18Nmo) $(I18Npot)
+
+install-i18n: $(I18Nmsgs)
+
+### Targets:
+
+$(SOFILE): $(OBJS)
+ $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(OBJS) -o $@
+
+fskcheck: $(CMDOBJS)
+ $(CXX) $(CXXFLAGS) $(CMDOBJS) -o $@
+ @strip $@
+
+install-lib: $(SOFILE)
+ install -D $^ $(DESTDIR)$(LIBDIR)/$^.$(APIVERSION)
+
+install: install-lib install-i18n
+
+dist: $(I18Npo) clean
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @mkdir $(TMPDIR)/$(ARCHIVE)
+ @cp -a * $(TMPDIR)/$(ARCHIVE)
+ @tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE)
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @echo Distribution package created as $(PACKAGE).tgz
+
+clean:
+ @-rm -f $(PODIR)/*.mo $(PODIR)/*.pot $(PODIR)/*~
+ @-rm -f $(OBJS) $(CMDOBJS) $(DEPFILE) *.so *.tgz core* *~ fskcheck
diff --git a/README b/README
new file mode 100644
index 0000000..e3bdae8
--- /dev/null
+++ b/README
@@ -0,0 +1,62 @@
+
+This is a "plugin" for the Video Disk Recorder (VDR).
+
+Written by: Jörg Wendel (horchi@vdr-portal)
+Project's homepage: http://www.jwendel.de
+Latest version available at: vdr-developer.org
+
+This software is released under the GPL, version 2 (see COPYING).
+Additionally, compiling, linking, and/or using the OpenSSL toolkit in
+conjunction with this software is allowed.
+
+PIN Plugin:
+-----------
+Das Plugin ist eine Kindersicherung für Aufnahmen, Kanäle, Sendungen, Plugins und Shell-Skripten.
+Der bis zu 5-stellige numerische PIN kann im Plugin-Setup Menü eingestellt werden.
+Das freischalten mittels des PIN Codes erfolgt über den Menüpunkt 'Kindersicherung' im Hauptmenü.
+Der Code wird nach jedem Neustart zurückgesetzt. Die Sperre kann jederzeit mit dem entsprechenden
+Menüpunktes oder auch automatisch nach einer einstellbaren Zeit aktiviert werden.
+
+Es werden Aufnahmen in deren Ordner sich die Datei 'protection.fsk' befindet geschützt.
+Hierzu 'Aufnahmen->Befehle' - 'FSK Schutz hinzufügen' oder 'FSK Schutz entfernen' wählen.
+Plugin, Kanal- und Sendungs-Sperren werden über das Plugin-Menü verwaltet.
+
+Zum Schutz von Shell-Skripten dient das Tool fskcheck, dieses stellt ein Shell-Interface zum
+PIN Plugin dar. Hierüber kann aus einem Skript bzw. von der Kommandozeile abgefragt werden ob die
+Kindersicherung gerade aktiviert ist. Auch OSD Meldungen können mittels fskcheck angezeigt werden.
+Ein Beispiel Skript hierzu ist im Ordner scripts enthalten.
+
+Es werden VDR Versionen ab 1.3.47 bis 2.0.2 unterstützt.
+
+Installation:
+-------------
+
+ Plugin in .../VDR/PLUGINS/src/
+ auspacken und Link anlegen.
+
+ Ggf. dir Zeile EXCLUDEPLUGINS im Makefile anpassen, die hier aufgelisteten Plugins werden nicht
+ in der Sperrliste aufgeführt und lassen sich somit auch nicht sperren.
+
+ PIN-Patch auf den VDR anwenden.
+
+ cd /usr/src/VDR
+ cat PLUGINS/src/pin/patches/vdr-<version>.diff | patch (etwaige rejects auflösen)
+ make plugins
+
+ ALLE Plugins neu gegen den gepatchten VDR übersezten und installieren!
+
+ Das Skript fskprotect.sh und das tool fskcheck in den Suchpfad (PATH) z.B.: '/usr/local/bin' oder '/usr/bin'
+ kopieren und in die Konfigurationsdatei reccmd.conf eintragen.
+
+ Beispiel:
+
+ cp -p /usr/src/VDR/PLUGINS/src/pin/scripts/*.sh /usr/bin
+ cp -p /usr/src/VDR/PLUGINS/src/pin/fskcheck /usr/bin
+ echo 'FSK Schutz hinzufügen: /usr/bin/fskprotect.sh protect' >> /etc/vdr/reccmds.conf
+ echo 'FSK Schutz entfernen: /usr/bin/fskprotect.sh unprotect' >> /etc/vdr/reccmds.conf
+
+ Bitte die beiden unteren Zeilen nicht so in die reccmds.conf eintragen sondern auf der Kommandozeile ausführen ;) !
+
+ Beim scheiden einer Aufnahme wird diese vom VDR umkopiert, diese Kopie ist nun nicht gesperrt!
+ Um die Sperre automatisch zu übernehmen das beim Start des VDR mit der Option -r angegebene
+ Skript (noadcall.sh, vdr-recordingaction, ...) um den Code aus scripts/cut.sh erweitern.
diff --git a/brdclocks.c b/brdclocks.c
new file mode 100644
index 0000000..e22a748
--- /dev/null
+++ b/brdclocks.c
@@ -0,0 +1,125 @@
+/*
+ * pin.c: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * Date: 25.01.06 - 02.02.07, horchi
+ */
+
+//***************************************************************************
+// Includes
+//***************************************************************************
+
+#include "locks.h"
+#include "def.h"
+
+//***************************************************************************
+// Object
+//***************************************************************************
+
+cLockedBroadcast::cLockedBroadcast()
+ : cLockItem(yes)
+{
+ hasRange = no;
+}
+
+cLockedBroadcast::cLockedBroadcast(const char* aName)
+ : cLockItem(aName, yes)
+{
+ hasRange = no;
+}
+
+cLockedBroadcast::~cLockedBroadcast()
+{
+ // nothicng yet ...
+}
+
+//***************************************************************************
+// Parse
+//***************************************************************************
+
+bool cLockedBroadcast::Parse(char* line)
+{
+ int fields;
+
+ char* aName = 0;
+ char* aPattern = 0;
+ char* aSearchMode = 0;
+ char* aActive = 0;
+
+ fields = sscanf(line, "%a[^:]:%a[^:]:%a[^:]:%a[^\n]",
+ &aName, &aPattern, &aSearchMode, &aActive);
+
+ if (fields == 4)
+ {
+ if (name) free(name);
+ name = aName;
+
+ if (pattern) free(pattern);
+ pattern = aPattern;
+
+ for (int i = 0; i < smCount; i++)
+ {
+ if (strcmp(searchModes[i], aSearchMode) == 0)
+ {
+ searchMode = i;
+ break;
+ }
+ }
+
+ active = strcmp(aActive, "yes") == 0;
+ }
+
+ if (aSearchMode) free(aSearchMode);
+ if (aActive) free(aActive);
+
+ return fields == 4;
+}
+
+//***************************************************************************
+// Save
+//***************************************************************************
+
+bool cLockedBroadcast::Save(FILE* file)
+{
+ // Format: "<name>:<pattern>:<searchMode>:<active>"
+
+ return fprintf(file, "%s:%s:%s:%s\n",
+ name,
+ pattern,
+ searchModes[searchMode],
+ active ? "yes" : "no") > 0;
+}
+
+//***************************************************************************
+// Locked
+//***************************************************************************
+
+int cLockedBroadcast::Locked(long /*startTime*/)
+{
+ return active;
+}
+
+//***************************************************************************
+// Class cLockedBroadcasts
+//***************************************************************************
+//***************************************************************************
+// Locked
+//***************************************************************************
+
+int cLockedBroadcasts::Locked(const char* aName, long /*startTime*/)
+{
+ cLockedBroadcast* broadcast;
+
+ if (!aName)
+ return no;
+
+ for (broadcast = First(); broadcast; broadcast = Next(broadcast))
+ {
+ if (broadcast->MatchPattern(aName))
+ if (broadcast->Locked())
+ return yes;
+ }
+
+ return no;
+}
diff --git a/def.c b/def.c
new file mode 100644
index 0000000..55bd7fa
--- /dev/null
+++ b/def.c
@@ -0,0 +1,41 @@
+/*
+ * pin.c: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * Date: 11.04.05 - 12.02.07, horchi
+ */
+
+//***************************************************************************
+// Include
+//***************************************************************************
+
+#include <stdarg.h>
+#include <syslog.h>
+
+#include "def.h"
+
+int logLevel = eloOff;
+
+//***************************************************************************
+// Tell
+//***************************************************************************
+
+int tell(int eloquence, const char* format, ...)
+{
+ char tmp[1024];
+ va_list ap;
+
+ va_start(ap, format);
+
+ if (logLevel >= eloquence)
+ {
+ vsnprintf(tmp + 5, sizeof tmp - 5, format, ap);
+ memcpy(tmp, "[pin] ", 5);
+ syslog(LOG_INFO, "%s", tmp);
+ }
+
+ va_end(ap);
+
+ return success;
+}
diff --git a/def.h b/def.h
new file mode 100644
index 0000000..31d5a66
--- /dev/null
+++ b/def.h
@@ -0,0 +1,178 @@
+/*
+ * def.h
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+
+#ifndef __DEF_H__
+#define __DEF_H__
+
+//***************************************************************************
+// Includes
+//***************************************************************************
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h> // memset
+#include <time.h>
+#include <ctype.h> // isdigit
+
+//***************************************************************************
+// Public Declarations
+//***************************************************************************
+
+enum Misc
+{
+ success = 0,
+ done = success,
+ fail = -1,
+ ignore = -1,
+ na = -1,
+ yes = 1,
+ no = 0,
+ TB = 1
+};
+
+enum Eloquence
+{
+ eloOff, // 0
+ eloNormal, // 1
+ eloAlways = eloNormal, // 1 historical ;)
+ eloDetail, // 2
+
+ eloDebug, // 3
+ eloDebug1 = eloDebug, // 3
+ eloDebug2, // 4
+ eloDebug3 // 5
+};
+
+enum Sizes
+{
+ sizeStamp = 14,
+ sizeTime = 6,
+ sizeDate = 8,
+ sizeHHMM = 4
+};
+
+enum TimeFact
+{
+ // Defintion
+
+ tfMonthsPerYear = 12,
+ tfWeeksPerYear = 52,
+ tfDaysPerYear = 365,
+ tfDaysPerWeek = 7,
+ tfHoursPerDay = 24,
+ tfMinutesPerHour = 60,
+ tfSecondsPerMinute = 60,
+
+ // Calculation
+
+ tfSecondsPerHour = tfMinutesPerHour * tfSecondsPerMinute,
+ tfSecondsPerDay = tfHoursPerDay * tfSecondsPerHour,
+ tfSecondsPerYear = tfDaysPerYear * tfSecondsPerDay,
+ tfSecondsPerWeek = tfDaysPerWeek * tfSecondsPerDay,
+};
+
+//***************************************************************************
+// Tell
+//***************************************************************************
+
+extern int logLevel;
+
+int tell(int eloquence, const char* format, ...);
+
+//***************************************************************************
+// Date Time
+//***************************************************************************
+
+class DT
+{
+ public:
+
+ typedef tm STime;
+
+ static long lNow()
+ {
+ long lt = time(0);
+ STime st;
+
+ localtime_r(&lt, &st);
+ lt = mktime(&st);
+
+ lt -= timezone; // care timezone
+ lt += st.tm_isdst ? tfSecondsPerHour : 0; // care daylightsavingtime
+
+ return lt;
+ }
+
+ static char* int2Hhmm(long lt, char* ct)
+ {
+ STime st;
+ memset(&st, 0, sizeof(STime));
+ gmtime_r(&lt, &st);
+
+ sprintf(ct, "%2.2d%2.2d", st.tm_hour, st.tm_min);
+
+ return ct;
+ }
+
+ static long hhmm2Int(const char* c)
+ {
+ long lt = 0;
+ char ct[sizeHHMM+TB];
+
+ if (strlen(c) != 4)
+ return na;
+
+ strcpy(ct, c);
+ lt = atoi(ct+2) * 60;
+ ct[2] = 0;
+ lt += atoi(ct) * 60 * 60;
+
+ return lt;
+ }
+};
+
+//***************************************************************************
+// String
+//***************************************************************************
+
+class Str
+{
+ public:
+
+ static const char* stripHotKey(const char* text)
+ {
+ static char buffer[64];
+ const char* p = text;
+
+ while (*p)
+ {
+ if (*p == ' ')
+ p++;
+ else if (isdigit(*p) && isdigit(*(p+1)))
+ p++;
+ else if (isdigit(*p) && *(p+1) == ' ')
+ {
+ p++;
+ while (*p == ' ') p++;
+ break;
+ }
+ else
+ {
+ p = text;
+ break;
+ }
+ }
+
+ strcpy(buffer, p);
+
+ return buffer;
+ }
+};
+
+//***************************************************************************
+#endif // __DEF_H__
diff --git a/fskcheck.c b/fskcheck.c
new file mode 100644
index 0000000..c860c30
--- /dev/null
+++ b/fskcheck.c
@@ -0,0 +1,172 @@
+//***************************************************************************
+/*
+ * fskcheck.c: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id: fskcheck.c,v 1.3 2007/01/13 07:52:27 root Exp $
+ */
+//***************************************************************************
+
+//***************************************************************************
+// Includes
+//***************************************************************************
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/msg.h>
+
+#include "def.h"
+#include "talk.h"
+
+//*************************************************************************
+// Global
+//*************************************************************************
+
+int choice(char** argv, int& cmd, const char*& msg, long& timeout);
+
+const char* commands[] =
+{
+ "Check",
+ "Info",
+
+ 0
+};
+
+enum Command
+{
+ cmdUnknown = na,
+
+ cmdCheck,
+ cmdInfo,
+
+ cmdCount
+};
+
+//***************************************************************************
+// Main
+//***************************************************************************
+
+int main(int argc, char** argv)
+{
+ Talk sender;
+ int status;
+ int event = Talk::evtUnknown;
+ const char* msg;
+ int cmd;
+ long t;
+
+ if ((status = sender.open(11)) != success)
+ {
+ printf("Falal: Can't init message queue, '%s'!\n", strerror(errno));
+ return fail;
+ }
+
+ t = sender.getTimeout();
+ msg = 0;
+
+ if (choice(argv, cmd, msg, t) != success)
+ {
+ printf("Usage: fskcheck <command> [-t timeout][-v]\n");
+ printf(" command: check - check if vdr currently protected\n");
+ printf(" command: info <message> - show message on osd\n");
+
+ return fail;
+ }
+
+ // init
+
+ sender.init();
+ sender.setTimeout(t);
+
+ // select command
+
+ switch (cmd)
+ {
+ case cmdCheck: event = Talk::evtCheck; break;
+ case cmdInfo: event = Talk::evtShow; break;
+ }
+
+ // send
+
+ if (sender.send(10, event, msg) != success)
+ {
+ printf("Fatal: Can't send event (%d) with message '%s' to vdr (port %d), '%s'\n",
+ event, msg ? msg : "", 10, strerror(errno));
+ return fail;
+ }
+
+ // wait
+
+ if (cmd == cmdCheck)
+ {
+ if (sender.wait() == success)
+ {
+ // printf("Got (%d) \n", sender.getEvent());
+
+ if (sender.getEvent() == Talk::evtConfirm) return 1;
+ if (sender.getEvent() == Talk::evtAbort) return 2;
+
+ return 0;
+ }
+
+ else
+ return 0;
+ }
+
+ return 0;
+}
+
+//***************************************************************************
+// Choise
+//***************************************************************************
+
+int choice(char** argv, int& cmd, const char*& msg, long& timeout)
+{
+ // skip program path
+
+ argv++;
+
+ // get command
+
+ if (!(*argv))
+ return fail;
+
+ for (cmd = 0; cmd < cmdCount; cmd++)
+ if (strcasecmp(commands[cmd], *argv) == 0)
+ break;
+
+ if (cmd >= cmdCount)
+ return fail;
+
+ argv++;
+
+ while (*argv)
+ {
+ if (**argv == '-')
+ {
+ if (strcmp(*argv, "-t") == 0)
+ {
+ // timeout
+
+ if (!(*(++argv))) break;
+
+ timeout = atoi(*argv);
+ }
+ }
+ else
+ {
+ // the message
+
+ msg = *argv;
+ }
+
+ argv++;
+ }
+
+ return success;
+}
diff --git a/locks.c b/locks.c
new file mode 100644
index 0000000..bda84c6
--- /dev/null
+++ b/locks.c
@@ -0,0 +1,274 @@
+/*
+ * pin.c: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * Date: 11.04.05 - 02.02.07, horchi
+ */
+
+//***************************************************************************
+// Includes
+//***************************************************************************
+
+#include "locks.h"
+#include "def.h"
+
+//***************************************************************************
+// Statics
+//***************************************************************************
+
+const char* cLockItem::searchModes[] =
+{
+ "regular expression",
+ "includes",
+ "exact",
+ "start with",
+ 0
+};
+
+//***************************************************************************
+// Object
+//***************************************************************************
+
+cLockItem::cLockItem(int aActive)
+{
+ name = 0;
+ title = 0;
+ active = aActive;
+ hasRange = yes;
+ start = 0;
+ end = 0;
+ pattern = 0;
+ searchMode = smExact;
+}
+
+cLockItem::cLockItem(const char* aName, int aActive, const char* aTitle)
+{
+ int res;
+ res = asprintf(&name, "%s", aName);
+ res = asprintf(&pattern, "%s", aName);
+ if (res < 0) ; // memory problems :o
+ active = aActive;
+ hasRange = yes;
+ title = 0;
+ start = DT::hhmm2Int("0000");
+ end = DT::hhmm2Int("2359");
+ searchMode = smExact;
+
+ if (aTitle)
+ res = asprintf(&title, "%s", aTitle);
+}
+
+cLockItem::~cLockItem()
+{
+ if (name) free(name);
+ if (title) free(title);
+ if (pattern) free(pattern);
+}
+
+//***************************************************************************
+// Name
+//***************************************************************************
+
+void cLockItem::SetName(const char* aName)
+{
+ int res;
+
+ if (!aName)
+ return;
+
+ if (name)
+ free(name);
+
+ res = asprintf(&name, "%s", aName);
+ if (res < 0) ; // memory problems :o
+}
+
+//***************************************************************************
+// Pattern
+//***************************************************************************
+
+void cLockItem::SetPattern(const char* aPattern)
+{
+ int res;
+
+ if (pattern)
+ free(pattern);
+
+ res = asprintf(&pattern, "%s", aPattern);
+ if (res < 0) ; // memory problems :o
+}
+
+//***************************************************************************
+// Title
+//***************************************************************************
+
+void cLockItem::SetTitle(const char* aTitle)
+{
+ int res;
+
+ if (!aTitle)
+ return;
+
+ if (title)
+ free(title);
+
+ res = asprintf(&title, "%s", aTitle);
+
+ if (res < 0) ; // memory problems :o
+}
+
+//***************************************************************************
+// Parse
+//***************************************************************************
+
+bool cLockItem::Parse(char* line)
+{
+ int fields;
+
+ char* aName = 0;
+ char* aActive = 0;
+ char* aStart = 0;
+ char* aEnd = 0;
+ char* aPattern = 0;
+ char* aSearchMode = 0;
+
+ fields = sscanf(line, "%a[^:]:%a[^:]:%a[^:]:%a[^:]:%a[^:]:%a[^\n]",
+ &aName, &aActive, &aStart, &aEnd,
+ &aPattern, &aSearchMode);
+
+ if (fields >= 4)
+ {
+ if (name) free(name);
+ name = aName;
+ active = strcmp(aActive, "yes") == 0;
+ start = DT::hhmm2Int(aStart);
+ end = DT::hhmm2Int(aEnd);
+ }
+
+ if (fields == 6)
+ {
+ if (pattern) free(pattern);
+ pattern = aPattern;
+
+ for (int i = 0; i < smCount; i++)
+ {
+ if (strcmp(searchModes[i], aSearchMode) == 0)
+ {
+ searchMode = i;
+ break;
+ }
+ }
+ }
+ else if (fields == 4)
+ {
+ SetPattern(name);
+ searchMode = smExact;
+ }
+
+ if (aActive) free(aActive);
+ if (aStart) free(aStart);
+ if (aEnd) free(aEnd);
+ if (aSearchMode) free(aSearchMode);
+
+ return fields == 4 || fields == 6;
+}
+
+//***************************************************************************
+// Save
+//***************************************************************************
+
+bool cLockItem::Save(FILE* file)
+{
+ char aStart[sizeHHMM+TB];
+ char aEnd[sizeHHMM+TB];
+
+ DT::int2Hhmm(start, aStart);
+ DT::int2Hhmm(end, aEnd);
+
+ // Format: "<name>:<active>:<start>:<end>:<pattern>:<searchMode>"
+
+ return fprintf(file, "%s:%s:%s:%s:%s:%s\n",
+ name,
+ active ? "yes" : "no",
+ aStart, aEnd, pattern,
+ searchModes[searchMode]) > 0;
+}
+
+//***************************************************************************
+// Locked
+//***************************************************************************
+
+int cLockItem::Locked(long startTime)
+{
+ long sec;
+ char ct[sizeHHMM+TB];
+ long theTime;
+
+ if (startTime)
+ theTime = startTime;
+ else
+ theTime = DT::lNow(); // get now in seconds
+
+ // get seconds from today 00:00 in actual timezone
+
+ tell(eloDebug, "Checking protection at '%s'", DT::int2Hhmm(theTime, ct));
+
+ sec = DT::hhmm2Int(DT::int2Hhmm(theTime, ct));
+
+ if (sec >= start && sec <= end && active)
+ return yes;
+
+ return no;
+}
+
+//***************************************************************************
+// Match Pattern
+//***************************************************************************
+
+bool cLockItem::MatchPattern(const char* aName)
+{
+ if (searchMode == smRegexp)
+ return rep(aName, pattern) == success;
+ else if (searchMode == smExact)
+ return strcmp(pattern, aName) == 0;
+ else if (searchMode == smInclude)
+ return strstr(aName, pattern) > 0;
+ else if (searchMode == smStartWith)
+ return strstr(aName, pattern) == aName;
+
+ return no;
+}
+
+//***************************************************************************
+// Has Name
+//***************************************************************************
+
+bool cLockItem::HasName(const char* aName)
+{
+ return strcmp(name, aName) == 0;
+}
+
+//***************************************************************************
+// Class cLockItems
+//***************************************************************************
+//***************************************************************************
+// Locked
+//***************************************************************************
+
+int cLockItems::Locked(const char* aName, long startTime)
+{
+ cLockItem* item;
+
+ if (!aName)
+ return no;
+
+ for (item = First(); item; item = Next(item))
+ {
+ if (item->MatchPattern(aName))
+ if (item->Locked(startTime))
+ return yes;
+ }
+
+ return no;
+}
diff --git a/locks.h b/locks.h
new file mode 100644
index 0000000..f64ccbb
--- /dev/null
+++ b/locks.h
@@ -0,0 +1,182 @@
+/*
+ * pin.c: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * Date: 11.04.05 - 02.02.07, horchi
+ */
+
+#ifndef VDR_PIN_LOCKS_H
+#define VDR_PIN_LOCKS_H
+
+//***************************************************************************
+// Includes
+//***************************************************************************
+
+#include <vdr/tools.h>
+#include <vdr/config.h>
+
+#include "rep.h"
+#include "def.h"
+
+//***************************************************************************
+// Lock Item
+//***************************************************************************
+
+class cLockItem : public cListObject
+{
+ public:
+
+ enum SearchMode
+ {
+ smUnknown = na,
+
+ smRegexp,
+ smInclude,
+ smExact,
+ smStartWith,
+
+ smCount
+ };
+
+ cLockItem(int aActive = no);
+ cLockItem(const char* aName, int aActive = yes, const char* aTitle = 0);
+ virtual ~cLockItem();
+
+ // interface
+
+ virtual bool Parse(char* line);
+ virtual bool Save(FILE* file);
+
+ // tests
+
+ virtual int Locked(long startTime = 0);
+ bool MatchPattern(const char* aName);
+ bool HasName(const char* aName);
+
+ // gettings
+
+ int GetActive() { return active; }
+ const char* GetName() { return name; }
+ const char* GetTitle() { return title; }
+ long GetStart() { return start; }
+ long GetEnd() { return end; }
+ char* GetStart(char* ct) { return DT::int2Hhmm(start, ct); }
+ char* GetEnd(char* ct) { return DT::int2Hhmm(end, ct); }
+ int HasRange() { return hasRange; }
+ const char* GetPattern() { return pattern; }
+ int SearchMode() { return searchMode; }
+
+ // settings
+
+ void SetActive(int aActive) { active = aActive; }
+ void SetName(const char* aName);
+ void SetTitle(const char* aTitle);
+ void SetStart(const char* ct) { start = DT::hhmm2Int(ct); }
+ void SetEnd(const char* ct) { end = DT::hhmm2Int(ct); }
+ void SetStart(long lt) { start = lt; }
+ void SetEnd(long lt) { end = lt; }
+ void SetRangeFlag(int flag) { hasRange = flag; }
+ void SetSearchMode(int aMode) { searchMode = aMode; }
+ void SetPattern(const char* aPattern);
+
+ static const char* searchModes[smCount+1];
+
+ protected:
+
+ char* name;
+ char* title;
+ long start; // in seconds
+ long end; // in seconds
+ int active;
+ int hasRange;
+ char* pattern;
+ int searchMode;
+};
+
+//***************************************************************************
+// Locked Broadcast
+//***************************************************************************
+
+class cLockedBroadcast : public cLockItem
+{
+ public:
+
+ cLockedBroadcast();
+ cLockedBroadcast(const char* aName);
+ virtual ~cLockedBroadcast();
+
+ // interface
+
+ virtual bool Parse(char* line);
+ virtual bool Save(FILE* file);
+
+ // tests
+
+ virtual int Locked(long startTime = 0);
+};
+
+//***************************************************************************
+// Lock List Base
+//***************************************************************************
+
+template<class T> class cLockList : public cConfig<T>
+{
+ public:
+
+ cLockList(int aType) : cConfig<T>::cConfig() { listType = aType; }
+
+ virtual int Locked(const char* aName, long startTime = 0) = 0;
+ virtual int GetListType() { return listType; }
+ virtual void SetListType(int aType) { listType = aType; }
+
+ virtual cLockItem* FindByName(const char* aName)
+ {
+ T* item;
+
+ if (!aName)
+ return 0;
+
+ for (item = (T*)this->First(); item; item = (T*)this->Next(item))
+ {
+ if (item->HasName(aName))
+ return item;
+ }
+
+ return 0;
+ }
+
+ protected:
+
+ int listType;
+};
+
+//***************************************************************************
+// Lock Item List
+//***************************************************************************
+
+class cLockItems : public cLockList<cLockItem>
+{
+ public:
+
+ cLockItems(int aType = na) : cLockList<cLockItem>::cLockList(aType) {}
+
+ virtual int Locked(const char* aName, long startTime = 0);
+};
+
+//***************************************************************************
+// Lock Broadcasts List
+//***************************************************************************
+
+class cLockedBroadcasts : public cLockList<cLockedBroadcast>
+{
+ public:
+
+ cLockedBroadcasts(int aType = na) : cLockList<cLockedBroadcast>::cLockList(aType) {}
+
+ virtual int Locked(const char* aName, long startTime = 0);
+};
+
+//***************************************************************************
+#endif // VDR_PIN_LOCKS_H
+
diff --git a/menu.c b/menu.c
new file mode 100644
index 0000000..a5fd2c2
--- /dev/null
+++ b/menu.c
@@ -0,0 +1,518 @@
+/*
+ * pin.c: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+//***************************************************************************
+// Includes
+//***************************************************************************
+
+#include <vdr/interface.h>
+
+#include "menu.h"
+#include "pin.h"
+
+//***************************************************************************
+// Pin Menu
+//***************************************************************************
+//***************************************************************************
+// Object
+//***************************************************************************
+
+cPinMenu::cPinMenu(const char* title, cLockItems* theChannels,
+ cLockedBroadcasts* theBroadcasts, cLockItems* thePlugins,
+ cLockItems* theMenuItems)
+ : cOsdMenu(title)
+{
+ cLockItem* setupItem;
+ int setupLocked = no;
+
+ lockedChannels = theChannels;
+ lockedBroadcasts = theBroadcasts;
+ lockedPlugins = thePlugins;
+ lockedMenuItems = theMenuItems;
+
+ if ((setupItem = lockedMenuItems->FindByName(tr("Setup"))))
+ setupLocked = setupItem->GetActive();
+
+ if (!setupLocked)
+ if ((setupItem = lockedPlugins->FindByName("setup")))
+ setupLocked = setupItem->GetActive();
+
+ SetHasHotkeys();
+
+ cOsdMenu::Add(new cPinMenuItem(hk(tr("Add current channel to protection list")), miAddCurrentChannel));
+ cOsdMenu::Add(new cPinMenuItem(hk(tr("Add current broadcast to protection list")), miAddCurrentBroadcast));
+ cOsdMenu::Add(new cPinMenuItem(hk(tr("Edit channel locks")), miEditChannelList));
+ cOsdMenu::Add(new cPinMenuItem(hk(tr("Edit broadcast locks")), miEditBroadcastList));
+ cOsdMenu::Add(new cPinMenuItem(hk(tr("Edit plugin locks")), miEditPluginList));
+ cOsdMenu::Add(new cPinMenuItem(hk(tr("Edit menu locks")), miEditMenuItemList));
+ cOsdMenu::Add(new cPinMenuItem(setupLocked ? hk(tr("Unlock setup")) : hk(tr("Lock setup")), miProtectSetup));
+ cOsdMenu::Add(new cPinMenuItem(hk(tr("Activate childlock")), miLock));
+
+ Display();
+}
+
+cPinMenu::~cPinMenu()
+{
+ // Aenderungen speichern
+
+ lockedChannels->Save();
+ lockedBroadcasts->Save();
+ lockedPlugins->Save();
+ lockedMenuItems->Save();
+}
+
+//***************************************************************************
+// Process Key
+//***************************************************************************
+
+eOSState cPinMenu::ProcessKey(eKeys key)
+{
+ eOSState state = cOsdMenu::ProcessKey(key);
+
+ switch (state)
+ {
+ case miLock:
+ {
+ if (*cPinPlugin::pinCode)
+ {
+ cOsd::pinValid = false;
+ Skins.Message(mtInfo, tr("Activated pin protection"));
+ }
+ else
+ {
+ Skins.Message(mtInfo, tr("Please setup a pin code first!"));
+ }
+
+ return osEnd;
+ }
+
+ case miAddCurrentChannel: addCurrentChannel(); break;
+ case miAddCurrentBroadcast: addCurrentBroadcast(); break;
+
+ case miEditChannelList:
+ state = AddSubMenu(new cLockMenu(tr("Protected channels"),
+ lockedChannels, ltChannels));
+ break;
+ case miEditBroadcastList:
+ state = AddSubMenu(new cLockMenu(tr("Protected broadcasts"),
+ (cLockItems*)lockedBroadcasts, ltBroadcasts));
+ break;
+ case miEditPluginList:
+ state = AddSubMenu(new cLockMenu(tr("Plugin protections"),
+ lockedPlugins, ltPlugins));
+ break;
+ case miEditMenuItemList:
+ state = AddSubMenu(new cLockMenu(tr("Menu protections"),
+ lockedMenuItems, ltMenuItems));
+ break;
+ case miProtectSetup:
+ {
+ char* buf;
+ int res;
+ int locked = protectSetup();
+ cPinMenuItem* item = (cPinMenuItem*)Get(7-1);
+ int hotKey = atoi(item->Text());
+
+ res = asprintf(&buf, " %2d %s", hotKey, locked ? tr("Unlock setup") : tr("Lock setup"));
+ if (res < 0) ; // memory problems :o
+ item->SetText(buf, /*copy*/ true);
+ free(buf);
+
+ Display();
+ break;
+ }
+
+ default: ;
+ }
+
+ return state;
+}
+
+//***************************************************************************
+// Protect Setup
+//***************************************************************************
+
+int cPinMenu::protectSetup()
+{
+ cLockItem* setupItemPlugin;
+ cLockItem* setupItemMenu;
+ char* buf;
+ int locked;
+ int res;
+
+ if (!(setupItemMenu = lockedMenuItems->FindByName(tr("Setup"))))
+ lockedMenuItems->Add(setupItemMenu = new cLockItem(tr("Setup")));
+
+ if (!(setupItemPlugin = lockedPlugins->FindByName("setup")))
+ lockedPlugins->Add(setupItemPlugin = new cLockItem("setup"));
+
+ locked = !(setupItemPlugin->GetActive() && setupItemMenu->GetActive());
+
+ setupItemPlugin->SetActive(locked);
+ setupItemMenu->SetActive(locked);
+
+ res = asprintf(&buf, "setup %s", locked ? tr("locked") : tr("unlocked"));
+ if (res < 0) ; // memory problems :o
+ Skins.Message(mtInfo, buf);
+ free(buf);
+
+ return locked;
+}
+
+//***************************************************************************
+// Add Current Channel
+//***************************************************************************
+
+int cPinMenu::addCurrentChannel()
+{
+ const cChannel* channel = 0;
+ char* buf;
+ int res;
+
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+ LOCK_CHANNELS_READ;
+
+ channel = Channels->GetByNumber(cDevice::CurrentChannel());
+#else
+ channel = Channels.GetByNumber(cDevice::CurrentChannel());
+#endif
+
+ if (!channel || channel->GroupSep())
+ return ignore;
+
+ if (lockedChannels->FindByName(channel->Name()))
+ {
+ res = asprintf(&buf, "%s - %s", channel->Name(), tr("already in list"));
+ if (res < 0) ; // memory problems :o
+ Skins.Message(mtInfo, buf);
+ free(buf);
+
+ return ignore;
+ }
+
+ res = asprintf(&buf, "%s - %s", channel->Name(), tr("added to protection list"));
+ if (res < 0) ; // memory problems :o
+ Skins.Message(mtInfo, buf);
+ free(buf);
+
+ // append channel to lock list
+
+ lockedChannels->Add(new cLockItem(channel->Name()));
+
+ return success;
+}
+
+//***************************************************************************
+// Add Current Broadcast
+//***************************************************************************
+
+int cPinMenu::addCurrentBroadcast()
+{
+ const cChannel* channel = 0;
+ const cSchedule* sched;
+ const cEvent* event;
+ char* buf;
+ int res;
+
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+ LOCK_CHANNELS_READ;
+
+ channel = Channels->GetByNumber(cDevice::CurrentChannel());
+#else
+ channel = Channels.GetByNumber(cDevice::CurrentChannel());
+#endif
+
+ if (channel && !channel->GroupSep())
+ {
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+ cStateKey schedulesKey;
+ cSchedules* schedules = cSchedules::GetSchedulesWrite(schedulesKey, 100/*ms*/);
+#else
+ cSchedulesLock schedulesLock(true);
+ cSchedules* schedules = (cSchedules*)cSchedules::Schedules(schedulesLock);
+#endif
+
+ if (!schedules)
+ return done;
+
+ if (!(sched = schedules->GetSchedule(channel->GetChannelID())) ||
+ !(event = sched->GetPresentEvent()))
+ {
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+ schedulesKey.Remove();
+#endif
+ return done;
+ }
+
+ // Info
+
+ res = asprintf(&buf, "%s - %s", event->Title(), tr("added to protection list"));
+ if (res < 0) ; // memory problems :o
+ Skins.Message(mtInfo, buf);
+ free(buf);
+
+ // append event to lock list
+
+ lockedBroadcasts->Add(new cLockedBroadcast(event->Title()));
+
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+ schedulesKey.Remove();
+#endif
+ }
+
+ return success;
+}
+
+//***************************************************************************
+// Pin Menu Item
+//***************************************************************************
+//***************************************************************************
+// Process Key
+//***************************************************************************
+
+cPinMenuItem::cPinMenuItem(const char* title, int osState)
+ : cOsdItem(title, (eOSState)osState)
+{
+
+}
+
+//***************************************************************************
+// Lock Menu
+//***************************************************************************
+//***************************************************************************
+// Object
+//***************************************************************************
+
+cLockMenu::cLockMenu(const char* title, cLockItems* theItems, ListType theType)
+ : cOsdMenu(title)
+{
+ cLockItem* item;
+
+ items = theItems;
+ type = theType;
+
+ for (item = items->First(); item; item = items->Next(item))
+ cOsdMenu::Add(new cLockMenuItem(item));
+
+ if (type == ltPlugins)
+ SetHelp(tr("Edit"), 0, 0, tr("On/Off"));
+ else
+ SetHelp(tr("Edit"), tr("Add"), tr("Delete"), tr("On/Off"));
+
+ Display();
+}
+
+cLockMenu::~cLockMenu()
+{
+}
+
+//***************************************************************************
+// Process Key
+//***************************************************************************
+
+eOSState cLockMenu::ProcessKey(eKeys key)
+{
+ eOSState state = cOsdMenu::ProcessKey(key);
+
+ if (state == osUnknown)
+ {
+ switch (key)
+ {
+ case kOk:
+ case kRed: // Edit
+ {
+ if (!items->Count() || !Get(Current()))
+ break;
+
+ if (items->GetListType() == cPinPlugin::ltBroadcasts)
+ state = AddSubMenu(new cBroadcastEditMenu((cLockedBroadcast*)
+ (((cLockMenuItem*)Get(Current()))->GetLockItem())));
+ else
+ state = AddSubMenu(new cLockEditMenu(((cLockMenuItem*)Get(Current()))->GetLockItem()));
+
+ break;
+ }
+
+ case kGreen: // New
+ {
+ cLockItem* item;
+
+ if (type != ltPlugins)
+ {
+ if (items->GetListType() == cPinPlugin::ltBroadcasts)
+ items->Add(item = new cLockedBroadcast("- new -"));
+ else
+ items->Add(item = new cLockItem("- new -"));
+
+ cOsdMenu::Add(new cLockMenuItem(item));
+
+ Display();
+ }
+
+ break;
+ }
+
+ case kYellow: // Delete
+ {
+ if (type != ltPlugins)
+ {
+ if (!items->Count() || !Get(Current()))
+ break;
+
+ if (Interface->Confirm(tr("Remove entry?")))
+ {
+ // remove from list
+
+ items->Del(((cLockMenuItem*)Get(Current()))->GetLockItem());
+ cOsdMenu::Del(Current());
+ }
+
+ Display();
+ }
+
+ break;
+ }
+
+ case kBlue: // Toggle
+ {
+ cLockItem* p = ((cLockMenuItem*)Get(Current()))->GetLockItem();
+
+ p->SetActive(!p->GetActive());
+ ((cLockMenuItem*)Get(Current()))->Set();
+
+ Display();
+
+ break;
+ }
+
+ default: break;
+ }
+ }
+
+ return state;
+}
+
+//***************************************************************************
+// Lock Menu Item
+//***************************************************************************
+//***************************************************************************
+// Object
+//***************************************************************************
+
+cLockMenuItem::cLockMenuItem(cLockItem* aItem)
+ : cOsdItem()
+{
+ item = aItem;
+ Set();
+}
+
+cLockMenuItem::~cLockMenuItem()
+{
+}
+
+//***************************************************************************
+// Set
+//***************************************************************************
+
+void cLockMenuItem::Set()
+{
+ char* tmp;
+ int res;
+ res = asprintf(&tmp, "%c %s", item->GetActive() ? '§' : ' ', GetTitle());
+ if (res < 0) ; // memory problems :o
+ SetText(tmp, /*copy*/ true);
+ free(tmp);
+}
+
+//***************************************************************************
+// Get Title
+//***************************************************************************
+
+const char* cLockMenuItem::GetTitle()
+{
+ if (item->GetTitle())
+ return item->GetTitle();
+
+ return item->GetName();
+}
+
+//***************************************************************************
+// Lock Edit Menu
+//***************************************************************************
+//***************************************************************************
+// Object
+//***************************************************************************
+
+cLockEditMenu::cLockEditMenu(cLockItem* aItem)
+{
+ static const char* trSearchModes[cLockedBroadcast::smCount] = { 0 };
+
+ item = aItem;
+ strncpy(name, item->GetName(), sizeNameMax);
+ name[sizeNameMax] = 0;
+ strncpy(pattern, aItem->GetPattern(), sizePatternMax);
+ pattern[sizePatternMax] = 0;
+ active = item->GetActive();
+ searchMode = aItem->SearchMode();
+
+ // translate search modes
+
+ if (!trSearchModes[0])
+ for (int i = 0; i < cLockedBroadcast::smCount; i++)
+ trSearchModes[i] = tr(cLockedBroadcast::searchModes[i]);
+
+ Add(new cMenuEditStrItem(tr("Name"), name, sizeNameMax, editCharacters));
+ Add(new cMenuEditBoolItem(tr("Lock active"), &active));
+ Add(new cMenuEditStrItem(tr("Keyword"), pattern, sizePatternMax, editCharacters));
+ Add(new cMenuEditStraItem(tr("Search mode"), &searchMode, cLockedBroadcast::smCount, trSearchModes));
+
+ if (item->HasRange())
+ {
+ long l;
+
+ l = item->GetStart();
+ start = (l/60/60) * 100 + l/60%60;
+ l = item->GetEnd();
+ end = (l/60/60) * 100 + l/60%60;
+
+ Add(new cMenuEditTimeItem(tr("Start"), &start));
+ Add(new cMenuEditTimeItem(tr("Stop"), &end));
+ }
+
+ SetTitle(item->GetTitle() ? item->GetTitle() : item->GetName());
+}
+
+//***************************************************************************
+// Store
+//***************************************************************************
+
+void cLockEditMenu::Store(void)
+{
+ item->SetName(name);
+ item->SetActive(active);
+ item->SetPattern(pattern);
+ item->SetSearchMode(searchMode);
+
+ if (item->HasRange())
+ {
+ item->SetStart((start/100)*60*60 + (start%100)*60);
+ item->SetEnd((end/100)*60*60 + (end%100)*60);
+ }
+}
+
+//***************************************************************************
+// Broadcast Edit Menu
+//***************************************************************************
+//***************************************************************************
+// Object
+//***************************************************************************
+
+cBroadcastEditMenu::cBroadcastEditMenu(cLockedBroadcast* aItem)
+ : cLockEditMenu((cLockItem*)aItem)
+{
+ // notching yet ...
+}
diff --git a/menu.h b/menu.h
new file mode 100644
index 0000000..186cd3a
--- /dev/null
+++ b/menu.h
@@ -0,0 +1,151 @@
+/*
+ * menu.h: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ */
+
+//***************************************************************************
+// Includes
+//***************************************************************************
+
+#include <vdr/interface.h>
+#include <vdr/osdbase.h>
+#include <vdr/plugin.h>
+
+#include "locks.h"
+#include "pin.h"
+
+#define editCharacters " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+-/()[]{}ßöäüÖÄÜ.,;#<>@0123456789"
+
+//***************************************************************************
+// Pin Menu Item
+//***************************************************************************
+
+class cPinMenuItem : public cOsdItem
+{
+ public:
+
+ cPinMenuItem(const char* title, int osState);
+};
+
+//***************************************************************************
+// Pin Menu
+//***************************************************************************
+
+class cPinMenu : public cOsdMenu, public PinService
+{
+ public:
+
+ enum Item
+ {
+ miLock = os_User,
+ miAddCurrentChannel,
+ miDelChannel,
+ miEditChannelList,
+ miEditBroadcastList,
+ miAddCurrentBroadcast,
+ miEditPluginList,
+ miEditMenuItemList,
+ miProtectSetup
+ };
+
+ cPinMenu(const char* title, cLockItems* theChannels,
+ cLockedBroadcasts* theBroadcasts, cLockItems* thePlugins,
+ cLockItems* theMenuItems);
+ virtual ~cPinMenu();
+
+ virtual eOSState ProcessKey(eKeys key);
+
+ int addCurrentChannel();
+ int addCurrentBroadcast();
+ int protectSetup();
+
+ protected:
+
+ cLockItems* lockedChannels;
+ cLockedBroadcasts* lockedBroadcasts;
+ cLockItems* lockedPlugins;
+ cLockItems* lockedMenuItems;
+};
+
+//***************************************************************************
+// Channel Menu Item
+//***************************************************************************
+
+class cLockMenuItem : public cOsdItem
+{
+
+ public:
+
+ cLockMenuItem(cLockItem* aItem);
+ virtual ~cLockMenuItem();
+
+ const char* GetTitle();
+ cLockItem* GetLockItem() { return item; }
+ virtual void Set();
+
+ protected:
+
+ cLockItem* item;
+};
+
+//***************************************************************************
+// Channel Menu
+//***************************************************************************
+
+class cLockMenu : public cOsdMenu, public PinService
+{
+ public:
+
+ cLockMenu(const char* title, cLockItems* theItems, ListType theType);
+ virtual ~cLockMenu();
+
+ eOSState ProcessKey(eKeys key);
+
+ protected:
+
+ cLockItems* items;
+ ListType type;
+};
+
+//***************************************************************************
+// Channel Edit Menu
+//***************************************************************************
+
+class cLockEditMenu : public cMenuSetupPage
+{
+ public:
+
+ enum Misc
+ {
+ sizeNameMax = 50,
+ sizePatternMax = 50,
+ sizeTitleMax = 50
+ };
+
+ cLockEditMenu(cLockItem* aItem);
+ virtual ~cLockEditMenu() {};
+
+ virtual void Store(void);
+
+ protected:
+
+ cLockItem* item;
+ char name[sizeNameMax+TB];
+ int active;
+ int start;
+ int end;
+ char pattern[sizePatternMax+TB];
+ int searchMode;
+};
+
+//***************************************************************************
+// Broadcast Edit Menu
+//***************************************************************************
+
+class cBroadcastEditMenu : public cLockEditMenu
+{
+ public:
+
+ cBroadcastEditMenu(cLockedBroadcast* aBroadcast);
+};
diff --git a/msgreceiver.c b/msgreceiver.c
new file mode 100644
index 0000000..478b65b
--- /dev/null
+++ b/msgreceiver.c
@@ -0,0 +1,141 @@
+/*
+ * msgreceiver.c: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * Date: 11.04.05 - 10.09.06
+ */
+
+//***************************************************************************
+// Includes
+//***************************************************************************
+
+#include <sys/stat.h>
+#include <sys/msg.h>
+#include <signal.h>
+
+#include <vdr/interface.h>
+#include "pin.h"
+
+//***************************************************************************
+// Object
+//***************************************************************************
+
+MessageReceiver::MessageReceiver()
+{
+ active = false;
+ talk = new Talk;
+ talk->setTimeout(0); // wichtig !!
+}
+
+MessageReceiver::~MessageReceiver()
+{
+ delete talk;
+}
+
+//***************************************************************************
+// Start Receiver
+//***************************************************************************
+
+int MessageReceiver::StartReceiver()
+{
+ if (!talk->isOpen())
+ talk->open(10);
+
+ if (!active)
+ {
+ Start();
+ }
+ else
+ {
+ tell(eloDebug, "Info: Receiver still running, stopping first!");
+
+ StopReceiver();
+ usleep(250);
+ Start();
+ }
+
+ return success;
+}
+
+//***************************************************************************
+// Stop Receiver
+//***************************************************************************
+
+int MessageReceiver::StopReceiver()
+{
+ tell(eloAlways, "Try to stop receiver thread (%d)", pid);
+ active = false;
+ talk->send(10, Talk::evtExit);
+ Cancel(3);
+
+ return success;
+}
+
+//***************************************************************************
+// Action
+//***************************************************************************
+
+void MessageReceiver::Action()
+{
+ active = true;
+ pid = getpid();
+
+ tell(eloAlways, "PIN plugin receiver thread started (pid=%d)", pid);
+
+ while (active)
+ {
+ wait();
+ tell(eloDebug, "wait ...");
+ }
+
+ tell(eloAlways, "PIN plugin receiver thread ended (pid=%d)", pid);
+}
+
+//***************************************************************************
+// Wait
+//***************************************************************************
+
+int MessageReceiver::wait()
+{
+ int status;
+
+ if ((status = talk->wait()) != success)
+ {
+ sleep(1);
+
+ return status;
+ }
+
+ // info
+
+ tell(eloDebug, "Got event (%d) from (%ld) with message [%s]",
+ talk->getEvent(), talk->getFrom(),
+ talk->getMessage() ? talk->getMessage() : "");
+
+ // perform request
+
+ switch (talk->getEvent())
+ {
+ case Talk::evtShow:
+ {
+ if (talk->getMessage() && *talk->getMessage())
+ {
+ Skins.QueueMessage(mtInfo, tr(talk->getMessage()));
+ tell(eloDebug, "Got '%s'", talk->getMessage());
+ }
+
+ break;
+ }
+
+ case Talk::evtCheck:
+ {
+ int evt = cOsd::pinValid ? Talk::evtConfirm : Talk::evtAbort;
+
+ if (talk->send(11, evt) != success)
+ tell(eloDebug, "Sending event (%d) to (%d) failed", evt, 11);
+ }
+ }
+
+ return success;
+}
diff --git a/patches/epgsearch-0.9.19.diff b/patches/epgsearch-0.9.19.diff
new file mode 100644
index 0000000..75d6d29
--- /dev/null
+++ b/patches/epgsearch-0.9.19.diff
@@ -0,0 +1,85 @@
+--- ../epgsearch-0.9.19/menu_myedittimer.c 2006-09-24 20:05:41.000000000 +0200
++++ menu_myedittimer.c 2006-11-28 13:50:41.000000000 +0100
+@@ -29,6 +29,7 @@
+ stop = Timer->Stop();
+ priority = Timer->Priority();
+ lifetime = Timer->Lifetime();
++ fskProtection = Timer->FskProtection(); // PIN PATCH
+ strcpy(file, Timer->File());
+ channel = Timer->Channel()->Number();
+ if (forcechannel)
+@@ -94,6 +95,14 @@
+ Add(new cMenuEditBitItem( tr("VPS"), &flags, tfVps));
+ Add(new cMenuEditIntItem( tr("Priority"), &priority, 0, MAXPRIORITY));
+ Add(new cMenuEditIntItem( tr("Lifetime"), &lifetime, 0, MAXLIFETIME));
++ // PIN PATCH
++ if (cOsd::pinValid || !fskProtection) Add(new cMenuEditBoolItem(tr("Childlock"),&fskProtection));
++ else {
++ char* buf = 0;
++ asprintf(&buf, "%s\t%s", tr("Childlock"), fskProtection ? tr("yes") : tr("no"));
++ Add(new cOsdItem(buf));
++ free(buf);
++ }
+ Add(new cMenuEditStrItem( tr("File"), file, MaxFileName, tr(FileNameChars)));
+ Add(new cMenuEditStrItem( tr("Directory"), directory, MaxFileName, tr(AllowedChars)));
+
+@@ -204,6 +213,33 @@
+ return osContinue;
+ }
+
++char* cMenuMyEditTimer::SetFskProtection(int fskProtection, char* aux) // PIN PATCH
++{
++ char* p;
++ char* tmp = 0;
++
++ if (fskProtection && (!aux || !strstr(aux, "<pin-plugin><protected>yes</protected></pin-plugin>")))
++ {
++ // add protection info to aux
++
++ if (aux) { tmp = strdup(aux); free(aux); }
++ asprintf(&aux,"%s<pin-plugin><protected>yes</protected></pin-plugin>", tmp ? tmp : "");
++ }
++ else if (!fskProtection && aux && (p = strstr(aux, "<pin-plugin><protected>yes</protected></pin-plugin>")))
++ {
++ // remove protection info to aux
++
++ asprintf(&tmp, "%.*s%s", p-aux, aux, p+strlen("<pin-plugin><protected>yes</protected></pin-plugin>"));
++ free(aux);
++ aux = strdup(tmp);
++ }
++
++ if (tmp)
++ free(tmp);
++
++ return aux;
++}
++
+ eOSState cMenuMyEditTimer::ProcessKey(eKeys Key)
+ {
+ eOSState state = cOsdMenu::ProcessKey(Key);
+@@ -284,6 +320,7 @@
+ char* tmpSummary = NULL;
+ if (timer && timer->Aux())
+ tmpSummary = strdup(timer->Aux());
++ tmpSummary = SetFskProtection(fskProtection, tmpSummary); // PIN PATCH
+ if (timer)
+ {
+ char* cmdbuf = NULL;
+--- ../epgsearch-0.9.19/menu_myedittimer.h 2006-09-24 20:05:41.000000000 +0200
++++ menu_myedittimer.h 2006-11-28 13:48:45.000000000 +0100
+@@ -15,6 +15,7 @@
+ cMenuEditDateItem *firstday;
+ void SetFirstDayItem(void);
+ cMenuEditStrItem* m_DirItem;
++ char* SetFskProtection(int fskProtection, char* aux); // PIN PATCH
+
+ uint flags;
+ time_t day;
+@@ -25,6 +26,7 @@
+ int lifetime;
+ char file[MaxFileName];
+ char directory[MaxFileName];
++ int fskProtection; // PIN PATCH
+ public:
+ cMenuMyEditTimer(cTimer *Timer, bool New, const cEvent* event, const cChannel* forcechannel=NULL);
+ virtual ~cMenuMyEditTimer();
diff --git a/patches/epgsearch-0.9.20.diff b/patches/epgsearch-0.9.20.diff
new file mode 100644
index 0000000..c1c2602
--- /dev/null
+++ b/patches/epgsearch-0.9.20.diff
@@ -0,0 +1,221 @@
+--- ../plain/epgsearch-0.9.20//i18n.c 2007-01-28 16:29:57.000000000 +0100
++++ i18n.c 2007-02-03 16:54:11.000000000 +0100
+@@ -7327,6 +7327,28 @@
+ "",// TODO
+ "",// TODO
+ },
++ { "Childlock",
++ "Kindersicherung",
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "Adulte",
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ },
+ { NULL }
+ };
+
+--- ../plain/epgsearch-0.9.20//menu_commands.c 2007-01-13 15:20:59.000000000 +0100
++++ menu_commands.c 2007-02-10 09:38:32.000000000 +0100
+@@ -135,11 +135,11 @@
+ else
+ {
+ string fullaux = "";
++ string aux = "";
+ if (event)
+ {
+ int bstart = event->StartTime() - timer->StartTime();
+ int bstop = timer->StopTime() - event->EndTime();
+- string aux = "";
+ int checkmode = DefTimerCheckModes.GetMode(timer->Channel());
+ aux = UpdateAuxValue(aux, "update", checkmode);
+ aux = UpdateAuxValue(aux, "eventid", event->EventID());
+@@ -147,6 +147,12 @@
+ aux = UpdateAuxValue(aux, "bstop", bstop);
+ fullaux = UpdateAuxValue(fullaux, "epgsearch", aux);
+ }
++
++ // #PIN PATCH
++ aux = "";
++ aux = UpdateAuxValue(aux, "protected", timer->FskProtection() ? "yes" : "no");
++ fullaux = UpdateAuxValue(fullaux, "pin-plugin", aux);
++
+ SetAux(timer, fullaux);
+ Timers.Add(timer);
+ timer->Matches();
+--- ../plain/epgsearch-0.9.20//menu_main.c 2007-01-16 23:38:11.000000000 +0100
++++ menu_main.c 2007-02-10 09:38:47.000000000 +0100
+@@ -159,12 +159,12 @@
+ else
+ {
+ string fullaux = "";
++ string aux = "";
+ if (item->event)
+ {
+ const cEvent* event = item->event;
+ int bstart = event->StartTime() - timer->StartTime();
+ int bstop = timer->StopTime() - event->EndTime();
+- string aux = "";
+ int checkmode = DefTimerCheckModes.GetMode(timer->Channel());
+ aux = UpdateAuxValue(aux, "update", checkmode);
+ aux = UpdateAuxValue(aux, "eventid", event->EventID());
+@@ -172,6 +172,12 @@
+ aux = UpdateAuxValue(aux, "bstop", bstop);
+ fullaux = UpdateAuxValue(fullaux, "epgsearch", aux);
+ }
++
++ // #PIN PATCH
++ aux = "";
++ aux = UpdateAuxValue(aux, "protected", timer->FskProtection() ? "yes" : "no");
++ fullaux = UpdateAuxValue(fullaux, "pin-plugin", aux);
++
+ SetAux(timer, fullaux);
+ Timers.Add(timer);
+ timer->Matches();
+--- ../plain/epgsearch-0.9.20//menu_myedittimer.c 2007-01-23 20:26:12.000000000 +0100
++++ menu_myedittimer.c 2007-02-10 09:40:33.000000000 +0100
+@@ -36,6 +36,7 @@
+ stop = Timer->Stop();
+ priority = Timer->Priority();
+ lifetime = Timer->Lifetime();
++ fskProtection = Timer->FskProtection(); // PIN PATCH
+ strcpy(file, Timer->File());
+ channel = Timer->Channel()->Number();
+ if (forcechannel)
+@@ -107,6 +108,14 @@
+ Add(new cMenuEditBitItem( tr("VPS"), &flags, tfVps));
+ Add(new cMenuEditIntItem( tr("Priority"), &priority, 0, MAXPRIORITY));
+ Add(new cMenuEditIntItem( tr("Lifetime"), &lifetime, 0, MAXLIFETIME));
++ // PIN PATCH
++ if (cOsd::pinValid || !fskProtection) Add(new cMenuEditBoolItem(tr("Childlock"),&fskProtection));
++ else {
++ char* buf = 0;
++ asprintf(&buf, "%s\t%s", tr("Childlock"), fskProtection ? tr("yes") : tr("no"));
++ Add(new cOsdItem(buf));
++ free(buf);
++ }
+ Add(new cMenuEditStrItem( tr("File"), file, MaxFileName, tr(FileNameChars)));
+ Add(new cMenuEditStrItem( tr("Directory"), directory, MaxFileName, tr(AllowedChars)));
+
+@@ -211,6 +220,33 @@
+ return osContinue;
+ }
+
++char* cMenuMyEditTimer::SetFskProtection(int fskProtection, char* aux) // PIN PATCH
++{
++ char* p;
++ char* tmp = 0;
++
++ if (fskProtection && (!aux || !strstr(aux, "<pin-plugin><protected>yes</protected></pin-plugin>")))
++ {
++ // add protection info to aux
++
++ if (aux) { tmp = strdup(aux); free(aux); }
++ asprintf(&aux,"%s<pin-plugin><protected>yes</protected></pin-plugin>", tmp ? tmp : "");
++ }
++ else if (!fskProtection && aux && (p = strstr(aux, "<pin-plugin><protected>yes</protected></pin-plugin>")))
++ {
++ // remove protection info to aux
++
++ asprintf(&tmp, "%.*s%s", p-aux, aux, p+strlen("<pin-plugin><protected>yes</protected></pin-plugin>"));
++ free(aux);
++ aux = strdup(tmp);
++ }
++
++ if (tmp)
++ free(tmp);
++
++ return aux;
++}
++
+ eOSState cMenuMyEditTimer::ProcessKey(eKeys Key)
+ {
+ bool bWasSingleEvent = IsSingleEvent();
+@@ -290,6 +326,7 @@
+ }
+
+ string fullaux = "";
++ string aux = "";
+ if (timer && timer->Aux())
+ fullaux = timer->Aux();
+
+@@ -307,7 +344,6 @@
+ bstop = stopTime - event->EndTime();
+
+ char* epgsearchaux = GetAuxValue(timer, "epgsearch");
+- string aux = "";
+ if (epgsearchaux)
+ {
+ aux = epgsearchaux;
+@@ -320,6 +356,10 @@
+ fullaux = UpdateAuxValue(fullaux, "epgsearch", aux);
+ }
+
++ // #PIN PATCH
++ aux = "";
++ aux = UpdateAuxValue(aux, "protected", timer->FskProtection() ? "yes" : "no");
++ fullaux = UpdateAuxValue(fullaux, "pin-plugin", aux);
+
+ char* tmpFile = strdup(file);
+ tmpFile = strreplace(tmpFile, ':', '|');
+--- ../plain/epgsearch-0.9.20//menu_myedittimer.h 2006-12-08 21:50:22.000000000 +0100
++++ menu_myedittimer.h 2007-02-03 16:40:11.000000000 +0100
+@@ -13,6 +13,7 @@
+ bool addIfConfirmed;
+ int UserDefDaysOfWeek;
+ cMenuEditStrItem* m_DirItem;
++ char* SetFskProtection(int fskProtection, char* aux); // PIN PATCH
+
+ uint flags;
+ time_t day;
+@@ -23,6 +24,7 @@
+ int lifetime;
+ char file[MaxFileName];
+ char directory[MaxFileName];
++ int fskProtection; // PIN PATCH
+ int checkmode;
+ public:
+ cMenuMyEditTimer(cTimer *Timer, bool New, const cEvent* event, const cChannel* forcechannel=NULL);
+--- ../plain/epgsearch-0.9.20//menu_whatson.c 2007-01-27 14:30:52.000000000 +0100
++++ menu_whatson.c 2007-02-10 09:39:52.000000000 +0100
+@@ -450,12 +450,12 @@
+ else
+ {
+ string fullaux = "";
++ string aux = "";
+ if (item->event)
+ {
+ const cEvent* event = item->event;
+ int bstart = event->StartTime() - timer->StartTime();
+ int bstop = timer->StopTime() - event->EndTime();
+- string aux = "";
+ int checkmode = DefTimerCheckModes.GetMode(timer->Channel());
+ aux = UpdateAuxValue(aux, "update", checkmode);
+ aux = UpdateAuxValue(aux, "eventid", event->EventID());
+@@ -463,6 +463,12 @@
+ aux = UpdateAuxValue(aux, "bstop", bstop);
+ fullaux = UpdateAuxValue(fullaux, "epgsearch", aux);
+ }
++
++ // #PIN PATCH
++ aux = "";
++ aux = UpdateAuxValue(aux, "protected", timer->FskProtection() ? "yes" : "no");
++ fullaux = UpdateAuxValue(fullaux, "pin-plugin", aux);
++
+ SetAux(timer, fullaux);
+ Timers.Add(timer);
+ timer->Matches();
diff --git a/patches/epgsearch-0.9.21.diff b/patches/epgsearch-0.9.21.diff
new file mode 100644
index 0000000..f5d49e7
--- /dev/null
+++ b/patches/epgsearch-0.9.21.diff
@@ -0,0 +1,194 @@
+--- ../epgsearch-0.9.21-plain//i18n.c 2007-04-28 11:42:43.000000000 +0200
++++ i18n.c 2007-05-28 09:33:29.000000000 +0200
+@@ -9,6 +9,28 @@
+ #include "i18n.h"
+
+ const tI18nPhrase Phrases[] = {
++ { "Childlock",
++ "Kindersicherung",
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "Adulte",
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ },
+ { "search the EPG for repeats and more",
+ "Suche im EPG nach Wiederholungen und anderem",// Deutsch
+ "",// TODO Slovenski
+--- ../epgsearch-0.9.21-plain//menu_commands.c 2007-03-28 21:17:40.000000000 +0200
++++ menu_commands.c 2007-05-28 09:33:29.000000000 +0200
+@@ -135,11 +135,11 @@
+ else
+ {
+ string fullaux = "";
++ string aux = "";
+ if (event)
+ {
+ int bstart = event->StartTime() - timer->StartTime();
+ int bstop = timer->StopTime() - event->EndTime();
+- string aux = "";
+ int checkmode = DefTimerCheckModes.GetMode(timer->Channel());
+ aux = UpdateAuxValue(aux, "update", checkmode);
+ aux = UpdateAuxValue(aux, "eventid", event->EventID());
+@@ -147,6 +147,11 @@
+ aux = UpdateAuxValue(aux, "bstop", bstop);
+ fullaux = UpdateAuxValue(fullaux, "epgsearch", aux);
+ }
++ // #PIN PATCH
++ aux = "";
++ aux = UpdateAuxValue(aux, "protected", timer->FskProtection() ? "yes" : "no");
++ fullaux = UpdateAuxValue(fullaux, "pin-plugin", aux);
++
+ SetAux(timer, fullaux);
+ Timers.Add(timer);
+ timer->Matches();
+--- ../epgsearch-0.9.21-plain//menu_main.c 2007-03-20 19:35:50.000000000 +0100
++++ menu_main.c 2007-05-28 09:33:29.000000000 +0200
+@@ -159,12 +159,12 @@
+ else
+ {
+ string fullaux = "";
++ string aux = "";
+ if (item->event)
+ {
+ const cEvent* event = item->event;
+ int bstart = event->StartTime() - timer->StartTime();
+ int bstop = timer->StopTime() - event->EndTime();
+- string aux = "";
+ int checkmode = DefTimerCheckModes.GetMode(timer->Channel());
+ aux = UpdateAuxValue(aux, "update", checkmode);
+ aux = UpdateAuxValue(aux, "eventid", event->EventID());
+@@ -172,6 +172,12 @@
+ aux = UpdateAuxValue(aux, "bstop", bstop);
+ fullaux = UpdateAuxValue(fullaux, "epgsearch", aux);
+ }
++
++ // #PIN PATCH
++ aux = "";
++ aux = UpdateAuxValue(aux, "protected", timer->FskProtection() ? "yes" : "no");
++ fullaux = UpdateAuxValue(fullaux, "pin-plugin", aux);
++
+ SetAux(timer, fullaux);
+ Timers.Add(timer);
+ timer->Matches();
+--- ../epgsearch-0.9.21-plain//menu_myedittimer.c 2007-03-28 20:43:21.000000000 +0200
++++ menu_myedittimer.c 2007-06-02 22:48:46.000000000 +0200
+@@ -36,6 +36,7 @@
+ stop = Timer->Stop();
+ priority = Timer->Priority();
+ lifetime = Timer->Lifetime();
++ fskProtection = Timer->FskProtection(); // PIN PATCH
+ strcpy(file, Timer->File());
+ channel = Timer->Channel()->Number();
+ if (forcechannel)
+@@ -96,7 +97,14 @@
+ cSearchExt* search = TriggeredFromSearchTimer(timer);
+
+ Add(new cMenuEditBitItem( tr("Active"), &flags, tfActive));
+- Add(new cMenuEditChanItem(tr("Channel"), &channel));
++ // PIN PATCH
++ if (cOsd::pinValid) Add(new cMenuEditChanItem(tr("Channel"), &channel));
++ else {
++ char* buf = 0;
++ asprintf(&buf, "%s\t%s", tr("Channel"), Channels.GetByNumber(channel)->Name());
++ Add(new cOsdItem(buf));
++ free(buf);
++ }
+ Add(new cMenuEditDateItem(tr("Day"), &day, &weekdays));
+
+ if (!IsSingleEvent())
+@@ -107,6 +115,14 @@
+ Add(new cMenuEditBitItem( tr("VPS"), &flags, tfVps));
+ Add(new cMenuEditIntItem( tr("Priority"), &priority, 0, MAXPRIORITY));
+ Add(new cMenuEditIntItem( tr("Lifetime"), &lifetime, 0, MAXLIFETIME));
++ // PIN PATCH
++ if (cOsd::pinValid || !fskProtection) Add(new cMenuEditBoolItem(tr("Childlock"),&fskProtection));
++ else {
++ char* buf = 0;
++ asprintf(&buf, "%s\t%s", tr("Childlock"), fskProtection ? tr("yes") : tr("no"));
++ Add(new cOsdItem(buf));
++ free(buf);
++ }
+ Add(new cMenuEditStrItem( tr("File"), file, MaxFileName, tr(FileNameChars)));
+ Add(new cMenuEditStrItem( tr("Directory"), directory, MaxFileName, tr(AllowedChars)));
+
+@@ -290,6 +306,7 @@
+ }
+
+ string fullaux = "";
++ string aux = "";
+ if (timer && timer->Aux())
+ fullaux = timer->Aux();
+
+@@ -307,7 +324,6 @@
+ bstop = stopTime - event->EndTime();
+
+ char* epgsearchaux = GetAuxValue(timer, "epgsearch");
+- string aux = "";
+ if (epgsearchaux)
+ {
+ aux = epgsearchaux;
+@@ -321,6 +337,10 @@
+ fullaux = UpdateAuxValue(fullaux, "epgsearch", aux);
+ }
+
++ // #PIN PATCH
++ aux = "";
++ aux = UpdateAuxValue(aux, "protected", timer->FskProtection() ? "yes" : "no");
++ fullaux = UpdateAuxValue(fullaux, "pin-plugin", aux);
+
+ char* tmpFile = strdup(file);
+ tmpFile = strreplace(tmpFile, ':', '|');
+--- ../epgsearch-0.9.21-plain//menu_myedittimer.h 2007-02-07 19:55:36.000000000 +0100
++++ menu_myedittimer.h 2007-05-28 09:33:29.000000000 +0200
+@@ -23,6 +23,7 @@
+ int lifetime;
+ char file[MaxFileName];
+ char directory[MaxFileName];
++ int fskProtection; // PIN PATCH
+ int checkmode;
+ public:
+ cMenuMyEditTimer(cTimer *Timer, bool New, const cEvent* event, const cChannel* forcechannel=NULL);
+--- ../epgsearch-0.9.21-plain//menu_whatson.c 2007-03-24 13:20:54.000000000 +0100
++++ menu_whatson.c 2007-05-28 09:34:19.000000000 +0200
+@@ -450,12 +450,12 @@
+ else
+ {
+ string fullaux = "";
++ string aux = "";
+ if (item->event)
+ {
+ const cEvent* event = item->event;
+ int bstart = event->StartTime() - timer->StartTime();
+ int bstop = timer->StopTime() - event->EndTime();
+- string aux = "";
+ int checkmode = DefTimerCheckModes.GetMode(timer->Channel());
+ aux = UpdateAuxValue(aux, "update", checkmode);
+ aux = UpdateAuxValue(aux, "eventid", event->EventID());
+@@ -463,6 +463,12 @@
+ aux = UpdateAuxValue(aux, "bstop", bstop);
+ fullaux = UpdateAuxValue(fullaux, "epgsearch", aux);
+ }
++
++ // #PIN PATCH
++ aux = "";
++ aux = UpdateAuxValue(aux, "protected", timer->FskProtection() ? "yes" : "no");
++ fullaux = UpdateAuxValue(fullaux, "pin-plugin", aux);
++
+ SetAux(timer, fullaux);
+ Timers.Add(timer);
+ timer->Matches();
diff --git a/patches/vdr-1.4.5.diff b/patches/vdr-1.4.5.diff
new file mode 100644
index 0000000..59972e1
--- /dev/null
+++ b/patches/vdr-1.4.5.diff
@@ -0,0 +1,454 @@
+--- ../vdr-1.4.5//device.c 2006-09-03 12:13:25.000000000 +0200
++++ device.c 2007-01-28 19:24:06.000000000 +0100
+@@ -581,8 +581,10 @@
+ int n = CurrentChannel() + Direction;
+ int first = n;
+ cChannel *channel;
+- while ((channel = Channels.GetByNumber(n, Direction)) != NULL) {
++ while ((channel = Channels.GetByNumber(n, Direction)) != NULL)
++ {
+ // try only channels which are currently available
++ if (cStatus::MsgChannelProtected(0, channel) == false) // PIN PATCH
+ if (PrimaryDevice()->ProvidesChannel(channel, Setup.PrimaryLimit) || PrimaryDevice()->CanReplay() && GetDevice(channel, 0))
+ break;
+ n = channel->Number() + Direction;
+@@ -604,6 +606,11 @@
+
+ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
+ {
++ // I hope 'LiveView = false' indicates a channel switch for recording, // PIN PATCH
++ // I really don't know, but it works ... // PIN PATCH
++ if (LiveView && cStatus::MsgChannelProtected(this, Channel) == true) // PIN PATCH
++ return scrNotAvailable; // PIN PATCH
++
+ if (LiveView)
+ StopReplay();
+
+--- ../vdr-1.4.5//i18n.c 2006-10-14 11:26:41.000000000 +0200
++++ i18n.c 2007-01-28 19:24:06.000000000 +0100
+@@ -6126,6 +6126,27 @@
+ "Ingen titel",
+ "Bez názvu",
+ },
++ { "Childlock", // PIN PATCH
++ "Kindersicherung",
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "Adulte",
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ "",//TODO
++ },
+ { NULL }
+ };
+
+--- ../vdr-1.4.5//menu.c 2006-12-02 12:12:02.000000000 +0100
++++ menu.c 2007-02-03 12:16:23.000000000 +0100
+@@ -664,6 +664,16 @@
+ Add(new cMenuEditBitItem( tr("VPS"), &data.flags, tfVps));
+ Add(new cMenuEditIntItem( tr("Priority"), &data.priority, 0, MAXPRIORITY));
+ Add(new cMenuEditIntItem( tr("Lifetime"), &data.lifetime, 0, MAXLIFETIME));
++
++ // PIN PATCH
++ if (cOsd::pinValid || !data.fskProtection) Add(new cMenuEditBoolItem(tr("Childlock"),&data.fskProtection));
++ else {
++ char* buf = 0;
++ asprintf(&buf, "%s\t%s", tr("Childlock"), data.fskProtection ? tr("yes") : tr("no"));
++ Add(new cOsdItem(buf));
++ free(buf);
++ }
++
+ Add(new cMenuEditStrItem( tr("File"), data.file, sizeof(data.file), tr(FileNameChars)));
+ SetFirstDayItem();
+ }
+@@ -1913,7 +1923,9 @@
+ for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) {
+ if (!base || (strstr(recording->Name(), base) == recording->Name() && recording->Name()[strlen(base)] == '~')) {
+ cMenuRecordingItem *Item = new cMenuRecordingItem(recording, level);
+- if (*Item->Text() && (!LastItem || strcmp(Item->Text(), LastItemText) != 0)) {
++ if ((*Item->Text() && (!LastItem || strcmp(Item->Text(), LastItemText) != 0))
++ && (!cStatus::MsgReplayProtected(GetRecording(Item), Item->Name(), base,
++ Item->IsDirectory(), true))) { // PIN PATCH
+ Add(Item);
+ LastItem = Item;
+ free(LastItemText);
+@@ -1963,6 +1975,9 @@
+ {
+ cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current());
+ if (ri) {
++ if (cStatus::MsgReplayProtected(GetRecording(ri), ri->Name(), base,
++ ri->IsDirectory()) == true) // PIN PATCH
++ return osContinue; // PIN PATCH
+ if (ri->IsDirectory())
+ Open();
+ else {
+@@ -2818,28 +2833,32 @@
+
+ // Basic menu items:
+
+- Add(new cOsdItem(hk(tr("Schedule")), osSchedule));
+- Add(new cOsdItem(hk(tr("Channels")), osChannels));
+- Add(new cOsdItem(hk(tr("Timers")), osTimers));
+- Add(new cOsdItem(hk(tr("Recordings")), osRecordings));
++ // PIN PATCH
++ if (!cStatus::MsgMenuItemProtected("Schedule", true)) Add(new cOsdItem(hk(tr("Schedule")), osSchedule));
++ if (!cStatus::MsgMenuItemProtected("Channels", true)) Add(new cOsdItem(hk(tr("Channels")), osChannels));
++ if (!cStatus::MsgMenuItemProtected("Timers", true)) Add(new cOsdItem(hk(tr("Timers")), osTimers));
++ if (!cStatus::MsgMenuItemProtected("Recordings", true)) Add(new cOsdItem(hk(tr("Recordings")), osRecordings));
+
+ // Plugins:
+
+ for (int i = 0; ; i++) {
+ cPlugin *p = cPluginManager::GetPlugin(i);
+ if (p) {
++ if (!cStatus::MsgPluginProtected(p, true)) { // PIN PATCH
+ const char *item = p->MainMenuEntry();
+ if (item)
+ Add(new cMenuPluginItem(hk(item), i));
+ }
++ }
+ else
+ break;
+ }
+
+ // More basic menu items:
+
+- Add(new cOsdItem(hk(tr("Setup")), osSetup));
++ if (!cStatus::MsgMenuItemProtected("Setup", true)) Add(new cOsdItem(hk(tr("Setup")), osSetup)); // PIN PATCH
+ if (Commands.Count())
++ if (!cStatus::MsgMenuItemProtected("Commands", true)) // PIN PATCH
+ Add(new cOsdItem(hk(tr("Commands")), osCommands));
+
+ Update(true);
+@@ -2927,6 +2946,14 @@
+ eOSState state = cOsdMenu::ProcessKey(Key);
+ HadSubMenu |= HasSubMenu();
+
++ // > PIN PATCH
++ cOsdItem* item = Get(Current());
++
++ if (item && item->Text() && state != osContinue && state != osUnknown && state != osBack)
++ if (cStatus::MsgMenuItemProtected(item->Text()))
++ return osContinue;
++ // PIN PATCH <
++
+ switch (state) {
+ case osSchedule: return AddSubMenu(new cMenuSchedule);
+ case osChannels: return AddSubMenu(new cMenuChannels);
+@@ -2952,6 +2979,7 @@
+ if (item) {
+ cPlugin *p = cPluginManager::GetPlugin(item->PluginIndex());
+ if (p) {
++ if (!cStatus::MsgPluginProtected(p)) { // PIN PATCH
+ cOsdObject *menu = p->MainMenuAction();
+ if (menu) {
+ if (menu->IsMenu())
+@@ -2963,6 +2991,7 @@
+ }
+ }
+ }
++ }
+ state = osEnd;
+ }
+ break;
+@@ -3126,6 +3155,7 @@
+ if (Direction) {
+ while (Channel) {
+ Channel = Direction > 0 ? Channels.Next(Channel) : Channels.Prev(Channel);
++ if (cStatus::MsgChannelProtected(0, Channel) == false) // PIN PATCH
+ if (Channel && !Channel->GroupSep() && (cDevice::PrimaryDevice()->ProvidesChannel(Channel, Setup.PrimaryLimit) || cDevice::GetDevice(Channel, 0)))
+ return Channel;
+ }
+@@ -3663,6 +3693,7 @@
+ for (int i = 0; i < MAXRECORDCONTROLS; i++) {
+ if (!RecordControls[i]) {
+ RecordControls[i] = new cRecordControl(device, Timer, Pause);
++ cStatus::MsgRecordingFile(RecordControls[i]->FileName()); // PIN PATCH
+ return RecordControls[i]->Process(time(NULL));
+ }
+ }
+--- ../vdr-1.4.5//osd.c 2006-02-26 15:31:31.000000000 +0100
++++ osd.c 2007-01-28 19:24:06.000000000 +0100
+@@ -594,6 +594,7 @@
+ // --- cOsd ------------------------------------------------------------------
+
+ int cOsd::isOpen = 0;
++bool cOsd::pinValid = false; // PIN PATCH
+
+ cOsd::cOsd(int Left, int Top)
+ {
+--- ../vdr-1.4.5//osd.h 2006-02-26 15:45:05.000000000 +0100
++++ osd.h 2007-01-28 19:24:06.000000000 +0100
+@@ -324,6 +324,7 @@
+ ///< 7: vertical, falling, upper
+ virtual void Flush(void);
+ ///< Actually commits all data to the OSD hardware.
++ static bool pinValid; // PIN PATCH
+ };
+
+ class cOsdProvider {
+--- ../vdr-1.4.5//status.c 2005-12-31 16:10:10.000000000 +0100
++++ status.c 2007-02-03 12:04:26.000000000 +0100
+@@ -112,3 +112,55 @@
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+ sm->OsdProgramme(PresentTime, PresentTitle, PresentSubtitle, FollowingTime, FollowingTitle, FollowingSubtitle);
+ }
++
++bool cStatus::MsgChannelProtected(const cDevice* Device, const cChannel* Channel) // PIN PATCH
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ if (sm->ChannelProtected(Device, Channel) == true)
++ return true;
++
++ return false;
++}
++
++bool cStatus::MsgReplayProtected(const cRecording* Recording, const char* Name,
++ const char* Base, bool isDirectory, int menuView) // PIN PATCH
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ if (sm->ReplayProtected(Recording, Name, Base, isDirectory, menuView) == true)
++ return true;
++ return false;
++}
++
++void cStatus::MsgRecordingFile(const char* FileName)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm)) // PIN PATCH
++ sm->RecordingFile(FileName);
++}
++
++void cStatus::MsgTimerCreation(cTimer* Timer, const cEvent *Event)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm)) // PIN PATCH
++ sm->TimerCreation(Timer, Event);
++}
++
++bool cStatus::MsgPluginProtected(cPlugin* Plugin, int menuView) // PIN PATCH
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ if (sm->PluginProtected(Plugin, menuView) == true)
++ return true;
++ return false;
++}
++
++void cStatus::MsgUserAction(const eKeys key, const cOsdObject* Interact) // PIN PATCH
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ sm->UserAction(key, Interact);
++}
++
++bool cStatus::MsgMenuItemProtected(const char* Name, int menuView) // PIN PATCH
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ if (sm->MenuItemProtected(Name, menuView) == true)
++ return true;
++ return false;
++}
+--- ../vdr-1.4.5//status.h 2005-12-31 16:15:25.000000000 +0100
++++ status.h 2007-02-03 12:03:19.000000000 +0100
+@@ -14,6 +14,7 @@
+ #include "device.h"
+ #include "player.h"
+ #include "tools.h"
++#include "plugin.h"
+
+ class cStatus : public cListObject {
+ private:
+@@ -67,6 +68,24 @@
+ // The OSD displays the single line Text with the current channel information.
+ virtual void OsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle) {}
+ // The OSD displays the given programme information.
++ virtual bool ChannelProtected(const cDevice *Device, const cChannel* Channel) { return false; } // PIN PATCH
++ // Checks if a channel is protected.
++ virtual bool ReplayProtected(const cRecording* Recording, const char* Name,
++ const char* Base, bool isDirectory, int menuView = false) { return false; } // PIN PATCH
++ // Checks if a recording is protected.
++ virtual void RecordingFile(const char* FileName) {} // PIN PATCH
++ // The given DVB device has started recording to FileName. FileName is the name of the
++ // recording directory
++ virtual void TimerCreation(cTimer* Timer, const cEvent *Event) {} // PIN PATCH
++ // The given timer is created
++ virtual bool PluginProtected(cPlugin* Plugin, int menuView = false) { return false; } // PIN PATCH
++ // Checks if a plugin is protected.
++ virtual void UserAction(const eKeys key, const cOsdObject* Interact) {} // PIN PATCH
++ // report user action
++ virtual bool MenuItemProtected(const char* Name, int menuView = false) { return false; } // PIN PATCH
++ // Checks if a menu entry is protected.
++
++
+ public:
+ cStatus(void);
+ virtual ~cStatus();
+@@ -86,6 +105,15 @@
+ static void MsgOsdTextItem(const char *Text, bool Scroll = false);
+ static void MsgOsdChannel(const char *Text);
+ static void MsgOsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle);
++ static bool MsgChannelProtected(const cDevice* Device, const cChannel* Channel); // PIN PATCH
++ static bool MsgReplayProtected(const cRecording* Recording, const char* Name,
++ const char* Base, bool isDirectory, int menuView = false); // PIN PATCH
++ static void MsgRecordingFile(const char* FileName); // PIN PATCH
++ static void MsgTimerCreation(cTimer* Timer, const cEvent *Event); // PIN PATCH
++ static bool MsgPluginProtected(cPlugin* Plugin, int menuView = false); // PIN PATCH
++ static void MsgUserAction(const eKeys key, const cOsdObject* Interact);
++ static bool MsgMenuItemProtected(const char* Name, int menuView = false); // PIN PATCH
++
+ };
+
+ #endif //__STATUS_H
+--- ../vdr-1.4.5//timers.c 2006-09-15 16:15:53.000000000 +0200
++++ timers.c 2007-01-28 19:24:06.000000000 +0100
+@@ -14,6 +14,7 @@
+ #include "i18n.h"
+ #include "libsi/si.h"
+ #include "remote.h"
++#include "status.h" // PIN PATCH
+
+ // IMPORTANT NOTE: in the 'sscanf()' calls there is a blank after the '%d'
+ // format characters in order to allow any number of blanks after a numeric
+@@ -42,6 +43,7 @@
+ stop -= 2400;
+ priority = Pause ? Setup.PausePriority : Setup.DefaultPriority;
+ lifetime = Pause ? Setup.PauseLifetime : Setup.DefaultLifetime;
++ fskProtection = 0; // PIN PATCH
+ *file = 0;
+ aux = NULL;
+ event = NULL;
+@@ -75,12 +77,14 @@
+ stop -= 2400;
+ priority = Setup.DefaultPriority;
+ lifetime = Setup.DefaultLifetime;
++ fskProtection = 0; // PIN PATCH
+ *file = 0;
+ const char *Title = Event->Title();
+ if (!isempty(Title))
+ strn0cpy(file, Event->Title(), sizeof(file));
+ aux = NULL;
+ event = NULL; // let SetEvent() be called to get a log message
++ cStatus::MsgTimerCreation(this, Event); // PIN PATCH
+ }
+
+ cTimer::cTimer(const cTimer &Timer)
+@@ -113,6 +117,7 @@
+ stop = Timer.stop;
+ priority = Timer.priority;
+ lifetime = Timer.lifetime;
++ fskProtection = Timer.fskProtection; // PIN PATCH
+ strncpy(file, Timer.file, sizeof(file));
+ free(aux);
+ aux = Timer.aux ? strdup(Timer.aux) : NULL;
+@@ -288,6 +293,7 @@
+ result = false;
+ }
+ }
++ fskProtection = aux && strstr(aux, "<pin-plugin><protected>yes</protected></pin-plugin>"); // PIN PATCH
+ free(channelbuffer);
+ free(daybuffer);
+ free(filebuffer);
+@@ -597,6 +603,33 @@
+ Matches(); // refresh start and end time
+ }
+
++void cTimer::SetFskProtection(int aFlag) // PIN PATCH
++{
++ char* p;
++ char* tmp = 0;
++
++ fskProtection = aFlag;
++
++ if (fskProtection && (!aux || !strstr(aux, "<pin-plugin><protected>yes</protected></pin-plugin>")))
++ {
++ // add protection info to aux
++
++ if (aux) { tmp = strdup(aux); free(aux); }
++ asprintf(&aux,"%s<pin-plugin><protected>yes</protected></pin-plugin>", tmp ? tmp : "");
++ }
++ else if (!fskProtection && aux && (p = strstr(aux, "<pin-plugin><protected>yes</protected></pin-plugin>")))
++ {
++ // remove protection info to aux
++
++ asprintf(&tmp, "%.*s%s", p-aux, aux, p+strlen("<pin-plugin><protected>yes</protected></pin-plugin>"));
++ free(aux);
++ aux = strdup(tmp);
++ }
++
++ if (tmp)
++ free(tmp);
++}
++
+ // -- cTimers ----------------------------------------------------------------
+
+ cTimers Timers;
+--- ../vdr-1.4.5//timers.h 2006-09-04 19:07:39.000000000 +0200
++++ timers.h 2007-01-28 19:24:06.000000000 +0100
+@@ -37,6 +37,7 @@
+ int start;
+ int stop;
+ int priority;
++ int fskProtection; // PIN PATCH
+ int lifetime;
+ char file[MaxFileName];
+ char *aux;
+@@ -58,6 +59,7 @@
+ int Start(void) const { return start; }
+ int Stop(void) const { return stop; }
+ int Priority(void) const { return priority; }
++ int FskProtection(void) const { return fskProtection; } // PIN PATCH
+ int Lifetime(void) const { return lifetime; }
+ const char *File(void) const { return file; }
+ time_t FirstDay(void) const { return weekdays ? day : 0; }
+@@ -86,6 +88,7 @@
+ void SetInVpsMargin(bool InVpsMargin);
+ void SetPriority(int Priority);
+ void SetFlags(uint Flags);
++ void SetFskProtection(int aFlag); // PIN PATCH
+ void ClrFlags(uint Flags);
+ void InvFlags(uint Flags);
+ bool HasFlags(uint Flags) const;
+--- ../vdr-1.4.5//vdr.c 2006-12-02 17:22:12.000000000 +0100
++++ vdr.c 2007-01-28 19:32:00.000000000 +0100
+@@ -865,6 +865,7 @@
+ cOsdObject *Interact = Menu ? Menu : cControl::Control();
+ eKeys key = Interface->GetKey((!Interact || !Interact->NeedsFastResponse()) && time(NULL) - LastCamMenu > LASTCAMMENUTIMEOUT);
+ if (NORMALKEY(key) != kNone) {
++ cStatus::MsgUserAction(key, Interact); // PIN PATCH
+ EITScanner.Activity();
+ LastActivity = time(NULL);
+ }
+@@ -930,10 +931,12 @@
+ cControl::Control()->Hide();
+ cPlugin *plugin = cPluginManager::GetPlugin(PluginName);
+ if (plugin) {
++ if (!cStatus::MsgPluginProtected(plugin)) {
+ Menu = plugin->MainMenuAction();
+ if (Menu)
+ Menu->Show();
+ }
++ }
+ else
+ esyslog("ERROR: unknown plugin '%s'", PluginName);
+ }
+@@ -1137,9 +1140,11 @@
+ // Instant resume of the last viewed recording:
+ case kPlay:
+ if (cReplayControl::LastReplayed()) {
++ if (cStatus::MsgReplayProtected(0, cReplayControl::LastReplayed(), 0, false) == false) { // PIN PATCH
+ cControl::Shutdown();
+ cControl::Launch(new cReplayControl);
+ }
++ }
+ break;
+ default: break;
+ }
diff --git a/patches/vdr-1.7.17.diff b/patches/vdr-1.7.17.diff
new file mode 100644
index 0000000..5909652
--- /dev/null
+++ b/patches/vdr-1.7.17.diff
@@ -0,0 +1,416 @@
+--- ../vdr-1.7.17.plain//device.c 2011-02-25 16:12:03.000000000 +0100
++++ device.c 2011-03-31 11:47:58.122037275 +0200
+@@ -665,6 +665,7 @@
+ cChannel *channel;
+ while ((channel = Channels.GetByNumber(n, Direction)) != NULL) {
+ // try only channels which are currently available
++ if (!cStatus::MsgChannelProtected(0, channel)) // PIN PATCH
+ if (GetDevice(channel, 0, true))
+ break;
+ n = channel->Number() + Direction;
+@@ -686,6 +687,12 @@
+
+ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
+ {
++ // I hope 'LiveView = false' indicates a channel switch for recording, // PIN PATCH
++ // I really don't know, but it works ... // PIN PATCH
++
++ if (LiveView && cStatus::MsgChannelProtected(this, Channel)) // PIN PATCH
++ return scrNotAvailable; // PIN PATCH
++
+ if (LiveView) {
+ StopReplay();
+ DELETENULL(liveSubtitle);
+--- ../vdr-1.7.17.plain//menu.c 2011-02-27 13:37:48.000000000 +0100
++++ menu.c 2011-03-31 12:17:34.903691287 +0200
+@@ -918,6 +918,16 @@
+ Add(new cMenuEditBitItem( tr("VPS"), &data.flags, tfVps));
+ Add(new cMenuEditIntItem( tr("Priority"), &data.priority, 0, MAXPRIORITY));
+ Add(new cMenuEditIntItem( tr("Lifetime"), &data.lifetime, 0, MAXLIFETIME));
++
++ // PIN PATCH
++ if (cOsd::pinValid || !data.fskProtection) Add(new cMenuEditBoolItem(tr("Childlock"),&data.fskProtection));
++ else {
++ char* buf = 0;
++ asprintf(&buf, "%s\t%s", tr("Childlock"), data.fskProtection ? tr("yes") : tr("no"));
++ Add(new cOsdItem(buf));
++ free(buf);
++ }
++
+ Add(file = new cMenuEditStrItem( tr("File"), data.file, sizeof(data.file)));
+ SetFirstDayItem();
+ }
+@@ -2276,7 +2286,10 @@
+ for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) {
+ if (!base || (strstr(recording->Name(), base) == recording->Name() && recording->Name()[strlen(base)] == FOLDERDELIMCHAR)) {
+ cMenuRecordingItem *Item = new cMenuRecordingItem(recording, level);
+- if (*Item->Text() && (!Item->IsDirectory() || (!LastItem || !LastItem->IsDirectory() || strcmp(Item->Text(), LastItemText) != 0))) {
++
++ if (*Item->Text() && (!Item->IsDirectory() || (!LastItem || !LastItem->IsDirectory() || strcmp(Item->Text(), LastItemText) != 0))
++ && (!cStatus::MsgReplayProtected(GetRecording(Item), Item->Name(), base,
++ Item->IsDirectory(), true))) { // PIN PATCH
+ Add(Item);
+ LastItem = Item;
+ free(LastItemText);
+@@ -2326,6 +2339,9 @@
+ {
+ cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current());
+ if (ri) {
++ if (cStatus::MsgReplayProtected(GetRecording(ri), ri->Name(), base,
++ ri->IsDirectory()) == true) // PIN PATCH
++ return osContinue; // PIN PATCH
+ if (ri->IsDirectory())
+ Open();
+ else {
+@@ -3305,28 +3321,32 @@
+
+ // Basic menu items:
+
+- Add(new cOsdItem(hk(tr("Schedule")), osSchedule));
+- Add(new cOsdItem(hk(tr("Channels")), osChannels));
+- Add(new cOsdItem(hk(tr("Timers")), osTimers));
+- Add(new cOsdItem(hk(tr("Recordings")), osRecordings));
++ // PIN PATCH
++ if (!cStatus::MsgMenuItemProtected("Schedule", true)) Add(new cOsdItem(hk(tr("Schedule")), osSchedule));
++ if (!cStatus::MsgMenuItemProtected("Channels", true)) Add(new cOsdItem(hk(tr("Channels")), osChannels));
++ if (!cStatus::MsgMenuItemProtected("Timers", true)) Add(new cOsdItem(hk(tr("Timers")), osTimers));
++ if (!cStatus::MsgMenuItemProtected("Recordings", true)) Add(new cOsdItem(hk(tr("Recordings")), osRecordings));
+
+ // Plugins:
+
+ for (int i = 0; ; i++) {
+ cPlugin *p = cPluginManager::GetPlugin(i);
+ if (p) {
++ if (!cStatus::MsgPluginProtected(p, true)) { // PIN PATCH
+ const char *item = p->MainMenuEntry();
+ if (item)
+ Add(new cMenuPluginItem(hk(item), i));
+ }
++ }
+ else
+ break;
+ }
+
+ // More basic menu items:
+
+- Add(new cOsdItem(hk(tr("Setup")), osSetup));
++ if (!cStatus::MsgMenuItemProtected("Setup", true)) Add(new cOsdItem(hk(tr("Setup")), osSetup)); // PIN PATCH
+ if (Commands.Count())
++ if (!cStatus::MsgMenuItemProtected("Commands", true)) // PIN PATCH
+ Add(new cOsdItem(hk(tr("Commands")), osCommands));
+
+ Update(true);
+@@ -3402,6 +3422,14 @@
+ eOSState state = cOsdMenu::ProcessKey(Key);
+ HadSubMenu |= HasSubMenu();
+
++ // > PIN PATCH
++ cOsdItem* item = Get(Current());
++
++ if (item && item->Text() && state != osContinue && state != osUnknown && state != osBack)
++ if (cStatus::MsgMenuItemProtected(item->Text()))
++ return osContinue;
++ // PIN PATCH <
++
+ switch (state) {
+ case osSchedule: return AddSubMenu(new cMenuSchedule);
+ case osChannels: return AddSubMenu(new cMenuChannels);
+@@ -3427,6 +3455,7 @@
+ if (item) {
+ cPlugin *p = cPluginManager::GetPlugin(item->PluginIndex());
+ if (p) {
++ if (!cStatus::MsgPluginProtected(p)) { // PIN PATCH
+ cOsdObject *menu = p->MainMenuAction();
+ if (menu) {
+ if (menu->IsMenu())
+@@ -3438,6 +3467,7 @@
+ }
+ }
+ }
++ }
+ state = osEnd;
+ }
+ break;
+@@ -3608,6 +3638,8 @@
+ Channel = Direction > 0 ? Channels.Next(Channel) : Channels.Prev(Channel);
+ if (!Channel && Setup.ChannelsWrap)
+ Channel = Direction > 0 ? Channels.First() : Channels.Last();
++
++ if (!cStatus::MsgChannelProtected(0, Channel)) // PIN PATCH
+ if (Channel && !Channel->GroupSep() && cDevice::GetDevice(Channel, 0, true))
+ return Channel;
+ }
+@@ -4241,6 +4273,7 @@
+ for (int i = 0; i < MAXRECORDCONTROLS; i++) {
+ if (!RecordControls[i]) {
+ RecordControls[i] = new cRecordControl(device, Timer, Pause);
++ cStatus::MsgRecordingFile(RecordControls[i]->FileName()); // PIN PATCH
+ return RecordControls[i]->Process(time(NULL));
+ }
+ }
+--- ../vdr-1.7.17.plain//osd.c 2011-03-12 16:32:33.000000000 +0100
++++ osd.c 2011-03-31 11:57:37.102037290 +0200
+@@ -1576,6 +1576,7 @@
+ int cOsd::osdWidth = 0;
+ int cOsd::osdHeight = 0;
+ cVector<cOsd *> cOsd::Osds;
++bool cOsd::pinValid = false; // PIN PATCH
+
+ cOsd::cOsd(int Left, int Top, uint Level)
+ {
+--- ../vdr-1.7.17.plain//osd.h 2011-03-12 17:06:48.000000000 +0100
++++ osd.h 2011-03-31 11:58:26.802037290 +0200
+@@ -917,6 +917,7 @@
+ ///< MyOsdDrawPixmap(Left() + pm->ViewPort().X(), Top() + pm->ViewPort().Y(), pm->Data(), w, h, h * d);
+ ///< delete pm;
+ ///< }
++ static bool pinValid; // PIN PATCH
+ };
+
+ #define MAXOSDIMAGES 64
+--- ../vdr-1.7.17.plain//status.c 2008-02-16 15:46:31.000000000 +0100
++++ status.c 2011-03-31 11:40:45.532037296 +0200
+@@ -124,3 +124,55 @@
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+ sm->OsdProgramme(PresentTime, PresentTitle, PresentSubtitle, FollowingTime, FollowingTitle, FollowingSubtitle);
+ }
++
++bool cStatus::MsgChannelProtected(const cDevice* Device, const cChannel* Channel) // PIN PATCH
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ if (sm->ChannelProtected(Device, Channel) == true)
++ return true;
++
++ return false;
++}
++
++bool cStatus::MsgReplayProtected(const cRecording* Recording, const char* Name,
++ const char* Base, bool isDirectory, int menuView) // PIN PATCH
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ if (sm->ReplayProtected(Recording, Name, Base, isDirectory, menuView) == true)
++ return true;
++ return false;
++}
++
++void cStatus::MsgRecordingFile(const char* FileName)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm)) // PIN PATCH
++ sm->RecordingFile(FileName);
++}
++
++void cStatus::MsgTimerCreation(cTimer* Timer, const cEvent *Event)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm)) // PIN PATCH
++ sm->TimerCreation(Timer, Event);
++}
++
++bool cStatus::MsgPluginProtected(cPlugin* Plugin, int menuView) // PIN PATCH
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ if (sm->PluginProtected(Plugin, menuView) == true)
++ return true;
++ return false;
++}
++
++void cStatus::MsgUserAction(const eKeys key, const cOsdObject* Interact) // PIN PATCH
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ sm->UserAction(key, Interact);
++}
++
++bool cStatus::MsgMenuItemProtected(const char* Name, int menuView) // PIN PATCH
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ if (sm->MenuItemProtected(Name, menuView) == true)
++ return true;
++ return false;
++}
+--- ../vdr-1.7.17.plain//status.h 2008-02-16 16:00:33.000000000 +0100
++++ status.h 2011-03-31 11:40:45.532037296 +0200
+@@ -14,6 +14,7 @@
+ #include "device.h"
+ #include "player.h"
+ #include "tools.h"
++#include "plugin.h"
+
+ enum eTimerChange { tcMod, tcAdd, tcDel };
+
+@@ -80,6 +81,24 @@
+ // The OSD displays the single line Text with the current channel information.
+ virtual void OsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle) {}
+ // The OSD displays the given programme information.
++ virtual bool ChannelProtected(const cDevice *Device, const cChannel* Channel) { return false; } // PIN PATCH
++ // Checks if a channel is protected.
++ virtual bool ReplayProtected(const cRecording* Recording, const char* Name,
++ const char* Base, bool isDirectory, int menuView = false) { return false; } // PIN PATCH
++ // Checks if a recording is protected.
++ virtual void RecordingFile(const char* FileName) {} // PIN PATCH
++ // The given DVB device has started recording to FileName. FileName is the name of the
++ // recording directory
++ virtual void TimerCreation(cTimer* Timer, const cEvent *Event) {} // PIN PATCH
++ // The given timer is created
++ virtual bool PluginProtected(cPlugin* Plugin, int menuView = false) { return false; } // PIN PATCH
++ // Checks if a plugin is protected.
++ virtual void UserAction(const eKeys key, const cOsdObject* Interact) {} // PIN PATCH
++ // report user action
++ virtual bool MenuItemProtected(const char* Name, int menuView = false) { return false; } // PIN PATCH
++ // Checks if a menu entry is protected.
++
++
+ public:
+ cStatus(void);
+ virtual ~cStatus();
+@@ -101,6 +120,15 @@
+ static void MsgOsdTextItem(const char *Text, bool Scroll = false);
+ static void MsgOsdChannel(const char *Text);
+ static void MsgOsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle);
++ static bool MsgChannelProtected(const cDevice* Device, const cChannel* Channel); // PIN PATCH
++ static bool MsgReplayProtected(const cRecording* Recording, const char* Name,
++ const char* Base, bool isDirectory, int menuView = false); // PIN PATCH
++ static void MsgRecordingFile(const char* FileName); // PIN PATCH
++ static void MsgTimerCreation(cTimer* Timer, const cEvent *Event); // PIN PATCH
++ static bool MsgPluginProtected(cPlugin* Plugin, int menuView = false); // PIN PATCH
++ static void MsgUserAction(const eKeys key, const cOsdObject* Interact);
++ static bool MsgMenuItemProtected(const char* Name, int menuView = false); // PIN PATCH
++
+ };
+
+ #endif //__STATUS_H
+--- ../vdr-1.7.17.plain//timers.c 2010-01-16 12:18:53.000000000 +0100
++++ timers.c 2011-03-31 12:07:34.363691286 +0200
+@@ -46,6 +46,7 @@
+ stop -= 2400;
+ priority = Pause ? Setup.PausePriority : Setup.DefaultPriority;
+ lifetime = Pause ? Setup.PauseLifetime : Setup.DefaultLifetime;
++ fskProtection = 0; // PIN PATCH
+ *file = 0;
+ aux = NULL;
+ event = NULL;
+@@ -84,6 +85,7 @@
+ stop -= 2400;
+ priority = Setup.DefaultPriority;
+ lifetime = Setup.DefaultLifetime;
++ fskProtection = 0; // PIN PATCH
+ *file = 0;
+ const char *Title = Event->Title();
+ if (!isempty(Title))
+@@ -95,6 +97,7 @@
+ }
+ aux = NULL;
+ event = NULL; // let SetEvent() be called to get a log message
++ cStatus::MsgTimerCreation(this, Event);
+ }
+
+ cTimer::cTimer(const cTimer &Timer)
+@@ -129,6 +132,7 @@
+ stop = Timer.stop;
+ priority = Timer.priority;
+ lifetime = Timer.lifetime;
++ fskProtection = Timer.fskProtection; // PIN PATCH
+ strncpy(file, Timer.file, sizeof(file));
+ free(aux);
+ aux = Timer.aux ? strdup(Timer.aux) : NULL;
+@@ -323,6 +327,7 @@
+ result = false;
+ }
+ }
++ fskProtection = aux && strstr(aux, "<pin-plugin><protected>yes</protected></pin-plugin>"); // PIN PATCH
+ free(channelbuffer);
+ free(daybuffer);
+ free(filebuffer);
+@@ -632,6 +637,33 @@
+ Matches(); // refresh start and end time
+ }
+
++void cTimer::SetFskProtection(int aFlag)
++{
++ char* p;
++ char* tmp = 0;
++
++ fskProtection = aFlag;
++
++ if (fskProtection && (!aux || !strstr(aux, "<pin-plugin><protected>yes</protected></pin-plugin>")))
++ {
++ // add protection info to aux
++
++ if (aux) { tmp = strdup(aux); free(aux); }
++ asprintf(&aux,"%s<pin-plugin><protected>yes</protected></pin-plugin>", tmp ? tmp : "");
++ }
++ else if (!fskProtection && aux && (p = strstr(aux, "<pin-plugin><protected>yes</protected></pin-plugin>")))
++ {
++ // remove protection info to aux
++
++ asprintf(&tmp, "%.*s%s", p-aux, aux, p+strlen("<pin-plugin><protected>yes</protected></pin-plugin>"));
++ free(aux);
++ aux = strdup(tmp);
++ }
++
++ if (tmp)
++ free(tmp);
++}
++
+ // --- cTimers ---------------------------------------------------------------
+
+ cTimers Timers;
+--- ../vdr-1.7.17.plain//timers.h 2008-02-16 15:33:23.000000000 +0100
++++ timers.h 2011-03-31 11:40:45.532037296 +0200
+@@ -37,6 +37,7 @@
+ int start;
+ int stop;
+ int priority;
++ int fskProtection; // PIN PATCH
+ int lifetime;
+ mutable char file[MaxFileName];
+ char *aux;
+@@ -58,6 +59,7 @@
+ int Start(void) const { return start; }
+ int Stop(void) const { return stop; }
+ int Priority(void) const { return priority; }
++ int FskProtection(void) const { return fskProtection; } // PIN PATCH
+ int Lifetime(void) const { return lifetime; }
+ const char *File(void) const { return file; }
+ time_t FirstDay(void) const { return weekdays ? day : 0; }
+@@ -86,6 +88,7 @@
+ void SetInVpsMargin(bool InVpsMargin);
+ void SetPriority(int Priority);
+ void SetFlags(uint Flags);
++ void SetFskProtection(int aFlag); // PIN PATCH
+ void ClrFlags(uint Flags);
+ void InvFlags(uint Flags);
+ bool HasFlags(uint Flags) const;
+--- ../vdr-1.7.17.plain//vdr.c 2010-12-12 14:42:00.000000000 +0100
++++ vdr.c 2011-03-31 12:09:31.913691288 +0200
+@@ -929,6 +929,7 @@
+ cOsdObject *Interact = Menu ? Menu : cControl::Control();
+ eKeys key = Interface->GetKey(!Interact || !Interact->NeedsFastResponse());
+ if (ISREALKEY(key)) {
++ cStatus::MsgUserAction(key, Interact); // PIN PATCH
+ EITScanner.Activity();
+ // Cancel shutdown countdown:
+ if (ShutdownHandler.countdown)
+@@ -1001,10 +1002,12 @@
+ cControl::Control()->Hide();
+ cPlugin *plugin = cPluginManager::GetPlugin(PluginName);
+ if (plugin) {
++ if (!cStatus::MsgPluginProtected(plugin)) {
+ Menu = plugin->MainMenuAction();
+ if (Menu)
+ Menu->Show();
+ }
++ }
+ else
+ esyslog("ERROR: unknown plugin '%s'", PluginName);
+ }
+@@ -1218,9 +1221,11 @@
+ // Instant resume of the last viewed recording:
+ case kPlay:
+ if (cReplayControl::LastReplayed()) {
++ if (cStatus::MsgReplayProtected(0, cReplayControl::LastReplayed(), 0, false) == false) { // PIN PATCH
+ cControl::Shutdown();
+ cControl::Launch(new cReplayControl);
+ }
++ }
+ break;
+ default: break;
+ }
diff --git a/patches/vdr-1.7.32.diff b/patches/vdr-1.7.32.diff
new file mode 100644
index 0000000..35d4219
--- /dev/null
+++ b/patches/vdr-1.7.32.diff
@@ -0,0 +1,426 @@
+--- ../vdr-1.7.32.plain//device.c 2012-11-13 10:11:43.000000000 +0100
++++ device.c 2012-12-07 11:15:14.952979954 +0100
+@@ -721,6 +721,7 @@
+ cChannel *channel;
+ while ((channel = Channels.GetByNumber(n, Direction)) != NULL) {
+ // try only channels which are currently available
++ if (!cStatus::MsgChannelProtected(0, channel)) // PIN PATCH
+ if (GetDevice(channel, LIVEPRIORITY, true, true))
+ break;
+ n = channel->Number() + Direction;
+@@ -742,6 +743,12 @@
+
+ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
+ {
++ // I hope 'LiveView = false' indicates a channel switch for recording, // PIN PATCH
++ // I really don't know, but it works ... // PIN PATCH
++
++ if (LiveView && cStatus::MsgChannelProtected(this, Channel)) // PIN PATCH
++ return scrNotAvailable; // PIN PATCH
++
+ cStatus::MsgChannelSwitch(this, 0, LiveView);
+
+ if (LiveView) {
+--- ../vdr-1.7.32.plain//menu.c 2012-11-18 14:07:53.000000000 +0100
++++ menu.c 2012-12-07 10:42:08.664909711 +0100
+@@ -889,6 +889,16 @@
+ Add(new cMenuEditBitItem( tr("VPS"), &data.flags, tfVps));
+ Add(new cMenuEditIntItem( tr("Priority"), &data.priority, 0, MAXPRIORITY));
+ Add(new cMenuEditIntItem( tr("Lifetime"), &data.lifetime, 0, MAXLIFETIME));
++
++ // PIN PATCH
++ if (cOsd::pinValid || !data.fskProtection) Add(new cMenuEditBoolItem(tr("Childlock"),&data.fskProtection));
++ else {
++ char* buf = 0;
++ asprintf(&buf, "%s\t%s", tr("Childlock"), data.fskProtection ? tr("yes") : tr("no"));
++ Add(new cOsdItem(buf));
++ free(buf);
++ }
++
+ Add(file = new cMenuEditStrItem( tr("File"), data.file, sizeof(data.file)));
+ SetFirstDayItem();
+ }
+@@ -2261,7 +2271,10 @@
+ for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) {
+ if (!base || (strstr(recording->Name(), base) == recording->Name() && recording->Name()[strlen(base)] == FOLDERDELIMCHAR)) {
+ cMenuRecordingItem *Item = new cMenuRecordingItem(recording, level);
+- if (*Item->Text() && (!Item->IsDirectory() || (!LastItem || !LastItem->IsDirectory() || strcmp(Item->Text(), LastItemText) != 0))) {
++
++ if (*Item->Text() && (!Item->IsDirectory() || (!LastItem || !LastItem->IsDirectory() || strcmp(Item->Text(), LastItemText) != 0))
++ && (!cStatus::MsgReplayProtected(GetRecording(Item), Item->Name(), base,
++ Item->IsDirectory(), true))) { // PIN PATCH
+ Add(Item);
+ LastItem = Item;
+ free(LastItemText);
+@@ -2321,6 +2334,9 @@
+ {
+ cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current());
+ if (ri) {
++ if (cStatus::MsgReplayProtected(GetRecording(ri), ri->Name(), base,
++ ri->IsDirectory()) == true) // PIN PATCH
++ return osContinue; // PIN PATCH
+ if (ri->IsDirectory())
+ Open();
+ else {
+@@ -3373,28 +3389,32 @@
+
+ // Basic menu items:
+
+- Add(new cOsdItem(hk(tr("Schedule")), osSchedule));
+- Add(new cOsdItem(hk(tr("Channels")), osChannels));
+- Add(new cOsdItem(hk(tr("Timers")), osTimers));
+- Add(new cOsdItem(hk(tr("Recordings")), osRecordings));
++ // PIN PATCH
++ if (!cStatus::MsgMenuItemProtected("Schedule", true)) Add(new cOsdItem(hk(tr("Schedule")), osSchedule));
++ if (!cStatus::MsgMenuItemProtected("Channels", true)) Add(new cOsdItem(hk(tr("Channels")), osChannels));
++ if (!cStatus::MsgMenuItemProtected("Timers", true)) Add(new cOsdItem(hk(tr("Timers")), osTimers));
++ if (!cStatus::MsgMenuItemProtected("Recordings", true)) Add(new cOsdItem(hk(tr("Recordings")), osRecordings));
+
+ // Plugins:
+
+ for (int i = 0; ; i++) {
+ cPlugin *p = cPluginManager::GetPlugin(i);
+ if (p) {
++ if (!cStatus::MsgPluginProtected(p, true)) { // PIN PATCH
+ const char *item = p->MainMenuEntry();
+ if (item)
+ Add(new cMenuPluginItem(hk(item), i));
+ }
++ }
+ else
+ break;
+ }
+
+ // More basic menu items:
+
+- Add(new cOsdItem(hk(tr("Setup")), osSetup));
++ if (!cStatus::MsgMenuItemProtected("Setup", true)) Add(new cOsdItem(hk(tr("Setup")), osSetup)); // PIN PATCH
+ if (Commands.Count())
++ if (!cStatus::MsgMenuItemProtected("Commands", true)) // PIN PATCH
+ Add(new cOsdItem(hk(tr("Commands")), osCommands));
+
+ Update(true);
+@@ -3463,6 +3483,14 @@
+ eOSState state = cOsdMenu::ProcessKey(Key);
+ HadSubMenu |= HasSubMenu();
+
++ // > PIN PATCH
++ cOsdItem* item = Get(Current());
++
++ if (item && item->Text() && state != osContinue && state != osUnknown && state != osBack)
++ if (cStatus::MsgMenuItemProtected(item->Text()))
++ return osContinue;
++ // PIN PATCH <
++
+ switch (state) {
+ case osSchedule: return AddSubMenu(new cMenuSchedule);
+ case osChannels: return AddSubMenu(new cMenuChannels);
+@@ -3488,6 +3516,7 @@
+ if (item) {
+ cPlugin *p = cPluginManager::GetPlugin(item->PluginIndex());
+ if (p) {
++ if (!cStatus::MsgPluginProtected(p)) { // PIN PATCH
+ cOsdObject *menu = p->MainMenuAction();
+ if (menu) {
+ if (menu->IsMenu())
+@@ -3499,6 +3528,7 @@
+ }
+ }
+ }
++ }
+ state = osEnd;
+ }
+ break;
+@@ -3669,6 +3699,7 @@
+ Channel = Direction > 0 ? Channels.Next(Channel) : Channels.Prev(Channel);
+ if (!Channel && Setup.ChannelsWrap)
+ Channel = Direction > 0 ? Channels.First() : Channels.Last();
++ if (!cStatus::MsgChannelProtected(0, Channel)) // PIN PATCH
+ if (Channel && !Channel->GroupSep() && cDevice::GetDevice(Channel, LIVEPRIORITY, true, true))
+ return Channel;
+ }
+@@ -4318,6 +4349,7 @@
+ for (int i = 0; i < MAXRECORDCONTROLS; i++) {
+ if (!RecordControls[i]) {
+ RecordControls[i] = new cRecordControl(device, Timer, Pause);
++ cStatus::MsgRecordingFile(RecordControls[i]->FileName()); // PIN PATCH
+ return RecordControls[i]->Process(time(NULL));
+ }
+ }
+--- ../vdr-1.7.32.plain//osd.c 2012-06-09 16:37:24.000000000 +0200
++++ osd.c 2012-12-07 10:44:54.184915564 +0100
+@@ -1623,6 +1623,7 @@
+ int cOsd::osdHeight = 0;
+ cVector<cOsd *> cOsd::Osds;
+ cMutex cOsd::mutex;
++bool cOsd::pinValid = false; // PIN PATCH
+
+ cOsd::cOsd(int Left, int Top, uint Level)
+ {
+--- ../vdr-1.7.32.plain//osd.h 2012-06-02 12:32:38.000000000 +0200
++++ osd.h 2012-12-07 10:38:33.248902093 +0100
+@@ -931,6 +931,7 @@
+ ///< MyOsdDrawPixmap(Left() + pm->ViewPort().X(), Top() + pm->ViewPort().Y(), pm->Data(), w, h, h * d);
+ ///< delete pm;
+ ///< }
++ static bool pinValid; // PIN PATCH
+ };
+
+ #define MAXOSDIMAGES 64
+--- ../vdr-1.7.32.plain//status.c 2012-03-07 15:17:24.000000000 +0100
++++ status.c 2012-12-07 10:38:33.248902093 +0100
+@@ -124,3 +124,55 @@
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+ sm->OsdProgramme(PresentTime, PresentTitle, PresentSubtitle, FollowingTime, FollowingTitle, FollowingSubtitle);
+ }
++
++bool cStatus::MsgChannelProtected(const cDevice* Device, const cChannel* Channel) // PIN PATCH
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ if (sm->ChannelProtected(Device, Channel) == true)
++ return true;
++
++ return false;
++}
++
++bool cStatus::MsgReplayProtected(const cRecording* Recording, const char* Name,
++ const char* Base, bool isDirectory, int menuView) // PIN PATCH
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ if (sm->ReplayProtected(Recording, Name, Base, isDirectory, menuView) == true)
++ return true;
++ return false;
++}
++
++void cStatus::MsgRecordingFile(const char* FileName)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm)) // PIN PATCH
++ sm->RecordingFile(FileName);
++}
++
++void cStatus::MsgTimerCreation(cTimer* Timer, const cEvent *Event)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm)) // PIN PATCH
++ sm->TimerCreation(Timer, Event);
++}
++
++bool cStatus::MsgPluginProtected(cPlugin* Plugin, int menuView) // PIN PATCH
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ if (sm->PluginProtected(Plugin, menuView) == true)
++ return true;
++ return false;
++}
++
++void cStatus::MsgUserAction(const eKeys key, const cOsdObject* Interact) // PIN PATCH
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ sm->UserAction(key, Interact);
++}
++
++bool cStatus::MsgMenuItemProtected(const char* Name, int menuView) // PIN PATCH
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ if (sm->MenuItemProtected(Name, menuView) == true)
++ return true;
++ return false;
++}
+--- ../vdr-1.7.32.plain//status.h 2012-03-07 15:16:57.000000000 +0100
++++ status.h 2012-12-07 10:38:33.248902093 +0100
+@@ -14,6 +14,7 @@
+ #include "device.h"
+ #include "player.h"
+ #include "tools.h"
++#include "plugin.h"
+
+ enum eTimerChange { tcMod, tcAdd, tcDel };
+
+@@ -81,6 +82,24 @@
+ // The OSD displays the single line Text with the current channel information.
+ virtual void OsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle) {}
+ // The OSD displays the given programme information.
++ virtual bool ChannelProtected(const cDevice *Device, const cChannel* Channel) { return false; } // PIN PATCH
++ // Checks if a channel is protected.
++ virtual bool ReplayProtected(const cRecording* Recording, const char* Name,
++ const char* Base, bool isDirectory, int menuView = false) { return false; } // PIN PATCH
++ // Checks if a recording is protected.
++ virtual void RecordingFile(const char* FileName) {} // PIN PATCH
++ // The given DVB device has started recording to FileName. FileName is the name of the
++ // recording directory
++ virtual void TimerCreation(cTimer* Timer, const cEvent *Event) {} // PIN PATCH
++ // The given timer is created
++ virtual bool PluginProtected(cPlugin* Plugin, int menuView = false) { return false; } // PIN PATCH
++ // Checks if a plugin is protected.
++ virtual void UserAction(const eKeys key, const cOsdObject* Interact) {} // PIN PATCH
++ // report user action
++ virtual bool MenuItemProtected(const char* Name, int menuView = false) { return false; } // PIN PATCH
++ // Checks if a menu entry is protected.
++
++
+ public:
+ cStatus(void);
+ virtual ~cStatus();
+@@ -102,6 +121,15 @@
+ static void MsgOsdTextItem(const char *Text, bool Scroll = false);
+ static void MsgOsdChannel(const char *Text);
+ static void MsgOsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle);
++ static bool MsgChannelProtected(const cDevice* Device, const cChannel* Channel); // PIN PATCH
++ static bool MsgReplayProtected(const cRecording* Recording, const char* Name,
++ const char* Base, bool isDirectory, int menuView = false); // PIN PATCH
++ static void MsgRecordingFile(const char* FileName); // PIN PATCH
++ static void MsgTimerCreation(cTimer* Timer, const cEvent *Event); // PIN PATCH
++ static bool MsgPluginProtected(cPlugin* Plugin, int menuView = false); // PIN PATCH
++ static void MsgUserAction(const eKeys key, const cOsdObject* Interact);
++ static bool MsgMenuItemProtected(const char* Name, int menuView = false); // PIN PATCH
++
+ };
+
+ #endif //__STATUS_H
+--- ../vdr-1.7.32.plain//timers.c 2012-10-16 10:22:39.000000000 +0200
++++ timers.c 2012-12-07 11:07:02.344962535 +0100
+@@ -78,6 +78,7 @@
+ stop -= 2400;
+ priority = Pause ? Setup.PausePriority : Setup.DefaultPriority;
+ lifetime = Pause ? Setup.PauseLifetime : Setup.DefaultLifetime;
++ fskProtection = 0; // PIN PATCH
+ if (Instant && channel)
+ snprintf(file, sizeof(file), "%s%s", Setup.MarkInstantRecord ? "@" : "", *Setup.NameInstantRecord ? Setup.NameInstantRecord : channel->Name());
+ if (VfatFileSystem && (Utf8StrLen(file) > VFAT_MAX_FILENAME)) {
+@@ -117,6 +118,7 @@
+ stop -= 2400;
+ priority = Setup.DefaultPriority;
+ lifetime = Setup.DefaultLifetime;
++ fskProtection = 0; // PIN PATCH
+ const char *Title = Event->Title();
+ if (!isempty(Title))
+ Utf8Strn0Cpy(file, Event->Title(), sizeof(file));
+@@ -126,6 +128,7 @@
+ dsyslog("timer file name truncated to '%s'", file);
+ }
+ SetEvent(Event);
++ cStatus::MsgTimerCreation(this, Event); // PIN PATCH
+ }
+
+ cTimer::cTimer(const cTimer &Timer)
+@@ -161,6 +164,7 @@
+ stop = Timer.stop;
+ priority = Timer.priority;
+ lifetime = Timer.lifetime;
++ fskProtection = Timer.fskProtection; // PIN PATCH
+ strncpy(file, Timer.file, sizeof(file));
+ free(aux);
+ aux = Timer.aux ? strdup(Timer.aux) : NULL;
+@@ -355,6 +359,7 @@
+ result = false;
+ }
+ }
++ fskProtection = aux && strstr(aux, "<pin-plugin><protected>yes</protected></pin-plugin>"); // PIN PATCH
+ free(channelbuffer);
+ free(daybuffer);
+ free(filebuffer);
+@@ -713,6 +718,36 @@
+ Matches(); // refresh start and end time
+ }
+
++void cTimer::SetFskProtection(int aFlag) // PIN PATCH
++{
++ char* p;
++ char* tmp = 0;
++ int res = 0;
++
++ fskProtection = aFlag;
++
++ if (fskProtection && (!aux || !strstr(aux, "<pin-plugin><protected>yes</protected></pin-plugin>")))
++ {
++ // add protection info to aux
++
++ if (aux) { tmp = strdup(aux); free(aux); }
++ res = asprintf(&aux, "%s<pin-plugin><protected>yes</protected></pin-plugin>", tmp ? tmp : "");
++ }
++ else if (!fskProtection && aux && (p = strstr(aux, "<pin-plugin><protected>yes</protected></pin-plugin>")))
++ {
++ // remove protection info from aux
++
++ res = asprintf(&tmp, "%.*s%s", (int)(p-aux), aux, p+strlen("<pin-plugin><protected>yes</protected></pin-plugin>"));
++ free(aux);
++ aux = strdup(tmp);
++ }
++
++ if (res < 0) ; // memory problems :o
++
++ if (tmp)
++ free(tmp);
++}
++
+ // --- cTimers ---------------------------------------------------------------
+
+ cTimers Timers;
+--- ../vdr-1.7.32.plain//timers.h 2012-04-15 15:21:31.000000000 +0200
++++ timers.h 2012-12-07 10:38:33.248902093 +0100
+@@ -38,6 +38,7 @@
+ int start;
+ int stop;
+ int priority;
++ int fskProtection; // PIN PATCH
+ int lifetime;
+ mutable char file[MaxFileName];
+ char *aux;
+@@ -59,6 +60,7 @@
+ int Start(void) const { return start; }
+ int Stop(void) const { return stop; }
+ int Priority(void) const { return priority; }
++ int FskProtection(void) const { return fskProtection; } // PIN PATCH
+ int Lifetime(void) const { return lifetime; }
+ const char *File(void) const { return file; }
+ time_t FirstDay(void) const { return weekdays ? day : 0; }
+@@ -95,6 +97,7 @@
+ void SetAux(const char *Aux);
+ void SetDeferred(int Seconds);
+ void SetFlags(uint Flags);
++ void SetFskProtection(int aFlag); // PIN PATCH
+ void ClrFlags(uint Flags);
+ void InvFlags(uint Flags);
+ bool HasFlags(uint Flags) const;
+--- ../vdr-1.7.32.plain//vdr.c 2012-10-13 14:48:56.000000000 +0200
++++ vdr.c 2012-12-07 11:07:50.728964248 +0100
+@@ -65,6 +65,7 @@
+ #include "tools.h"
+ #include "transfer.h"
+ #include "videodir.h"
++#include "status.h" // PIN PATCH
+
+ #define MINCHANNELWAIT 10 // seconds to wait between failed channel switchings
+ #define ACTIVITYTIMEOUT 60 // seconds before starting housekeeping
+@@ -948,6 +949,7 @@
+ cOsdObject *Interact = Menu ? Menu : cControl::Control();
+ eKeys key = Interface->GetKey(!Interact || !Interact->NeedsFastResponse());
+ if (ISREALKEY(key)) {
++ cStatus::MsgUserAction(key, Interact); // PIN PATCH
+ EITScanner.Activity();
+ // Cancel shutdown countdown:
+ if (ShutdownHandler.countdown)
+@@ -1020,10 +1022,12 @@
+ cControl::Control()->Hide();
+ cPlugin *plugin = cPluginManager::GetPlugin(PluginName);
+ if (plugin) {
++ if (!cStatus::MsgPluginProtected(plugin)) {
+ Menu = plugin->MainMenuAction();
+ if (Menu)
+ Menu->Show();
+ }
++ }
+ else
+ esyslog("ERROR: unknown plugin '%s'", PluginName);
+ }
+@@ -1235,9 +1239,11 @@
+ // Instant resume of the last viewed recording:
+ case kPlay:
+ if (cReplayControl::LastReplayed()) {
++ if (cStatus::MsgReplayProtected(0, cReplayControl::LastReplayed(), 0, false) == false) { // PIN PATCH
+ cControl::Shutdown();
+ cControl::Launch(new cReplayControl);
+ }
++ }
+ else
+ DirectMainFunction(osRecordings); // no last viewed recording, so enter the Recordings menu
+ break;
diff --git a/patches/vdr-1.7.33.diff b/patches/vdr-1.7.33.diff
new file mode 100644
index 0000000..38ba45f
--- /dev/null
+++ b/patches/vdr-1.7.33.diff
@@ -0,0 +1,427 @@
+--- ../vdr-1.7.33/device.c 2012-11-19 10:59:09.000000000 +0100
++++ device.c 2012-12-10 10:14:03.106016736 +0100
+@@ -721,6 +721,7 @@
+ cChannel *channel;
+ while ((channel = Channels.GetByNumber(n, Direction)) != NULL) {
+ // try only channels which are currently available
++ if (!cStatus::MsgChannelProtected(0, channel)) // PIN PATCH
+ if (GetDevice(channel, LIVEPRIORITY, true, true))
+ break;
+ n = channel->Number() + Direction;
+@@ -742,6 +743,12 @@
+
+ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
+ {
++ // I hope 'LiveView = false' indicates a channel switch for recording, // PIN PATCH
++ // I really don't know, but it works ... // PIN PATCH
++
++ if (LiveView && cStatus::MsgChannelProtected(this, Channel)) // PIN PATCH
++ return scrNotAvailable; // PIN PATCH
++
+ cStatus::MsgChannelSwitch(this, 0, LiveView);
+
+ if (LiveView) {
+--- ../vdr-1.7.33/menu.c 2012-12-07 14:48:15.000000000 +0100
++++ menu.c 2012-12-10 10:21:12.498031920 +0100
+@@ -896,6 +896,17 @@
+ Add(new cMenuEditBitItem( tr("VPS"), &data.flags, tfVps));
+ Add(new cMenuEditIntItem( tr("Priority"), &data.priority, 0, MAXPRIORITY));
+ Add(new cMenuEditIntItem( tr("Lifetime"), &data.lifetime, 0, MAXLIFETIME));
++
++ // PIN PATCH
++ if (cOsd::pinValid || !data.fskProtection) Add(new cMenuEditBoolItem(tr("Childlock"),&data.fskProtection));
++ else {
++ char* buf = 0;
++ if (asprintf(&buf, "%s\t%s", tr("Childlock"), data.fskProtection ? tr("yes") : tr("no")) >= 0){
++ Add(new cOsdItem(buf));
++ free(buf);
++ }
++ }
++
+ Add(file = new cMenuEditStrItem( tr("File"), data.file, sizeof(data.file)));
+ SetFirstDayItem();
+ }
+@@ -2285,7 +2296,10 @@
+ for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) {
+ if (!base || (strstr(recording->Name(), base) == recording->Name() && recording->Name()[strlen(base)] == FOLDERDELIMCHAR)) {
+ cMenuRecordingItem *Item = new cMenuRecordingItem(recording, level);
+- if (*Item->Text() && (!Item->IsDirectory() || (!LastItem || !LastItem->IsDirectory() || strcmp(Item->Text(), LastItemText) != 0))) {
++
++ if (*Item->Text() && (!Item->IsDirectory() || (!LastItem || !LastItem->IsDirectory() || strcmp(Item->Text(), LastItemText) != 0))
++ && (!cStatus::MsgReplayProtected(Item->Recording(), Item->Name(), base,
++ Item->IsDirectory(), true))) { // PIN PATCH
+ Add(Item);
+ LastItem = Item;
+ free(LastItemText);
+@@ -2337,6 +2351,9 @@
+ {
+ cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current());
+ if (ri) {
++ if (cStatus::MsgReplayProtected(ri->Recording(), ri->Name(), base,
++ ri->IsDirectory()) == true) // PIN PATCH
++ return osContinue; // PIN PATCH
+ if (ri->IsDirectory())
+ Open();
+ else {
+@@ -3379,28 +3396,32 @@
+
+ // Basic menu items:
+
+- Add(new cOsdItem(hk(tr("Schedule")), osSchedule));
+- Add(new cOsdItem(hk(tr("Channels")), osChannels));
+- Add(new cOsdItem(hk(tr("Timers")), osTimers));
+- Add(new cOsdItem(hk(tr("Recordings")), osRecordings));
++ // PIN PATCH
++ if (!cStatus::MsgMenuItemProtected("Schedule", true)) Add(new cOsdItem(hk(tr("Schedule")), osSchedule));
++ if (!cStatus::MsgMenuItemProtected("Channels", true)) Add(new cOsdItem(hk(tr("Channels")), osChannels));
++ if (!cStatus::MsgMenuItemProtected("Timers", true)) Add(new cOsdItem(hk(tr("Timers")), osTimers));
++ if (!cStatus::MsgMenuItemProtected("Recordings", true)) Add(new cOsdItem(hk(tr("Recordings")), osRecordings));
+
+ // Plugins:
+
+ for (int i = 0; ; i++) {
+ cPlugin *p = cPluginManager::GetPlugin(i);
+ if (p) {
++ if (!cStatus::MsgPluginProtected(p, true)) { // PIN PATCH
+ const char *item = p->MainMenuEntry();
+ if (item)
+ Add(new cMenuPluginItem(hk(item), i));
+ }
++ }
+ else
+ break;
+ }
+
+ // More basic menu items:
+
+- Add(new cOsdItem(hk(tr("Setup")), osSetup));
++ if (!cStatus::MsgMenuItemProtected("Setup", true)) Add(new cOsdItem(hk(tr("Setup")), osSetup)); // PIN PATCH
+ if (Commands.Count())
++ if (!cStatus::MsgMenuItemProtected("Commands", true)) // PIN PATCH
+ Add(new cOsdItem(hk(tr("Commands")), osCommands));
+
+ Update(true);
+@@ -3469,6 +3490,14 @@
+ eOSState state = cOsdMenu::ProcessKey(Key);
+ HadSubMenu |= HasSubMenu();
+
++ // > PIN PATCH
++ cOsdItem* item = Get(Current());
++
++ if (item && item->Text() && state != osContinue && state != osUnknown && state != osBack)
++ if (cStatus::MsgMenuItemProtected(item->Text()))
++ return osContinue;
++ // PIN PATCH <
++
+ switch (state) {
+ case osSchedule: return AddSubMenu(new cMenuSchedule);
+ case osChannels: return AddSubMenu(new cMenuChannels);
+@@ -3494,6 +3523,7 @@
+ if (item) {
+ cPlugin *p = cPluginManager::GetPlugin(item->PluginIndex());
+ if (p) {
++ if (!cStatus::MsgPluginProtected(p)) { // PIN PATCH
+ cOsdObject *menu = p->MainMenuAction();
+ if (menu) {
+ if (menu->IsMenu())
+@@ -3505,6 +3535,7 @@
+ }
+ }
+ }
++ }
+ state = osEnd;
+ }
+ break;
+@@ -3675,6 +3706,7 @@
+ Channel = Direction > 0 ? Channels.Next(Channel) : Channels.Prev(Channel);
+ if (!Channel && Setup.ChannelsWrap)
+ Channel = Direction > 0 ? Channels.First() : Channels.Last();
++ if (!cStatus::MsgChannelProtected(0, Channel)) // PIN PATCH
+ if (Channel && !Channel->GroupSep() && cDevice::GetDevice(Channel, LIVEPRIORITY, true, true))
+ return Channel;
+ }
+@@ -4324,6 +4356,7 @@
+ for (int i = 0; i < MAXRECORDCONTROLS; i++) {
+ if (!RecordControls[i]) {
+ RecordControls[i] = new cRecordControl(device, Timer, Pause);
++ cStatus::MsgRecordingFile(RecordControls[i]->FileName()); // PIN PATCH
+ return RecordControls[i]->Process(time(NULL));
+ }
+ }
+--- ../vdr-1.7.33/osd.c 2012-06-09 16:37:24.000000000 +0200
++++ osd.c 2012-12-10 10:14:03.110016736 +0100
+@@ -1623,6 +1623,7 @@
+ int cOsd::osdHeight = 0;
+ cVector<cOsd *> cOsd::Osds;
+ cMutex cOsd::mutex;
++bool cOsd::pinValid = false; // PIN PATCH
+
+ cOsd::cOsd(int Left, int Top, uint Level)
+ {
+--- ../vdr-1.7.33/osd.h 2012-12-03 14:49:02.000000000 +0100
++++ osd.h 2012-12-10 10:14:03.114016736 +0100
+@@ -931,6 +931,7 @@
+ ///< MyOsdDrawPixmap(Left() + pm->ViewPort().X(), Top() + pm->ViewPort().Y(), pm->Data(), w, h, h * d);
+ ///< delete pm;
+ ///< }
++ static bool pinValid; // PIN PATCH
+ };
+
+ #define MAXOSDIMAGES 64
+--- ../vdr-1.7.33/status.c 2012-03-07 15:17:24.000000000 +0100
++++ status.c 2012-12-10 10:14:03.114016736 +0100
+@@ -124,3 +124,55 @@
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+ sm->OsdProgramme(PresentTime, PresentTitle, PresentSubtitle, FollowingTime, FollowingTitle, FollowingSubtitle);
+ }
++
++bool cStatus::MsgChannelProtected(const cDevice* Device, const cChannel* Channel) // PIN PATCH
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ if (sm->ChannelProtected(Device, Channel) == true)
++ return true;
++
++ return false;
++}
++
++bool cStatus::MsgReplayProtected(const cRecording* Recording, const char* Name,
++ const char* Base, bool isDirectory, int menuView) // PIN PATCH
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ if (sm->ReplayProtected(Recording, Name, Base, isDirectory, menuView) == true)
++ return true;
++ return false;
++}
++
++void cStatus::MsgRecordingFile(const char* FileName)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm)) // PIN PATCH
++ sm->RecordingFile(FileName);
++}
++
++void cStatus::MsgTimerCreation(cTimer* Timer, const cEvent *Event)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm)) // PIN PATCH
++ sm->TimerCreation(Timer, Event);
++}
++
++bool cStatus::MsgPluginProtected(cPlugin* Plugin, int menuView) // PIN PATCH
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ if (sm->PluginProtected(Plugin, menuView) == true)
++ return true;
++ return false;
++}
++
++void cStatus::MsgUserAction(const eKeys key, const cOsdObject* Interact) // PIN PATCH
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ sm->UserAction(key, Interact);
++}
++
++bool cStatus::MsgMenuItemProtected(const char* Name, int menuView) // PIN PATCH
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ if (sm->MenuItemProtected(Name, menuView) == true)
++ return true;
++ return false;
++}
+--- ../vdr-1.7.33/status.h 2012-03-07 15:16:57.000000000 +0100
++++ status.h 2012-12-10 10:14:03.114016736 +0100
+@@ -14,6 +14,7 @@
+ #include "device.h"
+ #include "player.h"
+ #include "tools.h"
++#include "plugin.h"
+
+ enum eTimerChange { tcMod, tcAdd, tcDel };
+
+@@ -81,6 +82,24 @@
+ // The OSD displays the single line Text with the current channel information.
+ virtual void OsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle) {}
+ // The OSD displays the given programme information.
++ virtual bool ChannelProtected(const cDevice *Device, const cChannel* Channel) { return false; } // PIN PATCH
++ // Checks if a channel is protected.
++ virtual bool ReplayProtected(const cRecording* Recording, const char* Name,
++ const char* Base, bool isDirectory, int menuView = false) { return false; } // PIN PATCH
++ // Checks if a recording is protected.
++ virtual void RecordingFile(const char* FileName) {} // PIN PATCH
++ // The given DVB device has started recording to FileName. FileName is the name of the
++ // recording directory
++ virtual void TimerCreation(cTimer* Timer, const cEvent *Event) {} // PIN PATCH
++ // The given timer is created
++ virtual bool PluginProtected(cPlugin* Plugin, int menuView = false) { return false; } // PIN PATCH
++ // Checks if a plugin is protected.
++ virtual void UserAction(const eKeys key, const cOsdObject* Interact) {} // PIN PATCH
++ // report user action
++ virtual bool MenuItemProtected(const char* Name, int menuView = false) { return false; } // PIN PATCH
++ // Checks if a menu entry is protected.
++
++
+ public:
+ cStatus(void);
+ virtual ~cStatus();
+@@ -102,6 +121,15 @@
+ static void MsgOsdTextItem(const char *Text, bool Scroll = false);
+ static void MsgOsdChannel(const char *Text);
+ static void MsgOsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle);
++ static bool MsgChannelProtected(const cDevice* Device, const cChannel* Channel); // PIN PATCH
++ static bool MsgReplayProtected(const cRecording* Recording, const char* Name,
++ const char* Base, bool isDirectory, int menuView = false); // PIN PATCH
++ static void MsgRecordingFile(const char* FileName); // PIN PATCH
++ static void MsgTimerCreation(cTimer* Timer, const cEvent *Event); // PIN PATCH
++ static bool MsgPluginProtected(cPlugin* Plugin, int menuView = false); // PIN PATCH
++ static void MsgUserAction(const eKeys key, const cOsdObject* Interact);
++ static bool MsgMenuItemProtected(const char* Name, int menuView = false); // PIN PATCH
++
+ };
+
+ #endif //__STATUS_H
+--- ../vdr-1.7.33/timers.c 2012-12-07 14:14:00.000000000 +0100
++++ timers.c 2012-12-10 10:14:03.114016736 +0100
+@@ -78,6 +78,7 @@
+ stop -= 2400;
+ priority = Pause ? Setup.PausePriority : Setup.DefaultPriority;
+ lifetime = Pause ? Setup.PauseLifetime : Setup.DefaultLifetime;
++ fskProtection = 0; // PIN PATCH
+ if (Instant && channel)
+ snprintf(file, sizeof(file), "%s%s", Setup.MarkInstantRecord ? "@" : "", *Setup.NameInstantRecord ? Setup.NameInstantRecord : channel->Name());
+ if (VfatFileSystem && (Utf8StrLen(file) > VFAT_MAX_FILENAME)) {
+@@ -117,6 +118,7 @@
+ stop -= 2400;
+ priority = Setup.DefaultPriority;
+ lifetime = Setup.DefaultLifetime;
++ fskProtection = 0; // PIN PATCH
+ const char *Title = Event->Title();
+ if (!isempty(Title))
+ Utf8Strn0Cpy(file, Event->Title(), sizeof(file));
+@@ -126,6 +128,7 @@
+ dsyslog("timer file name truncated to '%s'", file);
+ }
+ SetEvent(Event);
++ cStatus::MsgTimerCreation(this, Event); // PIN PATCH
+ }
+
+ cTimer::cTimer(const cTimer &Timer)
+@@ -161,6 +164,7 @@
+ stop = Timer.stop;
+ priority = Timer.priority;
+ lifetime = Timer.lifetime;
++ fskProtection = Timer.fskProtection; // PIN PATCH
+ strncpy(file, Timer.file, sizeof(file));
+ free(aux);
+ aux = Timer.aux ? strdup(Timer.aux) : NULL;
+@@ -355,6 +359,7 @@
+ result = false;
+ }
+ }
++ fskProtection = aux && strstr(aux, "<pin-plugin><protected>yes</protected></pin-plugin>"); // PIN PATCH
+ free(channelbuffer);
+ free(daybuffer);
+ free(filebuffer);
+@@ -713,6 +718,36 @@
+ Matches(); // refresh start and end time
+ }
+
++void cTimer::SetFskProtection(int aFlag) // PIN PATCH
++{
++ char* p;
++ char* tmp = 0;
++ int res = 0;
++
++ fskProtection = aFlag;
++
++ if (fskProtection && (!aux || !strstr(aux, "<pin-plugin><protected>yes</protected></pin-plugin>")))
++ {
++ // add protection info to aux
++
++ if (aux) { tmp = strdup(aux); free(aux); }
++ res = asprintf(&aux, "%s<pin-plugin><protected>yes</protected></pin-plugin>", tmp ? tmp : "");
++ }
++ else if (!fskProtection && aux && (p = strstr(aux, "<pin-plugin><protected>yes</protected></pin-plugin>")))
++ {
++ // remove protection info from aux
++
++ res = asprintf(&tmp, "%.*s%s", (int)(p-aux), aux, p+strlen("<pin-plugin><protected>yes</protected></pin-plugin>"));
++ free(aux);
++ aux = strdup(tmp);
++ }
++
++ if (res < 0) ; // memory problems :o
++
++ if (tmp)
++ free(tmp);
++}
++
+ // --- cTimers ---------------------------------------------------------------
+
+ cTimers Timers;
+--- ../vdr-1.7.33/timers.h 2012-12-07 14:13:40.000000000 +0100
++++ timers.h 2012-12-10 10:14:03.114016736 +0100
+@@ -38,6 +38,7 @@
+ int start;
+ int stop;
+ int priority;
++ int fskProtection; // PIN PATCH
+ int lifetime;
+ mutable char file[MaxFileName];
+ char *aux;
+@@ -59,6 +60,7 @@
+ int Start(void) const { return start; }
+ int Stop(void) const { return stop; }
+ int Priority(void) const { return priority; }
++ int FskProtection(void) const { return fskProtection; } // PIN PATCH
+ int Lifetime(void) const { return lifetime; }
+ const char *File(void) const { return file; }
+ time_t FirstDay(void) const { return weekdays ? day : 0; }
+@@ -95,6 +97,7 @@
+ void SetAux(const char *Aux);
+ void SetDeferred(int Seconds);
+ void SetFlags(uint Flags);
++ void SetFskProtection(int aFlag); // PIN PATCH
+ void ClrFlags(uint Flags);
+ void InvFlags(uint Flags);
+ bool HasFlags(uint Flags) const;
+--- ../vdr-1.7.33/vdr.c 2012-12-06 11:29:23.000000000 +0100
++++ vdr.c 2012-12-10 10:14:03.114016736 +0100
+@@ -65,6 +65,7 @@
+ #include "tools.h"
+ #include "transfer.h"
+ #include "videodir.h"
++#include "status.h" // PIN PATCH
+
+ #define MINCHANNELWAIT 10 // seconds to wait between failed channel switchings
+ #define ACTIVITYTIMEOUT 60 // seconds before starting housekeeping
+@@ -947,6 +948,7 @@
+ cOsdObject *Interact = Menu ? Menu : cControl::Control();
+ eKeys key = Interface->GetKey(!Interact || !Interact->NeedsFastResponse());
+ if (ISREALKEY(key)) {
++ cStatus::MsgUserAction(key, Interact); // PIN PATCH
+ EITScanner.Activity();
+ // Cancel shutdown countdown:
+ if (ShutdownHandler.countdown)
+@@ -1019,10 +1021,12 @@
+ cControl::Control()->Hide();
+ cPlugin *plugin = cPluginManager::GetPlugin(PluginName);
+ if (plugin) {
++ if (!cStatus::MsgPluginProtected(plugin)) {
+ Menu = plugin->MainMenuAction();
+ if (Menu)
+ Menu->Show();
+ }
++ }
+ else
+ esyslog("ERROR: unknown plugin '%s'", PluginName);
+ }
+@@ -1235,9 +1239,11 @@
+ // Instant resume of the last viewed recording:
+ case kPlay:
+ if (cReplayControl::LastReplayed()) {
++ if (cStatus::MsgReplayProtected(0, cReplayControl::LastReplayed(), 0, false) == false) { // PIN PATCH
+ cControl::Shutdown();
+ cControl::Launch(new cReplayControl);
+ }
++ }
+ else
+ DirectMainFunction(osRecordings); // no last viewed recording, so enter the Recordings menu
+ break;
diff --git a/patches/vdr-2.0.2.diff b/patches/vdr-2.0.2.diff
new file mode 100644
index 0000000..81dcd22
--- /dev/null
+++ b/patches/vdr-2.0.2.diff
@@ -0,0 +1,420 @@
+--- ../vdr-2.0.2/device.c 2013-04-05 12:47:38.000000000 +0200
++++ device.c 2013-05-22 10:17:56.758196352 +0200
+@@ -722,6 +722,7 @@
+ cChannel *channel;
+ while ((channel = Channels.GetByNumber(n, Direction)) != NULL) {
+ // try only channels which are currently available
++ if (!cStatus::MsgChannelProtected(0, channel)) // PIN PATCH
+ if (GetDevice(channel, LIVEPRIORITY, true, true))
+ break;
+ n = channel->Number() + Direction;
+@@ -743,6 +744,12 @@
+
+ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
+ {
++ // I hope 'LiveView = false' indicates a channel switch for recording, // PIN PATCH
++ // I really don't know, but it works ... // PIN PATCH
++
++ if (LiveView && cStatus::MsgChannelProtected(this, Channel)) // PIN PATCH
++ return scrNotAvailable; // PIN PATCH
++
+ cStatus::MsgChannelSwitch(this, 0, LiveView);
+
+ if (LiveView) {
+--- ../vdr-2.0.2/menu.c 2013-04-27 12:32:28.000000000 +0200
++++ menu.c 2013-05-22 10:26:55.198215394 +0200
+@@ -896,6 +896,18 @@
+ Add(new cMenuEditBitItem( tr("VPS"), &data.flags, tfVps));
+ Add(new cMenuEditIntItem( tr("Priority"), &data.priority, 0, MAXPRIORITY));
+ Add(new cMenuEditIntItem( tr("Lifetime"), &data.lifetime, 0, MAXLIFETIME));
++
++ // PIN PATCH
++ if (cOsd::pinValid || !data.fskProtection) Add(new cMenuEditBoolItem(tr("Childlock"),&data.fskProtection));
++ else {
++ char* buf = 0;
++ int res = 0;
++ res = asprintf(&buf, "%s\t%s", tr("Childlock"), data.fskProtection ? tr("yes") : tr("no"));
++ if (res < 0) ; // memory problems :o
++ Add(new cOsdItem(buf));
++ free(buf);
++ }
++
+ Add(file = new cMenuEditStrItem( tr("File"), data.file, sizeof(data.file)));
+ SetFirstDayItem();
+ }
+@@ -2294,7 +2306,8 @@
+ }
+ }
+ }
+- if (*Item->Text() && !LastDir) {
++ if (*Item->Text() && !LastDir
++ && (!cStatus::MsgReplayProtected(Item->Recording(), Item->Name(), base, Item->IsDirectory(), true))) { // PIN PATCH
+ Add(Item);
+ LastItem = Item;
+ if (Item->IsDirectory())
+@@ -2345,6 +2358,9 @@
+ {
+ cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current());
+ if (ri) {
++ if (cStatus::MsgReplayProtected(ri->Recording(), ri->Name(), base,
++ ri->IsDirectory()) == true) // PIN PATCH
++ return osContinue; // PIN PATCH
+ if (ri->IsDirectory())
+ Open();
+ else {
+@@ -3403,28 +3419,32 @@
+
+ // Basic menu items:
+
+- Add(new cOsdItem(hk(tr("Schedule")), osSchedule));
+- Add(new cOsdItem(hk(tr("Channels")), osChannels));
+- Add(new cOsdItem(hk(tr("Timers")), osTimers));
+- Add(new cOsdItem(hk(tr("Recordings")), osRecordings));
++ // PIN PATCH
++ if (!cStatus::MsgMenuItemProtected("Schedule", true)) Add(new cOsdItem(hk(tr("Schedule")), osSchedule));
++ if (!cStatus::MsgMenuItemProtected("Channels", true)) Add(new cOsdItem(hk(tr("Channels")), osChannels));
++ if (!cStatus::MsgMenuItemProtected("Timers", true)) Add(new cOsdItem(hk(tr("Timers")), osTimers));
++ if (!cStatus::MsgMenuItemProtected("Recordings", true)) Add(new cOsdItem(hk(tr("Recordings")), osRecordings));
+
+ // Plugins:
+
+ for (int i = 0; ; i++) {
+ cPlugin *p = cPluginManager::GetPlugin(i);
+ if (p) {
++ if (!cStatus::MsgPluginProtected(p, true)) { // PIN PATCH
+ const char *item = p->MainMenuEntry();
+ if (item)
+ Add(new cMenuPluginItem(hk(item), i));
+ }
++ }
+ else
+ break;
+ }
+
+ // More basic menu items:
+
+- Add(new cOsdItem(hk(tr("Setup")), osSetup));
++ if (!cStatus::MsgMenuItemProtected("Setup", true)) Add(new cOsdItem(hk(tr("Setup")), osSetup)); // PIN PATCH
+ if (Commands.Count())
++ if (!cStatus::MsgMenuItemProtected("Commands", true)) // PIN PATCH
+ Add(new cOsdItem(hk(tr("Commands")), osCommands));
+
+ Update(true);
+@@ -3493,6 +3513,14 @@
+ eOSState state = cOsdMenu::ProcessKey(Key);
+ HadSubMenu |= HasSubMenu();
+
++ // > PIN PATCH
++ cOsdItem* item = Get(Current());
++
++ if (item && item->Text() && state != osContinue && state != osUnknown && state != osBack)
++ if (cStatus::MsgMenuItemProtected(item->Text()))
++ return osContinue;
++ // PIN PATCH <
++
+ switch (state) {
+ case osSchedule: return AddSubMenu(new cMenuSchedule);
+ case osChannels: return AddSubMenu(new cMenuChannels);
+@@ -3518,6 +3546,7 @@
+ if (item) {
+ cPlugin *p = cPluginManager::GetPlugin(item->PluginIndex());
+ if (p) {
++ if (!cStatus::MsgPluginProtected(p)) { // PIN PATCH
+ cOsdObject *menu = p->MainMenuAction();
+ if (menu) {
+ if (menu->IsMenu())
+@@ -3529,6 +3558,7 @@
+ }
+ }
+ }
++ }
+ state = osEnd;
+ }
+ break;
+@@ -3699,6 +3729,7 @@
+ Channel = Direction > 0 ? Channels.Next(Channel) : Channels.Prev(Channel);
+ if (!Channel && Setup.ChannelsWrap)
+ Channel = Direction > 0 ? Channels.First() : Channels.Last();
++ if (!cStatus::MsgChannelProtected(0, Channel)) // PIN PATCH
+ if (Channel && !Channel->GroupSep() && cDevice::GetDevice(Channel, LIVEPRIORITY, true, true))
+ return Channel;
+ }
+@@ -4348,6 +4379,7 @@
+ for (int i = 0; i < MAXRECORDCONTROLS; i++) {
+ if (!RecordControls[i]) {
+ RecordControls[i] = new cRecordControl(device, Timer, Pause);
++ cStatus::MsgRecordingFile(RecordControls[i]->FileName()); // PIN PATCH
+ return RecordControls[i]->Process(time(NULL));
+ }
+ }
+--- ../vdr-2.0.2/osd.c 2013-05-18 14:41:48.000000000 +0200
++++ osd.c 2013-05-22 10:17:56.758196352 +0200
+@@ -1623,6 +1623,7 @@
+ int cOsd::osdHeight = 0;
+ cVector<cOsd *> cOsd::Osds;
+ cMutex cOsd::mutex;
++bool cOsd::pinValid = false; // PIN PATCH
+
+ cOsd::cOsd(int Left, int Top, uint Level)
+ {
+--- ../vdr-2.0.2/osd.h 2013-02-12 14:39:08.000000000 +0100
++++ osd.h 2013-05-22 10:24:49.822210960 +0200
+@@ -926,6 +926,7 @@
+ ///< MyOsdDrawPixmap(Left() + pm->ViewPort().X(), Top() + pm->ViewPort().Y(), pm->Data(), w, h, h * d);
+ ///< delete pm;
+ ///< }
++ static bool pinValid; // PIN PATCH
+ };
+
+ #define MAXOSDIMAGES 64
+--- ../vdr-2.0.2/status.c 2012-03-07 15:17:24.000000000 +0100
++++ status.c 2013-05-22 10:17:56.758196352 +0200
+@@ -124,3 +124,55 @@
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+ sm->OsdProgramme(PresentTime, PresentTitle, PresentSubtitle, FollowingTime, FollowingTitle, FollowingSubtitle);
+ }
++
++bool cStatus::MsgChannelProtected(const cDevice* Device, const cChannel* Channel) // PIN PATCH
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ if (sm->ChannelProtected(Device, Channel) == true)
++ return true;
++
++ return false;
++}
++
++bool cStatus::MsgReplayProtected(const cRecording* Recording, const char* Name,
++ const char* Base, bool isDirectory, int menuView) // PIN PATCH
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ if (sm->ReplayProtected(Recording, Name, Base, isDirectory, menuView) == true)
++ return true;
++ return false;
++}
++
++void cStatus::MsgRecordingFile(const char* FileName)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm)) // PIN PATCH
++ sm->RecordingFile(FileName);
++}
++
++void cStatus::MsgTimerCreation(cTimer* Timer, const cEvent *Event)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm)) // PIN PATCH
++ sm->TimerCreation(Timer, Event);
++}
++
++bool cStatus::MsgPluginProtected(cPlugin* Plugin, int menuView) // PIN PATCH
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ if (sm->PluginProtected(Plugin, menuView) == true)
++ return true;
++ return false;
++}
++
++void cStatus::MsgUserAction(const eKeys key, const cOsdObject* Interact) // PIN PATCH
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ sm->UserAction(key, Interact);
++}
++
++bool cStatus::MsgMenuItemProtected(const char* Name, int menuView) // PIN PATCH
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ if (sm->MenuItemProtected(Name, menuView) == true)
++ return true;
++ return false;
++}
+--- ../vdr-2.0.2/status.h 2012-03-07 15:16:57.000000000 +0100
++++ status.h 2013-05-22 10:20:19.902201414 +0200
+@@ -14,6 +14,7 @@
+ #include "device.h"
+ #include "player.h"
+ #include "tools.h"
++#include "plugin.h"
+
+ enum eTimerChange { tcMod, tcAdd, tcDel };
+
+@@ -81,6 +82,22 @@
+ // The OSD displays the single line Text with the current channel information.
+ virtual void OsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle) {}
+ // The OSD displays the given programme information.
++ virtual bool ChannelProtected(const cDevice *Device, const cChannel* Channel) { return false; } // PIN PATCH
++ // Checks if a channel is protected.
++ virtual bool ReplayProtected(const cRecording* Recording, const char* Name,
++ const char* Base, bool isDirectory, int menuView = false) { return false; } // PIN PATCH
++ // Checks if a recording is protected.
++ virtual void RecordingFile(const char* FileName) {} // PIN PATCH
++ // The given DVB device has started recording to FileName. FileName is the name of the
++ // recording directory
++ virtual void TimerCreation(cTimer* Timer, const cEvent *Event) {} // PIN PATCH
++ // The given timer is created
++ virtual bool PluginProtected(cPlugin* Plugin, int menuView = false) { return false; } // PIN PATCH
++ // Checks if a plugin is protected.
++ virtual void UserAction(const eKeys key, const cOsdObject* Interact) {} // PIN PATCH
++ // report user action
++ virtual bool MenuItemProtected(const char* Name, int menuView = false) { return false; } // PIN PATCH
++ // Checks if a menu entry is protected.
+ public:
+ cStatus(void);
+ virtual ~cStatus();
+@@ -102,6 +119,14 @@
+ static void MsgOsdTextItem(const char *Text, bool Scroll = false);
+ static void MsgOsdChannel(const char *Text);
+ static void MsgOsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle);
++ static bool MsgChannelProtected(const cDevice* Device, const cChannel* Channel); // PIN PATCH
++ static bool MsgReplayProtected(const cRecording* Recording, const char* Name,
++ const char* Base, bool isDirectory, int menuView = false); // PIN PATCH
++ static void MsgRecordingFile(const char* FileName); // PIN PATCH
++ static void MsgTimerCreation(cTimer* Timer, const cEvent *Event); // PIN PATCH
++ static bool MsgPluginProtected(cPlugin* Plugin, int menuView = false); // PIN PATCH
++ static void MsgUserAction(const eKeys key, const cOsdObject* Interact);
++ static bool MsgMenuItemProtected(const char* Name, int menuView = false); // PIN PATCH
+ };
+
+ #endif //__STATUS_H
+--- ../vdr-2.0.2/timers.c 2013-03-29 16:37:16.000000000 +0100
++++ timers.c 2013-05-22 10:17:56.762196352 +0200
+@@ -76,6 +76,7 @@
+ stop -= 2400;
+ priority = Pause ? Setup.PausePriority : Setup.DefaultPriority;
+ lifetime = Pause ? Setup.PauseLifetime : Setup.DefaultLifetime;
++ fskProtection = 0; // PIN PATCH
+ if (Instant && channel)
+ snprintf(file, sizeof(file), "%s%s", Setup.MarkInstantRecord ? "@" : "", *Setup.NameInstantRecord ? Setup.NameInstantRecord : channel->Name());
+ }
+@@ -110,10 +111,12 @@
+ stop -= 2400;
+ priority = Setup.DefaultPriority;
+ lifetime = Setup.DefaultLifetime;
++ fskProtection = 0; // PIN PATCH
+ const char *Title = Event->Title();
+ if (!isempty(Title))
+ Utf8Strn0Cpy(file, Event->Title(), sizeof(file));
+ SetEvent(Event);
++ cStatus::MsgTimerCreation(this, Event); // PIN PATCH
+ }
+
+ cTimer::cTimer(const cTimer &Timer)
+@@ -149,6 +152,7 @@
+ stop = Timer.stop;
+ priority = Timer.priority;
+ lifetime = Timer.lifetime;
++ fskProtection = Timer.fskProtection; // PIN PATCH
+ strncpy(file, Timer.file, sizeof(file));
+ free(aux);
+ aux = Timer.aux ? strdup(Timer.aux) : NULL;
+@@ -331,6 +335,7 @@
+ result = false;
+ }
+ }
++ fskProtection = aux && strstr(aux, "<pin-plugin><protected>yes</protected></pin-plugin>"); // PIN PATCH
+ free(channelbuffer);
+ free(daybuffer);
+ free(filebuffer);
+@@ -689,6 +694,36 @@
+ Matches(); // refresh start and end time
+ }
+
++void cTimer::SetFskProtection(int aFlag) // PIN PATCH
++{
++ char* p;
++ char* tmp = 0;
++ int res = 0;
++
++ fskProtection = aFlag;
++
++ if (fskProtection && (!aux || !strstr(aux, "<pin-plugin><protected>yes</protected></pin-plugin>")))
++ {
++ // add protection info to aux
++
++ if (aux) { tmp = strdup(aux); free(aux); }
++ res = asprintf(&aux, "%s<pin-plugin><protected>yes</protected></pin-plugin>", tmp ? tmp : "");
++ }
++ else if (!fskProtection && aux && (p = strstr(aux, "<pin-plugin><protected>yes</protected></pin-plugin>")))
++ {
++ // remove protection info from aux
++
++ res = asprintf(&tmp, "%.*s%s", (int)(p-aux), aux, p+strlen("<pin-plugin><protected>yes</protected></pin-plugin>"));
++ free(aux);
++ aux = strdup(tmp);
++ }
++
++ if (res < 0) ; // memory problems :o
++
++ if (tmp)
++ free(tmp);
++}
++
+ // --- cTimers ---------------------------------------------------------------
+
+ cTimers Timers;
+--- ../vdr-2.0.2/timers.h 2013-03-11 11:35:53.000000000 +0100
++++ timers.h 2013-05-22 10:17:56.762196352 +0200
+@@ -38,6 +38,7 @@
+ int start;
+ int stop;
+ int priority;
++ int fskProtection; // PIN PATCH
+ int lifetime;
+ mutable char file[NAME_MAX * 2 + 1]; // *2 to be able to hold 'title' and 'episode', which can each be up to 255 characters long
+ char *aux;
+@@ -59,6 +60,7 @@
+ int Start(void) const { return start; }
+ int Stop(void) const { return stop; }
+ int Priority(void) const { return priority; }
++ int FskProtection(void) const { return fskProtection; } // PIN PATCH
+ int Lifetime(void) const { return lifetime; }
+ const char *File(void) const { return file; }
+ time_t FirstDay(void) const { return weekdays ? day : 0; }
+@@ -95,6 +97,7 @@
+ void SetAux(const char *Aux);
+ void SetDeferred(int Seconds);
+ void SetFlags(uint Flags);
++ void SetFskProtection(int aFlag); // PIN PATCH
+ void ClrFlags(uint Flags);
+ void InvFlags(uint Flags);
+ bool HasFlags(uint Flags) const;
+--- ../vdr-2.0.2/vdr.c 2013-03-15 11:44:54.000000000 +0100
++++ vdr.c 2013-05-22 10:17:56.762196352 +0200
+@@ -65,6 +65,7 @@
+ #include "tools.h"
+ #include "transfer.h"
+ #include "videodir.h"
++#include "status.h" // PIN PATCH
+
+ #define MINCHANNELWAIT 10 // seconds to wait between failed channel switchings
+ #define ACTIVITYTIMEOUT 60 // seconds before starting housekeeping
+@@ -1015,6 +1016,7 @@
+ cOsdObject *Interact = Menu ? Menu : cControl::Control();
+ eKeys key = Interface->GetKey(!Interact || !Interact->NeedsFastResponse());
+ if (ISREALKEY(key)) {
++ cStatus::MsgUserAction(key, Interact); // PIN PATCH
+ EITScanner.Activity();
+ // Cancel shutdown countdown:
+ if (ShutdownHandler.countdown)
+@@ -1087,10 +1089,12 @@
+ cControl::Control()->Hide();
+ cPlugin *plugin = cPluginManager::GetPlugin(PluginName);
+ if (plugin) {
++ if (!cStatus::MsgPluginProtected(plugin)) { // PIN PATCH
+ Menu = plugin->MainMenuAction();
+ if (Menu)
+ Menu->Show();
+ }
++ }
+ else
+ esyslog("ERROR: unknown plugin '%s'", PluginName);
+ }
+@@ -1303,9 +1307,11 @@
+ // Instant resume of the last viewed recording:
+ case kPlay:
+ if (cReplayControl::LastReplayed()) {
++ if (cStatus::MsgReplayProtected(0, cReplayControl::LastReplayed(), 0, false) == false) { // PIN PATCH
+ cControl::Shutdown();
+ cControl::Launch(new cReplayControl);
+ }
++ }
+ else
+ DirectMainFunction(osRecordings); // no last viewed recording, so enter the Recordings menu
+ break;
diff --git a/patches/vdr-2.3.1.diff b/patches/vdr-2.3.1.diff
new file mode 100644
index 0000000..4ec4be9
--- /dev/null
+++ b/patches/vdr-2.3.1.diff
@@ -0,0 +1,432 @@
+--- ../vdr-2.3.1.plain//device.c 2015-09-05 13:42:17.000000000 +0200
++++ device.c 2015-10-09 16:43:27.982132231 +0200
+@@ -726,6 +726,7 @@
+ const cChannel *Channel;
+ while ((Channel = Channels->GetByNumber(n, Direction)) != NULL) {
+ // try only channels which are currently available
++ if (!cStatus::MsgChannelProtected(0, Channel)) // PIN PATCH
+ if (GetDevice(Channel, LIVEPRIORITY, true, true))
+ break;
+ n = Channel->Number() + Direction;
+@@ -747,6 +748,12 @@
+
+ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
+ {
++ // I hope 'LiveView = false' indicates a channel switch for recording, // PIN PATCH
++ // I really don't know, but it works ... // PIN PATCH
++
++ if (LiveView && cStatus::MsgChannelProtected(this, Channel)) // PIN PATCH
++ return scrNotAvailable; // PIN PATCH
++
+ cStatus::MsgChannelSwitch(this, 0, LiveView);
+
+ if (LiveView) {
+--- ../vdr-2.3.1.plain//Makefile 2015-02-09 13:28:24.000000000 +0100
++++ Makefile 2015-10-12 08:49:55.734546896 +0200
+@@ -324,7 +324,7 @@
+ clean:
+ @$(MAKE) --no-print-directory -C $(LSIDIR) clean
+ @-rm -f $(OBJS) $(DEPFILE) vdr vdr.pc core* *~
+- @-rm -rf $(LOCALEDIR) $(PODIR)/*.mo $(PODIR)/*.pot
++ @-rm -rf $(LOCALEDIR) $(PODIR)/*~ $(PODIR)/*.mo $(PODIR)/*.pot
+ @-rm -rf include
+ @-rm -rf srcdoc
+ CLEAN: clean
+--- ../vdr-2.3.1.plain//menu.c 2015-09-14 15:22:49.000000000 +0200
++++ menu.c 2015-10-09 16:26:16.481017293 +0200
+@@ -997,6 +997,18 @@
+ Add(new cMenuEditBitItem( tr("VPS"), &data.flags, tfVps));
+ Add(new cMenuEditIntItem( tr("Priority"), &data.priority, 0, MAXPRIORITY));
+ Add(new cMenuEditIntItem( tr("Lifetime"), &data.lifetime, 0, MAXLIFETIME));
++
++ // PIN PATCH
++ if (cOsd::pinValid || !data.fskProtection) Add(new cMenuEditBoolItem(tr("Childlock"),&data.fskProtection));
++ else {
++ char* buf = 0;
++ int res = 0;
++ res = asprintf(&buf, "%s\t%s", tr("Childlock"), data.fskProtection ? tr("yes") : tr("no"));
++ if (res < 0) ; // memory problems :o
++ Add(new cOsdItem(buf));
++ free(buf);
++ }
++
+ Add(file = new cMenuEditStrItem( tr("File"), data.file, sizeof(data.file)));
+ SetFirstDayItem();
+ if (data.remote)
+@@ -2973,7 +2985,8 @@
+ }
+ }
+ }
+- if (*Item->Text() && !LastDir) {
++ if (*Item->Text() && !LastDir
++ && (!cStatus::MsgReplayProtected(Item->Recording(), Item->Name(), base, Item->IsDirectory(), true))) { // PIN PATCH
+ Add(Item);
+ LastItem = Item;
+ if (Item->IsDirectory())
+@@ -3041,6 +3054,9 @@
+ {
+ cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current());
+ if (ri) {
++ if (cStatus::MsgReplayProtected(ri->Recording(), ri->Name(), base,
++ ri->IsDirectory()) == true) // PIN PATCH
++ return osContinue; // PIN PATCH
+ if (ri->IsDirectory())
+ Open();
+ else {
+@@ -4282,28 +4298,32 @@
+
+ // Basic menu items:
+
+- Add(new cOsdItem(hk(tr("Schedule")), osSchedule));
+- Add(new cOsdItem(hk(tr("Channels")), osChannels));
+- Add(new cOsdItem(hk(tr("Timers")), osTimers));
+- Add(new cOsdItem(hk(tr("Recordings")), osRecordings));
++ // PIN PATCH
++ if (!cStatus::MsgMenuItemProtected("Schedule", true)) Add(new cOsdItem(hk(tr("Schedule")), osSchedule));
++ if (!cStatus::MsgMenuItemProtected("Channels", true)) Add(new cOsdItem(hk(tr("Channels")), osChannels));
++ if (!cStatus::MsgMenuItemProtected("Timers", true)) Add(new cOsdItem(hk(tr("Timers")), osTimers));
++ if (!cStatus::MsgMenuItemProtected("Recordings", true)) Add(new cOsdItem(hk(tr("Recordings")), osRecordings));
+
+ // Plugins:
+
+ for (int i = 0; ; i++) {
+ cPlugin *p = cPluginManager::GetPlugin(i);
+ if (p) {
++ if (!cStatus::MsgPluginProtected(p, true)) { // PIN PATCH
+ const char *item = p->MainMenuEntry();
+ if (item)
+ Add(new cMenuPluginItem(hk(item), i));
+ }
++ }
+ else
+ break;
+ }
+
+ // More basic menu items:
+
+- Add(new cOsdItem(hk(tr("Setup")), osSetup));
++ if (!cStatus::MsgMenuItemProtected("Setup", true)) Add(new cOsdItem(hk(tr("Setup")), osSetup)); // PIN PATCH
+ if (Commands.Count())
++ if (!cStatus::MsgMenuItemProtected("Commands", true)) // PIN PATCH
+ Add(new cOsdItem(hk(tr("Commands")), osCommands));
+
+ Update(true);
+@@ -4372,6 +4392,14 @@
+ eOSState state = cOsdMenu::ProcessKey(Key);
+ HadSubMenu |= HasSubMenu();
+
++ // > PIN PATCH
++ cOsdItem* item = Get(Current());
++
++ if (item && item->Text() && state != osContinue && state != osUnknown && state != osBack)
++ if (cStatus::MsgMenuItemProtected(item->Text()))
++ return osContinue;
++ // PIN PATCH <
++
+ switch (state) {
+ case osSchedule: return AddSubMenu(new cMenuSchedule);
+ case osChannels: return AddSubMenu(new cMenuChannels);
+@@ -4396,6 +4424,7 @@
+ if (item) {
+ cPlugin *p = cPluginManager::GetPlugin(item->PluginIndex());
+ if (p) {
++ if (!cStatus::MsgPluginProtected(p)) { // PIN PATCH
+ cOsdObject *menu = p->MainMenuAction();
+ if (menu) {
+ if (menu->IsMenu())
+@@ -4407,6 +4436,7 @@
+ }
+ }
+ }
++ }
+ state = osEnd;
+ }
+ break;
+@@ -4576,6 +4606,7 @@
+ Channel = Direction > 0 ? Channels->Next(Channel) : Channels->Prev(Channel);
+ if (!Channel && Setup.ChannelsWrap)
+ Channel = Direction > 0 ? Channels->First() : Channels->Last();
++ if (!cStatus::MsgChannelProtected(0, Channel)) // PIN PATCH
+ if (Channel && !Channel->GroupSep() && cDevice::GetDevice(Channel, LIVEPRIORITY, true, true))
+ return Channel;
+ }
+@@ -5226,6 +5257,7 @@
+ for (int i = 0; i < MAXRECORDCONTROLS; i++) {
+ if (!RecordControls[i]) {
+ RecordControls[i] = new cRecordControl(device, Timers, Timer, Pause);
++ cStatus::MsgRecordingFile(RecordControls[i]->FileName()); // PIN PATCH
+ return RecordControls[i]->Process(time(NULL));
+ }
+ }
+--- ../vdr-2.3.1.plain//osd.c 2015-09-10 16:12:06.000000000 +0200
++++ osd.c 2015-10-09 16:10:13.400241634 +0200
+@@ -1644,6 +1644,7 @@
+ cSize cOsd::maxPixmapSize(2048, 2048);
+ cVector<cOsd *> cOsd::Osds;
+ cMutex cOsd::mutex;
++bool cOsd::pinValid = false; // PIN PATCH
+
+ cOsd::cOsd(int Left, int Top, uint Level)
+ {
+--- ../vdr-2.3.1.plain//osd.h 2015-04-19 14:18:25.000000000 +0200
++++ osd.h 2015-10-09 16:10:13.400241634 +0200
+@@ -952,6 +952,7 @@
+ ///<
+ ///< If a plugin uses a derived cPixmap implementation, it needs to use that
+ ///< type instead of cPixmapMemory.
++ static bool pinValid; // PIN PATCH
+ };
+
+ #define MAXOSDIMAGES 64
+--- ../vdr-2.3.1.plain//status.c 2014-01-25 11:47:39.000000000 +0100
++++ status.c 2015-10-12 10:41:28.695735498 +0200
+@@ -130,3 +130,55 @@
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+ sm->OsdProgramme(PresentTime, PresentTitle, PresentSubtitle, FollowingTime, FollowingTitle, FollowingSubtitle);
+ }
++
++bool cStatus::MsgChannelProtected(const cDevice* Device, const cChannel* Channel) // PIN PATCH
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ if (sm->ChannelProtected(Device, Channel) == true)
++ return true;
++
++ return false;
++}
++
++bool cStatus::MsgReplayProtected(const cRecording* Recording, const char* Name,
++ const char* Base, bool isDirectory, int menuView) // PIN PATCH
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ if (sm->ReplayProtected(Recording, Name, Base, isDirectory, menuView) == true)
++ return true;
++ return false;
++}
++
++void cStatus::MsgRecordingFile(const char* FileName)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm)) // PIN PATCH
++ sm->RecordingFile(FileName);
++}
++
++void cStatus::MsgTimerCreation(cTimer* Timer, const cEvent *Event)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm)) // PIN PATCH
++ sm->TimerCreation(Timer, Event);
++}
++
++bool cStatus::MsgPluginProtected(cPlugin* Plugin, int menuView) // PIN PATCH
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ if (sm->PluginProtected(Plugin, menuView) == true)
++ return true;
++ return false;
++}
++
++void cStatus::MsgUserAction(const eKeys key) // PIN PATCH
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ sm->UserAction(key);
++}
++
++bool cStatus::MsgMenuItemProtected(const char* Name, int menuView) // PIN PATCH
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ if (sm->MenuItemProtected(Name, menuView) == true)
++ return true;
++ return false;
++}
+--- ../vdr-2.3.1.plain//status.h 2015-08-02 12:34:44.000000000 +0200
++++ status.h 2015-10-12 10:41:15.287669011 +0200
+@@ -14,6 +14,7 @@
+ #include "device.h"
+ #include "player.h"
+ #include "tools.h"
++#include "plugin.h"
+
+ enum eTimerChange { tcMod, tcAdd, tcDel }; // tcMod is obsolete and no longer used!
+
+@@ -81,6 +82,23 @@
+ // The OSD displays the single line Text with the current channel information.
+ virtual void OsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle) {}
+ // The OSD displays the given programme information.
++ virtual bool ChannelProtected(const cDevice *Device, const cChannel* Channel) { return false; } // PIN PATCH
++ // Checks if a channel is protected.
++ virtual bool ReplayProtected(const cRecording* Recording, const char* Name,
++ const char* Base, bool isDirectory, int menuView = false) { return false; } // PIN PATCH
++ // Checks if a recording is protected.
++ virtual void RecordingFile(const char* FileName) {} // PIN PATCH
++ // The given DVB device has started recording to FileName. FileName is the name of the
++ // recording directory
++ virtual void TimerCreation(cTimer* Timer, const cEvent *Event) {} // PIN PATCH
++ // The given timer is created
++ virtual bool PluginProtected(cPlugin* Plugin, int menuView = false) { return false; } // PIN PATCH
++ // Checks if a plugin is protected.
++ virtual void UserAction(const eKeys key) {} // PIN PATCH
++ // report user action
++ virtual bool MenuItemProtected(const char* Name, int menuView = false) { return false; } // PIN PATCH
++ // Checks if a menu entry is protected.
++
+ public:
+ cStatus(void);
+ virtual ~cStatus();
+@@ -103,6 +121,14 @@
+ static void MsgOsdTextItem(const char *Text, bool Scroll = false);
+ static void MsgOsdChannel(const char *Text);
+ static void MsgOsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle);
++ static bool MsgChannelProtected(const cDevice* Device, const cChannel* Channel); // PIN PATCH
++ static bool MsgReplayProtected(const cRecording* Recording, const char* Name,
++ const char* Base, bool isDirectory, int menuView = false); // PIN PATCH
++ static void MsgRecordingFile(const char* FileName); // PIN PATCH
++ static void MsgTimerCreation(cTimer* Timer, const cEvent *Event); // PIN PATCH
++ static bool MsgPluginProtected(cPlugin* Plugin, int menuView = false); // PIN PATCH
++ static void MsgUserAction(const eKeys key); // PIN PATCH
++ static bool MsgMenuItemProtected(const char* Name, int menuView = false); // PIN PATCH
+ };
+
+ #endif //__STATUS_H
+--- ../vdr-2.3.1.plain//timers.c 2015-09-13 15:10:24.000000000 +0200
++++ timers.c 2015-10-09 16:10:13.404241653 +0200
+@@ -77,6 +77,7 @@
+ stop -= 2400;
+ priority = Pause ? Setup.PausePriority : Setup.DefaultPriority;
+ lifetime = Pause ? Setup.PauseLifetime : Setup.DefaultLifetime;
++ fskProtection = 0; // PIN PATCH
+ if (Instant && channel)
+ snprintf(file, sizeof(file), "%s%s", Setup.MarkInstantRecord ? "@" : "", *Setup.NameInstantRecord ? Setup.NameInstantRecord : channel->Name());
+ }
+@@ -114,10 +115,12 @@
+ stop -= 2400;
+ priority = Setup.DefaultPriority;
+ lifetime = Setup.DefaultLifetime;
++ fskProtection = 0; // PIN PATCH
+ const char *Title = Event->Title();
+ if (!isempty(Title))
+ Utf8Strn0Cpy(file, Event->Title(), sizeof(file));
+ SetEvent(Event);
++ cStatus::MsgTimerCreation(this, Event); // PIN PATCH
+ }
+
+ cTimer::cTimer(const cTimer &Timer)
+@@ -156,6 +159,7 @@
+ stop = Timer.stop;
+ priority = Timer.priority;
+ lifetime = Timer.lifetime;
++ fskProtection = Timer.fskProtection; // PIN PATCH
+ strncpy(file, Timer.file, sizeof(file));
+ free(aux);
+ aux = Timer.aux ? strdup(Timer.aux) : NULL;
+@@ -344,6 +348,7 @@
+ result = false;
+ }
+ }
++ fskProtection = aux && strstr(aux, "<pin-plugin><protected>yes</protected></pin-plugin>"); // PIN PATCH
+ free(channelbuffer);
+ free(daybuffer);
+ free(filebuffer);
+@@ -708,6 +713,36 @@
+ Matches(); // refresh start and end time
+ }
+
++void cTimer::SetFskProtection(int aFlag) // PIN PATCH
++{
++ char* p;
++ char* tmp = 0;
++ int res = 0;
++
++ fskProtection = aFlag;
++
++ if (fskProtection && (!aux || !strstr(aux, "<pin-plugin><protected>yes</protected></pin-plugin>")))
++ {
++ // add protection info to aux
++
++ if (aux) { tmp = strdup(aux); free(aux); }
++ res = asprintf(&aux, "%s<pin-plugin><protected>yes</protected></pin-plugin>", tmp ? tmp : "");
++ }
++ else if (!fskProtection && aux && (p = strstr(aux, "<pin-plugin><protected>yes</protected></pin-plugin>")))
++ {
++ // remove protection info from aux
++
++ res = asprintf(&tmp, "%.*s%s", (int)(p-aux), aux, p+strlen("<pin-plugin><protected>yes</protected></pin-plugin>"));
++ free(aux);
++ aux = strdup(tmp);
++ }
++
++ if (res < 0) ; // memory problems :o
++
++ if (tmp)
++ free(tmp);
++}
++
+ // --- cTimers ---------------------------------------------------------------
+
+ cTimers cTimers::timers;
+--- ../vdr-2.3.1.plain//timers.h 2015-09-09 12:40:24.000000000 +0200
++++ timers.h 2015-10-09 16:10:13.404241653 +0200
+@@ -39,6 +39,7 @@
+ int start;
+ int stop;
+ int priority;
++ int fskProtection; // PIN PATCH
+ int lifetime;
+ mutable char file[NAME_MAX * 2 + 1]; // *2 to be able to hold 'title' and 'episode', which can each be up to 255 characters long
+ char *aux;
+@@ -62,6 +63,7 @@
+ int Start(void) const { return start; }
+ int Stop(void) const { return stop; }
+ int Priority(void) const { return priority; }
++ int FskProtection(void) const { return fskProtection; } // PIN PATCH
+ int Lifetime(void) const { return lifetime; }
+ const char *File(void) const { return file; }
+ time_t FirstDay(void) const { return weekdays ? day : 0; }
+@@ -102,6 +104,7 @@
+ void SetRemote(const char *Remote);
+ void SetDeferred(int Seconds);
+ void SetFlags(uint Flags);
++ void SetFskProtection(int aFlag); // PIN PATCH
+ void ClrFlags(uint Flags);
+ void InvFlags(uint Flags);
+ bool HasFlags(uint Flags) const;
+--- ../vdr-2.3.1.plain//vdr.c 2015-09-11 10:02:50.000000000 +0200
++++ vdr.c 2015-10-12 10:40:33.159460108 +0200
+@@ -71,6 +71,7 @@
+ #include "tools.h"
+ #include "transfer.h"
+ #include "videodir.h"
++#include "status.h" // PIN PATCH
+
+ #define MINCHANNELWAIT 10 // seconds to wait between failed channel switchings
+ #define ACTIVITYTIMEOUT 60 // seconds before starting housekeeping
+@@ -1153,6 +1154,7 @@
+ cOsdObject *Interact = Menu ? Menu : cControl::Control();
+ eKeys key = Interface->GetKey(!Interact || !Interact->NeedsFastResponse());
+ if (ISREALKEY(key)) {
++ cStatus::MsgUserAction(key); // PIN PATCH
+ EITScanner.Activity();
+ // Cancel shutdown countdown:
+ if (ShutdownHandler.countdown)
+@@ -1225,10 +1227,12 @@
+ cControl::Control()->Hide();
+ cPlugin *plugin = cPluginManager::GetPlugin(PluginName);
+ if (plugin) {
++ if (!cStatus::MsgPluginProtected(plugin)) { // PIN PATCH
+ Menu = plugin->MainMenuAction();
+ if (Menu)
+ Menu->Show();
+ }
++ }
+ else
+ esyslog("ERROR: unknown plugin '%s'", PluginName);
+ }
+@@ -1446,9 +1450,11 @@
+ // Instant resume of the last viewed recording:
+ case kPlay:
+ if (cReplayControl::LastReplayed()) {
++ if (cStatus::MsgReplayProtected(0, cReplayControl::LastReplayed(), 0, false) == false) { // PIN PATCH
+ cControl::Shutdown();
+ cControl::Launch(new cReplayControl);
+ }
++ }
+ else
+ DirectMainFunction(osRecordings); // no last viewed recording, so enter the Recordings menu
+ break;
diff --git a/pin.c b/pin.c
new file mode 100644
index 0000000..6a8ac9f
--- /dev/null
+++ b/pin.c
@@ -0,0 +1,882 @@
+/*
+ * pin.c: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * Date: 11.04.05 - 23.11.06
+ */
+
+//***************************************************************************
+// Includes
+//***************************************************************************
+
+#include <vdr/interface.h>
+#include <vdr/videodir.h>
+#include <vdr/menu.h>
+
+#include "pin.h"
+#include "menu.h"
+#include "setupmenu.h"
+
+//***************************************************************************
+// Pin Service
+//***************************************************************************
+
+void Translations::append(const char* in, const char* out)
+{
+ if (!in)
+ return ;
+
+ Translation* t = new Translation;
+
+ Add(t);
+
+ strncpy(t->in, in, 200);
+ t->in[200] = 0;
+ strncpy(t->out, out ? out : "", 200);
+ t->out[200] = 0;
+}
+
+const char* Translations::lookup(const char* in)
+{
+ for (Translation* p = First(); p; p = Next(p))
+ if (strcmp(p->in, in) == 0)
+ return p->out;
+
+ return 0;
+}
+
+//***************************************************************************
+// Pin
+//***************************************************************************
+//***************************************************************************
+// Object
+//***************************************************************************
+
+cPin::cPin(void)
+{
+ pinSetup = *cPinPlugin::pinCode ? no : yes; // code already configured
+
+ if (pinSetup)
+ pinTxt = tr("Setup your pin code: ");
+ else
+ pinTxt = tr("Pin Code: ");
+
+ tell(eloDebug, "Debug: pin is '%s', text = '%s'", cPinPlugin::pinCode, pinTxt);
+
+ display = 0;
+ clear();
+}
+
+cPin::~cPin()
+{
+ if (display) delete display;
+}
+
+//***************************************************************************
+// Clear
+//***************************************************************************
+
+void cPin::clear()
+{
+ *code = 0;
+ cOsd::pinValid = no;
+}
+
+//***************************************************************************
+// Show
+//***************************************************************************
+
+void cPin::Show(void)
+{
+ display = Skins.Current()->DisplayMessage();
+ display->SetMessage(mtInfo, pinTxt);
+ cStatus::MsgOsdStatusMessage(pinTxt);
+ display->Flush();
+}
+
+//***************************************************************************
+// Process Key
+//***************************************************************************
+
+eOSState cPin::ProcessKey(eKeys Key)
+{
+ char txt[50+TB];
+ int len = strlen(code);
+ eOSState state = cOsdObject::ProcessKey(Key);
+
+ if (state == osUnknown)
+ {
+ switch (Key)
+ {
+ case kBack:
+ {
+ return osEnd;
+ }
+
+ case kLeft:
+ {
+ if (len <= 0)
+ break;
+
+ len--;
+ code[len] = 0;
+ break;
+ }
+
+ case k0 ... k9:
+ {
+ if (len >= 5) // max 5 digits
+ break;
+
+ code[len++] = Key-k0+48;
+ code[len] = 0;
+ break;
+ }
+
+ case kOk:
+ {
+ cOsd::pinValid = no;
+
+ if (!pinSetup || (pinSetup && *code))
+ {
+ if (pinSetup)
+ strcpy(cPinPlugin::pinCode, code); // setup pin code
+
+ if (strcmp(code, cPinPlugin::pinCode) == 0)
+ cOsd::pinValid = yes;
+ }
+
+ state = osEnd;
+ break;
+ }
+
+ default: return state;
+ }
+
+ if (state == osEnd)
+ {
+ if (pinSetup && cOsd::pinValid)
+ {
+ (cPinPlugin::getObject())->StorePin();
+ display->SetMessage(mtInfo, tr("Pin code successfully setup"));
+ cStatus::MsgOsdStatusMessage(tr("Pin code successfully setup"));
+ display->Flush();
+ sleep(2);
+ }
+
+ if (cOsd::pinValid && cPinPlugin::autoMenuOpen)
+ {
+ cRemote::CallPlugin("pin");
+ }
+ else
+ {
+ display->SetMessage(cOsd::pinValid ? mtInfo : mtWarning,
+ cOsd::pinValid ? tr("Code accepted") : tr("Invalid Code !"));
+ cStatus::MsgOsdStatusMessage(cOsd::pinValid ? tr("Code accepted") : tr("Invalid Code !"));
+ display->Flush();
+ sleep(1);
+ }
+ }
+ else
+ {
+ int i;
+
+ strcpy(txt, pinTxt);
+
+ for (i = 0; i < len; i++)
+ txt[strlen(pinTxt)+i] = '*';
+
+ txt[strlen(pinTxt)+i] = 0;
+
+ display->SetMessage(mtInfo, txt);
+ cStatus::MsgOsdStatusMessage(txt);
+ display->Flush();
+ state = osContinue;
+ }
+ }
+
+ return state;
+}
+
+//***************************************************************************
+// PIN Plugin
+//***************************************************************************
+//***************************************************************************
+// Static Stuff
+//***************************************************************************
+
+char cPinPlugin::pinCode[] = "";
+cPinPlugin* cPinPlugin::object = 0;
+bool cPinPlugin::skipChannelSilent = no;
+bool cPinPlugin::hideProtectedMenus = no;
+bool cPinPlugin::hideProtectedPlugins = no;
+bool cPinPlugin::hideProtectedRecordings = no;
+int cPinPlugin::pinResetTime = 90 /* minutes */; // default 1,5 hour
+bool cPinPlugin::autoMenuOpen = yes;
+int cPinPlugin::autoProtectionMode = apmIntelligent;
+
+const char* cPinPlugin::autoProtectionModes[] =
+{
+ "always",
+ "intelligent",
+ "never",
+
+ 0
+};
+
+//***************************************************************************
+// Object
+//***************************************************************************
+
+cPinPlugin::cPinPlugin(void)
+ : lockedChannels(ltChannels),
+ lockedBroadcasts(ltBroadcasts),
+ lockedPlugins(ltPlugins),
+ lockedMenuItems(ltMenuItems)
+{
+ statusMonitor = 0;
+ lastAction = time(0);
+ receiver = 0;
+ cOsd::pinValid = no; // initial sperren
+}
+
+cPinPlugin::~cPinPlugin()
+{
+ if (receiver) delete receiver;
+ if (statusMonitor) delete statusMonitor;
+}
+
+//***************************************************************************
+// Store Pin Code
+//***************************************************************************
+
+void cPinPlugin::StorePin()
+{
+ SetupStore("pinCode", cPinPlugin::pinCode);
+}
+
+//***************************************************************************
+// Initialize
+//***************************************************************************
+
+bool cPinPlugin::Initialize(void)
+{
+ char* path;
+ int res;
+
+ res = asprintf(&path, "%s/%s", cPlugin::ConfigDirectory(), "pin");
+
+ if (res < 0) ; // memory problems :o
+
+ if (!DirectoryOk(path, no))
+ {
+ if (mkdir(path, 0770))
+ {
+ // cannot create the directory
+
+ tell(eloAlways, "Creating of directory '%s' failed, errno was (%d) - '%s'",
+ path, errno, strerror(errno));
+ free(path);
+
+ return true; // anyhow let vdr start
+ }
+ }
+
+ free(path);
+
+ lockedChannels.Load(AddDirectory(cPlugin::ConfigDirectory(),
+ "pin/channellocks.conf"), true, false);
+ lockedBroadcasts.Load(AddDirectory(cPlugin::ConfigDirectory(),
+ "pin/broadcastlocks.conf"), true, false);
+ lockedPlugins.Load(AddDirectory(cPlugin::ConfigDirectory(),
+ "pin/pluginlocks.conf"), true, false);
+ lockedMenuItems.Load(AddDirectory(cPlugin::ConfigDirectory(),
+ "pin/menulocks.conf"), true, false);
+
+ return true;
+}
+
+//***************************************************************************
+// Main Menu Action
+//***************************************************************************
+
+cOsdObject* cPinPlugin::MainMenuAction(void)
+{
+ initPluginList();
+
+ if (cOsd::pinValid)
+ return new cPinMenu(MAINMENUENTRY, &lockedChannels,
+ &lockedBroadcasts, &lockedPlugins,
+ &lockedMenuItems);
+
+ return new cPin();
+}
+
+//***************************************************************************
+// Start
+//***************************************************************************
+
+bool cPinPlugin::Start(void)
+{
+ statusMonitor = new cPinStatusMonitor;
+ object = this;
+
+ receiver = new MessageReceiver;
+ receiver->StartReceiver();
+
+ return true;
+}
+
+//***************************************************************************
+// Stop
+//***************************************************************************
+
+void cPinPlugin::Stop(void)
+{
+ receiver->StopReceiver();
+}
+
+//***************************************************************************
+// Setup Menu
+//***************************************************************************
+
+cMenuSetupPage* cPinPlugin::SetupMenu(void)
+{
+ if (cOsd::pinValid)
+ return new PinSetupMenu;
+
+ Skins.Message(mtInfo, tr("Please enter pin code first"));
+
+ return 0;
+}
+
+//***************************************************************************
+// Setup Parse
+//***************************************************************************
+
+bool cPinPlugin::SetupParse(const char *Name, const char *Value)
+{
+ // Pase Setup Parameters
+
+ if (!strcasecmp(Name, "pinCode"))
+ strcpy(pinCode, Value);
+ else if (!strcasecmp(Name, "skipChannelSilent"))
+ skipChannelSilent = atoi(Value);
+ else if (!strcasecmp(Name, "hideProtectedMenus"))
+ hideProtectedMenus = atoi(Value);
+ else if (!strcasecmp(Name, "hideProtectedPlugins"))
+ hideProtectedPlugins = atoi(Value);
+ else if (!strcasecmp(Name, "hideProtectedRecordings"))
+ hideProtectedRecordings = atoi(Value);
+ else if (!strcasecmp(Name, "pinResetTime"))
+ pinResetTime = atoi(Value);
+ else if (!strcasecmp(Name, "autoMenuOpen"))
+ autoMenuOpen = atoi(Value);
+ else if (!strcasecmp(Name, "autoProtectionMode"))
+ autoProtectionMode = atoi(Value);
+ else
+ return false;
+
+ return true;
+}
+
+//***************************************************************************
+// Test if Channel Protected
+//***************************************************************************
+
+int cPinPlugin::channelProtected(const char* name, long startTime)
+{
+ return lockedChannels.Locked(name, startTime);
+}
+
+//***************************************************************************
+// Test if Plugin Protected
+//***************************************************************************
+
+int cPinPlugin::pluginProtected(const char* name)
+{
+ return lockedPlugins.Locked(name);
+}
+
+//***************************************************************************
+// Test if Menu Item Protected
+//***************************************************************************
+
+int cPinPlugin::menuItemProtected(const char* text)
+{
+ return lockedMenuItems.Locked(text);
+}
+
+//***************************************************************************
+// Append Menu Item
+//***************************************************************************
+
+int cPinPlugin::menuItemAppend(const char* text)
+{
+ cLockItem* item;
+
+ if (!lockedMenuItems.FindByName(text))
+ {
+ lockedMenuItems.Add(item = new cLockItem(text));
+ item->SetActive(no);
+ }
+
+ return success;
+}
+
+//***************************************************************************
+// Test if Broadcast Protected
+//***************************************************************************
+
+int cPinPlugin::broadcastProtected(const char* title)
+{
+ return lockedBroadcasts.Locked(title);
+}
+
+//***************************************************************************
+// Check Action Time
+//***************************************************************************
+
+void cPinPlugin::checkActivity()
+{
+ time_t lNow = time(0);
+
+ if (pinResetTime > 0 && lNow > lastAction+(pinResetTime*60) && cOsd::pinValid)
+ {
+ // keine User Aktion, Kindersicherung aktivieren ..
+
+ tell(eloAlways, "Pin: Locking pin due to no user inactivity of (%ld) seconds",
+ lNow-lastAction);
+
+ cOsd::pinValid = no;
+ }
+
+ lastAction = lNow;
+}
+
+//***************************************************************************
+// Init Plugin List
+//***************************************************************************
+
+int cPinPlugin::initPluginList()
+{
+ static int initialized = no;
+
+ cPlugin* plugin;
+ int i = 0;
+ int cnt = 0;
+ cLockItem* item;
+
+ if (initialized)
+ return done;
+
+ initialized = yes;
+
+#ifdef __EXCL_PLUGINS
+ tell(eloAlways, "exclude plugins '%s' from pin protection", __EXCL_PLUGINS);
+#endif
+
+ while ((plugin = cPluginManager::GetPlugin(i)))
+ {
+ if (strcasecmp(plugin->Name(), "pin") != 0
+#ifdef __EXCL_PLUGINS
+ && !strstr(__EXCL_PLUGINS, plugin->Name())
+#endif
+ )
+ {
+ // nicht das pin plugin und keine Plugs aus der Exclude Liste
+
+ if (!(item = lockedPlugins.FindByName(plugin->Name())))
+ {
+ lockedPlugins.Add(new cLockItem(plugin->Name(), no,
+ plugin->MainMenuEntry()));
+
+ tell(eloAlways, "PIN added plugin (%d) '%s' - '%s'",
+ i, plugin->Name(), plugin->MainMenuEntry());
+
+ cnt++;
+ }
+ else
+ {
+ item->SetTitle(plugin->MainMenuEntry());
+ }
+ }
+
+ i++;
+ };
+
+ if (cnt)
+ lockedPlugins.Save();
+
+ return done;
+}
+
+//***************************************************************************
+// Channel Switch
+//***************************************************************************
+
+void cPinStatusMonitor::ChannelSwitch(const cDevice *Device, int ChannelNumber, bool LiveView)
+{
+ // cChannel* channel = Channels.GetByNumber(ChannelNumber);
+}
+
+//***************************************************************************
+// User Action
+//***************************************************************************
+
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+void cPinStatusMonitor::UserAction(const eKeys key)
+#else
+void cPinStatusMonitor::UserAction(const eKeys key, const cOsdObject* Interact)
+#endif
+{
+ tell(eloDebug, "Pin: UserAction, key (%d)", key);
+
+ (cPinPlugin::getObject())->checkActivity();
+}
+
+//***************************************************************************
+// Replay Protected
+//***************************************************************************
+
+bool cPinStatusMonitor::ReplayProtected(const cRecording* Recording, const char* Name,
+ const char* Base, bool isDirectory, int menuView)
+{
+ bool fProtected;
+ struct stat ifo;
+ char* path = 0;
+ char* base = 0;
+ char* name = 0;
+ int res;
+
+ if (Base)
+ {
+ base = strdup(Base);
+ base = ExchangeChars(base, true);
+ }
+
+ if (Name)
+ {
+ name = strdup(Name);
+
+ if (isDirectory)
+ name = ExchangeChars(name, true);
+ }
+
+ tell(eloDebug, "checking protection of %s '%s%s%s'",
+ isDirectory ? "directory" : "recording",
+ isDirectory && base ? base : "",
+ isDirectory && base ? "/" : "",
+ isDirectory || !Recording ? name : Recording->Name());
+
+ if (cOsd::pinValid)
+ return false;
+
+ if (menuView && !cPinPlugin::hideProtectedRecordings)
+ return false;
+
+ if (isDirectory)
+ res = asprintf(&path, "%s%s%s/%s/%s",
+#if APIVERSNUM > 20103
+ cVideoDirectory::Name(),
+#else
+ VideoDirectory,
+#endif
+ base ? "/" : "",
+ base ? base : "",
+ name ? name : "",
+ PROTECTION_FILE);
+ else
+ res = asprintf(&path, "%s/%s",
+ Recording ? Recording->FileName() : name,
+ PROTECTION_FILE);
+
+
+ if (res < 0) ; // memory problems :o
+
+ tell(eloDebug, "checking rights, protection file is '%s'", path);
+
+ fProtected = stat(path, &ifo) == 0;
+
+ if (path) free(path);
+ if (base) free(base);
+ if (name) free(name);
+
+ if (fProtected)
+ {
+ if (!menuView)
+ Skins.Message(mtError, tr("Recording protected, enter pin code first"));
+
+ return true;
+ }
+
+ return false;
+}
+
+//***************************************************************************
+// Channel Protected
+//***************************************************************************
+
+bool cPinStatusMonitor::ChannelProtected(const cDevice *Device, const cChannel* Channel)
+{
+ char* buf;
+ const cEvent* event;
+
+ if (cOsd::pinValid)
+ return false;
+
+ if (!Channel || !Channel->Name() || Channel->GroupSep())
+ return false;
+
+ // first check channel protection
+
+ tell(eloDebug, "checking protection of channel '%s', '%s'",
+ Channel->Name(),
+ Channel->ShortName());
+
+ if ((cPinPlugin::getObject())->channelProtected(Channel->Name()))
+ {
+ int res;
+
+ tell(eloDebug, "channel %d '%s' is protected", Channel->Number(), Channel->Name());
+
+ if (cPinPlugin::skipChannelSilent)
+ return true;
+
+ res = asprintf(&buf, "%d %s - %s", Channel->Number(), Channel->Name(), tr("channel is protected"));
+ if (res < 0) ; // memory problems :o
+ Skins.Message(mtInfo, buf);
+
+ free(buf);
+
+ return true;
+ }
+
+ // get schedules lock
+
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+ LOCK_SCHEDULES_READ;
+
+ if (!(event = GetEventOf(Schedules, Channel)))
+ return false;
+#else
+ cSchedulesLock schedLock;
+ const cSchedules* schedules;
+
+ if (!(schedules = cSchedules::Schedules(schedLock)))
+ return false;
+
+ if (!(event = GetEventOf(schedules, Channel)))
+ return false;
+#endif
+
+ // second check broadcast protection
+
+ if ((cPinPlugin::getObject())->broadcastProtected(event->Title()))
+ {
+ int res;
+
+ if (cPinPlugin::skipChannelSilent)
+ return true;
+
+ tell(eloDebug, "broadcast '%s' is protected", event->Title());
+ res = asprintf(&buf, "%s - %s", event->Title(), tr("broadcast is protected"));
+ if (res < 0) ; // memory problems :o
+ Skins.Message(mtInfo, buf);
+
+ free(buf);
+
+ return true;
+ }
+
+ return false;
+}
+
+//***************************************************************************
+// Recording
+//***************************************************************************
+
+void cPinStatusMonitor::RecordingFile(const char* FileName)
+{
+ char* path = 0;
+ cRecordControl* rc;
+ FILE* f;
+
+ if (!FileName || !strlen(FileName))
+ return;
+
+ rc = cRecordControls::GetRecordControl(FileName);
+
+ tell(eloDebug, "checking if autoprotection for '%s' is needed", FileName);
+
+ if (rc && rc->Timer() && rc->Timer()->Channel())
+ {
+ if (rc->Timer()->FskProtection())
+ {
+ int res;
+ res = asprintf(&path, "%s/%s", FileName, PROTECTION_FILE);
+ if (res < 0) ; // memory problems :o
+
+ tell(eloDebug, "autoprotecting recording due to '%s'; channel '%s'; file '%s'",
+ rc->Timer()->FskProtection() ? "timer configuration" : "of protected channel",
+ rc->Timer()->Channel()->Name(), path);
+
+ f = fopen(path, "w");
+ if (f) fclose(f);
+
+ free(path);
+ }
+ }
+}
+
+//***************************************************************************
+// Get Event Of
+//***************************************************************************
+
+const cEvent* cPinStatusMonitor::GetEventOf(const cSchedules* schedules, const cChannel* Channel)
+{
+ const cSchedule* schedule;
+ const cEvent* event;
+
+ if (!(schedule = schedules->GetSchedule(Channel->GetChannelID())))
+ return 0;
+
+ if (!(event = schedule->GetPresentEvent()))
+ return 0;
+
+ return event;
+}
+
+//***************************************************************************
+// Timer Creation
+//***************************************************************************
+
+void cPinStatusMonitor::TimerCreation(cTimer* Timer, const cEvent* Event)
+{
+ int fsk = 0;
+
+ if (!Timer || !Event || !Timer->Channel())
+ return;
+
+ tell(eloDebug, "Timer creation, event '%s', protection mode (%d)",
+ Event->Title(), cPinPlugin::autoProtectionMode);
+
+ switch (cPinPlugin::autoProtectionMode)
+ {
+ case cPinPlugin::apmAlways: fsk = 1; break;
+ case cPinPlugin::apmNever: fsk = 0; break;
+
+ case cPinPlugin::apmIntelligent:
+ {
+ tell(eloDebug, "Checking protection for channel '%s' at (%ld))",
+ Timer->Channel()->Name(), Event->StartTime());
+
+ fsk = (cPinPlugin::getObject())->channelProtected(
+ Timer->Channel()->Name(), Event->StartTime())
+ || (cPinPlugin::getObject())->broadcastProtected(Event->Title());
+
+ break;
+ }
+ }
+
+ Timer->SetFskProtection(fsk);
+}
+
+//***************************************************************************
+// Plugin Protected
+//***************************************************************************
+
+bool cPinStatusMonitor::PluginProtected(cPlugin* Plugin, int menuView)
+{
+ char* buf;
+
+ if (cOsd::pinValid)
+ return false;
+
+ if (menuView && !cPinPlugin::hideProtectedPlugins)
+ return false;
+
+ if (!Plugin)
+ return false;
+
+ // check if plugin is protected
+
+ tell(eloDebug, "checking protection of plugin '%s'",Plugin->MainMenuEntry());
+
+ if ((cPinPlugin::getObject())->pluginProtected(Plugin->Name()))
+ {
+ tell(eloDebug, "plugin '%s' is protected", Plugin->MainMenuEntry());
+
+ if (!menuView)
+ {
+ int res;
+ res = asprintf(&buf, "%s %s", Plugin->MainMenuEntry(), tr("is protected"));
+ if (res < 0) ; // memory problems :o
+ Skins.Message(mtInfo, buf);
+ free(buf);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+//***************************************************************************
+// Menu Item Protectes
+//***************************************************************************
+
+bool cPinStatusMonitor::MenuItemProtected(const char* Name, int menuView)
+{
+ char* buf;
+ const char* name;
+
+ if (!Name)
+ return false;
+
+ if (!(name = translations.lookup(Name)))
+ {
+ name = tr(Str::stripHotKey(Name));
+ translations.append(Name, name);
+ }
+
+ tell(eloDebug, "translation: '%s' -> '%'s ",
+ Str::stripHotKey(Name), name);
+
+ if (menuView)
+ (cPinPlugin::getObject())->menuItemAppend(name);
+
+ if (cOsd::pinValid)
+ return false;
+
+ if (menuView && !cPinPlugin::hideProtectedMenus)
+ return false;
+
+ // check if menu item is protected
+
+ tell(eloDebug, "checking protection of menu entry '%s' for '%s'",
+ name, menuView ? "view": "execution");
+
+ if ((cPinPlugin::getObject())->menuItemProtected(name))
+ {
+ tell(eloDebug, "menu entry '%s' is protected", name);
+
+ if (!menuView)
+ {
+ int res;
+ res = asprintf(&buf, "%s %s", name, tr("is protected"));
+ if (res < 0) ; // memory problems :o
+ Skins.Message(mtInfo, buf);
+ free(buf);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+//***************************************************************************
+
+VDRPLUGINCREATOR(cPinPlugin);
diff --git a/pin.h b/pin.h
new file mode 100644
index 0000000..7c77c48
--- /dev/null
+++ b/pin.h
@@ -0,0 +1,237 @@
+/*
+ * A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * File: pin.h
+ *
+ */
+
+#ifndef __PIN_H__
+#define __PIN_H__
+
+//***************************************************************************
+// Includes
+//***************************************************************************
+
+#include <vdr/status.h>
+#include <vdr/plugin.h>
+
+#include "locks.h"
+#include "def.h"
+#include "talk.h"
+
+//***************************************************************************
+// Constants
+//***************************************************************************
+
+static const char *VERSION = "0.1.16";
+static const char *DESCRIPTION = "Childlock plugin";
+static const char *MAINMENUENTRY = tr("Childlock");
+
+#define PROTECTION_FILE "protection.fsk"
+// #define __DEBUG__
+
+//***************************************************************************
+// Pin Service
+//***************************************************************************
+
+class PinService
+{
+ public:
+
+ enum Size
+ {
+ sizePinCode = 5
+ };
+
+ enum ListType
+ {
+ ltUnknown = na,
+ ltChannels,
+ ltBroadcasts,
+ ltPlugins,
+ ltMenuItems
+ };
+
+ class Translation : public cListObject
+ {
+ public:
+
+ char in[200+TB];
+ char out[200+TB];
+ };
+};
+
+class Translations : public cList<PinService::Translation>, PinService
+{
+ public:
+
+ void append(const char* in, const char* out);
+ const char* lookup(const char* in);
+
+};
+
+//***************************************************************************
+// Message Reciver
+//***************************************************************************
+
+class MessageReceiver : public cThread
+{
+ public:
+
+ MessageReceiver();
+ ~MessageReceiver();
+
+ int StartReceiver();
+ int StopReceiver();
+
+ protected:
+
+ void Action();
+ int wait();
+
+ // data
+
+ Talk* talk;
+ int pid;
+ bool active;
+};
+
+//***************************************************************************
+// Pin Status Monitor
+//***************************************************************************
+
+class cPinStatusMonitor : public cStatus
+{
+ protected:
+
+ // interface
+
+ virtual void ChannelSwitch(const cDevice *Device, int ChannelNumber, bool LiveView);
+ virtual bool ChannelProtected(const cDevice *Device, const cChannel* Channel);
+ virtual bool ReplayProtected(const cRecording* Recording, const char* Name,
+ const char* Base, bool isDirectory, int menuView = false);
+ virtual void RecordingFile(const char* FileName);
+ virtual void TimerCreation(cTimer* Timer, const cEvent *Event);
+ virtual bool PluginProtected(cPlugin* Plugin, int menuView = false);
+ virtual bool MenuItemProtected(const char* Name, int menuView = false);
+
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+ virtual void UserAction(const eKeys key);
+#else
+ virtual void UserAction(const eKeys key, const cOsdObject* Interact);
+#endif
+
+ // internal stuff
+
+ const cEvent* GetEventOf(const cSchedules* schedules, const cChannel* Channel);
+
+ Translations translations;
+};
+
+//***************************************************************************
+// Pin
+//***************************************************************************
+
+class cPin : public cOsdObject, public PinService
+{
+ public:
+
+ cPin(void);
+ virtual ~cPin();
+ virtual void Show(void);
+ virtual eOSState ProcessKey(eKeys Key);
+ void clear();
+
+ private:
+
+ cOsd* osd;
+ int textWidth;
+ const char* pinTxt;
+ char code[sizePinCode+TB];
+ cSkinDisplayMessage* display;
+ int pinSetup;
+};
+
+//***************************************************************************
+// Pin Plugin
+//***************************************************************************
+
+class cPinPlugin : public cPlugin, public PinService
+{
+ public:
+
+ enum AutoProtectionMode
+ {
+ apmUnknown = na,
+
+ apmAlways,
+ apmIntelligent,
+ apmNever,
+
+ apmCount
+ };
+
+ cPinPlugin(void);
+ virtual ~cPinPlugin();
+
+ const char* CommandLineHelp(void) { return 0; }
+ bool ProcessArgs(int argc, char *argv[]) { return true; }
+ bool Start(void);
+ void Stop(void);
+ void Housekeeping(void) {}
+
+ virtual cMenuSetupPage *SetupMenu(void);
+ virtual bool SetupParse(const char *Name, const char *Value);
+ const char* Version(void) { return VERSION; }
+ const char* Description(void) { return trNOOP(DESCRIPTION); }
+ virtual bool Initialize(void);
+ virtual const char* MainMenuEntry(void) { return tr(MAINMENUENTRY); }
+ virtual cOsdObject* MainMenuAction(void);
+
+ void addChannel();
+ void delChannel();
+ int channelProtected(const char* name, long startTime = 0);
+ int pluginProtected(const char* name);
+ int menuItemProtected(const char* text);
+ int menuItemAppend(const char* text);
+ int broadcastProtected(const char* title);
+ void checkActivity();
+ int initPluginList();
+ void StorePin();
+
+ static cPinPlugin* getObject() { return object; }
+
+ // static config items
+
+ static char pinCode[sizePinCode+TB];
+ static bool skipChannelSilent;
+ static bool hideProtectedMenus;
+ static bool hideProtectedPlugins;
+ static bool hideProtectedRecordings;
+ static int pinResetTime;
+ static bool autoMenuOpen;
+ static int autoProtectionMode;
+
+ // other static stuff
+
+ static const char* autoProtectionModes[apmCount+1];
+
+ protected:
+
+ // data
+
+ cLockItems lockedChannels;
+ cLockedBroadcasts lockedBroadcasts;
+ cLockItems lockedPlugins;
+ cLockItems lockedMenuItems;
+ time_t lastAction;
+ cPinStatusMonitor* statusMonitor;
+ MessageReceiver* receiver;
+
+ static cPinPlugin* object; // the object
+};
+
+//***************************************************************************
+#endif // __PIN_H__
diff --git a/po/de_DE.po b/po/de_DE.po
new file mode 100644
index 0000000..2872afc
--- /dev/null
+++ b/po/de_DE.po
@@ -0,0 +1,170 @@
+# VDR plugin language source file.
+# Copyright (C) 2007 Klaus Schmidinger <kls@cadsoft.de>
+# This file is distributed under the same license as the VDR package.
+# Klaus Schmidinger <kls@cadsoft.de>, 2000
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: VDR 1.5.7\n"
+"Report-Msgid-Bugs-To: <see README>\n"
+"POT-Creation-Date: 2013-05-22 11:28+0200\n"
+"PO-Revision-Date: 2013-05-22 10:37+0200\n"
+"Last-Translator: Klaus Schmidinger <kls@cadsoft.de>\n"
+"Language-Team: <vdr@linuxtv.org>\n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-15\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "Setup"
+msgstr ""
+
+msgid "Add current channel to protection list"
+msgstr "Aktuellen Kanal sperren"
+
+msgid "Add current broadcast to protection list"
+msgstr "Aktuelle Sendung sperren"
+
+msgid "Edit channel locks"
+msgstr "Gesperrte Kanäle bearbeiten"
+
+msgid "Edit broadcast locks"
+msgstr "Gesperrte Sendungen bearbeiten"
+
+msgid "Edit plugin locks"
+msgstr "Systemeinstellungen bearbeiten"
+
+msgid "Edit menu locks"
+msgstr "Menüsperren bearbeiten"
+
+msgid "Unlock setup"
+msgstr "Setup entsperren"
+
+msgid "Lock setup"
+msgstr "Setup sperren"
+
+msgid "Activate childlock"
+msgstr "Kindersicherung aktivieren"
+
+msgid "Activated pin protection"
+msgstr "Kindersicherung aktiviert"
+
+msgid "Please setup a pin code first!"
+msgstr "Bitte konfigurieren zuerst einen Pin Code!"
+
+msgid "Protected channels"
+msgstr "Gesperrte Kanäle"
+
+msgid "Protected broadcasts"
+msgstr "Gesperrte Sendungen"
+
+msgid "Plugin protections"
+msgstr "Sperren / Entsperren"
+
+msgid "Menu protections"
+msgstr "Menü sperren"
+
+msgid "locked"
+msgstr "gesperrt"
+
+msgid "unlocked"
+msgstr "entsperrt"
+
+msgid "already in list"
+msgstr "bereits in der Liste"
+
+msgid "added to protection list"
+msgstr "hinzugefügt"
+
+msgid "Edit"
+msgstr "Bearbeiten"
+
+msgid "On/Off"
+msgstr "An/Aus"
+
+msgid "Add"
+msgstr "Hinzufügen"
+
+msgid "Delete"
+msgstr "Löschen"
+
+msgid "Remove entry?"
+msgstr "Eintrag entfernen?"
+
+msgid "Name"
+msgstr ""
+
+msgid "Lock active"
+msgstr "Sperre aktiv"
+
+msgid "Keyword"
+msgstr "Suchbegriff"
+
+msgid "Search mode"
+msgstr ""
+
+msgid "Start"
+msgstr ""
+
+msgid "Stop"
+msgstr ""
+
+msgid "Setup your pin code: "
+msgstr "Bitte Pin Code festlegen: "
+
+msgid "Pin Code: "
+msgstr "Pin Eingabe: "
+
+msgid "Pin code successfully setup"
+msgstr "Pin Code erfolgreich festgelegt."
+
+msgid "Code accepted"
+msgstr "Eingabe angenommen"
+
+msgid "Invalid Code !"
+msgstr "Falsche Eingabe !"
+
+msgid "Please enter pin code first"
+msgstr "Bitte zuerst Pin Code eingeben"
+
+msgid "Recording protected, enter pin code first"
+msgstr "Aufnahme gesperrt, bitte Kindersicherung freischalten"
+
+msgid "channel is protected"
+msgstr "Kanal gesperrt"
+
+msgid "broadcast is protected"
+msgstr "Sendung gesperrt"
+
+msgid "is protected"
+msgstr "ist gesperrt"
+
+msgid "Pin code"
+msgstr ""
+
+msgid "Skip protected channel silent"
+msgstr "Gesperrte Kanäle ohne Meldung überspringen"
+
+msgid "Hide protected menus"
+msgstr "Gesperrte Menüs verstecken"
+
+msgid "Hide protected plugins"
+msgstr "Gesperrte Plugins verstecken"
+
+msgid "Hide protected recordings"
+msgstr "Gesperrte Aufnahmen verstecken"
+
+msgid "Open menu after pin request"
+msgstr "Menü nach Eingabe des Pin öffnen"
+
+msgid "Pin inactivity time [min]"
+msgstr "Sperre aktivieren nach Inaktivität [min]"
+
+msgid "Autoprotection of recordings"
+msgstr "Automatischer Schutz der Aufnahmen"
+
+msgid "Log Level [0-5]"
+msgstr ""
+
+msgid "Ignoring invalid pin"
+msgstr "Ungültiger code"
diff --git a/po/fr_FR.po b/po/fr_FR.po
new file mode 100644
index 0000000..2c11b4f
--- /dev/null
+++ b/po/fr_FR.po
@@ -0,0 +1,173 @@
+# VDR plugin language source file.
+# Copyright (C) 2007 Klaus Schmidinger <kls@cadsoft.de>
+# This file is distributed under the same license as the VDR package.
+# Jean-Claude Repetto <jc@repetto.org>, 2001
+# Olivier Jacques <jacquesolivier@hotmail.com>, 2003
+# Gregoire Favre <greg@magma.unil.ch>, 2003
+# Nicolas Huillard <nhuillard@e-dition.fr>, 2005
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: VDR 1.5.7\n"
+"Report-Msgid-Bugs-To: <see README>\n"
+"POT-Creation-Date: 2013-05-22 11:28+0200\n"
+"PO-Revision-Date: 2013-05-22 10:37+0200\n"
+"Last-Translator: Nicolas Huillard <nhuillard@e-dition.fr>\n"
+"Language-Team: <vdr@linuxtv.org>\n"
+"Language: fr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-1\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "Setup"
+msgstr ""
+
+msgid "Add current channel to protection list"
+msgstr "Ajouter la chaîne actuelle à la protection parentale"
+
+msgid "Add current broadcast to protection list"
+msgstr "Ajouter l'émission actuelle dans la liste de protection parentale"
+
+msgid "Edit channel locks"
+msgstr "Editer la liste de chaînes"
+
+msgid "Edit broadcast locks"
+msgstr "Editer les émissions protégés"
+
+msgid "Edit plugin locks"
+msgstr "editer la protection de plugin"
+
+msgid "Edit menu locks"
+msgstr ""
+
+msgid "Unlock setup"
+msgstr ""
+
+msgid "Lock setup"
+msgstr ""
+
+msgid "Activate childlock"
+msgstr "Protection parentales activée"
+
+msgid "Activated pin protection"
+msgstr "Activé la protection parentale"
+
+msgid "Please setup a pin code first!"
+msgstr "Entrer le code de protection d'abord!"
+
+msgid "Protected channels"
+msgstr "Protéger chaîne"
+
+msgid "Protected broadcasts"
+msgstr "Proteger l'émission"
+
+msgid "Plugin protections"
+msgstr "Plugin sous protection"
+
+msgid "Menu protections"
+msgstr "Protégier des menus"
+
+msgid "locked"
+msgstr "protégé"
+
+msgid "unlocked"
+msgstr "libéré"
+
+msgid "already in list"
+msgstr "Toujours dans la liste"
+
+msgid "added to protection list"
+msgstr "ajouter"
+
+msgid "Edit"
+msgstr "Editer"
+
+msgid "On/Off"
+msgstr "Enclencher/déclencher"
+
+msgid "Add"
+msgstr "Ajouter"
+
+msgid "Delete"
+msgstr "Effacer"
+
+msgid "Remove entry?"
+msgstr "Enlever?"
+
+msgid "Name"
+msgstr ""
+
+msgid "Lock active"
+msgstr "Protection active"
+
+msgid "Keyword"
+msgstr "Expression de recherche"
+
+msgid "Search mode"
+msgstr ""
+
+msgid "Start"
+msgstr ""
+
+msgid "Stop"
+msgstr ""
+
+msgid "Setup your pin code: "
+msgstr "Introduisez le code d'accès:"
+
+msgid "Pin Code: "
+msgstr "Le code pin"
+
+msgid "Pin code successfully setup"
+msgstr "Le code d'accès est accepté"
+
+msgid "Code accepted"
+msgstr "Le code est accepté"
+
+msgid "Invalid Code !"
+msgstr "Le code est faux!"
+
+msgid "Please enter pin code first"
+msgstr "Entré d'abord le code"
+
+msgid "Recording protected, enter pin code first"
+msgstr "Entrée le code de protection"
+
+msgid "channel is protected"
+msgstr "Chaînes protégée"
+
+msgid "broadcast is protected"
+msgstr "Emission protégée"
+
+msgid "is protected"
+msgstr "est protégé"
+
+msgid "Pin code"
+msgstr ""
+
+msgid "Skip protected channel silent"
+msgstr "Les chaînes protégés seront ignorées sans infos"
+
+msgid "Hide protected menus"
+msgstr "Menu protégés sont invisibles"
+
+msgid "Hide protected plugins"
+msgstr "Plugins protégés sont invisibles"
+
+msgid "Hide protected recordings"
+msgstr "Enregistrements protégés sont invisibles"
+
+msgid "Open menu after pin request"
+msgstr "Ouvrir le menu après la rentré de la cléed"
+
+msgid "Pin inactivity time [min]"
+msgstr "Protection après une inactivitées de [min]"
+
+msgid "Autoprotection of recordings"
+msgstr "Protection automatique des enregistrement"
+
+msgid "Log Level [0-5]"
+msgstr ""
+
+msgid "Ignoring invalid pin"
+msgstr "Le code est fau"
diff --git a/po/hu_HU.po b/po/hu_HU.po
new file mode 100644
index 0000000..a834225
--- /dev/null
+++ b/po/hu_HU.po
@@ -0,0 +1,171 @@
+# VDR plugin language source file.
+# Copyright (C) 2007 Klaus Schmidinger <kls@cadsoft.de>
+# This file is distributed under the same license as the VDR package.
+# Istvan Koenigsberger <istvnko@hotmail.com>, 2002
+# Guido Josten <guido.josten@t-online.de>, 2002
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: VDR 1.5.7\n"
+"Report-Msgid-Bugs-To: <see README>\n"
+"POT-Creation-Date: 2013-05-22 11:28+0200\n"
+"PO-Revision-Date: 2013-05-22 10:37+0200\n"
+"Last-Translator: Istvan Koenigsberger <istvnko@hotmail.com>, Guido Josten <guido.josten@t-online.de>\n"
+"Language-Team: <vdr@linuxtv.org>\n"
+"Language: hu\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-2\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "Setup"
+msgstr ""
+
+msgid "Add current channel to protection list"
+msgstr "Aktuális csatorna zároltakhoz adása"
+
+msgid "Add current broadcast to protection list"
+msgstr "Aktuális adás zároltakhoz adása"
+
+msgid "Edit channel locks"
+msgstr "Zárolt csatornák szerkesztése"
+
+msgid "Edit broadcast locks"
+msgstr "Zárolt adások szerkesztése"
+
+msgid "Edit plugin locks"
+msgstr ""
+
+msgid "Edit menu locks"
+msgstr ""
+
+msgid "Unlock setup"
+msgstr ""
+
+msgid "Lock setup"
+msgstr ""
+
+msgid "Activate childlock"
+msgstr "Gyerekzár aktiválása"
+
+msgid "Activated pin protection"
+msgstr "Gyerekzár aktiválva"
+
+msgid "Please setup a pin code first!"
+msgstr ""
+
+msgid "Protected channels"
+msgstr "Védett csatornák"
+
+msgid "Protected broadcasts"
+msgstr "Védett adások"
+
+msgid "Plugin protections"
+msgstr ""
+
+msgid "Menu protections"
+msgstr ""
+
+msgid "locked"
+msgstr ""
+
+msgid "unlocked"
+msgstr ""
+
+msgid "already in list"
+msgstr "mar a zaroltak között"
+
+msgid "added to protection list"
+msgstr "Zároltakhoz hozzáadva"
+
+msgid "Edit"
+msgstr ""
+
+msgid "On/Off"
+msgstr ""
+
+msgid "Add"
+msgstr "Hozzáadás"
+
+msgid "Delete"
+msgstr "Törlés"
+
+msgid "Remove entry?"
+msgstr "Törlése?"
+
+msgid "Name"
+msgstr ""
+
+msgid "Lock active"
+msgstr "Zár aktiválva"
+
+msgid "Keyword"
+msgstr "Kulcsszó"
+
+msgid "Search mode"
+msgstr ""
+
+msgid "Start"
+msgstr ""
+
+msgid "Stop"
+msgstr ""
+
+msgid "Setup your pin code: "
+msgstr ""
+
+msgid "Pin Code: "
+msgstr "Pin kód"
+
+msgid "Pin code successfully setup"
+msgstr ""
+
+msgid "Code accepted"
+msgstr ""
+
+msgid "Invalid Code !"
+msgstr ""
+
+msgid "Please enter pin code first"
+msgstr ""
+
+msgid "Recording protected, enter pin code first"
+msgstr "Kérem oldja fel a gyerekzárat"
+
+msgid "channel is protected"
+msgstr "Csatorna zárolva"
+
+msgid "broadcast is protected"
+msgstr "Adás zárolva"
+
+msgid "is protected"
+msgstr ""
+
+msgid "Pin code"
+msgstr ""
+
+msgid "Skip protected channel silent"
+msgstr "Zárolt csatornákat nem megjeleníteni"
+
+msgid "Hide protected menus"
+msgstr ""
+
+msgid "Hide protected plugins"
+msgstr ""
+
+msgid "Hide protected recordings"
+msgstr ""
+
+msgid "Open menu after pin request"
+msgstr ""
+
+msgid "Pin inactivity time [min]"
+msgstr "Zár aktiválása [perc] inaktivitás után"
+
+msgid "Autoprotection of recordings"
+msgstr "Automatikus védelem felvétel ellen"
+
+msgid "Log Level [0-5]"
+msgstr ""
+
+msgid "Ignoring invalid pin"
+msgstr "Érvénytelen pin kód"
diff --git a/po/it_IT.po b/po/it_IT.po
new file mode 100644
index 0000000..0143791
--- /dev/null
+++ b/po/it_IT.po
@@ -0,0 +1,172 @@
+# VDR plugin language source file.
+# Copyright (C) 2007 Klaus Schmidinger <kls@cadsoft.de>
+# This file is distributed under the same license as the VDR package.
+# Alberto Carraro <bertocar@tin.it>, 2001
+# Antonio Ospite <ospite@studenti.unina.it>, 2003
+# Sean Carlos <seanc@libero.it>, 2005
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: VDR 1.5.7\n"
+"Report-Msgid-Bugs-To: <see README>\n"
+"POT-Creation-Date: 2013-05-22 11:28+0200\n"
+"PO-Revision-Date: 2013-05-22 10:37+0200\n"
+"Last-Translator: Sean Carlos <seanc@libero.it>\n"
+"Language-Team: <vdr@linuxtv.org>\n"
+"Language: it\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-15\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "Setup"
+msgstr ""
+
+msgid "Add current channel to protection list"
+msgstr "Aggiungi canale attuale all'elenco protezione"
+
+msgid "Add current broadcast to protection list"
+msgstr "Aggiungi trasmissione attuale all'elenco protezione"
+
+msgid "Edit channel locks"
+msgstr "Modifica protezioni canale"
+
+msgid "Edit broadcast locks"
+msgstr "Modifica protezioni trasmissione"
+
+msgid "Edit plugin locks"
+msgstr "Modifica protezioni plugin"
+
+msgid "Edit menu locks"
+msgstr "Modifica protezioni menu"
+
+msgid "Unlock setup"
+msgstr "Sblocca impostazioni"
+
+msgid "Lock setup"
+msgstr "Blocca impostazioni"
+
+msgid "Activate childlock"
+msgstr "Attiva filtro famiglia"
+
+msgid "Activated pin protection"
+msgstr "Attivato filtro famiglia"
+
+msgid "Please setup a pin code first!"
+msgstr "Per favore imposta prima un codice Pin"
+
+msgid "Protected channels"
+msgstr "Canali protetti"
+
+msgid "Protected broadcasts"
+msgstr "Trasmissioni protette"
+
+msgid "Plugin protections"
+msgstr "Plugin di protezione"
+
+msgid "Menu protections"
+msgstr "Protezioni menu"
+
+msgid "locked"
+msgstr "protetto"
+
+msgid "unlocked"
+msgstr "sprotetto"
+
+msgid "already in list"
+msgstr "già nell'elenco"
+
+msgid "added to protection list"
+msgstr "aggiunto all'elenco protezione"
+
+msgid "Edit"
+msgstr "Modifica"
+
+msgid "On/Off"
+msgstr "Attivo/Disattivo"
+
+msgid "Add"
+msgstr "Aggiungi"
+
+msgid "Delete"
+msgstr "Elimina"
+
+msgid "Remove entry?"
+msgstr "Rimuovere valore?"
+
+msgid "Name"
+msgstr ""
+
+msgid "Lock active"
+msgstr "Protezione attiva"
+
+msgid "Keyword"
+msgstr "Parola chiave"
+
+msgid "Search mode"
+msgstr ""
+
+msgid "Start"
+msgstr ""
+
+msgid "Stop"
+msgstr ""
+
+msgid "Setup your pin code: "
+msgstr "Imposta il codice pin:"
+
+msgid "Pin Code: "
+msgstr "Codice Pin"
+
+msgid "Pin code successfully setup"
+msgstr "Codice pin impostato con successo"
+
+msgid "Code accepted"
+msgstr "Codice accettato"
+
+msgid "Invalid Code !"
+msgstr "Codice non valido !"
+
+msgid "Please enter pin code first"
+msgstr "Per favore inserisci prima un codice Pin"
+
+msgid "Recording protected, enter pin code first"
+msgstr "Registrazione protetta, inserisci prima il codice pin"
+
+msgid "channel is protected"
+msgstr "Canale protetto"
+
+msgid "broadcast is protected"
+msgstr "Trasmissione protetta"
+
+msgid "is protected"
+msgstr "è protetta"
+
+msgid "Pin code"
+msgstr ""
+
+msgid "Skip protected channel silent"
+msgstr "Salta i canali protetti in modo silenzioso"
+
+msgid "Hide protected menus"
+msgstr "Nascondi menu protetti"
+
+msgid "Hide protected plugins"
+msgstr "Nascondi plugin protetti"
+
+msgid "Hide protected recordings"
+msgstr "Nascondi registrazioni protette"
+
+msgid "Open menu after pin request"
+msgstr "Apri menu dopo richiesta Pin"
+
+msgid "Pin inactivity time [min]"
+msgstr "Tempo inattività Pin [min]"
+
+msgid "Autoprotection of recordings"
+msgstr "Protezione automatica registrazioni"
+
+msgid "Log Level [0-5]"
+msgstr ""
+
+msgid "Ignoring invalid pin"
+msgstr "Ignora pin non valido"
diff --git a/po/ru_RU.po b/po/ru_RU.po
new file mode 100644
index 0000000..848a9e8
--- /dev/null
+++ b/po/ru_RU.po
@@ -0,0 +1,170 @@
+# VDR plugin language source file.
+# Copyright (C) 2007 Klaus Schmidinger <kls@cadsoft.de>
+# This file is distributed under the same license as the VDR package.
+# Vyacheslav Dikonov <sdiconov@mail.ru>, 2004
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: VDR 1.5.7\n"
+"Report-Msgid-Bugs-To: <see README>\n"
+"POT-Creation-Date: 2013-05-22 11:28+0200\n"
+"PO-Revision-Date: 2013-05-22 10:37+0200\n"
+"Last-Translator: Vyacheslav Dikonov <sdiconov@mail.ru>\n"
+"Language-Team: <vdr@linuxtv.org>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-5\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "Setup"
+msgstr ""
+
+msgid "Add current channel to protection list"
+msgstr "´ÞÑÐÒØâì âÕÚãéØÙ ÚÐÝÐÛ Ò ÑÛÞÚ. áßØáÞÚ"
+
+msgid "Add current broadcast to protection list"
+msgstr "´ÞÑÐÒØâì âÕÚãéãî àÐÔØÞßÕàÕÔÐçã Ò ÑÛÞÚ. áßØáÞÚ"
+
+msgid "Edit channel locks"
+msgstr "ÀÕÔÐÚâØàÞÒÐâì ÑÛÞÚØàÞÒÚã ÚÐÝÐÛÐ"
+
+msgid "Edit broadcast locks"
+msgstr "ÀÕÔÐÚâØàÞÒÐâì ÑÛÞÚØàÞÒÚã àÐÔØÞßÕàÕÔÐçØ"
+
+msgid "Edit plugin locks"
+msgstr ""
+
+msgid "Edit menu locks"
+msgstr ""
+
+msgid "Unlock setup"
+msgstr ""
+
+msgid "Lock setup"
+msgstr ""
+
+msgid "Activate childlock"
+msgstr "°ÚâØÒØàÞÒÐâì ÑÛÞÚØàÞÒÚã"
+
+msgid "Activated pin protection"
+msgstr "°ÚâØÒØàÞÒÐâì ÑÛÞÚØàÞÒÚã"
+
+msgid "Please setup a pin code first!"
+msgstr ""
+
+msgid "Protected channels"
+msgstr "·ÐÑÛÞÚØàÞÒÐÝÝëÕ ÚÐÝÐÛë"
+
+msgid "Protected broadcasts"
+msgstr "·ÐÑÛÞÚØàÞÒÐÝÝëÕ àÐÔØÞßÕàÕÔÐçØ"
+
+msgid "Plugin protections"
+msgstr ""
+
+msgid "Menu protections"
+msgstr ""
+
+msgid "locked"
+msgstr ""
+
+msgid "unlocked"
+msgstr ""
+
+msgid "already in list"
+msgstr "ãÖÕ Ò áßØáÚÕ"
+
+msgid "added to protection list"
+msgstr "ÔÞÑÐÒÛÕÝÞ Ò áßØáÞÚ ÑÛÞÚØàÞÒÚØ"
+
+msgid "Edit"
+msgstr "ÀÕÔÐÚâØàÞÒÐâì"
+
+msgid "On/Off"
+msgstr ""
+
+msgid "Add"
+msgstr "´ÞÑÐÒØâì"
+
+msgid "Delete"
+msgstr "ÃÔÐÛØâì"
+
+msgid "Remove entry?"
+msgstr "ÚÐÝÐÛ?"
+
+msgid "Name"
+msgstr ""
+
+msgid "Lock active"
+msgstr "±ÛÞÚØàÞÒÚÐ ÐÚâØÒÝÐ"
+
+msgid "Keyword"
+msgstr ""
+
+msgid "Search mode"
+msgstr ""
+
+msgid "Start"
+msgstr ""
+
+msgid "Stop"
+msgstr ""
+
+msgid "Setup your pin code: "
+msgstr ""
+
+msgid "Pin Code: "
+msgstr "¿ØÝ ÚÞÔ"
+
+msgid "Pin code successfully setup"
+msgstr ""
+
+msgid "Code accepted"
+msgstr ""
+
+msgid "Invalid Code !"
+msgstr ""
+
+msgid "Please enter pin code first"
+msgstr ""
+
+msgid "Recording protected, enter pin code first"
+msgstr "¿ÞÖÐÛãÙáâÐ ÒÒÕÔØâÕ áÝÐçÐÛÐ ßØÝ ÚÞÔ"
+
+msgid "channel is protected"
+msgstr "ÚÐÝÐÛ ×ÐÑÛÞÚØàÞÒÐÝ"
+
+msgid "broadcast is protected"
+msgstr "àÐÔØÞßÕàÕÔÐçÐ ×ÐÑÛÞÚØàÞÒÐÝÐ"
+
+msgid "is protected"
+msgstr ""
+
+msgid "Pin code"
+msgstr ""
+
+msgid "Skip protected channel silent"
+msgstr "¿àÞßãáâØâì ×ÐéØéÕÝÝëÙ ÚÐÝÐÛ âØåÞ"
+
+msgid "Hide protected menus"
+msgstr ""
+
+msgid "Hide protected plugins"
+msgstr ""
+
+msgid "Hide protected recordings"
+msgstr ""
+
+msgid "Open menu after pin request"
+msgstr ""
+
+msgid "Pin inactivity time [min]"
+msgstr "ÂÐÙÜÕà ÝÕÐÚâØÒÝÞáâØ ßØÝ [ÜØÝãâ]"
+
+msgid "Autoprotection of recordings"
+msgstr ""
+
+msgid "Log Level [0-5]"
+msgstr ""
+
+msgid "Ignoring invalid pin"
+msgstr "¸ÓÝÞàØàÞÒÐâì ÝÕÒÕàÝëÙ ßØÝ"
diff --git a/rep.c b/rep.c
new file mode 100644
index 0000000..55b3920
--- /dev/null
+++ b/rep.c
@@ -0,0 +1,77 @@
+/*
+ * pin.c: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * Date: 29.01.2006, horchi
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <regex.h>
+
+#include "rep.h"
+#include "def.h"
+
+//**************************************************************************
+// Regular Expression Searching
+//**************************************************************************
+
+int rep(const char* string, const char* expression, Option options)
+{
+ const char* tmpA;
+ const char* tmpB;
+
+ return rep( string, expression, tmpA, tmpB, options);
+}
+
+
+int rep(const char* string, const char* expression, const char*& s_location,
+ Option options)
+{
+ const char* tmpA;
+
+ return rep( string, expression, s_location, tmpA, options);
+}
+
+
+int rep(const char* string, const char* expression, const char*& s_location,
+ const char*& e_location, Option options)
+{
+ regex_t reg;
+ regmatch_t rm;
+ int status;
+ int opt = 0;
+
+ // Vorbereiten von reg fuer die Expressionsuche mit regexec
+ // Flags: REG_EXTENDED = Use Extended Regular Expressions
+ // REG_ICASE = Ignore case in match.
+
+ reg.re_nsub = 0;
+
+ // Options umwandeln
+ if (options & repUseRegularExpression)
+ opt = opt | REG_EXTENDED;
+ if (options & repIgnoreCase)
+ opt = opt | REG_ICASE;
+
+ if (regcomp( &reg, expression, opt) != 0)
+ return fail;
+
+ // Suchen des ersten Vorkommens von reg in string
+
+ status = regexec( &reg, string, 1, &rm, 0);
+ regfree(&reg);
+
+ if (status != 0)
+ return fail;
+
+ // Suche erfolgreich =>
+ // Setzen der ermittelten Start- und Endpositionen
+
+ s_location = (char*)(string + rm.rm_so);
+ e_location = (char*)(string + rm.rm_eo);
+
+ return success;
+}
+
diff --git a/rep.h b/rep.h
new file mode 100644
index 0000000..f0373c0
--- /dev/null
+++ b/rep.h
@@ -0,0 +1,31 @@
+/*
+ * pin.c: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * Date: 28.01.2006, horchi
+ */
+
+//***************************************************************************
+
+#ifndef __REP_H__
+#define __REP_H__
+
+//***************************************************************************
+
+enum Option
+{
+ repUseRegularExpression = 1,
+ repIgnoreCase = 2
+};
+
+int rep(const char* string, const char* expression, Option options = repUseRegularExpression);
+
+int rep(const char* string, const char* expression,
+ const char*& s_location, Option options = repUseRegularExpression);
+
+int rep(const char* string, const char* expression, const char*& s_location,
+ const char*& e_location, Option options = repUseRegularExpression);
+
+//***************************************************************************
+#endif
diff --git a/scripts/cut.sh b/scripts/cut.sh
new file mode 100755
index 0000000..3b4a1db
--- /dev/null
+++ b/scripts/cut.sh
@@ -0,0 +1,14 @@
+#
+# Copy FSK protection of the PIN plugin when cutting a protected recording
+#
+
+if [ $1 == "edited" ]; then
+ target_recording="$2"
+ source_recording=`echo $2 | sed s/"%"/""/ `
+ protection_file="$source_recording/protection.fsk"
+
+ if [ -e "$protection_file" ]; then
+ cp "$protection_file" "$target_recording/"
+ fi
+fi
+
diff --git a/scripts/fskcheck-demo.sh b/scripts/fskcheck-demo.sh
new file mode 100755
index 0000000..02aa17e
--- /dev/null
+++ b/scripts/fskcheck-demo.sh
@@ -0,0 +1,23 @@
+
+
+./fskcheck check
+
+case "$?" in
+ 0)
+ echo "check went wrong"
+ ;;
+
+ 1)
+ echo "not protected"
+ ;;
+
+ 2)
+ echo "protected"
+ ./fskcheck info "Can't execute script due to pin protection"
+ ;;
+
+ *)
+ echo "unexpected return code"
+ ;;
+
+esac
diff --git a/scripts/fskprotect.sh b/scripts/fskprotect.sh
new file mode 100755
index 0000000..8e9a78b
--- /dev/null
+++ b/scripts/fskprotect.sh
@@ -0,0 +1,34 @@
+
+fskcheck check
+
+if [ $? != 1 ]; then
+ fskcheck info "Please enable pin protection first"
+ exit 1
+fi
+
+if [ "$1" == "protect" ]; then
+
+ if [ -f $2/protection.fsk ]; then
+ fskcheck info "Recording was already protected!"
+ exit 1
+ fi
+
+ touch $2/protection.fsk
+ fskcheck info "Recording now protected!"
+
+fi
+
+if [ "$1" == "unprotect" ]; then
+
+ if [ ! -f $2/protection.fsk ]; then
+ echo fskcheck info "Recording was not protected!"
+ exit 1
+ fi
+
+ rm -f $2/protection.fsk
+ fskcheck info "Removed protection of recording!"
+
+fi
+
+exit 0
+
diff --git a/scripts/getSetupMenues b/scripts/getSetupMenues
new file mode 100755
index 0000000..ea46edd
--- /dev/null
+++ b/scripts/getSetupMenues
@@ -0,0 +1,8 @@
+
+cat $1 | awk 'BEGIN { insidecom=0; }
+ /[<][!][-][-]/ { insidecom=1; }
+ /[-][-][>]/ { insidecom=0; }
+ /^[^-]/ { if (!insidecom) { printf( "%s\n", $0); begin=0 } }' | \
+egrep "<(system|menu|command) " | sed s/"^.* name=\""/""/ | sed s/"\".*"/""/ | sort | uniq | awk '{ printf("%s:no:0000:2359:%s:exact\n", $0, $0) }'
+
+
diff --git a/setupmenu.c b/setupmenu.c
new file mode 100644
index 0000000..4d77382
--- /dev/null
+++ b/setupmenu.c
@@ -0,0 +1,139 @@
+
+//***************************************************************************
+// Includes
+//***************************************************************************
+
+#include <vdr/menuitems.h>
+#include "setupmenu.h"
+#include <ctype.h> // isdigit
+
+//***************************************************************************
+// Object
+//***************************************************************************
+
+PinSetupMenu::PinSetupMenu()
+{
+ static const char* trProtectionModes[cPinPlugin::apmCount] = { 0 };
+
+ hidePinCode = yes;
+ // hidePinCode = no;
+
+ if (hidePinCode)
+ {
+ for (int i = 0; i < cPinPlugin::sizePinCode; i++)
+ pinCode[i] = '*';
+
+ pinCode[cPinPlugin::sizePinCode] = 0;
+ }
+ else
+ {
+ strcpy(pinCode, cPinPlugin::pinCode);
+ }
+
+ skipChannelSilent = cPinPlugin::skipChannelSilent;
+ pinResetTime = cPinPlugin::pinResetTime;
+ autoMenuOpen = cPinPlugin::autoMenuOpen;
+ autoProtectionMode = cPinPlugin::autoProtectionMode;
+ hideProtectedMenus = cPinPlugin::hideProtectedMenus;
+ hideProtectedPlugins = cPinPlugin::hideProtectedPlugins;
+ hideProtectedRecordings = cPinPlugin::hideProtectedRecordings;
+
+ // translate protection modes
+
+ if (!trProtectionModes[0])
+ for (int i = 0; i < cPinPlugin::apmCount; i++)
+ trProtectionModes[i] = tr(cPinPlugin::autoProtectionModes[i]);
+
+ if (hidePinCode)
+ Add(new cMenuEditStrItem(tr("Pin code"), pinCode, cPinPlugin::sizePinCode+TB, "0123456789*"));
+ else
+ Add(new cMenuEditNumItem(tr("Pin code"), pinCode, cPinPlugin::sizePinCode, false));
+
+ Add(new cMenuEditBoolItem(tr("Skip protected channel silent"), &skipChannelSilent));
+ Add(new cMenuEditBoolItem(tr("Hide protected menus"), &hideProtectedMenus));
+ Add(new cMenuEditBoolItem(tr("Hide protected plugins"), &hideProtectedPlugins));
+ Add(new cMenuEditBoolItem(tr("Hide protected recordings"), &hideProtectedRecordings));
+ Add(new cMenuEditBoolItem(tr("Open menu after pin request"), &autoMenuOpen));
+ Add(new cMenuEditIntItem(tr("Pin inactivity time [min]"), &pinResetTime, 0, 5*60)); // max 5 hour
+ Add(new cMenuEditStraItem(tr("Autoprotection of recordings"), &autoProtectionMode,
+ cPinPlugin::apmCount, trProtectionModes));
+ Add(new cMenuEditIntItem(tr("Log Level [0-5]"), &logLevel, 0, 5));
+}
+
+PinSetupMenu::~PinSetupMenu()
+{
+ // nothing yet
+}
+
+//***************************************************************************
+// Store
+//***************************************************************************
+
+void PinSetupMenu::Store(void)
+{
+ int i = 0;
+ int pinValid = yes;
+
+ if (hidePinCode)
+ {
+ int pinChanged = no;
+
+ // store only numerical pin codes
+
+ for (i = 0; i < cPinPlugin::sizePinCode; i++)
+ {
+ if (pinCode[i] != '*')
+ {
+ pinChanged = yes;
+ break;
+ }
+ }
+
+ if (pinChanged)
+ {
+ i = 0;
+
+ while (pinCode[i])
+ {
+ if (!isdigit(pinCode[i++]))
+ {
+ i--;
+ pinCode[i] = 0;
+ break;
+ }
+ }
+
+ if (i < 2)
+ {
+ Skins.Message(mtError, tr("Ignoring invalid pin"));
+ pinValid = no;
+ }
+ }
+ else
+ pinValid = no;
+ }
+
+ if (pinValid)
+ {
+ strncpy(cPinPlugin::pinCode, pinCode, cPinPlugin::sizePinCode);
+ cPinPlugin::pinCode[cPinPlugin::sizePinCode] = 0;
+ SetupStore("pinCode", cPinPlugin::pinCode);
+ }
+
+ cPinPlugin::skipChannelSilent = skipChannelSilent;
+ cPinPlugin::pinResetTime = pinResetTime;
+ cPinPlugin::autoMenuOpen = autoMenuOpen;
+ cPinPlugin::autoProtectionMode = autoProtectionMode;
+
+ cPinPlugin::hideProtectedMenus = hideProtectedMenus;
+ cPinPlugin::hideProtectedPlugins = hideProtectedPlugins;
+ cPinPlugin::hideProtectedRecordings = hideProtectedRecordings;
+
+ SetupStore("skipChannelSilent", skipChannelSilent);
+ SetupStore("hideProtectedMenus", hideProtectedMenus);
+ SetupStore("hideProtectedPlugins", hideProtectedPlugins);
+ SetupStore("hideProtectedRecordings", hideProtectedRecordings);
+ SetupStore("pinResetTime", pinResetTime);
+ SetupStore("autoMenuOpen", autoMenuOpen);
+ SetupStore("autoProtectionMode", autoProtectionMode);
+}
diff --git a/setupmenu.h b/setupmenu.h
new file mode 100644
index 0000000..7b70944
--- /dev/null
+++ b/setupmenu.h
@@ -0,0 +1,42 @@
+
+#ifndef __PINSETUTMENU_H_
+#define __PINSETUTMENU_H_
+
+//***************************************************************************
+// Includes
+//***************************************************************************
+
+#include <vdr/plugin.h>
+#include "pin.h"
+
+//***************************************************************************
+// Pin Setup Menu
+//***************************************************************************
+
+class PinSetupMenu : public cMenuSetupPage
+{
+
+ public:
+
+ PinSetupMenu();
+ ~PinSetupMenu();
+
+ protected:
+
+ void Store(void);
+
+ // data
+
+ char pinCode[cPinPlugin::sizePinCode+TB];
+ int skipChannelSilent;
+ int pinResetTime;
+ int autoMenuOpen;
+ int autoProtectionMode;
+ int hidePinCode;
+ int hideProtectedMenus;
+ int hideProtectedPlugins;
+ int hideProtectedRecordings;
+};
+
+//***************************************************************************
+#endif // __PINSETUTMENU_H_
diff --git a/talk.c b/talk.c
new file mode 100644
index 0000000..7921b39
--- /dev/null
+++ b/talk.c
@@ -0,0 +1,198 @@
+
+
+#include <sys/msg.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <vdr/tools.h>
+
+#include "def.h"
+#include "talk.h"
+
+//***************************************************************************
+// Statics
+//***************************************************************************
+
+static int fTimeout = no;
+
+//*************************************************************************
+// Timeout-Handler
+//*************************************************************************
+
+void timeoutHandler(...)
+{
+ fTimeout = true;
+}
+
+//***************************************************************************
+// Object
+//***************************************************************************
+
+Talk::Talk()
+{
+ struct msgbuf* theMsg = (struct msgbuf*)buffer;
+
+ header = (Header*)theMsg->mtext;
+ message = theMsg->mtext + sizeof(Header);
+ receiverID = na;
+ msgID = na;
+ timeout = 30;
+ opened = no;
+}
+
+Talk::~Talk()
+{
+ if (opened)
+ {
+ if (msgctl(msgID, IPC_RMID, 0) < 0)
+ tell(eloAlways, "Error: Closing message queue failed, '%s'", strerror(errno));
+ }
+}
+
+//***************************************************************************
+// Init
+//***************************************************************************
+
+int Talk::init()
+{
+ return success;
+}
+
+//***************************************************************************
+// Open
+//***************************************************************************
+
+int Talk::open(long port)
+{
+ if ((msgID = msgget(0xd8000000 + port, IPC_CREAT|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) < 0)
+ {
+ tell(eloAlways, "Creating of message queue failed, error was '%s'", strerror(errno));
+
+ return fail;
+ }
+
+ opened = yes;
+
+ return success;
+}
+
+//***************************************************************************
+// Wait
+//***************************************************************************
+
+int Talk::wait()
+{
+ int result;
+ int status = success;
+ struct itimerval oldTimer, newTimer;
+ struct sigaction oldAction, newAction;
+ struct msgbuf* theMsg = (struct msgbuf*)buffer;
+
+ // clear message buffer
+
+ memset(buffer, 0, sizeBuffer);
+
+ // set timeout-handler
+
+ if (timeout)
+ {
+ // init register
+
+ memset(&newAction, 0, sizeof(newAction));
+ memset(&newTimer, 0, sizeof(newTimer));
+
+ // set timer
+
+ newTimer.it_value.tv_sec = timeout;
+ setitimer(ITIMER_REAL, &newTimer, &oldTimer);
+
+ // set handler
+
+ newAction.sa_handler = (void(*)(int))timeoutHandler;
+ sigaction(SIGALRM, &newAction, &oldAction);
+
+ // clear timeout-flag
+
+ fTimeout = no;
+ }
+
+ // receive auf
+
+ if ((result = msgrcv(msgID, theMsg, sizeBuffer, 0, 0)) < 0)
+ {
+ if (!fTimeout)
+ {
+ if (errno == EINTR)
+ return wrnSysInterrupt;
+ else if (errno == E2BIG)
+ return errMessageToBig;
+ else if (errno != ENOMSG)
+ tell(eloAlways, "Receiving message failed, errno (%d), '%s'",
+ errno, strerror(errno));
+ }
+
+ status = fail;
+ }
+
+ if (timeout)
+ {
+ sigaction(SIGALRM, &oldAction, 0);
+ setitimer(ITIMER_REAL, &oldTimer,0);
+ }
+
+ if (status == success)
+ {
+ event = header->event;
+ from = header->sender;
+ }
+
+ return status;
+}
+
+//***************************************************************************
+// Send
+//***************************************************************************
+
+int Talk::send(long to, int event, const char* msg)
+{
+ struct msgbuf* theMsg = (struct msgbuf*)buffer;
+ int sizeMsg = sizeof(Header);
+ int receiverID;
+
+ if (!msg) msg = "";
+
+ if (strlen(msg) > sizeBuffer - sizeof(Header) - sizeof(theMsg->mtype))
+ {
+ tell(eloAlways, "Message to big, aborting");
+ return fail;
+ }
+
+ // try to get the receiver ID
+
+ if ((receiverID = msgget(0xd8000000 + to, 0)) < 0)
+ {
+ tell(eloAlways, "Attach to message queue failed, '%s'", strerror(errno));
+ return fail;
+ }
+
+ strcpy(message, msg);
+ sizeMsg += strlen(message)+1;
+
+ theMsg->mtype = 1;
+ header->sender = msgID;
+ header->receiver = receiverID;
+ header->event = event;
+ header->type = 0;
+
+ if (msgsnd(header->receiver, theMsg, sizeMsg, 0) < 0)
+ {
+ tell(eloAlways, "send failed, '%s'", strerror(errno));
+ return fail;
+ }
+
+ tell(eloAlways, "send to (%ld) succeeded", header->receiver);
+
+ return success;
+}
diff --git a/talk.h b/talk.h
new file mode 100644
index 0000000..94cb1db
--- /dev/null
+++ b/talk.h
@@ -0,0 +1,97 @@
+//***************************************************************************
+/*
+ * fskcheck.c: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id: talk.h,v 1.1 2007/01/13 07:52:27 root Exp $
+ */
+//***************************************************************************
+
+//***************************************************************************
+// Includes
+//***************************************************************************
+
+#include "def.h"
+
+//***************************************************************************
+// Class Talk
+//***************************************************************************
+
+class Talk
+{
+ public:
+
+ // declarations
+
+ enum Error
+ {
+ errMessageServiceFirst = -1000,
+
+ errMessageToBig,
+
+ wrnSysInterrupt,
+ wrnNoMessage
+ };
+
+ enum Size
+ {
+ sizeBuffer = 1000
+ };
+
+ enum Event
+ {
+ evtUnknown = na,
+
+ evtCheck, // = 0
+ evtShow, // = 1
+ evtAsk, // = 2
+ evtConfirm, // = 3
+ evtAbort, // = 4
+ evtAnswer, // = 5
+ evtExit // = 6
+ };
+
+ struct Header
+ {
+ long sender;
+ long receiver;
+ int event;
+ int type; // eMessageType
+ };
+
+ // object
+
+ Talk();
+ ~Talk();
+
+ // functions
+
+ int wait();
+ int init();
+ int send(long to, int event, const char* msg = 0);
+ int open(long port);
+
+ int isOpen() { return opened; }
+ long getTimeout() { return timeout; }
+ int getEvent() { return event; }
+ long getFrom() { return from; }
+ const char* getMessage() { return message; }
+
+ void setTimeout(long t) { timeout = t; }
+
+ protected:
+
+ // data
+
+ int receiverID;
+ int msgID;
+ char buffer[sizeBuffer];
+ char* message;
+ Header* header;
+ long timeout;
+ int opened;
+
+ int event;
+ long from;
+};
diff --git a/test.cc b/test.cc
new file mode 100644
index 0000000..87900a0
--- /dev/null
+++ b/test.cc
@@ -0,0 +1,13 @@
+#include "def.h"
+
+
+int main()
+{
+ char ct[sizeHHMM+TB];
+ long theTime = DT::lNow();
+
+ printf("lNow = '%s'; daylight %d; timezone %ld hours\n", DT::int2Hhmm(theTime, ct), daylight, timezone/tfSecondsPerHour);
+
+ return 0;
+}
+