Import Upstream version 3.20.0.2022.0601
|
@ -0,0 +1,142 @@
|
|||
name: Check build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev-3.1
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
- dev-3.1
|
||||
|
||||
jobs:
|
||||
# archlinux-latest:
|
||||
# name: on Archlinux Latest
|
||||
# runs-on: ubuntu-20.04
|
||||
# container: docker.io/library/archlinux:latest
|
||||
# steps:
|
||||
# - name: Checkout ukui-control-center source code
|
||||
# uses: actions/checkout@v2
|
||||
# - name: Refresh pacman repository and force upgrade
|
||||
# run: pacman -Syyu --noconfirm
|
||||
# - name: Install build dependencies
|
||||
# run: pacman -S --noconfirm base-devel qt5-base gsettings-qt kwindowsystem poppler-qt5 qt5-x11extras glib2 qt5-tools pkg-config kwidgetsaddons kconfig kconfigwidgets kscreen qt5-svg libmatekbd qt5-x11extras libxklavier qt5-declarative libmatemixer libqtxdg qt5-multimedia libxml2 libcanberra mate-desktop libxkbcommon libxkbfile ki18n kguiaddons kcoreaddons boost glibc bluez-qt
|
||||
# - name: QMake configure & Make
|
||||
# run: |
|
||||
# mkdir build;
|
||||
# cd build;
|
||||
# qmake-qt5 ..;
|
||||
# make -j$(nproc);
|
||||
|
||||
# debian-sid:
|
||||
# name: on Debian Sid
|
||||
# runs-on: ubuntu-20.04
|
||||
# container: docker.io/library/debian:sid
|
||||
# env:
|
||||
# DEBIAN_FRONTEND: noninteractive
|
||||
# steps:
|
||||
# - name: Checkout ukui-control-center source code
|
||||
# uses: actions/checkout@v2
|
||||
# - name: Update apt repository
|
||||
# run: apt-get update -y
|
||||
# - name: Install build dependencies
|
||||
# run: apt-get install -y build-essential qt5-qmake qttools5-dev-tools pkg-kde-tools pkg-config libkf5widgetsaddons-dev libkf5config-dev libkf5configwidgets-dev libkf5screen-dev debhelper-compat libqt5svg5-dev libgsettings-qt-dev libglib2.0-dev libmatekbd-dev libqt5x11extras5-dev libxklavier-dev qtdeclarative5-dev libdconf-dev libmatemixer-dev libqt5xdg-dev qtmultimedia5-dev libxml2-dev libcanberra-dev libmate-desktop-dev libxkbcommon-dev libxkbfile-dev libkf5i18n-dev libkf5windowsystem-dev libkf5guiaddons-dev libkf5coreaddons-dev libboost-dev libxcb-xkb-dev libpolkit-qt5-1-dev libpulse-dev libkf5bluezqt-dev
|
||||
# - name: QMake configure & Make
|
||||
# run: |
|
||||
# mkdir build;
|
||||
# cd build;
|
||||
# qmake ..;
|
||||
# make -j$(nproc);
|
||||
|
||||
# fedora-latest:
|
||||
# name: on Fedora Latest
|
||||
# runs-on: ubuntu-20.04
|
||||
# container: docker.io/library/fedora:latest
|
||||
# steps:
|
||||
# - name: Checkout ukui-control-center source code
|
||||
# uses: actions/checkout@v2
|
||||
# - name: Install build dependencies
|
||||
# run: dnf install --refresh -y which gcc gcc-c++ make cmake cmake-rpm-macros autoconf automake intltool rpm-build qt5-rpm-macros qt5-qtbase-devel qt5-qtsvg-devel gsettings-qt-devel glib2-devel qt5-qtx11extras-devel libmatekbd-devel libxklavier-devel kf5-kconfigwidgets-devel kf5-kconfig-devel qt5-qtdeclarative-devel dconf-devel redshift edid-decode libmatemixer-devel libqtxdg-devel qt5-qtmultimedia-devel libxml2-devel libkscreen-qt5-devel kf5-ki18n-devel libcanberra-devel libXi-devel mate-desktop-devel libxkbcommon-devel libxkbfile-devel qt5-linguist kf5-kwindowsystem-devel kf5-kguiaddons-devel kf5-kcoreaddons-devel boost-devel libxcb-devel xcb-util-devel polkit-qt5-1-devel kf5-bluez-qt-devel
|
||||
# - name: QMake configure & Make
|
||||
# run: |
|
||||
# ln -s /usr/bin/lrelease-qt5 /usr/bin/lrelease;
|
||||
# mkdir build;
|
||||
# cd build;
|
||||
# qmake-qt5 ..;
|
||||
# make -j$(nproc);
|
||||
|
||||
# fedora-rawhide:
|
||||
# name: on Fedora Rawhide
|
||||
# runs-on: ubuntu-20.04
|
||||
# container: docker.io/library/fedora:rawhide
|
||||
# steps:
|
||||
# - name: Checkout ukui-control-center source code
|
||||
# uses: actions/checkout@v2
|
||||
# - name: Install build dependencies
|
||||
# run: dnf install --refresh --nogpg -y which gcc gcc-c++ make cmake cmake-rpm-macros autoconf automake intltool rpm-build qt5-rpm-macros qt5-qtbase-devel qt5-qtsvg-devel gsettings-qt-devel glib2-devel qt5-qtx11extras-devel libmatekbd-devel libxklavier-devel kf5-kconfigwidgets-devel kf5-kconfig-devel qt5-qtdeclarative-devel dconf-devel redshift edid-decode libmatemixer-devel libqtxdg-devel qt5-qtmultimedia-devel libxml2-devel libkscreen-qt5-devel kf5-ki18n-devel libcanberra-devel libXi-devel mate-desktop-devel libxkbcommon-devel libxkbfile-devel qt5-linguist kf5-kwindowsystem-devel kf5-kguiaddons-devel kf5-kcoreaddons-devel boost-devel libxcb-devel xcb-util-devel polkit-qt5-1-devel kf5-bluez-qt-devel
|
||||
# - name: QMake configure & Make
|
||||
# run: |
|
||||
# ln -s /usr/bin/lrelease-qt5 /usr/bin/lrelease;
|
||||
# mkdir build;
|
||||
# cd build;
|
||||
# qmake-qt5 ..;
|
||||
# make -j$(nproc);
|
||||
|
||||
# opensuse-tumbleweed:
|
||||
# name: on openSUSE Tumbleweed
|
||||
# runs-on: ubuntu-20.04
|
||||
# container: opensuse/tumbleweed:latest
|
||||
# steps:
|
||||
# - name: Install source checkout utils
|
||||
# run: zypper -n install tar git
|
||||
# - name: Checkout ukui-control-center source code
|
||||
# uses: actions/checkout@v2
|
||||
# - name: Install build dependencies
|
||||
# run: zypper -n install atk-devel at-spi2-atk-devel at-spi2-core-devel cairo-devel cmake cmake-full cmake-man dbus-1-devel dconf-devel extra-cmake-modules extra-cmake-modules-doc fontconfig-devel freetype2-devel fribidi-devel gcc10-c++ gcc-c++ gdk-pixbuf-devel gettext-its-gtk4 glib2-devel graphite2-devel gsettings-qt-devel gtk3-devel harfbuzz-devel kauth-devel kcodecs-devel kconfig-devel kconfigwidgets-devel kcoreaddons-devel kf5-filesystem ki18n-devel kwidgetsaddons-devel kwindowsystem-devel libblkid-devel libbrotli-devel libbz2-devel libcairo-script-interpreter2 libcanberra-devel libdatrie-devel libdrm-devel libepoxy-devel libexpat-devel libffi-devel libglvnd-devel libharfbuzz-gobject0 libharfbuzz-subset0 libicu-devel libjsoncpp24 libkscreen2-devel libmatekbd-devel libmatemixer-devel libmount-devel libpcre16-0 libpcrecpp0 libpcreposix0 libpixman-1-0-devel libpng16-compat-devel libpng16-devel libpulse-devel libQt5Concurrent-devel libQt5Core-devel libQt5DBus-devel libQt5Gui-devel libqt5-linguist libQt5Network-devel libQt5PrintSupport-devel libqt5-qtbase-common-devel libqt5-qtdeclarative-devel libqt5-qtdeclarative-tools libqt5-qtmultimedia-devel libqt5-qtsvg-devel libqt5-qtx11extras-devel libQt5Sql-devel libQt5Test-devel libQt5Widgets-devel libQt5Xml-devel librhash0 libselinux-devel libsepol-devel libstdc++6-devel-gcc10 libstdc++-devel libthai-devel libuuid-devel libX11-devel libXau-devel libxcb-devel libxcb-screensaver0 libxcb-xf86dri0 libxcb-xtest0 libxcb-xvmc0 libXcomposite-devel libXcursor-devel libXdamage-devel libXext-devel libXfixes-devel libXft-devel libXi-devel libXinerama-devel libxkbcommon-devel libxklavier-devel libxml2-devel libXrandr-devel libXrender-devel libXtst-devel mate-desktop-devel Mesa-KHR-devel Mesa-libEGL-devel Mesa-libGL-devel ncurses-devel pango-devel pcre-devel readline-devel startup-notification-devel tack typelib-1_0-Atk-1_0 typelib-1_0-Atspi-2_0 typelib-1_0-GdkPixbuf-2_0 typelib-1_0-GdkPixdata-2_0 typelib-1_0-Gtk-3_0 typelib-1_0-HarfBuzz-0_0 typelib-1_0-Pango-1_0 typelib-1_0-Xkl-1_0 vulkan-devel vulkan-headers wayland-devel wayland-protocols-devel xorgproto-devel xz-devel zlib-devel libKF5Screen7 libkscreen2-plugin libpulse0 libpulse-mainloop-glib0 pulseaudio pulseaudio-bash-completion pulseaudio-module-bluetooth pulseaudio-module-gsettings pulseaudio-module-x11 pulseaudio-module-zeroconf pulseaudio-utils extra-cmake-modules-doc libpng16-compat-devel kguiaddons-devel libpwquality1 libxkbcommon-devel libxkbfile-devel libpolkit-qt5-1-devel 'libboost*-devel' bluez-qt-devel
|
||||
# - name: QMake configure & Make
|
||||
# run: |
|
||||
# ln -s /usr/bin/lrelease-qt5 /usr/bin/lrelease;
|
||||
# mkdir build;
|
||||
# cd build;
|
||||
# qmake-qt5 ..;
|
||||
# make -j$(nproc);
|
||||
|
||||
ubuntu-latest:
|
||||
name: on Ubuntu Latest
|
||||
runs-on: ubuntu-20.04
|
||||
container: docker.io/library/ubuntu:latest
|
||||
env:
|
||||
DEBIAN_FRONTEND: noninteractive
|
||||
steps:
|
||||
- name: Checkout ukui-control-center source code
|
||||
uses: actions/checkout@v2
|
||||
- name: Update apt repository
|
||||
run: apt-get update -y
|
||||
- name: Install build dependencies
|
||||
run: apt-get install -y build-essential qt5-default qttools5-dev-tools pkg-kde-tools pkg-config libkf5widgetsaddons-dev libkf5config-dev libkf5configwidgets-dev libkf5screen-dev debhelper-compat libqt5svg5-dev libgsettings-qt-dev libglib2.0-dev libmatekbd-dev libqt5x11extras5-dev libxklavier-dev qtdeclarative5-dev libdconf-dev libmatemixer-dev libqt5xdg-dev qtmultimedia5-dev libxml2-dev libcanberra-dev libmate-desktop-dev libxkbcommon-dev libxkbfile-dev libkf5i18n-dev libkf5windowsystem-dev libkf5guiaddons-dev libkf5coreaddons-dev libboost-dev libxcb-xkb-dev libpolkit-qt5-1-dev libkf5bluezqt-dev libudev-dev xserver-xorg-dev libupower-glib-dev libpam0g-dev libkf5xmlgui-dev libkf5globalaccel-dev
|
||||
- name: QMake configure & Make
|
||||
run: |
|
||||
mkdir build;
|
||||
cd build;
|
||||
qmake ..;
|
||||
make -j$(nproc);
|
||||
|
||||
# ubuntu-rolling:
|
||||
# name: on Ubuntu Rolling
|
||||
# runs-on: ubuntu-20.04
|
||||
# container: docker.io/library/ubuntu:rolling
|
||||
# env:
|
||||
# DEBIAN_FRONTEND: noninteractive
|
||||
# steps:
|
||||
# - name: Checkout ukui-control-center source code
|
||||
# uses: actions/checkout@v2
|
||||
# - name: Update apt repository
|
||||
# run: apt-get update -y
|
||||
# - name: Install build dependencies
|
||||
# run: apt-get install -y build-essential qttools5-dev-tools pkg-kde-tools pkg-config libkf5widgetsaddons-dev libkf5config-dev libkf5configwidgets-dev libkf5screen-dev debhelper-compat libqt5svg5-dev libgsettings-qt-dev libglib2.0-dev libmatekbd-dev libqt5x11extras5-dev libxklavier-dev qtdeclarative5-dev libdconf-dev libmatemixer-dev libqt5xdg-dev qtmultimedia5-dev libxml2-dev libcanberra-dev libmate-desktop-dev libxkbcommon-dev libxkbfile-dev libkf5i18n-dev libkf5windowsystem-dev libkf5guiaddons-dev libkf5coreaddons-dev libboost-dev libxcb-xkb-dev libpolkit-qt5-1-dev libpulse-dev libkf5bluezqt-dev libudev-dev xserver-xorg-dev libupower-glib-dev libpam0g-dev libkf5xmlgui-dev libkf5globalaccel-dev
|
||||
# - name: QMake configure & Make
|
||||
# run: |
|
||||
# mkdir build;
|
||||
# cd build;
|
||||
# qmake ..;
|
||||
# make -j$(nproc);
|
After Width: | Height: | Size: 72 KiB |
|
@ -0,0 +1,339 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
|
@ -0,0 +1,8 @@
|
|||
2.0.0
|
||||
* Migrate from gtk to qt.
|
||||
|
||||
1.1.0
|
||||
* update gtk3 API
|
||||
|
||||
0.0.1
|
||||
* init.
|
|
@ -0,0 +1,385 @@
|
|||
## ukui-control-center
|
||||
|
||||
![build](https://github.com/ukui/ukui-control-center/workflows/build/badge.svg?branch=master)
|
||||
|
||||
UKCC(ukui-control-center)是UKUI桌面环境的控制面板
|
||||
|
||||
### 依赖
|
||||
|
||||
------
|
||||
|
||||
### 编译依赖
|
||||
|
||||
- KF5
|
||||
- libkf5widgetsaddons-dev
|
||||
- libkf5config-dev
|
||||
- libkf5configwidgets-dev
|
||||
- libkf5screen-dev
|
||||
|
||||
- debhelper-compat
|
||||
- libqt5svg5-dev
|
||||
- libgsettings-qt-dev
|
||||
- libglib2.0-dev
|
||||
- libmatekbd-dev
|
||||
- libqt5x11extras5-dev
|
||||
- libxklavier-dev
|
||||
- qtdeclarative5-dev
|
||||
- libdconf-dev
|
||||
- libmatemixer-dev
|
||||
- libqt5xdg-dev
|
||||
- qtmultimedia5-dev
|
||||
- libxml2-dev
|
||||
|
||||
### 运行依赖
|
||||
|
||||
- ukui-power-manager
|
||||
- ukui-session-manager
|
||||
- ukui-screensaver
|
||||
- ukui-settings-daemon
|
||||
- qml-module-qtgraphicaleffects
|
||||
- redshift
|
||||
- edid-decode
|
||||
|
||||
### 编译
|
||||
|
||||
------
|
||||
|
||||
```shell
|
||||
$ cd ukui-control-center
|
||||
$ mkdir build
|
||||
$ cd build
|
||||
$ qmake ..
|
||||
$ make
|
||||
```
|
||||
|
||||
### 安装
|
||||
|
||||
------
|
||||
|
||||
```shell
|
||||
$ sudo make install
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 主体框架
|
||||
- **InProgress**
|
||||
- [x] 界面绘制
|
||||
- [x] 功能实现
|
||||
- [x] 界面美化
|
||||
- **TROUBLE**
|
||||
- 无
|
||||
- **TODO**
|
||||
- 搜索
|
||||
- 动画效果
|
||||
|
||||
### 功能插件
|
||||
#### 系统
|
||||
##### 显示器
|
||||
- **InProgress**
|
||||
- [x] 界面绘制
|
||||
- [x] 功能实现
|
||||
- [x] 界面美化
|
||||
- **TODO**
|
||||
- 部分环境重启不生效BUG[DBus后台服务编写]
|
||||
|
||||
##### 默认应用程序
|
||||
- **InProgress**
|
||||
- [x] 界面绘制
|
||||
- [x] 功能实现
|
||||
- [ ] 界面美化
|
||||
- **TROUBLE**
|
||||
- 无
|
||||
- **TODO**
|
||||
- 添加界面未设计
|
||||
|
||||
##### 电源
|
||||
- **InProgress**
|
||||
- [x] 界面绘制
|
||||
- [x] 功能实现
|
||||
- [ ] 界面美化
|
||||
- **TROUBLE**
|
||||
- 无
|
||||
- **TODO**
|
||||
- 无
|
||||
|
||||
##### 开机启动
|
||||
- **InProgress**
|
||||
- [x] 界面绘制
|
||||
- [x] 功能实现
|
||||
- [ ] 界面美化
|
||||
- **TROUBLE**
|
||||
- 无
|
||||
- **TODO**
|
||||
- 添加程序从desktop改为易于理解的程序名称
|
||||
|
||||
#### 设备
|
||||
##### 打印机
|
||||
- **InProgress**
|
||||
- [x] 界面绘制
|
||||
- [ ] 功能实现
|
||||
- [ ] 界面美化
|
||||
- **TROUBLE**
|
||||
- 无
|
||||
- **TODO**
|
||||
- 无
|
||||
|
||||
##### 鼠标
|
||||
- **InProgress**
|
||||
- [x] 界面绘制
|
||||
- [x] 功能实现
|
||||
- [ ] 界面美化
|
||||
- **TROUBLE**
|
||||
- 光标粗细暂无接口
|
||||
- **TODO**
|
||||
- 文本区域光标闪烁功能
|
||||
|
||||
##### 触摸板
|
||||
|
||||
- **InProgress**
|
||||
- [x] 界面绘制
|
||||
- [x] 功能实现
|
||||
- [ ] 界面美化
|
||||
- **TROUBLE**
|
||||
- 无
|
||||
- **TODO**
|
||||
- 无
|
||||
|
||||
##### 键盘
|
||||
- **InProgress**
|
||||
- [x] 界面绘制
|
||||
- [x] 功能实现
|
||||
- [ ] 界面美化
|
||||
- **TROUBLE**
|
||||
- 无
|
||||
- **TODO**
|
||||
- 键盘布局的实现
|
||||
|
||||
##### 快捷键
|
||||
|
||||
- **InProgress**
|
||||
- [x] 界面绘制
|
||||
- [x] 功能实现
|
||||
- [ ] 界面美化
|
||||
- **TROUBLE**
|
||||
- 无
|
||||
- **TODO**
|
||||
- 快捷键功能不生效问题
|
||||
|
||||
##### 声音
|
||||
- **InProgress**
|
||||
- [x] 界面绘制
|
||||
- [x] 功能实现
|
||||
- [ ] 界面美化
|
||||
- **TROUBLE**
|
||||
- 无
|
||||
- **TODO**
|
||||
- 无
|
||||
|
||||
#### 个性化
|
||||
##### 背景
|
||||
- **InProgress**
|
||||
- [x] 界面绘制
|
||||
- [x] 功能实现
|
||||
- [ ] 界面美化
|
||||
- **TROUBLE**
|
||||
- 无
|
||||
- **TODO**
|
||||
- 纯色
|
||||
- 幻灯片
|
||||
- 背景图片放置方式
|
||||
|
||||
##### 主题
|
||||
- **InProgress**
|
||||
- [x] 界面绘制
|
||||
- [x] 功能实现
|
||||
- [ ] 界面美化
|
||||
- **TROUBLE**
|
||||
- 无
|
||||
- **TODO**
|
||||
- 主题切换速度优化
|
||||
|
||||
##### 锁屏
|
||||
- **InProgress**
|
||||
- [x] 界面绘制
|
||||
- [x] 功能实现
|
||||
- [ ] 界面美化
|
||||
- **TROUBLE**
|
||||
- 无
|
||||
- **TODO**
|
||||
- 预览效果
|
||||
|
||||
##### 字体
|
||||
- **InProgress**
|
||||
- [x] 界面绘制
|
||||
- [x] 功能实现
|
||||
- [ ] 界面美化
|
||||
- **TROUBLE**
|
||||
- 无
|
||||
- **TODO**
|
||||
- 字体预览效果
|
||||
- 字体高级设置
|
||||
|
||||
##### 屏幕保护
|
||||
- **InProgress**
|
||||
- [x] 界面绘制
|
||||
- [x] 功能实现
|
||||
- [ ] 界面美化
|
||||
- **TROUBLE**
|
||||
- 无
|
||||
- **TODO**
|
||||
- 预览
|
||||
|
||||
##### 桌面
|
||||
- **InProgress**
|
||||
- [x] 界面绘制
|
||||
- [ ] 功能实现
|
||||
- [ ] 界面美化
|
||||
- **TROUBLE**
|
||||
- 无
|
||||
- **TODO**
|
||||
- 桌面图标开启/关闭功能
|
||||
|
||||
#### 网络
|
||||
##### 网络连接
|
||||
- **InProgress**
|
||||
- [x] 界面绘制
|
||||
- [ ] 功能实现
|
||||
- [ ] 界面美化
|
||||
- **TROUBLE**
|
||||
- 无
|
||||
- **TODO**
|
||||
- 实时刷新功能
|
||||
|
||||
##### VPN
|
||||
- **InProgress**
|
||||
- [x] 界面绘制
|
||||
- [ ] 功能实现
|
||||
- [ ] 界面美化
|
||||
- **TROUBLE**
|
||||
- 无
|
||||
- **TODO**
|
||||
- 无
|
||||
|
||||
##### 代理
|
||||
- **InProgress**
|
||||
- [x] 界面绘制
|
||||
- [x] 功能实现
|
||||
- [ ] 界面美化
|
||||
- **TROUBLE**
|
||||
- 无
|
||||
- **TODO**
|
||||
- 无
|
||||
|
||||
#### 帐户
|
||||
##### 帐户信息
|
||||
- **InProgress**
|
||||
- [x] 界面绘制
|
||||
- [x] 功能实现
|
||||
- [ ] 界面美化
|
||||
- **TROUBLE**
|
||||
- 无
|
||||
- **TODO**
|
||||
- 密码加密后才能设置
|
||||
- 添加新用户控件换行问题
|
||||
- 仅剩一个管理员时的处理
|
||||
- 密码过期功能
|
||||
|
||||
##### 登录选项
|
||||
- **InProgress**
|
||||
- [x] 界面绘制
|
||||
- [ ] 功能实现
|
||||
- [ ] 界面美化
|
||||
- **TROUBLE**
|
||||
- 功能暂时不支持
|
||||
- **TODO**
|
||||
- 无
|
||||
|
||||
##### 云帐户
|
||||
|
||||
- **InProgress**
|
||||
- [x] 界面绘制
|
||||
- [x] 功能实现
|
||||
- [x] 界面美化
|
||||
- **TROUBLE**
|
||||
- 无
|
||||
- **TODO**
|
||||
- 无
|
||||
|
||||
|
||||
#### 时间和语言
|
||||
##### 时间和日期
|
||||
- **InProgress**
|
||||
- [x] 界面绘制
|
||||
- [x] 功能实现
|
||||
- [x] 界面美化
|
||||
- **TROUBLE**
|
||||
- 无
|
||||
- **TODO**
|
||||
- 无
|
||||
|
||||
##### 语言和地区
|
||||
- **InProgress**
|
||||
- [x] 界面绘制
|
||||
- [x] 功能实现
|
||||
- [x] 界面美化
|
||||
- **TROUBLE**
|
||||
- 无
|
||||
- **TODO**
|
||||
- 中英文以外其他区域格式切换
|
||||
- 中英文以外其他语言切换
|
||||
- 语言添加功能
|
||||
|
||||
|
||||
#### 更新和安全
|
||||
##### 更新
|
||||
- **InProgress**
|
||||
- [x] 界面绘制
|
||||
- [ ] 功能实现
|
||||
- [ ] 界面美化
|
||||
- **TROUBLE**
|
||||
- 无
|
||||
- **TODO**
|
||||
- 无
|
||||
|
||||
##### 备份
|
||||
- **InProgress**
|
||||
- [x] 界面绘制
|
||||
- [ ] 功能实现
|
||||
- [ ] 界面美化
|
||||
- **TROUBLE**
|
||||
- 无
|
||||
- **TODO**
|
||||
- 无
|
||||
|
||||
|
||||
#### 通知和操作
|
||||
##### 通知
|
||||
- **InProgress**
|
||||
- [x] 界面绘制
|
||||
- [ ] 功能实现
|
||||
- [ ] 界面美化
|
||||
- **TROUBLE**
|
||||
- 无
|
||||
- **TODO**
|
||||
- 无
|
||||
|
||||
##### 多任务
|
||||
- **InProgress**
|
||||
- [ ] 界面绘制
|
||||
- [ ] 功能实现
|
||||
- [ ] 界面美化
|
||||
- **TROUBLE**
|
||||
- 无
|
||||
- **TODO**
|
||||
- 功能待实现
|
||||
|
||||
##### 关于
|
||||
- **InProgress**
|
||||
- [x] 界面绘制
|
||||
- [x] 功能实现
|
||||
- [ ] 界面美化
|
||||
- **TROUBLE**
|
||||
- 无
|
||||
- **TODO**
|
||||
- 无
|
|
@ -0,0 +1,38 @@
|
|||
QT -= gui
|
||||
|
||||
TARGET = changeotheruserpwd
|
||||
TEMPLATE = app
|
||||
|
||||
CONFIG += c++11
|
||||
CONFIG -= app_bundle
|
||||
|
||||
# The following define makes your compiler emit warnings if you use
|
||||
# any Qt feature that has been marked deprecated (the exact warnings
|
||||
# depend on your compiler). Please consult the documentation of the
|
||||
# deprecated API in order to know how to port your code away from it.
|
||||
DEFINES += QT_DEPRECATED_WARNINGS
|
||||
|
||||
|
||||
##加载gio库和gio-unix库
|
||||
CONFIG += link_pkgconfig \
|
||||
C++11
|
||||
PKGCONFIG += gio-2.0 \
|
||||
gio-unix-2.0 \
|
||||
|
||||
# You can also make your code fail to compile if it uses deprecated APIs.
|
||||
# In order to do so, uncomment the following line.
|
||||
# You can also select to disable deprecated APIs only up to a certain version of Qt.
|
||||
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||
|
||||
target.source += $$TARGET
|
||||
target.path = /usr/bin
|
||||
INSTALLS += \
|
||||
target \
|
||||
|
||||
|
||||
SOURCES += \
|
||||
main.cpp \
|
||||
run-passwd2.cpp
|
||||
|
||||
HEADERS += \
|
||||
run-passwd2.h
|
|
@ -0,0 +1,57 @@
|
|||
#include <QCoreApplication>
|
||||
|
||||
#include <glib.h>
|
||||
#include "run-passwd2.h"
|
||||
|
||||
PasswdHandler *passwd_handler = NULL;
|
||||
|
||||
static void auth_cb (PasswdHandler *passwd_handler, GError *error, gpointer user_data);
|
||||
static void chpasswd_cb (PasswdHandler *passwd_handler, GError *error, gpointer user_data);
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
if (argc != 3)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
QCoreApplication a(argc, argv);
|
||||
|
||||
passwd_handler = passwd_init();
|
||||
|
||||
passwd_change_password(passwd_handler, argv[1], argv[2], chpasswd_cb, NULL);
|
||||
|
||||
return a.exec();
|
||||
}
|
||||
|
||||
static void
|
||||
auth_cb (PasswdHandler *passwd_handler, GError *error, gpointer user_data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief chpasswd_cb
|
||||
* @param passwd_handler
|
||||
* @param error
|
||||
* @param user_data
|
||||
*/
|
||||
static void
|
||||
chpasswd_cb (PasswdHandler *passwd_handler,
|
||||
GError *error,
|
||||
gpointer user_data)
|
||||
{
|
||||
|
||||
|
||||
if (!error) {
|
||||
|
||||
qApp->exit(0);
|
||||
} else {
|
||||
|
||||
passwd_destroy (passwd_handler);
|
||||
|
||||
qApp->exit(1);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,712 @@
|
|||
|
||||
|
||||
/* qt会将glib里的signals成员识别为宏,所以取消该宏
|
||||
* 后面如果用到signals时,使用Q_SIGNALS代替即可
|
||||
**/
|
||||
#ifdef signals
|
||||
#undef signals
|
||||
#endif
|
||||
|
||||
#include <glib.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "run-passwd2.h"
|
||||
|
||||
/* Buffer size for backend output */
|
||||
#define BUFSIZE 64
|
||||
|
||||
/* Passwd states */
|
||||
//后端passwd的状态,NONE应该是passwd还没有启动,ERROR表示报错但还没退出
|
||||
typedef enum {
|
||||
PASSWD_STATE_NONE, /* Passwd is not asking for anything */
|
||||
PASSWD_STATE_AUTH, /* Passwd is asking for our current password */
|
||||
PASSWD_STATE_NEW, /* Passwd is asking for our new password */
|
||||
PASSWD_STATE_RETYPE, /* Passwd is asking for our retyped new password */
|
||||
PASSWD_STATE_ERR /* Passwd reported an error but has not yet exited */
|
||||
} PasswdState;
|
||||
|
||||
struct PasswdHandler {
|
||||
// GtkBuilder *ui;
|
||||
|
||||
const char *current_password;
|
||||
const char *new_password;
|
||||
const char *retyped_password;
|
||||
|
||||
/* Communication with the passwd program */
|
||||
GPid backend_pid;
|
||||
|
||||
GIOChannel *backend_stdin;
|
||||
GIOChannel *backend_stdout;
|
||||
|
||||
GQueue *backend_stdin_queue; /* Write queue to backend_stdin */
|
||||
|
||||
/* GMainLoop IDs */
|
||||
guint backend_child_watch_id; /* g_child_watch_add (PID) */
|
||||
guint backend_stdout_watch_id; /* g_io_add_watch (stdout) */
|
||||
|
||||
/* State of the passwd program */
|
||||
PasswdState backend_state;
|
||||
gboolean changing_password;
|
||||
|
||||
PasswdCallback auth_cb;
|
||||
gpointer auth_cb_data;
|
||||
|
||||
PasswdCallback chpasswd_cb;
|
||||
gpointer chpasswd_cb_data;
|
||||
};
|
||||
|
||||
//GQuark是一个guint32
|
||||
static GQuark
|
||||
passwd_error_quark (void)
|
||||
{
|
||||
static GQuark q = 0;
|
||||
|
||||
//返回错误的标识码
|
||||
if (q == 0) {
|
||||
q = g_quark_from_static_string("passwd_error");
|
||||
}
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
/* Error handling */
|
||||
#define PASSWD_ERROR (passwd_error_quark ())
|
||||
|
||||
static void stop_passwd (PasswdHandler *passwd_handler);
|
||||
|
||||
static void free_passwd_resources (PasswdHandler *passwd_handler);
|
||||
|
||||
static gboolean io_watch_stdout (GIOChannel *source, GIOCondition condition, PasswdHandler *passwd_handler);
|
||||
|
||||
|
||||
static void free_passwd_resources (PasswdHandler *passwd_handler)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
/* Remove the child watcher */
|
||||
if (passwd_handler->backend_child_watch_id != 0) {
|
||||
|
||||
g_source_remove (passwd_handler->backend_child_watch_id);
|
||||
|
||||
passwd_handler->backend_child_watch_id = 0;
|
||||
}
|
||||
|
||||
|
||||
/* Close IO channels (internal file descriptors are automatically closed) */
|
||||
if (passwd_handler->backend_stdin != NULL) {
|
||||
|
||||
if (g_io_channel_shutdown (passwd_handler->backend_stdin, TRUE, &error) != G_IO_STATUS_NORMAL) {
|
||||
g_warning ("Could not shutdown backend_stdin IO channel: %s", error->message);
|
||||
g_error_free (error);
|
||||
error = NULL;
|
||||
}
|
||||
|
||||
g_io_channel_unref (passwd_handler->backend_stdin);
|
||||
passwd_handler->backend_stdin = NULL;
|
||||
}
|
||||
|
||||
if (passwd_handler->backend_stdout != NULL) {
|
||||
|
||||
if (g_io_channel_shutdown (passwd_handler->backend_stdout, TRUE, &error) != G_IO_STATUS_NORMAL) {
|
||||
g_warning ("Could not shutdown backend_stdout IO channel: %s", error->message);
|
||||
g_error_free (error);
|
||||
error = NULL;
|
||||
}
|
||||
|
||||
g_io_channel_unref (passwd_handler->backend_stdout);
|
||||
|
||||
passwd_handler->backend_stdout = NULL;
|
||||
}
|
||||
|
||||
/* Remove IO watcher */
|
||||
if (passwd_handler->backend_stdout_watch_id != 0) {
|
||||
|
||||
g_source_remove (passwd_handler->backend_stdout_watch_id);
|
||||
|
||||
passwd_handler->backend_stdout_watch_id = 0;
|
||||
}
|
||||
|
||||
/* Close PID */
|
||||
//因为flag为G_SPAWN_DO_NOT_REAP_CHILD,所以child不会自动的被reap掉,需要在子进程上free
|
||||
if (passwd_handler->backend_pid != -1) {
|
||||
|
||||
g_spawn_close_pid (passwd_handler->backend_pid);
|
||||
|
||||
passwd_handler->backend_pid = -1;
|
||||
}
|
||||
|
||||
/* Clear backend state */
|
||||
passwd_handler->backend_state = PASSWD_STATE_NONE;
|
||||
}
|
||||
|
||||
static void authenticate (PasswdHandler *passwd_handler)
|
||||
{
|
||||
gchar *s;
|
||||
|
||||
s = g_strdup_printf ("%s\n", passwd_handler->current_password);
|
||||
g_queue_push_tail (passwd_handler->backend_stdin_queue, s);
|
||||
}
|
||||
|
||||
static void io_queue_pop (GQueue *queue, GIOChannel *channel)
|
||||
{
|
||||
gchar *buf;
|
||||
gsize bytes_written;
|
||||
GError *error = NULL;
|
||||
|
||||
buf = (gchar *)g_queue_pop_head (queue);
|
||||
|
||||
if (buf != NULL) {
|
||||
//将队列中的首元素写入到channel中
|
||||
if (g_io_channel_write_chars (channel, buf, -1, &bytes_written, &error) != G_IO_STATUS_NORMAL) {
|
||||
g_warning ("Could not write queue element \"%s\" to channel: %s", buf, error->message);
|
||||
g_error_free (error);
|
||||
}
|
||||
|
||||
/* Ensure passwords are cleared from memory */
|
||||
//清除内存中的passwords
|
||||
memset (buf, 0, strlen (buf));
|
||||
g_free (buf);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static gboolean is_string_complete (gchar *str, ...)
|
||||
{
|
||||
va_list ap;
|
||||
gchar *arg;
|
||||
|
||||
if (strlen (str) == 0) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
va_start (ap, str);
|
||||
|
||||
while ((arg = va_arg (ap, char *)) != NULL) {
|
||||
if (g_strrstr (str, arg) != NULL) {
|
||||
va_end (ap);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
va_end (ap);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean io_watch_stdout (GIOChannel *source, GIOCondition condition, PasswdHandler *passwd_handler)
|
||||
{
|
||||
static GString *str = NULL; /* Persistent buffer */
|
||||
|
||||
gchar buf[BUFSIZE]; /* Temporary buffer */
|
||||
gsize bytes_read;
|
||||
GError *gio_error = NULL; /* Error returned by functions */
|
||||
GError *error = NULL; /* Error sent to callbacks */
|
||||
|
||||
//GtkBuilder *dialog;
|
||||
|
||||
gboolean reinit = FALSE;
|
||||
|
||||
/* Initialize buffer */
|
||||
if (str == NULL) {
|
||||
str = g_string_new ("");
|
||||
}
|
||||
|
||||
//dialog = passwd_handler->ui;
|
||||
//buf将保存从channel中读取到的数据,bytes_read表示从buf中读取的数据长度
|
||||
if (g_io_channel_read_chars (source, buf, BUFSIZE, &bytes_read, &gio_error)
|
||||
!= G_IO_STATUS_NORMAL) {
|
||||
g_warning ("IO Channel read error: %s", gio_error->message);
|
||||
g_error_free (gio_error);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// g_warning("----------bytes_read=%d",bytes_read);
|
||||
// g_warning("----------io_watch_buf=%s-------",buf);
|
||||
|
||||
str = g_string_append_len (str, buf, bytes_read);
|
||||
|
||||
/* In which state is the backend? */
|
||||
switch (passwd_handler->backend_state) {
|
||||
case PASSWD_STATE_AUTH:
|
||||
/* Passwd is asking for our current password */
|
||||
|
||||
if (is_string_complete (str->str, "assword: ", "failure", "wrong", "error", NULL)) {
|
||||
|
||||
if (g_strrstr (str->str, "assword: ") != NULL) {
|
||||
/* Authentication successful */
|
||||
|
||||
passwd_handler->backend_state = PASSWD_STATE_NEW;
|
||||
|
||||
/* Trigger callback to update authentication status */
|
||||
if (passwd_handler->auth_cb)
|
||||
passwd_handler->auth_cb (passwd_handler,
|
||||
NULL,
|
||||
passwd_handler->auth_cb_data);
|
||||
|
||||
} else {
|
||||
/* Authentication failed */
|
||||
|
||||
error = g_error_new_literal (PASSWD_ERROR, PASSWD_ERROR_AUTH_FAILED,
|
||||
"Authentication failure!");
|
||||
|
||||
passwd_handler->changing_password = FALSE;
|
||||
|
||||
/* This error can happen both while authenticating or while changing password:
|
||||
* if chpasswd_cb is set, this means we're already changing password */
|
||||
if (passwd_handler->chpasswd_cb)
|
||||
passwd_handler->chpasswd_cb (passwd_handler,
|
||||
error,
|
||||
passwd_handler->auth_cb_data);
|
||||
else if (passwd_handler->auth_cb)
|
||||
passwd_handler->auth_cb (passwd_handler,
|
||||
error,
|
||||
passwd_handler->auth_cb_data);
|
||||
|
||||
g_error_free (error);
|
||||
}
|
||||
|
||||
reinit = TRUE;
|
||||
}
|
||||
break;
|
||||
case PASSWD_STATE_NEW:
|
||||
/* Passwd is asking for our new password */
|
||||
|
||||
if (is_string_complete (str->str, "assword: ", NULL)) {
|
||||
/* Advance to next state */
|
||||
passwd_handler->backend_state = PASSWD_STATE_RETYPE;
|
||||
|
||||
/* Pop retyped password from queue and into IO channel */
|
||||
io_queue_pop (passwd_handler->backend_stdin_queue, passwd_handler->backend_stdin);
|
||||
|
||||
reinit = TRUE;
|
||||
}
|
||||
break;
|
||||
case PASSWD_STATE_RETYPE:
|
||||
/* Passwd is asking for our retyped new password */
|
||||
|
||||
// if (is_string_complete (str->str,
|
||||
// "successfully",
|
||||
// "short",
|
||||
// "longer",
|
||||
// "palindrome",
|
||||
// "dictionary",
|
||||
// "simple",
|
||||
// "simplistic",
|
||||
// "similar",
|
||||
// "different",
|
||||
// "case",
|
||||
// "wrapped",
|
||||
// "recovered",
|
||||
// "recent",
|
||||
// "unchanged",
|
||||
// "match",
|
||||
// "1 numeric or special",
|
||||
// "failure",
|
||||
// "length",
|
||||
// NULL)) {
|
||||
if (TRUE){
|
||||
|
||||
if (g_strrstr (str->str, "successfully") != NULL) {
|
||||
/* Hooray! */
|
||||
|
||||
/* Trigger callback to update status */
|
||||
if (passwd_handler->chpasswd_cb)
|
||||
passwd_handler->chpasswd_cb (passwd_handler,
|
||||
NULL,
|
||||
passwd_handler->chpasswd_cb_data);
|
||||
}
|
||||
else {
|
||||
/* Ohnoes! */
|
||||
|
||||
if (g_strrstr (str->str, "recovered") != NULL) {
|
||||
/* What does this indicate?
|
||||
* "Authentication information cannot be recovered?" from libpam? */
|
||||
error = g_error_new_literal (PASSWD_ERROR, PASSWD_ERROR_UNKNOWN,
|
||||
str->str);
|
||||
} else if (g_strrstr (str->str, "short") != NULL ||
|
||||
g_strrstr (str->str, "longer") != NULL) {
|
||||
error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_REJECTED,
|
||||
"New password length is too short!");
|
||||
} else if (g_strrstr (str->str, "palindrome") != NULL ||
|
||||
g_strrstr (str->str, "simple") != NULL ||
|
||||
g_strrstr (str->str, "simplistic") != NULL ||
|
||||
g_strrstr (str->str, "dictionary") != NULL) {
|
||||
error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_REJECTED,
|
||||
"The new password is too simple!");
|
||||
} else if (g_strrstr (str->str, "similar") != NULL ||
|
||||
g_strrstr (str->str, "different") != NULL ||
|
||||
g_strrstr (str->str, "case") != NULL ||
|
||||
g_strrstr (str->str, "wrapped") != NULL) {
|
||||
error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_REJECTED,
|
||||
"The new password is too similar to the old one!");
|
||||
} else if (g_strrstr (str->str, "1 numeric or special") != NULL) {
|
||||
error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_REJECTED,
|
||||
"The new password must contain numbers or special characters!");
|
||||
} else if (g_strrstr (str->str, "unchanged") != NULL ||
|
||||
g_strrstr (str->str, "match") != NULL) {
|
||||
error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_REJECTED,
|
||||
"The new password is the same as the old one!");
|
||||
} else if (g_strrstr (str->str, "recent") != NULL) {
|
||||
error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_REJECTED,
|
||||
"The new password has been used recently!");
|
||||
} else if (g_strrstr (str->str, "failure") != NULL) {
|
||||
/* Authentication failure */
|
||||
error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_AUTH_FAILED,
|
||||
"Your password has been changed after you verify!");
|
||||
}
|
||||
else {
|
||||
error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_UNKNOWN,
|
||||
"Unknown error");
|
||||
}
|
||||
|
||||
/* At this point, passwd might have exited, in which case
|
||||
* child_watch_cb should clean up for us and remove this watcher.
|
||||
* On some error conditions though, passwd just re-prompts us
|
||||
* for our new password. */
|
||||
passwd_handler->backend_state = PASSWD_STATE_ERR;
|
||||
|
||||
passwd_handler->changing_password = FALSE;
|
||||
|
||||
/* Trigger callback to update status */
|
||||
if (passwd_handler->chpasswd_cb)
|
||||
passwd_handler->chpasswd_cb (passwd_handler,
|
||||
error,
|
||||
passwd_handler->chpasswd_cb_data);
|
||||
|
||||
g_error_free (error);
|
||||
|
||||
}
|
||||
|
||||
reinit = TRUE;
|
||||
|
||||
/* child_watch_cb should clean up for us now */
|
||||
}
|
||||
break;
|
||||
case PASSWD_STATE_NONE:
|
||||
/* Passwd is not asking for anything yet */
|
||||
if (is_string_complete (str->str, "assword: ", NULL)) {
|
||||
|
||||
/* If the user does not have a password set,
|
||||
* passwd will immediately ask for the new password,
|
||||
* so skip the AUTH phase */
|
||||
if (is_string_complete (str->str, "new", "New", NULL)) {
|
||||
gchar *pw;
|
||||
|
||||
passwd_handler->backend_state = PASSWD_STATE_NEW;
|
||||
|
||||
/* since passwd didn't ask for our old password
|
||||
* in this case, simply remove it from the queue */
|
||||
pw = (gchar *)g_queue_pop_head (passwd_handler->backend_stdin_queue);
|
||||
g_free (pw);
|
||||
|
||||
/* Pop the IO queue, i.e. send new password */
|
||||
io_queue_pop (passwd_handler->backend_stdin_queue, passwd_handler->backend_stdin);
|
||||
} else {
|
||||
|
||||
passwd_handler->backend_state = PASSWD_STATE_AUTH;
|
||||
|
||||
/* Pop the IO queue, i.e. send current password */
|
||||
io_queue_pop (passwd_handler->backend_stdin_queue, passwd_handler->backend_stdin);
|
||||
}
|
||||
|
||||
reinit = TRUE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* Passwd has returned an error */
|
||||
reinit = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (reinit) {
|
||||
g_string_free (str, TRUE);
|
||||
str = NULL;
|
||||
}
|
||||
|
||||
/* Continue calling us */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Child watcher */
|
||||
static void child_watch_cb (GPid pid, gint status, PasswdHandler *passwd_handler)
|
||||
{
|
||||
//子进程正常结束为非0
|
||||
if (WIFEXITED (status)) {
|
||||
//取得子进程正常退出时返回的结束代码
|
||||
if (WEXITSTATUS (status) >= 255) {
|
||||
g_warning ("Child exited unexpectedly");
|
||||
}
|
||||
}
|
||||
|
||||
free_passwd_resources (passwd_handler);
|
||||
}
|
||||
|
||||
static void stop_passwd (PasswdHandler *passwd_handler)
|
||||
{
|
||||
/* This is the standard way of returning from the dialog with passwd.
|
||||
* If we return this way we can safely kill passwd as it has completed
|
||||
* its task.
|
||||
*/
|
||||
|
||||
if (passwd_handler->backend_pid != -1) {
|
||||
kill (passwd_handler->backend_pid, 9);
|
||||
}
|
||||
|
||||
/* We must run free_passwd_resources here and not let our child
|
||||
* watcher do it, since it will access invalid memory after the
|
||||
* dialog has been closed and cleaned up.
|
||||
*
|
||||
* If we had more than a single thread we'd need to remove
|
||||
* the child watch before trying to kill the child.
|
||||
*/
|
||||
free_passwd_resources (passwd_handler);
|
||||
}
|
||||
|
||||
static gboolean spawn_passwd (PasswdHandler *passwd_handler, const char * user_name, GError **error)
|
||||
{
|
||||
gchar *argv[3];
|
||||
gchar *envp[1];
|
||||
gint my_stdin, my_stdout, my_stderr;
|
||||
|
||||
argv[0] = "/usr/bin/passwd"; /* Is it safe to rely on a hard-coded path? */
|
||||
argv[1] = g_strdup_printf ("%s", user_name);
|
||||
argv[2] = NULL;
|
||||
|
||||
// g_warning("spawn_passwd: %s %s", argv[0], argv[1]);
|
||||
|
||||
envp[0] = NULL; /* If we pass an empty array as the environment,
|
||||
* will the childs environment be empty, and the
|
||||
* locales set to the C default? From the manual:
|
||||
* "If envp is NULL, the child inherits its
|
||||
* parent'senvironment."
|
||||
* If I'm wrong here, we somehow have to set
|
||||
* the locales here.
|
||||
*/
|
||||
|
||||
//创建一个管道,进行通信,子进程执行passwd命令
|
||||
if (!g_spawn_async_with_pipes (NULL, /* Working directory */
|
||||
argv, /* Argument vector */
|
||||
envp, /* Environment */
|
||||
G_SPAWN_DO_NOT_REAP_CHILD, /* Flags */
|
||||
NULL, /* Child setup (在子进程调用exec()之前,该函数会被调用)*/
|
||||
NULL, /* Data to child setup */
|
||||
&passwd_handler->backend_pid, /* PID */
|
||||
&my_stdin, /* Stdin */
|
||||
&my_stdout, /* Stdout */
|
||||
&my_stderr, /* Stderr */
|
||||
error)) { /* GError */
|
||||
|
||||
/* An error occured */
|
||||
free_passwd_resources (passwd_handler);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* 2>&1 */
|
||||
//复制文件描述符,也就是将stderr重定向到stdout
|
||||
if (dup2 (my_stderr, my_stdout) == -1) {
|
||||
/* Failed! */
|
||||
g_set_error_literal (error,
|
||||
PASSWD_ERROR,
|
||||
PASSWD_ERROR_BACKEND,
|
||||
strerror (errno));
|
||||
|
||||
/* Clean up */
|
||||
stop_passwd (passwd_handler);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Open IO Channels */
|
||||
//指定一个文件描述符,创建一个IO Channel,默认使用UTF-8编码格式
|
||||
passwd_handler->backend_stdin = g_io_channel_unix_new (my_stdin);
|
||||
passwd_handler->backend_stdout = g_io_channel_unix_new (my_stdout);
|
||||
|
||||
/* Set raw encoding */
|
||||
/* Set nonblocking mode */
|
||||
//设置通道的编码方式为NULL,设置为非阻塞的方式
|
||||
if (g_io_channel_set_encoding (passwd_handler->backend_stdin, NULL, error) != G_IO_STATUS_NORMAL ||
|
||||
g_io_channel_set_encoding (passwd_handler->backend_stdout, NULL, error) != G_IO_STATUS_NORMAL ||
|
||||
g_io_channel_set_flags (passwd_handler->backend_stdin, G_IO_FLAG_NONBLOCK, error) != G_IO_STATUS_NORMAL ||
|
||||
g_io_channel_set_flags (passwd_handler->backend_stdout, G_IO_FLAG_NONBLOCK, error) != G_IO_STATUS_NORMAL ) {
|
||||
|
||||
/* Clean up */
|
||||
stop_passwd (passwd_handler);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Turn off buffering */
|
||||
//只有通道的编码方式为NULL,才能设置缓冲状态为FASLE,其他任何编码,通道必须被缓冲,这里是为了清掉上次的密码
|
||||
g_io_channel_set_buffered (passwd_handler->backend_stdin, FALSE);
|
||||
g_io_channel_set_buffered (passwd_handler->backend_stdout, FALSE);
|
||||
|
||||
/* Add IO Channel watcher */
|
||||
//当IO通道的状态为G_IO_IN(从IO通道读数据时)或者G_IO_PRI(读紧急数据时)时,调用io_watch_stdout
|
||||
passwd_handler->backend_stdout_watch_id = g_io_add_watch (passwd_handler->backend_stdout,
|
||||
G_IO_IN /*| G_IO_PRI*/,
|
||||
(GIOFunc) io_watch_stdout, passwd_handler);
|
||||
|
||||
/* Add child watcher */
|
||||
//在指定pid的进程退出时,调用child_watch_cb(),进行错误检查,以及资源回收
|
||||
passwd_handler->backend_child_watch_id = g_child_watch_add (passwd_handler->backend_pid, (GChildWatchFunc) child_watch_cb, passwd_handler);
|
||||
|
||||
/* Success! */
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void update_password (PasswdHandler *passwd_handler)
|
||||
{
|
||||
gchar *s;
|
||||
|
||||
s = g_strdup_printf ("%s\n", passwd_handler->new_password);
|
||||
|
||||
g_queue_push_tail (passwd_handler->backend_stdin_queue, s);
|
||||
/* We need to allocate new space because io_queue_pop() g_free()s
|
||||
* every element of the queue after it's done */
|
||||
g_queue_push_tail (passwd_handler->backend_stdin_queue, g_strdup (s));
|
||||
}
|
||||
|
||||
gboolean passwd_change_password (PasswdHandler *passwd_handler, const char *user_name,
|
||||
const char *new_password,
|
||||
PasswdCallback cb,
|
||||
const gpointer user_data)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
passwd_handler->changing_password = TRUE;
|
||||
|
||||
passwd_handler->new_password = new_password;
|
||||
passwd_handler->chpasswd_cb = cb;
|
||||
passwd_handler->chpasswd_cb_data = user_data;
|
||||
|
||||
/* Stop passwd if an error occured and it is still running */
|
||||
if (passwd_handler->backend_state == PASSWD_STATE_ERR) {
|
||||
|
||||
/* Stop passwd, free resources */
|
||||
stop_passwd (passwd_handler);
|
||||
}
|
||||
|
||||
/* Check that the backend is still running, or that an error
|
||||
* has occured but it has not yet exited */
|
||||
|
||||
g_warning("passwd pid is: %d", passwd_handler->backend_pid);
|
||||
if (passwd_handler->backend_pid == -1) {
|
||||
/* If it is not, re-run authentication */
|
||||
|
||||
/* Spawn backend */
|
||||
stop_passwd (passwd_handler);
|
||||
|
||||
if (!spawn_passwd (passwd_handler, user_name, &error)) {
|
||||
g_error_free (error);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_warning("------------1----------------------");
|
||||
|
||||
/* Add current and new passwords to queue */
|
||||
//将当前的密码和新密码入队,新密码会入队两次
|
||||
authenticate (passwd_handler);
|
||||
update_password (passwd_handler);
|
||||
} else {
|
||||
g_warning("-----2-----------");
|
||||
/* Only add new passwords to queue */
|
||||
update_password (passwd_handler);
|
||||
}
|
||||
|
||||
/* Pop new password through the backend. If user has no password, popping the queue
|
||||
would output current password, while 'passwd' is waiting for the new one. So wait
|
||||
for io_watch_stdout() to remove current password from the queue, and output
|
||||
the new one for us.*/
|
||||
//如果密码为空,将新进队列的密码,作为current_passwd弹出
|
||||
if (passwd_handler->current_password)
|
||||
{
|
||||
io_queue_pop (passwd_handler->backend_stdin_queue, passwd_handler->backend_stdin);
|
||||
}
|
||||
|
||||
/* Our IO watcher should now handle the rest */
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//void passwd_authenticate (PasswdHandler *passwd_handler,
|
||||
// const char *current_password,
|
||||
// PasswdCallback cb,
|
||||
// const gpointer user_data)
|
||||
//{
|
||||
// GError *error = NULL;
|
||||
|
||||
// /* Don't stop if we've already started chaging password */
|
||||
// if (passwd_handler->changing_password)
|
||||
// return;
|
||||
|
||||
// /* Clear data from possible previous attempts to change password */
|
||||
// passwd_handler->new_password = NULL;
|
||||
// passwd_handler->chpasswd_cb = NULL;
|
||||
// passwd_handler->chpasswd_cb_data = NULL;
|
||||
// g_queue_foreach (passwd_handler->backend_stdin_queue, (GFunc) g_free, NULL);
|
||||
// g_queue_clear (passwd_handler->backend_stdin_queue);
|
||||
|
||||
// passwd_handler->current_password = current_password;
|
||||
// passwd_handler->auth_cb = cb;
|
||||
// passwd_handler->auth_cb_data = user_data;
|
||||
|
||||
// /* Spawn backend */
|
||||
// //重新启动后台passwd
|
||||
// stop_passwd (passwd_handler);
|
||||
|
||||
// if (!spawn_passwd (passwd_handler, &error)) {
|
||||
// g_warning ("%s", error->message);
|
||||
// g_error_free (error);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// //将current passwd从尾部插入队列
|
||||
// authenticate (passwd_handler);
|
||||
|
||||
// /* Our IO watcher should now handle the rest */
|
||||
//}
|
||||
|
||||
|
||||
PasswdHandler * passwd_init ()
|
||||
{
|
||||
PasswdHandler *passwd_handler;
|
||||
|
||||
passwd_handler = g_new0 (PasswdHandler, 1);
|
||||
|
||||
/* Initialize backend_pid. -1 means the backend is not running */
|
||||
//-1代表后台还没启动
|
||||
passwd_handler->backend_pid = -1;
|
||||
|
||||
/* Initialize IO Channels */
|
||||
passwd_handler->backend_stdin = NULL;
|
||||
passwd_handler->backend_stdout = NULL;
|
||||
|
||||
/* Initialize write queue */
|
||||
passwd_handler->backend_stdin_queue = g_queue_new ();
|
||||
|
||||
/* Initialize watchers */
|
||||
passwd_handler->backend_child_watch_id = 0;
|
||||
passwd_handler->backend_stdout_watch_id = 0;
|
||||
|
||||
/* Initialize backend state */
|
||||
passwd_handler->backend_state = PASSWD_STATE_NONE;
|
||||
passwd_handler->changing_password = FALSE;
|
||||
|
||||
return passwd_handler;
|
||||
}
|
||||
|
||||
|
||||
void passwd_destroy (PasswdHandler *passwd_handler)
|
||||
{
|
||||
g_queue_free (passwd_handler->backend_stdin_queue);
|
||||
stop_passwd (passwd_handler);
|
||||
g_free (passwd_handler);
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
#ifndef RUNPASSWD_H
|
||||
#define RUNPASSWD_H
|
||||
|
||||
|
||||
struct PasswdHandler;
|
||||
|
||||
typedef struct PasswdHandler PasswdHandler;
|
||||
|
||||
typedef void (*PasswdCallback) (PasswdHandler * passwd_handler, GError * error, const gpointer user_data);
|
||||
|
||||
/* Error codes */
|
||||
typedef enum {
|
||||
PASSWD_ERROR_REJECTED, /* New password is not secure enough */
|
||||
PASSWD_ERROR_AUTH_FAILED, /* Wrong old password, or PAM failure */
|
||||
PASSWD_ERROR_REAUTH_FAILED, /* Password has changed since first authentication */
|
||||
PASSWD_ERROR_BACKEND, /* Backend error */
|
||||
PASSWD_ERROR_UNKNOWN /* General error */
|
||||
} PasswdError;
|
||||
|
||||
PasswdHandler *passwd_init ();
|
||||
|
||||
void passwd_destroy (PasswdHandler *passwd_handler);
|
||||
|
||||
void passwd_authenticate (PasswdHandler *passwd_handler,
|
||||
const char *current_password,
|
||||
PasswdCallback cb,
|
||||
gpointer user_data);
|
||||
|
||||
gboolean passwd_change_password (PasswdHandler *passwd_handler,
|
||||
const char *user_name,
|
||||
const char *new_password,
|
||||
PasswdCallback cb,
|
||||
const gpointer user_data);
|
||||
|
||||
#endif // RUNPASSWD_H
|
|
@ -0,0 +1,38 @@
|
|||
QT -= gui
|
||||
|
||||
TARGET = changeuserpwd
|
||||
TEMPLATE = app
|
||||
|
||||
CONFIG += c++11
|
||||
CONFIG -= app_bundle
|
||||
|
||||
# The following define makes your compiler emit warnings if you use
|
||||
# any Qt feature that has been marked deprecated (the exact warnings
|
||||
# depend on your compiler). Please consult the documentation of the
|
||||
# deprecated API in order to know how to port your code away from it.
|
||||
DEFINES += QT_DEPRECATED_WARNINGS
|
||||
|
||||
|
||||
##加载gio库和gio-unix库
|
||||
CONFIG += link_pkgconfig \
|
||||
C++11
|
||||
PKGCONFIG += gio-2.0 \
|
||||
gio-unix-2.0 \
|
||||
|
||||
# You can also make your code fail to compile if it uses deprecated APIs.
|
||||
# In order to do so, uncomment the following line.
|
||||
# You can also select to disable deprecated APIs only up to a certain version of Qt.
|
||||
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||
|
||||
target.source += $$TARGET
|
||||
target.path = /usr/bin
|
||||
INSTALLS += \
|
||||
target \
|
||||
|
||||
|
||||
SOURCES += \
|
||||
main.cpp \
|
||||
run-passwd.cpp
|
||||
|
||||
HEADERS += \
|
||||
run-passwd.h
|
|
@ -0,0 +1,81 @@
|
|||
#include <QCoreApplication>
|
||||
|
||||
#include <glib.h>
|
||||
#include "run-passwd.h"
|
||||
|
||||
PasswdHandler *passwd_handler = NULL;
|
||||
|
||||
static void auth_cb (PasswdHandler *passwd_handler, GError *error, gpointer user_data);
|
||||
static void chpasswd_cb (PasswdHandler *passwd_handler, GError *error, gpointer user_data);
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
if (argc != 3)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
QCoreApplication a(argc, argv);
|
||||
|
||||
passwd_handler = passwd_init ();
|
||||
|
||||
passwd_authenticate (passwd_handler, argv[1], auth_cb, argv[2]);
|
||||
|
||||
return a.exec();
|
||||
}
|
||||
|
||||
static void
|
||||
auth_cb (PasswdHandler *passwd_handler,
|
||||
GError *error,
|
||||
gpointer user_data)
|
||||
{
|
||||
char *secondary_text;
|
||||
char * pwd = (char*) user_data;
|
||||
|
||||
if (error){
|
||||
secondary_text = error->message;
|
||||
printf("%s\n", secondary_text);
|
||||
qApp->exit(1);
|
||||
} else {
|
||||
passwd_change_password (passwd_handler, pwd, chpasswd_cb, NULL);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief chpasswd_cb
|
||||
* @param passwd_handler
|
||||
* @param error
|
||||
* @param user_data
|
||||
*/
|
||||
static void
|
||||
chpasswd_cb (PasswdHandler *passwd_handler,
|
||||
GError *error,
|
||||
gpointer user_data)
|
||||
{
|
||||
// char *primary_text;
|
||||
char *secondary_text;
|
||||
|
||||
if (!error) {
|
||||
//finish_password_change (TRUE);
|
||||
// primary_text = "Success";
|
||||
secondary_text = "";
|
||||
|
||||
printf("%s\n", secondary_text);
|
||||
|
||||
qApp->exit(0);
|
||||
} else {
|
||||
// primary_text = "Failed";
|
||||
secondary_text = error->message;
|
||||
|
||||
char ** lines = g_strsplit(secondary_text, "\n", -1);
|
||||
|
||||
printf("%s\n", lines[0]);
|
||||
|
||||
passwd_destroy (passwd_handler);
|
||||
|
||||
qApp->exit(1);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,704 @@
|
|||
|
||||
|
||||
/* qt会将glib里的signals成员识别为宏,所以取消该宏
|
||||
* 后面如果用到signals时,使用Q_SIGNALS代替即可
|
||||
**/
|
||||
#ifdef signals
|
||||
#undef signals
|
||||
#endif
|
||||
|
||||
#include <glib.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "run-passwd.h"
|
||||
|
||||
|
||||
/* Buffer size for backend output */
|
||||
#define BUFSIZE 64
|
||||
|
||||
/* Passwd states */
|
||||
//后端passwd的状态,NONE应该是passwd还没有启动,ERROR表示报错但还没退出
|
||||
typedef enum {
|
||||
PASSWD_STATE_NONE, /* Passwd is not asking for anything */
|
||||
PASSWD_STATE_AUTH, /* Passwd is asking for our current password */
|
||||
PASSWD_STATE_NEW, /* Passwd is asking for our new password */
|
||||
PASSWD_STATE_RETYPE, /* Passwd is asking for our retyped new password */
|
||||
PASSWD_STATE_ERR /* Passwd reported an error but has not yet exited */
|
||||
} PasswdState;
|
||||
|
||||
struct PasswdHandler {
|
||||
// GtkBuilder *ui;
|
||||
|
||||
const char *current_password;
|
||||
const char *new_password;
|
||||
const char *retyped_password;
|
||||
|
||||
/* Communication with the passwd program */
|
||||
GPid backend_pid;
|
||||
|
||||
GIOChannel *backend_stdin;
|
||||
GIOChannel *backend_stdout;
|
||||
|
||||
GQueue *backend_stdin_queue; /* Write queue to backend_stdin */
|
||||
|
||||
/* GMainLoop IDs */
|
||||
guint backend_child_watch_id; /* g_child_watch_add (PID) */
|
||||
guint backend_stdout_watch_id; /* g_io_add_watch (stdout) */
|
||||
|
||||
/* State of the passwd program */
|
||||
PasswdState backend_state;
|
||||
gboolean changing_password;
|
||||
|
||||
PasswdCallback auth_cb;
|
||||
gpointer auth_cb_data;
|
||||
|
||||
PasswdCallback chpasswd_cb;
|
||||
gpointer chpasswd_cb_data;
|
||||
};
|
||||
|
||||
//GQuark是一个guint32
|
||||
static GQuark
|
||||
passwd_error_quark (void)
|
||||
{
|
||||
static GQuark q = 0;
|
||||
|
||||
//返回错误的标识码
|
||||
if (q == 0) {
|
||||
q = g_quark_from_static_string("passwd_error");
|
||||
}
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
/* Error handling */
|
||||
#define PASSWD_ERROR (passwd_error_quark ())
|
||||
|
||||
static void stop_passwd (PasswdHandler *passwd_handler);
|
||||
|
||||
static void free_passwd_resources (PasswdHandler *passwd_handler);
|
||||
|
||||
static gboolean io_watch_stdout (GIOChannel *source, GIOCondition condition, PasswdHandler *passwd_handler);
|
||||
|
||||
|
||||
static void free_passwd_resources (PasswdHandler *passwd_handler)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
/* Remove the child watcher */
|
||||
if (passwd_handler->backend_child_watch_id != 0) {
|
||||
|
||||
g_source_remove (passwd_handler->backend_child_watch_id);
|
||||
|
||||
passwd_handler->backend_child_watch_id = 0;
|
||||
}
|
||||
|
||||
|
||||
/* Close IO channels (internal file descriptors are automatically closed) */
|
||||
if (passwd_handler->backend_stdin != NULL) {
|
||||
|
||||
if (g_io_channel_shutdown (passwd_handler->backend_stdin, TRUE, &error) != G_IO_STATUS_NORMAL) {
|
||||
g_warning ("Could not shutdown backend_stdin IO channel: %s", error->message);
|
||||
g_error_free (error);
|
||||
error = NULL;
|
||||
}
|
||||
|
||||
g_io_channel_unref (passwd_handler->backend_stdin);
|
||||
passwd_handler->backend_stdin = NULL;
|
||||
}
|
||||
|
||||
if (passwd_handler->backend_stdout != NULL) {
|
||||
|
||||
if (g_io_channel_shutdown (passwd_handler->backend_stdout, TRUE, &error) != G_IO_STATUS_NORMAL) {
|
||||
g_warning ("Could not shutdown backend_stdout IO channel: %s", error->message);
|
||||
g_error_free (error);
|
||||
error = NULL;
|
||||
}
|
||||
|
||||
g_io_channel_unref (passwd_handler->backend_stdout);
|
||||
|
||||
passwd_handler->backend_stdout = NULL;
|
||||
}
|
||||
|
||||
/* Remove IO watcher */
|
||||
if (passwd_handler->backend_stdout_watch_id != 0) {
|
||||
|
||||
g_source_remove (passwd_handler->backend_stdout_watch_id);
|
||||
|
||||
passwd_handler->backend_stdout_watch_id = 0;
|
||||
}
|
||||
|
||||
/* Close PID */
|
||||
//因为flag为G_SPAWN_DO_NOT_REAP_CHILD,所以child不会自动的被reap掉,需要在子进程上free
|
||||
if (passwd_handler->backend_pid != -1) {
|
||||
|
||||
g_spawn_close_pid (passwd_handler->backend_pid);
|
||||
|
||||
passwd_handler->backend_pid = -1;
|
||||
}
|
||||
|
||||
/* Clear backend state */
|
||||
passwd_handler->backend_state = PASSWD_STATE_NONE;
|
||||
}
|
||||
|
||||
static void authenticate (PasswdHandler *passwd_handler)
|
||||
{
|
||||
gchar *s;
|
||||
|
||||
s = g_strdup_printf ("%s\n", passwd_handler->current_password);
|
||||
g_queue_push_tail (passwd_handler->backend_stdin_queue, s);
|
||||
}
|
||||
|
||||
static void io_queue_pop (GQueue *queue, GIOChannel *channel)
|
||||
{
|
||||
gchar *buf;
|
||||
gsize bytes_written;
|
||||
GError *error = NULL;
|
||||
|
||||
buf = (gchar *)g_queue_pop_head (queue);
|
||||
|
||||
if (buf != NULL) {
|
||||
//将队列中的首元素写入到channel中
|
||||
if (g_io_channel_write_chars (channel, buf, -1, &bytes_written, &error) != G_IO_STATUS_NORMAL) {
|
||||
g_warning ("Could not write queue element \"%s\" to channel: %s", buf, error->message);
|
||||
g_error_free (error);
|
||||
}
|
||||
|
||||
/* Ensure passwords are cleared from memory */
|
||||
//清除内存中的passwords
|
||||
memset (buf, 0, strlen (buf));
|
||||
g_free (buf);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static gboolean is_string_complete (gchar *str, ...)
|
||||
{
|
||||
va_list ap;
|
||||
gchar *arg;
|
||||
|
||||
if (strlen (str) == 0) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
va_start (ap, str);
|
||||
|
||||
while ((arg = va_arg (ap, char *)) != NULL) {
|
||||
if (g_strrstr (str, arg) != NULL) {
|
||||
va_end (ap);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
va_end (ap);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean io_watch_stdout (GIOChannel *source, GIOCondition condition, PasswdHandler *passwd_handler)
|
||||
{
|
||||
static GString *str = NULL; /* Persistent buffer */
|
||||
|
||||
gchar buf[BUFSIZE]; /* Temporary buffer */
|
||||
gsize bytes_read;
|
||||
GError *gio_error = NULL; /* Error returned by functions */
|
||||
GError *error = NULL; /* Error sent to callbacks */
|
||||
|
||||
//GtkBuilder *dialog;
|
||||
|
||||
gboolean reinit = FALSE;
|
||||
|
||||
/* Initialize buffer */
|
||||
if (str == NULL) {
|
||||
str = g_string_new ("");
|
||||
}
|
||||
|
||||
//dialog = passwd_handler->ui;
|
||||
//buf将保存从channel中读取到的数据,bytes_read表示从buf中读取的数据长度
|
||||
if (g_io_channel_read_chars (source, buf, BUFSIZE, &bytes_read, &gio_error)
|
||||
!= G_IO_STATUS_NORMAL) {
|
||||
g_warning ("IO Channel read error: %s", gio_error->message);
|
||||
g_error_free (gio_error);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// g_warning("----------bytes_read=%d",bytes_read);
|
||||
// g_warning("----------io_watch_buf=%s-------",buf);
|
||||
|
||||
str = g_string_append_len (str, buf, bytes_read);
|
||||
|
||||
/* In which state is the backend? */
|
||||
switch (passwd_handler->backend_state) {
|
||||
case PASSWD_STATE_AUTH:
|
||||
/* Passwd is asking for our current password */
|
||||
|
||||
if (is_string_complete (str->str, "assword: ", "failure", "wrong", "error", NULL)) {
|
||||
|
||||
if (g_strrstr (str->str, "New password: ") != NULL) {
|
||||
/* Authentication successful */
|
||||
|
||||
passwd_handler->backend_state = PASSWD_STATE_NEW;
|
||||
|
||||
/* Trigger callback to update authentication status */
|
||||
if (passwd_handler->auth_cb)
|
||||
passwd_handler->auth_cb (passwd_handler,
|
||||
NULL,
|
||||
passwd_handler->auth_cb_data);
|
||||
|
||||
} else {
|
||||
/* Authentication failed */
|
||||
|
||||
error = g_error_new_literal (PASSWD_ERROR, PASSWD_ERROR_AUTH_FAILED,
|
||||
"Authentication token manipulation error!");
|
||||
|
||||
passwd_handler->changing_password = FALSE;
|
||||
|
||||
/* This error can happen both while authenticating or while changing password:
|
||||
* if chpasswd_cb is set, this means we're already changing password */
|
||||
if (passwd_handler->chpasswd_cb)
|
||||
passwd_handler->chpasswd_cb (passwd_handler,
|
||||
error,
|
||||
passwd_handler->auth_cb_data);
|
||||
else if (passwd_handler->auth_cb)
|
||||
passwd_handler->auth_cb (passwd_handler,
|
||||
error,
|
||||
passwd_handler->auth_cb_data);
|
||||
|
||||
g_error_free (error);
|
||||
}
|
||||
|
||||
reinit = TRUE;
|
||||
}
|
||||
break;
|
||||
case PASSWD_STATE_NEW:
|
||||
/* Passwd is asking for our new password */
|
||||
|
||||
if (is_string_complete (str->str, "assword: ", NULL)) {
|
||||
/* Advance to next state */
|
||||
passwd_handler->backend_state = PASSWD_STATE_RETYPE;
|
||||
|
||||
/* Pop retyped password from queue and into IO channel */
|
||||
io_queue_pop (passwd_handler->backend_stdin_queue, passwd_handler->backend_stdin);
|
||||
|
||||
reinit = TRUE;
|
||||
}
|
||||
break;
|
||||
case PASSWD_STATE_RETYPE:
|
||||
/* Passwd is asking for our retyped new password */
|
||||
|
||||
// if (is_string_complete (str->str,
|
||||
// "successfully",
|
||||
// "short",
|
||||
// "longer",
|
||||
// "palindrome",
|
||||
// "dictionary",
|
||||
// "simple",
|
||||
// "simplistic",
|
||||
// "similar",
|
||||
// "different",
|
||||
// "case",
|
||||
// "wrapped",
|
||||
// "recovered",
|
||||
// "recent",
|
||||
// "unchanged",
|
||||
// "match",
|
||||
// "1 numeric or special",
|
||||
// "failure",
|
||||
// "length",
|
||||
// NULL)) {
|
||||
if (TRUE){
|
||||
|
||||
if (g_strrstr (str->str, "successfully") != NULL) {
|
||||
/* Hooray! */
|
||||
|
||||
/* Trigger callback to update status */
|
||||
if (passwd_handler->chpasswd_cb)
|
||||
passwd_handler->chpasswd_cb (passwd_handler,
|
||||
NULL,
|
||||
passwd_handler->chpasswd_cb_data);
|
||||
}
|
||||
else {
|
||||
/* Ohnoes! */
|
||||
|
||||
if (g_strrstr (str->str, "recovered") != NULL) {
|
||||
/* What does this indicate?
|
||||
* "Authentication information cannot be recovered?" from libpam? */
|
||||
error = g_error_new_literal (PASSWD_ERROR, PASSWD_ERROR_UNKNOWN,
|
||||
str->str);
|
||||
}/* else if (g_strrstr (str->str, "short") != NULL ||
|
||||
g_strrstr (str->str, "longer") != NULL) {
|
||||
error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_REJECTED,
|
||||
"New password length is too short!");
|
||||
} else if (g_strrstr (str->str, "palindrome") != NULL ||
|
||||
g_strrstr (str->str, "simple") != NULL ||
|
||||
g_strrstr (str->str, "simplistic") != NULL ||
|
||||
g_strrstr (str->str, "dictionary") != NULL) {
|
||||
error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_REJECTED,
|
||||
"The new password is too simple!");
|
||||
} else if (g_strrstr (str->str, "similar") != NULL ||
|
||||
g_strrstr (str->str, "different") != NULL ||
|
||||
g_strrstr (str->str, "case") != NULL ||
|
||||
g_strrstr (str->str, "wrapped") != NULL) {
|
||||
error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_REJECTED,
|
||||
"The new password is too similar to the old one!");
|
||||
} else if (g_strrstr (str->str, "1 numeric or special") != NULL) {
|
||||
error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_REJECTED,
|
||||
"The new password must contain numbers or special characters!");
|
||||
} else if (g_strrstr (str->str, "unchanged") != NULL ||
|
||||
g_strrstr (str->str, "match") != NULL) {
|
||||
error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_REJECTED,
|
||||
"The new password is the same as the old one!");
|
||||
} else if (g_strrstr (str->str, "recent") != NULL) {
|
||||
error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_REJECTED,
|
||||
"The new password has been used recently!");
|
||||
} else if (g_strrstr (str->str, "failure") != NULL) {
|
||||
//Authentication failure
|
||||
error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_AUTH_FAILED,
|
||||
"Your password has been changed after you verify!");
|
||||
} */else {
|
||||
error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_UNKNOWN,
|
||||
str->str);
|
||||
}
|
||||
|
||||
/* At this point, passwd might have exited, in which case
|
||||
* child_watch_cb should clean up for us and remove this watcher.
|
||||
* On some error conditions though, passwd just re-prompts us
|
||||
* for our new password. */
|
||||
passwd_handler->backend_state = PASSWD_STATE_ERR;
|
||||
|
||||
passwd_handler->changing_password = FALSE;
|
||||
|
||||
/* Trigger callback to update status */
|
||||
if (passwd_handler->chpasswd_cb)
|
||||
passwd_handler->chpasswd_cb (passwd_handler,
|
||||
error,
|
||||
passwd_handler->chpasswd_cb_data);
|
||||
|
||||
g_error_free (error);
|
||||
|
||||
}
|
||||
|
||||
reinit = TRUE;
|
||||
|
||||
/* child_watch_cb should clean up for us now */
|
||||
}
|
||||
break;
|
||||
case PASSWD_STATE_NONE:
|
||||
/* Passwd is not asking for anything yet */
|
||||
if (is_string_complete (str->str, "assword: ", NULL)) {
|
||||
|
||||
/* If the user does not have a password set,
|
||||
* passwd will immediately ask for the new password,
|
||||
* so skip the AUTH phase */
|
||||
if (is_string_complete (str->str, "new", "New", NULL)) {
|
||||
gchar *pw;
|
||||
|
||||
passwd_handler->backend_state = PASSWD_STATE_NEW;
|
||||
|
||||
/* since passwd didn't ask for our old password
|
||||
* in this case, simply remove it from the queue */
|
||||
pw = (gchar *)g_queue_pop_head (passwd_handler->backend_stdin_queue);
|
||||
g_free (pw);
|
||||
|
||||
/* Pop the IO queue, i.e. send new password */
|
||||
io_queue_pop (passwd_handler->backend_stdin_queue, passwd_handler->backend_stdin);
|
||||
} else {
|
||||
|
||||
passwd_handler->backend_state = PASSWD_STATE_AUTH;
|
||||
|
||||
/* Pop the IO queue, i.e. send current password */
|
||||
io_queue_pop (passwd_handler->backend_stdin_queue, passwd_handler->backend_stdin);
|
||||
}
|
||||
|
||||
reinit = TRUE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* Passwd has returned an error */
|
||||
reinit = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (reinit) {
|
||||
g_string_free (str, TRUE);
|
||||
str = NULL;
|
||||
}
|
||||
|
||||
/* Continue calling us */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Child watcher */
|
||||
static void child_watch_cb (GPid pid, gint status, PasswdHandler *passwd_handler)
|
||||
{
|
||||
//子进程正常结束为非0
|
||||
if (WIFEXITED (status)) {
|
||||
//取得子进程正常退出时返回的结束代码
|
||||
if (WEXITSTATUS (status) >= 255) {
|
||||
g_warning ("Child exited unexpectedly");
|
||||
}
|
||||
}
|
||||
|
||||
free_passwd_resources (passwd_handler);
|
||||
}
|
||||
|
||||
static void stop_passwd (PasswdHandler *passwd_handler)
|
||||
{
|
||||
/* This is the standard way of returning from the dialog with passwd.
|
||||
* If we return this way we can safely kill passwd as it has completed
|
||||
* its task.
|
||||
*/
|
||||
|
||||
if (passwd_handler->backend_pid != -1) {
|
||||
kill (passwd_handler->backend_pid, 9);
|
||||
}
|
||||
|
||||
/* We must run free_passwd_resources here and not let our child
|
||||
* watcher do it, since it will access invalid memory after the
|
||||
* dialog has been closed and cleaned up.
|
||||
*
|
||||
* If we had more than a single thread we'd need to remove
|
||||
* the child watch before trying to kill the child.
|
||||
*/
|
||||
free_passwd_resources (passwd_handler);
|
||||
}
|
||||
|
||||
static gboolean spawn_passwd (PasswdHandler *passwd_handler, GError **error)
|
||||
{
|
||||
gchar *argv[2];
|
||||
gchar *envp[1];
|
||||
gint my_stdin, my_stdout, my_stderr;
|
||||
|
||||
argv[0] = "/usr/bin/passwd"; /* Is it safe to rely on a hard-coded path? */
|
||||
argv[1] = NULL;
|
||||
|
||||
envp[0] = NULL; /* If we pass an empty array as the environment,
|
||||
* will the childs environment be empty, and the
|
||||
* locales set to the C default? From the manual:
|
||||
* "If envp is NULL, the child inherits its
|
||||
* parent'senvironment."
|
||||
* If I'm wrong here, we somehow have to set
|
||||
* the locales here.
|
||||
*/
|
||||
|
||||
//创建一个管道,进行通信,子进程执行passwd命令
|
||||
if (!g_spawn_async_with_pipes (NULL, /* Working directory */
|
||||
argv, /* Argument vector */
|
||||
envp, /* Environment */
|
||||
G_SPAWN_DO_NOT_REAP_CHILD, /* Flags */
|
||||
NULL, /* Child setup (在子进程调用exec()之前,该函数会被调用)*/
|
||||
NULL, /* Data to child setup */
|
||||
&passwd_handler->backend_pid, /* PID */
|
||||
&my_stdin, /* Stdin */
|
||||
&my_stdout, /* Stdout */
|
||||
&my_stderr, /* Stderr */
|
||||
error)) { /* GError */
|
||||
|
||||
/* An error occured */
|
||||
free_passwd_resources (passwd_handler);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* 2>&1 */
|
||||
//复制文件描述符,也就是将stderr重定向到stdout
|
||||
if (dup2 (my_stderr, my_stdout) == -1) {
|
||||
/* Failed! */
|
||||
g_set_error_literal (error,
|
||||
PASSWD_ERROR,
|
||||
PASSWD_ERROR_BACKEND,
|
||||
strerror (errno));
|
||||
|
||||
/* Clean up */
|
||||
stop_passwd (passwd_handler);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Open IO Channels */
|
||||
//指定一个文件描述符,创建一个IO Channel,默认使用UTF-8编码格式
|
||||
passwd_handler->backend_stdin = g_io_channel_unix_new (my_stdin);
|
||||
passwd_handler->backend_stdout = g_io_channel_unix_new (my_stdout);
|
||||
|
||||
/* Set raw encoding */
|
||||
/* Set nonblocking mode */
|
||||
//设置通道的编码方式为NULL,设置为非阻塞的方式
|
||||
if (g_io_channel_set_encoding (passwd_handler->backend_stdin, NULL, error) != G_IO_STATUS_NORMAL ||
|
||||
g_io_channel_set_encoding (passwd_handler->backend_stdout, NULL, error) != G_IO_STATUS_NORMAL ||
|
||||
g_io_channel_set_flags (passwd_handler->backend_stdin, G_IO_FLAG_NONBLOCK, error) != G_IO_STATUS_NORMAL ||
|
||||
g_io_channel_set_flags (passwd_handler->backend_stdout, G_IO_FLAG_NONBLOCK, error) != G_IO_STATUS_NORMAL ) {
|
||||
|
||||
/* Clean up */
|
||||
stop_passwd (passwd_handler);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Turn off buffering */
|
||||
//只有通道的编码方式为NULL,才能设置缓冲状态为FASLE,其他任何编码,通道必须被缓冲,这里是为了清掉上次的密码
|
||||
g_io_channel_set_buffered (passwd_handler->backend_stdin, FALSE);
|
||||
g_io_channel_set_buffered (passwd_handler->backend_stdout, FALSE);
|
||||
|
||||
/* Add IO Channel watcher */
|
||||
//当IO通道的状态为G_IO_IN(从IO通道读数据时)或者G_IO_PRI(读紧急数据时)时,调用io_watch_stdout
|
||||
passwd_handler->backend_stdout_watch_id = g_io_add_watch (passwd_handler->backend_stdout,
|
||||
G_IO_IN /*| G_IO_PRI*/,
|
||||
(GIOFunc) io_watch_stdout, passwd_handler);
|
||||
|
||||
/* Add child watcher */
|
||||
//在指定pid的进程退出时,调用child_watch_cb(),进行错误检查,以及资源回收
|
||||
passwd_handler->backend_child_watch_id = g_child_watch_add (passwd_handler->backend_pid, (GChildWatchFunc) child_watch_cb, passwd_handler);
|
||||
|
||||
/* Success! */
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void update_password (PasswdHandler *passwd_handler)
|
||||
{
|
||||
gchar *s;
|
||||
|
||||
s = g_strdup_printf ("%s\n", passwd_handler->new_password);
|
||||
|
||||
g_queue_push_tail (passwd_handler->backend_stdin_queue, s);
|
||||
/* We need to allocate new space because io_queue_pop() g_free()s
|
||||
* every element of the queue after it's done */
|
||||
g_queue_push_tail (passwd_handler->backend_stdin_queue, g_strdup (s));
|
||||
}
|
||||
|
||||
gboolean passwd_change_password (PasswdHandler *passwd_handler,
|
||||
const char *new_password,
|
||||
PasswdCallback cb,
|
||||
const gpointer user_data)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
passwd_handler->changing_password = TRUE;
|
||||
|
||||
passwd_handler->new_password = new_password;
|
||||
passwd_handler->chpasswd_cb = cb;
|
||||
passwd_handler->chpasswd_cb_data = user_data;
|
||||
|
||||
/* Stop passwd if an error occured and it is still running */
|
||||
if (passwd_handler->backend_state == PASSWD_STATE_ERR) {
|
||||
|
||||
/* Stop passwd, free resources */
|
||||
stop_passwd (passwd_handler);
|
||||
}
|
||||
|
||||
/* Check that the backend is still running, or that an error
|
||||
* has occured but it has not yet exited */
|
||||
if (passwd_handler->backend_pid == -1) {
|
||||
/* If it is not, re-run authentication */
|
||||
|
||||
/* Spawn backend */
|
||||
stop_passwd (passwd_handler);
|
||||
|
||||
if (!spawn_passwd (passwd_handler, &error)) {
|
||||
g_error_free (error);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Add current and new passwords to queue */
|
||||
//将当前的密码和新密码入队,新密码会入队两次
|
||||
authenticate (passwd_handler);
|
||||
update_password (passwd_handler);
|
||||
} else {
|
||||
/* Only add new passwords to queue */
|
||||
update_password (passwd_handler);
|
||||
}
|
||||
|
||||
/* Pop new password through the backend. If user has no password, popping the queue
|
||||
would output current password, while 'passwd' is waiting for the new one. So wait
|
||||
for io_watch_stdout() to remove current password from the queue, and output
|
||||
the new one for us.*/
|
||||
//如果密码为空,将新进队列的密码,作为current_passwd弹出
|
||||
if (passwd_handler->current_password)
|
||||
{
|
||||
io_queue_pop (passwd_handler->backend_stdin_queue, passwd_handler->backend_stdin);
|
||||
}
|
||||
|
||||
/* Our IO watcher should now handle the rest */
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void passwd_authenticate (PasswdHandler *passwd_handler,
|
||||
const char *current_password,
|
||||
PasswdCallback cb,
|
||||
const gpointer user_data)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
/* Don't stop if we've already started chaging password */
|
||||
if (passwd_handler->changing_password)
|
||||
return;
|
||||
|
||||
/* Clear data from possible previous attempts to change password */
|
||||
passwd_handler->new_password = NULL;
|
||||
passwd_handler->chpasswd_cb = NULL;
|
||||
passwd_handler->chpasswd_cb_data = NULL;
|
||||
g_queue_foreach (passwd_handler->backend_stdin_queue, (GFunc) g_free, NULL);
|
||||
g_queue_clear (passwd_handler->backend_stdin_queue);
|
||||
|
||||
passwd_handler->current_password = current_password;
|
||||
passwd_handler->auth_cb = cb;
|
||||
passwd_handler->auth_cb_data = user_data;
|
||||
|
||||
/* Spawn backend */
|
||||
//重新启动后台passwd
|
||||
stop_passwd (passwd_handler);
|
||||
|
||||
if (!spawn_passwd (passwd_handler, &error)) {
|
||||
g_warning ("%s", error->message);
|
||||
g_error_free (error);
|
||||
return;
|
||||
}
|
||||
|
||||
//将current passwd从尾部插入队列
|
||||
authenticate (passwd_handler);
|
||||
|
||||
/* Our IO watcher should now handle the rest */
|
||||
}
|
||||
|
||||
|
||||
PasswdHandler * passwd_init ()
|
||||
{
|
||||
PasswdHandler *passwd_handler;
|
||||
|
||||
passwd_handler = g_new0 (PasswdHandler, 1);
|
||||
|
||||
/* Initialize backend_pid. -1 means the backend is not running */
|
||||
//-1代表后台还没启动
|
||||
passwd_handler->backend_pid = -1;
|
||||
|
||||
/* Initialize IO Channels */
|
||||
passwd_handler->backend_stdin = NULL;
|
||||
passwd_handler->backend_stdout = NULL;
|
||||
|
||||
/* Initialize write queue */
|
||||
passwd_handler->backend_stdin_queue = g_queue_new ();
|
||||
|
||||
/* Initialize watchers */
|
||||
passwd_handler->backend_child_watch_id = 0;
|
||||
passwd_handler->backend_stdout_watch_id = 0;
|
||||
|
||||
/* Initialize backend state */
|
||||
passwd_handler->backend_state = PASSWD_STATE_NONE;
|
||||
passwd_handler->changing_password = FALSE;
|
||||
|
||||
return passwd_handler;
|
||||
}
|
||||
|
||||
|
||||
void passwd_destroy (PasswdHandler *passwd_handler)
|
||||
{
|
||||
g_queue_free (passwd_handler->backend_stdin_queue);
|
||||
stop_passwd (passwd_handler);
|
||||
g_free (passwd_handler);
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
#ifndef RUNPASSWD_H
|
||||
#define RUNPASSWD_H
|
||||
|
||||
|
||||
struct PasswdHandler;
|
||||
|
||||
typedef struct PasswdHandler PasswdHandler;
|
||||
|
||||
typedef void (*PasswdCallback) (PasswdHandler * passwd_handler, GError * error, const gpointer user_data);
|
||||
|
||||
/* Error codes */
|
||||
typedef enum {
|
||||
PASSWD_ERROR_REJECTED, /* New password is not secure enough */
|
||||
PASSWD_ERROR_AUTH_FAILED, /* Wrong old password, or PAM failure */
|
||||
PASSWD_ERROR_REAUTH_FAILED, /* Password has changed since first authentication */
|
||||
PASSWD_ERROR_BACKEND, /* Backend error */
|
||||
PASSWD_ERROR_UNKNOWN /* General error */
|
||||
} PasswdError;
|
||||
|
||||
PasswdHandler *passwd_init ();
|
||||
|
||||
void passwd_destroy (PasswdHandler *passwd_handler);
|
||||
|
||||
void passwd_authenticate (PasswdHandler *passwd_handler,
|
||||
const char *current_password,
|
||||
PasswdCallback cb,
|
||||
gpointer user_data);
|
||||
|
||||
gboolean passwd_change_password (PasswdHandler *passwd_handler,
|
||||
const char *new_password,
|
||||
PasswdCallback cb,
|
||||
const gpointer user_data);
|
||||
|
||||
#endif // RUNPASSWD_H
|
|
@ -0,0 +1,343 @@
|
|||
/*
|
||||
* Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
**/
|
||||
#include "auth-pam.h"
|
||||
#include <string.h>
|
||||
#include <QDebug>
|
||||
#include <sys/prctl.h>
|
||||
#include <unistd.h>
|
||||
#include <wait.h>
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
|
||||
#define PAM_SERVICE_NAME "control-center"
|
||||
|
||||
//通信管道的文件描述符
|
||||
int toParent[2], toChild[2];
|
||||
|
||||
static void writeData(int fd, const void *buf, ssize_t count);
|
||||
static void writeString(int fd, const char *data);
|
||||
static int readData(int fd, void *buf, size_t count);
|
||||
static char * readString(int fd);
|
||||
static int pam_conversation(int msgLength, const struct pam_message **msg,
|
||||
PAM_RESPONSE **resp, void *appData);
|
||||
static void sigchld_handler(int signo);
|
||||
|
||||
AuthPAM::AuthPAM(QObject *parent)
|
||||
: Auth(parent),
|
||||
pid(0),
|
||||
nPrompts(0),
|
||||
_isAuthenticated(false),
|
||||
_isAuthenticating(false)
|
||||
{
|
||||
signal(SIGCHLD, sigchld_handler);
|
||||
}
|
||||
|
||||
void AuthPAM::authenticate(const QString &userName, const QString &userPwd)
|
||||
{
|
||||
stopAuth();
|
||||
|
||||
if(pipe(toParent) || pipe(toChild))
|
||||
qDebug()<< "create pipe failed: " << strerror(errno);
|
||||
if((pid = fork()) < 0)
|
||||
{
|
||||
qDebug() << "fork error: " << strerror(errno);
|
||||
}
|
||||
else if(pid == 0)
|
||||
{
|
||||
int arg1_int = toParent[1];
|
||||
int arg2_int = toChild[0];
|
||||
char arg1[128];
|
||||
char arg2[128];
|
||||
snprintf(arg1,128,"%d",arg1_int);
|
||||
snprintf(arg2,128,"%d",arg2_int);
|
||||
//_authenticate(userName.toLocal8Bit().data());
|
||||
prctl(PR_SET_PDEATHSIG,SIGHUP);
|
||||
execlp ("childCheckpwdwithPAM",
|
||||
"childCheckpwdwithPAM",
|
||||
arg1, arg2,userName.toLocal8Bit().data(), NULL);
|
||||
_exit (EXIT_FAILURE);
|
||||
}
|
||||
else
|
||||
{
|
||||
_isAuthenticating = true;
|
||||
notifier = new QSocketNotifier(toParent[0], QSocketNotifier::Read);
|
||||
connect(notifier, &QSocketNotifier::activated, this, &AuthPAM::onSockRead);
|
||||
}
|
||||
|
||||
QTimer::singleShot(100, this, [=]{respond(userPwd);});
|
||||
|
||||
}
|
||||
|
||||
void AuthPAM::stopAuth()
|
||||
{
|
||||
// qDebug()<<"pppppppppppppppppid = "<<pid;
|
||||
if(pid != 0)
|
||||
{
|
||||
messageList.clear();
|
||||
responseList.clear();
|
||||
_isAuthenticating = false;
|
||||
_isAuthenticated = false;
|
||||
nPrompts = 0;
|
||||
|
||||
::kill(pid, SIGKILL);
|
||||
|
||||
pid = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void AuthPAM::respond(const QString &response)
|
||||
{
|
||||
nPrompts--;
|
||||
responseList.push_back(response);
|
||||
|
||||
// for(auto msg : messageList)
|
||||
// qDebug() << msg.msg;
|
||||
// qDebug() << responseList;
|
||||
// qDebug() << nPrompts;
|
||||
|
||||
if(nPrompts == 0)
|
||||
{
|
||||
//发送响应到子进程
|
||||
int j = 0;
|
||||
PAM_RESPONSE *resp = (PAM_RESPONSE*)calloc(messageList.size(), sizeof(PAM_RESPONSE));
|
||||
//响应的数量和消息的数量一致,如果消息类型不是PROMPT,则响应是空的
|
||||
for(int i = 0; i < messageList.size(); i++)
|
||||
{
|
||||
struct pam_message message = messageList[i];
|
||||
PAM_RESPONSE *r = &resp[i];
|
||||
if(message.msg_style == PAM_PROMPT_ECHO_OFF
|
||||
|| message.msg_style == PAM_PROMPT_ECHO_ON)
|
||||
{
|
||||
int respLength = responseList[j].length() + 1;
|
||||
r->resp = (char *)malloc(sizeof(char) * respLength);
|
||||
memcpy(r->resp, responseList[j].toLocal8Bit().data(), respLength);
|
||||
j++;
|
||||
}
|
||||
}
|
||||
_respond(resp);
|
||||
free(resp);
|
||||
resp = NULL;
|
||||
messageList.clear();
|
||||
responseList.clear();
|
||||
}
|
||||
}
|
||||
|
||||
bool AuthPAM::isAuthenticated()
|
||||
{
|
||||
return _isAuthenticated;
|
||||
}
|
||||
|
||||
bool AuthPAM::isAuthenticating()
|
||||
{
|
||||
return _isAuthenticating;
|
||||
}
|
||||
|
||||
|
||||
void AuthPAM::onSockRead()
|
||||
{
|
||||
// qDebug() << "has message";
|
||||
int msgLength;
|
||||
int authComplete;
|
||||
readData(toParent[0], &authComplete, sizeof(authComplete));
|
||||
|
||||
if(authComplete)
|
||||
{
|
||||
int authRet;
|
||||
if(readData(toParent[0], (void*)&authRet, sizeof(authRet)) <= 0)
|
||||
qDebug() << "get authentication result failed: " << strerror(errno);
|
||||
// qDebug() << "result: " << authRet;
|
||||
_isAuthenticated = (authRet == PAM_SUCCESS);
|
||||
_isAuthenticating = false;
|
||||
Q_EMIT authenticateComplete();
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
readData(toParent[0], &msgLength, sizeof(msgLength));
|
||||
// qDebug() << "message length: " << msgLength;
|
||||
|
||||
for(int i = 0; i < msgLength; i++)
|
||||
{
|
||||
//读取message
|
||||
struct pam_message message;
|
||||
readData(toParent[0], &message.msg_style, sizeof(message.msg_style));
|
||||
message.msg = readString(toParent[0]);
|
||||
|
||||
// qDebug() << message.msg;
|
||||
|
||||
messageList.push_back(message);
|
||||
|
||||
switch (message.msg_style)
|
||||
{
|
||||
case PAM_PROMPT_ECHO_OFF:
|
||||
nPrompts++;
|
||||
Q_EMIT showPrompt(message.msg, Auth::PromptTypeSecret);
|
||||
break;
|
||||
case PAM_PROMPT_ECHO_ON:
|
||||
nPrompts++;
|
||||
Q_EMIT showPrompt(message.msg, Auth::PromptTypeQuestion);
|
||||
break;
|
||||
case PAM_ERROR_MSG:
|
||||
Q_EMIT showMessage(message.msg, Auth::MessageTypeInfo);
|
||||
break;
|
||||
case PAM_TEXT_INFO:
|
||||
Q_EMIT showMessage(message.msg, Auth::MessageTypeError);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(nPrompts == 0)
|
||||
{
|
||||
//不需要响应,发送一个空的
|
||||
PAM_RESPONSE *response = (PAM_RESPONSE*)calloc(messageList.size(), sizeof(PAM_RESPONSE));
|
||||
_respond(response);
|
||||
free(response);
|
||||
response = NULL;
|
||||
messageList.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
writeData(int fd, const void *buf, ssize_t count)
|
||||
{
|
||||
if(write(fd, buf, count) != count)
|
||||
qDebug() << "write to parent failed: " << strerror(errno);
|
||||
}
|
||||
|
||||
static void
|
||||
writeString(int fd, const char *data)
|
||||
{
|
||||
int length = data ? strlen(data) : -1;
|
||||
writeData(fd, &length, sizeof(length));
|
||||
if(data)
|
||||
writeData(fd, data, sizeof(char) * length);
|
||||
}
|
||||
|
||||
static int
|
||||
readData(int fd, void *buf, size_t count)
|
||||
{
|
||||
ssize_t nRead = read(fd, buf, count);
|
||||
if(nRead < 0)
|
||||
qDebug() << "read data failed: " << strerror(errno);
|
||||
return nRead;
|
||||
}
|
||||
|
||||
static char *
|
||||
readString(int fd)
|
||||
{
|
||||
int length;
|
||||
|
||||
if(readData(fd, &length, sizeof(length)) <= 0)
|
||||
return NULL;
|
||||
if(length <= 0)
|
||||
return NULL;
|
||||
|
||||
char *value = (char *)malloc(sizeof(char) * (length + 1));
|
||||
readData(fd, value, length);
|
||||
value[length] = '\0';
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void AuthPAM::_authenticate(const char *userName)
|
||||
{
|
||||
// qDebug() << "authenticate " << userName;
|
||||
|
||||
pam_handle_t *pamh = NULL;
|
||||
char *newUser;
|
||||
int ret;
|
||||
int authRet;
|
||||
struct pam_conv conv;
|
||||
|
||||
conv.conv = pam_conversation;
|
||||
conv.appdata_ptr = NULL;
|
||||
|
||||
ret = pam_start(PAM_SERVICE_NAME, userName, &conv, &pamh);
|
||||
if(ret != PAM_SUCCESS)
|
||||
{
|
||||
qDebug() << "failed to start PAM: " << pam_strerror(NULL, ret);
|
||||
}
|
||||
|
||||
authRet = pam_authenticate(pamh, 0);
|
||||
|
||||
ret = pam_get_item(pamh, PAM_USER, (const void **)&newUser);
|
||||
if(ret != PAM_SUCCESS)
|
||||
{
|
||||
pam_end(pamh, 0);
|
||||
qDebug() << "failed to get username";
|
||||
}
|
||||
free(newUser);
|
||||
newUser = NULL;
|
||||
// fprintf(stderr, "authentication result: %d\n", authRet);
|
||||
|
||||
// 发送认证结果
|
||||
int authComplete = 1;
|
||||
writeData(toParent[1], (const void*)&authComplete, sizeof(authComplete));
|
||||
writeData(toParent[1], (const void *)&authRet, sizeof(authRet));
|
||||
// qDebug() << "--- 认证完成";
|
||||
_exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
void AuthPAM::_respond(const PAM_RESPONSE *response)
|
||||
{
|
||||
for(int i = 0; i < messageList.size(); i++)
|
||||
{
|
||||
const PAM_RESPONSE *resp = &response[i];
|
||||
writeData(toChild[1], (const void *)&resp->resp_retcode,
|
||||
sizeof(resp->resp_retcode));
|
||||
writeString(toChild[1], resp->resp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
pam_conversation(int msgLength, const struct pam_message **msg,
|
||||
PAM_RESPONSE **resp, void */*appData*/)
|
||||
{
|
||||
PAM_RESPONSE *response = (PAM_RESPONSE*)calloc(msgLength,sizeof(PAM_RESPONSE));
|
||||
|
||||
int authComplete = 0;
|
||||
writeData(toParent[1], (const void*)&authComplete, sizeof(authComplete));
|
||||
writeData(toParent[1], (const void*)&msgLength, sizeof(msgLength));
|
||||
//发送pam消息
|
||||
for(int i = 0; i < msgLength; i++)
|
||||
{
|
||||
const struct pam_message *m = msg[i];
|
||||
writeData(toParent[1], (const void *)&m->msg_style, sizeof(m->msg_style));
|
||||
writeString(toParent[1], m->msg);
|
||||
}
|
||||
//读取响应
|
||||
for(int i = 0; i < msgLength; i++)
|
||||
{
|
||||
PAM_RESPONSE *r = &response[i];
|
||||
readData(toChild[0], &r->resp_retcode, sizeof(r->resp_retcode));
|
||||
r->resp = readString(toChild[0]);
|
||||
}
|
||||
*resp = response;
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
void sigchld_handler(int signo)
|
||||
{
|
||||
if(signo == SIGCHLD)
|
||||
{
|
||||
::waitpid(-1, NULL, WNOHANG);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
**/
|
||||
#ifndef AUTHPAM_H
|
||||
#define AUTHPAM_H
|
||||
#include "auth.h"
|
||||
#include <QSocketNotifier>
|
||||
#include <QList>
|
||||
|
||||
#include <security/pam_appl.h>
|
||||
|
||||
typedef struct pam_message PAM_MESSAGE;
|
||||
typedef struct pam_response PAM_RESPONSE;
|
||||
|
||||
class AuthPAM : public Auth
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
AuthPAM(QObject *parent = nullptr);
|
||||
|
||||
void authenticate(const QString &userName, const QString &userPwd);
|
||||
void stopAuth();
|
||||
void respond(const QString &response);
|
||||
bool isAuthenticated();
|
||||
bool isAuthenticating();
|
||||
|
||||
private:
|
||||
void _authenticate(const char *userName);
|
||||
void _respond(const struct pam_response *response);
|
||||
|
||||
private Q_SLOTS:
|
||||
void onSockRead();
|
||||
|
||||
private:
|
||||
QString userName;
|
||||
pid_t pid;
|
||||
QSocketNotifier *notifier;
|
||||
int nPrompts;
|
||||
QStringList responseList;
|
||||
QList<PAM_MESSAGE> messageList;
|
||||
bool _isAuthenticated; //认证结果
|
||||
bool _isAuthenticating;
|
||||
};
|
||||
|
||||
#endif // AUTHPAM_H
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
**/
|
||||
#ifndef AUTH_H
|
||||
#define AUTH_H
|
||||
|
||||
#ifndef QT_NO_KEYWORDS
|
||||
#define QT_NO_KEYWORDS
|
||||
#endif
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class Auth : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_ENUMS(PromptType MessageType)
|
||||
public:
|
||||
explicit Auth(QObject *parent = nullptr)
|
||||
: QObject(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
enum PromptType {
|
||||
PromptTypeQuestion,
|
||||
PromptTypeSecret
|
||||
};
|
||||
enum MessageType {
|
||||
MessageTypeInfo,
|
||||
MessageTypeError
|
||||
};
|
||||
|
||||
|
||||
Q_SIGNALS:
|
||||
void showPrompt(const QString &prompt, Auth::PromptType type);
|
||||
void showMessage(const QString &message, Auth::MessageType type);
|
||||
void authenticateComplete();
|
||||
|
||||
public:
|
||||
virtual void authenticate(const QString &userName, const QString &userPwd) = 0;
|
||||
virtual void stopAuth() = 0;
|
||||
virtual void respond(const QString &response) = 0;
|
||||
virtual bool isAuthenticating() = 0;
|
||||
virtual bool isAuthenticated() = 0;
|
||||
};
|
||||
|
||||
#endif // AUTH_H
|
|
@ -0,0 +1,38 @@
|
|||
QT += core
|
||||
|
||||
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||
|
||||
TARGET = checkUserPwd
|
||||
TEMPLATE = app
|
||||
|
||||
CONFIG += c++11
|
||||
|
||||
# The following define makes your compiler emit warnings if you use
|
||||
# any Qt feature that has been marked deprecated (the exact warnings
|
||||
# depend on your compiler). Please consult the documentation of the
|
||||
# deprecated API in order to know how to port your code away from it.
|
||||
DEFINES += QT_DEPRECATED_WARNINGS
|
||||
|
||||
# You can also make your code fail to compile if it uses deprecated APIs.
|
||||
# In order to do so, uncomment the following line.
|
||||
# You can also select to disable deprecated APIs only up to a certain version of Qt.
|
||||
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||
|
||||
LIBS += -lpam
|
||||
|
||||
SOURCES += \
|
||||
auth-pam.cpp \
|
||||
main.cpp \
|
||||
widget.cpp
|
||||
|
||||
HEADERS += \
|
||||
auth-pam.h \
|
||||
auth.h \
|
||||
widget.h
|
||||
|
||||
target.source += $$TARGET
|
||||
target.path = /usr/bin
|
||||
|
||||
|
||||
INSTALLS += \
|
||||
target \
|
|
@ -0,0 +1,20 @@
|
|||
#include "widget.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QCoreApplication a(argc, argv);
|
||||
|
||||
|
||||
Widget w;
|
||||
if (argc == 3){
|
||||
w.pwdCheck(argv[1], argv[2]);
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return a.exec();
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
#include "widget.h"
|
||||
|
||||
#include "auth-pam.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
Widget::Widget()
|
||||
{
|
||||
|
||||
auth = new AuthPAM(this);
|
||||
|
||||
accountlock = false;
|
||||
|
||||
connect(auth, &Auth::showMessage, this, &Widget::onShowMessage);
|
||||
connect(auth, &Auth::showPrompt, this, &Widget::onShowPrompt);
|
||||
connect(auth, &Auth::authenticateComplete, this, &Widget::onAuthComplete);
|
||||
|
||||
}
|
||||
|
||||
Widget::~Widget()
|
||||
{
|
||||
|
||||
auth->stopAuth();
|
||||
|
||||
delete auth;
|
||||
}
|
||||
|
||||
void Widget::pwdCheck(QString userName, QString userPwd){
|
||||
auth->authenticate(userName, userPwd);
|
||||
}
|
||||
|
||||
void Widget::onShowMessage(const QString &message, Auth::MessageType type)
|
||||
{
|
||||
// qDebug() << "showMessage" << message;
|
||||
accountlock = true;
|
||||
printf("%s\n", message.toUtf8().data());
|
||||
}
|
||||
|
||||
void Widget::onShowPrompt(const QString &prompt, Auth::PromptType type)
|
||||
{
|
||||
// qDebug() << "prompt: " << prompt;
|
||||
}
|
||||
|
||||
void Widget::onAuthComplete()
|
||||
{
|
||||
|
||||
if (!accountlock){
|
||||
if(auth->isAuthenticated()){
|
||||
// qDebug() << "Succes!\n";
|
||||
// printf("Succes!\n");
|
||||
} else {
|
||||
printf("Failed!\n");
|
||||
// qDebug() << "Failed!";
|
||||
}
|
||||
}
|
||||
|
||||
exit(0);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef WIDGET_H
|
||||
#define WIDGET_H
|
||||
|
||||
|
||||
#include "auth-pam.h"
|
||||
|
||||
|
||||
class Widget : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Widget();
|
||||
~Widget();
|
||||
|
||||
public:
|
||||
void pwdCheck(QString userName, QString userPwd);
|
||||
|
||||
private:
|
||||
Auth * auth;
|
||||
|
||||
bool accountlock;
|
||||
|
||||
private Q_SLOTS:
|
||||
void onShowMessage(const QString &message, Auth::MessageType type);
|
||||
void onShowPrompt(const QString &prompt, Auth::PromptType type);
|
||||
void onAuthComplete();
|
||||
};
|
||||
#endif // WIDGET_H
|
|
@ -0,0 +1,7 @@
|
|||
TEMPLATE = subdirs
|
||||
|
||||
CONFIG += ordered
|
||||
|
||||
SUBDIRS = \
|
||||
childCheckPwdWithPAM \
|
||||
checkUserPwd \
|
|
@ -0,0 +1,39 @@
|
|||
QT -= core
|
||||
QT -= gui
|
||||
|
||||
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||
|
||||
TARGET = childCheckpwdwithPAM
|
||||
TEMPLATE = app
|
||||
|
||||
CONFIG += c++11
|
||||
|
||||
# The following define makes your compiler emit warnings if you use
|
||||
# any Qt feature that has been marked deprecated (the exact warnings
|
||||
# depend on your compiler). Please consult the documentation of the
|
||||
# deprecated API in order to know how to port your code away from it.
|
||||
DEFINES += QT_DEPRECATED_WARNINGS
|
||||
|
||||
# You can also make your code fail to compile if it uses deprecated APIs.
|
||||
# In order to do so, uncomment the following line.
|
||||
# You can also select to disable deprecated APIs only up to a certain version of Qt.
|
||||
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||
|
||||
LIBS += -lpam
|
||||
|
||||
SOURCES += \
|
||||
main.cpp
|
||||
|
||||
HEADERS +=
|
||||
|
||||
|
||||
cf.files += ../conf/control-center
|
||||
cf.path = /etc/pam.d/
|
||||
|
||||
target.source += $$TARGET
|
||||
target.path = /usr/bin
|
||||
|
||||
|
||||
INSTALLS += \
|
||||
cf \
|
||||
target \
|
|
@ -0,0 +1,166 @@
|
|||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* Copyright (C) 2019 Tianjin KYLIN Information Technology Co., Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <wait.h>
|
||||
#include <fcntl.h>
|
||||
#include <security/pam_appl.h>
|
||||
#include <sys/mman.h>
|
||||
static int toParent = 0;
|
||||
static int fromChild = 0;
|
||||
|
||||
typedef struct pam_message PAM_MESSAGE;
|
||||
typedef struct pam_response PAM_RESPONSE;
|
||||
|
||||
static void
|
||||
writeData(int fd, const void *buf, ssize_t count)
|
||||
{
|
||||
if(write(fd, buf, count) != count)
|
||||
printf("write to parent failed: %s\n",strerror(errno));
|
||||
}
|
||||
|
||||
static void
|
||||
writeString(int fd, const char *data)
|
||||
{
|
||||
int length = data ? strlen(data) : -1;
|
||||
writeData(fd, &length, sizeof(length));
|
||||
if(data)
|
||||
writeData(fd, data, sizeof(char) * length);
|
||||
}
|
||||
|
||||
static int
|
||||
readData(int fd, void *buf, size_t count)
|
||||
{
|
||||
ssize_t nRead = read(fd, buf, count);
|
||||
if(nRead < 0)
|
||||
printf("read data failed: %s\n",strerror(errno));
|
||||
return nRead;
|
||||
}
|
||||
|
||||
static char *
|
||||
readString(int fd)
|
||||
{
|
||||
int length;
|
||||
|
||||
if(readData(fd, &length, sizeof(length)) <= 0)
|
||||
return NULL;
|
||||
if(length <= 0)
|
||||
return NULL;
|
||||
|
||||
char *value = (char *)malloc(sizeof(char) * (length + 1));
|
||||
readData(fd, value, length);
|
||||
value[length] = '\0';
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static int
|
||||
pam_conversation(int msgLength, const struct pam_message **msg,
|
||||
PAM_RESPONSE **resp, void */*appData*/)
|
||||
{
|
||||
PAM_RESPONSE *response = (PAM_RESPONSE*)calloc(msgLength,sizeof(PAM_RESPONSE));
|
||||
|
||||
int authComplete = 0;
|
||||
writeData(toParent, (const void*)&authComplete, sizeof(authComplete));
|
||||
writeData(toParent, (const void*)&msgLength, sizeof(msgLength));
|
||||
//发送pam消息
|
||||
for(int i = 0; i < msgLength; i++)
|
||||
{
|
||||
const struct pam_message *m = msg[i];
|
||||
writeData(toParent, (const void *)&m->msg_style, sizeof(m->msg_style));
|
||||
writeString(toParent, m->msg);
|
||||
}
|
||||
//读取响应
|
||||
for(int i = 0; i < msgLength; i++)
|
||||
{
|
||||
PAM_RESPONSE *r = &response[i];
|
||||
readData(fromChild, &r->resp_retcode, sizeof(r->resp_retcode));
|
||||
r->resp = readString(fromChild);
|
||||
}
|
||||
*resp = response;
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
static void
|
||||
_authenticate(const char *userName)
|
||||
{
|
||||
// printf("authenticate %s\n",userName);
|
||||
|
||||
pam_handle_t *pamh = NULL;
|
||||
char *newUser;
|
||||
int ret;
|
||||
int authRet;
|
||||
struct pam_conv conv;
|
||||
|
||||
conv.conv = pam_conversation;
|
||||
conv.appdata_ptr = NULL;
|
||||
|
||||
ret = pam_start("control-center", userName, &conv, &pamh);
|
||||
if(ret != PAM_SUCCESS)
|
||||
{
|
||||
printf("failed to start PAM: = %s\n", pam_strerror(NULL, ret));
|
||||
}
|
||||
|
||||
authRet = pam_authenticate(pamh, 0);
|
||||
|
||||
ret = pam_get_item(pamh, PAM_USER, (const void **)&newUser);
|
||||
if(ret != PAM_SUCCESS)
|
||||
{
|
||||
pam_end(pamh, 0);
|
||||
printf("failed to get username\n");
|
||||
}
|
||||
free(newUser);
|
||||
newUser = NULL;
|
||||
// fprintf(stderr, "authentication result: %d\n", authRet);
|
||||
|
||||
// 发送认证结果
|
||||
int authComplete = 1;
|
||||
writeData(toParent, (const void*)&authComplete, sizeof(authComplete));
|
||||
writeData(toParent, (const void *)&authRet, sizeof(authRet));
|
||||
|
||||
/* ---认证完成\n*/
|
||||
_exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc != 4)
|
||||
{
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
toParent = atoi (argv[1]);
|
||||
fromChild = atoi (argv[2]);
|
||||
if (toParent == 0 || fromChild == 0)
|
||||
{
|
||||
printf ("Invalid file descriptors %s %s\n", argv[2], argv[3]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
//mlockall (MCL_CURRENT | MCL_FUTURE);
|
||||
fcntl (toParent, F_SETFD, FD_CLOEXEC);
|
||||
fcntl (fromChild, F_SETFD, FD_CLOEXEC);
|
||||
|
||||
|
||||
_authenticate(argv[3]);
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
@include common-auth
|
||||
auth optional pam_gnome_keyring.so
|
||||
|
||||
|
||||
#If you are using Arch,comment out the
|
||||
#above and use the following.
|
||||
|
||||
#auth include system-auth
|
||||
#account include system-auth
|
||||
#password include system-auth
|
||||
#session include system-auth
|
After Width: | Height: | Size: 68 KiB |
After Width: | Height: | Size: 166 KiB |
After Width: | Height: | Size: 130 KiB |
After Width: | Height: | Size: 89 KiB |
After Width: | Height: | Size: 429 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 78 KiB |
After Width: | Height: | Size: 335 KiB |
After Width: | Height: | Size: 71 KiB |
After Width: | Height: | Size: 161 KiB |
After Width: | Height: | Size: 91 KiB |
After Width: | Height: | Size: 103 KiB |
After Width: | Height: | Size: 45 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 29 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 8.5 KiB |
After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 72 KiB |
After Width: | Height: | Size: 79 KiB |
After Width: | Height: | Size: 70 KiB |
After Width: | Height: | Size: 1.2 MiB |
After Width: | Height: | Size: 1.3 MiB |
After Width: | Height: | Size: 98 KiB |
After Width: | Height: | Size: 78 KiB |
After Width: | Height: | Size: 96 KiB |
After Width: | Height: | Size: 88 KiB |
After Width: | Height: | Size: 53 KiB |
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 56 KiB |
After Width: | Height: | Size: 114 KiB |
After Width: | Height: | Size: 66 KiB |
After Width: | Height: | Size: 70 KiB |
After Width: | Height: | Size: 70 KiB |
After Width: | Height: | Size: 123 KiB |
After Width: | Height: | Size: 116 KiB |
After Width: | Height: | Size: 86 KiB |
After Width: | Height: | Size: 3.6 MiB |
After Width: | Height: | Size: 4.1 MiB |
After Width: | Height: | Size: 3.6 MiB |
After Width: | Height: | Size: 3.6 MiB |
After Width: | Height: | Size: 101 KiB |
After Width: | Height: | Size: 3.9 MiB |
After Width: | Height: | Size: 126 KiB |
After Width: | Height: | Size: 3.7 MiB |
After Width: | Height: | Size: 754 KiB |
After Width: | Height: | Size: 473 KiB |
After Width: | Height: | Size: 502 KiB |
After Width: | Height: | Size: 156 KiB |
After Width: | Height: | Size: 4.1 MiB |
After Width: | Height: | Size: 4.0 MiB |
After Width: | Height: | Size: 117 KiB |
After Width: | Height: | Size: 3.8 MiB |
After Width: | Height: | Size: 347 KiB |
After Width: | Height: | Size: 3.6 MiB |
|
@ -0,0 +1,362 @@
|
|||
# Ukui Control Center
|
||||
## Overview
|
||||
Ukui Control Center provides a friendly graphic interface to set the system. As shown in Fig 1.
|
||||
|
||||
![Fig 1 Ukui Control Center-big](image/1.png)
|
||||
<br>
|
||||
|
||||
## System
|
||||
### Display
|
||||
As shown in Fig 2.
|
||||
|
||||
![Fig 2 Display-big](image/2.png)
|
||||
|
||||
- monitor:Select current monitor.
|
||||
|
||||
- resolution, orientation, refresh rate, screen zoom are all for the current active monitor.
|
||||
|
||||
- screen zoom to global zoom.
|
||||
|
||||
- night mode has the functions of adjusting color temperature and customizing the night mode time period.
|
||||
|
||||
### Audio
|
||||
As shown in Fig 3.
|
||||
|
||||
![Fig 3 Audio-big](image/3.png)
|
||||
|
||||
### Power
|
||||
As shown in Fig 4.
|
||||
|
||||
![Fig 4 Audio-big](image/4.png)
|
||||
|
||||
### Notice
|
||||
As shown in Fig 5.
|
||||
|
||||
![Fig 5 Notice-big](image/5.png)
|
||||
|
||||
### Vino
|
||||
As shown in Fig 6.
|
||||
|
||||
![Fig 6 Vino](image/6.png)
|
||||
|
||||
### About
|
||||
As shown in Fig 4.
|
||||
|
||||
![Fig 7 About-big](image/7.png)
|
||||
|
||||
<br>
|
||||
|
||||
## Devices
|
||||
### Bluetooth
|
||||
Provide the functions like those: open the bluetooth, rename the device, show/hide the icon of the bluetooth, find the nearby devices, etc.. As shown in Fig 8.
|
||||
|
||||
![Fig 8 Bluetooth-big](image/8.png)
|
||||
|
||||
### Printer
|
||||
Provide an entry to add printers or scanners.
|
||||
|
||||
![Fig 9 Printer-big](image/9.png)
|
||||
|
||||
### Mouse
|
||||
As shown in Fig mouse.
|
||||
|
||||
![Fig mouse Mouse-big](image/mouse.png)
|
||||
|
||||
### Touchpad
|
||||
As shown in Fig touchpad.
|
||||
|
||||
![Fig touchpad Touchpad-big](image/touchpad.png)
|
||||
|
||||
### Keyboard
|
||||
As shown in Fig keyboard.
|
||||
|
||||
![Fig keyboard Keyboard-big](image/keyboard.png)
|
||||
|
||||
### Shortcut
|
||||
As shown in Fig 13.
|
||||
|
||||
![Fig 13 Shortcut-big](image/13.png)
|
||||
|
||||
### Projection
|
||||
Projection is a software that is allowed to be projected by mobile phones and other Kylin-OS and can be projected to other Kylin OS in Kylin OS desktop environment. It is divided into two parts: receiving terminal and sending terminal. The main interface is shown in Figure.
|
||||
|
||||
![Fig 14-1 Projection main interface-big](image/mainInterface.png)
|
||||
|
||||
The function buttons at the receiving terminal are in the upper part of the main interface, from top to bottom:
|
||||
|
||||
1) Receiving terminal on / off button: after the button is turned on, the current Kylin-OS device can be searched by mobile phone device or other Kylin-OS devices.
|
||||
|
||||
2) PIN code function on / off button: after the button is turned on, the PIN code needs to be entered during connection.
|
||||
|
||||
![Fig 14-2 Enter PIN code interface-big](image/PINCodeinterface.png)
|
||||
|
||||
![Fig 14-3 Without PIN code interface-big](image/withoutPINCodeinterface.png)
|
||||
|
||||
3) Historical device show / hide button: when the button arrow points down, you can see the relevant devices connected to the current Kylin-OS device.
|
||||
|
||||
![Fig 14-4 Historical device interface-big](image/deviceInterface.png)
|
||||
|
||||
The function buttons at the sending terminal are in the lower part of the main interface, including:
|
||||
|
||||
Receiving terminal on / off button: this button is mutually exclusive with the sending terminal on / off button. When the receiving terminal on / off button is turned on, the receiving terminal on / off button needs to be turned off before the sending terminal on / off button can be turned on (the same is true for the receiving terminal turn on / off button). After opening this button, click Find device to pop up the search window, in which the searchable Kylin-OS device at the receiving terminal will be displayed.
|
||||
|
||||
Connection process between mobile phone and Kylin-OS:
|
||||
|
||||
1) Open the receiving terminal on / off button (close the sending terminal on / off button first).
|
||||
|
||||
2) The mobile phone drop-down menu opens the projection (or mobile phone projection / multi screen collaboration).
|
||||
|
||||
![Fig 14-5 Phone open projection interface-big](image/phoneOpenProjectionInterface.png)
|
||||
|
||||
3) Select the Kylin-OS device to be projected in the mobile search device list.
|
||||
|
||||
![Fig 14-6 Phone search device interface-big](image/phoneSearchInterface.png)
|
||||
|
||||
4) Connect (there are two types: PIN code required and PIN code not required).
|
||||
|
||||
![Fig 14-7 Phone enter pin code interface-big](image/phonePINcodeInterface.png)
|
||||
|
||||
5) Select the mobile phone model (currently divided into Huawei, Xiaomi and others).
|
||||
|
||||
![Fig 14-8 Select phone model interface-big](image/chooseTypeInterface.png)
|
||||
|
||||
6) Projection interface: the mobile phone screen is displayed in the center of the interface. There are 5 buttons on the right side of the interface, from top to bottom: soft / hard decoding switching button; Full screen / window switching button; Return to parent directory button; Return to the main interface button; Display the application process button;
|
||||
|
||||
![Fig 14-9 Receiving terminal projection interface-big](image/receiverConnectInterface.png)
|
||||
|
||||
Kylin-OS and Kylin-OS connection process:
|
||||
|
||||
1) Open the on / off button at the sending terminal (close the on / off button at the receiving terminal in advance).
|
||||
|
||||
2) Click the search device on the left side of the switch button;
|
||||
|
||||
![Fig 14-10 Find device interface-big](image/searchDeviceInterface.png)
|
||||
|
||||
3) In the pop-up search window, select the Kylin-OS device to project to.
|
||||
|
||||
![Fig 14-11 Choose device interface-big](image/chooseDevice.png)
|
||||
|
||||
4) Projection interface.
|
||||
|
||||
![Fig 14-12 Sending terminal projection interface-big](image/senderConnectInterface.png)
|
||||
|
||||
**FAQ**
|
||||
|
||||
1. If the projection is unsuccessful, you can try to connect again, and the device connected once will be automatically recorded in the history device.
|
||||
|
||||
2. Kylin OS and some phones support the control return function (that is, the receiving terminal can indirectly affect the sending terminal by operating the mouse or keyboard).
|
||||
|
||||
3. At present only some Huawei phones and Xiaomi phones support Android three button function (the last three buttons on the interface on the right side of the screen). For other models, clicking these buttons may cause unknown problems.
|
||||
|
||||
4. The system will use soft decoding by default. At this time, you can manually switch to hard decoding (use hardware to decode video, reduce CPU occupation, and need hardware support).
|
||||
|
||||
5. The name of the receiving device can be changed in the screen projection interface. Click the "pen" icon on the right side of the projection screen to enter the modification interface.
|
||||
|
||||
![Fig 14-9 Sending terminal name modification interface-big](image/nameChangeInterface.png)
|
||||
|
||||
6. When the wireless network card is not inserted, or the wireless network card does not support the connection required for projection, it is necessary to insert the network card or replace the inserted network card.
|
||||
|
||||
![Fig 14-10 Projection function unavailable interface(1)-big](image/unUseInterface.png)
|
||||
|
||||
7. When the package with wireless projection is not installed or the package version is too low, the projection function will not be displayed in the control panel.
|
||||
|
||||
![Fig 14-11 Projection function unavailable interface(2)-big](image/unUseInterface2.png)
|
||||
|
||||
8.When used as the sending terminal, it does not support projecting to the receiving terminal of SP1 version.
|
||||
|
||||
<br>
|
||||
|
||||
## Network
|
||||
### WiredConnect
|
||||
As shown in Fig 15.
|
||||
|
||||
![Fig 15 WiredConnect-big](image/37.png)
|
||||
|
||||
### WlanConnect
|
||||
As shown in Fig 16.
|
||||
|
||||
![Fig 16 WlanConnect-big](image/38.png)
|
||||
|
||||
### Proxy
|
||||
As shown in Fig 17.
|
||||
|
||||
![Fig 17 Proxy-big](image/14.png)
|
||||
|
||||
### Vpn
|
||||
As shown in Fig 18.
|
||||
|
||||
![Fig 18 Vpn-big](image/15.png)
|
||||
|
||||
### MobileHotspot
|
||||
As shown in Fig 19.
|
||||
|
||||
![Fig 19 MobileHotspot-big](image/39.png)
|
||||
|
||||
<br>
|
||||
|
||||
## Personalized
|
||||
### Background
|
||||
As shown in Fig 20.
|
||||
|
||||
![Fig 20 Background-big](image/16.png)
|
||||
|
||||
### Theme
|
||||
- Theme Mode:
|
||||
|
||||
![Fig 21-1 Theme mode](image/17.png)
|
||||
|
||||
- Icon theme and cursor theme:
|
||||
|
||||
![Fig 21-2 Icon&cursor theme](image/18.png)
|
||||
|
||||
- Effect settings (some machines do not support this function):
|
||||
|
||||
![Fig 21-3 Effect settings](image/19.png)
|
||||
|
||||
### Screenlock
|
||||
As shown in Fig 22.
|
||||
|
||||
![Fig 22 Screenlock-big](image/20.png)
|
||||
|
||||
### Screensaver
|
||||
As shown in Fig 23.
|
||||
|
||||
![Fig 23 Screensaver-big](image/21.png)
|
||||
|
||||
### Fonts
|
||||
As shown in Fig 24.
|
||||
|
||||
![Fig 24 Fonts-big](image/22.png)
|
||||
|
||||
<br>
|
||||
|
||||
## Account
|
||||
### User Info
|
||||
![Fig 25-1 Account-big](image/23.png)
|
||||
#### Current User
|
||||
- Change User Face: Click user's face can change it.
|
||||
|
||||
![Fig 25-2 Change face](image/24.png)
|
||||
|
||||
- Change Password: Click "Password" to modify the current user's password.
|
||||
|
||||
![Fig 25-3 Change password](image/25.png)
|
||||
|
||||
- Change Account Type: administrator -- can elevated permission temporarily; standard user -- can't elevated permission.
|
||||
|
||||
![Fig 25-4 Change password](image/26.png)
|
||||
|
||||
#### Other Users
|
||||
Administrator can modify other user's information, add new user, etc..
|
||||
|
||||
- Add new user
|
||||
|
||||
-Password complexity requirements(Password strength can be customized through security neutrality):
|
||||
|
||||
1. The user password cannot contain illegal characters ("'" and non-standard characters);
|
||||
|
||||
2. The minimum password length is 8;
|
||||
|
||||
3. The password shall contain at least two types of characters;
|
||||
|
||||
4. The password must not contain the user name;
|
||||
|
||||
5. It is forbidden to use palindrome in password;
|
||||
|
||||
6. Enable password similarity check (detect when modifying password);
|
||||
|
||||
7. Enable password dictionary;
|
||||
|
||||
8. The validity period of the password is unlimited;
|
||||
|
||||
![Fig 25-5 Add new user](image/27.png)
|
||||
|
||||
### Biometrics
|
||||
As shown in Fig 26.
|
||||
|
||||
![Fig 26 Biometrics-big](image/40.png)
|
||||
|
||||
### Cloud Account
|
||||
Synchronize personalized settings and data, and this function needs to sign in.
|
||||
|
||||
![Fig 27 Cloud account-big](image/28.png)
|
||||
|
||||
#### Sign In
|
||||
Login through Kylin ID login Center.
|
||||
|
||||
#### Synchronizable Items
|
||||
- Desktop wallpaper
|
||||
|
||||
- Screensaver: wallpaper and idle time
|
||||
|
||||
- Fonts
|
||||
|
||||
- User's face
|
||||
|
||||
- The settings in control center, such as start menu, taskbar, theme, etc.
|
||||
|
||||
- The settings of pluma, kylin weather, peony, terminal, kylin video.
|
||||
|
||||
#### Tips
|
||||
1) When opening the cloud account first time, it will synchronize once by default. If the cloud exists configuration files, it will download them and sync to local; Otherwise, the local configuration files will be uploaded to the cloud.
|
||||
|
||||
2) After login, if the automatic synchronization is opened, the cloud will synchronize the local configurations every 5 minutes. And they can be used by different machines, different users.
|
||||
|
||||
3) If the automatic synchronization is closed, all the cloud configurations will keep the last upload status.
|
||||
|
||||
4) The automatic synchronization for the single item is the similar effects.
|
||||
|
||||
<br>
|
||||
|
||||
## Datetime
|
||||
### Date
|
||||
As shown in Fig 28:
|
||||
|
||||
![Fig 28 Datetime-big](image/29.png)
|
||||
|
||||
### Area
|
||||
As shown in Fig 29:
|
||||
|
||||
![Fig 29 Area-big](image/30.png)
|
||||
|
||||
<br>
|
||||
|
||||
## Update Operation
|
||||
|
||||
In update configuration, you can configure backup and update.
|
||||
|
||||
-Click start backup to automatically open our Kirin backup and restore tool for system content backup.
|
||||
-Click Detect update to automatically open our Kirin update manager to obtain the updated content.
|
||||
|
||||
|
||||
![Fig 30 Backup-big](image/31.png)
|
||||
|
||||
![Fig 31 Upgrade-big](image/32.png)
|
||||
|
||||
<br>
|
||||
|
||||
## Security
|
||||
### Security Center
|
||||
As shown in Fig 32.
|
||||
|
||||
![Fig 32 SecurityCenter-big](image/33.png)
|
||||
|
||||
<br>
|
||||
|
||||
## Application
|
||||
### Auto Boot
|
||||
As shown in Fig 33:
|
||||
|
||||
![Fig 33 Autoboot-big](image/34.png)
|
||||
|
||||
### Default App
|
||||
As shown in Fig 34:
|
||||
|
||||
![Fig 34 Defaultapp-big](image/35.png)
|
||||
|
||||
## Investigation
|
||||
|
||||
### Search
|
||||
As shown in Fig 35:
|
||||
|
||||
![Fig 35 Search-big](image/36.png)
|
After Width: | Height: | Size: 86 KiB |
After Width: | Height: | Size: 83 KiB |
After Width: | Height: | Size: 57 KiB |
After Width: | Height: | Size: 53 KiB |
After Width: | Height: | Size: 72 KiB |
After Width: | Height: | Size: 78 KiB |
After Width: | Height: | Size: 69 KiB |
After Width: | Height: | Size: 66 KiB |
After Width: | Height: | Size: 94 KiB |
After Width: | Height: | Size: 61 KiB |
After Width: | Height: | Size: 57 KiB |
After Width: | Height: | Size: 86 KiB |
|
@ -0,0 +1,27 @@
|
|||
<schemalist gettext-domain="ukui-control-center">
|
||||
<schema id="org.ukui.control-center.apt.proxy" path="/org/ukui/control-center/apt/proxy/">
|
||||
<key name="enabled" type="b">
|
||||
<default>false</default>
|
||||
<summary>Whether open</summary>
|
||||
<description>
|
||||
This key is used to control whether to open apt-proxy.
|
||||
</description>
|
||||
</key>
|
||||
<key name="host" type="s">
|
||||
<default>''</default>
|
||||
<summary>APT proxy host name</summary>
|
||||
<description>
|
||||
The machine name to proxy APT through.
|
||||
</description>
|
||||
</key>
|
||||
<key name="port" type="i">
|
||||
<range min="0" max="65535"/>
|
||||
<default>8080</default>
|
||||
<summary>APT proxy port</summary>
|
||||
<description>
|
||||
The port on the machine defined by “/apt/proxy/host” that you
|
||||
proxy through.
|
||||
</description>
|
||||
</key>
|
||||
</schema>
|
||||
</schemalist>
|
|
@ -0,0 +1,54 @@
|
|||
<schemalist gettext-domain="ukui-control-center">
|
||||
<schema id="org.ukui.control-center.desktop" path="/org/ukui/control-center/desktop/">
|
||||
<key name="computer-icon-visible" type="b">
|
||||
<default>true</default>
|
||||
<summary>Show computer icon on desktop</summary>
|
||||
<description>Whether show computer icon on desktop or not.</description>
|
||||
</key>
|
||||
<key name="home-icon-visible" type="b">
|
||||
<default>true</default>
|
||||
<summary>Show home icon on desktop</summary>
|
||||
<description>Whether show home icon on desktop or not.</description>
|
||||
</key>
|
||||
<key name="network-icon-visible" type="b">
|
||||
<default>true</default>
|
||||
<summary>Show network icon on desktop</summary>
|
||||
<description>Whether show network icon on desktop or not.</description>
|
||||
</key>
|
||||
<key name="trash-icon-visible" type="b">
|
||||
<default>true</default>
|
||||
<summary>Show trash icon on desktop</summary>
|
||||
<description>Whether show trash icon on desktop or not.</description>
|
||||
</key>
|
||||
<key name="volumes-visible" type="b">
|
||||
<default>true</default>
|
||||
<summary>Show volumes icon on desktop</summary>
|
||||
<description>Whether show volumes icon on desktop or not.</description>
|
||||
</key>
|
||||
<key name="menufull-screen" type="b">
|
||||
<default>false</default>
|
||||
<summary>menufull-screen switch button</summary>
|
||||
<description>Whether to always use the start menu in full screen.</description>
|
||||
</key>
|
||||
<key name="computer-icon-locking" type="b">
|
||||
<default>true</default>
|
||||
<summary>Lock computer icon on start menu</summary>
|
||||
<description>Whether lock computer icon on start menu or not.</description>
|
||||
</key>
|
||||
<key name="personal-icon-locking" type="b">
|
||||
<default>false</default>
|
||||
<summary>Lock personal icon on start menu</summary>
|
||||
<description>Whether personal icon on start menu or not.</description>
|
||||
</key>
|
||||
<key name="settings-icon-locking" type="b">
|
||||
<default>true</default>
|
||||
<summary>Lock settings icon on start menu</summary>
|
||||
<description>Whether lock settings icon on start menu or not.</description>
|
||||
</key>
|
||||
<key name="trash-icon-locking" type="b">
|
||||
<default>false</default>
|
||||
<summary>Lock trash icon on start menu</summary>
|
||||
<description>Whether lock trash icon on start menu or not.</description>
|
||||
</key>
|
||||
</schema>
|
||||
</schemalist>
|
|
@ -0,0 +1,9 @@
|
|||
<schemalist gettext-domain="ukui-control-center">
|
||||
<schema id="org.ukui.control-center.experienceplan" path="/org/ukui/control-center/experienceplan/">
|
||||
<key name="join" type="b">
|
||||
<default>true</default>
|
||||
<summary>join user experience plan</summary>
|
||||
<description>Whether this plugin would be activated by ukui-settings-daemon or not</description>
|
||||
</key>
|
||||
</schema>
|
||||
</schemalist>
|
|
@ -0,0 +1,19 @@
|
|||
<schemalist gettext-domain="ukui-control-center">
|
||||
<schema id="org.ukui.control-center.keybinding">
|
||||
<key name="binding" type="s">
|
||||
<default>''</default>
|
||||
<summary>Keybinding</summary>
|
||||
<description>Keybinding associated with a custom shortcut.</description>
|
||||
</key>
|
||||
<key name="action" type="s">
|
||||
<default>''</default>
|
||||
<summary>Command</summary>
|
||||
<description>Command associated with a custom keybinding.</description>
|
||||
</key>
|
||||
<key name="name" type="s">
|
||||
<default>''</default>
|
||||
<summary>Name</summary>
|
||||
<description>Description associated with a custom keybinding.</description>
|
||||
</key>
|
||||
</schema>
|
||||
</schemalist>
|
|
@ -0,0 +1,11 @@
|
|||
<schemalist gettext-domain="ukui-control-center">
|
||||
<schema id="org.ukui.control-center.keyboard" path="/org/ukui/control-center/keyboard/">
|
||||
</schema>
|
||||
<schema id="org.ukui.control-center.osd" path="/org/ukui/control-center/osd/">
|
||||
<key type="b" name="show-lock-tip">
|
||||
<default>true</default>
|
||||
<summary>show keyboard tip</summary>
|
||||
<description>show keyboard tip or not. eg. Caps Lock. Num Lock</description>
|
||||
</key>
|
||||
</schema>
|
||||
</schemalist>
|
|
@ -0,0 +1,66 @@
|
|||
<schemalist gettext-domain="ukui-control-center">
|
||||
<enum id="org.ukui.control-center.noticeorigin.Type">
|
||||
<value value="0" nick="none"/>
|
||||
<value value="1" nick="corner"/>
|
||||
<value value="2" nick="all"/>
|
||||
</enum>
|
||||
<schema id="org.ukui.control-center.notice" path="/org/ukui/control-center/notice/">
|
||||
<key name="show-new-feature" type="b">
|
||||
<default>true</default>
|
||||
<summary>Activation of this plugin</summary>
|
||||
<description>Whether this plugin would be activated by ukui-settings-daemon or not</description>
|
||||
</key>
|
||||
<key name="enable-notice" type="b">
|
||||
<default>true</default>
|
||||
<summary>Show OSD notification</summary>
|
||||
<description>Whether an OSD notification is shown to notify about changes</description>
|
||||
</key>
|
||||
<key name="show-on-lockscreen" type="b">
|
||||
<default>true</default>
|
||||
<summary>Eject</summary>
|
||||
<description>Binding to eject an optical disc.</description>
|
||||
</key>
|
||||
<key name="iscn-env" type="b">
|
||||
<default>true</default>
|
||||
<summary>"" </summary>
|
||||
<description>Determine whether the environment is Chinese</description>
|
||||
</key>
|
||||
<key name="blacklist" type="as">
|
||||
<default>[]</default>
|
||||
<summary>"" </summary>
|
||||
<description>Hidden DektopList</description>
|
||||
</key>
|
||||
</schema>
|
||||
<schema id="org.ukui.control-center.noticeorigin">
|
||||
<key name="messages" type="b">
|
||||
<default>true</default>
|
||||
<summary>messages notice</summary>
|
||||
<description>.</description>
|
||||
</key>
|
||||
<key name="voice" type="b">
|
||||
<default>true</default>
|
||||
<summary>voice notice</summary>
|
||||
<description>.</description>
|
||||
</key>
|
||||
<key name="maximize" type='i'>
|
||||
<default>3</default>
|
||||
<summary>maximize num of messages</summary>
|
||||
<description>The maximize num of messages on notice window.</description>
|
||||
</key>
|
||||
<key name="name-cn" type='s'>
|
||||
<default>''</default>
|
||||
<summary>app's name</summary>
|
||||
<description>The name passed to the sidebar</description>
|
||||
</key>
|
||||
<key name="name-us" type='s'>
|
||||
<default>''</default>
|
||||
<summary>app's name</summary>
|
||||
<description>The name passed to the sidebar</description>
|
||||
</key>
|
||||
<key name="type" enum="org.ukui.control-center.noticeorigin.Type">
|
||||
<default>'corner'</default>
|
||||
<summary>type of notice message</summary>
|
||||
<description>the type of notice in system.</description>
|
||||
</key>
|
||||
</schema>
|
||||
</schemalist>
|
|
@ -0,0 +1,59 @@
|
|||
<schemalist gettext-domain="ukui-control-center">
|
||||
<schema id="org.ukui.control-center.panel.plugins" path="/org/ukui/control-center/panel/plugins/">
|
||||
<key name="hoursystem" type="s">
|
||||
<default>'24'</default>
|
||||
<summary>HourSystem status</summary>
|
||||
<description>hoursystem used in UKUI Desktop Environment ,ontrol by ukui-panel and ukui-control-center</description>
|
||||
</key>
|
||||
<key name="synctime" type="b">
|
||||
<default>true</default>
|
||||
<summary>sync time from network</summary>
|
||||
<description></description>
|
||||
</key>
|
||||
<key name="ntp" type="s">
|
||||
<default>''</default>
|
||||
<summary>customize ntp server address</summary>
|
||||
<description>user-defined ntp server address</description>
|
||||
</key>
|
||||
<key name="timezones" type="as">
|
||||
<default>[]</default>
|
||||
<summary>all time zones of time display</summary>
|
||||
<description>other time zones</description>
|
||||
</key>
|
||||
<key name="calendar" type="s">
|
||||
<default>'lunar'</default>
|
||||
<summary>Lunar calendar</summary>
|
||||
<description>calendar system used in UKUI Desktop Environment ,ontrol by ukui-panel and ukui-control-center</description>
|
||||
</key>
|
||||
<key name="firstday" type="s">
|
||||
<default>'monday'</default>
|
||||
<summary>first of week</summary>
|
||||
<description>Select the first day of the week</description>
|
||||
</key>
|
||||
<key name="date" type="s">
|
||||
<default>'cn'</default>
|
||||
<summary>date formate</summary>
|
||||
<description>date format</description>
|
||||
</key>
|
||||
<key name="time" type="s">
|
||||
<default>'24'</default>
|
||||
<summary>first of week</summary>
|
||||
<description>Select the first day of the week</description>
|
||||
</key>
|
||||
<key name="showlanguage" type="as">
|
||||
<default>[]</default>
|
||||
<summary>show language in plugin area</summary>
|
||||
<description>show language in plugin area</description>
|
||||
</key>
|
||||
<key name="nightmodestatus" type="b">
|
||||
<default>false</default>
|
||||
<summary>ukui-control-center sets the night mode status so that ukui-panel can get the status</summary>
|
||||
<description></description>
|
||||
</key>
|
||||
<key name="themebynight" type="b">
|
||||
<default>false</default>
|
||||
<summary>ukui-control-center theme changes follow with the night mode</summary>
|
||||
<description></description>
|
||||
</key>
|
||||
</schema>
|
||||
</schemalist>
|