pax_global_header 0000666 0000000 0000000 00000000064 12660516711 0014517 g ustar 00root root 0000000 0000000 52 comment=eaa2a20e23fee8697a47336f1502127854748086
Plinth-0.8.1/ 0000775 0000000 0000000 00000000000 12660516711 0012763 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/.coveragerc 0000664 0000000 0000000 00000000152 12660516711 0015102 0 ustar 00root root 0000000 0000000 # .coveragerc -- specifies execution options for coverage.py
[run]
branch = True
[report]
precision = 2
Plinth-0.8.1/.gitignore 0000664 0000000 0000000 00000000427 12660516711 0014756 0 ustar 00root root 0000000 0000000 *.pyc
*.py.bak
*.swp
*.tiny.css
data/var/log/plinth/*.log
data/var/lib/plinth/*.sqlite3
data/var/lib/plinth/sessions/*
data/var/run/*.pid
doc/*.pdf
doc/*.html
doc/plinth.1
\#*
.#*
*~
predepend
build/
.emacs.desktop*
*.egg-info/
dist/
.coverage
plinth/tests/coverage/report/
*.mo
Plinth-0.8.1/.travis.yml 0000664 0000000 0000000 00000001327 12660516711 0015077 0 ustar 00root root 0000000 0000000 # Travis-CI configuration file for Plinth
language: python
python:
- "3.4"
env:
- DJANGO_VERSION=">=1.7.0,<1.8.0"
- DJANGO_VERSION=">=1.8.0,<1.9.0"
- DJANGO_VERSION=">=1.9.0"
# Debian packages required
before_install:
- sudo apt-get update -qq
- sudo apt-get install augeas-tools gir1.2-packagekitglib-1.0
# Command to install dependencies
install:
- pip install Django$DJANGO_VERSION
- pip install coverage==3.7
- pip install "pgi>=0.0.10.1"
- pip install -r requirements.txt
# Command to run tests
script: python3 setup.py test
notifications:
email:
on_success: change
on_failure: always
irc:
channels:
- "irc.oftc.net#freedombox"
on_success: always
on_failure: always
Plinth-0.8.1/COPYING 0000664 0000000 0000000 00000230417 12660516711 0014025 0 ustar 00root root 0000000 0000000 # License to Copy Plinth
Plinth is Copyright 2011, 2012, 2013, 2014, 2015 Plinth Authors. See
Git log in the source repository for a full list of authors. It is
distributed under the GNU Affero General Public License, Version 3 or
later. A copy of AGPLv3 is available [from the Free Software
Foundation](http://www.gnu.org/licenses/agpl.html).
In addition, the documentation to this software is distributed under a
Creative Commons Attribution-ShareAlike 3.0 Unported, Version 3
license. This CC-By-SA license is available in both
[full](http://creativecommons.org/licenses/by-sa/3.0/legalcode) and
[summarized](http://creativecommons.org/licenses/by-sa/3.0/) versions
from Creative Commons.
The documentation to this software is also distributed under the [GNU
Free Documentation License](http://www.gnu.org/licenses/fdl.html),
version 1.3 or later.
## GNU Affero General Public License, Version 3
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
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
them 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.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey 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;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If 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 convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero 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 that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
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.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
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.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
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
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright (C)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
.
## Creative Commons Attribution-ShareAlike 3.0 Unported
License
THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
1. Definitions
1. "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License.
2. "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined below) for the purposes of this License.
3. "Creative Commons Compatible License" means a license that is listed at http://creativecommons.org/compatiblelicenses that has been approved by Creative Commons as being essentially equivalent to this License, including, at a minimum, because that license: (i) contains terms that have the same purpose, meaning and effect as the License Elements of this License; and, (ii) explicitly permits the relicensing of adaptations of works made available under that license under this License or a Creative Commons jurisdiction license with the same License Elements as this License.
4. "Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership.
5. "License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, ShareAlike.
6. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License.
7. "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast.
8. "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work.
9. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
10. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images.
11. "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium.
2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws.
3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
1. to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections;
2. to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified.";
3. to Distribute and Publicly Perform the Work including as incorporated in Collections; and,
4. to Distribute and Publicly Perform Adaptations.
5.
For the avoidance of doubt:
1. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License;
2. Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and,
3. Voluntary License Schemes. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License.
The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved.
4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
1. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(c), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(c), as requested.
2. You may Distribute or Publicly Perform an Adaptation only under the terms of: (i) this License; (ii) a later version of this License with the same License Elements as this License; (iii) a Creative Commons jurisdiction license (either this or a later license version) that contains the same License Elements as this License (e.g., Attribution-ShareAlike 3.0 US)); (iv) a Creative Commons Compatible License. If you license the Adaptation under one of the licenses mentioned in (iv), you must comply with the terms of that license. If you license the Adaptation under the terms of any of the licenses mentioned in (i), (ii) or (iii) (the "Applicable License"), you must comply with the terms of the Applicable License generally and the following provisions: (I) You must include a copy of, or the URI for, the Applicable License with every copy of each Adaptation You Distribute or Publicly Perform; (II) You may not offer or impose any terms on the Adaptation that restrict the terms of the Applicable License or the ability of the recipient of the Adaptation to exercise the rights granted to that recipient under the terms of the Applicable License; (III) You must keep intact all notices that refer to the Applicable License and to the disclaimer of warranties with every copy of the Work as included in the Adaptation You Distribute or Publicly Perform; (IV) when You Distribute or Publicly Perform the Adaptation, You may not impose any effective technological measures on the Adaptation that restrict the ability of a recipient of the Adaptation from You to exercise the rights granted to that recipient under the terms of the Applicable License. This Section 4(b) applies to the Adaptation as incorporated in a Collection, but this does not require the Collection apart from the Adaptation itself to be made subject to the terms of the Applicable License.
3. If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and (iv) , consistent with Ssection 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4(c) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties.
4. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise.
5. Representations, Warranties and Disclaimer
UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
7. Termination
1. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
2. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
8. Miscellaneous
1. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
2. Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
3. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
4. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
5. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
6. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law.
## GNU Free Documentation License, Version 1.3
GNU Free Documentation License
Version 1.3, 3 November 2008
Copyright (C) 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
0. PREAMBLE
The purpose of this License is to make a manual, textbook, or other
functional and useful document "free" in the sense of freedom: to
assure everyone the effective freedom to copy and redistribute it,
with or without modifying it, either commercially or noncommercially.
Secondarily, this License preserves for the author and publisher a way
to get credit for their work, while not being considered responsible
for modifications made by others.
This License is a kind of "copyleft", which means that derivative
works of the document must themselves be free in the same sense. It
complements the GNU General Public License, which is a copyleft
license designed for free software.
We have designed this License in order to use it for manuals for free
software, because free software needs free documentation: a free
program should come with manuals providing the same freedoms that the
software does. But this License is not limited to software manuals;
it can be used for any textual work, regardless of subject matter or
whether it is published as a printed book. We recommend this License
principally for works whose purpose is instruction or reference.
1. APPLICABILITY AND DEFINITIONS
This License applies to any manual or other work, in any medium, that
contains a notice placed by the copyright holder saying it can be
distributed under the terms of this License. Such a notice grants a
world-wide, royalty-free license, unlimited in duration, to use that
work under the conditions stated herein. The "Document", below,
refers to any such manual or work. Any member of the public is a
licensee, and is addressed as "you". You accept the license if you
copy, modify or distribute the work in a way requiring permission
under copyright law.
A "Modified Version" of the Document means any work containing the
Document or a portion of it, either copied verbatim, or with
modifications and/or translated into another language.
A "Secondary Section" is a named appendix or a front-matter section of
the Document that deals exclusively with the relationship of the
publishers or authors of the Document to the Document's overall
subject (or to related matters) and contains nothing that could fall
directly within that overall subject. (Thus, if the Document is in
part a textbook of mathematics, a Secondary Section may not explain
any mathematics.) The relationship could be a matter of historical
connection with the subject or with related matters, or of legal,
commercial, philosophical, ethical or political position regarding
them.
The "Invariant Sections" are certain Secondary Sections whose titles
are designated, as being those of Invariant Sections, in the notice
that says that the Document is released under this License. If a
section does not fit the above definition of Secondary then it is not
allowed to be designated as Invariant. The Document may contain zero
Invariant Sections. If the Document does not identify any Invariant
Sections then there are none.
The "Cover Texts" are certain short passages of text that are listed,
as Front-Cover Texts or Back-Cover Texts, in the notice that says that
the Document is released under this License. A Front-Cover Text may
be at most 5 words, and a Back-Cover Text may be at most 25 words.
A "Transparent" copy of the Document means a machine-readable copy,
represented in a format whose specification is available to the
general public, that is suitable for revising the document
straightforwardly with generic text editors or (for images composed of
pixels) generic paint programs or (for drawings) some widely available
drawing editor, and that is suitable for input to text formatters or
for automatic translation to a variety of formats suitable for input
to text formatters. A copy made in an otherwise Transparent file
format whose markup, or absence of markup, has been arranged to thwart
or discourage subsequent modification by readers is not Transparent.
An image format is not Transparent if used for any substantial amount
of text. A copy that is not "Transparent" is called "Opaque".
Examples of suitable formats for Transparent copies include plain
ASCII without markup, Texinfo input format, LaTeX input format, SGML
or XML using a publicly available DTD, and standard-conforming simple
HTML, PostScript or PDF designed for human modification. Examples of
transparent image formats include PNG, XCF and JPG. Opaque formats
include proprietary formats that can be read and edited only by
proprietary word processors, SGML or XML for which the DTD and/or
processing tools are not generally available, and the
machine-generated HTML, PostScript or PDF produced by some word
processors for output purposes only.
The "Title Page" means, for a printed book, the title page itself,
plus such following pages as are needed to hold, legibly, the material
this License requires to appear in the title page. For works in
formats which do not have any title page as such, "Title Page" means
the text near the most prominent appearance of the work's title,
preceding the beginning of the body of the text.
The "publisher" means any person or entity that distributes copies of
the Document to the public.
A section "Entitled XYZ" means a named subunit of the Document whose
title either is precisely XYZ or contains XYZ in parentheses following
text that translates XYZ in another language. (Here XYZ stands for a
specific section name mentioned below, such as "Acknowledgements",
"Dedications", "Endorsements", or "History".) To "Preserve the Title"
of such a section when you modify the Document means that it remains a
section "Entitled XYZ" according to this definition.
The Document may include Warranty Disclaimers next to the notice which
states that this License applies to the Document. These Warranty
Disclaimers are considered to be included by reference in this
License, but only as regards disclaiming warranties: any other
implication that these Warranty Disclaimers may have is void and has
no effect on the meaning of this License.
2. VERBATIM COPYING
You may copy and distribute the Document in any medium, either
commercially or noncommercially, provided that this License, the
copyright notices, and the license notice saying this License applies
to the Document are reproduced in all copies, and that you add no
other conditions whatsoever to those of this License. You may not use
technical measures to obstruct or control the reading or further
copying of the copies you make or distribute. However, you may accept
compensation in exchange for copies. If you distribute a large enough
number of copies you must also follow the conditions in section 3.
You may also lend copies, under the same conditions stated above, and
you may publicly display copies.
3. COPYING IN QUANTITY
If you publish printed copies (or copies in media that commonly have
printed covers) of the Document, numbering more than 100, and the
Document's license notice requires Cover Texts, you must enclose the
copies in covers that carry, clearly and legibly, all these Cover
Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on
the back cover. Both covers must also clearly and legibly identify
you as the publisher of these copies. The front cover must present
the full title with all words of the title equally prominent and
visible. You may add other material on the covers in addition.
Copying with changes limited to the covers, as long as they preserve
the title of the Document and satisfy these conditions, can be treated
as verbatim copying in other respects.
If the required texts for either cover are too voluminous to fit
legibly, you should put the first ones listed (as many as fit
reasonably) on the actual cover, and continue the rest onto adjacent
pages.
If you publish or distribute Opaque copies of the Document numbering
more than 100, you must either include a machine-readable Transparent
copy along with each Opaque copy, or state in or with each Opaque copy
a computer-network location from which the general network-using
public has access to download using public-standard network protocols
a complete Transparent copy of the Document, free of added material.
If you use the latter option, you must take reasonably prudent steps,
when you begin distribution of Opaque copies in quantity, to ensure
that this Transparent copy will remain thus accessible at the stated
location until at least one year after the last time you distribute an
Opaque copy (directly or through your agents or retailers) of that
edition to the public.
It is requested, but not required, that you contact the authors of the
Document well before redistributing any large number of copies, to
give them a chance to provide you with an updated version of the
Document.
4. MODIFICATIONS
You may copy and distribute a Modified Version of the Document under
the conditions of sections 2 and 3 above, provided that you release
the Modified Version under precisely this License, with the Modified
Version filling the role of the Document, thus licensing distribution
and modification of the Modified Version to whoever possesses a copy
of it. In addition, you must do these things in the Modified Version:
A. Use in the Title Page (and on the covers, if any) a title distinct
from that of the Document, and from those of previous versions
(which should, if there were any, be listed in the History section
of the Document). You may use the same title as a previous version
if the original publisher of that version gives permission.
B. List on the Title Page, as authors, one or more persons or entities
responsible for authorship of the modifications in the Modified
Version, together with at least five of the principal authors of the
Document (all of its principal authors, if it has fewer than five),
unless they release you from this requirement.
C. State on the Title page the name of the publisher of the
Modified Version, as the publisher.
D. Preserve all the copyright notices of the Document.
E. Add an appropriate copyright notice for your modifications
adjacent to the other copyright notices.
F. Include, immediately after the copyright notices, a license notice
giving the public permission to use the Modified Version under the
terms of this License, in the form shown in the Addendum below.
G. Preserve in that license notice the full lists of Invariant Sections
and required Cover Texts given in the Document's license notice.
H. Include an unaltered copy of this License.
I. Preserve the section Entitled "History", Preserve its Title, and add
to it an item stating at least the title, year, new authors, and
publisher of the Modified Version as given on the Title Page. If
there is no section Entitled "History" in the Document, create one
stating the title, year, authors, and publisher of the Document as
given on its Title Page, then add an item describing the Modified
Version as stated in the previous sentence.
J. Preserve the network location, if any, given in the Document for
public access to a Transparent copy of the Document, and likewise
the network locations given in the Document for previous versions
it was based on. These may be placed in the "History" section.
You may omit a network location for a work that was published at
least four years before the Document itself, or if the original
publisher of the version it refers to gives permission.
K. For any section Entitled "Acknowledgements" or "Dedications",
Preserve the Title of the section, and preserve in the section all
the substance and tone of each of the contributor acknowledgements
and/or dedications given therein.
L. Preserve all the Invariant Sections of the Document,
unaltered in their text and in their titles. Section numbers
or the equivalent are not considered part of the section titles.
M. Delete any section Entitled "Endorsements". Such a section
may not be included in the Modified Version.
N. Do not retitle any existing section to be Entitled "Endorsements"
or to conflict in title with any Invariant Section.
O. Preserve any Warranty Disclaimers.
If the Modified Version includes new front-matter sections or
appendices that qualify as Secondary Sections and contain no material
copied from the Document, you may at your option designate some or all
of these sections as invariant. To do this, add their titles to the
list of Invariant Sections in the Modified Version's license notice.
These titles must be distinct from any other section titles.
You may add a section Entitled "Endorsements", provided it contains
nothing but endorsements of your Modified Version by various
parties--for example, statements of peer review or that the text has
been approved by an organization as the authoritative definition of a
standard.
You may add a passage of up to five words as a Front-Cover Text, and a
passage of up to 25 words as a Back-Cover Text, to the end of the list
of Cover Texts in the Modified Version. Only one passage of
Front-Cover Text and one of Back-Cover Text may be added by (or
through arrangements made by) any one entity. If the Document already
includes a cover text for the same cover, previously added by you or
by arrangement made by the same entity you are acting on behalf of,
you may not add another; but you may replace the old one, on explicit
permission from the previous publisher that added the old one.
The author(s) and publisher(s) of the Document do not by this License
give permission to use their names for publicity for or to assert or
imply endorsement of any Modified Version.
5. COMBINING DOCUMENTS
You may combine the Document with other documents released under this
License, under the terms defined in section 4 above for modified
versions, provided that you include in the combination all of the
Invariant Sections of all of the original documents, unmodified, and
list them all as Invariant Sections of your combined work in its
license notice, and that you preserve all their Warranty Disclaimers.
The combined work need only contain one copy of this License, and
multiple identical Invariant Sections may be replaced with a single
copy. If there are multiple Invariant Sections with the same name but
different contents, make the title of each such section unique by
adding at the end of it, in parentheses, the name of the original
author or publisher of that section if known, or else a unique number.
Make the same adjustment to the section titles in the list of
Invariant Sections in the license notice of the combined work.
In the combination, you must combine any sections Entitled "History"
in the various original documents, forming one section Entitled
"History"; likewise combine any sections Entitled "Acknowledgements",
and any sections Entitled "Dedications". You must delete all sections
Entitled "Endorsements".
6. COLLECTIONS OF DOCUMENTS
You may make a collection consisting of the Document and other
documents released under this License, and replace the individual
copies of this License in the various documents with a single copy
that is included in the collection, provided that you follow the rules
of this License for verbatim copying of each of the documents in all
other respects.
You may extract a single document from such a collection, and
distribute it individually under this License, provided you insert a
copy of this License into the extracted document, and follow this
License in all other respects regarding verbatim copying of that
document.
7. AGGREGATION WITH INDEPENDENT WORKS
A compilation of the Document or its derivatives with other separate
and independent documents or works, in or on a volume of a storage or
distribution medium, is called an "aggregate" if the copyright
resulting from the compilation is not used to limit the legal rights
of the compilation's users beyond what the individual works permit.
When the Document is included in an aggregate, this License does not
apply to the other works in the aggregate which are not themselves
derivative works of the Document.
If the Cover Text requirement of section 3 is applicable to these
copies of the Document, then if the Document is less than one half of
the entire aggregate, the Document's Cover Texts may be placed on
covers that bracket the Document within the aggregate, or the
electronic equivalent of covers if the Document is in electronic form.
Otherwise they must appear on printed covers that bracket the whole
aggregate.
8. TRANSLATION
Translation is considered a kind of modification, so you may
distribute translations of the Document under the terms of section 4.
Replacing Invariant Sections with translations requires special
permission from their copyright holders, but you may include
translations of some or all Invariant Sections in addition to the
original versions of these Invariant Sections. You may include a
translation of this License, and all the license notices in the
Document, and any Warranty Disclaimers, provided that you also include
the original English version of this License and the original versions
of those notices and disclaimers. In case of a disagreement between
the translation and the original version of this License or a notice
or disclaimer, the original version will prevail.
If a section in the Document is Entitled "Acknowledgements",
"Dedications", or "History", the requirement (section 4) to Preserve
its Title (section 1) will typically require changing the actual
title.
9. TERMINATION
You may not copy, modify, sublicense, or distribute the Document
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense, or distribute it is void, and
will automatically terminate your rights under this License.
However, if you cease all violation of this License, then your license
from a particular copyright holder is reinstated (a) provisionally,
unless and until the copyright holder explicitly and finally
terminates your license, and (b) permanently, if the copyright holder
fails to notify you of the violation by some reasonable means prior to
60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, receipt of a copy of some or all of the same material does
not give you any rights to use it.
10. FUTURE REVISIONS OF THIS LICENSE
The Free Software Foundation may publish new, revised versions of the
GNU Free Documentation 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. See
http://www.gnu.org/copyleft/.
Each version of the License is given a distinguishing version number.
If the Document specifies that a particular numbered version of this
License "or any later version" applies to it, you have the option of
following the terms and conditions either of that specified version or
of any later version that has been published (not as a draft) by the
Free Software Foundation. If the Document does not specify a version
number of this License, you may choose any version ever published (not
as a draft) by the Free Software Foundation. If the Document
specifies that a proxy can decide which future versions of this
License can be used, that proxy's public statement of acceptance of a
version permanently authorizes you to choose that version for the
Document.
11. RELICENSING
"Massive Multiauthor Collaboration Site" (or "MMC Site") means any
World Wide Web server that publishes copyrightable works and also
provides prominent facilities for anybody to edit those works. A
public wiki that anybody can edit is an example of such a server. A
"Massive Multiauthor Collaboration" (or "MMC") contained in the site
means any set of copyrightable works thus published on the MMC site.
"CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0
license published by Creative Commons Corporation, a not-for-profit
corporation with a principal place of business in San Francisco,
California, as well as future copyleft versions of that license
published by that same organization.
"Incorporate" means to publish or republish a Document, in whole or in
part, as part of another Document.
An MMC is "eligible for relicensing" if it is licensed under this
License, and if all works that were first published under this License
somewhere other than this MMC, and subsequently incorporated in whole or
in part into the MMC, (1) had no cover texts or invariant sections, and
(2) were thus incorporated prior to November 1, 2008.
The operator of an MMC Site may republish an MMC contained in the site
under CC-BY-SA on the same site at any time before August 1, 2009,
provided the MMC is eligible for relicensing.
ADDENDUM: How to use this License for your documents
To use this License in a document you have written, include a copy of
the License in the document and put the following copyright and
license notices just after the title page:
Copyright (c) YEAR YOUR NAME.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3
or any later version published by the Free Software Foundation;
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
A copy of the license is included in the section entitled "GNU
Free Documentation License".
If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts,
replace the "with...Texts." line with this:
with the Invariant Sections being LIST THEIR TITLES, with the
Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
If you have Invariant Sections without Cover Texts, or some other
combination of the three, merge those two alternatives to suit the
situation.
If your document contains nontrivial examples of program code, we
recommend releasing these examples in parallel under your choice of
free software license, such as the GNU General Public License,
to permit their use in free software.
Plinth-0.8.1/HACKING 0000664 0000000 0000000 00000012216 12660516711 0013754 0 ustar 00root root 0000000 0000000 # Hacking
## Installing for Development
1. Instead of running "setup.py install" after every source modification, run
the following command:
$ sudo python3 setup.py develop
This will install the python package in a special development mode. Run it
normally. Any updates to the code (and core package data files) do not
require re-installation after every modification.
CherryPy web server also monitors changes to the source files and reloads
the server as soon as a file is modified. Hence it is usually sufficient
to modify the source and refresh the browser page to see the changes.
2. Plinth also support running without installing (as much as possible).
Simply run it as:
$ sudo ./run --no-daemon --debug
In this mode, Plinth runs in working directory without need for
installation. It uses the plinth.conf config file in the working
directory if no regular config file (/etc/plinth/plinth.conf) is found.
It creates all that data and runtime files in data/var/*.
*Note:* This mode is supported only in a limited manner. The following are
the unknown issues with it:
1. Help pages are also not built. Run 'make -C doc' manually.
2. Actions do not work when running as normal user without 'sudo' prefix.
You need to add 'actions' directory to be allowed for 'sudo' commands.
See data/etc/sudoers.d/plinth for a hint.
## Running Tests
1. Run tests:
$ python3 setup.py test
## Running the Test Coverage Analysis
1. Run the coverage tool:
$ python3 setup.py test_coverage
Invoking this command generates a binary-format '.coverage' data file in
the top-level project directory which is recreated with each run, and
writes a set of HTML and other supporting files which comprise the
browsable coverage report to the 'plinth/tests/coverage/report' directory.
Index.html presents the coverage summary, broken down by module. Data
columns can be sorted by clicking on the column header or by using mnemonic
hot-keys specified in the keyboard widget in the upper-right corner of the
page. Clicking on the name of a particular source file opens a page that
displays the contents of that file, with color-coding in the left margin to
indicate which statements or branches were executed via the tests (green)
and which statements or branches were not executed (red).
## Testing Inside a Virtual Machine
1. Checkout source on the host.
2. Share the source folder and mount it on virtual machine. This could be done
over NFS, SSH-fs or 'Shared Folders' feature on VirtualBox.
3. Run 'setup.py develop' or 'setup.py install' as described above on guest
machine.
4. Access the guest machine's Plinth web UI from host after setting bridging or
NATing for guest virtual machine.
## Building the Documentation Separately
Plinth man page is built from DocBook source in the doc/ directory.
FreedomBox manual is downloaded from the wiki is also available there.
Both these are build during the installation process.
1. To build the documentation separately, run:
$ make -C doc
## Repository
Plinth is available from [GitHub](https://github.com/freedombox/plinth).
## Bugs & TODO
You can report bugs on Plinth's [issue
tracker](https://github.com/freedombox/Plinth/issues).
For new developers looking to start contributing to the project, this
is a good place to pick up a task to work on. Tasks that are labeled
as 'beginner' are easy to work on and have a known solution. Also,
other developers are ready to guide you on the implementation for such
tasks.
Feel free to pickup a task from the issue by announcing it on the
issue or by creating a new issue for whatever task you are going to
work on.
## Submitting Your Changes
Once you have completed implementing the solution, request a merge
into the upstream.
Pacthes can be submitted in either of the two ways:
- Post your patches to the FreedomBox discuss mailing list. Look at
Git documention on how to create submittable patches out of your
commits and post them to the list.
- Create a pull request on Github. For information on placing a merge
request, consult GitHub documentation.
## Coding Practices
Plinth confirms to [PEP 8](http://www.python.org/dev/peps/pep-0008/) Python
coding standard. You should check your code with *pep8* and *pylint* tools
before placing a merge request.
## Internationalization
Every module should `from gettext import gettext as _` and wrap
displayed strings with _(). We don't have the language stuff in place
yet (we have no translation files), but we need to put the
infrastructure in place for it from the start. Use it like this:
log.error(_("Couldn't import %s: %s"), path, e)
## Translations
Introduce yourself on #freedombox IRC (irc.debian.org) and start translating
the PO file from your language directory from:
Plinth/plinth/locale/
Introducing yourself is important since some work may have been done already
on Debian translators discussion lists and Transifex localization platform.
https://www.transifex.com/freedombox/
https://www.debian.org/MailingLists/subscribe
For more information on translations: https://wiki.debian.org/FreedomBox/Translate
Plinth-0.8.1/INSTALL 0000664 0000000 0000000 00000001645 12660516711 0014022 0 ustar 00root root 0000000 0000000 # Installing Plinth
1. Install the dependencies:
On a Debian based system, run:
$ sudo apt-get install \
augeas-tools \
dblatex \
gettext \
gir1.2-glib-2.0 \
gir1.2-networkmanager-1.0 \
gir1.2-packagekitglib-1.0 \
ldapscripts \
libjs-bootstrap \
libjs-jquery \
libjs-modernizr \
make \
network-manager \
packagekit \
ppp \
pppoe \
python3 \
python3-augeas \
python3-bootstrapform \
python3-cherrypy3 \
python3-coverage \
python3-django \
python3-django-stronghold \
python3-gi \
python3-psutil \
python3-setuptools \
python3-yaml \
xmlto
2. Install Plinth:
Unzip the source into a directory. Change to the directory containing the
program and run:
$ sudo python3 setup.py install
3. Run Plinth:
$ sudo plinth
4. Access Plinth UI:
Plinth UI should be accessible at http://localhost:8000
Plinth-0.8.1/LICENSES 0000664 0000000 0000000 00000005744 12660516711 0014125 0 ustar 00root root 0000000 0000000 # -*- mode: org; mode: org-indent; mode: visual-line; -*-
Files containing license headers are not listed here. For each of the
files listed, the copyright specified for the file is listed. "-" means
"no license specified in the file," licenses are specified and linked
otherwise.
- COPYING :: N/A
- HACKING :: -
- INSTALL :: -
- plinth.config :: -
- README :: -
- data/etc/apache2/plinth.conf :: -
- data/etc/apache2/plinth-ssl.conf :: -
- data/etc/sudoers.d/plinth :: -
- static/themes/default/FreedomBox-Identity-Manual.pdf :: -
- static/themes/default/FreedomBox-Logo.7z :: [[http://thread.gmane.org/gmane.linux.debian.freedombox.user/4124/focus=4439][GPL3+/CC-BY-SA]]
- static/themes/default/readme.md :: [[file:themes/default/readme.md::This%20theme%20is%20free%20software%20offered%20to%20you%20under%20the%20terms%20of%20the%20GNU%20Affero%20General%20Public%20License,%20Version%203%20or%20later:][GNU Affero General Public License Version 3]]
- static/themes/default/screenshot.png :: -
- static/themes/default/img/apple-touch-icon-114px-precomposed.png :: -
- static/themes/default/img/apple-touch-icon-57px-precomposed.png :: -
- static/themes/default/img/apple-touch-icon-72px-precomposed.png :: -
- static/themes/default/img/apple-touch-icon.xcf :: -
- static/themes/default/img/favicon-32px.ico :: -
- static/themes/default/img/favicon.ico :: -
- static/themes/default/img/freedombox-logo-200px.png :: -
- static/themes/default/img/freedombox-logo-250px.png :: -
- static/themes/default/img/freedombox-logo-32px.png :: -
- static/themes/default/img/FreedomBox-logo-grayscale-negative.png :: -
- static/themes/default/img/FreedomBox-logo-grayscale-negative.svg :: -
- static/themes/default/img/FreedomBox-logo-grayscale.png :: -
- static/themes/default/img/FreedomBox-logo-grayscale.svg :: -
- static/themes/default/img/FreedomBox-logo-lineart-negative.png :: -
- static/themes/default/img/FreedomBox-logo-lineart-negative.svg :: -
- static/themes/default/img/FreedomBox-logo-lineart.png :: -
- static/themes/default/img/FreedomBox-logo-lineart.svg :: -
- static/themes/default/img/FreedomBox-logo-standard.png :: -
- static/themes/default/img/FreedomBox-logo-standard.svg :: -
- static/themes/default/img/freedombox-logotype.png :: -
- static/themes/default/img/network-computer.svg :: [[https://commons.wikimedia.org/wiki/File:Gnome-computer.svg][LGPLv3+]]
- static/themes/default/img/network-connection.svg :: [[http://tango.freedesktop.org/][Public Domain]]
- static/themes/default/img/network-connection-vertical.svg :: [[http://tango.freedesktop.org/][Public Domain]]
- static/themes/default/img/network-ethernet.svg :: [[http://tango.freedesktop.org/][Public Domain]]
- static/themes/default/img/network-freedombox.svg :: [[http://thread.gmane.org/gmane.linux.debian.freedombox.user/4124/focus=4439][GPL3+/CC-BY-SA]]
- static/themes/default/img/network-internet.svg :: [[http://tango.freedesktop.org/][Public Domain]]
- static/themes/default/img/network-wireless.svg :: [[http://tango.freedesktop.org/][Public Domain]]
Plinth-0.8.1/README.md 0000664 0000000 0000000 00000004003 12660516711 0014237 0 ustar 00root root 0000000 0000000 [](https://travis-ci.org/freedombox/Plinth)
[](https://hosted.weblate.org/engage/freedombox/?utm_source=widget)
# Plinth
[Plinth](https://wiki.debian.org/FreedomBox/Plinth) - a web front end
for administering [FreedomBox](https://freedomboxfoundation.org/)
# Description
FreedomBox is a community project to develop, design and promote
personal servers running free software for private, personal
communications. It is a networking appliance designed to allow
interfacing with the rest of the Internet under conditions of
protected privacy and data security. It hosts applications such as
blog, wiki, website, social network, email, web proxy and a Tor relay,
on a device that can replace your Wi-Fi router, so that your data
stays with you.
Plinth is a web interface to administer the functions of the
FreedomBox. It is extensible and provides various applications of
FreedomBox as modules. Each module or application provides simplified
user interface to control the underlying functionality. As FreedomBox
can act as a wireless router, it is possible to configure networking
from Plinth. Plinth also allows configuration of basic system
parameters such as time zone, hostname and automatic upgrades.
You can find more information about Plinth on the
[Plinth Wiki](https://wiki.debian.org/FreedomBox/Plinth) page,
the [FreedomBox Wiki](https://wiki.debian.org/FreedomBox/) and the
[FreedomBox Manual](https://wiki.debian.org/FreedomBox/Manual).
# Getting Started
See the INSTALL file for additional details and dependencies. To install run:
$ sudo python3 setup.py install
Run Plinth on the local system with:
$ sudo plinth
# Contributing
See the HACKING file for contributing to Plinth.
# Localization
[](https://hosted.weblate.org/engage/freedombox/?utm_source=widget)
Plinth-0.8.1/actions/ 0000775 0000000 0000000 00000000000 12660516711 0014423 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/actions/avahi 0000775 0000000 0000000 00000003230 12660516711 0015437 0 ustar 00root root 0000000 0000000 #!/usr/bin/python3
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
"""
Configuration helper for service discovery.
"""
import argparse
from plinth import action_utils
def parse_arguments():
"""Return parsed command line arguments as dictionary."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
subparsers.add_parser('enable', help='Enable avahi-daemon service')
subparsers.add_parser('disable', help='Disable avahi-daemon service')
return parser.parse_args()
def subcommand_enable(_):
"""Start service."""
action_utils.service_enable('avahi-daemon')
def subcommand_disable(_):
"""Stop service."""
action_utils.service_disable('avahi-daemon')
def main():
"""Parse arguments and perform all duties."""
arguments = parse_arguments()
subcommand = arguments.subcommand.replace('-', '_')
subcommand_method = globals()['subcommand_' + subcommand]
subcommand_method(arguments)
if __name__ == '__main__':
main()
Plinth-0.8.1/actions/datetime 0000775 0000000 0000000 00000003210 12660516711 0016141 0 ustar 00root root 0000000 0000000 #!/usr/bin/python3
# -*- mode: python -*-
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
"""
Configuration helper for date and time
"""
import argparse
from plinth import action_utils
def parse_arguments():
"""Return parsed command line arguments as dictionary."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
subparsers.add_parser('enable', help='Enable NTP service')
subparsers.add_parser('disable', help='Disable NTP service')
return parser.parse_args()
def subcommand_enable(_):
"""Start service."""
action_utils.service_enable('ntp')
def subcommand_disable(_):
"""Stop service."""
action_utils.service_disable('ntp')
def main():
"""Parse arguments and perform all duties."""
arguments = parse_arguments()
subcommand = arguments.subcommand.replace('-', '_')
subcommand_method = globals()['subcommand_' + subcommand]
subcommand_method(arguments)
if __name__ == '__main__':
main()
Plinth-0.8.1/actions/deluge 0000775 0000000 0000000 00000005164 12660516711 0015624 0 ustar 00root root 0000000 0000000 #!/usr/bin/python3
# -*- mode: python -*-
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
"""
Configuration helper for BitTorrent web client.
"""
import argparse
import os
import subprocess
from plinth import action_utils
SYSTEMD_SERVICE_PATH = '/etc/systemd/system/deluge-web.service'
SYSTEMD_SERVICE = '''
#
# This file is managed and overwritten by Plinth. If you wish to edit
# it, disable Deluge in Plinth, remove this file and manage it manually.
#
[Unit]
Description=Deluge Web Interface
Documentation=man:deluge-web(1)
After=network.target
[Service]
ExecStart=/usr/bin/deluge-web --base=deluge
Restart=on-failure
User=debian-deluged
Group=debian-deluged
[Install]
WantedBy=multi-user.target
'''
def parse_arguments():
"""Return parsed command line arguments as dictionary."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
subparsers.add_parser('enable', help='Enable deluge-web site')
subparsers.add_parser('disable', help='Disable deluge-web site')
return parser.parse_args()
def subcommand_enable(_):
"""Enable deluge-web site and start deluge-web."""
setup()
action_utils.service_enable('deluge-web')
action_utils.webserver_enable('deluge-plinth')
def subcommand_disable(_):
"""Disable deluge-web site and stop deluge-web."""
action_utils.webserver_disable('deluge-plinth')
action_utils.service_disable('deluge-web')
def setup():
"""Perform initial setup for deluge-web."""
if not os.path.isfile(SYSTEMD_SERVICE_PATH):
with open(SYSTEMD_SERVICE_PATH, 'w') as file_handle:
file_handle.write(SYSTEMD_SERVICE)
subprocess.check_call(['systemctl', 'daemon-reload'])
def main():
"""Parse arguments and perform all duties."""
arguments = parse_arguments()
subcommand = arguments.subcommand.replace('-', '_')
subcommand_method = globals()['subcommand_' + subcommand]
subcommand_method(arguments)
if __name__ == '__main__':
main()
Plinth-0.8.1/actions/domainname-change 0000775 0000000 0000000 00000001704 12660516711 0017706 0 ustar 00root root 0000000 0000000 #!/bin/sh
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
domainname="$1"
hostname=$(hostname)
if grep -q 127.0.1.1 /etc/hosts ; then
sed -i "s/127.0.1.1.*/127.0.1.1 $hostname.$domainname $hostname/" /etc/hosts
else
sed -i "/127.0.0.1.*/a \
127.0.1.1 $hostname.$domainname $hostname" /etc/hosts
fi
Plinth-0.8.1/actions/dynamicdns 0000775 0000000 0000000 00000036275 12660516711 0016517 0 ustar 00root root 0000000 0000000 #!/bin/bash
############################################################################
# #
# This file is part of Plinth. #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU Affero General Public License as #
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. #
# #
# You should have received a copy of the GNU Affero General Public License #
# along with this program. If not, see . #
# #
# #
# This script is a wrapper around ez-ipupdate and/or wget #
# to update a Dynamic DNS account. The script is used as an #
# interface between plinth and ez-ipupdate #
# the script will store configuration, return configuration #
# to plinth UI and do a dynamic DNS update. The script will #
# also determe if we are behind a NAT device, if we can use #
# ez-ipupdate tool or if we need to do some wget magic #
# #
# Todo: IPv6 #
# Todo: GET WAN IP from Router via UPnP if supported #
# Todo: licence string? #
# author: Daniel Steglich #
# #
############################################################################
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
# static values
WGET=$(which wget)
WGETOPTIONS="-4 -o /dev/null -t 3 -T 3"
EMPTYSTRING="none"
NOIP="0.0.0.0"
# how often do we poll for IP changes if we are behind a NAT?
UPDATEMINUTES=5
# if we do not have a IP check URL, how often should we do a "blind" update
UPDATEMINUTESUNKNOWN=3600
TOOLNAME=ez-ipupdate
UPDATE_TOOL=$(which ${TOOLNAME})
DISABLED_STRING='disabled'
ENABLED_STRING='enabled'
# Dirs and filenames
CFGDIR="/etc/${TOOLNAME}/"
CFG="${CFGDIR}${TOOLNAME}.conf"
CFG_disabled="${CFGDIR}${TOOLNAME}.inactive"
IPFILE="${CFGDIR}${TOOLNAME}.currentIP"
STATUSFILE="${CFGDIR}${TOOLNAME}.status"
LASTUPDATE="${CFGDIR}/last-update"
HELPERCFG="${CFGDIR}${TOOLNAME}-plinth.cfg"
CRONJOB="/etc/cron.d/${TOOLNAME}"
PIDFILE="/var/run/ez-ipupdate.pid"
# this function will parse commandline options
doGetOpt()
{
basicauth=0
ignoreCertError=0
while getopts ":s:d:u:P:I:U:c:b:p" opt; do
case ${opt} in
s)
if [ "${OPTARG}" != "${EMPTYSTRING}" ];then
server=${OPTARG}
else
server=""
fi
;;
d)
host=${OPTARG}
;;
u)
user=${OPTARG}
;;
P)
pass=${OPTARG}
;;
p)
if read -t 0; then
IFS= read -r pass
fi
;;
I)
if [ "${OPTARG}" != "${EMPTYSTRING}" ];then
ipurl=${OPTARG}
else
ipurl=""
fi
;;
U)
if [ "${OPTARG}" != "${EMPTYSTRING}" ];then
updateurl=${OPTARG}
else
updateurl=""
fi
;;
b)
basicauth=${OPTARG}
;;
c)
ignoreCertError=${OPTARG}
;;
\?)
echo "Invalid option: -${OPTARG}" >&2
exit 1
;;
esac
done
}
# this function will write a persistent config file to disk
doWriteCFG()
{
mkdir ${CFGDIR} 2> /dev/null
# always write to the inactive config - needs to be enabled via "start" command later
local out_file=${CFG_disabled}
# reset the last update time
echo 0 > ${LASTUPDATE}
# reset the last updated IP
echo "0.0.0.0" > ${IPFILE}
# reset last update (if there is one)
rm ${STATUSFILE} 2> /dev/null
# find the interface (always the default gateway interface)
default_interface=$(ip route |grep default |awk '{print $5}')
# store the given options in ez-ipupdate compatible config file
{
echo "host=${host}"
echo "server=${server}"
echo "user=${user}:${pass}"
echo "service-type=gnudip"
echo "retrys=3"
echo "wildcard"
} > ${out_file}
# store UPDATE URL params
{
echo "POSTURL ${updateurl}"
echo "POSTAUTH ${basicauth}"
echo "POSTSSLIGNORE ${ignoreCertError}"
} > ${HELPERCFG}
# check if we are behind a NAT Router
echo "IPURL ${ipurl}" >> ${HELPERCFG}
if [ -z ${ipurl} ];then
echo "NAT unknown" >> ${HELPERCFG}
else
doGetWANIP
ISGLOBAL=$(ip addr ls "${default_interface}" | grep "${wanip}")
if [ -z "${ISGLOBAL}" ];then
# we are behind NAT
echo "NAT yes" >> ${HELPERCFG}
else
# we are directly connected
echo "NAT no" >> ${HELPERCFG}
# if this file is added ez-ipupdate will take ip form this interface
{
echo "interface=${default_interface}"
# if this line is added to config file, ez-ipupdate will be launched on startup via init.d
echo "daemon"
echo "execute=${0} success"
} >> ${out_file}
fi
fi
}
# this function will read the config file from disk
# special treatment for empty strings is done here:
# plinth will give empty strings like: ''
# but we don't want this single quotes to be used
doReadCFG()
{
host=""
server=""
user=""
pass=""
ipurl=""
if [ ! -z "${cfgfile}" ];then
host=$(grep ^host= "${cfgfile}" 2> /dev/null | cut -d = -f 2-)
server=$(grep ^server= "${cfgfile}" 2> /dev/null | cut -d = -f 2- | grep -v ^\'\')
user=$(grep ^user= "${cfgfile}" 2> /dev/null | cut -d = -f 2- | cut -d : -f 1 )
pass=$(grep ^user= "${cfgfile}" 2> /dev/null | cut -d = -f 2- | cut -d : -f 2-)
fi
if [ ! -z ${HELPERCFG} ];then
ipurl=$(grep ^IPURL "${HELPERCFG}" 2> /dev/null |awk '{print $2}' |grep -v ^\'\')
updateurl=$(grep POSTURL "${HELPERCFG}" 2> /dev/null |awk '{print $2}' |grep -v ^\'\')
basicauth=$(grep POSTAUTH "${HELPERCFG}" 2> /dev/null |awk '{print $2}' |grep -v ^\'\')
ignoreCertError=$(grep POSTSSLIGNORE "${HELPERCFG}" 2> /dev/null |awk '{print $2}' |grep -v ^\'\')
fi
}
# replace vars from url: i.e.:
# https://example.com/update.php?domain=&User=&Pass=
# also this function will remove the surounding single quotes from the URL string
# as plinth will add them
doReplaceVars()
{
local url=$(echo "${updateurl}" | sed "s//${wanip}/g")
url=$(echo "${url}" | sed "s//${host}/g")
url=$(echo "${url}" | sed "s//${user}/g")
url=$(echo "${url}" | sed "s//${pass}/g")
url=$(echo "${url}" | sed "s/'//g")
updateurl=${url}
logger "expanded update URL as ${url}"
}
# doReadCFG() needs to be run before this
# this function will return all configured parameters in a way that
# plinth will understand (plinth know the order of
# parameters this function will return)
doStatus()
{
PROC=$(pgrep ${TOOLNAME})
if [ -f "${CRONJOB}" ];then
echo "${ENABLED_STRING}"
elif [ ! -z "${PROC}" ];then
echo "${ENABLED_STRING}"
else
echo "${DISABLED_STRING}"
fi
if [ ! -z "${server}" ];then
echo "${server}"
else
echo "${DISABLED_STRING}"
fi
if [ ! -z "${host}" ];then
echo "${host}"
else
echo "${DISABLED_STRING}"
fi
if [ ! -z "${user}" ];then
echo "${user}"
else
echo "${DISABLED_STRING}"
fi
if [ ! -z "${pass}" ];then
echo "${pass}"
else
echo "${DISABLED_STRING}"
fi
if [ ! -z "${ipurl}" ];then
echo "${ipurl}"
else
echo "${DISABLED_STRING}"
fi
if [ ! -z "${updateurl}" ];then
echo "${updateurl}"
else
echo "${DISABLED_STRING}"
fi
if [ ! -z "${ignoreCertError}" ];then
echo "${ignoreCertError}"
else
echo "${DISABLED_STRING}"
fi
if [ ! -z "${basicauth}" ];then
echo "${basicauth}"
else
echo "${DISABLED_STRING}"
fi
}
# ask a public WEB Server for the WAN IP we are comming from
# and store this ip within $wanip
doGetWANIP()
{
if [ ! -z "${ipurl}" ];then
outfile=$(mktemp)
local cmd="${WGET} ${WGETOPTIONS} -O ${outfile} ${ipurl}"
$cmd
wanip=$(sed s/[^0-9.]//g "${outfile}")
rm "${outfile}"
[ -z "${wanip}" ] && wanip=${NOIP}
else
# no WAN IP found because of missing check URL
wanip=${NOIP}
fi
}
# actualy do the update (using wget or ez-ipupdate or even both)
# this function is called via cronjob
doUpdate()
{
local dnsentry=$(nslookup "${host}"|tail -n2|grep A|sed s/[^0-9.]//g)
if [ "${dnsentry}" = "${wanip}" ];then
return
fi
if [ ! -z "${server}" ];then
start-stop-daemon -S -x "${UPDATE_TOOL}" -m -p "${PIDFILE}" -- -c "${cfgfile}"
fi
if [ ! -z "${updateurl}" ];then
doReplaceVars
if [ "${basicauth}" = "enabled" ];then
WGETOPTIONS="${WGETOPTIONS} --user ${user} --password ${pass} "
fi
if [ "${ignoreCertError}" = "enabled" ];then
WGETOPTIONS="${WGETOPTIONS} --no-check-certificate "
fi
local cmd="${WGET} ${WGETOPTIONS} ${updateurl}"
$cmd
# ToDo: check the returning text from WEB Server. User need to give expected string.
if [ ${?} -eq 0 ];then
${0} success ${wanip}
else
${0} failed
fi
fi
}
cmd=${1}
shift
# decide which config to use
cfgfile="/tmp/none"
[ -f ${CFG_disabled} ] && cfgfile=${CFG_disabled}
[ -f ${CFG} ] && cfgfile=${CFG}
# check what action is requested
case ${cmd} in
configure)
doGetOpt "${@}"
doWriteCFG
;;
start)
doGetWANIP
if [ "$(grep ^NAT ${HELPERCFG} | awk '{print $2}')" = "no" ];then
#if we are not behind a NAT device and we use gnudip, start the daemon tool
gnudipServer=$(grep ^server= ${cfgfile} 2> /dev/null | cut -d = -f 2- |grep -v ^\'\')
if [ ! -f ${CFG} -a ! -z "${gnudipServer}" ];then
mv ${CFG_disabled} ${CFG}
/etc/init.d/${TOOLNAME} start
fi
# if we are not behind a NAT device and we use update-URL, add a cronjob
# (daemon tool does not support update-URL feature)
if [ ! -z "$(grep ^POSTURL $HELPERCFG | awk '{print $2}')" ];then
echo "*/${UPDATEMINUTES} * * * * root ${0} update" > ${CRONJOB}
$0 update
fi
else
# if we are behind a NAT device, add a cronjob (daemon tool cannot monitor WAN IP changes)
echo "*/${UPDATEMINUTES} * * * * root ${0} update" > $CRONJOB
$0 update
fi
;;
get-nat)
NAT=$(grep ^NAT $HELPERCFG 2> /dev/null | awk '{print $2}')
[ -z "${NAT}" ] && NAT="unknown"
echo ${NAT}
;;
update)
doReadCFG
dnsentry=$(nslookup "${host}"|tail -n2|grep A|sed s/[^0-9.]//g)
doGetWANIP
echo ${wanip} > ${IPFILE}
grep -v execute ${cfgfile} > ${cfgfile}.tmp
mv ${cfgfile}.tmp ${cfgfile}
echo "execute=${0} success ${wanip}" >> ${cfgfile}
# if we know our WAN IP, only update if IP changes
if [ "${dnsentry}" != "${wanip}" -a "${wanip}" != ${NOIP} ];then
doUpdate
fi
# if we don't know our WAN IP do a blind update once a hour
if [ "${wanip}" = ${NOIP} ];then
currenttime=$(date +%s)
LAST=0
[ -f ${LASTUPDATE} ] && LAST=$(cat ${LASTUPDATE})
diff=$((currenttime - LAST))
if [ ${diff} -gt ${UPDATEMINUTESUNKNOWN} ];then
doUpdate
fi
fi
;;
stop)
rm ${CRONJOB} 2> /dev/null
/etc/init.d/${TOOLNAME} stop
kill "$(cat ${PIDFILE})" 2> /dev/null
mv ${CFG} ${CFG_disabled}
;;
success)
date=$(date)
echo "last update done (${date})" > ${STATUSFILE}
date +%s > ${LASTUPDATE}
# if called from cronjob, the current IP is given as parameter
if [ $# -eq 1 ];then
echo "${1}" > ${IPFILE}
else
# if called from ez-ipupdate daemon, no WAN IP is given as parameter
doGetWANIP
echo ${wanip} > ${IPFILE}
fi
;;
failed)
date=$(date)
echo "last update failed (${date})" > ${STATUSFILE}
;;
get-last-success)
if [ -f ${STATUSFILE} ];then
cat ${STATUSFILE}
else
echo "no successful update recorded since last config change"
fi
;;
status)
doReadCFG
doStatus
;;
get-timer)
echo ${UPDATEMINUTES}
;;
clean)
rm ${CFGDIR}/*
rm ${CRONJOB}
;;
*)
echo "usage: status|configure |start|stop|update|get-nat|clean|success [updated IP]|failed|get-last-success"
echo ""
echo "options are:"
echo "-s Gnudip Server address"
echo "-d Domain to be updated"
echo "-u Account username"
echo "-P Account password"
echo "-p Read Account Password from stdin"
echo "-I A URL which returns the IP of the client who is requesting"
echo "-U The update URL (a HTTP GET on this URL will be done)"
echo "-c <1|0> disable SSL check on Update URL"
echo "-b <1|0> use HTTP basic auth on Update URL"
echo ""
echo "update do a one time update"
echo "clean delete configuration"
echo "success store update success and optional the updated IP"
echo "failed store update failure"
echo "get-nat return the detected nat type"
echo "get-last-success return date of last successful update"
;;
esac
exit 0
Plinth-0.8.1/actions/firewall 0000775 0000000 0000000 00000006655 12660516711 0016172 0 ustar 00root root 0000000 0000000 #!/usr/bin/python3
# -*- mode: python -*-
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
"""
Configuration helper for Plinth firewall inteface
"""
import argparse
import subprocess
def parse_arguments():
"""Return parsed command line arguments as dictionary"""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
# Get status
subparsers.add_parser('get-status',
help='Get whether firewalld is running')
# Get service status
get_enabled_services = subparsers.add_parser(
'get-enabled-services', help='Get list of enabled services')
get_enabled_services.add_argument(
'--zone', help='Zone from which the list is to be retrieved',
required=True)
# Add a service
add_service = subparsers.add_parser('add-service', help='Add a service')
add_service.add_argument('service', help='Name of the service to add')
add_service.add_argument('--zone',
help='Zone to which service is to be added',
required=True)
# Remove a service status
remove_service = subparsers.add_parser('remove-service',
help='Remove a service')
remove_service.add_argument('service',
help='Name of the service to remove')
remove_service.add_argument(
'--zone', help='Zone from which service is to be removed',
required=True)
return parser.parse_args()
def subcommand_get_status(_):
"""Print status of the firewalld service"""
subprocess.call(['firewall-cmd', '--state'])
def subcommand_get_enabled_services(arguments):
"""Print the status of variours services"""
subprocess.call(['firewall-cmd', '--zone', arguments.zone,
'--list-services'])
def subcommand_add_service(arguments):
"""Permit a service in the firewall"""
subprocess.call(['firewall-cmd', '--zone', arguments.zone, '--add-service',
arguments.service])
subprocess.call(['firewall-cmd', '--zone', arguments.zone, '--permanent',
'--add-service', arguments.service])
def subcommand_remove_service(arguments):
"""Block a service in the firewall"""
subprocess.call(['firewall-cmd', '--zone', arguments.zone,
'--remove-service', arguments.service])
subprocess.call(['firewall-cmd', '--zone', arguments.zone, '--permanent',
'--remove-service', arguments.service])
def main():
"""Parse arguments and perform all duties"""
arguments = parse_arguments()
subcommand = arguments.subcommand.replace('-', '_')
subcommand_method = globals()['subcommand_' + subcommand]
subcommand_method(arguments)
if __name__ == "__main__":
main()
Plinth-0.8.1/actions/hostname-change 0000775 0000000 0000000 00000001760 12660516711 0017416 0 ustar 00root root 0000000 0000000 #!/bin/sh
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
hostname="$1"
if [ -d /run/systemd/system ] ; then
hostnamectl set-hostname --transient --static "$hostname"
else
echo "$hostname" > /etc/hostname
if [ -x /etc/init.d/hostname.sh ] ; then
invoke-rc.d hostname.sh start
else
service hostname start
fi
fi
service avahi-daemon restart
Plinth-0.8.1/actions/ikiwiki 0000775 0000000 0000000 00000011301 12660516711 0016005 0 ustar 00root root 0000000 0000000 #!/usr/bin/python3
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
"""
Configuration helper for ikiwiki
"""
import argparse
import os
import shutil
import subprocess
import sys
from plinth import action_utils
SETUP_WIKI = '/etc/ikiwiki/plinth-wiki.setup'
SETUP_BLOG = '/etc/ikiwiki/plinth-blog.setup'
SITE_PATH = '/var/www/ikiwiki'
WIKI_PATH = '/var/lib/ikiwiki'
def parse_arguments():
"""Return parsed command line arguments as dictionary."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
# Setup ikiwiki site
subparsers.add_parser('setup', help='Perform first time setup operations')
# Enable ikiwiki site
subparsers.add_parser('enable', help='Enable ikiwiki site')
# Disable ikiwiki site
subparsers.add_parser('disable', help='Disable ikiwiki site')
# Get wikis and blogs
subparsers.add_parser('get-sites', help='Get wikis and blogs')
# Create a wiki
create_wiki = subparsers.add_parser('create-wiki', help='Create a wiki')
create_wiki.add_argument('--wiki_name', help='Name of new wiki')
create_wiki.add_argument('--admin_name', help='Administrator account name')
# Create a blog
create_blog = subparsers.add_parser('create-blog', help='Create a blog')
create_blog.add_argument('--blog_name', help='Name of new blog')
create_blog.add_argument('--admin_name', help='Administrator account name')
# Delete a wiki or blog
delete = subparsers.add_parser('delete', help='Delete a wiki or blog.')
delete.add_argument('--name', help='Name of wiki or blog to delete.')
return parser.parse_args()
def subcommand_setup(_):
"""Perform first time setup operations."""
setup()
def subcommand_enable(_):
"""Enable ikiwiki site."""
action_utils.webserver_enable('ikiwiki-plinth')
def subcommand_disable(_):
"""Disable ikiwiki site."""
action_utils.webserver_disable('ikiwiki-plinth')
def subcommand_get_sites(_):
"""Get wikis and blogs."""
try:
sites = os.listdir(SITE_PATH)
print('\n'.join(sites))
except FileNotFoundError:
pass
def subcommand_create_wiki(arguments):
"""Create a wiki."""
pw_bytes = sys.stdin.read().encode()
proc = subprocess.Popen(
['ikiwiki', '-setup', SETUP_WIKI,
arguments.wiki_name, arguments.admin_name],
stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
outs, errs = proc.communicate(input=pw_bytes + b'\n' + pw_bytes)
print(outs)
print(errs)
def subcommand_create_blog(arguments):
"""Create a blog."""
pw_bytes = sys.stdin.read().encode()
proc = subprocess.Popen(
['ikiwiki', '-setup', SETUP_BLOG,
arguments.blog_name, arguments.admin_name],
stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
outs, errs = proc.communicate(input=pw_bytes + b'\n' + pw_bytes)
print(outs)
print(errs)
def subcommand_delete(arguments):
"""Delete a wiki or blog."""
html_folder = os.path.join(SITE_PATH, arguments.name)
wiki_folder = os.path.join(WIKI_PATH, arguments.name)
try:
shutil.rmtree(html_folder)
shutil.rmtree(wiki_folder)
shutil.rmtree(wiki_folder + '.git')
os.remove(wiki_folder + '.setup')
print('Deleted {0}'.format(arguments.name))
except FileNotFoundError:
print('Error: {0} not found.'.format(arguments.name))
exit(1)
def setup():
"""Write Apache configuration and wiki/blog setup scripts."""
if not os.path.exists(SITE_PATH):
os.makedirs(SITE_PATH)
with action_utils.WebserverChange() as webserver_change:
webserver_change.enable('cgi', kind='module')
webserver_change.enable('authnz_ldap', kind='module')
webserver_change.enable('ikiwiki-plinth')
def main():
"""Parse arguments and perform all duties."""
arguments = parse_arguments()
subcommand = arguments.subcommand.replace('-', '_')
subcommand_method = globals()['subcommand_' + subcommand]
subcommand_method(arguments)
if __name__ == '__main__':
main()
Plinth-0.8.1/actions/ldap 0000775 0000000 0000000 00000010030 12660516711 0015263 0 ustar 00root root 0000000 0000000 #!/bin/bash
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
# Store anything available from stdin.
# This is used to receive passwords from Plinth.
if read -t 0; then
IFS= read -r input
fi
set -e # Exit on failure
# XXX: ldapscripts has an issue that it can't properly extract
# built-in templates under certain locales due to grep command
# recognizing the source file as binary. Remove using this once the
# bug is fixed. Passing '-a' as argument to grep seems to be a
# solution.
export LC_ALL=C
create_user()
{
username="$1"
password="$2"
# All users shall have 'users' (a group in /etc/group) as primary group.
ldapadduser $username users > /dev/null
set_user_password $username $password
}
delete_user()
{
username="$1"
groups=$(get_user_groups $username)
ldapdeleteuser $username
while read -r group; do
ldapdeleteuserfromgroup $username $group > /dev/null || true
done <<< "$groups"
}
rename_user()
{
old_username="$1"
new_username="$2"
groups=$(get_user_groups $old_username)
ldaprenameuser $old_username $new_username
while read -r group; do
ldapdeleteuserfromgroup $old_username $group > /dev/null || true
ldapaddusertogroup $new_username $group > /dev/null || true
done <<< "$groups"
}
set_user_password()
{
username="$1"
password=$(slappasswd -s "$2")
# XXX: Use ldapsetpasswd as soon as ldapscripts can handle
# changing passwords with SASL auth EXTERNAL.
cat < /dev/null
dn: uid=$username,ou=Users,dc=thisbox
changetype: modify
replace: userPassword
userPassword: $password
EOF
}
get_user_groups()
{
# Return only supplimentary groups and don't include the 'users'
# primary group.
username="$1"
ldapid $username | cut -f 3 -d ' ' | cut -d = -f 2 | sed 's+,+\n+g' | sed "s+.*(\(.*\))+\1+" | grep -v users || true
}
add_user_to_group()
{
username="$1"
groupname="$2"
# Try to create group and ignore failure if group already exists
ldapaddgroup $groupname > /dev/null 2>&1 || true
ldapaddusertogroup $username $groupname > /dev/null
}
remove_user_from_group()
{
username="$1"
groupname="$2"
ldapdeleteuserfromgroup $username $groupname > /dev/null
}
setup()
{
# XXX: Password setting on users is disabled as changing passwords
# using SASL Auth is not supported.
cat < /dev/null
set /files/etc/ldapscripts/ldapscripts.conf/SERVER '"ldapi://"'
set /files/etc/ldapscripts/ldapscripts.conf/SASLAUTH '"EXTERNAL"'
set /files/etc/ldapscripts/ldapscripts.conf/SUFFIX '"dc=thisbox"'
set /files/etc/ldapscripts/ldapscripts.conf/USUFFIX '"ou=Users"'
set /files/etc/ldapscripts/ldapscripts.conf/GSUFFIX '"ou=Groups"'
set /files/etc/ldapscripts/ldapscripts.conf/PASSWORDGEN '"true"'
save
EOF
}
setup
command=$1
shift
case $command in
create-user)
create_user "$1" "$input"
;;
delete-user)
delete_user "$@"
;;
rename-user)
rename_user "$@"
;;
set-user-password)
set_user_password "$1" "$input"
;;
get-user-groups)
get_user_groups "$@"
;;
add-user-to-group)
add_user_to_group "$@"
;;
remove-user-from-group)
remove_user_from_group "$@"
;;
*)
echo "Invalid sub-command"
exit -1
;;
esac
Plinth-0.8.1/actions/letsencrypt 0000775 0000000 0000000 00000013560 12660516711 0016732 0 ustar 00root root 0000000 0000000 #!/usr/bin/python3
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
"""
Configuration helper for Let's Encrypt.
"""
import argparse
import json
import os
import subprocess
import sys
from plinth import action_utils
TEST_MODE = False
LIVE_DIRECTORY = '/etc/letsencrypt/live/'
APACHE_PREFIX = '/etc/apache2/sites-available/'
APACHE_CONFIGURATION = '''
ServerAdmin webmaster@localhost
ServerName {domain}
DocumentRoot /var/www/html
Options FollowSymLinks
AllowOverride None
Options Indexes FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
allow from all
ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
AllowOverride None
Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
Order allow,deny
Allow from all
ErrorLog ${{APACHE_LOG_DIR}}/error.log
# Possible values include: debug, info, notice, warn, error, crit, alert, emerg.
LogLevel warn
CustomLog ${{APACHE_LOG_DIR}}/ssl_access.log combined
# GnuTLS Switch: Enable/Disable SSL/TLS for this virtual host.
GnuTLSEnable On
# Automatically obtained certficates from Let's Encrypt
GnuTLSCertificateFile /etc/letsencrypt/live/{domain}/fullchain.pem
GnuTLSKeyFile /etc/letsencrypt/live/{domain}/privkey.pem
# See http://www.outoforder.cc/projects/apache/mod_gnutls/docs/#GnuTLSPriorities
GnuTLSPriorities NORMAL
'''
def parse_arguments():
"""Return parsed command line arguments as dictionary."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
subparsers.add_parser(
'get-status', help='Return the status of configured domains.')
revoke_parser = subparsers.add_parser(
'revoke', help='Disable and domain and revoke its certificate.')
revoke_parser.add_argument(
'--domain', help='Domain name to revoke certificate for')
obtain_parser = subparsers.add_parser(
'obtain', help='Obtain certficate for a domain and setup website.')
obtain_parser.add_argument(
'--domain', help='Domain name to obtain certificate for')
return parser.parse_args()
def get_certficate_expiry(domain):
"""Return the expiry date of a certificate."""
certificate_file = os.path.join(LIVE_DIRECTORY, domain, 'cert.pem')
output = subprocess.check_output(['openssl', 'x509', '-enddate', '-noout',
'-in', certificate_file])
return output.decode().strip().split('=')[1]
def subcommand_get_status(_):
"""Return a JSON dictionary of currently configured domains."""
try:
domains = os.listdir(LIVE_DIRECTORY)
except OSError:
domains = []
domains = [domain for domain in domains
if os.path.isdir(os.path.join(LIVE_DIRECTORY, domain))]
domain_status = {}
for domain in domains:
domain_status[domain] = {
'certificate_available': True,
'expiry_date': get_certficate_expiry(domain),
'web_enabled':
action_utils.webserver_is_enabled(domain, kind='site')
}
print(json.dumps({'domains': domain_status}))
def subcommand_revoke(arguments):
"""Disable a domain and revoke the certificate."""
domain = arguments.domain
command = ['letsencrypt', 'revoke', '--domain', domain, '--cert-path',
os.path.join(LIVE_DIRECTORY, domain, 'cert.pem')]
if TEST_MODE:
command.append('--staging')
process = subprocess.Popen(command, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout, stderr = process.communicate()
if process.returncode:
print(stderr.decode(), file=sys.stderr)
sys.exit(1)
action_utils.webserver_disable(domain, kind='site')
def subcommand_obtain(arguments):
"""Obtain a certificate for a domain and setup website."""
domain = arguments.domain
command = [
'letsencrypt', 'certonly', '--agree-tos',
'--register-unsafely-without-email', '--domain', arguments.domain,
'--authenticator', 'webroot', '--webroot-path', '/var/www/html/',
'--renew-by-default']
if TEST_MODE:
command.append('--staging')
process = subprocess.Popen(command, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdin, stderr = process.communicate()
if process.returncode:
print(stderr.decode(), file=sys.stderr)
sys.exit(1)
setup_webserver_config(domain)
action_utils.webserver_enable(domain, kind='site')
def setup_webserver_config(domain):
"""Create SSL web server configuration for a domain.
Do so only if there is no configuration existing.
"""
file_name = os.path.join(APACHE_PREFIX, domain + '.conf')
if os.path.isfile(file_name):
return
with open(file_name, 'w') as file_handle:
file_handle.write(APACHE_CONFIGURATION.format(domain=domain))
def main():
"""Parse arguments and perform all duties."""
arguments = parse_arguments()
subcommand = arguments.subcommand.replace('-', '_')
subcommand_method = globals()['subcommand_' + subcommand]
subcommand_method(arguments)
if __name__ == '__main__':
main()
Plinth-0.8.1/actions/monkeysphere 0000775 0000000 0000000 00000007710 12660516711 0017067 0 ustar 00root root 0000000 0000000 #!/usr/bin/python3
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
"""
Configuration helper for monkeysphere.
"""
import argparse
import json
import os
import subprocess
def parse_arguments():
"""Return parsed command line arguments as dictionary."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
host_show_keys = subparsers.add_parser(
'host-show-keys', help='Show host key fingerprints')
host_show_keys.add_argument(
'key_ids', nargs='*', help='Optional list of KEYIDs')
host_import_ssh_key = subparsers.add_parser(
'host-import-ssh-key', help='Import host SSH key')
host_import_ssh_key.add_argument(
'hostname', help='Fully-qualified hostname')
host_publish_key = subparsers.add_parser(
'host-publish-key', help='Push host key to keyserver')
host_publish_key.add_argument(
'key_ids', nargs='*', help='Optional list of KEYIDs')
return parser.parse_args()
def subcommand_host_show_keys(arguments):
"""Show host key fingerprints."""
try:
output = subprocess.check_output(
['monkeysphere-host', 'show-keys'] + arguments.key_ids,
stderr=subprocess.DEVNULL)
except subprocess.CalledProcessError:
# no keys available
print(json.dumps({'keys': []}))
return
# parse output
keys = [dict()]
lines = output.decode().strip().split('\n')
for line in lines:
if line.startswith('pub'):
data = line.lstrip('pub').split()
keys[-1]['pub'] = data[0]
keys[-1]['date'] = data[1]
elif line.startswith('uid'):
keys[-1]['uid'] = line.lstrip('uid').strip()
elif line.startswith('OpenPGP fingerprint:'):
keys[-1]['pgp_fingerprint'] = line.lstrip('Open PGP fingerprint:')
elif line.startswith('ssh fingerprint:'):
data = line.lstrip('ssh fingerprint:').split()
keys[-1]['ssh_key_size'] = data[0]
keys[-1]['ssh_fingerprint'] = data[1]
keys[-1]['ssh_key_type'] = data[2].strip('()')
elif line == '':
keys.append(dict())
print(json.dumps({'keys': keys}))
def subcommand_host_import_ssh_key(arguments):
"""Import host SSH key."""
output = subprocess.check_output(
['monkeysphere-host', 'import-key',
'/etc/ssh/ssh_host_rsa_key', arguments.hostname])
print(output.decode())
def subcommand_host_publish_key(arguments):
"""Push host key to keyserver."""
# setting TMPDIR as workaround for Debian bug #656750
proc = subprocess.Popen(
['monkeysphere-host', 'publish-keys'] + arguments.key_ids,
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
env=dict(
os.environ,
TMPDIR='/var/lib/monkeysphere/authentication/tmp/',
MONKEYSPHERE_PROMPT='false'))
output, error = proc.communicate()
output, error = output.decode(), error.decode()
if proc.returncode != 0:
raise Exception(output, error)
print(output)
def main():
"""Parse arguments and perform all duties."""
arguments = parse_arguments()
subcommand = arguments.subcommand.replace('-', '_')
subcommand_method = globals()['subcommand_' + subcommand]
subcommand_method(arguments)
if __name__ == '__main__':
main()
Plinth-0.8.1/actions/mumble 0000775 0000000 0000000 00000003322 12660516711 0015632 0 ustar 00root root 0000000 0000000 #!/usr/bin/python3
# -*- mode: python -*-
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
"""
Configuration helper for Mumble server
"""
import argparse
from plinth import action_utils
SERVICE_CONFIG = '/etc/default/mumble-server'
def parse_arguments():
"""Return parsed command line arguments as dictionary."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
subparsers.add_parser('enable', help='Enable Mumble service')
subparsers.add_parser('disable', help='Disable Mumble service')
return parser.parse_args()
def subcommand_enable(_):
"""Start service."""
action_utils.service_enable('mumble-server')
def subcommand_disable(_):
"""Stop service."""
action_utils.service_disable('mumble-server')
def main():
"""Parse arguments and perform all duties."""
arguments = parse_arguments()
subcommand = arguments.subcommand.replace('-', '_')
subcommand_method = globals()['subcommand_' + subcommand]
subcommand_method(arguments)
if __name__ == '__main__':
main()
Plinth-0.8.1/actions/openvpn 0000775 0000000 0000000 00000014007 12660516711 0016040 0 ustar 00root root 0000000 0000000 #!/usr/bin/python3
# -*- mode: python -*-
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
"""
Configuration helper for OpenVPN server.
"""
import argparse
import os
import subprocess
from plinth import action_utils
KEYS_DIRECTORY = '/etc/openvpn/freedombox-keys'
DH_KEY = '/etc/openvpn/freedombox-keys/dh4096.pem'
SERVER_CONFIGURATION_PATH = '/etc/openvpn/freedombox.conf'
CA_CERTIFICATE_PATH = KEYS_DIRECTORY + '/ca.crt'
USER_CERTIFICATE_PATH = KEYS_DIRECTORY + '/{username}.crt'
USER_KEY_PATH = KEYS_DIRECTORY + '/{username}.key'
SERVER_CONFIGURATION = '''
port 1194
proto udp
dev tun
ca /etc/openvpn/freedombox-keys/ca.crt
cert /etc/openvpn/freedombox-keys/server.crt
key /etc/openvpn/freedombox-keys/server.key
dh /etc/openvpn/freedombox-keys/dh4096.pem
server 10.91.0.0 255.255.255.0
keepalive 10 120
cipher AES-256-CBC
comp-lzo
verb 3
'''
CLIENT_CONFIGURATION = '''
client
remote {remote} 1194
proto udp
dev tun
nobind
remote-cert-tls server
cipher AES-256-CBC
comp-lzo
redirect-gateway
verb 3
{ca}
{cert}
{key}'''
CERTIFICATE_CONFIGURATION = {
'KEY_CONFIG': '/usr/share/easy-rsa/openssl-1.0.0.cnf',
'KEY_DIR': KEYS_DIRECTORY,
'OPENSSL': 'openssl',
'KEY_SIZE': '4096',
'CA_EXPIRE': '3650',
'KEY_EXPIRE': '3650',
'KEY_COUNTRY': 'US',
'KEY_PROVINCE': 'NY',
'KEY_CITY': 'New York',
'KEY_ORG': 'FreedomBox',
'KEY_EMAIL': 'me@freedombox',
'KEY_OU': 'Home',
'KEY_NAME': 'FreedomBox'
}
COMMON_ARGS = {'env': CERTIFICATE_CONFIGURATION,
'cwd': KEYS_DIRECTORY}
def parse_arguments():
"""Return parsed command line arguments as dictionary."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
subparsers.add_parser('is-setup', help='Return whether setup is completed')
subparsers.add_parser('setup', help='Setup OpenVPN server configuration')
subparsers.add_parser('enable', help='Enable OpenVPN server')
subparsers.add_parser('disable', help='Disable OpenVPN server')
get_profile = subparsers.add_parser(
'get-profile', help='Return the OpenVPN profile of a user')
get_profile.add_argument('username', help='User to get profile for')
get_profile.add_argument('remote_server',
help='The server name for the user to connect')
return parser.parse_args()
def subcommand_is_setup(_):
"""Return whether setup is complete."""
print('true' if os.path.isfile(DH_KEY) else 'false')
def subcommand_setup(_):
"""Setup configuration, CA and certificates."""
_create_server_config()
_create_certificates()
_setup_firewall()
action_utils.service_enable('openvpn@freedombox')
action_utils.service_restart('openvpn@freedombox')
def _create_server_config():
"""Write server configuration."""
if os.path.exists(SERVER_CONFIGURATION_PATH):
return
with open(SERVER_CONFIGURATION_PATH, 'w') as file_handle:
file_handle.write(SERVER_CONFIGURATION)
def _setup_firewall():
"""Add TUN device to internal zone in firewalld."""
subprocess.call(['firewall-cmd', '--zone', 'internal',
'--add-interface', 'tun+'])
subprocess.call(['firewall-cmd', '--permanent', '--zone', 'internal',
'--add-interface', 'tun+'])
def _create_certificates():
"""Generate CA and server certificates."""
try:
os.mkdir(KEYS_DIRECTORY, 0o700)
except FileExistsError:
pass
subprocess.check_call(['/usr/share/easy-rsa/clean-all'], **COMMON_ARGS)
subprocess.check_call(['/usr/share/easy-rsa/pkitool', '--initca'],
**COMMON_ARGS)
subprocess.check_call(['/usr/share/easy-rsa/pkitool', '--server', 'server'],
**COMMON_ARGS)
subprocess.check_call(['/usr/share/easy-rsa/build-dh'], **COMMON_ARGS)
def subcommand_enable(_):
"""Start OpenVPN service."""
action_utils.service_enable('openvpn@freedombox')
def subcommand_disable(_):
"""Stop OpenVPN service."""
action_utils.service_disable('openvpn@freedombox')
def subcommand_get_profile(arguments):
"""Return the profile for a user."""
username = arguments.username
remote_server = arguments.remote_server
if username == 'ca' or username == 'server':
raise Exception('Invalid username')
user_certificate = USER_CERTIFICATE_PATH.format(username=username)
user_key = USER_KEY_PATH.format(username=username)
if not os.path.isfile(user_certificate) or not os.path.isfile(user_key):
subprocess.check_call(['/usr/share/easy-rsa/pkitool', username],
**COMMON_ARGS)
user_certificate_string = _read_file(user_certificate)
user_key_string = _read_file(user_key)
ca_string = _read_file(CA_CERTIFICATE_PATH)
profile = CLIENT_CONFIGURATION.format(
ca=ca_string, cert=user_certificate_string, key=user_key_string,
remote=remote_server)
print(profile)
def _read_file(filename):
"""Return the entire contens of a file as string."""
with open(filename, 'r') as file_handle:
return ''.join(file_handle.readlines())
def main():
"""Parse arguments and perform all duties."""
arguments = parse_arguments()
subcommand = arguments.subcommand.replace('-', '_')
subcommand_method = globals()['subcommand_' + subcommand]
subcommand_method(arguments)
if __name__ == '__main__':
main()
Plinth-0.8.1/actions/owncloud-setup 0000775 0000000 0000000 00000005322 12660516711 0017343 0 ustar 00root root 0000000 0000000 #!/bin/sh
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
# See also
# http://doc.owncloud.org/server/6.0/admin_manual/configuration/configuration_automation.html
grep -q "^\s*['\"]dbtype['\"]" /etc/owncloud/config.php 2> /dev/null
db_in_config=$(( ! $? ))
grep -q "^\s*['\"]dbtype['\"]" /etc/owncloud/autoconfig.php 2> /dev/null
db_in_autoconfig=$(( ! $? ))
if [ -e /etc/apache2/conf-enabled/owncloud.conf ] && \
[ $db_in_config -ne 0 -o $db_in_autoconfig -ne 0 ] ; then
owncloud_enable_cur=true
else
owncloud_enable_cur=false
fi
while [ "$1" ] ; do
arg="$1"
shift
case "$arg" in
enable|noenable) # Not using disable for consistency with other options
if [ 'enable' = "$arg" ] ; then
owncloud_enable=true
else
owncloud_enable=false
fi
;;
status)
printstatus() {
if "$2" ; then
echo "$1"
else
echo no"$1"
fi
}
printstatus enable $owncloud_enable_cur
exit 0
;;
*)
;;
esac
done
if [ "$owncloud_enable" != "$owncloud_enable_cur" ] ; then
if $owncloud_enable ; then
# Keep existing configuration if it exist
if [ $db_in_config -eq 0 -a $db_in_autoconfig -eq 0 ] ; then
# Set up postgresql database and user
dbpwd=$(pwgen -1 30)
su - postgres -c "psql -c \"CREATE USER owncloud WITH NOCREATEDB NOCREATEUSER\"" \
2>&1 | logger -t owncloud-setup
su - postgres -c "psql -c \"ALTER USER owncloud ENCRYPTED PASSWORD '$dbpwd'\"" \
2>&1 | logger -t owncloud-setup
su - postgres -c "createdb --owner owncloud owncloud" \
2>&1 | logger -t owncloud-setup
cat > /etc/owncloud/autoconfig.php < '/usr/share/owncloud/data',
'dbtype' => 'pgsql',
'dbname' => 'owncloud',
'dbuser' => 'owncloud',
'dbpass' => '$dbpwd',
'dbhost' => 'localhost',
'dbtableprefix' => 'oc_',
'installed' => false,
);
EOF
fi
a2enconf owncloud 2>&1 | logger -t owncloud-setup
else
a2disconf owncloud 2>&1 | logger -t owncloud-setup
fi
service apache2 reload 2>&1 | logger -t owncloud-setup
fi
Plinth-0.8.1/actions/pagekite 0000775 0000000 0000000 00000021650 12660516711 0016146 0 ustar 00root root 0000000 0000000 #!/usr/bin/python3
# -*- mode: python -*-
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
"""
Configuration helper for Plinth PageKite interface.
"""
import argparse
import augeas
import json
import os
import sys
from plinth import action_utils
from plinth.modules.pagekite import utils
aug = None
PATHS = {
'service_on': os.path.join(utils.CONF_PATH, '*', 'service_on', '*'),
'kitename': os.path.join(utils.CONF_PATH, '10_account.rc', 'kitename'),
'kitesecret': os.path.join(utils.CONF_PATH, '10_account.rc', 'kitesecret'),
'abort_not_configured': os.path.join(utils.CONF_PATH, '10_account.rc',
'abort_not_configured'),
'defaults': os.path.join(utils.CONF_PATH, '20_frontends.rc', 'defaults'),
'frontend': os.path.join(utils.CONF_PATH, '20_frontends.rc', 'frontend'),
}
def parse_arguments():
"""Return parsed command line arguments as dictionary"""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
# Enable/disable the pagekite service
subparsers.add_parser('start-and-enable', help='Enable PageKite service')
subparsers.add_parser('stop-and-disable', help='Disable PageKite service')
subparsers.add_parser('restart', help='Restart PageKite service')
subparsers.add_parser('is-disabled',
help=('Whether PageKite is disabled in the file '
'/etc/pagekite.d/10_accounts.rc'))
# Frontend
subparsers.add_parser('get-frontend', help='Get pagekite frontend')
set_frontend = subparsers.add_parser('set-frontend',
help='Set pagekite frontend')
set_frontend.add_argument('url', help='frontend url')
# Kite details (name + secret)
subparsers.add_parser('get-kite',
help='Get configured kite name and secret')
set_kite = subparsers.add_parser(
'set-kite',
help='Configure kite name and its secret. Secret is read from stdin.')
set_kite.add_argument('--kite-name',
help='Name of the kite (eg: mybox.pagekite.me)')
# Add/remove pagekite services (service_on entries)
subparsers.add_parser('get-services', help='Get list of enabled services')
add_service = subparsers.add_parser('add-service',
help='Add a pagekite service')
add_service.add_argument('--service', help='json service dictionary')
remove_service = subparsers.add_parser('remove-service',
help='Remove a pagekite service')
remove_service.add_argument('--service', help='json service dictionary')
return parser.parse_args()
def subcommand_restart(_):
"""Restart the pagekite service"""
action_utils.service_restart('pagekite')
print('restarted')
def subcommand_is_disabled(_):
if aug.match(PATHS['abort_not_configured']):
print('true')
else:
print('false')
def subcommand_start_and_enable(_):
aug.remove(PATHS['abort_not_configured'])
aug.save()
# 'start' alone sometimes fails, even if the service is not running
action_utils.service_restart('pagekite')
print('enabled')
def subcommand_stop_and_disable(_):
action_utils.service_stop('pagekite')
aug.set(PATHS['abort_not_configured'], '')
aug.save()
print('disabled')
def subcommand_get_frontend(_):
"""Get pagekite frontend url"""
if aug.match(PATHS['defaults']):
print("pagekite.net")
else:
url = aug.get(PATHS['frontend'])
print(url or '')
def subcommand_set_frontend(arguments):
"""Set pagekite frontend url, taking care of defaults and pagekite.net"""
frontend_domain = arguments.url.split(':')[0]
if frontend_domain in ('pagekite.net', 'defaults', 'default'):
enable_pagekitenet_frontend()
else:
aug.remove(PATHS['defaults'])
aug.set(PATHS['frontend'], arguments.url)
aug.save()
def enable_pagekitenet_frontend():
"""Enable using default pageket.net frontend
This disables any other frontends.
"""
aug.set(PATHS['defaults'], '')
aug.remove(PATHS['frontend'])
aug.save()
print("enabled")
def subcommand_get_services(arguments):
""" lists all available (enabled) services """
for match in aug.match(PATHS['service_on']):
service = dict([(param, aug.get(os.path.join(match, param)))
for param in utils.SERVICE_PARAMS])
print(json.dumps(service))
def subcommand_remove_service(arguments):
"""Searches and removes the service(s) that match all given parameters"""
service = utils.load_service(arguments.service)
paths = get_existing_service_paths(service)
# TODO: theoretically, everything to do here is:
# [aug.remove(path) for path in paths]
# but augeas won't let you save the changed files and doesn't say why
for path in paths:
filepath = convert_augeas_path_to_filepath(path)
service_found = False
with open(filepath, 'r') as file:
lines = file.readlines()
for i, line in enumerate(lines):
if line.startswith('service_on') and \
all(param in line for param in service.values()):
lines[i] = ""
service_found = True
break
if service_found:
with open(filepath, 'w') as file:
file.writelines(lines)
# abort to only allow deleting one service
break
action_utils.service_restart('pagekite')
def get_existing_service_paths(service):
"""Return paths of existing services that match the given service params"""
# construct an augeas query path with patterns like:
# */service_on/*[protocol='http']
path = PATHS['service_on']
for param, value in service.items():
path += "[%s='%s']" % (param, value)
return aug.match(path)
def subcommand_add_service(arguments):
"""Add one service"""
service = utils.load_service(arguments.service)
if get_existing_service_paths(service):
msg = "Service with the parameters %s already exists"
raise RuntimeError(msg % service)
root = get_new_service_path(service['protocol'])
# TODO: after adding a service, augeas fails writing the config;
# so add the service_on entry manually instead
path = convert_augeas_path_to_filepath(root)
with open(path, 'a') as servicefile:
line = "\nservice_on = %s\n" % utils.convert_service_to_string(service)
servicefile.write(line)
action_utils.service_restart('pagekite')
def convert_augeas_path_to_filepath(augpath, prefix='/files',
suffix='service_on'):
"""Convert an augeas service_on path to the actual file path"""
if augpath.startswith(prefix):
augpath = augpath.replace(prefix, "", 1)
index = augpath.rfind(suffix)
if index:
augpath = augpath[:index]
return augpath.rstrip('/')
def get_new_service_path(protocol):
"""Get the augeas path of a new service for a protocol
This takes care of existing services using a /service_on/*/ query"""
root = utils.get_augeas_servicefile_path(protocol)
new_index = len(aug.match(root + '/*')) + 1
return os.path.join(root, str(new_index))
def subcommand_get_kite(_):
"""Print details of the currently configured kite"""
kitename = aug.get(PATHS['kitename'])
kitesecret = aug.get(PATHS['kitesecret'])
print(kitename or '')
print(kitesecret or '')
def subcommand_set_kite(arguments):
"""Set details of the kite"""
aug.set(PATHS['kitename'], arguments.kite_name)
aug.set(PATHS['kitesecret'], sys.stdin.read())
aug.save()
def augeas_load():
"""Initialize Augeas."""
global aug
aug = augeas.Augeas(flags=augeas.Augeas.NO_LOAD +
augeas.Augeas.NO_MODL_AUTOLOAD)
aug.set('/augeas/load/Pagekite/lens', 'Pagekite.lns')
aug.set('/augeas/load/Pagekite/incl[last() + 1]', '/etc/pagekite.d/*.rc')
aug.load()
def main():
"""Parse arguments and perform all duties"""
augeas_load()
arguments = parse_arguments()
subcommand = arguments.subcommand.replace('-', '_')
subcommand_method = globals()['subcommand_' + subcommand]
subcommand_method(arguments)
if __name__ == "__main__":
main()
Plinth-0.8.1/actions/power 0000775 0000000 0000000 00000003154 12660516711 0015510 0 ustar 00root root 0000000 0000000 #!/usr/bin/python3
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
"""
Configuration helper for power controls.
"""
import argparse
import subprocess
def parse_arguments():
"""Return parsed command line arguments as dictionary."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
subparsers.add_parser('restart', help='Restart the system')
subparsers.add_parser('shutdown', help='Shut down the system')
return parser.parse_args()
def subcommand_restart(_):
"""Restart the system."""
subprocess.call('reboot')
def subcommand_shutdown(_):
"""Shut down the system."""
subprocess.call(['shutdown', 'now'])
def main():
"""Parse arguments and perform all duties."""
arguments = parse_arguments()
subcommand = arguments.subcommand.replace('-', '_')
subcommand_method = globals()['subcommand_' + subcommand]
subcommand_method(arguments)
if __name__ == '__main__':
main()
Plinth-0.8.1/actions/privoxy 0000775 0000000 0000000 00000004607 12660516711 0016100 0 ustar 00root root 0000000 0000000 #!/usr/bin/python3
# -*- mode: python -*-
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
"""
Configuration helper for Privoxy server.
"""
import argparse
import re
from plinth import action_utils
CONFIG_FILE = '/etc/privoxy/config'
def parse_arguments():
"""Return parsed command line arguments as dictionary."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
subparsers.add_parser('setup',
help='Perform Privoxy configuration setup')
subparsers.add_parser('enable', help='Enable Privoxy service')
subparsers.add_parser('disable', help='Disable Privoxy service')
return parser.parse_args()
def subcommand_setup(_):
"""Setup configuration for the first time."""
with open(CONFIG_FILE, 'r') as conffile:
lines = conffile.readlines()
with open(CONFIG_FILE, 'w') as conffile:
written = False
for line in lines:
if re.match(r'#?listen-address', line) and not written:
conffile.write('listen-address [::]:8118\n')
written = True
else:
conffile.write(line)
if not written:
conffile.write('listen-address [::]:8118')
action_utils.service_restart('privoxy')
def subcommand_enable(_):
"""Start service."""
action_utils.service_enable('privoxy')
def subcommand_disable(_):
"""Stop service."""
action_utils.service_disable('privoxy')
def main():
"""Parse arguments and perform all duties."""
arguments = parse_arguments()
subcommand = arguments.subcommand.replace('-', '_')
subcommand_method = globals()['subcommand_' + subcommand]
subcommand_method(arguments)
if __name__ == '__main__':
main()
Plinth-0.8.1/actions/quassel 0000775 0000000 0000000 00000003223 12660516711 0016026 0 ustar 00root root 0000000 0000000 #!/usr/bin/python3
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
"""
Configuration helper for Quassel core.
"""
import argparse
from plinth import action_utils
def parse_arguments():
"""Return parsed command line arguments as dictionary."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
subparsers.add_parser('enable', help='Enable Quassel core service')
subparsers.add_parser('disable', help='Disable Quassel core service')
return parser.parse_args()
def subcommand_enable(_):
"""Start service."""
action_utils.service_enable('quasselcore')
def subcommand_disable(_):
"""Stop service."""
action_utils.service_disable('quasselcore')
def main():
"""Parse arguments and perform all duties."""
arguments = parse_arguments()
subcommand = arguments.subcommand.replace('-', '_')
subcommand_method = globals()['subcommand_' + subcommand]
subcommand_method(arguments)
if __name__ == '__main__':
main()
Plinth-0.8.1/actions/repro 0000775 0000000 0000000 00000004705 12660516711 0015506 0 ustar 00root root 0000000 0000000 #!/usr/bin/python3
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
"""
Configuration helper for repro SIP proxy.
"""
import argparse
from plinth import action_utils
CONFIG = '/etc/repro/repro.config'
def parse_arguments():
"""Return parsed command line arguments as dictionary."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
subparsers.add_parser('setup', help='Configure repro')
subparsers.add_parser('enable', help='Enable repro service')
subparsers.add_parser('disable', help='Disable repro service')
return parser.parse_args()
def subcommand_setup(_):
"""Configure repro."""
with open(CONFIG, 'r') as conf:
lines = conf.readlines()
with open(CONFIG, 'w') as conf:
for line in lines:
if line.startswith('Database1Path'):
# workaround for Debian bug #803113
conf.write('Database1Path = /var/lib/repro\n')
elif line.startswith('TLSPort'):
conf.write('TLSPort = 5061\n')
elif line.startswith('DisableHttpAuth'):
# let apache handle authentication
conf.write('DisableHttpAuth = true\n')
else:
conf.write(line)
action_utils.service_restart('repro')
action_utils.webserver_enable('repro-plinth')
def subcommand_enable(_):
"""Start service."""
action_utils.service_enable('repro')
def subcommand_disable(_):
"""Stop service."""
action_utils.service_disable('repro')
def main():
"""Parse arguments and perform all duties."""
arguments = parse_arguments()
subcommand = arguments.subcommand.replace('-', '_')
subcommand_method = globals()['subcommand_' + subcommand]
subcommand_method(arguments)
if __name__ == '__main__':
main()
Plinth-0.8.1/actions/restore 0000775 0000000 0000000 00000003221 12660516711 0016032 0 ustar 00root root 0000000 0000000 #!/usr/bin/python3
# -*- mode: python -*-
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
"""
Configuration helper for reStore.
"""
import argparse
from plinth import action_utils
def parse_arguments():
"""Return parsed command line arguments as dictionary."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
subparsers.add_parser('enable', help='Enable reStore')
subparsers.add_parser('disable', help='Disable reStore')
return parser.parse_args()
def subcommand_enable(_):
"""Enable reStore."""
action_utils.service_enable('node-restore')
def subcommand_disable(_):
"""Disable reStore."""
action_utils.service_disable('node-restore')
def main():
"""Parse arguments and perform all duties."""
arguments = parse_arguments()
subcommand = arguments.subcommand.replace('-', '_')
subcommand_method = globals()['subcommand_' + subcommand]
subcommand_method(arguments)
if __name__ == '__main__':
main()
Plinth-0.8.1/actions/roundcube 0000775 0000000 0000000 00000005512 12660516711 0016342 0 ustar 00root root 0000000 0000000 #!/usr/bin/python3
# -*- mode: python -*-
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
"""
Configuration helper for Roundcube server.
"""
import argparse
import re
import subprocess
from plinth import action_utils
APACHE_CONF = '/etc/apache2/conf-available/roundcube.conf'
def parse_arguments():
"""Return parsed command line arguments as dictionary."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
subparsers.add_parser('pre-install',
help='Perform Roundcube pre-install configuration')
subparsers.add_parser('setup',
help='Perform Roundcube configuration setup')
subparsers.add_parser('enable', help='Enable Roundcube')
subparsers.add_parser('disable', help='Disable Roundcube')
return parser.parse_args()
def subcommand_pre_install(_):
"""Preseed debconf values before packages are installed."""
subprocess.check_output(
['debconf-set-selections'],
input=b'roundcube-core roundcube/dbconfig-install boolean true')
subprocess.check_output(
['debconf-set-selections'],
input=b'roundcube-core roundcube/database-type string sqlite3')
def subcommand_setup(_):
"""Setup Roundcube Apache configuration."""
with open(APACHE_CONF, 'r') as conffile:
lines = conffile.readlines()
with open(APACHE_CONF, 'w') as conffile:
for line in lines:
match = re.match(r'#\s*(Alias /roundcube.*)', line)
if match:
conffile.write(match.group(1) + '\n')
else:
conffile.write(line)
action_utils.service_reload('apache2')
def subcommand_enable(_):
"""Enable web configuration and reload."""
action_utils.webserver_enable('roundcube')
def subcommand_disable(_):
"""Disable web configuration and reload."""
action_utils.webserver_disable('roundcube')
def main():
"""Parse arguments and perform all duties."""
arguments = parse_arguments()
subcommand = arguments.subcommand.replace('-', '_')
subcommand_method = globals()['subcommand_' + subcommand]
subcommand_method(arguments)
if __name__ == '__main__':
main()
Plinth-0.8.1/actions/shaarli 0000775 0000000 0000000 00000003277 12660516711 0016005 0 ustar 00root root 0000000 0000000 #!/usr/bin/python3
# -*- mode: python -*-
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
"""
Configuration helper for Shaarli.
"""
import argparse
from plinth import action_utils
def parse_arguments():
"""Return parsed command line arguments as dictionary."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
subparsers.add_parser('enable', help='Enable Shaarli site')
subparsers.add_parser('disable', help='Disable Shaarli site')
return parser.parse_args()
def subcommand_enable(_):
"""Enable web configuration and reload."""
action_utils.webserver_enable('shaarli')
def subcommand_disable(_):
"""Disable web configuration and reload."""
action_utils.webserver_disable('shaarli')
def main():
"""Parse arguments and perform all duties."""
arguments = parse_arguments()
subcommand = arguments.subcommand.replace('-', '_')
subcommand_method = globals()['subcommand_' + subcommand]
subcommand_method(arguments)
if __name__ == '__main__':
main()
Plinth-0.8.1/actions/ssh 0000775 0000000 0000000 00000005463 12660516711 0015156 0 ustar 00root root 0000000 0000000 #!/usr/bin/python3
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
"""
Configuration helper for SSH server.
"""
import argparse
import os
import re
import shutil
import stat
import subprocess
import sys
def parse_arguments():
"""Return parsed command line arguments as dictionary."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
get_keys = subparsers.add_parser('get-keys', help='Get SSH authorized keys')
get_keys.add_argument('--username')
set_keys = subparsers.add_parser('set-keys', help='Set SSH authorized keys')
set_keys.add_argument('--username')
set_keys.add_argument('--keys')
return parser.parse_args()
def _assert_valid_username(username):
"""Verify that username is a valid one."""
if not re.match(r'^[a-z][-a-z0-9_]*$', username):
print('Bad username')
sys.exit(1)
def subcommand_get_keys(arguments):
"""Get SSH authorized keys."""
user = arguments.username
_assert_valid_username(user)
path = os.path.join(os.path.expanduser('~' + user),
'.ssh', 'authorized_keys')
try:
with open(path, 'r') as file_handle:
print(file_handle.read())
except FileNotFoundError:
pass
def subcommand_set_keys(arguments):
"""Set SSH authorized keys."""
user = arguments.username
_assert_valid_username(user)
subprocess.check_call(['mkhomedir_helper', user])
ssh_folder = os.path.join(os.path.expanduser('~' + user), '.ssh')
key_file_path = os.path.join(ssh_folder, 'authorized_keys')
if not os.path.exists(ssh_folder):
os.makedirs(ssh_folder)
shutil.chown(ssh_folder, user, 'users')
with open(key_file_path, 'w') as file_handle:
file_handle.write(arguments.keys)
shutil.chown(key_file_path, user, 'users')
os.chmod(key_file_path, stat.S_IRUSR | stat.S_IWUSR)
def main():
"""Parse arguments and perform all duties."""
arguments = parse_arguments()
subcommand = arguments.subcommand.replace('-', '_')
subcommand_method = globals()['subcommand_' + subcommand]
subcommand_method(arguments)
if __name__ == '__main__':
main()
Plinth-0.8.1/actions/timezone-change 0000775 0000000 0000000 00000001630 12660516711 0017426 0 ustar 00root root 0000000 0000000 #!/bin/sh
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
zonename="$1"
tzpath="/usr/share/zoneinfo/$zonename"
if [ -e "$tzpath" ] ; then
cp "$tzpath" /etc/localtime
echo "$zonename" > /etc/timezone
exit 0
else
echo "Time zone not valid" 1>&2
exit 1
fi
Plinth-0.8.1/actions/tor 0000775 0000000 0000000 00000024561 12660516711 0015165 0 ustar 00root root 0000000 0000000 #!/usr/bin/python3
# -*- mode: python -*-
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
"""
Configuration helper for the Tor service
"""
import argparse
import codecs
import os
import re
import socket
import time
from plinth import action_utils
from plinth.modules.tor import is_enabled, is_running, get_augeas, \
get_real_apt_uri_path, iter_apt_uris, APT_TOR_PREFIX
SERVICE_FILE = '/etc/firewalld/services/tor-{0}.xml'
TOR_CONFIG = '/etc/tor/torrc'
TOR_STATE_FILE = '/var/lib/tor/state'
TOR_AUTH_COOKIE = '/var/run/tor/control.authcookie'
def parse_arguments():
"""Return parsed command line arguments as dictionary"""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
subparsers.add_parser('setup', help='Setup Tor configuration')
subparsers.add_parser('get-ports', help='Get list of Tor ports')
subparsers.add_parser('get-hs', help='Get hidden service')
configure = subparsers.add_parser('configure', help='Configure Tor')
configure.add_argument('--service', choices=['enable', 'disable'],
help='Configure Tor service')
configure.add_argument('--hidden-service', choices=['enable', 'disable'],
help='Configure hidden service')
configure.add_argument('--apt-transport-tor',
choices=['enable', 'disable'],
help='Configure package download over Tor')
return parser.parse_args()
def subcommand_setup(_):
"""Setup Tor configuration after installing it."""
# XXX: Performing this as a post-install step instead of
# pre-install setup for now. Creating a configuration before hand
# leads dpkg to ask question about configuration overwrite which
# makes aptcc backend of packagekit to wait forever even with
# interactive=False.
lines = """
# Run as non-exit bridge relay
SocksPort [::]:9050
SocksPort 0.0.0.0:9050
ORPort auto
ControlPort 9051
BridgeRelay 1
Exitpolicy reject *:*
Exitpolicy reject6 *:*
# Enable obfsproxy
ServerTransportPlugin obfs3,obfs4 exec /usr/bin/obfs4proxy
ExtORPort auto
# Enable transparent proxy
VirtualAddrNetworkIPv4 10.192.0.0/10
AutomapHostsOnResolve 1
TransPort 127.0.0.1:9040
TransPort [::1]:9040
DNSPort 127.0.0.1:9053
DNSPort [::1]:9053
"""
with open(TOR_CONFIG, 'w') as conffile:
conffile.writelines(lines)
action_utils.service_restart('tor')
_update_ports()
def subcommand_get_ports(_):
"""Get list of Tor ports."""
ports = get_ports()
for name, number in ports.items():
print(name, number)
def subcommand_get_hs(_):
"""Print currently configured Tor hidden service information"""
print(get_hidden_service())
def subcommand_configure(arguments):
"""Configure Tor."""
if arguments.service == 'disable':
_disable()
restart = arguments.service == None
if arguments.hidden_service == 'enable':
_enable_hs(restart=restart)
elif arguments.hidden_service == 'disable':
_disable_hs(restart=restart)
if arguments.service == 'enable':
_enable()
if arguments.apt_transport_tor == 'enable':
_enable_apt_transport_tor()
elif arguments.apt_transport_tor == 'disable':
_disable_apt_transport_tor()
def get_ports():
"""Return dict mapping port names to numbers."""
ports = {}
try:
ports['orport'] = _get_orport()
except Exception:
pass
try:
with open(TOR_STATE_FILE, 'r') as state_file:
for line in state_file:
matches = re.match(
r'^\s*TransportProxy\s+(\S*)\s+\S+:(\d+)\s*$', line)
if matches:
ports[matches.group(1)] = matches.group(2)
except FileNotFoundError:
pass
return ports
def _get_orport():
"""Return the ORPort by querying running instance."""
cookie = open(TOR_AUTH_COOKIE, 'rb').read()
cookie = codecs.encode(cookie, 'hex').decode()
commands = '''AUTHENTICATE {cookie}
GETINFO net/listeners/or
QUIT
'''.format(cookie=cookie)
tor_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tor_socket.connect(('localhost', 9051))
tor_socket.send(commands.encode())
response = tor_socket.recv(1024)
tor_socket.close()
line = response.split(b'\r\n')[1].decode()
matches = re.match(r'.*="[^:]+:(\d+)"', line)
return matches.group(1)
def get_hidden_service():
"""Return a string with configured Tor hidden service information"""
hs_dir = None
hs_ports = []
try:
with open(TOR_CONFIG, 'r') as conf_file:
for line in conf_file:
if line.startswith('HiddenServiceDir'):
hs_dir = line.split()[1]
elif line.startswith('HiddenServicePort'):
hs_ports.append(line.split()[1])
except FileNotFoundError:
return 'error'
if not hs_dir:
return ''
try:
with open(os.path.join(hs_dir, 'hostname'), 'r') as conf_file:
hs_hostname = conf_file.read().strip()
except Exception:
return 'error'
return hs_hostname + ' ' + ','.join(hs_ports)
def _enable():
"""Enable and start the service."""
action_utils.service_enable('tor')
_update_ports()
def _disable():
"""Disable and stop the service."""
_disable_apt_transport_tor()
action_utils.service_disable('tor')
def _enable_hs(restart=True):
"""Enable Tor hidden service"""
if get_hidden_service():
return
with open(TOR_CONFIG, 'r') as conffile:
lines = conffile.readlines()
lines.append('# Hidden Service configured by Plinth\n')
lines.append('HiddenServiceDir /var/lib/tor/hidden_service/\n')
lines.append('HiddenServicePort 80 127.0.0.1:80\n')
lines.append('HiddenServicePort 443 127.0.0.1:443\n')
lines.append('# end of Plinth Hidden Service config\n')
with open(TOR_CONFIG, 'w') as conffile:
conffile.writelines(lines)
if restart:
if is_enabled() and is_running():
action_utils.service_restart('tor')
# wait until hidden service information is available
tries = 0
while get_hidden_service() in ('', 'error'):
tries += 1
if tries >= 12:
return
time.sleep(10)
def _disable_hs(restart=True):
"""Disable Tor hidden service"""
if not get_hidden_service():
return
with open(TOR_CONFIG, 'r') as conffile:
lines = conffile.readlines()
filtered_lines = []
removing = False
for line in lines:
if removing:
if line.startswith('# end of Plinth Hidden Service config'):
# last line of Plinth hidden service block
# stop removing after this line
removing = False
elif not line.startswith('HiddenService'):
# end of Plinth hidden service block
# stop removing lines
removing = False
filtered_lines.append(line)
else:
if line.startswith('# Hidden Service configured by Plinth'):
# start of Plinth hidden service block
# remove following HiddenService lines
removing = True
else:
filtered_lines.append(line)
with open(TOR_CONFIG, 'w') as conffile:
conffile.writelines(filtered_lines)
if restart:
if is_enabled() and is_running():
action_utils.service_restart('tor')
def _enable_apt_transport_tor():
"""Enable package download over Tor."""
try:
aug = get_augeas()
except Exception:
# If there was an error, don't proceed
print('Error: Unable to understand sources format.')
exit(1)
for uri_path in iter_apt_uris(aug):
uri_path = get_real_apt_uri_path(aug, uri_path)
uri = aug.get(uri_path)
if uri.startswith('http://') or uri.startswith('https://'):
aug.set(uri_path, APT_TOR_PREFIX + uri)
aug.save()
def _disable_apt_transport_tor():
"""Disable package download over Tor."""
try:
aug = get_augeas()
except Exception:
# Disable what we can, so APT is not unusable.
pass
for uri_path in iter_apt_uris(aug):
uri_path = get_real_apt_uri_path(aug, uri_path)
uri = aug.get(uri_path)
if uri.startswith(APT_TOR_PREFIX):
aug.set(uri_path, uri[len(APT_TOR_PREFIX):])
aug.save()
def _update_ports():
"""Update firewall service information."""
ready = False
tries = 0
# port information may not be available immediately after Tor started
while not ready:
ports = get_ports()
ready = 'orport' in ports and 'obfs3' in ports and 'obfs4' in ports
if ready:
break
tries += 1
if tries >= 12:
return
time.sleep(10)
lines = """
Tor - {0}
"""
for name, number in ports.items():
try:
with open(SERVICE_FILE.format(name), 'w') as service_file:
service_file.writelines(lines.format(name, number))
except FileNotFoundError:
return
# XXX: We should ideally do firewalld reload instead. However,
# firewalld seems to fail to successfully reload sometimes.
action_utils.service_restart('firewalld')
def main():
"""Parse arguments and perform all duties"""
arguments = parse_arguments()
subcommand = arguments.subcommand.replace('-', '_')
subcommand_method = globals()['subcommand_' + subcommand]
subcommand_method(arguments)
if __name__ == '__main__':
main()
Plinth-0.8.1/actions/transmission 0000775 0000000 0000000 00000005461 12660516711 0017110 0 ustar 00root root 0000000 0000000 #!/usr/bin/python3
# -*- mode: python -*-
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
"""
Configuration helper for Transmission daemon.
"""
import argparse
import json
import subprocess
import sys
from plinth import action_utils
SERVICE_CONFIG = '/etc/default/transmission-daemon'
TRANSMISSION_CONFIG = '/etc/transmission-daemon/settings.json'
def parse_arguments():
"""Return parsed command line arguments as dictionary."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
# Enable service
subparsers.add_parser('enable', help='Enable Transmission service')
# Disable service
subparsers.add_parser('disable', help='Disable Transmission service')
# Merge given JSON configration with existing
subparsers.add_parser(
'merge-configuration',
help='Merge JSON configuration from stdin with existing')
return parser.parse_args()
def subcommand_enable(_):
"""Start Transmission service."""
action_utils.service_enable('transmission-daemon')
action_utils.webserver_enable('transmission-plinth')
def subcommand_disable(_):
"""Stop Transmission service."""
action_utils.webserver_disable('transmission-plinth')
action_utils.service_disable('transmission-daemon')
def subcommand_merge_configuration(arguments):
"""Merge given JSON configuration with existing configuration."""
configuration = sys.stdin.read()
configuration = json.loads(configuration)
current_configuration = open(TRANSMISSION_CONFIG, 'r').read()
current_configuration = json.loads(current_configuration)
new_configuration = current_configuration
new_configuration.update(configuration)
new_configuration = json.dumps(new_configuration, indent=4, sort_keys=True)
open(TRANSMISSION_CONFIG, 'w').write(new_configuration)
action_utils.service_reload('transmission-daemon')
def main():
"""Parse arguments and perform all duties."""
arguments = parse_arguments()
subcommand = arguments.subcommand.replace('-', '_')
subcommand_method = globals()['subcommand_' + subcommand]
subcommand_method(arguments)
if __name__ == '__main__':
main()
Plinth-0.8.1/actions/upgrades 0000775 0000000 0000000 00000010442 12660516711 0016164 0 ustar 00root root 0000000 0000000 #!/usr/bin/python3
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
"""
Configures or runs unattended-upgrades
"""
import argparse
import os
import re
import subprocess
import sys
CONF_FILE = '/etc/apt/apt.conf.d/50unattended-upgrades'
AUTO_CONF_FILE = '/etc/apt/apt.conf.d/20auto-upgrades'
def parse_arguments():
"""Return parsed command line arguments as dictionary"""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
# Run unattended-upgrades
subparsers.add_parser('run', help='Upgrade packages on the system')
# Check if automatic upgrades are enabled
subparsers.add_parser('check-auto',
help='Check if automatic upgrades are enabled')
# Enable automatic upgrades
subparsers.add_parser('enable-auto', help='Enable automatic upgrades')
# Disable automatic upgrades
subparsers.add_parser('disable-auto', help='Disable automatic upgrades.')
return parser.parse_args()
def subcommand_run(_):
"""Run unattended-upgrades"""
try:
setup()
except FileNotFoundError:
print('Error: Could not configure unattended-upgrades.',
file=sys.stderr)
sys.exit(1)
try:
subprocess.Popen(
['unattended-upgrades', '-v'],
stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL, close_fds=True,
start_new_session=True)
except FileNotFoundError:
print('Error: unattended-upgrades is not available.', file=sys.stderr)
sys.exit(2)
except Exception as error:
print('Error: {0}'.format(error), file=sys.stderr)
sys.exit(3)
def subcommand_check_auto(_):
"""Check if automatic upgrades are enabled"""
arguments = ['apt-config', 'shell', 'UpdateInterval',
'APT::Periodic::Update-Package-Lists']
try:
output = subprocess.check_output(arguments).decode()
except subprocess.CalledProcessError as error:
print('Error: {0}'.format(error), file=sys.stderr)
sys.exit(1)
update_interval = 0
match = re.match(r"UpdateInterval='(.*)'", output)
if match:
update_interval = int(match.group(1))
print(bool(update_interval))
def subcommand_enable_auto(_):
"""Enable automatic upgrades"""
try:
setup()
except FileNotFoundError:
print('Error: Could not configure unattended-upgrades.',
file=sys.stderr)
sys.exit(1)
with open(AUTO_CONF_FILE, 'w') as conffile:
conffile.write('APT::Periodic::Update-Package-Lists "1";\n')
conffile.write('APT::Periodic::Unattended-Upgrade "1";\n')
def subcommand_disable_auto(_):
"""Disable automatic upgrades"""
try:
os.rename(AUTO_CONF_FILE, AUTO_CONF_FILE + '.disabled')
except FileNotFoundError:
print('Already disabled.')
def setup():
"""Sets unattended-upgrades config to upgrade any package from Debian."""
with open(CONF_FILE, 'r') as conffile:
lines = conffile.readlines()
for line in lines:
if re.match(r'\s*"o(rigin)?=Debian";', line):
return # already configured
with open(CONF_FILE, 'w') as conffile:
for line in lines:
conffile.write(line)
if re.match(r'\s*Unattended-Upgrade::Origins-Pattern\s+{', line):
conffile.write(' "origin=Debian";\n')
def main():
"""Parse arguments and perform all duties"""
arguments = parse_arguments()
subcommand = arguments.subcommand.replace('-', '_')
subcommand_method = globals()['subcommand_' + subcommand]
subcommand_method(arguments)
if __name__ == '__main__':
main()
Plinth-0.8.1/actions/xmpp 0000775 0000000 0000000 00000021300 12660516711 0015331 0 ustar 00root root 0000000 0000000 #!/usr/bin/python3
# -*- mode: python -*-
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
"""
Configuration helper for the ejabberd service
"""
import argparse
import os
import shutil
import socket
import subprocess
import re
from plinth import action_utils
JWCHAT_CONFIG = '/etc/jwchat/config.js'
EJABBERD_CONFIG = '/etc/ejabberd/ejabberd.yml'
EJABBERD_BACKUP = '/var/log/ejabberd/ejabberd.dump'
EJABBERD_BACKUP_NEW = '/var/log/ejabberd/ejabberd_new.dump'
def parse_arguments():
"""Return parsed command line arguments as dictionary"""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
# Preseed debconf values before packages are installed.
pre_install = subparsers.add_parser(
'pre-install',
help='Preseed debconf values before packages are installed.')
pre_install.add_argument(
'--domainname',
help='The domain name that will be used by the XMPP service.')
# Setup jwchat apache conf
subparsers.add_parser('setup', help='Setup jwchat apache conf')
subparsers.add_parser('enable', help='Enable XMPP service')
subparsers.add_parser('disable', help='Disable XMPP service')
# Prepare ejabberd for hostname change
pre_hostname_change = subparsers.add_parser(
'pre-change-hostname',
help='Prepare ejabberd for nodename change')
pre_hostname_change.add_argument('--old-hostname',
help='Previous hostname')
pre_hostname_change.add_argument('--new-hostname',
help='New hostname')
# Update ejabberd nodename
hostname_change = subparsers.add_parser('change-hostname',
help='Update ejabberd nodename')
hostname_change.add_argument('--old-hostname',
help='Previous hostname')
hostname_change.add_argument('--new-hostname',
help='New hostname')
# Update ejabberd and jwchat with new domainname
domainname_change = subparsers.add_parser(
'change-domainname',
help='Update ejabberd and jwchat with new domainname')
domainname_change.add_argument('--domainname', help='New domainname')
return parser.parse_args()
def subcommand_pre_install(arguments):
"""Preseed debconf values before packages are installed."""
domainname = arguments.domainname
if not domainname:
# If new domainname is blank, use hostname instead.
domainname = socket.gethostname()
subprocess.check_output(
['debconf-set-selections'],
input=b'ejabberd ejabberd/hostname string ' + domainname.encode())
subprocess.check_output(
['debconf-set-selections'],
input=b'jwchat jwchat/ApacheServerName string ' + domainname.encode())
def subcommand_setup(_):
"""Enabled LDAP authentication and setup jwchat apache conf"""
with open(EJABBERD_CONFIG, 'r') as conffile:
lines = conffile.readlines()
with open(EJABBERD_CONFIG, 'w') as conffile:
for line in lines:
if re.match(r'^\s*tls:\s+true', line):
conffile.write(' tls: false\n')
elif 'auth_method: internal' in line:
conffile.write('## ' + line)
elif '## auth_method: ldap' in line:
conffile.write('auth_method: ldap\n')
elif '## ldap_servers:' in line:
conffile.write('ldap_servers:\n')
conffile.write(' - "localhost"\n')
elif '## ldap_base:' in line:
conffile.write('ldap_base: "ou=users,dc=thisbox"\n')
else:
conffile.write(line)
try:
subprocess.check_output(['ejabberdctl', 'restart'])
except subprocess.CalledProcessError as err:
print('Failed to restart ejabberd with new configuration: %s', err)
with action_utils.WebserverChange() as webserver_change:
webserver_change.disable('jwchat', kind='site')
webserver_change.enable('jwchat-plinth')
def subcommand_enable(_):
"""Enable XMPP service"""
action_utils.service_enable('ejabberd')
action_utils.webserver_enable('jwchat-plinth')
def subcommand_disable(_):
"""Disable XMPP service"""
action_utils.webserver_disable('jwchat-plinth')
action_utils.service_disable('ejabberd')
def subcommand_pre_change_hostname(arguments):
"""Prepare ejabberd for hostname change"""
if not shutil.which('ejabberdctl'):
print('ejabberdctl not found. Is ejabberd installed?')
return
old_hostname = arguments.old_hostname
new_hostname = arguments.new_hostname
subprocess.call(['ejabberdctl', 'backup', EJABBERD_BACKUP])
try:
subprocess.check_output(['ejabberdctl', 'mnesia-change-nodename',
'ejabberd@' + old_hostname,
'ejabberd@' + new_hostname,
EJABBERD_BACKUP, EJABBERD_BACKUP_NEW])
os.remove(EJABBERD_BACKUP)
except subprocess.CalledProcessError as err:
print('Failed to change hostname in ejabberd backup database: %s', err)
def subcommand_change_hostname(arguments):
"""Update ejabberd and jwchat with new hostname"""
if not shutil.which('ejabberdctl'):
print('ejabberdctl not found. Is ejabberd installed?')
return
action_utils.service_stop('ejabberd')
subprocess.call(['pkill', '-u', 'ejabberd'])
# Make sure there aren't files in the Mnesia spool dir
os.makedirs('/var/lib/ejabberd/oldfiles', exist_ok=True)
subprocess.call('mv /var/lib/ejabberd/*.* /var/lib/ejabberd/oldfiles/',
shell=True)
action_utils.service_start('ejabberd')
# restore backup database
if os.path.exists(EJABBERD_BACKUP_NEW):
try:
subprocess.check_output(['ejabberdctl',
'restore',
EJABBERD_BACKUP_NEW])
os.remove(EJABBERD_BACKUP_NEW)
except subprocess.CalledProcessError as err:
print('Failed to restore ejabberd backup database: %s', err)
else:
print('Could not load ejabberd backup database: %s not found'
% EJABBERD_BACKUP_NEW)
def subcommand_change_domainname(arguments):
"""Update ejabberd and jwchat with new domainname"""
if not shutil.which('ejabberdctl'):
print('ejabberdctl not found. Is ejabberd installed?')
return
domainname = arguments.domainname
if not domainname:
# If new domainname is blank, use hostname instead.
domainname = socket.gethostname()
# update jwchat's sitename, if it's installed
if os.path.exists(JWCHAT_CONFIG):
with open(JWCHAT_CONFIG, 'r') as conffile:
lines = conffile.readlines()
with open(JWCHAT_CONFIG, 'w') as conffile:
for line in lines:
if re.match(r'\s*var\s+SITENAME', line):
conffile.write('var SITENAME = "' + domainname + '";\n')
else:
conffile.write(line)
else:
print('Skipping configuring jwchat sitename: %s not found',
JWCHAT_CONFIG)
action_utils.service_stop('ejabberd')
subprocess.call(['pkill', '-u', 'ejabberd'])
# Add updated domainname to ejabberd hosts list.
with open(EJABBERD_CONFIG, 'r') as conffile:
lines = conffile.readlines()
with open(EJABBERD_CONFIG, 'w') as conffile:
in_hosts = False
for line in lines:
if in_hosts:
if re.match(r'\s*-\s*', line):
continue
in_hosts = False
conffile.write(line)
if re.match(r'\s*hosts:', line):
in_hosts = True
conffile.write(' - "' + domainname + '"\n')
action_utils.service_start('ejabberd')
def main():
"""Parse arguments and perform all duties"""
arguments = parse_arguments()
subcommand = arguments.subcommand.replace('-', '_')
subcommand_method = globals()['subcommand_' + subcommand]
subcommand_method(arguments)
if __name__ == '__main__':
main()
Plinth-0.8.1/bin/ 0000775 0000000 0000000 00000000000 12660516711 0013533 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/bin/plinth 0000775 0000000 0000000 00000001367 12660516711 0014766 0 ustar 00root root 0000000 0000000 #!/usr/bin/python3
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
import plinth.__main__
plinth.__main__.main()
Plinth-0.8.1/data/ 0000775 0000000 0000000 00000000000 12660516711 0013674 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/data/etc/ 0000775 0000000 0000000 00000000000 12660516711 0014447 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/data/etc/apache2/ 0000775 0000000 0000000 00000000000 12660516711 0015752 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/data/etc/apache2/conf-available/ 0000775 0000000 0000000 00000000000 12660516711 0020615 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/data/etc/apache2/conf-available/deluge-plinth.conf 0000664 0000000 0000000 00000000210 12660516711 0024216 0 ustar 00root root 0000000 0000000 ##
## On all sites, provide Deluge on a default path: /deluge
##
ProxyPass http://localhost:8112
Plinth-0.8.1/data/etc/apache2/conf-available/ikiwiki-plinth.conf 0000664 0000000 0000000 00000001003 12660516711 0024412 0 ustar 00root root 0000000 0000000 Alias /ikiwiki /var/www/ikiwiki
Alias /ikiwiki-auth /var/www/ikiwiki
AddHandler cgi-script .cgi
Options +ExecCGI
Options +ExecCGI
AuthType basic
AuthName "FreedomBox Login"
AuthBasicProvider ldap
AuthLDAPUrl "ldap:///ou=users,dc=thisbox?uid"
AuthLDAPGroupAttribute memberUid
AuthLDAPGroupAttributeIsDN off
Require ldap-group cn=admin,ou=groups,dc=thisbox
Require ldap-group cn=wiki,ou=groups,dc=thisbox
Plinth-0.8.1/data/etc/apache2/conf-available/jwchat-plinth.conf 0000664 0000000 0000000 00000000536 12660516711 0024244 0 ustar 00root root 0000000 0000000 Alias /jwchat /usr/share/jwchat/www
Options +Indexes +Multiviews +FollowSymLinks
# proxy for BOSH server
ProxyPass /http-bind/ http://localhost:5280/http-bind/
ProxyPassReverse /http-bind/ http://localhost:5280/http-bind/
Require all granted
Plinth-0.8.1/data/etc/apache2/conf-available/repro-plinth.conf 0000664 0000000 0000000 00000000660 12660516711 0024111 0 ustar 00root root 0000000 0000000 ##
## On all sites, provide repro admin interface on a path: /repro
## Only allow users of admin LDAP group.
##
ProxyPass http://localhost:5080
AuthType basic
AuthName "FreedomBox Login"
AuthBasicProvider ldap
AuthLDAPUrl "ldap:///ou=users,dc=thisbox?uid"
AuthLDAPGroupAttribute memberUid
AuthLDAPGroupAttributeIsDN off
Require ldap-group cn=admin,ou=groups,dc=thisbox
Plinth-0.8.1/data/etc/apache2/conf-available/transmission-plinth.conf 0000664 0000000 0000000 00000000752 12660516711 0025515 0 ustar 00root root 0000000 0000000 ##
## On all sites, provide Transmission on a default path: /transmission
##
## Requires the following Apache modules to be enabled:
## mod_headers
## mod_proxy
## mod_proxy_http
##
ProxyPass http://localhost:9091/transmission
## Send the scheme from user's request to enable Transmission to
## redirect URLs, set cookies, set absolute URLs (if any)
## properly.
RequestHeader set X-Forwarded-Proto 'https' env=HTTPS
Plinth-0.8.1/data/etc/apache2/sites-available/ 0000775 0000000 0000000 00000000000 12660516711 0021017 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/data/etc/apache2/sites-available/plinth-ssl.conf 0000664 0000000 0000000 00000001023 12660516711 0023757 0 ustar 00root root 0000000 0000000 ##
## When enabled allows only SSL traffic onto Plinth. This is done by
## redirecting non-secure traffic to secure traffic. The redirect is
## permanent as recommended in:
## http://tools.ietf.org/html/rfc6797#section-7
##
## Requires the following Apache modules to be enabled:
## mod_rewrite
## mod_gnutls
##
RewriteEngine on
# FIXME: Workaround for mod_gnutls (Debian Bug #514005)
RewriteCond %{SERVER_PORT} !^443$
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
Plinth-0.8.1/data/etc/apache2/sites-available/plinth.conf 0000664 0000000 0000000 00000002072 12660516711 0023165 0 ustar 00root root 0000000 0000000 ##
## On all sites, provide Plinth on a default path: /plinth
##
## Requires the following Apache modules to be enabled:
## mod_headers
## mod_proxy
## mod_proxy_http
##
ProxyPass http://127.0.0.1:8000/plinth
## Send the scheme from user's request to enable Plinth to redirect
## URLs, set cookies, set absolute URLs (if any) properly.
RequestHeader set X-Forwarded-Proto 'https' env=HTTPS
## Allow traffic only from private networks
## IPv4 local addresses
Require ip 127.0.0.0/8
## IPv4 link local addresses
Require ip 169.254.0.0/16
## IPv4 class A private addresses
Require ip 10.0.0.0/8
## IPv4 class B private addresses
Require ip 172.16.0.0/12
## IPv4 class C private addresses
Require ip 192.168.0.0/16
## IPv6 local address
Require ip ::1
## IPv6 link local addresses
Require ip fe80::/10
## IPv6 private addresses
Require ip fc00::/7
Plinth-0.8.1/data/etc/ikiwiki/ 0000775 0000000 0000000 00000000000 12660516711 0016107 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/data/etc/ikiwiki/plinth-blog.setup 0000664 0000000 0000000 00000002354 12660516711 0021414 0 ustar 00root root 0000000 0000000 #!/usr/bin/perl
# Ikiwiki setup automator for Plinth (blog version).
require IkiWiki::Setup::Automator;
our $wikiname=$ARGV[0];
our $admin=$ARGV[1];
if (($wikiname eq "") || ($admin eq "")) {
print "Usage: ikiwiki -setup /etc/ikiwiki/plinth-blog.setup blog_name admin_name";
exit;
}
our $wikiname_short=IkiWiki::Setup::Automator::sanitize_wikiname($wikiname);
IkiWiki::Setup::Automator->import(
wikiname => $wikiname,
adminuser => [$admin],
rcs => "git",
srcdir => "/var/lib/ikiwiki/$wikiname_short",
destdir => "/var/www/ikiwiki/$wikiname_short",
repository => "/var/lib/ikiwiki/$wikiname_short.git",
dumpsetup => "/var/lib/ikiwiki/$wikiname_short.setup",
url => "/ikiwiki/$wikiname_short",
cgiurl => "/ikiwiki/$wikiname_short/ikiwiki.cgi",
cgiauthurl => "/ikiwiki-auth/$wikiname_short/ikiwiki.cgi",
cgi_wrapper => "/var/www/ikiwiki/$wikiname_short/ikiwiki.cgi",
add_plugins => [qw{goodstuff websetup comments calendar sidebar trail httpauth}],
rss => 1,
atom => 1,
syslog => 1,
example => "blog",
comments_pagespec => "posts/* and !*/Discussion",
archive_pagespec => "page(posts/*) and !*/Discussion",
global_sidebars => 0,
discussion => 0,
tagbase => "tags",
)
Plinth-0.8.1/data/etc/ikiwiki/plinth-wiki.setup 0000664 0000000 0000000 00000001757 12660516711 0021442 0 ustar 00root root 0000000 0000000 #!/usr/bin/perl
# Ikiwiki setup automator for Plinth.
require IkiWiki::Setup::Automator;
our $wikiname=$ARGV[0];
our $admin=$ARGV[1];
if (($wikiname eq "") || ($admin eq "")) {
print "Usage: ikiwiki -setup /etc/ikiwiki/plinth-wiki.setup wiki_name admin_name";
exit;
}
our $wikiname_short=IkiWiki::Setup::Automator::sanitize_wikiname($wikiname);
IkiWiki::Setup::Automator->import(
wikiname => $wikiname,
adminuser => [$admin],
rcs => "git",
srcdir => "/var/lib/ikiwiki/$wikiname_short",
destdir => "/var/www/ikiwiki/$wikiname_short",
repository => "/var/lib/ikiwiki/$wikiname_short.git",
dumpsetup => "/var/lib/ikiwiki/$wikiname_short.setup",
url => "/ikiwiki/$wikiname_short",
cgiurl => "/ikiwiki/$wikiname_short/ikiwiki.cgi",
cgiauthurl => "/ikiwiki-auth/$wikiname_short/ikiwiki.cgi",
cgi_wrapper => "/var/www/ikiwiki/$wikiname_short/ikiwiki.cgi",
add_plugins => [qw{goodstuff websetup httpauth}],
rss => 1,
atom => 1,
syslog => 1,
)
Plinth-0.8.1/data/etc/init.d/ 0000775 0000000 0000000 00000000000 12660516711 0015634 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/data/etc/init.d/plinth 0000775 0000000 0000000 00000003644 12660516711 0017067 0 ustar 00root root 0000000 0000000 #!/bin/sh
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
### BEGIN INIT INFO
# Provides: plinth
# Required-Start: $network $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: plinth web frontend
# Description:
# Control the plinth web frontend.
### END INIT INFO
# This file is /etc/init.d/plinth
DESC="embedded web frontend"
NAME=plinth
DAEMON=/usr/bin/plinth
PID_FILE=/var/run/plinth.pid
SERVER_DIR=/plinth
PLINTH_USER=plinth
PLINTH_GROUP=plinth
test -x $DAEMON || exit 0
. /lib/lsb/init-functions
case "$1" in
start)
log_daemon_msg "Starting $DESC" "$NAME"
start_daemon -p $PID_FILE $DAEMON --pidfile=$PID_FILE \
--server_dir=$SERVER_DIR
log_end_msg $?
;;
stop)
log_daemon_msg "Stopping $DESC" "$NAME"
killproc -p $PID_FILE $DAEMON
RETVAL=$?
[ $RETVAL -eq 0 ] && [ -e "$PID_FILE" ] && rm -f $PID_FILE
log_end_msg $RETVAL
;;
restart|force-reload)
$0 stop
$0 start
;;
status)
status_of_proc -p $PID_FILE "$DAEMON" plinth && exit 0 || exit $?
;;
*)
echo "Usage: $NAME {start|stop|restart|force-reload|status}" >&2
exit 1
;;
esac
Plinth-0.8.1/data/etc/plinth/ 0000775 0000000 0000000 00000000000 12660516711 0015745 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/data/etc/plinth/modules-enabled/ 0000775 0000000 0000000 00000000000 12660516711 0021005 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/data/etc/plinth/modules-enabled/apps 0000664 0000000 0000000 00000000024 12660516711 0021667 0 ustar 00root root 0000000 0000000 plinth.modules.apps
Plinth-0.8.1/data/etc/plinth/modules-enabled/avahi 0000664 0000000 0000000 00000000025 12660516711 0022015 0 ustar 00root root 0000000 0000000 plinth.modules.avahi
Plinth-0.8.1/data/etc/plinth/modules-enabled/config 0000664 0000000 0000000 00000000026 12660516711 0022173 0 ustar 00root root 0000000 0000000 plinth.modules.config
Plinth-0.8.1/data/etc/plinth/modules-enabled/datetime 0000664 0000000 0000000 00000000030 12660516711 0022515 0 ustar 00root root 0000000 0000000 plinth.modules.datetime
Plinth-0.8.1/data/etc/plinth/modules-enabled/deluge 0000664 0000000 0000000 00000000026 12660516711 0022173 0 ustar 00root root 0000000 0000000 plinth.modules.deluge
Plinth-0.8.1/data/etc/plinth/modules-enabled/diagnostics 0000664 0000000 0000000 00000000033 12660516711 0023233 0 ustar 00root root 0000000 0000000 plinth.modules.diagnostics
Plinth-0.8.1/data/etc/plinth/modules-enabled/dynamicdns 0000664 0000000 0000000 00000000032 12660516711 0023054 0 ustar 00root root 0000000 0000000 plinth.modules.dynamicdns
Plinth-0.8.1/data/etc/plinth/modules-enabled/firewall 0000664 0000000 0000000 00000000030 12660516711 0022526 0 ustar 00root root 0000000 0000000 plinth.modules.firewall
Plinth-0.8.1/data/etc/plinth/modules-enabled/first_boot 0000664 0000000 0000000 00000000032 12660516711 0023075 0 ustar 00root root 0000000 0000000 plinth.modules.first_boot
Plinth-0.8.1/data/etc/plinth/modules-enabled/help 0000664 0000000 0000000 00000000024 12660516711 0021654 0 ustar 00root root 0000000 0000000 plinth.modules.help
Plinth-0.8.1/data/etc/plinth/modules-enabled/ikiwiki 0000664 0000000 0000000 00000000027 12660516711 0022367 0 ustar 00root root 0000000 0000000 plinth.modules.ikiwiki
Plinth-0.8.1/data/etc/plinth/modules-enabled/letsencrypt 0000664 0000000 0000000 00000000033 12660516711 0023300 0 ustar 00root root 0000000 0000000 plinth.modules.letsencrypt
Plinth-0.8.1/data/etc/plinth/modules-enabled/monkeysphere 0000664 0000000 0000000 00000000034 12660516711 0023436 0 ustar 00root root 0000000 0000000 plinth.modules.monkeysphere
Plinth-0.8.1/data/etc/plinth/modules-enabled/mumble 0000664 0000000 0000000 00000000026 12660516711 0022207 0 ustar 00root root 0000000 0000000 plinth.modules.mumble
Plinth-0.8.1/data/etc/plinth/modules-enabled/names 0000664 0000000 0000000 00000000025 12660516711 0022030 0 ustar 00root root 0000000 0000000 plinth.modules.names
Plinth-0.8.1/data/etc/plinth/modules-enabled/networks 0000664 0000000 0000000 00000000027 12660516711 0022603 0 ustar 00root root 0000000 0000000 plinth.modules.networks Plinth-0.8.1/data/etc/plinth/modules-enabled/openvpn 0000664 0000000 0000000 00000000027 12660516711 0022414 0 ustar 00root root 0000000 0000000 plinth.modules.openvpn
Plinth-0.8.1/data/etc/plinth/modules-enabled/owncloud 0000664 0000000 0000000 00000000030 12660516711 0022553 0 ustar 00root root 0000000 0000000 plinth.modules.owncloud
Plinth-0.8.1/data/etc/plinth/modules-enabled/pagekite 0000664 0000000 0000000 00000000030 12660516711 0022512 0 ustar 00root root 0000000 0000000 plinth.modules.pagekite
Plinth-0.8.1/data/etc/plinth/modules-enabled/power 0000664 0000000 0000000 00000000025 12660516711 0022061 0 ustar 00root root 0000000 0000000 plinth.modules.power
Plinth-0.8.1/data/etc/plinth/modules-enabled/privoxy 0000664 0000000 0000000 00000000027 12660516711 0022447 0 ustar 00root root 0000000 0000000 plinth.modules.privoxy
Plinth-0.8.1/data/etc/plinth/modules-enabled/quassel 0000664 0000000 0000000 00000000027 12660516711 0022404 0 ustar 00root root 0000000 0000000 plinth.modules.quassel
Plinth-0.8.1/data/etc/plinth/modules-enabled/repro 0000664 0000000 0000000 00000000025 12660516711 0022054 0 ustar 00root root 0000000 0000000 plinth.modules.repro
Plinth-0.8.1/data/etc/plinth/modules-enabled/restore 0000664 0000000 0000000 00000000027 12660516711 0022412 0 ustar 00root root 0000000 0000000 plinth.modules.restore
Plinth-0.8.1/data/etc/plinth/modules-enabled/roundcube 0000664 0000000 0000000 00000000031 12660516711 0022710 0 ustar 00root root 0000000 0000000 plinth.modules.roundcube
Plinth-0.8.1/data/etc/plinth/modules-enabled/shaarli 0000664 0000000 0000000 00000000027 12660516711 0022352 0 ustar 00root root 0000000 0000000 plinth.modules.shaarli
Plinth-0.8.1/data/etc/plinth/modules-enabled/system 0000664 0000000 0000000 00000000026 12660516711 0022252 0 ustar 00root root 0000000 0000000 plinth.modules.system
Plinth-0.8.1/data/etc/plinth/modules-enabled/tor 0000664 0000000 0000000 00000000023 12660516711 0021527 0 ustar 00root root 0000000 0000000 plinth.modules.tor
Plinth-0.8.1/data/etc/plinth/modules-enabled/transmission 0000664 0000000 0000000 00000000034 12660516711 0023456 0 ustar 00root root 0000000 0000000 plinth.modules.transmission
Plinth-0.8.1/data/etc/plinth/modules-enabled/upgrades 0000664 0000000 0000000 00000000030 12660516711 0022533 0 ustar 00root root 0000000 0000000 plinth.modules.upgrades
Plinth-0.8.1/data/etc/plinth/modules-enabled/users 0000664 0000000 0000000 00000000025 12660516711 0022066 0 ustar 00root root 0000000 0000000 plinth.modules.users
Plinth-0.8.1/data/etc/plinth/modules-enabled/xmpp 0000664 0000000 0000000 00000000024 12660516711 0021710 0 ustar 00root root 0000000 0000000 plinth.modules.xmpp
Plinth-0.8.1/data/etc/plinth/plinth.config 0000664 0000000 0000000 00000002677 12660516711 0020446 0 ustar 00root root 0000000 0000000 [Path]
# directory locations
file_root = /usr/share/plinth
config_dir = /etc/plinth
data_dir = /var/lib/plinth
log_dir = /var/log/plinth
pid_dir = /var/run
server_dir = /plinth
actions_dir = /usr/share/plinth/actions
doc_dir = /usr/share/doc/plinth
# file locations
store_file = %(data_dir)s/plinth.sqlite3
status_log_file = %(log_dir)s/status.log
access_log_file = %(log_dir)s/access.log
pidfile = %(pid_dir)s/plinth.pid
[Network]
host = 127.0.0.1
port = 8000
# Enable the following only if Plinth is behind a proxy server. The
# proxy server should properly clean and the following HTTP headers:
# X-Forwarded-Host
# X-Forwarded-Proto
# If you enable these unnecessarily, this will lead to serious security
# problems. For more information, see
# https://docs.djangoproject.com/en/1.7/ref/settings/
#
# These are enabled by default in Plinth because the default
# configuration allows only connections from localhost
#
# Leave the values blank to disable
use_x_forwarded_host = True
secure_proxy_ssl_header = HTTP_X_FORWARDED_PROTO
[Misc]
box_name = FreedomBox
# The danube_edition changes the firstboot process and offers entering a
# voucher for a freedombox.me sub-domain. This functionality requires
# additional debian packages to be installed:
#
# pagekite, python3-requests
#
# They are not added as dependencies to keep the normal installation images
# lean, but make sure to add them if you want to build danube-edition images.
danube_edition = False
Plinth-0.8.1/data/etc/sudoers.d/ 0000775 0000000 0000000 00000000000 12660516711 0016355 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/data/etc/sudoers.d/plinth 0000664 0000000 0000000 00000000075 12660516711 0017600 0 ustar 00root root 0000000 0000000 plinth ALL=(ALL:ALL) NOPASSWD:/usr/share/plinth/actions/*
Plinth-0.8.1/data/lib/ 0000775 0000000 0000000 00000000000 12660516711 0014442 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/data/lib/systemd/ 0000775 0000000 0000000 00000000000 12660516711 0016132 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/data/lib/systemd/system/ 0000775 0000000 0000000 00000000000 12660516711 0017456 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/data/lib/systemd/system/plinth.service 0000664 0000000 0000000 00000001633 12660516711 0022341 0 ustar 00root root 0000000 0000000 #
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
[Unit]
Description=Plinth Web Interface
Documentation=man:plinth(1)
After=network.target
[Service]
ExecStart=/usr/bin/plinth --no-daemon
Restart=on-failure
ExecReload=/bin/kill -HUP $MAINPID
[Install]
WantedBy=multi-user.target
Plinth-0.8.1/data/usr/ 0000775 0000000 0000000 00000000000 12660516711 0014505 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/data/usr/lib/ 0000775 0000000 0000000 00000000000 12660516711 0015253 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/data/usr/lib/firewalld/ 0000775 0000000 0000000 00000000000 12660516711 0017224 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/data/usr/lib/firewalld/services/ 0000775 0000000 0000000 00000000000 12660516711 0021047 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/data/usr/lib/firewalld/services/mumble-plinth.xml 0000664 0000000 0000000 00000001134 12660516711 0024345 0 ustar 00root root 0000000 0000000
Mumble Voice Chat Server
Mumble is an open source, low-latency, encrypted, high quality voice chat software primarily intended for use while gaming. Mumble uses a client-server architecture which allows users to talk to each other via the same server. Enable this if you are running a Mumble server and if you wish to connect external clients such as Mumble desktop client and Plumble Android app to your Mumble server.
Plinth-0.8.1/data/usr/lib/firewalld/services/quassel-plinth.xml 0000664 0000000 0000000 00000000421 12660516711 0024537 0 ustar 00root root 0000000 0000000
Quassel IRC
Quassel is a distributed IRC client, meaning that one or more clients can attach to and detach from the central core.
Plinth-0.8.1/data/usr/lib/firewalld/services/sip-plinth.xml 0000664 0000000 0000000 00000000643 12660516711 0023663 0 ustar 00root root 0000000 0000000
SIP
The Session Initiaion Protocol (SIP) is commonly used in Internet telephony for audio/video calls and instant messaging. Enable this if you are running a SIP proxy, registrar, redirector or gateway server over an unencrypted channel.
Plinth-0.8.1/data/usr/lib/firewalld/services/sip-tls-plinth.xml 0000664 0000000 0000000 00000000700 12660516711 0024455 0 ustar 00root root 0000000 0000000
SIP over TLS/DTLS
The Session Initiaion Protocol (SIP) is commonly used in Internet telephony for audio/video calls and instant messaging. Enable this if you are running a SIP proxy, registrar, redirector or gateway server over a channel encrypted using TLS or DTLS.
Plinth-0.8.1/data/usr/lib/freedombox/ 0000775 0000000 0000000 00000000000 12660516711 0017405 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/data/usr/lib/freedombox/first-run.d/ 0000775 0000000 0000000 00000000000 12660516711 0021560 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/data/usr/lib/freedombox/first-run.d/90_firewall 0000775 0000000 0000000 00000007532 12660516711 0023632 0 ustar 00root root 0000000 0000000 #!/bin/sh
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
# Enable tracing to see the commands in
# /var/log/freedombox-first-run.log
set -x
# Set the default firewall zone. When network connections are
# configured outside of FreedomBox/Plinth, they will not be able to
# serve the Plinth web interface. This is because all such interfaces
# will fall in the default firewall zone and that is, by default,
# 'public'. On 'public' zone we don't allow Plinth web interface as
# this zone is not managed.
#
# Configuration of network connections happen outside for
# FreedomBox/Plinth for various reasons:
#
# - Existing network connections before installation of
# freedombox-setup
#
# - Connections configured in /etc/network/interfaces
#
# - Connections manually configured using nmtui
#
# - Connections created using GUI environments such as GNOME
#
# Rather then clearing out /etc/network/interfaces during setup and
# expecting the connections not to be created outside of Plinth,
# setting the default firewall zone is a better approach. This
# default zone selection fits with the main purpose of FreedomBox to
# be a router which is also reflected by the fact that only 'external'
# and 'internal' zones are managed.
firewall-cmd --set-default-zone=external
# Setup firewall rules for all the services enabled by default.
# Ideally all non-essential services are enabled from Plinth which
# automatically takes care of enabling appropirate firewall ports. The
# following is then for essential services and services that are not
# yet configurable from Plinth.
# HTTP (JWChat, ownCloud)
firewall-cmd --zone=external --permanent --add-service=http
firewall-cmd --zone=internal --permanent --add-service=http
# HTTPS (Plinth, JWChat, ownCloud)
firewall-cmd --zone=external --permanent --add-service=https
firewall-cmd --zone=internal --permanent --add-service=https
# Tor
firewall-cmd --zone=internal --permanent --add-service=tor-socks
# NTP
firewall-cmd --zone=internal --permanent --add-service=ntp
# DNS
firewall-cmd --zone=internal --permanent --add-service=dns
# mDNS
firewall-cmd --zone=internal --permanent --add-service=mdns
# DHCP
firewall-cmd --zone=internal --permanent --add-service=dhcp
# Bootp Server and Client (not enabled)
#firewall-cmd --zone=internal --permanent --add-port=67/tcp
#firewall-cmd --zone=internal --permanent --add-port=67/udp
#firewall-cmd --zone=internal --permanent --add-port=68/tcp
#firewall-cmd --zone=internal --permanent --add-port=68/udp
# LDAP (not enabled)
#firewall-cmd --zone=internal --permanent --add-service=ldap
#firewall-cmd --zone=internal --permanent --add-service=ldaps
# OpenVPN (not enabled)
#firewall-cmd --zone=external --permanent --add-service=openvpn
#firewall-cmd --zone=internal --permanent --add-service=openvpn
# Privoxy
firewall-cmd --zone=internal --permanent --add-service=privoxy
# XMPP
firewall-cmd --zone=external --permanent --add-service=xmpp-server
firewall-cmd --zone=internal --permanent --add-service=xmpp-server
firewall-cmd --zone=external --permanent --add-service=xmpp-client
firewall-cmd --zone=internal --permanent --add-service=xmpp-client
firewall-cmd --zone=external --permanent --add-service=xmpp-bosh
firewall-cmd --zone=internal --permanent --add-service=xmpp-bosh
Plinth-0.8.1/data/usr/lib/freedombox/setup.d/ 0000775 0000000 0000000 00000000000 12660516711 0020767 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/data/usr/lib/freedombox/setup.d/86_plinth 0000775 0000000 0000000 00000002000 12660516711 0022520 0 ustar 00root root 0000000 0000000 #!/bin/sh
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
# Enable Apache modules required for Plinth.
echo "Configuring Apache for Plinth..."
make-ssl-cert generate-default-snakeoil
a2enmod headers
a2enmod proxy
a2enmod proxy_http
a2enmod rewrite
a2enmod ssl
a2enconf javascript-common
a2ensite plinth.conf
a2ensite plinth-ssl.conf
echo "Done configuring Apache for Plinth."
Plinth-0.8.1/data/usr/share/ 0000775 0000000 0000000 00000000000 12660516711 0015607 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/data/usr/share/augeas/ 0000775 0000000 0000000 00000000000 12660516711 0017054 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/data/usr/share/augeas/lenses/ 0000775 0000000 0000000 00000000000 12660516711 0020345 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/data/usr/share/augeas/lenses/pagekite.aug 0000664 0000000 0000000 00000005147 12660516711 0022643 0 ustar 00root root 0000000 0000000 (*
Module: Pagekite
Parses /etc/pagekite.d/
Author: Michael Pimmer
About: License
This file is licenced under the LGPL v2+, like the rest of Augeas.
*)
module Pagekite =
autoload xfm
(* View: lns *)
(* Variables *)
let equals = del /[ \t]*=[ \t]*/ "="
let neg2 = /[^# \n\t]+/
let neg3 = /[^# \:\n\t]+/
let eol = del /\n/ "\n"
(* Match everything from here to eol, cropping whitespace at both ends *)
let to_eol = /[^ \t\n](.*[^ \t\n])?/
(* A key followed by comma-separated values
k: name of the key
key_sep: separator between key and values
value_sep: separator between values
sto: store for values
*)
let key_csv_line (k:string) (key_sep:lens) (value_sep:lens) (sto:lens) =
[ key k . key_sep . [ seq k . sto ] .
[ seq k . value_sep . sto ]* . Util.eol ]
(* entries for pagekite.d/10_account.rc *)
let domain = [ key "domain" . equals . store neg2 . Util.comment_or_eol ]
let frontend = Build.key_value_line ("frontend" | "frontends")
equals (store Rx.neg1)
let host = Build.key_value_line "host" equals (store Rx.ip)
let ports = key_csv_line "ports" equals Sep.comma (store Rx.integer)
let protos = key_csv_line "protos" equals Sep.comma (store Rx.word)
(* entries for pagekite.d/20_frontends.rc *)
let kitesecret = Build.key_value_line "kitesecret" equals (store Rx.space_in)
let kv_frontend = Build.key_value_line ( "kitename" | "fe_certname" |
"ca_certs" | "tls_endpoint" )
equals (store Rx.neg1)
(* entries for services like 80_httpd.rc *)
let service_colon = del /[ \t]*:[ \t]*/ " : "
let service_on = [ key "service_on" . [ seq "service_on" . equals .
[ label "protocol" . store neg3 ] . service_colon .
[ label "kitename" . (store neg3) ] . service_colon .
[ label "backend_host" . (store neg3) ] . service_colon .
[ label "backend_port" . (store neg3) ] . service_colon . (
[ label "secret" . (store Rx.no_spaces) . Util.eol ] | eol
) ] ]
let service_cfg = [ key "service_cfg" . equals . store to_eol . eol ]
let flags = ( "defaults" | "isfrontend" | "abort_not_configured" | "insecure" )
let entries = Build.flag_line flags
| domain
| frontend
| host
| ports
| protos
| kv_frontend
| kitesecret
| service_on
| service_cfg
let lns = ( entries | Util.empty | Util.comment )*
(* View: filter *)
let filter = incl "/etc/pagekite.d/*.rc"
. Util.stdexcl
let xfm = transform lns filter
Plinth-0.8.1/data/usr/share/augeas/lenses/tests/ 0000775 0000000 0000000 00000000000 12660516711 0021507 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/data/usr/share/augeas/lenses/tests/test_pagekite.aug 0000664 0000000 0000000 00000005376 12660516711 0025050 0 ustar 00root root 0000000 0000000 module Test_Pagekite =
let conf1 = "# Use the pagekite.net service defaults.
defaults
"
test Pagekite.lns get conf1 =
{ "#comment" = "Use the pagekite.net service defaults." }
{ "defaults" }
let conf2 ="
frontends = pagekite.freedombox.me
ports=80,81
"
test Pagekite.lns get conf2 =
{ }
{ "frontends" = "pagekite.freedombox.me" }
{ "ports"
{ "1" = "80" }
{ "2" = "81" } }
let conf3 = "frontend=pagekite.freedombox.me
host=192.168.0.3
"
test Pagekite.lns get conf3 =
{ "frontend" = "pagekite.freedombox.me" }
{ "host" = "192.168.0.3" }
let conf4 = "isfrontend
ports=80,443
protos=http,https
domain=http,https:*.your.domain:MakeUpAPasswordHere
"
test Pagekite.lns get conf4 =
{ "isfrontend" }
{ "ports"
{ "1" = "80" }
{ "2" = "443" } }
{ "protos"
{ "1" = "http" }
{ "2" = "https" } }
{ "domain" = "http,https:*.your.domain:MakeUpAPasswordHere" }
let conf_account = "kitename = my.freedombox.me
kitesecret = 0420
# Delete this line!
abort_not_configured
"
test Pagekite.lns get conf_account =
{ "kitename" = "my.freedombox.me" }
{ "kitesecret" = "0420" }
{ "#comment" = "Delete this line!" }
{ "abort_not_configured" }
let conf_service = "
service_on = raw/22:@kitename : localhost:22 : @kitesecret
service_on=http:192.168.0.1:127.0.0.1:80:
service_on=https:yourhostname,fqdn:127.0.0.1:443:
"
test Pagekite.lns get conf_service =
{ }
{ "service_on"
{ "1"
{ "protocol" = "raw/22" }
{ "kitename" = "@kitename" }
{ "backend_host" = "localhost" }
{ "backend_port" = "22" }
{ "secret" = "@kitesecret" }
}
}
{ "service_on"
{ "2"
{ "protocol" = "http" }
{ "kitename" = "192.168.0.1" }
{ "backend_host" = "127.0.0.1" }
{ "backend_port" = "80" }
}
}
{ "service_on"
{ "3"
{ "protocol" = "https" }
{ "kitename" = "yourhostname,fqdn" }
{ "backend_host" = "127.0.0.1" }
{ "backend_port" = "443" }
}
}
let conf_encryption = "
frontend=frontend.your.domain:443
fe_certname=frontend.your/domain
ca_certs=/etc/pagekite.d/site-cert.pem
tls_endpoint=frontend.your.domain:/path/to/frontend.pem
"
test Pagekite.lns get conf_encryption =
{ }
{ "frontend" = "frontend.your.domain:443" }
{ "fe_certname" = "frontend.your/domain" }
{ "ca_certs" = "/etc/pagekite.d/site-cert.pem" }
{ "tls_endpoint" = "frontend.your.domain:/path/to/frontend.pem" }
let conf_service_cfg = "insecure
service_cfg = KITENAME.pagekite.me/80 : insecure : True
"
test Pagekite.lns get conf_service_cfg =
{ "insecure" }
{ "service_cfg" = "KITENAME.pagekite.me/80 : insecure : True" }
Plinth-0.8.1/data/var/ 0000775 0000000 0000000 00000000000 12660516711 0014464 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/data/var/lib/ 0000775 0000000 0000000 00000000000 12660516711 0015232 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/data/var/lib/plinth/ 0000775 0000000 0000000 00000000000 12660516711 0016530 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/data/var/lib/plinth/.gitkeep 0000664 0000000 0000000 00000000000 12660516711 0020147 0 ustar 00root root 0000000 0000000 Plinth-0.8.1/data/var/lib/plinth/sessions/ 0000775 0000000 0000000 00000000000 12660516711 0020376 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/data/var/lib/plinth/sessions/.gitkeep 0000664 0000000 0000000 00000000000 12660516711 0022015 0 ustar 00root root 0000000 0000000 Plinth-0.8.1/data/var/log/ 0000775 0000000 0000000 00000000000 12660516711 0015245 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/data/var/log/plinth/ 0000775 0000000 0000000 00000000000 12660516711 0016543 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/data/var/log/plinth/.gitkeep 0000664 0000000 0000000 00000000000 12660516711 0020162 0 ustar 00root root 0000000 0000000 Plinth-0.8.1/data/var/run/ 0000775 0000000 0000000 00000000000 12660516711 0015270 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/data/var/run/.gitkeep 0000664 0000000 0000000 00000000000 12660516711 0016707 0 ustar 00root root 0000000 0000000 Plinth-0.8.1/doc/ 0000775 0000000 0000000 00000000000 12660516711 0013530 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/doc/Makefile 0000664 0000000 0000000 00000004153 12660516711 0015173 0 ustar 00root root 0000000 0000000 #!/usr/bin/make -f
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
MANUAL_URL="https://wiki.debian.org/FreedomBox/Manual?action=show&mimetype=text%2Fdocbook"
OUTPUTS=freedombox-manual.pdf freedombox-manual.html freedombox-manual.part.html plinth.1
all: $(OUTPUTS)
# Do not edit the manual page in this directory. The manual is
# maintained as separate pages on the FreedomBox wiki and aggregated
# into a single page using the MoinMoin include feature. Then it is
# exported as Docbook format and kept here.
#
# It is important to keep a copy of the manual in this source tree
# because Debian packages promise an entire source tree including the
# documentation.
#
# Use 'make fetch' to retrieve the latest manual from the wiki and
# commit it to the repository. The wiki page is already reviewed, so
# commits that update the manual just using the 'fetch' target do not
# require further reviews.
.PHONY: fetch
fetch:
wget --quiet -O - $(MANUAL_URL) | \
xmllint --format --output freedombox-manual.raw.xml -
xsltproc --output freedombox-manual.xml fixes.xslt freedombox-manual.raw.xml
xsltproc fetch-images.xslt freedombox-manual.raw.xml | sort -u | \
awk '{print "wget --quiet -O images/" $$1 " " $$2}' | sh
rm -f freedombox-manual.raw.xml
%.pdf: %.xml
xmlto --with-dblatex pdf $<
%.part.html: %.html
perl -pe 'BEGIN {undef $/} s/.*]*>(.*)<\/body>.*/$1/s' $< > $@
%.html: %.xml
docbook2html --nochunks $<
%.1: %.xml
xmlto man $<
.PHONY: clean
clean:
rm -f $(OUTPUTS)
Plinth-0.8.1/doc/fetch-images.xslt 0000664 0000000 0000000 00000004303 12660516711 0017000 0 ustar 00root root 0000000 0000000
"
"
Plinth-0.8.1/doc/fixes.xslt 0000664 0000000 0000000 00000007406 12660516711 0015571 0 ustar 00root root 0000000 0000000
pt
images/
Plinth-0.8.1/doc/freedombox-manual.xml 0000664 0000000 0000000 00001045316 12660516711 0017671 0 ustar 00root root 0000000 0000000
FreedomBox Manual
FreedomBox Introduction
FreedomBox is a personal server that protects your privacy. It is a free software stack, a subset of the Debian universal operating system, that can be installed in many flavors of inexpensive and power-efficient hardware. The simplicity of setting up and operating a FreedomBox is similar to that of a smart phone.
Smart Router
FreedomBox runs in a physical computer and can route your traffic. It can sit between various devices at home such as mobiles, laptops and TVs and the Internet replacing a home wireless router. By routing traffic, FreedomBox can remove tracking advertisements and malicious web bugs before they ever reach your devices. FreedomBox can cloak your location and protect your anonymity by "onion routing" your traffic over Tor. FreedomBox provides a VPN server that you can use while you are away from home to keep your traffic secret on untrusted public wireless networks and to securely access various devices at home. It can also be carried along with your laptop and used to connect to public networks at work, school, or office to avail its services. It could be used in a village to provide communications throughout the village. In future, FreedomBox intends to provide support for alternative ways of connecting to the Internet such as Mesh networks.
Private Cloud
FreedomBox provides services: to your computers and mobile devices in your home and to computers and mobile devices of other people who are your friends. FreedomBox provides file sharing like Dropbox, shared calendering like Google or Yahoo and photo sharing. FreedomBox provides instant messaging and truly secure voice conference calling that works on low bandwidth providing high quality. FreedomBox has a blog and wiki to let you publish your content and collaborate with the rest of the world. Coming soon, a personal email server and federated social networking using GNU Social and Diaspora, providing privacy-respecting alternatives to Gmail and Facebook.
Quick Start
If you have not already done so, download and install a FreedomBox image by following the instructions on the Download page.
Plug one end of your ethernet cord into your FreedomBox's ethernet port, and plug the other end into your router.
On the Dreamplug, the eth0 port (the one toward the middle of the box) should be connected to your router.
If your device has a 2nd ethernet port, you can connect your computer to it directly, using an ethernet cable.
Power on your your FreedomBox.
On first boot, the FreedomBox will perform initial setup and then reboot. This may take several minutes.
After the FreedomBox has rebooted, you can access its web interface (called Plinth) through your web browser.
If your computer is connected directly to the FreedomBox through a second (LAN) ethernet port, you can browse to: or .
If your computer supports mDNS (GNU/Linux, Mac OSX and Windows with mDNS software installed), you can browse to: (or )
If neither of these methods are available, then you will need to figure out the IP address of your FreedomBox. You can use the "nmap" program to find its IP address:
nmap -p 80 --open -sV 192.168.0.0/24
Your FreedomBox will show up as an IP address with an open tcp port 80 using Apache httpd service on Debian, such as the example below which would make it accessible at :
Nmap scan report for 192.168.0.165
Host is up (0.00088s latency).
PORT STATE SERVICE VERSION
80/tcp open http Apache httpd 2.4.17 ((Debian))
On accessing Plinth your browser will warn you that it communicates securely but that it regards the security certificate for doing so as invalid. This is a fact you need to accept because the certificate is auto generated on the box and therefore "self-signed" (the browser might also use words such as "untrusted", "not private", "privacy error" or "unknown issuer/authority"). Telling your browser that you are aware of this might involve pressing buttons such as "I understand the Risks", "proceed to ... (unsafe)" or "Add exception".
On the intial access you will see a welcome page that asks you to provide some basic information for setting up your FreedomBox.
After completing the form, you will be logged in to Plinth and able to access apps and configuration through the interface.
If your computer is connected directly to the FreedomBox, your FreedomBox can act as a router, allowing you to access the Internet.
Now, you can try any of the Apps that are available on FreedomBox.
Getting Help
This document is intended to give you all the information you need to get started with your FreedomBox. However, if you have any questions after reading this document, you can get help by:
Emailing freedombox-discuss@lists.alioth.debian.org. You can also sign up to receive copies of every discussion that happens on the mailing list and read the archives.
Chatting at #freedombox@irc.oftc.net.
Reading the wiki.
Reading the FreedomBox Foundation's website.
Reading the FreedomBox Project Page.
Release Notes
The following are the release notes for each FreedomBox version.
Version 0.8 (2016-02)
Added Quassel, an IRC client that stays connected to IRC networks and can synchronize multiple frontends.
Improved first boot user interface.
Fixed Transmission RPC whitelist issue.
Added translations for Turkish, Chinese, and Russian. Fixed and updated translations in other languages.
Added Monkeysphere, which uses PGP web of trust for SSH host key verification.
Added Let's Encrypt, to obtain certificates for domains, so that browser certificate warnings can be avoided.
Added repro, a SIP server for audio and video calls.
Allow users to set their SSH public keys, so they can login over SSH without a password.
Version 0.7 (2015-12-13)
Translations! Full translations of the interface in Danish, Dutch, French, German and Norwegian Bokmål, and partial Telugu.
Support for OLinuXino A20 MICRO and LIME2
New Plinth applications: OpenVPN, reStore
Improved first-boot experience
Many bugfixes and cleanups
Version 0.6 (2015-10-31)
New supported hardware target: Raspberry Pi 2
New modules in Plinth:
Shaarli: Web application to manage and share bookmarks
Date & Time: Configure time zone and NTP service
Service Discovery: Configure Avahi service
Documentation revamp including new user manual and developer guide
Improved diagnostic tests, available in Plinth
Avoid unnecessary changes when installing on existing Debian system
Network configuration supports PPPoE connections
Debian packages can be download over Tor
Version 0.5 (2015-08-07)
New targets: CubieTruck, i386, amd64
New apps in Plinth: Transmission, Dynamic DNS, Mumble, ikiwiki, Deluge, Roundcube, Privoxy
NetworkManager handles network configuration and can be manipulated through Plinth.
Software Upgrades (unattended-upgrades) module can upgrade the system, and enable automatic upgrades.
Plinth is now capable of installing ejabberd, jwchat, and privoxy, so they are not included in image but can be installed when needed.
User authentication through LDAP for SSH, XMPP (ejabberd), and ikiwiki.
Unit test suite is automatically run on Plinth upstream. This helps us catch at least some code errors before they are discovered by users!
New, simpler look for Plinth.
Performance improvements for Plinth.
Version 0.3 (2015-01-20)
Tor Bridges: All boxes now act as non-exit Tor bridges, routing traffic for the Tor network.
Firewall: firewall is on by default and is automatically managed.
Add BeagleBone support. We now have images for BeagleBone, RaspberryPi, VirtualBox i386/amd64, and DreamPlug.
Ability to enable and use Tor Hidden Services. Works with Ejabberd/JWChat and ownCloud services.
Enable Tor obfsproxy with scramblesuit.
Drop well-known root password (an account with sudo capabilities still exists for now but will be removed soon).
Switch to unstable as suite of choice for easier development.
Newer images are built with systemd by default (due to Debian change).
Install and operate firewall automatically (uses firewalld).
Major restructuring of Plinth UI using Python3, Django web development framework and Bootstrap3. Code quality is much better and UI is more polished.
Introduced packaging framework in Plinth UI for on-demand application installation.
Version 0.2 (2014-03-16)
Support for Raspberry Pi and VirtualBox (x86) in addition to the DreamPlug.
New Services:
Configuration Management UI.
Instant Messaging.
OwnCloud.
dnsmasq.
Low-Level Configuration Management.
Service Announcement.
LDAP Server.
LXC Support.
Source Packages.
The privoxy setup is now the default from Debian.
Version 0.1 (2013-02-26)
First FreedomBox software release (0.1 image, developer release).
Full hardware support in Debian
Support for DreamPlug.
Basic software tools selected as common working environment:
User interface system "plinth"
Cryptography tools: gpg or "monkeysphere"
Box-to-box communication design: Freedom-buddy (uses TOR network)
Web cleaning: "privoxy-freedombox".
Download and Install
Wellcome to the FreedomBox download page. You may either install FreedomBox on one of the supported inexpensive hardware, on a Linux Debian operating system, or deploy on a virtual machine. Installing on Debian is easy because FreedomBox is available as packages. On hardware, you may need a little bit of technical expertise to setup. What we are requiring is to buy a device and plug in an SD card. In case of trouble, please read and interact with the Questions and Answers page based on Freedombox-discuss mailing list archives.
Downloading on Debian
If you are installing on Debian, you don't need to download these images. Instead read instructions on setting up FreedomBox on Debian.
Downloading on Hardware or Virtual Machine
Prepare your device
See hardware specific instructions on how to prepare your device at the Hardware section. Gather and read a lot of documentation on the web about first boot and flashing USB or SD Card on your hardware.
Downloading Images
Recent images for supported targets are available here:
Official Images:
Verifying the Downloaded Images
It is important to verify the images you have downloaded to ensure that the file has not be corrupted during the transmission and that it is indeed the image built by FreedomBox developers.
First open a terminal and import the public key of the FreedomBox developer who built the images:
$ gpg --keyserver x-hkp://pool.sks-keyservers.net --recv-keys 0x36C361440C9BC971
Next, verify the fingerprint of the public key:
$ gpg --fingerprint 0x36C361440C9BC971
pub 4096R/0C9BC971 2011-11-12
Key fingerprint = BCBE BD57 A11F 70B2 3782 BC57 36C3 6144 0C9B C971
uid Sunil Mohan Adapa <sunil@medhas.org>
sub 4096R/4C1D4B57 2011-11-12
Finally, verify your downloaded image with its signature file .sig
. For example:
$ gpg --verify freedombox-unstable-free_2015-12-13_cubietruck-armhf.img.xz.sig freedombox-unstable-free_2015-12-13_cubietruck-armhf.img.xz
gpg: Signature made Thursday 15 January 2015 09:27:50 AM IST using RSA key ID 0C9BC971
gpg: Good signature from "Sunil Mohan Adapa <sunil@medhas.org>"
gpg: WARNING: This key is not certified with a trusted signature!
gpg: There is no indication that the signature belongs to the owner.
Primary key fingerprint: BCBE BD57 A11F 70B2 3782 BC57 36C3 6144 0C9B C971
Installation
After the download you can use the image to boot supported hardware (including virtual machines). You'll need to copy the image to the memory card or USB stick as follows:
Figure out which device your card actually is.
Unplug your card.
Run dmesg -w
to show and follow the kernel messages.
Plug your card in. You will see messages such as following:
[33299.023096] usb 4-6: new high-speed USB device number 12 using ehci-pci
[33299.157160] usb 4-6: New USB device found, idVendor=058f, idProduct=6361
[33299.157162] usb 4-6: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[33299.157164] usb 4-6: Product: Mass Storage Device
[33299.157165] usb 4-6: Manufacturer: Generic
[33299.157167] usb 4-6: SerialNumber: XXXXXXXXXXXX
[33299.157452] usb-storage 4-6:1.0: USB Mass Storage device detected
[33299.157683] scsi host13: usb-storage 4-6:1.0
[33300.155626] scsi 13:0:0:0: Direct-Access Generic- Compact Flash 1.01 PQ: 0 ANSI: 0
[33300.156223] scsi 13:0:0:1: Direct-Access Multiple Flash Reader 1.05 PQ: 0 ANSI: 0
[33300.157059] sd 13:0:0:0: Attached scsi generic sg4 type 0
[33300.157462] sd 13:0:0:1: Attached scsi generic sg5 type 0
[33300.462115] sd 13:0:0:1: [sdg] 30367744 512-byte logical blocks: (15.5 GB/14.4 GiB)
[33300.464144] sd 13:0:0:1: [sdg] Write Protect is off
[33300.464159] sd 13:0:0:1: [sdg] Mode Sense: 03 00 00 00
[33300.465896] sd 13:0:0:1: [sdg] No Caching mode page found
[33300.465912] sd 13:0:0:1: [sdg] Assuming drive cache: write through
[33300.470489] sd 13:0:0:0: [sdf] Attached SCSI removable disk
[33300.479493] sdg: sdg1
[33300.483566] sd 13:0:0:1: [sdg] Attached SCSI removable disk
In the above case, the disk that is newly inserted is available as /dev/sdg. Very carefully note this and use it in the copying step below.
Decompress the downloaded image using tar:
$ xz -d freedombox-unstable-free_2015-12-13_cubietruck-armhf.img.xz
The above command is an example for the cubietruck image built on 2015-12-13. Your downloaded file name will be different.
Copy the image to your card. Double check and make sure you don't write to your computer's main storage (such as /dev/sda). Also make sure that you don't run this step as root to avoid potentially overriding data on your hard drive due to a mistake in identifying the device or errors while typing the command. USB disks and SD cards inserted into the system should typically be write accessible to normal users. If you don't have permission to write to your SD card as a user, you may need to run this command as root. In this case triple check everything before you run the command. Another safety precaution is to unplug all external disks except the SD card before running the command.
For example, if your SD card is /dev/sdf as noted in the first step above, then to copy the image, run:
$ dd bs=1M if=freedombox-unstable-free_2015-12-13_cubietruck-armhf.img of=/dev/sdf conv=fdatasync
The above command is an example for the cubietruck image built on 2015-12-13. Your image file name will be different.
When picking a device, use the drive-letter destination, like /dev/sdf, not a numbered destination, like /dev/sdf1. The device without a number refers to the entire device, while the device with a number refers to a specific partition. We want to use the whole device. Downloaded images contain complete information about how many partitions there should be, their sizes and types. You don't have to format your SD card or create partitions. All the data on the SD card will be wiped off during the write process.
Use the image by inserting the SD card or USB disk into the target
device and booting from it. Your device should also be prepared (see the Hardware section).
Read (the rest of) the Manual for instructions on how to use applications in FreedomBox.
Apps
Anonymity Network (Tor)
What is Tor?
Tor is a network of server operated by volunteers. It allows users of these servers to improve their privacy and security while surfing on the Internet. You and your friends are able to access to your FreedomBox via Tor network without revealing its IP address. Activating Tor application on your FreedomBox, you will be able to offer remote services (chat, wiki, file sharing, etc...) without showing your location. This application will give you a better protection than a public web server because you will be less exposed to intrusive people on the web.
Using Tor to browse anonymously
Tor Browser is the recommended way to browse the web using Tor. You can download the Tor Browser from and follow the instructions on that site to install and run it.
Using Tor Hidden Service to access your FreedomBox
Tor Hidden Service provides a way to access your FreedomBox, even if it's behind a router or firewall.
To enable Tor Hidden Service, first navigate to the Anonymity Network (Tor) page. (If you don't see it, click on the FreedomBox logo at the top-left of the page, to go to the main Apps page.) On the Anonymity Network (Tor) page, under Configuration, check "Enable Tor Hidden Service", then press the Update setup button. Tor will be reconfigured and restarted.
After a while, the page will refresh and under Status, you will see a table listing the Hidden Service .onion address. Copy the entire address (ending in .onion) and paste it into the Tor Browser's address field, and you should be able to access your FreedomBox. (You may see a certificate warning because FreedomBox has a self-signed certificate.)
Currently only HTTP (port 80) and HTTPS (port 443) are accessible through the Tor Hidden Service configured on the FreedomBox.
Using Tor SOCKS port (advanced)
FreedomBox provides a Tor SOCKS port that other applications can connect to, in order to route their traffic over the Tor network. This port is accessible on any interfaces configured in the internal firewall zone. To configure the application, set SOCKS Host to the internal network connection's IP address, and set the SOCKS Port to 9050.
Deluge
What is Deluge?
Your FreedomBox provides a Deluge application to enable. Deluge is a lightweight Bit Torrent client. Bit Torrent is a communications protocol using peer-to-peer (P2P) file sharing. P2P is a system that aims to interconnect end-user machines. Highly configurable, Deluge offers functionalities in the form of plugins.
Transmission
What is Transmission ?
In addition to Deluge Bit Torrent, your FreedomBox provides a Transmission application to enable. Transmission is a lightweight Bit Torrent client allowing end-user machine to share files (documents, pictures, sounds, videos and programs). Transmission is well known for its simplicity and a default configuration that "Just Works".
Shaarli
What is Shaarli?
Shaarli is personal (single-user) bookmarking application to install on your FreedomBox. It can also be used for micro-blogging, pastebin, online notepad and snippet archive. Shaarli is designed as a no-database delicious clone. As such, it provides very fast services, easy backup and import/export links as desktop or mobile browser bookmarks. Links stored can be public or private. Shaarli delivers ATOM and RSS feeds from its minimalist interface.
Chat Server (XMPP)
What is XMPP?
XMPP is a federated protocol for Instant Messaging. This means that users who have accounts on one server, can talk to users that are on another server.
Setting the Domain Name
For XMPP to work, your FreedomBox needs to have a Domain Name that can be accessed over the public Internet. You can read more about obtaining a Domain Name in the Dynamic DNS section of this manual.
Once you have a Domain Name, you can tell your FreedomBox to use it by setting the Domain Name in the System Config.
Please note that Pagekite does not support the XMPP protocol at this time.
Registering XMPP users through SSO
Currently, all users created through Plinth will be able to login to the XMPP server. You can add new users through the System Users and Groups module. It does not matter which Groups are selected for the new user.
Dynamic DNS
What is Dynamic DNS?
In order to reach a server on the Internet, the server needs to have permanent address also know as the static IP address. Many Internet service providers don't provide home users with a static IP address or they charge more providing a static IP address. Instead they provide the home user with an IP address that changes every time the user connects to the Internet. Clients wishing to contact the server will have difficulty reaching the server.
Dynamic DNS service providers assist in working around a problem. First they provide you with a domain name, such as 'myhost.example.org'. Then they associate your IP address, whenever it changes, with this domain name. Then anyone intending to reach the server will be to contact the server using the domain name 'myhost.example.org' which always points to the latest IP address of the server.
For this to work, every time you connect to the Internet, you will have to tell your Dynamic DNS provider what your current IP address is. Hence you need special software on your server to perform this operation. The Dynamic DNS function in FreedomBox will allow users without a static public IP address to push the current public IP address to a Dynamic DNS Server. This allows you to expose services on FreedomBox, such as ownCloud, to the Internet.
GnuDIP vs. Update URL
There are two main mechanism to notify the Dynamic DNS server of your new IP address; using the GnuDIP protocol and using the Update URL mechanism.
If a service provided using update URL is not properly secured using HTTPS, your credentials may be visible to an adversary. Once an adversary gains your credentials, they will be able to replay your request your server and hijack your domain.
On the other hand, the GnuDIP protocol will only transport a salted MD5 value of your password, in a way that is secure against replay attacks.
Using the GnuDIP protocol
Register an account with any Dynamic DNS service provider. A free service provided by the FreedomBox community is available at .
In FreedomBox UI, enable the Dynamic DNS Service.
Select GnuDIP as Service type, enter your Dynamic DNS service provider address (for example, gnudip.datasystems24.net) into GnuDIP Server Address field.
Fill Domain Name, Username, Password information given by your provider into the corresponding fields.
Using an Update URL
This feature is implemented because the most popular Dynamic DNS providers are using Update URLs mechanism.
Register an account with a Dynamic DNS service provider providing their service using Update URL mechanism. Some example providers are listed in the configuration page itself.
In FreedomBox UI, enable the Dynamic DNS service.
Select other Update URL as Service type, enter the update URL given by your provider into Update URL field.
If you browse the update URL with your Internet browser and a warning message about untrusted certificate appears, then enable accept all SSL certificates. WARNING: your credentials may be readable here because man-in-the-middle attacks are possible! Consider choosing a better service provider instead.
If you browse the update URL with your Internet browser and the username/password box appears, enable use HTTP basic authentication checkbox and provide the Username and Password.
If the update URL contains your current IP address, replace the IP address with the string <Ip>.
Checking If It Works
Make sure that external services you have enabled such as /jwchat, /roundcube and /ikiwiki are available on your domain address.
Go to the Status page, make sure that the NAT type is detected correctly. If your FreedomBox is behind a NAT device, this should be detected over there (Text: Behind NAT). If your FreedomBox has a public IP address assigned, the text should be "Direct connection to the Internet".
Check that the last update status is not failed.
Roundcube
What is Roundcube?
RoundCube is a browser-based multilingual email client with an application-like user interface. RoundCube is using the Internet Message Access Protocol (IMAP) to access e-mail on a remote mail server. It supports MIME to send files, and provides particularly address book, folder management, message searching and spell checking.
ownCloud
What is ownCloud?
ownCloud is a self-hosted file sync and share server. It provides access to your data through a platform to view, sync and share across devices. Calendars and Contacts feature will help you keeping google at a nice distance. ownCloud's functionalities are native or available via plugins (Collaborative Editing, Play Music, Watch Movies, Store Passwords, Dashboard, Mozilla Sync...) via
Installation
Clicking on the ownCloud application in Plinth will show an installation prompt. Proceed to install. After the installation, visit the /owncloud link provided in the ownCloud page. First time installation wizard will show up asking for administrator username and password to setup (no additional details such as database configuration are requested). After providing the details, you will be logged. You will be able to start using the ownCloud and create more users.
External Storage
ownCloud's external storage plugin allows you to expose the contents of a hard drive or those of an online storage account as a folder. Following are the steps required to setup such storage.
Mount your hard drive or external storage to any fixed directory on the system.
Install two packages needed via the 'apt-get' on the SSH command line shell (this step will not be needed in future):
$ sudo apt-get install php-google-api-php-client php-dropbox
Goto ownCloud Apps section and enable 'External Storage Support' plugin.
Goto 'Admin' section and add your hard drive mount path in the external storage section. This folder will now show up in your folders list to access and sync across devices.
PageKite
What is PageKite?
PageKite makes local websites and services publicly accessible immediately without creating yourself a public IP address. PageKite provides "Kites" and "Services". Kites aims to make accessible in a second a web page (for instance foo.pagekite.me). Services can expose a file or a folder. Technically speaking, PageKite is free Software solution for tunneling HTTP, HTTPS and SSH servers through firewalls and NAT.
Use PageKite
See PageKite website.
Secure Shell
What is Secure Shell?
FreedomBox runs openssh-server
server by default allowing remote logins from all interfaces. If your hardware device is connected to a monitor and a keyboard, you may login directly as well. Regular operation of FreedomBox does not require you to use the shell. However, some tasks or identifying a problem may require you to login to a shell.
Default User Account
The pre-built FreedomBox images have a default user account called "fbx". However the password is not set for this account, so it will not be possible to log in with this account by default.
There is a script included in the freedom-maker program, that will allow you to set the password for this account, if it is needed. To set a password for the "fbx" user:
1. Decompress the image file.
2. Get a copy of freedom-maker from .
3. Run sudo ./bin/passwd-in-image <image-file> fbx
.
4. Copy the image file to SD card and boot device as normal.
The "fbx" user also has superuser privileges via sudo.
Logging In
To login via SSH, to your FreedomBox:
$ ssh fbx@freedombox
Replace fbx
with the name of the user you wish to login as. freedombox
should be replaced with the hostname or IP address of you FreedomBox device as found in the Quick Start process.
fbx
is the default user present on FreedomBox with superuser privileges. Any other user created using Plinth and belonging to the group admin
will be able to login. The root
account has no password set and will not be able to login. Access will be denied to all other users.
fbx
and users in admin
group will also be able to login on the terminal directly. Other users will be denied access.
If you repeatedly try to login as a user and fail, you will be blocked from logging in for some time. This is due to libpam-abl
package that FreedomBox installs by default. To control this behavior consult libpam-abl
documentation.
Becoming Superuser
After logging in, if you want to become the superuser for performing administrative activities:
$ sudo su
Make a habit of logging in as root only when you need to. If you aren't logged in as root, you can't accidentally break everything.
Changing Password
To change the password of a user managed by Plinth, use the change password page. However, the fbx
default user is not managed by Plinth and its password cannot be changed in the web interface.
To change password on the terminal, log in to your FreedomBox as the user whose password you want to change. Then, run the following command:
$ passwd
This will ask you for your current password before giving you the opportunity to set a new one.
Mumble
What is Mumble?
Mumble is a voice chat software. Primarily intended for use while gaming, it is suitable for simple talking with high audio quality, noise suppression, encrypted communication, public/private-key authentication by default, and "wizards" to configure your microphone for instance. A user can be marked as a "priority speaker" within a channel.
Privoxy
What is Privoxy?
Privoxy is a software for security, privacy, and accurate control over the web. It provides a much more powerful web proxy (anonymity on the web) than what your browser can offer. Privoxy "is a proxy that is primarily focused on privacy enhancement, ad and junk elimination and freeing the user from restrictions placed on his activities" (source: Privoxy FAQ). Learning about networking protocols like HTTP, about HTML, and "Regular Expressions" can help a lot using Privoxy.
Wiki & Blog (Ikiwiki)
What is Ikiwiki?
Ikiwiki converts wiki pages into HTML pages suitable for publishing on a website. It provides particularly blogging, podcasting, calendars and a large selection of plugins.
Creating a wiki or blog
You can create a wiki or blog to be hosted on your FreedomBox through the Wiki & Blog (Ikiwiki) page in Plinth. The first time you visit this page, it will ask to install packages required by Ikiwiki.
After the package install has completed, select the Create tab. You can select the type to be Wiki or Blog. Also type in a name for the wiki or blog, and the username and password for the wiki's/blog's admin account. Then click Update setup and you will see the wiki/blog added to your list. Note that each wiki/blog has its own admin account.
Accessing your wiki or blog
From the Wiki & Blog (Ikiwiki) page, select the Manage tab and you will see a list of your wikis and blogs. Click a name to navigate to that wiki or blog.
From here, if you click Edit or Preferences, you will be taken to a login page. To log in with the admin account that you created before, select the Other tab, enter the username and password, and click Login.
User login through SSO
Besides the wiki/blog admin, other FreedomBox users can be given access to login and edit wikis and blogs. However, they will not have all the same permissions as the wiki admin. They can add or edit pages, but cannot change the wiki's configuration.
To add a wiki user, go to the Users and Groups page in Plinth (under System configuration, the gear icon at the top right corner of the page). Create or modify a user, and add them to the wiki group. (Users in the admin group will also have wiki access.)
To login as a FreedomBox user, go to the wiki/blog's login page and select the Other tab. Then click the "Login with HTTP auth" button. The browser will show a popup dialog where you can enter the username and password of the FreedomBox user.
Unhosted Storage
What is Unhosted?
Unhosted is a way to uncouple web applications from data. No matter where a web application is served from, the data can be stored on an Unhosted storage server of user's choice. Unhosted web apps do not send your user data to their server and are hence known as "serverless", "client-side", or "static" web apps. Either you connect your own server at runtime, or your data stays within the browser. Your FreedomBox can become your Unhosted storage server using a remoteStorage server know as reStore.
This module is currently disabled in FreedomBox as the package required for reStore server is not available in Debian yet. The package is available for testing via
Setup
Your FreedomBox contains a remoteStorage server called reStore, that can serve as your personal storage server for Unhosted web apps. To setup reStore, simply install and enable in FreedomBox web UI. After the setup, create an account by visiting the link provided on the Unhosted app page https://<yourdomain>/restore/
.
User accounts are currently not integrated with Plinth user management, and public sign-up is enabled!
Try Unhosted apps
Once Unhosted is setup on FreedomBox and when FreedomBox is accessible by a domain name (such by using PageKite, Dynamic DNS or Tor Hidden Service), try one of the following Unhosted web apps (more are listed at ):
(a note taking application)
(list your favorite drinks)
(a simple todo list)
To connect the Unhosted app to your FreedomBox's Unhosted storage, click on the remoteStorage icon and type your address <user>@<yourdomain>
, e.g.:
remotestorage.png
If this doesn't work, make sure that
FreedomBox has a domain name using PageKite, Dynamic DNS or Tor Hidden Service.
The reStore server is running.
You have created the account specified in the reStore server.
Your FreedomBox SSL certificate is trusted in your current browser session (important when using private browsing).
Finish the OAuth flow by authenticating with your password and authorizing access, then you should get redirected back to the Unhosted app, and be able to use it. All data of the Unhosted web app is now stored on your FreedomBox.
OpenVPN
What is OpenVPN?
OpenVPN provides to your FreedomBox a virtual private network service. You can use this software for remote access, site-to-site VPNs and Wi-Fi security. OpenVPN includes support for dynamic IP addresses and NAT.
GnuSocial
What is GNU social?
GNU social is a continuation of the StatusNet project. It is social communication software for both public and private communications. It is widely supported and has a large userbase. It is already used by the Free Software Foundation, and Richard Stallman himself. Think of GNU Social as twitter and beyond.
Status of package
GNU Social is still getting packaged for debian and will be available soon for everyone to use. check the progress by tracking the bug #782812.
System
Networks
This section describes how networking is setup by default in FreedomBox and how you can customize it. See also the Firewall section for more information on how firewall works.
Default setup
In a fresh image of FreedomBox, network is not configured at all. When the image is written to an SD card and the device boots, configuration is done. During first boot, FreedomBox setup package detects the networks interfaces and tries to automatically configure them so that FreedomBox is available for further configuration via the web interface from another machine without the need to connect a monitor. Automatic configuration also tries to make FreedomBox useful, out of the box, for the most important scenarios FreedomBox is used for.
There are two scenarios it handles: when is a single ethernet interface and when there are multiple ethernet interfaces.
Single ethernet interface
When there is only single ethernet interface available on the hardware device, there is not much scope for it to play the role of a router. In this case, the device is assumed to be just another machine in the network. Accordingly, the only available interface is configured to be an internal
interface in automatic
configuration mode. This means that it connects to the Internet using the configuration provided by a router in the network and also makes all (internal and external) of its services available to all the clients on this network.
Multiple ethernet interface
When there are multiple ethernet interfaces available on the hardware device, the device can act as a router. The interfaces are then configured to perform this function.
The first network interface is configured to be an WAN or external
interface in automatic configuration mode. This means that it connects to the Internet using network configuration provided by the Internet Service Provider (ISP). Only services that are meant to be provided across the entire Internet (external services) will be exposed on this interface. You must plug your Internet connection into the port of this ethernet interface. If you wish to continue to have your existing router manage the Internet connection for you, then plug a connection from your router to the port on this interface.
The remaining network interfaces are configured for the clients of a router. They are configured as LAN or internal
interfaces in shared
configuration mode. This means that all the services (both external and internal) services are provided to who ever connects on this interface. Further, the shared
mode means that clients will be able to receive details of automatic network connection on this interface. Specifically, DHCP configuration and DNS servers are provided on this interface. The Internet connection available to the device using the first network interface will be shared
with clients using this interface. This all means that you can connect your computers to this network interface and they will get automatically configured and will be able to access the Internet via the FreedomBox.
Currently, it is not very clear which interface will be come the WAN interface (and the remaining being LAN interfaces) although the assignment process is deterministic. So, it take a bit of trail and error to figure out which one is which. In future, for each device, this will be well documented.
Wi-Fi configuration
All Wi-Fi interfaces are configured to be LAN or internal
interfaces in shared
configuration mode. They are also configured to become Wi-Fi access points with following details.
Name of the access point will be FreedomBox
plus the name of the interface (to handle the case where there are multiple of them).
Password for connecting to the interface will be freedombox123
.
Internet Connection Sharing
Although the primary duty of FreedomBox is to provide decentralized services, it can't also act like a home router. Hence, in most cases, FreedomBox connects to the Internet and provides other machines in the network the ability to use that Internet connection. FreedomBox can do this in two ways: using a shared
mode connection or using an internal
connection.
When an interface is set in shared
mode, you may connect your machine directly to it. This is either by plugging in an ethernet cable from this interface to your machine or by connecting to a Wi-Fi access point. This case is the simplest to use, as FreedomBox automatically provides your machine with the necessary network configuration. Your machine will automatically connect to FreedomBox provided network and will be able to connect to the Internet given that FreedomBox can itself connect to the Internet.
Sometimes the above setup may not be possible because the hardware device may have only one network interface or for other reasons. Even in this case, your machine can still connect to the Internet via FreedomBox. For this to work, make sure that the network interface that your machine is connecting to is in internal
mode. Then, connect your machine to network in which FreedomBox is present. After this, in your machine's network configuration, set FreedomBox's IP address as the gateway. FreedomBox will then accept your network traffic from your machine and send it over to the Internet. This works because network interfaces in internal
mode are configured to masquerade
packets from local machines to the Internet and receive packets from Internet and forward them back to local machines.
Customization
The above default configuration may not be fit for your setup. You can customize the configuration to suit your needs from the Networks
area in the 'setup' section of the FreedomBox web interface.
PPPoE connections
If your ISP does not provide automatic network configuration via DHCP and requires you to connection via PPPoE. To configure PPPoE, remove any network connection existing on an interface and add a PPPoE connection. Here, optionally, provide the account username and password given by your ISP and activate the connection.
Connect to Internet via Wi-Fi
By default Wi-Fi devices attached during first boot will be configured as access points. They can be configured as regular Wi-Fi devices instead to connection to a local network or an existing Wi-Fi router. To do this, click on the Wi-Fi connection to edit it. Change the mode to Infrastructure
instead of Access Point
mode and IPv4 Addressing Method
to Automatic (DHCP)
instead of Shared
mode. Then the SSID provided will mean the Wi-Fi network name you wish to connect to and passphrase will be the used to while making the connection.
Adding a new network device
When a new network device is added, network manager will automatically configure it. In most cases this will not work to your liking. Delete the automatic configuration created on the interface and create a new network connection. Select your newly added network interface in the add connection page.
Then set firewall zone to internal
and external
appropriately.
You can configure the interface to connect to a network or provide network configuration to whatever machine connects to it.
Similarly, if it is a Wi-Fi interface, you can configure it to become a Wi-FI access point or to connect to an existing access points in the network.
Manual Network Operation
FreedomBox automatically configures networks by default and provides a simplified interface to customize the configuration to specific needs. In most cases, manual operation is not necessary. The following steps describe how to manually operate network configuration in the event that a user finds FreedomBox interface to insufficient for task at hand or to diagnose a problem that FreedomBox does not identify.
On the command line interface:
To see the list of available network devices:
nmcli device
To see the list of configured connections:
nmcli connection
To see the current status of a connection:
nmcli connection show '<conneciton_name>'
To see the current firewall zone assigned to a network interface:
nmcli connection show '<conneciton_name>' | grep zone
or
firewall-cmd --zone=internal --list=all
firewall-cmd --zone=external --list=all
To create a new network connection:
nmcli con add con-name "<connection_name>" ifname "<interface>" type ethernet
nmcli con modify "<connection_name>" connection.autoconnect TRUE
nmcli con modify "<connection_name>" connection.zone internal
To change the firewall zone for a connection:
nmcli con modify "<connection_name>" connection.zone "<internal|external>"
For more information on how to use nmcli
command, see its man page. Also for a full list of configuration settings and type of connections accepted by Network Manager see:
To see the current status of the firewall and manually operate it, see the Firewall section.
Upgrades
FreedomBox can automatically install security upgrades. On the Upgrades page of the Settings section in Plinth you can turn on automatic upgrades. For FreedomBox versions above 0.5, this feature is enabled by default and there is no manual action necessary. It is strongly recommended that you have this option enabled to keep your FreedomBox secure.
Upgrades are performed every day at night. If you wish to shutdown FreedomBox every day after use, keep it running at night once a week or so to let the automatic upgrades happen. Alternatively, you can perform manual upgrades as described below.
Manual Upgrades
In the Plinth web interface, you can initiate a manual upgrade process from Upgrades page of the Settings section. Note that once the upgrades start, it may take a long time to complete and Plinth may seem to wait for the page to load.
Under some circumstances, automatic upgrades may fail and require you perform a manual upgrade action. Even upgrades initiated from Plinth may not finish properly. This may be because the upgrade process requires you to make a decision. In these cases, manual upgrade on the terminal may be the only option.
In addition, while the upgrade task is running any application installations will wait until the upgrade task is finished. Depending on the hardware, the upgrade task may take a little time, therefore, giving the impression that the application installation stalled.
To perform manual upgrades on the terminal, login into FreedomBox on a terminal or using a remote secure shell (see Secure Shell section). Then run the following commands:
$ sudo su -
Password:
# apt-get update
# apt-get dist-upgrade
This will ask you if it is alright to install/upgrade (or remove) some packages and use (or release) some disk space. Say yes after review. In some cases, during the upgrades process you will be asked questions about modified configuration files, answering with a default Keep current configuration is usually safe.
Firewall
Firewall is a network security system that controls the incoming and outgoing network traffic. Keeping a firewall enabled and properly configured reduces risk of security threat from the Internet.
The operation of the firewall in Plinth web interface of FreedomBox is automatic. When you enable a service it is automatically permitted in the firewall and when you disable a service it is automatically disabled in the firewall. For services which are enabled by default on FreedomBox, firewall ports are also enabled by default during the first run process.
Firewall management in FreedomBox is done using FirewallD.
Interfaces
Each interface is needs to be assigned to one (and only one) zone. Whatever rules are in effect for a zone, those rules start to apply for that interface. For example, if HTTP traffic is allowed in a particular zone, then web requests will be accepted on all the addresses configured for all the interfaces assigned to that zone.
There are primarily two firewall zones used. The internal
zone is meant for services that are provided to all machines on the local network. This may include services such as streaming media and simple file sharing. The external
zone is meant for services that are provided publicly on the Internet. This may include services such as blog, website, email web client etc.
For details on how network interfaces are configured by default, see the Networks section.
Ports/Services
The following table attempts to document the ports, services and their default statuses in FreedomBox. If you find this page outdated, see the Plinth source for lib/freedombox/first-run.d/90_firewall and Firewall status page in Plinth UI.
Service
Port
External
Enabled by default
Status shown in Plinth
Managed by Plinth
SSH
22/tcp
{*}
(./)
(./)
{X}
JWChat
80/tcp
{*}
(./)
(./)
{X}
JWChat
443/tcp
{*}
(./)
(./)
{X}
OwnCloud
80/tcp
{*}
(./)
(./)
(./)
OwnCloud
443/tcp
{*}
(./)
(./)
(./)
Plinth
443/tcp
{*}
(./)
(./)
{X}
Tor (Socks)
9050/tcp
{o}
(./)
{X}
{X}
NTP
123/udp
{o}
(./)
{X}
{X}
DNS
53/tcp
{o}
(./)
{X}
{X}
DNS
53/tdp
{o}
(./)
{X}
{X}
mDNS
5353/udp
{o}
(./)
{X}
{X}
DHCP
67/udp
{o}
(./)
{X}
{X}
Bootp
67/tcp
{o}
{X}
{X}
{X}
Bootp
67/udp
{o}
{X}
{X}
{X}
Bootp
68/tcp
{o}
{X}
{X}
{X}
Bootp
68/udp
{o}
{X}
{X}
{X}
LDAP
389/tcp
{o}
{X}
{X}
{X}
LDAPS
636/tcp
{o}
{X}
{X}
{X}
OpenVPN
1194/udp
{*}
{X}
{X}
{X}
Privoxy
8118/tcp
{o}
(./)
{X}
{X}
XMPP Server
5269/tcp
{*}
(./)
(./)
{X}
XMPP Client
5222/tcp
{*}
(./)
(./)
{X}
XMPP Bosh
5280/tcp
{*}
(./)
(./)
{X}
Obfsproxy
<random>/tcp
{*}
{X}
{X}
{X}
Manual operation
See FirewallD documentation for more information on the basic concepts and comprehensive documentation.
Enable/disable firewall
To disable firewall
service firewalld stop
or with systemd
systemctl stop firewalld
To re-enable firewall
service firewalld start
or with systemd
systemctl start firewalld
Modifying services/ports
You can manually add or remove a service from a zone.
To see list of services enabled:
firewall-cmd --zone=<zone> --list-services
Example:
firewall-cmd --zone=internal --list-services
To see list of ports enabled:
firewall-cmd --zone=<zone> --list-ports
Example:
firewall-cmd --zone=internal --list-ports
To remove a service from a zone:
firewall-cmd --zone=<zone> --remove-service=<service>
firewall-cmd --permanent --zone=<zone> --remove-service=<interface>
Example:
firewall-cmd --zone=internal --remove-service=xmpp-bosh
firewall-cmd --permanent --zone=internal --remove-service=xmpp-bosh
To remove a port from a zone:
firewall-cmd --zone=internal --remove-port=<port>/<protocol>
firewall-cmd --permanent --zone=internal --remove-port=<port>/<protocol>
Example:
firewall-cmd --zone=internal --remove-port=5353/udp
firewall-cmd --permanent --zone=internal --remove-port=5353/udp
To add a service to a zone:
firewall-cmd --zone=<zone> --add-service=<service>
firewall-cmd --permanent --zone=<zone> --add-service=<interface>
Example:
firewall-cmd --zone=internal --add-service=xmpp-bosh
firewall-cmd --permanent --zone=internal --add-service=xmpp-bosh
To add a port to a zone:
firewall-cmd --zone=internal --add-port=<port>/<protocol>
firewall-cmd --permanent --zone=internal --add-port=<port>/<protocol>
Example:
firewall-cmd --zone=internal --add-port=5353/udp
firewall-cmd --permanent --zone=internal --add-port=5353/udp
Modifying the zone of interfaces
You can manually change the assignment of zones of each interfaces after they have been autuomatically assigned by the first boot process.
To see current assignment of interfaces to zones:
firewall-cmd --list-all-zones
To remove an interface from a zone:
firewall-cmd --zone=<zone> --remove-interface=<interface>
firewall-cmd --permanent --zone=<zone> --remove-interface=<interface>
Example:
firewall-cmd --zone=external --remove-interface=eth0
firewall-cmd --permanent --zone=external --remove-interface=eth0
To add an interface to a zone:
firewall-cmd --zone=<zone> --add-interface=<interface>
firewall-cmd --permanent --zone=<zone> --add-interface=<interface>
Example:
firewall-cmd --zone=internal --add-interface=eth0
firewall-cmd --permanent --zone=internal --add-interface=eth0
Hardware
FreedomBox is aimed as a consumer electronics device that is easy to setup, maintain and use. The project does not aim to create a custom hardware device. Instead we plan to support/customize exiting hardware.
In addition to supporting various single board computers and other devices, FreedomBox also supports being installed in a virtual machine. Also, any Debian machine can be turned into a FreedomBox by installing the freedombox-setup
package. See the manual for more details.
Supported Hardware
Recommended Hardware
FreedomBox Danube Edition
FreedomBox - Danube Edition
(based on Cubietruck)
Cubieboard 2
Cubieboard2
BeagleBone Black
BeagleBone Black
A20 OLinuXino Lime2
A20 OLinuXino Lime2
A20 OLinuXino MICRO
A20 OLinuXino MICRO
PC Engines APU
PC Engines APU
Debian
Debian
VirtualBox
VirtualBox
.
.
.
Also Working Hardware
This hardware works but is not recommended due to freedom, performance-per-cost, or other concerns:
DreamPlug
DreamPlug
Raspberry Pi
Raspberry Pi
Raspberry Pi 2
Raspberry Pi 2
Note: As FreedomBox is in development state, Supported Hardware means that FreedomBox images are built for the said hardware and at least one developers has reported the basic functions to be working.
Targeted Hardware
List of Hardware Targeted
Although the project may focus on supporting specific devices, we are looking to support as wider a variety of hardware as possible that is suitable for FreedomBox needs. Take a look at the list of hardware targeted for support.
Adding Hardware Support
If you are a developer, consider adding hardware support for your device by modifying Freedom Maker and FreedomBox Setup.
Cubietruck
FreedomBox Danube Edition
FreedomBox Danube Edition
FreedomBox Danube Edition is a custom casing around Cubietruck coupled with an SSD.
Cubietruck / Cubieboard3
Cubietruck (Cubieboard3) is a single board computer with better performance than many other such boards. FreedomBox images are being built for it. For using this board as FreedomBox, a separate USB WiFi device that does not require non-free firmware is recommended.
Download
FreedomBox SD card images for this hardware are being built. These SD card images are meant for use with the on-board SD card slot and won't work when used with a separate SD card reader connected via USB.
An alternative to downloading these images is to install Debian on Cubietruck and then install FreedomBox on it.
Build Image
FreedomBox images for this hardware can be built using Freedom Maker.
Availability
FreedomBox Danue Edition
A limited number of units are planned to be shipped along with the release of FreedomBox 1.0. If you wish to get one, express your interest.
Cubietruck / Cubieboard3
Price: 89 USD
List of suppliers
Hardware
Open Hardware: No
CPU: Allwinner A20, ARM Cortex-A7 @ 1GHz dual-core
RAM: 2 GiB DDR3 @ 480 MHz
Storage: 8 GB NAND flash built-in, 1x microSD slot
Architecture: armhf
Ethernet: 10/100/1000, RJ45
WiFi: Broadcom BCM4329/BCM40181 (no free WiFi drivers + firmware available)
SATA: 1x 2.0 port
Non-Free Status
Non-free blobs required: ?
WiFi: no free WiFi drivers + firmware available
Works with stock Debian kernel: yes
Known Issues
WiFi does not work with free software. A separate USB WiFi device is recommended.
Beagle Bone Black
Beagle Bone Black
Beagle Bone Black (Revision C.1) is an open hardware single board computer. FreedomBox images are built and tested for it. For using this board as FreedomBox, a USB WiFi device that does not require non-free firmware is recommended.
Download
FreedomBox SD card images for this hardware are available. Follow the instructions on the download page to create a FreedomBox SD card and boot into FreedomBox.
Note: This image is for BeagleBone Black. It won't work on BeagleBone Green.
An alternative to downloading these images is to install Debian on BeagleBone and then install FreedomBox on it.
Build Image
FreedomBox images for this hardware can be built using Freedom Maker.
Availability
Price: ~ 59 USD (50 EUR)
Mouser Electronics
Full list of suppliers
Hardware
Open Hardware: Yes
CPU: AM335x 1GHz ARM Cortex-A8
RAM: 512MB DDR3L 800 Mhz
Storage: Onboard 4GB, 8bit Embedded MMC and microSD
Architecture: armhf
Ethernet: 10/100, RJ45
WiFi: None, use a USB WiFi device
SATA: None
Non-Free Status
Non-free blobs required: No
WiFi: Not available
Works with stock Debian kernel: Yes
A20 OLinuXino Lime2
A20 OLinuXino Lime2
Olimex's A20 OLinuXino Lime2 is fully open source hardware single board computer using the Allwinner A20 Dual Core ARM processor. FreedomBox images are being built for it starting with version 0.7. For using this board as FreedomBox, a separate USB WiFi device that does not require non-free firmware is recommended.
Similar Hardware
The following similar hardware will also work well with FreedomBox.
Olimex's A20 OLinuXino Lime2 4GB. This hardware merely has extra 4GB NAND storage that is not used by FreedomBox.
Download
FreedomBox SD card images for this hardware are being built. These SD card images are meant for use with the on-board SD card slot and won't work when used with a separate SD card reader connected via USB.
An alternative to downloading these images is to install Debian on the device and then install FreedomBox on it.
Build Image
FreedomBox images for this hardware can be built using Freedom Maker once the support for it is added.
Availability
Price: 45 EUR (A20 OLinuXino Lime2)
Price: 55 EUR (A20 OLinuXino Lime2 4GB)
Olimex Store
Hardware
Open Source Hardware (OSHW): Yes
CPU: Allwinner A20, ARM Cortex-A7 @ 1GHz dual-core
RAM: 1 GiB DDR3
Storage: 4 GB NAND flash built-in (only on 4GB model), 1x microSD slot
Architecture: armhf
Ethernet: 10/100/1000, RJ45
WiFi: None, use a USB WiFi device
SATA: 1x port
Non-Free Status
Non-free blobs required: No
WiFi: Not available
Works with stock Debian kernel: Yes
Boot Firmware: BROM (GPLV2+)
Known Issues
WiFi is not available. A separate USB WiFi device is recommended.
A20 OLinuXino MICRO
A20 OLinuXino MICRO
Olimex's A20 OLinuXino MICRO is fully open source hardware single board computer using the Allwinner A20 Dual Core ARM processor. FreedomBox images are being built for it starting with version 0.7. For using this board as FreedomBox, a separate USB WiFi device that does not require non-free firmware is recommended.
Similar Hardware
The following similar hardware will also work well with FreedomBox.
Olimex's A20 OLinuXino MICRO 4GB. This hardware merely has extra 4GB NAND storage that is not used by FreedomBox.
Download
FreedomBox SD card images for this hardware are being built. These SD card images are meant for use with the on-board SD card slot and won't work when used with a separate SD card reader connected via USB.
An alternative to downloading these images is to install Debian on the device and then install FreedomBox on it.
Build Image
FreedomBox images for this hardware can be built using Freedom Maker once the support for it is added.
Availability
Price: 55 EUR (A20 OLinuXino MICRO)
Price: 65 EUR (A20 OLinuXino MICRO 4GB)
Olimex Store
Hardware
Open Source Hardware (OSHW): Yes
CPU: Allwinner A20, ARM Cortex-A7 @ 1GHz dual-core
RAM: 1 GiB DDR3
Storage: 4 GB NAND flash built-in (only on 4GB model), 1x microSD slot
Architecture: armhf
Ethernet: 10/100, RJ45
WiFi: None, use a USB WiFi device
SATA: 1x port
Non-Free Status
Non-free blobs required: No
WiFi: Not available
Works with stock Debian kernel: Yes
Boot Firmware: BROM (GPLV2+)
Known Issues
WiFi is not available. A separate USB WiFi device is recommended.
APU
PC Engines APU 1D
PC Engines APU 1D is a single board computer with 3 Gigabit ethernet ports, a powerful AMD APU and Coreboot firmware. FreedomBox images built for AMD64 machines are tested to work well for it. For using this board as FreedomBox, a USB WiFi device that does not require non-free firmware is recommended.
Similar Hardware
Although untested, the following similar hardware is also likely to work well with FreedomBox.
Using amd64 image:
apu1c
apu1c4
apu1d4
Using i386 image:
alix1d
alix1e
alix2d2
alix2d3
alix2d13
alix3d2
alix3d3
alix6f2
Download
FreedomBox disk images for this hardware are available. Follow the instructions on the download page to create a FreedomBox SD card, USB disk, SSD or hard drive and boot into FreedomBox. Pick the image meant for all amd64 machines.
An alternative to downloading these images is to install Debian on the APU and then install FreedomBox on it.
Networking
The first network port, the left most one in the above picture, is configured by FreedomBox to be an upstream Internet link and the remaining 2 ports are configured for local computers to connect to.
Build Image
FreedomBox images for this hardware, which is for all amd64 machines, can be built using Freedom Maker.
Availability
Price: 110 - 170 USD (depending on the board and supplier)
PC Engines
Full list of suppliers
Hardware
Open Hardware: No
CPU: AMD G series T40E
RAM: 2 GB DDR3-1066 DRAM
Storage: SD card, External USB
Architecture: amd64
Ethernet: 3 Gigabit Ethernet ports
WiFi: None, use a USB WiFi device
SATA: 1 m-SATA and 1 SATA
Non-Free Status
Non-free blobs required: No
WiFi: Not available
Works with stock Debian kernel: Yes
Boot firmware: Coreboot
VirtualBox
VirtualBox
This page will help you get started with using FreedomBox on a virtual machine using VirtualBox. While VirtualBox images are primarily used for testing and development, they can also be used for regular use if you have spare resources on one of your machines. This setup is useful if:
You don't own one of the supported hardware devices.
You don't use Debian GNU/Linux as your operating system.
You don't want to disturb your Debian installation to try out FreedomBox.
Download
FreedomBox SD card images for this VirtualBox are available. Follow the instructions on the download page to download and verify VirtualBox images.
An alternative to downloading these images is to install Debian on VirtualBox and then install FreedomBox on it.
Creating a Virtual Machine
Decompress the downloaded VDI image.
Create a new VirtualBox VM.
When asked for a "Virtual Hard Disk" select the .vdi file you just extracted in step 1.
After a virtual machine is created, go to settings -> [Network] -> [Interface] and set on the following options.
Network Configuration
VirtualBox provides many types of networking options. Each has its advantages and disadvantages. For more information about how various networking types work in VirtualBox, see VirtualBox's networking documentation.
For a simple setup, it is recommended that you use a single network interface in your guest machine. This will make the first boot script automatically configure that interface as an internal
network with automatic
network configuration. Inside the guest machine, the networking is configured automatically and all the services are made available on this network interface. For more information on how networks are configured by default in FreedomBox, see Networks section.
What remains is to make those services available to the host machine or to other machines in the network. You must then choose one of the following types of networking for the network interface on your guest machine. To set a particular type of network for the guest's network adapter, go to the guest VM's settings then the network options and then select the adapter you wish to configure. There, set the network type from the available list of networks.
First and the recommended option is to use the Bridge type of network. This option exposes the guest machine to the same network that host network is connected to. The guest obtains network configuration information from a router or DHCP server on the network. The guest will appear as just another machine in the network. A major advantage of this of setup is that the host and all other machines in the network will be able to access the services provided by guest without requiring any further setup. The only drawback of this approach is that if the host is not connected to any network, the guest's network will remain unconfigured making it inaccessible even from the host.
Second method is Host only type of networking. With a guest's network interface configured in this manner, it will only be accessible from the host machine. The guest will not able access any other machine but the host, so you do not have internet access on the guest. All services on the guest are available to the host machine without any configuration such as port forwarding.
The third option is to use the NAT type of network. This the networking type that VirtualBox assigns to a freshly created virtual machine. This option works even when host is not connected to any network. The guest is automatically configured and is able to access the Internet and local networks that host is able to connect to. However, the services provided by the guest require port forwarding configuration setup to be available outside.
To configure this go to VM settings -> [Network] -> [Adapter] -> [Port Forwarding]. Map a port such as 2222 from host to guest port 22 and you will be able to ssh into FreedomBox from host machine as follows:
ssh -p 2222 fbx@localhost
Map 4443 on host to 443 on the guest. This make FreedomBox HTTPS service available on host using the URL You will need to add a mapping for each such services from host to guest.
The final option is to create two network interfaces, one host only and one NAT type. This way you can access the guest without any additional configuration, and you have internet access on the guest. The guest will be invisible to any other machines on the network.
Summary of various network types:
-
Guest accessible from other machines
Guest accessible from host
Works without port forwarding
Works without host connected to network
Guest has internet access
Bridged
(./)
(./)
(./)
{X}
(./)
Host only
{X}
(./)
(./)
(./)
{X}
NAT
(./)
(./)
{X}
(./)
(./)
NAT and Host
{X}
(./)
(./)
(./)
(./)
Using
You can log in as the user created during Plinth setup.
After logging in, you can become root with the command sudo su
. See the FreedomBox usage page for more details.
Build Image
If you wish to build your own images instead of downloading available images, it can be done using Freedom Maker.
Tips & Troubleshooting
Finding out the IP address of the virtual machine
This depends on the network configuration you chose. With a bridged adapter, your virtual machine gets its IP address from the DHCP server of your network, most likely of your Router. You can try the first couple of IP addresses or check your router web interface for a list of connected devices.
If you chose host-only adapter, the IP address is assigned by the DHCP server of your VirtualBox network. In the VirtualBox Manager, go to File -> Preferences -> Network -> Host-only Networks. You can see and edit the DHCP address range there, typically you get assigned addresses close to the Lower Address Bound.
Another possibility of finding the IP address is to login via the Virtualbox Manager (or similar software). The FreedomBox images do not have any default user accounts, so you need to set an initial user and password using the passwd-in-image script.
Networking Problems with macchanger
The package macchanger
can cause network problems with VirtualBox. If you have a valid IP address on your guest's host network adapter (like 192.168.56.101) but are not able to ping or access the host (like 192.168.56.1), try uninstalling macchanger
:
$ dpkg --ignore-depends=freedombox-setup --remove macchanger
You might have to manually remove the script /etc/network/if-prep-up/macchanger
. If Debian complains about unmet dependencies when you use a package manager (apt-get, aptitude, dpkg), try to remove 'macchanger' from the dependencies of 'freedombox-setup' in the file /var/lib/dpkg/status
.
Mounting Images Locally
If you want to mount images locally, use the following to copy built images off the VirtualBox:
$ mkdir /tmp/vbox-img1 /tmp/vbox-root1
$ vdfuse -f freedombox-unstable_2013.0519_virtualbox-i386-hdd.vdi /tmp/vbox-img1/
$ sudo mount -o loop /tmp/vbox-img1/Partition1 /tmp/vbox-root1
$ cp /tmp/vbox-root1/home/fbx/freedom-maker/build/freedom*vdi ~/
$ sudo umount /tmp/vbox-root1
# $ sudo umount /tmp/vbox-img1 # corruption here.
Fixing the time after suspend and resume
The virtual machine loses the correct time/date after suspending and resuming. One way to fix this is to create a cron-job that restarts the time service ntp
. You can add a crontab entry as root to restart ntp every 15 minutes by typing 'crontab -e'
and adding this line:
*/15 * * * * /etc/init.d/ntp restart
Do not restart this service too often as this increases the load of publicly and freely available NTP servers.
Debian
FreedomBox is a pure blend of Debian. This means that all the work on FreedomBox is available in Debian as packages. It also means that any machine running Debian can be turned into a FreedomBox.
This page describes the process of installing FreedomBox on a Debian system. Currently, FreedomBox works in Debian Testing (Stretch) and Unstable (Sid).
Use a fresh Debian installation
Installing FreedomBox changes your Debian system in many important ways. This includes installing a firewall and regenerating server certificates. It is hence recommended that you install FreedomBox on a fresh Debian installation instead of an existing setup.
use "fbx" as the login name
If you choose to create an initial user account, use "fbx" as the login name. (Once the FreedomBox setup program completes, all user accounts except for the "fbx" account will be locked out via pam_access. This also affects sudo access.)
Installing on Debian
Check the Troubleshooting section below, for any tips or work-arounds that might help during the install.
Install Debian Testing (Stretch) or Unstable (Sid) on your hardware.
Update your package list.
$ sudo apt-get update
Install freedombox-setup
package.
$ sudo apt-get install freedombox-setup
When asked to specify whether Macchanger should be set up to run automatically, please choose "No".
Run FreedomBox setup program. This installs further packages and sets up basic configuration.
$ sudo /usr/lib/freedombox/setup | tee freedombox-setup.log
You may have to clear your existing network configuration. See Troubleshooting note #2 below.
Reboot the system. This is necessary to trigger the first-run script.
$ sudo reboot
After the system boots up, wait for it to reboot again. The first-run scripts sets up a few things and initiates a reboot.
After the second reboot you can start using FreedomBox.
Troubleshooting
There is a bug in policykit-1 package that causes errors and hangs during installation of freedombox-setup package. A workaround is to first install policykit-1 package and then reboot. After that, follow the above procedure setup procedure.
$ sudo apt-get update
$ sudo apt-get install policykit-1
$ sudo reboot
Freedombox does not support network device configuration via /etc/network/interfaces
, and it will not manage any non-loopback interfaces mentioned there. (See bug #797614.) Future versions of freedombox-setup will clear this file automatically; for now, edit it manually and ensure that it contains only the following:
auto lo
iface lo inet loopback
If you have already completed the setup process without doing this step, you will need to clear out the /etc/network/interfaces
file keeping only the above lines. Then perform a reboot. After this network connections configured by the setup
step above will configure your network. Network interfaces will then be in the internal
or external
firwall zone. This is essential for the FreedomBox's web interface to be reachable from other machines in the network. You can tweak network manager connections with the nmtui
command if you wish.
DreamPlug
DreamPlug
DreamPlug is the hardware for which FreedomBox has been originally targeted. FreedomBox images are built and tested for it. For using this device as FreedomBox, a USB WiFi device that does not require non-free firmware is recommended.
You can find more support and discussion for DreamPlug on the official forum.
Download
FreedomBox SD card images for this hardware are available. Follow the instructions on the download page to create a FreedomBox SD card and boot into FreedomBox. See also instructions for using an internal micro-SD with DreamPlug.
An alternative to downloading these images is to install Debian on DreamPlug and then install FreedomBox on it.
Firmware
Note that the factory firmware configurations may vary between revisions of the hardware, and render some images incompatible. See the DreamPlug firmware page for information on what images are compatible and how to update your DreamPlug firmware.
Build Image
FreedomBox images for this hardware can be built using Freedom Maker.
Testing
Instructions on how to test this hardware are available.
Availability
Price: 159 USD
DreamPlug manufacturer
Reseller Spinifex in Australia
Hardware
Open Hardware: No
CPU: Marvell Kirkwood 88F6281 @ 1.2GHz
RAM: 512MB 16bit DDR2-800 MHz
Storage: 4 GB on board micro-SD
Architecture: armel
Ethernet: 2x 10/100/1000, RJ45
WiFi: SD8787, 802.11 b/g/n
SATA: eSATA 2.0 port
Non-Free Status
Non-free blobs required: built-in WiFi
WiFi: no free WiFi drivers + firmware available
Works with stock Debian kernel: yes
Known Issues
WiFi does not work with free software. A separate USB WiFi device is recommended.
Raspberry Pi Model B+
Raspberry Pi (Model B+)
Raspberry Pi (Model B+) is a popular single board computer developed with the intention of promoting teaching of basic computer science in schools. FreedomBox images are built and tested for it. For using this board as FreedomBox, a USB WiFi device that does not require non-free firmware is recommended.
Note: The Debian architecture used for this device is armel
. This means floating point computations are done in software and most operations are slower than what Raspberry Pi is capable of.
Download
FreedomBox SD card images for this hardware are available. Follow the instructions on the download page to create a FreedomBox SD card and boot into FreedomBox.
Build Image
FreedomBox images for this hardware can be built using Freedom Maker.
Availability
Price: 35 USD
List of official distributors
Hardware
Open Hardware: No
CPU: ARM1176JZF-S (ARMv6k) 700 MHz
RAM: 512 MB
Storage: MicroSD card slot
Architecture: armel
Ethernet: 10/100, RJ45
WiFi: None, use a USB WiFi device
SATA: None
Non-Free Status
Non-free blobs required: boot firmware
WiFi: Not available
Works with stock Debian kernel: No
Known Issues
The Debian architecture used for this device is armel
. This means floating point computations are done in software and generally most operations are slower than what Raspberry Pi is capable of.
Raspberry Pi 2 Model B
Raspberry Pi 2
Raspberry Pi 2 (Model B ) is a popular single board computer developed with the intention of promoting teaching of basic computer science in schools. It is a successor to Raspberry Pi Model B+ with much faster processor and more RAM. FreedomBox images are built and tested for it. For using this board as FreedomBox, a USB WiFi device that does not require non-free firmware is recommended.
Note: For FreedomBox release 0.5, the Debian architecture used for this device is armel
. This means floating point computations are done in software and most operations are slower than what Raspberry Pi 2 is capable of. Starting with FreedomBox release 0.6 separate armhf
images with full hardware floating point support will be available.
Download
FreedomBox SD card images for this hardware are available. Follow the instructions on the download page to create a FreedomBox SD card and boot into FreedomBox.
Build Image
FreedomBox images for this hardware can be built using Freedom Maker.
Availability
Price: 35 USD
List of official distributors
Hardware
Open Hardware: No
CPU: 900 MHz quad-core ARM Cortex-A7
RAM: 1 GB
Storage: MicroSD card slot
Architecture: armhf
Ethernet: 10/100, RJ45
WiFi: None, use a USB WiFi device
SATA: None
Non-Free Status
Non-free blobs required: boot firmware
WiFi: Not available
Works with stock Debian kernel: No
Known Issues
The Debian architecture used for this device is armel
. This means floating point computations are done in software and generally most operations are slower than what Raspberry Pi 2 is capable of. However, starting with FreedomBox 0.6 separate images for Raspberry Pi 2 with armhf
architecture will be built.
USB Wi-Fi
FreedomBox works on many single board computers. However, many of these boards do not have built-in Wi-Fi capabilities. Even when Wi-Fi capability is available, non-free proprietary firmware is required to make them work.
A solution to the problem is to plug-in a USB Wi-Fi device into one of the available USB ports. There are many such devices available which do not require non-free firmware to work. The following is a list of such devices that work with FreedomBox devices. Some devices based on these chips have tested to work well with FreedomBox including functions such as access point mode.
Devices with Atheros AR7010 chip
Devices with Atheros AR9271 chip
Firmware Installation
The free firmware for these devices is not packaged in Debian yet. You can manually download and install the firmware as follows:
sudo su [enter password]
cd /lib/firmware
wget https://www.thinkpenguin.com/files/ath9k-htc/version-1.4-beta/htc_9271.fw
wget https://www.thinkpenguin.com/files/ath9k_firmware_free-version/htc_7010.fw
Resources
Debian Wiki on WiFi drivers
Wikipedia: Comparison of open-source Linux wireless network drivers
WikiDevi: database of computer hardware
Advanced Topics
Adding Additional Features
There are a number of incomplete projects that you might find useful, for setting up a wiki, an IM server, and so forth. To check these out, download the repository:
$ hg clone https://bitbucket.org/nickdaly/plugserver ~/plugserver
Then, read the README. It's pretty detailed. Also, if you can, it may be best to wait until these tools are fully integrated into the FreedomBox image. Otherwise, migrating from these custom tools to the officially supported FreedomBox tools may be difficult. Ultimately, that decision is up to you.
Contributing
From code, design and translation to spreading the world and donation, here is a list of possible contributions to develop FreedomBox.
Quick Links
Progess calls
TODO page
Donation page
Welcome to newcomers
As a newcomer, you are more than welcome to introduce yourself to all users and doers on the "FreedomBox-discuss" mailing list or on the #freedombox IRC channel. In addition to make useful contacts, you can start reporting bugs and translate (see below) the wiki website and the FreedomBox web interface.
Development priorities
Upcoming priorities have been discussed end of October 2015 by several core developers and the Freedombox Foundation. You'll find on the mailing list archives a Medium Term Roadmap for 2015 and 2016. We want to enjoy soon a version 1.0. We are targeting mid January for a 0.8 release. The main focus of the 0.8 release is going to be integrated in the PGP based SSL Client authentication work. We are planning on a 0.9 polish release for late February with general usability improvements.
Please check next progess calls to keep yourself on track and meet members of the release team. A TODO page aggregates the complete list of the items to work on for FreedomBox.
Contributions needed
Add an Application
If you are a developer and wish to see an application available in FreedomBox, you can contribute by adding the application to FreedomBox. See the FreedomBox Developer Manual.
Bugs
List of bugs listed on Debian universal system.
Code
If you are a developer, you can contribute code to one of the sub-projects of FreedomBox. Step-by-step process of contributing code to FreedomBox is available.
FreedomBox Setup: a Debian package for setting up the FreedomBox.
Plinth: a web interface to administer the functions of FreedomBox.
Freedom Maker: a script to build FreedomBox disk images for use on various hardware devices or virtual machines.
You can pickup a task from one of the TODO lists. The individual page project pages contain information availabily of the code, how to build and TODO lists.
Design
User Experience Design
If you are a user experience designer, you can help FreedomBox with the following items:
UI experience for the Plinth web interface
Web design for freedomboxfoundation.org and FreedomBox wiki pages
Logo and branding (we currently have an identity manual and logos)
Possible designs for custom FreedomBox cases on single board computers
User experience design
Technical Design
FreedomBox is still under development any many components are yet to be worked on. You can contribute to the discussion on various technical design and implementation aspects of FreedomBox. See:
Design portal
Donate
The FreedomBox Foundation is a Delaware non-profit corporation in the process of applying for 501(c)(3) federal nonprofit recognition from the IRS. FreedomBox project is run by volunteers. You can help the project financially by donating via PayPal, Bitcoin or by mailing a check. Please see the donation page for details on how to donate.
Document: User Manual, Website and Wiki
FreedomBox needs better documentation for users and contributors. FreedomBox manual is prepared by aggregating various pages on the wiki and exporting to various formats. The manual is then used in Plinth and elsewhere.
If you wish to contribute to the FreedomBox wiki (and consequently the FreedomBox manual), you can create a wiki account and start editing.
For contributing to the website please start a discussion on the FreedomBox mailing list.
Quality Assurance
FreedomBox already runs on many platforms and it is not possible for developers to test all possible platforms. If you have one of the supported hardware you can help with testing FreedomBox on the platform.
When an application is made available on FreedomBox, not all of its functionality is tested in the real world by developer doing the work. Deploying the application and testing it will help ensure high quality applications in FreedomBox.
See the quality assurance page for a basic list of test cases to check for and information on reporting bugs.
Localization
All text visible to users of FreedomBox needs to be localized to various languages. This translation work includes:
Plinth web interface for FreedomBox
FreedomBox documentation
FreedomBox website and wiki
Individual applications that FreedomBox exposes to users such as ownCloud, JWChat etc.
Some of the translation work are implemented in user interface (Plinth) since the 0.7 release. Documents for user interface translation are currently available on Transifex localization platform and GitHub. If you wish to see FreedomBox available for one of your languages, please start a discussion on the FreedomBox discuss mailing list or on the #freedombox IRC channel to avoid double translations.
For more information, please visit the FreedomBox translation landing page.
Spread the Word
Speak to your family, friends, local community or at global conferences about the importance of FreedomBox. To be a successful project we need many more participants, be it users or contributors. Write about your efforts at the talks page and on the wiki.
Developer Guide
This manual is meant for developers intending to develop applications for FreedomBox. It provides a step by step tutorial and an API reference.
Writing Applications - Tutorial
This tutorial covers writing an application for FreedomBox. FreedomBox is a pure blend of Debian with a web interface, known as Plinth, that configures its applications. We shall discuss various aspects of building an application for FreedomBox, by creating an example application.
There are two parts to writing a FreedomBox application. First is to make sure that the application is available as a Debian package uploaded to the repositories. This is the majority of the work involved. However, if an application is already available in Debian repositories, it is trivial to build a FreedomBox UI for it. The second part of writing an application for FreedomBox is to provide a thin web interface layer for configuring the application. This is done by extending Plinth's user interface to provide visibility to the application and to let the user control its operations in a highly simplified way. This layer is referred to as 'Plinth application'.
Plinth applications can either be distributed as part of Plinth source code by submitting the applications to the Plinth project or they can distributed independently. This tutorial covers writing an application that is meant to be distributed as part of Plinth. However, writing independent Plinth applications is also very similar and most of this tutorial is applicable.
Note
The term application, in this tutorial, is used to mean multiple concepts. FreedomBox application is a combination of Debian package and a web interface layer. The web interface layer is also called a Plinth application which is very similar to and built upon a Django application.
Before we begin
Plinth is a web interface built using Python3 and Django. FreedomBox applications are simply Django applications within the Plinth project. Hence, for the most part, writing a FreedomBox application is all about writing a Django application.
You should start by reading the Django tutorial. All the concepts described there are applicable for how plinth and its applications are be built.
Picking an application
We must first, of course, pick an application to add to FreedomBox. For the purpose of this tutorial, let us pick Tiny Tiny RSS. The project description reads as, Tiny Tiny RSS is an open source web-based news feed (RSS/Atom) reader and aggregator, designed to allow you to read news from any location, while feeling as close to a real desktop application as possible.
Choosing an application
When choosing an application we must make sure that the application respects users' freedom and privacy. By choosing to use FreedomBox, users have explicitly made a choice to keep the data with themselves, to not provide privacy compromising data to centralized entities and to use Free Software that respects their Software Freedom. These are not properties of some of the applications in FreedomBox but all applications must adhere to these principles. Applications should not even ask the users questions to this effect, because users have already made a choice.
Packaging the application
Majority of the effort in creating an application for FreedomBox is to package it for Debian and get it uploaded to Debian repositories. Going through the process of packaging itself is outside the scope of this tutorial. It is, however, well documented elsewhere. You should start here.
Debian packaging might seem like an unnecessary process that takes time with its adherence to standards, review process, legal checks, etc. However, upon close examination, one will find that without these steps the goals of the FreedomBox project cannot be met. Some of the advantages of Debian packaging are listed below:
Legal check ensures that proprietary licensed code or code with bad licenses does not inadvertently creep in.
Libraries have to be packaged separately easing security handling. When a security vulnerability is identified in a library, just the library will have to be updated and not all the applications that depend on it.
Upgrades become smoother. The dependency handling of the packaging system, configuration handling tools, tools to deal with various types of well known files help with ensuring a proper upgrade.
Collaborative maintenance teams ensure that the package is well cared for even if you get busy with other work and can't spend time on your package. Following standards and using common infrastructure is critical to enable this development methodology.
Creating the project structure
Create a directory structure as follows with empty files. We will fill them up in a step-by-step manner.
+- <plinth_root>/
|
+- plinth/
| |
| +- modules/
| |
| +- ttrss/
| |
| +- __init__.py
| |
| +- forms.py
| |
| +- urls.py
| |
| +- views.py
| |
| +- templates/
| | |
| | +- ttrss.html
| |
| +- tests
| |
| +- __init__.py
|
+- actions/
| |
| +- ttrss
|
+- data/
|
+- etc/
|
+- plinth/
|
+- modules-enabled/
|
+- ttrss
The __init__.py
indicates that the directory in which it is present is a Python module. For now, it is an empty file.
Plinth's setup script setup.py
will automatically install the plinth/modules/ttrss
directory (along with other files described later) to an appropriate location. If you are creating an application that stays independent and outside of Plinth source tree, then your setup.py
script will need to install it a proper location on the system. The plinth/modules/
directory is a Python3 namespace package. So, you can install it with the plinth/modules/
directory structure into any Python path and still be discovered as plinth.modules.*
.
Tell Plinth that we exist
The first thing to do is tell Plinth that our application exists. This is done by writing a small file with the Python import path to our application and placing it in data/etc/plinth/modules-enabled/
. Let us create this file ttrss
:
plinth.modules.ttrss
This file is automatically installed to /etc/plinth/modules-enabled/
by Plinth's installation script setup.py
. If we are writing a module that resides independently outside the Plinth's source code, the setup script will need to copy it to the target location. Further, it is not necessary for the application to be part of the plinth.modules
namespace. It can, for example, be plinth_ttrss
.
Writing the URLs
For a user to visit our application in Plinth, we need to provide a URL. When the user visits this URL, a view is executed and a page is displayed. In urls.py
write the following:
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^apps/ttrss/$', views.index, name='index'),
]
This routes the /apps/ttrss/
URL to a view called index
defined in plinth/modules/ttrss/views.py
. This is no different than how routing URLs are written in Django. See Django URL dispatcher for more information.
Adding a menu item
We have added a URL to be handled by our application but this does not yet show up to be a link in Plinth web interface. Let us add a link in the applications list. In __init__.py
add the following:
from plinth import cfg
def init():
"""Intialize the module."""
menu = cfg.main_menu.get('apps:index')
menu.add_urlname('News Feed Reader (Tiny Tiny RSS)', 'glyphicon-bullhorn',
'ttrss:index', 850)
As soon as Plinth starts, it will load all the enabled modules into memory. After this, it gives a chance to each of the modules to initialize itself by calling the init()
method if there is such a method available as <app>.init()
. Here we have implemented this method and added our menu item to the applications menu as part of the initialization process.
We wish to add our menu item to the list of applications which is why we have retrieved the applications menu which is available under the main menu. After this we add our own menu item to this menu. There are several parameters during this process that are important:
In the first parameter we are providing the display name to use for our application when showing the menu item.
In the second parameter we are providing the icon to show for this menu item. This is an icon from the Twitter Bootstrap library. See
the Twitter Bootstrap library documentation for a list of available icons. We can pick an icon from the available list of icons and just mention its glyphicon class as name here.
The third parameter is the name of the URL we have created for our application. Note that when including this application's URLs, Plinth will automatically set the name of the module as the Django
URL namespace. Hence it is ttrss:index
and not just index
.
The final parameter specifies where in the menu this application shows up. This is weightage number with which Plinth sorts the menu items. Higher the weightage, the lower the menu item appears (as it sinks). Since Plinth menu items are alphabetically sorted, for our
application we wish for it to appear between Public Visibility and Voice Chat. Their weights are 800 and 900 respectively. So we selected 850.
We have used the application menu item to insert our own menu item as a child. To be able to use the application menu item, we need to make sure that the module providing the application menu is loaded before our application is loaded. We will do that in the next step.
Specifying module dependencies
Specifying a simple list of applications to be loaded before our application provided to Plinth is sufficient. Add this in __init__.py
.
depends = ['plinth.modules.apps']
Plinth will now make sure that the apps
module is loaded before our module is loaded. Application initialization is also ensured to happen in this order. We can safely use any features of this module knowing that they have been initialized.
Circular dependencies
Circular dependencies are not possible among Plinth applications. Attempting to add them will result in error during startup.
Writing the enable/disable form
We wish to provide a user interface to the user to enable and disable the application. Complex modules may require more options but this is sufficient for our application. Add the following forms.py
.
from django import forms
class TtrssForm(forms.Form):
"""Tiny Tiny RSS configuration form."""
enabled = forms.BooleanField(
label='Enable Tiny Tiny RSS',
required=False)
This creates a Django form that shows a single option to enable/disable the application. It also shows its current state. This is how a regular Django form is built. See Django Forms documentation for more information.
Too many options
Resist the temptation to create a lot of configuration options. Although this will put more control in the hands of the users, it will make FreedomBox less usable. FreedomBox is a consumer product. Our target users are not technically savvy and we have make most of the decisions on behalf of the user to make the interface as simple and easy to use as possible.
Writing a view
In views.py
, let us add a view that can handle the URL we have provided above.
from .forms import TtrssForm
def index(request):
"""Serve configuration page."""
status = get_status()
form = None
if request.method == 'POST':
form = TtrssForm(request.POST, prefix='ttrss')
if form.is_valid():
_apply_changes(request, status, form.cleaned_data)
status = get_status()
form = TtrssForm(initial=status, prefix='ttrss')
else:
form = TtrssForm(initial=status, prefix='ttrss')
return TemplateResponse(request, 'ttrss.html',
{'title': 'News Feed Reader (Tiny Tiny RSS)',
'status': status,
'form': form})
This view works with the form we created in the previous step. It shows the current status of the service in form. This status is retrieved with the help of get_status()
helper method. When the form is posted, again this view is called and it verifies whether the form's input values are correct. If so, it will apply the actions necessary for changed form values using the _apply_changes()
method.
Getting the current status of the application
The view in the previous setup requires the status of the application to be retrieved using the get_status()
method. Let us implement that method in views.py
.
from plinth.modules import ttrss
def get_status():
"""Get the current status."""
return {'enabled': ttrss.is_enabled()}
This method retrieves the various statuses of the application for display in the view. Currently, we only need to show whether the application is enabled or disabled. So, we retrieve that using a helper method defined in __init__.py
.
from plinth import action_utils
def is_enabled():
"""Return whether the module is enabled."""
return action_utils.webserver_is_enabled('50-tt-rss')
This method uses one of the several action utilities provided by Plinth. This method checks whether a webserver configuration named 50-tt-rss
is enabled.
Displaying the application page
The view that we have written above requires a template file known as ttrss.html
to work. This template file controls how the web page for our application is displayed. Let us create this template file in templates/ttrss.html
.
{% extends "base.html" %}
{% load bootstrap %}
{% block content %}
<h2>News Feed Reader (Tiny Tiny RSS)</h2>
<p>Tiny Tiny RSS is a news feed (RSS/Atom) reader and aggregator,
designed to allow you to read news from any location, while feeling
as close to a real desktop application as possible.</p>
<h3>Configuration</h3>
<form class="form" method="post">
{% csrf_token %}
{{ form|bootstrap }}
<input type="submit" class="btn btn-primary" value="Update setup"/>
</form>
{% endblock %}
This template extends an existing template known as base.html
. This template is available in Plinth core to provide all the basic layout, styling, menus, JavaScript and CSS libraries. We will override the content area of the base template and keep the rest.
Yet again, there is nothing special about the way this template is written. This is a regular Django template. See Django Template documentation.
For styling and UI components, Plinth uses the Twitter Bootstrap project. See Bootstrap documentation for reference.
Applying the changes from the form
The view we have created displays the form and processes the form after the user submits it. It used a helper method called _apply_changes()
to actually get the work done. Let us implement that method in views.py
.
from django.contrib import messages
from plinth import actions
def _apply_changes(request, old_status, new_status):
"""Apply the changes."""
modified = False
if old_status['enabled'] != new_status['enabled']:
sub_command = 'enable' if new_status['enabled'] else 'disable'
actions.superuser_run('ttrss', [sub_command])
modified = True
if modified:
messages.success(request, 'Configuration updated')
else:
messages.info(request, 'Setting unchanged')
We check to make sure that we don't try to disable the application when it is already disabled or try to enable the application when it is already enabled. Although Plinth's operations are idempotent, meaning that running them twice will not be problematic, we still wish avoid unnecessary operations for the sake of speed.
We are actually perform the operation using Plinth actions. We will implement the action to be performed a bit later.
After we perform the operation, we will show a message on the response page showing that the action was successful or that nothing happened. We use the Django messaging framework to accomplish this. See Django messaging framework for more information.
Installing packages required for the application
Plinth takes care of installing all the Debian packages required for our application to work. All we need to do is specify the list of the Debian packages required using a decorator on our view as follows:
from plinth import package
@package.required(['tt-rss'])
def index(request):
"""Serve configuration page."""
...
The first time this application's view is accessed, Plinth shows a package installation page and allows the user to install the required packages. After the package installation is completed, the user is shown the application's configuration page.
If there are configuration tasks to be performed immediately before or after the package installation, Plinth provides hooks for it. The before_install=
and on_install=
parameters to the @package.required
decorator take a callback methods that are called before installation of packages and after installation of packages respectively. See the reference section of this manual or the plinth.package
module for details. Other modules in Plinth that use this feature provided example usage.
Writing actions
The actual work of performing the configuration change is carried out by a Plinth action. Actions are independent scripts that run with higher privileges required to perform a task. They are placed in a separate directory and invoked as scripts via sudo. For our application we need to write an action that can enable and disable the web configuration. We will do this by creating a file actions/ttrss
.
import argparse
from plinth import action_utils
def parse_arguments():
"""Return parsed command line arguments as dictionary."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
subparsers.add_parser('enable', help='Enable Tiny Tiny RSS')
subparsers.add_parser('disable', help='Disable Tiny Tiny RSS')
return parser.parse_args()
def subcommand_enable(_):
"""Enable web configuration and reload."""
action_utils.webserver_enable('50-tt-rss')
def subcommand_disable(_):
"""Disable web configuration and reload."""
action_utils.webserver_disable('50-tt-rss')
def main():
"""Parse arguments and perform all duties."""
arguments = parse_arguments()
subcommand = arguments.subcommand.replace('-', '_')
subcommand_method = globals()['subcommand_' + subcommand]
subcommand_method(arguments)
if __name__ == '__main__':
main()
This is a simple Python3 program that parses command line arguments. While Python3 is preferred, it can be written in other languages also. It uses a helper utility provided by Plinth to actually enable and disable Apache2 web server configuration.
This script is automatically installed to /usr/share/plinth/actions
by Plinth's installation script setup.py
. Only from here will there is a possibility of running the script under sudo
. If you are writing an application that resides indenpendently of Plinth's source code, your setup.py
script will need to take care of copying the file to the target location.
Creating diagnostics
Plinth provides a simple API for showing diagnostics results. The application has to implement a method for actually running the diagnostics and return the results as a list. Plinth then takes care of calling the diagnostics method and displaying the list in a formatted manner.
To implement the diagnostics method, method called diagnose()
has to be available as <app>.diagnose()
. It must return a list in which each item is the result of a test performed. The item itself is a two-tuple containing the display name of the test followed by the result as passed
, failed
or error
.
def diagnose():
"""Run diagnostics and return the results."""
results = []
results.extend(action_utils.diagnose_url_on_all(
'https://{host}/ttrss', extra_options=['--no-check-certificate']))
return results
There are several helpers available to implement some of the common diagnostic tests. For our application we wish to implement a test to check whether the /ttrss
URL is accessible. Since this is a commonly performed test, there is a helper method available and we have used it in the above code. The {host}
tag replaced with various IP addresses, hostnames and domain names by the helper to produce different kinds of URLs and they are all tested. Results for all tests are returned which we then pass on to Plinth.
The user can trigger the diagnostics test by going to System -> Diagnostics
page. This runs diagnostics for all the applications. If we want users to be able to run diagnostics specifically for this application, we can include a button for it in our template immediately after the application description.
{% include "diagnostics_button.html" with module="ttrss" %}
Logging
Sometimes we may feel the need to write some debug messages to the console and Plinth log file. Doing this in Plinth is just like doing this any other Python application.
import logging
logger = logging.getLogger(__name__)
def example_method():
logger.debug('A debug level message')
logger.info('Showing application page - %s', request.method)
try:
something()
except Exception as exception:
# Print stack trace
logger.exception('Encountered an exception - %s', exception)
For more information see Python logging framework documentation.
Adding a License
Plinth is licensed under the GNU Affero General Public License Version 3 or later. FreedomBox UI applications, which run as modules under Plinth, also need to be under the same license or under a compatible license. The license of our application needs to clear for our application to be accepted by users and other developers. Let us add license headers to our application.
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
The above header needs to be present in every file of the application. It is suitable for Python files. However, in template files, we need to modify it slightly.
{% extends "base.html" %}
{% comment %}
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
{% endcomment %}
...
Internationalization
Every string message that is visible to the user must be localized to user's native language. For this to happen, our application needs to be internationalized. This requires marking the user visible messages for translation. Plinth applications use the Django's localization methods to make that happen.
from django.utils.translation import ugettext as _
def index(request):
...
return TemplateResponse(request, 'ttrss.html',
{'title': _('News Feed Reader (Tiny Tiny RSS)'),
'status': status,
'form': form})
Notice that the page's title is wrapped in the _()
method call. Let us do that for the menu item of the application too.
from django.utils.translation import ugettext_lazy as _
def init():
"""Intialize the module."""
menu = cfg.main_menu.get('apps:index')
menu.add_urlname(_('News Feed Reader (Tiny Tiny RSS)'), 'glyphicon-envelope',
'ttrss:index', 600)
Notice that in this case, we have used the ugettext_lazy
and in the first case we have used the regular ugettext
. This is because in the second case the gettext
lookup is made once and reused for every user looking at the interface. These users may each have a different language set for their interface. Lookup made for one language should not be used for other users. The _lazy
method provided by Django makes sure that the return value is an object that will actually be converted to string at the final moment when the string is being displayed. In the first case, the looked is made and string is returned immediately.
All of this is the usual way internationalization is done in Django. See Django internationalization and localization documentation for more information.
Coding standards
For readability and easy collaboration it is important to follow common coding standards. Plinth uses the Python coding standards and uses the pylint
and flake8
tools to check if the there are any violations. Run these tools on our application and fix any errors and warnings. Better yet, integrate these tools into your favorite IDE for on-the-fly checking.
For the most part, the code we have written so far, is already compliant with the coding standards. This includes variable/method naming, indentation, document strings, comments, etc. One thing we have to add are the module documentation strings. Let us add those. In __init__.py
add the top:
"""
Plinth module to configure Tiny Tiny RSS.
"""
Reference Guide
This section describes Plinth API that is most frequently used by application. Note that since Plinth is under development and has not yet reached 1.0, this API is subject to change. This is not usually a problem because all the Plinth applications currently reside in Plinth source repository itself and are updated when the API is updated.
Applications
These methods are optionally provided by the application and Plinth calls/uses them if they are present.
<application>.init()
Optional. This method is called by Plinth soon after all the applications are loaded. The init()
call order guarantees that other applications that this application depends on will be initialized before this application is initialized.
<application>.diagnose()
Optional. Called when the user invokes system-wide diagnostics by visiting System -> Diagnositcs
. This method must return an array of diagnostic results. Each diagnostic result must be a two-tuple with first element as a string that is shown to the user as name of the test and second element is the result of the test. It must be one of passed
, failed
, error
. Example return value:
[('Check http://localhost/app is reachable', 'passed'),
('Check configuration is sane', 'passed')]
<appliation>.depends
Optional. This module property must contain a list of all applications that this application depends on. The application is specified as string containing the full module load path. For example, plinth.modules.apps
.
plinth.package.required(package_list, before_install=None, on_install=on_install)
Make sure that a set of Debian packages are installed before a view can be accessed. If the packages are not currently installed on the system, a special installation view is displayed showing the list of packages to be installed. If the user chooses to proceed, package installation will start and an installation progress screen will be shown. After completion of the installation process, the original view is shown.
The package_list
must be an iterable containing the Debian package names as strings. If provided, the before_install
callable is called just before the installation process starts. Similarly, on_install
callable is called just after the package installation completes.
Actions
Plinth's web front does not directly change any aspect of the underlying operating system. Instead, it calls upon Actions, as shell commands. Actions live in /usr/share/plinth/actions
directory. They require no interaction beyond passing command line arguments or taking sensitive arguments via stdin. They change the operation of the services and applications of the FreedomBox and nothing else. These actions are also directly usable by a skilled administrator.
The following methods are provided by Plinth to run actions and to implement them easily by reusing code for common tasks.
plinth.actions.run(action, options=None, input=None, async=False)
Run an action command present under the actions/
directory. This runs subprocess.Popen()
after some checks. The action must be present in the actions/ directory.
options
are a list of additional arguments to pass to the command. If input
is given it must be bytearray containing the input to pass on to the executed action. If async
is set to True, the method will return without waiting for the command to finish.
plinth.actions.superuser_run(action, options=None, input=None, async=False)
This is same as plinth.actions.run()
except the command is run with superuser privelages.
plinth.action_utils
Several utlities to help with the implementation of actions and diagnotic tests are implemented in this module. Refer to the module source code for a list of these methods and their documentation.
Menus
plinth.cfg.main_menu
This is a reference to the global main menu. All menu entries in Plinth are decendents of this menu item. See Menu.add_item()
and Menu.add_urlname()
for adding items to this menu or its children.
plinth.menu.Menu.get(self, urlname, url_args=None, url_kwargs=None)
Return a child of this menu item. urlname
must be the name of a URL as configured in Django. django.core.urlresolvers.reverse()
is called before the lookup for child menu item is performed. url_args
and url_kwargs
are passed on to reverse()
.
plinth.menu.Menu.add_item(self, label, icon, url, order=50)
Add a menu item as a child to the current menu item. label
is the user visible string shown for the menu item. icon
must be a glyphicon class from the Twitter Bootstrap library. url
is the relative URL to which this menu item will take the user to.
plinth.menu.Menu.add_urlname(self, label, icon, urlname, order=50, url_args=None, url_kwargs=None)
Same as plinth.menu.Menu.add_item()
but instead of URL as input it is the name of a URL as configured in Django. django.core.urlresolvers.reverse()
is called before it is added to the parent menu item. url_args
and url_kwargs
are passed on to reverse()
.
Services
plinth.service.Service.__init__(self, service_id, name, ports=None, is_external=False, enabled=True)
Create a new Service object to notify all applications about the existence and status of a given application. service_id
is a unique identifier for this application. name
is a display name of this application that is shown by other applications such as on the firewall status page. ports
is a list of names recognized by firewalld when enabling or disabling firewalld services. If is_external
is true, the ports for this service are accessible from external interfaces, that is, from the Internet. Otherwise, the service is only available for client connected via LAN. enabled
is the current state of the application.
plinth.service.Service.is_enabled(self)
Return whether the service is currently enabled.
plinth.service.Service.notify_enabled(self, sender, enabled)
Notify other applications about the change of status of this application. sender
object should identify which application made the change. enabled
is a boolean that signifies whether the application is enabled (= True) or disabled (= False).
This is typically caught by the firewall application to enable or disable the ports corresponding to an application.
Hacking
FreedomBox consists of three main projects:
Plinth, the web interface
FreedomBox Setup, the Debian package to perform initial setup and
Freedom Maker, a script to build disk images for various hardware
Plinth
Plinth is a web interface to administer the functions of the FreedomBox.
Plinth is Free Software under GNU Affero General Public License version 3 or (at your option) a later version.
Using
Plinth comes installed with all FreedomBox images. You can download FreedomBox images and run on any of the supported hardware. Then, you can access Plinth by visiting the URL .
If you are on a Debian box, you may install Plinth from Debian package archive. Currently, only Stretch (testing) and Sid (unstable) are supported. To install Plinth run:
$ sudo apt-get install plinth
You can also get Plinth from its Git repository and install from source.
Screenshots
About Page
Enabling Tor Hidden Services
Setting up ownCloud
Automatic Firewall Operation
Support
You may ask for support on
The mailing list
#freedombox IRC channel
Contributing
We are looking for help to improve Plinth. You can contribute to Plinth by not just by coding but also by translating, documenting, designing, packaging and providing support.
Instructions on how to contribute code are available.
The primary Git repository is hosted at FreedomBox GitHub Page.
Instructions for installing from source and hacking the source are available.
List of bugs, TODO items and feature requests are available on the issue tracker.
Before contributing to Plinth code, you need understand Python and Django on top which it is built.
You can request for development assistance on the mailing list or the #freedombox IRC channel.
Debian Package
Plinth is packaged for Debian.
Packaging project is on Alioth along with sources.
Issues related to packaging are listed on Debian BTS.
FreedomBox Setup
FreedomBox Setup is a Debian package for setting up the FreedomBox. If you download and use pre-built images you don't have to worry about this package.
FreedomBox Setup is responsible for setting up basic networking, web server, user accounts, installing essential packages etc. It performs first part of the setup during the image build process. Later, when the image is booted for the first time on actual hardware (or on a virtual machine), it does the remaining setup and then reboots the machine. It also comes with a diagnostic script to check if the FreedomBox Setup is running as expected.
FreedomBox Setup is Free Software licensed under GNU General Public License version 3 or (at your option) a later version.
Using
FreedomBox Setup comes installed with all FreedomBox images. You can download FreedomBox images and run on any of the supported hardware.
If you are on a Debian box, you may install FreedomBox Setup from Debian package archive. This essentially turns your Debian installation into a FreedomBox! Currently, only Sid (unstable) is supported. To install FreedomBox Setup, see instructions on setting up FreedomBox on a Debian machine.
You can also get FreedomBox Setup from its Git repository and build Debian package from source.
Support
You may ask for support on
The mailing list
#freedombox IRC channel
Contributing
We are looking for help to improve FreedomBox Setup.
Instructions on how to contribute code are available.
FreedomBox Setup is part of the FreedomBox Alioth Project.
List of bugs, TODO items, packages issues and feature requests are available on the issue tracker.
You can request for development assistance on the mailing list or the #freedombox IRC channel.
See Debian tracker for information on Debian package. FreedomBox Setup is a Debian native package meaning it is primarily built for Debian and comes with Debian packaging scripts in its repository.
Freedom Maker
Freedom Maker is a script to build FreedomBox disk images for use on various hardware devices or virtual machines.
Freedom Maker can currently build FreedomBox disk images for the following:
DreamPlug
Raspberry Pi
BeagleBone
Cubietruck (work in progress)
VirtualBox
Other virtual machines (using raw disk images)
It relies on the vmdebootstrap project actually create images. If a hardware platform is capable of running Debian, it should not be too much effort adopt Freedom Maker to create FreedomBox images for the platform.
Freedom Maker is Free Software licensed under GNU General Public License version 3 or (at your option) a later version.
Building FreedomBox Images
You can get Freedom Maker from its Git repository and follow the instructions in the README to build a FreedomBox image.
Support
You may ask for support on
The mailing list
#freedombox IRC channel
Contributing
We are looking for help to improve Freedom Maker.
Instructions on how to contribute code are available.
Freedom Maker is hosted on FreedomBox Alioth Portal. The primary Git repository is hosted there.
Freedom Maker is also hosted on FreedomBox GitHub Page. Pull requests are accepted there.
You can contribute to FreedomBox by adding support for more hardware platforms. Freedom Maker can be easily adopted to newer platforms if they already support running Debian.
You can create and test images with Freedom Maker regularly to test for new features and check for regressions.
List of bugs, TODO items and feature requests are available on the issue tracker.
You can request for development assistance on the mailing list or the #freedombox IRC channel.
Other Resources: Manual Older Versions
0.3 Manual
0.2 Manual
Jessie Manual
Tell people around you
FreedomBox in the Press
Conferences
Talks and presentations
Available Material Slides and other raw material
Facebook
Twitter
Debconf11 Videos
Plinth-0.8.1/doc/images/ 0000775 0000000 0000000 00000000000 12660516711 0014775 5 ustar 00root root 0000000 0000000 Plinth-0.8.1/doc/images/a20-olinuxino-lime2.jpg 0000664 0000000 0000000 00000722616 12660516711 0021127 0 ustar 00root root 0000000 0000000 JFIF H H VExif II*
( 1 2 i 0 SAMSUNG SM-G900H H H GIMP 2.8.14 2015:11:19 21:46:55 " ' @ 0220 *
> F
N
V ^
f | b n (- 0100 / d
2015:11:19 21:14:19 2015:11:19 21:14:19 d d d
d d 0100 Z @ P ASCII F & w _ &