diff options
Diffstat (limited to 'ktalkd')
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 Binary files differnew file mode 100644 index 00000000..8d9c97f7 --- /dev/null +++ b/ktalkd/kcmktalkd/cr128-app-ktalkd.png diff --git a/ktalkd/kcmktalkd/cr16-app-ktalkd.png b/ktalkd/kcmktalkd/cr16-app-ktalkd.png Binary files differnew file mode 100644 index 00000000..fd36501c --- /dev/null +++ b/ktalkd/kcmktalkd/cr16-app-ktalkd.png diff --git a/ktalkd/kcmktalkd/cr22-app-ktalkd.png b/ktalkd/kcmktalkd/cr22-app-ktalkd.png Binary files differnew file mode 100644 index 00000000..9ac54546 --- /dev/null +++ b/ktalkd/kcmktalkd/cr22-app-ktalkd.png diff --git a/ktalkd/kcmktalkd/cr32-app-ktalkd.png b/ktalkd/kcmktalkd/cr32-app-ktalkd.png Binary files differnew file mode 100644 index 00000000..896c9be3 --- /dev/null +++ b/ktalkd/kcmktalkd/cr32-app-ktalkd.png diff --git a/ktalkd/kcmktalkd/cr48-app-ktalkd.png b/ktalkd/kcmktalkd/cr48-app-ktalkd.png Binary files differnew file mode 100644 index 00000000..0ad792c7 --- /dev/null +++ b/ktalkd/kcmktalkd/cr48-app-ktalkd.png 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 Binary files differnew file mode 100644 index 00000000..f8d4292d --- /dev/null +++ b/ktalkd/ktalkd/ktalkd.wav 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" |