Browse Source

Merged branches/slim-pam back into trunk.

git-svn-id: svn+ssh://svn.berlios.de/svnroot/repos/slim/trunk@141 7c53e7cc-98ea-0310-8f1f-a0b24da60408
parmus 14 years ago
parent
commit
488266cb19
24 changed files with 1174 additions and 951 deletions
  1. 2 2
      ChangeLog
  2. 15 13
      Makefile
  3. 12 11
      Makefile.freebsd
  4. 12 11
      Makefile.netbsd
  5. 4 16
      Makefile.openbsd
  6. 275 0
      PAM.cpp
  7. 99 0
      PAM.h
  8. 374 100
      app.cpp
  9. 11 1
      app.h
  10. 48 6
      cfg.cpp
  11. 4 1
      cfg.h
  12. 7 16
      const.h
  13. 9 9
      image.cpp
  14. 0 249
      input.cpp
  15. 0 63
      input.h
  16. 2 2
      jpeg.c
  17. 7 7
      numlock.cpp
  18. 215 150
      panel.cpp
  19. 41 16
      panel.h
  20. 27 27
      png.c
  21. 0 47
      ssetbg.1
  22. 0 144
      ssetbg.cpp
  23. 6 56
      switchuser.cpp
  24. 4 4
      switchuser.h

+ 2 - 2
ChangeLog

@@ -1,10 +1,10 @@
-1.2.7 - XXXX.XX.XX
+1.3.0 - XXXX.XX.XX
+    * Added PAM support by Martin Parm
     * Fixed segfault on exit when testing themes. Thanks
       to Darren Salt & Mike Massonnet
     * Fixed vt argument detection, thanks to Henrik Brix Andersen
     * Corrected reference to input_color in the default theme
     * Fixed default shell setting
-    * Added ssetbg, a simple tool for setting the backround image.
 
 1.2.6 - 2006.09.15
     * Bug #008167: Update pid when in daemon mode

+ 15 - 13
Makefile

@@ -5,9 +5,14 @@
 #######################################################
 CXX=/usr/bin/g++
 CC=/usr/bin/gcc
-CFLAGS=-I. -I/usr/X11R6/include -I/usr/include/freetype2 -I/usr/include/freetype2/config -I/usr/include/libpng12 -I/usr/include
+CFLAGS=-Wall -I. -I/usr/X11R6/include -I/usr/include/freetype2 -I/usr/include/freetype2/config -I/usr/include/libpng12 -I/usr/include
+CPPFLAGS=-Wold-style-cast $(CFLAGS)
 LDFLAGS=-L/usr/X11R6/lib -lXft -lX11 -lfreetype -lXrender -lfontconfig -lpng12 -lz -lm -lcrypt -lXmu -lpng -ljpeg
 CUSTOM=-DHAVE_SHADOW
+ifdef USE_PAM
+LDFLAGS+= -lpam
+CUSTOM+= -DUSE_PAM
+endif
 PREFIX=/usr
 CFGDIR=/etc
 MANDIR=/usr/man
@@ -20,39 +25,36 @@ VERSION=1.2.6
 DEFINES=-DPACKAGE=\"$(NAME)\" -DVERSION=\"$(VERSION)\" \
 		-DPKGDATADIR=\"$(PREFIX)/share/slim\" -DSYSCONFDIR=\"$(CFGDIR)\"
 
-OBJECTS=jpeg.o png.o main.o image.o numlock.o cfg.o switchuser.o input.o app.o panel.o
-BGOBJECTS=jpeg.o png.o image.o ssetbg.o
+OBJECTS=jpeg.o png.o main.o image.o numlock.o cfg.o switchuser.o app.o panel.o
+ifdef USE_PAM
+OBJECTS+=PAM.o
+endif
 
-all: slim ssetbg
+all: slim
 
 slim: $(OBJECTS)
 	$(CXX) $(LDFLAGS) $(OBJECTS) -o $(NAME)
 
-ssetbg: $(BGOBJECTS)
-	$(CXX) $(LDFLAGS) $(BGOBJECTS) -o ssetbg
-
 .cpp.o:
-	$(CXX) $(CFLAGS) $(DEFINES) $(CUSTOM) -c $< -o $@
+	$(CXX) $(CPPFLAGS) $(DEFINES) $(CUSTOM) -c $< -o $@
 
 .c.o:
 	$(CC) $(CFLAGS) $(DEFINES) $(CUSTOM) -c $< -o $@
 
-install: slim ssetbg install-theme
+install: slim install-theme
 	install -D -m 755 slim $(DESTDIR)$(PREFIX)/bin/slim
-	install -D -m 755 ssetbg $(DESTDIR)$(PREFIX)/bin/ssetbg
 	install -D -m 644 slim.1 $(DESTDIR)$(MANDIR)/man1/slim.1
-	install -D -m 644 ssetbg.1 $(DESTDIR)$(MANDIR)/man1/ssetbg.1
 	test -e $(DESTDIR)$(CFGDIR)/slim.conf || \
 		install -D -m 644 slim.conf $(DESTDIR)$(CFGDIR)/slim.conf
 
 clean:
-	@rm -f slim ssetbg *.o
+	@rm -f slim *.o
 
 dist:
 	@rm -rf $(NAME)-$(VERSION)
 	@mkdir $(NAME)-$(VERSION)
 	@cp -r *.cpp *.h *.c Makefile Makefile.* COPYING ChangeLog INSTALL README TODO \
-		xinitrc.sample slim.1 ssetbg.1 THEMES themes slim.conf $(NAME)-$(VERSION)
+		xinitrc.sample slim.1 THEMES themes slim.conf $(NAME)-$(VERSION)
 	@rm -rf $(NAME)-$(VERSION)/themes/.svn	$(NAME)-$(VERSION)/themes/default/.svn
 	@tar cvzf $(NAME)-$(VERSION).tar.gz $(NAME)-$(VERSION)
 	@rm -rf $(NAME)-$(VERSION)

+ 12 - 11
Makefile.freebsd

@@ -8,6 +8,10 @@ CC=/usr/bin/gcc
 CFLAGS=-I. -I/usr/X11R6/include -I/usr/local/include/freetype2 -I/usr/local/include/freetype2/config -I/usr/local/include/libpng -I/usr/local/include -I/usr/include
 LDFLAGS=-L/usr/X11R6/lib -L/usr/local/lib -lXft -lX11 -lfreetype -lXrender -lfontconfig -lpng -lz -lm -lcrypt -lXmu -lpng -ljpeg
 CUSTOM=-DNEEDS_BASENAME
+.ifdef(USE_PAM)
+  LDFLAGS+= -lpam
+  CUSTOM+= -DUSE_PAM
+.endif
 PREFIX=/usr
 CFGDIR=/etc
 MANDIR=/usr/share/man
@@ -20,42 +24,39 @@ VERSION=1.2.6
 DEFINES=-DPACKAGE=\"$(NAME)\" -DVERSION=\"$(VERSION)\" \
 		-DPKGDATADIR=\"$(PREFIX)/share/slim\" -DSYSCONFDIR=\"$(CFGDIR)\"
 
-OBJECTS=jpeg.o png.o main.o image.o numlock.o cfg.o switchuser.o input.o app.o panel.o
-BGOBJECTS=jpeg.o png.o image.o ssetbg.o
+OBJECTS=jpeg.o png.o main.o image.o numlock.o cfg.o switchuser.o app.o panel.o
+.ifdef USE_PAM
+  OBJECTS+=PAM.o 
+.endif
 
-all: slim ssetbg
+all: slim
 
 slim: $(OBJECTS) 
 	$(CXX) $(LDFLAGS) $(OBJECTS) -o $(NAME)
 
-ssetbg: $(BGOBJECTS)
-	$(CXX) $(LDFLAGS) $(BGOBJECTS) -o ssetbg
-
 .cpp.o:
 	$(CXX) $(CFLAGS) $(DEFINES) $(CUSTOM) -c $< -o $@
 
 .c.o:
 	$(CC) $(CFLAGS) $(DEFINES) $(CUSTOM) -c $< -o $@
 
-install: slim ssetbg install-theme
+install: slim install-theme
 	install -d $(DESTDIR)$(PREFIX)/bin
 	install -d $(DESTDIR)$(MANDIR)/man1
 	install -d $(DESTDIR)$(CFGDIR)
 	install -m 755 slim $(DESTDIR)$(PREFIX)/bin
-	install -D -m 755 ssetbg $(DESTDIR)$(PREFIX)/bin/ssetbg
 	install -m 644 slim.1 $(DESTDIR)$(MANDIR)/man1
-	install -m 644 ssetbg.1 $(DESTDIR)$(MANDIR)/man1
 	test -e $(DESTDIR)$(CFGDIR)/slim.conf || \
 		install -m 644 slim.conf $(DESTDIR)$(CFGDIR)
 
 clean:
-	@rm -f slim ssetbg *.o
+	@rm -f slim *.o
 
 dist:
 	@rm -rf $(NAME)-$(VERSION)
 	@mkdir $(NAME)-$(VERSION)
 	@cp -r *.cpp *.h *.c Makefile Makefile.* COPYING ChangeLog README INSTALL TODO \
-		xinitrc.sample slim.1 ssetbg.1 THEMES themes slim.conf $(NAME)-$(VERSION)
+		xinitrc.sample slim.1 THEMES themes slim.conf $(NAME)-$(VERSION)
 	@rm -rf $(NAME)-$(VERSION)/themes/.svn	$(NAME)-$(VERSION)/themes/default/.svn
 	@tar cvzf $(NAME)-$(VERSION).tar.gz $(NAME)-$(VERSION)
 	@rm -rf $(NAME)-$(VERSION)

+ 12 - 11
Makefile.netbsd

@@ -8,6 +8,10 @@ CC=/usr/bin/gcc
 CFLAGS=-I. -I/usr/X11R6/include -I/usr/X11R6/include/freetype2 -I/usr/X11R6/include/freetype2/config -I/usr/pkg/include -I/usr/include
 LDFLAGS=-L/usr/X11R6/lib -L/usr/pkg/lib -lXft -lX11 -lfreetype -lXrender -lfontconfig -lpng -lz -lm -lcrypt -lXmu -lpng -ljpeg
 CUSTOM=-DNEEDS_BASENAME
+.ifdef(USE_PAM)
+  LDFLAGS+= -lpam
+  CUSTOM+= -DUSE_PAM
+.endif
 PREFIX=/usr
 CFGDIR=/etc
 MANDIR=/usr/share/man
@@ -20,42 +24,39 @@ VERSION=1.2.6
 DEFINES=-DPACKAGE=\"$(NAME)\" -DVERSION=\"$(VERSION)\" \
 		-DPKGDATADIR=\"$(PREFIX)/share/slim\" -DSYSCONFDIR=\"$(CFGDIR)\"
 
-OBJECTS=jpeg.o png.o main.o image.o numlock.o cfg.o switchuser.o input.o app.o panel.o
-BGOBJECTS=jpeg.o png.o image.o ssetbg.o
+OBJECTS=jpeg.o png.o main.o image.o numlock.o cfg.o switchuser.o app.o panel.o
+.ifdef USE_PAM
+  OBJECTS+=PAM.o 
+.endif
 
-all: slim ssetbg
+all: slim
 
 slim: $(OBJECTS) 
 	$(CXX) $(LDFLAGS) $(OBJECTS) -o $(NAME)
 
-ssetbg: $(BGOBJECTS)
-	$(CXX) $(LDFLAGS) $(BGOBJECTS) -o ssetbg
-
 .cpp.o:
 	$(CXX) $(CFLAGS) $(DEFINES) $(CUSTOM) -c $< -o $@
 
 .c.o:
 	$(CC) $(CFLAGS) $(DEFINES) $(CUSTOM) -c $< -o $@
 
-install: slim ssetbg install-theme
+install: slim install-theme
 	install -d $(DESTDIR)$(PREFIX)/bin
 	install -d $(DESTDIR)$(MANDIR)/man1
 	install -d $(DESTDIR)$(CFGDIR)
 	install -m 755 slim $(DESTDIR)$(PREFIX)/bin
-	install -m 755 ssetbg $(DESTDIR)$(PREFIX)/bin
 	install -m 644 slim.1 $(DESTDIR)$(MANDIR)/man1
-	install -m 644 ssetbg.1 $(DESTDIR)$(MANDIR)/man1
 	test -e $(DESTDIR)$(CFGDIR)/slim.conf || \
 		install -m 644 slim.conf $(DESTDIR)$(CFGDIR)
 
 clean:
-	@rm -f slim ssetbg *.o
+	@rm -f slim *.o
 
 dist:
 	@rm -rf $(NAME)-$(VERSION)
 	@mkdir $(NAME)-$(VERSION)
 	@cp -r *.cpp *.h *.c Makefile Makefile.* COPYING ChangeLog README INSTALL TODO \
-		xinitrc.sample slim.1 ssetbg.1 THEMES themes slim.conf $(NAME)-$(VERSION)
+		xinitrc.sample slim.1 THEMES themes slim.conf $(NAME)-$(VERSION)
 	@rm -rf $(NAME)-$(VERSION)/themes/.svn	$(NAME)-$(VERSION)/themes/default/.svn
 	@tar cvzf $(NAME)-$(VERSION).tar.gz $(NAME)-$(VERSION)
 	@rm -rf $(NAME)-$(VERSION)

+ 4 - 16
Makefile.openbsd

@@ -20,8 +20,7 @@ VERSION=1.2.6
 DEFINES=-DPACKAGE=\"$(NAME)\" -DVERSION=\"$(VERSION)\" \
 		-DPKGDATADIR=\"$(PREFIX)/share/slim\" -DSYSCONFDIR=\"$(CFGDIR)\"
 
-OBJECTS=jpeg.o png.o main.o image.o numlock.o cfg.o switchuser.o input.o app.o panel.o
-BGOBJECTS=jpeg.o png.o image.o ssetbg.o
+OBJECTS=jpeg.o png.o main.o image.o numlock.o cfg.o switchuser.o app.o panel.o
 
 .SUFFIXES: .c.o .cpp.o
 
@@ -30,9 +29,6 @@ all: slim
 slim: $(OBJECTS) 
 	$(CXX) $(LDFLAGS) $(OBJECTS) -o $(NAME)
 
-ssetbg: $(BGOBJECTS)
-	$(CXX) $(LDFLAGS) $(BGOBJECTS) -o ssetbg
-
 main.o:
 	$(CXX) $(CFLAGS) $(DEFINES) $(CUSTOM) -c main.cpp -o $@
 
@@ -48,40 +44,32 @@ cfg.o:
 switchuser.o:
 	$(CXX) $(CFLAGS) $(DEFINES) $(CUSTOM) -c switchuser.cpp -o $@
 
-input.o:
-	$(CXX) $(CFLAGS) $(DEFINES) $(CUSTOM) -c input.cpp -o $@
-
 app.o:
 	$(CXX) $(CFLAGS) $(DEFINES) $(CUSTOM) -c app.cpp -o $@
 
 panel.o:
 	$(CXX) $(CFLAGS) $(DEFINES) $(CUSTOM) -c panel.cpp -o $@
 
-ssetbg.o:
-	$(CXX) $(CFLAGS) $(DEFINES) $(CUSTOM) -c ssetbg.cpp -o $@
-
 .c.o:
 	$(CC) $(CFLAGS) $(DEFINES) $(CUSTOM) -c $< -o $@
 
-install: slim ssetbg install-theme
+install: slim install-theme
 	install -d $(DESTDIR)$(PREFIX)/bin
 	install -d $(DESTDIR)$(MANDIR)/man1
 	install -d $(DESTDIR)$(CFGDIR)
 	install -m 755 slim $(DESTDIR)$(PREFIX)/bin
-	install -m 755 ssetbg $(DESTDIR)$(PREFIX)/bin
 	install -m 644 slim.1 $(DESTDIR)$(MANDIR)/man1
-	install -m 644 ssetbg.1 $(DESTDIR)$(MANDIR)/man1
 	test -e $(DESTDIR)$(CFGDIR)/slim.conf || \
 		install -m 644 slim.conf $(DESTDIR)$(CFGDIR)
 
 clean:
-	@rm -f slim ssetbg *.o
+	@rm -f slim *.o
 
 dist:
 	@rm -rf $(NAME)-$(VERSION)
 	@mkdir $(NAME)-$(VERSION)
 	@cp -r *.cpp *.h *.c Makefile Makefile.* COPYING ChangeLog README INSTALL TODO \
-		xinitrc.sample slim.1 ssetbg.1 THEMES themes slim.conf $(NAME)-$(VERSION)
+		xinitrc.sample slim.1 THEMES themes slim.conf $(NAME)-$(VERSION)
 	@rm -rf $(NAME)-$(VERSION)/themes/.svn	$(NAME)-$(VERSION)/themes/default/.svn
 	@tar cvzf $(NAME)-$(VERSION).tar.gz $(NAME)-$(VERSION)
 	@rm -rf $(NAME)-$(VERSION)

+ 275 - 0
PAM.cpp

@@ -0,0 +1,275 @@
+/* SLiM - Simple Login Manager
+   Copyright (C) 2007 Martin Parm
+
+   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.
+*/
+#include <PAM.h>
+#include <string>
+#include <iostream>
+
+namespace PAM {
+    Exception::Exception(pam_handle_t* _pam_handle,
+               const std::string& _func_name,
+               int _errnum):
+        errnum(_errnum),
+        errstr(pam_strerror(_pam_handle, _errnum)),
+        func_name(_func_name)
+        {}
+
+    Exception::~Exception(void){}
+
+    Auth_Exception::Auth_Exception(pam_handle_t* _pam_handle,
+                                   const std::string& _func_name,
+                                   int _errnum):
+        Exception(_pam_handle, _func_name, _errnum){}
+
+    Cred_Exception::Cred_Exception(pam_handle_t* _pam_handle,
+                                   const std::string& _func_name,
+                                   int _errnum):
+        Exception(_pam_handle, _func_name, _errnum){}
+
+    int Authenticator::_end(void){
+        int result=pam_end(pam_handle, last_result);
+        pam_handle=0;
+        return result;
+    }
+
+    Authenticator::Authenticator(conversation* conv, void* data):
+        pam_handle(0),
+        last_result(PAM_SUCCESS)
+    {
+        pam_conversation.conv=conv;
+        pam_conversation.appdata_ptr=data;
+    }
+
+    Authenticator::~Authenticator(void){
+        if (pam_handle) _end();
+    }
+
+    void Authenticator::start(const std::string& service){
+        switch((last_result=pam_start(service.c_str(), NULL, &pam_conversation, &pam_handle))){
+            default:
+                throw Exception(pam_handle, "pam_start()", last_result);
+
+            case PAM_SUCCESS:
+                break;
+        }
+        return;
+    }
+
+    void Authenticator::end(void){
+        switch((last_result=_end())){
+            default:
+                throw Exception(pam_handle, "pam_end()", last_result);
+
+            case PAM_SUCCESS:
+                break;
+
+        }
+        return;
+    }
+
+    void Authenticator::set_item(const Authenticator::ItemType item, const void* value){
+        switch((last_result=pam_set_item(pam_handle, item, value))){
+            default:
+            _end();
+                throw Exception(pam_handle, "pam_set_item()", last_result);
+
+            case PAM_SUCCESS:
+                break;
+        }
+        return;
+    }
+
+    const void* Authenticator::get_item(const Authenticator::ItemType item){
+        const void* data;
+        switch ((last_result=pam_get_item(pam_handle, item, &data))){
+            default:
+            case PAM_SYSTEM_ERR:
+#ifdef __LIBPAM_VERSION
+            case PAM_BAD_ITEM:
+#endif
+                _end();
+                throw Exception(pam_handle, "pam_get_item()", last_result);
+
+            case PAM_PERM_DENIED: // The value of item was NULL
+            case PAM_SUCCESS:
+                break;
+        }
+        return data;
+    }
+
+#ifdef __LIBPAM_VERSION
+    void Authenticator::fail_delay(const unsigned int micro_sec){
+        switch((last_result=pam_fail_delay(pam_handle, micro_sec))){
+            default:
+                _end();
+                throw Exception(pam_handle, "fail_delay()", last_result);
+
+            case PAM_SUCCESS:
+                break;
+        }
+        return;
+    }
+#endif
+
+    void Authenticator::authenticate(void){
+        switch((last_result=pam_authenticate(pam_handle, 0))){
+            default:
+            case PAM_ABORT:
+            case PAM_AUTHINFO_UNAVAIL:
+                _end();
+                throw Exception(pam_handle, "pam_authenticate()", last_result);
+
+            case PAM_USER_UNKNOWN:
+            case PAM_MAXTRIES:
+            case PAM_CRED_INSUFFICIENT:
+            case PAM_AUTH_ERR:
+                throw Auth_Exception(pam_handle, "pam_authentication()", last_result);
+
+            case PAM_SUCCESS:
+                break;
+        }
+
+        switch((last_result=pam_acct_mgmt(pam_handle, PAM_SILENT))){
+            // The documentation and implementation of Linux PAM differs:
+            // PAM_NEW_AUTHTOKEN_REQD is described in the documentation but
+            // don't exists in the actual implementation. This issue needs
+            // to be fixes at some point.
+
+            default:
+            //case PAM_NEW_AUTHTOKEN_REQD:
+            case PAM_ACCT_EXPIRED:
+            case PAM_USER_UNKNOWN:
+                _end();
+                throw Exception(pam_handle, "pam_acct_mgmt()", last_result);
+                
+            case PAM_AUTH_ERR:
+            case PAM_PERM_DENIED:
+                throw Auth_Exception(pam_handle, "pam_acct_mgmt()", last_result);
+
+            case PAM_SUCCESS:
+                break;
+        };
+        return;
+    }
+
+    void Authenticator::open_session(void){
+        switch((last_result=pam_setcred(pam_handle, PAM_ESTABLISH_CRED))){
+            default:
+            case PAM_CRED_ERR:
+            case PAM_CRED_UNAVAIL:
+                _end();
+                throw Exception(pam_handle, "pam_setcred()", last_result);
+
+            case PAM_CRED_EXPIRED:
+            case PAM_USER_UNKNOWN:
+                throw Cred_Exception(pam_handle, "pam_setcred()", last_result);
+
+            case PAM_SUCCESS:
+                break;
+        }
+
+        switch((last_result=pam_open_session(pam_handle, 0))){
+            // The documentation and implementation of Linux PAM differs:
+            // PAM_SESSION_ERROR is described in the documentation but
+            // don't exists in the actual implementation. This issue needs
+            // to be fixes at some point.
+
+            default:
+            //case PAM_SESSION_ERROR:
+                pam_setcred(pam_handle, PAM_DELETE_CRED);
+                _end();
+                throw Exception(pam_handle, "pam_open_session()", last_result);
+
+            case PAM_SUCCESS:
+                break;
+        };
+        return;
+    }
+
+    void Authenticator::close_session(void){
+        switch((last_result=pam_close_session(pam_handle, 0))){
+            // The documentation and implementation of Linux PAM differs:
+            // PAM_SESSION_ERROR is described in the documentation but
+            // don't exists in the actual implementation. This issue needs
+            // to be fixes at some point.
+
+            default:
+            //case PAM_SESSION_ERROR:
+                pam_setcred(pam_handle, PAM_DELETE_CRED);
+                _end();
+                throw Exception(pam_handle, "pam_close_session", last_result);
+
+            case PAM_SUCCESS:
+                break;
+        };
+        switch((last_result=pam_setcred(pam_handle, PAM_DELETE_CRED))){
+            default:
+            case PAM_CRED_ERR:
+            case PAM_CRED_UNAVAIL:
+            case PAM_CRED_EXPIRED:
+            case PAM_USER_UNKNOWN:
+                _end();
+                throw Exception(pam_handle, "pam_setcred()", last_result);
+
+            case PAM_SUCCESS:
+                break;
+        }
+        return;
+    }
+
+    void Authenticator::setenv(const std::string& key, const std::string& value){
+        std::string name_value = key+"="+value;
+        switch((last_result=pam_putenv(pam_handle, name_value.c_str()))){
+            default:
+            case PAM_PERM_DENIED:
+            case PAM_ABORT:
+            case PAM_BUF_ERR:
+#ifdef __LIBPAM_VERSION
+            case PAM_BAD_ITEM:
+#endif
+                _end();
+                throw Exception(pam_handle, "pam_putenv()", last_result);
+
+            case PAM_SUCCESS:
+                break;
+        };
+        return;
+    }
+
+    void Authenticator::delenv(const std::string& key){
+        switch((last_result=pam_putenv(pam_handle, key.c_str()))){
+            default:
+            case PAM_PERM_DENIED:
+            case PAM_ABORT:
+            case PAM_BUF_ERR:
+#ifdef __LIBPAM_VERSION
+            case PAM_BAD_ITEM:
+#endif
+                _end();
+                throw Exception(pam_handle, "pam_putenv()", last_result);
+
+            case PAM_SUCCESS:
+                break;
+        };
+        return;
+    }
+
+    const char* Authenticator::getenv(const std::string& key){
+        return pam_getenv(pam_handle, key.c_str());
+    }
+
+    char** Authenticator::getenvlist(void){
+        return pam_getenvlist(pam_handle);
+    }
+
+};
+
+std::ostream& operator<<( std::ostream& os, const PAM::Exception& e){
+    os << e.func_name << ": " << e.errstr;
+    return os;
+}

+ 99 - 0
PAM.h

@@ -0,0 +1,99 @@
+/* SLiM - Simple Login Manager
+   Copyright (C) 2007 Martin Parm
+
+   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.
+*/
+
+#ifndef _PAM_H_
+#define _PAM_H_
+#include <string>
+#include <security/pam_appl.h>
+
+#ifdef __LIBPAM_VERSION
+#include <security/pam_misc.h>
+#endif
+
+namespace PAM {
+    class Exception{
+    public:
+        int errnum;
+        std::string errstr;
+        std::string func_name;
+        Exception(pam_handle_t* _pam_handle,
+                  const std::string& _func_name,
+                  int _errnum);
+        virtual ~Exception(void);
+    };
+
+    class Auth_Exception: public Exception{
+    public:
+        Auth_Exception(pam_handle_t* _pam_handle,
+                       const std::string& _func_name,
+                       int _errnum);
+    };
+
+    class Cred_Exception: public Exception{
+    public:
+        Cred_Exception(pam_handle_t* _pam_handle,
+                       const std::string& _func_name,
+                       int _errnum);
+    };
+
+
+    class Authenticator{
+    private:
+        struct pam_conv pam_conversation;
+        pam_handle_t* pam_handle;
+        int last_result;
+
+        int _end(void);
+    public:
+        typedef int (conversation)(int num_msg,
+                           const struct pam_message **msg,
+                           struct pam_response **resp,
+                           void *appdata_ptr);
+
+        enum ItemType {
+            Service     = PAM_SERVICE,
+            User        = PAM_USER,
+            User_Prompt = PAM_USER_PROMPT,
+            TTY         = PAM_TTY,
+            Requestor   = PAM_RUSER,
+            Host        = PAM_RHOST,
+            Conv        = PAM_CONV,
+#ifdef __LIBPAM_VERSION
+            //Fail_Delay  = PAM_FAIL_DELAY
+#endif
+        };
+
+    public:
+        Authenticator(conversation* conv, void* data=0);
+        ~Authenticator(void);
+        
+        void start(const std::string& service);
+        void end(void);
+        void set_item(const ItemType item, const void* value);
+        const void* get_item(const ItemType item);
+#ifdef __LIBPAM_VERSION
+        void fail_delay(const unsigned int micro_sec);
+#endif
+        void authenticate(void);
+        void open_session(void);
+        void close_session(void);
+        void setenv(const std::string& key, const std::string& value);
+        void delenv(const std::string& key);
+        const char* getenv(const std::string& key);
+        char** getenvlist(void);
+
+    private:
+        // Explicitly disable copy constructor and copy assignment
+        Authenticator(const PAM::Authenticator&);
+        Authenticator& operator=(const PAM::Authenticator&);
+    };
+};
+
+std::ostream& operator<<( std::ostream& os, const PAM::Exception& e);
+#endif

+ 374 - 100
app.cpp

@@ -26,6 +26,78 @@
 #include "numlock.h"
 
 
+#ifdef HAVE_SHADOW
+#include <shadow.h>
+#endif
+
+#ifdef USE_PAM
+#include <string>
+
+int conv(int num_msg, const struct pam_message **msg,
+         struct pam_response **resp, void *appdata_ptr){
+    *resp = (struct pam_response *) calloc(num_msg, sizeof(struct pam_response));
+    Panel* panel = *static_cast<Panel**>(appdata_ptr);
+    int result = PAM_SUCCESS;
+    for (int i=0; i<num_msg; i++){
+        resp[i]->resp=0;
+        resp[i]->resp_retcode=0;
+        switch(msg[i]->msg_style){
+            case PAM_PROMPT_ECHO_ON:
+                // We assume PAM is asking for the username
+                panel->EventHandler(Panel::Get_Name);
+                switch(panel->getAction()){
+                    case Panel::Suspend:
+                    case Panel::Halt:
+                    case Panel::Reboot:
+                        resp[i]->resp=strdup("root");
+                        break;
+
+                    case Panel::Console:
+                    case Panel::Exit:
+                    case Panel::Login:
+                        resp[i]->resp=strdup(panel->GetName().c_str());
+                        break;
+                }
+                break;
+
+            case PAM_PROMPT_ECHO_OFF:
+                // We assume PAM is asking for the password
+                switch(panel->getAction()){
+                    case Panel::Console:
+                    case Panel::Exit:
+                        // We should leave now!
+                        result=PAM_CONV_ERR;
+                        break;
+
+                    default:
+                        panel->EventHandler(Panel::Get_Passwd);
+                        resp[i]->resp=strdup(panel->GetPasswd().c_str());
+                        break;
+                }
+                break;
+
+            case PAM_ERROR_MSG:
+            case PAM_TEXT_INFO:
+                // We simply write these to the log
+                // TODO: Maybe we should simply ignore them
+                cerr << APPNAME << ": " << msg[i]->msg << endl;
+                break;
+        }
+        if (result!=PAM_SUCCESS) break;
+    }
+    if (result!=PAM_SUCCESS){
+        for (int i=0; i<num_msg; i++){
+            if (resp[i]->resp==0) continue;
+            free(resp[i]->resp);
+            resp[i]->resp=0;
+        };
+        free(*resp);
+        *resp=0;
+    };
+    return result;
+}
+#endif
+
 extern App* LoginApp;
 
 void CatchSignal(int sig) {
@@ -53,8 +125,12 @@ void User1Signal(int sig) {
 }
 
 
-App::App(int argc, char** argv) {
-
+#ifdef USE_PAM
+App::App(int argc, char** argv):
+    pam(conv, static_cast<void*>(&LoginPanel)){
+#else
+App::App(int argc, char** argv){
+#endif
     int tmp;
     ServerPID = -1;
     testing = false;
@@ -63,7 +139,7 @@ App::App(int argc, char** argv) {
     // Parse command line
     while((tmp = getopt(argc, argv, "vhp:d?")) != EOF) {
         switch (tmp) {
-        case 'p':	// Test theme
+        case 'p':    // Test theme
             testtheme = optarg;
             testing = true;
             if (testtheme == NULL) {
@@ -71,14 +147,14 @@ App::App(int argc, char** argv) {
                 exit(ERR_EXIT);
             }
             break;
-        case 'd':	// Daemon mode
+        case 'd':    // Daemon mode
             daemonmode = true;
             break;
-        case 'v':	// Version
+        case 'v':    // Version
             std::cout << APPNAME << " version " << VERSION << endl;
             exit(OK_EXIT);
             break;
-        case '?':	// Illegal
+        case '?':    // Illegal
             cerr << endl;
         case 'h':   // Help
             cerr << "usage:  " << APPNAME << " [option ...]" << endl
@@ -100,7 +176,6 @@ App::App(int argc, char** argv) {
 
 
 void App::Run() {
-
     DisplayName = DISPLAY;
 
 #ifdef XNEST_DEBUG
@@ -111,6 +186,7 @@ void App::Run() {
     }
 #endif
 
+
     // Read configuration and theme
     cfg = new Cfg;
     cfg->readConf(CFGFILE);
@@ -133,6 +209,20 @@ void App::Run() {
         }
     }
 
+#ifdef USE_PAM
+    try{
+        pam.start("slim");
+        pam.set_item(PAM::Authenticator::TTY, DisplayName);
+        pam.set_item(PAM::Authenticator::Requestor, "root");
+        pam.set_item(PAM::Authenticator::Host, "localhost");
+
+    }
+    catch(PAM::Exception& e){
+        cerr << APPNAME << ": " << e << endl;
+        exit(ERR_EXIT);
+    };
+#endif
+
     bool loaded = false;
     while (!loaded) {
         themedir =  themebase + themeName;
@@ -169,10 +259,10 @@ void App::Run() {
 
 #ifndef XNEST_DEBUG
         OpenLog();
-		
-		if (cfg->getOption("daemon") == "yes") {
-			daemonmode = true;
-		}
+        
+        if (cfg->getOption("daemon") == "yes") {
+            daemonmode = true;
+        }
 
         // Daemonize
         if (daemonmode) {
@@ -202,7 +292,6 @@ void App::Run() {
     Scr = DefaultScreen(Dpy);
     Root = RootWindow(Dpy, Scr);
 
-
     // for tests we use a standard window
     if (testing) {
         Window RealRoot = RootWindow(Dpy, Scr);
@@ -219,9 +308,8 @@ void App::Run() {
     LoginPanel = new Panel(Dpy, Scr, Root, cfg, themedir);
 
     // Start looping
-    XEvent event;
     int panelclosed = 1;
-    int Action;
+    Panel::ActionType Action;
     bool firstloop = true; // 1st time panel is shown (for automatic username)
 
     while(1) {
@@ -239,53 +327,124 @@ void App::Run() {
             LoginPanel->OpenPanel();
         }
 
-        Action = WAIT;
-        LoginPanel->GetInput()->Reset();
+        LoginPanel->Reset();
         if (firstloop && cfg->getOption("default_user") != "") {
-            LoginPanel->GetInput()->SetName(cfg->getOption("default_user") );
+            LoginPanel->SetName(cfg->getOption("default_user") );
             firstloop = false;
         }
 
-        while(Action == WAIT) {
-            XNextEvent(Dpy, &event);
-            Action = LoginPanel->EventHandler(&event);
-        }
 
-        if(Action == FAIL) {
+        if (!AuthenticateUser()){
             panelclosed = 0;
             LoginPanel->ClearPanel();
             XBell(Dpy, 100);
-        } else {
-            // for themes test we just quit
-            if (testing) {
-                Action = EXIT;
-            }
-            panelclosed = 1;
-            LoginPanel->ClosePanel();
-
-            switch(Action) {
-                case LOGIN:
-                    Login();
-                    break;
-                case CONSOLE:
-                    Console();
-                    break;
-                case REBOOT:
-                    Reboot();
-                    break;
-                case HALT:
-                    Halt();
-                    break;
-                case SUSPEND:
-                    Suspend();
-                    break;
-                case EXIT:
-                    Exit();
-                    break;
-            }
+            continue;
+        }
+        
+
+        Action = LoginPanel->getAction();
+        // for themes test we just quit
+        if (testing) {
+            Action = Panel::Exit;
         }
+        panelclosed = 1;
+        LoginPanel->ClosePanel();
+
+        switch(Action) {
+            case Panel::Login:
+                Login();
+                break;
+            case Panel::Console:
+                Console();
+                break;
+            case Panel::Reboot:
+                Reboot();
+                break;
+            case Panel::Halt:
+                Halt();
+                break;
+            case Panel::Suspend:
+                Suspend();
+                break;
+            case Panel::Exit:
+                Exit();
+                break;
+        }
+    }
+}
+
+#ifdef USE_PAM
+bool App::AuthenticateUser(void){
+    // Reset the username
+    try{
+        pam.set_item(PAM::Authenticator::User, 0);
+        pam.authenticate();
+    }
+    catch(PAM::Auth_Exception& e){
+        switch(LoginPanel->getAction()){
+            case Panel::Exit:
+            case Panel::Console:
+                return true; // <--- This is simply fake!
+            default:
+                break;
+        };
+        cerr << APPNAME << ": " << e << endl;
+        return false;
+    }
+    catch(PAM::Exception& e){
+        cerr << APPNAME << ": " << e << endl;
+        exit(ERR_EXIT);
+    };
+    return true;
+}
+#else
+bool App::AuthenticateUser(void){
+    LoginPanel->EventHandler(Panel::Get_Name);
+    switch(LoginPanel->getAction()){
+        case Panel::Exit:
+        case Panel::Console:
+            cerr << APPNAME << ": Got a special command (" << LoginPanel->GetName() << ")" << endl;
+            return true; // <--- This is simply fake!
+        default:
+            break;
+    };
+    LoginPanel->EventHandler(Panel::Get_Passwd);
+    
+    char *encrypted, *correct;
+    struct passwd *pw;
+
+    switch(LoginPanel->getAction()){
+        case Panel::Suspend:
+        case Panel::Halt:
+        case Panel::Reboot:
+            pw = getpwnam("root");
+            break;
+        case Panel::Console:
+        case Panel::Exit:
+        case Panel::Login:
+            pw = getpwnam(LoginPanel->GetName().c_str());
+            break;
     }
+    endpwent();
+    if(pw == 0)
+        return false;
+
+#ifdef HAVE_SHADOW
+    struct spwd *sp = getspnam(pw->pw_name);    
+    endspent();
+    if(sp)
+        correct = sp->sp_pwdp;
+    else
+#endif        // HAVE_SHADOW
+        correct = pw->pw_passwd;
+
+    if(correct == 0 || correct[0] == '\0')
+        return true;
+
+    encrypted = crypt(LoginPanel->GetPasswd().c_str(), correct);
+    return ((strcmp(encrypted, correct) == 0) ? true : false);
 }
+#endif
 
 
 int App::GetServerPID() {
@@ -294,34 +453,104 @@ int App::GetServerPID() {
 
 // Hide the cursor
 void App::HideCursor() {
-	if (cfg->getOption("hidecursor") == "true") {
-		XColor		    black;
-		char		    cursordata[1];
-		Pixmap		    cursorpixmap;
-		Cursor		    cursor;
-		cursordata[0]=0;
-		cursorpixmap=XCreateBitmapFromData(Dpy,Root,cursordata,1,1);
-		black.red=0;
-		black.green=0;
-		black.blue=0;
-		cursor=XCreatePixmapCursor(Dpy,cursorpixmap,cursorpixmap,&black,&black,0,0);
-		XDefineCursor(Dpy,Root,cursor);
-	}
+    if (cfg->getOption("hidecursor") == "true") {
+        XColor            black;
+        char            cursordata[1];
+        Pixmap            cursorpixmap;
+        Cursor            cursor;
+        cursordata[0]=0;
+        cursorpixmap=XCreateBitmapFromData(Dpy,Root,cursordata,1,1);
+        black.red=0;
+        black.green=0;
+        black.blue=0;
+        cursor=XCreatePixmapCursor(Dpy,cursorpixmap,cursorpixmap,&black,&black,0,0);
+        XDefineCursor(Dpy,Root,cursor);
+    }
 }
 
 void App::Login() {
     struct passwd *pw;
     pid_t pid;
 
-    pw = LoginPanel->GetInput()->GetPasswdStruct();
+#ifdef USE_PAM
+    try{
+        pam.open_session();
+        pw = getpwnam(static_cast<const char*>(pam.get_item(PAM::Authenticator::User)));
+    }
+    catch(PAM::Cred_Exception& e){
+        // Credentials couldn't be established
+        cerr << APPNAME << ": " << e << endl;
+        return;
+    }
+    catch(PAM::Exception& e){
+        cerr << APPNAME << ": " << e << endl;
+        exit(ERR_EXIT);
+    };
+#else
+    pw = getpwnam(LoginPanel->GetName().c_str());
+#endif
+    endpwent();
     if(pw == 0)
         return;
+    if (pw->pw_shell[0] == '\0') {
+        setusershell();
+        strcpy(pw->pw_shell, getusershell());
+        endusershell();
+    }
+
+    // Setup the environment
+    char* term = getenv("TERM");
+    string maildir = _PATH_MAILDIR;
+    maildir.append("/");
+    maildir.append(pw->pw_name);
+    string xauthority = pw->pw_dir;
+    xauthority.append("/.Xauthority");
+    
+#ifdef USE_PAM
+    // Setup the PAM environment
+    try{
+        if(term) pam.setenv("TERM", term);
+        pam.setenv("HOME", pw->pw_dir);
+        pam.setenv("SHELL", pw->pw_shell);
+        pam.setenv("USER", pw->pw_name);
+        pam.setenv("LOGNAME", pw->pw_name);
+        pam.setenv("PATH", cfg->getOption("default_path").c_str());
+        pam.setenv("DISPLAY", DisplayName);
+        pam.setenv("MAIL", maildir.c_str());
+        pam.setenv("XAUTHORITY", xauthority.c_str());
+    }
+    catch(PAM::Exception& e){
+        cerr << APPNAME << ": " << e << endl;
+        exit(ERR_EXIT);
+    }
+#endif
 
     // Create new process
     pid = fork();
     if(pid == 0) {
+#ifdef USE_PAM
+        // Get a copy of the environment and close the child's copy
+        // of the PAM-handle.
+        char** child_env = pam.getenvlist();
+        pam.end();
+#else
+        const int Num_Of_Variables = 10; // Number of env. variables + 1
+        char** child_env = static_cast<char**>(malloc(sizeof(char*)*Num_Of_Variables));
+        int n = 0;
+        if(term) child_env[n++]=StrConcat("TERM=", term);
+        child_env[n++]=StrConcat("HOME=", pw->pw_dir);
+        child_env[n++]=StrConcat("SHELL=", pw->pw_shell);
+        child_env[n++]=StrConcat("USER=", pw->pw_name);
+        child_env[n++]=StrConcat("LOGNAME=", pw->pw_name);
+        child_env[n++]=StrConcat("PATH=", cfg->getOption("default_path").c_str());
+        child_env[n++]=StrConcat("DISPLAY=", DisplayName);
+        child_env[n++]=StrConcat("MAIL=", maildir.c_str());
+        child_env[n++]=StrConcat("XAUTHORITY=", xauthority.c_str());
+        child_env[n++]=0;
+#endif
+
         // Login process starts here
-        SwitchUser Su(pw, cfg, DisplayName);
+        SwitchUser Su(pw, cfg, DisplayName, child_env);
         string session = LoginPanel->getSession();
         string loginCommand = cfg->getOption("login_cmd");
         replaceVariables(loginCommand, SESSION_VAR, session);
@@ -332,7 +561,7 @@ void App::Login() {
             system(sessStart.c_str());
         }
         Su.Login(loginCommand.c_str(), mcookie.c_str());
-        exit(OK_EXIT);
+        _exit(OK_EXIT);
     }
 
 #ifndef XNEST_DEBUG
@@ -356,7 +585,16 @@ void App::Login() {
         }
     }
 
-    // Close all clients
+#ifdef USE_PAM
+    try{
+        pam.close_session();
+    }
+    catch(PAM::Exception& e){
+        cerr << APPNAME << ": " << e << endl;
+    };
+#endif
+
+// Close all clients
     KillAllClients(False);
     KillAllClients(True);
 
@@ -383,6 +621,15 @@ void App::Reboot() {
     // Stop alarm clock
     alarm(0);
 
+#ifdef USE_PAM
+    try{
+        pam.end();
+    }
+    catch(PAM::Exception& e){
+        cerr << APPNAME << ": " << e << endl;
+    };
+#endif
+
     // Write message
     LoginPanel->Message((char*)cfg->getOption("reboot_msg").c_str());
     sleep(3);
@@ -399,6 +646,15 @@ void App::Halt() {
     // Stop alarm clock
     alarm(0);
 
+#ifdef USE_PAM
+    try{
+        pam.end();
+    }
+    catch(PAM::Exception& e){
+        cerr << APPNAME << ": " << e << endl;
+    };
+#endif
+
     // Write message
     LoginPanel->Message((char*)cfg->getOption("shutdown_msg").c_str());
     sleep(3);
@@ -434,6 +690,15 @@ void App::Console() {
 
 
 void App::Exit() {
+#ifdef USE_PAM
+    try{
+        pam.end();
+    }
+    catch(PAM::Exception& e){
+        cerr << APPNAME << ": " << e << endl;
+    };
+#endif
+
     if (testing) {
         char* testmsg = "This is a test message :-)";
         LoginPanel->Message(testmsg);
@@ -455,9 +720,18 @@ int CatchErrors(Display *dpy, XErrorEvent *ev) {
 }
 
 void App::RestartServer() {
-        StopServer(); 
-        RemoveLock();
-        Run();
+#ifdef USE_PAM
+    try{
+        pam.end();
+    }
+    catch(PAM::Exception& e){
+        cerr << APPNAME << ": " << e << endl;
+    };
+#endif
+
+    StopServer(); 
+    RemoveLock();
+    Run();
 } 
 
 void App::KillAllClients(Bool top) {
@@ -493,9 +767,9 @@ void App::KillAllClients(Bool top) {
 
 
 int App::ServerTimeout(int timeout, char* text) {
-    int	i = 0;
+    int    i = 0;
     int pidfound = -1;
-    static char	*lasttext;
+    static char    *lasttext;
 
     for(;;) {
         pidfound = waitpid(ServerPID, NULL, WNOHANG);
@@ -522,8 +796,8 @@ int App::ServerTimeout(int timeout, char* text) {
 
 
 int App::WaitForServer() {
-    int	ncycles	 = 120;
-    int	cycles;
+    int    ncycles     = 120;
+    int    cycles;
 
     for(cycles = 0; cycles < ncycles; cycles++) {
         if((Dpy = XOpenDisplay(DisplayName))) {
@@ -628,9 +902,9 @@ int App::StartServer() {
         NumLock::setOn();
     } else if (numlock == "off") {
         NumLock::setOff();
-	}
+    }
     
-	delete args;
+    delete args;
 
     return ServerPID;
 }
@@ -730,15 +1004,15 @@ void App::setBackground(const string& themedir) {
         } else if (bgstyle == "tile") {
             image->Tile(XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)), XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)));
         } else if (bgstyle == "center") {
-    	    string hexvalue = cfg->getOption("background_color");
+            string hexvalue = cfg->getOption("background_color");
             hexvalue = hexvalue.substr(1,6);
-    	    image->Center(XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)), XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)),
-        			    hexvalue.c_str());
+            image->Center(XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)), XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)),
+                        hexvalue.c_str());
         } else { // plain color or error
-    	    string hexvalue = cfg->getOption("background_color");
+            string hexvalue = cfg->getOption("background_color");
             hexvalue = hexvalue.substr(1,6);
-    	    image->Center(XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)), XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)),
-        			    hexvalue.c_str());
+            image->Center(XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)), XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)),
+                        hexvalue.c_str());
         }
         Pixmap p = image->createPixmap(Dpy, Scr, Root);
         XSetWindowBackgroundPixmap(Dpy, Root, p);
@@ -845,8 +1119,8 @@ string App::findValidRandomTheme(const string& set)
 
 
 void App::replaceVariables(string& input,
-			   const string& var,
-			   const string& value)
+               const string& var,
+               const string& value)
 {
     string::size_type pos = 0;
     int len = var.size();
@@ -857,19 +1131,19 @@ void App::replaceVariables(string& input,
 
 
 void App::CreateServerAuth() {
-	/* create mit cookie */
-	int i, r;
-	int hexcount = 0;
+    /* create mit cookie */
+    int i, r;
+    int hexcount = 0;
         string authfile;
-	string cmd;
-	char *digits = "0123456789abcdef";
+    string cmd;
+    char *digits = "0123456789abcdef";
         srand( time(NULL) );
-	for ( i = 0; i < 31; i++ ) {
-		r = rand()%16;
+    for ( i = 0; i < 31; i++ ) {
+        r = rand()%16;
                 mcookie[i] = digits[r];
                 if (r>9)
                         hexcount++;
-	}
+    }
         /* MIT-COOKIE: even occurrences of digits and hex digits */
         if ((hexcount%2) == 0) {
                 r = rand()%10;
@@ -877,12 +1151,12 @@ void App::CreateServerAuth() {
                 r = rand()%5+10;
         }
         mcookie[31] = digits[r];
-	/* reinitialize auth file */
-	authfile = cfg->getOption("authfile");
-	remove(authfile.c_str());
-        putenv(StrConcat("XAUTHORITY=", authfile.c_str()));
-        cmd = cfg->getOption("xauth_path") + " -q -f " + authfile + " add :0 . " + mcookie;
-        system(cmd.c_str());
+    /* reinitialize auth file */
+    authfile = cfg->getOption("authfile");
+    remove(authfile.c_str());
+    putenv(StrConcat("XAUTHORITY=", authfile.c_str()));
+    cmd = cfg->getOption("xauth_path") + " -q -f " + authfile + " add :0 . " + mcookie;
+    system(cmd.c_str());
 }
 
 char* App::StrConcat(const char* str1, const char* str2) {
@@ -895,8 +1169,8 @@ char* App::StrConcat(const char* str1, const char* str2) {
 void App::UpdatePid() {
     std::ofstream lockfile(cfg->getOption("lockfile").c_str(), ios_base::out);
     if (!lockfile) {
-	    cerr << APPNAME << ": Could not update lock file: " << cfg->getOption("lockfile").c_str() << std::endl;
-	    exit(ERR_EXIT);
+        cerr << APPNAME << ": Could not update lock file: " << cfg->getOption("lockfile").c_str() << std::endl;
+        exit(ERR_EXIT);
     }
     lockfile << getpid() << std::endl;
     lockfile.close();

+ 11 - 1
app.h

@@ -24,6 +24,10 @@
 #include "cfg.h"
 #include "image.h"
 
+#ifdef USE_PAM
+#include "PAM.h"
+#endif
+
 class App {
 public:
     App(int argc, char** argv);
@@ -31,7 +35,7 @@ public:
     void Run();
     int GetServerPID();
     void StopServer();
-	
+
     // Lock functions
     void GetLock();
     void RemoveLock();
@@ -52,6 +56,8 @@ private:
     void CreateServerAuth();
     char* StrConcat(const char* str1, const char* str2);
     void UpdatePid();
+
+    bool AuthenticateUser(void);
  
     static std::string findValidRandomTheme(const std::string& set);
     static void replaceVariables(std::string& input,
@@ -71,6 +77,10 @@ private:
     int ServerPID;
     char* DisplayName;
 
+#ifdef USE_PAM
+	PAM::Authenticator pam;
+#endif
+
     // Options
     char* DispName;
 

+ 48 - 6
cfg.cpp

@@ -13,6 +13,10 @@
 #include <iostream>
 #include <unistd.h>
 
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+
 #include "cfg.h"
 
 using namespace std;
@@ -36,7 +40,7 @@ Cfg::Cfg()
     options.insert(option("sessionstart_cmd",""));
     options.insert(option("sessionstop_cmd",""));
     options.insert(option("console_cmd","/usr/X11R6/bin/xterm -C -fg white -bg black +sb -g %dx%d+%d+%d -fn %dx%d -T ""Console login"" -e /bin/sh -c ""/bin/cat /etc/issue; exec /bin/login"""));
-    options.insert(option("screenshot_cmd","import -window root /login.app.png"));
+    options.insert(option("screenshot_cmd","import -window root /slim.png"));
     options.insert(option("welcome_msg","Welcome to %host"));
     options.insert(option("default_user",""));
     options.insert(option("current_theme","default"));
@@ -46,6 +50,7 @@ Cfg::Cfg()
     options.insert(option("shutdown_msg","The system is halting..."));
     options.insert(option("reboot_msg","The system is rebooting..."));
     options.insert(option("sessions","wmaker,blackbox,icewm"));
+    options.insert(option("sessiondir",""));
     options.insert(option("hidecursor","false"));
 
     // Theme stuff
@@ -107,7 +112,7 @@ Cfg::Cfg()
 }
 
 Cfg::~Cfg() {
-	options.clear();
+    options.clear();
 }
 /*
  * Creates the Cfg object and parses
@@ -131,7 +136,9 @@ bool Cfg::readConf(string configfile) {
             }
         }
         cfgfile.close();
-        split(sessions, getOption("sessions"), ',', false);
+
+        fillSessionList();
+
         return true;
     } else {
         error = "Cannot read configuration file: " + configfile;
@@ -229,20 +236,55 @@ void Cfg::split(vector<string>& v, const string& str, char c, bool useEmpty) {
     while (true) {
         string::const_iterator begin = s;
         while (*s != c && s != str.end()) { ++s; }
-	tmp = string(begin, s);
-	if (useEmpty || tmp.size() > 0)
+    tmp = string(begin, s);
+    if (useEmpty || tmp.size() > 0)
             v.push_back(tmp);
         if (s == str.end()) {
             break;
         }
         if (++s == str.end()) {
-	    if (useEmpty)
+        if (useEmpty)
                 v.push_back("");
             break;
         }
     }
 }
 
+void Cfg::fillSessionList(){
+    string strSessionList = getOption("sessions");
+    string strSessionDir  = getOption("sessiondir");
+
+    sessions.clear();
+
+    if( !strSessionDir.empty() ) {
+        DIR *pDir = opendir(strSessionDir.c_str());
+
+        if (pDir != NULL) {
+            struct dirent *pDirent = NULL;
+
+            while ((pDirent = readdir(pDir)) != NULL) {
+                string strFile(strSessionDir);
+                strFile += "/";
+                strFile += pDirent->d_name;
+
+                struct stat oFileStat;
+
+                if (stat(strFile.c_str( ), &oFileStat) == 0){
+                    if (S_ISREG(oFileStat.st_mode) && 
+                        access(strFile.c_str(), R_OK | X_OK) == 0){
+                        sessions.push_back(string(pDirent->d_name));
+                    }
+                }
+            }
+            closedir(pDir);
+        }
+    } 
+
+    if (sessions.empty()){
+        split(sessions, strSessionList, ',', false);
+    }
+}
+
 string Cfg::nextSession(string current) {
     if (sessions.size() < 1)
         return current;

+ 4 - 1
cfg.h

@@ -38,11 +38,14 @@ public:
     static int absolutepos(const string& position, int max, int width);
     static int string2int(const char* string, bool* ok = 0);
     static void split(vector<string>& v, const string& str, 
-		      char c, bool useEmpty=true);
+                      char c, bool useEmpty=true);
     static string Trim(const string& s);
 
     string nextSession(string current);
 
+private:
+    void fillSessionList();
+
 private:
     map<string,string> options;
     vector<string> sessions;

+ 7 - 16
const.h

@@ -13,9 +13,9 @@
 #define _CONST_H_
 
 
-#define APPNAME		"slim"
+#define APPNAME    "slim"
 
-#define DISPLAY		":0.0"
+#define DISPLAY    ":0.0"
 
 #define CONSOLE_STR     "console"
 #define HALT_STR        "halt"
@@ -23,23 +23,14 @@
 #define EXIT_STR        "exit"
 #define SUSPEND_STR     "suspend"
 
-#define HIDE		0
-#define SHOW		1
-
-#define WAIT		0
-#define LOGIN		1
-#define FAIL		2
-#define CONSOLE		3
-#define REBOOT		4
-#define HALT		5
-#define EXIT		6
-#define SUSPEND		7
+#define HIDE        0
+#define SHOW        1
 
 #define GET_NAME    0
-#define GET_PASSWD	1
+#define GET_PASSWD  1
 
-#define	OK_EXIT		0
-#define	ERR_EXIT	1
+#define OK_EXIT     0
+#define ERR_EXIT    1
 
 // duration for showing error messages, as "login command failed", in seconds
 #define ERROR_DURATION  5

+ 9 - 9
image.cpp

@@ -369,7 +369,7 @@ void Image::Crop(const int x, const int y, const int w, const int h) {
                     new_rgb[3*ipos + k] = static_cast<unsigned char> (rgb_data[3*opos + k]);
                 }
                 if (png_alpha != NULL)
-					new_alpha[ipos] = static_cast<unsigned char> (png_alpha[opos]);
+                    new_alpha[ipos] = static_cast<unsigned char> (png_alpha[opos]);
                 ipos++;
             }
             opos++;
@@ -380,7 +380,7 @@ void Image::Crop(const int x, const int y, const int w, const int h) {
     free(png_alpha);
     rgb_data = new_rgb;
     if (png_alpha != NULL)
-	    png_alpha = new_alpha;
+        png_alpha = new_alpha;
     width = w;
     height = h;
     area = w * h;
@@ -407,11 +407,11 @@ void Image::Center(const int w, const int h, const char *hex) {
     int y = (h - height) / 2;
     
     if (x<0) {
-    	Crop((width - w)/2,0,w,height);
+        Crop((width - w)/2,0,w,height);
         x = 0;
     }
     if (y<0) {
-    	Crop(0,(height - h)/2,width,h);
+        Crop(0,(height - h)/2,width,h);
         y = 0;
     }
     int x2 = x + width;
@@ -428,13 +428,13 @@ void Image::Center(const int w, const int h, const char *hex) {
         new_rgb[3*i+2] = b;
     }
 
-	if (png_alpha != NULL) {
+    if (png_alpha != NULL) {
         for (int j = 0; j < h; j++) {
             for (int i = 0; i < w; i++) {
                 if (j>=y && i>=x && j<y2 && i<x2) {
-            	    ipos = j*w + i;
+                    ipos = j*w + i;
                     for (int k = 0; k < 3; k++) {
-	                    tmp = rgb_data[3*opos + k]*png_alpha[opos]/255.0
+                        tmp = rgb_data[3*opos + k]*png_alpha[opos]/255.0
                               + new_rgb[k]*(1-png_alpha[opos]/255.0);
                         new_rgb[3*ipos + k] = static_cast<unsigned char> (tmp);
                     }
@@ -447,9 +447,9 @@ void Image::Center(const int w, const int h, const char *hex) {
         for (int j = 0; j < h; j++) {
             for (int i = 0; i < w; i++) {
                 if (j>=y && i>=x && j<y2 && i<x2) {
-            	    ipos = j*w + i;
+                    ipos = j*w + i;
                     for (int k = 0; k < 3; k++) {
-	                    tmp = rgb_data[3*opos + k];
+                        tmp = rgb_data[3*opos + k];
                         new_rgb[3*ipos + k] = static_cast<unsigned char> (tmp);
                     }
                     opos++;

+ 0 - 249
input.cpp

@@ -1,249 +0,0 @@
-/* SLiM - Simple Login Manager
-   Copyright (C) 1997, 1998 Per Liden
-   Copyright (C) 2004-06 Simone Rota <sip@varlock.com>
-   Copyright (C) 2004-06 Johannes Winkelmann <jw@tks6.net>
-
-   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.
-*/
-
-#include "input.h"
-#include <cstdlib>
-
-Input::Input(Cfg* c) {
-    NameBuffer[0] = '\0';
-    PasswdBuffer[0] = '\0';
-    HiddenPasswdBuffer[0] = '\0';
-    Field = GET_NAME;
-    Action = WAIT;
-    cfg = c;
-}
-
-
-Input::~Input() {
-    // Does nothing
-}
-
-
-char Input::Key(char ascii, KeySym keysym, bool singleInputMode) {
-    char tmp = 0;
-
-    // Take a screenshot
-    if(keysym == XK_F11) {
-        system(cfg->getOption("screenshot_cmd").c_str());
-    }
-
-    if  (!singleInputMode && (keysym == XK_Tab || keysym == XK_ISO_Left_Tab)) {
-        if (Field == GET_NAME) {
-            // Move to next field
-            Field = GET_PASSWD;
-        } else {
-            Field = GET_NAME;
-        }
-    } else if(keysym == XK_Return || keysym == XK_KP_Enter) {
-        if(!strcmp(NameBuffer, ""))
-            return tmp;
-
-        // Check for special command (console, halt, reboot, exit)
-        int special = SpecialWanted();
-
-        if(Field == GET_NAME) {
-            // Move to next field
-            Field = GET_PASSWD;
-
-            // Check for special command (console, exit)
-            if(special == CONSOLE || special == EXIT)
-                Action = special;
-        } else {
-            // Check for special command (halt, reboot)
-            if(special == REBOOT || special == HALT || special == SUSPEND)
-                Action = SpecialCorrect(special);
-
-            // Regular login
-            else {
-                if(Correct())
-                    Action = LOGIN;
-                else
-                    Action = FAIL;
-            }
-        }
-    } else if(keysym == XK_Delete || keysym == XK_BackSpace)
-        tmp = DeleteLast();
-    else if(isprint(ascii))
-        if(keysym < XK_Shift_L || keysym > XK_Hyper_R)
-            Add(ascii);
-
-    return tmp;
-}
-
-
-int Input::GetAction() {
-    return Action;
-}
-
-
-int Input::GetField() {
-    return Field;
-}
-
-
-char* Input::GetName() {
-    return NameBuffer;
-}
-
-
-char* Input::GetHiddenPasswd() {
-    return HiddenPasswdBuffer;
-}
-
-
-struct passwd* Input::GetPasswdStruct() {
-    struct passwd* pw = getpwnam(NameBuffer);
-    endpwent();
-    if (pw->pw_shell[0] == '\0') {
-        setusershell();
-        strcpy(pw->pw_shell, getusershell());
-        endusershell();
-    }
-    return pw;
-}
-
-
-void Input::Add(char ascii) {
-    char tmp[2];
-    tmp[0] = ascii;
-    tmp[1] = '\0';
-
-    switch(Field) {
-    case GET_NAME:
-        if(strlen(NameBuffer) < INPUT_MAXLENGTH_NAME-1)
-            strcat(NameBuffer, tmp);
-        break;
-    case GET_PASSWD:
-        if(strlen(PasswdBuffer) < INPUT_MAXLENGTH_PASSWD-1) {
-            strcat(PasswdBuffer, tmp);
-            tmp[0] = '*';
-            strcat(HiddenPasswdBuffer, tmp);
-        }
-        break;
-    }
-}
-
-
-char Input::DeleteLast() {
-    char tmp = 0;
-    int len = 0;
-
-    switch(Field) {
-    case GET_NAME:
-        len = strlen(NameBuffer) - 1;
-        tmp = NameBuffer[len];
-        NameBuffer[len] = '\0';
-        break;
-    case GET_PASSWD:
-        len = strlen(PasswdBuffer) - 1;
-        tmp = '*';
-        PasswdBuffer[len] = '\0';
-        HiddenPasswdBuffer[len] = '\0';
-        break;
-    }
-
-    return tmp;
-}
-
-
-void Input::Reset() {
-    ResetName();
-    ResetPassword();
-    Field = GET_NAME;
-    Action = WAIT;
-}
-
-void Input::ResetName() {
-    NameBuffer[0] = '\0';
-    Action = WAIT;
-}
-
-void Input::ResetPassword() {
-    PasswdBuffer[0] = '\0';
-    HiddenPasswdBuffer[0] = '\0';
-    Action = WAIT;
-}
-
-// used for automatically fill name at startup
-void Input::SetName(string name) {
-    NameBuffer[0] = '\0';
-    if(name.size() < INPUT_MAXLENGTH_NAME) {
-        strcat(NameBuffer, name.c_str());
-    }
-    Field = GET_PASSWD;
-    Action = WAIT;
-}
-
-int Input::Correct() {
-    char *unencrypted, *encrypted, *correct;
-    struct passwd *pw;
-
-    pw = getpwnam(NameBuffer);
-    endpwent();
-    if(pw == 0)
-        return 0;
-
-#ifdef HAVE_SHADOW
-    struct spwd *sp = getspnam(pw->pw_name);    
-    endspent();
-    if(sp)
-	correct = sp->sp_pwdp;
-    else
-#endif
-	correct = pw->pw_passwd;
-
-    if(correct == 0 || correct[0] == '\0')
-        return 1;
-
-    unencrypted = PasswdBuffer;
-    encrypted = crypt(unencrypted, correct);
-    memset(unencrypted, 0, strlen (unencrypted));
-    return (strcmp(encrypted, correct) == 0);
-}
-
-
-int Input::SpecialWanted() {
-    int result;
-
-    if(!strcmp(NameBuffer, CONSOLE_STR))
-        result = CONSOLE;
-    else if(!strcmp(NameBuffer, HALT_STR))
-        result = HALT;
-    else if(!strcmp(NameBuffer, REBOOT_STR))
-        result = REBOOT;
-    else if(!strcmp(NameBuffer, SUSPEND_STR))
-        result = SUSPEND;
-    else if(!strcmp(NameBuffer, EXIT_STR))
-        result = EXIT;
-    else
-        result = 0;
-
-    return result;
-}
-
-
-int Input::SpecialCorrect(int special) {
-    int result, c;
-    char tmp[INPUT_MAXLENGTH_NAME];
-
-    strcpy(tmp, NameBuffer);
-    strcpy(NameBuffer, "root");
-    c = Correct();
-    strcpy(NameBuffer, tmp);
-
-    if(c)
-        result = special;
-    else
-        result = FAIL;
-
-    return result;
-}
-

+ 0 - 63
input.h

@@ -1,63 +0,0 @@
-/* SLiM - Simple Login Manager
-   Copyright (C) 1997, 1998 Per Liden
-   Copyright (C) 2004-06 Simone Rota <sip@varlock.com>
-   Copyright (C) 2004-06 Johannes Winkelmann <jw@tks6.net>
-
-   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.
-*/
-
-#ifndef _INPUT_H_
-#define _INPUT_H_
-
-#include <X11/Xlib.h>
-#include <X11/keysym.h>
-#include <X11/Xft/Xft.h>
-#include <pwd.h>
-#include <unistd.h>
-#include <string>
-#include <ctype.h>
-#include <iostream>
-
-#include "const.h"
-#include "cfg.h"
-
-#ifdef HAVE_SHADOW
-#include <shadow.h>
-#endif
-
-class Input {
-public:
-    Input(Cfg* c);
-    ~Input();
-    char Key(char ascii, KeySym keysym, bool singleInputMode);
-    int GetAction();
-    int GetField();
-    char* GetName();
-    char* GetHiddenPasswd();
-    struct passwd* GetPasswdStruct();
-    void Reset();
-    void ResetName();
-    void ResetPassword();
-    void SetName(string name);
-
-private:
-    void Add(char ascii);
-    char DeleteLast();
-    int Correct();
-    int SpecialWanted();
-    int SpecialCorrect(int special);
-    Cfg* cfg;
-
-    char NameBuffer[INPUT_MAXLENGTH_NAME];
-    char PasswdBuffer[INPUT_MAXLENGTH_PASSWD];
-    char HiddenPasswdBuffer[INPUT_MAXLENGTH_PASSWD];
-    int Action;
-    int Field;
-
-};
-
-#endif
-

+ 2 - 2
jpeg.c

@@ -46,7 +46,7 @@ read_jpeg(const char *filename, int *width, int *height, unsigned char **rgb)
     if (rgb[0] == NULL)
     {
         fprintf(stderr, "Can't allocate memory for JPEG file.\n");
-	fclose(infile);
+    fclose(infile);
         return(0);
     }
 
@@ -65,7 +65,7 @@ read_jpeg(const char *filename, int *width, int *height, unsigned char **rgb)
         if (ptr == NULL)
         {
             fprintf(stderr, "Can't allocate memory for JPEG file.\n");
-	    fclose(infile);
+        fclose(infile);
             return(0);
         }
 

+ 7 - 7
numlock.cpp

@@ -24,19 +24,19 @@ int NumLock::xkb_init(Display* dpy) {
     int xkb_lmin = XkbMinorVersion;
     return XkbLibraryVersion( &xkb_lmaj, &xkb_lmin )
         && XkbQueryExtension( dpy, &xkb_opcode, &xkb_event, &xkb_error,
-			       &xkb_lmaj, &xkb_lmin );
+                   &xkb_lmaj, &xkb_lmin );
 }
     
 unsigned int NumLock::xkb_mask_modifier( XkbDescPtr xkb, const char *name ) {
     int i;
     if( !xkb || !xkb->names )
-	return 0;
+    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;
+        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;

+ 215 - 150
panel.cpp

@@ -76,9 +76,10 @@ Panel::Panel(Display* dpy, int scr, Window root, Cfg* config,
         panelpng = themedir + "/panel.jpg";
         loaded = image->Read(panelpng.c_str());
         if (!loaded) {
-            cerr << APPNAME << ": could not load panel image for theme '"
-		 << basename((char*)themedir.c_str()) << "'"
-		 << endl;
+            cerr << APPNAME
+                 << ": could not load panel image for theme '"
+                 << basename((char*)themedir.c_str()) << "'"
+                 << endl;
             exit(ERR_EXIT);
         }
     }
@@ -92,9 +93,10 @@ Panel::Panel(Display* dpy, int scr, Window root, Cfg* config,
             panelpng = themedir + "/background.jpg";
             loaded = bg->Read(panelpng.c_str());
             if (!loaded){
-                cerr << APPNAME << ": could not load background image for theme '"
-		        << basename((char*)themedir.c_str()) << "'"
-		        << endl;
+                cerr << APPNAME
+                     << ": could not load background image for theme '"
+                     << basename((char*)themedir.c_str()) << "'"
+                     << endl;
                 exit(ERR_EXIT);
             }
         }
@@ -104,15 +106,17 @@ Panel::Panel(Display* dpy, int scr, Window root, Cfg* config,
     } else if (bgstyle == "tile") {
         bg->Tile(XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)), XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)));
     } else if (bgstyle == "center") {
- 	    string hexvalue = cfg->getOption("background_color");
+        string hexvalue = cfg->getOption("background_color");
         hexvalue = hexvalue.substr(1,6);
-  	    bg->Center(XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)), XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)),
-       			    hexvalue.c_str());
+        bg->Center(XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)),
+                   XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)),
+                   hexvalue.c_str());
     } else { // plain color or error
-	    string hexvalue = cfg->getOption("background_color");
+        string hexvalue = cfg->getOption("background_color");
         hexvalue = hexvalue.substr(1,6);
-  	    bg->Center(XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)), XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)),
-    			    hexvalue.c_str());
+        bg->Center(XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)),
+                   XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)),
+                   hexvalue.c_str());
     }
 
     string cfgX = cfg->getOption("input_panel_x");
@@ -128,9 +132,6 @@ Panel::Panel(Display* dpy, int scr, Window root, Cfg* config,
     // Read (and substitute vars in) the welcome message
     welcome_message = cfg->getWelcomeMessage();
     intro_message = cfg->getOption("intro_msg");
-
-    // Init In
-    In = new Input(cfg);
 }
 
 Panel::~Panel() {
@@ -144,7 +145,6 @@ Panel::~Panel() {
     XftFontClose(Dpy, introfont);
     XftFontClose(Dpy, welcomefont);
     XftFontClose(Dpy, enterfont);
-    delete In;
     delete image;
 
 }
@@ -182,7 +182,7 @@ void Panel::ClosePanel() {
 
 void Panel::ClearPanel() {
     session = "";
-    In->Reset();
+    Reset();
     XClearWindow(Dpy, Root);
     XClearWindow(Dpy, Win);
     Cursor(SHOW);
@@ -190,13 +190,13 @@ void Panel::ClearPanel() {
     XFlush(Dpy);
 }
 
-void Panel::Message(const char* text) {
+void Panel::Message(const string& text) {
     string cfgX, cfgY;
     XGlyphInfo extents;
     XftDraw *draw = XftDrawCreate(Dpy, Root,
                                   DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr));
-    XftTextExtents8(Dpy, msgfont, (XftChar8*)text,
-                    strlen(text), &extents);
+    XftTextExtents8(Dpy, msgfont, reinterpret_cast<const XftChar8*>(text.c_str()),
+                    text.length(), &extents);
     cfgX = cfg->getOption("msg_x");
     cfgY = cfg->getOption("msg_y");
     int shadowXOffset =
@@ -208,14 +208,14 @@ void Panel::Message(const char* text) {
     int msg_y = Cfg::absolutepos(cfgY, XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)), extents.height);
 
     SlimDrawString8 (draw, &msgcolor, msgfont, msg_x, msg_y,
-                     (XftChar8*)text, strlen(text),
+                     text,
                      &msgshadowcolor,
                      shadowXOffset, shadowYOffset);
     XFlush(Dpy);
     XftDrawDestroy(draw);
 }
 
-void Panel::Error(const char* text) {
+void Panel::Error(const string& text) {
     ClosePanel();
     Message(text);
     sleep(ERROR_DURATION);
@@ -224,10 +224,6 @@ void Panel::Error(const char* text) {
 }
 
 
-Input* Panel::GetInput() {
-    return In;
-}
-
 unsigned long Panel::GetColor(const char* colorname) {
     XColor color;
     XWindowAttributes attributes;
@@ -244,31 +240,24 @@ unsigned long Panel::GetColor(const char* colorname) {
 }
 
 void Panel::Cursor(int visible) {
-    char* text;
+    const char* text;
     int xx, yy, y2, cheight;
     char* txth = "Wj"; // used to get cursor height
 
-    switch(In->GetField()) {
-        case GET_PASSWD:
-            text = In->GetHiddenPasswd();
+    switch(field) {
+        case Get_Passwd:
+            text = HiddenPasswdBuffer.c_str();
             xx = input_pass_x;
             yy = input_pass_y;
             break;
 
-        case GET_NAME:
-            text = In->GetName();
+        case Get_Name:
+            text = NameBuffer.c_str();
             xx = input_name_x;
             yy = input_name_y;
             break;
-
-		default: /* Origin & NULL string as default values. */
-			text = (char *)NULL;
-			xx = (int)0;
-			yy = (int)0;
-			break;
     }
 
-
     XGlyphInfo extents;
     XftTextExtents8(Dpy, font, (XftChar8*)txth, strlen(txth), &extents);
     cheight = extents.height;
@@ -288,49 +277,53 @@ void Panel::Cursor(int visible) {
     }
 }
 
-int Panel::EventHandler(XEvent* event) {
-    Action = WAIT;
-
-    switch(event->type) {
-    case Expose:
-        OnExpose(event);
-        break;
+void Panel::EventHandler(const Panel::FieldType& curfield) {
+    XEvent event;
+    field=curfield;
+    bool loop = true;
+    OnExpose();
+    while(loop) {
+        XNextEvent(Dpy, &event);
+        switch(event.type) {
+            case Expose:
+                OnExpose();
+                break;
 
-    case KeyPress:
-        OnKeyPress(event);
-        break;
+            case KeyPress:
+                loop=OnKeyPress(event);
+                break;
+        }
     }
 
-    return Action;
+    return;
 }
 
-void Panel::OnExpose(XEvent* event) {
-    char* name = In->GetName();
-    char* passwd = In->GetHiddenPasswd();
+void Panel::OnExpose(void) {
     XftDraw *draw = XftDrawCreate(Dpy, Win,
                         DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr));
+    XClearWindow(Dpy, Win);
     if (input_pass_x != input_name_x || input_pass_y != input_name_y){
         SlimDrawString8 (draw, &inputcolor, font, input_name_x, input_name_y,
-                         (XftChar8*)name, strlen(name),
+                         NameBuffer,
                          &inputshadowcolor,
                          inputShadowXOffset, inputShadowYOffset);
         SlimDrawString8 (draw, &inputcolor, font, input_pass_x, input_pass_y,
-                         (XftChar8*)passwd, strlen(passwd),
+                         HiddenPasswdBuffer,
                          &inputshadowcolor,
                          inputShadowXOffset, inputShadowYOffset);
     } else { //single input mode
-        switch(In->GetField()) {
-            case GET_PASSWD:
+        switch(field) {
+            case Get_Passwd:
                 SlimDrawString8 (draw, &inputcolor, font,
                                  input_pass_x, input_pass_y,
-                                 (XftChar8*)passwd, strlen(passwd),
+                                 HiddenPasswdBuffer,
                                  &inputshadowcolor,
                                  inputShadowXOffset, inputShadowYOffset);
                 break;
-            case GET_NAME:
+            case Get_Name:
                 SlimDrawString8 (draw, &inputcolor, font,
                                  input_name_x, input_name_y,
-                                 (XftChar8*)name, strlen(name),
+                                 NameBuffer,
                                  &inputshadowcolor,
                                  inputShadowXOffset, inputShadowYOffset);
                 break;
@@ -342,108 +335,153 @@ void Panel::OnExpose(XEvent* event) {
     ShowText();
 }
 
-void Panel::OnKeyPress(XEvent* event) {
-    char del;
-    char buffer;
+bool Panel::OnKeyPress(XEvent& event) {
+    char ascii;
     KeySym keysym;
     XComposeStatus compstatus;
     int xx;
     int yy;
-    char* text;
+    string text;
+    string formerString = "";
+    
+    XLookupString(&event.xkey, &ascii, 1, &keysym, &compstatus);
+    switch(keysym){
+        case XK_F1:
+            SwitchSession();
+            return true;
+
+        case XK_F11:
+            // Take a screenshot
+            system(cfg->getOption("screenshot_cmd").c_str());
+            return true;
+
+        case XK_Return:
+        case XK_KP_Enter:
+            if (field==Get_Name){
+                // Don't allow an empty username
+                if (NameBuffer.empty()) return true;
+
+                if (NameBuffer==CONSOLE_STR){
+                    action = Console;
+                } else if (NameBuffer==HALT_STR){
+                    action = Halt;
+                } else if (NameBuffer==REBOOT_STR){
+                    action = Reboot;
+                } else if (NameBuffer==SUSPEND_STR){
+                    action = Suspend;
+                } else if (NameBuffer==EXIT_STR){
+                    action = Exit;
+                } else{
+                    action = Login;
+                }
+            };
+            return false;
+        default:
+            break;
+    };
 
-    bool singleInputMode =
-        input_name_x == input_pass_x &&
-        input_name_y == input_pass_y;
     Cursor(HIDE);
-    XLookupString(&event->xkey, &buffer, 1, &keysym, &compstatus);
-    del = In->Key(buffer, keysym, singleInputMode);
-    Action = In->GetAction();
+    switch(keysym){
+        case XK_Delete:
+        case XK_BackSpace:
+            switch(field) {
+                case GET_NAME:
+                    if (! NameBuffer.empty()){
+                        formerString=NameBuffer;
+                        NameBuffer.erase(--NameBuffer.end());
+                    };
+                    break;
+                case GET_PASSWD:
+                    if (! PasswdBuffer.empty()){
+                        formerString=HiddenPasswdBuffer;
+                        PasswdBuffer.erase(--PasswdBuffer.end());
+                        HiddenPasswdBuffer.erase(--HiddenPasswdBuffer.end());
+                    };
+                    break;
+            };
+            break;
+
+        case XK_w:
+        case XK_u:
+            if (reinterpret_cast<XKeyEvent&>(event).state & ControlMask) {
+                switch(field) {
+                    case Get_Passwd:
+                        formerString = HiddenPasswdBuffer;
+                        HiddenPasswdBuffer.clear();
+                        PasswdBuffer.clear();
+                        break;
+
+                    case Get_Name:
+                        formerString = NameBuffer;
+                        NameBuffer.clear();
+                        break;
+                };
+                break;
+            }
+            // Deliberate fall-through
+        
+        default:
+            if (isprint(ascii) && (keysym < XK_Shift_L || keysym > XK_Hyper_R)){
+                switch(field) {
+                    case GET_NAME:
+                        formerString=NameBuffer;
+                        if (NameBuffer.length() < INPUT_MAXLENGTH_NAME-1){
+                            NameBuffer.append(&ascii,1);
+                        };
+                        break;
+                    case GET_PASSWD:
+                        formerString=HiddenPasswdBuffer;
+                        if (PasswdBuffer.length() < INPUT_MAXLENGTH_NAME-1){
+                            PasswdBuffer.append(&ascii,1);
+                            HiddenPasswdBuffer.append("*");
+                        };
+                    break;
+                };
+            };
+            break;
+    };
 
     XGlyphInfo extents;
     XftDraw *draw = XftDrawCreate(Dpy, Win,
                                   DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr));
 
-    if (keysym == XK_F1) {
-        SwitchSession();
-    }
-
-    bool clearField = false;
-    string formerString = "";
-    if ((((XKeyEvent*)event)->state & ControlMask)) {
-        if (keysym == XK_w || keysym == XK_u) {
-            clearField = true;
-        }
-    }
-
-    switch(In->GetField()) {
-        case GET_PASSWD:
-            if (strlen(In->GetHiddenPasswd()) == 0){
-                // clear name and welcome label if we just entered the
-                // password field
-                if (singleInputMode) {
-                    xx = input_name_x;
-                    yy = input_name_y;
-                    text = In->GetName();
-                    XftTextExtents8(Dpy, font, (XftChar8*)text,
-                                    strlen(text), &extents);
-                    XClearWindow(Dpy, Win);
-                    ShowText();
-                }
-            }
-
-            if (clearField) {
-                formerString = In->GetHiddenPasswd();
-                In->ResetPassword();
-            }
-            text = In->GetHiddenPasswd();
-            xx = input_pass_x;
-            yy = input_pass_y;
-            break;
-
-        case GET_NAME:
-            if (clearField) {
-                formerString = In->GetName();
-                In->ResetName();
-            }
-            text = In->GetName();
+   switch(field) {
+        case Get_Name:
+            text = NameBuffer;
             xx = input_name_x;
             yy = input_name_y;
             break;
 
-		default: /* Origin & NULL string as default values. */
-			text = (char *)NULL;
-			xx = (int)0;
-			yy = (int)0;
-			break;
+        case Get_Passwd:
+            text = HiddenPasswdBuffer;
+            xx = input_pass_x;
+            yy = input_pass_y;
+            break;
     }
 
-    char* txth = "Wj"; // get proper maximum height ?
-    XftTextExtents8(Dpy, font, (XftChar8*)txth, strlen(txth), &extents);
-    int maxHeight = extents.height;
+    if (!formerString.empty()){
+        char* txth = "Wj"; // get proper maximum height ?
+        XftTextExtents8(Dpy, font, reinterpret_cast<const XftChar8*>(txth), strlen(txth), &extents);
+        int maxHeight = extents.height;
 
-    string tmp = "";
-    if (clearField) {
-        tmp = formerString;
-    } else {
-        tmp = text;
-        tmp = tmp + del;
-    }
-    XftTextExtents8(Dpy, font, (XftChar8*)tmp.c_str(),
-                    strlen(tmp.c_str()), &extents);
-    int maxLength = extents.width;
+        XftTextExtents8(Dpy, font, reinterpret_cast<const XftChar8*>(formerString.c_str()),
+                        formerString.length(), &extents);
+        int maxLength = extents.width;
 
-    XClearArea(Dpy, Win, xx-3, yy-maxHeight-3,
-               maxLength+6, maxHeight+6, false);
+        XClearArea(Dpy, Win, xx-3, yy-maxHeight-3,
+                   maxLength+6, maxHeight+6, false);
+    }
 
-    if (!clearField) {
+    if (!text.empty()) {
         SlimDrawString8 (draw, &inputcolor, font, xx, yy,
-                         (XftChar8*)text, strlen(text),
+                         text,
                          &inputshadowcolor,
                          inputShadowXOffset, inputShadowYOffset);
     }
 
     XftDrawDestroy (draw);
     Cursor(SHOW);
+    return true;
 }
 
 // Draw welcome and "enter username" message
@@ -471,14 +509,13 @@ void Panel::ShowText(){
     if (welcome_x >= 0 && welcome_y >= 0) {
         SlimDrawString8 (draw, &welcomecolor, welcomefont,
                          welcome_x, welcome_y,
-                         (XftChar8*)welcome_message.c_str(),
-                         strlen(welcome_message.c_str()),
+                         welcome_message,
                          &welcomeshadowcolor, shadowXOffset, shadowYOffset);
     }
 
     /* Enter username-password message */
     string msg;
-    if (!singleInputMode|| In->GetField() == GET_PASSWD ) {
+    if (!singleInputMode|| field == Get_Passwd ) {
         msg = cfg->getOption("password_msg");
         XftTextExtents8(Dpy, enterfont, (XftChar8*)msg.c_str(),
                         strlen(msg.c_str()), &extents);
@@ -492,11 +529,10 @@ void Panel::ShowText(){
         password_y = Cfg::absolutepos(cfgY, image->Height(), extents.height);
         if (password_x >= 0 && password_y >= 0){
             SlimDrawString8 (draw, &entercolor, enterfont, password_x, password_y,
-                             (XftChar8*)msg.c_str(), strlen(msg.c_str()),
-                             &entershadowcolor, shadowXOffset, shadowYOffset);
+                             msg, &entershadowcolor, shadowXOffset, shadowYOffset);
         }
     }
-    if (!singleInputMode|| In->GetField() == GET_NAME ) {
+    if (!singleInputMode|| field == Get_Name ) {
         msg = cfg->getOption("username_msg");
         XftTextExtents8(Dpy, enterfont, (XftChar8*)msg.c_str(),
                         strlen(msg.c_str()), &extents);
@@ -510,8 +546,7 @@ void Panel::ShowText(){
         username_y = Cfg::absolutepos(cfgY, image->Height(), extents.height);
         if (username_x >= 0 && username_y >= 0){
             SlimDrawString8 (draw, &entercolor, enterfont, username_x, username_y,
-                             (XftChar8*)msg.c_str(), strlen(msg.c_str()),
-                             &entershadowcolor, shadowXOffset, shadowYOffset);
+                             msg, &entershadowcolor, shadowXOffset, shadowYOffset);
         }
     }
     XftDrawDestroy(draw);
@@ -533,12 +568,11 @@ void Panel::SwitchSession() {
 void Panel::ShowSession() {
     XClearWindow(Dpy, Root);
     string currsession = "Session: " + session;
-    char* text = (char*) currsession.c_str();
     XGlyphInfo extents;
     XftDraw *draw = XftDrawCreate(Dpy, Root,
                                   DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr));
-    XftTextExtents8(Dpy, msgfont, (XftChar8*)text,
-                    strlen(text), &extents);
+    XftTextExtents8(Dpy, msgfont, reinterpret_cast<const XftChar8*>(currsession.c_str()),
+                    currsession.length(), &extents);
     int msg_x = Cfg::absolutepos("50%", XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)), extents.width);
     int msg_y = XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)) - extents.height -100;
     int shadowXOffset =
@@ -547,7 +581,7 @@ void Panel::ShowSession() {
         Cfg::string2int(cfg->getOption("msg_shadow_yoffset").c_str());
 
     SlimDrawString8(draw, &msgcolor, msgfont, msg_x, msg_y,
-                    (XftChar8*)text, strlen(text),
+                    currsession, 
                     &msgshadowcolor,
                     shadowXOffset, shadowYOffset);
     XFlush(Dpy);
@@ -556,13 +590,44 @@ void Panel::ShowSession() {
 
 
 void Panel::SlimDrawString8(XftDraw *d, XftColor *color, XftFont *font,
-                            int x, int y, XftChar8 *string, int len,
+                            int x, int y, const string& str,
                             XftColor* shadowColor,
                             int xOffset, int yOffset)
 {
     if (xOffset && yOffset) {
         XftDrawString8(d, shadowColor, font, x+xOffset, y+yOffset,
-                       string, len);
+                       reinterpret_cast<const FcChar8*>(str.c_str()), str.length());
     }
-    XftDrawString8(d, color, font, x, y, string, len);
+    XftDrawString8(d, color, font, x, y, reinterpret_cast<const FcChar8*>(str.c_str()), str.length());
 }
+
+
+Panel::ActionType Panel::getAction(void) const{
+    return action;
+};
+
+
+void Panel::Reset(void){
+    ResetName();
+    ResetPasswd();
+};
+
+void Panel::ResetName(void){
+    NameBuffer.clear();
+};
+
+void Panel::ResetPasswd(void){
+    PasswdBuffer.clear();
+    HiddenPasswdBuffer.clear();
+};
+
+void Panel::SetName(const string& name){
+    NameBuffer=name;
+    return;
+};
+const string& Panel::GetName(void) const{
+    return NameBuffer;
+};
+const string& Panel::GetPasswd(void) const{
+    return PasswdBuffer;
+};

+ 41 - 16
panel.h

@@ -13,51 +13,73 @@
 #define _PANEL_H_
 
 #include <X11/Xlib.h>
+#include <X11/keysym.h>
+#include <X11/Xft/Xft.h>
 #include <X11/cursorfont.h>
 #include <X11/Xmu/WinUtil.h>
 #include <sys/wait.h>
 #include <stdlib.h>
 #include <signal.h>
 #include <iostream>
+#include <string>
 
 #ifdef NEEDS_BASENAME
 #include <libgen.h>
 #endif
 
 #include "switchuser.h"
-#include "input.h"
 #include "const.h"
 #include "image.h"
 
 
 class Panel {
 public:
+    enum ActionType {
+        Login,
+        Console,
+        Reboot,
+        Halt,
+        Exit,
+        Suspend
+    };
+    enum FieldType {
+        Get_Name,
+        Get_Passwd
+    };
+
+
     Panel(Display* dpy, int scr, Window root, Cfg* config,
           const string& themed);
     ~Panel();
     void OpenPanel();
     void ClosePanel();
     void ClearPanel();
-    void Message(const char* text);
-    void Error(const char* text);
-    Input* GetInput();
-    int EventHandler(XEvent* event);
+    void Message(const string& text);
+    void Error(const string& text);
+    void EventHandler(const FieldType& curfield);
     string getSession();
-
+    ActionType getAction(void) const;
+
+    void Reset(void);
+    void ResetName(void);
+    void ResetPasswd(void);
+    void SetName(const string& name);
+    const string& GetName(void) const;
+    const string& GetPasswd(void) const;
 private:
     Panel();
     void Cursor(int visible);
     unsigned long GetColor(const char* colorname);
-    void OnExpose(XEvent* event);
-    void OnKeyPress(XEvent* event);
+    void OnExpose(void);
+    bool OnKeyPress(XEvent& event);
     void ShowText();
     void SwitchSession();
     void ShowSession();
 
-    void SlimDrawString8(XftDraw* d, XftColor* color, XftFont* font,
-                         int x, int y, XftChar8 *string, int len,
-                         XftColor* shadowColor,
-                         int xOffset, int yOffset);
+    void SlimDrawString8(XftDraw *d, XftColor *color, XftFont *font,
+                            int x, int y, const string& str,
+                            XftColor* shadowColor,
+                            int xOffset, int yOffset);
 
     Cfg* cfg;
 
@@ -82,7 +104,13 @@ private:
     XftFont* enterfont;
     XftColor entercolor;
     XftColor entershadowcolor;
-    int Action;
+    ActionType action;
+    FieldType field;
+    
+    // Username/Password
+    string NameBuffer;
+    string PasswdBuffer;
+    string HiddenPasswdBuffer;
 
     // Configuration
     int input_name_x;
@@ -110,9 +138,6 @@ private:
     // Pixmap data
     Pixmap PanelPixmap;
 
-    // Name/Passwd handler
-    Input* In;
-
     Image* image;
 
     // For thesting themes

+ 27 - 27
png.c

@@ -25,7 +25,7 @@
 
 int
 read_png(const char *filename, int *width, int *height, unsigned char **rgb, 
-	 unsigned char **alpha)
+     unsigned char **alpha)
 {
     FILE *infile = fopen(filename, "rb");
 
@@ -74,14 +74,14 @@ read_png(const char *filename, int *width, int *height, unsigned char **rgb,
     *height = (int) h;
     
     if (color_type == PNG_COLOR_TYPE_RGB_ALPHA
-	|| color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+    || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
     {
-	alpha[0] = malloc(*width * *height);
-	if (alpha[0] == NULL)
-	{
-	    fprintf(stderr, "Can't allocate memory for alpha channel in PNG file.\n");
-	    return(0); 
-	}
+    alpha[0] = malloc(*width * *height);
+    if (alpha[0] == NULL)
+    {
+        fprintf(stderr, "Can't allocate memory for alpha channel in PNG file.\n");
+        return(0); 
+    }
     }
 
     /* Change a paletted/grayscale image to RGB */
@@ -127,28 +127,28 @@ read_png(const char *filename, int *width, int *height, unsigned char **rgb,
 
     if (alpha[0] == NULL)
     {
-	ptr = rgb[0];
-	for (i = 0; i < *height; i++)
-	{
-	    memcpy(ptr, row_pointers[i], 3 * *width);
-	    ptr += 3 * *width;
-	}
+    ptr = rgb[0];
+    for (i = 0; i < *height; i++)
+    {
+        memcpy(ptr, row_pointers[i], 3 * *width);
+        ptr += 3 * *width;
+    }
     }
     else
     {
-	int j;
-	ptr = rgb[0];
-	for (i = 0; i < *height; i++)
-	{
-	    int ipos = 0;
-	    for (j = 0; j < *width; j++)
-	    {
-		*ptr++ = row_pointers[i][ipos++];
-		*ptr++ = row_pointers[i][ipos++];
-		*ptr++ = row_pointers[i][ipos++];
-		alpha[0][i * *width + j] = row_pointers[i][ipos++];
-	    }
-	}
+    int j;
+    ptr = rgb[0];
+    for (i = 0; i < *height; i++)
+    {
+        int ipos = 0;
+        for (j = 0; j < *width; j++)
+        {
+        *ptr++ = row_pointers[i][ipos++];
+        *ptr++ = row_pointers[i][ipos++];
+        *ptr++ = row_pointers[i][ipos++];
+        alpha[0][i * *width + j] = row_pointers[i][ipos++];
+        }
+    }
     }
 
     png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);

+ 0 - 47
ssetbg.1

@@ -1,47 +0,0 @@
-." Text automatically generated by txt2man
-.TH ssetbg  "January 18, 2007" "" ""
-.SH NAME
-\fBssetbg \fP- set X background
-\fB
-.SH SYNOPSIS
-.nf
-.fam C
-\fBssetbg\fP [\fB-c\fP \fIcolor\fP] [\fB-s\fP|\fB-t\fP|\fB-n\fP \fIfilename\fP]
-.fam T
-.fi
-.SH DESCRIPTION
-\fBssetbg\fP is a simple tool to set the bacground of the X11 root window.
-It allows setting a jpg / png image or plain \fIcolor\fP.
-.SH OPTIONS
-.TP
-.B
-\fB-c\fP \fIcolor\fP
-Set backround \fIcolor\fP. \fIcolor\fP is in hex format, ie 0066CC
-Use this option to set plain \fIcolor\fP or together with the \fB-n\fP option
-.TP
-.B
-\fB-s\fP \fIfilename\fP
-Stretch the image to fill the sreen size. This is the default option
-.TP
-.B
-\fB-t\fP \fIfilename\fP
-Tile the image
-.TP
-.B
-\fB-n\fP \fIfilename\fP
-Center the image on the screen. Can be used with an alpha-transpaent
-png image and the the \fB-c\fP option.
-.TP
-.B
-\fIfilename\fP
-is the path to a JPEG or PNG image.
-.SH EXAMPLE
-\fBssetbg\fP /some/path/image.jpg
-.PP
-\fBssetbg\fP \fB-t\fP /some/path/tile.jpg
-.PP
-\fBssetbg\fP \fB-c\fP BBBBBB