summaryrefslogtreecommitdiffstats
path: root/ktalkd
diff options
context:
space:
mode:
Diffstat (limited to 'ktalkd')
-rw-r--r--ktalkd/ChangeLog532
-rw-r--r--ktalkd/Makefile.am29
-rw-r--r--ktalkd/SNPRINTF_MISSING21
-rw-r--r--ktalkd/TODO16
-rw-r--r--ktalkd/configure.in.in73
-rw-r--r--ktalkd/kcmktalkd/Makefile.am15
-rw-r--r--ktalkd/kcmktalkd/answmachpage.cpp265
-rw-r--r--ktalkd/kcmktalkd/answmachpage.h83
-rw-r--r--ktalkd/kcmktalkd/cr128-app-ktalkd.pngbin0 -> 13158 bytes
-rw-r--r--ktalkd/kcmktalkd/cr16-app-ktalkd.pngbin0 -> 792 bytes
-rw-r--r--ktalkd/kcmktalkd/cr22-app-ktalkd.pngbin0 -> 1342 bytes
-rw-r--r--ktalkd/kcmktalkd/cr32-app-ktalkd.pngbin0 -> 2133 bytes
-rw-r--r--ktalkd/kcmktalkd/cr48-app-ktalkd.pngbin0 -> 3882 bytes
-rw-r--r--ktalkd/kcmktalkd/forwmachpage.cpp183
-rw-r--r--ktalkd/kcmktalkd/forwmachpage.h75
-rw-r--r--ktalkd/kcmktalkd/kcmktalkd.desktop207
-rw-r--r--ktalkd/kcmktalkd/main.cpp113
-rw-r--r--ktalkd/kcmktalkd/main.h64
-rw-r--r--ktalkd/kcmktalkd/soundpage.cpp330
-rw-r--r--ktalkd/kcmktalkd/soundpage.h90
-rw-r--r--ktalkd/ktalkd/.talkdrc78
-rw-r--r--ktalkd/ktalkd/Makefile.am41
-rw-r--r--ktalkd/ktalkd/announce.cpp502
-rw-r--r--ktalkd/ktalkd/announce.h51
-rw-r--r--ktalkd/ktalkd/defs.h87
-rw-r--r--ktalkd/ktalkd/find_user.cpp400
-rw-r--r--ktalkd/ktalkd/find_user.h39
-rw-r--r--ktalkd/ktalkd/includ.h79
-rw-r--r--ktalkd/ktalkd/ktalkd.wavbin0 -> 10026 bytes
-rw-r--r--ktalkd/ktalkd/ktalkdrc73
-rw-r--r--ktalkd/ktalkd/machines/Makefile.am14
-rw-r--r--ktalkd/ktalkd/machines/answmach.cpp405
-rw-r--r--ktalkd/ktalkd/machines/answmach.h86
-rw-r--r--ktalkd/ktalkd/machines/forwmach.cpp442
-rw-r--r--ktalkd/ktalkd/machines/forwmach.h166
-rw-r--r--ktalkd/ktalkd/machines/talkconn.cpp583
-rw-r--r--ktalkd/ktalkd/machines/talkconn.h135
-rw-r--r--ktalkd/ktalkd/options.cpp57
-rw-r--r--ktalkd/ktalkd/options.h71
-rw-r--r--ktalkd/ktalkd/otalkd.h64
-rw-r--r--ktalkd/ktalkd/print.c226
-rw-r--r--ktalkd/ktalkd/process.cpp285
-rw-r--r--ktalkd/ktalkd/process.h39
-rw-r--r--ktalkd/ktalkd/prot_talkd.h136
-rw-r--r--ktalkd/ktalkd/proto.h75
-rw-r--r--ktalkd/ktalkd/readcfg++.cpp261
-rw-r--r--ktalkd/ktalkd/readconf.cpp188
-rw-r--r--ktalkd/ktalkd/readconf.h69
-rw-r--r--ktalkd/ktalkd/repairs.c274
-rw-r--r--ktalkd/ktalkd/table.cpp217
-rw-r--r--ktalkd/ktalkd/table.h73
-rw-r--r--ktalkd/ktalkd/talkd.conf82
-rw-r--r--ktalkd/ktalkd/talkd.cpp401
-rw-r--r--ktalkd/ktalkd/threads.cpp99
-rw-r--r--ktalkd/ktalkd/threads.h39
-rw-r--r--ktalkd/ktalkd/unixsock.cpp145
-rw-r--r--ktalkd/ktalkd/unixsock.h6
-rw-r--r--ktalkd/ktalkdlg/Makefile.am15
-rw-r--r--ktalkd/ktalkdlg/ktalkdlg.cpp168
-rw-r--r--ktalkd/mail.local/Makefile.am8
-rw-r--r--ktalkd/mail.local/README.mail.local12
-rw-r--r--ktalkd/mail.local/mail.local.c983
-rw-r--r--ktalkd/mail.local/pathnames.h37
63 files changed, 9307 insertions, 0 deletions
diff --git a/ktalkd/ChangeLog b/ktalkd/ChangeLog
new file mode 100644
index 00000000..c2e26b51
--- /dev/null
+++ b/ktalkd/ChangeLog
@@ -0,0 +1,532 @@
+2002-12-03 David Faure <faure@kde.org>
+
+ * *: Merged with netkit-ntalk-0.17-pre20000412/talkd, including:
+ - many security improvements (checks for bad things received in the packets etc.)
+ - protocol-related checks and fixes
+ This also brings the code a bit closer to the initial talkd again,
+ for easier maintainance.
+ * kotalkd: Got rid of it (the same daemon can handle both).
+
+2000-12-14 Danny Tholen <danny@mailmij.org>
+
+ * announce.cpp: Improvement for error cases. If the announce program
+ can't open the display, or doesn't exist, or doesn't behave properly,
+ fallback on text announce. For this, the pipe is made non-blocking and
+ the exit code of the announce program is checked. It also means we don't
+ wait forever on the announce program if it doesn't send the expected '#'.
+
+2000-03-04 David Faure <faure@kde.org>
+
+ * talkd.cpp, ktalkdrc: Some fixes for KDE2 (KInstance, kvt->konsole...
+
+2000-03-04 David Smith <dsmith@algonet.se>
+
+ * kcmktalkd/* : Ported to KDE2 kcontrol architecture
+
+2000-01-26 David Faure <faure@kde.org>
+
+ * readcfg++.cpp, announce.cpp : remove HAVE_FUNC_SETENV tests and
+ putenv(), since ktalkd can now use kdecore's fake for setenv if setenv
+ is not available. Thanks to Stefan.Becker@nokia.com.
+
+2000-01-18 David Faure <faure@kde.org>
+
+ * unixsock.cpp : Remove unix socket even if ktalk isn't running
+ Thanks to Danil Mantione <daniel@deadlock.et.tudelft.nl> for reporting & testing
+
+1999-05-09 David Faure <faure@kde.org> (1.5.2)
+
+ * find_user.cpp : Check for ':' in ut_host, otherwise it's not a display.
+ Thanks to Manuel Cepedello Boiso <manuel@cauchy.fie.us.es> for reporting & testing
+
+1999-02-27 David Faure <faure@kde.org>
+
+ * announce.cpp, ktalkdlg.cpp : Use standard output instead of stderr,
+ for ktalkdlg->ktalkd communication.
+ Solves problem when Qt/KDE prints a warning before the '#'.
+ Thanks to Andrew Standley-Jones for his help (on irc).
+
+1999-02-03 David Faure <faure@kde.org> (1.5.1)
+
+ * readcfg++.cpp : Fixed answmach message stopping at first empty line
+
+1999-02-02 David Faure <faure@kde.org>
+
+ * kotalkd/vsnprintf.c, SNPRINTF_MISSING : new files for systems
+ without snprintf (e.g. Solaris). vsnprintf.c is _not_ to be
+ compiled on systems with a snprintf implementation.
+
+ * *lsm: Upgraded version number to 1.5.1 (!) in order to be above
+ the old talkd-1.4.4 that I released two years ago and that people
+ keep sending me bug reports about !
+
+1998-12-02 David Faure <faure@kde.org> (0.9.1)
+
+ * ktalkd/readc*.cpp: Fixed strncpy length arg for NEU_forwardmethod
+
+1998-11-15 David Faure <faure@kde.org>
+
+ * ktalkd/readc*.cpp, ktalkd/options.*, ktalkd/process.cpp: Added
+ NEU forward method, for multi-networked hosts with forwards
+
+ * ktalkd/includ.h: Improved used of <paths.h> and default settings
+
+ * mail.local/mail.local.c: Used HAVE_VSNPRINTF instead of
+ hardcoded list of OSes.
+
+1998-11-11 David Faure <faure@kde.org> (0.9.0)
+
+ * ktalkd/talkconn.cpp: Moved static init to TalkConnection::init()
+ Added reply address detection for multiple networked hosts.
+ Thanks to Burkhard Lehner for the method getReplyAddr().
+
+ * acinclude.m4.in (non-KDE version) : Used /usr/local as
+ default_prefix for non-KDE systems.
+
+ * ktalkd/readconf.cpp: Fixed read_user_config (bug with ForwardMethod)
+ Speeds up configuration file parsing.
+
+1998-11-03 David Faure <faure@kde.org>
+
+ * ktalkd/process.cpp: Fixed a bug, when user config file doesn't
+ exist. Reported by valankar@bigfoot.com.
+
+1998-10-02 David Faure <faure@kde.org> (0.8.9)
+
+ * mail.local/mail.local.c: Started using config.h
+
+1998-09-29 David Faure <faure@kde.org>
+
+ * ktalkd/unixsock.cpp: Support for multiple-display ktalk.
+
+1998-09-13 David Faure <faure@kde.org>
+
+ * ktalkd/find_user.cpp: Converted the get_display function to use
+ ifstream instead of read() and realloc()...
+
+ * ktalkd/announce.cpp: Do setuid from the beginning of announce().
+ Solves socket permissions for sendToKtalk and speeds up X announce.
+
+ * ktalkd/readcfg++.cpp: Handles two user config files :
+ ktalkdrc & ktalkannouncerc
+
+ * ktalkd/read*: Removed *kdebindir from readconf.h and readconf.cpp.
+ Internal to readcfg++.cpp.
+
+ * ktalkd/options.*: Now a structure instead of a class.
+
+1998-09-07 David Faure <faure@kde.org>
+
+ * ktalkd/announce.cpp: No text announce on a xterm if X announce was ok
+
+1998-09-03 David Faure <faure@kde.org> (0.8.8)
+
+ * ktalkd/unixsock.cpp: Added chmod => ktalk can write to the socket.
+
+1998-09-02 David Faure <faure@kde.org>
+
+ * ktalkd/unixsock.cpp, .h: New. Direct communication with ktalk.
+ * ktalkd/announce.cpp: Call sendToKtalk.
+
+1998-08-29 David Faure <faure@kde.org>
+
+ * ktalkd/options.cpp: Default values moved from .h to .cpp.
+ Makes -ansi happier.
+
+1998-08-23 David Faure <faure@kde.org>
+
+ * ktalkd/process.cpp (process_request): print_response enabled again.
+
+ * ktalkd/find_user.cpp: Removed the 'break;' for xdm, and didn't
+ override tty. This way, you can have both announcements (text & X).
+
+1998-08-19 David Faure <faure@kde.org> (0.8.7)
+
+ * ktalkd/mail.local: mail.local is back in the distrib. Has been
+ forgotten since 0.7.0 !! I added a README.mail.local to explain
+ its purpose.
+
+ * ktalkd/announce.cpp: Added text announce in addition to X announce.
+
+ * ktalkd/process.cpp: Removed check for the family field of addr. Oops.
+
+ * doc/en/*, *: Updated my email address from
+ <david.faure@insa-lyon.fr> to <faure@kde.org>
+
+1998-08-15 David Faure <faure@kde.org> (0.8.6)
+
+ * ktalkd/machines/talkconn.cpp: Added check for remote protocol.
+ This means that it is now possible to forward to an otalk machine ...
+
+ * ktalk/machines/forwmach.cpp: ... and/or from an otalk machine.
+
+ The first one who sends me an email after testing both, wins :)
+
+ * ktalkd/machines/check_protocol.cpp: Removed. Integrated into
+ talkconn.cpp. Thanks to Burkhard Lehner for the example code.
+
+ * ktalk/machines/answmach.cpp: Added a sleep(1) for not logged/NEU.
+
+ * ktalkd/print.cpp: Used c++ overriding to name "message()" the
+ former message_s() et message2().
+
+1998-08-12 David Faure <faure@kde.org> (0.8.5)
+
+ * ktalkd/find_user.cpp: A nasty bug with unsigned int fixed.
+ Thanks to Rolf Offermanns who found it.
+ Uncommented the use of ut_host for PTYs (ex : xterms).
+ Exit the loop if XDM login found (highest priority).
+
+ * kotalkd/includ.h: Added a simpler version of includ.h.
+
+1998-08-10 David Faure <faure@kde.org> (0.8.4)
+
+ * ktalkd/machines/talkconn.cpp: Support for otalk. (not finished).
+
+ * kotalkd/*.c: Just send to ktalkd, which responds itself.
+
+ * ktalkd/process.cpp: Handle otalk packets (with vers=0)
+
+1998-08-09 David Faure <faure@kde.org>
+
+ * kotalkd/kotalkd.c: Dies if ktalk protocol detection (-> ntalk detected).
+
+ * ktalkd/machines/forwmach.cpp: Now forwards DELETEs too. (cf. sig_handler).
+
+ * ktalkd/machines/forwmach.cpp: Final cleanup improved. (for forwmachines).
+
+ * ktalkd/options.cpp: Created, to hold systemwide options.
+
+ * ktalkd/*.c: Converted to c++ all c files.
+
+1998-08-07 David Faure <faure@kde.org> (0.8.3)
+
+ * ktalkd/doc/en/Makefile.am: index.html -> ktalkd.html
+
+1998-08-07 David Faure <faure@kde.org> (0.8.2)
+
+ * ../acincktalk.m4: Bug fix for the bug fix. Linux detection ok.
+
+1998-08-02 David Faure <faure@kde.org>
+
+ * doc/en/ktalkd.sgml: Converted all documentation to sgml. Phew.
+
+ * kcmktalkd/forwmachpage.cpp: i18n'ed the explanation for forwards
+
+1998-07-31 David Faure <faure@kde.org>
+
+ * ../acincktalk.m4: More output printed out and a bug fix
+
+1998-07-30 David Faure <faure@kde.org> (0.8.1)
+
+ * kotalkd/*.c: New way to support otalk protocol : forward everything
+ to local ntalk daemon (possibly ktalkd, but any other should work too)
+
+ * ktalkd/machines/talkconn.cpp (listen): Use SOMAXCONN as arg to listen
+
+1998-07-27 David Faure <faure@kde.org> (0.8.0)
+
+ * kotalkd/: Created to support otalk protocol. No new source
+ files. Everything is links in it, except Makefile.am and all
+ generated files.
+
+1998-07-26 David Faure <faure@kde.org>
+
+ * ktalkd/threads.c: Created to manage children processes (register, wait, ...)
+ No more zombie processes waiting 1mn30s to be acknowledged. :)
+
+ * ktalkd/machines/forwmach.*: FWT. Lots of bug fixed. Fully tested now.
+
+ * ktalkd/machines/talkconn.*: Bug fixing.
+
+1998-07-24 David Faure <faure@kde.org>
+
+ * ktalkd/machines/forwmach.*: Created the Forwarding machine. FWA. FWR.
+
+ * kcmktalkd/*: Added the 'forward' configuration page.
+
+ * ktalkd/*: Reverted most of the patch from Enrico Scholz.
+ The forwarding machine is now used for NEU if NEUBehaviour=1.
+
+1998-07-18 David Faure <faure@kde.org>
+
+ * ktalkd/readcfg++.h: Removed. Now in readconf.h
+
+1998-07-15 David Faure <faure@kde.org> (0.7.0)
+
+ * ktalkd/machines/talkconn.cpp (set_edit_chars): At last ! Fixed the
+ bug in answmach banners, which first appeared 8 months ago,
+ erasing half of some lines !
+
+ * ktalkd/table.c, ktalkd/print.c: Improved logs.
+
+ * ktalkd/process.c: Bug fixed : insert_table called even for NEU.
+
+ * ktalkd/machines/*: Converted the answering machine to C++.
+ Split into 3 classes.
+ TalkConnection : Handles the protocol.
+ TalkMachine : Generic talk machine.
+ AnswMachine : Answering machine. Inherits from TalkMachine.
+
+1998-07-08 David Faure <faure@kde.org> (0.6.2)
+
+ * ktalkd/*, ktalkdlg.cpp: Applied patch for NEUBehaviour=1
+ by Enrico Scholz <enrico.scholz@wirtschaft.tu-chemnitz.de>
+
+ * answmach/init_disp.c: Handle VWERASE if not defined (for AIX)
+
+1998-07-06 David Faure <faure@kde.org>
+
+ * ktalkd/announce.c: Small bug fix in text announcement (remotename).
+
+1998-06-15 David Faure <faure@kde.org>
+
+ * ktalkd/find_user.c: Added blank after display, needed by announce.c
+
+ * kcmktalkd/answmachpage.cpp: Override help() to display ktalkd's help.
+
+1998-06-13 David Faure <faure@kde.org> (0.6.1)
+
+ * includ.h, talkd.h: took talkd.h from ktalk. ktalkd doesn't use the
+ system one anymore.
+
+ * answmach/look_up.c: use sockaddr instead of osockaddr
+
+ * acincktalk.m4, configure.in.1: removed the check for osockaddr
+
+ * ktalkd/talkd.c: Use sys/params.h where available, for hostname length
+
+1998-06-11 David Faure <faure@kde.org> (0.6.0)
+
+ * ktalkd/: Reorganised the directory structure, to ship ktalkd
+ with ktalkdlg and kcmktalkd in a single package.
+
+ * talkd.c: Caller's hostname was limited to 32 ! Now 256.
+
+ * Makefile.am: rewrote some of them, for non-KDE users.
+
+1998-06-09 David Faure <faure@kde.org> (0.5.7)
+
+ * kcmktalkd: First release of the configuration dialog
+
+1998-05-16 David Faure <faure@kde.org> (0.5.6)
+
+ * announce.c: Announces with ktalkdlg on ALL displays where the
+ user is found. It works !
+
+ * readcfg++.cpp (init_user_config):
+ Added setenv("HOME",...) because the kdelibs rely on that to find
+ the user config file.
+
+1998-05-15 David Faure <faure@kde.org> (0.5.5)
+
+ * doc/: Made HTML documentation. Phew. Done.
+
+ * readcfg++.cpp (init_user_config):
+ Removed the un-necessary looking in pwd file.
+ KDElibs do that for us :)
+
+ * find_user.c:
+ Disabled X processes scanning if uid < 10. (Security hole)
+ Corrected a bug (S_ISCHR() is now "& 020") which didn't let you talk
+ to tty2 if tty1 was "mesg n".
+
+ * announce.c: open user config file before calling announce_proc, so
+ that it *is* closed now. It wasn't because of 'return'.
+
+
+1998-05-06 David Faure <faure@kde.org>
+
+ * announce.c: now the non-KDE sound works with and without option.
+ * acinclude.m4.in - the ktalkd one : some more corrections for
+ working without X.
+
+1998-04-28 David Faure <faure@kde.org> (0.5.4)
+
+ * acinclude.m4.in - the one from ktalkd package, not the kdenonbeta one
+ rewrote X detection, so that it works also without X :)
+
+1998-04-20 David Faure <faure@kde.org> (0.5.3)
+
+ * Makefile.am: Removed -lkdeui, not needed.
+ * io.c: Corrected an awful bug
+ (NEUBanner displayed instead of OPTinvitelines)
+ * readcfg++.cpp: Added missing "/" before 'ktalkdrc'
+
+1998-04-19 David Faure <faure@kde.org> (0.5.2)
+
+ * readcfg++.cpp: added check for user config file.
+ (was created with root permission otherwise)
+ * acinclude.m4.in - the ktalkd one: updated
+
+1998-04-15 David Faure <faure@kde.org> (0.5.1)
+
+ ktalkd doesn't use anymore kdedir() because it's protected.
+ It sets now $KDEBINDIR, for portable ktalkdrc files. KDEBINDIR
+ is determined by a call to the kde_bindir() function.
+ Makefile doesn't define TALKD_CONF anymore for compilation :
+ ktalkd now opens ktalkdrc from KApplication::kde_configdir().
+
+1998-03-26 David Faure <faure@kde.org> (0.5.0)
+ ktalkdrc: Extprg is now $KDEDIR/bin/ktalkdlg.
+ KDEDIR is now set when reading global configuration file.
+ Default value for Extprg includes $KDEDIR.
+
+1998-03-25 David Faure <faure@kde.org> (0.4.8)
+ KDEDIR is now set by ktalkd, not by ktalkdlg.
+ ktalkdrc*:removed path in front of sound files. ktalkdlg now finds them
+ in kde_sounddir().
+ Added option ExtPrg. Set to ktalkdlg (default) or ktalk.
+
+1998-03-14 David Faure <faure@kde.org> (0.4.7)
+ Made 2 packages out of ktalkd : ktalkd and ktalkdlg (new name for
+ atdlg), so that Burkhard Lehner <b_lehner@informatik.uni-kl.de>
+ can improve it to communicate with ktalk.
+
+1998-03-13 David Faure <faure@kde.org> (0.4.6)
+ Changed S_MESSG size. (Too little for mail first line, if NEU)
+ Used mkstemp instead of popen for the message left to the
+ answering machine. Added option EmptyMail, to avoid getting
+ empty mails.
+
+1998-03-10 David Faure <faure@kde.org> (0.4.5)
+ Renamed debug to debug_mode (debug exists in qt)
+ Updated configure.in and acinclude.m4 to match CVS ones.
+ Same for ltconfig, ltmain.sh, ...
+ Added new translations (es, it) and changed po structure.
+ Added NEUBanner* options to ktalkdrc.
+
+1998-02-27 D.F. (thanks to Juraj Bednar <bednar@isternet.sk>) (0.4.4)
+ Removed ktalkd-0.4.x/protocols from the distribution. Caused a
+ bug when compiling.
+
+1998-02-15 D.F. (thanks to B. Lehner<b_lehner@informatik.uni-kl.de>) (0.4.3)
+ added checks for paths.h and protocols/talkd.h (for Solaris)
+ Added default paths (/dev/ and /var/run/utmp) if paths.h not found
+ Included talkd.h in the distribution, for Solaris which doesn't have it
+ Changed AC_CHECK_OSOCKADDR to use this file if protocols/talkd.h absent
+
+1998-02-04 David Faure <faure@kde.org> (0.4.2)
+ Added user option Answmach.
+ Moved the daemon to $KDEDIR/bin. One must now change inetd.conf
+ (this way, rpms won't conflict with standard ones, and the old
+ in.ntalkd will remain available). Should I deviate 'talk' protocol
+ too (in addition to 'ntalk') ?
+ Made installation NOT overwrite actual sitewide config file.
+ Merged and updated READMEs.
+
+1998-02-03 David Faure <faure@kde.org> (0.4.1)
+ Added -rpath option, as it's necessary for ktalkd if $KDEDIR/lib is
+ not set in ld.so.conf (LD_LIBRARY_PATH not read by a daemon)
+
+1998-02-03 David Faure <faure@kde.org> (0.4.0)
+ Corrected bugs related to new acinclude.m4 : compiling without X
+ and without KDE is possible again.
+ Added memcpy for structs.
+ Made atdlg re-write ktalkdrc for user if necessary.
+
+1998-01-29 David Faure <faure@kde.org> (0.3.4)
+ Removed answinfo var., added return val to announce and process_request.
+ Non existent user (NEU) support : either launch answmach or do nothing.
+ (set it in systemwide ktalkdrc). Don't sleep() before answering if NEU
+ or not logged.
+
+1998-01-26 David Faure <faure@kde.org> (0.3.3)
+ Deutsch translation added by J. Mertin <smurphy@stardust.phantasia.org>
+ Enabled atdlg without sound, following option set.
+ Removed nasty \r\n, not needed.
+ Made banner arrive 16 chars at a time, not the whole at once !
+
+1998-01-25 David Faure <faure@kde.org> (0.3.2)
+ Changed default configuration : ktalkd.wav will be installed
+ in $KDEDIR/share/apps/ktalkd. ktalkdrc points to it.
+ Made package install_root capable (e.g. for building rpms).
+
+1998-01-25 David Faure <faure@kde.org> (0.3.1)
+ atdlg will now play sound itself, using libmediatool.
+ Typos and bugs corrected.
+
+1998-01-24 still me ... :) (0.3.0)
+ Added internationalization to atdlg.cpp. Had to change atdlg params.
+ User must set language in ktalkdrc.
+ Added more user options to ktalkdrc_user: Sound, SoundPlayer, SoundFile.
+ Updated configure.in and acinclude.m4 to stick to kdenetwork as much
+ as possible.
+
+1998-01-14 David Faure <faure@kde.org> (0.2.5)
+ Moved -DHAVE_KDE from config.h (was a bad hack) to Makefile.am
+ Added AC_CHECK_GETDOMAINNAME and AC_CHECK_GETHOSTNAME...
+ and some other little changes to get closer to autoconf stuff from
+ kdenetwork, for future integration. Worked on BSD portability.
+
+1998-01-12 David Faure <faure@kde.org> (0.2.4)
+ Changed acinclude.m4, to remove NULL, and to make check for osockaddr
+ work better under bsd... Also removed NULL from anywhere in the code.
+
+1998-01-11 David Faure <faure@kde.org> (0.2.3)
+ Added a macro in acinclude.m4, to check for sockaddr and osockaddr.
+
+1998-01-11 David Faure <faure@kde.org> (0.2.2)
+ Wrote a new way of finding users, in addition to reading utmp,
+ which reads /proc to find $DISPLAY of processes. (Linux only).
+
+1998-01-08 David Faure <faure@kde.org> (0.2.1)
+ Removed MSG_EOR as it used in BSD with another meaning.
+
+1997-12-19 David Faure <faure@kde.org> (0.2.0)
+ Merged patch from Ralph Weichert (check for libbsd, needed under libc5)
+
+1997-12-16 David Faure <faure@kde.org> (0.1.9)
+ Fixed link command (back to $(LINK), not $(CXXLINK).
+ Fixed process.c (config.h not included => NEW_FIND_USER not defined)
+ Wrote includ.h to resolve struct definitions problems.
+ Started user config file processing (~/.kde/share/config/ktalkdrc)
+
+1997-12-13 David Faure <faure@kde.org> (0.1.8)
+ Improved configure.in, acinclude.m4, and ktalkd/Makefile.am, to
+ * find out where to install the daemon
+ * compile even without X, Qt, and KDE
+ * remove jpeg/gif dependencies
+
+1997-12-12 David Faure <faure@kde.org> (0.1.7)
+ Small bugs correction.
+
+1997-12-08 David Faure and Ralph Weichert (0.1.6)
+ Added autoconf and automake support. Added support for glibc.
+ Back to c compiling, except for .cpp files, of course.
+
+1997-12-02 David Faure <faure@kde.org> (0.1.5)
+ Read KDE configuration file, $KDEDIR/share/config/ktalkdrc, both by
+ atdlg and ktalkd, in readcfg++.cpp. Made all daemon compile with g++.
+ (Is this right ?)
+
+1997-11-23 David Faure <faure@kde.org> (0.1.4)
+ Re-wrote announcement by answering machine. One line at a time, not
+ one char at a time.
+
+1997-11-22 David Faure <faure@kde.org> (0.1.3)
+ Re-wrote process_etc_file, to read talkd.conf sequentially.
+
+1997-11-21 David Faure <faure@kde.org> (0.1.2c)
+ Merged patch from <Ralph.Weichert@physik.th-darmstadt.de> :
+ Used KDE libs in atdlg. User configurable talk client.
+ Merged patch from Bruce Gingery <bruce@gtcs.com> :
+ User configurable To: E-Mail address
+
+1997-10-25 David Faure <faure@kde.org> (0.1.2b)
+ Made atdlg finish after RING_WAIT seconds, so that the re-announce
+ will display another window (=> compatibility with other clients than
+ ktalk)
+
+1997-10-22 David Faure <faure@kde.org> (0.1.2)
+ Added to ktalkd (see README for description):
+ * sound capability
+ * configuration (/etc/talkd.conf)
+ * answering machine
+
+1997-05-14 R. (0.1.1)
+ Improved (I hope) X11 recognition:
+ Local XDM logins (depends on sessreg)
+ Read $DISPLAY variable for PTY logins
+ atdlg is run as user and can use MIT-magic-cookies
+ atdlg will run talk program
diff --git a/ktalkd/Makefile.am b/ktalkd/Makefile.am
new file mode 100644
index 00000000..5a89fd1a
--- /dev/null
+++ b/ktalkd/Makefile.am
@@ -0,0 +1,29 @@
+## -*- makefile -*-
+# Ktalkd - Main Makefile.am
+
+if KDE_INSTALLED
+kde_SUBDIRS = kcmktalkd ktalkdlg
+endif
+SUBDIRS = ktalkd mail.local $(kde_SUBDIRS)
+
+EXTRA_DIST = ChangeLog TODO ktalkd.lsm
+
+install-data-local:
+ @echo "**************************************************************************"
+ @echo
+ @echo "Don't forget to update /etc/inetd.conf :"
+ @echo
+ @echo "For example, on a linux system, if kde stays in "$(prefix)", set it to :"
+ @echo "talk dgram udp wait root /usr/sbin/tcpd "$(bindir)"/ktalkd"
+ @echo "ntalk dgram udp wait root /usr/sbin/tcpd "$(bindir)"/ktalkd"
+ @echo
+ @echo "Alternatively, you can use the script post-install.sh, to do the job"
+ @echo
+ @echo "Anyway, you'll have to restart inetd after this."
+ @echo "On most linux system, do a 'killall -HUP inetd'"
+ @echo
+ @echo "**************************************************************************"
+
+messages:
+ $(XGETTEXT) */*.cpp -o $(podir)/kcmktalkd.pot
+
diff --git a/ktalkd/SNPRINTF_MISSING b/ktalkd/SNPRINTF_MISSING
new file mode 100644
index 00000000..95e85366
--- /dev/null
+++ b/ktalkd/SNPRINTF_MISSING
@@ -0,0 +1,21 @@
+ktalkd needs the snprintf function, but some system don't have it (some
+Solaris versions, for instance).
+
+If snprintf is missing on your system, you can do the following :
+
+* compile ktalkd/kotalkd/vsnprintf.c, by entering 'make vsnprintf.o' in this
+directory. (there might be warnings, don't be afraid of them...)
+* if it compiles
+ - add vsnprintf.o to the kotalkd_OBJECTS line in ktalkd/kotalkd/Makefile
+ - copy vsnprintf.o to ktalkd/ktalkd
+ - add vsnprintf.o to the cond0ktalkd_OBJECTS line in ktalkd/ktalkd/Makefile
+ then run make from the toplevel directory again.
+
+Many thanks to Olof S Kylander <olof@frozenriver.com> for providing vsnprintf.c
+and to Bert Haverkamp <b.r.j.haverkamp@its.tudelft.nl> for reporting bugs in this readme.
+
+I know, the really good approach would be to include the missing
+automatically, which can be done. But for this I need to know if
+vsnprintf.c compiles on all architectures where snprintf is missing.
+If yes, I'll make the code in vsnprintf included automatically (in kdecore/fakes.cpp)
+
diff --git a/ktalkd/TODO b/ktalkd/TODO
new file mode 100644
index 00000000..07fb44bc
--- /dev/null
+++ b/ktalkd/TODO
@@ -0,0 +1,16 @@
+
+TODO:
+ - a 'not here' option, which launches the answmach at once (if set to on).
+ (or refuse the talks otherwise).
+ This way, when you leave, you switch 'on' the answmach.
+ - insensitive case and long logins supported for user names (big problem).
+ - a kdelnk file, not for launching ktalkd, but to make kdehelp's
+ helpindex find ktalkd - how to avoid it to appear in the K menu ?
+
+I thought also of a kde app. swallowed in kpanel, to choose options like
+ "refuse talks ('not here')", "activate/desactivate answering machine"
+ to launch a talk client, and to set up forwardings.
+But you can add to the panel a button to launch kcmktalkd, which does
+ all this except launch talk sessions.
+
+Last updated 30-Sep-1998
diff --git a/ktalkd/configure.in.in b/ktalkd/configure.in.in
new file mode 100644
index 00000000..60a7df9c
--- /dev/null
+++ b/ktalkd/configure.in.in
@@ -0,0 +1,73 @@
+## ktalkd specific checks
+## David Faure <faure@kde.org>
+
+AC_DEFUN([AC_FIND_USER_METHOD],
+[
+AC_MSG_CHECKING(ktalkd find_user method)
+if test -n "`echo $target_os | grep linux`" ; then
+ if test -d /proc; then
+ AC_DEFINE(PROC_FIND_USER, 1, [/proc exists])
+
+## Sufficient if all xdm and kdm would call sessreg to log the user into utmp
+ AC_DEFINE(UTMP_AND_PROC_FIND_USER, 1, [kdm/xmd log user])
+
+## Waiting for this, here is complement, looking for DISPLAY set in any process
+## in /proc that the user owns
+ AC_DEFINE(ALL_PROCESSES_AND_PROC_FIND_USER, 1, [whatever])
+
+ AC_MSG_RESULT(using /proc.)
+ else
+ AC_MSG_RESULT(/proc not found, using utmp.)
+ fi
+else
+ AC_MSG_RESULT(not a linux system, using utmp.)
+fi
+
+])
+
+AC_FIND_USER_METHOD
+
+# Define a symbol, to know that we're compiling WITH kde.
+# (Separate distributions of ktalkd can compile without KDE)
+AM_CONDITIONAL(KDE_INSTALLED, test "$have_kde" = "yes")
+
+dnl Check for utmp file
+AC_CHECK_UTMP_FILE([], [DO_NOT_COMPILE="$DO_NOT_COMPILE ktalkd"])
+
+AC_LANG_C
+dnl Checks for libraries.
+AC_CHECK_LIB(bsd, bsd_ioctl, [LIBBSD="-lbsd"]) dnl for Linux with libc5
+AC_SUBST(LIBBSD)
+
+AC_CHECK_HEADERS(sgtty.h bsd/sgtty.h sys/select.h)
+
+AC_HEADER_TIME
+
+dnl check for this stupid scandir constness problem
+AC_LANG_CPLUSPLUS
+save_CXXFLAGS="$CXXFLAGS"
+dnl for some reason CXXFLAGS contains $(KDE_CXXFLAGS) at this point. Argl.
+CXXFLAGS="-Wall -W"
+if test "$GCC" = "yes"; then
+CXXFLAGS="$CXXFLAGS -pedantic-errors"
+fi
+AC_MSG_CHECKING(whether the third argument of scandir needs const)
+AC_CACHE_VAL(ac_cv_scandir_const,
+[
+AC_TRY_COMPILE([
+#include <dirent.h>
+int select_process(const struct dirent *) { return 0; }
+],
+[
+ struct dirent **namelist;
+ (void) scandir("/proc", &namelist, select_process, 0 /*no sort*/);
+],
+ac_cv_scandir_const=yes,
+ac_cv_scandir_const=no)
+])
+AC_MSG_RESULT($ac_cv_scandir_const)
+
+if eval "test \"`echo $ac_cv_scandir_const`\" = yes"; then
+ AC_DEFINE(SCANDIR_NEEDS_CONST, 1, [Define if third argument of scandir needs const])
+fi
+CXXFLAGS="$save_CXXFLAGS"
diff --git a/ktalkd/kcmktalkd/Makefile.am b/ktalkd/kcmktalkd/Makefile.am
new file mode 100644
index 00000000..21fddc81
--- /dev/null
+++ b/ktalkd/kcmktalkd/Makefile.am
@@ -0,0 +1,15 @@
+xdg_apps_DATA = kcmktalkd.desktop
+KDE_ICON = ktalkd
+
+INCLUDES = $(all_includes)
+METASOURCES=AUTO
+
+kde_module_LTLIBRARIES = kcm_ktalkd.la
+
+kcm_ktalkd_la_SOURCES = main.cpp soundpage.cpp answmachpage.cpp \
+ forwmachpage.cpp
+kcm_ktalkd_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module
+kcm_ktalkd_la_LIBADD = $(LIB_KDEUI) $(LIB_KIO)
+
+#for extra warnings during compilation :
+# AMDEFS = -ansi -pedantic -D_POSIX_SOURCE -D_BSD_SOURCE
diff --git a/ktalkd/kcmktalkd/answmachpage.cpp b/ktalkd/kcmktalkd/answmachpage.cpp
new file mode 100644
index 00000000..5df2bee3
--- /dev/null
+++ b/ktalkd/kcmktalkd/answmachpage.cpp
@@ -0,0 +1,265 @@
+/*
+ * answmachpage.cpp - Answering machine settings for KTalkd
+ *
+ * Copyright (C) 1998 David Faure, faure@kde.org
+ *
+ * Requires the Qt widget libraries, available at no cost at
+ * http://www.troll.no/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "answmachpage.h"
+
+
+#include <ksimpleconfig.h>
+
+#include <stdlib.h>
+#include <klocale.h> // for getenv
+
+KAnswmachPageConfig::KAnswmachPageConfig( QWidget *parent, const char* name,
+ KSimpleConfig *_config)
+ : KCModule (parent, name)
+{
+ if (!_config) {
+ delete_config = true;
+ config = new KSimpleConfig("ktalkdrc");
+ }
+ else {
+ delete_config = false;
+ config = _config;
+ }
+
+ answmach_cb = new QCheckBox(i18n("&Activate answering machine"), this);
+ answmach_cb->adjustSize();
+ mail_edit = new QLineEdit(this);
+ mail_edit->adjustSize();
+ mail_edit->setMinimumWidth(150);
+ mail_label = new QLabel(mail_edit,i18n("&Mail address:"),this);
+ mail_label->adjustSize();
+ mail_label->setAlignment( ShowPrefix | AlignVCenter );
+
+ subj_edit = new QLineEdit(this);
+ subj_edit->adjustSize();
+ subj_edit->setMinimumWidth(150);
+ subj_label = new QLabel(subj_edit, i18n("Mail s&ubject:"),this);
+ subj_label->adjustSize();
+ subj_label->setAlignment( ShowPrefix | AlignVCenter );
+ subj_tip = new QLabel(i18n("Use %s for the caller name"),this);
+ subj_tip->setAlignment( ShowPrefix );
+
+ head_edit = new QLineEdit(this);
+ head_edit->adjustSize();
+ head_edit->setMinimumWidth(150);
+ head_label = new QLabel(head_edit, i18n("Mail &first line:"),this);
+ head_label->adjustSize();
+ head_label->setAlignment( ShowPrefix | AlignVCenter );
+ head_tip = new QLabel(
+ i18n("Use first %s for caller name, and second %s for caller hostname"),
+ this);
+ head_tip->setAlignment( ShowPrefix );
+
+ emptymail_cb = new QCheckBox(i18n("&Receive a mail even if no message left"), this);
+ emptymail_cb->adjustSize();
+
+ msg_ml = new QMultiLineEdit(this);
+ msg_ml->adjustSize();
+ msg_ml->setMinimumWidth(150);
+ msg_label = new QLabel(msg_ml, i18n("&Banner displayed on answering machine startup:"),this);
+ msg_label->adjustSize();
+ msg_label->setAlignment( ShowPrefix | AlignVCenter );
+
+ int h = 10 + answmach_cb->height() + mail_edit->height() +
+ subj_edit->height() + subj_tip->height() + head_edit->height()
+ + head_tip->height() + emptymail_cb->height() +
+ msg_label->height() + msg_ml->height() + 30;
+ setMinimumSize(400, h); // 400 : otherwise, buttons may overlap
+ msg_default = new QString(i18n("The person you are asking to talk with is not answering.\n"
+"Please leave a message to be delivered via email.\n"
+"Just start typing and when you have finished, exit normally."));
+
+ load();
+
+ connect(answmach_cb, SIGNAL(clicked()), this, SLOT(answmachOnOff()));
+
+ // Emit changed(true) when anything changes
+ connect(answmach_cb, SIGNAL(clicked()), this, SLOT(slotChanged()));
+ connect(mail_edit, SIGNAL(textChanged(const QString&)),
+ this, SLOT(slotChanged()));
+ connect(subj_edit, SIGNAL(textChanged(const QString&)),
+ this, SLOT(slotChanged()));
+ connect(head_edit, SIGNAL(textChanged(const QString&)),
+ this, SLOT(slotChanged()));
+ connect(emptymail_cb, SIGNAL(clicked()), this, SLOT(slotChanged()));
+ connect(msg_ml, SIGNAL(textChanged()), this, SLOT(slotChanged()));
+
+}
+
+KAnswmachPageConfig::~KAnswmachPageConfig( ) {
+ if (delete_config) delete config;
+ delete answmach_cb;
+ delete mail_label;
+ delete mail_edit;
+ delete subj_label;
+ delete subj_edit;
+ delete subj_tip;
+ delete head_label;
+ delete head_edit;
+ delete head_tip;
+ delete emptymail_cb;
+ delete msg_label;
+ delete msg_ml;
+ delete msg_default;
+}
+
+void KAnswmachPageConfig::slotChanged() {
+ emit changed(true);
+}
+
+void KAnswmachPageConfig::resizeEvent(QResizeEvent *) {
+
+ int h_txt = answmach_cb->height(); // taken for the general label height
+ int h_edt = mail_edit->height(); // taken for the general QLineEdit height
+ int spc = h_txt / 3;
+ int w = rect().width();
+
+ int leftedits = mail_label->width();
+ if ( subj_label->width() > leftedits )
+ leftedits = subj_label->width();
+ if ( head_label->width() > leftedits )
+ leftedits = head_label->width();
+ leftedits += 20;
+
+ int h = 10 + spc*2;
+ answmach_cb->move(10, h);
+ h += h_txt+spc;
+ mail_label->setFixedHeight(h_edt); // same size -> vertical center aligned
+ mail_label->move(10, h);
+ mail_edit->setGeometry(leftedits, h, w-leftedits-10, h_edt);
+ h += h_edt+spc;
+
+ subj_label->setFixedHeight(h_edt); // same size -> vertical center aligned
+ subj_label->move(10, h);
+ subj_edit->setGeometry(leftedits, h, w-leftedits-10, h_edt);
+ h += h_edt+spc;
+ subj_tip->setFixedWidth(w-20);
+ subj_tip->move(leftedits, h);
+ h += h_txt+spc;
+
+ head_label->setFixedHeight(h_edt); // same size -> vertical center aligned
+ head_label->move(10, h);
+ head_edit->setGeometry(leftedits, h, w-leftedits-10, h_edt);
+ h += h_edt+spc;
+ head_tip->setFixedWidth(w-20);
+ head_tip->move(leftedits, h);
+ h += h_txt+spc;
+
+ emptymail_cb->move(10,h);
+ h += h_txt+spc;
+
+ msg_label->move(10, h);
+ h += h_txt+spc;
+
+ msg_ml->setGeometry(10, h, w-20, height() - h - 10);
+ h += h_edt+spc;
+}
+
+void KAnswmachPageConfig::answmachOnOff()
+{
+ bool b = answmach_cb->isChecked();
+ mail_label->setEnabled(b);
+ mail_edit->setEnabled(b);
+ subj_label->setEnabled(b);
+ subj_edit->setEnabled(b);
+ subj_tip->setEnabled(b);
+ head_label->setEnabled(b);
+ head_edit->setEnabled(b);
+ head_tip->setEnabled(b);
+ emptymail_cb->setEnabled(b);
+ msg_label->setEnabled(b);
+ msg_ml->setEnabled(b);
+}
+
+void KAnswmachPageConfig::defaults() {
+
+ answmach_cb->setChecked(true);
+
+ mail_edit->setText(getenv("REPLYTO"));
+ subj_edit->setText(i18n("Message from %s"));
+ head_edit->setText(i18n("Message left on the answering machine, by %s@%s"));
+
+ emptymail_cb->setChecked(true);
+ msg_ml->setText(*msg_default);
+
+ // Activate things according to configuration
+ answmachOnOff();
+
+}
+
+void KAnswmachPageConfig::load() {
+
+ config->setGroup("ktalkd");
+
+ answmach_cb->setChecked(config->readBoolEntry("Answmach",true));
+
+ mail_edit->setText(config->readEntry("Mail",getenv("REPLYTO")));
+ subj_edit->setText(config->readEntry("Subj",i18n("Message from %s")));
+ head_edit->setText(config->readEntry("Head",
+ i18n("Message left on the answering machine, by %s@%s")));
+
+ emptymail_cb->setChecked(config->readBoolEntry("EmptyMail",true));
+
+ msg_ml->clear();
+ char m[]="Msg1"; // used as key to read configuration
+ QString msg;
+ while (!(msg=config->readEntry(m)).isNull())
+ {
+ msg_ml->append(msg);
+ ++m[3];
+ }
+
+ if (m[3]=='1') // nothing in the config file
+ msg_ml->setText(*msg_default);
+
+ // Activate things according to configuration
+ answmachOnOff();
+
+ emit changed(false);
+}
+
+void KAnswmachPageConfig::save() {
+
+ config->setGroup("ktalkd");
+ config->writeEntry("Answmach", answmach_cb->isChecked());
+ config->writeEntry("Mail",mail_edit->text());
+ config->writeEntry("Subj",subj_edit->text());
+ config->writeEntry("Head",head_edit->text());
+ config->writeEntry("EmptyMail", emptymail_cb->isChecked());
+ char m[]="Msg1"; // used as key to read configuration
+ int linenr=0;
+ QString msg;
+ while ((linenr<8) && (linenr<msg_ml->numLines()))
+ {
+ config->writeEntry(m,msg_ml->textLine(linenr));
+ ++m[3]; ++linenr;
+ }
+ config->deleteEntry(m,false/*non localized*/);
+ // necessary to avoid old msg lines to reappear after
+ // deleting some.
+
+ config->sync();
+}
+
+#include "answmachpage.moc"
diff --git a/ktalkd/kcmktalkd/answmachpage.h b/ktalkd/kcmktalkd/answmachpage.h
new file mode 100644
index 00000000..e1615364
--- /dev/null
+++ b/ktalkd/kcmktalkd/answmachpage.h
@@ -0,0 +1,83 @@
+/*
+ * answmachpage.h
+ *
+ * Copyright (c) 1998 David Faure
+ *
+ * Requires the Qt widget libraries, available at no cost at
+ * http://www.troll.no/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __KCONTROL_KANSWMACHPAGE_H__
+#define __KCONTROL_KANSWMACHPAGE_H__
+
+#include <qdir.h>
+/* has to be before everything because of #define Unsorted 0 in X11/X.h !! */
+
+#include <qobject.h>
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <qmultilineedit.h>
+#include <qpushbutton.h>
+#include <qbutton.h>
+#include <qcheckbox.h>
+
+#include <kcmodule.h>
+
+class KSimpleConfig;
+
+class KAnswmachPageConfig : public KCModule
+{
+ Q_OBJECT
+
+public:
+ KAnswmachPageConfig( QWidget *parent=0, const char* name=0,
+ KSimpleConfig *config=0 );
+ ~KAnswmachPageConfig( );
+
+ void load();
+ void save();
+ void defaults();
+
+protected:
+ void resizeEvent(QResizeEvent *e);
+
+private slots:
+ void answmachOnOff();
+ void slotChanged();
+
+private:
+ KSimpleConfig *config;
+ bool delete_config;
+
+ QCheckBox *answmach_cb;
+ QLabel *mail_label;
+ QLineEdit *mail_edit;
+ QLabel *subj_label;
+ QLineEdit *subj_edit;
+ QLabel *subj_tip;
+ QLabel *head_label;
+ QLineEdit *head_edit;
+ QLabel *head_tip;
+ QCheckBox *emptymail_cb;
+ QLabel *msg_label;
+ QMultiLineEdit *msg_ml;
+
+ QString *msg_default;
+};
+
+#endif
+
diff --git a/ktalkd/kcmktalkd/cr128-app-ktalkd.png b/ktalkd/kcmktalkd/cr128-app-ktalkd.png
new file mode 100644
index 00000000..8d9c97f7
--- /dev/null
+++ b/ktalkd/kcmktalkd/cr128-app-ktalkd.png
Binary files differ
diff --git a/ktalkd/kcmktalkd/cr16-app-ktalkd.png b/ktalkd/kcmktalkd/cr16-app-ktalkd.png
new file mode 100644
index 00000000..fd36501c
--- /dev/null
+++ b/ktalkd/kcmktalkd/cr16-app-ktalkd.png
Binary files differ
diff --git a/ktalkd/kcmktalkd/cr22-app-ktalkd.png b/ktalkd/kcmktalkd/cr22-app-ktalkd.png
new file mode 100644
index 00000000..9ac54546
--- /dev/null
+++ b/ktalkd/kcmktalkd/cr22-app-ktalkd.png
Binary files differ
diff --git a/ktalkd/kcmktalkd/cr32-app-ktalkd.png b/ktalkd/kcmktalkd/cr32-app-ktalkd.png
new file mode 100644
index 00000000..896c9be3
--- /dev/null
+++ b/ktalkd/kcmktalkd/cr32-app-ktalkd.png
Binary files differ
diff --git a/ktalkd/kcmktalkd/cr48-app-ktalkd.png b/ktalkd/kcmktalkd/cr48-app-ktalkd.png
new file mode 100644
index 00000000..0ad792c7
--- /dev/null
+++ b/ktalkd/kcmktalkd/cr48-app-ktalkd.png
Binary files differ
diff --git a/ktalkd/kcmktalkd/forwmachpage.cpp b/ktalkd/kcmktalkd/forwmachpage.cpp
new file mode 100644
index 00000000..5d82afed
--- /dev/null
+++ b/ktalkd/kcmktalkd/forwmachpage.cpp
@@ -0,0 +1,183 @@
+/*
+ * forwmachpage.cpp - Forwarding machine settings for KTalkd
+ *
+ * Copyright (C) 1998 David Faure, faure@kde.org
+ *
+ * Requires the Qt widget libraries, available at no cost at
+ * http://www.troll.no/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "forwmachpage.h"
+
+#include <ksimpleconfig.h>
+#include <klocale.h>
+
+KForwmachPageConfig::KForwmachPageConfig( QWidget *parent, const char* name,
+ KSimpleConfig *_config)
+ : KCModule (parent, name)
+{
+ if (!_config) {
+ delete_config = true;
+ config = new KSimpleConfig("ktalkdrc");
+ }
+ else {
+ delete_config = false;
+ config = _config;
+ }
+ forwmach_cb = new QCheckBox(i18n("Activate &forward"), this);
+ forwmach_cb->adjustSize();
+ address_edit = new QLineEdit(this);
+ address_edit->adjustSize();
+ address_edit->setMinimumWidth(150);
+ address_label = new QLabel(address_edit,i18n("&Destination (user or user@host):"),this);
+ address_label->adjustSize();
+ address_label->setAlignment( ShowPrefix | AlignVCenter );
+
+ method_combo = new QComboBox(this);
+ method_combo->insertItem("FWA");
+ method_combo->insertItem("FWR");
+ method_combo->insertItem("FWT");
+ method_combo->adjustSize();
+ method_combo->setMinimumWidth(80);
+ method_label = new QLabel(method_combo, i18n("Forward &method:"),this);
+ method_label->adjustSize();
+ method_label->setAlignment( ShowPrefix | AlignVCenter );
+
+ expl_label = new QLabel(i18n(
+"FWA: Forward announcement only. Direct connection. Not recommended.\n\
+FWR: Forward all requests, changing info when necessary. Direct connection.\n\
+FWT: Forward all requests and handle the talk request. No direct connection.\n\
+\n\
+Recommended use: FWT if you want to use it behind a firewall (and if ktalkd\n\
+can access both networks). Otherwise choose FWR.\n\
+\n\
+See Help for further explanation.\n\
+"),this);
+ expl_label->adjustSize();
+
+ int h = 10 + forwmach_cb->height() + address_edit->height() +
+ method_combo->height()+expl_label->height()+30;
+ setMinimumSize(400, h); // 400: otherwise, buttons may overlap
+
+ load();
+
+ connect(forwmach_cb, SIGNAL(clicked()), this, SLOT(forwmachOnOff()));
+
+ // emit changed(true) on changes
+ connect(forwmach_cb, SIGNAL(clicked()), this, SLOT(slotChanged()));
+ connect(address_edit, SIGNAL(textChanged(const QString&)), this, SLOT(slotChanged()));
+ connect(method_combo, SIGNAL(activated(int)), this, SLOT(slotChanged()));
+}
+
+KForwmachPageConfig::~KForwmachPageConfig( ) {
+ if (delete_config) delete config;
+
+ /* I've been told that this is not necessary as
+ they will be deleted by Qt. But, well... */
+ delete forwmach_cb;
+ delete address_label;
+ delete address_edit;
+ delete method_label;
+ delete method_combo;
+ delete expl_label;
+}
+
+void KForwmachPageConfig::slotChanged() {
+ emit changed(true);
+}
+
+void KForwmachPageConfig::resizeEvent(QResizeEvent *) {
+ int h_txt = forwmach_cb->height(); // taken for the general label height
+ int h_edt = address_edit->height(); // taken for the general QLineEdit height
+ int spc = h_txt / 3;
+ int w = rect().width();
+
+ int h = 10 + spc*2;
+ forwmach_cb->move(10, h);
+ h += h_txt+spc;
+ address_label->setFixedHeight(h_edt); // same size -> vertical center aligned
+ address_label->move(10, h);
+ int w_label = address_label->width()+20;
+ address_edit->setGeometry(w_label, h, w-w_label-10, h_edt);
+ h += h_edt+spc;
+
+ method_label->setFixedHeight(h_edt); // same size -> vertical center aligned
+ method_label->move(10, h);
+ method_combo->move(w_label, h);
+ h += h_edt+spc;
+
+ expl_label->move(10,h);
+
+}
+
+void KForwmachPageConfig::forwmachOnOff() {
+ bool b = forwmach_cb->isChecked();
+ address_label->setEnabled(b);
+ address_edit->setEnabled(b);
+ method_label->setEnabled(b);
+ method_combo->setEnabled(b);
+ expl_label->setEnabled(b);
+}
+
+void KForwmachPageConfig::defaults() {
+
+ forwmach_cb->setChecked(false);
+ method_combo->setCurrentItem(1);
+ address_edit->setText("");
+
+ // Activate things according to configuration
+ forwmachOnOff();
+
+}
+
+void KForwmachPageConfig::load() {
+
+ config->setGroup("ktalkd");
+
+ QString forward = config->readEntry("Forward","unset");
+ forwmach_cb->setChecked(forward!="unset");
+ if (forward != "unset" )
+ address_edit->setText(forward);
+ else
+ address_edit->setText("");
+
+ QString forwardMethod = config->readEntry("ForwardMethod","FWR");
+ for (int i=0; i<method_combo->count(); i++)
+ if (forwardMethod == method_combo->text(i))
+ method_combo->setCurrentItem(i);
+
+ // Activate things according to configuration
+ forwmachOnOff();
+
+ emit changed(false);
+}
+
+void KForwmachPageConfig::save() {
+
+ config->setGroup("ktalkd");
+
+ if (forwmach_cb->isChecked())
+ {
+ config->writeEntry("Forward",address_edit->text());
+ } else
+ config->deleteEntry("Forward", false /*non localized*/);
+ config->writeEntry("ForwardMethod",method_combo->currentText());
+
+ config->sync();
+}
+
+#include "forwmachpage.moc"
diff --git a/ktalkd/kcmktalkd/forwmachpage.h b/ktalkd/kcmktalkd/forwmachpage.h
new file mode 100644
index 00000000..5db2a7ea
--- /dev/null
+++ b/ktalkd/kcmktalkd/forwmachpage.h
@@ -0,0 +1,75 @@
+/*
+ * forwmachpage.h
+ *
+ * Copyright (c) 1998 David Faure
+ *
+ * Requires the Qt widget libraries, available at no cost at
+ * http://www.troll.no/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __KCONTROL_KFORWMACHPAGE_H__
+#define __KCONTROL_KFORWMACHPAGE_H__
+
+#include <qdir.h>
+/* has to be before everything because of #define Unsorted 0 in X11/X.h !! */
+
+#include <qobject.h>
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <qcombobox.h>
+#include <qpushbutton.h>
+#include <qbutton.h>
+#include <qcheckbox.h>
+
+#include <kcmodule.h>
+
+class KSimpleConfig;
+
+class KForwmachPageConfig : public KCModule
+{
+ Q_OBJECT
+
+public:
+ KForwmachPageConfig( QWidget *parent=0, const char* name=0,
+ KSimpleConfig *config=0);
+ ~KForwmachPageConfig( );
+
+ void load();
+ void save();
+ void defaults();
+
+protected:
+ void resizeEvent(QResizeEvent *e);
+
+private slots:
+ void forwmachOnOff();
+ void slotChanged();
+
+private:
+ KSimpleConfig *config;
+ bool delete_config;
+
+ QCheckBox *forwmach_cb;
+ QLabel *address_label;
+ QLineEdit *address_edit;
+ QLabel *method_label;
+ QComboBox * method_combo;
+ QLabel *expl_label;
+};
+
+#endif
+
diff --git a/ktalkd/kcmktalkd/kcmktalkd.desktop b/ktalkd/kcmktalkd/kcmktalkd.desktop
new file mode 100644
index 00000000..22422eb4
--- /dev/null
+++ b/ktalkd/kcmktalkd/kcmktalkd.desktop
@@ -0,0 +1,207 @@
+[Desktop Entry]
+Icon=ktalkd
+Type=Application
+DocPath=kcontrol/kcmtalkd/index.html
+Exec=kcmshell kcmktalkd
+
+X-KDE-ModuleType=Library
+X-KDE-Library=ktalkd
+X-KDE-FactoryName=ktalkd
+
+Name=Local Network Chat
+Name[af]=Plaaslike Netwerk Gesels
+Name[ar]=تحادث عبر الشبكة المحلية
+Name[be]=Гутарка па мясцовай сетцы
+Name[bg]=Разговор в локална мрежа
+Name[bn]=স্থানীয় নেটওয়ার্ক চ্যাট
+Name[br]=Flapañ rouedad lec'hel
+Name[bs]=Chat u lokalnoj mreži
+Name[ca]=Xat de la xarxa local
+Name[cs]=Rozhovor v lokální síti
+Name[cy]=Sgwrs Rhwydwaith Lleol
+Name[da]=Lokalt netværk-chat
+Name[de]=Lokaler Netzwerk-Chat
+Name[el]=Συνομιλία στο τοπικό δίκτυο
+Name[eo]=Loka reta Babilejo
+Name[es]= Charla red local
+Name[et]=Kohtvõrgu chat
+Name[eu]=Sare lokaleko elkarrizketa
+Name[fa]=گپ شبکۀ محلی
+Name[fi]=Paikallinen verkkokeskustelu
+Name[fr]=Discussion sur Réseau local
+Name[gl]=Chat na rede local
+Name[he]=שיחות ברשת המקומית
+Name[hi]=स्थानीय नेटवर्क गपशप
+Name[hr]=Razgovor u lokalnoj mreži
+Name[hu]=Csevegés a helyi hálózaton
+Name[is]=Staðarnetsspjall
+Name[it]=Chat rete locale
+Name[ja]=ローカルネットワークチャット
+Name[ka]=ლოკალური ქსელის ჩატი
+Name[kk]=Жергілікті желідегі әңгіме дүкені
+Name[km]=ជជែក​កំសាន្ដ​ក្នុង​បណ្ដាញ​មូលដ្ឋាន
+Name[lt]=Vietinio tinklo pokalbiai
+Name[mk]=Разговор на локална мрежа
+Name[mn]=Дотоод сүлжээнд чалчих
+Name[ms]=Borak Jaringan Setempat
+Name[nb]=Lokalt nettverksprat
+Name[nds]=Lokaalnettwark-Klönen
+Name[ne]=स्थानीय सञ्जाल कुराकानी
+Name[nl]=Gesprek over lokaal netwerk
+Name[nn]=Lokal nettverksprat
+Name[nso]=Poledisano ya Kgokagano ya Selegae
+Name[pa]=ਲੋਕਲ ਨੈੱਟਵਰਕ ਗੱਲਬਾਤ
+Name[pl]=Pogawędka w sieci lokalnej
+Name[pt]=Conversação na Rede Local
+Name[pt_BR]=Bate-papo de Rede Local
+Name[ru]=Чат в локальной сети
+Name[se]=Báikkalaš fierpmádatbuillardeapmi
+Name[sk]=Rozhovor na lokálnej sieti
+Name[sl]=Klepet prek lokalnega omrežja
+Name[sr]=Ћаскање у локалној мрежи
+Name[sr@Latn]=Ćaskanje u lokalnoj mreži
+Name[sv]=Chatt via lokalt nätverk
+Name[ta]=உள்ளக பிணைய அரட்டை
+Name[tg]=Чати Шабакаи Маҳаллӣ
+Name[th]=การสนทนาบนเครือข่ายท้องถิ่น
+Name[tr]=Yerel Ağ Sohbeti
+Name[uk]=Балачка в локальній мережі
+Name[ven]=U davhidzana ha vhukwamani ha tsini
+Name[xh]=Umsebenzi womnatha Wobulali Wokuncokola
+Name[zh_CN]=局域网聊天
+Name[zh_HK]=區域網路聊天
+Name[zh_TW]=區域網路聊天室
+Name[zu]=Oluseduze Uxhumaniso olusakazekile
+
+Comment=Talk daemon configuration
+Comment[af]=Praat bediener opstelling
+Comment[ar]=اعدادات مراقب Talh
+Comment[az]=Talk quraşdırması
+Comment[be]=Настаўленне сервіса гутаркі
+Comment[bg]=Настройване на демона за разговор в локална мрежа
+Comment[bn]=টক(Talk) ডিমন কনফিগারেশন
+Comment[br]=Kefluniadur an diaoul Talk
+Comment[bs]=Podešavanje Talk demona
+Comment[ca]=Configuració del dimoni talk
+Comment[cs]=Nastavení talk démona
+Comment[cy]=Ffurfweddiad daemon sgwrs
+Comment[da]=Talkdæmon-indstilling
+Comment[de]=Einrichtung des Talk-Dienstes
+Comment[el]=Ρύθμιση του δαίμονα talk
+Comment[eo]=Agordo de la "Talk"-demono
+Comment[es]=Configuración del demonio Talk
+Comment[et]=Talk deemoni seadistamine
+Comment[eu]=Elkarrizketa deabruaren konfigurazioa
+Comment[fa]=پیکربندی شبح گفتگو
+Comment[fi]=Talk-palvelimen asetukset
+Comment[fr]=Configuration du démon Talk
+Comment[ga]=Cumraíocht deamhan comhrá
+Comment[gl]=Configuración do demo talk
+Comment[he]=שינוי הגדרות תהליך הרקע Talk
+Comment[hi]=टाक डेमन कॉन्फ़िगरेशन
+Comment[hr]=Postava talk daemona
+Comment[hu]=A Talk szolgáltatás beállításai
+Comment[id]=Konfigurasi daemon Talk
+Comment[is]=Stillingar samtalsþjóns
+Comment[it]=Configurazione demone talk
+Comment[ja]=Talk デーモンの設定
+Comment[ka]=საუბრის დემონის კონფიგურაცია
+Comment[kk]=Talk қызметінің параметрлері
+Comment[km]=ការ​កំណត់​រចនា​សម្ព័ន្ធ​ដេមិន​សន្ទនា
+Comment[ko]=이야기 데몬 설정
+Comment[lt]=Pokalbių tarnybos derinimas
+Comment[mk]=Конфигурација на даемонот за разговарање
+Comment[mn]=Talk демон тохируулга
+Comment[ms]=Penyelarasan daemon Talk
+Comment[mt]=Konfigurazzjoni tad-daemon Talk
+Comment[nb]=Oppsett av Talk-nissen
+Comment[nds]=Talkdämoon instellen
+Comment[ne]=डेइमन कन्फिगरेसन बोल्नुहोस्
+Comment[nl]=Talk-daemon instellen
+Comment[nn]=Oppsett av Talk-nissen
+Comment[nso]=Peakanyo ya daemon ya polelo
+Comment[pa]=ਗੱਲਬਾਤ ਡੈਮਨ ਸੰਰਚਨਾ
+Comment[pl]=Konfiguracja demona talk
+Comment[pt]=Configuração do servidor do talk
+Comment[pt_BR]=Configuração do servidor talk
+Comment[ro]=Configurează demonul "talk"
+Comment[ru]=Параметры службы Talk
+Comment[se]=Heivet talk-duogášprográmma
+Comment[sk]=Nastavenie talk démona
+Comment[sl]=Nastavitev strežnika za talk
+Comment[sr]=Подешавање „Talk“ демона
+Comment[sr@Latn]=Podešavanje „Talk“ demona
+Comment[sv]=Anpassa talk-demon
+Comment[ta]=Talk daemon வடிவமைப்பு
+Comment[tg]=Батанзимдарории азозили Talk
+Comment[th]=ปรับแต่งเซิร์ฟเวอร์ Talk
+Comment[tr]=Talk sunucu programı yapılandırması
+Comment[uk]=Налаштування демону Talk
+Comment[ven]=Nzudzanyo ya daemon yau amba
+Comment[xh]=Uqwalaselo lwenkcoko ye daemon
+Comment[zh_CN]=Talk 守护进程配置
+Comment[zh_HK]=Talk 系統程式設定
+Comment[zh_TW]=Talk 伺服程式組態
+Comment[zu]=Inhlanganiselo lwedaemon yenkulumo
+
+Keywords=talk,announcement,client,sound,answering,mail,caller,banner,forward,destination
+Keywords[af]=talk,announcement,client,sound,answering,mail,caller,banner,forward,bestemming
+Keywords[ar]=تحدث,اعلان,عميل,صوت,استجابة,بريد,متصل,شعار,ارسال,اتجاه
+Keywords[az]=talk,e'lan,alıcı,Səs,cavablama,poçt,çağıran,bayraq,yolla,hədəf
+Keywords[be]=гутарка,кліент,гук,адказ,прызначэнне,talk,announcement,client,sound,answering,mail,caller,banner,forward,destination
+Keywords[bg]=разговор, локална, мрежа, чат, клиент, колега, talk, announcement, client, sound, answering, mail, caller, banner, forward, destination
+Keywords[bs]=talk,announcement,client,sound,answering,mail,caller,banner,forward,destination,zvuk,klijent,odgovor,odredište
+Keywords[ca]=parlar,notificació,client,so,contestar,correu,qui truca,rètol,enviar,destí
+Keywords[cs]=Talk,Rozhovor,Oznámení,Klient,Zvuk,Odpovídání,Pošta,Volající,Banner,Předání,Určení
+Keywords[cy]=siarad,cyhoeddiad,dibynnydd,swn,ateb,ebost,ymwelwr,baner,ymlaen,cyrchfan
+Keywords[da]=talk,annoncering,klient,lyd,svar,post,opringer,banner,fremad,destination
+Keywords[de]=Talk,Ankündigung,Client,Sound,Antwort,Mail,Banner,Forward,Weiterleitung
+Keywords[el]=ομιλία,ανακοίνωση,πελάτης,ήχος,απάντηση,mail,καλών,banner,προώθηση,προορισμός
+Keywords[eo]=parolo,anonco,babilo,kliento,sonoro,respondo,retpoŝto,alvokanto,flago,plusendo,celo
+Keywords[es]=hablar,notificación,cliente,sonido,contestar,correo,llamador,rótulo,enviar,destino
+Keywords[et]=talk,teadaanne,klient,heli,vastamine,meil,helistaja,bänner,edasisuunamine, adressaat
+Keywords[eu]=elkarrizketa,berri-ematea,bezeroa,soinua,erantzungailu automatikoa,posta,deitzailea,titularra,birbidali,helburua
+Keywords[fa]=گفتگو، اعلان، کارخواه، صوت، پاسخ، نامه، شمارۀ گیرنده، عنوان‌سازی، پیش‌سو، مقصد
+Keywords[fi]=talk,julkistus,asiakas,ääni,vastaus,sähköposti,soittaja,mainos,uudelleenohjaa,kohde
+Keywords[fr]=talk,discussion,annonce,bavardage,client,son,réponse,mail,courrier électronique,message,appel,bannière,envoi,destination
+Keywords[gl]=talk,anuncio,cliente,son,resposta,correo
+Keywords[he]=שיחות,הכרזה,לקוח,צליל,תשובה,דואר,מתקשר,כתובית,העברה,יעד,talk,announcement,client,sound,answering,mail,caller,banner,forward,destination
+Keywords[hi]=टाक,घोषणा,क्लाएंट,ध्वनि,उत्तर,मेल,कालर,ध्वज,फारवर्ड,गंतव्य
+Keywords[hu]=talk,bejelentés,kliens,hang,válasz,e-mail,hívó,üdvözlő szöveg,továbbítás,cél
+Keywords[is]=talk,tilkynningar,biðlari,hljóð,svörun,svara,póstur,mail,client,sound,answering,mail,caller,banner,forward,destination
+Keywords[it]=talk,annuncio,client,suono,risposte,posta,chiamante,messaggio,inoltra,destinazione
+Keywords[ja]=トーク,アナウンス,クライアント,音,answering,メール,呼出側,バナー,前へ,行き先
+Keywords[km]=ជជែក​គ្នា,ប្រកាស,ភ្ញៀវ,សំឡេង,ឆ្លើយឆ្លង,សំបុត្រ,អ្នកហៅ,បដា,ទៅ​មុខ,ទិសដៅ
+Keywords[ko]=talk,announcement,client,sound,answering,mail,caller,banner,forward,destination,이야기,토크,톡,알림,알려줌,클라이언트,소리,답신,댓거리,답신,편지,부른이,콜,깃발,기,배너,전달,포워드
+Keywords[lt]=talk,announcement,client,sound,answering,mail,caller,banner,forward,destination,kalbėti,kalbėtis,pokalbis,anonsas,pranešimas,paskelbimas,panešti,paskelbti,klientas,garsas,atsakyti,atsakymas,atsiliepti,paštas,laiškas,skambintojas,skydelis,persiųsti,persiuntimas,gavėjas
+Keywords[lv]=runa,paziņojums,klients,skaņa,atbildēšana,pasts,saucējs,banners,pārsūtīt,adresāts
+Keywords[mk]=talk,announcement,client,sound,answering,mail,caller,banner,forward,destination,разговарање,објавување,клиент,звук,одговор,пошта,повикувач,банер,препрати,одредиште
+Keywords[ms]=cakap,pengumuman,klien,bunyi,jawapan,mel,pemanggil,pemidang,destinasi,talk,announcement,client,sound,answering,mail,caller,banner,forward,destination
+Keywords[mt]=talk,annunzja,announcement,client,sound,answering,mail,caller,banner,forward,destination
+Keywords[nb]=talk,kunngjøring,klient,lyd,svar,post,oppringing,fane,videresend,mål
+Keywords[nds]=talk,Mellen,client,Klang,antern,Antermaschien,Nettpost,Anroper,Startnaricht,wiederledden,Teel
+Keywords[ne]=बोलचाल,घोषणा,क्लाइन्ट,ध्वनि,जवाफ,मेल,कलर,ब्यानर,अगाडि,गन्तब्य
+Keywords[nl]=talk,aankondiging,client,sound,geluid,beantwoorden,mail,caller,banner,banier,bestemming,forward,doorsturen,destination,antwoordapparaat
+Keywords[nn]=talk,kunngjering,klient,lyd,svar,post,oppringing,vidaresend,mål
+Keywords[nso]=polelo,tsebiso,moreki,lesata,araba,poso,mmitsi,banner,pele,mafelelo a leeto
+Keywords[pl]=rozmowa,ogłoszenie,klient,dźwięk,odpowiadanie,mail,dzwoniący,banner,podaj dalej,cel
+Keywords[pt]=talk,anúncio,cliente,som,resposta,correio,chamador,mensagem,reenviar,destino
+Keywords[pt_BR]=talk,anúncio,cliente,som,resposta,e-mail,correio,chamador,banner,repassar, destino
+Keywords[ro]=convorbire,talk,anunţ,client,sunet,răspuns,apel,mail,apelant,apelat,înaintare,destinatar
+Keywords[se]=talk,almmuheapmi,klienta,jietna,vástideapmi,boasta,sádde viidáset,ulbmil
+Keywords[sk]=talk,oznámenie, klient,zvuk,odpoveď,mail,pošta,volajúci,cieľ,predať ďalej
+Keywords[sl]=talk,govor,objava,odjemalec,zvok,odgovarjanje,pošta,klicatelj,reklama,posredovanje,cilj
+Keywords[sr]=talk,announcement,client,sound,answering,mail,caller,banner,forward,destination,разговор,објава,клијент,звук,одговор,одговарање,пошта,позивалац,прослеђивање,одредиште
+Keywords[sr@Latn]=talk,announcement,client,sound,answering,mail,caller,banner,forward,destination,razgovor,objava,klijent,zvuk,odgovor,odgovaranje,pošta,pozivalac,prosleđivanje,odredište
+Keywords[sv]=talk,annonsering,klient,ljud,svar,e-post,uppringare,rubrik,vidarebefordran,destination
+Keywords[ta]=பேச்சு, அறிவிப்பு, கிளையன், ஒலி, பதிலளித்தல், மின்னஞ்சல், அழைப்பு, பேனர், முன்னனுப்பு, சேருமிடம்
+Keywords[tr]=talk,duyuru,istemci,Ses,cevaplama,posta,çağıran,bayrak,gönder,hedef
+Keywords[uk]=розмова,оголошення,клієнт,звук,відповідь,пошта,заставка,переслати,talk,призначення
+Keywords[ven]=amba,divhadza,mushumisani,mubvumo,phindulo,zwamarifhi,muvhidzi,mupandeli,phanda, vhuyo
+Keywords[xh]=ncokola,isaziso,umxhasi,isandi,iyaphendula,iposi,umbizi,iceba lelaphu elinomyalezo,phambili,indawo ephelela kuyo
+Keywords[zh_CN]=talk,announcement,client,sound,answering,mail,caller,banner,forward,destination,交谈,通知,客户程序,声音,应答,邮件,提示,呼叫者,转发,目标
+Keywords[zh_HK]=talk,announcement,client,sound,answering,mail,caller,banner,forward,destination,聊天,公佈,客戶端,聲音,答覆,郵件,呼叫者,目的地
+Keywords[zh_TW]=talk,announcement,client,sound,answering,mail,caller,banner,forward,destination,聊天,公佈,客戶端,聲音,答覆,郵件,呼叫者,目的地
+Keywords[zu]=khuluma,isaziso,umthengi,umsindo,iyaphendula,iposi,umbizi,ibhodi elinomyalezo,phambili,indawo ophikekelekuyo
+
+Categories=Qt;KDE;X-KDE-settings-network;Settings;
diff --git a/ktalkd/kcmktalkd/main.cpp b/ktalkd/kcmktalkd/main.cpp
new file mode 100644
index 00000000..f43fecff
--- /dev/null
+++ b/ktalkd/kcmktalkd/main.cpp
@@ -0,0 +1,113 @@
+/*
+ main.cpp - The KControl module for ktalkd
+
+ Copyright (C) 1998 by David Faure, faure@kde.org
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+
+#include "main.h"
+#include "soundpage.h"
+#include "answmachpage.h"
+#include "forwmachpage.h"
+#include <ksimpleconfig.h>
+#include <klocale.h>
+#include <kglobal.h>
+#include <qtabwidget.h>
+#include <qlayout.h>
+
+KTalkdConfigModule::KTalkdConfigModule(QWidget *parent, const char *name)
+ : KCModule(parent, name)
+{
+ config = new KSimpleConfig("ktalkdrc");
+ announceconfig = new KSimpleConfig("ktalkannouncerc");
+
+ QVBoxLayout *layout = new QVBoxLayout(this);
+
+ tab = new QTabWidget(this);
+
+ layout->addWidget(tab);
+
+ soundpage = new KSoundPageConfig(this, "soundpage", config, announceconfig);
+ answmachpage = new KAnswmachPageConfig(this, "answmachpage", config);
+ forwmachpage = new KForwmachPageConfig(this, "forwmachpage", config);
+
+ tab->addTab(soundpage, i18n("&Announcement"));
+ tab->addTab(answmachpage, i18n("Ans&wering Machine"));
+ tab->addTab(forwmachpage, i18n("forward call", "&Forward"));
+
+ connect(soundpage, SIGNAL(changed(bool)), this, SIGNAL(changed(bool)));
+ connect(answmachpage, SIGNAL(changed(bool)), this, SIGNAL(changed(bool)));
+ connect(forwmachpage, SIGNAL(changed(bool)), this, SIGNAL(changed(bool)));
+ }
+
+KTalkdConfigModule::~KTalkdConfigModule() {
+ delete config;
+ delete announceconfig;
+ }
+
+void KTalkdConfigModule::defaults()
+{
+ if (soundpage) soundpage->defaults();
+ if (answmachpage) answmachpage->defaults();
+ if (forwmachpage) forwmachpage->defaults();
+}
+
+void KTalkdConfigModule::save()
+{
+ if (soundpage) soundpage->save();
+ if (answmachpage) answmachpage->save();
+ if (forwmachpage) forwmachpage->save();
+}
+
+void KTalkdConfigModule::load()
+{
+ if (soundpage) soundpage->load();
+ if (answmachpage) answmachpage->load();
+ if (forwmachpage) forwmachpage->load();
+}
+
+void KTalkdConfigModule::resizeEvent(QResizeEvent *)
+{
+ tab->setGeometry(0,0,width(),height());
+}
+
+extern "C"
+{
+ KDE_EXPORT KCModule *create_ktalkd(QWidget *parent, const char *)
+ {
+ return new KTalkdConfigModule(parent, "kcmktalkd");
+}
+
+ KDE_EXPORT KCModule *create_ktalkd_answmach(QWidget *parent, const char *)
+{
+ return new KAnswmachPageConfig(parent, "kcmktalkd");
+ }
+
+ KDE_EXPORT KCModule *create_ktalkd_sound(QWidget *parent, const char *)
+ {
+ return new KSoundPageConfig(parent, "kcmktalkd");
+ }
+
+ KDE_EXPORT KCModule *create_ktalkd_forwmach(QWidget *parent, const char *)
+ {
+ return new KForwmachPageConfig(parent, "kcmktalkd");
+ }
+}
+
+#include "main.moc"
+
diff --git a/ktalkd/kcmktalkd/main.h b/ktalkd/kcmktalkd/main.h
new file mode 100644
index 00000000..4b7ec823
--- /dev/null
+++ b/ktalkd/kcmktalkd/main.h
@@ -0,0 +1,64 @@
+/*
+ main.cpp - The KControl module for ktalkd
+
+ Copyright (C) 1998 by David Faure, faure@kde.org
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef __kcmktalkd_main_h
+#define __kcmktalkd_main_h
+
+#include <kcmodule.h>
+
+class QTabWidget;
+
+class KSimpleConfig;
+
+class KSoundPageConfig;
+class KAnswmachPageConfig;
+class KForwmachPageConfig;
+
+class KTalkdConfigModule : public KCModule
+{
+ Q_OBJECT
+
+public:
+
+ KTalkdConfigModule(QWidget *parent, const char *name);
+ virtual ~KTalkdConfigModule();
+
+ //void init();
+ void load();
+ void save();
+ void defaults();
+
+protected:
+ void resizeEvent(QResizeEvent *);
+
+private:
+ KSimpleConfig *config;
+ KSimpleConfig *announceconfig;
+
+ QTabWidget *tab;
+
+ KSoundPageConfig *soundpage;
+ KAnswmachPageConfig *answmachpage;
+ KForwmachPageConfig *forwmachpage;
+};
+
+#endif
+
diff --git a/ktalkd/kcmktalkd/soundpage.cpp b/ktalkd/kcmktalkd/soundpage.cpp
new file mode 100644
index 00000000..bbd2431c
--- /dev/null
+++ b/ktalkd/kcmktalkd/soundpage.cpp
@@ -0,0 +1,330 @@
+/*
+ * soundpage.cpp - Sound settings for KTalkd
+ *
+ * Copyright (C) 1998 David Faure, faure@kde.org
+ *
+ * Requires the Qt widget libraries, available at no cost at
+ * http://www.troll.no/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "soundpage.h"
+#include <config.h>
+
+#include <stdlib.h> //for setenv
+
+#include <qgroupbox.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qlistbox.h>
+#include <qcheckbox.h>
+
+#include <klineedit.h>
+#include <ksimpleconfig.h>
+#include <kstandarddirs.h>
+#include <klocale.h>
+#include <kaudioplayer.h>
+#include <kmessagebox.h>
+#include <kurlrequester.h>
+#include <kurldrag.h>
+#include <kdialog.h>
+
+/* Lots of stuff taken from syssound.cpp */
+
+KSoundPageConfig::KSoundPageConfig( QWidget *parent, const char* name,
+ KSimpleConfig *_config, KSimpleConfig *_announceconfig)
+ : KCModule (parent, name)
+{
+ if (!_config) {
+ delete_config = true;
+ config = new KSimpleConfig("ktalkdrc");
+ announceconfig = new KSimpleConfig("");
+ }
+ else {
+ delete_config = false;
+ config = _config;
+ announceconfig = _announceconfig;
+ }
+
+ QBoxLayout* toplay = new QVBoxLayout(this, KDialog::marginHint(),
+ KDialog::spacingHint() );
+
+ QGroupBox* extprg_box = new QGroupBox(this);
+ extprg_box->setColumnLayout( 0, Qt::Horizontal );
+ toplay->addWidget(extprg_box);
+
+ QGridLayout* l = new QGridLayout(extprg_box->layout());
+
+ extprg_edit = new KURLRequester(extprg_box);
+ l->addWidget(extprg_edit, 2, 4);
+
+ extprg_label = new QLabel(extprg_edit,i18n("&Announcement program:"), extprg_box);
+ l->addWidget(extprg_label, 2, 2);
+
+ client_edit = new KURLRequester(extprg_box);
+ l->addWidget(client_edit, 4, 4);
+
+ client_label = new QLabel(client_edit,i18n("&Talk client:"), extprg_box);
+ l->addWidget(client_label, 4, 2);
+
+ toplay->addSpacing(10);
+
+ sound_cb = new QCheckBox(i18n("&Play sound"), this);
+ toplay->addWidget(sound_cb);
+
+ QGroupBox* sound_box = new QGroupBox(this);
+ toplay->addWidget(sound_box);
+
+ QBoxLayout* lay = new QVBoxLayout(sound_box, 10, 10);
+
+ int edit_h = client_edit->height(); // The height of a QLineEdit
+
+ sound_list = new QListBox(sound_box);
+ sound_list->setMinimumHeight( 3 * edit_h );
+ sound_list->setAcceptDrops(true);
+ sound_list->installEventFilter(this);
+
+ sound_label = new QLabel(sound_list,i18n("&Sound file:"), sound_box);
+ lay->addWidget(sound_label);
+
+ QBoxLayout* l2 = new QHBoxLayout(lay, 10);
+ l2->addWidget(sound_list);
+
+ btn_test = new QPushButton(i18n("&Test"), sound_box);
+ l2->addWidget(btn_test);
+
+ sound_tip = new QLabel(
+ i18n("Additional WAV files can be dropped onto the sound list."),
+ sound_box);
+
+ lay->addWidget(sound_tip);
+
+ QStringList strlist( KGlobal::dirs()->findAllResources( "sound" ) );
+ sound_list->insertStringList( strlist );
+
+ load();
+
+ connect(sound_cb, SIGNAL(clicked()), this, SLOT(soundOnOff()));
+ connect(btn_test, SIGNAL(clicked()), this, SLOT(playCurrentSound()));
+
+ // emit changed(true) on changes
+ connect(extprg_edit->lineEdit(), SIGNAL(textChanged(const QString&)), this, SLOT(slotChanged()));
+ connect(client_edit->lineEdit(), SIGNAL(textChanged(const QString&)), this, SLOT(slotChanged()));
+}
+
+KSoundPageConfig::~KSoundPageConfig( ) {
+ if (delete_config) {
+ delete config;
+ delete announceconfig;
+ }
+ delete extprg_label;
+ delete extprg_edit;
+ delete client_label;
+ delete client_edit;
+ delete sound_cb;
+ delete sound_label;
+ delete sound_list;
+ delete sound_tip;
+ delete btn_test;
+
+}
+
+void KSoundPageConfig::slotChanged() {
+ emit changed(true);
+}
+
+bool KSoundPageConfig::eventFilter(QObject* /*o*/, QEvent* e)
+{
+ if (e->type() == QEvent::DragEnter) {
+ sound_listDragEnterEvent((QDragEnterEvent *) e);
+ return true;
+ }
+
+ if (e->type() == QEvent::Drop) {
+ sound_listDropEvent((QDropEvent *) e);
+ return true;
+ }
+
+ return false;
+}
+
+
+void KSoundPageConfig::sound_listDragEnterEvent(QDragEnterEvent* e)
+{
+ e->accept(KURLDrag::canDecode(e));
+}
+
+void KSoundPageConfig::sound_listDropEvent(QDropEvent* e){
+
+ KURL::List list;
+ // This should never happen, but anyway...
+ if(!KURLDrag::decode(e, list))
+ return;
+
+ // For now, we do only accept FILES ending with .wav...
+ for( KURL::List::ConstIterator it = list.begin();
+ it != list.end(); ++it)
+ {
+ const KURL &url = *it;
+
+ if (!url.isLocalFile()) { // for now, only file URLs are supported
+
+ KMessageBox::sorry(this,
+ i18n("This type of URL is currently unsupported "\
+ "by the KDE system sound module."),
+ i18n("Unsupported URL"));
+
+ }
+ else
+ { // Now check for the ending ".wav"
+
+ if (url.path().right(4).upper() != ".WAV") {
+ QString msg = i18n("%1\ndoes not appear "\
+ "to be a WAV file.").arg(url.path());
+
+ KMessageBox::sorry(this, msg, i18n("Improper File Extension"));
+
+ }
+ else
+ { // Hurra! Finally we've got a WAV file to add to the list
+
+ if (!addToSound_List(url.path())) {
+ // did not add file because it is already in the list
+ QString msg = i18n("The file %1 is already in the list").arg(url.path());
+
+ KMessageBox::information(this, msg, i18n("File Already in List"));
+
+ }
+ }
+ }
+ }
+}
+
+int KSoundPageConfig::findInSound_List(QString sound) {
+// Searches for <sound> in sound_list. Returns position or -1 if not found
+
+ bool found = false;
+
+ int i = 0;
+ int len = sound_list->count();
+
+ while ((!found) && (i < len)) {
+
+ found = sound == sound_list->text(i);
+ i++;
+ }
+ return (found ? i-1 : -1);
+}
+
+bool KSoundPageConfig::addToSound_List(QString sound){
+// Add "sound" to the sound list, but only if it is not already there
+
+ bool found = (findInSound_List(sound) != -1);
+ if (!found) { // Fine, the sound is not already in the sound list!
+
+ QString *tmp = new QString(sound); // take a copy...
+ sound_list->insertItem(*tmp);
+ sound_list->setTopItem(sound_list->count()-1);
+
+ slotChanged();
+ }
+
+ return !found;
+}
+
+void KSoundPageConfig::playCurrentSound()
+{
+ QString hlp, sname;
+ int soundno;
+
+ soundno = sound_list->currentItem();
+ if (soundno != -1) {
+ sname = sound_list->text(soundno);
+ if (sname[0] != '/')
+ KAudioPlayer::play(locate("sound", sname));
+ else
+ KAudioPlayer::play(sname);
+ }
+}
+
+void KSoundPageConfig::soundOnOff()
+{
+ bool b = sound_cb->isChecked();
+ sound_label->setEnabled(b);
+ sound_list->setEnabled(b);
+ btn_test->setEnabled(b);
+ sound_tip->setEnabled(b);
+
+ slotChanged();
+}
+
+void KSoundPageConfig::defaults() {
+
+ extprg_edit->lineEdit()->setText(KStandardDirs::findExe("ktalkdlg"));
+ client_edit->lineEdit()->setText(KStandardDirs::findExe("konsole")+" -e talk");
+ // will be ktalk when ktalk is in CVS.
+ sound_cb->setChecked(true);
+
+ // Activate things according to configuration
+ soundOnOff();
+}
+
+void KSoundPageConfig::load() {
+
+ config->setGroup("ktalkd");
+ announceconfig->setGroup("ktalkannounce");
+
+ setenv("KDEBINDIR",QFile::encodeName(KStandardDirs::kde_default("exe")),false/*don't overwrite*/);
+ // for the first reading of the config file
+
+ extprg_edit->lineEdit()->setText(config->readPathEntry("ExtPrg",
+ KStandardDirs::findExe("ktalkdlg")));
+ client_edit->lineEdit()->setText(announceconfig->readPathEntry("talkprg",
+ KStandardDirs::findExe("konsole")+" -e talk")); // will be ktalk when ktalk is in CVS
+
+ bool b = announceconfig->readBoolEntry("Sound",true/*default value*/);
+ sound_cb->setChecked(b);
+
+ const QString soundFile = announceconfig->readPathEntry("SoundFile");
+ if (!soundFile.isEmpty())
+ {
+ int pos = findInSound_List(soundFile);
+ if (pos != -1) sound_list->setSelected(pos,true);
+ else {
+ addToSound_List(soundFile);
+ sound_list->setSelected(sound_list->count()-1,true);
+ }
+ } else { sound_list->setSelected(0,true); }
+
+ // Activate things according to configuration
+ soundOnOff();
+
+ emit changed(false);
+}
+
+void KSoundPageConfig::save() {
+
+ config->setGroup("ktalkd");
+ config->writePathEntry("ExtPrg", extprg_edit->lineEdit()->text());
+ config->sync();
+ announceconfig->setGroup("ktalkannounce");
+ announceconfig->writePathEntry("talkprg", client_edit->lineEdit()->text());
+ announceconfig->writeEntry("Sound", sound_cb->isChecked());
+ announceconfig->writePathEntry("SoundFile",sound_list->text(sound_list->currentItem()));
+ announceconfig->sync();
+}
+
+#include "soundpage.moc"
diff --git a/ktalkd/kcmktalkd/soundpage.h b/ktalkd/kcmktalkd/soundpage.h
new file mode 100644
index 00000000..b5f713e4
--- /dev/null
+++ b/ktalkd/kcmktalkd/soundpage.h
@@ -0,0 +1,90 @@
+/*
+ * soundpage.h
+ *
+ * Copyright (c) 1998 David Faure
+ *
+ * Requires the Qt widget libraries, available at no cost at
+ * http://www.troll.no/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __KCONTROL_KSOUNDPAGE_H__
+#define __KCONTROL_KSOUNDPAGE_H__
+
+#include <qdir.h>
+/* has to be before everything because of #define Unsorted 0 in X11/X.h !! */
+
+#include <qobject.h>
+
+#include <kcmodule.h>
+
+
+class QDragMoveEvent;
+class QDragEnterEvent;
+class QDropEvent;
+
+class KSimpleConfig;
+class KURLRequester;
+class QCheckBox;
+class QLabel;
+class QListBox;
+class QPushButton;
+
+class KSoundPageConfig : public KCModule
+{
+ Q_OBJECT
+
+public:
+ KSoundPageConfig( QWidget *parent=0, const char* name=0,
+ KSimpleConfig *config=0, KSimpleConfig *announceconfig=0);
+ ~KSoundPageConfig( );
+
+ void load();
+ void save();
+ void defaults();
+
+ bool eventFilter(QObject* o, QEvent* e);
+
+private slots:
+ void soundOnOff();
+ void playCurrentSound();
+ void slotChanged();
+
+ // Sound DnD
+ void sound_listDragEnterEvent(QDragEnterEvent* e);
+ void sound_listDropEvent(QDropEvent* e);
+
+private:
+ KSimpleConfig *config;
+ KSimpleConfig *announceconfig;
+ bool delete_config;
+
+ QLabel *extprg_label;
+ KURLRequester *extprg_edit;
+ QLabel *client_label;
+ KURLRequester *client_edit;
+ QCheckBox *sound_cb;
+ QLabel *sound_label;
+ QListBox *sound_list;
+ QLabel *sound_tip;
+ QPushButton *btn_test;
+
+ int findInSound_List(QString sound);
+ bool addToSound_List(QString sound);
+};
+
+#endif
+
diff --git a/ktalkd/ktalkd/.talkdrc b/ktalkd/ktalkd/.talkdrc
new file mode 100644
index 00000000..b3804dc3
--- /dev/null
+++ b/ktalkd/ktalkd/.talkdrc
@@ -0,0 +1,78 @@
+# .talkdrc Config file for ktalkd when used without X/KDE.
+
+# If your E-Mail inbox is not <logid@localhost> (where logid is the
+# name you use to log in), uncomment the next line and place the
+# E-Mail address of your inbox there.
+#Mail:
+
+# Set to 1 to activate answering machine.
+# Will work only if your administrator has enabled it.
+Answmach: 1
+
+# Change this to customize the message displayed by the answering machine
+# when you receive a request while you're away. You may have up to 9 lines.
+Msg1: Hello. You're connected with the talk program answering machine.
+Msg2: I'm away from the computer at the moment.
+Msg3: Please leave a message and quit normally at the end of it.
+Msg4: - -
+Msg5: There's no way to delete across lines. Even if your talk program
+Msg6: allows you to cursor-around. Please use only normal keys and
+Msg7: backspace. Otherwise your note may be unreadable.
+
+# Subject of the mail you'll receive. '%s' will be replaced by the name of
+# the caller, qualified with their hostname.dom, presuming that they have
+# valid DNS.
+Subj: %s tried to "talk" you.
+
+# First line of the mail you'll receive. '%s' will be replaced by the
+# complete address of the caller.
+Head: Message left in the answering machine, by %s:
+
+# Do you wish to receive an empty mail if the caller didn't leave any message ?
+# (If "1", you'll only know who called you)
+EmptyMail: 1
+
+# Set this to 'off' if all you want is a beep to notify you of talk
+# requests, to 'on' if you want to play an audio file instead.
+Sound: on
+
+# Define this to the full path of the sound file you wish to
+# have played when you receive talk requests. It may be of
+# any format, as long as the player defined below is capable
+# of playing it.
+SoundFile: /usr/lib/talkd/talk.wav
+
+# Set this to the command you will be using to play audio
+# files. This can be any .wav, .au, .snd or whatever player,
+# just so long as it will play the format that your chosen
+# audio file is in.
+SoundPlayer: /usr/local/bin/wavplay
+SoundPlayerOpt: -q
+# ==> SoundPlayer + SoundPlayerOpt = /usr/local/bin/wavplay -q
+
+########### Edit below to set up a forward ###########
+
+# Enable forward by uncommenting and editing this line
+#Forward: user@host
+
+# Choose forward method :
+# None is perfect, they all have pros (+) and cons (-).
+#
+# FWA : Forward announcement only. Direct connection. Not recommended.
+# (+) You know who is the caller, but
+# (-) Caller will have to respond to an announcement from you. Annoying.
+# (-) Don't use if you have an answering machine on your 'away' location
+# (The answering machine can't popup an announcement, it would be confusing!)
+#
+# FWR : Forward all requests, changing info when necessary. Direct connection.
+# (+) Caller won't know that you're away, but
+# (-) You won't really know who's the caller - only his username,
+# (so you might see "talk from Wintalk@my_host")
+#
+# FWT : Forward all requests and take the talk. No direct connection.
+# (+) Same as above, but works also if you and caller can't be in direct
+# contact one with the other (e.g. firewall).
+# (+) You'll be told who's really talking to you when you accept the talk
+# (-) But as in FWR, you won't know his machine name in the announcement
+#
+#ForwardMethod: FWR
diff --git a/ktalkd/ktalkd/Makefile.am b/ktalkd/ktalkd/Makefile.am
new file mode 100644
index 00000000..7338b06f
--- /dev/null
+++ b/ktalkd/ktalkd/Makefile.am
@@ -0,0 +1,41 @@
+## -*- makefile -*-
+# Ktalkd - Makefile.am
+
+SUBDIRS = machines
+
+bin_PROGRAMS = ktalkd
+EXTRA_HEADERS = readcfg++.h
+INCLUDES = $(all_includes)
+ktalkd_DEPS = machines/libmach.a
+
+#for extra warnings during compilation :
+#KDE_CXXFLAGS = -ansi -pedantic -D_POSIX_SOURCE -D_BSD_SOURCE
+
+###########################################################
+
+# Config file location
+TALKD_CONF = $(kde_confdir)/ktalkdrc
+TALKD_CONF_NAME = ktalkdrc
+AM_CPPFLAGS = -DHAVE_KDE
+ktalkd_SOURCES = print.c repairs.c \
+ announce.cpp process.cpp readcfg++.cpp table.cpp talkd.cpp \
+ find_user.cpp threads.cpp options.cpp unixsock.cpp
+ktalkd_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+ktalkd_LDADD = machines/libmach.a $(LIBBSD) $(LIB_KDECORE) $(LIBSOCKET)
+
+###########################################################
+
+
+EXTRA_DIST = .talkdrc talkd.conf ktalkdrc
+
+install-data-local:
+ $(mkinstalldirs) $(DESTDIR)$(kde_confdir)
+ $(mkinstalldirs) $(DESTDIR)$(kde_sounddir)
+ $(INSTALL_DATA) $(srcdir)/ktalkd.wav $(DESTDIR)$(kde_sounddir)
+ @echo "**************************************************************************"
+ @echo
+ @if [ -f $(DESTDIR)$(TALKD_CONF) ]; then \
+ echo "Please check $(TALKD_CONF) to be up-to-date."; \
+ else \
+ $(INSTALL_DATA) $(srcdir)/$(TALKD_CONF_NAME) $(DESTDIR)$(TALKD_CONF); \
+ fi
diff --git a/ktalkd/ktalkd/announce.cpp b/ktalkd/ktalkd/announce.cpp
new file mode 100644
index 00000000..d21f27f4
--- /dev/null
+++ b/ktalkd/ktalkd/announce.cpp
@@ -0,0 +1,502 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+/* Autoconf: */
+#include <config.h>
+
+// strange symbol for HP-UX. It should not hurt on other systems.
+#ifndef notdef
+#define notdef 1
+#endif
+
+#include "includ.h"
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#include <time.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <errno.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <pwd.h>
+#include <netdb.h>
+#include "proto.h"
+#include "announce.h"
+#include "readconf.h"
+#include "defs.h"
+#include "threads.h"
+#include "unixsock.h"
+
+#ifdef HAVE_SGTTY_H
+#include <sgtty.h>
+#else
+#ifdef HAVE_BSD_SGTTY_H
+#include <bsd/sgtty.h>
+#endif
+#endif
+
+#define ktalkdmax(a,b) ( (a) > (b) ? (a) : (b) )
+#define N_LINES 5
+
+/*
+ * Announce an invitation to talk.
+ *
+ * Because the tty driver insists on attaching a terminal-less
+ * process to any terminal that it writes on, we must fork a child
+ * to protect ourselves
+ */
+int announce(NEW_CTL_MSG *request, const char *remote_machine, char *disp, int
+ usercfg, char * callee) {
+
+ int pid, status;
+ int returncode;
+
+ if (strstr(remote_machine,"\033")) {
+ syslog(LOG_WARNING,
+ "blocked VT100 FLASH BOMB to user: %s (from field in packet contains bomb)",
+ request->r_name);
+ } else if (strstr(request->l_name,"\033")) {
+ syslog(LOG_WARNING,
+ "blocked VT100 FLASH BOMB to user: %s (apparently from: %s)",
+ request->r_name, remote_machine);
+ } else {
+ if ((pid = fork())) {
+ /* we are the parent, so wait for the child */
+ if (pid == -1) /* the fork failed */
+ return (FAILED);
+ status = wait_process(pid);
+ if ((status&0377) > 0) /* we were killed by some signal */
+ return (FAILED);
+ /* Get the second byte, this is the exit/return code */
+ return ((status >> 8) & 0377);
+ }
+ /* we are the child, go and do it */
+
+ struct passwd * pw_buf;
+ /** Change our uid. Once and for all that follows. */
+ pw_buf = getpwnam(request->r_name);
+
+ /* XXX: Not good
+ * You should also set the supplementary groups using
+ * setgroups/initgroups
+ */
+ if (setgid(pw_buf -> pw_gid) || setuid(pw_buf -> pw_uid)) {
+ syslog(LOG_WARNING, "Warning: setgid/setuid as %s: %s", request->r_name, strerror(errno));
+ _exit(1);
+ }
+
+ /** Also set $HOME once and for all that follows (not used for text
+ announce, but doesn't harm) */
+ setenv("HOME", pw_buf -> pw_dir, 1);
+
+#ifdef HAVE_KDE
+ returncode = try_Xannounce(request, remote_machine, disp, usercfg, callee);
+ if (returncode != SUCCESS)
+#endif
+ returncode = print_std_mesg( request, remote_machine, usercfg, 0 );
+ _exit(returncode);
+
+ }
+ return (FAILED);
+}
+
+#ifdef HAVE_KDE
+
+int try_Xannounce(NEW_CTL_MSG *request, const char *remote_machine,
+ char *disp, int usercfg, char * callee) {
+ int readPipe[2];
+ char ch_aux;
+ int pid;
+ struct timeval clock;
+ struct timezone zone;
+ struct tm *localclock;
+ char line_buf[N_CHARS];
+
+ char * adisp_begin, *disp_ptr, * disp_end;
+ char extprg [S_MESSG]; /* Program used for notification. */
+ int Xannounceok = 0; /* Set to 1 if at least one Xannounceok succeeded */
+
+#ifdef PROC_FIND_USER
+ if ((Options.XAnnounce) && (strlen(disp) >= 2)) {
+#else
+ if ((Options.XAnnounce) && ((int) request->r_tty[3] > (int) 'f')) {
+#endif /* PROC_FIND_USER */
+
+ /*
+ * He is in X (probably :-) -> try to connect with external program
+ */
+
+ /* No more needed : ktalkdlg will play sound itself.
+ Other external programs can do whatever they want...
+ Maybe a config for this could be useful to know whether the
+ extprg handles the sound announcement...
+ sound_or_beep(usercfg); */
+
+ gettimeofday(&clock, &zone);
+ localclock = localtime( (const time_t *) &clock.tv_sec );
+ (void)snprintf(line_buf, N_CHARS, "%s@%s", request->l_name,
+ remote_machine);
+
+#ifndef DONT_TRY_KTALK // never defined. Define if you want...
+ Xannounceok = sendToKtalk ( request->r_name, line_buf );
+ if (!Xannounceok) {
+#endif
+ /* XXX: I assume extprg comes from a user config file? Then
+ * strcpy is quire a bad idea
+ */
+ if ((!usercfg) || (!read_user_config("ExtPrg", extprg, S_MESSG)))
+ /* try to read extprg from user config file, otherwise : */
+ strcpy(extprg,Options.extprg); /* take default */
+
+ /* disp can be a LIST of displays, such as ":0 :1", or
+ ":0 hostname:0". Let's announce on ALL displays found.
+ disp_end is the end of the display list (saved because we'll
+ insert zeros).
+ adisp_begin will point to a display in the list.
+ disp_ptr will go char by char through disp.*/
+
+ adisp_begin=disp;
+ disp_end=disp+strlen(disp);
+ for (disp_ptr=disp; disp_ptr<disp_end; disp_ptr++) {
+ if (*disp_ptr==' ') { /* the final display will be taken also,
+ as a final space has been inserted in
+ find_X_process */
+ *disp_ptr='\0'; /* mark the end of the display name in the
+ list */
+
+ pipe( readPipe );
+ switch (pid = fork()) {
+ case -1: {
+ syslog(LOG_ERR,"Announce : Fork failed ! - %s", strerror(errno));
+ return ( FAILED );
+ }
+ case 0: {
+
+ close(readPipe[0]); // we do not need to read the pipe in the child
+
+ /* set DISPLAY */
+ setenv("DISPLAY", adisp_begin, 1);
+
+ if (Options.debug_mode)
+ {
+ syslog(LOG_DEBUG, "Trying to run '%s' at '%s' as '%s'", extprg,
+ getenv("DISPLAY"), request->r_name );
+ if (callee) ktalk_debug("With mention of callee : %s",callee);
+ }
+ /*
+ * point stdout of external program to the pipe
+ * announce a talk request by execing external program
+ */
+ dup2( readPipe[1], STDOUT_FILENO );
+
+ if (callee)
+ execl( extprg, extprg, line_buf, callee, (void *)0 );
+ else
+ execl( extprg, extprg, line_buf, (void *)0 );
+
+ /*
+ * failure - tell it to father
+ * exit with status=42
+ */
+ syslog(LOG_WARNING, "error while execl(%s): %s", extprg, strerror(errno));
+ _exit( 42 );
+ }
+ default: {
+ /* Register the new process for waitpid */
+ new_process();
+ /*
+ * I don't know any way how to find that external program run ok
+ * so parent returns always SUCCESS
+ * Well I know a solution - read the pipe :-) - It blocks, so
+ * we can't wait the reaction of the user
+ *
+ * Danny: made the pipe non blocking and check for exit-status
+ */
+
+ int exited = 0, c = 0;
+ int status = -1; // Set to an unlikely value (no program returns -1, Right?)
+ ch_aux='0'; // initialise ch_aux
+
+ /* make the pipe non-blocking:
+ * does this work on systems other than BSD&Linux??
+ */
+ if (fcntl(readPipe[0], F_SETFL, O_NONBLOCK)== -1)
+ ktalk_debug("Failed to make pipe non-blocking!!");
+
+ /* Stop this loop when:
+ * the child exited with a value > 0 AND the pipe is empty
+ * Maybe we can add a time-out? (what is reasonable)
+ */
+ while (c > 0 || (exited < 1)){
+ if ( exited < 1 )
+ {
+ status = wait_process(pid);
+ exited += (WIFEXITED(status) || WIFSIGNALED(status)); // non-zero when exited OR non-caught signal
+ }
+ c = read( readPipe[0], &ch_aux, 1 ); // this is many times -1 because no data in pipe
+
+ if (ch_aux == '#') Xannounceok = 1;
+ }
+
+ /* Check whether the # was returned, if not, return the exit status
+ * 0 means: exit normally, but no # received-> do a text announce as well.
+ * 1 can be for instance: cannot open X display
+ * 42 is: failure to start external program.
+ */
+ if (Xannounceok == 1) {
+ ktalk_debug("OK - Child process responded");
+ } else
+ ktalk_debug("External program failed with exit status: %i", WEXITSTATUS(status));
+
+ // close the pipes: (is this necessary?):
+ close( readPipe[0] );
+ close( readPipe[1] );
+
+ } /* default */
+ } /* switch */
+ adisp_begin=disp_ptr+1;
+ } /* if */
+ } /* for */
+#ifndef DONT_TRY_KTALK
+ } // if
+#endif
+
+ if (Xannounceok) {
+#ifndef NO_TEXT_ANNOUNCE_IF_X
+ // never defined. Define it if you don't want text announce in
+ // addition to X announce
+ if ((request->r_tty[0]!='\0') && ((int)request->r_tty[3]<(int)'f')) {
+ // Not a pseudo terminal (such as an xterm, ...)
+ ktalk_debug("Doing also text announce on %s",request->r_tty);
+ print_std_mesg(request, remote_machine, usercfg, 1 /*force no sound*/);
+ }
+#endif
+ return (SUCCESS);
+ }
+ }
+ return (FAILED);
+}
+#endif /* HAVE_KDE */
+
+/*
+ * See if the user is accepting messages. If so, announce that
+ * a talk is requested.
+ */
+
+int print_std_mesg( NEW_CTL_MSG *request, const char *remote_machine, int
+ usercfg, int force_no_sound ) {
+
+ char full_tty[32];
+ FILE *tf;
+ struct stat stbuf;
+
+ (void)snprintf(full_tty, sizeof(full_tty), "%s/%s", _PATH_DEV, request->r_tty);
+ if (access(full_tty, 0) != 0)
+ return (FAILED);
+ if ((tf = fopen(full_tty, "w")) == 0)
+ return (PERMISSION_DENIED);
+ /*
+ * On first tty open, the server will have
+ * it's pgrp set, so disconnect us from the
+ * tty before we catch a signal.
+ */
+ ioctl(fileno(tf), TIOCNOTTY, (struct sgttyb *) 0);
+ if (fstat(fileno(tf), &stbuf) < 0)
+ return (PERMISSION_DENIED);
+ if ((stbuf.st_mode&020) == 0)
+ return (PERMISSION_DENIED);
+ print_mesg(tf, request, remote_machine, usercfg, force_no_sound);
+ fclose(tf);
+ return (SUCCESS);
+}
+
+/*
+ * Build a block of characters containing the message.
+ * It is sent blank filled and in a single block to
+ * try to keep the message in one piece if the recipient
+ * in in vi at the time
+ */
+void print_mesg(FILE * tf, NEW_CTL_MSG * request, const char *
+ remote_machine, int usercfg, int force_no_sound)
+{
+ struct timeval clock;
+ struct timezone zone;
+ struct tm *localclock;
+ char line_buf[N_LINES][N_CHARS];
+ char buffer [N_CHARS];
+ int sizes[N_LINES];
+ char big_buf[N_LINES*N_CHARS];
+ char *bptr, *lptr;
+ int i, j, max_size;
+
+ char * remotemach = new char[strlen(remote_machine)+1];
+ strcpy(remotemach,remote_machine);
+ /* We have to duplicate it because param_remote_machine is contained
+ in the hostent structure, and will be erased by gethostbyname. */
+
+ char localname[SYS_NMLN];
+ strcpy(localname,gethostbyname(Options.hostname)->h_name);
+ /* We have to duplicate localname also. Same reasons as above ! */
+
+ const char *remotename = gethostbyname(remotemach)->h_name;
+
+ i = 0;
+ max_size = 0;
+ gettimeofday(&clock, &zone);
+ localclock = localtime( (const time_t *) &clock.tv_sec );
+ (void)snprintf(line_buf[i], N_CHARS, " ");
+ sizes[i] = strlen(line_buf[i]);
+ max_size = ktalkdmax(max_size, sizes[i]);
+ i++;
+ (void)snprintf(line_buf[i], N_CHARS, Options.announce1,
+ localclock->tm_hour , localclock->tm_min );
+ sizes[i] = strlen(line_buf[i]);
+ max_size = ktalkdmax(max_size, sizes[i]);
+ i++;
+ snprintf(buffer, N_CHARS, "%s@%s", request->l_name, remotename);
+ snprintf(line_buf[i], N_CHARS, Options.announce2, buffer);
+ sizes[i] = strlen(line_buf[i]);
+ max_size = ktalkdmax(max_size, sizes[i]);
+ i++;
+
+ if (!(strcmp(localname,remotename))) {
+ snprintf(line_buf[i], N_CHARS, Options.announce3, request->l_name);
+ } else {
+ snprintf(line_buf[i], N_CHARS, Options.announce3, buffer);
+ }
+
+ sizes[i] = strlen(line_buf[i]);
+ max_size = ktalkdmax(max_size, sizes[i]);
+ i++;
+ (void)snprintf(line_buf[i], N_CHARS, " ");
+ sizes[i] = strlen(line_buf[i]);
+ sizes[i] = strlen(line_buf[i]);
+ max_size = ktalkdmax(max_size, sizes[i]);
+ i++;
+ bptr = big_buf;
+ if (!force_no_sound) /* set if a X announce has been done */
+ if (sound_or_beep(usercfg)) /* if no sound then : */
+ *bptr++ = ''; /* send something to wake them up */
+ *bptr++ = '\r'; /* add a \r in case of raw mode */
+ *bptr++ = '\n';
+ for (i = 0; i < N_LINES; i++) {
+ /* copy the line into the big buffer */
+ lptr = line_buf[i];
+ while (*lptr != '\0')
+ *(bptr++) = *(lptr++);
+ /* pad out the rest of the lines with blanks */
+ for (j = sizes[i]; j < max_size + 2; j++)
+ *(bptr++) = ' ';
+ *(bptr++) = '\r'; /* add a \r in case of raw mode */
+ *(bptr++) = '\n';
+ }
+ *bptr = '\0';
+ fprintf(tf, "%s", big_buf);
+ fflush(tf);
+ ioctl(fileno(tf), TIOCNOTTY, (struct sgttyb *) 0);
+ delete remotemach;
+}
+
+int play_sound(int usercfg)
+{
+ int pid, status, returncode;
+ char sSoundPlayer[S_CFGLINE], sSoundFile[S_CFGLINE];
+ char sSoundPlayerOpt[S_CFGLINE];
+ /* We must absolutely read the configuration before forking,
+ because the father will close the file soon !! */
+ if ((!usercfg) || (!read_user_config("SoundPlayer",sSoundPlayer,S_CFGLINE)))
+ strcpy(sSoundPlayer,Options.soundplayer);
+ ktalk_debug(sSoundPlayer);
+ if ((!usercfg) || (!read_user_config("SoundPlayerOpt",sSoundPlayerOpt,S_CFGLINE)))
+ strcpy(sSoundPlayerOpt,Options.soundplayeropt);
+ ktalk_debug(sSoundPlayerOpt);
+ if ((!usercfg) || (!read_user_config("SoundFile",sSoundFile,S_CFGLINE)))
+ strcpy(sSoundFile,Options.soundfile);
+ ktalk_debug(sSoundFile);
+
+ if ((pid = fork())) {
+ /* we are the parent, so wait for the child */
+ if (pid == -1) /* the fork failed */
+ {
+ syslog(LOG_ERR,"Fork before play_sound : FAILED.");
+ return (FAILED);
+ }
+ status = wait_process(pid);
+ if ((status&0377) > 0) /* we were killed by some signal */
+ return (FAILED);
+ /* Get the second byte, this is the exit/return code */
+ return ((status >> 8) & 0377);
+ }
+ /* we are the child, go and do it */
+
+ if (strlen(sSoundPlayerOpt)>0)
+ returncode = execl(sSoundPlayer,sSoundPlayer/*arg0*/,sSoundPlayerOpt,sSoundFile, (void *)0);
+ else
+ returncode = execl(sSoundPlayer,sSoundPlayer/*arg0*/,sSoundFile, (void *)0);
+
+ ktalk_debug(strerror(errno));
+ syslog(LOG_ERR,"ERROR PLAYING SOUND FILE !!!");
+ syslog(LOG_ERR, "%s,%s,%s",sSoundPlayer,sSoundFile,sSoundPlayerOpt);
+ syslog(LOG_ERR,"RETURN-CODE : %d",returncode);
+
+ _exit(returncode);
+ /*NOTREACHED*/
+ return 0;
+}
+
+/*
+ * Calls play_sound if necessary. Returns 1 if a tty beep is needed.
+ */
+int sound_or_beep(int usercfg)
+{
+ int bSound;
+ int ret;
+ if ((!usercfg) || (!read_bool_user_config("Sound",&bSound)))
+ bSound=Options.sound;
+
+ ktalk_debug("Sound option in sound_or_beep : %d",bSound);
+
+ if (bSound)
+ {
+ /* try to play sound, otherwise beeps (if not in X) */
+ ret = play_sound(usercfg); /* 1 = it didn't work. ^G needed */
+ }
+ else ret = 1; /* ^G needed */
+ return ret;
+}
diff --git a/ktalkd/ktalkd/announce.h b/ktalkd/ktalkd/announce.h
new file mode 100644
index 00000000..7411cdb4
--- /dev/null
+++ b/ktalkd/ktalkd/announce.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California, (c) 1997 David Faure
+ * All rights reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+#ifndef ANNOUNCE_H
+#define ANNOUNCE_H
+
+int announce(NEW_CTL_MSG * request, const char *remote_machine, char *disp, int usercfg, char * callee);
+
+#ifdef HAVE_KDE
+int try_Xannounce(NEW_CTL_MSG *request, const char *remote_machine,
+ char *disp, int usercfg, char * callee);
+#endif
+
+int print_std_mesg(NEW_CTL_MSG *request, const char *remote_machine, int usercfg, int force_no_sound);
+void print_mesg(FILE * tf,NEW_CTL_MSG * request, const char * remote_machine, int usercfg, int force_no_sound);
+int sound_or_beep(int usercfg);
+
+/* Maximum char length for announcement lines */
+#define N_CHARS 120
+
+#endif
diff --git a/ktalkd/ktalkd/defs.h b/ktalkd/ktalkd/defs.h
new file mode 100644
index 00000000..de4a96a9
--- /dev/null
+++ b/ktalkd/ktalkd/defs.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright 1999-2002 David Faure <faure@kde.org>
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+#ifndef __DEFS_H
+#define __DEFS_H
+
+#include "includ.h"
+
+/* Configuration file-read buffer sizes */
+#define S_CFGLINE 80
+
+ /* from answmach/io.c */
+#define S_MESSG 120
+#define S_COMMAND 100
+
+#define S_INVITE_LINES 200
+
+#define DISPLAYS_LIST_MAX 200
+#define DISPLAY_MAX 50
+
+/*
+ * Invitation displayed by the answering machine when the caller connects.
+ * Add \n at the end of any line you want it to display,
+ * and \ at the end of any line of the definition of INVITE_LINES
+ * They are not necessarily the same ...
+ *
+ * ANNOUNCE* are the 3 lines displayed on the terminal / on
+ * the console, to announce the request.
+ */
+
+#define ANNOUNCE1 "Message from Talk_Daemon at %d:%02d ..."
+#define ANNOUNCE2 "talk: connection requested by %s."
+#define ANNOUNCE3 "talk: respond with: talk %s"
+#define INVITE_LINES "Hello. You're connected to a talk answering \
+machine.\nThe person you have paged isn't there at the moment.\n\
+Please leave a message and quit normally when finished.\n"
+
+/*Please check that INVITE_LINES is max S_INVITE_LINES chars*/
+
+/* Default message if N.E.U (non existent user) called */
+#define NEU_BANNER1 "The person you're asking to talk with is unknown at this host."
+#define NEU_BANNER2 "You may have mistyped the name, or network address. Try again"
+#define NEU_BANNER3 "or leave a message which will be sent to the system administrator."
+
+/* return value from process_request : */
+#define PROC_REQ_OK 0
+#define PROC_REQ_ERR 1
+#define PROC_REQ_FORWMACH 2
+#define PROC_REQ_ANSWMACH 3
+#define PROC_REQ_ANSWMACH_NOT_LOGGED 4
+#define PROC_REQ_ANSWMACH_NOT_HERE 5
+
+/* Min value to launch answer machine : */
+#define PROC_REQ_MIN_A 3
+
+#include "options.h"
+
+#endif /* __DEFS_H */
diff --git a/ktalkd/ktalkd/find_user.cpp b/ktalkd/ktalkd/find_user.cpp
new file mode 100644
index 00000000..948a4dba
--- /dev/null
+++ b/ktalkd/ktalkd/find_user.cpp
@@ -0,0 +1,400 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+#include "includ.h"
+#include <sys/param.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#include <netdb.h>
+#include <syslog.h>
+#include <stdio.h>
+#include <string.h>
+#define USE_UT_HOST
+#ifndef UT_HOSTSIZE
+#define UT_HOSTSIZE 12 /*whatever*/
+#undef USE_UT_HOST
+#endif
+
+#include <pwd.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <fstream>
+
+#ifdef ALL_PROCESSES_AND_PROC_FIND_USER
+#include <dirent.h>
+#include <ctype.h>
+#include <errno.h>
+#endif
+
+#include "proto.h"
+#include "defs.h"
+
+#ifdef PROC_FIND_USER
+
+#define DISPLAYS_LIST_MAX 200
+#define DISPLAY_MAX 50
+
+/* get DISPLAY variable of a process */
+char *get_display(pid_t pid) {
+ static char buf[1024];
+
+ sprintf(buf, "/proc/%d/environ", pid);
+
+ std::ifstream ifstr(buf);
+ if (ifstr.fail()) return 0;
+ /* ktalk_debug("reading %s...", fname); */
+
+ char * dpy = 0;
+ while (!ifstr.eof()) {
+ ifstr.getline(buf, sizeof buf - 1, '\0');
+ if (ifstr.fail()) {
+ syslog(LOG_ERR, "getline: %s", strerror(errno));
+ return 0; /* read error */
+ }
+ if (!strncmp("DISPLAY=", buf, 8)) {
+ int l = strlen(buf + 8);
+ if( l >= DISPLAY_MAX )
+ return 0;
+ if (l >= 2 && l < (int) sizeof buf - 1) {
+ dpy = new char[ l+2 ];
+ strcpy(dpy," ");
+ strcat(dpy, buf + 8); /* returns " "+dpy */
+ /* ktalk_debug("- %s",dpy); */
+ }
+ break;
+ }
+ }
+ return dpy;
+}
+
+/* As utmp isn't reliable (neither xdm nor kdm logs into it ! :( ),
+ we have to look at processes directly. /proc helps a lot, under linux.
+ How to do it under other unixes ? */
+#ifdef ALL_PROCESSES_AND_PROC_FIND_USER
+
+/* awful global variable, but how to pass it to select_process() otherwise ?
+ scandir() doesn't allow this, of course... */
+unsigned int user_uid;
+
+/* selection function used by scandir */
+int select_process(
+#ifdef SCANDIR_NEEDS_CONST
+const struct dirent *direntry
+#else
+struct dirent *direntry
+#endif
+)
+{
+ /* returns 1 if username owns <direntry> */
+ struct stat statbuf;
+
+ /* make absolute path (would sprintf be better ?)*/
+ char abspath[20]="/proc/";
+ if( strlen( direntry->d_name ) > 12 )
+ return 0;
+ strcat(abspath,direntry->d_name);
+
+ if (isdigit(direntry->d_name[0])) { /* starts with [0-9]*/
+ if (!stat(abspath, &statbuf)) { /* if exists */
+ if (S_ISDIR(statbuf.st_mode)) { /* and is a directory and */
+ if (statbuf.st_uid == user_uid) /* is owned by user_uid*/
+ {
+ /* We have to force errno=0, because otherwise, scandir will stop ! */
+ /* the problem is that glibc sets errno in getpwnam and syslog
+ (glibc-2.0.5c/6, with libstdc++ 2.7.2) */
+ errno=0;
+ return 1;
+ }
+ /* else ktalk_debug("st_uid=%d", statbuf.st_uid); */
+ } /* else ktalk_debug("st_mode=%d", statbuf.st_mode); */
+ } else ktalk_debug("stat error : %s", strerror(errno));
+ }
+
+ errno=0;
+ return 0;
+}
+
+/* scan /proc for any process owned by 'name'.
+ If DISPLAY is set, set 'disp'.
+
+ Called only if no X utmp entry found. */
+
+/*
+ * Major memory leak, never frees the memory allocated by scandir
+ * (why not use readdir() in the first place?
+ * Major buffer overflow: If I set my DISPLAY variable to a
+ * 1024 bytes string, it'll overflow displays_list.
+ */
+
+int find_X_process(char *name, char *disp) {
+ char displays_list[DISPLAYS_LIST_MAX] = " "; /* yes, one space */
+ char * dispwithblanks;
+ struct dirent **namelist;
+
+ struct passwd * pw = getpwnam(name);
+ ktalk_debug("find_X_process");
+ /* find uid */
+ if ((pw) && ((user_uid=pw->pw_uid)>10))
+ { /* uid<10 : no X detection because any suid program will be taken
+ as owned by root, not by its real owner */
+ /* scan /proc */
+ int n = scandir("/proc", &namelist, select_process, 0 /*no sort*/);
+ if (n < 0)
+ ktalk_debug("scandir: %s", strerror(errno));
+ else
+ while(n--)
+ {
+ /* find DISPLAY */
+ dispwithblanks = get_display(atoi(namelist[n]->d_name));
+ if (dispwithblanks) {
+ /* This way, if :0.0 is in the list, :0 is not inserted */
+ /* XXX Yes, but if I have two displays, one foo:0.0
+ * and one bar:0.0, then bar:0 is not caught by the
+ * check below. But this is just peanuts.
+ */
+ if (!strstr(displays_list,dispwithblanks))
+ { /* not already in the list? */
+ char * pointlocation=strstr(dispwithblanks,".");
+ if (pointlocation) *pointlocation='\0';
+ if (!strstr(displays_list,dispwithblanks)
+ && strlen(dispwithblanks)+strlen(displays_list)<DISPLAYS_LIST_MAX)
+ { /* display up to the '.' mustn't be already in the list */
+ strcat(displays_list,dispwithblanks+1); /* insert display (no ' ') */
+ strcat(displays_list," "); /* and a blank */
+ }
+ } /* if strtsr */
+ delete dispwithblanks;
+ } /* if dispwithblanks */
+ } /* while */
+ if (strlen(displays_list)>1)
+ {
+ strcpy(disp,displays_list+1); /* removes the leading white space
+ but leave the final one, needed by announce.c */
+ return 1;
+ }
+ } /* if pw */
+ return 0;
+}
+
+#endif /* ALL_PROCESSES_AND_PROC_FIND_USER */
+
+#ifdef UTMP_AND_PROC_FIND_USER
+
+/*
+ * Search utmp for the local user
+ *
+ * Priorities:
+ * login from xdm
+ * login from pseudo terminal with $DISPLAY set
+ * login from pseudo terminal
+ * other login
+ */
+#define PRIO_LOGIN 1 /* user is logged in */
+#define PRIO_PTY 2 /* user is logged in on a
+ pseudo terminal */
+#define PRIO_DISPLAY 3 /* user is logged in on a
+ pseudo terminal and has
+ $DISPLAY set. */
+#define PRIO_XDM 4 /* user is logged in from xdm */
+
+#define SCMPN(a, b) strncmp(a, b, sizeof (a))
+
+int find_user(char *name, char *tty, char *disp) {
+ struct utmp *ubuf;
+ int prio = 0, status = NOT_HERE;
+ struct stat statb;
+ char ftty[20+UT_LINESIZE];
+ char *ntty, *dpy;
+ char ttyFound[UT_LINESIZE] = "";
+ char dispFound[DISPLAYS_LIST_MAX] = "";
+
+ strcpy(ftty, _PATH_DEV);
+ ntty = ftty + strlen(ftty);
+ setutent();
+ while ((ubuf = getutent())) {
+ if ((ubuf->ut_type == USER_PROCESS) &&
+ (!SCMPN(ubuf->ut_name, name))) {
+
+ if (*tty == '\0') { /* no particular tty was requested */
+
+ if (Options.XAnnounce && ubuf->ut_line[0] == ':') {
+ /* this is a XDM login (really?). GREAT! */
+ syslog(LOG_DEBUG, "XDM login: %s at %s", name, ubuf->ut_line);
+ status = SUCCESS;
+ if (prio < PRIO_XDM) {
+ strcpy(dispFound, ubuf->ut_line);
+ strcat(dispFound, " ");
+ prio = PRIO_XDM;
+ }
+ continue;
+ }
+
+ strcpy(ntty, ubuf->ut_line);
+ if (stat(ftty, &statb) != 0 || (!(statb.st_mode & 020)))
+ {
+ ktalk_debug("Permission denied on %s", ntty);
+ continue; /* not a char dev */
+ }
+
+ /* device exists and is a character device */
+ status = SUCCESS;
+ if (Options.debug_mode) syslog(LOG_DEBUG, "Found %s at %s", name, ubuf->ut_line);
+ if (prio < PRIO_LOGIN) {
+ prio = PRIO_LOGIN;
+ strcpy(ttyFound, ubuf->ut_line);
+ *dispFound = '\0';
+ }
+
+ /* the following code is Linux specific...
+ * is there a portable way to
+ * 1) determine if a device is a pseudo terminal and
+ * 2) get environment variables of an arbitrary process?
+ */
+ if (strncmp("tty", ubuf->ut_line, 3) != 0 ||
+ !strchr("pqrstuvwxyzabcde", ubuf->ut_line[3]))
+ continue; /* not a pty */
+
+ /* device is a pseudo terminal (ex : a xterm) */
+ if (Options.debug_mode) syslog(LOG_DEBUG, "PTY %s, ut_host=%s",
+ ubuf->ut_line, ubuf->ut_host);
+ if (prio < PRIO_PTY) {
+ prio = PRIO_PTY;
+ strcpy(ttyFound, ubuf->ut_line);
+ strcpy(dispFound, ubuf->ut_host);
+ strcat(dispFound, " ");
+ }
+
+ dpy = get_display(ubuf->ut_pid);
+ if (!dpy) continue; /* DISPLAY not set or empty */
+
+ /* $DISPLAY is set. */
+ if (Options.debug_mode) syslog(LOG_DEBUG, "Found display %s on %s",
+ dpy, ubuf->ut_line);
+ if (prio < PRIO_DISPLAY) {
+ prio = PRIO_DISPLAY;
+ strcpy(ttyFound, ubuf->ut_line);
+ strcpy(dispFound, dpy+1); /*no space*/
+ strcat(dispFound, " ");
+ }
+ delete dpy;
+ continue;
+ }
+ if (!strcmp(ubuf->ut_line, tty)) {
+ strcpy(ttyFound, ubuf->ut_line);
+ status = SUCCESS;
+ break;
+ }
+ }
+ }
+ endutent();
+
+ ktalk_debug("End of Utmp reading");
+#if defined(HAVE_KDE) && defined(ALL_PROCESSES_AND_PROC_FIND_USER)
+ if (Options.XAnnounce && prio < PRIO_DISPLAY)
+ if (find_X_process(name, dispFound))
+ { ktalk_debug(dispFound); status=SUCCESS; }
+#endif
+ if (status == SUCCESS) {
+ (void) strcpy(tty, ttyFound);
+ (void) strcpy(disp, dispFound);
+ if (Options.debug_mode)
+ syslog(LOG_DEBUG, "Returning tty '%s', display '%s'", ttyFound, dispFound);
+ } else ktalk_debug("Returning status %d",status);
+ return (status);
+}
+
+#endif /*UTMP_AND_PROC_FIND_USER*/
+
+#else /*not PROC_FIND_USER*/
+
+int find_user(char *name, char *tty, char *disp) {
+
+ struct utmp ubuf;
+ int status;
+ FILE *fd;
+ struct stat statb;
+ char ftty[20+UT_LINESIZE];
+ char ttyFound[UT_LINESIZE] = "";
+ char dispFound[UT_HOSTSIZE+1] = "";
+
+ if (!(fd = fopen(_PATH_UTMP, "r"))) {
+ fprintf(stderr, "talkd: can't read %s.\n", _PATH_UTMP);
+ return (FAILED);
+ }
+#define SCMPN(a, b) strncmp(a, b, sizeof (a))
+ status = NOT_HERE;
+ (void) strcpy(ftty, _PATH_DEV);
+ while (fread((char *) &ubuf, sizeof ubuf, 1, fd) == 1) {
+ if (!SCMPN(ubuf.ut_name, name)) {
+ if (*tty == '\0') {
+ /* no particular tty was requested */
+ (void) strcpy(ftty+5, ubuf.ut_line);
+ if (stat(ftty,&statb) == 0) {
+ if (!(statb.st_mode & 020)) /* ?character device? */
+ continue;
+ (void) strcpy(ttyFound, ubuf.ut_line);
+#ifdef USE_UT_HOST
+ (void) strcpy(dispFound, ubuf.ut_host);
+ strcat(dispFound, " ");
+#endif
+ status = SUCCESS;
+
+ syslog(LOG_DEBUG, "%s", ttyFound);
+ if ((int) ttyFound[3] > (int) 'f') {
+#ifdef USE_UT_HOST
+ if (Options.debug_mode) {
+ syslog(LOG_DEBUG, "I wanna this:%s", ttyFound);
+ syslog(LOG_DEBUG, "ut_host=%s", ubuf.ut_host);
+ syslog(LOG_DEBUG, "%s", ubuf.ut_line);
+ }
+#endif
+ break;
+ }
+ }
+ }
+ else if (!strcmp(ubuf.ut_line, tty)) {
+ status = SUCCESS;
+ break;
+ }
+ }
+ }
+ fclose(fd);
+ if (status == SUCCESS) {
+ (void) strcpy(tty, ttyFound);
+ (void) strcpy(disp, dispFound);
+ }
+ return (status);
+}
+#endif /*PROC_FIND_USER*/
diff --git a/ktalkd/ktalkd/find_user.h b/ktalkd/ktalkd/find_user.h
new file mode 100644
index 00000000..bb9394a2
--- /dev/null
+++ b/ktalkd/ktalkd/find_user.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 1997 David Faure
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ *
+ */
+
+#include "includ.h"
+
+int find_user(char * name, char * tty, char *disp);
+
+
diff --git a/ktalkd/ktalkd/includ.h b/ktalkd/ktalkd/includ.h
new file mode 100644
index 00000000..34971c51
--- /dev/null
+++ b/ktalkd/ktalkd/includ.h
@@ -0,0 +1,79 @@
+/**
+ * Copyright 2002 David Faure <faure@kde.org>
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+#ifndef __INCLUD_H
+#define __INCLUD_H "$Id"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h> // Needed on some systems.
+#endif
+
+#include "prot_talkd.h"
+#define NEW_NAME_SIZE NAME_SIZE
+#define NEW_CTL_MSG CTL_MSG
+#define NEW_CTL_RESPONSE CTL_RESPONSE
+#include "otalkd.h"
+
+#ifdef HAVE_UTMP_H
+#include <utmp.h>
+#endif
+#ifndef UT_LINESIZE
+#define UT_LINESIZE 12
+#endif
+
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+
+#ifndef _PATH_UTMP
+#define _PATH_UTMP UTMP
+#endif
+#ifndef _PATH_DEV
+#define _PATH_DEV "/dev/"
+#endif
+#ifndef _PATH_TMP
+#define _PATH_TMP "/tmp/"
+#endif
+
+#endif /* __INCLUD_H */
diff --git a/ktalkd/ktalkd/ktalkd.wav b/ktalkd/ktalkd/ktalkd.wav
new file mode 100644
index 00000000..f8d4292d
--- /dev/null
+++ b/ktalkd/ktalkd/ktalkd.wav
Binary files differ
diff --git a/ktalkd/ktalkd/ktalkdrc b/ktalkd/ktalkd/ktalkdrc
new file mode 100644
index 00000000..ee6292d8
--- /dev/null
+++ b/ktalkd/ktalkd/ktalkdrc
@@ -0,0 +1,73 @@
+[ktalkd]
+# Administrator config file for ktalkd.
+# There is now a configuration dialog box (see "Settings" menu) for the user's
+# ktalkdrc. But how to provide a nice way to set system-wide parameters ?
+
+
+###### SECTION 1 : Administrator settings
+
+# Mail sender (can be mail.local, sendmail(preferred), qmail(as good:)) )
+# mail.local is included in the dist., just in case you don't have sendmail
+MailProg=$KDEBINDIR/mail.local
+
+# What should I do if somebody tries to talk to a non-existent user ?
+# 0 = Launch answer machine saying 'non-existent user...'
+# and 'I' receive the message (to know that it happened)
+# 1 = 'I' take the talk. (The names of caller & callee appear to 'I')
+# 2 = Do nothing. ('Not logged' will appear to caller).
+NEUBehaviour=2
+
+# (Multi-user secured host : set Behaviour=2).
+# (Multi-user host : set Behaviour=0 and User=root or postmaster)
+# (Almost single-user networked PC : set Behaviour=1 and User=your_user_name)
+
+# If you choose 0, then you can set the following
+# (no internationalization possible : this file is manually read by ktalkd)
+NEUBanner1=The person you're asking to talk with is unknown at this host.
+NEUBanner2=You may have mistyped the name, or network address. Try again
+NEUBanner3=or leave a message which will be sent to the system administrator.
+
+# Who is 'I' ? (=> who should take the talk / receive the message)
+NEUUser=
+
+# If NEUBehaviour is 1 ('I' take the talk), which forward method to use ?
+# Choose FWT if your machine is on two separate networks, and if you (NEUUser)
+# plan to set a forward. FWR is ok (and lighter) in all other cases.
+NEUForwardMethod=FWR
+
+
+##### SECTION 2 : Default values that users can overwrite
+
+# Set to 1 to activate answering machine
+# Warning : if set to 0, no user will be able to activate it.
+# (The value won't overwrite this one).
+Answmach=1
+
+# Set this to 1 to enable X-aware announcement. (Why not ?)
+XAnnounce=1
+
+# External program launched by ktalkd to notify a talk. It can be :
+# ktalkdlg, a pop-up KDE dialog box, which can launch any talk client
+# or ktalk, the KDE talk client.
+# Will be launched with the caller's address as parameter
+# KDEBINDIR will be set by ktalkd.
+ExtPrg=$KDEBINDIR/ktalkdlg
+
+# Which talk client should ktalkdlg launch ? (Default value)
+# The default is "$KDEBINDIR/konsole -e talk". If ktalk is installed,
+# use "$KDEBINDIR/ktalk"
+# KDEBINDIR will be set by ktalkd.
+talkprg=$KDEBINDIR/konsole -e talk
+
+# Set to 1 to enable talk notifications with sound.
+Sound=1
+# Default sound file - ktalkd.wav is included
+SoundFile=ktalkd.wav
+
+# Do you wish to receive an empty mail if the caller didn't leave any message ?
+# (If "1", you'll only know who called you)
+EmptyMail=1
+
+# Time in seconds between "Ringing your party again" and launching answering
+# machine (not very important) (can't be overridden by users)
+Time=10
diff --git a/ktalkd/ktalkd/machines/Makefile.am b/ktalkd/ktalkd/machines/Makefile.am
new file mode 100644
index 00000000..73484dfa
--- /dev/null
+++ b/ktalkd/ktalkd/machines/Makefile.am
@@ -0,0 +1,14 @@
+## -*- makefile -*-
+# Ktalkd - answmach/Makefile.am
+
+##########################################################
+####### Paths
+
+AM_LIBS = $(LIBSOCKET)
+noinst_LIBRARIES = libmach.a
+
+libmach_a_SOURCES = answmach.cpp forwmach.cpp talkconn.cpp
+noinst_HEADERS = answmach.h forwmach.h talkconn.h
+
+#for extra warnings during compilation :
+#AM_CXXFLAGS = -ansi -pedantic -D_POSIX_SOURCE -D_BSD_SOURCE
diff --git a/ktalkd/ktalkd/machines/answmach.cpp b/ktalkd/ktalkd/machines/answmach.cpp
new file mode 100644
index 00000000..0e36c3e2
--- /dev/null
+++ b/ktalkd/ktalkd/machines/answmach.cpp
@@ -0,0 +1,405 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California, (c) 1997 David Faure
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ *
+ */
+
+#include "answmach.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pwd.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <time.h>
+#include <sys/ioctl.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#include <fcntl.h>
+#include <errno.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <syslog.h>
+#include <netdb.h>
+#include "../defs.h"
+#include "../proto.h"
+#include "../readconf.h"
+
+#define A_LONG_TIME 10000000 /* seconds before timeout */
+
+/** AnswMachine constructor */
+AnswMachine::AnswMachine(struct in_addr r_addr,
+ char * r_name,
+ char * l_name,
+ int _mode)
+{
+ /* Copy the answering machine mode */
+ mode = _mode;
+ /* Copy the local user name (i.e. the callee, existent or not) */
+ strcpy(local_user, r_name);
+ /* Copy the caller's machine address */
+ caller_machine_addr = r_addr;
+
+ /* Create a talk connection */
+ talkconn = new TalkConnection(r_addr,
+ l_name, /* the caller (remote) */
+ local_user,
+ ntalkProtocol); /* the callee (local) */
+ if (mode==PROC_REQ_ANSWMACH_NOT_HERE)
+ {
+ /* The caller is trying to talk to somebody this system doesn't know.
+ We can display a NEU banner (non-existent user) and take a message
+ for Options.NEU_user (root?). */
+ strncpy(NEUperson,local_user,NEW_NAME_SIZE); /* the person the talk was aimed to */
+ NEUperson[ NEW_NAME_SIZE - 1 ] = '\0';
+ strncpy(local_user,Options.NEU_user,NEW_NAME_SIZE); /* for mail address, config file... */
+ local_user[ NEW_NAME_SIZE - 1 ] = '\0';
+ } else *NEUperson='\0';
+}
+
+AnswMachine::~AnswMachine()
+{
+ delete talkconn;
+}
+
+int AnswMachine::LaunchIt(const char * key)
+{
+ int launchIt = 1;
+ if (usercfg) {
+ read_bool_user_config(key,&launchIt);
+ /* if (launchIt==0)
+ debug("Not launched. Option set to 0 : ", key);*/
+ }
+ return launchIt;
+}
+
+void AnswMachine::start()
+{
+ /* Only wait if somebody could possibly answer.
+ (The 'ringing your party again' has just been displayed,
+ we want to leave a second chance for the callee to answer.)
+
+ If NEU/not logged, start quickly. (Wait just a little for the
+ LEAVE_INVITE to come.) */
+ sleep( (mode==PROC_REQ_ANSWMACH) ? Options.time_before_answmach : 1 );
+
+ usercfg = init_user_config(local_user);
+
+ if (LaunchIt("Answmach"))
+ {
+ talkconn->open_sockets();
+
+ if (talkconn->look_for_invite(1/*mandatory*/))
+ /* otherwise, either the caller gave up before we
+ started or the callee answered ... */
+ {
+ /* There was an invitation waiting for us,
+ * so connect with the other (hopefully waiting) party */
+ if (talkconn->connect())
+ {
+ /* send the first 3 chars, machine dependent */
+ talkconn->set_edit_chars();
+
+ /* Do the talking */
+ talk();
+ }
+ }
+ }
+ if (usercfg) end_user_config();
+}
+
+static char * shell_quote(const char *s)
+{
+ char *result;
+ char *p;
+ p = result = (char *) malloc(strlen(s)*5+1);
+ while(*s)
+ {
+ if (*s == '\'')
+ {
+ *p++ = '\'';
+ *p++ = '"';
+ *p++ = *s++;
+ *p++ = '"';
+ *p++ = '\'';
+ }
+ else
+ {
+ *p++ = *s++;
+ }
+ }
+ *p = '\0';
+ return result;
+}
+
+/** The actual talking (user to answering machine) */
+void AnswMachine::talk()
+{
+ char command[S_COMMAND];
+ char messg_myaddr [S_MESSG];
+ struct hostent *hp;
+ FILE * fd = 0; /* file descriptor, to write the message */
+ char customline[S_CFGLINE];
+
+ char fname[ 256 ];
+ int fildes;
+ int oldumask = umask( 066 );
+ /* set permissions for temp file to rw- --- --- */
+ int emptymail; /* 1 if empty mail allowed */
+
+ int something_entered;
+
+ hp = gethostbyaddr((char *)&caller_machine_addr, sizeof (struct in_addr), AF_INET);
+ if (hp == (struct hostent *)0)
+ TalkConnection::p_error("Answering machine : Remote machine unknown.");
+
+ if ((!usercfg) || (!read_user_config("Mail",messg_myaddr,S_MESSG-1))) {
+ strncpy(messg_myaddr, local_user, S_MESSG);
+ messg_myaddr[ S_MESSG - 1 ] = '\0';
+ }
+
+ sprintf(fname, _PATH_TMP"/ktalkdXXXXXX"); // ### should use mkstemps
+ if ((fildes = mkstemp(fname)) == -1 || (fd = fdopen(fildes, "w+")) == 0) {
+ TalkConnection::p_error("Unable to open temporary file");
+ }
+
+ umask(oldumask);
+
+ write_headers(fd, hp, messg_myaddr, usercfg);
+
+ /* read other options before setting usercfg to 0, below. */
+ if ((!usercfg) || (!read_bool_user_config("EmptyMail",&emptymail)))
+ /* try from user config file, otherwise default : */
+ emptymail = 1;
+
+ /* debug("Connection established"); */
+
+ if (usercfg) {
+ if (!read_user_config("Msg1",customline,S_CFGLINE-1))
+ { /* debug("Error reading Msg1");*/ end_user_config(); usercfg=0; }
+ }
+
+ /* No user-config'ed banner */
+ if (!usercfg)
+ { /* => Display Options.invitelines */
+ talkconn->write_banner(Options.invitelines);
+ }
+ else if (mode==PROC_REQ_ANSWMACH_NOT_HERE)
+ { /* => Display Options.NEUBanner* */
+ talkconn->write_banner(Options.NEUBanner1);
+ talkconn->write_banner(Options.NEUBanner2);
+ talkconn->write_banner(Options.NEUBanner3);
+ } else {
+ int linenr = 1;
+ /* number of the Msg[1-*] line. is set to 0 after displaying banner*/
+ char m[]="Msg1"; /* used as key to read configuration */
+
+ while (linenr) /* still something to write to the caller */
+ {
+ talkconn->write_banner(customline);
+
+ /* read next line in custom file. */
+ m[3]=(++linenr)+'0';
+ if (!read_user_config(m,customline,S_CFGLINE-1))
+ linenr=0; /* end of message */
+ }
+ }
+ /* Banner displayed. Let's take the message. */
+ something_entered = read_message(fd);
+ fclose(fd);
+
+ if (something_entered || emptymail)
+ { /* Don't send empty message, except if 'EmptyMail' has been set */
+ int retcode;
+ char *quoted_fname;
+ char *quoted_messg_myaddr;
+ quoted_fname = shell_quote(fname);
+ quoted_messg_myaddr = shell_quote(messg_myaddr);
+
+ snprintf(command,S_COMMAND,"cat '%s' | %s '%s'",quoted_fname,Options.mailprog,quoted_messg_myaddr);
+
+ free(quoted_fname);
+ free(quoted_messg_myaddr);
+
+ /* XXX:
+ * It looks to me as if we're still running as root when we
+ * get here. So using system() is an extremely _bad_ idea,
+ * especially when we use a user-specified address. What
+ * keeps the user from specifying `rm -rf /` as his
+ * address? Yo, way cool.
+ * DF: doesn't shell_quote address this issue? */
+ retcode = system(command);
+ if ((retcode==127) || (retcode==-1))
+ syslog(LOG_ERR,"system() error : %s", strerror(errno));
+ else if (retcode!=0)
+ syslog(LOG_WARNING,"%s : %s", command, strerror(errno));
+ }
+ (void)unlink(fname);
+}
+
+void AnswMachine::write_headers(FILE * fd, struct hostent * hp, char *
+ messg_myaddr, int usercfg)
+{
+ char messg [S_MESSG];
+ char messg_tmpl [S_MESSG];
+ char * r_user = talkconn->get_caller_name();
+
+ /* if using mail.local, set 'Date:' and 'From:', because they will be missing otherwise */
+ int ismaillocal = (strstr(Options.mailprog,"mail.local")!=NULL);
+ if (ismaillocal)
+ /* should we check only the end of the name ? */
+ {
+ time_t tmp = time(0);
+ snprintf(messg,S_MESSG,"Date: %s",ctime(&tmp)); /* \n is included in ctime */
+ fwrite(messg,strlen(messg),1,fd); /* Date */
+
+ snprintf(messg,S_MESSG,"From: %s@%s\n",r_user,hp->h_name);
+ fwrite(messg,strlen(messg),1,fd); /* From */
+ }
+
+ snprintf(messg,S_MESSG,"To: %s\n",messg_myaddr);
+ fwrite(messg,strlen(messg),1,fd); /* To */
+
+ if ((!usercfg) || (!read_user_config("Subj",messg_tmpl,S_CFGLINE)))
+ /* try from user config file, otherwise default subject: */
+ strcpy(messg_tmpl,"Message from %s");
+ snprintf(messg,S_MESSG,messg_tmpl,r_user);
+ fwrite("Subject: ",9,1,fd);
+ fwrite(messg,strlen(messg),1,fd); /* Subject */
+ fwrite("\n",1,1,fd);
+
+ if (!ismaillocal)
+ { /* No need to set Reply-To if From has been set correctly */
+ snprintf(messg,S_MESSG,"Reply-To: %s@%s\n",r_user,hp->h_name);
+ fwrite(messg,strlen(messg),1,fd); /* Reply-To */
+ }
+
+ fwrite("\n",1,1,fd); /* empty line -> end of headers */
+
+ if ((!usercfg) || (!read_user_config("Head",messg_tmpl,S_CFGLINE)))
+ /* try from user config file, otherwise default headline: */
+ strcpy(messg_tmpl,"Message left in the answering machine, by %s@%s");
+ snprintf(messg,S_MESSG,messg_tmpl,r_user,hp->h_name);
+
+ if (mode==PROC_REQ_ANSWMACH_NOT_HERE)
+ {
+ char tmp[ NEW_NAME_SIZE + 10 ];
+ snprintf(tmp, NEW_NAME_SIZE + 9, " => '%s'", NEUperson);
+ if( strlen(tmp)+strlen(messg) < S_MESSG )
+ strcat(messg,tmp);
+ }
+ fwrite(messg,strlen(messg),1,fd); /* First line of the message */
+ fwrite("\n\n",2,1,fd);
+}
+
+int AnswMachine::read_message(FILE * fd) // returns 1 if something has been entered
+{
+ int nb;
+ fd_set read_template, read_set;
+ int pos = 0; // position on the line. left=0.
+ int something = 0; // nothing entered by caller up to now.
+ struct timeval wait;
+ char buff[BUFSIZ];
+ char line[80] = ""; // buffer for current line
+ char char_erase = talkconn->get_char_erase();
+
+ FD_ZERO(&read_template);
+ FD_SET(talkconn->get_sockt(), &read_template);
+
+ for (;;) {
+ read_set = read_template;
+ wait.tv_sec = A_LONG_TIME;
+ wait.tv_usec = 0;
+
+ nb = select(32, &read_set, 0, 0, &wait);
+ if (nb <= 0) {
+ if (errno == EINTR || errno == EAGAIN) {
+ read_set = read_template;
+ continue;
+ } /* panic, we don't know what happened */
+ TalkConnection::p_error("Unexpected error from select");
+ }
+ if (FD_ISSET(talkconn->get_sockt(), &read_set)) {
+ int i;
+ /* There is data on sockt */
+ nb = read(talkconn->get_sockt(), buff, sizeof buff);
+ if (nb <= 0) {
+ /* debug("Connection closed. Exiting"); */
+ break;
+ }
+ something = 1;
+ for (i=0; i<nb; i++ ) {
+ if ((buff[i]==char_erase) && (pos>0)) /* backspace */
+ pos--;
+ else {
+ if (pos == 79) {
+ fwrite(line,pos,1,fd);
+ pos = 0;
+ }
+ line[pos++]=buff[i];
+ if (buff[i]=='\n') {
+ fwrite(line,pos,1,fd);
+ pos = 0;
+ }
+ }
+ }
+ } /* if read_set ... */
+ }
+ if (pos>0) { line[pos++]='\n'; fwrite(line,pos,1,fd); }
+ /* last line */
+ return something; // 1 if something entered.
+}
+
+/** Create and start a new answering machine from the given info */
+void AnswMachine::launchAnswMach(NEW_CTL_MSG msginfo, int mode)
+ {
+ if ((fork()) == 0) /* let's fork to let the daemon process other messages */
+ {
+
+#define satosin(sa) ((struct sockaddr_in *)(sa))
+
+ AnswMachine * am = new AnswMachine(
+ (satosin(&msginfo.ctl_addr))->sin_addr, /* Caller's machine address */
+ msginfo.r_name,
+ msginfo.l_name,
+ mode);
+ am->start();
+ delete am;
+
+ // exit the child
+ exit(-1);
+ }
+ }
+
diff --git a/ktalkd/ktalkd/machines/answmach.h b/ktalkd/ktalkd/machines/answmach.h
new file mode 100644
index 00000000..7bae8d17
--- /dev/null
+++ b/ktalkd/ktalkd/machines/answmach.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California, (c) 1997 David Faure
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ *
+ */
+#include "../includ.h"
+#include "talkconn.h"
+#include <stdio.h>
+
+/** Implements the answering machine. */
+class AnswMachine
+{
+ public:
+ /** Constructor.
+ * @param r_addr Remote machine IP address
+ * @param r_name Remote user name
+ * @param l_name Local user name
+ * @param _mode Answering machine mode, cf defs.h */
+ AnswMachine(struct in_addr r_addr,
+ char * r_name,
+ char * l_name,
+ int _mode);
+
+ /** Destructor. */
+ virtual ~AnswMachine();
+
+ /** Launch the machine */
+ virtual void start();
+
+ /** Create and start a new answering machine from the given info */
+ static void launchAnswMach(NEW_CTL_MSG msginfo, int mode);
+
+ protected:
+
+ /** Read usercfg file to know if user wants it to be launched */
+ int LaunchIt(const char * key);
+
+ int read_message(FILE * fd); // message to mail
+ void write_headers(FILE * fd, struct hostent * hp, char *
+ messg_myaddr, int usercfg); // mail headers
+
+ /** Do the actual talk. */
+ void talk();
+
+ // Protected members
+ /** Answering machine mode */
+ int mode;
+ /** Talk Connection to the caller */
+ TalkConnection * talkconn;
+ /** Local user name (for config file. Is also the default mail addr) */
+ char local_user[NEW_NAME_SIZE];
+ /** Non-existent user name, to be written in the mail. */
+ char NEUperson[NEW_NAME_SIZE];
+ /** Caller's machine address */
+ struct in_addr caller_machine_addr;
+ /** User config file */
+ int usercfg;
+};
diff --git a/ktalkd/ktalkd/machines/forwmach.cpp b/ktalkd/ktalkd/machines/forwmach.cpp
new file mode 100644
index 00000000..4d6b2146
--- /dev/null
+++ b/ktalkd/ktalkd/machines/forwmach.cpp
@@ -0,0 +1,442 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California, (c) 1998 David Faure
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+#include "../includ.h"
+#include "forwmach.h"
+#include <stdio.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <syslog.h>
+#include <errno.h>
+#include <netdb.h>
+#include <signal.h>
+#include "../proto.h"
+#include "../defs.h"
+#include "../readconf.h"
+#include "../process.h"
+#include "../threads.h"
+
+ForwMachine * ForwMachine::pForwMachine = 0L;
+
+void sig_handler(int signum);
+
+ForwMachine::ForwMachine(const NEW_CTL_MSG * mp,
+ char * forward,
+ char * _forwardMethod,
+ int c_id_num) : pid(0), caller_id_num(c_id_num)
+{
+ // Store callee as 'local_user'
+ strncpy(local_user, mp->r_name, NEW_NAME_SIZE);
+ local_user[ NEW_NAME_SIZE - 1 ] = '\0';
+ // -1 is to be sure to have a '\0' at the end
+ // Store caller's name
+ strncpy(caller_username, mp->l_name, NEW_NAME_SIZE);
+ caller_username[ NEW_NAME_SIZE - 1 ] = '\0';
+ // Store caller's protocol
+ callerProtocol = (mp->vers==0) ? talkProtocol : ntalkProtocol;
+
+ // Forward method : from string to enumerate
+ if (!strcmp(_forwardMethod,"FWA"))
+ forwardMethod = FWA;
+ else if (!strcmp(_forwardMethod,"FWR"))
+ forwardMethod = FWR;
+ else if (!strcmp(_forwardMethod,"FWT"))
+ forwardMethod = FWT;
+ else syslog(LOG_ERR,"Unknown forward method : %s",_forwardMethod);
+
+ // Get answerer machine address and username
+ if (getNames(forward)) {
+ ktalk_debug("-- Talking to %s",answ_user);
+ ktalk_debug("-- On %s",answ_machine_name);
+ // Create a new talk connection, to the answerer ...
+ tcAnsw = new TalkConnection(answ_machine_addr,
+ answ_user,
+ // from caller's username
+ (char *) mp->l_name,
+ noProtocol); // to be checked
+ tcAnsw->open_sockets();
+ // and from here if FWT or ...
+ if (forwardMethod != FWT) {
+ //from the caller if FWA or FWR
+ tcAnsw->set_addr(&mp->addr);
+ // but WE DO NOT CHANGE THE ctl_addr, we want the response !!
+ }
+ // Store caller's ctl_addr (to respond to its announce)
+ caller_ctl_addr = mp->ctl_addr;
+ // And his machine addr (to send a LOOK_UP)
+ caller_machine_addr = ((struct sockaddr_in * )(&mp->ctl_addr))->sin_addr;
+ }
+}
+
+ForwMachine::~ForwMachine()
+{
+ delete answ_machine_name;
+ delete tcAnsw;
+ if (pid) kill(pid,SIGTERM);
+}
+
+/** Fills private fields from forward
+ * @param forward user@host to forward the talk */
+int ForwMachine::getNames(char * forward)
+{ /* taken from old get_names.c */
+ register char *cp;
+
+ /* strip out the machine name of the target */
+ for (cp = forward; *cp && !strchr("@:!.", *cp); cp++)
+ ;
+ if (*cp == '\0') {
+ /* this is a forward to a local user */
+ strncpy(answ_user, forward, NEW_NAME_SIZE);
+ answ_user[ NEW_NAME_SIZE -1 ] = '\0';
+ answ_machine_name = new char[strlen(Options.hostname)+1];
+ strcpy(answ_machine_name, Options.hostname); /* set by the daemon */
+ } else {
+ if (*cp == '@') {
+ /* user@host */
+ *cp++ = '\0';
+ strncpy(answ_user, forward, NEW_NAME_SIZE);
+ answ_user[ NEW_NAME_SIZE -1 ] = '\0';
+ answ_machine_name = new char[strlen(cp)+1];
+ strcpy(answ_machine_name, cp);
+ } else {
+ /* host.user or host!user or host:user */
+ *cp++ = '\0';
+ strncpy(answ_user, cp, NEW_NAME_SIZE);
+ answ_user[ NEW_NAME_SIZE -1 ] = '\0';
+ answ_machine_name = new char[strlen(forward)+1];
+ strcpy(answ_machine_name, forward);
+ }
+ }
+
+ struct hostent * hp = gethostbyname(answ_machine_name);
+ if (!hp) {
+ syslog(LOG_ERR, "gethostbyname for %s: %s", answ_machine_name, strerror(errno));
+ return 0;
+ }
+ memcpy(&answ_machine_addr, hp->h_addr, hp->h_length);
+ return 1;
+}
+
+int ForwMachine::isLookupForMe(const NEW_CTL_MSG * mp)
+{
+ /** We want to check if this LOOK_UP concerns this forwmachine.
+ * It does if :
+ mp->l_name = answ_user
+ mp->r_name = caller_username
+ mp->addr.sin_addr is 0.0.0.0, can't be tested ...
+ mp->ctl_addr.sin_addr could be tested but how ?
+ */
+ if (Options.debug_mode)
+ {
+ syslog(LOG_DEBUG,"-- mp->l_name : '%s' answerer : '%s' mp->r_name : '%s' caller : '%s'",
+ mp->l_name, answ_user, mp->r_name, caller_username);
+ }
+
+ return ( (!strcmp(mp->l_name, answ_user)) &&
+ (!strcmp(mp->r_name, caller_username)) );
+
+}
+
+char * ForwMachine::findMatch(NEW_CTL_MSG * mp)
+{
+ /** Check if there is a forwarding machine on this host,
+ * matching answerer = r_name and caller = l_name
+ * Then return the initial callee (local_user), to display in ktalkdlg
+ * This is used by local forwards, and therefore also if NEUBehaviour=1 */
+ if (Options.debug_mode)
+ {
+ syslog(LOG_DEBUG,"-- mp->l_name : '%s' caller : '%s' mp->r_name : '%s' answerer : '%s'",
+ mp->l_name, caller_username, mp->r_name, answ_user);
+ }
+ if ((!strcmp(mp->l_name, caller_username)) &&
+ (!strcmp(mp->r_name, answ_user)) )
+ return local_user;
+ return NULL;
+}
+
+int ForwMachine::transmit_chars(int sockt1, int sockt2, unsigned char * buf)
+{
+ int nb = read(sockt1, buf, BUFSIZ);
+ if (nb <= 0) {
+ return 0; // finished.
+ }
+ write(sockt2, buf, nb);
+ if ((nb <= 0) && (errno != EINTR)) {
+ syslog(LOG_ERR,"Unexpected error in write to socket");
+ }
+ return nb;
+}
+
+void ForwMachine::connect_FWT(TalkConnection * tcCaller)
+{
+ /** FWT : This is the method in which we take the connection to both
+ * clients and send each character received from one side to the other
+ * side. This allows to pass a firewall for instance. */
+ /* debug("-- connect_FWT : Waiting for connection from Answerer (%s)", answ_user); */
+ if (tcAnsw->accept())
+ {
+ /* debug("-- connect_FWT : Trying to connect to Caller (%s)",caller_username); */
+ if (tcCaller->connect())
+ {
+ /*
+ debug("-- connect_FWT : Connected to caller (%s)", caller_username);
+ debug("-- connect_FWT : Connected to both. Let's go");
+ */
+ int socktC = tcCaller->get_sockt();
+ int socktA = tcAnsw->get_sockt();
+ int max_sockt = (socktC>socktA) ? socktC : socktA;
+ unsigned char buf[BUFSIZ];
+ fd_set read_mask;
+ int nb;
+ int nbtot = 0;
+ for (;;) {
+ FD_ZERO(&read_mask);
+ FD_SET(socktA, &read_mask); // wait on both connections
+ FD_SET(socktC, &read_mask);
+ nb = select(max_sockt+1, &read_mask, NULL, NULL, NULL); // no timeout
+ if (nb <= 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ /* panic, we don't know what happened */
+ TalkConnection::p_error("Unexpected error from select");
+ }
+ if (FD_ISSET(socktA, &read_mask)) {
+ /* There is data on sockt A */
+ nb = transmit_chars(socktA, socktC, buf);
+ if (nb==0) return ;
+ }
+ if (FD_ISSET(socktC, &read_mask)) {
+ /* There is data on sockt C */
+ nb = transmit_chars(socktC, socktA, buf);
+ if (nb==0) return ;
+ nbtot += nb;
+ if (nbtot == 3) // just after the 3 edit chars
+ {
+ struct hostent * hp = gethostbyaddr((char *)&caller_machine_addr,
+ sizeof (struct in_addr), AF_INET);
+ if (hp != (struct hostent *)0) {
+ // Write first line for answerer.
+ // i18n() missing
+ sprintf((char *)buf, "Speaking to %s@%s\n", caller_username, hp->h_name);
+ write(socktA, (char *)buf, strlen((char *)buf));
+ } else ktalk_debug("-- ERROR : Unable to resolve caller_machine_addr !");
+ }
+ }
+ } // for
+ } else syslog(LOG_ERR,"-- FWT : Caller connected, but not answerer !");
+ } else syslog(LOG_ERR,"-- FWT : Caller did not connect !");
+}
+
+void ForwMachine::sendResponse(const struct talk_addr target, NEW_CTL_RESPONSE * rp)
+{
+ if (rp->vers == 0) { // otalk protocol (internal coding for it)
+ rp->vers /*type in otalk*/ = rp->type;
+ rp->type /*answer in otalk*/ = rp->answer;
+ }
+ int cc = sendto(1 /*talkd_sockt*/, (char *) rp,
+ sizeof (NEW_CTL_RESPONSE), 0, (struct sockaddr *)&target,
+ sizeof (struct talk_addr));
+ if (cc != sizeof (NEW_CTL_RESPONSE))
+ syslog(LOG_WARNING, "sendto: %s", strerror(errno));
+}
+
+/** processAnnounce is done by a child (to let the daemon process other
+ * messages, including ours). Then the child is left running (he only knows the
+ * value of answ_id_num) and waits for SIGDELETE to use this value. */
+void ForwMachine::processAnnounce()
+{
+ if ((pid=fork())==0) // store pid in the parent
+ {
+ // Send announce to the answerer, and wait for response
+ ktalk_debug("-------------- ForwMachine : sending ANNOUNCE to %s",answ_user);
+ tcAnsw->ctl_transact(ANNOUNCE, caller_id_num);
+ // Copy answer and id_num from the response struct
+ ktalk_debug("-------------- ForwMachine : got a response");
+ NEW_CTL_RESPONSE rp; // build our response struct
+ tcAnsw->getResponseItems(&rp.answer, &answ_id_num, 0L);
+ // answ_id_num stores id_num for delete.
+ rp.type = ANNOUNCE;
+ rp.vers = TALK_VERSION;
+ rp.id_num = htonl(our_id_num);
+
+ ktalk_debug("Storing response id_num %d",answ_id_num);
+ // Now send the response to the caller
+ print_response("-- => response (processAnnounce)", &rp);
+ sendResponse(caller_ctl_addr, &rp);
+ // -- Now wait for SIGDELETE
+
+ // store static ref to this forwmachine in this child.
+ pForwMachine = this;
+ // register signal hander
+ if (signal(SIGDELETE,&sig_handler)==SIG_ERR) ktalk_debug("ERROR for SIGUSR2");
+ ktalk_debug("Signal handler registered. Waiting...");
+ // infinite loop waiting for signals
+ while(1)
+ sleep(100);
+ }
+ ktalk_debug("Forwmachine started for Announce (now) and Delete (later). pid : %d",pid);
+ // new_process(); // We DON'T register new process.
+ // in case of re-announce, this forwmach will be forgotten.
+ // we don't want ktalkd to wait infinitely for it to die, it won't.
+}
+
+/** Process the lookup in a child process. The current running child can't do
+ * it with a signal, but we need the answerer's ctl_addr to respond... */
+void ForwMachine::processLookup(const NEW_CTL_MSG * mp)
+{
+ if (fork()==0)
+ { // here we are the child
+ ktalk_debug("------------- Got LOOKUP : send it to caller (%s)", caller_username);
+ // Let's send a LOOK_UP on caller's machine, to make sure he still
+ // wants to speak to the callee...
+ TalkConnection * tcCaller = new TalkConnection(caller_machine_addr,
+ caller_username,
+ local_user,
+ callerProtocol);
+ tcCaller->open_sockets();
+ tcCaller->look_for_invite(0/*no error if no invite*/);
+ NEW_CTL_RESPONSE rp;
+ tcCaller->getResponseItems(&rp.answer, &rp.id_num, &rp.addr);
+ ktalk_debug("------------- Done. Forward response to answerer");
+
+ rp.type = LOOK_UP;
+ rp.vers = mp->vers;
+ rp.id_num = htonl(rp.id_num);
+ // Now send the response to the answerer
+ if (forwardMethod == FWR)
+ {
+ // with caller's addr copied in the NEW_CTL_RESPONSE (if FWR),
+ // so that they can talk to each other.
+ /* rp.addr filled by getResponseItems */
+ rp.addr.ta_family = htons(rp.addr.ta_family);
+ }
+ else // FWT. (FWA doesn't let us get the LOOK_UP)
+ {
+ // in this case, we copy in the NEW_CTL_RESPONSE the address
+ // of the connection socket set up here for the answerer
+ rp.addr = tcAnsw->get_addr();
+ rp.addr.ta_family = htons(AF_INET);
+ }
+ print_response("-- => response (processLookup)", &rp);
+ if (forwardMethod == FWT)
+ tcAnsw->listen(); // start listening before we send the response,
+ // just in case the answerer is very fast (ex: answ mach)
+ sendResponse(mp->ctl_addr, &rp);
+ if (forwardMethod == FWT)
+ connect_FWT(tcCaller);
+ delete tcCaller;
+ _exit(0);
+ }
+ new_process();
+}
+
+/** Done by the forwmachine child that processed the ANNOUNCE. (He know answ_id_num)
+ * Exits at the end of the method */
+void ForwMachine::processDelete()
+{
+ // Send DELETE to the answerer, and don't wait for response
+ ktalk_debug("-------------- ForwMachine : sending DELETE to %s",answ_user);
+ ktalk_debug("Using resp->id_num %d",answ_id_num);
+ tcAnsw->ctl_transact(DELETE, answ_id_num);
+ _exit(0); // We exit the child, we have finished.
+}
+
+// Static functions
+
+int ForwMachine::forwMachProcessLookup(TABLE_ENTRY * table, const NEW_CTL_MSG * mp)
+{
+ /** This goes through the table, looking for non-NULL fwm entries.
+ * After a cast to (ForwMachine *), the fwm entries allows us to
+ * speak to currently availabe ForwMachines, to handle correctly
+ * this LOOK_UP */
+ ktalk_debug("-- forwMachProcessLookup(mp,rp)");
+ TABLE_ENTRY *ptr;
+ for (ptr = table; ptr != 0L; ptr = ptr->next) {
+ if (ptr->fwm != 0L)
+ {
+ ForwMachine * fwm = (ForwMachine *) ptr->fwm;
+ if (fwm->isLookupForMe(mp)) {
+ ktalk_debug("-- Found match : id %d", ptr->request.id_num);
+ fwm->processLookup(mp);
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/** Check if there is a forwarding machine on this machine,
+ * matching answerer = r_name and caller = l_name
+ * Then set callee to the initial callee, to display in ktalkdlg */
+char * ForwMachine::forwMachFindMatch(TABLE_ENTRY * table, NEW_CTL_MSG * mp)
+{
+ ktalk_debug("-- forwMachFindMatch(mp)");
+ TABLE_ENTRY *ptr;
+ char * callee;
+ for (ptr = table; ptr != 0L; ptr = ptr->next) {
+ if (ptr->fwm != 0L)
+ {
+ ForwMachine * fwm = (ForwMachine *) ptr->fwm;
+ callee = fwm->findMatch(mp);
+ if (callee) {
+ ktalk_debug("-- Found match : id %d", ptr->request.id_num);
+ return callee;
+ }
+ }
+ }
+ return NULL;
+}
+
+void sig_handler(int signum)
+{
+ ktalk_debug("SIGNAL received : %d",signum);
+ ForwMachine * fwm = ForwMachine::getForwMachine();
+ fwm->processDelete();
+}
+
+void ForwMachine::start(int o_id_num)
+{
+ our_id_num = o_id_num;
+ processAnnounce();
+
+}
+
diff --git a/ktalkd/ktalkd/machines/forwmach.h b/ktalkd/ktalkd/machines/forwmach.h
new file mode 100644
index 00000000..8ad067f9
--- /dev/null
+++ b/ktalkd/ktalkd/machines/forwmach.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California, (c) 1997 David Faure
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ *
+ */
+#ifndef FORWMACH_H
+#define FORWMACH_H
+
+#include "../includ.h"
+#include "../table.h"
+#include "talkconn.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <signal.h>
+
+// Strangely enough, SIGUSR1 doesn't get caught ... Why ??
+
+#define SIGDELETE SIGUSR2
+
+/* Forwarding machine scheme : (ANNOUNCE request only)
+
+ Caller (client) ForwMach--- Answerer (client)
+ \ | \
+ \ | \
+ Caller (daemon) ----ktalkd ------Answerer (daemon)
+
+ Caller always talks to us using ntalk (if otalk then kotalkd translates it)
+ But answerer might be using otalk. Let's check for it in processLookup.
+ */
+/** Implements the forwarding machine. */
+class ForwMachine
+{
+
+ public:
+ /** Constructor.
+ * @param mp Request received
+ * @param forward User@host to forward the talk
+ * @param _forwardMethod 3 letters to choose the method (see .talkdrc)
+ * @param c_id_num announce id_num, got from caller
+ * @param o_id_num our id_num in our table
+ * */
+ ForwMachine(const NEW_CTL_MSG * mp,
+ char * forward,
+ char * _forwardMethod,
+ int c_id_num);
+
+ /** Destructor. */
+ virtual ~ForwMachine();
+
+ // Child methods
+
+ /** Process the incoming ANNOUNCE request. */
+ void processAnnounce();
+
+ /** Processes the LOOK_UP request from answerer. */
+ void processLookup(const NEW_CTL_MSG * mp);
+
+ /** Processes the DELETE request from caller. Called by sig_handler. */
+ void processDelete();
+
+ // Parent methods
+
+ /** Send a DELETE signal to the child */
+ void sendDelete() { kill(pid, SIGDELETE); pid = 0; }
+
+ /** Checks if a LOOK_UP concerns this Forwarding Machine */
+ int isLookupForMe(const NEW_CTL_MSG * mp);
+ /** Used by forwMachFindMatch for NEUuser or local forward */
+ char * findMatch(NEW_CTL_MSG * mp);
+
+ /** Calls processLookup after finding the correct forwmachine instance in the table */
+ static int forwMachProcessLookup(TABLE_ENTRY * table, const NEW_CTL_MSG * mp);
+
+ /** Tries to find a match in the table for the given REQUEST structure.
+ * @see findMatch */
+ static char * forwMachFindMatch(TABLE_ENTRY * table, NEW_CTL_MSG * mp);
+
+ /** Get static ref to current ForwMachine. For sig_handler */
+ static ForwMachine * getForwMachine() { return pForwMachine; }
+
+ /** Start the ForwMachine process. Processes the announcement and waits for signals
+ * @param o_id_num Our id num in our table. */
+ void start(int o_id_num);
+
+ protected:
+ /** Static ref to current forwmachine. For sig_handler */
+ static ForwMachine * pForwMachine;
+
+ /** Pid of the child forwmachine */
+ int pid;
+
+ /** Fills privates fields from forward
+ * @param forward user@host to forward the talk */
+ int getNames(char * forward);
+
+ /** Respond to caller's daemon
+ * @param target the caller's machine address
+ * @param rp the response to send to it */
+ void sendResponse(const struct talk_addr target, NEW_CTL_RESPONSE * rp);
+
+ /** FWT Method : transmit characters from sockt1 to sockt2 */
+ int transmit_chars(int sockt1, int sockt2, unsigned char * buf);
+
+ /** FWT Method : we want to connect to both sides */
+ void connect_FWT(TalkConnection * tcCaller);
+
+ /** Method for the forwarding. */
+ enum {FWA, FWR, FWT} forwardMethod;
+
+ /** Answerer user name */
+ char answ_user[NEW_NAME_SIZE];
+ /** Answerer machine name */
+ char * answ_machine_name;
+ /** Answerer machine address */
+ struct in_addr answ_machine_addr;
+ /** Talk Connection to the answerer */
+ TalkConnection * tcAnsw;
+ /** id_num for the announce on answerer's machine. */
+ uint32_t answ_id_num;
+
+ /** Local user name, the original 'callee' */
+ char local_user[NEW_NAME_SIZE];
+ /** Our id_num (in our table) */
+ int our_id_num;
+
+ /** Caller's user name */
+ char caller_username[NEW_NAME_SIZE];
+ /** Caller's ctl_addr*/
+ struct talk_addr caller_ctl_addr;
+ /** Caller's machine address */
+ struct in_addr caller_machine_addr;
+ /** Caller's announce id_num */
+ int caller_id_num;
+ /** Caller's protocol */
+ ProtocolType callerProtocol;
+};
+#endif
diff --git a/ktalkd/ktalkd/machines/talkconn.cpp b/ktalkd/ktalkd/machines/talkconn.cpp
new file mode 100644
index 00000000..2836bc62
--- /dev/null
+++ b/ktalkd/ktalkd/machines/talkconn.cpp
@@ -0,0 +1,583 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California, (c) 1998 David Faure
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+/*
+ * This file handles haggling with the various talk daemons to
+ * get a socket to talk to. sockt is opened and connected in
+ * the progress
+ */
+
+#include "talkconn.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <termios.h>
+#include <syslog.h>
+#include <netdb.h>
+
+#include <sys/time.h>
+#include <time.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include "../proto.h"
+#include "../defs.h" // for hostname
+
+#ifndef SOMAXCONN
+#warning SOMAXCONN not defined in your headers
+#define SOMAXCONN 5
+#endif
+
+#define CTL_WAIT 2 /* time to wait for a response, in seconds */
+
+struct in_addr TalkConnection::defaultReplyAddr;
+short int TalkConnection::talkDaemonPort; // Port number of talk demon (517)
+short int TalkConnection::ntalkDaemonPort; // Port number of ntalk demon (518)
+
+
+TalkConnection::TalkConnection(struct in_addr caller_machine_addr,
+ char * r_name,
+ char * local_user,
+ ProtocolType _protocol) :
+ protocol(_protocol), his_machine_addr(caller_machine_addr), ctl_sockt(-1), sockt(-1)
+{
+ my_machine_addr = getReplyAddr(his_machine_addr);
+
+ new_msg.vers = TALK_VERSION;
+ new_msg.pid = htonl (getpid ()); // is it necessary ?
+ *new_msg.r_tty = '\0';
+ old_msg.pid = htonl (getpid ()); // is it necessary ?
+ *old_msg.r_tty = '\0';
+
+ strncpy(new_msg.l_name,local_user,NEW_NAME_SIZE);
+ new_msg.l_name[NEW_NAME_SIZE-1]='\0';
+ strncpy(new_msg.r_name,r_name,NEW_NAME_SIZE);
+ new_msg.r_name[NEW_NAME_SIZE-1]='\0';
+ strncpy(old_msg.l_name,local_user,OLD_NAME_SIZE);
+ old_msg.l_name[OLD_NAME_SIZE-1]='\0';
+ strncpy(old_msg.r_name,r_name,OLD_NAME_SIZE);
+ old_msg.r_name[OLD_NAME_SIZE-1]='\0';
+
+}
+
+TalkConnection::~TalkConnection()
+{
+ close_sockets();
+}
+
+void TalkConnection::init()
+{
+ /* look up the address of the local host */
+ struct hostent *hp = gethostbyname(Options.hostname);
+ if (!hp) {
+ syslog(LOG_ERR, "GetHostByName failed for %s.",Options.hostname);
+ exit(-1);
+ }
+ memcpy(&defaultReplyAddr, hp->h_addr, hp->h_length);
+
+ /* find the server's ports */
+ struct servent * sp = getservbyname("talk", "udp");
+ if (sp == 0)
+ syslog(LOG_ERR, "talkconnection: talk/udp: service is not registered.\n");
+ talkDaemonPort = sp->s_port; // already in network byte order
+
+ sp = getservbyname("ntalk", "udp");
+ if (sp == 0)
+ syslog(LOG_ERR, "talkconnection: ntalk/udp: service is not registered.\n");
+ ntalkDaemonPort = sp->s_port; // already in network byte order
+}
+
+int TalkConnection::open_socket (struct sockaddr_in *addr, int type)
+{
+ addr->sin_family = AF_INET;
+ addr->sin_addr = my_machine_addr;
+ addr->sin_port = 0;
+ int newSocket = socket (PF_INET, type, 0);
+ if (newSocket <= 0)
+ p_error ("Unable to open a new socket!");
+
+ ksize_t length = sizeof (*addr);
+ if (bind (newSocket, (struct sockaddr *) addr, length) != 0) {
+ ::close (newSocket);
+ p_error ("Error binding socket!");
+ }
+ if (getsockname (newSocket, (struct sockaddr *) addr, &length) == -1) {
+ ::close (newSocket);
+ p_error ("New socket has a bad address!");
+ }
+ return newSocket;
+}
+
+void TalkConnection::open_sockets()
+{
+ struct sockaddr_in ctl_addr;
+ struct sockaddr_in my_addr;
+
+ /* open the ctl socket */
+ ctl_sockt = open_socket(&ctl_addr, SOCK_DGRAM);
+ /* store its address */
+ set_ctl_addr((const struct talk_addr *)&ctl_addr);
+
+ /* open the text socket */
+ sockt = open_socket(&my_addr, SOCK_STREAM);
+ /* store its address */
+ set_addr((const struct talk_addr *)&my_addr);
+}
+
+/* Tries to find out the correct IP address that the daemon at host
+ "destination" has to respond to - code borrowed from ktalk, thanks Burkhard ! */
+struct in_addr TalkConnection::getReplyAddr (struct in_addr destination) {
+
+ in_addr *result;
+ unsigned char *help1;
+ unsigned char *help2;
+
+ /* disabled caching - I don't have QIntDict ...
+ result = replyAddrList [(long) destination.s_addr];
+ if (result) {
+ return *result;
+ }
+ */
+ int testsock, i;
+ result = new (struct in_addr);
+ struct sockaddr_in client, daemon;
+ for (i = 0; i < 2; i++) {
+ client.sin_family = daemon.sin_family = AF_INET;
+ client.sin_addr.s_addr = htonl (INADDR_ANY);
+ client.sin_port = htons (0);
+ daemon.sin_addr = destination;
+ daemon.sin_port = i ? ntalkDaemonPort : talkDaemonPort;
+
+ // Connect to the daemon socket address
+ // On some UNIXes (such as Linux) this works and sets the IP address queried
+ // by getsockname to the local machine address used to reach the daemon.
+ // If it doesn't work (e.g. on SunOS and Solaris), the default machine
+ // address is used instead.
+ ksize_t length = sizeof (daemon);
+ if ((testsock = socket (AF_INET, SOCK_DGRAM, 0)) >= 0 &&
+ bind (testsock, (struct sockaddr *) &client, sizeof (client)) == 0 &&
+ ::connect (testsock, (struct sockaddr *) &daemon,
+ sizeof (daemon)) == 0 &&
+ getsockname (testsock, (struct sockaddr *) &client, &length) != -1 &&
+ client.sin_addr.s_addr != htonl (INADDR_ANY))
+ {
+ *result = client.sin_addr;
+ ktalk_debug("Found reply address");
+ ::close (testsock);
+ break;
+ }
+ if (testsock >= 0) ::close (testsock);
+ }
+ if (i == 2) {
+ *result = defaultReplyAddr;
+ ktalk_debug("Couldn't find reply address, using default");
+ }
+ if (Options.debug_mode) {
+ help1 = (unsigned char *) &destination;
+ help2 = (unsigned char *) result;
+ syslog ( LOG_DEBUG,
+ "detected reply address for %d.%d.%d.%d: %d.%d.%d.%d",
+ help1 [0], help1 [1], help1 [2], help1 [3],
+ help2 [0], help2 [1], help2 [2], help2 [3]);
+ /* replyAddrList.insert ((long) destination.s_addr, result); disabled */
+ }
+ return *result;
+}
+/* QIntDict <in_addr> TalkConnection::replyAddrList; */
+
+/** Check the remote protocol. Result stored in <protocol>.
+ * @return 1 if succeeded to find at least 1 protocol */
+void TalkConnection::findProtocol() {
+
+ ktalk_debug("Remote protocol unknown. Trying to find it. findProtocol()");
+ /* The existing ctl_sockt will be used for ntalk */
+ int new_socket = ctl_sockt;
+ /* We need a new SOCK_DGRAM socket for otalk */
+ struct sockaddr_in old_ctl_addr;
+ int old_socket = open_socket(&old_ctl_addr, SOCK_DGRAM);
+
+ /* Fill the old_msg return-address to match the address of old_socket */
+ old_msg.ctl_addr = *(struct talk_addr *)&old_ctl_addr;
+ old_msg.ctl_addr.ta_family = htons(AF_INET);
+
+ /* Prepare two LOOK_UP ctl messages */
+ old_msg.type = LOOK_UP;
+ old_msg.id_num = htonl(0L);
+ new_msg.type = LOOK_UP;
+ new_msg.id_num = htonl(0L);
+ char svg_r_name[NEW_NAME_SIZE]; // Save the real r_name
+ strcpy(svg_r_name, new_msg.r_name);
+ strcpy(old_msg.r_name, "ktalk");
+ strcpy(new_msg.r_name, "ktalk");
+
+ struct sockaddr_in daemon;
+ daemon.sin_family = AF_INET;
+ daemon.sin_addr = his_machine_addr;
+
+ /* Prepare the variables used for reading on sockets */
+ fd_set read_mask, ctl_mask;
+ int nready=0, cc;
+ struct timeval wait;
+
+ FD_ZERO(&ctl_mask);
+ FD_SET(new_socket, &ctl_mask);
+ FD_SET(old_socket, &ctl_mask);
+ int max_socket = (new_socket > old_socket) ? new_socket : old_socket;
+
+ /* Method : we send the two packets to the two remote daemons.
+ We wait for the first one correct answer, and then we stop everything.
+ If a wrong answer comes, ignore it (but note that we got it).
+ If no answer (or two wrong answers), retry, up to 3 times. */
+
+ for (int retry = 0; (retry < 3) && (protocol==noProtocol); retry ++)
+ {
+ ktalk_debug("Send packets. Retry = %d",retry);
+ /* Send the messages */
+ daemon.sin_port = ntalkDaemonPort;
+ int len = sendto (new_socket, (char *) &new_msg, sizeof new_msg, 0,
+ (struct sockaddr *) &daemon, sizeof daemon);
+ if (len != sizeof new_msg)
+ syslog(LOG_ERR, "findProtocol: sendto() for ntalk failed!");
+
+ daemon.sin_port = talkDaemonPort;
+ len = sendto (old_socket, (char *) &old_msg, sizeof old_msg, 0,
+ (struct sockaddr *) &daemon, sizeof daemon);
+ if (len != sizeof old_msg)
+ syslog(LOG_ERR, "findProtocol: sendto() for otalk failed!");
+
+ do {
+ /* Wait for response */
+ read_mask = ctl_mask;
+ wait.tv_sec = CTL_WAIT;
+ wait.tv_usec = 0;
+ nready = ::select(max_socket+1, &read_mask, 0, 0, &wait);
+ if (nready < 0) {
+ if (errno == EINTR)
+ continue;
+ // Timeout. Let's exit this loop and retry sending.
+ break;
+ }
+ if (nready == 0) ktalk_debug("select returned 0 ! ");
+
+ /* Analyze response */
+ if (FD_ISSET(old_socket, &read_mask)) {
+ ktalk_debug("Reading on old_socket");
+ cc = ::recv(old_socket, (char *)&old_resp, sizeof (old_resp), 0);
+ if (cc < 0) {
+ if (errno == EINTR)
+ continue;
+ // Wrong answer (e.g. too short).
+ } else
+ if (old_resp.type == LOOK_UP) protocol=talkProtocol; // FOUND
+ }
+ if (FD_ISSET(new_socket, &read_mask)) {
+ ktalk_debug("Reading on new_socket");
+ cc = ::recv(new_socket, (char *)&new_resp, sizeof (new_resp), 0);
+ if (cc < 0) {
+ if (errno == EINTR)
+ continue;
+ // Wrong answer (e.g. too short). Note ntalk answered
+ } else
+ if ((new_resp.type == LOOK_UP) && (new_resp.vers == TALK_VERSION))
+ protocol=ntalkProtocol;
+ }
+ } while (protocol==noProtocol);
+ // wait for a time out, or ok.
+ } // for
+
+ /* restore the real r_name */
+ strncpy(old_msg.r_name, svg_r_name, OLD_NAME_SIZE);
+ strncpy(new_msg.r_name, svg_r_name, NEW_NAME_SIZE);
+ /* restore old.ctl_addr */
+ old_msg.ctl_addr = new_msg.ctl_addr;
+ ::close(old_socket);
+ ktalk_debug("Exiting findProtocol");
+ if (protocol==ntalkProtocol) ktalk_debug("Exiting findProtocol with protocol = NTALK");
+ else if (protocol==talkProtocol) ktalk_debug("Exiting findProtocol with protocol = NTALK");
+ else p_error("FATAL : no protocol found.");
+}
+
+void TalkConnection::set_addr(const struct talk_addr * addr)
+{
+ old_msg.addr = *addr;
+ old_msg.addr.ta_family = htons(AF_INET);
+ new_msg.addr = *addr;
+ new_msg.addr.ta_family = htons(AF_INET);
+}
+
+void TalkConnection::set_ctl_addr(const struct talk_addr * ctl_addr)
+{
+ old_msg.ctl_addr = *ctl_addr;
+ old_msg.ctl_addr.ta_family = htons(AF_INET);
+ new_msg.ctl_addr = *ctl_addr;
+ new_msg.ctl_addr.ta_family = htons(AF_INET);
+}
+
+void TalkConnection::close_sockets()
+{
+ if (sockt!=-1) { close(sockt); sockt = -1; }
+ if (ctl_sockt!=-1) { close(ctl_sockt); ctl_sockt = -1; }
+}
+
+/*
+ * SOCKDGRAM is unreliable, so we must repeat messages if we have
+ * not received an acknowledgement within a reasonable amount
+ * of time
+ */
+void TalkConnection::ctl_transact(int type, int id_num)
+{
+
+ if (protocol == noProtocol)
+ /** We've been so far, but we still don't know which protocol to use.
+ * Let's check it, the way ktalk does. */
+ findProtocol();
+
+ fd_set read_mask, ctl_mask;
+ int nready=0, cc, size, ok=0;
+ struct timeval wait;
+ struct sockaddr_in daemon_addr;
+ char * msg;
+
+ if (protocol == talkProtocol) {
+ old_msg.type = type;
+ old_msg.id_num = htonl(id_num);
+ msg = (char *)&old_msg;
+ size = sizeof old_msg;
+ } else {
+ new_msg.type = type;
+ new_msg.id_num = htonl(id_num);
+ msg = (char *)&new_msg;
+ size = sizeof new_msg;
+ print_request("ctl_transact: ",&new_msg);
+ }
+
+ daemon_addr.sin_family = AF_INET;
+ daemon_addr.sin_addr = his_machine_addr;
+ daemon_addr.sin_port = (protocol == talkProtocol) ? talkDaemonPort : ntalkDaemonPort;
+ FD_ZERO(&ctl_mask);
+ FD_SET(ctl_sockt, &ctl_mask);
+
+ /* Keep sending the message until a response of
+ * the proper type is obtained.
+ */
+ do {
+ /* resend message until a response is obtained */
+ do {
+ cc = sendto(ctl_sockt, msg, size, 0,
+ (struct sockaddr *)&daemon_addr,
+ sizeof (daemon_addr));
+ if (cc != size) {
+ if (errno == EINTR)
+ continue;
+ p_error("Error on write to talk daemon");
+ }
+ read_mask = ctl_mask;
+ wait.tv_sec = CTL_WAIT;
+ wait.tv_usec = 0;
+ nready = ::select(ctl_sockt+1, &read_mask, 0, 0, &wait);
+ if (nready < 0) {
+ if (errno == EINTR)
+ continue;
+ p_error("Error waiting for daemon response");
+ }
+ if (nready == 0) ktalk_debug("select returned 0 ! ");
+ } while (nready == 0);
+ /*
+ * Keep reading while there are queued messages
+ * (this is not necessary, it just saves extra
+ * request/acknowledgements being sent)
+ */
+ do {
+ if (protocol == talkProtocol)
+ cc = ::recv(ctl_sockt, (char *)&old_resp, sizeof (old_resp), 0);
+ else
+ cc = ::recv(ctl_sockt, (char *)&new_resp, sizeof (new_resp), 0);
+ if (cc < 0) {
+ if (errno == EINTR)
+ continue;
+ p_error("Error on read from talk daemon");
+ }
+ read_mask = ctl_mask;
+ /* an immediate poll */
+ timerclear(&wait);
+ nready = ::select(ctl_sockt+1, &read_mask, 0, 0, &wait);
+ if (protocol == talkProtocol) ok = (old_resp.type == type);
+ else ok = ((new_resp.type == type) && (new_resp.vers == TALK_VERSION));
+ } while (nready > 0 && (!ok));
+ } while (!ok);
+ if (protocol == talkProtocol) {
+ old_resp.id_num = ntohl(old_resp.id_num);
+ old_resp.addr.ta_family = ntohs(old_resp.addr.ta_family);
+ } else {
+ new_resp.id_num = ntohl(new_resp.id_num);
+ new_resp.addr.ta_family = ntohs(new_resp.addr.ta_family);
+ }
+}
+
+/** Look for an invitation on remote machine */
+int TalkConnection::look_for_invite(int mandatory)
+{
+ /* Check for invitation on caller's machine */
+ ctl_transact(LOOK_UP, 0);
+
+ uint8_t answer;
+ uint32_t id_num;
+ getResponseItems(&answer, &id_num, &lookup_addr);
+
+ if (!mandatory) return 0;
+
+ /* the switch is for later options, such as multiple invitations */
+ switch (answer) {
+
+ case SUCCESS:
+ new_msg.id_num = htonl(id_num);
+ old_msg.id_num = htonl(id_num);
+ ktalk_debug("TalkConnection::look_for_invite : got SUCCESS");
+ if (lookup_addr.ta_family != AF_INET)
+ p_error("Response uses invalid network address");
+ return (1);
+
+ default:
+ /* there wasn't an invitation waiting for us */
+ ktalk_debug("TalkConnection::look_for_invite : didn't get SUCCESS");
+ return (0);
+ }
+}
+
+/** Prepare to accept a connection from another talk client */
+void TalkConnection::listen()
+{
+ if (::listen(sockt, SOMAXCONN) != 0)
+ p_error("Error on attempt to listen for caller");
+}
+
+/** Accept a connection from another talk client */
+int TalkConnection::accept()
+{
+ int accept_sockt;
+ while ((accept_sockt = ::accept(sockt, 0, 0)) < 0) {
+ if (errno == EINTR)
+ continue;
+ p_error("Unable to connect with your party");
+ }
+ ::close(sockt);
+ sockt = accept_sockt;
+ return sockt;
+}
+
+/** Connect to another talk client. */
+int TalkConnection::connect()
+{
+ ktalk_debug("Waiting to connect");
+ do {
+ errno = 0;
+ if (::connect(sockt, (const sockaddr *)(&lookup_addr), sizeof (struct talk_addr)) != -1)
+ return 1;
+ } while (errno == EINTR);
+ if (errno == ECONNREFUSED) {
+ /*
+ * The caller gave up, but his invitation somehow
+ * was not cleared. Clear it and initiate an
+ * invitation. (We know there are no newer invitations,
+ * the talkd works LIFO.)
+ */
+ ktalk_debug("ECONNREFUSED");
+ ctl_transact(DELETE, 0);
+ close_sockets();
+ return 0;
+ }
+ p_error("Unable to connect with initiator");
+ /*NOTREACHED*/
+ return 0;
+}
+
+/** Trade edit characters with the other talk. By agreement
+ * the first three characters each talk transmits after
+ * connection are the three edit characters.
+ * A normal talk client uses tcgetattr() to get the chars,
+ * but the daemon isn't connected to a terminal, so we can't call it.
+ * We just send dummy chars, to disable control chars. */
+void TalkConnection::set_edit_chars()
+{
+ char buf[3];
+ int cc;
+ buf[0] = buf[1] = buf[2] = (char)0xff;
+ /* Write our config to the caller */
+ cc = write(sockt, buf, sizeof(buf));
+ if (cc != sizeof(buf) )
+ p_error("Lost the connection");
+ /* Read the caller configuration */
+ cc = read(sockt, buf, sizeof(buf));
+ if (cc != sizeof(buf) )
+ p_error("Lost the connection");
+ char_erase = buf[0]; // store it in TalkConnection
+}
+
+void TalkConnection::write_banner(char * banner)
+{ /* writes the message 'banner', null-terminated */
+ int count = strlen(banner);
+ int nbsent;
+ char * str = banner;
+ /* message_d("Count : %d.",count); */
+ while (count>0) {
+ /* let's send 16 -bytes-max packets */
+ if (count>=16) nbsent = write(sockt,str,16);
+ else nbsent = write(sockt,str,count);
+ count -= nbsent;
+ str += nbsent;
+ fsync(sockt);
+ }
+ write(sockt,"\n",1);
+}
+
+void TalkConnection::getResponseItems(uint8_t * answer, uint32_t * id_num, struct talk_addr * addr) {
+ if (protocol == talkProtocol) {
+ if (answer) *answer = old_resp.answer;
+ if (id_num) *id_num = old_resp.id_num;
+ if (addr) *addr = old_resp.addr;
+ } else {
+ if (answer) *answer = new_resp.answer;
+ if (id_num) *id_num = new_resp.id_num;
+ if (addr) *addr = new_resp.addr;
+ }
+}
+
+/** p_error prints the system error message in the log
+ * and then exits. */
+void TalkConnection::p_error(const char *str)
+{
+ syslog(LOG_ERR, "%s", str);
+ _exit(0);
+}
diff --git a/ktalkd/ktalkd/machines/talkconn.h b/ktalkd/ktalkd/machines/talkconn.h
new file mode 100644
index 00000000..dd08e10a
--- /dev/null
+++ b/ktalkd/ktalkd/machines/talkconn.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California, (c) 1998 David Faure
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+#ifndef TALKCONNECTION_H
+#define TALKCONNECTION_H
+
+#include "../includ.h"
+
+enum ProtocolType {noProtocol, talkProtocol, ntalkProtocol};
+
+class TalkConnection
+{
+ public:
+
+ /** Global initialization. To be called once. */
+ static void init();
+
+ /** Create a talk connection.
+ * @param r_addr Remote machine IP address
+ * @param r_name Remote user name
+ * @param l_name Local user name
+ * @param _protocol Caller's protocol */
+ TalkConnection(struct in_addr r_addr,
+ char * r_name,
+ char * l_name,
+ ProtocolType _protocol);
+
+ /** Destructor. Closes the sockets if opened.*/
+ ~TalkConnection();
+
+ /** Create the sockets */
+ void open_sockets();
+ /** Close the sockets */
+ void close_sockets();
+
+ /** Methods for talking to remote daemon */
+ void ctl_transact(int type, int id_num);
+ int look_for_invite(int mandatory);
+
+ /** Connect with address given back in response to LOOK_UP. */
+ int connect();
+ /** Prepare to accept a connection from another talk client */
+ void listen();
+ /** Accept a connection from another talk client */
+ int accept();
+
+ /** Exchange the first 3 characters, which are edit characters */
+ void set_edit_chars();
+
+ /** Write data into the socket, by 16 char blocks. */
+ void write_banner(char * banner);
+
+ // Methods to retrieve some information
+ /** Returns the erase char used by the caller. */
+ char get_char_erase() { return char_erase; }
+ /** Returns the caller's name. */
+ char * get_caller_name() { return new_msg.r_name; } // idem in old_msg
+ /** Returns socket, for reading or writing */
+ int get_sockt() { return sockt; }
+ /** Returns response answer and id_num. Allows protocol independence.
+ * Each param can be null (0L). Then it isn't filled.*/
+ void getResponseItems(uint8_t * answer, uint32_t * id_num, struct talk_addr * addr);
+ /** Returns connection socket address. For FWT. */
+ const struct talk_addr get_addr() { return new_msg.addr; } // idem in old_msg
+
+ // Methods to cheat with this talk connection
+ // Used by the forwarding machine
+ void set_addr(const struct talk_addr * addr);
+ void set_ctl_addr(const struct talk_addr * ctl_addr);
+
+ /** Prints the system error message in the log and exits the current thread */
+ static void p_error(const char * str);
+
+ protected:
+
+ /** Used by open_sockets. */
+ int open_socket (struct sockaddr_in *addr, int type);
+ /** Check remote protocol. Used by ctl_transact. */
+ void findProtocol();
+ /** Find the correct IP address that the daemon at host
+ "destination" has to respond to */
+ static struct in_addr getReplyAddr (struct in_addr destination);
+ static struct in_addr defaultReplyAddr;
+
+ static short int talkDaemonPort; // Port number of talk demon (517)
+ static short int ntalkDaemonPort; // Port number of ntalk demon (518)
+
+ ProtocolType protocol;
+
+ /* inet addresses of the two machines */
+ struct in_addr my_machine_addr;
+ struct in_addr his_machine_addr;
+
+ int ctl_sockt;
+ int sockt;
+
+ OLD_CTL_MSG old_msg; // holds interesting data
+ NEW_CTL_MSG new_msg; // holds interesting data
+ OLD_CTL_RESPONSE old_resp; // only convenience structure for responses
+ NEW_CTL_RESPONSE new_resp; // only convenience structure for responses
+ struct talk_addr lookup_addr; // address returned by LOOKUP. Points to xxx_resp.addr
+
+ char char_erase;
+};
+
+#endif
diff --git a/ktalkd/ktalkd/options.cpp b/ktalkd/ktalkd/options.cpp
new file mode 100644
index 00000000..5596f8ef
--- /dev/null
+++ b/ktalkd/ktalkd/options.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright 1999-2002 David Faure <faure@kde.org>
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+#include "defs.h"
+
+struct sOptions Options = {
+/* answmach :*/ 1,
+/* time_before_answmach :*/ 20,
+/* sound :*/ 0,
+/* XAnnounce :*/ 1,
+/* soundfile :*/ {""},
+/* soundplayer :*/ {""},
+/* soundplayeropt :*/ {""},
+/* announce1 :*/ {ANNOUNCE1},
+/* announce2 :*/ {ANNOUNCE2},
+/* announce3 :*/ {ANNOUNCE3},
+/* invitelines :*/ {INVITE_LINES},
+/* mailprog :*/ {"mail.local"},
+/* NEU_behaviour :*/ 2,
+/* NEU_user :*/ {""},
+/* NEUBanner1 :*/ {NEU_BANNER1},
+/* NEUBanner2 :*/ {NEU_BANNER2},
+/* NEUBanner3 :*/ {NEU_BANNER3},
+/* NEU_forwardmethod :*/ {"FWR"},
+/* extprg :*/ {""},
+/* hostname :*/ {""},
+/* debug_mode :*/ 0
+};
diff --git a/ktalkd/ktalkd/options.h b/ktalkd/ktalkd/options.h
new file mode 100644
index 00000000..42b72350
--- /dev/null
+++ b/ktalkd/ktalkd/options.h
@@ -0,0 +1,71 @@
+/**
+ * Copyright 2002 David Faure <faure@kde.org>
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+#ifndef OPTIONS_H
+#define OPTIONS_H
+/** This struct handles global options. Not user ones */
+
+#include <sys/utsname.h>
+#ifndef SYS_NMLN
+#define SYS_NMLN 65 /* max hostname size */
+#endif
+
+struct sOptions
+{
+ int answmach; /* used in talkd.cpp */
+ int time_before_answmach; /* in machines/answmach.cpp */
+ /* used in announce.cpp : */
+ int sound;
+ int XAnnounce;
+ char soundfile [S_CFGLINE];
+ char soundplayer [S_CFGLINE];
+ char soundplayeropt [S_CFGLINE];
+ char announce1 [S_CFGLINE];
+ char announce2 [S_CFGLINE];
+ char announce3 [S_CFGLINE];
+ char invitelines [S_INVITE_LINES];/* used in machines/answmach.cpp */
+ char mailprog [S_CFGLINE]; /* used in machines/answmach.cpp */
+ int NEU_behaviour;
+ char NEU_user[S_CFGLINE];
+ char NEUBanner1 [S_CFGLINE];
+ char NEUBanner2 [S_CFGLINE];
+ char NEUBanner3 [S_CFGLINE];
+ char NEU_forwardmethod [5];
+ char extprg [S_CFGLINE];
+ // No really an option, but it's useful to have it here :
+ char hostname[SYS_NMLN];
+ int debug_mode;
+};
+
+extern struct sOptions Options;
+
+#endif
diff --git a/ktalkd/ktalkd/otalkd.h b/ktalkd/ktalkd/otalkd.h
new file mode 100644
index 00000000..cacb17ca
--- /dev/null
+++ b/ktalkd/ktalkd/otalkd.h
@@ -0,0 +1,64 @@
+/**
+ * Copyright 2002 David Faure <faure@kde.org>
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+#ifndef OTALKD_H
+#define OTALKD_H
+
+#include "prot_talkd.h"
+
+#define OLD_NAME_SIZE 9
+
+/* Control Message structure for old talk protocol (earlier than BSD4.2) */
+
+typedef struct {
+ char type; /* request type, see below */
+ char l_name [OLD_NAME_SIZE]; /* caller's name */
+ char r_name [OLD_NAME_SIZE]; /* callee's name */
+ char pad;
+ int id_num; /* message id */
+ int pid; /* caller's process id */
+ char r_tty [TTY_SIZE]; /* callee's tty name */
+ struct talk_addr addr; /* socket address for connection */
+ struct talk_addr ctl_addr; /* control socket address */
+} OLD_CTL_MSG;
+
+/* Control Response structure for old talk protocol (earlier than BSD4.2) */
+
+typedef struct {
+ char type; /* type of request message, see below */
+ char answer; /* response to request message, see below */
+ char pad [2];
+ int id_num; /* message id */
+ struct talk_addr addr; /* address for establishing conversation */
+} OLD_CTL_RESPONSE;
+
+#endif
diff --git a/ktalkd/ktalkd/print.c b/ktalkd/ktalkd/print.c
new file mode 100644
index 00000000..ea8fe95c
--- /dev/null
+++ b/ktalkd/ktalkd/print.c
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+/* debug print routines */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <syslog.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <config.h>
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+#include <errno.h>
+#include "prot_talkd.h"
+#include "proto.h"
+
+#ifndef _PATH_VAR_LOG
+#define _PATH_VAR_LOG "/var/log/"
+#endif
+
+#define NTYPES 4
+static const char *types[NTYPES] = {
+ "LEAVE_INVITE",
+ "LOOK_UP",
+ "DELETE",
+ "ANNOUNCE"
+};
+
+#define NANSWERS 9
+static const char *answers[NANSWERS] = {
+ "SUCCESS",
+ "NOT_HERE",
+ "FAILED",
+ "MACHINE_UNKNOWN",
+ "PERMISSION_DENIED",
+ "UNKNOWN_REQUEST",
+ "BADVERSION",
+ "BADADDR",
+ "BADCTLADDR"
+};
+
+static int logging, badpackets;
+static int logfd, packfd;
+
+void
+set_debug(int l, int b)
+{
+ const char *file;
+ logging = l;
+ badpackets = b;
+ if (logging) {
+ file = _PATH_VAR_LOG "talkd.log";
+ logfd = open(file, O_WRONLY|O_APPEND);
+ if (logfd<0) {
+ syslog(LOG_WARNING, "%s: %s", file, strerror(errno));
+ logging = 0;
+ }
+ }
+ if (badpackets) {
+ file = _PATH_VAR_LOG "talkd.packets";
+ packfd = open(file, O_WRONLY|O_APPEND);
+ if (packfd<0) {
+ syslog(LOG_WARNING, "%s: %s", file, strerror(errno));
+ badpackets = 0;
+ }
+ }
+}
+
+/****************** ktalkd addition ************/
+
+/* print_addr is a debug print routine for sockaddr_in structures.
+ * Call with a structure in network byte order.
+ * @param cp a string to identify the log output
+ * @param addr the address to read
+ * This code is obviously NOT IPv6 ready :)
+ */
+static void print_addr(const char *cp, struct sockaddr_in * addr)
+{
+ int a0,a1,a2,a3;
+ unsigned int s_add = addr->sin_addr.s_addr;
+ char buf[1024];
+ a0 = s_add % 256L;
+ s_add /= 256L;
+ a1 = s_add % 256L;
+ s_add /= 256L;
+ a2 = s_add % 256L;
+ s_add /= 256L;
+ a3 = s_add % 256L;
+ snprintf(buf, sizeof(buf), "%s: addr = %d.%d.%d.%d port = %o, family = %o",
+ cp, a0, a1, a2, a3, ntohs(addr->sin_port), ntohs(addr->sin_family));
+ write(logfd, buf, strlen(buf));
+}
+
+/**********************************************/
+
+static const char *
+print_type(int type)
+{
+ static char rv[80];
+ if (type > NTYPES) {
+ snprintf(rv, sizeof(rv), "type %d", type);
+ return rv;
+ }
+ return types[type];
+}
+
+static const char *
+print_answer(int answer)
+{
+ static char rv[80];
+ if (answer > NANSWERS) {
+ snprintf(rv, sizeof(rv), "answer %d", answer);
+ return rv;
+ }
+ return answers[answer];
+}
+
+void
+print_request(const char *cp, const CTL_MSG *mp)
+{
+ char lu[NAME_SIZE+1], ru[NAME_SIZE+1], tt[TTY_SIZE+1];
+ char buf[1024];
+ const char *tp;
+ if (!logging) return;
+
+ tp = print_type(mp->type);
+ strncpy(lu, mp->l_name, sizeof(lu));
+ strncpy(ru, mp->r_name, sizeof(ru));
+ strncpy(tt, mp->r_tty, sizeof(tt));
+ lu[sizeof(lu)-1]=0;
+ ru[sizeof(ru)-1]=0;
+ tt[sizeof(tt)-1]=0;
+
+ snprintf(buf, sizeof(buf),
+ "%s: %s: id %u, l_user %s, r_user %s, r_tty %s\n",
+ cp, tp, mp->id_num, lu, ru, tt);
+ write(logfd, buf, strlen(buf));
+ print_addr(" addr", (struct sockaddr_in *)&mp->addr);
+ print_addr(" ctl_addr", (struct sockaddr_in *)&mp->ctl_addr);
+}
+
+void
+print_response(const char *cp, const CTL_RESPONSE *rp)
+{
+ char buf[1024];
+ const char *tp, *ap;
+ if (!logging) return;
+
+ tp = print_type(rp->type);
+ ap = print_answer(rp->answer);
+
+ snprintf(buf, sizeof(buf),
+ "%s: %s <-- %s, id %d\n",
+ cp, tp, ap, ntohl(rp->id_num));
+ write(logfd, buf, strlen(buf));
+ if ((rp->type == LOOK_UP) && (rp->answer == SUCCESS))
+ print_addr(" resp addr", (struct sockaddr_in *)&rp->addr);
+}
+
+void
+ktalk_debug(const char *format, ...)
+{
+ char buf[1024];
+ va_list ap;
+ if (!logging) return;
+
+ va_start(ap, format);
+ vsnprintf(buf, sizeof(buf), format, ap);
+ va_end(ap);
+ write(logfd, buf, strlen(buf));
+}
+
+void
+print_broken_packet(const char *pack, size_t len, struct sockaddr_in *from)
+{
+ size_t i;
+ char tmp[4], buf[128];
+ if (!badpackets) return;
+ snprintf(buf, sizeof(buf), "From: %s [%u]",
+ inet_ntoa(from->sin_addr), from->sin_addr.s_addr);
+ write(packfd, buf, strlen(buf));
+ for (i=0; i<len; i++) {
+ if (i%24 == 0) write(packfd, "\n ", 5);
+ snprintf(tmp, sizeof(tmp), "%02x ", (unsigned char)pack[i]);
+ write(packfd, tmp, strlen(tmp));
+ }
+ write(packfd, "\n", 1);
+}
diff --git a/ktalkd/ktalkd/process.cpp b/ktalkd/ktalkd/process.cpp
new file mode 100644
index 00000000..6c638d57
--- /dev/null
+++ b/ktalkd/ktalkd/process.cpp
@@ -0,0 +1,285 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California,
+ * (c) 1998 David Faure.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+/*
+ * process.cpp handles the requests, which can be of three types:
+ * ANNOUNCE - announce to a user that a talk is wanted
+ * LEAVE_INVITE - insert the request into the table
+ * LOOK_UP - look up to see if a request is waiting in
+ * in the table for the local user
+ * DELETE - delete invitation
+ */
+
+#include "includ.h"
+#include <sys/param.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#include <netdb.h>
+#include <syslog.h>
+#include <stdio.h>
+#include <string.h>
+#include <pwd.h>
+#include <stdlib.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+
+#include "process.h"
+#include "proto.h"
+#include "announce.h"
+#include "find_user.h"
+#include "table.h"
+#include "readconf.h"
+#include "defs.h"
+#include "machines/forwmach.h"
+
+extern KTalkdTable * ktable;
+
+int prepare_response(NEW_CTL_MSG *mp, NEW_CTL_RESPONSE *rp)
+{
+ rp->type = mp->type;
+ rp->id_num = htonl(0);
+ rp->vers = mp->vers;
+ if ((mp->vers != 0) && (mp->vers != TALK_VERSION)) {
+ syslog(LOG_WARNING, "Bad protocol version %d", mp->vers);
+ rp->answer = BADVERSION;
+ return 0;
+ }
+ mp->id_num = ntohl(mp->id_num);
+#if 0 // not in the original talkd anymore?
+ mp->addr.ta_family = ntohs(mp->addr.ta_family);
+ mp->ctl_addr.ta_family = ntohs(mp->ctl_addr.ta_family);
+ if (mp->ctl_addr.ta_family != AF_INET) {
+ syslog(LOG_WARNING, "Bad address, family %d",
+ mp->ctl_addr.ta_family);
+ rp->answer = BADCTLADDR;
+ return 0;
+ }
+#endif
+ mp->pid = ntohl(mp->pid);
+ return 1; /* Ok */
+}
+
+int process_request(NEW_CTL_MSG *mp, NEW_CTL_RESPONSE *rp, const char* theirhost)
+{
+ NEW_CTL_MSG *ptr;
+ int ret;
+ int usercfg = 0; /* set if a user config file exists */
+
+ print_request("process_request", mp);
+
+ if (!prepare_response(mp, rp))
+ return PROC_REQ_ERR;
+
+ /* Ensure null-termination */
+ mp->l_name[sizeof(mp->l_name)-1] = 0;
+ mp->r_name[sizeof(mp->r_name)-1] = 0;
+ mp->r_tty[sizeof(mp->r_tty)-1] = 0;
+
+ ret = PROC_REQ_OK;
+
+ switch (mp->type) {
+
+ case ANNOUNCE:
+ /* Open user config file. */
+ usercfg = init_user_config(mp->r_name);
+ ret = do_announce(mp, rp, theirhost, usercfg);
+ if (usercfg) end_user_config();
+
+ /* Store in table if normal announce or answmach replacing it.
+ Not if re-announce, nor if error, nor for forwarding machine */
+ if ((ret == PROC_REQ_OK) || (ret == PROC_REQ_ANSWMACH_NOT_LOGGED)
+ || (ret == PROC_REQ_ANSWMACH_NOT_HERE))
+ ktable->insert_table(mp, rp, 0L);
+
+ case LEAVE_INVITE:
+ ptr = ktable->find_request(mp);
+ if (ptr != (NEW_CTL_MSG *)0) {
+ rp->id_num = htonl(ptr->id_num);
+ rp->answer = SUCCESS;
+ } else
+ ktable->insert_table(mp, rp, 0L);
+ break;
+
+ case LOOK_UP:
+ ptr = ktable->find_match(mp);
+ if (ptr != (NEW_CTL_MSG *)0) {
+ rp->id_num = htonl(ptr->id_num);
+ rp->addr = ptr->addr;
+ rp->addr.ta_family = htons(ptr->addr.ta_family);
+ rp->answer = SUCCESS;
+ } else {
+ if (ForwMachine::forwMachProcessLookup(ktable->getTable(), mp)) {
+ ret = PROC_REQ_FORWMACH; // Don't send any response, forwmach will do it
+ } else
+ rp->answer = NOT_HERE;
+ }
+ break;
+
+ case DELETE:
+ rp->answer = ktable->delete_invite(mp->id_num);
+ break;
+
+ default:
+ rp->answer = UNKNOWN_REQUEST;
+ break;
+ }
+ if (ret != PROC_REQ_FORWMACH)
+ print_response("=> response", rp);
+ if (mp->vers == 0) { // it's kotalkd talking to us.
+ // Let's prepare an OTALK response, shifting the first 2 fields
+ rp->vers /*type in otalk*/ = rp->type;
+ rp->type /*answer in otalk*/ = rp->answer;
+ }
+ return ret;
+}
+
+int do_announce(NEW_CTL_MSG *mp, NEW_CTL_RESPONSE *rp, const char* theirhost, int usercfg)
+{
+ NEW_CTL_MSG *ptr;
+ int result;
+ char disp[DISPLAYS_LIST_MAX];
+ char forward[S_CFGLINE], forwardMethod[4];
+ char * callee;
+ ForwMachine * fwm;
+
+ /* Check if already in the table */
+ ptr = ktable->find_request(mp);
+
+ /* TODO use Voodo #1 from current process.c for byte-swapping stuff */
+
+ if ((ptr != NULL) && (
+ (mp->id_num <= ptr->id_num) || (mp->id_num == (uint32_t)~0x0L))) {
+ /* a duplicated request, so ignore it */
+ ktalk_debug("dupannounce %d", mp->id_num);
+ rp->id_num = htonl(ptr->id_num);
+ rp->answer = SUCCESS;
+ return PROC_REQ_ERR;
+
+ } else {
+ if (ptr == (NEW_CTL_MSG *) 0) { /* Not already in the table */
+ /* see if a forward has been set up */
+ if ( (usercfg)
+ && (read_user_config("Forward", forward, S_CFGLINE))
+ && (read_user_config("ForwardMethod", forwardMethod, 4 )) )
+ {
+ fwm = new ForwMachine(mp, forward, forwardMethod, mp->id_num);
+ /* Store in table, because :
+ (Any method) : this allows to detect dupannounces.
+ (FWR & FWT) : we'll receive the LOOK_UP */
+ ktable->insert_table(mp, 0L, fwm);
+ fwm->start(mp->id_num);
+ return PROC_REQ_FORWMACH;
+ }
+ }
+
+ /* see if the user is logged */
+ char tty[ UT_LINESIZE ]; // mp->r_tty may be smaller then UT_LINESIZE
+ *tty = '\0';
+ result = find_user(mp->r_name, tty, disp);
+ strlcpy( mp->r_tty, tty, sizeof( mp->r_tty ));
+
+ ktalk_debug("find_user : result = %d",result);
+
+ if (result != SUCCESS) {
+ ktalk_debug("Couldn t find user ...");
+ if (result == NOT_HERE)
+ { /* Not here ? -> Start answering machine ! */
+ if (getpwnam(mp->r_name)) /* Does the user exist ? */
+ { /* Yes ! -> SUCCESS. */
+ ktalk_debug("Not logged.");
+ rp->answer = SUCCESS;
+ endpwent();
+ return PROC_REQ_ANSWMACH_NOT_LOGGED; /* answer machine. */
+ } else
+ { /* Non-existent user ... */
+ endpwent();
+ /* output an error into the logs */
+
+ syslog(LOG_ERR,"User unknown : %s.",mp->r_name);
+ syslog(LOG_ERR,"The caller is : %s.",mp->l_name);
+
+ switch (Options.NEU_behaviour) {
+ case 2: /* Paranoid setting. Do nothing. */
+ ktalk_debug("Paranoid setting. Do nothing.");
+ rp->answer = NOT_HERE;
+ return PROC_REQ_ERR;
+ case 0: /* Launch answering machine. */
+ ktalk_debug("Not here.");
+ rp->answer = SUCCESS;
+ return PROC_REQ_ANSWMACH_NOT_HERE;
+ case 1: /* NEU_user will take the talk. */
+ ktalk_debug("Not here. I ll take the talk.");
+ fwm = new ForwMachine(mp, Options.NEU_user,
+ Options.NEU_forwardmethod, mp->id_num);
+ /* store in table, because we'll receive the LOOK_UP */
+ ktable->insert_table(mp, 0L, fwm);
+ fwm->start(mp->id_num);
+ return PROC_REQ_FORWMACH;
+ } /* switch */
+ } /* getpwnam */
+ } /* result */
+ else {
+ ktalk_debug("not SUCCESS, nor NOT_HERE");
+ rp->answer = result; /* not SUCCESS, nor NOT_HERE*/
+ return PROC_REQ_ERR;
+ }
+ }
+
+ /* Check if there is a forwarding machine on this machine,
+ matching answerer = r_name and caller = l_name
+ Then set callee to the initial callee, to display in ktalkdlg */
+ callee = ForwMachine::forwMachFindMatch(ktable->getTable(), mp);
+
+ if (ptr == (NEW_CTL_MSG *) 0) { /* Not already in the table => announce */
+ rp->answer = announce(mp, theirhost, disp, usercfg, callee);
+ if (rp->answer == PERMISSION_DENIED) return PROC_REQ_ERR;
+ ktalk_debug("Announce done.");
+ return PROC_REQ_OK;
+ } else {
+ /* This is an explicit re-announce, so update the id_num
+ * field to avoid duplicates and re-announce the talk. */
+ int new_id_num = ktable->new_id();
+ if (Options.debug_mode)
+ syslog(LOG_DEBUG, "reannounce : updating id %d to id %d",
+ ptr->id_num, new_id_num);
+ ptr->id_num = new_id_num; /* update in the table */
+ rp->id_num = htonl(ptr->id_num);
+ rp->answer = announce(mp, theirhost, disp, usercfg, callee);
+ return PROC_REQ_ANSWMACH;
+ }
+ }
+}
diff --git a/ktalkd/ktalkd/process.h b/ktalkd/ktalkd/process.h
new file mode 100644
index 00000000..090824ff
--- /dev/null
+++ b/ktalkd/ktalkd/process.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California, (c) 1997 David Faure
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ *
+ */
+
+#include "includ.h"
+
+int process_request(NEW_CTL_MSG *mp, NEW_CTL_RESPONSE *rp, const char* theirhost );
+int do_announce(NEW_CTL_MSG *mp, NEW_CTL_RESPONSE *rp, const char* theirhost, int usercfg);
+
diff --git a/ktalkd/ktalkd/prot_talkd.h b/ktalkd/ktalkd/prot_talkd.h
new file mode 100644
index 00000000..eca1e64b
--- /dev/null
+++ b/ktalkd/ktalkd/prot_talkd.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ *
+ * @(#)talkd.h 8.1 (Berkeley) 6/2/93
+ */
+
+#ifndef _PROTOCOLS_TALKD_H
+#define _PROTOCOLS_TALKD_H
+
+/*
+ * This describes the protocol used by the talk server and clients.
+ *
+ * The talk server acts a repository of invitations, responding to
+ * requests by clients wishing to rendezvous for the purpose of
+ * holding a conversation. In normal operation, a client, the caller,
+ * initiates a rendezvous by sending a CTL_MSG to the server of
+ * type LOOK_UP. This causes the server to search its invitation
+ * tables to check if an invitation currently exists for the caller
+ * (to speak to the callee specified in the message). If the lookup
+ * fails, the caller then sends an ANNOUNCE message causing the server
+ * to broadcast an announcement on the callee's login ports requesting
+ * contact. When the callee responds, the local server uses the
+ * recorded invitation to respond with the appropriate rendezvous
+ * address and the caller and callee client programs establish a
+ * stream connection through which the conversation takes place.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <sys/types.h>
+
+/*
+ * This is a copy of 4.3BSD struct sockaddr.
+ */
+struct talk_addr {
+ uint16_t ta_family;
+ uint16_t ta_port;
+ uint32_t ta_addr;
+ uint32_t ta_junk1;
+ uint32_t ta_junk2;
+};
+
+
+/*
+ * Client->server request message format.
+ */
+#define NAME_SIZE 12
+#define TTY_SIZE 16
+
+typedef struct {
+ uint8_t vers; /* protocol version */
+ uint8_t type; /* request type, see below */
+ uint8_t answer; /* not used */
+ uint8_t pad;
+ uint32_t id_num; /* message id */
+ struct talk_addr addr; /* address of client tcp port */
+ struct talk_addr ctl_addr; /* address of client udp port */
+ uint32_t pid; /* caller's process id */
+ char l_name[NAME_SIZE];/* caller's name */
+ char r_name[NAME_SIZE];/* callee's name */
+ char r_tty[TTY_SIZE];/* callee's tty name */
+} CTL_MSG;
+
+/*
+ * Server->client response message format.
+ */
+typedef struct {
+ uint8_t vers; /* protocol version */
+ uint8_t type; /* type of request message, see below */
+ uint8_t answer; /* respose to request message, see below */
+ uint8_t pad;
+ uint32_t id_num; /* message id */
+ struct talk_addr addr; /* address for establishing conversation */
+} CTL_RESPONSE;
+
+#define TALK_VERSION 1 /* protocol version */
+
+/* message type values */
+#define LEAVE_INVITE 0 /* leave invitation with server */
+#define LOOK_UP 1 /* check for invitation by callee */
+#define DELETE 2 /* delete invitation by caller */
+#define ANNOUNCE 3 /* announce invitation by caller */
+
+/* answer values */
+#define SUCCESS 0 /* operation completed properly */
+#define NOT_HERE 1 /* callee not logged in */
+#define FAILED 2 /* operation failed for unexplained reason */
+#define MACHINE_UNKNOWN 3 /* caller's machine name unknown */
+#define PERMISSION_DENIED 4 /* callee's tty doesn't permit announce */
+#define UNKNOWN_REQUEST 5 /* request has invalid type value */
+#define BADVERSION 6 /* request has invalid protocol version */
+#define BADADDR 7 /* request has invalid addr value */
+#define BADCTLADDR 8 /* request has invalid ctl_addr value */
+
+/*
+ * Operational parameters.
+ * RING_WAIT should be 10's of seconds less than MAX_LIFE
+ */
+#define MAX_LIFE 60 /* max time daemon saves invitations */
+#define RING_WAIT 30 /* time to wait before resending invitation */
+
+#endif /* !_PROTOCOLS_TALKD_H */
diff --git a/ktalkd/ktalkd/proto.h b/ktalkd/ktalkd/proto.h
new file mode 100644
index 00000000..7679a9f2
--- /dev/null
+++ b/ktalkd/ktalkd/proto.h
@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* quirks for repairs.c */
+
+#define QUIRK_NONE 0
+#define QUIRK_OTALK 1
+
+struct sockaddr_in;
+
+extern char ourhostname[];
+
+/* print.c */
+void print_request(const char *cp, const CTL_MSG *mp);
+void print_response(const char *cp, const CTL_RESPONSE *rp);
+void print_broken_packet(const char *pack, size_t len, struct sockaddr_in *);
+void ktalk_debug(const char *fmt, ...);
+void set_debug(int logging, int badpackets);
+
+/* table.c */
+/*void insert_table(CTL_MSG *request, CTL_RESPONSE *response);
+ CTL_MSG *find_request(CTL_MSG *request);
+ CTL_MSG *find_match(CTL_MSG *request);
+*/
+
+/* repairs.c */
+uint32_t byte_swap32(uint32_t);
+int rationalize_packet(char *buf, size_t len, size_t maxlen,
+ struct sockaddr_in *);
+size_t irrationalize_reply(char *buf, size_t maxbuf, int quirk);
+
+/* other */
+/*int announce(CTL_MSG *request, const char *remote_machine);
+void process_request(CTL_MSG *mp, CTL_RESPONSE *rp, const char *fromhost);
+int new_id(void);
+int delete_invite(unsigned id_num);
+*/
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/ktalkd/ktalkd/readcfg++.cpp b/ktalkd/ktalkd/readcfg++.cpp
new file mode 100644
index 00000000..07a10dc2
--- /dev/null
+++ b/ktalkd/ktalkd/readcfg++.cpp
@@ -0,0 +1,261 @@
+/* This file is part of ktalkd
+ *
+ * Copyright (C) 1997 David Faure (faure@kde.org)
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ *
+ */
+
+/*
+ * Routines for reading configuration from KDE configuration
+ * for ktalkd.
+ */
+
+/* Unix includes */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <pwd.h>
+
+/* KDE & Qt includes */
+#include <kconfig.h>
+#include <kstandarddirs.h>
+#include <kinstance.h>
+#include <qfile.h>
+
+/* Ktalkd includes */
+#include "readconf.h"
+#include "defs.h"
+#include <config.h>
+
+#include "proto.h"
+
+/** Converts a string s into a boolean. Handles 0/1, on/off, true/false.
+ * (case insensitive) */
+int booleanresult(const char * s)
+{
+ if (strlen(s)==1)
+ { return atoi(s); }
+ else if ((!strncasecmp(s,"on",2))||(!strncasecmp(s,"true",4))) {return 1;}
+ else if ((!strncasecmp(s,"off",3))||(!strncasecmp(s,"false",5))){return 0;}
+ else {
+ syslog(LOG_ERR,"Wrong boolean value %s in ktalkdrc",s);
+ return 0;
+ }
+}
+
+/* User configuration file, ktalkdrc in localconfigdir(). */
+KConfig * ktalkdcfg = 0;
+/* User config file for talk announces, ktalkannouncerc in localconfigdir(). */
+KConfig * ktkanncfg = 0;
+
+/* Initiate user-config-file reading. */
+int init_user_config(const char * l_name)
+{
+ struct passwd * pw = getpwnam(l_name);
+
+ if (!pw) return 0;
+ else {
+ struct stat buf;
+ QString cfgFileName, tkannFileName;
+
+ /* Set $HOME, because KInstance uses it */
+ setenv("HOME",pw->pw_dir,1 /* overwrite */);
+ unsetenv("KDEHOME");
+ unsetenv("XAUTHORITY");
+ktalk_debug("%s",pw->pw_dir);
+ KInstance tmpInstance("tmpInstance");
+ cfgFileName = locateLocal("config", "ktalkdrc", &tmpInstance );
+ tkannFileName = locateLocal("config", "ktalkannouncerc", &tmpInstance);
+ endpwent();
+ktalk_debug("%s","endpwent");
+
+//WABA: New KConfig should be able to handle this gracefully:
+ if (stat(QFile::encodeName(tkannFileName),&buf)!=-1) {
+ // check if it exists, 'cause otherwise it would be created empty with
+ // root as owner !
+ ktkanncfg = new KConfig(tkannFileName);
+ ktkanncfg -> setGroup("ktalkannounce");
+ ktkanncfg -> setDollarExpansion(true);
+ } else ktkanncfg = 0L;
+ if (stat(QFile::encodeName(cfgFileName),&buf)!=-1) {
+ // check if it exists, 'cause otherwise it would be created empty with
+ // root as owner !
+ ktalkdcfg = new KConfig(cfgFileName);
+ ktalkdcfg -> setGroup("ktalkd");
+ ktalkdcfg -> setDollarExpansion(true);
+ ktalk_debug("User config file ok");
+ } else {
+ ktalkdcfg = 0L;
+ ktalk_debug("No user config file %s !",cfgFileName.ascii());
+ }
+ktalk_debug("%s","done");
+ return ((ktkanncfg != 0L) || (ktalkdcfg != 0L));
+ /* Return true if at least one file exists */
+ }
+}
+
+/*
+ * Read one entry in user-config-file
+ */
+
+int read_user_config(const char * key, char * result, int max)
+{
+ KConfig * cfg;
+ if (!strncmp(key,"Sound",5))
+ // Any key starting with Sound is in ktalkannouncerc
+ // talkprg is there too, but we don't care about it here
+ cfg = ktkanncfg;
+ else
+ cfg = ktalkdcfg;
+
+ if (!cfg) return 0; // file doesn't exist
+ QString Qresult;
+ if ((Qresult = cfg -> readEntry(key, "unset")) != "unset")
+ {
+ qstrncpy( result, Qresult.ascii(), max);
+
+ if (Options.debug_mode) syslog(LOG_DEBUG,"User option %s : %s", key, result);
+ return 1;
+ }
+ else
+ {
+ ktalk_debug("User option %s NOT found", key);
+ return 0;
+ }
+}
+
+int read_bool_user_config(const char * key, int * result)
+{
+ char msgtmpl[S_CFGLINE];
+ int ret = read_user_config(key, msgtmpl, S_CFGLINE); // ret=1 if found
+ if (ret!=0) *result = booleanresult(msgtmpl);
+ return ret;
+}
+
+// Close user-config-file and destroys objects used.
+
+void end_user_config()
+{
+ if (ktalkdcfg) delete ktalkdcfg;
+ if (ktkanncfg) delete ktkanncfg;
+ ktalkdcfg = 0L;
+ ktkanncfg = 0L;
+}
+
+// System configuration file
+
+int process_config_file(void)
+{
+ // Where is ktalkdlg installed ?
+ QString ktalkdlg_dir = locate("exe", "ktalkdlg");
+ ktalkdlg_dir.truncate( ktalkdlg_dir.findRev('/') );
+ // Has to be done, for any $KDEBINDIR in ktalkdrc.
+ setenv("KDEBINDIR", QFile::encodeName(ktalkdlg_dir), 0/*don't overwrite*/);
+
+ KConfig * syscfg = new KConfig( "ktalkdrc" );
+
+ syscfg -> setGroup("ktalkd");
+ syscfg -> setDollarExpansion(true);
+
+ QString result;
+
+#define found(k) (!(result = syscfg -> readEntry(k)).isEmpty())
+ // QString cfgStr = cfgStr0.stripWhiteSpace();
+
+ if (found("AnswMach")) {
+ Options.answmach=booleanresult(result.ascii());
+ ktalk_debug("AnswMach : %d",Options.answmach);}
+
+ if (found("XAnnounce")) {
+ Options.XAnnounce=booleanresult(result.ascii());
+ ktalk_debug("XAnnounce : %d",Options.XAnnounce); }
+
+ if (found("Time")) {
+ Options.time_before_answmach=result.toInt();
+ ktalk_debug("Time : %d",Options.time_before_answmach); }
+
+ if (found("Sound")) {
+ Options.sound=booleanresult(result.ascii());
+ ktalk_debug("Sound : %d",Options.sound); }
+
+ if (found("SoundFile")) {
+ qstrncpy(Options.soundfile,QFile::encodeName(result),S_CFGLINE);
+ ktalk_debug("SoundFile = %s",Options.soundfile); }
+
+ if (found("SoundPlayer")) {
+ qstrncpy(Options.soundplayer,QFile::encodeName(result),S_CFGLINE);
+ ktalk_debug("SoundPlayer = %s",Options.soundplayer); }
+
+ if (found("SoundPlayerOpt")) {
+ qstrncpy(Options.soundplayeropt,QFile::encodeName(result),S_CFGLINE);
+ ktalk_debug("SoundPlayerOpt = %s",Options.soundplayeropt); }
+
+ if (found("MailProg")) {
+ qstrncpy(Options.mailprog,QFile::encodeName(result),S_CFGLINE);
+ ktalk_debug("Mail prog = %s",Options.mailprog); }
+
+ /* text based announcement */
+ if (found("Announce1")) { qstrncpy(Options.announce1,result.local8Bit(),S_CFGLINE); }
+ if (found("Announce2")) { qstrncpy(Options.announce2,result.local8Bit(),S_CFGLINE); }
+ if (found("Announce3")) { qstrncpy(Options.announce3,result.local8Bit(),S_CFGLINE); }
+
+ if (found("NEUUser")) {
+ qstrncpy(Options.NEU_user,result.local8Bit(),S_CFGLINE);
+ ktalk_debug("NEUUser = %s", Options.NEU_user);
+ }
+ if (found("NEUBehaviour")) {
+ Options.NEU_behaviour=result.toInt();
+ ktalk_debug("NEUBehaviour : %d",Options.NEU_behaviour);
+ }
+ if (found("NEUForwardMethod")) {
+ qstrncpy(Options.NEU_forwardmethod,result.ascii(),5);
+ ktalk_debug("NEUForwardMethod = %s", Options.NEU_forwardmethod);
+ }
+
+ if (found("ExtPrg")) {
+ qstrncpy(Options.extprg,QFile::encodeName(result),S_CFGLINE);
+ ktalk_debug("Ext prg = %s",Options.extprg); }
+ else { /* has to work even without config file at all */
+ KStandardDirs stddirs;
+ qstrncpy(Options.extprg, QFile::encodeName(stddirs.findResource("exe","ktalkdlg")), S_CFGLINE-1);
+ }
+
+ delete syscfg;
+ ktalk_debug("End of global configuration");
+ return 1;
+}
+
+
diff --git a/ktalkd/ktalkd/readconf.cpp b/ktalkd/ktalkd/readconf.cpp
new file mode 100644
index 00000000..7b48b0c1
--- /dev/null
+++ b/ktalkd/ktalkd/readconf.cpp
@@ -0,0 +1,188 @@
+/*
+ * readconf.cpp
+ *
+ * Routines for reading talkd.conf and .talkdrc
+ *
+ * Copyright 1999-2002, David Faure, faure@kde.org
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ *
+ */
+
+#include <pwd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include "proto.h"
+#include "readconf.h"
+#include "defs.h"
+
+FILE * fd;
+
+int booleanresult(const char * s)
+{
+ if (strlen(s)==1)
+ { return atoi(s); }
+ else if ((!strncasecmp(s,"on",2))||(!strncasecmp(s,"true",4))) {return 1;}
+ else if ((!strncasecmp(s,"off",3))||(!strncasecmp(s,"false",5))){return 0;}
+ else {
+ syslog(LOG_ERR,"Wrong boolean value %s in %s",s,TALKD_CONF);
+ return 0;
+ }
+}
+
+/* obsolete routine, but still used for .talkdrc */
+int read_user_config(const char * key, char * result, int max)
+{
+ char * value;
+ char * buff = new char[S_CFGLINE];
+ char * ret;
+ fseek(fd,0,SEEK_SET);
+ do {
+ ret = fgets(buff,S_CFGLINE,fd);
+ } while ((ret) && (strncasecmp(buff,key,strlen(key))));
+ if (ret) {
+ value = strchr(buff,':')+1;
+ while (isspace(*value)) value++; /* get rid of spaces, tabs... */
+ strncpy(result,value,max);
+ result[max-1]='\0'; /* in case it was longer than max chars */
+ char * lastchar = result + strlen(result) - 1; /* points to last char */
+ if (*lastchar=='\n') *lastchar = '\0'; /* get rid of \n */
+ }
+ delete buff;
+ if (Options.debug_mode) {
+ if (ret)
+ syslog(LOG_DEBUG,"read_user_config : %s, %s",key,result);
+ else
+ syslog(LOG_DEBUG,"read_user_config : %s -> not found",key);
+ }
+ return (ret) ? 1 : 0;
+}
+
+int read_bool_user_config(const char * key, int * result)
+{
+ char msgtmpl[S_CFGLINE];
+ int ret = read_user_config(key, msgtmpl, S_CFGLINE); // ret=1 if found
+ if (ret!=0) *result = booleanresult(msgtmpl);
+ return ret;
+}
+
+int init_user_config(const char * l_name)
+{
+#define S_MSGPATH 50
+ char msgpath[S_MSGPATH];
+ struct passwd * pw = getpwnam(l_name);
+ if (!pw) return 0;
+ else {
+ snprintf(msgpath, S_MSGPATH, "%s/.talkdrc", pw->pw_dir);
+ endpwent();
+ fd=fopen(msgpath, "r");
+ return (fd != 0);
+ }
+}
+
+void end_user_config()
+{
+ fclose(fd);
+}
+
+/* routine for reading talkd.conf */
+
+int process_config_file(void)
+{
+ FILE * fd = fopen(TALKD_CONF,"r");
+ char * ret;
+ char buff[S_CFGLINE];
+ char * result;
+
+#define found(k) (!strncasecmp(buff,k,strlen(k)))
+
+ if (!fd) { return 0; }
+ do {
+ ret = fgets(buff,S_CFGLINE,fd);
+ if ((ret) && (*buff!='#') && ((result = strchr(buff,':')))) {
+ result++;
+ while (isspace(*result))
+ result++; /* get rid of spaces, tabs... */
+ result[strlen(result)-1]='\0'; /* get rid of \n */
+
+ if (found("AnswMach:")) {
+ Options.answmach=booleanresult(result);
+ debug("AnswMach : %d",Options.answmach);}
+
+ if (found("XAnnounce:")) {
+ Options.XAnnounce=booleanresult(result);
+ debug("XAnnounce : %d",Options.XAnnounce); }
+
+ if (found("Time:")) {
+ Options.time_before_answmach=atoi(result);
+ debug("Time : %d",Options.time_before_answmach); }
+
+ if (found("Sound:")) {
+ Options.sound=booleanresult(result);
+ debug("Sound : %d",Options.sound); }
+
+ if (found("SoundFile:")) {
+ strncpy(Options.soundfile,result,S_CFGLINE);
+ debug("SoundFile =");debug(Options.soundfile); }
+
+ if (found("SoundPlayer:")) {
+ strncpy(Options.soundplayer,result,S_CFGLINE);
+ debug("SoundPlayer ="); debug(result); }
+
+ if (found("SoundPlayerOpt:")) {
+ strncpy(Options.soundplayeropt,result,S_CFGLINE);
+ debug("SoundPlayerOpt ="); debug(result); }
+
+ if (found("MailProg:")) {
+ strncpy(Options.mailprog,result,S_CFGLINE);
+ debug("Mail prog ="); debug(result); }
+
+ /* text based announcement */
+ if (found("Announce1")) { strncpy(Options.announce1,result,S_CFGLINE); }
+ if (found("Announce2")) { strncpy(Options.announce2,result,S_CFGLINE); }
+ if (found("Announce3")) { strncpy(Options.announce3,result,S_CFGLINE); }
+
+ if (found("NEUUser")) { strncpy(Options.NEU_user,result,S_INVITE_LINES); }
+ if (found("NEUBehaviour")) { Options.NEU_behaviour=booleanresult(result); }
+ if (found("NEUBanner1")) { strncpy(Options.NEUBanner1,result,S_CFGLINE); }
+ if (found("NEUBanner2")) { strncpy(Options.NEUBanner2,result,S_CFGLINE); }
+ if (found("NEUBanner3")) { strncpy(Options.NEUBanner3,result,S_CFGLINE); }
+ if (found("NEUForwardMethod")) { strncpy(Options.NEU_forwardmethod,result,5); }
+
+ }
+ } while (ret);
+ fclose(fd);
+ return 1;
+}
diff --git a/ktalkd/ktalkd/readconf.h b/ktalkd/ktalkd/readconf.h
new file mode 100644
index 00000000..b80b6a02
--- /dev/null
+++ b/ktalkd/ktalkd/readconf.h
@@ -0,0 +1,69 @@
+/* This file is part of ktalkd
+ *
+ * Copyright (C) 1997 David Faure (faure@kde.org)
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+/** Warning to ktalkd hackers : this file is the definition of a
+ * generic interface for reading systemwide and user configuration.
+ * Two files implement it : readconf.cpp for non-KDE users
+ * and readcfg++.cpp for KDE users.
+ */
+
+ /**
+ * Initiate user-config-file reading.
+ *
+ * @param l_name User name
+ */
+int init_user_config(const char * l_name);
+
+ /**
+ * Read one entry in user-config-file.
+ *
+ * @param key Key, such as "Mail", "Subj", "Head", "Msg1".."Msg3"
+ */
+int read_user_config(const char * key, char * result, int max);
+
+ /**
+ * Read a boolean entry in user-config-file.
+ *
+ * @param key Key, such as "Sound"
+ */
+int read_bool_user_config(const char * key, int * result);
+
+ /**
+ * Close user-config-file and destroys objects used.
+ */
+void end_user_config();
+
+ /**
+ * Read all site-wide configuration in one pass
+ */
+int process_config_file(void);
diff --git a/ktalkd/ktalkd/repairs.c b/ktalkd/ktalkd/repairs.c
new file mode 100644
index 00000000..9473a473
--- /dev/null
+++ b/ktalkd/ktalkd/repairs.c
@@ -0,0 +1,274 @@
+/*
+ * Copyright 1998 David A. Holland.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder(s) nor the names of their
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+char repairs_rcsid[] =
+ "$Id$";
+
+/*
+ * Most, but not quite all, of the voodoo for detecting and handling
+ * malformed packets goes here.
+ *
+ * Basically anything which we can detect by examining the packet we
+ * try to do in rationalize_packet(). We return a quirk code for what
+ * we did so we can send back replies that will make sense to the other
+ * end. That's irrationalize_reply().
+ */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <syslog.h>
+#include "prot_talkd.h"
+#include "proto.h"
+
+uint32_t
+byte_swap32(uint32_t k)
+{
+ return (k<<24) | ((k&0xff00) << 8) | ((k>>8) & 0xff00) | (k>>24);
+}
+
+static uint16_t
+byte_swap16(uint16_t k)
+{
+ return (k<<8) | (k>>8);
+}
+
+/***************************************************/
+
+/*
+ * probe for strings that are meaningful in talkd packets.
+ * rejects all control characters and delete. newlines and tabs have
+ * no business in tty names or usernames.
+ */
+static int probe_string(const char *buf, size_t len) {
+ size_t i;
+ int ch;
+ for (i=0; i<len; i++) {
+ if (buf[i]==0) return 0; /* success */
+ ch = (unsigned char) buf[i];
+ if ((ch&127) < 32 || (ch&127)==127) return -1;
+ }
+ return -1; /* no null-terminator, assume it's not a string */
+}
+
+/*
+ * Check if an address from a talk packet matches the actual sender
+ * address. If it doesn't, it's a good bet it's not the right packet format.
+ * Allow assorted endianness though.
+ * In an ideal world we'd save the endianness info for use elsewhere instead
+ * of reprobing it, but oh well.
+ */
+static int probe_addr(struct talk_addr *ta, struct sockaddr_in *sn) {
+ uint16_t family = sn->sin_family;
+ uint16_t xfamily = byte_swap16(family);
+ uint16_t port = sn->sin_port;
+ uint16_t xport = byte_swap16(port);
+ uint32_t addr = sn->sin_addr.s_addr;
+ uint32_t xaddr = byte_swap32(addr);
+
+ if (ta->ta_family != family && ta->ta_family != xfamily) return -1;
+ if (ta->ta_port != port && ta->ta_port != xport) return -1;
+ if (ta->ta_addr != addr && ta->ta_addr != xaddr) return -1;
+ return 0;
+}
+
+/***************************************************/
+
+/*
+ * warning warning: in some cases this packet may need compiler
+ * pragmas to force the compiler to not pad it. shouldn't with
+ * gcc though.
+ */
+
+#define OTALK_PACKET_SIZE 76
+
+#define OLD_NAME_SIZE 9
+struct otalk_packet {
+ char type;
+ char l_name[OLD_NAME_SIZE];
+ char r_name[OLD_NAME_SIZE];
+ char filler;
+ uint32_t id_num;
+ uint32_t pid;
+ char r_tty[TTY_SIZE];
+ struct talk_addr addr;
+ struct talk_addr ctl_addr;
+};
+
+struct otalk_reply {
+ char type;
+ char answer;
+ uint16_t filler;
+ uint32_t id_num;
+ struct talk_addr addr;
+};
+
+/* additional types */
+#define OLD_DELETE_INVITE 4
+#define OLD_AUTO_LOOK_UP 5
+#define OLD_AUTO_DELETE 6
+
+static int probe_otalk_packet(char *buf, size_t len, size_t maxlen,
+ struct sockaddr_in *sn)
+{
+ struct otalk_packet otp;
+ CTL_MSG m;
+
+ ktalk_debug("Probing for QUIRK_OTALK\n");
+
+ if (sizeof(otp)!=OTALK_PACKET_SIZE) {
+ syslog(LOG_ERR, "QUIRK_OTALK: struct otalk_packet padding "
+ "is wrong\n");
+ return -1;
+ }
+
+ if (len!=sizeof(otp)) {
+ ktalk_debug("QUIRK_OTALK: wrong size\n");
+ return -1;
+ }
+
+ memcpy(&otp, buf, len);
+ if (probe_string(otp.l_name, sizeof(otp.l_name))) {
+ ktalk_debug("QUIRK_OTALK: l_name not a string\n");
+ return -1;
+ }
+ if (probe_string(otp.r_name, sizeof(otp.r_name))) {
+ ktalk_debug("QUIRK_OTALK: r_name not a string\n");
+ return -1;
+ }
+ if (probe_string(otp.r_tty, sizeof(otp.r_tty))) {
+ ktalk_debug("QUIRK_OTALK: r_tty not a string\n");
+ return -1;
+ }
+ if (probe_addr(&otp.ctl_addr, sn)) {
+ ktalk_debug("QUIRK_OTALK: addresses do not match\n");
+ return -1;
+ }
+
+ switch (otp.type) {
+ case LEAVE_INVITE:
+ case LOOK_UP:
+ case DELETE:
+ case ANNOUNCE:
+ break;
+ /* I'm not sure these will work. */
+ case OLD_DELETE_INVITE: otp.type = DELETE; break;
+ case OLD_AUTO_LOOK_UP: otp.type = LOOK_UP; break;
+ case OLD_AUTO_DELETE: otp.type = DELETE; break;
+ default:
+ ktalk_debug("QUIRK_OTALK: invalid type field\n");
+ return -1;
+ }
+
+ if (OLD_NAME_SIZE >= NAME_SIZE) {
+ syslog(LOG_ERR, "QUIRK_OTALK: OLD_NAME_SIZE >= NAME_SIZE\n");
+ syslog(LOG_ERR, "QUIRK_OTALK: fix repairs.c and recompile\n");
+ return -1;
+ }
+ if (maxlen < sizeof(CTL_MSG)) {
+ syslog(LOG_ERR, "QUIRK_OTALK: maxlen too small; enlarge "
+ "inbuf[] in talkd.c and recompile\n");
+ return -1;
+ }
+
+ m.vers = TALK_VERSION;
+ m.type = otp.type;
+ m.answer = 0;
+ m.pad = 0;
+ m.id_num = otp.id_num;
+ m.addr = otp.addr;
+ m.ctl_addr = otp.ctl_addr;
+ m.pid = otp.pid;
+ memcpy(m.l_name, otp.l_name, OLD_NAME_SIZE);
+ m.l_name[OLD_NAME_SIZE] = 0;
+ memcpy(m.r_name, otp.r_name, OLD_NAME_SIZE);
+ m.r_name[OLD_NAME_SIZE] = 0;
+ memcpy(m.r_tty, otp.r_tty, TTY_SIZE);
+ m.r_tty[TTY_SIZE-1] = 0;
+ memcpy(buf, &m, sizeof(m));
+ return 0;
+}
+
+static size_t do_otalk_reply(char *buf, size_t maxlen) {
+ struct otalk_reply or;
+ CTL_RESPONSE *r = (CTL_RESPONSE *)buf;
+ if (sizeof(or) > maxlen) {
+ syslog(LOG_ERR, "QUIRK_OTALK: reply: maxlen too small; "
+ "enlarge buf[] in send_packet and recompile\n");
+ return sizeof(CTL_RESPONSE);
+ }
+
+ /*
+ * If we changed the type above, this might break. Should we encode
+ * it in the quirk code?
+ */
+ or.type = r->type;
+ or.answer = r->answer;
+ or.filler = 0;
+ or.id_num = r->id_num;
+ or.addr = r->addr;
+ memcpy(buf, &or, sizeof(or));
+ return sizeof(or);
+}
+
+/***************************************************/
+
+
+/*
+ * Return 0 if the packet's normal, -1 if we can't figure it out,
+ * otherwise a quirk code from the quirk list.
+ *
+ * For now, we don't support any quirks. Need more data.
+ */
+int
+rationalize_packet(char *buf, size_t len, size_t mlen, struct sockaddr_in *sn)
+{
+ if (len == sizeof(CTL_MSG)) {
+ return 0;
+ }
+
+ ktalk_debug("Malformed packet (length %u)\n", len);
+
+ if (probe_otalk_packet(buf, len, mlen, sn)==0) {
+ ktalk_debug("Using QUIRK_OTALK\n");
+ return QUIRK_OTALK;
+ }
+ return -1;
+}
+
+size_t
+irrationalize_reply(char *buf, size_t maxlen, int quirk)
+{
+ switch (quirk) {
+ case QUIRK_NONE: return sizeof(CTL_RESPONSE);
+ case QUIRK_OTALK: return do_otalk_reply(buf, maxlen);
+ }
+ /* ? */
+ return 0;
+}
diff --git a/ktalkd/ktalkd/table.cpp b/ktalkd/ktalkd/table.cpp
new file mode 100644
index 00000000..1a3713e3
--- /dev/null
+++ b/ktalkd/ktalkd/table.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+/*
+ * Routines to handle insertion, deletion, etc on the table
+ * of requests kept by the daemon. Nothing fancy here, linear
+ * search on a double-linked list. A time is kept with each
+ * entry so that overly old invitations can be eliminated.
+ */
+
+#include "table.h"
+#include <sys/param.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <syslog.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include "proto.h"
+#include "defs.h"
+#include "machines/forwmach.h"
+
+#define MAX_ID 16000 /* << 2^15 so I don't have sign troubles */
+
+#define NIL ((TABLE_ENTRY *)0)
+
+/*
+ * Look in the table for an invitation that matches the current
+ * request looking for an invitation
+ */
+NEW_CTL_MSG * KTalkdTable::find_match(NEW_CTL_MSG *request)
+{
+ register TABLE_ENTRY *ptr;
+ time_t current_time;
+
+ (void) gettimeofday(&tp, &txp);
+ current_time = tp.tv_sec;
+ for (ptr = table; ptr != NIL; ptr = ptr->next) {
+ if ((ptr->time - current_time) > MAX_LIFE) {
+ /* the entry is too old */
+ ktalk_debug("deleting expired entry : id %d",
+ ptr->request.id_num);
+ delete_entry(ptr);
+ continue;
+ }
+ if ((strcmp(request->l_name, ptr->request.r_name) == 0) &&
+ (strcmp(request->r_name, ptr->request.l_name) == 0) &&
+ (ptr->request.type == LEAVE_INVITE) && (ptr->fwm == 0L))
+ { /* Not the forw. machines : they aren't stored to match LOOK_UPs */
+ ktalk_debug("Found match : id %d", ptr->request.id_num);
+ return (&ptr->request);
+ }
+ }
+ return ((NEW_CTL_MSG *)0);
+}
+
+/*
+ * Look for an identical request, as opposed to a complimentary
+ * one as find_match does
+ */
+NEW_CTL_MSG * KTalkdTable::find_request(NEW_CTL_MSG *request)
+{
+ register TABLE_ENTRY *ptr;
+ time_t current_time;
+
+ (void) gettimeofday(&tp, &txp);
+ current_time = tp.tv_sec;
+ /*
+ * See if this is a repeated message, and check for
+ * out of date entries in the table while we are it.
+ */
+ for (ptr = table; ptr != NIL; ptr = ptr->next) {
+ if ((ptr->time - current_time) > MAX_LIFE) {
+ /* the entry is too old */
+ ktalk_debug("deleting expired entry : id %d",
+ ptr->request.id_num);
+ delete_entry(ptr);
+ continue;
+ }
+ if (strcmp(request->r_name, ptr->request.r_name) == 0 &&
+ strcmp(request->l_name, ptr->request.l_name) == 0 &&
+ request->type == ptr->request.type &&
+ request->pid == ptr->request.pid) {
+ /* update the time if we 'touch' it */
+ ptr->time = current_time;
+ ktalk_debug("Found identical request : id %d", ptr->request.id_num);
+ return (&ptr->request);
+ }
+ }
+ return ((NEW_CTL_MSG *)0);
+}
+
+void KTalkdTable::insert_table(NEW_CTL_MSG *request, NEW_CTL_RESPONSE *response, ForwMachine * fwm)
+{
+ register TABLE_ENTRY *ptr;
+ time_t current_time;
+
+ gettimeofday(&tp, &txp);
+ current_time = tp.tv_sec;
+ request->id_num = new_id();
+ ktalk_debug("Stored as id %d",request->id_num);
+ if (response != 0L) response->id_num = htonl(request->id_num);
+ /* insert a new entry into the top of the list */
+ ptr = new TABLE_ENTRY;
+ if (ptr == NIL) {
+ syslog(LOG_ERR, "insert_table: Out of memory");
+ _exit(1);
+ }
+ ptr->fwm = fwm;
+ ptr->time = current_time;
+ ptr->request = *request;
+ ptr->next = table;
+ if (ptr->next != NIL)
+ ptr->next->last = ptr;
+ ptr->last = NIL;
+ table = ptr;
+}
+
+/*
+ * Generate a unique non-zero sequence number
+ */
+int KTalkdTable::new_id()
+{
+ static int current_id = 0;
+
+ current_id = (current_id + 1) % MAX_ID;
+ /* 0 is reserved, helps to pick up bugs */
+ if (current_id == 0)
+ current_id = 1;
+ return (current_id);
+}
+
+/*
+ * Delete the invitation with id 'id_num'
+ */
+int KTalkdTable::delete_invite(uint32_t id_num)
+{
+ register TABLE_ENTRY *ptr;
+
+ ptr = table;
+ ktalk_debug("delete_invite(%d)", id_num);
+ for (ptr = table; ptr != NIL; ptr = ptr->next) {
+ if (ptr->request.id_num == id_num)
+ break;
+ }
+ if (ptr != NIL) {
+ if (ptr->fwm) {
+ ptr->fwm->sendDelete(); // Calls processDelete() in the child process.
+ }
+ ktalk_debug("Deleted : id %d", ptr->request.id_num);
+ delete_entry(ptr);
+ return (SUCCESS);
+ }
+ return (NOT_HERE);
+}
+
+/*
+ * Classic delete from a double-linked list
+ */
+void KTalkdTable::delete_entry(register TABLE_ENTRY *ptr)
+{
+ if (table == ptr)
+ table = ptr->next;
+ else if (ptr->last != NIL)
+ ptr->last->next = ptr->next;
+ if (ptr->next != NIL)
+ ptr->next->last = ptr->last;
+ delete ptr;
+}
+
+KTalkdTable::~KTalkdTable()
+{
+ register TABLE_ENTRY *ptr;
+ ptr = table;
+ ktalk_debug("final_clean()");
+ for (ptr = table; ptr != 0L; ptr = ptr->next) {
+ if (ptr->fwm != 0L)
+ {
+ ktalk_debug("CLEAN : Found a forwarding machine to clean : id %d",ptr->request.id_num);
+ delete ptr->fwm;
+ }
+ ktalk_debug("CLEAN : Deleting id %d", ptr->request.id_num);
+ delete_entry(ptr);
+ }
+}
diff --git a/ktalkd/ktalkd/table.h b/ktalkd/ktalkd/table.h
new file mode 100644
index 00000000..edeba232
--- /dev/null
+++ b/ktalkd/ktalkd/table.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California, (c) 1997 David Faure
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ *
+ */
+#ifndef TABLE_H
+#define TABLE_H
+
+#include "includ.h"
+#include <sys/time.h>
+#include <time.h>
+
+class ForwMachine;
+
+typedef struct table_entry TABLE_ENTRY;
+
+struct table_entry {
+ NEW_CTL_MSG request;
+ long time;
+ ForwMachine * fwm;
+ TABLE_ENTRY *next;
+ TABLE_ENTRY *last;
+};
+
+class KTalkdTable
+{
+ public:
+ KTalkdTable() : table (0L) {};
+ ~KTalkdTable();
+ void insert_table(NEW_CTL_MSG *request, NEW_CTL_RESPONSE *response, ForwMachine * fwm);
+ int delete_invite(uint32_t id_num);
+ NEW_CTL_MSG * find_match(NEW_CTL_MSG *request);
+ NEW_CTL_MSG * find_request(NEW_CTL_MSG *request);
+ int new_id();
+ TABLE_ENTRY * getTable() { return table; }
+
+ private:
+ void delete_entry(register TABLE_ENTRY *ptr);
+
+ struct timeval tp;
+ struct timezone txp;
+
+ TABLE_ENTRY *table;
+};
+#endif
diff --git a/ktalkd/ktalkd/talkd.conf b/ktalkd/ktalkd/talkd.conf
new file mode 100644
index 00000000..17deef07
--- /dev/null
+++ b/ktalkd/ktalkd/talkd.conf
@@ -0,0 +1,82 @@
+# /etc/talkd.conf
+#
+# Administrator config file for ktalkd, the talk daemon by David Faure
+#
+# Wait for actual ktalkd to terminate before changes are taken into account.
+#
+# Boolean values can be 0/1, off/on, false/true
+# Syntax : "key: value" (no space between key and ':')
+# If a key is absent, the value set at compile time will be set.
+
+###### SECTION 1 : Administrator settings
+
+# Mail sender (can be mail.local, sendmail(preferred), qmail(as good:)) )
+# mail.local is included in the dist., just in case you don't have sendmail
+MailProg: mail.local
+
+# What should I do if somebody tries to talk to a non-existent user ?
+# 0 : Launch answer machine saying 'non-existent user...'
+# and 'I' receive the message (to know that it happened)
+# 1 : 'I' take the talk. (The names of caller & callee appear to 'I')
+# 2 : Do nothing. ('Not logged' will appear to caller).
+NEUBehaviour: 2
+
+# (Multi-user secured host : set Behaviour: 2).
+# (Multi-user host : set Behaviour: 0 and User: root or postmaster)
+# (Almost single-user networked PC : set Behaviour: 1 and User: your_user_name)
+
+# If you choose 0, then you can set the following
+# (no internationalization possible : this file is manually read by ktalkd)
+NEUBanner1: The person you're asking to talk with is unknown at this host.
+NEUBanner2: You may have mistyped the name, or network address. Try again
+NEUBanner3: or leave a message which will be sent to the system administrator.
+
+# Who is 'I' ? (=> who should take the talk / receive the message)
+NEUUser:
+
+# If NEUBehaviour is 1 ('I' take the talk), which forward method to use ?
+# Choose FWT if your machine is on two separate networks, and if you (NEUUser)
+# plan to set a forward. FWR is ok (and lighter) in all other cases.
+NEUForwardMethod: FWR
+
+
+##### SECTION 2 : Default values that users can overwrite
+
+# Set to 1 to activate answering machine
+# Warning : if set to 0, no user will be able to activate it.
+# (The value won't overwrite this one).
+Answmach: 1
+
+# Set this to 1 to enable X-aware announcement.
+# (Currently impossible without KDE)
+XAnnounce: 0
+
+# Set this to 'off' if all you want is a beep to notify you of talk
+# requests, to 'on' if you want to play an audio file instead.
+Sound: off
+
+# Define this to the full path of the sound file you wish to
+# have played when you receive talk requests. It may be of
+# any format, as long as the player defined below is capable
+# of playing it.
+SoundFile: /usr/lib/talkd/talk.wav
+
+# Set this to the command you will be using to play audio
+# files. This can be any .wav, .au, .snd or whatever player,
+# just so long as it will play the format that your chosen
+# audio file is in.
+SoundPlayer: /usr/local/bin/wavplay
+SoundPlayerOpt: -q
+# ==> SoundPlayer + SoundPlayerOpt = /usr/local/bin/wavplay -q
+
+# Contents of the talk request, 3 lines.
+# First one must include %d:%02d for the time of the request
+# Second one must include a '%s' for the caller name and machine
+# Third one must include %s, for caller address
+Announce1: Message from Talk_Daemon at %d:%02d ...
+Announce2: talk: connection requested by %s.
+Announce3: talk: respond with: talk %s
+
+# Time in seconds between "Ringing your party again" and launching answering
+# machine (not very important) (can't be overridden by users)
+Time: 10
diff --git a/ktalkd/ktalkd/talkd.cpp b/ktalkd/ktalkd/talkd.cpp
new file mode 100644
index 00000000..0f2f27a0
--- /dev/null
+++ b/ktalkd/ktalkd/talkd.cpp
@@ -0,0 +1,401 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California, (c) 1998 David Faure.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+char copyright[] =
+ "@(#) Copyright (c) 1983 Regents of the University of California.\n"
+ "All rights reserved.\n";
+
+/*
+ * From: @(#)talkd.c 5.8 (Berkeley) 2/26/91
+ */
+
+#include "includ.h"
+
+/*
+ * talkd - internet talk daemon
+ * loops waiting for and processing requests until idle for a while,
+ * then exits.
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <signal.h>
+#include <syslog.h>
+#include <time.h>
+#include <errno.h>
+#include <unistd.h>
+/*#include <stdio.h>*/
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+
+#include <sys/utsname.h>
+
+#include "proto.h"
+#include "process.h"
+#include "readconf.h"
+#include "defs.h"
+#include "threads.h"
+#include "table.h"
+#include "machines/answmach.h"
+#include "machines/talkconn.h"
+
+#include <kinstance.h>
+KTalkdTable * ktable;
+
+#define TIMEOUT 20
+ /* TIMEOUT was 30, but has been reduced to remove the
+ zombie process (answering machine) as soon as possible */
+#define MAXIDLE 120
+ /* #define MAXIDLE 30 for debugging purposes */
+
+#define NB_MAX_CHILD 20 /* Max number of answering / forwarding machines at a time */
+
+#if !defined(MAXHOSTNAMELEN)
+#define MAXHOSTNAMELEN 64
+#endif
+/*char ourhostname[MAXHOSTNAMELEN];*/
+
+static time_t lastmsgtime;
+
+static void
+timeout(int ignore)
+{
+ (void)ignore;
+
+ int ok_exit = ack_process();
+ if (ok_exit && (time(0) - lastmsgtime >= MAXIDLE)) {
+ delete ktable;
+ _exit(0);
+ }
+
+ signal(SIGALRM, timeout);
+ alarm(TIMEOUT);
+}
+
+/*
+ * Returns true if the address belongs to the local host. If it's
+ * not a loopback address, try binding to it.
+ */
+static int
+is_local_address(uint32_t addr)
+{
+ struct sockaddr_in sn;
+ int sock, ret;
+ if (addr == htonl(INADDR_LOOPBACK)) {
+ return 1;
+ }
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock<0) {
+ syslog(LOG_WARNING, "socket: %s", strerror(errno));
+ return 0;
+ }
+ memset(&sn, 0, sizeof(sn));
+ sn.sin_family = AF_INET;
+ sn.sin_port = htons(0);
+ sn.sin_addr.s_addr = addr;
+ ret = bind(sock, (struct sockaddr *)&sn, sizeof(sn));
+ close(sock);
+ return ret==0;
+}
+
+static void
+send_packet(CTL_RESPONSE *response, struct sockaddr_in *sn, int quirk)
+{
+ char buf[2*sizeof(CTL_RESPONSE)];
+ size_t sz = sizeof(CTL_RESPONSE);
+ int cc, err=0;
+
+ memcpy(buf, response, sz);
+ if (quirk) {
+ sz = irrationalize_reply(buf, sizeof(buf), quirk);
+ }
+ while (sz > 0) {
+ cc = sendto(1, buf, sz, 0, (struct sockaddr *)sn, sizeof(*sn));
+ if (cc<0) {
+ syslog(LOG_WARNING, "sendto: %s", strerror(errno));
+ if (err) return;
+ err = 1;
+ }
+ else sz -= cc;
+ }
+}
+
+/*
+ * Issue an error packet. Should not assume anything other than the
+ * header part (the uint8_t's) of mp is valid, and assume as little
+ * as possible about that, since it might have been a packet we
+ * couldn't dequirk.
+ */
+static void
+send_reject_packet(CTL_MSG *mp, struct sockaddr_in *sn, int code, int quirk)
+{
+ CTL_RESPONSE rsp;
+ memset(&rsp, 0, sizeof(rsp));
+ rsp.vers = TALK_VERSION;
+ rsp.type = mp->type;
+ rsp.answer = code;
+ send_packet(&rsp, sn, quirk);
+}
+
+static void
+do_one_packet(void)
+{
+ char inbuf[2*sizeof(CTL_MSG)];
+ int quirk = 0;
+ CTL_RESPONSE response;
+ CTL_MSG *mp;
+ char theirhost[MAXHOSTNAMELEN];
+ const char *theirip;
+
+ struct hostent *hp;
+ struct sockaddr_in sn;
+ int cc, i, ok;
+ socklen_t addrlen;
+ int ret_value = PROC_REQ_OK; /* return value from process_request */
+
+ addrlen = sizeof(sn);
+ cc = recvfrom(0, inbuf, sizeof(inbuf), 0,
+ (struct sockaddr *)&sn, &addrlen);
+ if (cc<0) {
+ if (errno==EINTR || errno==EAGAIN) {
+ return;
+ }
+ syslog(LOG_WARNING, "recvfrom: %s", strerror(errno));
+ return;
+ }
+
+ /*
+ * This should be set on any input, even trash, because even
+ * trash input will cause us to be restarted if we exit.
+ */
+ lastmsgtime = time(NULL);
+
+ if (addrlen!=sizeof(sn)) {
+ syslog(LOG_WARNING, "recvfrom: bogus address length");
+ return;
+ }
+ if (sn.sin_family!=AF_INET) {
+ syslog(LOG_WARNING, "recvfrom: bogus address family");
+ return;
+ }
+
+ /*
+ * If we get here we have an address we can reply to, although
+ * it may not be good for much. If possible, reply to it, because
+ * if we just drop the packet the remote talk client will keep
+ * throwing junk at us.
+ */
+ theirip = inet_ntoa(sn.sin_addr);
+ mp = (CTL_MSG *)inbuf;
+
+ /*
+ * Check they're not being weenies.
+ * We should look into using libwrap here so hosts.deny works.
+ * Wrapping talkd with tcpd isn't very useful.
+ */
+ hp = gethostbyaddr((char *)&sn.sin_addr, sizeof(struct in_addr),
+ AF_INET);
+ if (hp == NULL) {
+ syslog(LOG_WARNING, "%s: bad dns", theirip);
+ send_reject_packet(mp, &sn, MACHINE_UNKNOWN, 0);
+ return;
+ }
+ strncpy(theirhost, hp->h_name, sizeof(theirhost));
+ theirhost[sizeof(theirhost)-1] = 0;
+
+ hp = gethostbyname(theirhost);
+ if (hp == NULL) {
+ syslog(LOG_WARNING, "%s: bad dns", theirip);
+ send_reject_packet(mp, &sn, MACHINE_UNKNOWN, 0);
+ return;
+ }
+
+ for (i=ok=0; hp->h_addr_list[i] && !ok; i++) {
+ if (!memcmp(hp->h_addr_list[i], &sn.sin_addr,
+ sizeof(sn.sin_addr))) ok = 1;
+ }
+ if (!ok) {
+ syslog(LOG_WARNING, "%s: bad dns", theirip);
+ send_reject_packet(mp, &sn, MACHINE_UNKNOWN, 0);
+ return;
+ }
+
+ /*
+ * Try to straighten out bad packets.
+ */
+ quirk = rationalize_packet(inbuf, cc, sizeof(inbuf), &sn);
+ if (quirk<0) {
+ print_broken_packet(inbuf, cc, &sn);
+ syslog(LOG_WARNING, "%s (%s): unintelligible packet",
+ theirhost, theirip);
+ send_reject_packet(mp, &sn, UNKNOWN_REQUEST, 0);
+ return;
+ }
+
+ /*
+ * Make sure we know what we're getting into.
+ */
+ if (mp->vers!=TALK_VERSION) {
+ syslog(LOG_WARNING, "%s (%s): bad protocol version %d",
+ theirhost, theirip, mp->vers);
+ send_reject_packet(mp, &sn, BADVERSION, 0);
+ return;
+ }
+
+ /*
+ * LEAVE_INVITE messages should only come from localhost.
+ * Of course, old talk clients send from our hostname's IP
+ * rather than localhost, complicating the issue...
+ */
+ if (mp->type==LEAVE_INVITE && !is_local_address(sn.sin_addr.s_addr)) {
+ syslog(LOG_WARNING, "%s (%s) sent invite packet",
+ theirhost, theirip);
+ send_reject_packet(mp, &sn, MACHINE_UNKNOWN, quirk);
+ return;
+ }
+
+ /*
+ * Junk the reply address they reported for themselves. Write
+ * the real one over it because announce.c gets it from there.
+ */
+ mp->ctl_addr.ta_family = AF_INET;
+ mp->ctl_addr.ta_port = sn.sin_port;
+ mp->ctl_addr.ta_addr = sn.sin_addr.s_addr;
+
+ /*
+ * Since invite messages only come from localhost, and nothing
+ * but invite messages use the TCP address, force it to be our
+ * address.
+ *
+ * Actually, if it's a local address, leave it alone. talk has
+ * to play games to figure out the right interface address to
+ * use, and we don't want to get into that - they can work it
+ * out, but we can't since we don't know who they're trying to
+ * talk to.
+ *
+ * If it's not a local address, someone's trying to play games
+ * with us. Rather than trying to pick a local address to use,
+ * reject the packet.
+ */
+ if (mp->type==LEAVE_INVITE) {
+ mp->addr.ta_family = AF_INET;
+ if (!is_local_address(mp->addr.ta_addr)) {
+ syslog(LOG_WARNING,
+ "invite packet had bad return address");
+ send_reject_packet(mp, &sn, BADADDR, quirk);
+ return;
+ }
+ }
+ else {
+ /* non-invite packets don't use this field */
+ memset(&mp->addr, 0, sizeof(mp->addr));
+ }
+
+ ret_value = process_request(mp, &response, theirhost);
+
+ if (ret_value != PROC_REQ_FORWMACH)
+ {
+ /* can block here, is this what I want? */
+ send_packet(&response, &sn, quirk);
+ }
+
+ if (Options.answmach && (ret_value>=PROC_REQ_MIN_A))
+ {
+ ktalk_debug("Launch answer machine, mode %d.", ret_value);
+ AnswMachine::launchAnswMach(*mp, ret_value);
+ new_process();
+ }
+}
+
+/* the following copies defaults values to 'Options.*' variables so that
+ * configuration file can overwrite them */
+
+int
+main(int argc, char *argv[])
+{
+ struct sockaddr_in sn;
+ socklen_t sz = sizeof(sn);
+ int do_debug=0, do_badpackets=0, ch;
+
+ new KInstance("ktalkd"); // for KConfig and friends
+ ktable = new KTalkdTable();
+
+ /* make sure we're a daemon */
+ if (getsockname(0, (struct sockaddr *)&sn, &sz)) {
+ const char *msg = strerror(errno);
+ write(2, msg, strlen(msg));
+ exit(1);
+ }
+
+ openlog(*argv, LOG_PID, LOG_DAEMON);
+
+ /* get local machine name */
+ // using 'uname' instead of 'gethostname', as suggested by Stephan Kulow
+ struct utsname buf;
+ if (uname (&buf) == -1) {
+ syslog (LOG_ERR, "Unable to get name of local host : %s", strerror(errno));
+ exit(1);
+ }
+ strcpy(Options.hostname, buf.nodename);
+
+ /* if (gethostname(Options.hostname, sizeof (Options.hostname) - 1) < 0) {
+ syslog(LOG_ERR, "gethostname: %m");
+ exit(1);
+ }*/
+ if (chdir(_PATH_DEV) < 0) {
+ syslog(LOG_ERR, "chdir: %s: %s", _PATH_DEV, strerror(errno));
+ exit(1);
+ }
+ while ((ch = getopt(argc, argv, "dp"))!=-1) {
+ switch (ch) {
+ case 'd':
+ Options.debug_mode = 1;
+ do_debug=1; break;
+ case 'p': do_badpackets=1; break;
+ }
+ }
+ set_debug(do_debug, do_badpackets);
+
+ TalkConnection::init(); /* global initialization */
+ process_config_file(); /* read configuration */
+
+ signal(SIGALRM, timeout);
+ alarm(TIMEOUT);
+ for (;;) {
+ do_one_packet();
+ }
+/* return 0; <--- unreachable because of the above loop */
+}
diff --git a/ktalkd/ktalkd/threads.cpp b/ktalkd/ktalkd/threads.cpp
new file mode 100644
index 00000000..09d5bb4c
--- /dev/null
+++ b/ktalkd/ktalkd/threads.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California, (c) 1998 David Faure.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include "includ.h"
+#include "proto.h"
+
+static int nb_child_to_wait=0;
+
+#undef DEBUG_THREADS
+
+/** Register a new process (by just increasing the number of children */
+void new_process()
+{
+ nb_child_to_wait++;
+#ifdef DEBUG_THREADS
+ debug("New Child. Nb child to wait : %d",nb_child_to_wait);
+#endif
+}
+
+/** Wait for a given process - not registered - and see if others exited */
+int wait_process(int pid)
+{
+ int val, status;
+ do {
+ val = wait(&status);
+ if (val == -1) {
+ if (errno == EINTR)
+ continue;
+ /* shouldn't happen */
+ syslog(LOG_WARNING, "announce: wait: %s", strerror(errno));
+ return 1;
+ }
+ if (val != pid) {
+ nb_child_to_wait--;
+#ifdef DEBUG_THREADS
+ debug("Child exited. Nb child to wait : %d",nb_child_to_wait);
+#endif
+ }
+ } while (val != pid);
+ return status;
+}
+
+/** Wait for children (if any) to exit. Return 1 if no more child to wait */
+int ack_process()
+{
+ if (nb_child_to_wait>0)
+ {
+ int pid;
+ do {
+ pid = waitpid(-1,0,WNOHANG);
+ if (pid==-1)
+ syslog(LOG_ERR,"Timeout. Error waiting for child.");
+ if ((pid!=0) & (nb_child_to_wait>0)) {
+ nb_child_to_wait--;
+#ifdef DEBUG_THREADS
+ debug("Child exited as expected. Nb child to wait : %d",nb_child_to_wait);
+#endif
+ }
+ } while ((pid>0) && (nb_child_to_wait>0));
+ return 0;
+ } else return 1; /* ok for exiting */
+}
+
diff --git a/ktalkd/ktalkd/threads.h b/ktalkd/ktalkd/threads.h
new file mode 100644
index 00000000..5d76e54e
--- /dev/null
+++ b/ktalkd/ktalkd/threads.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 1998 David Faure.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+/** Register a new process (by just increasing the number of children */
+void new_process();
+/** Wait for a given process - not registered - and see if others exited */
+int wait_process(int pid);
+/** Wait for children (if any) to exit. Return 1 if no more child to wait */
+int ack_process();
diff --git a/ktalkd/ktalkd/unixsock.cpp b/ktalkd/ktalkd/unixsock.cpp
new file mode 100644
index 00000000..34272405
--- /dev/null
+++ b/ktalkd/ktalkd/unixsock.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 1998 Burkhard Lehner and David Faure
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+#include <config.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <time.h>
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h> // Needed on some systems.
+#endif
+
+#include <qstrlist.h>
+#include <qfile.h>
+
+#include "includ.h"
+#include "proto.h"
+#include "announce.h" // for N_CHARS
+
+bool sendToKtalk (const char *username, const char *announce)
+/*
+ sends an announcement to all running ktalk clients.
+ username: name of the user who shall receive the announce
+ announce: name and IP address of the one who requests the talk
+
+ return value: TRUE if at least one ktalk was found and running
+ FALSE otherwise
+*/
+
+{
+ // WABA: Disabled for reasons outlined below by XXX.
+ return FALSE;
+#if 0
+ // Create a socket
+ int sock;
+ if ((sock = socket (AF_UNIX, SOCK_DGRAM, 0)) < 0) return FALSE;
+ // Bind it to a temporary file in /tmp
+ struct sockaddr_un tempAddr;
+ tempAddr.sun_family = AF_UNIX;
+ if (tmpnam (tempAddr.sun_path) == 0 ||
+ bind (sock, (struct sockaddr *) &tempAddr, sizeof (tempAddr)) == -1) {
+ close (sock);
+ debug("Couldn't create temporary socket!");
+ return FALSE;
+ }
+
+ // find sockets of running ktalk clients
+ QString tempDir = "/tmp";
+ QString templ = QString ("ktalk-") + username + "-";
+ bool announceok = FALSE;
+ char buffer [N_CHARS+2];
+ buffer [0] = 1;
+ strcpy (buffer + 1, announce); // announce is at most N_CHARS long.
+ unsigned int announcelen = strlen(buffer);
+ unsigned int len;
+
+ DIR *dir = opendir (QFile::encodeName(tempDir));
+ struct dirent *entry;
+ QStrList dirList;
+ struct sockaddr_un ktalkAddr;
+ ktalkAddr.sun_family = AF_UNIX;
+ while ((entry = readdir (dir))) {
+ // send announce to each of them
+
+ // XXX: What happens if user okir does this:
+ // cd /tmp
+ // ln kfm_foo_bar_baz ktalk-joedoe-blablabla
+ // when I now try to talk to joedoe, the name@host string
+ // is sent to the kfm socket (which is not owned
+ // by me but someone else!)
+ //
+ // Besides, this approach allows me to snoop on the
+ // talk requests other users receive; if I want to find
+ // out who's talking to janet, all I need is to write
+ // a small app that listens on /tmp/ktalk-janet-foo
+ //
+ if (templ == QFile::decodeName(entry->d_name).left(templ.length())) {
+ QString path = tempDir + "/" + QFile::decodeName(entry->d_name);
+ strncpy (ktalkAddr.sun_path, QFile::encodeName(path), sizeof (ktalkAddr.sun_path));
+ len = sendto (sock, buffer, announcelen, 0,
+ (struct sockaddr *) &ktalkAddr, sizeof (ktalkAddr));
+ if (len == announcelen)
+ announceok = TRUE;
+ }
+ }
+ closedir (dir);
+ if (!announceok) {
+ close (sock);
+ unlink (tempAddr.sun_path);
+ return FALSE;
+ }
+ // at least one accepted the packet, wait for response :
+ bool result = FALSE;
+ fd_set readFDs;
+ FD_ZERO (&readFDs);
+ FD_SET (sock, &readFDs);
+ char answer;
+ struct timeval timeout;
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 500000; // Wait for answer at most 0.5 seconds
+ if ( (select (sock + 1, &readFDs, 0, 0, &timeout) > 0) &&
+ (recv (sock, &answer, 1, 0) == 1) ) {
+ result = ( answer == 42 ); // Answer from ktalk has to be 42.
+ }
+ close (sock);
+ unlink (tempAddr.sun_path);
+ debug("Announce to ktalk : result = %d",result);
+ return result;
+#endif
+}
diff --git a/ktalkd/ktalkd/unixsock.h b/ktalkd/ktalkd/unixsock.h
new file mode 100644
index 00000000..719c667a
--- /dev/null
+++ b/ktalkd/ktalkd/unixsock.h
@@ -0,0 +1,6 @@
+#ifndef UNIXSOCK_H
+#define UNIXSOCK_H
+
+bool sendToKtalk (const char *username, const char *announce);
+
+#endif
diff --git a/ktalkd/ktalkdlg/Makefile.am b/ktalkd/ktalkdlg/Makefile.am
new file mode 100644
index 00000000..d6d9104c
--- /dev/null
+++ b/ktalkd/ktalkdlg/Makefile.am
@@ -0,0 +1,15 @@
+## -*- makefile -*-
+# Ktalkdlg - Makefile.am
+
+EXTRA_DIST =
+
+bin_PROGRAMS = ktalkdlg
+ktalkdlg_SOURCES = ktalkdlg.cpp
+
+INCLUDES = $(all_includes)
+ktalkdlg_LDFLAGS= $(all_libraries) $(KDE_RPATH)
+ktalkdlg_LDADD = $(LIB_KDEUI)
+
+#for extra warnings during compilation :
+#AM_CXXFLAGS = -ansi -pedantic -D_POSIX_SOURCE -D_BSD_SOURCE
+
diff --git a/ktalkd/ktalkdlg/ktalkdlg.cpp b/ktalkd/ktalkdlg/ktalkdlg.cpp
new file mode 100644
index 00000000..f347330e
--- /dev/null
+++ b/ktalkd/ktalkdlg/ktalkdlg.cpp
@@ -0,0 +1,168 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include <config.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <qmessagebox.h>
+#include <qfile.h>
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kcmdlineargs.h>
+#include <kstandarddirs.h>
+#include <kconfig.h>
+
+#include <kaudioplayer.h>
+#include <klocale.h>
+
+#define RING_WAIT 30
+#define MAX_DLG_LIFE RING_WAIT
+/* so that the dialog lasts exactly the time of an announce */
+
+class TimeoutDialog : public QMessageBox {
+ public:
+ TimeoutDialog (int timeout_ms,
+ const QString& caption, const QString &text, Icon icon,
+ int button0, int button1, int button2,
+ QWidget *parent=0, const char *name=0, bool modal=TRUE,
+ WFlags f=WStyle_DialogBorder ):
+ QMessageBox (caption, text, icon, button0, button1, button2,
+ parent, name, modal, f)
+ {startTimer (timeout_ms);}
+
+ ~TimeoutDialog ()
+ {killTimers ();}
+
+ virtual void timerEvent (QTimerEvent *)
+ {killTimers (); done (Rejected);}
+};
+
+static KCmdLineOptions option[] =
+{
+ { "+user@host", I18N_NOOP("Caller identification"), 0 },
+ { "+[callee]", I18N_NOOP("Name of the callee, if he doesn't exist on this system (we're taking his call)"), 0 },
+ KCmdLineLastOption
+};
+
+static const char description[] =
+ I18N_NOOP("Dialog box for incoming talk requests");
+
+static const char version[] = "v1.5.2";
+
+int main (int argc, char **argv)
+{
+ KCmdLineArgs::init(argc, argv, "ktalkdlg", description, version );
+ KCmdLineArgs::addCmdLineOptions( option );
+ KLocale::setMainCatalogue( "kcmktalkd" );
+ KApplication a;
+
+ struct timeval clock;
+ struct timezone zone;
+ gettimeofday (&clock, &zone);
+ struct tm *localclock = localtime ((const time_t *) &clock.tv_sec);
+
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+
+ if (args->count() == 0)
+ KCmdLineArgs::usage(i18n("'user@host' expected."));
+
+ QString s;
+ s.sprintf ("%d:%02d", localclock->tm_hour, localclock->tm_min);
+ s = i18n ("Message from talk demon at ") + s + " ...\n" +
+ i18n ("Talk connection requested by ") + args->arg(0);
+
+ if ( args->count() == 2 )
+ {
+ s += '\n';
+ QString callee = args->arg(1);
+ s += i18n ("for user %1").arg( callee.isEmpty() ? i18n("<nobody>") : callee );
+ }
+
+ s += ".";
+
+ TimeoutDialog dialog (MAX_DLG_LIFE * 1000,
+ i18n ("Talk requested..."), s,
+ QMessageBox::Information,
+ QMessageBox::Yes | QMessageBox::Default,
+ QMessageBox::No | QMessageBox::Escape,
+ 0 );
+ dialog.setButtonText( QMessageBox::Yes, i18n ("Respond") );
+ dialog.setButtonText( QMessageBox::No, i18n ("Ignore") );
+
+ a.setTopWidget (&dialog);
+
+ // don't erase this! - ktalkd waits for it!
+ printf("#\n");
+ fflush(stdout);
+
+ KConfig *cfg = new KConfig ( "ktalkannouncerc" );
+ cfg->setGroup ("ktalkannounce");
+ bool bSound = cfg->readNumEntry ("Sound", 0);
+
+ if (bSound) {
+ QString soundFile = cfg->readPathEntry ("SoundFile");
+ if (soundFile[0] != '/')
+ soundFile = locate( "sound", soundFile );
+
+ if (!soundFile.isEmpty ()) {
+ KAudioPlayer::play (soundFile);
+ }
+ }
+ //if (!audio) a.beep (); // If no audio is played (whatever reason), beep!
+
+ int result = dialog.exec ();
+ if (result == QMessageBox::Yes) {
+ dialog.killTimers ();
+ kdDebug() << "Running talk client..." << endl;
+
+ QString konsole = locate("exe", "konsole");
+ QString konsole_dir = konsole;
+ konsole_dir.truncate( konsole.findRev('/') );
+ setenv("KDEBINDIR", QFile::encodeName(konsole_dir).data(), 0/*don't overwrite*/);
+ QString cmd0 = cfg->readPathEntry ("talkprg", konsole + " -e talk");
+
+ QString cmd = cmd0.stripWhiteSpace();
+ cmd += " '";
+ cmd += args->arg(0);
+ cmd += "' &";
+
+ kdDebug() << cmd << endl;
+
+ // Open /dev/null for stdin, stdout and stderr:
+ int fd=open("/dev/null", O_RDWR);
+ for (int i = 0; i <= 2; i++) {
+ dup2(fd, i);
+ }
+
+ /* XXX: The sender's name or hostname may contain `rm -rf .`
+ * That's why it's bad to use system()
+ */
+ system (QFile::encodeName(cmd));
+ kapp->quit();
+ }
+
+ return 0;
+}
diff --git a/ktalkd/mail.local/Makefile.am b/ktalkd/mail.local/Makefile.am
new file mode 100644
index 00000000..15a44399
--- /dev/null
+++ b/ktalkd/mail.local/Makefile.am
@@ -0,0 +1,8 @@
+## -*- makefile -*-
+# Ktalkd - mail.local/Makefile.am
+
+LDADD = $(LIBSOCKET)
+bin_PROGRAMS = mail.local
+mail_local_SOURCES = mail.local.c
+noinst_HEADERS = pathnames.h
+
diff --git a/ktalkd/mail.local/README.mail.local b/ktalkd/mail.local/README.mail.local
new file mode 100644
index 00000000..57e932bc
--- /dev/null
+++ b/ktalkd/mail.local/README.mail.local
@@ -0,0 +1,12 @@
+A note about mail.local :
+
+This program is used by the answering machine to let the callee a mail.
+
+It is supplied in case you don't have sendmail nor qmail.
+It is used by the default system-wide configuration file, so that ktalkd works
+for anybody.
+
+If this program doesn't compile, or if you don't want to install it,
+or also if you want to receive the mail on another machine,
+just edit the config file (ktalkdrc if KDE installed, otherwise talkd.conf)
+so that MailProg is set to either sendmail or qmail (in sendmail mode).
diff --git a/ktalkd/mail.local/mail.local.c b/ktalkd/mail.local/mail.local.c
new file mode 100644
index 00000000..9c90d272
--- /dev/null
+++ b/ktalkd/mail.local/mail.local.c
@@ -0,0 +1,983 @@
+/*
+ *
+ SENDMAIL LICENSE
+
+The following license terms and conditions apply, unless a different
+license is obtained from Sendmail, Inc., 6425 Christie Ave, Fourth Floor,
+Emeryville, CA 94608, or by electronic mail at license@sendmail.com.
+
+License Terms:
+
+Use, Modification and Redistribution (including distribution of any
+modified or derived work) in source and binary forms is permitted only if
+each of the following conditions is met:
+
+1. Redistributions qualify as "freeware" or "Open Source Software" under
+ one of the following terms:
+
+ (a) Redistributions are made at no charge beyond the reasonable cost of
+ materials and delivery.
+
+ (b) Redistributions are accompanied by a copy of the Source Code or by an
+ irrevocable offer to provide a copy of the Source Code for up to three
+ years at the cost of materials and delivery. Such redistributions
+ must allow further use, modification, and redistribution of the Source
+ Code under substantially the same terms as this license. For the
+ purposes of redistribution "Source Code" means the complete compilable
+ and linkable source code of sendmail including all modifications.
+
+2. Redistributions of source code must retain the copyright notices as they
+ appear in each source code file, these license terms, and the
+ disclaimer/limitation of liability set forth as paragraph 6 below.
+
+3. Redistributions in binary form must reproduce the Copyright Notice,
+ these license terms, and the disclaimer/limitation of liability set
+ forth as paragraph 6 below, in the documentation and/or other materials
+ provided with the distribution. For the purposes of binary distribution
+ the "Copyright Notice" refers to the following language:
+ "Copyright (c) 1998-2002 Sendmail, Inc. All rights reserved."
+
+4. Neither the name of Sendmail, Inc. nor the University of California nor
+ the names of their contributors may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission. The name "sendmail" is a trademark of Sendmail, Inc.
+
+5. All redistributions must comply with the conditions imposed by the
+ University of California on certain embedded code, whose copyright
+ notice and conditions for redistribution are as follows:
+
+ (a) Copyright (c) 1988, 1993 The Regents of the University of
+ California. All rights reserved.
+
+ (b) Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ (i) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (ii) Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ (iii) Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+6. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY
+ SENDMAIL, INC. AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ NO EVENT SHALL SENDMAIL, INC., THE REGENTS OF THE UNIVERSITY OF
+ CALIFORNIA OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+ */
+
+#if defined(LIBM_SCCS) && !defined(lint)
+static char copyright[] =
+"@(#) Copyright (c) 1990, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif
+
+/*
+ * This is not intended to compile on System V derived systems
+ * such as Solaris or HP-UX, since they use a totally different
+ * approach to mailboxes (essentially, they have a setgid program
+ * rather than setuid, and they rely on the ability to "give away"
+ * files to do their work). IT IS NOT A BUG that this doesn't
+ * compile on such architectures.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h> /* autoconf */
+#endif
+
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#include <netinet/in.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+
+#ifdef EX_OK
+# undef EX_OK /* unistd.h may have another use for this */
+#endif
+
+#ifdef _UNIXWARE
+ #define EX_OK 0 /* successful termination */
+ #define EX__BASE 64 /* base value for error messages */
+ #define EX_USAGE 64 /* command line usage error */
+ #define EX_DATAERR 65 /* data format error */
+ #define EX_NOINPUT 66 /* cannot open input */
+ #define EX_NOUSER 67 /* addressee unknown */
+ #define EX_NOHOST 68 /* host name unknown */
+ #define EX_UNAVAILABLE 69 /* service unavailable */
+ #define EX_SOFTWARE 70 /* internal software error */
+ #define EX_OSERR 71 /* system error (e.g., can't fork) */
+ #define EX_OSFILE 72 /* critical OS file missing */
+ #define EX_CANTCREAT 73 /* can't create (user) output file */
+ #define EX_IOERR 74 /* input/output error */
+ #define EX_TEMPFAIL 75 /* temp failure; user is invited to retry */
+ #define EX_PROTOCOL 76 /* remote error in protocol */
+ #define EX_NOPERM 77 /* permission denied */
+ #define EX_CONFIG 78 /* configuration error */
+ #define EX__MAX 78 /* maximum listed value */
+#else
+ #include <sysexits.h>
+#endif
+
+#include <ctype.h>
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#ifdef HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+
+#ifndef HAVE_VSNPRINTF
+#include "../kotalkd/vsnprintf.c"
+#endif
+
+#if (defined(sun) && defined(__svr4__)) || defined(__SVR4) || defined(_UNIXWARE)
+ #define USE_LOCKF 1
+ #define USE_SETEUID 1
+ #define _PATH_MAILDIR "/var/mail"
+#endif
+
+#if defined(_SCO_DS)
+ #define USE_LOCKF 1
+#endif
+
+#if defined(_AIX)
+ #define USE_LOCKF 1
+ #define USE_VSYSLOG 0
+#endif
+
+#if defined(ultrix)
+ #define USE_VSYSLOG 0
+#endif
+
+#if defined(__osf__)
+ #define USE_VSYSLOG 0
+#endif
+
+#if defined(NeXT)
+ #include <libc.h>
+ #define _PATH_MAILDIR "/usr/spool/mail"
+ #define __dead /* empty */
+ #define S_IRUSR S_IREAD
+ #define S_IWUSR S_IWRITE
+#endif
+
+/*
+ * If you don't have flock, you could try using lockf instead.
+ */
+
+#if defined(USE_LOCKF) || !defined(HAVE_FLOCK)
+ #define flock(a, b) lockf(a, b, 0)
+ #undef LOCK_EX
+ #define LOCK_EX F_LOCK
+#endif
+
+#ifndef USE_VSYSLOG
+ #define USE_VSYSLOG 1
+#endif
+
+#ifdef BSD4_4
+# include "pathnames.h"
+# define USE_SETEUID
+#endif
+
+#ifndef __P
+# ifdef __STDC__
+# define __P(protos) protos
+# else
+# define __P(protos) ()
+# define const
+# endif
+#endif
+#ifndef __dead
+# if defined(__GNUC__) && (__GNUC__ < 2 || __GNUC_MINOR__ < 5) && !defined(__STRICT_ANSI__)
+# define __dead __volatile
+# else
+# define __dead
+# endif
+#endif
+
+#ifndef _BSD_VA_LIST_
+# define _BSD_VA_LIST_ va_list
+#endif
+
+#if !defined(BSD4_4) && !defined(linux) && !defined(_UNIXWARE)
+extern char *strerror __P((int));
+extern FILE *fdopen __P((int, const char *));
+#endif
+
+/*
+ * If you don't have setreuid, and you have saved uids, and you have
+ * a seteuid() call that doesn't try to emulate using setuid(), then
+ * you can try defining USE_SETEUID.
+ */
+#ifdef USE_SETEUID
+# define setreuid(r, e) seteuid(e)
+#endif
+
+#ifndef _PATH_LOCTMP
+# define _PATH_LOCTMP "/tmp/local.XXXXXX"
+#endif
+#ifndef _PATH_MAILDIR
+# define _PATH_MAILDIR "/var/spool/mail"
+#endif
+
+#ifndef S_ISREG
+# define S_ISREG(mode) (((mode) & _S_IFMT) == S_IFREG)
+#endif
+#define KDEMAXPATHLEN 136
+
+int eval = EX_OK; /* sysexits.h error value. */
+
+void deliver __P((int, char *));
+void e_to_sys __P((int));
+__dead void err __P((const char *, ...));
+void notifybiff __P((char *));
+int store __P((char *));
+void usage __P((void));
+void vwarn __P((const char *, _BSD_VA_LIST_));
+void warn __P((const char *, ...));
+void lockmbox __P((char *));
+void unlockmbox __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct passwd *pw;
+ int ch, fd;
+ uid_t uid;
+ char *from;
+ extern char *optarg;
+ extern int optind;
+
+ /* make sure we have some open file descriptors */
+ for (fd = 10; fd < 30; fd++)
+ (void) close(fd);
+
+ /* use a reasonable umask */
+ (void) umask(0077);
+
+#ifdef LOG_MAIL
+ openlog("mail.local", 0, LOG_MAIL);
+#else
+ openlog("mail.local", 0);
+#endif
+
+ from = 0;
+ while ((ch = getopt(argc, argv, "df:r:")) != EOF)
+ switch(ch) {
+ case 'd': /* Backward compatible. */
+ break;
+ case 'f':
+ case 'r': /* Backward compatible. */
+ if (from != 0) {
+ warn("multiple -f options");
+ usage();
+ }
+ from = optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!*argv)
+ usage();
+
+ /*
+ * If from not specified, use the name from getlogin() if the
+ * uid matches, otherwise, use the name from the password file
+ * corresponding to the uid.
+ */
+ uid = getuid();
+ if (!from && (!(from = getlogin()) ||
+ !(pw = getpwnam(from)) || pw->pw_uid != uid))
+ from = (pw = getpwuid(uid)) ? pw->pw_name : "???";
+
+ /*
+ * There is no way to distinguish the error status of one delivery
+ * from the rest of the deliveries. So, if we failed hard on one
+ * or more deliveries, but had no failures on any of the others, we
+ * return a hard failure. If we failed temporarily on one or more
+ * deliveries, we return a temporary failure regardless of the other
+ * failures. This results in the delivery being reattempted later
+ * at the expense of repeated failures and multiple deliveries.
+ */
+ for (fd = store(from); *argv; ++argv)
+ deliver(fd, *argv);
+ exit(eval);
+}
+
+int
+store(from)
+ char *from;
+{
+ FILE *fp = 0;
+ time_t tval;
+ int fd, eline;
+ char line[2048];
+ char tmpbuf[sizeof _PATH_LOCTMP + 1];
+
+ strcpy(tmpbuf, _PATH_LOCTMP);
+ if ((fd = mkstemp(tmpbuf)) == -1 || (fp = fdopen(fd, "w+")) == 0) {
+ e_to_sys(errno);
+ err("unable to open temporary file");
+ }
+ (void)unlink(tmpbuf);
+
+ (void)time(&tval);
+ (void)fprintf(fp, "From %s %s", from, ctime(&tval));
+
+ line[0] = '\0';
+ for (eline = 1; fgets(line, sizeof(line), stdin);) {
+ if (line[0] == '\n')
+ eline = 1;
+ else {
+ if (eline && line[0] == 'F' &&
+ !memcmp(line, "From ", 5))
+ (void)putc('>', fp);
+ eline = 0;
+ }
+ (void)fprintf(fp, "%s", line);
+ if (ferror(fp)) {
+ e_to_sys(errno);
+ err("temporary file write error");
+ }
+ }
+
+ /* If message not newline terminated, need an extra. */
+ if (!strchr(line, '\n'))
+ (void)putc('\n', fp);
+ /* Output a newline; note, empty messages are allowed. */
+ (void)putc('\n', fp);
+
+ if (fflush(fp) == EOF || ferror(fp)) {
+ e_to_sys(errno);
+ err("temporary file write error");
+ }
+ return (fd);
+}
+
+void
+deliver(fd, name)
+ int fd;
+ char *name;
+{
+ struct stat fsb, sb;
+ struct passwd *pw;
+ int mbfd, nr, nw, off;
+ char *p;
+ char biffmsg[100], buf[8*1024], path[KDEMAXPATHLEN];
+ off_t curoff;
+
+ /*
+ * Disallow delivery to unknown names -- special mailboxes can be
+ * handled in the sendmail aliases file.
+ */
+ if (!(pw = getpwnam(name))) {
+ if (eval != EX_TEMPFAIL)
+ eval = EX_UNAVAILABLE;
+ warn("unknown name: %s", name);
+ return;
+ }
+ endpwent();
+
+ /*
+ * Keep name reasonably short to avoid buffer overruns.
+ * This isn't necessary on BSD because of the proper
+ * definition of snprintf(), but it can cause problems
+ * on other systems.
+ * Also, clear out any bogus characters.
+ */
+
+ if (strlen(name) > 40)
+ name[40] = '\0';
+ for (p = name; *p != '\0'; p++)
+ {
+ if (!isascii(*p))
+ *p &= 0x7f;
+ else if (!isprint(*p))
+ *p = '.';
+ }
+
+ (void)snprintf(path, sizeof(path), "%s/%s", _PATH_MAILDIR, name);
+
+ /*
+ * If the mailbox is linked or a symlink, fail. There's an obvious
+ * race here, that the file was replaced with a symbolic link after
+ * the lstat returned, but before the open. We attempt to detect
+ * this by comparing the original stat information and information
+ * returned by an fstat of the file descriptor returned by the open.
+ *
+ * NB: this is a symptom of a larger problem, that the mail spooling
+ * directory is writeable by the wrong users. If that directory is
+ * writeable, system security is compromised for other reasons, and
+ * it cannot be fixed here.
+ *
+ * If we created the mailbox, set the owner/group. If that fails,
+ * just return. Another process may have already opened it, so we
+ * can't unlink it. Historically, binmail set the owner/group at
+ * each mail delivery. We no longer do this, assuming that if the
+ * ownership or permissions were changed there was a reason.
+ *
+ * XXX
+ * open(2) should support flock'ing the file.
+ */
+tryagain:
+ lockmbox(path);
+ if (lstat(path, &sb)) {
+ mbfd = open(path,
+ O_APPEND|O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
+ if (mbfd == -1) {
+ if (errno == EEXIST)
+ goto tryagain;
+ } else if (fchown(mbfd, pw->pw_uid, pw->pw_gid)) {
+ e_to_sys(errno);
+ warn("chown %u.%u: %s", pw->pw_uid, pw->pw_gid, name);
+ goto err1;
+ }
+ } else if (sb.st_nlink != 1 || !S_ISREG(sb.st_mode)) {
+ e_to_sys(errno);
+ warn("%s: irregular file", path);
+ goto err0;
+ } else if (sb.st_uid != pw->pw_uid) {
+ eval = EX_CANTCREAT;
+ warn("%s: wrong ownership (%d)", path, sb.st_uid);
+ goto err0;
+ } else {
+ mbfd = open(path, O_APPEND|O_WRONLY, 0);
+ if (mbfd != -1 &&
+ (fstat(mbfd, &fsb) || fsb.st_nlink != 1 ||
+ !S_ISREG(fsb.st_mode) || sb.st_dev != fsb.st_dev ||
+ sb.st_ino != fsb.st_ino || sb.st_uid != fsb.st_uid)) {
+ eval = EX_CANTCREAT;
+ warn("%s: file changed after open", path);
+ goto err1;
+ }
+ }
+
+ if (mbfd == -1) {
+ e_to_sys(errno);
+ warn("%s: %s", path, strerror(errno));
+ goto err0;
+ }
+
+ /* Wait until we can get a lock on the file. */
+ if (flock(mbfd, LOCK_EX)) {
+ e_to_sys(errno);
+ warn("%s: %s", path, strerror(errno));
+ goto err1;
+ }
+
+ /* Get the starting offset of the new message for biff. */
+ curoff = lseek(mbfd, (off_t)0, SEEK_END);
+ (void)snprintf(biffmsg, sizeof(biffmsg),
+ sizeof curoff > sizeof(long) ? "%s@%qd\n" : "%s@%ld\n",
+ name, curoff);
+
+ /* Copy the message into the file. */
+ if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
+ e_to_sys(errno);
+ warn("temporary file: %s", strerror(errno));
+ goto err1;
+ }
+ if (setreuid(0, pw->pw_uid) < 0) {
+ e_to_sys(errno);
+ warn("setreuid(0, %d): %s (r=%d, e=%d)",
+ pw->pw_uid, strerror(errno), getuid(), geteuid());
+ goto err1;
+ }
+#ifdef DEBUG
+ printf("new euid = %d\n", geteuid());
+#endif
+ while ((nr = read(fd, buf, sizeof(buf))) > 0)
+ for (off = 0; off < nr; off += nw)
+ if ((nw = write(mbfd, buf + off, nr - off)) < 0) {
+ e_to_sys(errno);
+ warn("%s: %s", path, strerror(errno));
+ goto err3;
+ }
+ if (nr < 0) {
+ e_to_sys(errno);
+ warn("temporary file: %s", strerror(errno));
+ goto err3;
+ }
+
+ /* Flush to disk, don't wait for update. */
+ if (fsync(mbfd)) {
+ e_to_sys(errno);
+ warn("%s: %s", path, strerror(errno));
+err3:
+ if (setreuid(0, 0) < 0) {
+ e_to_sys(errno);
+ warn("setreuid(0, 0): %s", strerror(errno));
+ }
+#ifdef DEBUG
+ printf("reset euid = %d\n", geteuid());
+#endif
+ (void)ftruncate(mbfd, curoff);
+err1: (void)close(mbfd);
+err0: unlockmbox();
+ return;
+ }
+
+ /* Close and check -- NFS doesn't write until the close. */
+ if (close(mbfd)) {
+ e_to_sys(errno);
+ warn("%s: %s", path, strerror(errno));
+ unlockmbox();
+ return;
+ }
+
+ if (setreuid(0, 0) < 0) {
+ e_to_sys(errno);
+ warn("setreuid(0, 0): %s", strerror(errno));
+ }
+#ifdef DEBUG
+ printf("reset euid = %d\n", geteuid());
+#endif
+ unlockmbox();
+ notifybiff(biffmsg);
+}
+
+/*
+ * user.lock files are necessary for compatibility with other
+ * systems, e.g., when the mail spool file is NFS exported.
+ * Alas, mailbox locking is more than just a local matter.
+ * EPA 11/94.
+ */
+
+char lockname[KDEMAXPATHLEN];
+int locked = 0;
+
+void
+lockmbox(path)
+ char *path;
+{
+ int statfailed = 0;
+
+ if (locked)
+ return;
+ snprintf(lockname, sizeof(lockname), "%s.lock", path);
+ for (;; sleep(5)) {
+ int fd;
+ struct stat st;
+ time_t now;
+
+ fd = open(lockname, O_WRONLY|O_EXCL|O_CREAT, 0);
+ if (fd >= 0) {
+ locked = 1;
+ close(fd);
+ return;
+ }
+ if (stat(lockname, &st) < 0) {
+ if (statfailed++ > 5)
+ return;
+ continue;
+ }
+ statfailed = 0;
+ time(&now);
+ if (now < st.st_ctime + 300)
+ continue;
+ unlink(lockname);
+ }
+}
+
+void
+unlockmbox()
+{
+ if (!locked)
+ return;
+ unlink(lockname);
+ locked = 0;
+}
+
+void
+notifybiff(msg)
+ char *msg;
+{
+ static struct sockaddr_in addr;
+ static int f = -1;
+ struct hostent *hp;
+ struct servent *sp;
+ int len;
+
+ if (!addr.sin_family) {
+ /* Be silent if biff service not available. */
+ if (!(sp = getservbyname("biff", "udp")))
+ return;
+ if (!(hp = gethostbyname("localhost"))) {
+ warn("localhost: %s", strerror(errno));
+ return;
+ }
+ addr.sin_family = hp->h_addrtype;
+ memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
+ addr.sin_port = sp->s_port;
+ }
+ if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+ warn("socket: %s", strerror(errno));
+ return;
+ }
+ len = strlen(msg) + 1;
+ if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof(addr))
+ != len)
+ warn("sendto biff: %s", strerror(errno));
+}
+
+void
+usage()
+{
+ eval = EX_USAGE;
+ err("usage: mail.local [-f from] user ...");
+}
+
+#if __STDC__
+__dead void
+err(const char *fmt, ...)
+#else
+__dead void
+err(fmt, va_alist)
+ const char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ vwarn(fmt, ap);
+ va_end(ap);
+
+ exit(eval);
+}
+
+void
+#if __STDC__
+warn(const char *fmt, ...)
+#else
+warn(fmt, va_alist)
+ const char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ vwarn(fmt, ap);
+ va_end(ap);
+}
+
+void
+vwarn(fmt, ap)
+ const char *fmt;
+ _BSD_VA_LIST_ ap;
+{
+ /*
+ * Log the message to stderr.
+ *
+ * Don't use LOG_PERROR as an openlog() flag to do this,
+ * it's not portable enough.
+ */
+ if (eval != EX_USAGE)
+ (void)fprintf(stderr, "mail.local: ");
+ (void)vfprintf(stderr, fmt, ap);
+ (void)fprintf(stderr, "\n");
+
+#if USE_VSYSLOG
+ /* Log the message to syslog. */
+ vsyslog(LOG_ERR, fmt, ap);
+#else
+ {
+ char fmtbuf[10240];
+
+ (void) vsprintf(fmtbuf, fmt, ap);
+ syslog(LOG_ERR, "%s", fmtbuf);
+ }
+#endif
+}
+
+/*
+ * e_to_sys --
+ * Guess which errno's are temporary. Gag me.
+ */
+void
+e_to_sys(num)
+ int num;
+{
+ /* Temporary failures override hard errors. */
+ if (eval == EX_TEMPFAIL)
+ return;
+
+ switch(num) { /* Hopefully temporary errors. */
+#ifdef EAGAIN
+ case EAGAIN: /* Resource temporarily unavailable */
+#endif
+#ifdef EDQUOT
+ case EDQUOT: /* Disc quota exceeded */
+#endif
+#ifdef EBUSY
+ case EBUSY: /* Device busy */
+#endif
+#ifdef EPROCLIM
+ case EPROCLIM: /* Too many processes */
+#endif
+#ifdef EUSERS
+ case EUSERS: /* Too many users */
+#endif
+#ifdef ECONNABORTED
+ case ECONNABORTED: /* Software caused connection abort */
+#endif
+#ifdef ECONNREFUSED
+ case ECONNREFUSED: /* Connection refused */
+#endif
+#ifdef ECONNRESET
+ case ECONNRESET: /* Connection reset by peer */
+#endif
+#ifdef EDEADLK
+ case EDEADLK: /* Resource deadlock avoided */
+#endif
+#ifdef EFBIG
+ case EFBIG: /* File too large */
+#endif
+#ifdef EHOSTDOWN
+ case EHOSTDOWN: /* Host is down */
+#endif
+#ifdef EHOSTUNREACH
+ case EHOSTUNREACH: /* No route to host */
+#endif
+#ifdef EMFILE
+ case EMFILE: /* Too many open files */
+#endif
+#ifdef ENETDOWN
+ case ENETDOWN: /* Network is down */
+#endif
+#ifdef ENETRESET
+ case ENETRESET: /* Network dropped connection on reset */
+#endif
+#ifdef ENETUNREACH
+ case ENETUNREACH: /* Network is unreachable */
+#endif
+#ifdef ENFILE
+ case ENFILE: /* Too many open files in system */
+#endif
+#ifdef ENOBUFS
+ case ENOBUFS: /* No buffer space available */
+#endif
+#ifdef ENOMEM
+ case ENOMEM: /* Cannot allocate memory */
+#endif
+#ifdef ENOSPC
+ case ENOSPC: /* No space left on device */
+#endif
+#ifdef EROFS
+ case EROFS: /* Read-only file system */
+#endif
+#ifdef ESTALE
+ case ESTALE: /* Stale NFS file handle */
+#endif
+#ifdef ETIMEDOUT
+ case ETIMEDOUT: /* Connection timed out */
+#endif
+#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK
+ case EWOULDBLOCK: /* Operation would block. */
+#endif
+ eval = EX_TEMPFAIL;
+ break;
+ default:
+ eval = EX_UNAVAILABLE;
+ break;
+ }
+}
+
+#if !defined(BSD4_4) && !defined(__osf__) && !defined(__GLIBC__)
+
+char *
+strerror(eno)
+ int eno;
+{
+ extern int sys_nerr;
+ extern char *sys_errlist[];
+ static char ebuf[60];
+
+ if (eno >= 0 && eno <= sys_nerr)
+ return sys_errlist[eno];
+ (void) sprintf(ebuf, "Error %d", eno);
+ return ebuf;
+}
+
+# endif
+
+#if defined(ultrix) || defined(_UNIXWARE)
+
+/*
+ * Copyright (c) 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)mktemp.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <ctype.h>
+
+static int _gettemp();
+
+mkstemp(path)
+ char *path;
+{
+ int fd;
+
+ return (_gettemp(path, &fd) ? fd : -1);
+}
+
+/*
+char *
+mktemp(path)
+ char *path;
+{
+ return(_gettemp(path, (int *)0) ? path : (char *)0);
+}
+*/
+
+static
+_gettemp(path, doopen)
+ char *path;
+ register int *doopen;
+{
+ extern int errno;
+ register char *start, *trv;
+ struct stat sbuf;
+ u_int pid;
+
+ pid = getpid();
+ for (trv = path; *trv; ++trv); /* extra X's get set to 0's */
+ while (*--trv == 'X') {
+ *trv = (pid % 10) + '0';
+ pid /= 10;
+ }
+
+ /*
+ * check the target directory; if you have six X's and it
+ * doesn't exist this runs for a *very* long time.
+ */
+ for (start = trv + 1;; --trv) {
+ if (trv <= path)
+ break;
+ if (*trv == '/') {
+ *trv = '\0';
+ if (stat(path, &sbuf))
+ return(0);
+ if (!S_ISDIR(sbuf.st_mode)) {
+ errno = ENOTDIR;
+ return(0);
+ }
+ *trv = '/';
+ break;
+ }
+ }
+
+ for (;;) {
+ if (doopen) {
+ if ((*doopen =
+ open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0)
+ return(1);
+ if (errno != EEXIST)
+ return(0);
+ }
+ else if (stat(path, &sbuf))
+ return(errno == ENOENT ? 1 : 0);
+
+ /* tricky little algorithm for backward compatibility */
+ for (trv = start;;) {
+ if (!*trv)
+ return(0);
+ if (*trv == 'z')
+ *trv++ = 'a';
+ else {
+ if (isdigit(*trv))
+ *trv = 'a';
+ else
+ ++*trv;
+ break;
+ }
+ }
+ }
+ /*NOTREACHED*/
+}
+
+#endif
diff --git a/ktalkd/mail.local/pathnames.h b/ktalkd/mail.local/pathnames.h
new file mode 100644
index 00000000..8e439254
--- /dev/null
+++ b/ktalkd/mail.local/pathnames.h
@@ -0,0 +1,37 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/4/93
+ */
+#include <paths.h>
+
+#define _PATH_LOCTMP "/tmp/local.XXXXXX"