diff -Naur original/INSTALL trunk/INSTALL --- original/INSTALL 2009-09-09 11:10:57.000000000 +0000 +++ trunk/INSTALL 2010-03-07 18:27:19.000000000 +0000 @@ -11,7 +11,7 @@ http://sourceforge.net/projects/pexpect - simplejson Python module http://www.undefined.org/python/#simplejson - - PyGTK+ 2.14 or higher + - PyGTK+ 2.10 or higher http://www.pygtk.org/ - PyGObject 2.14 or higher http://www.pygtk.org/ @@ -58,9 +58,9 @@ $ rpmdev-setuptree $ cd /path/to/neatx/tree $ tar czf ~/rpmbuild/SOURCES/neatx-0.1.tar.gz * -$ rpmbuild -bs contrib/rpm/neatx.spec --nodeps +$ rpmbuild -bs extras/rpm/neatx.spec --nodeps # yum-builddep ~/rpmbuild/SRPMS/neatx-*.src.rpm -$ rpmbuild -bb contrib/rpm/neatx.spec +$ rpmbuild -bb extras/rpm/neatx.spec # yum install --nogpgcheck ~/rpmbuild/RPMS// Then customise /etc/neatx.conf as below. diff -Naur original/Makefile.am trunk/Makefile.am --- original/Makefile.am 2009-09-09 11:10:57.000000000 +0000 +++ trunk/Makefile.am 2010-03-07 18:27:19.000000000 +0000 @@ -146,7 +146,8 @@ doc/%.html: doc/%.rst @if test -z "$(RST2HTML)"; then \ - echo 'rst2html not found during configure, not building docs'; \ + echo "rst2html not found during configure, can't build docs"; \ + exit 1; \ else \ $(RST2HTML) $< $@; \ fi diff -Naur original/autotools/gen-log-wrapper trunk/autotools/gen-log-wrapper --- original/autotools/gen-log-wrapper 2009-09-09 11:10:56.000000000 +0000 +++ trunk/autotools/gen-log-wrapper 2010-03-07 18:27:18.000000000 +0000 @@ -20,10 +20,12 @@ logger -t "\$PROGNAME[\$\$]" -p "user.\${PRIORITY:-crit}" -- "\$@" } +PRIORITY=debug log "Called with args: \$*" + # Duplicate stdout to fd 3 exec 3>&1 exec $1 "\$@" 2>&1 >&3 | { - PRIORITY=debug log "Started" + PRIORITY=debug log "Started $1 \$*" while read; do log "\$REPLY" done diff -Naur original/configure.ac trunk/configure.ac --- original/configure.ac 2009-09-09 11:10:57.000000000 +0000 +++ trunk/configure.ac 2010-03-07 18:27:19.000000000 +0000 @@ -30,7 +30,8 @@ # Check for rst2html AC_ARG_VAR(RST2HTML, [rst2html path]) -AC_PATH_PROG(RST2HTML, [rst2html], []) +AC_PATH_PROGS(RST2HTML, [rst2html rst2html.py], []) + if test -z "$RST2HTML" then AC_MSG_WARN([rst2html not found, documentation rebuild will not be possible]) diff -Naur original/extras/rpm/neatx.initscript trunk/extras/rpm/neatx.initscript --- original/extras/rpm/neatx.initscript 1970-01-01 00:00:00.000000000 +0000 +++ trunk/extras/rpm/neatx.initscript 2010-03-07 18:27:18.000000000 +0000 @@ -0,0 +1,26 @@ +#!/bin/bash +# +# Copied from freenx-server fedora package +# +# chkconfig: 2345 91 35 +# description: Creates /tmp/.X11-unix/ if required + +start() { + install -d -o root -g root -m 1777 "/tmp/.X11-unix" + restorecon /tmp/.X11-unix 2>/dev/null || : +} + +case "$1" in + start) + start + ;; + stop) + : + ;; + restart) + : + ;; + *) + echo $"Usage: $prog {start}" + exit 1 +esac diff -Naur original/extras/rpm/neatx.spec trunk/extras/rpm/neatx.spec --- original/extras/rpm/neatx.spec 2009-09-09 11:10:56.000000000 +0000 +++ trunk/extras/rpm/neatx.spec 2010-03-07 18:27:18.000000000 +0000 @@ -23,16 +23,19 @@ BuildRequires: python-devel BuildRequires: python-docutils +Requires: nc +Requires: nx Requires: openssh Requires: pexpect Requires: pygobject2 >= 2.14 -Requires: pygtk2 >= 2.13 +Requires: pygtk2 >= 2.10 Requires: python >= 2.4 Requires: python-simplejson -Requires: nc -Requires: nx Requires: xauth +Requires: xorg-x11-fonts-misc +Requires: xorg-x11-xkb-utils Requires: xrdb +Requires: xterm Requires(pre): shadow-utils Requires(post): %__install @@ -44,6 +47,11 @@ %setup -cq %build +# fixup paths +sed -ri 's@^(XSESSION\s+=).*@\1 "/etc/X11/xinit/Xsession"@' lib/constants.py +sed -ri 's@^(NETCAT\s+=).*@\1 "/usr/bin/nc"@' lib/constants.py +sed -ri 's@^(#xsession-path\s+=).*@\1 /etc/X11/xinit/Xsession@' doc/neatx.conf.example +sed -ri 's@^(#netcat-path\s+=).*@\1 /usr/bin/nc@' doc/neatx.conf.example ./autogen.sh %configure make @@ -53,6 +61,7 @@ make DESTDIR=%{buildroot} install # provide a meaningfull config file %__install -D -m 644 %{buildroot}/%_docdir/%{name}/neatx.conf.example %{buildroot}/etc/neatx.conf +%__install -D -m 755 extras/rpm/neatx.initscript %{buildroot}/etc/init.d/neatx %clean rm -rf %{buildroot} @@ -63,6 +72,7 @@ getent passwd nx >/dev/null || \ useradd -r -g nx -m -d %nx_homedir -s %_libdir/%{name}/nxserver-login-wrapper \ -c "System account for the %{name} package" nx +chown -R nx: %nx_homedir exit 0 %post @@ -71,10 +81,13 @@ %__install -d -m 700 -o nx -g nx %nx_homedir/.ssh/ %__install -D -m 600 -o nx -g nx %_datadir/%{name}/authorized_keys.nomachine %nx_homedir/.ssh/authorized_keys fi +/sbin/chkconfig --add neatx +/sbin/service neatx start > /dev/null 2>&1 %files %defattr(-,root,root) %config(noreplace) /etc/neatx.conf +/etc/init.d/neatx %_libdir/%{name} %python_sitelib/%{name}/* %doc %_docdir/%{name} diff -Naur original/lib/agent.py trunk/lib/agent.py --- original/lib/agent.py 2009-09-09 11:10:57.000000000 +0000 +++ trunk/lib/agent.py 2010-03-07 18:27:19.000000000 +0000 @@ -69,7 +69,7 @@ _GENERAL_WARNING_RE = re.compile(r"^Warning:\s+(?P.*)$") _GEOMETRY_RE = re.compile(r"^Info:\s+Screen\s+\[0\]\s+resized\s+to\s+" r"geometry\s+\[(?P[^\]]+)\]" - r"( fullscreen \[\d\])?\.$") + r"( fullscreen \[(?P\d)\])?\.$") class UserApplication(daemon.Program): @@ -384,8 +384,10 @@ m = _GEOMETRY_RE.match(line) if m: geometry = m.group("geometry") - self._ChangeGeometry(geometry) - logging.info("Matched info geometry change, new is %r", geometry) + fullscreen = (m.group("fullscreen") == "1") + self._ChangeGeometry(geometry, fullscreen) + logging.info("Matched info geometry change, new is %r, fullscreen %r", + geometry, fullscreen) return def _CheckStatus(self, line, _status_map=None): @@ -426,11 +428,10 @@ if new == old: pass - elif (old == constants.SESS_STATE_CREATED and - new == constants.SESS_STATE_STARTING): - self.__EmitDisplayReady() - elif new == constants.SESS_STATE_WAITING: + if old == constants.SESS_STATE_STARTING: + self.__EmitDisplayReady() + port = m.group("port") try: @@ -468,15 +469,18 @@ # Send SIGHUP to reopen port self._SendSighup() - def _ChangeGeometry(self, geometry): + def _ChangeGeometry(self, geometry, fullscreen): """Called when geometry changed. @type geometry: str @param geometry: Geometry information + @type fullscreen: boolean + @param fullscreen: Fullscreen state """ sess = self._ctx.session sess.geometry = geometry + sess.fullscreen = fullscreen sess.Save() def _FormatNxAgentOptions(self, opts): @@ -491,7 +495,7 @@ formatted = ",".join(["%s=%s" % (name, value) for name, value in opts.iteritems()]) - return "nx/nx,%s:%s\n" % (formatted, sess.display) + return "nx/nx,%s:%d\n" % (formatted, sess.display) def _GetDisplayWithOptions(self): """Returns the value for the DISPLAY variable for nxagent. @@ -499,7 +503,9 @@ """ sess = self._ctx.session - return "nx/nx,options=%s:%s" % (sess.optionsfile, sess.display) + self.__CheckStrChars(sess.optionsfile, "Session options file") + + return "nx/nx,options=%s:%d" % (sess.optionsfile, sess.display) def _GetOptions(self): """Returns session options for nxagent. @@ -552,10 +558,10 @@ opts["shadowuid"] = self._ctx.uid opts["shadow"] = ":%s" % sess.shadow_display - if not sess.rootless: - opts["geometry"] = sess.geometry - else: + if sess.rootless: opts["menu"] = "1" + else: + opts["geometry"] = sess.geometry opts["fullscreen"] = protocol.FormatNxBoolean(sess.fullscreen) if sess.rootless and sess.type == constants.SESS_TYPE_CONSOLE: @@ -592,7 +598,7 @@ # nxagent port). "-nolisten", "tcp", - ":%s" % sess.display, + ":%d" % sess.display, ] if sess.type == constants.SESS_TYPE_SHADOW: @@ -615,6 +621,7 @@ """ sess = self._ctx.session filename = sess.optionsfile + self.__CheckOptsChars(opts) formatted = self._FormatNxAgentOptions(opts) logging.debug("Writing session options %r to %s", formatted, filename) @@ -622,3 +629,30 @@ def __EmitDisplayReady(self): self.emit(self.DISPLAY_READY_SIGNAL) + + def __CheckOptsChars(self, opts): + """Checks to make sure option name/values don't contain illegal characters. + + @type opts: dict + @param opts: Options + + """ + + for name, value in opts.iteritems(): + self.__CheckStrChars(name, "Name of option %r" % name) + self.__CheckStrChars(value, "Value of option %r (%r)" % (name, value)) + + def __CheckStrChars(self, s, description): + """Checks to make sure string don't contain illegal characters. + + @type s: string + @param s: text to test + @type description: string + @param description: description of text + + """ + illegal_chars = [","] + for c in illegal_chars: + if c in s: + raise errors.IllegalCharacterError("%s contains illegal character %r" % + (description, c)) diff -Naur original/lib/app/nxdialog.py trunk/lib/app/nxdialog.py --- original/lib/app/nxdialog.py 2009-09-09 11:10:56.000000000 +0000 +++ trunk/lib/app/nxdialog.py 2010-03-07 18:27:18.000000000 +0000 @@ -163,15 +163,13 @@ """ if action == DISCONNECT: - ppid = os.getppid() - logging.info("Disconnecting from session, sending SIGHUP to %s", ppid) - os.kill(ppid, signal.SIGHUP) + logging.info("Disconnecting from session, sending SIGHUP to %s", agentpid) + os.kill(agentpid, signal.SIGHUP) elif action == TERMINATE: - if agentpid: - logging.info("Terminating session, sending SIGTERM to process %s", - agentpid) - os.kill(agentpid, signal.SIGTERM) + logging.info("Terminating session, sending SIGTERM to process %s", + agentpid) + os.kill(agentpid, signal.SIGTERM) elif action is None: logging.debug("Dialog canceled, nothing to do") @@ -223,6 +221,11 @@ logging.error("Dialog type '%s' not supported", dlgtype) sys.exit(constants.EXIT_FAILURE) + if dlgtype in (constants.DLG_TYPE_PULLDOWN, + constants.DLG_TYPE_YESNOSUSPEND) and not self.options.agentpid: + logging.error("Agent pid not supplied via --parent") + sys.exit(constants.EXIT_FAILURE) + if self.options.caption: message_caption = self.options.caption else: diff -Naur original/lib/app/nxserver_login.py trunk/lib/app/nxserver_login.py --- original/lib/app/nxserver_login.py 2009-09-09 11:10:56.000000000 +0000 +++ trunk/lib/app/nxserver_login.py 2010-03-07 18:27:18.000000000 +0000 @@ -148,7 +148,10 @@ # Not writing username. If user specified a username starting with "NX>", # the client could interpret it as a response. - server.WriteLine("") + if username.startswith('NX>'): + server.WriteLine("") + else: + server.WriteLine(username) # Read password without echo on interactive terminals def _RequestPassword(): @@ -157,15 +160,16 @@ return server.ReadLine(hide=True) password = server.WithoutTerminalEcho(_RequestPassword) + + # Not writing real password for security reasons. + server.WriteLine(NX_DUMMY_PASSWORD) + if not password: server.Write(500, ("Password cannot be in MD5 when not using the NX " "password DB.")) server.Write(500, "Please update your NX Client") raise protocol.NxQuitServer() - # Not writing real password for security reasons. - server.WriteLine(NX_DUMMY_PASSWORD) - self._TryLogin(username, password) def _Set(self, args): diff -Naur original/lib/errors.py trunk/lib/errors.py --- original/lib/errors.py 2009-09-09 11:10:57.000000000 +0000 +++ trunk/lib/errors.py 2010-03-07 18:27:19.000000000 +0000 @@ -81,6 +81,11 @@ """ +class IllegalCharacterError(GenericError): + """String contains illegal character (e.g. a comma in session options). + + """ + # Exception classes should be added above diff -Naur original/lib/session.py trunk/lib/session.py --- original/lib/session.py 2009-09-09 11:10:57.000000000 +0000 +++ trunk/lib/session.py 2010-03-07 18:27:19.000000000 +0000 @@ -49,7 +49,7 @@ """ if _data is None: - _data = random.getrandbits(1024) + _data = random.SystemRandom().getrandbits(1024) return md5.md5(str(_data)).hexdigest().upper()