summaryrefslogtreecommitdiffstats
path: root/kdm
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commit4aed2c8219774f5d797760606b8489a92ddc5163 (patch)
tree3f8c130f7d269626bf6a9447407ef6c35954426a /kdm
downloadtdebase-4aed2c8219774f5d797760606b8489a92ddc5163.tar.gz
tdebase-4aed2c8219774f5d797760606b8489a92ddc5163.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdebase@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kdm')
-rw-r--r--kdm/ChangeLog540
-rw-r--r--kdm/Makefile.am20
-rw-r--r--kdm/README454
-rw-r--r--kdm/TODO243
-rw-r--r--kdm/backend/Imakefile203
-rw-r--r--kdm/backend/Makefile.am46
-rw-r--r--kdm/backend/access.c468
-rw-r--r--kdm/backend/auth.c1247
-rw-r--r--kdm/backend/bootman.c282
-rw-r--r--kdm/backend/choose.c1051
-rw-r--r--kdm/backend/client.c1760
-rw-r--r--kdm/backend/ctrl.c955
-rw-r--r--kdm/backend/daemon.c78
-rw-r--r--kdm/backend/dm.c1652
-rw-r--r--kdm/backend/dm.h620
-rw-r--r--kdm/backend/dm_auth.h99
-rw-r--r--kdm/backend/dm_error.h58
-rw-r--r--kdm/backend/dm_socket.h72
-rw-r--r--kdm/backend/dpylist.c294
-rw-r--r--kdm/backend/error.c130
-rw-r--r--kdm/backend/genauth.c490
-rw-r--r--kdm/backend/greet.h278
-rw-r--r--kdm/backend/inifile.c255
-rw-r--r--kdm/backend/krb5auth.c248
-rw-r--r--kdm/backend/mitauth.c87
-rw-r--r--kdm/backend/netaddr.c219
-rw-r--r--kdm/backend/policy.c278
-rw-r--r--kdm/backend/printf.c872
-rw-r--r--kdm/backend/process.c761
-rw-r--r--kdm/backend/protodpy.c141
-rw-r--r--kdm/backend/reset.c111
-rw-r--r--kdm/backend/resource.c486
-rw-r--r--kdm/backend/rpcauth.c89
-rw-r--r--kdm/backend/server.c360
-rw-r--r--kdm/backend/session.c792
-rw-r--r--kdm/backend/sessreg.c307
-rw-r--r--kdm/backend/socket.c418
-rw-r--r--kdm/backend/streams.c127
-rw-r--r--kdm/backend/util.c621
-rw-r--r--kdm/backend/xdmauth.c267
-rw-r--r--kdm/backend/xdmcp.c1160
-rw-r--r--kdm/config.def2615
-rw-r--r--kdm/configure.in.bot8
-rw-r--r--kdm/configure.in.in243
-rwxr-xr-xkdm/confproc.pl838
-rw-r--r--kdm/kfrontend/Makefile.am66
-rw-r--r--kdm/kfrontend/genkdmconf.c2899
-rw-r--r--kdm/kfrontend/kchooser.cpp227
-rw-r--r--kdm/kfrontend/kchooser.h59
-rw-r--r--kdm/kfrontend/kconsole.cpp183
-rw-r--r--kdm/kfrontend/kconsole.h53
-rw-r--r--kdm/kfrontend/kdm_config.c1470
-rw-r--r--kdm/kfrontend/kdm_greet.c787
-rw-r--r--kdm/kfrontend/kdm_greet.h91
-rw-r--r--kdm/kfrontend/kdmclock.cpp176
-rw-r--r--kdm/kfrontend/kdmclock.h52
-rw-r--r--kdm/kfrontend/kdmconfig.cpp177
-rw-r--r--kdm/kfrontend/kdmconfig.h55
-rw-r--r--kdm/kfrontend/kdmctl.c236
-rw-r--r--kdm/kfrontend/kdmshutdown.cpp714
-rw-r--r--kdm/kfrontend/kdmshutdown.h196
-rw-r--r--kdm/kfrontend/kfdialog.cpp149
-rw-r--r--kdm/kfrontend/kfdialog.h63
-rw-r--r--kdm/kfrontend/kgapp.cpp253
-rw-r--r--kdm/kfrontend/kgapp.h49
-rw-r--r--kdm/kfrontend/kgdialog.cpp238
-rw-r--r--kdm/kfrontend/kgdialog.h87
-rw-r--r--kdm/kfrontend/kgreeter.cpp998
-rw-r--r--kdm/kfrontend/kgreeter.h164
-rw-r--r--kdm/kfrontend/kgverify.cpp1211
-rw-r--r--kdm/kfrontend/kgverify.h248
-rw-r--r--kdm/kfrontend/krootimage.cpp122
-rw-r--r--kdm/kfrontend/krootimage.h48
-rw-r--r--kdm/kfrontend/pics/Makefile.am9
-rw-r--r--kdm/kfrontend/pics/default1.pngbin0 -> 2622 bytes
-rw-r--r--kdm/kfrontend/pics/default2.pngbin0 -> 5663 bytes
-rw-r--r--kdm/kfrontend/pics/default3.pngbin0 -> 4260 bytes
-rw-r--r--kdm/kfrontend/pics/kdelogo-crystal.pngbin0 -> 11375 bytes
-rw-r--r--kdm/kfrontend/pics/kdelogo.pngbin0 -> 16480 bytes
-rw-r--r--kdm/kfrontend/pics/root1.pngbin0 -> 3070 bytes
-rw-r--r--kdm/kfrontend/pics/shutdown.jpgbin0 -> 2536 bytes
-rw-r--r--kdm/kfrontend/sessions/9wm.desktop76
-rw-r--r--kdm/kfrontend/sessions/Makefile.am49
-rw-r--r--kdm/kfrontend/sessions/aewm++.desktop74
-rw-r--r--kdm/kfrontend/sessions/aewm.desktop76
-rw-r--r--kdm/kfrontend/sessions/afterstep.desktop83
-rw-r--r--kdm/kfrontend/sessions/amaterus.desktop75
-rw-r--r--kdm/kfrontend/sessions/amiwm.desktop78
-rw-r--r--kdm/kfrontend/sessions/asclassic.desktop81
-rw-r--r--kdm/kfrontend/sessions/blackbox.desktop88
-rw-r--r--kdm/kfrontend/sessions/cde.desktop74
-rw-r--r--kdm/kfrontend/sessions/ctwm.desktop72
-rw-r--r--kdm/kfrontend/sessions/cwwm.desktop74
-rw-r--r--kdm/kfrontend/sessions/enlightenment.desktop86
-rw-r--r--kdm/kfrontend/sessions/evilwm.desktop77
-rw-r--r--kdm/kfrontend/sessions/fluxbox.desktop81
-rw-r--r--kdm/kfrontend/sessions/flwm.desktop77
-rw-r--r--kdm/kfrontend/sessions/fvwm.desktop71
-rw-r--r--kdm/kfrontend/sessions/fvwm2.desktop70
-rw-r--r--kdm/kfrontend/sessions/fvwm95.desktop76
-rw-r--r--kdm/kfrontend/sessions/gnome.desktop81
-rw-r--r--kdm/kfrontend/sessions/golem.desktop81
-rw-r--r--kdm/kfrontend/sessions/icewm.desktop81
-rw-r--r--kdm/kfrontend/sessions/ion.desktop77
-rw-r--r--kdm/kfrontend/sessions/kde.desktop.in45
-rw-r--r--kdm/kfrontend/sessions/larswm.desktop72
-rw-r--r--kdm/kfrontend/sessions/lwm.desktop76
-rw-r--r--kdm/kfrontend/sessions/matchbox.desktop82
-rw-r--r--kdm/kfrontend/sessions/metacity.desktop83
-rw-r--r--kdm/kfrontend/sessions/mwm.desktop78
-rw-r--r--kdm/kfrontend/sessions/olvwm.desktop71
-rw-r--r--kdm/kfrontend/sessions/olwm.desktop76
-rw-r--r--kdm/kfrontend/sessions/openbox.desktop84
-rw-r--r--kdm/kfrontend/sessions/oroborus.desktop76
-rw-r--r--kdm/kfrontend/sessions/phluid.desktop80
-rw-r--r--kdm/kfrontend/sessions/pwm.desktop72
-rw-r--r--kdm/kfrontend/sessions/qvwm.desktop80
-rw-r--r--kdm/kfrontend/sessions/ratpoison.desktop82
-rw-r--r--kdm/kfrontend/sessions/sapphire.desktop84
-rw-r--r--kdm/kfrontend/sessions/sawfish.desktop74
-rw-r--r--kdm/kfrontend/sessions/twm.desktop71
-rw-r--r--kdm/kfrontend/sessions/ude.desktop75
-rw-r--r--kdm/kfrontend/sessions/vtwm.desktop72
-rw-r--r--kdm/kfrontend/sessions/w9wm.desktop74
-rw-r--r--kdm/kfrontend/sessions/waimea.desktop77
-rw-r--r--kdm/kfrontend/sessions/wm2.desktop77
-rw-r--r--kdm/kfrontend/sessions/wmaker.desktop82
-rw-r--r--kdm/kfrontend/sessions/xfce.desktop73
-rw-r--r--kdm/kfrontend/sessions/xfce4.desktop72
-rw-r--r--kdm/kfrontend/themer/Makefile.am16
-rw-r--r--kdm/kfrontend/themer/kdmitem.cpp532
-rw-r--r--kdm/kfrontend/themer/kdmitem.h263
-rw-r--r--kdm/kfrontend/themer/kdmlabel.cpp231
-rw-r--r--kdm/kfrontend/themer/kdmlabel.h81
-rw-r--r--kdm/kfrontend/themer/kdmlayout.cpp167
-rw-r--r--kdm/kfrontend/themer/kdmlayout.h98
-rw-r--r--kdm/kfrontend/themer/kdmpixmap.cpp242
-rw-r--r--kdm/kfrontend/themer/kdmpixmap.h69
-rw-r--r--kdm/kfrontend/themer/kdmrect.cpp154
-rw-r--r--kdm/kfrontend/themer/kdmrect.h65
-rw-r--r--kdm/kfrontend/themer/kdmthemer.cpp329
-rw-r--r--kdm/kfrontend/themer/kdmthemer.h123
-rw-r--r--kdm/kfrontend/themes/Makefile.am1
-rw-r--r--kdm/kfrontend/themes/circles/GdmGreeterTheme.desktop135
-rw-r--r--kdm/kfrontend/themes/circles/Makefile.am11
-rw-r--r--kdm/kfrontend/themes/circles/background.svg39
-rw-r--r--kdm/kfrontend/themes/circles/circles.xml207
-rw-r--r--kdm/kfrontend/themes/circles/flower.pngbin0 -> 120376 bytes
-rw-r--r--kdm/kfrontend/themes/circles/help.pngbin0 -> 2138 bytes
-rw-r--r--kdm/kfrontend/themes/circles/options.pngbin0 -> 2297 bytes
-rw-r--r--kdm/kfrontend/themes/circles/screenshot.pngbin0 -> 16847 bytes
151 files changed, 39832 insertions, 0 deletions
diff --git a/kdm/ChangeLog b/kdm/ChangeLog
new file mode 100644
index 000000000..0189ab24a
--- /dev/null
+++ b/kdm/ChangeLog
@@ -0,0 +1,540 @@
+This change log contains only changes relevant to the KDM configuration,
+startup and packaging. Bug fixes are not listed, and feature changes only
+if they affect the configuration.
+
+2005-08-21 Oswald Buddenhagen <ossi@kde.org>
+
+ * Added timed login. Option AutoLoginDelay.
+ * Added persistent auto-login. Option AutoLoginAgain.
+
+2005-02-01 Oswald Buddenhagen <ossi@kde.org>
+
+ * Made the word splitter more sh-like. Affects HaltCmd, RebootCmd,
+ Setup, Startup, Reset, Session and Xrdb.
+ * Replaced option Xservers with StaticServers, ReserveServers,
+ ServerCmd, ServerArgsLocal, ServerArgsRemote, ServerVT and ServerTTY.
+
+2005-01-31 Oswald Buddenhagen <ossi@kde.org>
+
+ * Added console mode that is suitable for systems with VTs (Linux).
+ Option ConsoleTTYs. The @tty spec in Xservers is irrelevant on
+ those systems now.
+
+2005-01-23 Oswald Buddenhagen <ossi@kde.org>
+
+ * Added Grub support to boot options. Option UseLilo replaced
+ with BootManager { None, Grub, Lilo }. Removed options LiloCmd
+ and LiloMap.
+
+2005-01-09 Oswald Buddenhagen <ossi@kde.org>
+
+ * Merged sessreg into kdm. Option UseSessReg.
+
+2004-08-14 Oswald Buddenhagen <ossi@kde.org>
+
+ * Replaced dysfunct InteractiveSd with working
+ ScheduledSd { Never, Optional, Always }
+
+2004-07-28 Oswald Buddenhagen <ossi@kde.org>
+
+ * Added control sockets. Control FiFos are now obsolete.
+ Added control socket command line client 'kdmctl'.
+ * The Setup program is now run even for automatic logins.
+ Setup, Startup and Reset have the arg "auto" for automatic logins.
+
+2004-07-23 Oswald Buddenhagen <ossi@kde.org>
+
+ * Dynamic VT allocation added; option ServerVTs; no need to specify
+ vtX in Xservers any more.
+
+2004-07-10 Oswald Buddenhagen <ossi@kde.org>
+
+ * GreeterPosX, GreeterPosY and GreeterPosFixed replaced with
+ single GreeterPos which is a pair of relative coordinates
+
+2004-07-01 Oswald Buddenhagen <ossi@kde.org>
+
+ * The greeter can now run a "session preloader"; option Preloader
+
+2004-04-15 Oswald Buddenhagen <ossi@kde.org>
+
+ * Merge from XDM:
+ - IPv6 support
+ - LISTEN keyword in Xaccess
+ - Changes to Enable and Port in [Xdmcp] now take effect when HUPed
+ - Support for EGD/PRNGD; options PrngdPort and PrngdSocket
+
+2004-04-14 Oswald Buddenhagen <ossi@kde.org>
+
+ * -debug now groks additional bits for stracing and valgrinding
+ helper processes and disabling syslog usage.
+
+2004-04-09 Oswald Buddenhagen <ossi@kde.org>
+
+ * NoPassUsers now accepts an asterisk ("*") meaning all users
+
+2004-04-08 Oswald Buddenhagen <ossi@kde.org>
+
+ * Changes to FifoDir and FifoGroup now take effect on the global
+ command FiFo when HUPed.
+
+2004-03-16 Oswald Buddenhagen <ossi@kde.org>
+
+ * Changed AllowClose default to true; only the default kdmrc
+ disables it for local display now.
+
+2004-03-11 Chris Cheney <ccheney@cheney.cx>
+
+ * Sanitized UserPath and SystemPath defaults.
+
+2004-03-07 Oswald Buddenhagen <ossi@kde.org>
+
+ * Add user name autocompletion to greeter; option UserCompletion.
+ As a side effect, "None" is gone from ShowUsers and UserList
+ appeared; ShowUsers & SortUsers affect both the user list and
+ the completion list.
+
+2004-02-28 Oswald Buddenhagen <ossi@kde.org>
+
+ * The default Xsession no longer tries to merge kdm's PATH into the
+ one set up by the shell startup scripts. Instead, kde.desktop
+ contains a full pathname and startkde fixes up PATH if necessary.
+ * The default Xsession will emulate the startup behaviour of more
+ shells, in particular bash, zsh and csh/tcsh.
+ * Setting up the session log is now done by kdm, not the Session
+ script; option ClientLogFile.
+
+2004-01-25 Oswald Buddenhagen <ossi@kde.org>
+
+ * Add support for specifying groups in SelectedUsers, HiddenUsers and
+ NoPassUsers. Prefix group names with a @.
+
+2003-12-17 Oswald Buddenhagen <ossi@kde.org>
+
+ * XDMCP initiated local displays are now treated as remote displays
+ (on localhost) by the config reader.
+
+2003-11-09 Oswald Buddenhagen <ossi@kde.org>
+
+ * Sanitized display restart behaviour; option StartInterval is gone.
+
+2003-11-04 Oswald Buddenhagen <ossi@kde.org>
+
+ * Conversation plugins can be configured now; option PluginOptions.
+ * The "Restart X Server"/"Close Connection" action can be configured
+ away; option AllowClose.
+ * The "Console Login" action can be configured away without touching
+ Xservers; option AllowConsole.
+
+2003-10-27 Oswald Buddenhagen <ossi@kde.org>
+
+ * KDM now complies with the input model of PAM. The greeter has an
+ interface for conversation plugins, so alternative authentication
+ mechanisms can be handled; options PluginsLogin & PluginsShutdown.
+ * Password-less and automatic logins now use a separate PAM service
+ (${KDM_PAM_SERVICE}-np).
+
+2003-10-17 Oswald Buddenhagen <ossi@kde.org>
+
+ * Add --with-kdm-xconsole configure switch. No need to patch
+ Makefile.am to enable the built-in xconsole anymore.
+
+2003-09-23 Oswald Buddenhagen <ossi@kde.org>
+
+ * Session types are now defined with .desktop files; consequently the
+ SessionTypes option is gone and we got SessionsDirs instead.
+ * The default Xsession now hard-wires the session types
+ - "default" to startkde
+ - "custom" to ~/.xsession
+ * The previous session type choice is now saved in a different file
+ (~/.dmrc) in a different format (ini-file); the SessSaveFile option
+ is gone. Optionally KDM can be configured to store all .dmrc files
+ in a common directory (option DmrcDir); this can be useful for AFS
+ based installations.
+ * The location of the administratively set user faces is now specified
+ by the FaceDir option and the pictures have a .face.icon extension
+ (or .face for "natural" images, possibly photos).
+
+ The spec for the above changes is shared with GDM, so packagers should
+ choose common directories.
+
+ * The kdmsts file moved to /var/lib/kdm by default; option DataDir.
+ * Nuked the AutoLoginSession option; i don't think it was useful at all
+ and it can be emulated anyway.
+
+2003-09-03 Oswald Buddenhagen <ossi@kde.org>
+
+ * Add option RandomDevice to override the OS specific default
+ entropy source.
+
+2003-08-26 Oswald Buddenhagen <ossi@kde.org>
+
+ * Add random seed to forged "previous" session type calculation;
+ option ForgingSeed.
+
+2003-07-15 Malte Starostik <malte@kde.org>
+
+ * ColorScheme is now interpreted as the base name of the .kcsrc file,
+ not the contents of its Name field.
+
+2003-05-11 Oswald Buddenhagen <ossi@kde.org>
+
+ * GUIStyle & ColorScheme now accept an empty string, meaning
+ "built-in default". Made defaults empty, consequently.
+
+2002-12-01 Oswald Buddenhagen <ossi@kde.org>
+
+ * Integrated chooser into greeter; external 'chooser' executable
+ and the Chooser option are gone.
+ * The chooser can be started locally (without an XDMCP query);
+ options LoginMode and ChooserHosts.
+ * Added built-in xconsole to greeter; options ShowLog and LogSource.
+ This code is not built by default; uncomment the first three lines
+ in kfrontend/Makefile.am to enable it.
+ * The DaemonMode option is gone. The command line switches -daemon and
+ -nodaemon still exist, but are mostly unnecessary, as KDM can decide
+ what to do based on the parent process ID.
+ * The AutoLogin option and the -autolog/-noautolog switches are gone.
+ * The AutoLogin1st option is gone.
+ * The position of the -debug and -logfile command line options is
+ irrelevant again. The -xrm option is back, but is ignored by the
+ KDE frontend.
+
+2002-08-28 Oswald Buddenhagen <ossi@kde.org>
+
+ * Made it possible to specify the color scheme for the greeter;
+ option ColorScheme
+
+2002-08-10 Oswald Buddenhagen <ossi@kde.org>
+
+ * Renamed kdmdesktop to krootimage, moved it back into the KDM source
+ tree, and changed its command line
+ * krootimage will be automatically invoked by the greeter by default;
+ option UseBackground
+ * Chucked out the [Desktop0] section from kdmrc. Instead, the
+ location of the config file containing such a section is specified
+ with the BackgroundRc option.
+ * User images can now be optionally fetched from the users's home
+ directories; option FaceSource
+ * The default of the KeyFile option is now empty again
+ * GreeterScreen now groks -2, meaning upper-right screen
+
+2002-08-06 Oswald Buddenhagen <ossi@kde.org>
+
+ * Automatically don't daemonize if started by init.
+
+2002-03-19 Oswald Buddenhagen <ossi@kde.org>
+
+ * The default Xsession will emulate the startup behaviour of sh/ksh
+ by sourcing /etc/profile and ~/.profile.
+
+2002-03-10 Oswald Buddenhagen <ossi@kde.org>
+
+ * Added InteractiveSd option. This is not really implemented yet,
+ so enabling it just makes KDM deny the existence of the shutdown
+ condition/timing options.
+ * The Setup script is now executed synchronously. Long-lasting
+ commands should be put in the background explicitly.
+
+2002-02-28 George Staikos <staikos@kde.org>
+
+ * GreeterScreen now groks -1, meaning upper-left screen.
+
+2002-01-14 Oswald Buddenhagen <ossi@kde.org>
+
+ * Added option NumLock {On,Off,Keep} to preset the NumLock modifier
+ state for the greeter
+
+2001-12-11 Oswald Buddenhagen <ossi@kde.org>
+
+ * Added AntiAliasing option to disable antialiasing in the greeter
+
+2001-11-30 Oswald Buddenhagen <ossi@kde.org>
+
+ * Added GreeterScreen option to put the greeter on a particular
+ screen in multi-headed setups.
+ * Changed the default of Language from "C" to "en_US"
+
+2001-11-22 Oswald Buddenhagen <ossi@kde.org>
+
+ * The defaults of the options Xservers, Session, Setup, Startup,
+ Reset and PidFile are now back to the saner XDM defaults,
+ so KDM works even without config files.
+ * Changed option ShowUsers: All -> NotHidden
+ * Renamed the option Users to SelectedUsers and NoUsers to HiddenUsers.
+ * The GUIStyle option now groks all installed widget styles.
+ Note that Motif+ and KDE are now called MotifPlus resp. Default.
+
+2001-11-02 Oswald Buddenhagen <ossi@kde.org>
+
+ * Added conditional/scheduled shutdown modes; options
+ DefaultSdMode and AllowSdForceNow; moved AllowShutdown from
+ [X-<dpy>-Greeter] to [X-<dpy>-Core].
+ * Added reserve display support; extension to Xservers.
+ * Added command FiFo support (see README); options FifoDir,
+ FifoGroup, [ShutDown]/AllowFifo, and [ShutDown]/AllowFifoNow.
+ FiFo location and capabilities are exported in $XDM_MANAGED.
+
+2001-10-04 Oswald Buddenhagen <ossi@kde.org>
+
+ * Xauth files are now created in AuthDir, not AuthDir/authdir.
+ Changed AuthDir default to /var/run/xauth.
+
+2001-07-12 Oswald Buddenhagen <ossi@kde.org>
+
+ * Renamed the option Xwilling to Willing
+ * The RandomFile option is not recognized on Linux and OpenBSD any
+ longer, as they have better entropy sources
+
+2001-07-10 Oswald Buddenhagen <ossi@kde.org>
+
+ * Added the tool 'genkdmconf'. It's supposed to create a suitable
+ configuration for KDM during 'make install' by merging new defaults
+ with a previous XDM/KDM config (if any is found).
+
+2001-07-03 Oswald Buddenhagen <ossi@kde.org>
+
+ * Added counterpart to the MinShowUID option: MaxShowUID
+
+2001-06-23 Oswald Buddenhagen <ossi@kde.org>
+
+ * Xauth files are now created in AuthDir/authfiles, not
+ AuthDir/authdir/authfiles
+
+2001-06-16 Oswald Buddenhagen <ossi@kde.org>
+
+ * Optionally put the cursor right in the "Password" field when
+ a user is preselected in the "Login" field; option FocusPasswd
+
+2001-06-15 Oswald Buddenhagen <ossi@kde.org>
+
+ * Replaced the ShutdownButton + ShutdownNeedsRoot option pair with
+ the AllowShutdown {None,Root,All} option
+
+2001-06-10 Oswald Buddenhagen <ossi@kde.org>
+
+ * The source directory structure changed entirely
+ * The argument to the -debug command line option is now a bit field;
+ the DebugLevel resource is gone
+ * The ErrorLogFile resource is gone
+ * The greeter module libKdmGreet.so has been converted to an
+ executable named kdm_greet; the external config parser is now
+ named kdm_config. The resource GreeterLib and the command line
+ option -getcfg (and -cfg2get) are gone, as the locations of the
+ config parser and greeter are derived from the location of the
+ kdm executable
+ * The config files are all located in ${kde_confdir}/kdm now; the
+ defaults for Setup & Session were adapted to this.
+ * The keys in kdmrc were reorganized:
+ - [KDM]/ShutdownButton -> [X-<dpy>-Greeter]/(ShutdownButton &
+ ShutdownNeedsRoot)
+ - [KDM]/Shutdown -> [Shutdown]/HaltCmd
+ - [KDM]/Restart -> [Shutdown]/RebootCmd
+ - [KDM]/LogoArea -> [X-*-Greeter]/ {None,Logo,Clock}
+ - remaining keys from [KDM] -> [X-*-Greeter]/
+ - [Lilo]/Lilo -> [Shutdown]/UseLilo
+ - [Lilo]/LiloCommand -> [Shutdown]/LiloCmd
+ - [Lilo]/LiloMap -> [Shutdown]/
+ - [Locale]/Language -> [X-*-Greeter]/ (Country key dropped)
+ * KDM will no longer use kdm-config; most of its resources were
+ absorbed into kdmrc:
+ - Servers -> [General]/Xservers
+ - RequestPort -> [Xdmcp]/(Port & Enable)
+ - DaemonMode -> [General]/
+ - PidFile -> [General]/
+ - LockPidFile -> [General]/
+ - AuthDir -> [General]/
+ - AutoRescan -> [General]/
+ - RemoveDomainname -> [Xdmcp]/
+ - KeyFile -> [Xdmcp]/
+ - AccessFile -> [Xdmcp]/Xaccess/
+ - ExportList -> [General]/
+ - RandomFile -> [General]/
+ - ChoiceTimeout -> [Xdmcp]/
+ - SourceAddress -> [Xdmcp]/
+ - Willing -> [Xdmcp]/Xwilling
+ - AutoLogin -> [General]/
+ - GrabServer -> [X-<dpy>-Greeter]/
+ - GrabTimeout -> [X-<dpy>-Greeter]/
+ - AuthComplain -> [X-<dpy>-Greeter]/
+ - AuthName -> [X-<dpy>-Core]/AuthNames
+ - NoPassUsers -> [X-<dpy>-Core]/ & [X-<dpy>-Core]/NoPassEnable
+ - AutoUser -> [X-<dpy>-Core]/(AutoLoginUser & AutoLoginEnable)
+ - AutoPass -> [X-<dpy>-Core]/AutoLoginPass
+ - AutoString -> [X-<dpy>-Core]/AutoLoginSession
+ - remaining server & session resources -> [X-<dpy>-Core]/
+ * In GreetString the HOSTNAME substitution was replaced with the
+ %%, %d, %h, %n, %s, %r & %m expandos
+ * EchoMode does not understand "NoStars" any more. Use "NoEcho".
+ * Defaults changed: AuthDir to /var/lib/kdm, KeyFile to
+ $kdm_confdir/kdmkeys, Xservers to $kdm_confdir/Xservers, Xaccess to
+ $kdm_confdir/Xaccess, Startup to $kdm_confdir/Xstartup, Reset to
+ $kdm_confdir/Xreset, removed kde2 from SessionTypes
+ * The previous user is now saved in $kdm_confdir/kdmsts, not kdmrc
+ * Added option SessSaveFile, defaulting to .wmrc
+ * Command line option changes:
+ - -server, -udpPort, -resources, -session and -xrm are gone
+ - -error is aliased to -logfile
+ - -debug and -error/-logfile must be specified first
+ - options are now accepted with both one and two leading dashes
+ * The default Xsession will now
+ - source ~/.xprofile if present
+ - try harder to determine what executable $1 corresponds to
+ - interpret "default" as ~/.xsession
+
+2001-03-19 Oswald Buddenhagen <ossi@kde.org>
+
+ * %DMNAME% and %DMPATH% are expanded in string resources;
+ changed the defaults for PidFile and ConfigFile accordingly -
+ the latter resulting in KDM now using kdm-config, NOT xdm-config
+ * Use external config parser to merge platform-specific (that is,
+ KDE-like) configuration data into the XDM resources; command line
+ options -getcfg (default %DMPATH%_getcfg) and -cfg2get (no default,
+ meaning kdm_getcfg will use $kde_confdir/kdmrc).
+ * Changed "kde" to "kde2" in the default SessionTypes
+ * Stolen idea for console mode handling from dtlogin; options
+ ConsoleMode and AllowConsoleMode are gone; extension to Xservers
+
+2001-01-19 Oswald Buddenhagen <ossi@kde.org>
+
+ * Added resources AllowRootLogin and AllowNullPasswd
+
+2001-01-15 Oswald Buddenhagen <ossi@kde.org>
+
+ * Renamed UserIDLow option to MinShowUID
+ * The LogoArea option now accepts the value "None"
+
+2001-01-13 Oswald Buddenhagen <ossi@kde.org>
+
+ * The GUIStyle option now works again and groks all of Qt's
+ built-in widget styles and the "KDE" style
+
+2001-01-11 Oswald Buddenhagen <ossi@kde.org>
+
+ * Added placing of the greeter at fixed coordinates; options
+ GreeterPosFixed, GreeterPosX, and GreeterPosY.
+ * Added "default" to the default SessionTypes
+
+2000-01-06 Oswald Buddenhagen <ossi@kde.org>
+
+ * Added option AllowConsoleMode
+
+2000-12-09 Oswald Buddenhagen <ossi@kde.org>
+
+ * Added auto-login; options AutoLoginEnable, AutoLoginUser &
+ AutoLogin1st; resources AutoUser, AutoPass, AutoString & AutoLogin1st
+ * Added password-less login; options NoPassEnable & NoPassUsers;
+ resource NoPassUsers
+ * Added auto-re-login on XServer crash; resource & option AutoReLogin
+
+ The kdmrc options and xdm-config resources are "ORed", i.e., if
+ either is enabled, the function is enabled.
+ The command line options -autolog/-noautolog and the resource AutoLogin
+ can be used to disable auto-login and password-less login at once.
+
+ * Added displaying the previously logged in user in the "Login"
+ field; option ShowPrevious. The previous user is saved in kdmrc,
+ section [Previous].
+
+2000-12-07 Oswald Buddenhagen <ossi@kde.org>
+
+ * New XDM port from XFree86 4.0.1
+ - new resources SourceAddress & Willing
+ - /authdir/authfiles is now automatically appended to AuthDir
+ * Default for PidFile and Setup changed back to empty
+ * Displays restarting too fast are disabled; resource StartInterval
+ * Option UserView, and NoUsers dependency on Users being empty
+ replaced with explicit option ShowUsers {All,Selected,None}
+ * Made the greeter dynamically loadable (libKdmGreet.so)
+ * Moved chooser and greeter to separate directories,
+ same for unused stuff (misc/)
+
+ * Added half-baked support for command FiFos; resources
+ FifoCreate, FifoGroup, FifoMode. Replaced on 2001-11-02
+
+2000-10-10 Steffen Hansen <hansen@kde.org>
+
+ * Made kdmdesktop read the [Desktop0] section from kdmrc instead
+ of kdmdesktoprc.
+
+2000-09-07 Waldo Bastian <bastian@kde.org>
+
+ * Make password echo mode configurable;
+ option EchoMode {OneStar,ThreeStars,NoEcho}
+
+2000-08-07 Christopher Molnar <molnar@kde.org>
+
+ * The minimal user ID to show in the user view can be specified now;
+ option UserIDLow.
+
+2000-06-04 Espen Sand <espen@kde.org>
+
+ * The logo area can now display either a clock or a pixmap;
+ option LogoArea {KdmClock,KdmLogo}
+
+1999-12-12 Jaromir Dolecek <jdolecek@NetBSD.org>
+
+ * Use OS-specific defaults for Shutdown & Restart
+ * Make PidFile, UserPath & SystemPath defaults on NetBSD match FreeBSD
+
+1999-11-17 Harald Hoyer <Harald.Hoyer@RedHat.de>
+
+ * Made kchooser
+
+1999-11-15 Matthias Hoelzer-Kluepfel <hoelzer@kde.org>
+
+ * kdmdesktop replaced with ../kdesktop/kdmdesktop. Uses new config
+ file (kdmdesktoprc) with new options (all in section [Desktop0]):
+
+1999-07-01 Steffen Hansen <hansen@kde.org>
+
+ * Xaccess now accepts NOBROADCAST
+
+1999-06-07 Matthias Hoelzer-Kluepfel <hoelzer@kde.org>
+
+ * Added next boot OS selection via LiLo; options [Lilo] Lilo,
+ LiloCommand & LiloMap
+ * Added button to switch to console mode; option [KDM] ConsoleMode
+
+1999-03-01 Stephan Kulow <coolo@kde.org>
+
+ * Option GUIStyle temporarily removed
+
+1998-10-08 Thomas Tanghus <tanghus@earthling.net>
+
+ * [KDMDESKTOP] option changes:
+ - BackgroundPictureTile, BackgroundPictureCenter, FancyBackground ->
+ BackgroundPictureMode {None,Tile,Center,Scale,
+ TopLeft,TopRight,BottomLeft,BottomRight,Fancy}
+ - add BackGroundColorMode {Plain,Horizontal,Vertical}
+ - BackgroundColor -> BackGroundColor1, BackGroundColor2
+
+1998-09-20 Hans Petter Bieker <bieker@kde.org>
+
+ * Change defaults:
+ - Setup: "" -> XDMDIR/Xsetup
+ - PidFile: "" -> FreeBSD: /var/run/kdm.pid, others: XDMDIR/kdm-pid
+ - Session: "XBINDIR/xterm -ls" -> XDMDIR/Xsession
+ - UserPath & SystemPath: no /usr/ucb for Linux & FreeBSD
+
+1998-09-11 Hans Petter Bieker <bieker@kde.org>
+
+ * Replace hard-coded paths with XBINDIR/XDMDIR in various defaults
+
+1998-09-06 Hans Petter Bieker <bieker@kde.org>
+
+ * Default Xsession now searches $1 in PATH and execs it
+
+1998-03-26 Stephan Kulow <coolo@kde.org>
+
+ * Nuke -kdedir cmdline option and Kdedir resource
+
+1997-09-09 Steffen Hansen <hansen@kde.org>
+
+ * Change defaults:
+ - AuthDir: XDMDIR/authDir -> XDMDIR/authdir
+
+1997-09-04 kdecvs
+
+ * Change defaults:
+ - AuthDir: XDMDIR -> XDMDIR/authDir
diff --git a/kdm/Makefile.am b/kdm/Makefile.am
new file mode 100644
index 000000000..106e47862
--- /dev/null
+++ b/kdm/Makefile.am
@@ -0,0 +1,20 @@
+SUBDIRS = . backend kfrontend
+
+PAM = @KDM_PAM_SERVICE@
+
+noinst_DATA=config.ci
+
+kdmdocdir = $(datadir)/doc/kdm
+kdmdoc_DATA = README
+
+install-data-local:
+ -@test -n "$(DESTDIR)" || test -z "$(PAM)" || { $(top_srcdir)/mkpamserv $(PAM) && $(top_srcdir)/mkpamserv -P $(PAM)-np; }
+
+config.ci: $(srcdir)/config.def $(srcdir)/confproc.pl
+ $(PERL) -w $(srcdir)/confproc.pl $(srcdir)/config.def $@
+
+CLEANFILES = config.ci
+
+### this is *only* for the kdm home page maintainer! ###
+hp:
+ scp README ChangeLog TODO devel-home:files/kdm
diff --git a/kdm/README b/kdm/README
new file mode 100644
index 000000000..bdf34814e
--- /dev/null
+++ b/kdm/README
@@ -0,0 +1,454 @@
+This is the K Display Manager (KDM) for KDE 3.4,
+the KDE replacement for the X Display Manager (XDM).
+
+Semi-official home page: http://devel-home.kde.org/~ossi/sw/kdm.html
+
+
+configure options that affect KDM
+---------------------------------
+
+--with-pam[=service]
+ Compile KDM (and other parts of kdebase) with PAM support. The default
+ service is "kde". PAM is automatically used if found.
+
+--with-kdm-pam=service
+ Override the PAM service used specifically by KDM. Depends on --with-pam.
+
+--with-shadow
+ Compile KDM (and other parts of kdebase) with shadow password support.
+ Shadow passwords are automatically used if found. This affects KDM only
+ if PAM is not used.
+
+--with-krb4[=path]
+ Compile KDM (and the LDAP KIO slave) with KTH Kerberos 4 support. Note
+ that this does not work with the Kerberos 4 compatibility layer found in
+ MIT Kerberos 5. This affects KDM only if PAM is not used.
+
+--with-afs
+ Compile KDM with AFS support. Depends on --with-krb4.
+
+--with-krb5auth[=path]
+--with-rpcauth
+ Compile KDM with Kerberos 5 resp. secure RPC support for X authorization
+ cookies. It's pretty pointless to enable this if you don't use an X server
+ that supports it.
+
+ If you want user authentication against a Kerberos realm, compile KDM with
+ PAM support and use the appropriate module.
+
+--without-xdmcp
+ Compile KDM without XDMCP support.
+
+--with-kdm-xconsole
+ Compile KDM with a builtin "xconsole" replacement in the greeter. I don't
+ consider this too useful, but SuSE wanted it, so it's there. ;)
+
+
+KDM's file system layout
+------------------------
+
+${kde_confdir} is usually ${prefix}/share/config
+${kde_datadir} is usually ${prefix}/share/apps
+The indented locations are envisioned for a configuration shared with GDM.
+
+${kde_confdir}/kdm/{kdmrc,Xservers,Xaccess,Xwilling,...}
+${kde_datadir}/kdm/sessions/*.desktop
+ /etc/X11/sessions/,/usr/share/xsessions/
+${kde_datadir}/kdm/pics/users/
+${kde_datadir}/kdm/pics/
+${kde_datadir}/kdm/faces/*.face{,.icon}
+ /usr/share/faces/
+/var/run/xauth/A*
+/var/run/xdmctl/xdmctl*
+/var/run/kdm.pid
+/var/lib/kdm/kdmsts
+<site-specific>/*.dmrc
+$HOME/.face{,.icon}
+$HOME/.dmrc
+
+
+How to setup KDM
+----------------
+
+KDM's config files are all located in ${kde_confdir}/kdm.
+"make install" will create a probably working configuration, either by
+deriving it from an already present KDM/XDM installation or by using
+defaults if no previous installation is found.
+
+You can change the configuration from the KDE Control Center. You will
+find the "Login Manager" module in the "System Administration" group.
+
+Have a look at README.pam in the kdebase top level directory if your
+system uses PAM.
+
+
+Configuring session types
+-------------------------
+
+The way session types are configured changed drastically in KDE 3.2.
+Session types are now represented by .desktop files in appropriate locations.
+The format of the .desktop files is (not yet) defined in the FreeDesktop.org
+desktop entry spec. Differences to "standard" .desktop files are:
+- the Type is fixed to XSession and can be omitted
+- the Encoding is fixed to UTF-8 and can be omitted
+- the Exec field will be passed to "eval exec" in a bourne shell; no macro
+ expansion is performed on it. "default", "custom" and "failsafe" are magic
+ constants that cause special actions.
+- Name, Comment, TryExec and Hidden are supported
+- the remaining keys have no meaning currently
+Session types are internally identified by filename (without extension);
+that's what will be saved to ~/.dmrc and what DESKTOP_SESSION will be set to.
+For every magic Exec constant a session type of the same name exists.
+
+Unless your system is configured differently already, you should create a
+directory ${kde_confdir}/kdm/sessions and add this to kdmrc:
+
+[X-*-Core]
+SessionsDirs=${kde_confdir}/kdm/sessions,${kde_datadir}/kdm/sessions
+
+(Note that you must use actual paths instead of variables, see the section
+about KDM's file system layout.)
+Do any changes only in the config directory - any changes in the data
+directory will be lost after the next KDE update.
+
+To override a session type, copy the .desktop file from the data dir to the
+config dir and edit it at will. Removing the shipped session types can be
+accomplished by "shadowing" them with .desktop files containing Hidden=true.
+For the magic session types no .desktop files exist by default, but KDM
+pretends they would, so you can override them like any other type.
+I guess you already know how to add a new session type by now. ;-)
+
+
+Running KDM from init
+---------------------
+
+NOTE, that this description applies to RedHat 5.x and must be adapted for
+other distributions/systems. Generally I'd advise _against_ starting KDM
+directly from init - better use a proper init script, possibly by slightly
+modifying the XDM init script shipped by your distribution.
+
+ Edit (as root) /etc/inittab.
+
+ Look for the line:
+
+ x:5:respawn:/usr/X11/bin/xdm -nodaemon
+
+ Replace it with:
+
+ x:5:respawn:/opt/kde/bin/kdm
+
+ This tells init(8) to respawn KDM, the KDE display manager, when
+ the system is in run level 5.
+ Note that KDM does not need the -nodaemon option.
+
+ To start KDM, either run (as root) /sbin/telinit 5 (to switch to
+ run level 5), or (this is risky! don't do it until you _know_ you
+ want the system to boot into this every time!) edit /etc/inittab
+ and change the line:
+
+ id:3:initdefault:
+
+ to
+
+ id:5:initdefault:
+
+ If you do the latter step, then every time your system boots
+ successfully it will go into run level 5 and run KDM,
+ presenting you the exceedingly cute KDE login screen.
+
+
+The command sockets
+-------------------
+
+This is a feature you can use to remote-control KDM. It's mostly intended
+for use by ksmserver and kdesktop from a running session, but other
+applications are possible as well.
+
+The sockets are UNIX domain sockets which live in subdirectories of the
+directory specified by FifoDir=. The subdir is the key to addressing and
+security; the sockets all have the file name "socket" and file permissions
+rw-rw-rw- (0666). This is because some systems don't care for the file
+permissions of the socket files.
+There are two types of sockets: the global one (dmctl) and the per-display
+ones (dmctl-<display>).
+The global one's subdir is owned by root, the subdirs of the per-display
+ones' are owned by the user currently owning the session (root or the
+logged in user). Group ownership of the subdirs can be set via FifoGroup=,
+otherwise it's root. The file permissions of the subdirs are rwxr-x--- (0750).
+
+The fields of a command are separated by tabs (\t), the fields of a list
+are separated by spaces, literal spaces in list fields are denoted by "\s".
+The command is terminated by a newline (\n).
+The same applies to replies. The reply on success is "ok", possibly followed
+by the requested information. The reply on error is an errno-style word (e.g.,
+"perm", "noent", etc.) followed by a longer explanation.
+
+Global commands:
+
+"login" display ("now"|"schedule") user password [session_arguments]
+ - login user at specified display. if "now" is specified, a possibly
+ running session is killed, otherwise the login is done after the
+ session exits.
+ session_arguments are printf-like escaped contents for .dmrc. Unlisted
+ keys will default to previously saved values.
+
+Per-display commands:
+
+"lock"
+ - The display is marked as locked. If the X-Server crashes in this state,
+ no auto-relogin will be performed even if the option is on.
+
+"unlock"
+ - Reverse the effect of "lock": re-enable auto-relogin.
+
+"suicide"
+ - The currently running session is forcibly terminated. No auto-relogin is
+ attempted, but a scheduled "login" command will be executed.
+
+Commands for all sockets:
+
+"caps"
+ - Returns a list this socket's capabilities:
+ "kdm" - identifies kdm, in case some other DM implements this protocol, too.
+ "list", "activate", "lock", "suicide", "login" - the respective command
+ is supported.
+ "bootoptions" - the "listbootoptions" command and the "=" option to
+ "shutdown" are supported.
+ "shutdown <list>" - "shutdown" is supported and allowed to the listed users
+ (comma-separated). "*" means all authenticated users.
+ "shutdown" - "shutdown" is supported and allowed to everybody.
+ "nuke <list>" - forced shutdown is allowed to the listed users.
+ "nuke" - forced shutdown is allowed to everybody.
+ "reserve <number>" - reserve displays are configured and <number> are
+ available at this time.
+
+"list" ["all"|"alllocal"]
+ - Return a list of running sessions. By default all active sessions are
+ listed. If "all" is specified, passive sessions are listed as well. If
+ "alllocal" is specified, passive sessions are listed as well, but all
+ incoming remote sessions are skipped.
+ Each session entry is a comma-separated tuple of:
+ - Display or TTY name
+ - VT name for local sessions
+ - Logged in user's name, empty for passive sessions and outgoing remote
+ sessions (local chooser mode)
+ - Session type or remote host for outgoing remote sessions, empty for
+ passive sessions
+ - A flag field:
+ - "t" for tty sessions
+ - "*" for the display belonging to the requesting socket
+ - "!" for sessions that cannot be killed by the requesting socket
+ - New flags might be added later
+ - New fields might be added later
+
+"reserve" [timeout in seconds]
+ - Start a reserve login screen. If nobody logs in within the specified amount
+ of time (one minute by default), the display is removed again. When the
+ session on the display exits, the display is removed, too.
+ - Permitted only on sockets of local displays and the global socket.
+
+"activate" (vt|display)
+ - Switch to a particular VT (virtual terminal). The VT may be specified
+ either directly (e.g., vt3) or by a display using it (e.g., :2).
+ - Permitted only on sockets of local displays and the global socket.
+
+"listbootoptions"
+ - List available boot options.
+ => "ok" list default current
+ default and current are indices into the list and are -1 if unset or
+ undeterminable.
+
+"shutdown" ("reboot"|"halt") ["="bootchoice] \
+ ("ask"|"trynow"|"forcenow"|"schedule"|\
+ start ("-1"|end ("force"|"forcemy"|"cancel")))
+ - Request a system shutdown, either a reboot or a halt/poweroff.
+ - An OS choice for the next boot may be specified from the list returned by
+ "listbootoptions".
+ - Shutdowns requested from per-display sockets are executed when the current
+ session on that display exits. Such a request may pop up a dialog asking
+ for confirmation and/or authentication.
+ - start is the time for which the shutdown is scheduled. If it starts with
+ a plus-sign, the current time is added. Zero means immediately.
+ - end is the latest time at which the shutdown should be performed if active
+ sessions are still running. If it starts with a plus-sign, the start time
+ is added. Minus one means wait infinitely. If end is through and active
+ sessions are still running, KDM can do one of the following:
+ * "cancel" - give up the shutdown.
+ * "force" - shut down nonetheless.
+ * "forcemy" - shut down nonetheless if all active sessions belong to the
+ requesting user. Only for per-display sockets.
+ - start and end are specified in seconds since the UNIX epoch.
+ - "trynow" is a synonym for "0 0 cancel", "forcenow" for "0 0 force" and
+ "schedule" for "0 -1".
+ - "ask" attempts an immediate shutdown and interacts with the user if active
+ sessions are still running. Only for per-display sockets.
+
+"shutdown" "cancel" ["local"|"global"]
+ - Cancel a scheduled shutdown. The global socket always cancels the currently
+ pending shutdown, while per-display sockets default to cancelling their
+ queued request.
+
+"shutdown" "status"
+ - Return a list with information about shutdowns.
+ The entries are comma-separated tuples of:
+ - ("global"|"local") - pending vs. queued shutdown. A local entry can be
+ returned only by a per-display socket.
+ - ("halt"|"reboot")
+ - start
+ - end
+ - ("ask"|"force"|"forcemy"|"cancel")
+ - Numeric user ID of the requesting user, -1 for the global socket.
+ - The next boot OS choice or "-" for none.
+ - New fields might be added later.
+
+There are two ways of using the sockets:
+- Connecting them directly. FifoDir is exported as $DM_CONTROL; the name
+ of per-display sockets can be derived from $DISPLAY.
+- By using the kdmctl command (e.g., from within a shell script).
+ Try "kdmctl -h" to find out more.
+
+Here is an example bash script "reboot into FreeBSD":
+
+if kdmctl | grep -q shutdown; then
+ IFS=$'\t'
+ set -- `kdmctl listbootoptions`
+ if [ "$1" = ok ]; then
+ fbsd=$(echo "$2" | tr ' ' '\n' | sed -ne 's,\\s, ,g;/freebsd/I{p;q}')
+ if [ -n "$fbsd" ]; then
+ kdmctl shutdown reboot "=$fbsd" ask > /dev/null
+ else
+ echo "FreeBSD boot unavailable."
+ fi
+ else
+ echo "Boot options unavailable."
+ fi
+else
+ echo "Cannot reboot system."
+fi
+
+
+"It doesn't work!!"
+-------------------
+
+More input! ;-)
+
+KDM accepts two command line options related to logging:
+
+ -debug <n>
+ <n> is a decimal or hexadecimal (prefix 0x) number.
+ The number is a bitfield, i.e., it is formed by summing up the
+ required values from this table:
+ 1 (0x1) - core debugging. Probably the most useful one.
+ 2 (0x2) - config reader debugging.
+ 4 (0x4) - greeter debugging.
+ 8 (0x8) - IPC debugging. This logs _all_ communication between the
+ core, the config reader and the greeter - including the
+ passwords you type, so edit the log before showing it to
+ somebody.
+ This attempts to synchronize the processes to interleave the
+ log messages optimally, but will probably fail unless you use
+ -debug 0x80 as well.
+ 16 (0x10) - wait after forking session sub-daemon.
+ 32 (0x20) - wait after starting config reader.
+ 64 (0x40) - wait after starting greeter.
+ The wait options are only useful if you need to attach a debugger
+ to a process, but it crashes before you are able to do so without
+ the delay. See below.
+ 128 (0x80) - don't use syslog for internally generated messages.
+ 256 (0x100) - core Xauth debugging.
+ 1024 (0x400) - run config reader and greeter through valgrind.
+ 2048 (0x800) - run config reader and greeter through strace.
+
+ Logs from "-debug 7" are usually a good start.
+
+ -logfile <file>
+ <file> is the file to log various messages to. The default log file is
+ /var/log/kdm.log. For internal reasons there is no option in kdmrc to
+ permanently specify the log file location. If you redirect KDM's
+ standard error output to a file, KDM will log there.
+ If KDM is configured to use syslog (and it _very_ probably is on any
+ modern system), all internally generated messages are logged to the
+ "daemon" facility. The log usually can be found in /var/log/debug.log
+ and /var/log/daemon.log; make sure that daemon.* is logged (look at
+ /etc/syslog.conf).
+ If you have problems logging in and your system uses PAM (also quite
+ probable on modern systems), the "auth" and "authpriv" syslog facilities
+ are interesting, too.
+
+Send me all the logs together with a detailed description of what you did
+and what happened. If your problem is related to a specific configuration,
+you should also attach a tar.gz archive of your KDM config directory.
+
+If I request a backtrace from you and KDM didn't create one yet via the
+usual drkonqi procedure, you'll have to do that yourself. The keyphrase
+is "attaching gdb". How exactly this is done depends on the part that
+crashes:
+- master daemon. Actually you should never need to attach to it, as
+ you can start it within the debugger already:
+ # gdb --args kdm -nodaemon -debug 7
+ (gdb) run
+- display subdaemon. Find (using ps) the process with a name like
+ "-:0" (where :0 is actually the display this process is for). This
+ process' PPID is the master daemon. Attach to it this way:
+ # gdb kdm <the PID you found>
+ (gdb) cont
+ If the subdaemon crashes before you can attach, add 16 to the debug flags
+ when you start KDM.
+- config reader. You will have to add 32 to the debug flags almost certainly.
+ The PPID will be the master daemon as well.
+ # gdb kdm_config $(pidof kdm_config)
+ (gdb) cont
+- greeter. If it's too fast, add 64 to -debug. The PPID will be the subdaemon.
+ # gdb kdm_greet $(pidof kdm_greet)
+ (gdb) cont
+ The simplification with "pidof" works only if you have only one display,
+ otherwise you have to find the PID manually (by using ps -fx).
+Once you got gdb attached to the offending process, do whatever is needed
+to make it crash (probably nothing, if you had to use a delay parameter).
+Once it crashed, gdb will tell you a signal name, like SIGSEGV - that's the
+first interesting part for me. Then you have to create the actual backtrace:
+ (gdb) bt
+The output of this command is interesting for me.
+I might request a backtrace even if nothing crashes, but instead hangs. In
+this case don't use "cont" after attaching, but use "bt" right away. If the
+process is already running, interrupt it with ctrl-c.
+For obvious reasons you have to run gdb on a different virtual terminal than
+the X server. To get there, press alt-ctrl-f1 and log in as root. To
+switch to the X server's vt, press alt-ctrl-f7 (the exact function key may
+be different on your system). You may also use a remote login from a
+second machine. In any case it is advantageous to have mouse support on the
+debugging console for copying the backtrace.
+Note that a backtrace is usually _much_ more useful if the binary contains
+debugging info, so you should install from source with the --enable-debug
+configure flag if at all possible.
+
+
+Random rambings and license information
+---------------------------------------
+
+Version 0.1 of KDM is copyright
+ Matthias Ettrich <ettrich@trolltech.com>
+All later versions copyright:
+ (C) 1997-2000 Steffen Hansen <hansen@kde.org>
+Since version 0.90 (KDE 2.1) copyright:
+ (C) 2000-2003 Oswald Buddenhagen <ossi@kde.org>
+
+The files in the backend directory are licensed under the X licence
+(see http://www.x.org/Downloads_terms.html for more info).
+The files in the kfrontend directory are licensed under the GNU GPL.
+
+Thanks to (in no particular order):
+Michael Bach Jensen and Torsten Rahn for drawing icons.
+Duncan Haldane for investigation of PAM issues.
+Stephan Kulow for helping with the autoconf stuff.
+Martin Baehr for intensive testing and writing the sample Xsession scripts.
+Harald Hoyer <Harald.Hoyer@redhat.de> for the (now obsoleted) chooser.
+SuSE for employing me (ossi) for three months to work on kdm.
+BasysKom for sponsoring my (ossi's) work on the conversation plugin stuff.
+... and _many_ others ...
+
+
+--
+Have fun with it (and feel free to comment),
+
+ Oswald Buddenhagen <ossi@kde.org>
diff --git a/kdm/TODO b/kdm/TODO
new file mode 100644
index 000000000..12a1ec545
--- /dev/null
+++ b/kdm/TODO
@@ -0,0 +1,243 @@
+theming (#37349):
+- maybe add a Themable plugin flag. if not set and no talker, abort.
+- minor: show QWidgets only when the layout is ready and the theme was painted.
+ but one can't hide the widgets in a QLayout, as they have no size then.
+- add attribute inheritance. apply attributes extracted from particular
+ elements of the (hidden) talker.
+- make plugin return a QDom instead of embedding a QLayoutItem (QLabels look
+ just awful in the themed greeter). big problem: there is no KdmGrid ... try
+ to (ab)use QLayout.
+- extract background from theme. use explicit node-id "background", i think.
+- automatic talker node detection/creation. same for background, possibly.
+
+- remote login can have the chosen host as the sessName
+- popup menu grabs keyboard. that means it is ungrabbed afterwards ...
+- error label uses fixed colors. red might be ok, but not black.
+
+- message after switching to text mode
+
+- handle non-linux VTs:
+ on systems without VT_GETSTATE, try activating all consoles in turn to
+ find free ones. wow, this sucks so much.
+ - BSD: 1st: pcvt, /dev/ttyC[0] (OpenBSD), /dev/ttyv[0] (other),
+ also emulated by wscons on /dev/ttyE.
+ 2nd: syscons, /dev/ttyv[0], fallback /dev/vga
+ - Lynx, /dev/atc[0]
+ - Solaris, /dev/vt[00]
+ - SVR4, /dev/vc[00] (ESIX), /dev/vt[00] (other)
+ - SCO, /dev/tty[00], query current with CONS_GETINFO, counts 0-based
+ ref: xorg/programs/Xserver/hw/xfree86/os-support/xf86_OSlib.h
+- act on BSD_INIT
+- before nuking X server on other vt, save current vt and restore it before
+ disallocating server vt. or just make the xserver not switch wildly.
+
+- possibly parse Xserver log to find failure cause. this is very hacky.
+
+- try harder to get rid of processes, see X servers failure cleanup path
+
+- make auto-re-login a per-user option; save in .dmrc.
+
+- add Xserver option set selection (#56329)
+- add support for XRandR (#48602)
+save these options to .dmrc?
+
+- per-display sections in .dmrc. read-only, as far as kdm is concerned, as
+ otherwise the GUI would become insanely complex.
+
+- make config position independent
+- parse /etc/kderc?
+- merge multiple kdmrcs in the style of kconfig. how to set section priorities?
+- genkdmconf: treat backgroundrc as an ini file, not as a text blob
+- add proper quoting and dequoting to genkdmconf ini parser & writer
+
+- write generic conversation plugin
+- write modern conv plugin. or maybe this should be a parallel vs. serial
+ setting of the classic plugin?
+
+- actually implement the libpam_client support
+
+- check if pam works before trying to authenticate
+- test whether nis, kerberos4 & kerberos5 work
+- sync BSD_AUTH from xdm, sync osfc2 from kcheckpass
+
+- swap pam_setcred and pam_open_session order.
+- check how the system specific functions like setpcred (AIX) and
+ setusercontext (BSD) combine with pam_setcred.
+
+- Move clock from greeter dialog to desktop
+- add more clock types (#18178)
+- add icons to action menu. icon theme selection!
+
+- Add XDMCP _client_ to core (for remote login like in dtlogin).
+ Currently this is done by simply restarting the x-server with -query.
+
+- add login restrictions for reserve displays (#59353)
+
+- possibly do the authentication for the reserve display on the display it
+ is launched from (relates #59353)
+
+- remote-accessible command sockets for remote shutdown, etc.
+ or maybe implement it as an xdmcp extension?
+- LoginMode=DirectQuery
+
+- "XDMCP over FiFo" - or at least a "manage <dpy> [<xauth>]" command
+- the per-display sockets are in fact nonsense; gdm's approach is better
+
+- add bgset to XDM_MANAGED
+ add FiFo command "background\t{inprogress,aborted,done}"
+
+- lilo boot option <default>, i.e., -R with no argument
+- support lilo -A mode
+
+- support sleep/suspend in the shutdown menu. should this be really treated
+ like a shutdown? (#33839)
+
+- add language selection (export as LC_*). kde should respect this until the
+ language is explicitly configured. and later? option "use system setting"?
+ integrate with $KDE_LANG somehow. (#55379, #63804)
+- add keymap selection (via xkb) (#51245, #64642)
+for both, one would preset a list of available options and make one entry
+the greeter's own setting. explicitly setting it sets it for both the greeter
+and the session. .dmrc later affects only the session, not the greeter.
+
+- handle failsafe internally, take care of focus. see #32973
+
+- TryExec for "custom" session type. always show the entry, but disable it
+ if it is unavailable for the selected user.
+
+- cursor theming support via Xcursor (#66829)
+
+- add screensaver (#41941)
+- support DPMS (#18597)
+
+- add a minimalistic window manager to the greeter (#17716, #51039)
+
+- write a separate configurator application, as kcontrol does not scale well
+ enough to cover all of kdm's options.
+
+- Different logos for each session type (see #74500)
+- User pictures in logo field
+- display user's .plan/.project (or .person? .userinfo?) in the greeter?
+ text area/label would suck -> tooltip?
+
+- allow disabling full names or login names in userview (#54110)
+- user list loading in the background (after first few to get a reasonable
+ width estimate)
+
+- faking session parameters (type, language, etc.) of nonexistent users based
+ on statistical analysis of actual users ... severe overkill!?
+
+- export password to the startup/session scripts. somehow ... (#35396)
+
+- maybe reset CapsLock in the greeter. there is some CapsLock vs. ShiftLock
+ confusion, though.
+
+- maybe add kiosk mode: the user and his options are preset and locked in
+ the greeter. i doubt it's usefulness, though.
+
+- make builtin xconsole hideable; it should free the device when invisible.
+ possibly auto-hide it on vt switch - see kdesktop_lock for the x event
+ handling.
+
+- ssh-agent/gpg-agent integration (#44177, #65709)
+
+- lbxproxy integration (tell ghakko)
+
+- in kcm_kdm, detach backgroundrc change status from kdmrc change status.
+
+- when a shutdown is scheduled, don't remove all login possibilities.
+ instead, display a warning in the greeter. use SIGUSR1 to notify already
+ running greeters about changes.
+- user notification about scheduled shutdown (and cancelled forced shutdown):
+ - wall
+ - greeter popup
+ - d-bus message. this would be best, particularly because screen savers
+ would need no special handling then.
+- maybe bomb DefaultSdMode, save in state file instead. compare with ksmserver.
+
+- gdm changelog indicates that PAM sometimes
+ - continues despite PAM_CONV_ERR
+ - asks user name twice
+- gdm avoids the PAM_MESSAGE message box vs. prompt problem by displaying
+ everything in one "error area". all messages are simply appended; an empty
+ message clears the area.
+- gdm stops cursor blinking on not used (remote) displays after 20 secs to
+ save bandwidth.
+
+internal stuff:
+- improve signal handling in the subdaemon, it's incredibly racy (GOpen/GClose).
+ depends on proper main loop.
+ alternative extreme measure: launch greeter from master daemon?
+- the process reaping from GClose should be in sync with the main loop.
+- kill warning on AIX - see bug #13628 (really present?)
+- implement auto-re-login by keeping the display subdaemon alive instead
+ of starting a new one and feeding it the old auth data.
+- options for running the greeter and the core unprivileged. problem: xauth.
+- rethink the coupling of the kdm components, particularily the config reader.
+ options:
+ - keep things basically as-is, make the Xaccess interface even more flexible,
+ add capability flags.
+ - as previous, but don't use #defines, but textual constants. even more
+ flexible, but slower, bigger, no compile-time checking, and the typing
+ system would have to be more core-based. keys in the rc are considered
+ invalid if they were not queried.
+ - completely opposite: no explicit queries, but hard-code everything. that
+ kills the idea of having one backend binary for multiple frontends, but
+ that's a BlueSkyDream anyway.
+ following that path, the config reader could be nuked at all.
+
+ralf says:
+- put the kmenu sidebar image on the left of the greeter
+- enable the clock by default
+
+thoughts (not really todo):
+- PAM sucks. big time.
+ historically, it is completely incapable of operating in event-driven contexts
+ when it comes to non-console authentication schemes. the module just hangs in
+ pam_sm_authenticate() (pam_authenticate() to the outside), waiting for input
+ from its device.
+ then came linux-pam 0.58, introducing PAM_BINARY_{MSG,PROMPT} to the
+ conversation function interface. no conversation function could handle the
+ binary prompts generically, of course. so came linux-pam 0.63 with a client
+ library that would add another layer of indirection, so the conversation
+ function could simply call into it and it would do whatever was configured
+ by the admin. and everbody was happy, right? wrong! i've yet to see a single
+ module (except for the demo module in linux-pam, of course) that actually
+ uses this feature. not to mention the non-existing portability (you don't
+ seriously expect TOG to extend the PAM standard within the next decade, do
+ you?). so we're right where we started from.
+ this imposes problems in two use cases:
+ - cancelling authentication alltogether. this happens when the user changes
+ the authentication method or when the greeter exits for some reason. if
+ the process waits in the conversation function, it can simply return
+ PAM_CONV_ABORT. if the module hangs, we're screwed.
+ - suspending authentication. this is needed for shutdowns that need auth.
+ if the module hangs, we're screwed, of course. if we're waiting in the
+ conversation function, we have three options: 1) just abort the auth
+ cycle and start a new one. this is what is done currently. 2) just open
+ a second pam handle and authenticate with it, all from within the "outer"
+ pam_authenticate(). if we're lucky, no involved modules use static variables
+ and things work out. 3) linux-pam 0.65 introduced the following: the
+ conversation function can return PAM_CONV_AGAIN. this in turn makes the
+ module and consequently libpam return PAM_INCOMPLETE, requesting the
+ application to call the resp. libpam function again. in theory this
+ guarantees that authentication with a second pam handle is safe. of course,
+ PAM_INCOMPLETE is just as popular and thus useful as PAM_BINARY_PROMPT.
+ we could just longjmp() out of hanging modules from a signal handler.
+ however, this might lead to resource leaks and even leave us with an unstable
+ libpam. killing the hanging process seems like the most viable solution.
+ however, for this we first need to make the greeter a child of the master
+ daemon. also, the display sub-daemon (which happens to do the main auth.)
+ is responsible for keeping the initial X connection open. killing it would
+ terminate the session according to the XDMCP spec. other issues are probable.
+- multiple conv. plugins could be used in a row, each serving a pam module.
+ the plugins would have to detect that it's their turn by filtering messages
+ and prompts.
+- consider making the menu an actions-only menu again and put an "options >>"
+ button somewhere. relates #63401, #61492
+- pipe .xsession-errors through the daemon and put a size limit on it.
+ remove old logs in disk-full situation.
+- set LC_ALL in the backend for i18n-capable PAM libs - does one exist?
+
+
+last sync with XFree86 HEAD: 2004-04-02
diff --git a/kdm/backend/Imakefile b/kdm/backend/Imakefile
new file mode 100644
index 000000000..75efcb45a
--- /dev/null
+++ b/kdm/backend/Imakefile
@@ -0,0 +1,203 @@
+/* well, we have no subdirs ...
+#define PassCDebugFlags 'CDEBUGFLAGS=$(CDEBUGFLAGS)'
+*/
+
+#ifdef DEBUG
+CDEBUGFLAGS := $(CDEBUGFLAGS) -g
+#endif
+
+#ifndef BuildBoth
+#define BuildBoth (defined(LinuxArchitecture) && !UseElfFormat)
+#endif
+
+#ifndef LinuxShadowSuite
+#define LinuxShadowSuite NO
+#endif
+
+#if FSUseSyslog
+LOG_DEFINES = -DUSE_SYSLOG
+#endif
+
+#ifdef NoXDMCP
+XDMCPLIB =
+#else
+XDMCP_DEFINES = -DXDMCP
+#endif
+
+#if HasXdmAuth
+XDMAUTH_DEFINES = -DHASXDMAUTH
+XDMAUTHOBJS = xdmauth.o
+XDMAUTHSRCS = xdmauth.c
+#endif
+
+#if HasSecureRPC
+RPC_DEFINES = -DSECURE_RPC
+RPCOBJS = rpcauth.o
+RPCSRCS = rpcauth.c
+RPCLIB = -lrpcsvc
+#endif
+
+#if HasKrbIV
+#if NOAFS
+KRBIV_DEFINES = KrbIVDefines -DNO_AFS
+#else
+KRBIV_DEFINES = KrbIVDefines
+#endif
+KRBIV_INCLUDES = KrbIVIncludes
+KRBIVLIB = KrbIVLibraries
+#endif
+
+#if HasKrb5
+KRB5_DEFINES = Krb5Defines
+KRB5_INCLUDE = Krb5Includes
+KRB5OBJS = krb5auth.o
+KRB5SRCS = krb5auth.c
+#endif
+
+/* This is correct for Linux and FreeBSD */
+#if HasPam
+PAM_LIBRARIES = PamLibraries
+PAM_DEFINES = -DUSE_PAM
+#endif
+
+#if HasPam
+#undef HasShadowPasswd
+#define HasShadowPasswd NO
+#undef HasLibCrypt
+#define HasLibCrypt NO
+#endif
+
+/*
+#if HasBSDAuth
+BSDAUTH_DEFINES = -DUSE_BSDAUTH
+#endif
+*/
+
+#if SystemV4 || HasShadowPasswd
+
+#if !LinuxShadowSuite
+PWD_DEFINES = -DUSESHADOW
+#else
+PWD_DEFINES = -DUSESHADOW -DSHADOWSUITE
+#endif
+
+#if !defined(i386IscArchitecture) && !defined(i386ScoArchitecture) && !defined(LinuxArchitecture) && !defined(NTOArchitecture) && !defined(SGIArchitecture)
+SYS_LIBRARIES3 = -lresolv
+#endif
+#if SystemV || defined(SequentArchitecture)
+SYS_LIBRARIES1 = -lsec
+#endif
+#if defined(LinuxArchitecture) && (!UseElfFormat || LinuxShadowSuite)
+SYS_LIBRARIES1 = -lshadow
+#endif
+
+#endif
+
+#if defined(UltrixArchitecture)
+SYS_LIBRARIES1 = -lauth
+#endif
+
+#if (defined(AIXArchitecture) && (OSMajorVersion >= 3))
+SYS_LIBRARIES1 = -ls
+#endif
+
+#if HasLibCrypt
+#ifdef SpecialLibCrypt
+CRYPT_LIBRARIES = SpecialLibCrypt
+#else
+CRYPT_LIBRARIES = -lcrypt
+#if defined(LynxOSArchitecture)
+CRYPT_DEFINES = -DHAS_CRYPT
+#endif
+#endif
+#endif
+
+#if HasBSD44Sockets
+SOCK_DEFINES = -DBSD44SOCKETS
+#endif
+
+#if defined(i386Architecture) || defined(AmigaArchitecture)
+FRAGILE_DEFINES = -DFRAGILE_DEV_MEM
+#endif
+
+#ifdef RandomDefines
+RANDOM_DEFINES = RandomDefines
+#elif defined(OpenBSDArchitecture)
+RANDOM_DEFINES = -DARC4_RANDOM
+#elif defined(LinuxArchitecture)
+RANDOM_DEFINES = -DDEV_RANDOM=\"/dev/urandom\"
+#elif defined(NetBSDArchitecture) && \
+ ((OSMajorVersion > 1) || \
+ (OSMajorVersion == 1 && OSMinorVersion > 3))
+RANDOM_DEFINES = -DDEV_RANDOM=\"/dev/urandom\"
+#endif
+
+
+#if HasSetUserContext
+USER_CONTEXT_DEFINES = -DHAS_SETUSERCONTEXT
+# XXX - only FreeBSD has this in libutil
+SYS_LIBRARIES1 = -lutil
+#endif
+
+#if HasSetProcTitle
+PROCTITLE_DEFINES = -DHAS_SETPROCTITLE
+#endif
+
+ SYS_LIBRARIES = $(SYS_LIBRARIES1) $(SYS_LIBRARIES2) $(SYS_LIBRARIES3)
+
+ INCLUDES = $(KRB5_INCLUDE)
+ DEPLIBS = $(DEPXLIB) $(DEPXAUTHLIB) $(DEPXDMCPLIB)
+ LOCAL_LIBRARIES = $(XLIB) $(XAUTHLIB) \
+ $(XDMCPLIB) $(RPCLIB) $(PAM_LIBRARIES) \
+ $(CRYPT_LIBRARIES) $(KRBIVLIB)
+
+ COMMSRCS = auth.c daemon.c server.c dpylist.c dm.c error.c \
+ netaddr.c reset.c resource.c protodpy.c policy.c \
+ session.c socket.c streams.c util.c xdmcp.c \
+ process.c mitauth.c \
+ genauth.c access.c choose.c \
+ $(XDMAUTHSRCS) $(RPCSRCS) $(KRB5SRCS)
+ COMMOBJS = auth.o daemon.o server.o dpylist.o dm.o error.o \
+ netaddr.o reset.o resource.o protodpy.o policy.o \
+ session.o socket.o streams.o util.o xdmcp.o \
+ process.o mitauth.o \
+ genauth.o access.o choose.o \
+ $(XDMAUTHOBJS) $(RPCOBJS) $(KRB5OBJS)
+
+ SRCS1 = $(COMMSRCS) client.c
+ OBJS1 = $(COMMOBJS) client.o
+
+#if BuildBoth
+ SRCS2 = $(COMMSRCS) clientsh.c
+ OBJS2 = $(COMMOBJS) clientsh.o
+
+ XDM_SHADOW = xdm-shadow
+#endif
+
+ PROGRAMS = xdm $(XDM_SHADOW)
+
+
+ OSMAJORVERSION = OSMajorVersion
+ OSMINORVERSION = OSMinorVersion
+ CONN_DEFINES = $(CONNECTION_FLAGS)
+ DEFINES = $(SIGNAL_DEFINES) $(LOG_DEFINES) \
+ $(CRYPT_DEFINES)$(PWD_DEFINES) \
+ $(BSDAUTH_DEFINES) $(PAM_DEFINES) $(USER_CONTEXT_DEFINES) \
+ $(XDMAUTH_DEFINES) $(RPC_DEFINES) $(KRB5_DEFINES) \
+ $(XDMCP_DEFINES) $(SOCK_DEFINES) $(CONN_DEFINES) \
+ $(FRAGILE_DEFINES) $(RANDOM_DEFINES) $(PROCTITLE_DEFINES) \
+ -DOSMAJORVERSION=$(OSMAJORVERSION) -DOSMINORVERSION=$(OSMINORVERSION) \
+ -Dconst=
+
+ComplexProgramTarget_1(xdm,$(LOCAL_LIBRARIES),NullParameter)
+#if BuildBoth
+NormalProgramTarget(xdm-shadow,$(OBJS2),$(DEPLIBS),$(LOCAL_LIBRARIES),-lshadow)
+InstallProgram(xdm-shadow,$(BINDIR))
+ObjectFromSpecialSource(clientsh,client,-DUSESHADOW)
+#endif
+
+#if defined(FreeBSDArchitecture) && (OSMajorVersion < 2)
+XCOMM only for daemon.c? it's used in some other places, too.
+SpecialCObjectRule(daemon,$(ICONFIGFILES),-UCSRG_BASED)
+#endif
+
diff --git a/kdm/backend/Makefile.am b/kdm/backend/Makefile.am
new file mode 100644
index 000000000..81dc2702f
--- /dev/null
+++ b/kdm/backend/Makefile.am
@@ -0,0 +1,46 @@
+# forcibly remove thread-related defines & flags
+AUTOMAKE_OPTIONS = foreign
+CPPFLAGS = $(USER_INCLUDES) $(X_INCLUDES) $(KRB4_INCS) $(KRB5_INCS) -I.. -I../..
+LDFLAGS = $(USER_LDFLAGS) $(X_LDFLAGS) $(X_RPATH) $(KRB4_RPATH) $(KRB5_RPATH)
+LDADD = $(LIB_X11) -lXau $(LIBXDMCP) $(PASSWDLIBS) $(LIBSHADOW) $(LIBGEN) \
+ $(LIB_LIBS) $(KRB4_LIBS) $(KRB5_LIBS) $(LIBSOCKET) $(LIBRESOLV) \
+ $(LIBUCB) $(LIBUTIL) $(LIBPOSIX4)
+
+bin_PROGRAMS = kdm
+kdm_SOURCES = \
+ access.c \
+ auth.c \
+ bootman.c \
+ choose.c \
+ client.c \
+ ctrl.c \
+ daemon.c \
+ dm.c \
+ dpylist.c \
+ error.c \
+ genauth.c \
+ inifile.c \
+ krb5auth.c \
+ mitauth.c \
+ netaddr.c \
+ policy.c \
+ process.c \
+ protodpy.c \
+ reset.c \
+ resource.c \
+ rpcauth.c \
+ server.c \
+ session.c \
+ sessreg.c \
+ socket.c \
+ streams.c \
+ util.c \
+ xdmauth.c \
+ xdmcp.c
+
+EXTRA_DIST = printf.c
+
+noinst_HEADERS = dm.h dm_socket.h dm_error.h dm_auth.h greet.h
+
+# for unsermake (automake is handled by SUBDIRS in ../)
+kdm_COMPILE_FIRST = ../config.ci
diff --git a/kdm/backend/access.c b/kdm/backend/access.c
new file mode 100644
index 000000000..82736be57
--- /dev/null
+++ b/kdm/backend/access.c
@@ -0,0 +1,468 @@
+/*
+
+Copyright 1990, 1998 The Open Group
+Copyright 2001,2004 Oswald Buddenhagen <ossi@kde.org>
+Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the copyright holder.
+
+*/
+
+/*
+ * xdm - display manager daemon
+ * Author: Keith Packard, MIT X Consortium
+ *
+ * Access control for XDMCP - keep a database of allowable display addresses
+ * and (potentially) a list of hosts to send ForwardQuery packets to
+ */
+
+#include <config.h>
+
+#ifdef XDMCP
+
+#include "dm.h"
+#include "dm_error.h"
+#include "dm_socket.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include <netdb.h>
+#if defined(IPv6) && defined(AF_INET6)
+# include <arpa/inet.h>
+#endif
+
+typedef struct {
+ short int type;
+ union {
+ char *aliasPattern;
+ char *hostPattern;
+ struct _displayAddress {
+ CARD16 connectionType;
+ ARRAY8 hostAddress;
+ } displayAddress;
+ } entry;
+} HostEntry;
+
+typedef struct {
+ short int iface;
+ short int mcasts;
+ short int nmcasts;
+} ListenEntry;
+
+typedef struct {
+ char *name;
+ short int hosts;
+ short int nhosts;
+} AliasEntry;
+
+typedef struct {
+ short int entries;
+ short int nentries;
+ short int hosts;
+ short int nhosts;
+ short int flags;
+} AclEntry;
+
+typedef struct {
+ HostEntry *hostList;
+ ListenEntry *listenList;
+ AliasEntry *aliasList;
+ AclEntry *acList;
+ short int nHosts, nListens, nAliases, nAcls;
+ CfgDep dep;
+} AccArr;
+
+static AccArr accData[1];
+
+
+static ARRAY8 localAddress;
+
+ARRAY8Ptr
+getLocalAddress( void )
+{
+ static int haveLocalAddress;
+
+ if (!haveLocalAddress) {
+#if defined(IPv6) && defined(AF_INET6)
+ struct addrinfo *ai;
+
+ if (getaddrinfo( localHostname(), NULL, NULL, &ai )) {
+ XdmcpAllocARRAY8( &localAddress, 4 );
+ localAddress.data[0] = 127;
+ localAddress.data[1] = 0;
+ localAddress.data[2] = 0;
+ localAddress.data[3] = 1;
+ } else {
+ if (ai->ai_family == AF_INET) {
+ XdmcpAllocARRAY8( &localAddress, sizeof(struct in_addr) );
+ memcpy( localAddress.data,
+ &((struct sockaddr_in *)ai->ai_addr)->sin_addr,
+ sizeof(struct in_addr) );
+ } else /* if (ai->ai_family == AF_INET6) */ {
+ XdmcpAllocARRAY8( &localAddress, sizeof(struct in6_addr) );
+ memcpy( localAddress.data,
+ &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr,
+ sizeof(struct in6_addr) );
+ }
+ freeaddrinfo( ai );
+#else
+ struct hostent *hostent;
+
+ if ((hostent = gethostbyname( localHostname() ))) {
+ XdmcpAllocARRAY8( &localAddress, hostent->h_length );
+ memmove( localAddress.data, hostent->h_addr, hostent->h_length );
+#endif
+ haveLocalAddress = 1;
+ }
+ }
+ return &localAddress;
+}
+
+
+void
+ScanAccessDatabase( int force )
+{
+ struct _displayAddress *da;
+ char *cptr;
+ int nChars, i;
+
+ Debug( "ScanAccessDatabase\n" );
+ if (Setjmp( cnftalk.errjmp ))
+ return; /* may memleak */
+ if (startConfig( GC_gXaccess, &accData->dep, force ) <= 0)
+ return;
+ if (accData->hostList)
+ free( accData->hostList );
+ accData->nHosts = GRecvInt();
+ accData->nListens = GRecvInt();
+ accData->nAliases = GRecvInt();
+ accData->nAcls = GRecvInt();
+ nChars = GRecvInt();
+ if (!(accData->hostList = (HostEntry *)
+ Malloc( accData->nHosts * sizeof(HostEntry) +
+ accData->nListens * sizeof(ListenEntry) +
+ accData->nAliases * sizeof(AliasEntry) +
+ accData->nAcls * sizeof(AclEntry) +
+ nChars )))
+ {
+ CloseGetter();
+ return;
+ }
+ accData->listenList = (ListenEntry *)(accData->hostList + accData->nHosts);
+ accData->aliasList = (AliasEntry *)(accData->listenList + accData->nListens);
+ accData->acList = (AclEntry *)(accData->aliasList + accData->nAliases);
+ cptr = (char *)(accData->acList + accData->nAcls);
+ for (i = 0; i < accData->nHosts; i++) {
+ switch ((accData->hostList[i].type = GRecvInt())) {
+ case HOST_ALIAS:
+ accData->hostList[i].entry.aliasPattern = cptr;
+ cptr += GRecvStrBuf( cptr );
+ break;
+ case HOST_PATTERN:
+ accData->hostList[i].entry.hostPattern = cptr;
+ cptr += GRecvStrBuf( cptr );
+ break;
+ case HOST_ADDRESS:
+ da = &accData->hostList[i].entry.displayAddress;
+ da->hostAddress.data = (unsigned char *)cptr;
+ cptr += (da->hostAddress.length = GRecvArrBuf( cptr ));
+ switch (GRecvInt())
+ {
+#ifdef AF_INET
+ case AF_INET:
+ da->connectionType = FamilyInternet;
+ break;
+#endif
+#if defined(IPv6) && defined(AF_INET6)
+ case AF_INET6:
+ da->connectionType = FamilyInternet6;
+ break;
+#endif
+#ifdef AF_DECnet
+ case AF_DECnet:
+ da->connectionType = FamilyDECnet;
+ break;
+#endif
+/*#ifdef AF_UNIX
+ case AF_UNIX:
+#endif*/
+ default:
+ da->connectionType = FamilyLocal;
+ break;
+ }
+ break;
+ case HOST_BROADCAST:
+ break;
+ default:
+ LogError( "Received unknown host type %d from config reader\n", accData->hostList[i].type );
+ return;
+ }
+ }
+ for (i = 0; i < accData->nListens; i++) {
+ accData->listenList[i].iface = GRecvInt();
+ accData->listenList[i].mcasts = GRecvInt();
+ accData->listenList[i].nmcasts = GRecvInt();
+ }
+ for (i = 0; i < accData->nAliases; i++) {
+ accData->aliasList[i].name = cptr;
+ cptr += GRecvStrBuf( cptr );
+ accData->aliasList[i].hosts = GRecvInt();
+ accData->aliasList[i].nhosts = GRecvInt();
+ }
+ for (i = 0; i < accData->nAcls; i++) {
+ accData->acList[i].entries = GRecvInt();
+ accData->acList[i].nentries = GRecvInt();
+ accData->acList[i].hosts = GRecvInt();
+ accData->acList[i].nhosts = GRecvInt();
+ accData->acList[i].flags = GRecvInt();
+ }
+}
+
+
+/* Returns non-0 if string is matched by pattern. Does case folding.
+ */
+static int
+patternMatch( const char *string, const char *pattern )
+{
+ int p, s;
+
+ if (!string)
+ string = "";
+
+ for (;;) {
+ s = *string++;
+ switch (p = *pattern++) {
+ case '*':
+ if (!*pattern)
+ return 1;
+ for (string--; *string; string++)
+ if (patternMatch( string, pattern ))
+ return 1;
+ return 0;
+ case '?':
+ if (s == '\0')
+ return 0;
+ break;
+ case '\0':
+ return s == '\0';
+ case '\\':
+ p = *pattern++;
+ /* fall through */
+ default:
+ if (tolower( p ) != tolower( s ))
+ return 0;
+ }
+ }
+}
+
+
+#define MAX_DEPTH 32
+
+static void
+scanHostlist( int fh, int nh,
+ ARRAY8Ptr clientAddress, CARD16 connectionType,
+ ChooserFunc function, char *closure,
+ int broadcast, int *haveLocalhost )
+{
+ HostEntry *h;
+ AliasEntry *a;
+ int na;
+
+ for (h = accData->hostList + fh; nh; nh--, h++) {
+ switch (h->type) {
+ case HOST_ALIAS:
+ for (a = accData->aliasList, na = accData->nAliases; na; na--, a++)
+ if (patternMatch( a->name, h->entry.aliasPattern )) /* XXX originally swapped, no wildcards in alias name matching */
+ scanHostlist( a->hosts, a->nhosts,
+ clientAddress, connectionType,
+ function, closure, broadcast,
+ haveLocalhost );
+ break;
+ case HOST_ADDRESS:
+ if (XdmcpARRAY8Equal( getLocalAddress(), &h->entry.displayAddress.hostAddress ))
+ *haveLocalhost = 1;
+ else if (function)
+ (*function)( connectionType, &h->entry.displayAddress.hostAddress, closure );
+ break;
+ case HOST_BROADCAST:
+ if (broadcast && function)
+ (*function)( FamilyBroadcast, 0, closure );
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static int
+scanEntrylist( int fh, int nh,
+ ARRAY8Ptr clientAddress, CARD16 connectionType,
+ char **clientName )
+{
+ HostEntry *h;
+ AliasEntry *a;
+ int na;
+
+ for (h = accData->hostList + fh; nh; nh--, h++) {
+ switch (h->type) {
+ case HOST_ALIAS:
+ for (a = accData->aliasList, na = accData->nAliases; na; na--, a++)
+ if (patternMatch( a->name, h->entry.aliasPattern ))
+ if (scanEntrylist( a->hosts, a->nhosts,
+ clientAddress, connectionType,
+ clientName ))
+ return 1;
+ break;
+ case HOST_PATTERN:
+ if (!*clientName)
+ *clientName = NetworkAddressToHostname( connectionType,
+ clientAddress );
+ if (patternMatch( *clientName, h->entry.hostPattern ))
+ return 1;
+ break;
+ case HOST_ADDRESS:
+ if (h->entry.displayAddress.connectionType == connectionType &&
+ XdmcpARRAY8Equal( &h->entry.displayAddress.hostAddress,
+ clientAddress ))
+ return 1;
+ break;
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+static AclEntry *
+matchAclEntry( ARRAY8Ptr clientAddress, CARD16 connectionType, int direct )
+{
+ AclEntry *e, *re;
+ char *clientName = 0;
+ int ne;
+
+ for (e = accData->acList, ne = accData->nAcls, re = 0; ne; ne--, e++)
+ if (!e->nhosts == direct)
+ if (scanEntrylist( e->entries, e->nentries,
+ clientAddress, connectionType,
+ &clientName ))
+ {
+ re = e;
+ break;
+ }
+ if (clientName)
+ free( clientName );
+ return re;
+}
+
+/*
+ * calls the given function for each valid indirect entry. Returns TRUE if
+ * the local host exists on any of the lists, else FALSE
+ */
+int
+ForEachMatchingIndirectHost( ARRAY8Ptr clientAddress,
+ CARD16 connectionType,
+ ChooserFunc function, char *closure )
+{
+ AclEntry *e;
+ int haveLocalhost = 0;
+
+ e = matchAclEntry( clientAddress, connectionType, 0 );
+ if (e && !(e->flags & a_notAllowed)) {
+ if (e->flags & a_useChooser) {
+ ARRAY8Ptr choice;
+
+ choice = IndirectChoice( clientAddress, connectionType );
+ if (!choice || XdmcpARRAY8Equal( getLocalAddress(), choice ))
+ haveLocalhost = 1;
+ else
+ (*function)( connectionType, choice, closure );
+ } else
+ scanHostlist( e->hosts, e->nhosts, clientAddress, connectionType,
+ function, closure, FALSE, &haveLocalhost );
+ }
+ return haveLocalhost;
+}
+
+int
+UseChooser( ARRAY8Ptr clientAddress, CARD16 connectionType )
+{
+ AclEntry *e;
+
+ e = matchAclEntry( clientAddress, connectionType, 0 );
+ return e && !(e->flags & a_notAllowed) && (e->flags & a_useChooser) &&
+ !IndirectChoice( clientAddress, connectionType );
+}
+
+void
+ForEachChooserHost( ARRAY8Ptr clientAddress, CARD16 connectionType,
+ ChooserFunc function, char *closure )
+{
+ AclEntry *e;
+ int haveLocalhost = 0;
+
+ e = matchAclEntry( clientAddress, connectionType, 0 );
+ if (e && !(e->flags & a_notAllowed) && (e->flags & a_useChooser))
+ scanHostlist( e->hosts, e->nhosts, clientAddress, connectionType,
+ function, closure, TRUE, &haveLocalhost );
+ if (haveLocalhost)
+ (*function)( connectionType, getLocalAddress(), closure );
+}
+
+/*
+ * returns TRUE if the given client is acceptable to the local host. The
+ * given display client is acceptable if it occurs without a host list.
+ */
+int
+AcceptableDisplayAddress( ARRAY8Ptr clientAddress, CARD16 connectionType,
+ xdmOpCode type )
+{
+ AclEntry *e;
+
+ if (type == INDIRECT_QUERY)
+ return 1;
+
+ e = matchAclEntry( clientAddress, connectionType, 1 );
+ return e && !(e->flags & a_notAllowed) &&
+ (type != BROADCAST_QUERY || !(e->flags & a_notBroadcast));
+}
+
+void
+ForEachListenAddr( ListenFunc listenfunction, ListenFunc mcastfunction,
+ void **closure )
+{
+ int i, j, ifc, mc, nmc;
+
+ for (i = 0; i < accData->nListens; i++) {
+ ifc = accData->listenList[i].iface;
+ (*listenfunction)( ifc < 0 ? 0 :
+ &accData->hostList[ifc].entry.displayAddress.hostAddress,
+ closure );
+ mc = accData->listenList[i].mcasts;
+ nmc = accData->listenList[i].nmcasts;
+ for (j = 0; j < nmc; j++, mc++)
+ (*mcastfunction)( &accData->hostList[mc].entry.displayAddress.hostAddress,
+ closure );
+ }
+}
+#endif /* XDMCP */
diff --git a/kdm/backend/auth.c b/kdm/backend/auth.c
new file mode 100644
index 000000000..fa7afac01
--- /dev/null
+++ b/kdm/backend/auth.c
@@ -0,0 +1,1247 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2000-2004 Oswald Buddenhagen <ossi@kde.org>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the copyright holder.
+
+*/
+
+/*
+ * xdm - display manager daemon
+ * Author: Keith Packard, MIT X Consortium
+ *
+ * maintain the authorization generation daemon
+ */
+
+#include "dm.h"
+#include "dm_auth.h"
+#include "dm_error.h"
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+#include <sys/ioctl.h>
+
+#include "dm_socket.h"
+#ifdef DNETCONN
+# include <netdnet/dnetdb.h>
+#endif
+
+#if (defined(_POSIX_SOURCE) && !defined(_AIX) && !defined(__QNX__)) || defined(__hpux) || defined(__svr4__) /* XXX */
+# define NEED_UTSNAME
+# include <sys/utsname.h>
+#endif
+
+#ifdef __GNU__
+# include <netdb.h>
+# undef SIOCGIFCONF
+#else /* __GNU__ */
+# include <net/if.h>
+# ifdef __svr4__
+# include <netdb.h>
+# include <sys/sockio.h>
+# include <sys/stropts.h>
+# endif
+# ifdef __EMX__
+# define link rename
+# define chown(a,b,c)
+# include <io.h>
+# endif
+#endif /* __GNU__ */
+
+struct AuthProtocol {
+ unsigned short name_length;
+ const char *name;
+ void (*InitAuth)( unsigned short len, const char *name );
+ Xauth *(*GetAuth)( unsigned short len, const char *name );
+#ifdef XDMCP
+ void (*GetXdmcpAuth)( struct protoDisplay *pdpy,
+ unsigned short authorizationNameLen,
+ const char *authorizationName );
+#endif
+ int inited;
+};
+
+#ifdef XDMCP
+# define xdmcpauth(arg) , arg
+#else
+# define xdmcpauth(arg)
+#endif
+
+static struct AuthProtocol AuthProtocols[] = {
+{ (unsigned short)18, "MIT-MAGIC-COOKIE-1",
+ MitInitAuth, MitGetAuth xdmcpauth(NULL), 0
+},
+#ifdef HASXDMAUTH
+{ (unsigned short)19, "XDM-AUTHORIZATION-1",
+ XdmInitAuth, XdmGetAuth xdmcpauth(XdmGetXdmcpAuth), 0
+},
+#endif
+#ifdef SECURE_RPC
+{ (unsigned short)9, "SUN-DES-1",
+ SecureRPCInitAuth, SecureRPCGetAuth xdmcpauth(NULL), 0
+},
+#endif
+#ifdef K5AUTH
+{ (unsigned short)14, "MIT-KERBEROS-5",
+ Krb5InitAuth, Krb5GetAuth xdmcpauth(NULL), 0
+},
+#endif
+};
+
+static struct AuthProtocol *
+findProtocol( unsigned short name_length, const char *name )
+{
+ unsigned i;
+
+ for (i = 0; i < as(AuthProtocols); i++)
+ if (AuthProtocols[i].name_length == name_length &&
+ memcmp( AuthProtocols[i].name, name, name_length ) == 0)
+ {
+ return &AuthProtocols[i];
+ }
+ return (struct AuthProtocol *)0;
+}
+
+int
+ValidAuthorization( unsigned short name_length, const char *name )
+{
+ if (findProtocol( name_length, name ))
+ return TRUE;
+ return FALSE;
+}
+
+static Xauth *
+GenerateAuthorization( unsigned short name_length, const char *name )
+{
+ struct AuthProtocol *a;
+ Xauth *auth = 0;
+
+ Debug( "GenerateAuthorization %s\n", name );
+ if ((a = findProtocol( name_length, name ))) {
+ if (!a->inited) {
+ (*a->InitAuth)( name_length, name );
+ a->inited = TRUE;
+ }
+ auth = (*a->GetAuth)( name_length, name );
+ if (auth) {
+ Debug( "got %p (%d %.*s) %02[*hhx\n", auth,
+ auth->name_length, auth->name_length, auth->name,
+ auth->data_length, auth->data );
+ } else
+ Debug( "got (null)\n" );
+ } else
+ Debug( "unknown authorization %s\n", name );
+ return auth;
+}
+
+#ifdef XDMCP
+
+void
+SetProtoDisplayAuthorization( struct protoDisplay *pdpy,
+ unsigned short authorizationNameLen,
+ const char *authorizationName )
+{
+ struct AuthProtocol *a;
+ Xauth *auth;
+
+ a = findProtocol( authorizationNameLen, authorizationName );
+ pdpy->xdmcpAuthorization = pdpy->fileAuthorization = 0;
+ if (a) {
+ if (!a->inited) {
+ (*a->InitAuth)( authorizationNameLen, authorizationName );
+ a->inited = TRUE;
+ }
+ if (a->GetXdmcpAuth) {
+ (*a->GetXdmcpAuth)( pdpy, authorizationNameLen, authorizationName );
+ auth = pdpy->xdmcpAuthorization;
+ } else {
+ auth = (*a->GetAuth)( authorizationNameLen, authorizationName );
+ pdpy->fileAuthorization = auth;
+ pdpy->xdmcpAuthorization = 0;
+ }
+ if (auth)
+ Debug( "got %p (%d %.*s)\n", auth,
+ auth->name_length, auth->name_length, auth->name );
+ else
+ Debug( "got (null)\n" );
+ }
+}
+
+#endif /* XDMCP */
+
+void
+CleanUpFileName( const char *src, char *dst, int len )
+{
+ while (*src) {
+ if (--len <= 0)
+ break;
+ switch (*src & 0x7f) {
+ case '/':
+ *dst++ = '_';
+ break;
+ case '-':
+ *dst++ = '.';
+ break;
+ default:
+ *dst++ = (*src & 0x7f);
+ }
+ ++src;
+ }
+ *dst = '\0';
+}
+
+
+static FILE *
+fdOpenW( int fd )
+{
+ FILE *f;
+
+ if (fd >= 0) {
+ if ((f = fdopen( fd, "w" )))
+ return f;
+ close( fd );
+ }
+ return 0;
+}
+
+
+#define NAMELEN 255
+
+static FILE *
+MakeServerAuthFile( struct display *d )
+{
+ FILE *f;
+#ifndef HAVE_MKSTEMP
+ int r;
+#endif
+ char cleanname[NAMELEN], nambuf[NAMELEN+128];
+
+ /*
+ * Some paranoid, but still not sufficient (DoS was still possible)
+ * checks used to be here. I removed all this stuff because
+ * a) authDir is supposed to be /var/run/xauth (=safe) or similar and
+ * b) even if it's not (say, /tmp), we create files safely (hopefully).
+ */
+ if (mkdir( authDir, 0755 ) < 0 && errno != EEXIST)
+ return 0;
+ CleanUpFileName( d->name, cleanname, NAMELEN - 8 );
+#ifdef HAVE_MKSTEMP
+ sprintf( nambuf, "%s/A%s-XXXXXX", authDir, cleanname );
+ if ((f = fdOpenW( mkstemp( nambuf ) ))) {
+ StrDup( &d->authFile, nambuf );
+ return f;
+ }
+#else
+ for (r = 0; r < 100; r++) {
+ sprintf( nambuf, "%s/A%s-XXXXXX", authDir, cleanname );
+ (void)mktemp( nambuf );
+ if ((f = fdOpenW( open( nambuf, O_WRONLY | O_CREAT | O_EXCL, 0600 ) ))) {
+ StrDup( &d->authFile, nambuf );
+ return f;
+ }
+ }
+#endif
+ return 0;
+}
+
+int
+SaveServerAuthorizations( struct display *d, Xauth **auths, int count )
+{
+ FILE *auth_file;
+ int i;
+
+ if (!d->authFile && d->clientAuthFile && *d->clientAuthFile)
+ StrDup( &d->authFile, d->clientAuthFile );
+ if (d->authFile) {
+ if (!(auth_file = fdOpenW( creat( d->authFile, 0600 ) ))) {
+ LogError( "Cannot open X server authorization file %s\n", d->authFile );
+ free( d->authFile );
+ d->authFile = NULL;
+ return FALSE;
+ }
+ } else {
+ if (!(auth_file = MakeServerAuthFile( d ))) {
+ LogError( "Cannot create X server authorization file\n" );
+ return FALSE;
+ }
+ }
+ Debug( "file: %s auth: %p\n", d->authFile, auths );
+ for (i = 0; i < count; i++) {
+ /*
+ * User-based auths may not have data until
+ * a user logs in. In which case don't write
+ * to the auth file so xrdb and setup programs don't fail.
+ */
+ if (auths[i]->data_length > 0)
+ if (!XauWriteAuth( auth_file, auths[i] ) ||
+ fflush( auth_file ) == EOF)
+ {
+ fclose( auth_file );
+ LogError( "Cannot write X server authorization file %s\n",
+ d->authFile );
+ free( d->authFile );
+ d->authFile = NULL;
+ return FALSE;
+ }
+ }
+ fclose( auth_file );
+ return TRUE;
+}
+
+void
+SetLocalAuthorization( struct display *d )
+{
+ Xauth *auth, **auths;
+ int i, j;
+
+ if (d->authorizations)
+ {
+ for (i = 0; i < d->authNum; i++)
+ XauDisposeAuth( d->authorizations[i] );
+ free( (char *)d->authorizations );
+ d->authorizations = (Xauth **)NULL;
+ d->authNum = 0;
+ }
+ Debug( "SetLocalAuthorization %s, auths %[s\n", d->name, d->authNames );
+ if (!d->authNames)
+ return;
+ for (i = 0; d->authNames[i]; i++)
+ ;
+ d->authNameNum = i;
+ if (d->authNameLens)
+ free( (char *)d->authNameLens );
+ d->authNameLens = (unsigned short *)Malloc( d->authNameNum * sizeof(unsigned short) );
+ if (!d->authNameLens)
+ return;
+ for (i = 0; i < d->authNameNum; i++)
+ d->authNameLens[i] = strlen( d->authNames[i] );
+ auths = (Xauth **)Malloc( d->authNameNum * sizeof(Xauth *) );
+ if (!auths)
+ return;
+ j = 0;
+ for (i = 0; i < d->authNameNum; i++) {
+ auth = GenerateAuthorization( d->authNameLens[i], d->authNames[i] );
+ if (auth)
+ auths[j++] = auth;
+ }
+ if (SaveServerAuthorizations( d, auths, j )) {
+ d->authorizations = auths;
+ d->authNum = j;
+ } else {
+ for (i = 0; i < j; i++)
+ XauDisposeAuth( auths[i] );
+ free( (char *)auths );
+ }
+}
+
+/*
+ * Set the authorization to use for xdm's initial connection
+ * to the X server. Cannot use user-based authorizations
+ * because no one has logged in yet, so we don't have any
+ * user credentials.
+ * Well, actually we could use SUN-DES-1 because we tell the server
+ * to allow root in. This is bogus and should be fixed.
+ */
+void
+SetAuthorization( struct display *d )
+{
+ register Xauth **auth = d->authorizations;
+ int i;
+
+ for (i = 0; i < d->authNum; i++) {
+ if (auth[i]->name_length == 9 &&
+ memcmp( auth[i]->name, "SUN-DES-1", 9 ) == 0)
+ continue;
+ if (auth[i]->name_length == 14 &&
+ memcmp( auth[i]->name, "MIT-KERBEROS-5", 14 ) == 0)
+ continue;
+ XSetAuthorization( auth[i]->name, (int)auth[i]->name_length,
+ auth[i]->data, (int)auth[i]->data_length );
+ }
+}
+
+static int
+openFiles( const char *name, char *new_name, FILE **oldp, FILE **newp )
+{
+ strcat( strcpy( new_name, name ), "-n" );
+ if (!(*newp =
+ fdOpenW( creat( new_name, 0600 ) ))) {
+ Debug( "can't open new file %s\n", new_name );
+ return 0;
+ }
+ *oldp = fopen( name, "r" );
+ Debug( "opens succeeded %s %s\n", name, new_name );
+ return 1;
+}
+
+struct addrList {
+ struct addrList *next;
+ unsigned short family, address_length, number_length;
+ char data[1];
+};
+
+static struct addrList *addrs;
+
+static void
+initAddrs( void )
+{
+ addrs = 0;
+}
+
+static void
+doneAddrs( void )
+{
+ struct addrList *a, *n;
+ for (a = addrs; a; a = n) {
+ n = a->next;
+ free( a );
+ }
+}
+
+static void
+saveEntry( Xauth *auth )
+{
+ struct addrList *new;
+
+ if (!(new = Malloc( offsetof(struct addrList, data) +
+ auth->address_length + auth->number_length )))
+ return;
+ new->address_length = auth->address_length;
+ new->number_length = auth->number_length;
+ memcpy( new->data, auth->address, (int)auth->address_length );
+ memcpy( new->data + (int)auth->address_length,
+ auth->number, (int)auth->number_length );
+ new->family = auth->family;
+ new->next = addrs;
+ addrs = new;
+}
+
+static int
+checkEntry( Xauth *auth )
+{
+ struct addrList *a;
+
+ for (a = addrs; a; a = a->next)
+ if (a->family == auth->family &&
+ a->address_length == auth->address_length &&
+ !memcmp( a->data, auth->address, auth->address_length ) &&
+ a->number_length == auth->number_length &&
+ !memcmp( a->data + a->address_length,
+ auth->number, auth->number_length ))
+ return 1;
+ return 0;
+}
+
+static void
+writeAuth( FILE *file, Xauth *auth, int *ok )
+{
+ if (debugLevel & DEBUG_AUTH) /* normally too verbose */
+ Debug( "writeAuth: doWrite = %d\n"
+ "family: %d\n"
+ "addr: %02[*:hhx\n"
+ "number: %02[*:hhx\n"
+ "name: %02[*:hhx\n"
+ "data: %02[*:hhx\n",
+ ok != 0, auth->family,
+ auth->address_length, auth->address,
+ auth->number_length, auth->number,
+ auth->name_length, auth->name,
+ auth->data_length, auth->data );
+ if (ok && !XauWriteAuth( file, auth ))
+ *ok = FALSE;
+}
+
+static void
+writeAddr( int family, int addr_length, char *addr,
+ FILE *file, Xauth *auth, int *ok )
+{
+ auth->family = (unsigned short)family;
+ auth->address_length = addr_length;
+ auth->address = addr;
+ Debug( "writeAddr: writing and saving an entry\n" );
+ writeAuth( file, auth, ok );
+ if (!checkEntry( auth ))
+ saveEntry( auth );
+}
+
+static void
+DefineLocal( FILE *file, Xauth *auth, int *ok )
+{
+#if !defined(NEED_UTSNAME) || defined(__hpux)
+ char displayname[100];
+#endif
+#ifdef NEED_UTSNAME
+ struct utsname name;
+#endif
+
+ /* stolen from xinit.c */
+
+/* Make sure this produces the same string as _XGetHostname in lib/X/XlibInt.c.
+ * Otherwise, Xau will not be able to find your cookies in the Xauthority file.
+ *
+ * Note: POSIX says that the ``nodename'' member of utsname does _not_ have
+ * to have sufficient information for interfacing to the network,
+ * and so, you may be better off using gethostname (if it exists).
+ */
+
+#ifdef NEED_UTSNAME
+
+ /* hpux:
+ * Why not use gethostname()? Well, at least on my system, I've had to
+ * make an ugly kernel patch to get a name longer than 8 characters, and
+ * uname() lets me access to the whole string (it smashes release, you
+ * see), whereas gethostname() kindly truncates it for me.
+ */
+ uname( &name );
+ writeAddr( FamilyLocal, strlen( name.nodename ), name.nodename,
+ file, auth, ok );
+#endif
+
+#if !defined(NEED_UTSNAME) || defined(__hpux)
+ /* _AIX:
+ * In _AIX, _POSIX_SOURCE is defined, but uname gives only first
+ * field of hostname. Thus, we use gethostname instead.
+ */
+
+ /*
+ * For HP-UX, HP's Xlib expects a fully-qualified domain name, which
+ * is achieved by using gethostname(). For compatability, we must
+ * also still create the entry using uname() above.
+ */
+ displayname[0] = 0;
+ if (!gethostname( displayname, sizeof(displayname) ))
+ displayname[sizeof(displayname) - 1] = 0;
+
+# ifdef NEED_UTSNAME
+ /*
+ * If gethostname and uname both returned the same name,
+ * do not write a duplicate entry.
+ */
+ if (strcmp( displayname, name.nodename ))
+# endif
+ writeAddr( FamilyLocal, strlen( displayname ), displayname,
+ file, auth, ok );
+#endif
+}
+
+#ifdef SYSV_SIOCGIFCONF
+
+/* Deal with different SIOCGIFCONF ioctl semantics on SYSV, SVR4 */
+
+int
+ifioctl (int fd, int cmd, char *arg)
+{
+ struct strioctl ioc;
+ int ret;
+
+ bzero( (char *)&ioc, sizeof(ioc) );
+ ioc.ic_cmd = cmd;
+ ioc.ic_timout = 0;
+ if (cmd == SIOCGIFCONF) {
+ ioc.ic_len = ((struct ifconf *)arg)->ifc_len;
+ ioc.ic_dp = ((struct ifconf *)arg)->ifc_buf;
+ } else {
+ ioc.ic_len = sizeof(struct ifreq);
+ ioc.ic_dp = arg;
+ }
+ ret = ioctl( fd, I_STR, (char *)&ioc );
+ if (ret >= 0 && cmd == SIOCGIFCONF)
+ ((struct ifconf *)arg)->ifc_len = ioc.ic_len;
+ return (ret);
+}
+
+#endif /* SYSV_SIOCGIFCONF */
+
+#ifdef HAVE_GETIFADDRS
+# include <ifaddrs.h>
+
+static void
+DefineSelf( FILE *file, Xauth *auth, int *ok )
+{
+ struct ifaddrs *ifap, *ifr;
+ char *addr;
+ int family, len;
+
+ if (getifaddrs( &ifap ) < 0)
+ return;
+ for (ifr = ifap; ifr; ifr = ifr->ifa_next) {
+ if (!ifr->ifa_addr)
+ continue;
+ family = ConvertAddr( (char *)(ifr->ifa_addr), &len, &addr );
+ if (family == -1 || family == FamilyLocal)
+ continue;
+ /*
+ * don't write out 'localhost' entries, as
+ * they may conflict with other local entries.
+ * DefineLocal will always be called to add
+ * the local entry anyway, so this one can
+ * be tossed.
+ */
+ if (family == FamilyInternet &&
+ addr[0] == 127 && addr[1] == 0 && addr[2] == 0 && addr[3] == 1)
+ {
+ Debug( "Skipping localhost address\n" );
+ continue;
+ }
+# if defined(IPv6) && defined(AF_INET6)
+ if (family == FamilyInternet6) {
+ if (IN6_IS_ADDR_LOOPBACK( ((struct in6_addr *)addr) )) {
+ Debug( "Skipping IPv6 localhost address\n" );
+ continue;
+ }
+ /* Also skip XDM-AUTHORIZATION-1 */
+ if (auth->name_length == 19 &&
+ !memcmp( auth->name, "XDM-AUTHORIZATION-1", 19 )) {
+ Debug( "Skipping IPv6 XDM-AUTHORIZATION-1\n" );
+ continue;
+ }
+ }
+# endif
+ writeAddr( family, len, addr, file, auth, ok );
+ }
+ freeifaddrs( ifap );
+}
+#else /* GETIFADDRS */
+
+#if defined(STREAMSCONN) && !defined(SYSV_SIOCGIFCONF) && !defined(WINTCP)
+
+#include <tiuser.h>
+
+/* Define this host for access control. Find all the hosts the OS knows about
+ * for this fd and add them to the selfhosts list.
+ * TLI version, written without sufficient documentation.
+ */
+static void
+DefineSelf( int fd, FILE *file, Xauth *auth, int *ok )
+{
+ struct netbuf netb;
+ char addrret[1024]; /* easier than t_alloc */
+
+ netb.maxlen = sizeof(addrret);
+ netb.buf = addrret;
+ if (t_getname( fd, &netb, LOCALNAME ) == -1)
+ t_error( "t_getname" );
+ /* what a kludge */
+ writeAddr( FamilyInternet, 4, netb.buf+4, file, auth, ok );
+}
+
+#else
+
+#ifdef WINTCP /* NCR with Wollongong TCP */
+
+#include <stropts.h>
+#include <tiuser.h>
+
+#include <sys/stream.h>
+#include <net/if.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+
+static void
+DefineSelf( int fd, FILE *file, Xauth *auth, int *ok )
+{
+ /*
+ * The Wollongong drivers used by NCR SVR4/MP-RAS don't understand the
+ * socket IO calls that most other drivers seem to like. Because of
+ * this, this routine must be special cased for NCR. Eventually,
+ * this will be cleared up.
+ */
+
+ struct ipb ifnet;
+ struct in_ifaddr ifaddr;
+ struct strioctl str;
+ unsigned char *addr;
+ int len, ipfd;
+
+ if ((ipfd = open( "/dev/ip", O_RDWR, 0 )) < 0) {
+ LogError( "Trouble getting interface configuration\n" );
+ return;
+ }
+
+ /* Indicate that we want to start at the begining */
+ ifnet.ib_next = (struct ipb *)1;
+
+ while (ifnet.ib_next) {
+ str.ic_cmd = IPIOC_GETIPB;
+ str.ic_timout = 0;
+ str.ic_len = sizeof(struct ipb);
+ str.ic_dp = (char *)&ifnet;
+
+ if (ioctl( ipfd, (int)I_STR, (char *)&str ) < 0) {
+ close( ipfd );
+ LogError( "Trouble getting interface configuration\n" );
+ return;
+ }
+
+ ifaddr.ia_next = (struct in_ifaddr *)ifnet.if_addrlist;
+ str.ic_cmd = IPIOC_GETINADDR;
+ str.ic_timout = 0;
+ str.ic_len = sizeof(struct in_ifaddr);
+ str.ic_dp = (char *)&ifaddr;
+
+ if (ioctl( ipfd, (int)I_STR, (char *)&str ) < 0) {
+ close( ipfd );
+ LogError( "Trouble getting interface configuration\n" );
+ return;
+ }
+
+ /*
+ * Ignore the 127.0.0.1 entry.
+ */
+ if (IA_SIN( &ifaddr )->sin_addr.s_addr == htonl( 0x7f000001 ) )
+ continue;
+
+ writeAddr( FamilyInternet, 4, (char *)&(IA_SIN( &ifaddr )->sin_addr),
+ file, auth, ok );
+
+ }
+ close( ipfd );
+}
+
+#else /* WINTCP */
+
+/* Solaris provides an extended interface SIOCGLIFCONF. Other systems
+ * may have this as well, but the code has only been tested on Solaris
+ * so far, so we only enable it there. Other platforms may be added as
+ * needed.
+ *
+ * Test for Solaris commented out -- TSI @ UQV 2003.06.13
+ */
+#ifdef SIOCGLIFCONF
+/* #if defined(sun) */
+# define USE_SIOCGLIFCONF
+/* #endif */
+#endif
+
+#if defined(SIOCGIFCONF) || defined (USE_SIOCGLIFCONF)
+
+#if !defined(SYSV_SIOCGIFCONF) || defined(USE_SIOCGLIFCONF)
+# define ifioctl ioctl
+#endif
+
+#ifdef USE_SIOCGLIFCONF
+# define ifr_type struct lifreq
+#else
+# define ifr_type struct ifreq
+#endif
+
+/* Handle variable length ifreq in BNR2 and later */
+#ifdef VARIABLE_IFREQ
+# define ifr_size(p) (sizeof(struct ifreq) + \
+ (p->ifr_addr.sa_len > sizeof(p->ifr_addr) ? \
+ p->ifr_addr.sa_len - sizeof(p->ifr_addr) : 0))
+#else
+# define ifr_size(p) (sizeof(ifr_type))
+#endif
+
+#ifdef USE_SIOCGLIFCONF
+# define IFC_IOCTL_REQ SIOCGLIFCONF
+# define IFC_REQ(ifc) ifc.lifc_req
+# define IFC_LEN(ifc) ifc.lifc_len
+# define IFR_ADDR(ifr) ifr->lifr_addr
+# define IFR_NAME(ifr) ifr->lifr_name
+#else
+# define IFC_IOCTL_REQ SIOCGIFCONF
+# define IFC_REQ(ifc) ifc.ifc_req
+# define IFC_LEN(ifc) ifc.ifc_len
+# define IFR_ADDR(ifr) ifr->ifr_addr
+# define IFR_NAME(ifr) ifr->ifr_name
+#endif
+
+/* Define this host for access control. Find all the hosts the OS knows about
+ * for this fd and add them to the selfhosts list.
+ */
+static void
+DefineSelf( int fd, FILE *file, Xauth *auth, int *ok )
+{
+ char buf[2048], *cp, *cplim;
+ int len;
+ char *addr;
+ int family;
+ ifr_type *ifr;
+#ifdef USE_SIOCGLIFCONF
+ int n;
+ void * bufptr = buf;
+ size_t buflen = sizeof(buf);
+ struct lifconf ifc;
+# ifdef SIOCGLIFNUM
+ struct lifnum ifn;
+# endif
+#else
+ struct ifconf ifc;
+#endif
+
+#if defined(SIOCGLIFNUM) && defined(SIOCGLIFCONF)
+ ifn.lifn_family = AF_UNSPEC;
+ ifn.lifn_flags = 0;
+ if (ioctl( fd, (int)SIOCGLIFNUM, (char *)&ifn ) < 0)
+ LogError( "Failed getting interface count\n" );
+ if (buflen < (ifn.lifn_count * sizeof(struct lifreq))) {
+ buflen = ifn.lifn_count * sizeof(struct lifreq);
+ bufptr = Malloc( buflen );
+ }
+#endif
+
+#ifdef USE_SIOCGLIFCONF
+ ifc.lifc_family = AF_UNSPEC;
+ ifc.lifc_flags = 0;
+ ifc.lifc_len = buflen;
+ ifc.lifc_buf = bufptr;
+#else
+ ifc.ifc_len = sizeof(buf);
+ ifc.ifc_buf = buf;
+#endif
+ if (ifioctl (fd, IFC_IOCTL_REQ, (char *)&ifc) < 0) {
+ LogError( "Trouble getting network interface configuration\n" );
+#if defined(SIOCGLIFNUM) && defined(SIOCGLIFCONF)
+ if (bufptr != buf)
+ free( bufptr );
+#endif
+ return;
+ }
+
+ cplim = (char *)IFC_REQ( ifc ) + IFC_LEN( ifc );
+
+ for (cp = (char *)IFC_REQ( ifc ); cp < cplim; cp += ifr_size (ifr)) {
+ ifr = (ifr_type *) cp;
+#ifdef DNETCONN
+ /*
+ * this is ugly but SIOCGIFCONF returns decnet addresses in
+ * a different form from other decnet calls
+ */
+ if (IFR_ADDR( ifr ).sa_family == AF_DECnet) {
+ len = sizeof(struct dn_naddr);
+ addr = (char *)IFR_ADDR( ifr ).sa_data;
+ family = FamilyDECnet;
+ } else
+#endif
+ {
+ family = ConvertAddr( (char *)&IFR_ADDR( ifr ), &len, &addr );
+ if (family < 0)
+ continue;
+
+ if (len == 0) {
+ Debug( "skipping zero length address\n" );
+ continue;
+ }
+ /*
+ * don't write out 'localhost' entries, as
+ * they may conflict with other local entries.
+ * DefineLocal will always be called to add
+ * the local entry anyway, so this one can
+ * be tossed.
+ */
+ if (family == FamilyInternet &&
+ addr[0] == 127 && addr[1] == 0 &&
+ addr[2] == 0 && addr[3] == 1)
+ {
+ Debug( "skipping localhost address\n" );
+ continue;
+ }
+#if defined(IPv6) && defined(AF_INET6)
+ if (family == FamilyInternet6) {
+ if (IN6_IS_ADDR_LOOPBACK( ((struct in6_addr *)addr) )) {
+ Debug( "Skipping IPv6 localhost address\n" );
+ continue;
+ }
+ /* Also skip XDM-AUTHORIZATION-1 */
+ if (auth->name_length == 19 &&
+ !memcmp( auth->name, "XDM-AUTHORIZATION-1", 19 )) {
+ Debug( "Skipping IPv6 XDM-AUTHORIZATION-1\n" );
+ continue;
+ }
+ }
+#endif
+ }
+ Debug( "DefineSelf: write network address, length %d\n", len );
+ writeAddr( family, len, addr, file, auth, ok );
+ }
+#if defined(SIOCGLIFNUM) && defined(SIOCGLIFCONF)
+ if (bufptr != buf)
+ free( bufptr );
+#endif
+}
+
+#else /* SIOCGIFCONF */
+
+/* Define this host for access control. Find all the hosts the OS knows about
+ * for this fd and add them to the selfhosts list.
+ */
+static void
+DefineSelf( int fd, int file, int auth, int *ok )
+{
+ int len;
+ caddr_t addr;
+ int family;
+
+ struct utsname name;
+ register struct hostent *hp;
+
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in in;
+ } saddr;
+
+ struct sockaddr_in *inetaddr;
+
+ /* hpux:
+ * Why not use gethostname()? Well, at least on my system, I've had to
+ * make an ugly kernel patch to get a name longer than 8 characters, and
+ * uname() lets me access to the whole string (it smashes release, you
+ * see), whereas gethostname() kindly truncates it for me.
+ */
+ uname( &name );
+ if ((hp = gethostbyname( name.nodename ))) {
+ saddr.sa.sa_family = hp->h_addrtype;
+ inetaddr = (struct sockaddr_in *)(&(saddr.sa));
+ memmove( (char *)&(inetaddr->sin_addr), (char *)hp->h_addr,
+ (int)hp->h_length );
+ if ( (family = ConvertAddr( &(saddr.sa), &len, &addr )) >= 0)
+ writeAddr( FamilyInternet, sizeof(inetaddr->sin_addr),
+ (char *)(&inetaddr->sin_addr), file, auth, ok );
+ }
+}
+
+#endif /* SIOCGIFCONF else */
+#endif /* WINTCP else */
+#endif /* STREAMSCONN && !SYSV_SIOCGIFCONF else */
+#endif /* HAVE_GETIFADDRS */
+
+static void
+setAuthNumber( Xauth *auth, const char *name )
+{
+ char *colon;
+ char *dot;
+
+ Debug( "setAuthNumber %s\n", name );
+ colon = strrchr( name, ':' );
+ if (colon) {
+ ++colon;
+ dot = strchr( colon, '.' );
+ if (dot)
+ auth->number_length = dot - colon;
+ else
+ auth->number_length = strlen( colon );
+ if (!StrNDup( &auth->number, colon, auth->number_length ))
+ auth->number_length = 0;
+ Debug( "setAuthNumber: %s\n", auth->number );
+ }
+}
+
+static void
+writeLocalAuth( FILE *file, Xauth *auth, const char *name, int *ok )
+{
+#if defined(STREAMSCONN) || !defined(HAVE_GETIFADDRS)
+ int fd;
+#endif
+
+ Debug( "writeLocalAuth: %s %.*s\n", name, auth->name_length, auth->name );
+ setAuthNumber( auth, name );
+# ifdef STREAMSCONN
+ fd = t_open( "/dev/tcp", O_RDWR, 0 );
+ t_bind( fd, NULL, NULL );
+ DefineSelf( fd, file, auth, ok );
+ t_unbind( fd );
+ t_close( fd );
+# elif defined(HAVE_GETIFADDRS)
+ DefineSelf( file, auth, ok );
+# else
+# ifdef TCPCONN
+# if defined(IPv6) && defined(AF_INET6)
+ fd = socket( AF_INET6, SOCK_STREAM, 0 );
+ if (fd < 0)
+# endif
+ fd = socket( AF_INET, SOCK_STREAM, 0 );
+ DefineSelf( fd, file, auth, ok );
+ close( fd );
+# endif
+# ifdef DNETCONN
+ fd = socket( AF_DECnet, SOCK_STREAM, 0 );
+ DefineSelf( fd, file, auth, ok );
+ close( fd );
+# endif
+# endif /* HAVE_GETIFADDRS */
+ DefineLocal( file, auth, ok );
+}
+
+#ifdef XDMCP
+
+/*
+ * Call ConvertAddr(), and if it returns an IPv4 localhost, convert it
+ * to a local display name. Meets the _XTransConvertAddress's localhost
+ * hack.
+ */
+
+static int
+ConvertAuthAddr( char *saddr, int *len, char **addr )
+{
+ int ret = ConvertAddr( saddr, len, addr );
+ if (ret == FamilyInternet &&
+ ((struct in_addr *)*addr)->s_addr == htonl( 0x7F000001L ))
+ ret = FamilyLocal;
+ return ret;
+}
+
+static void
+writeRemoteAuth( FILE *file, Xauth *auth, XdmcpNetaddr peer, int peerlen,
+ const char *name, int *ok )
+{
+ int family = FamilyLocal;
+ char *addr;
+
+ Debug( "writeRemoteAuth: %s %.*s\n", name, auth->name_length, auth->name );
+ if (!peer || peerlen < 2)
+ return;
+ setAuthNumber( auth, name );
+ family = ConvertAuthAddr( peer, &peerlen, &addr );
+ Debug( "writeRemoteAuth: family %d\n", family );
+ if (family != FamilyLocal) {
+ Debug( "writeRemoteAuth: %d, %02[*:hhx\n",
+ family, peerlen, addr );
+ writeAddr( family, peerlen, addr, file, auth, ok );
+ } else
+ writeLocalAuth( file, auth, name, ok );
+}
+
+#endif /* XDMCP */
+
+#define NBSIZE 1024
+
+static void
+startUserAuth( char *buf, char *nbuf, FILE **old, FILE **new )
+{
+ const char *home;
+ int lockStatus;
+
+ initAddrs();
+ *new = 0;
+ if ((home = getEnv( userEnviron, "HOME" )) && strlen( home ) < NBSIZE - 12) {
+ sprintf( buf, "%s/.Xauthority", home );
+ Debug( "XauLockAuth %s\n", buf );
+ lockStatus = XauLockAuth( buf, 1, 2, 10 );
+ Debug( "lock is %d\n", lockStatus );
+ if (lockStatus == LOCK_SUCCESS)
+ if (!openFiles( buf, nbuf, old, new ))
+ XauUnlockAuth( buf );
+ }
+ if (!*new)
+ LogWarn( "Can't update authorization file in home dir %s\n", home );
+}
+
+static int
+endUserAuth( FILE *old, FILE *new, const char *nname, int ok )
+{
+ Xauth *entry;
+ struct stat statb;
+
+ if (old) {
+ if (fstat( fileno( old ), &statb ) != -1)
+ chmod( nname, (int)(statb.st_mode & 0777) );
+ /*SUPPRESS 560*/
+ while ((entry = XauReadAuth( old ))) {
+ if (!checkEntry( entry )) {
+ Debug( "writing an entry\n" );
+ writeAuth( new, entry, &ok );
+ }
+ XauDisposeAuth( entry );
+ }
+ fclose( old );
+ }
+ if (fclose( new ) == EOF)
+ ok = FALSE;
+ doneAddrs();
+ return ok;
+}
+
+static void
+undoUserAuth( const char *name, const char *new_name )
+{
+ LogWarn( "Can't save user authorization in home dir\n" );
+ unlink( new_name );
+ XauUnlockAuth( name );
+}
+
+static char *
+moveUserAuth( const char *name, char *new_name, char *envname )
+{
+ if (unlink( name ))
+ Debug( "unlink %s failed\n", name );
+ if (link( new_name, name )) {
+ Debug( "link failed %s %s\n", new_name, name );
+ LogError( "Can't move user authorization into place\n" );
+ envname = new_name;
+ } else {
+ Debug( "new authorization moved into place\n" );
+ unlink( new_name );
+ }
+ XauUnlockAuth( name );
+ return envname;
+}
+
+void
+SetUserAuthorization( struct display *d )
+{
+ FILE *old, *new;
+ char *name;
+ char *envname;
+ Xauth **auths;
+ int i, ok;
+ int magicCookie;
+ int data_len;
+ char name_buf[NBSIZE], new_name[NBSIZE + 2];
+
+ Debug( "SetUserAuthorization\n" );
+ auths = d->authorizations;
+ if (auths) {
+ startUserAuth( name_buf, new_name, &old, &new );
+ if (new) {
+ envname = 0;
+ name = name_buf;
+ } else {
+ fallback:
+ if (strlen( d->userAuthDir ) >= NBSIZE - 13)
+ return;
+ /*
+ * Note, that we don't lock the auth file here, as it's
+ * temporary - we can assume, that we are the only ones
+ * knowing about this file anyway.
+ */
+#ifdef HAVE_MKSTEMP
+ sprintf( name_buf, "%s/.XauthXXXXXX", d->userAuthDir );
+ new = fdOpenW( mkstemp( name_buf ) );
+#else
+ for (i = 0; i < 100; i++) {
+ sprintf( name_buf, "%s/.XauthXXXXXX", d->userAuthDir );
+ (void)mktemp( name_buf );
+ if ((new =
+ fdOpenW( open( name_buf, O_WRONLY | O_CREAT | O_EXCL,
+ 0600 ) )))
+ break;
+ }
+#endif
+ if (!new) {
+ LogError( "Can't create authorization file in %s\n",
+ d->userAuthDir );
+ return;
+ }
+ name = 0;
+ envname = name_buf;
+ old = 0;
+ }
+ ok = TRUE;
+ Debug( "%d authorization protocols for %s\n", d->authNum, d->name );
+ /*
+ * Write MIT-MAGIC-COOKIE-1 authorization first, so that
+ * R4 clients which only knew that, and used the first
+ * matching entry will continue to function
+ */
+ magicCookie = -1;
+ for (i = 0; i < d->authNum; i++) {
+ if (auths[i]->name_length == 18 &&
+ !memcmp( auths[i]->name, "MIT-MAGIC-COOKIE-1", 18 ))
+ {
+ magicCookie = i;
+ if ((d->displayType & d_location) == dLocal)
+ writeLocalAuth( new, auths[i], d->name, &ok );
+#ifdef XDMCP
+ else
+ writeRemoteAuth( new, auths[i], (XdmcpNetaddr)d->peer.data,
+ d->peer.length, d->name, &ok );
+#endif
+ break;
+ }
+ }
+ /* now write other authorizations */
+ for (i = 0; i < d->authNum; i++) {
+ if (i != magicCookie) {
+ data_len = auths[i]->data_length;
+ /* client will just use default Kerberos cache, so don't
+ * even write cache info into the authority file.
+ */
+ if (auths[i]->name_length == 14 &&
+ !strncmp( auths[i]->name, "MIT-KERBEROS-5", 14 ))
+ auths[i]->data_length = 0;
+ if ((d->displayType & d_location) == dLocal)
+ writeLocalAuth( new, auths[i], d->name, &ok );
+#ifdef XDMCP
+ else
+ writeRemoteAuth( new, auths[i], (XdmcpNetaddr)d->peer.data,
+ d->peer.length, d->name, &ok );
+#endif
+ auths[i]->data_length = data_len;
+ }
+ }
+ if (!endUserAuth( old, new, new_name, ok )) {
+ if (!name) {
+ LogError( "Can't save user authorization\n" );
+ return;
+ }
+ undoUserAuth( name, new_name );
+ initAddrs();
+ goto fallback;
+ }
+ if (name)
+ envname = moveUserAuth( name, new_name, envname );
+ if (envname) {
+ userEnviron = setEnv( userEnviron, "XAUTHORITY", envname );
+ systemEnviron = setEnv( systemEnviron, "XAUTHORITY", envname );
+ }
+ /* a chown() used to be here, but this code runs as user anyway */
+ }
+ Debug( "done SetUserAuthorization\n" );
+}
+
+void
+RemoveUserAuthorization( struct display *d )
+{
+ Xauth **auths;
+ FILE *old, *new;
+ int i;
+ char name[NBSIZE], new_name[NBSIZE + 2];
+
+ if (!(auths = d->authorizations))
+ return;
+ Debug( "RemoveUserAuthorization\n" );
+ startUserAuth( name, new_name, &old, &new );
+ if (new) {
+ for (i = 0; i < d->authNum; i++) {
+ if ((d->displayType & d_location) == dLocal)
+ writeLocalAuth( new, auths[i], d->name, 0 );
+#ifdef XDMCP
+ else
+ writeRemoteAuth( new, auths[i], (XdmcpNetaddr)d->peer.data,
+ d->peer.length, d->name, 0 );
+#endif
+ }
+ if (endUserAuth( old, new, new_name, TRUE ))
+ (void)moveUserAuth( name, new_name, 0 );
+ else
+ undoUserAuth( name, new_name );
+ }
+ Debug( "done RemoveUserAuthorization\n" );
+}
diff --git a/kdm/backend/bootman.c b/kdm/backend/bootman.c
new file mode 100644
index 000000000..c6ac968a2
--- /dev/null
+++ b/kdm/backend/bootman.c
@@ -0,0 +1,282 @@
+/*
+
+Copyright 2005 Stephan Kulow <coolo@kde.org>
+Copyright 2005 Oswald Buddenhagen <ossi@kde.org>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the copyright holder.
+
+*/
+
+/*
+ * xdm - display manager daemon
+ * Author: Keith Packard, MIT X Consortium
+ *
+ * Boot options
+ */
+
+#include "dm.h"
+#include "dm_error.h"
+
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+
+extern char **environ;
+
+static int
+getNull( char ***opts ATTR_UNUSED, int *def ATTR_UNUSED, int *cur ATTR_UNUSED )
+{
+ return BO_NOMAN;
+}
+
+static int
+setNull( const char *opt ATTR_UNUSED, SdRec *sdr ATTR_UNUSED )
+{
+ return BO_NOMAN;
+}
+
+static char *
+match( char *obuf, int *blen, const char *key, int klen )
+{
+ char *buf = obuf;
+ if (memcmp( buf, key, klen ) || !isspace( buf[klen] ))
+ return 0;
+ buf += klen + 1;
+ for (; isspace( *buf ); buf++);
+ if (!*buf)
+ return 0;
+ *blen -= buf - obuf;
+ return buf;
+}
+
+#define GRUB_MENU "/boot/grub/menu.lst"
+
+static char *grub;
+
+static int
+getGrub( char ***opts, int *def, int *cur )
+{
+ FILE *f;
+ char *ptr, *linp;
+ int len;
+ char line[1000];
+
+ if (!grub && !(grub = locate( "grub" )))
+ return BO_NOMAN;
+
+ *def = 0;
+ *cur = -1;
+ *opts = initStrArr( 0 );
+
+ if (!(f = fopen( GRUB_MENU, "r" )))
+ return errno == ENOENT ? BO_NOMAN : BO_IO;
+ while ((len = fGets( line, sizeof(line), f )) != -1) {
+ for (linp = line; isspace(*linp); linp++, len--);
+ if ((ptr = match( linp, &len, "default", 7 )))
+ *def = atoi( ptr );
+ else if ((ptr = match( linp, &len, "title", 5 ))) {
+ for (; isspace( ptr[len - 1] ); len--);
+ *opts = addStrArr( *opts, ptr, len );
+ }
+ }
+ fclose( f );
+
+ return BO_OK;
+}
+
+static int
+setGrub( const char *opt, SdRec *sdr )
+{
+ FILE *f;
+ char *ptr;
+ int len, i;
+ char line[1000];
+
+ if (!(f = fopen( GRUB_MENU, "r" )))
+ return errno == ENOENT ? BO_NOMAN : BO_IO;
+ for (i = 0; (len = fGets( line, sizeof(line), f )) != -1; )
+ if ((ptr = match( line, &len, "title", 5 ))) {
+ if (!strcmp( ptr, opt )) {
+ fclose( f );
+ sdr->osindex = i;
+ sdr->bmstamp = mTime( GRUB_MENU );
+ return BO_OK;
+ }
+ i++;
+ }
+ fclose( f );
+ return BO_NOENT;
+}
+
+static void
+commitGrub( void )
+{
+ FILE *f;
+ int pid;
+ static const char *args[] = { 0, "--batch", "--no-floppy", 0 };
+
+ if (sdRec.bmstamp != mTime( GRUB_MENU ) &&
+ setGrub( sdRec.osname, &sdRec ) != BO_OK)
+ return;
+
+ args[0] = grub;
+ if ((f = pOpen( (char **)args, 'w', &pid ))) {
+ fprintf( f, "savedefault --default=%d --once\n", sdRec.osindex );
+ pClose( f, pid );
+ }
+}
+
+static char *lilo;
+
+static int
+getLilo( char ***opts, int *def, int *cur )
+{
+ FILE *f;
+ int cdef, pid, len, ret = BO_OK;
+ static const char *args[5] = { 0, "-w", "-v", "-q", 0 };
+ char buf[256], next[256];
+
+ if (!lilo && !(lilo = locate( "lilo" )))
+ return BO_NOMAN;
+
+ args[0] = lilo;
+ if (!(f = pOpen( (char **)args, 'r', &pid )))
+ return BO_IO;
+ *opts = 0;
+ next[0] = 0;
+ for (;;) {
+ if ((len = fGets( buf, sizeof(buf), f)) == -1) {
+ ret = BO_NOMAN;
+ goto out;
+ }
+ if (!memcmp( buf, "Images:", 7 ))
+ break;
+#define Ldeflin " Default boot command line:"
+ if (!memcmp( buf, Ldeflin, strlen(Ldeflin) )) {
+ memcpy( next, buf + strlen(Ldeflin) + 2, len - strlen(Ldeflin) - 3 );
+ next[len - strlen(Ldeflin) - 3] = 0;
+ }
+ }
+ cdef = *def = 0;
+ *cur = -1;
+ *opts = initStrArr( 0 );
+ while ((len = fGets( buf, sizeof(buf), f)) != -1)
+ if (buf[0] == ' ' && buf[1] == ' ' && buf[2] != ' ') {
+ if (buf[len - 1] == '*') {
+ *def = cdef;
+ len--;
+ }
+ for (; buf[len - 1] == ' '; len--);
+ *opts = addStrArr( *opts, buf + 2, len - 2 );
+ if (!strcmp( (*opts)[cdef], next ))
+ *cur = cdef;
+ cdef++;
+ }
+ out:
+ if (pClose( f, pid )) {
+ if (*opts)
+ freeStrArr( *opts );
+ return BO_IO;
+ }
+ return ret;
+}
+
+static int
+setLilo( const char *opt, SdRec *sdr ATTR_UNUSED )
+{
+ char **opts;
+ int def, cur, ret, i;
+
+ if ((ret = getLilo( &opts, &def, &cur )) != BO_OK)
+ return ret;
+ if (!*opt)
+ opt = 0;
+ else {
+ for (i = 0; opts[i]; i++)
+ if (!strcmp( opts[i], opt ))
+ goto oke;
+ freeStrArr( opts );
+ return BO_NOENT;
+ }
+ oke:
+ freeStrArr( opts );
+ return BO_OK;
+}
+
+static void
+commitLilo( void )
+{
+ static const char *args[5] = { 0, "-w", "-R", 0, 0 };
+
+ args[0] = lilo;
+ args[3] = sdRec.osname;
+ runAndWait( (char **)args, environ );
+}
+
+static struct {
+ int (*get)( char ***, int *, int * );
+ int (*set)( const char *, SdRec * );
+ void (*commit)( void );
+} bootOpts[] = {
+ { getNull, setNull, 0 },
+ { getGrub, setGrub, commitGrub },
+ { getLilo, setLilo, commitLilo },
+};
+
+int
+getBootOptions( char ***opts, int *def, int *cur )
+{
+ return bootOpts[bootManager].get( opts, def, cur );
+}
+
+int
+setBootOption( const char *opt, SdRec *sdr )
+{
+ int ret;
+
+ if (sdr->osname) {
+ free( sdr->osname );
+ sdr->osname = 0;
+ }
+ if (opt) {
+ if ((ret = bootOpts[bootManager].set( opt, sdr )) != BO_OK)
+ return ret;
+ if (!StrDup( &sdr->osname, opt ))
+ return BO_IO; /* BO_NOMEM */
+ }
+ return BO_OK;
+}
+
+void
+commitBootOption( void )
+{
+ if (sdRec.osname) {
+ bootOpts[bootManager].commit();
+/*
+ free( sdRec.osname );
+ sdRec.osname = 0;
+*/
+ }
+}
+
diff --git a/kdm/backend/choose.c b/kdm/backend/choose.c
new file mode 100644
index 000000000..ead841228
--- /dev/null
+++ b/kdm/backend/choose.c
@@ -0,0 +1,1051 @@
+/*
+
+Copyright 1990, 1998 The Open Group
+Copyright 2002-2004 Oswald Buddenhagen <ossi@kde.org>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the copyright holder.
+
+*/
+
+/*
+ * xdm - display manager daemon
+ * Author: Keith Packard, MIT X Consortium
+ *
+ * chooser backend
+ */
+
+#include <config.h>
+
+#ifdef XDMCP
+
+#include "dm.h"
+#include "dm_error.h"
+#include "dm_socket.h"
+
+#include <ctype.h>
+
+#ifdef __svr4__
+# include <sys/sockio.h>
+#endif
+#include <arpa/inet.h>
+
+#include <sys/ioctl.h>
+#ifdef STREAMSCONN
+# ifdef WINTCP /* NCR with Wollongong TCP */
+# include <netinet/ip.h>
+# endif
+# include <stropts.h>
+# include <tiuser.h>
+# include <netconfig.h>
+# include <netdir.h>
+#endif
+
+#if !defined(__GNU__) && !defined(__hpux) /* XXX __hpux might be wrong */
+# include <net/if.h>
+#endif
+
+#include <netdb.h>
+
+typedef struct _IndirectUsers {
+ struct _IndirectUsers *next;
+ ARRAY8 client;
+ CARD16 connectionType;
+} IndirectUsersRec, *IndirectUsersPtr;
+
+static IndirectUsersPtr indirectUsers;
+
+int
+RememberIndirectClient( ARRAY8Ptr clientAddress, CARD16 connectionType )
+{
+ IndirectUsersPtr i;
+
+ for (i = indirectUsers; i; i = i->next)
+ if (XdmcpARRAY8Equal( clientAddress, &i->client ) &&
+ connectionType == i->connectionType)
+ return 1;
+ if (!(i = (IndirectUsersPtr)Malloc( sizeof(IndirectUsersRec) )))
+ return 0;
+ if (!XdmcpCopyARRAY8( clientAddress, &i->client )) {
+ free( (char *)i );
+ return 0;
+ }
+ i->connectionType = connectionType;
+ i->next = indirectUsers;
+ indirectUsers = i;
+ return 1;
+}
+
+void
+ForgetIndirectClient( ARRAY8Ptr clientAddress, CARD16 connectionType )
+{
+ IndirectUsersPtr *i, ni;
+
+ for (i = &indirectUsers; *i; i = &(*i)->next)
+ if (XdmcpARRAY8Equal( clientAddress, &(*i)->client ) &&
+ connectionType == (*i)->connectionType)
+ {
+ ni = (*i)->next;
+ XdmcpDisposeARRAY8( &(*i)->client );
+ free( (char *)(*i) );
+ (*i) = ni;
+ break;
+ }
+}
+
+int
+IsIndirectClient( ARRAY8Ptr clientAddress, CARD16 connectionType )
+{
+ IndirectUsersPtr i;
+
+ for (i = indirectUsers; i; i = i->next)
+ if (XdmcpARRAY8Equal( clientAddress, &i->client ) &&
+ connectionType == i->connectionType)
+ return 1;
+ return 0;
+}
+
+typedef struct _Choices {
+ struct _Choices *next;
+ ARRAY8 client;
+ CARD16 connectionType;
+ ARRAY8 choice;
+ Time_t time;
+} ChoiceRec, *ChoicePtr;
+
+static ChoicePtr choices;
+
+ARRAY8Ptr
+IndirectChoice( ARRAY8Ptr clientAddress, CARD16 connectionType )
+{
+ ChoicePtr c, next, prev;
+
+ prev = 0;
+ for (c = choices; c; c = next) {
+ next = c->next;
+ Debug( "choice checking timeout: %ld >? %d\n",
+ (long)(now - c->time), choiceTimeout );
+ if (now - c->time > (Time_t)choiceTimeout) {
+ Debug( "timeout choice %ld > %d\n",
+ (long)(now - c->time), choiceTimeout );
+ if (prev)
+ prev->next = next;
+ else
+ choices = next;
+ XdmcpDisposeARRAY8( &c->client );
+ XdmcpDisposeARRAY8( &c->choice );
+ free( (char *)c );
+ } else {
+ if (XdmcpARRAY8Equal( clientAddress, &c->client ) &&
+ connectionType == c->connectionType)
+ return &c->choice;
+ prev = c;
+ }
+ }
+ return 0;
+}
+
+int
+RegisterIndirectChoice( ARRAY8Ptr clientAddress, CARD16 connectionType,
+ ARRAY8Ptr choice )
+{
+ ChoicePtr c;
+ int insert;
+#if 0
+ int found = 0;
+#endif
+
+ Debug( "got indirect choice back\n" );
+ for (c = choices; c; c = c->next) {
+ if (XdmcpARRAY8Equal( clientAddress, &c->client ) &&
+ connectionType == c->connectionType) {
+#if 0
+ found = 1;
+#endif
+ break;
+ }
+ }
+#if 0
+ if (!found)
+ return 0;
+#endif
+
+ insert = 0;
+ if (!c) {
+ insert = 1;
+ c = (ChoicePtr)Malloc( sizeof(ChoiceRec) );
+ if (!c)
+ return 0;
+ c->connectionType = connectionType;
+ if (!XdmcpCopyARRAY8( clientAddress, &c->client )) {
+ free( (char *)c );
+ return 0;
+ }
+ } else
+ XdmcpDisposeARRAY8( &c->choice );
+ if (!XdmcpCopyARRAY8( choice, &c->choice )) {
+ XdmcpDisposeARRAY8( &c->client );
+ free( (char *)c );
+ return 0;
+ }
+ if (insert) {
+ c->next = choices;
+ choices = c;
+ }
+ c->time = now;
+ return 1;
+}
+
+#if 0
+static void
+RemoveIndirectChoice( ARRAY8Ptr clientAddress, CARD16 connectionType )
+{
+ ChoicePtr c, prev;
+
+ prev = 0;
+ for (c = choices; c; c = c->next) {
+ if (XdmcpARRAY8Equal( clientAddress, &c->client ) &&
+ connectionType == c->connectionType)
+ {
+ if (prev)
+ prev->next = c->next;
+ else
+ choices = c->next;
+ XdmcpDisposeARRAY8( &c->client );
+ XdmcpDisposeARRAY8( &c->choice );
+ free( (char *)c );
+ return;
+ }
+ prev = c;
+ }
+}
+#endif
+
+
+/* ####################### */
+
+
+typedef struct _hostAddr {
+ struct _hostAddr *next;
+ struct sockaddr *addr;
+ int addrlen;
+ xdmOpCode type;
+} HostAddr;
+
+static HostAddr *hostAddrdb;
+
+typedef struct _hostName {
+ struct _hostName *next;
+ unsigned willing:1, alive:1;
+ ARRAY8 hostname, status;
+ CARD16 connectionType;
+ ARRAY8 hostaddr;
+} HostName;
+
+static HostName *hostNamedb;
+
+static XdmcpBuffer directBuffer, broadcastBuffer;
+static XdmcpBuffer buffer;
+
+static int socketFD;
+#if defined(IPv6) && defined(AF_INET6)
+static int socket6FD;
+#endif
+
+
+static void
+doPingHosts()
+{
+ HostAddr *hosts;
+
+ for (hosts = hostAddrdb; hosts; hosts = hosts->next)
+#if defined(IPv6) && defined(AF_INET6)
+ XdmcpFlush( hosts->addr->sa_family == AF_INET6 ? socket6FD : socketFD,
+#else
+ XdmcpFlush( socketFD,
+#endif
+ hosts->type == QUERY ? &directBuffer : &broadcastBuffer,
+ (XdmcpNetaddr)hosts->addr, hosts->addrlen );
+}
+
+static int
+addHostname( ARRAY8Ptr hostname, ARRAY8Ptr status,
+ struct sockaddr *addr, int will )
+{
+ HostName **names, *name;
+ ARRAY8 hostAddr;
+ CARD16 connectionType;
+
+ switch (addr->sa_family) {
+ case AF_INET:
+ hostAddr.data =
+ (CARD8 *)& ((struct sockaddr_in *)addr)->sin_addr;
+ hostAddr.length = 4;
+ connectionType = FamilyInternet;
+ break;
+#if defined(IPv6) && defined(AF_INET6)
+ case AF_INET6:
+ hostAddr.data =
+ (CARD8 *)&((struct sockaddr_in6 *)addr)->sin6_addr;
+ hostAddr.length = 16;
+ connectionType = FamilyInternet6;
+ break;
+#endif
+ default:
+ hostAddr.data = (CARD8 *)"";
+ hostAddr.length = 0;
+ connectionType = FamilyLocal;
+ break;
+ }
+ for (names = &hostNamedb; *names; names = &(*names)->next) {
+ name = *names;
+ if (connectionType == name->connectionType &&
+ XdmcpARRAY8Equal( &hostAddr, &name->hostaddr ))
+ {
+ if (XdmcpARRAY8Equal( status, &name->status ))
+ return 0;
+ XdmcpDisposeARRAY8( &name->status );
+ XdmcpDisposeARRAY8( hostname );
+
+ GSendInt( G_Ch_ChangeHost );
+ goto gotold;
+ }
+ }
+ if (!(name = (HostName *)Malloc( sizeof(*name) )))
+ return 0;
+ if (hostname->length) {
+ switch (addr->sa_family) {
+ case AF_INET:
+#if defined(IPv6) && defined(AF_INET6)
+ case AF_INET6:
+#endif
+ {
+ struct hostent *hostent;
+ char *host;
+
+ hostent = gethostbyaddr( (char *)hostAddr.data,
+ hostAddr.length, addr->sa_family );
+ if (hostent) {
+ XdmcpDisposeARRAY8( hostname );
+ host = hostent->h_name;
+ XdmcpAllocARRAY8( hostname, strlen( host ) );
+ memmove( hostname->data, host, hostname->length );
+ }
+ }
+ }
+ }
+ if (!XdmcpAllocARRAY8( &name->hostaddr, hostAddr.length )) {
+ free( (char *)name );
+ return 0;
+ }
+ memmove( name->hostaddr.data, hostAddr.data, hostAddr.length );
+ name->connectionType = connectionType;
+ name->hostname = *hostname;
+
+ *names = name;
+ name->next = 0;
+
+ GSendInt( G_Ch_AddHost );
+ gotold:
+ name->alive = 1;
+ name->willing = will;
+ name->status = *status;
+
+ GSendInt( (int)name ); /* just an id */
+ GSendNStr( (char *)name->hostname.data, name->hostname.length );
+ GSendNStr( (char *)name->status.data, name->status.length );
+ GSendInt( will );
+
+ return 1;
+}
+
+static void
+disposeHostname( HostName *host )
+{
+ XdmcpDisposeARRAY8( &host->hostname );
+ XdmcpDisposeARRAY8( &host->hostaddr );
+ XdmcpDisposeARRAY8( &host->status );
+ free( (char *)host );
+}
+
+static void
+emptyHostnames( void )
+{
+ HostName *host, *nhost;
+
+ for (host = hostNamedb; host; host = nhost) {
+ nhost = host->next;
+ disposeHostname( host );
+ }
+ hostNamedb = 0;
+}
+
+static void
+receivePacket( int sfd )
+{
+ XdmcpHeader header;
+ ARRAY8 authenticationName;
+ ARRAY8 hostname;
+ ARRAY8 status;
+ int saveHostname = 0;
+#if defined(IPv6) && defined(AF_INET6)
+ struct sockaddr_storage addr;
+#else
+ struct sockaddr addr;
+#endif
+ int addrlen;
+
+ addrlen = sizeof(addr);
+ if (!XdmcpFill( sfd, &buffer, (XdmcpNetaddr)&addr, &addrlen ))
+ return;
+ if (!XdmcpReadHeader( &buffer, &header ))
+ return;
+ if (header.version != XDM_PROTOCOL_VERSION)
+ return;
+ hostname.data = 0;
+ status.data = 0;
+ authenticationName.data = 0;
+ switch (header.opcode) {
+ case WILLING:
+ if (XdmcpReadARRAY8( &buffer, &authenticationName ) &&
+ XdmcpReadARRAY8( &buffer, &hostname ) &&
+ XdmcpReadARRAY8( &buffer, &status )) {
+ if (header.length == 6 + authenticationName.length +
+ hostname.length + status.length) {
+ if (addHostname( &hostname, &status,
+ (struct sockaddr *)&addr, 1 ))
+ saveHostname = 1;
+ }
+ }
+ XdmcpDisposeARRAY8( &authenticationName );
+ break;
+ case UNWILLING:
+ if (XdmcpReadARRAY8( &buffer, &hostname ) &&
+ XdmcpReadARRAY8( &buffer, &status )) {
+ if (header.length == 4 + hostname.length + status.length) {
+ if (addHostname( &hostname, &status,
+ (struct sockaddr *)&addr, 0 ))
+ saveHostname = 1;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ if (!saveHostname) {
+ XdmcpDisposeARRAY8( &hostname );
+ XdmcpDisposeARRAY8( &status );
+ }
+}
+
+static void
+addHostaddr( HostAddr **hosts, struct sockaddr *addr, int len, xdmOpCode type )
+{
+ HostAddr *host;
+
+ Debug( "adding host %[*hhu, type %d\n", len, addr, type );
+ for (host = *hosts; host; host = host->next)
+ if (host->type == type && host->addr->sa_family == addr->sa_family)
+ switch (addr->sa_family) {
+ case AF_INET:
+ {
+ struct sockaddr_in *na = (struct sockaddr_in *)addr;
+ struct sockaddr_in *oa = (struct sockaddr_in *)host->addr;
+ if (na->sin_port == oa->sin_port &&
+ na->sin_addr.s_addr == oa->sin_addr.s_addr)
+ return;
+ break;
+ }
+#if defined(IPv6) && defined(AF_INET6)
+ case AF_INET6:
+ {
+ struct sockaddr_in6 *na = (struct sockaddr_in6 *)addr;
+ struct sockaddr_in6 *oa = (struct sockaddr_in6 *)host->addr;
+ if (na->sin6_port == oa->sin6_port &&
+ !memcmp( &na->sin6_addr, &oa->sin6_addr, 16 ))
+ return;
+ break;
+ }
+#endif
+ default: /* ... */
+ break;
+ }
+ Debug( " not dupe\n" );
+ if (!(host = (HostAddr *)Malloc( sizeof(*host) )))
+ return;
+ if (!(host->addr = (struct sockaddr *)Malloc( len ))) {
+ free( (char *)host );
+ return;
+ }
+ memcpy( (char *)host->addr, (char *)addr, len );
+ host->addrlen = len;
+ host->type = type;
+ host->next = *hosts;
+ *hosts = host;
+}
+
+static void
+registerHostaddr( struct sockaddr *addr, int len, xdmOpCode type )
+{
+ addHostaddr( &hostAddrdb, addr, len, type );
+}
+
+static void
+emptyPingHosts( void )
+{
+ HostAddr *host, *nhost;
+
+ for (host = hostAddrdb; host; host = nhost) {
+ nhost = host->next;
+ free( host->addr );
+ free( host );
+ }
+ hostAddrdb = 0;
+}
+
+/* Handle variable length ifreq in BNR2 and later */
+#ifdef VARIABLE_IFREQ
+# define ifr_size(p) \
+ (sizeof(struct ifreq) + \
+ (p->ifr_addr.sa_len > sizeof (p->ifr_addr) ? \
+ p->ifr_addr.sa_len - sizeof (p->ifr_addr) : 0))
+#else
+# define ifr_size(p) (sizeof(struct ifreq))
+#endif
+
+#define IFC_REQ(ifc) ifc.ifc_req
+
+#ifndef SYSV_SIOCGIFCONF
+# define ifioctl ioctl
+#endif
+
+static void
+registerBroadcastForPing( void )
+{
+ struct sockaddr_in in_addr;
+
+#ifdef __GNU__
+ in_addr.sin_addr.s_addr = htonl( 0xFFFFFFFF );
+ in_addr.sin_port = htons( XDM_UDP_PORT );
+ registerHostaddr( (struct sockaddr *)&in_addr, sizeof(in_addr),
+ BROADCAST_QUERY );
+#else /* __GNU__ */
+ struct ifconf ifc;
+ register struct ifreq *ifr;
+ struct sockaddr broad_addr;
+ char buf[2048], *cp, *cplim;
+# ifdef WINTCP /* NCR with Wollongong TCP */
+ int ipfd;
+ struct ifconf *ifcp;
+ struct strioctl ioc;
+ int n;
+
+ ifcp = (struct ifconf *)buf;
+ ifcp->ifc_buf = buf + 4;
+ ifcp->ifc_len = sizeof(buf) - 4;
+
+ if ((ipfd = open( "/dev/ip", O_RDONLY )) < 0) {
+ t_error( "RegisterBroadcastForPing() t_open(/dev/ip) failed" );
+ return;
+ }
+
+ ioc.ic_cmd = IPIOC_GETIFCONF;
+ ioc.ic_timout = 60;
+ ioc.ic_len = sizeof(buf);
+ ioc.ic_dp = (char *)ifcp;
+
+ if (ioctl( ipfd, (int)I_STR, (char *)&ioc ) < 0) {
+ perror( "RegisterBroadcastForPing() ioctl(I_STR(IPIOC_GETIFCONF)) failed" );
+ close( ipfd );
+ return;
+ }
+
+ for (ifr = ifcp->ifc_req, n = ifcp->ifc_len / sizeof(struct ifreq);
+ --n >= 0; ifr++)
+# else /* WINTCP */
+ ifc.ifc_len = sizeof(buf);
+ ifc.ifc_buf = buf;
+ if (ifioctl(socketFD, (int)SIOCGIFCONF, (char *)&ifc) < 0)
+ return;
+
+ cplim = (char *)IFC_REQ( ifc ) + ifc.ifc_len;
+
+ for (cp = (char *)IFC_REQ( ifc ); cp < cplim; cp += ifr_size(ifr))
+# endif /* WINTCP */
+ {
+# ifndef WINTCP
+ ifr = (struct ifreq *)cp;
+# endif
+ if (ifr->ifr_addr.sa_family != AF_INET)
+ continue;
+
+ broad_addr = ifr->ifr_addr;
+ ((struct sockaddr_in *)&broad_addr)->sin_addr.s_addr =
+ htonl( INADDR_BROADCAST );
+# ifdef SIOCGIFBRDADDR
+ {
+ struct ifreq broad_req;
+
+ broad_req = *ifr;
+# ifdef WINTCP /* NCR with Wollongong TCP */
+ ioc.ic_cmd = IPIOC_GETIFFLAGS;
+ ioc.ic_timout = 0;
+ ioc.ic_len = sizeof(broad_req);
+ ioc.ic_dp = (char *)&broad_req;
+
+ if (ioctl (ipfd, I_STR, (char *) &ioc ) != -1 &&
+# else /* WINTCP */
+ if (ifioctl( socketFD, SIOCGIFFLAGS, (char *)&broad_req ) !=
+ -1 &&
+# endif /* WINTCP */
+ (broad_req.ifr_flags & IFF_BROADCAST) &&
+ (broad_req.ifr_flags & IFF_UP))
+ {
+ broad_req = *ifr;
+# ifdef WINTCP /* NCR with Wollongong TCP */
+ ioc.ic_cmd = IPIOC_GETIFBRDADDR;
+ ioc.ic_timout = 0;
+ ioc.ic_len = sizeof(broad_req);
+ ioc.ic_dp = (char *)&broad_req;
+
+ if (ioctl( ipfd, I_STR, (char *) &ioc) != -1 )
+# else /* WINTCP */
+ if (ifioctl( socketFD, SIOCGIFBRDADDR, (char *)&broad_req )
+ != -1)
+# endif /* WINTCP */
+ broad_addr = broad_req.ifr_addr;
+ else
+ continue;
+ } else
+ continue;
+ }
+# endif
+ in_addr = *((struct sockaddr_in *)&broad_addr);
+ in_addr.sin_port = htons( XDM_UDP_PORT );
+# ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ in_addr.sin_len = sizeof(in_addr);
+# endif
+ registerHostaddr( (struct sockaddr *)&in_addr, sizeof(in_addr),
+ BROADCAST_QUERY );
+ }
+#endif
+}
+
+static int
+makeSockAddrs( const char *name, HostAddr **hosts )
+{
+#if defined(IPv6) && defined(AF_INET6)
+ struct addrinfo *ai, *nai, hints;
+ bzero( &hints, sizeof(hints) );
+ hints.ai_socktype = SOCK_DGRAM;
+ if (getaddrinfo( name, stringify( XDM_UDP_PORT ), &hints, &ai ))
+ return 0;
+ for (nai = ai; nai; nai = nai->ai_next)
+ if ((nai->ai_family == AF_INET) || (nai->ai_family == AF_INET6))
+ addHostaddr( hosts, nai->ai_addr, nai->ai_addrlen,
+ (nai->ai_family == AF_INET ?
+ IN_MULTICAST( ((struct sockaddr_in *)nai->ai_addr)->sin_addr.s_addr ) :
+ IN6_IS_ADDR_MULTICAST( &((struct sockaddr_in6 *)nai->ai_addr)->sin6_addr )) ?
+ BROADCAST_QUERY : QUERY );
+#else
+ struct sockaddr_in in_addr;
+ /* Per RFC 1123, check first for IP address in dotted-decimal form */
+ if ((in_addr.sin_addr.s_addr = inet_addr( name )) == (unsigned)-1) {
+ struct hostent *hostent;
+ if (!(hostent = gethostbyname( name )) ||
+ hostent->h_addrtype != AF_INET)
+ return 0;
+ memcpy( &in_addr.sin_addr, hostent->h_addr, 4 );
+ }
+ in_addr.sin_family = AF_INET;
+ in_addr.sin_port = htons( XDM_UDP_PORT );
+# ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ in_addr.sin_len = sizeof(in_addr);
+# endif
+ addHostaddr( hosts, (struct sockaddr *)&in_addr, sizeof(in_addr),
+# ifdef IN_MULTICAST
+ IN_MULTICAST( in_addr.sin_addr.s_addr ) ?
+ BROADCAST_QUERY :
+# endif
+ QUERY );
+#endif
+ return 1;
+}
+
+/*
+ * Register the address for this host.
+ * Called for interactively specified hosts.
+ * The special names "BROADCAST" and "*" look up all the broadcast
+ * addresses on the local host.
+ */
+
+static int
+registerForPing( const char *name )
+{
+ Debug( "manual host registration: %s\n", name );
+ if (!strcmp( name, "BROADCAST" ) || !strcmp( name, "*" ))
+ registerBroadcastForPing();
+ else if (!makeSockAddrs( name, &hostAddrdb ))
+ return 0;
+ return 1;
+}
+
+/*ARGSUSED*/
+static void
+AddChooserHost(
+ CARD16 connectionType,
+ ARRAY8Ptr addr,
+ char *closure ATTR_UNUSED )
+{
+ if (connectionType == FamilyBroadcast) {
+ registerBroadcastForPing();
+ return;
+ }
+ Debug( "internal host registration: %[*hhu, family %hx\n",
+ addr->length, addr->data, connectionType );
+ if (connectionType == FamilyInternet) {
+ struct sockaddr_in in_addr;
+ in_addr.sin_family = AF_INET;
+ memmove( &in_addr.sin_addr, addr->data, 4 );
+ in_addr.sin_port = htons( XDM_UDP_PORT );
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ in_addr.sin_len = sizeof(in_addr);
+#endif
+ registerHostaddr( (struct sockaddr *)&in_addr, sizeof(in_addr),
+#ifdef IN_MULTICAST
+ IN_MULTICAST( in_addr.sin_addr.s_addr ) ?
+ BROADCAST_QUERY :
+#endif
+ QUERY );
+ }
+#if defined(IPv6) && defined(AF_INET6)
+ else if (connectionType == FamilyInternet6) {
+ struct sockaddr_in6 in6_addr;
+ in6_addr.sin6_family = AF_INET6;
+ memmove( &in6_addr.sin6_addr, addr->data, 16 );
+ in6_addr.sin6_port = htons( XDM_UDP_PORT );
+#ifdef SIN6_LEN
+ in6_addr.sin6_len = sizeof(in6_addr);
+#endif
+ registerHostaddr( (struct sockaddr *)&in6_addr, sizeof(in6_addr),
+ IN6_IS_ADDR_MULTICAST( &in6_addr.sin6_addr ) ?
+ BROADCAST_QUERY : QUERY );
+ }
+#endif
+}
+
+static ARRAYofARRAY8 AuthenticationNames;
+
+#if 0
+static void RegisterAuthenticationName( char *name, int namelen )
+{
+ ARRAY8Ptr authName;
+ if (!XdmcpReallocARRAYofARRAY8( &AuthenticationNames,
+ AuthenticationNames.length + 1 ))
+ return;
+ authName = &AuthenticationNames.data[AuthenticationNames.length - 1];
+ if (!XdmcpAllocARRAY8( authName, namelen ))
+ return;
+ memmove( authName->data, name, namelen );
+}
+#endif
+
+static int
+initXDMCP()
+{
+ XdmcpHeader header;
+#if 0
+ int i;
+#endif
+#ifndef STREAMSCONN
+#ifdef SO_BROADCAST
+ int soopts;
+#endif
+#endif
+
+ header.version = XDM_PROTOCOL_VERSION;
+ header.length = 1;
+#if 0
+ for (i = 0; i < (int)AuthenticationNames.length; i++)
+ header.length += 2 + AuthenticationNames.data[i].length;
+#endif
+
+ header.opcode = (CARD16)BROADCAST_QUERY;
+ XdmcpWriteHeader( &broadcastBuffer, &header );
+ XdmcpWriteARRAYofARRAY8( &broadcastBuffer, &AuthenticationNames );
+
+ header.opcode = (CARD16)QUERY;
+ XdmcpWriteHeader( &directBuffer, &header );
+ XdmcpWriteARRAYofARRAY8( &directBuffer, &AuthenticationNames );
+
+#if defined(STREAMSCONN)
+ if ((socketFD = t_open( "/dev/udp", O_RDWR, 0 )) < 0)
+ return 0;
+
+ if (t_bind( socketFD, NULL, NULL ) < 0) {
+ t_close( socketFD );
+ return 0;
+ }
+
+ /*
+ * This part of the code looks contrived. It will actually fit in nicely
+ * when the CLTS part of Xtrans is implemented.
+ */
+ {
+ struct netconfig *nconf;
+
+ if ((nconf = getnetconfigent( "udp" )) == NULL) {
+ t_unbind( socketFD );
+ t_close( socketFD );
+ return 0;
+ }
+
+ if (netdir_options( nconf, ND_SET_BROADCAST, socketFD, NULL )) {
+ freenetconfigent( nconf );
+ t_unbind( socketFD );
+ t_close( socketFD );
+ return 0;
+ }
+
+ freenetconfigent( nconf );
+ }
+#else
+ if ((socketFD = socket( AF_INET, SOCK_DGRAM, 0 )) < 0)
+ return 0;
+#if defined(IPv6) && defined(AF_INET6)
+ socket6FD = socket( AF_INET6, SOCK_DGRAM, 0 );
+#endif
+# ifdef SO_BROADCAST
+ soopts = 1;
+ if (setsockopt( socketFD, SOL_SOCKET, SO_BROADCAST, (char *)&soopts,
+ sizeof(soopts) ) < 0)
+ perror( "setsockopt" );
+# endif
+#endif
+
+ return 1;
+}
+
+static void ATTR_NORETURN
+chooseHost( int hid )
+{
+ HostName *h;
+#if defined(IPv6) && defined(AF_INET6)
+ char addr[64];
+#endif
+
+ for (h = hostNamedb; h; h = h->next)
+ if ((int)h == hid) {
+ /* XXX error handling */
+ GSet( &mstrtalk );
+ if ((td->displayType & d_location) == dLocal) {
+ GSendInt( D_RemoteHost );
+#if defined(IPv6) && defined(AF_INET6)
+ switch (h->connectionType) {
+ case FamilyInternet6:
+ inet_ntop( AF_INET6, h->hostaddr.data, addr, sizeof(addr) );
+ break;
+ default: /* FamilyInternet */
+ inet_ntop( AF_INET, h->hostaddr.data, addr, sizeof(addr) );
+ break;
+ }
+ GSendStr( addr );
+#else
+ GSendStr( inet_ntoa( *(struct in_addr *)h->hostaddr.data ) );
+#endif
+ CloseGreeter( FALSE );
+ SessionExit( EX_REMOTE );
+ } else {
+ GSendInt( D_ChooseHost );
+ GSendArr( td->clientAddr.length, (char *)td->clientAddr.data );
+ GSendInt( td->connectionType );
+ GSendArr( h->hostaddr.length, (char *)h->hostaddr.data );
+ CloseGreeter( FALSE );
+ goto bout;
+ }
+ break;
+ }
+/* LogError( "Internal error: chose unexisting host\n" ); */
+ bout:
+ SessionExit( EX_NORMAL );
+}
+
+static void
+directChooseHost( const char *name )
+{
+ HostAddr *hosts = 0;
+
+ if (!makeSockAddrs( name, &hosts ))
+ return;
+ GSendInt( G_Ch_Exit );
+ /* XXX error handling */
+ GSet( &mstrtalk );
+ if ((td->displayType & d_location) == dLocal) {
+ GSendInt( D_RemoteHost );
+ GSendStr( name );
+ CloseGreeter( FALSE );
+ SessionExit( EX_REMOTE );
+ } else {
+ GSendInt( D_ChooseHost );
+ GSendArr( td->clientAddr.length, (char *)td->clientAddr.data );
+ GSendInt( td->connectionType );
+ GSendArr( hosts->addrlen, (char *)hosts->addr );
+ CloseGreeter( FALSE );
+ SessionExit( EX_NORMAL );
+ }
+}
+
+#define PING_TRIES 3
+
+int
+DoChoose()
+{
+ HostName **hp, *h;
+ char *host, **hostp;
+ struct timeval *to, tnow, nextPing;
+ int pingTry, n, cmd;
+ FD_TYPE rfds;
+ static int xdmcpInited;
+
+ OpenGreeter();
+ GSendInt( G_Choose );
+ switch (cmd = CtrlGreeterWait( TRUE )) {
+ case G_Ready:
+ break;
+ default: /* error */
+ return cmd;
+ }
+
+ if (!xdmcpInited) {
+ if (!initXDMCP())
+ SessionExit( EX_UNMANAGE_DPY );
+ xdmcpInited = 1;
+ }
+ if ((td->displayType & d_location) == dLocal) {
+ /* XXX the config reader should do the lookup already */
+ for (hostp = td->chooserHosts; *hostp; hostp++)
+ if (!registerForPing( *hostp ))
+ LogError( "Unkown host %\"s specified for local chooser preload of display %s\n", *hostp, td->name );
+ } else
+ ForEachChooserHost( &td->clientAddr, td->connectionType,
+ AddChooserHost, 0 );
+
+ GSendInt( 0 ); /* entering async mode signal */
+
+ reping:
+ for (h = hostNamedb; h; h = h->next)
+ h->alive = 0;
+ pingTry = 0;
+ goto pingen;
+
+ for (;;) {
+ to = 0;
+ if (pingTry <= PING_TRIES) {
+ gettimeofday( &tnow, 0 );
+ if (nextPing.tv_sec < tnow.tv_sec ||
+ (nextPing.tv_sec == tnow.tv_sec &&
+ nextPing.tv_usec < tnow.tv_usec)) {
+ if (pingTry < PING_TRIES) {
+ pingen:
+ pingTry++;
+ doPingHosts();
+ gettimeofday( &tnow, 0 );
+ nextPing = tnow;
+ nextPing.tv_sec++;
+ } else {
+ for (hp = &hostNamedb; *hp; )
+ if (!(*hp)->alive) {
+ h = (*hp)->next;
+ disposeHostname( *hp );
+ GSendInt( G_Ch_RemoveHost );
+ GSendInt( (int)*hp ); /* just an id */
+ *hp = h;
+ } else
+ hp = &(*hp)->next;
+ goto noto;
+ }
+ }
+ to = &tnow;
+ tnow.tv_sec = nextPing.tv_sec - tnow.tv_sec;
+ tnow.tv_usec = nextPing.tv_usec - tnow.tv_usec;
+ if (tnow.tv_usec < 0) {
+ tnow.tv_usec += 1000000;
+ tnow.tv_sec--;
+ }
+ }
+ noto:
+ FD_ZERO( &rfds );
+ FD_SET( grtproc.pipe.rfd, &rfds );
+ FD_SET( socketFD, &rfds );
+#if defined(IPv6) && defined(AF_INET6)
+ if (socket6FD >= 0)
+ FD_SET( socket6FD, &rfds );
+#endif
+ n = grtproc.pipe.rfd;
+ if (socketFD > n)
+ n = socketFD;
+#if defined(IPv6) && defined(AF_INET6)
+ if (socket6FD > n)
+ n = socket6FD;
+#endif
+ if (select( n + 1, &rfds, 0, 0, to ) > 0) {
+ if (FD_ISSET( grtproc.pipe.rfd, &rfds ))
+ switch (cmd = CtrlGreeterWait( FALSE )) {
+ case -1:
+ break;
+ case G_Ch_Refresh:
+ goto reping;
+ case G_Ch_RegisterHost:
+ host = GRecvStr();
+ if (!registerForPing( host )) {
+ GSendInt( G_Ch_BadHost );
+ GSendStr( host );
+ }
+ free( host );
+ goto reping;
+ case G_Ch_DirectChoice:
+ host = GRecvStr();
+ directChooseHost( host );
+ GSendInt( G_Ch_BadHost );
+ GSendStr( host );
+ free( host );
+ break;
+ case G_Ready:
+ chooseHost( GRecvInt() );
+ /* NOTREACHED */
+ default:
+ emptyHostnames();
+ emptyPingHosts();
+ return cmd;
+ }
+ if (FD_ISSET( socketFD, &rfds ))
+ receivePacket( socketFD );
+#if defined(IPv6) && defined(AF_INET6)
+ if (socket6FD >= 0 && FD_ISSET( socket6FD, &rfds ))
+ receivePacket( socket6FD );
+#endif
+ }
+ }
+
+}
+
+#endif /* XDMCP */
diff --git a/kdm/backend/client.c b/kdm/backend/client.c
new file mode 100644
index 000000000..c807b6e15
--- /dev/null
+++ b/kdm/backend/client.c
@@ -0,0 +1,1760 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2000-2004 Oswald Buddenhagen <ossi@kde.org>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the copyright holder.
+
+*/
+
+/*
+ * xdm - display manager daemon
+ * Author: Keith Packard, MIT X Consortium
+ *
+ * user verification and session initiation.
+ */
+
+#include "dm.h"
+#include "dm_auth.h"
+#include "dm_error.h"
+
+#include <sys/stat.h>
+#include <pwd.h>
+#include <grp.h>
+#ifdef SECURE_RPC
+# include <rpc/rpc.h>
+# include <rpc/key_prot.h>
+extern int key_setnet( struct key_netstarg *arg );
+#endif
+#ifdef K5AUTH
+# include <krb5/krb5.h>
+#endif
+#ifdef HAVE_SETUSERCONTEXT
+# include <login_cap.h>
+#endif
+#ifdef USE_PAM
+# ifdef HAVE_PAM_PAM_APPL_H
+# include <pam/pam_appl.h>
+# else
+# include <security/pam_appl.h>
+# endif
+#elif defined(_AIX) /* USE_PAM */
+# include <login.h>
+# include <usersec.h>
+extern int loginrestrictions( const char *Name, const int Mode, const char *Tty, char **Msg );
+extern int loginfailed( const char *User, const char *Host, const char *Tty );
+extern int loginsuccess( const char *User, const char *Host, const char *Tty, char **Msg );
+#else /* USE_PAM || _AIX */
+# ifdef KERBEROS
+# include <sys/param.h>
+# include <krb.h>
+# ifndef NO_AFS
+# include <kafs.h>
+# endif
+# endif
+/* for nologin */
+# include <sys/types.h>
+# include <unistd.h>
+/* for expiration */
+# include <time.h>
+#endif /* USE_PAM || _AIX */
+#ifdef HAVE_SHADOW
+# include <shadow.h>
+#endif
+#include <signal.h>
+
+/*
+ * Session data, mostly what struct verify_info was for
+ */
+char *curuser;
+char *curpass;
+char *curtype;
+char *newpass;
+char **userEnviron;
+char **systemEnviron;
+static int curuid;
+static int curgid;
+int cursource;
+
+char *dmrcuser;
+char *curdmrc;
+char *newdmrc;
+
+static struct passwd *p;
+#ifdef HAVE_SETUSERCONTEXT
+# ifdef HAVE_LOGIN_GETCLASS
+login_cap_t *lc;
+# else
+struct login_cap *lc;
+# endif
+#endif
+#ifdef USE_PAM
+static pam_handle_t *pamh;
+#elif defined(_AIX)
+static char tty[16], hostname[100];
+#else
+# ifdef USESHADOW
+static struct spwd *sp;
+# endif
+# ifdef KERBEROS
+static char krbtkfile[MAXPATHLEN];
+# endif
+#endif
+
+#define V_RET_AUTH \
+ do { \
+ PrepErrorGreet (); \
+ GSendInt (V_AUTH); \
+ return 0; \
+ } while(0)
+
+#define V_RET_FAIL(m) \
+ do { \
+ PrepErrorGreet (); \
+ GSendInt (V_MSG_ERR); \
+ GSendStr (m); \
+ GSendInt (V_FAIL); \
+ return 0; \
+ } while(0)
+
+#ifdef USE_PAM
+
+# ifdef PAM_MESSAGE_NONCONST
+typedef struct pam_message pam_message_type;
+typedef void *pam_gi_type;
+# else
+typedef const struct pam_message pam_message_type;
+typedef const void *pam_gi_type;
+# endif
+
+struct pam_data {
+ GConvFunc gconv;
+ int usecur;
+ int abort;
+};
+
+static int
+PAM_conv( int num_msg,
+ pam_message_type **msg,
+ struct pam_response **resp,
+ void *appdata_ptr )
+{
+ int count;
+ struct pam_response *reply;
+ struct pam_data *pd = (struct pam_data *)appdata_ptr;
+
+ if (!(reply = Calloc( num_msg, sizeof(*reply) )))
+ return PAM_CONV_ERR;
+
+ ReInitErrorLog();
+ Debug( "PAM_conv\n" );
+ for (count = 0; count < num_msg; count++)
+ switch (msg[count]->msg_style) {
+ case PAM_TEXT_INFO:
+ Debug( " PAM_TEXT_INFO: %s\n", msg[count]->msg );
+ PrepErrorGreet();
+ GSendInt( V_MSG_INFO );
+ GSendStr( msg[count]->msg );
+ continue;
+ case PAM_ERROR_MSG:
+ Debug( " PAM_ERROR_MSG: %s\n", msg[count]->msg );
+ PrepErrorGreet();
+ GSendInt( V_MSG_ERR );
+ GSendStr( msg[count]->msg );
+ continue;
+ default:
+ /* could do better error handling here, but see below ... */
+ if (pd->usecur) {
+ switch (msg[count]->msg_style) {
+ /* case PAM_PROMPT_ECHO_ON: cannot happen */
+ case PAM_PROMPT_ECHO_OFF:
+ Debug( " PAM_PROMPT_ECHO_OFF (usecur): %s\n", msg[count]->msg );
+ if (!curpass)
+ pd->gconv( GCONV_PASS, 0 );
+ StrDup( &reply[count].resp, curpass );
+ break;
+ default:
+ LogError( "Unknown PAM message style <%d>\n", msg[count]->msg_style );
+ goto conv_err;
+ }
+ } else {
+ switch (msg[count]->msg_style) {
+ case PAM_PROMPT_ECHO_ON:
+ Debug( " PAM_PROMPT_ECHO_ON: %s\n", msg[count]->msg );
+ reply[count].resp = pd->gconv( GCONV_NORMAL, msg[count]->msg );
+ break;
+ case PAM_PROMPT_ECHO_OFF:
+ Debug( " PAM_PROMPT_ECHO_OFF: %s\n", msg[count]->msg );
+ reply[count].resp = pd->gconv( GCONV_HIDDEN, msg[count]->msg );
+ break;
+#ifdef PAM_BINARY_PROMPT
+ case PAM_BINARY_PROMPT:
+ Debug( " PAM_BINARY_PROMPT\n" );
+ reply[count].resp = pd->gconv( GCONV_BINARY, msg[count]->msg );
+ break;
+#endif
+ default:
+ LogError( "Unknown PAM message style <%d>\n", msg[count]->msg_style );
+ goto conv_err;
+ }
+ }
+ if (!reply[count].resp) {
+ Debug( " PAM_conv aborted\n" );
+ pd->abort = TRUE;
+ goto conv_err;
+ }
+ reply[count].resp_retcode = PAM_SUCCESS; /* unused in linux-pam */
+ }
+ Debug( " PAM_conv success\n" );
+ *resp = reply;
+ return PAM_SUCCESS;
+
+ conv_err:
+ for (; count >= 0; count--)
+ if (reply[count].resp)
+ switch (msg[count]->msg_style) {
+ case PAM_PROMPT_ECHO_ON:
+ case PAM_PROMPT_ECHO_OFF: /* could wipe ... */
+#ifdef PAM_BINARY_PROMPT
+ case PAM_BINARY_PROMPT: /* ... that too ... */
+#endif
+ free( reply[count].resp );
+ break;
+ }
+ free( reply );
+ return PAM_CONV_ERR;
+}
+
+static int
+PAM_conv_null( int num_msg,
+ pam_message_type **msg,
+ struct pam_response **resp,
+ void *appdata_ptr ATTR_UNUSED )
+{
+ int count;
+ struct pam_response *reply;
+
+ if (!(reply = Calloc( num_msg, sizeof(*reply) )))
+ return PAM_CONV_ERR;
+
+ ReInitErrorLog();
+ Debug( "PAM_conv_null\n" );
+ for (count = 0; count < num_msg; count++) {
+ switch (msg[count]->msg_style) {
+ case PAM_TEXT_INFO:
+ Debug( " PAM_TEXT_INFO: %s\n", msg[count]->msg );
+ continue;
+ case PAM_ERROR_MSG:
+ LogError( "PAM error message: %s\n", msg[count]->msg );
+ continue;
+ default:
+ /* unknown */
+ Debug( " PAM_<%d>\n", msg[count]->msg_style );
+ free( reply );
+ return PAM_CONV_ERR;
+ }
+ reply[count].resp_retcode = PAM_SUCCESS; /* unused in linux-pam */
+ }
+ Debug( " PAM_conv_null success\n" );
+ *resp = reply;
+ return PAM_SUCCESS;
+}
+
+# ifdef PAM_FAIL_DELAY
+static void
+fail_delay( int retval ATTR_UNUSED, unsigned usec_delay ATTR_UNUSED,
+ void *appdata_ptr ATTR_UNUSED )
+{}
+# endif
+
+static int
+doPAMAuth( const char *psrv, struct pam_data *pdata )
+{
+ pam_gi_type pitem;
+ struct pam_conv pconv;
+ int pretc;
+
+ pdata->abort = FALSE;
+ pconv.conv = PAM_conv;
+ pconv.appdata_ptr = (void *)pdata;
+ Debug( " PAM service %s\n", psrv );
+ if ((pretc = pam_start( psrv, curuser, &pconv, &pamh )) != PAM_SUCCESS)
+ goto pam_bail2;
+ if ((pretc = pam_set_item( pamh, PAM_TTY, td->name )) != PAM_SUCCESS) {
+ pam_bail:
+ pam_end( pamh, pretc );
+ pamh = 0;
+ pam_bail2:
+ ReInitErrorLog();
+ LogError( "PAM error: %s\n", pam_strerror( 0, pretc ) );
+ V_RET_FAIL( 0 );
+ }
+ if ((td->displayType & d_location) == dForeign) {
+ char *cp = strchr( td->name, ':' );
+ *cp = 0;
+ pretc = pam_set_item( pamh, PAM_RHOST, td->name );
+ *cp = ':';
+ if (pretc != PAM_SUCCESS)
+ goto pam_bail;
+ }
+# ifdef __sun__ /* Only Solaris <= 9, but checking it does not seem worth it. */
+ else if (pam_set_item( pamh, PAM_RHOST, 0 ) != PAM_SUCCESS)
+ goto pam_bail;
+# endif
+# ifdef PAM_FAIL_DELAY
+ pam_set_item( pamh, PAM_FAIL_DELAY, (void *)fail_delay );
+# endif
+ ReInitErrorLog();
+
+ Debug( " pam_authenticate() ...\n" );
+ pretc = pam_authenticate( pamh,
+ td->allowNullPasswd ? 0 : PAM_DISALLOW_NULL_AUTHTOK );
+ ReInitErrorLog();
+ Debug( " pam_authenticate() returned: %s\n", pam_strerror( pamh, pretc ) );
+ if (pdata->abort) {
+ pam_end( pamh, PAM_SUCCESS );
+ pamh = 0;
+ return 0;
+ }
+ if (!curuser) {
+ Debug( " asking PAM for user ...\n" );
+ pam_get_item( pamh, PAM_USER, &pitem );
+ ReInitErrorLog();
+ StrDup( &curuser, (const char *)pitem );
+ GSendInt( V_PUT_USER );
+ GSendStr( curuser );
+ }
+ if (pretc != PAM_SUCCESS) {
+ switch (pretc) {
+ case PAM_USER_UNKNOWN:
+ case PAM_AUTH_ERR:
+ case PAM_MAXTRIES: /* should handle this better ... */
+ case PAM_AUTHINFO_UNAVAIL: /* returned for unknown users ... bogus */
+ pam_end( pamh, pretc );
+ pamh = 0;
+ V_RET_AUTH;
+ default:
+ pam_end( pamh, pretc );
+ pamh = 0;
+ V_RET_FAIL( 0 );
+ }
+ }
+ return 1;
+}
+
+#endif /* USE_PAM */
+
+static int
+#if defined(USE_PAM) || defined(_AIX)
+AccNoPass( const char *un )
+{
+ struct passwd *pw = 0;
+# ifdef HAVE_SHADOW /* (sic!) - not USESHADOW */
+ struct spwd *spw;
+# endif
+#else
+AccNoPass( const char *un, struct passwd *pw )
+{
+#endif
+ struct group *gr;
+ char **fp;
+ int hg;
+
+ if (!*un)
+ return 0;
+
+ if (cursource != PWSRC_MANUAL)
+ return 1;
+
+ for (hg = 0, fp = td->noPassUsers; *fp; fp++)
+ if (**fp == '@')
+ hg = 1;
+ else if (!strcmp( un, *fp ))
+ return 1;
+ else if (!strcmp( "*", *fp )) {
+#if defined(USE_PAM) || defined(_AIX)
+ if (!(pw = getpwnam( un )))
+ return 0;
+ if (pw->pw_passwd[0] == '!' || pw->pw_passwd[0] == '*')
+ continue;
+# ifdef HAVE_SHADOW /* (sic!) - not USESHADOW */
+ if ((spw = getspnam( un )) &&
+ (spw->sp_pwdp[0] == '!' || spw->sp_pwdp[0] == '*'))
+ continue;
+# endif
+#endif
+ if (pw->pw_uid)
+ return 1;
+ }
+
+#if defined(USE_PAM) || defined(_AIX)
+ if (hg && (pw || (pw = getpwnam( un )))) {
+#else
+ if (hg) {
+#endif
+ for (setgrent(); (gr = getgrent()); )
+ for (fp = td->noPassUsers; *fp; fp++)
+ if (**fp == '@' && !strcmp( gr->gr_name, *fp + 1 )) {
+ if (pw->pw_gid == gr->gr_gid) {
+ endgrent();
+ return 1;
+ }
+ for (; *gr->gr_mem; gr->gr_mem++)
+ if (!strcmp( un, *gr->gr_mem )) {
+ endgrent();
+ return 1;
+ }
+ }
+ endgrent();
+ }
+
+ return 0;
+}
+
+#if !defined(USE_PAM) && !defined(_AIX) && defined(HAVE_SETUSERCONTEXT)
+# define LC_RET0 do { login_close(lc); return 0; } while(0)
+#else
+# define LC_RET0 return 0
+#endif
+
+int
+Verify( GConvFunc gconv, int rootok )
+{
+#ifdef USE_PAM
+ const char *psrv;
+ struct pam_data pdata;
+ int pretc, pnopass;
+ char psrvb[64];
+#elif defined(_AIX)
+ char *msg, *curret;
+ int i, reenter;
+#else
+ struct stat st;
+ const char *nolg;
+ char *buf;
+ int fd;
+# ifdef HAVE_GETUSERSHELL
+ char *s;
+# endif
+# if defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || defined(USESHADOW)
+ int tim, expir, warntime, quietlog;
+# endif
+#endif
+
+ Debug( "Verify ...\n" );
+
+#ifdef USE_PAM
+
+ pnopass = FALSE;
+ if (!strcmp( curtype, "classic" )) {
+ if (!gconv( GCONV_USER, 0 ))
+ return 0;
+ if (AccNoPass( curuser )) {
+ gconv( GCONV_PASS_ND, 0 );
+ if (!*curpass) {
+ pnopass = TRUE;
+ sprintf( psrvb, "%.31s-np", PAMService );
+ psrv = psrvb;
+ } else
+ psrv = PAMService;
+ } else
+ psrv = PAMService;
+ pdata.usecur = TRUE;
+ } else {
+ sprintf( psrvb, "%.31s-%.31s", PAMService, curtype );
+ psrv = psrvb;
+ pdata.usecur = FALSE;
+ }
+ pdata.gconv = gconv;
+ if (!doPAMAuth( psrv, &pdata ))
+ return 0;
+
+#elif defined(_AIX)
+
+ if ((td->displayType & d_location) == dForeign) {
+ char *tmpch;
+ strncpy( hostname, td->name, sizeof(hostname) - 1 );
+ hostname[sizeof(hostname)-1] = '\0';
+ if ((tmpch = strchr( hostname, ':' )))
+ *tmpch = '\0';
+ } else
+ hostname[0] = '\0';
+
+ /* tty names should only be 15 characters long */
+# if 0
+ for (i = 0; i < 15 && td->name[i]; i++) {
+ if (td->name[i] == ':' || td->name[i] == '.')
+ tty[i] = '_';
+ else
+ tty[i] = td->name[i];
+ }
+ tty[i] = '\0';
+# else
+ memcpy( tty, "/dev/xdm/", 9 );
+ for (i = 0; i < 6 && td->name[i]; i++) {
+ if (td->name[i] == ':' || td->name[i] == '.')
+ tty[9 + i] = '_';
+ else
+ tty[9 + i] = td->name[i];
+ }
+ tty[9 + i] = '\0';
+# endif
+
+ if (!strcmp( curtype, "classic" )) {
+ if (!gconv( GCONV_USER, 0 ))
+ return 0;
+ if (AccNoPass( curuser )) {
+ gconv( GCONV_PASS_ND, 0 );
+ if (!*curpass) {
+ Debug( "accepting despite empty password\n" );
+ goto done;
+ }
+ } else
+ if (!gconv( GCONV_PASS, 0 ))
+ return 0;
+ enduserdb();
+ msg = NULL;
+ if ((i = authenticate( curuser, curpass, &reenter, &msg ))) {
+ Debug( "authenticate() failed: %s\n", msg );
+ if (msg)
+ free( msg );
+ loginfailed( curuser, hostname, tty );
+ if (i == ENOENT || i == ESAD)
+ V_RET_AUTH;
+ else
+ V_RET_FAIL( 0 );
+ }
+ if (reenter) {
+ LogError( "authenticate() requests more data: %s\n", msg );
+ free( msg );
+ V_RET_FAIL( 0 );
+ }
+ } else if (!strcmp( curtype, "generic" )) {
+ if (!gconv( GCONV_USER, 0 ))
+ return 0;
+ for (curret = 0;;) {
+ msg = NULL;
+ if ((i = authenticate( curuser, curret, &reenter, &msg ))) {
+ Debug( "authenticate() failed: %s\n", msg );
+ if (msg)
+ free( msg );
+ loginfailed( curuser, hostname, tty );
+ if (i == ENOENT || i == ESAD)
+ V_RET_AUTH;
+ else
+ V_RET_FAIL( 0 );
+ }
+ if (curret)
+ free( curret );
+ if (!reenter)
+ break;
+ if (!(curret = gconv( GCONV_HIDDEN, msg )))
+ return 0;
+ free( msg );
+ }
+ } else {
+ LogError( "Unsupported authentication type %\"s requested\n", curtype );
+ V_RET_FAIL( 0 );
+ }
+ if (msg) {
+ PrepErrorGreet();
+ GSendInt( V_MSG_INFO );
+ GSendStr( msg );
+ free( msg );
+ }
+
+ done:
+
+#else
+
+ if (strcmp( curtype, "classic" )) {
+ LogError( "Unsupported authentication type %\"s requested\n", curtype );
+ V_RET_FAIL( 0 );
+ }
+
+ if (!gconv( GCONV_USER, 0 ))
+ return 0;
+
+ if (!(p = getpwnam( curuser ))) {
+ Debug( "getpwnam() failed.\n" );
+ gconv( GCONV_PASS, 0 );
+ V_RET_AUTH;
+ }
+ if (p->pw_passwd[0] == '!' || p->pw_passwd[0] == '*') {
+ Debug( "account is locked\n" );
+ gconv( GCONV_PASS, 0 );
+ V_RET_AUTH;
+ }
+
+# ifdef USESHADOW
+ if ((sp = getspnam( curuser ))) {
+ p->pw_passwd = sp->sp_pwdp;
+ if (p->pw_passwd[0] == '!' || p->pw_passwd[0] == '*') {
+ Debug( "account is locked\n" );
+ gconv( GCONV_PASS, 0 );
+ V_RET_AUTH;
+ }
+ } else
+ Debug( "getspnam() failed: %m. Are you root?\n" );
+# endif
+
+ if (!*p->pw_passwd) {
+ if (!td->allowNullPasswd) {
+ Debug( "denying user with empty password\n" );
+ gconv( GCONV_PASS, 0 );
+ V_RET_AUTH;
+ }
+ goto nplogin;
+ }
+
+ if (AccNoPass( curuser, p )) {
+ nplogin:
+ gconv( GCONV_PASS_ND, 0 );
+ if (!*curpass) {
+ Debug( "accepting password-less login\n" );
+ goto done;
+ }
+ } else
+ if (!gconv( GCONV_PASS, 0 ))
+ return 0;
+
+# ifdef KERBEROS
+ if (p->pw_uid) {
+ int ret;
+ char realm[REALM_SZ];
+
+ if (krb_get_lrealm( realm, 1 )) {
+ LogError( "Can't get KerberosIV realm.\n" );
+ V_RET_FAIL( 0 );
+ }
+
+ sprintf( krbtkfile, "%s.%.*s", TKT_ROOT, MAXPATHLEN - strlen( TKT_ROOT ) - 2, td->name );
+ krb_set_tkt_string( krbtkfile );
+ unlink( krbtkfile );
+
+ ret = krb_verify_user( curuser, "", realm, curpass, 1, "rcmd" );
+ if (ret == KSUCCESS) {
+ chown( krbtkfile, p->pw_uid, p->pw_gid );
+ Debug( "KerberosIV verify succeeded\n" );
+ goto done;
+ } else if (ret != KDC_PR_UNKNOWN && ret != SKDC_CANT) {
+ LogError( "KerberosIV verification failure %\"s for %s\n",
+ krb_get_err_text( ret ), curuser );
+ krbtkfile[0] = '\0';
+ V_RET_FAIL( 0 );
+ }
+ Debug( "KerberosIV verify failed: %s\n", krb_get_err_text( ret ) );
+ }
+ krbtkfile[0] = '\0';
+# endif /* KERBEROS */
+
+# if defined(ultrix) || defined(__ultrix__)
+ if (authenticate_user( p, curpass, NULL ) < 0)
+# elif defined(HAVE_CRYPT)
+ if (strcmp( crypt( curpass, p->pw_passwd ), p->pw_passwd ))
+# else
+ if (strcmp( curpass, p->pw_passwd ))
+# endif
+ {
+ Debug( "password verify failed\n" );
+ V_RET_AUTH;
+ }
+
+ done:
+
+#endif /* !defined(USE_PAM) && !defined(_AIX) */
+
+ Debug( "restrict %s ...\n", curuser );
+
+#if defined(USE_PAM) || defined(_AIX)
+ if (!(p = getpwnam( curuser ))) {
+ LogError( "getpwnam(%s) failed.\n", curuser );
+ V_RET_FAIL( 0 );
+ }
+#endif
+ if (!p->pw_uid) {
+ if (!rootok && !td->allowRootLogin)
+ V_RET_FAIL( "Root logins are not allowed" );
+ return 1; /* don't deny root to log in */
+ }
+
+#ifdef USE_PAM
+
+ Debug( " pam_acct_mgmt() ...\n" );
+ pretc = pam_acct_mgmt( pamh, 0 );
+ ReInitErrorLog();
+ Debug( " pam_acct_mgmt() returned: %s\n", pam_strerror( pamh, pretc ) );
+ if (pretc == PAM_NEW_AUTHTOK_REQD) {
+ pdata.usecur = FALSE;
+ pdata.gconv = conv_interact;
+ /* pam will have output a message already, so no PrepErrorGreet () */
+ if (gconv != conv_interact || pnopass) {
+ pam_end( pamh, PAM_SUCCESS );
+ pamh = 0;
+ GSendInt( V_CHTOK_AUTH );
+ /* this cannot auth the wrong user, as only classic auths get here */
+ while (!doPAMAuth( PAMService, &pdata ))
+ if (pdata.abort)
+ return 0;
+ GSendInt( V_PRE_OK );
+ } else
+ GSendInt( V_CHTOK );
+ for (;;) {
+ Debug( " pam_chauthtok() ...\n" );
+ pretc = pam_chauthtok( pamh, PAM_CHANGE_EXPIRED_AUTHTOK );
+ ReInitErrorLog();
+ Debug( " pam_chauthtok() returned: %s\n", pam_strerror( pamh, pretc ) );
+ if (pdata.abort) {
+ pam_end( pamh, PAM_SUCCESS );
+ pamh = 0;
+ return 0;
+ }
+ if (pretc == PAM_SUCCESS)
+ break;
+ /* effectively there is only PAM_AUTHTOK_ERR */
+ GSendInt( V_FAIL );
+ }
+ if (curpass)
+ free( curpass );
+ curpass = newpass;
+ newpass = 0;
+ } else if (pretc != PAM_SUCCESS) {
+ pam_end( pamh, pretc );
+ pamh = 0;
+ V_RET_AUTH;
+ }
+
+#elif defined(_AIX) /* USE_PAM */
+
+ msg = NULL;
+ if (loginrestrictions( curuser,
+ ((td->displayType & d_location) == dForeign) ? S_RLOGIN : S_LOGIN,
+ tty, &msg ) == -1)
+ {
+ Debug( "loginrestrictions() - %s\n", msg ? msg : "error" );
+ loginfailed( curuser, hostname, tty );
+ PrepErrorGreet();
+ if (msg) {
+ GSendInt( V_MSG_ERR );
+ GSendStr( msg );
+ }
+ GSendInt( V_AUTH );
+ return 0;
+ }
+ if (msg)
+ free( (void *)msg );
+
+#endif /* USE_PAM || _AIX */
+
+#ifndef _AIX
+
+# ifdef HAVE_SETUSERCONTEXT
+# ifdef HAVE_LOGIN_GETCLASS
+ lc = login_getclass( p->pw_class );
+# else
+ lc = login_getpwclass( p );
+# endif
+ if (!lc)
+ V_RET_FAIL( 0 );
+
+ p->pw_shell = login_getcapstr( lc, "shell", p->pw_shell, p->pw_shell );
+# endif
+
+# ifndef USE_PAM
+
+/* restrict_expired */
+# if defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || defined(USESHADOW)
+
+# if !defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || (!defined(HAVE_SETUSERCONTEXT) && defined(USESHADOW))
+ if (sp)
+# endif
+ {
+
+# define DEFAULT_WARN (2L * 7L) /* Two weeks */
+
+ tim = time( NULL ) / 86400L;
+
+# ifdef HAVE_SETUSERCONTEXT
+ quietlog = login_getcapbool( lc, "hushlogin", 0 );
+ warntime = login_getcaptime( lc, "warnexpire",
+ DEFAULT_WARN * 86400L,
+ DEFAULT_WARN * 86400L ) / 86400L;
+# else
+ quietlog = 0;
+# ifdef USESHADOW
+ warntime = sp->sp_warn != -1 ? sp->sp_warn : DEFAULT_WARN;
+# else
+ warntime = DEFAULT_WARN;
+# endif
+# endif
+
+# ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE
+ if (p->pw_expire) {
+ expir = p->pw_expire / 86400L;
+# else
+ if (sp->sp_expire != -1) {
+ expir = sp->sp_expire;
+# endif
+ if (tim > expir) {
+ PrepErrorGreet();
+ GSendInt( V_MSG_ERR );
+ GSendStr( "Your account has expired;"
+ " please contact your system administrator" );
+ GSendInt( V_FAIL );
+ LC_RET0;
+ } else if (tim > (expir - warntime) && !quietlog) {
+ ASPrintf( &buf,
+ "Warning: your account will expire in %d day(s)",
+ expir - tim );
+ if (buf) {
+ PrepErrorGreet();
+ GSendInt( V_MSG_INFO );
+ GSendStr( buf );
+ free( buf );
+ }
+ }
+ }
+
+# ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE
+ if (p->pw_change) {
+ expir = p->pw_change / 86400L;
+# else
+ if (!sp->sp_lstchg) {
+ PrepErrorGreet();
+ GSendInt( V_MSG_ERR );
+ GSendStr( "You are required to change your password immediately"
+ " (root enforced)" );
+ /* XXX todo password change */
+ GSendInt( V_FAIL );
+ LC_RET0;
+ } else if (sp->sp_max != -1) {
+ expir = sp->sp_lstchg + sp->sp_max;
+ if (sp->sp_inact != -1 && tim > expir + sp->sp_inact) {
+ PrepErrorGreet();
+ GSendInt( V_MSG_ERR );
+ GSendStr( "Your account has expired;"
+ " please contact your system administrator" );
+ GSendInt( V_FAIL );
+ LC_RET0;
+ }
+# endif
+ if (tim > expir) {
+ PrepErrorGreet();
+ GSendInt( V_MSG_ERR );
+ GSendStr( "You are required to change your password immediately"
+ " (password aged)" );
+ /* XXX todo password change */
+ GSendInt( V_FAIL );
+ LC_RET0;
+ } else if (tim > (expir - warntime) && !quietlog) {
+ ASPrintf( &buf,
+ "Warning: your password will expire in %d day(s)",
+ expir - tim );
+ if (buf) {
+ PrepErrorGreet();
+ GSendInt( V_MSG_INFO );
+ GSendStr( buf );
+ free( buf );
+ }
+ }
+ }
+
+ }
+
+# endif /* HAVE_STRUCT_PASSWD_PW_EXPIRE || USESHADOW */
+
+/* restrict_nologin */
+# ifndef _PATH_NOLOGIN
+# define _PATH_NOLOGIN "/etc/nologin"
+# endif
+
+ if ((
+# ifdef HAVE_SETUSERCONTEXT
+ /* Do we ignore a nologin file? */
+ !login_getcapbool( lc, "ignorenologin", 0 )) &&
+ (!stat( (nolg = login_getcapstr( lc, "nologin", "", NULL )), &st ) ||
+# endif
+ !stat( (nolg = _PATH_NOLOGIN), &st )))
+ {
+ PrepErrorGreet();
+ GSendInt( V_MSG_ERR );
+ if (st.st_size && (fd = open( nolg, O_RDONLY )) >= 0) {
+ if ((buf = Malloc( st.st_size + 1 ))) {
+ if (read( fd, buf, st.st_size ) == st.st_size) {
+ buf[st.st_size] = 0;
+ GSendStr( buf );
+ free( buf );
+ close( fd );
+ GSendInt( V_FAIL );
+ LC_RET0;
+ }
+ free( buf );
+ }
+ close( fd );
+ }
+ GSendStr( "Logins are not allowed at the moment.\nTry again later" );
+ GSendInt( V_FAIL );
+ LC_RET0;
+ }
+
+/* restrict_time */
+# if defined(HAVE_SETUSERCONTEXT) && defined(HAVE_AUTH_TIMEOK)
+ if (!auth_timeok( lc, time( NULL ) )) {
+ PrepErrorGreet();
+ GSendInt( V_MSG_ERR );
+ GSendStr( "You are not allowed to login at the moment" );
+ GSendInt( V_FAIL );
+ LC_RET0;
+ }
+# endif
+
+# ifdef HAVE_GETUSERSHELL
+ for (;;) {
+ if (!(s = getusershell())) {
+ Debug( "shell not in /etc/shells\n" );
+ endusershell();
+ V_RET_FAIL( "Your login shell is not listed in /etc/shells" );
+ }
+ if (!strcmp( s, p->pw_shell )) {
+ endusershell();
+ break;
+ }
+ }
+# endif
+
+# endif /* !USE_PAM */
+
+/* restrict_nohome */
+# ifdef HAVE_SETUSERCONTEXT
+ if (login_getcapbool( lc, "requirehome", 0 )) {
+ struct stat st;
+ if (!*p->pw_dir || stat( p->pw_dir, &st ) || st.st_uid != p->pw_uid) {
+ PrepErrorGreet();
+ GSendInt( V_MSG_ERR );
+ GSendStr( "Home folder not available" );
+ GSendInt( V_FAIL );
+ LC_RET0;
+ }
+ }
+# endif
+
+#endif /* !_AIX */
+
+ return 1;
+
+}
+
+
+static const char *envvars[] = {
+ "TZ", /* SYSV and SVR4, but never hurts */
+#ifdef _AIX
+ "AUTHSTATE", /* for kerberos */
+#endif
+ NULL
+};
+
+
+#if defined(USE_PAM) && defined(HAVE_INITGROUPS)
+static int num_saved_gids;
+static gid_t *saved_gids;
+
+static int
+saveGids( void )
+{
+ num_saved_gids = getgroups( 0, 0 );
+ if (!(saved_gids = Malloc( sizeof(gid_t) * num_saved_gids )))
+ return 0;
+ if (getgroups( num_saved_gids, saved_gids ) < 0) {
+ LogError( "saving groups failed: %m\n" );
+ return 0;
+ }
+ return 1;
+}
+
+static int
+restoreGids( void )
+{
+ if (setgroups( num_saved_gids, saved_gids ) < 0) {
+ LogError( "restoring groups failed: %m\n" );
+ return 0;
+ }
+ if (setgid( p->pw_gid ) < 0) {
+ LogError( "restoring gid failed: %m\n" );
+ return 0;
+ }
+ return 1;
+}
+#endif /* USE_PAM && HAVE_INITGROUPS */
+
+static int
+resetGids( void )
+{
+#ifdef HAVE_INITGROUPS
+ if (setgroups( 0, &p->pw_gid /* anything */ ) < 0) {
+ LogError( "restoring groups failed: %m\n" );
+ return 0;
+ }
+#endif
+ if (setgid( 0 ) < 0) {
+ LogError( "restoring gid failed: %m\n" );
+ return 0;
+ }
+ return 1;
+}
+
+static int
+SetGid( const char *name, int gid )
+{
+ if (setgid( gid ) < 0) {
+ LogError( "setgid(%d) (user %s) failed: %m\n", gid, name );
+ return 0;
+ }
+#ifdef HAVE_INITGROUPS
+ if (initgroups( name, gid ) < 0) {
+ LogError( "initgroups for %s failed: %m\n", name );
+ setgid( 0 );
+ return 0;
+ }
+#endif /* QNX4 doesn't support multi-groups, no initgroups() */
+ return 1;
+}
+
+static int
+SetUid( const char *name, int uid )
+{
+ if (setuid( uid ) < 0) {
+ LogError( "setuid(%d) (user %s) failed: %m\n", uid, name );
+ return 0;
+ }
+ return 1;
+}
+
+static int
+SetUser( const char *name, int uid, int gid )
+{
+ if (SetGid( name, gid )) {
+ if (SetUid( name, uid ))
+ return 1;
+ resetGids();
+ }
+ return 0;
+}
+
+#if defined(SECURE_RPC) || defined(K5AUTH)
+static void
+NukeAuth( int len, const char *name )
+{
+ int i;
+
+ for (i = 0; i < td->authNum; i++)
+ if (td->authorizations[i]->name_length == len &&
+ !memcmp( td->authorizations[i]->name, name, len ))
+ {
+ memcpy( &td->authorizations[i], &td->authorizations[i+1],
+ sizeof(td->authorizations[i]) * (--td->authNum - i) );
+ break;
+ }
+}
+#endif
+
+static void
+mergeSessionArgs( int cansave )
+{
+ char *mfname;
+ const char *fname;
+ int i, needsave;
+
+ mfname = 0;
+ fname = ".dmrc";
+ if ((!curdmrc || newdmrc) && *dmrcDir)
+ if (StrApp( &mfname, dmrcDir, "/", curuser, fname, (char *)0 ))
+ fname = mfname;
+ needsave = 0;
+ if (!curdmrc) {
+ curdmrc = iniLoad( fname );
+ if (!curdmrc) {
+ StrDup( &curdmrc, "[Desktop]\nSession=default\n" );
+ needsave = 1;
+ }
+ }
+ if (newdmrc) {
+ curdmrc = iniMerge( curdmrc, newdmrc );
+ needsave = 1;
+ }
+ if (needsave && cansave)
+ if (!iniSave( curdmrc, fname ) && errno == ENOENT && mfname) {
+ for (i = 0; mfname[i]; i++)
+ if (mfname[i] == '/') {
+ mfname[i] = 0;
+ mkdir( mfname, 0755 );
+ mfname[i] = '/';
+ }
+ iniSave( curdmrc, mfname );
+ }
+ if (mfname)
+ free( mfname );
+}
+
+static int removeAuth;
+#ifdef USE_PAM
+static int removeSession;
+static int removeCreds;
+#endif
+
+int
+StartClient()
+{
+ const char *home, *sessargs, *desksess;
+ char **env, *xma;
+ char **argv, *fname, *str;
+#ifdef USE_PAM
+ char **pam_env;
+# ifdef _AIX
+ char **saved_env;
+# endif
+ struct pam_conv pconv;
+ int pretc;
+#else
+# ifdef _AIX
+ char *msg;
+ char **theenv;
+ extern char **newenv; /* from libs.a, this is set up by setpenv */
+# endif
+#endif
+#ifdef HAVE_SETUSERCONTEXT
+ extern char **environ;
+#endif
+ char *failsafeArgv[2], *lname;
+ int i, pid, lfd;
+
+ if (StrCmp( dmrcuser, curuser )) {
+ if (curdmrc) { free( curdmrc ); curdmrc = 0; }
+ if (dmrcuser) { free( dmrcuser ); dmrcuser = 0; }
+ }
+
+#if defined(USE_PAM) || defined(_AIX)
+ if (!(p = getpwnam( curuser ))) {
+ LogError( "getpwnam(%s) failed.\n", curuser );
+ return 0;
+ }
+#endif
+
+#ifndef USE_PAM
+# ifdef _AIX
+ msg = NULL;
+ loginsuccess( curuser, hostname, tty, &msg );
+ if (msg) {
+ Debug( "loginsuccess() - %s\n", msg );
+ free( (void *)msg );
+ }
+# else /* _AIX */
+# if defined(KERBEROS) && !defined(NO_AFS)
+ if (krbtkfile[0] != '\0') {
+ if (k_hasafs()) {
+ if (k_setpag() == -1)
+ LogError( "setpag() for %s failed\n", curuser );
+ if ((ret = k_afsklog( NULL, NULL )) != KSUCCESS)
+ LogError( "AFS Warning: %s\n", krb_get_err_text( ret ) );
+ }
+ }
+# endif /* KERBEROS && AFS */
+# endif /* _AIX */
+#endif /* !PAM */
+
+ curuid = p->pw_uid;
+ curgid = p->pw_gid;
+
+ env = baseEnv( curuser );
+ xma = 0;
+ if (td->ctrl.fpath && StrDup( &xma, td->ctrl.fpath )) {
+ if ((td->allowShutdown == SHUT_ALL ||
+ (td->allowShutdown == SHUT_ROOT && !curuser)) &&
+ StrApp( &xma, ",maysd", (char *)0 ))
+ {
+ if (td->allowNuke == SHUT_ALL ||
+ (td->allowNuke == SHUT_ROOT && !curuser))
+ StrApp( &xma, ",mayfn", (char *)0 );
+ StrApp( &xma, td->defSdMode == SHUT_FORCENOW ? ",fn" :
+ td->defSdMode == SHUT_TRYNOW ? ",tn" : ",sched",
+ (char *)0 );
+ }
+ if ((td->displayType & d_location) == dLocal && AnyReserveDisplays())
+ StrApp( &xma, ",rsvd", (char *)0 );
+ } else
+ StrDup( &xma, "true" );
+ StrApp( &xma, ",method=", curtype, (char *)0 );
+ if (td_setup)
+ StrApp( &xma, ",auto", (char *)0 );
+ if (xma) {
+ env = setEnv( env, "XDM_MANAGED", xma );
+ free( xma );
+ }
+ if (td->autoLock && cursource == PWSRC_AUTOLOGIN)
+ env = setEnv( env, "DESKTOP_LOCKED", "true" );
+ env = setEnv( env, "PATH", curuid ? td->userPath : td->systemPath );
+ env = setEnv( env, "SHELL", p->pw_shell );
+ env = setEnv( env, "HOME", p->pw_dir );
+#if !defined(USE_PAM) && !defined(_AIX) && defined(KERBEROS)
+ if (krbtkfile[0] != '\0')
+ env = setEnv( env, "KRBTKFILE", krbtkfile );
+#endif
+ userEnviron = inheritEnv( env, envvars );
+ env = systemEnv( p->pw_name );
+ systemEnviron = setEnv( env, "HOME", p->pw_dir );
+ Debug( "user environment:\n%[|''>'\n's"
+ "system environment:\n%[|''>'\n's"
+ "end of environments\n",
+ userEnviron,
+ systemEnviron );
+
+ /*
+ * for user-based authorization schemes,
+ * add the user to the server's allowed "hosts" list.
+ */
+ for (i = 0; i < td->authNum; i++) {
+#ifdef SECURE_RPC
+ if (td->authorizations[i]->name_length == 9 &&
+ !memcmp( td->authorizations[i]->name, "SUN-DES-1", 9 ))
+ {
+ XHostAddress addr;
+ char netname[MAXNETNAMELEN+1];
+ char domainname[MAXNETNAMELEN+1];
+
+ getdomainname( domainname, sizeof(domainname) );
+ user2netname( netname, curuid, domainname );
+ addr.family = FamilyNetname;
+ addr.length = strlen( netname );
+ addr.address = netname;
+ XAddHost( dpy, &addr );
+ }
+#endif
+#ifdef K5AUTH
+ if (td->authorizations[i]->name_length == 14 &&
+ !memcmp( td->authorizations[i]->name, "MIT-KERBEROS-5", 14 ))
+ {
+ /* Update server's auth file with user-specific info.
+ * Don't need to AddHost because X server will do that
+ * automatically when it reads the cache we are about
+ * to point it at.
+ */
+ XauDisposeAuth( td->authorizations[i] );
+ td->authorizations[i] =
+ Krb5GetAuthFor( 14, "MIT-KERBEROS-5", td->name );
+ SaveServerAuthorizations( td, td->authorizations, td->authNum );
+ }
+#endif
+ }
+
+ if (*dmrcDir)
+ mergeSessionArgs( TRUE );
+
+ Debug( "now starting the session\n" );
+
+#ifdef USE_PAM
+ /* the greeter is gone by now ... */
+ pconv.conv = PAM_conv_null;
+ pconv.appdata_ptr = 0;
+ if ((pretc = pam_set_item( pamh, PAM_CONV, &pconv )) != PAM_SUCCESS) {
+ ReInitErrorLog();
+ LogError( "pam_set_item() for %s failed: %s\n",
+ curuser, pam_strerror( pamh, pretc ) );
+ return 0;
+ }
+ ReInitErrorLog();
+#endif
+
+#ifdef USE_PAM
+
+# ifdef HAVE_SETUSERCONTEXT
+ if (setusercontext( lc, p, p->pw_uid, LOGIN_SETGROUP )) {
+ LogError( "setusercontext(groups) for %s failed: %m\n",
+ curuser );
+ return 0;
+ }
+# else
+ if (!SetGid( curuser, curgid ))
+ return 0;
+# endif
+
+# ifdef _AIX
+ if (!(pam_env = initStrArr( 0 ))) {
+ resetGids();
+ return 0;
+ }
+ saved_env = environ;
+ environ = pam_env;
+# endif
+ removeCreds = 1; /* set it first - i don't trust PAM's rollback */
+ pretc = pam_setcred( pamh, 0 );
+ ReInitErrorLog();
+# ifdef _AIX
+ pam_env = environ;
+ environ = saved_env;
+# endif
+# ifdef HAVE_INITGROUPS
+ /* This seems to be a strange place for it, but do it:
+ - after the initial groups are set
+ - after pam_setcred might have set something, even in the error case
+ - before pam_setcred(DELETE_CRED) might need it
+ */
+ if (!saveGids())
+ return 0;
+# endif
+ if (pretc != PAM_SUCCESS) {
+ LogError( "pam_setcred() for %s failed: %s\n",
+ curuser, pam_strerror( pamh, pretc ) );
+ resetGids();
+ return 0;
+ }
+
+ removeSession = 1; /* set it first - same as above */
+ pretc = pam_open_session( pamh, 0 );
+ ReInitErrorLog();
+ if (pretc != PAM_SUCCESS) {
+ LogError( "pam_open_session() for %s failed: %s\n",
+ curuser, pam_strerror( pamh, pretc ) );
+ resetGids();
+ return 0;
+ }
+
+ /* we don't want sessreg and the startup/reset scripts run with user
+ credentials. unfortunately, we can reset only the gids. */
+ resetGids();
+
+# define D_LOGIN_SETGROUP LOGIN_SETGROUP
+#else /* USE_PAM */
+# define D_LOGIN_SETGROUP 0
+#endif /* USE_PAM */
+
+ removeAuth = 1;
+ chownCtrl( &td->ctrl, curuid );
+ endpwent();
+#if !defined(USE_PAM) && defined(USESHADOW) && !defined(_AIX)
+ endspent();
+#endif
+ ClearCloseOnFork( mstrtalk.pipe->wfd );
+ switch (pid = Fork()) {
+ case 0:
+
+ sessreg( td, getpid(), curuser, curuid );
+
+ if (source( systemEnviron, td->startup, td_setup )) {
+ LogError( "Cannot execute startup script %\"s\n", td->startup );
+ exit( 1 );
+ }
+
+ if (Setjmp( mstrtalk.errjmp ))
+ exit( 1 );
+ GSet( &mstrtalk );
+
+ setsid();
+ Signal( SIGINT, SIG_DFL );
+
+ /* Memory leaks are ok here as we exec() soon. */
+
+#if defined(USE_PAM) || !defined(_AIX)
+
+# ifdef USE_PAM
+ /* pass in environment variables set by libpam and modules it called */
+# ifndef _AIX
+ pam_env = pam_getenvlist( pamh );
+ ReInitErrorLog();
+# endif
+ if (pam_env)
+ for (; *pam_env; pam_env++)
+ userEnviron = putEnv( *pam_env, userEnviron );
+# endif
+
+# ifdef HAVE_SETLOGIN
+ if (setlogin( curuser ) < 0) {
+ LogError( "setlogin for %s failed: %m\n", curuser );
+ exit( 1 );
+ }
+# define D_LOGIN_SETLOGIN LOGIN_SETLOGIN
+# else
+# define D_LOGIN_SETLOGIN 0
+# endif
+
+# if defined(USE_PAM) && defined(HAVE_INITGROUPS)
+ if (!restoreGids())
+ exit( 1 );
+# endif
+
+# ifndef HAVE_SETUSERCONTEXT
+
+# ifdef USE_PAM
+ if (!SetUid( curuser, curuid ))
+ exit( 1 );
+# else
+ if (!SetUser( curuser, curuid, curgid ))
+ exit( 1 );
+# endif
+
+# else /* !HAVE_SETUSERCONTEXT */
+
+ /*
+ * Destroy environment.
+ * We need to do this before setusercontext() because that may
+ * set or reset some environment variables.
+ */
+ if (!(environ = initStrArr( 0 )))
+ exit( 1 );
+
+ /*
+ * Set the user's credentials: uid, gid, groups,
+ * environment variables, resource limits, and umask.
+ */
+ if (setusercontext( lc, p, p->pw_uid,
+ LOGIN_SETALL & ~(D_LOGIN_SETGROUP|D_LOGIN_SETLOGIN) ) < 0)
+ {
+ LogError( "setusercontext for %s failed: %m\n", curuser );
+ exit( 1 );
+ }
+
+ for (i = 0; environ[i]; i++)
+ userEnviron = putEnv( environ[i], userEnviron );
+
+# endif /* !HAVE_SETUSERCONTEXT */
+
+#else /* PAM || !_AIX */
+ /*
+ * Set the user's credentials: uid, gid, groups,
+ * audit classes, user limits, and umask.
+ */
+ if (setpcred( curuser, NULL ) == -1) {
+ LogError( "setpcred for %s failed: %m\n", curuser );
+ exit( 1 );
+ }
+
+ /*
+ * Set the users process environment. Store protected variables and
+ * obtain updated user environment list. This call will initialize
+ * global 'newenv'.
+ */
+ if (setpenv( curuser, PENV_INIT | PENV_ARGV | PENV_NOEXEC,
+ userEnviron, NULL ) != 0)
+ {
+ LogError( "Can't set %s's process environment\n", curuser );
+ exit( 1 );
+ }
+ userEnviron = newenv;
+
+#endif /* _AIX */
+
+ /*
+ * for user-based authorization schemes,
+ * use the password to get the user's credentials.
+ */
+#ifdef SECURE_RPC
+ /* do like "keylogin" program */
+ if (!curpass[0])
+ LogInfo( "No password for NIS provided.\n" );
+ else {
+ char netname[MAXNETNAMELEN+1], secretkey[HEXKEYBYTES+1];
+ int nameret, keyret;
+ int len;
+ int key_set_ok = 0;
+ struct key_netstarg netst;
+
+ nameret = getnetname( netname );
+ Debug( "user netname: %s\n", netname );
+ len = strlen( curpass );
+ if (len > 8)
+ bzero( curpass + 8, len - 8 );
+ keyret = getsecretkey( netname, secretkey, curpass );
+ Debug( "getsecretkey returns %d, key length %d\n",
+ keyret, strlen( secretkey ) );
+ netst.st_netname = netname;
+ memcpy( netst.st_priv_key, secretkey, HEXKEYBYTES );
+ memset( netst.st_pub_key, 0, HEXKEYBYTES );
+ if (key_setnet( &netst ) < 0)
+ Debug( "Could not set secret key.\n" );
+ /* is there a key, and do we have the right password? */
+ if (keyret == 1) {
+ if (*secretkey) {
+ keyret = key_setsecret( secretkey );
+ Debug( "key_setsecret returns %d\n", keyret );
+ if (keyret == -1)
+ LogError( "Failed to set NIS secret key\n" );
+ else
+ key_set_ok = 1;
+ } else {
+ /* found a key, but couldn't interpret it */
+ LogError( "Password incorrect for NIS principal %s\n",
+ nameret ? netname : curuser );
+ }
+ }
+ if (!key_set_ok)
+ NukeAuth( 9, "SUN-DES-1" );
+ bzero( secretkey, strlen( secretkey ) );
+ }
+#endif
+#ifdef K5AUTH
+ /* do like "kinit" program */
+ if (!curpass[0])
+ LogInfo( "No password for Kerberos5 provided.\n" );
+ else
+ if ((str = Krb5Init( curuser, curpass, td->name )))
+ userEnviron = setEnv( userEnviron, "KRB5CCNAME", str );
+ else
+ NukeAuth( 14, "MIT-KERBEROS-5" );
+#endif /* K5AUTH */
+ if (td->autoReLogin) {
+ GSendInt( D_ReLogin );
+ GSendStr( curuser );
+ GSendStr( curpass );
+ GSendStr( newdmrc );
+ }
+ if (curpass)
+ bzero( curpass, strlen( curpass ) );
+ SetUserAuthorization( td );
+ home = getEnv( userEnviron, "HOME" );
+ if (home) {
+ if (chdir( home ) < 0) {
+ LogError( "Cannot chdir to %s's home %s: %m, using /\n",
+ curuser, home );
+ home = 0;
+ userEnviron = setEnv( userEnviron, "HOME", "/" );
+ goto cdroot;
+ }
+ ASPrintf( &lname, td->clientLogFile, td->name );
+ if ((lfd = creat( lname, 0600 )) < 0) {
+ LogWarn( "Cannot create session log file %s: %m\n", lname );
+ free( lname );
+ goto tmperr;
+ }
+ } else {
+ cdroot:
+ chdir( "/" );
+ tmperr:
+ ASPrintf( &lname, "/tmp/xerr-%s-%s", curuser, td->name );
+ unlink( lname );
+ if ((lfd = open( lname, O_WRONLY|O_CREAT|O_EXCL, 0600 )) < 0) {
+ LogError( "Cannot create fallback session log file %s: %m\n",
+ lname );
+ goto logerr;
+ }
+ }
+ dup2( lfd, 1 );
+ dup2( lfd, 2 );
+ close( lfd );
+ logerr:
+ free( lname );
+ if (!*dmrcDir)
+ mergeSessionArgs( home != 0 );
+ if (!(desksess = iniEntry( curdmrc, "Desktop", "Session", 0 )))
+ desksess = "failsafe"; /* only due to OOM */
+ GSendInt( D_User );
+ GSendInt( curuid );
+ GSendStr( curuser );
+ GSendStr( desksess );
+ close( mstrtalk.pipe->wfd );
+ userEnviron = setEnv( userEnviron, "DESKTOP_SESSION", desksess );
+ for (i = 0; td->sessionsDirs[i]; i++) {
+ fname = 0;
+ if (StrApp( &fname, td->sessionsDirs[i], "/", desksess, ".desktop", (char *)0 )) {
+ if ((str = iniLoad( fname ))) {
+ if (!StrCmp( iniEntry( str, "Desktop Entry", "Hidden", 0 ), "true" ) ||
+ !(sessargs = iniEntry( str, "Desktop Entry", "Exec", 0 )))
+ sessargs = "";
+ free( str );
+ free( fname );
+ goto gotit;
+ }
+ free( fname );
+ }
+ }
+ if (!strcmp( desksess, "failsafe" ) ||
+ !strcmp( desksess, "default" ) ||
+ !strcmp( desksess, "custom" ))
+ sessargs = desksess;
+ else
+ sessargs = "";
+ gotit:
+ if (!(argv = parseArgs( (char **)0, td->session )) ||
+ !(argv = addStrArr( argv, sessargs, -1 )))
+ exit( 1 );
+ if (argv[0] && *argv[0]) {
+ Debug( "executing session %\"[s\n", argv );
+ execute( argv, userEnviron );
+ LogError( "Session %\"s execution failed: %m\n", argv[0] );
+ } else
+ LogError( "Session has no command/arguments\n" );
+ failsafeArgv[0] = td->failsafeClient;
+ failsafeArgv[1] = 0;
+ execute( failsafeArgv, userEnviron );
+ LogError( "Failsafe client %\"s execution failed: %m\n",
+ failsafeArgv[0] );
+ exit( 1 );
+ case -1:
+ RegisterCloseOnFork( mstrtalk.pipe->wfd );
+ LogError( "Forking session on %s failed: %m\n", td->name );
+ return 0;
+ default:
+ RegisterCloseOnFork( mstrtalk.pipe->wfd );
+ Debug( "StartSession, fork succeeded %d\n", pid );
+ return pid;
+ }
+}
+
+void
+SessionExit( int status )
+{
+ int pid;
+#ifdef USE_PAM
+ int pretc;
+#endif
+
+ Signal( SIGTERM, SIG_IGN );
+
+ if (removeAuth) {
+ if (source( systemEnviron, td->reset, td_setup ))
+ LogError( "Cannot execute reset script %\"s\n", td->reset );
+ sessreg( td, 0, 0, 0 );
+
+ switch ((pid = Fork())) {
+ case 0:
+#if defined(USE_PAM) && defined(HAVE_INITGROUPS)
+ if (restoreGids() && SetUid( curuser, curuid ))
+#else
+ if (SetUser( curuser, curuid, curgid ))
+#endif
+
+ {
+ RemoveUserAuthorization( td );
+#ifdef K5AUTH
+ Krb5Destroy( td->name );
+#endif /* K5AUTH */
+#if !defined(USE_PAM) && !defined(_AIX)
+# ifdef KERBEROS
+ if (krbtkfile[0]) {
+ (void)dest_tkt();
+# ifndef NO_AFS
+ if (k_hasafs())
+ (void)k_unlog();
+# endif
+ }
+# endif
+#endif /* !USE_PAM && !_AIX*/
+ }
+ exit( 0 );
+ case -1:
+ LogError( "Cannot clean up session: fork() failed: %m" );
+ break;
+ default:
+ Wait4( pid );
+ break;
+ }
+ }
+
+#ifdef USE_PAM
+ if (removeCreds) {
+# ifdef HAVE_INITGROUPS
+ restoreGids();
+# endif
+ if (removeSession)
+ if ((pretc = pam_close_session( pamh, 0 )) != PAM_SUCCESS)
+ LogError( "pam_close_session() failed: %s\n",
+ pam_strerror( pamh, pretc ) );
+ if ((pretc = pam_setcred( pamh, PAM_DELETE_CRED )) != PAM_SUCCESS)
+ LogError( "pam_setcred(DELETE_CRED) failed: %s\n",
+ pam_strerror( pamh, pretc ) );
+ resetGids();
+ }
+ if (pamh) {
+ pam_end( pamh, PAM_SUCCESS );
+ ReInitErrorLog();
+ }
+#endif
+
+ /* make sure the server gets reset after the session is over */
+ if (td->serverPid >= 2) {
+ if (!td->terminateServer && td->resetSignal)
+ TerminateProcess( td->serverPid, td->resetSignal );
+ } else
+ ResetServer( td );
+ Debug( "display %s exiting with status %d\n", td->name, status );
+ exit( status );
+}
+
+int
+ReadDmrc()
+{
+ char *data, *fname = 0;
+ int len, pid, pfd[2], err;
+
+ if (!dmrcuser || !dmrcuser[0] || !(p = getpwnam( dmrcuser )))
+ return GE_NoUser;
+
+ if (*dmrcDir) {
+ if (!StrApp( &fname, dmrcDir, "/", dmrcuser, ".dmrc", (char *)0 ))
+ return GE_Error;
+ if (!(curdmrc = iniLoad( fname ))) {
+ free( fname );
+ return GE_Ok;
+ }
+ free( fname );
+ return GE_NoFile;
+ }
+
+ if (!StrApp( &fname, p->pw_dir, "/.dmrc", (char *)0 ))
+ return GE_Error;
+ if (pipe( pfd ))
+ return GE_Error;
+ if ((pid = Fork()) < 0) {
+ close( pfd[0] );
+ close( pfd[1] );
+ return GE_Error;
+ }
+ if (!pid) {
+ if (!SetUser( p->pw_name, p->pw_uid, p->pw_gid ))
+ exit( 0 );
+ if (!(data = iniLoad( fname ))) {
+ static const int m1 = -1;
+ write( pfd[1], &m1, sizeof(int) );
+ exit( 0 );
+ }
+ len = strlen( data );
+ write( pfd[1], &len, sizeof(int) );
+ write( pfd[1], data, len + 1 );
+ exit( 0 );
+ }
+ close( pfd[1] );
+ free( fname );
+ err = GE_Error;
+ if (Reader( pfd[0], &len, sizeof(int) ) == sizeof(int)) {
+ if (len == -1)
+ err = GE_Denied;
+ else if ((curdmrc = Malloc( len + 1 ))) {
+ if (Reader( pfd[0], curdmrc, len + 1 ) == len + 1)
+ err = GE_Ok;
+ else {
+ free( curdmrc );
+ curdmrc = 0;
+ }
+ }
+ }
+ close( pfd[0] );
+ (void)Wait4( pid );
+ return err;
+}
diff --git a/kdm/backend/ctrl.c b/kdm/backend/ctrl.c
new file mode 100644
index 000000000..d813af0f2
--- /dev/null
+++ b/kdm/backend/ctrl.c
@@ -0,0 +1,955 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2001-2005 Oswald Buddenhagen <ossi@kde.org>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the copyright holder.
+
+*/
+
+/*
+ * xdm - display manager daemon
+ * Author: Keith Packard, MIT X Consortium
+ *
+ * display manager
+ */
+
+#include "dm.h"
+#include "dm_socket.h"
+#include "dm_error.h"
+
+#include <string.h>
+#include <signal.h>
+#include <pwd.h>
+
+static void
+acceptSock( CtrlRec *cr )
+{
+ struct cmdsock *cs;
+ int fd;
+
+ if ((fd = accept( cr->fd, 0, 0 )) < 0) {
+ bust:
+ LogError( "Error accepting command connection\n" );
+ return;
+ }
+ if (!(cs = Malloc( sizeof(*cs) ))) {
+ close( fd );
+ goto bust;
+ }
+ cs->sock.fd = fd;
+ cs->sock.buffer = 0;
+ cs->sock.buflen = 0;
+ cs->next = cr->css;
+ cr->css = cs;
+ fcntl( fd, F_SETFL, fcntl( fd, F_GETFL ) | O_NONBLOCK );
+ RegisterCloseOnFork( fd );
+ RegisterInput( fd );
+}
+
+static void
+nukeSock( struct cmdsock *cs )
+{
+ UnregisterInput( cs->sock.fd );
+ CloseNClearCloseOnFork( cs->sock.fd );
+ if (cs->sock.buffer)
+ free( cs->sock.buffer );
+ free( cs );
+}
+
+
+static CtrlRec ctrl = { 0, 0, -1, 0, 0, { -1, 0, 0 } };
+
+void
+openCtrl( struct display *d )
+{
+ CtrlRec *cr;
+ const char *dname;
+ char *sockdir;
+ struct sockaddr_un sa;
+
+ if (!*fifoDir)
+ return;
+ if (d) {
+ cr = &d->ctrl, dname = d->name;
+ if (!memcmp( dname, "localhost:", 10 ))
+ dname += 9;
+ } else
+ cr = &ctrl, dname = 0;
+ if (cr->fifo.fd < 0) {
+ if (mkdir( fifoDir, 0755 )) {
+ if (errno != EEXIST) {
+ LogError( "mkdir %\"s failed; no control FiFos will be available\n",
+ fifoDir );
+ return;
+ }
+ } else
+ chmod( fifoDir, 0755 ); /* override umask */
+ StrApp( &cr->fpath, fifoDir, dname ? "/xdmctl-" : "/xdmctl",
+ dname, (char *)0 );
+ if (cr->fpath) {
+ unlink( cr->fpath );
+ if (mkfifo( cr->fpath, 0 ) < 0)
+ LogError( "Cannot create control FiFo %\"s\n", cr->fpath );
+ else {
+ cr->gid = fifoGroup;
+ if (!d)
+ chown( cr->fpath, -1, fifoGroup );
+ chmod( cr->fpath, 0620 );
+ if ((cr->fifo.fd = open( cr->fpath, O_RDWR | O_NONBLOCK )) >= 0) {
+ RegisterCloseOnFork( cr->fifo.fd );
+ RegisterInput( cr->fifo.fd );
+ goto fifok;
+ }
+ unlink( cr->fpath );
+ LogError( "Cannot open control FiFo %\"s\n", cr->fpath );
+ }
+ free( cr->fpath );
+ cr->fpath = 0;
+ }
+ }
+ fifok:
+ if (cr->fd < 0) {
+ /* fifoDir is created above already */
+ sockdir = 0;
+ StrApp( &sockdir, fifoDir, dname ? "/dmctl-" : "/dmctl",
+ dname, (char *)0 );
+ if (sockdir) {
+ StrApp( &cr->path, sockdir, "/socket", (char *)0 );
+ if (cr->path) {
+ if (strlen( cr->path ) >= sizeof(sa.sun_path))
+ LogError( "path %\"s too long; no control sockets will be available\n",
+ cr->path );
+ else if (mkdir( sockdir, 0755 ) && errno != EEXIST)
+ LogError( "mkdir %\"s failed; no control sockets will be available\n",
+ sockdir );
+ else {
+ if (!d)
+ chown( sockdir, -1, fifoGroup );
+ chmod( sockdir, 0750 );
+ if ((cr->fd = socket( PF_UNIX, SOCK_STREAM, 0 )) < 0)
+ LogError( "Cannot create control socket\n" );
+ else {
+ unlink( cr->path );
+ sa.sun_family = AF_UNIX;
+ strcpy( sa.sun_path, cr->path );
+ if (!bind( cr->fd, (struct sockaddr *)&sa, sizeof(sa) )) {
+ if (!listen( cr->fd, 5 )) {
+ chmod( cr->path, 0666 );
+ RegisterCloseOnFork( cr->fd );
+ RegisterInput( cr->fd );
+ free( sockdir );
+ return;
+ }
+ unlink( cr->path );
+ LogError( "Cannot listen on control socket %\"s\n",
+ cr->path );
+ } else
+ LogError( "Cannot bind control socket %\"s\n",
+ cr->path );
+ close( cr->fd );
+ cr->fd = -1;
+ }
+ }
+ free( cr->path );
+ cr->path = 0;
+ }
+ free( sockdir );
+ }
+ }
+}
+
+void
+closeCtrl( struct display *d )
+{
+ CtrlRec *cr = d ? &d->ctrl : &ctrl;
+
+ if (cr->fd >= 0) {
+ UnregisterInput( cr->fd );
+ CloseNClearCloseOnFork( cr->fd );
+ cr->fd = -1;
+ unlink( cr->path );
+ *strrchr( cr->path, '/' ) = 0;
+ rmdir( cr->path );
+ free( cr->path );
+ cr->path = 0;
+ while (cr->css) {
+ struct cmdsock *cs = cr->css;
+ cr->css = cs->next;
+ nukeSock( cs );
+ }
+ }
+ if (cr->fifo.fd >= 0) {
+ UnregisterInput( cr->fifo.fd );
+ CloseNClearCloseOnFork( cr->fifo.fd );
+ cr->fifo.fd = -1;
+ unlink( cr->fpath );
+ free( cr->fpath );
+ cr->fpath = 0;
+ if (cr->fifo.buffer)
+ free( cr->fifo.buffer );
+ cr->fifo.buffer = 0;
+ cr->fifo.buflen = 0;
+ }
+}
+
+void
+chownCtrl( CtrlRec *cr, int uid )
+{
+ if (cr->fpath)
+ chown( cr->fpath, uid, -1 );
+ if (cr->path) {
+ char *ptr = strrchr( cr->path, '/' );
+ *ptr = 0;
+ chown( cr->path, uid, -1 );
+ *ptr = '/';
+ }
+}
+
+void
+updateCtrl( void )
+{
+ unsigned ffl, slc;
+
+ ffl = 0;
+ if (ctrl.path)
+ for (ffl = strlen( ctrl.path ), slc = 2; ;)
+ if (ctrl.path[--ffl] == '/')
+ if (!--slc)
+ break;
+ if (ffl != strlen( fifoDir ) || memcmp( fifoDir, ctrl.path, ffl ) ||
+ ctrl.gid != fifoGroup)
+ {
+ closeCtrl( 0 );
+ openCtrl( 0 );
+ }
+}
+
+
+static void
+fLog( struct display *d, int fd, const char *sts, const char *msg, ... )
+{
+ char *fmsg, *otxt;
+ const char *what;
+ int olen;
+ va_list va;
+
+ va_start( va, msg );
+ VASPrintf( &fmsg, msg, va );
+ va_end( va );
+ if (!fmsg)
+ return;
+ if (fd >= 0) {
+ olen = ASPrintf( &otxt, "%s\t%\\s\n", sts, fmsg );
+ if (otxt) {
+ Writer( fd, otxt, olen );
+ free( otxt );
+ }
+ what = "socket";
+ } else
+ what = "FiFo";
+ if (d)
+ Debug( "control %s for %s: %s - %s", what, d->name, sts, fmsg );
+ else
+ Debug( "global control %s: %s - %s", what, sts, fmsg );
+ free( fmsg );
+}
+
+
+static char *
+unQuote( const char *str )
+{
+ char *ret, *adp;
+
+ if (!(ret = Malloc( strlen( str ) + 1 )))
+ return 0;
+ for (adp = ret; *str; str++, adp++)
+ if (*str == '\\')
+ switch (*++str) {
+ case 0: str--; /* fallthrough */
+ case '\\': *adp = '\\'; break;
+ case 'n': *adp = '\n'; break;
+ case 't': *adp = '\t'; break;
+ default: *adp++ = '\\'; *adp = *str; break;
+ }
+ else
+ *adp = *str;
+ *adp = 0;
+ return ret;
+}
+
+static void
+str_cat_l( char **bp, const char *str, int max )
+{
+ int dnl = StrNLen( str, max );
+ memcpy( *bp, str, dnl );
+ *bp += dnl;
+}
+
+static void
+str_cat( char **bp, const char *str )
+{
+ int dnl = strlen( str );
+ memcpy( *bp, str, dnl );
+ *bp += dnl;
+}
+
+static void
+sd_cat( char **bp, SdRec *sdr )
+{
+ if (sdr->how == SHUT_HALT)
+ str_cat( bp, "halt," );
+ else
+ str_cat( bp, "reboot," );
+ if (sdr->start == TO_INF)
+ str_cat( bp, "0," );
+ else
+ *bp += sprintf( *bp, "%d,", sdr->start );
+ if (sdr->timeout == TO_INF)
+ str_cat( bp, "-1," );
+ else
+ *bp += sprintf( *bp, "%d,", sdr->timeout );
+ if (sdr->force == SHUT_ASK)
+ str_cat( bp, "ask" );
+ else if (sdr->force == SHUT_FORCE)
+ str_cat( bp, "force" );
+ else if (sdr->force == SHUT_FORCEMY)
+ str_cat( bp, "forcemy" );
+ else
+ str_cat( bp, "cancel" );
+ *bp += sprintf( *bp, ",%d,%s", sdr->uid, sdr->osname ? sdr->osname : "-" );
+}
+
+static void
+emitXSessC( struct display *di, struct display *d, void *ctx )
+{
+ char *dname, *bp;
+ char cbuf[1024];
+
+ bp = cbuf;
+ *bp++ = '\t';
+ dname = di->name;
+ if (!memcmp( dname, "localhost:", 10 ))
+ dname += 9;
+ str_cat_l( &bp, dname, sizeof(cbuf)/2 );
+ *bp++ = ',';
+#ifdef HAVE_VTS
+ if (di->serverVT)
+ bp += sprintf( bp, "vt%d", di->serverVT );
+#endif
+ *bp++ = ',';
+#ifdef XDMCP
+ if (di->status == remoteLogin) {
+ *bp++ = ',';
+ str_cat_l( &bp, di->remoteHost, sizeof(cbuf)/3 );
+ } else
+#endif
+ {
+ if (di->userName)
+ str_cat_l( &bp, di->userName, sizeof(cbuf)/5 );
+ *bp++ = ',';
+ if (di->sessName)
+ str_cat_l( &bp, di->sessName, sizeof(cbuf)/5 );
+ }
+ *bp++ = ',';
+ if (di == d)
+ *bp++ = '*';
+ if (di->userSess >= 0 &&
+ (d ? (d->userSess != di->userSess &&
+ (d->allowNuke == SHUT_NONE ||
+ (d->allowNuke == SHUT_ROOT && d->userSess))) :
+ !fifoAllowNuke))
+ *bp++ = '!';
+ Writer( (int)ctx, cbuf, bp - cbuf );
+}
+
+static void
+emitTTYSessC( STRUCTUTMP *ut, struct display *d, void *ctx )
+{
+ struct passwd *pw;
+ char *bp;
+ int vt, l;
+ char cbuf[sizeof(ut->ut_line) + sizeof(ut->ut_user) + sizeof(ut->ut_host) + 16];
+ char user[sizeof(ut->ut_user) + 1];
+
+#ifndef BSD_UTMP
+ if (ut->ut_type != USER_PROCESS)
+ l = 0;
+ else
+#endif
+ {
+ l = StrNLen( ut->ut_user, sizeof(ut->ut_user) );
+ memcpy( user, ut->ut_user, l );
+ }
+ user[l] = 0;
+ bp = cbuf;
+ *bp++ = '\t';
+ str_cat_l( &bp, ut->ut_line, sizeof(ut->ut_line) );
+ *bp++ = ',';
+ if (*ut->ut_host) {
+ *bp++ = '@';
+ str_cat_l( &bp, ut->ut_host, sizeof(ut->ut_host) );
+ }
+#ifdef HAVE_VTS
+ else if ((vt = TTYtoVT( ut->ut_line )))
+ bp += sprintf( bp, "vt%d", vt );
+#endif
+ *bp++ = ',';
+ str_cat( &bp, user );
+ *bp++ = ',';
+ /* blank: session type unknown */
+ *bp++ = ',';
+ /* blank: certainly not querying display */
+ *bp++ = 't';
+ if (*user &&
+ (d ? ((d->allowNuke == SHUT_NONE ||
+ (d->allowNuke == SHUT_ROOT && d->userSess)) &&
+ (!(pw = getpwnam( user )) || d->userSess != (int)pw->pw_uid)) :
+ !fifoAllowNuke))
+ *bp++ = '!';
+ Writer( (int)ctx, cbuf, bp - cbuf );
+}
+
+static void
+processCtrl( const char *string, int len, int fd, struct display *d )
+{
+#define Reply(t) Writer (fd, t, strlen (t))
+
+ struct display *di;
+ const char *word;
+ char **ar, **ap, *args, *bp;
+ SdRec sdr;
+ char cbuf[1024];
+
+ if (!(ar = initStrArr( 0 )))
+ return;
+ for (word = string; ; string++, len--)
+ if (!len || *string == '\t') {
+ if (!(ar = addStrArr( ar, word, string - word )))
+ return;
+ if (!len)
+ break;
+ word = string + 1;
+ }
+ word = fd >= 0 ? "socket" : "FiFo";
+ if (d)
+ Debug( "control %s for %s received %'[s\n", word, d->name, ar );
+ else
+ Debug( "global control %s received %'[s\n", word, ar );
+ if (ar[0]) {
+ if (fd >= 0 && !strcmp( ar[0], "caps" )) {
+ if (ar[1])
+ goto exce;
+ Reply( "ok\tkdm\tlist\t" );
+ if (bootManager != BO_NONE)
+ Reply( "bootoptions\t" );
+ if (d) {
+ if ((d->displayType & d_location) == dLocal)
+#ifdef HAVE_VTS
+ Reply( "local\tactivate\t" );
+#else
+ Reply( "local\t" );
+#endif
+ if (d->allowShutdown != SHUT_NONE) {
+ if (d->allowShutdown == SHUT_ROOT && d->userSess)
+ Reply( "shutdown root\t" );
+ else
+ Reply( "shutdown\t" );
+ Reply( "shutdown ask\t" );
+ if (d->allowNuke != SHUT_NONE) {
+ if (d->allowNuke == SHUT_ROOT && d->userSess)
+ Reply( "nuke root\t" );
+ else
+ Reply( "nuke\t" );
+ }
+ }
+ if ((d->displayType & d_location) == dLocal &&
+ AnyReserveDisplays())
+ Writer( fd, cbuf, sprintf( cbuf, "reserve %d\t",
+ idleReserveDisplays() ) );
+ Reply( "lock\tsuicide\n" );
+ } else {
+ if (fifoAllowShutdown) {
+ Reply( "shutdown\t" );
+ if (fifoAllowNuke)
+ Reply( "nuke\t" );
+ }
+ if (AnyReserveDisplays())
+ Writer( fd, cbuf, sprintf( cbuf, "reserve %d\t",
+ idleReserveDisplays() ) );
+#ifdef HAVE_VTS
+ Reply( "login\tactivate\n" );
+#else
+ Reply( "login\n" );
+#endif
+ }
+ goto bust;
+ } else if (fd >= 0 && !strcmp( ar[0], "list" )) {
+ int flags = lstRemote | lstTTY;
+ if (ar[1]) {
+ if (!strcmp( ar[1], "all" ))
+ flags = lstRemote | lstPassive | lstTTY;
+ else if (!strcmp( ar[1], "alllocal" ))
+ flags = lstPassive | lstTTY;
+ else {
+ fLog( d, fd, "bad", "invalid list scope %\"s", ar[1] );
+ goto bust;
+ }
+ if (ar[2])
+ goto exce;
+ }
+ Reply( "ok" );
+ ListSessions( flags, d, (void *)fd, emitXSessC, emitTTYSessC );
+ Reply( "\n" );
+ goto bust;
+ } else if (!strcmp( ar[0], "reserve" )) {
+ int lt = 60; /* XXX make default timeout configurable? */
+ if (ar[1]) {
+ lt = strtol( ar[1], &bp, 10 );
+ if (lt < 15 || *bp) {
+ fLog( d, fd, "bad", "invalid timeout %\"s", ar[1] );
+ goto bust;
+ }
+ if (ar[2])
+ goto exce;
+ }
+ if (d && (d->displayType & d_location) != dLocal) {
+ fLog( d, fd, "perm", "display is not local" );
+ goto bust;
+ }
+ if (!StartReserveDisplay( lt )) {
+ fLog( d, fd, "noent", "no reserve display available" );
+ goto bust;
+ }
+#ifdef HAVE_VTS
+ } else if (!strcmp( ar[0], "activate" )) {
+ int vt;
+ if (!ar[1])
+ goto miss;
+ if (ar[2])
+ goto exce;
+ if (d && (d->displayType & d_location) != dLocal) {
+ fLog( d, fd, "perm", "display is not local" );
+ goto bust;
+ }
+ if (ar[1][0] != 'v' || ar[1][1] != 't' ||
+ (vt = atoi( ar[1] + 2 )) <= 0)
+ {
+ if (!(di = FindDisplayByName( ar[1] ))) {
+ fLog( d, fd, "noent", "display not found" );
+ goto bust;
+ }
+ if ((di->displayType & d_location) != dLocal) {
+ fLog( d, fd, "inval", "target display is not local" );
+ goto bust;
+ }
+ if (!di->serverVT) {
+ fLog( d, fd, "noent", "target display has no VT assigned" );
+ goto bust;
+ }
+ vt = di->serverVT;
+ }
+ if (!activateVT( vt )) {
+ fLog( d, fd, "inval", "VT switch failed" );
+ goto bust;
+ }
+#endif
+ } else if (!strcmp( ar[0], "shutdown" )) {
+ ap = ar;
+ if (!*++ap)
+ goto miss;
+ sdr.force = SHUT_CANCEL;
+ sdr.osname = 0;
+ if (!strcmp( *ap, "status" )) {
+ if (fd < 0)
+ goto bust;
+ if (*++ap)
+ goto exce;
+ bp = cbuf;
+ *bp++ = 'o';
+ *bp++ = 'k';
+ if (sdRec.how) {
+ str_cat( &bp, "\tglobal," );
+ sd_cat( &bp, &sdRec );
+ }
+ if (d && d->hstent->sdRec.how) {
+ str_cat( &bp, "\tlocal," );
+ sd_cat( &bp, &d->hstent->sdRec );
+ }
+ *bp++ = '\n';
+ Writer( fd, cbuf, bp - cbuf );
+ goto bust;
+ } else if (!strcmp( *ap, "cancel" )) {
+ sdr.how = 0;
+ sdr.start = 0;
+ if (ap[1]) {
+ if (!d)
+ goto exce;
+ if (!strcmp( *++ap, "global" ))
+ sdr.start = TO_INF;
+ else if (strcmp( *ap, "local" )) {
+ fLog( d, fd, "bad", "invalid cancel scope %\"s", *ap );
+ goto bust;
+ }
+ }
+ } else {
+ if (!strcmp( *ap, "reboot" ))
+ sdr.how = SHUT_REBOOT;
+ else if (!strcmp( *ap, "halt" ))
+ sdr.how = SHUT_HALT;
+ else {
+ fLog( d, fd, "bad", "invalid type %\"s", *ap );
+ goto bust;
+ }
+ sdr.uid = -1;
+ if (!*++ap)
+ goto miss;
+ if (**ap == '=') {
+ switch (setBootOption( *ap + 1, &sdr )) {
+ case BO_NOMAN:
+ fLog( d, fd, "notsup", "boot options unavailable" );
+ goto bust;
+ case BO_NOENT:
+ fLog( d, fd, "noent", "no such boot option" );
+ goto bust;
+ case BO_IO:
+ fLog( d, fd, "io", "io error" );
+ goto bust;
+ }
+ if (!*++ap)
+ goto miss;
+ }
+ sdr.start = strtol( *ap, &bp, 10 );
+ if (bp != *ap && !*bp) {
+ if (**ap == '+')
+ sdr.start += now;
+ if (!*++ap)
+ goto miss;
+ sdr.timeout = strtol( *ap, &bp, 10 );
+ if (bp == *ap || *bp) {
+ fLog( d, fd, "bad", "invalid timeout %\"s", ar[3] );
+ goto bust;
+ }
+ if (**ap == '+')
+ sdr.timeout += sdr.start ? sdr.start : now;
+ if (sdr.timeout < 0)
+ sdr.timeout = TO_INF;
+ else {
+ if (!*++ap)
+ goto miss;
+ if (!strcmp( *ap, "force" ))
+ sdr.force = SHUT_FORCE;
+ else if (d && !strcmp( *ap, "forcemy" ))
+ sdr.force = SHUT_FORCEMY;
+ else if (strcmp( *ap, "cancel" )) {
+ fLog( d, fd, "bad", "invalid timeout action %\"s",
+ *ap );
+ goto bust;
+ }
+ }
+ } else {
+ sdr.timeout = 0;
+ if (d && !strcmp( *ap, "ask" ))
+ sdr.force = SHUT_ASK;
+ else if (!strcmp( *ap, "forcenow" ))
+ sdr.force = SHUT_FORCE;
+ else if (!strcmp( *ap, "schedule" ))
+ sdr.timeout = TO_INF;
+ else if (strcmp( *ap, "trynow" )) {
+ fLog( d, fd, "bad", "invalid mode %\"s", *ap );
+ goto bust;
+ }
+ }
+ }
+ if (*++ap)
+ goto exce;
+ if (d) {
+ sdr.uid = d->userSess >= 0 ? d->userSess : 0;
+ if (d->allowShutdown == SHUT_NONE ||
+ (d->allowShutdown == SHUT_ROOT && sdr.uid &&
+ sdr.force != SHUT_ASK))
+ {
+ fLog( d, fd, "perm", "shutdown forbidden" );
+ goto bust;
+ }
+ if (!sdr.how && !sdr.start) {
+ if (d->hstent->sdRec.osname)
+ free( d->hstent->sdRec.osname );
+ d->hstent->sdRec = sdr;
+ } else {
+ if (sdRec.how && sdRec.force == SHUT_FORCE &&
+ ((d->allowNuke == SHUT_NONE && sdRec.uid != sdr.uid) ||
+ (d->allowNuke == SHUT_ROOT && sdr.uid)))
+ {
+ fLog( d, fd, "perm", "overriding forced shutdown forbidden" );
+ goto bust;
+ }
+ if (sdr.force == SHUT_FORCE &&
+ (d->allowNuke == SHUT_NONE ||
+ (d->allowNuke == SHUT_ROOT && sdr.uid)))
+ {
+ fLog( d, fd, "perm", "forced shutdown forbidden" );
+ goto bust;
+ }
+ if (!sdr.start) {
+ if (d->hstent->sdRec.osname)
+ free( d->hstent->sdRec.osname );
+ d->hstent->sdRec = sdr;
+ } else {
+ if (!sdr.how)
+ cancelShutdown();
+ else {
+ if (sdRec.osname)
+ free( sdRec.osname );
+ sdRec = sdr;
+ }
+ }
+ }
+ } else {
+ if (!fifoAllowShutdown) {
+ fLog( d, fd, "perm", "shutdown forbidden" );
+ goto bust;
+ }
+ if (sdRec.how && sdRec.force == SHUT_FORCE &&
+ sdRec.uid != -1 && !fifoAllowNuke)
+ {
+ fLog( d, fd, "perm", "overriding forced shutdown forbidden" );
+ goto bust;
+ }
+ if (!sdr.how)
+ cancelShutdown();
+ else {
+ if (sdr.force != SHUT_CANCEL) {
+ if (!fifoAllowNuke) {
+ fLog( d, fd, "perm", "forced shutdown forbidden" );
+ goto bust;
+ }
+ } else {
+ if (!sdr.start && !sdr.timeout && AnyActiveDisplays()) {
+ fLog( d, fd, "busy", "user sessions running" );
+ goto bust;
+ }
+ }
+ sdr.uid = -1;
+ if (sdRec.osname)
+ free( sdRec.osname );
+ sdRec = sdr;
+ }
+ }
+ } else if (fd >= 0 && !strcmp( ar[0], "listbootoptions" )) {
+ char **opts;
+ int def, cur, i, j;
+
+ if (ar[1])
+ goto exce;
+ switch (getBootOptions( &opts, &def, &cur )) {
+ case BO_NOMAN:
+ fLog( d, fd, "notsup", "boot options unavailable" );
+ goto bust;
+ case BO_IO:
+ fLog( d, fd, "io", "io error" );
+ goto bust;
+ }
+ Reply( "ok\t" );
+ for (i = 0; opts[i]; i++) {
+ bp = cbuf;
+ if (i)
+ *bp++ = ' ';
+ for (j = 0; opts[i][j]; j++)
+ if (opts[i][j] == ' ') {
+ *bp++ = '\\';
+ *bp++ = 's';
+ } else
+ *bp++ = opts[i][j];
+ Writer( fd, cbuf, bp - cbuf );
+ }
+ freeStrArr( opts );
+ Writer( fd, cbuf, sprintf( cbuf, "\t%d\t%d\n", def, cur ) );
+ goto bust;
+ } else if (d) {
+ if (!strcmp( ar[0], "lock" )) {
+ if (ar[1])
+ goto exce;
+ d->hstent->lock = 1;
+ } else if (!strcmp( ar[0], "unlock" )) {
+ if (ar[1])
+ goto exce;
+ d->hstent->lock = 0;
+ } else if (!strcmp( ar[0], "suicide" )) {
+ if (ar[1])
+ goto exce;
+ if (d->status == running && d->pid != -1) {
+ TerminateProcess( d->pid, SIGTERM );
+ d->status = raiser;
+ }
+ } else {
+ fLog( d, fd, "nosys", "unknown command" );
+ goto bust;
+ }
+ } else {
+ if (!strcmp( ar[0], "login" )) {
+ int nuke;
+ if (arrLen( ar ) < 5) {
+ miss:
+ fLog( d, fd, "bad", "missing argument(s)" );
+ goto bust;
+ }
+ if (!(di = FindDisplayByName( ar[1] ))) {
+ fLog( d, fd, "noent", "display %s not found", ar[1] );
+ goto bust;
+ }
+ if (ar[5]) {
+ if (!(args = unQuote( ar[5] ))) {
+ fLog( d, fd, "nomem", "out of memory" );
+ goto bust;
+ }
+ if (ar[6]) {
+ free( args );
+ exce:
+ fLog( d, fd, "bad", "excess argument(s)" );
+ goto bust;
+ }
+ setNLogin( di, ar[3], ar[4], args, 2 );
+ free( args );
+ } else
+ setNLogin( di, ar[3], ar[4], 0, 2 );
+ nuke = !strcmp( ar[2], "now" );
+ switch (di->status) {
+ case running:
+ if (di->pid != -1 && (di->userSess < 0 || nuke)) {
+ TerminateProcess( di->pid, SIGTERM );
+ di->status = raiser;
+ }
+ break;
+ case remoteLogin:
+ if (di->serverPid != -1 && nuke)
+ TerminateProcess( di->serverPid, di->termSignal );
+ break;
+ case reserve:
+ di->status = notRunning;
+ break;
+ case textMode:
+#ifndef HAVE_VTS
+ SwitchToX( di );
+#endif
+ break;
+ default:
+ break;
+ }
+ } else {
+ fLog( d, fd, "nosys", "unknown command" );
+ goto bust;
+ }
+ }
+ if (fd >= 0)
+ Reply( "ok\n" );
+ }
+ bust:
+ freeStrArr( ar );
+}
+
+static int
+handleChan( struct display *d, struct bsock *cs, int fd, FD_TYPE *reads )
+{
+ char *bufp, *nbuf, *obuf, *eol;
+ int len, bl, llen;
+ char buf[1024];
+
+ bl = cs->buflen;
+ obuf = cs->buffer;
+ if (bl <= 0 && FD_ISSET( cs->fd, reads )) {
+ FD_CLR( cs->fd, reads );
+ bl = -bl;
+ memcpy( buf, obuf, bl );
+ if ((len = Reader( cs->fd, buf + bl, sizeof(buf) - bl )) <= 0)
+ return -1;
+ bl += len;
+ bufp = buf;
+ } else {
+ len = 0;
+ bufp = obuf;
+ }
+ if (bl > 0) {
+ if ((eol = memchr( bufp, '\n', bl ))) {
+ llen = eol - bufp + 1;
+ bl -= llen;
+ if (bl) {
+ if (!(nbuf = Malloc( bl )))
+ return -1;
+ memcpy( nbuf, bufp + llen, bl );
+ } else
+ nbuf = 0;
+ cs->buffer = nbuf;
+ cs->buflen = bl;
+ processCtrl( bufp, llen - 1, fd, d );
+ if (obuf)
+ free( obuf );
+ return 1;
+ } else if (!len) {
+ if (fd >= 0)
+ cs->buflen = -bl;
+ else
+ fLog( d, -1, "bad", "unterminated command" );
+ }
+ }
+ return 0;
+}
+
+int
+handleCtrl( FD_TYPE *reads, struct display *d )
+{
+ CtrlRec *cr = d ? &d->ctrl : &ctrl;
+ struct cmdsock *cs, **csp;
+
+ if (cr->fifo.fd >= 0) {
+ switch (handleChan( d, &cr->fifo, -1, reads )) {
+ case -1:
+ if (cr->fifo.buffer)
+ free( cr->fifo.buffer );
+ cr->fifo.buflen = 0;
+ break;
+ case 1:
+ return 1;
+ default:
+ break;
+ }
+ }
+ if (cr->fd >= 0 && FD_ISSET( cr->fd, reads ))
+ acceptSock( cr );
+ else {
+ for (csp = &cr->css; (cs = *csp); ) {
+ switch (handleChan( d, &cs->sock, cs->sock.fd, reads )) {
+ case -1:
+ *csp = cs->next;
+ nukeSock( cs );
+ continue;
+ case 1:
+ return 1;
+ default:
+ break;
+ }
+ csp = &cs->next;
+ }
+ }
+ return 0;
+}
diff --git a/kdm/backend/daemon.c b/kdm/backend/daemon.c
new file mode 100644
index 000000000..79d9e47ff
--- /dev/null
+++ b/kdm/backend/daemon.c
@@ -0,0 +1,78 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2000,2001,2003 Oswald Buddenhagen <ossi@kde.org>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the copyright holder.
+
+*/
+
+/*
+ * xdm - display manager daemon
+ * Author: Keith Packard, MIT X Consortium
+ */
+
+#include "dm.h"
+#include "dm_error.h"
+
+void
+BecomeDaemon( void )
+{
+ int pfd[2];
+
+ /*
+ * fork so that the process goes into the background automatically. Also
+ * has a nice side effect of having the child process get inherited by
+ * init (pid 1).
+ * Create a pipe and block on it, so the parent knows when the child is
+ * done with detaching. This eliminates the possibility that the child
+ * might get killed when the init script that's running xdm exits.
+ */
+
+ if (pipe( pfd ))
+ pfd[0] = pfd[1] = -1; /* so what ...? */
+ switch (fork ()) {
+ case 0:
+ /* child */
+ break;
+ case -1:
+ /* error */
+ LogError( "Daemon fork failed: %m\n" );
+ break;
+
+ default:
+ /* parent */
+ close( pfd[1] );
+ read( pfd[0], &pfd[1] /* dummy */, 1 );
+ exit( 0 );
+ }
+
+ /* don't use daemon() - it doesn't buy us anything but an additional fork */
+
+ setsid();
+
+ close( pfd[0] );
+ close( pfd[1] ); /* tell parent that we're done with detaching */
+
+ chdir( "/" );
+}
diff --git a/kdm/backend/dm.c b/kdm/backend/dm.c
new file mode 100644
index 000000000..e696a1a5e
--- /dev/null
+++ b/kdm/backend/dm.c
@@ -0,0 +1,1652 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2000-2005 Oswald Buddenhagen <ossi@kde.org>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the copyright holder.
+
+*/
+
+/*
+ * xdm - display manager daemon
+ * Author: Keith Packard, MIT X Consortium
+ *
+ * display manager
+ */
+
+#include "dm.h"
+#include "dm_auth.h"
+#include "dm_error.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_VTS
+# include <sys/ioctl.h>
+# include <sys/vt.h>
+#endif
+
+static void SigHandler( int n );
+static int ScanConfigs( int force );
+static void StartDisplays( void );
+#define XS_KEEP 0
+#define XS_RESTART 1
+#define XS_RETRY 2
+static void ExitDisplay( struct display *d, int endState, int serverCmd, int goodExit );
+static void rStopDisplay( struct display *d, int endState );
+static void MainLoop( void );
+
+static int signalFds[2];
+
+#if !defined(HAVE_SETPROCTITLE) && !defined(NOXDMTITLE)
+static char *Title;
+static int TitleLen;
+#endif
+
+static int StorePid( void );
+
+static int Stopping;
+SdRec sdRec = { 0, 0, 0, TO_INF, TO_INF, 0, 0, 0 };
+
+time_t now;
+
+char *prog, *progpath;
+
+int
+main( int argc, char **argv )
+{
+ int oldpid, oldumask, fd, noDaemonMode;
+ char *pt, *errorLogFile, **opts;
+
+ /* make sure at least world write access is disabled */
+ if (((oldumask = umask( 022 )) & 002) == 002)
+ (void)umask( oldumask );
+
+ /* give /dev/null as stdin */
+ if ((fd = open( "/dev/null", O_RDONLY )) > 0) {
+ dup2( fd, 0 );
+ close( fd );
+ }
+ if (fcntl( 1, F_GETFD ) < 0)
+ dup2( 0, 1 );
+ if (fcntl( 2, F_GETFD ) < 0)
+ dup2( 0, 2 );
+
+ if (argv[0][0] == '/') {
+ if (!StrDup( &progpath, argv[0] ))
+ Panic( "Out of memory" );
+ } else
+#ifdef __linux__
+ {
+ /* note that this will resolve symlinks ... */
+ int len;
+ char fullpath[PATH_MAX];
+ if ((len = readlink( "/proc/self/exe", fullpath, sizeof(fullpath) )) < 0)
+ Panic( "Invoke with full path specification or mount /proc" );
+ if (!StrNDup( &progpath, fullpath, len ))
+ Panic( "Out of memory" );
+ }
+#else
+# if 0
+ Panic( "Must be invoked with full path specification" );
+# else
+ {
+ char directory[PATH_MAX+1];
+ if (!getcwd( directory, sizeof(directory) ))
+ Panic( "Can't find myself (getcwd failed)" );
+ if (strchr( argv[0], '/' ))
+ StrApp( &progpath, directory, "/", argv[0], (char *)0 );
+ else {
+ int len;
+ char *path, *pathe, *name, *thenam, nambuf[PATH_MAX+1];
+
+ if (!(path = getenv( "PATH" )))
+ Panic( "Can't find myself (no PATH)" );
+ len = strlen( argv[0] );
+ name = nambuf + PATH_MAX - len;
+ memcpy( name, argv[0], len + 1 );
+ *--name = '/';
+ do {
+ if (!(pathe = strchr( path, ':' )))
+ pathe = path + strlen( path );
+ len = pathe - path;
+ if (!len || (len == 1 && *path == '.')) {
+ len = strlen( directory );
+ path = directory;
+ }
+ thenam = name - len;
+ if (thenam >= nambuf) {
+ memcpy( thenam, path, len );
+ if (!access( thenam, X_OK ))
+ goto found;
+ }
+ path = pathe;
+ } while (*path++ != '\0');
+ Panic( "Can't find myself (not in PATH)" );
+ found:
+ if (!StrDup( &progpath, thenam ))
+ Panic( "Out of memory" );
+ }
+ }
+# endif
+#endif
+ prog = strrchr( progpath, '/' ) + 1;
+
+#if !defined(HAVE_SETPROCTITLE) && !defined(NOXDMTITLE)
+ Title = argv[0];
+ TitleLen = (argv[argc - 1] + strlen( argv[argc - 1] )) - Title;
+#endif
+
+ /*
+ * Parse command line options
+ */
+ noDaemonMode = getppid();
+ errorLogFile = 0;
+ if (!(opts = Malloc( 2 * sizeof(char *) )))
+ return 1;
+ opts[0] = (char *)"";
+ opts[1] = 0;
+ while (*++argv) {
+ if (**argv != '-')
+ break;
+ pt = *argv + 1;
+ if (*pt == '-')
+ pt++;
+ if (!strcmp( pt, "help" ) || !strcmp( pt, "h" )) {
+ printf( "Usage: %s [options] [tty]\n"
+" -daemon\t - Daemonize even when started by init\n"
+" -nodaemon\t - Don't daemonize even when started from command line\n"
+" -config <file> - Use alternative master configuration file\n"
+" -xrm <res>\t - Override frontend-specific resource\n"
+" -error <file>\t - Use alternative log file\n"
+" -debug <num>\t - Debug option bitfield:\n"
+"\t\t\t0x1 - core log\n"
+"\t\t\t0x2 - config reader log\n"
+"\t\t\t0x4 - greeter log\n"
+"\t\t\t0x8 - IPC log\n"
+"\t\t\t0x10 - session sub-daemon post-fork delay\n"
+"\t\t\t0x20 - config reader post-start delay\n"
+"\t\t\t0x40 - greeter post-start delay\n"
+"\t\t\t0x80 - don't use syslog\n"
+"\t\t\t0x100 - core Xauth log\n"
+"\t\t\t0x400 - valgrind config reader and greeter\n"
+"\t\t\t0x800 - strace config reader and greeter\n"
+ , prog );
+ exit( 0 );
+ } else if (!strcmp( pt, "daemon" ))
+ noDaemonMode = 0;
+ else if (!strcmp( pt, "nodaemon" ))
+ noDaemonMode = 1;
+ else if (argv[1] && !strcmp( pt, "config" ))
+ StrDup( opts, *++argv );
+ else if (argv[1] && !strcmp( pt, "xrm" ))
+ opts = addStrArr( opts, *++argv, -1 );
+ else if (argv[1] && !strcmp( pt, "debug" ))
+ sscanf( *++argv, "%i", &debugLevel );
+ else if (argv[1] && (!strcmp( pt, "error" ) || !strcmp( pt, "logfile" )))
+ errorLogFile = *++argv;
+ else {
+ fprintf( stderr, "\"%s\" is an unknown option or is missing a parameter\n", *argv );
+ exit( 1 );
+ }
+ }
+
+ /*
+ * Only allow root to run in non-debug mode to avoid problems
+ */
+ if (!debugLevel && getuid()) {
+ fprintf( stderr, "Only root wants to run %s\n", prog );
+ exit( 1 );
+ }
+
+ InitErrorLog( errorLogFile );
+
+ if (noDaemonMode != 1)
+ BecomeDaemon();
+
+ /*
+ * Step 1 - load configuration parameters
+ */
+ if (!InitResources( opts ) || ScanConfigs( FALSE ) < 0)
+ LogPanic( "Config reader failed. Aborting ...\n" );
+
+ /* SUPPRESS 560 */
+ if ((oldpid = StorePid())) {
+ if (oldpid == -1)
+ LogError( "Can't create/lock pid file %s\n", pidFile );
+ else
+ LogError( "Can't lock pid file %s, another xdm is running (pid %d)\n",
+ pidFile, oldpid );
+ exit( 1 );
+ }
+
+#ifdef NEED_ENTROPY
+ AddOtherEntropy();
+#endif
+
+ /*
+ * We used to clean up old authorization files here. As authDir is
+ * supposed to be /var/run/xauth or /tmp, we needn't to care for it.
+ */
+
+#ifdef XDMCP
+ init_session_id();
+#else
+ Debug( "not compiled for XDMCP\n" );
+#endif
+ if (pipe( signalFds ))
+ LogPanic( "Unable to create signal notification pipe.\n" );
+ RegisterInput( signalFds[0] );
+ RegisterCloseOnFork( signalFds[0] );
+ RegisterCloseOnFork( signalFds[1] );
+ (void)Signal( SIGTERM, SigHandler );
+ (void)Signal( SIGINT, SigHandler );
+ (void)Signal( SIGHUP, SigHandler );
+ (void)Signal( SIGCHLD, SigHandler );
+ (void)Signal( SIGUSR1, SigHandler );
+
+ /*
+ * Step 2 - run a sub-daemon for each entry
+ */
+#ifdef XDMCP
+ UpdateListenSockets();
+#endif
+ openCtrl( 0 );
+ MainLoop();
+ closeCtrl( 0 );
+ if (sdRec.how) {
+ commitBootOption();
+ if (Fork() <= 0) {
+ char *cmd = sdRec.how == SHUT_HALT ? cmdHalt : cmdReboot;
+ execute( parseArgs( (char **)0, cmd ), (char **)0 );
+ LogError( "Failed to execute shutdown command %\"s\n", cmd );
+ exit( 1 );
+ } else {
+ sigset_t mask;
+ sigemptyset( &mask );
+ sigaddset( &mask, SIGCHLD );
+ sigaddset( &mask, SIGHUP );
+ sigsuspend( &mask );
+ }
+ }
+ Debug( "nothing left to do, exiting\n" );
+ return 0;
+}
+
+
+#ifdef HAVE_VTS
+int
+TTYtoVT( const char *tty )
+{
+ return memcmp( tty, "tty", 3 ) ? 0 : atoi( tty + 3 );
+}
+
+int
+activateVT( int vt )
+{
+ int ret = 0;
+ int con = open( "/dev/console", O_RDONLY );
+ if (con >= 0) {
+ if (!ioctl( con, VT_ACTIVATE, vt ))
+ ret = 1;
+ close( con );
+ }
+ return ret;
+}
+
+
+static void
+WakeDisplay( struct display *d )
+{
+ if (d->status == textMode)
+ d->status = (d->displayType & d_lifetime) == dReserve ? reserve : notRunning;
+}
+#endif
+
+enum utState { UtDead, UtWait, UtActive };
+
+struct utmps {
+ struct utmps *next;
+#ifndef HAVE_VTS
+ struct display *d;
+#endif
+ time_t time;
+ enum utState state;
+ int hadSess;
+};
+
+#define TIME_LOG 40
+#define TIME_RELOG 10
+
+static struct utmps *utmpList;
+static time_t utmpTimeout = TO_INF;
+
+static void
+bombUtmp( void )
+{
+ struct utmps *utp;
+
+ while ((utp = utmpList)) {
+#ifdef HAVE_VTS
+ ForEachDisplay( WakeDisplay );
+#else
+ utp->d->status = notRunning;
+#endif
+ utmpList = utp->next;
+ free( utp );
+ }
+}
+
+static void
+CheckUtmp( void )
+{
+ static time_t modtim;
+ time_t nck;
+ time_t ends;
+ struct utmps *utp, **utpp;
+ struct stat st;
+#ifdef BSD_UTMP
+ int fd;
+ struct utmp ut[1];
+#else
+ STRUCTUTMP *ut;
+#endif
+
+ if (!utmpList)
+ return;
+ if (stat( UTMP_FILE, &st )) {
+ LogError( UTMP_FILE " not found - cannot use console mode\n" );
+ bombUtmp();
+ return;
+ }
+ if (modtim != st.st_mtime) {
+ Debug( "rescanning " UTMP_FILE "\n" );
+ for (utp = utmpList; utp; utp = utp->next)
+ utp->state = UtDead;
+#ifdef BSD_UTMP
+ if ((fd = open( UTMP_FILE, O_RDONLY )) < 0) {
+ LogError( "Cannot open " UTMP_FILE " - cannot use console mode\n" );
+ bombUtmp();
+ return;
+ }
+ while (Reader( fd, ut, sizeof(ut[0]) ) == sizeof(ut[0]))
+#else
+ SETUTENT();
+ while ((ut = GETUTENT()))
+#endif
+ {
+ for (utp = utmpList; utp; utp = utp->next) {
+#ifdef HAVE_VTS
+ char **line;
+ for (line = consoleTTYs; *line; line++)
+ if (!strncmp( *line, ut->ut_line, sizeof(ut->ut_line) ))
+ goto hitlin;
+ continue;
+ hitlin:
+#else
+ if (strncmp( utp->d->console, ut->ut_line, sizeof(ut->ut_line) ))
+ continue;
+#endif
+#ifdef BSD_UTMP
+ if (!*ut->ut_user) {
+#else
+ if (ut->ut_type != USER_PROCESS) {
+#endif
+#ifdef HAVE_VTS
+ if (utp->state == UtActive)
+ break;
+#endif
+ utp->state = UtWait;
+ } else {
+ utp->hadSess = 1;
+ utp->state = UtActive;
+ }
+ if (utp->time < ut->ut_time) /* theoretically superfluous */
+ utp->time = ut->ut_time;
+ break;
+ }
+ }
+#ifdef BSD_UTMP
+ close( fd );
+#else
+ ENDUTENT();
+#endif
+ modtim = st.st_mtime;
+ }
+ for (utpp = &utmpList; (utp = *utpp); ) {
+ if (utp->state != UtActive) {
+ if (utp->state == UtDead) /* shouldn't happen ... */
+ utp->time = 0;
+ ends = utp->time + (utp->hadSess ? TIME_RELOG : TIME_LOG);
+ if (ends <= now) {
+#ifdef HAVE_VTS
+ ForEachDisplay( WakeDisplay );
+ Debug( "console login timed out\n" );
+#else
+ utp->d->status = notRunning;
+ Debug( "console login for %s at %s timed out\n",
+ utp->d->name, utp->d->console );
+#endif
+ *utpp = utp->next;
+ free( utp );
+ continue;
+ } else
+ nck = ends;
+ } else
+ nck = TIME_RELOG + now;
+ if (nck < utmpTimeout)
+ utmpTimeout = nck;
+ utpp = &(*utpp)->next;
+ }
+}
+
+static void
+#ifdef HAVE_VTS
+SwitchToTty( void )
+#else
+SwitchToTty( struct display *d )
+#endif
+{
+ struct utmps *utp;
+#ifdef HAVE_VTS
+ int vt;
+#endif
+
+ if (!(utp = Malloc( sizeof(*utp) ))) {
+#ifdef HAVE_VTS
+ ForEachDisplay( WakeDisplay );
+#else
+ d->status = notRunning;
+#endif
+ return;
+ }
+#ifndef HAVE_VTS
+ d->status = textMode;
+ utp->d = d;
+#endif
+ utp->time = now;
+ utp->hadSess = 0;
+ utp->next = utmpList;
+ utmpList = utp;
+ CheckUtmp();
+
+#ifdef HAVE_VTS
+ if ((vt = TTYtoVT( *consoleTTYs )))
+ activateVT( vt );
+#endif
+
+ /* XXX output something useful here */
+}
+
+#ifdef HAVE_VTS
+static void
+StopToTTY( struct display *d )
+{
+ if ((d->displayType & d_location) == dLocal)
+ switch (d->status) {
+ default:
+ rStopDisplay( d, DS_TEXTMODE | 0x100 );
+ case reserve:
+ case textMode:
+ break;
+ }
+}
+
+static void
+CheckTTYMode( void )
+{
+ struct display *d;
+
+ for (d = displays; d; d = d->next)
+ if (d->status == zombie)
+ return;
+
+ SwitchToTty();
+}
+
+#else
+
+void
+SwitchToX( struct display *d )
+{
+ struct utmps *utp, **utpp;
+
+ for (utpp = &utmpList; (utp = *utpp); utpp = &(*utpp)->next)
+ if (utp->d == d) {
+ *utpp = utp->next;
+ free( utp );
+ d->status = notRunning;
+ return;
+ }
+}
+#endif
+
+#ifdef XDMCP
+static void
+StartRemoteLogin( struct display *d )
+{
+ char **argv;
+ int pid;
+
+ Debug( "StartRemoteLogin for %s\n", d->name );
+ /* HACK: omitting LoadDisplayResources( d ) here! */
+ switch (pid = Fork()) {
+ case 0:
+ argv = PrepServerArgv( d, d->serverArgsRemote );
+ if (!(argv = addStrArr( argv, "-once", 5 )) ||
+ !(argv = addStrArr( argv, "-query", 6 )) ||
+ !(argv = addStrArr( argv, d->remoteHost, -1 )))
+ exit( 1 );
+ Debug( "exec %\"[s\n", argv );
+ (void)execv( argv[0], argv );
+ LogError( "X server %\"s cannot be executed\n", argv[0] );
+ exit( 1 );
+ case -1:
+ LogError( "Forking X server for remote login failed: %m" );
+ d->status = notRunning;
+ return;
+ default:
+ break;
+ }
+ Debug( "X server forked, pid %d\n", pid );
+ d->serverPid = pid;
+
+ d->status = remoteLogin;
+}
+#endif
+
+
+static void
+StopInactiveDisplay( struct display *d )
+{
+ if (d->status != remoteLogin && d->userSess < 0)
+ StopDisplay( d );
+}
+
+static void
+stoppen( int force )
+{
+#ifdef XDMCP
+ request_port = 0;
+ UpdateListenSockets();
+#endif
+ if (force)
+ ForEachDisplay( StopDisplay );
+ else
+ ForEachDisplay( StopInactiveDisplay );
+ Stopping = 1;
+}
+
+
+void
+setNLogin( struct display *d,
+ const char *nuser, const char *npass, char *nargs, int rl )
+{
+ struct disphist *he = d->hstent;
+ he->rLogin =
+ (ReStr( &he->nuser, nuser ) &&
+ ReStr( &he->npass, npass ) &&
+ ReStr( &he->nargs, nargs )) ? rl : 0;
+ Debug( "set next login for %s, level %d\n", nuser, rl );
+}
+
+static void
+processDPipe( struct display *d )
+{
+ char *user, *pass, *args;
+ int cmd;
+ GTalk dpytalk;
+#ifdef XDMCP
+ int ct, len;
+ ARRAY8 ca, ha;
+#endif
+
+ dpytalk.pipe = &d->pipe;
+ if (Setjmp( dpytalk.errjmp )) {
+ StopDisplay( d );
+ return;
+ }
+ GSet( &dpytalk );
+ if (!GRecvCmd( &cmd )) {
+ /* process already exited */
+ UnregisterInput( d->pipe.rfd );
+ return;
+ }
+ switch (cmd) {
+ case D_User:
+ d->userSess = GRecvInt();
+ d->userName = GRecvStr();
+ d->sessName = GRecvStr();
+ break;
+ case D_ReLogin:
+ user = GRecvStr();
+ pass = GRecvStr();
+ args = GRecvStr();
+ setNLogin( d, user, pass, args, 1 );
+ free( args );
+ free( pass );
+ free( user );
+ break;
+#ifdef XDMCP
+ case D_ChooseHost:
+ ca.data = (unsigned char *)GRecvArr( &len );
+ ca.length = (CARD16)len;
+ ct = GRecvInt();
+ ha.data = (unsigned char *)GRecvArr( &len );
+ ha.length = (CARD16)len;
+ RegisterIndirectChoice( &ca, ct, &ha );
+ XdmcpDisposeARRAY8( &ha );
+ XdmcpDisposeARRAY8( &ca );
+ break;
+ case D_RemoteHost:
+ if (d->remoteHost)
+ free( d->remoteHost );
+ d->remoteHost = GRecvStr();
+ break;
+#endif
+ case D_XConnOk:
+ startingServer = 0;
+ break;
+ default:
+ LogError( "Internal error: unknown D_* command %d\n", cmd );
+ StopDisplay( d );
+ break;
+ }
+}
+
+static void
+emitXSessG( struct display *di, struct display *d, void *ctx ATTR_UNUSED )
+{
+ GSendStr( di->name );
+ GSendStr( "" );
+#ifdef HAVE_VTS
+ GSendInt( di->serverVT );
+#endif
+#ifdef XDMCP
+ if (di->status == remoteLogin) {
+ GSendStr( "" );
+ GSendStr( di->remoteHost );
+ } else
+#endif
+ {
+ GSendStr( di->userName );
+ GSendStr( di->sessName );
+ }
+ GSendInt( di == d ? isSelf : 0 );
+}
+
+static void
+emitTTYSessG( STRUCTUTMP *ut, struct display *d ATTR_UNUSED, void *ctx ATTR_UNUSED )
+{
+ GSendStrN( ut->ut_line, sizeof(ut->ut_line) );
+ GSendStrN( ut->ut_host, sizeof(ut->ut_host) );
+#ifdef HAVE_VTS
+ GSendInt( TTYtoVT( ut->ut_line ) );
+#endif
+#ifdef BSD_UTMP
+ GSendStrN( *ut->ut_user ? ut->ut_user : 0, sizeof(ut->ut_user) );
+#else
+ GSendStrN( ut->ut_type == USER_PROCESS ? ut->ut_user : 0, sizeof(ut->ut_user) );
+#endif
+ GSendStr( 0 ); /* session type unknown */
+ GSendInt( isTTY );
+}
+
+static void
+processGPipe( struct display *d )
+{
+ char **opts, *option;
+ int cmd, ret, dflt, curr;
+ GTalk dpytalk;
+
+ dpytalk.pipe = &d->gpipe;
+ if (Setjmp( dpytalk.errjmp )) {
+ StopDisplay( d );
+ return;
+ }
+ GSet( &dpytalk );
+ if (!GRecvCmd( &cmd )) {
+ /* process already exited */
+ UnregisterInput( d->gpipe.rfd );
+ return;
+ }
+ switch (cmd) {
+ case G_ListBootOpts:
+ ret = getBootOptions( &opts, &dflt, &curr );
+ GSendInt( ret );
+ if (ret == BO_OK) {
+ GSendArgv( opts );
+ freeStrArr( opts );
+ GSendInt( dflt );
+ GSendInt( curr );
+ }
+ break;
+ case G_Shutdown:
+ sdRec.how = GRecvInt();
+ sdRec.start = GRecvInt();
+ sdRec.timeout = GRecvInt();
+ sdRec.force = GRecvInt();
+ sdRec.uid = GRecvInt();
+ option = GRecvStr();
+ setBootOption( option, &sdRec );
+ if (option)
+ free( option );
+ break;
+ case G_QueryShutdown:
+ GSendInt( sdRec.how );
+ GSendInt( sdRec.start );
+ GSendInt( sdRec.timeout );
+ GSendInt( sdRec.force );
+ GSendInt( sdRec.uid );
+ GSendStr( sdRec.osname );
+ break;
+ case G_List:
+ ListSessions( GRecvInt(), d, 0, emitXSessG, emitTTYSessG );
+ GSendInt( 0 );
+ break;
+#ifdef HAVE_VTS
+ case G_Activate:
+ activateVT( GRecvInt() );
+ break;
+#endif
+ case G_Console:
+#ifdef HAVE_VTS
+ if (*consoleTTYs) { /* sanity check against greeter */
+ ForEachDisplay( StopToTTY );
+ CheckTTYMode();
+ }
+#else
+ if (*d->console) /* sanity check against greeter */
+ rStopDisplay( d, DS_TEXTMODE );
+#endif
+ break;
+ default:
+ LogError( "Internal error: unknown G_* command %d\n", cmd );
+ StopDisplay( d );
+ break;
+ }
+}
+
+
+static int
+ScanConfigs( int force )
+{
+ int ret;
+
+ if ((ret = LoadDMResources( force )) <= 0)
+ return ret;
+ ScanServers();
+#ifdef XDMCP
+ ScanAccessDatabase( force );
+#endif
+ return 1;
+}
+
+static void
+MarkDisplay( struct display *d )
+{
+ d->stillThere = 0;
+}
+
+static void
+RescanConfigs( int force )
+{
+ if (ScanConfigs( force ) > 0) {
+#ifdef XDMCP
+ UpdateListenSockets();
+#endif
+ updateCtrl();
+ }
+}
+
+void
+cancelShutdown( void )
+{
+ sdRec.how = 0;
+ if (sdRec.osname) {
+ free( sdRec.osname );
+ sdRec.osname = 0;
+ }
+ Stopping = 0;
+ RescanConfigs( TRUE );
+}
+
+
+static void
+ReapChildren( void )
+{
+ int pid;
+ struct display *d;
+ waitType status;
+
+ while ((pid = waitpid( -1, &status, WNOHANG )) > 0)
+ {
+ Debug( "manager wait returns pid %d sig %d core %d code %d\n",
+ pid, waitSig( status ), waitCore( status ), waitCode( status ) );
+ /* SUPPRESS 560 */
+ if ((d = FindDisplayByPid( pid ))) {
+ d->pid = -1;
+ UnregisterInput( d->pipe.rfd );
+ GClosen (&d->pipe);
+ UnregisterInput( d->gpipe.rfd );
+ GClosen (&d->gpipe);
+ closeCtrl( d );
+ switch (waitVal( status )) {
+#ifdef XDMCP
+ case EX_REMOTE:
+ Debug( "display exited with EX_REMOTE\n" );
+ ExitDisplay( d, DS_REMOTE, 0, 0 );
+ break;
+#endif
+ case EX_NORMAL:
+ /* (any type of) session ended */
+ Debug( "display exited with EX_NORMAL\n" );
+ if ((d->displayType & d_lifetime) == dReserve)
+ ExitDisplay( d, DS_RESERVE, 0, 0 );
+ else
+ ExitDisplay( d, DS_RESTART, XS_KEEP, TRUE );
+ break;
+#if 0
+ case EX_REMANAGE_DPY:
+ /* user session ended */
+ Debug( "display exited with EX_REMANAGE_DPY\n" );
+ ExitDisplay( d, DS_RESTART, XS_KEEP, TRUE );
+ break;
+#endif
+ case EX_OPENFAILED_DPY:
+ /* WaitForServer() failed */
+ LogError( "Display %s cannot be opened\n", d->name );
+#ifdef XDMCP
+ /*
+ * no display connection was ever made, tell the
+ * terminal that the open attempt failed
+ */
+ if ((d->displayType & d_origin) == dFromXDMCP)
+ SendFailed( d, "cannot open display" );
+#endif
+ ExitDisplay( d, DS_RESTART, XS_RETRY, FALSE );
+ break;
+ case waitCompose( SIGTERM,0,0 ):
+ /* killed before/during WaitForServer()
+ - local Xserver died
+ - display stopped (is zombie)
+ - "login now" and "suicide" pipe commands (is raiser)
+ */
+ Debug( "display exited on SIGTERM\n" );
+ ExitDisplay( d, DS_RESTART, XS_RETRY, FALSE );
+ break;
+ case EX_AL_RESERVER_DPY:
+ /* - killed after WaitForServer()
+ - Xserver dead after remote session exit
+ */
+ Debug( "display exited with EX_AL_RESERVER_DPY\n" );
+ ExitDisplay( d, DS_RESTART, XS_RESTART, FALSE );
+ break;
+ case EX_RESERVER_DPY:
+ /* induced by greeter:
+ - could not secure display
+ - requested by user
+ */
+ Debug( "display exited with EX_RESERVER_DPY\n" );
+ ExitDisplay( d, DS_RESTART, XS_RESTART, TRUE );
+ break;
+ case EX_UNMANAGE_DPY:
+ /* some fatal error */
+ Debug( "display exited with EX_UNMANAGE_DPY\n" );
+ ExitDisplay( d, DS_REMOVE, 0, 0 );
+ break;
+ default:
+ /* prolly crash */
+ LogError( "Unknown session exit code %d (sig %d) from manager process\n",
+ waitCode( status ), waitSig( status ) );
+ ExitDisplay( d, DS_REMOVE, 0, 0 );
+ break;
+ }
+ } else if ((d = FindDisplayByServerPid( pid ))) {
+ d->serverPid = -1;
+ switch (d->status) {
+ case zombie:
+ Debug( "zombie X server for display %s reaped\n", d->name );
+#ifdef HAVE_VTS
+ if (d->serverVT && d->zstatus != DS_REMOTE) {
+ if (d->follower) {
+ d->follower->serverVT = d->serverVT;
+ d->follower = 0;
+ } else {
+ int con = open( "/dev/console", O_RDONLY );
+ if (con >= 0) {
+ struct vt_stat vtstat;
+ ioctl( con, VT_GETSTATE, &vtstat );
+ if (vtstat.v_active == d->serverVT) {
+ int vt = 1;
+ struct display *di;
+ for (di = displays; di; di = di->next)
+ if (di != d && di->serverVT)
+ vt = di->serverVT;
+ for (di = displays; di; di = di->next)
+ if (di != d && di->serverVT &&
+ (di->userSess >= 0 ||
+ di->status == remoteLogin))
+ vt = di->serverVT;
+ ioctl( con, VT_ACTIVATE, vt );
+ }
+ ioctl( con, VT_DISALLOCATE, d->serverVT );
+ close( con );
+ }
+ }
+ d->serverVT = 0;
+ }
+#endif
+ rStopDisplay( d, d->zstatus );
+ break;
+ case phoenix:
+ Debug( "phoenix X server arises, restarting display %s\n",
+ d->name );
+ d->status = notRunning;
+ break;
+ case remoteLogin:
+ Debug( "remote login X server for display %s exited\n",
+ d->name );
+ d->status = ((d->displayType & d_lifetime) == dReserve) ?
+ reserve : notRunning;
+ break;
+ case raiser:
+ LogError( "X server for display %s terminated unexpectedly\n",
+ d->name );
+ /* don't kill again */
+ break;
+ case running:
+ if (startingServer == d && d->serverStatus != ignore) {
+ if (d->serverStatus == starting && waitCode( status ) != 47)
+ LogError( "X server died during startup\n" );
+ StartServerFailed();
+ break;
+ }
+ LogError( "X server for display %s terminated unexpectedly\n",
+ d->name );
+ if (d->pid != -1) {
+ Debug( "terminating session pid %d\n", d->pid );
+ TerminateProcess( d->pid, SIGTERM );
+ }
+ break;
+ case notRunning:
+ case textMode:
+ case reserve:
+ /* this cannot happen */
+ Debug( "X server exited for passive (%d) session on display %s\n",
+ (int)d->status, d->name );
+ break;
+ }
+ } else
+ Debug( "unknown child termination\n" );
+ }
+#ifdef NEED_ENTROPY
+ AddOtherEntropy();
+#endif
+}
+
+static int
+wouldShutdown( void )
+{
+ struct display *d;
+
+ if (sdRec.force != SHUT_CANCEL) {
+ if (sdRec.force == SHUT_FORCEMY)
+ for (d = displays; d; d = d->next)
+ if (d->status == remoteLogin ||
+ (d->userSess >= 0 && d->userSess != sdRec.uid))
+ return 0;
+ return 1;
+ }
+ return !AnyActiveDisplays();
+}
+
+FD_TYPE WellKnownSocketsMask;
+int WellKnownSocketsMax;
+int WellKnownSocketsCount;
+
+void
+RegisterInput( int fd )
+{
+ /* can be omited, as it is always called right after opening a socket
+ if (!FD_ISSET (fd, &WellKnownSocketsMask))
+ */
+ {
+ FD_SET( fd, &WellKnownSocketsMask );
+ if (fd > WellKnownSocketsMax)
+ WellKnownSocketsMax = fd;
+ WellKnownSocketsCount++;
+ }
+}
+
+void
+UnregisterInput( int fd )
+{
+ /* the check _is_ necessary, as some handles are unregistered before
+ the regular close sequence.
+ */
+ if (FD_ISSET( fd, &WellKnownSocketsMask )) {
+ FD_CLR( fd, &WellKnownSocketsMask );
+ WellKnownSocketsCount--;
+ }
+}
+
+static void
+SigHandler( int n )
+{
+ int olderrno = errno;
+ char buf = (char)n;
+ /* Debug( "caught signal %d\n", n ); this hangs in syslog() */
+ write( signalFds[1], &buf, 1 );
+#ifdef __EMX__
+ (void)Signal( n, SigHandler );
+#endif
+ errno = olderrno;
+}
+
+static void
+MainLoop( void )
+{
+ struct display *d;
+ struct timeval *tvp, tv;
+ time_t to;
+ int nready;
+ char buf;
+ FD_TYPE reads;
+
+ Debug( "MainLoop\n" );
+ time( &now );
+ while (
+#ifdef XDMCP
+ AnyListenSockets() ||
+#endif
+ (Stopping ? AnyRunningDisplays() : AnyDisplaysLeft()))
+ {
+ if (!Stopping)
+ StartDisplays();
+ to = TO_INF;
+ if (sdRec.how) {
+ if (sdRec.start != TO_INF && now < sdRec.start) {
+ /*if (sdRec.start < to)*/
+ to = sdRec.start;
+ } else {
+ sdRec.start = TO_INF;
+ if (now >= sdRec.timeout) {
+ sdRec.timeout = TO_INF;
+ if (wouldShutdown())
+ stoppen( TRUE );
+ else
+ cancelShutdown();
+ } else {
+ stoppen( FALSE );
+ /*if (sdRec.timeout < to)*/
+ to = sdRec.timeout;
+ }
+ }
+ }
+ if (serverTimeout < to)
+ to = serverTimeout;
+ if (utmpTimeout < to)
+ to = utmpTimeout;
+ if (to == TO_INF)
+ tvp = 0;
+ else {
+ to -= now;
+ if (to < 0)
+ to = 0;
+ tv.tv_sec = to;
+ tv.tv_usec = 0;
+ tvp = &tv;
+ }
+ reads = WellKnownSocketsMask;
+ nready = select( WellKnownSocketsMax + 1, &reads, 0, 0, tvp );
+ Debug( "select returns %d\n", nready );
+ time( &now );
+#ifdef NEED_ENTROPY
+ AddTimerEntropy();
+#endif
+ if (now >= serverTimeout) {
+ serverTimeout = TO_INF;
+ StartServerTimeout();
+ }
+ if (now >= utmpTimeout) {
+ utmpTimeout = TO_INF;
+ CheckUtmp();
+ }
+ if (nready > 0) {
+ /*
+ * we restart after the first handled fd, as
+ * a) it makes things simpler
+ * b) the probability that multiple fds trigger at once is
+ * ridiculously small. we handle it in the next iteration.
+ */
+ /* XXX a cleaner solution would be a callback mechanism */
+ if (FD_ISSET( signalFds[0], &reads )) {
+ if (read( signalFds[0], &buf, 1 ) != 1)
+ LogPanic( "Signal notification pipe broken.\n" );
+ switch (buf) {
+ case SIGTERM:
+ case SIGINT:
+ Debug( "shutting down entire manager\n" );
+ stoppen( TRUE );
+ break;
+ case SIGHUP:
+ LogInfo( "Rescanning all config files\n" );
+ ForEachDisplay( MarkDisplay );
+ RescanConfigs( TRUE );
+ break;
+ case SIGCHLD:
+ ReapChildren();
+ if (!Stopping && autoRescan)
+ RescanConfigs( FALSE );
+ break;
+ case SIGUSR1:
+ if (startingServer &&
+ startingServer->serverStatus == starting)
+ StartServerSuccess();
+ break;
+ }
+ continue;
+ }
+#ifdef XDMCP
+ if (ProcessListenSockets( &reads ))
+ continue;
+#endif /* XDMCP */
+ if (handleCtrl( &reads, 0 ))
+ continue;
+ /* Must be last (because of the breaks)! */
+ again:
+ for (d = displays; d; d = d->next) {
+ if (handleCtrl( &reads, d ))
+ goto again;
+ if (d->pipe.rfd >= 0 && FD_ISSET( d->pipe.rfd, &reads )) {
+ processDPipe( d );
+ break;
+ }
+ if (d->gpipe.rfd >= 0 && FD_ISSET( d->gpipe.rfd, &reads )) {
+ processGPipe( d );
+ break;
+ }
+ }
+ }
+ }
+}
+
+static void
+CheckDisplayStatus( struct display *d )
+{
+ if ((d->displayType & d_origin) == dFromFile && !d->stillThere)
+ StopDisplay( d );
+ else if ((d->displayType & d_lifetime) == dReserve &&
+ d->status == running && d->userSess < 0 && !d->idleTimeout)
+ rStopDisplay( d, DS_RESERVE );
+ else if (d->status == notRunning)
+ if (LoadDisplayResources( d ) < 0) {
+ LogError( "Unable to read configuration for display %s; "
+ "stopping it.\n", d->name );
+ StopDisplay( d );
+ return;
+ }
+}
+
+static void
+KickDisplay( struct display *d )
+{
+ if (d->status == notRunning)
+ StartDisplay( d );
+ if (d->serverStatus == awaiting && !startingServer)
+ StartServer( d );
+}
+
+#ifdef HAVE_VTS
+static int active_vts;
+
+static int
+GetBusyVTs( void )
+{
+ struct vt_stat vtstat;
+ int con;
+
+ if (active_vts == -1) {
+ vtstat.v_state = 0;
+ if ((con = open( "/dev/console", O_RDONLY )) >= 0) {
+ ioctl( con, VT_GETSTATE, &vtstat );
+ close( con );
+ }
+ active_vts = vtstat.v_state;
+ }
+ return active_vts;
+}
+
+static void
+AllocateVT( struct display *d )
+{
+ struct display *cd;
+ int i, tvt, volun;
+
+ if ((d->displayType & d_location) == dLocal &&
+ d->status == notRunning && !d->serverVT && d->reqSrvVT >= 0)
+ {
+ if (d->reqSrvVT && d->reqSrvVT < 16)
+ d->serverVT = d->reqSrvVT;
+ else {
+ for (i = tvt = 0;;) {
+ if (serverVTs[i]) {
+ tvt = atoi( serverVTs[i++] );
+ volun = 0;
+ if (tvt < 0) {
+ tvt = -tvt;
+ volun = 1;
+ }
+ if (!tvt || tvt >= 16)
+ continue;
+ } else {
+ if (++tvt >= 16)
+ break;
+ volun = 1;
+ }
+ for (cd = displays; cd; cd = cd->next) {
+ if (cd->reqSrvVT == tvt && /* protect from lusers */
+ (cd->status != zombie || cd->zstatus != DS_REMOVE))
+ goto next;
+ if (cd->serverVT == tvt) {
+ if (cd->status != zombie || cd->zstatus == DS_REMOTE)
+ goto next;
+ if (!cd->follower) {
+ d->serverVT = -1;
+ cd->follower = d;
+ return;
+ }
+ }
+ }
+ if (!volun || !((1 << tvt) & GetBusyVTs())) {
+ d->serverVT = tvt;
+ return;
+ }
+ next: ;
+ }
+ }
+ }
+}
+#endif
+
+static void
+StartDisplays( void )
+{
+ ForEachDisplay( CheckDisplayStatus );
+ CloseGetter();
+#ifdef HAVE_VTS
+ active_vts = -1;
+ ForEachDisplayRev( AllocateVT );
+#endif
+ ForEachDisplay( KickDisplay );
+}
+
+void
+StartDisplay( struct display *d )
+{
+ if (Stopping) {
+ Debug( "stopping display %s because shutdown is scheduled\n", d->name );
+ StopDisplay( d );
+ return;
+ }
+
+#ifdef HAVE_VTS
+ if (d->serverVT < 0)
+ return;
+#endif
+
+ d->status = running;
+ if ((d->displayType & d_location) == dLocal) {
+ Debug( "StartDisplay %s\n", d->name );
+ /* don't bother pinging local displays; we'll
+ * certainly notice when they exit
+ */
+ d->pingInterval = 0;
+ if (d->authorize) {
+ SetLocalAuthorization( d );
+ /*
+ * reset the server after writing the authorization information
+ * to make it read the file (for compatibility with old
+ * servers which read auth file only on reset instead of
+ * at first connection)
+ */
+ if (d->serverPid != -1 && d->resetForAuth && d->resetSignal)
+ kill( d->serverPid, d->resetSignal );
+ }
+ if (d->serverPid == -1) {
+ d->serverStatus = awaiting;
+ return;
+ }
+ } else {
+ Debug( "StartDisplay %s, try %d\n", d->name, d->startTries + 1 );
+ /* this will only happen when using XDMCP */
+ if (d->authorizations)
+ SaveServerAuthorizations( d, d->authorizations, d->authNum );
+ }
+ StartDisplayP2( d );
+}
+
+void
+StartDisplayP2( struct display *d )
+{
+ char *cname, *cgname;
+ int pid;
+
+ openCtrl( d );
+ Debug( "forking session\n" );
+ ASPrintf( &cname, "sub-daemon for display %s", d->name );
+ ASPrintf( &cgname, "greeter for display %s", d->name );
+ pid = GFork( &d->pipe, "master daemon", cname,
+ &d->gpipe, cgname );
+ switch (pid) {
+ case 0:
+ SetTitle( d->name );
+ if (debugLevel & DEBUG_WSESS)
+ sleep( 100 );
+ mstrtalk.pipe = &d->pipe;
+ (void)Signal( SIGPIPE, SIG_IGN );
+ SetAuthorization( d );
+ WaitForServer( d );
+ if ((d->displayType & d_location) == dLocal) {
+ GSet( &mstrtalk );
+ GSendInt( D_XConnOk );
+ }
+ ManageSession( d );
+ /* NOTREACHED */
+ case -1:
+ closeCtrl( d );
+ d->status = notRunning;
+ break;
+ default:
+ Debug( "forked session, pid %d\n", pid );
+
+ /* (void) fcntl (d->pipe.rfd, F_SETFL, O_NONBLOCK); */
+ /* (void) fcntl (d->gpipe.rfd, F_SETFL, O_NONBLOCK); */
+ RegisterInput( d->pipe.rfd );
+ RegisterInput( d->gpipe.rfd );
+
+ d->pid = pid;
+ d->hstent->lock = d->hstent->rLogin = d->hstent->goodExit =
+ d->hstent->sdRec.how = 0;
+ d->lastStart = now;
+ break;
+ }
+}
+
+/*
+ * transition from running to zombie, textmode, reserve or deleted
+ */
+
+static void
+rStopDisplay( struct display *d, int endState )
+{
+ Debug( "stopping display %s to state %d\n", d->name, endState );
+ AbortStartServer( d );
+ d->idleTimeout = 0;
+ if (d->serverPid != -1 || d->pid != -1) {
+ if (d->pid != -1)
+ TerminateProcess( d->pid, SIGTERM );
+ if (d->serverPid != -1)
+ TerminateProcess( d->serverPid, d->termSignal );
+ d->status = zombie;
+ d->zstatus = endState & 0xff;
+ Debug( " zombiefied\n" );
+ } else if (endState == DS_TEXTMODE) {
+#ifdef HAVE_VTS
+ d->status = textMode;
+ CheckTTYMode();
+ } else if (endState == (DS_TEXTMODE | 0x100)) {
+ d->status = textMode;
+#else
+ SwitchToTty( d );
+#endif
+ } else if (endState == DS_RESERVE)
+ d->status = reserve;
+#ifdef XDMCP
+ else if (endState == DS_REMOTE)
+ StartRemoteLogin( d );
+#endif
+ else {
+#ifndef HAVE_VTS
+ SwitchToX( d );
+#endif
+ RemoveDisplay( d );
+ }
+}
+
+void
+StopDisplay( struct display *d )
+{
+ rStopDisplay( d, DS_REMOVE );
+}
+
+static void
+ExitDisplay(
+ struct display *d,
+ int endState,
+ int serverCmd,
+ int goodExit )
+{
+ struct disphist *he;
+
+ if (d->status == raiser) {
+ serverCmd = XS_KEEP;
+ goodExit = TRUE;
+ }
+
+ Debug( "ExitDisplay %s, "
+ "endState = %d, serverCmd = %d, GoodExit = %d\n",
+ d->name, endState, serverCmd, goodExit );
+
+ d->userSess = -1;
+ if (d->userName)
+ free( d->userName );
+ d->userName = 0;
+ if (d->sessName)
+ free( d->sessName );
+ d->sessName = 0;
+ he = d->hstent;
+ he->lastExit = now;
+ he->goodExit = goodExit;
+ if (he->sdRec.how) {
+ if (he->sdRec.force == SHUT_ASK &&
+ (AnyActiveDisplays() || d->allowShutdown == SHUT_ROOT))
+ {
+ endState = DS_RESTART;
+ } else {
+ if (!sdRec.how || sdRec.force != SHUT_FORCE ||
+ !((d->allowNuke == SHUT_NONE && sdRec.uid != he->sdRec.uid) ||
+ (d->allowNuke == SHUT_ROOT && he->sdRec.uid)))
+ {
+ if (sdRec.osname)
+ free( sdRec.osname );
+ sdRec = he->sdRec;
+ if (now < sdRec.timeout || wouldShutdown())
+ endState = DS_REMOVE;
+ } else if (he->sdRec.osname)
+ free( he->sdRec.osname );
+ he->sdRec.how = 0;
+ he->sdRec.osname = 0;
+ }
+ }
+ if (d->status == zombie)
+ rStopDisplay( d, d->zstatus );
+ else {
+ if (Stopping) {
+ StopDisplay( d );
+ return;
+ }
+ if (endState != DS_RESTART ||
+ (d->displayType & d_origin) != dFromFile)
+ {
+ rStopDisplay( d, endState );
+ } else {
+ if (serverCmd == XS_RETRY) {
+ if ((d->displayType & d_location) == dLocal) {
+ if (he->lastExit - d->lastStart < 120) {
+ LogError( "Unable to fire up local display %s;"
+ " disabling.\n", d->name );
+ StopDisplay( d );
+ return;
+ }
+ } else {
+ if (++d->startTries > d->startAttempts) {
+ LogError( "Disabling foreign display %s"
+ " (too many attempts)\n", d->name );
+ StopDisplay( d );
+ return;
+ }
+ }
+ } else
+ d->startTries = 0;
+ if (d->serverPid != -1 &&
+ (serverCmd != XS_KEEP || d->terminateServer))
+ {
+ Debug( "killing X server for %s\n", d->name );
+ TerminateProcess( d->serverPid, d->termSignal );
+ d->status = phoenix;
+ } else
+ d->status = notRunning;
+ }
+ }
+}
+
+
+static int pidFd;
+static FILE *pidFilePtr;
+
+static int
+StorePid( void )
+{
+ int oldpid;
+
+ if (pidFile[0] != '\0') {
+ pidFd = open( pidFile, O_RDWR );
+ if (pidFd == -1 && errno == ENOENT)
+ pidFd = open( pidFile, O_RDWR|O_CREAT, 0666 );
+ if (pidFd == -1 || !(pidFilePtr = fdopen( pidFd, "r+" ))) {
+ LogError( "process-id file %s cannot be opened\n",
+ pidFile );
+ return -1;
+ }
+ if (fscanf( pidFilePtr, "%d\n", &oldpid ) != 1)
+ oldpid = -1;
+ fseek( pidFilePtr, 0l, 0 );
+ if (lockPidFile) {
+#ifdef F_SETLK
+# ifndef SEEK_SET
+# define SEEK_SET 0
+# endif
+ struct flock lock_data;
+ lock_data.l_type = F_WRLCK;
+ lock_data.l_whence = SEEK_SET;
+ lock_data.l_start = lock_data.l_len = 0;
+ if (fcntl( pidFd, F_SETLK, &lock_data ) == -1) {
+ if (errno == EAGAIN)
+ return oldpid;
+ else
+ return -1;
+ }
+#else
+# ifdef LOCK_EX
+ if (flock( pidFd, LOCK_EX|LOCK_NB ) == -1) {
+ if (errno == EWOULDBLOCK)
+ return oldpid;
+ else
+ return -1;
+ }
+# else
+ if (lockf( pidFd, F_TLOCK, 0 ) == -1) {
+ if (errno == EACCES)
+ return oldpid;
+ else
+ return -1;
+ }
+# endif
+#endif
+ }
+ fprintf( pidFilePtr, "%ld\n", (long)getpid() );
+ (void)fflush( pidFilePtr );
+ RegisterCloseOnFork( pidFd );
+ }
+ return 0;
+}
+
+#if 0
+void
+UnlockPidFile( void )
+{
+ if (lockPidFile)
+# ifdef F_SETLK
+ {
+ struct flock lock_data;
+ lock_data.l_type = F_UNLCK;
+ lock_data.l_whence = SEEK_SET;
+ lock_data.l_start = lock_data.l_len = 0;
+ (void)fcntl( pidFd, F_SETLK, &lock_data );
+ }
+# else
+# ifdef F_ULOCK
+ lockf( pidFd, F_ULOCK, 0 );
+# else
+ flock( pidFd, LOCK_UN );
+# endif
+# endif
+ close( pidFd );
+ fclose( pidFilePtr );
+}
+#endif
+
+void
+SetTitle( const char *name )
+{
+#if !defined(HAVE_SETPROCTITLE) && !defined(NOXDMTITLE)
+ char *p;
+ int left;
+#endif
+
+ ASPrintf( &prog, "%s: %s", prog, name );
+ ReInitErrorLog();
+#ifdef HAVE_SETPROCTITLE
+ setproctitle( "%s", name );
+#elif !defined(NOXDMTITLE)
+ p = Title;
+ left = TitleLen;
+
+ *p++ = '-';
+ --left;
+ while (*name && left > 0) {
+ *p++ = *name++;
+ --left;
+ }
+ while (left > 0) {
+ *p++ = '\0';
+ --left;
+ }
+#endif
+}
diff --git a/kdm/backend/dm.h b/kdm/backend/dm.h
new file mode 100644
index 000000000..6d2f59768
--- /dev/null
+++ b/kdm/backend/dm.h
@@ -0,0 +1,620 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2000-2005 Oswald Buddenhagen <ossi@kde.org>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the copyright holder.
+
+*/
+
+/*
+ * xdm - display manager daemon
+ * Author: Keith Packard, MIT X Consortium
+ *
+ * global xdm core declarations
+ */
+
+#ifndef _DM_H_
+#define _DM_H_ 1
+
+#include "greet.h"
+#include <config.ci>
+
+#include <X11/X.h> /* FamilyInternet6 */
+#include <X11/Xos.h>
+#include <X11/Xfuncs.h>
+#include <X11/Xmd.h>
+#include <X11/Xauth.h>
+#include <X11/Intrinsic.h>
+
+#include <sys/param.h>
+#ifdef HAVE_LIMITS_H
+# include <limits.h>
+#endif
+
+#include <time.h>
+#define Time_t time_t
+
+#include <stdlib.h>
+#include <errno.h>
+
+#ifdef XDMCP
+# if defined(__osf__)
+/* someone somewhere defines QUERY under Tru64 which confuses Xdmcp.h */
+# undef QUERY
+# endif
+# include <X11/Xdmcp.h>
+#endif
+
+#ifndef PATH_MAX
+# ifdef MAXPATHLEN
+# define PATH_MAX MAXPATHLEN
+# else
+# define PATH_MAX 1024
+# endif
+#endif
+
+#include <sys/wait.h>
+#define waitCode(w) (WIFEXITED(w) ? WEXITSTATUS(w) : 0)
+#define waitSig(w) (WIFSIGNALED(w) ? WTERMSIG(w) : 0)
+#ifdef WCOREDUMP
+# define waitCore(w) (WCOREDUMP(w))
+#else
+# define waitCore(w) 0 /* not in POSIX. so what? */
+#endif
+typedef int waitType;
+
+#define waitCompose(sig,core,code) ((sig) * 256 + (core) * 128 + (code))
+#define waitVal(w) waitCompose(waitSig(w), waitCore(w), waitCode(w))
+#define WaitCode(w) ((w) & 0x7f)
+#define WaitCore(w) (((w) >> 7) & 1)
+#define WaitSig(w) (((w) >> 8) & 0xff)
+
+#include <sys/time.h>
+#define FD_TYPE fd_set
+
+#include <setjmp.h>
+#if defined(__EMX__) || (defined(__NetBSD__) && defined(__sparc__)) /* XXX netbsd? */
+# define Setjmp(e) setjmp(e)
+# define Longjmp(e,v) longjmp(e,v)
+# define Jmp_buf jmp_buf
+#else
+# define Setjmp(e) sigsetjmp(e,1)
+# define Longjmp(e,v) siglongjmp(e,v)
+# define Jmp_buf sigjmp_buf
+#endif
+
+#include <utmp.h>
+#ifdef HAVE_UTMPX
+# include <utmpx.h>
+# define STRUCTUTMP struct utmpx
+# define UTMPNAME utmpxname
+# define SETUTENT setutxent
+# define GETUTENT getutxent
+# define PUTUTLINE pututxline
+# define ENDUTENT endutxent
+# define LASTLOG lastlogx
+# define ut_time ut_tv.tv_sec
+# define ll_time ll_tv.tv_sec
+#else
+# define STRUCTUTMP struct utmp
+# define UTMPNAME utmpname
+# define SETUTENT setutent
+# define GETUTENT getutent
+# define PUTUTLINE pututline
+# define ENDUTENT endutent
+# define LASTLOG lastlog
+#endif
+#ifndef HAVE_STRUCT_UTMP_UT_USER
+# define ut_user ut_name
+#endif
+#ifndef WTMP_FILE
+# ifdef _PATH_WTMPX
+# define WTMP_FILE _PATH_WTMPX
+# elif defined(_PATH_WTMP)
+# define WTMP_FILE _PATH_WTMP
+# else
+# define WTMP_FILE "/usr/adm/wtmp"
+# endif
+#endif
+#ifndef UTMP_FILE
+# ifdef _PATH_UTMPX
+# define UTMP_FILE _PATH_UTMPX
+# elif defined(_PATH_UTMP)
+# define UTMP_FILE _PATH_UTMP
+# else
+# define UTMP_FILE "/etc/utmp"
+# endif
+#endif
+
+#ifdef HAVE_NETCONFIG_H
+# define STREAMSCONN
+#else
+# define UNIXCONN
+# define TCPCONN
+# ifdef FamilyInternet6
+# define IPv6
+# endif
+# ifdef HAVE_NETDNET_DN_H
+# define DNETCONN
+# endif
+#endif
+
+#if !defined(HAVE_ARC4RANDOM) && !defined(DEV_RANDOM)
+# define NEED_ENTROPY
+#endif
+
+typedef struct GPipe {
+ int wfd, rfd;
+ char *who;
+} GPipe;
+
+typedef struct GTalk {
+ GPipe *pipe;
+ Jmp_buf errjmp;
+} GTalk;
+
+typedef struct GProc {
+ GPipe pipe;
+ int pid;
+} GProc;
+
+typedef enum displayStatus { notRunning = 0, running, zombie, phoenix, raiser,
+ textMode, reserve, remoteLogin } DisplayStatus;
+
+typedef enum serverStatus { ignore = 0, awaiting, starting,
+ terminated, killed, pausing } ServerStatus;
+
+typedef struct RcStr {
+ struct RcStr *next;
+ char *str;
+ int cnt;
+} RcStr;
+
+typedef struct CfgDep {
+ RcStr *name;
+ long time;
+} CfgDep;
+
+typedef struct CfgArr {
+ char *data; /* config value array; allocated */
+ long *idx; /* config index array; alias */
+ CfgDep dep; /* filestamp */
+ int numCfgEnt; /* number of config entries */
+} CfgArr;
+
+struct bsock {
+ int fd;
+ int buflen;
+ char *buffer;
+};
+
+struct cmdsock {
+ struct cmdsock *next;
+ struct bsock sock; /* buffered fd of the socket */
+};
+
+typedef struct {
+ struct cmdsock *css; /* open connections */
+
+ char *path; /* filename of the socket */
+ int fd; /* fd of the socket */
+ int gid; /* owner group of the socket */
+
+ char *fpath; /* filename of the fifo */
+ struct bsock fifo; /* buffered fd of the fifo */
+} CtrlRec;
+
+struct display {
+ struct display *next;
+ struct disphist *hstent; /* display history entry */
+
+ /* basic display information */
+ char *name; /* DISPLAY name -- also referenced in hstent */
+ char *class2; /* display class (may be NULL) */
+ int displayType; /* location/origin/lifetime */
+ CfgArr cfg; /* config data array */
+
+ /* display state */
+ DisplayStatus status; /* current status */
+ int zstatus; /* substatus while zombie */
+ int pid; /* process id of child */
+ int serverPid; /* process id of server (-1 if none) */
+#ifdef HAVE_VTS
+ int serverVT; /* server VT (0 = none, -1 = pending) */
+ struct display *follower; /* on exit, hand VT to this display */
+#endif
+ ServerStatus serverStatus; /* X server startup state */
+ Time_t lastStart; /* time of last display start */
+ int startTries; /* current start try */
+ int stillThere; /* state during HUP processing */
+ int userSess; /* -1=nobody, otherwise uid */
+ char *userName;
+ char *sessName;
+ CtrlRec ctrl; /* command socket & fifo */
+ GPipe pipe; /* comm master <-> slave */
+ GPipe gpipe; /* comm master <-> greeter */
+#ifdef XDMCP
+ char *remoteHost; /* for X -query type remote login */
+ /* XDMCP state */
+ unsigned sessionID; /* ID of active session */
+ ARRAY8 peer; /* display peer address */
+ ARRAY8 from; /* XDMCP port of display */
+ unsigned displayNumber; /* numerical part of name */
+ int useChooser; /* Run the chooser for this display */
+ ARRAY8 clientAddr; /* for chooser picking */
+ unsigned connectionType; /* ... */
+ int xdmcpFd;
+#endif
+
+ CONF_CORE_LOCAL_DEFS
+
+ int idleTimeout; /* abort login after that time */
+
+ unsigned short *authNameLens; /* authorization protocol name lens */
+
+ /* information potentially derived from resources */
+ int authNameNum; /* number of protocol names */
+ Xauth **authorizations; /* authorization data */
+ int authNum; /* number of authorizations */
+ char *authFile; /* file to store authorization in */
+};
+
+typedef struct {
+ unsigned how:2, /* 0=none 1=reboot 2=halt (SHUT_*) */
+ force:2;
+ int uid;
+ int start;
+ int timeout;
+ char *osname;
+ time_t bmstamp;
+ int osindex;
+} SdRec;
+
+struct disphist {
+ struct disphist *next;
+ char *name;
+ Time_t lastExit; /* time of last display exit */
+ unsigned rLogin:2, /* 0=nothing 1=relogin 2=login */
+ lock:1, /* screen locker running */
+ goodExit:1; /* was the last exit "peaceful"? */
+ SdRec sdRec;
+ char *nuser, *npass, *nargs;
+};
+
+#ifdef XDMCP
+
+#define PROTO_TIMEOUT (30 * 60) /* 30 minutes should be long enough */
+
+struct protoDisplay {
+ struct protoDisplay *next;
+ XdmcpNetaddr address; /* UDP address */
+ int addrlen; /* UDP address length */
+ unsigned long date; /* creation date */
+ CARD16 displayNumber;
+ CARD16 connectionType;
+ ARRAY8 connectionAddress;
+ CARD32 sessionID;
+ Xauth *fileAuthorization;
+ Xauth *xdmcpAuthorization;
+ ARRAY8 authenticationName;
+ ARRAY8 authenticationData;
+ XdmAuthKeyRec key;
+};
+#endif /* XDMCP */
+
+/* status code for RStopDisplay */
+#define DS_RESTART 0
+#define DS_TEXTMODE 1
+#define DS_RESERVE 2
+#define DS_REMOTE 3
+#define DS_REMOVE 4
+
+/* command codes dpy process -> master process */
+#define D_User 1
+#define D_ReLogin 2
+#define D_ChooseHost 4
+#define D_RemoteHost 5
+#define D_XConnOk 6
+
+extern int debugLevel;
+
+CONF_CORE_GLOBAL_DECLS
+
+/* in daemon.c */
+void BecomeDaemon( void );
+
+/* in dm.c */
+extern char *prog, *progpath;
+extern time_t now;
+extern SdRec sdRec;
+void StartDisplay( struct display *d );
+void StartDisplayP2( struct display *d );
+void StopDisplay( struct display *d );
+void SetTitle( const char *name );
+void SwitchToX( struct display *d );
+void setNLogin( struct display *d,
+ const char *nuser, const char *npass, char *nargs,
+ int rl );
+void cancelShutdown( void );
+int TTYtoVT( const char *tty );
+int activateVT( int vt );
+
+/* in ctrl.c */
+void openCtrl( struct display *d );
+void closeCtrl( struct display *d );
+int handleCtrl( FD_TYPE *reads, struct display *d );
+void chownCtrl( CtrlRec *cr, int uid );
+void updateCtrl( void );
+
+/* in dpylist.c */
+extern struct display *displays; /* that's ugly ... */
+int AnyDisplaysLeft( void );
+void ForEachDisplay( void (*f)( struct display * ) );
+#ifdef HAVE_VTS
+void ForEachDisplayRev( void (*f)( struct display * ) );
+#endif
+void RemoveDisplay( struct display *old );
+struct display
+ *FindDisplayByName( const char *name ),
+#ifdef XDMCP
+ *FindDisplayBySessionID( CARD32 sessionID ),
+ *FindDisplayByAddress( XdmcpNetaddr addr, int addrlen, CARD16 displayNumber ),
+#endif /* XDMCP */
+ *FindDisplayByPid( int pid ),
+ *FindDisplayByServerPid( int serverPid ),
+ *NewDisplay( const char *name );
+int AnyActiveDisplays( void );
+int AnyRunningDisplays( void );
+int AnyReserveDisplays( void );
+int idleReserveDisplays( void );
+int AllLocalDisplaysLocked( struct display *dp );
+int StartReserveDisplay( int lt );
+void ReapReserveDisplays( void );
+
+/* in reset.c */
+void pseudoReset( void );
+
+/* in resource.c */
+char **FindCfgEnt( struct display *d, int id );
+int InitResources( char **argv );
+int LoadDMResources( int force );
+int LoadDisplayResources( struct display *d );
+void ScanServers( void );
+void CloseGetter( void );
+int startConfig( int what, CfgDep *dep, int force );
+RcStr *newStr( char *str );
+void delStr( RcStr *str );
+extern GTalk cnftalk;
+
+/* in session.c */
+extern struct display *td;
+extern const char *td_setup;
+char **baseEnv( const char *user );
+char **inheritEnv( char **env, const char **what );
+char **systemEnv( const char *user );
+int source( char **env, const char *file, const char *arg );
+void ManageSession( struct display *d );
+
+extern GTalk mstrtalk, grttalk;
+extern GProc grtproc;
+void OpenGreeter( void );
+int CloseGreeter( int force );
+int CtrlGreeterWait( int wreply );
+void PrepErrorGreet( void );
+char *conv_interact( int what, const char *prompt );
+
+/* process.c */
+typedef void (*SIGFUNC)( int );
+SIGFUNC Signal( int, SIGFUNC Handler );
+
+void RegisterInput( int fd );
+void UnregisterInput( int fd );
+void RegisterCloseOnFork( int fd );
+void ClearCloseOnFork( int fd );
+void CloseNClearCloseOnFork( int fd );
+int Fork( void );
+int Wait4( int pid );
+void execute( char **argv, char **env );
+int runAndWait( char **args, char **env );
+FILE *pOpen( char **what, char m, int *pid );
+int pClose( FILE *f, int pid );
+char *locate( const char *exe );
+void TerminateProcess( int pid, int sig );
+
+void GSet( GTalk *talk); /* call before GOpen! */
+int GFork( GPipe *pajp, const char *pname, char *cname,
+ GPipe *ogp, char *cgname );
+void GClosen( GPipe *pajp );
+int GOpen( GProc *proc,
+ char **argv, const char *what, char **env, char *cname,
+ GPipe *gp );
+int GClose( GProc *proc, GPipe *gp, int force );
+
+void GSendInt( int val );
+int GRecvInt( void );
+int GRecvCmd( int *cmd );
+void GSendArr( int len, const char *data );
+char *GRecvArr( int *len );
+int GRecvStrBuf( char *buf );
+int GRecvArrBuf( char *buf );
+void GSendStr( const char *buf );
+void GSendNStr( const char *buf, int len ); /* exact len, buf != 0 */
+void GSendStrN( const char *buf, int len ); /* maximal len */
+char *GRecvStr( void );
+void GSendArgv( char **argv );
+void GSendStrArr( int len, char **data );
+char **GRecvStrArr( int *len );
+char **GRecvArgv( void );
+
+/* client.c */
+#define GCONV_NORMAL 0
+#define GCONV_HIDDEN 1
+#define GCONV_USER 2
+#define GCONV_PASS 3
+#define GCONV_PASS_ND 4
+#define GCONV_BINARY 5
+typedef char *(*GConvFunc)( int what, const char *prompt );
+int Verify( GConvFunc gconv, int rootok );
+int StartClient( void );
+void SessionExit( int status ) ATTR_NORETURN;
+int ReadDmrc( void );
+extern char **userEnviron, **systemEnviron;
+extern char *curuser, *curpass, *curtype, *newpass,
+ *dmrcuser, *curdmrc, *newdmrc;
+extern int cursource;
+#define PWSRC_MANUAL 0
+#define PWSRC_AUTOLOGIN 1
+#define PWSRC_RELOGIN 2
+
+/* server.c */
+char **PrepServerArgv( struct display *d, const char *args );
+void StartServer( struct display *d );
+void AbortStartServer( struct display *d );
+void StartServerSuccess( void );
+void StartServerFailed( void );
+void StartServerTimeout( void );
+extern struct display *startingServer;
+extern time_t serverTimeout;
+
+void WaitForServer( struct display *d );
+void ResetServer( struct display *d );
+int PingServer(struct display *d );
+extern Display *dpy;
+
+/* in util.c */
+void *Calloc( size_t nmemb, size_t size );
+void *Malloc( size_t size );
+void *Realloc( void *ptr, size_t size );
+void WipeStr( char *str );
+int StrCmp( const char *s1, const char *s2 );
+#ifdef HAVE_STRNLEN
+# define StrNLen(s, m) strnlen(s, m)
+#else
+int StrNLen( const char *s, int max );
+#endif
+int StrNDup( char **dst, const char *src, int len );
+int StrDup( char **dst, const char *src );
+int arrLen( char **arr );
+void freeStrArr( char **arr );
+char **initStrArr( char **arr );
+char **xCopyStrArr( int rn, char **arr );
+/* Note: the following functions free the old data even in case of failure */
+int ReStrN( char **dst, const char *src, int len );
+int ReStr( char **dst, const char *src );
+int StrApp( char **dst, ... );
+char **addStrArr( char **arr, const char *str, int len );
+char **parseArgs( char **argv, const char *string );
+/* End note */
+char **setEnv( char **e, const char *name, const char *value );
+char **putEnv( const char *string, char **env );
+const char *getEnv( char **e, const char *name );
+const char *localHostname( void );
+int Reader( int fd, void *buf, int len );
+int Writer( int fd, const void *buf, int len );
+int fGets( char *buf, int max, FILE *f );
+time_t mTime( const char *fn );
+void ListSessions( int flags, struct display *d, void *ctx,
+ void (*emitXSess)( struct display *, struct display *, void * ),
+ void (*emitTTYSess)( STRUCTUTMP *, struct display *, void * ) );
+
+/* in inifile.c */
+char *iniLoad( const char *fname );
+int iniSave( const char *data, const char *fname );
+char *iniEntry( char *data, const char *section, const char *key, const char *value );
+char *iniMerge( char *data, const char *newdata );
+
+/* in bootman.c */
+int getBootOptions( char ***opts, int *def, int *cur );
+int setBootOption( const char *opt, SdRec *sdr );
+void commitBootOption( void );
+
+/* in netaddr.c */
+char *NetaddrAddress( char *netaddrp, int *lenp );
+char *NetaddrPort( char *netaddrp, int *lenp );
+int ConvertAddr( char *saddr, int *len, char **addr );
+int NetaddrFamily( char *netaddrp );
+int addressEqual( char *a1, int len1, char *a2, int len2 );
+
+#ifdef XDMCP
+
+/* in xdmcp.c */
+char *NetworkAddressToHostname( CARD16 connectionType, ARRAY8Ptr connectionAddress );
+void SendFailed( struct display *d, const char *reason );
+void init_session_id( void );
+
+/* in policy.c */
+struct sockaddr;
+ARRAY8Ptr Accept( struct sockaddr *from, int fromlen, CARD16 displayNumber );
+ARRAY8Ptr ChooseAuthentication( ARRAYofARRAY8Ptr authenticationNames );
+int CheckAuthentication( struct protoDisplay *pdpy, ARRAY8Ptr displayID, ARRAY8Ptr name, ARRAY8Ptr data );
+int SelectAuthorizationTypeIndex( ARRAY8Ptr authenticationName, ARRAYofARRAY8Ptr authorizationNames );
+int SelectConnectionTypeIndex( ARRAY16Ptr connectionTypes, ARRAYofARRAY8Ptr connectionAddresses );
+int Willing( ARRAY8Ptr addr, CARD16 connectionType, ARRAY8Ptr authenticationName, ARRAY8Ptr status, xdmOpCode type );
+
+/* in protodpy.c */
+void DisposeProtoDisplay( struct protoDisplay *pdpy );
+
+struct protoDisplay *FindProtoDisplay( XdmcpNetaddr address, int addrlen,
+ CARD16 displayNumber );
+struct protoDisplay *NewProtoDisplay( XdmcpNetaddr address, int addrlen,
+ CARD16 displayNumber,
+ CARD16 connectionType,
+ ARRAY8Ptr connectionAddress,
+ CARD32 sessionID );
+
+#define FamilyBroadcast 0xffff
+typedef void (*ChooserFunc)( CARD16 connectionType, ARRAY8Ptr addr, char *closure );
+typedef void (*ListenFunc)( ARRAY8Ptr addr, void **closure );
+
+/* in access.c */
+ARRAY8Ptr getLocalAddress( void );
+int AcceptableDisplayAddress( ARRAY8Ptr clientAddress, CARD16 connectionType, xdmOpCode type );
+int ForEachMatchingIndirectHost( ARRAY8Ptr clientAddress, CARD16 connectionType, ChooserFunc function, char *closure );
+void ScanAccessDatabase( int force );
+int UseChooser( ARRAY8Ptr clientAddress, CARD16 connectionType );
+void ForEachChooserHost( ARRAY8Ptr clientAddress, CARD16 connectionType, ChooserFunc function, char *closure );
+void ForEachListenAddr( ListenFunc listenfunction, ListenFunc mcastfcuntion, void **closure );
+
+/* in choose.c */
+ARRAY8Ptr IndirectChoice( ARRAY8Ptr clientAddress, CARD16 connectionType );
+int IsIndirectClient( ARRAY8Ptr clientAddress, CARD16 connectionType );
+int RememberIndirectClient( ARRAY8Ptr clientAddress, CARD16 connectionType );
+void ForgetIndirectClient( ARRAY8Ptr clientAddress, CARD16 connectionType );
+int RegisterIndirectChoice( ARRAY8Ptr clientAddress, CARD16 connectionType, ARRAY8Ptr choice );
+int DoChoose( void );
+
+/* socket.c or streams.c */
+void UpdateListenSockets( void );
+int AnyListenSockets( void );
+int ProcessListenSockets( FD_TYPE *reads );
+
+/* in xdmcp.c */
+void ProcessRequestSocket( int fd );
+
+#endif /* XDMCP */
+
+/* in sessreg.c */
+void sessreg( struct display *d, int pid, const char *user, int uid );
+
+#endif /* _DM_H_ */
diff --git a/kdm/backend/dm_auth.h b/kdm/backend/dm_auth.h
new file mode 100644
index 000000000..ccf4697b8
--- /dev/null
+++ b/kdm/backend/dm_auth.h
@@ -0,0 +1,99 @@
+/************************************************************
+
+Copyright 1998 by Thomas E. Dickey <dickey@clark.net>
+
+ All Rights Reserved
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name(s) of the above copyright
+holders shall not be used in advertising or otherwise to promote the
+sale, use or other dealings in this Software without prior written
+authorization.
+
+********************************************************/
+
+#ifndef _DM_AUTH_H_
+#define _DM_AUTH_H_ 1
+
+#include "dm.h"
+
+void MitInitAuth( unsigned short name_len, const char *name );
+Xauth *MitGetAuth( unsigned short namelen, const char *name );
+
+#ifdef HASXDMAUTH
+void XdmInitAuth( unsigned short name_len, const char *name );
+Xauth *XdmGetAuth( unsigned short namelen, const char *name );
+# ifdef XDMCP
+void XdmGetXdmcpAuth( struct protoDisplay *pdpy,
+ unsigned short authorizationNameLen,
+ const char *authorizationName );
+int XdmCheckAuthentication( struct protoDisplay *pdpy,
+ ARRAY8Ptr displayID,
+ ARRAY8Ptr authenticationName,
+ ARRAY8Ptr authenticationData );
+# else
+# define XdmGetXdmcpAuth NULL
+# endif
+#endif
+
+#ifdef SECURE_RPC
+void SecureRPCInitAuth( unsigned short name_len, const char *name );
+Xauth *SecureRPCGetAuth( unsigned short name_len, const char *name );
+#endif
+
+#ifdef K5AUTH
+void Krb5InitAuth( unsigned short name_len, const char *name );
+Xauth *Krb5GetAuth( unsigned short name_len, const char *name );
+
+Xauth *Krb5GetAuthFor( unsigned short name_len, const char *name, const char *dname );
+char *Krb5Init( const char *user, const char *passwd, const char *dname );
+void Krb5Destroy( const char *dname );
+#endif
+
+/* auth.c */
+int ValidAuthorization( unsigned short name_length, const char *name );
+
+
+#ifdef XDMCP
+
+void
+SetProtoDisplayAuthorization( struct protoDisplay *pdpy,
+ unsigned short authorizationNameLen,
+ const char *authorizationName );
+
+#endif /* XDMCP */
+
+int SaveServerAuthorizations( struct display *d, Xauth **auths, int count );
+void CleanUpFileName( const char *src, char *dst, int len );
+void RemoveUserAuthorization( struct display *d );
+void SetAuthorization( struct display *d );
+void SetLocalAuthorization( struct display *d );
+void SetUserAuthorization( struct display *d );
+
+/* genauth.c */
+int GenerateAuthData( char *auth, int len );
+#ifdef NEED_ENTROPY
+void AddPreGetEntropy( void );
+void AddOtherEntropy( void );
+void AddTimerEntropy( void );
+#endif
+
+#endif /* _DM_AUTH_H_ */
diff --git a/kdm/backend/dm_error.h b/kdm/backend/dm_error.h
new file mode 100644
index 000000000..3570c18fc
--- /dev/null
+++ b/kdm/backend/dm_error.h
@@ -0,0 +1,58 @@
+/************************************************************
+
+Copyright 1998 by Thomas E. Dickey <dickey@clark.net>
+
+ All Rights Reserved
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name(s) of the above copyright
+holders shall not be used in advertising or otherwise to promote the
+sale, use or other dealings in this Software without prior written
+authorization.
+
+********************************************************/
+
+
+#ifndef _DM_ERROR_H_
+#define _DM_ERROR_H_ 1
+
+#include "greet.h"
+
+#include <stdarg.h>
+
+void GDebug( const char *fmt, ... );
+void Debug( const char *fmt, ... );
+void LogInfo( const char *fmt, ... );
+void LogWarn( const char *fmt, ... );
+void LogError( const char *fmt, ... );
+void LogPanic( const char *fmt, ... ) ATTR_NORETURN;
+void LogOutOfMem( void );
+void Panic( const char *mesg ) ATTR_NORETURN;
+void InitErrorLog( const char *errorLogFile );
+#ifdef USE_SYSLOG
+void ReInitErrorLog( void );
+#else
+# define ReInitErrorLog() while(0)
+#endif
+int ASPrintf( char **strp, const char *fmt, ... );
+int VASPrintf( char **strp, const char *fmt, va_list args );
+
+#endif /* _DM_ERROR_H_ */
diff --git a/kdm/backend/dm_socket.h b/kdm/backend/dm_socket.h
new file mode 100644
index 000000000..56a39fd0e
--- /dev/null
+++ b/kdm/backend/dm_socket.h
@@ -0,0 +1,72 @@
+/************************************************************
+
+Copyright 1998 by Thomas E. Dickey <dickey@clark.net>
+Copyright 2002-2004 Oswald Buddenhagen <ossi@kde.org>
+
+ All Rights Reserved
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name(s) of the above copyright
+holders shall not be used in advertising or otherwise to promote the
+sale, use or other dealings in this Software without prior written
+authorization.
+
+********************************************************/
+
+#ifndef _DM_SOCKET_H_
+#define _DM_SOCKET_H_ 1
+
+#ifndef __Lynx__
+# include <sys/socket.h>
+#else
+# include <socket.h>
+#endif
+
+#ifdef TCPCONN
+# include <netinet/in.h>
+#endif
+
+#ifdef UNIXCONN
+# ifndef __Lynx__
+# include <sys/un.h>
+# else
+# include <un.h>
+# endif
+#endif
+
+#ifdef DNETCONN
+# include <netdnet/dn.h>
+#endif
+
+#if (defined(__svr4__) && !defined(__sun__)) && defined(SIOCGIFCONF)
+# define SYSV_SIOCGIFCONF
+int ifioctl( int fd, int cmd, char *arg );
+#else
+# define ifioctl ioctl
+#endif
+
+#ifdef BSD
+# if (BSD >= 199103)
+# define VARIABLE_IFREQ
+# endif
+#endif
+
+#endif /* _DM_SOCKET_H_ */
diff --git a/kdm/backend/dpylist.c b/kdm/backend/dpylist.c
new file mode 100644
index 000000000..b512293f7
--- /dev/null
+++ b/kdm/backend/dpylist.c
@@ -0,0 +1,294 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2000-2005 Oswald Buddenhagen <ossi@kde.org>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the copyright holder.
+
+*/
+
+/*
+ * xdm - display manager daemon
+ * Author: Keith Packard, MIT X Consortium
+ *
+ * a simple linked list of known displays
+ */
+
+#include "dm.h"
+#include "dm_error.h"
+
+struct display *displays;
+static struct disphist *disphist;
+
+int
+AnyDisplaysLeft( void )
+{
+ return displays != (struct display *)0;
+}
+
+int
+AnyActiveDisplays( void )
+{
+ struct display *d;
+
+ for (d = displays; d; d = d->next)
+ if (d->status == remoteLogin || d->userSess >= 0)
+ return 1;
+ return 0;
+}
+
+int
+AnyRunningDisplays( void )
+{
+ struct display *d;
+
+ for (d = displays; d; d = d->next)
+ switch (d->status) {
+ case notRunning:
+ case textMode:
+ case reserve:
+ break;
+ default:
+ return 1;
+ }
+ return 0;
+}
+
+int
+AnyReserveDisplays( void )
+{
+ struct display *d;
+
+ for (d = displays; d; d = d->next)
+ if ((d->displayType & d_lifetime) == dReserve)
+ return 1;
+ return 0;
+}
+
+int
+idleReserveDisplays( void )
+{
+ struct display *d;
+ int cnt = 0;
+
+ for (d = displays; d; d = d->next)
+ if (d->status == reserve)
+ cnt++;
+ return cnt;
+}
+
+int
+StartReserveDisplay( int lt )
+{
+ struct display *d, *rd;
+
+ for (rd = 0, d = displays; d; d = d->next)
+ if (d->status == reserve)
+ rd = d;
+ if (rd) {
+ rd->idleTimeout = lt;
+ rd->status = notRunning;
+ return 1;
+ }
+ return 0;
+}
+
+void
+ForEachDisplay( void (*f)( struct display * ) )
+{
+ struct display *d, *next;
+
+ for (d = displays; d; d = next) {
+ next = d->next;
+ (*f)( d );
+ }
+}
+
+#ifdef HAVE_VTS
+static void
+_forEachDisplayRev( struct display *d, void (*f)( struct display * ) )
+{
+ if (d) {
+ if (d->next)
+ _forEachDisplayRev( d->next, f );
+ (*f)( d );
+ }
+}
+
+void
+ForEachDisplayRev( void (*f)( struct display * ) )
+{
+ _forEachDisplayRev( displays, f );
+}
+#endif
+
+struct display *
+FindDisplayByName( const char *name )
+{
+ struct display *d;
+
+ for (d = displays; d; d = d->next)
+ if (!strcmp( name, d->name ))
+ return d;
+ return 0;
+}
+
+struct display *
+FindDisplayByPid( int pid )
+{
+ struct display *d;
+
+ for (d = displays; d; d = d->next)
+ if (pid == d->pid)
+ return d;
+ return 0;
+}
+
+struct display *
+FindDisplayByServerPid( int serverPid )
+{
+ struct display *d;
+
+ for (d = displays; d; d = d->next)
+ if (serverPid == d->serverPid)
+ return d;
+ return 0;
+}
+
+#ifdef XDMCP
+
+struct display *
+FindDisplayBySessionID( CARD32 sessionID )
+{
+ struct display *d;
+
+ for (d = displays; d; d = d->next)
+ if (sessionID == d->sessionID)
+ return d;
+ return 0;
+}
+
+struct display *
+FindDisplayByAddress( XdmcpNetaddr addr, int addrlen, CARD16 displayNumber )
+{
+ struct display *d;
+
+ for (d = displays; d; d = d->next)
+ if ((d->displayType & d_origin) == dFromXDMCP &&
+ d->displayNumber == displayNumber &&
+ addressEqual( (XdmcpNetaddr)d->from.data, d->from.length,
+ addr, addrlen ))
+ return d;
+ return 0;
+}
+
+#endif /* XDMCP */
+
+#define IfFree(x) if (x) free( (char *)x )
+
+void
+RemoveDisplay( struct display *old )
+{
+ struct display *d, **dp;
+ int i;
+
+ for (dp = &displays; (d = *dp); dp = &(*dp)->next) {
+ if (d == old) {
+ Debug( "Removing display %s\n", d->name );
+ *dp = d->next;
+ IfFree( d->class2 );
+ IfFree( d->cfg.data );
+ delStr( d->cfg.dep.name );
+#ifdef XDMCP
+ IfFree( d->remoteHost );
+#endif
+ if (d->authorizations) {
+ for (i = 0; i < d->authNum; i++)
+ XauDisposeAuth( d->authorizations[i] );
+ free( (char *)d->authorizations );
+ }
+ if (d->authFile) {
+ (void)unlink( d->authFile );
+ free( d->authFile );
+ }
+ IfFree( d->authNameLens );
+#ifdef XDMCP
+ XdmcpDisposeARRAY8( &d->peer );
+ XdmcpDisposeARRAY8( &d->from );
+ XdmcpDisposeARRAY8( &d->clientAddr );
+#endif
+ free( (char *)d );
+ break;
+ }
+ }
+}
+
+static struct disphist *
+FindHist( const char *name )
+{
+ struct disphist *hstent;
+
+ for (hstent = disphist; hstent; hstent = hstent->next)
+ if (!strcmp( hstent->name, name ))
+ return hstent;
+ return 0;
+}
+
+struct display *
+NewDisplay( const char *name )
+{
+ struct display *d;
+ struct disphist *hstent;
+
+ if (!(hstent = FindHist( name ))) {
+ if (!(hstent = Calloc( 1, sizeof(*hstent) )))
+ return 0;
+ if (!StrDup( &hstent->name, name )) {
+ free( hstent );
+ return 0;
+ }
+ hstent->next = disphist; disphist = hstent;
+ }
+
+ if (!(d = (struct display *)Calloc( 1, sizeof(*d) )))
+ return 0;
+ d->next = displays;
+ d->hstent = hstent;
+ d->name = hstent->name;
+ /* initialize fields (others are 0) */
+ d->pid = -1;
+ d->serverPid = -1;
+ d->ctrl.fd = -1;
+ d->ctrl.fifo.fd = -1;
+ d->pipe.rfd = -1;
+ d->pipe.wfd = -1;
+ d->gpipe.rfd = -1;
+ d->gpipe.wfd = -1;
+ d->userSess = -1;
+#ifdef XDMCP
+ d->xdmcpFd = -1;
+#endif
+ displays = d;
+ Debug( "created new display %s\n", d->name );
+ return d;
+}
diff --git a/kdm/backend/error.c b/kdm/backend/error.c
new file mode 100644
index 000000000..0680ed3a5
--- /dev/null
+++ b/kdm/backend/error.c
@@ -0,0 +1,130 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2000-2004 Oswald Buddenhagen <ossi@kde.org>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the copyright holder.
+
+*/
+
+/*
+ * xdm - display manager daemon
+ * Author: Keith Packard, MIT X Consortium
+ *
+ * Log display manager errors to a file as
+ * we generally do not have a terminal to talk to
+ * or use syslog if it exists
+ */
+
+#include "dm.h"
+#include "dm_error.h"
+
+#include <unistd.h>
+#include <stdio.h>
+
+#define PRINT_QUOTES
+#define PRINT_ARRAYS
+#define LOG_DEBUG_MASK DEBUG_CORE
+#define LOG_PANIC_EXIT 1
+#define NEED_ASPRINTF
+#define STATIC
+#include "printf.c"
+
+void
+GDebug( const char *fmt, ... )
+{
+ va_list args;
+
+ if (debugLevel & DEBUG_HLPCON) {
+ va_start( args, fmt );
+ Logger( DM_DEBUG, fmt, args );
+ va_end( args );
+ }
+}
+
+void
+Panic( const char *mesg )
+{
+ int fd = open( "/dev/console", O_WRONLY );
+ write( fd, "xdm panic: ", 11 );
+ write( fd, mesg, strlen( mesg ) );
+ write( fd, "\n", 1 );
+#ifdef USE_SYSLOG
+ ReInitErrorLog();
+ syslog( LOG_ALERT, "%s", mesg );
+#endif
+ exit( 1 );
+}
+
+#ifdef USE_SYSLOG
+void
+ReInitErrorLog()
+{
+ if (!(debugLevel & DEBUG_NOSYSLOG))
+ InitLog();
+}
+#endif
+
+void
+InitErrorLog( const char *errorLogFile )
+{
+ int fd;
+ char buf[128];
+
+#ifdef USE_SYSLOG
+ ReInitErrorLog();
+#endif
+ /* We do this independently of using syslog, as we cannot redirect
+ * the output of external programs to syslog.
+ */
+ if (!errorLogFile || strcmp( errorLogFile, "-" )) {
+ if (!errorLogFile) {
+ sprintf( buf, "/var/log/%s.log", prog );
+ errorLogFile = buf;
+ }
+ if ((fd = open( errorLogFile, O_CREAT | O_APPEND | O_WRONLY, 0666 )) < 0)
+ LogError( "Cannot open log file %s\n", errorLogFile );
+ else {
+#ifdef USE_SYSLOG
+# ifdef USE_PAM
+# define PAMLOG " PAM logs messages related to authentication to authpriv.*."
+# else
+# define PAMLOG
+# endif
+# define WARNMSG \
+ "********************************************************************************\n" \
+ "Note that your system uses syslog. All of kdm's internally generated messages\n" \
+ "(i.e., not from libraries and external programs/scripts it uses) go to the\n" \
+ "daemon.* syslog facility; check your syslog configuration to find out to which\n" \
+ "file(s) it is logged." PAMLOG "\n" \
+ "********************************************************************************\n\n"
+ if (!lseek( fd, 0, SEEK_END ))
+ write( fd, WARNMSG, sizeof(WARNMSG) - 1 );
+#endif
+ dup2( fd, 1 );
+ close( fd );
+ dup2( 1, 2 );
+ }
+ }
+}
+
diff --git a/kdm/backend/genauth.c b/kdm/backend/genauth.c
new file mode 100644
index 000000000..2978851e1
--- /dev/null
+++ b/kdm/backend/genauth.c
@@ -0,0 +1,490 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2003-2004 Oswald Buddenhagen <ossi@kde.org>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the copyright holder.
+
+*/
+
+/*
+ * xdm - display manager daemon
+ * Author: Keith Packard, MIT X Consortium
+ */
+
+#include "dm.h"
+#include "dm_auth.h"
+#include "dm_error.h"
+
+#ifdef NEED_ENTROPY
+
+# include <signal.h>
+
+/* ####################################################################### */
+
+/*
+ * Copyright (c) 1995,1999 Theo de Raadt. All rights reserved.
+ * Copyright (c) 2001-2002 Damien Miller. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "dm_socket.h"
+
+#include <string.h>
+
+#ifndef INADDR_LOOPBACK
+# define INADDR_LOOPBACK 0x7F000001U
+#endif
+
+#ifndef offsetof
+# define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+
+static int
+getPrngdBytes( char *buf, int len,
+ unsigned short tcp_port, const char *socket_path )
+{
+ int fd, addr_len, rval, errors;
+ char msg[2];
+ struct sockaddr *addr;
+ struct sockaddr_in addr_in;
+ struct sockaddr_un addr_un;
+ int af;
+ SIGFUNC old_sigpipe;
+
+ if (tcp_port) {
+ memset( &addr_in, 0, sizeof(addr_in) );
+ af = addr_in.sin_family = AF_INET;
+ addr_in.sin_addr.s_addr = htonl( INADDR_LOOPBACK );
+ addr_in.sin_port = htons( tcp_port );
+ addr_len = sizeof(addr_in);
+ addr = (struct sockaddr *)&addr_in;
+ } else if (*socket_path) {
+ unsigned spl = strlen( socket_path );
+ if (spl >= sizeof(addr_un.sun_path)) {
+ LogError( "get_random_prngd: "
+ "Random pool path is too long\n" );
+ return -1;
+ }
+ af = addr_un.sun_family = AF_UNIX;
+ strncpy( addr_un.sun_path, socket_path,
+ sizeof(addr_un.sun_path) );
+ addr_len = offsetof( struct sockaddr_un, sun_path ) + spl + 1;
+ addr = (struct sockaddr *)&addr_un;
+ } else
+ return -1;
+
+ old_sigpipe = Signal( SIGPIPE, SIG_IGN );
+
+ errors = 0;
+ rval = -1;
+reopen:
+ if ((fd = socket( af, SOCK_STREAM, 0 )) < 0) {
+ LogError( "Couldn't create socket: %m\n" );
+ goto done;
+ }
+
+ if (connect( fd, (struct sockaddr *)addr, addr_len )) {
+ if (af == AF_INET)
+ LogError( "Couldn't connect to PRNGD port %d: %m\n",
+ tcp_port );
+ else
+ LogError( "Couldn't connect to PRNGD socket %\"s: %m\n",
+ socket_path );
+ goto done;
+ }
+
+ /* Send blocking read request to PRNGD */
+ msg[0] = 0x02;
+ msg[1] = len;
+
+ if (Writer( fd, msg, sizeof(msg) ) != sizeof(msg)) {
+ if (errno == EPIPE && errors < 10) {
+ close( fd );
+ errors++;
+ goto reopen;
+ }
+ LogError( "Couldn't write to PRNGD socket: %m\n" );
+ goto done;
+ }
+
+ if (Reader( fd, buf, len ) != len) {
+ if (errno == EPIPE && errors < 10) {
+ close( fd );
+ errors++;
+ goto reopen;
+ }
+ LogError( "Couldn't read from PRNGD socket: %m\n" );
+ goto done;
+ }
+
+ rval = 0;
+done:
+ Signal( SIGPIPE, old_sigpipe );
+ if (fd != -1)
+ close( fd );
+ return rval;
+}
+
+/* ####################################################################### */
+
+/*
+ * Stolen from the Linux kernel.
+ *
+ * Copyright Theodore Ts'o, 1994, 1995, 1996, 1997, 1998, 1999. All
+ * rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+static unsigned epool[32], erotate, eadd_ptr;
+
+static void
+add_entropy( unsigned const *in, int nwords )
+{
+ static unsigned const twist_table[8] = {
+ 0, 0x3b6e20c8, 0x76dc4190, 0x4db26158,
+ 0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278 };
+ unsigned i, w;
+ int new_rotate;
+
+ while (nwords--) {
+ w = *in++;
+ w = (w<<erotate | w>>(32-erotate)) & 0xffffffff;
+ i = eadd_ptr = (eadd_ptr - 1) & 31;
+ new_rotate = erotate + 14;
+ if (i)
+ new_rotate = erotate + 7;
+ erotate = new_rotate & 31;
+ w ^= epool[(i + 26) & 31];
+ w ^= epool[(i + 20) & 31];
+ w ^= epool[(i + 14) & 31];
+ w ^= epool[(i + 7) & 31];
+ w ^= epool[(i + 1) & 31];
+ w ^= epool[i];
+ epool[i] = (w >> 3) ^ twist_table[w & 7];
+ }
+}
+
+/* ####################################################################### */
+
+/*
+ * This code implements something close to the MD5 message-digest
+ * algorithm. This code is based on code written by Colin Plumb
+ * in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ */
+
+/* The four core functions - F1 is optimized somewhat */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1 (z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define pmd5_step(f, w, x, y, z, data, s) \
+ (w += (f(x, y, z) + data) & 0xffffffff, w = w<<s | w>>(32-s), w += x)
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.
+ */
+static void
+pmd5_hash( unsigned *out, unsigned const in[16] )
+{
+ unsigned a, b, c, d;
+
+ a = out[0];
+ b = out[1];
+ c = out[2];
+ d = out[3];
+
+ pmd5_step( F1, a, b, c, d, in[0] + 0xd76aa478, 7 );
+ pmd5_step( F1, d, a, b, c, in[1] + 0xe8c7b756, 12 );
+ pmd5_step( F1, c, d, a, b, in[2] + 0x242070db, 17 );
+ pmd5_step( F1, b, c, d, a, in[3] + 0xc1bdceee, 22 );
+ pmd5_step( F1, a, b, c, d, in[4] + 0xf57c0faf, 7 );
+ pmd5_step( F1, d, a, b, c, in[5] + 0x4787c62a, 12 );
+ pmd5_step( F1, c, d, a, b, in[6] + 0xa8304613, 17 );
+ pmd5_step( F1, b, c, d, a, in[7] + 0xfd469501, 22 );
+ pmd5_step( F1, a, b, c, d, in[8] + 0x698098d8, 7 );
+ pmd5_step( F1, d, a, b, c, in[9] + 0x8b44f7af, 12 );
+ pmd5_step( F1, c, d, a, b, in[10] + 0xffff5bb1, 17 );
+ pmd5_step( F1, b, c, d, a, in[11] + 0x895cd7be, 22 );
+ pmd5_step( F1, a, b, c, d, in[12] + 0x6b901122, 7 );
+ pmd5_step( F1, d, a, b, c, in[13] + 0xfd987193, 12 );
+ pmd5_step( F1, c, d, a, b, in[14] + 0xa679438e, 17 );
+ pmd5_step( F1, b, c, d, a, in[15] + 0x49b40821, 22 );
+
+ pmd5_step( F2, a, b, c, d, in[1] + 0xf61e2562, 5 );
+ pmd5_step( F2, d, a, b, c, in[6] + 0xc040b340, 9 );
+ pmd5_step( F2, c, d, a, b, in[11] + 0x265e5a51, 14 );
+ pmd5_step( F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20 );
+ pmd5_step( F2, a, b, c, d, in[5] + 0xd62f105d, 5 );
+ pmd5_step( F2, d, a, b, c, in[10] + 0x02441453, 9 );
+ pmd5_step( F2, c, d, a, b, in[15] + 0xd8a1e681, 14 );
+ pmd5_step( F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20 );
+ pmd5_step( F2, a, b, c, d, in[9] + 0x21e1cde6, 5 );
+ pmd5_step( F2, d, a, b, c, in[14] + 0xc33707d6, 9 );
+ pmd5_step( F2, c, d, a, b, in[3] + 0xf4d50d87, 14 );
+ pmd5_step( F2, b, c, d, a, in[8] + 0x455a14ed, 20 );
+ pmd5_step( F2, a, b, c, d, in[13] + 0xa9e3e905, 5 );
+ pmd5_step( F2, d, a, b, c, in[2] + 0xfcefa3f8, 9 );
+ pmd5_step( F2, c, d, a, b, in[7] + 0x676f02d9, 14 );
+ pmd5_step( F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20 );
+
+ pmd5_step( F3, a, b, c, d, in[5] + 0xfffa3942, 4 );
+ pmd5_step( F3, d, a, b, c, in[8] + 0x8771f681, 11 );
+ pmd5_step( F3, c, d, a, b, in[11] + 0x6d9d6122, 16 );
+ pmd5_step( F3, b, c, d, a, in[14] + 0xfde5380c, 23 );
+ pmd5_step( F3, a, b, c, d, in[1] + 0xa4beea44, 4 );
+ pmd5_step( F3, d, a, b, c, in[4] + 0x4bdecfa9, 11 );
+ pmd5_step( F3, c, d, a, b, in[7] + 0xf6bb4b60, 16 );
+ pmd5_step( F3, b, c, d, a, in[10] + 0xbebfbc70, 23 );
+ pmd5_step( F3, a, b, c, d, in[13] + 0x289b7ec6, 4 );
+ pmd5_step( F3, d, a, b, c, in[0] + 0xeaa127fa, 11 );
+ pmd5_step( F3, c, d, a, b, in[3] + 0xd4ef3085, 16 );
+ pmd5_step( F3, b, c, d, a, in[6] + 0x04881d05, 23 );
+ pmd5_step( F3, a, b, c, d, in[9] + 0xd9d4d039, 4 );
+ pmd5_step( F3, d, a, b, c, in[12] + 0xe6db99e5, 11 );
+ pmd5_step( F3, c, d, a, b, in[15] + 0x1fa27cf8, 16 );
+ pmd5_step( F3, b, c, d, a, in[2] + 0xc4ac5665, 23 );
+
+ pmd5_step( F4, a, b, c, d, in[0] + 0xf4292244, 6 );
+ pmd5_step( F4, d, a, b, c, in[7] + 0x432aff97, 10 );
+ pmd5_step( F4, c, d, a, b, in[14] + 0xab9423a7, 15 );
+ pmd5_step( F4, b, c, d, a, in[5] + 0xfc93a039, 21 );
+ pmd5_step( F4, a, b, c, d, in[12] + 0x655b59c3, 6 );
+ pmd5_step( F4, d, a, b, c, in[3] + 0x8f0ccc92, 10 );
+ pmd5_step( F4, c, d, a, b, in[10] + 0xffeff47d, 15 );
+ pmd5_step( F4, b, c, d, a, in[1] + 0x85845dd1, 21 );
+ pmd5_step( F4, a, b, c, d, in[8] + 0x6fa87e4f, 6 );
+ pmd5_step( F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10 );
+ pmd5_step( F4, c, d, a, b, in[6] + 0xa3014314, 15 );
+ pmd5_step( F4, b, c, d, a, in[13] + 0x4e0811a1, 21 );
+ pmd5_step( F4, a, b, c, d, in[4] + 0xf7537e82, 6 );
+ pmd5_step( F4, d, a, b, c, in[11] + 0xbd3af235, 10 );
+ pmd5_step( F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15 );
+ pmd5_step( F4, b, c, d, a, in[9] + 0xeb86d391, 21 );
+
+ out[0] += a;
+ out[1] += b;
+ out[2] += c;
+ out[3] += d;
+}
+
+/* ####################################################################### */
+
+
+static int
+sumFile( const char *name, int len, int whence, long offset )
+{
+ int fd, i, cnt, readlen = 0;
+ unsigned char buf[0x1000];
+
+ if ((fd = open( name, O_RDONLY )) < 0) {
+ Debug( "cannot open entropy source %\"s: %m\n", name );
+ return -1;
+ }
+ lseek( fd, offset, whence );
+ while (readlen < len) {
+ if (!(cnt = read( fd, buf, sizeof(buf) )))
+ break;
+ if (cnt < 0) {
+ close( fd );
+ Debug( "cannot read entropy source %\"s: %m\n", name );
+ return -1;
+ }
+ readlen += cnt;
+ if (sizeof(unsigned) == 4)
+ add_entropy( (unsigned *)buf, (cnt + 3) / 4 );
+ else {
+ unsigned buf2[sizeof(buf) / 4];
+ for (i = 0; i < cnt; i += 8) {
+ buf2[i / 4] = *(unsigned *)(buf + i) & 0xffffffff;
+ buf2[i / 4 + 1] = *(unsigned *)(buf + i) >> 32;
+ }
+ add_entropy( buf2, (cnt + 3) / 4 );
+ }
+ }
+ close( fd );
+ Debug( "read %d bytes from entropy source %\"s\n", readlen, name );
+ return readlen;
+}
+
+void
+AddTimerEntropy( void )
+{
+ struct timeval now;
+ gettimeofday( &now, 0 );
+ add_entropy( (unsigned *)&now, sizeof(now)/sizeof(unsigned) );
+}
+
+#define BSIZ 0x10000
+
+void
+AddOtherEntropy( void )
+{
+ AddTimerEntropy();
+ /* XXX -- setup-specific ... use some common ones */
+ sumFile( "/var/log/messages", 0x1000, SEEK_END, -0x1000 );
+ sumFile( "/var/log/syslog", 0x1000, SEEK_END, -0x1000 );
+ sumFile( "/var/log/debug", 0x1000, SEEK_END, -0x1000 );
+ sumFile( "/var/log/kern.log", 0x1000, SEEK_END, -0x1000 );
+ sumFile( "/var/log/daemon.log", 0x1000, SEEK_END, -0x1000 );
+/* root hardly ever has an own box ... maybe pick a random mailbox instead? eek ...
+ sumFile( "/var/spool/mail/root", 0x1000, SEEK_END, -0x1000 );
+*/
+}
+
+void
+AddPreGetEntropy( void )
+{
+ static long offset;
+ int readlen;
+
+ AddTimerEntropy();
+ if ((readlen = sumFile( randomFile, BSIZ, SEEK_SET, offset )) == BSIZ) {
+ offset += readlen;
+#if defined(__i386__) || defined(amiga)
+ if (!strcmp( randomFile, "/dev/mem" )) {
+ if (offset == 0xa0000) /* skip 640kB-1MB ROM mappings */
+ offset = 0x100000;
+ else if (offset == 0xf00000) /* skip 15-16MB memory hole */
+ offset = 0x1000000;
+ }
+#endif
+ return;
+ } else if (readlen >= 0 && offset) {
+ if ((offset = sumFile( randomFile, BSIZ, SEEK_SET, 0 )) == BSIZ)
+ return;
+ }
+ LogError( "Cannot read randomFile %\"s; "
+ "X cookies may be easily guessable\n", randomFile );
+}
+#endif
+
+/* ONLY 8 or 16 bytes! */
+/* auth MUST be sizeof(unsigned)-aligned! */
+int
+GenerateAuthData( char *auth, int len )
+{
+#ifdef HAVE_ARC4RANDOM
+ int i;
+ unsigned *rnd = (unsigned *)auth;
+ if (sizeof(unsigned) == 4)
+ for (i = 0; i < len; i += 4)
+ rnd[i / 4] = arc4random();
+ else
+ for (i = 0; i < len; i += 8)
+ rnd[i / 8] = arc4random() | (arc4random() << 32);
+ return 1;
+#else
+ int fd;
+ const char *rd = randomDevice;
+# ifdef DEV_RANDOM
+ if (!*rd)
+ rd = DEV_RANDOM;
+# else
+ if (*rd) {
+# endif
+ if ((fd = open( rd, O_RDONLY )) >= 0) {
+ if (read( fd, auth, len ) == len) {
+ close( fd );
+ return 1;
+ }
+ close( fd );
+ LogError( "Cannot read randomDevice %\"s: %m\n", rd );
+ } else
+ LogError( "Cannot open randomDevice %\"s: %m\n", rd );
+# ifdef DEV_RANDOM
+ return 0;
+# else
+ }
+
+ if (!getPrngdBytes( auth, len, prngdPort, prngdSocket ))
+ return 1;
+
+ {
+ unsigned *rnd = (unsigned *)auth;
+ unsigned tmp[4] = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 };
+ AddPreGetEntropy();
+ pmd5_hash( tmp, epool );
+ add_entropy( tmp, 1 );
+ pmd5_hash( tmp, epool + 16 );
+ add_entropy( tmp + 2, 1 );
+ if (sizeof(unsigned) == 4)
+ memcpy( auth, tmp, len );
+ else {
+ int i;
+ for (i = 0; i < len; i += 8)
+ rnd[i / 8] = tmp[i / 4] | (tmp[i / 4 + 1] << 32);
+ }
+ }
+ return 1;
+# endif
+#endif
+}
diff --git a/kdm/backend/greet.h b/kdm/backend/greet.h
new file mode 100644
index 000000000..985edc29c
--- /dev/null
+++ b/kdm/backend/greet.h
@@ -0,0 +1,278 @@
+/*
+
+Copyright 2001-2005 Oswald Buddenhagen <ossi@kde.org>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the copyright holder.
+
+*/
+
+/*
+ * xdm - display manager daemon
+ * Author: Keith Packard, MIT X Consortium
+ *
+ * interface to xdm's external greeter and config reader
+ */
+
+#ifndef GREET_H
+#define GREET_H
+
+#include <config.h>
+
+#define DEBUG_CORE 0x01
+#define DEBUG_CONFIG 0x02
+#define DEBUG_GREET 0x04
+#define DEBUG_HLPCON 0x08
+#define DEBUG_WSESS 0x10
+#define DEBUG_WCONFIG 0x20
+#define DEBUG_WGREET 0x40
+#define DEBUG_NOSYSLOG 0x80
+#define DEBUG_AUTH 0x100
+#define DEBUG_VALGRIND 0x400
+#define DEBUG_STRACE 0x800
+
+#ifndef TRUE
+# define TRUE 1
+# define FALSE 0
+#endif
+
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
+# define ATTR_UNUSED __attribute__((unused))
+# define ATTR_NORETURN __attribute__((noreturn))
+# define ATTR_PRINTFLIKE(fmt,var) __attribute__((format(printf,fmt,var)))
+#else
+# define ATTR_UNUSED
+# define ATTR_NORETURN
+# define ATTR_PRINTFLIKE(fmt,var)
+#endif
+
+#define as(ar) ((int)(sizeof(ar)/sizeof(ar[0])))
+
+#define __stringify(x) #x
+#define stringify(x) __stringify(x)
+
+/*
+ * Exit codes for fork()ed session process, greeter, and config reader
+ */
+#define EX_NORMAL 30 /* do whatever seems appropriate */
+#define EX_REMANAGE_DPY 31 /* force remanage; same as EX_NORMAL, but cannot return to reserve mode immediately */
+#define EX_UNMANAGE_DPY 32 /* force deletion */
+#define EX_RESERVER_DPY 33 /* force server termination */
+#define EX_AL_RESERVER_DPY 34 /* reserver; maybe, auto-(re-)login */
+#define EX_OPENFAILED_DPY 35 /* XOpenDisplay failed, retry */
+#define EX_RESERVE 37 /* put in reserve mode */
+#ifdef XDMCP
+#define EX_REMOTE 38 /* start -query-ing X-server */
+#define EX_MAX EX_REMOTE
+#else
+#define EX_MAX EX_RESERVE
+#endif
+
+/*
+ * Command codes core -> greeter
+ */
+#define G_Greet 1 /* get login; bidi */
+#define G_ErrorGreet 2 /* print failed auto-login */
+#ifdef XDMCP
+#define G_Choose 3 /* run chooser; bidi */
+# define G_Ch_AddHost 301
+# define G_Ch_ChangeHost 302
+# define G_Ch_RemoveHost 303
+# define G_Ch_BadHost 304
+# define G_Ch_Exit 305
+#endif
+#define G_SessMan 4 /* start "session manager" */
+#define G_ConfShutdown 5 /* confirm forced shutdown */
+#define G_GreetTimed 6 /* get login; timed login permitted */
+
+#ifdef XDMCP
+#define G_Ch_Refresh 10 /* XXX change */
+#define G_Ch_RegisterHost 11 /* str name XXX change */
+#define G_Ch_DirectChoice 12 /* str name XXX change */
+#endif
+
+/*
+ * Status/command codes greeter -> core
+ */
+#define G_Ready 0 /* nop */
+#define G_Cancel 1 /* abort login, etc. */
+
+#define G_DGreet 2 /* get login */
+#ifdef XDMCP
+#define G_DChoose 3 /* run chooser */
+#endif
+
+#define G_Shutdown 101 /* 5*int, string; async */
+# define SHUT_REBOOT 1 /* how */
+# define SHUT_HALT 2
+# define SHUT_CONSOLE -1 /* pseudo-code */
+# define SHUT_SCHEDULE 0 /* when; config only */
+# define SHUT_TRYNOW 1
+# define SHUT_FORCENOW 2
+# define SHUT_CANCEL 0 /* force */
+# define SHUT_FORCEMY 1
+# define SHUT_FORCE 2
+# define SHUT_ASK 3
+# define TO_INF 0x7fffffff
+#define G_SessionExit 102 /* int code; async */
+#define G_GetCfg 103 /* int what; int sts, <variable> */
+#define G_SetupDpy 104 /* ; int <syncer> */
+#define G_ReadDmrc 105 /* str user; int sts - curdmrc */
+#define G_GetDmrc 106 /* str key; str value - curdmrc */
+/*#define G_ResetDmrc 107*/ /* ; async - newdmrc */
+#define G_PutDmrc 108 /* str key, str value; async - newdmrc */
+#define G_Verify 109 /* str type; ..., int V_ret */
+#define G_VerifyRootOK 110 /* str type; ..., int V_ret */
+#define G_List 111 /* int flags; ?*(str,str,[int,]str,str,int), int 0 */
+# define lstRemote 1
+# define lstPassive 2
+# define lstTTY 4
+# define isSelf 1
+# define isTTY 2
+#define G_QueryShutdown 112 /* ; 5*int; string */
+#define G_Activate 113 /* int vt; async */
+#define G_ListBootOpts 114 /* ; int sts, [argv opts, int dflt, int cur] */
+# define BO_OK 0
+# define BO_NOMAN -1
+# define BO_NOENT -2
+# define BO_IO -3
+#define G_Console 116 /* ; async */
+#define G_AutoLogin 117 /* ; async */
+
+/*
+ * Command codes core -> config reader
+ */
+#define GC_Files 1 /* get file list */
+#define GC_GetConf 2 /* get a config group */
+# define GC_gGlobal 1 /* get global config array */
+#ifdef XDMCP
+# define GC_gXaccess 3 /* get Xaccess equivalent */
+#endif
+# define GC_gDisplay 4 /* get per-display config array */
+
+/*
+ * Error code core -> greeter
+ */
+#define GE_Ok 0
+#define GE_NoFkt 1 /* no such function (only for extensions!) */
+#define GE_Error 2 /* internal error, like OOM */
+/* for config reading */
+#define GE_NoEnt 10 /* no such config entry */
+#define GE_BadType 11 /* unknown config entry type */
+/* for dmrc reading */
+#define GE_NoUser 20 /* no such user */
+#define GE_NoFile 21 /* no such file */
+#define GE_Denied 22 /* permission denied */
+
+/*
+ * Log levels.
+ * Used independently in core, greeter & config reader.
+ */
+#define DM_DEBUG 0
+#define DM_INFO 1
+#define DM_WARN 2
+#define DM_ERR 3
+#define DM_PANIC 4
+
+/*
+ * Status codes from Verify
+ */
+/* terminal status codes */
+#define V_OK 0
+#define V_FAIL 10 /* whatever, already reported with V_MSG_* */
+#define V_AUTH 11 /* authentication failed */
+/* non-terminal status codes */
+#define V_MSG_INFO 110 /* info message attached */
+#define V_MSG_ERR 111 /* error message attached (null for generic) */
+#define V_PUT_USER 112 /* user name attached; only with pam & no user send */
+#define V_CHTOK 113 /* password expired; change now */
+#define V_CHTOK_AUTH 114 /* password expired; change now, but authenticate first */
+#define V_PRE_OK 115 /* authentication succeeded, continue with password change */
+/* queries */
+#define V_GET_TEXT 200 /* str prompt, int echo, int ndelay; str return, int tag */
+# define V_IS_SECRET 1
+# define V_IS_USER 2
+# define V_IS_PASSWORD 4
+# define V_IS_OLDPASSWORD 8
+# define V_IS_NEWPASSWORD 16
+#define V_GET_BINARY 201 /* array prompt, int ndelay; array return */
+
+/*
+ * Config/Runtime data keys
+ */
+#define C_WHO_MASK 0x00ff0000 /* Non-zero for proprietary extensions (see manufacturer table [to be written]) */
+#define C_TYPE_MASK 0x0f000000 /* Type of the value */
+# define C_TYPE_INT 0x00000000 /* Integer */
+# define C_TYPE_STR 0x01000000 /* String */
+# define C_TYPE_ARGV 0x02000000 /* 0-terminated Array of Strings */
+# define C_TYPE_ARR 0x03000000 /* Array (only when XDCMP is enabled) */
+#define C_PRIVATE 0xf0000000 /* Private, don't make it visible to interfaces! */
+
+/* display variables */
+#define C_isLocal (C_TYPE_INT | 0x200)
+#define C_hasConsole (C_TYPE_INT | 0x201)
+#define C_isAuthorized (C_TYPE_INT | 0x202)
+
+/**
+ ** for struct display
+ **/
+
+#define d_location 1
+#define dLocal 1 /* server runs on local host */
+#define dForeign 0 /* server runs on remote host */
+
+#define d_lifetime 6
+#define dPermanent 4 /* display restarted when session exits */
+#define dReserve 2 /* display not restarted when session exits */
+#define dTransient 0 /* display removed when session exits */
+
+#ifdef XDMCP
+#define d_origin 8
+#else
+#define d_origin 0 /* clever, huh? :) */
+#endif
+#define dFromXDMCP 8 /* started with XDMCP */
+#define dFromFile 0 /* started via entry in servers file */
+
+#ifdef XDMCP
+/**
+ ** for xdmcp acls
+ **/
+
+/*
+ * flags in acl entries
+ */
+#define a_notAllowed 1 /* both direct and indirect */
+#define a_notBroadcast 2 /* only direct */
+#define a_useChooser 2 /* only indirect */
+
+/*
+ * type of host entries
+ */
+#define HOST_ALIAS 0
+#define HOST_ADDRESS 1
+#define HOST_PATTERN 2
+#define HOST_BROADCAST 3
+
+#endif
+
+#endif /* GREET_H */
diff --git a/kdm/backend/inifile.c b/kdm/backend/inifile.c
new file mode 100644
index 000000000..b5426de75
--- /dev/null
+++ b/kdm/backend/inifile.c
@@ -0,0 +1,255 @@
+/*
+
+Copyright 2003 Oswald Buddenhagen <ossi@kde.org>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the copyright holder.
+
+*/
+
+/*
+ * xdm - display manager daemon
+ * Author: Keith Packard, MIT X Consortium
+ *
+ * load, save and manipulate ini-style config files
+ */
+
+#include "dm.h"
+#include "dm_error.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+char *
+iniLoad( const char *fname )
+{
+ char *data;
+ int fd, len;
+ struct stat st;
+
+ if ((fd = open( fname, O_RDONLY | O_NONBLOCK )) < 0) {
+ Debug( "cannot open ini-file %\"s: %m", fname );
+ return 0;
+ }
+ if (fstat( fd, &st ) || !S_ISREG( st.st_mode )) {
+ LogWarn( "Ini-file %\"s is no regular file\n", fname );
+ close( fd );
+ return 0;
+ }
+ if (st.st_size >= 0x10000) {
+ LogWarn( "Ini-file %\"s is too big\n", fname );
+ close( fd );
+ return 0;
+ }
+ len = st.st_size;
+ if (!(data = Malloc( len + 2 ))) {
+ close( fd );
+ return 0;
+ }
+ if (read( fd, data, len ) != len) {
+ Debug( "cannot read ini-file %\"s: %m", fname );
+ free( data );
+ close( fd );
+ return 0;
+ }
+ close( fd );
+ if (data[len - 1] != '\n')
+ data[len++] = '\n';
+ data[len] = 0;
+ return data;
+}
+
+int
+iniSave( const char *data, const char *fname )
+{
+ int fd, cnt, len;
+
+ if ((fd = open( fname, O_WRONLY | O_CREAT | O_TRUNC | O_NONBLOCK, 0600 )) < 0) {
+ Debug( "cannot create ini-file %\"s: %m", fname );
+ return 0;
+ }
+ len = strlen( data );
+ if ((cnt = write( fd, data, len )) == len) {
+ close( fd );
+ return 1;
+ }
+ if (cnt == -1)
+ Debug( "cannot write ini-file %\"s: %m", fname );
+ else
+ Debug( "cannot write ini-file %\"s: partial write", fname );
+ close( fd );
+ return 0;
+}
+
+#define apparr(d,s,n) do { memcpy (d, s, n); d += n; } while(0)
+#define appbyte(d,b) *d++ = b
+
+char *
+iniEntry( char *data, const char *section, const char *key, const char *value )
+{
+ char *p = data, *secinsert = 0, *pastinsert = 0, *cb, *ce, *ndata;
+ const char *t;
+ int insect = 0, ll, sl, kl, vl, len, nlen;
+
+ if (p) {
+ while (*p) {
+ for (; *p == ' ' || *p == '\t'; p++);
+ if (*p == '\n') {
+ p++;
+ continue;
+ }
+ if (*p == '[') {
+ for (t = section; *++p == *t; t++);
+ insect = !*t && *p == ']';
+ } else if (insect) {
+ for (t = key; *p == *t; t++, p++);
+ for (; *p == ' ' || *p == '\t'; p++);
+ if (!*t && *p == '=') {
+ for (p++; *p == ' ' || *p == '\t'; p++);
+ cb = p;
+ while (*p++ != '\n');
+ ce = p;
+ if (value) {
+ ll = sl = kl = 0;
+ len = (ce - data) + strlen( ce );
+ goto insert;
+ } else {
+ for (ce--; ce != cb && (*(ce - 1) == ' ' || *(ce - 1) == '\t'); ce--);
+ if (!StrNDup( &p, cb, ce - cb ))
+ return 0;
+ return p;
+ }
+ }
+ }
+ for (; *p != '\n'; p++);
+ p++;
+ if (insect)
+ secinsert = p;
+ else
+ pastinsert = p;
+ }
+ }
+ if (!value)
+ return 0;
+ len = p - data;
+ if (secinsert) {
+ ce = cb = secinsert;
+ sl = ll = 0;
+ } else {
+ sl = strlen( section ) + 3;
+ if (pastinsert) {
+ ce = cb = pastinsert;
+ ll = 1;
+ } else {
+ ce = cb = data;
+ ll = 0;
+ }
+ }
+ kl = strlen( key ) + 1;
+ insert:
+ vl = strlen( value );
+ nlen = len - (ce - cb) + ll + sl + kl + vl + 1;
+ if (!(p = ndata = Malloc( nlen + 1 )))
+ return data;
+ apparr( p, data, cb - data );
+ if (kl) {
+ if (sl) {
+ if (ll)
+ appbyte( p, '\n' );
+ appbyte( p, '[' );
+ apparr( p, section, sl - 3 );
+ appbyte( p, ']' );
+ appbyte( p, '\n' );
+ }
+ apparr( p, key, kl - 1 );
+ appbyte( p, '=' );
+ }
+ apparr( p, value, vl );
+ appbyte( p, '\n' );
+ if (data) {
+ apparr( p, ce, len - (ce - data) );
+ free( data );
+ }
+ appbyte( p, 0 );
+ return ndata;
+}
+
+char *
+iniMerge( char *data, const char *newdata )
+{
+ const char *p, *cb, *ce;
+ char *section = 0, *key, *value;
+
+ if (!newdata)
+ return data;
+ for (p = newdata;;) {
+ for (; *p == ' ' || *p == '\t'; p++);
+ if (!*p)
+ break;
+ if (*p == '\n') {
+ p++;
+ continue;
+ }
+ if (*p == '#') {
+ for (p++; *p != '\n'; p++)
+ if (!*p)
+ goto bail;
+ p++;
+ continue;
+ }
+ if (*p == '[') {
+ cb = ++p;
+ for (; *p != ']'; p++)
+ if (!*p || *p == '\n') /* missing ] */
+ goto bail;
+ if (!ReStrN( &section, cb, p - cb ))
+ break;
+ p++;
+ } else {
+ cb = p;
+ for (; *p != '='; p++)
+ if (!*p || *p == '\n') /* missing = */
+ goto bail;
+ for (ce = p; ce != cb && (*(ce - 1) == ' ' || *(ce - 1) == '\t'); ce--);
+ if (!StrNDup( &key, cb, ce - cb ))
+ break;
+ for (p++; *p == ' ' || *p == '\t'; p++);
+ cb = p;
+ for (; *p && *p != '\n'; p++);
+ for (ce = p; ce != cb && (*(ce - 1) == ' ' || *(ce - 1) == '\t'); ce--);
+ if (!StrNDup( &value, cb, ce - cb ))
+ break;
+ if (section)
+ data = iniEntry( data, section, key, value );
+ free( value );
+ free( key );
+ }
+ }
+ bail:
+ if (section)
+ free( section );
+ return data;
+}
diff --git a/kdm/backend/krb5auth.c b/kdm/backend/krb5auth.c
new file mode 100644
index 000000000..16b640a35
--- /dev/null
+++ b/kdm/backend/krb5auth.c
@@ -0,0 +1,248 @@
+/*
+
+Copyright 1994, 1998 The Open Group
+Copyright 2003 Oswald Buddenhagen <ossi@kde.org>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the copyright holder.
+
+*/
+
+/*
+ * xdm - display manager daemon
+ * Author: Stephen Gildea, The Open Group
+ *
+ * generate Kerberos Version 5 authorization records
+ */
+
+#include <config.h>
+
+#ifdef K5AUTH
+
+#include "dm.h"
+#include "dm_auth.h"
+#include "dm_error.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <krb5/krb5.h>
+
+static krb5_context ctx;
+
+/*ARGSUSED*/
+void
+Krb5InitAuth( unsigned short name_len ATTR_UNUSED, const char *name ATTR_UNUSED )
+{
+ if (krb5_init_context( &ctx ))
+ LogError( "Error while initializing Krb5 context\n" );
+}
+
+/*
+ * Returns malloc'ed string that is the credentials cache name.
+ * name should be freed by caller.
+ */
+static char *
+Krb5CCacheName( const char *dname )
+{
+ char *name;
+ const char *tmpdir;
+ int dnl, nl;
+
+ tmpdir = getenv( "TMPDIR" );
+ if (!tmpdir)
+ tmpdir = "/tmp";
+ dnl = strlen( dname );
+ name = Malloc( strlen( tmpdir ) + dnl + 20 );
+ if (!name)
+ return NULL;
+ nl = sprintf( name, "FILE:%s/K5C", tmpdir );
+ CleanUpFileName( dname, name + nl, dnl + 1 );
+ return name;
+}
+
+Xauth *
+Krb5GetAuthFor( unsigned short namelen, const char *name, const char *dname )
+{
+ Xauth *new;
+ char *filename;
+
+ if (!(new = (Xauth *)Malloc( sizeof(*new) )))
+ return (Xauth *)0;
+ new->family = FamilyWild;
+ new->address_length = 0;
+ new->address = 0;
+ new->number_length = 0;
+ new->number = 0;
+
+ if (dname) {
+ if (!(filename = Krb5CCacheName( dname ))) {
+ free( (char *)new );
+ return (Xauth *)0;
+ }
+ new->data = 0;
+ if (!StrApp( &new->data, "UU:", filename, (char *)0 )) {
+ free( filename );
+ free( (char *)new );
+ return (Xauth *)0;
+ }
+ free( filename );
+ new->data_length = strlen( new->data );
+ } else {
+ new->data = NULL;
+ new->data_length = 0;
+ }
+
+ if (!(new->name = (char *)Malloc( namelen ))) {
+ free( (char *)new->data );
+ free( (char *)new );
+ return (Xauth *)0;
+ }
+ memmove( new->name, name, namelen );
+ new->name_length = namelen;
+ return new;
+}
+
+
+Xauth *
+Krb5GetAuth( unsigned short namelen, const char *name )
+{
+ return Krb5GetAuthFor( namelen, name, NULL );
+}
+
+
+static krb5_error_code
+Krb5DisplayCCache( const char *dname, krb5_ccache *ccache_return, char **name )
+{
+ char *ccname;
+ krb5_error_code code;
+
+ if (!(ccname = Krb5CCacheName( dname )))
+ return ENOMEM;
+ Debug( "resolving Kerberos cache %s\n", ccname );
+ if ((code = krb5_cc_resolve( ctx, ccname, ccache_return )) || !name)
+ free( ccname );
+ else
+ *name = ccname;
+ return code;
+}
+
+char *
+Krb5Init( const char *user, const char *passwd, const char *dname )
+{
+ krb5_error_code code;
+ krb5_get_init_creds_opt options;
+ krb5_principal me;
+ krb5_creds my_creds;
+ krb5_ccache ccache;
+ char *ccname;
+
+ if (!ctx)
+ return 0;
+
+ if ((code = krb5_parse_name( ctx, user, &me ))) {
+ LogError( "%s while parsing Krb5 user %\"s\n",
+ error_message( code ), user );
+ return 0;
+ }
+
+ krb5_get_init_creds_opt_init( &options );
+ /*krb5_get_init_creds_opt_set_tkt_life (&options, 60*60*8);*/ /* 8 hours */
+
+ if ((code = krb5_get_init_creds_password( ctx, &my_creds,
+ me, /* principal */
+ (char * /* for MIT */) passwd,
+ 0, /* prompter */
+ 0, /* prompter ctx */
+ 0, /* start time delta */
+ 0, /* service */
+ &options )))
+ {
+ char *my_name = NULL;
+ int code2 = krb5_unparse_name( ctx, me, &my_name );
+ if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY)
+ LogError( "Password incorrect for Krb5 principal %\"s\n",
+ code2 ? user : my_name );
+ else
+ LogError( "%s while getting initial Krb5 credentials for %\"s\n",
+ error_message( code ), code2 ? user : my_name );
+ if (my_name)
+ free( my_name );
+ goto err3;
+ }
+
+ if ((code = Krb5DisplayCCache( dname, &ccache, &ccname ))) {
+ LogError( "%s while getting Krb5 ccache for %\"s\n",
+ error_message( code ), dname );
+ goto err2;
+ }
+
+ if ((code = krb5_cc_initialize( ctx, ccache, me ))) {
+ LogError( "%s while initializing Krb5 cache %\"s\n",
+ error_message( code ), ccname );
+ goto err1;
+ }
+
+ if ((code = krb5_cc_store_cred( ctx, ccache, &my_creds ))) {
+ LogError( "%s while storing Krb5 credentials to cache %\"s\n",
+ error_message( code ), ccname );
+ err1:
+ krb5_cc_close( ctx, ccache );
+ free( ccname );
+ err2:
+ krb5_free_cred_contents( ctx, &my_creds );
+ err3:
+ krb5_free_principal( ctx, me );
+ return 0;
+ }
+
+ krb5_cc_close( ctx, ccache );
+ krb5_free_cred_contents( ctx, &my_creds );
+ krb5_free_principal( ctx, me );
+ return ccname;
+}
+
+void
+Krb5Destroy( const char *dname )
+{
+ krb5_error_code code;
+ krb5_ccache ccache;
+
+ if (!ctx)
+ return;
+
+ if ((code = Krb5DisplayCCache( dname, &ccache, 0 )))
+ LogError( "%s while getting Krb5 ccache to destroy\n",
+ error_message( code ) );
+ else {
+ if ((code = krb5_cc_destroy( ctx, ccache ))) {
+ if (code == KRB5_FCC_NOFILE)
+ Debug( "no Kerberos ccache file found to destroy\n" );
+ else
+ LogError( "%s while destroying Krb5 credentials cache\n",
+ error_message( code ) );
+ } else
+ Debug( "kerberos ccache destroyed\n" );
+ }
+}
+
+#endif
diff --git a/kdm/backend/mitauth.c b/kdm/backend/mitauth.c
new file mode 100644
index 000000000..fd18d41df
--- /dev/null
+++ b/kdm/backend/mitauth.c
@@ -0,0 +1,87 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2003 Oswald Buddenhagen <ossi@kde.org>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the copyright holder.
+
+*/
+
+/*
+ * xdm - display manager daemon
+ * Author: Keith Packard, MIT X Consortium
+ *
+ * generate authorization keys
+ * for MIT-MAGIC-COOKIE-1 type authorization
+ */
+
+#include "dm.h"
+#include "dm_auth.h"
+
+#define AUTH_DATA_LEN 16 /* bytes of authorization data */
+static char auth_name[256];
+
+void
+MitInitAuth( unsigned short name_len, const char *name )
+{
+ if (name_len > 256)
+ name_len = 256;
+ memmove( auth_name, name, name_len );
+}
+
+Xauth *
+MitGetAuth( unsigned short namelen, const char *name )
+{
+ Xauth *new;
+ new = (Xauth *)Malloc( sizeof(Xauth) );
+
+ if (!new)
+ return (Xauth *)0;
+ new->family = FamilyWild;
+ new->address_length = 0;
+ new->address = 0;
+ new->number_length = 0;
+ new->number = 0;
+
+ new->data = (char *)Malloc( AUTH_DATA_LEN );
+ if (!new->data) {
+ free( (char *)new );
+ return (Xauth *)0;
+ }
+ new->name = (char *)Malloc( namelen );
+ if (!new->name) {
+ free( (char *)new->data );
+ free( (char *)new );
+ return (Xauth *)0;
+ }
+ memmove( (char *)new->name, name, namelen );
+ new->name_length = namelen;
+ if (!GenerateAuthData( new->data, AUTH_DATA_LEN )) {
+ free( (char *)new->name );
+ free( (char *)new->data );
+ free( (char *)new );
+ return (Xauth *)0;
+ }
+ new->data_length = AUTH_DATA_LEN;
+ return new;
+}
diff --git a/kdm/backend/netaddr.c b/kdm/backend/netaddr.c
new file mode 100644
index 000000000..349a53528
--- /dev/null
+++ b/kdm/backend/netaddr.c
@@ -0,0 +1,219 @@
+/*
+
+Copyright 1991, 1998 The Open Group
+Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the copyright holder.
+
+*/
+
+/*
+ * xdm - display manager daemon
+ * Author: Keith Packard, MIT X Consortium
+ *
+ * netaddr.c - Interpretation of XdmcpNetaddr object.
+ */
+
+#include "dm.h"
+#include "dm_socket.h"
+#include "dm_error.h"
+
+/* given an char *, returns the socket protocol family used,
+ e.g., AF_INET */
+
+int
+NetaddrFamily( char *netaddrp )
+{
+#ifdef STREAMSCONN
+ short family = *(short *)netaddrp;
+ return family;
+#else
+ return ((struct sockaddr *)netaddrp)->sa_family;
+#endif
+}
+
+
+/* given an char *, returns a pointer to the TCP/UDP port used
+ and sets *lenp to the length of the address
+ or 0 if not using TCP or UDP. */
+
+char *
+NetaddrPort( char *netaddrp, int *lenp )
+{
+#ifdef STREAMSCONN
+ *lenp = 2;
+ return netaddrp+2;
+#else
+ switch (NetaddrFamily( netaddrp ))
+ {
+ case AF_INET:
+ *lenp = 2;
+ return (char *)&(((struct sockaddr_in *)netaddrp)->sin_port);
+#if defined(IPv6) && defined(AF_INET6)
+ case AF_INET6:
+ *lenp = 2;
+ return (char *)&(((struct sockaddr_in6 *)netaddrp)->sin6_port);
+#endif
+ default:
+ *lenp = 0;
+ return NULL;
+ }
+#endif
+}
+
+
+/* given an char *, returns a pointer to the network address
+ and sets *lenp to the length of the address */
+
+char *
+NetaddrAddress( char *netaddrp, int *lenp )
+{
+#ifdef STREAMSCONN
+ *lenp = 4;
+ return netaddrp+4;
+#else
+ switch (NetaddrFamily( netaddrp )) {
+#ifdef UNIXCONN
+ case AF_UNIX:
+ *lenp = strlen( ((struct sockaddr_un *)netaddrp)->sun_path );
+ return (char *)(((struct sockaddr_un *)netaddrp)->sun_path);
+#endif
+#ifdef TCPCONN
+ case AF_INET:
+ *lenp = sizeof(struct in_addr);
+ return (char *)&(((struct sockaddr_in *)netaddrp)->sin_addr);
+#if defined(IPv6) && defined(AF_INET6)
+ case AF_INET6:
+ {
+ struct in6_addr *a = &(((struct sockaddr_in6 *)netaddrp)->sin6_addr);
+ if (IN6_IS_ADDR_V4MAPPED( a )) {
+ *lenp = sizeof(struct in_addr);
+ return ((char *)&(a->s6_addr))+12;
+ } else {
+ *lenp = sizeof(struct in6_addr);
+ return (char *)&(a->s6_addr);
+ }
+ }
+#endif
+#endif
+#ifdef DNETCONN
+ case AF_DECnet:
+ *lenp = sizeof(struct dn_naddr);
+ return (char *)&(((struct sockaddr_dn *)netaddrp)->sdn_add);
+#endif
+#ifdef AF_CHAOS
+ case AF_CHAOS:
+#endif
+ default:
+ *lenp = 0;
+ return NULL;
+ }
+#endif /* STREAMSCONN else */
+}
+
+
+/* given an char *, sets *addr to the network address used and
+ sets *len to the number of bytes in addr.
+ Returns the X protocol family used, e.g., FamilyInternet */
+
+int
+ConvertAddr( char *saddr, int *len, char **addr )
+{
+ int retval;
+
+ if (len == NULL)
+ return -1;
+ *addr = NetaddrAddress( saddr, len );
+#ifdef STREAMSCONN
+ /* kludge */
+ if (NetaddrFamily( saddr ) == 2)
+ retval = FamilyInternet;
+#else
+ switch (NetaddrFamily( saddr )) {
+#ifdef AF_UNSPEC
+ case AF_UNSPEC:
+ retval = FamilyLocal;
+ break;
+#endif
+#ifdef AF_UNIX
+#ifndef __hpux
+ case AF_UNIX:
+ retval = FamilyLocal;
+ break;
+#endif
+#endif
+#ifdef TCPCONN
+ case AF_INET:
+ retval = FamilyInternet;
+ break;
+#if defined(IPv6) && defined(AF_INET6)
+ case AF_INET6:
+ if (*len == sizeof(struct in_addr))
+ retval = FamilyInternet;
+ else
+ retval = FamilyInternet6;
+ break;
+#endif
+#endif
+#ifdef DNETCONN
+ case AF_DECnet:
+ retval = FamilyDECnet;
+ break;
+#endif
+#ifdef AF_CHAOS
+ case AF_CHAOS:
+ retval = FamilyChaos;
+ break;
+#endif
+ default:
+ retval = -1;
+ break;
+ }
+#endif /* STREAMSCONN else */
+ Debug( "ConvertAddr returning %d for family %d\n", retval,
+ NetaddrFamily( saddr ) );
+ return retval;
+}
+
+#ifdef XDMCP
+int
+addressEqual( char *a1, int len1, char *a2, int len2 )
+{
+ int partlen1, partlen2;
+ char *part1, *part2;
+
+ if (len1 != len2)
+ return FALSE;
+ if (NetaddrFamily( a1 ) != NetaddrFamily( a2 ))
+ return FALSE;
+ part1 = NetaddrPort( a1, &partlen1 );
+ part2 = NetaddrPort( a2, &partlen2 );
+ if (partlen1 != partlen2 || memcmp( part1, part2, partlen1 ) != 0)
+ return FALSE;
+ part1 = NetaddrAddress( a1, &partlen1 );
+ part2 = NetaddrAddress( a2, &partlen2 );
+ if (partlen1 != partlen2 || memcmp( part1, part2, partlen1 ) != 0)
+ return FALSE;
+ return TRUE;
+}
+#endif
diff --git a/kdm/backend/policy.c b/kdm/backend/policy.c
new file mode 100644
index 000000000..cabee7088
--- /dev/null
+++ b/kdm/backend/policy.c
@@ -0,0 +1,278 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2001 Oswald Buddenhagen <ossi@kde.org>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the copyright holder.
+
+*/
+
+/*
+ * xdm - display manager daemon
+ * Author: Keith Packard, MIT X Consortium
+ *
+ * policy.c. Implement site-dependent policy for XDMCP connections
+ */
+
+#include <config.h>
+
+#ifdef XDMCP
+
+#include "dm.h"
+#include "dm_auth.h"
+#include "dm_socket.h"
+
+static ARRAY8 noAuthentication = { (CARD16)0, (CARD8Ptr) 0 };
+
+typedef struct _XdmAuth {
+ ARRAY8 authentication;
+ ARRAY8 authorization;
+} XdmAuthRec, *XdmAuthPtr;
+
+static XdmAuthRec auth[] = {
+#ifdef HASXDMAUTH
+{ {(CARD16)20, (CARD8 *)"XDM-AUTHENTICATION-1"},
+ {(CARD16)19, (CARD8 *)"XDM-AUTHORIZATION-1"},
+},
+#endif
+{ {(CARD16)0, (CARD8 *)0},
+ {(CARD16)0, (CARD8 *)0},
+}
+};
+
+#define NumAuth as(auth)
+
+ARRAY8Ptr
+ChooseAuthentication( ARRAYofARRAY8Ptr authenticationNames )
+{
+ int i, j;
+
+ for (i = 0; i < (int)authenticationNames->length; i++)
+ for (j = 0; j < NumAuth; j++)
+ if (XdmcpARRAY8Equal( &authenticationNames->data[i],
+ &auth[j].authentication ))
+ return &authenticationNames->data[i];
+ return &noAuthentication;
+}
+
+int
+CheckAuthentication(
+ struct protoDisplay *pdpy ATTR_UNUSED,
+ ARRAY8Ptr displayID ATTR_UNUSED,
+ ARRAY8Ptr name ATTR_UNUSED,
+ ARRAY8Ptr data ATTR_UNUSED )
+{
+#ifdef HASXDMAUTH
+ if (name->length && !memcmp( (char *)name->data, "XDM-AUTHENTICATION-1", 20 ))
+ return XdmCheckAuthentication( pdpy, displayID, name, data );
+#endif
+ return TRUE;
+}
+
+int
+SelectAuthorizationTypeIndex( ARRAY8Ptr authenticationName,
+ ARRAYofARRAY8Ptr authorizationNames )
+{
+ int i, j;
+
+ for (j = 0; j < NumAuth; j++)
+ if (XdmcpARRAY8Equal( authenticationName,
+ &auth[j].authentication ))
+ break;
+ if (j < NumAuth)
+ for (i = 0; i < (int)authorizationNames->length; i++)
+ if (XdmcpARRAY8Equal( &authorizationNames->data[i],
+ &auth[j].authorization ))
+ return i;
+ for (i = 0; i < (int)authorizationNames->length; i++)
+ if (ValidAuthorization( authorizationNames->data[i].length,
+ (char *)authorizationNames->data[i].data ))
+ return i;
+ return -1;
+}
+
+
+/*#define WILLING_INTERNAL*/
+
+#ifdef WILLING_INTERNAL
+/* Report the loadavg to chooser. Nice feature ...
+ *
+ * Wed Mar 10 1999 -- Steffen Hansen
+ */
+static void
+Willing_msg( char *mbuf )
+{
+#ifdef __linux__
+ int fd;
+ int numcpu;
+ const char *fail_msg = "Willing to manage";
+ FILE *f;
+ float load[3];
+ float mhz = 0.0;
+ char buf[1024];
+
+ fd = open( "/proc/loadavg", O_RDONLY );
+ if (fd == -1) {
+ sprintf( mbuf, fail_msg );
+ return;
+ } else if (read( fd, buf, 100 ) < 4) {
+ close( fd );
+ sprintf( mbuf, fail_msg );
+ return;
+ }
+ close( fd );
+
+ sscanf( buf, "%f %f %f", &load[0], &load[1], &load[2] );
+ sprintf( mbuf, "Available (load: %0.2f, %0.2f, %0.2f)",
+ load[0], load[1], load[2] );
+
+ numcpu = 0;
+
+ if (!(f = fopen( "/proc/cpuinfo", "r" )))
+ return;
+
+ while (fGets( buf, sizeof(buf), f ) != -1) {
+ float m;
+ if (sscanf( buf, "cpu MHz : %f", &m )) {
+ numcpu++;
+ mhz = m;
+ }
+ }
+
+ fclose( f );
+
+ if (numcpu) {
+ if (numcpu > 1)
+ sprintf( buf, " %d*%0.0f MHz", numcpu, mhz );
+ else
+ sprintf( buf, " %0.0f MHz", mhz );
+
+ strncat( mbuf, buf, 256 );
+
+ mbuf[255] = 0;
+ }
+#elif HAVE_GETLOADAVG /* !__linux__ */
+#ifdef __GNUC__
+# warning This code is untested...
+#endif
+ double load[3];
+ getloadavg( load, 3 );
+ sprintf( mbuf, "Available (load: %0.2f, %0.2f, %0.2f)", load[0],
+ load[1], load[2] );
+#else /* !__linux__ && !GETLOADAVG */
+ strcpy( mbuf, "Willing to manage" );
+#endif
+}
+#endif
+
+/*ARGSUSED*/
+int
+Willing( ARRAY8Ptr addr, CARD16 connectionType,
+ ARRAY8Ptr authenticationName ATTR_UNUSED,
+ ARRAY8Ptr status, xdmOpCode type )
+{
+ int ret;
+ char statusBuf[256];
+ static time_t lastscan;
+
+ if (autoRescan && lastscan + 15 < now) {
+ lastscan = now;
+ ScanAccessDatabase( FALSE );
+ }
+ ret = AcceptableDisplayAddress( addr, connectionType, type );
+ if (!ret)
+ sprintf( statusBuf, "Display not authorized to connect" );
+ else {
+ if (*willing) {
+ FILE *fd;
+ int len, ok = 0;
+ if ((fd = popen( willing, "r" ))) {
+ for (;;) {
+ if ((len = fGets( statusBuf, sizeof(statusBuf), fd )) != -1) {
+ if (len) {
+ ok = 1;
+ break;
+ }
+ }
+ if (feof( fd ) || errno != EINTR)
+ break;
+ }
+ pclose( fd );
+ }
+ if (!ok)
+ sprintf( statusBuf, "Willing, but %.*s failed",
+ sizeof(statusBuf) - 21, willing );
+ } else
+#ifdef WILLING_INTERNAL
+ Willing_msg( statusBuf );
+#else
+ strcpy( statusBuf, "Willing to manage" );
+#endif
+ }
+ status->length = strlen( statusBuf );
+ status->data = (CARD8Ptr) Malloc( status->length );
+ if (!status->data)
+ status->length = 0;
+ else
+ memmove( status->data, statusBuf, status->length );
+ return ret;
+}
+
+/*ARGSUSED*/
+ARRAY8Ptr
+Accept( struct sockaddr *from ATTR_UNUSED, int fromlen ATTR_UNUSED,
+ CARD16 displayNumber ATTR_UNUSED )
+{
+ return 0;
+}
+
+/*ARGSUSED*/
+int
+SelectConnectionTypeIndex( ARRAY16Ptr connectionTypes,
+ ARRAYofARRAY8Ptr connectionAddresses ATTR_UNUSED )
+{
+ int i;
+
+ /*
+ * Select one supported connection type
+ */
+
+ for (i = 0; i < connectionTypes->length; i++) {
+ switch (connectionTypes->data[i]) {
+ case FamilyLocal:
+#if defined(TCPCONN)
+ case FamilyInternet:
+# if defined(IPv6) && defined(AF_INET6)
+ case FamilyInternet6:
+# endif /* IPv6 */
+#endif /* TCPCONN */
+#if defined(DNETCONN)
+ case FamilyDECnet:
+#endif /* DNETCONN */
+ return i;
+ }
+ } /* for */
+ return -1;
+}
+
+#endif /* XDMCP */
diff --git a/kdm/backend/printf.c b/kdm/backend/printf.c
new file mode 100644
index 000000000..d7220642b
--- /dev/null
+++ b/kdm/backend/printf.c
@@ -0,0 +1,872 @@
+/*
+
+Copyright 2001,2002,2004 Oswald Buddenhagen <ossi@kde.org>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the copyright holder.
+
+*/
+
+/*
+ * xdm - display manager daemon
+ * Author: Keith Packard, MIT X Consortium
+ *
+ * printf.c - working horse of error.c
+ */
+
+/*
+ * NOTE: this file is meant to be included, not linked,
+ * so it can be used in the helper programs without much voodoo.
+ */
+
+/* ########## printf core implementation with some extensions ########## */
+/*
+ * How to use the extensions:
+ * - put ' or " in the flags field to quote a string with this char and
+ * escape special characters (only available, if PRINT_QUOTES is defined)
+ * - put \\ in the flags field to quote special characters and leading and
+ * trailing spaces (only available, if PRINT_QUOTES is defined)
+ * - arrays (only available, if PRINT_ARRAYS is defined)
+ * - the array modifier [ comes after the maximal field width specifier
+ * - the array length can be specified literally, with the '*' modifier
+ * (in which case an argument is expected) or will be automatically
+ * determined (stop values are -1 for ints and 0 for strings)
+ * - these modifiers expect their argument to be an in-line string quoted
+ * with an arbitrary character:
+ * - (, ) -> array pre-/suf-fix; default ""
+ * - <, > -> element pre-/suf-fix; default ""
+ * - | -> element separator; default " "
+ * - these modifiers expect no argument:
+ * - : -> print '<number of elements>: ' before an array
+ * - , -> short for |','
+ * - { -> short for ('{')' }'<' '|''
+ * - the pointer to the array is the last argument to the format
+ * - the %m conversion from syslog() is supported
+ */
+
+/**************************************************************
+ * Partially stolen from OpenSSH's OpenBSD compat directory.
+ * (C) Patrick Powell, Brandon Long, Thomas Roessler,
+ * Michael Elkins, Ben Lindstrom
+ **************************************************************/
+
+#include <ctype.h>
+#include <string.h>
+#include <stdarg.h>
+
+/* format flags - Bits */
+#define DP_F_MINUS (1 << 0)
+#define DP_F_PLUS (1 << 1)
+#define DP_F_SPACE (1 << 2)
+#define DP_F_NUM (1 << 3)
+#define DP_F_ZERO (1 << 4)
+#define DP_F_UPCASE (1 << 5)
+#define DP_F_UNSIGNED (1 << 6)
+#define DP_F_SQUOTE (1 << 7)
+#define DP_F_DQUOTE (1 << 8)
+#define DP_F_BACKSL (1 << 9)
+#define DP_F_ARRAY (1 << 10)
+#define DP_F_COLON (1 << 11)
+
+/* Conversion Flags */
+#define DP_C_INT 0
+#define DP_C_BYTE 1
+#define DP_C_SHORT 2
+#define DP_C_LONG 3
+#define DP_C_STR 10
+
+typedef void (*OutCh)( void *bp, char c );
+
+
+static void
+fmtint( OutCh dopr_outch, void *bp,
+ long value, int base, int min, int max, int flags )
+{
+ const char *ctab;
+ unsigned long uvalue;
+ int signvalue = 0;
+ int place = 0;
+ int spadlen = 0; /* amount to space pad */
+ int zpadlen = 0; /* amount to zero pad */
+ char convert[20];
+
+ if (max < 0)
+ max = 0;
+
+ uvalue = value;
+
+ if (!(flags & DP_F_UNSIGNED)) {
+ if (value < 0) {
+ signvalue = '-';
+ uvalue = -value;
+ } else if (flags & DP_F_PLUS) /* Do a sign (+/i) */
+ signvalue = '+';
+ else if (flags & DP_F_SPACE)
+ signvalue = ' ';
+ }
+
+ ctab = (flags & DP_F_UPCASE) ? "0123456789ABCDEF" : "0123456789abcdef";
+ do {
+ convert[place++] = ctab[uvalue % (unsigned)base];
+ uvalue = uvalue / (unsigned)base;
+ } while (uvalue);
+
+ zpadlen = max - place;
+ spadlen = min - (max > place ? max : place) -
+ (signvalue ? 1 : 0) - ((flags & DP_F_NUM) ? 2 : 0);
+ if (zpadlen < 0)
+ zpadlen = 0;
+ if (spadlen < 0)
+ spadlen = 0;
+ if (flags & DP_F_ZERO) {
+ zpadlen = zpadlen > spadlen ? zpadlen : spadlen;
+ spadlen = 0;
+ }
+ if (flags & DP_F_MINUS)
+ spadlen = -spadlen; /* Left Justifty */
+
+
+ /* Spaces */
+ while (spadlen > 0) {
+ dopr_outch( bp, ' ' );
+ --spadlen;
+ }
+
+ /* Sign */
+ if (signvalue)
+ dopr_outch( bp, signvalue );
+
+ /* Prefix */
+ if (flags & DP_F_NUM) {
+ dopr_outch( bp, '0' );
+ dopr_outch( bp, 'x' );
+ }
+
+ /* Zeros */
+ if (zpadlen > 0)
+ while (zpadlen > 0) {
+ dopr_outch( bp, '0' );
+ --zpadlen;
+ }
+
+ /* Digits */
+ while (place > 0)
+ dopr_outch( bp, convert[--place] );
+
+ /* Left Justified spaces */
+ while (spadlen < 0) {
+ dopr_outch( bp, ' ' );
+ ++spadlen;
+ }
+}
+
+typedef struct {
+ const char *str;
+ size_t len;
+} str_t;
+
+static void
+putstr( OutCh dopr_outch, void *bp, str_t *st )
+{
+ size_t pt;
+
+ for (pt = 0; pt < st->len; pt++)
+ dopr_outch( bp, st->str[pt] );
+}
+
+static str_t _null_parents = { "(null)", 6 };
+#ifdef PRINT_ARRAYS
+static str_t _null_dparents = { "((null))", 8 };
+#endif
+#if defined(PRINT_QUOTES) || defined(PRINT_ARRAYS)
+static str_t _null_caps = { "NULL", 4 };
+#endif
+
+static void
+fmtstr( OutCh dopr_outch, void *bp,
+ const char *value, int flags, int min, int max )
+{
+ int padlen, strln, curcol;
+#ifdef PRINT_QUOTES
+ int lastcol;
+#endif
+ char ch;
+
+ if (!value) {
+#ifdef PRINT_QUOTES
+ if (flags & (DP_F_SQUOTE | DP_F_DQUOTE))
+ putstr( dopr_outch, bp, &_null_caps );
+ else
+#endif
+ putstr( dopr_outch, bp, &_null_parents );
+ return;
+ }
+
+ for (strln = 0; (unsigned)strln < (unsigned)max && value[strln]; strln++);
+ padlen = min - strln;
+ if (padlen < 0)
+ padlen = 0;
+ if (flags & DP_F_MINUS)
+ padlen = -padlen; /* Left Justify */
+
+ for (; padlen > 0; padlen--)
+ dopr_outch( bp, ' ' );
+#ifdef PRINT_QUOTES
+# if 0 /* gcc's flow analyzer is not the smartest ... */
+ lastcol = 0;
+# endif
+ if (flags & DP_F_SQUOTE)
+ dopr_outch( bp, '\'' );
+ else if (flags & DP_F_DQUOTE)
+ dopr_outch( bp, '"');
+ else if (flags & DP_F_BACKSL)
+ for (lastcol = strln; lastcol && value[lastcol - 1] == ' '; lastcol--);
+#endif
+ for (curcol = 0; curcol < strln; curcol++) {
+ ch = value[curcol];
+#ifdef PRINT_QUOTES
+ if (flags & (DP_F_SQUOTE | DP_F_DQUOTE | DP_F_BACKSL)) {
+ switch (ch) {
+ case '\r': ch = 'r'; break;
+ case '\n': ch = 'n'; break;
+ case '\t': ch = 't'; break;
+ case '\a': ch = 'a'; break;
+ case '\b': ch = 'b'; break;
+ case '\v': ch = 'v'; break;
+ case '\f': ch = 'f'; break;
+ default:
+ if (ch < 32 ||
+ ((unsigned char)ch >= 0x7f && (unsigned char)ch < 0xa0))
+ {
+ dopr_outch( bp, '\\' );
+ fmtint( dopr_outch, bp, (unsigned char)ch, 8, 3, 3, DP_F_ZERO );
+ continue;
+ } else {
+ if ((ch == '\'' && (flags & DP_F_SQUOTE)) ||
+ (ch == '"' && (flags & DP_F_DQUOTE) ) ||
+ (ch == ' ' && (flags & DP_F_BACKSL) &&
+ (!curcol || curcol >= lastcol)) ||
+ ch == '\\')
+ dopr_outch( bp, '\\' );
+ dopr_outch( bp, ch );
+ continue;
+ }
+ }
+ dopr_outch( bp, '\\' );
+ }
+#endif
+ dopr_outch( bp, ch );
+ }
+#ifdef PRINT_QUOTES
+ if (flags & DP_F_SQUOTE)
+ dopr_outch( bp, '\'' );
+ else if (flags & DP_F_DQUOTE)
+ dopr_outch( bp, '"' );
+#endif
+ for (; padlen < 0; padlen++)
+ dopr_outch( bp, ' ' );
+}
+
+static void
+DoPr( OutCh dopr_outch, void *bp, const char *format, va_list args )
+{
+ const char *strvalue;
+#ifdef PRINT_ARRAYS
+ str_t arpr, arsf, arepr, aresf, aresp, *arp;
+ void *arptr;
+#endif
+ unsigned long value;
+ int radix, min, max, flags, cflags, errn;
+#ifdef PRINT_ARRAYS
+ int arlen;
+ unsigned aridx;
+ char sch;
+#endif
+ char ch;
+#define NCHR if (!(ch = *format++)) return
+
+#if 0 /* gcc's flow analyzer is not the smartest ... */
+# ifdef PRINT_ARRAYS
+ arlen = 0;
+# endif
+ radix = 0;
+#endif
+ errn = errno;
+ for (;;) {
+ for (;;) {
+ NCHR;
+ if (ch == '%')
+ break;
+ dopr_outch (bp, ch);
+ }
+ flags = cflags = min = 0;
+ max = -1;
+ for (;;) {
+ NCHR;
+ switch (ch) {
+ case '#': flags |= DP_F_NUM; continue;
+ case '-': flags |= DP_F_MINUS; continue;
+ case '+': flags |= DP_F_PLUS; continue;
+ case ' ': flags |= DP_F_SPACE; continue;
+ case '0': flags |= DP_F_ZERO; continue;
+#ifdef PRINT_QUOTES
+ case '"': flags |= DP_F_DQUOTE; continue;
+ case '\'': flags |= DP_F_SQUOTE; continue;
+ case '\\': flags |= DP_F_BACKSL; continue;
+#endif
+ }
+ break;
+ }
+ for (;;) {
+ if (isdigit( (unsigned char)ch )) {
+ min = 10 * min + (ch - '0');
+ NCHR;
+ continue;
+ } else if (ch == '*') {
+ min = va_arg( args, int );
+ NCHR;
+ }
+ break;
+ }
+ if (ch == '.') {
+ max = 0;
+ for (;;) {
+ NCHR;
+ if (isdigit( (unsigned char)ch )) {
+ max = 10 * max + (ch - '0');
+ continue;
+ } else if (ch == '*') {
+ max = va_arg( args, int );
+ NCHR;
+ }
+ break;
+ }
+ }
+#ifdef PRINT_ARRAYS
+ if (ch == '[') {
+ flags |= DP_F_ARRAY;
+ arlen = -1;
+ arpr.len = arsf.len = arepr.len = aresf.len = 0;
+ aresp.len = 1, aresp.str = " ";
+ for (;;) {
+ NCHR;
+ if (isdigit( (unsigned char)ch )) {
+ arlen = 0;
+ for (;;) {
+ arlen += (ch - '0');
+ NCHR;
+ if (!isdigit( (unsigned char)ch ))
+ break;
+ arlen *= 10;
+ }
+ }
+ switch (ch) {
+ case ':': flags |= DP_F_COLON; continue;
+ case '*': arlen = va_arg( args, int ); continue;
+ case '(': arp = &arpr; goto rar;
+ case ')': arp = &arsf; goto rar;
+ case '<': arp = &arepr; goto rar;
+ case '>': arp = &aresf; goto rar;
+ case '|': arp = &aresp;
+ rar:
+ NCHR;
+ sch = ch;
+ arp->str = format;
+ do {
+ NCHR;
+ } while (ch != sch);
+ arp->len = format - arp->str - 1;
+ continue;
+ case ',':
+ aresp.len = 1, aresp.str = ",";
+ continue;
+ case '{':
+ aresp.len = 0, arpr.len = arepr.len = 1, arsf.len = 2;
+ arpr.str = "{", arepr.str = " ", arsf.str = " }";
+ continue;
+ }
+ break;
+ }
+ }
+#endif
+ for (;;) {
+ switch (ch) {
+ case 'h':
+ cflags = DP_C_SHORT;
+ NCHR;
+ if (ch == 'h') {
+ cflags = DP_C_BYTE;
+ NCHR;
+ }
+ continue;
+ case 'l':
+ cflags = DP_C_LONG;
+ NCHR;
+ continue;
+ }
+ break;
+ }
+ switch (ch) {
+ case '%':
+ dopr_outch( bp, ch );
+ break;
+ case 'm':
+ fmtstr( dopr_outch, bp, strerror( errn ), flags, min, max );
+ break;
+ case 'c':
+ dopr_outch( bp, va_arg( args, int ) );
+ break;
+ case 's':
+#ifdef PRINT_ARRAYS
+ cflags = DP_C_STR;
+ goto printit;
+#else
+ strvalue = va_arg( args, char * );
+ fmtstr( dopr_outch, bp, strvalue, flags, min, max );
+ break;
+#endif
+ case 'u':
+ flags |= DP_F_UNSIGNED;
+ case 'd':
+ case 'i':
+ radix = 10;
+ goto printit;
+ case 'X':
+ flags |= DP_F_UPCASE;
+ case 'x':
+ flags |= DP_F_UNSIGNED;
+ radix = 16;
+ printit:
+#ifdef PRINT_ARRAYS
+ if (flags & DP_F_ARRAY) {
+ if (!(arptr = va_arg( args, void * )))
+ putstr( dopr_outch, bp,
+ arpr.len ? &_null_caps : &_null_dparents );
+ else {
+ if (arlen == -1) {
+ arlen = 0;
+ switch (cflags) {
+ case DP_C_STR: while (((char **)arptr)[arlen]) arlen++; break;
+ case DP_C_BYTE: while (((unsigned char *)arptr)[arlen] != (unsigned char)-1) arlen++; break;
+ case DP_C_SHORT: while (((unsigned short int *)arptr)[arlen] != (unsigned short int)-1) arlen++; break;
+ case DP_C_LONG: while (((unsigned long int *)arptr)[arlen] != (unsigned long int)-1) arlen++; break;
+ default: while (((unsigned int *)arptr)[arlen] != (unsigned int)-1) arlen++; break;
+ }
+ }
+ if (flags & DP_F_COLON) {
+ fmtint( dopr_outch, bp, (long)arlen, 10, 0, -1, DP_F_UNSIGNED );
+ dopr_outch( bp, ':' );
+ dopr_outch( bp, ' ' );
+ }
+ putstr( dopr_outch, bp, &arpr );
+ for (aridx = 0; aridx < (unsigned)arlen; aridx++) {
+ if (aridx)
+ putstr( dopr_outch, bp, &aresp );
+ putstr( dopr_outch, bp, &arepr );
+ if (cflags == DP_C_STR) {
+ strvalue = ((char **)arptr)[aridx];
+ fmtstr( dopr_outch, bp, strvalue, flags, min, max );
+ } else {
+ if (flags & DP_F_UNSIGNED) {
+ switch (cflags) {
+ case DP_C_BYTE: value = ((unsigned char *)arptr)[aridx]; break;
+ case DP_C_SHORT: value = ((unsigned short int *)arptr)[aridx]; break;
+ case DP_C_LONG: value = ((unsigned long int *)arptr)[aridx]; break;
+ default: value = ((unsigned int *)arptr)[aridx]; break;
+ }
+ } else {
+ switch (cflags) {
+ case DP_C_BYTE: value = ((signed char *)arptr)[aridx]; break;
+ case DP_C_SHORT: value = ((short int *)arptr)[aridx]; break;
+ case DP_C_LONG: value = ((long int *)arptr)[aridx]; break;
+ default: value = ((int *)arptr)[aridx]; break;
+ }
+ }
+ fmtint( dopr_outch, bp, value, radix, min, max, flags );
+ }
+ putstr( dopr_outch, bp, &aresf );
+ }
+ putstr( dopr_outch, bp, &arsf );
+ }
+ } else {
+ if (cflags == DP_C_STR) {
+ strvalue = va_arg( args, char * );
+ fmtstr( dopr_outch, bp, strvalue, flags, min, max );
+ } else {
+#endif
+ if (flags & DP_F_UNSIGNED) {
+ switch (cflags) {
+ case DP_C_LONG: value = va_arg( args, unsigned long int ); break;
+ default: value = va_arg( args, unsigned int ); break;
+ }
+ } else {
+ switch (cflags) {
+ case DP_C_LONG: value = va_arg( args, long int ); break;
+ default: value = va_arg( args, int ); break;
+ }
+ }
+ fmtint( dopr_outch, bp, value, radix, min, max, flags );
+#ifdef PRINT_ARRAYS
+ }
+ }
+#endif
+ break;
+ case 'p':
+ value = (long)va_arg( args, void * );
+ fmtint( dopr_outch, bp, value, 16, sizeof(long) * 2 + 2,
+ max, flags | DP_F_UNSIGNED | DP_F_ZERO | DP_F_NUM );
+ break;
+ }
+ }
+}
+
+/* ########## end of printf core implementation ########## */
+
+
+/*
+ * Logging function for xdm and helper programs.
+ */
+#ifndef NO_LOGGER
+
+#include <stdio.h>
+#include <time.h>
+
+#ifdef USE_SYSLOG
+# include <syslog.h>
+# ifdef LOG_NAME
+# define InitLog() openlog(LOG_NAME, LOG_PID, LOG_DAEMON)
+# else
+# define InitLog() openlog(prog, LOG_PID, LOG_DAEMON)
+# endif
+static int lognums[] = { LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERR, LOG_CRIT };
+#else
+# define InitLog() while(0)
+#endif
+
+static const char *lognams[] = { "debug", "info", "warning", "error", "panic" };
+
+static void
+logTime( char *dbuf )
+{
+ time_t tim;
+ (void)time( &tim );
+ strftime( dbuf, 20, "%b %e %H:%M:%S", localtime( &tim ) );
+}
+
+#if defined(LOG_DEBUG_MASK) || defined(USE_SYSLOG)
+STATIC int debugLevel;
+#endif
+
+#define OOMSTR "Out of memory. Expect problems.\n"
+
+STATIC void
+LogOutOfMem( void )
+{
+ static time_t last;
+ time_t tnow;
+
+ time( &tnow );
+ if (last + 100 > tnow) { /* don't log bursts */
+ last = tnow;
+ return;
+ }
+ last = tnow;
+#ifdef USE_SYSLOG
+ if (!(debugLevel & DEBUG_NOSYSLOG))
+ syslog( LOG_CRIT, OOMSTR );
+ else
+#endif
+ {
+ int el;
+ char dbuf[24], sbuf[128];
+ logTime( dbuf );
+ el = sprintf( sbuf, "%s "
+#ifdef LOG_NAME
+ LOG_NAME "[%ld]: " OOMSTR, dbuf,
+#else
+ "%s[%ld]: " OOMSTR, dbuf, prog,
+#endif
+ (long)getpid() );
+ write( 2, sbuf, el );
+ }
+}
+
+typedef struct {
+ char *buf;
+ int clen, blen, type;
+ char lmbuf[128];
+} OCLBuf;
+
+static void
+OutChLFlush( OCLBuf *oclbp )
+{
+ if (oclbp->clen) {
+#ifdef USE_SYSLOG
+ if (!(debugLevel & DEBUG_NOSYSLOG))
+ syslog( lognums[oclbp->type], "%.*s", oclbp->clen, oclbp->buf );
+ else
+#endif
+ {
+ oclbp->buf[oclbp->clen] = '\n';
+ write( 2, oclbp->buf, oclbp->clen + 1 );
+ }
+ oclbp->clen = 0;
+ }
+}
+
+static void
+OutChL( void *bp, char c )
+{
+ OCLBuf *oclbp = (OCLBuf *)bp;
+ char *nbuf;
+ int nlen;
+
+ if (c == '\n')
+ OutChLFlush( oclbp );
+ else {
+ if (oclbp->clen >= oclbp->blen - 1) {
+ if (oclbp->buf == oclbp->lmbuf) {
+ OutChLFlush( oclbp );
+ oclbp->buf = 0;
+ oclbp->blen = 0;
+ }
+ nlen = oclbp->blen * 3 / 2 + 128;
+ nbuf = Realloc( oclbp->buf, nlen );
+ if (nbuf) {
+ oclbp->buf = nbuf;
+ oclbp->blen = nlen;
+ } else {
+ OutChLFlush( oclbp );
+ oclbp->buf = oclbp->lmbuf;
+ oclbp->blen = sizeof(oclbp->lmbuf);
+ }
+ }
+#ifdef USE_SYSLOG
+ if (!oclbp->clen && (debugLevel & DEBUG_NOSYSLOG)) {
+#else
+ if (!oclbp->clen) {
+#endif
+ char dbuf[24];
+ logTime( dbuf );
+ oclbp->clen = sprintf( oclbp->buf, "%s "
+#ifdef LOG_NAME
+ LOG_NAME "[%ld] %s: ", dbuf,
+#else
+ "%s[%ld] %s: ", dbuf, prog,
+#endif
+ (long)getpid(), lognams[oclbp->type] );
+ }
+ oclbp->buf[oclbp->clen++] = c;
+ }
+}
+
+static void
+Logger( int type, const char *fmt, va_list args )
+{
+ OCLBuf oclb;
+
+ oclb.buf = 0;
+ oclb.blen = oclb.clen = 0;
+ oclb.type = type;
+ DoPr( OutChL, &oclb, fmt, args );
+ /* no flush, every message is supposed to be \n-terminated */
+ if (oclb.buf && oclb.buf != oclb.lmbuf)
+ free( oclb.buf );
+}
+
+#ifdef LOG_DEBUG_MASK
+STATIC void
+Debug( const char *fmt, ... )
+{
+ if (debugLevel & LOG_DEBUG_MASK) {
+ va_list args;
+ int olderrno = errno;
+ va_start( args, fmt );
+ Logger( DM_DEBUG, fmt, args );
+ va_end( args );
+ errno = olderrno;
+ }
+}
+#endif
+
+#ifndef LOG_NO_INFO
+STATIC void
+LogInfo( const char *fmt, ... )
+{
+ va_list args;
+
+ va_start( args, fmt );
+ Logger( DM_INFO, fmt, args );
+ va_end( args );
+}
+#endif
+
+#ifndef LOG_NO_WARN
+STATIC void
+LogWarn( const char *fmt, ... )
+{
+ va_list args;
+
+ va_start( args, fmt );
+ Logger( DM_WARN, fmt, args );
+ va_end( args );
+}
+#endif
+
+#ifndef LOG_NO_ERROR
+STATIC void
+LogError( const char *fmt, ... )
+{
+ va_list args;
+
+ va_start( args, fmt );
+ Logger( DM_ERR, fmt, args );
+ va_end( args );
+}
+#endif
+
+#ifdef LOG_PANIC_EXIT
+STATIC void
+LogPanic( const char *fmt, ... )
+{
+ va_list args;
+
+ va_start( args, fmt );
+ Logger( DM_PANIC, fmt, args );
+ va_end( args );
+ exit( LOG_PANIC_EXIT );
+}
+#endif
+
+#endif /* NO_LOGGER */
+
+#ifdef NEED_FDPRINTF
+
+typedef struct {
+ char *buf;
+ int clen, blen, tlen;
+} OCFBuf;
+
+static void
+OutCh_OCF( void *bp, char c )
+{
+ OCFBuf *ocfbp = (OCFBuf *)bp;
+ char *nbuf;
+ int nlen;
+
+ ocfbp->tlen++;
+ if (ocfbp->clen >= ocfbp->blen) {
+ if (ocfbp->blen < 0)
+ return;
+ nlen = ocfbp->blen * 3 / 2 + 100;
+ nbuf = Realloc( ocfbp->buf, nlen );
+ if (!nbuf) {
+ free( ocfbp->buf );
+ ocfbp->blen = -1;
+ ocfbp->buf = 0;
+ ocfbp->clen = 0;
+ return;
+ }
+ ocfbp->blen = nlen;
+ ocfbp->buf = nbuf;
+ }
+ ocfbp->buf[ocfbp->clen++] = c;
+}
+
+STATIC int
+FdPrintf( int fd, const char *fmt, ... )
+{
+ va_list args;
+ OCFBuf ocfb = { 0, 0, 0, -1 };
+
+ va_start( args, fmt );
+ DoPr( OutCh_OCF, &ocfb, fmt, args );
+ va_end( args );
+ if (ocfb.buf) {
+ Debug( "FdPrintf %\".*s to %d\n", ocfb.clen, ocfb.buf, fd );
+ (void)write( fd, ocfb.buf, ocfb.clen );
+ free( ocfb.buf );
+ }
+ return ocfb.tlen;
+}
+
+#endif /* NEED_FDPRINTF */
+
+#ifdef NEED_ASPRINTF
+
+typedef struct {
+ char *buf;
+ int clen, blen, tlen;
+} OCABuf;
+
+static void
+OutCh_OCA( void *bp, char c )
+{
+ OCABuf *ocabp = (OCABuf *)bp;
+ char *nbuf;
+ int nlen;
+
+ ocabp->tlen++;
+ if (ocabp->clen >= ocabp->blen) {
+ if (ocabp->blen < 0)
+ return;
+ nlen = ocabp->blen * 3 / 2 + 100;
+ nbuf = Realloc( ocabp->buf, nlen );
+ if (!nbuf) {
+ free( ocabp->buf );
+ ocabp->blen = -1;
+ ocabp->buf = 0;
+ ocabp->clen = 0;
+ return;
+ }
+ ocabp->blen = nlen;
+ ocabp->buf = nbuf;
+ }
+ ocabp->buf[ocabp->clen++] = c;
+}
+
+STATIC int
+VASPrintf( char **strp, const char *fmt, va_list args )
+{
+ OCABuf ocab = { 0, 0, 0, -1 };
+
+ DoPr( OutCh_OCA, &ocab, fmt, args );
+ OutCh_OCA( &ocab, 0 );
+ *strp = Realloc( ocab.buf, ocab.clen );
+ if (!*strp)
+ *strp = ocab.buf;
+ return ocab.tlen;
+}
+
+STATIC int
+ASPrintf( char **strp, const char *fmt, ... )
+{
+ va_list args;
+ int len;
+
+ va_start( args, fmt );
+ len = VASPrintf( strp, fmt, args );
+ va_end( args );
+ return len;
+}
+
+#endif /* NEED_ASPRINTF */
diff --git a/kdm/backend/process.c b/kdm/backend/process.c
new file mode 100644
index 000000000..30dca096d
--- /dev/null
+++ b/kdm/backend/process.c
@@ -0,0 +1,761 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2001-2004 Oswald Buddenhagen <ossi@kde.org>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the copyright holder.
+
+*/
+
+/*
+ * xdm - display manager daemon
+ * Author: Keith Packard, MIT X Consortium
+ *
+ * subdaemon and external process management and communication
+ */
+
+#include "dm.h"
+#include "dm_error.h"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <unistd.h>
+#ifdef _POSIX_PRIORITY_SCHEDULING
+# include <sched.h>
+#endif
+
+extern char **environ;
+
+
+SIGFUNC Signal( int sig, SIGFUNC handler )
+{
+#ifndef __EMX__
+ struct sigaction sigact, osigact;
+ sigact.sa_handler = handler;
+ sigemptyset( &sigact.sa_mask );
+# ifdef SA_RESTART
+ sigact.sa_flags = SA_RESTART;
+# else
+ sigact.sa_flags = 0;
+# endif
+ sigaction( sig, &sigact, &osigact );
+ return osigact.sa_handler;
+#else
+ return signal( sig, handler );
+#endif
+}
+
+
+void
+TerminateProcess( int pid, int sig )
+{
+ kill( pid, sig );
+#ifdef SIGCONT
+ kill( pid, SIGCONT );
+#endif
+}
+
+
+static FD_TYPE CloseMask;
+static int max = -1;
+
+void
+RegisterCloseOnFork( int fd )
+{
+ FD_SET( fd, &CloseMask );
+ if (fd > max)
+ max = fd;
+}
+
+void
+ClearCloseOnFork( int fd )
+{
+ FD_CLR( fd, &CloseMask );
+}
+
+void
+CloseNClearCloseOnFork( int fd )
+{
+ close( fd );
+ FD_CLR( fd, &CloseMask );
+}
+
+static void
+CloseOnFork( void )
+{
+ int fd;
+
+ for (fd = 0; fd <= max; fd++)
+ if (FD_ISSET( fd, &CloseMask ))
+ close( fd );
+ FD_ZERO( &CloseMask );
+ max = -1;
+}
+
+int
+Fork()
+{
+ int pid;
+
+ sigset_t ss, oss;
+ sigfillset( &ss );
+ sigprocmask( SIG_SETMASK, &ss, &oss );
+
+ if (!(pid = fork())) {
+#ifdef SIGCHLD
+ (void)Signal( SIGCHLD, SIG_DFL );
+#endif
+ (void)Signal( SIGTERM, SIG_DFL );
+ (void)Signal( SIGINT, SIG_IGN ); /* for -nodaemon */
+ (void)Signal( SIGPIPE, SIG_DFL );
+ (void)Signal( SIGALRM, SIG_DFL );
+ (void)Signal( SIGHUP, SIG_DFL );
+ sigemptyset( &ss );
+ sigprocmask( SIG_SETMASK, &ss, NULL );
+ CloseOnFork();
+ return 0;
+ }
+
+ sigprocmask( SIG_SETMASK, &oss, 0 );
+
+ return pid;
+}
+
+int
+Wait4( int pid )
+{
+ waitType result;
+
+ while (waitpid( pid, &result, 0 ) < 0)
+ if (errno != EINTR) {
+ Debug( "Wait4(%d) failed: %m\n", pid );
+ return 0;
+ }
+ return waitVal( result );
+}
+
+
+void
+execute( char **argv, char **env )
+{
+ Debug( "execute: %[s ; %[s\n", argv, env );
+ execve( argv[0], argv, env );
+ /*
+ * In case this is a shell script which hasn't been
+ * made executable (or this is a SYSV box), do
+ * a reasonable thing
+ */
+ if (errno != ENOENT) {
+ char **newargv;
+ FILE *f;
+ int nu;
+ char program[1024];
+
+ /*
+ * emulate BSD kernel behaviour -- read
+ * the first line; check if it starts
+ * with "#!", in which case it uses
+ * the rest of the line as the name of
+ * program to run. Else use "/bin/sh".
+ */
+ if (!(f = fopen( argv[0], "r" )))
+ return;
+ if (!fGets( program, sizeof(program), f )) {
+ fclose( f );
+ return;
+ }
+ fclose( f );
+ if (!strncmp( program, "#!", 2 ))
+ newargv = parseArgs( 0, program + 2 );
+ else
+ newargv = addStrArr( 0, "/bin/sh", 7 );
+ if (!newargv)
+ return;
+ nu = arrLen( newargv );
+ if (!(argv = xCopyStrArr( nu, argv )))
+ return;
+ memcpy( argv, newargv, sizeof(char *) * nu );
+ Debug( "shell script execution: %[s\n", argv );
+ execve( argv[0], argv, env );
+ }
+}
+
+int
+runAndWait( char **args, char **env )
+{
+ int pid, ret;
+
+ switch (pid = Fork()) {
+ case 0:
+ execute( args, env );
+ LogError( "Can't execute %\"s: %m\n", args[0] );
+ exit( 127 );
+ case -1:
+ LogError( "Can't fork to execute %\"s: %m\n", args[0] );
+ return 1;
+ }
+ ret = Wait4( pid );
+ return waitVal( ret );
+}
+
+FILE *
+pOpen( char **what, char m, int *pid )
+{
+ int dp[2];
+
+ if (pipe( dp ))
+ return 0;
+ switch ((*pid = Fork())) {
+ case 0:
+ if (m == 'r')
+ dup2( dp[1], 1 );
+ else
+ dup2( dp[0], 0 );
+ close( dp[0] );
+ close( dp[1] );
+ execute( what, environ );
+ LogError( "Can't execute %\"s: %m\n", what[0] );
+ exit( 127 );
+ case -1:
+ close( dp[0] );
+ close( dp[1] );
+ LogError( "Can't fork to execute %\"s: %m\n", what[0] );
+ return 0;
+ }
+ if (m == 'r') {
+ close( dp[1] );
+ return fdopen( dp[0], "r" );
+ } else {
+ close( dp[0] );
+ return fdopen( dp[1], "w" );
+ }
+}
+
+int
+pClose( FILE *f, int pid )
+{
+ fclose( f );
+ return Wait4( pid );
+}
+
+char *
+locate( const char *exe )
+{
+ int len;
+ char *path, *pathe, *name, *thenam, nambuf[PATH_MAX+1];
+
+ if (!(path = getenv( "PATH" ))) {
+ LogError( "Can't execute %'s: $PATH not set.\n", exe );
+ return 0;
+ }
+ len = strlen( exe );
+ name = nambuf + PATH_MAX - len;
+ memcpy( name, exe, len + 1 );
+ *--name = '/';
+ do {
+ if (!(pathe = strchr( path, ':' )))
+ pathe = path + strlen( path );
+ len = pathe - path;
+ if (len && !(len == 1 && *path == '.')) {
+ thenam = name - len;
+ if (thenam >= nambuf) {
+ memcpy( thenam, path, len );
+ if (!access( thenam, X_OK )) {
+ StrDup( &name, thenam );
+ return name;
+ }
+ }
+ }
+ path = pathe;
+ } while (*path++ != '\0');
+ LogError( "Can't execute %'s: not in $PATH.\n", exe );
+ return 0;
+}
+
+
+static GTalk *curtalk;
+
+void
+GSet( GTalk *tlk )
+{
+ curtalk = tlk;
+}
+
+int
+GFork( GPipe *pajp, const char *pname, char *cname,
+ GPipe *ogp, char *cgname )
+{
+ int opipe[2], ipipe[2], ogpipe[2], igpipe[2], pid;
+
+ if (pipe( opipe ))
+ goto badp1;
+ if (pipe( ipipe ))
+ goto badp2;
+ if (ogp) {
+ if (pipe( ogpipe ))
+ goto badp3;
+ if (pipe( igpipe )) {
+ close( ogpipe[0] );
+ close( ogpipe[1] );
+ badp3:
+ close( ipipe[0] );
+ close( ipipe[1] );
+ badp2:
+ close( opipe[0] );
+ close( opipe[1] );
+ badp1:
+ LogError( "Cannot start %s, pipe() failed", cname );
+ if (cname)
+ free( cname );
+ return -1;
+ }
+ }
+ RegisterCloseOnFork( opipe[1] );
+ RegisterCloseOnFork( ipipe[0] );
+ if (ogp) {
+ RegisterCloseOnFork( ogpipe[1] );
+ RegisterCloseOnFork( igpipe[0] );
+ }
+ switch (pid = Fork()) {
+ case -1:
+ close( opipe[0] );
+ close( ipipe[1] );
+ CloseNClearCloseOnFork( opipe[1] );
+ CloseNClearCloseOnFork( ipipe[0] );
+ if (ogp) {
+ close( ogpipe[0] );
+ close( igpipe[1] );
+ CloseNClearCloseOnFork( ogpipe[1] );
+ CloseNClearCloseOnFork( igpipe[0] );
+ }
+ LogError( "Cannot start %s, fork() failed\n", cname );
+ if (cname)
+ free( cname );
+ return -1;
+ case 0:
+ pajp->wfd = ipipe[1];
+ RegisterCloseOnFork( ipipe[1] );
+ pajp->rfd = opipe[0];
+ RegisterCloseOnFork( opipe[0] );
+ pajp->who = (char *)pname;
+ if (ogp) {
+ ogp->wfd = igpipe[1];
+ RegisterCloseOnFork( igpipe[1] );
+ ogp->rfd = ogpipe[0];
+ RegisterCloseOnFork( ogpipe[0] );
+ ogp->who = (char *)pname;
+ }
+ break;
+ default:
+ close( opipe[0] );
+ close( ipipe[1] );
+ pajp->rfd = ipipe[0];
+ pajp->wfd = opipe[1];
+ pajp->who = cname;
+ if (ogp) {
+ close( ogpipe[0] );
+ close( igpipe[1] );
+ ogp->rfd = igpipe[0];
+ ogp->wfd = ogpipe[1];
+ ogp->who = cgname;
+ }
+ break;
+ }
+ return pid;
+}
+
+int
+GOpen( GProc *proc, char **argv, const char *what, char **env, char *cname,
+ GPipe *gp )
+{
+ char **margv;
+ int pip[2];
+ char coninfo[32];
+
+/* ### GSet (proc->pipe); */
+ if (proc->pid) {
+ LogError( "%s already running\n", cname );
+ if (cname)
+ free( cname );
+ return -1;
+ }
+ if (!(margv = xCopyStrArr( 1, argv ))) {
+ if (cname)
+ free( cname );
+ return -1;
+ }
+ if (!StrApp( margv, progpath, what, (char *)0 )) {
+ free( margv );
+ if (cname)
+ free( cname );
+ return -1;
+ }
+ if (pipe( pip )) {
+ LogError( "Cannot start %s, pipe() failed\n", cname );
+ if (cname)
+ free( cname );
+ goto fail;
+ }
+ if (gp) {
+ ClearCloseOnFork( gp->rfd );
+ ClearCloseOnFork( gp->wfd );
+ }
+ proc->pid = GFork( &proc->pipe, 0, cname, 0, 0 );
+ if (proc->pid) {
+ close( pip[1] );
+ if (gp) {
+ RegisterCloseOnFork( gp->rfd );
+ RegisterCloseOnFork( gp->wfd );
+ }
+ }
+ switch (proc->pid) {
+ case -1:
+ fail1:
+ close( pip[0] );
+ fail:
+ free( margv[0] );
+ free( margv );
+ return -1;
+ case 0:
+ (void)Signal( SIGPIPE, SIG_IGN );
+ close( pip[0] );
+ fcntl( pip[1], F_SETFD, FD_CLOEXEC );
+ if (gp)
+ sprintf( coninfo, "CONINFO=%d %d %d %d",
+ proc->pipe.rfd, proc->pipe.wfd, gp->rfd, gp->wfd );
+ else
+ sprintf( coninfo, "CONINFO=%d %d",
+ proc->pipe.rfd, proc->pipe.wfd );
+ env = putEnv( coninfo, env );
+ if (debugLevel & DEBUG_VALGRIND) {
+ char **nmargv = xCopyStrArr( 1, margv );
+ nmargv[0] = locate( "valgrind" );
+ execute( nmargv, env );
+ } else if (debugLevel & DEBUG_STRACE) {
+ char **nmargv = xCopyStrArr( 1, margv );
+ nmargv[0] = locate( "strace" );
+ execute( nmargv, env );
+ } else
+ execute( margv, env );
+ write( pip[1], "", 1 );
+ exit( 1 );
+ default:
+ (void)Signal( SIGPIPE, SIG_IGN );
+ if (Reader( pip[0], coninfo, 1 )) {
+ Wait4( proc->pid );
+ LogError( "Cannot execute %\"s (%s)\n", margv[0], cname );
+ GClosen (&proc->pipe);
+ goto fail1;
+ }
+ close( pip[0] );
+ Debug( "started %s (%\"s), pid %d\n", cname, margv[0], proc->pid );
+ free( margv[0] );
+ free( margv );
+ GSendInt( debugLevel );
+ return 0;
+ }
+}
+
+static void
+iGClosen( GPipe *pajp )
+{
+ CloseNClearCloseOnFork( pajp->rfd );
+ CloseNClearCloseOnFork( pajp->wfd );
+ pajp->rfd = pajp->wfd = -1;
+}
+
+void
+GClosen (GPipe *pajp)
+{
+ iGClosen( pajp );
+ if (pajp->who)
+ free( pajp->who );
+ pajp->who = 0;
+}
+
+int
+GClose (GProc *proc, GPipe *gp, int force)
+{
+ int ret;
+
+ if (!proc->pid) {
+ Debug( "whoops, GClose while helper not running\n" );
+ return 0;
+ }
+ iGClosen( &proc->pipe );
+ if (gp)
+ GClosen (gp);
+ if (force)
+ TerminateProcess( proc->pid, SIGTERM );
+ ret = Wait4( proc->pid );
+ proc->pid = 0;
+ if (WaitSig( ret ) ? WaitSig( ret ) != SIGTERM :
+ (WaitCode( ret ) < EX_NORMAL || WaitCode( ret ) > EX_MAX))
+ LogError( "Abnormal termination of %s, code %d, signal %d\n",
+ proc->pipe.who, WaitCode( ret ), WaitSig( ret ) );
+ Debug( "closed %s\n", proc->pipe.who );
+ if (proc->pipe.who)
+ free( proc->pipe.who );
+ proc->pipe.who = 0;
+ return ret;
+}
+
+static void ATTR_NORETURN
+GErr( void )
+{
+ Longjmp( curtalk->errjmp, 1 );
+}
+
+static void
+GRead( void *buf, int len )
+{
+ if (Reader( curtalk->pipe->rfd, buf, len ) != len) {
+ LogError( "Cannot read from %s\n", curtalk->pipe->who );
+ GErr();
+ }
+}
+
+static void
+GWrite( const void *buf, int len )
+{
+ if (Writer( curtalk->pipe->wfd, buf, len ) != len) {
+ LogError( "Cannot write to %s\n", curtalk->pipe->who );
+ GErr();
+ }
+#ifdef _POSIX_PRIORITY_SCHEDULING
+ if ((debugLevel & DEBUG_HLPCON))
+ sched_yield();
+#endif
+}
+
+void
+GSendInt( int val )
+{
+ GDebug( "sending int %d (%#x) to %s\n", val, val, curtalk->pipe->who );
+ GWrite( &val, sizeof(val) );
+}
+
+int
+GRecvInt()
+{
+ int val;
+
+ GDebug( "receiving int from %s ...\n", curtalk->pipe->who );
+ GRead( &val, sizeof(val) );
+ GDebug( " -> %d (%#x)\n", val, val );
+ return val;
+}
+
+int
+GRecvCmd( int *cmd )
+{
+ GDebug( "receiving command from %s ...\n", curtalk->pipe->who );
+ if (Reader( curtalk->pipe->rfd, cmd, sizeof(*cmd) ) == sizeof(*cmd)) {
+ GDebug( " -> %d\n", *cmd );
+ return 1;
+ }
+ GDebug( " -> no data\n" );
+ return 0;
+}
+
+void
+GSendArr( int len, const char *data )
+{
+ GDebug( "sending array[%d] %02[*{hhx to %s\n",
+ len, len, data, curtalk->pipe->who );
+ GWrite( &len, sizeof(len) );
+ GWrite( data, len );
+}
+
+static char *
+iGRecvArr( int *rlen )
+{
+ int len;
+ char *buf;
+
+ GRead( &len, sizeof(len) );
+ *rlen = len;
+ GDebug( " -> %d bytes\n", len );
+ if (!len)
+ return (char *)0;
+ if (!(buf = Malloc( len )))
+ GErr();
+ GRead( buf, len );
+ return buf;
+}
+
+char *
+GRecvArr( int *rlen )
+{
+ char *buf;
+
+ GDebug( "receiving array from %s ...\n", curtalk->pipe->who );
+ buf = iGRecvArr( rlen );
+ GDebug( " -> %02[*{hhx\n", *rlen, buf );
+ return buf;
+}
+
+static int
+iGRecvArrBuf( char *buf )
+{
+ int len;
+
+ GRead( &len, sizeof(len) );
+ GDebug( " -> %d bytes\n", len );
+ if (len)
+ GRead( buf, len );
+ return len;
+}
+
+int
+GRecvArrBuf( char *buf )
+{
+ int len;
+
+ GDebug( "receiving already allocated array from %s ...\n",
+ curtalk->pipe->who );
+ len = iGRecvArrBuf( buf );
+ GDebug( " -> %02[*{hhx\n", len, buf );
+ return len;
+}
+
+int
+GRecvStrBuf( char *buf )
+{
+ int len;
+
+ GDebug( "receiving already allocated string from %s ...\n",
+ curtalk->pipe->who );
+ len = iGRecvArrBuf( buf );
+ GDebug( " -> %\".*s\n", len, buf );
+ return len;
+}
+
+void
+GSendStr( const char *buf )
+{
+ int len;
+
+ GDebug( "sending string %\"s to %s\n", buf, curtalk->pipe->who );
+ if (buf) {
+ len = strlen( buf ) + 1;
+ GWrite( &len, sizeof(len) );
+ GWrite( buf, len );
+ } else
+ GWrite( &buf, sizeof(int) );
+}
+
+void
+GSendNStr( const char *buf, int len )
+{
+ int tlen = len + 1;
+ GDebug( "sending string %\".*s to %s\n", len, buf, curtalk->pipe->who );
+ GWrite( &tlen, sizeof(tlen) );
+ GWrite( buf, len );
+ GWrite( "", 1 );
+}
+
+void
+GSendStrN( const char *buf, int len )
+{
+ if (buf)
+ GSendNStr( buf, StrNLen( buf, len ) );
+ else
+ GSendStr( buf );
+}
+
+char *
+GRecvStr()
+{
+ int len;
+ char *buf;
+
+ GDebug( "receiving string from %s ...\n", curtalk->pipe->who );
+ buf = iGRecvArr( &len );
+ GDebug( " -> %\".*s\n", len, buf );
+ return buf;
+}
+
+static void
+iGSendStrArr( int num, char **data )
+{
+ char **cdata;
+
+ GWrite( &num, sizeof(num) );
+ for (cdata = data; --num >= 0; cdata++)
+ GSendStr( *cdata );
+}
+
+/*
+void
+GSendStrArr (int num, char **data)
+{
+ GDebug( "sending string array[%d] to %s\n", num, curtalk->pipe->who );
+ iGSendStrArr( num, data );
+}
+*/
+
+char **
+GRecvStrArr( int *rnum )
+{
+ int num;
+ char **argv, **cargv;
+
+ GDebug( "receiving string array from %s ...\n", curtalk->pipe->who );
+ GRead( &num, sizeof(num) );
+ GDebug( " -> %d strings\n", num );
+ *rnum = num;
+ if (!num)
+ return (char **)0;
+ if (!(argv = Malloc( num * sizeof(char *) )))
+ GErr();
+ for (cargv = argv; --num >= 0; cargv++)
+ *cargv = GRecvStr();
+ return argv;
+}
+
+void
+GSendArgv( char **argv )
+{
+ int num;
+
+ if (argv) {
+ for (num = 0; argv[num]; num++);
+ GDebug( "sending argv[%d] to %s ...\n", num, curtalk->pipe->who );
+ iGSendStrArr( num + 1, argv );
+ } else {
+ GDebug( "sending NULL argv to %s\n", curtalk->pipe->who );
+ GWrite( &argv, sizeof(int) );
+ }
+}
+
+char **
+GRecvArgv()
+{
+ int num;
+
+ return GRecvStrArr( &num );
+}
+
diff --git a/kdm/backend/protodpy.c b/kdm/backend/protodpy.c
new file mode 100644
index 000000000..08c38fbd1
--- /dev/null
+++ b/kdm/backend/protodpy.c
@@ -0,0 +1,141 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the copyright holder.
+
+*/
+
+/*
+ * xdm - display manager daemon
+ * Author: Keith Packard, MIT X Consortium
+ *
+ * manage a collection of proto-displays. These are displays for
+ * which sessionID's have been generated, but no session has been
+ * started.
+ */
+
+#include <config.h>
+
+#ifdef XDMCP
+
+#include "dm.h"
+#include "dm_error.h"
+
+static struct protoDisplay *protoDisplays;
+
+struct protoDisplay *
+FindProtoDisplay(
+ XdmcpNetaddr address,
+ int addrlen,
+ CARD16 displayNumber )
+{
+ struct protoDisplay *pdpy;
+
+ Debug( "FindProtoDisplay\n" );
+ for (pdpy = protoDisplays; pdpy; pdpy=pdpy->next) {
+ if (pdpy->displayNumber == displayNumber &&
+ addressEqual( address, addrlen, pdpy->address, pdpy->addrlen ))
+ {
+ return pdpy;
+ }
+ }
+ return (struct protoDisplay *)0;
+}
+
+static void
+TimeoutProtoDisplays (void)
+{
+ struct protoDisplay *pdpy, *next;
+
+ for (pdpy = protoDisplays; pdpy; pdpy = next) {
+ next = pdpy->next;
+ if (pdpy->date < (unsigned long)(now - PROTO_TIMEOUT))
+ DisposeProtoDisplay( pdpy );
+ }
+}
+
+struct protoDisplay *
+NewProtoDisplay( XdmcpNetaddr address, int addrlen, CARD16 displayNumber,
+ CARD16 connectionType, ARRAY8Ptr connectionAddress,
+ CARD32 sessionID )
+{
+ struct protoDisplay *pdpy;
+
+ Debug( "NewProtoDisplay\n" );
+ TimeoutProtoDisplays ();
+ pdpy = (struct protoDisplay *)Malloc( sizeof(*pdpy) );
+ if (!pdpy)
+ return NULL;
+ pdpy->address = (XdmcpNetaddr)Malloc( addrlen );
+ if (!pdpy->address) {
+ free( (char *)pdpy );
+ return NULL;
+ }
+ pdpy->addrlen = addrlen;
+ memmove( pdpy->address, address, addrlen );
+ pdpy->displayNumber = displayNumber;
+ pdpy->connectionType = connectionType;
+ pdpy->date = now;
+ if (!XdmcpCopyARRAY8( connectionAddress, &pdpy->connectionAddress )) {
+ free( (char *)pdpy->address );
+ free( (char *)pdpy );
+ return NULL;
+ }
+ pdpy->sessionID = sessionID;
+ pdpy->fileAuthorization = (Xauth *)NULL;
+ pdpy->xdmcpAuthorization = (Xauth *)NULL;
+ pdpy->next = protoDisplays;
+ protoDisplays = pdpy;
+ return pdpy;
+}
+
+void
+DisposeProtoDisplay( pdpy )
+ struct protoDisplay *pdpy;
+{
+ struct protoDisplay *p, *prev;
+
+ prev = 0;
+ for (p = protoDisplays; p; p=p->next) {
+ if (p == pdpy)
+ break;
+ prev = p;
+ }
+ if (!p)
+ return;
+ if (prev)
+ prev->next = pdpy->next;
+ else
+ protoDisplays = pdpy->next;
+ bzero( &pdpy->key, sizeof(pdpy->key) );
+ if (pdpy->fileAuthorization)
+ XauDisposeAuth( pdpy->fileAuthorization );
+ if (pdpy->xdmcpAuthorization)
+ XauDisposeAuth( pdpy->xdmcpAuthorization );
+ XdmcpDisposeARRAY8( &pdpy->connectionAddress );
+ free( (char *)pdpy->address );
+ free( (char *)pdpy );
+}
+
+#endif /* XDMCP */
diff --git a/kdm/backend/reset.c b/kdm/backend/reset.c
new file mode 100644
index 000000000..2c2100870
--- /dev/null
+++ b/kdm/backend/reset.c
@@ -0,0 +1,111 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the copyright holder.
+
+*/
+
+/*
+ * xdm - display manager daemon
+ * Author: Keith Packard, MIT X Consortium
+ *
+ * pseudoReset -- pretend to reset the server by killing all clients
+ * with windows. It will reset the server most of the time, unless
+ * a client remains connected with no windows.
+ */
+
+#include "dm.h"
+#include "dm_error.h"
+
+#include <X11/Xlib.h>
+
+#include <signal.h>
+
+/*ARGSUSED*/
+static int
+ignoreErrors( Display *dspl ATTR_UNUSED, XErrorEvent *event ATTR_UNUSED )
+{
+ Debug( "ignoring error\n" );
+ return 0;
+}
+
+/*
+ * this is mostly bogus -- but quite useful. I wish the protocol
+ * had some way of enumerating and identifying clients, that way
+ * this code wouldn't have to be this kludgy.
+ */
+
+static void
+killWindows( Window window )
+{
+ Window root, parent, *children;
+ unsigned int child, nchildren = 0;
+
+ while (XQueryTree( dpy, window, &root, &parent, &children, &nchildren )
+ && nchildren > 0)
+ {
+ for (child = 0; child < nchildren; child++) {
+ Debug( "XKillClient %p\n", children[child] );
+ XKillClient( dpy, children[child] );
+ }
+ XFree( (char *)children );
+ }
+}
+
+static Jmp_buf resetJmp;
+
+/* ARGSUSED */
+static void
+abortReset( int n ATTR_UNUSED )
+{
+ Longjmp( resetJmp, 1 );
+}
+
+/*
+ * this display connection better not have any windows...
+ */
+
+void
+pseudoReset()
+{
+ int screen;
+
+ if (Setjmp( resetJmp )) {
+ LogError( "pseudoReset timeout\n" );
+ } else {
+ (void)Signal( SIGALRM, abortReset );
+ (void)alarm( 30 );
+ XSetErrorHandler( ignoreErrors );
+ for (screen = 0; screen < ScreenCount (dpy); screen++) {
+ Debug( "pseudoReset screen %d\n", screen );
+ killWindows( RootWindow( dpy, screen ) );
+ }
+ Debug( "before XSync\n" );
+ XSync( dpy, False );
+ (void)alarm( 0 );
+ }
+ Signal( SIGALRM, SIG_DFL );
+ XSetErrorHandler( (XErrorHandler)0 );
+ Debug( "pseudoReset done\n" );
+}
diff --git a/kdm/backend/resource.c b/kdm/backend/resource.c
new file mode 100644
index 000000000..f17db78ec
--- /dev/null
+++ b/kdm/backend/resource.c
@@ -0,0 +1,486 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2000-2005 Oswald Buddenhagen <ossi@kde.org>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the copyright holder.
+
+*/
+
+/*
+ * xdm - display manager daemon
+ * Author: Keith Packard, MIT X Consortium
+ *
+ * obtain configuration data
+ */
+
+#include "dm.h"
+#include "dm_error.h"
+
+#include <sys/stat.h>
+
+
+static char **originalArgv;
+
+static GProc getter;
+GTalk cnftalk;
+
+static void
+OpenGetter()
+{
+ GSet( &cnftalk );
+ if (!getter.pid) {
+ if (GOpen( &getter,
+ originalArgv, "_config", 0, strdup( "config reader" ),
+ 0 ))
+ LogPanic( "Cannot run config reader\n" );
+ Debug( "getter now ready\n" );
+ }
+}
+
+void
+CloseGetter()
+{
+ if (getter.pid) {
+ GSet( &cnftalk );
+ (void)GClose (&getter, 0, 0);
+ Debug( "getter now closed\n" );
+ }
+}
+
+/*
+ * ref-counted, unique-instance strings
+ */
+static RcStr *strs;
+
+/*
+ * make a ref-counted string of the argument. the new string will
+ * have a ref-count of 1. the passed string pointer is no longer valid.
+ */
+RcStr *
+newStr( char *str )
+{
+ RcStr *cs;
+
+ for (cs = strs; cs; cs = cs->next)
+ if (!strcmp( str, cs->str )) {
+ free( str );
+ cs->cnt++;
+ return cs;
+ }
+ if (!(cs = Malloc( sizeof(*cs) )))
+ return 0;
+ cs->cnt = 1;
+ cs->str = str;
+ cs->next = strs;
+ strs = cs;
+ return cs;
+}
+
+/*
+ * decrement ref-count and delete string when count drops to 0.
+ */
+void
+delStr( RcStr *str )
+{
+ RcStr **cs;
+
+ if (!str || --str->cnt)
+ return;
+ for (cs = &strs; *cs; cs = &((*cs)->next))
+ if (str == *cs) {
+ *cs = (*cs)->next;
+ free( (*cs)->str );
+ free( *cs );
+ break;
+ }
+}
+
+
+typedef struct CfgFile {
+ RcStr *name;
+ int depidx;
+ long deptime;
+} CfgFile;
+
+static int numCfgFiles;
+static CfgFile *cfgFiles;
+
+static int cfgMapT[] = {
+ GC_gGlobal,
+ GC_gDisplay,
+#ifdef XDMCP
+ GC_gXaccess,
+#endif
+};
+static int cfgMap[as(cfgMapT)];
+
+static int
+GetDeps()
+{
+ int ncf, i, dep, ret;
+ CfgFile *cf;
+
+ OpenGetter();
+ GSendInt( GC_Files );
+ ncf = GRecvInt();
+ if (!(cf = Malloc( ncf * sizeof(*cf) ))) {
+ CloseGetter();
+ return 0;
+ }
+ for (i = 0; i < ncf; i++) {
+ cf[i].name = newStr( GRecvStr() );
+ if ((dep = cf[i].depidx = GRecvInt()) != -1)
+ cf[i].deptime = mTime( cf[dep].name->str );
+ }
+ if (cfgFiles) {
+ for (i = 0; i < numCfgFiles; i++)
+ delStr( cfgFiles[i].name );
+ free( cfgFiles );
+ }
+ ret = 1;
+ cfgFiles = cf;
+ numCfgFiles = ncf;
+ for (i = 0; i < as(cfgMapT); i++) {
+ GSendInt( cfgMapT[i] );
+ if ((cfgMap[i] = GRecvInt()) < 0) {
+ LogError( "Config reader does not support config cathegory %#x\n",
+ cfgMapT[i] );
+ ret = 0;
+ }
+ }
+ GSendInt( -1 );
+ return ret;
+}
+
+static int
+checkDep( int idx )
+{
+ int dep;
+
+ if ((dep = cfgFiles[idx].depidx) == -1)
+ return 0;
+ if (checkDep( dep ))
+ return 1;
+ return mTime( cfgFiles[dep].name->str ) != cfgFiles[idx].deptime;
+}
+
+static int
+needsReScan( int what, CfgDep *dep )
+{
+ int widx, idx;
+ long mt;
+
+ for (widx = 0; cfgMapT[widx] != what; widx++);
+ idx = cfgMap[widx];
+ if (checkDep( idx )) {
+ if (!GetDeps())
+ return -1;
+ idx = cfgMap[widx];
+ }
+ mt = mTime( cfgFiles[idx].name->str );
+ if (dep->name != cfgFiles[idx].name) {
+ if (dep->name)
+ delStr( dep->name );
+ dep->name = cfgFiles[idx].name;
+ dep->name->cnt++;
+ dep->time = mt;
+ return 1;
+ } else if (dep->time != mt) {
+ dep->time = mt;
+ return 1;
+ } else
+ return 0;
+}
+
+int
+startConfig( int what, CfgDep *dep, int force )
+{
+ int ret;
+
+ if ((ret = needsReScan( what, dep )) < 0 || (!ret && !force))
+ return ret;
+ OpenGetter();
+ GSendInt( GC_GetConf );
+ GSendInt( what );
+ GSendStr( dep->name->str );
+ return 1;
+}
+
+static void
+LoadResources( CfgArr *conf )
+{
+ char **vptr, **pptr, *cptr;
+ long *iptr, i, id, nu, j, nptr, nint, nchr;
+
+ if (conf->data)
+ free( conf->data );
+ conf->numCfgEnt = GRecvInt();
+ nptr = GRecvInt();
+ nint = GRecvInt();
+ nchr = GRecvInt();
+ if (!(conf->data = Malloc( conf->numCfgEnt *
+ (sizeof(long) +
+ sizeof(char *)) +
+ nptr * sizeof(char *) +
+ nint * sizeof(long) +
+ nchr )))
+ {
+ CloseGetter();
+ return;
+ }
+ vptr = (char **)conf->data;
+ pptr = vptr + conf->numCfgEnt;
+ conf->idx = (long *)(pptr + nptr);
+ iptr = conf->idx + conf->numCfgEnt;
+ cptr = (char *)(iptr + nint);
+ for (i = 0; i < conf->numCfgEnt; i++) {
+ id = GRecvInt();
+ conf->idx[i] = id;
+ switch (id & C_TYPE_MASK) {
+ case C_TYPE_INT:
+ vptr[i] = (char *)((unsigned long)GRecvInt());
+ break;
+ case C_TYPE_STR:
+ vptr[i] = cptr;
+ cptr += GRecvStrBuf( cptr );
+ break;
+ case C_TYPE_ARGV:
+ nu = GRecvInt();
+ vptr[i] = (char *)pptr;
+ for (j = 0; j < nu; j++) {
+ *pptr++ = cptr;
+ cptr += GRecvStrBuf( cptr );
+ }
+ *pptr++ = (char *)0;
+ break;
+ default:
+ LogError( "Config reader supplied unknown data type in id %#x\n",
+ id );
+ break;
+ }
+ }
+}
+
+static void
+ApplyResource( int id, char **src, char **dst )
+{
+ switch (id & C_TYPE_MASK) {
+ case C_TYPE_INT:
+ *(int *)dst = *(long *)src;
+ break;
+ case C_TYPE_STR:
+ case C_TYPE_ARGV:
+ *dst = *src;
+ break;
+ }
+}
+
+
+#define boffset(f) XtOffsetOf(struct display, f)
+
+/* no global variables exported currently
+struct globEnts {
+ int id;
+ char **off;
+} globEnt[] = {
+};
+ */
+
+/* no per-display variables exported currently
+struct dpyEnts {
+ int id;
+ int off;
+} dpyEnt[] = {
+};
+ */
+
+CfgArr cfg;
+
+char **
+FindCfgEnt( struct display *d, int id )
+{
+ int i;
+
+/* no global variables exported currently
+ for (i = 0; i < as(globEnt); i++)
+ if (globEnt[i].id == id)
+ return globEnt[i].off;
+ */
+ for (i = 0; i < cfg.numCfgEnt; i++)
+ if (cfg.idx[i] == id)
+ return ((char **)cfg.data) + i;
+ if (d) {
+/* no per-display variables exported currently
+ for (i = 0; i < as(dpyEnt); i++)
+ if (dpyEnt[i].id == id)
+ return (char **)(((char *)d) + dpyEnt[i].off);
+ */
+ for (i = 0; i < d->cfg.numCfgEnt; i++)
+ if (d->cfg.idx[i] == id)
+ return ((char **)d->cfg.data) + i;
+ }
+ Debug( "unknown config entry %#x requested\n", id );
+ return (char **)0;
+}
+
+
+CONF_CORE_GLOBAL_DEFS
+
+struct globVals {
+ int id;
+ char **off;
+} globVal[] = {
+CONF_CORE_GLOBALS
+};
+
+int
+LoadDMResources( int force )
+{
+ int i, ret;
+ char **ent;
+
+ if (Setjmp( cnftalk.errjmp ))
+ return -1; /* may memleak, but we probably have to abort anyway */
+ if ((ret = startConfig( GC_gGlobal, &cfg.dep, force )) <= 0)
+ return ret;
+ LoadResources( &cfg );
+/* Debug( "manager resources: %[*x\n",
+ cfg.numCfgEnt, ((char **)cfg.data) + cfg.numCfgEnt );*/
+ ret = 1;
+ for (i = 0; i < as(globVal); i++) {
+ if (!(ent = FindCfgEnt( 0, globVal[i].id )))
+ ret = -1;
+ else
+ ApplyResource( globVal[i].id, ent, globVal[i].off );
+ }
+ if (ret < 0)
+ LogError( "Internal error: config reader supplied incomplete data\n" );
+ return ret;
+}
+
+
+struct dpyVals {
+ int id;
+ int off;
+} dpyVal[] = {
+CONF_CORE_LOCALS
+};
+
+int
+LoadDisplayResources( struct display *d )
+{
+ int i, ret;
+ char **ent;
+
+ if (Setjmp( cnftalk.errjmp ))
+ return -1; /* may memleak */
+ if ((ret = startConfig( GC_gDisplay, &d->cfg.dep, FALSE )) <= 0)
+ return ret;
+ GSendStr( d->name );
+ GSendStr( d->class2 );
+ LoadResources( &d->cfg );
+/* Debug( "display(%s, %s) resources: %[*x\n", d->name, d->class2,
+ d->cfg.numCfgEnt, ((char **)d->cfg.data) + d->cfg.numCfgEnt );*/
+ ret = 1;
+ for (i = 0; i < as(dpyVal); i++) {
+ if (!(ent = FindCfgEnt( d, dpyVal[i].id )))
+ ret = -1;
+ else
+ ApplyResource( dpyVal[i].id, ent,
+ (char **)(((char *)d) + dpyVal[i].off) );
+ }
+ if (ret < 0)
+ LogError( "Internal error: config reader supplied incomplete data\n" );
+ return ret;
+}
+
+int
+InitResources( char **argv )
+{
+ originalArgv = argv;
+ cnftalk.pipe = &getter.pipe;
+ if (Setjmp( cnftalk.errjmp ))
+ return 0; /* may memleak */
+ return GetDeps();
+}
+
+static void
+addServers( char **srv, int bType )
+{
+ char *name, *class2;
+ const char *dtx, *cls;
+ struct display *d;
+
+ for (; *srv; srv++) {
+ if ((cls = strchr( *srv, '_' ))) {
+ if (!StrNDup( &name, *srv, cls - *srv ))
+ return;
+ if (!StrDup( &class2, cls )) {
+ free( name );
+ return;
+ }
+ } else {
+ if (!StrDup( &name, *srv ))
+ return;
+ class2 = 0;
+ }
+ if ((d = FindDisplayByName( name ))) {
+ if (d->class2)
+ free( d->class2 );
+ dtx = "existing";
+ } else {
+ if (!(d = NewDisplay( name ))) {
+ free( name );
+ if (class2)
+ free( class2 );
+ return;
+ }
+ dtx = "new";
+ }
+ d->stillThere = 1;
+ d->class2 = class2;
+ d->displayType = (*name == ':' ? dLocal : dForeign) | bType;
+ if ((bType & d_lifetime) == dReserve) {
+ if (d->status == notRunning)
+ d->status = reserve;
+ } else {
+ if (d->status == reserve)
+ d->status = notRunning;
+ }
+ Debug( "found %s %s%s display: %s %s\n", dtx,
+ ((d->displayType & d_location) == dLocal) ? "local" : "foreign",
+ ((d->displayType & d_lifetime) == dReserve) ? " reserve" : "",
+ d->name, d->class2 );
+ free( name );
+ }
+}
+
+void
+ScanServers( void )
+{
+ Debug( "ScanServers\n" );
+ addServers( staticServers, dFromFile | dPermanent );
+ addServers( reserveServers, dFromFile | dReserve );
+}
+
diff --git a/kdm/backend/rpcauth.c b/kdm/backend/rpcauth.c
new file mode 100644
index 000000000..1186f72c2
--- /dev/null
+++ b/kdm/backend/rpcauth.c
@@ -0,0 +1,89 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the copyright holder.
+
+*/
+
+/*
+ * xdm - display manager daemon
+ * Author: Keith Packard, MIT X Consortium
+ *
+ * generate SecureRPC authorization records
+ */
+
+#include <config.h>
+
+#ifdef SECURE_RPC
+
+#include "dm.h"
+#include "dm_auth.h"
+#include "dm_error.h"
+
+#include <rpc/rpc.h>
+#include <rpc/key_prot.h>
+
+/*ARGSUSED*/
+void
+SecureRPCInitAuth( unsigned short name_len ATTR_UNUSED,
+ const char *name ATTR_UNUSED )
+{
+}
+
+Xauth *
+SecureRPCGetAuth( unsigned short namelen, const char *name )
+{
+ Xauth *new;
+ char key[MAXNETNAMELEN+1];
+
+ new = (Xauth *)Malloc( sizeof(*new) );
+ if (!new)
+ return (Xauth *)0;
+ new->family = FamilyWild;
+ new->address_length = 0;
+ new->address = 0;
+ new->number_length = 0;
+ new->number = 0;
+
+ getnetname( key );
+ Debug( "system netname %s\n", key );
+ new->data_length = strlen( key );
+ new->data = (char *)Malloc( new->data_length );
+ if (!new->data) {
+ free( (char *)new );
+ return (Xauth *)0;
+ }
+ new->name = (char *)Malloc( namelen );
+ if (!new->name) {
+ free( (char *)new->data );
+ free( (char *)new );
+ return (Xauth *)0;
+ }
+ memmove( new->name, name, namelen );
+ new->name_length = namelen;
+ memmove( new->data, key, new->data_length );
+ return new;
+}
+
+#endif
diff --git a/kdm/backend/server.c b/kdm/backend/server.c
new file mode 100644
index 000000000..129c8bfe1
--- /dev/null
+++ b/kdm/backend/server.c
@@ -0,0 +1,360 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2001,2003,2005 Oswald Buddenhagen <ossi@kde.org>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the copyright holder.
+
+*/
+
+/*
+ * xdm - display manager daemon
+ * Author: Keith Packard, MIT X Consortium
+ *
+ * server.c - manage the X server
+ */
+
+#include "dm.h"
+#include "dm_error.h"
+#include "dm_socket.h"
+
+#include <X11/Xlib.h>
+
+#include <stdio.h>
+#include <signal.h>
+
+
+struct display *startingServer;
+time_t serverTimeout = TO_INF;
+
+char **
+PrepServerArgv( struct display *d, const char *args )
+{
+ char **argv;
+#ifdef HAVE_VTS
+ char vtstr[8];
+#endif
+
+ if (!(argv = parseArgs( 0, d->serverCmd )) ||
+ !(argv = parseArgs( argv, args )) ||
+ !(argv = addStrArr( argv, d->name, -1 )))
+ exit( 47 );
+#ifdef HAVE_VTS
+ if (d->serverVT &&
+ !(argv = addStrArr( argv, vtstr,
+ sprintf( vtstr, "vt%d", d->serverVT ) )))
+ exit( 47 );
+#endif
+ return argv;
+}
+
+static void
+StartServerOnce( void )
+{
+ struct display *d = startingServer;
+ char **argv;
+ int pid;
+
+ Debug( "StartServerOnce for %s, try %d\n", d->name, ++d->startTries );
+ d->serverStatus = starting;
+ switch (pid = Fork()) {
+ case 0:
+ argv = PrepServerArgv( d, d->serverArgsLocal );
+ if (d->authFile) {
+ if (!(argv = addStrArr( argv, "-auth", 5 )) ||
+ !(argv = addStrArr( argv, d->authFile, -1 )))
+ exit( 47 );
+ }
+ Debug( "exec %\"[s\n", argv );
+ /*
+ * give the server SIGUSR1 ignored,
+ * it will notice that and send SIGUSR1
+ * when ready
+ */
+ (void)Signal( SIGUSR1, SIG_IGN );
+ (void)execv( argv[0], argv );
+ LogError( "X server %\"s cannot be executed\n", argv[0] );
+ exit( 47 );
+ case -1:
+ LogError( "X server fork failed\n" );
+ StartServerFailed();
+ break;
+ default:
+ Debug( "X server forked, pid %d\n", pid );
+ d->serverPid = pid;
+ serverTimeout = d->serverTimeout + now;
+ break;
+ }
+}
+
+void
+StartServer( struct display *d )
+{
+ startingServer = d;
+ d->startTries = 0;
+ StartServerOnce();
+}
+
+void
+AbortStartServer( struct display *d )
+{
+ if (startingServer == d)
+ {
+ if (d->serverStatus != ignore)
+ {
+ d->serverStatus = ignore;
+ serverTimeout = TO_INF;
+ Debug( "aborting X server start\n" );
+ }
+ startingServer = 0;
+ }
+}
+
+void
+StartServerSuccess()
+{
+ struct display *d = startingServer;
+ d->serverStatus = ignore;
+ serverTimeout = TO_INF;
+ Debug( "X server ready, starting session\n" );
+ StartDisplayP2( d );
+}
+
+void
+StartServerFailed()
+{
+ struct display *d = startingServer;
+ if (!d->serverAttempts || d->startTries < d->serverAttempts) {
+ d->serverStatus = pausing;
+ serverTimeout = d->openDelay + now;
+ } else {
+ d->serverStatus = ignore;
+ serverTimeout = TO_INF;
+ startingServer = 0;
+ LogError( "X server for display %s can't be started,"
+ " session disabled\n", d->name );
+ StopDisplay( d );
+ }
+}
+
+void
+StartServerTimeout()
+{
+ struct display *d = startingServer;
+ switch (d->serverStatus) {
+ case ignore:
+ case awaiting:
+ break; /* cannot happen */
+ case starting:
+ LogError( "X server startup timeout, terminating\n" );
+ kill( d->serverPid, d->termSignal );
+ d->serverStatus = d->termSignal == SIGKILL ? killed : terminated;
+ serverTimeout = d->serverTimeout + now;
+ break;
+ case terminated:
+ LogInfo( "X server termination timeout, killing\n" );
+ kill( d->serverPid, SIGKILL );
+ d->serverStatus = killed;
+ serverTimeout = 10 + now;
+ break;
+ case killed:
+ LogInfo( "X server is stuck in D state; leaving it alone\n" );
+ StartServerFailed();
+ break;
+ case pausing:
+ StartServerOnce();
+ break;
+ }
+}
+
+
+Display *dpy;
+
+/*
+ * this code is complicated by some TCP failings. On
+ * many systems, the connect will occasionally hang forever,
+ * this trouble is avoided by setting up a timeout to Longjmp
+ * out of the connect (possibly leaving piles of garbage around
+ * inside Xlib) and give up, terminating the server.
+ */
+
+static Jmp_buf openAbort;
+
+/* ARGSUSED */
+static void
+abortOpen( int n ATTR_UNUSED )
+{
+ Longjmp( openAbort, 1 );
+}
+
+#ifdef XDMCP
+
+#ifdef STREAMSCONN
+# include <tiuser.h>
+#endif
+
+static void
+GetRemoteAddress( struct display *d, int fd )
+{
+ char buf[512];
+ int len = sizeof(buf);
+#ifdef STREAMSCONN
+ struct netbuf netb;
+#endif
+
+ XdmcpDisposeARRAY8( &d->peer );
+#ifdef STREAMSCONN
+ netb.maxlen = sizeof(buf);
+ netb.buf = buf;
+ t_getname( fd, &netb, REMOTENAME );
+ len = 8;
+ /* lucky for us, t_getname returns something that looks like a sockaddr */
+#else
+ getpeername( fd, (struct sockaddr *)buf, (void *)&len );
+#endif
+ if (len && XdmcpAllocARRAY8( &d->peer, len ))
+ memmove( (char *)d->peer.data, buf, len );
+ Debug( "got remote address %s %d\n", d->name, d->peer.length );
+}
+
+#endif /* XDMCP */
+
+static int
+openErrorHandler( Display *dspl ATTR_UNUSED )
+{
+ LogError( "IO Error in XOpenDisplay\n" );
+ exit( EX_OPENFAILED_DPY );
+ /*NOTREACHED*/
+ return (0);
+}
+
+void
+WaitForServer( struct display *d )
+{
+ volatile int i;
+ /* static int i; */
+
+ i = 0;
+ do {
+ (void)Signal( SIGALRM, abortOpen );
+ (void)alarm( (unsigned)d->openTimeout );
+ if (!Setjmp( openAbort )) {
+ Debug( "before XOpenDisplay(%s)\n", d->name );
+ errno = 0;
+ (void)XSetIOErrorHandler( openErrorHandler );
+ dpy = XOpenDisplay( d->name );
+#ifdef STREAMSCONN
+ {
+ /* For some reason, the next XOpenDisplay we do is
+ going to fail, so we might as well get that out
+ of the way. There is something broken here. */
+ Display *bogusDpy = XOpenDisplay( d->name );
+ Debug( "bogus XOpenDisplay %s\n",
+ bogusDpy ? "succeeded" : "failed" );
+ if (bogusDpy) XCloseDisplay( bogusDpy ); /* just in case */
+ }
+#endif
+ (void)alarm( (unsigned)0 );
+ (void)Signal( SIGALRM, SIG_DFL );
+ (void)XSetIOErrorHandler( (int (*)( Display * )) 0 );
+ Debug( "after XOpenDisplay(%s)\n", d->name );
+ if (dpy) {
+#ifdef XDMCP
+ if ((d->displayType & d_location) == dForeign)
+ GetRemoteAddress( d, ConnectionNumber( dpy ) );
+#endif
+ RegisterCloseOnFork( ConnectionNumber( dpy ) );
+ return;
+ }
+ Debug( "OpenDisplay(%s) attempt %d failed: %m\n", d->name, i + 1 );
+ sleep( (unsigned)d->openDelay );
+ } else {
+ LogError( "Hung in XOpenDisplay(%s), aborting\n", d->name );
+ (void)Signal( SIGALRM, SIG_DFL );
+ break;
+ }
+ } while (++i < d->openRepeat);
+ LogError( "Cannot connect to %s, giving up\n", d->name );
+ exit( EX_OPENFAILED_DPY );
+}
+
+
+void
+ResetServer( struct display *d )
+{
+ if (dpy && (d->displayType & d_origin) != dFromXDMCP)
+ pseudoReset();
+}
+
+
+static Jmp_buf pingTime;
+
+static void
+PingLost( void )
+{
+ Longjmp( pingTime, 1 );
+}
+
+/* ARGSUSED */
+static int
+PingLostIOErr( Display *dspl ATTR_UNUSED )
+{
+ PingLost();
+ return 0;
+}
+
+/* ARGSUSED */
+static void
+PingLostSig( int n ATTR_UNUSED )
+{
+ PingLost();
+}
+
+int
+PingServer( struct display *d )
+{
+ int (*oldError)( Display * );
+ void (*oldSig)( int );
+ int oldAlarm;
+
+ oldError = XSetIOErrorHandler( PingLostIOErr );
+ oldAlarm = alarm( 0 );
+ oldSig = Signal( SIGALRM, PingLostSig );
+ (void)alarm( d->pingTimeout * 60 );
+ if (!Setjmp( pingTime )) {
+ Debug( "ping X server\n" );
+ XSync( dpy, 0 );
+ } else {
+ Debug( "X server dead\n" );
+ (void)alarm( 0 );
+ (void)Signal( SIGALRM, SIG_DFL );
+ XSetIOErrorHandler( oldError );
+ return 0;
+ }
+ (void)alarm( 0 );
+ (void)Signal( SIGALRM, oldSig );
+ (void)alarm( oldAlarm );
+ Debug( "X server alive\n" );
+ XSetIOErrorHandler( oldError );
+ return 1;
+}
diff --git a/kdm/backend/session.c b/kdm/backend/session.c
new file mode 100644
index 000000000..72da0a820
--- /dev/null
+++ b/kdm/backend/session.c
@@ -0,0 +1,792 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2000-2004 Oswald Buddenhagen <ossi@kde.org>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the copyright holder.
+
+*/
+
+/*
+ * xdm - display manager daemon
+ * Author: Keith Packard, MIT X Consortium
+ *
+ * subdaemon event loop, etc.
+ */
+
+#include "dm.h"
+#include "dm_error.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/cursorfont.h>
+
+#include <stdio.h>
+#include <ctype.h>
+#include <signal.h>
+
+struct display *td;
+const char *td_setup = "auto";
+
+static void DeleteXloginResources( void );
+static void LoadXloginResources( void );
+static void SetupDisplay( const char *arg );
+
+
+static Jmp_buf pingTime;
+
+/* ARGSUSED */
+static void
+catchAlrm( int n ATTR_UNUSED )
+{
+ Longjmp( pingTime, 1 );
+}
+
+static Jmp_buf tenaciousClient;
+
+/* ARGSUSED */
+static void
+waitAbort( int n ATTR_UNUSED )
+{
+ Longjmp( tenaciousClient, 1 );
+}
+
+static void
+AbortClient( int pid )
+{
+ int sig = SIGTERM;
+ volatile int i;
+ int retId;
+
+ for (i = 0; i < 4; i++) {
+ if (kill( -pid, sig ) == -1) {
+ switch (errno) {
+ case EPERM:
+ LogError( "Can't kill client\n" );
+ case EINVAL:
+ case ESRCH:
+ return;
+ }
+ }
+ if (!Setjmp( tenaciousClient )) {
+ (void)Signal( SIGALRM, waitAbort );
+ (void)alarm( (unsigned)10 );
+ retId = wait( (waitType *)0 );
+ (void)alarm( (unsigned)0 );
+ (void)Signal( SIGALRM, SIG_DFL );
+ if (retId == pid)
+ break;
+ } else
+ (void)Signal( SIGALRM, SIG_DFL );
+ sig = SIGKILL;
+ }
+}
+
+
+static char *
+conv_auto( int what, const char *prompt ATTR_UNUSED )
+{
+ switch (what) {
+ case GCONV_USER:
+ return curuser;
+ case GCONV_PASS:
+ case GCONV_PASS_ND:
+ return curpass;
+ default:
+ LogError( "Unknown authentication data type requested for autologin.\n" );
+ return 0;
+ }
+}
+
+static void
+DoAutoLogon( void )
+{
+ ReStr( &curuser, td->autoUser );
+ ReStr( &curpass, td->autoPass );
+ ReStr( &curtype, "classic" );
+ cursource = PWSRC_AUTOLOGIN;
+}
+
+static int
+AutoLogon( Time_t tdiff )
+{
+ Debug( "autoLogon, tdiff = %d, rLogin = %d, goodexit = %d, nuser = %s\n",
+ tdiff, td->hstent->rLogin, td->hstent->goodExit, td->hstent->nuser );
+ if (td->hstent->rLogin == 2 ||
+ (td->hstent->rLogin == 1 &&
+ tdiff <= 0 && !td->hstent->goodExit && !td->hstent->lock))
+ {
+ curuser = td->hstent->nuser;
+ td->hstent->nuser = 0;
+ curpass = td->hstent->npass;
+ td->hstent->npass = 0;
+ newdmrc = td->hstent->nargs;
+ td->hstent->nargs = 0;
+ ReStr( &curtype, "classic" );
+ cursource = (td->hstent->rLogin == 1) ? PWSRC_RELOGIN : PWSRC_MANUAL;
+ return 1;
+ } else if (*td->autoUser && !td->autoDelay &&
+ ((tdiff > 0 && ((td->displayType & d_lifetime) == dTransient ||
+ !td->hstent->lastExit)) ||
+ td->autoAgain))
+ {
+ unsigned int lmask;
+ Window dummy1, dummy2;
+ int dummy3, dummy4, dummy5, dummy6;
+ XQueryPointer( dpy, DefaultRootWindow( dpy ),
+ &dummy1, &dummy2, &dummy3, &dummy4, &dummy5, &dummy6,
+ &lmask );
+ if (lmask & ShiftMask)
+ return 0;
+ DoAutoLogon();
+ return 1;
+ }
+ return 0;
+}
+
+
+static const struct {
+ int vcode, echo, ndelay;
+} grqs[] = {
+ { V_GET_TEXT, TRUE, FALSE },
+ { V_GET_TEXT, FALSE, FALSE },
+ { V_GET_TEXT, TRUE, FALSE },
+ { V_GET_TEXT, FALSE, FALSE },
+ { V_GET_TEXT, FALSE, TRUE },
+ { V_GET_BINARY, 0, 0 }
+};
+
+char *
+conv_interact( int what, const char *prompt )
+{
+ char *ret;
+ int tag;
+
+ GSendInt( grqs[what].vcode );
+ if (what == GCONV_BINARY) {
+ unsigned const char *up = (unsigned const char *)prompt;
+ int len = up[3] | (up[2] << 8) | (up[1] << 16) | (up[0] << 24);
+ GSendArr( len, prompt );
+ GSendInt( FALSE ); /* ndelay */
+ return GRecvArr( &len );
+ } else {
+ GSendStr( prompt );
+ GSendInt( grqs[what].echo );
+ GSendInt( grqs[what].ndelay );
+ ret = GRecvStr();
+ if (ret) {
+ tag = GRecvInt();
+ switch (what) {
+ case GCONV_USER:
+ /* assert(tag & V_IS_USER); */
+ if (curuser)
+ free( curuser );
+ curuser = ret;
+ break;
+ case GCONV_PASS:
+ case GCONV_PASS_ND:
+ /* assert(tag & V_IS_PASSWORD); */
+ if (curpass)
+ free( curpass );
+ curpass = ret;
+ break;
+ default:
+ if (tag & V_IS_USER)
+ ReStr( &curuser, ret );
+ else if (tag & V_IS_PASSWORD)
+ ReStr( &curpass, ret );
+ else if (tag & V_IS_NEWPASSWORD)
+ ReStr( &newpass, ret );
+ else if (tag & V_IS_OLDPASSWORD)
+ ReStr( &ret, curpass );
+ }
+ }
+ return ret;
+ }
+}
+
+static int greeter;
+GProc grtproc;
+GTalk grttalk;
+
+GTalk mstrtalk; /* make static; see dm.c */
+
+int
+CtrlGreeterWait( int wreply )
+{
+ int i, cmd, type, rootok;
+ char *name, *pass, **avptr;
+#ifdef XDMCP
+ ARRAY8Ptr aptr;
+#endif
+
+ if (Setjmp( mstrtalk.errjmp )) {
+ CloseGreeter( TRUE );
+ SessionExit( EX_UNMANAGE_DPY );
+ }
+
+ while (GRecvCmd( &cmd )) {
+ switch (cmd)
+ {
+ case G_Ready:
+ Debug( "G_Ready\n" );
+ return 0;
+ case G_GetCfg:
+ /*Debug ("G_GetCfg\n");*/
+ type = GRecvInt();
+ /*Debug (" index %#x\n", type);*/
+ if (type == C_isLocal)
+ i = (td->displayType & d_location) == dLocal;
+ else if (type == C_hasConsole)
+#ifdef HAVE_VTS
+ i = *consoleTTYs != 0;
+#else
+ i = td->console != 0;
+#endif
+ else if (type == C_isAuthorized)
+ i = td->authorizations != 0;
+ else
+ goto normal;
+ GSendInt( GE_Ok );
+ /*Debug (" -> bool %d\n", i);*/
+ GSendInt( i );
+ break;
+ normal:
+ if (!(avptr = FindCfgEnt( td, type ))) {
+ /*Debug (" -> not found\n");*/
+ GSendInt( GE_NoEnt );
+ break;
+ }
+ switch (type & C_TYPE_MASK) {
+ default:
+ /*Debug (" -> unknown type\n");*/
+ GSendInt( GE_BadType );
+ break;
+ case C_TYPE_INT:
+ case C_TYPE_STR:
+ case C_TYPE_ARGV:
+#ifdef XDMCP
+ case C_TYPE_ARR:
+#endif
+ GSendInt( GE_Ok );
+ switch (type & C_TYPE_MASK) {
+ case C_TYPE_INT:
+ /*Debug (" -> int %#x (%d)\n", *(int *)avptr, *(int *)avptr);*/
+ GSendInt( *(long *)avptr );
+ break;
+ case C_TYPE_STR:
+ /*Debug (" -> string %\"s\n", *avptr);*/
+ GSendStr( *avptr );
+ break;
+ case C_TYPE_ARGV:
+ /*Debug (" -> sending argv %\"[{s\n", *(char ***)avptr);*/
+ GSendArgv( *(char ***)avptr );
+ break;
+#ifdef XDMCP
+ case C_TYPE_ARR:
+ aptr = *(ARRAY8Ptr *)avptr;
+ /*Debug (" -> sending array %02[*:hhx\n",
+ aptr->length, aptr->data);*/
+ GSendArr( aptr->length, (char *)aptr->data );
+ break;
+#endif
+ }
+ break;
+ }
+ break;
+ case G_ReadDmrc:
+ Debug( "G_ReadDmrc\n" );
+ name = GRecvStr();
+ Debug( " user %\"s\n", name );
+ if (StrCmp( dmrcuser, name )) {
+ if (curdmrc) { free( curdmrc ); curdmrc = 0; }
+ if (dmrcuser)
+ free( dmrcuser );
+ dmrcuser = name;
+ i = ReadDmrc();
+ Debug( " -> status %d\n", i );
+ GSendInt( i );
+ Debug( " => %\"s\n", curdmrc );
+ } else {
+ if (name)
+ free( name );
+ Debug( " -> status " stringify( GE_Ok ) "\n" );
+ GSendInt( GE_Ok );
+ Debug( " => keeping old\n" );
+ }
+ break;
+ case G_GetDmrc:
+ Debug( "G_GetDmrc\n" );
+ name = GRecvStr();
+ Debug( " key %\"s\n", name );
+ pass = iniEntry( curdmrc, "Desktop", name, 0 );
+ Debug( " -> %\"s\n", pass );
+ GSendStr( pass );
+ if (pass)
+ free( pass );
+ free( name );
+ break;
+/* case G_ResetDmrc:
+ Debug ("G_ResetDmrc\n");
+ if (newdmrc) { free (newdmrc); newdmrc = 0; }
+ break; */
+ case G_PutDmrc:
+ Debug( "G_PutDmrc\n" );
+ name = GRecvStr();
+ Debug( " key %\"s\n", name );
+ pass = GRecvStr();
+ Debug( " value %\"s\n", pass );
+ newdmrc = iniEntry( newdmrc, "Desktop", name, pass );
+ free( pass );
+ free( name );
+ break;
+ case G_VerifyRootOK:
+ Debug( "G_VerifyRootOK\n" );
+ rootok = TRUE;
+ goto doverify;
+ case G_Verify:
+ Debug( "G_Verify\n" );
+ rootok = FALSE;
+ doverify:
+ if (curuser) { free( curuser ); curuser = 0; }
+ if (curpass) { free( curpass ); curpass = 0; }
+ if (curtype) free( curtype );
+ curtype = GRecvStr();
+ Debug( " type %\"s\n", curtype );
+ cursource = PWSRC_MANUAL;
+ if (Verify( conv_interact, rootok )) {
+ Debug( " -> return success\n" );
+ GSendInt( V_OK );
+ } else
+ Debug( " -> failure returned\n" );
+ break;
+ case G_AutoLogin:
+ Debug( "G_AutoLogin\n" );
+ DoAutoLogon();
+ if (Verify( conv_auto, FALSE )) {
+ Debug( " -> return success\n" );
+ GSendInt( V_OK );
+ } else
+ Debug( " -> failure returned\n" );
+ break;
+ case G_SetupDpy:
+ Debug( "G_SetupDpy\n" );
+ SetupDisplay( 0 );
+ td_setup = 0;
+ GSendInt( 0 );
+ break;
+ default:
+ return cmd;
+ }
+ if (!wreply)
+ return -1;
+ }
+ Debug( "lost connection to greeter\n" );
+ return -2;
+}
+
+void
+OpenGreeter()
+{
+ char *name, **env;
+ static Time_t lastStart;
+ int cmd;
+ Cursor xcursor;
+
+ GSet( &grttalk );
+ if (greeter)
+ return;
+ if (time( 0 ) < lastStart + 10) /* XXX should use some readiness indicator instead */
+ SessionExit( EX_UNMANAGE_DPY );
+ greeter = 1;
+ ASPrintf( &name, "greeter for display %s", td->name );
+ Debug( "starting %s\n", name );
+
+ /* Hourglass cursor */
+ if ((xcursor = XCreateFontCursor( dpy, XC_watch ))) {
+ XDefineCursor( dpy, DefaultRootWindow( dpy ), xcursor );
+ XFreeCursor( dpy, xcursor );
+ }
+ XFlush( dpy );
+
+ /* Load system default Resources (if any) */
+ LoadXloginResources();
+
+ grttalk.pipe = &grtproc.pipe;
+ env = systemEnv( (char *)0 );
+ if (GOpen( &grtproc, (char **)0, "_greet", env, name, &td->gpipe ))
+ SessionExit( EX_UNMANAGE_DPY );
+ freeStrArr( env );
+ if ((cmd = CtrlGreeterWait( TRUE ))) {
+ if (cmd != -2)
+ LogError( "Received unknown or unexpected command %d from greeter\n", cmd );
+ CloseGreeter( TRUE );
+ SessionExit( EX_UNMANAGE_DPY );
+ }
+ Debug( "%s ready\n", name );
+ time( &lastStart );
+}
+
+int
+CloseGreeter( int force )
+{
+ int ret;
+
+ if (!greeter)
+ return EX_NORMAL;
+ greeter = 0;
+ ret = GClose (&grtproc, 0, force);
+ Debug( "greeter for %s stopped\n", td->name );
+ if (WaitCode( ret ) > EX_NORMAL && WaitCode( ret ) <= EX_MAX) {
+ Debug( "greeter-initiated session exit, code %d\n", WaitCode( ret ) );
+ SessionExit( WaitCode( ret ) );
+ }
+ return ret;
+}
+
+void
+PrepErrorGreet()
+{
+ if (!greeter) {
+ OpenGreeter();
+ GSendInt( G_ErrorGreet );
+ GSendStr( curuser );
+ }
+}
+
+static Jmp_buf idleTOJmp;
+
+/* ARGSUSED */
+static void
+IdleTOJmp( int n ATTR_UNUSED )
+{
+ Longjmp( idleTOJmp, 1 );
+}
+
+
+static Jmp_buf abortSession;
+
+/* ARGSUSED */
+static void
+catchTerm( int n ATTR_UNUSED )
+{
+ Signal( SIGTERM, SIG_IGN );
+ Longjmp( abortSession, EX_AL_RESERVER_DPY );
+}
+
+/*
+ * We need our own error handlers because we can't be sure what exit code Xlib
+ * will use, and our Xlib does exit(1) which matches EX_REMANAGE_DPY, which
+ * can cause a race condition leaving the display wedged. We need to use
+ * EX_RESERVER_DPY for IO errors, to ensure that the manager waits for the
+ * server to terminate. For other X errors, we should give up.
+ */
+
+/*ARGSUSED*/
+static int
+IOErrorHandler( Display *dspl ATTR_UNUSED )
+{
+ LogError( "Fatal X server IO error: %m\n" );
+ /* The only X interaction during the session are pings, and those
+ have an own IOErrorHandler -> not EX_AL_RESERVER_DPY */
+ Longjmp( abortSession, EX_RESERVER_DPY );
+ /*NOTREACHED*/
+ return 0;
+}
+
+/*ARGSUSED*/
+static int
+ErrorHandler( Display *dspl ATTR_UNUSED, XErrorEvent *event )
+{
+ LogError( "X error\n" );
+ if (event->error_code == BadImplementation)
+ Longjmp( abortSession, EX_UNMANAGE_DPY );
+ return 0;
+}
+
+void
+ManageSession( struct display *d )
+{
+ int ex, cmd;
+ volatile int clientPid = 0;
+ volatile Time_t tdiff = 0;
+
+ td = d;
+ Debug( "ManageSession %s\n", d->name );
+ if ((ex = Setjmp( abortSession ))) {
+ CloseGreeter( TRUE );
+ if (clientPid)
+ AbortClient( clientPid );
+ SessionExit( ex );
+ /* NOTREACHED */
+ }
+ (void)XSetIOErrorHandler( IOErrorHandler );
+ (void)XSetErrorHandler( ErrorHandler );
+ (void)Signal( SIGTERM, catchTerm );
+
+ (void)Signal( SIGHUP, SIG_IGN );
+
+ if (Setjmp( grttalk.errjmp ))
+ Longjmp( abortSession, EX_RESERVER_DPY ); /* EX_RETRY_ONCE */
+
+#ifdef XDMCP
+ if (d->useChooser)
+ DoChoose();
+ /* NOTREACHED */
+#endif
+
+ if (d->hstent->sdRec.how) {
+ OpenGreeter();
+ GSendInt( G_ConfShutdown );
+ GSendInt( d->hstent->sdRec.how );
+ GSendInt( d->hstent->sdRec.uid );
+ GSendStr( d->hstent->sdRec.osname );
+ if ((cmd = CtrlGreeterWait( TRUE )) != G_Ready) {
+ LogError( "Received unknown command %d from greeter\n", cmd );
+ CloseGreeter( TRUE );
+ }
+ goto regreet;
+ }
+
+ tdiff = time( 0 ) - td->hstent->lastExit - td->openDelay;
+ if (AutoLogon( tdiff )) {
+ if (!Verify( conv_auto, FALSE ))
+ goto gcont;
+ if (greeter)
+ GSendInt( V_OK );
+ } else {
+ regreet:
+ OpenGreeter();
+ if (Setjmp( idleTOJmp )) {
+ CloseGreeter( TRUE );
+ SessionExit( EX_NORMAL );
+ }
+ Signal( SIGALRM, IdleTOJmp );
+ alarm( td->idleTimeout );
+#ifdef XDMCP
+ if (((d->displayType & d_location) == dLocal) &&
+ d->loginMode >= LOGIN_DEFAULT_REMOTE)
+ goto choose;
+#endif
+ for (;;) {
+ Debug( "ManageSession, greeting, tdiff = %d\n", tdiff );
+ GSendInt( (*td->autoUser && td->autoDelay &&
+ (tdiff > 0 || td->autoAgain)) ?
+ G_GreetTimed : G_Greet );
+ gcont:
+ cmd = CtrlGreeterWait( TRUE );
+#ifdef XDMCP
+ recmd:
+ if (cmd == G_DChoose) {
+ choose:
+ cmd = DoChoose();
+ goto recmd;
+ }
+ if (cmd == G_DGreet)
+ continue;
+#endif
+ alarm( 0 );
+ if (cmd == G_Ready)
+ break;
+ if (cmd == -2)
+ CloseGreeter( FALSE );
+ else {
+ LogError( "Received unknown command %d from greeter\n", cmd );
+ CloseGreeter( TRUE );
+ }
+ goto regreet;
+ }
+ }
+
+ if (CloseGreeter( FALSE ) != EX_NORMAL)
+ goto regreet;
+
+ DeleteXloginResources();
+
+ if (td_setup)
+ SetupDisplay( td_setup );
+
+ if (!(clientPid = StartClient())) {
+ LogError( "Client start failed\n" );
+ SessionExit( EX_NORMAL ); /* XXX maybe EX_REMANAGE_DPY? -- enable in dm.c! */
+ }
+ Debug( "client Started\n" );
+
+ /*
+ * Wait for session to end,
+ */
+ for (;;) {
+ if (!Setjmp( pingTime )) {
+ (void)Signal( SIGALRM, catchAlrm );
+ (void)alarm( d->pingInterval * 60 ); /* may be 0 */
+ (void)Wait4( clientPid );
+ (void)alarm( 0 );
+ break;
+ } else {
+ (void)alarm( 0 );
+ if (!PingServer( d ))
+ catchTerm( SIGTERM );
+ }
+ }
+ /*
+ * Sometimes the Xsession somehow manages to exit before
+ * a server crash is noticed - so we sleep a bit and wait
+ * for being killed.
+ */
+ if (!PingServer( d )) {
+ Debug( "X server dead upon session exit.\n" );
+ if ((d->displayType & d_location) == dLocal)
+ sleep( 10 );
+ SessionExit( EX_AL_RESERVER_DPY );
+ }
+ SessionExit( EX_NORMAL ); /* XXX maybe EX_REMANAGE_DPY? -- enable in dm.c! */
+}
+
+static int xResLoaded;
+
+void
+LoadXloginResources()
+{
+ char **args;
+ char **env;
+
+ if (!xResLoaded && td->resources[0] && access( td->resources, 4 ) == 0) {
+ env = systemEnv( (char *)0 );
+ if ((args = parseArgs( (char **)0, td->xrdb )) &&
+ (args = addStrArr( args, td->resources, -1 )))
+ {
+ Debug( "loading resource file: %s\n", td->resources );
+ (void)runAndWait( args, env );
+ freeStrArr( args );
+ }
+ freeStrArr( env );
+ xResLoaded = TRUE;
+ }
+}
+
+void
+SetupDisplay( const char *arg )
+{
+ char **env;
+
+ env = systemEnv( (char *)0 );
+ (void)source( env, td->setup, arg );
+ freeStrArr( env );
+}
+
+void
+DeleteXloginResources()
+{
+ int i;
+ Atom prop;
+
+ if (!xResLoaded)
+ return;
+ xResLoaded = FALSE;
+ prop = XInternAtom( dpy, "SCREEN_RESOURCES", True );
+ XDeleteProperty( dpy, RootWindow( dpy, 0 ), XA_RESOURCE_MANAGER );
+ if (prop)
+ for (i = ScreenCount(dpy); --i >= 0; )
+ XDeleteProperty( dpy, RootWindow( dpy, i ), prop );
+ XSync( dpy, 0 );
+}
+
+
+int
+source( char **env, const char *file, const char *arg )
+{
+ char **args;
+ int ret;
+
+ if (file && file[0]) {
+ Debug( "source %s\n", file );
+ if (!(args = parseArgs( (char **)0, file )))
+ return waitCompose( 0,0,3 );
+ if (arg && !(args = addStrArr( args, arg, -1 )))
+ return waitCompose( 0,0,3 );
+ ret = runAndWait( args, env );
+ freeStrArr( args );
+ return ret;
+ }
+ return 0;
+}
+
+char **
+inheritEnv( char **env, const char **what )
+{
+ char *value;
+
+ for (; *what; ++what)
+ if ((value = getenv( *what )))
+ env = setEnv( env, *what, value );
+ return env;
+}
+
+char **
+baseEnv( const char *user )
+{
+ char **env;
+
+ env = 0;
+
+#ifdef _AIX
+ /* we need the tags SYSENVIRON: and USRENVIRON: in the call to setpenv() */
+ env = setEnv( env, "SYSENVIRON:", 0 );
+#endif
+
+ if (user) {
+ env = setEnv( env, "USER", user );
+#ifdef _AIX
+ env = setEnv( env, "LOGIN", user );
+#endif
+ env = setEnv( env, "LOGNAME", user );
+ }
+
+#ifdef _AIX
+ env = setEnv( env, "USRENVIRON:", 0 );
+#endif
+
+ env = inheritEnv( env, (const char **)exportList );
+
+ env = setEnv( env, "DISPLAY",
+ memcmp( td->name, "localhost:", 10 ) ?
+ td->name : td->name + 9 );
+
+ if (td->ctrl.path)
+ env = setEnv( env, "DM_CONTROL", fifoDir );
+
+ return env;
+}
+
+char **
+systemEnv( const char *user )
+{
+ char **env;
+
+ env = baseEnv( user );
+ if (td->authFile)
+ env = setEnv( env, "XAUTHORITY", td->authFile );
+ env = setEnv( env, "PATH", td->systemPath );
+ env = setEnv( env, "SHELL", td->systemShell );
+ return env;
+}
diff --git a/kdm/backend/sessreg.c b/kdm/backend/sessreg.c
new file mode 100644
index 000000000..03a46e992
--- /dev/null
+++ b/kdm/backend/sessreg.c
@@ -0,0 +1,307 @@
+/*
+
+Copyright 1990, 1998 The Open Group
+Copyright 2005 Oswald Buddenhagen <ossi@kde.org>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from The Open Group.
+
+*/
+
+/*
+
+ Author: Keith Packard, MIT X Consortium
+ Lastlog support and dynamic utmp entry allocation
+ by Andreas Stolcke <stolcke@icsi.berkeley.edu>
+
+*/
+
+#define _FILE_OFFSET_BITS 64
+#include "dm.h"
+#include "dm_error.h"
+
+#if defined(__svr4__) || defined(__Lynx__) || defined(__QNX__) || defined(__APPLE__) || defined(_SEQUENT_) /*|| defined(USE_PAM)*/
+# define NO_LASTLOG
+#endif
+
+#ifndef NO_LASTLOG
+# ifdef HAVE_LASTLOG_H
+# include <lastlog.h>
+# endif
+# ifndef LLOG_FILE
+# ifdef _PATH_LASTLOGX
+# define LLOG_FILE _PATH_LASTLOGX
+# elif defined(_PATH_LASTLOG)
+# define LLOG_FILE _PATH_LASTLOG
+# else
+# define LLOG_FILE "/usr/adm/lastlog"
+# endif
+# endif
+#endif
+
+#if !defined(__svr4__) && !defined(__QNX__)
+# define SESSREG_HOST
+#endif
+
+#ifdef BSD
+# if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__)
+/* *BSD doesn't like a ':0' type entry in utmp */
+# define NO_UTMP
+# endif
+#endif
+
+#ifdef BSD_UTMP
+# ifndef TTYS_FILE
+# define TTYS_FILE "/etc/ttys"
+# endif
+#endif
+
+#ifdef _AIX
+# define UTL_PFX "xdm/"
+# define UTL_OFF strlen(UTL_PFX)
+#else
+# define UTL_OFF 0
+#endif
+
+#ifndef BSD_UTMP
+static unsigned
+crc32s( const unsigned char *str )
+{
+ int b;
+ unsigned crc = 0xffffffff, by;
+
+ for (; *str; str++) {
+ by = (crc & 255) ^ *str;
+ for (b = 0; b < 8; b++)
+ by = (by >> 1) ^ (-(by & 1) & 0xedb88320);
+ crc = (crc >> 8) ^ by;
+ }
+ return crc;
+}
+#endif
+
+void
+sessreg( struct display *d, int pid, const char *user, int uid )
+{
+ const char *dot, *colon;
+ int left, clen;
+#ifdef BSD_UTMP
+ FILE *ttys;
+ int utmp, slot, freeslot;
+ STRUCTUTMP entry;
+#else
+ unsigned crc, i;
+#endif
+ int wtmp, c;
+#ifndef NO_LASTLOG
+ int llog;
+ struct LASTLOG ll;
+#endif
+ STRUCTUTMP ut_ent;
+
+ if (!d->useSessReg)
+ return;
+
+ bzero( &ut_ent, sizeof(ut_ent) );
+
+ if (pid) {
+ strncpy( ut_ent.ut_user, user, sizeof(ut_ent.ut_user) );
+#ifndef BSD_UTMP
+ ut_ent.ut_pid = pid;
+ ut_ent.ut_type = USER_PROCESS;
+ } else {
+ ut_ent.ut_type = DEAD_PROCESS;
+#endif
+ }
+ ut_ent.ut_time = time( 0 );
+
+ colon = strchr( d->name, ':' );
+ clen = strlen( colon );
+ if (clen > (int)(sizeof(ut_ent.ut_line) - UTL_OFF) - 2)
+ return; /* uhm, well ... */
+ if (colon == d->name) {
+#ifndef BSD_UTMP
+ strncpy( ut_ent.ut_id, d->name, sizeof(ut_ent.ut_id) );
+#endif
+ left = 0;
+ } else {
+#ifdef SESSREG_HOST
+# ifndef BSD_UTMP
+ if (pid)
+# endif
+ {
+ if (colon - d->name > (int)sizeof(ut_ent.ut_host)) {
+ ut_ent.ut_host[0] = '~';
+ memcpy( ut_ent.ut_host + 1,
+ colon - (sizeof(ut_ent.ut_host) - 1),
+ sizeof(ut_ent.ut_host) - 1 );
+ } else
+ memcpy( ut_ent.ut_host, d->name, colon - d->name );
+ }
+#endif
+#ifndef BSD_UTMP
+ crc = crc32s( d->name );
+ ut_ent.ut_id[0] = crc % 26 + 'A';
+ crc /= 26;
+ for (i = 1; i < sizeof(ut_ent.ut_id); i++) {
+ c = crc % 62;
+ crc /= 62;
+ ut_ent.ut_id[i] = c < 26 ? c + 'A' :
+ c < 52 ? c - 26 + 'a' : c - 52 + '0';
+ }
+#endif
+ left = sizeof(ut_ent.ut_line) - UTL_OFF - clen;
+ if (colon - d->name <= left) {
+ clen += colon - d->name;
+ colon = d->name;
+ left = 0;
+ } else {
+ dot = strchr( d->name, '.' );
+ if (dot && dot - d->name < left) {
+ memcpy( ut_ent.ut_line + UTL_OFF, d->name, left - 1 );
+ ut_ent.ut_line[UTL_OFF + left - 1] = '~';
+ } else {
+ memcpy( ut_ent.ut_line + UTL_OFF, d->name, left/2 - 1 );
+ ut_ent.ut_line[UTL_OFF + left/2 - 1] = '~';
+ if (dot) {
+ memcpy( ut_ent.ut_line + UTL_OFF + left/2,
+ dot - (left - left/2 - 1),
+ left - left/2 - 1 );
+ ut_ent.ut_line[UTL_OFF + left - 1] = '~';
+ } else
+ memcpy( ut_ent.ut_line + UTL_OFF + left/2,
+ colon - (left - left/2), left - left/2 );
+ }
+ }
+ }
+#ifdef UTL_PFX
+ memcpy( ut_ent.ut_line, UTL_PFX, UTL_OFF );
+#endif
+ memcpy( ut_ent.ut_line + UTL_OFF + left, colon, clen );
+
+#ifndef NO_UTMP
+# ifdef BSD_UTMP
+ if ((utmp = open( UTMP_FILE, O_RDWR )) < 0)
+ Debug( "cannot open utmp file " UTMP_FILE ": %m\n" );
+ else {
+
+ slot = 1;
+ if (pid) {
+ if (!(ttys = fopen( TTYS_FILE, "r" )))
+ LogWarn( "Cannot open tty file " TTYS_FILE ": %m\n" );
+ else {
+ int column0 = 1;
+ while ((c = getc( ttys )) != EOF)
+ if (c == '\n') {
+ slot++;
+ column0 = 1;
+ } else
+ column0 = 0;
+ if (!column0)
+ slot++;
+ fclose( ttys );
+ }
+ }
+ freeslot = -1;
+ lseek( utmp, slot * sizeof(entry), SEEK_SET );
+ while (read( utmp, (char *)&entry, sizeof(entry) ) == sizeof(entry)) {
+ if (!strncmp( entry.ut_line, ut_ent.ut_line,
+ sizeof(entry.ut_line) ))
+# ifdef SESSREG_HOST
+ if (!strncmp( entry.ut_host, ut_ent.ut_host,
+ sizeof(entry.ut_host) ))
+# endif
+ goto found;
+ if (freeslot < 0 && *entry.ut_user == '\0')
+ freeslot = slot;
+ slot++;
+ }
+ if (!pid) {
+ Debug( "utmp entry for display %s vanished\n", d->name );
+ goto skip;
+ }
+ if (freeslot >= 0)
+ slot = freeslot;
+ found:
+
+# ifdef SESSREG_HOST
+ if (!pid)
+ bzero( ut_ent.ut_host, sizeof(ut_ent.ut_host) );
+# endif
+ lseek( utmp, slot * sizeof(ut_ent), SEEK_SET );
+ if (write( utmp, (char *)&ut_ent, sizeof(ut_ent) ) != sizeof(ut_ent))
+ LogError( "Cannot write utmp file " UTMP_FILE ": %m\n" );
+ skip:
+ close( utmp );
+ }
+# else
+ UTMPNAME( UTMP_FILE );
+ SETUTENT();
+ PUTUTLINE( &ut_ent );
+ ENDUTENT();
+# endif
+#endif
+
+ if ((wtmp = open( WTMP_FILE, O_WRONLY|O_APPEND )) < 0)
+ Debug( "cannot open wtmp file " WTMP_FILE ": %m\n" );
+ else {
+ if (write( wtmp, (char *)&ut_ent, sizeof(ut_ent) ) != sizeof(ut_ent))
+ LogError( "Cannot write wtmp file " WTMP_FILE ": %m\n" );
+ close( wtmp );
+ }
+
+#ifndef NO_LASTLOG
+ if (pid) {
+ bzero( (char *)&ll, sizeof(ll) );
+ ll.ll_time = ut_ent.ut_time;
+ memcpy( ll.ll_line, ut_ent.ut_line, sizeof(ll.ll_line) );
+ memcpy( ll.ll_host, ut_ent.ut_host, sizeof(ll.ll_host) );
+# ifdef HAVE_UTMPX
+ updlastlogx( LLOG_FILE, uid, &ll );
+# else
+ if ((llog = open( LLOG_FILE, O_RDWR )) < 0)
+ Debug( "cannot open lastlog file " LLOG_FILE ": %m\n" );
+ else {
+ lseek( llog, (off_t)uid * sizeof(ll), SEEK_SET );
+ if (write( llog, (char *)&ll, sizeof(ll) ) != sizeof(ll))
+ LogError( "Cannot write llog file " WTMP_FILE ": %m\n" );
+ close( llog );
+ }
+# endif
+ }
+#else
+ (void)uid;
+#endif
+
+#ifdef UTL_PFX
+ {
+ char tmp[sizeof("/dev/") + sizeof(ut_ent.ut_line)];
+ mkdir( "/dev/" UTL_PFX, 0755 );
+ chmod( "/dev/" UTL_PFX, 0755 );
+ sprintf( tmp, "/dev/%.*s", sizeof(ut_ent.ut_line), ut_ent.ut_line );
+ if (pid)
+ close( creat( tmp, 0644 ) );
+ else
+ unlink( tmp );
+ }
+#endif
+}
diff --git a/kdm/backend/socket.c b/kdm/backend/socket.c
new file mode 100644
index 000000000..677a3d32f
--- /dev/null
+++ b/kdm/backend/socket.c
@@ -0,0 +1,418 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+Copyright 2002,2004 Oswald Buddenhagen <ossi@kde.org>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the copyright holder.
+
+*/
+
+/*
+ * xdm - display manager daemon
+ * Author: Keith Packard, MIT X Consortium
+ *
+ * socket.c - Support for BSD sockets
+ */
+
+#include "dm.h"
+
+#if defined(XDMCP) && !defined(STREAMSCONN)
+
+#include "dm_error.h"
+#include "dm_socket.h"
+
+#include <netdb.h>
+#include <arpa/inet.h>
+
+static int c_request_port;
+
+static int
+CreateListeningSocket( struct sockaddr *sock_addr, int salen )
+{
+ int fd;
+#if defined(IPv6) && defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY)
+ int on = 0;
+#endif
+ const char *addrstring = "unknown";
+#if defined(IPv6) && defined(AF_INET6)
+ char addrbuf[INET6_ADDRSTRLEN];
+#endif
+
+ if (!request_port)
+ return -1;
+
+ if (debugLevel & DEBUG_CORE) {
+#if defined(IPv6) && defined(AF_INET6)
+ void *ipaddr;
+ if (sock_addr->sa_family == AF_INET6)
+ ipaddr = & ((struct sockaddr_in6 *)sock_addr)->sin6_addr;
+ else
+ ipaddr = & ((struct sockaddr_in *)sock_addr)->sin_addr;
+ addrstring =
+ inet_ntop( sock_addr->sa_family, ipaddr, addrbuf, sizeof(addrbuf) );
+
+#else
+ addrstring = inet_ntoa( ((struct sockaddr_in *)sock_addr)->sin_addr );
+#endif
+
+ Debug( "creating socket to listen on port %d of address %s\n",
+ request_port, addrstring );
+ }
+
+ if ((fd = socket( sock_addr->sa_family, SOCK_DGRAM, 0 )) == -1) {
+ LogError( "XDMCP socket creation failed, errno %d\n", errno );
+ return -1;
+ }
+#if defined(IPv6) && defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY)
+ setsockopt( fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on) );
+#endif
+
+ if (bind( fd, sock_addr, salen ) == -1) {
+ LogError( "error %d binding socket address %d\n", errno, request_port );
+ close( fd );
+ return -1;
+ }
+
+ RegisterCloseOnFork( fd );
+ RegisterInput( fd );
+ return fd;
+}
+
+struct socklist {
+ struct socklist *next;
+ struct socklist *mcastgroups;
+ struct sockaddr *addr;
+ int salen;
+ int addrlen;
+ int fd;
+ int ref; /* referenced bit - see UpdateListenSockets */
+};
+
+static struct socklist *listensocks;
+
+static void
+DestroyListeningSocket( struct socklist *s )
+{
+ struct socklist *g, *n;
+
+ if (s->fd >= 0) {
+ CloseNClearCloseOnFork( s->fd );
+ UnregisterInput( s->fd );
+ s->fd = -1;
+ }
+ if (s->addr) {
+ free( s->addr );
+ s->addr = NULL;
+ }
+ for (g = s->mcastgroups; g; g = n) {
+ n = g->next;
+ if (g->addr)
+ free( g->addr );
+ free( g );
+ }
+ s->mcastgroups = NULL;
+}
+
+static struct socklist*
+FindInList( struct socklist *list, ARRAY8Ptr addr )
+{
+ struct socklist *s;
+
+ for (s = list; s; s = s->next) {
+ if (s->addrlen == addr->length) {
+ char *addrdata;
+
+ switch (s->addr->sa_family) {
+ case AF_INET:
+ addrdata = (char *)
+ &(((struct sockaddr_in *)s->addr)->sin_addr.s_addr);
+ break;
+#if defined(IPv6) && defined(AF_INET6)
+ case AF_INET6:
+ addrdata = (char *)
+ &(((struct sockaddr_in6 *)s->addr)->sin6_addr.s6_addr);
+ break;
+#endif
+ default:
+ /* Unrecognized address family */
+ continue;
+ }
+ if (!memcmp( addrdata, addr->data, addr->length ))
+ return s;
+ }
+ }
+ return NULL;
+}
+
+static struct socklist *
+CreateSocklistEntry( ARRAY8Ptr addr )
+{
+ struct socklist *s;
+
+ if (!(s = Calloc( 1, sizeof(struct socklist) )))
+ return NULL;
+
+ if (addr->length == 4) { /* IPv4 */
+ struct sockaddr_in *sin4;
+ sin4 = Calloc( 1, sizeof(struct sockaddr_in) );
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ sin4->sin_len = sizeof(struct sockaddr_in);
+#endif
+ s->addr = (struct sockaddr *)sin4;
+ s->salen = sizeof(struct sockaddr_in);
+ s->addrlen = sizeof(struct in_addr);
+ sin4->sin_family = AF_INET;
+ sin4->sin_port = htons( (short)request_port );
+ memcpy( &sin4->sin_addr, addr->data, addr->length );
+ }
+#if defined(IPv6) && defined(AF_INET6)
+ else if (addr->length == 16) { /* IPv6 */
+ struct sockaddr_in6 *sin6;
+ sin6 = Calloc( 1, sizeof(struct sockaddr_in6) );
+#ifdef SIN6_LEN
+ sin6->sin6_len = sizeof(struct sockaddr_in6);
+#endif
+ s->addr = (struct sockaddr *)sin6;
+ s->salen = sizeof(struct sockaddr_in6);
+ s->addrlen = sizeof(struct in6_addr);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = htons( (short)request_port );
+ memcpy( &sin6->sin6_addr, addr->data, addr->length );
+ }
+#endif
+ else {
+ /* Unknown address type */
+ free( s );
+ s = NULL;
+ }
+ return s;
+}
+
+static void
+UpdateListener( ARRAY8Ptr addr, void **closure )
+{
+ struct socklist *s;
+
+ *closure = NULL;
+
+ if (addr == NULL) {
+ ARRAY8 tmpaddr;
+ struct in_addr in;
+#if defined(IPv6) && defined(AF_INET6)
+ struct in6_addr in6 = in6addr_any;
+ tmpaddr.length = sizeof(in6);
+ tmpaddr.data = (CARD8Ptr) &in6;
+ UpdateListener( &tmpaddr, closure );
+ if (*closure)
+ return;
+#endif
+ in.s_addr = htonl( INADDR_ANY );
+ tmpaddr.length = sizeof(in);
+ tmpaddr.data = (CARD8Ptr) &in;
+ UpdateListener( &tmpaddr, closure );
+ return;
+ }
+
+ if (c_request_port == request_port &&
+ (s = FindInList( listensocks, addr )))
+ {
+ *closure = (void *)s;
+ s->ref = 1;
+ return;
+ }
+
+ if (!(s = CreateSocklistEntry( addr )))
+ return;
+
+ if ((s->fd = CreateListeningSocket( s->addr, s->salen )) < 0) {
+ free( s->addr );
+ free( s );
+ return;
+ }
+ s->ref = 1;
+ s->next = listensocks;
+ listensocks = s;
+ *closure = (void *)s;
+}
+
+#define JOIN_MCAST_GROUP 0
+#define LEAVE_MCAST_GROUP 1
+
+static void
+ChangeMcastMembership( struct socklist *s, struct socklist *g, int op )
+{
+ int sockopt;
+
+ switch (s->addr->sa_family)
+ {
+ case AF_INET:
+ {
+ struct ip_mreq mreq;
+ memcpy( &mreq.imr_multiaddr,
+ &((struct sockaddr_in *)g->addr)->sin_addr,
+ sizeof(struct in_addr) );
+ memcpy( &mreq.imr_interface,
+ &((struct sockaddr_in *)s->addr)->sin_addr,
+ sizeof(struct in_addr) );
+ if (op == JOIN_MCAST_GROUP)
+ sockopt = IP_ADD_MEMBERSHIP;
+ else
+ sockopt = IP_DROP_MEMBERSHIP;
+ if (setsockopt( s->fd, IPPROTO_IP, sockopt,
+ &mreq, sizeof(mreq) ) < 0) {
+ LogError( "XDMCP socket multicast %s to %s failed, errno %d\n",
+ (op == JOIN_MCAST_GROUP) ? "join" : "drop",
+ inet_ntoa( ((struct sockaddr_in *)g->addr)->sin_addr ),
+ errno );
+ } else if (debugLevel & DEBUG_CORE) {
+ Debug( "XDMCP socket multicast %s to %s succeeded\n",
+ (op == JOIN_MCAST_GROUP) ? "join" : "drop",
+ inet_ntoa( ((struct sockaddr_in *)g->addr)->sin_addr ) );
+ }
+ return;
+ }
+#if defined(IPv6) && defined(AF_INET6)
+# ifndef IPV6_JOIN_GROUP
+# define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP
+# endif
+# ifndef IPV6_LEAVE_GROUP
+# define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP
+# endif
+ case AF_INET6:
+ {
+ struct ipv6_mreq mreq6;
+ memcpy( &mreq6.ipv6mr_multiaddr,
+ &((struct sockaddr_in6 *)g->addr)->sin6_addr,
+ sizeof(struct in6_addr) );
+ mreq6.ipv6mr_interface = 0; /* TODO: fix this */
+ if (op == JOIN_MCAST_GROUP)
+ sockopt = IPV6_JOIN_GROUP;
+ else
+ sockopt = IPV6_LEAVE_GROUP;
+ if (setsockopt( s->fd, IPPROTO_IPV6, sockopt,
+ &mreq6, sizeof(mreq6) ) < 0)
+ {
+ int saveerr = errno;
+ char addrbuf[INET6_ADDRSTRLEN];
+
+ inet_ntop( s->addr->sa_family,
+ &((struct sockaddr_in6 *)g->addr)->sin6_addr,
+ addrbuf, sizeof(addrbuf) );
+
+ LogError( "XDMCP socket multicast %s to %s failed, errno %d\n",
+ (op == JOIN_MCAST_GROUP) ? "join" : "drop", addrbuf,
+ saveerr );
+ } else if (debugLevel & DEBUG_CORE) {
+ char addrbuf[INET6_ADDRSTRLEN];
+
+ inet_ntop( s->addr->sa_family,
+ &((struct sockaddr_in6 *)g->addr)->sin6_addr,
+ addrbuf, sizeof(addrbuf) );
+
+ Debug( "XDMCP socket multicast %s to %s succeeded\n",
+ (op == JOIN_MCAST_GROUP) ? "join" : "drop", addrbuf );
+ }
+ return;
+ }
+#endif
+ }
+}
+
+static void
+UpdateMcastGroup( ARRAY8Ptr addr, void **closure )
+{
+ struct socklist *s = (struct socklist *)*closure;
+ struct socklist *g;
+
+ if (!s)
+ return;
+
+ /* Already in the group, mark & continue */
+ if ((g = FindInList( s->mcastgroups, addr ))) {
+ g->ref = 1;
+ return;
+ }
+
+ /* Need to join the group */
+ if (!(g = CreateSocklistEntry( addr )))
+ return;
+
+ ChangeMcastMembership( s, g, JOIN_MCAST_GROUP );
+ free( g );
+}
+
+/* Open or close listening sockets to match the current settings read in
+ from the access database. */
+void
+UpdateListenSockets( void )
+{
+ struct socklist *s, *g, **ls, **lg;
+ void *tmpPtr = NULL;
+
+ /* Clear Ref bits - any not marked by UpdateCallback will be closed */
+ for (s = listensocks; s; s = s->next) {
+ s->ref = 0;
+ for (g = s->mcastgroups; g; g = g->next)
+ g->ref = 0;
+ }
+ ForEachListenAddr( UpdateListener, UpdateMcastGroup, &tmpPtr );
+ c_request_port = request_port;
+ for (ls = &listensocks; (s = *ls); )
+ if (!s->ref) {
+ DestroyListeningSocket( s );
+ *ls = s->next;
+ free( s );
+ } else {
+ ls = &s->next;
+ for (lg = &s->mcastgroups; (g = *lg); )
+ if (!g->ref) {
+ ChangeMcastMembership( s, g, LEAVE_MCAST_GROUP );
+ *lg = g->next;
+ free( g );
+ } else
+ lg = &g->next;
+ }
+}
+
+int
+AnyListenSockets( void )
+{
+ return listensocks != NULL;
+}
+
+int
+ProcessListenSockets( FD_TYPE *reads )
+{
+ struct socklist *s;
+ int ret = 0;
+
+ for (s = listensocks; s; s = s->next)
+ if (FD_ISSET( s->fd, reads )) {
+ ProcessRequestSocket( s->fd );
+ ret = 1;
+ }
+ return ret;
+}
+
+#endif /* !STREAMSCONN && XDMCP */
diff --git a/kdm/backend/streams.c b/kdm/backend/streams.c
new file mode 100644
index 000000000..f60fb955e
--- /dev/null
+++ b/kdm/backend/streams.c
@@ -0,0 +1,127 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2002,2004 Oswald Buddenhagen <ossi@kde.org>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the copyright holder.
+
+*/
+
+/*
+ * xdm - display manager daemon
+ * Author: Keith Packard, MIT X Consortium
+ *
+ * streams.c - Support for STREAMS
+ */
+
+#include "dm.h"
+
+#if defined(XDMCP) && defined(STREAMSCONN)
+
+#include "dm_error.h"
+
+#include <fcntl.h>
+#include <tiuser.h>
+#include <netconfig.h>
+#include <netdir.h>
+
+static int xdmcpFd = -1, c_request_port;
+
+void
+UpdateListenSockets( void )
+{
+ struct t_bind bind_addr;
+ struct netconfig *nconf;
+ struct nd_hostserv service;
+ struct nd_addrlist *servaddrs;
+ char bindbuf[15];
+ int it;
+
+ if (c_request_port == request_port)
+ return;
+ c_request_port = request_port;
+
+ if (xdmcpFd != -1) {
+ CloseNClearCloseOnFork( xdmcpFd );
+ UnregisterInput( xdmcpFd );
+ xdmcpFd = -1;
+ }
+
+ if (!request_port)
+ return;
+
+ Debug( "creating UDP stream %d\n", request_port );
+
+ nconf = getnetconfigent( "udp" );
+ if (!nconf) {
+ t_error( "getnetconfigent udp" );
+ return;
+ }
+
+ xdmcpFd = t_open( nconf->nc_device, O_RDWR, NULL );
+ if (xdmcpFd == -1) {
+ LogError( "XDMCP stream creation failed\n" );
+ t_error( "CreateWellKnownSockets(xdmcpFd): t_open failed" );
+ return;
+ }
+
+ service.h_host = HOST_SELF;
+ sprintf( bindbuf, "%d", request_port );
+ service.h_serv = bindbuf;
+ netdir_getbyname( nconf, &service, &servaddrs );
+ freenetconfigent( nconf );
+
+ bind_addr.qlen = 5;
+ bind_addr.addr.buf = servaddrs->n_addrs[0].buf;
+ bind_addr.addr.len = servaddrs->n_addrs[0].len;
+ bind_addr.addr.maxlen = servaddrs->n_addrs[0].len;
+ it = t_bind( xdmcpFd, &bind_addr, &bind_addr );
+ netdir_free( (char *)servaddrs, ND_ADDRLIST );
+ if (it < 0) {
+ LogError( "Error binding UDP port %d\n", request_port );
+ t_error( "CreateWellKnownSockets(xdmcpFd): t_bind failed" );
+ t_close( xdmcpFd );
+ xdmcpFd = -1;
+ return;
+ }
+ RegisterCloseOnFork( xdmcpFd );
+ RegisterInput( xdmcpFd );
+}
+
+int
+AnyListenSockets( void )
+{
+ return xdmcpFd != -1;
+}
+
+int
+ProcessRequestSockets( FD_TYPE *reads )
+{
+ if (xdmcpFd >= 0 && FD_ISSET( xdmcpFd, reads )) {
+ ProcessRequestSocket( xdmcpFd );
+ return 1;
+ }
+ return 0;
+}
+
+#endif /* STREAMSCONN && XDMCP */
diff --git a/kdm/backend/util.c b/kdm/backend/util.c
new file mode 100644
index 000000000..a5358cd65
--- /dev/null
+++ b/kdm/backend/util.c
@@ -0,0 +1,621 @@
+/*
+
+Copyright 1989, 1998 The Open Group
+Copyright 2000-2005 Oswald Buddenhagen <ossi@kde.org>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the copyright holder.
+
+*/
+
+/*
+ * xdm - display manager daemon
+ * Author: Keith Packard, MIT X Consortium
+ *
+ * various utility routines
+ */
+
+#include "dm.h"
+#include "dm_error.h"
+
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#if 0 /*def USG; this was hpux once upon a time */
+# define NEED_UTSNAME
+#endif
+
+#ifdef NEED_UTSNAME
+# include <sys/utsname.h>
+#endif
+
+void *
+Calloc( size_t nmemb, size_t size )
+{
+ void *ret;
+
+ if (!(ret = calloc( nmemb, size )))
+ LogOutOfMem();
+ return ret;
+}
+
+void *
+Malloc( size_t size )
+{
+ void *ret;
+
+ if (!(ret = malloc( size )))
+ LogOutOfMem();
+ return ret;
+}
+
+void *
+Realloc( void *ptr, size_t size )
+{
+ void *ret;
+
+ if (!(ret = realloc( ptr, size )) && size)
+ LogOutOfMem();
+ return ret;
+}
+
+int
+StrCmp( const char *s1, const char *s2 )
+{
+ if (s1 == s2)
+ return 0;
+ if (!s1)
+ return -1;
+ if (!s2)
+ return 1;
+ return strcmp( s1, s2 );
+}
+
+void
+WipeStr( char *str )
+{
+ if (str) {
+ bzero( str, strlen( str ) );
+ free( str );
+ }
+}
+
+#ifndef HAVE_STRNLEN
+int
+StrNLen( const char *s, int max )
+{
+ unsigned l;
+
+ for (l = 0; l < (unsigned)max && s[l]; l++);
+ return l;
+}
+#endif
+
+/* duplicate src; wipe & free old dst string */
+int
+ReStrN( char **dst, const char *src, int len )
+{
+ char *ndst = 0;
+
+ if (src) {
+ if (len < 0)
+ len = strlen( src );
+ if (*dst && !memcmp( *dst, src, len ) && !(*dst)[len])
+ return 1;
+ if (!(ndst = Malloc( len + 1 ))) {
+ WipeStr( *dst );
+ *dst = 0;
+ return 0;
+ }
+ memcpy( ndst, src, len );
+ ndst[len] = 0;
+ }
+ WipeStr( *dst ); /* make an option, if we should become heavily used */
+ *dst = ndst;
+ return 2;
+}
+
+int
+ReStr( char **dst, const char *src )
+{
+ return ReStrN( dst, src, -1 );
+}
+
+/* duplicate src */
+int
+StrNDup( char **dst, const char *src, int len )
+{
+ if (src) {
+ if (len < 0)
+ len = strlen( src );
+ if (!(*dst = Malloc( len + 1 )))
+ return 0;
+ memcpy( *dst, src, len );
+ (*dst)[len] = 0;
+ } else
+ *dst = 0;
+ return 1;
+}
+
+int
+StrDup( char **dst, const char *src )
+{
+ return StrNDup( dst, src, -1 );
+}
+
+/* append any number of strings to dst */
+int
+StrApp( char **dst, ... )
+{
+ int len;
+ char *bk, *pt, *dp;
+ va_list va;
+
+ len = 1;
+ if (*dst)
+ len += strlen( *dst );
+ va_start( va, dst );
+ for (;;) {
+ pt = va_arg( va, char * );
+ if (!pt)
+ break;
+ len += strlen( pt );
+ }
+ va_end( va );
+ if (!(bk = Malloc( len ))) {
+ if (*dst) {
+ free( *dst );
+ *dst = 0;
+ }
+ return 0;
+ }
+ dp = bk;
+ if (*dst) {
+ len = strlen( *dst );
+ memcpy( dp, *dst, len );
+ dp += len;
+ free( *dst );
+ }
+ va_start( va, dst );
+ for (;;) {
+ pt = va_arg( va, char * );
+ if (!pt)
+ break;
+ len = strlen( pt );
+ memcpy( dp, pt, len );
+ dp += len;
+ }
+ va_end( va );
+ *dp = '\0';
+ *dst = bk;
+ return 1;
+}
+
+
+char **
+initStrArr( char **arr )
+{
+ if (!arr && (arr = Malloc( sizeof(char *) )))
+ arr[0] = 0;
+ return arr;
+}
+
+int
+arrLen( char **arr )
+{
+ int nu = 0;
+ if (arr)
+ for (; arr[nu]; nu++);
+ return nu;
+}
+
+static char **
+extStrArr( char **arr, char ***strp )
+{
+ char **rarr;
+ int nu;
+
+ nu = arrLen( arr );
+ if ((rarr = Realloc( arr, sizeof(char *) * (nu + 2) ))) {
+ rarr[nu + 1] = 0;
+ *strp = rarr + nu;
+ return rarr;
+ }
+ freeStrArr( arr );
+ return 0;
+}
+
+char **
+addStrArr( char **arr, const char *str, int len )
+{
+ char **strp;
+
+ if ((arr = extStrArr( arr, &strp ))) {
+ if (StrNDup( strp, str, len ))
+ return arr;
+ freeStrArr( arr );
+ }
+ return 0;
+}
+
+char **
+xCopyStrArr( int rn, char **arr )
+{
+ char **rarr;
+ int nu;
+
+ nu = arrLen( arr );
+ if ((rarr = Calloc( sizeof(char *), nu + rn + 1 )))
+ memcpy( rarr + rn, arr, sizeof(char *) * nu );
+ return rarr;
+}
+
+void
+freeStrArr( char **arr )
+{
+ char **a;
+
+ if (arr) {
+ for (a = arr; *a; a++)
+ free( *a );
+ free( arr );
+ }
+}
+
+
+char **
+parseArgs( char **argv, const char *string )
+{
+ const char *word;
+ char **strp, *str;
+ int wlen;
+
+ if (!(argv = initStrArr( argv )))
+ return 0;
+ while (*string) {
+ if (isspace( *string )) {
+ string++;
+ continue;
+ }
+ word = string;
+ wlen = 0;
+ do {
+ if (*string == '\\') {
+ if (!*++string)
+ string--;
+ wlen++;
+ } else if (*string == '\'') {
+ while (*++string != '\'' && *string)
+ wlen++;
+ } else if (*string == '"') {
+ while (*++string != '"' && *string) {
+ if (*string == '\\') {
+ if (!*++string)
+ string--;
+ }
+ wlen++;
+ }
+ } else
+ wlen++;
+ } while (*++string && !isspace( *string ));
+ if (!(argv = extStrArr( argv, &strp )))
+ return 0;
+ if (!(*strp = str = Malloc( wlen + 1 ))) {
+ freeStrArr( argv );
+ return 0;
+ }
+ do {
+ if (*word == '\\') {
+ if (!*++word)
+ word--;
+ *str++ = *word;
+ } else if (*word == '\'') {
+ while (*++word != '\'' && *word)
+ *str++ = *word;
+ } else if (*word == '"') {
+ while (*++word != '"' && *word) {
+ if (*word == '\\') {
+ if (!*++word)
+ word--;
+ }
+ *str++ = *word;
+ }
+ } else
+ *str++ = *word;
+ } while (*++word && !isspace( *word ));
+ *str = 0;
+ }
+ return argv;
+}
+
+
+const char *
+getEnv( char **e, const char *name )
+{
+ if (e) {
+ int l = strlen( name );
+ for (; *e; e++)
+ if (!memcmp( *e, name, l ) && (*e)[l] == '=')
+ return (*e) + l + 1;
+ }
+ return 0;
+}
+
+char **
+setEnv( char **e, const char *name, const char *value )
+{
+ char **new, **old;
+ char *newe;
+ int envsize;
+ int l;
+
+#ifdef _AIX
+ /* setpenv() depends on "SYSENVIRON:", not "SYSENVIRON:=" */
+ if (!value) {
+ if (!StrDup( &newe, name ))
+ return e;
+ } else
+#endif
+ {
+ newe = 0;
+ if (!StrApp( &newe, name, "=", value, (char *)0 ))
+ return e;
+ }
+ envsize = 0;
+ if (e) {
+ l = strlen( name );
+ for (old = e; *old; old++)
+ if (!memcmp( *old, name, l ) && ((*old)[l] == '=' || !(*old)[l]))
+ {
+ free( *old );
+ *old = newe;
+ return e;
+ }
+ envsize = old - e;
+ }
+ if (!(new = (char **)
+ Realloc( (char *)e, (unsigned)((envsize + 2) * sizeof(char *)) )))
+ {
+ free( newe );
+ return e;
+ }
+ new[envsize] = newe;
+ new[envsize + 1] = 0;
+ return new;
+}
+
+char **
+putEnv( const char *string, char **env )
+{
+ char *b, *n;
+
+ if (!(b = strchr( string, '=' )))
+ return NULL;
+ if (!StrNDup( &n, string, b - string ))
+ return NULL;
+ env = setEnv( env, n, b + 1 );
+ free( n );
+ return env;
+}
+
+static int
+GetHostname( char *buf, int maxlen )
+{
+ int len;
+
+#ifdef NEED_UTSNAME
+ /*
+ * same host name crock as in server and xinit.
+ */
+ struct utsname name;
+
+ uname( &name );
+ len = strlen( name.nodename );
+ if (len >= maxlen) len = maxlen - 1;
+ memcpy( buf, name.nodename, len );
+ buf[len] = '\0';
+#else
+ buf[0] = '\0';
+ (void)gethostname( buf, maxlen );
+ buf[maxlen - 1] = '\0';
+ len = strlen( buf );
+#endif /* NEED_UTSNAME */
+ return len;
+}
+
+static char localHostbuf[256];
+static int gotLocalHostname;
+
+const char *
+localHostname( void )
+{
+ if (!gotLocalHostname)
+ {
+ GetHostname( localHostbuf, sizeof(localHostbuf) - 1 );
+ gotLocalHostname = 1;
+ }
+ return localHostbuf;
+}
+
+static int
+AtomicIO( ssize_t (*f)( int, void *, size_t ), int fd, void *buf, int count )
+{
+ int ret, rlen;
+
+ for (rlen = 0; rlen < count; ) {
+ dord:
+ ret = f( fd, (void *)((char *)buf + rlen), count - rlen );
+ if (ret < 0) {
+ if (errno == EINTR)
+ goto dord;
+ if (errno == EAGAIN)
+ break;
+ return -1;
+ }
+ if (!ret)
+ break;
+ rlen += ret;
+ }
+ return rlen;
+}
+
+int
+Reader( int fd, void *buf, int count )
+{
+ return AtomicIO( read, fd, buf, count );
+}
+
+int
+Writer( int fd, const void *buf, int count )
+{
+ return AtomicIO( (ssize_t(*)( int, void *, size_t ))write,
+ fd, (void *)buf, count );
+}
+
+int
+fGets( char *buf, int max, FILE *f )
+{
+ int len;
+
+ if (!fgets( buf, max, f ))
+ return -1;
+ len = strlen( buf );
+ if (len && buf[len - 1] == '\n')
+ buf[--len] = 0;
+ return len;
+}
+
+time_t
+mTime( const char *fn )
+{
+ struct stat st;
+
+ if (stat( fn, &st ))
+ return -1;
+ else
+ return st.st_mtime;
+}
+
+static int
+StrNChrCnt( const char *s, int slen, char c )
+{
+ int i, cnt;
+
+ for (i = cnt = 0; i < slen && s[i]; i++)
+ if (s[i] == c)
+ cnt++;
+ return cnt;
+}
+
+/* X -from ip6-addr does not work here, so i don't know whether this is needed.
+#define IP6_MAGIC
+*/
+
+void
+ListSessions( int flags, struct display *d, void *ctx,
+ void (*emitXSess)( struct display *, struct display *, void * ),
+ void (*emitTTYSess)( STRUCTUTMP *, struct display *, void * ) )
+{
+ struct display *di;
+#ifdef IP6_MAGIC
+ int le, dot;
+#endif
+#ifdef BSD_UTMP
+ int fd;
+ struct utmp ut[1];
+#else
+ STRUCTUTMP *ut;
+#endif
+
+ for (di = displays; di; di = di->next)
+ if (((flags & lstRemote) || (di->displayType & d_location) == dLocal) &&
+ (di->status == remoteLogin ||
+ ((flags & lstPassive) ? di->status == running : di->userSess >= 0)))
+ emitXSess( di, d, ctx );
+
+ if (!(flags & lstTTY))
+ return;
+
+#ifdef BSD_UTMP
+ if ((fd = open( UTMP_FILE, O_RDONLY )) < 0)
+ return;
+ while (Reader( fd, ut, sizeof(ut[0]) ) == sizeof(ut[0])) {
+ if (*ut->ut_user) { /* no idea how to list passive TTYs on BSD */
+#else
+ SETUTENT();
+ while ((ut = GETUTENT())) {
+ if (ut->ut_type == USER_PROCESS
+# if 0 /* list passive TTYs at all? not too sensible, i think. */
+ || ((flags & lstPassive) && ut->ut_type == LOGIN_PROCESS)
+# endif
+ )
+ {
+#endif
+ if (*ut->ut_host) { /* from remote or x */
+ if (!(flags & lstRemote))
+ continue;
+ } else {
+ /* hack around broken konsole which does not set ut_host. */
+ /* this check is probably linux-specific. */
+ /* alternatively we could open the device and try VT_OPENQRY. */
+ if (memcmp( ut->ut_line, "tty", 3 ) ||
+ !isdigit( ut->ut_line[3] ))
+ continue;
+ }
+ if (StrNChrCnt( ut->ut_line, sizeof(ut->ut_line), ':' ))
+ continue; /* x login */
+ switch (StrNChrCnt( ut->ut_host, sizeof(ut->ut_host), ':' )) {
+ case 1: /* x terminal */
+ continue;
+ default:
+#ifdef IP6_MAGIC
+ /* unknown - IPv6 makes things complicated */
+ le = StrNLen( ut->ut_host, sizeof(ut->ut_host) );
+ /* cut off screen number */
+ for (dot = le; ut->ut_host[--dot] != ':'; )
+ if (ut->ut_host[dot] == '.') {
+ le = dot;
+ break;
+ }
+ for (di = displays; di; di = di->next)
+ if (!memcmp( di->name, ut->ut_host, le ) && !di->name[le])
+ goto cont; /* x terminal */
+ break;
+ cont:
+ continue;
+ case 0: /* no x terminal */
+#endif
+ break;
+ }
+ emitTTYSess( ut, d, ctx );
+ }
+ }
+#ifdef BSD_UTMP
+ close( fd );
+#else
+ ENDUTENT();
+#endif
+}
+
diff --git a/kdm/backend/xdmauth.c b/kdm/backend/xdmauth.c
new file mode 100644
index 000000000..86257c651
--- /dev/null
+++ b/kdm/backend/xdmauth.c
@@ -0,0 +1,267 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2001,2003 Oswald Buddenhagen <ossi@kde.org>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the copyright holder.
+
+*/
+
+/*
+ * xdm - display manager daemon
+ * Author: Keith Packard, MIT X Consortium
+ *
+ * generate authorization data for XDM-AUTHORIZATION-1 as per XDMCP spec
+ */
+
+#include <config.h>
+
+#ifdef HASXDMAUTH
+
+#include "dm.h"
+#include "dm_auth.h"
+#include "dm_error.h"
+
+static char auth_name[256];
+static int auth_name_len;
+
+void
+XdmInitAuth( unsigned short name_len, const char *name )
+{
+ if (name_len > 256)
+ name_len = 256;
+ auth_name_len = name_len;
+ memmove( auth_name, name, name_len );
+}
+
+/*
+ * Generate authorization for XDM-AUTHORIZATION-1
+ *
+ * When being used with XDMCP, 8 bytes are generated for the session key
+ * (sigma), as the random number (rho) is already shared between xdm and
+ * the server. Otherwise, we'll prepend a random number to pass in the file
+ * between xdm and the server (16 bytes total)
+ */
+
+static Xauth *
+XdmGetAuthHelper( unsigned short namelen, const char *name, int includeRho )
+{
+ Xauth *new;
+
+ if (!(new = (Xauth *)Malloc( sizeof(Xauth) )))
+ return (Xauth *)0;
+ new->family = FamilyWild;
+ new->address_length = 0;
+ new->address = 0;
+ new->number_length = 0;
+ new->number = 0;
+ if (includeRho)
+ new->data_length = 16;
+ else
+ new->data_length = 8;
+
+ new->data = (char *)Malloc( new->data_length );
+ if (!new->data) {
+ free( (char *)new );
+ return (Xauth *)0;
+ }
+ new->name = (char *)Malloc( namelen );
+ if (!new->name) {
+ free( (char *)new->data );
+ free( (char *)new );
+ return (Xauth *)0;
+ }
+ memmove( (char *)new->name, name, namelen );
+ new->name_length = namelen;
+ if (!GenerateAuthData( (char *)new->data, new->data_length )) {
+ free( (char *)new->name );
+ free( (char *)new->data );
+ free( (char *)new );
+ return (Xauth *)0;
+ }
+ /*
+ * set the first byte of the session key to zero as it
+ * is a DES key and only uses 56 bits
+ */
+ ((char *)new->data)[new->data_length - 8] = '\0';
+ Debug( "local server auth %02[*hhx\n", new->data_length, new->data );
+ return new;
+}
+
+Xauth *
+XdmGetAuth( unsigned short namelen, const char *name )
+{
+ return XdmGetAuthHelper( namelen, name, TRUE );
+}
+
+#ifdef XDMCP
+
+void
+XdmGetXdmcpAuth( struct protoDisplay *pdpy,
+ unsigned short authorizationNameLen,
+ const char *authorizationName )
+{
+ Xauth *fileauth, *xdmcpauth;
+
+ if (pdpy->fileAuthorization && pdpy->xdmcpAuthorization)
+ return;
+ xdmcpauth = XdmGetAuthHelper( authorizationNameLen, authorizationName,
+ FALSE );
+ if (!xdmcpauth)
+ return;
+ fileauth = (Xauth *)Malloc( sizeof(Xauth) );
+ if (!fileauth) {
+ XauDisposeAuth( xdmcpauth );
+ return;
+ }
+ /* build the file auth from the XDMCP auth */
+ *fileauth = *xdmcpauth;
+ fileauth->name = Malloc( xdmcpauth->name_length );
+ fileauth->data = Malloc( 16 );
+ fileauth->data_length = 16;
+ if (!fileauth->name || !fileauth->data) {
+ XauDisposeAuth( xdmcpauth );
+ if (fileauth->name)
+ free( (char *)fileauth->name );
+ if (fileauth->data)
+ free( (char *)fileauth->data );
+ free( (char *)fileauth );
+ return;
+ }
+ /*
+ * for the file authorization, prepend the random number (rho)
+ * which is simply the number we've been passing back and
+ * forth via XDMCP
+ */
+ memmove( fileauth->name, xdmcpauth->name, xdmcpauth->name_length );
+ memmove( fileauth->data, pdpy->authenticationData.data, 8 );
+ memmove( fileauth->data + 8, xdmcpauth->data, 8 );
+ Debug( "accept packet auth %02[*hhx\nauth file auth %02[*hhx\n",
+ xdmcpauth->data_length, xdmcpauth->data,
+ fileauth->data_length, fileauth->data );
+ /* encrypt the session key for its trip back to the server */
+ XdmcpWrap( (unsigned char *)xdmcpauth->data, (unsigned char *)&pdpy->key,
+ (unsigned char *)xdmcpauth->data, 8 );
+ pdpy->fileAuthorization = fileauth;
+ pdpy->xdmcpAuthorization = xdmcpauth;
+}
+
+#define atox(c) ('0' <= c && c <= '9' ? c - '0' : \
+ 'a' <= c && c <= 'f' ? c - 'a' + 10 : \
+ 'A' <= c && c <= 'F' ? c - 'A' + 10 : -1)
+
+static int
+HexToBinary( char *key )
+{
+ char *out, *in;
+ int top, bottom;
+
+ in = key + 2;
+ out= key;
+ while (in[0] && in[1]) {
+ top = atox( in[0] );
+ if (top == -1)
+ return 0;
+ bottom = atox( in[1] );
+ if (bottom == -1)
+ return 0;
+ *out++ = (top << 4) | bottom;
+ in += 2;
+ }
+ if (in[0])
+ return 0;
+ *out++ = '\0';
+ return 1;
+}
+
+/*
+ * Search the Keys file for the entry matching this display. This
+ * routine accepts either plain ascii strings for keys, or hex-encoded numbers
+ */
+
+static int
+XdmGetKey( struct protoDisplay *pdpy, ARRAY8Ptr displayID )
+{
+ FILE *keys;
+ char line[1024], id[1024], key[1024];
+ int keylen;
+
+ Debug( "lookup key for %.*s\n", displayID->length, displayID->data );
+ keys = fopen( keyFile, "r" );
+ if (!keys)
+ return FALSE;
+ while (fgets( line, sizeof(line), keys )) {
+ if (line[0] == '#' || sscanf( line, "%s %s", id, key ) != 2)
+ continue;
+ bzero( line, sizeof(line) );
+ Debug( "key entry for %\"s %d bytes\n", id, strlen( key ) );
+ if (strlen( id ) == displayID->length &&
+ !strncmp( id, (char *)displayID->data, displayID->length ))
+ {
+ if (!strncmp( key, "0x", 2 ) || !strncmp( key, "0X", 2 ))
+ if (!HexToBinary( key ))
+ break;
+ keylen = strlen( key );
+ while (keylen < 7)
+ key[keylen++] = '\0';
+ pdpy->key.data[0] = '\0';
+ memmove( pdpy->key.data + 1, key, 7 );
+ bzero( key, sizeof(key) );
+ fclose( keys );
+ return TRUE;
+ }
+ }
+ bzero( line, sizeof(line) );
+ bzero( key, sizeof(key) );
+ fclose( keys );
+ return FALSE;
+}
+
+/*ARGSUSED*/
+int
+XdmCheckAuthentication( struct protoDisplay *pdpy,
+ ARRAY8Ptr displayID,
+ ARRAY8Ptr authenticationName ATTR_UNUSED,
+ ARRAY8Ptr authenticationData )
+{
+ XdmAuthKeyPtr incoming;
+
+ if (!XdmGetKey( pdpy, displayID ))
+ return FALSE;
+ if (authenticationData->length != 8)
+ return FALSE;
+ XdmcpUnwrap( authenticationData->data, (unsigned char *)&pdpy->key,
+ authenticationData->data, 8 );
+ Debug( "request packet auth %02[*hhx\n",
+ authenticationData->length, authenticationData->data );
+ if (!XdmcpCopyARRAY8( authenticationData, &pdpy->authenticationData ))
+ return FALSE;
+ incoming = (XdmAuthKeyPtr)authenticationData->data;
+ XdmcpIncrementKey( incoming );
+ XdmcpWrap( authenticationData->data, (unsigned char *)&pdpy->key,
+ authenticationData->data, 8 );
+ return TRUE;
+}
+
+#endif /* XDMCP */
+#endif /* HASXDMAUTH (covering the entire file) */
diff --git a/kdm/backend/xdmcp.c b/kdm/backend/xdmcp.c
new file mode 100644
index 000000000..2925a6bbc
--- /dev/null
+++ b/kdm/backend/xdmcp.c
@@ -0,0 +1,1160 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+Copyright 2001-2004 Oswald Buddenhagen <ossi@kde.org>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the copyright holder.
+
+*/
+
+/*
+ * xdm - display manager daemon
+ * Author: Keith Packard, MIT X Consortium
+ *
+ * xdmcp.c - Support for XDMCP
+ */
+
+#include <config.h>
+
+#ifdef XDMCP
+
+#include "dm.h"
+#include "dm_error.h"
+#include "dm_auth.h"
+#include "dm_socket.h"
+
+#include <sys/types.h>
+#include <ctype.h>
+
+#include <netdb.h>
+#if defined(IPv6) && defined(AF_INET6)
+# include <arpa/inet.h>
+#endif
+
+/*
+ * Forward reference
+ */
+static void broadcast_respond( struct sockaddr *from, int fromlen, int length, int fd );
+static void forward_respond (struct sockaddr *from, int fromlen, int length, int fd);
+static void manage( struct sockaddr *from, int fromlen, int length, int fd );
+static void query_respond( struct sockaddr *from, int fromlen, int length, int fd );
+static void request_respond( struct sockaddr *from, int fromlen, int length, int fd );
+static void send_accept( struct sockaddr *to, int tolen, CARD32 sessionID, ARRAY8Ptr authenticationName, ARRAY8Ptr authenticationData, ARRAY8Ptr authorizationName, ARRAY8Ptr authorizationData, int fd );
+static void send_alive( struct sockaddr *from, int fromlen, int length, int fd );
+static void send_decline( struct sockaddr *to, int tolen, ARRAY8Ptr authenticationName, ARRAY8Ptr authenticationData, ARRAY8Ptr status, int fd );
+static void send_failed( struct sockaddr *from, int fromlen, const char *name, CARD32 sessionID, const char *reason, int fd );
+static void send_refuse( struct sockaddr *from, int fromlen, CARD32 sessionID, int fd );
+static void send_unwilling( struct sockaddr *from, int fromlen, ARRAY8Ptr authenticationName, ARRAY8Ptr status, int fd );
+static void send_willing( struct sockaddr *from, int fromlen, ARRAY8Ptr authenticationName, ARRAY8Ptr status, int fd );
+
+
+static XdmcpBuffer buffer;
+
+static void
+sendForward( CARD16 connectionType, ARRAY8Ptr address, char *closure )
+{
+#ifdef AF_INET
+ struct sockaddr_in in_addr;
+#endif
+#if defined(IPv6) && defined(AF_INET6)
+ struct sockaddr_in6 in6_addr;
+#endif
+#ifdef AF_DECnet
+#endif
+ struct sockaddr *addr;
+ int addrlen;
+
+ switch (connectionType) {
+#ifdef AF_INET
+ case FamilyInternet:
+ addr = (struct sockaddr *)&in_addr;
+ bzero( (char *)&in_addr, sizeof(in_addr) );
+# ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ in_addr.sin_len = sizeof(in_addr);
+# endif
+ in_addr.sin_family = AF_INET;
+ in_addr.sin_port = htons( (short)XDM_UDP_PORT );
+ if (address->length != 4)
+ return;
+ memmove( (char *)&in_addr.sin_addr, address->data, address->length );
+ addrlen = sizeof(struct sockaddr_in);
+ break;
+#endif
+#if defined(IPv6) && defined(AF_INET6)
+ case FamilyInternet6:
+ addr = (struct sockaddr *)&in6_addr;
+ bzero( (char *)&in6_addr, sizeof(in6_addr) );
+# ifdef SIN6_LEN
+ in6_addr.sin6_len = sizeof(in6_addr);
+# endif
+ in6_addr.sin6_family = AF_INET6;
+ in6_addr.sin6_port = htons( (short)XDM_UDP_PORT );
+ if (address->length != 16)
+ return;
+ memmove( (char *)&in6_addr.sin6_addr, address->data, address->length );
+ addrlen = sizeof(struct sockaddr_in6);
+ break;
+#endif
+#ifdef AF_DECnet
+ case FamilyDECnet:
+#endif
+ default:
+ return;
+ }
+ XdmcpFlush( (int)closure, &buffer, (XdmcpNetaddr)addr, addrlen );
+ return;
+}
+
+static void
+ClientAddress( struct sockaddr *from,
+ ARRAY8Ptr addr, /* return */
+ ARRAY8Ptr port, /* return */
+ CARD16 *type ) /* return */
+{
+ int length, family;
+ char *data;
+
+ data = NetaddrPort( (XdmcpNetaddr)from, &length );
+ XdmcpAllocARRAY8( port, length );
+ memmove( port->data, data, length );
+ port->length = length;
+
+ family = ConvertAddr( (XdmcpNetaddr)from, &length, &data );
+ XdmcpAllocARRAY8( addr, length );
+ memmove( addr->data, data, length );
+ addr->length = length;
+
+ *type = family;
+}
+
+static void
+all_query_respond( struct sockaddr *from, int fromlen,
+ ARRAYofARRAY8Ptr authenticationNames,
+ xdmOpCode type, int fd )
+{
+ ARRAY8Ptr authenticationName;
+ ARRAY8 status;
+ ARRAY8 addr;
+ CARD16 connectionType;
+ int family;
+ int length;
+
+ family = ConvertAddr( (XdmcpNetaddr)from, &length, &(addr.data) );
+ addr.length = length; /* convert int to short */
+ Debug( "all_query_respond: conntype=%d, addr=%02[*:hhx\n",
+ family, addr.length, addr.data );
+ if (family < 0)
+ return;
+ connectionType = family;
+
+ if (type == INDIRECT_QUERY)
+ RememberIndirectClient( &addr, connectionType );
+ else
+ ForgetIndirectClient( &addr, connectionType );
+
+ authenticationName = ChooseAuthentication( authenticationNames );
+ if (Willing( &addr, connectionType, authenticationName, &status, type ))
+ send_willing( from, fromlen, authenticationName, &status, fd );
+ else
+ if (type == QUERY)
+ send_unwilling( from, fromlen, authenticationName, &status, fd );
+ XdmcpDisposeARRAY8( &status );
+}
+
+static void
+indirect_respond( struct sockaddr *from, int fromlen, int length, int fd )
+{
+ ARRAYofARRAY8 queryAuthenticationNames;
+ ARRAY8 clientAddress;
+ ARRAY8 clientPort;
+ CARD16 connectionType;
+ int expectedLen;
+ int i;
+ XdmcpHeader header;
+ int localHostAsWell;
+
+ Debug( "<indirect> respond %d\n", length );
+ if (!XdmcpReadARRAYofARRAY8( &buffer, &queryAuthenticationNames ))
+ return;
+ expectedLen = 1;
+ for (i = 0; i < (int)queryAuthenticationNames.length; i++)
+ expectedLen += 2 + queryAuthenticationNames.data[i].length;
+ if (length == expectedLen) {
+ ClientAddress( from, &clientAddress, &clientPort, &connectionType );
+ /*
+ * set up the forward query packet
+ */
+ header.version = XDM_PROTOCOL_VERSION;
+ header.opcode = (CARD16)FORWARD_QUERY;
+ header.length = 0;
+ header.length += 2 + clientAddress.length;
+ header.length += 2 + clientPort.length;
+ header.length += 1;
+ for (i = 0; i < (int)queryAuthenticationNames.length; i++)
+ header.length += 2 + queryAuthenticationNames.data[i].length;
+ XdmcpWriteHeader( &buffer, &header );
+ XdmcpWriteARRAY8( &buffer, &clientAddress );
+ XdmcpWriteARRAY8( &buffer, &clientPort );
+ XdmcpWriteARRAYofARRAY8( &buffer, &queryAuthenticationNames );
+
+ localHostAsWell =
+ ForEachMatchingIndirectHost( &clientAddress, connectionType,
+ sendForward, (char *)fd );
+
+ XdmcpDisposeARRAY8( &clientAddress );
+ XdmcpDisposeARRAY8( &clientPort );
+ if (localHostAsWell)
+ all_query_respond( from, fromlen, &queryAuthenticationNames,
+ INDIRECT_QUERY, fd );
+ } else
+ Debug( "<indirect> length error got %d expect %d\n",
+ length, expectedLen );
+ XdmcpDisposeARRAYofARRAY8( &queryAuthenticationNames );
+}
+
+void
+ProcessRequestSocket( int fd )
+{
+ XdmcpHeader header;
+#if defined(IPv6) && defined(AF_INET6)
+ struct sockaddr_storage addr;
+#else
+ struct sockaddr addr;
+#endif
+ int addrlen = sizeof(addr);
+
+ Debug( "ProcessRequestSocket\n" );
+ bzero( (char *)&addr, sizeof(addr) );
+ if (!XdmcpFill( fd, &buffer, (XdmcpNetaddr)&addr, &addrlen )) {
+ Debug( "XdmcpFill failed\n" );
+ return;
+ }
+ if (!XdmcpReadHeader( &buffer, &header )) {
+ Debug( "XdmcpReadHeader failed\n" );
+ return;
+ }
+ if (header.version != XDM_PROTOCOL_VERSION) {
+ Debug( "XDMCP header version read was %d, expected %d\n",
+ header.version, XDM_PROTOCOL_VERSION );
+ return;
+ }
+ Debug( "header: %d %d %d\n", header.version, header.opcode, header.length );
+ switch (header.opcode) {
+ case BROADCAST_QUERY:
+ broadcast_respond( (struct sockaddr *)&addr, addrlen, header.length, fd );
+ break;
+ case QUERY:
+ query_respond( (struct sockaddr *)&addr, addrlen, header.length, fd );
+ break;
+ case INDIRECT_QUERY:
+ indirect_respond( (struct sockaddr *)&addr, addrlen, header.length, fd );
+ break;
+ case FORWARD_QUERY:
+ forward_respond ((struct sockaddr *)&addr, addrlen, header.length, fd);
+ break;
+ case REQUEST:
+ request_respond( (struct sockaddr *)&addr, addrlen, header.length, fd );
+ break;
+ case MANAGE:
+ manage( (struct sockaddr *)&addr, addrlen, header.length, fd );
+ break;
+ case KEEPALIVE:
+ send_alive( (struct sockaddr *)&addr, addrlen, header.length, fd );
+ break;
+ }
+}
+
+/*
+ * respond to a request on the UDP socket.
+ */
+
+static void
+direct_query_respond( struct sockaddr *from, int fromlen,
+ int length, xdmOpCode type, int fd )
+{
+ ARRAYofARRAY8 queryAuthenticationNames;
+ int expectedLen;
+ int i;
+
+ if (!XdmcpReadARRAYofARRAY8( &buffer, &queryAuthenticationNames ))
+ return;
+ expectedLen = 1;
+ for (i = 0; i < (int)queryAuthenticationNames.length; i++)
+ expectedLen += 2 + queryAuthenticationNames.data[i].length;
+ if (length == expectedLen)
+ all_query_respond( from, fromlen, &queryAuthenticationNames, type, fd );
+ XdmcpDisposeARRAYofARRAY8( &queryAuthenticationNames );
+}
+
+static void
+query_respond( struct sockaddr *from, int fromlen, int length, int fd )
+{
+ Debug( "<query> respond %d\n", length );
+ direct_query_respond( from, fromlen, length, QUERY, fd );
+}
+
+static void
+broadcast_respond( struct sockaddr *from, int fromlen, int length, int fd )
+{
+ direct_query_respond( from, fromlen, length, BROADCAST_QUERY, fd );
+}
+
+/* computes an X display name */
+
+static char *
+NetworkAddressToName( CARD16 connectionType, ARRAY8Ptr connectionAddress,
+ struct sockaddr *originalAddress, CARD16 displayNumber )
+{
+ switch (connectionType) {
+ case FamilyInternet:
+#if defined(IPv6) && defined(AF_INET6)
+ case FamilyInternet6:
+#endif
+ {
+ CARD8 *data;
+ struct hostent *hostent;
+ char *hostname = NULL;
+ char *name;
+ const char *localhost;
+ int multiHomed = 0;
+ int type;
+#if defined(IPv6) && defined(AF_INET6)
+ struct addrinfo *ai = NULL, *nai, hints;
+ char dotted[INET6_ADDRSTRLEN];
+
+ if (connectionType == FamilyInternet6)
+ type = AF_INET6;
+ else
+#endif
+ type = AF_INET;
+
+ data = connectionAddress->data;
+ hostent = gethostbyaddr( (char *)data,
+ connectionAddress->length, type );
+ if (hostent) {
+ if (sourceAddress) {
+#if defined(IPv6) && defined(AF_INET6)
+ bzero( &hints, sizeof(hints) );
+ hints.ai_flags = AI_CANONNAME;
+ if (!getaddrinfo( hostent->h_name, NULL, &hints, &ai )) {
+ hostname = ai->ai_canonname;
+ for (nai = ai->ai_next; nai; nai = nai->ai_next)
+ if (ai->ai_protocol == nai->ai_protocol &&
+ memcmp( ai->ai_addr, nai->ai_addr,
+ ai->ai_addrlen ))
+ multiHomed = 1;
+ }
+#else
+ hostent = gethostbyname( hostent->h_name );
+ if (hostent && hostent->h_addrtype == AF_INET) {
+ multiHomed = hostent->h_addr_list[1] != NULL;
+ hostname = hostent->h_name;
+ }
+#endif
+ } else
+ hostname = hostent->h_name;
+ }
+
+ localhost = localHostname();
+
+ /*
+ * protect against bogus host names
+ */
+ if (hostname && *hostname && *hostname != '.' && !multiHomed) {
+ if (!strcmp( localhost, hostname ))
+ ASPrintf( &name, "localhost:%d", displayNumber );
+ else {
+ if (removeDomainname) {
+ char *localDot, *remoteDot;
+
+ /* check for a common domain name. This
+ * could reduce names by recognising common
+ * super-domain names as well, but I don't think
+ * this is as useful, and will confuse more
+ * people
+ */
+ if ((localDot = strchr( localhost, '.' )) &&
+ (remoteDot = strchr( hostname, '.' )))
+ {
+ /* smash the name in place; it won't
+ * be needed later.
+ */
+ if (!strcmp( localDot+1, remoteDot+1 ))
+ *remoteDot = '\0';
+ }
+ }
+
+ ASPrintf( &name, "%s:%d", hostname, displayNumber );
+ }
+ } else {
+#if defined(IPv6) && defined(AF_INET6)
+ if (multiHomed) {
+ if (connectionType == FamilyInternet) {
+ data = (CARD8 *)
+ &((struct sockaddr_in *)originalAddress)->sin_addr;
+ } else {
+ data = (CARD8 *)
+ &((struct sockaddr_in6 *)originalAddress)->sin6_addr;
+ }
+ }
+ inet_ntop( type, data, dotted, sizeof(dotted) );
+ ASPrintf( &name, "%s:%d", dotted, displayNumber );
+#else
+ if (multiHomed)
+ data = (CARD8 *)
+ &((struct sockaddr_in *)originalAddress)->sin_addr;
+ ASPrintf( &name, "%[4|'.'hhu:%d", data, displayNumber );
+#endif
+ }
+#if defined(IPv6) && defined(AF_INET6)
+ if (ai)
+ freeaddrinfo( ai );
+#endif
+ return name;
+ }
+#ifdef DNET
+ case FamilyDECnet:
+ return NULL;
+#endif /* DNET */
+ default:
+ return NULL;
+ }
+}
+
+/*ARGSUSED*/
+static void
+forward_respond ( struct sockaddr *from, int fromlen ATTR_UNUSED,
+ int length, int fd)
+{
+ ARRAY8 clientAddress;
+ ARRAY8 clientPort;
+ ARRAYofARRAY8 authenticationNames;
+ struct sockaddr *client;
+ int clientlen;
+ int expectedLen;
+ int i;
+
+ Debug( "<forward> respond %d\n", length );
+ clientAddress.length = 0;
+ clientAddress.data = 0;
+ clientPort.length = 0;
+ clientPort.data = 0;
+ authenticationNames.length = 0;
+ authenticationNames.data = 0;
+ if (XdmcpReadARRAY8( &buffer, &clientAddress ) &&
+ XdmcpReadARRAY8( &buffer, &clientPort ) &&
+ XdmcpReadARRAYofARRAY8( &buffer, &authenticationNames ))
+ {
+ expectedLen = 0;
+ expectedLen += 2 + clientAddress.length;
+ expectedLen += 2 + clientPort.length;
+ expectedLen += 1; /* authenticationNames */
+ for (i = 0; i < (int)authenticationNames.length; i++)
+ expectedLen += 2 + authenticationNames.data[i].length;
+ if (length == expectedLen) {
+ int j;
+
+ j = 0;
+ for (i = 0; i < (int)clientPort.length; i++)
+ j = j * 256 + clientPort.data[i];
+ Debug( "<forward> client address (port %d) %[*hhu\n", j,
+ clientAddress.length, clientAddress.data );
+ switch (from->sa_family) {
+#ifdef AF_INET
+ case AF_INET:
+ {
+ struct sockaddr_in in_addr;
+
+ if (clientAddress.length != 4 || clientPort.length != 2)
+ goto badAddress;
+ bzero( (char *)&in_addr, sizeof(in_addr) );
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ in_addr.sin_len = sizeof(in_addr);
+#endif
+ in_addr.sin_family = AF_INET;
+ memmove( &in_addr.sin_addr, clientAddress.data, 4 );
+ memmove( (char *)&in_addr.sin_port, clientPort.data, 2 );
+ client = (struct sockaddr *)&in_addr;
+ clientlen = sizeof(in_addr);
+ all_query_respond( client, clientlen, &authenticationNames,
+ FORWARD_QUERY, fd );
+ }
+ break;
+#endif
+#if defined(IPv6) && defined(AF_INET6)
+ case AF_INET6:
+ {
+ struct sockaddr_in6 in6_addr;
+
+ if (clientAddress.length != 16 || clientPort.length != 2)
+ goto badAddress;
+ bzero( (char *)&in6_addr, sizeof(in6_addr) );
+#ifdef SIN6_LEN
+ in6_addr.sin6_len = sizeof(in6_addr);
+#endif
+ in6_addr.sin6_family = AF_INET6;
+ memmove( &in6_addr,clientAddress.data,clientAddress.length );
+ memmove( (char *)&in6_addr.sin6_port, clientPort.data, 2 );
+ client = (struct sockaddr *)&in6_addr;
+ clientlen = sizeof(in6_addr);
+ all_query_respond( client, clientlen, &authenticationNames,
+ FORWARD_QUERY, fd );
+ }
+ break;
+#endif
+#ifdef AF_UNIX
+ case AF_UNIX:
+ {
+ struct sockaddr_un un_addr;
+
+ if (clientAddress.length >= sizeof(un_addr.sun_path))
+ goto badAddress;
+ bzero( (char *)&un_addr, sizeof(un_addr) );
+ un_addr.sun_family = AF_UNIX;
+ memmove( un_addr.sun_path, clientAddress.data, clientAddress.length );
+ un_addr.sun_path[clientAddress.length] = '\0';
+ client = (struct sockaddr *)&un_addr;
+#if defined(HAVE_STRUCT_SOCKADDR_IN_SIN_LEN) && !defined(__Lynx__) && defined(UNIXCONN)
+ un_addr.sun_len = strlen( un_addr.sun_path );
+ clientlen = SUN_LEN( &un_addr );
+#else
+ clientlen = sizeof(un_addr);
+#endif
+ all_query_respond( client, clientlen, &authenticationNames,
+ FORWARD_QUERY, fd );
+ }
+ break;
+#endif
+#ifdef AF_CHAOS
+ case AF_CHAOS:
+ goto badAddress;
+#endif
+#ifdef AF_DECnet
+ case AF_DECnet:
+ goto badAddress;
+#endif
+ }
+ } else
+ Debug( "<forward> length error got %d expect %d\n", length, expectedLen );
+ }
+ badAddress:
+ XdmcpDisposeARRAY8( &clientAddress );
+ XdmcpDisposeARRAY8( &clientPort );
+ XdmcpDisposeARRAYofARRAY8( &authenticationNames );
+}
+
+static ARRAY8 Hostname;
+
+static void
+send_willing( struct sockaddr *from, int fromlen,
+ ARRAY8Ptr authenticationName, ARRAY8Ptr status, int fd )
+{
+ XdmcpHeader header;
+
+ Debug( "send <willing> %.*s %.*s\n", authenticationName->length,
+ authenticationName->data,
+ status->length,
+ status->data );
+ header.version = XDM_PROTOCOL_VERSION;
+ header.opcode = (CARD16)WILLING;
+ header.length =
+ 6 + authenticationName->length + Hostname.length + status->length;
+ XdmcpWriteHeader( &buffer, &header );
+ XdmcpWriteARRAY8( &buffer, authenticationName );
+ XdmcpWriteARRAY8( &buffer, &Hostname );
+ XdmcpWriteARRAY8( &buffer, status );
+ XdmcpFlush( fd, &buffer, (XdmcpNetaddr)from, fromlen );
+}
+
+static void
+send_unwilling( struct sockaddr *from, int fromlen,
+ ARRAY8Ptr authenticationName, ARRAY8Ptr status, int fd )
+{
+ XdmcpHeader header;
+
+ Debug( "send <unwilling> %.*s %.*s\n", authenticationName->length,
+ authenticationName->data,
+ status->length,
+ status->data );
+ header.version = XDM_PROTOCOL_VERSION;
+ header.opcode = (CARD16)UNWILLING;
+ header.length = 4 + Hostname.length + status->length;
+ XdmcpWriteHeader( &buffer, &header );
+ XdmcpWriteARRAY8( &buffer, &Hostname );
+ XdmcpWriteARRAY8( &buffer, status );
+ XdmcpFlush( fd, &buffer, (XdmcpNetaddr)from, fromlen );
+}
+
+static unsigned long globalSessionID;
+
+#define NextSessionID() (++globalSessionID)
+
+void init_session_id( void )
+{
+ /* Set randomly so we are unlikely to reuse id's from a previous
+ * incarnation so we don't say "Alive" to those displays.
+ * Start with low digits 0 to make debugging easier.
+ */
+ globalSessionID = (time( (Time_t *)0 ) & 0x7fff) * 16000;
+
+ Hostname.data = (char *)localHostname();
+ Hostname.length = strlen( Hostname.data );
+}
+
+static ARRAY8 outOfMemory = { (CARD16)13, (CARD8Ptr)"Out of memory" };
+static ARRAY8 noValidAddr = { (CARD16)16, (CARD8Ptr)"No valid address" };
+static ARRAY8 noValidAuth = { (CARD16)22, (CARD8Ptr)"No valid authorization" };
+static ARRAY8 noAuthentic = { (CARD16)29, (CARD8Ptr)"XDM has no authentication key" };
+
+static void
+request_respond( struct sockaddr *from, int fromlen, int length, int fd )
+{
+ CARD16 displayNumber;
+ ARRAY16 connectionTypes;
+ ARRAYofARRAY8 connectionAddresses;
+ ARRAY8 authenticationName;
+ ARRAY8 authenticationData;
+ ARRAYofARRAY8 authorizationNames;
+ ARRAY8 manufacturerDisplayID;
+ ARRAY8Ptr reason = 0;
+ int expectlen;
+ int i, j;
+ struct protoDisplay *pdpy;
+ ARRAY8 authorizationName, authorizationData;
+ ARRAY8Ptr connectionAddress;
+
+ Debug( "<request> respond %d\n", length );
+ connectionTypes.data = 0;
+ connectionAddresses.data = 0;
+ authenticationName.data = 0;
+ authenticationData.data = 0;
+ authorizationNames.data = 0;
+ authorizationName.length = 0;
+ authorizationData.length = 0;
+ manufacturerDisplayID.data = 0;
+ if (XdmcpReadCARD16( &buffer, &displayNumber ) &&
+ XdmcpReadARRAY16( &buffer, &connectionTypes ) &&
+ XdmcpReadARRAYofARRAY8( &buffer, &connectionAddresses ) &&
+ XdmcpReadARRAY8( &buffer, &authenticationName ) &&
+ XdmcpReadARRAY8( &buffer, &authenticationData ) &&
+ XdmcpReadARRAYofARRAY8( &buffer, &authorizationNames ) &&
+ XdmcpReadARRAY8( &buffer, &manufacturerDisplayID ))
+ {
+ expectlen = 0;
+ expectlen += 2; /* displayNumber */
+ expectlen += 1 + 2 * connectionTypes.length; /* connectionTypes */
+ expectlen += 1; /* connectionAddresses */
+ for (i = 0; i < (int)connectionAddresses.length; i++)
+ expectlen += 2 + connectionAddresses.data[i].length;
+ expectlen += 2 + authenticationName.length; /* authenticationName */
+ expectlen += 2 + authenticationData.length; /* authenticationData */
+ expectlen += 1; /* authoriationNames */
+ for (i = 0; i < (int)authorizationNames.length; i++)
+ expectlen += 2 + authorizationNames.data[i].length;
+ expectlen += 2 + manufacturerDisplayID.length; /* displayID */
+ if (expectlen != length) {
+ Debug( "<request> length error got %d expect %d\n",
+ length, expectlen );
+ goto abort;
+ }
+ if (connectionTypes.length == 0 ||
+ connectionAddresses.length != connectionTypes.length)
+ {
+ reason = &noValidAddr;
+ pdpy = 0;
+ goto decline;
+ }
+ pdpy = FindProtoDisplay( (XdmcpNetaddr)from, fromlen, displayNumber );
+ if (!pdpy) {
+
+ /* Check this Display against the Manager's policy */
+ reason = Accept( from, fromlen, displayNumber );
+ if (reason)
+ goto decline;
+
+ /* Check the Display's stream services against Manager's policy */
+ i = SelectConnectionTypeIndex( &connectionTypes,
+ &connectionAddresses );
+ if (i < 0) {
+ reason = &noValidAddr;
+ goto decline;
+ }
+
+ /* The Manager considers this a new session */
+ connectionAddress = &connectionAddresses.data[i];
+ pdpy = NewProtoDisplay( (XdmcpNetaddr)from, fromlen, displayNumber,
+ connectionTypes.data[i], connectionAddress,
+ NextSessionID() );
+ Debug( "NewProtoDisplay %p\n", pdpy );
+ if (!pdpy) {
+ reason = &outOfMemory;
+ goto decline;
+ }
+ }
+ if (authorizationNames.length == 0)
+ j = 0;
+ else
+ j = SelectAuthorizationTypeIndex( &authenticationName,
+ &authorizationNames );
+ if (j < 0) {
+ reason = &noValidAuth;
+ goto decline;
+ }
+ if (!CheckAuthentication( pdpy,
+ &manufacturerDisplayID,
+ &authenticationName,
+ &authenticationData ))
+ {
+ reason = &noAuthentic;
+ goto decline;
+ }
+ if (j < (int)authorizationNames.length) {
+ Xauth *auth;
+ SetProtoDisplayAuthorization( pdpy,
+ (unsigned short)authorizationNames.data[j].length,
+ (char *)authorizationNames.data[j].data );
+ auth = pdpy->xdmcpAuthorization;
+ if (!auth)
+ auth = pdpy->fileAuthorization;
+ if (auth) {
+ authorizationName.length = auth->name_length;
+ authorizationName.data = (CARD8Ptr) auth->name;
+ authorizationData.length = auth->data_length;
+ authorizationData.data = (CARD8Ptr) auth->data;
+ }
+ }
+ if (pdpy) {
+ send_accept( from, fromlen, pdpy->sessionID,
+ &authenticationName,
+ &authenticationData,
+ &authorizationName,
+ &authorizationData, fd );
+ } else {
+ decline:
+ send_decline( from, fromlen, &authenticationName,
+ &authenticationData,
+ reason, fd );
+ if (pdpy)
+ DisposeProtoDisplay( pdpy );
+ }
+ }
+ abort:
+ XdmcpDisposeARRAY16( &connectionTypes );
+ XdmcpDisposeARRAYofARRAY8( &connectionAddresses );
+ XdmcpDisposeARRAY8( &authenticationName );
+ XdmcpDisposeARRAY8( &authenticationData );
+ XdmcpDisposeARRAYofARRAY8( &authorizationNames );
+ XdmcpDisposeARRAY8( &manufacturerDisplayID );
+}
+
+static void
+send_accept( struct sockaddr *to, int tolen, CARD32 sessionID,
+ ARRAY8Ptr authenticationName, ARRAY8Ptr authenticationData,
+ ARRAY8Ptr authorizationName, ARRAY8Ptr authorizationData,
+ int fd )
+{
+ XdmcpHeader header;
+
+ Debug( "<accept> session ID %ld\n", (long)sessionID );
+ header.version = XDM_PROTOCOL_VERSION;
+ header.opcode = (CARD16)ACCEPT;
+ header.length = 4; /* session ID */
+ header.length += 2 + authenticationName->length;
+ header.length += 2 + authenticationData->length;
+ header.length += 2 + authorizationName->length;
+ header.length += 2 + authorizationData->length;
+ XdmcpWriteHeader( &buffer, &header );
+ XdmcpWriteCARD32( &buffer, sessionID );
+ XdmcpWriteARRAY8( &buffer, authenticationName );
+ XdmcpWriteARRAY8( &buffer, authenticationData );
+ XdmcpWriteARRAY8( &buffer, authorizationName );
+ XdmcpWriteARRAY8( &buffer, authorizationData );
+ XdmcpFlush( fd, &buffer, (XdmcpNetaddr)to, tolen );
+}
+
+static void
+send_decline( struct sockaddr *to, int tolen,
+ ARRAY8Ptr authenticationName, ARRAY8Ptr authenticationData,
+ ARRAY8Ptr status, int fd )
+{
+ XdmcpHeader header;
+
+ Debug( "<decline> %.*s\n", status->length, status->data );
+ header.version = XDM_PROTOCOL_VERSION;
+ header.opcode = (CARD16)DECLINE;
+ header.length = 0;
+ header.length += 2 + status->length;
+ header.length += 2 + authenticationName->length;
+ header.length += 2 + authenticationData->length;
+ XdmcpWriteHeader( &buffer, &header );
+ XdmcpWriteARRAY8( &buffer, status );
+ XdmcpWriteARRAY8( &buffer, authenticationName );
+ XdmcpWriteARRAY8( &buffer, authenticationData );
+ XdmcpFlush( fd, &buffer, (XdmcpNetaddr)to, tolen );
+}
+
+static void
+manage( struct sockaddr *from, int fromlen, int length, int fd )
+{
+ CARD32 sessionID;
+ CARD16 displayNumber;
+ ARRAY8 displayClass;
+ int expectlen;
+ struct protoDisplay *pdpy;
+ struct display *d;
+ char *name = NULL;
+ char *class2 = NULL;
+ XdmcpNetaddr from_save;
+ ARRAY8 clientAddress, clientPort;
+ CARD16 connectionType;
+
+ Debug( "<manage> %d\n", length );
+ displayClass.data = 0;
+ displayClass.length = 0;
+ if (XdmcpReadCARD32( &buffer, &sessionID ) &&
+ XdmcpReadCARD16( &buffer, &displayNumber ) &&
+ XdmcpReadARRAY8( &buffer, &displayClass ))
+ {
+ expectlen = 4 + /* session ID */
+ 2 + /* displayNumber */
+ 2 + displayClass.length; /* displayClass */
+ if (expectlen != length) {
+ Debug( "<manage> length error got %d expect %d\n", length, expectlen );
+ goto abort;
+ }
+ pdpy = FindProtoDisplay( (XdmcpNetaddr)from, fromlen, displayNumber );
+ Debug( "<manage> session ID %ld, pdpy %p\n", (long)sessionID, pdpy );
+ if (!pdpy || pdpy->sessionID != sessionID) {
+ /*
+ * We may have already started a session for this display
+ * but it hasn't seen the response in the form of an
+ * XOpenDisplay() yet. So check if it is in the list of active
+ * displays, and if so check that the session id's match.
+ * If all this is true, then we have a duplicate request that
+ * can be ignored.
+ */
+ if (!pdpy &&
+ (d = FindDisplayByAddress( (XdmcpNetaddr)from, fromlen,
+ displayNumber )) &&
+ d->sessionID == sessionID)
+ {
+ Debug( "manage: got duplicate pkt, ignoring\n" );
+ goto abort;
+ }
+ Debug( "session ID %ld refused\n", (long)sessionID );
+ if (pdpy)
+ Debug( "existing session ID %ld\n", (long)pdpy->sessionID );
+ send_refuse( from, fromlen, sessionID, fd );
+ } else {
+ name = NetworkAddressToName( pdpy->connectionType,
+ &pdpy->connectionAddress,
+ from,
+ pdpy->displayNumber );
+ if (!name) {
+ Debug( "could not compute display name\n" );
+ send_failed( from, fromlen, "(no name)", sessionID,
+ "out of memory", fd );
+ goto abort;
+ }
+ Debug( "computed display name: %s\n", name );
+ if ((d = FindDisplayByName( name ))) {
+ Debug( "terminating active session for %s\n", d->name );
+ StopDisplay( d );
+ }
+ if (displayClass.length) {
+ if (!StrNDup( &class2, (char *)displayClass.data,
+ displayClass.length ))
+ {
+ send_failed( from, fromlen, name, sessionID,
+ "out of memory", fd );
+ goto abort;
+ }
+ }
+ if (!(from_save = (XdmcpNetaddr)Malloc( fromlen ))) {
+ send_failed( from, fromlen, name, sessionID,
+ "out of memory", fd );
+ goto abort;
+ }
+ memmove( from_save, from, fromlen );
+ if (!(d = NewDisplay( name ))) {
+ free( (char *)from_save );
+ send_failed( from, fromlen, name, sessionID,
+ "out of memory", fd );
+ goto abort;
+ }
+ d->class2 = class2;
+ class2 = 0;
+ d->displayType = dForeign | dTransient | dFromXDMCP;
+ d->sessionID = pdpy->sessionID;
+ d->from.data = (unsigned char *)from_save;
+ d->from.length = fromlen;
+ d->displayNumber = pdpy->displayNumber;
+ ClientAddress( from, &clientAddress, &clientPort,
+ &connectionType );
+ d->useChooser = 0;
+ d->xdmcpFd = fd;
+ if (IsIndirectClient( &clientAddress, connectionType )) {
+ Debug( "IsIndirectClient\n" );
+ ForgetIndirectClient( &clientAddress, connectionType );
+ if (UseChooser( &clientAddress, connectionType )) {
+ d->useChooser = 1;
+ Debug( "use chooser for %s\n", d->name );
+ }
+ }
+ d->clientAddr = clientAddress;
+ d->connectionType = connectionType;
+ XdmcpDisposeARRAY8( &clientPort );
+ if (pdpy->fileAuthorization) {
+ d->authorizations = (Xauth **)Malloc( sizeof(Xauth *) );
+ if (!d->authorizations) {
+ free( (char *)from_save );
+ free( (char *)d );
+ send_failed( from, fromlen, name, sessionID,
+ "out of memory", fd );
+ goto abort;
+ }
+ d->authorizations[0] = pdpy->fileAuthorization;
+ d->authNum = 1;
+ pdpy->fileAuthorization = 0;
+ }
+ DisposeProtoDisplay( pdpy );
+ Debug( "starting display %s,%s\n", d->name, d->class2 );
+ if (LoadDisplayResources( d ) < 0) {
+ LogError( "Unable to read configuration for display %s; "
+ "stopping it.\n", d->name );
+ StopDisplay( d );
+ } else
+ StartDisplay( d );
+ CloseGetter();
+ }
+ }
+abort:
+ XdmcpDisposeARRAY8( &displayClass );
+ if (name)
+ free( (char *)name );
+ if (class2)
+ free( (char *)class2 );
+}
+
+void
+SendFailed( struct display *d, const char *reason )
+{
+ Debug( "display start failed, sending <failed>\n" );
+ send_failed( (struct sockaddr *)(d->from.data), d->from.length, d->name,
+ d->sessionID, reason, d->xdmcpFd );
+}
+
+static void
+send_failed( struct sockaddr *from, int fromlen,
+ const char *name, CARD32 sessionID, const char *reason, int fd )
+{
+ char buf[360];
+ XdmcpHeader header;
+ ARRAY8 status;
+
+ sprintf( buf, "Session %ld failed for display %.260s: %s",
+ (long)sessionID, name, reason );
+ Debug( "send_failed(%\"s)\n", buf );
+ status.length = strlen( buf );
+ status.data = (CARD8Ptr) buf;
+ header.version = XDM_PROTOCOL_VERSION;
+ header.opcode = (CARD16)FAILED;
+ header.length = 6 + status.length;
+ XdmcpWriteHeader( &buffer, &header );
+ XdmcpWriteCARD32( &buffer, sessionID );
+ XdmcpWriteARRAY8( &buffer, &status );
+ XdmcpFlush( fd, &buffer, (XdmcpNetaddr)from, fromlen );
+}
+
+static void
+send_refuse( struct sockaddr *from, int fromlen, CARD32 sessionID, int fd )
+{
+ XdmcpHeader header;
+
+ Debug( "send <refuse> %ld\n", (long)sessionID );
+ header.version = XDM_PROTOCOL_VERSION;
+ header.opcode = (CARD16)REFUSE;
+ header.length = 4;
+ XdmcpWriteHeader( &buffer, &header );
+ XdmcpWriteCARD32( &buffer, sessionID );
+ XdmcpFlush( fd, &buffer, (XdmcpNetaddr)from, fromlen );
+}
+
+static void
+send_alive( struct sockaddr *from, int fromlen, int length, int fd )
+{
+ CARD32 sessionID;
+ CARD16 displayNumber;
+ struct display *d;
+ XdmcpHeader header;
+ CARD8 sendRunning;
+ CARD32 sendSessionID;
+
+ Debug( "send <alive>\n" );
+ if (XdmcpReadCARD16( &buffer, &displayNumber ) &&
+ XdmcpReadCARD32( &buffer, &sessionID ))
+ {
+ if (length == 6) {
+ if (!(d = FindDisplayBySessionID( sessionID )))
+ d = FindDisplayByAddress( (XdmcpNetaddr)from, fromlen,
+ displayNumber );
+ sendRunning = 0;
+ sendSessionID = 0;
+ if (d && d->status == running) {
+ if (d->sessionID == sessionID)
+ sendRunning = 1;
+ sendSessionID = d->sessionID;
+ }
+ header.version = XDM_PROTOCOL_VERSION;
+ header.opcode = (CARD16)ALIVE;
+ header.length = 5;
+ Debug( "<alive>: %d %ld\n", sendRunning, (long)sendSessionID );
+ XdmcpWriteHeader( &buffer, &header );
+ XdmcpWriteCARD8( &buffer, sendRunning );
+ XdmcpWriteCARD32( &buffer, sendSessionID );
+ XdmcpFlush( fd, &buffer, (XdmcpNetaddr)from, fromlen );
+ }
+ }
+}
+
+char *
+NetworkAddressToHostname( CARD16 connectionType, ARRAY8Ptr connectionAddress )
+{
+ switch (connectionType) {
+ case FamilyInternet:
+#if defined(IPv6) && defined(AF_INET6)
+ case FamilyInternet6:
+#endif
+ {
+ struct hostent *he;
+ char *myDot, *name, *lname;
+ int af_type;
+#if defined(IPv6) && defined(AF_INET6)
+ char dotted[INET6_ADDRSTRLEN];
+
+ if (connectionType == FamilyInternet6)
+ af_type = AF_INET6;
+ else
+#endif
+ af_type = AF_INET;
+
+ he = gethostbyaddr( (char *)connectionAddress->data,
+ connectionAddress->length, af_type );
+ if (he) {
+#if defined(IPv6) && defined(AF_INET6)
+ struct addrinfo *ai, *nai;
+ if (!getaddrinfo( he->h_name, NULL, NULL, &ai )) {
+ for (nai = ai; nai; nai = nai->ai_next) {
+ if (af_type == nai->ai_family &&
+ !memcmp( nai->ai_family == AF_INET ?
+ (char *)&((struct sockaddr_in *)nai->ai_addr)->sin_addr :
+ (char *)&((struct sockaddr_in6 *)nai->ai_addr)->sin6_addr,
+ connectionAddress->data,
+ connectionAddress->length ))
+ {
+ freeaddrinfo( ai );
+ goto oki;
+ }
+ }
+ freeaddrinfo( ai );
+#else
+ if ((he = gethostbyname( he->h_name )) &&
+ he->h_addrtype == AF_INET)
+ {
+ int i;
+ for (i = 0; he->h_addr_list[i]; i++)
+ if (!memcmp( he->h_addr_list[i],
+ connectionAddress->data, 4 ))
+ goto oki;
+#endif
+ LogError( "DNS spoof attempt or misconfigured resolver.\n" );
+ }
+ goto gotnone;
+ oki:
+ if (StrDup( &name, he->h_name ) &&
+ !strchr( name, '.' ) &&
+ (myDot = strchr( localHostname(), '.' )))
+ {
+ if (ASPrintf( &lname, "%s%s", name, myDot )) {
+#if defined(IPv6) && defined(AF_INET6)
+ if (!getaddrinfo( lname, NULL, NULL, &ai )) {
+ for (nai = ai; nai; nai = nai->ai_next) {
+ if (af_type == nai->ai_family &&
+ !memcmp( nai->ai_family == AF_INET ?
+ (char *)&((struct sockaddr_in *)nai->ai_addr)->sin_addr :
+ (char *)&((struct sockaddr_in6 *)nai->ai_addr)->sin6_addr,
+ connectionAddress->data,
+ connectionAddress->length ))
+ {
+ freeaddrinfo( ai );
+ free( name );
+ return lname;
+ }
+ }
+ freeaddrinfo( ai );
+ }
+#else
+ if ((he = gethostbyname( lname )) &&
+ he->h_addrtype == AF_INET)
+ {
+ int i;
+ for (i = 0; he->h_addr_list[i]; i++)
+ if (!memcmp( he->h_addr_list[i],
+ connectionAddress->data, 4 ))
+ {
+ free( name );
+ return lname;
+ }
+ }
+#endif
+ free( lname );
+ }
+ }
+ } else {
+ gotnone:
+ /* can't get name, so use emergency fallback */
+#if defined(IPv6) && defined(AF_INET6)
+ inet_ntop( af_type, connectionAddress->data,
+ dotted, sizeof(dotted) );
+ StrDup( &name, dotted );
+#else
+ ASPrintf( &name, "%[4|'.'hhu", connectionAddress->data );
+#endif
+ LogWarn( "Cannot convert Internet address %s to host name\n",
+ name );
+ }
+ return name;
+ }
+#ifdef DNET
+ case FamilyDECnet:
+ break;
+#endif /* DNET */
+ default:
+ break;
+ }
+ return 0;
+}
+
+#endif /* XDMCP */
+
diff --git a/kdm/config.def b/kdm/config.def
new file mode 100644
index 000000000..ae026ba52
--- /dev/null
+++ b/kdm/config.def
@@ -0,0 +1,2615 @@
+#
+# Copyright 2004-2005 Oswald Buddenhagen <ossi@kde.org>
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation.
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+#
+# Except as contained in this notice, the name of a copyright holders shall
+# not be used in advertising or otherwise to promote the sale, use or
+# other dealings in this Software without prior written authorization
+# from the copyright holders.
+#
+
+# The contents of this section are copied into config.ci verbatim.
+<code>
+#define RCVERMAJOR 2
+#define RCVERMINOR 3
+
+#define KDMCONF KDE_CONFDIR "/kdm"
+#define KDMDATA KDE_DATADIR "/kdm"
+
+#ifdef _AIX
+# define HALT_CMD "/usr/sbin/shutdown -h now"
+# define REBOOT_CMD "/usr/sbin/shutdown -r now"
+#elif defined(BSD)
+# define HALT_CMD "/sbin/shutdown -h now"
+# define REBOOT_CMD "/sbin/shutdown -r now"
+#elif defined(__SVR4)
+# define HALT_CMD "/usr/sbin/halt"
+# define REBOOT_CMD "/usr/sbin/reboot"
+#else
+# define HALT_CMD "/sbin/halt"
+# define REBOOT_CMD "/sbin/reboot"
+#endif
+
+#if defined(BSD) || defined(__linux__)
+# define DEF_USER_PATH "/usr/local/bin:/usr/bin:/bin:" XBINDIR ":/usr/games"
+# define DEF_SYSTEM_PATH "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:" XBINDIR
+#else
+# define DEF_USER_PATH "/usr/local/bin:/usr/bin:/bin:" XBINDIR ":/usr/games:/usr/ucb"
+# define DEF_SYSTEM_PATH "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:" XBINDIR ":/etc:/usr/ucb"
+#endif
+
+#if 0 /*def HASXDMAUTH*/
+# define DEF_AUTH_NAME "XDM-AUTHORIZATION-1,MIT-MAGIC-COOKIE-1"
+#else
+# define DEF_AUTH_NAME "MIT-MAGIC-COOKIE-1"
+#endif
+
+#ifdef __linux__
+# define HAVE_VTS
+#elif defined(__sun__)
+# define DEF_SERVER_TTY "console"
+#elif defined(_AIX)
+# define DEF_SERVER_TTY "lft0"
+#else
+# define DEF_SERVER_TTY ""
+#endif
+
+#ifdef _AIX
+# define DEF_SERVER_CMD XBINDIR "/X -T -force"
+#elif defined(__linux__) || defined(__GNU__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
+/* we just assume that any free *nix installation has a recent xfree86/xorg */
+# define DEF_SERVER_CMD XBINDIR "/X -br"
+#else
+# define DEF_SERVER_CMD XBINDIR "/X"
+#endif
+</code>
+
+# The contents of this section are copied mostly verbatim to the
+# default/example configuration file.
+# Everything indented with a space is considered a comment for the output;
+# it is prefixed with a hash mark but otherwise copied verbatim (except
+# for lines consisting of a single underscore, which generate empty comment
+# lines).
+# Section headers are "condensation seeds" for the Instance:s in the key
+# definitions below.
+<kdmrc>
+ &kdm; master configuration file
+ _
+ Definition: the greeter is the login dialog, i.e., the part of &kdm;
+ which the user sees.
+ _
+ You can configure every X-display individually.
+ Every display has a display name, which consists of a host name
+ (which is empty for local displays specified in {Static|Reserve}Servers),
+ a colon, and a display number. Additionally, a display belongs to a
+ display class (which can be ignored in most cases; the control center
+ does not support this feature at all).
+ Sections with display-specific settings have the formal syntax
+ "[X-" host [":" number [ "_" class ]] "-" sub-section "]"
+ You can use the "*" wildcard for host, number, and class. You may omit
+ trailing components; they are assumed to be "*" then.
+ The host part may be a domain specification like ".inf.tu-dresden.de".
+ It may also be "+", which means non-empty, i.e. remote displays only.
+ From which section a setting is actually taken is determined by these
+ rules:
+ - an exact match takes precedence over a partial match (for the host part),
+ which in turn takes precedence over a wildcard ("+" taking precedence
+ over "*")
+ - precedence decreases from left to right for equally exact matches
+ Example: display name "myhost:0", class "dpy".
+ [X-myhost:0_dpy] precedes
+ [X-myhost:0_*] (same as [X-myhost:0]) precedes
+ [X-myhost:*_dpy] precedes
+ [X-myhost:*_*] (same as [X-myhost]) precedes
+ [X-+:0_dpy] precedes
+ [X-*:0_dpy] precedes
+ [X-*:0_*] (same as [X-*:0]) precedes
+ [X-*:*_*] (same as [X-*])
+ These sections do NOT match this display:
+ [X-hishost], [X-myhost:0_dec], [X-*:1], [X-:*]
+ If a setting is not found in any matching section, the default is used.
+ _
+ Every comment applies to the following section or key. Note that all
+ comments will be lost if you change this file with the kcontrol frontend.
+ The defaults refer to &kdm;'s built-in values, not anything set in this file.
+ _
+ Special characters need to be backslash-escaped (leading and trailing
+ spaces (\\s), tab (\\t), linefeed (\\n), carriage return (\\r) and the
+ backslash itself (\\\\)).
+ In lists, fields are separated with commas without whitespace in between.
+ Some command strings are subject to simplified sh-style word splitting:
+ single quotes (') and double quotes (") have the usual meaning; the backslash
+ quotes everything (not only special characters). Note that the backslashes
+ need to be doubled because of the two levels of quoting.
+
+[General]
+
+[Xdmcp]
+
+[Shutdown]
+
+ Rough estimations about how many seconds &kdm; will spend at most on
+ - opening a connection to the X-server (OpenTime) if the attempt
+ - times out: OpenTimeout
+ - is refused: OpenRepeat * OpenDelay
+ - starting a local X-server (ServerTime):
+ ServerAttempts * (ServerTimeout + OpenDelay)
+ - starting a display:
+ - local display: ServerTime + OpenTime
+ - foreign display: StartAttempts * OpenTime
+ - &XDMCP; display: OpenTime (repeated indefinitely by client)
+
+ Core config for all displays
+[X-*-Core]
+
+ Greeter config for all displays
+[X-*-Greeter]
+
+ Core config for local displays
+[X-:*-Core]
+
+ Greeter config for local displays
+[X-:*-Greeter]
+
+ Core config for 1st local display
+[X-:0-Core]
+
+ Greeter config for 1st local display
+[X-:0-Greeter]
+</kdmrc>
+
+# The contents of this section are copied into kdmrc-ref.docbook.
+# The macro %REF% is replaced with the accumulated Description:s from the key
+# definitions below.
+<docu>
+<chapter id="kdm-files">
+<title>The Files &kdm; Uses for Configuration</title>
+
+<para>This chapter documents the files that control &kdm;'s behavior.
+Some of this can be also controlled from the &kcontrol; module, but
+not all.</para>
+
+<sect1 id="kdmrc">
+<title>&kdmrc; - The &kdm; master configuration file</title>
+
+<para>The basic format of the file is <quote>INI-like</quote>.
+Options are key/value pairs, placed in sections.
+Everything in the file is case sensitive.
+Syntactic errors and unrecognized key/section identifiers cause &kdm; to
+issue non-fatal error messages.</para>
+
+<para>Lines beginning with <literal>#</literal> are comments; empty lines
+are ignored as well.</para>
+
+<para>Sections are denoted by
+<literal>[</literal><replaceable>Name of Section</replaceable><literal>]</literal>.
+</para>
+
+<para>You can configure every X-display individually.</para>
+<para>Every display has a display name, which consists of a host name
+(which is empty for local displays specified in <option>StaticServers</option>
+or <option>ReserveServers</option>), a colon, and a display number.
+Additionally, a display belongs to a
+display class (which can be ignored in most cases).</para>
+
+<para>Sections with display-specific settings have the formal syntax
+<literal>[X-</literal>&nbsp;<replaceable>host</replaceable>&nbsp;[&nbsp;<literal>:</literal>&nbsp;<replaceable>number</replaceable>&nbsp;[&nbsp;<literal>_</literal>&nbsp;<replaceable>class</replaceable>&nbsp;]&nbsp;]&nbsp;<literal>-</literal>&nbsp;<replaceable>sub-section</replaceable>&nbsp;<literal>]</literal>
+</para>
+<para>All sections with the same <replaceable>sub-section</replaceable>
+make up a section class.</para>
+
+<para>You can use the wildcard <literal>*</literal> (match any) for
+<replaceable>host</replaceable>, <replaceable>number</replaceable>,
+and <replaceable>class</replaceable>. You may omit trailing components;
+they are assumed to be <literal>*</literal> then. The host part may be a
+domain specification like <replaceable>.inf.tu-dresden.de</replaceable>
+or the wildcard <literal>+</literal> (match non-empty).</para>
+
+<para>From which section a setting is actually taken is determined by
+these rules:</para>
+
+<itemizedlist>
+<listitem>
+<para>An exact match takes precedence over a partial match (for the
+host part), which in turn takes precedence over a wildcard
+(<literal>+</literal> taking precendence over <literal>*</literal>).</para>
+</listitem>
+
+<listitem>
+<para>Precedence decreases from left to right for equally exact matches.</para>
+</listitem>
+
+<listitem>
+
+<para>
+Example: display name <quote>myhost.foo:0</quote>, class <quote>dpy</quote>
+</para>
+<itemizedlist>
+<listitem>
+<para>[X-myhost.foo:0_dpy] precedes</para>
+</listitem>
+<listitem>
+<para>[X-myhost.foo:0_*] (same as [X-myhost.foo:0]) precedes</para>
+</listitem>
+<listitem>
+<para>[X-myhost.foo:*_dpy] precedes</para>
+</listitem>
+<listitem>
+<para>[X-myhost.foo:*_*] (same as [X-myhost.foo]) precedes</para>
+</listitem>
+<listitem>
+<para>[X-.foo:*_*] (same as [X-.foo]) precedes</para>
+</listitem>
+<listitem>
+<para>[X-+:0_dpy] precedes</para>
+</listitem>
+<listitem>
+<para>[X-*:0_dpy] precedes</para>
+</listitem>
+<listitem>
+<para>[X-*:0_*] (same as [X-*:0]) precedes</para>
+</listitem>
+<listitem>
+<para>[X-*:*_*] (same as [X-*]).</para>
+</listitem>
+<listitem>
+<para>These sections do <emphasis>not</emphasis> match this display:</para>
+<para>[X-hishost], [X-myhost.foo:0_dec], [X-*:1], [X-:*]</para>
+</listitem>
+</itemizedlist>
+
+</listitem>
+
+</itemizedlist>
+
+<para>Common sections are [X-*] (all displays), [X-:*] (all local displays)
+and [X-:0] (the first local display).</para>
+
+<para>The format for all keys is
+<userinput><option><replaceable>key</replaceable></option>&nbsp;<literal>=</literal>&nbsp;<parameter>value</parameter></userinput>.
+Keys are only valid in the section class they are defined for.
+Some keys do not apply to particular displays, in which case they are ignored.
+</para>
+
+<para>If a setting is not found in any matching section, the default
+is used.</para>
+
+<para>Special characters need to be backslash-escaped (leading and trailing
+spaces (<literal>\s</literal>), tab (<literal>\t</literal>), linefeed
+(<literal>\n</literal>), carriage return (<literal>\r</literal>) and the
+backslash itself (<literal>\\</literal>)).</para>
+<para>In lists, fields are separated with commas without whitespace in between.
+</para>
+<para>Some command strings are subject to simplified sh-style word splitting:
+single quotes (<literal>'</literal>) and double quotes (<literal>"</literal>)
+have the usual meaning; the backslash quotes everything (not only special
+characters). Note that the backslashes need to be doubled because of the
+two levels of quoting.</para>
+
+<note><para>A pristine &kdmrc; is very thoroughly commented.
+All comments will be lost if you change this file with the
+kcontrol frontend.</para></note>
+
+%REF%
+
+</sect1>
+
+<sect1 id="kdmrc-xservers">
+<title>Specifying permanent &X-Server;s</title>
+
+<para>Each entry in the <option>StaticServers</option> list indicates a
+display which should constantly be
+managed and which is not using &XDMCP;. This method is typically used only for
+local &X-Server;s that are started by &kdm;, but &kdm; can manage externally
+started (<quote>foreign</quote>) &X-Server;s as well, may they run on the
+local machine or rather remotely.</para>
+
+<para>The formal syntax of a specification is
+<screen>
+<userinput><replaceable>display&nbsp;name</replaceable>&nbsp;[<literal>_</literal><replaceable>display&nbsp;class</replaceable>]</userinput>
+</screen>
+for all &X-Server;s. <quote>Foreign</quote> displays differ in having
+a host name in the display name, may it be <literal>localhost</literal>.</para>
+
+<para>The <replaceable>display name</replaceable> must be something that can
+be passed in the <option>-display</option> option to an X program. This string
+is used to generate the display-specific section names, so be careful to match
+the names.
+The display name of &XDMCP; displays is derived from the display's address by
+reverse host name resolution. For configuration purposes, the
+<literal>localhost</literal> prefix from locally running &XDMCP; displays is
+<emphasis>not</emphasis> stripped to make them distinguishable from local
+&X-Server;s started by &kdm;.</para>
+
+<para>The <replaceable>display class</replaceable> portion is also used in the
+display-specific sections. This is useful if you have a large collection of
+similar displays (such as a corral of X terminals) and would like to set
+options for groups of them.
+When using &XDMCP;, the display is required to specify the display class,
+so the manual for your particular X terminal should document the display
+class string for your device. If it does not, you can run &kdm; in debug
+mode and <command>grep</command> the log for <quote>class</quote>.</para>
+
+<para>The displays specified in <option>ReserveServers</option> will not be
+started when &kdm; starts up, but when it is explicitly requested via
+the command socket (or <acronym>FiFo</acronym>).
+If reserve displays are specified, the &kde; menu will have a
+<guilabel>Start New Session</guilabel> item near the bottom; use that to
+activate a reserve display with a new login session. The monitor will switch
+to the new display, and you will have a minute to login. If there are no more
+reserve displays available, the menu item will be disabled.</para>
+
+<para>When &kdm; starts a session, it sets up authorization data for the
+&X-Server;. For local servers, &kdm; passes
+<command><option>-auth</option>&nbsp;<filename><replaceable>filename</replaceable></filename></command>
+on the &X-Server;'s command line to point it at its authorization data.
+For &XDMCP; displays, &kdm; passes the authorization data to the &X-Server;
+via the <quote>Accept</quote> &XDMCP; message.</para>
+
+</sect1>
+
+<sect1 id="kdmrc-xaccess">
+<title>&XDMCP; access control</title>
+
+<para>The file specified by the <option>AccessFile</option> option provides
+information which &kdm; uses to control access from displays requesting service
+via &XDMCP;.
+The file contains four types of entries: entries which control the response
+to <quote>Direct</quote> and <quote>Broadcast</quote> queries, entries which
+control the response to <quote>Indirect</quote> queries, macro definitions for
+<quote>Indirect</quote> entries, and entries which control on which network
+interfaces &kdm; listens for &XDMCP; queries.
+Blank lines are ignored, <literal>#</literal> is treated as a comment
+delimiter causing the rest of that line to be ignored, and <literal>\</literal>
+causes an immediately following newline to be ignored, allowing indirect host
+lists to span multiple lines.
+</para>
+
+<para>The format of the <quote>Direct</quote> entries is simple, either a
+host name or a pattern, which is compared against the host name of the display
+device.
+Patterns are distinguished from host names by the inclusion of one or more
+meta characters; <literal>*</literal> matches any sequence of 0 or more
+characters, and <literal>?</literal> matches any single character.
+If the entry is a host name, all comparisons are done using network addresses,
+so any name which converts to the correct network address may be used. Note
+that only the first network address returned for a host name is used.
+For patterns, only canonical host names are used in the comparison, so ensure
+that you do not attempt to match aliases.
+Host names from &XDMCP; queries always contain the local domain name
+even if the reverse lookup returns a short name, so you can use
+patterns for the local domain.
+Preceding the entry with a <literal>!</literal> character causes hosts which
+match that entry to be excluded.
+To only respond to <quote>Direct</quote> queries for a host or pattern,
+it can be followed by the optional <literal>NOBROADCAST</literal> keyword.
+This can be used to prevent a &kdm; server from appearing on menus based on
+<quote>Broadcast</quote> queries.</para>
+
+<para>An <quote>Indirect</quote> entry also contains a host name or pattern,
+but follows it with a list of host names or macros to which the queries
+should be forwarded. <quote>Indirect</quote> entries can be excluding as well,
+in which case a (valid) dummy host name must be supplied to make the entry
+distinguishable from a <quote>Direct</quote> entry.
+If compiled with IPv6 support, multicast address groups may also be included
+in the list of addresses the queries are forwarded to.
+<!-- Not actually implemented!
+Multicast addresses may be followed by an optional <literal>/</literal>
+character and hop count. If no hop count is specified, the multicast hop count
+defaults to 1, keeping the packet on the local network. For IPv4 multicasting,
+the hop count is used as the TTL.
+-->
+If the indirect host list contains the keyword <literal>CHOOSER</literal>,
+<quote>Indirect</quote> queries are not forwarded, but instead a host chooser
+dialog is displayed by &kdm;. The chooser will send a <quote>Direct</quote>
+query to each of the remaining host names in the list and offer a menu of
+all the hosts that respond. The host list may contain the keyword
+<literal>BROADCAST</literal>, to make the chooser send a
+<quote>Broadcast</quote> query as well; note that on some operating systems,
+UDP packets cannot be broadcast, so this feature will not work.
+</para>
+
+<para>When checking access for a particular display host, each entry is scanned
+in turn and the first matching entry determines the response.
+<quote>Direct</quote> and <quote>Broadcast</quote> entries are ignored when
+scanning for an <quote>Indirect</quote> entry and vice-versa.</para>
+
+<para>A macro definition contains a macro name and a list of host names and
+other macros that the macro expands to. To distinguish macros from hostnames,
+macro names start with a <literal>%</literal> character.</para>
+
+<para>The last entry type is the <literal>LISTEN</literal> directive.
+The formal syntax is
+<screen>
+<userinput>&nbsp;<literal>LISTEN</literal>&nbsp;[<replaceable>interface</replaceable>&nbsp;[<replaceable>multicast&nbsp;list</replaceable>]]</userinput>
+</screen>
+If one or more <literal>LISTEN</literal> lines are specified, &kdm; listens
+for &XDMCP; requests only on the specified interfaces.
+<replaceable>interface</replaceable> may be a hostname or IP address
+representing a network interface on this machine, or the wildcard
+<literal>*</literal> to represent all available network interfaces.
+If multicast group addresses are listed on a <literal>LISTEN</literal> line,
+&kdm; joins the multicast groups on the given interface. For IPv6 multicasts,
+the IANA has assigned ff0<replaceable>X</replaceable>:0:0:0:0:0:0:12b as the
+permanently assigned range of multicast addresses for &XDMCP;. The
+<replaceable>X</replaceable> in the prefix may be replaced by any valid scope
+identifier, such as 1 for Node-Local, 2 for Link-Local, 5 for Site-Local, and
+so on (see IETF RFC 2373 or its replacement for further details and scope
+definitions). &kdm; defaults to listening on the Link-Local scope address
+ff02:0:0:0:0:0:0:12b to most closely match the IPv4 subnet broadcast behavior.
+If no <literal>LISTEN</literal> lines are given, &kdm; listens on all
+interfaces and joins the default &XDMCP; IPv6 multicast group (when
+compiled with IPv6 support).
+To disable listening for &XDMCP; requests altogether, a
+<literal>LISTEN</literal> line with no addresses may be specified, but using
+the <literal>[Xdmcp]</literal> <option>Enable</option> option is preferred.
+</para>
+
+</sect1>
+
+<sect1 id="kdm-scripts">
+<title>Supplementary programs</title>
+
+<para>
+The following programs are run by &kdm; at various stages of a session.
+They typically are shell scripts.
+</para>
+
+<para>
+The Setup, Startup and Reset programs are run as
+<systemitem class="username">root</systemitem>, so they should be careful
+about security.
+Their first argument is <literal>auto</literal> if the session results
+from an automatic login; otherwise, no arguments are passed to them.
+</para>
+
+<sect2 id="kdmrc-xsetup">
+<title>Setup program</title>
+
+<para>
+The <filename>Xsetup</filename> program is run after the &X-Server; is
+started or reset, but before the greeter is offered.
+This is the place to change the root background (if
+<option>UseBackground</option> is disabled) or bring up other windows that
+should appear on the screen along with the greeter.
+</para>
+
+<para>
+In addition to any specified by <option>ExportList</option>,
+the following environment variables are passed:</para>
+<variablelist>
+ <varlistentry>
+ <term>DISPLAY</term>
+ <listitem><para>the associated display name</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>PATH</term>
+ <listitem><para>the value of <option>SystemPath</option></para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>SHELL</term>
+ <listitem><para>the value of <option>SystemShell</option></para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>XAUTHORITY</term>
+ <listitem><para>may be set to an authority file</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>DM_CONTROL</term>
+ <listitem><para>the value of <option>FifoDir</option></para></listitem>
+ </varlistentry>
+</variablelist>
+
+<para> Note that since &kdm; grabs the keyboard, any other windows will not be
+able to receive keyboard input. They will be able to interact with the mouse,
+however; beware of potential security holes here. If <option>GrabServer</option>
+is set, <filename>Xsetup</filename> will not be able to connect to the display
+at all. Resources for this program can be put into the file named by
+<option>Resources</option>.
+</para>
+
+</sect2>
+
+<sect2 id="kdmrc-xstartup">
+<title>Startup program</title>
+
+<para>The <filename>Xstartup</filename> program is run as
+<systemitem class="username">root</systemitem> when the user logs in.
+This is the place to put commands which add entries to
+<filename>utmp</filename> (the <command>sessreg</command> program
+may be useful here), mount users' home directories from file servers,
+or abort the session if some requirements are not met (but note that on
+modern systems, many of these tasks are already taken care of by
+<acronym>PAM</acronym> modules).</para>
+
+<para>In addition to any specified by <option>ExportList</option>,
+the following environment variables are passed:</para>
+<variablelist>
+ <varlistentry>
+ <term>DISPLAY</term>
+ <listitem><para>the associated display name</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>HOME</term>
+ <listitem><para>the initial working directory of the user</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>LOGNAME</term>
+ <listitem><para>the username</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>USER</term>
+ <listitem><para>the username</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>PATH</term>
+ <listitem><para>the value of <option>SystemPath</option></para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>SHELL</term>
+ <listitem><para>the value of <option>SystemShell</option></para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>XAUTHORITY</term>
+ <listitem><para>may be set to an authority file</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>DM_CONTROL</term>
+ <listitem><para>the value of <option>FifoDir</option></para></listitem>
+ </varlistentry>
+</variablelist>
+
+<para>&kdm; waits until this program exits before starting the user session.
+If the exit value of this program is non-zero, &kdm; discontinues the session
+and starts another authentication cycle.</para>
+
+</sect2>
+
+<sect2 id="kdmrc-xsession">
+<title>Session program</title>
+
+<para>The <filename>Xsession</filename> program is the command which is run
+as the user's session. It is run with the permissions of the authorized user.
+One of the keywords <literal>failsafe</literal>, <literal>default</literal>
+or <literal>custom</literal>, or a string to <command>eval</command> by a
+Bourne-compatible shell is passed as the first argument.</para>
+
+<para>In addition to any specified by <option>ExportList</option>,
+the following environment variables are passed:</para>
+<variablelist>
+ <varlistentry>
+ <term>DISPLAY</term>
+ <listitem><para>the associated display name</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>HOME</term>
+ <listitem><para>the initial working directory of the user</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>LOGNAME</term>
+ <listitem><para>the username</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>USER</term>
+ <listitem><para>the username</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>PATH</term>
+ <listitem><para>the value of <option>UserPath</option>
+ (or <option>SystemPath</option> for
+ <systemitem class="username">root</systemitem> user sessions)</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>SHELL</term>
+ <listitem><para>the user's default shell</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>XAUTHORITY</term>
+ <listitem><para>may be set to a non-standard authority file</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>KRBTKFILE</term>
+ <listitem><para>may be set to a Kerberos4 credentials cache name</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>KRB5CCNAME</term>
+ <listitem><para>may be set to a Kerberos5 credentials cache name</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>DM_CONTROL</term>
+ <listitem><para>the value of <option>FifoDir</option></para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>XDM_MANAGED</term>
+ <listitem><para>will contain a comma-separated list of parameters the
+ session might find interesting, like the location of the command
+ <acronym>FiFo</acronym> and its capabilities, and which conversation
+ plugin was used for the login</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>DESKTOP_SESSION</term>
+ <listitem><para>the name of the session the user has chosen to run</para>
+ </listitem>
+ </varlistentry>
+</variablelist>
+
+</sect2>
+
+<sect2 id="kdmrc-xreset">
+<title>Reset program</title>
+
+<para>Symmetrical with <filename>Xstartup</filename>, the
+<filename>Xreset</filename> program is run after the user session has
+terminated. Run as <systemitem class="username">root</systemitem>, it should
+contain commands that undo the effects of commands in
+<filename>Xstartup</filename>, removing entries from <filename>utmp</filename>
+or unmounting directories from file servers.</para>
+
+<para>The environment variables that were passed to
+<filename>Xstartup</filename> are also passed to <filename>Xreset</filename>.
+</para>
+
+</sect2>
+
+</sect1>
+
+</chapter>
+</docu>
+
+
+# The rest of this file are section and key definitions for the options.
+# The order of the keywords is fixed and everything is case sensitive.
+# A keyword may expect supplementary data in the form of space-indented
+# lines following it. Definitions are delimited by empty lines.
+#
+# Section definition:
+# Section: <name>
+# Section name. Section classes start with a dash.
+# If: <expression>
+# C preprocessor conditional for supporting this section.
+# If it evaluates to false, all keys in this section are disabled as well.
+# Description:
+# A docbook description of this section is expected in the next lines.
+# The contents are automatically enclosed in <para></para>.
+#
+# Option key definition:
+# Key: <name>
+# Option name.
+# If: <expression>
+# C preprocessor conditional for supporting this option.
+# Type: (int|bool|enum|group|string|path|list)
+# The option's data type.
+# If the type is enum, the element definitions follow in the next lines:
+# <term>[/<c #define>]: <docbook style description>
+# Default: <default>
+# Default value. string, path and list are copied verbatim and therefore
+# must be already quoted appropriately. The other types are auto-quoted.
+# If the default value is prefixed with a "*", a c #define def_<Key> is
+# created.
+# The default is automatically appended to the kdmrc comment and the
+# documentation entry.
+# CDefault: <verbose default>
+# Append this instead of the real default to the two docs. The quoting
+# rules are the same as for Default.
+# DDefault: -
+# If specified, the default value will not be appended to the documentation
+# entry. The Description should mention the default then. Use this when
+# the default is system-dependent.
+# PostProc: <function>
+# A function to postprocess the read config value before using it.
+# User: (dummy|(core|greeter|greeter-c|dep|config)[(<variable>)][:font])
+# These entries specify which parts of kdm need the option in question:
+# dummy: no user; entry is there only for syntactical correctness.
+# dep: this option is an internal dependency for another option.
+# config: this option configures the config reader itself.
+# core: the kdm backend needs this option.
+# greeter-c: the kdm frontend needs this option as a C data type.
+# greeter: the kdm frontend needs this option as a C++/Qt data type.
+# If a :font tag is appended, a string entry is converted to a QFont.
+# If no variable name is specified, it will be derived from the Key by
+# un-capitalizing it.
+# Instance: (-|[#][<display spec>/](!|<value>))
+# These entries specify option instances for the default/example kdmrc.
+# A "-" entry is a dummy for syntactical correctness.
+# A prefixing hash mark will be copied to kdmrc.
+# For options in a section class a display must be specified.
+# For bool options "!" can be used as the value to specify the negation
+# of the default.
+# Update: <function>[/<number>]
+# Call this function on each occurence of this option in genkdmconf.
+# Options with higher numbers (default is 0) will be processed later.
+# Merge: (xdm[:<resource>][(<function>)]|kdm:[<section>/][<key>][<function>])
+# Specify config options to merge from xdm and older kdm versions.
+# Kdm options from the current version are automatically merged.
+# When merging an xdm resource and no resource name is specified, it is
+# derived from the Key by un-capitalizing it.
+# When merging a kdm option, at least one of <section> and <key> must
+# be given; an unspecified entity defaults to the current Section/Key.
+# <section> may be a dash-prefixed section class.
+# A function to postprocess the read value can be specified.
+# Comment: [&|-]
+# A kdmrc comment for this option is expected in the next lines.
+# If "-" is given to Comment, no comment is generated at all.
+# If "&" is given, the comment is derived from the Description below by
+# applying some simple docbook interpretation to it. Note that the
+# Description must be preformatted in this case. Use
+# sed -ne 's/^\(.\{79,\}\)$/\1/p' < kdmrc
+# after running "make install" to see whether all lines still fit.
+# If Type is enum, a list of the previously defined element/description
+# pairs is appended; the descriptions undergo docbook interpretation.
+# Finally, a sentence with the Default (or CDefault, if given) is appended.
+# Description: [!|-]
+# A docbook description of this option is expected in the next lines.
+# The contents are automatically enclosed in <para></para>.
+# If "-" is given to Description, no comment is generated at all.
+# If "!" is given, enums are not treated specially; otherwise, the macro
+# %ENUM% is replaced with a list of the defined element/description pairs,
+# or - if the macro is not present - the list is appended to the
+# description.
+# Finally, a sentence with the Default (or CDefault, if given) is appended,
+# unless "DDefault: -" was specified.
+# Each option entry generates an anchor named option-<lowercase(Key)>;
+# it can be referenced in the main documentation.
+# Do not forget to run "make ref" in kdebase/doc/kdm after changing
+# Descriptions.
+
+Section: General
+Description:
+ This section contains global options that do not fit into any specific section.
+
+Key: ConfigVersion
+Type: string
+Default: ""
+CDefault: -
+User: dummy
+# will be overwritten
+Instance:
+Comment:
+ This option exists solely for the purpose of a clean automatic upgrade.
+ Do not even think about changing it!
+Description:
+ This option exists solely for the purpose of clean automatic upgrades.
+ <emphasis>Do not</emphasis> change it, you may interfere with future
+ upgrades and this could result in &kdm; failing to run.
+
+Key: PAMService
+If: defined(USE_PAM)
+Type: string
+Default: KDM_PAM_SERVICE
+User: core
+Instance: -
+Comment: -
+Description: -
+
+<legacy>
+Proc: absorb_xservers
+# note: this can miss Xservers from kdm for kde 2.2 because of stupid default.
+Source: kdm:General/Xservers
+Source: xdm:servers
+</legacy>
+
+Key: StaticServers
+Type: list
+Default: ":0"
+User: core
+Instance: ":0"
+Comment:
+ List of permanent displays. Displays with a hostname are foreign. A display
+ class may be specified separated by an underscore.
+Description:
+ List of displays (&X-Server;s) permanently managed by &kdm;. Displays with a
+ hostname are foreign displays which are expected to be already running,
+ the others are local displays for which &kdm; starts an own &X-Server;;
+ see <option>ServerCmd</option>. Each display may belong to a display class;
+ append it to the display name separated by an underscore.
+ See <xref linkend="kdmrc-xservers"/> for the details.
+
+Key: ReserveServers
+Type: list
+Default: ""
+User: core
+Instance: ":1,:2,:3"
+Comment: &
+Description:
+ List of on-demand displays. See <option>StaticServers</option> for syntax.
+
+Key: ServerVTs
+If: defined(HAVE_VTS)
+Type: list
+Default: ""
+User: core
+Instance: #"7,8,-9,-10"
+Update: upd_servervts
+Comment:
+ VTs to allocate to &X-Server;s. A negative number means that the VT will be
+ used only if it is free. If all VTs in this list are used up, the next free
+ one greater than the last one in this list will be allocated.
+Description:
+ List of Virtual Terminals to allocate to &X-Server;s. For negative numbers the
+ absolute value is used, and the <acronym>VT</acronym> will be allocated only
+ if the kernel says it is free. If &kdm; exhausts this list, it will allocate
+ free <acronym>VT</acronym>s greater than the absolute value of the last entry
+ in this list.
+ Currently Linux only.
+
+Key: ConsoleTTYs
+If: defined(HAVE_VTS)
+Type: list
+Default: ""
+User: core
+Instance: #"tty1,tty2,tty3,tty4,tty5,tty6"
+Update: upd_consolettys
+Comment:
+ TTYs (without /dev/) to monitor for activity while in console mode.
+Description:
+ This option is for operating systems (<acronym>OS</acronym>s) with support
+ for virtual terminals (<acronym>VT</acronym>s), by both &kdm; and the
+ <acronym>OS</acronym>s itself.
+ Currently this applies only to Linux.
+ </para><para>
+ When &kdm; switches to console mode, it starts monitoring all
+ <acronym>TTY</acronym> lines listed here (without the leading
+ <literal>/dev/</literal>).
+ If none of them is active for some time, &kdm; switches back to the X login.
+
+Key: PidFile
+Type: string
+Default: ""
+User: core
+Instance: "/var/run/kdm.pid"
+Merge: xdm
+Comment:
+ Where &kdm; should store its PID (do not store if empty).
+Description:
+ The filename specified will be created to contain an ASCII representation
+ of the process ID of the main &kdm; process; the PID will not be stored
+ if the filename is empty.
+
+Key: LockPidFile
+Type: bool
+Default: true
+User: core
+Instance: #!
+Merge: xdm
+Comment:
+ Whether &kdm; should lock the PID file to prevent having multiple &kdm;
+ instances running at once. Do not change unless you are brave.
+Description:
+ This option controls whether &kdm; uses file locking to keep multiple
+ display managers from running onto each other.
+
+Key: AuthDir
+Type: path
+# differs from XDM
+Default: "/var/run/xauth"
+User: core
+Instance: #"/tmp"
+Merge: xdm(P_authDir)
+Comment:
+ Where to store authorization files.
+Description:
+ This names a directory under which &kdm; stores &X-Server; authorization
+ files while initializing the session. &kdm; expects the system to clean up
+ this directory from stale files on reboot.
+ </para><para>
+ The authorization file to be used for a particular display can be
+ specified with the <option>AuthFile</option> option in [X-*-Core].
+
+Key: AutoRescan
+Type: bool
+Default: true
+User: core
+Instance: #!
+Merge: xdm
+Comment:
+ Whether &kdm; should automatically re-read configuration files, if it
+ finds them having changed.
+Description:
+ This boolean controls whether &kdm; automatically re-reads its
+ configuration files if it finds them to have changed.
+
+Key: ExportList
+Type: list
+Default: ""
+User: core
+Instance: #"LD_LIBRARY_PATH,ANOTHER_IMPORTANT_VAR"
+Merge: xdm(P_List)
+Comment: &
+Description:
+ Additional environment variables &kdm; should pass on to all programs it runs.
+ <envar>LD_LIBRARY_PATH</envar> and <envar>XCURSOR_THEME</envar> are good candidates;
+ otherwise, it should not be necessary very often.
+
+Key: RandomFile
+If: !defined(ARC4_RANDOM) && !defined(DEV_RANDOM)
+Type: string
+Default: "/dev/mem"
+User: core
+Instance: #""
+Merge: xdm
+Comment:
+ A file &kdm; should read entropy from.
+Description:
+ If the system has no native entropy source like /dev/urandom (see
+ <option>RandomDevice</option>) and no entropy daemon like EGD (see
+ <option>PrngdSocket</option> and <option>PrngdPort</option>) is running,
+ &kdm; will fall back to its own pseudo-random number generator
+ that will, among other things, successively checksum parts of this file
+ (which, obviously, should change frequently).
+ </para><para>
+ This option does not exist on Linux and various BSDs.
+
+Key: PrngdSocket
+If: !defined(ARC4_RANDOM) && !defined(DEV_RANDOM)
+Type: string
+# differs from xdm!
+Default: ""
+User: core
+Instance: #"/tmp/entropy"
+Merge: xdm
+Comment:
+ A UNIX domain socket &kdm; should read entropy from.
+Description:
+ If the system has no native entropy source like /dev/urandom (see
+ <option>RandomDevice</option>), read random data from a Pseudo-Random
+ Number Generator Daemon,
+ like EGD (http://egd.sourceforge.net) via this UNIX domain socket.
+ </para><para>
+ This option does not exist on Linux and various BSDs.
+
+Key: PrngdPort
+If: !defined(ARC4_RANDOM) && !defined(DEV_RANDOM)
+Type: int
+Default: 0
+User: core
+Instance: #4840
+Merge: xdm
+Comment:
+ A TCP socket on localhost &kdm; should read entropy from.
+Description:
+ Same as <option>PrngdSocket</option>, only use a TCP socket on localhost.
+
+Key: RandomDevice
+If: !defined(ARC4_RANDOM)
+Type: string
+Default: ""
+User: core
+Instance: #"/dev/altrandom"
+Merge: xdm
+Comment:
+ A character device &kdm; should read entropy from.
+ Empty means use the system's preferred entropy device.
+Description:
+ The path to a character device which &kdm; should read random data from.
+ Empty means to use the system's preferred entropy device if there is one.
+ </para><para>
+ This option does not exist on OpenBSD, as it uses the arc4_random
+ function instead.
+
+Key: FifoDir
+Type: path
+Default: *"/var/run/xdmctl"
+User: core
+Instance: #"/tmp"
+Update: upd_fifodir
+Comment:
+ Where the command FiFos should be created; make it empty to disable
+ them.
+Description:
+ The directory in which the command <acronym>FiFo</acronym>s should
+ be created; make it empty to disable them.
+# See <xref linkend="kdm-fifos"/> for the details.
+
+Key: FifoGroup
+Type: group
+Default: 0
+User: core
+Instance: #xdmctl
+Comment: &
+Description:
+ The group to which the global command <acronym>FiFo</acronym> should belong;
+ can be either a name or a numerical ID.
+
+Key: DataDir
+Type: path
+Default: *"/var/lib/kdm"
+User: greeter
+Instance: #""
+Update: upd_datadir
+Comment:
+ The directory in which &kdm; should store persistent working data.
+Description:
+ The directory in which &kdm; should store persistent working data; such data
+ is, for example, the previous user that logged in on a particular display.
+
+Key: DmrcDir
+Type: path
+Default: ""
+User: core
+Instance: #"/nfs-shared/var/dmrcs"
+Comment: &
+Description:
+ The directory in which &kdm; should store users' <filename>.dmrc</filename> files. This is only
+ needed if the home directories are not readable before actually logging in
+ (like with AFS).
+
+
+Section: Xdmcp
+If: defined(XDMCP)
+Description:
+ This section contains options that control &kdm;'s handling of
+ &XDMCP; requests.
+# See <xref linkend="xdmcp-with-kdm"/> to find out what &XDMCP; is.
+
+Key: Enable
+Type: bool
+Default: true
+User: dep(xdmcpEnable)
+Instance: false
+Comment: &
+Description:
+ Whether &kdm; should listen to incoming &XDMCP; requests.
+
+Key: Port
+Type: int
+Default: 177
+PostProc: PrequestPort
+User: core(request_port)
+Instance: #
+Merge: xdm:requestPort(P_requestPort)
+Comment:
+ The UDP port on which &kdm; should listen for &XDMCP; requests. Do not change.
+Description:
+ This indicates the UDP port number which &kdm; uses to listen for incoming
+ &XDMCP; requests. Unless you need to debug the system, leave this with its
+ default value.
+
+Key: KeyFile
+Type: string
+Default: ""
+User: core
+Instance: #KDMCONF "/kdmkeys"
+Update: cp_keyfile
+Merge: xdm
+Comment:
+ File with the private keys of X-terminals. Required for XDM authentication.
+Description:
+ XDM-AUTHENTICATION-1 style &XDMCP; authentication requires a private
+ key to be shared between &kdm; and the terminal. This option specifies
+ the file containing those values. Each entry in the file consists of a
+ display name and the shared key.
+
+Key: Xaccess
+Type: string
+# differs from xdm
+Default: *KDMCONF "/Xaccess"
+User: config(Xaccess)
+Instance: #""
+Update: mk_xaccess
+Merge: xdm:accessFile
+Comment:
+ &XDMCP; access control file in the usual XDM-Xaccess format.
+Description:
+ To prevent unauthorized &XDMCP; service and to allow forwarding of &XDMCP;
+ IndirectQuery requests, this file contains a database of hostnames which
+ are either allowed direct access to this machine, or have a list of hosts
+ to which queries should be forwarded to. The format of this file is
+ described in <xref linkend="kdmrc-xaccess"/>.
+
+Key: ChoiceTimeout
+Type: int
+Default: 15
+User: core
+Instance: #10
+Merge: xdm
+Comment:
+ Number of seconds to wait for display to respond after the user has
+ selected a host from the chooser.
+Description:
+ Number of seconds to wait for the display to respond after the user has
+ selected a host from the chooser. If the display sends an &XDMCP;
+ IndirectQuery within this time, the request is forwarded to the chosen
+ host; otherwise, it is assumed to be from a new session and the chooser
+ is offered again.
+
+Key: RemoveDomainname
+Type: bool
+Default: true
+User: core
+Instance: #!
+Merge: xdm
+Comment:
+ Strip domain name from remote display names if it is equal to the local
+ domain.
+Description:
+ When computing the display name for &XDMCP; clients, the name resolver will
+ typically create a fully qualified host name for the terminal. As this is
+ sometimes confusing, &kdm; will remove the domain name portion of the host
+ name if it is the same as the domain name of the local host when this option
+ is enabled.
+
+Key: SourceAddress
+Type: bool
+Default: false
+User: core
+Instance: #!
+Merge: xdm
+Comment:
+ Use the numeric IP address of the incoming connection on multihomed hosts
+ instead of the host name.
+Description:
+ Use the numeric IP address of the incoming connection on multihomed hosts
+ instead of the host name. This is to avoid trying to connect on the wrong
+ interface which might be down at this time.
+
+Key: Willing
+Type: string
+Default: ""
+User: core
+# will be overwritten
+Instance: #
+Update: mk_willing
+Merge: xdm
+Merge: kdm:Xwilling
+Comment:
+ The program which is invoked to dynamically generate replies to &XDMCP;
+ DirectQuery or BroadcastQuery requests.
+ If empty, no program is invoked and "Willing to manage" is sent.
+Description:
+ This specifies a program which is run (as
+ <systemitem class="username">root</systemitem>) when an &XDMCP;
+ DirectQuery or BroadcastQuery is received and this host is configured
+ to offer &XDMCP; display management. The output of this program may be
+ displayed in a chooser window. If no program is specified, the string
+ <quote>Willing to manage</quote> is sent.
+
+
+Section: Shutdown
+Description:
+ This section contains global options concerning system shutdown.
+
+Key: HaltCmd
+Type: string
+Default: HALT_CMD
+DDefault: -
+User: core(cmdHalt)
+Instance: #""
+Comment:
+ The command (subject to word splitting) to run to halt the system.
+Description:
+ The command (subject to word splitting) to run to halt/poweroff the system.
+ </para><para>
+ The default is something reasonable for the system on which &kdm; was built, like
+ <command>/sbin/shutdown&nbsp;<option>-h</option>&nbsp;<parameter>now</parameter></command>.
+
+Key: RebootCmd
+Type: string
+Default: REBOOT_CMD
+DDefault: -
+User: core(cmdReboot)
+Instance: #""
+Comment:
+ The command (subject to word splitting) to run to reboot the system.
+Description:
+ The command (subject to word splitting) to run to reboot the system.
+ </para><para>
+ The default is something reasonable for the system &kdm; on which was built, like
+ <command>/sbin/shutdown&nbsp;<option>-r</option>&nbsp;<parameter>now</parameter></command>.
+
+Key: AllowFifo
+Type: bool
+Default: false
+User: core(fifoAllowShutdown)
+Instance: #!
+Comment: &
+Description:
+ Whether it is allowed to shut down the system via the global command <acronym>FiFo</acronym>.
+
+Key: AllowFifoNow
+Type: bool
+Default: true
+User: core(fifoAllowNuke)
+Instance: #!
+Comment:
+ Whether it is allowed to abort active sessions when shutting down the
+ system via the global command FiFo.
+Description:
+ Whether it is allowed to abort active sessions when shutting down the
+ system via the global command <acronym>FiFo</acronym>.
+ </para><para>
+ This will have no effect unless <option>AllowFifo</option> is enabled.
+
+Key: BootManager
+Type: enum
+ None/BO_NONE: no boot manager
+ Grub/BO_GRUB: Grub boot manager
+ Lilo/BO_LILO: Lilo boot manager (Linux on i386 &amp; x86-64 only)
+Default: None
+User: core
+User: greeter
+Instance: #Grub
+Merge: kdm:UseLilo(P_UseLilo)
+Comment: &
+Description:
+ The boot manager &kdm; should use for offering boot options in the
+ shutdown dialog.
+
+
+Section: -Core
+Description:
+ This section class contains options concerning the configuration
+ of the &kdm; backend (core).
+
+Key: OpenDelay
+Type: int
+Default: 15
+User: core
+Instance: #*/
+Merge: xdm(P_openDelay)
+Comment:
+ How long to wait before retrying to connect a display.
+Description:
+ See <option>OpenRepeat</option>.
+
+Key: OpenTimeout
+Type: int
+Default: 120
+User: core
+Instance: #*/
+Merge: xdm
+Comment:
+ How long to wait before timing out a display connection attempt.
+Description:
+ See <option>OpenRepeat</option>.
+
+Key: OpenRepeat
+Type: int
+Default: 5
+User: core
+Instance: #*/
+Merge: xdm
+Comment:
+ How many connection attempts to make during a start attempt. Note that
+ a timeout aborts the entire start attempt.
+Description:
+ These options control the behavior of &kdm; when attempting to open a
+ connection to an &X-Server;. <option>OpenDelay</option> is the length
+ of the pause (in seconds) between successive attempts,
+ <option>OpenRepeat</option> is the number of attempts to make and
+ <option>OpenTimeout</option> is the amount of time to spend on a
+ connection attempt. After <option>OpenRepeat</option> attempts have been
+ made, or if <option>OpenTimeout</option> seconds elapse in any particular
+ connection attempt, the start attempt is considered failed.
+
+Key: StartAttempts
+Type: int
+Default: 4
+User: core
+Instance: #*/
+Merge: xdm
+Comment:
+ Try at most that many times to start a display. If this fails, the display
+ is disabled.
+Description:
+ How many times &kdm; should attempt to start a <literal>foreign</literal>
+ display listed in <option>StaticServers</option> before giving up
+ and disabling it.
+ Local displays are attempted only once, and &XDMCP; displays are retried
+ indefinitely by the client (unless the option <option>-once</option>
+ was given to the &X-Server;).
+
+Key: ServerAttempts
+Type: int
+Default: 1
+User: core
+Instance: #:*/
+Merge: xdm
+Comment:
+ How often to try to run the &X-Server;. Running includes executing it and
+ waiting for it to come up.
+Description:
+ How many times &kdm; should attempt to start up a local &X-Server;.
+ Starting up includes executing it and waiting for it to come up.
+
+Key: ServerTimeout
+Type: int
+Default: 15
+User: core
+Instance: #:*/
+Comment:
+ How long to wait for a local &X-Server; to come up.
+Description:
+ How many seconds &kdm; should wait for a local &X-Server; to come up.
+
+Key: ServerCmd
+Type: string
+Default: DEF_SERVER_CMD
+DDefault: -
+User: core
+Instance: :*/DEF_SERVER_CMD
+Comment:
+ The command line to start the &X-Server;, without display number and VT spec.
+ This string is subject to word splitting.
+Description:
+ The command line to start the &X-Server;, without display number and VT spec.
+ This string is subject to word splitting.
+ </para><para>
+ The default is something reasonable for the system on which &kdm; was built,
+ like <command>/usr/X11R6/bin/X</command>.
+
+Key: ServerArgsLocal
+Type: string
+Default: ""
+User: core
+Instance: :*/"-nolisten tcp"
+Comment: &
+Description:
+ Additional arguments for the &X-Server;s for local sessions.
+ This string is subject to word splitting.
+
+Key: ServerArgsRemote
+Type: string
+Default: ""
+User: core
+Instance: #:*/""
+Comment: &
+Description:
+ Additional arguments for the &X-Server;s for remote sessions.
+ This string is subject to word splitting.
+
+Key: ServerVT
+If: defined(HAVE_VTS)
+Type: int
+Default: 0
+User: core(reqSrvVT)
+Instance: #:0/7
+Comment:
+ The VT the &X-Server; should run on; auto-assign if zero, don't assign if -1.
+ Better leave it zero and use ServerVTs.
+Description:
+ The VT the &X-Server; should run on.
+ <option>ServerVTs</option> should be used instead of this option.
+ Leave it zero to let &kdm; assign a <acronym>VT</acronym> automatically.
+ Set it to <literal>-1</literal> to avoid assigning a <acronym>VT</acronym>
+ alltogether - this is required for setups with multiple physical consoles.
+ Currently Linux only.
+
+Key: ServerTTY
+If: !defined(HAVE_VTS)
+Type: string
+Default: ""
+User: core(console)
+Instance: :0/DEF_SERVER_TTY
+Comment:
+ The TTY line (without /dev/) the &X-Server; covers physically.
+Description:
+ This option is for <acronym>OS</acronym>s without support for
+ <acronym>VT</acronym>s, either by &kdm; or the <acronym>OS</acronym> itself.
+ Currently this applies to all <acronym>OS</acronym>s but Linux.
+ </para><para>
+ When &kdm; switches to console mode, it starts monitoring this
+ <acronym>TTY</acronym> line (specified without the leading
+ <literal>/dev/</literal>) for activity. If the line is not used for some time,
+ &kdm; switches back to the X login.
+
+Key: PingInterval
+Type: int
+Default: 5
+User: core
+User: greeter
+Instance: #*/
+Merge: xdm
+Comment:
+ Ping remote display every that many minutes.
+Description:
+ See <option>PingTimeout</option>.
+
+Key: PingTimeout
+Type: int
+Default: 5
+User: core
+User: greeter
+Instance: #*/
+Merge: xdm
+Comment:
+ Wait for a Pong that many minutes.
+Description:
+ To discover when <emphasis>remote</emphasis> displays disappear, &kdm;
+ regularly pings them.
+ <option>PingInterval</option> specifies the time (in minutes) between the
+ pings and <option>PingTimeout</option> specifies the maximum amount of
+ time (in minutes) to wait for the terminal to respond to the request. If
+ the terminal does not respond, the session is declared dead and terminated.
+ </para><para>
+ If you frequently use X terminals which can become isolated from
+ the managing host, you may wish to increase the timeout. The only worry
+ is that sessions will continue to exist after the terminal has been
+ accidentally disabled.
+
+Key: TerminateServer
+Type: bool
+Default: false
+User: core
+Instance: #:*/!
+Merge: xdm
+Comment:
+ Restart instead of resetting the local &X-Server; after session exit.
+ Use it if the server leaks memory etc.
+Description:
+ Whether &kdm; should restart the local &X-Server; after session exit instead
+ of resetting it. Use this if the &X-Server; leaks memory or crashes the system
+ on reset attempts.
+
+Key: ResetSignal
+Type: int
+Default: 1
+CDefault: 1 (SIGHUP)
+User: core
+Instance: #:*/
+Merge: xdm
+Comment:
+ The signal needed to reset the local &X-Server;.
+Description:
+ The signal number to use to reset the local &X-Server;.
+
+Key: TermSignal
+Type: int
+Default: 15
+CDefault: 15 (SIGTERM)
+User: core
+Instance: #:*/
+Merge: xdm
+Comment:
+ The signal needed to terminate the local &X-Server;.
+Description:
+ The signal number to use to terminate the local &X-Server;.
+
+Key: Authorize
+Type: bool
+Default: true
+User: core
+Instance: #:*/!
+Merge: xdm
+Comment:
+ Create X-authorizations for local displays.
+Description:
+ Controls whether &kdm; generates and uses authorization for
+ <emphasis>local</emphasis> &X-Server; connections.
+ For &XDMCP; displays the authorization requested by the display is used;
+ foreign non-&XDMCP; displays do not support authorization at all.
+
+Key: AuthNames
+Type: list
+Default: DEF_AUTH_NAME
+User: core
+Instance: #:*/""
+Merge: xdm:authName
+Comment:
+ Which X-authorization mechanisms should be used.
+Description:
+ If <option>Authorize</option> is true, use the authorization mechanisms
+ listed herein. The MIT-MAGIC-COOKIE-1 authorization is always available;
+ XDM-AUTHORIZATION-1, SUN-DES-1 and MIT-KERBEROS-5 might be available as well,
+ depending on the build configuration.
+
+Key: ResetForAuth
+Type: bool
+Default: false
+User: core
+Instance: #:*/!
+Merge: xdm
+Comment:
+ Need to reset the &X-Server; to make it read initial Xauth file.
+Description:
+ Some <emphasis>old</emphasis> &X-Server;s re-read the authorization file
+ at &X-Server; reset time, instead of when checking the initial connection.
+ As &kdm; generates the authorization information just before connecting to
+ the display, an old &X-Server; would not get up-to-date authorization
+ information. This option causes &kdm; to send SIGHUP to the &X-Server;
+ after setting up the file, causing an additional &X-Server; reset to occur,
+ during which time the new authorization information will be read.
+
+Key: AuthFile
+Type: string
+Default: ""
+User: core(clientAuthFile)
+Instance: #*/""
+Merge: xdm
+Comment:
+ The name of this &X-Server;'s Xauth file.
+ If empty, a random name in the AuthDir directory will be used.
+Description:
+ This file is used to communicate the authorization data from &kdm; to
+ the &X-Server;, using the <option>-auth</option> &X-Server; command line
+ option. It should be kept in a directory which is not world-writable
+ as it could easily be removed, disabling the authorization mechanism in
+ the &X-Server;. If not specified, a random name is generated from
+ <option>AuthDir</option> and the name of the display.
+
+Key: Resources
+# XXX strictly speaking this is supposed to be a string list, i think.
+Type: string
+Default: ""
+User: core
+Instance: #*/""
+Update: cp_resources
+Merge: xdm
+Comment:
+ Specify a file with X-resources for the greeter, chooser and background.
+ The KDE frontend does not use this file, so you do not need it unless you
+ use another background generator than krootimage.
+Description:
+ This option specifies the name of the file to be loaded by
+ <command>xrdb</command> as the resource database onto the root window
+ of screen 0 of the display. KDE programs generally do not use
+ X-resources, so this option is only needed if the <option>Setup</option>
+ program needs some X-resources.
+
+Key: Xrdb
+Type: string
+Default: XBINDIR "/xrdb"
+User: core
+Instance: #*/""
+Merge: xdm
+Comment:
+ The xrdb program to use to read the above specified recources.
+ Subject to word splitting.
+Description:
+ The <command>xrdb</command> program to use to read the X-resources file
+ specified in <option>Recources</option>.
+ The command is subject to word splitting.
+
+Key: Setup
+Type: string
+Default: ""
+User: core
+# will be overwritten
+Instance: #*/""
+Update: mk_setup
+Merge: xdm
+Comment:
+ A program to run before the greeter is shown. Can be used to start an
+ xconsole or an alternative background generator. Subject to word splitting.
+Description:
+ This string is subject to word splitting.
+ It specifies a program which is run (as
+ <systemitem class="username">root</systemitem>) before offering the
+ greeter window. This may be used to change the appearance of the screen
+ around the greeter window or to put up other windows (e.g., you may want
+ to run <command>xconsole</command> here).
+ The conventional name for a file used here is <command>Xsetup</command>.
+ See <xref linkend="kdmrc-xsetup"/>.
+
+Key: Startup
+Type: string
+Default: ""
+User: core
+# will be overwritten
+Instance: #*/""
+Update: mk_startup
+Merge: xdm
+Comment:
+ A program to run before a user session starts. Subject to word splitting.
+Description:
+ This string is subject to word splitting.
+ It specifies a program which is run (as
+ <systemitem class="username">root</systemitem>) after the user
+ authentication process succeeds.
+ The conventional name for a file used here is <command>Xstartup</command>.
+ See <xref linkend="kdmrc-xstartup"/>.
+
+Key: Reset
+Type: string
+Default: ""
+User: core
+# will be overwritten
+Instance: #*/""
+Update: mk_reset
+Merge: xdm
+Comment:
+ A program to run after a user session exits. Subject to word splitting.
+Description:
+ This string is subject to word splitting.
+ It specifies a program which is run (as
+ <systemitem class="username">root</systemitem>) after the session
+ terminates.
+ The conventional name for a file used here is <command>Xreset</command>.
+ See <xref linkend="kdmrc-xreset"/>.
+
+Key: Session
+Type: string
+Default: XBINDIR "/xterm -ls -T"
+#Merge: xdm - incompatible!
+User: core
+# will be overwritten
+Instance: #*/""
+Update: mk_session
+Comment:
+ The program which is run as the user which logs in. It is supposed to
+ interpret the session argument (see SessionsDirs) and start an appropriate
+ session according to it. Subject to word splitting.
+Description:
+ This string is subject to word splitting.
+ It specifies the session program to be executed (as the user owning
+ the session).
+ The conventional name for a file used here is <command>Xsession</command>.
+ See <xref linkend="kdmrc-xsession"/>.
+
+Key: FailsafeClient
+Type: string
+Default: XBINDIR "/xterm"
+User: core
+Instance: #*/""
+Merge: xdm
+Comment:
+ The program to run if Session fails.
+Description:
+ If the <option>Session</option> program fails to execute, &kdm; will
+ fall back to this program. This program is executed with no arguments,
+ but executes using the same environment variables as the session would
+ have had (see <xref linkend="kdmrc-xsession"/>).
+
+Key: UserPath
+Type: string
+Default: DEF_USER_PATH
+DDefault: -
+User: core
+Instance: #*/""
+Merge: xdm
+Comment:
+ The PATH for the Session program.
+Description:
+ The <envar>PATH</envar> environment variable for
+ non-<systemitem class="username">root</systemitem> <option>Session</option>s.
+ </para><para>
+ The default depends on the system &kdm; was built on.
+
+Key: SystemPath
+Type: string
+Default: DEF_SYSTEM_PATH
+DDefault: -
+User: core
+Instance: #*/""
+Merge: xdm
+Comment:
+ The PATH for Setup, Startup and Reset, etc.
+Description:
+ The <envar>PATH</envar> environment variable for all programs but
+ non-<systemitem class="username">root</systemitem>
+ <option>Session</option>s. Note that it is good practice not to include
+ <literal>.</literal> (the current directory) into this entry.
+ </para><para>
+ The default depends on the system &kdm; was built on.
+
+Key: SystemShell
+Type: string
+Default: "/bin/sh"
+User: core
+Instance: #*/"/bin/bash"
+Merge: xdm
+Comment:
+ The default system shell.
+Description:
+ The <envar>SHELL</envar> environment variable for all programs but the
+ <option>Session</option>.
+
+Key: UserAuthDir
+Type: path
+Default: "/tmp"
+User: core
+Instance: #*/""
+Merge: xdm
+Comment:
+ Where to put the user's &X-Server; authorization file if ~/.Xauthority
+ cannot be created.
+Description:
+ When &kdm; is unable to write to the usual user authorization file
+ ($<envar>HOME</envar>/.Xauthority), it creates a unique file name in this
+ directory and points the environment variable <envar>XAUTHORITY</envar>
+ at the created file.
+
+Key: AutoReLogin
+Type: bool
+Default: false
+User: core
+Instance: #*/!
+Merge: xdm
+Comment:
+ Whether to automatically restart sessions after &X-Server; crashes.
+ Note that enabling this makes circumventing screen lockers other than
+ KDE's built-in one possible!
+Description:
+ If enabled, &kdm; will automatically restart a session after an &X-Server;
+ crash (or if it is killed by Alt-Ctrl-BackSpace). Note that enabling this
+ feature opens a security hole: a secured display lock can be circumvented
+ (unless &kde;'s built-in screen locker is used).
+
+Key: AllowRootLogin
+Type: bool
+Default: true
+User: core
+User: greeter(showRoot)
+Instance: */false
+Merge: xdm
+Comment:
+ Allow root logins?
+Description:
+ If disabled, do not allow <systemitem class="username">root</systemitem>
+ (and any other user with UID = 0) to log in directly.
+
+Key: AllowNullPasswd
+Type: bool
+Default: true
+User: core
+# sensible?
+Instance: */false
+Instance: :*/true
+Merge: xdm
+Comment:
+ Allow to log in, when user has set an empty password?
+Description:
+ If disabled, only users that have passwords assigned can log in.
+
+Key: AllowShutdown
+Type: enum
+ None/SHUT_NONE: no <guilabel>Shutdown...</guilabel> menu entry is shown at all
+ Root/SHUT_ROOT: the <systemitem class="username">root</systemitem> password must be entered to shut down
+ All/SHUT_ALL: everybody can shut down the machine
+Default: All
+User: core
+User: greeter
+Instance: */Root
+Instance: :*/All
+Merge: kdm:-Greeter/
+Comment: &
+Description:
+ Who is allowed to shut down the system. This applies both to the
+ greeter and to the command <acronym>FiFo</acronym>.
+
+Key: AllowSdForceNow
+Type: enum
+ None: no forced shutdown is allowed at all
+ Root: the <systemitem class="username">root</systemitem> password must be entered to shut down forcibly
+ All: everybody can shut down the machine forcibly
+Default: All
+User: core(allowNuke)
+User: greeter(allowNuke)
+Instance: #*/Root
+Comment: &
+Description:
+ Who is allowed to abort active sessions when shutting down.
+
+Key: DefaultSdMode
+Type: enum
+ Schedule: shut down after all active sessions exit (possibly at once)
+ TryNow: shut down, if no active sessions are open; otherwise, do nothing
+ ForceNow: shut down unconditionally
+Default: Schedule
+User: core(defSdMode)
+User: greeter(defSdMode)
+Instance: #*/ForceNow
+Comment: &
+Description:
+ The default choice for the shutdown condition/timing.
+
+Key: ScheduledSd
+Type: enum
+ Never/SHUT_NEVER: not at all
+ Optional/SHUT_OPTION: as a button in the simple shutdown dialogs
+ Always/SHUT_ALWAYS: instead of the simple shutdown dialogs
+Default: Never
+User: greeter
+Instance: #*/Optional
+Comment: &
+Description:
+ How to offer shutdown scheduling options:
+
+Key: NoPassEnable
+Type: bool
+Default: false
+User: dep
+Instance: #:*/true
+Comment: &
+Description:
+ Enable password-less logins on this display. <emphasis>Use with extreme care!</emphasis>
+
+Key: NoPassUsers
+Type: list
+Default: ""
+PostProc: PnoPassUsers
+User: core
+Instance: #:*/"fred,ethel"
+Merge: xdm(P_noPassUsers)
+Comment:
+ The users that do not need to provide a password to log in. NEVER list root!
+ "*" means all non-root users. @<group> means all users in that group.
+Description:
+ The users that do not need to provide a password to log in.
+ Items which are prefixed with <literal>@</literal> represent all users in the
+ user group named by that item.
+ <literal>*</literal> means all users but
+ <systemitem class="username">root</systemitem>
+ (and any other user with UID = 0).
+ <emphasis>Never</emphasis> list <systemitem class="username">root</systemitem>.
+
+Key: AutoLoginEnable
+Type: bool
+Default: false
+User: dep
+Instance: #:0/true
+Comment: &
+Description:
+ Enable automatic login. <emphasis>Use with extreme care!</emphasis>
+
+Key: AutoLoginAgain
+Type: bool
+Default: false
+User: core(autoAgain)
+User: greeter
+Instance: #:0/true
+Comment: &
+Description:
+ If true, auto-login after logout. If false, auto-login is performed only
+ when a display session starts up.
+
+Key: AutoLoginDelay
+Type: int
+Default: 0
+User: core(autoDelay)
+User: greeter
+Instance: #:0/10
+Comment:
+ The delay in seconds before automatic login kicks in.
+Description:
+ The delay in seconds before automatic login kicks in. This is also known as
+ <quote>Timed Login</quote>.
+
+Key: AutoLoginUser
+Type: string
+Default: ""
+PostProc: PautoLoginX
+User: core(autoUser)
+User: greeter
+Instance: #:0/"fred"
+Merge: xdm:autoUser(P_autoUser)
+Comment: &
+Description:
+ The user to log in automatically. <emphasis>Never</emphasis> specify <systemitem class="username">root</systemitem>!
+
+Key: AutoLoginPass
+Type: string
+Default: ""
+PostProc: PautoLoginX
+User: core(autoPass)
+Instance: #:0/"secret!"
+Merge: xdm:autoPass(P_autoPass)
+Comment: &
+Description:
+ The password for the user to log in automatically. This is <emphasis>not</emphasis> required
+ unless the user is logged into a <acronym>NIS</acronym> or Kerberos domain. If you use this
+ option, you should <command>chmod&nbsp;<option>600</option>&nbsp;<filename>kdmrc</filename></command> for obvious reasons.
+
+Key: AutoLoginLocked
+Type: bool
+Default: false
+User: core(autoLock)
+Instance: #:0/!
+Comment: &
+Description:
+ Immediately lock the automatically started session. This works only with
+ KDE sessions.
+
+Key: SessionsDirs
+Type: list
+Default: KDMDATA "/sessions"
+User: core
+User: greeter-c
+Instance: #*/"/etc/X11/sessions,/usr/share/xsessions"
+Comment:
+ The directories containing session type definitions in .desktop format.
+Description:
+ A list of directories containing session type definitions.
+# See <xref linkend="kdmrc-sessions"> for details.
+
+Key: ClientLogFile
+Type: string
+Default: ".xsession-errors"
+User: core
+Instance: */".xsession-errors-%s"
+Instance: :0/".xsession-errors"
+Comment:
+ The file (relative to $HOME) to redirect the session output to. This is
+ a printf format string; one %s will be replaced with the display name.
+Description:
+ The file (relative to the user's home directory) to redirect the session
+ output to. One occurrence of <parameter>%s</parameter> in this string will be
+ substituted with the display name. Use <parameter>%%</parameter> to obtain a
+ literal <literal>%</literal>.
+
+Key: UseSessReg
+Type: bool
+Default: true
+User: core
+Instance: #*/!
+Comment:
+ Whether &kdm;'s built-in utmp/wtmp/lastlog registration should be used.
+Description:
+ Specify whether &kdm;'s built-in utmp/wtmp/lastlog registration should
+ be used. If it is not, the tool <command>sessreg</command> should be used
+ in the <option>Startup</option> and <option>Reset</option> scripts, or,
+ alternatively, the pam_lastlog module should be used on
+ <acronym>PAM</acronym>-enabled systems.
+
+
+Section: -Greeter
+Description:
+ This section class contains options concerning the configuration
+ of the &kdm; frontend (greeter).
+
+Key: GUIStyle
+Type: string
+Default: ""
+User: greeter
+Instance: #*/"Windows"
+Update: upd_guistyle
+Comment:
+ Widget style of the greeter. "" means the built-in default which currently
+ is "Plastik".
+Description:
+ Specify the widget style for the greeter. Empty means to use the
+ built-in default which currently is <literal>Plastik</literal>.
+
+Key: ColorScheme
+Type: string
+Default: ""
+User: greeter
+Instance: #*/"Pumpkin"
+Comment:
+ Widget color scheme of the greeter. "" means the built-in default which
+ currently is yellowish grey with some light blue and yellow elements.
+Description:
+ Specify the widget color scheme for the greeter. Empty means to use
+ the built-in default which currently is yellowish grey with some light
+ blue and yellow elements.
+
+Key: LogoArea
+Type: enum
+ None/LOGO_NONE: nothing
+ Logo/LOGO_LOGO: the image specified by <option>LogoPixmap</option>
+ Clock/LOGO_CLOCK: a neat analog clock
+Default: Clock
+User: greeter
+Instance: */Logo
+Comment:
+ What should be shown in the greeter's logo are:
+Description:
+ What should be shown in the greeter righthand of the input lines (if
+ <option>UserList</option> is disabled) or above them (if
+ <option>UserList</option> is enabled):
+
+Key: LogoPixmap
+Type: string
+Default: ""
+User: greeter(logo)
+Instance: */KDMDATA "/pics/kdelogo.png"
+Comment:
+ The image to show when LogoArea=Logo.
+Description:
+ The image to show in the greeter if <option>LogoArea</option> is
+ <literal>Logo</literal>.
+
+Key: GreeterPos
+Type: string
+Default: "50,50"
+User: greeter-c
+Instance: #*/"30,40"
+Comment:
+ The relative coordinates (X,Y in percent) of the center of the greeter.
+Description:
+ The relative coordinates (percentages of the screen size; X,Y) at which
+ the center of the greeter is put. &kdm; aligns the greeter to the edges
+ of the screen it would cross otherwise.
+
+Key: GreeterScreen
+Type: int
+Default: 0
+User: greeter
+Instance: #*/-1
+Comment: &
+Description:
+ The screen the greeter should be displayed on in multi-headed and Xinerama
+ setups. The numbering starts with 0. For Xinerama, it corresponds to the
+ listing order in the active ServerLayout section of XF86Config; -1 means
+ to use the upper-left screen, -2 means to use the upper-right screen.
+
+Key: GreetString
+Type: string
+Default: "Welcome to %s at %n"
+User: greeter
+Instance: #*/"K Desktop Environment (%n)"
+Comment:
+ The headline in the greeter. The following character pairs are replaced:
+ - %d -> current display
+ - %h -> host name, possibly with domain name
+ - %n -> node name, most probably the host name without domain name
+ - %s -> the operating system
+ - %r -> the operating system's version
+ - %m -> the machine (hardware) type
+ - %% -> a single %
+Description:
+ The headline in the greeter. An empty greeting means none at all.
+ </para><para>
+ The following character pairs are replaced by their value:
+ <variablelist>
+ <varlistentry>
+ <term><parameter>%d</parameter></term>
+ <listitem><para>name of the current display</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>%h</parameter></term>
+ <listitem><para>local host name, possibly with the
+ domain name</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>%n</parameter></term>
+ <listitem><para>local node name, most probably the host name without the
+ domain name</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>%s</parameter></term>
+ <listitem><para>operating system</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>%r</parameter></term>
+ <listitem><para>operating system version</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>%m</parameter></term>
+ <listitem><para>machine (hardware) type</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>%%</parameter></term>
+ <listitem><para>a single <literal>%</literal></para></listitem>
+ </varlistentry>
+ </variablelist>
+
+# This needs to come _in front_ of the font settings to be effective!
+Key: AntiAliasing
+Type: bool
+Default: false
+User: greeter
+Instance: #*/!
+Comment: &
+Description:
+ Whether the fonts used in the greeter should be antialiased.
+
+Key: GreetFont
+Type: string
+Default: "Serif,20,5,0,50,0"
+CDefault: "Serif,20,bold"
+User: greeter:font
+Instance: #*/"Serif,20,5,0,50,0"
+Comment: &
+Description:
+ The font for the greeter headline.
+
+Key: StdFont
+Type: string
+Default: "Sans Serif,10,5,0,50,0"
+CDefault: "Sans Serif,10"
+User: greeter(normalFont):font
+Instance: #*/"Sans Serif,10,5,0,50,0"
+Comment: &
+Description:
+ The normal font used in the greeter.
+
+Key: FailFont
+Type: string
+Default: "Sans Serif,10,5,0,75,0"
+CDefault: "Sans Serif,10,bold"
+User: greeter:font
+Instance: #*/"Sans Serif,10,5,0,75,0"
+Comment: &
+Description:
+ The font used for the <quote>Login Failed</quote> message.
+
+Key: NumLock
+Type: enum
+ Off: turn off
+ On: turn on
+ Keep: do not change the state
+Default: Keep
+User: greeter(numLockStatus)
+Instance: #*/Off
+Comment: &
+Description:
+ What to do with the Num Lock modifier for the time the greeter is running:
+
+Key: Language
+Type: string
+Default: "en_US"
+User: greeter-c
+Instance: #*/"de_DE"
+Update: upd_language
+Comment: &
+Description:
+ Language and locale to use in the greeter, encoded like $<envar>LC_LANG</envar>.
+
+Key: UserCompletion
+Type: bool
+Default: false
+User: greeter
+Instance: #*/!
+Comment: &
+Description:
+ Enable autocompletion in the username line edit.
+
+Key: UserList
+Type: bool
+Default: true
+User: greeter
+Instance: #*/!
+Comment:
+ Enable user list (names along with images) in the greeter.
+Description:
+ Show a user list with unix login names, real names, and images in the greeter.
+
+Key: ShowUsers
+Type: enum
+ NotHidden/SHOW_ALL: all users except those listed in HiddenUsers
+ Selected/SHOW_SEL: only the users listed in SelectedUsers
+Default: NotHidden
+User: greeter
+Instance: #*/Selected
+Update: upd_showusers
+Comment:
+ User selection for UserCompletion and UserList:
+Description: !
+ This option controls which users will be shown in the user view
+ (<option>UserList</option>) and/or offered for autocompletion
+ (<option>UserCompletion</option>).
+ If it is <literal>Selected</literal>, <option>SelectedUsers</option> contains
+ the final list of users.
+ If it is <literal>NotHidden</literal>, the initial user list are all users
+ found on the system. Users contained in <option>HiddenUsers</option> are
+ removed from the list, just like all users with a UID greater than specified
+ in <option>MaxShowUID</option> and users with a non-zero UID less than
+ specified in <option>MinShowUID</option>.
+ Items in <option>SelectedUsers</option> and <option>HiddenUsers</option>
+ which are prefixed with <literal>@</literal> represent all users in the
+ user group named by that item.
+ Finally, the user list will be sorted alphabetically, if
+ <option>SortUsers</option> is enabled.
+
+Key: SelectedUsers
+Type: list
+Default: ""
+User: greeter-c(users)
+Instance: #*/"root,johndoe"
+Merge: kdm:Users
+Comment:
+ For ShowUsers=Selected. @<group> means all users in that group.
+Description:
+ See <option>ShowUsers</option>.
+
+Key: HiddenUsers
+Type: list
+Default: ""
+User: greeter-c(noUsers)
+Instance: #*/"root"
+# depends on {Min,Max}ShowUID
+Update: upd_hiddenusers/1
+Merge: kdm:NoUsers
+Comment:
+ For ShowUsers=NotHidden. @<group> means all users in that group.
+Description:
+ See <option>ShowUsers</option>.
+
+Key: MinShowUID
+Type: int
+Default: 0
+User: greeter(lowUserId)
+# will be overwritten
+Instance: #*/
+Update: upd_minshowuid
+Comment:
+ Special case of HiddenUsers: users with a non-zero UID less than this number
+ will not be shown as well.
+Description:
+ See <option>ShowUsers</option>.
+
+Key: MaxShowUID
+Type: int
+Default: 65535
+User: greeter(highUserId)
+# will be overwritten
+Instance: #*/
+Update: upd_maxshowuid
+Comment:
+ Complement to MinShowUID: users with a UID greater than this number will
+ not be shown as well.
+Description:
+ See <option>ShowUsers</option>.
+
+Key: SortUsers
+Type: bool
+Default: true
+User: greeter
+Instance: #*/!
+Comment:
+ If false, the users are listed in the order they appear in /etc/passwd.
+ If true, they are sorted alphabetically.
+Description:
+ See <option>ShowUsers</option>.
+
+Key: FaceSource
+Type: enum
+ AdminOnly/FACE_ADMIN_ONLY: from <filename>&lt;<option>FaceDir</option>&gt;/$<envar>USER</envar>.face[.icon]</filename>
+ PreferAdmin/FACE_PREFER_ADMIN: prefer &lt;<option>FaceDir</option>&gt;, fallback on $<envar>HOME</envar>
+ PreferUser/FACE_PREFER_USER: ... and the other way round
+ UserOnly/FACE_USER_ONLY: from the user's <filename>$<envar>HOME</envar>/.face[.icon]</filename>
+Default: AdminOnly
+User: greeter
+Instance: #*/PreferUser
+Comment:
+ Specify, where the users' pictures should be taken from.
+Description:
+ If <option>UserList</option> is enabled, this specifies where &kdm; gets the
+ images from:
+ </para>
+ %ENUM%
+ <para>
+ The images can be in any format Qt recognizes, but the filename
+ must match &kdm;'s expectations: <literal>.face.icon</literal> should be a
+ 48x48 icon, while <literal>.face</literal> should be a 300x300 image.
+ Currently the big image is used only as a fallback and is scaled down,
+ but in the future it might be displayed full-size in the logo area or a
+ tooltip.
+
+Key: FaceDir
+Type: string
+Default: *KDMDATA "/faces"
+User: greeter
+Instance: #*/"/usr/share/faces"
+Update: upd_facedir
+Comment:
+ The directory containing the user images if FaceSource is not UserOnly.
+Description:
+ See <option>FaceSource</option>.
+
+Key: PreselectUser
+Type: enum
+ None/PRESEL_NONE: do not preselect any user
+ Previous/PRESEL_PREV: the user which successfully logged in last time
+ Default/PRESEL_DEFAULT: the user specified in the <option>DefaultUser</option> option
+Default: None
+User: greeter(preselUser)
+Instance: #*/Previous
+Instance: :*/Previous
+Instance: #:0/Default
+Comment:
+ Specify, if/which user should be preselected for log in.
+Description:
+ Specify, if/which user should be preselected for log in:
+ </para>
+ %ENUM%
+ <para>
+ If <option>FocusPasswd</option> is enabled and a user was preselected,
+ the cursor is placed in the password input field automatically.
+ </para>
+ <note><para>Enabling user preselection can be considered a security hole,
+ as it presents a valid login name to a potential attacker, so he
+ <quote>only</quote> needs to guess the password. On the other hand,
+ one could set <option>DefaultUser</option> to a fake login name.</para></note>
+ <para>
+
+Key: DefaultUser
+Type: string
+Default: ""
+User: greeter
+Instance: #:0/"johndoe"
+Comment:
+ The user to preselect if PreselectUser=Default.
+Description:
+ See <option>PreselectUser</option>.
+
+Key: FocusPasswd
+Type: bool
+Default: false
+User: greeter
+Instance: #*/!
+Instance: :*/true
+Comment:
+ If this is true, the password input line is focused automatically if
+ a user is preselected.
+Description:
+ See <option>PreselectUser</option>.
+
+Key: EchoMode
+Type: enum
+ OneStar: <literal>*</literal> is shown for every typed letter
+ ThreeStars: <literal>***</literal> is shown for every typed letter
+ NoEcho: nothing is shown at all, the cursor does not move
+# HACK! This must be in sync with KPasswordEdit::EchoModes (kpassdlg.h)
+Default: OneStar
+User: greeter
+Instance: #*/NoEcho
+Comment: &
+Description:
+ The password input fields cloak the typed in text. Specify, how to do it:
+
+Key: UseBackground
+Type: bool
+Default: true
+User: greeter
+Instance: #*/!
+Comment:
+ If true, krootimage will be automatically started by &kdm;; otherwise, the
+ Setup script should be used to setup the background.
+Description:
+ If enabled, &kdm; will automatically start the <command>krootimage</command>
+ program to set up the background; otherwise, the <option>Setup</option>
+ program is responsible for the background.
+
+Key: BackgroundCfg
+Type: string
+Default: *KDMCONF "/backgroundrc"
+User: greeter-c
+Instance: #*/""
+Update: handBgCfg
+Comment:
+ The configuration file to be used by krootimage.
+Description:
+ The configuration file to be used by <command>krootimage</command>.
+ It contains a section named <literal>[Desktop0]</literal> like
+ <filename>kdesktoprc</filename> does. Its options are not described
+ herein; guess their meanings or use the control center.
+
+Key: GrabServer
+Type: bool
+Default: false
+User: greeter-c
+Instance: #*/!
+Comment:
+ Hold the &X-Server; grabbed the whole time the greeter is visible. This
+ may be more secure, but it will disable any background and other
+ X-clients started from the Setup script.
+Description:
+ To improve security, the greeter grabs the &X-Server; and then the keyboard
+ when it starts up. This option specifies if the &X-Server; grab should be held
+ for the duration of the name/password reading. When disabled, the &X-Server;
+ is ungrabbed after the keyboard grab succeeds; otherwise, the &X-Server; is
+ grabbed until just before the session begins.
+ </para>
+ <note><para>Enabling this option disables <option>UseBackground</option> and
+ <option>Setup</option>.</para></note>
+ <para>
+
+Key: GrabTimeout
+Type: int
+Default: 3
+User: greeter
+Instance: #*/
+Comment:
+ How many seconds to wait for grab to succeed.
+Description:
+ This option specifies the maximum time &kdm; will wait for the grabs to
+ succeed. A grab may fail if some other X-client has the &X-Server; or the
+ keyboard grabbed, or possibly if the network latencies are very high. You
+ should be cautious when raising the timeout, as a user can be spoofed by
+ a look-alike window on the display. If a grab fails, &kdm; kills and
+ restarts the &X-Server; (if possible) and the session.
+
+Key: AuthComplain
+Type: bool
+Default: true
+User: greeter
+Instance: #*/!
+Merge: xdm
+Comment:
+ Warn, if display has no X-authorization (local auth cannot be created,
+ &XDMCP; display wants no auth, or display is foreign from StaticServers).
+Description:
+ Warn, if a display has no X-authorization. This will be the case if
+ <itemizedlist>
+ <listitem><para>
+ the authorization file for a local &X-Server; could not be created,
+ </para></listitem>
+ <listitem><para>
+ a remote display from &XDMCP; did not request any authorization or
+ </para></listitem>
+ <listitem><para>
+ the display is a <quote>foreign</quote> display specified in
+ <option>StaticServers</option>.
+ </para></listitem>
+ </itemizedlist>
+
+Key: LoginMode
+If: defined(XDMCP)
+Type: enum
+ LocalOnly/LOGIN_LOCAL_ONLY: only local login possible
+ DefaultLocal/LOGIN_DEFAULT_LOCAL: start up in local mode, but allow switching to remote mode
+ DefaultRemote/LOGIN_DEFAULT_REMOTE: ... and the other way round
+ RemoteOnly/LOGIN_REMOTE_ONLY: only choice of remote host possible
+Default: LocalOnly
+User: core
+User: greeter
+Instance: :*/DefaultLocal
+# from make_it_cool branch and SuSE 8.1
+Merge: kdm:EnableChooser(P_EnableChooser)
+Comment: &
+Description:
+ Specify whether the greeter of local displays should start up in host chooser
+ (remote) or login (local) mode and whether it is allowed to switch to the
+ other mode.
+
+Key: ChooserHosts
+If: defined(XDMCP)
+Type: list
+Default: "*"
+User: core
+Instance: #:*/"*,ugly,sky,dino,kiste.local,login.crap.com"
+Comment:
+ A list of hosts to be automatically added to the remote login menu. The
+ special name "*" means broadcast.
+Description:
+ A list of hosts to be automatically added to the remote login menu.
+ The special name <literal>*</literal> means broadcast.
+ Has no effect if <option>LoginMode</option> is <literal>LocalOnly</literal>.
+
+Key: ForgingSeed
+Type: int
+Default: 0
+User: greeter
+Instance: #*/
+Update: upd_forgingseed
+Comment:
+ Random seed for forging saved session types, etc. of unknown users.
+ This value should be random but constant across the login domain.
+Description:
+ Use this number as a random seed when forging saved session types, etc. of
+ unknown users. This is used to avoid telling an attacker about existing users
+ by reverse conclusion. This value should be random but constant across the
+ login domain.
+
+Key: ShowLog
+If: defined(WITH_KDM_XCONSOLE)
+Type: bool
+Default: false
+User: greeter
+Instance: :0/true
+Comment:
+ Enable &kdm;'s built-in xconsole. Note that this can be enabled for only
+ one display at a time.
+Description:
+ Enable &kdm;'s built-in <command>xconsole</command>.
+ Note that this can be enabled for only one display at a time.
+ This option is available only if &kdm; was <command>configure</command>d
+ with <option>--enable-kdm-xconsole</option>.
+
+Key: LogSource
+If: defined(WITH_KDM_XCONSOLE)
+Type: string
+Default: ""
+User: greeter-c
+Instance: :0/"/dev/xconsole"
+Comment:
+ The data source for &kdm;'s built-in xconsole.
+ If empty, a console log redirection is requested from /dev/console.
+Description:
+ The data source for &kdm;'s built-in <command>xconsole</command>.
+ If empty, a console log redirection is requested from
+ <filename>/dev/console</filename>.
+ Has no effect if <option>ShowLog</option> is disabled.
+
+Key: PluginsLogin
+Type: list
+Default: "classic"
+User: greeter
+Instance: #*/"sign"
+Comment:
+ Specify conversation plugins for the login dialog. Each plugin can be
+ specified as a base name (which expands to $kde_modulesdir/kgreet_$base)
+ or as a full pathname.
+Description:
+ Specify conversation plugins for the login dialog; the first in the list
+ is selected initially.
+ Each plugin can be specified as a base name (which expands to
+ <filename>$<envar>kde_modulesdir</envar>/kgreet_<replaceable>base</replaceable></filename>)
+ or as a full pathname.
+ </para><para>
+ Conversation plugins are modules for the greeter which obtain authentication
+ data from the user. Currently only the <literal>classic</literal> plugin is
+ shipped with &kde;; it presents the well-known username and password form.
+
+Key: PluginsShutdown
+Type: list
+Default: "classic"
+User: greeter
+Instance: #*/"modern"
+Comment: &
+Description:
+ Same as <option>PluginsLogin</option>, but for the shutdown dialog.
+
+Key: PluginOptions
+Type: list
+Default: ""
+User: greeter
+Instance: #*/"SomeKey=randomvalue,Foo=bar"
+Comment:
+ A list of options of the form Key=Value. The conversation plugins can query
+ these settings; it is up to them what possible keys are.
+Description:
+ A list of options of the form
+ <replaceable>Key</replaceable><literal>=</literal><replaceable>Value</replaceable>.
+ The conversation plugins can query these settings; it is up to them what
+ possible keys are.
+
+Key: AllowConsole
+Type: bool
+Default: true
+User: greeter(hasConsole)
+Instance: #*/!
+Comment: &
+Description:
+ Show the <guilabel>Console Login</guilabel> action in the greeter (if <option>ServerTTY</option>/<option>ConsoleTTYs</option>
+ is configured).
+
+Key: AllowClose
+Type: bool
+Default: true
+User: greeter
+Instance: :*/false
+Comment: &
+Description:
+ Show the <guilabel>Restart X Server</guilabel>/<guilabel>Close Connection</guilabel> action in the greeter.
+
+Key: Preloader
+Type: string
+Default: ""
+User: greeter-c
+Instance: */KDE_BINDIR "/preloadkde"
+Comment: &
+Description:
+ A program to run while the greeter is visible. It is supposed to preload
+ as much as possible of the session that is going to be started (most
+ probably).
+
+Key: UseTheme
+Type: bool
+Default: false
+User: greeter
+Instance: #*/true
+Comment: &
+Description:
+ Whether the greeter should be themed.
+
+Key: Theme
+Type: string
+Default: ""
+User: greeter
+Instance: */KDMDATA "/themes/circles"
+Comment: &
+Description:
+ The theme to use for the greeter. Can point to either a directory or an XML
+ file.
diff --git a/kdm/configure.in.bot b/kdm/configure.in.bot
new file mode 100644
index 000000000..838d29faa
--- /dev/null
+++ b/kdm/configure.in.bot
@@ -0,0 +1,8 @@
+if $kdm_no_Xau; then
+ AC_MSG_WARN([Cannot build KDM! Make sure that libXau.a is installed!])
+fi
+if $kdm_no_Xdmcp; then
+ AC_MSG_WARN([Cannot build KDM! Make sure that libXdmcp.a and Xdmcp.h
+are installed or use --without-xdmcp to disable XDMCP support!])
+fi
+
diff --git a/kdm/configure.in.in b/kdm/configure.in.in
new file mode 100644
index 000000000..4f3ff82d7
--- /dev/null
+++ b/kdm/configure.in.in
@@ -0,0 +1,243 @@
+KDE_FIND_PATH(xmkmf, XMKMF, [], [AC_MSG_ERROR([xmkmf/imake not found. Please make sure it's in PATH!])])
+
+dnl ask imake about various X settings
+AC_MSG_CHECKING([X paths])
+imkv=8
+test "$kde_cv_defines_imake_version" = $imkv || unset kde_cv_defines_imake
+AC_CACHE_VAL(kde_cv_defines_imake, [
+ rm -fr conftestdir
+ if mkdir conftestdir; then
+ cd conftestdir
+ cat > Imakefile <<'EOF'[
+
+acimake:
+ @echo "XBINDIR=\"$(BINDIR)\" XLIBDIR=\"$(LIBDIR)\""
+
+]EOF
+ if $XMKMF >&5 2>&1 && test -f Makefile; then
+ kde_cv_defines_imake=`${MAKE-make} acimake 2> /dev/null | grep -v "^make"`
+ kde_cv_defines_imake_version=$imkv
+ else
+ AC_MSG_RESULT([failed])
+ AC_MSG_ERROR([$XMKMF (imake) failed.
+Make sure you have all necessary X development packages installed.
+On some systems a missing /lib/cpp symlink is at fault.])
+ fi
+ cd ..
+ rm -fr conftestdir
+ else
+ AC_MSG_RESULT([failed])
+ AC_MSG_ERROR([cannot create temporary directory])
+ fi
+])
+AC_MSG_RESULT([done])
+eval "$kde_cv_defines_imake"
+AC_DEFINE_UNQUOTED(XBINDIR, "$XBINDIR", [X binaries directory])
+AC_DEFINE_UNQUOTED(XLIBDIR, "$XLIBDIR", [X libraries directory])
+
+if test -f /etc/ttys; then
+ AC_DEFINE(BSD_INIT, 1, [Define if the system uses a BSD-style init])
+fi
+
+AC_CHECK_FUNCS([getttyent])
+case $host_os in
+ linux*) ac_cv_func_getutxent=no;;
+ darwin*) ac_cv_func_getutxent=no;;
+ kfreebsd*-gnu) ac_cv_func_getutxent=no;;
+ *) AC_CHECK_FUNC([getutxent]);;
+esac
+if test $ac_cv_func_getutxent = yes; then
+ AC_DEFINE(HAVE_UTMPX, 1, [Define if the system uses extended utmp])
+else
+ AC_CHECK_FUNC([getutent], ,
+ [AC_DEFINE(BSD_UTMP, 1, [Define if the system has no getutent])])
+fi
+
+AC_CHECK_MEMBERS([struct utmp.ut_user], , , [#include <utmp.h>])
+AC_CHECK_MEMBERS([struct passwd.pw_expire], , , [#include <pwd.h>])
+AC_CHECK_MEMBERS([struct sockaddr_in.sin_len], , , [
+#include <sys/socket.h>
+#include <netinet/in.h>
+])
+
+ac_save_libs=$LIBS
+LIBS="$LIBS $LIBUTIL"
+AC_CHECK_FUNCS([setlogin setusercontext getusershell login_getclass auth_timeok])
+LIBS=$ac_save_libs
+
+dnl is getifaddrs always available without additional libs?
+AC_CHECK_FUNCS([mkstemp setproctitle sysinfo strnlen getifaddrs])
+
+AC_CHECK_FUNCS([arc4random], ,
+ [
+dnl assume that /dev/random is non-blocking if /dev/urandom does not exist
+for i in urandom random; do
+ if test -c /dev/$i; then
+ AC_DEFINE_UNQUOTED(DEV_RANDOM, "/dev/$i", [Define the system's entropy device])
+ break
+ fi
+done
+ ])
+
+AC_CHECK_FUNC(vsyslog, [
+ AC_DEFINE(USE_SYSLOG, 1, [Define if kdm should be built with syslog support])])
+
+kdm_no_Xau=false
+kdm_no_Xdmcp=false
+
+AC_CHECK_LIB(Xau, main, [:],
+ [
+ kdm_no_Xau=true
+ DO_NOT_COMPILE="$DO_NOT_COMPILE kdm"
+ ],
+ $X_LDFLAGS -lX11 $LIBSOCKET)
+
+AC_ARG_WITH(xdmcp,
+ AC_HELP_STRING([--without-xdmcp],[build kdm without xdmcp support [default=with xdmcp]]), ,
+ [with_xdmcp=yes])
+if test "x$with_xdmcp" = xyes; then
+ AC_CHECK_LIB(Xdmcp, main, [LIBXDMCP="-lXdmcp"], , $X_LDFLAGS -lX11 $LIBSOCKET)
+ if test -n "$LIBXDMCP"; then
+ cppflags_safe=$CPPFLAGS
+ CPPFLAGS="$CPPFLAGS $X_INCLUDES"
+ AC_CHECK_HEADER(X11/Xdmcp.h, [HAVE_X11_XDMCP_H=1], , [#include <X11/Xmd.h>])
+ CPPFLAGS=$cppflags_safe
+ fi
+ if test -z "$HAVE_X11_XDMCP_H"; then
+ kdm_no_Xdmcp=true
+ DO_NOT_COMPILE="$DO_NOT_COMPILE kdm"
+ fi
+ AC_DEFINE(XDMCP, 1, [Define if kdm should be built with XDMCP support])
+ ac_save_libs=$LIBS
+ LIBS="$LIBS $LIBXDMCP"
+ AC_CHECK_FUNC(XdmcpWrap, [
+ AC_DEFINE(HASXDMAUTH, 1, [Define if kdm should be built with XDMAUTH support])
+ ])
+ LIBS=$ac_save_libs
+fi
+AC_SUBST(LIBXDMCP)
+
+KRB4_INCS=
+KRB4_LIBS=
+KRB4_RPATH=
+
+AC_MSG_CHECKING(whether to use Kerberos v4)
+AC_ARG_WITH(krb4,
+AC_HELP_STRING([--with-krb4=PATH],[Compile in Kerberos v4 support]),
+[ test "x$with_krb4" = xyes && with_krb4=/usr/kerberos ],
+[ with_krb4=no ]
+)
+case "$with_krb4" in
+no)
+ AC_MSG_RESULT(no)
+ ;;
+*)
+ AC_MSG_RESULT(yes)
+ AC_DEFINE_UNQUOTED(KRB4, 1, [define if you have Kerberos IV])
+ KRB4_INCS="-I$with_krb4/include"
+ KRB4_LIBS="-L$with_krb4/lib -lkrb -ldes"
+ if test "$USE_RPATH" = "yes" ; then
+ KRB4_RPATH="-R $with_krb4/lib"
+ fi
+ AC_CHECK_LIB(resolv, dn_expand, KRB4_LIBS="$KRB4_LIBS -lresolv")
+ ;;
+esac
+
+AC_MSG_CHECKING(whether to use AFS)
+AC_ARG_WITH(afs,
+ AC_HELP_STRING([--with-afs],[Compile in AFS support (requires KTH krb4)]), ,
+ [ with_afs=no ])
+if test "$with_afs" = no; then
+ AC_MSG_RESULT(no)
+else
+ if test "$with_krb4" = no; then
+ AC_MSG_RESULT(no)
+ AC_MSG_WARN("AFS requires Kerberos v4 support.")
+ with_afs=no
+ else
+ AC_MSG_RESULT(yes)
+ AC_DEFINE_UNQUOTED(AFS, 1, [define if you have KTH Kerberos IV and AFS])
+ KRB4_LIBS="$KRB4_LIBS -lkafs"
+ if test -n "$os_aix"; then
+ KRB4_LIBS="$KRB4_LIBS -lld"
+ fi
+ fi
+fi
+
+AC_SUBST(KRB4_INCS)
+AC_SUBST(KRB4_LIBS)
+AC_SUBST(KRB4_RPATH)
+
+AC_CHECK_LIB(s, main, [LIB_LIBS="-ls"]) dnl for AIX
+AC_SUBST(LIB_LIBS)
+
+AC_CHECK_LIB(posix4, sched_yield, [LIBPOSIX4=-lposix4])
+AC_SUBST(LIBPOSIX4)
+
+KRB5_INCS=
+KRB5_LIBS=
+KRB5_RPATH=
+
+AC_MSG_CHECKING([whether to use Kerberos5 for Xauth cookies in kdm])
+AC_ARG_WITH(krb5auth,
+ AC_HELP_STRING([--with-krb5auth=PATH],[Use Kerberos5 for Xauth cookies in kdm]), ,
+ [ with_krb5auth=no ])
+if test "x$with_krb5auth" = xno; then
+ AC_MSG_RESULT(no)
+else
+ AC_MSG_RESULT(yes)
+ if test "x$with_krb5auth" != xyes; then
+ KRB5_INCS="-I$with_krb5auth/include"
+ KRB5_LIBS="-L$with_krb5auth/lib"
+ if test "$USE_RPATH" = "yes" ; then
+ KRB5_RPATH="-R $with_krb5auth/lib"
+ fi
+ fi
+ KRB5_LIBS="$KRB5_LIBS -lkrb5" dnl -lk5crypto -lcom_err -lresolv
+ keepcflags=$CFLAGS
+ CFLAGS="$KRB5_INCS $CFLAGS"
+ AC_CHECK_HEADER(krb5/krb5.h,
+ [ AC_DEFINE(K5AUTH, 1, [Define if kdm should use Kerberos 5 for Xauth cookies.]) ],
+ [ AC_MSG_ERROR([--with-krb5auth requires Kerberos5 header files.
+Due to a problem with X includes you probably have to run "ln -s . krb5"
+in the directory where the krb5.h include resides to make things actually work.])])
+ CFLAGS="$keepcflags"
+fi
+
+AC_SUBST(KRB5_INCS)
+AC_SUBST(KRB5_LIBS)
+AC_SUBST(KRB5_RPATH)
+
+AC_MSG_CHECKING([whether to use Sun's secure RPC for Xauth cookies in kdm])
+AC_ARG_WITH(rpcauth,
+ AC_HELP_STRING([--with-rpcauth],[Use Sun's secure RPC for Xauth cookies in kdm.]), ,
+ [ with_rpcauth=no ])
+if test "x$with_rpcauth" = xno; then
+ AC_MSG_RESULT(no)
+else
+ AC_MSG_RESULT(yes)
+ AC_CHECK_HEADER(rpc/rpc.h,
+ [ AC_DEFINE(SECURE_RPC, 1, [Define if kdm should use Sun's secure RPC for Xauth cookies.]) ],
+ [ AC_MSG_ERROR([--with-rpcauth requires Sun RPC header files.])])
+fi
+
+if test "x$use_pam" = xyes; then
+ AC_DEFINE(USE_PAM, 1, [Define if kdm should use PAM])
+elif test "x$use_shadow" = xyes; then
+ AC_DEFINE(USESHADOW, 1, [Define if kdm should use shadow passwords])
+fi
+if test "x$with_krb4" != xno; then
+ AC_DEFINE(KERBEROS, 1, [Define if kdm should use Kerberos IV])
+ if test "x$with_afs" = xno; then
+ AC_DEFINE(NO_AFS, 1, [Define if kdm should not use AFS])
+ fi
+fi
+
+AC_ARG_WITH(kdm-xconsole,
+ AC_HELP_STRING([--with-kdm-xconsole],[build kdm with built-in xconsole [default=no]]), ,
+ [with_kdm_xconsole=no])
+if test "x$with_kdm_xconsole" = xyes; then
+ AC_DEFINE(WITH_KDM_XCONSOLE, 1, [Build kdm with built-in xconsole])
+fi
+
+dnl AC_OUTPUT(kdm/kfrontend/sessions/kde.desktop)
diff --git a/kdm/confproc.pl b/kdm/confproc.pl
new file mode 100755
index 000000000..88be28847
--- /dev/null
+++ b/kdm/confproc.pl
@@ -0,0 +1,838 @@
+#! /usr/bin/perl -w
+#
+# Copyright 2004-2005 Oswald Buddenhagen <ossi@kde.org>
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation.
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+#
+# Except as contained in this notice, the name of a copyright holders shall
+# not be used in advertising or otherwise to promote the sale, use or
+# other dealings in this Software without prior written authorization
+# from the copyright holders.
+#
+
+use strict;
+use Cwd 'abs_path';
+
+sub pegout($)
+{
+ print STDERR $_[0]."\n";
+ exit 1;
+}
+
+sub relpath($$)
+{
+ my @src = split(/\//, abs_path(shift));
+ my @dst = split(/\//, abs_path(shift));
+ pop @dst;
+ while (@src && @dst && $src[0] eq $dst[0]) {
+ shift @src;
+ shift @dst;
+ }
+ return "../"x@dst . join("/", @src);
+}
+
+sub getl()
+{
+ while (<INFILE>) {
+ next if (/^#/);
+ chop;
+# print "read: ".$_."\n";
+ return;
+ }
+ $_ = "";
+}
+
+sub dedb($)
+{
+ my $t = shift;
+ $t =~ s,</?(command|guilabel|quote)>,\",g;
+ $t =~ s,</?(acronym|envar|filename|option|systemitem( [^ >]+)?)>,,g;
+ $t =~ s,<emphasis>([^<]+)</emphasis>,uc($1),ge;
+ $t =~ s,&nbsp;, ,g;
+ $t =~ s,&lt;,<,g;
+ $t =~ s,&gt;,>,g;
+ $t =~ s,&kdm;,KDM,g;
+ $t =~ s,&XDMCP;,XDMCP,g;
+ $t =~ s,&X-Server;,X-server,g;
+ return $t;
+}
+
+sub mkvname($)
+{
+ my $v = shift;
+ if ($v !~ /^[A-Z]{2}/) {
+ $v = lcfirst $v;
+ }
+ return $v;
+}
+
+sub emit_conds($)
+{
+ my $ret = "";
+ for my $c (keys %{$_[0]}) {
+ my ($then, $else) = ("", "");
+ for my $d (@{${$_[0]}{$c}}) {
+ my $bas = "# define ".$d->[0];
+ if ($d->[1] =~ /\n/) {
+ $then .= $bas." \\\n".$d->[1]."\n";
+ } else {
+ $then .= $bas." ".$d->[1]."\n";
+ }
+ $else .= $bas."\n";
+ }
+ $ret .= "#if ".$c."\n".$then."#else\n".$else."#endif\n\n";
+ }
+ return $ret;
+}
+
+sub add_cond($$$$)
+{
+ if ($_[0]) {
+ my $vn = uc($_[1]);
+ for my $i (@{$_[2]}) {
+ push @{${$_[3]}{$_[0]}}, [ $vn."_".$i->[1], $i->[0] ];
+ $i->[0] = $vn."_".$i->[1];
+ }
+ }
+}
+
+my $do_doc = 0;
+if ($ARGV[0] eq "--doc") {
+ $do_doc = 1;
+ shift @ARGV;
+}
+
+@ARGV != 2 && pegout("usage: $0 [--doc] <def-file> <out-file>");
+
+open (INFILE, $ARGV[0]) || pegout("$0: cannot open definition file ".$ARGV[0]);
+
+my %ex_conds = ();
+my %ex_sects = (); # $name -> $index
+my @ex_config = (); # ($name, $comment, $entries)
+
+my $raw_out = "";
+
+my %ov_enum_conds = ();
+my $ov_enums = "";
+my $ov_enum_defs = "";
+
+my $ov_defaults = "";
+
+my %arr_ov_vars = ();
+my $ov_rd_sects = "";
+my $ov_rd_ents = "";
+
+my $ov_gen_sects = "";
+my $ov_gen_ents = "";
+my $max_prio = 0;
+
+my %ov_sec_conds = ();
+my %ov_ent_conds = ();
+my $ov_sect_defs = "";
+my $ov_sect_refs = "";
+
+my %ov_glob_conds = ();
+my $ov_globs = "";
+my %ov_loc_conds = ();
+my $ov_locs = "";
+
+my %ov_glob_decl_conds = ();
+my $ov_glob_decls = "";
+my %ov_glob_def_conds = ();
+my %ov_glob_defs = ();
+my %ov_loc_def_conds = ();
+my %ov_loc_defs = ();
+
+my %ov_greet_conds = ();
+my $ov_greet_init = "";
+my @ov_greet_decls = ();
+my %ov_greet_defs = ();
+
+my %ov_xm_conds = ();
+my @ov_xm = ("", "");
+
+my %ov_km_conds = ();
+my %ov_km = ();
+
+my %sect_names = ();
+my $sect = "";
+my $sect_if;
+
+my $key_if;
+
+my %key_names;
+
+my $kid_seq = 0x1000;
+
+my $doc = "";
+my $doc_ref = "";
+
+sub emit_section()
+{
+ my $ts = $sect;
+ $ts =~ s/-/_/;
+ my @oa = (
+ ["static Ent ents".$ts."[] = { \\\n".$ov_rd_ents."};", "ENTS"],
+ ["static Ent ents".$ts."[] = { \\\n".$ov_gen_ents."};", "GENS"],
+ ["sec".$ts." = { \"".$sect."\", ents".$ts.", as(ents".$ts.") },", "SEC"],
+ ["&sec".$ts.",", "SECS"]
+ );
+ $ov_rd_ents = "";
+ $ov_gen_ents = "";
+ add_cond($sect_if, $ts, \@oa, \%ov_sec_conds);
+ $ov_rd_sects .= " \\\n".$oa[0][0]." \\\n";
+ $ov_gen_sects .= " \\\n".$oa[1][0]." \\\n";
+ $ov_sect_defs .= " ".$oa[2][0]." \\\n";
+ $ov_sect_refs .= "\t".$oa[3][0]." \\\n";
+ $doc_ref .= "</variablelist>\n</sect2>\n\n";
+}
+
+my %th = (
+ "int" => [ "C_TYPE_INT", "", "int\t", "", "GetCfgInt", "" ],
+ "bool" => [ "C_TYPE_INT", " | C_BOOL", "int\t", "bool\t", "GetCfgInt", "GetCfgInt" ],
+ "enum" => [ "C_TYPE_INT", " | C_ENUM", "int\t", "", "GetCfgInt", "" ],
+ "group" => [ "C_TYPE_INT", " | C_GRP", "int\t", "", "GetCfgInt", "" ],
+ "string" => [ "C_TYPE_STR", "", "char\t*", "QString\t", "GetCfgStr", "GetCfgQStr" ],
+ "path" => [ "C_TYPE_STR", " | C_PATH", "char\t*", "QString\t", "GetCfgStr", "GetCfgQStr" ],
+ "list" => [ "C_TYPE_ARGV", "", "char\t**", "QStringList\t", "GetCfgStrArr", "GetCfgQStrList" ]
+);
+
+my @tl = ("QFont\t", "QStringList\t", "QString\t", "char\t**", "char\t*", "int\t", "bool\t");
+
+sub init_defs($)
+{
+ for my $t (@tl) {
+ $_[0]{$t} = "";
+ }
+}
+
+init_defs(\%ov_glob_defs);
+init_defs(\%ov_loc_defs);
+init_defs(\%ov_greet_defs);
+
+sub emit_defs($)
+{
+ my $ret = "";
+ for my $t (@tl) {
+ $ret .= $_[0]{$t};
+ }
+ return $ret;
+}
+
+while (<INFILE>) {
+ chop;
+ if (/^<code>$/) {
+ while (<INFILE>) {
+ last if (/^<\/code>\n$/);
+ $raw_out .= $_;
+ }
+ } elsif (/^<kdmrc>$/) {
+ my $comm = "";
+ while (<INFILE>) {
+ last if (/^<\/kdmrc>\n$/);
+ chop;
+ if (/^\[(.*)\]$/) {
+ defined($ex_sects{$1}) &&
+ pegout("redefinition of example section [$1]");
+ push @ex_config, [$1, dedb($comm), "", ""];
+ $ex_sects{$1} = $#ex_config;
+ $comm = "";
+ } else {
+ if (!$_) {
+ $comm .= " \\\n\"\\n\"";
+ } elsif ($_ eq " _") {
+ $comm .= " \\\n\"#\\n\"";
+ } else {
+ s/"/\\"/g;
+ $comm .= " \\\n\"#".$_."\\n\"";
+ }
+ }
+ }
+ } elsif (/^<docu>$/) {
+ while (<INFILE>) {
+ last if (/^<\/docu>\n$/);
+ $doc .= $_;
+ }
+ } elsif (/^<legacy>$/) {
+ while (<INFILE>) {
+ next if (/^($|#)/);
+ my ($proc, $kif);
+ if (/^If: (.+)$/) {
+ $kif = $1;
+ getl();
+ } else {
+ $kif = "";
+ }
+ if (/^Proc: (.+)$/) {
+ $proc = $1;
+ getl();
+ } else {
+ pegout("expecting Proc keyword in legacy section");
+ }
+ my $nsrc = 0;
+ my $mcnt = 0;
+ while (/^Source: (.+)$/) {
+ my $src = $1;
+ if ($src =~ /^xdm:(.*)$/) {
+ my $what = $1;
+ my $dsp = ($what =~ s/^\*\.//);
+ my @oa = ([ "{ \"".$what."\", (char *)-1, 0, ".$proc." },", "XMO" ]);
+ add_cond($kif, $what, \@oa, \%ov_xm_conds);
+ $ov_xm[$dsp] .= $oa[0][0]." \\\n";
+ } elsif ($src =~ /^kdm:(.*)\/(.*)$/) {
+ my ($sec, $key) = ($1, $2);
+ my @oa = ([ "{ \"".$key."\", (char *)-1, 0, ".$proc." },", "KMO".($mcnt++) ]);
+ add_cond($kif, $key, \@oa, \%ov_km_conds);
+ $ov_km{$sec} .= $oa[0][0]." \\\n";
+ } else {
+ pegout("invalid legacy option '$_'");
+ }
+ $nsrc++;
+ getl();
+ }
+ $nsrc || pegout("no sources for legacy processor ".$proc);
+ last if (/^<\/legacy>$/);
+ pegout("unidentified section body '".$_."' in legacy section") if ($_);
+ }
+ } else {
+ next if (/^($|#)/);
+ if (/^Key: (.+)$/) {
+ my $key = $1;
+ $sect || pegout("defining key ".$key." outside any section");
+ defined($key_names{$key}) &&
+ pegout("redefinition of key ".$key." in section [".$sect."]");
+ $key_names{$key} = "";
+ getl();
+ if (/^If: (.+)$/) {
+ $key_if = $1;
+ getl();
+ } else {
+ $key_if = "";
+ }
+ my $kif = $sect_if ?
+ ($key_if ? "(".$sect_if.") && (".$key_if.")" : $sect_if) : $key_if;
+ my ($e_comm, $e_desc) = ("", "");
+ my $type = "";
+ if (/^Type: (.+)$/) {
+ $type = $1;
+ if ($type eq "enum") {
+ my $enum = "static const char *e".$key."[] = { ";
+ my $n_e_def = 0;
+ while (getl(), /^ ([A-Za-z]+)(\/([A-Z_]+))?: (.+)$/) {
+ my $e_nam = $1;
+ $enum .= "\"".$e_nam."\", ";
+ defined($3) &&
+ ($ov_enum_defs .= "#define ".$3." ".($n_e_def++)."\n");
+ my ($comm, $desc) = (dedb($4), $4);
+ $comm =~ s/\"/\\\"/g;
+ $e_comm .= " \\\n\"# \\\"".$e_nam."\\\" - ".$comm."\\n\"";
+ $e_desc .=
+ "<varlistentry>\n".
+ "<term><parameter>".$e_nam."</parameter></term>\n".
+ "<listitem><para>".$desc."</para></listitem>\n".
+ "</varlistentry>\n";
+ }
+ $enum .= "0 };";
+ my @oa = ( [ $enum, "ENUM" ] );
+ add_cond($kif, $key, \@oa, \%ov_enum_conds);
+ $ov_enums .= $oa[0][0]." \\\n";
+ $n_e_def && ($ov_enum_defs .= "\n");
+ } elsif ($type =~ /^(int|bool|group|string|path|list)$/) {
+ getl();
+ } else {
+ pegout("unknown Type ".$type." for key ".$key." in section [".$sect."]");
+ }
+ } else {
+ pegout("expecting Type for key ".$key." in section [".$sect."]");
+ }
+ my ($odflt, $dflt, $cdflt, $ddflt);
+ my $quot = ($type =~ /^(int|bool|enum|group)$/);
+ if (/^Default: (\*?)(.+)$/) {
+ my $defd = $1;
+ ($odflt, $dflt) = ($2, $2);
+ $quot && ($dflt = "\"".$dflt."\"");
+ $defd && ($ov_defaults .= "#define def_".$key." ".$dflt."\n");
+ getl();
+ } else {
+ pegout("expecting Default for key ".$key." in section [".$sect."]");
+ }
+ if (/^CDefault: (.+)$/) {
+ if ($1 eq "-") {
+ $ddflt =
+ $cdflt = "";
+ } else {
+ $ddflt =
+ $cdflt = $1;
+ $cdflt =~ s/"/\\"/g;
+ $cdflt = " \\\n\"# Default is ".$cdflt."\\n\"";
+ }
+ getl();
+ } else {
+ $ddflt = $odflt;
+ if ($quot) {
+ $cdflt = " \\\n\"# Default is ".$odflt."\\n\"";
+ } else {
+ $cdflt = " \\\n\"# Default is \\\"\" ".$dflt." \"\\\"\\n\"";
+ }
+ }
+ if (/^DDefault: -$/) {
+ $ddflt = "";
+ getl();
+ }
+ my $pproc;
+ if (/^PostProc: (.+)$/) {
+ $pproc = $1;
+ getl();
+ } else {
+ $pproc = "";
+ }
+ my $nusers = 0;
+ my ($vname, $kid, $xkid, $ctype, $cpptype, $cget, $cppget);
+ while (/^User: (.+)$/) {
+ my $user = $1;
+ if ($user eq "dummy") {
+ $vname = "dummy";
+ $kid = "C_INTERNAL | C_TYPE_STR";
+ $xkid = "";
+ } elsif ($user =~ s/^(core|greeter|greeter-c|dep|config)(\((.+)\))?(:font)?$/$1/) {
+ my ($hvn, $isfn) = (defined($3) ? $3 : mkvname($key), $4);
+ ($kid, $xkid, $ctype, $cpptype, $cget, $cppget) = @{$th{$type}};
+ $kid = sprintf "%#x | %s", $kid_seq, $kid;
+ if ($user eq "dep") {
+ $vname = $hvn;
+ $xkid .= " | C_INTERNAL";
+ } elsif ($user eq "config") {
+ $vname = $hvn;
+ $xkid .= " | C_INTERNAL | C_CONFIG";
+ } else {
+ $vname = "";
+ if ($user eq "core") {
+ if ($sect =~ /^-/) {
+ my @oa = (
+ [ "{ ".$kid.", boffset(".$hvn.") },", "LOC" ],
+ [ $ctype.$hvn.";", "LDEF" ]
+ );
+ add_cond($kif, $hvn, \@oa, \%ov_loc_conds);
+ $ov_locs .= " \\\n".$oa[0][0];
+ $ov_loc_defs{$ctype} .= " \\\n\t".$oa[1][0];
+ } else {
+ my @oa = (
+ [ "{ ".$kid.", (char **) &".$hvn." },", "GLOB" ],
+ [ $ctype.$hvn.";", "GDEF" ],
+ [ "extern ".$ctype.$hvn.";", "GDECL" ]
+ );
+ add_cond($kif, $hvn, \@oa, \%ov_glob_conds);
+ $ov_globs .= " \\\n".$oa[0][0];
+ $ov_glob_defs{$ctype} .= " \\\n".$oa[1][0];
+ $ov_glob_decls .= " \\\n".$oa[2][0];
+ }
+ } else { # greeter(-c)?
+ my ($typ, $gtr, $isc);
+ if ($isfn) {
+ $typ = "QFont\t";
+ $gtr = "Str2Font( GetCfgQStr( ".$kid." ) )";
+ $isc = 0;
+ } elsif ($user eq "greeter" && $cppget) {
+ $typ = $cpptype;
+ $gtr = $cppget."( ".$kid." )";
+ $isc = 0;
+ } else {
+ $typ = $ctype;
+ $gtr = $cget."( ".$kid.(($type eq "list") ? ", 0" : "")." )";
+ $isc = 1;
+ }
+ my @oa = (
+ [ "_".$hvn." = ".$gtr.";", "GRINIT" ],
+ [ $typ."_".$hvn.";", "GRDEF" ],
+ [ "extern ".$typ."_".$hvn.";", "GRDECL" ]
+ );
+ add_cond($kif, $hvn, \@oa, \%ov_greet_conds);
+ $ov_greet_init .= " \\\n ".$oa[0][0];
+ $ov_greet_defs{$typ} .= " \\\n".$oa[1][0];
+ $ov_greet_decls[$isc] .= " \\\n".$oa[2][0];
+ }
+ }
+ } else {
+ pegout("unrecognized User '".$user."' for key ".$key." in section [".$sect."]");
+ }
+ $nusers++;
+ getl();
+ }
+ $nusers || pegout("expecting User for key ".$key." in section [".$sect."]");
+ my $ninsts = 0;
+ while (/^Instance: ?(.*)$/) {
+ my $inst = $1;
+ if ($inst ne "-") {
+ my $on = 1 - ($inst =~ s/^#//);
+ my $sec;
+ if ($sect =~ /^-/) {
+ ($inst =~ s/^([^\/]+)\///) || pegout("instance for key ".$key." in section [".$sect."] does not specify display");
+ $sec = "X-".$1.$sect;
+ } else {
+ $sec = $sect;
+ }
+ if ($type eq "bool" && $inst eq "!") {
+ $inst = ($dflt eq "\"true\"") ? "\"false\"" : "\"true\"";
+ } elsif (!$inst) {
+ $inst = $dflt;
+ } else {
+ $quot && ($inst = "\"".$inst."\"");
+ }
+ defined($ex_sects{$sec}) ||
+ pegout("instantiating key ".$key." in section [".$sect."] in undeclared section");
+ my @oa = ( [ "{ \"".$key."\",\t".$inst.", ".$on." },", "INST" ] );
+ add_cond($key_if, $key, \@oa, \%ex_conds);
+ $ex_config[$ex_sects{$sec}][2] .= $oa[0][0]." \\\n";
+ $ex_config[$ex_sects{$sec}][3] = $sect_if;
+ }
+ $ninsts++;
+ getl();
+ }
+ $ninsts ||
+ print STDERR "Warning: key ".$key." in section [".$sect."] not instanciated\n";
+ my ($update, $prio) = ("0", "");
+ if (/^Update: ([^\/]+)(\/(\d+))?$/) {
+ ($update, $prio) = ($1, $3);
+ getl();
+ }
+ if ($prio) {
+ ($max_prio < $prio) && ($max_prio = $prio);
+ } else {
+ $prio = 0;
+ }
+ my $mcnt = 0;
+ while (/^Merge: (.+)$/) {
+ my $merge = $1;
+ if ($merge =~ /^xdm(:([^\(]+))?(\((.+)\))?$/) {
+ my ($what, $proc) = ($2, $4);
+ my @oa = (
+ [ "{ \"".($what ? $what : lcfirst($key))."\", ".
+ "\"".(($sect =~ /^-/) ? "X-%s" : "").$sect."\", ".
+ ($what ? "\"".$key."\"" : "0").", ".
+ ($proc ? $proc : "0")." },", "XM" ]
+ );
+ add_cond($kif, $key, \@oa, \%ov_xm_conds);
+ $ov_xm[$sect =~ /^-/] .= $oa[0][0]." \\\n";
+ } elsif ($merge =~ /^kdm:([^\(]+)(\((.+)\))?$/) {
+ my ($where, $func) = ($1, $3);
+ my $sec = "";
+ ($where =~ s/^([^\/]+)\///) && ($sec = $1);
+ my @oa = (
+ [ "{ \"".($where ? $where : $key)."\", ".
+ ($sec ? "\"".$sect."\"" : "0").", ".
+ ($where ? "\"".$key."\"" : "0").", ".
+ ($func ? $func : "0")." },", "KM".($mcnt++) ]
+ );
+ add_cond($kif, $key, \@oa, \%ov_km_conds);
+ $ov_km{$sec ? $sec : $sect} .= $oa[0][0]." \\\n";
+ } else {
+ pegout("bogus Merge '".$merge."' for key ".$key." in section [".$sect."]");
+ }
+ getl();
+ }
+ # todo: handle $func here, too
+ my @oa = ( [ "{ \"".$key."\", 0, 0, 0 },", "KM" ] );
+ add_cond($kif, $key, \@oa, \%ov_km_conds);
+ $ov_km{$sect} .= $oa[0][0]." \\\n";
+ my $comm = "";
+ if (/^Comment:(( [-&])?)$/) {
+ if ($1 eq " &") {
+ $comm = "&";
+ getl();
+ } elsif ($1 ne " -") {
+ while (getl(), /^ (.*)$/) {
+ $comm .= $1."\n";
+ }
+ $comm ||
+ print STDERR "Warning: key ".$key." in section [".$sect."] has empty Comment\n";
+ } else {
+ getl();
+ }
+ } else {
+ print STDERR "Warning: key ".$key." in section [".$sect."] has no Comment\n";
+ }
+ if (/^Description:(( [-!])?)$/) {
+ if ($1 ne " -") {
+ $doc_ref .=
+ "<varlistentry>\n".
+ "<term id=\"option-".lc($key)."\"><option>".$key."</option></term>\n".
+ "<listitem>\n";
+ ($1 eq " !") &&
+ ($e_desc = "");
+ my $desc = "";
+ while (getl(), /^ (_|(.*))$/) {
+ $desc .= $2."\n";
+ }
+ $desc ||
+ print STDERR "Warning: key ".$key." in section [".$sect."] has empty Description\n";
+ ($comm eq "&") &&
+ ($comm = $desc);
+ $desc = "<para>\n".$desc."</para>\n";
+ if ($e_desc) {
+ $e_desc = "<variablelist>\n".$e_desc."</variablelist>\n";
+ ($desc =~ s/%ENUM%/$e_desc/) ||
+ ($desc .= $e_desc);
+ }
+ $doc_ref .= $desc;
+ if ($ddflt) {
+ if ($ddflt eq '""') {
+ $doc_ref .= "<para>Empty by default.</para>\n";
+ } else {
+ $ddflt =~ s/\"//g;
+ $ddflt =~ s,KDMCONF ,\${<envar>kde_confdir</envar>}/kdm,;
+ $ddflt =~ s,KDMDATA ,\${<envar>kde_datadir</envar>}/kdm,;
+ $ddflt =~ s,XBINDIR ,\${<envar>x_bindir</envar>},;
+ $doc_ref .= "<para>The default is <quote>".$ddflt."</quote>.</para>\n";
+ }
+ }
+ $doc_ref .= "</listitem>\n</varlistentry>\n\n";
+ } else {
+ getl();
+ }
+ } else {
+ print STDERR "Warning: key ".$key." in section [".$sect."] has no Description\n";
+ }
+ pegout("unidentified section body '".$_."' in section [".$sect."]") if ($_);
+ if ($vname) {
+ ($vname ne "dummy") &&
+ ($arr_ov_vars{$vname} = $kif);
+ $vname = "&V".$vname;
+ } elsif ($pproc) {
+ $vname = "(void *)".$pproc;
+ } elsif ($type eq "enum") {
+ $vname = "e".$key;
+ } else {
+ $vname = "0";
+ }
+ $comm = dedb($comm);
+ $comm =~ s/"/\\"/g;
+ $comm =~ s/([^\n]*)\n/ \\\n\"# $1\\n\"/g;
+ @oa = (
+ [ "{ \"".$key."\", ".$kid.$xkid.", ".$vname.", ".$dflt." },", "RENT" ],
+ [ "{ \"".$key."\", ".$prio.", ".$update.",".$comm.$e_comm.$cdflt." },", "GENT" ],
+ );
+ add_cond($key_if, $key, \@oa, \%ov_ent_conds);
+ $ov_rd_ents .= $oa[0][0]." \\\n";
+ $ov_gen_ents .= $oa[1][0]." \\\n";
+ $kid_seq++;
+ } elsif (/^Section: (.+)$/) {
+ emit_section() if ($sect);
+ $sect = $1;
+ defined($sect_names{$sect}) && pegout("redefinition of section [".$sect."]");
+ $sect_names{$sect} = "";
+ %key_names = ();
+ getl();
+ if (/^If: (.+)$/) {
+ $sect_if = $1;
+ getl();
+ } else {
+ $sect_if = "";
+ }
+ my ($sref, $stit, $sna);
+ if ($sect =~ /^-(.*)$/) {
+ $sref = lc($1);
+ $stit = "X-*-".$1;
+ $sna = "section class";
+ } else {
+ $sref = lc($sect);
+ $stit = $sect;
+ $sna = "section";
+ }
+ $doc_ref .=
+ "\n<sect2 id=\"kdmrc-".$sref."\">\n".
+ "<title>The [".$stit."] ".$sna." of &kdmrc;</title>\n\n";
+ if (/^Description:(( -)?)$/) {
+ if ($1 ne " -") {
+ my $desc = 0;
+ $doc_ref .= "<para>\n";
+ while (getl(), /^ (_|(.*))$/) {
+ $doc_ref .= $2."\n";
+ $desc = 1;
+ }
+ $doc_ref .= "</para>\n";
+ $desc ||
+ print STDERR "Warning: section [".$sect."] has empty Description\n";
+ } else {
+ getl();
+ }
+ } else {
+ print STDERR "Warning: section [".$sect."] has no Description\n";
+ }
+ $doc_ref .= "\n<variablelist>\n\n";
+ pegout("unidentified section body '".$_."' in section [".$sect."]") if ($_);
+ } else {
+ pegout("invalid section leadin: '".$_."'");
+ }
+ }
+}
+emit_section();
+close INFILE;
+
+my $srcf = relpath($ARGV[0], $ARGV[1]);
+my $exen = relpath($0, $ARGV[1]);
+
+open (OUTFILE, ">".$ARGV[1]) || pegout("$0: cannot create output file ".$ARGV[1]);
+
+if (!$do_doc) {
+
+print OUTFILE
+ "/* generated from $srcf by $exen - DO NOT EDIT! */\n\n".
+ "#ifndef CONFIG_DEFS\n".
+ "#define CONFIG_DEFS\n\n".
+ $raw_out."\n\n".
+ $ov_enum_defs."\n".
+ $ov_defaults."\n\n";
+
+my $ov_vars = "";
+my %ov_var_conds = ();
+for my $v (keys %arr_ov_vars) {
+ my @oa = ( ["V".$v.",", "VAR"] );
+ add_cond($arr_ov_vars{$v}, $v, \@oa, \%ov_var_conds);
+ $ov_vars .= " ".$oa[0][0]." \\\n";
+}
+print OUTFILE
+ emit_conds(\%ov_var_conds).
+ "#define CONF_READ_VARS \\\n \\\n".
+ "static Value \\\n".
+ $ov_vars.
+ " Vdummy;\n\n\n";
+
+print OUTFILE
+ emit_conds(\%ov_ent_conds).
+ emit_conds(\%ov_sec_conds).
+ "#define CONF_SECTS \\\n \\\n".
+ "static Sect \\\n".
+ $ov_sect_defs.
+ " *allSects[]\t= { \\\n".
+ $ov_sect_refs.
+ " };\n\n\n";
+
+print OUTFILE
+ emit_conds(\%ov_enum_conds).
+ "#define CONF_READ_ENTRIES \\\n \\\n".
+ $ov_enums.
+ $ov_rd_sects." \\\n".
+ "CONF_SECTS\n\n\n";
+
+print OUTFILE
+ "#define CONF_MAX_PRIO ".$max_prio."\n\n".
+ "#define CONF_GEN_ENTRIES \\\n".
+ $ov_gen_sects." \\\n".
+ "CONF_SECTS\n\n\n";
+
+print OUTFILE
+ emit_conds(\%ov_glob_conds).
+ "#define CONF_CORE_GLOBALS \\\n".
+ $ov_globs."\n\n\n".
+ "#define CONF_CORE_GLOBAL_DECLS \\\n".
+ $ov_glob_decls."\n\n\n".
+ "#define CONF_CORE_GLOBAL_DEFS \\\n".
+ emit_defs(\%ov_glob_defs)."\n\n\n";
+
+print OUTFILE
+ emit_conds(\%ov_loc_conds).
+ "#define CONF_CORE_LOCALS \\\n".
+ $ov_locs."\n\n\n".
+ "#define CONF_CORE_LOCAL_DEFS \\\n".
+ emit_defs(\%ov_loc_defs)."\n\n\n\n";
+
+print OUTFILE
+ emit_conds(\%ov_greet_conds).
+ "#define CONF_GREET_INIT \\\n".
+ $ov_greet_init."\n\n\n".
+ "#define CONF_GREET_C_DECLS \\\n".
+ $ov_greet_decls[1]."\n\n\n".
+ "#define CONF_GREET_CPP_DECLS \\\n".
+ $ov_greet_decls[0]."\n\n\n".
+ "#define CONF_GREET_DEFS \\\n".
+ emit_defs(\%ov_greet_defs)."\n\n\n";
+
+my ($ov1, $ov2) = ("", "");
+my %ex_sec_conds = ();
+for my $i (@ex_config) {
+ my $vn;
+ if ($i->[0] =~ /^X-(.+)-(.+)$/) {
+ if ($1 eq "*") {
+ $vn = "dEntsAny".$2;
+ } elsif ($1 eq ":*") {
+ $vn = "dEntsLocal".$2;
+ } else {
+ my ($t1, $t2) = ($1, $2);
+ $t1 =~ s/[-:]/_/g;
+ $vn = "dEnts".$t1.$t2;
+ }
+ } else {
+ $vn = "dEnts".$i->[0];
+ }
+ my @oa = (
+ [ "static DEnt ".$vn."[] = { \\\n".$i->[2]."};", "DSEC" ],
+ [ "{ \"".$i->[0]."\",\t".$vn.",\tas(".$vn."),".$i->[1]." },", "DSECS" ]
+ );
+ add_cond($i->[3], $vn, \@oa, \%ex_sec_conds);
+ $ov1 .= $oa[0][0]." \\\n \\\n";
+ $ov2 .= $oa[1][0]." \\\n";
+}
+print OUTFILE
+ emit_conds(\%ex_conds).
+ emit_conds(\%ex_sec_conds).
+ "#define CONF_GEN_EXAMPLE \\\n \\\n".
+ $ov1.
+ "static DSect dAllSects[] = { \\\n".
+ $ov2.
+ "};\n\n\n";
+
+print OUTFILE
+ emit_conds(\%ov_xm_conds).
+ "#define CONF_GEN_XMERGE \\\n \\\n".
+ "XResEnt globents[] = { \\\n".
+ $ov_xm[0].
+ "}, dpyents[] = { \\\n".
+ $ov_xm[1].
+ "};\n\n\n";
+
+my $ov_km_sects = "";
+my $ov_km_sect_refs = "";
+for my $s (keys %ov_km) {
+ my $ts = $s;
+ $ts =~ s/-/_/;
+ $ov_km_sects .=
+ "KUpdEnt upd".$ts."[] = { \\\n".
+ $ov_km{$s}.
+ "}; \\\n \\\n";
+ $ov_km_sect_refs .= "{ \"".$s."\", upd".$ts.", as(upd".$ts.") }, \\\n";
+}
+print OUTFILE
+ emit_conds(\%ov_km_conds).
+ "#define CONF_GEN_KMERGE \\\n \\\n".
+ $ov_km_sects.
+ "KUpdSec kupsects[] = { \\\n".
+ $ov_km_sect_refs.
+ "};\n";
+print OUTFILE
+ "\n#endif /* CONFIG_DEFS */\n";
+
+} else {
+
+$doc =~ s/%REF%/$doc_ref/;
+print OUTFILE
+ "<!-- generated from $srcf - DO NOT EDIT! -->\n\n".
+ $doc;
+
+}
+
+close OUTFILE;
diff --git a/kdm/kfrontend/Makefile.am b/kdm/kfrontend/Makefile.am
new file mode 100644
index 000000000..8f123509d
--- /dev/null
+++ b/kdm/kfrontend/Makefile.am
@@ -0,0 +1,66 @@
+# use 'make GENKDMCONF_FLAGS=... install' to override
+GENKDMCONF_FLAGS =
+
+SUBDIRS = themer themes pics sessions
+
+AM_CPPFLAGS = -I$(srcdir)/../backend -I.. -I$(top_srcdir)/kcontrol/background \
+ -I$(top_srcdir)/kdmlib $(all_includes)
+
+bin_PROGRAMS = kdm_config kdm_greet krootimage genkdmconf kdmctl
+
+kdm_config_SOURCES = kdm_config.c
+kdm_config_LDADD = $(LIBRESOLV) $(LIBSOCKET) $(LIBPOSIX4)
+
+kdm_greet_SOURCES = \
+ kdm_greet.c \
+ kdmconfig.cpp \
+ kdmclock.cpp \
+ kconsole.cpp \
+ kfdialog.cpp \
+ kgdialog.cpp \
+ kchooser.cpp \
+ kgverify.cpp \
+ kdmshutdown.cpp \
+ kgreeter.cpp \
+ kgapp.cpp
+kdm_greet_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+kdm_greet_LDADD = themer/libkdmthemer.a $(LIB_KDEUI) $(XTESTLIB) $(LIBPOSIX4)
+
+krootimage_SOURCES = krootimage.cpp
+krootimage_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+krootimage_LDADD = $(top_builddir)/kcontrol/background/libbgnd.la $(LIB_KIO)
+
+METASOURCES = AUTO
+
+genkdmconf_SOURCES = genkdmconf.c
+genkdmconf_LDFLAGS = $(X_LDFLAGS) $(X_RPATH)
+genkdmconf_LDADD = $(LIB_X11)
+
+kdmctl_SOURCES = kdmctl.c
+kdmctl_LDADD = $(LIBSOCKET)
+
+install-data-local: genkdmconf
+ ./genkdmconf --in $(DESTDIR)$(kde_confdir)/kdm --no-in-notice --face-src $(srcdir)/pics $(GENKDMCONF_FLAGS)
+
+messages:
+ $(XGETTEXT) `find . -name "*.cpp"` -o $(podir)/kdmgreet.pot
+
+noinst_HEADERS = \
+ kdm_greet.h \
+ kdmconfig.h \
+ kdmclock.h \
+ kconsole.h \
+ kfdialog.h \
+ kgdialog.h \
+ kchooser.h \
+ kgverify.h \
+ kdmshutdown.h \
+ kgreeter.h \
+ kgapp.h \
+ \
+ krootimage.h
+
+kdm_greet_COMPILE_FIRST = ../config.ci
+kdm_config_COMPILE_FIRST = ../config.ci
+genkdmconf_COMPILE_FIRST = ../config.ci
+
diff --git a/kdm/kfrontend/genkdmconf.c b/kdm/kfrontend/genkdmconf.c
new file mode 100644
index 000000000..5c0cb91e0
--- /dev/null
+++ b/kdm/kfrontend/genkdmconf.c
@@ -0,0 +1,2899 @@
+/*
+
+Create a suitable configuration for kdm taking old xdm/kdm
+installations into account
+
+Copyright (C) 2001-2005 Oswald Buddenhagen <ossi@kde.org>
+
+
+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.
+
+*/
+
+#include <X11/Xlib.h>
+#include <X11/Xresource.h>
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <utime.h>
+#include <dirent.h>
+#include <errno.h>
+#include <pwd.h>
+#include <time.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#ifdef BSD
+# include <utmp.h>
+#endif
+
+#include "config.ci"
+
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
+# define ATTR_UNUSED __attribute__((unused))
+#else
+# define ATTR_UNUSED
+#endif
+
+#if defined(__sun) && !defined(__sun__)
+# define __sun__
+#endif
+
+#define as(ar) ((int)(sizeof(ar)/sizeof(ar[0])))
+
+#define __stringify(x) #x
+#define stringify(x) __stringify(x)
+
+#define RCVERSTR stringify(RCVERMAJOR) "." stringify(RCVERMINOR)
+
+static int old_scripts, no_old_scripts, old_confs, no_old,
+ no_backup, no_in_notice, use_destdir, mixed_scripts;
+static const char *newdir = KDMCONF, *facesrc = KDMDATA "/pics/users",
+ *oldxdm, *oldkde;
+
+static int oldver;
+
+
+typedef struct StrList {
+ struct StrList *next;
+ const char *str;
+} StrList;
+
+
+static void *
+mmalloc( size_t sz )
+{
+ void *ptr;
+
+ if (!(ptr = malloc( sz ))) {
+ fprintf( stderr, "Out of memory\n" );
+ exit( 1 );
+ }
+ return ptr;
+}
+
+static void *
+mcalloc( size_t sz )
+{
+ void *ptr;
+
+ if (!(ptr = calloc( 1, sz ))) {
+ fprintf( stderr, "Out of memory\n" );
+ exit( 1 );
+ }
+ return ptr;
+}
+
+static void *
+mrealloc( void *optr, size_t sz )
+{
+ void *ptr;
+
+ if (!(ptr = realloc( optr, sz ))) {
+ fprintf( stderr, "Out of memory\n" );
+ exit( 1 );
+ }
+ return ptr;
+}
+
+static char *
+mstrdup( const char *optr )
+{
+ char *ptr;
+
+ if (!optr)
+ return 0;
+ if (!(ptr = strdup( optr ))) {
+ fprintf( stderr, "Out of memory\n" );
+ exit( 1 );
+ }
+ return ptr;
+}
+
+
+#define NO_LOGGER
+#define STATIC static
+#include <printf.c>
+
+typedef struct {
+ char *buf;
+ int clen, blen, tlen;
+} OCABuf;
+
+static void
+OutCh_OCA( void *bp, char c )
+{
+ OCABuf *ocabp = (OCABuf *)bp;
+
+ ocabp->tlen++;
+ if (ocabp->clen >= ocabp->blen) {
+ ocabp->blen = ocabp->blen * 3 / 2 + 100;
+ ocabp->buf = mrealloc( ocabp->buf, ocabp->blen );
+ }
+ ocabp->buf[ocabp->clen++] = c;
+}
+
+static int
+VASPrintf( char **strp, const char *fmt, va_list args )
+{
+ OCABuf ocab = { 0, 0, 0, -1 };
+
+ DoPr( OutCh_OCA, &ocab, fmt, args );
+ OutCh_OCA( &ocab, 0 );
+ *strp = realloc( ocab.buf, ocab.clen );
+ if (!*strp)
+ *strp = ocab.buf;
+ return ocab.tlen;
+}
+
+static int
+ASPrintf( char **strp, const char *fmt, ... )
+{
+ va_list args;
+ int len;
+
+ va_start( args, fmt );
+ len = VASPrintf( strp, fmt, args );
+ va_end( args );
+ return len;
+}
+
+static void
+StrCat( char **strp, const char *fmt, ... )
+{
+ char *str, *tstr;
+ va_list args;
+ int el;
+
+ va_start( args, fmt );
+ el = VASPrintf( &str, fmt, args );
+ va_end( args );
+ if (*strp) {
+ int ol = strlen( *strp );
+ tstr = mmalloc( el + ol + 1 );
+ memcpy( tstr, *strp, ol );
+ memcpy( tstr + ol, str, el + 1 );
+ free( *strp );
+ free( str );
+ *strp = tstr;
+ } else
+ *strp = str;
+}
+
+
+#define WANT_CLOSE 1
+
+typedef struct File {
+ char *buf, *eof, *cur;
+#if defined(HAVE_MMAP) && defined(WANT_CLOSE)
+ int ismapped;
+#endif
+} File;
+
+static int
+readFile( File *file, const char *fn )
+{
+ off_t flen;
+ int fd;
+
+ if ((fd = open( fn, O_RDONLY )) < 0)
+ return 0;
+
+ flen = lseek( fd, 0, SEEK_END );
+#ifdef HAVE_MMAP
+# ifdef WANT_CLOSE
+ file->ismapped = 0;
+# endif
+ file->buf = mmap( 0, flen + 1, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0 );
+# ifdef WANT_CLOSE
+ if (file->buf)
+ file->ismapped = 1;
+ else
+# else
+ if (!file->buf)
+# endif
+#endif
+ {
+ file->buf = mmalloc( flen + 1 );
+ lseek( fd, 0, SEEK_SET );
+ if (read( fd, file->buf, flen ) != flen) {
+ free( file->buf );
+ close( fd );
+ fprintf( stderr, "Cannot read file\n" );
+ return 0; /* maybe better abort? */
+ }
+ }
+ file->eof = file->buf + flen;
+ close( fd );
+ return 1;
+}
+
+#ifdef WANT_CLOSE
+static void
+freeBuf( File *file )
+{
+# ifdef HAVE_MMAP
+ if (file->ismapped)
+ munmap( file->buf, file->eof - file->buf );
+ else
+# endif
+ free( file->buf );
+}
+#endif
+
+static int
+isTrue( const char *val )
+{
+ return !strcmp( val, "true" ) ||
+ !strcmp( val, "yes" ) ||
+ !strcmp( val, "on" ) ||
+ atoi( val );
+}
+
+static int
+mkdirp( const char *name, int mode, const char *what, int existok )
+{
+ char *mfname = mstrdup( name );
+ int i;
+ struct stat st;
+
+ for (i = 1; mfname[i]; i++)
+ if (mfname[i] == '/') {
+ mfname[i] = 0;
+ if (stat( mfname, &st )) {
+ if (mkdir( mfname, 0755 )) {
+ fprintf( stderr, "Cannot create parent %s of %s directory %s: %s\n",
+ mfname, what, name, strerror( errno ) );
+ free( mfname );
+ return 0;
+ }
+ chmod( mfname, 0755 );
+ }
+ mfname[i] = '/';
+ }
+ free( mfname );
+ if (stat( name, &st )) {
+ if (mkdir( name, mode )) {
+ fprintf( stderr, "Cannot create %s directory %s: %s\n",
+ what, name, strerror( errno ) );
+ return 0;
+ }
+ chmod( name, mode );
+ return 1;
+ }
+ return existok;
+}
+
+
+static void
+displace( const char *fn )
+{
+ if (!no_backup) {
+ char bn[PATH_MAX + 4];
+ sprintf( bn, "%s.bak", fn ); /* won't overflow if only existing paths are passed */
+ rename( fn, bn );
+ } else
+ unlink( fn );
+}
+
+
+static char *
+locate( const char *exe )
+{
+ int len;
+ char *path, *pathe, *name, *thenam, nambuf[PATH_MAX+1];
+
+ if (!(path = getenv( "PATH" )))
+ return 0;
+ len = strlen( exe );
+ name = nambuf + PATH_MAX - len;
+ memcpy( name, exe, len + 1 );
+ *--name = '/';
+ do {
+ if (!(pathe = strchr( path, ':' )))
+ pathe = path + strlen( path );
+ len = pathe - path;
+ if (len && !(len == 1 && *path == '.')) {
+ thenam = name - len;
+ if (thenam >= nambuf) {
+ memcpy( thenam, path, len );
+ if (!access( thenam, X_OK ))
+ return mstrdup( thenam );
+ }
+ }
+ path = pathe;
+ } while (*path++ != '\0');
+ return 0;
+}
+
+
+/*
+ * target data to be written to kdmrc
+ */
+
+typedef struct Entry {
+ struct Entry *next;
+ struct Ent *spec;
+ const char *value;
+ int active:1;
+ int written:1;
+} Entry;
+
+typedef struct Section {
+ struct Section *next;
+ struct Sect *spec;
+ const char *name;
+ const char *comment;
+ Entry *ents;
+} Section;
+
+static Section *config; /* the kdmrc data to be written */
+
+/*
+ * Specification of the (currently possible) kdmrc entries
+ */
+
+typedef struct Ent {
+ const char *key;
+ int prio;
+ void (*func)( Entry *ce, Section *cs );
+ const char *comment;
+} Ent;
+
+typedef struct Sect {
+ const char *name;
+ Ent *ents;
+ int nents;
+} Sect;
+
+static Sect *findSect( const char *name );
+static Ent *findEnt( Sect *sect, const char *key );
+
+/*
+ * Functions to manipulate the current kdmrc data
+ */
+
+static const char *
+getfqval( const char *sect, const char *key, const char *defval )
+{
+ Section *cs;
+ Entry *ce;
+
+ for (cs = config; cs; cs = cs->next)
+ if (!strcmp( cs->name, sect )) {
+ for (ce = cs->ents; ce; ce = ce->next)
+ if (!strcmp( ce->spec->key, key )) {
+ if (ce->active && ce->written)
+ return ce->value;
+ break;
+ }
+ break;
+ }
+ return defval;
+}
+
+static void
+putfqval( const char *sect, const char *key, const char *value )
+{
+ Section *cs, **csp;
+ Entry *ce, **cep;
+
+ if (!value)
+ return;
+
+ for (csp = &config; (cs = *csp); csp = &(cs->next))
+ if (!strcmp( sect, cs->name ))
+ goto havesec;
+ cs = mcalloc( sizeof(*cs) );
+ ASPrintf( (char **)&cs->name, "%s", sect );
+ cs->spec = findSect( sect );
+ *csp = cs;
+ havesec:
+
+ for (cep = &(cs->ents); (ce = *cep); cep = &(ce->next))
+ if (!strcmp( key, ce->spec->key ))
+ goto haveent;
+ ce = mcalloc( sizeof(*ce) );
+ ce->spec = findEnt( cs->spec, key );
+ *cep = ce;
+ haveent:
+ ASPrintf( (char **)&ce->value, "%s", value );
+ ce->written = ce->active = 1;
+}
+
+static const char *csect;
+
+#define setsect(se) csect = se
+
+static void
+putval( const char *key, const char *value )
+{
+ putfqval( csect, key, value );
+}
+
+
+static void
+wrconf( FILE *f )
+{
+ Section *cs;
+ Entry *ce;
+ StrList *sl = 0, *sp;
+ const char *cmt;
+
+ putfqval( "General", "ConfigVersion", RCVERSTR );
+ for (cs = config; cs; cs = cs->next) {
+ fprintf( f, "%s[%s]\n",
+ cs->comment ? cs->comment : "\n", cs->name );
+ for (ce = cs->ents; ce; ce = ce->next) {
+ if (ce->spec->comment) {
+ cmt = ce->spec->comment;
+ for (sp = sl; sp; sp = sp->next)
+ if (sp->str == cmt) {
+ cmt = "# See above\n";
+ goto havit;
+ }
+ if (!(sp = malloc( sizeof(*sp) )))
+ fprintf( stderr, "Warning: Out of memory\n" );
+ else {
+ sp->str = cmt;
+ sp->next = sl; sl = sp;
+ }
+ } else
+ cmt = "";
+ havit:
+ fprintf( f, "%s%s%s=%s\n",
+ cmt, ce->active ? "" : "#", ce->spec->key, ce->value );
+ }
+ }
+}
+
+
+/*
+ * defaults
+ */
+#ifdef XDMCP
+static const char def_xaccess[] =
+"# Xaccess - Access control file for XDMCP connections\n"
+"#\n"
+"# To control Direct and Broadcast access:\n"
+"#\n"
+"# pattern\n"
+"#\n"
+"# To control Indirect queries:\n"
+"#\n"
+"# pattern list of hostnames and/or macros ...\n"
+"#\n"
+"# To use the chooser:\n"
+"#\n"
+"# pattern CHOOSER BROADCAST\n"
+"#\n"
+"# or\n"
+"#\n"
+"# pattern CHOOSER list of hostnames and/or macros ...\n"
+"#\n"
+"# To define macros:\n"
+"#\n"
+"# %name list of hosts ...\n"
+"#\n"
+"# The first form tells xdm which displays to respond to itself.\n"
+"# The second form tells xdm to forward indirect queries from hosts matching\n"
+"# the specified pattern to the indicated list of hosts.\n"
+"# The third form tells xdm to handle indirect queries using the chooser;\n"
+"# the chooser is directed to send its own queries out via the broadcast\n"
+"# address and display the results on the terminal.\n"
+"# The fourth form is similar to the third, except instead of using the\n"
+"# broadcast address, it sends DirectQuerys to each of the hosts in the list\n"
+"#\n"
+"# In all cases, xdm uses the first entry which matches the terminal;\n"
+"# for IndirectQuery messages only entries with right hand sides can\n"
+"# match, for Direct and Broadcast Query messages, only entries without\n"
+"# right hand sides can match.\n"
+"#\n"
+"\n"
+"* #any host can get a login window\n"
+"\n"
+"#\n"
+"# To hardwire a specific terminal to a specific host, you can\n"
+"# leave the terminal sending indirect queries to this host, and\n"
+"# use an entry of the form:\n"
+"#\n"
+"\n"
+"#terminal-a host-a\n"
+"\n"
+"\n"
+"#\n"
+"# The nicest way to run the chooser is to just ask it to broadcast\n"
+"# requests to the network - that way new hosts show up automatically.\n"
+"# Sometimes, however, the chooser can't figure out how to broadcast,\n"
+"# so this may not work in all environments.\n"
+"#\n"
+"\n"
+"* CHOOSER BROADCAST #any indirect host can get a chooser\n"
+"\n"
+"#\n"
+"# If you'd prefer to configure the set of hosts each terminal sees,\n"
+"# then just uncomment these lines (and comment the CHOOSER line above)\n"
+"# and edit the %hostlist line as appropriate\n"
+"#\n"
+"\n"
+"#%hostlist host-a host-b\n"
+"\n"
+"#* CHOOSER %hostlist #\n";
+#endif
+
+#ifdef XDMCP
+static const char def_willing[] =
+"#! /bin/sh\n"
+"# The output of this script is displayed in the chooser window\n"
+"# (instead of \"Willing to manage\").\n"
+"\n"
+"load=`uptime|sed -e 's/^.*load[^0-9]*//'`\n"
+"nrusers=`who|cut -c 1-8|sort -u|wc -l|sed 's/^[ \t]*//'`\n"
+"s=\"\"; [ \"$nrusers\" != 1 ] && s=s\n"
+"\n"
+"echo \"${nrusers} user${s}, load: ${load}\"\n";
+#endif
+
+static const char def_setup[] =
+"#! /bin/sh\n"
+"# Xsetup - run as root before the login dialog appears\n"
+"\n"
+"#xconsole -geometry 480x130-0-0 -notify -verbose -fn fixed -exitOnFail -file /dev/xconsole &\n";
+
+static const char def_startup[] =
+"#! /bin/sh\n"
+"# Xstartup - run as root before session starts\n"
+"\n"
+"# By convention, both xconsole and xterm -C check that the\n"
+"# console is owned by the invoking user and is readable before attaching\n"
+"# the console output. This way a random user can invoke xterm -C without\n"
+"# causing serious grief; still, it can cause havoc, so xconsole is started\n"
+"# by Xsetup usually.\n"
+"# This is not required if you use PAM with the pam_console module.\n"
+"#\n"
+"#chown $USER /dev/console\n"
+"\n"
+#ifdef _AIX
+"# We create a pseudodevice for finger. (host:0 becomes xdm/host_0)\n"
+"# Without it, finger errors out with \"Can't stat /dev/host:0\".\n"
+"#\n"
+"#devname=`echo $DISPLAY | cut -c1-8`\n"
+"#if [ ! -d /dev/xdm ]; then\n"
+"# mkdir /dev/xdm\n"
+"# chmod 755 /dev/xdm\n"
+"#fi\n"
+"#touch /dev/xdm/$devname\n"
+"#chmod 644 /dev/xdm/$devname\n"
+"#exec sessreg -a -l xdm/$devname -h \"`echo $DISPLAY | cut -d: -f1`\""
+#else
+"#exec sessreg -a -l $DISPLAY -h \"`echo $DISPLAY | cut -d: -f1`\""
+# ifdef BSD
+" -x " KDMCONF "/Xservers"
+# endif
+#endif /* _AIX */
+" $USER\n"
+"\n# NOTE: The session is aborted if the last command returns non-zero.\n";
+
+static const char def_reset[] =
+"#! /bin/sh\n"
+"# Xreset - run as root after session exits\n"
+"\n"
+"# Reassign ownership of the console to root, this should disallow\n"
+"# assignment of console output to any random users's xterm. See Xstartup.\n"
+"#\n"
+"#chown root /dev/console\n"
+"#chmod 622 /dev/console\n"
+"\n"
+#ifdef _AIX
+"#devname=`echo $DISPLAY | cut -c1-8`\n"
+"#exec sessreg -d -l xdm/$devname -h \"`echo $DISPLAY | cut -d: -f1`\""
+#else
+"#exec sessreg -d -l $DISPLAY -h \"`echo $DISPLAY | cut -d: -f1`\""
+# ifdef BSD
+" -x " KDMCONF "/Xservers"
+# endif
+#endif /* _AIX */
+" $USER\n";
+
+static const char def_session1[] =
+"#! /bin/sh\n"
+"# Xsession - run as user\n"
+"\n"
+"session=$1\n"
+"\n"
+"# Note that the respective logout scripts are not sourced.\n"
+"case $SHELL in\n"
+" */bash)\n"
+" [ -z \"$BASH\" ] && exec $SHELL $0 \"$@\"\n"
+" set +o posix\n"
+" [ -f /etc/profile ] && . /etc/profile\n"
+" if [ -f $HOME/.bash_profile ]; then\n"
+" . $HOME/.bash_profile\n"
+" elif [ -f $HOME/.bash_login ]; then\n"
+" . $HOME/.bash_login\n"
+" elif [ -f $HOME/.profile ]; then\n"
+" . $HOME/.profile\n"
+" fi\n"
+" ;;\n"
+" */zsh)\n"
+" [ -z \"$ZSH_NAME\" ] && exec $SHELL $0 \"$@\"\n"
+" emulate -R zsh\n"
+" [ -d /etc/zsh ] && zdir=/etc/zsh || zdir=/etc\n"
+" zhome=${ZDOTDIR:-$HOME}\n"
+" # zshenv is always sourced automatically.\n"
+" [ -f $zdir/zprofile ] && . $zdir/zprofile\n"
+" [ -f $zhome/.zprofile ] && . $zhome/.zprofile\n"
+" [ -f $zdir/zlogin ] && . $zdir/zlogin\n"
+" [ -f $zhome/.zlogin ] && . $zhome/.zlogin\n"
+" ;;\n"
+" */csh|*/tcsh)\n"
+" # [t]cshrc is always sourced automatically.\n"
+" # Note that sourcing csh.login after .cshrc is non-standard.\n"
+" xsess_tmp=";
+static const char def_session2[] =
+"\n"
+" $SHELL -c \"if (-f /etc/csh.login) source /etc/csh.login; if (-f ~/.login) source ~/.login; /bin/sh -c export -p >! $xsess_tmp\"\n"
+" . $xsess_tmp\n"
+" rm -f $xsess_tmp\n"
+" ;;\n"
+" *) # Plain sh, ksh, and anything we don't know.\n"
+" [ -f /etc/profile ] && . /etc/profile\n"
+" [ -f $HOME/.profile ] && . $HOME/.profile\n"
+" ;;\n"
+"esac\n"
+"\n"
+"[ -f /etc/xprofile ] && . /etc/xprofile\n"
+"[ -f $HOME/.xprofile ] && . $HOME/.xprofile\n"
+"\n"
+"case $session in\n"
+" \"\")\n"
+" exec xmessage -center -buttons OK:0 -default OK \"Sorry, $DESKTOP_SESSION is no valid session.\"\n"
+" ;;\n"
+" failsafe)\n"
+" exec xterm -geometry 80x24-0-0\n"
+" ;;\n"
+" custom)\n"
+" exec $HOME/.xsession\n"
+" ;;\n"
+" default)\n"
+" exec " KDE_BINDIR "/startkde\n"
+" ;;\n"
+" *)\n"
+" eval exec \"$session\"\n"
+" ;;\n"
+"esac\n"
+"exec xmessage -center -buttons OK:0 -default OK \"Sorry, cannot execute $session. Check $DESKTOP_SESSION.desktop.\"\n";
+
+static const char def_background[] =
+"[Desktop0]\n"
+"BackgroundMode=Flat\n"
+"BlendBalance=100\n"
+"BlendMode=NoBlending\n"
+"ChangeInterval=60\n"
+"Color1=0,0,200\n"
+"Color2=192,192,192\n"
+"CurrentWallpaper=0\n"
+"LastChange=0\n"
+"MinOptimizationDepth=1\n"
+"MultiWallpaperMode=NoMulti\n"
+"Pattern=fish\n"
+"Program=\n"
+"ReverseBlending=false\n"
+"UseSHM=false\n"
+"Wallpaper=default_blue.jpg\n"
+"WallpaperList=\n"
+"WallpaperMode=Scaled\n";
+
+static char *
+prepName( const char *fn )
+{
+ const char *tname;
+ char *nname;
+
+ tname = strrchr( fn, '/' );
+ ASPrintf( &nname, "%s/%s", newdir, tname ? tname + 1 : fn );
+ displace( nname );
+ return nname;
+}
+
+static FILE *
+Create( const char *fn, int mode )
+{
+ char *nname;
+ FILE *f;
+
+ nname = prepName( fn );
+ if (!(f = fopen( nname, "w" ))) {
+ fprintf( stderr, "Cannot create %s\n", nname );
+ exit( 1 );
+ }
+ chmod( nname, mode );
+ free( nname );
+ return f;
+}
+
+static void
+WriteOut( const char *fn, int mode, time_t stamp, const char *buf, size_t len )
+{
+ char *nname;
+ int fd;
+ struct utimbuf utim;
+
+ nname = prepName( fn );
+ if ((fd = creat( nname, mode )) < 0) {
+ fprintf( stderr, "Cannot create %s\n", nname );
+ exit( 1 );
+ }
+ write( fd, buf, len );
+ close( fd );
+ if (stamp) {
+ utim.actime = utim.modtime = stamp;
+ utime( nname, &utim );
+ }
+ free( nname );
+}
+
+
+/* returns static array! */
+static const char *
+resect( const char *sec, const char *name )
+{
+ static char sname[64];
+ char *p;
+
+ if ((p = strrchr( sec, '-' ))) {
+ sprintf( sname, "%.*s-%s", p - sec, sec, name );
+ return sname;
+ } else
+ return name;
+}
+
+static int
+inNewDir( const char *name )
+{
+ return !memcmp( name, KDMCONF "/", sizeof(KDMCONF) );
+}
+
+static int
+inList( StrList *sp, const char *s )
+{
+ for (; sp; sp = sp->next)
+ if (!strcmp( sp->str, s ))
+ return 1;
+ return 0;
+}
+
+static void
+addStr( StrList **sp, const char *s )
+{
+ for (; *sp; sp = &(*sp)->next)
+ if (!strcmp( (*sp)->str, s ))
+ return;
+ *sp = mcalloc( sizeof(**sp) );
+ ASPrintf( (char **)&(*sp)->str, "%s", s );
+}
+
+StrList *aflist, *uflist, *eflist, *cflist, *lflist;
+
+/* file is part of new config */
+static void
+addedFile( const char *fn )
+{
+ addStr( &aflist, fn );
+}
+
+/* file from old config was parsed */
+static void
+usedFile( const char *fn )
+{
+ addStr( &uflist, fn );
+}
+
+/* file from old config was copied with slight modifications */
+static void
+editedFile( const char *fn )
+{
+ addStr( &eflist, fn );
+}
+
+/* file from old config was copied verbatim */
+static void
+copiedFile( const char *fn )
+{
+ addStr( &cflist, fn );
+}
+
+/* file from old config is still being used */
+static void
+linkedFile( const char *fn )
+{
+ addStr( &lflist, fn );
+}
+
+/*
+ * XXX this stuff is highly borked. it does not handle collisions at all.
+ */
+static int
+copyfile( Entry *ce, const char *tname, int mode, int (*proc)( File * ) )
+{
+ const char *tptr;
+ char *nname;
+ File file;
+ int rt;
+
+ if (!*ce->value)
+ return 1;
+
+ tptr = strrchr( tname, '/' );
+ ASPrintf( &nname, KDMCONF "/%s", tptr ? tptr + 1 : tname );
+ if (inList( cflist, ce->value ) ||
+ inList( eflist, ce->value ) ||
+ inList( lflist, ce->value ))
+ {
+ rt = 1;
+ goto doret;
+ }
+ if (!readFile( &file, ce->value )) {
+ fprintf( stderr, "Warning: cannot copy file %s\n", ce->value );
+ rt = 0;
+ } else {
+ if (!proc || !proc( &file )) {
+ if (!use_destdir && !strcmp( ce->value, nname ))
+ linkedFile( nname );
+ else {
+ struct stat st;
+ stat( ce->value, &st );
+ WriteOut( nname, mode, st.st_mtime, file.buf, file.eof - file.buf );
+ copiedFile( ce->value );
+ }
+ } else {
+ WriteOut( nname, mode, 0, file.buf, file.eof - file.buf );
+ editedFile( ce->value );
+ }
+ if (strcmp( ce->value, nname ) && inNewDir( ce->value ) && !use_destdir)
+ displace( ce->value );
+ addedFile( nname );
+ rt = 1;
+ }
+ doret:
+ ce->value = nname;
+ return rt;
+}
+
+static void
+dlinkfile( const char *name )
+{
+ File file;
+
+ if (!readFile( &file, name )) {
+ fprintf( stderr, "Warning: cannot read file %s\n", name );
+ return;
+ }
+ if (inNewDir( name ) && use_destdir) {
+ struct stat st;
+ stat( name, &st );
+ WriteOut( name, st.st_mode, st.st_mtime, file.buf, file.eof - file.buf );
+ copiedFile( name );
+ } else
+ linkedFile( name );
+ addedFile( name );
+}
+
+static void
+linkfile( Entry *ce )
+{
+ if (ce->written && *ce->value)
+ dlinkfile( ce->value );
+}
+
+static void
+writefile( const char *tname, int mode, const char *cont )
+{
+ WriteOut( tname, mode, 0, cont, strlen( cont ) );
+ addedFile( tname );
+}
+
+
+char *background;
+
+static void
+handBgCfg( Entry *ce, Section *cs ATTR_UNUSED )
+{
+ if (!ce->active) /* can be only the X-*-Greeter one */
+ writefile( def_BackgroundCfg, 0644,
+ background ? background : def_background );
+#if 0 /* risk of kcontrol clobbering the original file */
+ else if (old_confs)
+ linkfile( ce );
+#endif
+ else {
+ if (!copyfile( ce, ce->value, 0644, 0 )) {
+ if (!strcmp( cs->name, "X-*-Greeter" ))
+ writefile( def_BackgroundCfg, 0644, def_background );
+ ce->active = 0;
+ }
+ }
+}
+
+
+#ifdef HAVE_VTS
+static char *
+mem_mem( char *mem, int lmem, const char *smem, int lsmem )
+{
+ for (; lmem >= lsmem; mem++, lmem--)
+ if (!memcmp( mem, smem, lsmem ))
+ return mem + lsmem;
+ return 0;
+}
+
+static int maxTTY, TTYmask;
+
+static void
+getInitTab( void )
+{
+ File it;
+ char *p, *eol, *ep;
+ int tty;
+
+ if (maxTTY)
+ return;
+ if (readFile( &it, "/etc/inittab" )) {
+ usedFile( "/etc/inittab" );
+ for (p = it.buf; p < it.eof; p = eol + 1) {
+ for (eol = p; eol < it.eof && *eol != '\n'; eol++);
+ if (*p != '#') {
+ if ((ep = mem_mem( p, eol - p, " tty", 4 )) &&
+ ep < eol && isdigit( *ep ))
+ {
+ if (ep + 1 == eol || isspace( *(ep + 1) ))
+ tty = *ep - '0';
+ else if (isdigit( *(ep + 1) ) &&
+ (ep + 2 == eol || isspace( *(ep + 2) )))
+ tty = (*ep - '0') * 10 + (*(ep + 1) - '0');
+ else
+ continue;
+ TTYmask |= 1 << (tty - 1);
+ if (tty > maxTTY)
+ maxTTY = tty;
+ }
+ }
+ }
+ freeBuf( &it );
+ }
+ if (!maxTTY) {
+ maxTTY = 6;
+ TTYmask = 0x3f;
+ }
+}
+#endif
+
+
+/* TODO: handle solaris' local_uid specs */
+
+static char *
+ReadWord( File *file, int EOFatEOL )
+{
+ char *wordp, *wordBuffer;
+ int quoted;
+ char c;
+
+ rest:
+ wordp = wordBuffer = file->cur;
+ mloop:
+ quoted = 0;
+ qloop:
+ if (file->cur == file->eof) {
+ doeow:
+ if (wordp == wordBuffer)
+ return 0;
+ retw:
+ *wordp = '\0';
+ return wordBuffer;
+ }
+ c = *file->cur++;
+ switch (c) {
+ case '#':
+ if (quoted)
+ break;
+ do {
+ if (file->cur == file->eof)
+ goto doeow;
+ c = *file->cur++;
+ } while (c != '\n');
+ case '\0':
+ case '\n':
+ if (EOFatEOL && !quoted) {
+ file->cur--;
+ goto doeow;
+ }
+ if (wordp != wordBuffer) {
+ file->cur--;
+ goto retw;
+ }
+ goto rest;
+ case ' ':
+ case '\t':
+ if (wordp != wordBuffer)
+ goto retw;
+ goto rest;
+ case '\\':
+ if (!quoted) {
+ quoted = 1;
+ goto qloop;
+ }
+ break;
+ }
+ *wordp++ = c;
+ goto mloop;
+}
+
+/* backslashes are double-escaped - for KConfig and for parseArgs */
+static const char *
+joinArgs( StrList *argv )
+{
+ StrList *av;
+ const char *s, *rs;
+ char *str;
+ int slen;
+
+ if (!argv)
+ return "";
+ for (slen = 0, av = argv; slen++, av; av = av->next) {
+ int nq = 0;
+ for (s = av->str; *s; s++, slen++)
+ if (isspace( *s ) || *s == '\'')
+ nq = 2;
+ else if (*s == '"')
+ slen += 2;
+ else if (*s == '\\')
+ slen += 3;
+ slen += nq;
+ }
+ rs = str = mmalloc( slen );
+ for (av = argv; av; av = av->next) {
+ int nq = 0;
+ for (s = av->str; *s; s++)
+ if (isspace( *s ) || *s == '\'')
+ nq = 2;
+ if (av != argv)
+ *str++ = ' ';
+ if (nq)
+ *str++ = '"';
+ for (s = av->str; *s; s++) {
+ if (*s == '\\')
+ *str++ = '\\';
+ if (*s == '"' || *s == '\\') {
+ *str++ = '\\';
+ *str++ = '\\';
+ }
+ *str++ = *s;
+ }
+ if (nq)
+ *str++ = '"';
+ }
+ *str = 0;
+ return rs;
+}
+
+# define dLocation 1
+# define dLocal 0
+# define dForeign 1
+
+static struct displayMatch {
+ const char *name;
+ int len, type;
+} displayTypes[] = {
+ { "local", 5, dLocal },
+ { "foreign", 7, dForeign },
+};
+
+static int
+parseDisplayType( const char *string, const char **atPos )
+{
+ struct displayMatch *d;
+
+ *atPos = 0;
+ for (d = displayTypes; d < displayTypes + as(displayTypes); d++) {
+ if (!memcmp( d->name, string, d->len ) &&
+ (!string[d->len] || string[d->len] == '@'))
+ {
+ if (string[d->len] == '@' && string[d->len + 1])
+ *atPos = string + d->len + 1;
+ return d->type;
+ }
+ }
+ return -1;
+}
+
+typedef struct serverEntry {
+ struct serverEntry *next;
+ const char *name, *class2, *console, *argvs, *arglvs;
+ StrList *argv, *arglv;
+ int type, reserve, vt;
+} ServerEntry;
+
+static void
+absorb_xservers( const char *sect ATTR_UNUSED, char **value )
+{
+ ServerEntry *se, *se1, *serverList, **serverPtr;
+ const char *word, *word2;
+ char *sdpys, *rdpys;
+ StrList **argp, **arglp, *ap, *ap2;
+ File file;
+ int nldpys = 0, nrdpys = 0, dpymask = 0;
+ int cpcmd, cpcmdl;
+#ifdef HAVE_VTS
+ int dn, cpvt, mtty;
+#endif
+
+ if (**value == '/') {
+ if (!readFile( &file, *value ))
+ return;
+ usedFile( *value );
+ } else {
+ file.buf = *value;
+ file.eof = *value + strlen( *value );
+ }
+ file.cur = file.buf;
+
+ serverPtr = &serverList;
+#ifdef HAVE_VTS
+ bustd:
+#endif
+ while ((word = ReadWord( &file, 0 ))) {
+ se = mcalloc( sizeof(*se) );
+ se->name = word;
+ if (!(word = ReadWord( &file, 1 )))
+ continue;
+ se->type = parseDisplayType( word, &se->console );
+ if (se->type < 0) {
+ se->class2 = word;
+ if (!(word = ReadWord( &file, 1 )))
+ continue;
+ se->type = parseDisplayType( word, &se->console );
+ if (se->type < 0) {
+ while (ReadWord( &file, 1 ));
+ continue;
+ }
+ }
+ word = ReadWord( &file, 1 );
+ if (word && !strcmp( word, "reserve" )) {
+ se->reserve = 1;
+ word = ReadWord( &file, 1 );
+ }
+ if (((se->type & dLocation) == dLocal) != (word != 0))
+ continue;
+ argp = &se->argv;
+ arglp = &se->arglv;
+ while (word) {
+#ifdef HAVE_VTS
+ if (word[0] == 'v' && word[1] == 't')
+ se->vt = atoi( word + 2 );
+ else if (!strcmp( word, "-crt" )) { /* SCO style */
+ if (!(word = ReadWord( &file, 1 )) ||
+ memcmp( word, "/dev/tty", 8 ))
+ goto bustd;
+ se->vt = atoi( word + 8 );
+ } else
+#endif
+ if (strcmp( word, se->name )) {
+ ap = mmalloc( sizeof(*ap) );
+ ap->str = word;
+ if (!strcmp( word, "-nolisten" )) {
+ if (!(word2 = ReadWord( &file, 1 )))
+ break;
+ ap2 = mmalloc( sizeof(*ap2) );
+ ap2->str = word2;
+ ap->next = ap2;
+ if (!strcmp( word2, "unix" )) {
+ *argp = ap;
+ argp = &ap2->next;
+ } else {
+ *arglp = ap;
+ arglp = &ap2->next;
+ }
+ } else {
+ *argp = ap;
+ argp = &ap->next;
+ }
+ }
+ word = ReadWord( &file, 1 );
+ }
+ *argp = *arglp = 0;
+ if ((se->type & dLocation) == dLocal) {
+ nldpys++;
+ dpymask |= 1 << atoi( se->name + 1 );
+ if (se->reserve)
+ nrdpys++;
+ }
+ *serverPtr = se;
+ serverPtr = &se->next;
+ }
+ *serverPtr = 0;
+
+#ifdef HAVE_VTS
+ /* don't copy only if all local displays are ordered and have a vt */
+ cpvt = 0;
+ getInitTab();
+ for (se = serverList, mtty = maxTTY; se; se = se->next)
+ if ((se->type & dLocation) == dLocal) {
+ mtty++;
+ if (se->vt != mtty) {
+ cpvt = 1;
+ break;
+ }
+ }
+#endif
+
+ for (se = serverList; se; se = se->next) {
+ se->argvs = joinArgs( se->argv );
+ se->arglvs = joinArgs( se->arglv );
+ }
+
+ se1 = 0, cpcmd = cpcmdl = 0;
+ for (se = serverList; se; se = se->next)
+ if ((se->type & dLocation) == dLocal) {
+ if (!se1)
+ se1 = se;
+ else {
+ if (strcmp( se1->argvs, se->argvs ))
+ cpcmd = 1;
+ if (strcmp( se1->arglvs, se->arglvs ))
+ cpcmdl = 1;
+ }
+ }
+ if (se1) {
+ putfqval( "X-:*-Core", "ServerCmd", se1->argvs );
+ putfqval( "X-:*-Core", "ServerArgsLocal", se1->arglvs );
+ for (se = serverList; se; se = se->next)
+ if ((se->type & dLocation) == dLocal) {
+ char sec[32];
+ sprintf( sec, "X-%s-Core", se->name );
+ if (cpcmd)
+ putfqval( sec, "ServerCmd", se->argvs );
+ if (cpcmdl)
+ putfqval( sec, "ServerArgsLocal", se->arglvs );
+#ifdef HAVE_VTS
+ if (cpvt && se->vt) {
+ char vt[8];
+ sprintf( vt, "%d", se->vt );
+ putfqval( sec, "ServerVT", vt );
+ }
+#else
+ if (se->console)
+ putfqval( sec, "ServerTTY", se->console );
+#endif
+ }
+ }
+
+ sdpys = rdpys = 0;
+ for (se = serverList; se; se = se->next)
+ StrCat( se->reserve ? &rdpys : &sdpys,
+ se->class2 ? ",%s_%s" : ",%s", se->name, se->class2 );
+
+#ifdef HAVE_VTS
+ /* add reserve dpys */
+ if (nldpys < 4 && nldpys && !nrdpys)
+ for (; nldpys < 4; nldpys++) {
+ for (dn = 0; dpymask & (1 << dn); dn++);
+ dpymask |= (1 << dn);
+ StrCat( &rdpys, ",:%d", dn );
+ }
+#endif
+
+ putfqval( "General", "StaticServers", sdpys ? sdpys + 1 : "" );
+ putfqval( "General", "ReserveServers", rdpys ? rdpys + 1 : "" );
+
+ if (**value == '/' && inNewDir( *value ) && !use_destdir)
+ displace( *value );
+}
+
+#ifdef HAVE_VTS
+static void
+upd_servervts( Entry *ce, Section *cs ATTR_UNUSED )
+{
+ if (!ce->active) { /* there is only the Global one */
+#ifdef __linux__ /* XXX actually, sysvinit */
+ getInitTab();
+ ASPrintf( (char **)&ce->value, "-%d", maxTTY + 1 );
+ ce->active = ce->written = 1;
+#endif
+ }
+}
+
+static void
+upd_consolettys( Entry *ce, Section *cs ATTR_UNUSED )
+{
+ if (!ce->active) { /* there is only the Global one */
+#ifdef __linux__ /* XXX actually, sysvinit */
+ char *buf;
+ int i;
+
+ getInitTab();
+ for (i = 0, buf = 0; i < 16; i++)
+ if (TTYmask & (1 << i))
+ StrCat( &buf, ",tty%d", i + 1 );
+ if (buf) {
+ ce->value = buf + 1;
+ ce->active = ce->written = 1;
+ }
+#endif
+ }
+}
+#endif
+
+#ifdef XDMCP
+static void
+cp_keyfile( Entry *ce, Section *cs ATTR_UNUSED )
+{
+ if (!ce->active) /* there is only the Global one */
+ return;
+ if (old_confs)
+ linkfile( ce );
+ else
+ if (!copyfile( ce, "kdmkeys", 0600, 0 ))
+ ce->active = 0;
+}
+
+static void
+mk_xaccess( Entry *ce, Section *cs ATTR_UNUSED )
+{
+ if (!ce->active) /* there is only the Global one */
+ writefile( def_Xaccess, 0644, def_xaccess );
+ else if (old_confs)
+ linkfile( ce );
+ else
+ copyfile( ce, "Xaccess", 0644, 0 ); /* don't handle error, it will disable Xdmcp automatically */
+}
+
+static void
+mk_willing( Entry *ce, Section *cs ATTR_UNUSED )
+{
+ const char *fname;
+
+ if (!ce->active) /* there is only the Global one */
+ goto dflt;
+ else {
+ if (!(fname = strchr( ce->value, '/' )))
+ return; /* obviously in-line (or empty) */
+ if (old_scripts || inNewDir( fname ))
+ dlinkfile( fname );
+ else {
+ dflt:
+ ce->value = KDMCONF "/Xwilling";
+ ce->active = ce->written = 1;
+ writefile( ce->value, 0755, def_willing );
+ }
+ }
+}
+#endif
+
+/*
+static int
+edit_resources( File *file )
+{
+ // XXX remove any login*, chooser*, ... resources
+ return 0;
+}
+*/
+
+static void
+cp_resources( Entry *ce, Section *cs ATTR_UNUSED )
+{
+ if (!ce->active) /* the X-*-Greeter one */
+ return;
+ if (old_confs)
+ linkfile( ce );
+ else
+ if (!copyfile( ce, ce->value, 0644, 0/*edit_resources*/ ))
+ ce->active = 0;
+}
+
+static int
+delstr( File *fil, const char *pat )
+{
+ char *p, *pp, *bpp;
+ const char *pap, *paap;
+
+ *fil->eof = 0;
+ for (p = fil->buf; *p; p++) {
+ for (pp = p, pap = pat; ; ) {
+ if (!*pap) {
+ *p = '\n';
+ memcpy( p + 1, pp, fil->eof - pp + 1 );
+ fil->eof -= pp - p - 1;
+ return 1;
+ } else if (!memcmp( pap, "*/", 2 )) {
+ paap = pap += 2;
+ while (!isspace( *pap ))
+ pap++;
+ if (*pp != '/')
+ break;
+ for (;;)
+ for (bpp = ++pp; *pp != '/'; pp++)
+ if (!*pp || isspace( *pp ))
+ goto wbrk;
+ wbrk:
+ if ((pp - bpp != pap - paap) || memcmp( bpp, paap, pap - paap ))
+ break;
+ } else if (*pap == '\t') {
+ pap++;
+ while (*pp == ' ' || *pp == '\t')
+ pp++;
+ } else if (*pap == '[') {
+ pap++;
+ for (;;) {
+ if (!*pap) {
+ fprintf( stderr, "Internal error: unterminated char set\n" );
+ exit( 1 );
+ }
+ if (*pap == *pp) {
+ while (*++pap != ']')
+ if (!*pap) {
+ fprintf( stderr, "Internal error: unterminated char set\n" );
+ exit( 1 );
+ }
+ pap++;
+ pp++;
+ break;
+ }
+ if (*++pap == ']')
+ goto no;
+ }
+ } else {
+ if (*pap == '\n')
+ while (*pp == ' ' || *pp == '\t')
+ pp++;
+ if (*pap != *pp)
+ break;
+ pap++;
+ pp++;
+ }
+ }
+ no: ;
+ }
+ return 0;
+}
+
+/* XXX
+ the UseBackground voodoo will horribly fail, if multiple sections link
+ to the same Xsetup file
+*/
+
+static int mod_usebg;
+
+static int
+edit_setup( File *file )
+{
+ int chg =
+ delstr( file, "\n"
+ "(\n"
+ " PIDFILE=/var/run/kdmdesktop-$DISPLAY.pid\n"
+ " */kdmdesktop\t&\n"
+ " echo $! >$PIDFILE\n"
+ " wait $!\n"
+ " rm $PIDFILE\n"
+ ")\t&\n" ) |
+ delstr( file, "\n"
+ "*/kdmdesktop\t&\n" ) |
+ delstr( file, "\n"
+ "kdmdesktop\t&\n" ) |
+ delstr( file, "\n"
+ "kdmdesktop\n" );
+ putval( "UseBackground", chg ? "true" : "false" );
+ return chg;
+}
+
+static void
+mk_setup( Entry *ce, Section *cs )
+{
+ setsect( resect( cs->name, "Greeter" ) );
+ if (old_scripts || mixed_scripts) {
+ if (mod_usebg && *ce->value)
+ putval( "UseBackground", "false" );
+ linkfile( ce );
+ } else {
+ if (ce->active && inNewDir( ce->value )) {
+ if (mod_usebg)
+ copyfile( ce, ce->value, 0755, edit_setup );
+ else
+ linkfile( ce );
+ } else {
+ ce->value = KDMCONF "/Xsetup";
+ ce->active = ce->written = 1;
+ writefile( ce->value, 0755, def_setup );
+ }
+ }
+}
+
+static int
+edit_startup( File *file )
+{
+ int chg1 = 0, chg2 = 0;
+
+ if (mod_usebg &&
+ (delstr( file, "\n"
+ "PIDFILE=/var/run/kdmdesktop-$DISPLAY.pid\n"
+ "if [[] -f $PIDFILE ] ; then\n"
+ " kill `cat $PIDFILE`\n"
+ "fi\n" ) ||
+ delstr( file, "\n"
+ "PIDFILE=/var/run/kdmdesktop-$DISPLAY.pid\n"
+ "test -f $PIDFILE && kill `cat $PIDFILE`\n" )))
+ chg1 = 1;
+ if (oldver < 0x0203) {
+ chg2 =
+#ifdef _AIX
+ delstr( file, "\n"
+"# We create a pseudodevice for finger. (host:0 becomes [kx]dm/host_0)\n" );
+"# Without it, finger errors out with \"Can't stat /dev/host:0\".\n"
+"#\n"
+"if [[] -f /usr/lib/X11/xdm/sessreg ]; then\n"
+" devname=`echo $DISPLAY | /usr/bin/sed -e 's/[[]:\\.]/_/g' | /usr/bin/cut -c1-8`\n"
+" hostname=`echo $DISPLAY | /usr/bin/cut -d':' -f1`\n"
+"\n"
+" if [[] -z \"$devname\" ]; then\n"
+" devname=\"unknown\"\n"
+" fi\n"
+" if [[] ! -d /dev/[kx]dm ]; then\n"
+" /usr/bin/mkdir /dev/[kx]dm\n"
+" /usr/bin/chmod 755 /dev/[kx]dm\n"
+" fi\n"
+" /usr/bin/touch /dev/[kx]dm/$devname\n"
+" /usr/bin/chmod 644 /dev/[kx]dm/$devname\n"
+"\n"
+" if [[] -z \"$hostname\" ]; then\n"
+" exec /usr/lib/X11/xdm/sessreg -a -l [kx]dm/$devname $USER\n"
+" else\n"
+" exec /usr/lib/X11/xdm/sessreg -a -l [kx]dm/$devname -h $hostname $USER\n"
+" fi\n"
+"fi\n") |
+#else
+# ifdef BSD
+ delstr( file, "\n"
+"exec sessreg -a -l $DISPLAY -x */Xservers -u " _PATH_UTMP " $USER\n" ) |
+# endif
+#endif /* _AIX */
+ delstr( file, "\n"
+"exec sessreg -a -l $DISPLAY"
+#ifdef BSD
+" -x */Xservers"
+#endif
+" $USER\n" ) |
+ delstr( file, "\n"
+"exec sessreg -a -l $DISPLAY -u /var/run/utmp -x */Xservers $USER\n" );
+ putval( "UseSessReg", chg2 ? "true" : "false");
+ }
+ return chg1 | chg2;
+}
+
+static void
+mk_startup( Entry *ce, Section *cs )
+{
+ setsect( cs->name );
+ if (old_scripts || mixed_scripts)
+ linkfile( ce );
+ else {
+ if (ce->active && inNewDir( ce->value )) {
+ if (mod_usebg || oldver < 0x0203)
+ copyfile( ce, ce->value, 0755, edit_startup );
+ else
+ linkfile( ce );
+ } else {
+ ce->value = KDMCONF "/Xstartup";
+ ce->active = ce->written = 1;
+ writefile( ce->value, 0755, def_startup );
+ }
+ }
+}
+
+static int
+edit_reset( File *file )
+{
+ return
+#ifdef _AIX
+ delstr( file, "\n"
+"if [[] -f /usr/lib/X11/xdm/sessreg ]; then\n"
+" devname=`echo $DISPLAY | /usr/bin/sed -e 's/[[]:\\.]/_/g' | /usr/bin/cut -c1-8`\n"
+" exec /usr/lib/X11/xdm/sessreg -d -l [kx]dm/$devname $USER\n"
+"fi\n" ) |
+#else
+# ifdef BSD
+ delstr( file, "\n"
+"exec sessreg -d -l $DISPLAY -x */Xservers -u " _PATH_UTMP " $USER\n" ) |
+# endif
+#endif /* _AIX */
+ delstr( file, "\n"
+"exec sessreg -d -l $DISPLAY"
+# ifdef BSD
+" -x */Xservers"
+# endif
+" $USER\n" ) |
+ delstr( file, "\n"
+"exec sessreg -d -l $DISPLAY -u /var/run/utmp -x */Xservers $USER\n" );
+}
+
+static void
+mk_reset( Entry *ce, Section *cs ATTR_UNUSED )
+{
+ if (old_scripts || mixed_scripts)
+ linkfile( ce );
+ else {
+ if (ce->active && inNewDir( ce->value )) {
+ if (oldver < 0x0203)
+ copyfile( ce, ce->value, 0755, edit_reset );
+ else
+ linkfile( ce );
+ } else {
+ ce->value = KDMCONF "/Xreset";
+ ce->active = ce->written = 1;
+ writefile( ce->value, 0755, def_reset );
+ }
+ }
+}
+
+static void
+mk_session( Entry *ce, Section *cs ATTR_UNUSED )
+{
+ char *def_session;
+ const char *tmpf;
+
+ if ((old_scripts || (ce->active && inNewDir( ce->value ))) &&
+ oldver >= 0x202)
+ linkfile( ce );
+ else {
+ tmpf = locate( "mktemp" ) ?
+ "`mktemp /tmp/xsess-env-XXXXXX`" :
+ locate( "tempfile" ) ?
+ "`tempfile`" :
+ "$HOME/.xsession-env-$DISPLAY";
+ ASPrintf( &def_session, "%s%s%s", def_session1, tmpf, def_session2 );
+ ce->value = KDMCONF "/Xsession";
+ ce->active = ce->written = 1;
+ writefile( ce->value, 0755, def_session );
+ }
+}
+
+static void
+upd_language( Entry *ce, Section *cs ATTR_UNUSED )
+{
+ if (!strcmp( ce->value, "C" ))
+ ce->value = (char *)"en_US";
+}
+
+static void
+upd_guistyle( Entry *ce, Section *cs ATTR_UNUSED )
+{
+ if (!strcmp( ce->value, "Motif+" ))
+ ce->value = (char *)"MotifPlus";
+ else if (!strcmp( ce->value, "KDE" ))
+ ce->value = (char *)"Default";
+}
+
+static void
+upd_showusers( Entry *ce, Section *cs )
+{
+ if (!strcmp( ce->value, "All" ))
+ ce->value = (char *)"NotHidden";
+ else if (!strcmp( ce->value, "None" )) {
+ if (ce->active)
+ putfqval( cs->name, "UserList", "false" );
+ ce->value = (char *)"Selected";
+ ce->active = 0;
+ ce->written = 1;
+ }
+}
+
+static const char *defminuid, *defmaxuid;
+
+static void
+upd_minshowuid( Entry *ce, Section *cs ATTR_UNUSED )
+{
+ if (!ce->active) {
+ ce->value = defminuid;
+ ce->active = ce->written = 1;
+ }
+}
+
+static void
+upd_maxshowuid( Entry *ce, Section *cs ATTR_UNUSED )
+{
+ if (!ce->active) {
+ ce->value = defmaxuid;
+ ce->active = ce->written = 1;
+ }
+}
+
+static void
+upd_hiddenusers( Entry *ce, Section *cs ATTR_UNUSED )
+{
+ char *nv;
+ const char *msu, *pt, *et;
+ struct passwd *pw;
+ unsigned minuid, maxuid;
+ char nbuf[128];
+
+ if (!ce->active)
+ return;
+
+ msu = getfqval( cs->name, "MinShowUID", "0" );
+ sscanf( msu, "%u", &minuid );
+ msu = getfqval( cs->name, "MaxShowUID", "65535" );
+ sscanf( msu, "%u", &maxuid );
+
+ nv = 0;
+ pt = ce->value;
+ for (;;) {
+ et = strpbrk( pt, ";," );
+ if (et) {
+ memcpy( nbuf, pt, et - pt );
+ nbuf[et - pt] = 0;
+ } else
+ strcpy( nbuf, pt );
+ if ((pw = getpwnam( nbuf ))) {
+ if (!pw->pw_uid ||
+ (pw->pw_uid >= minuid && pw->pw_uid <= maxuid))
+ {
+ if (nv)
+ StrCat( &nv, ",%s", nbuf );
+ else
+ nv = mstrdup( nbuf );
+ }
+ }
+ if (!et)
+ break;
+ pt = et + 1;
+ }
+ ce->value = nv ? nv : "";
+}
+
+static void
+upd_forgingseed( Entry *ce, Section *cs ATTR_UNUSED )
+{
+ if (!ce->active) {
+ ASPrintf( (char **)&ce->value, "%d", time( 0 ) );
+ ce->active = ce->written = 1;
+ }
+}
+
+static void
+upd_fifodir( Entry *ce, Section *cs ATTR_UNUSED )
+{
+ const char *dir;
+ struct stat st;
+
+ if (use_destdir)
+ return;
+ dir = ce->active ? ce->value : def_FifoDir;
+ stat( dir, &st );
+ chmod( dir, st.st_mode | 0755 );
+}
+
+static void
+upd_datadir( Entry *ce, Section *cs ATTR_UNUSED )
+{
+ char *oldsts, *newsts;
+ const char *dir;
+
+ if (use_destdir)
+ return;
+ dir = ce->active ? ce->value : def_DataDir;
+ if (mkdirp( dir, 0755, "data", 0 ) && oldkde) {
+ ASPrintf( &oldsts, "%s/kdm/kdmsts", oldkde );
+ ASPrintf( &newsts, "%s/kdmsts", dir );
+ rename( oldsts, newsts );
+ }
+}
+
+static void
+CopyFile( const char *from, const char *to )
+{
+ File file;
+ int fd;
+
+ if (readFile( &file, from )) {
+ if ((fd = creat( to, 0644 )) >= 0) {
+ write( fd, file.buf, file.eof - file.buf );
+ close( fd );
+ }
+ freeBuf( &file );
+ }
+}
+
+static void
+upd_facedir( Entry *ce, Section *cs ATTR_UNUSED )
+{
+ char *oldpic, *newpic, *defpic, *rootpic;
+ const char *dir;
+ struct passwd *pw;
+
+ if (use_destdir)
+ return;
+ dir = ce->active ? ce->value : def_FaceDir;
+ if (mkdirp( dir, 0755, "user face", 0 )) {
+ ASPrintf( &defpic, "%s/.default.face.icon", dir );
+ ASPrintf( &rootpic, "%s/root.face.icon", dir );
+ if (oldkde) {
+ setpwent();
+ while ((pw = getpwent()))
+ if (strcmp( pw->pw_name, "root" )) {
+ ASPrintf( &oldpic, "%s/../apps/kdm/pics/users/%s.png",
+ oldkde, pw->pw_name );
+ ASPrintf( &newpic, "%s/%s.face.icon", dir, pw->pw_name );
+ rename( oldpic, newpic );
+ free( newpic );
+ free( oldpic );
+ }
+ endpwent();
+ ASPrintf( &oldpic, "%s/../apps/kdm/pics/users/default.png", oldkde );
+ if (!rename( oldpic, defpic ))
+ defpic = 0;
+ ASPrintf( &oldpic, "%s/../apps/kdm/pics/users/root.png", oldkde );
+ if (!rename( oldpic, rootpic ))
+ rootpic = 0;
+ }
+ if (defpic) {
+ ASPrintf( &oldpic, "%s/default1.png", facesrc );
+ CopyFile( oldpic, defpic );
+ }
+ if (rootpic) {
+ ASPrintf( &oldpic, "%s/root1.png", facesrc );
+ CopyFile( oldpic, rootpic );
+ }
+ }
+}
+
+CONF_GEN_ENTRIES
+
+static Sect *
+findSect( const char *name )
+{
+ const char *p;
+ int i;
+
+ p = strrchr( name, '-' );
+ if (!p)
+ p = name;
+ for (i = 0; i < as(allSects); i++)
+ if (!strcmp( allSects[i]->name, p ))
+ return allSects[i];
+ fprintf( stderr, "Internal error: unknown section %s\n", name );
+ exit( 1 );
+}
+
+static Ent *
+findEnt( Sect *sect, const char *key )
+{
+ int i;
+
+ for (i = 0; i < sect->nents; i++)
+ if (!strcmp( sect->ents[i].key, key ))
+ return sect->ents + i;
+ fprintf( stderr, "Internal error: unknown key %s in section %s\n",
+ key, sect->name );
+ exit( 1 );
+}
+
+
+/*
+ * defaults
+ */
+
+typedef struct DEnt {
+ const char *key;
+ const char *value;
+ int active;
+} DEnt;
+
+typedef struct DSect {
+ const char *name;
+ DEnt *ents;
+ int nents;
+ const char *comment;
+} DSect;
+
+CONF_GEN_EXAMPLE
+
+static void
+mkdefconf( void )
+{
+ Section *cs, **csp;
+ Entry *ce, **cep;
+ int sc, ec;
+
+ for (csp = &config, sc = 0; sc < as(dAllSects); csp = &(cs->next), sc++) {
+ cs = mcalloc( sizeof(*cs) );
+ *csp = cs;
+ cs->spec = findSect( dAllSects[sc].name );
+ cs->name = dAllSects[sc].name;
+ cs->comment = dAllSects[sc].comment;
+ for (cep = &(cs->ents), ec = 0; ec < dAllSects[sc].nents;
+ cep = &(ce->next), ec++)
+ {
+ ce = mcalloc( sizeof(*ce) );
+ *cep = ce;
+ ce->spec = findEnt( cs->spec, dAllSects[sc].ents[ec].key );
+ ce->value = dAllSects[sc].ents[ec].value;
+ ce->active = dAllSects[sc].ents[ec].active;
+ }
+ }
+}
+
+
+/*
+ * read rc file structure
+ */
+
+typedef struct REntry {
+ struct REntry *next;
+ const char *key;
+ char *value;
+} REntry;
+
+typedef struct RSection {
+ struct RSection *next;
+ const char *name;
+ REntry *ents;
+} RSection;
+
+static RSection *
+ReadConf( const char *fname )
+{
+ char *nstr;
+ char *s, *e, *st, *en, *ek, *sl;
+ RSection *rootsec = 0, *cursec;
+ REntry *curent;
+ int nlen;
+ int line, sectmoan;
+ File file;
+
+ if (!readFile( &file, fname ))
+ return 0;
+ usedFile( fname );
+
+ for (s = file.buf, line = 0, cursec = 0, sectmoan = 1; s < file.eof; s++) {
+ line++;
+
+ while ((s < file.eof) && isspace( *s ) && (*s != '\n'))
+ s++;
+
+ if ((s < file.eof) && ((*s == '\n') || (*s == '#'))) {
+ sktoeol:
+ while ((s < file.eof) && (*s != '\n'))
+ s++;
+ continue;
+ }
+ sl = s;
+
+ if (*s == '[') {
+ while ((s < file.eof) && (*s != '\n'))
+ s++;
+ e = s - 1;
+ while ((e > sl) && isspace( *e ))
+ e--;
+ if (*e != ']') {
+ fprintf( stderr, "Invalid section header at %s:%d\n",
+ fname, line );
+ continue;
+ }
+ sectmoan = 0;
+ nstr = sl + 1;
+ nlen = e - nstr;
+ for (cursec = rootsec; cursec; cursec = cursec->next)
+ if (!memcmp( nstr, cursec->name, nlen ) &&
+ !cursec->name[nlen])
+ {
+#if 0 /* not our business ... */
+ fprintf( stderr, "Warning: Multiple occurrences of section "
+ "[%.*s] in %s. Consider merging them.\n",
+ nlen, nstr, fname );
+#endif
+ goto secfnd;
+ }
+ cursec = mmalloc( sizeof(*cursec) );
+ ASPrintf( (char **)&cursec->name, "%.*s", nlen, nstr );
+ cursec->ents = 0;
+ cursec->next = rootsec;
+ rootsec = cursec;
+ secfnd:
+ continue;
+ }
+
+ if (!cursec) {
+ if (sectmoan) {
+ sectmoan = 0;
+ fprintf( stderr, "Entry outside any section at %s:%d",
+ fname, line );
+ }
+ goto sktoeol;
+ }
+
+ for (; (s < file.eof) && (*s != '\n'); s++)
+ if (*s == '=')
+ goto haveeq;
+ fprintf( stderr, "Invalid entry (missing '=') at %s:%d\n", fname, line );
+ continue;
+
+ haveeq:
+ for (ek = s - 1;; ek--) {
+ if (ek < sl) {
+ fprintf( stderr, "Invalid entry (empty key) at %s:%d\n",
+ fname, line );
+ goto sktoeol;
+ }
+ if (!isspace( *ek ))
+ break;
+ }
+
+ s++;
+ while ((s < file.eof) && isspace( *s ) && (*s != '\n'))
+ s++;
+ st = s;
+ while ((s < file.eof) && (*s != '\n'))
+ s++;
+ for (en = s - 1; en >= st && isspace( *en ); en--);
+
+ nstr = sl;
+ nlen = ek - sl + 1;
+ for (curent = cursec->ents; curent; curent = curent->next)
+ if (!memcmp( nstr, curent->key, nlen ) &&
+ !curent->key[nlen]) {
+ fprintf( stderr, "Multiple occurrences of key '%s' in section "
+ "[%s] of %s.\n", curent->key, cursec->name, fname );
+ goto keyfnd;
+ }
+ curent = mmalloc( sizeof(*curent) );
+ ASPrintf( (char **)&curent->key, "%.*s", nlen, nstr );
+ ASPrintf( (char **)&curent->value, "%.*s", en - st + 1, st );
+ curent->next = cursec->ents;
+ cursec->ents = curent;
+ keyfnd:
+ continue;
+ }
+ return rootsec;
+}
+
+
+static int
+mergeKdmRcOld( const char *path )
+{
+ char *p;
+ struct stat st;
+
+ ASPrintf( &p, "%s/kdmrc", path );
+ if (stat( p, &st )) {
+ free( p );
+ return 0;
+ }
+ printf( "Information: ignoring old kdmrc %s from kde < 2.2\n", p );
+ free( p );
+ return 1;
+}
+
+typedef struct {
+ const char *sect, *key, *def;
+ int (*cond)( void );
+} FDefs;
+
+static void
+applydefs( FDefs *chgdef, int ndefs, const char *path )
+{
+ char *p;
+ int i;
+
+ for (i = 0; i < ndefs; i++)
+ if (!getfqval( chgdef[i].sect, chgdef[i].key, 0 ) &&
+ (!chgdef[i].cond || chgdef[i].cond()))
+ {
+ ASPrintf( &p, chgdef[i].def, path );
+ putfqval( chgdef[i].sect, chgdef[i].key, p );
+ free( p );
+ }
+}
+
+#ifdef XDMCP
+static FDefs kdmdefs_all[] = {
+{ "Xdmcp", "Xaccess", "%s/kdm/Xaccess", 0 },
+{ "Xdmcp", "Willing", "", 0 },
+};
+#endif
+
+static FDefs kdmdefs_eq_22[] = {
+{ "General", "PidFile", "/var/run/xdm.pid", 0 },
+{ "X-*-Core", "Setup", "%s/kdm/Xsetup", 0 },
+{ "X-*-Core", "Startup", "%s/kdm/Xstartup", 0 },
+{ "X-*-Core", "Reset", "%s/kdm/Xreset", 0 },
+{ "X-*-Core", "Session", "%s/kdm/Xsession", 0 },
+};
+
+#ifdef XDMCP
+static int
+if_xdmcp (void)
+{
+ return isTrue( getfqval( "Xdmcp", "Enable", "true" ) );
+}
+
+static FDefs kdmdefs_le_30[] = {
+{ "Xdmcp", "KeyFile", "%s/kdm/kdmkeys", if_xdmcp },
+};
+#endif
+
+/* HACK: misused by is22conf() below */
+static FDefs kdmdefs_ge_30[] = {
+{ "X-*-Core", "Setup", "", 0 },
+{ "X-*-Core", "Startup", "", 0 },
+{ "X-*-Core", "Reset", "", 0 },
+{ "X-*-Core", "Session", XBINDIR "/xterm -ls -T", 0 },
+};
+
+static int
+if_usebg (void)
+{
+ return isTrue( getfqval( "X-*-Greeter", "UseBackground", "true" ) );
+}
+
+static FDefs kdmdefs_ge_31[] = {
+{ "X-*-Greeter","BackgroundCfg","%s/kdm/backgroundrc", if_usebg },
+};
+
+static int
+is22conf( const char *path )
+{
+ char *p;
+ const char *val;
+ int i, sl;
+
+ sl = ASPrintf( &p, "%s/kdm/", path );
+ /* safe bet, i guess ... */
+ for (i = 0; i < 4; i++) {
+ val = getfqval( "X-*-Core", kdmdefs_ge_30[i].key, 0 );
+ if (val && !memcmp( val, p, sl )) {
+ free( p );
+ return 0;
+ }
+ }
+ free( p );
+ return 1;
+}
+
+typedef struct KUpdEnt {
+ const char *okey, *nsec, *nkey;
+ void (*func)( const char *sect, char **value );
+} KUpdEnt;
+
+typedef struct KUpdSec {
+ const char *osec;
+ KUpdEnt *ents;
+ int nents;
+} KUpdSec;
+
+#ifdef XDMCP
+static void
+P_EnableChooser( const char *sect ATTR_UNUSED, char **value )
+{
+ *value = (char *)(isTrue( *value ) ? "DefaultLocal" : "LocalOnly");
+}
+#endif
+
+static void
+P_UseLilo( const char *sect ATTR_UNUSED, char **value )
+{
+ *value = (char *)(isTrue( *value ) ? "Lilo" : "None");
+}
+
+CONF_GEN_KMERGE
+
+static int
+mergeKdmRcNewer( const char *path )
+{
+ char *p;
+ const char *cp, *sec, *key;
+ RSection *rootsect, *cs;
+ REntry *ce;
+ int i, j;
+ static char sname[64];
+
+ ASPrintf( &p, "%s/kdm/kdmrc", path );
+ if (!(rootsect = ReadConf( p ))) {
+ free( p );
+ return 0;
+ }
+ printf( "Information: reading current kdmrc %s (from kde >= 2.2.x)\n", p );
+ free( p );
+
+ for (cs = rootsect; cs; cs = cs->next) {
+ if (!strcmp( cs->name, "Desktop0" )) {
+ background = mstrdup( "[Desktop0]\n" );
+ for (ce = cs->ents; ce; ce = ce->next)
+ StrCat( &background, "%s=%s\n", ce->key, ce->value );
+ } else {
+ cp = strrchr( cs->name, '-' );
+ if (!cp)
+ cp = cs->name;
+ else if (cs->name[0] != 'X' || cs->name[1] != '-')
+ goto dropsec;
+ for (i = 0; i < as(kupsects); i++)
+ if (!strcmp( cp, kupsects[i].osec )) {
+ for (ce = cs->ents; ce; ce = ce->next) {
+ for (j = 0; j < kupsects[i].nents; j++)
+ if (!strcmp( ce->key, kupsects[i].ents[j].okey )) {
+ if (kupsects[i].ents[j].nsec == (char *)-1) {
+ kupsects[i].ents[j].func( 0, &ce->value );
+ goto gotkey;
+ }
+ if (!kupsects[i].ents[j].nsec)
+ sec = cs->name;
+ else {
+ sec = sname;
+ sprintf( sname, "%.*s-%s", cp - cs->name, cs->name,
+ kupsects[i].ents[j].nsec );
+ }
+ if (!kupsects[i].ents[j].nkey)
+ key = ce->key;
+ else
+ key = kupsects[i].ents[j].nkey;
+ if (kupsects[i].ents[j].func)
+ kupsects[i].ents[j].func( sec, &ce->value );
+ putfqval( sec, key, ce->value );
+ goto gotkey;
+ }
+ printf( "Information: dropping key %s from section [%s]\n",
+ ce->key, cs->name );
+ gotkey: ;
+ }
+ goto gotsec;
+ }
+ dropsec:
+ printf( "Information: dropping section [%s]\n", cs->name );
+ gotsec: ;
+ }
+ }
+
+#ifdef XDMCP
+ applydefs( kdmdefs_all, as(kdmdefs_all), path );
+#endif
+ if (!*(cp = getfqval( "General", "ConfigVersion", "" ))) { /* < 3.1 */
+ mod_usebg = 1;
+ if (is22conf( path )) {
+ /* work around 2.2.x defaults borkedness */
+ applydefs( kdmdefs_eq_22, as(kdmdefs_eq_22), path );
+ printf( "Information: current kdmrc is from kde 2.2\n" );
+ } else {
+ applydefs( kdmdefs_ge_30, as(kdmdefs_ge_30), path );
+ printf( "Information: current kdmrc is from kde 3.0\n" );
+ }
+#ifdef XDMCP
+ /* work around minor <= 3.0.x defaults borkedness */
+ applydefs( kdmdefs_le_30, as(kdmdefs_le_30), path );
+#endif
+ } else {
+ int ma, mi;
+ sscanf( cp, "%d.%d", &ma, &mi );
+ oldver = (ma << 8) | mi;
+ printf( "Information: current kdmrc is from kde >= 3.1 (config version %d.%d)\n", ma, mi );
+ applydefs( kdmdefs_ge_30, as(kdmdefs_ge_30), path );
+ applydefs( kdmdefs_ge_31, as(kdmdefs_ge_31), path );
+ }
+
+ return 1;
+}
+
+
+typedef struct XResEnt {
+ const char *xname;
+ const char *ksec, *kname;
+ void (*func)( const char *sect, char **value );
+} XResEnt;
+
+static void
+handleXdmVal( const char *dpy, const char *key, char *value,
+ const XResEnt *ents, int nents )
+{
+ const char *kname;
+ int i;
+ char knameb[80], sname[80];
+
+ for (i = 0; i < nents; i++)
+ if (!strcmp( key, ents[i].xname ) ||
+ (key[0] == toupper( ents[i].xname[0] ) &&
+ !strcmp( key + 1, ents[i].xname + 1 )))
+ {
+ if (ents[i].ksec == (char *)-1) {
+ ents[i].func (0, &value);
+ break;
+ }
+ sprintf( sname, ents[i].ksec, dpy );
+ if (ents[i].kname)
+ kname = ents[i].kname;
+ else {
+ kname = knameb;
+ sprintf( knameb, "%c%s",
+ toupper( ents[i].xname[0] ), ents[i].xname + 1 );
+ }
+ if (ents[i].func)
+ ents[i].func( sname, &value );
+ putfqval( sname, kname, value );
+ break;
+ }
+}
+
+static void
+P_List( const char *sect ATTR_UNUSED, char **value )
+{
+ int is, d, s;
+ char *st;
+
+ for (st = *value, is = d = s = 0; st[s]; s++)
+ if (st[s] == ' ' || st[s] == '\t') {
+ if (!is)
+ st[d++] = ',';
+ is = 1;
+ } else {
+ st[d++] = st[s];
+ is = 0;
+ }
+ st[d] = 0;
+}
+
+static void
+P_authDir( const char *sect ATTR_UNUSED, char **value )
+{
+ int l;
+
+ l = strlen( *value );
+ if (l < 4) {
+ *value = 0;
+ return;
+ }
+ if ((*value)[l-1] == '/')
+ (*value)[--l] = 0;
+ if (!strncmp( *value, "/tmp/", 5 ) ||
+ !strncmp( *value, "/var/tmp/", 9 ))
+ {
+ printf( "Warning: Resetting inappropriate value %s for AuthDir to default\n",
+ *value );
+ *value = 0;
+ return;
+ }
+ if ((l >= 4 && !strcmp( *value + l - 4, "/tmp" )) ||
+ (l >= 6 && !strcmp( *value + l - 6, "/xauth" )) ||
+ (l >= 8 && !strcmp( *value + l - 8, "/authdir" )) ||
+ (l >= 10 && !strcmp( *value + l - 10, "/authfiles" )))
+ return;
+ ASPrintf( value, "%s/authdir", *value );
+}
+
+static void
+P_openDelay( const char *sect, char **value )
+{
+ putfqval( sect, "ServerTimeout", *value );
+}
+
+static void
+P_noPassUsers( const char *sect, char **value ATTR_UNUSED )
+{
+ putfqval( sect, "NoPassEnable", "true" );
+}
+
+static void
+P_autoUser( const char *sect, char **value ATTR_UNUSED )
+{
+ putfqval( sect, "AutoLoginEnable", "true" );
+}
+
+#ifdef XDMCP
+static void
+P_requestPort( const char *sect, char **value )
+{
+ if (!strcmp( *value, "0" )) {
+ *value = 0;
+ putfqval( sect, "Enable", "false" );
+ } else
+ putfqval( sect, "Enable", "true" );
+}
+#endif
+
+static int kdmrcmode = 0644;
+
+static void
+P_autoPass( const char *sect ATTR_UNUSED, char **value ATTR_UNUSED )
+{
+ kdmrcmode = 0600;
+}
+
+CONF_GEN_XMERGE
+
+static XrmQuark XrmQString, empty = NULLQUARK;
+
+static Bool
+DumpEntry( XrmDatabase *db ATTR_UNUSED,
+ XrmBindingList bindings,
+ XrmQuarkList quarks,
+ XrmRepresentation *type,
+ XrmValuePtr value,
+ XPointer data ATTR_UNUSED )
+{
+ const char *dpy, *key;
+ int el, hasu;
+ char dpybuf[80];
+
+ if (*type != XrmQString)
+ return False;
+ if (*bindings == XrmBindLoosely ||
+ strcmp( XrmQuarkToString (*quarks), "DisplayManager" ))
+ return False;
+ bindings++, quarks++;
+ if (!*quarks)
+ return False;
+ if (*bindings != XrmBindLoosely && !quarks[1]) { /* DM.foo */
+ key = XrmQuarkToString (*quarks);
+ handleXdmVal( 0, key, value->addr, globents, as(globents) );
+ return False;
+ } else if (*bindings == XrmBindLoosely && !quarks[1]) { /* DM*bar */
+ dpy = "*";
+ key = XrmQuarkToString (*quarks);
+ } else if (*bindings != XrmBindLoosely && quarks[1] &&
+ *bindings != XrmBindLoosely && !quarks[2])
+ { /* DM.foo.bar */
+ dpy = dpybuf + 4;
+ strcpy( dpybuf + 4, XrmQuarkToString (*quarks) );
+ for (hasu = 0, el = 4; dpybuf[el]; el++)
+ if (dpybuf[el] == '_')
+ hasu = 1;
+ if (!hasu/* && isupper (dpy[0])*/) {
+ dpy = dpybuf;
+ memcpy( dpybuf, "*:*_", 4 );
+ } else {
+ for (; --el >= 0; )
+ if (dpybuf[el] == '_') {
+ dpybuf[el] = ':';
+ for (; --el >= 4; )
+ if (dpybuf[el] == '_')
+ dpybuf[el] = '.';
+ break;
+ }
+ }
+ key = XrmQuarkToString (quarks[1]);
+ } else
+ return False;
+ handleXdmVal( dpy, key, value->addr, dpyents, as(dpyents) );
+ return False;
+}
+
+static FDefs xdmdefs[] = {
+#ifdef XDMCP
+{ "Xdmcp", "Xaccess", "%s/Xaccess", 0 },
+{ "Xdmcp", "Willing", "", 0 },
+#endif
+{ "X-*-Core", "Setup", "", 0 },
+{ "X-*-Core", "Startup", "", 0 },
+{ "X-*-Core", "Reset", "", 0 },
+{ "X-*-Core", "Session", "", 0 },
+};
+
+static int
+mergeXdmCfg( const char *path )
+{
+ char *p;
+ XrmDatabase db;
+
+ ASPrintf( &p, "%s/xdm-config", path );
+ if ((db = XrmGetFileDatabase( p ))) {
+ printf( "Information: reading current xdm config file %s\n", p );
+ usedFile( p );
+ free( p );
+ XrmEnumerateDatabase( db, &empty, &empty, XrmEnumAllLevels,
+ DumpEntry, (XPointer)0 );
+ applydefs( xdmdefs, as(xdmdefs), path );
+ mod_usebg = 1;
+ return 1;
+ }
+ free( p );
+ return 0;
+}
+
+static void
+fwrapprintf( FILE *f, const char *msg, ... )
+{
+ char *txt, *ftxt, *line;
+ va_list ap;
+ int col, lword, fspace;
+
+ va_start( ap, msg );
+ VASPrintf( &txt, msg, ap );
+ va_end( ap );
+ ftxt = 0;
+ for (line = txt, col = 0, lword = fspace = -1; line[col]; ) {
+ if (line[col] == '\n') {
+ StrCat( &ftxt, "%.*s", ++col, line );
+ line += col;
+ col = 0;
+ lword = fspace = -1;
+ continue;
+ } else if (line[col] == ' ') {
+ if (lword >= 0) {
+ fspace = col;
+ lword = -1;
+ }
+ } else {
+ if (lword < 0)
+ lword = col;
+ if (col >= 78 && fspace >= 0) {
+ StrCat( &ftxt, "%.*s\n", fspace, line );
+ line += lword;
+ col -= lword;
+ lword = 0;
+ fspace = -1;
+ }
+ }
+ col++;
+ }
+ free( txt );
+ fputs( ftxt, f );
+ free( ftxt );
+}
+
+
+static const char *oldkdes[] = {
+ KDE_CONFDIR,
+ "/opt/kde3/share/config",
+ "/usr/local/kde3/share/config",
+
+ "/opt/kde/share/config",
+ "/usr/local/kde/share/config",
+ "/usr/local/share/config",
+ "/usr/share/config",
+
+ "/opt/kde2/share/config",
+ "/usr/local/kde2/share/config",
+};
+
+static const char *oldxdms[] = {
+ "/etc/X11/xdm",
+ XLIBDIR "/xdm",
+};
+
+int main( int argc, char **argv )
+{
+ const char **where;
+ char *newkdmrc;
+ FILE *f;
+ StrList *fp;
+ Section *cs;
+ Entry *ce, **cep;
+ int i, ap, newer, locals, foreigns;
+ int no_old_xdm = 0, no_old_kde = 0;
+ struct stat st;
+ char *nname;
+
+ for (ap = 1; ap < argc; ap++) {
+ if (!strcmp( argv[ap], "--help" )) {
+ printf(
+"genkdmconf - generate configuration files for kdm\n"
+"\n"
+"If an older xdm/kdm configuration is found, its config files are \"absorbed\";\n"
+"if it lives in the new target directory, its scripts are reused (and possibly\n"
+"modified) as well, otherwise the scripts are ignored and default scripts are\n"
+"installed.\n"
+"\n"
+"options:\n"
+" --in /path/to/new/kdm-config-dir\n"
+" In which directory to put the new configuration. You can use this\n"
+" to support a $(DESTDIR), but not to change the final location of\n"
+" the installation - the paths inside the files are not affected.\n"
+" Default is " KDMCONF ".\n"
+" --old-xdm /path/to/old/xdm-dir\n"
+" Where to look for the config files of an xdm/older kdm.\n"
+" Default is to scan /etc/X11/kdm, $XLIBDIR/kdm, /etc/X11/xdm,\n"
+" $XLIBDIR/xdm; there in turn look for kdm-config and xdm-config.\n"
+" Note that you possibly need to use --no-old-kde to make this take effect.\n"
+" --old-kde /path/to/old/kde-config-dir\n"
+" Where to look for the kdmrc of an older kdm.\n"
+" Default is to scan " KDE_CONFDIR " and\n"
+" {/usr,/usr/local,{/opt,/usr/local}/{kde3,kde,kde2,kde1}}/share/config.\n"
+" --no-old\n"
+" Don't look at older xdm/kdm configurations, just create default config.\n"
+" --no-old-xdm\n"
+" Don't look at older xdm configurations.\n"
+" --no-old-kde\n"
+" Don't look at older kdm configurations.\n"
+" --old-scripts\n"
+" Directly use all scripts from the older xdm/kdm configuration.\n"
+" --no-old-scripts\n"
+" Don't use scripts from the older xdm/kdm configuration even if it lives\n"
+" in the new target directory.\n"
+" --old-confs\n"
+" Directly use all ancillary config files from the older xdm/kdm\n"
+" configuration. This is usually a bad idea.\n"
+" --no-backup\n"
+" Overwrite/delete old config files instead of backing them up.\n"
+" --no-in-notice\n"
+" Don't put the notice about --in being used into the generated README.\n"
+);
+ exit( 0 );
+ }
+ if (!strcmp( argv[ap], "--no-old" )) {
+ no_old = 1;
+ continue;
+ }
+ if (!strcmp( argv[ap], "--old-scripts" )) {
+ old_scripts = 1;
+ continue;
+ }
+ if (!strcmp( argv[ap], "--no-old-scripts" )) {
+ no_old_scripts = 1;
+ continue;
+ }
+ if (!strcmp( argv[ap], "--old-confs" )) {
+ old_confs = 1;
+ continue;
+ }
+ if (!strcmp( argv[ap], "--no-old-xdm" )) {
+ no_old_xdm = 1;
+ continue;
+ }
+ if (!strcmp( argv[ap], "--no-old-kde" )) {
+ no_old_kde = 1;
+ continue;
+ }
+ if (!strcmp( argv[ap], "--no-backup" )) {
+ no_backup = 1;
+ continue;
+ }
+ if (!strcmp( argv[ap], "--no-in-notice" )) {
+ no_in_notice = 1;
+ continue;
+ }
+ where = 0;
+ if (!strcmp( argv[ap], "--in" ))
+ where = &newdir;
+ else if (!strcmp( argv[ap], "--old-xdm" ))
+ where = &oldxdm;
+ else if (!strcmp( argv[ap], "--old-kde" ))
+ where = &oldkde;
+ else if (!strcmp( argv[ap], "--face-src" ))
+ where = &facesrc;
+ else {
+ fprintf( stderr, "Unknown command line option '%s', try --help\n", argv[ap] );
+ exit( 1 );
+ }
+ if (ap + 1 == argc || argv[ap + 1][0] == '-') {
+ fprintf( stderr, "Missing argument to option '%s', try --help\n", argv[ap] );
+ exit( 1 );
+ }
+ *where = argv[++ap];
+ }
+ if (memcmp( newdir, KDMCONF, sizeof(KDMCONF) ))
+ use_destdir = 1;
+
+ if (!mkdirp( newdir, 0755, "target", 1 ))
+ exit( 1 );
+
+ mkdefconf();
+ newer = 0;
+ if (no_old) {
+ DIR *dir;
+ if ((dir = opendir( newdir ))) {
+ struct dirent *ent;
+ char bn[PATH_MAX];
+ while ((ent = readdir( dir ))) {
+ int l;
+ if (!strcmp( ent->d_name, "." ) || !strcmp( ent->d_name, ".." ))
+ continue;
+ l = sprintf( bn, "%s/%s", newdir, ent->d_name ); /* cannot overflow (kernel would not allow the creation of a longer path) */
+ if (!stat( bn, &st ) && !S_ISREG( st.st_mode ))
+ continue;
+ if (no_backup || !memcmp( bn + l - 4, ".bak", 5 ))
+ unlink( bn );
+ else
+ displace( bn );
+ }
+ closedir( dir );
+ }
+ } else {
+ if (oldkde) {
+ if (!(newer = mergeKdmRcNewer( oldkde )) && !mergeKdmRcOld( oldkde ))
+ fprintf( stderr,
+ "Cannot read old kdmrc at specified location\n" );
+ } else if (!no_old_kde) {
+ for (i = 0; i < as(oldkdes); i++) {
+ if ((newer = mergeKdmRcNewer( oldkdes[i] )) ||
+ mergeKdmRcOld( oldkdes[i] )) {
+ oldkde = oldkdes[i];
+ break;
+ }
+ }
+ }
+ if (!newer && !no_old_xdm) {
+ XrmInitialize();
+ XrmQString = XrmPermStringToQuark( "String" );
+ if (oldxdm) {
+ if (!mergeXdmCfg( oldxdm ))
+ fprintf( stderr,
+ "Cannot read old kdm-config/xdm-config at specified location\n" );
+ } else
+ for (i = 0; i < as(oldxdms); i++)
+ if (mergeXdmCfg( oldxdms[i] )) {
+ oldxdm = oldxdms[i];
+ break;
+ }
+ } else
+ oldxdm = 0;
+ }
+ if (no_old_scripts)
+ goto no_old_s;
+ if (!old_scripts) {
+ locals = foreigns = 0;
+ for (cs = config; cs; cs = cs->next)
+ if (!strcmp( cs->spec->name, "-Core" )) {
+ for (ce = cs->ents; ce; ce = ce->next)
+ if (ce->active &&
+ (!strcmp( ce->spec->key, "Setup" ) ||
+ !strcmp( ce->spec->key, "Startup" ) ||
+ !strcmp( ce->spec->key, "Reset" )))
+ {
+ if (inNewDir( ce->value ))
+ locals = 1;
+ else
+ foreigns = 1;
+ }
+ }
+ if (foreigns) {
+ if (locals) {
+ fprintf( stderr,
+ "Warning: both local and foreign scripts referenced. "
+ "Won't touch any.\n" );
+ mixed_scripts = 1;
+ } else {
+ no_old_s:
+ for (cs = config; cs; cs = cs->next) {
+ if (!strcmp( cs->spec->name, "Xdmcp" )) {
+ for (ce = cs->ents; ce; ce = ce->next)
+ if (!strcmp( ce->spec->key, "Willing" ))
+ ce->active = ce->written = 0;
+ } else if (!strcmp( cs->spec->name, "-Core" )) {
+ for (cep = &cs->ents; (ce = *cep); ) {
+ if (ce->active &&
+ (!strcmp( ce->spec->key, "Setup" ) ||
+ !strcmp( ce->spec->key, "Startup" ) ||
+ !strcmp( ce->spec->key, "Reset" ) ||
+ !strcmp( ce->spec->key, "Session" )))
+ {
+ if (!memcmp( cs->name, "X-*-", 4 ))
+ ce->active = ce->written = 0;
+ else {
+ *cep = ce->next;
+ free( ce );
+ continue;
+ }
+ }
+ cep = &ce->next;
+ }
+ }
+ }
+ }
+ }
+ }
+#ifdef __linux__
+ if (!stat( "/etc/debian_version", &st )) { /* debian */
+ defminuid = "1000";
+ defmaxuid = "29999";
+ } else if (!stat( "/usr/portage", &st )) { /* gentoo */
+ defminuid = "1000";
+ defmaxuid = "65000";
+ } else if (!stat( "/etc/mandrake-release", &st )) { /* mandrake - check before redhat! */
+ defminuid = "500";
+ defmaxuid = "65000";
+ } else if (!stat( "/etc/redhat-release", &st )) { /* redhat */
+ defminuid = "100";
+ defmaxuid = "65000";
+ } else /* if (!stat( "/etc/SuSE-release", &st )) */ { /* suse */
+ defminuid = "500";
+ defmaxuid = "65000";
+ }
+#else
+ defminuid = "1000";
+ defmaxuid = "65000";
+#endif
+ for (i = 0; i < CONF_MAX_PRIO; i++)
+ for (cs = config; cs; cs = cs->next)
+ for (ce = cs->ents; ce; ce = ce->next)
+ if (ce->spec->func && i == ce->spec->prio)
+ ce->spec->func( ce, cs );
+ ASPrintf( &newkdmrc, "%s/kdmrc", newdir );
+ f = Create( newkdmrc, kdmrcmode );
+ wrconf( f );
+ fclose( f );
+
+ ASPrintf( &nname, "%s/README", newdir );
+ f = Create( nname, 0644 );
+ fprintf( f,
+"This automatically generated configuration consists of the following files:\n" );
+ fprintf( f, "- " KDMCONF "/kdmrc\n" );
+ for (fp = aflist; fp; fp = fp->next)
+ fprintf( f, "- %s\n", fp->str );
+ if (use_destdir && !no_in_notice)
+ fwrapprintf( f,
+"All files destined for " KDMCONF " were actually saved in %s; "
+"this config won't be workable until moved in place.\n", newdir );
+ if (uflist || eflist || cflist || lflist) {
+ fprintf( f,
+"\n"
+"This config was derived from existing files. As the used algorithms are\n"
+"pretty dumb, it may be broken.\n" );
+ if (uflist) {
+ fprintf( f,
+"Information from these files was extracted:\n" );
+ for (fp = uflist; fp; fp = fp->next)
+ fprintf( f, "- %s\n", fp->str );
+ }
+ if (lflist) {
+ fprintf( f,
+"These files were directly incorporated:\n" );
+ for (fp = lflist; fp; fp = fp->next)
+ fprintf( f, "- %s\n", fp->str );
+ }
+ if (cflist) {
+ fprintf( f,
+"These files were copied verbatim:\n" );
+ for (fp = cflist; fp; fp = fp->next)
+ fprintf( f, "- %s\n", fp->str );
+ }
+ if (eflist) {
+ fprintf( f,
+"These files were copied with modifications:\n" );
+ for (fp = eflist; fp; fp = fp->next)
+ fprintf( f, "- %s\n", fp->str );
+ }
+ if (!no_backup && !use_destdir)
+ fprintf( f,
+"Old files that would have been overwritten were renamed to <oldname>.bak.\n" );
+ }
+ fprintf( f,
+"\nTry 'genkdmconf --help' if you want to generate another configuration.\n"
+"\nYou may delete this README.\n" );
+ fclose( f );
+
+ return 0;
+}
diff --git a/kdm/kfrontend/kchooser.cpp b/kdm/kfrontend/kchooser.cpp
new file mode 100644
index 000000000..6d0b20350
--- /dev/null
+++ b/kdm/kfrontend/kchooser.cpp
@@ -0,0 +1,227 @@
+/*
+
+chooser widget for KDM
+
+Copyright (C) 2002-2003 Oswald Buddenhagen <ossi@kde.org>
+based on the chooser (C) 1999 by Harald Hoyer <Harald.Hoyer@RedHat.de>
+
+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.
+
+*/
+
+#include <config.h>
+
+#ifdef XDMCP
+
+#include "kchooser.h"
+#include "kconsole.h"
+#include "kdmconfig.h"
+#include "kdm_greet.h"
+
+#include <klocale.h>
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qpushbutton.h>
+#include <qpopupmenu.h>
+#include <qsocketnotifier.h>
+#include <qlistview.h>
+#include <qlineedit.h>
+
+#include <stdlib.h> // for free()
+
+class ChooserListViewItem : public QListViewItem {
+ public:
+ ChooserListViewItem( QListView* parent, int _id, const QString& nam, const QString& sts )
+ : QListViewItem( parent, nam, sts ) { id = _id; };
+
+ int id;
+};
+
+
+ChooserDlg::ChooserDlg()
+ : inherited()
+{
+ completeMenu( LOGIN_REMOTE_ONLY, ex_greet, i18n("&Local Login"), ALT+Key_L );
+
+ QBoxLayout *vbox = new QVBoxLayout( this, 10, 10 );
+
+ QLabel *title = new QLabel( i18n("XDMCP Host Menu"), this );
+ title->setAlignment( AlignCenter );
+ vbox->addWidget( title );
+
+ host_view = new QListView( this, "hosts" );
+ host_view->addColumn( i18n("Hostname") );
+ host_view->setColumnWidth( 0, fontMetrics().width( "login.crap.net" ) );
+ host_view->addColumn( i18n("Status") );
+ host_view->setMinimumWidth( fontMetrics().width( "login.crap.com Display not authorized to connect this server" ) );
+ host_view->setResizeMode( QListView::LastColumn );
+ host_view->setAllColumnsShowFocus( true );
+ vbox->addWidget( host_view );
+
+ iline = new QLineEdit( this );
+ iline->setEnabled( TRUE );
+ QLabel *itxt = new QLabel( iline, i18n("Hos&t:"), this );
+ QPushButton *addButton = new QPushButton( i18n("A&dd"), this );
+ connect( addButton, SIGNAL(clicked()), SLOT(addHostname()) );
+ QBoxLayout *hibox = new QHBoxLayout( vbox, 10 );
+ hibox->addWidget( itxt );
+ hibox->addWidget( iline );
+ hibox->addWidget( addButton );
+
+ // Buttons
+ QPushButton *acceptButton = new QPushButton( i18n("&Accept"), this );
+ acceptButton->setDefault( true );
+ QPushButton *pingButton = new QPushButton( i18n("&Refresh"), this );
+
+ QBoxLayout *hbox = new QHBoxLayout( vbox, 20 );
+ hbox->addWidget( acceptButton );
+ hbox->addWidget( pingButton );
+ hbox->addStretch( 1 );
+
+ if (optMenu) {
+ QPushButton *menuButton = new QPushButton( i18n("&Menu"), this );
+ menuButton->setPopup( optMenu );
+ hbox->addWidget( menuButton );
+ hbox->addStretch( 1 );
+ }
+
+// QPushButton *helpButton = new QPushButton( i18n("&Help"), this );
+// hbox->addWidget( helpButton );
+
+#ifdef WITH_KDM_XCONSOLE
+ if (consoleView)
+ vbox->addWidget( consoleView );
+#endif
+
+ sn = new QSocketNotifier( rfd, QSocketNotifier::Read, this );
+ connect( sn, SIGNAL(activated( int )), SLOT(slotReadPipe()) );
+
+ connect( pingButton, SIGNAL(clicked()), SLOT(pingHosts()) );
+ connect( acceptButton, SIGNAL(clicked()), SLOT(accept()) );
+// connect( helpButton, SIGNAL(clicked()), SLOT(slotHelp()) );
+ connect( host_view, SIGNAL(doubleClicked(QListViewItem *)), SLOT(accept()) );
+
+ adjustGeometry();
+}
+
+/*
+void ChooserDlg::slotHelp()
+{
+ KMessageBox::information(0,
+ i18n("Choose a host, you want to work on,\n"
+ "in the list or add one.\n\n"
+ "After this box, you must press cancel\n"
+ "in the Host Menu to enter a host. :("));
+ iline->setFocus();
+}
+*/
+
+void ChooserDlg::addHostname()
+{
+ if (!iline->text().isEmpty()) {
+ GSendInt( G_Ch_RegisterHost );
+ GSendStr( iline->text().latin1() );
+ iline->clear();
+ }
+}
+
+void ChooserDlg::pingHosts()
+{
+ GSendInt( G_Ch_Refresh );
+}
+
+void ChooserDlg::accept()
+{
+ if (focusWidget() == iline) {
+ if (!iline->text().isEmpty()) {
+ GSendInt( G_Ch_DirectChoice );
+ GSendStr( iline->text().latin1() );
+ iline->clear();
+ }
+ return;
+ } else /*if (focusWidget() == host_view)*/ {
+ QListViewItem *item = host_view->currentItem();
+ if (item) {
+ GSendInt( G_Ready );
+ GSendInt( ((ChooserListViewItem *)item)->id );
+ ::exit( EX_NORMAL );
+ }
+ }
+}
+
+void ChooserDlg::reject()
+{
+}
+
+QString ChooserDlg::recvStr()
+{
+ char *arr = GRecvStr();
+ if (arr) {
+ QString str = QString::fromLatin1( arr );
+ free( arr );
+ return str;
+ } else
+ return i18n("<unknown>");
+}
+
+QListViewItem *ChooserDlg::findItem( int id )
+{
+ QListViewItem *itm;
+ for (QListViewItemIterator it( host_view ); (itm = it.current()); ++it)
+ if (((ChooserListViewItem *)itm)->id == id)
+ return itm;
+ return 0;
+}
+
+void ChooserDlg::slotReadPipe()
+{
+ int id;
+ QString nam, sts;
+
+ int cmd = GRecvInt();
+ switch (cmd) {
+ case G_Ch_AddHost:
+ case G_Ch_ChangeHost:
+ id = GRecvInt();
+ nam = recvStr();
+ sts = recvStr();
+ GRecvInt(); /* swallow willing for now */
+ if (cmd == G_Ch_AddHost)
+ host_view->insertItem(
+ new ChooserListViewItem( host_view, id, nam, sts ) );
+ else {
+ QListViewItem *itm = findItem( id );
+ itm->setText( 0, nam );
+ itm->setText( 1, sts );
+ }
+ break;
+ case G_Ch_RemoveHost:
+ delete findItem( GRecvInt() );
+ break;
+ case G_Ch_BadHost:
+ KFMsgBox::box( this, QMessageBox::Warning, i18n("Unknown host %1").arg( recvStr() ) );
+ break;
+ case G_Ch_Exit:
+ done( ex_exit );
+ break;
+ default: /* XXX huuh ...? */
+ break;
+ }
+}
+
+#include "kchooser.moc"
+
+#endif
diff --git a/kdm/kfrontend/kchooser.h b/kdm/kfrontend/kchooser.h
new file mode 100644
index 000000000..93ede2694
--- /dev/null
+++ b/kdm/kfrontend/kchooser.h
@@ -0,0 +1,59 @@
+/*
+
+chooser widget for KDM
+
+Copyright (C) 2002-2003 Oswald Buddenhagen <ossi@kde.org>
+based on the chooser (C) 1999 by Harald Hoyer <Harald.Hoyer@RedHat.de>
+
+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.
+
+*/
+
+#ifndef KCHOOSER_H
+#define KCHOOSER_H
+
+#include "kgdialog.h"
+
+class QSocketNotifier;
+class QPopupMenu;
+class QLineEdit;
+class QListView;
+class QListViewItem;
+
+class ChooserDlg : public KGDialog {
+ Q_OBJECT
+ typedef KGDialog inherited;
+
+ public:
+ ChooserDlg();
+
+ public slots:
+ void slotReadPipe();
+ void addHostname();
+// void slotHelp();
+ void pingHosts();
+ void accept();
+ void reject();
+
+ private:
+ QString recvStr();
+ QListViewItem *findItem( int id );
+
+ QListView *host_view;
+ QLineEdit *iline;
+ QSocketNotifier *sn;
+};
+
+#endif /* KCHOOSER_H */
diff --git a/kdm/kfrontend/kconsole.cpp b/kdm/kfrontend/kconsole.cpp
new file mode 100644
index 000000000..b101712da
--- /dev/null
+++ b/kdm/kfrontend/kconsole.cpp
@@ -0,0 +1,183 @@
+/*
+
+xconsole widget for KDM
+
+Copyright (C) 2002-2003 Oswald Buddenhagen <ossi@kde.org>
+
+
+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.
+
+*/
+
+#include <config.h>
+
+#ifdef WITH_KDM_XCONSOLE
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+
+#ifdef HAVE_TERMIOS_H
+/* for HP-UX (some versions) the extern C is needed, and for other
+ platforms it doesn't hurt */
+extern "C" {
+#include <termios.h>
+}
+#endif
+#if !defined(__osf__)
+#ifdef HAVE_TERMIO_H
+/* needed at least on AIX */
+#include <termio.h>
+#endif
+#endif
+
+#if defined (_HPUX_SOURCE)
+#define _TERMIOS_INCLUDED
+#include <bsdtty.h>
+#endif
+
+
+#include "kconsole.h"
+#include "kdmconfig.h"
+#include "kdm_greet.h"
+
+#include <klocale.h>
+#include <kpty.h>
+
+#include <qsocketnotifier.h>
+
+KConsole::KConsole( QWidget *_parent )
+ : inherited( _parent )
+ , pty( 0 )
+ , notifier( 0 )
+ , fd( -1 )
+{
+ setReadOnly( true );
+ setWordWrap( NoWrap );
+ setTextFormat( PlainText );
+
+ if (!OpenConsole())
+ append( i18n("Cannot open console") );
+}
+
+KConsole::~KConsole()
+{
+ CloseConsole();
+}
+
+int
+KConsole::OpenConsole()
+{
+#ifdef TIOCCONS
+ static const char on = 1;
+#endif
+
+ if (*_logSource) {
+ if ((fd = open( _logSource, O_RDONLY | O_NONBLOCK )) >= 0)
+ goto gotcon;
+ LogError( "Cannot open log source %s, "
+ "falling back to /dev/console.\n", _logSource );
+ }
+
+ pty = new KPty;
+ if (!pty->open()) {
+ delete pty;
+ pty = 0;
+ return 0;
+ }
+
+#ifdef TIOCCONS
+ if (ioctl( pty->slaveFd(), TIOCCONS, &on ) < 0) {
+ perror( "ioctl TIOCCONS" );
+ delete pty;
+ pty = 0;
+ return 0;
+ }
+#else
+ int consfd;
+ if ((consfd = open( "/dev/console", O_RDONLY )) < 0) {
+ perror( "opening /dev/console" );
+ delete pty;
+ pty = 0;
+ return 0;
+ }
+ if (ioctl( consfd, SRIOCSREDIR, slave_fd ) < 0) {
+ perror( "ioctl SRIOCSREDIR" );
+ ::close( consfd );
+ delete pty;
+ pty = 0;
+ return 0;
+ }
+ ::close( consfd );
+#endif
+ fd = pty->masterFd();
+
+ gotcon:
+ notifier = new QSocketNotifier( fd, QSocketNotifier::Read, this );
+ connect( notifier, SIGNAL(activated( int )), SLOT(slotData()) );
+ return 1;
+}
+
+void
+KConsole::CloseConsole()
+{
+ delete notifier;
+ notifier = 0;
+ if (pty) {
+ delete pty;
+ pty = 0;
+ } else
+ ::close( fd );
+ fd = -1;
+}
+
+void
+KConsole::slotData()
+{
+ int n;
+ char buffer[1024];
+
+ if ((n = read( fd, buffer, sizeof(buffer) )) <= 0) {
+ CloseConsole();
+ if (!n)
+ if (!OpenConsole())
+ append( i18n("\n*** Cannot open console log source ***") );
+ } else {
+ bool as = !verticalScrollBar()->isVisible() ||
+ (verticalScrollBar()->value() ==
+ verticalScrollBar()->maxValue());
+ QString str( QString::fromLocal8Bit( buffer, n ).remove( '\r' ) );
+ int pos, opos;
+ for (opos = 0; (pos = str.find( '\n', opos )) >= 0; opos = pos + 1) {
+ if (paragraphs() == 100)
+ removeParagraph( 0 );
+ if (!leftover.isEmpty()) {
+ append( leftover + str.mid( opos, pos - opos ) );
+ leftover = QString::null;
+ } else
+ append( str.mid( opos, pos - opos ) );
+ }
+ leftover += str.mid( opos );
+ if (as)
+ scrollToBottom();
+ }
+}
+
+#include "kconsole.moc"
+
+#endif
diff --git a/kdm/kfrontend/kconsole.h b/kdm/kfrontend/kconsole.h
new file mode 100644
index 000000000..8a3515173
--- /dev/null
+++ b/kdm/kfrontend/kconsole.h
@@ -0,0 +1,53 @@
+/*
+
+xconsole widget for KDM
+
+Copyright (C) 2002-2003 Oswald Buddenhagen <ossi@kde.org>
+
+
+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.
+
+*/
+
+#ifndef KCONSOLE_H
+#define KCONSOLE_H
+
+#include <qtextedit.h>
+
+class QSocketNotifier;
+class KPty;
+
+class KConsole : public QTextEdit {
+ Q_OBJECT
+ typedef QTextEdit inherited;
+
+ public:
+ KConsole( QWidget *_parent = 0 );
+ ~KConsole();
+
+ private slots:
+ void slotData();
+
+ private:
+ int OpenConsole();
+ void CloseConsole();
+
+ KPty *pty;
+ QSocketNotifier *notifier;
+ QString leftover;
+ int fd;
+};
+
+#endif // KCONSOLE_H
diff --git a/kdm/kfrontend/kdm_config.c b/kdm/kfrontend/kdm_config.c
new file mode 100644
index 000000000..5d188e33d
--- /dev/null
+++ b/kdm/kfrontend/kdm_config.c
@@ -0,0 +1,1470 @@
+/*
+
+Read options from kdmrc
+
+Copyright (C) 2001-2005 Oswald Buddenhagen <ossi@kde.org>
+
+
+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.
+
+*/
+
+#include <config.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <grp.h>
+#ifdef _POSIX_PRIORITY_SCHEDULING
+# include <sched.h>
+#endif
+
+#include <X11/X.h>
+#ifdef FamilyInternet6
+# define IPv6
+#endif
+
+#include <greet.h>
+#include <config.ci>
+
+/*
+ * Section/Entry definition structs
+ */
+
+typedef struct Ent {
+ const char *name;
+ int id;
+ void *ptr;
+ const char *def;
+} Ent;
+
+typedef struct Sect {
+ const char *name;
+ Ent *ents;
+ int numents;
+} Sect;
+
+/*
+ * Parsed ini file structs
+ */
+
+typedef struct Entry {
+ struct Entry *next;
+ const char *val;
+ Ent *ent;
+ int vallen;
+ int line;
+} Entry;
+
+typedef struct Section {
+ struct Section *next;
+ Entry *entries;
+ Sect *sect;
+ const char *name, *dname, *dhost, *dnum, *dclass;
+ int nlen, dlen, dhostl, dnuml, dclassl;
+} Section;
+
+
+/*
+ * Split up display-name/-class for fast comparison
+ */
+typedef struct DSpec {
+ const char *dhost, *dnum, *dclass;
+ int dhostl, dnuml, dclassl;
+} DSpec;
+
+
+/*
+ * Config value storage structures
+ */
+
+typedef struct Value {
+ const char *ptr;
+ int len;
+} Value;
+
+typedef struct Val {
+ Value val;
+ int id;
+} Val;
+
+typedef struct ValArr {
+ Val *ents;
+ int nents, esiz, nchars, nptrs;
+} ValArr;
+
+
+static void *Malloc( size_t size );
+static void *Realloc( void *ptr, size_t size );
+
+#define PRINT_QUOTES
+#define LOG_NAME "kdm_config"
+#define LOG_DEBUG_MASK DEBUG_CONFIG
+#define LOG_PANIC_EXIT 1
+#define STATIC static
+#include <printf.c>
+
+
+static void *
+Malloc( size_t size )
+{
+ void *ret;
+
+ if (!(ret = malloc( size )))
+ LogOutOfMem();
+ return ret;
+}
+
+static void *
+Realloc( void *ptr, size_t size )
+{
+ void *ret;
+
+ if (!(ret = realloc( ptr, size )) && size)
+ LogOutOfMem();
+ return ret;
+}
+
+
+static void
+MkDSpec( DSpec *spec, const char *dname, const char *dclass )
+{
+ spec->dhost = dname;
+ for (spec->dhostl = 0; dname[spec->dhostl] != ':'; spec->dhostl++);
+ spec->dnum = dname + spec->dhostl + 1;
+ spec->dnuml = strlen( spec->dnum );
+ spec->dclass = dclass;
+ spec->dclassl = strlen( dclass );
+}
+
+
+static int rfd, wfd;
+
+static int
+Reader( void *buf, int count )
+{
+ int ret, rlen;
+
+ for (rlen = 0; rlen < count; ) {
+ dord:
+ ret = read( rfd, (void *)((char *)buf + rlen), count - rlen );
+ if (ret < 0) {
+ if (errno == EINTR)
+ goto dord;
+ if (errno == EAGAIN)
+ break;
+ return -1;
+ }
+ if (!ret)
+ break;
+ rlen += ret;
+ }
+ return rlen;
+}
+
+static void
+GRead( void *buf, int count )
+{
+ if (Reader( buf, count ) != count)
+ LogPanic( "Can't read from core\n" );
+}
+
+static void
+GWrite( const void *buf, int count )
+{
+ if (write( wfd, buf, count ) != count)
+ LogPanic( "Can't write to core\n" );
+#ifdef _POSIX_PRIORITY_SCHEDULING
+ if ((debugLevel & DEBUG_HLPCON))
+ sched_yield();
+#endif
+}
+
+static void
+GSendInt( int val )
+{
+ GWrite( &val, sizeof(val) );
+}
+
+static void
+GSendStr( const char *buf )
+{
+ if (buf) {
+ int len = strlen( buf ) + 1;
+ GWrite( &len, sizeof(len) );
+ GWrite( buf, len );
+ } else
+ GWrite( &buf, sizeof(int));
+}
+
+static void
+GSendNStr( const char *buf, int len )
+{
+ int tlen = len + 1;
+ GWrite( &tlen, sizeof(tlen) );
+ GWrite( buf, len );
+ GWrite( "", 1 );
+}
+
+#ifdef XDMCP
+static void
+GSendArr( int len, const char *data )
+{
+ GWrite( &len, sizeof(len) );
+ GWrite( data, len );
+}
+#endif
+
+static int
+GRecvCmd( int *val )
+{
+ if (Reader( val, sizeof(*val) ) != sizeof(*val))
+ return 0;
+ return 1;
+}
+
+static int
+GRecvInt()
+{
+ int val;
+
+ GRead( &val, sizeof(val) );
+ return val;
+}
+
+static char *
+GRecvStr()
+{
+ int len;
+ char *buf;
+
+ len = GRecvInt();
+ if (!len)
+ return 0;
+ if (!(buf = malloc( len )))
+ LogPanic( "No memory for read buffer" );
+ GRead( buf, len );
+ return buf;
+}
+
+
+/* #define WANT_CLOSE 1 */
+
+typedef struct File {
+ char *buf, *eof, *cur;
+#if defined(HAVE_MMAP) && defined(WANT_CLOSE)
+ int ismapped;
+#endif
+} File;
+
+static int
+readFile( File *file, const char *fn, const char *what )
+{
+ int fd;
+ off_t flen;
+
+ if ((fd = open( fn, O_RDONLY )) < 0) {
+ LogInfo( "Cannot open %s file %s\n", what, fn );
+ return 0;
+ }
+
+ flen = lseek( fd, 0, SEEK_END );
+#ifdef HAVE_MMAP
+# ifdef WANT_CLOSE
+ file->ismapped = 0;
+# endif
+ file->buf = mmap( 0, flen + 1, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0 );
+# ifdef WANT_CLOSE
+ if (file->buf)
+ file->ismapped = 1;
+ else
+# else
+ if (!file->buf)
+# endif
+#endif
+ {
+ if (!(file->buf = Malloc( flen + 1 ))) {
+ close( fd );
+ return 0;
+ }
+ lseek( fd, 0, SEEK_SET );
+ if (read( fd, file->buf, flen ) != flen) {
+ free( file->buf );
+ LogError( "Cannot read %s file %s\n", what, fn );
+ close( fd );
+ return 0;
+ }
+ }
+ file->eof = (file->cur = file->buf) + flen;
+ close( fd );
+ return 1;
+}
+
+#ifdef WANT_CLOSE
+static void
+freeBuf( File *file )
+{
+# ifdef HAVE_MMAP
+ if (file->ismapped)
+ munmap( file->buf, file->eof - file->buf + 1 );
+ else
+# endif
+ free( file->buf );
+}
+#endif
+
+CONF_READ_VARS
+
+#define C_MTYPE_MASK 0x30000000
+# define C_PATH 0x10000000 /* C_TYPE_STR is a path spec */
+# define C_BOOL 0x10000000 /* C_TYPE_INT is a boolean */
+# define C_ENUM 0x20000000 /* C_TYPE_INT is an enum (option) */
+# define C_GRP 0x30000000 /* C_TYPE_INT is a group spec */
+#define C_INTERNAL 0x40000000 /* don't expose to core */
+#define C_CONFIG 0x80000000 /* process only for finding deps */
+
+#ifdef XDMCP
+static int
+PrequestPort( Value *retval )
+{
+ if (!VxdmcpEnable.ptr) {
+ retval->ptr = (char *)0;
+ return 1;
+ }
+ return 0;
+}
+#endif
+
+static Value
+ emptyStr = { "", 1 },
+ nullValue = { 0, 0 },
+ emptyArgv = { (char *)&nullValue, 0 };
+
+static int
+PnoPassUsers( Value *retval )
+{
+ if (!VnoPassEnable.ptr) {
+ *retval = emptyArgv;
+ return 1;
+ }
+ return 0;
+}
+
+static int
+PautoLoginX( Value *retval )
+{
+ if (!VautoLoginEnable.ptr) {
+ *retval = emptyStr;
+ return 1;
+ }
+ return 0;
+}
+
+CONF_READ_ENTRIES
+
+static const char *kdmrc = KDMCONF "/kdmrc";
+
+static Section *rootsec;
+
+static void
+ReadConf()
+{
+ const char *nstr, *dstr, *cstr, *dhost, *dnum, *dclass;
+ char *s, *e, *st, *en, *ek, *sl, *pt;
+ Section *cursec;
+ Entry *curent;
+ Ent *ce;
+ int nlen, dlen, clen, dhostl, dnuml, dclassl;
+ int i, line, sectmoan, restl;
+ File file;
+ static int confread;
+
+ if (confread)
+ return;
+ confread = 1;
+
+ Debug( "reading config %s ...\n", kdmrc );
+ if (!readFile( &file, kdmrc, "master configuration" ))
+ return;
+
+ for (s = file.buf, line = 0, cursec = 0, sectmoan = 1; s < file.eof; s++) {
+ line++;
+
+ while ((s < file.eof) && isspace( *s ) && (*s != '\n'))
+ s++;
+
+ if ((s < file.eof) && ((*s == '\n') || (*s == '#'))) {
+ sktoeol:
+ while ((s < file.eof) && (*s != '\n'))
+ s++;
+ continue;
+ }
+ sl = s;
+
+ if (*s == '[') {
+ sectmoan = 0;
+ while ((s < file.eof) && (*s != '\n'))
+ s++;
+ e = s - 1;
+ while ((e > sl) && isspace( *e ))
+ e--;
+ if (*e != ']') {
+ cursec = 0;
+ LogError( "Invalid section header at %s:%d\n", kdmrc, line );
+ continue;
+ }
+ nstr = sl + 1;
+ nlen = e - nstr;
+ for (cursec = rootsec; cursec; cursec = cursec->next)
+ if (nlen == cursec->nlen &&
+ !memcmp( nstr, cursec->name, nlen ))
+ {
+ LogInfo( "Multiple occurrences of section [%.*s] in %s. "
+ "Consider merging them.\n", nlen, nstr, kdmrc );
+ goto secfnd;
+ }
+ if (nstr[0] == 'X' && nstr[1] == '-') {
+ cstr = nstr + nlen;
+ clen = 0;
+ while (++clen, *--cstr != '-');
+ if (cstr == nstr + 1)
+ goto illsec;
+ dstr = nstr + 2;
+ dlen = nlen - clen - 2;
+ dhost = dstr;
+ dhostl = 0;
+ for (restl = dlen; restl; restl--) {
+ if (dhost[dhostl] == ':') {
+ dnum = dhost + dhostl + 1;
+ dnuml = 0;
+ for (restl--; restl; restl--) {
+ if (dnum[dnuml] == '_') {
+ dclass = dnum + dnuml + 1;
+ dclassl = restl;
+ goto gotall;
+ }
+ dnuml++;
+ }
+ goto gotnum;
+ }
+ dhostl++;
+ }
+ dnum = "*";
+ dnuml = 1;
+ gotnum:
+ dclass = "*";
+ dclassl = 1;
+ gotall: ;
+ } else {
+ if (nstr[0] == '-')
+ goto illsec;
+ dstr = 0;
+ dlen = 0;
+ dhost = 0;
+ dhostl = 0;
+ dnum = 0;
+ dnuml = 0;
+ dclass = 0;
+ dclassl = 0;
+ cstr = nstr;
+ clen = nlen;
+ }
+ for (i = 0; i < as(allSects); i++)
+ if ((int)strlen( allSects[i]->name ) == clen &&
+ !memcmp( allSects[i]->name, cstr, clen ))
+ goto newsec;
+ illsec:
+ cursec = 0;
+ LogError( "Unrecognized section name [%.*s] at %s:%d\n",
+ nlen, nstr, kdmrc, line );
+ continue;
+ newsec:
+ if (!(cursec = Malloc( sizeof(*cursec) )))
+ return;
+ cursec->name = nstr;
+ cursec->nlen = nlen;
+ cursec->dname = dstr;
+ cursec->dlen = dlen;
+ cursec->dhost = dhost;
+ cursec->dhostl = dhostl;
+ cursec->dnum = dnum;
+ cursec->dnuml = dnuml;
+ cursec->dclass = dclass;
+ cursec->dclassl = dclassl;
+ cursec->sect = allSects[i];
+ cursec->entries = 0;
+ cursec->next = rootsec;
+ rootsec = cursec;
+ /*Debug( "now in section [%.*s], dpy '%.*s', core '%.*s'\n",
+ nlen, nstr, dlen, dstr, clen, cstr );*/
+ secfnd:
+ continue;
+ }
+
+ if (!cursec) {
+ if (sectmoan) {
+ sectmoan = 0;
+ LogError( "Entry outside any section at %s:%d", kdmrc, line );
+ }
+ goto sktoeol;
+ }
+
+ for (; (s < file.eof) && (*s != '\n'); s++)
+ if (*s == '=')
+ goto haveeq;
+ LogError( "Invalid entry (missing '=') at %s:%d\n", kdmrc, line );
+ continue;
+
+ haveeq:
+ for (ek = s - 1; ; ek--) {
+ if (ek < sl) {
+ LogError( "Invalid entry (empty key) at %s:%d\n", kdmrc, line );
+ goto sktoeol;
+ }
+ if (!isspace( *ek ))
+ break;
+ }
+
+ s++;
+ while ((s < file.eof) && isspace( *s ) && (*s != '\n'))
+ s++;
+ for (pt = st = en = s; s < file.eof && *s != '\n'; s++) {
+ if (*s == '\\') {
+ s++;
+ if (s >= file.eof || *s == '\n') {
+ LogError( "Trailing backslash at %s:%d\n", kdmrc, line );
+ break;
+ }
+ switch (*s) {
+ case 's': *pt++ = ' '; break;
+ case 't': *pt++ = '\t'; break;
+ case 'n': *pt++ = '\n'; break;
+ case 'r': *pt++ = '\r'; break;
+ case '\\': *pt++ = '\\'; break;
+ default: *pt++ = '\\'; *pt++ = *s; break;
+ }
+ en = pt;
+ } else {
+ *pt++ = *s;
+ if (*s != ' ' && *s != '\t')
+ en = pt;
+ }
+ }
+
+ nstr = sl;
+ nlen = ek - sl + 1;
+ /*Debug( "read entry '%.*s'='%.*s'\n", nlen, nstr, en - st, st );*/
+ for (i = 0; i < cursec->sect->numents; i++) {
+ ce = cursec->sect->ents + i;
+ if ((int)strlen( ce->name ) == nlen &&
+ !memcmp( ce->name, nstr, nlen ))
+ goto keyok;
+ }
+ LogError( "Unrecognized key '%.*s' in section [%.*s] at %s:%d\n",
+ nlen, nstr, cursec->nlen, cursec->name, kdmrc, line );
+ continue;
+ keyok:
+ for (curent = cursec->entries; curent; curent = curent->next)
+ if (ce == curent->ent) {
+ LogError( "Multiple occurrences of key '%s' in section [%.*s]"
+ " of %s\n",
+ ce->name, cursec->nlen, cursec->name, kdmrc );
+ goto keyfnd;
+ }
+ if (!(curent = Malloc( sizeof(*curent) )))
+ return;
+ curent->ent = ce;
+ curent->line = line;
+ curent->val = st;
+ curent->vallen = en - st;
+ curent->next = cursec->entries;
+ cursec->entries = curent;
+ keyfnd:
+ continue;
+ }
+}
+
+static Entry *
+FindGEnt( int id )
+{
+ Section *cursec;
+ Entry *curent;
+
+ for (cursec = rootsec; cursec; cursec = cursec->next)
+ if (!cursec->dname)
+ for (curent = cursec->entries; curent; curent = curent->next)
+ if (curent->ent->id == id) {
+ Debug( "line %d: %s = %'.*s\n",
+ curent->line, curent->ent->name,
+ curent->vallen, curent->val );
+ return curent;
+ }
+ return 0;
+}
+
+/* Display name match scoring:
+ * - class (any/exact) -> 0/1
+ * - number (any/exact) -> 0/2
+ * - host (any/nonempty/trail/exact) -> 0/4/8/12
+ */
+static Entry *
+FindDEnt( int id, DSpec *dspec )
+{
+ Section *cursec, *bestsec;
+ Entry *curent, *bestent;
+ int score, bestscore;
+
+ bestscore = -1, bestent = 0;
+ for (cursec = rootsec; cursec; cursec = cursec->next)
+ if (cursec->dname) {
+ score = 0;
+ if (cursec->dclassl != 1 || cursec->dclass[0] != '*') {
+ if (cursec->dclassl == dspec->dclassl &&
+ !memcmp( cursec->dclass, dspec->dclass, dspec->dclassl ))
+ score = 1;
+ else
+ continue;
+ }
+ if (cursec->dnuml != 1 || cursec->dnum[0] != '*') {
+ if (cursec->dnuml == dspec->dnuml &&
+ !memcmp( cursec->dnum, dspec->dnum, dspec->dnuml ))
+ score += 2;
+ else
+ continue;
+ }
+ if (cursec->dhostl != 1 || cursec->dhost[0] != '*') {
+ if (cursec->dhostl == 1 && cursec->dhost[0] == '+') {
+ if (dspec->dhostl)
+ score += 4;
+ else
+ continue;
+ } else if (cursec->dhost[0] == '.') {
+ if (cursec->dhostl < dspec->dhostl &&
+ !memcmp( cursec->dhost,
+ dspec->dhost + dspec->dhostl - cursec->dhostl,
+ cursec->dhostl ))
+ score += 8;
+ else
+ continue;
+ } else {
+ if (cursec->dhostl == dspec->dhostl &&
+ !memcmp( cursec->dhost, dspec->dhost, dspec->dhostl ))
+ score += 12;
+ else
+ continue;
+ }
+ }
+ if (score > bestscore) {
+ for (curent = cursec->entries; curent; curent = curent->next)
+ if (curent->ent->id == id) {
+ bestent = curent;
+ bestsec = cursec;
+ bestscore = score;
+ break;
+ }
+ }
+ }
+ if (bestent)
+ Debug( "line %d: %.*s:%.*s_%.*s/%s = %'.*s\n", bestent->line,
+ bestsec->dhostl, bestsec->dhost,
+ bestsec->dnuml, bestsec->dnum,
+ bestsec->dclassl, bestsec->dclass,
+ bestent->ent->name, bestent->vallen, bestent->val );
+ return bestent;
+}
+
+static const char *
+CvtValue( Ent *et, Value *retval, int vallen, const char *val, char **eopts )
+{
+ Value *ents;
+ int i, b, e, tlen, nents, esiz;
+ char buf[80];
+
+ switch (et->id & C_TYPE_MASK) {
+ case C_TYPE_INT:
+ for (i = 0; i < vallen && i < (int)sizeof(buf) - 1; i++)
+ buf[i] = tolower( val[i] );
+ buf[i] = 0;
+ if ((et->id & C_MTYPE_MASK) == C_BOOL) {
+ if (!strcmp( buf, "true" ) ||
+ !strcmp( buf, "on" ) ||
+ !strcmp( buf, "yes" ) ||
+ !strcmp( buf, "1" ))
+ retval->ptr = (char *)1;
+ else if (!strcmp( buf, "false" ) ||
+ !strcmp( buf, "off" ) ||
+ !strcmp( buf, "no" ) ||
+ !strcmp( buf, "0" ))
+ retval->ptr = (char *)0;
+ else
+ return "boolean";
+ return 0;
+ } else if ((et->id & C_MTYPE_MASK) == C_ENUM) {
+ for (i = 0; eopts[i]; i++)
+ if (!memcmp( eopts[i], val, vallen ) && !eopts[i][vallen]) {
+ retval->ptr = (char *)i;
+ return 0;
+ }
+ return "option";
+ } else if ((et->id & C_MTYPE_MASK) == C_GRP) {
+ struct group *ge;
+ if ((ge = getgrnam( buf ))) {
+ retval->ptr = (char *)ge->gr_gid;
+ return 0;
+ }
+ }
+ retval->ptr = 0;
+ if (sscanf( buf, "%li", &retval->ptr ) != 1)
+ return "integer";
+ return 0;
+ case C_TYPE_STR:
+ retval->ptr = val;
+ retval->len = vallen + 1;
+ if ((et->id & C_MTYPE_MASK) == C_PATH)
+ if (vallen && val[vallen-1] == '/')
+ retval->len--;
+ return 0;
+ case C_TYPE_ARGV:
+ if (!(ents = Malloc( sizeof(Value) * (esiz = 10) )))
+ return 0;
+ for (nents = 0, tlen = 0, i = 0; ; i++) {
+ for (; i < vallen && isspace( val[i] ); i++);
+ for (b = i; i < vallen && val[i] != ','; i++);
+ if (b == i)
+ break;
+ for (e = i; e > b && isspace( val[e - 1] ); e--);
+ if (esiz < nents + 2) {
+ Value *entsn = Realloc( ents,
+ sizeof(Value) * (esiz = esiz * 2 + 1) );
+ if (!nents)
+ break;
+ ents = entsn;
+ }
+ ents[nents].ptr = val + b;
+ ents[nents].len = e - b;
+ nents++;
+ tlen += e - b + 1;
+ }
+ ents[nents].ptr = 0;
+ retval->ptr = (char *)ents;
+ retval->len = tlen;
+ return 0;
+ default:
+ LogError( "Internal error: unknown value type in id %#x\n", et->id );
+ return 0;
+ }
+}
+
+static void
+GetValue( Ent *et, DSpec *dspec, Value *retval, char **eopts )
+{
+ Entry *ent;
+ const char *errs;
+
+/* Debug( "Getting value %#x\n", et->id );*/
+ if (dspec)
+ ent = FindDEnt( et->id, dspec );
+ else
+ ent = FindGEnt( et->id );
+ if (ent) {
+ if (!(errs = CvtValue( et, retval, ent->vallen, ent->val, eopts )))
+ return;
+ LogError( "Invalid %s value '%.*s' at %s:%d\n",
+ errs, ent->vallen, ent->val, kdmrc, ent->line );
+ }
+ Debug( "default: %s = %'s\n", et->name, et->def );
+ if ((errs = CvtValue( et, retval, strlen( et->def ), et->def, eopts )))
+ LogError( "Internal error: invalid default %s value '%s' for key %s\n",
+ errs, et->def, et->name );
+}
+
+static int
+AddValue( ValArr *va, int id, Value *val )
+{
+ int nu;
+
+/* Debug( "Addig value %#x\n", id );*/
+ if (va->nents == va->esiz) {
+ va->ents = Realloc( va->ents, sizeof(Val) * (va->esiz += 50) );
+ if (!va->ents)
+ return 0;
+ }
+ va->ents[va->nents].id = id;
+ va->ents[va->nents].val = *val;
+ va->nents++;
+ switch (id & C_TYPE_MASK) {
+ case C_TYPE_INT:
+ break;
+ case C_TYPE_STR:
+ va->nchars += val->len;
+ break;
+ case C_TYPE_ARGV:
+ va->nchars += val->len;
+ for (nu = 0; ((Value *)val->ptr)[nu++].ptr; );
+ va->nptrs += nu;
+ break;
+ }
+ return 1;
+}
+
+static void
+CopyValues( ValArr *va, Sect *sec, DSpec *dspec, int isconfig )
+{
+ Value val;
+ int i;
+
+ Debug( "getting values for section class [%s]\n", sec->name );
+ for (i = 0; i < sec->numents; i++) {
+/*Debug ("value %#x\n", sec->ents[i].id);*/
+ if ((sec->ents[i].id & (int)C_CONFIG) != isconfig)
+ ;
+ else if (sec->ents[i].id & C_INTERNAL) {
+ GetValue( sec->ents + i, dspec, ((Value *)sec->ents[i].ptr), 0 );
+ } else {
+ if (((sec->ents[i].id & C_MTYPE_MASK) == C_ENUM) ||
+ !sec->ents[i].ptr ||
+ !((int (*)( Value * ))sec->ents[i].ptr)(&val)) {
+ GetValue( sec->ents + i, dspec, &val,
+ (char **)sec->ents[i].ptr );
+ }
+ if (!AddValue( va, sec->ents[i].id, &val ))
+ break;
+ }
+ }
+ return;
+}
+
+static void
+SendValues( ValArr *va )
+{
+ Value *cst;
+ int i, nu;
+
+ GSendInt( va->nents );
+ GSendInt( va->nptrs );
+ GSendInt( 0/*va->nints*/ );
+ GSendInt( va->nchars );
+ for (i = 0; i < va->nents; i++) {
+ GSendInt( va->ents[i].id & ~C_PRIVATE );
+ switch (va->ents[i].id & C_TYPE_MASK) {
+ case C_TYPE_INT:
+ GSendInt( (int)va->ents[i].val.ptr );
+ break;
+ case C_TYPE_STR:
+ GSendNStr( va->ents[i].val.ptr, va->ents[i].val.len - 1 );
+ break;
+ case C_TYPE_ARGV:
+ cst = (Value *)va->ents[i].val.ptr;
+ for (nu = 0; cst[nu].ptr; nu++);
+ GSendInt( nu );
+ for (; cst->ptr; cst++)
+ GSendNStr( cst->ptr, cst->len );
+ break;
+ }
+ }
+}
+
+
+#ifdef XDMCP
+static char *
+ReadWord( File *file, int *len, int EOFatEOL )
+{
+ char *wordp, *wordBuffer;
+ int quoted;
+ char c;
+
+ rest:
+ wordp = wordBuffer = file->cur;
+ mloop:
+ quoted = 0;
+ qloop:
+ if (file->cur == file->eof) {
+ doeow:
+ if (wordp == wordBuffer)
+ return 0;
+ retw:
+ *wordp = '\0';
+ *len = wordp - wordBuffer;
+ return wordBuffer;
+ }
+ c = *file->cur++;
+ switch (c) {
+ case '#':
+ if (quoted)
+ break;
+ do {
+ if (file->cur == file->eof)
+ goto doeow;
+ c = *file->cur++;
+ } while (c != '\n');
+ case '\0':
+ case '\n':
+ if (EOFatEOL && !quoted) {
+ file->cur--;
+ goto doeow;
+ }
+ if (wordp != wordBuffer) {
+ file->cur--;
+ goto retw;
+ }
+ goto rest;
+ case ' ':
+ case '\t':
+ if (wordp != wordBuffer)
+ goto retw;
+ goto rest;
+ case '\\':
+ if (!quoted) {
+ quoted = 1;
+ goto qloop;
+ }
+ break;
+ }
+ *wordp++ = c;
+ goto mloop;
+}
+
+#define ALIAS_CHARACTER '%'
+#define EQUAL_CHARACTER '='
+#define NEGATE_CHARACTER '!'
+#define CHOOSER_STRING "CHOOSER"
+#define BROADCAST_STRING "BROADCAST"
+#define NOBROADCAST_STRING "NOBROADCAST"
+#define LISTEN_STRING "LISTEN"
+#define WILDCARD_STRING "*"
+
+typedef struct hostEntry {
+ struct hostEntry *next;
+ int type;
+ union _hostOrAlias {
+ char *aliasPattern;
+ char *hostPattern;
+ struct _display {
+ int connectionType;
+ int hostAddrLen;
+ char *hostAddress;
+ } displayAddress;
+ } entry;
+} HostEntry;
+
+typedef struct listenEntry {
+ struct listenEntry *next;
+ int iface;
+ int mcasts;
+ int nmcasts;
+} ListenEntry;
+
+typedef struct aliasEntry {
+ struct aliasEntry *next;
+ char *name;
+ HostEntry **pHosts;
+ int hosts;
+ int nhosts;
+ int hasBad;
+} AliasEntry;
+
+typedef struct aclEntry {
+ struct aclEntry *next;
+ HostEntry **pEntries;
+ int entries;
+ int nentries;
+ HostEntry **pHosts;
+ int hosts;
+ int nhosts;
+ int flags;
+} AclEntry;
+
+
+static int
+HasGlobCharacters( char *s )
+{
+ for (;;)
+ switch (*s++) {
+ case '?':
+ case '*':
+ return 1;
+ case '\0':
+ return 0;
+ }
+}
+
+#define PARSE_ALL 0
+#define PARSE_NO_BCAST 1
+#define PARSE_NO_PAT 2
+#define PARSE_NO_ALIAS 4
+
+static int
+ParseHost( int *nHosts, HostEntry ***hostPtr, int *nChars,
+ char *hostOrAlias, int len, int parse )
+{
+#if defined(IPv6) && defined(AF_INET6)
+ struct addrinfo *ai;
+#else
+ struct hostent *hostent;
+#endif
+ void *addr;
+ int addr_type, addr_len;
+
+ if (!(**hostPtr = (HostEntry *)Malloc( sizeof(HostEntry))))
+ return 0;
+ if (!(parse & PARSE_NO_BCAST) && !strcmp( hostOrAlias, BROADCAST_STRING ))
+ {
+ (**hostPtr)->type = HOST_BROADCAST;
+ }
+ else if (!(parse & PARSE_NO_ALIAS) && *hostOrAlias == ALIAS_CHARACTER)
+ {
+ (**hostPtr)->type = HOST_ALIAS;
+ (**hostPtr)->entry.aliasPattern = hostOrAlias + 1;
+ *nChars += len;
+ }
+ else if (!(parse & PARSE_NO_PAT) && HasGlobCharacters( hostOrAlias ))
+ {
+ (**hostPtr)->type = HOST_PATTERN;
+ (**hostPtr)->entry.hostPattern = hostOrAlias;
+ *nChars += len + 1;
+ }
+ else
+ {
+ (**hostPtr)->type = HOST_ADDRESS;
+#if defined(IPv6) && defined(AF_INET6)
+ if (getaddrinfo( hostOrAlias, NULL, NULL, &ai ))
+#else
+ if (!(hostent = gethostbyname( hostOrAlias )))
+#endif
+ {
+ LogWarn( "XDMCP ACL: unresolved host %'s\n", hostOrAlias );
+ free( (char *)(**hostPtr) );
+ return 0;
+ }
+#if defined(IPv6) && defined(AF_INET6)
+ addr_type = ai->ai_addr->sa_family;
+ if (ai->ai_family == AF_INET) {
+ addr = &((struct sockaddr_in *)ai->ai_addr)->sin_addr;
+ addr_len = sizeof(struct in_addr);
+ } else /*if (ai->ai_addr->sa_family == AF_INET6)*/ {
+ addr = &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr;
+ addr_len = sizeof(struct in6_addr);
+ }
+#else
+ addr_type = hostent->h_addrtype;
+ addr = hostent->h_addr;
+ addr_len = hostent->h_length;
+#endif
+ if (!((**hostPtr)->entry.displayAddress.hostAddress =
+ Malloc( addr_len )))
+ {
+#if defined(IPv6) && defined(AF_INET6)
+ freeaddrinfo( ai );
+#endif
+ free( (char *)(**hostPtr) );
+ return 0;
+ }
+ memcpy( (**hostPtr)->entry.displayAddress.hostAddress, addr, addr_len );
+ *nChars += addr_len;
+ (**hostPtr)->entry.displayAddress.hostAddrLen = addr_len;
+ (**hostPtr)->entry.displayAddress.connectionType = addr_type;
+#if defined(IPv6) && defined(AF_INET6)
+ freeaddrinfo( ai );
+#endif
+ }
+ *hostPtr = &(**hostPtr)->next;
+ (*nHosts)++;
+ return 1;
+}
+
+/* Returns non-0 if string is matched by pattern. Does case folding. */
+static int
+patternMatch( const char *string, const char *pattern )
+{
+ int p, s;
+
+ if (!string)
+ string = "";
+
+ for (;;) {
+ s = *string++;
+ switch (p = *pattern++) {
+ case '*':
+ if (!*pattern)
+ return 1;
+ for (string--; *string; string++)
+ if (patternMatch( string, pattern ))
+ return 1;
+ return 0;
+ case '?':
+ if (s == '\0')
+ return 0;
+ break;
+ case '\0':
+ return s == '\0';
+ case '\\':
+ p = *pattern++;
+ /* fall through */
+ default:
+ if (tolower( p ) != tolower( s ))
+ return 0;
+ }
+ }
+}
+
+#define MAX_DEPTH 32
+
+#define CHECK_NOT 1
+#define CHECK_NO_PAT 2
+
+static int
+checkHostlist( HostEntry **hosts, int nh, AliasEntry *aliases, int na,
+ int depth, int flags )
+{
+ HostEntry *h;
+ AliasEntry *a;
+ int hn, an, am;
+
+ for (h = *hosts, hn = 0; hn < nh; hn++, h = h->next)
+ if (h->type == HOST_ALIAS) {
+ if (depth == MAX_DEPTH) {
+ LogError( "XDMCP ACL: alias recursion involving %%%s\n",
+ h->entry.aliasPattern );
+ return 1;
+ }
+ for (a = aliases, an = 0, am = 0; an < na; an++, a = a->next)
+ if (patternMatch( a->name, h->entry.aliasPattern )) {
+ am = 1;
+ if ((flags & CHECK_NOT) && a->hasBad) {
+ LogError( "XDMCP ACL: alias %%%s with unresolved hosts "
+ "in denying rule\n", a->name );
+ return 1;
+ }
+ if (checkHostlist( a->pHosts, a->nhosts, aliases, na,
+ depth + 1, flags ))
+ return 1;
+ }
+ if (!am) {
+ if (flags & CHECK_NOT) {
+ LogError( "XDMCP ACL: unresolved alias pattern %%%s "
+ "in denying rule\n", h->entry.aliasPattern );
+ return 1;
+ } else
+ LogWarn( "XDMCP ACL: unresolved alias pattern %%%s\n",
+ h->entry.aliasPattern );
+ }
+ } else if (h->type == HOST_PATTERN && (flags & CHECK_NO_PAT))
+ LogWarn( "XDMCP ACL: wildcarded pattern %'s in host-only context\n",
+ h->entry.hostPattern );
+ return 0;
+}
+
+static void
+ReadAccessFile( const char *fname )
+{
+ HostEntry *hostList, **hostPtr = &hostList;
+ AliasEntry *aliasList, **aliasPtr = &aliasList;
+ AclEntry *acList, **acPtr = &acList, *acl;
+ ListenEntry *listenList, **listenPtr = &listenList;
+ char *displayOrAlias, *hostOrAlias;
+ File file;
+ int nHosts, nAliases, nAcls, nListens, nChars, error, bad;
+ int i, len;
+
+ nHosts = nAliases = nAcls = nListens = nChars = error = 0;
+ if (!readFile( &file, fname, "XDMCP access control" ))
+ goto sendacl;
+ while ((displayOrAlias = ReadWord( &file, &len, FALSE ))) {
+ if (*displayOrAlias == ALIAS_CHARACTER)
+ {
+ if (!(*aliasPtr = (AliasEntry *)Malloc( sizeof(AliasEntry)))) {
+ error = 1;
+ break;
+ }
+ (*aliasPtr)->name = displayOrAlias + 1;
+ nChars += len;
+ (*aliasPtr)->hosts = nHosts;
+ (*aliasPtr)->pHosts = hostPtr;
+ (*aliasPtr)->nhosts = 0;
+ (*aliasPtr)->hasBad = 0;
+ while ((hostOrAlias = ReadWord( &file, &len, TRUE ))) {
+ if (ParseHost( &nHosts, &hostPtr, &nChars, hostOrAlias, len,
+ PARSE_NO_BCAST ))
+ (*aliasPtr)->nhosts++;
+ else
+ (*aliasPtr)->hasBad = 1;
+ }
+ aliasPtr = &(*aliasPtr)->next;
+ nAliases++;
+ }
+ else if (!strcmp( displayOrAlias, LISTEN_STRING ))
+ {
+ if (!(*listenPtr = (ListenEntry *)Malloc( sizeof(ListenEntry)))) {
+ error = 1;
+ break;
+ }
+ (*listenPtr)->iface = nHosts;
+ if (!(hostOrAlias = ReadWord( &file, &len, TRUE )) ||
+ !strcmp( hostOrAlias, WILDCARD_STRING ) ||
+ !ParseHost( &nHosts, &hostPtr, &nChars, hostOrAlias, len,
+ PARSE_NO_BCAST|PARSE_NO_PAT|PARSE_NO_ALIAS ))
+ {
+ (*listenPtr)->iface = -1;
+ }
+ (*listenPtr)->mcasts = nHosts;
+ (*listenPtr)->nmcasts = 0;
+ while ((hostOrAlias = ReadWord( &file, &len, TRUE ))) {
+ if (ParseHost( &nHosts, &hostPtr, &nChars, hostOrAlias, len,
+ PARSE_NO_BCAST|PARSE_NO_PAT|PARSE_NO_ALIAS ))
+ (*listenPtr)->nmcasts++;
+ }
+ listenPtr = &(*listenPtr)->next;
+ nListens++;
+ }
+ else
+ {
+ if (!(*acPtr = (AclEntry *)Malloc( sizeof(AclEntry)))) {
+ error = 1;
+ break;
+ }
+ (*acPtr)->flags = 0;
+ if (*displayOrAlias == NEGATE_CHARACTER) {
+ (*acPtr)->flags |= a_notAllowed;
+ displayOrAlias++;
+ } else if (*displayOrAlias == EQUAL_CHARACTER)
+ displayOrAlias++;
+ (*acPtr)->entries = nHosts;
+ (*acPtr)->pEntries = hostPtr;
+ (*acPtr)->nentries = 1;
+ if (!ParseHost( &nHosts, &hostPtr, &nChars, displayOrAlias, len,
+ PARSE_NO_BCAST ))
+ {
+ bad = 1;
+ if ((*acPtr)->flags & a_notAllowed) {
+ LogError( "XDMCP ACL: unresolved host in denying rule\n" );
+ error = 1;
+ }
+ } else
+ bad = 0;
+ (*acPtr)->hosts = nHosts;
+ (*acPtr)->pHosts = hostPtr;
+ (*acPtr)->nhosts = 0;
+ while ((hostOrAlias = ReadWord( &file, &len, TRUE ))) {
+ if (!strcmp( hostOrAlias, CHOOSER_STRING ))
+ (*acPtr)->flags |= a_useChooser;
+ else if (!strcmp( hostOrAlias, NOBROADCAST_STRING ))
+ (*acPtr)->flags |= a_notBroadcast;
+ else {
+ if (ParseHost( &nHosts, &hostPtr, &nChars,
+ hostOrAlias, len, PARSE_NO_PAT ))
+ (*acPtr)->nhosts++;
+ }
+ }
+ if (!bad) {
+ acPtr = &(*acPtr)->next;
+ nAcls++;
+ }
+ }
+ }
+
+ if (!nListens) {
+ if (!(*listenPtr = (ListenEntry *)Malloc( sizeof(ListenEntry))))
+ error = 1;
+ else {
+ (*listenPtr)->iface = -1;
+ (*listenPtr)->mcasts = nHosts;
+ (*listenPtr)->nmcasts = 0;
+#if defined(IPv6) && defined(AF_INET6) && defined(XDM_DEFAULT_MCAST_ADDR6)
+ if (ParseHost( &nHosts, &hostPtr, &nChars,
+ XDM_DEFAULT_MCAST_ADDR6,
+ sizeof(XDM_DEFAULT_MCAST_ADDR6)-1,
+ PARSE_ALL ))
+ (*listenPtr)->nmcasts++;
+#endif
+ nListens++;
+ }
+ }
+
+ for (acl = acList, i = 0; i < nAcls; i++, acl = acl->next)
+ if (checkHostlist( acl->pEntries, acl->nentries, aliasList, nAliases,
+ 0, (acl->flags & a_notAllowed) ? CHECK_NOT : 0 ) ||
+ checkHostlist( acl->pHosts, acl->nhosts, aliasList, nAliases,
+ 0, CHECK_NO_PAT ))
+ error = 1;
+
+ if (error) {
+ nHosts = nAliases = nAcls = nListens = nChars = 0;
+ sendacl:
+ LogError( "No XDMCP requests will be granted\n" );
+ }
+ GSendInt( nHosts );
+ GSendInt( nListens );
+ GSendInt( nAliases );
+ GSendInt( nAcls );
+ GSendInt( nChars );
+ for (i = 0; i < nHosts; i++, hostList = hostList->next) {
+ GSendInt( hostList->type );
+ switch (hostList->type) {
+ case HOST_ALIAS:
+ GSendStr( hostList->entry.aliasPattern );
+ break;
+ case HOST_PATTERN:
+ GSendStr( hostList->entry.hostPattern );
+ break;
+ case HOST_ADDRESS:
+ GSendArr( hostList->entry.displayAddress.hostAddrLen,
+ hostList->entry.displayAddress.hostAddress );
+ GSendInt( hostList->entry.displayAddress.connectionType );
+ break;
+ }
+ }
+ for (i = 0; i < nListens; i++, listenList = listenList->next) {
+ GSendInt( listenList->iface );
+ GSendInt( listenList->mcasts );
+ GSendInt( listenList->nmcasts );
+ }
+ for (i = 0; i < nAliases; i++, aliasList = aliasList->next) {
+ GSendStr( aliasList->name );
+ GSendInt( aliasList->hosts );
+ GSendInt( aliasList->nhosts );
+ }
+ for (i = 0; i < nAcls; i++, acList = acList->next) {
+ GSendInt( acList->entries );
+ GSendInt( acList->nentries );
+ GSendInt( acList->hosts );
+ GSendInt( acList->nhosts );
+ GSendInt( acList->flags );
+ }
+}
+#endif
+
+
+int main( int argc ATTR_UNUSED, char **argv )
+{
+ DSpec dspec;
+ ValArr va;
+ char *ci, *disp, *dcls, *cfgfile;
+ int what;
+
+ if (!(ci = getenv( "CONINFO" ))) {
+ fprintf( stderr, "This program is part of kdm and should not be run manually.\n" );
+ return 1;
+ }
+ if (sscanf( ci, "%d %d", &rfd, &wfd ) != 2)
+ return 1;
+
+ InitLog();
+
+ if ((debugLevel = GRecvInt()) & DEBUG_WCONFIG)
+ sleep( 100 );
+
+/* Debug ("parsing command line\n");*/
+ if (**++argv)
+ kdmrc = *argv;
+/*
+ while (*++argv) {
+ }
+*/
+
+ for (;;) {
+/* Debug ("Awaiting command ...\n");*/
+ if (!GRecvCmd( &what ))
+ break;
+ switch (what) {
+ case GC_Files:
+/* Debug ("GC_Files\n");*/
+ ReadConf();
+ CopyValues( 0, &secGeneral, 0, C_CONFIG );
+#ifdef XDMCP
+ CopyValues( 0, &secXdmcp, 0, C_CONFIG );
+ GSendInt( 2 );
+#else
+ GSendInt( 1 );
+#endif
+ GSendStr( kdmrc );
+ GSendInt( -1 );
+#ifdef XDMCP
+ GSendNStr( VXaccess.ptr, VXaccess.len - 1 );
+ GSendInt( 0 );
+#endif
+ for (; (what = GRecvInt()) != -1; )
+ switch (what) {
+ case GC_gGlobal:
+ case GC_gDisplay:
+ GSendInt( 0 );
+ break;
+#ifdef XDMCP
+ case GC_gXaccess:
+ GSendInt( 1 );
+ break;
+#endif
+ default:
+ GSendInt( -1 );
+ break;
+ }
+ break;
+ case GC_GetConf:
+/* Debug( "GC_GetConf\n" );*/
+ memset( &va, 0, sizeof(va) );
+ what = GRecvInt();
+ cfgfile = GRecvStr();
+ switch (what) {
+ case GC_gGlobal:
+/* Debug( "GC_gGlobal\n" );*/
+ Debug( "getting global config\n" );
+ ReadConf();
+ CopyValues( &va, &secGeneral, 0, 0 );
+#ifdef XDMCP
+ CopyValues( &va, &secXdmcp, 0, 0 );
+#endif
+ CopyValues( &va, &secShutdown, 0, 0 );
+ SendValues( &va );
+ break;
+ case GC_gDisplay:
+/* Debug( "GC_gDisplay\n" );*/
+ disp = GRecvStr();
+/* Debug( " Display %s\n", disp );*/
+ dcls = GRecvStr();
+/* Debug( " Class %s\n", dcls );*/
+ Debug( "getting config for display %s, class %s\n", disp, dcls );
+ MkDSpec( &dspec, disp, dcls ? dcls : "" );
+ ReadConf();
+ CopyValues( &va, &sec_Core, &dspec, 0 );
+ CopyValues( &va, &sec_Greeter, &dspec, 0 );
+ free( disp );
+ if (dcls)
+ free( dcls );
+ SendValues( &va );
+ break;
+#ifdef XDMCP
+ case GC_gXaccess:
+ ReadAccessFile( cfgfile );
+ break;
+#endif
+ default:
+ Debug( "Unsupported config category %#x\n", what );
+ }
+ free( cfgfile );
+ break;
+ default:
+ Debug( "Unknown config command %#x\n", what );
+ }
+ }
+
+/* Debug( "Config reader exiting ..." );*/
+ return EX_NORMAL;
+}
diff --git a/kdm/kfrontend/kdm_greet.c b/kdm/kfrontend/kdm_greet.c
new file mode 100644
index 000000000..6abc2c057
--- /dev/null
+++ b/kdm/kfrontend/kdm_greet.c
@@ -0,0 +1,787 @@
+/*
+
+KDE Greeter module for xdm
+
+Copyright (C) 2001-2003 Oswald Buddenhagen <ossi@kde.org>
+
+This file contains code from the old xdm core,
+Copyright 1988, 1998 Keith Packard, MIT X Consortium/The Open Group
+
+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.
+
+*/
+
+#include <config.h>
+
+#include "kdm_greet.h"
+#include "kdmconfig.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <string.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <ctype.h>
+#include <time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#ifdef _POSIX_PRIORITY_SCHEDULING
+# include <sched.h>
+#endif
+
+#if defined(HAVE_XTEST) || defined(HAVE_XKB)
+# include <X11/Xlib.h>
+# include <X11/keysym.h>
+#endif
+
+#ifdef HAVE_XTEST
+# include <X11/extensions/XTest.h>
+#endif
+
+#ifdef HAVE_XKB
+# include <X11/XKBlib.h>
+#endif
+
+extern void LogOutOfMem( void );
+
+static void *
+Realloc( void *ptr, size_t size )
+{
+ void *ret;
+
+ if (!(ret = realloc( ptr, size )) && size)
+ LogOutOfMem();
+ return ret;
+}
+
+#define PRINT_QUOTES
+#define PRINT_ARRAYS
+#define LOG_NAME "kdm_greet"
+#define LOG_DEBUG_MASK DEBUG_GREET
+#define LOG_PANIC_EXIT 1
+#define STATIC
+#include <printf.c>
+
+static void
+GDebug( const char *fmt, ... )
+{
+ va_list args;
+
+ if (debugLevel & DEBUG_HLPCON) {
+ va_start( args, fmt );
+ Logger( DM_DEBUG, fmt, args );
+ va_end( args );
+ }
+}
+
+
+char *dname;
+
+int rfd;
+static int wfd, mrfd, mwfd, srfd, swfd;
+static const char *who;
+
+void
+GSet( int master )
+{
+ if (master)
+ rfd = mrfd, wfd = mwfd, who = "core (master)";
+ else
+ rfd = srfd, wfd = swfd, who = "core";
+
+}
+
+static int
+Reader( void *buf, int count )
+{
+ int ret, rlen;
+
+ for (rlen = 0; rlen < count; ) {
+ dord:
+ ret = read( rfd, (void *)((char *)buf + rlen), count - rlen );
+ if (ret < 0) {
+ if (errno == EINTR)
+ goto dord;
+ if (errno == EAGAIN)
+ break;
+ return -1;
+ }
+ if (!ret)
+ break;
+ rlen += ret;
+ }
+ return rlen;
+}
+
+static void
+GRead( void *buf, int count )
+{
+ if (Reader( buf, count ) != count)
+ LogPanic( "Can't read from %s\n", who );
+}
+
+static void
+GWrite( const void *buf, int count )
+{
+ if (write( wfd, buf, count ) != count)
+ LogPanic( "Can't write to %s\n", who );
+#ifdef _POSIX_PRIORITY_SCHEDULING
+ if ((debugLevel & DEBUG_HLPCON))
+ sched_yield();
+#endif
+}
+
+void
+GSendInt( int val )
+{
+ GDebug( "Sending int %d (%#x) to %s\n", val, val, who );
+ GWrite( &val, sizeof(val) );
+}
+
+void
+GSendStr( const char *buf )
+{
+ int len = buf ? strlen( buf ) + 1 : 0;
+ GDebug( "Sending string %'s to %s\n", buf, who );
+ GWrite( &len, sizeof(len) );
+ GWrite( buf, len );
+}
+
+/*
+static void
+GSendNStr( const char *buf, int len )
+{
+ int tlen = len + 1;
+ GDebug( "Sending string %'.*s to %s\n", len, buf, who );
+ GWrite( &tlen, sizeof(tlen) );
+ GWrite( buf, len );
+ GWrite( "", 1 );
+}
+*/
+
+void
+GSendArr( int len, const char *buf )
+{
+ GDebug( "Sending array %02[:*hhx to %s\n", len, buf, who );
+ GWrite( &len, sizeof(len) );
+ GWrite( buf, len );
+}
+
+int
+GRecvInt()
+{
+ int val;
+
+ GDebug( "Receiving int from %s ...\n", who );
+ GRead( &val, sizeof(val) );
+ GDebug( " -> %d (%#x)\n", val, val );
+ return val;
+}
+
+static char *
+iGRecvArr( int *rlen )
+{
+ int len;
+ char *buf;
+
+ GRead( &len, sizeof(len) );
+ *rlen = len;
+ GDebug( " -> %d bytes\n", len );
+ if (!len)
+ return (char *)0;
+ if (!(buf = malloc( len )))
+ LogPanic( "No memory for read buffer\n" );
+ GRead( buf, len );
+ return buf;
+}
+
+char *
+GRecvStr()
+{
+ int len;
+ char *buf;
+
+ GDebug( "Receiving string from %s ...\n", who );
+ buf = iGRecvArr( &len );
+ GDebug( " -> %'.*s\n", len, buf );
+ return buf;
+}
+
+char **
+GRecvStrArr( int *rnum )
+{
+ int num;
+ char **argv, **cargv;
+
+ GDebug( "Receiving string array from %s ...\n", who );
+ GRead( &num, sizeof(num) );
+ GDebug( " -> %d strings\n", num );
+ if (rnum)
+ *rnum = num;
+ if (!num)
+ return (char **)0;
+ if (!(argv = malloc( num * sizeof(char *))))
+ LogPanic( "No memory for read buffer\n" );
+ for (cargv = argv; --num >= 0; cargv++)
+ *cargv = GRecvStr();
+ return argv;
+}
+
+char *
+GRecvArr( int *num )
+{
+ char *arr;
+
+ GDebug( "Receiving array from %s ...\n", who );
+ GRead( num, sizeof(*num) );
+ GDebug( " -> %d bytes\n", *num );
+ if (!*num)
+ return (char *)0;
+ if (!(arr = malloc( *num )))
+ LogPanic( "No memory for read buffer\n" );
+ GRead( arr, *num );
+ GDebug( " -> %02[*hhx\n", *num, arr );
+ return arr;
+}
+
+static void
+ReqCfg( int id )
+{
+ GSendInt( G_GetCfg );
+ GSendInt( id );
+ switch (GRecvInt()) {
+ case GE_NoEnt:
+ LogPanic( "Config value %#x not available\n", id );
+ case GE_BadType:
+ LogPanic( "Core does not know type of config value %#x\n", id );
+ }
+}
+
+int
+GetCfgInt( int id )
+{
+ ReqCfg( id );
+ return GRecvInt();
+}
+
+char *
+GetCfgStr( int id )
+{
+ ReqCfg( id );
+ return GRecvStr();
+}
+
+char **
+GetCfgStrArr( int id, int *len )
+{
+ ReqCfg( id );
+ return GRecvStrArr( len );
+}
+
+static void
+disposeSession( dpySpec *sess )
+{
+ free( sess->display );
+ free( sess->from );
+ if (sess->user)
+ free( sess->user );
+ if (sess->session)
+ free( sess->session );
+}
+
+dpySpec *
+fetchSessions( int flags )
+{
+ dpySpec *sess, *sessions = 0, tsess;
+
+ GSet( 1 );
+ GSendInt( G_List );
+ GSendInt( flags );
+ next:
+ while ((tsess.display = GRecvStr())) {
+ tsess.from = GRecvStr();
+#ifdef HAVE_VTS
+ tsess.vt = GRecvInt();
+#endif
+ tsess.user = GRecvStr();
+ tsess.session = GRecvStr();
+ tsess.flags = GRecvInt();
+ if ((tsess.flags & isTTY) && *tsess.from)
+ for (sess = sessions; sess; sess = sess->next)
+ if (sess->user && !strcmp( sess->user, tsess.user ) &&
+ !strcmp( sess->from, tsess.from ))
+ {
+ sess->count++;
+ disposeSession( &tsess );
+ goto next;
+ }
+ if (!(sess = malloc( sizeof(*sess) )))
+ LogPanic( "Out of memory\n" );
+ tsess.count = 1;
+ tsess.next = sessions;
+ *sess = tsess;
+ sessions = sess;
+ }
+ GSet( 0 );
+ return sessions;
+}
+
+void
+disposeSessions( dpySpec *sess )
+{
+ while (sess) {
+ dpySpec *nsess = sess->next;
+ disposeSession( sess );
+ free( sess );
+ sess = nsess;
+ }
+}
+
+void
+freeStrArr( char **arr )
+{
+ char **tarr;
+
+ if (arr) {
+ for (tarr = arr; *tarr; tarr++)
+ free( *tarr );
+ free( arr );
+ }
+}
+
+
+static int
+ignoreErrors( Display *dpy ATTR_UNUSED, XErrorEvent *event ATTR_UNUSED )
+{
+ Debug( "ignoring X error\n" );
+ return 0;
+}
+
+/*
+ * this is mostly bogus -- but quite useful. I wish the protocol
+ * had some way of enumerating and identifying clients, that way
+ * this code wouldn't have to be this kludgy.
+ */
+
+static void
+killWindows( Display *dpy, Window window )
+{
+ Window root, parent, *children;
+ unsigned child, nchildren = 0;
+
+ while (XQueryTree( dpy, window, &root, &parent, &children, &nchildren )
+ && nchildren > 0)
+ {
+ for (child = 0; child < nchildren; child++) {
+ Debug( "XKillClient 0x%lx\n", (unsigned long)children[child] );
+ XKillClient( dpy, children[child] );
+ }
+ XFree( (char *)children );
+ }
+}
+
+static jmp_buf resetJmp;
+
+static void
+abortReset( int n ATTR_UNUSED )
+{
+ longjmp (resetJmp, 1);
+}
+
+/*
+ * this display connection better not have any windows...
+ */
+
+static void
+pseudoReset( Display *dpy )
+{
+ int screen;
+
+ if (setjmp( resetJmp )) {
+ LogError( "pseudoReset timeout\n" );
+ } else {
+ (void)signal( SIGALRM, abortReset );
+ (void)alarm( 30 );
+ XSetErrorHandler( ignoreErrors );
+ for (screen = 0; screen < ScreenCount( dpy ); screen++) {
+ Debug( "pseudoReset screen %d\n", screen );
+ killWindows( dpy, RootWindow( dpy, screen ) );
+ }
+ Debug( "before XSync\n" );
+ XSync( dpy, False );
+ (void)alarm( 0 );
+ }
+ signal( SIGALRM, SIG_DFL );
+ XSetErrorHandler( (XErrorHandler)0 );
+ Debug( "pseudoReset done\n" );
+}
+
+
+static jmp_buf syncJump;
+
+static void
+syncTimeout( int n ATTR_UNUSED )
+{
+ longjmp( syncJump, 1 );
+}
+
+void
+SecureDisplay( Display *dpy )
+{
+ Debug( "SecureDisplay %s\n", dname );
+ (void)signal( SIGALRM, syncTimeout );
+ if (setjmp( syncJump )) {
+ LogError( "Display %s could not be secured\n", dname );
+ exit( EX_RESERVER_DPY );
+ }
+ (void)alarm( (unsigned)_grabTimeout );
+ Debug( "Before XGrabServer %s\n", dname );
+ XGrabServer( dpy );
+ Debug( "XGrabServer succeeded %s\n", dname );
+ if (XGrabKeyboard( dpy, DefaultRootWindow( dpy ), True, GrabModeAsync,
+ GrabModeAsync, CurrentTime ) != GrabSuccess)
+ {
+ (void)alarm( 0 );
+ (void)signal( SIGALRM, SIG_DFL );
+ LogError( "Keyboard on display %s could not be secured\n", dname );
+ sleep( 10 );
+ exit( EX_RESERVER_DPY );
+ }
+ (void)alarm( 0 );
+ (void)signal( SIGALRM, SIG_DFL );
+ pseudoReset( dpy );
+ if (!_grabServer)
+ {
+ XUngrabServer( dpy );
+ XSync( dpy, 0 );
+ }
+ Debug( "done secure %s\n", dname );
+#ifdef HAVE_XKBSETPERCLIENTCONTROLS
+ /*
+ * Activate the correct mapping for modifiers in XKB extension as
+ * grabbed keyboard has its own mapping by default
+ */
+ {
+ int opcode, evbase, errbase, majret, minret;
+ unsigned int value = XkbPCF_GrabsUseXKBStateMask;
+ if (XkbQueryExtension( dpy, &opcode, &evbase,
+ &errbase, &majret, &minret ))
+ XkbSetPerClientControls( dpy, value, &value );
+ }
+#endif
+}
+
+void
+UnsecureDisplay( Display *dpy )
+{
+ Debug( "Unsecure display %s\n", dname );
+ if (_grabServer) {
+ XUngrabServer( dpy );
+ XSync( dpy, 0 );
+ }
+}
+
+static jmp_buf pingTime;
+
+static int
+PingLostIOErr( Display *dpy ATTR_UNUSED )
+{
+ longjmp( pingTime, 1 );
+}
+
+static void
+PingLostSig( int n ATTR_UNUSED )
+{
+ longjmp( pingTime, 1 );
+}
+
+int
+PingServer( Display *dpy )
+{
+ int (*oldError)( Display * );
+ void (*oldSig)( int );
+ int oldAlarm;
+
+ oldError = XSetIOErrorHandler( PingLostIOErr );
+ oldAlarm = alarm( 0 );
+ oldSig = signal( SIGALRM, PingLostSig );
+ (void)alarm( _pingTimeout * 60 );
+ if (!setjmp( pingTime )) {
+ Debug( "Ping server\n" );
+ XSync( dpy, 0 );
+ } else {
+ Debug( "Server dead\n" );
+ (void)alarm( 0 );
+ (void)signal( SIGALRM, SIG_DFL );
+ XSetIOErrorHandler( oldError );
+ return 0;
+ }
+ (void)alarm( 0 );
+ (void)signal( SIGALRM, oldSig );
+ (void)alarm( oldAlarm );
+ Debug( "Server alive\n" );
+ XSetIOErrorHandler( oldError );
+ return 1;
+}
+
+/*
+ * Modifier changing code based on kdebase/kxkb/kcmmisc.cpp
+ *
+ * XTest part: Copyright (C) 2000-2001 Lubos Lunak <l.lunak@kde.org>
+ * XKB part: Copyright (C) 2001-2002 Oswald Buddenhagen <ossi@kde.org>
+ *
+ */
+
+#ifdef HAVE_XKB
+static int
+xkb_init( Display *dpy )
+{
+ int xkb_opcode, xkb_event, xkb_error;
+ int xkb_lmaj = XkbMajorVersion;
+ int xkb_lmin = XkbMinorVersion;
+ return XkbLibraryVersion( &xkb_lmaj, &xkb_lmin ) &&
+ XkbQueryExtension( dpy, &xkb_opcode, &xkb_event,
+ &xkb_error, &xkb_lmaj, &xkb_lmin );
+}
+
+static unsigned int
+xkb_modifier_mask_work( XkbDescPtr xkb, const char *name )
+{
+ int i;
+
+ if (!xkb->names)
+ return 0;
+ for (i = 0; i < XkbNumVirtualMods; i++) {
+ char *modStr = XGetAtomName( xkb->dpy, xkb->names->vmods[i] );
+ if (modStr != NULL && strcmp( name, modStr ) == 0) {
+ unsigned int mask;
+ XkbVirtualModsToReal( xkb, 1 << i, &mask );
+ return mask;
+ }
+ }
+ return 0;
+}
+
+static unsigned int
+xkb_modifier_mask( Display *dpy, const char *name )
+{
+ XkbDescPtr xkb;
+
+ if ((xkb = XkbGetKeyboard( dpy, XkbAllComponentsMask, XkbUseCoreKbd ))) {
+ unsigned int mask = xkb_modifier_mask_work( xkb, name );
+ XkbFreeKeyboard( xkb, 0, True );
+ return mask;
+ }
+ return 0;
+}
+
+static int
+xkb_get_modifier_state( Display *dpy, const char *name )
+{
+ unsigned int mask;
+ XkbStateRec state;
+
+ if (!(mask = xkb_modifier_mask( dpy, name )))
+ return 0;
+ XkbGetState( dpy, XkbUseCoreKbd, &state );
+ return (mask & state.locked_mods) != 0;
+}
+
+static int
+xkb_set_modifier( Display *dpy, const char *name, int sts )
+{
+ unsigned int mask;
+
+ if (!(mask = xkb_modifier_mask( dpy, name )))
+ return 0;
+ XkbLockModifiers( dpy, XkbUseCoreKbd, mask, sts ? mask : 0 );
+ return 1;
+}
+#endif /* HAVE_XKB */
+
+#ifdef HAVE_XTEST
+static int
+xtest_get_modifier_state( Display *dpy, int key )
+{
+ XModifierKeymap *map;
+ KeyCode modifier_keycode;
+ unsigned int i, mask;
+ Window dummy1, dummy2;
+ int dummy3, dummy4, dummy5, dummy6;
+
+ if ((modifier_keycode = XKeysymToKeycode( dpy, key )) == NoSymbol)
+ return 0;
+ map = XGetModifierMapping( dpy );
+ for (i = 0; i < 8; ++i)
+ if (map->modifiermap[map->max_keypermod * i] == modifier_keycode) {
+ XFreeModifiermap( map );
+ XQueryPointer( dpy, DefaultRootWindow( dpy ),
+ &dummy1, &dummy2, &dummy3, &dummy4, &dummy5, &dummy6,
+ &mask );
+ return (mask & (1 << i)) != 0;
+ }
+ XFreeModifiermap( map );
+ return 0;
+}
+
+static void
+xtest_fake_keypress( Display *dpy, int key )
+{
+ XTestFakeKeyEvent( dpy, XKeysymToKeycode( dpy, key ), True, CurrentTime );
+ XTestFakeKeyEvent( dpy, XKeysymToKeycode( dpy, key ), False, CurrentTime );
+}
+#endif /* HAVE_XTEST */
+
+#ifdef HAVE_XKB
+static int havexkb;
+#endif
+static int nummodified, oldnumstate, newnumstate;
+static Display *dpy;
+
+void
+setup_modifiers( Display *mdpy, int numlock )
+{
+ if (numlock == 2)
+ return;
+ newnumstate = numlock;
+ nummodified = 1;
+ dpy = mdpy;
+#ifdef HAVE_XKB
+ if (xkb_init( mdpy )) {
+ havexkb = 1;
+ oldnumstate = xkb_get_modifier_state( mdpy, "NumLock" );
+ xkb_set_modifier( mdpy, "NumLock", numlock );
+ return;
+ }
+#endif
+#ifdef HAVE_XTEST
+ oldnumstate = xtest_get_modifier_state( mdpy, XK_Num_Lock );
+ if (oldnumstate != numlock)
+ xtest_fake_keypress( mdpy, XK_Num_Lock );
+#endif
+}
+
+void
+restore_modifiers( void )
+{
+#ifdef HAVE_XTEST
+ int numstat;
+#endif
+
+ if (!nummodified)
+ return;
+#ifdef HAVE_XKB
+ if (havexkb) {
+ if (xkb_get_modifier_state( dpy, "NumLock" ) == newnumstate)
+ xkb_set_modifier( dpy, "NumLock", oldnumstate );
+ return;
+ }
+#endif
+#ifdef HAVE_XTEST
+ numstat = xtest_get_modifier_state( dpy, XK_Num_Lock );
+ if (numstat == newnumstate && newnumstate != oldnumstate)
+ xtest_fake_keypress( dpy, XK_Num_Lock );
+#endif
+}
+
+void
+setCursor( Display *mdpy, int window, int shape )
+{
+ Cursor xcursor;
+
+ if ((xcursor = XCreateFontCursor( mdpy, shape ))) {
+ XDefineCursor( mdpy, window, xcursor );
+ XFreeCursor( mdpy, xcursor );
+ XFlush( mdpy );
+ }
+}
+
+static void
+sigterm( int n ATTR_UNUSED )
+{
+ exit( EX_NORMAL );
+}
+
+static char *savhome;
+
+static void
+cleanup( void )
+{
+ char buf[128];
+
+ if (strcmp( savhome, getenv( "HOME" ) ) || memcmp( savhome, "/tmp/", 5 ))
+ LogError( "Internal error: memory corruption detected\n" ); /* no panic: recursion */
+ else {
+ sprintf( buf, "rm -rf %s", savhome );
+ system( buf );
+ }
+}
+
+extern void kg_main( const char *argv0 );
+
+int
+main( int argc ATTR_UNUSED, char **argv )
+{
+ char *ci;
+ int i;
+ char qtrc[40];
+
+ if (!(ci = getenv( "CONINFO" ))) {
+ fprintf( stderr, "This program is part of kdm and should not be run manually.\n" );
+ return 1;
+ }
+ if (sscanf( ci, "%d %d %d %d", &srfd, &swfd, &mrfd, &mwfd ) != 4)
+ return 1;
+ fcntl( srfd, F_SETFD, FD_CLOEXEC );
+ fcntl( swfd, F_SETFD, FD_CLOEXEC );
+ fcntl( mrfd, F_SETFD, FD_CLOEXEC );
+ fcntl( mwfd, F_SETFD, FD_CLOEXEC );
+ GSet( 0 );
+
+ InitLog();
+
+ if ((debugLevel = GRecvInt()) & DEBUG_WGREET)
+ sleep( 100 );
+
+ signal( SIGTERM, sigterm );
+
+ dname = getenv( "DISPLAY" );
+
+ init_config();
+
+ /* for QSettings */
+ srand( time( 0 ) );
+ for (i = 0; i < 10000; i++) {
+ sprintf( qtrc, "/tmp/%010d", rand() );
+ if (!mkdir( qtrc, 0700 ))
+ goto okay;
+ }
+ LogPanic( "Cannot create $HOME\n" );
+ okay:
+ if (setenv( "HOME", qtrc, 1 ))
+ LogPanic( "Cannot set $HOME\n" );
+ if (!(savhome = strdup( qtrc )))
+ LogPanic( "Cannot save $HOME\n" );
+ atexit( cleanup );
+
+ setenv( "LC_ALL", _language, 1 );
+
+ kg_main( argv[0] );
+
+ return EX_NORMAL;
+}
diff --git a/kdm/kfrontend/kdm_greet.h b/kdm/kfrontend/kdm_greet.h
new file mode 100644
index 000000000..df325c28d
--- /dev/null
+++ b/kdm/kfrontend/kdm_greet.h
@@ -0,0 +1,91 @@
+/*
+
+KDE Greeter module for xdm
+
+Copyright (C) 2001-2003 Oswald Buddenhagen <ossi@kde.org>
+
+This file contains code from the old xdm core,
+Copyright 1988, 1998 Keith Packard, MIT X Consortium/The Open Group
+
+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.
+
+*/
+
+#ifndef _KDM_GREET_H_
+#define _KDM_GREET_H_
+
+#include <greet.h> /* for the ATTR_ defines */
+#include <config.ci> /* for the HAVE_VTS define */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void GSet( int master );
+void GSendInt( int val );
+void GSendStr( const char *buf );
+/*void GSendNStr( const char *buf, int len );*/
+void GSendArr( int len, const char *buf );
+int GRecvInt( void );
+char *GRecvStr( void );
+char **GRecvStrArr( int *len );
+char *GRecvArr( int *len );
+
+int GetCfgInt( int id );
+char *GetCfgStr( int id );
+char **GetCfgStrArr( int id, int *len );
+
+typedef struct dpySpec {
+ struct dpySpec *next;
+ char *display, *from, *user, *session;
+#ifdef HAVE_VTS
+ int vt;
+#endif
+ int flags;
+ int count;
+} dpySpec;
+
+dpySpec *fetchSessions( int flags );
+void disposeSessions( dpySpec *sess );
+
+void freeStrArr( char **arr );
+
+void Debug( const char *fmt, ... );
+void LogInfo( const char *fmt, ... );
+void LogWarn( const char *fmt, ... );
+void LogError( const char *fmt, ... );
+void LogPanic( const char *fmt, ... ) ATTR_NORETURN;
+
+struct _XDisplay;
+
+void SecureDisplay( struct _XDisplay *dpy );
+void UnsecureDisplay( struct _XDisplay *dpy );
+int PingServer( struct _XDisplay *dpy );
+
+void setup_modifiers( struct _XDisplay *mdpy, int numlock );
+void restore_modifiers( void );
+
+void setCursor( struct _XDisplay *mdpy, int window, int shape );
+
+
+extern int rfd; /* for select() loops */
+
+extern char *dname; /* d->name */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _KDM_GREET_H_ */
diff --git a/kdm/kfrontend/kdmclock.cpp b/kdm/kfrontend/kdmclock.cpp
new file mode 100644
index 000000000..88bc33aaa
--- /dev/null
+++ b/kdm/kfrontend/kdmclock.cpp
@@ -0,0 +1,176 @@
+/*
+
+clock module for kdm
+
+Copyright (C) 2000 Espen Sand, espen@kde.org
+ Based on work by NN(yet to be determined)
+flicker free code by Remi Guyomarch <rguyom@mail.dotcom.fr>
+
+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.
+
+*/
+
+#include "kdmclock.h"
+
+//#include <kapplication.h>
+//#include <kconfig.h>
+
+#include <qdatetime.h>
+#include <qpixmap.h>
+#include <qpainter.h>
+#include <qtimer.h>
+
+KdmClock::KdmClock( QWidget *parent, const char *name )
+ : inherited( parent, name )
+{
+ // start timer
+ QTimer *timer = new QTimer( this );
+ connect( timer, SIGNAL(timeout()), SLOT(timeout()) );
+ timer->start( 1000 );
+
+ // reading rc file
+ //KConfig *config = kapp->config();
+
+ //config->setGroup( "Option" );
+ mDate = false;//config->readNumEntry( "date", FALSE );
+ mSecond = true;//config->readNumEntry( "second", TRUE );
+ mDigital = false;//config->readNumEntry( "digital", FALSE );
+ mBorder = false;//config->readNumEntry( "border", FALSE );
+
+ //config->setGroup( "Font" );
+ mFont.setFamily( QString::fromLatin1("Utopia")/*config->readEntry( "Family", "Utopia")*/ );
+ mFont.setPointSize( 51/*config->readNumEntry( "Point Size", 51)*/ );
+ mFont.setWeight( 75/*config->readNumEntry( "Weight", 75)*/ );
+ mFont.setItalic( TRUE/*config->readNumEntry( "Italic",TRUE )*/ );
+ mFont.setBold( TRUE/*config->readNumEntry( "Bold",TRUE )*/ );
+
+ setFixedSize( 100, 100 );
+
+ if (mBorder) {
+ setLineWidth( 1 );
+ setFrameStyle( Box|Plain );
+ //setFrameStyle( WinPanel|Sunken );
+ }
+
+/*
+ if (!mDigital) {
+ if (height() < width())
+ resize( height(), height() );
+ else
+ resize( width() ,width() );
+ }
+*/
+
+ //setBackgroundOrigin( WindowOrigin );
+ mBackgroundBrush = backgroundBrush();
+ setBackgroundMode( NoBackground );
+ repaint();
+}
+
+
+void KdmClock::showEvent( QShowEvent * )
+{
+ repaint();
+}
+
+
+void KdmClock::timeout()
+{
+ repaint();
+}
+
+void KdmClock::paintEvent( QPaintEvent * )
+{
+ if (!isVisible())
+ return;
+
+ QPainter p( this );
+ drawFrame( &p );
+
+ QPixmap pm( contentsRect().size() );
+ QPainter paint;
+ paint.begin( &pm );
+ paint.fillRect( contentsRect(), mBackgroundBrush );
+
+ // get current time
+ QTime time = QTime::currentTime();
+
+/*
+ if (mDigital) {
+ QString buf;
+ if (mSecond)
+ buf.sprintf( "%02d:%02d:%02d", time.hour(), time.minute(),
+ time.second() );
+ else
+ buf.sprintf( "%02d:%02d", time.hour(), time.minute() );
+ mFont.setPointSize( QMIN( (int)(width()/buf.length()*1.5),height() ) );
+ paint.setFont( mFont );
+ paint.setPen( backgroundColor() );
+ paint.drawText( contentsRect(),AlignHCenter|AlignVCenter, buf,-1,0,0 );
+ } else {
+*/
+ QPointArray pts;
+ QPoint cp = contentsRect().center() - QPoint( 2,2 );
+ int d = QMIN( contentsRect().width()-15,contentsRect().height()-15 );
+ paint.setPen( foregroundColor() );
+ paint.setBrush( foregroundColor() );
+
+ QWMatrix matrix;
+ matrix.translate( cp.x(), cp.y() );
+ matrix.scale( d/1000.0F, d/1000.0F );
+
+ // Hour
+ float h_angle = 30*(time.hour()%12-3) + time.minute()/2;
+ matrix.rotate( h_angle );
+ paint.setWorldMatrix( matrix );
+ pts.setPoints( 4, -20,0, 0,-20, 300,0, 0,20 );
+ paint.drawPolygon( pts );
+ matrix.rotate( -h_angle );
+
+ // Minute
+ float m_angle = (time.minute()-15)*6;
+ matrix.rotate( m_angle );
+ paint.setWorldMatrix( matrix );
+ pts.setPoints( 4, -10,0, 0,-10, 400,0, 0,10 );
+ paint.drawPolygon( pts );
+ matrix.rotate( -m_angle );
+
+ // Second
+ float s_angle = (time.second()-15)*6;
+ matrix.rotate( s_angle );
+ paint.setWorldMatrix( matrix );
+ pts.setPoints( 4,0,0,0,0,400,0,0,0 );
+ if (mSecond)
+ paint.drawPolygon( pts );
+ matrix.rotate( -s_angle );
+
+ // quadrante
+ for (int i=0 ; i < 60 ; i++) {
+ paint.setWorldMatrix( matrix );
+ if ((i % 5) == 0)
+ paint.drawLine( 450,0, 500,0 ); // draw hour lines
+ else
+ paint.drawPoint( 480,0 ); // draw second lines
+ matrix.rotate( 6 );
+ }
+
+// } // if (mDigital)
+ paint.end();
+
+ // flicker free code by Remi Guyomarch <rguyom@mail.dotcom.fr>
+ bitBlt( this, contentsRect().topLeft(), &pm );
+}
+
+#include "kdmclock.moc"
diff --git a/kdm/kfrontend/kdmclock.h b/kdm/kfrontend/kdmclock.h
new file mode 100644
index 000000000..92ce97733
--- /dev/null
+++ b/kdm/kfrontend/kdmclock.h
@@ -0,0 +1,52 @@
+/*
+
+clock module for kdm
+
+Copyright (C) 2000 Espen Sand, espen@kde.org
+ Based on work by NN (yet to be determined)
+
+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.
+
+*/
+
+#ifndef _KDM_CLOCK_H_
+#define _KDM_CLOCK_H_
+
+#include <qframe.h>
+
+class KdmClock : public QFrame {
+ Q_OBJECT
+ typedef QFrame inherited;
+
+ public:
+ KdmClock( QWidget *parent=0, const char *name=0 );
+
+ protected:
+ virtual void showEvent( QShowEvent * );
+ virtual void paintEvent( QPaintEvent * );
+
+ private slots:
+ void timeout();
+
+ private:
+ QBrush mBackgroundBrush;
+ QFont mFont;
+ bool mSecond;
+ bool mDigital;
+ bool mDate;
+ bool mBorder;
+};
+
+#endif
diff --git a/kdm/kfrontend/kdmconfig.cpp b/kdm/kfrontend/kdmconfig.cpp
new file mode 100644
index 000000000..5d8d24e91
--- /dev/null
+++ b/kdm/kfrontend/kdmconfig.cpp
@@ -0,0 +1,177 @@
+/*
+
+Config for kdm
+
+Copyright (C) 1997, 1998 Steffen Hansen <hansen@kde.org>
+Copyright (C) 2000-2003 Oswald Buddenhagen <ossi@kde.org>
+
+
+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.
+
+*/
+
+#include "kdmconfig.h"
+#include "kdm_greet.h"
+
+#include <kapplication.h>
+#include <klocale.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/utsname.h>
+
+CONF_GREET_DEFS
+
+QString _stsFile;
+bool _isLocal;
+bool _authorized;
+
+static QString
+GetCfgQStr( int id )
+{
+ char *tmp = GetCfgStr( id );
+ QString qs = QString::fromUtf8( tmp );
+ free( tmp );
+ return qs;
+}
+
+static QStringList
+GetCfgQStrList( int id )
+{
+ int i, len;
+ char **tmp = GetCfgStrArr( id, &len );
+ QStringList qsl;
+ for (i = 0; i < len - 1; i++) {
+ qsl.append( QString::fromUtf8( tmp[i] ) );
+ free( tmp[i] );
+ }
+ free( tmp );
+ return qsl;
+}
+
+// Based on kconfigbase.cpp
+static QFont
+Str2Font( const QString &aValue )
+{
+ uint nFontBits;
+ QFont aRetFont;
+ QString chStr;
+
+ QStringList sl = QStringList::split( QString::fromLatin1(","), aValue );
+
+ if (sl.count() == 1) {
+ /* X11 font spec */
+ aRetFont = QFont( aValue );
+ aRetFont.setRawMode( true );
+ } else if (sl.count() == 10) {
+ /* qt3 font spec */
+ aRetFont.fromString( aValue );
+ } else if (sl.count() == 6) {
+ /* backward compatible kde2 font spec */
+ aRetFont = QFont( sl[0], sl[1].toInt(), sl[4].toUInt() );
+
+ aRetFont.setStyleHint( (QFont::StyleHint)sl[2].toUInt() );
+
+ nFontBits = sl[5].toUInt();
+ aRetFont.setItalic( (nFontBits & 0x01) != 0 );
+ aRetFont.setUnderline( (nFontBits & 0x02) != 0 );
+ aRetFont.setStrikeOut( (nFontBits & 0x04) != 0 );
+ aRetFont.setFixedPitch( (nFontBits & 0x08) != 0 );
+ aRetFont.setRawMode( (nFontBits & 0x20) != 0 );
+ }
+ aRetFont.setStyleStrategy( (QFont::StyleStrategy)
+ (QFont::PreferMatch |
+ (_antiAliasing ? QFont::PreferAntialias : QFont::NoAntialias)) );
+
+ return aRetFont;
+}
+
+extern "C"
+void init_config( void )
+{
+ CONF_GREET_INIT
+
+ _isLocal = GetCfgInt( C_isLocal );
+ _hasConsole = _hasConsole && _isLocal && GetCfgInt( C_hasConsole );
+ _authorized = GetCfgInt( C_isAuthorized );
+
+ _stsFile = _dataDir + "/kdmsts";
+
+ // Greet String
+ char hostname[256], *ptr;
+ hostname[0] = '\0';
+ if (!gethostname( hostname, sizeof(hostname) ))
+ hostname[sizeof(hostname)-1] = '\0';
+ struct utsname tuname;
+ uname( &tuname );
+ QString gst = _greetString;
+ _greetString = QString::null;
+ int i, j, l = gst.length();
+ for (i = 0; i < l; i++) {
+ if (gst[i] == '%') {
+ switch (gst[++i].cell()) {
+ case '%': _greetString += gst[i]; continue;
+ case 'd': ptr = dname; break;
+ case 'h': ptr = hostname; break;
+ case 'n': ptr = tuname.nodename;
+ for (j = 0; ptr[j]; j++)
+ if (ptr[j] == '.') {
+ ptr[j] = 0;
+ break;
+ }
+ break;
+ case 's': ptr = tuname.sysname; break;
+ case 'r': ptr = tuname.release; break;
+ case 'm': ptr = tuname.machine; break;
+ default: _greetString += i18n("[fix kdmrc!]"); continue;
+ }
+ _greetString += QString::fromLocal8Bit( ptr );
+ } else
+ _greetString += gst[i];
+ }
+}
+
+
+/* out-of-place utility function */
+void
+decodeSess( dpySpec *sess, QString &user, QString &loc )
+{
+ if (sess->flags & isTTY) {
+ user =
+ i18n( "%1: TTY login", "%1: %n TTY logins", sess->count )
+ .arg( sess->user );
+ loc =
+#ifdef HAVE_VTS
+ sess->vt ?
+ QString("vt%1").arg( sess->vt ) :
+#endif
+ QString::fromLatin1( *sess->from ? sess->from : sess->display );
+ } else {
+ user =
+ !sess->user ?
+ i18n("Unused") :
+ *sess->user ?
+ i18n("user: session type", "%1: %2")
+ .arg( sess->user ).arg( sess->session ) :
+ i18n("... host", "X login on %1").arg( sess->session );
+ loc =
+#ifdef HAVE_VTS
+ sess->vt ?
+ QString("%1, vt%2").arg( sess->display ).arg( sess->vt ) :
+#endif
+ QString::fromLatin1( sess->display );
+ }
+}
diff --git a/kdm/kfrontend/kdmconfig.h b/kdm/kfrontend/kdmconfig.h
new file mode 100644
index 000000000..3077a2ce0
--- /dev/null
+++ b/kdm/kfrontend/kdmconfig.h
@@ -0,0 +1,55 @@
+/*
+
+Configuration for kdm
+
+Copyright (C) 1997, 1998, 2000 Steffen Hansen <hansen@kde.org>
+Copyright (C) 2000-2003 Oswald Buddenhagen <ossi@kde.org>
+
+
+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.
+
+*/
+
+
+#ifndef KDMCONFIG_H
+#define KDMCONFIG_H
+
+#include <config.h>
+
+#include "config.ci"
+
+#ifdef __cplusplus
+
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qfont.h>
+
+extern QString _stsFile;
+extern bool _isLocal;
+extern bool _authorized;
+
+CONF_GREET_CPP_DECLS
+
+// this file happens to be included everywhere, so just put it here
+struct dpySpec;
+void decodeSess( dpySpec *sess, QString &user, QString &loc );
+
+extern "C"
+#endif
+void init_config( void );
+
+CONF_GREET_C_DECLS
+
+#endif /* KDMCONFIG_H */
diff --git a/kdm/kfrontend/kdmctl.c b/kdm/kfrontend/kdmctl.c
new file mode 100644
index 000000000..72e133162
--- /dev/null
+++ b/kdm/kfrontend/kdmctl.c
@@ -0,0 +1,236 @@
+/*
+
+KDM remote control application
+
+Copyright (C) 2004 Oswald Buddenhagen <ossi@kde.org>
+
+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.
+
+*/
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+static int
+openctl( int fd, int err, const char *ctl, const char *dpy )
+{
+ struct sockaddr_un sa;
+
+ sa.sun_family = AF_UNIX;
+ if (dpy)
+ snprintf( sa.sun_path, sizeof(sa.sun_path),
+ "%s/dmctl-%s/socket", ctl, dpy );
+ else
+ snprintf( sa.sun_path, sizeof(sa.sun_path),
+ "%s/dmctl/socket", ctl );
+ if (!connect( fd, (struct sockaddr *)&sa, sizeof(sa) ))
+ return 1;
+ if (err)
+ fprintf( stderr, "Cannot connect socket '%s'.\n", sa.sun_path );
+ return 0;
+}
+
+static const char *
+readcfg( const char *cfg )
+{
+ FILE *fp;
+ const char *ctl;
+ char *ptr, *ptr2;
+ char buf[1024];
+
+ if (!(fp = fopen( cfg, "r" ))) {
+ fprintf( stderr,
+ "Cannot open kdm config file '%s'.\n",
+ cfg );
+ return 0;
+ }
+ ctl = "/var/run/xdmctl";
+ while (fgets( buf, sizeof(buf), fp ))
+ if (!strncmp( buf, "FifoDir", 7 )) {
+ ptr = buf + 7;
+ while (*ptr && isspace( *ptr ))
+ ptr++;
+ if (*ptr++ != '=')
+ continue;
+ while (*ptr && isspace( *ptr ))
+ ptr++;
+ for (ptr2 = buf + strlen( buf );
+ ptr2 > ptr && isspace( *(ptr2 - 1) );
+ ptr2--);
+ *ptr2 = 0;
+ ctl = strdup( ptr );
+ break;
+ }
+ fclose( fp );
+ return ctl;
+}
+
+static int
+exe( int fd, const char *in, int len )
+{
+ char buf[4096];
+
+ if (write( fd, in, len ) != len) {
+ fprintf( stderr, "Cannot send command\n" );
+ return 1;
+ }
+ if ((len = read( fd, buf, sizeof(buf) )) <= 0) {
+ fprintf( stderr, "Cannot receive reply\n" );
+ return 1;
+ }
+ if (len == sizeof(buf) && buf[sizeof(buf) - 1] != '\n')
+ fprintf( stderr, "Warning: reply is too long\n" );
+ fwrite( buf, 1, len, stdout );
+ if (len == sizeof(buf) && buf[sizeof(buf) - 1] != '\n')
+ puts( "[...]" );
+ return 0;
+}
+
+static int
+run( int fd, char **argv )
+{
+ unsigned len, l;
+ char buf[1024];
+
+ if (!*argv)
+ return exe( fd, "caps\n", 5 );
+ if (!strcmp( *argv, "-" )) {
+ for (;;) {
+ if (isatty( 0 )) {
+ fputs( "> ", stdout );
+ fflush( stdout );
+ }
+ if (!fgets( buf, sizeof(buf), stdin ))
+ return 0;
+ if (exe( fd, buf, strlen( buf ) ))
+ return 1;
+ }
+ } else {
+ len = strlen( *argv );
+ if (len >= sizeof(buf))
+ goto bad;
+ memcpy( buf, *argv, len );
+ while (*++argv) {
+ l = strlen( *argv );
+ if (len + l + 1 >= sizeof(buf))
+ goto bad;
+ buf[len++] = '\t';
+ memcpy( buf + len, *argv, l );
+ len += l;
+ }
+ buf[len++] = '\n';
+ return exe( fd, buf, len );
+ bad:
+ fprintf( stderr, "Command too long\n" );
+ return 1;
+ }
+}
+
+int
+main( int argc, char **argv )
+{
+ char *dpy = getenv( "DISPLAY" );
+ const char *ctl = getenv( "DM_CONTROL" );
+ const char *cfg = KDE_CONFDIR "/kdm/kdmrc";
+ char *ptr;
+ int fd;
+
+ (void)argc;
+ while (*++argv) {
+ ptr = *argv;
+ if (*ptr != '-' || !*(ptr + 1))
+ break;
+ ptr++;
+ if (*ptr == '-')
+ ptr++;
+ if (!strcmp( ptr, "h" ) || !strcmp( ptr, "help" )) {
+ puts(
+"Usage: kdmctl [options] [command [command arguments]]\n"
+"\n"
+"Options are:\n"
+" -h -help This help message.\n"
+" -g -global Use global control socket even if $DISPLAY is set\n"
+" -d -display Override $DISPLAY\n"
+" -s -sockets Override $DM_CONTROL\n"
+" -c -config Use alternative kdm config file\n"
+"\n"
+"The directory in which the sockets are located is determined this way:\n"
+"- the -s option is examined\n"
+"- the $DM_CONTROL variable is examined\n"
+"- the kdm config file is searched for the FifoDir key\n"
+"- /var/run/xdmctl and /var/run are tried\n"
+"\n"
+"If $DISPLAY is set (or -d was specified) and -g was not specified, the\n"
+"display-specific control socket will be used, otherwise the global one.\n"
+"\n"
+"Tokens in the command and the reply are tab-separated.\n"
+"Command arguments can be specified as separate command line parameters,\n"
+"in which case they are simply concatenated with tabs in between.\n"
+"\n"
+"If the command is '-', kdmctl reads commands from stdin.\n"
+"The default command is 'caps'.\n"
+ );
+ return 0;
+ } else if (!strcmp( ptr, "g" ) || !strcmp( ptr, "global" ))
+ dpy = 0;
+ else if (!strcmp( ptr, "d" ) || !strcmp( ptr, "display" )) {
+ if (!argv[1])
+ goto needarg;
+ dpy = *++argv;
+ } else if (!strcmp( ptr, "s" ) || !strcmp( ptr, "sockets" )) {
+ if (!argv[1])
+ goto needarg;
+ ctl = *++argv;
+ } else if (!strcmp( ptr, "c" ) || !strcmp( ptr, "config" )) {
+ if (!argv[1]) {
+ needarg:
+ fprintf( stderr, "Option '%s' needs argument.\n",
+ ptr );
+ return 1;
+ }
+ cfg = *++argv;
+ } else {
+ fprintf( stderr, "Unknown option '%s'.\n", ptr );
+ return 1;
+ }
+ }
+ if ((!ctl || !*ctl) && *cfg)
+ ctl = readcfg( cfg );
+ if ((fd = socket( PF_UNIX, SOCK_STREAM, 0 )) < 0) {
+ fprintf( stderr, "Cannot create UNIX socket\n" );
+ return 1;
+ }
+ if (dpy && (ptr = strchr( dpy, ':' )) && (ptr = strchr( ptr, '.' )))
+ *ptr = 0;
+ if (ctl && *ctl) {
+ if (!openctl( fd, 1, ctl, dpy ))
+ return 1;
+ } else {
+ if (!openctl( fd, 0, "/var/run/xdmctl", dpy ) &&
+ !openctl( fd, 0, "/var/run", dpy ))
+ {
+ fprintf( stderr, "No command socket found.\n" );
+ return 1;
+ }
+ }
+ return run( fd, argv );
+}
diff --git a/kdm/kfrontend/kdmshutdown.cpp b/kdm/kfrontend/kdmshutdown.cpp
new file mode 100644
index 000000000..dfd8558e2
--- /dev/null
+++ b/kdm/kfrontend/kdmshutdown.cpp
@@ -0,0 +1,714 @@
+/*
+
+Shutdown dialog
+
+Copyright (C) 1997, 1998, 2000 Steffen Hansen <hansen@kde.org>
+Copyright (C) 2000-2003,2005 Oswald Buddenhagen <ossi@kde.org>
+
+
+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.
+
+*/
+
+#include "kdmshutdown.h"
+#include "kdm_greet.h"
+
+#include <kapplication.h>
+#include <kseparator.h>
+#include <klocale.h>
+#include <kpushbutton.h>
+#include <kstdguiitem.h>
+#include <kprocio.h>
+#include <kdialog.h>
+#include <kstandarddirs.h>
+#include <kuser.h>
+
+#include <qcombobox.h>
+#include <qvbuttongroup.h>
+#include <qstyle.h>
+#include <qlayout.h>
+#include <qaccel.h>
+#include <qpopupmenu.h>
+#include <qcheckbox.h>
+#include <qlineedit.h>
+#include <qlabel.h>
+#include <qdatetime.h>
+#include <qlistview.h>
+#include <qheader.h>
+#include <qdatetime.h>
+
+#define KDmh KDialog::marginHint()
+#define KDsh KDialog::spacingHint()
+
+#include <stdlib.h>
+
+int KDMShutdownBase::curPlugin = -1;
+PluginList KDMShutdownBase::pluginList;
+
+KDMShutdownBase::KDMShutdownBase( int _uid, QWidget *_parent )
+ : inherited( _parent )
+ , box( new QVBoxLayout( this, KDmh, KDsh ) )
+#ifdef HAVE_VTS
+ , willShut( true )
+#endif
+ , mayNuke( false )
+ , doesNuke( false )
+ , mayOk( true )
+ , maySched( false )
+ , rootlab( 0 )
+ , verify( 0 )
+ , needRoot( -1 )
+ , uid( _uid )
+{
+}
+
+KDMShutdownBase::~KDMShutdownBase()
+{
+ hide();
+ delete verify;
+}
+
+void
+KDMShutdownBase::complete( QWidget *prevWidget )
+{
+ QSizePolicy fp( QSizePolicy::Fixed, QSizePolicy::Fixed );
+
+ if (uid &&
+ ((willShut && _allowShutdown == SHUT_ROOT) ||
+ (mayNuke && _allowNuke == SHUT_ROOT)))
+ {
+ rootlab = new QLabel( i18n("Root authorization required."), this );
+ box->addWidget( rootlab );
+ if (curPlugin < 0) {
+ curPlugin = 0;
+ pluginList = KGVerify::init( _pluginsShutdown );
+ }
+ verify = new KGStdVerify( this, this,
+ prevWidget, "root",
+ pluginList, KGreeterPlugin::Authenticate,
+ KGreeterPlugin::Shutdown );
+ verify->selectPlugin( curPlugin );
+ box->addLayout( verify->getLayout() );
+ QAccel *accel = new QAccel( this );
+ accel->insertItem( ALT+Key_A, 0 );
+ connect( accel, SIGNAL(activated( int )), SLOT(slotActivatePlugMenu()) );
+ }
+
+ box->addWidget( new KSeparator( KSeparator::HLine, this ) );
+
+ QBoxLayout *hlay = new QHBoxLayout( box, KDsh );
+ hlay->addStretch( 1 );
+ if (mayOk) {
+ okButton = new KPushButton( KStdGuiItem::ok(), this );
+ okButton->setSizePolicy( fp );
+ okButton->setDefault( true );
+ hlay->addWidget( okButton );
+ hlay->addStretch( 1 );
+ connect( okButton, SIGNAL(clicked()), SLOT(accept()) );
+ }
+ if (maySched) {
+ KPushButton *schedButton =
+ new KPushButton( KGuiItem( i18n("&Schedule...") ), this );
+ schedButton->setSizePolicy( fp );
+ hlay->addWidget( schedButton );
+ hlay->addStretch( 1 );
+ connect( schedButton, SIGNAL(clicked()), SLOT(slotSched()) );
+ }
+ cancelButton = new KPushButton( KStdGuiItem::cancel(), this );
+ cancelButton->setSizePolicy( fp );
+ if (!mayOk)
+ cancelButton->setDefault( true );
+ hlay->addWidget( cancelButton );
+ hlay->addStretch( 1 );
+ connect( cancelButton, SIGNAL(clicked()), SLOT(reject()) );
+
+ updateNeedRoot();
+}
+
+void
+KDMShutdownBase::slotActivatePlugMenu()
+{
+ if (needRoot) {
+ QPopupMenu *cmnu = verify->getPlugMenu();
+ if (!cmnu)
+ return;
+ QSize sh( cmnu->sizeHint() / 2 );
+ cmnu->exec( geometry().center() - QPoint( sh.width(), sh.height() ) );
+ }
+}
+
+void
+KDMShutdownBase::accept()
+{
+ if (needRoot == 1)
+ verify->accept();
+ else
+ accepted();
+}
+
+void
+KDMShutdownBase::slotSched()
+{
+ done( Schedule );
+}
+
+void
+KDMShutdownBase::updateNeedRoot()
+{
+ int nNeedRoot = uid &&
+ (((willShut && _allowShutdown == SHUT_ROOT) ||
+ (_allowNuke == SHUT_ROOT && doesNuke)));
+ if (verify && nNeedRoot != needRoot) {
+ if (needRoot == 1)
+ verify->abort();
+ needRoot = nNeedRoot;
+ rootlab->setEnabled( needRoot );
+ verify->setEnabled( needRoot );
+ if (needRoot)
+ verify->start();
+ }
+}
+
+void
+KDMShutdownBase::accepted()
+{
+ inherited::done( needRoot ? (int)Authed : (int)Accepted );
+}
+
+void
+KDMShutdownBase::verifyPluginChanged( int id )
+{
+ curPlugin = id;
+ adjustSize();
+}
+
+void
+KDMShutdownBase::verifyOk()
+{
+ accepted();
+}
+
+void
+KDMShutdownBase::verifyFailed()
+{
+ okButton->setEnabled( false );
+ cancelButton->setEnabled( false );
+}
+
+void
+KDMShutdownBase::verifyRetry()
+{
+ okButton->setEnabled( true );
+ cancelButton->setEnabled( true );
+}
+
+void
+KDMShutdownBase::verifySetUser( const QString & )
+{
+}
+
+
+static void
+doShutdown( int type, const char *os )
+{
+ GSet( 1 );
+ GSendInt( G_Shutdown );
+ GSendInt( type );
+ GSendInt( 0 );
+ GSendInt( 0 );
+ GSendInt( SHUT_FORCE );
+ GSendInt( 0 ); /* irrelevant, will timeout immediately anyway */
+ GSendStr( os );
+ GSet( 0 );
+}
+
+
+
+KDMShutdown::KDMShutdown( int _uid, QWidget *_parent )
+ : inherited( _uid, _parent )
+{
+ QSizePolicy fp( QSizePolicy::Fixed, QSizePolicy::Fixed );
+
+ QHBoxLayout *hlay = new QHBoxLayout( box, KDsh );
+
+ howGroup = new QVButtonGroup( i18n("Shutdown Type"), this );
+ hlay->addWidget( howGroup, 0, AlignTop );
+
+ QRadioButton *rb;
+ rb = new KDMRadioButton( i18n("&Turn off computer"), howGroup );
+ rb->setChecked( true );
+ rb->setFocus();
+
+ restart_rb = new KDMRadioButton( i18n("&Restart computer"), howGroup );
+
+ connect( rb, SIGNAL(doubleClicked()), SLOT(accept()) );
+ connect( restart_rb, SIGNAL(doubleClicked()), SLOT(accept()) );
+
+ GSet( 1 );
+ GSendInt( G_ListBootOpts );
+ if (GRecvInt() == BO_OK) { /* XXX show dialog on failure */
+ char **tlist = GRecvStrArr( 0 );
+ int defaultTarget = GRecvInt();
+ oldTarget = GRecvInt();
+ QWidget *hlp = new QWidget( howGroup );
+ targets = new QComboBox( hlp );
+ for (int i = 0; tlist[i]; i++)
+ targets->insertItem( QString::fromLocal8Bit( tlist[i] ) );
+ freeStrArr( tlist );
+ targets->setCurrentItem( oldTarget == -1 ? defaultTarget : oldTarget );
+ QHBoxLayout *hb = new QHBoxLayout( hlp, 0, KDsh );
+ int spc = kapp->style().pixelMetric( QStyle::PM_ExclusiveIndicatorWidth )
+ + howGroup->insideSpacing();
+ hb->addSpacing( spc );
+ hb->addWidget( targets );
+ connect( targets, SIGNAL(activated( int )), SLOT(slotTargetChanged()) );
+ }
+ GSet( 0 );
+
+ howGroup->setSizePolicy( fp );
+
+ schedGroup = new QGroupBox( i18n("Scheduling"), this );
+ hlay->addWidget( schedGroup, 0, AlignTop );
+
+ le_start = new QLineEdit( schedGroup );
+ QLabel *lab1 = new QLabel( le_start, i18n("&Start:"), schedGroup );
+
+ le_timeout = new QLineEdit( schedGroup );
+ QLabel *lab2 = new QLabel( le_timeout, i18n("T&imeout:"), schedGroup );
+
+ cb_force = new QCheckBox( i18n("&Force after timeout"), schedGroup );
+ if (_allowNuke != SHUT_NONE) {
+ connect( cb_force, SIGNAL(clicked()), SLOT(slotWhenChanged()) );
+ mayNuke = true;
+ } else
+ cb_force->setEnabled( false );
+
+ QGridLayout *grid = new QGridLayout( schedGroup, 0, 0, KDmh, KDsh );
+ grid->addRowSpacing( 0, schedGroup->fontMetrics().height() - 5 );
+ grid->addWidget( lab1, 1, 0, AlignRight );
+ grid->addWidget( le_start, 1, 1 );
+ grid->addWidget( lab2, 2, 0, AlignRight );
+ grid->addWidget( le_timeout, 2, 1 );
+ grid->addMultiCellWidget( cb_force, 3,3, 0,1 );
+
+ schedGroup->setSizePolicy( fp );
+
+ le_start->setText( "0" );
+ if (_defSdMode == SHUT_SCHEDULE)
+ le_timeout->setText( "-1" );
+ else {
+ le_timeout->setText( "0" );
+ if (_defSdMode == SHUT_FORCENOW && cb_force->isEnabled())
+ cb_force->setChecked( true );
+ }
+
+ complete( schedGroup );
+}
+
+static int
+get_date( const char *str )
+{
+ KProcIO prc;
+ prc << "/bin/date" << "+%s" << "-d" << str;
+ prc.start( KProcess::Block, false );
+ QString dstr;
+ if (prc.readln( dstr, false, 0 ) < 0)
+ return -1;
+ return dstr.toInt();
+}
+
+void
+KDMShutdown::accept()
+{
+ if (le_start->text() == "0" || le_start->text() == "now")
+ sch_st = time( 0 );
+ else if (le_start->text()[0] == '+')
+ sch_st = time( 0 ) + le_start->text().toInt();
+ else if ((sch_st = get_date( le_start->text().latin1() )) < 0) {
+ MsgBox( errorbox, i18n("Entered start date is invalid.") );
+ le_start->setFocus();
+ return;
+ }
+ if (le_timeout->text() == "-1" || le_timeout->text().startsWith( "inf" ))
+ sch_to = TO_INF;
+ else if (le_timeout->text()[0] == '+')
+ sch_to = sch_st + le_timeout->text().toInt();
+ else if ((sch_to = get_date( le_timeout->text().latin1() )) < 0) {
+ MsgBox( errorbox, i18n("Entered timeout date is invalid.") );
+ le_timeout->setFocus();
+ return;
+ }
+
+ inherited::accept();
+}
+
+void
+KDMShutdown::slotTargetChanged()
+{
+ restart_rb->setChecked( true );
+}
+
+void
+KDMShutdown::slotWhenChanged()
+{
+ doesNuke = cb_force->isChecked();
+ updateNeedRoot();
+}
+
+void
+KDMShutdown::accepted()
+{
+ GSet( 1 );
+ GSendInt( G_Shutdown );
+ GSendInt( restart_rb->isChecked() ? SHUT_REBOOT : SHUT_HALT );
+ GSendInt( sch_st );
+ GSendInt( sch_to );
+ GSendInt( cb_force->isChecked() ? SHUT_FORCE : SHUT_CANCEL );
+ GSendInt( _allowShutdown == SHUT_ROOT ? 0 : -2 );
+ GSendStr( (restart_rb->isChecked() &&
+ targets && targets->currentItem() != oldTarget) ?
+ targets->currentText().local8Bit().data() : 0 );
+ GSet( 0 );
+ inherited::accepted();
+}
+
+void
+KDMShutdown::scheduleShutdown( QWidget *_parent )
+{
+ GSet( 1 );
+ GSendInt( G_QueryShutdown );
+ int how = GRecvInt();
+ int start = GRecvInt();
+ int timeout = GRecvInt();
+ int force = GRecvInt();
+ int uid = GRecvInt();
+ char *os = GRecvStr();
+ GSet( 0 );
+ if (how) {
+ int ret =
+ KDMCancelShutdown( how, start, timeout, force, uid, os,
+ _parent ).exec();
+ if (!ret)
+ return;
+ doShutdown( 0, 0 );
+ uid = ret == Authed ? 0 : -1;
+ } else
+ uid = -1;
+ if (os)
+ free( os );
+ KDMShutdown( uid, _parent ).exec();
+}
+
+
+KDMRadioButton::KDMRadioButton( const QString &label, QWidget *parent )
+ : inherited( label, parent )
+{
+}
+
+void
+KDMRadioButton::mouseDoubleClickEvent( QMouseEvent * )
+{
+ emit doubleClicked();
+}
+
+
+KDMDelayedPushButton::KDMDelayedPushButton( const KGuiItem &item,
+ QWidget *parent,
+ const char *name )
+ : inherited( item, parent, name )
+ , pop( 0 )
+{
+ connect( this, SIGNAL(pressed()), SLOT(slotPressed()) );
+ connect( this, SIGNAL(released()), SLOT(slotReleased()) );
+ connect( &popt, SIGNAL(timeout()), SLOT(slotTimeout()) );
+}
+
+void KDMDelayedPushButton::setPopup( QPopupMenu *p )
+{
+ pop = p;
+ setIsMenuButton( p != 0 );
+}
+
+void KDMDelayedPushButton::slotPressed()
+{
+ if (pop)
+ popt.start( QApplication::startDragTime() );
+}
+
+void KDMDelayedPushButton::slotReleased()
+{
+ popt.stop();
+}
+
+void KDMDelayedPushButton::slotTimeout()
+{
+ popt.stop();
+ pop->popup( mapToGlobal( rect().bottomLeft() ) );
+ setDown( false );
+}
+
+
+KDMSlimShutdown::KDMSlimShutdown( QWidget *_parent )
+ : inherited( _parent )
+ , targetList( 0 )
+{
+ QHBoxLayout *hbox = new QHBoxLayout( this, KDmh, KDsh );
+
+ QFrame *lfrm = new QFrame( this );
+ lfrm->setFrameStyle( QFrame::Panel | QFrame::Sunken );
+ hbox->addWidget( lfrm, AlignCenter );
+ QLabel *icon = new QLabel( lfrm );
+ icon->setPixmap( QPixmap( locate( "data", "kdm/pics/shutdown.jpg" ) ) );
+ QVBoxLayout *iconlay = new QVBoxLayout( lfrm );
+ iconlay->addWidget( icon );
+
+ QVBoxLayout *buttonlay = new QVBoxLayout( hbox, KDsh );
+
+ buttonlay->addStretch( 1 );
+
+ KPushButton *btnHalt = new
+ KPushButton( KGuiItem( i18n("&Turn Off Computer"), "exit" ), this );
+ buttonlay->addWidget( btnHalt );
+ connect( btnHalt, SIGNAL(clicked()), SLOT(slotHalt()) );
+
+ buttonlay->addSpacing( KDialog::spacingHint() );
+
+ KDMDelayedPushButton *btnReboot = new
+ KDMDelayedPushButton( KGuiItem( i18n("&Restart Computer"), "reload" ), this );
+ buttonlay->addWidget( btnReboot );
+ connect( btnReboot, SIGNAL(clicked()), SLOT(slotReboot()) );
+
+ GSet( 1 );
+ GSendInt( G_ListBootOpts );
+ if (GRecvInt() == BO_OK) {
+ targetList = GRecvStrArr( 0 );
+ /*int def =*/ GRecvInt();
+ int cur = GRecvInt();
+ QPopupMenu *targets = new QPopupMenu( this );
+ for (int i = 0; targetList[i]; i++) {
+ QString t( QString::fromLocal8Bit( targetList[i] ) );
+ targets->insertItem( i == cur ?
+ i18n("current option in boot loader",
+ "%1 (current)").arg( t ) :
+ t, i );
+ }
+ btnReboot->setPopup( targets );
+ connect( targets, SIGNAL(activated(int)), SLOT(slotReboot(int)) );
+ }
+ GSet( 0 );
+
+ buttonlay->addStretch( 1 );
+
+ if (_scheduledSd != SHUT_NEVER) {
+ KPushButton *btnSched = new
+ KPushButton( KGuiItem( i18n("&Schedule...") ), this );
+ buttonlay->addWidget( btnSched );
+ connect( btnSched, SIGNAL(clicked()), SLOT(slotSched()) );
+
+ buttonlay->addStretch( 1 );
+ }
+
+ buttonlay->addWidget( new KSeparator( this ) );
+
+ buttonlay->addSpacing( 0 );
+
+ KPushButton *btnBack = new KPushButton( KStdGuiItem::cancel(), this );
+ buttonlay->addWidget( btnBack );
+ connect( btnBack, SIGNAL(clicked()), SLOT(reject()) );
+
+ buttonlay->addSpacing( KDialog::spacingHint() );
+}
+
+KDMSlimShutdown::~KDMSlimShutdown()
+{
+ freeStrArr( targetList );
+}
+
+void
+KDMSlimShutdown::slotSched()
+{
+ reject();
+ KDMShutdown::scheduleShutdown();
+}
+
+void
+KDMSlimShutdown::slotHalt()
+{
+ if (checkShutdown( SHUT_HALT, 0 ))
+ doShutdown( SHUT_HALT, 0 );
+}
+
+void
+KDMSlimShutdown::slotReboot()
+{
+ if (checkShutdown( SHUT_REBOOT, 0 ))
+ doShutdown( SHUT_REBOOT, 0 );
+}
+
+void
+KDMSlimShutdown::slotReboot( int opt )
+{
+ if (checkShutdown( SHUT_REBOOT, targetList[opt] ))
+ doShutdown( SHUT_REBOOT, targetList[opt] );
+}
+
+bool
+KDMSlimShutdown::checkShutdown( int type, const char *os )
+{
+ reject();
+ dpySpec *sess = fetchSessions( lstRemote | lstTTY );
+ if (!sess && _allowShutdown != SHUT_ROOT)
+ return true;
+ int ret = KDMConfShutdown( -1, sess, type, os ).exec();
+ disposeSessions( sess );
+ if (ret == Schedule) {
+ KDMShutdown::scheduleShutdown();
+ return false;
+ }
+ return ret;
+}
+
+void
+KDMSlimShutdown::externShutdown( int type, const char *os, int uid )
+{
+ dpySpec *sess = fetchSessions( lstRemote | lstTTY );
+ int ret = KDMConfShutdown( uid, sess, type, os ).exec();
+ disposeSessions( sess );
+ if (ret == Schedule)
+ KDMShutdown( uid ).exec();
+ else if (ret)
+ doShutdown( type, os );
+}
+
+
+KDMConfShutdown::KDMConfShutdown( int _uid, dpySpec *sess, int type, const char *os,
+ QWidget *_parent )
+ : inherited( _uid, _parent )
+{
+#ifdef HAVE_VTS
+ if (type == SHUT_CONSOLE)
+ willShut = false;
+#endif
+ box->addWidget( new QLabel( QString( "<qt><center><b><nobr>"
+ "%1%2"
+ "</nobr></b></center><br></qt>" )
+ .arg( (type == SHUT_HALT) ?
+ i18n("Turn Off Computer") :
+#ifdef HAVE_VTS
+ (type == SHUT_CONSOLE) ?
+ i18n("Switch to Console") :
+#endif
+ i18n("Restart Computer") )
+ .arg( os ?
+ i18n("<br>(Next boot: %1)")
+ .arg( QString::fromLocal8Bit( os ) ) :
+ QString::null ),
+ this ) );
+
+ if (sess) {
+ if (willShut && _scheduledSd != SHUT_NEVER)
+ maySched = true;
+ mayNuke = doesNuke = true;
+ if (_allowNuke == SHUT_NONE)
+ mayOk = false;
+ QLabel *lab = new QLabel( mayOk ?
+ i18n("Abort active sessions:") :
+ i18n("No permission to abort active sessions:"),
+ this );
+ box->addWidget( lab );
+ QListView *lv = new QListView( this );
+ lv->setSelectionMode( QListView::NoSelection );
+ lv->setAllColumnsShowFocus( true );
+ lv->header()->setResizeEnabled( false );
+ lv->addColumn( i18n("Session") );
+ lv->addColumn( i18n("Location") );
+ QListViewItem *itm;
+ int ns = 0;
+ QString user, loc;
+ do {
+ decodeSess( sess, user, loc );
+ itm = new QListViewItem( lv, user, loc );
+ sess = sess->next, ns++;
+ } while (sess);
+ int fw = lv->frameWidth() * 2;
+ lv->setFixedHeight( fw + lv->header()->height() +
+ itm->height() * (ns < 3 ? 3 : ns > 10 ? 10 : ns) );
+ box->addWidget( lv );
+ complete( lv );
+ } else
+ complete( 0 );
+}
+
+
+KDMCancelShutdown::KDMCancelShutdown( int how, int start, int timeout,
+ int force, int uid, const char *os,
+ QWidget *_parent )
+ : inherited( -1, _parent )
+{
+ if (force == SHUT_FORCE) {
+ if (_allowNuke == SHUT_NONE)
+ mayOk = false;
+ else if (_allowNuke == SHUT_ROOT)
+ mayNuke = doesNuke = true;
+ }
+ QLabel *lab = new QLabel( mayOk ?
+ i18n("Abort pending shutdown:") :
+ i18n("No permission to abort pending shutdown:"),
+ this );
+ box->addWidget( lab );
+ QDateTime qdt;
+ QString strt, end;
+ if (start < time( 0 ))
+ strt = i18n("now");
+ else {
+ qdt.setTime_t( start );
+ strt = qdt.toString( LocalDate );
+ }
+ if (timeout == TO_INF)
+ end = i18n("infinite");
+ else {
+ qdt.setTime_t( timeout );
+ end = qdt.toString( LocalDate );
+ }
+ QString trg =
+ i18n("Owner: %1"
+ "\nType: %2%5"
+ "\nStart: %3"
+ "\nTimeout: %4")
+ .arg( uid == -2 ?
+ i18n("console user") :
+ uid == -1 ?
+ i18n("control socket") :
+ KUser( uid ).loginName() )
+ .arg( how == SHUT_HALT ?
+ i18n("turn off computer") :
+ i18n("restart computer") )
+ .arg( strt ).arg( end )
+ .arg( os ?
+ i18n("\nNext boot: %1").arg( QString::fromLocal8Bit( os ) ) :
+ QString::null );
+ if (timeout != TO_INF)
+ trg += i18n("\nAfter timeout: %1")
+ .arg( force == SHUT_FORCE ?
+ i18n("abort all sessions") :
+ force == SHUT_FORCEMY ?
+ i18n("abort own sessions") :
+ i18n("cancel shutdown") );
+ lab = new QLabel( trg, this );
+ box->addWidget( lab );
+ complete( 0 );
+}
+
+#include "kdmshutdown.moc"
diff --git a/kdm/kfrontend/kdmshutdown.h b/kdm/kfrontend/kdmshutdown.h
new file mode 100644
index 000000000..9020b1513
--- /dev/null
+++ b/kdm/kfrontend/kdmshutdown.h
@@ -0,0 +1,196 @@
+/*
+
+Shutdown dialog
+
+Copyright (C) 1997, 1998 Steffen Hansen <hansen@kde.org>
+Copyright (C) 2000-2003,2005 Oswald Buddenhagen <ossi@kde.org>
+
+
+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.
+
+*/
+
+
+#ifndef KDMSHUTDOWN_H
+#define KDMSHUTDOWN_H
+
+#include "kdmconfig.h" // for HAVE_VTS
+#include "kgverify.h"
+
+#include <kpushbutton.h>
+
+#include <qradiobutton.h>
+
+class QLabel;
+class KPushButton;
+class QButtonGroup;
+class QGroupBox;
+class QComboBox;
+class QCheckBox;
+class QLineEdit;
+
+enum { Authed = QDialog::Accepted + 1, Schedule };
+
+class KDMShutdownBase : public FDialog, public KGVerifyHandler {
+ Q_OBJECT
+ typedef FDialog inherited;
+
+ public:
+ KDMShutdownBase( int _uid, QWidget *_parent );
+ virtual ~KDMShutdownBase();
+
+ protected slots:
+ virtual void accept();
+
+ protected:
+ virtual void accepted();
+
+ protected:
+ void updateNeedRoot();
+ void complete( QWidget *prevWidget );
+
+ QVBoxLayout *box;
+#ifdef HAVE_VTS
+ bool willShut;
+#else
+ static const bool willShut = true;
+#endif
+ bool mayNuke, doesNuke, mayOk, maySched;
+
+ private slots:
+ void slotSched();
+ void slotActivatePlugMenu();
+
+ private:
+ KPushButton *okButton, *cancelButton;
+ QLabel *rootlab;
+ KGStdVerify *verify;
+ int needRoot, uid;
+
+ static int curPlugin;
+ static PluginList pluginList;
+
+ public: // from KGVerifyHandler
+ virtual void verifyPluginChanged( int id );
+ virtual void verifyOk();
+ virtual void verifyFailed();
+ virtual void verifyRetry();
+ virtual void verifySetUser( const QString &user );
+};
+
+
+class KDMShutdown : public KDMShutdownBase {
+ Q_OBJECT
+ typedef KDMShutdownBase inherited;
+
+ public:
+ KDMShutdown( int _uid, QWidget *_parent = 0 );
+ static void scheduleShutdown( QWidget *_parent = 0 );
+
+ protected slots:
+ virtual void accept();
+
+ protected:
+ virtual void accepted();
+
+ private slots:
+ void slotTargetChanged();
+ void slotWhenChanged();
+
+ private:
+ QButtonGroup *howGroup;
+ QGroupBox *schedGroup;
+ QRadioButton *restart_rb;
+ QLineEdit *le_start, *le_timeout;
+ QCheckBox *cb_force;
+ QComboBox *targets;
+ int oldTarget;
+ int sch_st, sch_to;
+
+};
+
+class KDMRadioButton : public QRadioButton {
+ Q_OBJECT
+ typedef QRadioButton inherited;
+
+ public:
+ KDMRadioButton( const QString &label, QWidget *parent );
+
+ private:
+ virtual void mouseDoubleClickEvent( QMouseEvent * );
+
+ signals:
+ void doubleClicked();
+
+};
+
+class KDMDelayedPushButton : public KPushButton {
+ Q_OBJECT
+ typedef KPushButton inherited;
+
+ public:
+ KDMDelayedPushButton( const KGuiItem &item, QWidget *parent, const char *name = 0 );
+ void setPopup( QPopupMenu *pop );
+
+ private slots:
+ void slotTimeout();
+ void slotPressed();
+ void slotReleased();
+
+ private:
+ QPopupMenu *pop;
+ QTimer popt;
+};
+
+class KDMSlimShutdown : public FDialog {
+ Q_OBJECT
+ typedef FDialog inherited;
+
+ public:
+ KDMSlimShutdown( QWidget *_parent = 0 );
+ ~KDMSlimShutdown();
+ static void externShutdown( int type, const char *os, int uid );
+
+ private slots:
+ void slotHalt();
+ void slotReboot();
+ void slotReboot( int );
+ void slotSched();
+
+ private:
+ bool checkShutdown( int type, const char *os );
+ char **targetList;
+
+};
+
+class KDMConfShutdown : public KDMShutdownBase {
+ Q_OBJECT
+ typedef KDMShutdownBase inherited;
+
+ public:
+ KDMConfShutdown( int _uid, struct dpySpec *sess, int type, const char *os,
+ QWidget *_parent = 0 );
+};
+
+class KDMCancelShutdown : public KDMShutdownBase {
+ Q_OBJECT
+ typedef KDMShutdownBase inherited;
+
+ public:
+ KDMCancelShutdown( int how, int start, int timeout, int force, int uid,
+ const char *os, QWidget *_parent );
+};
+
+#endif /* KDMSHUTDOWN_H */
diff --git a/kdm/kfrontend/kfdialog.cpp b/kdm/kfrontend/kfdialog.cpp
new file mode 100644
index 000000000..e3bb9d9c8
--- /dev/null
+++ b/kdm/kfrontend/kfdialog.cpp
@@ -0,0 +1,149 @@
+/*
+
+Dialog class that handles input focus in absence of a wm
+
+Copyright (C) 1997, 1998 Steffen Hansen <hansen@kde.org>
+Copyright (C) 2000-2003 Oswald Buddenhagen <ossi@kde.org>
+
+
+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.
+
+*/
+
+#include "kfdialog.h"
+#include "kdmconfig.h"
+
+#include <klocale.h>
+#include <kpushbutton.h>
+#include <kstdguiitem.h>
+
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qapplication.h>
+#include <qcursor.h>
+
+FDialog::FDialog( QWidget *parent, bool framed )
+ : inherited( parent, 0, true/*, framed ? 0 : WStyle_NoBorder*/ )
+{
+ if (framed) {
+ winFrame = new QFrame( this, 0, WNoAutoErase );
+ winFrame->setFrameStyle( QFrame::WinPanel | QFrame::Raised );
+ winFrame->setLineWidth( 2 );
+ } else
+ winFrame = 0;
+}
+
+void
+FDialog::resizeEvent( QResizeEvent *e )
+{
+ inherited::resizeEvent( e );
+ if (winFrame) {
+ winFrame->resize( size() );
+ winFrame->erase();
+ }
+}
+
+void
+FDialog::adjustGeometry()
+{
+ QDesktopWidget *dsk = qApp->desktop();
+
+ if (_greeterScreen < 0)
+ _greeterScreen = _greeterScreen == -2 ?
+ dsk->screenNumber( QPoint( dsk->width() - 1, 0 ) ) :
+ dsk->screenNumber( QPoint( 0, 0 ) );
+
+ QRect scr = dsk->screenGeometry( _greeterScreen );
+ if (!winFrame)
+ setFixedSize( scr.size() );
+ else {
+ setMaximumSize( scr.size() * .9 );
+ adjustSize();
+ }
+
+ if (parentWidget())
+ return;
+
+ QRect grt( rect() );
+ if (winFrame) {
+ unsigned x = 50, y = 50;
+ sscanf( _greeterPos, "%u,%u", &x, &y );
+ grt.moveCenter( QPoint( scr.x() + scr.width() * x / 100,
+ scr.y() + scr.height() * y / 100 ) );
+ int di;
+ if ((di = scr.right() - grt.right()) < 0)
+ grt.moveBy( di, 0 );
+ if ((di = scr.left() - grt.left()) > 0)
+ grt.moveBy( di, 0 );
+ if ((di = scr.bottom() - grt.bottom()) < 0)
+ grt.moveBy( 0, di );
+ if ((di = scr.top() - grt.top()) > 0)
+ grt.moveBy( 0, di );
+ setGeometry( grt );
+ }
+
+ if (dsk->screenNumber( QCursor::pos() ) != _greeterScreen)
+ QCursor::setPos( grt.center() );
+}
+
+struct WinList {
+ struct WinList *next;
+ QWidget *win;
+};
+
+int
+FDialog::exec()
+{
+ static WinList *wins;
+ WinList *win;
+
+ win = new WinList;
+ win->win = this;
+ win->next = wins;
+ wins = win;
+ show();
+ setActiveWindow();
+ inherited::exec();
+ hide();
+ wins = win->next;
+ delete win;
+ if (wins)
+ wins->win->setActiveWindow();
+ return result();
+}
+
+void
+FDialog::box( QWidget *parent, QMessageBox::Icon type, const QString &text )
+{
+ KFMsgBox dlg( parent, type, text.stripWhiteSpace() );
+ dlg.exec();
+}
+
+KFMsgBox::KFMsgBox( QWidget *parent, QMessageBox::Icon type, const QString &text )
+ : inherited( parent )
+{
+ QLabel *label1 = new QLabel( this );
+ label1->setPixmap( QMessageBox::standardIcon( type ) );
+ QLabel *label2 = new QLabel( text, this );
+ KPushButton *button = new KPushButton( KStdGuiItem::ok(), this );
+ button->setDefault( true );
+ button->setSizePolicy( QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred ) );
+ connect( button, SIGNAL(clicked()), SLOT(accept()) );
+
+ QGridLayout *grid = new QGridLayout( this, 2, 2, 10 );
+ grid->addWidget( label1, 0, 0, Qt::AlignCenter );
+ grid->addWidget( label2, 0, 1, Qt::AlignCenter );
+ grid->addMultiCellWidget( button, 1,1, 0,1, Qt::AlignCenter );
+}
diff --git a/kdm/kfrontend/kfdialog.h b/kdm/kfrontend/kfdialog.h
new file mode 100644
index 000000000..7ab456f22
--- /dev/null
+++ b/kdm/kfrontend/kfdialog.h
@@ -0,0 +1,63 @@
+/*
+
+Dialog class that handles input focus in absence of a wm
+
+Copyright (C) 1997, 1998 Steffen Hansen <hansen@kde.org>
+Copyright (C) 2000-2003 Oswald Buddenhagen <ossi@kde.org>
+
+
+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.
+
+*/
+
+
+#ifndef FDIALOG_H
+#define FDIALOG_H
+
+#include <qdialog.h>
+#include <qmessagebox.h>
+
+class QFrame;
+
+class FDialog : public QDialog {
+ typedef QDialog inherited;
+
+ public:
+ FDialog( QWidget *parent = 0, bool framed = true );
+ virtual int exec();
+
+ static void box( QWidget *parent, QMessageBox::Icon type,
+ const QString &text );
+#define errorbox QMessageBox::Critical
+#define sorrybox QMessageBox::Warning
+#define infobox QMessageBox::Information
+ void MsgBox( QMessageBox::Icon typ, const QString &msg ) { box( this, typ, msg ); }
+
+ protected:
+ virtual void resizeEvent( QResizeEvent *e );
+ void adjustGeometry();
+
+ private:
+ QFrame *winFrame;
+};
+
+class KFMsgBox : public FDialog {
+ typedef FDialog inherited;
+
+ public:
+ KFMsgBox( QWidget *parent, QMessageBox::Icon type, const QString &text );
+};
+
+#endif /* FDIALOG_H */
diff --git a/kdm/kfrontend/kgapp.cpp b/kdm/kfrontend/kgapp.cpp
new file mode 100644
index 000000000..711853c37
--- /dev/null
+++ b/kdm/kfrontend/kgapp.cpp
@@ -0,0 +1,253 @@
+/*
+
+Greeter module for xdm
+
+Copyright (C) 1997, 1998 Steffen Hansen <hansen@kde.org>
+Copyright (C) 2000-2003 Oswald Buddenhagen <ossi@kde.org>
+
+
+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.
+
+*/
+
+#include "kdm_greet.h"
+#include "kdmshutdown.h"
+#include "kdmconfig.h"
+#include "kgapp.h"
+#include "kgreeter.h"
+#ifdef XDMCP
+# include "kchooser.h"
+#endif
+
+#include <kprocess.h>
+#include <kcmdlineargs.h>
+#include <kcrash.h>
+#include <kstandarddirs.h>
+#include <ksimpleconfig.h>
+
+#include <qtimer.h>
+#include <qcursor.h>
+#include <qpalette.h>
+
+#include <stdlib.h> // free(), exit()
+#include <unistd.h> // alarm()
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/keysym.h>
+#include <X11/cursorfont.h>
+
+extern "C" {
+
+static void
+sigAlarm( int )
+{
+ exit( EX_RESERVER_DPY );
+}
+
+}
+
+GreeterApp::GreeterApp()
+{
+ pingInterval = _isLocal ? 0 : _pingInterval;
+ if (pingInterval) {
+ struct sigaction sa;
+ sigemptyset( &sa.sa_mask );
+ sa.sa_flags = 0;
+ sa.sa_handler = sigAlarm;
+ sigaction( SIGALRM, &sa, 0 );
+ alarm( pingInterval * 70 ); // sic! give the "proper" pinger enough time
+ startTimer( pingInterval * 60000 );
+ }
+}
+
+void
+GreeterApp::timerEvent( QTimerEvent * )
+{
+ alarm( 0 );
+ if (!PingServer( qt_xdisplay() ))
+ ::exit( EX_RESERVER_DPY );
+ alarm( pingInterval * 70 ); // sic! give the "proper" pinger enough time
+}
+
+bool
+GreeterApp::x11EventFilter( XEvent * ev )
+{
+ KeySym sym;
+
+ switch (ev->type) {
+ case FocusIn:
+ case FocusOut:
+ // Hack to tell dialogs to take focus when the keyboard is grabbed
+ ev->xfocus.mode = NotifyNormal;
+ break;
+ case KeyPress:
+ sym = XLookupKeysym( &ev->xkey, 0 );
+ if (sym != XK_Return && !IsModifierKey( sym ))
+ emit activity();
+ break;
+ case ButtonPress:
+ emit activity();
+ /* fall through */
+ case ButtonRelease:
+ // Hack to let the RMB work as LMB
+ if (ev->xbutton.button == 3)
+ ev->xbutton.button = 1;
+ /* fall through */
+ case MotionNotify:
+ if (ev->xbutton.state & Button3Mask)
+ ev->xbutton.state = (ev->xbutton.state & ~Button3Mask) | Button1Mask;
+ break;
+ }
+ return false;
+}
+
+extern bool kde_have_kipc;
+
+extern "C" {
+
+static int
+xIOErr( Display * )
+{
+ exit( EX_RESERVER_DPY );
+}
+
+void
+kg_main( const char *argv0 )
+{
+ static char *argv[] = { (char *)"kdmgreet", 0 };
+ KCmdLineArgs::init( 1, argv, *argv, 0, 0, 0, true );
+
+ kde_have_kipc = false;
+ KApplication::disableAutoDcopRegistration();
+ KCrash::setSafer( true );
+ GreeterApp app;
+ XSetIOErrorHandler( xIOErr );
+
+ Display *dpy = qt_xdisplay();
+
+ if (!_GUIStyle.isEmpty())
+ app.setStyle( _GUIStyle );
+
+ _colorScheme = locate( "data", "kdisplay/color-schemes/" + _colorScheme + ".kcsrc" );
+ if (!_colorScheme.isEmpty()) {
+ KSimpleConfig config( _colorScheme, true );
+ config.setGroup( "Color Scheme" );
+ app.setPalette( app.createApplicationPalette( &config, 7 ) );
+ }
+
+ app.setFont( _normalFont );
+
+ setup_modifiers( dpy, _numLockStatus );
+ SecureDisplay( dpy );
+ KProcess *proc = 0;
+ if (!_grabServer) {
+ if (_useBackground) {
+ proc = new KProcess;
+ *proc << QCString( argv0, strrchr( argv0, '/' ) - argv0 + 2 ) + "krootimage";
+ *proc << _backgroundCfg;
+ proc->start();
+ }
+ GSendInt( G_SetupDpy );
+ GRecvInt();
+ }
+
+ GSendInt( G_Ready );
+
+ setCursor( dpy, app.desktop()->winId(), XC_left_ptr );
+
+ for (;;) {
+ int rslt, cmd = GRecvInt();
+
+ if (cmd == G_ConfShutdown) {
+ int how = GRecvInt(), uid = GRecvInt();
+ char *os = GRecvStr();
+ KDMSlimShutdown::externShutdown( how, os, uid );
+ if (os)
+ free( os );
+ GSendInt( G_Ready );
+ _autoLoginDelay = 0;
+ continue;
+ }
+
+ if (cmd == G_ErrorGreet) {
+ if (KGVerify::handleFailVerify( qApp->desktop()->screen( _greeterScreen ) ))
+ break;
+ _autoLoginDelay = 0;
+ cmd = G_Greet;
+ }
+
+ KProcess *proc2 = 0;
+ app.setOverrideCursor( Qt::WaitCursor );
+ FDialog *dialog;
+#ifdef XDMCP
+ if (cmd == G_Choose) {
+ dialog = new ChooserDlg;
+ GSendInt( G_Ready ); /* tell chooser to go into async mode */
+ GRecvInt(); /* ack */
+ } else
+#endif
+ {
+ if ((cmd != G_GreetTimed && !_autoLoginAgain) ||
+ _autoLoginUser.isEmpty())
+ _autoLoginDelay = 0;
+ if (_useTheme && !_theme.isEmpty()) {
+ KThemedGreeter *tgrt;
+ dialog = tgrt = new KThemedGreeter;
+ if (!tgrt->isOK()) {
+ delete tgrt;
+ dialog = new KStdGreeter;
+ }
+ } else
+ dialog = new KStdGreeter;
+ if (*_preloader) {
+ proc2 = new KProcess;
+ *proc2 << _preloader;
+ proc2->start();
+ }
+ }
+ app.restoreOverrideCursor();
+ Debug( "entering event loop\n" );
+ rslt = dialog->exec();
+ Debug( "left event loop\n" );
+ delete dialog;
+ delete proc2;
+#ifdef XDMCP
+ switch (rslt) {
+ case ex_greet:
+ GSendInt( G_DGreet );
+ continue;
+ case ex_choose:
+ GSendInt( G_DChoose );
+ continue;
+ default:
+ break;
+ }
+#endif
+ break;
+ }
+
+ KGVerify::done();
+
+ delete proc;
+ UnsecureDisplay( dpy );
+ restore_modifiers();
+
+ XSetInputFocus( qt_xdisplay(), PointerRoot, PointerRoot, CurrentTime );
+}
+
+} // extern "C"
+
+#include "kgapp.moc"
diff --git a/kdm/kfrontend/kgapp.h b/kdm/kfrontend/kgapp.h
new file mode 100644
index 000000000..9174cd5c8
--- /dev/null
+++ b/kdm/kfrontend/kgapp.h
@@ -0,0 +1,49 @@
+/*
+
+Greeter module for xdm
+
+Copyright (C) 1997, 1998 Steffen Hansen <hansen@kde.org>
+Copyright (C) 2000-2003 Oswald Buddenhagen <ossi@kde.org>
+
+
+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.
+
+*/
+
+
+#ifndef KGAPP_H
+#define KGAPP_H
+
+#include <kapplication.h>
+
+class GreeterApp : public KApplication {
+ Q_OBJECT
+ typedef KApplication inherited;
+
+ public:
+ GreeterApp();
+ virtual bool x11EventFilter( XEvent * );
+
+ protected:
+ virtual void timerEvent( QTimerEvent * );
+
+ signals:
+ void activity();
+
+ private:
+ int pingInterval;
+};
+
+#endif /* KGAPP_H */
diff --git a/kdm/kfrontend/kgdialog.cpp b/kdm/kfrontend/kgdialog.cpp
new file mode 100644
index 000000000..636b20dec
--- /dev/null
+++ b/kdm/kfrontend/kgdialog.cpp
@@ -0,0 +1,238 @@
+/*
+
+Base class for various kdm greeter dialogs
+
+Copyright (C) 1997, 1998 Steffen Hansen <hansen@kde.org>
+Copyright (C) 2000-2004 Oswald Buddenhagen <ossi@kde.org>
+
+
+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.
+
+*/
+
+#include "kgdialog.h"
+#include "kgverify.h"
+#include "kconsole.h"
+#include "kdmshutdown.h"
+#include "kdm_greet.h"
+
+#include <klocale.h>
+
+#include <qaccel.h>
+#include <qlayout.h>
+#include <qpushbutton.h>
+#include <qpopupmenu.h>
+#include <qapplication.h>
+
+#include <stdlib.h>
+
+KGDialog::KGDialog( bool themed ) : inherited( 0, !themed )
+{
+#ifdef WITH_KDM_XCONSOLE
+ consoleView = _showLog ? new KConsole( this ) : 0;
+#endif
+
+ optMenu = 0;
+ verify = 0;
+}
+
+void
+#ifdef XDMCP
+KGDialog::completeMenu( int _switchIf, int _switchCode, const QString &_switchMsg, int _switchAccel )
+#else
+KGDialog::completeMenu()
+#endif
+{
+#ifdef HAVE_VTS
+ if (_isLocal) {
+ dpyMenu = new QPopupMenu( this );
+ int id = inserten( i18n("Sw&itch User"), ALT+Key_I, dpyMenu );
+ connect( dpyMenu, SIGNAL(activated( int )),
+ SLOT(slotDisplaySelected( int )) );
+ connect( dpyMenu, SIGNAL(aboutToShow()),
+ SLOT(slotPopulateDisplays()) );
+ QAccel *accel = new QAccel( this );
+ accel->insertItem( ALT+CTRL+Key_Insert, id );
+ connect( accel, SIGNAL(activated( int )), SLOT(slotActivateMenu( int )) );
+ }
+#endif
+
+ if (_allowClose)
+ inserten( _isLocal ? i18n("R&estart X Server") : i18n("Clos&e Connection"),
+ ALT+Key_E, SLOT(slotExit()) );
+
+#ifdef XDMCP
+ if (_isLocal && _loginMode != _switchIf) {
+ switchCode = _switchCode;
+ inserten( _switchMsg, _switchAccel, SLOT(slotSwitch()) );
+ }
+#endif
+
+ if (_hasConsole)
+ inserten( i18n("Co&nsole Login"), ALT+Key_N, SLOT(slotConsole()) );
+
+ if (_allowShutdown != SHUT_NONE) {
+ inserten( i18n("&Shutdown..."), ALT+Key_S, SLOT(slotShutdown( int )) );
+ QAccel *accel = new QAccel( this );
+ accel->insertItem( ALT+CTRL+Key_Delete );
+ connect( accel, SIGNAL(activated( int )), SLOT(slotShutdown( int )) );
+ accel = new QAccel( this );
+ accel->insertItem( SHIFT+ALT+CTRL+Key_PageUp, SHUT_REBOOT );
+ connect( accel, SIGNAL(activated( int )), SLOT(slotShutdown( int )) );
+ accel = new QAccel( this );
+ accel->insertItem( SHIFT+ALT+CTRL+Key_PageDown, SHUT_HALT );
+ connect( accel, SIGNAL(activated( int )), SLOT(slotShutdown( int )) );
+ }
+}
+
+void
+KGDialog::ensureMenu()
+{
+ if (!optMenu) {
+ optMenu = new QPopupMenu( this );
+ optMenu->setCheckable( false );
+ needSep = false;
+ } else if (needSep) {
+ optMenu->insertSeparator();
+ needSep = false;
+ }
+}
+
+void
+KGDialog::inserten( const QString& txt, int accel, const char *member )
+{
+ ensureMenu();
+ optMenu->insertItem( txt, this, member, accel );
+}
+
+int
+KGDialog::inserten( const QString& txt, int accel, QPopupMenu *cmnu )
+{
+ ensureMenu();
+ int id = optMenu->insertItem( txt, cmnu );
+ optMenu->setAccel( accel, id );
+ optMenu->connectItem( id, this, SLOT(slotActivateMenu( int )) );
+ optMenu->setItemParameter( id, id );
+ return id;
+}
+
+void
+KGDialog::slotActivateMenu( int id )
+{
+ QPopupMenu *cmnu = optMenu->findItem( id )->popup();
+ QSize sh( cmnu->sizeHint() / 2 );
+ cmnu->exec( geometry().center() - QPoint( sh.width(), sh.height() ) );
+}
+
+void
+KGDialog::slotExit()
+{
+ if (verify)
+ verify->abort();
+ ::exit( EX_RESERVER_DPY );
+}
+
+void
+KGDialog::slotSwitch()
+{
+#ifdef XDMCP
+ // workaround for Qt bug
+ QTimer::singleShot( 0, this, SLOT(slotReallySwitch()) );
+#endif
+}
+
+void
+KGDialog::slotReallySwitch()
+{
+#ifdef XDMCP
+ done( switchCode );
+#endif
+}
+
+void
+KGDialog::slotConsole()
+{
+#ifdef HAVE_VTS
+ dpySpec *sess = fetchSessions( 0 );
+ if (sess) {
+ if (verify)
+ verify->suspend();
+ int ret = KDMConfShutdown( -1, sess, SHUT_CONSOLE, 0 ).exec();
+ if (verify)
+ verify->resume();
+ disposeSessions( sess );
+ if (!ret)
+ return;
+ }
+#else
+ if (verify)
+ verify->abort();
+#endif
+ GSet( 1 );
+ GSendInt( G_Console );
+ GSet( 0 );
+}
+
+void
+KGDialog::slotShutdown( int id )
+{
+ if (verify)
+ verify->suspend();
+ if (id < 0) {
+ if (_scheduledSd == SHUT_ALWAYS)
+ KDMShutdown::scheduleShutdown( this );
+ else
+ KDMSlimShutdown( this ).exec();
+ } else
+ KDMSlimShutdown::externShutdown( id, 0, -1 );
+ if (verify)
+ verify->resume();
+}
+
+void
+KGDialog::slotDisplaySelected( int vt )
+{
+#ifdef HAVE_VTS
+ GSet( 1 );
+ GSendInt( G_Activate );
+ GSendInt( vt );
+ GSet( 0 );
+#else
+ (void)vt;
+#endif
+}
+
+void
+KGDialog::slotPopulateDisplays()
+{
+#ifdef HAVE_VTS
+ dpyMenu->clear();
+ dpySpec *sessions = fetchSessions( lstPassive | lstTTY );
+ QString user, loc;
+ for (dpySpec *sess = sessions; sess; sess = sess->next) {
+ decodeSess( sess, user, loc );
+ int id = dpyMenu->insertItem(
+ i18n("session (location)", "%1 (%2)").arg( user ).arg( loc ),
+ sess->vt ? sess->vt : -1 );
+ if (!sess->vt)
+ dpyMenu->setItemEnabled( id, false );
+ if (sess->flags & isSelf)
+ dpyMenu->setItemChecked( id, true );
+ }
+ disposeSessions( sessions );
+#endif
+}
+
+#include "kgdialog.moc"
diff --git a/kdm/kfrontend/kgdialog.h b/kdm/kfrontend/kgdialog.h
new file mode 100644
index 000000000..05a4be5cf
--- /dev/null
+++ b/kdm/kfrontend/kgdialog.h
@@ -0,0 +1,87 @@
+/*
+
+Base class for various kdm greeter dialogs
+
+Copyright (C) 1997, 1998 Steffen Hansen <hansen@kde.org>
+Copyright (C) 2000-2004 Oswald Buddenhagen <ossi@kde.org>
+
+
+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.
+
+*/
+
+
+#ifndef KGDIALOG_H
+#define KGDIALOG_H
+
+#include <config.h> // for WITH_KDM_XCONSOLE
+
+#include "kdmconfig.h"
+#include "kfdialog.h"
+
+class QPopupMenu;
+class QGridLayout;
+class KConsole;
+class KGVerify;
+
+#define ex_exit 1
+#define ex_greet 2
+#define ex_choose 3
+
+class KGDialog : public FDialog {
+ Q_OBJECT
+ typedef FDialog inherited;
+
+ public:
+ KGDialog( bool themed = false );
+
+ public slots:
+ void slotActivateMenu( int id );
+ void slotExit();
+ void slotSwitch();
+ void slotReallySwitch();
+ void slotConsole();
+ void slotShutdown( int id );
+
+ protected:
+#ifdef XDMCP
+ void completeMenu( int _switchIf, int _switchCode, const QString &_switchMsg, int _switchAccel );
+#else
+ void completeMenu();
+#endif
+ void inserten( const QString& txt, int accel, const char *member );
+ int inserten( const QString& txt, int accel, QPopupMenu *cmnu );
+
+ bool needSep;
+ QPopupMenu *optMenu;
+ KGVerify *verify;
+#ifdef WITH_KDM_XCONSOLE
+ KConsole *consoleView;
+#endif
+
+ private slots:
+ void slotDisplaySelected( int vt );
+ void slotPopulateDisplays();
+
+ private:
+ void ensureMenu();
+
+#ifdef HAVE_VTS
+ QPopupMenu *dpyMenu;
+#endif
+ int switchCode;
+};
+
+#endif /* KGDIALOG_H */
diff --git a/kdm/kfrontend/kgreeter.cpp b/kdm/kfrontend/kgreeter.cpp
new file mode 100644
index 000000000..574f4e340
--- /dev/null
+++ b/kdm/kfrontend/kgreeter.cpp
@@ -0,0 +1,998 @@
+/*
+
+Greeter widget for kdm
+
+Copyright (C) 1997, 1998, 2000 Steffen Hansen <hansen@kde.org>
+Copyright (C) 2000-2004 Oswald Buddenhagen <ossi@kde.org>
+
+
+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.
+
+*/
+
+#include "kgreeter.h"
+#include "kconsole.h"
+#include "kdmconfig.h"
+#include "kdmclock.h"
+#include "kdm_greet.h"
+#include "themer/kdmthemer.h"
+#include "themer/kdmitem.h"
+#include "themer/kdmlabel.h"
+
+#include <kapplication.h>
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include <kseparator.h>
+#include <klistview.h>
+#include <ksimpleconfig.h>
+#include <kstringhandler.h>
+
+#undef Unsorted // x headers suck - make qdir.h work with --enable-final
+#include <qdir.h>
+#include <qfile.h>
+#include <qbuffer.h>
+#include <qmemarray.h>
+#include <qimage.h>
+#include <qmovie.h>
+#include <qpopupmenu.h>
+#include <qtimer.h>
+#include <qheader.h>
+#include <qstyle.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qpushbutton.h>
+#include <qtooltip.h>
+#include <qaccel.h>
+#include <qeventloop.h>
+
+#include <pwd.h>
+#include <grp.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+
+#include <X11/Xlib.h>
+
+class UserListView : public KListView {
+ public:
+ UserListView( QWidget *parent = 0, const char *name = 0 )
+ : KListView( parent, name )
+ , cachedSizeHint( -1, 0 )
+ {
+ setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Ignored );
+ header()->hide();
+ addColumn( QString::null );
+ setColumnAlignment( 0, AlignVCenter );
+ setResizeMode( QListView::LastColumn );
+ }
+
+ mutable QSize cachedSizeHint;
+
+ protected:
+ virtual QSize sizeHint() const
+ {
+ if (!cachedSizeHint.isValid()) {
+ constPolish();
+ uint maxw = 0;
+ for (QListViewItem *itm = firstChild(); itm; itm = itm->nextSibling()) {
+ uint thisw = itm->width( fontMetrics(), this, 0 );
+ if (thisw > maxw)
+ maxw = thisw;
+ }
+ cachedSizeHint.setWidth(
+ style().pixelMetric( QStyle::PM_ScrollBarExtent ) +
+ frameWidth() * 2 + maxw );
+ }
+ return cachedSizeHint;
+ }
+};
+
+
+int KGreeter::curPlugin = -1;
+PluginList KGreeter::pluginList;
+
+KGreeter::KGreeter( bool framed )
+ : inherited( framed )
+ , dName( dname )
+ , userView( 0 )
+ , userList( 0 )
+ , nNormals( 0 )
+ , nSpecials( 0 )
+ , curPrev( -1 )
+ , curSel( -1 )
+ , prevValid( true )
+ , needLoad( false )
+{
+ stsFile = new KSimpleConfig( _stsFile );
+ stsFile->setGroup( "PrevUser" );
+
+ if (_userList) {
+ userView = new UserListView( this );
+ connect( userView, SIGNAL(clicked( QListViewItem * )),
+ SLOT(slotUserClicked( QListViewItem * )) );
+ connect( userView, SIGNAL(doubleClicked( QListViewItem * )),
+ SLOT(accept()) );
+ }
+ if (_userCompletion)
+ userList = new QStringList;
+ if (userView || userList)
+ insertUsers();
+
+ sessMenu = new QPopupMenu( this );
+ connect( sessMenu, SIGNAL(activated( int )),
+ SLOT(slotSessionSelected( int )) );
+ insertSessions();
+
+ if (curPlugin < 0) {
+ curPlugin = 0;
+ pluginList = KGVerify::init( _pluginsLogin );
+ }
+}
+
+KGreeter::~KGreeter()
+{
+ hide();
+ delete userList;
+ delete verify;
+ delete stsFile;
+}
+
+class UserListViewItem : public KListViewItem {
+ public:
+ UserListViewItem( UserListView *parent, const QString &text,
+ const QPixmap &pixmap, const QString &username )
+ : KListViewItem( parent )
+ , login( username )
+ {
+ setPixmap( 0, pixmap );
+ setMultiLinesEnabled( true );
+ setText( 0, text );
+ parent->cachedSizeHint.setWidth( -1 );
+ }
+
+ QString login;
+};
+
+#define FILE_LIMIT_ICON 20
+#define FILE_LIMIT_IMAGE 200
+
+void
+KGreeter::insertUser( const QImage &default_pix,
+ const QString &username, struct passwd *ps )
+{
+ if (userList)
+ userList->append( username );
+ if (!userView)
+ return;
+
+ int dp = 0, nd = 0;
+ if (_faceSource == FACE_USER_ONLY ||
+ _faceSource == FACE_PREFER_USER)
+ dp = 1;
+ if (_faceSource != FACE_USER_ONLY &&
+ _faceSource != FACE_ADMIN_ONLY)
+ nd = 1;
+ QImage p;
+ do {
+ dp ^= 1;
+ QCString fn = !dp ?
+ QCString( ps->pw_dir ) + '/' :
+ QFile::encodeName( _faceDir + '/' + username );
+ fn += ".face.icon";
+ int fd, ico;
+ if ((fd = open( fn.data(), O_RDONLY | O_NONBLOCK )) < 0) {
+ fn.truncate( fn.length() - 5 );
+ if ((fd = open( fn.data(), O_RDONLY | O_NONBLOCK )) < 0)
+ continue;
+ ico = 0;
+ } else
+ ico = 1;
+ QFile f;
+ f.open( IO_ReadOnly, fd );
+ int fs = f.size();
+ if (fs > (ico ? FILE_LIMIT_ICON : FILE_LIMIT_IMAGE) * 1000) {
+ LogWarn( "%s exceeds file size limit (%dkB)\n",
+ fn.data(), ico ? FILE_LIMIT_ICON : FILE_LIMIT_IMAGE );
+ continue;
+ }
+ QByteArray fc( fs );
+ int rfs = f.readBlock( fc.data(), fs );
+ ::close( fd );
+ fc.resize( rfs > 0 ? rfs : 0 );
+ QBuffer buf( fc );
+ buf.open( IO_ReadOnly );
+ QImageIO ir;
+ ir.setIODevice( &buf );
+ if (!ir.read()) {
+ LogInfo( "%s is no valid image\n", fn.data() );
+ continue;
+ }
+ p = ir.image();
+ QSize ns( 48, 48 );
+ if (p.size() != ns)
+ p = p.convertDepth( 32 ).smoothScale( ns, QImage::ScaleMin );
+ goto gotit;
+ } while (--nd >= 0);
+ p = default_pix;
+ gotit:
+ QString realname = KStringHandler::from8Bit( ps->pw_gecos );
+ realname.truncate( realname.find( ',' ) );
+ if (realname.isEmpty() || realname == username)
+ new UserListViewItem( userView, username, QPixmap( p ), username );
+ else {
+ realname.append( "\n" ).append( username );
+ new UserListViewItem( userView, realname, QPixmap( p ), username );
+ }
+}
+
+class KCStringList : public QValueList<QCString> {
+ public:
+ bool contains( const char *str ) const
+ {
+ for (ConstIterator it = begin(); it != end(); ++it)
+ if (*it == str)
+ return true;
+ return false;
+ }
+};
+
+class UserList {
+ public:
+ UserList( char **in );
+ bool hasUser( const char *str ) const { return users.contains( str ); }
+ bool hasGroup( gid_t gid ) const
+ { return groups.find( gid ) != groups.end(); }
+ bool hasGroups() const { return !groups.isEmpty(); }
+ KCStringList users;
+
+ private:
+ QValueList<gid_t> groups;
+};
+
+UserList::UserList( char **in )
+{
+ struct group *grp;
+
+ for (; *in; in++)
+ if (**in == '@') {
+ if ((grp = getgrnam( *in + 1 ))) {
+ for (; *grp->gr_mem; grp->gr_mem++)
+ users.append( *grp->gr_mem );
+ groups.append( grp->gr_gid );
+ }
+ } else
+ users.append( *in );
+}
+
+void
+KGreeter::insertUsers()
+{
+ struct passwd *ps;
+
+ // XXX remove seteuid-voodoo when we run as nobody
+ if (!(ps = getpwnam( "nobody" )))
+ return;
+ if (setegid( ps->pw_gid ))
+ return;
+ if (seteuid( ps->pw_uid )) {
+ setegid(0);
+ return;
+ }
+
+ QImage default_pix;
+ if (userView) {
+ if (!default_pix.load( _faceDir + "/.default.face.icon" ))
+ if (!default_pix.load( _faceDir + "/.default.face" ))
+ LogError( "Can't open default user face\n" );
+ QSize ns( 48, 48 );
+ if (default_pix.size() != ns)
+ default_pix =
+ default_pix.convertDepth( 32 ).smoothScale( ns, QImage::ScaleMin );
+ }
+ if (_showUsers == SHOW_ALL) {
+ UserList noUsers( _noUsers );
+ QDict<int> dupes( 1000 );
+ for (setpwent(); (ps = getpwent()) != 0;) {
+ if (*ps->pw_dir && *ps->pw_shell &&
+ (ps->pw_uid >= (unsigned)_lowUserId ||
+ !ps->pw_uid && _showRoot) &&
+ ps->pw_uid <= (unsigned)_highUserId &&
+ !noUsers.hasUser( ps->pw_name ) &&
+ !noUsers.hasGroup( ps->pw_gid ))
+ {
+ QString username( QFile::decodeName( ps->pw_name ) );
+ if (!dupes.find( username )) {
+ dupes.insert( username, (int *)-1 );
+ insertUser( default_pix, username, ps );
+ }
+ }
+ }
+ } else {
+ UserList users( _users );
+ if (users.hasGroups()) {
+ QDict<int> dupes( 1000 );
+ for (setpwent(); (ps = getpwent()) != 0;) {
+ if (*ps->pw_dir && *ps->pw_shell &&
+ (ps->pw_uid >= (unsigned)_lowUserId ||
+ !ps->pw_uid && _showRoot) &&
+ ps->pw_uid <= (unsigned)_highUserId &&
+ (users.hasUser( ps->pw_name ) ||
+ users.hasGroup( ps->pw_gid )))
+ {
+ QString username( QFile::decodeName( ps->pw_name ) );
+ if (!dupes.find( username )) {
+ dupes.insert( username, (int *)-1 );
+ insertUser( default_pix, username, ps );
+ }
+ }
+ }
+ } else {
+ KCStringList::ConstIterator it = users.users.begin();
+ for (; it != users.users.end(); ++it)
+ if ((ps = getpwnam( (*it).data() )) &&
+ (ps->pw_uid || _showRoot))
+ insertUser( default_pix, QFile::decodeName( *it ), ps );
+ }
+ }
+ endpwent();
+ if (_sortUsers) {
+ if (userView)
+ userView->sort();
+ if (userList)
+ userList->sort();
+ }
+
+ // XXX remove seteuid-voodoo when we run as nobody
+ seteuid( 0 );
+ setegid( 0 );
+}
+
+void
+KGreeter::putSession( const QString &type, const QString &name, bool hid, const char *exe )
+{
+ int prio = exe ? (!strcmp( exe, "default" ) ? 0 :
+ !strcmp( exe, "custom" ) ? 1 :
+ !strcmp( exe, "failsafe" ) ? 3 : 2) : 2;
+ for (uint i = 0; i < sessionTypes.size(); i++)
+ if (sessionTypes[i].type == type) {
+ sessionTypes[i].prio = prio;
+ return;
+ }
+ sessionTypes.append( SessType( name, type, hid, prio ) );
+}
+
+void
+KGreeter::insertSessions()
+{
+ for (char **dit = _sessionsDirs; *dit; ++dit) {
+ QStringList ents = QDir( *dit ).entryList();
+ for (QStringList::ConstIterator it = ents.begin(); it != ents.end(); ++it)
+ if ((*it).endsWith( ".desktop" )) {
+ KSimpleConfig dsk( QString( *dit ).append( '/' ).append( *it ) );
+ dsk.setGroup( "Desktop Entry" );
+ putSession( (*it).left( (*it).length() - 8 ),
+ dsk.readEntry( "Name" ),
+ (dsk.readBoolEntry( "Hidden", false ) ||
+ (dsk.hasKey( "TryExec" ) &&
+ KStandardDirs::findExe( dsk.readEntry( "TryExec" ) ).isEmpty())),
+ dsk.readEntry( "Exec" ).latin1() );
+ }
+ }
+ putSession( "default", i18n("Default"), false, "default" );
+ putSession( "custom", i18n("Custom"), false, "custom" );
+ putSession( "failsafe", i18n("Failsafe"), false, "failsafe" );
+ qBubbleSort( sessionTypes );
+ for (uint i = 0; i < sessionTypes.size() && !sessionTypes[i].hid; i++) {
+ sessMenu->insertItem( sessionTypes[i].name, i );
+ switch (sessionTypes[i].prio) {
+ case 0: case 1: nSpecials++; break;
+ case 2: nNormals++; break;
+ }
+ }
+}
+
+void
+KGreeter::slotUserEntered()
+{
+ if (userView) {
+ QListViewItem *item;
+ for (item = userView->firstChild(); item; item = item->nextSibling())
+ if (((UserListViewItem *)item)->login == curUser) {
+ userView->setSelected( item, true );
+ userView->ensureItemVisible( item );
+ goto oke;
+ }
+ userView->clearSelection();
+ }
+ oke:
+ if (isVisible())
+ slotLoadPrevWM();
+ else
+ QTimer::singleShot( 0, this, SLOT(slotLoadPrevWM()) );
+}
+
+void
+KGreeter::slotUserClicked( QListViewItem *item )
+{
+ if (item) {
+ curUser = ((UserListViewItem *)item)->login;
+ verify->setUser( curUser );
+ slotLoadPrevWM();
+ }
+}
+
+void
+KGreeter::slotSessionSelected( int id )
+{
+ if (id != curSel) {
+ sessMenu->setItemChecked( curSel, false );
+ sessMenu->setItemChecked( id, true );
+ curSel = id;
+ verify->gplugActivity();
+ }
+}
+
+void
+KGreeter::reject()
+{
+ verify->reject();
+}
+
+void
+KGreeter::accept()
+{
+ if (userView && userView->hasFocus())
+ slotUserClicked( userView->currentItem() );
+ else
+ verify->accept();
+}
+
+void // private
+KGreeter::setPrevWM( int wm )
+{
+ if (curPrev != wm) {
+ if (curPrev != -1)
+ sessMenu->changeItem( curPrev, sessionTypes[curPrev].name );
+ if (wm != -1)
+ sessMenu->changeItem( wm, sessionTypes[wm].name + i18n(" (previous)") );
+ curPrev = wm;
+ }
+}
+
+void
+KGreeter::slotLoadPrevWM()
+{
+ int len, i, b;
+ unsigned long crc, by;
+ QCString name;
+ char *sess;
+
+ if (verify->coreLock) {
+ needLoad = true;
+ return;
+ }
+ needLoad = false;
+
+ prevValid = true;
+ name = curUser.local8Bit();
+ GSendInt( G_ReadDmrc );
+ GSendStr( name.data() );
+ GRecvInt(); // ignore status code ...
+ if ((len = name.length())) {
+ GSendInt( G_GetDmrc );
+ GSendStr( "Session" );
+ sess = GRecvStr();
+ if (!sess) { /* no such user */
+ if (!userView && !userList) { // don't fake if user list shown
+ prevValid = false;
+ /* simple crc32 */
+ for (crc = _forgingSeed, i = 0; i < len; i++) {
+ by = (crc & 255) ^ name[i];
+ for (b = 0; b < 8; b++)
+ by = (by >> 1) ^ (-(by & 1) & 0xedb88320);
+ crc = (crc >> 8) ^ by;
+ }
+ /* forge a session with this hash - default & custom more probable */
+ /* XXX - this should do a statistical analysis of the real users */
+#if 1
+ setPrevWM( crc % (nSpecials * 2 + nNormals) % (nSpecials + nNormals) );
+#else
+ i = crc % (nSpecials * 2 + nNormals);
+ if (i < nNormals)
+ setPrevWM( i + nSpecials );
+ else
+ setPrevWM( (i - nNormals) / 2 );
+#endif
+ return;
+ }
+ } else {
+ for (uint i = 0; i < sessionTypes.count() && !sessionTypes[i].hid; i++)
+ if (sessionTypes[i].type == sess) {
+ free( sess );
+ setPrevWM( i );
+ return;
+ }
+ if (curSel == -1)
+ MsgBox( sorrybox, i18n("Your saved session type '%1' is not valid any more.\n"
+ "Please select a new one, otherwise 'default' will be used.").arg( sess ) );
+ free( sess );
+ prevValid = false;
+ }
+ }
+ setPrevWM( -1 );
+}
+
+void // protected
+KGreeter::pluginSetup()
+{
+ int field = 0;
+ QString ent, pn( verify->pluginName() ), dn( dName + '_' + pn );
+
+ if (_preselUser != PRESEL_PREV)
+ stsFile->deleteEntry( verify->entitiesLocal() ? dName : dn, false );
+ if (_preselUser != PRESEL_NONE && verify->entityPresettable()) {
+ if (verify->entitiesLocal())
+ ent = _preselUser == PRESEL_PREV ?
+ stsFile->readEntry( dName ) : _defaultUser;
+ else
+ ent = _preselUser == PRESEL_PREV ?
+ stsFile->readEntry( dn ) :
+ verify->getConf( 0, (pn + ".DefaultEntity").latin1(), QVariant() ).toString();
+ field = verify->entitiesFielded() ?
+ verify->getConf( 0, (pn + ".FocusField").latin1(), QVariant( 0 ) ).toInt() :
+ _focusPasswd;
+ }
+ verify->presetEntity( ent, field );
+ if (userList)
+ verify->loadUsers( *userList );
+}
+
+void
+KGreeter::verifyPluginChanged( int id )
+{
+ curPlugin = id;
+ pluginSetup();
+}
+
+void
+KGreeter::verifyClear()
+{
+ curUser = QString::null;
+ slotUserEntered();
+ slotSessionSelected( -1 );
+}
+
+void
+KGreeter::verifyOk()
+{
+ if (_preselUser == PRESEL_PREV && verify->entityPresettable())
+ stsFile->writeEntry( verify->entitiesLocal() ?
+ dName :
+ dName + '_' + verify->pluginName(),
+ verify->getEntity() );
+ if (curSel != -1) {
+ GSendInt( G_PutDmrc );
+ GSendStr( "Session" );
+ GSendStr( sessionTypes[curSel].type.utf8() );
+ } else if (!prevValid) {
+ GSendInt( G_PutDmrc );
+ GSendStr( "Session" );
+ GSendStr( "default" );
+ }
+ GSendInt( G_Ready );
+ done( ex_exit );
+}
+
+void
+KGreeter::verifyFailed()
+{
+ if (needLoad)
+ slotLoadPrevWM();
+}
+
+void
+KGreeter::verifySetUser( const QString &user )
+{
+ curUser = user;
+ slotUserEntered();
+}
+
+
+KStdGreeter::KStdGreeter()
+ : KGreeter()
+ , clock( 0 )
+ , pixLabel( 0 )
+{
+ QBoxLayout *main_box;
+#ifdef WITH_KDM_XCONSOLE
+ if (consoleView) {
+ QBoxLayout *ex_box = new QVBoxLayout( this, 10, 10 );
+ main_box = new QHBoxLayout( ex_box, 10 );
+ ex_box->addWidget( consoleView );
+ } else
+#endif
+ main_box = new QHBoxLayout( this, 10, 10 );
+
+ if (userView)
+ main_box->addWidget( userView );
+
+ QBoxLayout *inner_box = new QVBoxLayout( main_box, 10 );
+
+ if (!_authorized && _authComplain) {
+ QLabel *complainLabel = new QLabel(
+ i18n("Warning: this is an unsecured session"), this );
+ QToolTip::add( complainLabel,
+ i18n("This display requires no X authorization.\n"
+ "This means that anybody can connect to it,\n"
+ "open windows on it or intercept your input.") );
+ complainLabel->setAlignment( AlignCenter );
+ complainLabel->setFont( _failFont );
+ complainLabel->setPaletteForegroundColor( Qt::red );
+ inner_box->addWidget( complainLabel );
+ }
+ if (!_greetString.isEmpty()) {
+ QLabel *welcomeLabel = new QLabel( _greetString, this );
+ welcomeLabel->setAlignment( AlignCenter );
+ welcomeLabel->setFont( _greetFont );
+ inner_box->addWidget( welcomeLabel );
+ }
+
+ switch (_logoArea) {
+ case LOGO_CLOCK:
+ clock = new KdmClock( this, "clock" );
+ break;
+ case LOGO_LOGO:
+ {
+ QMovie movie( _logo );
+ kapp->eventLoop()->processEvents( QEventLoop::ExcludeUserInput | QEventLoop::ExcludeSocketNotifiers, 100 );
+ QPixmap pixmap;
+ if (!movie.framePixmap().isNull() || pixmap.load( _logo )) {
+ pixLabel = new QLabel( this );
+ if (!movie.framePixmap().isNull()) {
+ pixLabel->setMovie( movie );
+ if (!movie.framePixmap().hasAlpha())
+ pixLabel->setFrameStyle( QFrame::Panel | QFrame::Sunken );
+ } else {
+ pixLabel->setPixmap( pixmap );
+ if (!pixmap.hasAlpha())
+ pixLabel->setFrameStyle( QFrame::Panel | QFrame::Sunken );
+ }
+ pixLabel->setIndent( 0 );
+ }
+ }
+ break;
+ }
+
+ if (userView) {
+ if (clock)
+ inner_box->addWidget( clock, 0, AlignCenter );
+ else if (pixLabel)
+ inner_box->addWidget( pixLabel, 0, AlignCenter );
+ } else {
+ if (clock)
+ main_box->addWidget( clock, 0, AlignCenter );
+ else if (pixLabel)
+ main_box->addWidget( pixLabel, 0, AlignCenter );
+ }
+
+ goButton = new QPushButton( i18n("L&ogin"), this );
+ goButton->setDefault( true );
+ connect( goButton, SIGNAL(clicked()), SLOT(accept()) );
+ menuButton = new QPushButton( i18n("&Menu"), this );
+ //helpButton
+
+ QWidget *prec;
+ if (userView)
+ prec = userView;
+#ifdef WITH_KDM_XCONSOLE
+ else if (consoleView)
+ prec = consoleView;
+#endif
+ else
+ prec = menuButton;
+ KGStdVerify *sverify =
+ new KGStdVerify( this, this, prec, QString::null,
+ pluginList, KGreeterPlugin::Authenticate,
+ KGreeterPlugin::Login );
+ inner_box->addLayout( sverify->getLayout() );
+ QPopupMenu *plugMenu = sverify->getPlugMenu();
+ sverify->selectPlugin( curPlugin );
+ verify = sverify;
+
+ inner_box->addWidget( new KSeparator( KSeparator::HLine, this ) );
+
+ QBoxLayout *hbox2 = new QHBoxLayout( inner_box, 10 );
+ hbox2->addWidget( goButton );
+ hbox2->addStretch( 1 );
+ hbox2->addWidget( menuButton );
+ hbox2->addStretch( 1 );
+
+ if (sessMenu->count() > 1) {
+ inserten( i18n("Session &Type"), ALT+Key_T, sessMenu );
+ needSep = true;
+ }
+
+ if (plugMenu) {
+ inserten( i18n("&Authentication Method"), ALT+Key_A, plugMenu );
+ needSep = true;
+ }
+
+#ifdef XDMCP
+ completeMenu( LOGIN_LOCAL_ONLY, ex_choose, i18n("&Remote Login"), ALT+Key_R );
+#else
+ completeMenu();
+#endif
+
+ if (optMenu)
+ menuButton->setPopup( optMenu );
+ else
+ menuButton->hide();
+
+ pluginSetup();
+
+ verify->start();
+}
+
+void
+KStdGreeter::pluginSetup()
+{
+ inherited::pluginSetup();
+ if (userView) {
+ if (verify->entitiesLocal() && verify->entityPresettable())
+ userView->show();
+ else
+ userView->hide();
+ }
+ adjustGeometry();
+ update();
+}
+
+void
+KStdGreeter::verifyFailed()
+{
+ goButton->setEnabled( false );
+ menuButton->setEnabled( false );
+ if (userView)
+ userView->setEnabled( false );
+ inherited::verifyFailed();
+}
+
+void
+KStdGreeter::verifyRetry()
+{
+ goButton->setEnabled( true );
+ menuButton->setEnabled( true );
+ if (userView)
+ userView->setEnabled( true );
+}
+
+
+KThemedGreeter::KThemedGreeter()
+ : KGreeter( true )
+ , themer( 0 )
+// , clock( 0 )
+{
+ // We do all painting ourselves
+ setBackgroundMode( NoBackground );
+ // Allow tracking the mouse position
+ setMouseTracking( true );
+
+ adjustGeometry();
+
+ themer = new KdmThemer( _theme, "console", this );
+ if (!themer->isOK()) {
+ delete themer;
+ themer = 0;
+ return;
+ }
+
+ connect( themer, SIGNAL(activated( const QString & )),
+ SLOT(slotThemeActivated( const QString & )) );
+
+ console_rect = themer->findNode( "xconsole" ); // kdm ext
+ userlist_rect = themer->findNode( "userlist" );
+ caps_warning = themer->findNode( "caps-lock-warning" );
+ xauth_warning = themer->findNode( "xauth-warning" ); // kdm ext
+ pam_error = themer->findNode( "pam-error" );
+ timed_label = themer->findNode( "timed-label" );
+ if (pam_error && pam_error->isA( "KdmLabel" ))
+ static_cast<KdmLabel*>(pam_error)->setText( i18n("Login Failed.") );
+
+ KdmItem *itm;
+ if ((itm = themer->findNode( "pam-message" ))) // done via msgboxes
+ itm->hide( true );
+ if ((itm = themer->findNode( "language_button" ))) // not implemented yet
+ itm->hide( true );
+
+#ifdef WITH_KDM_XCONSOLE
+ if (console_rect) {
+ if (consoleView)
+ console_rect->setWidget( consoleView );
+ else
+ console_rect->hide( true );
+ }
+#endif
+
+ if (xauth_warning && (_authorized || !_authComplain))
+ xauth_warning->hide( true );
+
+// if (!_greetString.isEmpty()) {
+// }
+// clock = new KdmClock( this, "clock" );
+
+ QWidget *prec;
+ if (userView)
+ prec = userView;
+#ifdef WITH_KDM_XCONSOLE
+ else if (consoleView)
+ prec = consoleView;
+#endif
+ else
+ prec = 0;
+ KGThemedVerify *tverify =
+ new KGThemedVerify( this, themer, this, prec, QString::null,
+ pluginList, KGreeterPlugin::Authenticate,
+ KGreeterPlugin::Login );
+ QPopupMenu *plugMenu = tverify->getPlugMenu();
+ tverify->selectPlugin( curPlugin );
+ verify = tverify;
+
+ session_button = 0;
+ if ((itm = themer->findNode( "session_button" ))) {
+ if (sessMenu->count() <= 1)
+ itm->hide( true );
+ else {
+ session_button = itm;
+ QAccel *accel = new QAccel( this );
+ accel->insertItem( ALT+Key_T, 0 );
+ connect( accel, SIGNAL(activated( int )), SLOT(slotSessMenu()) );
+ }
+ } else {
+ if (sessMenu->count() > 1) {
+ inserten( i18n("Session &Type"), ALT+Key_T, sessMenu );
+ needSep = true;
+ }
+ }
+
+ if (plugMenu) {
+ inserten( i18n("&Authentication Method"), ALT+Key_A, plugMenu );
+ needSep = true;
+ }
+
+#ifdef XDMCP
+ completeMenu( LOGIN_LOCAL_ONLY, ex_choose, i18n("&Remote Login"), ALT+Key_R );
+#else
+ completeMenu();
+#endif
+
+ system_button = themer->findNode( "system_button" );
+ QAccel *accel = new QAccel( this );
+ accel->insertItem( ALT+Key_M, 0 );
+ connect( accel, SIGNAL(activated( int )), SLOT(slotActionMenu()) );
+
+ pluginSetup();
+
+ verify->start();
+}
+
+bool
+KThemedGreeter::event( QEvent *e )
+{
+ if (themer)
+ themer->widgetEvent( e );
+ return inherited::event( e );
+}
+
+void
+KThemedGreeter::pluginSetup()
+{
+ inherited::pluginSetup();
+
+ if (userView && verify->entitiesLocal() && verify->entityPresettable() && userlist_rect) {
+ userlist_rect->setWidget( userView );
+ userView->show();
+ } else {
+ if (userView)
+ userView->hide();
+ if (userlist_rect)
+ userlist_rect->hide( true );
+ }
+
+ update();
+}
+
+void
+KThemedGreeter::verifyFailed()
+{
+// goButton->setEnabled( false );
+ inherited::verifyFailed();
+}
+
+void
+KThemedGreeter::verifyRetry()
+{
+// goButton->setEnabled( true );
+}
+
+QString KThemedGreeter::timedUser = QString::null;
+int KThemedGreeter::timedDelay = -1;
+
+void
+KThemedGreeter::updateStatus( bool fail, bool caps, int timedleft )
+{
+ if (pam_error) {
+ if (fail)
+ pam_error->show( true );
+ else
+ pam_error->hide( true );
+ }
+ if (caps_warning) {
+ if (caps)
+ caps_warning->show( true );
+ else
+ caps_warning->hide( true );
+ }
+ if (timed_label) {
+ if (timedleft) {
+ if (timedleft != timedDelay) {
+ timedDelay = timedleft;
+ timedUser = curUser;
+ timed_label->show( true );
+ timed_label->update();
+ }
+ } else {
+ timedDelay = -1;
+ timed_label->hide( true );
+ }
+ }
+}
+
+void
+KThemedGreeter::slotThemeActivated( const QString &id )
+{
+ if (id == "login_button")
+ accept();
+ else if (id == "session_button")
+ slotSessMenu();
+ else if (id == "system_button")
+ slotActionMenu();
+}
+
+void
+KThemedGreeter::slotSessMenu()
+{
+ sessMenu->popup( mapToGlobal( session_button->rect().center() ) );
+}
+
+void
+KThemedGreeter::slotActionMenu()
+{
+ if (system_button)
+ optMenu->popup( mapToGlobal( system_button->rect().center() ) );
+ else
+ optMenu->popup( mapToGlobal( rect().center() ) );
+}
+
+void
+KThemedGreeter::keyPressEvent( QKeyEvent *e )
+{
+ inherited::keyPressEvent( e );
+ if (!(e->state() & KeyButtonMask) &&
+ (e->key() == Key_Return || e->key() == Key_Enter))
+ accept();
+}
+
+#include "kgreeter.moc"
diff --git a/kdm/kfrontend/kgreeter.h b/kdm/kfrontend/kgreeter.h
new file mode 100644
index 000000000..32191fafe
--- /dev/null
+++ b/kdm/kfrontend/kgreeter.h
@@ -0,0 +1,164 @@
+/*
+
+Greeter widget for kdm
+
+Copyright (C) 1997, 1998 Steffen Hansen <hansen@kde.org>
+Copyright (C) 2000-2004 Oswald Buddenhagen <ossi@kde.org>
+
+
+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.
+
+*/
+
+
+#ifndef KGREETER_H
+#define KGREETER_H
+
+#include "kgverify.h"
+#include "kgdialog.h"
+
+class KdmClock;
+class UserListView;
+class KdmThemer;
+class KdmItem;
+
+class KListView;
+class KSimpleConfig;
+
+class QLabel;
+class QPushButton;
+class QPopupMenu;
+class QListViewItem;
+
+struct SessType {
+ QString name, type;
+ bool hid;
+ int prio;
+
+ SessType() {}
+ SessType( const QString &n, const QString &t, bool h, int p ) :
+ name( n ), type( t ), hid( h ), prio( p ) {}
+ bool operator<( const SessType &st ) {
+ return hid != st.hid ? hid < st.hid :
+ prio != st.prio ? prio < st.prio :
+ name < st.name;
+ }
+};
+
+class KGreeter : public KGDialog, public KGVerifyHandler {
+ Q_OBJECT
+ typedef KGDialog inherited;
+
+ public:
+ KGreeter( bool themed = false );
+ ~KGreeter();
+
+ public slots:
+ void accept();
+ void reject();
+ void slotUserClicked( QListViewItem * );
+ void slotSessionSelected( int );
+ void slotUserEntered();
+
+ protected:
+ void installUserList();
+ void insertUser( const QImage &, const QString &, struct passwd * );
+ void insertUsers();
+ void putSession( const QString &, const QString &, bool, const char * );
+ void insertSessions();
+ virtual void pluginSetup();
+ void setPrevWM( int );
+
+ QString curUser, dName;
+ KSimpleConfig *stsFile;
+ UserListView *userView;
+ QStringList *userList;
+ QPopupMenu *sessMenu;
+ QValueVector<SessType> sessionTypes;
+ int nNormals, nSpecials;
+ int curPrev, curSel;
+ bool prevValid;
+ bool needLoad;
+
+ static int curPlugin;
+ static PluginList pluginList;
+
+ private slots:
+ void slotLoadPrevWM();
+
+ public: // from KGVerifyHandler
+ virtual void verifyPluginChanged( int id );
+ virtual void verifyClear();
+ virtual void verifyOk();
+ virtual void verifyFailed();
+// virtual void verifyRetry();
+ virtual void verifySetUser( const QString &user );
+};
+
+class KStdGreeter : public KGreeter {
+ Q_OBJECT
+ typedef KGreeter inherited;
+
+ public:
+ KStdGreeter();
+
+ protected:
+ virtual void pluginSetup();
+
+ private:
+ KdmClock *clock;
+ QLabel *pixLabel;
+ QPushButton *goButton;
+ QPushButton *menuButton;
+
+ public: // from KGVerifyHandler
+ virtual void verifyFailed();
+ virtual void verifyRetry();
+};
+
+class KThemedGreeter : public KGreeter {
+ Q_OBJECT
+ typedef KGreeter inherited;
+
+ public:
+ KThemedGreeter();
+ bool isOK() { return themer != 0; }
+ static QString timedUser;
+ static int timedDelay;
+
+ public slots:
+ void slotThemeActivated( const QString &id );
+ void slotSessMenu();
+ void slotActionMenu();
+
+ protected:
+ virtual void updateStatus( bool fail, bool caps, int timedleft );
+ virtual void pluginSetup();
+ virtual void keyPressEvent( QKeyEvent * );
+ virtual bool event( QEvent *e );
+
+ private:
+// KdmClock *clock;
+ KdmThemer *themer;
+ KdmItem *caps_warning, *xauth_warning, *pam_error, *timed_label,
+ *console_rect, *userlist_rect,
+ *session_button, *system_button;
+
+ public: // from KGVerifyHandler
+ virtual void verifyFailed();
+ virtual void verifyRetry();
+};
+
+#endif /* KGREETER_H */
diff --git a/kdm/kfrontend/kgverify.cpp b/kdm/kfrontend/kgverify.cpp
new file mode 100644
index 000000000..f9bb77ff8
--- /dev/null
+++ b/kdm/kfrontend/kgverify.cpp
@@ -0,0 +1,1211 @@
+/*
+
+Shell for kdm conversation plugins
+
+Copyright (C) 1997, 1998, 2000 Steffen Hansen <hansen@kde.org>
+Copyright (C) 2000-2004 Oswald Buddenhagen <ossi@kde.org>
+
+
+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.
+
+*/
+
+#include <config.h>
+
+#include "kgverify.h"
+#include "kdmconfig.h"
+#include "kdm_greet.h"
+
+#include "themer/kdmthemer.h"
+#include "themer/kdmitem.h"
+
+#include <kapplication.h>
+#include <klocale.h>
+#include <klibloader.h>
+#include <kseparator.h>
+#include <kstdguiitem.h>
+#include <kpushbutton.h>
+
+#include <qregexp.h>
+#include <qpopupmenu.h>
+#include <qlayout.h>
+#include <qfile.h>
+#include <qlabel.h>
+
+#include <pwd.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <X11/Xlib.h> // for updateLockStatus()
+#include <fixx11h.h> // ... and make eventFilter() work again
+
+#define FULL_GREET_TO 40 // normal inactivity timeout
+#define TIMED_GREET_TO 20 // inactivity timeout when persisting timed login
+#define MIN_TIMED_TO 5 // minimal timed login delay
+#define DEAD_TIMED_TO 2 // <enter> dead time after re-activating timed login
+#define SECONDS 1000 // reduce to 100 to speed up testing
+
+void KGVerifyHandler::verifyClear()
+{
+}
+
+void KGVerifyHandler::updateStatus( bool, bool, int )
+{
+}
+
+KGVerify::KGVerify( KGVerifyHandler *_handler, KdmThemer *_themer,
+ QWidget *_parent, QWidget *_predecessor,
+ const QString &_fixedUser,
+ const PluginList &_pluginList,
+ KGreeterPlugin::Function _func,
+ KGreeterPlugin::Context _ctx )
+ : inherited()
+ , coreLock( 0 )
+ , fixedEntity( _fixedUser )
+ , pluginList( _pluginList )
+ , handler( _handler )
+ , themer( _themer )
+ , parent( _parent )
+ , predecessor( _predecessor )
+ , plugMenu( 0 )
+ , curPlugin( -1 )
+ , timedLeft( 0 )
+ , func( _func )
+ , ctx( _ctx )
+ , enabled( true )
+ , running( false )
+ , suspended( false )
+ , failed( false )
+ , isClear( true )
+{
+ connect( &timer, SIGNAL(timeout()), SLOT(slotTimeout()) );
+ connect( kapp, SIGNAL(activity()), SLOT(slotActivity()) );
+
+ _parent->installEventFilter( this );
+}
+
+KGVerify::~KGVerify()
+{
+ Debug( "delete %s\n", pName.data() );
+ delete greet;
+}
+
+QPopupMenu *
+KGVerify::getPlugMenu()
+{
+ // assert( !cont );
+ if (!plugMenu) {
+ uint np = pluginList.count();
+ if (np > 1) {
+ plugMenu = new QPopupMenu( parent );
+ connect( plugMenu, SIGNAL(activated( int )),
+ SLOT(slotPluginSelected( int )) );
+ for (uint i = 0; i < np; i++)
+ plugMenu->insertItem( i18n(greetPlugins[pluginList[i]].info->name), pluginList[i] );
+ }
+ }
+ return plugMenu;
+}
+
+bool // public
+KGVerify::entitiesLocal() const
+{
+ return greetPlugins[pluginList[curPlugin]].info->flags & kgreeterplugin_info::Local;
+}
+
+bool // public
+KGVerify::entitiesFielded() const
+{
+ return greetPlugins[pluginList[curPlugin]].info->flags & kgreeterplugin_info::Fielded;
+}
+
+bool // public
+KGVerify::entityPresettable() const
+{
+ return greetPlugins[pluginList[curPlugin]].info->flags & kgreeterplugin_info::Presettable;
+}
+
+bool // public
+KGVerify::isClassic() const
+{
+ return !strcmp( greetPlugins[pluginList[curPlugin]].info->method, "classic" );
+}
+
+QString // public
+KGVerify::pluginName() const
+{
+ QString name( greetPlugins[pluginList[curPlugin]].library->fileName() );
+ uint st = name.findRev( '/' ) + 1;
+ uint en = name.find( '.', st );
+ if (en - st > 7 && QConstString( name.unicode() + st, 7 ).string() == "kgreet_")
+ st += 7;
+ return name.mid( st, en - st );
+}
+
+static void
+showWidgets( QLayoutItem *li )
+{
+ QWidget *w;
+ QLayout *l;
+
+ if ((w = li->widget()))
+ w->show();
+ else if ((l = li->layout())) {
+ QLayoutIterator it = l->iterator();
+ for (QLayoutItem *itm = it.current(); itm; itm = ++it)
+ showWidgets( itm );
+ }
+}
+
+void // public
+KGVerify::selectPlugin( int id )
+{
+ if (pluginList.isEmpty()) {
+ MsgBox( errorbox, i18n("No greeter widget plugin loaded. Check the configuration.") );
+ ::exit( EX_UNMANAGE_DPY );
+ }
+ curPlugin = id;
+ if (plugMenu)
+ plugMenu->setItemChecked( id, true );
+ pName = ("greet_" + pluginName()).latin1();
+ Debug( "new %s\n", pName.data() );
+ greet = greetPlugins[pluginList[id]].info->create( this, themer, parent, predecessor, fixedEntity, func, ctx );
+ timeable = _autoLoginDelay && entityPresettable() && isClassic();
+}
+
+void // public
+KGVerify::loadUsers( const QStringList &users )
+{
+ Debug( "%s->loadUsers(...)\n", pName.data() );
+ greet->loadUsers( users );
+}
+
+void // public
+KGVerify::presetEntity( const QString &entity, int field )
+{
+ presEnt = entity;
+ presFld = field;
+}
+
+bool // private
+KGVerify::applyPreset()
+{
+ if (!presEnt.isEmpty()) {
+ Debug( "%s->presetEntity(%\"s, %d)\n", pName.data(),
+ presEnt.latin1(), presFld );
+ greet->presetEntity( presEnt, presFld );
+ if (entitiesLocal()) {
+ curUser = presEnt;
+ handler->verifySetUser( presEnt );
+ }
+ return true;
+ }
+ return false;
+}
+
+bool // private
+KGVerify::scheduleAutoLogin( bool initial )
+{
+ if (timeable) {
+ Debug( "%s->presetEntity(%\"s, -1)\n", pName.data(),
+ _autoLoginUser.latin1(), -1 );
+ greet->presetEntity( _autoLoginUser, -1 );
+ curUser = _autoLoginUser;
+ handler->verifySetUser( _autoLoginUser );
+ timer.start( 1000 );
+ if (initial) {
+ timedLeft = _autoLoginDelay;
+ deadTicks = 0;
+ } else {
+ timedLeft = QMAX( _autoLoginDelay - TIMED_GREET_TO, MIN_TIMED_TO );
+ deadTicks = DEAD_TIMED_TO;
+ }
+ updateStatus();
+ running = false;
+ isClear = true;
+ return true;
+ }
+ return false;
+}
+
+void // private
+KGVerify::performAutoLogin()
+{
+// timer.stop();
+ GSendInt( G_AutoLogin );
+ handleVerify();
+}
+
+QString // public
+KGVerify::getEntity() const
+{
+ Debug( "%s->getEntity()\n", pName.data() );
+ QString ent = greet->getEntity();
+ Debug( " entity: %s\n", ent.latin1() );
+ return ent;
+}
+
+void
+KGVerify::setUser( const QString &user )
+{
+ // assert( fixedEntity.isEmpty() );
+ curUser = user;
+ Debug( "%s->setUser(%\"s)\n", pName.data(), user.latin1() );
+ greet->setUser( user );
+ gplugActivity();
+}
+
+void
+KGVerify::start()
+{
+ authTok = (func == KGreeterPlugin::ChAuthTok);
+ cont = false;
+ if (func == KGreeterPlugin::Authenticate && ctx == KGreeterPlugin::Login) {
+ if (scheduleAutoLogin( true )) {
+ if (!_autoLoginAgain)
+ _autoLoginDelay = 0, timeable = false;
+ return;
+ } else
+ applyPreset();
+ }
+ running = true;
+ Debug( "%s->start()\n", pName.data() );
+ greet->start();
+ if (!(func == KGreeterPlugin::Authenticate ||
+ ctx == KGreeterPlugin::ChangeTok ||
+ ctx == KGreeterPlugin::ExChangeTok))
+ {
+ cont = true;
+ handleVerify();
+ }
+}
+
+void
+KGVerify::abort()
+{
+ Debug( "%s->abort()\n", pName.data() );
+ greet->abort();
+ running = false;
+}
+
+void
+KGVerify::suspend()
+{
+ // assert( !cont );
+ if (running) {
+ Debug( "%s->abort()\n", pName.data() );
+ greet->abort();
+ }
+ suspended = true;
+ updateStatus();
+ timer.suspend();
+}
+
+void
+KGVerify::resume()
+{
+ timer.resume();
+ suspended = false;
+ updateLockStatus();
+ if (running) {
+ Debug( "%s->start()\n", pName.data() );
+ greet->start();
+ } else if (delayed) {
+ delayed = false;
+ running = true;
+ Debug( "%s->start()\n", pName.data() );
+ greet->start();
+ }
+}
+
+void // not a slot - called manually by greeter
+KGVerify::accept()
+{
+ Debug( "%s->next()\n", pName.data() );
+ greet->next();
+}
+
+void // private
+KGVerify::doReject( bool initial )
+{
+ // assert( !cont );
+ if (running) {
+ Debug( "%s->abort()\n", pName.data() );
+ greet->abort();
+ }
+ handler->verifyClear();
+ Debug( "%s->clear()\n", pName.data() );
+ greet->clear();
+ curUser = QString::null;
+ if (!scheduleAutoLogin( initial )) {
+ isClear = !(isClear && applyPreset());
+ if (running) {
+ Debug( "%s->start()\n", pName.data() );
+ greet->start();
+ }
+ if (!failed)
+ timer.stop();
+ }
+}
+
+void // not a slot - called manually by greeter
+KGVerify::reject()
+{
+ doReject( true );
+}
+
+void
+KGVerify::setEnabled( bool on )
+{
+ Debug( "%s->setEnabled(%s)\n", pName.data(), on ? "true" : "false" );
+ greet->setEnabled( on );
+ enabled = on;
+ updateStatus();
+}
+
+void // private
+KGVerify::slotTimeout()
+{
+ if (failed) {
+ failed = false;
+ updateStatus();
+ Debug( "%s->revive()\n", pName.data() );
+ greet->revive();
+ handler->verifyRetry();
+ if (suspended)
+ delayed = true;
+ else {
+ running = true;
+ Debug( "%s->start()\n", pName.data() );
+ greet->start();
+ slotActivity();
+ gplugActivity();
+ if (cont)
+ handleVerify();
+ }
+ } else if (timedLeft) {
+ deadTicks--;
+ if (!--timedLeft)
+ performAutoLogin();
+ else
+ timer.start( 1000 );
+ updateStatus();
+ } else {
+ // assert( ctx == Login );
+ isClear = true;
+ doReject( false );
+ }
+}
+
+void
+KGVerify::slotActivity()
+{
+ if (timedLeft) {
+ Debug( "%s->revive()\n", pName.data() );
+ greet->revive();
+ Debug( "%s->start()\n", pName.data() );
+ greet->start();
+ running = true;
+ timedLeft = 0;
+ updateStatus();
+ timer.start( TIMED_GREET_TO * SECONDS );
+ } else if (timeable)
+ timer.start( TIMED_GREET_TO * SECONDS );
+}
+
+
+void // private static
+KGVerify::VMsgBox( QWidget *parent, const QString &user,
+ QMessageBox::Icon type, const QString &mesg )
+{
+ FDialog::box( parent, type, user.isEmpty() ?
+ mesg : i18n("Authenticating %1...\n\n").arg( user ) + mesg );
+}
+
+static const char *msgs[]= {
+ I18N_NOOP( "You are required to change your password immediately (password aged)." ),
+ I18N_NOOP( "You are required to change your password immediately (root enforced)." ),
+ I18N_NOOP( "You are not allowed to login at the moment." ),
+ I18N_NOOP( "Home folder not available." ),
+ I18N_NOOP( "Logins are not allowed at the moment.\nTry again later." ),
+ I18N_NOOP( "Your login shell is not listed in /etc/shells." ),
+ I18N_NOOP( "Root logins are not allowed." ),
+ I18N_NOOP( "Your account has expired; please contact your system administrator." )
+};
+
+void // private static
+KGVerify::VErrBox( QWidget *parent, const QString &user, const char *msg )
+{
+ QMessageBox::Icon icon;
+ QString mesg;
+
+ if (!msg) {
+ mesg = i18n("A critical error occurred.\n"
+ "Please look at KDM's logfile(s) for more information\n"
+ "or contact your system administrator.");
+ icon = errorbox;
+ } else {
+ mesg = QString::fromLocal8Bit( msg );
+ QString mesg1 = mesg + '.';
+ for (uint i = 0; i < as(msgs); i++)
+ if (mesg1 == msgs[i]) {
+ mesg = i18n(msgs[i]);
+ break;
+ }
+ icon = sorrybox;
+ }
+ VMsgBox( parent, user, icon, mesg );
+}
+
+void // private static
+KGVerify::VInfoBox( QWidget *parent, const QString &user, const char *msg )
+{
+ QString mesg = QString::fromLocal8Bit( msg );
+ QRegExp rx( "^Warning: your account will expire in (\\d+) day" );
+ if (rx.search( mesg ) >= 0) {
+ int expire = rx.cap( 1 ).toInt();
+ mesg = expire ?
+ i18n("Your account expires tomorrow.",
+ "Your account expires in %n days.", expire) :
+ i18n("Your account expires today.");
+ } else {
+ rx.setPattern( "^Warning: your password will expire in (\\d+) day" );
+ if (rx.search( mesg ) >= 0) {
+ int expire = rx.cap( 1 ).toInt();
+ mesg = expire ?
+ i18n("Your password expires tomorrow.",
+ "Your password expires in %n days.", expire) :
+ i18n("Your password expires today.");
+ }
+ }
+ VMsgBox( parent, user, infobox, mesg );
+}
+
+bool // public static
+KGVerify::handleFailVerify( QWidget *parent )
+{
+ Debug( "handleFailVerify ...\n" );
+ char *msg = GRecvStr();
+ QString user = QString::fromLocal8Bit( msg );
+ free( msg );
+
+ for (;;) {
+ int ret = GRecvInt();
+
+ // non-terminal status
+ switch (ret) {
+ /* case V_PUT_USER: cannot happen - we are in "classic" mode */
+ /* case V_PRE_OK: cannot happen - not in ChTok dialog */
+ /* case V_CHTOK: cannot happen - called by non-interactive verify */
+ case V_CHTOK_AUTH:
+ Debug( " V_CHTOK_AUTH\n" );
+ {
+ QStringList pgs( _pluginsLogin );
+ pgs += _pluginsShutdown;
+ QStringList::ConstIterator it;
+ for (it = pgs.begin(); it != pgs.end(); ++it)
+ if (*it == "classic" || *it == "modern") {
+ pgs = *it;
+ goto gotit;
+ } else if (*it == "generic") {
+ pgs = "modern";
+ goto gotit;
+ }
+ pgs = "classic";
+ gotit:
+ KGChTok chtok( parent, user, init( pgs ), 0,
+ KGreeterPlugin::AuthChAuthTok,
+ KGreeterPlugin::Login );
+ return chtok.exec();
+ }
+ case V_MSG_ERR:
+ Debug( " V_MSG_ERR\n" );
+ msg = GRecvStr();
+ Debug( " message %\"s\n", msg );
+ VErrBox( parent, user, msg );
+ if (msg)
+ free( msg );
+ continue;
+ case V_MSG_INFO:
+ Debug( " V_MSG_INFO\n" );
+ msg = GRecvStr();
+ Debug( " message %\"s\n", msg );
+ VInfoBox( parent, user, msg );
+ free( msg );
+ continue;
+ }
+
+ // terminal status
+ switch (ret) {
+ case V_OK:
+ Debug( " V_OK\n" );
+ return true;
+ case V_AUTH:
+ Debug( " V_AUTH\n" );
+ VMsgBox( parent, user, sorrybox, i18n("Authentication failed") );
+ return false;
+ case V_FAIL:
+ Debug( " V_FAIL\n" );
+ return false;
+ default:
+ LogPanic( "Unknown V_xxx code %d from core\n", ret );
+ }
+ }
+}
+
+void // private
+KGVerify::handleVerify()
+{
+ QString user;
+
+ Debug( "handleVerify ...\n" );
+ for (;;) {
+ char *msg;
+ int ret, echo, ndelay;
+ KGreeterPlugin::Function nfunc;
+
+ ret = GRecvInt();
+
+ // requests
+ coreLock = 1;
+ switch (ret) {
+ case V_GET_TEXT:
+ Debug( " V_GET_TEXT\n" );
+ msg = GRecvStr();
+ Debug( " prompt %\"s\n", msg );
+ echo = GRecvInt();
+ Debug( " echo = %d\n", echo );
+ ndelay = GRecvInt();
+ Debug( " ndelay = %d\n%s->textPrompt(...)\n", ndelay, pName.data() );
+ greet->textPrompt( msg, echo, ndelay );
+ if (msg)
+ free( msg );
+ return;
+ case V_GET_BINARY:
+ Debug( " V_GET_BINARY\n" );
+ msg = GRecvArr( &ret );
+ Debug( " %d bytes prompt\n", ret );
+ ndelay = GRecvInt();
+ Debug( " ndelay = %d\n%s->binaryPrompt(...)\n", ndelay, pName.data() );
+ greet->binaryPrompt( msg, ndelay );
+ if (msg)
+ free( msg );
+ return;
+ }
+
+ // non-terminal status
+ coreLock = 2;
+ switch (ret) {
+ case V_PUT_USER:
+ Debug( " V_PUT_USER\n" );
+ msg = GRecvStr();
+ curUser = user = QString::fromLocal8Bit( msg );
+ // greet needs this to be able to return something useful from
+ // getEntity(). but the backend is still unable to tell a domain ...
+ Debug( " %s->setUser(%\"s)\n", pName.data(), user.latin1() );
+ greet->setUser( curUser );
+ handler->verifySetUser( curUser );
+ if (msg)
+ free( msg );
+ continue;
+ case V_PRE_OK: // this is only for func == AuthChAuthTok
+ Debug( " V_PRE_OK\n" );
+ // With the "classic" method, the wrong user simply cannot be
+ // authenticated, even with the generic plugin. Other methods
+ // could do so, but this applies only to ctx == ChangeTok, which
+ // is not implemented yet.
+ authTok = true;
+ cont = true;
+ Debug( "%s->succeeded()\n", pName.data() );
+ greet->succeeded();
+ continue;
+ case V_CHTOK_AUTH:
+ Debug( " V_CHTOK_AUTH\n" );
+ nfunc = KGreeterPlugin::AuthChAuthTok;
+ user = curUser;
+ goto dchtok;
+ case V_CHTOK:
+ Debug( " V_CHTOK\n" );
+ nfunc = KGreeterPlugin::ChAuthTok;
+ user = QString::null;
+ dchtok:
+ {
+ timer.stop();
+ Debug( "%s->succeeded()\n", pName.data() );
+ greet->succeeded();
+ KGChTok chtok( parent, user, pluginList, curPlugin, nfunc, KGreeterPlugin::Login );
+ if (!chtok.exec())
+ goto retry;
+ handler->verifyOk();
+ return;
+ }
+ case V_MSG_ERR:
+ Debug( " V_MSG_ERR\n" );
+ msg = GRecvStr();
+ Debug( " %s->textMessage(%\"s, true)\n", pName.data(), msg );
+ if (!greet->textMessage( msg, true )) {
+ Debug( " message passed\n" );
+ VErrBox( parent, user, msg );
+ } else
+ Debug( " message swallowed\n" );
+ if (msg)
+ free( msg );
+ continue;
+ case V_MSG_INFO:
+ Debug( " V_MSG_INFO\n" );
+ msg = GRecvStr();
+ Debug( " %s->textMessage(%\"s, false)\n", pName.data(), msg );
+ if (!greet->textMessage( msg, false )) {
+ Debug( " message passed\n" );
+ VInfoBox( parent, user, msg );
+ } else
+ Debug( " message swallowed\n" );
+ free( msg );
+ continue;
+ }
+
+ // terminal status
+ coreLock = 0;
+ running = false;
+ timer.stop();
+
+ if (ret == V_OK) {
+ Debug( " V_OK\n" );
+ if (!fixedEntity.isEmpty()) {
+ Debug( " %s->getEntity()\n", pName.data() );
+ QString ent = greet->getEntity();
+ Debug( " entity %\"s\n", ent.latin1() );
+ if (ent != fixedEntity) {
+ Debug( "%s->failed()\n", pName.data() );
+ greet->failed();
+ MsgBox( sorrybox,
+ i18n("Authenticated user (%1) does not match requested user (%2).\n")
+ .arg( ent ).arg( fixedEntity ) );
+ goto retry;
+ }
+ }
+ Debug( "%s->succeeded()\n", pName.data() );
+ greet->succeeded();
+ handler->verifyOk();
+ return;
+ }
+
+ Debug( "%s->failed()\n", pName.data() );
+ greet->failed();
+
+ if (ret == V_AUTH) {
+ Debug( " V_AUTH\n" );
+ failed = true;
+ updateStatus();
+ handler->verifyFailed();
+ timer.start( 1500 + kapp->random()/(RAND_MAX/1000) );
+ return;
+ }
+ if (ret != V_FAIL)
+ LogPanic( "Unknown V_xxx code %d from core\n", ret );
+ Debug( " V_FAIL\n" );
+ retry:
+ Debug( "%s->revive()\n", pName.data() );
+ greet->revive();
+ running = true;
+ Debug( "%s->start()\n", pName.data() );
+ greet->start();
+ if (!cont)
+ return;
+ user = QString::null;
+ }
+}
+
+void
+KGVerify::gplugReturnText( const char *text, int tag )
+{
+ Debug( "%s: gplugReturnText(%\"s, %d)\n", pName.data(),
+ tag & V_IS_SECRET ? "<masked>" : text, tag );
+ GSendStr( text );
+ if (text) {
+ GSendInt( tag );
+ handleVerify();
+ } else
+ coreLock = 0;
+}
+
+void
+KGVerify::gplugReturnBinary( const char *data )
+{
+ if (data) {
+ unsigned const char *up = (unsigned const char *)data;
+ int len = up[3] | (up[2] << 8) | (up[1] << 16) | (up[0] << 24);
+ Debug( "%s: gplugReturnBinary(%d bytes)\n", pName.data(), len );
+ GSendArr( len, data );
+ handleVerify();
+ } else {
+ Debug( "%s: gplugReturnBinary(NULL)\n", pName.data() );
+ GSendArr( 0, 0 );
+ coreLock = 0;
+ }
+}
+
+void
+KGVerify::gplugSetUser( const QString &user )
+{
+ Debug( "%s: gplugSetUser(%\"s)\n", pName.data(), user.latin1() );
+ curUser = user;
+ handler->verifySetUser( user );
+}
+
+void
+KGVerify::gplugStart()
+{
+ // XXX handle func != Authenticate
+ Debug( "%s: gplugStart()\n", pName.data() );
+ GSendInt( ctx == KGreeterPlugin::Shutdown ? G_VerifyRootOK : G_Verify );
+ GSendStr( greetPlugins[pluginList[curPlugin]].info->method );
+ handleVerify();
+}
+
+void
+KGVerify::gplugActivity()
+{
+ Debug( "%s: gplugActivity()\n", pName.data() );
+ if (func == KGreeterPlugin::Authenticate &&
+ ctx == KGreeterPlugin::Login)
+ {
+ isClear = false;
+ if (!timeable)
+ timer.start( FULL_GREET_TO * SECONDS );
+ }
+}
+
+void
+KGVerify::gplugMsgBox( QMessageBox::Icon type, const QString &text )
+{
+ Debug( "%s: gplugMsgBox(%d, %\"s)\n", pName.data(), type, text.latin1() );
+ MsgBox( type, text );
+}
+
+bool
+KGVerify::eventFilter( QObject *o, QEvent *e )
+{
+ switch (e->type()) {
+ case QEvent::KeyPress:
+ if (timedLeft) {
+ QKeyEvent *ke = (QKeyEvent *)e;
+ if (ke->key() == Key_Return || ke->key() == Key_Enter) {
+ if (deadTicks <= 0) {
+ timedLeft = 0;
+ performAutoLogin();
+ }
+ return true;
+ }
+ }
+ /* fall through */
+ case QEvent::KeyRelease:
+ updateLockStatus();
+ /* fall through */
+ default:
+ break;
+ }
+ return inherited::eventFilter( o, e );
+}
+
+void
+KGVerify::updateLockStatus()
+{
+ unsigned int lmask;
+ Window dummy1, dummy2;
+ int dummy3, dummy4, dummy5, dummy6;
+ XQueryPointer( qt_xdisplay(), DefaultRootWindow( qt_xdisplay() ),
+ &dummy1, &dummy2, &dummy3, &dummy4, &dummy5, &dummy6,
+ &lmask );
+ capsLocked = lmask & LockMask;
+ updateStatus();
+}
+
+void
+KGVerify::MsgBox( QMessageBox::Icon typ, const QString &msg )
+{
+ timer.suspend();
+ FDialog::box( parent, typ, msg );
+ timer.resume();
+}
+
+
+QVariant // public static
+KGVerify::getConf( void *, const char *key, const QVariant &dflt )
+{
+ if (!qstrcmp( key, "EchoMode" ))
+ return QVariant( _echoMode );
+ else {
+ QString fkey = QString::fromLatin1( key ) + '=';
+ for (QStringList::ConstIterator it = _pluginOptions.begin();
+ it != _pluginOptions.end(); ++it)
+ if ((*it).startsWith( fkey ))
+ return (*it).mid( fkey.length() );
+ return dflt;
+ }
+}
+
+QValueVector<GreeterPluginHandle> KGVerify::greetPlugins;
+
+PluginList
+KGVerify::init( const QStringList &plugins )
+{
+ PluginList pluginList;
+
+ for (QStringList::ConstIterator it = plugins.begin(); it != plugins.end(); ++it) {
+ GreeterPluginHandle plugin;
+ QString path = KLibLoader::self()->findLibrary(
+ ((*it)[0] == '/' ? *it : "kgreet_" + *it ).latin1() );
+ if (path.isEmpty()) {
+ LogError( "GreeterPlugin %s does not exist\n", (*it).latin1() );
+ continue;
+ }
+ uint i, np = greetPlugins.count();
+ for (i = 0; i < np; i++)
+ if (greetPlugins[i].library->fileName() == path)
+ goto next;
+ if (!(plugin.library = KLibLoader::self()->library( path.latin1() ))) {
+ LogError( "Cannot load GreeterPlugin %s (%s)\n", (*it).latin1(), path.latin1() );
+ continue;
+ }
+ if (!plugin.library->hasSymbol( "kgreeterplugin_info" )) {
+ LogError( "GreeterPlugin %s (%s) is no valid greet widget plugin\n",
+ (*it).latin1(), path.latin1() );
+ plugin.library->unload();
+ continue;
+ }
+ plugin.info = (kgreeterplugin_info*)plugin.library->symbol( "kgreeterplugin_info" );
+ if (!plugin.info->init( QString::null, getConf, 0 )) {
+ LogError( "GreeterPlugin %s (%s) refuses to serve\n",
+ (*it).latin1(), path.latin1() );
+ plugin.library->unload();
+ continue;
+ }
+ Debug( "GreeterPlugin %s (%s) loaded\n", (*it).latin1(), plugin.info->name );
+ greetPlugins.append( plugin );
+ next:
+ pluginList.append( i );
+ }
+ return pluginList;
+}
+
+void
+KGVerify::done()
+{
+ for (uint i = 0; i < greetPlugins.count(); i++) {
+ if (greetPlugins[i].info->done)
+ greetPlugins[i].info->done();
+ greetPlugins[i].library->unload();
+ }
+}
+
+
+KGStdVerify::KGStdVerify( KGVerifyHandler *_handler, QWidget *_parent,
+ QWidget *_predecessor, const QString &_fixedUser,
+ const PluginList &_pluginList,
+ KGreeterPlugin::Function _func,
+ KGreeterPlugin::Context _ctx )
+ : inherited( _handler, 0, _parent, _predecessor, _fixedUser,
+ _pluginList, _func, _ctx )
+ , failedLabelState( 0 )
+{
+ grid = new QGridLayout;
+ grid->setAlignment( AlignCenter );
+
+ failedLabel = new QLabel( parent );
+ failedLabel->setFont( _failFont );
+ grid->addWidget( failedLabel, 1, 0, AlignCenter );
+
+ updateLockStatus();
+}
+
+KGStdVerify::~KGStdVerify()
+{
+ grid->removeItem( greet->getLayoutItem() );
+}
+
+void // public
+KGStdVerify::selectPlugin( int id )
+{
+ inherited::selectPlugin( id );
+ grid->addItem( greet->getLayoutItem(), 0, 0 );
+ showWidgets( greet->getLayoutItem() );
+}
+
+void // private slot
+KGStdVerify::slotPluginSelected( int id )
+{
+ if (failed)
+ return;
+ if (id != curPlugin) {
+ plugMenu->setItemChecked( curPlugin, false );
+ parent->setUpdatesEnabled( false );
+ grid->removeItem( greet->getLayoutItem() );
+ Debug( "delete %s\n", pName.data() );
+ delete greet;
+ selectPlugin( id );
+ handler->verifyPluginChanged( id );
+ if (running)
+ start();
+ parent->setUpdatesEnabled( true );
+ }
+}
+
+void
+KGStdVerify::updateStatus()
+{
+ int nfls;
+
+ if (!enabled)
+ nfls = 1;
+ else if (failed)
+ nfls = 2;
+ else if (timedLeft)
+ nfls = -timedLeft;
+ else if (!suspended && capsLocked)
+ nfls = 3;
+ else
+ nfls = 1;
+
+ if (failedLabelState != nfls) {
+ failedLabelState = nfls;
+ if (nfls < 0) {
+ failedLabel->setPaletteForegroundColor( Qt::black );
+ failedLabel->setText( i18n( "Automatic login in 1 second...",
+ "Automatic login in %n seconds...",
+ timedLeft ) );
+ } else {
+ switch (nfls) {
+ default:
+ failedLabel->clear();
+ break;
+ case 3:
+ failedLabel->setPaletteForegroundColor( Qt::red );
+ failedLabel->setText( i18n("Warning: Caps Lock on") );
+ break;
+ case 2:
+ failedLabel->setPaletteForegroundColor( Qt::black );
+ failedLabel->setText( authTok ?
+ i18n("Change failed") :
+ fixedEntity.isEmpty() ?
+ i18n("Login failed") :
+ i18n("Authentication failed") );
+ break;
+ }
+ }
+ }
+}
+
+KGThemedVerify::KGThemedVerify( KGVerifyHandler *_handler,
+ KdmThemer *_themer,
+ QWidget *_parent, QWidget *_predecessor,
+ const QString &_fixedUser,
+ const PluginList &_pluginList,
+ KGreeterPlugin::Function _func,
+ KGreeterPlugin::Context _ctx )
+ : inherited( _handler, _themer, _parent, _predecessor, _fixedUser,
+ _pluginList, _func, _ctx )
+{
+ updateLockStatus();
+}
+
+KGThemedVerify::~KGThemedVerify()
+{
+}
+
+void // public
+KGThemedVerify::selectPlugin( int id )
+{
+ inherited::selectPlugin( id );
+ QLayoutItem *l;
+ KdmItem *n;
+ if (themer && (l = greet->getLayoutItem())) {
+ if (!(n = themer->findNode( "talker" )))
+ MsgBox( errorbox,
+ i18n("Theme not usable with authentication method '%1'.")
+ .arg( i18n(greetPlugins[pluginList[id]].info->name) ) );
+ else {
+ n->setLayoutItem( l );
+ showWidgets( l );
+ }
+ }
+ if (themer)
+ themer->updateGeometry( true );
+}
+
+void // private slot
+KGThemedVerify::slotPluginSelected( int id )
+{
+ if (failed)
+ return;
+ if (id != curPlugin) {
+ plugMenu->setItemChecked( curPlugin, false );
+ Debug( "delete %s\n", pName.data() );
+ delete greet;
+ selectPlugin( id );
+ handler->verifyPluginChanged( id );
+ if (running)
+ start();
+ }
+}
+
+void
+KGThemedVerify::updateStatus()
+{
+ handler->updateStatus( enabled && failed,
+ enabled && !suspended && capsLocked,
+ timedLeft );
+}
+
+
+KGChTok::KGChTok( QWidget *_parent, const QString &user,
+ const PluginList &pluginList, int curPlugin,
+ KGreeterPlugin::Function func,
+ KGreeterPlugin::Context ctx )
+ : inherited( _parent )
+ , verify( 0 )
+{
+ QSizePolicy fp( QSizePolicy::Fixed, QSizePolicy::Fixed );
+ okButton = new KPushButton( KStdGuiItem::ok(), this );
+ okButton->setSizePolicy( fp );
+ okButton->setDefault( true );
+ cancelButton = new KPushButton( KStdGuiItem::cancel(), this );
+ cancelButton->setSizePolicy( fp );
+
+ verify = new KGStdVerify( this, this, cancelButton, user, pluginList, func, ctx );
+ verify->selectPlugin( curPlugin );
+
+ QVBoxLayout *box = new QVBoxLayout( this, 10 );
+
+ box->addWidget( new QLabel( i18n("Changing authentication token"), this ), 0, AlignHCenter );
+
+ box->addLayout( verify->getLayout() );
+
+ box->addWidget( new KSeparator( KSeparator::HLine, this ) );
+
+ QHBoxLayout *hlay = new QHBoxLayout( box );
+ hlay->addStretch( 1 );
+ hlay->addWidget( okButton );
+ hlay->addStretch( 1 );
+ hlay->addWidget( cancelButton );
+ hlay->addStretch( 1 );
+
+ connect( okButton, SIGNAL(clicked()), SLOT(accept()) );
+ connect( cancelButton, SIGNAL(clicked()), SLOT(reject()) );
+
+ QTimer::singleShot( 0, verify, SLOT(start()) );
+}
+
+KGChTok::~KGChTok()
+{
+ hide();
+ delete verify;
+}
+
+void
+KGChTok::accept()
+{
+ verify->accept();
+}
+
+void
+KGChTok::verifyPluginChanged( int )
+{
+ // cannot happen
+}
+
+void
+KGChTok::verifyOk()
+{
+ inherited::accept();
+}
+
+void
+KGChTok::verifyFailed()
+{
+ okButton->setEnabled( false );
+ cancelButton->setEnabled( false );
+}
+
+void
+KGChTok::verifyRetry()
+{
+ okButton->setEnabled( true );
+ cancelButton->setEnabled( true );
+}
+
+void
+KGChTok::verifySetUser( const QString & )
+{
+ // cannot happen
+}
+
+
+////// helper class, nuke when qtimer supports suspend()/resume()
+
+QXTimer::QXTimer()
+ : inherited( 0 )
+ , left( -1 )
+{
+ connect( &timer, SIGNAL(timeout()), SLOT(slotTimeout()) );
+}
+
+void
+QXTimer::start( int msec )
+{
+ left = msec;
+ timer.start( left, true );
+ gettimeofday( &stv, 0 );
+}
+
+void
+QXTimer::stop()
+{
+ timer.stop();
+ left = -1;
+}
+
+void
+QXTimer::suspend()
+{
+ if (timer.isActive()) {
+ timer.stop();
+ struct timeval tv;
+ gettimeofday( &tv, 0 );
+ left -= (tv.tv_sec - stv.tv_sec) * 1000 + (tv.tv_usec - stv.tv_usec) / 1000;
+ if (left < 0)
+ left = 0;
+ }
+}
+
+void
+QXTimer::resume()
+{
+ if (left >= 0 && !timer.isActive()) {
+ timer.start( left, true );
+ gettimeofday( &stv, 0 );
+ }
+}
+
+void
+QXTimer::slotTimeout()
+{
+ left = 0;
+ emit timeout();
+}
+
+
+#include "kgverify.moc"
diff --git a/kdm/kfrontend/kgverify.h b/kdm/kfrontend/kgverify.h
new file mode 100644
index 000000000..a0d285e3d
--- /dev/null
+++ b/kdm/kfrontend/kgverify.h
@@ -0,0 +1,248 @@
+/*
+
+Shell for kdm conversation plugins
+
+Copyright (C) 1997, 1998 Steffen Hansen <hansen@kde.org>
+Copyright (C) 2000-2004 Oswald Buddenhagen <ossi@kde.org>
+
+
+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.
+
+*/
+
+
+#ifndef KGVERIFY_H
+#define KGVERIFY_H
+
+#include "kgreeterplugin.h"
+#include "kfdialog.h"
+
+#include <qlayout.h>
+#include <qtimer.h>
+#include <qvaluevector.h>
+
+#include <sys/time.h>
+#include <time.h>
+
+// helper class, nuke when qt supports suspend()/resume()
+class QXTimer : public QObject {
+ Q_OBJECT
+ typedef QObject inherited;
+
+ public:
+ QXTimer();
+ void start( int msec );
+ void stop();
+ void suspend();
+ void resume();
+
+ signals:
+ void timeout();
+
+ private slots:
+ void slotTimeout();
+
+ private:
+ QTimer timer;
+ struct timeval stv;
+ long left;
+};
+
+class KGVerifyHandler {
+ public:
+ virtual void verifyPluginChanged( int id ) = 0;
+ virtual void verifyClear();
+ virtual void verifyOk() = 0;
+ virtual void verifyFailed() = 0;
+ virtual void verifyRetry() = 0;
+ virtual void verifySetUser( const QString &user ) = 0;
+ virtual void updateStatus( bool fail, bool caps, int left ); // for themed only
+};
+
+class QWidget;
+class QLabel;
+class QPopupMenu;
+class QTimer;
+class KPushButton;
+class KLibrary;
+
+struct GreeterPluginHandle {
+ KLibrary *library;
+ kgreeterplugin_info *info;
+};
+
+typedef QValueVector<int> PluginList;
+
+class KGVerify : public QObject, public KGreeterPluginHandler {
+ Q_OBJECT
+ typedef QObject inherited;
+
+ public:
+ KGVerify( KGVerifyHandler *handler, KdmThemer *themer,
+ QWidget *parent, QWidget *predecessor,
+ const QString &fixedEntity, const PluginList &pluginList,
+ KGreeterPlugin::Function func, KGreeterPlugin::Context ctx );
+ virtual ~KGVerify();
+ QPopupMenu *getPlugMenu();
+ void loadUsers( const QStringList &users );
+ void presetEntity( const QString &entity, int field );
+ QString getEntity() const;
+ void setUser( const QString &user );
+ /* virtual */ void selectPlugin( int id );
+ bool entitiesLocal() const;
+ bool entitiesFielded() const;
+ bool entityPresettable() const;
+ bool isClassic() const;
+ QString pluginName() const;
+ void setEnabled( bool on );
+ void abort();
+ void suspend();
+ void resume();
+ void accept();
+ void reject();
+
+ int coreLock;
+
+ static bool handleFailVerify( QWidget *parent );
+ static PluginList init( const QStringList &plugins );
+ static void done();
+
+ public slots:
+ void start();
+
+ protected:
+ bool eventFilter( QObject *, QEvent * );
+ void MsgBox( QMessageBox::Icon typ, const QString &msg );
+ void setTimer();
+ void updateLockStatus();
+ virtual void updateStatus() = 0;
+ void handleVerify();
+
+ QXTimer timer;
+ QString fixedEntity, presEnt, curUser;
+ PluginList pluginList;
+ KGVerifyHandler *handler;
+ KdmThemer *themer;
+ QWidget *parent, *predecessor;
+ KGreeterPlugin *greet;
+ QPopupMenu *plugMenu;
+ int curPlugin, presFld, timedLeft, deadTicks;
+ QCString pName;
+ KGreeterPlugin::Function func;
+ KGreeterPlugin::Context ctx;
+ bool capsLocked;
+ bool enabled, running, suspended, failed, delayed, cont;
+ bool authTok, isClear, timeable;
+
+ static void VMsgBox( QWidget *parent, const QString &user, QMessageBox::Icon type, const QString &mesg );
+ static void VErrBox( QWidget *parent, const QString &user, const char *msg );
+ static void VInfoBox( QWidget *parent, const QString &user, const char *msg );
+
+ static QValueVector<GreeterPluginHandle> greetPlugins;
+
+ private:
+ bool applyPreset();
+ void performAutoLogin();
+ bool scheduleAutoLogin( bool initial );
+ void doReject( bool initial );
+
+ private slots:
+ //virtual void slotPluginSelected( int id ) = 0;
+ void slotTimeout();
+ void slotActivity();
+
+ public: // from KGreetPluginHandler
+ virtual void gplugReturnText( const char *text, int tag );
+ virtual void gplugReturnBinary( const char *data );
+ virtual void gplugSetUser( const QString &user );
+ virtual void gplugStart();
+ virtual void gplugActivity();
+ virtual void gplugMsgBox( QMessageBox::Icon type, const QString &text );
+
+ static QVariant getConf( void *ctx, const char *key, const QVariant &dflt );
+};
+
+class KGStdVerify : public KGVerify {
+ Q_OBJECT
+ typedef KGVerify inherited;
+
+ public:
+ KGStdVerify( KGVerifyHandler *handler, QWidget *parent,
+ QWidget *predecessor, const QString &fixedEntity,
+ const PluginList &pluginList,
+ KGreeterPlugin::Function func, KGreeterPlugin::Context ctx );
+ virtual ~KGStdVerify();
+ QLayout *getLayout() const { return grid; }
+ void selectPlugin( int id );
+
+ protected:
+ void updateStatus();
+
+ private:
+ QGridLayout *grid;
+ QLabel *failedLabel;
+ int failedLabelState;
+
+ private slots:
+ void slotPluginSelected( int id );
+};
+
+class KGThemedVerify : public KGVerify {
+ Q_OBJECT
+ typedef KGVerify inherited;
+
+ public:
+ KGThemedVerify( KGVerifyHandler *handler, KdmThemer *themer,
+ QWidget *parent, QWidget *predecessor,
+ const QString &fixedEntity,
+ const PluginList &pluginList,
+ KGreeterPlugin::Function func,
+ KGreeterPlugin::Context ctx );
+ virtual ~KGThemedVerify();
+ void selectPlugin( int id );
+
+ protected:
+ void updateStatus();
+
+ private slots:
+ void slotPluginSelected( int id );
+};
+
+class KGChTok : public FDialog, public KGVerifyHandler {
+ Q_OBJECT
+ typedef FDialog inherited;
+
+ public:
+ KGChTok( QWidget *parent, const QString &user,
+ const PluginList &pluginList, int curPlugin,
+ KGreeterPlugin::Function func, KGreeterPlugin::Context ctx );
+ ~KGChTok();
+
+ public slots:
+ void accept();
+
+ private:
+ KPushButton *okButton, *cancelButton;
+ KGStdVerify *verify;
+
+ public: // from KGVerifyHandler
+ virtual void verifyPluginChanged( int id );
+ virtual void verifyOk();
+ virtual void verifyFailed();
+ virtual void verifyRetry();
+ virtual void verifySetUser( const QString &user );
+};
+
+#endif /* KGVERIFY_H */
diff --git a/kdm/kfrontend/krootimage.cpp b/kdm/kfrontend/krootimage.cpp
new file mode 100644
index 000000000..c630d9fa7
--- /dev/null
+++ b/kdm/kfrontend/krootimage.cpp
@@ -0,0 +1,122 @@
+/*
+
+Copyright (C) 1999 Matthias Hoelzer-Kluepfel <hoelzer@kde.org>
+Copyright (C) 2002,2004 Oswald Buddenhagen <ossi@kde.org>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public
+License version 2 as published by the Free Software Foundation.
+
+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; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA.
+
+*/
+
+#include <config.h>
+
+#include <kcmdlineargs.h>
+#include <ksimpleconfig.h>
+#include <klocale.h>
+
+#include <qfile.h>
+
+#include "krootimage.h"
+
+#include <X11/Xlib.h>
+
+#include <stdlib.h>
+
+static const char description[] =
+ I18N_NOOP( "Fancy desktop background for kdm" );
+
+static const char version[] = "v2.0";
+
+static KCmdLineOptions options[] = {
+ { "+config", I18N_NOOP( "Name of the configuration file" ), 0 },
+ KCmdLineLastOption
+};
+
+
+MyApplication::MyApplication( const char *conf )
+ : KApplication(),
+ renderer( 0, new KSimpleConfig( QFile::decodeName( conf ) ) )
+{
+ connect( &timer, SIGNAL(timeout()), SLOT(slotTimeout()) );
+ connect( &renderer, SIGNAL(imageDone( int )), this, SLOT(renderDone()) );
+ renderer.enableTiling( true ); // optimize
+ renderer.changeWallpaper(); // cannot do it when we're killed, so do it now
+ timer.start( 60000 );
+ renderer.start();
+}
+
+
+void
+MyApplication::renderDone()
+{
+ desktop()->setBackgroundPixmap( renderer.pixmap() );
+ desktop()->repaint( true );
+ renderer.saveCacheFile();
+ renderer.cleanup();
+ for (unsigned i=0; i<renderer.numRenderers(); ++i)
+ {
+ KBackgroundRenderer * r = renderer.renderer(i);
+ if (r->backgroundMode() == KBackgroundSettings::Program ||
+ (r->multiWallpaperMode() != KBackgroundSettings::NoMulti &&
+ r->multiWallpaperMode() != KBackgroundSettings::NoMultiRandom))
+ return;
+ }
+ quit();
+}
+
+void
+MyApplication::slotTimeout()
+{
+ bool change = false;
+
+ if (renderer.needProgramUpdate()) {
+ renderer.programUpdate();
+ change = true;
+ }
+
+ if (renderer.needWallpaperChange()) {
+ renderer.changeWallpaper();
+ change = true;
+ }
+
+ if (change)
+ renderer.start();
+}
+
+int
+main( int argc, char *argv[] )
+{
+ KApplication::disableAutoDcopRegistration();
+
+ KLocale::setMainCatalogue( "kdesktop" );
+ KCmdLineArgs::init( argc, argv, "krootimage", I18N_NOOP( "KRootImage" ), description, version );
+ KCmdLineArgs::addCmdLineOptions( options );
+
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+ if (!args->count())
+ args->usage();
+ MyApplication app( args->arg( 0 ) );
+ args->clear();
+
+ app.exec();
+
+ app.flushX();
+
+ // Keep color resources after termination
+ XSetCloseDownMode( qt_xdisplay(), RetainTemporary );
+
+ return 0;
+}
+
+#include "krootimage.moc"
diff --git a/kdm/kfrontend/krootimage.h b/kdm/kfrontend/krootimage.h
new file mode 100644
index 000000000..608cfa3a1
--- /dev/null
+++ b/kdm/kfrontend/krootimage.h
@@ -0,0 +1,48 @@
+/*
+
+Copyright (C) 1999 Matthias Hoelzer-Kluepfel <hoelzer@kde.org>
+Copyright (C) 2002,2004 Oswald Buddenhagen <ossi@kde.org>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public
+License version 2 as published by the Free Software Foundation.
+
+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; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA.
+
+*/
+
+#ifndef __KDMDESKTOP_H__
+#define __KDMDESKTOP_H__
+
+
+#include <kapplication.h>
+#include <qtimer.h>
+
+#include <bgrender.h>
+
+
+class MyApplication : public KApplication
+{
+ Q_OBJECT
+
+ public:
+ MyApplication( const char * );
+
+ private slots:
+ void renderDone();
+ void slotTimeout();
+
+ private:
+ KVirtualBGRenderer renderer;
+ QTimer timer;
+};
+
+#endif
diff --git a/kdm/kfrontend/pics/Makefile.am b/kdm/kfrontend/pics/Makefile.am
new file mode 100644
index 000000000..d6b8957e8
--- /dev/null
+++ b/kdm/kfrontend/pics/Makefile.am
@@ -0,0 +1,9 @@
+
+picsdir = $(kde_datadir)/kdm/pics
+pics_DATA = kdelogo.png kdelogo-crystal.png shutdown.jpg
+
+usersdir = $(picsdir)/users
+users_DATA = default1.png default2.png default3.png root1.png
+
+
+EXTRA_DIST = $(pics_DATA) $(users_DATA)
diff --git a/kdm/kfrontend/pics/default1.png b/kdm/kfrontend/pics/default1.png
new file mode 100644
index 000000000..ef3aef3f9
--- /dev/null
+++ b/kdm/kfrontend/pics/default1.png
Binary files differ
diff --git a/kdm/kfrontend/pics/default2.png b/kdm/kfrontend/pics/default2.png
new file mode 100644
index 000000000..194acfe2c
--- /dev/null
+++ b/kdm/kfrontend/pics/default2.png
Binary files differ
diff --git a/kdm/kfrontend/pics/default3.png b/kdm/kfrontend/pics/default3.png
new file mode 100644
index 000000000..a8663b15e
--- /dev/null
+++ b/kdm/kfrontend/pics/default3.png
Binary files differ
diff --git a/kdm/kfrontend/pics/kdelogo-crystal.png b/kdm/kfrontend/pics/kdelogo-crystal.png
new file mode 100644
index 000000000..592a7e321
--- /dev/null
+++ b/kdm/kfrontend/pics/kdelogo-crystal.png
Binary files differ
diff --git a/kdm/kfrontend/pics/kdelogo.png b/kdm/kfrontend/pics/kdelogo.png
new file mode 100644
index 000000000..bd4e199e5
--- /dev/null
+++ b/kdm/kfrontend/pics/kdelogo.png
Binary files differ
diff --git a/kdm/kfrontend/pics/root1.png b/kdm/kfrontend/pics/root1.png
new file mode 100644
index 000000000..fced75c11
--- /dev/null
+++ b/kdm/kfrontend/pics/root1.png
Binary files differ
diff --git a/kdm/kfrontend/pics/shutdown.jpg b/kdm/kfrontend/pics/shutdown.jpg
new file mode 100644
index 000000000..f1353c54b
--- /dev/null
+++ b/kdm/kfrontend/pics/shutdown.jpg
Binary files differ
diff --git a/kdm/kfrontend/sessions/9wm.desktop b/kdm/kfrontend/sessions/9wm.desktop
new file mode 100644
index 000000000..d17276782
--- /dev/null
+++ b/kdm/kfrontend/sessions/9wm.desktop
@@ -0,0 +1,76 @@
+[Desktop Entry]
+Type=XSession
+Exec=9wm
+TryExec=9wm
+Name=9WM
+Name[cy]= 9WM
+Name[eo]=9FA
+Name[hi]=9डबल्यू-एम
+Name[ta]=9 WM
+Name[te]=9 డబ్ల్యు ఎం
+Name[th]=ตัวจัดการหน้าต่าง gWM
+Comment=An emulation of the Plan 9 window manager 8-1/2
+Comment[af]='n Nabootsing van die 'Plan 9' venster bestuurder
+Comment[be]=Эмуляцыя кіраўніка вокнаў 8-1/2 для Plan 9
+Comment[bn]=প্ল্যান ৯ উইণ্ডো ম্যানেজার ৮-১/২ -এর এমুলেশন
+Comment[bs]=Simulacija Plan 9 window managera 8-1/2
+Comment[ca]=Una emulació del gestor de finestres Plan 9
+Comment[cs]=Emulace Plane 9 správce oken 8-1/2
+Comment[csb]=Emùlacëjô menedżera òknów Plan 9 - 8-1/2
+Comment[cy]=Efelychiad o 8-1/2, y trefnydd ffenestri Plan 9
+Comment[da]=En emulering af Plan 9 vindueshåndteringen 8-1/2
+Comment[de]=Emulation des Plan 9-Fenstermanagers 8-1/2
+Comment[el]=Μια προσομοίωση του Plan 9 διαχειριστή παραθύρων 8-1/2
+Comment[eo]=KDE-fenestroadministrilo de plano 9
+Comment[es]=Una emulación del gestor de ventanas Plan 9 8-1/2
+Comment[et]=Plan 9 aknahalduri 8-1/2 emuleerimine
+Comment[eu]=Plan 9 8-1/2 leiho kudeatzailearen emulazioa
+Comment[fa]=تقلیدی از نقشۀ مدیر پنجره ۹. ۸-۱/ ۲
+Comment[fi]=Emulaatio Plan9-ikkunaohjelmasta 8-1/2
+Comment[fr]=Une émulation du gestionnaire de fenêtres Plan 9 8-1/2
+Comment[fy]=In emulator foar de Plan9 finstersbehearder 8-1/2
+Comment[gl]=Unha emulación do xestor de fiestras de Plan9
+Comment[he]=מדמה של מנהל חלונות Plan 9 8-1/2
+Comment[hi]=प्लान 9 विंडो प्रबंधक 8-1/2 का एक एमुलेशन
+Comment[hr]=Emulacija Plan 9 upravitelja prozora 8-1/2
+Comment[hu]=A Plan 9 operációs rendszer 8-1/2 nevű ablakkezelőjének emulálása
+Comment[is]=Eftirlíking af Plan 9 gluggastjóranum 8-1/2
+Comment[it]=Un emulatore del window manager 8-1/2 Plan 9
+Comment[ja]=Plan9 ウィンドウマネージャのエミュレーション 8-1/2
+Comment[ka]=Plan 9 ფანჯრის მმართველის ემულატორი
+Comment[kk]=Plan 9 терезе менеджерінің эмуляторы
+Comment[km]=ការ​ត្រាប់​តាម​កម្មវិធី​គ្រប់គ្រង​បង្អួច Plan 9 8-1/2
+Comment[ko]=Plan 9 창 관리자 8-1/2 에뮬레이션
+Comment[lt]=Plan 9 langų tvarkyklės emuliatorius 8-1/2
+Comment[lv]=Plan 9 logu menedžera emulators 8-1/2
+Comment[mk]=Емулација на менаџерот на прозорци Plan 9 8-1/2
+Comment[ms]=Pelagakan Plan 9 pengurus tetingkap 8-1/2
+Comment[mt]=Emulazzjoni tal-window manager "Plan 9" 8½
+Comment[nb]=En emulering av vindusbehandleren 8 ½ fra Plan 9
+Comment[nds]=Emuleert den Plan-9-Finsterpleger 8-1/2
+Comment[ne]=योजना 9 सञ्झ्याल प्रबन्धक 8-1/2 को इमुलेसन
+Comment[nl]=Een emulator voor de Plan9 windowmanager 8-1/2
+Comment[nn]=Emulering av vindaugssjefen 8 ½ frå Plan 9
+Comment[pa]=ਪਲੇਨ 9 ਝਰੋਖਾ ਮੈਨੇਜਰ 8-1/2 ਦਾ ਸਮਰੂਪ
+Comment[pl]=Emulacja menedżera okien Plan 9 - 8-1/2
+Comment[pt]=Uma emulação do gestor de janelas do Plan 9 8-1/2
+Comment[pt_BR]=Uma emulação do gerenciador de janelas do Plan 9
+Comment[ro]=Un emulator al managerului de ferestre 8-1/2 din Plan 9
+Comment[ru]=Эмуляция оконного менеджера Plan 9
+Comment[rw]=Ikurura rya mugenga dirishya 8-1/2 Plan 9
+Comment[se]=Lásegieđahalli mii áddestaddá Plan 9 lásegieđahalli 8-1/2.
+Comment[sk]=Emulácia správcu okien 8-1/2 systému Plan 9
+Comment[sl]=Emulacija okenskega upravitelja Plan 9 8-1/2
+Comment[sr]=Емулација Plan 9 менаџера прозора 8-1/2
+Comment[sr@Latn]=Emulacija Plan 9 menadžera prozora 8-1/2
+Comment[sv]=Emulering av Plan-9-fönsterhanteraren 8-1/2
+Comment[ta]= திட்டம் 9 சாளர மேலாளர் 8-1/2 இன் முன்மாதிரி
+Comment[tg]=Эмулятори нақшаи 9-и мудири тирезаи 8-1/2
+Comment[th]=การจำลองตัวจัดการหน้าต่าง Plan 9 8-1/2
+Comment[tr]=Plan 9 pencere yöneticisi 8-1/2 için bir emülasyon
+Comment[tt]=Plan 9 atlı täräzä idäräçenä axşap eşläw
+Comment[uk]=Емуляція менеджера вікон Plan 9 "8-1/2"
+Comment[vi]=Một bộ mô phỏng bộ quản lý cửa sổ Plan 9 8-1/2
+Comment[wa]=Ene emulåcion do manaedjeu di purneas di Plan 9
+Comment[zh_CN]=Plan 9 窗口管理器 8-1/2 的模拟
+Comment[zh_TW]=模仿 Plan 9 的視窗管理程式 8-1/2
diff --git a/kdm/kfrontend/sessions/Makefile.am b/kdm/kfrontend/sessions/Makefile.am
new file mode 100644
index 000000000..14577ac42
--- /dev/null
+++ b/kdm/kfrontend/sessions/Makefile.am
@@ -0,0 +1,49 @@
+sessionsdir = $(kde_datadir)/kdm/sessions
+sessions_DATA = \
+ kde.desktop gnome.desktop \
+ 9wm.desktop \
+ aewm++.desktop \
+ aewm.desktop \
+ afterstep.desktop \
+ amaterus.desktop \
+ amiwm.desktop \
+ asclassic.desktop \
+ blackbox.desktop \
+ cde.desktop \
+ ctwm.desktop \
+ cwwm.desktop \
+ enlightenment.desktop \
+ evilwm.desktop \
+ fluxbox.desktop \
+ flwm.desktop \
+ fvwm.desktop \
+ fvwm95.desktop \
+ golem.desktop \
+ icewm.desktop \
+ ion.desktop \
+ larswm.desktop \
+ lwm.desktop \
+ matchbox.desktop \
+ metacity.desktop \
+ mwm.desktop \
+ olvwm.desktop \
+ olwm.desktop \
+ openbox.desktop \
+ oroborus.desktop \
+ phluid.desktop \
+ pwm.desktop \
+ qvwm.desktop \
+ ratpoison.desktop \
+ sapphire.desktop \
+ sawfish.desktop \
+ twm.desktop \
+ ude.desktop \
+ vtwm.desktop \
+ w9wm.desktop \
+ waimea.desktop \
+ wm2.desktop \
+ wmaker.desktop \
+ xfce.desktop \
+ xfce4.desktop
+
+EXTRA_DIST = $(sessions_DATA)
diff --git a/kdm/kfrontend/sessions/aewm++.desktop b/kdm/kfrontend/sessions/aewm++.desktop
new file mode 100644
index 000000000..3eb4ee8e8
--- /dev/null
+++ b/kdm/kfrontend/sessions/aewm++.desktop
@@ -0,0 +1,74 @@
+[Desktop Entry]
+Type=XSession
+Exec=aewm++_xsession
+TryExec=aewm++_xsession
+Name=AEWM++
+Name[eo]=MFA++
+Name[hi]=एईडबल्यूएम++
+Name[te]=ఎ ఈ డబ్ల్యు ఎం ++
+Comment=A minimal window manager based on AEWM, enhanced by virtual desktops and partial GNOME support
+Comment[af]='n Minimale venster bestuurder wat op AEWM gebaseer is. Dit is verbeter met virtuale werkskerms en gedeeltelike GNOME ondersteuning
+Comment[ar]=مدير نوافذ مصغّر مبني على AEWM، محسّن بأسطح مكتب وهمية ودعم جينوم جزئي
+Comment[be]=Мінімалістычны кіраўнік вокнаў, заснаваны на AEWM, з віртуальнымі працоўнымі сталамі і частковай падтрымкай GNOME
+Comment[bn]=AEWM ভিত্তিক একটি পরিমিত উইণ্ডো ম্যানেজার, ভার্চুয়াল ডেস্কটপ এবং আংশিক গনোম সমর্থন দ্বারা বর্ধিত
+Comment[bs]=Minimalni window manager baziran na AEWM, proširen virtuelnim desktopima i djelomičnom GNOME podrškom
+Comment[ca]=Un gestor de finestres minimalista basat en AEWM, orientat a escriptoris virtuals i funcionament parcial per a GNOME
+Comment[cs]=Minimalizovaný správce oken založený na AEWM rozšířený o virtuální plochy a částečnou podporu GNOME
+Comment[csb]=Prosti menedżer òknów na spòdlém AEWM, zbògacony o wirtualné pùltë ë dzélowé wspiarce dlô GNOME
+Comment[cy]= Trefnydd ffenestri lleiafol wed'i seilio ar AEWM, wedi'i wella gan penbyrddau rhith a cynhaliaeth Gnome rhannol.
+Comment[da]=En minimal vindueshåndtering baseret på AEWM, udvidet med virtuelle desktoppe og delvis GNOME-støtte
+Comment[de]=Minimalistischer Fenstermanager. Beruht auf AEWM, verbessert durch virtuelle Arbeitsflächen und teilweise GNOME-Unterstützung
+Comment[el]=Ένας μικρός διαχειριστής παραθύρων βασισμένος στον AEWM, εμπλουτισμένος με εικονικές επιφάνειες εργασίας και μερική υποστήριξη GNOME
+Comment[eo]=Minimuma fenestroadministrilo el MFA, plibonigita per virtualaj labortabloj kaj parta helpo por Gnomikuo
+Comment[es]=Un gestor de ventanas minimalista basado en AEWM, mejorado con soporte para escritorios virtailes y, parcialmente, GNOME
+Comment[et]=Vähenõudlik aknahaldur, mille aluseks on AEWM ja mida on täiendatud virtuaalsete töölaudade ning osalise GNOME toetusega
+Comment[eu]=AEWMn oinarritutako leiho kudeatzaile minimoa, mahaigain birtualen euskarriaz eta, zati batez, GNOMEz hobetua
+Comment[fa]= مدیر پنجرۀ کمینه بر اساس AEWM، گسترش‌یافته توسط رومیزیهای مجازی و پشتیبانی جزئی GNOME
+Comment[fi]=Minimaalinen AEWM:ään pohjautuva ikkunaohjelma, jota on parannettuna virtuaalityöpöydillä ja osittaisella GNOME-tuella
+Comment[fr]=Un gestionnaire de fenêtres minimal fondé sur AEWM avec, en plus, la gestion des bureaux virtuels ainsi qu'un support partiel de GNOME
+Comment[fy]=In minimalistyske finstersmanager basearre op AEWM, útbreide mei firtuele buroblêden en gedieltelike GNOME-stipe
+Comment[gl]=Un xestor de fiestras mínimo baseado en AEWM, mellorado cos escritórios virtuais e con soporte parcial para GNOME
+Comment[he]=מנהל חלונות מינימלי המבוסס על AEWM, המשופר על ידי שולחנות עבודה וירטואליים ותמיכה חלקית ב GNOME
+Comment[hi]= एईडबल्यूएम आधारित अल्प विंडो प्रबंधक, आभासी डेस्कटॉप तथा आंशिक ग्नोम समर्थन से बेहतर बनाया गया
+Comment[hr]=Minimalistički upravitelj prozora zasnovan na AEWM, unaprijeđen virtualnim radnim površinama i djelomičnom podrškom za GNOME
+Comment[hu]=Egy nagyon egyszerű ablakkezelő az AEWM alapján, virtuális munkaasztalokkal és részleges GNOME-támogatással kiegészítve
+Comment[is]=Einfaldur gluggastjóri byggður á AEWM en með stuðningi fyrir sýndarskjáborð og takmörkuðum GNOME stuðningi
+Comment[it]= Un window manager minimale basato su AEWM, migliorato con desktop virtuali e supporto parziale per GNOME
+Comment[ja]=仮想デスクトップと部分的な GNOME サポートを強化した AEWM ベースの小さなウィンドウマネージャ
+Comment[ka]=მინიმალური ფანჯრის მენეჯერი AEWM -ის ბაზაზე, имеющий частичную поддержку GNOME.
+Comment[kk]=Виртуалды үстелдері және шамалы GNOME қолдауы бар AEWM-негіздеген шағын терезе менеджері
+Comment[km]=កម្មវិធី​គ្រប់គ្រង​បង្អួច​តូច ដែល​ផ្អែក​លើ AEWM ដែល​ធ្វើ​ឲ្យ​ប្រសើរ​ដោយ​ផ្ទៃតុ​និមិត្ត និង​ការ​គាំទ្រ GNOME
+Comment[ko]=부분적 그놈 지원과 가상 데스크톱 지원을 사용하는 AEWM 기반 창 관리자
+Comment[lt]=Minimalistinė langų tvarkyklė paremta AEWM, išplėsta virtualių darbastalių ir daliniu GNOME palaikymu
+Comment[lv]=Minimālistisks logu menedžeris bāzēts uz AEWM, papildināts ar virtuālajām darbvirsmām un daļēju GNOME atbalstu
+Comment[mk]=Минимален менаџер на прозорци базиран на AEWM, подобрен со виртуелни површини и парцијална GNOME поддршка
+Comment[ms]=Pengurus tetingkap minima berdasarkan AEWM, dipertingkatkan dengan desktop visual dan sokongan GNOME separa
+Comment[mt]=Window manager żgħir ibbażat fuq AEWM, filmkien ma' desktops virtwali u sapport parzjali għal GNOME.
+Comment[nb]=En minimal vindusbehandler basert på AEWM, forbedret med virtuelle skrivebord og delvis støtte for GNOME
+Comment[nds]=En minimaal Finsterpleger, opbuut op AEWM, verwiedert üm virtuelle Schriefdischen un deelwies Ünnerstütten för GNOME
+Comment[ne]=अवास्तविक डेस्कटप र आंशिक GNOME समर्थनद्वारा बृद्धि गरिएको AEWM मा आधारित न्यूनतम सञ्झ्याल प्रबन्धक
+Comment[nl]=Een minimalistische windowmanager gebaseerd op AEWM, uitgebreid met virtuele bureaubladen en gedeeltelijke GNOME-ondersteuning
+Comment[nn]=Ein minimal vindaugssjef basert på AEWM, forbetra med virtuelle skrivebord og delvis støtte for GNOME
+Comment[pa]= AEWM ਤੇ ਅਧਾਰਿਤ ਛੋਟਾ ਝਰੋਖਾ ਮੈਨੇਜਰ, ਫਰਜ਼ੀ ਵੇਹੜਿਆਂ ਨਾਲ ਲੈੱਸ ਤੇ ਥੋੜਾ GNOME ਸਹਾਇਕ
+Comment[pl]=Prosty menedżer okien na bazie AEWM, wzbogacony o wirtualne pulpity i częściowe wsparcie dla GNOME
+Comment[pt]=Um gestor de janelas simples baseado no AEWM, melhorado com os ecrãs virtuais e com um suporte parcial do GNOME
+Comment[pt_BR]=Um gerenciador de janelas pequeno, baseado no AEWM, melhorado pelas áreas de trabalho virtuais e com suporte parcial ao GNOME
+Comment[ro]=Un manager de ferestre minimal bazat pe AEWM, îmbunătățit cu ecrane virtuale și suport parțial pentru GNOME
+Comment[ru]=Минимальный оконный менеджер на основе AEWM, имеющий частичную поддержку GNOME.
+Comment[rw]=Mugenga dirishya nto ishingiye kuri AEWM, rivuguruwe n'ibiro bitagaragara n'iyifashisha GNOME rituzuye
+Comment[se]=Geahpes lásegieđahalli vuođđoduvvon AEWM:as, mas leat virtuella čállinbeavddit ja doarju GNOME muhton muddui.
+Comment[sk]=Minimálny správca okien založený na AEWM, rozšírrený o virtuálne plochy a čiastočnú podporu GNOME
+Comment[sl]=Skromen okenski upavitelj na osnovi AEWM, izboljšan z navideznimi namizji in delno podporo GNOME
+Comment[sr]=Минимални менаџер прозора заснован на AEWM-у, побољшан виртуелним радним површинама и делимичном подршком за Гном
+Comment[sr@Latn]=Minimalni menadžer prozora zasnovan na AEWM-u, poboljšan virtuelnim radnim površinama i delimičnom podrškom za Gnom
+Comment[sv]=Minimal fönsterhanterare baserad på AEWM, utökad med virtuella skrivbord och delvis stöd för Gnome
+Comment[ta]=AEWM அடிப்படையிலான சிறிய சாளர மேலாண்மை, மெய்நிகர் மேல்மேசை மற்றும் பகுதி GNOME ஆதரவால் மேப்படுத்தப்பட்டுள்ளது
+Comment[tg]=Мудири равзанаҳои хурд дар асоси AEWM дорои нопурраи интерфейси GNOME
+Comment[th]=ตัวจัดการหน้าต่างขนาดเล็ก โดยใช้พื้นฐานของ AEWM แล้วเพิ่มความสามารถด้วย พื้นที่ทำงานเสมือน และสนับสนุน GNOME บางส่วน
+Comment[tr]=AEWM tabanlı bir pencere yöneticisi
+Comment[tt]=AEWM asılında yasalğan, xıyalí öställär belän beraz GNOME totqan ciñel täräzä-idäräçe
+Comment[uk]=Мінімальний менеджер вікон, заснований на AEWM, покращений підтримкою віртуальних стільниць та частковою підтримкою GNOME
+Comment[vi]=Bộ quản lý cửa sổ tối thiểu dựa trên AEWM, cải tiến với màn hình nền ảo và được hỗ trợ một phần bởi GNOME
+Comment[wa]=On manaedjeu di purneas minimå båzé so AEWM, avou sopoirt po les forveyous scribannes eyet ene miete di sopoirt po Gnome
+Comment[zh_CN]=基于 AEWM 的小型窗口管理器,增强了虚拟桌面和部分 GNOME 支持
+Comment[zh_TW]=基於 AEWM 的小型視窗管理程式,增強了虛擬桌面及部分的 GNOME 支援
diff --git a/kdm/kfrontend/sessions/aewm.desktop b/kdm/kfrontend/sessions/aewm.desktop
new file mode 100644
index 000000000..362fd921f
--- /dev/null
+++ b/kdm/kfrontend/sessions/aewm.desktop
@@ -0,0 +1,76 @@
+[Desktop Entry]
+Type=XSession
+Exec=aewm
+TryExec=aewm
+Name=AEWM
+Name[eo]=MFA
+Name[hi]=एईडबल्यूएम
+Name[te]=ఎ ఈ డబ్ల్యు ఎం
+Comment=A minimalist window manager
+Comment[af]='n Minimale venster bestuurder
+Comment[ar]=مسيير نوافذ ذو الميزات الأقل
+Comment[be]=Мінімалістычны кіраўнік вокнаў
+Comment[bn]=একটি পরিমিত উইণ্ডো ম্যানেজার
+Comment[bs]=Minimalistički window manager
+Comment[ca]=Un gestor de finestres minimalista
+Comment[cs]=Minimalistický správce oken
+Comment[csb]=Prosti menedżer òknów
+Comment[cy]=Trefnydd ffenestri lleiafol
+Comment[da]=En minimalistisk vindueshåndtering
+Comment[de]=Minimalistischer Fenstermanager
+Comment[el]=Ένας μινιμαλιστικός διαχειριστής παραθύρων
+Comment[eo]=Minimumema fenestroadministrilo
+Comment[es]=Un gestor de ventanas minimalista
+Comment[et]=Vähenõudlik aknahaldur
+Comment[eu]=Leiho kudeatzaile minimalista
+Comment[fa]=یک مدیر پنجرۀ کمینه
+Comment[fi]=Minimalistinen ikkunaohjelma
+Comment[fr]=Un gestionnaire de fenêtres minimaliste
+Comment[fy]=In minimalistyske finstersmanager
+Comment[gl]=Un xestor de fiestras minimalista
+Comment[he]=מנהל חלונות מינימליסטי
+Comment[hi]=एक अल्पतम विंडो प्रबंधक
+Comment[hr]=Minimalistički upravitelj prozora
+Comment[hu]=Egy nagyon egyszerű ablakkezelő
+Comment[is]=Einfaldur gluggastjóri
+Comment[it]=Un window manager minimalista
+Comment[ja]=小さなウィンドウマネージャ
+Comment[ka]=მინიმალისტური ფანჯრების მნეჯერი
+Comment[kk]=Шағын терезе менеджері
+Comment[km]=កម្មវិធី​គ្រប់គ្រង​បង្អួច​លក្ខណៈ​ពិសេស​តិច
+Comment[ko]=최소 지향 창 관리자
+Comment[lt]=Minimalistinė langų tvarkyklė
+Comment[lv]=Minimālistisks logu menedžeris
+Comment[mk]=Минималистички менаџер на прозорци
+Comment[mn]=Хамгийн жижиг цонхны удирдагч
+Comment[mt]=Window manager żgħir
+Comment[nb]=En minimalistisk vindusbehandler
+Comment[nds]=En minimalistisch Finsterpleger
+Comment[ne]=एक मिनिमलिस्ट सञ्झ्याल प्रबन्धक
+Comment[nl]=Een minimalistische windowmanager
+Comment[nn]=Ein minimalistisk vindaugssjef
+Comment[pa]=ਇੱਕ ਹਲਕਾ ਝਰੋਖਾ ਮੈਨੇਜਰ
+Comment[pl]=Prosty menedżer okien
+Comment[pt]=Um gestor de janelas minimalista
+Comment[pt_BR]=Um gerenciador de janelas minimalista
+Comment[ro]=Un manager de ferestre minimal
+Comment[ru]=Минимальный оконный менеджер
+Comment[rw]=Mugenga dirishya igira-bito
+Comment[se]=Minimalisttalaš lásegieđahalli
+Comment[sk]=Minimálny správca okien
+Comment[sl]=Minimalističen okenski upravitelj
+Comment[sr]=Минималистички менаџер прозора
+Comment[sr@Latn]=Minimalistički menadžer prozora
+Comment[sv]=Minimalistisk fönsterhanterare
+Comment[ta]=ஒரு சிறிதுப்படுத்தப்பட்ட சாளர மேலாளர்
+Comment[tg]=Мудири тирезаи minimalist
+Comment[th]=ระบบจัดการหน้าต่างขนาดเล็ก
+Comment[tr]=Küçük bir pencere yöneticisi
+Comment[tt]=Bik ciñel täräzä-idäräçe
+Comment[uk]=Аскетичний менеджер вікон
+Comment[uz]=Juda oddiy oyna boshqaruvchi
+Comment[uz@cyrillic]=Жуда оддий ойна бошқарувчи
+Comment[vi]=Trình quản lý cửa sổ đơn giản
+Comment[wa]=On manaedjeu di purneas minimå
+Comment[zh_CN]=最小的窗口管理器
+Comment[zh_TW]=一個最小型的視窗管理程式
diff --git a/kdm/kfrontend/sessions/afterstep.desktop b/kdm/kfrontend/sessions/afterstep.desktop
new file mode 100644
index 000000000..c3f8d7329
--- /dev/null
+++ b/kdm/kfrontend/sessions/afterstep.desktop
@@ -0,0 +1,83 @@
+[Desktop Entry]
+Type=XSession
+Exec=afterstep
+TryExec=afterstep
+Name=AfterStep
+Name[bn]=আফটার-স্টেপ
+Name[eo]=Postpaŝo
+Name[hi]=आफ्टर-स्टेप
+Name[ne]=चरण पछाडि
+Name[pa]=ਪਗਬਾਅਦ
+Name[rw]=NyumaIntambwe
+Name[sv]=Afterstep
+Name[ta]=ஆஃப்டர்ஸ்டெப்
+Name[te]=ఆఫ్టర్ స్టెప్
+Comment=A window manager with the NeXTStep look and feel, based on FVWM
+Comment[af]='n Venster bestuurder wat NeXTStep naboots, gebaseer of FVWM
+Comment[ar]=مدير نوافذ ذو مظهر شبيه بـNeXTStep، مبني على FVWM
+Comment[be]=Кіраўнік вокнаў з вонкавым выглядам NeXTStep, заснаваны на FVWM
+Comment[bn]=FVWM-এর ওপর ভিত্তি করে তৈরি একটি উইণ্ডো ম্যানেজার, যা দেখতে শুনতে অনেকটানেক্সট-স্টেপ (NeXTStep)-এর মত
+Comment[bs]=Window manager sa NeXTStep izgledom i osjećajem, baziran na FVWM
+Comment[ca]=Un gestor de finestres amb l'aspecte i comportament de NeXTStep, basat en FVWM
+Comment[cs]=Správce oken podobný NeXTStepu založený na FVWM
+Comment[csb]=Menedżer òknów jidący w szlach NeXTStep, ùsôdzony na spòdlém FVWM
+Comment[cy]= Trefnydd ffenestri efo golwg a theimlad CamNesaf (NeXTStep), wedi'i seilio ar FVWM
+Comment[da]=En vindueshåndtering med NeXTStep udseende, baseret på FVWM
+Comment[de]=Fenstermanager mit der Optik von NeXTStep, basiert auf FVWM
+Comment[el]=Ένας διαχειριστής παραθύρων με την όψη και αίσθηση του NeXTStep, βασισμένος στον FVWM
+Comment[eo]=Fenestroadministrilo, kiu aperas kiel Venontpaŝo, farita de FVWM
+Comment[es]=Un gestor de ventanas con el aspecto de NeXTStep, basado en FVWM
+Comment[et]=Aknahaldur NeXTStep välimuse ja vaimuga, aluseks FVWM
+Comment[eu]=FVWMn oinarritutako, eta NeXTStep-en itxura eta portaera duen leiho kudeatzailea
+Comment[fa]=یک مدیر پنجره توسط ظاهر و احساس NeXTStep، براساس FVWM
+Comment[fi]=NeXTStep-tyylinen ja -tuntuinen FVWM:ään pohjautuva ikkunaohjelma
+Comment[fr]=Un gestionnaire de fenêtres similaire à NeXTStep et fondé sur FVWM
+Comment[fy]=In finstersmanager mei it úterlik en gedrach fan NeXTStep; basearre op FVWM
+Comment[gl]=Un xestor de fiestras de aspeito NeXTStep, baseado en FVWM
+Comment[he]=מנהל חלונות עם מראה ומרגש כמו של NeXTStep המבוסס על, FVWM
+Comment[hi]=एफ़वीडबल्यूएम आधारित नेक्स्टस्टेप की तरह दिखने और महसूस होने वाला विंडो प्रबंधक
+Comment[hr]=Upravitelj prozora s NeXTStep izgledom i načinom rada, zasnovan na FVWM-u
+Comment[hu]=Egy FVWM-alapú ablakkezelő, megjelenése a NeXTStepére hasonlít
+Comment[is]=Gluggastjóri sem líkist þeim sem er í NeXTStep, byggður á FVWM
+Comment[it]=Un window manager con lo stile NeXTStep, basato su FVWM
+Comment[ja]=NeXTStep のルック&フィールをもった FVWM ベースのウィンドウマネージャ
+Comment[ka]=ფანჯრის მენეჯერი FVWM -ის ბაზაზე, რომელიც NeXTStep-ს ჰგავს
+Comment[kk]=FVWM-негіздеген, көрінісі NeXTStep-те сияқты, терезе менеджері
+Comment[km]=កម្មវិធី​គ្រប់គ្រង​បង្អួច​មាន​រូបរាង និង​មុខងារ NeXTStep ដែល​ផ្អែក​លើ FVWM
+Comment[ko]=FVWM 기반 NeXTStep 모양 창 관리자
+Comment[lt]=Langų tvarkyklė su NeXTStep išvaizda ir jausena, paremta FVWM
+Comment[lv]=Logu menedžeris ar NeXTStep izskatu un izturēšanos, bāzēts uz FVWM
+Comment[mk]=Менаџер на прозорци со изгледот и чувството на NeXTStep, базиран на FVWM
+Comment[mn]=NeXTStep look болон feel, based бүхий FVWM-д суурилсан цонх удирдагч
+Comment[ms]=Pengurus tetingkap dengan rupa dan rasa NeXTStep berdasarkan FVWM
+Comment[mt]=Window manager ibbażat fuq FVWM, jixbaħ lil NeXTStep
+Comment[nb]=En vindusbehandler som ligner på NeXTStep, basert på FVWM
+Comment[nds]=En Finsterpleger mit dat Utsehn vun NeXTStep, opbuut op FVWM
+Comment[ne]=FVWMA आधारित एउटा सञ्झ्याल प्रबन्धक NeXTStep मा हेर्छ र थाहा पाउँछ
+Comment[nl]=Een windowmanager met het uiterlijk en gedrag van NeXTStep; gebaseerd op FVWM
+Comment[nn]=Ein vindaugssjef som liknar på NeXTStep, basert på FVWM
+Comment[pa]=NeXTStep ਦੀ ਦਿੱਖ ਤੇ ਦਰਿਸ਼ ਵਾਲਾ FVWM ਆਧਾਰਿਤ ਝਰੋਖਾ ਮੈਨੇਜਰ
+Comment[pl]=Menedżer okien naśladujący NeXTStep, stworzony na podstawie FVWM
+Comment[pt]=Um gestor de janelas com a aparência e comportamento do NeXTStep. Baseado no FVWM.
+Comment[pt_BR]=Um gerenciador de janelas com a aparência do NeXTSep, baseado no FVWM
+Comment[ro]=Un manager de ferestre cu aspect NeXTStep, bazat pe FVWM
+Comment[ru]=Оконный менеджер на основе FVWM, повторяющий внешний вид NeXTStep
+Comment[rw]=Mugenga dirishya ifite imboneko n'ukumva NtambweIkurikira, ishingiye kuri FVWM
+Comment[se]=Lásegieđahalli mii sulastahttá NeXTStep, vuođđoduvvon FVWM:as
+Comment[sk]=Správca okien podobný NeXTStep založený na FVWM
+Comment[sl]=Okenski upravitelj z občutkom in izgledom NeXTStep-a, na osnovi FVWM
+Comment[sr]=Менаџер прозора са NeXTStep-овим изгледом и осећајем, заснован на FVWM-у
+Comment[sr@Latn]=Menadžer prozora sa NeXTStep-ovim izgledom i osećajem, zasnovan na FVWM-u
+Comment[sv]=Fönsterhanterare med Nextstep-utseende och -känsla, baserad på FVWM
+Comment[ta]=FVWM அடிப்படையிலான NeXTStep உடனான சாளர மேலாளர்.
+Comment[tg]=Мудири равзанаҳо дар асоси FVWM дорои намуди NeXTStep
+Comment[th]=ระบบจัดการหน้าต่างที่มีรูปแบบและสัมผัสของระบบปฏิบัติการ NeXTStep โดยใช้พื้นฐานของ FVWM
+Comment[tr]=NeXTStep görünümlü bir pencere yöneticisi
+Comment[tt]=FVWM asılında qorılğan, NeXTStep küreneşe belän täräzä-idäräçe
+Comment[uk]=Менеджер вікон з виглядом та поведінкою NeXTStep, заснований на FVWM
+Comment[uz]=FVWM asosida yaratilgan NeXTStep'ga oʻxshash oyna boshqaruvchi
+Comment[uz@cyrillic]=FVWM асосида яратилган NeXTStep'га ўхшаш ойна бошқарувчи
+Comment[vi]=Trình quản lý cửa sổ với giao diện NeXTStep, dựa trên FVWM
+Comment[wa]=On manaedjeu di purneas avou l' rivnance di NeXTStep, båzé so FVWM
+Comment[zh_CN]=一个带有 NeXTStep 观感的窗口管理器,基于 FVWM
+Comment[zh_TW]=一個基於 FVWM 並擁有 NeXTStep 外觀及感覺的視窗管理程式
diff --git a/kdm/kfrontend/sessions/amaterus.desktop b/kdm/kfrontend/sessions/amaterus.desktop
new file mode 100644
index 000000000..3c52ee180
--- /dev/null
+++ b/kdm/kfrontend/sessions/amaterus.desktop
@@ -0,0 +1,75 @@
+[Desktop Entry]
+Type=XSession
+Exec=amaterus
+TryExec=amaterus
+Name=AMATERUS
+Name[hi]=एमेच्योर्स
+Name[te]=అమాటెరస్
+Comment=A GTK+ based window manager with a window grouping feature
+Comment[af]='n GTK+ gebaseerde venster bestuurder met 'n venster groepering funksie
+Comment[ar]=مدير نوافذ مبني على GTK+ له ميزة تجميع النوافذ
+Comment[be]=Кіраўнік вокнаў, заснаваны на GTK+, са здольнасцю групавання вокнаў
+Comment[bn]=GTK+ ভিত্তিল উইণ্ডো ম্যানেজার, যাতে উইণ্ডো গ্রুপিং করা যায়
+Comment[bs]=Window manager baziran na GTK+ sa mogućnošću grupisanja prozora
+Comment[ca]=Un gestor de finestres de GTK+ amb una característica per a l'agrupament de finestres
+Comment[cs]=Správce oken založený na GTK+ s funkcí seskupování okne
+Comment[csb]=Menedżer òknów brëkùjący GTK+, z optacëją grëpòwaniô òknów
+Comment[cy]=Trefnydd ffenestri wedi'i seilio ar GTK+, efo nodwedd casglu ffenestri
+Comment[da]=En GTK+ baseret vindueshåndtering med en vinduesgrupperingsegenskab
+Comment[de]=Auf GTK+ basierender Fenstermanager mit Gruppierungsfunktion für die Fenster
+Comment[el]=Ένας διαχειριστής παραθύρων βασισμένος στο GTK+ με ένα χαρακτηριστικό ομαδοποίησης παραθύρων
+Comment[eo]=Fenestroadministrilo kun ebleco kunigi fenestrojn, kiu uzas GTK+
+Comment[es]=Un gestor de ventanas basado en GTK+ con la posibilidad de agrupar ventanas
+Comment[et]=GTK+-le tuginev aknahaldur akende grupeerimise võimalusega
+Comment[eu]=GTK+-n oinarritutako leiho kudeatzailea, leihoak taldeka biltzeko gaitasuna duena
+Comment[fa]=یک GTK+ بر مبنای مدیر پنجره توسط ویژگی گروهی کردن پنجره‌ها
+Comment[fi]=GTK+-pohjainen ikkunaohjelma ikkunoiden ryhmittely -ominaisuudella
+Comment[fr]=Un gestionnaire de fenêtres écrit en GTK+, avec une fonctionnalité de groupement des fenêtres
+Comment[fy]=In op GTK+ basearre finstersmanager mei finsterkeppelfûnksje
+Comment[gl]=Un xestor de fiestras baseado en GTK+ con agrupamento de fiestras
+Comment[he]=מנהל חלונות מבוסס GTK+ עם אפשרות לקבץ חלונות
+Comment[hi]=जीटीके+ आधारित विंडो प्रबंधक, विंडो समूह विशेषता सहित
+Comment[hr]=Upravitelj prozora zasnovan na GTK+ s mogućnošću grupiranja prozora
+Comment[hu]=Egy GTK+-alapú ablakkezelő ablakcsoportosítási lehetőséggel
+Comment[is]=Gluggastjóri sem hópar saman glugga og er byggður á GTK+
+Comment[it]=Un window manager basato su GTK+ con la possibilità di raggruppare le finestre
+Comment[ja]=ウィンドウのグループ化が可能な GTK+ ベースのウィンドウマネージャ
+Comment[ka]=ფანჯრის მენეჯერი GTK+ ს ბაზაზე ფანჯრების დაჯგუფების ფუნქციით
+Comment[kk]=GTK+ негіздеген, терезелерді топтастыру қасиеті бар, терезе менеджері
+Comment[km]=កម្មវិធី​គ្រប់គ្រង​បង្អួច​ផ្អែក​លើ GTK+ ដែល​មាន​លក្ខណៈ​ពិសេស​ដាក់​បង្អួច​ជា​ក្រុម
+Comment[ko]=창 그룹 기능을 사용하는 GTK+ 기반 창 관리자
+Comment[lt]=GTK+ paremta langų tvarkyklė su langų grupavimo galimybe
+Comment[lv]=GTK+ bāzēts logu menedžeris ar logu grupēšanas iespēju
+Comment[mk]=GTK+ базиран менаџер на прозорци со можност за групирање на прозорци
+Comment[mn]=A GTK+ суурилсан цонх бүлэглэгчтэй цонх удирдагч
+Comment[mt]=Window manager ibbażat fuq GTK+ b'faċilitajiet ta' gruppi ta' windows
+Comment[nb]=En vindusbehandler basert på GTK+ med vindusgruppering
+Comment[nds]=En Finsterpleger opbuut op GTK+, kann Finstern in Koppeln tosamenfaten
+Comment[ne]=सञ्झ्याल समूह विशेषतासँग GTK+ मा आधारित सञ्झ्याल प्रबन्धक
+Comment[nl]=Een op GTK+ gebaseerde windowmanager met venstergroeperingfunctionaliteit
+Comment[nn]=Ein GTK+-basert vindaugssjef med vindaugsgruppering
+Comment[pa]=ਇੱਕ GTK+ ਤੇ ਆਧਾਰਿਤ ਝਰੋਖਾ ਮੈਨੇਜਰ, ਜੋ ਕਿ ਝਰੋਖਿਆਂ ਨੂੰ ਇੱਕ ਥਾਂ ਸੰਭਾਲਣ ਦੀ ਸਹੂਲਤ ਨਾਲ ਲੈੱਸ ਹੈ।
+Comment[pl]=Menedżer okien korzystający z GTK+, z opcją grupowania okien
+Comment[pt]=Um gestor de janelas baseado em GTK+ com uma funcionalidade de agrupamento de janelas
+Comment[pt_BR]=Um gerenciador de janelas baseado em GTK+, com o recurso de agrupamento de janelas
+Comment[ro]=Un manager de ferestre bazat pe GTK+ și cu posibilitatea de grupare a ferestrelor
+Comment[ru]=Оконный менеджер на основе GTK+ c функцией группировки окон
+Comment[rw]=GTK + bishingiye mugenga dirishya ifite idirishya rihuza ibiranga
+Comment[se]=GTK+-vuođđoduvvon lásegieđahalli mii sáhttá sierra lásiid bidjat seamma joavkui
+Comment[sk]=Správca okien založený na GTK+ s funkciou zoskupovania okien
+Comment[sl]=Okenski upravitelj na osnovi GTK+ z možnostjo združevanja oken
+Comment[sr]=Менаџер прозора заснован на GTK+-у са особином груписања прозора
+Comment[sr@Latn]=Menadžer prozora zasnovan na GTK+-u sa osobinom grupisanja prozora
+Comment[sv]=GTK+-baserad fönsterhanterare med en funktion för fönstergruppering
+Comment[ta]=சாளர குழுப்பிரித்தல் தன்மையுடனான சாளர மேலாளர் அடிப்படையிலான GTK+.
+Comment[tg]=Мудири равзанаҳо дар асоси GTK+ дорои фаъолияти гурӯҳ кардан равзанаҳо
+Comment[th]=ตัวจัดการหน้าต่างที่ใช้พื้นฐานจาก GTK+ พร้อมด้วยความสามารถในการจัดกลุ่มหน้าต่าง
+Comment[tr]=GTK+ tabanlı bir pencere yöneticisi
+Comment[tt]=GTK+ asılında qorılğan, täräzälärne törkemli ala torğan täräzä-idäräçe
+Comment[uk]=Менеджер вікон заснований на GTK+ з підтримкою групування вікон
+Comment[uz]=A GTK+ asosida yaratilgan, oynalarni guruhlash imkoniyatiga ega oyna boshqaruvchi
+Comment[uz@cyrillic]=A GTK+ асосида яратилган, ойналарни гуруҳлаш имкониятига эга ойна бошқарувчи
+Comment[vi]=Trình quản lý cửa sổ với khả năng tạo nhóm cửa sổ dựa trên GTK+
+Comment[wa]=On manaedjeu di purneas båzé so GTK+, avou ene fonccionålité di rgroupaedje des purneas
+Comment[zh_CN]=一个基于 GTK+ 的窗口管理器,带有窗口成组特性
+Comment[zh_TW]=一個基於 GTK+ 的視窗管理程式並擁有視窗群組功能
diff --git a/kdm/kfrontend/sessions/amiwm.desktop b/kdm/kfrontend/sessions/amiwm.desktop
new file mode 100644
index 000000000..ced73c346
--- /dev/null
+++ b/kdm/kfrontend/sessions/amiwm.desktop
@@ -0,0 +1,78 @@
+[Desktop Entry]
+Type=XSession
+Exec=amiwm
+TryExec=amiwm
+Name=AmiWM
+Name[eo]=AmiFA
+Name[hi]=एमी-डबल्यूएम
+Name[sv]=Ami WM
+Name[te]=ఎమి డబ్ల్యు ఎం
+Comment=The Amiga look-alike window manager
+Comment[af]='n Amiga gebaseerde venster bestuurder
+Comment[ar]=مدبِر نوافذ مشابه لِــ Amiga
+Comment[be]=Кіраўнік вокнаў, падобны на Amiga
+Comment[bn]=অ্যামিগার মত দেখতে উইণ্ডো ম্যানেজার
+Comment[bs]=Window manager nalik na Amigu
+Comment[ca]=Un gestor de finestres que dona l'aspecte d'un Amiga
+Comment[cs]=Správce oken podobný Amize
+Comment[csb]=Menedżer òknów szlachùjący za Amiga
+Comment[cy]=Trefnydd ffenestri sy'n edrych yn debyg i'r Amiga
+Comment[da]=Amiga-lignende vindueshåndtering
+Comment[de]=Fenstermanager im Stil des Amiga
+Comment[el]=Ο διαχειριστής παραθύρων με όψη αλά Amiga
+Comment[eo]=Fenestroadministrilo kiel tiu de Amiga
+Comment[es]=Un gestor de ventanas con el aspecto de Amiga
+Comment[et]=Amiga välimusega aknahaldur
+Comment[eu]=Amigaren itxura duen leiho kudeatzailea
+Comment[fa]=Amiga شبیه مدیر پنجره
+Comment[fi]=Amigan tyylinen ikkunaohjelma
+Comment[fr]=Le gestionnaire de fenêtres ressemblant à Amiga
+Comment[fy]=In Amiga-likens finstersmanager
+Comment[gl]=Un xestor de fiestras coma o de Amiga
+Comment[he]=מנהל החלונות הדומה ל־Amiga
+Comment[hi]=अमीगा की तरह दिखने वाला विंडो प्रबंधक
+Comment[hr]=Upravitelj prozora koji podsjeća na Amigu
+Comment[hu]=Egy Amiga-szerű ablakkezelő
+Comment[is]=Gluggastjóri sem líkist Amiga tölvunum
+Comment[it]=Un window manager in stile Amiga
+Comment[ja]=Amiga に似たウィンドウマネージャ
+Comment[ka]=ფანჯრის მენეჯერი Amiga-ს სტილში
+Comment[kk]=Amiga секілді терезе менеджері
+Comment[km]=កម្មវិធី​គ្រប់គ្រប់​បង្អួច​ស្រដៀង Amiga
+Comment[ko]=Amiga를 닮은 창 관리자
+Comment[lt]=Langų tvarkyklė, panaši į Amiga
+Comment[lv]=Amiga izskata logu menedžeris
+Comment[mk]=Менаџер на прозорци со изглед на Amiga
+Comment[mn]=Amiga look-alike Цонхны удирдагч
+Comment[ms]=Pengurus tetingkap serupa Amiga
+Comment[mt]=Window manager jixbaħ lill-Amiga
+Comment[nb]=Vindusbehandler som ligner på Amiga
+Comment[nds]=De Finsterpleger mit dat Utsehn vun den Amiga
+Comment[ne]=अमिगा सञ्झ्याल प्रबन्धक जस्तो छ
+Comment[nl]=Een Amiga-achtige windowmanager
+Comment[nn]=Vindaugssjef som liknar på Amiga
+Comment[pa]=ਅਮੀਗਾ ਵਰਗਾ ਝਰੋਖਾ ਮੈਨੇਜਰ
+Comment[pl]=Menedżer okien naśladujący Amigę
+Comment[pt]=O gestor de janelas com o visual do Amiga
+Comment[pt_BR]=Um gerenciador de janelas com a aparência do Amiga
+Comment[ro]=Manager de ferestre cu aspect Amiga
+Comment[ru]=Оконный менеджер в стиле Amiga
+Comment[rw]=Amiga ijya gusa na mugenga dirishya
+Comment[se]=Amiga-lágan lásegieđahalli
+Comment[sk]=Správca okien podobný systému Amiga
+Comment[sl]=Okenski upravitelj, podoben Amiginemu
+Comment[sr]=Менаџер прозора који подсећа на Амигу
+Comment[sr@Latn]=Menadžer prozora koji podseća na Amigu
+Comment[sv]=Fönsterhanteraren som ser ut som Amiga
+Comment[ta]=சாளர மேலாளரை ஒத்த அமிகா
+Comment[tg]=Монанди мудири тирезаи Amiga look
+Comment[th]=ระบบจัดการหน้าต่างที่ดูเหมือน Amiga
+Comment[tr]=Amiga görünümlü bir pencere yöneticisi
+Comment[tt]=Amiga küreneşendä täräzä idäräçe
+Comment[uk]=Менеджер вікон на штиб Amiga
+Comment[uz]=Amiga'ga oʻxshash oyna boshqaruvchi
+Comment[uz@cyrillic]=Amiga'га ўхшаш ойна бошқарувчи
+Comment[vi]=Trình quản lý cửa sổ nhìn giống Amiga
+Comment[wa]=On manaedjeu di purneas rishonnant l' ci di l' Amiga
+Comment[zh_CN]=Amiga 外观的窗口管理器
+Comment[zh_TW]=一個看起來像 Amiga 視窗管理程式
diff --git a/kdm/kfrontend/sessions/asclassic.desktop b/kdm/kfrontend/sessions/asclassic.desktop
new file mode 100644
index 000000000..e84a0977e
--- /dev/null
+++ b/kdm/kfrontend/sessions/asclassic.desktop
@@ -0,0 +1,81 @@
+[Desktop Entry]
+Type=XSession
+Exec=asclassic
+TryExec=asclassic
+Name=ASClassic
+Name[af]= ASClassic
+Name[cy]=ASClasurol
+Name[eo]=KlasikaPP
+Name[hi]=एएस-क्लॉसिक
+Name[ne]=AS शास्त्रीय
+Name[sv]=AS klassisk
+Name[ta]=ASதரமான
+Name[te]=ఏఎస్ క్లాసిక్
+Comment=AfterStep Classic, a window manager based on AfterStep v1.1
+Comment[af]=AfterStep Classic, 'n venster bestuurder wat op AfterStep v1.1 gebaseer is.
+Comment[ar]=AfterStep كلاسيك، وهو مدير نوافذ مبني على AfterStep الإصدارة 1.1
+Comment[be]=Класічны AfterStep, кіраўнік вокнаў, заснаваны на AfterStep 1.1
+Comment[bn]=আফটার-স্টেপ ক্লাসিক: আফটার-স্টেপ ১.১ ভিত্তিক একটি উইণ্ডো ম্যানেজার
+Comment[bs]=AfterStep Classic, window manager baziran na AfterStep v1.1
+Comment[ca]=El clàssic AfterStep, un gestor de finestres basat en AfterStep v1.1
+Comment[cs]=AfterStep Classic, správce oken založený na AfterStepu v1.1
+Comment[csb]=AfterStep Classic, menedżer òknów ùsôdzony na spòdlém AfterStep v1.1
+Comment[cy]=ÔlGam Clasurol, trefnydd ffenestri wedi'i seilio ar AfterStep v1.1
+Comment[da]=AfterStep Classic, en vindueshåndtering baseret på AfterStep v1.1
+Comment[de]=AfterStep Classic, ein Fenstermanager, der auf AfterStep v1.1 basiert
+Comment[el]=AfterStep κλασικός, ένας διαχειριστής παραθύρων βασισμένος στον AfterStep v1.1
+Comment[eo]=Klasika Postpaŝo, fenestroadministrilo kiel Postpaŝo v1.1
+Comment[es]=AfterStep Classic, un gestor de ventanas basado en AfterStep v1.1
+Comment[et]=AfterStep Classic - aknahaldur, mille aluseks on AfterStep v1.1
+Comment[eu]=AfterStep Classic, AfterStep v1.1-en oinarrituta dagoen leiho kudeatzailea
+Comment[fa]=AfterStep کلاسیک، مدیر پنجره بر اساس AfterStep نسخه ۱.۱
+Comment[fi]=AfterStep Classic, After Step v1.1:een pohjautuva ikkunaohjelma
+Comment[fr]=AfterStep Classic, un gestionnaire de fenêtres fondé sur AfterStep v1.1
+Comment[fy]=AfterStep Classic, In finstersmanager basearre op AfterStep 1.1
+Comment[gl]=AfterStep Clásico, un xestor de fiestras baseado en AfterSetp v1.1
+Comment[he]=AfterStep Classic, מנהל חלונות המבוסס על AfterStep v1.1
+Comment[hi]=आफ्टरस्टेप क्लासिक, एक विंडो प्रबंधक जो आफ्टरस्टेप व.1 पर आधारित है
+Comment[hr]=Klasični AfterStep, upravitelj prozora zasnovan na AfterStepu verzija 1.1
+Comment[hu]=AfterStep Classic ablakkezelő, az AfterStep v1.1 alapján
+Comment[is]=Klassískur AfterStep gluggastjóri byggður á AfterStep v1.1
+Comment[it]=AfterStep Classico, un window manager basato su AfterStep v1.1
+Comment[ja]=AfterStep クラシック, AfterStep v1.1 ベースのウィンドウマネージャ
+Comment[ka]=AfterStep Classic, ფანჯრის მენეჯერი AfterStep v1.1 -ის ბაზაზე
+Comment[kk]=AfterStep v1.1 негіздеген AfterStep Classic терезе менеджері
+Comment[km]=AfterStep បុរាណ,កម្មវិធី​គ្រប់គ្រង​បង្អួច​ដែល​ផ្អែក​លើ AfterStep v1.1
+Comment[ko]=AfterStrp 1.1 기반 창 관리자
+Comment[lt]=AfterStep Classic, langų tvarkyklė, paremta AfterStep v1.1
+Comment[lv]=Klasiskais Afterstep, logu menedžeris bāzēts uz AfterStem v1.1
+Comment[mk]=AfterStep Classic, менаџер на прозорци базиран на AfterStep v1.1
+Comment[mn]=AfterStep Classic, AfterStep v1.1 дээр суурилсан цонх удирдагч
+Comment[mt]=AfterStep klassiku, window manager ibbażat fuq AfterStep v1.1
+Comment[nb]=AfterStep Classic, en vindusbehandler basert på AfterStep v1.1
+Comment[nds]=De AfterStepClassic-Finsterpleger is opbuut op AfterStep v1.1
+Comment[ne]=AfterStep शास्त्रीय, AfterStep v१.१ मा आधारित सञ्झ्याल प्रबन्धक
+Comment[nl]=AfterStep Classic, een windowmanager gebaseerd op AfterStep 1.1
+Comment[nn]=AfterStep Classic, ein vindaugssjef som byggjer på AfterStep 1.1
+Comment[pa]=AfterStep ਟਕਸਾਲੀ, AfterStep v1.1 ਤੇ ਆਧਾਰਿਤ ਝਰੋਖਾ ਮੈਨੇਜਰ
+Comment[pl]=AfterStep Classic, menedżer okien stworzony na podstawie AfterStep v1.1
+Comment[pt]=AfterStep Classic, um gestor de janelas baseado no AfterStep v1.1
+Comment[pt_BR]=AfterSep clássico, um gerenciador de janelas baseado no AfterStep v1.1
+Comment[ro]=AfterStep Classic, un manager de ferestre bazat pe AfterStep v1.1
+Comment[ru]=AfterStep Classic, оконный менеджер на основе AfterStep v1.1
+Comment[rw]=AfterStep Classic, mugenga dirishya ishingiye kuri AfterStep v1.1
+Comment[se]=AfterStep Classic, lásegieđahalli ráhkaduvvon AfterStep 1.1 vuođul
+Comment[sk]=AfterStep Classic, správca okien založený na AfterStep v1.1
+Comment[sl]=AfterStep Classic, okenski upravitelj na osnovi AfterStep različice 1.1
+Comment[sr]=Класични AfterStep, менаџер прозора заснован на AfterStep-у верзије 1.1
+Comment[sr@Latn]=Klasični AfterStep, menadžer prozora zasnovan na AfterStep-u verzije 1.1
+Comment[sv]=Afterstep klassisk, en fönsterhanterare baserad på Afterstep version 1.1
+Comment[ta]=ஆஃப்டர்ஸ்டெப் க்ளாசிக், ஆஃப்டர்ஸ்டெப் க்ளாசிக் v1.1 அடிப்படையிலான சாளர மேலாளர்
+Comment[tg]=Мудири равзанаҳои AfterStep Classic дар асоси AfterStep v1.1
+Comment[th]=AfterStep Classic คือระบบจัดการหน้าต่างที่ใช้ฐานของอาฟเตอร์สเตปเวอร์ชั่น 1.1
+Comment[tr]=AfterStep Classic pencere yöneticisi
+Comment[tt]=AfterStep Classic, AfterStep v1.1 asılında täräzä-idäräçe
+Comment[uk]=AfterStep Classic, менеджер вікон, заснований на AfterStep v1.1
+Comment[uz]=AfterStep Classic - AfterStep v1.1 asosida yaratilgan oyna boshqaruvchi
+Comment[uz@cyrillic]=AfterStep Classic - AfterStep v1.1 асосида яратилган ойна бошқарувчи
+Comment[vi]=AfterStep Classic, một trình quản lý cửa sổ dựa trên AfterStep v1.1
+Comment[wa]=AfterStep Classic, on manaedjeu di purneas båzé so AfterStep v1.1
+Comment[zh_CN]=AfterStep 经典,一个基于 AfterStep v1.1 的窗口管理器
+Comment[zh_TW]=AfterStep 經典, 一個基於 AfterStep v1.1 的視窗管理程式
diff --git a/kdm/kfrontend/sessions/blackbox.desktop b/kdm/kfrontend/sessions/blackbox.desktop
new file mode 100644
index 000000000..457ec7614
--- /dev/null
+++ b/kdm/kfrontend/sessions/blackbox.desktop
@@ -0,0 +1,88 @@
+[Desktop Entry]
+Type=XSession
+Exec=blackbox
+TryExec=blackbox
+Name=Blackbox
+Name[bn]=ব্ল্যাকবক্স
+Name[cy]= Du-flwch (Blackbox)
+Name[eo]=Negrujo
+Name[hi]=ब्लेकबॉक्स
+Name[ja]=BlackBox
+Name[mn]=Хар хайрцаг
+Name[ne]=कालो बाकस
+Name[pa]=ਕਾਲਾਬਕਸਾ
+Name[rw]=AgasandukuUmukara
+Name[ta]=கறுப்புப் பெட்டி
+Name[te]=నల్లడబ్బా
+Name[tg]=Қуттии сиёҳ
+Comment=A fast & light window manager
+Comment[af]='n Vinnige, lig gewig venster bestuurder
+Comment[ar]=مدير نوافذ خفيف وسريع
+Comment[be]=Хуткі і лёгкі кіраўнік вокнаў
+Comment[bg]=Бърз и лек мениджър на прозорци
+Comment[bn]=একটি হালকা এবং দ্রুত উইণ্ডো ম্যানেজার
+Comment[bs]=Brz i lagan window manager
+Comment[ca]=Un gestor de finestres ràpid i clar
+Comment[cs]=Rychlý a malý správce oken
+Comment[csb]=Chùtczi menedżer òknów o môłëch żądaniach
+Comment[cy]=Trefnydd ffenestri cyflym ac ysgafn
+Comment[da]=En hurtig & let vindueshåndtering
+Comment[de]=Kleiner, schneller Fenstermanager
+Comment[el]=Ένας γρήγορος και ελαφρύς διαχειριστής παραθύρων
+Comment[eo]=Rapida kaj malpeza fenestroadministrilo
+Comment[es]=Un gestor de ventanas rápido y ligero
+Comment[et]=Kiire ja vähenõudlik aknahaldur
+Comment[eu]=Leiho kudeatzaile bizkor eta arina
+Comment[fa]=یک مدیر پنجرۀ سبک و سریع
+Comment[fi]=Kevyt ja nopea ikkunaohjelma
+Comment[fr]=Un gestionnaire de fenêtres rapide et léger
+Comment[fy]=In flugge lichtgewicht finstersmanager
+Comment[ga]=Bainisteoir fuinneoga gasta éadrom
+Comment[gl]=Un xestor de fiestras lixeiro e rápido
+Comment[he]=מנהל חלונות מהיר וקל
+Comment[hi]=तेज और सरल विंडो प्रबंधक
+Comment[hr]=Brzi i lagani upravitelj prozora
+Comment[hu]=Egy gyors, egyszerű ablakkezelő
+Comment[is]=Léttur og hraðvirkur gluggastjóri
+Comment[it]=Un window manager veloce e leggero
+Comment[ja]=軽快なウィンドウマネージャ
+Comment[ka]=სწრაფი და მსუბუქი ფანჯრის მენეჯერი
+Comment[kk]=Жедел және жеңіл терезе менеджері
+Comment[km]=កម្មវិធី​គ្រប់គ្រង​បង្អួច​លឿន ហើយ​ភ្លឺ
+Comment[ko]=빠르고 가벼운 창 관리자
+Comment[lt]=Greita ir nedaug resursų naudojanti langų tvarkyklė
+Comment[lv]=Ātrs un viegls logu menedžeris
+Comment[mk]=Брз и лесен менаџер на прозорци
+Comment[mn]=Хурдан & хөнгөн цонхны удирдагч
+Comment[ms]=Pengurus tetingkap yang pantas & ringan
+Comment[mt]=Window manager ħafif u żgħir
+Comment[nb]=En rask og lett vindusbehandler
+Comment[nds]=En gaue un lütte Finsterpleger
+Comment[ne]=छिटो र हल्का सञ्झ्याल प्रबन्धक
+Comment[nl]=Een snelle lichtgewicht windowmanager
+Comment[nn]=Ein rask og lett vindaugssjef
+Comment[pa]=ਇੱਕ ਤੇਜ਼ ਅਤੇ ਹਲਕਾ ਝਰੋਖਾ ਮੈਨੇਜਰ
+Comment[pl]=Szybki menedżer okien o małych wymaganiach
+Comment[pt]=Um gestor de janelas rápido e leve
+Comment[pt_BR]=Um gerenciador de janelas rápido e leve
+Comment[ro]=Un manager de ferestre mic și rapid
+Comment[ru]=Быстрый и лёгкий оконный менеджер
+Comment[rw]=Mugenga dirishya yihuta & yoroshye
+Comment[se]=Jođánis ja geahpes lásegieđahalli
+Comment[sk]=Rýchly a nenáročný správca okien
+Comment[sl]=Hiter in lahek okenski uporavljalnik
+Comment[sr]=Брзи и лагани менаџер прозора
+Comment[sr@Latn]=Brzi i lagani menadžer prozora
+Comment[sv]=Snabb och lättviktig fönsterhanterare
+Comment[ta]=விரைவான மற்றும் இலகுவான KDE சாளர மேலாளர்
+Comment[tg]=Суст ва мудири тирезаи равшан
+Comment[th]=ระบบจัดการหน้าต่างที่เร็วและเบา
+Comment[tr]=Hızlı ve hafif bir pencere yöneticisi
+Comment[tt]=Citez ciñel täräzä-idäräçe
+Comment[uk]=Легкий та швидкий менеджер вікон
+Comment[uz]=Tez va oddiy oyna boshqaruvchi
+Comment[uz@cyrillic]=Тез ва оддий ойна бошқарувчи
+Comment[vi]=Một trình quản lý cửa sổ nhẹ và nhanh
+Comment[wa]=On ledjir et roed manaedjeu di purneas
+Comment[zh_CN]=又快又轻巧的窗口管理器
+Comment[zh_TW]=一個快速及輕量化的視窗管理程式
diff --git a/kdm/kfrontend/sessions/cde.desktop b/kdm/kfrontend/sessions/cde.desktop
new file mode 100644
index 000000000..1f2dee088
--- /dev/null
+++ b/kdm/kfrontend/sessions/cde.desktop
@@ -0,0 +1,74 @@
+[Desktop Entry]
+Type=XSession
+Exec=/usr/dt/bin/Xsession
+TryExec=/usr/dt/bin/Xsession
+Name=CDE
+Name[hi]=सीडीई
+Name[mn]=КДE
+Name[te]=సిడిఈ
+Name[tg]=Муҳити графикии муштарак (CDE)
+Name[th]=แบบ CDE
+Comment=The Common Desktop Environment, a proprietary industry standard desktop environment
+Comment[af]=Die 'Common Desktop Environment', 'n beskermde, industrie standaard werkskerm omgewing
+Comment[ar]=محيط سطح المكتب الشائع، محيط سطح المكتب الصناعي المعايير
+Comment[be]=Common Desktop Environment, прапрыетарнае стандартнае працоўнае асяроддзе
+Comment[bn]=কমন ডেস্কটপ এনভায়রনমেন্ট (Common Desktop Environment), একটি মালিকানাধীন ইনডাস্ট্রি স্ট্যান্ডার্ড
+Comment[bs]=Common Desktop Environment, vlasnička desktop okolina, industrijski standard
+Comment[ca]=The Common Desktop Environment, l'entorn d'escriptori estàndard de la indústria propietària
+Comment[csb]=Common Desktop Environment, sztandardowé industrëjné òkrãże pùltu
+Comment[cy]=Yr Amgylchedd Penbwrdd Cyffredin (Common Desktop Environment), amgylchedd penbwrdd perchnogol sy'n safonol yn y diwydiant
+Comment[da]=Common Desktop Environment, et privatejet industristandard desktopmiljø
+Comment[de]=Das Common Desktop Environment, eine proprietäre Arbeitsumgebung und ein Industriestandard
+Comment[el]=To Κοινό Περιβάλλον Επιφάνειας εργασίας, ένα βιομηχανικό πρότυπο επιφάνειας εργασίας
+Comment[eo]=La Komuna Labortablo Ĉirkaŭaĵo
+Comment[es]=El Common Desktop Environment, un estándar en los entornos de escritorio propietarios
+Comment[et]=Üldine töölaua keskond (Common Desktop Environment) on kaubanduslik standardne töölaua keskkond
+Comment[eu]=Common Desktopo Environment, mahaigain jabedun inguruneetako estandarra
+Comment[fa]=محیط رومیزی مشترک، محیط رومیزی استاندارد صنعت اختصاصی
+Comment[fi]=Common Desktop Environment, patentoitu työpöytäympäristöjen teollisuusstandardi
+Comment[fr]=Le Common Desktop Environment, un environnement de bureau propriétaire standard dans l'industrie
+Comment[fy]=The Common Desktop Environment, In kommersjele yndustrieel standerdisearre buroblêd omwrâld
+Comment[gl]=O Common Desktop Environment, un entorno de escritório proprietario estándar para industria
+Comment[he]=The Common Desktop Environment, סביבת עבודה מסחרית וקניינית סטנדרטית
+Comment[hi]=सामूहिक डेस्कटॉप माहौल, एक स्वामित्व युक्त औद्योगिक मानक डेस्कटॉप माहौल
+Comment[hr]=Opće okruženje radne površine, standardizirano industrijskim vlasništvima
+Comment[hu]=The Common Desktop Environment, egy kereskedelmi, kváziszabványnak számító grafikus környezet
+Comment[is]=Common Desktop Environment er lokað skjáborðsumhverfi sem var staðlað umhverfi til skamms tíma
+Comment[it]=Il Common Desktop Environment, un desktop environment proprietario standard.
+Comment[ja]=Common Desktop Environment,プロプライエタリな業界標準のデスクトップ環境
+Comment[ka]=Common Desktop Environment, UNIX -ის სამუშაო სფეროს სამრეწვლო სტანდარტი
+Comment[kk]=Common Desktop Environment - UNIX жұмыс ортаның өнеркәсіп стандарты
+Comment[km]=The Common Desktop Environment, បរិស្ថាន​ផ្ទៃតុ​ខ្នាត​គំរូ​ដែល​មាន​កម្មសិទ្ធិ
+Comment[lt]=Common Desktop Environment, nuosavybinių sistemų standartinė darbastalio tvarkyklė
+Comment[mk]=Common Desktop Environment, сопственичка индустриски стандардна работна околина
+Comment[mn]=Нийтлэг дэлгэцийн системийн орчин, үйлдвэрийн стандарт дэлгэцийн системийн орчин
+Comment[ms]=Persekitaran Desktop Biasa, persekitaran desktop standard industri proprietari
+Comment[mt]=Common Desktop Environment, ambjent grafiku propjetarju u standard tal-industrija
+Comment[nb]=Common desktop Environment, et godseid skrivebordsmiljø som er standard i programvareindustrien
+Comment[nds]=De Common Desktop Environment, de Schriefdisch-Ümgeven vun en proprieteren Industrie-Standard
+Comment[ne]=साझा डेस्कटप वातावरण, श: शुल्क उद्योग मानक डेस्कटप वातावरण
+Comment[nl]=The Common Desktop Environment, een commerciële industrieel gestandariseerde desktop environment
+Comment[nn]=Common Desktop Environment, eit godseigd skrivebordsmiljø som er standard i programvareindustrien
+Comment[pa]=ਇੱਕ ਆਮ ਵੇਹੜਾ ਵਾਤਾਵਰਣ, ਇੱਕ ਵਪਾਰਿਕ ਮਿਆਰ ਦਾ ਵੇਹੜਾ ਵਾਤਾਵਰਣ
+Comment[pl]=Common Desktop Environment, standardowe przemysłowe środowisko pulpitu
+Comment[pt]=O Common Desktop Environment, um ambiente de trabalho gráfico padrão e proprietário
+Comment[pt_BR]=O Ambiente de Trabalho Comum (CDE), um ambiente de trabalho proprietário padrão da indústria
+Comment[ro]=Common Desktop Environment, un mediu grafic proprietar și devenit standard industrial
+Comment[ru]=Common Desktop Environment, промышленный стандарт рабочей среды UNIX
+Comment[rw]=Ibikikije Ibiro Rusange, ibikikije ibiro bisanzwe bwite by'isosiyete
+Comment[se]=Common Desktop Environment, čállinbeavdebiras mii lea standárda prográmmagálvoindustriijas
+Comment[sk]=The Common Desktop Environment, proprietárne štandardné pracovné prostredie
+Comment[sl]=Common Desktop Environment, lastniško standardno industrijsko namizno okolje
+Comment[sr]=„Common Desktop Environment“, власничко индустријски стандардно радно окружење
+Comment[sr@Latn]=„Common Desktop Environment“, vlasničko industrijski standardno radno okruženje
+Comment[sv]=Common Desktop Environment, en privatägd industristandard skrivbordsmiljö
+Comment[ta]=பொதுவான மேல்மேசை, தன்உரிமை உடைய நிறுவனத்தின் நிலையான மேல்மேசை சூழல்
+Comment[tg]=Common Desktop Environment дар асоси UNIX
+Comment[th]=Common Desktop Environment คือ สภาพแวดล้อมของพื้นที่ทำงานที่ได้มาตรฐานอุตสาหกรรม ที่ไม่ใช่ซอฟต์แวร์เสรี
+Comment[tr]=Common Desktop Environment (CDE)
+Comment[tt]=Common Desktop Environment, UNIX öçen citeşterü standardı
+Comment[uk]=The Common Desktop Environment, закритий промисловий стандарт графічного середовища
+Comment[vi]=Môi trường Màn hình nền Chung, một môi trường màn hình nền giữ bản quyền, tuân thủ chuẩn công nghiệp
+Comment[wa]=Li Comon Evironmint d' Sicribanne (Common Desktop Environment), on evironmint d' sicribanne nén libe po l' industreye
+Comment[zh_CN]=通用窗口环境(CDE),私有工业标准的桌面环境
+Comment[zh_TW]=The Common Desktop Environment, 一個有專利的工業標準
diff --git a/kdm/kfrontend/sessions/ctwm.desktop b/kdm/kfrontend/sessions/ctwm.desktop
new file mode 100644
index 000000000..e7ee84471
--- /dev/null
+++ b/kdm/kfrontend/sessions/ctwm.desktop
@@ -0,0 +1,72 @@
+[Desktop Entry]
+Type=XSession
+Exec=ctwm
+TryExec=ctwm
+Name=CTWM
+Name[eo]=TFAC
+Name[hi]=सीटीडबल्यूएम
+Name[te]=సి టి డబ్ల్యు ఎం
+Comment=Claude's Tab Window Manager, TWM enhanced by virtual screens, etc.
+Comment[af]=Claude se Tab venster bestuurder. Dis TWM wat met virtuele skerms verbeter is
+Comment[ar]=مدير نوافذ Claude's Tab، وهي TWM محسّن بشاشات وهمية، إلخ.
+Comment[be]=Кіраўнік вокнаў з укладкамі ад Claude, TWM з падтрымкай віртуальных экранаў і інш.
+Comment[bg]=Claude"s Tab Window Manager, TWM enhanced by virtual screens, etc.
+Comment[bn]=ক্লড-এর ট্যাব উইণ্ডো ম্যানেজার
+Comment[bs]=Claude's Tab Window Manager, TWM proširen virtuelnim ekranima itd.
+Comment[ca]=Gestor de finestres amb pestanyes d'en Claude, millores TWM per a pantalles virtuals, etc.
+Comment[csb]=Menedżer òknów Claude, TWM zbògacony ò wirtualné pùltë, ëtp.
+Comment[cy]=Trefnydd Ffenestri Tab Claude, TWM wedi ei wella gan sgriniau rhith, ayyb.
+Comment[da]=Claude's Tab vindueshåndtering, TWM udvidet med virtuelle skærme osv.
+Comment[de]=Claudes Fenstermanager mit Karteikartenfenstern, eine verbesserte Fassung von TWM mit virtuellen Ansichten usw.
+Comment[el]=Ο διαχειριστής παραθύρων Tab του Claude, ο TWM εμπλουτισμένος με εικονικές οθόνες, κτλ.
+Comment[eo]=Taba Fenestroadministrilo de Claude, TWM, bonigita per virtualaj ekranoj ktp
+Comment[es]=Claude's Tab Window Manager, TWM mejorado con pantallas virtuales, etc.
+Comment[et]=Claude kaartidega aknahaldur, aluseks TWM, mida on täiendatud virtuaaltöölaudadega jne.
+Comment[eu]=Claude's Tab leiho kudeatzailea, pantaila birtual eta abarrez hobetutako TWMa
+Comment[fa]=مدیر پنجرۀ تب Claude، TWM، گسترش‌یافته توسط پرده‌های مجازی و غیره.
+Comment[fi]=Clauden välilehtiikkunaohjelma. TWM, johon lisätty muun muassa virtuaalityöpöydät.
+Comment[fr]=Claude's Tab Window Manager, TWM avec en plus les bureaux virtuels, etc.
+Comment[fy]=Claude's Tab _indow Manager, TWM útbreide mei firtuele skermen etc.
+Comment[gl]=Xestor de Fiestras de Claude, TWM mellorado con pantallas virtuais, etc.
+Comment[he]=Claude's Tab Window Manager, TWM המשופרת על ידי מסכים וירטואליים וכו'
+Comment[hi]=क्लाउडे का टैब युक्त विंडो प्रबंधक, टीडबल्यूएम को आभासी स्क्रीन इत्यादि से बेहतर बनाया गया.
+Comment[hr]=Claudov Tab upravitelj prozora, TWM poboljšan virtualnim zaslonima, itd.
+Comment[hu]=Claude lapozós ablakkezelője, lényegében a TWM, kiegészítve virtuális képernyőkkel, egyebekkel
+Comment[is]=Tab gluggastjórinn eftir Claude sem hefur verið endurbættur með sýndarskjáum og fl.
+Comment[it]=Window manager con linguette di Claude, TWM migliorato con schermi virtuali, ecc.
+Comment[ja]=TWM に仮想デスクトップなどを強化した Claude のウィンドウマネージャ
+Comment[ka]=Claude's Tab Window Manager - TWM-ის გაუმჯობესებული ვერსია
+Comment[kk]=Claude's Tab Window Manager, TWM-ның жетілдірген нұсқасы.
+Comment[km]=កម្មវិធី​គ្រប់គ្រង​បង្អួច​ជា​ផ្ទាំង​របស់ Claude, TWM ដែល​ធ្វើ​ឲ្យ​ប្រសើរ​ដោយ​អេក្រង់​និមិត្ត​ជាដើម
+Comment[lt]=Claude kortelių langų tvarkyklė, TWM praplėsta virtualių ekranų palaikymu ir t.t.
+Comment[mk]=Claude's Tab Window Manager, TWM подобрен менаџер со виртуелни екрани итн.
+Comment[mn]=Клаудиагийн  ТАВ цонхны удирдагч, TWM виртуал дэлгэцээр өргөтгөгдсөн, гэх мэт.
+Comment[mt]=Claude's Tab Window Manager, TWM flimkien ma' virtual screens, eċċ.
+Comment[nb]=Claude's Tab Window Manager, TWM forbedret med virtuelle skrivebord,osv.
+Comment[nds]=Claude's Tab Finsterpleger, TWM verbetert üm virtuelle Schriefdischen usw.
+Comment[ne]=क्लाउडको ट्याब सञ्झ्याल प्रबन्धक, अवास्तविक पर्दाद्वारा बृद्धि गरिएको TWM, आदि
+Comment[nl]=Claude's Tab Window Manager, TWM uitgebreid met virtuele schermen etc.
+Comment[nn]=Claude's Tab Window Manager, TWM med virtuelle skrivebord og andre forbetringar
+Comment[pa]=ਕਲਾਉਡੀ ਦਾ ਟੈਬ ਝਰੋਖਾ ਮੈਨੈਜਰ, TWM ਫਰਜ਼ੀ ਪਰਦਿਆਂ ਆਦਿ ਨਾਲ ਲੈੱਸ ਹੈ।
+Comment[pl]=Menedżer okien Claude, TWM wzbogacony o wirtualne pulpity, itp.
+Comment[pt]=O Tab Window Manager do Claude, um TWM melhorado com ecrãs virtuais, etc.
+Comment[pt_BR]=O gerenciador de janelas em abas do Claude, o TWM melhorado pelas telas virtuais, etc.
+Comment[ro]=Managerul de ferestre al lui Claude, o versiune îmbunătățită de TWM cu ecrane virtuale etc.
+Comment[ru]=Claude's Tab Window Manager - улучшенная версия TWM
+Comment[rw]=Mugenga Dirishya y'Agafishi ya Claude, TWM ivuguruwe na mugaragaza zitagaragara, n'ibindi
+Comment[se]=Claudea Tab Window Manager, TWM mas lea virtuella čállinbeavddit ja eará buorideamit.
+Comment[sk]=Claude's Tab Window Manager, TWM rozšírený o virtuálne plochy, atď.
+Comment[sl]=Claude's Tab Window Manager, TWM izboljšan z navideznimi zasloni ipd.
+Comment[sr]=„Claude's Tab Window Manager“, TWM побољшан виртуелним екранима и сл.
+Comment[sr@Latn]=„Claude's Tab Window Manager“, TWM poboljšan virtuelnim ekranima i sl.
+Comment[sv]=Claudes fönsterhanterare med flikar, TWM förbättrad med virtuella skärmar, etc.
+Comment[ta]= க்ளூடின் தத்தல் சாளர மேளாளர், TWM ஆல் மேம்படுத்தப்பட்ட மெய்நிகர் திரைகள்..
+Comment[tg]=Claude's Tab Window Manager - Версияи навтарини TWM
+Comment[th]=ตัวจัดการแท็บหน้าต่างของ Claude คือ TWM ที่เพิ่มความสามารถด้วยหน้าจอเสมือน และอื่นๆ
+Comment[tr]=Claude's Sekme Pencere Yöneticisi, sanal ekranlar ile TWM genişletilmiş, vb.
+Comment[tt]=Claude's Tab Window Manager, TWM'nıñ qulaylanğan töre
+Comment[uk]=Claude's Tab Window Manager, TWM з підтримкою віртуальних екранів, тощо.
+Comment[vi]=Trình quản lý cửa sổ kiểu Thẻ của Claude, TWM cải tiến với màn hình ảo v.v.
+Comment[wa]=Li Manaedjeu di Purneas a Linwetes di Claude (Claude's Tab Window Manager). TWM permete des forveyowès waitroûles, evnd.
+Comment[zh_CN]=Claude 的标签式窗口管理器,加强了虚拟屏幕等功能的 TWM。
+Comment[zh_TW]=Claude's Tab 視窗管理程式, 基於 TWM 並加強虛擬螢幕等功能
diff --git a/kdm/kfrontend/sessions/cwwm.desktop b/kdm/kfrontend/sessions/cwwm.desktop
new file mode 100644
index 000000000..51b60abd3
--- /dev/null
+++ b/kdm/kfrontend/sessions/cwwm.desktop
@@ -0,0 +1,74 @@
+[Desktop Entry]
+Type=XSession
+Exec=cwwm
+TryExec=cwwm
+Name=CWWM
+Name[eo]=CWFA
+Name[hi]=सीडबल्यूडबल्यूएम
+Name[te]=సి డబ్ల్యు డబ్ల్యు ఎం
+Comment=The ChezWam Window Manager, a minimalist window manager based on EvilWM
+Comment[af]=Die ChezWam venster bestuurder. Dis 'n minimalistiese venster bestuurder wat op EvilWM gebaseer is.
+Comment[ar]=مدير نوافذ ChezWam، وهو مدير نوافذ مصغّر مبني على EvilWM
+Comment[be]=Кіраўнік вокнаў ChezWam, мінімалістычны, заснаваны на EvilWM
+Comment[bn]=ChezWam উইণ্ডো ম্যানেজার, EvilWM ভিত্তিক একটি উইণ্ডো ম্যানেজার
+Comment[bs]=ChezWam Window Manager, minimalistički window manager baziran na EvilWM
+Comment[ca]=El gestor de finestres ChezWam, un gestor de finestres minimalista basat en EvilWM
+Comment[cs]=ChezWam, minimalistický správce oken založený na EvilWM
+Comment[csb]=Menedżer òknów ChezWam, prosti menedżer òknów ùsôdzony na spòdlém EvilWM
+Comment[cy]=Y Trefnydd Ffenestri ChezWam, trefnydd ffenestri lleiafol wedi'i seilio ar EvilWM
+Comment[da]=ChezWam vindueshåndteringen, en minimalistisk vindueshåndtering baseret på EvilWM
+Comment[de]=Deer ChezWam-Fenstermanager, eine minimalistische Lösung, die auf EvilWM basiert
+Comment[el]=Ο ChezWam διαχειριστής παραθύρων, ένας μινιμαλιστικός διαχειριστής παραθύρων βασισμένος στον EvilWM
+Comment[eo]=La ChezWam Fenestroadministrilo, devenigita de MalbonaFA
+Comment[es]=El gestor de ventanas ChezWam, un gestor minimalista basado en EvilWM
+Comment[et]=ChezWami aknahaldur on vähenõudlik aknahaldur, mille aluseks on EvilWM
+Comment[eu]=ChezWarn leiho kudeatzailea, EvilWM-en oinarritutako leiho kudeatzaile minimalista
+Comment[fa]=مدیر پنجره ChezWam، مدیر پنجرۀ کمینه بر اساس EvilWM
+Comment[fi]=ChezWam, minimalistinen EvilWM:ään pohjautuva ikkunaohjelma.
+Comment[fr]=Le ChezWam Window Manager, un gestionnaire de fenêtres minimaliste fondé sur EvilWM
+Comment[fy]=The ChezWam Window Manager, in minimalistyske finstersmanager baseare op EvilWM
+Comment[gl]=O Xestor de Fiestras ChezWarm, un xestor minimalista baseado en EvilWM
+Comment[he]=The ChezWam Window Manager, מנהל חלונות מינימליסטי המבוסס על EvilWM
+Comment[hi]=चेकवाम विंडो प्रबंधक, एक अल्पतम विंडो प्रबंधक EvilWM पर आधारित
+Comment[hr]=ChezWam, minimalistički upravitelj prozora zasnovan na EvilWM
+Comment[hu]=ChezWam ablakkezelő, egy nagyon egyszerű ablakkezelő az EvilWM alapján
+Comment[is]=ChezWam gluggastjórinn er lágmarks gluggastjóri sem er byggður á EvilWM
+Comment[it]=Il ChezWam Window Manager, un window manager minimalista basato su EvilWM
+Comment[ja]=ChezWam ウィンドウマネージャ, EvilWM ベースの小さなウィンドウマネージャ
+Comment[ka]=ChezWam Window Manager - მინიმალისტური ფანჯრის მენეჯერი EvilWM -ის ბაზაზე
+Comment[kk]=ChezWam Window Manager, EvilWM-негіздеген шағын терезе менеджері
+Comment[km]=កម្មវិធី​គ្រប់គ្រង​បង្អួច ChezWam ដែល​ជា​កម្មវិធី​គ្រប់គ្រង​បង្អួច​មិន​សូវ​សម្បូរ​បែប​ហើយ​ផ្អែក​លើ EvilWM
+Comment[lt]=ChezWam langų tvarkyklė, minimalistinė langų tvarkyklė, paremta EvilWM
+Comment[mk]=ChezWam Window Manager, минималистички менаџер на прозорци базиран на EvilWM
+Comment[mn]=ChezWam Цонхны удирдагч, EvilWM дээр суурилсан хамгийн жижиг цонхны удирдагч
+Comment[mt]=ChezWam Window Manager - window manager minimu ibbażat fuq EvilWM
+Comment[nb]=The ChezWam Window Manager, en minimalistisk vindusbehandler basert på EvilWM
+Comment[nds]=De ChezWam Finsterpleger is minimalistisch un buut op EvilWM op
+Comment[ne]=चेजवाम सञ्झ्याल प्रबन्धक, EvilWM आधारित मिनिमलिस्ट सञ्झ्याल प्रबन्धक
+Comment[nl]=The ChezWam Window Manager, een minimalistische windowmanager gebaseerd op EvilWM
+Comment[nn]=ChezWam Window Manager, ein minimalistisk vindaugssjef som byggjer på EvilWM
+Comment[pa]=ChezWam ਝਰੋਖਾ ਮੈਨੇਜਰ, ਇੱਕ EvilWM ਤੇ ਆਧਾਰਿਤ ਹਲਕਾ ਝਰੋਖਾ ਮੈਨੇਜਰ ਹੈ
+Comment[pl]=Menedżer okien ChezWam, prosty menedżer okien stworzony na podstawie EvilWM
+Comment[pt]=O ChezWam Window Manager, um gestor de janelas minimalista baseado no EvilWM
+Comment[pt_BR]=O gerenciador de janelas de ChezWam, um gerenciador de janelas minimalista baseado no EvilWM
+Comment[ro]=Managerul de ferestre ChezWam, o versiune minimalistică bazată pe EvilWM
+Comment[ru]=ChezWam Window Manager - минимальный оконный менеджер на основе EvilWM
+Comment[rw]=Mugenga Dirishya ChezWam, mugenga dirishya igira-nto ishingiye kuri EvilWM
+Comment[se]=ChezWam Window Manager, ein minimalisttalaš lásegieđahalli, ráhkaduvvon EvilWM vuođul
+Comment[sk]=The ChezWam Window Manager, minimálny správca okien založený na EvilWM
+Comment[sl]=ChezWam Window Manager, skromen okenski upravitelj na osnovi EvilWM
+Comment[sr]=„ChezWam Window Manager“, минималистички менаџер прозора заснован на EvilWM-у
+Comment[sr@Latn]=„ChezWam Window Manager“, minimalistički menadžer prozora zasnovan na EvilWM-u
+Comment[sv]=Fönsterhanteraren Chezwam, en minimalistisk fönsterhanterare baserad på Ond WM
+Comment[ta]=செஸ்வாம் சாளர மேளாளர்,EvilWM ஐ அடிப்படையிலான குறைந்தபட்ச சாளர மேளாளர்
+Comment[tg]=ChezWam Window Manager - Мудири равзанаҳои хурд дар асоси EvilWM
+Comment[th]=ตัวจัดการหน้าต่าง ChezWarm คือ ระบบจัดการ หน้าต่างขนาดเล็ก สร้างจากพื้นฐานของ EvilWM
+Comment[tr]=ChezWam Pencere Yöneticisi
+Comment[tt]=ChezWam Window Manager, EvilWM asılında ciñel täräzä-idäräçe
+Comment[uk]=Мінімалістичний менеджер вікон ChezWam, заснований на EvilWM
+Comment[uz]=EvilWM asosida yaratilgan juda oddiy oyna boshqaruvchi
+Comment[uz@cyrillic]=EvilWM асосида яратилган жуда оддий ойна бошқарувчи
+Comment[vi]=Trình quản lý cửa sổ ChezWam, một trình quản lý cửa sổ đơn giản dựa trên EvilWM
+Comment[wa]=Li Manaedjeu di Purneas di ChezWam (ChezWam Window Manager). On manaedjeu di purneas tot simpe, båzé so EvilWM
+Comment[zh_CN]=ChezWam 窗口管理器,基于 EvilWM 的最小化窗口管理器
+Comment[zh_TW]=The ChezWam 視窗管理程式,基於 EvilWM 的小型化視窗管理程式
diff --git a/kdm/kfrontend/sessions/enlightenment.desktop b/kdm/kfrontend/sessions/enlightenment.desktop
new file mode 100644
index 000000000..66dcdd574
--- /dev/null
+++ b/kdm/kfrontend/sessions/enlightenment.desktop
@@ -0,0 +1,86 @@
+[Desktop Entry]
+Type=XSession
+Exec=enlightenment_start
+TryExec=enlightenment
+Name=Enlightenment
+Name[bn]=এনলাইটেনমেন্ট
+Name[cy]=Goleuni (Enlightenment)
+Name[eo]=Lumaĵo
+Name[fa]=روشن‌فکری
+Name[hi]=एनलाइटनमेंट
+Name[km]=ភ្លឺ
+Name[mn]=Гэгээрэл
+Name[ne]=बुद्धत्व
+Name[pa]=ਗਿਆਨ
+Name[rw]=Imurika
+Name[ta]=விளக்கிக்கூறுதல்
+Name[te]=జ్ఞానొదయం
+Comment=An extremely themable very feature-rich window manager
+Comment[af]='n Uitbreibare, tema geaktiveerde venster bestuurder
+Comment[ar]=مدير نوافذ غني بالمزايا وقابل لاستخدام السّمات بشكل كبير.
+Comment[be]=Абсалютна змяняльны, багаты на здольнасці кіраўнік вокнаў
+Comment[bn]=প্রচুর বৈশিষ্ট্য সম্বলিত একটি উইণ্ডো ম্যানেজার, যা বহুভাবে নিজের পছন্দমত বদলে নেওয়া যায়
+Comment[bs]=Izuzetno prilagodljiv window manager bogat opcijama
+Comment[ca]=Un gestor de finestres extremadament temible molt ric en característiques
+Comment[cs]=Na funkce bohatý správce oken s širokou škálou témat
+Comment[csb]=Bògati w fùnkcëje menedżer òknów ò wiôldżich mòżnotach zjinaczi wëzdrzatkù
+Comment[cy]=Trefnydd ffenestri sy'n llawn o nodweddion, efo llawer o themau.
+Comment[da]=En ekstremt tema-fleksibel egenskabsrig vindueshåndtering
+Comment[de]=Sehr design- und funktionsreicher Fenstermanager
+Comment[el]=Ένας εξαιρετικά παραμετροποιήσιμος και πλούσιος σε χαρακτηριστικά διαχειριστής παραθύρων
+Comment[eo]=Fenestroadministrilo kun tre multaj eblecoj
+Comment[es]=Un gestor de ventanas lleno de características y extremadamente personalizable
+Comment[et]=Äärmiselt paljude teemade ja väga laialdaste võimalustega aknahaldur
+Comment[eu]=Gaitasun ugari dituen, eta zeharo pertsonalizagarria den leiho kudeatzailea
+Comment[fa]=مدیر پنجره با ویژگی زیاد و شدیداً موضوعی
+Comment[fi]=Erittäin muokattavissa oleva ominaisuusrikas ikkunaohjelma
+Comment[fr]=Un gestionnaire de fenêtres avec beaucoup de thèmes et de fonctionnalités
+Comment[fy]=In tige rike en ynstelbere finstersmanager
+Comment[gl]=Un xestor de fiestras moi configurábel en apariencia e rico en características
+Comment[he]=מנהל חלונות פונקציונלי המכיל אפשרויות רבות להגדרת ערכות נושא
+Comment[hi]=एक विंडो प्रबंधक जो अत्यंत प्रसंग युक्त तथा विशेषता से भरपूर है
+Comment[hr]=Izuzetno prilagodljiv upravitelj prozora, bogat mogućnostima
+Comment[hu]=Egy nagyon sokoldalúan témázható, sok lehetőséget biztosító ablakkezelő
+Comment[is]=Afskaplega öflugur gluggastjóri með góðum þemastuðningi
+Comment[it]=Un window manager estremamente temabile con molte funzionalità
+Comment[ja]=詳細な部分までもテーマ化が可能な非常に多機能のウィンドウマネージャ
+Comment[ka]=მრავალფუნქციური ფანჯრის მენეჯერი თემების მხარდაჭერით
+Comment[kk]=Қасиеттер жағынан бай, көп тақырыпты терезе менеджері
+Comment[km]=កម្មវិធី​គ្រប់គ្រង​បង្អួច​មាន​លក្ខណៈ​ពិសេស​សម្បូរ​បែប ដែល​អាច​ប្ដូរ​រូបរាង​បាន​តាម​ចិត្ត
+Comment[ko]=다양한 곳을 꾸밀 수 있는 기능이 풍부한 창 관리자
+Comment[lt]=Daug temų ir savybių turinti langų tvarkyklė
+Comment[lv]=Tēmām un iespējām bagāts logu menedžeris
+Comment[mk]=Менаџер на прозорци богат со можности и екстремно подложен на стилизирање со теми
+Comment[mn]=Маш ирээдүйтэй цонхны удирдагч
+Comment[ms]=Pengurus tetingkap kaya ciri yang amat boleh tema
+Comment[mt]=Window manager rikk u b'ħafna effetti viżwali.
+Comment[nb]=En svært omfattende vindusbehandler som kan tilpasses med temaer
+Comment[nds]=En Finsterpleger mit bannig vele Funkschonen un Mustern
+Comment[ne]=अत्याधिक विषयवस्तु योग्य धेरै आकृति सञ्झ्याल प्रबन्धक
+Comment[nl]=Een zeer rijke en configureerbare windowmanager
+Comment[nn]=Ein svært omfattande vindaugssjef som kan tilpassast med tema
+Comment[pa]=ਇੱਕ ਸਰੂਪ ਦੇ ਪੂਰੇ ਫੀਚਰਾਂ ਨਾਲ ਭਰਪੂਰ ਝਰੋਖਾ ਮੈਨੇਜਰ
+Comment[pl]=Bogaty w funkcje menedżer okien o dużych możliwościach zmiany wyglądu
+Comment[pt]=Um gestor de janelas muito rico em funcionalidades e extremamente personalizado na sua aparência
+Comment[pt_BR]=Um gerenciador de janelas rico em recursos e extremamente configurável através de temas
+Comment[ro]=Un manager de ferestre extrem de versatil și cu multe facilități
+Comment[ru]=Многофункциональный, поддерживающий темы оконный менеджер
+Comment[rw]=Mugenga Dirishya biranga-bikize cyane ngirwa-nsanganyamatsiko mu buryo burenze
+Comment[se]=Lášegieđahalli mas leat erenoamáš ollu doaimmat ja maid sáhttá heivehit fáttáin
+Comment[sk]=Správca okien s veľkým množstvom funkcií a extrémnou podporou tém
+Comment[sl]=Okenski upravitelj, zelo prilagodljiv in poln možnosti
+Comment[sr]=Изузетно прилагодљив менаџер прозора са пуно могућности
+Comment[sr@Latn]=Izuzetno prilagodljiv menadžer prozora sa puno mogućnosti
+Comment[sv]=En ytterst anpassningsbar mycket funktionsrik fönsterhanterare
+Comment[ta]=நிறைய தன்மைகள் அடங்கிய சாளர மேளாளர்
+Comment[tg]=Мудири равзанаҳо дорои мавзӯъҳои гуногун ва дигар бисёр функсияҳо
+Comment[th]=ระบบจัดการหน้าต่างที่เต็มไปด้วยความสามารถ และใช้ชุดตกแต่งมากมาย
+Comment[tr]=İleri derecede temalanabilir, özellik zengini bir masaüstü yöneticisi
+Comment[tt]=Küpçaralı tışlanulı täräzä-idäräçe
+Comment[uk]=Менеджер вікон з потужною підтримкою тем та різноманітних функцій
+Comment[uz]=Mavzu va imkoniyatlarga juda boy oyna boshqaruvchi
+Comment[uz@cyrillic]=Мавзу ва имкониятларга жуда бой ойна бошқарувчи
+Comment[vi]=Một trình quản lý cửa sổ cực kỳ dễ thay đổi sắc thái với nhiều tích năng
+Comment[wa]=On manaedjeu di purneas foirt bén po-z eployî des tinmes et foirt ritche en usteyes
+Comment[zh_CN]=强主题化的多功能窗口管理器
+Comment[zh_TW]=一個功能相當豐富的視窗管理程式
diff --git a/kdm/kfrontend/sessions/evilwm.desktop b/kdm/kfrontend/sessions/evilwm.desktop
new file mode 100644
index 000000000..3c8c4f657
--- /dev/null
+++ b/kdm/kfrontend/sessions/evilwm.desktop
@@ -0,0 +1,77 @@
+[Desktop Entry]
+Type=XSession
+Exec=evilwm
+TryExec=evilwm
+Name=EvilWM
+Name[eo]=MalbonaFA
+Name[hi]=एविलडबल्यूएम
+Name[sv]=Ond WM
+Name[te]=ఈవిల్ డబ్ల్యు ఎం
+Comment=A minimalist window manager based on AEWM
+Comment[af]='n Minimalistiese venster bestuurder wat op AEWM gebaseer is
+Comment[ar]=مدير نوافذ مصغّر مبني على AEWM
+Comment[be]=Кіраўнік вокнаў для мінімаліста, заснаваны на AEWM
+Comment[bn]=AEWM ভিত্তিক একটি পরিমিত উইণ্ডো ম্যানেজার
+Comment[bs]=Minimalistički window manager baziran na AEWM
+Comment[ca]=Un gestor de finestres minimalista basat en AEWM
+Comment[cs]=Minimalistický správce oken založený na AEWM
+Comment[csb]=Prosti menedżer òknów ùsôdzony na spòdlém AEWM
+Comment[cy]=Trefnydd ffenestri lleiafol wedi'i seilio ar AEM
+Comment[da]=En minimalistisk vindueshåndtering baseret på AEWM
+Comment[de]=Minimalistischer Fenstermanager, der auf AEWM basiert
+Comment[el]=Ένας μινιμαλιστικός διαχειριστής παραθύρων βασισμένος στον AEWM
+Comment[eo]=Minimumema fenestroadministrilo
+Comment[es]=Un administrado de ventanas minimalista basado en AEWM
+Comment[et]=Vähenõudlik aknahaldur, mille aluseks on AEWM
+Comment[eu]=AEWMn oinarritutako leiho kudeatzaile minimalista
+Comment[fa]=یک مدیر پنجرۀ کمینه براساس AEWM
+Comment[fi]=Minimalistinen, AEWM:ään pohjautuva ikkunaohjelma
+Comment[fr]=Un gestionnaire de fenêtres fondé sur AEWM
+Comment[fy]=In minimalistyske finstersmanager basearre op AEWM
+Comment[gl]=Un xestor de fiestras minimalista baseado en AEWM
+Comment[he]=מנהל חלונות מינימליסטי המבוסס על AEWM
+Comment[hi]= एक अल्पतम विंडो प्रबंधक एईडबल्यूएम पर आधारित
+Comment[hr]=Minimalistički upravitelj prozora zasnovan na AEWM-u
+Comment[hu]=Egy nagyon egyszerű ablakkezelő az AEWM alapján
+Comment[is]=Lágmarks gluggastjóri sem er byggður á AEWM
+Comment[it]=Un window manager minimalista basato su AEWM
+Comment[ja]=AEWM ベースの小さなウィンドウマネージャ
+Comment[ka]=მინიმალისტური ფანჯრის მენეჯერი AEWM-ის ბაზაზე
+Comment[kk]=AEWM-негіздеген шағын терезе менеджері
+Comment[km]=កម្មវិធី​គ្រប់គ្រង​បង្អួច​លក្ខណៈ​ពិសេស​តិច ដែល​ផ្អែក​លើ AEWM
+Comment[ko]=AEWM 기반 창 관리자
+Comment[lt]=Minimalistinė langų tvarkyklė, paremta AEWM
+Comment[lv]=Minimālistisks logu menedžeris bāzēts uz AEWM
+Comment[mk]=Минималистички менаџер на прозорци базиран на AEWM
+Comment[mn]=AEWM дээр суурилсан цонхны удирдагч
+Comment[mt]=Window manager minimu ibbażat fuq AEWM
+Comment[nb]=En minimalistisk vindusbehandler basert på AEWM
+Comment[nds]=En minimalistischen Finsterpleger, de op AEWM opbuut
+Comment[ne]=AEWM आधारित मिनिमलिस्ट सञ्झ्याल प्रबन्धक
+Comment[nl]=Een minimalistische windowmanager gebaseerd op AEWM
+Comment[nn]=Ein minimalistisk vindaugssjef som byggjer på AEWM
+Comment[pa]=AEWM ਤੇ ਆਧਾਰਿਤ ਨਿਊਨਤਮ ਝਰੋਖਾ ਮੈਨੇਜਰ
+Comment[pl]=Prosty menedżer okien stworzony na podstawie AEWM
+Comment[pt]=Um gestor de janelas minimalista baseado no AEWM
+Comment[pt_BR]=Um gerenciador de janelas minimalista baseado no AEWM
+Comment[ro]=Un manager de ferestre minimalistic bazat pe AEWM
+Comment[ru]=Минимальный оконный менеджер, основанный на AEWM
+Comment[rw]=Mugenga dirishya igira-nto ishingiye kuri AEWM
+Comment[se]=Minimalisttalaš lásegieđahalli, ráhkaduvvon AEWM vuođul
+Comment[sk]=Minimálny správca okien založený na AEWM
+Comment[sl]=Minimalističen okenski upravitelj na osnovi AEWM
+Comment[sr]=Минималистички менаџер прозора заснован на AEWM-у
+Comment[sr@Latn]=Minimalistički menadžer prozora zasnovan na AEWM-u
+Comment[sv]=Minimalistisk fönsterhanterare baserad på AEWM
+Comment[ta]=AEWM அடிப்படையிலான குறைதபட்ச சாளர மேளாளர்
+Comment[tg]=Мудири равзанаҳои хурд дар асоси AEWM
+Comment[th]=ระบบจัดการหน้าต่างขนาดเล็ก สร้างจากพื้นฐานของ AEWM
+Comment[tr]=AEWM tabanlı küçük bir pencere yöneticisi
+Comment[tt]=AEWM asılında ciñel täräcä-idäräçe
+Comment[uk]=Мінімалістичний менеджер вікон, заснований на AEWM
+Comment[uz]=AEWM asosida yaratilgan juda oddiy oyna boshqaruvchi
+Comment[uz@cyrillic]=AEWM асосида яратилган жуда оддий ойна бошқарувчи
+Comment[vi]=Một trình quản lý cửa sổ đơn giản dựa trên AEWM
+Comment[wa]=On manaedjeu di purneas minimå båzé so AEWM
+Comment[zh_CN]=基于 AEWM 的最小化窗口管理器
+Comment[zh_TW]=一個基於 AEWM 的小型化視窗管理程式
diff --git a/kdm/kfrontend/sessions/fluxbox.desktop b/kdm/kfrontend/sessions/fluxbox.desktop
new file mode 100644
index 000000000..df6a0813c
--- /dev/null
+++ b/kdm/kfrontend/sessions/fluxbox.desktop
@@ -0,0 +1,81 @@
+[Desktop Entry]
+Type=XSession
+Exec=startfluxbox
+TryExec=startfluxbox
+Name=Fluxbox
+Name[bn]=ফ্লাক্সবক্স
+Name[cy]=Llif-flwch (Fluxbox)
+Name[eo]=Fluujo
+Name[hi]=फ्ल्क्स-बाक्स
+Name[ne]=फ्लक्सबक्स
+Name[pa]=ਫਕਸਬਕਸ
+Name[rw]=AgasandukuUmujyoumwe
+Name[ta]=ப்ளக்ஸ் பெட்டி
+Name[te]=ఫ్లక్స్ బాక్స్
+Name[tg]=Қуттии Flux
+Comment=A highly configurable and low resource window manager based on Blackbox
+Comment[af]='n Baie konfigureerbare en lae hulpbron intensiewe venster bestuurder, wat op Blackbox gebaseer is.
+Comment[ar]=مدير نوافذ قابل للإعداد بشكل كبير ويستخدم القليل من موارد الجهاز وهو مبني Blackbox
+Comment[be]=Кіраўнік вокнаў, заснаваны на Blackbox, з вялікімі здольнасцямі змяняцца і з нізкай патрабавальнасцю да рэсурсаў
+Comment[bn]=ব্ল্যাকবক্স ভিত্তিক একটি উইণ্ডো ম্যানেজার, যা নানাভাবে কনফিগার করা যায়, কিন্তু খুবই কম রিসোর্স ব্যবহার করে
+Comment[bs]=Visoko prilagodljiv window manager sa malim zauzećem resursa baziran na Blackbox-u
+Comment[ca]=Un gestor de finestres altament configurable i que necessita pocs recursos basat en Blackbox
+Comment[cs]=Vysoce přizpůsobitelný a nízkoúrovňový správce oken založený na Blackboxu
+Comment[csb]=Menedżer òknów òpiarti na Blackbox ò wiôldżich mòżnotach kònfigùracëji ë nisczich żądaniach
+Comment[cy]=Trefnydd ffenestri hawdd ei ffurfweddu ac ysgafn ei ddefnydd adnoddau, wedi'i seilio ar Ddu-flwch (Blackbox)
+Comment[da]=En meget indstillelig vindueshåndtering med lavt ressourceforbrug baseret på Blackbox
+Comment[de]=Sehr flexibler, aber ressourcenschonender Fenstermanager, der auf Blackbox basiert
+Comment[el]=Ένας ιδιαίτερα παραμετροποιήσιμος και με μικρή κατανάλωση πόρων διαχειριστής παραθύρων βασισμένος στον Blackbox
+Comment[eo]=Fenestroadministrilo devenigita de Negrujo
+Comment[es]=Un gestor de ventanas basado en Blackbox muy configurable y con poco consumo de recursos
+Comment[et]=Väga hästi kohandatav ja vähenõudlik aknahaldur, mille aluseks on Blackbox
+Comment[eu]=Blackbox-en oinarritutako leiho kudeatzaile zeharo konfiguragarria, eta errekurtso gutxi kontsumitzen dituena
+Comment[fa]=یک مدیر پنجره با قابلیت پیکربندی بالا و منبع پایین براساس Blackbox
+Comment[fi]=Blackboxiin pohjautuva erittäin muokattava ja vähäisen resurssitarpeen omaava ikkunaohjelma
+Comment[fr]=Un gestionnaire de fenêtres très configurable et utilisant peu de ressources, fondé sur Blackbox
+Comment[fy]=In tige ynstelbere lichtgewicht finstersmanager basearre op Blackbox
+Comment[gl]=Un xestor de fiestras moi configurábel e lixeiro baseado en Blackbox
+Comment[he]=מנהל חלונות בעל הגדרות רבות הצורך משאבים מעטים ומבוסס עלBlackbox
+Comment[hi]=ब्लेक-बाक्स आधारित, अत्यंत कॉन्फ़िगरेबल तथा कम साधन चाहने वाला विंडो प्रबंधक
+Comment[hr]=Visoko konfigurabilan upravitelj prozora, male potrošnje resursa, zasnovan na Blackbox-u
+Comment[hu]=Egy Blackbox-alapú ablakkezelő, alacsony erőforrásigénnyel, sokféle beállítási lehetőséggel
+Comment[is]=Öflugur gluggastjóri sem notar lítið af auðlindum vélarinnar sem er byggður á Blackbox
+Comment[it]= Un window manager molto configurabile e che utilizza poche risorse basato su Blackbox
+Comment[ja]=Blackbox ベースの高度な設定が可能でリソースにやさしいウィンドウマネージャ
+Comment[ka]=კონფიგურირებადი ფანჯრის მენეჯერი Blackbox -ის ბაზაზე
+Comment[kk]=Blackbox-негіздеген, баптаулары көп, үнемді терезе менеджері
+Comment[km]=កម្មវិធី​គ្រប់គ្រង​បង្អួច​ដែល​អាច​កំណត់​រចនាសម្ព័ន្ធ​បាន​ខ្ពស់ និង​ប្រើ​ធនធាន​តិច ដែល​ផ្អែក​លើ Blackbox
+Comment[ko]=Blackbox 기반의 다양하게 설정 가능하고 자원을 적게 사용하는 창 관리자
+Comment[lt]=Daug konfigūravimo parinkčių turinti ir mažai resursų naudojanti langų tvarkyklė, paremta Blackbox
+Comment[lv]=Plaši konfigurējams un resursus taupošs logu menedžeris bāzēts uz Blackbox
+Comment[mk]=Високо конфигурабилен менаџер на прозорци со мали побарувања на ресурси базиран на Blackbox
+Comment[mn]=Хар хайрцаг дээр суурилсан маш бага нөөц хэрэглэдэг тохируулах чадвар өндөр цонхны удирдагч
+Comment[mt]=Window manager ħafif u konfigurabbli ibbażat fuq BlackBox
+Comment[nb]=En lite ressurskrevende og svært fleksibel vindusbehandler basert på Blackbox
+Comment[nds]=En Finsterpleger, de wenig Ressourcen bruukt, man bannig vele Instellen hett. Opbuut op Blackbox
+Comment[ne]=कालो बाकसमा आधारित उच्च कन्फिगरयोग्य र न्यून संसाधन सञ्झ्याल प्रबन्धक
+Comment[nl]=Een zeer configureerbare lichtgewicht windowmanager gebaseerd op Blackbox.
+Comment[nn]=Ein lite ressurskrevjande og svært fleksibel vindaugssjef basert på Blackbox
+Comment[pa]=ਇੱਕ ਬਿਲਕੁੱਲ ਹਲਕਾ ਤੇ ਜਿਆਦਾ ਸੋਧਯੋਗ ਫਾਇਲ਼ ਮੈਨੇਜਰ
+Comment[pl]=Menedżer okien oparty na Blackbox o dużych możliwościach konfiguracji i niskich wymaganiach
+Comment[pt]=Um gestor de janelas bastante configurável e que usa poucos recursos, baseado no Blackbox
+Comment[pt_BR]=Um gerenciador de janelas altamente configurável e com baixo uso de recursos, baseado no Blackbox
+Comment[ro]=Un manager de ferestre foarte configurabil și mic, bazat pe Blackbox
+Comment[ru]=Настраиваемый оконный менеджер, основанный на Blackbox
+Comment[rw]=Mugenga dirishya mbonezwa mu buryo buhanitse kandi bikorana bike ishingiye ku Gasandukumukara
+Comment[se]=Lásegieđahalli vuođđoduvvon Blackbox:as, hui heivehahtti ja geavaha unnán resurssaid
+Comment[sk]=Veľmi konfigurovateľný a nenáročný správca okien založený na Blackbox
+Comment[sl]=Visoko nastavljiv in učinkovit okenski upravitelj na osnovi Blackboxa
+Comment[sr]=Врло подесив и незахтеван менаџер прозора заснован на Blackbox-у
+Comment[sr@Latn]=Vrlo podesiv i nezahtevan menadžer prozora zasnovan na Blackbox-u
+Comment[sv]=Mycket anpassningsbar fönsterhanterare med litet resursbehov baserad på Blackbox
+Comment[ta]=கறுப்புபெட்டி சார்ந்த அமைக்கக்கூடிய குறைந்த வள சாளர மேளாளர்
+Comment[tg]=Мудири равзанаҳои танзимшаванда дар асоси Blackbox
+Comment[th]=ระบบจัดการหน้าต่างที่สามารถปรับแต่งได้อย่างละ เอียด และใช้ทรัพยากรน้อย สร้างบนพื้นฐานของ Blackbox
+Comment[tr]=Blackbox temelli, kolayca özelleştirilebilir düşük kaynaklı bir pencere yöneticisi
+Comment[tt]=Blackbox asılında qorılğan, yaqşı caylana torğan täräcä-idäräçe
+Comment[uk]=Дуже гнучкий та невибагливий для ресурсів менеджер вікон, заснований на Blackbox
+Comment[vi]=Một trình quản lý cửa sổ rất dễ cấu hình và đòi hỏi ít tài nguyên dựa trên Blackbox
+Comment[wa]=On ledjir et foirt apontiåve manaedjeu di purneas båzé so Blackbox
+Comment[zh_CN]=基于 BlackBox 可深入配置且占用资源较少的窗口管理器
+Comment[zh_TW]=一個基於 Blackbox 且高可組態及低資源的視窗管理程式
diff --git a/kdm/kfrontend/sessions/flwm.desktop b/kdm/kfrontend/sessions/flwm.desktop
new file mode 100644
index 000000000..d6dc24010
--- /dev/null
+++ b/kdm/kfrontend/sessions/flwm.desktop
@@ -0,0 +1,77 @@
+[Desktop Entry]
+Type=XSession
+Exec=flwm
+TryExec=flwm
+Name=FLWM
+Name[eo]=RMFA
+Name[hi]=एफएलडबल्यूएम
+Name[te]=ఎఫ్ ఎల్ డబ్ల్యు ఎం
+Name[th]=ตัวจัดการหน้าต่าง FLWM
+Comment=The Fast Light Window Manager, based primarily on WM2
+Comment[af]=Die Vinnige, ligte venster bestuurder, wat op WM2 gebaseer is
+Comment[ar]=مدير النوافذ السريع الخفيف، مبني بشكل أساسي على WM2
+Comment[be]=The Fast Light Window Manager, заснаваны на WM2
+Comment[bn]=ফাস্ট লাইট উইণ্ডো ম্যানেজার, WM2 ভিত্তি করে তৈরি
+Comment[bs]=Fast Light Window Manager, baziran uglavnom na WM2
+Comment[ca]=El gestor de finestres Fast Light, primerament basat en WM2
+Comment[cs]=Fast Light Window Manager založený původně na WM2
+Comment[csb]=Fast Light Window Manager, menedżer òknów ùsôdzony przédno na WM2
+Comment[cy]=Y Trefnydd Ffenestri Chwim ac Ysgafn, wedi'i seilio ar WM2 am y rhan fwyaf
+Comment[da]=Fast Light vindueshåndtering, baseret først og fremmest på WM2
+Comment[de]=Der Fast Light Window Manager, der vor allem auf WM2 basiert
+Comment[el]=Ο Fast Light διαχειριστής παραθύρων, βασισμένος κυρίως στον WM2
+Comment[eo]=Rapida malpeza fenestroadministrilo
+Comment[es]=El Fast Light Window Manager, basado principalmente en WM2
+Comment[et]=Kiire ja vähenõudlik aknahaldur, aluseks peamiselt WM2
+Comment[eu]=Batez ere WM2-en oinarritutako leiho kudeatzaile arin eta bizkorra
+Comment[fa]=مدیر پنجرۀ سریع و سبک، در اصل بر اساس WM2
+Comment[fi]=Fast Light Window Manager. Pohjautuu suurimmaksi osin WM2:een
+Comment[fr]=The Fast Light Window Manager, fondé au départ sur WM2
+Comment[fy]=De Fast Light Window Manager, primêr basearre op WM2
+Comment[gl]=O Fast Light Window Manager, un xestor de fiestras lixeiro baseado en WM2
+Comment[he]=The Fast Light Window Manager, מבוסס בעיקר על WM2
+Comment[hi]=मुख्यतः डबल्यूएम2 पर आधारित तेज और हल्का विंडो प्रबंधक
+Comment[hr]=Brzi i lagani upravitelj prozora (FLWM), zasnovan na WM2
+Comment[hu]=Fast Light Window Manager ablakkezelő, elsősorban a WM2 alapján
+Comment[is]=Léttur og hraðvirkur gluggastjóri sem er aðallega byggður á WM2
+Comment[it]=Il Fast Light Window Manager, basato principalmente su WM2
+Comment[ja]="高速で軽い"WM2 ベースのウィンドウマネージャ
+Comment[ka]=სწრაფი და მსუბუქი ფანჯრების მენეჯერი WM2 -ის ბაზაზე
+Comment[kk]=WM2-негіздеген жеңіл және жедел терезе менеджері
+Comment[km]=Fast Light Window Manager ផ្អែក​ចម្បង​លើ WM2
+Comment[ko]=WM2에 기반을 둔 빠르고 가벼운 창 관리자
+Comment[lt]=Greita ir lengva langų tvarkyklė, paremta daugiausiai WM2
+Comment[lv]=Fast Light logu menedžeris, bāzēts galvenokārt uz WM2
+Comment[mk]=Fast Light Window Manager, менаџер примарно базиран на WM2
+Comment[mn]=Ерөнхийдөө WM2 дээр суурилсан хурдан хөнгөн цонхны удирдагч
+Comment[mt]=Fast Light Window Manager, ibbażat fuq WM2
+Comment[nb]=En rask, lett vindusbehandler (Fast Light Window Manager), mest basert på WM2
+Comment[nds]=De "Fast Light"-Finsterpleger, to'n gröttsten Deel opbuut op WM2
+Comment[ne]=साधारणतया WM2 मा आधारित छिटो हल्का सञ्झ्याल प्रबन्धक
+Comment[nl]=De Fast Light Window Manager, primair gebaseerd op WM2.
+Comment[nn]=Ein rask, lett vindaugssjef (Fast Light Window Manager), mest basert på WM2
+Comment[pa]=ਇੱਕ ਹਲਕਾ ਤੇਜ਼ ਫਾਇਲ਼ ਮੈਨੇਜਰ, ਜੋ ਕਿ WM2 ਤੇ ਆਧਾਰਿਤ ਹੈ
+Comment[pl]=Fast Light Window Manager, menedżer okien stworzony głownie na podstawie WM2
+Comment[pt]=O Fast Light Window Manager, baseado em primeiro lugar no WM2
+Comment[pt_BR]=Acrônimo para Fast Light Window Manager (gerenciador rápido e leve), baseado primeiramente no WM2
+Comment[ro]=Fast Light Window Manager, bazat în principal pe WM2
+Comment[ru]=Быстрый и лёгкий оконный менеджер, основанный на WM2
+Comment[rw]=Mugenga Dirishya Yihuta Yoroshye, ishingiye mbere na mbere kuri WM2
+Comment[se]=Jođánis, geahpes lásegieđahalli, ráhkaduvvon WM2 vuođul
+Comment[sk]=The Fast Light Window Manager založený na WM2
+Comment[sl]=Fast Light Window Manager, v glavnem na osnovi WM2
+Comment[sr]=Брзи лаки менаџер прозора (FLWM), заснован на WM2
+Comment[sr@Latn]=Brzi laki menadžer prozora (FLWM), zasnovan na WM2
+Comment[sv]=Fönsterhanteraren Fast Light Window Manager, i huvudsak baserad på WM2
+Comment[ta]=WM2 அடிப்படையிலான வேக ஒளி வேக சாளர மேளாளர்
+Comment[tg]=мудири равзанаҳои тез ва осон дар асоси WM2
+Comment[th]=ระบบจัดการหน้าต่างที่เร็วและเบา สร้างบนพื้นฐานของ WM2
+Comment[tr]=Fast Light Pencere Yöneticisi
+Comment[tt]=WM2 asılında qorılğan, citez ciñel täräcä-idäräçe
+Comment[uk]=Швидкий легкий менеджер вікон, заснований здебільшого на WM2
+Comment[uz]=Asosan WM2 asosida yaratilgan tez va oddiy oyna boshqaruvchi
+Comment[uz@cyrillic]=Асосан WM2 асосида яратилган тез ва оддий ойна бошқарувчи
+Comment[vi]=Trình Quản lý Cửa sổ Nhanh và Nhẹ, dựa chủ yếu vào WM2
+Comment[wa]=Li Roed et Ledjir Manaedjeu di Purneas (ast Light Window Manager) båzé so WM2
+Comment[zh_CN]=又快又轻巧的窗口管理器,主要基于 WM2
+Comment[zh_TW]=一個主要基於 WM2 且快速及輕量化的視窗管理程式
diff --git a/kdm/kfrontend/sessions/fvwm.desktop b/kdm/kfrontend/sessions/fvwm.desktop
new file mode 100644
index 000000000..20ae57d4c
--- /dev/null
+++ b/kdm/kfrontend/sessions/fvwm.desktop
@@ -0,0 +1,71 @@
+[Desktop Entry]
+Type=XSession
+Exec=fvwm
+TryExec=fvwm
+Name=FVWM
+Name[hi]=एफ़वीडबल्यूएम
+Name[te]=ఎఫ్ వి డబ్ల్యు ఎం
+Comment=A powerful ICCCM-compliant multiple virtual desktop window manager
+Comment[af]='n Kragtige, ICCCM-aanpasbare veelvuldige virtuele werkskermvenster bestuurder.
+Comment[ar]=مدير نوافذ قوي ومتوافق مع ICCCM ذي أسطح مكتب وهمية متعددة
+Comment[be]=Магутны ICCCM-сумяшчальны кіраўнік вокнаў з падтрымкай віртуальных працоўных сталоў
+Comment[bn]= একটি শক্তিশালী ICCCM-compliant উইণ্ডো ম্যানেজার, যাতে একাধিক ভার্চুয়াল ডেস্কটপ সম্ভব
+Comment[bs]=Moćan ICCCM-sukladan window manager sa podrškom za više virtuelnih desktopa
+Comment[ca]=Un poderós gestor de finestres per a múltiples escriptoris virtuals que compleix amb ICCCM
+Comment[csb]=Stolemny menedżer òknów zgòdny z ICCCM òbsłëgòwujący wirtualné pùltë
+Comment[cy]=Trefnydd ffenestri pwerus efo penbyrddau rhith lluosol, sy'n cydymffurfio â ICCCM
+Comment[da]=En kraftig ICCCM-kompliant vindueshåndtering med flere virtuelle desktoppe
+Comment[de]=Ein leistungsfähiger ICCCM-kompatibler Fenstermanager mit virtuellen Arbeitsflächen
+Comment[el]=Ένας πολύ δυνατός, συμβατός με το ICCCM, διαχειριστής παραθύρων με πολλαπλές εικονικές επιφάνειες εργασίας
+Comment[eo]=Fenestroadministrilo
+Comment[es]=Un potente gestor de ventanas, compatible con ICCCM y que soporta varios escritorios virtuales
+Comment[et]=Võimas ICCCM nõuetele vastav mitme virtuaalse töölauaga aknahaldur
+Comment[eu]=ICCCM konpatiblea den, eta mahaigain birtual ugari dituen leiho kudeatzaile bortitza
+Comment[fa]=یک مدیر پنجرۀ رومیزی مجازی چندگانه ICCCM-compliant نیرومند
+Comment[fi]=Tehokas ICCCM-mukautuva virtuaalityöpöytiä tukeva ikkunaohjelma
+Comment[fr]=Un gestionnaire de fenêtres puissant compatible ICCCM avec gestion de bureaux virtuels multiples
+Comment[fy]=In krêftige ICCCM-compliant finstersmanager mei meardere buroblêden
+Comment[gl]=Un xestor de fiestras potente acorde coa ICCCM con múltiplos escritórios virtuais
+Comment[he]=מנהל חלונות עצמתי עם תאימות ל־ICCCM בעל שולחנות עבודה וירטואליים רבים
+Comment[hi]=शक्तिशाली आईसीसीसीएम-कम्पलाएंट अनेक आभासी डेस्कटॉप विंडो प्रबंधक
+Comment[hr]=Comment=Moćni, ICCCM kompatibilan, upravitelj prozora s više virtualnih radnih površina
+Comment[hu]=Egy sokoldalú, ICCCM-kompatibilis ablakkezelő, virtuális munkaasztal-kezeléssel
+Comment[is]=Öflugur ICCCM samhæfður gluggastjóri með sýndarskjáborðum
+Comment[it]=Un window manager molto potente e ICCCM-compatibile che supporta i desktop virtuali
+Comment[ja]=複数の仮想デスクトップをサポートした ICCCM 準拠のパワフルなウィンドウマネージャ
+Comment[ka]=ძლიერი ICCCM-თავსებადი ვირტულური სამუშაო დაფების მხარდამჭერი ფანჯრის მენეჯერი
+Comment[kk]=Қуатты ICCCM-үйлесімді, көп виртуалды үстел қолдайтын терезе менеджері
+Comment[km]=កម្មវិធី​គ្រប់គ្រង​បង្អួច​ផ្ទៃតុ​និមិត្ត​ច្រើន​ដ៏​មាន​អានុភាព ដែល​អនុវត្ត​តាម ICCCM
+Comment[ko]=다중 가상 데스크톱을 사용하는 ICCCM 호환 창 관리자
+Comment[lt]=galinga, su ICCCM suderinama daugelio virtualių darbastalių langų tvarkyklė
+Comment[lv]=Spēcīgs ICCCM-savietojams logu menedžeris ar vairāku darbvirsmu atbalstu
+Comment[mk]=Моќен менаџер на прозорци со повеќе виртуелни површини во согласност со ICCCM
+Comment[mt]=Window manager b'saħħtu, konformi ma' ICCCM, b'desktops virtwali.
+Comment[nb]=En slagkraftig vindusbehandler med flere virtuelle skrivebord, som støtter ICCCM
+Comment[nds]=En kraftvulle, ICCCM-kompatible Finsterpleger mit vele virtuelle Schriefdischen
+Comment[ne]=शक्तिशाली ICCCM-मान्ने बहुविध अवास्तविक डेस्कटप सञ्झ्याल प्रबन्धक
+Comment[nl]=Een krachtige ICCCM-compliant windowmanager met meerdere bureaubladen
+Comment[nn]=Ein slagkraftig vindaugssjef med fleire virtuelle skrivebord, som støttar ICCCM
+Comment[pa]=ਇੱਕ ਸ਼ਕਤੀਸ਼ਾਲੀ ICCCM-ਅਨੁਕੂਲ ਬਹੁ-ਫਰਜ਼ੀ ਵੇਹੜਿਆਂ ਵਾਲਾ ਝਰੋਖਾ ਮੈਨੇਜਰ
+Comment[pl]=Potężny menedżer okien zgodny z ICCCM obsługujący wirtualne pulpity
+Comment[pt]=Um gestor de janelas poderoso em conformidade com o ICCCM e que suporta vários ecrãs virtuais
+Comment[pt_BR]=Um poderoso gerenciador de janelas compatível com o ICCM, com suporte a múltiplas áreas de trabalho virtuais
+Comment[ro]=Un manager de ferestre puternic compliant ICCCM ce suportă ecrane virtuale
+Comment[ru]=Мощный ICCCM-совместимый оконный менеджер, поддерживающий виртуальные рабочие столы
+Comment[rw]=igikoranisha-ICCCM nyembaraga mugenga dirishya y'ibiro bitandukanye bitagaragara
+Comment[se]=Fápmolaš ICCCM-heivvolaš lásegieđahalli mas lea virtuealla čállinbeavddit
+Comment[sk]=Výkonný správca okien kompatibilný s ICCCM s podporou virtuálnych plôch
+Comment[sl]=Močan okenski upravitelj z večimi navideznimi namizji in popolnoma v skladu z ICCCM
+Comment[sr]=Моћни, ICCCM-сагласни, менаџер прозора са више виртуелних радних површина
+Comment[sr@Latn]=Moćni, ICCCM-saglasni, menadžer prozora sa više virtuelnih radnih površina
+Comment[sv]=Kraftfull fönsterhanterare med flera virtuella skrivbord som följer ICCCM
+Comment[ta]=ICCCM-தரத்தில் பலதரப்பட்ட மெய்நிகர் மேல் மேசை சாளர மேளாளர்
+Comment[tg]=Мудири равзанаҳои бузург дар асоси ICCCM дорои мудири равзанаҳо бо мизикориҳои виртуалӣ
+Comment[th]=ระบบจัดการหน้าต่างที่มีหลายพื้นที่ทำงานเสมือน สอดรับกับ ICCCM ประสิทธิภาพสูง
+Comment[tr]=Güçlü ICCCM-uyumlu çoklu sanal masaüstü yöneticisi
+Comment[tt]=ICCCM-ütkän, küp öställär totqan köçle täräzä-idäräçe
+Comment[uk]=Потужний, сумісний з ICCCM менеджер вікон, з підтримкою віртуальних стільниць
+Comment[vi]=Trình quản lý cửa sổ tương thích với ICCCM nhiều chức năng, quản lý nhiều màn hình nền ảo
+Comment[wa]=On manaedjeu di purneas avou multi-forveyowès sicribannes ki rote avou ICCM
+Comment[zh_CN]=强大的多虚拟桌面窗口管理器,与 ICCCM 兼容
+Comment[zh_TW]=一個強大的 ICCCM 相容的多重虛擬桌面視窗管理程式
diff --git a/kdm/kfrontend/sessions/fvwm2.desktop b/kdm/kfrontend/sessions/fvwm2.desktop
new file mode 100644
index 000000000..1a25a91e4
--- /dev/null
+++ b/kdm/kfrontend/sessions/fvwm2.desktop
@@ -0,0 +1,70 @@
+[Desktop Entry]
+Type=XSession
+Exec=fvwm2
+TryExec=fvwm2
+Name=FVWM2
+Name[te]=ఎఫ్ వి డబ్ల్యు ఎం 2
+Comment=A powerful ICCCM-compliant multiple virtual desktop window manager
+Comment[af]='n Kragtige, ICCCM-aanpasbare veelvuldige virtuele werkskermvenster bestuurder.
+Comment[ar]=مدير نوافذ قوي ومتوافق مع ICCCM ذي أسطح مكتب وهمية متعددة
+Comment[be]=Магутны ICCCM-сумяшчальны кіраўнік вокнаў з падтрымкай віртуальных працоўных сталоў
+Comment[bn]= একটি শক্তিশালী ICCCM-compliant উইণ্ডো ম্যানেজার, যাতে একাধিক ভার্চুয়াল ডেস্কটপ সম্ভব
+Comment[bs]=Moćan ICCCM-sukladan window manager sa podrškom za više virtuelnih desktopa
+Comment[ca]=Un poderós gestor de finestres per a múltiples escriptoris virtuals que compleix amb ICCCM
+Comment[csb]=Stolemny menedżer òknów zgòdny z ICCCM òbsłëgòwujący wirtualné pùltë
+Comment[cy]=Trefnydd ffenestri pwerus efo penbyrddau rhith lluosol, sy'n cydymffurfio â ICCCM
+Comment[da]=En kraftig ICCCM-kompliant vindueshåndtering med flere virtuelle desktoppe
+Comment[de]=Ein leistungsfähiger ICCCM-kompatibler Fenstermanager mit virtuellen Arbeitsflächen
+Comment[el]=Ένας πολύ δυνατός, συμβατός με το ICCCM, διαχειριστής παραθύρων με πολλαπλές εικονικές επιφάνειες εργασίας
+Comment[eo]=Fenestroadministrilo
+Comment[es]=Un potente gestor de ventanas, compatible con ICCCM y que soporta varios escritorios virtuales
+Comment[et]=Võimas ICCCM nõuetele vastav mitme virtuaalse töölauaga aknahaldur
+Comment[eu]=ICCCM konpatiblea den, eta mahaigain birtual ugari dituen leiho kudeatzaile bortitza
+Comment[fa]=یک مدیر پنجرۀ رومیزی مجازی چندگانه ICCCM-compliant نیرومند
+Comment[fi]=Tehokas ICCCM-mukautuva virtuaalityöpöytiä tukeva ikkunaohjelma
+Comment[fr]=Un gestionnaire de fenêtres puissant compatible ICCCM avec gestion de bureaux virtuels multiples
+Comment[fy]=In krêftige ICCCM-compliant finstersmanager mei meardere buroblêden
+Comment[gl]=Un xestor de fiestras potente acorde coa ICCCM con múltiplos escritórios virtuais
+Comment[he]=מנהל חלונות עצמתי עם תאימות ל־ICCCM בעל שולחנות עבודה וירטואליים רבים
+Comment[hi]=शक्तिशाली आईसीसीसीएम-कम्पलाएंट अनेक आभासी डेस्कटॉप विंडो प्रबंधक
+Comment[hr]=Comment=Moćni, ICCCM kompatibilan, upravitelj prozora s više virtualnih radnih površina
+Comment[hu]=Egy sokoldalú, ICCCM-kompatibilis ablakkezelő, virtuális munkaasztal-kezeléssel
+Comment[is]=Öflugur ICCCM samhæfður gluggastjóri með sýndarskjáborðum
+Comment[it]=Un window manager molto potente e ICCCM-compatibile che supporta i desktop virtuali
+Comment[ja]=複数の仮想デスクトップをサポートした ICCCM 準拠のパワフルなウィンドウマネージャ
+Comment[ka]=ძლიერი ICCCM-თავსებადი ვირტულური სამუშაო დაფების მხარდამჭერი ფანჯრის მენეჯერი
+Comment[kk]=Қуатты ICCCM-үйлесімді, көп виртуалды үстел қолдайтын терезе менеджері
+Comment[km]=កម្មវិធី​គ្រប់គ្រង​បង្អួច​ផ្ទៃតុ​និមិត្ត​ច្រើន​ដ៏​មាន​អានុភាព ដែល​អនុវត្ត​តាម ICCCM
+Comment[ko]=다중 가상 데스크톱을 사용하는 ICCCM 호환 창 관리자
+Comment[lt]=galinga, su ICCCM suderinama daugelio virtualių darbastalių langų tvarkyklė
+Comment[lv]=Spēcīgs ICCCM-savietojams logu menedžeris ar vairāku darbvirsmu atbalstu
+Comment[mk]=Моќен менаџер на прозорци со повеќе виртуелни површини во согласност со ICCCM
+Comment[mt]=Window manager b'saħħtu, konformi ma' ICCCM, b'desktops virtwali.
+Comment[nb]=En slagkraftig vindusbehandler med flere virtuelle skrivebord, som støtter ICCCM
+Comment[nds]=En kraftvulle, ICCCM-kompatible Finsterpleger mit vele virtuelle Schriefdischen
+Comment[ne]=शक्तिशाली ICCCM-मान्ने बहुविध अवास्तविक डेस्कटप सञ्झ्याल प्रबन्धक
+Comment[nl]=Een krachtige ICCCM-compliant windowmanager met meerdere bureaubladen
+Comment[nn]=Ein slagkraftig vindaugssjef med fleire virtuelle skrivebord, som støttar ICCCM
+Comment[pa]=ਇੱਕ ਸ਼ਕਤੀਸ਼ਾਲੀ ICCCM-ਅਨੁਕੂਲ ਬਹੁ-ਫਰਜ਼ੀ ਵੇਹੜਿਆਂ ਵਾਲਾ ਝਰੋਖਾ ਮੈਨੇਜਰ
+Comment[pl]=Potężny menedżer okien zgodny z ICCCM obsługujący wirtualne pulpity
+Comment[pt]=Um gestor de janelas poderoso em conformidade com o ICCCM e que suporta vários ecrãs virtuais
+Comment[pt_BR]=Um poderoso gerenciador de janelas compatível com o ICCM, com suporte a múltiplas áreas de trabalho virtuais
+Comment[ro]=Un manager de ferestre puternic compliant ICCCM ce suportă ecrane virtuale
+Comment[ru]=Мощный ICCCM-совместимый оконный менеджер, поддерживающий виртуальные рабочие столы
+Comment[rw]=igikoranisha-ICCCM nyembaraga mugenga dirishya y'ibiro bitandukanye bitagaragara
+Comment[se]=Fápmolaš ICCCM-heivvolaš lásegieđahalli mas lea virtuealla čállinbeavddit
+Comment[sk]=Výkonný správca okien kompatibilný s ICCCM s podporou virtuálnych plôch
+Comment[sl]=Močan okenski upravitelj z večimi navideznimi namizji in popolnoma v skladu z ICCCM
+Comment[sr]=Моћни, ICCCM-сагласни, менаџер прозора са више виртуелних радних површина
+Comment[sr@Latn]=Moćni, ICCCM-saglasni, menadžer prozora sa više virtuelnih radnih površina
+Comment[sv]=Kraftfull fönsterhanterare med flera virtuella skrivbord som följer ICCCM
+Comment[ta]=ICCCM-தரத்தில் பலதரப்பட்ட மெய்நிகர் மேல் மேசை சாளர மேளாளர்
+Comment[tg]=Мудири равзанаҳои бузург дар асоси ICCCM дорои мудири равзанаҳо бо мизикориҳои виртуалӣ
+Comment[th]=ระบบจัดการหน้าต่างที่มีหลายพื้นที่ทำงานเสมือน สอดรับกับ ICCCM ประสิทธิภาพสูง
+Comment[tr]=Güçlü ICCCM-uyumlu çoklu sanal masaüstü yöneticisi
+Comment[tt]=ICCCM-ütkän, küp öställär totqan köçle täräzä-idäräçe
+Comment[uk]=Потужний, сумісний з ICCCM менеджер вікон, з підтримкою віртуальних стільниць
+Comment[vi]=Trình quản lý cửa sổ tương thích với ICCCM nhiều chức năng, quản lý nhiều màn hình nền ảo
+Comment[wa]=On manaedjeu di purneas avou multi-forveyowès sicribannes ki rote avou ICCM
+Comment[zh_CN]=强大的多虚拟桌面窗口管理器,与 ICCCM 兼容
+Comment[zh_TW]=一個強大的 ICCCM 相容的多重虛擬桌面視窗管理程式
diff --git a/kdm/kfrontend/sessions/fvwm95.desktop b/kdm/kfrontend/sessions/fvwm95.desktop
new file mode 100644
index 000000000..2bbd1c403
--- /dev/null
+++ b/kdm/kfrontend/sessions/fvwm95.desktop
@@ -0,0 +1,76 @@
+[Desktop Entry]
+Type=XSession
+Exec=fvwm95
+TryExec=fvwm95
+Name=FVWM95
+Name[hi]=एफ़वीडबल्यूएम95
+Name[te]=ఎఫ్ వి డబ్ల్యు ఎం 95
+Comment=A Windows 95 look-alike derivative of FVWM
+Comment[af]='n FVWM venster bestuurder wat soos Windows 95 lyk
+Comment[ar]=شبيه بـWin95 ومشتق من FVWM
+Comment[be]=Вытворная ад FVWM, падобная на Windows 95
+Comment[bn]=FVWM-ভিত্তিক Windows 95-এর মত দেখতে একটি উইণ্ডো ম্যানেজার
+Comment[bs]=Derivacija FVWM nalik na Windows 95
+Comment[ca]=Un semblant a Win95 derivat de FVWM
+Comment[cs]=Správce oken se vzhledem Windows 95 odvozený od FVWM
+Comment[csb]=Pòchòdzący z FVWM menedżer òknów ò wëzdrzatkù szlachùjącym za Windows 95
+Comment[cy]=Deilliant o FVWM sy'n edrych yn debyg i Windows95
+Comment[da]=En Windows 95-lignende vindueshåndtering afledt af FVWM
+Comment[de]=Ein Abkömmling von FVWM im Stil von Windows 95
+Comment[el]=Ένας παρόμοιος με τα Windows 95 διαχειριστής παραθύρων προερχόμενος από τον FVWM
+Comment[eo]=Fenestroadministrilo kiel tiu de Vindozo 95
+Comment[es]=Un derivado de FVWM de aspecto similar a Win95
+Comment[et]=FVWM derivaat, mis näeb välja nagu Windows 95
+Comment[eu]=Windows 95en itxura duen FVWMren ondorengo bat
+Comment[fa]=ویندوز ۹۵ شبیه مشتق FVWM
+Comment[fi]=Windows 95:lta näyttävä FVWM-johdannainen
+Comment[fr]=Un gestionnaire de fenêtres ressemblant à Windows 95 dérivant de FVWM
+Comment[fy]=In op Windows 95 lykjende fariant fan FVWM
+Comment[gl]=Un xestor de fiestras coma o de Windows 95 derivado de FVWM
+Comment[he]=נגזרת של FVWM הנראה כמו חלונות 95
+Comment[hi]=विंडोज़ 95 जैसा दिखने वाला एफ़वीडबल्यूएम का प्रतिरूप
+Comment[hr]=Derivat FVWM-a nalik na Windows 95
+Comment[hu]=Win95-szerű FVWM-változat
+Comment[is]=Útgáfa af FVWM sem líkist Windows 95
+Comment[it]=Una variante di FVWM che assomiglia a Windows 95
+Comment[ja]=Windows95 に似た外見の FVWM 派生ウィンドウマネージャ
+Comment[ka]=FVWM კლონი Windows 95-ის სტილში
+Comment[kk]=Windows 95 секілді FVWM-негіздеген терезе менеджері
+Comment[km]=ស្រដៀង Windows 95 ដែល​ក្លាយ​មក​ពី FVWM
+Comment[ko]=FVWM의 윈도 95를 닮은 변종
+Comment[lt]=Windows 95 išvaizdą primenanti FVWM atmaina
+Comment[lv]=Windows 95 līdzīgs FVWM atvasinājums
+Comment[mk]=Деривација на FVWM менаџерот со изглед на Windows 95
+Comment[mn]=FVWM -ээс уламжилсан Виндовс 95 шиг харагдалттай
+Comment[ms]=Terbitan FVWM yang menyerupai Windows 95
+Comment[mt]=Derivattiv ta' FVWM jixbaħ lil Windows 95
+Comment[nb]=En variant av FVWM som ser ut som Windows 95
+Comment[nds]=Süht ut as A Windows 95 un is vun FVWM afleddt
+Comment[ne]=विण्डोज ९५ FVWM को डेरिभेटिभ जस्तो छ
+Comment[nl]=Een op Windows 95 lijkende variant van FVWM
+Comment[nn]=Ein variant av FVWM som ser ut som Windows 95
+Comment[pa]=ਵਿੰਡੋ 95 ਵਰਗਾ FVWM ਦਾ ਇੱਕ ਰੂਪ
+Comment[pl]=Wywodzący się z FVWM menedżer okien o wyglądzie podobnym do Windows 95
+Comment[pt]=Uma derivação do FVWM parecida com o Windows 95
+Comment[pt_BR]=Um derivado do FVWM com a aparência do Windows 95
+Comment[ro]=O versiune de FVWM cu aspect de Windows 95
+Comment[ru]=Клон FVWM в стиле Windows 95
+Comment[rw]=Windows 95 isa nk'ikomoka kuri FVWM
+Comment[se]=FVWM-vuođđoduvvon lásegieđahalli mii lea Windows 95-lágan
+Comment[sk]=Správca okien podobný Windows 95 založený na FVWM
+Comment[sl]=Izpeljanka FVWM v izgledu Windows 95
+Comment[sr]=Дериват FVWM-а налик на Windows 95
+Comment[sr@Latn]=Derivat FVWM-a nalik na Windows 95
+Comment[sv]=Fönsterhanterare som liknar Windows 95 med ursprung i FVWM
+Comment[ta]=FVWM லிருந்து தோன்றிய விண்டோஸ் 95 ஐப் போன்ற
+Comment[tg]=Windows 95 look-монанди маҳсулии FVWM
+Comment[th]=FVWM ที่ถูกทำให้ดูเหมือนวินโดวส์ 95
+Comment[tr]=Windows 95'e benzeyen bir pencere yönetimi
+Comment[tt]=Windows 95 küreneşendä FVWM qabatlama
+Comment[uk]=Похідний від FVWM з виглядом Windows 95
+Comment[uz]=Win95'ga oʻxshagan FVWM'ning turi
+Comment[uz@cyrillic]=Win95'га ўхшаган FVWM'нинг тури
+Comment[vi]=Trình quản lý cửa sổ giống Windows 95, hậu duệ của FVWM
+Comment[wa]=On manaedjeu di purneas rishonnant a Windows 95 et k' est båzé so FVWM
+Comment[zh_CN]=一个与 Windows 95 外观类似的 FVWM 变种
+Comment[zh_TW]=一個由 FVWM 演化出來且外觀像 Win95 的視窗管理程式
diff --git a/kdm/kfrontend/sessions/gnome.desktop b/kdm/kfrontend/sessions/gnome.desktop
new file mode 100644
index 000000000..ed89391ec
--- /dev/null
+++ b/kdm/kfrontend/sessions/gnome.desktop
@@ -0,0 +1,81 @@
+[Desktop Entry]
+Type=XSession
+Exec=gnome-session
+TryExec=gnome-session
+Name=GNOME
+Name[bn]=গনোম
+Name[eo]=Gnomikuo
+Name[hi]=ग्नोम
+Name[ko]=세임 그놈
+Name[mn]=ГНОМЕ
+Name[ne]=जिनोम
+Name[sv]=Gnome
+Name[ta]=க்னோம்
+Name[te]=గ్నొమ్
+Comment=The GNU Network Object Model Environment. A complete, free and easy-to-use desktop environment
+Comment[af]=Die GNU 'Network Object Model Environment'. 'n Volledige, gratis en maklik om te gebruik werkskerm omgewing.
+Comment[ar]=بيئة نموذج الكائن الشبكي من GNU، بيئة سطح مكتبي حرّة وسهلة الاستخدام
+Comment[be]=The GNU Network Object Model Environment. Поўнае, свабоднае і простае ў выкарыстанні працоўнае асяроддзе
+Comment[bn]=দি গনিউ নেটওয়ার্ক অবজেক্টমডেল এনভায়রনমেন্ট। একটি পূর্ণ, মুক্ত এবং সহজেই ব্যবহারযোগ্য ডেস্কটপ এনভায়রনমেন্ট
+Comment[bs]=GNU Network Object Model Environment. Kompletna, slobodna i jednostavna za upotrebu desktop okolina
+Comment[ca]=El GNU Network Object Model Environment. Un complet, lliure i fàcil d'usar entorn d'escriptori
+Comment[cs]=GNOME, GNU Network Object Model Environment. Kompletní, svobodné a uživatelsky přívětivé garfické prostředí.
+Comment[csb]=GNU Network Object Model Environment (GNOME). Fùlwôrtné, wòlné ë prosté w brëkòwaniô òkrãżé pùltu
+Comment[cy]=Yr Amgylchedd Model Gwrthrych Rhwydwaith GNU (GNU Network Object Model Environment). Amgylchedd penbwrdd cyflawn, rhydd, a hawdd ei ddefnyddio.
+Comment[da]=GNU Network Object Model Environment. Et fuldstænding, frit og nemt at bruge desktopmiljø
+Comment[de]=Das GNU Network Object Model Environment. Eine vollständige, freie und leicht bedienbare Arbeitsumgebung
+Comment[el]=Το GNU Network Object Model Environment. Ένα πλήρης, ελεύθερο και εύκολο στη χρήση περιβάλλον επιφάνειας εργασίας
+Comment[eo]=Plena labortabla ĉirkaŭaĵo
+Comment[es]=El GNU Network Object Model Environment, un entorno de escritorio completo, libre y fácil de usar
+Comment[et]=GNU Network Object Model Environment on täielik, vaba ja väga hõlpsasti kasutatav töölaua keskkond
+Comment[eu]=GNU Network Object Model Environment. mahaigain-ingurune oso, libre eta erabilterraza
+Comment[fa]=محیط مدل شئ شبکه گنو. محیط رومیزی کامل، آزاد و آسان برای استفاده.
+Comment[fi]=GNU Network Object Model Environment. Valmis, vapaa ja helppokäyttöinen työpöytäympäristö
+Comment[fr]=The GNU Network Object Model Environment. Un environnement de bureau complet, gratuit et facile à utiliser
+Comment[fy]=De GNU Network Object Model Environment, In komplete, frije en ienfâldige te brûken buroblêd omwrâld
+Comment[gl]=O GNU Network Object Model Environment. Un entorno de escritório completo, libre e de uso doado
+Comment[he]=The GNU Network Object Model Environment. סביבת עבודה מלאה, חופשית וקלה לשימוש
+Comment[hi]=जीएनयू नेटवर्क ऑब्जेक्ट मॉडल एनवायरनमेंट. एक संपूर्ण, उपयोग में आसान डेस्कटॉप वातावरण
+Comment[hr]=GNOME - GNU Network Object Model Environment - Cjelokupno, besplatno i jednostavno okruženje radne površine
+Comment[hu]=GNU Network Object Model Environment (GNOME), egy teljes, ingyenes, könnyen kezelhető grafikus környezet
+Comment[is]=GNU Network Object Model Environment er fullkomið og fjrálst skjáborðsumhverfi sem er auðvelt að nota
+Comment[it]=Il GNU Network Object Model Environment. Un ambiente desktop completo, libero e facile da usare
+Comment[ja]=GNU オブジェクトモデル環境, 完全にフリーで使いやすいデスクトップ環境
+Comment[ka]=GNU Network Object Model Environment - სრული, თავისუფალი და ადვილად გამოყენებადი სამუშაო გარემო
+Comment[kk]=GNU Network Object Model Environment. Толық, еркін таратылатын, ыңғайлы графикалық орта
+Comment[km]=GNU Network Object Model Environment ។ បរិស្ថាន​ផ្ទៃតុ​ពេញលេញ, ឥត​គិត​ថ្លៃ និង​ងាយស្រួល​ប្រើ
+Comment[ko]=GNU Network Object Model Environment(그놈). 완전한 자유 소프트웨어 데스크톱 환경입니다.
+Comment[lt]=GNU tinklo objektų modeliavimo aplinka. Savarankiška, laisva ir lengvai naudojama darbastalio aplinka
+Comment[lv]=GNU Network Object Model Environment. Pilnvērtīga bezmaksas viegli lietojama darba vide.
+Comment[mk]=GNU Network Object Model Environment. Работна околина која е комплетна, слободна и едноставна за користење
+Comment[mn]=GNU Network Object Model Environment. Бүрэн, үнэгүй хэрэглэхэд хялбар дэлгэцийн системийн орчин
+Comment[mt]=GNU Network Object Model Environment. Ambjent grafiku komplet, ħieles u faċli tużah.
+Comment[nb]=GNU Network Object Model Environment. Et skrivebordsmiljø som er komplett, fritt og lett å bruke.
+Comment[nds]=De "GNU Network Object Model Environment". En komplette Schriefdisch-Ümgeven, ümsunst un eenfach to bruken
+Comment[ne]=GNU सञ्जाल बस्तु नमूना वातावरण । पूर्ण, स्वतन्त्र र प्रयोग गर्न सजिलो डेस्कटप वातावरण
+Comment[nl]=De GNU Network Object Model Environment, een complete, vrije en eenvoudig te gebruiken desktop environment.
+Comment[nn]=GNU Network Object Model Environment. Eit skrivebordsmiljø som er komplett, fritt og lett å bruka.
+Comment[pa]=GNU Network Object Model Environment, ਇੱਕ ਸੰਪੂਰਨ, ਮੁਫਤ ਅਤ ਵਰਤਣ ਵਿੱਚ ਅਤਿ ਆਸਾਨ ਵੇਹੜਾ ਵਾਤਾਵਰਣ
+Comment[pl]=GNU Network Object Model Environment (GNOME). Pełne, wolne i łatwe w użyciu środowisko pulpitu
+Comment[pt]=O GNU Network Object Model Environment. Um ambiente de trabalho completo, livre e fácil de usar
+Comment[pt_BR]=Acrônimo para GNU Network Object Model Environment ou Ambiente de Modelo de Objetos de Rede GNU; um ambiente de trabalho completo, livre e fácil de usar
+Comment[ro]=GNU Network Object Model Environment. Un mediu grafic complet, gratuit și ușor de utilizat
+Comment[ru]=GNU Network Object Model Environment - полная, свободная и лёгкая в использовании графическая среда
+Comment[rw]=Ibikikije Moderi Igikoresho Urusobemiyoboro GNU. Ibikikije by'ibiro byuzuye, by'ubuntu kandi byoroshye-gukoresha.
+Comment[se]=GNU Netword Object Model Environment: Čállinbeavdebiras mii lea aibbas fridja ja álki geavahit.
+Comment[sk]=The GNU Network Object Model Environment. Úplné, voľne šíriteľné a ľahko použiteľné pracovné prostredie
+Comment[sl]=GNU Network Object Model Environment. Popolno, prosto in preposto namizno okolje
+Comment[sr]=„GNU Network Object Model Environment“(Gnome, Гном). Потпуно, бесплатно и лако за коришћење радно окружење
+Comment[sr@Latn]=„GNU Network Object Model Environment“(Gnome, Gnom). Potpuno, besplatno i lako za korišćenje radno okruženje
+Comment[sv]=GNU Network Object Model Environment. En fullständig, fri och lättanvänd skrivbordsmiljö
+Comment[ta]=GNU மாதிரி வலை பொருள் சூழல்.முழுமையான , இலவச சுலபமாக பயன்படுத்தக்கூடிய மேல்மேசை சூழல்
+Comment[th]=GNU Network Object Model Environment สภาพแวดล้อมสำหรับเดสก์ทอปที่ครบถ้วน, ฟรี และใช้งานง่าย
+Comment[tr]=GNU Network Object Model Environment (GNOME)
+Comment[tt]=The GNU Network Object Model Environment. Qullanu öçen ciñel, buşlay, tulıçaralı östäl möxite
+Comment[uk]=The GNU Network Object Model Environment. Повнофункціональне, вільне та зручне графічне середовище
+Comment[uz]=GNOME (GNU Network Object Model Environment) - mukammal, erkin va foydalanish uchun juda qulay ish stoli muhiti
+Comment[uz@cyrillic]=GNOME (GNU Network Object Model Environment) - мукаммал, эркин ва фойдаланиш учун жуда қулай иш столи муҳити
+Comment[vi]=Môi trường Mạng Mô hình Đối tượng của GNU. Một môi trường màn hình nền đầy đủ, tự do và dễ sử dụng
+Comment[wa]=L' evironmint di modele di cayets d' rantoele GNU (GNU Network Object Model Environment). On complet evironmint d' sicribanne, libe et åjhey a -z eployî.
+Comment[zh_CN]=GNU 网络对象模型环境。完整、自由、易用的桌面环境
+Comment[zh_TW]=GNU 網路物件模型環境。一個完整,免費且容易使用的桌面環境
diff --git a/kdm/kfrontend/sessions/golem.desktop b/kdm/kfrontend/sessions/golem.desktop
new file mode 100644
index 000000000..3ba7f2b41
--- /dev/null
+++ b/kdm/kfrontend/sessions/golem.desktop
@@ -0,0 +1,81 @@
+[Desktop Entry]
+Type=XSession
+Exec=golem
+TryExec=golem
+Name=Golem
+Name[bn]=গোলেম
+Name[eo]=Golemo
+Name[hi]=गोलेम
+Name[ne]=गोलेम
+Name[pa]=ਗੋਲੀਮ
+Name[te]=గొలెం
+Comment=A lightweight window manager
+Comment[af]='n Lig gewig venster bestuurder
+Comment[ar]=مدير نوافر خفيف العبء
+Comment[be]=Лёгкі кіраўнік вокнаў
+Comment[bn]=একটি হাল্কা উইণ্ডো ম্যানেজার
+Comment[bs]=Lagani window manager
+Comment[ca]=Un lleuger gestor de finestres KDE
+Comment[cs]=Malý správce oken
+Comment[csb]=Menedżer òknół ò môłëch żądaniach
+Comment[cy]=Trefnydd ffenestri ysgafn
+Comment[da]=En letvægtsvindueshåndtering
+Comment[de]=Eine schlanker Fenstermanager
+Comment[el]=Ένας ελαφρύς διαχειριστής παραθύρων
+Comment[eo]=Malpeza fenestroadministrilo
+Comment[es]=Un gestor de ventanas ligero
+Comment[et]=Imeväike aknahaldur
+Comment[eu]=Leiho kudeatzaile arina
+Comment[fa]=یک مدیر پنجرۀ سبک
+Comment[fi]=Kevyt ikkunaohjelma
+Comment[fr]=Un gestionnaire de fenêtres très léger
+Comment[fy]=In lichtgewicht finstersmanager
+Comment[gl]=Un xestor de fiestras lixeiro
+Comment[he]=מנהל החלונות קל משקל
+Comment[hi]=एक हल्का विंडो प्रबंधक
+Comment[hr]=Lagani upravitelj prozora
+Comment[hu]=Egy nagyon egyszerű ablakkezelő
+Comment[is]=Léttur gluggastjóri
+Comment[it]=Un window manager leggero
+Comment[ja]=軽量なウィンドウマネージャ
+Comment[ka]=მსუბუქი ფანჯრი მენეჯერი
+Comment[kk]=Жеңіл терезе менеджері
+Comment[km]=កម្មវិធី​គ្រប់គ្រង​បង្អួច​សមត្ថភាព​ខ្សោយ
+Comment[ko]=가벼운 창 관리자
+Comment[lt]=Nedaug resursų eikvojanti langų tvarkyklė
+Comment[lv]=Viegls logu menedžeris
+Comment[mk]=Лесен менаџер на прозорци
+Comment[mn]=Хөнгөн цонхны удирдагч
+Comment[ms]=Pengurus tetingkap ringan
+Comment[mt]=Window manager ħafif
+Comment[nb]=En lettvekts vindusbehandler
+Comment[nds]=En ranke Finsterpleger
+Comment[ne]=हल्का वजन सञ्झ्याल प्रबन्धक
+Comment[nl]=Een lichtgewicht windowmanager
+Comment[nn]=Ein lett vindaugssjef
+Comment[pa]=ਇੱਕ ਹਲਕਾ ਝਰੋਖਾ ਮੈਨੇਜਰ
+Comment[pl]=Menedżer okien o małych wymaganiach
+Comment[pt]=Um gestor de janelas leve
+Comment[pt_BR]=Um gerenciador de janelas leve
+Comment[ro]=Un manager de ferestre foarte mic
+Comment[ru]=Лёгкий оконный менеджер
+Comment[rw]=Mugenga dirishya yoroshye
+Comment[se]=Geahpes lásegieđahalli
+Comment[sk]=Nenáročný správca okien
+Comment[sl]=Lahek okenski upravitelj
+Comment[sr]=Лагани менаџер прозора
+Comment[sr@Latn]=Lagani menadžer prozora
+Comment[sv]=Lättviktig fönsterhanterare
+Comment[ta]=குறைவான எடை உடைய சாளர மேலாளர்
+Comment[te]=తెలికైన విండొ అభికర్త
+Comment[tg]=Мудири тирезаи сабук
+Comment[th]=ระบบจัดการหน้าต่างขนาดเบา
+Comment[tr]=Hızlı çalışan bir pencere yöneticisi
+Comment[tt]=Ciñel täräzä-idäräçe
+Comment[uk]=Швидкий менеджер вікон
+Comment[uz]=Oddiy oyna boshqaruvchi
+Comment[uz@cyrillic]=Оддий ойна бошқарувчи
+Comment[vi]=Trình quản lý cửa sổ nhẹ ký
+Comment[wa]=On ledjir manaedjeu di purneas
+Comment[zh_CN]=轻量级窗口管理器
+Comment[zh_TW]=一個輕量化的視窗管理程式
diff --git a/kdm/kfrontend/sessions/icewm.desktop b/kdm/kfrontend/sessions/icewm.desktop
new file mode 100644
index 000000000..8b70361fa
--- /dev/null
+++ b/kdm/kfrontend/sessions/icewm.desktop
@@ -0,0 +1,81 @@
+[Desktop Entry]
+Type=XSession
+Exec=icewm-session
+TryExec=icewm-session
+Name=IceWM
+Name[eo]=GlaciFA
+Name[hi]=आइस-डबल्यूएम
+Name[lo]=ຕົວຈັດການຫນ້າຕ່າງ IceWM
+Name[sv]=Ice WM
+Name[te]=ఐస్ డబ్ల్యు ఎం
+Name[th]=ตัวจัดการหน้าต่าง IceWM
+Comment=A Windows 95-OS/2-Motif-like window manager
+Comment[af]='n Windows 95-OS/2-Motif tipe venster bestuurder
+Comment[ar]=مدير نوافذ مشابه لـ Win95-OS/2-Motif
+Comment[be]=Кіраўнік вокнаў, падобны на Windows 95-OS/2-Motif
+Comment[bn]=Windows 95-OS/2-Motif-এর অনুরূপ একটি উইণ্ডো ম্যানেজার
+Comment[bs]=Window manager nalik na Windows 95-OS/2-Motif
+Comment[ca]=Un gestor de finestres com els de Windows 95-OS/2-Motif
+Comment[cs]=Správce oken podobný Windows 95-OS/2-Motif
+Comment[csb]=Menedżer òknół szlachùjący za Windows 95-OS/2-Motif
+Comment[cy]=Trefnydd ffenestri sy'n debyg i Windows95-OS/2-Motif
+Comment[da]=En Windows 95-OS/2-Motif-lignende vindueshåndtering
+Comment[de]=Fenstermanager im Stil von Windows 95, OS/2 und Motif
+Comment[el]=Ένας διαχειριστής παραθύρων παρόμοιος με τα Windows 95-OS/2-Motif
+Comment[eo]=Fenestroadministrilo kiel Vindozo 95, OS/2 kaj Motifo
+Comment[es]=Un gestor de ventanas similar a Win95-OS/2-Motif
+Comment[et]=Aknahaldur, mis näeb välja nagu Windows 95-OS/2-Motif
+Comment[eu]=Windows 95 OS/2 Motif-en itxura duen leiho kudeatzailea
+Comment[fa]=یک مدیر پنجره شبیه Windows 95-OS/2-Motif
+Comment[fi]=Windows 95:n ja OS/2-Motifin tyylinen ikkunaohjelma
+Comment[fr]=Un gestionnaire de fenêtres ressemblant à Windows 95-OS/2-Motif
+Comment[fy]=In Win95-OS/2-Motif-likens finstersmanager
+Comment[gl]=Un xestor de fiestras coma o de Windows 95-OS/2-Motif
+Comment[he]=מנהל חלונות מבוסס Motif הדומה במראהו לחלונות 95/OS-2
+Comment[hi]=विंडोज़ 95-ओएस/2-मोटिफ जैसा विंडो प्रबंधक
+Comment[hr]=Upravitelj prozora nalik na Windows 95/OS/2/Motif
+Comment[hu]=Win95-OS/2-Motif-szerű ablakkezelő
+Comment[is]=Gluggastjóri sem líkist 95-OS/2-Motif
+Comment[it]=Un window manager in stile Windows 95-OS/2-Motif
+Comment[ja]=Windows95, OS/2, Motif に似たウィンドウマネージャ
+Comment[ka]=ფანჯრების მენეჯერი Windows95-OS/2-Motif სტილში
+Comment[kk]=Windows 95-OS/2-Motif-секілді терезе менеджері
+Comment[km]=កម្មវិធី​គ្រប់គ្រង​បង្អួច​ដែល​ដូច Windows 95-OS/2-Motif
+Comment[ko]=윈도 95, OS/2, Motif를 닮은 창 관리자
+Comment[lt]=A Windows 95-OS/2-Motif-primenanti langų tvarkyklė
+Comment[lv]=Windows 95 - OS/2 - Motif līdzīgs logu menedžeris
+Comment[mk]=Менаџер на прозорци со изглед на Windows 95, OS/2 и Motif
+Comment[mn]=Виндовс 95-OS/2-Motif-шиг цонхны удирдагч
+Comment[ms]=Pengurus tetingkap seperti Motif Windows 95-OS/2
+Comment[mt]=Window manager jixbaħ lill-Windows 95-OS/2-Motif
+Comment[nb]=En vindusbehandler som likner Windows 95-OS/2-Motif
+Comment[nds]=Finsterpleger, de utsüht as Windows 95-OS/2-Motif
+Comment[ne]=विण्डोज ९५-OS/2-मोटिफ जस्तो सञ्झ्याल प्रबन्धक
+Comment[nl]=Een Win95-OS/2-Motif-achtige windowmanager
+Comment[nn]=Ein vindaugssjef som liknar Windows 95-OS/2-Motif
+Comment[pa]=ਇੱਕ ਵਿੰਡੋ 95-OS/2-Motif-ਵਰਗਾ ਝਰੋਖਾ ਮੈਨੇਜਰ
+Comment[pl]=Menedżer okien podobny do Windows 95-OS/2-Motif
+Comment[pt]=Um gestor de janelas parecido com o Windows 95, o OS/2 e o Motif
+Comment[pt_BR]=Um gerenciador de janelas parecido com Windows 95-OS/2-Motif
+Comment[ro]=Un manager de ferestre cu aspect de Windows 95, OS/2 sau Motif
+Comment[ru]=Оконный менеджер в стиле Windows95-OS/2-Motif
+Comment[rw]=Windows 95-OS/2-umutako-nka mugenga dirishya
+Comment[se]=Windows 95-OS/2-Motif-lágan lásegieđahalli
+Comment[sk]=Správca okien podobný Windows 95-OS/2-Motif
+Comment[sl]=Okenski upravitelj, podoben Windows 95, OS/2 in Motifu
+Comment[sr]=Менаџер прозора налик на Windows 95/OS/2/Motif
+Comment[sr@Latn]=Menadžer prozora nalik na Windows 95/OS/2/Motif
+Comment[sv]=Fönsterhanterare som liknar Windows 95-OS/2-Motif
+Comment[ta]=சாளரங்கள் 95-OS/2-மாடிஃப்-லைக் சாளர மேலாளர்
+Comment[te]=విండొస్ 95-ఒఎస్/2 -మొటిఫ్ లాంటి విండొ అభికర్త
+Comment[tg]=Windows 95-OS/2-Motif-монанди мудири тиреза
+Comment[th]=ระบบจัดการหน้าต่างของที่ดูคล้าย วินโดวส์ 95-โอเอสทู-โมทิฟ
+Comment[tr]=Windows 95-OS/2-Motif benzeri bir pencere yöneticisi
+Comment[tt]=Windows 95-OS/2-Motif küreneşendä täräzä-idäräçe
+Comment[uk]=Менеджер вікон на кшталт Windows 95-OS/2-Motif
+Comment[uz]=Win95-OS/2-Motif'ga oʻxshash oyna boshqaruvchi
+Comment[uz@cyrillic]=Win95-OS/2-Motif'га ўхшаш ойна бошқарувчи
+Comment[vi]=Trình quản lý cửa sổ với mô típ kiểu Windows 95
+Comment[wa]=On manaedjeu di purneas rishonnant Windows95-OS/2-Motif
+Comment[zh_CN]=类似 Windows-OS/2-Motif 的窗口管理器
+Comment[zh_TW]=一個像 Win95-OS/2-Motif 的視窗管理程式
diff --git a/kdm/kfrontend/sessions/ion.desktop b/kdm/kfrontend/sessions/ion.desktop
new file mode 100644
index 000000000..c4ae9ca04
--- /dev/null
+++ b/kdm/kfrontend/sessions/ion.desktop
@@ -0,0 +1,77 @@
+[Desktop Entry]
+Type=XSession
+Exec=ion
+TryExec=ion
+Name=Ion
+Name[bn]=আয়ন (Ion)
+Name[eo]=Iono
+Name[hi]=आयन
+Name[ja]=ION
+Name[ko]=카메룬
+Name[pa]=ਲੋਨ
+Name[te]=ఐయాన్
+Name[th]=ไอออน
+Comment=A keyboard-friendly window manager with tiled windows, based on PWM
+Comment[af]='n Sleutelbord vriendelike venster bestuurder, met geteëlde vensters. Dis op PWM gebaseer.
+Comment[ar]=مدير نوافذ سهل استخدام لوحة المفاتيح ذي نوافذ معنونة، مبني على PWM
+Comment[be]=Кіраўнік вокнаў для працы з клавіятурай, заснаваны на PWM
+Comment[bn]=PWM ভিত্তিক উইণ্ডো ম্যানেজার, যা কীবোর্ড দিয়ে নিয়ন্ত্রণ করা সহজ
+Comment[bs]=Window manager za tastaturu sa popločanim prozorima, baziran na PWM
+Comment[ca]=Un gestor de finestres amigable amb el teclat i amb finestres alicatades, basat en PWM
+Comment[csb]=Menedżer òknów z dobrą òbsłëgą klawiaturë ë kachelkòwaniém òknów, ùsôdzony na spòdlém PWM
+Comment[cy]=Trefnydd ffenestri sy'n hawdd ei ddefnyddio o'r alweddell, efo ffenestri wedi'u teilio, wedi'i seilio ar PVM
+Comment[da]=En tastaturvenlig vindueshåndtering med fliselagte vinduer, baseret på PWM
+Comment[de]=Tastaturfreundlicher Fenstermanager mit gekachelten Fenstern, basiert auf PWM
+Comment[el]=Ένας φιλικός με το πληκτρολόγιο διαχειριστής παραθύρων με υποστήριξη παραθύρων σε παράθεση, βασισμένος στον PWM
+Comment[eo]=Fenestroadministrilo por la uzo klaviara
+Comment[es]=Un gestor de ventanas utilizable desde el teclado con mosaico de ventanas, basado en PWM
+Comment[et]=Klaviatuurisõprade aknahaldur paanitud akendega, aluseks PWM
+Comment[eu]=PWMn oinarritutako leiho-mosaikoa duen leiho kudeatzailea, teklatutik erabil daitekeena
+Comment[fa]=یک مدیر پنجرۀ صفحه کلید پسند با پنجره‌های کاشی‌شده، براساس PWM
+Comment[fi]=Näppäimistöystävällinen ikkunaohjelma
+Comment[fr]=Un gestionnaire de fenêtres utilisable au clavier avec des fenêtres en mosaïque, fondé sur PWM
+Comment[fy]=In toetseboerdfreonlike finstersmanager mei tegele finsters. basearre op PWM
+Comment[gl]=Un xestor de fiestras de manexo co teclado e fiestras en mosaico baseado en PWM
+Comment[he]=מנהל חלונות ידידותי למקלדת עם חלונות פרושים המבוסס על PWM
+Comment[hi]=पीडबल्यूएम आधारित चटाई विंडो युक्त विंडो प्रबंधक जो कुंजीपट फ्रेडली है
+Comment[hr]=Comment=Upravitelj prozora s popločenim prozorima, namijenjen tipkovnici i zasnovan na PWM-u
+Comment[hu]=Egy billentyűzetről könnyen kezelhető ablakkezelő, mozaikszerű ablakelrendezéssel, a PWM alapján
+Comment[is]=Gluggastjóri sem er gott að vinna í með lyklaborðinu einu, byggður á PWM
+Comment[it]=Un window manager "amico della tastiera" con finestre affiancate, basato su PWM
+Comment[ja]=PWM ベースのキーボード操作に適したタイル表示式のウィンドウマネージャ
+Comment[ka]=კლავიატურით მართვადი ფანჯრების მენეჯერი, PWM-ის ბაზაზე
+Comment[kk]=PWM-негіздеген, пернетақтадан басқарылатын, терезелері кезектескен, терезе менеджері
+Comment[km]=កម្មវិធី​គ្រប់គ្រង​បង្អួច​ដែល​ងាយ​ប្រើ​ជាមួយ​ក្ដារចុច ហើយ​អាច​រៀប​បង្អួច​ជា​ក្បឿង និង​ផ្អែក​លើ PWM
+Comment[lt]=Patogi dirbti su klaviatūra langų tvarkyklė su išklotais langais, paremta PWM
+Comment[lv]=Tastatūrai draudzīgs logu menedžeris ar tiled logiem, bāzēts uz PWM
+Comment[mk]=Менаџер на прозорци со поплочени прозорци базиран на PWM погоден за работа со тастатура
+Comment[mn]=PWM дээр суурилсан гараар ажиллахад тохиромжтой цонх удирдагч
+Comment[ms]=Pengurus tetingkap mesra papan kekunci dengan tetingkap berjubin, berdasarkan PWM
+Comment[mt]=Window manager ibbażat fuq PWM, b'sapport għal tastieri u windows imqassma fuiq madum
+Comment[nb]=En tastaturvennlig vindusbehandler med flislagte vinduer, basert på PWM
+Comment[nds]=En tastatuurfründliche Finsterpleger mit kachelte Finstern, opbuut op PWM
+Comment[ne]=शीर्षक सञ्झ्यालसँग सोझो सञ्झ्याल प्रबन्धक कुञ्जीपाटी
+Comment[nl]=Een toetsenbordvriendelijke windowmanager met getegelde vensters. Gebaseerd op PWM
+Comment[nn]=Ein tastaturvennleg vindaugssjef med flislagde vindauge, basert på PWM
+Comment[pa]=ਇੱਕ ਕੀ-ਬੋਰਡ ਸਹਾਇਕ ਝਰੋਖਾ ਮੈਨੇਜਰ, ਜੋ ਕਿ PWM ਤੇ ਆਧਾਰਿਤ ਹੈ
+Comment[pl]=Menedżer okien z dobrą obsługą klawiatury i kafelkowaniem okiem, stworzony na podstawie PWM
+Comment[pt]=Um gestor de janelas amigável para o teclado, com janelas lado-a-lado, baseado no PWM
+Comment[pt_BR]=Um gerenciador de janelas amigável com o teclado, com janelas ladrilhadas, baseado no PWM
+Comment[ro]=Un manager de ferestre ușor de utilizat din tastatură, cu ferestre ce pot fi așezate în mozaic, bazat pe PWM
+Comment[ru]=Управляемый с клавиатуры оконный менежер, основанный на PWM
+Comment[rw]=mugenga dirishya ya mwandikisho-yoroshyeikoresha ifite amadirishya agerekeranye, ishingiye kuri PWM
+Comment[se]=Boallobeavdeustitlaš lašegieđahalli, mas leat bálddalas láset, ráhkaduvvon PWM vuođul
+Comment[sk]=Správca okien s podporou kláves, usporiadania okien do dlaždíc, založený na PWM
+Comment[sl]=Tipkovnici prijazen okenski upravitelj z razdeljenimi okni, na osnovi PWM
+Comment[sr]=Пријатељски према тастатури менаџер прозора са наслаганим прозорима, заснован на PWM-у
+Comment[sr@Latn]=Prijateljski prema tastaturi menadžer prozora sa naslaganim prozorima, zasnovan na PWM-u
+Comment[sv]=Tangentbordsvänlig fönsterhanterare med fönster sida vid sida, baserad på PWM
+Comment[ta]=PWM அடிப்படையிலான விசைப்பலகையால் கையாளமுடிந்த சாளர மேளாளர்
+Comment[th]=ระบบจัดการหน้าต่างที่เป็นมิตรกับการใช้แป้นพิมพ์ พร้อมกับหน้าต่างที่ถูกปูเรียง สร้างจาก PWM
+Comment[tr]=PWM tabanlı, klavye dostu uzatılmış pencereleriyle bir pencere yönetici
+Comment[tt]=Täräzä bülü belän töylek-söygän täräzä-idäräçe, PWM asılında
+Comment[uk]=Менеджер вікон налаштований для легкого використання з клавіатурою, засновано на PWM
+Comment[vi]=Trình quản lý cửa sổ thiết kết thân thiện với việc dùng bàn phím, có các cửa sổ xếp ngói, dựa trên PWM
+Comment[wa]=On manaedjeu di purneas k' inme li taprece avou des purneas å pus grand, båzé so PWM
+Comment[zh_CN]=一个基于 PWM 的窗口管理器,适合键盘操作,可平铺窗口
+Comment[zh_TW]=一個基於 PWM 且方便鍵盤使用和支援棋盤化視窗的視窗管理程式
diff --git a/kdm/kfrontend/sessions/kde.desktop.in b/kdm/kfrontend/sessions/kde.desktop.in
new file mode 100644
index 000000000..b032d395a
--- /dev/null
+++ b/kdm/kfrontend/sessions/kde.desktop.in
@@ -0,0 +1,45 @@
+[Desktop Entry]
+Encoding=UTF-8
+Type=XSession
+Exec=@KDE_BINDIR@/startkde
+TryExec=@KDE_BINDIR@/startkde
+Name=KDE
+Name[hi]=केडीई
+Name[mn]=КДЭ
+Name[ta]=Kஏற்றக் காவலன்
+Name[xh]=iKDE
+Name[xx]=xxKDExx
+Comment=The K Desktop Environment. A powerful Open Source graphical desktop environment
+Comment[bs]=K Desktop Environment. Moćan grafički desktop otvorenog izvornog koda
+Comment[ca]=L'entorn d'escriptori K. Un poderós entorn d'escriptori gràfic de Codi Font Obert
+Comment[cy]=Yr Amgylchedd Penbwrdd K. Amgylchedd penbwrdd graffegol pwerus, sy'n gôd-agored.
+Comment[da]=K Skrivebordsmiljøet. Et kraftigt, åbent, grafisk skrivebordsmiljø
+Comment[de]=Das K Desktop Environment. Eine mächtige, graphische Arbeitsumgebung und Open Source / Freie Software
+Comment[el]=Το K Desktop Environment. Ένα πανίσχυρο ελεύθερης προέλευσης γραφικό περιβάλλον επιφάνειας εργασίας
+Comment[es]=El Entorno de Escritorio K, un potente entorno de escritorio gráfico realizado de código abierto
+Comment[et]=K töölaua keskkond on võimas vaba tarkvara graafiline töölaua keskkond
+Comment[fi]=KDE-työpöytäympäristö (K Desktop Environment) on tehokas avoimen lähdekoodin graafinen työpöytäympäristö
+Comment[fr]=The K Desktop Environment. Un environnement de bureau graphique, puissant et Open Source
+Comment[he]=The K Desktop Environment. סביבת עבודה גרפית, בעלת-עוצמה בקוד פתוח
+Comment[hi]=के डेस्कटॉप वातावरण. एक शक्तिशाली, ओपन सोर्स चित्रमय डेस्कटॉप वातावरण
+Comment[hu]=A KDE grafikus munkakörnyezet, egy szabad forráskódú grafikus ablakkezelő környezet
+Comment[it]=L'ambiente desktop KDE. Un potente ambiente desktop grafico Open Source
+Comment[mn]=The K Desktop Environment. Хүчирхэг нээлттэй эх код бүхий график дэлгэцийн орчин
+Comment[nb]=K Desktop Environment. Et kraftig grafisk skrivebordsmiljø med åpen kildekode.
+Comment[nl]=De K Desktop Environment, een krachtige open source grafische desktop environment
+Comment[nn]=K Desktop Environment. Eit kraftig grafisk skrivebordsmiljø med open kjeldekode.
+Comment[pl]=Środowisko KDE. Potężne środowisko graficzne Wolnego Oprogramowania.
+Comment[pt]=O K Desktop Environment. Um ambiente gráfico open source poderoso
+Comment[pt_BR]=Acrônimo para K Desktop Environment (ou Ambiente de Trabalho K). Um poderoso ambiente de trabalho gráfico de código aberto
+Comment[ro]=K Desktop Environment. Un mediu grafic cu surse deschise, foarte puternic
+Comment[sk]=The K Desktop Environment. Výkonné, voľne šíriteľné grafické pracovné prostredie
+Comment[sl]=Namizno okolje K. Zmogljivo grafično namizno okolje odprte kode
+Comment[sr]=K Desktop Environment (KDE). Моћно графичко радно окружење отвореног кода
+Comment[sv]=K-skrivbordsmiljön. En kraftfull grafisk skrivbordsmiljö med öppen källkod
+Comment[ta]= Kமேல்மேசை சூழல். சக்திவாய்ந்த திறந்த ஆணைமூல சித்திர வகை மேல்மேசை சூழல்
+Comment[tr]=KDE Masaüstü Yöneticisi. Güçlü bir grafiksel masaüstü ortamı
+Comment[uk]=The K Desktop Environment. Потужне графічне середовище з відкритими текстами
+Comment[uz]=KDE (K Desktop Environment) - кучли Open Source график иш столи муҳити
+Comment[vi]=môi trường desktop K, môi trường desktop đồ hoạ mã nguồn mở rất mạnh
+Comment[xx]=xxThe K Desktop Environment. A powerful Open Source graphical desktop environmentxx
+Comment[zh_CN]=K 桌面环境。强大的开放源代码图形桌面环境
diff --git a/kdm/kfrontend/sessions/larswm.desktop b/kdm/kfrontend/sessions/larswm.desktop
new file mode 100644
index 000000000..072b382c5
--- /dev/null
+++ b/kdm/kfrontend/sessions/larswm.desktop
@@ -0,0 +1,72 @@
+[Desktop Entry]
+Type=XSession
+Exec=larswm
+TryExec=larswm
+Name=LarsWM
+Name[eo]=Lazero
+Name[hi]=लार्स-डबल्यूएम
+Name[ko]=라이코스
+Name[sv]=Lars WM
+Name[te]=లార్స్ డబ్ల్యు ఎం
+Comment=The Lars Window Manager, based on 9WM, supports tiled windows
+Comment[af]=Die Lars venster bestuurder, wat op 9WM gebaseer is. Dit ondersteun geteëlde vensters.
+Comment[ar]=مدير نوافذ لارس، مبني على 9WM، يدعم النوافذ المعنونة
+Comment[be]=Кіраўнік вокнаў Lars Window Manager, заснаваны на 9WM
+Comment[bn]=লার্স উইণ্ডো ম্যানেজার, 9WM-এর ওপর ভিত্তি করে তৈরি
+Comment[bs]=Lars Window Manager, baziran 9WM, podržava popločane prozore
+Comment[ca]=El gestor de finestres Lars, basat en 9WM, permet finestres alicatades
+Comment[csb]=Menedżer òknów Larsa, ùsôdzony na spòdlém 9WM, wspiérô kachelkòwanié òknów
+Comment[cy]=Y Trefnydd Ffenestri Lars, wedi'i seilio ar 9WM, sy'n cynnal ffenestri wedi'u teilio
+Comment[da]=Lars vindueshåndteringen, baseret på 9WM, understøtter fliselagte vinduer
+Comment[de]=Lars-Fenstermanager, basiert auf 9WM und unterstützt gekachelte Fenstern
+Comment[el]=Ο διαχειριστής παραθύρων Lars, βασισμένος στον 9WM, υποστηρίζει παράθυρα σε παράθεση
+Comment[eo]=La Laza Fenestroadministrilo, devenigita de 9FA
+Comment[es]=El Lars Window Manager, basado en 9WM, soporta mosaico de ventanas
+Comment[et]=Larsi aknahaldur, mille aluseks on 9WM, toetab paanitud aknaid
+Comment[eu]=Lars leiho kudeatzailea, 9WM-n oinarritua, leiho-mosaikorako euskarria duena
+Comment[fa]=مدیر پنجرۀ Lars، بر اساس 9WM، پنجره‌های کاشی‌شده را پشتیبانی می‌کند.
+Comment[fi]=Lars-ikkunaohjelma. Pohjautuu 9WM:ään ja tukee järjestettyjä ikkunoita
+Comment[fr]=The Lars Window Manager, fondé sur 9WM, avec une gestion des fenêtres en mosaïque
+Comment[fy]=De Lars Window Manager, basearre op 9WM. Biedt stipe foar tegele finsters
+Comment[gl]=O Xestor de Fiestras Lars, baseado en 9WM, atura fiestras en mosaico
+Comment[he]=The Lars Window Manager, מבוסס על 9WM, תומך בחלונות פרושים
+Comment[hi]=9डबल्यूएम आधारित लार्स विंडो प्रबंधक, चटाई विडो समर्थित करता है
+Comment[hr]=Lars upravitelj prozora, zasnovan na 9WMu, podržava popločene prozore
+Comment[hu]=Lars ablakkezelője, a 9WM alapján, mozaikszerű ablakelrendezési lehetőséggel
+Comment[is]=Lars gluggastjórinn, byggður á 9WM og styður flísaða glugga
+Comment[it]=Il Window Manager di Lars, basato su 9WM, supporta le finestre affiancate
+Comment[ja]=Lars ウィンドウマネージャ, 9WMベース, タイル表示をサポートするウィンドウマネージャ
+Comment[kk]=9WM-негіздеген терезе менеджері
+Comment[km]=Lars Window Manager ផ្អែក​លើ 9WM គាំទ្រ​បង្អួច​ជា​ក្បឿង
+Comment[lt]=Lars langų tvarkyklė, paremta 9WM, turi galimybę iškloti langus
+Comment[mk]=Lars Window Manager, менаџер базиран на 9WM, поддржува поплочени прозорци
+Comment[mn]=The Lars Window Manager, 9WM дээр суурилсан, олон цонхтой
+Comment[ms]=Pengurus Tetingkap Lars, berdasarkan 9WM, menyokong tetingkap berjubin
+Comment[mt]=Lars Window Manager - ibbażat fuq 9WM u jiflaħ windows imqassma f'madum
+Comment[nb]=Lars vindusbehandler, basert på 9WM, støtter flislagte vinduer
+Comment[nds]=De Lars-Finsterpleger, opbuut op 9WM, ünnerstütt kachelte Finstern
+Comment[ne]=9WM मा आधारित लार्स सञ्झ्याल प्रबन्धक, टायल गरिएका सञ्झ्यालहरू समर्थन गर्छ
+Comment[nl]=De Lars Window Manager, gebaseerd op 9WM. Biedt ondersteuning voor getegelde vensters
+Comment[nn]=Vindaugssjefen Lars, basert på 9WM, støttar flislagde vindauge
+Comment[pa]=ਲਾਰਸ ਫਾਇਲ਼ ਮੈਨੇਜਰ 9WM ਤੇ ਆਧਾਰਿਤ, ਝਰੋਖਿਆਂ ਦੇ ਸਮੇਟਣ ਲਈ ਸਹਾਈ
+Comment[pl]=Menedżer okien Larsa, stworzony na podstawie 9WM, obsługuje kafelkowanie okien
+Comment[pt]=O Lars Window Manager, baseado no 9WM, e que suporta janelas lado-a-lado
+Comment[pt_BR]=O gerenciador de janelas Lars, baseado no 9WM, com suporte a janelas ladrilhadas
+Comment[ro]=Managerul de ferestre al lui Lars, bazat pe 9WM. Suportă ferestre aranjate în mozaic
+Comment[ru]=Оконный менеджер на основе 9wm
+Comment[rw]=Mugenga Dirishya Lars, ishingiye kuri 9WM, yemera amadirishya agerekeranye
+Comment[se]=Lásegieđahalli Lars, ráhkaduvvon 9WM vuođul, doarju bálddalas lásiid.
+Comment[sk]=The Lars Window Manager založený na 9WM s podporou okien usporiadaných do dlaždíc
+Comment[sl]=Larsov okenski upravitelj, na osnovi 9WM, podpira porazdeljena okna
+Comment[sr]=„Lars Window Manager“, заснован на 9WM-у, подржава наслагане прозоре
+Comment[sr@Latn]=„Lars Window Manager“, zasnovan na 9WM-u, podržava naslagane prozore
+Comment[sv]=Lars fönsterhanterare, baserad på 9WM, med stöd för fönster sida vid sida
+Comment[ta]=9WM அடிபடையிலான லார்ஸ் சாளர மேலாளர் சீரமைக்கப்பட்ட சாளரங்களை ஆதரிக்கிறது
+Comment[th]=ระบบจัดการหน้าต่าง Lars สร้างจาก 9WM สนับสนุนการปูเรียงหน้าต่าง
+Comment[tr]=Lars Pencere Yöneticisi
+Comment[tt]=Lars Window Manager, 9WM asılında, bülengän täräzä tota
+Comment[uk]=The Lars Window Manager, засновано на 9WM, підтримує розташування вікон плиткою
+Comment[vi]= Trình Quản lý Cửa sổ Lars, dựa vào 9WM, hỗ trợ cửa sổ xếp ngói
+Comment[wa]=Li manaedjeu d' purnea d' Lasrs (Lars Window Manager), båzé so 9WM, sopoite les purneas å pus grand
+Comment[zh_CN]=Lars 窗口管理器,基于 9WM,支持平铺窗口
+Comment[zh_TW]=The Lars 視窗管理程式,基於 9WM,支援棋盤化視窗
diff --git a/kdm/kfrontend/sessions/lwm.desktop b/kdm/kfrontend/sessions/lwm.desktop
new file mode 100644
index 000000000..2f3e0ff00
--- /dev/null
+++ b/kdm/kfrontend/sessions/lwm.desktop
@@ -0,0 +1,76 @@
+[Desktop Entry]
+Type=XSession
+Exec=lwm
+TryExec=lwm
+Name=LWM
+Name[eo]=MFA
+Name[hi]=एलडबल्यूएम
+Name[te]=ఎల్ డబ్ల్యు ఎం
+Name[th]=ตัวจัดการหน้าต่าง LWM
+Comment=The Lightweight Window Manager. A non-configurable, bare window manager
+Comment[af]=Die lig gewig venster bestuurder.
+Comment[ar]=مدير النوافذ خفيف العبء، مدير نوافذ مجرّد غير قابل للإعداد
+Comment[be]=The Lightweight Window Manager. Пусты кіраўнік вокнаў без падтрымкі настаўленняў
+Comment[bn]=দি লাইটওয়েট উইণ্ডো ম্যানেজার
+Comment[bs]=Lightweight Window Manager. Ne-podesivi, potpuno osnovni window manager
+Comment[ca]=El gestor de finestres Lightweight. Un gestor de finestres no configurable i pelat
+Comment[cs]=The Lightweight Window Manager. Prostý a nekonfigurovatelný správce oken
+Comment[csb]=Lightweight Window Manager (Letczi menedżer òknół). Bro prosti menedżer òknów bez mòżnoté kònfigùracëji
+Comment[cy]=Y Trefnydd Ffenestri Ysgafn. Trefnydd ffenestri noeth, na ellir ffurfweddu
+Comment[da]=Lightweight Window Manager, en ikke-indstillelig, minimal vindueshåndtering
+Comment[de]=Lightweight Window Manager -- reiner Fenstermanager ohne Einstellmöglichkeiten
+Comment[el]=Ο ελαφρύς διαχειριστής παραθύρων. Ένας μη παραμετροποιήσιμος, πολύ απλός διαχειριστής παραθύρων
+Comment[eo]=Malpeza Fenestroadministrilo
+Comment[es]=El Lightweight Window Manager, un sencillísimo y no configurable gestor de ventanas
+Comment[et]=Imeväike aknahaldur on seadistamatu, sõna otseses mõttes ainult akende haldur
+Comment[eu]=Libhtweight leiho kudeatzailea. Konfiguraziorik onartzen ez duen leiho-kudeatzaile zeharo sinplea
+Comment[fa]=مدیر پنجرۀ سبک، مدیر پنجرۀ غیرقابل پیکربندی و ساده
+Comment[fi]=Kevyt ikkunaohjelma. Paljas, ei muokattavissa oleva ikkunaohjelma
+Comment[fr]=The Lightweight Window Manager. Un gestionnaire de fenêtres non configurable et nu
+Comment[fy]=De Lightweight Window Manager, in net-ynstelbere, keale finstersmanager
+Comment[gl]=O Xestor de Fiestras Lixeiro.
+Comment[he]=The Lightweight Window Manager. מנהל חלונות מצומצם בלי אפשרויות להגדרה.
+Comment[hi]=हल्का विंडो प्रबंधक. खाली विंडो प्रबंधक जो कॉन्फ़िगर नहीं हो सकता
+Comment[hr]=LWM - Lightweight Window Manager (Lagani upravitelj prozora) - Temeljni upravitelj prozora bez mogućnosti konfiguriranja
+Comment[hu]=Lightweight Window Manager, egy könnyen konfigurálható, alapszintű ablakkezelő
+Comment[is]=Hinn létti gluggastjóri. Ekki stillanlegur og hrár
+Comment[it]=Il Window Manager Leggero. Un window manager essenziale e non configurabile
+Comment[ja]=設定項目のない、単純なウィンドウマネージャ
+Comment[ka]=მსუბუქი არაკონფიგურირებადი ფანჯრების მენეჯერი
+Comment[kk]=Lightweight Window Manager. Баптауы жоқ, жай терезе менеджері
+Comment[km]=កម្មវិធី​គ្រប់គ្រង​បង្អួច​សមត្ថភាព​ខ្សោយ ។ កម្មវិធី​គ្រប់គ្រង​បង្អួច​ដែល​គ្មាន​អ្វី​បិទបាំង ហើយ​មិន​អាច​កំណត់​រចនាសម្ព័ន្ធ​បាន
+Comment[lt]=„Lengva“ langų tvarkyklė. Nekonfigūruojama, „plika“ langų tvarkyklė
+Comment[lv]=Vieglais logu menedžeris. Nekonfigurējams, vienkāršs logu menedžeris
+Comment[mk]=Lightweight Window Manager. Неконфигурабилен, скоро празен менаџер на прозорци
+Comment[mn]=The Lightweight Window Manager. Тохируулгах боломжгүй,цонхны удирдлага
+Comment[ms]=Pengurus tetingkap Ringan. Tidak boleh konfigur, pengurus tetingkap terdedah
+Comment[mt]=Lightweight Window Manager. Window Manager sempliċi u mhux konfigurabbli
+Comment[nb]=Lettvekts vindusbehandler- Lightweight Window Manager. En enkel vindusbehandler uten innstillinger.
+Comment[nds]=De "Lightweight"-Finsterpleger. En reen Finsterpleger ahn Instellen
+Comment[ne]=हल्का वजन सञ्झ्याल प्रबन्धक । कन्फिगर गर्न नसकिने, बेयर सञ्झ्याल प्रबन्धक
+Comment[nl]=De Lightweight Window Manager, een niet-instelbare, kale windowmanager
+Comment[nn]=Lightweight Window Manager. Ein enkel vindaugssjef utan innstillingar.
+Comment[pa]=ਇੱਕ ਹਲਕਾ, ਨਾ-ਸੰਰਚਿਤਯੋਗ ਪੱਟੀ ਝਰੋਖਾ ਮੈਨੇਜਰ
+Comment[pl]=Lightweight Window Manager (Lekki menedżer okien). Surowy menedżer okien bez możliwości konfiguracji
+Comment[pt]=O Lightweight Window Manager. Um gestor de janelas não-configurável e básico
+Comment[pt_BR]=Um gerenciador de janelas leve, sendo básico e não muito configurável
+Comment[ro]=Lightweight Window Manager. Un manager de ferestre neconfigurabil și minimal
+Comment[ru]=Облегчённый, не настраиваемый простой оконный менеджер
+Comment[rw]=Mugenga Dirishya Yoroshye. Mugenga dirishya idafite ikintu, itabonezwa.
+Comment[se]=Lightweight Window Manager. Oktageardánis lásegieđahalli mii ii lea heivehahtti.
+Comment[sk]=The Lightweight Window Manager. Nenastaviteľný, jednoduchý správca okien
+Comment[sl]=Lahek okenski upravitelj. Ni nastavljiv, osnovni okenski upravitelj
+Comment[sr]=„Lightweight Window Manager“. Неподесив, голи менаџер прозора
+Comment[sr@Latn]=„Lightweight Window Manager“. Nepodesiv, goli menadžer prozora
+Comment[sv]=Den lättviktiga fönsterhanteraren. En enkel fönsterhanterare utan anpassningsmöjligheter
+Comment[ta]=குறைந்த எடையுள்ள சாளர மேலாளர். வடிவமைக்க முடியாத
+Comment[th]=Lightweight Window Manager ระบบจัดการหน้าต่างเปล่าๆ ที่ไม่สามารถปรับแต่งได้เลย
+Comment[tr]=Lightweight Pencere Yöneticisi. Yapılandırılamayan, kaba pencere yönetici
+Comment[tt]=Lightweight Window Manager. Caylanmí torğan, ciñel täräzä-idäräçe
+Comment[uk]=Невеличкий менеджер вікон без можливості налаштування
+Comment[vi]=Trình Quản lý Cửa sổ Nhẹ ký. Rất khó cấu hình
+Comment[wa]=Li Ledjir Manaedjeu di Purneas (Lightweight Window Manager). On manaedjeu di purneas tot simpe, nén apontiåve
+Comment[zh_CN]=轻量级窗口管理器。不可配置的裸窗口管理器
+Comment[zh_TW]=一個輕量化的視窗管理程式。不可組態、只有基礎視窗管理的視窗管理程式
+# this can't be used as a session in itself
+Hidden=true
diff --git a/kdm/kfrontend/sessions/matchbox.desktop b/kdm/kfrontend/sessions/matchbox.desktop
new file mode 100644
index 000000000..8c6e2fd52
--- /dev/null
+++ b/kdm/kfrontend/sessions/matchbox.desktop
@@ -0,0 +1,82 @@
+[Desktop Entry]
+Type=XSession
+Exec=matchbox
+TryExec=matchbox
+Name=Matchbox
+Name[bn]=ম্যাচবক্স
+Name[eo]=Alumetujo
+Name[hi]=मैचबॉक्स
+Name[ne]=मिल्ने बाकस
+Name[pa]=ਮੈਚ-ਬਾਕਸ
+Name[rw]=Agasandukugahura
+Name[ta]=பொருத்தப்பெட்டி
+Name[te]=అగ్గి పెట్టె
+Name[tg]=Қуттии гӯгирд
+Name[wa]=Boesse di brocales (Matchbox)
+Comment=A window manager for handheld devices
+Comment[af]='n Venster bestuurder vir draagbare toestelle
+Comment[ar]=مدير نوافذ للأجهزة اليدوية
+Comment[be]=Кіраўнік вокнаў для кішанёвых кампутараў
+Comment[bn]=হ্যাণ্ডহেল্ড যন্ত্রাদির উপযোগী একটি উইণ্ডো ম্যানেজার
+Comment[bs]=Window manager za ručne uređaje
+Comment[ca]=Un gestor de finestres per a dispositius de ma
+Comment[cs]=Správce oken pro PDA
+Comment[csb]=Menedżer òknów dlô palmtopów
+Comment[cy]=Trefnydd ffenestri ar gyfer llawiaduron
+Comment[da]=En vindueshåndtering for håndholdte enheder
+Comment[de]=Fenstermanager für portable Geräte
+Comment[el]=Ένας διακομιστής παραθύρων για συσκευές παλάμης
+Comment[eo]=Fenestroadministrilo por manaj aparatoj
+Comment[es]=Un gestor de ventanas para dispositivos de mano
+Comment[et]=Aknahaldur pihuarvutitele
+Comment[eu]=Eskuan erabiltzeko gailuentzako leiho kudeatzailea
+Comment[fa]=یک مدیر پنجره برای دستگاههای دستی
+Comment[fi]=ikkunaohjelma PDA-laitteisiin
+Comment[fr]=Un gestionnaire de fenêtres pour les périphériques contrôlés à la main
+Comment[fy]=In finstersmanager foar hânkompjûters
+Comment[gl]=Un xestor de fiestras para dispositivos manuais
+Comment[he]=מנהל חלונות למכשירים נישאים
+Comment[hi]=हाथ में रखने वाले औज़ारों के लिए विंडो प्रबंधक
+Comment[hr]=Upravitelj prozora za ručna računala
+Comment[hu]=Ez az ablakkezelő elsősorban kéziszámítógépekhez ajánlott
+Comment[is]=Gluggastjóri fyrir lófatölvur
+Comment[it]=Un window manager per palmari
+Comment[ja]=ハンドヘルドデバイス向けのウィンドウマネージャ
+Comment[ka]=ფანჯრების მენეჯერი მობილური მოწყობილობებისთვის
+Comment[kk]=Қол құрылғыларға арналған терезе менеджері
+Comment[km]=កម្មវិធី​គ្រប់គ្រង​បង្អួច សម្រាប់​ឧបករណ៍​យួរដៃ
+Comment[ko]=핸드헬드 장치를 위한 창 관리자
+Comment[lt]=Langų tvarkyklė įvairiems įrenginiams
+Comment[lv]=Logu menedžeris priekš portatīvajām iekārtām
+Comment[mk]=Менаџер на прозорци за преносни уреди
+Comment[mn]=Гар төхөөрөмжид зориулсан цонхны удирдлага
+Comment[ms]=Pengurus tetingkap untuk peranti telapak
+Comment[mt]=Window manager għal apparat "handheld"
+Comment[nb]=En vindusbehandler for håndholdte enheter
+Comment[nds]=Finsterpleger för Handreekner
+Comment[ne]=ह्यान्डहेल्ड यन्त्रहरूका लागि सञ्झ्याल प्रबन्धक
+Comment[nl]=Een windowmanager voor handcomputers
+Comment[nn]=Ein vindaugssjef for handhaldne einingar
+Comment[pa]=ਹੱਥਲੇ ਜੰਤਰਾਂ ਲਈ ਝਰੋਖਾ ਮੈਨੇਜਰ
+Comment[pl]=Menedżer okien dla palmtopów
+Comment[pt]=Um gestor de janelas para dispositivos móveis
+Comment[pt_BR]=Um gerenciador de janelas para dispositivos handheld
+Comment[ro]=Un manager de ferestre pentru PDA-uri
+Comment[ru]=Оконный менеджер для мобильных устройств
+Comment[rw]=Mugenga dirishya y'amapareye atwarwa-ntoki
+Comment[se]=Lásegieđahalli giehtaovttadagaid várás
+Comment[sk]=Správca okien pre prenosné zariadenia
+Comment[sl]=Okenski upravitelj za dlančne naprave
+Comment[sr]=Менаџер прозора за мале преносне уређаје
+Comment[sr@Latn]=Menadžer prozora za male prenosne uređaje
+Comment[sv]=Fönsterhanterare för handburna enheter
+Comment[ta]=கையில் உள்ள சாதனங்களுக்கான சாளர மேலாளர்
+Comment[tg]=Як мудири тиреза барои дастгоҳҳои дастӣ
+Comment[th]=ระบบจัดการหน้าต่างของอุปกรณ์มือถือ
+Comment[tr]=El bilgisayarları için bir pencere yöneticisi
+Comment[tt]=Qul cıhazı öçen täräzä-idäräçe
+Comment[uk]=Менеджер вікон для портативних пристроїв
+Comment[vi]=Trình quản lý cửa sổ dành cho thiết bị cầm tay
+Comment[wa]=On manaedjeu di purneas po les éndjins ebarkés
+Comment[zh_CN]=手持设备的窗口管理器
+Comment[zh_TW]=一個掌上型設備所使用的視窗管理程式
diff --git a/kdm/kfrontend/sessions/metacity.desktop b/kdm/kfrontend/sessions/metacity.desktop
new file mode 100644
index 000000000..d2a5a5537
--- /dev/null
+++ b/kdm/kfrontend/sessions/metacity.desktop
@@ -0,0 +1,83 @@
+[Desktop Entry]
+Type=XSession
+Exec=metacity
+TryExec=metacity
+Name=Metacity
+Name[bn]=মেটাসিটি
+Name[eo]=Metaurbo
+Name[hi]=मेटासिटी
+Name[mn]=Метасити
+Name[ne]=मेटासिटी
+Name[rw]=Metacity(Mugenga-dirishya)
+Name[ta]=மெடாசிட்டி
+Name[te]=మెటాసిటి
+Comment=A lightweight GTK2 based window manager
+Comment[af]=Die lig gewig GTK2 gebaseerde venster bestuurder
+Comment[ar]=مدير نوافذ خفيف العبء مبني على GTK2
+Comment[be]=Лёгкі кіраўнік вокнаў, заснаваны на GTK2
+Comment[bn]=একটি হাল্কা GTK2 ভিত্তিক উইণ্ডো ম্যানেজার
+Comment[bs]=Lagani window manager baziran na GTK2
+Comment[ca]=Un gestor de finestres lleuger basat en GTK2
+Comment[cs]=Malý správce oken založený na GTK2
+Comment[csb]=Menedżer òknół ò môłëch żądniach, òpiarti na GTK2
+Comment[cy]=Trefnydd ffenestri ysgafn, wedi'i seilio ar GTK2
+Comment[da]=En letvægts GTK2 baseret vindueshåndtering
+Comment[de]=Schlanker Fenstermanager, der auf GTK2 basiert
+Comment[el]=Ένας ελαφρύς διαχειριστής παραθύρων βασισμένος στο GTK2
+Comment[eo]=Malpeza fenestroadministrilo
+Comment[es]=Un gestor de ventanas ligero basado en GTK2
+Comment[et]=Imeväike aknahaldur, mille aluseks on GTK2
+Comment[eu]=GTK2n oinarritutako leiho kudeatzaile arin bat
+Comment[fa]=GTK2 سبک بر اساس مدیر پنجره
+Comment[fi]=Kevyt, GTK2-pohjainen ikkunaohjelma
+Comment[fr]=Un gestionnaire de fenêtres fondé sur GTK2 et léger
+Comment[fy]=In lichtgewicht op GTK2 basearre finstersmanager
+Comment[gl]=Un xestor de fiestras lixeiro baseado en GTK2
+Comment[he]=מנהל חלונות קל מבוסס GTK2
+Comment[hi]=जीटीके2 आधारित हल्का विंडो प्रबंधक
+Comment[hr]=Lagani upravitelj prozora zasnovan na GTK2
+Comment[hu]=Egy egyszerű, GTK2-alapú ablakkezelő
+Comment[is]=Léttur gluggastjóri byggður á GTK2
+Comment[it]=Un window manager leggero basato su GTK2
+Comment[ja]=GTK2 ベースの軽量なウィンドウマネージャ
+Comment[ka]=GTK2-ს ბაზაზე მსუბუქი ფანჯრის მენეჯერი
+Comment[kk]=GTK2-негіздеген, жеңіл терезе менеджері
+Comment[km]=កម្មវិធី​គ្រប់គ្រង​បង្អួច​សមត្ថភាព​ខ្សោយ ដែល​ផ្អែក​លើ GTK2
+Comment[ko]=가벼운 GTK2 기반 창 관리자
+Comment[lt]=Nedaug resursų reikalaujanti langų tvarkyklė, paremta GTK2
+Comment[lv]=Viegls GTK 2 bāzēts logu menedžeris
+Comment[mk]=Лесен менаџер на прозорци базиран на GTK2
+Comment[mn]=Хөнгөн GTK2 суурьт цонхны удирдагч
+Comment[ms]=Pengurus tetingkap berasaskan GTK2 ringan
+Comment[mt]=Window manager ħafif ibbażat fuq GTK2
+Comment[nb]=En lettvekts vindusbehandler basert på GTK2
+Comment[nds]=En ranke Finsterpleger, opbuut op GTK2
+Comment[ne]=सञ्झ्याल प्रबन्धकमा आधारित हल्का वजन GTK2
+Comment[nl]=Een lichtgewicht op GTK2 gebaseerde windowmanager
+Comment[nn]=En lett vindaugssjef basert på GTK2
+Comment[pa]=ਇੱਕ ਹਲਕਾ GTK2 ਤੇ ਆਧਾਰਿਤ ਝਰੋਖਾ ਮੈਨੇਜਰ
+Comment[pl]=Menedżer okien o małych wymaganiach, oparty na GTK2
+Comment[pt]=Um gestor de janelas leve, baseado em GTK2
+Comment[pt_BR]=Um gerenciador de Janelas leve baseado em GTK2
+Comment[ro]=Un manager de ferestre mic bazat pe GTK2
+Comment[ru]=Лёгкий оконный менеджер на основе GTK2
+Comment[rw]=Mugenga dirishya ishingiye kuri GTK2 yoroshye
+Comment[se]=Geahpes GTK2-vuođđoduvvon lásegieđahalli
+Comment[sk]=Nenáročný správca okien založený na GTK2
+Comment[sl]=Lahek okenski upravitelj na osnovi GTK2
+Comment[sr]=Лагани менаџер прозора заснован на GTK2
+Comment[sr@Latn]=Lagani menadžer prozora zasnovan na GTK2
+Comment[sv]=Lättviktig GTK2-baserad fönsterhanterare
+Comment[ta]=சாளர மேலாளர் அடிப்படையிலான ஒரு குறைந்த எடை GTK2
+Comment[te]=తెలికైన జిటికె2 ఆధారిత విండొ అభికర్త
+Comment[tg]=Сабуки GTK2 ба асоси мудири тиреза
+Comment[th]=ระบบจัดการหน้าต่างขนาดเบาที่ใช้ GTK2
+Comment[tr]=Gtk2 tabanlı hafif bir pencere yöneticisi
+Comment[tt]=GTK2 asılında ciñel täräzä-idäräçe
+Comment[uk]=Простий менеджер вікон для GTK2
+Comment[uz]=GTK2 asosida yaratilgan oddiy oyna boshqaruvchi
+Comment[uz@cyrillic]=GTK2 асосида яратилган оддий ойна бошқарувчи
+Comment[vi]=Trình quản lý cửa sổ nhỏ gọn dựa trên GTK2
+Comment[wa]=On ledjir manaedjeu di purneas båzé so GTK2
+Comment[zh_CN]=轻量级 GTK2 窗口管理器
+Comment[zh_TW]=一個基於 GTK2 的輕量化視窗管理程式
diff --git a/kdm/kfrontend/sessions/mwm.desktop b/kdm/kfrontend/sessions/mwm.desktop
new file mode 100644
index 000000000..b1d6e2acd
--- /dev/null
+++ b/kdm/kfrontend/sessions/mwm.desktop
@@ -0,0 +1,78 @@
+[Desktop Entry]
+Type=XSession
+Exec=mwm
+TryExec=mwm
+Name=MWM
+Name[eo]=MFA
+Name[hi]=एमडबल्यूएम
+Name[te]=ఎం డబ్ల్యు ఎం
+Name[th]=ตัวจัดการหน้าต่าง MWM
+Comment=The Motif Window Manager
+Comment[af]=Die Motif venster bestuurder
+Comment[ar]=مسيير النوافذ Motif
+Comment[be]=Кіраўнік вокнаў Motif
+Comment[bn]=দি মোটিফ উইণ্ডো ম্যানেজার
+Comment[bs]=Motif Window Manager
+Comment[ca]=El gestor de finestres Motif
+Comment[cs]=Motif Window Manager
+Comment[csb]=Menedżer òknów Motif
+Comment[cy]=Y Trefnydd Ffenestri Motif
+Comment[da]=Motif vindueshåndtering
+Comment[de]=Motif-Fenstermanager
+Comment[el]=Ο διαχειριστής παραθύρων Motif
+Comment[eo]=Motifa fenestroadministrilo
+Comment[es]=El gestor de ventanas de Motif
+Comment[et]=Motifi aknahaldur
+Comment[eu]=Motif leiho kudeatzailea
+Comment[fa]=مدیر پنجره موتیف
+Comment[fi]=Motif-ikkunaohjelma
+Comment[fr]=Le gestionnaire de fenêtres Motif
+Comment[fy]=De Motif Window Manager
+Comment[ga]=Bainisteoir fuinneoga Motif
+Comment[gl]=O Xestor de Fiestras Motif
+Comment[he]=מנהל החלונות Motif
+Comment[hi]=मोटिफ विंडो प्रबंधक
+Comment[hr]=Motif upravitelj prozora
+Comment[hu]=Motif ablakkezelő
+Comment[is]=Motif gluggastjórinn
+Comment[it]=Il window manager di Motif
+Comment[ja]=Motif 風のウィンドウマネージャ
+Comment[ka]=ფანჯრის მენეჯერი Motif
+Comment[kk]=Motif терезе менеджері
+Comment[km]=កម្មវិធី​គ្រប់គ្រង​បង្អួច Motif
+Comment[ko]=Motif 창 관리자
+Comment[lt]=Motif langų tvarkyklė
+Comment[lv]=Motif logu menedžeris
+Comment[mk]=Менаџерот на прозорци Motif
+Comment[mn]=Motif Цонхны удирдагч
+Comment[ms]=Pengurus Tertingkap Motif
+Comment[mt]=Window manager tal-Motif
+Comment[nb]=Motif vindusbehandler
+Comment[nds]=De Motif-Finsterpleger
+Comment[ne]=मोटिफ सञ्झ्याल प्रबन्धक
+Comment[nl]=De Motif Window Manager
+Comment[nn]=Motif-vindaugssjefen
+Comment[pa]=Motif ਝਰੋਖਾ ਮੈਨੇਜਰ
+Comment[pl]=Menedżer okien Motif
+Comment[pt]=O gestor de janelas do Motif
+Comment[pt_BR]=O gerenciador de janelas Motif
+Comment[ro]=Managerul de ferestre Motif
+Comment[ru]=Оконный менеджер Motif
+Comment[rw]=Mugenga Dirishya Umutako
+Comment[se]=Motif lásegieđahalli
+Comment[sk]=Správca okien Motif
+Comment[sl]=Okenski upravitelj Motif
+Comment[sv]=Motifs fönsterhanterare
+Comment[ta]=மோடிஃப் சாளர மேலாளர்
+Comment[te]=మొటిఫ్ విండొ అభికర్త
+Comment[tg]=Мавзӯъи мудири тиреза
+Comment[th]=ระบบจัดการหน้าต่างของโมทิฟ
+Comment[tr]=Motif Pencere Yöneticisi
+Comment[tt]=Motif Täräzä İdäräçe
+Comment[uk]=Менеджер вікон Motif
+Comment[uz]=Motif oyna boshqaruvchi
+Comment[uz@cyrillic]=Motif ойна бошқарувчи
+Comment[vi]=Trình quản lý cửa sổ Motif
+Comment[wa]=Li manaedjeu di purneas di Motif
+Comment[zh_CN]=Motif 窗口管理器
+Comment[zh_TW]=Motif 視窗管理程式
diff --git a/kdm/kfrontend/sessions/olvwm.desktop b/kdm/kfrontend/sessions/olvwm.desktop
new file mode 100644
index 000000000..e181331bc
--- /dev/null
+++ b/kdm/kfrontend/sessions/olvwm.desktop
@@ -0,0 +1,71 @@
+[Desktop Entry]
+Type=XSession
+Exec=
+TryExec=
+Name=OLVWM
+Name[br]=OVLWM
+Name[eo]=OLVFA
+Name[hi]=ओएलवीडबल्यूएम
+Name[te]=ఒ ఎల్ వి డబ్యు ఎం
+Comment=The OpenLook Virtual Window Manager. OLWM enhanced for handling of virtual desktops
+Comment[af]=Die OpenLook virtuele venster bestuurder. OLWM wat uitgebrei is vir virtuele werkskerms
+Comment[ar]=مدير النوافذ الوهمي OpenLook، محسّن للتعامل مع أسطح المكتب الوهمية
+Comment[be]=The OpenLook Virtual Window Manager. OLWM дапрацаваны для падтрымкі віртуальных працоўных сталоў
+Comment[bn]=দি ওপেনলুক ভার্চুয়াল উইণ্ডো ম্যানেজার। OLWM-এর বর্ধিত সংস্করণ যাতে ভার্চুয়াল ডেস্কটপ সমর্থিত
+Comment[bs]=OpenLook Virtual Window Manager. OLWM proširen podrškom za virtualne desktope
+Comment[ca]=El OpenLook Virtual Window Manager. OLWM millorat per a escriptoris virtuals de ma
+Comment[csb]=OpenLook Virtual Window Manager. OLWM zbògacony ò wspiarce wirtualnëch pùltów
+Comment[cy]=Y Trefnydd Ffenestri Rhith GolwgAgored (OpenLook). OLWM wedi'i wella i gynnal penbyrddau rhith
+Comment[da]=OpenLook Virtual Window Manager. OLWM udvidet med håndtering af virtuelle desktoppe
+Comment[de]=OpenLook virtueller Fenstermanager, OLWM mit verbesserter Verwaltung von virtuellen Arbeitsflächen
+Comment[el]=Ο OpenLook εικονικός διαχειριστής παραθύρων. Ο OLWM εμπλουτισμένος με διαχείριση εικονικών επιφανειών εργασίας
+Comment[eo]=Fenestroadministrilo por virtualaj labortabloj
+Comment[es]=El OpenLook Virtual Window Manager, un versión mejorada de OLWM con soporte para escritorios virtuales
+Comment[et]=OpenLook virtuaalne aknahaldur on OLWM, mida on täiendatud virtuaalsete töölaudade võimalusega
+Comment[eu]=OpenLook leiho kudeatzaile birtuala. mahaigain birtualak kudeatzeko OLWM hobetua
+Comment[fa]=مدیر پنجرۀ مجازی OpenLook. OLWM گسترش‌یافته برای گرداندن رومیزیهای مجازی
+Comment[fi]=OpenLook virtuaalinen ikkunanhallinta. OLWM:n paranneltu versio,joka käsittelee virtuaalityöpöytiä paremmin
+Comment[fr]=The OpenLook Virtual Window Manager. OLWM avec en plus la gestion des bureaux virtuels
+Comment[fy]=De OpenLook Virtual Window Manager. OLWM útbreide mei firtuele buroblêden
+Comment[gl]=O OpenLook Virtual Window Manager. OLWM mellorado para manexar escritórios virtuais
+Comment[he]=The OpenLook Virtual Window Manager. OLWM משופר בשביל טיפול בשולחנות עבודה וירטואליים
+Comment[hi]=ओपनलुक आभासी विंडो प्रबंधक. ओएलडबल्यूएम को आभासी डेस्कटॉप हैंडल करने के लिए बेहतर बनाया गया
+Comment[hr]=OpenLook virtualni upravitelj prozora - OLWM unaprijeđen mogućnošću rukovanja s virtualnim radnim površinama
+Comment[hu]=OpenLook Virtual Window Manager (OLWM), virtuális munkaasztalok kezelésére is képes
+Comment[is]=OpenLook sýndargluggastjórinn. Endurbættur með OLWM til að styðja sýndarskjáborð
+Comment[it]=L'OpenLook Virtual Window Manager. OLWM migliorato per gestire i desktop virtuali
+Comment[ja]=OpenLook 仮想ウィンドウマネージャ, OLWM 強化仮想デスクトップ
+Comment[ka]=გაუმჯობესებული ფანჯრის მენეჯერი OpenLook, რამდენიმე სამუშაო დაფის მხარდაჭერით
+Comment[kk]=OpenLook Virtual Window Manager - виртуалды үстелдерді қолдайтын терезе менеджері
+Comment[km]=OpenLook Virtual Window Manager ។ OLWM ដែល​បាន​ធ្វើ​ឲ្យ​ប្រសើរ សម្រាប់​ការ​ប្រើ​ផ្ទៃតុ​និមិត្ត ។
+Comment[lt]=OpenLook virtuali langų tvarkyklė. OLWM išplėsta su virtualių darbastalių palaikymu
+Comment[mk]=OpenLook Virtual Window Manager. OLWM менаџерот со подобрено ракување на виртуелни површини
+Comment[ms]=Pengurus Tetingkap Maya OpenLook. OLWM dipertingkat untuk mengendalikan desktop maya
+Comment[mt]=OpenLook Virtual Window Manager - OLWM flimkien ma' desktops virtwali.
+Comment[nb]=OpenLook Virtual Window Manager. OLWM utvidet med virtuelle skrivebord.
+Comment[nds]=De "OpenLook Virtual Window Manager" is OLWM verwiedert üm virtuelle Schriefdischen
+Comment[ne]=अवास्तविक डेस्कटपहरूको ह्यान्डलका लागि बृद्धि गरिएको OLWM खुला देखिने अवास्तविक सञ्झ्याल प्रबन्धक ।
+Comment[nl]=De OpenLook Virtual Window Manager. OLWM uitgebreid met virtuele bureaubladen
+Comment[nn]=OpenLook Virtual Window Manager. OLWM utvida med virtuelle skrivebord.
+Comment[pa]=OpenLook Virtual Window Manager. OLWM ਫਰਜ਼ੀ ਵੇਹੜਿਆਂ ਲਈ ਖਾਸ ਤੌਰ ਤੇ ਤਿਆਰ
+Comment[pl]=OpenLook Virtual Window Manager. OLWM wzbogacony o obsługę wirtualnych pulpitów
+Comment[pt]=O OpenLook Virtual Window Manager. Um OLWM melhorado para lidar com ecrãs virtuais
+Comment[pt_BR]=Acrônimo para OpenLook Virtual Window Manager, o OLWM melhorado para a manipulação de áreas de trabalho virtuais
+Comment[ro]=OpenLook Virtual Window Manager. Un OLWM îmbunătățit cu ecrane virtuale
+Comment[ru]=Улучшенный оконный менеджер OpenLook, поддерживающий несколько рабочих столов
+Comment[rw]=Mugenga Dirishya Itagaragara GufunguraKureba. OLWM ivuguruwe mu gufasha ibiro bitagaragara
+Comment[se]=The OpenLook Virtual Window Manager. OLWM buoriduvvon nu ahte das leat virtuella čállinbeavddit
+Comment[sk]=Virtuálny správca okien OpenLook. OLWM rozšírený o podporu virtuálnych plôch
+Comment[sl]=Open Look Virtual Window Manager je izboljšan OLWM s podporo navideznim namizjem
+Comment[sr]=„OpenLook Virtual Window Manager“. OLWM побољшан за управљање виртуелним радним површинама
+Comment[sr@Latn]=„OpenLook Virtual Window Manager“. OLWM poboljšan za upravljanje virtuelnim radnim površinama
+Comment[sv]=Open Look virtuell fönsterhanterare. OLWM utökad för att hantera virtuella skrivbord
+Comment[ta]=ஓபன்லுக் மெய்நிகர் சாளர மேலாளர். மெய்நிகர் மேல்மேசைகளை கையாளுவதற்கு OLWM மேம்படுத்தப்பட்டது.
+Comment[th]=ระบบจัดการหน้าต่างเสมือน OpenLook คือ OLWM ที่ถูกเพิ่มเติมความสามารถในการรับมือกับพื้นที่ทำงานเสมือน
+Comment[tr]=OpenLook Sanal Pencere Yöneticisi.
+Comment[tt]=OpenLook Virtual Window Manager. Xıyalí öställär öçen yaqşırtılğan OpenLook
+Comment[uk]=OpenLook Virtual Window Manager. OLWM з підтримкою віртуальних стільниць
+Comment[vi]=Trình Quản lý Cửa sổ Ảo "Cái nhìn Mở". Nó được cải tiến cho việc xử lý màn hình nền ảo
+Comment[wa]=Li Manaedjeu di Forveyou Purnea OpenLook (OpenLook Virtual Window Manager). OLWM permete d' apougnî des forveyowès sicribannes
+Comment[zh_CN]=OpenLook 虚拟窗口管理器。OLWM 特别增强了虚拟桌面的处理
+Comment[zh_TW]=Openlook 視窗管理程式。基於 OLWM 並強化管理虛擬桌面
diff --git a/kdm/kfrontend/sessions/olwm.desktop b/kdm/kfrontend/sessions/olwm.desktop
new file mode 100644
index 000000000..e3368af74
--- /dev/null
+++ b/kdm/kfrontend/sessions/olwm.desktop
@@ -0,0 +1,76 @@
+[Desktop Entry]
+Type=XSession
+Exec=olwm
+TryExec=olwm
+Name=OLWM
+Name[eo]=OLFA
+Name[hi]=ओएलडबल्यूएम
+Name[te]=ఒ ఎల్ డబ్యు ఎం
+Name[th]=ตัวจัดการหน้าต่าง OLWM
+Comment=The traditional Open Look Window Manager
+Comment[af]=Die tradisionele Open Look venster bestuurder
+Comment[ar]=مسيير النوافذ Open Look التقليدي
+Comment[be]=Традыцыйны кіраўнік вокнаў Open Look
+Comment[bn]=দি ওপেনলুক উইণ্ডো ম্যানেজার
+Comment[bs]=Tradicionalni Open Look Window Manager
+Comment[ca]=El tradicional gestor de finestres Open Look
+Comment[cs]=Tradiční Open Look Window Manager
+Comment[csb]=Tradicëjny menedżer òknów Open Look
+Comment[cy]=Y Trefnydd Ffenestri GolwgAgored (OpenLook) traddodiadol
+Comment[da]=Den traditionelle Open Look vindueshåndtering
+Comment[de]=Der traditionelle Open-Look-Fenstermanager
+Comment[el]=Ο παραδοσιακός διαχειριστής παραθύρων Open Look
+Comment[eo]=La klasika OL-fenestroadministrilo
+Comment[es]=El tradicional Open Look Window Manager
+Comment[et]=Tavapärane OpenLooki aknahaldur
+Comment[eu]=Betiko Open Look leiho kudeatzailea
+Comment[fa]=مدیر پنجره Open Look سنتی
+Comment[fi]=Perinteinen Open Look -ikkunaohjelma
+Comment[fr]=Le gestionnaire de fenêtres traditionnel Open Look
+Comment[fy]=De tradisjoneel iepen like finstersManager
+Comment[gl]=O tradicional Xestor de Fiestras de Open Look
+Comment[he]=Open Look Window Manager המסורתי
+Comment[hi]=परम्परागत ओपन लुक विंडो प्रबंधक
+Comment[hr]=Tradicionalni 'Open Look' upravitelj prozora
+Comment[hu]=A hagyományos Open Look ablakkezelő
+Comment[is]=Hinn hefðbundni Open Look gluggastjóri
+Comment[it]=L'Open Look Window Manager tradizionale
+Comment[ja]=伝統的な OpenLook ウィンドウマネージャ
+Comment[ka]=OpenLook სისტემის ტრადიციული ფანჯრის მენეჯერი
+Comment[kk]=Дәстүрлі Open Look терезе менеджері
+Comment[km]=កម្មវិធី​គ្រប់គ្រង​បង្អួច​រូបរាង​បើក​ចំហ​បុរាណ
+Comment[lt]=Tradicinė Open Look langų tvarkyklė
+Comment[lv]=Tradicionālais Open Look logu menedžeris
+Comment[mk]=Традиционален Open Look Window Manager
+Comment[mn]=Уламжилалт нээж харагч цонхны удирдагч
+Comment[ms]=Pengurus Tetingkap OpenLook tradisional
+Comment[mt]=Window manager tradizzjonali ta' OpenLook
+Comment[nb]=Den tradisjonelle Open Look-vindusbehandleren
+Comment[nds]=De traditschonelle OpenLook-Finsterpleger
+Comment[ne]=खुला देखिने परम्परागत सञ्झ्याल प्रबन्धक
+Comment[nl]=De traditionele Open Look Window Manager
+Comment[nn]=Den tradisjonelle Open Look-vindaugssjefen
+Comment[pa]=ਇੱਕ ਮੂਲ ਓਪਨ ਲੁੱਕ ਝਰੋਖਾ ਮੈਨੇਜਰ
+Comment[pl]=Tradycyjny menedżer okien Open Look
+Comment[pt]=O gestor de janelas Open Look tradicional
+Comment[pt_BR]=O tradicional Open Look Window Manager
+Comment[ro]=Managerul de ferestre tradițional Open Look Window Manager
+Comment[ru]=Традиционный оконный менеджер системы OpenLook
+Comment[rw]=Mugenga Dirishya Gufungura Kureba karande
+Comment[se]=Árbevirolaš Open Look lásegieđahalli
+Comment[sk]=Tradičný správca okien Open Look
+Comment[sl]=Tradicionalni Open Look Window Manager
+Comment[sr]=Традиционални „Open Look“ менаџер прозора
+Comment[sr@Latn]=Tradicionalni „Open Look“ menadžer prozora
+Comment[sv]=Den traditionella Open Look fönsterhanteraren
+Comment[ta]=பழமையான சாளர மேலாளர்
+Comment[te]=సాంప్రదాయ ఒపెన్ లుక్ విండొ అభికర్త
+Comment[tg]=Расмшудаи Open Look-и мудири тиреза
+Comment[th]=ระบบจัดการหน้าต่าง OpenLook แบบดั้งเดิม
+Comment[tr]=OpenLook Pencere Yöneticisi
+Comment[tt]=Open Look täräzä-idäräçeneñ töp söreme
+Comment[uk]=Традиційний менеджер вікон Open Look
+Comment[vi]=Trình Quản lý Cửa sổ "Cái nhìn Mở" truyền thống
+Comment[wa]=Li mwaisse Manaedjeu di Purnea OpenLook (OpenLook Virtual Window Manager)
+Comment[zh_CN]=传统的 OpenLook 窗口管理器
+Comment[zh_TW]=傳統的 Open Look 視窗管理程式
diff --git a/kdm/kfrontend/sessions/openbox.desktop b/kdm/kfrontend/sessions/openbox.desktop
new file mode 100644
index 000000000..d0f4a06d7
--- /dev/null
+++ b/kdm/kfrontend/sessions/openbox.desktop
@@ -0,0 +1,84 @@
+[Desktop Entry]
+Type=XSession
+Exec=openbox-session
+TryExec=openbox
+Name=Openbox
+Name[bn]=ওপেনবক্স
+Name[cy]=Blwchagored (Openbox)
+Name[eo]=Malfermujo
+Name[hi]=ओपनबाक्स
+Name[ne]=खुला बाकस
+Name[pa]=ਓਪਨ ਬਕਸਾ
+Name[rw]=GufunguraAgasanduku
+Name[ta]=திறப்பு பெட்டி
+Name[te]=ఒపెన్ బాక్స్
+Name[tg]=Кушодани қуттӣ
+Comment=A lightweight window manager based on Blackbox
+Comment[af]='n Lig gewig venster bestuurder wat op Blackbox gebaseer is
+Comment[ar]=مدير نوافذ خفيف العبء مبني على Blackbox
+Comment[be]=Лёгкі кіраўнік вокнаў, заснаваны на Blackbox
+Comment[bn]=ব্ল্যাকবক্স ভিত্তিক হাল্কা উইণ্ডো ম্যানেজার
+Comment[bs]=Lagani window manager baziran na Blackbox-u
+Comment[ca]=Un lleuger gestor de finestres basat en Blackbox
+Comment[cs]=Malý správce oken založený na Blackboxu
+Comment[csb]=Menedżer òknów o môłëch żądaniach, òpairti na Blackbox
+Comment[cy]=Trefnydd ffenestri ysgafn, wedi'i seilio ar Ddu-flwch
+Comment[da]=En letvægts vindueshåndtering baseret på Blackbox
+Comment[de]=Schlanker Fenstermanager, der auf Blackbox beruht
+Comment[el]=Ένας ελαφρύς διαχειριστής παραθύρων βασισμένος στον Blackbox
+Comment[eo]=Fenestroadministrilo devenigita de Negrujo
+Comment[es]=Un gestor de ventanas ligero basado en BlackBox
+Comment[et]=Imeväike aknahaldur, mille aluseks on Blackbox
+Comment[eu]=Blackboxen oinarritutako leiho kudeatzaile arina
+Comment[fa]=یک مدیر پنجرۀ سبک بر اساس Blackbox
+Comment[fi]=Kevyt, Blackboxiin pohjautuva ikkunaohjelma
+Comment[fr]=Un gestionnaire de fenêtres léger fondé sur Blackbox
+Comment[fy]=In lichtgewicht finstersmanager, basearre op Blackbox
+Comment[gl]=Un xestor de fiestras lixeiro baseado en Blackbox
+Comment[he]=מנהל חלונות קל מבוסס על Blackbox
+Comment[hi]=ब्लेकबाक्स आधारित हल्का विंडो प्रबंधक
+Comment[hr]=Lagani upravitelj prozora zasnovan na Blackboxu
+Comment[hu]=Egy nagyon egyszerű ablakkezelő a Blackbox alapján
+Comment[is]=Léttur gluggastjóri byggður á Blackbox
+Comment[it]=Un window manager leggero basato su BlackBox
+Comment[ja]=Blackbox ベースの軽量なウィンドウマネージャ
+Comment[ka]=Blackbox-ის ბაზაზე ფანჯრიოს მენეჯერი
+Comment[kk]=Blackbox-негіздеген жеңіл терезе менеджері
+Comment[km]=កម្មវិធី​គ្រប់គ្រង​បង្អួច​សមត្ថភាព​ខ្សោយ ដែល​ផ្អែក​លើ Blackbox
+Comment[ko]=Blackbox 기반 가벼운 창 관리자
+Comment[lt]=Nedaug resursų reikalaujanti langų tvarkyklė, paremta Blackbox
+Comment[lv]=Viegls logu menedžeris, bāzēts uz Blackbox
+Comment[mk]=Лесен менаџер на прозорци базиран на Blackbox
+Comment[ms]=Pengurus tetingkap ringan berdasarkan Kotak Hitam
+Comment[mt]=Window manager ħafif ibbażat fuq BlackBox
+Comment[nb]=En lettvekts vindusbehandler basert på Blackbox
+Comment[nds]=En ranke Finsterpleger, opbuut op Blackbox
+Comment[ne]=कालो बाकसमा आधारित हल्का वजन सञ्झ्याल प्रबन्धक
+Comment[nl]=Een lichtgewicht windowmanager, gebaseerd op Blackbox
+Comment[nn]=Ein lett vindaugssjef basert på Blackbox
+Comment[pa]=ਬਲੈਕਬਕਸੇ ਤੇ ਆਧਾਰਿਤ ਹਲਕਾ ਝਰੋਖਾ ਮੈਨੇਜਰ
+Comment[pl]=Menedżer okien o małych wymaganiach, oparty na Blackbox
+Comment[pt]=Um gestor de janelas leve baseado no Blackbox
+Comment[pt_BR]=Um gerenciador de janelas leve, baseado no Blackbox
+Comment[ro]=Un manager de ferestre minimal bazat pe Blackbox
+Comment[ru]=Лёгкий оконный менеджер, основанный на Blackbox
+Comment[rw]=Mugenga dirishya yoroshye ishingiye ku Gasandukumukara
+Comment[se]=Geahpes lásegieđahalli ráhkaduvvon Blackboxa vuođul
+Comment[sk]=Nenáročný správca okien založený na Blackbox
+Comment[sl]=Lahek okenski upravitelj na osnovi Blackboxa
+Comment[sr]=Лагани менаџер прозора заснован на Blackbox-у
+Comment[sr@Latn]=Lagani menadžer prozora zasnovan na Blackbox-u
+Comment[sv]=Lättviktig fönsterhanterare baserad på Blackbox
+Comment[ta]=கருப்புப் பெட்டியின் அடிப்படையிலான ஒரு குறைந்த எடை சாளர மேலாளர்
+Comment[te]=బ్లాక్ బాక్స్ ఆధారిత తెలికైన విండొ అభికర్త
+Comment[tg]=Мудири тирезаи сабук ба асоси қуттии сиёҳ
+Comment[th]=ระบบจัดการหน้าต่างขนาดเบา สร้างมาจาก Blackbox
+Comment[tr]=Blackbox temelli küçük bir pencere yöneticisi
+Comment[tt]=Blackbox asılında ciñel täräzä-idäräçe
+Comment[uk]=Легкий менеджер вікон, заснований на Blackbox
+Comment[uz]=Blackbox asosida yaratilgan oddiy oyna boshqaruvchi
+Comment[uz@cyrillic]=Blackbox асосида яратилган оддий ойна бошқарувчи
+Comment[vi]=Trình quản lý cửa sổ nhỏ gọn dựa trên Blackbox
+Comment[wa]=On ledjir manaedjeu di purneas båzé so Blackbox
+Comment[zh_CN]=基于 BlackBox 的轻量级窗口管理器
+Comment[zh_TW]=一個基於 Blackbox 且輕量化的視窗管理程式
diff --git a/kdm/kfrontend/sessions/oroborus.desktop b/kdm/kfrontend/sessions/oroborus.desktop
new file mode 100644
index 000000000..b7ea37c30
--- /dev/null
+++ b/kdm/kfrontend/sessions/oroborus.desktop
@@ -0,0 +1,76 @@
+[Desktop Entry]
+Type=XSession
+Exec=oroborus
+TryExec=oroborus
+Name=Oroborus
+Name[eo]=Koloroj
+Name[hi]=ऑरोबोरस
+Name[pa]=ਓਰੂਬੋਰੁਸ
+Name[te]=ఒరొబొరస్
+Comment=A lightweight themeable window manager
+Comment[af]='n Lig gewig, tema venster bestuurder
+Comment[ar]=مدير نوافذ خفيف العبء قابل لاستخدام السمات
+Comment[be]=Лёгкі кіраўнік вокнаў з падтрымкай тэмаў
+Comment[bn]=হাল্কা থীমযুক্ত উইণ্ডো ম্যানেজার
+Comment[bs]=Lagani window manager sa podrškom za teme
+Comment[ca]=Un gestor de finestres lleuger i configurable amb temes
+Comment[cs]=Malý správce oken s tématy
+Comment[csb]=Menedżer òknów ò môłëch żądanich, z mòżnotą zmianë wëzdrzatkù
+Comment[cy]=Trefnydd ffenestri ysgafn sy'n defnyddio themau
+Comment[da]=En letvægts vindueshåndtering med temaer
+Comment[de]=Schlanker Fenstermanager mit Designs
+Comment[el]=Ένας ελαφρύς διαχειριστής παραθύρων με υποστήριξη θεμάτων
+Comment[eo]=Fenestroadministrilo
+Comment[es]=Un gestor de ventanas ligero con temas
+Comment[et]=Imeväike teemadega aknahaldur
+Comment[eu]=Temak onartzen dituen leiho kudeatzaile arina
+Comment[fa]=یک مدیر پنجرۀ قابل چهره‌بندی سبک
+Comment[fi]=Kevyt teemoitettava ikkunaohjelma
+Comment[fr]=Un gestionnaire de fenêtres avec gestion des thèmes
+Comment[fy]=In lichtgewicht finstersmanager mei temabehear
+Comment[gl]=Un xestor de fiestras lixeiro e con capacidade para temas
+Comment[he]=מנהל חלונות קל ובר התאמה אישית של ערכות נושא
+Comment[hi]=हल्का, प्रसंगयोग्य विंडो प्रबंधक
+Comment[hr]=Lagani upravitelj prozora s temama
+Comment[hu]=Kis erőforrásigényű ablakkezelő, témázási lehetőséggel
+Comment[is]=Léttur þemanlegur gluggastjóri
+Comment[it]=Un window manager leggero che supporta i temi
+Comment[ja]=テーマ化可能な軽量のウィンドウマネージャ
+Comment[ka]=მსუბუქი ფანჯრის მენეჯერი თემების მხარდაჭერით
+Comment[kk]=Жеңіл, нақыштары бар терезе менеджері
+Comment[km]=កម្មវិធី​គ្រប់គ្រង​បង្អួច​អាច​ប្ដូរ​ស្បែក​បាន តែ​មាន​សមត្ថភាព​ខ្សោយ
+Comment[ko]=가벼운 테마를 설정할 수 있는 창 관리자
+Comment[lt]=Nedaug resursų reikalaujanti temas palaikanti langų tvarkyklė
+Comment[lv]=Viegls logu menedžeris ar tēmu atbalstu
+Comment[mk]=Лесен менаџер на прозорци со теми
+Comment[ms]=Pengurus tetingkap boleh tema ringan
+Comment[mt]=Window manager ħafif u temabbli
+Comment[nb]=En lettvekts vindusbehandler med temaer
+Comment[nds]=En ranke Finsterpleger, kann Mustern bruken
+Comment[ne]=विषयवस्तु योग्य हल्का वजन सञ्झ्याल प्रबन्धक
+Comment[nl]=Een lichtgewicht windowmanager met themabeheer
+Comment[nn]=Ein lett vindaugssjef med tema
+Comment[pa]=ਇੱਕ ਸਰੂਪਾਂ ਨਾਲ ਹਲਕਾ ਝਰੋਖਾ ਮੈਨੇਜਰ
+Comment[pl]=Menedżer okien o małych wymaganiach, z możliwością zmiany wyglądu
+Comment[pt]=Um gestor de janelas leve e com suporte para temas
+Comment[pt_BR]=Um gerenciador de Janelas leve com vários temas
+Comment[ro]=Un manager de ferestre mic și configurabil cu diverse tematici
+Comment[ru]=Лёгкий оконный менеджер, поддерживающий темы
+Comment[rw]=Mugenga dirishya ngirwa-nsanganyamatsiko yoroshye
+Comment[se]=Geahpes lásegieđahalli mas leat fáttát
+Comment[sk]=Nenáročný správca okien s podporou tém
+Comment[sl]=Lahek okenski upravitelj s temami
+Comment[sr]=Лагани менаџер прозора са темама
+Comment[sr@Latn]=Lagani menadžer prozora sa temama
+Comment[sv]=Lättviktig fönsterhanterare med teman
+Comment[ta]=குறைந்தஎடை தலைப்புடைய சாளர மேலாளர்
+Comment[th]=ระบบจัดการหน้าต่างขนาดเบาที่ใช้ชุดตกแต่งได้
+Comment[tr]=Küçük, hafif, temalı bir pencere yöneticisi
+Comment[tt]=Tışlana torğan ciñel täräzä-idäräçe
+Comment[uk]=Легкий менеджер вікон з підтримкою тем
+Comment[vi]=Trình quản lý cửa sổ thay đổi được sắc thái
+Comment[wa]=On ledjir manaedjeu di purneas avou des tinmes
+Comment[zh_CN]=轻量级窗口管理器,可定义主题
+Comment[zh_TW]=一個輕量化且有佈景功能的視窗管理程式
+# not usable as a session
+Hidden=true
diff --git a/kdm/kfrontend/sessions/phluid.desktop b/kdm/kfrontend/sessions/phluid.desktop
new file mode 100644
index 000000000..f1cca1d66
--- /dev/null
+++ b/kdm/kfrontend/sessions/phluid.desktop
@@ -0,0 +1,80 @@
+[Desktop Entry]
+Type=XSession
+Exec=phluid
+TryExec=phluid
+Name=Phluid
+Name[eo]=Plena
+Name[hi]=फ्लुइड
+Name[pa]=ਫਲੁਇਡ
+Name[te]=ఫ్లూయిడ్
+Comment=An Imlib2 based window manager
+Comment[af]='n Imlib2 gebaseerde venster bestuurder
+Comment[ar]=مدير نوافذ مبني على Imlib2
+Comment[be]=Кіраўнік вокнаў, заснаваны на Imlib2
+Comment[bn]=Imlib2 ভিত্তিক উইণ্ডো ম্যানেজার
+Comment[bs]=Window manager baziran na Imlib2
+Comment[ca]=Un gestor de finestres basta en Imlib2
+Comment[cs]=Správce oken založený na Imlib2
+Comment[csb]=Menedżer òknów òpiarti na Imlib2
+Comment[cy]=Trefnydd ffenestri wedi'i seilio ar lmlib2
+Comment[da]=En Imlib2 baseret vindueshåndtering
+Comment[de]=Imlib2-basierter Fenstermanager
+Comment[el]=Ένας διαχειριστής παραθύρων βασισμένος στην imlib2
+Comment[eo]=Fenestroadministrilo
+Comment[es]=Un gestor de ventanas basado en Imlib2
+Comment[et]=Aknahaldur, mille aluseks on Imlib2
+Comment[eu]=Imlib2-n oinarritutako leiho kudeatzailea
+Comment[fa]=یک Imlib2 بر اساس مدیر پنجره
+Comment[fi]=Imlib2-pohjainen ikkunaohjelma
+Comment[fr]=Un gestionnaire de fenêtres fondé sur Imlib2
+Comment[fy]=In op imlib2 basearre finstersmanager
+Comment[ga]=Bainisteoir fuinneoga bunaithe ar Imlib2
+Comment[gl]=Un xestor de fiestras baseado en Imlib2
+Comment[he]=מנהל חלונות מבוסס Imlib2
+Comment[hi]=आईएमलिब2 आधारित विंडो प्रबंधक
+Comment[hr]=Upravitelj prozora zasnovan na Imlib2
+Comment[hu]=Egy Imlib2-alapú ablakkezelő
+Comment[is]=Gluggastjóri sem notar Imlib2
+Comment[it]=Un window manager basato su Imlib2
+Comment[ja]=Imlib2ベースのウィンドウマネージャ
+Comment[ka]=ფანჯრის მენეჯერი imlib2 ის ბაზაზე
+Comment[kk]=Imlib2-негіздеген терезе менеджері
+Comment[km]=កម្មវិធី​គ្រប់គ្រង​បង្អួច​ផ្អែក​លើ Imlib2
+Comment[ko]=Imlib2 기반 창 관리자
+Comment[lt]=Langų tvarkyklė, paremta Imlib2
+Comment[lv]=Imlib 2 bāzēts logu menedžeris
+Comment[mk]=Менаџер на прозорци базиран на Imlib2
+Comment[mn]=Imlib2 дээр суурилсан цонхны удирдагч
+Comment[ms]=Pengurus tetingkap berasaskan lmlib2
+Comment[mt]=Window manager ibbażat fuq Imlib2
+Comment[nb]=En vindusbehandler basert på Imlib2
+Comment[nds]=En Finsterpleger opbuut op Imlib2
+Comment[ne]=Imlib2 मा आधारित सञ्झ्याल प्रबन्धक
+Comment[nl]=Een op imlib2 gebaseerde windowmanager
+Comment[nn]=Ein vindaugssjef basert på Imlib2
+Comment[pa]=ਇੱਕ Imlib2 ਆਧਾਰਿਤ ਫਾਇਲ਼ ਮੈਨੇਜਰ
+Comment[pl]=Menedżer okien oparty na Imlib2
+Comment[pt]=Um gestor de janelas baseado na Imlib2
+Comment[pt_BR]=Um gerenciador de janelas baseado na lmlib2
+Comment[ro]=Un manager de ferestre bazat pe Imlib2
+Comment[ru]=Оконный менеджер на основе imlib2
+Comment[rw]=Mugenga dirishya ishingiye kuri Imlib2
+Comment[se]=Imlib2-vuođđoduvvon lásegieđahalli
+Comment[sk]=Správca okien založený na imlib2
+Comment[sl]=Okenski upravitelj na osnovi lmlib2
+Comment[sr]=Менаџер прозора заснован на Imlib2
+Comment[sr@Latn]=Menadžer prozora zasnovan na Imlib2
+Comment[sv]=Imlib2-baserad fönsterhanterare
+Comment[ta]=சாளர மேலாளர் அடிப்படையிலான ஒரு Imlib2
+Comment[te]=ఐఎంలిబ్2 ఆధారిత విండొ అభికర్త
+Comment[tg]=Imlib2 ба асосимудири тиреза
+Comment[th]=ระบบจัดการหน้าต่างที่สร้างบน Imlib2
+Comment[tr]=Imlib2 tabanlı bir pencere yöneticisi
+Comment[tt]=Imlib2 asılında täräzä-idäräçe
+Comment[uk]=Менеджер вікон, заснований на Imlib2
+Comment[uz]=Imlib2 asosida yaratilgan oyna boshqaruvchi
+Comment[uz@cyrillic]=Imlib2 асосида яратилган ойна бошқарувчи
+Comment[vi]=Trình quản lý cửa sổ dựa trên lmlib2
+Comment[wa]=On manaedjeu di purneas båzé so lmlib2
+Comment[zh_CN]=基于 Imlib2 的窗口管理器
+Comment[zh_TW]=一個基於 Imlib2 的視窗管理程式
diff --git a/kdm/kfrontend/sessions/pwm.desktop b/kdm/kfrontend/sessions/pwm.desktop
new file mode 100644
index 000000000..036c9bff4
--- /dev/null
+++ b/kdm/kfrontend/sessions/pwm.desktop
@@ -0,0 +1,72 @@
+[Desktop Entry]
+Type=XSession
+Exec=pwm
+TryExec=pwm
+Name=PWM
+Name[eo]=UnuFA
+Name[hi]=पीडबल्यूएम
+Name[te]=పి డబ్ల్యు ఎం
+Name[th]=ตัวจัดการหน้าต่าง PWM
+Comment=A lightweight window manager able to attach multiple windows to one frame
+Comment[af]='n Lig gewig venster bestuurder wat veelvuldige vensters aan een raam kan koppel
+Comment[ar]=مدير نوافذ خفيف العبء قابل لربط عدة نوافذ إلى إطار واحد
+Comment[be]=Лёгкі кіраўнік вокнаў, які можа далучаць некалькі вокнаў да аднаго фрэйма
+Comment[bn]=একটি হাল্কা উইণ্ডো ম্যানেজার, যাতে একটি ফ্রেমে একাধিক উইণ্ডো সংযুক্ত করা সম্ভব
+Comment[bs]=Lagani window manager koji može prikačiti više prozora na jedan okvir
+Comment[ca]=Un lleuger gestor de finestres capaç d'aplegar múltiples finestres en un marc
+Comment[csb]=Menedżer òknów ò môłëch żądaniach, rozmiejący doczepic wiele òknów do jedny ramë
+Comment[cy]=Trefnydd ffenestri ysgafn sy'n gallu atodi mwy nag un ffenestr at un ffrâm
+Comment[da]=En letvægts vindueshåndtering der kan knytte flere vinduer til én ramme
+Comment[de]=Schlanker Fenstermanager, der mehrere Fenster an einen Rahmen andocken kann
+Comment[el]=Ένας ελαφρύς διαχειριστής παραθύρων με δυνατότητα να προσαρτά πολλαπλά παράθυρα σε ένα πλαίσιο
+Comment[eo]=Fenestroadministrilo, kiu faras unun fenestron el kelkaj
+Comment[es]=Un gestor de ventanas ligero capaz de conectar varias ventanas a un mismo marco
+Comment[et]=Imeväike aknahaldur, mis suudab mitu akent ühe raami külge haakida
+Comment[eu]=Hainbat leiho marko bakarrean uztar ditzakeen leiho kudeatzaile arina
+Comment[fa]=مدیر پنجرۀ سبک قادر به پیوست پنجره‌های چندگانه در یک قابک
+Comment[fi]=Kevyt ikkunaohjelma, joka osaa liittää useita ikkunoita yhteen kehykseen
+Comment[fr]=Un gestionnaire de fenêtres léger capable d'attacher plusieurs fenêtres à un même cadre
+Comment[fy]=In lichtgewicht finstersmanager, hokker meardere finsters kin ferbine mei in kader
+Comment[gl]=Un xestor de fiestras lixeiro capaz de adxuntar varias fiestras nun marco
+Comment[he]=מנהל חלונות קל המסוגל לחבר חלונות רבים למסגרת אחת
+Comment[hi]=एक हल्का विंडो प्रबंधक जिसके एक फ़रमा में अनेक विंडो जोड़े जा सकते हैं
+Comment[hr]=Lagani upravitelj prozora koji jednom okviru može pridodati više prozora
+Comment[hu]=Alacsony erőforrásigényű ablakkezelő, több ablakot képes egy kerethez rendelni
+Comment[is]=Léttur gluggastjóri sem getur tengt marga glugga við einn ramma
+Comment[it]=Un window manager leggero in grado di attaccare più finestre ad una cornice
+Comment[ja]=複数のウィンドウ枠を設定可能な軽量なウィンドウマネージャ
+Comment[ka]=მსუბუქი ფანჯრის მენეჯერი, რომელსაც შეუძლია ბევრი ფანჯრის ერთ ჩარჩოში ჩასმა
+Comment[kk]=Жеңіл, бір коршауда бірнеше терезелерді біріктірелетін, терезе менеджері
+Comment[km]=កម្មវិធី​គ្រប់គ្រង​បង្អួច​សមត្ថភាព​ខ្សោយ អាច​ភ្ជាប់​បង្អួច​ច្រើន​ទៅ​ស៊ុម​មួយ
+Comment[lt]=Nedaug resursų reikalaujanti langų tvarkyklė, galinti prijungti daug langų prie vieno rėmo
+Comment[lv]=Viegls logu menedžeris ar iespēju pievienot vairākus logus vienam kadram
+Comment[mk]=Лесен менаџер на прозорци кој може да прикачи повеќе прозорци на една рамка
+Comment[ms]=Pengurus tetingkap ringan yang boleh melekapkan berbilang tetingkap pada satu bingkai
+Comment[mt]=Window manager ħafif li jista' jgħaqqad iżjed minn window waħda fl-istess gwarniċ
+Comment[nb]=En lettvekts vindusbehandler som kan koble flere vinduer til én ramme
+Comment[nds]=En ranke Finsterpleger, de mennige Finstern an een Rahmen andocken kann
+Comment[ne]=एक हल्का वजन सञ्झ्याल प्रबन्धकले बहुभागिय सञ्झ्यालहरूलाई एक फ्रेममा सङ्लग्न गर्न सक्छ
+Comment[nl]=Een lichtgewicht windowmanager, welke meerdere vensters kan verbinden met een frame
+Comment[nn]=Ein lett vindaugssjef som kan kopla fleire vindauge til den same ramma
+Comment[pa]=ਇੱਕ ਹਲਕਾ ਝਰੋਖਾ ਮੈਨੇਜਰ, ਜੋ ਕਿ ਕਈ ਝਰੋਖਿਆਂ ਨੂੰ ਇੱਕ ਫਰੇਮ ਵਿੱਚ ਰੱਖ ਸਕਦਾ ਹੈ
+Comment[pl]=Menedżer okien o małych wymaganiach, potrafiący doczepić wiele okien do jednego obramowania
+Comment[pt]=Um gestor de janelas leve, com a possibilidade de anexar várias janelas a uma área
+Comment[pt_BR]=Um gerenciador de janelas leve, capaz de anexar múltiplas janelas em um quadro
+Comment[ro]=Un manager de ferestre minimal capabil să atașeze multe ferestre la un singur cadru
+Comment[ru]=Лёгкий оконный менеджер, способный объединить несколько окон в одной рамке
+Comment[rw]=Mugenga dirishya yoroshye ishobora gufatishya amadirishya menshi ku ikadiri imwe.
+Comment[se]=Geahpes lásegieđahalli mii sáhttá ovttastahttit máŋga láse seamma rámmii.
+Comment[sk]=Nenáročný správca okien schopný spojiť viac okien do jednoho rámca
+Comment[sl]=Lahek okenski upravitelj, ki lahko pripne več oken na en okvir
+Comment[sr]=Лагани менаџер прозора способан да прикачи више прозора за један оквир
+Comment[sr@Latn]=Lagani menadžer prozora sposoban da prikači više prozora za jedan okvir
+Comment[sv]=Lättviktig fönsterhanterare som kan ansluta flera fönster till en ram
+Comment[ta]=குறைந்த கனமுள்ள பல சாளரங்களை இணைக்க முடிந்த ஒற்றை சாளர மேலாளர்
+Comment[th]=ระบบจัดการหน้าต่างขนาดเบา มีความสามารถในการปะติดหลายๆหน้าต่างลงใน 1 กรอบ
+Comment[tr]=Düşük ağırlıklı bir çok pencereyi bir çerçeveye toplayabilen bir pencere yöneticisi
+Comment[tt]=Ber qısa eçendä berniçä täräzä totaştırala torğan täräzä-idäräçe
+Comment[uk]=Простий менеджер вікон, що дозволяє долучати декілька вікон до однієї рамки
+Comment[vi]=Trình quản lý cửa sổ nhỏ gọn, có thể gắn nhiều cửa sổ vào một khung
+Comment[wa]=On ledjir manaedjeu di purneas ki pout ataetchî sacwants purneas so-z on cåde
+Comment[zh_CN]=轻量级窗口管理器,可将多个窗口附加到一个框架中
+Comment[zh_TW]=一個輕量化且可以將多個視窗結合在一起的視窗管理程式
diff --git a/kdm/kfrontend/sessions/qvwm.desktop b/kdm/kfrontend/sessions/qvwm.desktop
new file mode 100644
index 000000000..922e3e0bd
--- /dev/null
+++ b/kdm/kfrontend/sessions/qvwm.desktop
@@ -0,0 +1,80 @@
+[Desktop Entry]
+Type=XSession
+Exec=qvwm
+TryExec=qvwm
+Name=QVWM
+Name[eo]=QVFA
+Name[hi]=क्यूवीडबल्यूएम
+Name[te]=క్యు వి డబ్ల్యు ఎం
+Name[th]=ตัวจัดการหน้าต่าง QVWM
+Comment=A Windows 95 like window manager
+Comment[af]='n Venster bestuurder wat soos Windows 95 lyk
+Comment[ar]=مدير نوافذ شبيه بويندوز 95
+Comment[be]=Кіраўнік вокнаў, падобны на Windows 95
+Comment[bn]=Windows 95-এর অনুরূপ একটি উইণ্ডো ম্যানেজার
+Comment[bs]=Window manager nalik na Windows 95
+Comment[ca]=Un gestor de finestres com el Windows 95
+Comment[cs]=Správce oken se vzhledem Windows 95
+Comment[csb]=Menedżer òknów o wëzdrzatkù szlachùjącym za Windows 95
+Comment[cy]=Trefnydd Ffenestri sy'n debyg i Windows95
+Comment[da]=En Windows 95-lignende vindueshåndtering
+Comment[de]=Fenstermanager im Stil von Windows 95
+Comment[el]=Ένας διαχειριστής παραθύρων παρόμοιος με τα Windows 95
+Comment[eo]=Fenestroadministrilo kiel Vindozo 95
+Comment[es]=Un gestor de ventanas similar a Windows 95
+Comment[et]=Aknahaldur, mis näeb välja nagu Windows 95
+Comment[eu]=Windows 95en itxura duen leiho kudeatzailea
+Comment[fa]=یک مدیر پنجره شبیه ویندوز ۹۵
+Comment[fi]=Windows 95:n tyylinen ikkunaohjelma
+Comment[fr]=Un gestionnaire de fenêtres semblable à Windows 95
+Comment[fy]=In Win95-likens finstersmanager
+Comment[ga]=Bainisteoir fuinneoga cosúil le Windows 95
+Comment[gl]=Un xestor de fiestras como o de Windows 95
+Comment[he]=מנהל חלונות הדומה לחלונות 95
+Comment[hi]=विंडोज़ 95 जैसा विंडो प्रबंधक
+Comment[hr]=Upravitelj prozora nalik na Windows 95
+Comment[hu]=Egy Windows 95-szerű ablakkezelő
+Comment[is]=Gluggastjóri sem líkist Windows 95
+Comment[it]=Un window manager in stile Windows 95
+Comment[ja]=Windows95 風のウィンドウマネージャ
+Comment[ka]=ფანჯრის მენეჯერი Windows 95-ს სტილში
+Comment[kk]=Windows 95 секілді терезе менеджері
+Comment[km]=កម្មវិធី​គ្រប់គ្រង​បង្អួច​ស្រដៀង Windows 95
+Comment[ko]=윈도 95를 닮은 창 관리자
+Comment[lt]=Langų tvarkyklė, primenanti Windows 95
+Comment[lv]=Windows 95 līdzīgs logu menedžeris
+Comment[mk]=Менаџер на прозорци со изглед на Windows 95
+Comment[mn]=Виндовс 95 шиг цонхны удирдагч
+Comment[ms]=Pengurus tetingkap seperti Windows 95
+Comment[mt]=Window manager jixbaħ lil Windows95
+Comment[nb]=En vindusbehandler som likner Windows 95
+Comment[nds]=En Finsterpleger liek to Windows 95
+Comment[ne]=विण्डोज ९५ जस्तो सञ्झ्याल प्रबन्धक
+Comment[nl]=Een Win95-achtige windowmanager
+Comment[nn]=Ein vindaugssjef som liknar Windows 95
+Comment[pa]=ਇੱਕ ਵਿੰਡੋ 95 ਵਰਗਾ ਝਰੋਖਾ ਮੈਨੇਜਰ
+Comment[pl]=Menedżer okien o wyglądzie podobnym do Windows 95
+Comment[pt]=Um gestor de janelas com o visual do Windows 95
+Comment[pt_BR]=Um gerenciador de janelas parecido com o Windows 95
+Comment[ro]=Un manager de ferestre cu aspect de Windows 95
+Comment[ru]=Оконный менеджер в стиле Windows 95
+Comment[rw]=Windows 95 nka mugenga dirishya
+Comment[se]=Windows95-lágan lásegieđahalli
+Comment[sk]=Správca okien podobný Windows 95
+Comment[sl]=Okenski upravitelj, podoben Windows 95
+Comment[sr]=Менаџер прозора налик на Windows 95
+Comment[sr@Latn]=Menadžer prozora nalik na Windows 95
+Comment[sv]=Fönsterhanterare som liknar Windows 95
+Comment[ta]=சாளர மேலாளர் போன்ற விண்டோஸ் 95
+Comment[te]=విండొస్ 95 లాంటి విండొ అభికర్త
+Comment[tg]=Windows 95 монанди мудири тиреза
+Comment[th]=ระบบจัดการหน้าต่างที่ดูคล้ายวินโดวส์ 95
+Comment[tr]=Windows 95 benzeri bir pencere yöneticisi
+Comment[tt]=Windows 95 kebek täräzä-idäräçe
+Comment[uk]=Менеджер вікон на кшталт Windows95
+Comment[uz]=Win95'ga oʻxshagan oyna boshqaruvchi
+Comment[uz@cyrillic]=Win95'га ўхшаган ойна бошқарувчи
+Comment[vi]=Trình quản lý cửa sổ giống Windows 95
+Comment[wa]=On manaedjeu di purneas rishonnant a Windows 95
+Comment[zh_CN]=类似 Windows 95 的窗口管理器
+Comment[zh_TW]=一個像 Win95 的視窗管理程式
diff --git a/kdm/kfrontend/sessions/ratpoison.desktop b/kdm/kfrontend/sessions/ratpoison.desktop
new file mode 100644
index 000000000..1127db151
--- /dev/null
+++ b/kdm/kfrontend/sessions/ratpoison.desktop
@@ -0,0 +1,82 @@
+[Desktop Entry]
+Type=XSession
+Exec=ratpoison
+TryExec=ratpoison
+Name=Ratpoison
+Name[bn]=র‌্যাট-পয়সন
+Name[cy]=Gwenwynllygodmawr (Ratpoison)
+Name[eo]=Raticido
+Name[fa]=مرگ موش
+Name[hi]=रैट-पॉइज़न
+Name[ne]=र्याटपोइजन
+Name[pa]=ਰਾਟਪੋਈਸਾਨ
+Name[rw]=UburoziImbeba
+Name[sv]=Råttgift
+Name[ta]=ராட் பாய்சன்
+Name[te]=ఎలుకలమందు
+Name[vi]=Bả chuột
+Name[wa]=Pwezon po les rats (Ratpoison)
+Comment=A simple keyboard-only window manager modeled after Screen
+Comment[af]='n Eenvoudige venster bestuurder wat net met die sleutel bord werk en op Screen gemoduleer is.
+Comment[ar]=مدير نوافذ بسيط يستخدم لوحة المفاتيح فقط صنع مشابهاً لـScreen
+Comment[be]=Просты кіраўнік вокнаў, працуе толькі з клавіятурай
+Comment[bn]=একটি সরল কীবোর্ড-ভিত্তিক উইণ্ডো ম্যানেজার, Screen-এর আদর্শে তৈরি
+Comment[bs]=Jednostavan windows manager samo za tastaturu, modeliran po uzoru na Screen
+Comment[ca]=Un gestor de finestres simple de sols teclat modelat després de Screen
+Comment[csb]=Prosti menedżer òknów òbsłùgiwôny blós z klawiaturë, szlachùje za programą screen
+Comment[cy]=Trefnydd ffenestri syml sy'n defnyddio'r allweddell yn unig, wedi'i arddullio ar Sgrîn (Screen)
+Comment[da]=En simpel kun-tastatur vindueshåndtering modeleret efter Screen
+Comment[de]=Einfacher Fenstermanager, der nur über die Tastatur bedient wird und Screen nachgebildet ist
+Comment[el]=Ένας απλός, μονό για πληκτρολόγιο, διαχειριστής παραθύρων σχεδιασμένος με βάση το Screen
+Comment[en_GB]=A simple keyboard-only window manager modelled after Screen
+Comment[eo]=Fenestroadministrilo por klaviaro
+Comment[es]=Un gestor de ventanas sólo para teclado realizado a partir de Screen
+Comment[et]=Lihtne ainult klaviatuuri abil kasutatav aknahaldur, mille eeskujuks on Screen
+Comment[eu]=Screen-en oinarriturik egindako leiho kudeatzailea, teklatu hutsez erabiltzekoa
+Comment[fa]=یک مدیر پنجره فقط صفحه کلید سادۀ مدل‌یافته پس از پرده
+Comment[fi]=Yksinkertainen, vain näppäimistöltä käytettävä ikkunamanageri, screen-ohjelman tyyliin
+Comment[fr]=Un gestionnaire de fenêtres simple uniquement dirigeable au clavier et fondé sur Screen
+Comment[fy]=In ienfâldige finstersmanager dy allinnich mei it toetseboerd te betsjinnen is, ynspiraasje troch Screen
+Comment[gl]=Un xestor de fiestras de manexo co teclado modelado despois de Screen
+Comment[he]=מנהל חלונות פשוט למקלדת בלבד המעוצב בסגנון Screen
+Comment[hi]=आफ्टर स्क्रीन आधारित साधारण विंडो प्रबंधक जो सिर्फ कुंजीपट के लिए है
+Comment[hr]=Jednostavan, samo za tipkovnicu, upravitelj prozora napravljen prema Screenu
+Comment[hu]=Egyszerű, csak billentyűzetről vezérelhető ablakkezelő (a Screen alapján)
+Comment[is]=Einfaldur gluggastjóri sem notar eingöngu lyklaborðið hannaður eftir Screen forritinu
+Comment[it]=Un window manager semplice solo-tastiera pensato come Screen
+Comment[ja]=Screen をもとに作られた、キーボードインターフェースのみのウィンドウマネージャ
+Comment[ka]=მარტივი კლაიატურით მართვადი ფანჯრის მმართველი
+Comment[kk]=Screen үлгідегі, тек перенетақтадан басқарылатын, қарапайым терезе менеджері
+Comment[km]=កម្មវិធី​គ្រប់គ្រង​បង្អួច​សាមញ្ញ​ប្រើ​តែ​ក្ដារចុច ដែល​យក​គំរូ​តាម​អេក្រង់
+Comment[lt]=Paprasta, tik klaviatūra valdoma langų tvarkyklė, panaši į Screen
+Comment[lv]=Vienkāršs tikai tastatūras logu menedžeris, līdzīgs Screen
+Comment[mk]=Едноставен менаџер на прозорци кој работи само со тастатура, моделиран според Screen
+Comment[ms]=Pengurus tetingkap hanya papan kekunci ringkas dimodelkan seperti Skrin
+Comment[mt]=Window manager għal tastieri biss immudellat fuq Screen
+Comment[nb]=En enkel tastaturbasert vindusbehandler, etter forbilde av Screen
+Comment[nds]=En eenfache Finsterpleger, de bloots mit de Tastatuur bruukt warrt. Screen weer dat Modell dorför
+Comment[ne]=पर्दा पछाडि सञ्झ्याल प्रबन्धक मात्र नमूना बनाउने साधारण कुञ्जीपाटी
+Comment[nl]=Een eenvoudige windowmanager die alleen met het toetsenbord te bedienen is, geïnspireerd door Screen
+Comment[nn]=Ein enkel tastaturbasert vindaugssjef, med Screen som førebilete
+Comment[pa]=ਪਰਦੇ ਮੈਡੀਊਲ ਬਾਅਦ ਸਿਰਫ ਕੀ-ਬੋਰਡ ਤੇ ਆਧਾਰਿਤ ਝਰੋਖਾ ਮੈਨੇਜਰ
+Comment[pl]=Prosty menedżer okien obsługiwany wyłącznie za pomocą klawiatury, stworzony na podobieństwo programu screen
+Comment[pt]=Um gestor de janelas simples, só para o teclado e modelado sobre o Screen
+Comment[pt_BR]=Um simples gerenciador de janelas baseado somente em teclado, modelado após a tela
+Comment[ro]=Un manager de ferestre manipulat din tastatură, modelat după Screen
+Comment[ru]=Простой клавиатурный оконный менеджер, моделирующий Screen
+Comment[rw]=Mugenga dirishya ya mwandikisho-gusa yoroheje itunganyijwe nyuma ya Mugaragaza
+Comment[se]=Oktageardánis, boallobeavdestivrejuvvon lásegieđahalli, mas lea Screen ovdagovvan
+Comment[sk]=Jednoduchý správca okien ovládaný iba klávesami podľa programu Screen
+Comment[sl]=Preprost okenski uporavljalnik na osnovi Screen, upravlja se samo s tipkovnico
+Comment[sr]=Једноставан, само за тастатуру менаџер прозора, направљен према Screen-у
+Comment[sr@Latn]=Jednostavan, samo za tastaturu menadžer prozora, napravljen prema Screen-u
+Comment[sv]=Enkel fönsterhanterare bara för tangentbord modellerad efter Screen
+Comment[ta]=சாளரமொன்றில் நிழல் ஏறுகிறது
+Comment[th]=ตัวจัดการหน้าต่างแบบเรียบง่าย ใช้แป้นพิมพ์ควบคุมได้อย่างเดียว ถูกสร้างขึ้นมาตามหลัง Screen
+Comment[tr]=Screen'den sonra modellenmiş salt klavye basit pencere yöneticisi
+Comment[tt]=Töylekle genä ciñel täräzä-idäräçe
+Comment[uk]=Простий менеджер вікон, розроблений на базі Screen, з підтримкою тільки клавіатури
+Comment[vi]=Trình quản lý cửa sổ chỉ dùng bàn phím, dựa theo Screen
+Comment[wa]=On simpe manaedjeu di purneas eployant fok li taprece, båzé so ls idêyes da «Screen»
+Comment[zh_CN]=Screen 后又一只支持键盘的窗口管理器
+Comment[zh_TW]=一個簡易、只支援鍵盤的視窗管理程式
diff --git a/kdm/kfrontend/sessions/sapphire.desktop b/kdm/kfrontend/sessions/sapphire.desktop
new file mode 100644
index 000000000..5cdd998d7
--- /dev/null
+++ b/kdm/kfrontend/sessions/sapphire.desktop
@@ -0,0 +1,84 @@
+[Desktop Entry]
+Type=XSession
+Exec=sapphire
+TryExec=sapphire
+Name=Sapphire
+Name[bn]=স্যাফায়ার
+Name[eo]=Safiro
+Name[fa]=ياقوت كبود
+Name[hi]=सेफायर
+Name[ka]=საფირონი
+Name[ne]=नीलमणि
+Name[pa]=ਸਾਪਰਫੀਰੀ
+Name[ru]=Сапфир
+Name[ta]=சபையர்
+Name[te]=ఇంద్రనీలం
+Name[tg]=Ёқути кабуд
+Comment=A minimal but configurable window manager
+Comment[af]='n Minimalistiese venster bestuurder, wat nogsteeds opstel funksionaliteit bevat.
+Comment[ar]=مدير نوافذ مصغّر ولكن قابل للإعداد
+Comment[be]=Мінімалістычны кіраўнік вокнаў з магчымасцю настаўлення
+Comment[bn]=পরিমিত কিন্তু থীমযুক্ত উইণ্ডো ম্যানেজার
+Comment[bs]=Minimalan ali podesiv window manager
+Comment[ca]=Un minimalista però configurable gestor de finestres
+Comment[cs]=Minimalistický, ale přizpůsobitelný správce oken
+Comment[csb]=Prosti menedżer òknów, równak z mòżnotą kònfigùracëji
+Comment[cy]=Trefnydd ffenestri lleiafol a ffurfweddir
+Comment[da]=En minimal men indstillelig vindueshåndtering
+Comment[de]=Minimalistischer, aber anpassbarer Fenstermanager
+Comment[el]=Ένας μικρός αλλά παραμετροποιήσιμος διαχειριστής παραθύρων
+Comment[eo]=Fenestroadministrilo
+Comment[es]=Un gestor de ventanas minimalista pero configurable
+Comment[et]=Väga väike, kuid seadistatav aknahaldur
+Comment[eu]=Leiho kudeatzaile minimal baina konfiguragarria
+Comment[fa]=یک مدیر پنجرۀ کمینه، اما قابل پیکربندی
+Comment[fi]=Minimaalinen, mutta muokattavissa oleva ikkunaohjelma
+Comment[fr]=Un gestionnaire de fenêtres minimaliste mais configurable
+Comment[fy]=In minimale mar ynstelbere finstersmanager
+Comment[gl]=Un xestor de fiestras mínimo pero configurábel
+Comment[he]=מנהל חלונות מינימלי אך ניתן להגדרה
+Comment[hi]=एक अल्पतम परंतु कॉन्फ़िगर योग्य विंडो प्रबंधक
+Comment[hr]=Minimalan, ali podesiv upravitelj prozora
+Comment[hu]=Egyszerű, de jól konfigurálható ablakkezelő
+Comment[is]=Einfaldur en stillanlegur gluggastjóri
+Comment[it]=Un window manager minimale ma configurabile
+Comment[ja]=各種設定が可能な小さなウィンドウマネージャ
+Comment[ka]=მინიმალური მაგრამ კონფიგურირებადი ფანჯრის მენეჯერი
+Comment[kk]=Шағын, баптауы бар, терезе менеджері
+Comment[km]=កម្មវិធី​គ្រប់គ្រង​បង្អួច​តូច តែ​អាច​កំណត់​រចនាសម្ព័ន្ធ​បាន
+Comment[ko]=설정 가능한 최소한의 창 관리자
+Comment[lt]=Minimalistinė tačiau konfigūruojama langų tvarkyklė
+Comment[lv]=Minimālistisks, bet konfigurējams logu menedžeris
+Comment[mk]=Минимален но конфигурабилен менаџер на прозорци
+Comment[mn]=Маш жижиг тохируулах боломжгүй цонх удирдагч
+Comment[ms]=Pengurus tetingkap minimum tetapi boleh konfigur
+Comment[mt]=Window manager minimu imma konfigurabbli
+Comment[nb]=En minimal vindusbehandler, men med innstillinger
+Comment[nds]=En minimaal, man instellbor Finsterpleger
+Comment[ne]=सानो तर कन्फिगर गर्न सकिने सञ्झ्याल प्रबन्धक
+Comment[nl]=Een minimale maar instelbare windowmanager
+Comment[nn]=Ein minimal vindaugssjef, men med innstillingar
+Comment[pa]=ਇੱਕ ਨਿਊਨਤਮ, ਪਰ ਸੋਧਯੋਗ ਝਰੋਖਾ ਮੈਨੇਜਰ
+Comment[pl]=Prosty menedżer okien, ale z możliwością konfiguracji
+Comment[pt]=Um gestor de janelas configurável mas mínimo
+Comment[pt_BR]=Um gerenciador de janelas mínimo, mas configurável
+Comment[ro]=Un manager de ferestre minimal, dar configurabil
+Comment[ru]=Минимальный, но настраиваемый оконный менеджер
+Comment[rw]=Ntoya ariko mugenga dirishya ibonezwa
+Comment[se]=Unna, muhto heivehahtti lásegieđahalli
+Comment[sk]=Minimálny, ale nastaviteľný správca okien
+Comment[sl]=Skromen, a nastavljiv okenski upravitelj
+Comment[sr]=Минимални, али подесиви менаџер прозора
+Comment[sr@Latn]=Minimalni, ali podesivi menadžer prozora
+Comment[sv]=Minimal men anpassningsbar fönsterhanterare
+Comment[ta]=சாளர மேலாளரின் மேம்பட்ட திறன்களை வடிவமைக்கலாம்
+Comment[th]=ระบบจัดการหน้าต่างขนาดเล็ก แต่สามารถปรับแต่งได้
+Comment[tr]=Küçük, ancak kolayca özelleştirilebilir bir pencere yöneticisi
+Comment[tt]=Ciñel bulsa da, köylänä torğan täräzä-idäräçe
+Comment[uk]=Мінімальний менеджер вікон з можливістю налаштування
+Comment[uz]=Oddiy, ammo moslab boʻladigan oyna boshqaruvchi
+Comment[uz@cyrillic]=Оддий, аммо мослаб бўладиган ойна бошқарувчи
+Comment[vi]=Trình quản lý cửa sổ tối thiểu, nhưng có thể cấu hình được
+Comment[wa]=On ptit, nén apontiåve, manaedjeu di purnea
+Comment[zh_CN]=很小却可配置的窗口管理器
+Comment[zh_TW]=一個小但可組態的視窗管理程式
diff --git a/kdm/kfrontend/sessions/sawfish.desktop b/kdm/kfrontend/sessions/sawfish.desktop
new file mode 100644
index 000000000..668620fff
--- /dev/null
+++ b/kdm/kfrontend/sessions/sawfish.desktop
@@ -0,0 +1,74 @@
+[Desktop Entry]
+Type=XSession
+Exec=sawfish
+TryExec=sawfish
+Name=Sawfish
+Name[bn]=স-ফিশ
+Name[eo]=Segfiŝo
+Name[fa]=اره‌ماهی
+Name[hi]=सा-फिश
+Name[ne]=सफीस
+Name[pa]=ਸ਼ਾਅਫਿਸ਼
+Name[te]=సాఫిష్
+Comment=An extensible window manager scriptable with an Emacs Lisp-like language
+Comment[af]='n Uitbreibare venster bestuurder met 'n ingeboude skrip taal wat soos Emacs List lyk.
+Comment[ar]=مدير نوافذ قابل للتوسعة يمكن بشفيره بلغة إيماكس ليسب
+Comment[be]=Кіраўнік вокнаў з магчымасцю пашырэння сцэнарамі на мове, падобнай на Emacs Lisp
+Comment[bs]=Proširiv window manager sa podškom za skriptiranje u jeziku sličnom Emacs Lisp-u
+Comment[ca]=Un extensible gestor de finestres mitjançant scripts amb una aparença com el Lisp de Emacs
+Comment[cs]=Rožšiřitelný správce oken skriptovatelný jazykem podobným jazyku Emacs Lisp
+Comment[csb]=Menedżer òknów, jaczi je mòżno rozbùdowac dzãka skriptom w jãzëkù szlachùjącym za Emacs Lisp
+Comment[cy]=Trefnydd ffenestri estynadwy a all ei sgriptio efo iaith sy'n debyg i Emacs Lisp
+Comment[da]=En udvidelig vindueshåndtering der kan scriptes med et Emacs Lisp-lignende sprog
+Comment[de]=Erweiterbarer Fenstermanager, der über Skripts ähnlich Emacs-Lisp gesteuert werden kann
+Comment[el]=Ένας επεκτάσιμος διαχειριστής παραθύρων παραμετροποιήσιμος με μια γλώσσα παρόμοια με την Emacs Lisp
+Comment[eo]=Fenestroadministrilo, kiu uzas Lispon por esti programata
+Comment[es]=Un gestor de ventanas extensible con guiones escritos en un lenguaje similar a Lisp de Emacs
+Comment[et]=Laiendatav aknahaldur, mis kasutab Emacs Lispi keele moodi skripte
+Comment[eu]=Emacs Lisp bezalako hizkuntza batez idatziriko scripten bidez heda daitekeen leiho kudeatzailea
+Comment[fa]=مدیر پنجرۀ توسعه‌پذیر دست‌نوشته‌ای با یک زبان شبیه Emacs Lisp
+Comment[fi]=Laajennettavissa oleva ikkunaohjelma, johon voi luoda komentosarjoja Emacs lispin tyylisellä kielellä
+Comment[fr]=Un gestionnaire de fenêtres extensible, à l'aide scripts dans un langage semblable au Lisp d'Emacs
+Comment[fy]=In útbreidbere finstersmanager, skriptber fia in Emacs Lisp-likene taal
+Comment[gl]=Un xestor de fiestras extensíbel e configurábel con scripts en linguaxe Emacs Lisp
+Comment[he]=מנהל חלונות מקיף הניתן לתכנות עם שפה דמוית Emacs Lisp
+Comment[hi]=ई-मेक्स लिस्प जैसे भाषा में स्क्रिप्ट किया जा सकने लायक विंडो प्रबंधक जिसे विस्तार दिया जा सकता है
+Comment[hr]=Proširivi upravitelj prozora pisan u skripti nalik na jezik Emacs Lisp
+Comment[hu]=Egy könnyen tovább bővíthető ablakkezelő, egy Emacs Lisp-szerű nyelvvel szkriptelhető
+Comment[is]=Viðbætanlegur gluggastjóri sem er skriftanlegur á Emacs Lisp líku máli
+Comment[it]=Un window manager estensibile per cui è possibile fare script in un linguaggio simile all'Emacs lisp
+Comment[ja]=Emacs Lisp 言語スクリプトで機能拡張可能なウィンドウマネージャ
+Comment[ka]=Lisp სკრიპტებით გაფათოვებადი Emacs ის მაგვარი ფანჯრის მენეჯერი
+Comment[kk]=Emacs Lisp-секілді тілдегі скриптті қолданып, кеңейтілетін терезе менеджері
+Comment[km]=កម្មវិធី​គ្រប់គ្រង​បង្អួច​ដែល​អាច​ពង្រីក​បាន ហើយ​អាច​សរសេរ​ស្គ្រីប​ជាមួយ​ភាសា​ដែល​ដូច Emacs Lisp
+Comment[lt]=Langų tvarkyklė, kurią galima išplėsti Emacs Lisp primenančia programavimo kalba
+Comment[lv]=Paplašināms logu menedžeris ar Emacs Lisp līdzīgas valodas atbalstu
+Comment[mk]=Екстензивен менаџер на прозорци кој може да се скриптира со јазик како Emacs Lisp
+Comment[ms]=Pengurus tetingkap boleh kembang dan boleh diskrip dengan bahasa seperti Emacs Lisp
+Comment[mt]=Window manager li jista' jiġi estiż, b'lingwa tixbaħ lil Emacs Lisp
+Comment[nb]=En utvidbar vindusbehandler som kan skriptes med et språk som likner Emacs Lisp.
+Comment[nds]=En verwiederbor Finsterpleger, de över en Skriptspraak liek to Emacs-Lisp stüert warrn kann
+Comment[ne]=इमाक्स लिस्प जस्तो भाषसँग स्क्रिप्ट गर्न सकिने एउटा विस्तारयोग्य सञ्झ्याल प्रबन्धक
+Comment[nl]=Een uitbreidbare windowmanager, scriptbaar via een Emacs Lisp-achtige taal
+Comment[nn]=Ein utvidbar vindaugssjef som kan skriptast med eit språk som liknar Emacs Lisp
+Comment[pa]= Emacs Lisp-ਵਰਗੀ ਭਾਸ਼ਾ ਸਕ੍ਰਿਪਟਯੋਗ ਝਰੋਖਾ ਮੈਨੇਜਰ
+Comment[pl]=Menedżer okien, który można rozszerzać za pomocą skrytów w języku podobnym do Emacs Lisp
+Comment[pt]=Um gestor de janelas extensível e programável com uma linguagem semelhante ao Emacs Lisp
+Comment[pt_BR]=um gerenciador de janelas extensível, baseado em scripts, com uma linguagem parecida com o Lisp do Emacs
+Comment[ro]=Un manager de ferestre extensibil scriptabil cu un limbaj similar cu Emacs Lisp
+Comment[ru]=Расширяемый скриптами Lisp наподобие Emacs оконный менеджер
+Comment[rw]=Mugenga dirishya yagurwa yandikwaho hakoreshejwe Emacs Kudedemanga-nka ururimi
+Comment[sk]=Rozšíriteľný správca okien, ktorého je možné ovládať programovacím jazykom podobným Emacs Lispu
+Comment[sl]=Razširljiv okenski upravitelj, ki se lahko upravlja s skripti v jeziku podobnem Emacs Lisp
+Comment[sr]=Проширив менаџер прозора који се може скриптовати помоћу језика налик на Emacs-ов Lisp
+Comment[sr@Latn]=Proširiv menadžer prozora koji se može skriptovati pomoću jezika nalik na Emacs-ov Lisp
+Comment[sv]=Utökningsbar fönsterhanterare som kan styras med ett Emacs Lisp-liknande skriptspråk
+Comment[ta]=Emacs Lisp-like மொழியுடனான விரிவாக்ககூடிய சாளர மேலாளர் எழுத்தாக்கம்
+Comment[th]=ระบบจัดการหน้าต่างที่สามารถเพิ่มขยายได้ และควบคุมด้วยการเขียนสคริปต์โดยใช้ภาษาแบบ Emacs Lisp
+Comment[tr]=Eklenti destekli Emacs Lisp benzeri bir kodlama dili kullanan kodlanabilir bir pencere yöneticisi
+Comment[tt]=Emacs Lisp-kebek tel belän kiñäylelgän täräzä-idäräçe
+Comment[uk]=Менеджер вікон, що можна розширювати мовою скриптів на кшталт Emacs Lisp
+Comment[vi]=Trình quản lý cửa sổ có thể viết kịch bản được với ngôn ngữ giống Emacs Lisp
+Comment[wa]=On manaedjeu d' purnea k' on pout radjouter des rawetes, apontiåve e scripes dins on lingaedje do stîle Emacs Lisp
+Comment[zh_CN]=可用类似 Emacs Lisp 的语法进行编程的窗口管理器
+Comment[zh_TW]=一個可用類似 Emacs Lisp 語言延伸的視窗管理程式
diff --git a/kdm/kfrontend/sessions/twm.desktop b/kdm/kfrontend/sessions/twm.desktop
new file mode 100644
index 000000000..894371ae9
--- /dev/null
+++ b/kdm/kfrontend/sessions/twm.desktop
@@ -0,0 +1,71 @@
+[Desktop Entry]
+Type=XSession
+Exec=twm
+TryExec=twm
+Name=TWM
+Name[eo]=TFA
+Name[hi]=टीडबल्यूएम
+Name[te]=టి డబ్ల్యు ఎం
+Comment=The Tab Window Manager
+Comment[af]=Die Tab venster bestuurder
+Comment[ar]=مدير النوافذ Tab
+Comment[be]=Кіраўнік вокнаў з укладкамі Tab Window Manager
+Comment[bn]=দি ট্যাব উইণ্ডো ম্যানেজার
+Comment[bs]=Tab Window Manager
+Comment[ca]=El gestor de finestres Tab
+Comment[csb]=Tab Window Manager
+Comment[cy]=Y Trefnydd Ffenestri Tab
+Comment[da]=Tab-vindueshåndtering
+Comment[de]=Der Tab-Fenstermanager
+Comment[el]=Ο διαχειριστής παραθύρων Tab
+Comment[eo]=Taba fenestroadministrilo
+Comment[es]=El Tab Window Manager
+Comment[et]=Kaartidega aknahaldur
+Comment[eu]=Tab leiho kudeatzailea
+Comment[fa]=مدیر پنجرۀ تب
+Comment[fi]=Välilehtiä tukeva ikkunaohjelma
+Comment[fy]=De Ljepper Finster Behearder
+Comment[gl]=O Xestor de Fiestras Tab
+Comment[hi]=टैब विंडो प्रबंधक
+Comment[hr]=Tab upravitelj prozora
+Comment[hu]=Tab Window Manager ablakkezelő
+Comment[is]=Tab gluggastjórinn
+Comment[it]=Il Tab Window Manager
+Comment[ja]=Tab 化ウィンドウマネージャ
+Comment[ka]=X11 სისტემის ტრადიციული ფანჯრის მენეჯერი
+Comment[kk]=Tab терезе менеджері
+Comment[km]=កម្មវិធី​គ្រប់គ្រង​បង្អួច​ជា​ផ្ទាំង
+Comment[ko]=탭 창 관리자
+Comment[lt]=Kortelių langų tvarkyklė
+Comment[lv]=Tabu logu menedžeris
+Comment[mk]=Tab Window Manager
+Comment[mn]=Tab Цонхны удирдагч
+Comment[ms]=Pengurus Tetingkap Tab
+Comment[mt]=Tab Window Manager
+Comment[nb]=Tab Vindusbehandler
+Comment[nds]=De Tab-Finsterpleger
+Comment[ne]=ट्याब सञ्झ्याल प्रबन्धक
+Comment[nl]=De Tab Window Manager
+Comment[nn]=Tab Window Manager
+Comment[pa]=ਟੈਬ ਝਰੋਖਾ ਮੈਨੇਜਰ
+Comment[pl]=Tab Window Manager
+Comment[pt]=O Tab Window Manager
+Comment[pt_BR]=O Gerenciador de Janelas de Abas
+Comment[ro]=Tab Window Manager
+Comment[ru]=Традиционный оконный менеджер системы X11
+Comment[rw]=Mugenga Dirishya Agafishi
+Comment[se]=Tab-láse lásegieđahalli
+Comment[sk]=Správca okien s kartami
+Comment[sl]=Tab Window Manager, okenski upravitelj z zavihki
+Comment[sv]=Flikfönsterhanteraren
+Comment[ta]=டாப் சாளர மேலாளர்
+Comment[te]=టాబ్ విండొ అభికర్త
+Comment[tg]=Tab-и мудири тиреза
+Comment[th]=Tab Window Manager
+Comment[tr]=Tab Pencere Yöneticisi
+Comment[tt]=X11 sistemendäge kebek tabaqlı täräzä-idäräçe
+Comment[uk]=Tab Window Manager
+Comment[vi]=Trình Quản lý Cửa sổ Thẻ
+Comment[wa]=Li manaedjeu di purneas avou Linwetes (Tab Window Manager)
+Comment[zh_CN]=标签式窗口管理器
+Comment[zh_TW]=Tab 視窗管理程式
diff --git a/kdm/kfrontend/sessions/ude.desktop b/kdm/kfrontend/sessions/ude.desktop
new file mode 100644
index 000000000..fb451da1a
--- /dev/null
+++ b/kdm/kfrontend/sessions/ude.desktop
@@ -0,0 +1,75 @@
+[Desktop Entry]
+Type=XSession
+Exec=ude
+TryExec=ude
+Name=UDE
+Name[eo]=ULĈ
+Name[hi]=यूडीई
+Name[te]=యుడిఈ
+Comment=The UNIX Desktop Environment
+Comment[af]=Die 'Unix Desktop Environment'
+Comment[ar]=بيئة سطح مكتب يونكس
+Comment[be]=Працоўнае асяроддзе UNIX
+Comment[bn]=দি ইউনিক্স ডেস্কটপ এনভায়রনমেন্ট
+Comment[br]=An endro burev UNIX
+Comment[bs]=UNIX Desktop Environment
+Comment[ca]=L'entorn d'escriptori de Unix
+Comment[csb]=Òkrãże pùltu Uniksa
+Comment[cy]=Yr Amgylchedd Penbwrdd UNIX
+Comment[da]=UNIX desktopmiljø
+Comment[de]=Das UNIX Desktop Environment
+Comment[el]=Το περιβάλλον επιφάνειας εργασίας του UNIX
+Comment[eo]=La Uniksa Labortablo Ĉirkauaĵo
+Comment[es]=El UNIX Desktop Environment
+Comment[et]=UNIXi töölaua keskkond
+Comment[eu]=UNIX mahaigain ingurunea
+Comment[fa]=محیط رومیزی یونیکس
+Comment[fi]=UNIX-työpöytäympäristö
+Comment[fy]=De Unix Desktop Environment
+Comment[ga]=Timpeallacht Deisce UNIX (UDE)
+Comment[gl]=O Entorno de Escritório de UNIX
+Comment[hi]=यूनिक्स डेस्कटॉप माहौल
+Comment[hr]=UNIX radno okruženje
+Comment[hu]=UNIX Desktop Environment ablakkezelő
+Comment[is]=UNIX skjáborðsumhverfið
+Comment[it]=Lo Unix Desktop Environment
+Comment[ja]=UNIX デスクトップ環境
+Comment[ka]=UNIX-ის სამუშაო გარემო
+Comment[kk]=UNIX Desktop Environment
+Comment[km]=UNIX Desktop Environment
+Comment[ko]=UNIX 데스크톱 환경
+Comment[lt]=UNIX darbastalio aplinka
+Comment[lv]= UNIX Darba virsmas vide
+Comment[mk]=UNIX Desktop Environment
+Comment[mn]=ЮНИКС ажлын тавцангийн орчин
+Comment[ms]=Persekitaran Desktop UNIX
+Comment[mt]=UNIX Desktop Environment
+Comment[nb]=UNIX Desktop Environment
+Comment[nds]=De UNIX-Schriefdisch-Ümgeven
+Comment[ne]=युनिक्स डेस्कटप वातावरण
+Comment[nl]=De Unix Desktop Environment
+Comment[nn]=UNIX Desktop Environment
+Comment[pa]=UNIX Desktop Environment
+Comment[pl]=Środowisko pulpitu Uniksa
+Comment[pt]=O Unix Desktop Environment
+Comment[pt_BR]=Ambiente de Trabalho do UNIX
+Comment[ro]=Mediul grafic UNIX
+Comment[ru]=UNIX Desktop Environment
+Comment[rw]=Ibikikije Ibiro UNIX
+Comment[se]=UNIX Desktop Environment
+Comment[sl]=Namizno okolje UNIX
+Comment[sr]=Unix радно окружење
+Comment[sr@Latn]=Unix radno okruženje
+Comment[sv]=Unix-skrivbordsmiljön
+Comment[ta]=யுனிக்ஸ் மேல்மேசை சூழல்
+Comment[te]=యూనిక్స్ రంగస్థల పర్యావరణం
+Comment[tg]=Атрофи мизи кории UNIX
+Comment[th]=สภาพแวดล้อมเดสก์ทอป UNIX
+Comment[tr]=Unix Masaüstü Ortamı
+Comment[tt]=UNIX Desktop Environment
+Comment[uz]=Unix ish stoli muhiti (Unix Desktop Environment)
+Comment[uz@cyrillic]=Unix иш столи муҳити (Unix Desktop Environment)
+Comment[vi]=Môi trường Màn hình nền UNIX
+Comment[wa]=L' evironmint di scribanne Unix UDE
+Comment[zh_CN]=UNIX 桌面环境
+Comment[zh_TW]=Unix 桌面環境
diff --git a/kdm/kfrontend/sessions/vtwm.desktop b/kdm/kfrontend/sessions/vtwm.desktop
new file mode 100644
index 000000000..50af40a63
--- /dev/null
+++ b/kdm/kfrontend/sessions/vtwm.desktop
@@ -0,0 +1,72 @@
+[Desktop Entry]
+Type=XSession
+Exec=vtwm
+TryExec=vtwm
+Name=VTWM
+Name[eo]=VTFA
+Name[hi]=वीटीडबल्यूएम
+Name[te]= వి టి డబ్ల్యు ఎం
+Comment=The Virtual Tab Window Manager. TWM enhanced by virtual screens, etc.
+Comment[af]=Die virtuele tab venster bestuurder. TWM wat met virtuele skerms uitgebreik is.
+Comment[ar]=مدير نوافذ Tab الوهمي، مدير نوافذ TWM محسّن بأسطح المكتب الوهمية، إلخ.
+Comment[be]=Віртуальны кіраўнік вокнаў з укладкамі Virtual Tab Window Manager. TWM, з дадатковай падтрымкай віртуальных экранаў і інш.
+Comment[bn]=ভার্চুয়াল ট্যাব উইণ্ডো ম্যানেজার, ভার্চুয়াল স্ক্রীণ ইত্যাদি দ্বারা বর্ধিত
+Comment[bs]=Virtual Tab Window Manager. TWM proširen virtuelnim ekranima itd.
+Comment[ca]=El Virtual Tab Window Manager. TWM millorat per a pantalles virtuals, etc.
+Comment[cs]=The Virtual Tab Window Manager. TWM vylepšené o virtuální obrazovky aj.
+Comment[csb]=Virtual Tab Window Manager. TWM zbògacony ò wirtualné pùltë ëtp.
+Comment[cy]=Y Trefnydd Ffenestri Tab Rhith. TWM wedi'i wella gan sgriniau rhith, ayyb.
+Comment[da]=Virtual Tab Window Manager. TWM udvidet med virtuelle skærme osv.
+Comment[de]=Der Virtual-Tab-Fenstermanager, eine Erweiterung von TWM mit virtuellen Arbeitsflächen usw.
+Comment[el]=Ο εικονικός Tab διαχειριστής παραθύρων. Ο TWM εμπλουτισμένος με εικονικές οθόνες, κτλ.
+Comment[eo]=La Virtuala Taba Fenestroadministrilo, TFA plibonigita per virtualaj ekranoj
+Comment[es]=El Virtual Tab Window Manager, TWM mejorado con pantallas virtuales, etc.
+Comment[et]=Virtuaalsete kaartidega aknahaldur ehk TWM, mida on täiendatud virtuaalsete ekraanidega jne.
+Comment[eu]=Virtual Tab leiho kudeatzailea. Pantaila birtual eta abarrez hobetutako TWM-a
+Comment[fa]=یک مدیر پنجرۀ تب مجازی.TWM گسترش‌یافته توسط پرده‌های مجازی و غیره.
+Comment[fi]=Välilehtiä ja virtuaalityöpöytiä tukeva ikkunaohjelma, pohjautuu TWM:ään
+Comment[fr]=The Virtual Tab Window Manager. TWM avec en plus la gestion des bureaux multiples
+Comment[fy]=De firtuele Ljepper Window Manager. TWM útbreid mei firtuele buroblêden, ensfh.
+Comment[gl]=O Virtual Tab Window Manager. TWM mellorado con pantallas virtuais, etc.
+Comment[he]=The Virtual Tab Window Manager. TWM המשופר בשולחנות עבודה וירטואליים וכו'
+Comment[hi]=आभासी टैब विंडो प्रबंधक. TWM को आभासी स्क्रीन इत्यादि से बेहतर बनाया गया
+Comment[hr]=Comment=Virtual Tab Window Manager. TWM poboljšan virtualnim zaslonima itd.
+Comment[hu]=Virtual Tab Window Manager, egy TWM-változat, támogatja a virtuális képernyőkezelést
+Comment[is]=Tab gluggastjórinn endurbættur með sýndarskjám og fleiru.
+Comment[it]=Il Virtual Tab Window Manager. TWM migliorato con schermi virtuali ecc.
+Comment[ja]=仮想スクリーンなどの機能を拡張した TWM ベースの仮想タブウィンドウマネージャ
+Comment[ka]=ვირტუალური ეკრანებით აღჭურვილი TWM-ის ვარიანტი
+Comment[kk]=Virtual Tab Window Manager. TWM-негіздеген, виртуалды экрандары және т.б.с.с бар терезе менеджері.
+Comment[km]=Virtual Tab Window Manager ។ TWM ដែល​បាន​ធ្វើ​ឲ្យ​ប្រសើរ​ដោយ​អេក្រង់​និមិត្ត ជាដើម ។
+Comment[lt]=Virtualių kortelių darbastalio aplinka. TWM išplėsta virtualiais darbastaliais ir pan.
+Comment[lv]=Virtuālo tabu logu menedžeris. TWM papildināts ar virtuālajiem ekrāniem utml.
+Comment[mk]=Virtual Tab Window Manager. TWM подобрен со виртуелни површини итн.
+Comment[ms]=Pengurus Tetingkap Tab Maya. TWM dipertingkat dengan skrin maya, dsb.
+Comment[mt]=Virtual Tab Window Manager. TWM flimkien ma' desktops virtwali eċċ
+Comment[nb]=Virtual Tab vindusbehandler. TWM utvidet med virtuelle skjermer, osv.
+Comment[nds]=De "Virtual Tab Window Manager". Dat is TWM verwiedert üm virtuelle Schirmen etc.
+Comment[ne]=अवास्तविक ट्याब सञ्झ्याल प्रबन्धक । अवास्तविक पर्दाद्वारा बृद्धि गरिएको TWM, आदि
+Comment[nl]=De Virtual Tab Window Manager. TWM uitgebreid met virtuele bureaubladen, etc.
+Comment[nn]=Virtual Tab Window Manager. TWM utvida med virtuelle skjermar og anna.
+Comment[pa]=Virtual Tab Window Manager. TWM ਫਰਜ਼ੀ ਪਰਦਿਆਂ ਨਾਲ ਲੈੱਸ
+Comment[pl]=Virtual Tab Window Manager. TWM wzbogacony o wirtualne pulpity itp.
+Comment[pt]=O Virtual Tab Window Manager. Um TWM melhorado com ecrãs virtuais, etc.
+Comment[pt_BR]=O gerenciador de janelas de abas virtuais. TWM melhorado pelas telas virtuais.
+Comment[ro]=Virtual Tab Window Manager. TWM îmbunătățit cu ecrane virtuale etc.
+Comment[ru]=Вариант TWM, имеющий виртуальные экраны и т.д.
+Comment[rw]=Mugenga Dirishya y'Agafishi Itaboneka.TWM ivuguruwe na mugaragaza zitagaragara, n'ibindi.
+Comment[se]=The Virtual Tab Window Manager. TWM buoriduvvon virtuella čállinbevddiin, jna.
+Comment[sk]=The Virtual Tab Window Manager. TWM rozšírený o virtuálne obrazovky atď.
+Comment[sl]=Virtual Tab Window Manager. TWM, izboljšan z navideznimi zasloni ipd.
+Comment[sr]=„The Virtual Tab Window Manager“. TWM побољшан виртуелним екранима и сл.
+Comment[sr@Latn]=„The Virtual Tab Window Manager“. TWM poboljšan virtuelnim ekranima i sl.
+Comment[sv]=Virtuell flikfönsterhanterare. TWM utökad med virtuella skärmar, etc.
+Comment[ta]=மெய்நிகர் தத்தல் சாளர மேலாளர். TWM மெய்நிகர் திரைகளால் மேம்படுத்தப்பட்டது.
+Comment[th]=The Virtual Tab Window Manager คือ TWM ที่เพิ่มความสามารถโดยหน้าจอเสมือน และอื่นๆ
+Comment[tr]=Virtual Tab Masaüstü Yöneticisi.
+Comment[tt]=Virtual Tab Window Manager. Öställär östälengän TWM kebek.
+Comment[uk]=Virtual Tab Window Manager. TWM з підтримкою віртуальних екранів.
+Comment[vi]=Trình Quản lý Cửa sổ Thẻ Ảo. TWM cải tiến với màn hình ảo, ...
+Comment[wa]=Li Forveyou Manaedjeu di Purneas avou Linwetes (Virtual Tab Window Manager). TWM a sacwants forveyowès waitroûles, evnd.
+Comment[zh_CN]=虚拟标签式窗口管理器。用虚拟屏幕等功能增强的 TWM。
+Comment[zh_TW]=虛擬 Tab 視窗管理程式。基於 TWM 並加強虛擬螢幕等等。
diff --git a/kdm/kfrontend/sessions/w9wm.desktop b/kdm/kfrontend/sessions/w9wm.desktop
new file mode 100644
index 000000000..92633bcf7
--- /dev/null
+++ b/kdm/kfrontend/sessions/w9wm.desktop
@@ -0,0 +1,74 @@
+[Desktop Entry]
+Type=XSession
+Exec=w9wm
+TryExec=w9wm
+Name=W9WM
+Name[eo]=F9FA
+Name[hi]=डबल्यू9डबल्यूएम
+Name[ta]=IceWM
+Name[te]=డబ్ల్యు 9 డబ్ల్యు ఎం
+Name[th]=ตัวจัดการหน้าต่าง W9WM
+Comment=A window manager based on 9WM, enhanced by virtual screens and keyboard bindings
+Comment[af]='n Venster bestuurder wat op 9WM gebaseer is, uitgebrei met virtuele skerms en sleutelbord bindinge
+Comment[ar]=مدير نوافذ مبني على 9WM، محسّن الشاشات الوهمية والمفاتيح المرتبطة
+Comment[be]=Кіраўнік вокнаў, заснаваны на 9WM, з дадатковай падтрымкай віртуальных экранаў і клавішных скаротаў
+Comment[bn]=9WM ভিত্তিক উইণ্ডো ম্যানেজার, ভার্চুয়াল স্ক্রীণ এবং কীবোর্ড বাইন্ডিং দ্বারা বর্ধিত
+Comment[bs]=Window manager baziran na 9WM, proširen virtuelnim ekranima i prečicama tastature
+Comment[ca]=Un gestor de finestres basat en 9WM, millorat per a pantalles virtuals i amb dreceres de teclat
+Comment[cs]=Správce oken založený na 9WM rozšířený o virtuální plochy a klávesové zkratky
+Comment[csb]=Menedżer òknów òpiarty na 9WM, zbògacony ò wirtualné ekranë ë kònfigùracëjã klawiszowëch skrodzënów
+Comment[cy]=Trefnydd ffenestri wedi'i seilio ar 9WM, wedi'i wella gan sgriniau rhith a rhwymiadau bysyll.
+Comment[da]=En vindueshåndtering baseret på 9WM, udvidet med virtuelle skærme og tastaturbindinger
+Comment[de]=Fenstermanager auf der Basis von 9WM, erweitert durch virtuelle Arbeitsflächen und Tastaturzuordnungen
+Comment[el]=Ένας διαχειριστής παραθύρων βασισμένος στον 9WM, εμπλουτισμένος με εικονικές οθόνες και συνδυασμούς πλήκτρων
+Comment[eo]=Fenestroadministrilo devenigita de 9FA, plibonigita per virtualaj ekranoj kaj klaviara uzo
+Comment[es]=Un gestor de ventanas basado en 9WM, mejorado con ventanas virtuales y accesos rápidos de teclado
+Comment[et]=Aknahaldur, mille aluseks on 9WM ja mida on täiendatud virtuaalsete ekraanide ja kiirklahvide võimalusega
+Comment[eu]=9WM-n oinarritutako leiho kudeatzailea, pantaila birtual eta laster-teklez hobetua
+Comment[fa]=یک مدیر پنجره بر اساس 9WM، گسترش‌یافته توسط ‌پرده‌های مجازی و مقیدسازیهای صفحه کلید
+Comment[fi]=9WM:ään pohjautuva ikkunaohjelma, jossa tuki virtuaalityöpöydille ja näppäimistöyhdistelmille
+Comment[fr]=Un gestionnaire de fenêtres fondé sur 9WM, avec en plus la gestion des bureaux virtuels et des raccourcis clavier
+Comment[fy]=In finstersmanager basearrre op 9WM. útbreid mei firtuele buroblêden en fluchtoetsen
+Comment[gl]=Un xestor de fiestras baseado en 9WM, mellorado polas pantallas virtuais e atallos de teclado
+Comment[he]=מנהל חלונות המבוסס על 9WM, המשופר בשולחנות עבודה וירטואליים ומיפוי מקשים
+Comment[hi]= 9डबल्यूएम आधारित विंडो प्रबंधक, आभासी स्क्रीन तथा की-बोर्ड बाइंडिंग से बेहतर बनाया गया
+Comment[hr]=Upravitelj prozora zasnovan na 9WM, unaprijeđen virtualnim zaslonima i prečacima tipkovnice
+Comment[hu]=Egy 9WM-alapú ablakkezelő, virtuális képernyőkezeléssel, konfigurálható billentyűparancsokkal
+Comment[is]=Gluggastjóri byggður á 9WM en endurbættur með sýndarskjám og lyklaborðsvörpunum
+Comment[it]=Un window manager basato su 9WM, migliorato con schermi virtuali e scorciatoie per la tastiera.
+Comment[ja]=仮想スクリーンとキーボードショートカット機能を強化した 9WM ベースのウィンドウマネージャ
+Comment[ka]=ფანჯრის მენეჯერი 9wm-ს ბაზაზე, ვირტუალური ეკრანებით და კლავიატურის შესაბამისობებით
+Comment[kk]=9WM-негіздеген, виртуалды экрандары, пернетақта тіркесімдері бар терезе менеджері
+Comment[km]=កម្មវិធី​គ្រប់គ្រង​បង្អួច​ផ្អែក​លើ 9WM ដែល​ត្រូវ​បាន​ធ្វើ​ឲ្យ​ប្រសើរ​ដោយ​អេក្រង់​និមិត្ត និង​ការ​ចង​គ្រាប់ចុច
+Comment[ko]=부분적 그놈 지원과 가상 데스크톱 지원을 사용하는 AEWM 기반 창 관리자
+Comment[lt]=Langų tvarkyklė, paremta 9WM, išplėsta virtualių ekranų ir klaviatūros greitųjų klavišų palaikymu
+Comment[lv]=Logu menedžeris bāzēts uz 9WM, papildināts ar virtuālajiem ekrāniem un tastatūras saīsnēm
+Comment[mk]=Менаџер на прозорци базиран на 9WM, подобрен со виртуелни површини и поврзувања за тастатурата
+Comment[ms]=Pengurus tetingkap berdasarkan 9WM, dipertingkat dengan skrin maya dan ikatan papan kekunci
+Comment[mt]=Window manager ibbażat fuq 9WM, flimkien ma desktops virtwali u hotkeys
+Comment[nb]=En vindusbehandler basert på 9WM, forbedret med virtuelle skjermer og hurtigtaster
+Comment[nds]=En Finsterpleger opbuut op 9WM, verwiedert üm virtuelle Schirmen un Tastkombinatschonen
+Comment[ne]=कुञ्जीपाटी बाइन्डिङ र अवास्तविक पर्दाहरूद्वारा बृद्धि गरिएको 9WM मा आधारित सञ्झ्याल प्रबन्धक
+Comment[nl]=Een windowmanager gebaseerd op 9WM. Uitgebreid met virtuele bureaubladen en sneltoetsen
+Comment[nn]=Ein vindaugssjef basert på 9WM, forbetra med virtuelle skjermar og snøggtastar
+Comment[pa]=9WM ਤੇ ਆਧਾਰਿਤ ਝਰੋਖਾ ਮੈਨੇਜਰ, ਫਰਜ਼ੀ ਪਰਦਿਆਂ ਤੇ ਕੀ-ਬੋਰਡ ਬਾਈਡਿੰਗ ਨਾਲ ਲੈੱਸ
+Comment[pl]=Menedżer okien oparty na 9WM, wzbogacony o wirtualne ekrany i konfigurowanie skrótów klawiszowych
+Comment[pt]=Um gestor de janelas baseado no 9WM, melhorado com ecrãs virtuais e atalhos de teclado
+Comment[pt_BR]=Um gerenciador de janelas baseado no 9Wm, melhorado pelas telas virtuais e atalhos de teclado
+Comment[ro]=Un manager de ferestre bazat pe 9WM, îmbunătățir cu ecrane virtuale și acceleratori de tastatură
+Comment[ru]=Оконный менеджер на основе 9wm, имеющий виртуальные экраны и привязку клавиш
+Comment[rw]=Mugenga Dirishya ishingiye kuri 9WM, ivuguruwe hakoreshejwe mugaragaza itaboneka n'amahuza mwandikisho
+Comment[sk]=Správca okien založený na 9WM, rozšírený o virtuálne obrazovkya klávesové skratky
+Comment[sl]=Okenski upravitelj na osnovi 9WM, izboljšan z navideznimi zasloni in tipkovnimi vezmi
+Comment[sr]=Менаџер прозора заснован на 9WM-у, побољшан виртуелним екранима и повезивањем тастатуре
+Comment[sr@Latn]=Menadžer prozora zasnovan na 9WM-u, poboljšan virtuelnim ekranima i povezivanjem tastature
+Comment[sv]=Fönsterhanterare baserad på 9WM, utökad med virtuella skärmar och tangentbindingar
+Comment[ta]=9WM அடிப்படையிலான மெய்நிகர் திரை மற்றும் விசைப்பலகை சேர்ப்புகளால் மேம்படுத்தப்பட்ட சாளர மேலாளார்,
+Comment[th]=ระบบจัดการหน้าต่างที่สร้างมาจาก 9WM และเพิ่มความสามารถด้วยหน้าจอเสมือนและแป้นพิมพ์ลัด
+Comment[tr]=9WM tabanlı, sanal ekranları ve klavye kısayolları ile geliştirilmiş bir masaüstü yöneticisi
+Comment[tt]=Xıyalí öställär belän töylekne totqan täräzä-idäräçe. 9WM asılında
+Comment[uk]=Менеджер вікон, заснований на 9WM, додано віртуальні екрани та прив'язки клавіш
+Comment[vi]=Trình quản lý cửa sổ dựa vào 9WM, cải tiến với màn hình ảo, tổ hợp phím
+Comment[wa]=On manaedjeu di purneas båzé so 9WM, avou sopoirt po les forveyous scribannes eyet les rascourtis di taprece.
+Comment[zh_CN]=基于 9WM 的窗口管理器,用虚拟屏幕和键盘绑定等功能增强了
+Comment[zh_TW]=一個基於 9WM 並加強虛擬螢幕及鍵盤組合鍵功能
diff --git a/kdm/kfrontend/sessions/waimea.desktop b/kdm/kfrontend/sessions/waimea.desktop
new file mode 100644
index 000000000..8981b5af6
--- /dev/null
+++ b/kdm/kfrontend/sessions/waimea.desktop
@@ -0,0 +1,77 @@
+[Desktop Entry]
+Type=XSession
+Exec=waimea
+TryExec=waimea
+Name=Waimea
+Name[eo]=Vaimeo
+Name[hi]=वाईमिया
+Name[ne]=विमेआ
+Name[pa]=ਵਾਈਮਿਆ
+Name[te]=వైమెయా
+Comment=A highly customizable window manager based on Blackbox
+Comment[af]='n Hoë opstelbare venster bestuurder wat op Blackbox gebaseer is
+Comment[ar]=مدير نوافذ قابل جداً للتخصيص مبني على Blackbox
+Comment[be]=Кіраўнік вокнаў, заснаваны на Blackbox, з магчымасцю настаўлення
+Comment[bn]=ব্ল্যাকবক্স ভিত্তিক উইণ্ডো ম্যানেজার
+Comment[bs]=Visoko prilagodljiv window manager baziran na Blackbox
+Comment[ca]=Un gestor de finestres altament configurable basat en Blackbox
+Comment[cs]=Vysoce přizpůsobitelný správce oken založený na Blackboxu
+Comment[csb]=Menedżer òknów òpiartëch na Blackbox z wiôldżima mòżnotama dopasowaniô
+Comment[cy]=Trefnydd ffenestri sy'n hawdd ei ffurfweddu, wedi'i seilio ar Ddu-flwch
+Comment[da]=En meget indstillelig vindueshåndtering baseret på Blackbox
+Comment[de]=Vielfältig anpassbarer Fenstermanager, der auf Blackbox basiert
+Comment[el]=Ένας ιδιαίτερα παραμετροποιήσιμος διαχειριστής παραθύρων βασισμένος στον Blackbox
+Comment[en_GB]=A highly customisable window manager based on Blackbox
+Comment[eo]=Tre agordebla fenestroadministrilo, devenigita de Negrujo
+Comment[es]=Un gestor de ventanas muy personalizable basado en Blackbox
+Comment[et]=Väga hästi kohandatav aknahaldur, aluseks Blackbox
+Comment[eu]=Blackboxen oinarritutako leiho-kudeatzaile zeharo pertsonalizagarria
+Comment[fa]=یک مدیر پنجره با قابلیت سفارشی‌سازی بالا بر اساس Blackbox
+Comment[fi]=Blackboxiin perustuva paljon muokattavissa oleva ikkunaohjelma
+Comment[fr]=Un gestionnaire de fenêtres très configurable fondé sur Blackbox
+Comment[fy]=In tige ynstelbere finstersmanager, basearre op Blackbox
+Comment[gl]=Un xestor de fiestras moi personalizábel baseado en Blackbox
+Comment[he]=מנהל חלונות המאפשר התאמה אישית גבוהה והמבוסס על Blackbox
+Comment[hi]=ब्लेक-बाक्स आधारित, अत्यंत कस्टमाइजेबल विंडो प्रबंधक
+Comment[hr]=Vrlo prilagodljiv upravitelj prozora zasnovan na Blackboxu
+Comment[hu]=Egy sokféle beállítási lehetőséggel rendelkező ablakkezelő a Blackbox alapján
+Comment[is]=Afar stillanlegur gluggastjóri byggður á Blackbox
+Comment[it]=Un window manager molto personalizzabile basato su BlackBox
+Comment[ja]=Blackbox ベースの高度なカスタマイズが可能なウィンドウマネージャ
+Comment[ka]=კონფიგურირებადი ფანჯრის მენეჯერი Blackbox-ს ბაზაზე
+Comment[kk]=Blackbox-негіздеген, баптау жағынан бай, терезе менеджері
+Comment[km]=កម្មវិធី​គ្រប់គ្រង​បង្អួច​ដែល​អាច​ប្ដូរ​តាម​បំណង​កម្រិត​ខ្ពស់ ដោយ​ផ្អែក​លើ Blackbox
+Comment[ko]=Blackbox 기반의 사용자 정의가 가능한 관리자
+Comment[lt]=Daug konfigūravimo parinkčių turinti langų tvarkyklė, paremta Blackbox
+Comment[lv]=Plaši konfigurējams logu menedžeris bāzēts uz Blackbox
+Comment[mk]=Менаџер на прозорци базиран на Blackbox со голем број опции
+Comment[ms]=Pengurus tetingkap boleh suai berdasarkan Kotak Hitam
+Comment[mt]=Window manager konfigurabbli ibbażat fuq BlackBox
+Comment[nb]=En vindusbehandler med mange tilpasninger, basert på Blackbox
+Comment[nds]=En Finsterpleger mit mennige Instellen, opbuut op Blackbox
+Comment[ne]=कालो बाकसमा आधारित उच्च अनुकूल योग्य एक सञ्झ्याल प्रबन्धक
+Comment[nl]=Een zeer instelbare windowmanager, gebaseerd op Blackbox
+Comment[nn]=Ein vindaugssjef med mange tilpassingar, basert på Blackbox
+Comment[pa]=ਬਲੈਕਬਕਸੇ 'ਤੇ ਆਧਾਰਿਤ ਅਤਿ ਸੋਧਯਯੋਗ ਝਰੋਖਾ ਮੈਨੇਜਰ
+Comment[pl]=Menedżer okien oparty na Blackbox z dużymi możliwościami dostosowania
+Comment[pt]=Um gestor de janelas altamente configurável, baseado no Blackbox
+Comment[pt_BR]=Um gerenciador de janelas altamente personalizável, baseado no Blackbox
+Comment[ro]=Un manager de ferestre foarte configurabil bazat pe Blackbox
+Comment[ru]=Настраиваемый оконный менеджер, основанный на Blackbox
+Comment[rw]=Mugenga dirishya ihabwa imiterere yifuzwa mu buryo hejuru ishingiye ku Gasandukumukara
+Comment[sk]=Veľmi prispôsobiteľný správca okien založený na Blackbox
+Comment[sl]=Visoko nastavljiv okenski upravitelj na osnovi Blackboxa
+Comment[sr]=Врло прилагодљив менаџер прозора заснован на Blackbox-у
+Comment[sr@Latn]=Vrlo prilagodljiv menadžer prozora zasnovan na Blackbox-u
+Comment[sv]=Ytterst anpassningsbar fönsterhanterare baserad på Blackbox
+Comment[ta]=தனதாக்க வல்ல கருப்புப்பெட்டி சார்ந்த சாளர மேலாளர்
+Comment[th]=ระบบจัดการหน้าต่างที่ปรับแต่งได้อย่างละเอียด สร้างมาจาก Blackbox
+Comment[tr]=Blackbox temelli, kolayca özelleştirilebilir bir pencere yöneticisi
+Comment[tt]=Yaqşı köylänüçän täräzä-idäräçe. Blackbox asılında
+Comment[uk]=Надгнучкий менеджер вікон, заснований на Blackbox
+Comment[uz]=Blackbox asosida yaratilgan, moslab boʻladigan oyna boshqaruvchi
+Comment[uz@cyrillic]=Blackbox асосида яратилган, мослаб бўладиган ойна бошқарувчи
+Comment[vi]=Trình quản lý cửa sổ rất dễ cá nhân hoá dựa trên Blackbox
+Comment[wa]=On manaedjeu di purneas k' vos ploz pår mete da vosse båzé so Blackbox
+Comment[zh_CN]=基于 BlackBox 可高度自定义的窗口管理器
+Comment[zh_TW]=一個基於 Blackbox 且高度可客製化的視窗管理程式
diff --git a/kdm/kfrontend/sessions/wm2.desktop b/kdm/kfrontend/sessions/wm2.desktop
new file mode 100644
index 000000000..618c10bcc
--- /dev/null
+++ b/kdm/kfrontend/sessions/wm2.desktop
@@ -0,0 +1,77 @@
+[Desktop Entry]
+Type=XSession
+Exec=wm2
+TryExec=wm2
+Name=WM2
+Name[eo]=Fa2
+Name[hi]=डबल्यूएम2
+Name[te]=డబ్ల్యు ఎం 2
+Name[th]=ตัวจัดการหน้าต่าง WM2
+Comment=A small, non-configurable window manager
+Comment[af]='n Klein, nie opstelbare venster bestuurder
+Comment[ar]=مدير نوافذ صغير غير قابل للإعداد
+Comment[be]=Маленькі кіраўнік вокнаў, без магчымасці настаўленняў
+Comment[bn]=একটি ছোটো উইণ্ডো ম্যানেজার
+Comment[bs]=Mali, ne-konfigurabilni window manager
+Comment[ca]=Un petit i no configurable gestor de finestres
+Comment[cs]=Malý nepřizpůsobitelný správce oken
+Comment[csb]=Môłi menedżer òknów bez mòżnotë kònfigùracëji
+Comment[cy]=Trefnydd ffenestri bach na ellir ffurfweddu
+Comment[da]=En lille, ikke-indstillelig vindueshåndtering
+Comment[de]=Kleiner, nicht einstellbarer Fenstermanager
+Comment[el]=Ένας μικρός, μη παραμετροποιήσιμος διαχειριστής παραθύρων
+Comment[eo]=Malgranda, ne agordebla fenestroadministrilo
+Comment[es]=Un gestor de ventanas pequeño y no configurable
+Comment[et]=Väike ja seadistamatu aknahaldur
+Comment[eu]=Leiho-kudeatzaile txikia, konfiguratu ezin dena
+Comment[fa]=یک مدیر پنجرۀ کوچک و غیرقابل پیکربندی
+Comment[fi]=Pieni ikkunaohjelma, jossa ei ole asetuksia
+Comment[fr]=Un gestionnaire de fenêtres petit et non configurable
+Comment[fy]=In lytse, net-ynstelbere finstersmanager
+Comment[gl]=Un xestor de fiestras pequeno non configurábel
+Comment[he]=מנהל חלונות קטן ולא ניתן להגדרה
+Comment[hi]=एक छोटा विंडो प्रबंधक जो कॉन्फ़िगर नहीं हो सकता
+Comment[hr]=Malen, nepodesiv upravitelj prozora
+Comment[hu]=Egy nagyon egyszerű ablakkezelő, beállítási lehetőségek nélkül
+Comment[is]=Lítill, einfaldur gluggastjóri sem er ekki hægt að stilla
+Comment[it]=Un window manager piccolo e non configurabile
+Comment[ja]=小さくて設定項目のないウィンドウマネージャ
+Comment[ka]=პატარა და არაკონფიგურირებადი ფანჯრის მენეჯერი
+Comment[kk]=Шағын, баптауы жоқ, терезе менеджері
+Comment[km]=កម្មវិធី​គ្រប់គ្រង​បង្អួច​តូច ហើយ​មិន​អាច​កំណត់​រចនាសម្ព័ន្ធ​បាន
+Comment[ko]=설정할 수 없는 작은 창 관리자
+Comment[lt]=Maža, nekonfigūruojama langų tvarkyklė
+Comment[lv]=Mazs, nekonfigurējams logu menedžeris
+Comment[mk]=Мал и неконфигурабилен менаџер на прозорци
+Comment[mn]=Жижиг тохируулах боломжгүй цонх удирдагч
+Comment[ms]=Pengurus tetingkap yang kecil dan tidak boleh konfigur
+Comment[mt]=Window manager żgħir u mhux konfigurabbli
+Comment[nb]=En liten vindusbehandler uten tilpasninger
+Comment[nds]=En lütte Finsterpleger ahn Instellen
+Comment[ne]=सानो, कन्फिगर गर्न नसकिने सञ्झ्याल प्रबन्धक
+Comment[nl]=Een kleine, niet-instelbare windowmanager
+Comment[nn]=Ein liten vindaugssjef utan tilpassingar
+Comment[pa]=ਇੱਕ ਹਲਕਾ ਨਾ-ਸੋਧਯੋਗ ਝਰੋਖਾ ਮੈਨੇਜਰ
+Comment[pl]=Mały menedżer okien nie podlegający konfiguracji
+Comment[pt]=Um gestor de janelas pequeno e não-configurável
+Comment[pt_BR]=Um pequeno e não-configurável gerenciador de janelas
+Comment[ro]=Un manager de ferestre mic, neconfigurabil
+Comment[ru]=Маленький, не настраиваемый оконный менеджер
+Comment[rw]=Mugenga Dirishya itabonezwa, ntoya
+Comment[se]=Unna, ii heivehahtti lásegieđahalli
+Comment[sk]=Malý, nenastaviteľný správca okien
+Comment[sl]=Majhen, nenastavljiv okenski upravitelj
+Comment[sr]=Мали, неподесиви менаџер прозора
+Comment[sr@Latn]=Mali, nepodesivi menadžer prozora
+Comment[sv]=Liten fönsterhanterare utan anpassningsmöjligheter
+Comment[ta]=சிறிய, வடிவமைக்க முடியாத சாளர மேலாளர்
+Comment[th]=ระบบจัดการหน้าที่ขนาดเล็ก ที่ไม่สามารถปรับแต่งอะไรได้
+Comment[tr]=Küçük ve yapılandırılamayan bir pencere yöneticisi
+Comment[tt]=Caylanmí torğan keçkenä täräzä-idäräçe
+Comment[uk]=Невеличкий менеджер вікон без можливості налаштування
+Comment[uz]=Kichik, moslab boʻlmaydigan oyna boshqaruvchi
+Comment[uz@cyrillic]=Кичик, мослаб бўлмайдиган ойна бошқарувчи
+Comment[vi]=Trình quản lý cửa sổ nhỏ, không cấu hình được
+Comment[wa]=On ptit, nén apontiåve, manaedjeu di purneas
+Comment[zh_CN]=小巧的不可配置的窗口管理器
+Comment[zh_TW]=一個小型且不可組態的視窗管理者
diff --git a/kdm/kfrontend/sessions/wmaker.desktop b/kdm/kfrontend/sessions/wmaker.desktop
new file mode 100644
index 000000000..fada3d3b5
--- /dev/null
+++ b/kdm/kfrontend/sessions/wmaker.desktop
@@ -0,0 +1,82 @@
+[Desktop Entry]
+Type=XSession
+Exec=wmaker
+TryExec=wmaker
+Name=WindowMaker
+Name[bn]=উইণ্ডো-মেকার
+Name[cy]=GwneuthuryddFfenestri (WindowMaker)
+Name[eo]=Fenestroadministrilo
+Name[hi]=विंडोमेकर
+Name[ne]=सञ्झ्याल निर्माता
+Name[pa]=ਝਰੋਖਾ-ਨਿਰਮਾਤਾ
+Name[rw]=MukoraDirishya
+Name[sv]=Windowmaker
+Name[ta]=விண்டோஸ்மேக்கர்
+Name[te]=విండొమెకర్
+Name[tg]=Созандаи тиреза
+Comment=A simple window manager that resembles the NeXTStep look very closely
+Comment[af]='n Eenvoudige venster bestuurder wat soos NeXTStep lyk
+Comment[ar]=مدير نوافذ بسيط يمثّل مظهر NeXTStep بشكل قريب جداً
+Comment[be]=Просты кіраўнік вокнаў, які вельмі дакладна паўтарае вонкавы выгляд NeXTStep
+Comment[bn]=একটি উইণ্ডো ম্যানেজার যা ভীষণরকম NeXTStep-এর মত দেখতে
+Comment[bs]=Jednostavan window manager koji vrlo dosljedno imitira NeXTStep izgled
+Comment[ca]=Un gestor de finestres simple que s'assembla molt a l'aspecte de NeXTStep
+Comment[cs]=Jendoduchý správce oken, který se velmi podobá NeXTStep
+Comment[csb]=Prosti menedżer òknów szlachùjący za NeXTStep
+Comment[cy]=Trefnydd ffenestri syml sy'n debyg iawn i'r golwg CamNesaf
+Comment[da]=En simpel vindueshåndtering der ligner NeXTStep's udseende meget
+Comment[de]=Einfacher Fenstermanager mit starker Ähnlichkeit zu NeXTStep
+Comment[el]=Ένας απλός διαχειριστής παραθύρων που προσομοιώνει πολύ καλά το στυλ του NeXTStep
+Comment[eo]=Simpla fenestroadministrilo
+Comment[es]=Un gestor de ventanas sencillo cuyo aspecto se parece mucho al de NeXTStep
+Comment[et]=Lihtne aknahaldur, mis meenutab väga tugevasti NeXTStepi
+Comment[eu]=Leiho kudeatzaile sinplea, NeXTStep-en antz handia duena
+Comment[fa]=یک مدیر پنجرۀ ساده که خیلی شبیه گام بعدی است
+Comment[fi]=Yksinkertainen ikkunaohjelma, joka muistuttaa erittäin paljon NeXTStepiltä
+Comment[fr]=Un gestionnaire de fenêtres simple qui ressemble assez précisement à NeXTStep
+Comment[fy]=In ienfâldige finstersmanager dy it úterlik fan NeXTStep saer tichtby benaderd
+Comment[gl]=Un xestor de fiestras sinxelo que se achega moito á apariencia de NeXTStep
+Comment[he]=מנהל חלונות פשוט הדומה מאוד במראה שלו ל־NeXTStep
+Comment[hi]=नेक्स्टस्टेप की तरह दिखने वाला सादा विंडो प्रबंधक
+Comment[hr]=Jednostavan upravitelj prozora koji odražava vrlo blisko izgled NeXTStepa
+Comment[hu]=Egy egyszerű ablakkezelő, megjelenése nagyon hasonlít a NeXTStephez
+Comment[is]=Einfaldur gluggastjóri sem líkir vel eftir NeXTStep umhverfinu
+Comment[it]=Un semplice window manager che assomiglia molto a NeXTStep.
+Comment[ja]=NextStep にとてもよく似たシンプルなウィンドウマネージャ
+Comment[ka]=NeXTStep -ის მაგვარი მარტივი ფანჯრის მენეჯერი
+Comment[kk]=Қарапайым, NeXTStep-ке үқсас терезе менеджері
+Comment[km]=កម្មវិធី​គ្រប់គ្រង​បង្អួច​ធម្មតា​មួយ ដែល​ប្រហាក់ប្រហែល​នឹង​រូបរាង NeXTStep បំផុត
+Comment[lt]=Paprasta langų tvarkyklė, išvaizda labai primenanti NeXTStep
+Comment[lv]=Vienkāršs logu menedžeris, kas ir ļoti līdzīgs NeXTStep
+Comment[mk]=Едноставен менаџер на прозорци кој е многу сличен на изгледот на NeXTStep
+Comment[ms]=Pengurus tetingkap ringkas yang memasang NeXTStep dan menjadikannya tampak sangat hampir
+Comment[mt]=Window manager li jixbaħ ħafna lil NextStep
+Comment[nb]=En enkel vindusbehandler som ligner mye på NeXTStep
+Comment[nds]=En eenfache Finsterpleger, de meist utsüht as NeXTStep
+Comment[ne]=NeXTStep जस्तो देखिने साधारण सञ्झ्याल प्रबन्धक
+Comment[nl]=Een eenvoudige windowmanager die het uiterlijk van NeXTStep zeer dicht benaderd
+Comment[nn]=Ein enkel vindaugssjef som liknar mykje på NeXTStep
+Comment[pa]=ਇੱਕ ਸਧਾਰਨ ਜੋ ਕਿ NeXTStep ਵਰਗਾ ਜਾਪਦਾ ਹੈ
+Comment[pl]=Prosty menedżer okien przypominający bardzo wyglądem NeXTStep
+Comment[pt]=Um gestor de janelas simples que faz lembrar bastante o visual do NeXTStep
+Comment[pt_BR]=Um gerenciador de janelas simples, que lembra a aparência do NeXTStep
+Comment[ro]=Un manager de ferestre simplu, care amintește foarte bine de aspectul NeXTStep
+Comment[ru]=Простой оконный менеджер, воспроизводящий интерфейс NeXTStep
+Comment[rw]=Mugenga Dirishya yoroheje ihuriza hamwe imboneko IntambweIkurikira byegeranye cyane
+Comment[se]=Oktageardánis lásegieđahalli mii sulástahttá NeXTStep hui ollu
+Comment[sk]=Jednoduchý správca okien, ktorý veľmi pripojíma NeXTStep
+Comment[sl]=Preprost okenski upravitelj, ki zelo spominja na izgled NeXTStep
+Comment[sr]=Једноставан менаџер прозора који одражава врло блиско изглед NeXTStep-а
+Comment[sr@Latn]=Jednostavan menadžer prozora koji odražava vrlo blisko izgled NeXTStep-a
+Comment[sv]=Enkel fönsterhanterare som mycket nära efterliknar Nextstep-utseendet
+Comment[ta]=NeXTStep ஐ ஒத்த எளிய சாளர மேலாளார்.
+Comment[th]=ระบบจัดการหน้าต่างแบบเรียบง่าย ที่ดูคล้ายระบบ NeXTStep มากๆ
+Comment[tr]=NeXTStep'e aşırı benzeyen basit bir masaüstü yöneticisi
+Comment[tt]=NeXTStep küreneşendä ciñel täräzä-idäräçe
+Comment[uk]=Простий менеджер вікон, що дуже нагадує NeXTStep
+Comment[uz]=NeXTStep'ga juda oʻxshash oddiy oyna boshqaruvchi
+Comment[uz@cyrillic]=NeXTStep'га жуда ўхшаш оддий ойна бошқарувчи
+Comment[vi]=Trình quản lý cửa sổ giống với NeXTStep
+Comment[wa]=On simpe manaedjeu di purneas avou l' foite rivnance di NeXTStep
+Comment[zh_CN]=非常接近 NeXTStep 外观的简单窗口管理器
+Comment[zh_TW]=一個小型且與 NeXTStep 外觀很接近的視窗管理程式
diff --git a/kdm/kfrontend/sessions/xfce.desktop b/kdm/kfrontend/sessions/xfce.desktop
new file mode 100644
index 000000000..6b199228b
--- /dev/null
+++ b/kdm/kfrontend/sessions/xfce.desktop
@@ -0,0 +1,73 @@
+[Desktop Entry]
+Type=XSession
+Exec=xfwm
+TryExec=xfwm
+Name=XFce
+Name[eo]=Facoj
+Name[hi]=एक्सएफसीई
+Name[sv]=Xfce
+Name[te]=ఎక్స్ ఎఫ్ సి ఈ
+Comment=The Cholesterol Free Desktop Environment. A desktop environment reminiscent of CDE
+Comment[af]=Die Cholesterol Gratis Werkskerm Omgewing. 'n Werkskerm omgewing wat op CDE gebaseer is
+Comment[ar]=بيئة سطح المكتب الخالي من الكوليسترول، بيئة سطح مكتب مليئة بذكريات CDE
+Comment[be]=XFCE - Cholesterol Free Desktop Environment. Працоўнае асяроддзе, падобнае на CDE
+Comment[bn]=দি কলেস্টরল ফ্রী ডেস্কটপ এনভায়রনমেন্ট। CDE-র কথা মনে করিয়ে দেয় এমন একটি ডেস্কটপ এনভায়রনমেন্ট
+Comment[bs]=Cholesterol Free Desktop Environment. Desktop okolina nalik na CDE
+Comment[ca]=El Cholesterol Free Desktop Environment. Un entorn d'escriptori amb reminiscències de CDE
+Comment[cs]=Svobodné grafické prostředí neobsahující cholesterol. Prostředí připomínající CDE
+Comment[csb]=Òkrãże pùltu szlachùjące za CDE
+Comment[cy]=Yr Amgylchedd Penbwrdd Di-Golesterol. Amgylchedd penbwrdd sy'n atgoffaol o CDE
+Comment[da]=Det kolesterolfrie desktopmiljø. Et desktopmiljø der minder om CDE
+Comment[de]=Cholesterol Free Desktop Environment. Graphische Arbeitsumgebung, die an CDE erinnert
+Comment[el]=Το Cholesterol Free Desktop Environment. Ένα περιβάλλον επιφάνειας εργασίας βασισμένο στο CDE
+Comment[eo]=La libera labortablo ĉirkaŭajo
+Comment[es]=El Cholesterol Free Desktop Environment, un entorno de escritorio que recuerda a CDE
+Comment[et]=Kolesteroolivaba töölaua keskkond, mis meenutab mitmeti CDE-d
+Comment[eu]=Kolesterolik gabeko mahaigain ingurunea. CDE gogorarazten duen mahaigaina
+Comment[fa]=محیط رومیزی آزاد Cholesterol. یادآور محیط رومیزی CDE
+Comment[fi]=Cholesterol Free -työpöytäympäristö. CDE:tä muistuttavatyöpöytäympäristö.
+Comment[fr]=The Cholesterol Free Desktop Environment. Un environnement de bureau rappelant CDE
+Comment[fy]=The Cholesterol Free Desktop Environment. In buroblêd omwrâld die tinken dat oan CDE
+Comment[gl]=O Cholesterol Free Desktop Environment. Un entorno de escritório reminiscente de CDE
+Comment[he]=The Cholesterol Free Desktop Environment. סביבת עבודה המזכירה את CDE
+Comment[hi]=कॉलेस्ट्रॉल रहित डेस्कटॉप माहौल. एक डेस्कटॉप माहौल जो सीडीई जैसा है
+Comment[hr]=Cholesterol Free Desktop Environment - Okruženje radne površine koje podsjeća na CDE
+Comment[hu]=Cholesterol Free Desktop Environment, egy a CDE-re emlékeztető ablakkezelő
+Comment[is]=Kólesterol-lausa skjáborðsumhverfið. Skjáborð sem líkist CDE
+Comment[it]=Il Cholesterol Free Desktop Environment. Un desktop environment che ricorda CDE
+Comment[ja]=Cholesterol Free Desktop Environment, CDE を思わせる、むだのないデスクトップ環境
+Comment[ka]=CDE-ს მაგვარი სამუშაო დაფა ქოლესტერინის გარეშე
+Comment[kk]=Cholesterol Free Desktop Environment. CDE-ге үқсас графикалық орта
+Comment[km]=Cholesterol Free Desktop Environment ។ បរិស្ថាន​ផ្ទៃតុ​ដែល​សម្អាង​លើ CDE
+Comment[lt]=Darbatalio aplinka „Be cholesterolio“. CDE primenanti darbastalio aplinka
+Comment[lv]=Darbvirsmas vide bez holesterīna. Darba virsmas vide, kas atgādina CDE
+Comment[mk]=Cholesterol Free Desktop Environment. Работна околина која потсетува на CDE
+Comment[ms]=Persekitaran Desktop Bebas Kolesterol. Mengenang kembali persekitaran desktop CDE
+Comment[mt]=Cholesterol Free Desktop Environment. Ambjent grafiku li jixbaħ lis-CDE
+Comment[nb]=Det kolesterolfrie skrivebordsmiljø. Et skrivebordsmiljø som minner om CDE
+Comment[nds]=De "Cholesterol Free Desktop Environment". En Schriefdisch-Ümgeven, de wat liek is to CDE
+Comment[ne]=कोलेस्ट्रोल रहित डेस्कटप परिवेश । CDE को स्मरणशील डेस्कटप परिवेश
+Comment[nl]=The Cholesterol Free Desktop Environment. Een desktop environment die herinnert aan CDE
+Comment[nn]=Cholesterol Free Desktop Environment. Eit skrivebordsmiljø som minner om CDE
+Comment[pa]=ਚੋਲੀਸਟੀਰੋਲ ਮੁਫਤ ਵਾਤਾਵਰਣ, ਇੱਕ CDE ਵਰਗਾ ਵਾਤਾਵਰਣ
+Comment[pl]=Środowisko pulpitu przypominające CDE
+Comment[pt]=O Cholesterol Free Desktop Environment. Um ambiente de trabalho com vestígios do CDE
+Comment[pt_BR]=Acrônimo para Cholesterol Free Desktop Environment (ou ambiente livre de colesterol), um ambiente de trabalho remanescente do CDE
+Comment[ro]=Cholesterol Free Desktop Environment. Un mediu grafic cu reminescente din CDE
+Comment[ru]="Не содержащая холестерина" рабочая среда, напоминающая CDE
+Comment[rw]=Ibikikije Ibiro Ubuntu Kolesiterole. Ibikikije ibiro nkumburwa bya CDE
+Comment[se]=The Cholesterol Free Desktop Environment. Čállinbeavdebiras mii muittuha CDE
+Comment[sk]=The Cholesterol Free Desktop Environment. Pracovné prostredie pripomínajúce CDE
+Comment[sl]=Cholesterol Free Desktop Environment. Namizno okolje, podobno okolju CDE
+Comment[sr]=„The Cholesterol Free Desktop Environment“. Радно окружење које подсећа на CDE
+Comment[sr@Latn]=„The Cholesterol Free Desktop Environment“. Radno okruženje koje podseća na CDE
+Comment[sv]=Den kolesterolfria skrivbordsmiljön. En skrivbordsmiljö som påminner om CDE
+Comment[ta]=கொலஸ்ட்ரால் இல்லாத மேல்மேசை சூழல். CDE பொருள்பொதிந்த மேல்மேசை சூழல்
+Comment[th]=สภาพแวดล้อมสำหรับเดสก์ทอปไร้คอเลสเตอรอล เป็นสภาพแวดล้อมสำหรับเดสก์ทอปที่เหลือมาจาก CDE
+Comment[tr]=Cholesterol Masaüstü Ortamı
+Comment[tt]=Holesterol Bulmağan Östäl Möxite. CDE küreneşendä
+Comment[uk]=The Cholesterol Free Desktop Environment. Графічне середовище, що нагадує CDE
+Comment[vi]=Môi trường Màn hình nền Không có Cholesterol. Một môi trường màn hình nền gợi nhớ lại CDE
+Comment[wa]=Li Libe Evironmint di Scribanne Colesterole (Cholesterol Free Desktop Environment). On evironmint d' sicribanne ki nos vént d' CDE
+Comment[zh_CN]=胆固醇自由桌面环境。CDE 桌面环境的追随者
+Comment[zh_TW]=Cholesterol 免費桌面環境。一個另人懷念的 CDE 桌面環境
diff --git a/kdm/kfrontend/sessions/xfce4.desktop b/kdm/kfrontend/sessions/xfce4.desktop
new file mode 100644
index 000000000..11b4097a6
--- /dev/null
+++ b/kdm/kfrontend/sessions/xfce4.desktop
@@ -0,0 +1,72 @@
+[Desktop Entry]
+Type=XSession
+Exec=startxfce4
+TryExec=startxfce4
+Name=XFce 4
+Name[eo]=Facoj 4
+Name[hi]=एक्सएफसीई4
+Name[sv]=Xfce 4
+Name[te]=ఎక్స్ ఎఫ్ సి ఈ 4
+Comment=The Cholesterol Free Desktop Environment, version 4. A desktop environment reminiscent of CDE
+Comment[af]=Die Cholesterol Gratis Werkskerm Omgewing, weergawe 4. 'n Werkskerm omgewing wat op CDE gebaseer is
+Comment[be]=XFCE4 - Cholesterol Free Desktop Environment, version 4. Працоўнае асяроддзе, падобнае на CDE
+Comment[bn]=দি কলেস্টরল ফ্রী ডেস্কটপ এনভায়রনমেন্ট, ৪র্থ সংস্করণ। CDE-র কথা মনে করিয়ে দেয় এমন একটি ডেস্কটপ এনভায়রনমেন্ট
+Comment[bs]=Cholesterol Free Desktop Environment. Desktop okolina nalik na CDE
+Comment[ca]=L'entorn d'escriptori sense colesterol, versió 4. Un entorn d'escriptori que recorda a CDE
+Comment[cs]=Svobodné grafické prostředí neobsahující cholesterol verze 4. Prostředí připomínající CDE
+Comment[csb]=Cholesterol Free Desktop Environment, wersëjô 4 - graficzné òkrãże szlachùjące za CDE
+Comment[cy]=Yr Amgylchedd Penbwrdd Di-Golesterol, fersiwn 4. Amgylchedd penbwrdd sy'n atgoffaol o CDE
+Comment[da]=Det kolesterolfrie desktopmiljø, version 4. Et desktopmiljø der minder om CDE
+Comment[de]=Cholesterol Free Desktop Environment, Version 4. Benutzerumgebung in der Art von CDE
+Comment[el]=Το Cholesterol Free Desktop Environment, έκδοση 4. Ένα περιβάλλον επιφάνειας εργασίας βασισμένο στο CDE
+Comment[eo]=La libera labortablo ĉirkaŭajo 4
+Comment[es]=El Cholesterol Free Desktop Environment, versión 4. Un entorno de escritorio que recuerda a CDE
+Comment[et]=Kolesteroolivaba töölaua keskkond (versioon 4), mis meenutab mitmeti CDE-d
+Comment[eu]=Kolesterolik gabeko mahaigain ingurunea, 4 bertsioa. CDE gogorarazten duen mahaigaina
+Comment[fa]=محیط رومیزی آزاد Cholesterol، نسخه ۴. یادآور محیط رومیزی CDE
+Comment[fi]=Cholesterol Free -työpöytäympäristö. CDE:tä muistuttavatyöpöytäympäristö.
+Comment[fr]=The Cholesterol Free Desktop Environment, version 4. Un environnement de bureau rappelant CDE
+Comment[fy]=De Cholesterol Free Desktop Environment, ferzje 4. In buroblêd omwrâld die tinken dat oan CDE
+Comment[gl]=O Cholesterol Free Desktop Environment, versión 4. Un entorno de escritório reminiscencia de CDE
+Comment[he]=The Cholesterol Free Desktop Environment. גרסה 4, סביבת עבודה המזכירה את CDE
+Comment[hi]=कोलेस्ट्रॉल मुक्त डेस्कटॉप वातावरण, संस्करण 4. सीडीई की याद दिलाता एक डेस्कटॉप वातावरण
+Comment[hr]=Cholesterol Free Desktop Environment verzija 4 - Okruženje radne površine koje podsjeća na CDE
+Comment[hu]=The Cholesterol Free Desktop Environment, 4-es verzió. Egy CDE-szerű ablakkezelő
+Comment[is]=Kólesterol-lausa skjáborðsumhverfið, útgáfa 4. Skjáborð sem líkist CDE
+Comment[it]=Il Cholesterol Free Desktop Environment, versione 4. Un desktop environment che ricorda CDE
+Comment[ja]=Cholesterol Free Desktop Environment, version 4, CDE を思わせる、むだのないデスクトップ環境
+Comment[ka]=CDE-ს მაგვარი უქოლესტერინო სამუშაო დაფა xfce 4
+Comment[kk]=Cholesterol Free Desktop Environment, 4-нұсқа. CDE-ге ұқсас графикалық орта
+Comment[km]=Cholesterol Free Desktop Environment កំណែ 4 ។ បរិស្ថាន​ផ្ទៃតុ​ដែល​សម្អាង​លើ CDE
+Comment[lt]=Darbatalio aplinka „Be cholesterolio“ , 4 versija. CDE primenanti darbastalio aplinka
+Comment[lv]=Darbvirsmas vide bez holesterīna, versija 4. Darbavirsmas vide, kas atgādina CDE
+Comment[mk]=Cholesterol Free Desktop Environment, верзија 4. Работна околина која потсетува на CDE
+Comment[ms]=Persekitaran Desktop Bebas Kolesterol, versi 4. Mengingatkan kembali persekitaran desktop CDE
+Comment[mt]=Cholesterol Free Desktop Environment (v4). Ambjent grafiku li jixbaħ lis-CDE
+Comment[nb]=Det kolesterolfrie skrivebordet, versjon 4. Et skrivebordsmiljø som minner om CDE
+Comment[nds]=De "Cholesterol Free Desktop Environment", Verschoon 4. En Schriefdisch-Ümgeven, de wat liek is to CDE
+Comment[ne]=कोलेस्ट्रोल रहित डेस्कटप परिवेश, संस्करण ४ । CDE को स्मरणशील डेस्कटप परिवेश
+Comment[nl]=De Cholesterol Free Desktop Environment, versie 4. Een desktop environment die herinnert aan CDE
+Comment[nn]=Cholesterol Free Desktop Environment. Eit skrivebordsmiljø som minner om CDE
+Comment[pa]=ਚੋਲੀਸਟੀਰੋਲ ਮੁਫਤ ਵਾਤਾਵਰਣ, ਵਰਜਨ 4 ਇੱਕ CDE ਵਰਗਾ ਵਾਤਾਵਰਣ
+Comment[pl]=Cholesterol Free Desktop Environment, wersja 4 - środowisko graficzne podobne do CDE.
+Comment[pt]=O Cholesterol Free Desktop Environment, versão 4. Um ambiente de trabalho com vestígios do CDE
+Comment[pt_BR]=Acrônimo para Cholesterol Free Desktop Environment (ou ambiente livre de colesterol), versão 4; um ambiente de trabalho remanescente do CDE
+Comment[ro]=Cholesterol Free Desktop Environment, versiunea 4. Un mediu grafic cu reminescente din CDE
+Comment[ru]="Не содержащая холестерина" рабочая среда xfce версии 4, напоминающая CDE
+Comment[rw]=Ibikikije Ibiro Ubuntu Kolesiterole, verisiyo 4. Ibikikije ibiro nkumburwa bya CDE
+Comment[se]=The Cholesterol Free Desktop Environment, version 4. Čállinbeavdebiras mii muittuha CDE
+Comment[sk]=The Cholesterol Free Desktop Environment verzia 4. Pracovné prostredie pripomínajúce CDE
+Comment[sl]=Cholesterol Free Desktop Environment, različica 4. Namizno okolje, podobno okolju CDE
+Comment[sr]=„The Cholesterol Free Desktop Environment“ 4. издање. Радно окружење које подсећа на CDE
+Comment[sr@Latn]=„The Cholesterol Free Desktop Environment“ 4. izdanje. Radno okruženje koje podseća na CDE
+Comment[sv]=Den kolesterolfria skrivbordsmiljön, version 4. En skrivbordsmiljö som påminner om CDE
+Comment[ta]=கொலஸ்ட்ரால் இல்லாத மேல்மேசை சூழல். பதிப்பு 4. CDE பொருள்பொதிந்த மேல்மேசை சூழல்
+Comment[th]=สภาพแวดล้อมสำหรับเดสก์ทอปแบบไร้คอเลสเตอรอล เวอร์ชั่น 4 เป็นสภาพแวดล้อมสำหรับเดสก์ทอปที่เหลือมาจาก CDE
+Comment[tr]=Cholesterol Ücretsiz Masaüstü Ortamı, sürüm 4. CDE'nin benzeri olan masaüstü ortamı
+Comment[tt]=Holesterol Bulmağan Östäl Möxiteneñ 4. söreme. CDE küreneşendä
+Comment[uk]=The Cholesterol Free Desktop Environment, версія 4. Графічне середовище, що нагадує CDE
+Comment[vi]=Môi trường Màn hình nền Không có Cholesterol, phiên bản 4. Một môi trường màn hình nền gợi nhớ lại CDE
+Comment[wa]=Li Libe Evironmint di Scribanne Colesterole (Cholesterol Free Desktop Environment), modêye 4. On evironmint d' sicribanne ki nos vént d' CDE
+Comment[zh_CN]=胆固醇自由桌面环境,版本 4。CDE 桌面环境的追随者
+Comment[zh_TW]=Cholesterol 免費桌面環境,第 4 版。一個另人懷念的 CDE 桌面環境
diff --git a/kdm/kfrontend/themer/Makefile.am b/kdm/kfrontend/themer/Makefile.am
new file mode 100644
index 000000000..7f6eb5701
--- /dev/null
+++ b/kdm/kfrontend/themer/Makefile.am
@@ -0,0 +1,16 @@
+AM_CPPFLAGS = -I$(srcdir)/../../backend -I$(srcdir)/.. -I../.. \
+ -I$(top_srcdir)/kdmlib \
+ $(all_includes)
+
+noinst_LIBRARIES = libkdmthemer.a
+libkdmthemer_a_SOURCES = \
+ kdmthemer.cpp \
+ kdmitem.cpp \
+ kdmpixmap.cpp \
+ kdmrect.cpp \
+ kdmlabel.cpp \
+ kdmlayout.cpp
+
+METASOURCES = AUTO
+
+libkdmthemer_a_COMPILE_FIRST = ../../config.ci
diff --git a/kdm/kfrontend/themer/kdmitem.cpp b/kdm/kfrontend/themer/kdmitem.cpp
new file mode 100644
index 000000000..48c0d1faf
--- /dev/null
+++ b/kdm/kfrontend/themer/kdmitem.cpp
@@ -0,0 +1,532 @@
+/*
+ * Copyright (C) 2003 by Unai Garro <ugarro@users.sourceforge.net>
+ * Copyright (C) 2004 by Enrico Ros <rosenric@dei.unipd.it>
+ * Copyright (C) 2004 by Stephan Kulow <coolo@kde.org>
+ * Copyright (C) 2004 by Oswald Buddenhagen <ossi@kde.org>
+ *
+ * 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.
+ */
+
+/*
+ * Generic Kdm Item
+ */
+
+//#define DRAW_OUTLINE 1 // for debugging only
+
+#include "kdmitem.h"
+#include "kdmlayout.h"
+
+#include <kglobal.h>
+#include <kdebug.h>
+
+#include <qframe.h>
+#include <qwidget.h>
+#include <qlayout.h>
+#include <qimage.h>
+#ifdef DRAW_OUTLINE
+# include <qpainter.h>
+#endif
+
+KdmItem::KdmItem( KdmItem *parent, const QDomNode &node, const char *name )
+ : QObject( parent, name )
+ , boxManager( 0 )
+ , fixedManager( 0 )
+ , image( 0 )
+ , myWidget( 0 )
+ , myLayoutItem( 0 )
+ , buttonParent( 0 )
+{
+ // Set default layout for every item
+ currentManager = MNone;
+ pos.x = pos.y = 0;
+ pos.width = pos.height = 1;
+ pos.xType = pos.yType = pos.wType = pos.hType = DTnone;
+ pos.anchor = "nw";
+
+ isShown = InitialHidden;
+
+ // Set defaults for derived item's properties
+ properties.incrementalPaint = false;
+ state = Snormal;
+
+ // The "toplevel" node (the screen) is really just like a fixed node
+ if (!parent || !parent->inherits( "KdmItem" )) {
+ setFixedLayout();
+ return;
+ }
+ // Read the mandatory Pos tag. Other tags such as normal, prelighted,
+ // etc.. are read under specific implementations.
+ QDomNodeList childList = node.childNodes();
+ for (uint nod = 0; nod < childList.count(); nod++) {
+ QDomNode child = childList.item( nod );
+ QDomElement el = child.toElement();
+ QString tagName = el.tagName(), attr;
+
+ if (tagName == "pos") {
+ parseAttribute( el.attribute( "x", QString::null ), pos.x, pos.xType );
+ parseAttribute( el.attribute( "y", QString::null ), pos.y, pos.yType );
+ parseAttribute( el.attribute( "width", QString::null ), pos.width, pos.wType );
+ parseAttribute( el.attribute( "height", QString::null ), pos.height, pos.hType );
+ pos.anchor = el.attribute( "anchor", "nw" );
+ }
+ }
+
+ QDomNode tnode = node;
+ id = tnode.toElement().attribute( "id", QString::number( (ulong)this, 16 ) );
+
+ // Tell 'parent' to add 'me' to its children
+ KdmItem *parentItem = static_cast<KdmItem *>( parent );
+ parentItem->addChildItem( this );
+}
+
+KdmItem::~KdmItem()
+{
+ delete boxManager;
+ delete fixedManager;
+ delete image;
+}
+
+void
+KdmItem::update()
+{
+}
+
+void
+KdmItem::needUpdate()
+{
+ emit needUpdate( area.x(), area.y(), area.width(), area.height() );
+}
+
+void
+KdmItem::show( bool force )
+{
+ if (isShown != InitialHidden && !force)
+ return;
+
+ QValueList<KdmItem *>::Iterator it;
+ for (it = m_children.begin(); it != m_children.end(); ++it)
+ (*it)->show();
+
+ isShown = Shown;
+
+ if (myWidget)
+ myWidget->show();
+ // XXX showing of layouts not implemented, prolly pointless anyway
+
+ needUpdate();
+}
+
+void
+KdmItem::hide( bool force )
+{
+ if (isShown == ExplicitlyHidden)
+ return;
+
+ if (isShown == InitialHidden && force) {
+ isShown = ExplicitlyHidden;
+ return; // no need for further action
+ }
+
+ QValueList<KdmItem *>::Iterator it;
+ for (it = m_children.begin(); it != m_children.end(); ++it)
+ (*it)->hide();
+
+ isShown = force ? ExplicitlyHidden : InitialHidden;
+
+ if (myWidget)
+ myWidget->hide();
+ // XXX hiding of layouts not implemented, prolly pointless anyway
+
+ needUpdate();
+}
+
+void
+KdmItem::inheritFromButton( KdmItem *button )
+{
+ if (button)
+ buttonParent = button;
+
+ QValueList<KdmItem *>::Iterator it;
+ for (it = m_children.begin(); it != m_children.end(); ++it)
+ (*it)->inheritFromButton( button );
+}
+
+KdmItem *
+KdmItem::findNode( const QString &_id ) const
+{
+ if (id == _id)
+ return const_cast<KdmItem *>( this );
+
+ QValueList<KdmItem *>::ConstIterator it;
+ for (it = m_children.begin(); it != m_children.end(); ++it) {
+ KdmItem *t = (*it)->findNode( _id );
+ if (t)
+ return t;
+ }
+
+ return 0;
+}
+
+void
+KdmItem::setWidget( QWidget *widget )
+{
+// delete myWidget; -- themer->widget() owns the widgets
+
+ myWidget = widget;
+ if (isHidden())
+ myWidget->hide();
+ else
+ myWidget->show();
+
+ // Remove borders so that it blends nicely with the theme background
+ QFrame* frame = ::qt_cast<QFrame *>( widget );
+ if (frame)
+ frame->setFrameStyle( QFrame::NoFrame );
+
+ myWidget->setGeometry(area);
+
+ connect( myWidget, SIGNAL(destroyed()), SLOT(widgetGone()) );
+}
+
+void
+KdmItem::widgetGone()
+{
+ myWidget = 0;
+}
+
+void
+KdmItem::setLayoutItem( QLayoutItem *item )
+{
+ myLayoutItem = item;
+ // XXX hiding not supported - it think it's pointless here
+ if (myLayoutItem->widget())
+ connect( myLayoutItem->widget(), SIGNAL(destroyed()),
+ SLOT(layoutItemGone()) );
+ else if (myLayoutItem->layout())
+ connect( myLayoutItem->layout(), SIGNAL(destroyed()),
+ SLOT(layoutItemGone()) );
+}
+
+void
+KdmItem::layoutItemGone()
+{
+ myLayoutItem = 0;
+}
+
+/* This is called as a result of KdmLayout::update, and directly on the root */
+void
+KdmItem::setGeometry( const QRect &newGeometry, bool force )
+{
+ kdDebug() << " KdmItem::setGeometry " << id << newGeometry << endl;
+ // check if already 'in place'
+ if (!force && area == newGeometry)
+ return;
+
+ area = newGeometry;
+
+ if (myWidget)
+ myWidget->setGeometry( newGeometry );
+ if (myLayoutItem)
+ myLayoutItem->setGeometry( newGeometry );
+
+ // recurr to all boxed children
+ if (boxManager && !boxManager->isEmpty())
+ boxManager->update( newGeometry, force );
+
+ // recurr to all fixed children
+ if (fixedManager && !fixedManager->isEmpty())
+ fixedManager->update( newGeometry, force );
+
+ // TODO send *selective* repaint signal
+}
+
+void
+KdmItem::paint( QPainter *p, const QRect &rect )
+{
+ if (isHidden())
+ return;
+
+ if (myWidget || (myLayoutItem && myLayoutItem->widget()))
+ return;
+
+ if (area.intersects( rect )) {
+ QRect contentsRect = area.intersect( rect );
+ contentsRect.moveBy( QMIN( 0, -area.x() ), QMIN( 0, -area.y() ) );
+ drawContents( p, contentsRect );
+ }
+
+#ifdef DRAW_OUTLINE
+ // Draw bounding rect for this item
+ p->setPen( Qt::white );
+ p->drawRect( area );
+#endif
+
+ if (myLayoutItem)
+ return;
+
+ // Dispatch paint events to children
+ QValueList<KdmItem *>::Iterator it;
+ for (it = m_children.begin(); it != m_children.end(); ++it)
+ (*it)->paint( p, rect );
+}
+
+KdmItem *KdmItem::currentActive = 0;
+
+void
+KdmItem::mouseEvent( int x, int y, bool pressed, bool released )
+{
+ if (buttonParent && buttonParent != this) {
+ buttonParent->mouseEvent( x, y, pressed, released );
+ return;
+ }
+
+ ItemState oldState = state;
+ if (area.contains( x, y )) {
+ if (released && oldState == Sactive) {
+ if (buttonParent)
+ emit activated( id );
+ state = Sprelight;
+ currentActive = 0;
+ } else if (pressed || currentActive == this) {
+ state = Sactive;
+ currentActive = this;
+ } else if (!currentActive)
+ state = Sprelight;
+ else
+ state = Snormal;
+ } else {
+ if (released)
+ currentActive = 0;
+ if (currentActive == this)
+ state = Sprelight;
+ else
+ state = Snormal;
+ }
+
+ if (!buttonParent) {
+ QValueList<KdmItem *>::Iterator it;
+ for (it = m_children.begin(); it != m_children.end(); ++it)
+ (*it)->mouseEvent( x, y, pressed, released );
+ }
+
+ if (oldState != state)
+ statusChanged();
+}
+
+void
+KdmItem::statusChanged()
+{
+ if (buttonParent == this) {
+ QValueList<KdmItem *>::Iterator it;
+ for (it = m_children.begin(); it != m_children.end(); ++it) {
+ (*it)->state = state;
+ (*it)->statusChanged();
+ }
+ }
+}
+
+// BEGIN protected inheritable
+
+QSize
+KdmItem::sizeHint()
+{
+ if (myWidget)
+ return myWidget->size();
+ if (myLayoutItem)
+ return myLayoutItem->sizeHint();
+ int w = pos.wType == DTpixel ? kAbs( pos.width ) : -1,
+ h = pos.hType == DTpixel ? kAbs( pos.height ) : -1;
+ return QSize( w, h );
+}
+
+QRect
+KdmItem::placementHint( const QRect &parentRect )
+{
+ QSize hintedSize = sizeHint();
+ QSize boxHint;
+
+ int x = parentRect.left(),
+ y = parentRect.top(),
+ w = parentRect.width(),
+ h = parentRect.height();
+
+ kdDebug() << "KdmItem::placementHint parentRect=" << id << parentRect << " hintedSize=" << hintedSize << endl;
+ // check if width or height are set to "box"
+ if (pos.wType == DTbox || pos.hType == DTbox) {
+ if (myLayoutItem || myWidget)
+ boxHint = hintedSize;
+ else {
+ if (!boxManager)
+ return parentRect;
+ boxHint = boxManager->sizeHint();
+ }
+ kdDebug() << " => boxHint " << boxHint << endl;
+ }
+
+ if (pos.xType == DTpixel)
+ x += pos.x;
+ else if (pos.xType == DTnpixel)
+ x = parentRect.right() - pos.x;
+ else if (pos.xType == DTpercent)
+ x += int( parentRect.width() / 100.0 * pos.x );
+
+ if (pos.yType == DTpixel)
+ y += pos.y;
+ else if (pos.yType == DTnpixel)
+ y = parentRect.bottom() - pos.y;
+ else if (pos.yType == DTpercent)
+ y += int( parentRect.height() / 100.0 * pos.y );
+
+ if (pos.wType == DTpixel)
+ w = pos.width;
+ else if (pos.wType == DTnpixel)
+ w -= pos.width;
+ else if (pos.wType == DTpercent)
+ w = int( parentRect.width() / 100.0 * pos.width );
+ else if (pos.wType == DTbox)
+ w = boxHint.width();
+ else if (hintedSize.width() > 0)
+ w = hintedSize.width();
+ else
+ w = 0;
+
+ if (pos.hType == DTpixel)
+ h = pos.height;
+ else if (pos.hType == DTnpixel)
+ h -= pos.height;
+ else if (pos.hType == DTpercent)
+ h = int( parentRect.height() / 100.0 * pos.height );
+ else if (pos.hType == DTbox)
+ h = boxHint.height();
+ else if (hintedSize.height() > 0)
+ h = hintedSize.height();
+ else
+ h = 0;
+
+ // defaults to center
+ int dx = -w / 2, dy = -h / 2;
+
+ // anchor the rect to an edge / corner
+ if (pos.anchor.length() > 0 && pos.anchor.length() < 3) {
+ if (pos.anchor.find( 'n' ) >= 0)
+ dy = 0;
+ if (pos.anchor.find( 's' ) >= 0)
+ dy = -h;
+ if (pos.anchor.find( 'w' ) >= 0)
+ dx = 0;
+ if (pos.anchor.find( 'e' ) >= 0)
+ dx = -w;
+ }
+ // KdmItem *p = static_cast<KdmItem*>( parent() );
+ kdDebug() << "KdmItem::placementHint " << id << " x=" << x << " dx=" << dx << " w=" << w << " y=" << y << " dy=" << dy << " h=" << h << " " << parentRect << endl;
+ y += dy;
+ x += dx;
+
+ // Note: no clipping to parent because this broke many themes!
+ return QRect( x, y, w, h );
+}
+
+// END protected inheritable
+
+
+void
+KdmItem::addChildItem( KdmItem *item )
+{
+ m_children.append( item );
+ switch (currentManager) {
+ case MNone: // fallback to the 'fixed' case
+ setFixedLayout();
+ case MFixed:
+ fixedManager->addItem( item );
+ break;
+ case MBox:
+ boxManager->addItem( item );
+ break;
+ }
+
+ // signal bounce from child to parent
+ connect( item, SIGNAL(needUpdate( int, int, int, int )), SIGNAL(needUpdate( int, int, int, int )) );
+ connect( item, SIGNAL(activated( const QString & )), SIGNAL(activated( const QString & )) );
+}
+
+void
+KdmItem::parseAttribute( const QString &s, int &val, enum DataType &dType )
+{
+ if (s.isEmpty())
+ return;
+
+ int p;
+ if (s == "box") { // box value
+ dType = DTbox;
+ val = 0;
+ } else if ((p = s.find( '%' )) >= 0) { // percent value
+ dType = DTpercent;
+ QString sCopy = s;
+ sCopy.remove( p, 1 );
+ sCopy.replace( ',', '.' );
+ val = (int)sCopy.toDouble();
+ } else { // int value
+ dType = DTpixel;
+ QString sCopy = s;
+ if (sCopy.at( 0 ) == '-') {
+ sCopy.remove( 0, 1 );
+ dType = DTnpixel;
+ }
+ sCopy.replace( ',', '.' );
+ val = (int)sCopy.toDouble();
+ }
+}
+
+void
+KdmItem::parseFont( const QString &s, QFont &font )
+{
+ int splitAt = s.findRev( ' ' );
+ if (splitAt < 1)
+ return;
+ font.setFamily( s.left( splitAt ) );
+ int fontSize = s.mid( splitAt + 1 ).toInt();
+ if (fontSize > 1)
+ font.setPointSize( fontSize );
+}
+
+void
+KdmItem::parseColor( const QString &s, QColor &color )
+{
+ if (s.at( 0 ) != '#')
+ return;
+ bool ok;
+ QString sCopy = s;
+ int hexColor = sCopy.remove( 0, 1 ).toInt( &ok, 16 );
+ if (ok)
+ color.setRgb( hexColor );
+}
+
+void
+KdmItem::setBoxLayout( const QDomNode &node )
+{
+ if (!boxManager)
+ boxManager = new KdmLayoutBox( node );
+ currentManager = MBox;
+}
+
+void
+KdmItem::setFixedLayout( const QDomNode &node )
+{
+ if (!fixedManager)
+ fixedManager = new KdmLayoutFixed( node );
+ currentManager = MFixed;
+}
+
+#include "kdmitem.moc"
diff --git a/kdm/kfrontend/themer/kdmitem.h b/kdm/kfrontend/themer/kdmitem.h
new file mode 100644
index 000000000..66feedd02
--- /dev/null
+++ b/kdm/kfrontend/themer/kdmitem.h
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2003 by Unai Garro <ugarro@users.sourceforge.net>
+ * Copyright (C) 2004 by Enrico Ros <rosenric@dei.unipd.it>
+ * Copyright (C) 2004 by Stephan Kulow <coolo@kde.org>
+ * Copyright (C) 2004 by Oswald Buddenhagen <ossi@kde.org>
+ *
+ * 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.
+ */
+
+#ifndef KDMITEM_H
+#define KDMITEM_H
+
+#include <qobject.h>
+#include <qvaluelist.h>
+#include <qrect.h>
+#include <qdom.h>
+
+class KdmItem;
+class KdmLayoutBox;
+class KdmLayoutFixed;
+
+class QPainter;
+class QLayoutItem;
+
+/** class KdmItem
+ * @short Base class for every kdmthemes' element.
+ *
+ * This class provides methods for arranging it and its children to the
+ * screen (see note below), painting the whole area or a sub-region using
+ * an opened painter, handling mouse events or events in general dispatching
+ * them to children and sending some signals to the root (for example on
+ * mouse click).
+ *
+ * KdmItem sits in a hierarchical top to bottom tree with signals that
+ * traverse the tree back from leafs (or inner nodes) to the root.
+ *
+ * To implement a KdmItem only a few virtual protected methods must be
+ * reimplemented, other virtual functions are there for convenience only -
+ * the default implementation should satisfy your needs.
+ */
+
+/**
+ * A note on layouting - how does it work?
+ * - setgeometry is called by parent (passing the new geometry)
+ * - item changes its geometry
+ * - if item embeds a widget, reposition it too
+ * - call children's box manager. box->update( my geom )
+ * - sum up the whole space taken by children (via *hint calls) if
+ * needed for box width / height computation. note that the computed
+ * geometry should be equal or similar to parent's geometry.
+ * - pad the rectangle bounding box' contents
+ * - for every child
+ * - if vertical
+ * ( use a top-to-bottom insertion, spacing insertion lines by
+ * children's individual height )
+ * - set up a zero height Parent (placed at the insertion line's
+ * position) and get Geom = child->placementHint( p )
+ * - set up child's Size using Parent's width and Geom's height.
+ * - call to child->setGeometry( Parent.topLeft, Size )
+ * - if horizontal
+ * - flows like the vertical one but uses a left-to-right insertion
+ * and insertion entry points are vertical lines
+ * - call to children's fix manager. fixed->update( my geom )
+ * - for every child
+ * - S = get child's geometry hint (and we'll give item the whole
+ * space it needs, without constraints)
+ * - call to child->setGeometry( S )
+ * - TODO: send a selective redraw signal also merging children's areas
+ */
+
+class KdmItem : public QObject {
+ Q_OBJECT
+
+ friend class KdmThemer;
+
+public:
+ /**
+ * Item constructor and destructor
+ */
+ KdmItem( KdmItem *parent, const QDomNode &node = QDomNode(), const char *name = 0 );
+ virtual ~KdmItem();
+
+ /**
+ * Fixup the geometry of an item and its children (even if fixed
+ * or boxed ones). Note that this will generate repaint signals
+ * when needed. The default implementation should fit all needs.
+ */
+ virtual void setGeometry( const QRect &newGeometry, bool force );
+
+ /**
+ * Paint the item and its children using the given painter.
+ * This is the compositing core function. It buffers paint operations
+ * to speed up rendering of dynamic objects.
+ */
+ void paint( QPainter *painter, const QRect &boundaries );
+
+ /**
+ * Update representation of contents and repaint.
+ */
+ virtual void update();
+
+ /**
+ * Handle mouse motion and dispatch events to children. This
+ * leads to items prelighting, activation() on click and more..
+ */
+ void mouseEvent( int x, int y, bool pressed = false, bool released = false );
+
+ /**
+ * Similar to sizeHint(..), this returns the area of the item
+ * given the @p parentGeometry. The default implementation
+ * takes into account geometric constraints and layoutings.
+ * @param parentGeometry the geometry of the caller item or a
+ * null rect if the geometry of the parent is not yet defined.
+ */
+ virtual QRect placementHint( const QRect &parentGeometry );
+
+ /**
+ * Create the box layout manager; next children will be
+ * managed by the box layouter
+ */
+ void setBoxLayout( const QDomNode &node = QDomNode() );
+
+ /**
+ * Create the fixed layout manager; next children will be
+ * in fixed position relative to this item
+ */
+ void setFixedLayout( const QDomNode &node = QDomNode() );
+
+ QString type() const { return itemType; }
+ void setType( const QString &t ) { itemType = t; }
+ void setBaseDir( const QString &bd ) { basedir = bd; }
+
+ QString baseDir() const
+ {
+ if (basedir.isEmpty() && parent())
+ return static_cast<KdmItem *>( parent()->qt_cast( "KdmItem" ) )->baseDir();
+ return basedir;
+ }
+
+ KdmItem *findNode( const QString &id ) const;
+ virtual void setWidget( QWidget *widget );
+ virtual void setLayoutItem( QLayoutItem *item );
+
+ virtual void hide( bool force = false );
+ virtual void show( bool force = false );
+
+ bool isHidden() const { return isShown != Shown; }
+ bool isExplicitlyHidden() const { return isShown == ExplicitlyHidden; }
+ QRect rect() const { return area; }
+
+signals:
+ void needUpdate( int x, int y, int w, int h );
+ void activated( const QString &id );
+
+protected slots:
+ void widgetGone();
+ void layoutItemGone();
+
+protected:
+ /**
+ * Returns the optimal/minimal size for this item.
+ * This should be reimplemented in items like label and pixmap.
+ * @return (-1,-1) if no size can be determined (so it should
+ * default to parent's size).
+ */
+ virtual QSize sizeHint();
+
+ /**
+ * Low level graphical function to paint the item.
+ * All items must reimplement this function to draw themeselves
+ * (or a part of) into the @p image keeping inside the @p rect .
+ * Try to do this as fast as possible.
+ * @param painter the painter to draw the item with
+ * @param region the part of the the image to render
+ */
+ virtual void drawContents( QPainter *painter, const QRect &region ) = 0;
+
+ /**
+ * Called when item changes its 'state' variable. This must
+ * handle item's repaint.
+ */
+ virtual void statusChanged();
+
+ /**
+ * emits needUpdate( int, int, int, int ) with the full widget area.
+ */
+ void needUpdate();
+
+ // This enum identifies in which state the item is
+ enum ItemState { Snormal, Sactive, Sprelight } state;
+
+ static KdmItem *currentActive;
+
+ // This struct can be filled in by derived items
+ struct {
+ bool incrementalPaint;
+ } properties;
+
+ // This is the placement of the item
+ QRect area;
+
+ // This struct is filled in by KdmItem base class
+ enum DataType { DTnone, DTpixel, DTnpixel, DTpercent, DTbox };
+ struct {
+ enum DataType xType, yType, wType, hType;
+ int x;
+ int y;
+ int width;
+ int height;
+ QString anchor;
+ } pos;
+
+ /* For internal use ONLY
+ * Add a child item. This function is called automatically
+ * when constructing an @p item with this as the parent.
+ */
+ void addChildItem( KdmItem *item );
+
+ /* For internal use ONLY
+ * Parse type and value of an attribute (pos tag), a font or a
+ * color.
+ */
+ void parseAttribute( const QString &, int &, enum DataType & );
+ void parseFont( const QString &, QFont & );
+ void parseColor( const QString &, QColor & );
+
+ void inheritFromButton( KdmItem *button );
+
+ QString itemType, id;
+ QValueList<KdmItem *> m_children;
+
+ // Layouting related variables
+ enum { MNone = 0, MFixed = 1, MBox = 2 } currentManager;
+ KdmLayoutBox *boxManager;
+ KdmLayoutFixed *fixedManager;
+
+ // Compositing related variables
+ QImage *image;
+
+ // defines the directory the theme is in (may be present in the parent)
+ QString basedir;
+
+ QWidget *myWidget;
+ QLayoutItem *myLayoutItem;
+
+ enum { InitialHidden, ExplicitlyHidden, Shown } isShown;
+
+ KdmItem *buttonParent;
+};
+
+#endif
diff --git a/kdm/kfrontend/themer/kdmlabel.cpp b/kdm/kfrontend/themer/kdmlabel.cpp
new file mode 100644
index 000000000..41d7e4254
--- /dev/null
+++ b/kdm/kfrontend/themer/kdmlabel.cpp
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2003 by Unai Garro <ugarro@users.sourceforge.net>
+ * Copyright (C) 2004 by Enrico Ros <rosenric@dei.unipd.it>
+ * Copyright (C) 2004 by Stephan Kulow <coolo@kde.org>
+ * Copyright (C) 2004 by Oswald Buddenhagen <ossi@kde.org>
+ *
+ * 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.
+ */
+
+#include "kdmlabel.h"
+#include <kgreeter.h>
+
+#include <kglobal.h>
+#include <klocale.h>
+#include <kmacroexpander.h>
+#include <kdebug.h>
+
+#include <qdatetime.h>
+#include <qpainter.h>
+#include <qfontmetrics.h>
+#include <qtimer.h>
+
+#include <unistd.h>
+#include <sys/utsname.h>
+#if !defined(HAVE_GETDOMAINNAME) && defined(HAVE_SYSINFO)
+# include <sys/systeminfo.h>
+#endif
+
+KdmLabel::KdmLabel( KdmItem *parent, const QDomNode &node, const char *name )
+ : KdmItem( parent, node, name )
+{
+ itemType = "label";
+
+ // Set default values for label (note: strings are already Null)
+ label.active.color.setRgb( 0xFFFFFF );
+ label.active.present = false;
+ label.prelight.present = false;
+ label.maximumWidth = -1;
+
+ const QString locale = KGlobal::locale()->language();
+
+ // Read LABEL ID
+ QDomNode n = node;
+ QDomElement elLab = n.toElement();
+ // ID types: clock, pam-error, pam-message, pam-prompt,
+ // pam-warning, timed-label
+ label.id = elLab.attribute( "id", "" );
+ label.hasId = !(label.id).isEmpty();
+
+ // Read LABEL TAGS
+ QDomNodeList childList = node.childNodes();
+ bool stockUsed = false;
+ for (uint nod = 0; nod < childList.count(); nod++) {
+ QDomNode child = childList.item( nod );
+ QDomElement el = child.toElement();
+ QString tagName = el.tagName();
+
+ if (tagName == "pos")
+ label.maximumWidth = el.attribute( "max-width", "-1" ).toInt();
+ else if (tagName == "normal") {
+ parseColor( el.attribute( "color", "#ffffff" ), label.normal.color );
+ parseFont( el.attribute( "font", "Sans 14" ), label.normal.font );
+ } else if (tagName == "active") {
+ label.active.present = true;
+ parseColor( el.attribute( "color", "#ffffff" ), label.active.color );
+ parseFont( el.attribute( "font", "Sans 14" ), label.active.font );
+ } else if (tagName == "prelight") {
+ label.prelight.present = true;
+ parseColor( el.attribute( "color", "#ffffff" ), label.prelight.color );
+ parseFont( el.attribute( "font", "Sans 14" ), label.prelight.font );
+ } else if (tagName == "text" && el.attributes().count() == 0 && !stockUsed) {
+ label.text = el.text();
+ } else if (tagName == "text" && !stockUsed) {
+ QString lang = el.attribute( "xml:lang", "" );
+ if (lang == locale)
+ label.text = el.text();
+ } else if (tagName == "stock") {
+ label.text = lookupStock( el.attribute( "type", "" ) );
+ stockUsed = true;
+ }
+ }
+
+ // Check if this is a timer label
+ label.isTimer = label.text.find( "%c" ) >= 0;
+ if (label.isTimer) {
+ timer = new QTimer( this );
+ timer->start( 1000 );
+ connect( timer, SIGNAL(timeout()), SLOT(update()) );
+ }
+ cText = lookupText( label.text );
+}
+
+void
+KdmLabel::setText( const QString &txt )
+{
+ label.text = txt;
+ update();
+}
+
+QSize
+KdmLabel::sizeHint()
+{
+ // choose the correct label class
+ struct LabelStruct::LabelClass *l = &label.normal;
+ if (state == Sactive && label.active.present)
+ l = &label.active;
+ else if (state == Sprelight && label.prelight.present)
+ l = &label.prelight;
+ // get the hint from font metrics
+ QSize hint = QFontMetrics( l->font ).size( AlignLeft | SingleLine, cText );
+ // clip the result using the max-width label(pos) parameter
+ if (label.maximumWidth > 0 && hint.width() > label.maximumWidth)
+ hint.setWidth( label.maximumWidth );
+ return hint;
+}
+
+void
+KdmLabel::drawContents( QPainter *p, const QRect &/*r*/ )
+{
+ // choose the correct label class
+ struct LabelStruct::LabelClass *l = &label.normal;
+ if (state == Sactive && label.active.present)
+ l = &label.active;
+ else if (state == Sprelight && label.prelight.present)
+ l = &label.prelight;
+ // draw the label
+ p->setFont( l->font );
+ p->setPen( l->color );
+ //TODO paint clipped (tested but not working..)
+ p->drawText( area, AlignLeft | SingleLine, cText );
+}
+
+void
+KdmLabel::statusChanged()
+{
+ KdmItem::statusChanged();
+ if (!label.active.present && !label.prelight.present)
+ return;
+ if ((state == Sprelight && !label.prelight.present) ||
+ (state == Sactive && !label.active.present))
+ return;
+ needUpdate();
+}
+
+void
+KdmLabel::update()
+{
+ QString text = lookupText( label.text );
+ if (text != cText) {
+ cText = text;
+ needUpdate();
+ }
+}
+
+static const struct {
+ const char *type, *text;
+} stocks[] = {
+ { "language", I18N_NOOP("Language") },
+ { "session", I18N_NOOP("Session Type") },
+ { "system", I18N_NOOP("Menu") }, // i18n("Actions");
+ { "disconnect", I18N_NOOP("Disconnect") },
+ { "quit", I18N_NOOP("Quit") },
+ { "halt", I18N_NOOP("Power off") },
+ { "suspend", I18N_NOOP("Suspend") },
+ { "reboot", I18N_NOOP("Reboot") },
+ { "chooser", I18N_NOOP("XDMCP Chooser") },
+ { "config", I18N_NOOP("Configure") },
+ { "caps-lock-warning", I18N_NOOP("You have got caps lock on.") },
+ { "timed-label", I18N_NOOP("User %s will login in %d seconds") },
+ { "welcome-label", I18N_NOOP("Welcome to %h") }, // _greetString
+ { "username-label", I18N_NOOP("Username:") },
+ { "password-label", I18N_NOOP("Password:") },
+ { "login", I18N_NOOP("Login") }
+};
+
+QString
+KdmLabel::lookupStock( const QString &stock )
+{
+ //FIXME add key accels!
+ QString type( stock.lower() );
+
+ for (uint i = 0; i < sizeof(stocks)/sizeof(stocks[0]); i++)
+ if (type == stocks[i].type)
+ return i18n(stocks[i].text);
+
+ kdDebug() << "Invalid <stock> element. Check your theme!" << endl;
+ return stock;
+}
+
+QString
+KdmLabel::lookupText( const QString &t )
+{
+ QString text = t;
+
+ text.replace( '_', '&' );
+// text.remove( '_' ); // FIXME add key accels, remove underscores for now
+
+ QMap<QChar,QString> m;
+ struct utsname uts;
+ uname( &uts );
+ m['n'] = QString::fromLocal8Bit( uts.nodename );
+ char buf[256];
+ buf[sizeof(buf) - 1] = '\0';
+ m['h'] = gethostname( buf, sizeof(buf) - 1 ) ? "localhost" : QString::fromLocal8Bit( buf );
+#ifdef HAVE_GETDOMAINNAME
+ m['o'] = getdomainname( buf, sizeof(buf) - 1 ) ? "localdomain" : QString::fromLocal8Bit( buf );
+#elif defined(HAVE_SYSINFO)
+ m['o'] = (unsigned)sysinfo( SI_SRPC_DOMAIN, buf, sizeof(buf) ) > sizeof(buf) ? "localdomain" : QString::fromLocal8Bit( buf );
+#endif
+ m['d'] = QString::number( KThemedGreeter::timedDelay );
+ m['s'] = KThemedGreeter::timedUser;
+ // xgettext:no-c-format
+ KGlobal::locale()->setDateFormat( i18n("date format", "%a %d %B") );
+ m['c'] = KGlobal::locale()->formatDateTime( QDateTime::currentDateTime(), false, false );
+
+ return KMacroExpander::expandMacros( text, m );
+}
+
+#include "kdmlabel.moc"
diff --git a/kdm/kfrontend/themer/kdmlabel.h b/kdm/kfrontend/themer/kdmlabel.h
new file mode 100644
index 000000000..b80d0189a
--- /dev/null
+++ b/kdm/kfrontend/themer/kdmlabel.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2003 by Unai Garro <ugarro@users.sourceforge.net>
+ * Copyright (C) 2004 by Enrico Ros <rosenric@dei.unipd.it>
+ * Copyright (C) 2004 by Stephan Kulow <coolo@kde.org>
+ * Copyright (C) 2004 by Oswald Buddenhagen <ossi@kde.org>
+ *
+ * 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.
+ */
+
+#ifndef KDELABEL_H
+#define KDELABEL_H
+
+#include "kdmitem.h"
+
+#include <qcolor.h>
+#include <qfont.h>
+
+class QTimer;
+
+/*
+ * KdmLabel. A label element
+ */
+
+class KdmLabel : public KdmItem {
+ Q_OBJECT
+
+public:
+ KdmLabel( KdmItem *parent, const QDomNode &node, const char *name = 0 );
+ void setText( const QString &txt );
+
+protected:
+ // reimplemented; returns the minimum size of rendered text
+ virtual QSize sizeHint();
+
+ // draw the label
+ virtual void drawContents( QPainter *p, const QRect &r );
+
+ // handle switching between normal / active / prelight configurations
+ virtual void statusChanged();
+
+ struct LabelStruct {
+ QString text;
+ bool isTimer;
+ bool hasId;
+ QString id;
+ struct LabelClass {
+ QColor color;
+ QFont font;
+ bool present;
+ } normal, active, prelight;
+ int maximumWidth;
+ } label;
+
+ QTimer *timer;
+
+public slots:
+ void update();
+
+private:
+ /* Method to lookup the caption associated with an item */
+ QString lookupStock( const QString &stock );
+
+ /* Lookup variables in the text */
+ QString lookupText( const QString &t );
+
+ QString cText;
+};
+
+#endif
diff --git a/kdm/kfrontend/themer/kdmlayout.cpp b/kdm/kfrontend/themer/kdmlayout.cpp
new file mode 100644
index 000000000..ed93be264
--- /dev/null
+++ b/kdm/kfrontend/themer/kdmlayout.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2003 by Unai Garro <ugarro@users.sourceforge.net>
+ * Copyright (C) 2004 by Enrico Ros <rosenric@dei.unipd.it>
+ * Copyright (C) 2004 by Stephan Kulow <coolo@kde.org>
+ * Copyright (C) 2004 by Oswald Buddenhagen <ossi@kde.org>
+ *
+ * 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.
+ */
+
+#include "kdmlayout.h"
+#include "kdmitem.h"
+
+#include <kdebug.h>
+
+#include <qdom.h>
+#include <qrect.h>
+
+KdmLayoutFixed::KdmLayoutFixed( const QDomNode &/*node*/ )
+{
+ //Parsing FIXED parameters on 'node' [NONE!]
+}
+
+void
+KdmLayoutFixed::update( const QRect &parentGeometry, bool force )
+{
+ kdDebug() << "KdmLayoutFixed::update " << parentGeometry << endl;
+
+ // I can't layout children if the parent rectangle is not valid
+ if (parentGeometry.width() < 0 || parentGeometry.height() < 0) {
+ kdDebug() << "invalid\n";
+ return;
+ }
+ // For each child in list I ask their hinted size and set it!
+ for (QValueList<KdmItem *>::ConstIterator it = m_children.begin(); it != m_children.end(); ++it)
+ (*it)->setGeometry( (*it)->placementHint( parentGeometry ), force );
+}
+
+KdmLayoutBox::KdmLayoutBox( const QDomNode &node )
+{
+ //Parsing BOX parameters
+ QDomNode n = node;
+ QDomElement el = n.toElement();
+ box.isVertical = el.attribute( "orientation", "vertical" ) != "horizontal";
+ box.xpadding = el.attribute( "xpadding", "0" ).toInt();
+ box.ypadding = el.attribute( "ypadding", "0" ).toInt();
+ box.spacing = el.attribute( "spacing", "0" ).toInt();
+ box.minwidth = el.attribute( "min-width", "0" ).toInt();
+ box.minheight = el.attribute( "min-height", "0" ).toInt();
+ box.homogeneous = el.attribute( "homogeneous", "false" ) == "true";
+}
+
+void
+KdmLayoutBox::update( const QRect &parentGeometry, bool force )
+{
+ kdDebug() << this << " update " << parentGeometry << endl;
+
+ // I can't layout children if the parent rectangle is not valid
+ if (!parentGeometry.isValid() || parentGeometry.isEmpty())
+ return;
+
+ // Check if box size was computed. If not compute it
+ // TODO check if this prevents updating changing items
+// if (!hintedSize.isValid())
+// sizeHint();
+
+// kdDebug() << this << " hintedSize " << hintedSize << endl;
+
+ //XXX why was this asymmetric? it broke things big time.
+ QRect childrenRect = /*box.isVertical ? QRect( parentGeometry.topLeft(), hintedSize ) :*/ parentGeometry;
+ // Begin cutting the parent rectangle to attach children on the right place
+ childrenRect.addCoords( box.xpadding, box.ypadding, -box.xpadding, -box.ypadding );
+
+ kdDebug() << this << " childrenRect " << childrenRect << endl;
+
+ // For each child in list ...
+ if (box.homogeneous) {
+ int ccnt = 0;
+ for (QValueList<KdmItem *>::ConstIterator it = m_children.begin(); it != m_children.end(); ++it)
+ if (!(*it)->isExplicitlyHidden())
+ ccnt++;
+ int height = (childrenRect.height() - (ccnt - 1) * box.spacing) / ccnt;
+ int width = (childrenRect.width() - (ccnt - 1) * box.spacing) / ccnt;
+
+ for (QValueList<KdmItem *>::ConstIterator it = m_children.begin(); it != m_children.end(); ++it) {
+ if ((*it)->isExplicitlyHidden())
+ continue;
+ if (box.isVertical) {
+ QRect temp( childrenRect.left(), childrenRect.top(), childrenRect.width(), height );
+ (*it)->setGeometry( temp, force );
+ childrenRect.setTop( childrenRect.top() + height + box.spacing );
+ } else {
+ QRect temp( childrenRect.left(), childrenRect.top(), width, childrenRect.height() );
+ kdDebug() << "placement " << *it << " " << temp << " " << (*it)->placementHint( temp ) << endl;
+ temp = (*it)->placementHint( temp );
+ (*it)->setGeometry( temp, force );
+ childrenRect.setLeft( childrenRect.left() + width + box.spacing );
+ }
+ }
+ } else {
+ for (QValueList<KdmItem *>::ConstIterator it = m_children.begin(); it != m_children.end(); ++it) {
+ if ((*it)->isExplicitlyHidden())
+ continue;
+
+ QRect temp = childrenRect, itemRect;
+ if (box.isVertical) {
+ temp.setHeight( 0 );
+ itemRect = (*it)->placementHint( temp );
+ temp.setHeight( itemRect.height() );
+ childrenRect.setTop( childrenRect.top() + itemRect.size().height() + box.spacing );
+ } else {
+ temp.setWidth( 0 );
+ itemRect = (*it)->placementHint( temp );
+ kdDebug() << this << " placementHint " << *it << " " << temp << " " << itemRect << endl;
+ temp.setWidth( itemRect.width() );
+ childrenRect.setLeft( childrenRect.left() + itemRect.size().width() + box.spacing );
+ kdDebug() << "childrenRect after " << *it << " " << childrenRect << endl;
+ }
+ itemRect = (*it)->placementHint( temp );
+ kdDebug() << this << " placementHint2 " << *it << " " << temp << " " << itemRect << endl;
+ (*it)->setGeometry( itemRect, force );
+ }
+ }
+}
+
+//FIXME truly experimental (is so close to greeter_geometry.c)
+QSize
+KdmLayoutBox::sizeHint()
+{
+ // Sum up area taken by children
+ int w = 0, h = 0;
+ for (QValueList<KdmItem *>::ConstIterator it = m_children.begin(); it != m_children.end(); ++it) {
+ QSize s = (*it)->placementHint( QRect() ).size();
+ if (box.isVertical) {
+ if (s.width() > w)
+ w = s.width();
+ h += s.height();
+ } else {
+ if (s.height() > h)
+ h = s.height();
+ w += s.width();
+ }
+ }
+
+ // Add padding and items spacing
+ w += 2 * box.xpadding;
+ h += 2 * box.ypadding;
+ if (box.isVertical)
+ h += box.spacing * (m_children.count() - 1);
+ else
+ w += box.spacing * (m_children.count() - 1);
+
+ // Make hint at least equal to minimum size (if set)
+ return QSize( w < box.minwidth ? box.minwidth : w,
+ h < box.minheight ? box.minheight : h );
+}
diff --git a/kdm/kfrontend/themer/kdmlayout.h b/kdm/kfrontend/themer/kdmlayout.h
new file mode 100644
index 000000000..2e00675fb
--- /dev/null
+++ b/kdm/kfrontend/themer/kdmlayout.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2003 by Unai Garro <ugarro@users.sourceforge.net>
+ * Copyright (C) 2004 by Enrico Ros <rosenric@dei.unipd.it>
+ * Copyright (C) 2004 by Stephan Kulow <coolo@kde.org>
+ * Copyright (C) 2004 by Oswald Buddenhagen <ossi@kde.org>
+ *
+ * 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.
+ */
+
+#ifndef KDMLAYOUT_H
+#define KDMLAYOUT_H
+
+/**
+ * this is a container for a lot of other stuff
+ * but can be treated like a usual widget
+ */
+
+#include <qvaluelist.h>
+#include <qsize.h>
+
+class KdmItem;
+
+class QDomNode;
+class QRect;
+
+class KdmLayout {
+
+public:
+// virtual ~KdmLayout() {};
+
+ // Adds an item that will be managed
+ void addItem( KdmItem *item ) { m_children.append( item ); }
+
+ // Return false if any item are managed by this layouter
+ bool isEmpty() { return m_children.isEmpty(); }
+
+ // Updates the layout of all items knowing that the parent
+ // has the @p parentGeometry geometry
+// virtual void update( const QRect &parentGeometry ) = 0;
+
+protected:
+ QValueList<KdmItem *> m_children;
+};
+
+class KdmLayoutFixed : public KdmLayout {
+
+public:
+ KdmLayoutFixed( const QDomNode &node );
+
+ // Updates the layout of all boxed items knowing that the parent
+ // has the @p parentGeometry geometry
+ void update( const QRect &parentGeometry, bool force );
+};
+
+/**
+ * this is a container for a lot of other stuff
+ * but can be treated like a usual widget
+ */
+
+class KdmLayoutBox : public KdmLayout {
+
+public:
+ KdmLayoutBox( const QDomNode &node );
+
+ // Updates the layout of all boxed items knowing that they
+ // should fit into @p parentGeometry container
+ void update( const QRect &parentGeometry, bool force );
+
+ // Computes the size hint of the box, telling which is the
+ // smallest size inside which boxed items will fit
+ QSize sizeHint();
+
+private:
+ struct {
+ bool isVertical;
+ int spacing;
+ int xpadding;
+ int ypadding;
+ int minwidth;
+ int minheight;
+ bool homogeneous;
+ } box;
+// QSize hintedSize;
+};
+
+#endif
diff --git a/kdm/kfrontend/themer/kdmpixmap.cpp b/kdm/kfrontend/themer/kdmpixmap.cpp
new file mode 100644
index 000000000..337c19ced
--- /dev/null
+++ b/kdm/kfrontend/themer/kdmpixmap.cpp
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2003 by Unai Garro <ugarro@users.sourceforge.net>
+ * Copyright (C) 2004 by Enrico Ros <rosenric@dei.unipd.it>
+ * Copyright (C) 2004 by Stephan Kulow <coolo@kde.org>
+ * Copyright (C) 2004 by Oswald Buddenhagen <ossi@kde.org>
+ *
+ * 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.
+ */
+
+#include <config.h>
+
+#include "kdmpixmap.h"
+
+#include <kimageeffect.h>
+#ifdef HAVE_LIBART
+#include <ksvgiconengine.h>
+#endif
+
+#include <kdebug.h>
+
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qimage.h>
+
+KdmPixmap::KdmPixmap( KdmItem *parent, const QDomNode &node, const char *name )
+ : KdmItem( parent, node, name )
+{
+ itemType = "pixmap";
+
+ // Set default values for pixmap (note: strings are already Null)
+ pixmap.normal.tint.setRgb( 0xFFFFFF );
+ pixmap.normal.alpha = 1.0;
+ pixmap.active.present = false;
+ pixmap.prelight.present = false;
+
+ // Read PIXMAP ID
+ // it rarely happens that a pixmap can be a button too!
+ QDomNode n = node;
+ QDomElement elPix = n.toElement();
+
+ // Read PIXMAP TAGS
+ QDomNodeList childList = node.childNodes();
+ for (uint nod = 0; nod < childList.count(); nod++) {
+ QDomNode child = childList.item( nod );
+ QDomElement el = child.toElement();
+ QString tagName = el.tagName();
+
+ if (tagName == "normal") {
+ loadPixmap( el.attribute( "file", "" ), pixmap.normal.pixmap, pixmap.normal.fullpath );
+ parseColor( el.attribute( "tint", "#ffffff" ), pixmap.normal.tint );
+ pixmap.normal.alpha = el.attribute( "alpha", "1.0" ).toFloat();
+ } else if (tagName == "active") {
+ pixmap.active.present = true;
+ loadPixmap( el.attribute( "file", "" ), pixmap.active.pixmap, pixmap.active.fullpath );
+ parseColor( el.attribute( "tint", "#ffffff" ), pixmap.active.tint );
+ pixmap.active.alpha = el.attribute( "alpha", "1.0" ).toFloat();
+ } else if (tagName == "prelight") {
+ pixmap.prelight.present = true;
+ loadPixmap( el.attribute( "file", "" ), pixmap.prelight.pixmap, pixmap.prelight.fullpath );
+ parseColor( el.attribute( "tint", "#ffffff" ), pixmap.prelight.tint );
+ pixmap.prelight.alpha = el.attribute( "alpha", "1.0" ).toFloat();
+ }
+ }
+}
+
+QSize
+KdmPixmap::sizeHint()
+{
+ // choose the correct pixmap class
+ PixmapStruct::PixmapClass * pClass = &pixmap.normal;
+ if (state == Sactive && pixmap.active.present)
+ pClass = &pixmap.active;
+ if (state == Sprelight && pixmap.prelight.present)
+ pClass = &pixmap.prelight;
+ // use the pixmap size as the size hint
+ if (!pClass->pixmap.isNull())
+ return pClass->pixmap.size();
+ return KdmItem::sizeHint();
+}
+
+void
+KdmPixmap::setGeometry( const QRect &newGeometry, bool force )
+{
+ KdmItem::setGeometry( newGeometry, force );
+ pixmap.active.readyPixmap.resize( 0, 0 );
+ pixmap.prelight.readyPixmap.resize( 0, 0 );
+ pixmap.normal.readyPixmap.resize( 0, 0 );
+}
+
+
+void
+KdmPixmap::loadPixmap( const QString &fileName, QPixmap &map, QString &fullName )
+{
+ if (fileName.isEmpty())
+ return;
+
+ fullName = fileName;
+ if (fullName.at( 0 ) != '/')
+ fullName = baseDir() + "/" + fileName;
+
+ if (!fullName.endsWith( ".svg" )) // we delay it for svgs
+ if (!map.load( fullName ))
+ fullName = QString::null;
+}
+
+void
+KdmPixmap::renderSvg( PixmapStruct::PixmapClass *pClass, const QRect &area )
+{
+#ifdef HAVE_LIBART
+ // Special stuff for SVG icons
+ KSVGIconEngine *svgEngine = new KSVGIconEngine();
+
+ if (svgEngine->load( area.width(), area.height(), pClass->fullpath )) {
+ QImage *t = svgEngine->image();
+ pClass->pixmap = *t;
+ pClass->readyPixmap.resize( 0, 0 );
+ delete t;
+ } else {
+ kdWarning() << "failed to load " << pClass->fullpath << endl;
+ pClass->fullpath = QString::null;
+ }
+
+ delete svgEngine;
+#else
+ Q_UNUSED(pClass);
+ Q_UNUSED(area);
+#endif
+}
+
+void
+KdmPixmap::drawContents( QPainter *p, const QRect &r )
+{
+ // choose the correct pixmap class
+ PixmapStruct::PixmapClass *pClass = &pixmap.normal;
+ if (state == Sactive && pixmap.active.present)
+ pClass = &pixmap.active;
+ if (state == Sprelight && pixmap.prelight.present)
+ pClass = &pixmap.prelight;
+
+ if (pClass->pixmap.isNull()) {
+ if (pClass->fullpath.isEmpty()) // if neither is set, we're empty
+ return;
+
+ kdDebug() << "renderSVG\n";
+ renderSvg( pClass, area );
+ }
+
+ int px = area.left() + r.left();
+ int py = area.top() + r.top();
+ int sx = r.x();
+ int sy = r.y();
+ int sw = r.width();
+ int sh = r.height();
+ if (px < 0) {
+ px *= -1;
+ sx += px;
+ px = 0;
+ }
+ if (py < 0) {
+ py *= -1;
+ sy += py;
+ py = 0;
+ }
+
+
+ if (pClass->readyPixmap.isNull()) {
+ QImage scaledImage;
+
+ // use the loaded pixmap or a scaled version if needed
+
+ if (area.size() != pClass->pixmap.size()) {
+ if (pClass->fullpath.endsWith( ".svg" )) {
+ kdDebug() << "renderSVG\n";
+ renderSvg( pClass, area );
+ scaledImage = pClass->pixmap.convertToImage();
+ } else {
+ kdDebug() << "convertFromImage\n";
+ QImage tempImage = pClass->pixmap.convertToImage();
+ scaledImage = tempImage.smoothScale( area.width(), area.height() );
+ }
+ } else
+ scaledImage = pClass->pixmap.convertToImage();
+
+ bool haveTint = pClass->tint.rgb() != 0xFFFFFF;
+ bool haveAlpha = pClass->alpha < 1.0;
+
+ if (haveTint || haveAlpha) {
+ // blend image(pix) with the given tint
+
+ scaledImage = scaledImage.convertDepth( 32 );
+ int w = scaledImage.width();
+ int h = scaledImage.height();
+ float tint_red = float( pClass->tint.red() ) / 255;
+ float tint_green = float( pClass->tint.green() ) / 255;
+ float tint_blue = float( pClass->tint.blue() ) / 255;
+ float tint_alpha = pClass->alpha;
+
+ for (int y = 0; y < h; ++y) {
+ QRgb *ls = (QRgb *)scaledImage.scanLine( y );
+ for (int x = 0; x < w; ++x) {
+ QRgb l = ls[x];
+ int r = int( qRed( l ) * tint_red );
+ int g = int( qGreen( l ) * tint_green );
+ int b = int( qBlue( l ) * tint_blue );
+ int a = int( qAlpha( l ) * tint_alpha );
+ ls[x] = qRgba( r, g, b, a );
+ }
+ }
+
+ }
+
+ pClass->readyPixmap.convertFromImage( scaledImage );
+ }
+ // kdDebug() << "Pixmap::drawContents " << pClass->readyPixmap.size() << " " << px << " " << py << " " << sx << " " << sy << " " << sw << " " << sh << endl;
+ p->drawPixmap( px, py, pClass->readyPixmap, sx, sy, sw, sh );
+}
+
+void
+KdmPixmap::statusChanged()
+{
+ KdmItem::statusChanged();
+ if (!pixmap.active.present && !pixmap.prelight.present)
+ return;
+ if ((state == Sprelight && !pixmap.prelight.present) ||
+ (state == Sactive && !pixmap.active.present))
+ return;
+ needUpdate();
+}
+
+#include "kdmpixmap.moc"
diff --git a/kdm/kfrontend/themer/kdmpixmap.h b/kdm/kfrontend/themer/kdmpixmap.h
new file mode 100644
index 000000000..eb621a0a5
--- /dev/null
+++ b/kdm/kfrontend/themer/kdmpixmap.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2003 by Unai Garro <ugarro@users.sourceforge.net>
+ * Copyright (C) 2004 by Enrico Ros <rosenric@dei.unipd.it>
+ * Copyright (C) 2004 by Stephan Kulow <coolo@kde.org>
+ * Copyright (C) 2004 by Oswald Buddenhagen <ossi@kde.org>
+ *
+ * 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.
+ */
+
+#ifndef KDMPIXMAP_H
+#define KDMPIXMAP_H
+
+#include "kdmitem.h"
+
+//#include <qrect.h>
+#include <qpixmap.h>
+
+/*
+ * KdmPixmap. A pixmap element
+ */
+
+class KdmPixmap : public KdmItem {
+ Q_OBJECT
+
+public:
+ KdmPixmap( KdmItem *parent, const QDomNode &node, const char *name = 0 );
+
+protected:
+ // reimplemented; returns the size of loaded pixmap
+ virtual QSize sizeHint();
+
+ // draw the pixmap
+ virtual void drawContents( QPainter *p, const QRect &r );
+
+ // handle switching between normal / active / prelight configurations
+ virtual void statusChanged();
+
+ virtual void setGeometry( const QRect &newGeometry, bool force );
+
+ struct PixmapStruct {
+ struct PixmapClass {
+ QString fullpath;
+ QPixmap pixmap;
+ QPixmap readyPixmap;
+ QColor tint;
+ float alpha; //TODO added: not in greeter.dtd
+ bool present;
+ } normal, active, prelight;
+ } pixmap;
+
+private:
+ // Method to load the pixmap given by the theme
+ void loadPixmap( const QString &fileName, QPixmap &p, QString &path );
+ void renderSvg( PixmapStruct::PixmapClass *pClass, const QRect &area );
+};
+
+#endif
diff --git a/kdm/kfrontend/themer/kdmrect.cpp b/kdm/kfrontend/themer/kdmrect.cpp
new file mode 100644
index 000000000..478c5c8b3
--- /dev/null
+++ b/kdm/kfrontend/themer/kdmrect.cpp
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2003 by Unai Garro <ugarro@users.sourceforge.net>
+ * Copyright (C) 2004 by Enrico Ros <rosenric@dei.unipd.it>
+ * Copyright (C) 2004 by Stephan Kulow <coolo@kde.org>
+ * Copyright (C) 2004 by Oswald Buddenhagen <ossi@kde.org>
+ *
+ * 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.
+ */
+
+#include "kdmrect.h"
+#include "kdmthemer.h"
+
+#include <kimageeffect.h>
+#include <kdebug.h>
+
+#include <qimage.h>
+#include <qpainter.h>
+#include <qwidget.h>
+#include <qlayout.h>
+
+KdmRect::KdmRect( KdmItem *parent, const QDomNode &node, const char *name )
+ : KdmItem( parent, node, name )
+{
+ itemType = "rect";
+
+ // Set default values for rect (note: strings are already Null)
+ rect.normal.alpha = 1;
+ rect.active.present = false;
+ rect.prelight.present = false;
+ rect.hasBorder = false;
+
+ // A rect can have no properties (defaults to parent ones)
+ if (node.isNull())
+ return;
+
+ // Read RECT ID
+ QDomNode n = node;
+ QDomElement elRect = n.toElement();
+
+ // Read RECT TAGS
+ QDomNodeList childList = node.childNodes();
+ for (uint nod = 0; nod < childList.count(); nod++) {
+ QDomNode child = childList.item( nod );
+ QDomElement el = child.toElement();
+ QString tagName = el.tagName();
+
+ if (tagName == "normal") {
+ parseColor( el.attribute( "color", QString::null ), rect.normal.color );
+ rect.normal.alpha = el.attribute( "alpha", "1.0" ).toFloat();
+ parseFont( el.attribute( "font", "Sans 14" ), rect.normal.font );
+ } else if (tagName == "active") {
+ rect.active.present = true;
+ parseColor( el.attribute( "color", QString::null ), rect.active.color );
+ rect.active.alpha = el.attribute( "alpha", "1.0" ).toFloat();
+ parseFont( el.attribute( "font", "Sans 14" ), rect.active.font );
+ } else if (tagName == "prelight") {
+ rect.prelight.present = true;
+ parseColor( el.attribute( "color", QString::null ), rect.prelight.color );
+ rect.prelight.alpha = el.attribute( "alpha", "1.0" ).toFloat();
+ parseFont( el.attribute( "font", "Sans 14" ), rect.prelight.font );
+ } else if (tagName == "border")
+ rect.hasBorder = true;
+ }
+}
+
+void
+KdmRect::drawContents( QPainter *p, const QRect &r )
+{
+ // choose the correct rect class
+ RectStruct::RectClass *rClass = &rect.normal;
+ if (state == Sactive && rect.active.present)
+ rClass = &rect.active;
+ if (state == Sprelight && rect.prelight.present)
+ rClass = &rect.prelight;
+
+ if (rClass->alpha <= 0 || !rClass->color.isValid())
+ return;
+
+ if (rClass->alpha == 1)
+ p->fillRect( area, QBrush( rClass->color ) );
+ else {
+ QRect backRect = r;
+ backRect.moveBy( area.x(), area.y() );
+ QPixmap backPixmap( backRect.size() );
+ bitBlt( &backPixmap, QPoint( 0, 0 ), p->device(), backRect );
+ QImage backImage = backPixmap.convertToImage();
+ KImageEffect::blend( rClass->color, backImage, rClass->alpha );
+ p->drawImage( backRect.x(), backRect.y(), backImage );
+ // area.moveBy(1,1);
+ }
+}
+
+void
+KdmRect::statusChanged()
+{
+ KdmItem::statusChanged();
+ if (!rect.active.present && !rect.prelight.present)
+ return;
+ if ((state == Sprelight && !rect.prelight.present) ||
+ (state == Sactive && !rect.active.present))
+ return;
+ needUpdate();
+}
+
+/*
+void
+KdmRect::setAttribs( QWidget *widget )
+{
+ widget->setFont( rect.normal.font );
+}
+
+void
+KdmRect::recursiveSetAttribs( QLayoutItem *li )
+{
+ QWidget *w;
+ QLayout *l;
+
+ if ((w = li->widget()))
+ setAttribs( w );
+ else if ((l = li->layout())) {
+ QLayoutIterator it = l->iterator();
+ for (QLayoutItem *itm = it.current(); itm; itm = ++it)
+ recursiveSetAttribs( itm );
+ }
+}
+
+void
+KdmRect::setWidget( QWidget *widget )
+{
+ KdmItem::setWidget( widget );
+ setAttribs( widget );
+}
+
+void
+KdmRect::setLayoutItem( QLayoutItem *item )
+{
+ KdmItem::setLayoutItem( item );
+ recursiveSetAttribs( item );
+}
+*/
+
+#include "kdmrect.moc"
diff --git a/kdm/kfrontend/themer/kdmrect.h b/kdm/kfrontend/themer/kdmrect.h
new file mode 100644
index 000000000..2e89a6a52
--- /dev/null
+++ b/kdm/kfrontend/themer/kdmrect.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2003 by Unai Garro <ugarro@users.sourceforge.net>
+ * Copyright (C) 2004 by Enrico Ros <rosenric@dei.unipd.it>
+ * Copyright (C) 2004 by Stephan Kulow <coolo@kde.org>
+ * Copyright (C) 2004 by Oswald Buddenhagen <ossi@kde.org>
+ *
+ * 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.
+ */
+
+#ifndef KDMRECT_H
+#define KDMRECT_H
+
+#include "kdmitem.h"
+
+#include <qcolor.h>
+#include <qfont.h>
+
+/*
+ * KdmRect: A themed rectangular element
+ */
+
+class KdmRect : public KdmItem {
+ Q_OBJECT
+
+public:
+ KdmRect( KdmItem *parent, const QDomNode &node, const char *name = 0 );
+
+protected:
+ // draw the rect
+ virtual void drawContents( QPainter *p, const QRect &r );
+
+ // handle switching between normal / active / prelight configurations
+ virtual void statusChanged();
+
+ struct RectStruct {
+ struct RectClass {
+ float alpha;
+ QColor color;
+ bool present;
+ QFont font;
+ } normal, active, prelight;
+ bool hasBorder;
+ } rect;
+
+// virtual void setWidget( QWidget *widget );
+// virtual void setLayoutItem( QLayoutItem *item );
+
+private:
+ void setAttribs( QWidget *widget );
+ void recursiveSetAttribs( QLayoutItem *item );
+};
+
+#endif
diff --git a/kdm/kfrontend/themer/kdmthemer.cpp b/kdm/kfrontend/themer/kdmthemer.cpp
new file mode 100644
index 000000000..65eebabf4
--- /dev/null
+++ b/kdm/kfrontend/themer/kdmthemer.cpp
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2003 by Unai Garro <ugarro@users.sourceforge.net>
+ * Copyright (C) 2004 by Enrico Ros <rosenric@dei.unipd.it>
+ * Copyright (C) 2004 by Stephan Kulow <coolo@kde.org>
+ * Copyright (C) 2004 by Oswald Buddenhagen <ossi@kde.org>
+ *
+ * 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.
+ */
+
+#include "kdmthemer.h"
+#include "kdmitem.h"
+#include "kdmpixmap.h"
+#include "kdmrect.h"
+#include "kdmlabel.h"
+
+#include <kdmconfig.h>
+#include <kfdialog.h>
+
+#include <kiconloader.h>
+#include <kimageeffect.h>
+#include <klocale.h>
+#include <ksimpleconfig.h>
+#include <kdebug.h>
+
+#include <qfile.h>
+#include <qfileinfo.h>
+//#include <qtimer.h> // animation timer - TODO
+#include <qobjectlist.h>
+#include <qpainter.h>
+#include <qwidget.h>
+#include <qregion.h>
+
+#include <unistd.h>
+
+/*
+ * KdmThemer. The main theming interface
+ */
+KdmThemer::KdmThemer( const QString &_filename, const QString &mode, QWidget *parent )
+ : QObject( parent )
+ , rootItem( 0 )
+ , backBuffer( 0 )
+{
+ // Set the mode we're working in
+ m_currentMode = mode;
+
+ // read the XML file and create DOM tree
+ QString filename = _filename;
+ if (!::access( QFile::encodeName( filename + "/GdmGreeterTheme.desktop" ), R_OK )) {
+ KSimpleConfig cfg( filename + "/GdmGreeterTheme.desktop" );
+ cfg.setGroup( "GdmGreeterTheme" );
+ filename += '/' + cfg.readEntry( "Greeter" );
+ }
+ QFile opmlFile( filename );
+ if (!opmlFile.open( IO_ReadOnly )) {
+ FDialog::box( widget(), errorbox, i18n( "Cannot open theme file %1" ).arg(filename) );
+ return;
+ }
+ if (!domTree.setContent( &opmlFile )) {
+ FDialog::box( widget(), errorbox, i18n( "Cannot parse theme file %1" ).arg(filename) );
+ return;
+ }
+ // Set the root (screen) item
+ rootItem = new KdmRect( 0, QDomNode(), "kdm root" );
+ connect( rootItem, SIGNAL(needUpdate( int, int, int, int )),
+ widget(), SLOT(update( int, int, int, int )) );
+
+ rootItem->setBaseDir( QFileInfo( filename ).dirPath( true ) );
+
+ // generate all the items defined in the theme
+ generateItems( rootItem );
+
+ connect( rootItem, SIGNAL(activated( const QString & )), SIGNAL(activated( const QString & )) );
+
+/* *TODO*
+ // Animation timer
+ QTimer *time = new QTimer( this );
+ time->start( 500 );
+ connect( time, SIGNAL(timeout()), SLOT(update()) )
+*/
+}
+
+KdmThemer::~KdmThemer()
+{
+ delete rootItem;
+ delete backBuffer;
+}
+
+inline QWidget *
+KdmThemer::widget()
+{
+ return static_cast<QWidget *>(parent());
+}
+
+KdmItem *
+KdmThemer::findNode( const QString &item ) const
+{
+ return rootItem->findNode( item );
+}
+
+void
+KdmThemer::updateGeometry( bool force )
+{
+ rootItem->setGeometry( QRect( QPoint(), widget()->size() ), force );
+}
+
+// BEGIN other functions
+
+void
+KdmThemer::widgetEvent( QEvent *e )
+{
+ if (!rootItem)
+ return;
+ switch (e->type()) {
+ case QEvent::MouseMove:
+ {
+ QMouseEvent *me = static_cast<QMouseEvent *>(e);
+ rootItem->mouseEvent( me->x(), me->y() );
+ }
+ break;
+ case QEvent::MouseButtonPress:
+ {
+ QMouseEvent *me = static_cast<QMouseEvent *>(e);
+ rootItem->mouseEvent( me->x(), me->y(), true );
+ }
+ break;
+ case QEvent::MouseButtonRelease:
+ {
+ QMouseEvent *me = static_cast<QMouseEvent *>(e);
+ rootItem->mouseEvent( me->x(), me->y(), false, true );
+ }
+ break;
+ case QEvent::Show:
+ rootItem->show();
+ break;
+ case QEvent::Resize:
+ updateGeometry( false );
+ showStructure( rootItem );
+ break;
+ case QEvent::Paint:
+ {
+ QRect paintRect = static_cast<QPaintEvent *>(e)->rect();
+ kdDebug() << "paint on: " << paintRect << endl;
+
+ if (!backBuffer)
+ backBuffer = new QPixmap( widget()->size() );
+ if (backBuffer->size() != widget()->size())
+ backBuffer->resize( widget()->size() );
+
+ QPainter p;
+ p.begin( backBuffer );
+ rootItem->paint( &p, paintRect );
+ p.end();
+
+ bitBlt( widget(), paintRect.topLeft(), backBuffer, paintRect );
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+void
+KdmThemer::pixmap( const QRect &r, QPixmap *px )
+{
+ bitBlt( px, QPoint( 0, 0 ), widget(), r );
+}
+*/
+
+void
+KdmThemer::generateItems( KdmItem *parent, const QDomNode &node )
+{
+ if (!parent)
+ return;
+
+ QDomNodeList subnodeList; //List of subnodes of this node
+
+ /*
+ * Get the child nodes
+ */
+ if (node.isNull()) { // It's the first node, get its child nodes
+ QDomElement theme = domTree.documentElement();
+
+ // Get its tag, and check it's correct ("greeter")
+ if (theme.tagName() != "greeter") {
+ kdDebug() << "This does not seem to be a correct theme file." << endl;
+ return;
+ }
+ // Get the list of child nodes
+ subnodeList = theme.childNodes();
+ } else
+ subnodeList = node.childNodes();
+
+ /*
+ * Go through each of the child nodes
+ */
+ for (uint nod = 0; nod < subnodeList.count(); nod++) {
+ QDomNode subnode = subnodeList.item( nod );
+ QDomElement el = subnode.toElement();
+ QString tagName = el.tagName();
+
+ if (tagName == "item") {
+ if (!willDisplay( subnode ))
+ continue;
+ // It's a new item. Draw it
+ QString type = el.attribute( "type" );
+
+ KdmItem *newItem = 0;
+
+ if (type == "label")
+ newItem = new KdmLabel( parent, subnode );
+ else if (type == "pixmap")
+ newItem = new KdmPixmap( parent, subnode );
+ else if (type == "rect")
+ newItem = new KdmRect( parent, subnode );
+ else if (type == "entry") {
+ newItem = new KdmRect( parent, subnode );
+ newItem->setType( type );
+ }
+ // newItem = new KdmEntry( parent, subnode );
+ //else if (type=="list")
+ // newItem = new KdmList( parent, subnode );
+ else if (type == "svg")
+ newItem = new KdmPixmap( parent, subnode );
+ if (newItem) {
+ generateItems( newItem, subnode );
+ if (el.attribute( "button", "false" ) == "true")
+ newItem->inheritFromButton( newItem );
+ }
+ } else if (tagName == "box") {
+ if (!willDisplay( subnode ))
+ continue;
+ // It's a new box. Draw it
+ parent->setBoxLayout( subnode );
+ generateItems( parent, subnode );
+ } else if (tagName == "fixed") {
+ if (!willDisplay( subnode ))
+ continue;
+ // It's a new box. Draw it
+ parent->setFixedLayout( subnode );
+ generateItems( parent, subnode );
+ }
+ }
+}
+
+bool KdmThemer::willDisplay( const QDomNode &node )
+{
+ QDomNode showNode = node.namedItem( "show" );
+
+ // No "show" node means this item can be displayed at all times
+ if (showNode.isNull())
+ return true;
+
+ QDomElement el = showNode.toElement();
+
+ QString modes = el.attribute( "modes" );
+ if (!modes.isNull()) {
+ QStringList modeList = QStringList::split( ",", modes );
+
+ // If current mode isn't in this list, do not display item
+ if (modeList.find( m_currentMode ) == modeList.end())
+ return false;
+ }
+
+ QString type = el.attribute( "type" );
+ if (type == "config" || type == "suspend")
+ return false; // not implemented (yet)
+ if (type == "timed")
+ return _autoLoginDelay != 0;
+ if (type == "chooser")
+#ifdef XDMCP
+ return _loginMode != LOGIN_LOCAL_ONLY;
+#else
+ return false;
+#endif
+ if (type == "halt" || type == "reboot")
+ return _allowShutdown != SHUT_NONE;
+// if (type == "system")
+// return true;
+
+ // All tests passed, item will be displayed
+ return true;
+}
+
+void
+KdmThemer::showStructure( QObject *obj )
+{
+
+ const QObjectList *wlist = obj->children();
+ static int counter = 0;
+ if (counter == 0)
+ kdDebug() << "\n\n<======= Widget tree =================" << endl;
+ if (wlist) {
+ counter++;
+ QObjectListIterator it( *wlist );
+ QObject *object;
+
+ while ((object = it.current()) != 0) {
+ ++it;
+ QString node;
+ for (int i = 1; i < counter; i++)
+ node += "-";
+
+ if (object->inherits( "KdmItem" )) {
+ KdmItem *widget = (KdmItem *)object;
+ kdDebug() << node << "|" << widget->type() << " me=" << widget->id << " " << widget->area << endl;
+ }
+
+ showStructure( object );
+ }
+ counter--;
+ }
+ if (counter == 0)
+ kdDebug() << "\n\n<======= Widget tree =================\n\n" << endl;
+}
+
+#include "kdmthemer.moc"
diff --git a/kdm/kfrontend/themer/kdmthemer.h b/kdm/kfrontend/themer/kdmthemer.h
new file mode 100644
index 000000000..34baffbb7
--- /dev/null
+++ b/kdm/kfrontend/themer/kdmthemer.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2003 by Unai Garro <ugarro@users.sourceforge.net>
+ * Copyright (C) 2004 by Enrico Ros <rosenric@dei.unipd.it>
+ * Copyright (C) 2004 by Stephan Kulow <coolo@kde.org>
+ * Copyright (C) 2004 by Oswald Buddenhagen <ossi@kde.org>
+ *
+ * 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.
+ */
+
+#ifndef KDMTHEMER_H
+#define KDMTHEMER_H
+
+#include <qobject.h>
+#include <qdom.h>
+
+class KdmThemer;
+class KdmItem;
+class KdmPixmap;
+class KdmRect;
+class KdmBox;
+
+class QRect;
+class QWidget;
+class QEvent;
+
+/**
+* @author Unai Garro
+*/
+
+
+
+/*
+* The themer widget. Whatever drawn here is just themed
+* according to a XML file set by the user.
+*/
+
+
+class KdmThemer : public QObject {
+ Q_OBJECT
+
+public:
+ /*
+ * Construct and destruct the interface
+ */
+
+ KdmThemer( const QString &path, const QString &mode, QWidget *parent );
+ ~KdmThemer();
+
+ bool isOK() { return rootItem != 0; }
+ /*
+ * Gives a sizeHint to the widget (parent size)
+ */
+ //QSize sizeHint() const{ return parentWidget()->size(); }
+
+ /*
+ * Takes a shot of the current widget
+ */
+// void pixmap( const QRect &r, QPixmap *px );
+
+ virtual // just to put the reference in the vmt
+ KdmItem *findNode( const QString & ) const;
+
+ void updateGeometry( bool force ); // force = true for external calls
+
+ // must be called by parent widget
+ void widgetEvent( QEvent *e );
+
+signals:
+ void activated( const QString &id );
+
+private:
+ /*
+ * Our display mode (e.g. console, remote, ...)
+ */
+ QString m_currentMode;
+
+ /*
+ * The config file being used
+ */
+ QDomDocument domTree;
+
+ /*
+ * Stores the root of the theme
+ */
+ KdmItem *rootItem;
+
+ /*
+ * The backbuffer
+ */
+ QPixmap *backBuffer;
+
+ // methods
+
+ /*
+ * Test whether item needs to be displayed
+ */
+ bool willDisplay( const QDomNode &node );
+
+ /*
+ * Parses the XML file looking for the
+ * item list and adds those to the themer
+ */
+ void generateItems( KdmItem *parent = 0, const QDomNode &node = QDomNode() );
+
+ void showStructure( QObject *obj );
+
+ QWidget *widget();
+};
+
+
+#endif
diff --git a/kdm/kfrontend/themes/Makefile.am b/kdm/kfrontend/themes/Makefile.am
new file mode 100644
index 000000000..bad3ea323
--- /dev/null
+++ b/kdm/kfrontend/themes/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = circles
diff --git a/kdm/kfrontend/themes/circles/GdmGreeterTheme.desktop b/kdm/kfrontend/themes/circles/GdmGreeterTheme.desktop
new file mode 100644
index 000000000..72be02f41
--- /dev/null
+++ b/kdm/kfrontend/themes/circles/GdmGreeterTheme.desktop
@@ -0,0 +1,135 @@
+# This is not really a .desktop file like the rest, but it's useful to treat
+# it as such
+
+[GdmGreeterTheme]
+Greeter=circles.xml
+Name=Circles
+Name[ar]=الدوائر
+Name[bn]=সার্কল্‌স
+Name[br]=Kelc'hioù
+Name[bs]=Krugovi
+Name[ca]=Cercles
+Name[cs]=Kruhy
+Name[csb]=Kółka
+Name[cy]=Cylchoedd
+Name[da]=Cirkler
+Name[de]=Kreise
+Name[el]=Κύκλοι
+Name[eo]=cirkloj
+Name[es]=Círculos
+Name[et]=Ringid
+Name[fa]=دایره‌ها
+Name[fi]=Ympyrät
+Name[fy]=Cirkels
+Name[ga]=Ciorcail
+Name[he]=מעגלים
+Name[hi]=वृत्त
+Name[hr]=Krugovi
+Name[hu]=Körök
+Name[is]=Hringir
+Name[it]=Cerchi
+Name[ja]=サークル
+Name[ka]=წრეები
+Name[kk]=Дөңгелектер
+Name[km]=រង្វង់
+Name[ko]=원
+Name[lt]=Skrituliai
+Name[mk]=Кругови
+Name[ms]=Bulatan
+Name[nb]=Sirkler
+Name[nds]=Krinken
+Name[ne]=वृत्त
+Name[nl]=Cirkels
+Name[nn]=Sirklar
+Name[pa]=ਚੱਕਰ
+Name[pl]=Kółka
+Name[pt]=Círculos
+Name[pt_BR]=Círculos
+Name[ro]=Cercuri
+Name[ru]=Круги
+Name[rw]=Inziga
+Name[se]=Gearddut
+Name[sk]=Kruhy
+Name[sl]=Krogi
+Name[sr]=Кругови
+Name[sr@Latn]=Krugovi
+Name[sv]=Cirklar
+Name[ta]=வட்டங்கள்
+Name[te]=వృత్తాలు
+Name[tg]=Доираҳо
+Name[tr]=Çemberler
+Name[uk]=Кола
+Name[uz]=Aylanalar
+Name[uz@cyrillic]=Айланалар
+Name[vi]=Vòng tròn
+Name[wa]=Cekes
+Name[zh_CN]=圆环
+Description=Theme with blue circles
+Description[af]=Tema met blou sirkels
+Description[ar]=سمة بلدوائر الزرقاء
+Description[be]=Тэма з блакітнымі коламі
+Description[bn]=নীল বৃত্ত সম্বলিত থীম
+Description[bs]=Tema sa plavim krugovima
+Description[ca]=Tema amb cercles blaus
+Description[cs]=Motiv s modrými kruhy
+Description[csb]=Téma z mòdrima kółkama
+Description[da]=Tema med blå cirkler
+Description[de]=Thema mit blauen Kreisen
+Description[el]=Θέμα με μπλε κύκλους
+Description[eo]=Etoso kun bluaj cirkloj
+Description[es]=Tema con círculos azules
+Description[et]=Siniste ringidega teema
+Description[eu]=Biribil urdindun gaia
+Description[fa]=چهره با دایره‌های آبی
+Description[fi]=Teema sinisillä ympyröillä
+Description[fr]=Thème avec des cercles bleus
+Description[fy]=Tema mei blauwe sirkels
+Description[gl]=Tema con círculos azuis
+Description[he]=ערכת נושא עם מעגלים כחולים
+Description[hi]=नीले वृत्तों के साथ प्रसंग
+Description[hr]=Tema s plavim krugovima
+Description[hu]=Téma kék körökkel
+Description[is]=Þema með bláum hringjum
+Description[it]=Tema con cerchi blu
+Description[ja]=青い丸のテーマ
+Description[ka]=ლურჯ წრეებიანი თემა
+Description[kk]=Көк дөңгелекті нақыш
+Description[km]=ស្បែក​មាន​រង្វង់​ពណ៌​ខៀវ
+Description[ko]=파란 원이 있는 테마
+Description[lt]=Tema su melsvais skrituliais
+Description[lv]=Tēma ar ziliem riņķiem
+Description[mk]=Тема со сини кругови
+Description[ms]=Tema dengan bulatan biru
+Description[nb]=Tema med blå sirkler
+Description[nds]=Muster mit blage Krinken
+Description[ne]=निलो वृत्तसँग विषयवस्तु
+Description[nl]=Thema met blauwe cirkels
+Description[nn]=Tema med blåe sirklar
+Description[pa]=ਨੀਲੇ ਚੱਕਰਾਂ ਵਾਲਾ ਸਰੂਪ
+Description[pl]=Motyw z niebieskimi kółkami
+Description[pt]=Tema com círculos azuis
+Description[pt_BR]=Tema com círculos azuis
+Description[ro]=Temă cu cercuri albastre
+Description[ru]=Тема с синими кругами
+Description[rw]=Insanganyamatsiko ifite inziga z'ubururu
+Description[se]=Fáddá mas lea alit gearddut
+Description[sk]=Téma s modrými kruhmi
+Description[sl]=Tema z modrimi krogi
+Description[sr]=Тема са плавим круговима
+Description[sr@Latn]=Tema sa plavim krugovima
+Description[sv]=Tema med blåa cirklar
+Description[ta]=நீல வட்டங்களுடன் தலைப்பு
+Description[tg]=Мавзӯъ бо доираҳои кабуд
+Description[th]=ชุดตกแต่งมาพร้อมวงกลมสีน้ำเงิน
+Description[tr]=Mavi çemberli tema
+Description[tt]=Zäñgär tügäräklär belän tışlaw
+Description[uk]=Тема з синіми колами
+Description[uz]=Koʻk aylanalarli mavzu
+Description[uz@cyrillic]=Кўк айланаларли мавзу
+Description[vi]=Sắc thái với các vòng tròn xanh lam
+Description[wa]=Tinme avou des bleus cekes
+Description[zh_CN]=带蓝环的主题
+Description[zh_TW]=有藍色圓圈的佈景主題
+Author=Bond, James Bond
+Copyright=(c) 2002 Bond, James Bond
+Screenshot=screenshot.png
diff --git a/kdm/kfrontend/themes/circles/Makefile.am b/kdm/kfrontend/themes/circles/Makefile.am
new file mode 100644
index 000000000..97ad04b6a
--- /dev/null
+++ b/kdm/kfrontend/themes/circles/Makefile.am
@@ -0,0 +1,11 @@
+circlesdir = $(kde_datadir)/kdm/themes/circles
+circles_DATA = \
+ GdmGreeterTheme.desktop \
+ circles.xml \
+ background.svg \
+ flower.png \
+ help.png \
+ options.png \
+ screenshot.png
+
+EXTRA_DIST = $(circles_DATA)
diff --git a/kdm/kfrontend/themes/circles/background.svg b/kdm/kfrontend/themes/circles/background.svg
new file mode 100644
index 000000000..11abc4f43
--- /dev/null
+++ b/kdm/kfrontend/themes/circles/background.svg
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!-- Generator: Adobe Illustrator 9.0, SVG Export Plug-In -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20000303 Stylable//EN" "http://www.w3.org/TR/2000/03/WD-SVG-20000303/DTD/svg-20000303-stylable.dtd" [
+ <!ENTITY st0 "fill:url(#aigrd1);stroke:none;">
+ <!ENTITY st1 "fill:url(#aigrd3);stroke:none;">
+ <!ENTITY st2 "fill:url(#aigrd2);stroke:none;">
+ <!ENTITY st3 "fill-rule:nonzero;clip-rule:nonzero;fill:#2A569D;stroke:#000000;stroke-miterlimit:4;">
+ <!ENTITY st4 "stroke:none;">
+]>
+<svg width="508.104pt" height="383.717pt" viewBox="0 0 508.104 383.717" xml:space="preserve">
+ <g id="Layer_x0020_1" style="&st3;">
+ <g id="middle">
+ <linearGradient id="aigrd1" gradientUnits="userSpaceOnUse" x1="237.2617" y1="24.7095" x2="237.2617" y2="371.207">
+ <stop offset="0" style="stop-color:#4E77B9"/>
+ <stop offset="1" style="stop-color:#6E8CBE"/>
+ </linearGradient>
+ <path style="&st0;" d="M59.227,357.212c27.481,9.066,56.985,13.994,87.696,13.994c148.221,0,268.375-114.698,268.375-256.189c0-31.789-6.073-62.221-17.16-90.308C249.869,104.206,131.168,219.909,59.227,357.212z"/>
+ </g>
+ <linearGradient id="aigrd2" gradientUnits="userSpaceOnUse" x1="277.0767" y1="0" x2="277.0766" y2="383.7178">
+ <stop offset="0" style="stop-color:#335EA2"/>
+ <stop offset="1" style="stop-color:#6D8BBD"/>
+ </linearGradient>
+ <path id="lower_x0020_right" style="&st2;" d="M447.704,0c-16.859,7.792-33.389,16.037-49.566,24.709c11.087,28.087,17.16,58.52,17.16,90.308c0,141.491-120.154,256.189-268.375,256.189c-30.71,0-60.214-4.928-87.696-13.994
+ c-4.584,8.749-8.979,17.585-13.178,26.505h462.056V0h-60.4z"/>
+ <g id="upper_x0020_left">
+ <linearGradient id="aigrd3" gradientUnits="userSpaceOnUse" x1="199.0684" y1="0" x2="199.0683" y2="357.2129">
+ <stop offset="0" style="stop-color:#436FB6"/>
+ <stop offset="1" style="stop-color:#305CA3"/>
+ </linearGradient>
+ <path style="&st1;" d="M398.137,24.709C394.798,16.252,391.011,8.005,386.791,0H0v329.44c18.309,11.454,38.174,20.827,59.227,27.772c71.942-137.304,190.642-253.007,338.911-332.503z"/>
+ </g>
+ <g id="bottom_x0020_small">
+ <path style="&st4;" d="M0,329.44v54.277h46.048c4.2-8.92,8.594-17.756,13.178-26.505C38.174,350.267,18.309,340.894,0,329.44z"/>
+ </g>
+ <g id="top_x0020_small">
+ <path style="&st4;" d="M447.704,0h-60.913c4.221,8.005,8.008,16.252,11.347,24.709C414.315,16.037,430.844,7.792,447.704,0z"/>
+ </g>
+ </g>
+</svg>
diff --git a/kdm/kfrontend/themes/circles/circles.xml b/kdm/kfrontend/themes/circles/circles.xml
new file mode 100644
index 000000000..0596e0ee7
--- /dev/null
+++ b/kdm/kfrontend/themes/circles/circles.xml
@@ -0,0 +1,207 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE greeter SYSTEM "greeter.dtd">
+<greeter>
+ <item type="svg">
+ <normal file="background.svg"/>
+ <pos x="0" y="0" width="100%" height="-75"/>
+ </item>
+ <item type="rect">
+ <normal color="#000000"/>
+ <pos x="0" y="-75" width="100%" height="75"/>
+ <fixed>
+ <item type="rect">
+ <normal color="#ffffff"/>
+ <pos x="0" y="4" width="100%" height="100%"/>
+ <box orientation="horizontal" spacing="10" xpadding="10">
+ <item type="rect" id="language_button" button="true">
+ <normal color="#ffffff"/>
+ <pos y="50%" anchor="w" width="box" height="box"/>
+ <box orientation="horizontal" spacing="10" xpadding="10">
+ <item type="pixmap">
+ <normal file="options.png" tint="#dddddd"/>
+ <prelight file="options.png"/>
+ <active file="options.png" tint="#ff0000"/>
+ <pos y="50%" anchor="w"/>
+ </item>
+ <item type="label">
+ <normal color="#000000" font="Sans 12"/>
+ <prelight color="#666666" font="Sans 12"/>
+ <active color="#ff0000" font="Sans 12"/>
+ <pos y="50%" anchor="w"/>
+ <!-- Stock label for: _Language -->
+ <stock type="language"/>
+ </item>
+ </box>
+ </item>
+ <item type="rect" id="session_button" button="true">
+ <normal color="#ffffff"/>
+ <pos y="50%" anchor="w" width="box" height="box"/>
+ <box orientation="horizontal" spacing="10" xpadding="10">
+ <item type="pixmap">
+ <normal file="help.png" tint="#dddddd"/>
+ <prelight file="help.png"/>
+ <active file="help.png" tint="#ff0000"/>
+ <pos y="50%" anchor="w"/>
+ </item>
+ <item type="label">
+ <normal color="#000000" font="Sans 12"/>
+ <prelight color="#666666" font="Sans 12"/>
+ <active color="#ff0000" font="Sans 12"/>
+ <pos y="50%" anchor="w"/>
+ <!-- Stock label for: _Session -->
+ <stock type="session"/>
+ </item>
+ </box>
+ </item>
+ <item type="rect" id="system_button" button="true">
+ <normal color="#ffffff"/>
+ <show modes="console" type="system"/>
+ <pos y="50%" anchor="w" width="box" height="box"/>
+ <box orientation="horizontal" spacing="10" xpadding="10">
+ <item type="pixmap">
+ <normal file="options.png" tint="#dddddd"/>
+ <prelight file="options.png"/>
+ <active file="options.png" tint="#ff0000"/>
+ <pos y="50%" anchor="w"/>
+ </item>
+ <item type="label">
+ <normal color="#000000" font="Sans 12"/>
+ <prelight color="#666666" font="Sans 12"/>
+ <active color="#ff0000" font="Sans 12"/>
+ <pos y="50%" anchor="w"/>
+ <!-- Stock label for: _Actions -->
+ <stock type="system"/>
+ </item>
+ </box>
+ </item>
+ <item type="rect" id="disconnect_button" button="true">
+ <normal color="#ffffff"/>
+ <show modes="flexi,remote"/>
+ <pos y="50%" anchor="w" width="box" height="box"/>
+ <box orientation="horizontal" spacing="10" xpadding="10">
+ <item type="pixmap">
+ <normal file="options.png" tint="#dddddd"/>
+ <prelight file="options.png"/>
+ <active file="options.png" tint="#ff0000"/>
+ <pos y="50%" anchor="w"/>
+ </item>
+ <item type="label">
+ <normal color="#000000" font="Sans 12"/>
+ <prelight color="#666666" font="Sans 12"/>
+ <active color="#ff0000" font="Sans 12"/>
+ <pos y="50%" anchor="w"/>
+ <!-- Stock label for: D_isconnect -->
+ <stock type="disconnect"/>
+ <show modes="remote"/>
+ </item>
+ <item type="label">
+ <normal color="#000000" font="Sans 12"/>
+ <prelight color="#666666" font="Sans 12"/>
+ <active color="#ff0000" font="Sans 12"/>
+ <pos y="50%" anchor="w"/>
+ <!-- Stock label for: _Quit -->
+ <stock type="quit"/>
+ <show modes="flexi"/>
+ </item>
+ </box>
+ </item>
+ </box>
+ </item>
+ </fixed>
+ </item>
+ <item type="pixmap">
+ <normal file="flower.png"/>
+ <pos x="100%" y="100%" anchor="se"/>
+ </item>
+ <item type="label" id="clock">
+ <normal color="#000000" font="Sans 12"/>
+ <pos x="-20" y="-37" anchor="e"/>
+ <text>%c</text>
+ </item>
+
+ <item type="rect" id="caps-lock-warning">
+ <normal color="#FFFFFF" alpha="0.5"/>
+ <pos anchor="c" x="50%" y="75%" width="box" height="box"/>
+ <box orientation="vertical" min-width="400" xpadding="10" ypadding="5" spacing="0">
+ <item type="label">
+ <normal color="#000000" font="Sans 12"/>
+ <pos x="50%" anchor="n"/>
+ <!-- Stock label for: You've got capslock on! -->
+ <stock type="caps-lock-warning"/>
+ </item>
+ </box>
+ </item>
+
+ <item type="rect">
+ <show type="timed"/>
+ <normal color="#FFFFFF" alpha="0.5"/>
+ <pos anchor="c" x="50%" y="25%" width="box" height="box"/>
+ <box orientation="vertical" min-width="400" xpadding="10" ypadding="5" spacing="0">
+ <item type="label" id="timed-label">
+ <normal color="#000000" font="Sans 12"/>
+ <pos x="50%" anchor="n"/>
+ <!-- Stock label for: User %s will login in %d seconds -->
+ <stock type="timed-label"/>
+ </item>
+ </box>
+ </item>
+
+ <item type="rect">
+ <normal color="#FFFFFF" alpha="0.5"/>
+ <pos anchor="c" x="50%" y="50%" width="box" height="box"/>
+ <box orientation="vertical" min-width="340" xpadding="30" ypadding="30" spacing="10">
+ <item type="label">
+ <pos anchor="n" x="50%"/>
+ <normal color="#000000" font="Sans 14"/>
+ <!-- Stock label for: Welcome to %h -->
+ <stock type="welcome-label"/>
+ </item>
+ <item type="rect" id="talker">
+ <normal color="#0" alpha="0"/>
+ <pos anchor="n" x="50%" width="box" height="box"/>
+ <!-- box orientation="vertical" xpadding="0" ypadding="0" spacing="10" -->
+ <box orientation="horizontal" xpadding="0" ypadding="0" spacing="0">
+ <item type="rect">
+ <normal color="#FF8080" alpha="0.0"/>
+ <pos anchor="w" y="50%" width="box" height="box"/>
+ <box orientation="vertical" xpadding="0" ypadding="0" spacing="14">
+ <item type="label">
+ <pos anchor="ne" x="100%"/>
+ <normal color="#000000" font="Sans 12"/>
+ <!-- Stock label for: Username: -->
+ <stock type="username-label"/>
+ </item>
+ <item type="label">
+ <pos anchor="ne" x="100%"/>
+ <normal color="#000000" font="Sans 12"/>
+ <!-- Stock label for: Password: -->
+ <stock type="password-label"/>
+ </item>
+ </box>
+ </item>
+ <item type="rect">
+ <normal color="#FF80FF" alpha="0.0"/>
+ <pos anchor="w" y="50%" width="box" height="box"/>
+ <box orientation="vertical" xpadding="0" ypadding="0" spacing="10">
+ <item type="entry" id="user-entry">
+ <pos anchor="n" x="50%" height="24" width="150"/>
+ </item>
+ <item type="entry" id="pw-entry">
+ <pos anchor="n" x="50%" height="24" width="150"/>
+ </item>
+ </box>
+ </item>
+ </box>
+ </item>
+ </box>
+ <fixed>
+ <item type="label" id="pam-error">
+ <pos anchor="n" x="50%" y="110%"/>
+ <normal color="#000000" font="Sans 12"/>
+ <text></text>
+ </item>
+ </fixed>
+ </item>
+</greeter>
+
+
diff --git a/kdm/kfrontend/themes/circles/flower.png b/kdm/kfrontend/themes/circles/flower.png
new file mode 100644
index 000000000..92d25f32d
--- /dev/null
+++ b/kdm/kfrontend/themes/circles/flower.png
Binary files differ
diff --git a/kdm/kfrontend/themes/circles/help.png b/kdm/kfrontend/themes/circles/help.png
new file mode 100644
index 000000000..b38b48a6d
--- /dev/null
+++ b/kdm/kfrontend/themes/circles/help.png
Binary files differ
diff --git a/kdm/kfrontend/themes/circles/options.png b/kdm/kfrontend/themes/circles/options.png
new file mode 100644
index 000000000..3c08e02d4
--- /dev/null
+++ b/kdm/kfrontend/themes/circles/options.png
Binary files differ
diff --git a/kdm/kfrontend/themes/circles/screenshot.png b/kdm/kfrontend/themes/circles/screenshot.png
new file mode 100644
index 000000000..7120b03d5
--- /dev/null
+++ b/kdm/kfrontend/themes/circles/screenshot.png
Binary files differ