]> git.treefish.org Git - fex.git/commitdiff
Original release 20150120 20150120
authorfextracker <fextracker@treefish.org>
Wed, 18 Feb 2015 15:48:45 +0000 (16:48 +0100)
committerfextracker <fextracker@treefish.org>
Wed, 18 Feb 2015 15:48:45 +0000 (16:48 +0100)
2015-01-17: new fex.ph config variable $mail_authid (default yes)
2015-01-16: fixed bug no notfication for still existing file (overwrite)
2015-01-15: fixed bug no locale reminder notfication
2015-01-15: fixed bug wrong result for recipients with NOTIFICATION=no
2015-01-13: fexsend: added option -N resend notification email
2015-01-13: resending notification email deletes download ip restriction
2015-01-13: fup: fixed bug sending to groups broken
2015-01-10: fexsend: added option -S show server/user settings
2015-01-10: fup: added command LISTSETTINGS
2015-01-09: foc: added save-or-display (MIME) option for download
2015-01-04: fexsend: fixed bug dies too early on multiple files and one
2015-01-04: file has been already transfered
2014-12-25: fexget,fexsend,sexsend: use default SSL_cipher_list
2014-12-25: DEFAULT:!3DES:!MD5
2014-12-24: fexget,fexsend,sexsend: evaluate environment variables SSLVERIFY
2014-12-24: SSLVERSION SSLCAPATH SSLCAFILE SSLCIPHERLIST
2014-12-24: fexget,fexsend,sexsend: use TLS, not SSL
2014-12-23: fexsend: $HOME/.fex/config with $opt_* and %alias variables
2014-12-23: fexget: $HOME/.fex/config with $opt_* and %autoview variables
2014-12-19: fur: fixed bug race condition with fex_cleanup (external->internal)
2014-12-17: install/update: fixed bug some spool files are owned by user root
2014-12-16: fexsrv: fixed bug handling of User-Agent FDM
2014-12-09: added l ll lf to distribution
2014-12-09: fexwall: also mail to sub and group users
2014-12-03: fup: remove file after upload if restricted user has set NOMAIL
2014-12-03: fup: fixed bug wrong message "user notified" if NOMAIL
2014-12-02: fup: also check recipient restrictions on command CHECKRECIPIENT
2014-11-24: fexget: autoview gif jpg png tif after download
2014-11-20: count unfinished upload size into quota, too
2014-11-20: fixed bug wrong quota calculation on SysV UNIX like Solaris
2014-11-18: fexsend: added environment variables SSLVERIFY SSLCAPATH SSLCAFILE
2014-11-18: dop: added HTTP basic authentication for htdoc directory with
2014-11-18: .htauth file
2014-11-14: ignore @forbidden_recipients if $SPOOL/$USER exists
2014-11-14: (admin has created user)
2014-11-11: fup: fixed bug groups from other users in address book selection
2014-11-11: fup: added useragent to $SPOOL/$TO/$FROM/$FILE/
2014-11-10: fup: present locales in recipient query form, too
2014-11-07: FAQ: added text anchor URLs
2014-11-03: added missing fexget fexsend sexget sexsend for tools.html
2014-10-23: fexsend: on multiple recipients check only the first for resume
2014-10-14: fac: added option -L (list files detailed)
2014-10-01: fex_cleanup: fixed bug wrong default spool for virtual hosts
2014-09-19: sex: added transfered bytes to sex.log
2014-09-17: fup: fixed bug no locales presentation
2014-09-14: fex_cleanup: send new release notification to $admin
2014-09-11: dop: exclude .* and *~ from stream files
2014-09-01: fup: upload status bar waits longer, until $timeout
2014-08-27: fex_cleanup: use wget for new release dedection
2014-08-18: fexsend: workaround for stunnel bug (options -s and -g)
2014-08-18: fex_cleanup,fexsend: always restrict permissions on fexsend id-file
2014-08-16: fac: added options -P and -E (more examples)
2014-08-15: install: fixed bug wrong owner in spool
2014-08-15: fex_cleanup: do not terminate on error, but print warning
2014-08-13: fexsrv,fexsend,fexget: reenabled IPv6 support
2014-08-10: fac: added option -/ to set new admin
2014-08-08: moved $admin_pw from fex.ph to auth-ID for user $admin
2014-08-08: fex_cleanup: fixed bug in notify_newrelease
2014-08-08: fac(CGI): switched from HTTP basic authorization to auth-ID/akey
2014-08-08: fexsend: always use CHECKRECIPIENT, not only for aliases
2014-08-07: dop: generate on-the-fly gzipped documents if requested
2014-08-06: fixed bug install script dysfunctional (permission, hostname)
2014-07-25: reenabled vhost support
2014-07-13: dop: in directory index, list only files which are readable by
2014-07-13: group or other
2014-07-10: fup: fixed bug wildcards not working in @forbidden_recipients
2014-06-25: fup,fop: sender can download the files he has sent, too,
2014-06-25: if he uses the same ip for upload and download
2014-06-19: fac(CGI): @admin_hosts is now mandatory in fex.ph
2014-06-12: fur: reallow registration of internal and external users from
2014-06-12: the same ip range
2014-06-05: new fex.ph config variable @forbidden_hosts
2014-06-03: fuc: fixed bug cannot edit and save groups
2014-05-26: fuc: ignore akey cookie to prevent cross-site request forgery
2014-05-26: fup,foc,fuc,rup,pup: better parameter filtering to prevent
2014-05-26: cross-site scripting attacks
2014-05-25: fup: fixed bug insecure dependency when forwarding a file
2014-05-25: to a user which has set a default keep value
2014-05-23: fexget: fixed bug download fails on big file and slow disk
2014-05-12: set Reply-To in notification emails for @remote_domains
2014-05-03: fup: fixed bug wrong (old) keep time on forword-copy (bounce)
2014-04-10: fexsend: added "exclude from archive" option -#
2014-03-28: fexsend: do not copy "NOMAIL" comment in forward
2014-03-28: fexget: fixed bug server timeout because of storage check
2014-03-07: new fex.ph config variable $disclaimer to be appended to every email
2014-03-05: added bin/fexwall and doc/newfeatures
2014-03-04: fuc,foc: added PGP/GPG email encryption option
2014-02-26: fex.pp: fixed bug $keep_default ignored
2014-02-13: fur,fex.ph: config variable @local_domains may contain wildcard *
2014-02-13: in domain names
2014-01-03: fexget: checks storage capacity before download
2013-11-26: dop: substitute $variable$ in HTML document only if there is a
2013-11-26: corresponding environment variable
2013-10-15: fac: fixed bug wrong output with option -l
2013-10-14: fexget: use archive name as default extraction directory
2013-10-09: fixed bug afex URL for only one download availably
2013-10-07: fup: fixed bug no download links on multiple NOMAIL recipients
2013-09-23: fexsend: fixed bug option -c sends uncompressed file
2013-09-19: sub and group users have the same quota amount like their main user
2013-09-18: fixed bug permission denied for locale htdocs
2013-09-18: anonymous user now with hostname domain instead of mail domain
2013-09-17: dop: set locale cookie, too
2013-09-15: foc: added "Change the disclaimer" option
2013-09-12: fup: expand domainless address with server hostname if such a user
2013-09-12: exists (needed for fbm/nettest)
2013-09-09: new FAQ design (questions first, then Q+A)
2013-09-04: fac: modify $hostname when vhost fex.ph is created
2013-09-04: fuc: recognize comment=NOMAIL
2013-08-26: fexsend: always show download URL if recipient is "."
2013-08-20: fup,fop: fixed bug no DELETE and RESUME for fexmail and anonymous
2013-08-20: users (because of storage swap)
2013-08-18: sexsend: fixed bug data corruption when using https
2013-08-18: fop,fup: fixed bug bad file locking when using multiple recipients
2013-08-18: fexsend: fixed bug hangs on server error when sending archive
2013-08-17: fixed bug $sender_from ignored
2013-08-17: fup,foc: added notification email resending on user request
2013-08-16: fac: fixed bug wrong output order for option -l
2013-08-14: fop: fixed bug no multiple downloads for fexmail
2013-08-14: fac: added option -M for resending notification emails
2013-08-09: afex: fixed bug ID for input
2013-08-06: fexsrv: always includes "Server: fexsrv" in HTTP reply
2013-08-06: fexsend: terminates if no fexsrv HTTP reply
2013-07-28: dop: added .htaccessfrom support
2013-07-27: fex.ph,dop: security enhancement:
2013-07-27: static documents must be in @doc_dirs
2013-07-27: dop: documents with leading . are not allowed
2013-07-25: fexsend: added option -= to upload a file with another name
2013-07-21: fixed bug environment variables are cut at newline
2013-07-18: fup,fexsend: use header Content-Location instead of Content-Type for
2013-07-18: file linking
2013-07-15: fup: modifying keep references actual time, not upload date
2013-07-15: fexsend: fixed bug no feedback on option -x -k (modify keep)
2013-07-13: fup: fixed bug user specific keep and autodelete defaults are ignored
2013-07-12: fup,fop: added file link support
2013-07-12: fexsend: added option -/ for file linking
2013-07-09: fexget: added option -P proxy:port
2013-06-28: new all-in-one FAQ
2013-06-27: fup: to/from storage swap for fexmail and anonymous users
2013-06-27: fup: anonymous recipient with random number
2013-06-26: fop: allow multiple downloads from same ip
2013-06-26: fup,fac: extended "fex yourself" support
2013-06-26: added sup.html
2013-06-22: fop,fexsend: Location output for fexmail for already transfered files
2013-06-19: fexget: fixed bug cannot download MIME file
2013-06-15: fex.ph: new config variable $notify_newrelease checks
2013-06-15: fex.rus.uni-stuttgart.de for new release and sends mail
2013-06-12: fac(CGI): fixed bug wrong spooldir for virtual server
2013-06-11: fex.ph: new config variable $usage_conditions for registrations mails
2013-06-11: fop: fixed bug fexmail download possible only once
2013-06-10: fac: added option -D to disable a user
2013-06-10: (with hooks in fop,fop.fuc,foc,fur)
2013-06-03: fuc: fixed bug user can modify his auth-ID to an illegal value
2013-05-30: install: force creation of $admin_pw
2013-05-30: fex_cleanup: fixed bug wrong fexadmin fexid for reactivation emails
2013-05-25: added afex and asex to distribution
2013-05-23: dop: fixed bug no output on file.stream
2013-05-22: install script installs as user fex (and not as root)
2013-05-19: security patch: config variable @local_rhosts restricts download of
2013-05-19: files from new external users to specific ip ranges
2013-05-19: use &lt;from> and &lt;to> syntax in notification email header
2013-05-19: added support for $max_fail_handler()
2013-05-18: fac: fixed bug option -rd does not work
2013-05-18: fac: added option -R
2013-05-16: fop: fexmail support (multiple downloads allowed)
2013-05-09: fup: fixed bug anonymous only works if $mdomain is defined
2013-05-09: fop: fixed bug anonymous only works if recipient host is in
2013-05-09: @anonymous_upload list
2013-05-07: fup: fixed bug multiple Location HTTP headers generate an error with
2013-05-07: some web browsers
2013-05-03: added support for axel download accelerator (multiple HTTP Range)
2013-05-02: add X-FEX-File-ID header to notification emails
2013-04-23: dop: fixed bug HTTP 301 redirection timeout on symlinks
2013-04-05: fexsend: fixed bug server timeout when sending huge ZIPs
2013-02-20: fac: added -m maintenance mode
2013-02-17: fup: fixed bug stored comment in spool not in UTF8
2013-02-17: fop: fixed bug file deletion also deletes fop.log
2013-02-16: fur,fex.ph: allow "*" for local domains self registration
2013-02-16: fex.ph: new config variable @registration_hosts
2013-02-16: fex.ph: new config variable @admin_hosts
2013-02-11: fur: fixed bug insecure dependency with exuser
2013-01-31: receiving of reminder emails is user configurable
2013-01-24: fup: decode UTF8 comment
2013-01-07: added X-Frame-Options header to prevent clickjacking
2012-12-26: fup,fexsend: added replyto option
2012-12-21: dop: download manager prevention:
2012-12-21: no concurrent downloads from same client with HTTP Range
2012-12-17: fop: fixed bug ip based download restriction
2012-12-16: added name based virtual host server
2012-12-15: fexsrv: HTTP error logging to error.log
2012-12-07: fex.ph: added optional config variable @durl
2012-12-04: new feature forward a file from incoming spool
2012-11-21: fac: show more user information
2012-11-20: added CAPTIVE user mode
2012-11-19: fup: with comment NOSTORE file will not be stored ==> benchmarking
2012-11-14: fexsend: fixed bug gkey and skey URL recipients not working
2012-11-12: fexsend: 7z archives without compression
2012-11-08: fop: fixed bug cannot use "?" in file name with fexsend
2012-11-07: fixed security bug restricted user can redirect files
2012-11-06: fup: show download-URL after upload if sender = recipient
2012-11-06: fup,fop,fac: added user up/download IP restriction by admin
2012-11-05: added HTTP Strict Transport Security if $force_https is enabled
2012-11-05: fixed bug afex accessible via xkey from everywhere
2012-11-02: fup: fixed bug one time upload URL gives "no recipient specified"
2012-11-02: error
2012-11-01: fup: fixed bug public upload always gives error
2012-10-16: fop,fup: added afex support
2012-10-16: fup: accept recipients . and //
2012-10-15: sex,sexsend: added anonymous mode (no auth-ID necessary)
2012-10-14: fop,fup: added support for fexsend anonymous mode
2012-10-11: fex.ph: added optional config variable $overwrite
2012-10-10: fup: anonymous upload with non-anonymous recipient
2012-09-30: fup: fixed bug groups not working any more (NOMAIL)
2012-09-19: fup: logout functions respects login CGI (or symlink)
2012-09-19: foc: detailed/brief notification mail configuration
2012-09-18: fexsend: added options -. and -n
2012-09-18: fup: added shortmail option in comment
2012-09-17: added mailmode configuration option in fex.ph
2012-09-17: fup: shows download-URL if NOMAIL
2012-09-15: sex: public URL parameter may be in base64 format, too
2012-09-10: dop: added more security checks
2012-09-01: dop: added streaming document output
2012-08-30: sex: fixed bug second receiving client corrupts the stream
2012-08-26: Changed licence from AGPL to Perl Artistic
2012-08-21: schwuppdiwupp: error handling on network failures for Windows
2012-08-21: schwuppdiwupp: removed Tk::FileSelect for Windows
2012-07-11: fop: fixed bug no multiple downloads for anonymous uploads
2012-07-10: fixed French, Spanish and Czech localization (code syntax) bugs
2012-07-09: fup: anonymous upload with modifyable keep option and multiple
2012-07-09: downloads
2012-07-05: fexsrv: added camel easteregg
2012-07-02: fup: added optional anonymous upload with fex.ph variable
2012-07-02: @anonymous_upload
2012-07-02: fup: fixed bug throttle 0 configuration is ignored
2012-07-01: fexsend: optional argument '@' as files/recipients separator
2012-07-01: fexsend: fixed bug notification email for recipient '.'
2012-06-21: dop: inside HTML documents: #include "file"
2012-06-06: fexget: new option -+
2012-06-05: fexsend: new option -+
2012-06-01: fup: show transfered size instead of total size in status window
2012-05-04: fexsrv: added bunny easteregg
2012-05-02: fexsrv: allow HTTP 1.0 with Range header (wget!)
2012-05-02: fexsrv: disallow negative value in Range (client signed int bug)
2012-04-26: Changed licence from GPL to AGPL
2012-04-07: foc: check new address book for syntax errors
2012-04-06: foc: added comment field for new subuser information
2012-04-04: removed F*IX because of too many bugs and no maintainer any more
2012-03-05: fup: fixed bug shell wildcards in recipient address are expanded to
2012-03-05: known users from spool
2012-03-05: fex.ph: added optional config variable @locales
2012-03-01: dop: delivers MIME type text/plain if "?!" is appended to URL
2012-02-20: foc: show auth-ID after click on link
2012-02-07: fop: MIME-type text/html is no longer possible for security reasons
2012-02-04: added optional french localization
2012-02-03: fixed bug 0.0.0.0 not recognized as ip address
2012-02-02: HTTP parameter filtering to prevent cross-site scripting attacks
2012-02-01: config variable @throttle may also contains ip addresses
2012-01-25: pup: locale selection in native language, default autodelete=no
2012-01-25: fup: "send another file" with same keep and autodelete parameters
2012-01-17: fixed bug reactivation.txt in czech instead english
2012-01-06: fup: fixed bug show wrong remaining keep days
2012-01-02: fup.pl: fixed bug bad FAQ link
2011-12-31: fex_cleanup: fixed bug notification emails not localized
2011-12-30: fup: additional dkey.log
2011-12-05: fup: respect @throttle config for all clients
2011-11-29: dop: delivers text files (scripts!) without x-bit as "text/plain"
2011-11-15: fup: fixed bug with $autodelete = $NUMBER;
2011-11-08: fac: added option -S statistics
2011-11-02: fup: show remaining keep time in files listing
2011-10-28: fup: fixed bug uninitialized value when using copy-forward
2011-10-13: fup: FILESIZE hack for firefox 7 to support uploads > 4 GB
2011-10-05: fup: fixed bug wrong sender quota calculation
2011-10-03: fex.ph: added configuration variables @upload_hosts @download_hosts
2011-09-30: fexsend: added option -g for sending encrypted files
2011-09-21: fexsrv: use $default_locale if client sends illegal locale cookie
2011-09-20: fex.ph: added optional config variable $boring for unhumorous mode
2011-09-19: fur: $USER/.auto contains additional info about account creation
2011-09-07: fac(CGI): fixed bug infinitve loop in watch logfile
2011-09-06: fup,fac,fur: new additional login URL type:
2011-09-06: http://FEXSERVER/fup/B64ID
2011-09-06: fup: show "or select from address book" only if there are entries
2011-09-05: fexsrv: fixed bug locale cookie not fetched on http://cgi?parameter
2011-09-01: rup: fixed bug cannot find files (no more SID in akeys directory)
2011-08-30: fex.ph: new config variable @forbidden_recipients
2011-08-29: fexsend: accept file number for delete option -d, too
2011-08-29: dop: fixed bug no text document output if external file command is
2011-08-29: non-GNU
2011-08-26: added one time upload OKEY
2011-08-13: fex_cleanup: fixed bug comment missing in reminder email
2011-08-11: dop: #if ... #else ... #elseif ... #endif inside HTML documents
2011-08-11: dop: show HTML sourcecode if "!" is appended to URL
2011-08-10: fex_cleanup: delete obsolete users, too, via fex.ph $account_expire
2011-08-10: new FAQ design (with Javascript/CSS)
2011-08-09: fup: show error on invalid SKEY or GKEY
2011-08-09: fuc: fixed bug subuser and groupuser not lowercase forced
2011-08-09: address book may also contain option locale=&lt;languange>
2011-08-08: fex_cleanup: auto-expire user accounts with fex.ph variable
2011-08-08: $account_expire
2011-08-08: fexsend,fup: allow forward with locale
2011-08-08: dop: extra security check: files from lib and spool are not allowed
2011-08-07: fup: subusers and groupusers can also select a locale
2011-08-07: fup: if user selects a locale login, save it as default locale
2011-08-07: (does not affect fexsend and schwuppdiwupp)
2011-08-07: notification emails come in default locale
2011-08-03: fexsend: fixed bug uninitialized value when using chunked mode
2011-08-03: fexsend: added undocumented option -F female mode
2011-07-31: fexsend: added option -s streaming data
2011-07-31: fup: accept streaming data
2011-07-30: fexsend: -a tar archives no longer use a intermediate transferfile,
2011-07-30: but send via pipe (streaming)
2011-07-30: fexsend: fixed bug no resume on -a archives
2011-07-30: fexsend: always ask server if file already has been uploaded
2011-07-30: fup: more information on F*EX clients download and configuration
2011-07-27: fup: if comment contains "!bcc!" then sender will get a bcc
2011-07-27: of notification email
2011-07-26: fexget: added option -X do not extract archive file
2011-07-26: fexget: added option -a get all files
2011-07-26: fexsrv: fixed bug uninitialized value when using a reverse proxy
2011-07-26: fex_cleanup: fixed bug notify reminder email not localized
2011-07-22: fac(CGI): fixed bug displaying &lt; and & in logfiles
2011-07-22: fac(CGI): added getting error.log
2011-07-22: fop: allow multiple downloads from any client if sender = recipient
2011-07-16: added doc/reverse_proxy
2011-07-14: added optional czech localization
2011-07-01: FAQ.html reformated
2011-06-30: translate install job 20 times faster
2011-06-27: added robots.txt to disallow web robots indexing
2011-06-22: fup: disable HTML code in file listing (filename & comment)
2011-06-21: added optional italian localization
2011-06-17: fixed bug $bcc is ignored
2011-06-16: fexsend,fexget: better reverse proxy support
2011-06-16: (always send Host header)
2011-06-16: added optional galician localization
2011-06-15: fup: fixed bug always keep_default days in notification email
2011-06-14: fexsend: transparent proxy detection (and support)
2011-06-14: fixed bug $docdir ignored
2011-06-10: fex.ph: new config variable $bcc for notification emails
2011-06-09: set Reply-To in all notification emails
2011-06-09: fup: fixed security bug everyone can upload files with empty auth-ID
2011-06-05: fup: fixed bug insecure dependency in printf on "forward file"
2011-06-03: fup,fop: added throttle bandwith limit option (fex.ph)
2011-06-02: fup: added bandwith limit option
2011-06-01: added PID and request-number to the logs
2011-06-01: fex_cleanup: fixed bug no expire with AUTODELETE=NO
2011-05-31: support for FEXLIB /usr/local/share/fex/lib /usr/share/fex/lib
2011-05-30: fup,fexsend: added option -x to modify file parameters
2011-05-29: fup,fexsend: forward files with new comment and keep time
2011-05-29: rup: add mdomain to addresses without domain
2011-05-18: fup: fixed bug restricted users can forward files to anybody
2011-05-17: fixed bug access problems with AKEYs: now use SID instead of SIP
2011-05-11: added helper script mksgkeys (regenerates missing SKEYs and GKEYs)
2011-05-10: fex_cleanup: cleanup ADDRESS_BOOK file upload
2011-05-10: fex.ph,fex_cleanup: AUTODELETE=NUMBER ==>
2011-05-10: delete file on next NUMBER day after download
2011-05-09: fac,fup: added user specific autodelete default
2011-05-09: fac,fex_cleanup: added user specific keep default
2011-04-27: fexsend: fixed bug archiv.zip not working on Windows
2011-04-27: dop: added index function for htdoc directory with .htindex file
2011-04-25: fexsend: better proxy support (non-persistent connections)
2011-04-24: xx: better ESXi support (heuristic guessing of tar format)
2011-04-22: sexsend: base64 support for $FEXID and $FEXXX
2011-04-01: fexsend: continue without SID if SID is not available
2011-03-26: fexsrv: deactivate header_hook (inquisition) if request is a
2011-03-26: regular fop request
2011-03-18: fexsend: base64 support for $FEXID and $FEXXX
2011-03-09: fexget,fexsend: fixed bug no file listing for https
2011-03-04: removed "use Switch" because of warnings with perl 5.12
2011-02-28: sexsend: added option -g show transfer rate
2011-02-24: dop: evaluate &lt;&lt;perl-code>> inside html documents
2011-02-21: fexsend,fup: added option fexsend -U show authorized (login) URL
2011-02-18: do not modify download URL protocol if $dkey is set in fex.ph
2011-02-18: fac(CGI): fixed bug uninitialized value $server
2011-02-18: URLs in notification emails are derived from config variable $durl
2011-02-17: fup: fixed bug access denied with SKEY
2011-02-08: fup,fop,fuc: fixed bug access problems with sip in AKEYs
2011-02-08: fup: fixed bug no notification email for multiple recipients
2011-02-07: fexsend,fop: do not send same file (filename and mtime) twice
2011-02-06: fup: fixed bug no notification email after first failed upload
2011-01-31: schwuppdiwupp: added ISO-8859-1 support
2011-01-30: schwuppdiwupp: added running camel
2011-01-28: schwuppdiwupp: added chunksize to proxy options
2011-01-28: schwuppdiwupp: fixed bug timeout when using address book
2011-01-28: fexsend: fixed bug chunksize 0
2011-01-27: schwuppdiwupp: added advanced preferences Proxy and TMPDIR
2011-01-26: fex_cleanup: fixed bug uninitialized value in debuglog
2011-01-26: fex_cleanup: added option -v
2011-01-26: fexsend,fexget,sexsend: added option -V show version
2011-01-26: schwuppdiwupp: added drag&drop support for windows
2011-01-26: schwuppdiwupp: added 7zG support
2011-01-25: fuc: fixed bug cannot delete all subusers
2011-01-25: schwuppdiwupp: fixed bug 7-zip not found
2011-01-25: schwuppdiwupp: added drive letters in directory selection
2011-01-24: fop: IE bug workaround to store *.exe files
2011-01-18: schwuppdiwupp: added tar, zip and 7z container
2011-01-17: fexsend: fixed bug option -l not working with https URL
2011-01-16: fup: added 7zip hint in notification emails
2011-01-13: schwuppdiwupp: fixed bug no transfer at all when comment is set
2011-01-12: rup: added logging
2011-01-12: rup: wrong recipient cannot download file, but will get an error
2011-01-12: fac(CGI): fixed bug $server not declared
2011-01-07: fexget: keep file permission in overwrite mode
2011-01-04: fex_cleanup: fixed bug autodelete after partial download
2010-12-26: fex_cleanup: fixed bug too early expire for forwarded files
2010-12-10: new config variable @public_recipients for new CGI pup
2010-12-10: (public upload) - upload without auth-ID
2010-12-09: fex.ph,fur: new config variable @local_rdomain for self-
2010-12-09: registration of restricted external-to-internal users
2010-12-08: fup,foc: no access to foc for restricted users
2010-12-02: fup: if there is a cgi-bin/login it will be called on "logout"
2010-11-24: fex.ph: new config variable $default_locale
2010-11-24: dop: auto-expires every document (to prevent browser caching)
2010-11-16: fexsrv: better error handling if CGI is not executable
2010-11-09: new SKEYs and GKEYs, because old ones could be not unique
2010-11-09: access for subuser only with SKEY
2010-11-09: access for groupuser only with GKEY
2010-11-07: fup,fuc: added GKEY
2010-11-07: fexsend: SKEY or GKEY URLs can be recipients, too
2010-11-04: fexget: added (hidden) option -K
2010-11-03: fexsend: fixed bug proxy usage failed
2010-11-02: fop: fixed bug corrupted download with Internet Explorer
2010-10-25: fop,fup: better locking: no uploading is possible while a
2010-10-25: download is in progress for the same file
2010-10-24: fix,fop: fixed bug subuser not working (SKEY problem)
2010-10-24: xx: added locking
2010-10-23: xx,fop: added xx :slot option (multiple storage slots)
2010-10-20: fup,fop,fexsend: fexsend for subuser with SKEY
2010-10-19: fup: expires *KEY cookies on logout
2010-10-12: fup: fixed bug wrong interpretation of SKEY parameter+cookie
2010-10-05: fexend,fup: support of http reverse proxy
2010-09-27: fup: fixed bug missing Content-Type in upload status report
2010-09-20: fexsrv: IPv6 http support
2010-09-15: fac(CGI): fixed bug cannot delete and (re)create user
2010-09-15: fac(CGI): fixed bug cannot create user who was a recipient
2010-09-12: fexsend,fop: fixed bug resuming upload does not work with alias
2010-09-12: fup: fixed bug resuming upload handles autodelete and keep
2010-09-12: parameters incorrectly
2010-09-07: fac(CGI): fixed bug not working if there is no user at all
2010-09-05: fop: extra parameter keep=days
2010-08-31: install: fixed bug wrong ownership for spool files
2010-08-25: perl 5.8 required
2010-08-25: fexget: -s can write to named pipe or character special file
2010-08-21: fop: $limited_download checks dkey cookie instead of client IP
2009-08-20: removed mma
2010-08-18: fex_cleanup: fixed bug not expiring
2010-08-17: fac: fixed bug accept users without domain
2010-08-17: install: fixed bug empty $admin_pw
2010-08-15: fex.ph: optional fix address $sender_from (instead of F*EX user) in
2010-08-15: notification email From
2010-08-14: added optional spanish localization
2010-08-12: fup: speedup 90%
2010-08-12: fop: speedup 20%
2010-08-12: fop: better fexget compatibility
2010-08-12: (close connection after file delivery)
2010-08-11: fop: fixed IE download bug (missing header separating line)
2010-08-11: fop: fixed 1 min delay bug on AUTODELETE=YES
2010-08-08: sex: support for compressed streams
2010-08-08: sex,sexsend: removed unneccesary text mode (option -t)
2010-08-08: sex,sexsend: speedup factor 5
2010-08-08: added sexxx
2010-08-06: sex: fixed various bugs in client and server
2010-08-06: fac(CGI): fixed bug AKEY not working
2010-08-03: xx: no user inquiry for postprocessing if output is a pipe
2010-08-02: added optional german localization
2010-07-31: separated subusers in extra file $SPOOL/$USER/@SUBUSER
2010-07-25: fop: log also aborted downloads
2010-07-23: added fac CGI
2010-07-18: fexsrv,fup,fexsend: extra XKEY download with short // URL
2010-07-18: fexsend: fixed bug CHECKRECIPIENT not working
2010-07-16: added cookie support (for AKEY and SKEY)
2010-07-16: fup: fixed bug showstatus window too small for close button
2010-07-13: schwuppdiwupp: added CHECKRECIPIENT
2010-07-12: fop,xx: allow several concurrent downloads of STDFEX
2010-07-10: fop: workaround for stupid IE download bug
2010-07-03: fex.ph,fex.pp: new config variable @remote_domains
2010-07-01: fexsrv,fex.ph: new config variable $force_https
2010-06-29: fop: new config variable $limited_download with default NO
2010-06-29: => allow multiple downloads through proxy farm (varying IPs)
2010-06-29: fop: note every successful download in spool file "download"
2010-06-25: fexget: fixed bug download status info update too often
2010-06-23: fur: better sendmail clone compatibility:
2010-06-23: use space instead of comma as address separator
2010-06-19: fexget: new option -o overwrite mode
2010-06-19: fexget: use ./$file.tmp for downloading instead of $HOME/.fex/tmp/
2010-06-16: schwuppdiwupp: edit and select address book entries
2010-06-15: rup: fixed bug case sensitive recipient address
2010-06-12: fop: send X-File-ID on HEAD request, too
2010-06-12: fexget: added support of X-File-ID
2010-06-11: schwuppdiwupp: (chunked) multi-POST for proxy with 4 GB limit
2010-06-11: schwuppdiwupp: X-File-ID support
2010-06-08: fup,fexsend: (chunked) multi-POST for proxy with 4 GB limit
2010-06-06: fup,fop,fexsend: protocol extension X-File-ID (contains mtime
2010-06-06: of file) is the successor of X-Size for more
2010-06-06: reliable resume function
2010-06-02: schwuppdiwupp: added proxy support
2010-05-31: fexsend: fixed bug windows path elements in filename
2010-05-30: better server proxy support:
2010-05-30: AKEY and SKEY no longer rely on client ip
2010-05-29: fexsend: added proxy support
2010-05-28: fur: allow registration confirmation more than once
2010-05-27: fexsend: added option -b bounce (copy-forward)
2010-05-26: fup,foc: added copy-forward feature
2010-05-20: fexsend: fixed bug uninitialized value with option -@
2010-05-20: fur: fixed bug $main::admin not declared
2010-05-17: fexsend: added option -H for hints
2010-05-17: fexsend: added option -A for edit server address book
2010-05-16: fexsend: added HTTPS proxy support
2010-05-16: fup: fixed bug uninitialized value (line 1059)
2010-05-13: fup: fixed bug ignored KEEP and AUTODELETE options for groups
2010-05-12: fup: fixed bug ignored autodelete option from ADDRESSBOOK
2010-04-30: fup: fixed bug uninitialized value with CHECKRECIPIENT
2010-04-30: fexsend: no SID for https
2010-04-28: fup: fixed bug case sensitiv group addresses
2010-04-27: fexsend: fixed bug ignored server address book options
2010-04-27: fexsend: displays recipients and options before starting post
2010-04-27: fup: fixed bug ignored server address book autodelete option
2010-04-26: fexsrv: log all HTTP headers (no more ignore list)
2010-04-25: fexsrv: accept HTTP header with continuation lines
2010-04-22: fex.pp: added htdocs/header.html support
2010-04-22: fex.ph: added variable @H1_extra organization link and logo
2010-04-20: fexsrv,fexsend: HTTP header X-Timeout (info server->client)
2010-04-20: fexsrv: logging with locking
2010-04-19: fexsend: removed broken option -A and replaced it with more
2010-04-19: flexible feature "." for recipient address
2010-04-19: fexsend: fixed bug dies if sender is subuser (ADDRESS_BOOK error)
2010-04-19: fup: fixed bug no COMMENT in notification email
2010-04-19: dop: fixed bug error output with non GNU file command
2010-04-17: fexsend,fexget: added option -i for alternative accounts or servers
2010-04-12: fexsend: new verbose output format --> &lt;--
2010-04-11: fexsend: added option -Q quota query
2010-04-09: fup,fac: added quota support
2010-03-25: fup: fixed bug "Insecure dependency" when using AKEY parameter
2010-03-25: (eg: sending a second file)
2010-03-25: fup_template.html: fixed bug upload status window always shows
2010-03-25: "ERROR: no file data received"
2010-03-24: fexsend: fixed bug dies if there is no server address book
2010-03-22: FIX.jar: fixed bug interpret HTTP response "200 OK" as error.
2010-03-20: fup,fop: set mtime on user directory for last successfull access
2010-03-19: fexsend: fixed bug abort on short address if there is no server
2010-03-19: address book
2010-03-19: fex_cleanup: better cleanup for dkeys directory
2010-03-18: fup: fixed bug cannot DELETE with group recipient
2010-03-17: fup: fixed bug wrong success message on aborted uploads
2010-03-17: fop: fixed bug cannot handle @group names
2010-03-16: fup,fuc: fixed bug mixed case in F*EX group names and addresses
2010-03-14: fex.pp: do not send notification emails on empty files
2010-03-12: fup,fop: fixed bug case sensitiv FROM and TO addresses
2010-03-05: fup: fixed bug aliases are not accepted with fop_auth
2010-03-04: fexsrv: use CGI login if it exists as start-page
2010-02-26: fexsend: first check server address book, then mutt aliases
2010-02-26: fop: do not terminate session after ADDRESSBOOK request
2010-02-18: fexget: fixed bug always append existing file, ask for overwriting
2010-02-08: fexsrv: fixed bug uninitialized value in substitution (line 229)
2010-02-08: fex.pp: better qmail compatibility (space separated addresses)
2010-02-07: fac: fixed bug uninitialized $EDITOR environment variable
2009-12-28: fup,fop,fexsend: protocol extension X-Size for more reliable
2009-12-28: resume function (checks size of file)
2009-12-09: FIX.jar: can send more than one file (in zip archive)
2009-12-04: fex_cleanup: fixed bug send unnecessary reminder on AUTODELETE=NO
2009-11-26: fexsend,fexget,fup: added -z option for logfile inquiry
2009-11-13: fup,fuc: keep and autodelete options in server address book
2009-11-12: fexsrv: support for HTTP/1.0 persistant connections (for proxys)
2009-11-12: PID:SID in fexsrv.log CONNECT string (debugging help)
2009-11-06: added error.log
2009-11-03: fex_cleanup: also cleanup $SPOOL/.reg directory
2009-10-23: fup: added X-FEX-Filesize header in notification email
2009-10-18: fup: also test if $FROM is a valid email address
2009-10-11: fex_cleanup: fixed bug no cleanup at all on AUTODELETE=NO
2009-09-25: fex.pp: added X-Mailer in sent mails to please stupid spamfilter at
2009-09-25: hotmail or gmail
2009-09-02: fur,fex.pp: fixed bug not removing quote chars in qqq strings
2009-08-28: rup: fixed bug UTF-8 file names
2009-08-25: FIX.jar: accepts parameters TO, AKEY and SKEY (for subusers)
2009-08-24: fex_cleanup: fixed bug warning unitialized variable
2009-08-17: fex_cleanup: fixed bug typo mitime --> mtime
2009-08-12: fup: fixed bug "send another file" for subusers
2009-08-10: fexsend: fixed bug timeout on big archives
2009-07-27: to and from addresses in spool are now always localpart@domain,
2009-07-27: install contains automatic spool converter
2009-07-27: fup: fixed bug short aliases address list mismatch
2009-07-24: fup,fex_cleanup: fixed bug delete all files for multiple
2009-07-24: recipients after any download
2009-07-24: fexget: fixed bug delete local file before download
2009-07-20: fup: added autodelete and keep hack for HTML form
2009-07-18: fup: fixed bug leading . in file directory name
2009-07-18: fup: added CHECKRECIPIENT support
2009-07-18: fup: code cleanup, new 3-stage user interface
2009-07-18: fexsend: added CHECKRECIPIENT feature
2009-07-17: fexget: fixed bug wrong UTF8 handling
2009-07-16: fop: fixed bug sending wrong file size if TO or FROM has
2009-07-16: uppercase chars ==> resuming upload did not work
2009-07-11: fup: can select more than one address from address book
2009-07-08: fup,fex.pp: fixed bug wrong download URLs
2009-07-07: new spool directory layout $TO/$FROM/urlencode($FILENAME)
2009-07-07: fup: be more restrictive in accepting (illegal) parameters values
2009-07-07: fup,fuc: subuser access key name is now SKEY (KEY is depreciated)
2009-07-07: rup: new HTML layout, fixed bug in file select box
2009-07-06: fup: substitute all control characters in file name and comment
2009-07-06: with "_"
2009-07-02: better install script, guesses IP
2009-06-29: changed spool directory layout
2009-06-29: $TO/$FROM/$FILE --> $TO/$FROM/md5h($FILENAME)
2009-06-29: to avoid filename collisions
2009-06-28: added mailman authorization mma
2009-06-28: better address-book integration in fup
2009-06-26: FIX.jar: fixed several bugs, now working with Windows Vista, too
2009-06-25: added fup_template.html as an example for customizing upload page
2009-06-22: fup,fexsend,fexget: LIST also shows COMMENT
2009-06-10: default timeout 10 s --> 30 s
2009-06-01: fexsend: show transfer status on STDERR (also for xx)
2009-05-31: fexsend,fexget: show transfer rate (kB/s)
2009-05-29: fexsend: auto-reconnect after SIGPIPE (link failure, etc)
2009-05-26: fexsrv: ignore HTML anchors in GET requests (from stupid msnbot)
2009-05-17: fup: check if there is enough free space in spool
2009-04-07: new perl based install; requires server IP for xinetd binding
2009-03-25: fexget: fixed bug saving failed if on other partition then FEXHOME
2009-03-25: fexget: fixed bug calculated wrong transfer rate
2009-03-25: fexget: changed default answers to more secure values
2009-03-24: fexsend: new option -l for listing sent files
2009-03-24: fup: support for listing sent files
2009-03-24: fex.pp: default charset is now UTF-8 in HTTP reply
2009-03-16: fur: fixed bug no lower case transformation for user and domain
2009-03-05: fop: fixed bug no parallel download possible on multiple recipients
2009-03-03: dop: send Last-Modified HTTP header (java needs it)
2009-02-27: fuc: send information emails to sub-users by click-on-address
2009-02-22: fop: fixed bug download failed without FROM parameter
2009-02-20: test for /usr/lib/sendmail and /usr/sbin/sendmail
2009-02-18: fop: fixed bug file size query for alias recipient
2009-02-18: fexget: added option -a to get address-book from server
2009-02-17: fup,fuc: better linking
2009-02-14: fup: first send notification emails, then send HTTP 200 OK to client
2009-02-14: fup: accept ADDRESS_BOOK as upload
2009-02-13: fup,foc,fuc: added ADDRESS_BOOK support
2009-02-13: added fix and FIX.jar (Java applet client)
2009-02-11: fop: fixed bug file size request with multiple $to gives always 0
2009-02-11: (no upload resume possible with multiple recipients)
2009-02-11: fop: check for valid recipient address (in file path)
2009-02-11: ==> early abort possible when client uses illegal address for
2009-02-11: upload (resume-HEAD-request)
2009-02-10: fur: catch errors from sendmail(clone) and save them to $log
2009-02-09: fexsrv: fixed bug wrong log sequence in debug files
2009-02-04: fup: fixed bug cannot delete files by web interface
2009-02-03: fexsrv: fixed bug wrong download URL by HTTP_HOST from client request
2009-01-31: fexsrv: fixed bug handling of missing trailing / in doc requests
2009-01-30: rup: fixed bug wrong download URL in notification email
2009-01-26: fexsend: archive format 7z and zip with default compression
2009-01-26: fup,fuc,foc,rup: link to F*EX start page in top header
2009-01-21: fuc: URL for subusers with KEY parameter
2009-01-21: fup.fuc,foc: fixed bug wrong AKEY lookup
2009-01-20: fexsrv: better handling of URLs with trailing / (==> index.html)
2009-01-20: fop: fixed bug endless loop with fop_auth mode
2009-01-13: fup,fop: support for MIME-file types
2009-01-13: fexsend: added option -M for MIME-file to be displayed in webbrowser
2009-01-13: on download
2009-01-04: fup: increase minimum timeout to 10 s
2008-12-26: fup: do not allow re-upload (overwrite) if file is in download
2008-12-26: process
2008-12-23: sexsend,sexget: added HTTPS/SSL support
2008-12-21: fup: fixed bug removing old autodelete and error files failed
2008-12-20: added logwatch
2008-12-18: fexget: fixed bug responsiveness on slow links
2008-12-18: fexget: fixed bug save file name for archives
2008-12-12: fexget: better responsiveness on slow links (modem, ISDN)
2008-12-12: fup: added warning for incompatible clients (konqueror, etc)
2008-12-11: fexsend: allow comments in ID file
2008-12-03: fup,fex.pp: fixed bug UTF-8 subject in notfication email
2008-12-02: fexsend: better responsiveness on slow links (modem, ISDN)
2008-12-02: fexget: save original filename (parse HTTP header)
2008-11-28: fexserv: added special FlashGet (download sucker) brake
2008-11-27: added htdocs/version and htdocs/tools.html
2008-11-27: added fexsend, fexget, sexsend, sexget to htdocs/download
2008-11-27: dop: fixed bug symlink of symlink leads to hangup
2008-11-27: fop: teergrub download managers and other suckers
2008-11-26: fop: with URL parameter ?KEEP file can be downloaded more than once
2008-11-26: fexget: added option -k for keep on server
2008-11-24: fex_cleanup: fixed bug $autodelete not defined
2008-11-24: fexget: added HTTPS/SSL support
2008-11-22: fexsrv: reject requests with IP hostnames in HTTP Host header
2008-11-21: fex.ph,fop: $autodelete="DELAY" allows file download many times
2008-11-21: (but only from same IP and until next fex_cleanup run)
2008-11-21: fup,fop: fixed bug options keep and delete autodelay do not
2008-11-21: work with spool on NFS
2008-11-20: fexsend: added HTTPS/SSL support
2008-11-20: fex.ph: added config variable $autodelete
2008-11-20: fup: fixed bug subuser cannot send files
2008-11-19: use md5-hash of $from:$id instead of URL parameters FROM=$from&ID=$id
2008-11-19: fac: set correct exit status
2008-11-16: fup: fixed bug DELETE not working
2008-11-16: install: do not overwrite lib/fup.pl (perhaps contains site config)
2008-11-15: fex_cleanup: clean up $SPOOL/.ukeys/, too
2008-11-14: fup: show "user config" link only after authorization
2008-11-13: foc,fuc,fup: quick sub-user creation with auto-notification.
2008-11-10: fexget: fixed bug query &lt;> instead of &lt;STDIN>
2008-10-29: fup: do not require HTTP authorization if request already
2008-10-29: contains ID (methode used by xx)
2008-10-28: fex.pp: fixed bug $warning not defined
2008-10-28: fup: fixed bug do not allow subuser in fop_auth mode
2008-10-27: install: do not overwrite existing htdoc/index.html
2008-10-27: fup: fixed bug resend (SEEK) leads to HTTP error 666
2008-10-26: fexsrv: accept HTTP request with absolute URLs (http://...), too
2008-10-23: fexsrv: fixed bug continue connect logfile entry
2008-10-17: fexsrv: fixed bug keep_alive with HTTP/1.0
2008-10-17: fexsrv: fixed bug wrong warning in debug mode with empty line
2008-10-07: fexsrv: moved TIMEOUT message to debug.log
2008-10-06: dop: fixed bug opening file (did not deliver any file!)
2008-10-06: dop: implemented HTTP keep-alive (delivering more than one
2008-10-06: document per session)
2008-10-04: if config variable $fop_auth is set, download requires
2008-10-04: authentication and upload is restricted to registered users
2008-10-02: dop: declare exectuable scripts as application/octet-stream
2008-10-02: fup: added link to Windows client schwuppdiwupp.exe
2008-09-29: write upload speed to upload directory
2008-09-17: fac: fixed bug locating FEXLIB
2008-09-12: fup: added config lib/fup.pl
2008-09-05: fexsend: added rudimentary windows support
2008-09-03: dop: redirect on relative non-parent symlinks
2008-08-31: added fur (F*EX User (auto-) Registration)
2008-08-25: will die when no hostname is available
2008-08-21: added fexsend to htdocs/download
2008-08-21: fup: added ID mail sendback option
2008-08-20: fac: added -l option
2008-08-19: fexsrv: fixed bug SSL handling
2008-08-15: fup,fuc,fexsrv: dynamic protocol detection (HTTP/HTTPS)
2008-08-14: fup: fixed bug login possible with wrong login data (but no upload)
2008-08-13: fup: showstatus terminates immediately when empty file was uploaded
2008-08-13: fup: showstatus shows error message on illegal recipient address
2008-08-13: or when no file was uploaded
2008-08-13: (nececessary for stupid Internet Explorer!)
2008-08-11: splitted debugfiles with time stamp in filename
2008-08-11: fex_cleanup: clean up aborted uploads, .ukeys/ and .debug/, too
2008-08-11: fexsend,fexget: allow more than one file (with all options)
2008-08-08: fup: eliminate superfluous newlines in logfile on error handling
2008-08-08: changed bareword filehandles to indirect filehandles
2008-08-06: fup: decode %NUMBERs in file names from POST
2008-08-02: fexsend: bug fix -A option and argument handling
2008-08-01: fup: regular users can change the recipient in the upload form,
2008-08-01: sub users can not
2008-07-31: fup: fixed bug internet explorer not showing upload status window
2008-07-31: fup: fixed bug with id / special id / real id mixup
2008-07-31: fuc: nearly complete rewrite, better user interface
2008-07-30: fup: fixed bug when account is a symlink
2008-07-30: fup: fixed bug in authentication of subusers
2008-07-03: fop: workaround for Internet Explorer download bug
2008-07-02: fup,fop: switched default charset from ISO-8859-1 to UTF-8
2008-07-02: fup: uid for showstatus synchronization
2008-06-21: fexget: downloading without wget, file number as argument
2008-06-20: fexget,fop: added DELETE option
2008-06-20: fexsend: send more files in one run
2008-05-30: added missing sex to distribution
2008-05-30: fexsend: added -A archiv to yourself option
2008-05-28: fup: fixed bug in LIST and DELETE commands
2008-05-27: fexsrv: correct HTTP redirect on missing trailing / in URL
2008-05-26: sex,sexsend: better public mode
2008-05-24: sex,sexsend: added text mode option
2008-05-23: added missing foc and rup to distribution
2008-05-20: fexsend: fixed bug in list parsing (-l option)
2008-05-15: fexsrv,dop: fixed bug in HTTP keep_alive multi-requests
2008-05-02: fexsrv,dop: support for HTTP keep_alive multi-requests
2008-05-02: fexsrv: more robust header parsing (ignore superfluous spaces)
2008-04-28: added support for HTTP keep_alive
2008-04-20: added foc and rup
2008-04-20: or is expired; error message is kept 3*keep_default days
2008-04-19: install: do not overwrite old fex.ph, but create fex.ph_new instead
2008-04-18: fup: fixed bug filename with path in notification email
2008-04-16: fexsrv,fop,dop: implemented HTTP HEAD
2008-04-14: renamed cgilaunch to fexsrv
2008-04-14: fup: do not send notify-mail if file already exists (overwrite mode)
2008-04-14: fup: do not accept file if authentication fails
2008-04-14: fup,fop,fexsend: new secure download URL scheme with random dkey
2008-04-11: fup: fixed bug in upload bar with 8-bit file names
2008-04-11: fex_cleanup: fixed bug not removing aborted uploads
2008-04-10: added F*EX camel logo
2008-04-09: added dop (generic document output)
2008-04-08: renamed confusing ID to auth-ID (request by chris@citecs.de)
2008-04-08: fuc: fixed bug with more than 1 sub-user
2008-04-07: fup: readded keep parameter (code got lost sometime?)
2008-04-06: fup: use Net::DNS instead of external host command
2008-04-02: install: better error handling (patch by chris@citecs.de)
2008-04-02: more docs and improved logging
2008-04-01: cgilaunch: fixed bug in determing REMOTE_HOST when using stunnel
2008-04-01: sex,sexsend: added public mode
2008-03-31: changed project name to F*EX because of name collision with
2008-03-28: xx: changed syntax, now compatible to zz
2008-03-27: fup: fixed bug in mail address verification
2008-03-24: fup,fexsend: show transfer rate in kB if filesize &lt; 2 MB
2008-03-24: fex.pp: umask 077
2008-03-23: fup: fixed bug in using multiple recipients
2008-03-22: first public release
2008-03-22: 2006-11-??
2008-03-22: first code

133 files changed:
README [new file with mode: 0644]
bin/afex [new file with mode: 0755]
bin/asex [new symlink]
bin/ezz [new file with mode: 0755]
bin/fac [new file with mode: 0755]
bin/fbm [new file with mode: 0755]
bin/fex_cleanup [new file with mode: 0755]
bin/fexget [new file with mode: 0755]
bin/fexsend [new file with mode: 0755]
bin/fexsrv [new file with mode: 0755]
bin/fexwall [new file with mode: 0755]
bin/l [new file with mode: 0755]
bin/lf [new symlink]
bin/ll [new symlink]
bin/logwatch [new file with mode: 0755]
bin/mksgkeys [new file with mode: 0755]
bin/sexget [new symlink]
bin/sexsend [new file with mode: 0755]
bin/sexxx [new symlink]
bin/xx [new symlink]
bin/zz [new file with mode: 0755]
cgi-bin/fac [new file with mode: 0755]
cgi-bin/foc [new file with mode: 0755]
cgi-bin/fop [new file with mode: 0755]
cgi-bin/fuc [new file with mode: 0755]
cgi-bin/fup [new file with mode: 0755]
cgi-bin/fur [new file with mode: 0755]
cgi-bin/pup [new file with mode: 0755]
cgi-bin/rup [new file with mode: 0755]
cgi-bin/sex [new file with mode: 0755]
doc/Changes [new file with mode: 0644]
doc/Contribs [new file with mode: 0644]
doc/IPv6 [new file with mode: 0644]
doc/Licence [new file with mode: 0644]
doc/SEX [new file with mode: 0644]
doc/SSL [new file with mode: 0644]
doc/concept [new file with mode: 0644]
doc/debugging [new file with mode: 0644]
doc/fex-client_1.pdf [new file with mode: 0644]
doc/fex-client_2.pdf [new file with mode: 0644]
doc/installation [new file with mode: 0644]
doc/new [new file with mode: 0644]
doc/newfeatures [new file with mode: 0644]
doc/reverse_proxy [new file with mode: 0644]
doc/version [new file with mode: 0644]
doc/xx [new file with mode: 0644]
etc/mime.types [new file with mode: 0644]
etc/xinetd_fex [new file with mode: 0644]
htdocs/FAQ/FAQ.html [new symlink]
htdocs/FAQ/admin.faq [new file with mode: 0644]
htdocs/FAQ/admin.html [new file with mode: 0644]
htdocs/FAQ/all.html [new file with mode: 0644]
htdocs/FAQ/faq.pl [new file with mode: 0644]
htdocs/FAQ/index.html [new symlink]
htdocs/FAQ/local.faq [new file with mode: 0644]
htdocs/FAQ/local.html [new file with mode: 0644]
htdocs/FAQ/meta.faq [new file with mode: 0644]
htdocs/FAQ/meta.html [new file with mode: 0644]
htdocs/FAQ/misc.faq [new file with mode: 0644]
htdocs/FAQ/misc.html [new file with mode: 0644]
htdocs/FAQ/user.faq [new file with mode: 0644]
htdocs/FAQ/user.html [new file with mode: 0644]
htdocs/Licence [new file with mode: 0644]
htdocs/SEX.html [new file with mode: 0644]
htdocs/action-fex-camel.gif [new file with mode: 0644]
htdocs/download/fexget [new file with mode: 0755]
htdocs/download/fexsend [new file with mode: 0755]
htdocs/download/sex.stream [new symlink]
htdocs/download/sexget [new symlink]
htdocs/download/sexsend [new file with mode: 0755]
htdocs/dynamic.html [new file with mode: 0644]
htdocs/favicon.ico [new file with mode: 0644]
htdocs/features.html [new file with mode: 0644]
htdocs/fup_template.html [new file with mode: 0644]
htdocs/index.html [new file with mode: 0644]
htdocs/logo.jpg [new file with mode: 0644]
htdocs/robots.txt [new file with mode: 0644]
htdocs/small_logo.jpg [new file with mode: 0644]
htdocs/sup.html [new file with mode: 0644]
htdocs/tools.html [new file with mode: 0644]
htdocs/version [new file with mode: 0644]
install [new file with mode: 0755]
lib/dop [new file with mode: 0755]
lib/fex.ph [new file with mode: 0644]
lib/fex.pp [new file with mode: 0644]
lib/fup.pl [new file with mode: 0644]
lib/reactivation.txt [new file with mode: 0644]
locale/czech/htdocs/index.html [new file with mode: 0644]
locale/czech/lang.html [new file with mode: 0644]
locale/czech/lib/fup.pl [new file with mode: 0644]
locale/czech/lib/reactivation.txt [new file with mode: 0644]
locale/debian_translate [new file with mode: 0755]
locale/french/htdocs/FAQ/FAQ.html [new file with mode: 0644]
locale/french/htdocs/FAQ/admin.faq [new file with mode: 0644]
locale/french/htdocs/FAQ/admin.html [new file with mode: 0644]
locale/french/htdocs/FAQ/all.html [new file with mode: 0644]
locale/french/htdocs/FAQ/faq.pl [new file with mode: 0644]
locale/french/htdocs/FAQ/index.html [new symlink]
locale/french/htdocs/FAQ/local.faq [new file with mode: 0644]
locale/french/htdocs/FAQ/local.html [new file with mode: 0644]
locale/french/htdocs/FAQ/meta.faq [new file with mode: 0644]
locale/french/htdocs/FAQ/meta.html [new file with mode: 0644]
locale/french/htdocs/FAQ/misc.faq [new file with mode: 0644]
locale/french/htdocs/FAQ/misc.html [new file with mode: 0644]
locale/french/htdocs/FAQ/user.faq [new file with mode: 0644]
locale/french/htdocs/FAQ/user.html [new file with mode: 0644]
locale/french/htdocs/index.html [new file with mode: 0644]
locale/french/lang.html [new file with mode: 0644]
locale/french/lib/fup.pl [new file with mode: 0644]
locale/french/lib/reactivation.txt [new file with mode: 0644]
locale/galician/htdocs/FAQ.html [new file with mode: 0644]
locale/galician/htdocs/index.html [new file with mode: 0644]
locale/galician/lang.html [new file with mode: 0644]
locale/galician/lib/fup.pl [new file with mode: 0644]
locale/galician/lib/reactivation.txt [new file with mode: 0644]
locale/german/htdocs/FAQ.html [new file with mode: 0644]
locale/german/htdocs/index.html [new file with mode: 0644]
locale/german/lang.html [new file with mode: 0644]
locale/german/lib/fup.pl [new file with mode: 0644]
locale/german/lib/reactivation.txt [new file with mode: 0644]
locale/italian/htdocs/FAQ.html [new file with mode: 0644]
locale/italian/htdocs/index.html [new file with mode: 0644]
locale/italian/lang.html [new file with mode: 0644]
locale/italian/lib/fup.pl [new file with mode: 0644]
locale/italian/lib/reactivation.txt [new file with mode: 0644]
locale/spanish/htdocs/FAQ.html [new file with mode: 0644]
locale/spanish/htdocs/index.html [new file with mode: 0644]
locale/spanish/lang.html [new file with mode: 0644]
locale/spanish/lib/fup.pl [new file with mode: 0644]
locale/spanish/lib/reactivation.txt [new file with mode: 0644]
locale/translate [new file with mode: 0755]
locale/translations [new file with mode: 0644]
upgrade [new symlink]

diff --git a/README b/README
new file mode 100644 (file)
index 0000000..d866cd3
--- /dev/null
+++ b/README
@@ -0,0 +1,35 @@
+F*EX (File EXchange) is a service to send big (large, huge, giant, ...) files
+from sender user A to recipient user B by HTTP.
+
+Sender and recipient user must have an e-mail-address and a web-browser, 
+that is all (for them).
+
+The sender uploads the file to the F*EX-server and the recipient
+automatically gets a notification e-mail with a download-URL.
+
+The sender must have a valid auth-ID, given by the F*EX administrator.
+
+This F*EX distribution contains the F*EX server and the optional client 
+programs fexsend, fexget, sexsend, sexget, xx and zz which run on UNIX. 
+
+Simply run "./install", which installs all files into /home/fex/
+Then edit /home/fex/lib/fex.ph and set your local config.
+
+Afterwards you can add F*EX users with /home/fex/bin/fac
+
+If you want to upgrade from a previous F*EX version, you also can run
+"./install" which is save, because it will not overwrite:
+  /home/fex/lib/fex.ph
+  /home/fex/lib/fup.pl
+  /home/fex/lib/reactivation.txt
+  /home/fex/htdocs/index.html
+but will create *_new versions of these files which you then can copy manually 
+if you want.
+
+See directory doc for more information.
+
+If you have comments, suggestions, requests, etc, send me an e-mail:
+framstag@rus.uni-stuttgart.de
+
+Or fex some nice, funny, interesting videos (eg cute pets) to this address :-)
+
diff --git a/bin/afex b/bin/afex
new file mode 100755 (executable)
index 0000000..f9b1a0c
--- /dev/null
+++ b/bin/afex
@@ -0,0 +1,180 @@
+#!/usr/bin/perl -w
+
+# client for anonymous FEX or SEX
+#
+# Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
+#
+# Perl Artistic Licence
+
+$fexserver = '';
+
+$0 =~ s:.*/::;;
+
+$u = $ENV{AFEX} || $fexserver || &guessserver;
+$u = "http://$u" if $u !~ /^http/;
+
+if (@ARGV and $ARGV[0] eq '-h' or not @ARGV and -t STDIN) { &usage }
+
+$opt_v = (@ARGV and $ARGV[0] eq '-v') ? shift @ARGV : '';
+
+if ($0 eq 'asex') {
+  $a = "$u/anonymous";
+  if (-t STDIN and @ARGV and "@ARGV" =~ /^(\d+)( -)?$/) {
+    $id = $1;
+    if ($2) {
+      vexec("sexget $opt_v $a $id | tar xvf -");
+    } else {
+      vexec(ws("sexget $opt_v $a $id"));
+    }
+  } else {
+    if (@ARGV and $ARGV[0] =~ /^\d+$/) {
+      $id = shift @ARGV;
+    } else {
+      $id = sprintf("%06d",rand(1000000));
+    }
+    print "# commands for SEX recipient:\n";
+    if (@ARGV) {
+      print "wget -qO- $u/sex?anonymous=$id | tar xvf -\n";
+      print "asex $id -\n";
+      print "# streaming files and waiting for SEX recipient:\n";
+      vexec("tar cvf - @ARGV | sexsend -q $opt_v $a $id");
+    } else {
+      print "wget -qO- $u/sex?anonymous=$id\n";
+      print "asex $id\n";
+      vexec(ws("sexsend -q $opt_v $a $id"));
+    }
+  }
+  exit;
+}
+
+if ($0 eq 'afex') {
+
+  if ("@ARGV" =~ /^-(\d+)$/) {
+    $n = $1;
+    $cmd = "wget -qO/dev/null $u/fop/anonymous/anonymous/afex_$n.tar?DELETE";
+    vsystem(ws($cmd));
+    print "not " if $?;
+    print "deleted\n";
+    exit;
+  }
+
+  # download
+  if (-t STDIN and "@ARGV" =~ /^(\d+)( -)?$/) {
+    $id = $1;
+    $nq = $2;
+    $u .= "/fop/anonymous/anonymous/afex_$id.tar";
+    if ($nq) {
+      vexec("fexget $opt_v -s- $u | tar xvf -");
+    } else {
+      $aft = "/tmp/afex_$id.tar";
+      $cmd = "fexget $opt_v -s- $u >$aft";
+      vsystem($cmd);
+      if (`file $aft` =~ /tar archive/) {
+        print "Files in archive:\n";
+        $cmd = "tar tvf $aft";
+        vsystem(ws($cmd));
+        print "extract these files (Y/n)? ";
+        if ((<STDIN>||'y') =~ /^[Yy\n]/) {
+          $cmd = "tar xvf $aft";
+          vsystem(ws($cmd));
+          unlink $aft;
+        } else {
+          print "keeping $aft\n";
+        }
+      } else {
+        open $aft,$aft or die "$0: cannot open $aft = $!\n";
+        unlink $aft;
+        print while read($aft,$_,65536);
+      }
+    }
+    exit;
+  }
+
+  # upload
+  else {
+    if (@ARGV and $ARGV[0] =~ /^\d+$/) {
+      $id = shift @ARGV;
+    } else {
+      $id = sprintf("%06d",rand(1000000));
+    }
+    $aft = "afex_$id.tar";
+    my $durl ="$u/fop/anonymous/anonymous/$aft??ID=ANONYMOUS";
+    if (`wget -S --spider $durl 2>&1` =~ /X-Location:/) {
+      die "$0: afex $id already exists - choose another ID\n";
+    }
+    @fexsend = ws("fexsend $opt_v -o -K -k 1");
+    if (@ARGV) {
+      vsystem(@fexsend,'-a',$aft,@ARGV,"$u/anonymous");
+      exit $? if $?;
+    } elsif (not -t STDIN) {
+      $aft = "/tmp/$aft";
+      open $aft,'>',$aft or die "$0: cannot write $aft - $!\n";
+      print {$aft} $_ while read(STDIN,$_,65536);
+      close $aft;
+      $s = vsystem(@fexsend,$aft,"$u/anonymous");
+      unlink $aft;
+      exit $s if $s;
+    } else {
+      die "say captain, say WHOT?!";
+    }
+    print "For download use:\n";
+    print "$u//$aft\n" if -t STDIN;
+    print "afex $id\n";
+  }
+  exit;
+}
+
+&usage;
+
+sub guessserver {
+  my $fexserver = '';
+  my $rc = '/etc/resolv.conf';
+  local $_;
+  
+  open $rc,$rc or die "$0: cannot open $rc - $!\n";
+  while (<$rc>) {
+    if (/^\s*domain\s+([\w.-]+)/) {
+      $fexserver = "http://fex.$1";
+      last;
+    }
+    if (/^\s*search\s+([\w.-]+)/) {
+      $fexserver = "http://fex.$1";
+    }
+  }
+  close $rc;
+  return $fexserver;
+}
+
+sub ws {
+  local $_ = shift;
+  return split;
+}
+
+sub vexec {
+  warn "@_\n" if $opt_v;
+  exec @_;
+}
+
+sub vsystem {
+  warn "@_\n" if $opt_v;
+  system @_;
+}
+
+sub usage {
+  if ($0 eq 'afex') {
+    print "file input usage:  $0 [ID] files...\n";
+    print "file output usage: $0 ID [-]\n";
+    print "pipe input usage:  ... | $0 [ID]\n";
+    print "pipe output usage: $0 ID | ...\n";
+    print "delete usage:      $0 -ID\n";
+    exit;
+  }
+  if ($0 eq 'asex') {
+    print "file input usage:  $0 [ID] files...\n";
+    print "file output usage: $0 ID -\n";
+    print "pipe input usage:  ... | $0 [ID]\n";
+    print "pipe output usage: $0 ID | ...\n";
+    exit;
+  }
+  die "program name must be a afex or asex, not $0\n";
+}
diff --git a/bin/asex b/bin/asex
new file mode 120000 (symlink)
index 0000000..0bb73f5
--- /dev/null
+++ b/bin/asex
@@ -0,0 +1 @@
+afex
\ No newline at end of file
diff --git a/bin/ezz b/bin/ezz
new file mode 100755 (executable)
index 0000000..c15ece3
--- /dev/null
+++ b/bin/ezz
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+ZZ=${ZZ:-$HOME/.zz}
+
+usage() {
+  exec cat<<EOD
+ezz is the edit helper program for the generic zz clip board program.
+The clip board is \$ZZ (default: \$HOME/.zz). Options and modes are:
+
+"ezz"                  edit \$ZZ
+"... | ezz"            write STDIN from pipe to \$ZZ and call editor
+"... | ezz +"          add STDIN from pipe to \$ZZ and call editor
+"ezz 'perl-script'"    run perl-script on \$ZZ
+"ezz - 'perl-script'"  run perl-script on \$ZZ and write result to STDOUT
+"ezz filter [args]"    run filter [with args] on \$ZZ
+"ezz - filter [args]"  run filter [with args] on \$ZZ and write result to STDOUT
+"ezz -r"               restore \$ZZ from last ezz operation (\$ZZ~)
+
+Examples:
+
+  ls -l | ezz
+  ezz "s/ /_/g"
+  ezz head -3
+  ezz - head -3
+
+Limitation: zz does not work across different accounts!  
+EOD
+}
+
+JEDINIT="SAVE_STATE=0"; export JEDINIT
+
+if [ ! -t 0 ]; then
+  if [ x"$1"x = x+x ]; then
+    shift
+    cat >>$ZZ
+  else
+    cat >$ZZ
+  fi
+fi
+
+test -z "$1" && exec ${EDITOR:-vi} $ZZ
+
+case "X$*" in
+  X-h) usage;;
+  X-r) exec mv $ZZ~ $ZZ;;
+esac
+
+OUT="$1"
+test "X$OUT" = X- && shift
+test -z "$1" && exec cat $ZZ
+mv $ZZ $ZZ~
+case `type "$1" 2>&1` in
+  *not\ found) perl -pe "$@" <$ZZ~>$ZZ || mv $ZZ~ $ZZ;;
+            *) "$@"          <$ZZ~>$ZZ || mv $ZZ~ $ZZ;;
+esac
+test "X$OUT" = X- && exec cat $ZZ
diff --git a/bin/fac b/bin/fac
new file mode 100755 (executable)
index 0000000..0946704
--- /dev/null
+++ b/bin/fac
@@ -0,0 +1,915 @@
+#!/usr/bin/perl -w
+
+# CLI admin client for the FEX service
+#
+# Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
+#
+
+use 5.006;
+use Getopt::Std;
+use File::Basename;
+use Cwd 'abs_path';
+use Digest::MD5        'md5_hex';
+
+use constant M => 1024*1024;
+use constant DS => 60*60*24;
+
+# do not run as CGI!
+exit if $ENV{SCRIPT_NAME};
+
+unless ($FEXLIB = $ENV{FEXLIB}) {
+  if ($ENV{FEXHOME}) {
+    $FEXLIB = $ENV{FEXHOME}.'/lib';
+  } elsif (-f '/usr/share/fex/lib/fex.ph') {
+    $FEXLIB = '/usr/share/fex/lib';
+  } else {
+    $FEXLIB = dirname(dirname(abs_path($0))).'/lib';
+  }
+  $ENV{FEXLIB} = $FEXLIB;
+}
+die "$0: no FEXLIB\n" unless -f "$FEXLIB/fex.pp";
+
+# become effective user fex
+unless ($<) {
+  if (my @pw = getpwnam('fex')) {
+    $)         = $pw[3];
+    $>         = $pw[2];
+    $ENV{HOME} = $pw[7];
+  } else {
+    die "$0: no such user 'fex'\n";
+  }
+}
+
+umask 077;
+
+# import from fex.pp
+our ($FEXHOME,$FHS,$hostname,$spooldir,$logdir,$akeydir,$docdir);
+our ($durl,@durl,$mdomain,$admin,$mailmode);
+our ($autodelete,$keep_default,$keep_max,$recipient_quota,$sender_quota);
+our (@local_rdomains);
+local $notification = 'full';
+
+# load common code, local config : $HOME/lib/fex.ph
+require "$FEXLIB/fex.pp" or die "$0: cannot load $FEXLIB/fex.pp - $!\n";
+
+die "$0: \$admin not configured in $FEXLIB/fex.ph\n" unless $admin;
+
+$EDITOR = $ENV{EDITOR} || $ENV{VISUAL} ||
+          (-x '/usr/bin/editor' ? '/usr/bin/editor' : 'vi');
+
+$opt_c = $opt_v = $opt_l = $opt_L = $opt_h = $opt_w = $opt_u = $opt_R = 0;
+$opt_M = $opt_E = 0;
+$opt_r = $opt_d = $opt_q = $opt_a = $opt_n = $opt_k = $opt_m = '';
+$opt_y = $opt_S = $opt_C = $opt_D = $opt_A = $opt_V = $opt_P = '';
+${'opt_/'} = '';
+
+@__ = @ARGV;
+while (my $a = shift @__) {
+  if ($a eq '-V') {
+    shift @__;
+  } else {
+    push @_ARGV,$a;
+  }
+}
+
+chdir $spooldir or die "$0: no $spooldir\n";
+
+@stat = stat $spooldir or die "$0: cannot access $spooldir - $!\n";
+warn "WARNING: $spooldir with owner=root !?\n" unless $stat[4];
+if (abs_path($spooldir) ne abs_path("$FEXHOME/spool")) {
+  warn "WARNING: \$spooldir differs from $FEXHOME/spool !\n";
+}
+  
+getopts('hcvlLwuMRE/q:r:d:a:n:k:m:y:S:C:A:V:D:P:') or usage(2);
+usage(0)   if $opt_h;
+examples() if $opt_E;
+
+if (${'opt_/'}) {
+  my $admin = shift;
+  my $id = shift or die "usage: $0 -/ admin-email-address auth-ID\n";
+  if ($admin !~ /.\@[\w.-]+\.[a-z]+$/) {
+    die "$0: $admin is not an email address\n";
+  }
+  mkdir $admin;
+  my $aa = "$spooldir/$admin/@";
+  open $aa,'>',$aa or die "$0: cannot write $aa - $!\n";
+  print {$aa} $id,"\n";
+  close $aa or die "$0: cannot write $aa - $!\n";
+  my $fph = "$FEXLIB/fex.ph";
+  $_ = slurp($fph) or die "$0: cannot read $fph\n";
+  s/^\s*\$admin\s*=.*/\$admin = '$admin';/m or 
+  $_ = "\$admin = '$admin';\n".$_;
+  open $fph,">$fph.new" or die "$0: cannot write $fph.new\n";
+  print {$fph} $_;
+  close $fph;
+  rename "$fph.new",$fph or die "$0: cannot rename $fph.new to $fph\n";
+  my $fid = "$ENV{HOME}/.fex/id";
+  mkdir dirname($fid);
+  rename $fid,$fid.'_save';
+  open $fid,'>',$fid or die "$0: cannot create $fid - $!\n";
+  if ($durl =~ m{(https?://.+?)/}) {
+    print {$fid} "$1\n";
+  } else {
+    print {$fid} "$hostname\n";
+  }
+  print {$fid} "$admin\n";
+  print {$fid} "$id\n";
+  close $fid;
+  print "new admin account: $admin\n";
+  exit;
+}
+
+&check_admin;
+
+if ($opt_V) {
+  while (my ($hh,$vh) = each (%vhost)) {
+    if ($opt_V eq basename($vh) or $opt_V eq $hh) {
+      $ENV{HTTP_HOST} = $hh;
+      $ENV{VHOST} = "$hh:$vh";
+      $ENV{FEXLIB} = "$vh/lib";
+      die "$0: no $ENV{FEXLIB}/fex.ph\n" unless -f "$ENV{FEXLIB}/fex.ph";
+      exec $0,@_ARGV;
+      die "$0: cannot re-exec\n";
+    }
+  }
+  die "$0: no virtual host $opt_V defined\n";
+}
+
+$fup = $durl;
+$fup =~ s:/[^/]+$:/fup:;
+
+# maintenance mode
+if ($opt_m) {
+  if ($opt_m eq 'exit') {
+    if (unlink '@MAINTENANCE') {
+      warn "$0: leaving maintenance mode\n";
+    } else {
+      warn "$0: no maintenance mode\n";
+    }
+  } else {
+    unlink '@MAINTENANCE';
+    symlink $opt_m,'@MAINTENANCE'
+      or die "$0: cannot write $spooldir/\@MAINTENANCE - $!";
+    warn "$0: entering maintenance mode\n";
+  }
+  exit;
+}
+
+# list files or resend notification e-mails
+if ($opt_M) {
+  my ($mtime,$comment,$file,$keep);
+  local $_;
+  
+  if (@ARGV) {
+    foreach $file (glob("@ARGV")) {
+      $mtime = mtime("$file/data") or next;
+      $comment = slurp("$file/comment")||'';
+      next if $comment =~ /NOMAIL/;
+      $keep = readlink "$file/keep" 
+           || readlink "$file/../../\@KEEP" 
+           || $keep_default;
+      $keep = $keep - int((time-mtime("$file/data"))/60/60/24);
+
+      notify(
+        status     => 'new',
+        dkey       => readlink "$file/dkey",
+        filename   => filename($file),
+        keep       => $keep,
+        comment    => $comment,
+        warn       => int(($mtime-time)/DS)+$keep,
+        autodelete => readlink "$file/autodelete" || $autodelete,
+      );
+      print "send notification e-mail for $file\n";
+    }
+  } else {
+    # just list files
+    foreach $file (glob "*/*/*/data") {
+      next if $file =~ /^_?(anonymous|fexmail)/;
+      $file =~ s:/data$::;
+      $comment = "$file/comment";
+      if (open $comment,$comment and <$comment> =~ /NOMAIL/) {
+        next;
+      }
+      print "$file\n";
+    }
+  }
+  exit;
+}
+
+# show logfile
+if ($opt_w) {
+  $log = "$logdir/fexsrv.log";
+  warn "$0: polling $log\n\n";
+  exec "$FEXHOME/bin/logwatch",$log;
+  die "$0: logwatch not found\n";
+}
+
+# list files and download URLs
+if ($opt_l) {
+  my ($file,$dkey,@L);
+  chdir $spooldir or die "$0: $spooldir - $!\n";
+  foreach $file (glob "*/*/*") {
+    if (-s "$file/data" and 
+        $dkey = readlink("$file/dkey") and 
+        -l ".dkeys/$dkey"
+    ) {
+      push @L,sprintf "%2\$s --> %1\$s : $durl/$dkey/%3\$s\n",split "/",$file;
+    }
+  }
+  print sort @L if @L;
+  exit;
+}
+
+# list files detailed
+if ($opt_L) {
+  my $filter = shift;
+  my ($comment,$file,$keep,$old,$size,$download);
+  local $_;
+  
+  foreach $file (glob "*/*/*/data") {
+    next if $file =~ m:(.+?)/: and -l $1;
+    $size = -s $file or next;
+    $file =~ s:/data$::;
+    next if $filter and $file !~ /$filter/;
+    $comment = slurp("$file/comment")||'';
+    $dkey = readlink("$file/dkey")||'';
+    $keep = readlink("$file/keep")||$keep_default;
+    $old = int((time-mtime("$file/data"))/60/60/24);
+    $download = join(' & ',split("\n",(slurp("$file/download")||'')));
+    print "\n$file\n";
+    printf "  comment: %s\n",decode_utf8($comment);
+    printf "  size: %s\n",d3($size); 
+    printf "  sender ip: %s\n",readlink("$file/ip")||'';
+    printf "  expire in: %s days\n",$keep-$old;
+    printf "  upload speed: %s kB/s\n",readlink("$file/speed")||0;
+    printf "  URL: $durl/$dkey/%3\$s\n",split "/",$file;
+    printf "  download: %s\n",$download;
+  }
+  exit;
+}
+
+# delete user 
+if ($opt_d) {
+  $idf = "$spooldir/$opt_d/\@";
+  die "$0: no such user $opt_d\n" unless -f $idf;
+  unlink $idf or die "$0: cannot remove $idf - $!\n";
+  foreach $rf (glob "$spooldir/$opt_d/\@*") { unlink $rf }
+  print "$opt_d deleted\n";
+  exit;
+}
+
+# set user restriction file
+if ($opt_R) {
+  $user = shift or die "usage: $0 -R user\n";
+  $user .= '@'.$mdomain if $mdomain and $user !~ /@/;
+  die "$0: no user $user\n" unless -d "$spooldir/$user";
+  unless (@local_rdomains) {
+    die "$0: no \@local_rdomains in server config\n";
+  }
+  my $rf = "$spooldir/$user/\@ALLOWED_RECIPIENTS";
+  open $rf,'>',$rf or die "$0: cannot open $rf - $!";
+  print {$rf} "\@LOCAL_RDOMAINS\n";
+  close $rf;
+  print "$user restricted\n";
+  exit;
+}
+
+# edit user restriction file
+if ($opt_r) {
+  if    ($opt_r =~ /^r/i) { $opt_r = 'ALLOWED_RECIPIENTS' }
+  elsif ($opt_r =~ /^u/i) { $opt_r = 'UPLOAD_HOSTS' }
+  elsif ($opt_r =~ /^d/i) { $opt_r = 'DOWNLOAD_HOSTS' }
+  else                    { usage(2) }
+  $user = shift or usage(2);
+  $user .= '@'.$mdomain if $mdomain and $user !~ /@/;
+  die "$0: no user $user\n" unless -d "$spooldir/$user";
+  my $rf = "$spooldir/$user/\@$opt_r";
+  unless (-s $rf) {
+    open $rf,'>',$rf or die "$0: cannot open $rf - $!";
+    if ($opt_r eq 'ALLOWED_RECIPIENTS') {
+      print {$rf}<<EOD;
+# Restrict allowed recipients. Only listed addresses are allowed as recipients.
+# Make this file COMPLETLY empty if you want to disable the restriction.
+# An allowed recipient is an e-mail address. You can use * as wildcard.
+# Examples:
+#    framstag\@rus.uni-stuttgart.de
+#    *\@flupp.org
+EOD
+    } elsif ($opt_r eq 'UPLOAD_HOSTS') {
+      print {$rf}<<EOD;
+# Restrict allowed upload hosts. 
+# Only listed addresses are allowed as upload hosts.
+# Make this file COMPLETLY empty if you want to disable the restriction.
+# You can add single ip adresses or ip ranges.
+# Examples:
+#    129.69.1.11
+#    10.0.10.0-10.0.10.255
+EOD
+    } elsif ($opt_r eq 'DOWNLOAD_HOSTS') {
+      print {$rf}<<EOD;
+# Restrict allowed download hosts. 
+# Only listed addresses are allowed as download hosts.
+# Make this file COMPLETLY empty if you want to disable the restriction.
+# You can add single ip adresses or ip ranges.
+# Examples:
+#    129.69.1.11
+#    10.0.10.0-10.0.10.255
+EOD
+    } else {
+      die "$0: unknown option -r $opt_r\n";
+    }
+    close $rf;
+  }
+  system $EDITOR,$rf;
+  unlink $rf if -s $rf<5;
+  exit;
+}
+
+# edit configuration
+if ($opt_c) {
+  exec $EDITOR,"$FEXLIB/fex.ph";
+}
+
+# add virtual server
+if ($opt_A) {
+  if ($opt_A =~ /(.+):(.+)/) { 
+    $vhost = $1;
+    $hhost = $2;
+  } else { 
+    die "usage: $0 -A alias:hostname\n".
+        "example: $0 -A flupp:fex.flupp.org\n";
+  }
+  if ($FHS) {
+    $vhd = "/var/lib/fex/vhosts/$vhost";
+    mkdir $vhd or die "$0: cannot mkdir $vhd - $!\n";
+    mkdir   "/etc/fex/vhosts/$vhost";
+    symlink "/etc/fex/vhosts/$vhost", "$vhd/lib";
+    mkdir   "$spooldir/vhosts/$vhost";
+    symlink "$spooldir/vhosts/$vhost","$vhd/spool";
+  } else {
+    $vhd = "$FEXHOME/$vhost";
+    mkdir $vhd or die "$0: cannot mkdir $vhd - $!\n";
+    mkdir "$vhd/lib";
+    mkdir "$vhd/spool";
+  }
+
+  mkdir "$vhd/htdocs";
+  mkdir "$vhd/htdocs/locale";
+  $_ = slurp("$FEXLIB/fex.ph");
+  s/\$hostname\s*=.*/\$hostname = '$hhost';/ or s/^/\$hostname = '$hhost';\n/;
+  $fph = "$vhd/lib/fex.ph";
+  open $fph,">$fph" or die "$0: cannot write to $fph - $!\n";
+  print {$fph} $_;
+  close $fph;
+  system "cp $FEXLIB/fup.pl $vhd/lib/fup.pl";
+  foreach $i (qw'dop fex.pp fup.pl lf.pl reactivation.txt') {
+    # symlink "$FEXLIB/$i","$vhd/lib/$i";
+    symlink "../../lib/$i","$vhd/lib/$i";
+  }
+  foreach $i (qw(
+    index.html tools.html SEX.html robots.txt
+    logo.jpg small_logo.jpg action-fex-camel.gif favicon.ico
+    FAQ
+  )) {
+    cpa("$docdir/$i","$vhd/htdocs");
+  }
+  symlink "$docdir/version","../../htdocs/version";
+  symlink "$docdir/download","../../htdocs/download";
+  cpa("$FEXHOME/locale",$vhd);
+  foreach $ld (glob "$vhd/locale/*") {
+    if (not -l $ld and -d "$ld/cgi-bin") {
+      $locale = basename($ld);
+      rmrf("$ld/cgi-bin");
+      # symlink "../../../locale/$locale/cgi-bin","$ld/cgi-bin";
+      symlink "../../../locale/$locale/htdocs","$vhd/htdocs/locale/$locale";
+      unlink "$ld/lib/fex.ph";
+      symlink "../../../lib/fex.ph","$ld/lib/fex.ph";
+      symlink "../../../../locale/$locale/lib","$ld/lib/master";
+      foreach $f (qw'dop fex.pp lf.pl reactivation.txt') {
+        unlink "$ld/lib/$f";
+        symlink "master/$f","$ld/lib/$f";
+      }
+    }
+  }
+  $fph = "$FEXLIB/fex.ph";
+  open $fph,">>$fph" or die "$0: cannot write to $fph = $!\n";
+  print {$fph} "\n\$vhost{'$hhost'} = '$vhd';\n";
+  close $fph;
+  print "You must now edit and configure $vhd/lib/fex.ph\n";
+  print "or execute: $0 -V $vhost -c\n";
+  exit;
+}
+
+# show config
+if ($opt_v) {
+  print  "config from $FEXLIB/fex.ph :\n";
+  print  "  spooldir        = $spooldir\n";
+  print  "  logdir          = $logdir\n";
+  print  "  docdir          = $docdir\n";
+  print  "  durl            = @durl\n";
+  print  "  admin           = $admin\n";
+  print  "  mdomain         = $mdomain\n";
+  print  "  mailmode        = $mailmode\n";
+  print  "  autodelete      = $autodelete\n";
+  print  "  keep_default    = $keep_default\n";
+  printf "  keep_max        = %s\n",$keep_max||'unlimited';
+  printf "  recipient_quota = %d GB\n",int($recipient_quota/1024);
+  printf "  sender_quota    = %d GB\n",int($sender_quota/1024);
+  while (($hh,$vh) = each %vhost) {
+    printf "  virtual server %s : %s\n",basename($vh),$hh;
+  }
+#  unless (@ARGV) {
+#    foreach $ph (glob "$ENV{HOME}/*/lib/fex.ph") {
+#      $ENV{FEXLIB} = dirname($ph);
+#      print "\n";
+#      system $0,'-v',$ph;
+#    }
+#  }
+  if ($m = readlink '@MAINTENANCE') {
+    print "server is in maintenance mode ($m)!\n" ;
+  }
+  exit;
+}
+
+# add user or show user config
+if ($opt_u) {
+  if ($opt_u = shift @ARGV) {
+    $user = lc $opt_u;
+    $user .= '@'.$mdomain if $mdomain and $user !~ /@/;
+    $id = shift @ARGV;
+    $idf = "$spooldir/$user/@";
+    if (open $idf,$idf) {
+      chomp($ido = <$idf>||'');
+      close $idf;
+    }
+    unless ($id) {
+      die "$0: $user is not a FEX user\n" unless -f "$spooldir/$user/@";
+      showuser($user,$ido);
+      exit;
+    }
+    unless ($user =~ /\w@[\w.-]+\.[a-z]+$/) {
+      die "$0: $user is not a valid email-address!\n";
+    }
+    unless (-d "$spooldir/$user") {
+      mkdir "$spooldir/$user",0755
+        or die "$0: cannot mkdir $spooldir/$user - $!\n";
+    }
+    open F,">$idf" or die "$0: cannot write $idf - $!\n";
+    print F $id,"\n";
+    close F or die "$0: cannot write $idf - $!\n";
+    showuser($user,$id);
+  } else {
+    print "Users in $spooldir:\n";
+    foreach $user (glob "$spooldir/*/@") {
+      $user =~ s:.*/(.+)/@:$1:;
+      print "$user\n";
+    }
+  }
+  exit;
+}
+
+# set user autodelete default
+if ($opt_a) {
+  $user = lc $opt_a;
+  $user .= '@'.$mdomain if $mdomain and $user !~ /@/;
+  $_ = shift @ARGV || '';
+  if    (/^n/i) { $autodelete = 'no' }
+  elsif (/^y/i) { $autodelete = 'yes' }
+  elsif (/^d/i) { $autodelete = 'delay' }
+  else { 
+    die "usage: $0 -a user yes\n".
+        "usage: $0 -a user no\n".
+        "usage: $0 -a user delay\n".
+        "example: $0 -a framstag\@rus.uni-stuttgart.de no\n";
+  }
+  mkdir "$spooldir/$user",0755;
+  my $adf = "$spooldir/$user/\@AUTODELETE";
+  unlink $adf;
+  symlink $autodelete,$adf or die "$0: cannot create symlink $adf - $!\n";
+  exit;
+}
+
+# set user notification default
+if ($opt_n) {
+  $user = lc $opt_n;
+  $user .= '@'.$mdomain if $mdomain and $user !~ /@/;
+  $_ = shift @ARGV || '';
+  if    (/^n/i)    { $notification = 'no' }
+  elsif (/^[sb]/i) { $notification = 'short' }
+  elsif (/^[fd]/i) { $notification = '' }
+  else { 
+    die "usage: $0 -n user no\n".
+        "usage: $0 -n user brief\n".
+        "usage: $0 -n user detailed\n".
+        "example: $0 -n framstag\@rus.uni-stuttgart.de brief\n";
+  }
+  mkdir "$spooldir/$user",0755;
+  my $ndf = "$spooldir/$user/\@NOTIFICATION";
+  unlink $ndf;
+  if ($notification) {
+    symlink $notification,$ndf or die "$0: cannot create symlink $ndf - $!\n";
+  }
+  exit;
+}
+
+# set user keep default
+if ($opt_k) {
+  $user = lc $opt_k;
+  $user .= '@'.$mdomain if $mdomain and $user !~ /@/;
+  my $keep = shift @ARGV || '';
+  if ($keep !~ /^\d+$/) {
+    die "usage: $0 -k user keep_days\n".
+        "example: $0 -k framstag\@rus.uni-stuttgart.de 30\n";
+  }
+  mkdir "$spooldir/$user",0755;
+  my $kf = "$spooldir/$user/\@KEEP";
+  unlink $kf;
+  symlink $keep,$kf or die "$0: cannot create symlink $kf - $!\n";
+  exit;
+}
+
+# quota
+if ($opt_q) {
+  $user = lc $opt_q;
+  $user .= '@'.$mdomain if $mdomain and $user !~ /@/;
+  unless (-d "$spooldir/$user") {
+    die "$0: $user is not a regular FEX user\n";
+  }
+  quota($user,@ARGV);
+  exit;
+}
+
+if ($opt_C) {
+  $user = lc $opt_C;
+  $user .= '@'.$mdomain if $mdomain and $user !~ /@/;
+  unless (-f "$spooldir/$user/@") {
+    die "$0: $user is not a regular FEX user\n";
+  }
+  $_ = shift @ARGV || '';
+  if (/^y/i) {
+    open $user,">>$spooldir/$user/\@CAPTIVE";
+    close $user;
+    print "$user is now captive\n";
+  } elsif (/^n/i) {
+    unlink "$spooldir/$user/\@CAPTIVE";
+    print "$user is no more captive\n";
+  } else {
+    die "usage: $0 -C user yes\n".
+        "usage: $0 -C user no\n".
+        "example: $0 -C framstag\@rus.uni-stuttgart.de no\n";
+  }
+  exit;
+}
+
+# FEXYOURSELF = user can only fex to himself via web interface
+if ($opt_y) {
+  $user = lc $opt_y;
+  $user .= '@'.$mdomain if $mdomain and $user !~ /@/;
+  unless (-f "$spooldir/$user/@") {
+    die "$0: $user is not a regular FEX user\n";
+  }
+  $_ = shift @ARGV || '';
+  if (/^y/i) {
+    open $user,">>$spooldir/$user/\@FEXYOURSELF";
+    close $user;
+    print "$user has now \"fex yourself\" web default\n";
+  } elsif (/^n/i) {
+    unlink "$spooldir/$user/\@FEXYOURSELF";
+    print "$user has no \"fex yourself\" web default\n";
+  } else {
+    die "usage: $0 -y user yes\n".
+        "usage: $0 -y user no\n".
+        "example: $0 -y framstag\@rus.uni-stuttgart.de no\n";
+  }
+  exit;
+}
+
+if ($opt_D) {
+  $user = lc $opt_D;
+  $user .= '@'.$mdomain if $mdomain and $user !~ /@/;
+  $_ = shift @ARGV || '';
+  if (/^y/i) {
+    open $user,">>$spooldir/$user/\@DISABLED";
+    close $user;
+    print "$user is now disabled\n";
+  } elsif (/^n/i) {
+    unlink "$spooldir/$user/\@DISABLED";
+    print "$user is now enabled\n";
+  } else {
+    die "usage: $0 -D user yes\n".
+        "usage: $0 -D user no\n".
+        "example: $0 -D framstag\@rus.uni-stuttgart.de no\n";
+  }
+  exit;
+}
+
+if ($opt_P) {
+  $user = lc $opt_P;
+  $user .= '@'.$mdomain if $mdomain and $user !~ /@/;
+  $_ = shift @ARGV || '';
+  if (/^y/i) {
+    open $user,">>$spooldir/$user/\@PERSISTENT";
+    close $user;
+    print "$user is now persistent\n";
+  } elsif (/^n/i) {
+    unlink "$spooldir/$user/\@PERSISTENT";
+    print "$user is no more persistent\n";
+  } else {
+    die "usage: $0 -P user yes\n".
+        "usage: $0 -P user no\n".
+        "example: $0 -P framstag\@rus.uni-stuttgart.de yes\n";
+  }
+  exit;
+}
+
+if ($opt_S eq 'fup') {
+  &fupstat;
+  exit;
+}
+
+if ($opt_S eq 'fop') {
+  &fopstat;
+  exit;
+}
+
+usage(3);
+
+sub showuser {
+  my $user = shift;
+  my $id = shift;
+  my ($keep,$autodelete,$notification);
+
+  $user .= '@'.$mdomain if $mdomain and $user !~ /@/;
+
+  print "[using config $FEXLIB/fex.ph]\n";
+  print "$fup?from=$user&id=$id\n";
+  printf "%s/%s\n",$fup,b64("from=$user&id=$id");
+  # printf "%s/%s\n",$fup,b64("from=$user&to=$user&id=$id&submit=.");
+  print "spool: $spooldir/$user/\n";
+  printf "fex yourself web default: %s\n",
+         -e "$spooldir/$user/\@FEXYOURSELF" ? 'yes' : 'no';
+  printf "persistent: %s\n",
+         -e "$spooldir/$user/\@PERSISTENT" ? 'yes' : 'no';
+  printf "captive: %s\n",
+         -e "$spooldir/$user/\@CAPTIVE" ? 'yes' : 'no';
+  printf "disabled: %s\n",
+         -e "$spooldir/$user/\@DISABLED" ? 'yes' : 'no';
+  printf "recipients restrictions: %s\n",
+         -e "$spooldir/$user/\@ALLOWED_RECIPIENTS" ? 'yes' : 'no';
+  printf "upload restrictions: %s\n",
+         -e "$spooldir/$user/\@UPLOAD_HOSTS" ? 'yes' : 'no';
+  printf "download restrictions: %s\n",
+         -e "$spooldir/$user/\@DOWNLOAD_HOSTS" ? 'yes' : 'no';
+  $autodelete = lc(readlink "$spooldir/$user/\@AUTODELETE" || $::autodelete);
+  print "autodelete default: $autodelete\n";
+  $notification = lc(readlink "$spooldir/$user/\@NOTIFICATION" || $::notification);
+  print "notification default: $notification\n";
+  $keep = readlink "$spooldir/$user/\@KEEP" || $keep_default;
+  print "keep default: $keep\n";
+  quota($user);
+  printf "account creation: %s\n",slurp("$spooldir/$user/.auto")||'manual';
+}
+
+# set or show disk quota
+sub quota {
+  my $user = shift;
+  my $rquota = '';
+  my $squota = '';
+  my $qf = "$spooldir/$user/\@QUOTA";
+  local $_;
+
+  if (open $qf,$qf) {
+    while (<$qf>) {
+      s/#.*//;
+      $rquota = $1 if /recipient.*?(\d+)/i;
+      $squota = $1 if /sender.*?(\d+)/i;
+    }
+    close $qf;
+  }
+
+  if (@_) {
+    for (@_) {
+      $rquota = $1 if /^r.*:(\d*)/i;
+      $squota = $1 if /^s.*:(\d*)/i;
+    }
+    open $qf,'>',$qf or die "$0: cannot write $qf - $!\n";
+    print {$qf} "recipient:$rquota\n" if $rquota =~ /\d/;
+    print {$qf} "sender:$squota\n"    if $squota =~ /\d/;
+    close $qf;
+  }
+
+  $rquota = $recipient_quota if $rquota !~ /\d/;
+  $squota = $sender_quota    if $squota !~ /\d/;
+  printf "recpient quota (used): %d (%d) MB\n",
+         check_recipient_quota($user) if $rquota;
+  printf "sender quota (used): %d (%d) MB\n",
+         check_sender_quota($user) if $squota;
+}
+
+
+sub fupstat {
+  my (%user,%domain,%du);
+  my ($log,$u,$d,$z);
+  my $Z = 0;
+
+  if (-t) { $log = "$logdir/fup.log" }
+  else    { $log = '>&=STDIN' }
+  open $log,$log or die "$0: cannot open $log - $!\n";
+
+  while (<$log>) {
+    if (/^([\d: -]+) (\[[\d_]+\] )?(\w\S*) .* (\d+)$/) {
+      $z = $4;
+      $u = $3;
+      $u .= '@'.$mdomain if $mdomain and $u !~ /@/;
+      $user{$u} += $z;
+      $d = $u;
+      $d =~ s/.*@//;
+      $d =~ s/.*\.(.+\.\w+)/$1/;
+      $domain{$d} += $z;
+      $du{$d}{$u}++;
+      $Z += $z;
+    }
+  }
+
+  foreach $u (sort {$user{$a} <=> $user{$b}} keys %user) {
+    printf "%s : %d\n",$u,$user{$u}/M;
+  }
+  print "========================================================\n";
+  foreach $d (sort {$domain{$a} <=> $domain{$b}} keys %domain) {
+    printf "%s : %d MB, %d user\n",$d,$domain{$d}/M,scalar(keys %{$du{$d}});
+  }
+  printf "Total: %d GB\n",$Z/M/1024;
+
+  exit;
+}
+
+
+sub fopstat {
+  my $Z = 0;
+  my ($log,$u,$d,$z);
+  my (%user,%domain,%du);
+
+  if (-t) { $log = "$logdir/fop.log" }
+  else    { $log = '>&=STDIN' }
+  open $log,$log or die "$0: cannot open $log - $!\n";
+
+  while (<$log>) {
+    if (/^([\d: -]+) (\[[\d_]+\] )?[\d.]+ (.+?)\/.* (\d+)\/\d+/) {
+      $z = $4;
+      $u = $3;
+      $u .= '@'.$mdomain if $mdomain and $u !~ /@/;
+      $user{$u} += $z;
+      $d = $u;
+      $d =~ s/.*@//;
+      $d =~ s/.*\.(.+\.\w+)/$1/;
+      $domain{$d} += $z;
+      $du{$d}{$u}++;
+      $Z += $z;
+    }
+  }
+
+  foreach $u (sort {$user{$a} <=> $user{$b}} keys %user) {
+    printf "%s : %d\n",$u,$user{$u}/M;
+  }
+  print "========================================================\n";
+  foreach $d (sort {$domain{$a} <=> $domain{$b}} keys %domain) {
+    printf "%s : %d MB, %d user\n",$d,$domain{$d}/M,scalar(keys %{$du{$d}});
+  }
+  printf "Total: %d GB\n",$Z/M/1024;
+
+  exit;
+}
+
+
+sub cpa {
+  my $dd = pop @_;
+
+  die "(cpa): $dd is not a directory" unless -d $dd;
+  system "rsync -a @_ $dd/" ;
+}
+
+
+sub mtime {
+  my @s = lstat shift;
+  return @s ? $s[9] : undef;
+}
+
+sub check_admin {
+  
+  my $admin_id = slurp("$spooldir/$admin/@") or
+    die "$0: no admin account - you have to create it with:\n".
+        "$0 -/ $admin ".randstring(8)."\n";
+
+  chomp $admin_id;
+
+  my $fid = "$ENV{HOME}/.fex/id";
+  if (open $fid,$fid) {
+    $_ = <$fid>;
+    chomp($_ = <$fid>||'');
+    if ($_ ne $admin) {
+      warn "WARNING: user $admin not in $fid\n";
+      $mismatch++;
+    }
+    chomp($_ = <$fid>||'');
+    if ($_ ne $admin_id) {
+      warn "WARNING: $admin auth-ID mismatch in $fid\n";
+      $mismatch++;
+    }
+    close $fid;
+    if ($mismatch) {
+      warn "$0: moving $fid to ${fid}_save\n";
+      rename $fid,$fid.'_save';
+    }
+  } 
+  unless (-f $fid) {
+    mkdir dirname($fid);
+    open $fid,'>',$fid or die "$0: cannot create $fid - $!\n";
+    if ($durl =~ m{(https?://.+?)/}) {
+      print {$fid} "$1\n";
+    } else {
+      print {$fid} "$hostname\n";
+    }
+    print {$fid} "$admin\n";
+    print {$fid} "$admin_id\n";
+    close $fid;
+    warn "$0: new $fid created\n";
+  }
+}
+
+
+sub d3 {
+  local $_ = shift;
+  while (s/(\d)(\d\d\d\b)/$1,$2/) {};
+  return $_;
+}
+
+
+sub usage {
+  my $port = '';
+  my $proto = 'http';
+
+  if ($durl =~ /:(\d+)/)    { $port = ":$1" }
+  if ($durl =~ /^(https?)/) { $proto = $1 }
+
+  $0 =~ s:.*/::;
+  print <<EOD;
+Usages:
+$0 -u                 # list full users
+$0 -u user            # show user config
+$0 -u user auth-ID    # create new user or set new auth-ID
+$0 -/ admin auth-ID   # set new admin and auth-ID
+$0 -q user s:quota    # set new disk quota (MB) for sender user
+$0 -q user r:quota    # set new disk quota (MB) for recipient user
+$0 -R user            # restrict user: only internal recipients allowed
+$0 -rr user           # edit user recipients restriction
+$0 -ru user           # edit user upload restriction
+$0 -rd user           # edit user download restriction
+$0 -d user            # delete user
+$0 -D user [yn]       # disable user (yes,no)
+$0 -P user [yn]       # make user persistent = no account expiration (yes,no)
+$0 -a user [ynd]      # set user autodelete default (yes,no,delay)
+$0 -n user [dbn]      # set user notification default (detailed,brief,no)
+$0 -k user days       # set user keep default in days
+$0 -C user [yn]       # set user captive (yes,no)
+$0 -y user [yn]       # set user "fex yourself" web default (yes,no)
+$0 -S fup             # file upload statistics
+$0 -S fop             # file download statistics
+$0 -v                 # show server config
+$0 -c                 # edit server config
+$0 -w                 # watch fexsrv.log (continously)
+$0 -l                 # list pending files with download URLs
+$0 -L [filter]        # list pending files in detail
+$0 -M                 # list pending files with TO/FROM/FILE
+$0 -M TO/FROM/FILE    # resend notification email
+$0 -m "reason"        # enter maintenance mode (reason "exit" to leave)
+$0 -A alias:hostname  # add new virtual server
+$0 -V virtualhost ... # operations on virtualhost (alias or hostname)
+$0 -E                 # show usage examples
+EOD
+  if (-x "$FEXHOME/cgi-bin/fac") {
+    print "See also web admin interface $proto://$hostname$port/fac\n";
+  }
+  exit shift;
+}
+
+sub examples {
+  $0 =~ s:.*/::;
+  print <<EOD;
+create new user:
+$0 -u framstag\@rus.uni-stuttgart.de schwubbeldidu
+
+set 10 GB sender quota for this user:
+$0 -q framstag\@rus.uni-stuttgart.de s:10240
+
+set file expiration to 30 days for this user:
+$0 -k framstag\@rus.uni-stuttgart.de 30
+
+disable account expiration for this user:
+$0 -P framstag\@rus.uni-stuttgart.de y
+
+list spooled files and resend notification email for this file:
+$0 -M | grep frams
+$0 -M framstag\@rus.uni-stuttgart.de/hoppel\@flupp.org/jump.avi
+EOD
+  exit;
+}
diff --git a/bin/fbm b/bin/fbm
new file mode 100755 (executable)
index 0000000..b33fd6f
--- /dev/null
+++ b/bin/fbm
@@ -0,0 +1,469 @@
+#!/usr/bin/perl -w
+
+# F*EX benchmark
+#
+# Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
+#
+# Copyright: Perl Artistic
+
+use 5.006;
+use strict qw'vars subs';
+use Config;
+use Socket;
+use IO::Handle;
+use IO::Socket::INET;
+use Getopt::Std;
+use Time::HiRes qw'time';
+# use Smart::Comments;
+use constant k => 2**10;
+use constant M => 2**20;
+
+our ($SH,$windoof,$sigpipe,$useragent);
+our ($FEXSERVER);
+our $version = 20150120;
+
+# server defaults
+my $server = 'fex.rus.uni-stuttgart.de';
+my $port = 80;
+my $proxy = '';
+
+my $from = 'nettest';
+my $to = $from;
+my $id = $from;
+my $proxy_prefix = '';
+my $mb;
+my (@r,$r);
+my $bs = 2**16;
+my $timeout = 30;      # server timeout
+
+$version ||= mtime($0);
+$0 =~ s:.*/::;
+
+my $usage = <<EOD;
+usage: $0 [-n] [-s server] [-P proxy] #MB
+options: -n  do not store on server
+         -s  use alternative F*EX server:port
+         -P  use proxy server:port
+examples: $0 1000
+EOD
+  
+if ($Config{osname} =~ /^mswin/i) {
+  $windoof = $Config{osname};
+  $useragent = sprintf("fbm-$version (%s %s)",
+                       $Config{osname},$Config{archname});
+} else {
+  $0 =~ s:.*/::;
+  $_ = `(lsb_release -d||uname -a)2>/dev/null`||'';
+  chomp;
+  s/^Description:\s+//;
+  $useragent = "fbm-$version ($_)";
+}
+
+$| = 1;
+
+autoflush STDERR;
+
+my @_ARGV = @ARGV; # save arguments
+
+our $opt_n = 0;
+our $opt_v = 0;
+our $opt_h = 0;
+our $opt_s = '';
+our $opt_P = '';
+
+getopts('hvnP:s:') or die $usage;
+
+if ($opt_h) {
+  print $usage;
+  exit;
+}
+
+if ($opt_P) { 
+  if ($opt_P =~ /^[\w.-]+:\d+/) {
+    $proxy = $opt_P;
+  } else {
+    die "$0: proxy must be: SERVER:PORT\n";
+  }
+}
+
+$mb = shift    or die $usage;
+$mb =~ /^\d+$/ or die $usage;
+
+# $port = $1  if $server =~ s/:(\d+)//;
+
+if ($opt_s) {
+  ($server,$port) = split /:/,$opt_s;
+  $port = 80 unless $port;
+}
+$server =~ s{http://}{};
+$server =~ s{/.*}{};
+
+if ($proxy) {
+  if ($port == 80)  { $proxy_prefix = "http://$server" }
+  else              { $proxy_prefix = "http://$server:$port" }
+}
+
+print "Testing $server:\n";
+
+@r = formdatapost(
+  from         => $from,
+  to           => $to,
+  id           => $id,
+  comment      => $opt_n ? 'NOSTORE' : 'NOMAIL',
+  keep         => 1,
+  autodelete   => 'YES', 
+);
+  
+if (not @r or not grep /\w/,@r) {
+  die "$0: no response from server\n";
+}
+
+if (($r) = grep /ERROR:/,@r) {
+  $r =~ s/.*?:\s*//;
+  $r =~ s/<.*//;
+  die "$0: server error: $r\n";
+}
+
+if (($r) = grep /^Location: http/,@r) {
+  $r =~ s:.*(/fop/\w+/.+$):$1:;
+  download($r);
+} else {
+  download("/ddd/$mb");
+}
+  
+exit;
+
+
+sub formdatapost {
+  my %P = @_; 
+  my ($boundary,$filename,$filesize,$length);
+  my (@hh,@hb,@r,@pv);
+  my ($t,$bt,$t0,$t1,$t2,$tt);
+  my $buf = '#' x $bs;
+  local $_;
+
+
+  @hh = (); # HTTP header
+  @hb = (); # HTTP body
+  @r = ();
+
+  serverconnect($server,$port);
+    
+  $boundary = randstring(48);
+  $P{command} = 'CHECKRECIPIENT';
+  
+  # HTTP POST variables
+  @pv = qw'from to id command';
+  foreach my $v (@pv) {
+    if ($P{$v}) {
+      my $name = uc($v);
+      push @hb,"--$boundary";
+      push @hb,"Content-Disposition: form-data; name=\"$name\"";
+      push @hb,"";
+      push @hb,$P{$v};
+    }
+  }
+  push @hb,"--$boundary--";
+
+  $length = length(join('',@hb)) + scalar(@hb)*2 + $mb*M;
+
+  # HTTP header
+  push @hh,"POST $proxy_prefix/fup HTTP/1.1";
+  push @hh,"Host: $server:$port";
+  push @hh,"User-Agent: $useragent";
+  push @hh,"Content-Length: $length";
+  push @hh,"Content-Type: multipart/form-data; boundary=$boundary";
+  push @hh,"Connection: close";
+  push @hh,'';
+
+  if ($opt_v) {
+    printf "--> $_\n" foreach (@hh,@hb);
+  }
+
+  nvtsend(@hh,@hb) or die "$0: server has closed the connection\n";
+
+  while (<$SH>) {
+    s/[\r\n]+//;
+    print "<-- $_\n" if $opt_v;
+    push @r,$_;
+    last if /^$/;
+  }
+
+  unless (@r and $r[0] =~ / 204 /) {
+    $_ = $r[0] || '';
+    s/^HTTP.[.\d\s]+//;
+    die "$0: server error: $_\n";
+  }
+
+  @hh = (); # HTTP header
+  @hb = (); # HTTP body
+  @r = ();
+  $filename = 'test_'.int(time*1000);
+
+  serverconnect($server,$port);
+
+  # HTTP POST variables
+  @pv = qw'from to id keep autodelete comment filesize';
+  foreach my $v (@pv) {
+    if ($P{$v}) {
+      my $name = uc($v);
+      push @hb,"--$boundary";
+      push @hb,"Content-Disposition: form-data; name=\"$name\"";
+      push @hb,"";
+      push @hb,$P{$v};
+    }
+  }
+  
+  # at last, the file
+  push @hb,"--$boundary";
+  push @hb,"Content-Disposition: form-data; name=\"FILE\"; filename=\"$filename\"";
+  push @hb,"Content-Type: application/octet-stream";
+  push @hb,"";
+  push @hb,"";
+  push @hb,"--$boundary--";
+
+  $length = length(join('',@hb)) + scalar(@hb)*2 + $mb*M;
+
+  $hb[-2] = '(file content)';
+
+  # HTTP header
+  push @hh,"POST $proxy_prefix/fup HTTP/1.1";
+  push @hh,"Host: $server:$port";
+  push @hh,"User-Agent: $useragent";
+  push @hh,"Content-Length: $length";
+  push @hh,"Content-Type: multipart/form-data; boundary=$boundary";
+  push @hh,"Connection: close";
+  push @hh,'';
+
+  if ($opt_v) {
+    printf "--> $_\n" foreach (@hh,@hb);
+  }
+
+  pop @hb;
+  pop @hb;
+  nvtsend(@hh,@hb) or die "$0: server has closed the connection\n";
+      
+  $t0 = $t2 = int(time);
+  $t1 = 0;
+      
+  autoflush $SH 0;
+      
+  for (;;) {
+    print {$SH} $buf or die "$0: server has closed the connection\n";
+    $b += $bs;
+    $bt += $bs;
+    $t2 = time;
+    if (-t STDOUT and $t2-$t1>1) {
+      # smaller block size is better on slow links
+      if ($t1 and $bs>4096 and $bt/($t2-$t0)<65536) {
+        $bs = 4096;
+        $buf = '#' x $bs;
+      }
+      if ($bs>4096) {
+        printf STDERR "upload: %s MB of %d MB, %d kB/s        \r",
+          int($bt/M),
+          $mb,
+          int($b/k/($t2-$t1));
+      } else {
+        printf STDERR "upload: %s kB of %d MB, %d kB/s        \r",
+          int($bt/k),
+          $mb,
+          int($b/k/($t2-$t1));
+      }
+      $t1 = $t2;
+      $b = 0;
+    }
+    last if $bt >= $mb*M;
+  }
+  
+  autoflush $SH 1;
+  print {$SH} "\r\n--$boundary--\r\n";
+
+  while (<$SH>) {
+    s/[\r\n]+//;
+    print "<-- $_\n" if $opt_v;
+    last if @r and $r[0] =~ / 204 / and /^$/ or /<\/html>/i;
+    push @r,$_;
+  }
+  
+  $tt = (time-$t0)||1;
+  printf STDERR "upload: %d MB in %d s, %d kB/s        \n",
+                int($bt/M),$tt,int($bt/k/$tt);
+      
+  close $SH;
+  undef $SH;
+  
+  return @r;
+}
+
+
+sub randstring {
+    my $n = shift;
+    my @rc = ('A'..'Z','a'..'z',0..9 );
+    my $rn = @rc;
+    my $rs;
+
+    for (1..$n) { $rs .= $rc[int(rand($rn))] };
+    return $rs;
+}
+
+
+sub serverconnect {
+  my ($server,$port) = @_;
+  my $connect = "CONNECT $server:$port HTTP/1.1";
+  local $_;
+  
+  if ($proxy) {
+    tcpconnect(split(':',$proxy));
+    if ($port == 443) {
+      printf "--> %s\n",$connect if $opt_v;
+      nvtsend($connect,"");
+      $_ = <$SH>;
+      s/\r//;
+      printf "<-- $_"if $opt_v;
+      unless (/^HTTP.1.. 200/) {
+        die "$0: proxy error : $_";
+      }
+      eval "use IO::Socket::SSL";
+      die "$0: cannot load IO::Socket::SSL\n" if $@;
+      $SH = IO::Socket::SSL->start_SSL($SH);
+    }
+  } else {
+    tcpconnect($server,$port);
+  }
+}
+
+
+# set up tcp/ip connection
+sub tcpconnect {
+  my ($server,$port) = @_;
+  
+  if ($SH) {
+    close $SH;
+    undef $SH;
+  }
+  
+  if ($port == 443) {
+    eval "use IO::Socket::SSL";
+    die "$0: cannot load IO::Socket::SSL\n" if $@;
+    $SH = IO::Socket::SSL->new(
+      PeerAddr => $server,
+      PeerPort => $port,
+      Proto    => 'tcp',
+    );
+  } else {
+    $SH = IO::Socket::INET->new(
+      PeerAddr => $server,
+      PeerPort => $port,
+      Proto    => 'tcp',
+    );
+  }
+  
+  if ($SH) {
+    autoflush $SH 1;
+  } else {
+    die "$0: cannot connect $server:$port - $@\n";
+  }
+  
+  print "TCPCONNECT to $server:$port\n" if $opt_v;
+}
+
+
+sub nvtsend {
+  local $SIG{PIPE} = sub { $sigpipe = "@_" };
+  
+  $sigpipe = '';
+  
+  die "$0: internal error: no active network handle\n" unless $SH;
+  die "$0: remote host has closed the link\n" unless $SH->connected;
+  
+  foreach my $line (@_) {
+    print {$SH} $line,"\r\n";
+    if ($sigpipe) {
+      undef $SH;
+      return 0;
+    }
+  }
+  
+  return 1;
+}
+
+
+sub mtime {
+  my @d = localtime((stat shift)[9]);
+  return sprintf('%d%02d%02d',$d[5]+1900,$d[4]+1,$d[3]);
+}
+
+
+sub download {
+  my $fop = shift;
+  my ($file);
+  my ($t0,$t1,$t2,$tt,$kBs,$b,$bt,$tb,$B,$buf);
+  my $length = 0;
+  local $_;
+
+  serverconnect($server,$port);
+  
+  sendheader(
+    "GET $proxy_prefix$fop HTTP/1.1",
+    "User-Agent: $useragent",
+    "Host: $server:$port",
+  );
+
+  $_ = <$SH>;
+  die "$0: no response from fex server $server\n" unless $_;
+  s/\r//;
+
+  if (/^HTTP\/[\d.]+ 2/) {
+    warn "<-- $_" if $opt_v;
+    while (<$SH>) {
+      s/\r//;
+      print "<-- $_" if $opt_v;
+      last if /^\r?\n/;
+      if (/^Content-length:\s*(\d+)/i) {
+        $length = $1;
+      }
+    }
+  } else {
+    s/HTTP\/[\d.]+ \d+ //;
+    die "$0: bad server reply: $_";
+  }
+
+  $t0 = $t1 = $t2 = int(time);
+  $tb = $B = 0;
+  while ($B < $length and $b = read $SH,$buf,$bs) {
+    $B += $b;
+    $tb += $b;
+    $bt += $b;
+    $t2 = time;
+    if (int($t2) > $t1) {
+      $kBs = int($bt/k/($t2-$t1));
+      $kBs = int($tb/k/($t2-$t0)) if $kBs < 10;
+      $t1 = $t2;
+      $bt = 0;
+      # smaller block size is better on slow links
+      $bs = 4096 if $bs>4096 and $tb/($t2-$t0)<65536;
+      printf STDERR "download: %d MB in %d s, %d kB/s        \r",
+                    int($tb/M),$t2-$t0,$kBs;
+    }
+  }
+  close $SH;
+
+  $tt = $t2-$t0;
+  $kBs = int($tb/k/($tt||1));
+  printf STDERR "download: %d MB in %d s, %d kB/s        \n",
+                int($tb/M),$tt,$kBs;
+}
+
+
+sub sendheader {
+  my @head = @_;
+  my $head;
+  
+  foreach $head (@head) {
+    print "--> $head\n" if $opt_v;
+    print {$SH} $head,"\r\n";
+  }
+  print "-->\n" if $opt_v;
+  print {$SH} "\r\n";
+}
diff --git a/bin/fex_cleanup b/bin/fex_cleanup
new file mode 100755 (executable)
index 0000000..c54b2e0
--- /dev/null
@@ -0,0 +1,587 @@
+#!/usr/bin/perl -w 
+
+# cleanup for F*EX service
+#
+# run this program via cron-job once at night!
+#
+# Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
+#
+
+use Getopt::Std;
+use File::Basename;
+use IO::Socket::INET;
+use Cwd        'abs_path';
+use Digest::MD5        'md5_hex';
+
+use constant DS => 60*60*24;
+  
+# do not run as CGI!
+exit if $ENV{SCRIPT_NAME};
+
+unless ($FEXLIB = $ENV{FEXLIB}) {
+  if ($ENV{FEXHOME}) {
+    $FEXLIB = $ENV{FEXHOME}.'/lib';
+  } elsif (-f '/usr/share/fex/lib/fex.ph') {
+    $FEXLIB = '/usr/share/fex/lib';
+  } else {
+    $FEXLIB = dirname(dirname(abs_path($0))).'/lib';
+  }
+  $ENV{FEXLIB} = $FEXLIB;
+}
+die "$0: no FEXLIB\n" unless -r "$FEXLIB/fex.pp";
+
+# program name
+$_0 = $0;
+$0 =~ s:.*/::;
+
+$| = 1;
+
+# use fex.ph for site configuration!
+our ($FEXHOME);
+our ($spooldir,$logdir,$docdir);
+our ($akeydir,$ukeydir,$dkeydir,$skeydir,$gkeydir,$xkeydir,$lockdir);
+our ($durl,$debug,$autodelete,$hostname,$admin,$admin_pw,$bcc);
+$keep_default = 5;
+
+# load common code, local config : $HOME/lib/fex.ph
+require "$FEXLIB/fex.pp" or die "$0: cannot load $FEXLIB/fex.pp - $!\n";
+
+# localized functions 
+# (needed for reminder and account reactivation e-mails)
+foreach my $lf (glob "$FEXHOME/locale/*/lib/lf.pl") { require $lf }
+
+# default locale functions (from fex.pp)
+$notify{english} = \&notify;
+$reactivation{english} = \&reactivation;
+
+@_ARGV = @ARGV;
+
+$opt_v = $opt_V = $opt_d = 0;
+getopts('vVd');
+$opt_v = $opt_d if $opt_d;  # debug mode, no real action
+
+$today = time;
+$isodate = isodate($today);
+
+chdir $spooldir or die "$0: $spooldir - $!\n";
+open L,">>$logdir/cleanup.log";
+
+# clean up regular spool
+opendir $spooldir,'.' or die "$0: $spooldir - $!\n";
+while ($to = readdir $spooldir) {
+  next if $to !~ /@/ or -l $to;
+  if (@demo and -f "$to/.demo" and time > mtime("$to/.demo")+$demo[1]*DS) {
+    logdel($to,"demo user $to deleted");
+    next;
+  }
+  if (-d $to and $to !~ /^\./) {
+    unless (opendir TO,$to) {
+      warn "$0: $spooldir/$to - $!\n";
+      next;
+    }
+    while ($from = readdir TO) {
+      next if $from !~ /@/;
+      if ($from eq '@GROUP') {
+        foreach $group (glob "$to/$from/*") {
+          if (readlink $group and not -f $group) {
+            logdel($group,"$group deleted (master has gone)");
+          }
+        }
+      } else {
+        if (-d "$to/$from" and $from !~ /^\./) {
+          unless (opendir FROM,"$to/$from") {
+            warn "$0: $spooldir/$to/$from - $!\n";
+            next;
+          }
+          while ($file = readdir FROM) {
+            next if $file eq '.' or $file eq '..';
+            if (-d "$to/$from/$file" and $file !~ /^\./) {
+              cleanup($to,$from,$file);
+              rmdir "$to/$from/$file" unless $opt_d;
+            }
+          }
+          closedir FROM;
+          rmdir "$to/$from" unless $opt_d;
+        }
+      }
+    }
+    closedir TO;
+    unless (-f "$to/\@PERSISTENT" or $to eq $admin) {
+      @glob = glob "$to/*/* $to/\@MAINUSER/* $to/\@GROUP/*";
+      unless (@glob or -f "$to/\@") {
+        logdel($to,"$to deleted");
+      }
+      $user = $to;
+      if ($login_check and -l "$user/.login") {
+        my $lc = &$login_check(readlink("$user/.login"));
+        if ($lc) {
+          if (-f "$user/\@~" and not "$user/@") {
+            rename "$user/\@~","$user/@" unless $opt_d;
+            logv("$isodate $user reanimated (login_check)");
+          }
+        } else {
+          rename "$user/@","$user/\@~" unless $opt_d;
+          logv("$user deactivated (login_check)");
+        }
+      }
+    }
+  }
+}
+closedir $spooldir;
+
+# clean up download key lookup directory
+if (chdir $dkeydir and opendir D,'.') {
+  while ($file = readdir D) {
+    if ($link = readlink $file and 
+        (not -l "$link/dkey" or readlink "$link/dkey" ne $file)) {
+      logdel($file,".dkeys/$file deleted");
+    }
+  }
+  closedir D;
+}
+
+# clean up upload key lookup directory
+if (chdir $ukeydir and opendir D,'.') {
+  while ($file = readdir D) {
+    next if $file eq '.' or $file eq '..';
+    if (($link = readlink $file and not -e "$link/upload"
+         or -f $file and time > mtime($file)+DS)) {
+      logdel($file,".ukeys/$file deleted");
+    }
+  }
+  closedir D;
+}
+
+# clean up authorization key lookup directory
+if (chdir $akeydir and opendir D,'.') {
+  while ($file = readdir D) {
+    if (-l $file and time > mtime($file)+DS) {
+      logdel($file,".akeys/$file deleted");
+    }
+  }
+  closedir D;
+}
+
+# clean up extra download key lookup directory
+if (chdir $xkeydir and opendir D,'.') {
+  while ($file = readdir D) {
+    next if $file eq '.' or $file eq '..';
+    if (-l $file and not (-f "$file/upload" or -f "$file/data")) {
+      logdel($file,".xkeys/$file deleted");
+    }
+  }
+  closedir D;
+}
+
+# clean up lock directory
+if (chdir $lockdir and opendir D,'.') {
+  while ($file = readdir D) {
+    if (-f $file and time > mtime($file)+DS) {
+      logdel($file,".locks/$file deleted");
+    }
+  }
+  closedir D;
+}
+
+# clean up error directory
+if (chdir "$spooldir/.error" and opendir D,'.') {
+  while ($file = readdir D) {
+    if (-f $file) {
+      $mtime = mtime($file);
+      if ($mtime and $today > 10*$keep_default*DS+$mtime) {
+        if ($opt_d) { print "unlink .error/$file\n" }
+        else        { logdel($file,".error/$file deleted") }
+      }
+    }
+  }
+  closedir D;
+}
+
+# clean up debug directory
+if (chdir "$logdir/.debug" and opendir D,'.') {
+  while ($file = readdir D) {
+    if (-f $file) {
+      $mtime = mtime($file);
+      if ($mtime and $today > $keep_default*DS+$mtime) {
+        # logdel($file,".debug/$file deleted");
+        if ($opt_d) { print "unlink .debug/$file\n" }
+        else        { unlink $file }
+      }
+    }
+  }
+  closedir D;
+}
+
+# clean up subuser keys directory
+if (chdir $skeydir and opendir D,'.') {
+  while ($file = readdir D) {
+    if (-f $file and open F,$file) {
+      $delete = 1;
+      $from = $to = $id = '';
+      while (<F>) {
+        if (/^(\w+)=(.+)/) {
+          $from = $2 if $1 eq 'from';
+          $to   = $2 if $1 eq 'to';
+          $id   = $2 if $1 eq 'id';
+        }
+      }
+      close F;
+      if ($from and $to and $id and open F,"$spooldir/$to/\@SUBUSER") {
+        while (<F>) {
+          if (/^\Q$from:$id\E$/) {
+            $delete = 0;
+            last;
+          }
+        }
+        close F;
+      }
+      if ($delete) {
+        logdel($file,".skeys/$file deleted");
+      }
+    }
+  }
+  closedir D;
+}
+
+# clean up orphan subuser links
+chdir $spooldir;
+foreach $subuser (glob '*/@MAINUSER/*') {
+  if ($skey = readlink $subuser and not -f "$skeydir/$skey") {
+    logdel($subuser,"$subuser deleted");
+  }
+}
+foreach $subuser (glob '*/@MAINUSER') {
+  unlink $subuser unless $opt_d;
+}
+
+# clean up old OKEYs
+chdir $spooldir;
+foreach my $okey (glob '*/@OKEY/*') {
+  if (time > mtime($okey)+30*DS) {
+    logdel($okey,"$okey deleted");
+  }
+}
+
+
+# clean up group keys directory
+if (chdir $gkeydir and opendir D,'.') {
+  while ($gkey = readdir D) {
+    if (-f $gkey and open F,$gkey) {
+      $delete = 1;
+      $from = $group = $id = '';
+      while (<F>) {
+        if (/^(\w+)=(.+)/) {
+          $from  = $2 if $1 eq 'from';
+          $group = $2 if $1 eq 'to';
+          $id    = $2 if $1 eq 'id';
+        }
+      }
+      close F;
+      $group =~ s/^@//;
+      $gf = "$spooldir/$from/\@GROUP/$group";
+      if ($from and $group and $id and open F,$gf) {
+        while (<F>) {
+          if (/^\Q$from:$id\E$/) {
+            $delete = 0;
+            last;
+          }
+        }
+        close F;
+      }
+      if ($delete) {
+        logdel($gkey,".gkeys/$gkey deleted");
+        logdel($gf,"$gf deleted") if -l $gf;
+      }
+    }
+  }
+  closedir D;
+}
+
+# clean up self registration directory
+if (chdir "$spooldir/.reg" and opendir D,'.') {
+  while ($file = readdir D) {
+    if (-f $file) {
+      $mtime = mtime($file);
+      if ($mtime and $today > $mtime+DS) {
+        logdel($file,".reg/$file deleted");
+      }
+    }
+  }
+  closedir D;
+}
+
+# send account expiration warning 
+if ($account_expire and $account_expire =~ /^(\d+)/) {
+  my $expire = $1;
+  if (chdir $spooldir) {
+    chomp($admin_pw = slurp("$admin/\@")||'');
+    unless ($admin_pw) {
+      warn "create new fex account for $admin\n";
+      $admin_pw = randstring(8);
+      system("$FEXHOME/bin/fac -u $admin $admin_pw");
+    }
+    my $fid = "$FEXHOME/.fex/id";
+    unless (-f $fid) {
+      mkdir "$FEXHOME/.fex",0700;
+      if (open $fid,'>',$fid) {
+        if ($durl =~ m{(https?://.+?)/}) {
+          print {$fid} "$1\n";
+        } else {
+          print {$fid} "$hostname\n";
+        }
+        print {$fid} "$admin\n";
+        print {$fid} "$admin_pw\n";
+        close $fid;
+      } else {
+        warn"$0: cannot create $fid - $!";
+      }
+    }
+    chmod 0600,$fid;
+    opendir $spooldir,'.';
+    while ($user = readdir $spooldir) {
+      next unless -f "$user/\@";
+      next if -e "$user/$admin/reactivation.txt";
+      next if -e "$user/\@PERSISTENT";
+      next if $user !~ /@/ or -l $user;
+      next if $user =~ /^(fexmaster|fexmail)/ or $user eq $admin;
+      next if -l "$user/.login";
+
+      if (time > mtime($user)+$expire*DS) {
+        # print "$spooldir/$user\n";
+        my $locale = readlink "$user/\@LOCALE";
+        $locale = 'english' unless $locale and $reactivation{$locale};
+        &{$reactivation{$locale}}($expire,$user);
+        sleep 1;
+      }
+    }
+    closedir $spooldir;
+  }
+}
+
+close L;
+
+# vhosts
+exit if $opt_V;
+if (%vhost) {
+  foreach $vhost (keys %vhost) {
+    my $fexlib = $vhost{$vhost}.'/lib';
+    if (-f "$fexlib/fex.ph") {
+      warn "run $0 for $vhost :\n" if -t or $opt_v;
+      my $cmd = "HTTP_HOST=$vhost FEXLIB=$fexlib $_0 -V @_ARGV";
+      if ($opt_d) { print "$cmd\n" }
+      else        { system $cmd }
+    }
+  }
+}
+
+if ($notify_newrelease or not defined $notify_newrelease) {
+  $notify_newrelease ||= $admin;
+  $newnew = $new = '';
+  $snew = $FEXHOME.'/doc/new';
+  $new = slurp($snew)||'';
+  $_ = slurp("$FEXHOME/doc/version")||'';
+  if (/(\d+)/) { $qn = "new?$hostname:$1" }
+  else         { $qn = "new?$hostname:0" }
+  for (1..3) {
+    sleep rand(10);
+    $newnew = `wget -qO- http://fex.rus.uni-stuttgart.de/$qn 2>/dev/null`;
+    last if $newnew =~ /release/;
+    $newnew = `wget -qO- http://fex.belwue.de/$qn 2>/dev/null`;
+    last if $newnew =~ /release/;
+  };
+  if ($newnew =~ /release/) {
+    if ($newnew ne $new) {
+      if (open $sendmail,"|$sendmail $notify_newrelease $bcc") {
+        pq($sendmail,qq(
+          'From: fex\@$hostname'
+          'To: $notify_newrelease'
+          'Subject: new F*EX release'
+          ''
+          '$newnew'
+        ));
+        close $sendmail;
+        if (open $snew,'>',$snew) {
+          print {$snew} $newnew;
+          close $snew;
+        }
+      }
+    }
+  }
+}
+
+exit;
+
+
+# file clean up
+sub cleanup {
+  my ($to,$from,$file) = @_;
+  my ($data,$download,$notify,$mtime,$warn,$dir,$filename,$dkey,$delay);
+  my $comment = '';
+  my $keep = $keep_default;
+  my $kf = "$to/$from/$file/keep";
+  my $ef = "$to/$from/$file/error";
+  local $_;
+  
+  $keep = readlink $kf || readlink "$to/\@KEEP" || $keep_default;
+
+  $file       = "$to/$from/$file";
+  $data       = "$file/data";
+  $download   = "$file/download";
+  $notify     = "$file/notify";
+
+  if ($file =~ /\/ADDRESS_BOOK/) {
+    logdel($file,"$file deleted");
+  } elsif (-d $file and not -f $data) {
+    if ($mtime = mtime("$file/upload")) {
+      if ($today > $mtime+DS) {
+        verbose("rmrf $file (today=$today mtime_upload=$mtime)");
+        logdel($file,"$file deleted");
+      }
+    } elsif ($mtime = mtime("$file/error")) {
+      if ($today > 3*$keep*DS+$mtime) {
+        verbose("rmrf $file (today=$today mtime_error=$mtime keep=$keep)");
+        logdel($file,"$file deleted");
+      }
+    } else {
+      logdel($file,"$file deleted");
+    }
+  } elsif (-s $download and -s $data and autodelete($file) !~ /NO/i) {
+    $delay = autodelete($file);
+    $delay = 1 if $delay !~ /^\d+$/;
+    $delay--;
+    $mtime = mtime($download);
+    if ($mtime and $today > $delay*DS+$mtime 
+        and logdel($data,"$data deleted")) {
+      if (open $ef,'>',$ef) {
+        printf {$ef} "%s has been autodeleted after download at %s\n",
+                     filename($file),isodate(mtime($download));
+        close $ef;
+      }
+    }
+  } elsif (-f $data) {
+    my $reactivation = $file =~ m{/\Q$admin/reactivation.txt\E$};
+    $warn = $reactivation ? $keep-5 : $keep-2;
+    $mtime = mtime("$file/filename") || mtime($data) || 0;
+    if ($today > $mtime+$keep*DS) {
+      if ($account_expire and $reactivation) {
+        if ($account_expire =~ /delete/) {
+          logdel($to,"$to removed - expired");
+        } else {
+          if (open $sendmail,"|$sendmail $admin $bcc") {
+            $account_expire =~ /(\d+)/;
+            my $expire = $1 || 0;
+            pq($sendmail,qq(
+              'From: fex\@$hostname'
+              'To: $admin'
+              'Subject: user $to expired'
+              ''
+              'F*EX user $to has been inactive for $expire days'
+              'and has ignored the account reactivation mail.'
+              'You may want to delete this account.'
+            ));
+            close $sendmail;
+            unlink $data;
+          } else {
+            warn "$0: cannot send mail - $!\n";
+          }
+        }
+      } else {
+        if ($file =~ /^anonymous.*\/afex_\d/ or $to =~ /^_.+_/) {
+          # also _fexmail_*
+          logdel($file,"$file deleted") and
+          verbose("rmrf $file (today=$today mtime_upload=$mtime)");
+        } elsif (logdel($data,"$data deleted")) { 
+          verbose("unlink $data (today=$today mtime=$mtime keep=$keep)");
+          if (open $ef,'>',$ef) {
+            $filename = $file;
+            $filename =~ s:.*/::;
+            print $ef "$filename is expired";
+            close $ef;
+          }
+        }
+      }
+    } 
+    elsif ($file !~ /STDFEX$/ and
+           $mtime+$warn*DS < $today and 
+           $dkey = readlink("$file/dkey") and
+           not -s $download and 
+           not -f $notify and 
+           (readlink("$to/\@REMINDER")||'yes') ne 'no') 
+    {
+      my $locale = readlink "$to/\@LOCALE" || readlink "$file/\@LOCALE";
+      $locale = 'english' unless $locale and $notify{$locale};
+      if (open my $c,"$file/comment") {
+        chomp ($comment = <$c>||'');
+        close $c;
+      }
+      &{$notify{$locale}}(
+        status     => 'remind',
+        dkey       => $dkey,
+        filename   => filename($file),
+        keep       => $keep,
+        comment    => $comment,
+        warn       => int(($mtime-$today)/DS)+$keep,
+        autodelete => autodelete($file),
+      );
+      open $notify,'>',$notify;
+      close $notify;
+      print "sent reminder for $file\n" if -t or $opt_v;
+    }
+  }
+}
+
+sub autodelete {
+  my $file = shift;
+  my $adf = "$file/autodelete";
+  my $autodelete;
+
+  if (-l $adf) {
+    $autodelete = readlink $adf || '';
+  } elsif (open $adf,$adf) {
+    chomp($autodelete = <$adf>||'');
+    close $adf;
+  }
+  
+  return $autodelete||$::autodelete;
+}
+
+sub mtime {
+  my @s = lstat shift;
+  return @s ? $s[9] : undef;
+}
+
+sub logdel {
+  my ($file,$msg) = @_;
+  my $status = 0;
+
+  if ($opt_d) {
+    print "$msg\n";
+  } else {
+    if ($status = rmrf($file)) {
+      logv($msg);
+    } else {
+      print L "$isodate $file DEL FAILED : $!\n";
+      warn     "$file DEL FAILED : $!\n" if -t or $opt_v;
+    }
+  }
+  
+  return $status;
+}
+
+
+sub logv {
+  my $msg = shift;
+  print L "$isodate $msg\n" unless $opt_d;
+  print "$msg\n" if -t or $opt_v;
+}
+
+
+sub verbose {
+  local $_;
+  if ($opt_v) {
+    while ($_ = shift @_) {
+      s/\n*$/\n/;
+      print;
+    }
+  }
+}
diff --git a/bin/fexget b/bin/fexget
new file mode 100755 (executable)
index 0000000..034ced1
--- /dev/null
@@ -0,0 +1,1074 @@
+#!/usr/bin/perl -w
+
+# CLI client for the FEX service for retrieving files
+#
+# see also: fexsend
+#
+# Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
+#
+# Perl Artistic Licence
+
+use 5.006;
+use strict qw'vars subs';
+use Config;
+use POSIX;
+use Encode;
+use Getopt::Std;
+use File::Basename;
+use Socket;
+use IO::Handle;
+use IO::Socket::INET;
+use Time::HiRes 'time';
+use constant k => 2**10;
+use constant M => 2**20;
+
+eval 'use Net::INET6Glue::INET_is_INET6';
+
+$| = 1;
+
+our $SH;
+our ($fexhome,$idf,$tmpdir,$windoof,$useragent);
+our ($xv,%autoview);
+our $bs = 2**16; # blocksize for tcp-reading and writing file
+our $version = 20150120;
+our $CTYPE = 'ISO-8859-1';
+our $fexsend = $ENV{FEXSEND} || 'fexsend';
+
+my %SSL = (SSL_version => 'TLSv1');
+my $sigpipe;
+
+# inquire default character set
+# cannot use "use I18N::Langinfo" because of no windows support!
+eval {
+  local $^W = 0;
+  require I18N::Langinfo;
+  I18N::Langinfo->import(qw'langinfo CODESET');
+  $CTYPE = langinfo(CODESET());
+};
+
+if ($Config{osname} =~ /^mswin/i) {
+  $windoof = $Config{osname};
+  $ENV{HOME} = $ENV{USERPROFILE};
+  $fexhome = $ENV{FEXHOME} || $ENV{HOME}.'/fex';
+  $tmpdir = $ENV{FEXTMP} || $ENV{TMP} || "$fexhome/tmp";
+  $idf = "$fexhome/id";
+  $useragent = sprintf("fexget-$version (%s %s)",
+                       $Config{osname},$Config{archname});
+  $SSL{SSL_verify_mode} = 0;
+  chdir $ENV{USERPROFILE}.'\Desktop';
+  # open XX,'>XXXXXX';close XX;
+} else {
+  $0 =~ s:(.*)/:: and $ENV{PATH} .= ":$1";
+  $fexhome = $ENV{FEXHOME} || $ENV{HOME}.'/.fex';
+  $tmpdir = $ENV{FEXTMP} || "$fexhome/tmp";
+  $idf = "$fexhome/id";
+  $_ = `(lsb_release -d||uname -a)2>/dev/null`||'';
+  chomp;
+  s/^Description:\s+//;
+  $useragent = "fexget-$version ($_)";
+}
+
+if (-f ($_ = '/etc/fex/config.pl')) {
+  eval { require } or warn $@;
+}
+
+my $usage = <<EOD;
+usage: $0 [-v] [-m limit] [-s filename] [-o] [-k] [-X] [-P proxy:port] F*EX-URL(s)
+   or: $0 [-v] -d F*EX-URL(s)
+   or: $0 [-v] -f F*EX-URL(s) e-mail-address
+   or: $0 [-v] -a
+   or: $0 -l [-i tag]
+   or: $0 -H
+options: -v verbose mode
+         -m limit kB/s
+         -s save to filename (-s- means: write to STDOUT/pipe)
+         -o overwrite existing file
+        -k keep on server after download
+        -X do not extract archive files or autoview file
+        -d delete without download
+        -f forward a file to another recipient
+         -a get all files (implies -X)
+        -l list files on server
+         -i tag alternate server/account, see: $fexsend -h
+         -P use Proxy for connection to the F*EX server
+         -H show hints and examples
+argument: F*EX-URL may be file number (see: $0 -l)
+EOD
+
+my $hints = <<'EOD';
+When you download a file with extension .jpg .gif .png or .tif an image viewer
+will be started. This can be xv or xdg-open.
+In $HOME/.fex/config.pl you can set your prefered autoview applications:
+
+%autoview = (
+  '\.(gif|jpg|png|tiff?)' => 'my_prefered_image_viewer',
+  '\.(avi|mp4|mov)'       => 'vlc -f',
+  '\.pdf'                 => 'evince',
+);
+
+For HTTPS you can set the environment variables:
+SSLVERIFY=1                 # activate server identity verification
+SSLVERSION=TLSv1            # this is the default
+SSLCAPATH=/etc/ssl/certs    # path to trusted (root) certificates
+SSLCAFILE=/etc/ssl/cert.pem # file with trusted (root) certificates
+SSLCIPHERLIST=HIGH:!3DES    # see http://www.openssl.org/docs/apps/ciphers.html
+
+You can set these environment variables also in $HOME/.fex/config.pl, as well as
+the $opt_* variables, e.g.:
+  
+$ENV{SSLVERSION} = 'TLSv1';
+${'opt_+'} = 1;
+$opt_m = 200;
+EOD
+
+if ($windoof and not @ARGV and not $ENV{PROMPT}) {
+  # restart with cmd.exe to have mouse cut+paste
+  my $cmd = "cmd /k \"$0\"";
+  # print "$cmd\n";
+  exec $cmd;
+  exit;
+}
+
+my $atype = '\.(tgz|tar|zip|7z)$';
+my $proxy = '';
+my $proxy_prefix = '';
+my $chunksize;
+
+our ($opt_h,$opt_v,$opt_l,$opt_d,$opt_m,$opt_z,$opt_K,$opt_o,$opt_a);
+our ($opt_s,$opt_k,$opt_i,$opt_V,$opt_X,$opt_f,$opt_P,$opt_L,$opt_H);
+$opt_m = $opt_h = $opt_v = $opt_l = $opt_d = $opt_K = $opt_o = $opt_a = 0;
+$opt_V = $opt_X = $opt_f = $opt_L = $opt_H = 0;
+${'opt_+'} = 0;
+$opt_s = $opt_k = $opt_i = $opt_P = '';
+$_ = "$fexhome/config.pl"; require if -f;
+getopts('hvVHlLdkzoaXf+m:s:i:K:P:') or die $usage;
+$opt_k = '?KEEP' if $opt_k;
+
+if ($opt_m =~ /(\d+)/) {
+  $opt_m = $1
+} else {
+  $opt_m = 0
+}
+
+print "Version: $version\n" if $opt_V;
+die $usage                  if $opt_h;
+if ($opt_H) {
+  print $hints;
+  exit;
+}
+
+# set SSL/TLS options
+$SSL{SSL_verify_mode} = $ENV{SSLVERIFY} if defined($ENV{SSLVERIFY});
+foreach my $opt (qw(
+  SSL_version
+  SSL_cipher_list 
+  SSL_verify_mode 
+  SSL_ca_path 
+  SSL_ca_file)
+) {
+  my $env = uc($opt);
+  $env =~ s/_//g;
+  $SSL{$opt} = $ENV{$env} if defined($ENV{$env});
+}
+
+if ($SSL{SSL_verify_mode}) {
+  &search_ca;
+  unless ($SSL{SSL_ca_path} or $SSL{SSL_ca_file}) {
+    die "$0: \$SSLVERIFYMODE, but not valid \$SSLCAPATH or \$SSLCAFILE\n";
+  }
+} elsif (defined($SSL{SSL_verify_mode})) {
+  # user has set SSLVERIFY=0 !
+} else {
+  &search_ca;
+  $SSL{SSL_verify_mode} = 1 if $SSL{SSL_ca_path} or $SSL{SSL_ca_file};
+}
+
+sub search_ca {
+  local $_;
+  return if $SSL{SSL_ca_file} or $SSL{SSL_ca_path};
+  foreach (qw(/etc/ssl/certs/ca-certificates.crt)) {
+    if (-f) {
+      $SSL{SSL_ca_file} = $_;
+      return;
+    }
+  }
+  foreach (qw(/etc/ssl/certs /etc/pki/tls/certs)) {
+    if (-f) {
+      $SSL{SSL_ca_path} = $_;
+      return;
+    }
+  }
+}
+
+my $ffl = "$tmpdir/fexget";            # F*EX files list (cache)
+
+my @rcamel = (
+'\e[A
+(_*)  _  _     
+   \\\\/ \\/ \\
+    \  __  )=*
+    //\\\\//\\\\   
+',
+'\e[A     \\\\/\\\\/ 
+',
+'\e[A    //\\\\//\\\\
+');
+
+# get fexlog
+if ($opt_z) {
+  my $cmd = "$fexsend -Z";
+  $cmd .= " -i $opt_i" if $opt_i;
+  warn "$cmd\n" if $opt_v;
+  exec $cmd;
+  die "$0: cannot run $cmd : $!\n";
+}
+
+if ($opt_l) {
+  &list;
+  exit;
+}
+
+if ($opt_L) {
+  my $cmd = "$fexsend -L";
+  $cmd .= " -i $opt_i" if $opt_i;
+  warn "$cmd\n" if $opt_v;
+  exec $cmd;
+  die "$0: cannot run $cmd : $!\n";
+}
+
+if ($opt_P) {
+  if ($opt_P =~ /^([\w.-]+:\d+)(:(\d+))?/) {
+    $proxy = $1;
+    $chunksize = $3 || 0;
+  } else {
+    die "$0: proxy must be: SERVER:PORT\n";
+  }
+}
+
+if ($opt_a) {
+  $opt_X = $opt_a;
+  die $usage if @ARGV;
+  &list;
+  print "\n";
+  if (open $ffl,$ffl) {
+    while (<$ffl>) {
+      push @ARGV,$1 if /^\s+(\d+)/;
+    }
+    close $ffl;
+  }
+} else {
+  unless (@ARGV) {
+    if ($windoof) {
+      my $url;
+      for (;;) {
+        print "download-URL: ";
+        chomp($url = <STDIN>);
+        if ($url =~ /^http/) {
+          @ARGV = ($url);
+          last;
+        }
+      }
+    } else {
+      die $usage;
+    }
+  }
+}
+
+my ($file,%files,$download,$server,$port,$fop);
+
+if ($opt_f) {
+  unless ($ENV{FEXID} or -f $ENV{HOME}.'/.fex/id') {
+    die "$0: no local FEXID\n";
+  }
+  $opt_f = pop(@ARGV);
+  if ($opt_f =~ /^\d+$|^https?:/) {
+    die "$0: $opt_f is not an e-mail address\n";
+  }
+}
+
+URL: foreach my $url (@ARGV) {
+
+  # do not overrun server
+  sleep 1 if $fop;
+
+  if ($url !~ /^http/) {
+    unless (%files) {
+      open $ffl,$ffl or die "$0: no $ffl, use first: $0 -l\n";
+      my $from = '';
+      while (<$ffl>) {
+        if (/^from (.+) :$/) {
+          $from = $1;
+        } elsif (/^\s*(\d+)\)\s+\d+ MB.* (http\S+)/) {
+          push @{$files{all}},$2;
+          push @{$files{$from}},$2;
+        }
+      }
+      close $ffl;
+    }
+
+    if ($url =~ /^(\d+)$/) {
+      $url = ${files{all}}[$1-1] or die "$0: unknown file number\n";
+    }
+  }
+
+  if ($url =~ m{^http(s?)://([\w\.\-]+)(:(\d+))?(/.*fop/\S+)}) {
+    $server = $2;
+    $port   = $4 || ($1?443:80);
+    $fop    = $5;
+  } else {
+    die "$0: unknown F*EX URL $url\n";
+  }
+
+  if ($proxy) {
+    if    ($port == 80)   { $proxy_prefix = "http://$server" }
+    elsif ($port == 443)  { $proxy_prefix = "" }
+    else                  { $proxy_prefix = "http://$server:$port" }
+  }
+
+  serverconnect($server,$port);
+
+  if ($opt_f) {
+    forward($url);
+    next;
+  }
+
+  if ($opt_d) {
+    my @r = del($url);
+    $_ = shift @r;
+    if (/^HTTP.* 200/) {
+      ($file) = grep { $_ = $1 if /^X-File:\s+(.+)/ } @r;
+      $file = $url unless $file;
+      $file =~ s:.*/::;
+      printf "%s deleted\n",urldecode($file);
+    } else {
+      s:HTTP/[\d\. ]+::;
+      die "$0: server response: $_";
+    }
+    next;
+  }
+
+  if ($opt_K) {
+    my @r = keep($url);
+    $_ = shift @r;
+    if (/^HTTP.* 200/) {
+      $file = $url;
+      $file =~ s:.*/::;
+      print "$file kept\n";
+    } else {
+      s:HTTP/[\d\. ]+::;
+      die "$0: server response: $_";
+    }
+    next;
+  }
+
+  $download = download($server,$port,$fop);
+  exit if $opt_s eq '-';
+  unlink $download unless -s $download;
+  exit 2 unless -f $download;
+  
+  if ($windoof) {
+    print "READY\n";
+    exit;
+  }
+
+  if (not $opt_X and $download =~ /\.gpg$/) {
+    if (-t) {
+      print "decrypt \"$download\"? ";
+      $_ = <STDIN>||'y';
+      unless (/^[y\n]/i) {
+        print "keeping \"$download\"\n";
+        exit;
+      }
+    }
+    if (system('gpg',$download) == 0) {
+      unlink $download;
+      $download =~ s/\.gpg$//;
+    }
+  }
+
+  unless ($opt_X) {
+    
+    foreach my $a (keys %autoview) {
+      if ($download =~ /$a$/i and $autoview{$a}) {
+        printf "run \"%s %s\" [Yn] ? ",$autoview{$a},basename($download);
+        $_ = <STDIN>||'';
+        system sprintf("%s %s",$autoview{$a},quote($download)) if /^y|^$/i;
+        next URL;
+      }
+    }
+    
+    if ($ENV{DISPLAY} and $download =~ /\.(gif|jpg|png|tiff?)$/i) {
+      # see also mimeopen and xdg-mime
+      if (my $xv = $xv || pathsearch('xv') || pathsearch('xdg-open')) {
+        printf "run \"%s %s\" [Yn] ? ",basename($xv),basename($download);
+        $_ = <STDIN>||'';
+        system $xv,$download if /^y|^$/i;
+        next URL;
+      }
+    }
+  
+    if ($download =~ /$atype/) {
+      if    ($download =~ /\.(tgz|tar.gz)$/)  { extract('tar tvzf','tar xvzf') }
+      elsif ($download =~ /\.tar$/)           { extract('tar tvf','tar xvf') } 
+      elsif ($download =~ /\.zip$/i)          { extract('unzip -l','unzip') } 
+      elsif ($download =~ /\.7z$/i)           { extract('7z l','7z x') }
+      else { die "$0: unknown archive \"$download\"\n" }
+      if ($? == 0) {
+        unlink $download;
+      } else {
+        die "$0: keeping \"$download\"\n";
+      }
+    }
+  }
+
+}
+
+exit;
+
+sub extract {
+  my $l = shift;
+  my $x = shift;
+  my $d = $download;
+  my $xd = '.';
+  local $_;
+  
+  if (-t and not $windoof) {
+    print "Files in archive:\n";
+    system(split(' ',$l),$download);
+    $d =~ s:.*/:./:;
+    $d =~ s/\.[^.]+$//;
+    for (;;) {
+      $xd = inquire("extract to directory (Ctrl-C to keep archive): ",$d);
+      last if $xd =~ s:^(\./*)*!?$:./:;
+      if ($xd eq '-') {
+        print "keeping $download\n";
+        exit;
+      }    
+      if ($xd !~ s/!$//) {
+        if (-d $xd) {
+          print "directory $xd does already exist, add \"!\" to overwrite\n";
+          redo;
+        }
+        unless (mkdir $xd) {
+          print "cannot mkdir $xd - $!\n";
+          redo;
+        }
+      }
+      unless (chdir $xd) {
+        print "cannot chdir $xd - $!\n";
+        redo;
+      }
+      last;
+    }
+  }
+  print "extracting to $xd :\n";
+  system(split(' ',$x),$download);
+}
+
+sub del {
+  my $url = shift;
+  my ($server,$port);
+  my $del;
+  my @r;
+
+  if ($url =~ m{^http(s?)://([\w\.\-]+)(:(\d+))?(/fop/.+)}) {
+    $server = $2;
+    $port   = $4 || ($1?443:80);
+    $del    = $5.'?DELETE';
+  } else {
+    die "$0: unknown F*EX URL $url\n";
+  }
+
+  sendheader("$server:$port","GET $del HTTP/1.1","User-Agent: $useragent");
+  while (<$SH>) {
+    s/\r//;
+    last if /^\n/; # ignore HTML output
+    warn "<-- $_" if $opt_v;
+    push @r,$_;
+  }
+  die "$0: no response from fex server $server\n" unless @r;
+  return @r;
+}
+
+
+sub forward {
+  my $url = shift;
+  my ($server,$port);
+  my ($uri,$dkey,$list,$cmd,$n);
+  my @r;
+
+  if ($url =~ m{^http(s?)://([\w\.\-]+)(:(\d+))?(/fop/.+)}) {
+    $server = $2;
+    $port   = $4 || ($1?443:80);
+    $uri    = $5;
+  } else {
+    die "$0: unknown F*EX URL $url\n";
+  }
+
+  sendheader(
+    "$server:$port",
+    "GET $uri?COPY HTTP/1.1",
+    "User-Agent: $useragent",
+  );
+  
+  $_ = <$SH>;
+  die "$0: no reply from fex server $server\n" unless $_;
+  warn "<-- $_" if $opt_v;
+  
+  unless (/^HTTP.*200/) {
+    s/^HTTP.... \d+ //;
+    die "$0: $_";
+  }
+  
+  while (<$SH>) {
+    s/\r//;
+    last if /^\n/; # ignore HTML output
+    $dkey = $1 if /^Location:.*\/(\w+)\/.+/;
+    warn "<-- $_" if $opt_v;
+  }
+
+  $cmd = 'fexsend -l >/dev/null 2>&1';
+  print "$cmd\n" if $opt_v;
+  system 'fexsend -l >/dev/null 2>&1';
+  $list = $ENV{HOME}.'/.fex/tmp/fexlist';
+  open $list,$list or die "$0: cannot open $list - $!\n";
+  while (<$list>) {
+    if (/^\s+(\d+)\) (\w+)/ and $2 eq $dkey) {
+      $n = $1;
+      $cmd = "fexsend -b $n $opt_f";
+      print "$cmd\n" if $opt_v;
+      system $cmd;
+      last;
+    }
+  }
+  close $list;
+  
+  if ($n) {
+    $cmd = "fexsend -d $n >/dev/null 2>&1";
+    print "$cmd\n" if $opt_v;
+    system $cmd;
+  } else {
+    warn "$0: forwarding failed\n";
+  }
+}
+
+
+sub keep {
+  my $url = shift;
+  my ($server,$port);
+  my $keep;
+  my (@hh,@r);
+
+  if ($url =~ m{^http(s?)://([\w\.\-]+)(:(\d+))?(/fop/.+)}) {
+    $server = $2;
+    $port   = $4 || ($1?443:80);
+    $keep    = "$5?KEEP=$opt_K";
+  } else {
+    die "$0: unknown F*EX URL $url\n";
+  }
+
+  push @hh,"GET $keep HTTP/1.1",
+           "Host: $server:$port",
+           "User-Agent: $useragent",
+           "";
+
+  foreach (@hh) {
+    warn $_,"\n" if $opt_v;
+    print $SH $_,"\r\n";
+  }
+  while (<$SH>) {
+    s/\r//;
+    last if /^\n/;
+    push @r,$_;
+  }
+  die "$0: no response from fex server $server\n" unless @r;
+  grep { warn "\t$_" } @r if $opt_v;
+  return @r;
+}
+
+
+sub download {
+  my ($server,$port,$fop,$nocheck) = @_;
+  my ($file,$download,$ssl,$pipe,$filesize,$checkstorage);
+  my (@hh,@r);
+  my ($t0,$t1,$t2,$tt,$tm,$ts,$kBs,$b,$bt,$tb,$B,$buf);
+  my $length = 0;
+  my $seek = 0;
+  my $tc = 0;
+  local $_;
+  local *X;
+
+  if ($opt_s) {
+    $file = $opt_s;
+    if ($opt_s eq '-') {
+      $pipe = $download = $opt_s;
+    } elsif (-p $opt_s or -c $opt_s) {
+      $download = $opt_s;
+    } else {
+      $download = $file.'.tmp';
+      $seek = -s $download || 0;
+    }
+  } else {
+    # ask server for real file name
+    serverconnect($server, $port);
+    sendheader("$server:$port","HEAD $proxy_prefix$fop HTTP/1.1","User-Agent: $useragent");
+    my $reply = $_ = <$SH>;
+    unless (defined $_ and /\w/) {
+      die "$0: no response from server\n";
+    }
+    warn "<-- $_" if $opt_v;
+    unless (/^HTTP\/[\d.]+ 200/) {
+      s:HTTP/[\d. ]+::;
+      die "$0: server response: $_";
+    }
+    while (<$SH>) {
+      s/\r//;
+      warn "<-- $_" if $opt_v;
+      last if /^\r?\n/;
+      if (/^Content-Disposition: attachment; filename="(.+)"/i) {
+        $file = locale(decode_utf8($1));
+          $file =~ s:.*/::;
+      }
+    }
+    unless ($file) {
+      $file = $fop;
+      $file =~ s:.*/::;
+    }
+    $download = $file.'.tmp';
+    $seek = -s $download || 0;
+  }
+
+  push @hh,"GET $proxy_prefix$fop$opt_k HTTP/1.1",
+           "User-Agent: $useragent",
+           "Connection: close";
+  push @hh,"Range: bytes=$seek-" if $seek;
+
+  # HTTPS needs a new connection for actually downloading the file
+  serverconnect($server,$port) if $opt_P and $port == 443;
+  sendheader("$server:$port",@hh);
+  $_ = <$SH>;
+  die "$0: no response from fex server $server\n" unless $_;
+  s/\r//;
+
+  if (/^HTTP\/[\d.]+ 2/) {
+    warn "<-- $_" if $opt_v;
+    while (<$SH>) {
+      s/\r//;
+      warn "<-- $_" if $opt_v;
+      last if /^\r?\n/;
+      if (/^Content-length:\s*(\d+)/i) {
+        $length = $1;
+      } elsif (/^X-Size: (\d+)/i) {
+        $filesize = $1;
+      }
+    }
+  } else {
+    s/HTTP\/[\d.]+ \d+ //;
+    die "$0: bad server reply: $_";
+  }
+
+  if ($pipe) {
+    *X = *STDOUT;
+  } else {
+    if ($opt_s and $opt_s eq $download) {
+      open X,'>',$download or die "$0: cannot write to \"$download\" - $!\n";
+      $checkstorage = $filesize unless $nocheck;
+    } else {
+      if (-e $file and not $opt_o) {
+        die "$0: destination file \"$file\" does already exist\n";
+      }
+      if ($seek) {
+        open X,'>>',$download or die "$0: cannot write to \"$download\" - $!\n";
+      } else {
+        open X,'>',$download or die "$0: cannot write to \"$download\" - $!\n";
+        $checkstorage = $filesize unless $nocheck;
+      }
+    }
+    if ($checkstorage and not $nocheck) {
+      $t0 = time;
+      my $n = 0;
+      print STDERR "checking storage...\r";
+      $buf = '.' x M;
+      while (-s $download < $checkstorage) {
+        syswrite X,$buf or do {
+          unlink $download;
+          die "\n$0: cannot write $download - $!\n";
+        };
+        $n++;
+        print STDERR "checking storage... ".$n." MB\r";
+      }
+      close X or do {
+        unlink $download;
+        die "\n$0: cannot write $download - $!\n";
+      };
+      print STDERR "checking storage... ".$n." MB ok!\n";
+      unlink $download;
+      if (time-$t0 < 25) {
+        open X,'>',$download or die "$0: cannot write to \"$download\" - $!\n";
+      } else {
+        # retry after timeout
+        return(download($server,$port,$fop,'nocheck'))
+      }
+    }
+  }
+
+  $t0 = $t1 = $t2 = int(time);
+  $tb = $B = 0;
+  printf STDERR "resuming at byte %s\n",$seek if $seek;
+  print $rcamel[0] if ${'opt_+'};
+  while ($B < $length and $b = read $SH,$buf,$bs) {
+    syswrite X,$buf;
+    $B += $b;
+    $tb += $b;
+    $bt += $b;
+    $t2 = time;
+    if (${'opt_+'} and int($t2*10)>$tc) {
+      print $rcamel[$tc%2+1];
+      $tc = int($t2*10);
+    }
+    if (int($t2) > $t1) {
+      $kBs = int($bt/k/($t2-$t1));
+      $kBs = int($tb/k/($t2-$t0)) if $kBs < 10;
+      $t1 = $t2;
+      $bt = 0;
+      # smaller block size is better on slow links
+      $bs = 4096 if $bs>4096 and $tb/($t2-$t0)<65536;
+      if ($tb<10*M) {
+        printf STDERR "%s: %d kB (%d%%) %d kB/s \r",
+                      $download,
+                      int(($tb+$seek)/k),
+                      int(($tb+$seek)/($length+$seek)*100),
+                      $kBs;
+      } else {
+        printf STDERR "%s: %d MB (%d%%) %d kB/s        \r",
+                      $download,
+                      int(($tb+$seek)/M),
+                      int(($tb+$seek)/($length+$seek)*100),
+                      $kBs;
+      }
+    }
+    if ($opt_m) {
+      if ($t2 == $t0 and $B > $opt_m*k) {
+        print "\nsleeping...\r" if $opt_v;
+        sleep 1;
+      } else {
+        while ($t2 > $t0 and $tb/k/($t2-$t0) > $opt_m) {
+          print "\nsleeping...\r" if $opt_v;
+          sleep 1;
+          $t2 = time;
+        }
+      }
+    }
+  }
+  close $SH;
+  close X;
+  
+  print $rcamel[2] if ${'opt_+'};
+
+  $tt = $t2-$t0;
+  $tm = int($tt/60);
+  $ts = $tt-$tm*60;
+  $kBs = int($tb/k/($tt||1));
+  if ($seek) {
+    printf STDERR "$file: %d MB, last %d MB in %d s (%d kB/s)      \n",
+                  int(($tb+$seek)/M),int($tb/M),$tt,$kBs;
+  } else {
+    printf STDERR "$file: %d MB in %d s (%d kB/s)      \n",
+                  int($tb/M),$tt,$kBs;
+  }
+
+  if ($tb != $length) {
+    if ($windoof) {
+      exec "\"$0\" @ARGV";
+      exit;
+    } else {
+      die "$0: $server annouced $length bytes, but only $tb bytes has been read\n";
+    }
+  }
+
+  unless ($pipe or -p $download or -c $download) {
+    my @s = stat $file if -e $file;
+    rename $download,$file
+      or die "$0: cannot rename \"$download\" to \"$file\" - $!\n";
+    chmod $s[2],$file if @s;
+  }
+
+  return sprintf("%s/%s",getcwd(),$file);
+}
+
+
+sub list {
+  my $cmd = "$fexsend -L";
+  $cmd .= " -i $opt_i" if $opt_i;
+  if ($opt_v) {
+    $cmd .= " -v";
+    warn "$cmd\n";
+  }
+  open $cmd,"$cmd|" or die "$0: cannot run $cmd : $!\n";
+  open $ffl,'>',$ffl or die "$0: cannot open $ffl : $!\n";
+  my $n;
+  while (<$cmd>) {
+    if (/\d MB .*http/) {
+      $n++;
+      printf {$ffl} "%4d) %s",$n,$_;
+      s:http[^\"]*/::;
+      printf        "%4d) %s",$n,$_;
+    } else {
+      print;
+      print {$ffl} $_;
+    }
+  }
+}
+
+
+sub locale {
+  my $string = shift;
+
+  if ($CTYPE) {
+    if ($CTYPE =~ /UTF-?8/i) {
+      return $string;
+    } elsif (grep { $CTYPE =~ /^$_$/i } Encode->encodings()) {
+      return encode($CTYPE,$string);
+    } else {
+      return encode('ISO-8859-1',$string);
+    }
+  }
+
+  return $string;
+}
+
+
+sub pathsearch {
+  my $prg = shift;
+  
+  foreach my $dir (split(':',$ENV{PATH})) {
+    return "$dir/$prg" if -x "$dir/$prg";
+  }
+}
+
+    
+sub quote {
+  local $_ = shift;
+  s/([^\w¡-ÿ_%\/=~:.,-])/\\$1/g;
+  return $_;
+}
+
+    
+{
+  my $tty;
+
+  sub inquire {
+    my $prompt = shift;
+    my $default = shift;
+    local $| = 1;
+    local $_;
+
+    if (defined $default) {
+      unless ($tty) {
+        chomp($tty = `tty 2>/dev/null`);
+        eval { local $^W; require "sys/ioctl.ph"; };
+      }
+
+      if (defined(&TIOCSTI) and $tty and open($tty,'>',$tty)) {
+        print $prompt;
+        foreach my $a (split("",$default)) { ioctl($tty,&TIOCSTI,$a) } 
+        chomp($_ = <STDIN>||'');
+      } else {
+        $prompt =~ s/([\?:=]\s*)/ [$default]$1/ or $prompt .= " [$default]";
+        print $prompt;
+        chomp($_ = <STDIN>||'');
+        $_ = $default unless length;
+      }
+    } else {
+      print $prompt;
+      chomp($_ = <STDIN>||'');
+    }
+
+    return $_;
+  }    
+}    
+
+
+### common functions ###
+
+
+sub mtime {
+  my @d = localtime((stat shift)[9]);
+  return sprintf('%d%02d%02d',$d[5]+1900,$d[4]+1,$d[3]);
+}
+
+
+sub urldecode {
+  local $_ = shift;
+  s/\%([a-f\d]{2})/chr(hex($1))/ige;
+  return $_;
+}
+
+
+sub get_ssl_env {
+  # set SSL/TLS options
+  $SSL{SSL_verify_mode} = $ENV{SSLVERIFY} if defined($ENV{SSLVERIFY});
+  foreach my $opt (qw(
+    SSL_version
+    SSL_cipher_list 
+    SSL_verify_mode 
+    SSL_ca_path 
+    SSL_ca_file)
+  ) {
+    my $env = uc($opt);
+    $env =~ s/_//g;
+    $SSL{$opt} = $ENV{$env} if defined($ENV{$env});
+  }
+
+  if ($SSL{SSL_verify_mode}) {
+    &search_ca;
+    unless ($SSL{SSL_ca_path} or $SSL{SSL_ca_file}) {
+      die "$0: \$SSLVERIFYMODE, but not valid \$SSLCAPATH or \$SSLCAFILE\n";
+    }
+  } elsif (defined($SSL{SSL_verify_mode})) {
+    # user has set SSLVERIFY=0 !
+  } else {
+    &search_ca;
+    $SSL{SSL_verify_mode} = 1 if $SSL{SSL_ca_path} or $SSL{SSL_ca_file};
+  }
+}
+
+sub search_ca {
+  local $_;
+  return if $SSL{SSL_ca_file} or $SSL{SSL_ca_path};
+  foreach (qw(/etc/ssl/certs/ca-certificates.crt)) {
+    if (-f) {
+      $SSL{SSL_ca_file} = $_;
+      return;
+    }
+  }
+  foreach (qw(/etc/ssl/certs /etc/pki/tls/certs)) {
+    if (-f) {
+      $SSL{SSL_ca_path} = $_;
+      return;
+    }
+  }
+}
+
+
+sub serverconnect {
+  my ($server,$port) = @_;
+  my $connect = "CONNECT $server:$port HTTP/1.1";
+  local $_;
+  
+  if ($opt_v and $port == 443 and %SSL) {
+    foreach my $v (keys %SSL) {
+      printf "%s => %s\n",$v,$SSL{$v};
+    }
+  }
+  
+  if ($proxy) {
+    tcpconnect(split(':',$proxy));
+    if ($port == 443) {
+      printf "--> %s\n",$connect if $opt_v;
+      nvtsend($connect,"");
+      $_ = <$SH>;
+      s/\r//;
+      printf "<-- $_"if $opt_v;
+      unless (/^HTTP.1.. 200/) {
+        die "$0: proxy error : $_";
+      }
+      eval "use IO::Socket::SSL";
+      die "$0: cannot load IO::Socket::SSL\n" if $@;
+      $SH = IO::Socket::SSL->start_SSL($SH,%SSL);
+    }
+  } else {
+    tcpconnect($server,$port);
+  }
+#  if ($port == 443 and $opt_v) {
+#    printf "%s\n",$SH->get_cipher();
+#  }
+}
+
+
+# set up tcp/ip connection
+sub tcpconnect {
+  my ($server,$port) = @_;
+  
+  if ($SH) {
+    close $SH;
+    undef $SH;
+  }
+  
+  if ($port == 443) {
+    # eval "use IO::Socket::SSL qw(debug3)";
+    eval "use IO::Socket::SSL";
+    die "$0: cannot load IO::Socket::SSL\n" if $@;
+    $SH = IO::Socket::SSL->new(
+      PeerAddr => $server,
+      PeerPort => $port,
+      Proto    => 'tcp',
+      %SSL
+    );
+  } else {
+    $SH = IO::Socket::INET->new(
+      PeerAddr => $server,
+      PeerPort => $port,
+      Proto    => 'tcp',
+    );
+  }
+  
+  if ($SH) {
+    autoflush $SH 1;
+  } else {
+    die "$0: cannot connect $server:$port - $@\n";
+  }
+  
+  print "TCPCONNECT to $server:$port\n" if $opt_v;
+}
+
+
+sub sendheader {
+  my $sp = shift;
+  my @head = @_;
+  my $head;
+  
+  push @head,"Host: $sp";
+  
+  foreach $head (@head) {
+    print "--> $head\n" if $opt_v;
+    print {$SH} $head,"\r\n";
+  }
+  print "-->\n" if $opt_v;
+  print {$SH} "\r\n";
+}
+
+
+sub nvtsend {
+  local $SIG{PIPE} = sub { $sigpipe = "@_" };
+  
+  $sigpipe = '';
+  
+  die "$0: internal error: no active network handle\n" unless $SH;
+  die "$0: remote host has closed the link\n" unless $SH->connected;
+  
+  foreach my $line (@_) {
+    print {$SH} $line,"\r\n";
+    if ($sigpipe) {
+      undef $SH;
+      return 0;
+    }
+  }
+  
+  return 1;
+}
+
+
+# from MIME::Base64::Perl
+sub encode_b64 {
+  my $res = "";
+  my $eol = "\n";
+  my $padding;
+  
+  pos($_[0]) = 0;
+  $res = join '',map(pack('u',$_)=~ /^.(\S*)/, ($_[0]=~/(.{1,45})/gs));
+  $res =~ tr|` -_|AA-Za-z0-9+/|;
+  $padding = (3-length($_[0])%3)%3;
+  $res =~ s/.{$padding}$/'=' x $padding/e if $padding;
+  return $res;
+}
diff --git a/bin/fexsend b/bin/fexsend
new file mode 100755 (executable)
index 0000000..607d139
--- /dev/null
@@ -0,0 +1,3055 @@
+#!/usr/bin/perl -w
+
+# CLI client for the F*EX service (send, list, delete)
+#
+# see also: fexget
+#
+# Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
+#
+# Perl Artistic Licence
+
+use 5.006;
+use strict qw'vars subs';
+use Encode;
+use Config;
+use Socket;
+use IO::Handle;
+use IO::Socket::INET;
+use Getopt::Std;
+use File::Basename;
+use Cwd qw'abs_path';
+use Fcntl qw':flock :mode';
+use Digest::MD5 qw'md5_hex';  # encrypted ID / SID
+use Time::HiRes qw'time';
+# use Smart::Comments;
+use constant k => 2**10;
+use constant M => 2**20;
+
+eval 'use Net::INET6Glue::INET_is_INET6';
+
+&update if "@ARGV" eq 'UPDATE';
+
+$| = 1;
+
+our ($SH,$fexhome,$idf,$tmpdir,$windoof,$useragent,$editor,$nomail);
+our ($anonymous,$public);
+our ($tpid,$frecipient);
+our ($FEXID,$FEXXX,$HOME);
+our (%alias);
+our $chunksize = 0;
+our $version = 20150120;
+our $_0 = $0;
+our $DEBUG;
+
+my %SSL = (SSL_version => 'TLSv1');
+my $sigpipe;
+
+if ($Config{osname} =~ /^mswin/i) {
+  $windoof = $Config{osname};
+  $HOME = $ENV{USERPROFILE};
+  $fexhome = $ENV{FEXHOME} || $HOME.'\fex';
+  $tmpdir = $ENV{FEXTMP} || $ENV{TEMP} || "$fexhome\\tmp";
+  $idf = "$fexhome\\id";
+  $editor = $ENV{EDITOR} || 'notepad.exe';
+  $useragent = sprintf("fexsend-$version (%s %s)",
+                       $Config{osname},$Config{archname});
+  $SSL{SSL_verify_mode} = 0;
+} else {
+  $0 =~ s:.*/::;
+  $HOME = (getpwuid($<))[7]||$ENV{HOME};
+  $fexhome = $HOME.'/.fex';
+  $tmpdir = $ENV{FEXTMP} || "$fexhome/tmp";
+  $idf = "$fexhome/id";
+  $editor = $ENV{EDITOR} || 'vi';
+  $_ = `(lsb_release -d||uname -a)2>/dev/null`||'';
+  chomp;
+  s/^Description:\s+//;
+  $useragent = "fexsend-$version ($_)";
+  chmod 0600,$idf;
+}
+
+if (-f ($_ = '/etc/fex/config.pl')) {
+  eval { require } or warn $@;
+}
+
+my $from = '';
+my $to = '';
+my $id = '';
+my $skey = '';
+my $gkey = '';
+my $atype = '';                # archive type
+my $fexcgi;            # F*EX CGI URL
+my @files;             # files to send
+my %AB = ();           # server based address book
+my ($server,$port,$sid);
+my $proxy = '';
+my $proxy_prefix = '';
+my $features = ''; 
+my $timeout = 30;      # server timeout
+my $fexlist = "$tmpdir/fexlist";
+my ($usage,$hints);
+my $xx = $0 =~ /^xx/;
+
+if ($xx) {
+  $usage = "usage: send file(s):               xx [:slot] file...\n".
+           "   or: send STDIN:                 xx [:slot] -\n".
+           "   or: send pipe:                  ... | xx [:slot] \n".
+           "   or: get file(s) or STDIN:       xx [:slot] \n".
+           "   or: get file(s) no-questions:   xx [:slot] --\n".
+           "examples: dmesg | xx\n".
+           "          xx project\n".
+           "          xx --\n".
+           "          xx :conf /etc /boot\n";
+} else {
+  $usage = <<EOD;
+usage: $0 [options] file(s) [@] recipient(s)
+   or: $0 [special options]
+   or: $0 -f \# recipient(s)
+   or: $0 -x \# [-C -k -D -K -S]
+options: -v           verbose mode
+         -d           delete file on fex server
+         -c           compress file
+         -g           encrypt file with gpg
+         -m limit     limit throughput (kB/s)
+         -i tag       use ID data [tag] from ID file
+         -C comment   add comment to notification e-mail
+         -k max       keep file max days on fex server
+         -D           delay auto-delete after download
+         -K           no auto-delete after download
+         -M           MIME-file (to be displayed in recipient\'s webbrowser)
+         -o           overwrite mode, do not resume
+         -a archive   put files in archive (.zip .7z .tar .tgz)
+         -s stream    read data from pipe and upload it with stream name
+special options: -I      initialize ID file or show ID
+                 -I tag  add alternate ID data (secondary logins) to ID file
+                 -l      list sent files numbered (# needed for -f -x -d -N)
+                 -f \#    forward already uploaded file to another recipient
+                 -x \#    modify options -C -k -D -K for already uploaded file
+                 -d \#    delete file on fex server
+                 -N \#    resend notification e-mail
+                 -Q      check quotas
+                 -A      edit server address book (aliases)
+                 -S      show server/user settings and auth-ID
+                 -H      show hints, examples and more options
+                 -V      show version
+                 (\# is a file number, see output from $0 -l)
+examples: $0 visualization.mpg framstag\@rus.uni-stuttgart.de
+          $0 -a images.zip *.jpg webmaster\@flupp.org,metoo
+          lshw | $0 -s hardware.list admin\@flupp.org
+EOD
+#   or: $0 -R FEX-URL e-mail
+#         -R FEX mail  self-register your e-mail address at FEX server
+
+  $hints = <<EOD;
+$0 hints and more options:
+  
+usage: $0 [options] file recipient(s)
+
+Recipient can be a comma separated address list. Example:
+  $0 big.file framstag\@rus.uni-stuttgart.de,webmaster\@flupp.org
+
+Recipient can be an alias from your server address book 
+(use "$0 -A" to edit it). Example:
+  $0 big.file framstag
+
+Recipient can be a SKEY URL, which you have received from a regular F*EX user.
+When using this URL you are a subuser of this full user and the file will be 
+sent to him. Example:
+  $0 big.file http://fex.rus.uni-stuttgart.de/fup?skey=4285f8cdd881626524fba686d5f0a83a
+
+Recipient can be a GKEY URL, which you have received from a regular F*EX user.
+Using this URL you are a member of his group and the file will be sent to all
+members of this group. Example:
+  $0 big.file http://fex.rus.uni-stuttgart.de/fup?gkey=50d26547b1e8c1110beb8748fc1d9444
+
+When you use "FEX-URL/anonymous" as recipient and your F*EX administrator has 
+allowed anonymous upload for your IP address then no auth-ID is needed.
+    
+"." as recipient means fex to yourself and show immediately the download URL 
+(no notification e-mail will be sent). Example:
+  $0 software.tar .
+
+"//" as recipient means fex to yourself and create extra short download URL.
+Example:
+  $0 software.tar //
+
+If you want a Bcc of the notification e-mail then add '!bcc!' to the comment:
+fexsend -C '!bcc! for me and you' ...
+
+Additional special options:
+
+  -. sends a short instead of a detailed notification e-mail
+  -/ does not upload the file, but tells the server to link it
+  -= uses an alias name as file name
+  -# excludes files (# is list separator) from archive -a
+  -n sends no notification e-mail, but shows the download URL immediately
+  -q is quiet mode
+  -r ADDRESS sets e-mail Reply-To ADDRESS
+  -F activates female mode
+  -U show authorized URL
+  -+ is an undocumented feature - test it :-)
+    
+To manage your subuser and groups or forward or redirect files, use a 
+webbrowser with the URL from "$0 -U", e.g.:  firefox \$($0 -U)
+
+If you want to copy-forward an already uploaded file to another recipient,
+then you first have to query the file number with:
+  $0 -l
+and then copy-forward it with:
+  $0 -b # other\@address
+Where # is the file number.
+
+If you want to modify the keep time, comment or auto-delete behaviour of an
+already uploaded file then you first have to query the file number with:
+  $0 -l
+and then for example set the keep time to 30 days with:
+  $0 -x # -k 30
+Where # is the file number.
+
+With option -a you can send several files or whole directories within a single
+archive file. The archive types tar and tgz are build on-the-fly (streaming) 
+whereas archive types zip and 7z need a temporary archive file on local disk.
+
+With option -s you can send any data coming from a pipe (STDIN) as a file
+without wasting local disc space.
+With option -X you can specify any parameter, e.g.: -X autodelete=yes
+
+For HTTPS you can set the environment variables:
+SSLVERIFY=1                 # activate server identity verification
+SSLVERSION=TLSv1            # this is the default
+SSLCAPATH=/etc/ssl/certs    # path to trusted (root) certificates
+SSLCAFILE=/etc/ssl/cert.pem # file with trusted (root) certificates
+SSLCIPHERLIST=HIGH:!3DES    # see http://www.openssl.org/docs/apps/ciphers.html
+  
+Partner program xx is an internet clipboard. See: xx -h
+  
+Partner program fexget is for downloading. See: fexget -h
+  
+For temporary usage of a HTTP proxy use: 
+  $0 -P your_proxy:port:chunksize_in_MB file recipient
+Example:
+  $0 -P wwwproxy.uni-stuttgart.de.de:8080:1024 4GB.tar .
+  
+For temporary usage of an alternative F*EX server or user use: 
+  FEXID="FEXSERVER USER AUTHID" $0 file recipient
+Example:
+  FEXID="fex.flupp.org gaga\@flupp.org blubb" $0 big.file framstag\@rus.uni-stuttgart.de
+
+You can define aliases (and optional fexsend options) in \$HOME/.fex/config.pl:
+  %alias = (
+    'alias1' => 'user1\@domain1.org',
+    'alias2' => 'user2\@domain2.org',
+    'both'   => 'user1\@domain1.org,user2\@domain2.org',
+    'extra'  => 'extra\@special.net:-i other -K -k 30',
+  );
+
+fexsend also respects aliases in $HOME/.mutt/aliases
+The alias priority is (descending):
+\$HOME/.fex/config.pl
+\$HOME/.mutt/aliases 
+fexserver address book  
+
+In \$HOME/.fex/config.pl you can also set the SSL* environment variables and the
+\$opt_* variables, e.g.:
+  
+\$ENV{SSLVERSION} = 'TLSv1';
+\${'opt_+'} = 1;
+\$opt_m = 200;
+EOD
+}
+
+my @rcamel = (
+'\e[A
+     _  _  c*_)
+    / \/ \//
+ *=(  __  /
+    \\\\/\\\\/
+',
+'\e[A    \\\\/\\\\/ 
+',
+'\e[A   //\\\\//\\\\
+');
+
+autoflush STDERR;
+
+if ($windoof and not @ARGV and not $ENV{PROMPT}) {
+  # restart with cmd.exe to have mouse cut+paste
+  exec qw'cmd /k',$0,'-W';
+  exit;
+}
+
+unless (-d $fexhome) {
+  mkdir $fexhome,0700 or die "$0: cannot create FEXHOME $fexhome - $!\n";
+}
+
+unless (-d $tmpdir) {
+  mkdir $tmpdir,0700 or die "$0: cannot create tmpdir $tmpdir - $!\n";
+}
+
+my @_ARGV = @ARGV; # save arguments
+
+our ($opt_q,$opt_h,$opt_H,$opt_v,$opt_m,$opt_c,$opt_k,$opt_d,$opt_l,$opt_I,
+     $opt_K,$opt_D,$opt_u,$opt_f,$opt_a,$opt_C,$opt_R,$opt_M,$opt_L,$opt_Q,
+     $opt_A,$opt_i,$opt_z,$opt_Z,$opt_b,$opt_P,$opt_x,$opt_X,$opt_V,$opt_U,
+     $opt_s,$opt_o,$opt_g,$opt_F,$opt_n,$opt_r,$opt_S,$opt_N);
+
+if ($xx) {
+  $opt_q = 1 if @ARGV and $ARGV[-1] eq '--' and pop @ARGV or not -t STDOUT;
+  $opt_h = $opt_v = $opt_m = $opt_I = 0;
+  $opt_X = '';
+  $_ = "$fexhome/config.pl"; require if -f;
+  getopts('hvIm:') or die $usage;
+} else {
+  $opt_h = $opt_v = $opt_m = $opt_c = $opt_k = $opt_d = $opt_l = $opt_I = 0;
+  $opt_H = $opt_K = $opt_D = $opt_R = $opt_M = $opt_L = $opt_Q = $opt_A = 0;
+  $opt_x = $opt_o = $opt_g = $opt_V = $opt_U = $opt_F = $opt_n = $opt_q = 0;
+  $opt_S = $opt_N = 0;
+  ${'opt_@'} = ${'opt_!'} = ${'opt_+'} = ${'opt_.'} = ${'opt_/'} = 0;
+  ${'opt_='} = ${'opt_#'} = '';
+  $opt_u = $opt_f = $opt_a = $opt_C = $opt_i = $opt_b = $opt_P = $opt_X = '';
+  $opt_s = $opt_r = '';
+  $_ = "$fexhome/config.pl"; require if -f;
+  getopts('hHvcdognVDKlILUARWMFzZqQS@!+./r:m:k:u:f:a:s:C:i:b:P:x:X:N:=:#:') 
+    or die $usage;
+
+  if ($opt_H) {
+    print $hints;
+    exit;
+  }
+  
+  if ($opt_V) {
+    print "Version: $version\n";
+  }
+  
+  if ($opt_K and $opt_D) {
+    die "$0: you cannot use both options -D and -K\n";
+  }
+
+  if ($opt_a and $opt_c) {
+    die "$0: you cannot use both options -a and -c\n";
+  }
+
+  if ($opt_a and $opt_s) {
+    die "$0: you cannot use both options -a and -s\n";
+  }
+
+  if ($opt_g and $opt_c) {
+    $opt_c = 0;
+  }
+
+  $opt_f ||= $opt_b;
+  if ($opt_f and $opt_f !~ /^\d+$/) {
+    die "$0: option -f needs a number, see $0 -l\n";
+  }
+
+  if ($opt_I and $opt_R) {
+    die "$0: you cannot use both options -I and -R\n";
+  }
+
+  # $opt_C is COMMENT command in F*EX protocol
+  $opt_C =    
+    ($opt_d)           ? 'DELETE':
+    ($opt_l or $opt_L) ? 'LIST':
+    ($opt_Q)           ? 'CHECKQUOTA':
+    ($opt_S)           ? 'LISTSETTINGS':
+    ($opt_Z)           ? 'RECEIVEDLOG':
+    ($opt_z)           ? 'SENDLOG':
+    (${'opt_!'})       ? 'FOPLOG':
+  $opt_C;
+  
+  $opt_D =     
+    ($opt_D) ? 'DELAY':
+    ($opt_K) ? 'NO':
+  $opt_D;
+}
+
+&get_ssl_env;
+
+if ($opt_h) {
+  female_mode("show help?") if $opt_F;
+  print $usage;
+  exit;
+}
+
+
+if ($opt_R) {
+  &register;
+  exit;
+}
+
+
+die $usage if $opt_m and $opt_m !~ /^\d+/;
+
+if ($opt_P) { 
+  if ($opt_P =~ /^([\w.-]+:\d+)(:(\d+))?/) {
+    $proxy = $1;
+    $chunksize = $3 || 0;
+  } else {
+    die "$0: proxy must be: SERVER:PORT\n";
+  }
+}
+
+if ($FEXID = $ENV{FEXID}) {
+  $FEXID = decode_b64($FEXID) if $FEXID !~ /\s/;
+  ($fexcgi,$from,$id) = split(/\s+/,$FEXID);
+} else {
+  if ($windoof and not -f $idf) { &init_id }
+  if (open $idf,$idf) {
+    &get_id($idf);
+    close $idf;
+  }
+}
+
+if ($xx) {
+  # convert old idxx file
+  if ($idf and open $idf,$idf.'xx') {
+    &get_id($idf);
+    close $idf;
+    if (open $idf,'>>',$idf) {
+      print {$idf} "\n[xx]\n",
+                   "$fexcgi\n",
+                   "$from\n",
+                   "$id\n";
+      close $idf;
+      unlink $idf.'xx';
+    }
+  }
+  
+  # special xx ID?
+  if ($FEXXX = $ENV{FEXXX}) {
+    $FEXXX = decode_b64($FEXXX) if $FEXXX !~ /\s/;
+    ($fexcgi,$from,$id) = split(/\s+/,$FEXXX);
+  } elsif (open $idf,$idf) {
+    while (<$idf>) {
+      if (/^\[xx\]/) {
+        $proxy = $proxy_prefix = '';
+        &get_id($idf);
+        last;
+      }
+    }
+    close $idf;
+  }
+  
+} else {
+
+  # alternativ ID?
+  if ($opt_i) {
+    $proxy = $proxy_prefix = '';
+    open $idf,$idf or die "$0: cannot open $idf - $!\n";
+    while (<$idf>) {
+      if (/^\[$opt_i\]/) {
+        &get_id($idf);
+        last;
+      }
+    }
+    close $idf;
+    die "$0: no [$opt_i] in $idf\n" unless $_;
+  }
+}
+
+if ($opt_I) {
+  if ($xx) { &show_id } 
+  else     { &init_id }
+  exit;
+}
+
+if (@ARGV > 1 and $ARGV[-1] =~ /(^|\/)anonymous/) {
+  $fexcgi = $1 if $ARGV[-1] =~ s:(.+)/::;
+  die "usage: $0 [options] file FEXSERVER/anonymous\n" unless $fexcgi;
+  $anonymous = $from = 'anonymous';
+  $sid = $id = 'ANONYMOUS';
+} elsif (@ARGV > 1 and $id eq 'PUBLIC') {
+  $public = $sid = $id;
+} elsif (@ARGV > 1 and $ARGV[-1] =~ m{^(https?://[\w.-]+(:\d+)?/fup\?[sg]key=\w+)}) {
+  $fexcgi = $1;
+  $skey = $1 if $fexcgi =~ /skey=(\w+)/;
+  $gkey = $1 if $fexcgi =~ /gkey=(\w+)/;
+} else {
+
+  $fexcgi = $opt_u if $opt_u;
+  
+  if (not -e $idf and not ($fexcgi and $from and $id)) {
+    die "$0: no ID file $idf found, use \"fexsend -I\" to create it\n";
+  }
+  
+  unless ($fexcgi) {
+    die "$0: no FEX URL found, use \"$0 -u URL\" or \"$0 -I\"\n";
+  }
+  
+  unless ($from and $id) {
+    die "$0: no sender found, use \"$0 -f FROM:ID\" or \"$0 -I\"\n";
+  }
+
+  if ($fexcgi !~ /^http/) {
+    if ($fexcgi =~ /:443/) { $fexcgi = "https://$fexcgi" }
+    else                   { $fexcgi = "http://$fexcgi" }
+  }
+
+}
+
+$server = $fexcgi;
+
+$port = 80;
+$port = 443 if $server =~ s{https://}{};
+$port = $1  if $server =~ s/:(\d+)//;
+
+if (0 and $port == 443) {
+  $opt_s and die "$0: cannot use -s with https due to stunnel bug\n"; 
+  $opt_g and die "$0: cannot use -g with https due to stunnel bug\n"; 
+}
+
+$server =~ s{http://}{};
+$server =~ s{/.*}{};
+
+# $chunksize = 4*k unless $chunksize;
+$chunksize *= M;
+
+if ($proxy) {
+  if    ($port == 80)  { $proxy_prefix = "http://$server" }
+  elsif ($port != 443) { $proxy_prefix = "http://$server:$port" }
+}
+
+# xx: special file exchange between own accounts
+if ($xx) {
+  my $transferfile = "$tmpdir/STDFEX";
+  # slot?
+  if ($0 eq 'xxx') {
+    $transferfile = "$tmpdir/xx:xxx";
+  } elsif (@ARGV and $ARGV[0] =~ /^:([\w.=+-]+)$/) {
+    $transferfile = "$tmpdir/xx:$1";
+    shift @ARGV;
+  }
+  open my $lock,'>>',$transferfile 
+    or die "$0: cannot write $transferfile - $!\n";
+  flock($lock,LOCK_EX|LOCK_NB)
+    or die "$0: $transferfile is locked by another process\n";
+  truncate $transferfile,0;
+  if (not @ARGV and -t) {
+    &get_xx($transferfile);
+  } else {
+    &send_xx($transferfile);
+  }
+  exit;
+} 
+
+# regular fexsend
+
+&inquire if $windoof and not @ARGV and not
+            ($opt_l or $opt_L or $opt_Q or $opt_A or $opt_U or $opt_I or
+             $opt_f or $opt_x or $opt_N);
+
+if (${'opt_.'}) {
+  $opt_C = "!SHORTMAIL! $opt_C";
+}
+
+if ($opt_n or $opt_C =~ /NOMAIL|!#!/) {
+  $nomail = 'NOMAIL';
+}
+
+unless ($skey or $gkey or $anonymous) {
+  if (not $opt_q and (
+    $opt_f||$opt_x||$opt_Q||$opt_l||$opt_L||$opt_U||$opt_z||$opt_Z||$opt_A
+    ||$opt_d||${'opt_!'}||${'opt_@'})
+  ) { warn "Server/User: $fexcgi/$from\n" }
+}
+
+if    ($opt_V and not @ARGV)           { exit }
+if    ($opt_f)                                 { &forward } 
+elsif ($opt_x)                                 { &modify } 
+elsif ($opt_N)                                 { &renotify } 
+elsif ($opt_Q)                                 { &query_quotas } 
+elsif ($opt_S)                                 { &query_settings } 
+elsif ($opt_l or $opt_L)               { &list } 
+elsif ($opt_U)                         { &show_URL } 
+elsif ($opt_z or $opt_Z or ${'opt_!'}) { &get_log } 
+elsif ($opt_A)                         { edit_address_book($from) }
+elsif (${'opt_@'})                     { &show_address_book } 
+elsif ($opt_d and $anonymous)          { &purge }
+elsif ($opt_d and $ARGV[-1] =~ /^\d+$/)        { &delete }
+else                                   { &send_fex }
+
+exit;
+
+
+# initialize ID file or show ID
+sub init_id {
+  my $tag;
+  my $proxy = '';
+  
+  if ($opt_I) {
+    $tag = shift @ARGV;
+    die $usage if @ARGV;
+  }
+  
+  $fexcgi = $from = $id = '';
+  
+  unless (-d $fexhome) {
+    mkdir $fexhome,0700 or die "$0: cannot create FEXHOME $fexhome - $!\n";
+  }
+
+  # show ID
+  if (not $tag and open $idf,$idf) {
+    if ($opt_i) {
+      while (<$idf>) {
+        last if /^\[$opt_i\]/;
+      }
+    }
+    $fexcgi = <$idf>;
+    $from   = <$idf>;
+    $id     = <$idf>;
+    close $idf;
+    if ($id) {
+      chomp($fexcgi,$from,$id);
+      $FEXID = encode_b64("$fexcgi $from $id");
+      if (-t STDIN) {
+        print "# hint: to edit the ID file $idf use \"$0 -I .\" #\n";
+        print "export FEXID=$FEXID\n";
+        print "history -d \$((HISTCMD-1));history -d \$((HISTCMD-1))\n";
+      } else {
+        print "FEXID=$FEXID\n";
+      }
+      exit;
+    } else {
+      die "$0: no ID data found\n";
+    }
+  }
+
+  if ($tag and $tag eq '.') { exec $ENV{EDITOR}||'vi',$idf }
+  
+  if ($tag) { print "F*EX server URL for [$tag]: " }
+  else      { print "F*EX server URL: " }
+  $fexcgi = <STDIN>;
+  $fexcgi =~ s/[\s\n]//g;
+  die "you MUST provide a FEX-URL!\n" unless $fexcgi;
+  if ($fexcgi =~ /\?/) {
+    $from = $1 if $fexcgi =~ /\bfrom=(.+?)(&|$)/i;
+    $id   = $1 if $fexcgi =~ /\bid=(.+?)(&|$)/i;
+    $skey = $1 if $fexcgi =~ /\bskey=(.+?)(&|$)/i;
+    $gkey = $1 if $fexcgi =~ /\bgkey=(.+?)(&|$)/i;
+    $fexcgi =~ s/\?.*//;
+  }
+  unless ($fexcgi =~ /^[_:=\w\-\.\/\@\%]+$/) {
+    die "\"$fexcgi\" is not a legal FEX-URL!\n";
+  }
+  $fexcgi =~ s:/fup/*$::;
+  print "proxy address (hostname:port or empty if none): ";
+  $proxy = <STDIN>;
+  $proxy =~ s/[\s\n]//g;
+  if ($proxy =~ /^[\w.-]+:\d+$/) { 
+    $proxy = "!$proxy";
+  } elsif ($proxy =~ /\S/) { 
+    die "wrong proxy address format\n";
+  } else { 
+    $proxy = "";
+  }
+  if ($proxy) {
+    print "proxy POST limit in MB (use 2048 if unknown): ";
+    $_ = <STDIN>;
+    if (/(\d+)/) {
+      $proxy .= "[$1]";
+    }
+  }
+  if ($skey) {
+    $from = 'SUBUSER';
+    $id = $skey;
+  } elsif ($gkey) {
+    $from = 'GROUPMEMBER';
+    $id = $gkey;
+  } else {
+    unless ($from) {
+      print "Your e-mail address as registered at $fexcgi: ";
+      $from = <STDIN>;
+      $from =~ s/[\s\n]//g;
+      die "you MUST provide your e-mail address!\n" unless $from;
+    }
+    unless ($from =~ /^[_:=\w\-\.\/\@\%\+]+$/) {
+      die "\"$from\" is not a legal e-mail address!\n";
+    }
+    unless ($id) {
+      print "Your auth-ID for $from at $fexcgi: ";
+      $id = <STDIN>;
+      $id =~ s/[\s\n]//g;
+      die "you MUST provide your ID!\n" unless $id;
+    }
+  }
+  if (open $idf,'>>',$idf) {
+    print {$idf} "\n[$tag]\n" if $tag and -s $idf;
+    print {$idf} "$fexcgi$proxy\n",
+                 "$from\n",
+                 "$id\n";
+    close $idf;
+    print "data written to $idf\n";
+  } else {
+    die "$0: cannot write to $idf - $!\n";
+  }
+}
+
+
+sub show_id {
+  my ($fexcgi,$from,$id);
+  if (open $idf,$idf) {
+    $fexcgi = <$idf>;
+    $from   = <$idf>;
+    $id     = <$idf>;
+    while (<$idf>) {
+      if (/^\[xx\]/) {
+        $fexcgi = <$idf>;
+        $from   = <$idf>;
+        $id     = <$idf>;
+      }
+    }
+    close $idf;
+    die "$0: too few data in $idf" unless defined $id;
+    chomp($fexcgi);
+    chomp($from);
+    chomp($id);
+    $FEXXX = encode_b64("$fexcgi $from $id");
+    if (-t STDIN) {
+      print "export FEXXX=$FEXXX\n";
+      print "history -d \$((HISTCMD-1));history -d \$((HISTCMD-1))\n";
+    } else {
+      print "FEXXX=$FEXXX\n";
+    }
+  } else {
+    die "$0: cannot read $idf - $!\n";
+  }
+}
+
+
+sub register {
+  my $fs = shift @ARGV or die $usage;
+  my $mail = shift @ARGV or die $usage;
+  my $port;
+  my ($server,$user,$id);
+
+  die "$0: $idf does already exist\n" if -e $idf;
+
+  if ($fs =~ /^https/) {
+    die "$0: cannot handle https at this time\n";
+  }
+
+  $fs =~ s{^http://}{};
+  $fs =~ s{/.*}{};
+  if ($fs =~ s/:(\d+)//) { $port = $1 }
+  else                   { $port = 80 }
+
+  tcpconnect($fs,$port);
+  sendheader("$fs:$port","GET $proxy_prefix/fur?user=$mail&verify=no HTTP/1.1");
+  http_response();
+
+  while (<$SH>) {
+    s/\r//;
+    printf "<-- $_"if $opt_v;
+    last if /^\s*$/;
+  }
+
+  while (<$SH>) {
+    s/\r//;
+    printf "<-- $_"if $opt_v;
+    if (m{http://(.*)/fup\?from=(.+)&ID=(.+)}) {
+      $server = $1;
+      $user = $2;
+      $id = $3;
+
+      if (open F,">$idf") {
+        print F "$server\n",
+                "$user\n",
+                "$id\n";
+        close F;
+        chmod 0600,$idf;
+        print "user data written to $idf\n";
+        print "you can now fex!\n";
+        exit;
+      } else {
+        die "$0: cannot write to $idf - $!\n";
+      }
+    }
+  }
+
+  die "$0: no account data received from F*EX server\n";
+
+}
+
+
+sub send_xx {
+  my $transferfile = shift;
+  my $file = '';
+  my (@r,@tar);
+  
+  $SIG{PIPE} = $SIG{INT} = sub {
+    unlink $transferfile;
+    exit 3;
+  };
+  
+  if ($0 eq 'xxx') { @tar = qw'tar -cv' }
+  else             { @tar = qw'tar -cvz' }
+
+  if (-t) {
+    if ("@ARGV" eq '-') {
+      # store STDIN to transfer file
+      shelldo("cat >> $transferfile");
+    } elsif (@ARGV) {
+      print "making tar transfer file $transferfile :\n";
+      # single file? then add this directly 
+      if (scalar @ARGV == 1) {
+        my ($dir,$file);
+        # strip path if not ending with /
+        if ($ARGV[0] =~ m:(.+)/(.+): and $2 !~ m:/$:) {
+          ($dir,$file) = ($1,$2);
+          chdir $dir or die "$0: $dir - $!\n";
+        } else {
+          $file = $ARGV[0];
+        }
+        if (-l $file) {
+          shelldo(@tar,qw'--dereference -f',$transferfile,$file);
+        } else {
+          shelldo(@tar,'-f',$transferfile,$file);
+        }
+      } else {
+        shelldo(@tar,'-f',$transferfile,@ARGV);
+      }
+      if ($?) {
+        unlink $transferfile;
+        if ($? == 2) {
+          die "$0: interrupted making tar transfer file\n";
+        } else {
+          die "$0: error while making tar transfer file\n";
+        }
+      }
+    }
+  } else {
+    # write input from pipe to transfer file
+    shelldo("cat >> $transferfile");
+  }
+
+  die "$0: no transfer file\n" unless -s $transferfile;
+  
+  serverconnect($server,$port);
+  query_sid($server,$port);
+  
+  @r = formdatapost(
+    from       => $from,
+    to         => $from,
+    id         => $sid,
+    file       => $transferfile,
+    comment    => 'NOMAIL',
+    autodelete => $transferfile =~ /STDFEX/ ? 'NO' : 'DELAY',
+  );
+  
+  # open P,'|w3m -T text/html -dump' or die "$0: w3m - $!\n";
+  # print P @r;
+  http_response(@r);
+  if ($transferfile =~ /:/ and $0 ne 'xxx') {
+    if ("@r" =~ /\s(X-)?Location: (http.*)\s/) {
+      print "wget -O- $2 | tar xvzf -\n";
+    }
+  }
+  
+  unlink $transferfile;
+}
+
+
+sub query_quotas {
+  my (@r,$r);
+  local $_;
+
+  female_mode("query quotas?") if $opt_F;
+
+  @r = formdatapost(
+    from       => $from,
+    to         => $from,
+    id         => $sid,
+    command    => $opt_C, 
+  );
+  die "$0: no response from fex server $server\n" unless @r;
+  $_ = shift @r;
+  unless (/^HTTP.* 2/) {
+    s:HTTP/[\d\. ]+::;
+    die "$0: server response: $_\n";
+  }
+  if (($_) = grep(/^X-Sender-Quota/,@r) and /(\d+)\s+(\d+)/) {
+    print "sender quota (used): $1 ($2) MB\n";
+  } else {
+    print "sender quota: unlimited\n";
+  }
+  if (($_) = grep(/^X-Recipient-Quota/,@r) and /(\d+)\s+(\d+)/) {
+    print "recipient quota (used): $1 ($2) MB\n";
+  } else {
+    print "recipient quota: unlimited\n";
+  }
+}
+
+
+sub query_settings {
+  my (@r,$r);
+  local $_;
+
+  female_mode("query settings?") if $opt_F;
+
+  if ($FEXID) {
+    print "ID data from \$FEXID\n";
+  } elsif (-f $idf) {
+    print "ID data from $idf\n";
+  } else {
+    die "$0: found no ID\n";
+  }
+  print "server: $fexcgi\n";
+  print "user: $from\n";
+  print "auth-ID: $id\n";
+  print "login URL: ";
+  &show_URL;
+  
+  @r = formdatapost(
+    from       => $from,
+    to         => $from,
+    id         => $sid,
+    command    => $opt_C, 
+  );
+  die "$0: no response from fex server $server\n" unless @r;
+  $_ = shift @r;
+  unless (/^HTTP.* 2/) {
+    s:HTTP/[\d\. ]+::;
+    die "$0: server response: $_\n";
+  }
+  if (($_) = grep(/^X-Autodelete/,@r) and /:\s+(\w+)/) {
+    print "autodelete: $1\n";
+  }
+  if (($_) = grep(/^X-Default-Keep/,@r) and /(\d+)/) {
+    print "default keep: $1 days\n";
+  }
+  if (($_) = grep(/^X-Default-Locale/,@r) and /:\s+(\w+)/) {
+    print "default locale: $1\n";
+  }
+  if (($_) = grep(/^X-MIME/,@r) and /:\s+(\w+)/) {
+    print "display file with browser: $1\n";
+  }
+  if (($_) = grep(/^X-Sender-Quota/,@r) and /(\d+)\s+(\d+)/) {
+    print "sender quota (used): $1 ($2) MB\n";
+  } else {
+    print "sender quota: unlimited\n";
+  }
+  if (($_) = grep(/^X-Recipient-Quota/,@r) and /(\d+)\s+(\d+)/) {
+    print "recipient quota (used): $1 ($2) MB\n";
+  } else {
+    print "recipient quota: unlimited\n";
+  }
+}
+
+
+# list spool
+sub list {
+  my (@r,$r);
+  my ($data,$dkey,$n);
+  local $_;
+
+  female_mode("list spooled files?") if $opt_F;
+
+  if ($opt_l and $n = shift @ARGV and $n =~ /^\d+$/) {
+    open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
+    while (<$fexlist>) {
+      if (/^\s*(\d+)\) (\w+) (.+)/ and $1 eq $n) {
+        serverconnect($server,$port) unless $SH;
+        sendheader(
+          "$server:$port",
+          "GET $proxy_prefix/fop/$2/$2?LIST HTTP/1.1",
+          "User-Agent: $useragent",
+        );
+        $_ = <$SH>||'';
+        s/\r//;
+        print "<-- $_" if $opt_v;
+        if (/^HTTP.* 200/) {
+          print "<-- $_" if $opt_v;
+          while (<$SH>) {
+            s/\r//;
+            if (/^\n/) {
+              print;
+              print while <$SH>;
+            }
+          }
+        } elsif (s:HTTP/[\d\. ]+::) {
+          die "$0: server response: $_";
+        } else {
+          die "$0: no response from fex server $server\n";
+        }
+        exit;
+      }
+    }
+    die "$0: file \#$n not found in fexlist\n";
+  } else {
+    @r = formdatapost(
+      from     => $from,
+      to       => $opt_l ? '*' : $from,
+      command  => $opt_C, 
+    );
+  }
+  die "$0: no response from fex server $server\n" unless @r;
+  $_ = shift @r;
+  unless (/^HTTP.* 200/) {
+    s:HTTP/[\d\. ]+::;
+    die "$0: server response: $_\n";
+  }
+  
+  # list sent files
+  if ($opt_l) {
+    open $fexlist,">$fexlist" or die "$0: cannot write $fexlist - $!\n";
+    foreach (@r) {
+      next unless /<pre>/ or $data;
+      $data = 1;
+      last if m:</pre>:;
+      if (/<a href=".*dkey=(\w+).*?">/) { $dkey = $1 }
+      else                              { $dkey = '' }
+#      $_ = encode_utf8($_);
+      s/<.*?>//g;
+      if (/^(to .* :)/) {
+        print "\n$1\n";
+        print {$fexlist} "\n$1\n";
+      } elsif (m/(\d+) MB (.+)/) {
+        $n++;
+        printf "%4s) %8d MB %s\n","#$n",$1,$2;
+        printf {$fexlist} "%3d) %s %s\n",$n,$dkey,$2;
+      }
+    }
+    close $fexlist;
+  } 
+  
+  # list received files
+  if ($opt_L) {
+    foreach (@r) {
+      next unless /<pre>/ or $data;
+      $data = 1;
+      next if m:<pre>:;
+      last if m:</pre>:;
+      if (/(from .* :)/) {
+        print "\n$1\n";
+      }
+      if (m{(\d+) (MB.*)<a href="(https?://.*/fop/\w+/.+)">(.+)</a>( ".*")?}) {
+        printf "%8d %s%s%s\n",$1,$2,$3,($5||'');
+      }
+    }
+  }
+}
+
+
+sub show_URL {
+  printf "%s/fup/%s\n",$fexcgi,encode_b64("from=$from&id=$id");
+}
+
+
+sub get_log {
+  my (@r);
+  local $_;
+  
+  @r = formdatapost(
+    from       => $from,
+    to         => $from,
+    id         => $sid,
+    command    => $opt_C, 
+  );
+  die "$0: no response from fex server $server\n" unless @r;
+  $_ = shift @r;
+  unless (/^HTTP.* 200/) {
+    s:HTTP/[\d\. ]+::;
+    die "$0: server response: $_\n";
+  }
+  while (shift @r) {}
+  foreach (@r) { print "$_\n" }
+}
+
+
+sub show_address_book {
+  my (%AB,@r);
+  my $alias;
+  local $_;
+  
+  %AB = query_address_book($server,$port,$from);
+  foreach $alias (sort keys %AB) {
+    next if $alias eq 'ADDRESS_BOOK';
+    $_ = sprintf "%s = %s (%s) # %s\n",
+                 $alias,
+                 $AB{$alias},
+                 $AB{$alias}->{options},
+                 $AB{$alias}->{comment};
+    s/ \(\)//;
+    s/ \# $//;
+    print;
+  }
+}
+
+
+sub purge {
+  die "$0: not yet implemented\n";
+}
+
+
+sub delete {
+  my ($to,$file);
+
+  while (@ARGV) {
+    $opt_d = shift @ARGV;
+    die "$usage: $0 -d #\n" if $opt_d !~ /^\d+$/;
+  
+    open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
+    while (<$fexlist>) {
+      if (/^to (.+\@.+) :/) {
+        $to = $1;
+      } elsif (/^\s*(\d+)\) (\w+) (.+)/ and $1 eq $opt_d) {
+        serverconnect($server,$port) unless $SH;
+        sendheader(
+          "$server:$port",
+          "GET $proxy_prefix/fop/$2/$2?DELETE HTTP/1.1",
+          "User-Agent: $useragent",
+        );
+        $_ = <$SH>||'';
+        s/\r//;
+        print "<-- $_" if $opt_v;
+        if (/^HTTP.* 200/) {
+          while (<$SH>) {
+            s/\r//;
+            last if /^\n/; # ignore HTML output
+            print "<-- $_" if $opt_v;
+            if (/^X-File:.*\/(.+)/) {
+              printf "%s deleted\n",decode_utf8(urldecode($1));
+            }
+          }
+          undef $SH;
+        } elsif (s:HTTP/[\d\. ]+::) {
+          die "$0: server response: $_";
+        } else {
+          die "$0: no response from fex server $server\n";
+        }
+        last;
+      }
+    }
+    close $fexlist;
+    sleep 1; # do not overrun server
+  }
+
+  exit;
+}
+
+
+sub send_fex {
+  my @to;
+  my $file = '';
+  my @files = ();
+  my ($data,$aname,$alias);
+  my (@r,$r);
+  my $ma = $HOME.'/.mutt/aliases';
+  my $t0 = time;
+  my $transferfile;
+  my @transferfiles;
+  local $_;
+  
+  if ($from =~ /^SUBUSER|GROUPMEMBER$/) {
+    $to = '_';
+  } else {
+    # look for single @ in arguments
+    for (my $i=1; $i<$#ARGV; $i++) {
+      if ($ARGV[$i] eq '@') {
+        $ARGV[$i] = join(',',@ARGV[$i+1 .. $#ARGV]);
+        $#ARGV = $i;
+        last;
+      }
+    }
+    $to = pop @ARGV or die $usage;
+    if ($to eq '.') {
+      $to = $from;
+      $nomail = $opt_C ||= 'NOMAIL';
+    }
+    if ($to eq ':') {
+      $to = $from;
+      $nomail = $opt_C ||= 'NOMAIL';
+    }
+    if ($opt_g and $to =~ /,/) {
+      die "$0: encryption is supported to only one recipient\n";
+    }
+    if ($to =~ m{^https?://.*/fup\?skey=(\w+)}) {
+      $from = 'SUBUSER';
+      $to = '_';
+      $id = $1;
+    }
+    if ($to =~ m{^https?://.*/fup\?gkey=(\w+)}) {
+      $from = 'GROUPMEMBER';
+      $to = '_';
+      $id = $1;
+    }
+  }
+  @to = split(',',lc($to));
+  
+  die $usage unless @ARGV or $opt_a or $opt_s;
+  die $usage if $opt_s and @ARGV;
+
+  # early serverconnect necessary for X-Features info
+  serverconnect($server,$port);
+
+  if ($anonymous) {
+    my $aok;
+    sendheader("$server:$port","OPTIONS FEX HTTP/1.1");
+    $_ = <$SH>||'';
+    s/\r//;
+    die "$0: no response from fex server $server\n" unless $_;
+    print "<-- $_" if $opt_v;
+    if (/^HTTP.* 201/) {
+      while (<$SH>) {
+        s/\r//;
+        print "<-- $_" if $opt_v;
+        last unless /\w/;
+        $aok = $_ if /X-Features:.*ANONYMOUS/;
+      }
+      die "$0: no anonymous support on server $server\n" unless $aok;
+    } else {
+      die "$0: bad response from server $server : $_\n";
+    }
+  } elsif ($public) {
+  } else {
+    
+    query_sid($server,$port);
+    
+    if ($from eq 'SUBUSER') {
+      $skey = $sid;
+      # die "skey=$skey\nid=$id\nsid=$sid\n";
+    }
+
+    if ($from eq 'GROUPMEMBER') {
+      $gkey = $sid;
+    }
+    
+    if ($to eq '.') {
+      @to = ($from);
+      $opt_C ||= 'NOMAIL';
+    } elsif ($to =~ m:^(//.*):) {
+      my $xkey = $1;
+      if ($features =~ /XKEY/) {
+        @to = ($from);
+        $opt_C = $xkey;
+      } else {
+        die "$0: server does not support XKEY\n";
+      }
+    } elsif (grep /^[^@]*$/,@to and not $skey and not $gkey) {
+      %AB = query_address_book($server,$port,$from);
+      if ($proxy) {
+        serverconnect($server,$port);
+        query_sid($server,$port);
+      }
+      foreach $to (@to) {
+        # alias in local config?
+        if ($alias{$to}) {
+          if ($alias{$to} =~ /(.+?):(.+)/) {
+            my $ato = $1;
+            my $opt = $2;
+            my @argv = @_ARGV;
+            pop @argv;
+            # special extra upload
+            system $0,split(/\s/,$opt),@argv,$ato;
+            $to = '';
+          } else {
+            $to = $alias{$to};
+          }
+        }
+        # alias in server address book?
+        elsif ($AB{$to}) {  
+          # do not substitute alias with expanded addresses because then 
+          # keep and autodelete options from address book will get lost
+          # $to = $AB{$to};
+        } 
+        # look for mutt aliases
+        elsif ($to !~ /@/ and $to ne $from and open $ma,$ma) {
+          $alias = $to;
+          while (<$ma>) {
+            if (/^alias \Q$to\E\s/i) {
+              chomp;
+              s/\s*#.*//;
+              s/\(.*?\)//;
+              s/\s+$//;
+              s/.*\s+//;
+              s/[<>]//g;
+              if (/,/) {
+                warn "$0: ignoring mutt multi-alias $to = $alias\n";
+                last;
+              }
+              if (/@/) {
+                $alias = $_;
+                warn "$0: found mutt alias $to = $alias\n";
+                last;
+              }
+            }
+          }
+          close $ma;
+          $to = $alias;
+        }
+      }
+    }
+  
+    $to = join(',',grep /./,@to) or exit;
+    warn "Server/User: $fexcgi/$from\n" unless $opt_q;
+  
+    if (
+      not $skey and not $gkey
+      and $features =~ /CHECKRECIPIENT/ 
+      and $opt_C !~ /^(DELETE|LIST|RECEIVEDLOG|SENDLOG|FOPLOG)$/
+    ) {
+      checkrecipient($from,$to);
+      if ($proxy) {
+        serverconnect($server,$port);
+        query_sid($server,$port);
+      }
+    }
+  }
+
+  if (@ARGV > 1 and not ($opt_a or $opt_s or $opt_d)) {
+    print "Archive name (name.tar, name.tgz or name.zip) or [ENTER] to send file for file:\n";
+    $opt_a = <STDIN>;
+    $opt_a =~ s/^\s+//;
+    $opt_a =~ s/\s+$//;
+  }
+
+  if ($opt_s) {
+    $opt_s =~ s/^=//;
+    $opt_s =~ s:.*/::;
+    $opt_s =~ s/[^\w_.+-]/_/g;
+    @files = ($opt_s);
+  } elsif ($opt_a) {
+    $opt_a =~ s/^=//;
+    $opt_a =~ s:.*/::;
+    $opt_a =~ s/[^\w_.+-]/_/g;
+    if ($opt_a =~ /(.+)\.(zip|tar|tgz|7z)$/) {
+      $aname = $1;
+      $atype = $2;
+    } else {
+      die "$0: archive name must be one of ".
+          "$opt_a.tar $opt_a.tgz $opt_a.zip\n";
+    }
+    # no file argument left?
+    unless (@ARGV) {
+      # use file name as archive name
+      push @ARGV,$aname;
+      $opt_a =~ s:/+$::g;
+      $opt_a =~ s:.*/::g;
+    }
+    foreach my $file (@ARGV) {
+      die "$0: cannot read $file\n" unless -l $file or -r $file;
+    }
+    $opt_a .= ".$atype" if $opt_a !~ /\.$atype$/;
+    $transferfile = "$tmpdir/$opt_a";
+    unlink $transferfile;
+    print "Making fex archive ($opt_a):\n";
+    if ($atype eq 'zip') {
+      if ($windoof) {
+        # if ($opt_c) { system(qw'7z a -tzip',$transferfile,@ARGV) }
+        # else        { system(qw'7z a -tzip -mm=copy',$transferfile,@ARGV) }
+        system(qw'7z a -tzip',$transferfile,@ARGV);
+        @files = ($transferfile);
+      } else {
+        # zip archives must be < 2 GB, so split as necessary
+        @files = zipsplit($transferfile,@ARGV);
+        if (scalar(@files) == 1) {
+          $transferfile = $files[0];
+          $transferfile =~ s/_1.zip$/.zip/;
+          rename $files[0],$transferfile;
+          @files = ($transferfile);
+        }
+      }
+      @transferfiles =  @files;
+    } elsif ($atype eq '7z') {
+      # http://www.7-zip.org/
+      my @X = (); # exclude list
+      if (${'opt_#'}) {
+        foreach my $x (split('#',${'opt_#'})) {
+          push @X,"-x!$x";
+        }
+      }
+      if ($opt_c) { system(qw'7z a',@X,$transferfile,@ARGV) }
+      else        { system(qw'7z a -t7z -mx0',@X,$transferfile,@ARGV) }
+      @transferfiles = @files = ($transferfile);
+    } elsif ($atype eq 'tar') {
+      if ($windoof) {
+        system(qw'7z a -ttar',$transferfile,@ARGV);
+        @transferfiles = @files = ($transferfile);
+      } else {
+        ## tar is now handled by formdatapost()
+        # system(qw'tar cvf',$transferfile,@ARGV);
+        @files = ($opt_a);
+      }
+    } elsif ($atype eq 'tgz') {
+      if ($windoof) {
+        die "$0: archive type tgz not available, use tar, zip or 7z\n";
+      } else {
+        ## tgz is now handled by formdatapost()
+        # system(qw'tar cvzf',$transferfile,@ARGV);
+        @files = ($opt_a);
+      }
+    } else {
+      die "$0: unknown archive format \"$atype\"\n";
+    }
+    
+    if (@transferfiles) {
+      
+      # error in making transfer archive?
+      if ($?) {
+        unlink @transferfiles;
+        die "$0: $! - aborting upload\n";
+      }
+      
+      # maybe timeout, so make new connect
+      if (time-$t0 >= $timeout) {
+        serverconnect($server,$port);
+        query_sid($server,$port) unless $anonymous;
+      }
+      
+    }
+    
+  } else {
+    
+    unless (@ARGV) {
+      if ($windoof) {
+        &inquire;
+      } else {
+        die $usage;
+      }
+    }
+    
+    foreach (@ARGV) {
+      my $file = $_;
+      unless ($opt_d) {
+        unless (-f $file) {
+          if (-e $file) {
+            die "$0: $file is not a regular file, try option -a\n"
+          } else {
+            die "$0: $file does not exist\n";
+          }
+        }
+        die "$0: cannot read $file\n" unless -r $file;
+      }
+      push @files,$file;
+    }
+  }
+
+  if (${'opt_/'}) {
+    foreach my $file (@files) {
+      my @s = stat($file);
+      unless (@s and ($s[2] & S_IROTH) and -r $file) {
+        die "$0: $file is not world readable\n";
+      }
+    }
+  }
+  
+  foreach my $file (@files) {
+    sleep 1;    # do not overrun server!
+    unless (-s $file or $opt_d or $opt_a or $opt_s) {
+      die "$0: cannot send empty file $file\n";
+    }
+    female_mode("send file $file?") if $opt_F;
+    @r = formdatapost(
+      from             => $from,
+      to               => $to,
+      replyto          => $opt_r,
+      id               => $sid,
+      file             => $file,
+      keep             => $opt_k,
+      comment          => $opt_C,
+      autodelete       => $opt_D, 
+    );
+
+    if (not @r or not grep /\w/,@r) {
+      die "$0: no response from server\n";
+    }
+    if (($r) = grep /^ERROR:/,@r) {
+      if ($anonymous and $r =~ /purge it/) {
+        die "$0: file is already on server for $to - use another anonymous recipent\n";
+      } else {
+        $r =~ s/.*?:\s*//;
+        $r =~ s/<.+?>//g;
+        die "$0: server error: $r\n";
+      }
+    }
+    if (($r) = grep /<h3>\Q$file/,@r) {
+      $r =~ s/<.+?>//g;
+      print "$r\n";
+    }
+    if ($opt_a !~ /^afex_\d+\.tar$/ and $file !~ /afex_\d+\.tar$/) {
+      # print grep({s/^(X-Recipient:.*\((.+)\))/Parameters: $2\n/i} @r);
+      my $nonot = 0;
+      my ($recipient,$location);
+      foreach (@r) {
+        if (/^(X-)?(Recipient.*)/i) {
+          $recipient = $2;
+          if (/notification=no/i) { $nonot = 1 }
+          else                    { $nonot = 0 }
+        }
+        if (/^(X-)?(Location.*)/i) {
+          $location = $2;
+          if ($from eq $to or $from =~ /^\Q$to\E@/i 
+              or $nomail or $anonymous or $nonot) {
+            print "$recipient\n";
+            print "$location\n";
+          }
+        }
+      }
+    }
+  }
+  
+  # delete transfer tmp file
+  unlink $transferfile if $transferfile;
+}
+
+
+sub forward {
+  my (@r);
+  my ($to,$n,$dkey,$file,$req);
+  my $status = 1;
+  local $_;
+  
+  # look for single @ in arguments
+  for (my $i=1; $i<$#ARGV; $i++) {
+    if ($ARGV[$i] eq '@') {
+      $ARGV[$i] = join(',',@ARGV[$i+1 .. $#ARGV]);
+      $#ARGV = $i;
+      last;
+    }
+  }
+
+  # if ($windoof and not @ARGV) { &inquire }
+  $to = pop @ARGV or die $usage;
+  $to = $from if $to eq '.';
+
+  open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
+  while (<$fexlist>) {
+    if (/^\s*(\d+)\) (\w+) \[\d+ d\] (.+)/ and $1 eq $opt_f) {
+      $n = $1;
+      $dkey = $2;
+      $file = $3;
+      if ($file =~ s/ "(.*)"$//) {
+        $opt_C ||= $1 if $1 ne 'NOMAIL';
+      }
+      last;
+    }
+  }
+  close $fexlist;
+  
+  unless ($n) {
+    die "$0: file #$opt_f not found in fexlist\n";
+  }
+
+  female_mode("forward file #$opt_f?") if $opt_F;
+
+  serverconnect($server,$port);
+  query_sid($server,$port);
+  
+  $req = "GET $proxy_prefix/fup?"
+        ."from=$from&ID=$sid&to=$to&dkey=$dkey&command=FORWARD";
+  $req .= "&comment=$opt_C"    if $opt_C;
+  $req .= "&keep=$opt_k"       if $opt_k;
+  $req .= "&autodelete=$opt_D" if $opt_D;
+  $req .= "&$opt_X"            if $opt_X;
+  $req .= " HTTP/1.1";
+  sendheader("$server:$port",$req);
+  http_response();
+  while (<$SH>) { 
+    if ($opt_v) {
+      print;
+      $status = 0 if /\Q"$file"/;
+    } else {
+      if (/\Q"$file"/) {
+        print;
+        $status = 0;
+      }
+    }
+  }
+  
+  if ($status) {
+    die "$0: server failed, rerun command with option -v\n";
+  }
+  exit;
+}
+
+
+sub renotify {
+  my (@r);
+  my ($to,$n,$dkey,$file,$req,$recipient);
+  local $_;
+
+  die $usage if @ARGV;
+
+  open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
+  while (<$fexlist>) {
+    if (/^\s*(\d+)\) (\w+) \[\d+ d\] (.+)/ and $1 eq $opt_N) {
+      $n = $1;
+      $dkey = $2;
+      last;
+    }
+  }
+  close $fexlist;
+  
+  unless ($n) {
+    die "$0: file #$opt_N not found in fexlist\n";
+  }
+
+  female_mode("resend notification for file #$opt_N?") if $opt_F;
+
+  serverconnect($server,$port);
+  query_sid($server,$port);
+  
+  $req = "GET $proxy_prefix/fup?"
+        ."from=$from&ID=$sid&dkey=$dkey&command=RENOTIFY"
+        ." HTTP/1.1";
+  sendheader("$server:$port",$req);
+  http_response();
+  while (<$SH>) {
+    s/\r//;
+    print "<-- $_" if $opt_v;
+    last if /^\s*$/;
+    if (/^X-Notify: (.+)\/(.+)\/(.+)/) {
+      $recipient = $1;
+      $file = $3;
+    }
+  }
+  
+  if ($file) {
+    print "notification e-mail for $file has been resent to $recipient\n";
+  } else {
+    if ($opt_v) {
+      die "$0: server failed\n";
+    } else {
+      die "$0: server failed, rerun command with option -v\n";
+    }
+  }
+  
+  exit;
+}
+
+
+sub modify {
+  my (@r);
+  my ($n,$dkey,$file,$req);
+  local $_;
+  
+  die $usage if @ARGV;
+  die $usage unless $opt_C or $opt_k or $opt_D;
+  
+  open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
+  while (<$fexlist>) {
+    if (/^\s*(\d+)\) (\w+) \[\d+ d\] (.+)/ and $1 eq $opt_x) {
+      $n = $1;
+      $dkey = $2;
+      $file = $3;
+      $file =~ s/ "(.*)"$//;
+      last;
+    }
+  }
+  close $fexlist;
+  
+  unless ($n) {
+    die "$0: file #$opt_x not found in fexlist\n";
+  }
+
+  female_mode("modify file #$opt_x?") if $opt_F;
+  
+  serverconnect($server,$port);
+  query_sid($server,$port);
+  
+  $req = "GET $proxy_prefix/fup?"
+        ."from=$from&ID=$sid&dkey=$dkey&command=MODIFY";
+  $req .= "&comment=$opt_C"    if $opt_C;
+  $req .= "&keep=$opt_k"       if $opt_k;
+  $req .= "&autodelete=$opt_D" if $opt_D;
+  $req .= " HTTP/1.1";
+  sendheader("$server:$port",$req);
+  http_response();
+  while (<$SH>) { 
+    if ($opt_v) {
+      print "<-- $_";
+    } else {
+      print if /\Q$file/;
+    }
+  }
+  
+  exit;
+}
+
+
+sub get_xx {
+  my $transferfile = shift;
+  my $ft = '';
+  local $_;
+  
+  # get transfer file from FEX server
+  unless ($SH) {
+    serverconnect($server,$port);
+    query_sid($server,$port);
+  }
+  
+  xxget($from,$sid,$transferfile);
+  
+  # empty file?
+  unless (-s $transferfile) {
+    unlink $transferfile;
+    exit;
+  }
+  
+  # no further processing if delivering to pipe
+  exec 'cat',$transferfile unless -t STDOUT;
+  
+  if ($ft = `file $transferfile 2>/dev/null`) {
+    if ($ft =~ /compressed/) {
+      rename $transferfile,"$transferfile.gz";
+      shelldo(ws("gunzip $transferfile.gz"));
+    }
+    $ft = `file $transferfile`;
+  } 
+  # file command failed, so we look ourself into the file...
+  elsif (open $transferfile,$transferfile) {
+    read $transferfile,$_,4;
+    close $transferfile;
+    # gzip magic?
+    if (/\x1F\x8B\x08\x00/) {
+      rename $transferfile,"$transferfile.gz";
+      shelldo(ws("gunzip $transferfile.gz"));
+      # assuming tar
+      $ft = 'tar archive';
+    }
+  }
+  if ($ft =~ /tar archive/) {
+    rename $transferfile,"$transferfile.tar";
+    $transferfile .= '.tar';
+    if ($opt_q) {
+      $_ = 'y';
+    } else {
+      print "Files in transfer-container:\n\n";
+      shelldo(ws("tar tvf $transferfile"));
+      print "\nExtract these files? [Yn] ";
+      $_ = <STDIN>;
+    }
+    if (/^n/i) {
+      print "keeping $transferfile\n";
+    } else {
+      system("tar xvf $transferfile && rm $transferfile");
+      die "$0: error while untaring, see $transferfile\n" if -f $transferfile;
+    }
+  } else {
+    exec 'cat',$transferfile;
+  }
+  exit;
+}
+
+
+sub formdatapost {
+  my %P = @_; 
+  my ($boundary,$filename,$filesize,$length,$buf,$file,$fpsize,$resume,$seek);
+  my ($flink);
+  my (@hh,@hb,@r,@pv,$to);
+  my ($bytes,$t,$bt);
+  my ($t0,$t1,$t2,$tt,$tc);
+  my $bs = 2**16;        # blocksize for reading and sending file
+  my $fileid = int(time);
+  my $chunk = 0;
+  my $connection = '';
+  my $pct = '';
+  my ($tar,$aname,$atype,$tarlist,$tarerror,$location,$transferfile);
+  local $_;
+
+  if (defined($file = $P{file})) {
+    
+    $to = $AB{$P{to}} || $P{to}; # for gpg
+    
+    # special file: stream from STDIN
+    if ($opt_s) {
+      $filename = encode_utf8($file);
+      $filesize = -1;
+    }
+    
+    # compression?
+    if ($opt_c) {
+      my ($if,$of);
+      $if = $file;
+      $if =~ s/([^_\w\.\-])/\\$1/g;
+      $transferfile = $tmpdir . '/' . basename($file) . '.gz';
+      $of = $transferfile;
+      $of =~ s/([^_\w\.\-])/\\$1/g;
+      shelldo("gzip <$if>$of");
+      $filesize = -s $transferfile;
+      die "$0: cannot gzip $file\n" unless $filesize;
+      $file = $transferfile;
+    } 
+    
+    # special file: tar-on-the-fly
+    if (not $windoof and $opt_a and $file =~ /(.+)\.(tar|tgz)$/) {
+      $aname = $1;
+      $atype = $2;
+      $tarlist  = "$tmpdir/$aname.list";
+      $tarerror = "$tmpdir/$aname.error";
+      $tar = 'tar -cv';
+      $tar .= 'z' if $atype eq 'tgz';
+      if (`tar --help 2>/dev/null` =~ /--index-file/) {
+        $tar .= " --index-file=$tarlist -f-";
+      } else {
+        $tar .= " -f-";
+      }
+      if (${'opt_#'}) {
+        foreach my $x (split('#',${'opt_#'})) {
+          $tar .= " --exclude=$x";
+        }
+      }
+      foreach (@ARGV) {
+        $file = $_;
+        $file =~ s/([^\w\-\@\#%,.=+~_:])/\\$1/g;
+        $tar .= ' '.$file;
+      }
+      # print "calculating archive size... ";
+      open $tar,"$tar 2>$tarerror|" or die "$0: cannot run tar - $!\n";
+      $t0 = int(time) if -t STDOUT;
+      while ($b = read $tar,$_,$bs) {
+        $filesize += $b;
+        if ($t0) {
+          $t1 = int(time);
+          if ($t1>$t0) {
+            printf "Archive size: %d MB\r",int($filesize/M);
+            $t0 = $t1;
+          }
+        }
+      }
+      printf "Archive size: %d MB\n",int($filesize/M) if -t STDOUT;
+      unless (close $tar) {
+        $_ = '';
+        if (open $tarerror,$tarerror) {
+          local $/;
+          $_ = <$tarerror>;
+          close $tarerror;
+        }
+        unlink $tarlist,$tarerror;
+        die "$0: tar error:\n$_";
+      }
+      $file = "$aname.$atype";
+      $filename = encode_utf8($file);
+      undef $SH; # force reconnect (timeout!)
+    } 
+    
+    # single file
+    else {
+      $filename = encode_utf8(${'opt_='} || $file);
+    
+      if ($windoof) {
+        $filename =~ s/^[a-z]://;
+        $filename =~ s/.*\\//;
+      }
+      $filename =~ s:.*/::;
+      $filename =~ s:[\r\n]+: :g;
+      if ($opt_d) {
+        $filesize = 0;
+      } elsif (not $opt_g and not $opt_s) {
+        $filesize = -s $file or die "$0: $file is empty or not readable\n";
+      }
+    }
+
+    $filename .= '.gpg' if $opt_g;
+
+    unless ($opt_d) {
+      if ($opt_g) {
+        $filesize = -1;
+        $fileid = int(time);
+      } else {
+        if ($opt_a) {
+          $fileid = md5_hex(fmd(@ARGV));
+        } else {
+          $fileid = fileid($file);
+        }
+      }
+    }
+  
+  } else {
+    $file = $filename = '';
+    $filesize = 0;
+  }
+
+  FORMDATAPOST:
+    
+  @hh = (); # HTTP header
+  @hb = (); # HTTP body
+  @r = ();
+  $seek = 0;
+  $resume = '';
+  $chunk++;
+
+  unless ($SH) {
+    serverconnect($server,$port);
+    query_sid($server,$port) unless $anonymous;
+  }
+  
+  $P{id} = $sid; # ugly hack!
+  
+  # ask server if this file has been already sent
+  if ($file and not $xx and not 
+      ($opt_s or $opt_g or $opt_o or $opt_d or $opt_l or $opt_L or ${'opt_/'}))
+  {
+    ($seek,$location) = query_file($server,$port,$frecipient||$P{to},$P{from},
+                                   $P{id},$filename,$fileid);
+    if ($filesize == $seek) {
+      print "Location: $location\n" if $location and $nomail;
+      warn "$0: $file has been already transferred\n";
+      return $file;
+    } elsif ($seek and $seek < $filesize) {
+      $resume = " (resuming at byte $seek)";
+    } elsif ($filesize <= $seek) {
+      $seek = 0;
+    }
+    if ($proxy) {
+      sleep 1;    # do not overrun proxy
+      serverconnect($server,$port);
+    }
+  }
+  
+  # file part size
+  if ($chunksize and $proxy and $port != 443 
+      and $filesize - $seek > $chunksize - $bs) {
+    if ($features !~ /MULTIPOST/) {
+      die sprintf("$0: server does not support chunked multi-POST needed for"
+                  ." files > %d MB via proxy\n",$chunksize/M);
+    }
+    $opt_o = 0; # no overwriting mode for next chunks
+    $fpsize = $chunksize - $bs;
+  } else {
+    $fpsize = $filesize - $seek;
+  }
+
+  $boundary = randstring(48);
+  
+  $P{seek} = $seek;
+  $P{filesize} = $filesize;
+
+  # send HTTP POST variables
+  if ($skey) {
+    $P{skey} = $skey;
+    @pv = qw'from to skey keep autodelete comment seek filesize';
+  } elsif ($gkey) {
+    $P{gkey} = $gkey;
+    @pv = qw'from to gkey keep autodelete comment seek filesize';
+  } else {
+    @pv = qw'from to id replyto keep autodelete comment command seek filesize';
+  }
+  foreach my $v (@pv) {
+    if ($P{$v}) {
+      my $name = uc($v);
+      push @hb,"--$boundary";
+      push @hb,"Content-Disposition: form-data; name=\"$name\"";
+      push @hb,"";
+      push @hb,encode_utf8($P{$v});
+    }
+  }
+  
+  # at last, POST the file
+  if ($file) {
+    push @hb,"--$boundary";
+    push @hb,"Content-Disposition: form-data; name=\"FILE\"; filename=\"$filename\"";
+    unless ($opt_d) {
+      if ($opt_M) { push @hb,"Content-Type: application/x-mime" }
+      else        { push @hb,"Content-Type: application/octet-stream" }
+      if (${'opt_/'}) {
+        $flink = abs_path($file);
+        push @hb,"Content-Location: $flink";
+      } else {
+        # push @hb,"Content-Length: " . ((-s $file||0) - $seek); # optional header!
+        push @hb,"Content-Length: $fpsize"; # optional header! NOT filesize!
+        push @hb,"X-File-ID: $fileid";
+      }
+      push @hb,"";
+    }
+    push @hb,"";
+    # prevent proxy chunked mode reply
+    $connection = "close";
+  }
+
+  push @hb,"--$boundary--";
+
+  if ($fpsize < 0) {
+    $length = $fpsize;
+  } else {
+    $length = length(join('',@hb)) + scalar(@hb)*2 + $fpsize;
+  }
+
+  if ($file and not $opt_d) {
+    if ($flink) { $hb[-2] = $flink }
+    else        { $hb[-2] = '(file content)' }
+  }
+  # any other extra URL arguments
+  my $opt_X = '';
+  $opt_X = "?$::opt_X" if $::opt_X and $file;
+
+  # HTTP header
+  push @hh,"POST $proxy_prefix/fup$opt_X HTTP/1.1";
+  push @hh,"Host: $server:$port";
+  push @hh,"User-Agent: $useragent";
+  push @hh,"Content-Length: $length";
+  push @hh,"Content-Type: multipart/form-data; boundary=$boundary";
+  push @hh,"Connection: $connection" if $connection;
+  push @hh,'';
+
+  if ($opt_v) {
+    print "--> $_\n" foreach (@hh,@hb);
+  }
+
+  $SIG{PIPE} = \&sigpipehandler;
+#    foreach $sig (keys %SIG) {
+#      eval '$SIG{$sig} = sub { print "\n!!! SIGNAL '.$sig.' !!!\n"; exit; }';
+#    }
+
+  if ($file) {
+    pop @hb;
+    pop @hb unless $flink;
+    nvtsend(@hh,@hb) or do {
+      warn "$0: server has closed the connection, reconnecting...\n";
+      sleep 3;
+      goto FORMDATAPOST; # necessary: new $sid ==> new @hh
+    };
+    
+    unless ($opt_d or $flink) {
+      
+      $t0 = $t2 = int(time);
+      $tt = $t0-1;
+      $t1 = 0;
+      $tc = 0;
+      
+      if ($opt_s) {
+        if ($opt_g) {
+          open $file,"gpg -e -r $to|" or die "$0: cannot run gpg - $!\n";
+        } else {
+          open $file,'>&=STDIN' or die "$0: cannot open STDIN - $!\n";
+        }
+      } elsif ($tar) {
+        if ($opt_g) {
+          open $file,"$tar|gpg -e -r $to|" or die "$0: cannot run tar&gpg - $!\n";
+        } else {
+          open $file,"$tar|" or die "$0: cannot run tar - $!\n";
+        }
+        if (-t STDOUT) {
+          $tpid = fork();
+          if (defined $tpid and $tpid == 0) {
+            sleep 1;
+            if (open $tarlist,$tarlist) {
+              # print "\n$tar|\n"; system "ls -l $tarlist";
+              while ($tarlist) {
+                while (<$tarlist>) {
+                  print ' 'x(length($file)+40),"\r",$_;
+                }
+                sleep 1;
+              }
+            }
+            exit;
+          }
+          $SIG{CHLD} = 'IGNORE';
+        }
+        if ($seek) {
+          print "Fast forward to byte $seek (resuming)\n";
+          readahead($file,$seek);
+        }
+      } else {
+        if ($opt_g) {
+          my $fileq = $file;
+          $fileq =~ s/([^\w\-\@\#%,.=+~_:])/\\$1/g;
+          open $file,"gpg -e -r $to <$fileq|" or die "$0: cannot run gpg - $!\n";
+        } else {
+          open $file,$file or die "$0: cannot read $file - $!\n";
+          seek $file,$seek,0;
+        }
+        binmode $file;
+      }
+      
+      $bytes = 0;
+      autoflush $SH 0;
+      
+      print $rcamel[0] if ${'opt_+'};
+
+      while (my $b = read $file,$buf,$bs) {
+        print {$SH} $buf or &sigpipehandler;
+        $bytes += $b;
+        if ($filesize > 0 and $bytes+$seek > $filesize) {
+          die "$0: $file filesize has grown while uploading\n";
+        }
+        $bt += $b;
+        $t2 = time;
+        if (${'opt_+'} and int($t2*10)>$tc) {
+          print $rcamel[$tc%2+1];
+          $tc = int($t2*10);
+        }
+        if (not $opt_q and -t STDOUT and int($t2)>$t1) {
+          &sigpipehandler unless $SH->connected;
+          # smaller block size is better on slow links
+          $bs = 4096 if $t1 and $bs>4096 and $bytes/($t2-$t0)<65536;
+          if ($filesize > 0) {
+            $pct = sprintf "(%d%%)",int(($bytes+$seek)/$filesize*100);
+          }
+          if ($bytes>2*M and $bs>4096) {
+            printf STDERR "%s: %d MB of %d MB %s %d kB/s        \r",
+                   $opt_s||$opt_a||$file,
+                   int(($bytes+$seek)/M),
+                   int($filesize/M),
+                   $pct,
+                   int($bt/k/($t2-$tt));
+          } else {
+            printf STDERR "%s: %d kB of %d MB %s %d kB/s        \r",
+                   $opt_s||$opt_a||$file,
+                   int(($bytes+$seek)/k),
+                   int($filesize/M),
+                   $pct,
+                   int($bt/k/($t2-$tt));
+          }
+          $t1 = $t2;
+          # time window for transfer rate calculation
+          if ($t2-$tt>10) {
+            $bt = 0;
+            $tt = $t2;
+          }
+        }
+        last if $filesize > 0 and $bytes >= $fpsize;
+        sleep 1 while ($opt_m and $bytes/k/(time-$t0||1) > $opt_m);
+      }
+      close $file; # or die "$0: error while reading $file - $!\n";
+      $tt = ($t2-$t0)||1;
+      
+      print $rcamel[2] if ${'opt_+'};
+      
+      # terminate tar verbose output job
+      if ($tpid) {
+        sleep 2;
+        kill 9,$tpid;
+        unlink $tarlist;
+      }
+    
+      unless ($opt_q) {
+        if (not $chunksize and $bytes+$seek < $filesize) {
+          die "$0: $file filesize has shrunk while uploading\n";
+        }
+        
+        if ($seek or $chunksize and $chunksize < $filesize) {
+          if ($fpsize>2*M) {
+            printf STDERR "%s: %d MB in %d s (%d kB/s)",
+                           $opt_s||$opt_a||$file,
+                           int($bytes/M),
+                           $tt,
+                           int($bytes/k/$tt);
+            if ($bytes+$seek == $filesize) {
+              printf STDERR ", total %d MB\n",int($filesize/M);
+            } else {
+              printf STDERR ", chunk #%d : %d MB\n",
+                            $chunk,int(($bytes+$seek)/M);
+            }
+          } else {
+            printf STDERR "%s: %d kB in %d s (%d kB/s)",
+                          $opt_s||$opt_a||$file,
+                          int($bytes/k),
+                          $tt,
+                          int($bytes/k/$tt);
+            if ($bytes+$seek == $filesize) {
+              printf STDERR ", total %d kB\n",int($filesize/k);
+            } else {
+              printf STDERR ", chunk #%d : %d kB\n",
+                            $chunk,int(($bytes+$seek)/k);
+            }
+          }
+        } else {
+          if ($bytes>2*M) {
+            printf STDERR "%s: %d MB in %d s (%d kB/s)        \n",
+                          $opt_s||$opt_a||$file,
+                          int($bytes/M),
+                          $tt,
+                          int($bytes/k/$tt);
+          } else {
+            printf STDERR "%s: %d kB in %d s (%d kB/s)        \n",
+                          $opt_s||$opt_a||$file,
+                          int($bytes/k),
+                          $tt,
+                          int($bytes/k/$tt);
+          }
+        }
+        
+        if (-t STDOUT and not ($opt_s or $opt_g)) {
+          print STDERR "waiting for server ok..."
+        }
+      }
+    }
+    
+    autoflush $SH 1;
+    print {$SH} "\r\n--$boundary--\r\n";
+
+    # special handling of streaming file because of stunnel tcp shutdown bug
+    if ($opt_s or $opt_g) {
+      close $SH;
+      sleep 1;
+      serverconnect($server,$port);
+      query_sid($server,$port) unless $anonymous;
+      ($seek,$location) = query_file($server,$port,$P{to},$P{from},$sid,
+                                     $filename,$fileid);
+      if ($seek != $bytes) {
+        die "$0: streamed $bytes bytes but server received $seek bytes\n";
+      }
+      return "X-Location: $location\n";
+    }
+    
+    if ($flink) {
+      $bytes = -s $flink;
+      if ($bytes>2*M) {
+        printf STDERR "%s: %d MB\n",$flink,int($bytes/M);
+      } else {
+        printf STDERR "%s: %d kB\n",$flink,int($bytes/k);
+      }
+    }
+  } else {
+    autoflush $SH 1;
+    nvtsend(@hh,@hb);
+  }
+
+  # SuSe: Can't locate object method "BINMODE" via package "IO::Socket::SSL::SSL_HANDLE"
+  # binmode $SH,':utf8'; 
+  
+  if (not $opt_q and $file and -t STDOUT) {
+    print STDERR "\r                         \r";
+  }
+  while (<$SH>) {
+    s/[\r\n]+//;
+    print "<-- $_\n" if $opt_v;
+    last if @r and $r[0] =~ / 204 / and /^$/ or /<\/html>/i;
+    push @r,decode_utf8($_);
+  }
+  
+  if ($file) {
+    close $SH;
+    undef $SH;
+    if ($proxy and $fpsize+$seek < $filesize) {
+      goto FORMDATAPOST;
+    }
+  }
+  
+  return @r;
+}
+
+
+sub randstring {
+    my $n = shift;
+    my @rc = ('A'..'Z','a'..'z',0..9 );
+    my $rn = @rc;
+    my $rs;
+
+    for (1..$n) { $rs .= $rc[int(rand($rn))] };
+    return $rs;
+}
+
+
+sub zipsplit {
+  my $zipbase = shift;
+  my @files = @_;
+  my @zipfiles = ();
+  my $file;
+  my ($zsize,$size,$n);
+
+  $zipbase =~ s/\.zip$//;
+  map { s/([^_\w\+\-\.])/\\$1/g } @files;
+
+  open my $ff,"find @files|" or die "$0: cannot search for @_ - $!\n";
+  @files = ();
+
+  zipfile: for (;;) {
+    $n++;
+    if ($n eq 10) {
+      unlink @zipfiles;
+      die "$0: too many zip-archives\n";
+    }
+    $zsize = 0;
+    while ($file = <$ff>) {
+      chomp $file;
+      # next if -l $file or not -f $file;
+      next unless -f $file;
+      $size = -s $file;
+      if ($size > 2147480000) {
+        unlink @zipfiles;
+        die "$0: $file too big for zip\n";
+      }
+      if ($zsize + $size > 2147000000) {
+        push @zipfiles,zip($zipbase.'_'.$n.'.zip',@files);
+        @files = ($file);
+        next zipfile;
+      } else {
+        push @files,$file;
+        $zsize += $size;
+      }
+    }
+    close $ff;
+    last;
+  }
+  push @zipfiles,zip($zipbase.'_'.$n.'.zip',@files);
+  return @zipfiles;
+}
+
+
+sub zip {
+  no strict 'refs';
+  my $zip = shift;
+  my $cmd;
+  local $_;
+
+  unlink $zip;
+  # if ($opt_c) { $cmd = "zip -@ $zip" }
+  # else        { $cmd = "zip -0 -@ $zip" }
+  $cmd = "zip -@ $zip";
+  if (${'opt_#'}) {
+    ${'opt_#'} =~ s/#/ /g;
+    $cmd .= " -x ".${'opt_#'};
+  }
+  print $cmd,"\n" if $opt_v;
+  open $cmd,"|$cmd" or die "$0: cannot create $zip - $!\n";
+  foreach (@_) { 
+    print {$cmd} $_."\n";
+    print "  $_\n" if $opt_v;
+  }
+  close $cmd or die "$0: zip failed - $!\n";
+
+  return $zip;
+}
+
+
+sub getline {
+  my $file = shift;
+  local $_;
+  
+  while (<$file>) {
+    chomp;
+    s/^#.*//;
+    s/\s+#.*//;
+    s/^\s+//;
+    s/\s+$//;
+    return $_ if length($_);
+  }
+  return '';
+}
+
+
+sub query_file {
+  my ($server,$port,$to,$from,$id,$filename,$fileid) = @_;
+  my $seek = 0;
+  my $qfileid = '';
+  my ($head,$location);
+  my ($response,$fexsrv);
+  local $_;
+  
+  $to =~ s/,.*//;
+  $to =~ s/:\w+=.*//;
+  $to = $AB{$to} if $AB{$to};
+  $filename =~ s/([^_=:,;<>()+.\w\-])/'%'.uc(unpack("H2",$1))/ge; # urlencode
+  if ($skey) {
+    $head = "HEAD $proxy_prefix/fop/$to/$from/$filename??SKEY=$id HTTP/1.1";
+  } elsif ($gkey) {
+    $head = "HEAD $proxy_prefix/fop/$to/$from/$filename??GKEY=$id HTTP/1.1";
+  } else {
+    $head = "HEAD $proxy_prefix/fop/$to/$from/$filename??ID=$id HTTP/1.1";
+  }
+  sendheader("$server:$port",$head);
+  $_ = <$SH>;
+  unless (defined $_ and /\w/) {
+    die "$0: no response from server\n";
+  }
+  s/\r//;
+  print "<-- $_" if $opt_v;
+  unless (/^HTTP.* 200/) {
+    s:HTTP/[\d\. ]+::;
+    $response = $_;
+    while (<$SH>) {
+      s/\r//;
+      print "<-- $_" if $opt_v;
+      $fexsrv = $_ if /^(Server: fexsrv|X-Features:)/;
+      last if /^\s*$/;
+    }
+    die "$0: no fexserver at $server:$port\n" unless $fexsrv;
+    die "$0: server response: $response";
+  }
+  while (<$SH>) {
+    s/\r//;
+    print "<-- $_" if $opt_v;
+    last if /^$/;
+    if (/^Content-Length:\s+(\d+)/)    { $seek = $1 }
+    if (/^X-File-ID:\s+(.+)/)          { $qfileid = $1 }
+    if (/^X-Features:\s+(.+)/)         { $features = $1 }
+    if (/^X-Location:\s+(.+)/)         { $location = $1 }
+  }
+
+  # return true seek only if file is identified
+  $seek = 0 if $qfileid and $qfileid ne $fileid;
+  
+  return ($seek,$location);
+}
+
+
+sub edit_address_book {
+  my ($user) = @_;
+  my $alias;
+  my $ab = "$fexhome/ADDRESS_BOOK";
+  my (%AB,@r);
+  local $_;
+  
+  die "$0: address book not available for subusers\n"      if $skey;
+  die "$0: address book not available for group members\n" if $gkey;
+
+  female_mode("edit your address book?") if $opt_F;
+
+  %AB = query_address_book($server,$port,$user);
+  if ($AB{ADDRESS_BOOK} !~ /\w/) {
+    $AB{ADDRESS_BOOK} = 
+      "# Format: alias e-mail-address # Comment\n".
+      "# Example:\n".
+      "framstag framstag\@rus.uni-stuttgart.de\n";
+  }
+  open $ab,">$ab" or die "$0: cannot write to $ab - $!\n";
+  print {$ab} $AB{ADDRESS_BOOK};
+  close $ab;
+  
+  system $editor,$ab;
+  exit unless -s $ab;
+
+  $opt_o = $opt_A;
+  
+  serverconnect($server,$port);
+  query_sid($server,$port);
+  
+  @r = formdatapost(
+       from            => $user,
+        to             => $user,
+        id             => $sid,
+        file           => $ab,
+  );
+  
+  unlink $ab,$ab.'~';
+}
+
+
+sub query_address_book {
+  my ($server,$port,$user) = @_;
+  my ($req,$alias,$address,$options,$comment,$cl,$ab,$b);
+  my %AB;
+  local $_;
+
+  unless ($SH) {
+    serverconnect($server,$port);
+    query_sid($server,$port);
+  }
+  
+  $req = "GET $proxy_prefix/fop/$user/$user/ADDRESS_BOOK?ID=$sid HTTP/1.1";
+  sendheader("$server:$port",$req);
+  $_ = <$SH>;
+  unless (defined $_ and /\w/) {
+    die "$0: no response from server\n";
+  }
+  s/\r//;
+  print "<-- $_" if $opt_v;
+  unless (/^HTTP.* 200/) {
+    if (/^HTTP.* 404/) {
+      while (<$SH>) { last if /^\r?\n/ }
+      return;
+    } else {
+      # s:HTTP/[\d\. ]+::;
+      # die "$0: server response: $_";
+      close $SH;
+      undef $SH;
+      return ();
+    }
+  }
+  while (<$SH>) {
+    s/\r//;
+    print "<-- $_" if $opt_v;
+    last if /^$/;
+    $cl = $1 if /^Content-Length: (\d+)/;
+  }
+  
+  if ($cl) {
+    while (<$SH>) {
+      $b += length;
+      $ab .= $_;
+      s/[\r\n]//g;
+      s/^\s+//;
+      s/\s+$//;
+      print "<-- $_\n" if $opt_v;
+      s/\s*#\s*(.*)//;
+      if ($_) {
+        $comment = $1||'';
+        ($alias,$address,$options) = split;
+        if ($address) {
+          if ($options) { $options =~ s/[()]//g }
+          else          { $options = '' }
+          $AB{$alias} = $address;
+          $AB{$alias}->{options} = $options||'';
+          $AB{$alias}->{comment} = $comment||'';
+          if ($options and $options =~ /keep=(\d+)/i) {
+            $AB{$alias}->{keep} = $1;
+          }
+          if ($options and $options =~ /autodelete=(\w+)/i) {
+            $AB{$alias}->{autodelete} = $1;
+          }
+        }
+      }
+      last if $b >= $cl;
+    }
+  }
+  
+  $AB{ADDRESS_BOOK} = $ab;
+  
+  return %AB;
+}
+
+
+# sets global $sid $features $timeout # ugly hack! :-}
+sub query_sid {
+  my ($server,$port) = @_;
+  my ($req,$fexsrv);
+  local $_;
+
+  $sid = $id;
+
+  if ($port eq 443) {
+    return if $features;    # early return if we know enough
+    $req = "OPTIONS FEX HTTP/1.1";
+  } elsif ($proxy) {
+    return if $features;    # early return if we know enough
+    $req = "GET $proxy_prefix/SID HTTP/1.1";
+  } else {
+    $req = "GET SID HTTP/1.1";
+  }
+
+  sendheader("$server:$port",$req,"User-Agent: $useragent");
+  $_ = <$SH>;
+  unless (defined $_ and /\w/) {
+    print "\n" if $opt_v;
+    die "$0: no response from server\n";
+  }
+  s/\r//;
+  print "<-- $_" if $opt_v;
+    
+  if (/^HTTP.* [25]0[01] /) {
+    if (not $proxy and $port ne 443 and /^HTTP.* 201 (.+)/) {
+      $sid = 'MD5H:'.md5_hex($id.$1);
+    }
+    while (<$SH>) {
+      s/\r//;
+      print "<-- $_" if $opt_v;
+      $features = $1 if /^X-Features: (.+)/;
+      $timeout = $1  if /^X-Timeout: (\d+)/;
+      last if /^\n/;
+    }
+  } elsif (/^HTTP.* 301 /) {
+    while (<$SH>) { last if /Location/ }
+    die "$0: cannot use $server:$port because server has a redirection to\n".$_;
+  } else {
+    # no SID support - perhaps transparent web proxy?
+    while (<$SH>) {
+      s/\r//;
+      print "<-- $_" if $opt_v;
+      $fexsrv = $_ if /^(Server: fexsrv|X-Features:)/;
+      last if /^\s*$/;
+    }
+    die "$0: no fexserver at $server:$port\n" unless $fexsrv;
+    serverconnect($server,$port);
+    $sid = $id;
+  }
+  
+  # warn "proxy: $proxy\n";
+  if ($proxy) {
+    serverconnect($server,$port);
+    $sid = $id;
+  }
+  
+}
+
+
+sub xxget {
+  my ($from,$id,$save) = @_;
+  my $bs = 4096;
+  my $xx = $save;
+  my ($url,$B,$b,$t0,$t1,$cl);
+  my ($ts,$tso);
+  local $_;
+
+  $xx =~ s:.*/::;
+  $url = "$proxy_prefix/fop/$from/$from/$xx?ID=$id";
+
+  sendheader("$server:$port","GET $url HTTP/1.0","User-Agent: $useragent");
+  http_response();
+  while (<$SH>) {
+    s/\r//;
+    print "<-- $_" if $opt_v;
+    $cl = $1 if /^Content-Length:\s(\d+)/;
+    # $ft = $1 if /^X-File-Type:\s(.+)/;
+    last if /^$/;
+  }
+
+  die "$0: no Content-Length in server-reply\n" unless $cl;
+  
+  open F,">$save" or die "$0: cannot write to $save - $!\n";
+  binmode F;
+  
+  $t0 = $t1 = int(time);
+  $tso = '';
+  
+  while ($b = read($SH,$_,$bs)) {
+    $B += $b;
+    print F;
+    if (int(time) > $t1) {
+      $t1 = int(time);
+      $ts = ts($B,$cl);
+      if ($ts ne $tso) {
+        print STDERR $ts,"\r";
+        $tso = $ts;
+      }
+    }
+    sleep 1 while ($opt_m and $B/k/(time-$t0||1) > $opt_m);
+  }
+  
+  print STDERR ts($B,$cl),"\n";
+  close F;
+}
+
+
+# transfer status
+sub ts {
+  my ($b,$tb) = @_;
+  return sprintf("transferred: %d MB (%d%%)",int($b/M),int($b/$tb*100));
+}
+  
+
+sub sigpipehandler {
+  $SIG{ALRM} = sub { };
+  if (fileno $SH) {
+    alarm(1);
+    @_ = <$SH>;
+    alarm(0);
+    kill 9,$tpid if $tpid;
+    if (@_ and $opt_v) {
+      die "\n$0: ($$) server error: @_\n";
+    }
+    if (@_ and $_[0] =~ /^HTTP.* \d+ (.*)/) {
+      die "\n$0: server error: $1\n";
+    }
+  }
+  $timeout *= 2;
+  warn "\n$0: connection to $server died\n";
+  warn "retrying after $timeout seconds...\n";
+  sleep $timeout;
+  if ($windoof) { exec $^X,$0,@_ARGV }
+  else          { exec $_0,@_ARGV }
+  die $!;
+}
+
+
+sub checkrecipient {
+  my ($from,$to) = @_;
+  my @r;
+  local $_;
+  
+  @r = formdatapost(
+       from    => $from,
+        to     => $to,
+        id     => $sid,
+        command        => 'CHECKRECIPIENT',
+  );
+
+  $_ = shift @r or die "$0: no reply from server\n";
+
+  if (/ 2\d\d /) {
+    foreach (@r) {
+      last if /^$/;
+      if (s/X-(Recipient: .+)/$1\n/) {
+        s/autodelete=\w+/autodelete=$opt_D/ if $opt_D;
+        s/keep=\d+/keep=$opt_k/             if $opt_k;
+        print;
+        $frecipient ||= (split)[1];
+      }
+    }
+  } else {
+    http_response($_,@r);
+  }
+}
+
+
+# get ID data from ID file
+sub get_id {
+  my $idf = shift;
+
+  $fexcgi = getline($idf) || die "$0: no FEX-URL in $idf\n";
+  $from   = getline($idf) || die "$0: no FROM in $idf\n";
+  $id     = getline($idf) || die "$0: no ID in $idf\n";
+  if ($fexcgi =~ s/!([\w.-]+:\d+)(:(\d+))?//) {
+    $proxy = $1;
+    $chunksize = $3 || 0;
+  }
+  unless ($fexcgi =~ /^[_:=\w\-\.\/\@\%]+$/) {
+    die "$0: illegal FEX-URL \"$fexcgi\" in $idf\n";
+  }
+  unless ($from =~ /^[_:=\w\-\.\/\@\%\+]+$/) {
+    die "$0: illegal FROM \"$from\" in $idf\n";
+  }
+  $fexcgi =~ s:/+$::;
+}
+
+
+# for windows
+sub inquire {
+  my ($file,$to);
+  for (;;) {
+    print "file to send: ";
+    chomp($file = <STDIN>);
+    $file =~ s/^\"//;
+    $file =~ s/\"$//;
+    last if -e $file;
+    warn "$file does not exist\n";
+  }
+  print "recipient (e-mail address): ";
+  chomp($to = <STDIN>);
+  die $usage unless $to;
+  unless ($opt_n) {
+    print "comment: ";
+    chomp($opt_C = <STDIN>);
+  }
+  @ARGV = ($file,$to);
+}
+
+
+sub shelldo {
+  if (system(@_) < 0) { die "failed: @_\n" }
+}
+
+
+# emulate seek on a pipe
+sub readahead {
+  my $fh = shift; # filehandle
+  my $ba = shift; # bytes ahead
+  my $bs = 2**16;
+  my $s = 0;
+  my $n;
+  local $_;
+  
+  while ($s < $ba) { 
+    $n = $ba-$s;
+    $n = $bs if $n > $bs; 
+    $s += read $fh,$_,$n; 
+  }
+}
+
+
+# fileid is inode and mtime
+sub fileid {
+  my @s = stat(shift);
+  return @s ? $s[1].$s[9] : int(time);
+}
+
+
+# collect file meta data (filename, inode, mtime)
+sub fmd {
+  my @files = @_;
+  my ($file,$dir);
+  my $fmd = '';
+  
+  foreach $file (@files) {
+    if (not -l $file and -d $file) {
+      $dir = $file;
+      if (opendir $dir,$dir) {
+        while (defined ($file = readdir($dir))) {
+          next if $file eq '..';
+          if ($file eq '.') {
+            $fmd .= $file.fileid($dir);
+          } else {
+            $fmd .= fmd("$dir/$file");
+          }
+        }
+        closedir $dir;
+      }
+    } else {
+      $fmd .= $file.fileid($file);
+    }
+  }
+  
+  return $fmd;
+}
+
+
+# from MIME::Base64::Perl
+sub decode_b64 {
+  local $_ = shift;
+  my $uu = '';
+  my ($i,$l);
+  
+  tr|A-Za-z0-9+=/||cd;
+  s/=+$//;
+  tr|A-Za-z0-9+/| -_|;
+  return "" unless length;
+
+  $l = (length)-60;
+  for ($i = 0; $i <= $l; $i += 60) {
+    $uu .= "M" . substr($_,$i,60);
+  }
+  $_ = substr($_,$i);
+  if (length) {
+    $uu .= chr(32+(length)*3/4) . $_;
+  }
+  return unpack("u",$uu);
+}
+
+
+sub female_mode {
+  local $_;
+  if (open my $tty,'/dev/tty') {
+    print "@_\n";
+    print "  [y] yes\n",
+          "  [n] no\n",
+          "  [p] perhaps - don't know\n",
+          "your choice: ";
+    $_ = <$tty> || '';
+    close $tty;
+    if (/^y/i) { return }
+    if (/^n/i) { exit }
+    if (/^p/i) { int(rand(2)) ? return : exit }
+    female_mode(@_);
+  }
+}
+
+
+sub http_response {
+  local $_ = shift || <$SH>;
+  my @r = @_;
+  my $error;
+
+  $_ = <$SH> unless $_;
+  unless (defined $_ and /\w/) {
+    die "$0: no response from server\n";
+  }
+  s/\r?\n//;
+  # CGI fatalsToBrowser
+  if (/^HTTP.* 500/) {
+    @r = <$SH> unless @r;
+    @r = ()    unless @r;
+    die "$0: server error: $_\n@r\n";
+  }
+  unless (/^HTTP.* 200/) {
+    $error = $_;
+    $error =~ s/HTTP.[\s\d.]+//;
+    if ($opt_v) {
+      print "<-- $_";
+      print "<-- $_" while <$SH>;
+    }
+    die "$0: server error: $error\n";
+  }
+
+  print "<-- $_\n" if $opt_v;
+  return $_;
+}
+
+
+sub ws {
+  local $_ = shift;
+  return split;
+}
+
+
+sub update {
+  my $cfb = '### common functions ###';
+  my $cfc;
+  
+  local $/;
+  
+  open $0,$0 or die "cannot read $0 - $!\n";
+  $_ = <$0>;
+  close $0;
+  s/.*\n$cfb\n//s;
+  $cfc = $_;
+  
+  foreach my $p (qw(fexget sexsend)) {
+    open $p,$p or die "cannot read $p - $!\n";
+    $_ = <$p>;
+    close $p;
+    s/\n$cfb.*/\n$cfb\n$cfc/s;
+    system "vv -s $p";
+    open $p,'>',$p or die "cannot write $p - $!\n";
+    print {$p} $_;
+    close $p;
+  }
+
+  exec "l $0 fexget sexsend";
+  exit;
+}
+
+### common functions ###
+
+
+sub mtime {
+  my @d = localtime((stat shift)[9]);
+  return sprintf('%d%02d%02d',$d[5]+1900,$d[4]+1,$d[3]);
+}
+
+
+sub urldecode {
+  local $_ = shift;
+  s/\%([a-f\d]{2})/chr(hex($1))/ige;
+  return $_;
+}
+
+
+sub get_ssl_env {
+  # set SSL/TLS options
+  $SSL{SSL_verify_mode} = $ENV{SSLVERIFY} if defined($ENV{SSLVERIFY});
+  foreach my $opt (qw(
+    SSL_version
+    SSL_cipher_list 
+    SSL_verify_mode 
+    SSL_ca_path 
+    SSL_ca_file)
+  ) {
+    my $env = uc($opt);
+    $env =~ s/_//g;
+    $SSL{$opt} = $ENV{$env} if defined($ENV{$env});
+  }
+
+  if ($SSL{SSL_verify_mode}) {
+    &search_ca;
+    unless ($SSL{SSL_ca_path} or $SSL{SSL_ca_file}) {
+      die "$0: \$SSLVERIFYMODE, but not valid \$SSLCAPATH or \$SSLCAFILE\n";
+    }
+  } elsif (defined($SSL{SSL_verify_mode})) {
+    # user has set SSLVERIFY=0 !
+  } else {
+    &search_ca;
+    $SSL{SSL_verify_mode} = 1 if $SSL{SSL_ca_path} or $SSL{SSL_ca_file};
+  }
+}
+
+sub search_ca {
+  local $_;
+  return if $SSL{SSL_ca_file} or $SSL{SSL_ca_path};
+  foreach (qw(/etc/ssl/certs/ca-certificates.crt)) {
+    if (-f) {
+      $SSL{SSL_ca_file} = $_;
+      return;
+    }
+  }
+  foreach (qw(/etc/ssl/certs /etc/pki/tls/certs)) {
+    if (-f) {
+      $SSL{SSL_ca_path} = $_;
+      return;
+    }
+  }
+}
+
+
+sub serverconnect {
+  my ($server,$port) = @_;
+  my $connect = "CONNECT $server:$port HTTP/1.1";
+  local $_;
+  
+  if ($opt_v and $port == 443 and %SSL) {
+    foreach my $v (keys %SSL) {
+      printf "%s => %s\n",$v,$SSL{$v};
+    }
+  }
+  
+  if ($proxy) {
+    tcpconnect(split(':',$proxy));
+    if ($port == 443) {
+      printf "--> %s\n",$connect if $opt_v;
+      nvtsend($connect,"");
+      $_ = <$SH>;
+      s/\r//;
+      printf "<-- $_"if $opt_v;
+      unless (/^HTTP.1.. 200/) {
+        die "$0: proxy error : $_";
+      }
+      eval "use IO::Socket::SSL";
+      die "$0: cannot load IO::Socket::SSL\n" if $@;
+      $SH = IO::Socket::SSL->start_SSL($SH,%SSL);
+    }
+  } else {
+    tcpconnect($server,$port);
+  }
+#  if ($port == 443 and $opt_v) {
+#    printf "%s\n",$SH->get_cipher();
+#  }
+}
+
+
+# set up tcp/ip connection
+sub tcpconnect {
+  my ($server,$port) = @_;
+  
+  if ($SH) {
+    close $SH;
+    undef $SH;
+  }
+  
+  if ($port == 443) {
+    # eval "use IO::Socket::SSL qw(debug3)";
+    eval "use IO::Socket::SSL";
+    die "$0: cannot load IO::Socket::SSL\n" if $@;
+    $SH = IO::Socket::SSL->new(
+      PeerAddr => $server,
+      PeerPort => $port,
+      Proto    => 'tcp',
+      %SSL
+    );
+  } else {
+    $SH = IO::Socket::INET->new(
+      PeerAddr => $server,
+      PeerPort => $port,
+      Proto    => 'tcp',
+    );
+  }
+  
+  if ($SH) {
+    autoflush $SH 1;
+  } else {
+    die "$0: cannot connect $server:$port - $@\n";
+  }
+  
+  print "TCPCONNECT to $server:$port\n" if $opt_v;
+}
+
+
+sub sendheader {
+  my $sp = shift;
+  my @head = @_;
+  my $head;
+  
+  push @head,"Host: $sp";
+  
+  foreach $head (@head) {
+    print "--> $head\n" if $opt_v;
+    print {$SH} $head,"\r\n";
+  }
+  print "-->\n" if $opt_v;
+  print {$SH} "\r\n";
+}
+
+
+sub nvtsend {
+  local $SIG{PIPE} = sub { $sigpipe = "@_" };
+  
+  $sigpipe = '';
+  
+  die "$0: internal error: no active network handle\n" unless $SH;
+  die "$0: remote host has closed the link\n" unless $SH->connected;
+  
+  foreach my $line (@_) {
+    print {$SH} $line,"\r\n";
+    if ($sigpipe) {
+      undef $SH;
+      return 0;
+    }
+  }
+  
+  return 1;
+}
+
+
+# from MIME::Base64::Perl
+sub encode_b64 {
+  my $res = "";
+  my $eol = "\n";
+  my $padding;
+  
+  pos($_[0]) = 0;
+  $res = join '',map(pack('u',$_)=~ /^.(\S*)/, ($_[0]=~/(.{1,45})/gs));
+  $res =~ tr|` -_|AA-Za-z0-9+/|;
+  $padding = (3-length($_[0])%3)%3;
+  $res =~ s/.{$padding}$/'=' x $padding/e if $padding;
+  return $res;
+}
diff --git a/bin/fexsrv b/bin/fexsrv
new file mode 100755 (executable)
index 0000000..27e3318
--- /dev/null
@@ -0,0 +1,821 @@
+#!/usr/bin/perl -wT
+
+# fexsrv : web server for F*EX service
+#
+# Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
+#
+
+use 5.008;
+use Socket;
+use IO::Handle;
+use Fcntl qw':flock :seek';
+
+# stunnel workaround
+BEGIN { $SIG{CHLD} = "DEFAULT" }
+
+# use BSD::Resource;
+# setrlimit(RLIMIT_CPU,999,999) or die "$0: $!\n";
+
+# SSL remote address provided by stunnel
+if (@ARGV and $ARGV[0] eq 'stunnel' and $ENV{REMOTE_HOST} =~ /(.+)/) {
+  $ssl_ra = $1;
+}
+
+# KEEP_ALIVE <== callback from CGI
+if ($ENV{KEEP_ALIVE}) { 
+  $keep_alive = $ENV{KEEP_ALIVE};
+} else {
+  %ENV = ();   # clean environment
+}
+
+$ENV{HOME} = (getpwuid($<))[7] or die "$0: no HOME\n";
+
+# fexsrv MUST be run with full path!
+if ($0 =~ m:^(/.+)/bin/fexsrv:) {
+  $FEXHOME = $1;
+  $FEXHOME =~ s:/+:/:g;
+  $FEXHOME =~ s:/$::;
+  $ENV{FEXHOME} = $FEXHOME;
+}
+
+foreach my $lib (
+  $FEXHOME,
+  '/usr/local/fex',
+  '/usr/local/share/fex',
+  '/usr/share/fex',
+) {
+  $ENV{FEXLIB} = $FEXLIB = $lib       and last if -f "$lib/fex.pp";
+  $ENV{FEXLIB} = $FEXLIB = "$lib/lib" and last if -f "$lib/lib/fex.pp";
+}
+
+# import from fex.pp
+our ($hostname,$debug,$timeout,$max_error,$max_error_handler);
+our ($spooldir,$logdir,$docdir,$xkeydir,$lockdir);
+our ($force_https,$default_locale,$bs,$adlm);
+our (@locales);
+
+# load common code (local config: $FEXHOME/lib/fex.ph)
+require "$FEXLIB/fex.pp" or die "$0: cannot load $FEXLIB/fex.pp - $!\n";
+
+chdir $spooldir or http_die("$0: $spooldir - $!\n");
+
+our $log = "$logdir/fexsrv.log";
+our $error = 'F*EX ERROR';
+our $htmlsource;
+our $hid = ''; # header ID
+our @log;
+
+$0 = untaint($0);
+  
+$ENV{GATEWAY_INTERFACE} = 'CGI/1.1';
+$ENV{SERVER_NAME} = $hostname;
+$ENV{QUERY_STRING} = '';
+$ENV{HTTP_COOKIE} = '';
+$ENV{PATH_INFO} = '';
+$ENV{RANDOM} = randstring(8);
+$ENV{FEATURES} = join(',',qw(
+  SID CHECKRECIPIENT GROUPS QUOTA FILEID MULTIPOST XKEY FILEQUERY FILESTREAM
+  JUP NOSTORE AXEL FEXMAIL FILELINK
+));
+
+$port = 0;
+
+# continue session?
+if ($keep_alive) {
+  if ($ENV{HTTP_HOST} =~ /(.+):(.+)/) {
+    $hostname = $1;
+    $port = $2;
+  } else {
+    $hostname = $ENV{HTTP_HOST};
+    if ($ENV{PROTO} eq 'https') { $port = 443 }
+    else                        { $port = 80 }
+  }
+  $ra = $ENV{REMOTE_ADDR};
+  $rh = $ENV{REMOTE_HOST};
+} 
+
+# new session
+else {
+  my $iaddr;
+  
+  # HTTPS connect
+  if ($ssl_ra) {
+    $ENV{PROTO} = 'https';
+    $ENV{REMOTE_ADDR} = $ra = $ssl_ra;
+    if ($ssl_ra =~ /\w:\w/) {
+      # ($rh) = `host $ssl_ra 2>/dev/null` =~ /name pointer (.+)\.$/;
+      $^W = 0; eval 'use Socket6'; $^W = 1;
+      http_error(503) if $@;
+      $iaddr = inet_pton(AF_INET6,$ssl_ra) and
+      $rh = gethostbyaddr($iaddr,AF_INET6);
+    } else {
+      $rh = gethostbyaddr(inet_aton($ra),AF_INET);
+    }
+    $rh ||= '-';
+    $port = 443;
+    # print {$log} "X-SSL-Remote-Host: $ssl_ra\n";
+  } 
+
+  # HTTP connect
+  else {
+    $ENV{PROTO} = 'http';
+    my $sa = getpeername(STDIN) or die "$0: no network stream on STDIN\n";
+    if (sockaddr_family($sa) == AF_INET) {
+      ($ENV{REMOTE_PORT},$iaddr) = sockaddr_in($sa);
+      $ENV{REMOTE_ADDR} = $ra = inet_ntoa($iaddr);
+      $rh = gethostbyaddr($iaddr,AF_INET);
+      ($port) = sockaddr_in(getsockname(STDIN));
+    } elsif (sockaddr_family($sa) == AF_INET6) {
+      $^W = 0; eval 'use Socket6'; $^W = 1;
+      http_error(503) if $@;
+      ($ENV{REMOTE_PORT},$iaddr) = unpack_sockaddr_in6($sa);
+      $ENV{REMOTE_ADDR} = $ra = inet_ntop(AF_INET6, $iaddr);
+      $rh = gethostbyaddr($iaddr,AF_INET6);
+      ($port) = unpack_sockaddr_in6(getsockname(STDIN));
+    } else {
+      die "$0: unknown IP version\n";
+    }
+    $port = 80 unless $port;
+  }
+
+  $ENV{REMOTE_HOST} = $rh || '';
+
+  $ENV{HTTP_HOST} = ($port == 80 or $port == 443) 
+                  ? $hostname : "$hostname:$port";
+
+}
+
+if ($reverse_proxy_ip and $reverse_proxy_ip eq $ra) {
+  $ENV{FEATURES} =~ s/SID,//;
+}
+
+if (@anonymous_upload and ipin($ra,@anonymous_upload)) {
+  $ENV{FEATURES} .= ',ANONYMOUS';
+}
+
+$| = 1;
+
+$SIG{CHLD} = "DEFAULT"; # stunnel workaround
+
+$SIG{ALRM} = sub {
+  # printf {$log} "\nTIMEOUT %s %s\n",isodate(time),$connect;
+  if (@log) {
+    debuglog('TIMEOUT',isodate(time));
+    fexlog($connect,@log,"TIMEOUT");
+  }
+  exit;
+};
+
+REQUEST: while (*STDIN) {
+
+  if (defined $ENV{REQUESTCOUNT}) { $ENV{REQUESTCOUNT}++ }
+  else                            { $ENV{REQUESTCOUNT} = 0 }
+  
+  $connect = sprintf "%s:%s %s %s %s [%s_%s]",
+                     $keep_alive ? 'CONTINUE' : 'CONNECT',
+                     $port,
+                     isodate(time),
+                     $rh||'-',
+                     $ra,
+                     $$,$ENV{REQUESTCOUNT};
+  $hid = sprintf("%s %s\n",$rh||'-',$ra);
+
+  @header = @log = ();
+  $header = '';
+
+  # read complete HTTP header
+  while (defined ($_ = &getaline)) {
+    last if /^\s*$/;
+    $hl += length;
+    $header .= $_;
+    s/[\r\n]+$//;
+    # URL-encode non-printable chars
+    s/([\x00-\x08\x0E-\x1F\x7F-\x9F])/sprintf "%%%02X",ord($1)/ge;
+    s/%21/!/g;
+    if (@header and s/^\s+/ /) {
+      $header[-1] .= $_;
+    } else {
+      push @header,$_;
+      $header{$1} = $2 if /(.+)\s*:\s*(.+)/;
+      push @log,$_;
+    }
+    
+    if (/^(GET \/|X-Forwarded-For|User-Agent)/i) {
+      $hid .= $_."\n";
+    }
+
+    # reverse-proxy?
+    if ($reverse_proxy_ip and $reverse_proxy_ip eq $ra and
+       /^X-Forwarded-For: ([\d.]+)/
+    ) {
+      $ENV{REMOTE_ADDR} = $ra = $1;
+      $ENV{REMOTE_HOST} = $rh = gethostbyaddr(inet_aton($ra),AF_INET) || '';
+      $ENV{HTTP_HOST} = $hostname;
+      if ($ENV{PROTO} eq 'https') { $port = 443 }
+      else                        { $port = 80 }
+    }
+  }
+
+  exit unless @header;
+  exit if $header =~ /^\s*$/;
+  
+  $ENV{HTTP_HEADER} = $header;
+  debuglog($header);
+  # http_die("<pre>$header</pre>");
+  
+  $ENV{'HTTP_HEADER_LENGTH'} = $hl;
+  $ENV{REQUEST_URI} = $uri = '';
+  $cgi = '';
+
+  # is it a HTTP-request at all?
+  $request = shift @header;
+  if ($request !~ /^(GET|HEAD|POST|OPTIONS).*HTTP\/\d\.\d$/i) {
+    fexlog($connect,$request,"DISCONNECT: no HTTP request");
+    badlog("no HTTP request: $request");
+    exit;
+  }
+  
+  if ($force_https and $port != 443
+      and $request =~ /^(GET|HEAD|POST)\s+(.+)\s+(HTTP\/[\d\.]+$)/i) {
+    $request = $2;
+    nvt_print(
+      "HTTP/1.1 301 Moved Permanently",
+      "Location: https://$hostname$request",
+      "Content-Length: 0",
+      ""
+    );
+    fexlog($connect,@log);
+    exit;
+  }
+
+  $request =~ s{^(GET|HEAD|POST) https?://$hostname(:\d+)?}{$1 }i;
+
+  if ($request =~ m"^(GET|HEAD) /fop/\w+/") {
+    # no header inquisition on regular fop request
+    $header_hook = '';
+  } else {
+    &$header_hook($connect,$request,$ra) if $header_hook;
+  }
+
+  unless ($keep_alive) {
+    if ($request =~ m:(HTTP/1.(\d)): and $2) {
+      $ENV{KEEP_ALIVE} = $keep_alive = $ra
+    } else {
+      $ENV{KEEP_ALIVE} = $keep_alive = '';
+    }
+  }
+
+  if ($request =~ /^OPTIONS FEX HTTP\/[\d\.]+$/i) {
+    fexlog($connect,@log);
+    nvt_print(
+      "HTTP/1.1 201 OK",
+      "X-Features: $ENV{FEATURES}",
+      "X-Timeout: $timeout",
+      ''
+    );
+    next REQUEST if $keep_alive;
+    exit;
+  }
+
+  if ($request =~ m:^GET /?SID HTTP/[\d\.]+$:i) {
+    if ($ENV{FEATURES} !~ /\bSID\b/) {
+      fexlog($connect,@log);
+      nvt_print(
+        "HTTP/1.1 501 Not Available",
+        "Server: fexsrv",
+        "X-Features: ".$ENV{FEATURES},
+        "X-Timeout: ".$timeout,
+        'Content-Length: 0',
+        ''
+      );
+    } else {
+      $ENV{SID} = randstring(8);
+      fexlog($connect,@log);
+      nvt_print(
+        "HTTP/1.1 201 ".$ENV{SID},
+        "Server: fexsrv",
+        "X-Features: ".$ENV{FEATURES},
+        "X-SID: ".$ENV{SID},
+        "X-Timeout: ".$timeout,
+        'Content-Length: 0',
+        ''
+      );
+    }
+    next REQUEST if $keep_alive;
+    exit;
+  }
+
+  if ($request =~ /^(GET|HEAD|POST)\s+(.+)\s+(HTTP\/[\d\.]+$)/i) {
+    $ENV{REQUEST_METHOD} = uc($1);
+    $ENV{REQUEST_URI}    = $uri = $cgi = $2;
+    $ENV{HTTP_VERSION}   = $protocol = $3;
+    $ENV{QUERY_STRING}   = $1               if $cgi =~ s/\?(.*)//;
+    $ENV{PATH_INFO}      = $1               if $cgi =~ m:/.+?(/.+?)(\?|$):;
+    $ENV{KEEP_ALIVE}     = $keep_alive = '' if $protocol =~ /1\.0/;
+    $ENV{REQUEST_URL}    = "$ENV{PROTO}://$ENV{HTTP_HOST}$ENV{REQUEST_URI}";
+    if ($uri =~ /<|%3c/i)  { badchar("&lt;") }
+    if ($uri =~ />|%3e/i)  { badchar(">") }
+    if ($uri =~ /\||%7c/i) { badchar("|") }
+    if ($uri =~ /\\|%5c/i) { badchar("\\") }
+  }
+
+  while ($_ = shift @header) {
+    
+    # header inquisition!
+    &$header_hook($connect,$_,$ra) if $header_hook;
+
+    # mega stupid "Download Manager" FlashGet
+    if ($uri =~ m{^/fop/} and m{^Referer: https?://.*\Q$uri$}) {
+      fexlog($connect,@log,"NULL: FlashGet");
+      debuglog("NULL: FlashGet");
+      exec qw'cat /dev/zero' or sleep 30;
+      exit;
+    }
+
+    if ($header =~ /\nRange:/ and /^User-Agent: (FDM)/) {
+      disconnect($1,"499 Download Manager $1 Not Supported",30);
+    }
+    
+    if (/^User-Agent: (Java\/[\d\.]+)/) {
+      disconnect($1,"499 User-Agent $1 Not Supported",30);
+    }
+    
+    if (/^Range:.*,/) {
+      disconnect("Range a,b","416 Requested Range Not Satisfiable",30);
+    }
+    if (/^Range:.*(\d+)-(\d+)/) {
+      if ($1 > $2) {
+        disconnect("Range a>b","416 Requested Range Not Satisfiable",0);
+      }
+      if (($header{'User-Agent'}||'') !~ /$adlm/ ) {
+        disconnect("Range a-b","416 Requested Range Not Satisfiable",30);
+      }
+    }
+
+    if (/^Range:.*\d+-$/ and $hid) {
+      my $lock = untaint($lockdir.'/'.md5_hex($hid));
+      if (open $lock,'+>>',$lock) {
+        if (flock($lock,LOCK_EX|LOCK_NB)) {
+          seek $lock,0,0;
+          truncate $lock,0;
+          print {$lock} $hid;
+        } else {
+          disconnect(
+            "multiple Range request",
+            "400 Multiple Requests Not Allowed",
+            10,
+          );
+        }
+      }
+    }
+
+    # client signed int bug
+    if (/^Range:.*-\d+-/) {
+      disconnect("Range -a-","416 Requested Range Not Satisfiable",0);
+    }
+
+#    if (/^Range:/ and $protocol =~ /1\.0/) {
+#      &$header_hook($connect,$_,$ra) while ($header_hook and $_ = shift @header);
+#      fexlog($connect,@log,"DISCONNECT: Range + HTTP/1.0");
+#      debuglog("DISCONNECT: Range + HTTP/1.0");
+#      http_error(416);
+#      exit;
+#    }
+
+    if (/^Connection:\s*close/i) {
+      $ENV{KEEP_ALIVE} = $keep_alive = '';
+    }
+
+    # HTTP header ==> environment variables
+    if (/^([\w\-]+):\s*(.+)/s) {
+      $http_var = $1;
+      $http_val = $2;
+      $http_var =~ s/-/_/g;
+      $http_var = uc($http_var);
+      $http_val =~ s/^\s+//;
+      $http_val =~ s/\s+$//;
+      if ($http_var =~ /^X_(FEX_\w+|CONTENT_LENGTH)$/) {
+        $http_var = $1;
+      } else {
+        $http_val =~ s/\s+/ /g;
+        if ($http_var =~ /^HTTP_(HOST|VERSION)$/) {
+          $http_var = 'X-'.$http_var;
+        } elsif ($http_var !~ /^CONTENT_/) {
+          $http_var = 'HTTP_'.$http_var;
+        }
+      }
+      $ENV{$http_var} = $http_val;
+    }
+  }
+
+  # multiline header inquisition
+  &$header_hook($connect,$header,$ra) if $header_hook;
+
+  exit unless $cgi;
+  
+  # extra download request? (request http://fexserver//xkey)
+  if ($cgi =~ m{^//([^/]+)$}) {
+    my $xkey = $1;
+    my $dkey;
+    if ($xkey =~ /^afex_\d/) {
+      $dkey = readlink "$xkeydir/$xkey" and $dkey =~ s/^\.\.\///;
+    } else {
+      $dkey = readlink "$xkeydir/$xkey/dkey" and $dkey .= "/$xkey";
+    }
+    if ($dkey) {
+      # xkey downloads are only one time possible - besides afex
+      if ($xkey !~ /^afex_\d/) {
+        unlink "$xkeydir/$xkey/xkey";
+        unlink "$xkeydir/$xkey";
+      }
+      nvt_print(
+        "HTTP/1.1 301 Moved Permanently",
+        "Location: $ENV{PROTO}://$ENV{HTTP_HOST}/fop/$dkey",
+        "Content-Length: 0",
+        ""
+      );
+      fexlog($connect,@log);
+      exit;
+    }
+    fexlog($connect,@log);
+    http_error(404);
+    exit;
+  }
+
+  # get locale
+  if (($ENV{QUERY_STRING} =~ /.*locale=([\w-]+)/ or
+       $ENV{HTTP_COOKIE}  =~ /.*locale=([\w-]+)/) 
+      and -d "$FEXHOME/locale/$1") {
+    $ENV{LOCALE} = $locale = $1;
+  } else {
+    $ENV{LOCALE} = $locale = $default_locale;
+  }
+
+  # check for name based virtual host
+  $vhost = vhost($ENV{'HTTP_HOST'});
+
+  if ($debug) {
+    debuglog("ENV:\n");
+    foreach $var (sort keys %ENV) {
+      debuglog(sprintf "  %s = >%s<\n",$var,$ENV{$var});
+    }
+    debuglog("\n");
+  }
+
+  # locale definitions in fex.ph?
+  if (@locales) {
+    if (@locales == 1) {
+      $locale = $locales[0];
+    } elsif (not grep /^$locale$/,@locales) {
+      $locale = $default_locale;
+    }
+  }
+              
+  # prepare document file name
+  if ($ENV{REQUEST_METHOD} =~ /^GET|HEAD$/) {
+    $doc = untaint($uri);
+    $doc =~ s/%([\dA-F]{2})/unpack("a",pack("H2",$1))/ge;
+    $doc =~ m:/\.\./: and http_error(403);
+    $doc =~ s:^/+::;
+    $doc =~ s/\?.*//;
+    if ($locale and -e "$docdir/locale/$locale/$doc") {
+      $doc = "$docdir/locale/$locale/$doc";
+    } else {
+      $doc = "$docdir/$doc";
+    }
+  }
+
+  # CGI or document request?
+  if ($cgi =~ s:^/+::) {
+    $cgi =~ s:/.*::;
+    unless ($cgi) {
+      my $login = "$FEXHOME/cgi-bin/login";
+      if (-x $login) {
+        $cgi = untaint(readlink($login) || $login);
+        $cgi =~ s:.*/::;
+      }
+    }
+
+    $ENV{SCRIPT_NAME} = $cgi;
+
+    # locale CGIs? (vhost comes already with own FEXLIB)
+    if ($locale and $locale ne 'english'
+        and -f "$FEXHOME/locale/$locale/cgi-bin/$cgi") {
+      $ENV{SCRIPT_FILENAME} = $cgi = "$FEXHOME/locale/$locale/cgi-bin/$cgi";
+      $ENV{FEXLIB} = $FEXLIB = "$FEXHOME/locale/$locale/lib" unless $vhost;
+    } else {
+      $ENV{SCRIPT_FILENAME} = $cgi = "$FEXHOME/cgi-bin/$cgi";
+      $ENV{FEXLIB} = $FEXLIB = "$FEXHOME/lib" unless $vhost;
+    }
+
+    $status = '';
+    if (-x $cgi and -f $cgi) {
+      if (@forbidden_hosts and ipin($ra,@forbidden_hosts)) {
+        fexlog($connect,@log,"FORBIDDEN");
+        http_error(403);
+      }
+      unlink "$logdir/.error/$ra";
+      # push @log,"DEBUG: locale=$locale locales=(@locales)";
+      fexlog($connect,@log,"EXEC $cgi");
+      eval { local $^W = 0; exec $cgi };
+      $status = "$! or bad interpreter";
+      fexlog($connect,@log,"FAILED to exec $cgi : $status");
+      http_error(555);
+    } else {
+      if (-f "$doc/.htindex") {
+        require "$FEXLIB/dop";
+        fexlog($connect,@log);
+
+        showindex($doc);
+        STDOUT->flush;
+        next REQUEST if $keep_alive;
+        exit;
+      }
+      if (-f "$doc/index.html") {
+        # force redirect if trailing / is missing
+        # this is mandatory for processing further HTTP request!
+        if ($doc !~ m{/$}) {
+          nvt_print(
+            "HTTP/1.1 301 Moved Permanently",
+            "Location: $ENV{REQUEST_URL}/",
+            "Content-Length: 0",
+            ""
+          );
+          fexlog($connect,@log);
+          next REQUEST if $keep_alive;
+          exit;
+        }
+        $doc .= '/index.html';
+        $doc =~ s:/+:/:g;
+      }
+      $doc =~ s/#.*//; # ignore HTML anchors (stupid msnbot)
+
+      # special request for F*EX UNIX clients
+      if ($ENV{SCRIPT_NAME} eq 'xx.tar') {
+        bintar(qw'fexget fexsend xx zz ezz');
+      }
+      if ($ENV{SCRIPT_NAME} eq 'sex.tar') {
+        bintar(qw'sexsend sexget sexxx');
+      }
+      if ($ENV{SCRIPT_NAME} eq 'afex.tar') {
+        bintar(qw'afex asex fexget fexsend sexsend sexget');
+      }
+      if ($ENV{SCRIPT_NAME} eq 'afs.tar') {
+        bintar(qw'afex asex fexget fexsend xx sexsend sexget sexxx zz ezz');
+      }
+      # URL ends with ".html!" or ".html?!"
+      if ($doc =~ s/(\.html)!$/$1/ or 
+          $doc =~ /\.html$/ and $ENV{'QUERY_STRING'} eq '!') 
+      { $htmlsource = $doc } else { $htmlsource = '' }
+
+      if (-f $doc 
+          or $doc =~ /(.+)\.(tar|tgz|zip)$/ and lstat("$1.stream")
+          or $doc =~ /(.+)\.tgz$/           and -f "$1.tar"
+          or $doc =~ /(.+)\.gz$/            and -f $1)
+      {
+        unlink "$logdir/.error/$ra";
+        delete $ENV{SCRIPT_FILENAME};        
+        $ENV{DOCUMENT_FILENAME} = $doc;
+        require "$FEXLIB/dop";
+        fexlog($connect,@log);
+        dop($doc);
+        STDOUT->flush;
+        next REQUEST if $keep_alive;
+        exit;
+      } elsif ($uri eq '/bunny') {
+        fexlog($connect,@log);
+        nvt_print(
+          'HTTP/1.1 200 OK',
+          'Server: fexsrv',
+          "Content-Type: text/plain",
+          '',
+          '=:3',
+        );
+        exit;
+      } elsif ($uri eq '/camel') {
+        fexlog($connect,@log);
+        nvt_print(
+          'HTTP/1.1 200 OK',
+          'Server: fexsrv',
+          "Content-Type: text/plain",
+          '',
+        );
+        local $/;
+        print unpack('u',<DATA>);
+        exit;
+      } elsif (-e $cgi) {
+        $status = 'not executable';
+      }
+
+    }
+
+  }
+
+  # neither document nor CGI ==> error
+  
+  if ($status) {
+    fexlog($connect,@log,"FAILED to exec $cgi : $status");
+    http_error(666);
+  } else {
+    fexlog($connect,@log,"UNKNOWN URL");
+    badlog($request);
+    http_error(404);
+  }
+  exit;
+}
+
+
+# read one text line unbuffered from STDIN
+sub getaline {
+  my $line = '';
+  my $c;
+
+  alarm($timeout);
+
+  # must use sysread to avoid perl line buffering
+  # (later exec would destroy line buffer)
+  while (sysread STDIN,$c,1) {
+    $line .= $c;
+    last if $c eq "\n";
+  }
+
+  alarm(0);
+
+  return $line;
+}
+
+
+sub fexlog {
+  my @log = @_;
+  if (open $log,">>$log") {
+    flock $log,LOCK_EX;
+    seek $log,0,SEEK_END;
+    print {$log} "\n",join("\n",@log),"\n";
+    close $log;
+  } else {
+    http_die("$0: cannot write to $log - $!\n");
+  }
+}
+
+
+
+sub badchar {
+  my $bc = shift;
+  
+  fexlog($connect,@log,"DISCONNECT: bad characters in URL");
+  debuglog("DISCONNECT: bad characters in URL $uri");
+  badlog($request);
+  http_die("\"$bc\" is not allowed in URL");
+}
+
+
+sub bintar {
+  my $tmpdir = "$FEXHOME/tmp";
+  my $fs = "$ENV{PROTO}://$ENV{HTTP_HOST}";
+  
+  if (chdir "$FEXHOME/bin") {
+    fexlog($connect,@log);
+    chdir $fstb if $fstb;
+    mkdir $tmpdir;
+    foreach my $f (@_) {
+      copy($f,"$tmpdir/$f","s#fexserver = ''#fexserver = '$fs'#");
+      chmod 0755,"$tmpdir/$f";
+    }
+    chdir $tmpdir or http_die("internal error: $tmpdir - $!");
+    my $tar = `tar cf - @_ 2>/dev/null`;
+    unlink @_;
+    nvt_print(
+      'HTTP/1.1 200 OK',
+      'Server: fexsrv',
+      "Content-Length: ".length($tar),
+      "Content-Type: application/x-tar",
+      '',
+    );
+    print $tar;
+    exit;
+  }
+}
+
+
+sub http_error {
+  my $error = shift;
+  my $URL = $ENV{REQUEST_URL}||'';
+  my $URI = $ENV{REQUEST_URI}||'';
+
+  if ($error eq 400) {
+    http_error_header("400 Bad Request");
+    nvt_print("Your request $URL is not acceptable.");
+  } elsif ($error eq 403) {
+    http_error_header("403 Forbidden");
+    nvt_print("You have no permission to request $URL");
+  } elsif ($error eq 404) {
+    http_error_header("404 Not Found");
+    nvt_print("The requested URI $URI was not found on this server.");
+  } elsif ($error eq 416) {
+    http_error_header("416 Requested Range Not Satisfiable");
+  } elsif ($error eq 503) {
+    http_error_header("503 Service Unavailable");
+    # nvt_print("No Perl ipv6 support on this server.");
+  } else {
+    http_error_header("555 Unknown Error");
+    nvt_print("The requested URL $URL produced an internal error.");
+  }
+  nvt_print(
+    "<hr>",
+    "<address>fexsrv at <a href=\"/index.html\">$hostname:$port</a></address>",
+    "</body></html>",
+  );
+  exit;
+}
+
+
+sub disconnect {
+  my $info = shift;
+  my $error = shift;
+  my $wait = shift||0;
+  
+  # &$header_hook($connect,$_,$ra) while ($header_hook and $_ = shift @header);
+  fexlog($connect,@log,"DISCONNECT: $info");
+  debuglog("DISCONNECT: $info");
+  errorlog("$ENV{REQUEST_URI} ==> $error");
+  badlog("$ENV{REQUEST_URI} ==> $error ($info)");
+
+  sleep $wait;
+  nvt_print("HTTP/1.0 $error");
+  exit;
+}
+
+
+sub http_error_header {
+  my $error = shift;
+  my $uri = $ENV{REQUEST_URI};
+  
+  errorlog("$uri ==> $error") if $uri;
+  nvt_print(
+    "HTTP/1.1 $error",
+    "Connection: close",
+    "Content-Type: text/html; charset=iso-8859-1",
+    "",
+    '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">',
+    "<html>",
+    "<head><title>$error</title></head>",
+    "<body>",
+    "<h1>$error</h1>",
+  );
+}
+
+
+sub badlog {
+  my $request = shift;
+  my @n;
+  my $ed = "$spooldir/.error";
+  local $_;
+  
+  if (@ignore_error) {
+    foreach (@ignore_error) {
+      return if $request =~ /$_/;
+    }
+  }
+  
+  if ($ra and $max_error and $max_error_handler) {
+    mkdir($ed) unless -d $ed;
+
+    if (open $ra,"+>>$ed/$ra") {
+      flock($ra,LOCK_EX);
+      seek $ra,0,SEEK_SET;
+      @n = <$ra>;
+      printf {$ra} "%s %s\n",isodate(time),$request;
+      close $ra;
+      &$max_error_handler($ra,@n) if scalar(@n) > $max_error;
+    }
+  }
+}
+
+
+__END__
+M("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("PM)R(G+5P*("`@
+M("`@("`@("`@("`@("`@("`@("`@("]@8&`M+B`@(&!<("`@("Q=+B`@("`@
+M("`@("`@("`@("`@("`@("`L+BY?"B`@("`@("`@("`@("`@("`@("`@("`@
+M+&`@("`@(&`B+B`@72X@("`@(&`N("`@("`@("`@("`@("`@7U]?+BY>(&!?
+M)V`B(B(*("`@("`@("`@("`@("`@("`@("`@("`I("`@("`@("`G+"TG+2T@
+M+B`@("!@+B`@("`@(%]?7RPN+2<@("XN("T@("`G("TP("Y?"B`@("`@("`@
+M("`@+"X@("`@("`@("`@?%]?+"Y?("!?("`N+5\@("!<("`\("Y@(B<G)V`L
+M7R`@("`@("`@+2!?("TN("`@("`@("<M+BX*("`@("`@("`@('P@(&8M+2TM
+M+2TM+2<O("`@("!@)RTM+BXL("`@("`M("`@("`@("`@("T@("`@("`@("`@
+M("`@("`G("XL7U\N7%\N)PH@("`@("`@("`@8"TM)R<G)R<G8"TM7"`@("`@
+M("`@("`@("!@)RTM+B`@("`@("`@("`@("`@("`@("`@7RPN)RTM)R<*("`@
+M("`@("`@("`@("`@("`@("`@("\@("`@("`@("`@("`@("`@("!@8#T@+2`@
+M+2`@("!?+2`@7RXM)PH@("`@("`@("`@("`@("`@("`@("!\("`@("`@("`@
+M("`@("`@("`@("`@("!?)V`@("`G)U\M+2<*("`@("`@("`@("`@("`@("`@
+M("`@?"`@("`@("`@("`@("`@("`@("`O("`@?"T]+BXM+2<*("`@("`@("`@
+M("`@("`@(%\@("`@("<N("`@("`@("`N7R`@(%\N+2=@)R`@(&!?7PH@("`@
+M("`@("`@("`@("PG("`N("PG("TL3%]?+WP@(%P@8"<G+&\N("`@+5\@("`@
+M)V`M+2X*("`@("`@("`@("`@("`@+BPG(BT@("`O("`@("`@("`@?"`@8'8@
+M+R`N("PG8"TM+BXG7R`G+@H@("`@("`@("`@("`@("`@("TN+E\L)R`@("`@
+M('P@("!\("`@)RTM+E]@("!?+R`@('P@("!\"B`@("`@("`@("`@("`@("`@
+M("`@("`@("`@("=@8"<@6R`@("`@("`@("=@)R`@("`L+RXN+PH@("`@("`@
+M("`@("`@("`@("`@("`@("`@('P@("`M+B<@("`@("`@("`@("`@("Y;("`@
+M+PH@("`@("`@("`@("`@("`@("`@(%]?7U]?7RX@("`L.E]?7U]?7U]?7U]?
+M7U]?7RQ@("PG"B`@("TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2T@+2(M+2TM
+M+2TM+2TM+2TM+2TM+2T]+0H@+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM
+6+2TM+2TM+2TM+2TM+2TM+2TM+2TM"@``
diff --git a/bin/fexwall b/bin/fexwall
new file mode 100755 (executable)
index 0000000..7004a53
--- /dev/null
@@ -0,0 +1,124 @@
+#!/usr/bin/perl -w
+
+# send e-mail to all registered F*EX users
+#
+# Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
+#
+
+use Getopt::Std;
+use File::Basename;
+use Cwd 'abs_path';
+
+# do not run as CGI!
+exit if $ENV{SCRIPT_NAME};
+
+unless ($ENV{FEXLIB}) {
+  if ($ENV{FEXHOME}) {
+    $ENV{FEXLIB} = $ENV{FEXHOME}.'/lib';
+  } else {
+    $ENV{FEXLIB} = dirname(dirname(abs_path($0))).'/lib';
+  }
+}
+$FEXLIB = $ENV{FEXLIB};
+die "$0: no FEXLIB\n" unless -f "$FEXLIB/fex.pp";
+
+# program name
+$0 =~ s:.*/::;
+
+# become effective user fex
+unless ($<) {
+  if (my @pw = getpwnam('fex')) {
+    $)         = $pw[3];
+    $>         = $pw[2];
+    $ENV{HOME} = $pw[7];
+  } else {
+    die "$0: no such user 'fex'\n";
+  }
+}
+
+# import from fex.pp
+our ($FEXHOME,$hostname,$sendmail,$spooldir,$admin,$bcc);
+
+# load common code, local config : $HOME/lib/fex.ph
+require "$FEXLIB/fex.pp" or die "$0: cannot load $FEXLIB/fex.pp - $!\n";
+
+die "$0: \$admin not configured in $FEXLIB/fex.ph\n" 
+  if not $admin or $admin =~ /example.org/;
+
+$opt_h = 0;
+
+getopts('h') or usage(2);
+usage(0) if $opt_h;
+
+$subject = "@ARGV";
+die usage(1) unless $subject;
+
+local $/;
+$text = <STDIN>;
+die usage(1) unless $text;
+
+if (open my $sig,$ENV{HOME}.'/.signature') {
+  $text .= "\n-- \n" . <$sig>;
+  close $sig;
+}
+
+local $/ = "\n";
+
+chdir $spooldir or die "$0: $spooldir - $!\n";
+
+# @users = grep { chomp;s:/@:: } glob("*/@");
+foreach $user (glob("*@*")) {
+  if (-f "$user/@" and (readlink "$user/\@NOTIFICATION"||'') !~ /no/i) {
+    push @users,$user;
+  }
+}
+
+foreach $group (glob "*/\@GROUP/*") {
+  if (open $group,$group) {
+    while (<$group>) {
+      s/#.*//;
+      s/:.*\n//;
+      push @users,$_ if /@/;
+    }
+    close $group;
+  }
+}
+    
+foreach $subuser (glob "*/\@SUBUSER") {
+  if (open $subuser,$subuser) {
+    while (<$subuser>) {
+      s/#.*//;
+      s/:.*\n//;
+      push @users,$_ if /@/;
+    }
+    close $subuser;
+  }
+}
+    
+# @users = qw'framstag@fex';
+die "$0: no users found\n" unless @users or grep /@/,@users;
+push @users,$bcc;
+@users = uniq(@users);
+
+open $sendmail,'|-',$sendmail,@users or die "$0: $sendmail - $!\n";
+
+print {$sendmail}
+  "From: $admin\n",
+  "To: fexusers\@$hostname\n",
+  "Subject: $subject\n",
+  "\n",
+  $text;
+
+close $sendmail or die "$0: $sendmail - $!\n";
+print "mail sent to:\n",map { "$_\n" } @users;
+exit;
+
+sub uniq {
+  my %x;
+  grep !$x{$_}++,@_;
+}
+
+sub usage {
+  print "usage: $0 \"SUBJECT\" < mail.text\n";
+  exit shift;
+}
diff --git a/bin/l b/bin/l
new file mode 100755 (executable)
index 0000000..93c4aca
--- /dev/null
+++ b/bin/l
@@ -0,0 +1,597 @@
+#!/usr/bin/perl -w
+#
+# l / ll / lf / llf -  substitute of the classic ls command
+#
+# Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
+#
+# Copyright: GNU General Public License 
+
+use Cwd qw'abs_path';
+use File::Basename;
+use Getopt::Std;
+
+# the name of the game
+$0 =~ s:.*/::;
+
+$ENV{LC_CTYPE} = 'C';
+
+# unshift @ARGV,split /\s+/,$ENV{'l_opt'} if $ENV{'l_opt'};
+
+@ARGV = grep { chomp } <STDIN> if "@ARGV" eq '-';
+
+# parse CLI arguments
+$opt_l = $opt_i = $opt_t = $opt_s = $opt_a = $opt_r = $opt_d = $opt_n = 0;
+$opt_L = $opt_N = $opt_c = $opt_u = $opt_S = $opt_R = $opt_z = $opt_h = 0;
+$opt_U = 0;
+${'opt_*'} = ${'opt_?'} = 0;
+$opt_m = $opt_f = $opt_F = $opt_D = '';
+&usage if !getopts('hdnlLNitcuarsUSRz*?m:f:D:F:') || $opt_h;
+$opt_z = 1 unless $opt_R;
+$opt_l = 1                            if $0 eq 'll';
+$opt_l = $opt_i = $opt_a = $opt_S = 1 if $0 eq 'lll';
+if ($0 eq 'lf' or $0 eq 'llf') {
+  unless ($opt_F) {
+    $opt_F = shift;
+    unless (length $opt_F) {
+      print "find regexp: ";
+      chomp($opt_F = <STDIN>||'');
+    }
+  }
+  $opt_l = $0 if $0 eq 'llf';
+  $opt_F = '.' unless length $opt_F;
+  $opt_R = $opt_F;
+}
+
+$postsort = $opt_t||$opt_s;
+$postproc = $postsort||$opt_z;
+
+&examples if ${'opt_?'};
+
+# mark for squeeze operation
+$z = $opt_z ? "\0" : '';
+
+# default sorting methode
+if    ($opt_U) { $lcsort = sub { return @_ } }
+elsif ($opt_r) { $lcsort = sub { sort { lc $b cmp lc $a } @_ } }
+else           { $lcsort = sub { sort { lc $a cmp lc $b } @_ } }
+
+# default: list only files not beginning with a dot
+unless ($opt_m) {
+  if ($opt_a) { $opt_m = '.' }
+  else        { $opt_m = '^[^\.]' }
+}
+
+$older = $newer = 0;
+
+if ($opt_D) {
+  if ($opt_D =~ /:(\d+)([mhd])/) {
+    $older = $1;
+    my $z = $2 || 's';
+    if    ($z =~ /m/) { $older *= 60 }
+    elsif ($z =~ /h/) { $older *= 60*60 }
+    elsif ($z =~ /d/) { $older *= 60*60*24 }
+  } elsif ($opt_D =~ /:(\d\d\d\d-\d\d-\d\d)$/) {
+    $older = $1;
+  }
+  if ($opt_D =~ /(\d+)([mhd]):/) {
+    $newer = $1;
+    my $z = $2 || 's';
+    if    ($z =~ /m/) { $newer *= 60 }
+    elsif ($z =~ /h/) { $newer *= 60*60 }
+    elsif ($z =~ /d/) { $newer *= 60*60*24 }
+  } elsif ($opt_D =~ /^(\d\d\d\d-\d\d-\d\d):/) {
+    $newer = $1;
+  }
+}
+  
+# preselect date field number
+if    ($opt_c) { $sdf = 'c' }
+elsif ($opt_u) { $sdf = 'a' }
+else           { $sdf = 'm' }
+
+# any arguments?
+if (@ARGV) { @ARGV = &$lcsort(@ARGV) }
+else       { @ARGV = &getfiles('.') }
+
+# build files list
+&collect(@ARGV);
+
+# post process files list?
+# remark: if no postprocessing, files list has been already printed in list()
+if (@LIST && $postproc) {
+
+  # on -t or -s option sort list on date or size
+  # and then strip of leading sorting pre-string
+  @LIST = grep { s/.{21}// } reverse sort @LIST if $postsort;
+
+  # squeeze size field (= remove unnecessary spaces)
+  if ($opt_z and not $opt_f) {
+    $opt_z = '%'.$opt_z.'s ';
+    @LIST = grep { s/\0 *([,\d\.\-]+) /sprintf($opt_z,$1)/e } @LIST;
+  }
+  
+  @LIST = reverse @LIST if $opt_r;
+
+  if (not ($opt_t or $opt_U) and grep /^d[rR-][wW-][xX-]/,@LIST) {
+    foreach (@LIST) { print if /^d/ }
+    foreach (@LIST) { print unless /^d/ }
+  } else { 
+    print @LIST;
+  }
+}
+
+# print statistics summary?
+if ($opt_S && $SS) {
+  print "$SS file(s):";
+  printf " r=%d (%s Bytes)",$SS{'-'},&d3($Ss) if $SS{'-'};
+  delete $SS{'-'};
+  foreach my $type (qw(l d c b p s ?)) { 
+    printf " %s=%d",$type,$SS{$type} if $SS{$type};
+    delete $SS{$type};
+  }
+  foreach my $type (keys %SS) { printf " %s=%d",$type,$SS{$type} }
+  print "\n";
+}
+
+exit ($found ? 0 : 1);
+
+
+# collect files and build file lists
+# 
+# INPUT: filenames
+#
+# GLOBAL: @LIST
+sub collect {
+  my @files = @_;
+  my $f;
+
+  # loop over all argument files/directories
+  foreach $f (@files) {
+  
+    # skip jed and emacs backup files
+    # next if $f =~ /~$/ and not $opt_a and not $opt_l;
+    
+    # recursive?
+    if ($opt_R) {
+
+      # list single file
+      if ($opt_L) {
+        unless (-e $f) {
+          warn "$0: dangling symlink $f\n";
+          next;
+        }
+        $f = abs_path($f);
+      }
+      list($f);
+
+      # traverse real subdirs
+      if (-d $f and not -l $f) { 
+        $f =~ s:/*$:/:;
+        collect(getfiles($f));
+      }
+      
+    } else {
+  
+      # suppress trailing / on -d option
+      $f =~ s:/$:: if $opt_d;
+    
+      # on trailing / list subdirs, too
+      if ($f =~ m:/$:) { &list(&getfiles($f)) }
+      elsif ($f eq '') { &list('/') }
+      else {
+        if ($opt_L) {
+          unless (-e $f) {
+            warn "$0: dangling symlink $f\n";
+            next;
+          }
+          $f = abs_path($f);
+        }
+        list($f);
+      }
+      
+    }
+  }
+}
+
+
+# list file(s)
+#
+# INPUT: filenames
+#
+# GLOBAL: @LIST (filenames-list)
+sub list {
+  my @files = @_;
+  my ($file,$line,$linkname,$inode,$links,$size,$mode,$uid,$gid,$date,%dates);
+  my ($day);
+
+  foreach $file (@files) {
+
+    next if $opt_F and not fmatch($file);
+    next if $opt_N and (not -f $file or -l $file);
+
+    # get file information
+    # if ($opt_L and stat $file or not $opt_L and lstat $file) {
+    if (lstat $file) {
+      ($linkname,$inode,$links,$size,$mode,$uid,$gid,$date,%dates) = &info($file);
+    } elsif ($! eq "Permission denied") {
+      $linkname = $file;
+      $inode = $links = $size = $uid = $gid = '?';
+      $mode = $opt_l ? '?---------' : '?---';
+      $date = '????-??-?? ??:??:??';
+      %dates = ('m' => 0, 'a' => 0, 'c' => 0);
+    } else {
+      warn "$0: ".quote($file)." - $!\n";
+      next;
+    }
+
+    $day = $date;
+    $day =~ s/\s.*//;
+    
+    if ($older) {
+      next if $older =~ /-/ and $day gt $older;
+      next if $older !~ /-/ and $dates{m} > time-$older;
+    }
+    if ($newer) {
+      next if $newer =~ /-/ and $day lt $newer;
+      next if $newer !~ /-/ and $dates{m} < time-$newer;
+    }
+    
+    if (defined $linkname) {
+
+      # prepend sorting string
+      $line = '';
+      $line = sprintf '%21s',$date if $opt_t;
+      $line = sprintf '%21s',$size if $opt_s;
+
+      unless ($opt_n) {
+        $uid = substr($uid,0,8);
+        $gid = substr($gid,0,8);
+      }
+
+      # user defined format?
+      if ($opt_f) {
+        foreach my $i (split '',$opt_f) {
+         if ($opt_n) {
+           $i =~ tr/AD/ad/;
+           if    ($i eq 'm') { $line .= sprintf '%06o ',  $mode }
+           elsif ($i eq 'u') { $line .= sprintf '%6d ',   $uid }
+           elsif ($i eq 'g') { $line .= sprintf '%6d ',   $gid }
+           elsif ($i eq 's') { $line .= sprintf "$z%16s ",$size }
+           elsif ($i eq 'l') { $line .= sprintf '%3s ',   $links }
+           elsif ($i eq 'i') { $line .= sprintf '%14s ',  $inode }
+           elsif ($i eq 'd') { $line .= sprintf '%10s ',  $date }
+           elsif ($i eq 'a') { $line .= sprintf '%10s %10s %10s ', 
+                                        $dates{'a'},$dates{'m'},$dates{'c'} }
+         } else {
+           if    ($i eq 'm') { $line .= $mode.' ' }
+           elsif ($i eq 'u') { $line .= sprintf '%-8s ',  $uid }
+           elsif ($i eq 'g') { $line .= sprintf '%-8s ',  $gid }
+           elsif ($i eq 's') { $line .= sprintf "$z%19s ",$size }
+           elsif ($i eq 'l') { $line .= sprintf '%3s ',   $links }
+           elsif ($i eq 'i') { $line .= sprintf '%14s ',  $inode }
+           elsif ($i eq 'd') { $line .= $date.' ' }
+           elsif ($i eq 'D') { $line .= $date.' ' }
+           elsif ($i eq 'a') { $line .= &isodate($dates{'a'}).' '.
+                                        &isodate($dates{'m'}).' '.
+                                        &isodate($dates{'c'}).' ' }
+           elsif ($i eq 'A') { $line .= &isodate($dates{'a'}).' '.
+                                        &isodate($dates{'m'}).' '.
+                                        &isodate($dates{'c'}).' ' }
+         }
+       }
+       
+      # predefined formats
+      } else {
+      
+       if ($opt_n) {
+          if ($opt_l) { $line .= sprintf "%06o %6d %6d $z%15s %10d ",
+                                        $mode,$uid,$gid,$size,$date }
+          else        { $line .= sprintf "%06o $z%15s %10d ",
+                                        $mode,$size,$date }
+       } else {
+          if ($opt_l) { $line .= sprintf "%s %-8s %-8s $z%19s %s ",
+                                        $mode,$uid,$gid,$size,$date }
+          else        { $line .= sprintf "%s $z%19s %s ",
+                                        $mode,$size,substr($date,0,-3) }
+        }
+       
+       if ($opt_i)   { $line .= sprintf '%3s %10s ',$links,$inode }
+      }
+
+      $line .= $linkname."\n";
+      
+      if ($postproc) { 
+        push @LIST,$line;
+      } else { 
+        $line =~ s/\0//;
+        print $line;
+      }
+      $found++;
+      
+    } else {
+      lstat $file;
+      warn "$0: cannot get dir-info for ".quote($file)." - $!\n";
+    }
+    
+  }
+}
+
+# get file information
+#
+# INPUT: file name
+#
+# OUTPUT: filename with linkname, inode, hard link count, size, mode string, 
+#         UID, GID, isodate
+sub info {
+  my $file = shift;
+  my ($linkname,$links,$mode,$bmode,$uid,$gid,$date,%dates,@stat);
+  my $size = '-';
+  my $inode = '?';
+  my @rwx = qw/--- --x -w- -wx r-- r-x rw- rwx/;
+  my $type;
+
+  if ($opt_L) { @stat = stat $file }
+  else        { @stat = lstat $file }
+  
+  if (@stat) {
+  
+    $inode = $stat[1];
+    $bmode = $stat[2];
+    $links = $stat[3];
+    %dates = ('m' => $stat[9], 
+              'a' => $stat[8], 
+             'c' => $stat[10]);
+
+    if ($opt_n) {
+      $uid  = $stat[4];
+      $gid  = $stat[5];
+      $date = $dates{$sdf};
+    } else {
+      $uid  = getpwuid($stat[4]) || $stat[4];
+      $gid  = getgrgid($stat[5]) || $stat[5];
+      $date = &isodate($dates{$sdf});
+    }
+    
+    if    (-f _)           { $type = '-'; $size = $stat[7]; }
+    elsif (!$opt_L && -l _) { $type = 'l'; }
+    elsif (-d _)            { $type = 'd'; }
+    elsif (-c _)            { $type = 'c'; $size = &nodes($stat[6]); }
+    elsif (-b _)            { $type = 'b'; $size = &nodes($stat[6]); }
+    elsif (-p _)            { $type = 'p'; }
+    elsif (-S _)            { $type = 's'; }
+    else                    { $type = '?'; }
+
+    if ($opt_n) {
+      $mode = $stat[2];
+      $size = $stat[7] if $size eq '-';
+    } else {
+      if ($opt_l) {
+        $mode = $rwx[$bmode & 7];
+        $bmode >>= 3;
+        $mode = $rwx[$bmode & 7] . $mode;
+        $bmode >>= 3;
+        $mode = $rwx[$bmode & 7] . $mode;
+        substr($mode,2,1) =~ tr/-x/Ss/ if -u _;
+        substr($mode,5,1) =~ tr/-x/Ss/ if -g _;
+        substr($mode,8,1) =~ tr/-x/Tt/ if -k _;
+        $mode = $type.$mode;
+      } else {
+        # with short list display only effektive file access modes
+        $mode = $type 
+               . (-r _ ? 'R' : '-')
+                . (-w _ ? 'W' : '-')
+                . (-x _ ? 'X' : '-');
+        substr($mode,2,1) =~ tr/-x/Ss/ if -u _ or -g _;
+        substr($mode,3,1) =~ tr/-x/Tt/ if -k _;
+      }
+    }
+
+  # fall back to ls command if perl lstat failed
+  } else {
+    if ($opt_L) {
+      return;
+    } else {
+      ($mode,$links,$uid,$gid,$size) = split /\s+/,`ls -ld $file 2>/dev/null`;
+      return undef unless defined $mode;
+      $type = substr($mode,0,1);
+      # for (my $i=0;$i<3;$i++) { push @dates,'????-??-?? ??:??:??' }
+      # $date = `gfind $dir -maxdepth 1 -name $file -printf '%Ty-%Tm-%Td %TT\n'`;
+    }
+  }
+
+  # summarize statistics
+  if ($opt_S) {
+    $SS++;
+    $SS{$type}++;
+    $Ss += $size if $type eq '-';
+  }
+
+  $size = &d3($size);
+  
+  # determine longest size field
+  if ($opt_z) {
+    my $x = length $size;
+    $opt_z = $x if $x>$opt_z;
+  }
+  $linkname = ${'opt_*'} ? $file : quote($file) ;
+  if ($type eq 'l' and $opt_f !~ /n/) {
+    my $link = readlink($file);
+    if (defined $link) {
+      $linkname .= ' -> ' . (${'opt_*'} ? $link : quote($link));
+    }
+  }
+  $mode =~ s/\+$//;
+  #$mode .= ' ' unless $mode =~ /\+$/;
+  
+  return ($linkname,$inode,$links,$size,$mode,$uid,$gid,$date,%dates);
+}
+
+
+# reformat integer into 3-digit doted format
+# (when non-numerical mode is set)
+#
+# INPUT: integer or '-'
+#
+# OUTPUT: d3-string
+sub d3 {
+  local $_ = shift;
+  if ($opt_n) { s/-/0/ }
+  else        { while (s/(\d)(\d\d\d\b)/$1,$2/) {} }
+  return $_;
+}
+
+
+# get all files matching pattern $opt_m
+#
+# INPUT: directory to scan
+#
+# OUTPUT: files which match (sorted, directories first)
+sub getfiles {
+  my $dir = shift;
+  my @files = ();
+  my @dirs = ();
+  my $f;
+
+  if (opendir D,$dir) {
+    $dir = '' if $dir eq '.';
+    while (defined($f = readdir D)) { 
+    
+      # skip . and .. pseudo-subdirs
+      next if $f =~ m:(^|/)\.\.?/*$:;
+      # skip ONTAP snapshot dir
+      next if $f =~ m:(^|/)\.snapshot/*$:;
+      
+
+      # skip jed and emacs backup files
+      # next if $f =~ /~$/ and not $opt_a and not $opt_l;
+      
+      if ($f =~ /$opt_m/) {
+        my $x = $dir.$f;
+        if (not -l $x and -d $x and not ($opt_R or $postsort or $opt_U)) { 
+          push @dirs,$x;
+        } else { 
+          push @files,$x;
+        }
+      }
+    }
+    closedir D;
+    unless ($postsort) {
+      @files = &$lcsort(@files);
+      @dirs  = &$lcsort(@dirs);
+    }
+  } else {
+    warn "$0: cannot read $dir : $!\n";
+  }
+  
+  return (@dirs,@files);
+}
+
+
+# reformat integer to string node
+#
+# INPUT: integer node
+#
+# OUTPUT: string node
+sub nodes {
+  my $rdev = shift;
+  return sprintf("%03d,%03d", ($rdev >> 8) & 255, $rdev & 255);
+}
+
+
+# reformat timetick to ISO date string
+#
+# INPUT: timetick
+#
+# OUTPUT: ISO date string
+sub isodate {
+  my @d = localtime shift;
+  return sprintf('%d-%02d-%02d %02d:%02d:%02d',
+                 $d[5]+1900,$d[4]+1,$d[3],$d[2],$d[1],$d[0]);
+}
+
+
+# quote file name to printable name and escape shell meta chars
+#
+# INPUT: original file name
+#
+# OUTPUT: printable file name
+sub quote {
+  local $_ = shift;
+  my $mc = '\'\[\]\\\\ `"$?&<>$*()|{};';
+  
+  unless (defined $_) {
+    die "@_";
+    @x = caller;
+    die "@x";
+  }
+  if (s/[\000-\037\200-\237\241-\250]/?/g or /\'/) {
+    s/([$mc])/\\$1/g;
+    s/^~/\\~/;
+# } elsif (/[$mc]/ or -d and /:/) {
+  } elsif (/[$mc]/) {
+    $_ = "'$_'";
+  }
+  return $_;
+}
+
+
+sub fmatch {
+  my $file = shift;
+  my $link = readlink($file)||'';
+
+  return $file if basename($file) =~ /$opt_F/i;
+  return $link if basename($link) =~ /$opt_F/i;
+}
+
+
+sub usage {
+  my $opts = '[-lastcuidnrzLRNS*] [-f format] [-D X:Y]';
+  if ($0 ne 'lf') { 
+    print "usage: $0 $opts [-F regexp] [file...]\n";
+  }
+  $opts =~ s/R//;
+  print "usage: lf $opts regexp [directory...]\n";
+  print <<EOD;
+options: -l  long list
+         -a  list also .* files
+         -s  sort by size
+         -t  sort by time
+         -U  sort by nothing (original i-node order)
+         -c  list status change time instead of modification time
+         -u  list last access time instead of modification time
+         -i  list also inode and hard links numbers
+         -d  do not list contents of diretories
+         -n  numerical output
+         -r  reverse list
+         -z  squeeze size field (slows down output)
+         -L  derefernce symbolic links
+         -R  recursive into subdirs
+         -F  find files matching case insensitive regexp
+         -N  show only normal (regular) files
+         -S  print statistics summary at end
+         -*  list plain file names (without masking \\)
+        -f  user defined format output, format characters are:
+            m=mode, u=user, g=group, s=size, l=hard links count, i=inode
+            n=name only, d=date, a=access+modification+inodechange dates
+         -D  list only files newer than X and older than Y 
+             XY format: NUMBER[smhd] (s=seconds, m=minutes, h=hours, d=days)
+             XY format: YYYY-MM-DD (Y=year, M=month, D=day)
+         -?  show examples
+EOD
+  exit 2;
+}
+
+sub examples {
+  print <<EOD;
+l *.c            # list files ending with .c
+l -la            # list all files in long format
+l -Rrs           # list files recursive reverse sorted by size 
+l -*f mus        # list files native names with format: mode+user+size
+l -D 10d:        # list files newer than 10 days
+ll               # list files long format (equal to: l -l)
+lll              # list files extra long format (equal to: l -liS)
+lf 'status.*mp3' # list files recursive matching regexp (equal to: l -RF)
+lf sda3 /dev     # list devices matching sda3 (equal to: l -RF sd3 /dev)                                                       
+EOD
+  exit;
+}
diff --git a/bin/lf b/bin/lf
new file mode 120000 (symlink)
index 0000000..baf72b1
--- /dev/null
+++ b/bin/lf
@@ -0,0 +1 @@
+l
\ No newline at end of file
diff --git a/bin/ll b/bin/ll
new file mode 120000 (symlink)
index 0000000..baf72b1
--- /dev/null
+++ b/bin/ll
@@ -0,0 +1 @@
+l
\ No newline at end of file
diff --git a/bin/logwatch b/bin/logwatch
new file mode 100755 (executable)
index 0000000..7ef1d4b
--- /dev/null
@@ -0,0 +1,211 @@
+#!/usr/bin/perl -w
+
+use Encode;
+use File::Basename;
+use Cwd 'abs_path';
+use I18N::Langinfo qw'langinfo CODESET';
+
+# add fex lib
+unless ($FEXLIB = $ENV{FEXLIB}) {
+  if ($ENV{FEXHOME}) {
+    $FEXLIB = $ENV{FEXHOME}.'/lib';
+  } elsif (-f '/usr/share/fex/lib/fex.ph') {
+    $FEXLIB = '/usr/share/fex/lib';
+  } else {
+    $FEXLIB = dirname(dirname(abs_path($0))).'/lib';
+  }
+  $ENV{FEXLIB} = $FEXLIB;
+}
+die "$0: no $FEXLIB\n" unless -d $FEXLIB;
+
+# import from fex.pp
+our ($logdir,$spooldir,$debug);
+
+# load common code, local config : $HOME/lib/fex.ph
+require "$FEXLIB/fex.pp" or die "$0: cannot load $FEXLIB/fex.pp - $!\n";
+
+$CTYPE = langinfo(CODESET());
+binmode(STDOUT,":encoding($CTYPE)");
+
+$log = shift || "$logdir/fexsrv.log";
+
+$ignore = join('|',qw(
+  (CONNECT|CONTINUE).*(crawl|msnbot|obertux)
+  DISCONNECT:.no.HTTP.request
+  GET.*(favicon|robots\.txt)
+  GET./organization\.gif
+  GET./small_logo\.jpg
+  GET./logo\.jpg
+  GET./action-fex-camel\.gif
+  GET./fup\?showstatus
+  GET./FAQ/faq\.css
+  GET./FAQ/jquery\.js
+  GET./10+.B
+  GET.*Arrow\.gif
+  GET./apple-touch
+  User-Agent:.*(Webnote|FeedFetcher|\w+bot|bot/|Website.Watcher|crawler|spider|searchme|Yandex|Slurp|ScoutJet|findlinks|urlmon|nagios)
+  User-Agent:.fnb.*quak
+  From:.*(msnbot|yandex|googlebot|webcrawler)
+  Referer:.*sex.*stream
+  Referer:.*stream.*sex
+  X-.*prefetch
+  X-Purpose:.preview
+));
+
+@weed = qw(
+  .*keep-alive
+  .*no-cache
+  Connection:
+  Cache-Control:
+  Content-Type:
+  Accept
+  TE:
+  UA-CPU:
+  Pragma:
+  DNT:
+  Via:
+  profile:
+  if-modified-since
+  Surrogate-Capability
+  Proxy-Authorization
+  http\.
+  NOKIA_
+  GPRS
+  X-Proxy-ID
+  X-Moz
+  X.Wap
+  X-FH
+  X-Nokia
+  X-UCBrowser
+  X-NSN
+  X-OperaMini
+  x-Device
+  x-source-id
+  x.up
+  X-Behavioral
+  X-Do-Not-Track
+  X-\S*Via
+  x-Mobile
+  X-Country
+  X-ClickOnceSupport
+  .*:\s*$
+);
+
+$/ = "\n\n"; 
+$| = 1;
+
+if (-t STDIN or $ENV{GATEWAY_INTERFACE}) {
+  open L,$log or die "$0: $log - $!\n";
+  seek L,0,2;
+} else {
+  *L = *STDIN;
+}
+# binmode(L,":encoding(UTF-8)");
+
+for (;;) {
+  while (<L>) {
+    next if /(^|\n)($ignore)/i;
+    s/[\x00-\x08\x0B-\x1F\x1F\x80-\x9F]/_/g;
+    s/^\n//;
+    foreach $weed (@weed) {
+      while (s/\n$weed.*\n/\n/i) {}
+    }
+    if (/^\n*(CONNECT|CONTINUE).*\s\[([\d_]+)\]/i) { $pid = $2 }
+    if (/\n(POST|GET)\s+\/(\w+)/i)                 { $cgi = $2 }
+    if (/Content-Length: (\d+)/i) {
+      $d = $1;
+      while ($d =~ s/(\d)(\d\d\d\b)/$1,$2/) {};
+      s/Content-Length: \d+/Content-Length: $d/i;
+    }
+    s/[\s\n]*$/\n\n/;
+    print or exit;
+    $from = '';
+    if (m:\nGET /fup/(\w{40,}):) {
+      $_ = decode_b64($1);
+      printf "  FROM=\"%s\"\n\n",$1 if /from=([\w\@.-]+)/;
+    } elsif (m:\nGET /fop/(\w+)/:) {
+      $dkey = $1;
+      my $ddir = "$spooldir/.dkeys/$dkey";
+      $_ = readlink $ddir or next;
+      (undef,$to,$from) = split('/');
+      printf "  FROM=\"%s\"\n",$from;
+      printf "  TO=\"%s\"\n",$to;
+      $cgi = '';
+      if ($comment = slurp("$ddir/comment")) {
+        printf "  COMMENT=\"%s\"\n",decode_utf8($comment,0)||'';
+      }
+      if (not -f "$ddir/data" and $_ = slurp("$ddir/error")) {
+        s/\n.*//s;
+        print "  ERROR=\"$_\"\n";
+      }
+      elsif ($size = -s "$ddir/data") {
+        printf "  SIZE=%s MB\n",int($size/1024/1024);
+      }
+      print "\n";
+    } elsif (m:\nGET /fup.*skey=(\w+):) {
+      read_skey($1);
+      print "\n";
+    }
+  }
+  sleep 1;
+  if ($debug and $pid and $cgi) {
+    &read_debug_log;
+    $pid = $cgi = '';
+  };
+}
+
+
+sub read_debug_log {
+  my (@log,$log);
+  local $/ = "\n";
+  local $_;
+  local $^W;
+  # no warnings "all";
+
+  for (1..2) {
+    sleep 1;
+    @log = `ls -rt $logdir/.debug/*_${pid}.$cgi 2>/dev/null`;
+    if ($log = $log[-1] and open $log,$log) {
+      # binmode($log,":encoding(UTF-8)");
+      while (<$log>) {
+        s/\r//;
+        if (/^Content-Disposition:.*name="FILE".*filename="(.+)"/i) {
+          print "  FILE=\"$1\"\n";
+        } elsif (/^Content-Disposition:.*name="(\w+)"/i) {
+          my $p = uc($1);
+          $_ = <$log>;
+          my $v = <$log>||'';
+          $v =~ s/[\r\n]+//;
+          printf "  %s=\"%s\"\n",$p,decode_utf8($v,0)||$v if $v;
+          read_akey($v) if $p eq 'AKEY';
+          read_skey($v) if $p eq 'SKEY';
+        } elsif (/^(Param|Exp): (\w+=".+")/) {
+          print "  $2\n";
+        }
+      }
+      close $log;
+      print "\n";
+      return;
+    }
+  }
+}
+
+sub read_akey {
+  my $akey = "$spooldir/.akeys/" . shift;
+  if (my $user = readlink($akey)) {
+    $user =~ s:../::;
+    printf "  USER=\"%s\"\n",$user;
+  }
+}
+
+
+sub read_skey {
+  my $skey = "$spooldir/.skeys/" . shift;
+  if (open $skey,$skey) {
+    while (<$skey>) {
+      printf "  FROM=\"%s\"\n",$1 if /from=(.+)/;
+      printf "  TO=\"%s\"\n",$1   if /to=(.+)/;
+    }
+    close $skey;
+  }
+}
diff --git a/bin/mksgkeys b/bin/mksgkeys
new file mode 100755 (executable)
index 0000000..6deec68
--- /dev/null
@@ -0,0 +1,64 @@
+#!/usr/bin/perl -w
+
+# helper script to regenerate missing SKEYs and GKEYs
+
+use Digest::MD5 qw(md5_hex);
+
+@pw = getpwnam('fex');
+$spool = $pw[7].'/spool';
+chdir $spool or die "$spool - $!";
+
+foreach $suf (glob "*/\@SUBUSER") {
+  if (-f $suf and open $suf,$suf) {
+    $to = $user = $suf;
+    $to =~ s:/.*::;
+    $user =~ s:/.*::;
+    while (<$suf>) {
+      chomp;
+      s/#.*//;
+      if (/(.+):(.+)/) {
+        $from = $1;
+        $id = $2;
+        $skey = md5_hex("$user:$from:$id");
+        unless (-f ".skeys/$skey") {
+          if (open $skey,">.skeys/$skey") {
+            warn "creating $spool/.skeys/$skey\n";
+            print {$skey} "from=$from\n";
+            print {$skey} "to=$to\n";
+            print {$skey} "id=$id\n";
+            close $skey;
+          }
+        }
+      }
+    }
+    close $suf;
+  }
+}
+
+foreach $guf (glob "*/\@GROUP/*") {
+  if (-f $guf and not -l $guf and open $guf,$guf) {
+    $group = $user = $guf;
+    $group =~ s:.*/::;
+    $user =~ s:/.*::;
+    while (<$guf>) {
+      chomp;
+      s/#.*//;
+      if (/(.+):(.+)/) {
+        $from = $1;
+        $id = $2;
+        $gkey = md5_hex("$user:$group:$from:$id");
+        unless (-f ".gkeys/$gkey") {
+          if (open $gkey,">.gkeys/$gkey") {
+            warn "creating $spool/.gkeys/$gkey\n";
+            print {$gkey} "from=$from\n";
+            print {$gkey} "to=\@$group\n";
+            print {$gkey} "user=$user\n";
+            print {$gkey} "id=$id\n";
+            close $gkey;
+          }
+        }
+      }
+    }
+    close $guf;
+  }
+}
diff --git a/bin/sexget b/bin/sexget
new file mode 120000 (symlink)
index 0000000..73cf1eb
--- /dev/null
@@ -0,0 +1 @@
+sexsend
\ No newline at end of file
diff --git a/bin/sexsend b/bin/sexsend
new file mode 100755 (executable)
index 0000000..7340e49
--- /dev/null
@@ -0,0 +1,723 @@
+#!/usr/bin/perl -w
+
+# client for stream exchange of the FEX service
+#
+# Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
+#
+# Perl Artistic Licence
+
+# sexsend / sexget / sexxx
+
+use Getopt::Std;
+use Socket;
+use IO::Handle;
+use IO::Socket::INET;
+use Digest::MD5 qw(md5_hex);  # encypted ID / SID 
+
+use constant k => 2**10;
+use constant M => 2**20;
+
+eval 'use Net::INET6Glue::INET_is_INET6';
+
+our $version = 20150120;
+
+my %SSL = (SSL_version => 'TLSv1');
+my $sigpipe;
+
+if (-f ($_ = '/etc/fex/config.pl')) {
+  eval { require } or warn $@;
+}
+
+$0 =~ s:.*/::;
+$| = 1;
+
+# sexsend is default
+$usage = 
+  "usage: ... | $0 [options] [SEX-URL/]recipient [stream]\n".
+  "options: -v           verbose mode\n".
+  "         -g           show transfer rate\n".
+  "         -V           show version\n".
+  "         -t timeout   timeout in s (waiting for recipient)\n".
+  "special: recipient may be \"public\" or \"anonymous\" or \".\"\n".
+  "see also: sexget, sexxx\n".
+  "example: tail -f /var/log/syslog | $0 fex.flupp.org/admin log\n";
+
+if ($0 eq 'sexget' or $0 eq 'fuckme') {
+  $usage = 
+    "usage: $0 [options] [[SEX-URL/]user:ID] [stream]\n".
+    "options: -v           verbose mode\n".
+    "         -g           show transfer rate\n".
+    "         -V           show version\n".
+    "arguments: user:ID    use this user & ID\n".
+    "                      (ID may be \"public\" or user:ID may be \"anonymous\")\n".
+    "           stream     name of the stream\n".
+    "see also: sexsend, sexxx\n".
+    "example: $0 log | grep kernel\n";
+}
+
+if ($0 eq 'sexxx') {
+  $usage = 
+    "usage: $0 [-v] [-g] [-c] [-u [SEX-URL/]user] [-s stream] [files...]\n".
+    "usage: $0 [-v] [-g]      [-u [SEX-URL/]user] [-s stream] | ...\n".
+    "options: -v               verbose mode\n".
+    "         -g               show transfer rate\n".
+    "         -q               quiet mode\n".
+    "         -c               compress files\n".
+    "         -u SEX-URL/user  SEX-URL and user (default: use FEXID/FEXXX)\n".
+    "         -s stream        stream name (default: xx)\n".
+    "see also: sexsend, sexget\n".
+    "examples: $0 -s config /etc /usr/local/etc\n".
+    "          $0 > backup.tar\n";
+}
+
+$fexhome = $ENV{FEXHOME} || $ENV{HOME}.'/.fex';
+$user = $id = '';
+$type = $timeout = $stream = $mode = '';
+$idf = "$fexhome/id";
+$bs = $ENV{BS} || 2**16; # I/O blocksize
+
+# server URL, user and auth-ID
+if ($FEXID = $ENV{FEXID}) {
+  $FEXID = decode_b64($FEXID) if $FEXID !~ /\s/;
+  ($fexcgi,$user,$id) = split(/\s+/,$FEXID);
+} else {
+  if (open $idf,$idf) {
+    chomp($fexcgi = <$idf>) or die "$0: no FEX-URL in $idf\n";
+    chomp($user = <$idf>)   or die "$0: no FROM in $idf\n";
+    chomp($id = <$idf>)     or die "$0: no ID in $idf\n";
+    close $idf;
+    despace($fexcgi,$user,$id);
+    unless ($fexcgi =~ /^[_:=\w\-\.\/\@\%]+$/) {
+      die "$0: illegal FEX-URL \"$fexcgi\" in $idf\n";
+    }
+    unless ($user =~ /^[_:=\w\-\.\/\@\%\+]+$/) {
+      die "$0: illegal FROM \"$user\" in $idf\n";
+    }
+  }
+}
+
+$opt_h = $opt_v = $opt_V = $opt_q = 0;
+$opt_u = $opt_s = $opt_c = $opt_t = '';
+
+$_ = "$fexhome/config.pl"; require if -f;
+
+if ($0 eq 'sexxx') {
+  
+  # xx server URL, user and auth-ID
+  if ($FEXXX = $ENV{FEXXX}) {
+    $FEXXX = decode_b64($FEXXX) if $FEXXX !~ /\s/;
+    ($fexcgi,$user,$id) = split(/\s+/,$FEXXX);
+  } elsif (open $idf,$idf) {
+    while (<$idf>) {
+      if (/^\[xx\]/) {
+        chomp($fexcgi = <$idf>) or die "$0: no xx FEX-URL in $idf\n";
+        chomp($user = <$idf>)   or die "$0: no xx FROM in $idf\n";
+        chomp($id = <$idf>)     or die "$0: no xx ID in $idf\n";
+        last;
+      }
+    }
+    close $idf;
+  }
+  
+  getopts('hgvcu:s:') or die $usage;
+  die $usage if $opt_h;
+  die $usage unless -t;
+
+  if ($opt_c) {
+    $opt_c = 'z';
+    $type = '&type=GZIP';
+  }
+
+  if ($opt_u) {
+    $fexcgi = $1 if $opt_u =~ s:(.+)/::;
+    $user = $opt_u;
+  }
+
+  unless ($fexcgi) {
+    die "$0: no xx user found, use \"$0 -u SEX-URL/user\"\n";
+  }
+
+  unless ($user) {
+    die "$0: no xx user found, use \"$0 -u user\"\n";
+  }
+  
+} elsif ($0 eq 'sexget' or $0 eq 'fuckme') {
+  getopts('hgvVdu:') or die $usage;
+  die $usage if $opt_h;
+
+
+  if ($opt_V) {
+    print "Version: $version\n";
+    exit unless @ARGV;
+  }
+  
+  if (not $opt_u and @ARGV and $ARGV[0] =~ m{^anonymous|/|:}) {
+    $opt_u = shift @ARGV;
+  }
+  
+  if ($opt_u) {
+    $fexcgi = $1 if $opt_u =~ s:(.+)/::;
+    ($user,$id) = split(':',$opt_u);
+    if ($user =~ /^anonymous/) {
+      $anonymous = $user;
+    } elsif (not $id) {
+      die $usage;
+    }
+  }
+
+  unless ($fexcgi) {
+    die "$0: no SEX URL found, use \"$0 -u SEX-URL/recipient\" or \"fexsend -I\"\n";
+  }
+  
+  unless ($user) {
+    die "$0: no recipient found, use \"$0 -u SEX-URL/recipient\" or \"fexsend -I\"\n";
+  }
+  
+} else { # sexsend
+  
+  $opt_g = 1;
+  getopts('hguvqVTt:') or die $usage;
+  die $usage if $opt_h;
+
+  if ($opt_V) {
+    print "Version: $version\n";
+    exit unless @ARGV;
+  }
+  
+  if ($opt_t and $opt_t =~ /^\d+$/) {
+    $timeout = "&timeout=$opt_t";
+  }
+
+  my $save_user = $user;
+  $user = shift or die $usage;
+  $fexcgi = $1 if $user =~ s:(.+)/::;
+  
+  if ($user =~ /^anonymous/) {
+    die "$0: need SEX-URL with anonymous SEX\n" unless $fexcgi;
+    $mode = 'anonymous';
+  } elsif ($user eq 'public') {
+    unless ($id) {
+      die "$0: public SEX not possible without FEXID, set it with \"fexsend -I\"\n";
+    }
+    $mode = $user;
+    $user = $save_user;
+  } elsif ($user eq '.') {
+    open $idf,$idf or die "$0: no $idf\n";
+    $_ = <$idf>;
+    $user = <$idf>||'';
+    chomp $user;
+  } else {
+    unless ($fexcgi) {
+      die "$0: no SEX URL found, use \"$0 SEX-URL/recipient\" or \"fexsend -I\"\n";
+    }
+  }
+  
+}
+
+&get_ssl_env;
+
+$fexcgi =~ s(^http://)()i;
+$fexcgi =~ s(/fup.*)();
+$server = $fexcgi;
+
+if    ($server =~ s(^https://)()i) { $port = 443 } 
+elsif ($server =~ /:(\d+)/)        { $port = $1 } 
+else                               { $port = 80 }    
+
+$server =~ s([:/].*)();
+
+## set up tcp/ip connection
+# $iaddr = gethostbyname($server) 
+#          or die "$0: cannot find ip-address for $server $!\n";
+# socket(SH,PF_INET,SOCK_STREAM,getprotobyname('tcp')) or die "$0: socket $!\n";
+# connect(SH,sockaddr_in($port,$iaddr)) or die "$0: connect $!\n";
+# warn "connecting $server:$port user=$user\n";
+if ($port == 443) {
+  if ($opt_v and %SSL) {
+    foreach my $v (keys %SSL) {
+      printf "%s => %s\n",$v,$SSL{$v};
+    }
+  }
+  eval "use IO::Socket::SSL";
+  die "$0: cannot load IO::Socket::SSL\n" if $@;
+  $SH = IO::Socket::SSL->new(                                                  
+    PeerAddr => $server,                                                       
+    PeerPort => $port,                                                         
+    Proto    => 'tcp',
+    %SSL
+  );                                                                           
+} else {                                                                       
+  $SH = IO::Socket::INET->new(
+    PeerAddr => $server,
+    PeerPort => $port,
+    Proto    => 'tcp',                                                         
+  );                                                                           
+}
+
+die "cannot connect $server:$port - $!\n" unless $SH;                          
+warn "TCPCONNECT to $server:$port\n" if $opt_v;
+
+# autoflush $SH 1;
+autoflush STDERR;
+
+$SIG{PIPE} = \&sigpipehandler;
+
+if ($0 eq 'sexget' or $0 eq 'fuckme') {
+  $stream = "&stream=" . shift if @ARGV;
+  if ($anonymous) {
+    $cid = 'anonymous';
+  } elsif ($id eq 'public') {
+    $cid = 'public';
+  } else {
+    $cid = query_sid($server,$port,$id);
+  }
+  request("GET /sex?BS=$bs&user=$user&ID=$cid$stream HTTP/1.0");
+  transfer($SH,STDOUT);
+  # print while sysread $SH,$_,$bs;
+  exit;
+}
+
+if ($0 eq 'sexxx') {
+  $stream = "&stream=" . ($opt_s || 'xx');
+  if (@ARGV) {
+    warn "streaming:\n";
+    open my $tar,'-|','tar',"cv${opt_c}f",'-',@ARGV or die "$0: cannot run tar - $!\n";
+    request("POST /sex?BS=$bs&user=$user$type$stream HTTP/1.0");
+    transfer($tar,$SH);
+    # while (read $tar,$_,$bs) { syswrite $SH,$_ }
+  } else {
+    $cid = query_sid($server,$port,$id);
+    request("GET /sex?BS=$bs&user=$user&ID=$cid$stream HTTP/1.0");
+    $opt_c = 'z' if $H{'CONTENT-TYPE'} =~ /gzip/i;
+    if (-t STDOUT) {
+      print "extracting from stream:\n";
+      open $out,"|tar xv${opt_c}f -" or die "$0: cannot run tar - $!\n";
+    } else {
+      if ($opt_c) {
+        open $out,"|gzip -d" or die "$0: cannot run gunzip - $!\n";
+      } else {
+        $out = *STDOUT;
+      }
+    }
+    print {$out} $_ while sysread $SH,$_,$bs;
+  }
+  exit;
+}
+
+# sexsend
+$stream = "&stream=" . shift if @ARGV;
+
+if ($mode eq 'anonymous') {
+  unless ($opt_q) {
+    print "http://$server:$port/sex?user=$user&ID=anonymous$stream\n";
+    printf "http://$server:$port/sex?%s\n",
+           encode_b64("user=$user&ID=anonymous$stream");
+  }
+  $mode = "&mode=anonymous";
+} elsif ($mode eq 'public') {
+  die "$0: need user/ID when sending to public, set it with fexsend -I\n" unless $user and $id;
+  unless ($opt_q) {
+    print "http://$server:$port/sex?user=$user&ID=public$stream\n";
+    printf "http://$server:$port/sex?%s\n",
+           encode_b64("user=$user&ID=public$stream");
+  }
+  $cid = query_sid($server,$port,$id);
+  $mode = "&ID=$cid&mode=public";
+} else {
+  # $user = checkalias($user) unless $opt_d;
+}
+
+request("POST /sex?BS=$bs&user=$user$mode$type$timeout$stream HTTP/1.0");
+print STDERR "==> (streaming ...)\n" if $opt_v;
+
+transfer(STDIN,$SH);
+  
+exit;
+
+
+sub transfer {
+  my $source = shift;
+  my $destination = shift;
+  my ($t0,$t1,$tt);
+  my ($B,$b,$bt);
+  
+  $t0 = $t2 = time;
+  $tt = $t0-1;
+  $t1 = 0;
+
+  while ($b = sysread $source,$_,$bs) {
+    print {$destination} $_ or die "$0: link failure - $!\n";
+    $B += $b;
+    $bt += $b;
+    $t2 = time;
+    if ($t2>$t1) {
+      if ($opt_g) {
+        if ($B>2*M) {
+          printf STDERR "%d MB %d kB/s        \r",
+            int($B/M),int($bt/k/($t2-$tt));
+        } else {
+          printf STDERR "%d kB %d kB/s        \r",
+            int($B/k),int($bt/k/($t2-$tt));
+        }
+      }
+      $t1 = $t2;
+      if ($t2-$tt>10) {
+        sleep 1; # be nice to bandwith
+        $bt = 0;
+        $tt = $t2;
+      }
+    }
+  }
+
+  die "$0: no stream data\n" unless $B;
+  
+  $tt = (time-$t0)||1;
+  
+  if ($opt_v or $opt_g) {
+    if ($B>2097152) {
+      printf STDERR "transfered: %d MB in %d s with %d kB/s\n",
+        int($B/1048576),$tt,int($B/1024/$tt);
+    } elsif($B>2048) {
+      printf STDERR "transfered: %d kB in %d s with %d kB/s\n",
+        int($B/1024),$tt,int($B/1024/$tt);
+    } else {
+      printf STDERR "transfered: %d B in %d s with %d kB/s\n",
+        $B,$tt,int($B/1024/$tt);
+    }
+  }
+  
+}
+
+
+sub request {
+  my $req = shift;
+  
+  print STDERR "==> $req\n" if $opt_v;
+  syswrite $SH,"$req\r\n\r\n";
+  for (;;) {
+    unless (defined($_ = &getline)) {
+      die "$0: server has closed the connection\n";
+    }
+    if (/^HTTP\/[\d\.]+ 200/) {
+      print STDERR "<== $_" if $opt_v;
+      last;
+    } elsif (/^HTTP\/[\d\.]+ 199/) {
+      print STDERR "<== $_" if $opt_v;
+    } else {
+      if ($opt_v) {
+        print STDERR "<== $_";
+        exit 3;
+      } else {
+        s:^HTTP/[ \d\.]+::;
+        s/\r//;
+        die "$0: server response: $_";
+      }
+    }
+  }
+  while (defined($_ = &getline)) {
+    last if /^\s*$/;
+    $H{uc($1)} = $2 if /(.+):\s*(.+)/;
+    print STDERR "<== $_" if $opt_v;
+  }
+}
+
+# check for (mutt) alias
+sub checkalias {
+  my $to = shift;
+  if ($to !~ /@/ and open F,$ENV{HOME}.'/.mutt/aliases') {
+    while (<F>) {
+      next if /,/;
+      if (/^alias $to\s/i) {
+        chomp;
+        s/\s*#.*//;
+        s/\s+$//;
+        s/.*\s+//;
+        s/<//;
+        s/>//;
+        $to = $_;
+        warn "$0: found alias, using address $to\n";
+        die unless $to;
+        last;
+      }
+    }
+    close F;
+  }
+  return $to;
+}
+
+sub despace {
+  foreach (@_) {
+    s/^\s+//;
+    s/\s+$//;
+  }
+}
+
+sub query_sid {
+  my ($server,$port,$id) = @_;
+  my $req;
+  local $_;
+  
+  $req = "GET SID HTTP/1.1";
+  print STDERR "==> $req\n" if $opt_v;
+  syswrite $SH,"$req\r\n\r\n";
+  $_ = &getline;
+  unless (defined $_ and /\w/) { 
+    print STDERR "\n" if $opt_v;
+    die "$0: no response from server\n";
+  }
+  s/\r//;
+  if (/^HTTP.* 201 (.+)/) {
+    print STDERR "<== $_" if $opt_v;
+    $id = 'MD5H:'.md5_hex($id.$1);
+    while (defined($_ = &getline)) { 
+      s/\r//;
+      last if /^\n/;
+      print STDERR "<== $_" if $opt_v;
+    }
+  } else {
+    die "$0: $server does not support session ID\n";
+  }
+  return $id;
+}
+
+sub sigpipehandler { 
+  local $_ = '';
+  $SIG{ALRM} = sub { };
+  alarm(1);
+  $_ = &getline||'';
+  if (/^HTTP.* \d+ (.*)/) {
+    if ($opt_v) {
+      die "\n$0: server error: @_\n";
+    } else {
+      die "\n$0: server error: $1\n";
+    }
+  } else {
+    die "\n$0: got SIGPIPE (server closed connection)\n";
+  }
+}
+
+# read one text line from $SH;
+sub getline {
+  my $line = '';
+  my $c;
+
+  local $SIG{ALRM} = sub { die "$0: timeout while waiting for server reply\n" };
+  alarm($opt_t||300);
+  
+  # must use sysread to avoid perl line buffering
+  while (sysread $SH,$c,1) {
+    $line .= $c;
+    last if $c eq "\n";
+  }
+  
+  alarm(0);
+  
+  return $line;
+}
+
+# from MIME::Base64::Perl
+sub decode_b64 {
+  local $_ = shift;
+  my $uu = '';
+  my ($i,$l);
+  
+  tr|A-Za-z0-9+=/||cd;
+  s/=+$//;
+  tr|A-Za-z0-9+/| -_|;
+  return "" unless length;
+
+  $l = (length) - 60;
+  for ($i = 0; $i <= $l; $i += 60) {
+    $uu .= "M" . substr($_,$i,60);
+  }
+  $_ = substr($_,$i);
+  if (length) {
+    $uu .= chr(32 + (length)*3/4) . $_;
+  }
+  return unpack ("u",$uu);
+}
+
+
+### common functions ###
+
+
+sub mtime {
+  my @d = localtime((stat shift)[9]);
+  return sprintf('%d%02d%02d',$d[5]+1900,$d[4]+1,$d[3]);
+}
+
+
+sub urldecode {
+  local $_ = shift;
+  s/\%([a-f\d]{2})/chr(hex($1))/ige;
+  return $_;
+}
+
+
+sub get_ssl_env {
+  # set SSL/TLS options
+  $SSL{SSL_verify_mode} = $ENV{SSLVERIFY} if defined($ENV{SSLVERIFY});
+  foreach my $opt (qw(
+    SSL_version
+    SSL_cipher_list 
+    SSL_verify_mode 
+    SSL_ca_path 
+    SSL_ca_file)
+  ) {
+    my $env = uc($opt);
+    $env =~ s/_//g;
+    $SSL{$opt} = $ENV{$env} if defined($ENV{$env});
+  }
+
+  if ($SSL{SSL_verify_mode}) {
+    &search_ca;
+    unless ($SSL{SSL_ca_path} or $SSL{SSL_ca_file}) {
+      die "$0: \$SSLVERIFYMODE, but not valid \$SSLCAPATH or \$SSLCAFILE\n";
+    }
+  } elsif (defined($SSL{SSL_verify_mode})) {
+    # user has set SSLVERIFY=0 !
+  } else {
+    &search_ca;
+    $SSL{SSL_verify_mode} = 1 if $SSL{SSL_ca_path} or $SSL{SSL_ca_file};
+  }
+}
+
+sub search_ca {
+  local $_;
+  return if $SSL{SSL_ca_file} or $SSL{SSL_ca_path};
+  foreach (qw(/etc/ssl/certs/ca-certificates.crt)) {
+    if (-f) {
+      $SSL{SSL_ca_file} = $_;
+      return;
+    }
+  }
+  foreach (qw(/etc/ssl/certs /etc/pki/tls/certs)) {
+    if (-f) {
+      $SSL{SSL_ca_path} = $_;
+      return;
+    }
+  }
+}
+
+
+sub serverconnect {
+  my ($server,$port) = @_;
+  my $connect = "CONNECT $server:$port HTTP/1.1";
+  local $_;
+  
+  if ($opt_v and $port == 443 and %SSL) {
+    foreach my $v (keys %SSL) {
+      printf "%s => %s\n",$v,$SSL{$v};
+    }
+  }
+  
+  if ($proxy) {
+    tcpconnect(split(':',$proxy));
+    if ($port == 443) {
+      printf "--> %s\n",$connect if $opt_v;
+      nvtsend($connect,"");
+      $_ = <$SH>;
+      s/\r//;
+      printf "<-- $_"if $opt_v;
+      unless (/^HTTP.1.. 200/) {
+        die "$0: proxy error : $_";
+      }
+      eval "use IO::Socket::SSL";
+      die "$0: cannot load IO::Socket::SSL\n" if $@;
+      $SH = IO::Socket::SSL->start_SSL($SH,%SSL);
+    }
+  } else {
+    tcpconnect($server,$port);
+  }
+#  if ($port == 443 and $opt_v) {
+#    printf "%s\n",$SH->get_cipher();
+#  }
+}
+
+
+# set up tcp/ip connection
+sub tcpconnect {
+  my ($server,$port) = @_;
+  
+  if ($SH) {
+    close $SH;
+    undef $SH;
+  }
+  
+  if ($port == 443) {
+    # eval "use IO::Socket::SSL qw(debug3)";
+    eval "use IO::Socket::SSL";
+    die "$0: cannot load IO::Socket::SSL\n" if $@;
+    $SH = IO::Socket::SSL->new(
+      PeerAddr => $server,
+      PeerPort => $port,
+      Proto    => 'tcp',
+      %SSL
+    );
+  } else {
+    $SH = IO::Socket::INET->new(
+      PeerAddr => $server,
+      PeerPort => $port,
+      Proto    => 'tcp',
+    );
+  }
+  
+  if ($SH) {
+    autoflush $SH 1;
+  } else {
+    die "$0: cannot connect $server:$port - $@\n";
+  }
+  
+  print "TCPCONNECT to $server:$port\n" if $opt_v;
+}
+
+
+sub sendheader {
+  my $sp = shift;
+  my @head = @_;
+  my $head;
+  
+  push @head,"Host: $sp";
+  
+  foreach $head (@head) {
+    print "--> $head\n" if $opt_v;
+    print {$SH} $head,"\r\n";
+  }
+  print "-->\n" if $opt_v;
+  print {$SH} "\r\n";
+}
+
+
+sub nvtsend {
+  local $SIG{PIPE} = sub { $sigpipe = "@_" };
+  
+  $sigpipe = '';
+  
+  die "$0: internal error: no active network handle\n" unless $SH;
+  die "$0: remote host has closed the link\n" unless $SH->connected;
+  
+  foreach my $line (@_) {
+    print {$SH} $line,"\r\n";
+    if ($sigpipe) {
+      undef $SH;
+      return 0;
+    }
+  }
+  
+  return 1;
+}
+
+
+# from MIME::Base64::Perl
+sub encode_b64 {
+  my $res = "";
+  my $eol = "\n";
+  my $padding;
+  
+  pos($_[0]) = 0;
+  $res = join '',map(pack('u',$_)=~ /^.(\S*)/, ($_[0]=~/(.{1,45})/gs));
+  $res =~ tr|` -_|AA-Za-z0-9+/|;
+  $padding = (3-length($_[0])%3)%3;
+  $res =~ s/.{$padding}$/'=' x $padding/e if $padding;
+  return $res;
+}
diff --git a/bin/sexxx b/bin/sexxx
new file mode 120000 (symlink)
index 0000000..73cf1eb
--- /dev/null
+++ b/bin/sexxx
@@ -0,0 +1 @@
+sexsend
\ No newline at end of file
diff --git a/bin/xx b/bin/xx
new file mode 120000 (symlink)
index 0000000..e7f3ac4
--- /dev/null
+++ b/bin/xx
@@ -0,0 +1 @@
+fexsend
\ No newline at end of file
diff --git a/bin/zz b/bin/zz
new file mode 100755 (executable)
index 0000000..48a5a9f
--- /dev/null
+++ b/bin/zz
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+# to use zz with vim, write to your .vimrc:
+#
+# noremap <silent> zz> :w !zz<CR><CR>                                              
+# noremap <silent> zz< :r !zz<CR>
+
+ZZ=${ZZ:-$HOME/.zz}
+
+if [ "X$*" = X-h -o "X$*" = X--help ]; then
+  exec cat<<EOD
+zz is the generic clip board program. See also the edit helper program ezz.
+The clip board is \$ZZ (default: \$HOME/.zz). Options and modes are:
+
+"zz"            write \$ZZ to STDOUT
+"zz file(s)"    copy file(s) into \$ZZ
+"zz -"          write STDIN (keyboard, mouse buffer) to \$ZZ
+"zz +"          add STDIN (keyboard, mouse buffer) to \$ZZ
+"... | zz"      write STDIN from pipe to \$ZZ
+"... | zz +"    add STDIN from pipe to \$ZZ
+"zz | ..."      write \$ZZ to pipe
+"zz .."         write previous \$ZZ to STDOUT
+
+Examples:
+
+  zz *.txt
+  ls -l | zz
+  zz | wc -l
+  (within mutt:) |zz
+  (within tin:)  |azz
+  (within vi:)   :w !zz
+  (within vi:)   :r !zz
+  
+Limitation: zz does not work across different accounts or hosts! Use xx instead.
+EOD
+fi
+
+if [ x"$1"x = x+x ]; then 
+  shift
+  exec cat -- "$@" >>$ZZ
+fi
+
+if [ -t 0 ]; then
+    if [ x"$1"x = xx ]; then 
+        exec cat -- $ZZ
+    elif [ x"$1"x = x..x ]; then 
+        exec cat -- $ZZ~
+    else 
+        test -f $ZZ && mv $ZZ $ZZ~
+        exec cat -- "$@" >$ZZ
+    fi
+else
+    test -f $ZZ && mv $ZZ $ZZ~
+    exec cat >$ZZ
+fi     
diff --git a/cgi-bin/fac b/cgi-bin/fac
new file mode 100755 (executable)
index 0000000..1470b83
--- /dev/null
@@ -0,0 +1,902 @@
+#!/usr/bin/perl -w
+
+# F*EX CGI for administration
+#
+# Author: Andre Hafner <andrehafner@gmx.net>
+#
+
+use CGI                        qw(:standard);
+use CGI::Carp          qw(fatalsToBrowser);
+
+$| = 1;
+
+# add fex lib
+(our $FEXLIB) = $ENV{FEXLIB} =~ /(.+)/;
+die "no \$FEXLIB\n" unless -d $FEXLIB;
+
+# import from fex.pp and fex.ph
+our ($FEXHOME,$spooldir,$logdir,$docdir,$durl,$mdomain);
+our ($bs,$hostname,$keep_default,$recipient_quota,$sender_quota,$autodelete);
+our ($admin,$admin_pw,$admin_hosts);
+our ($sendmail,$bcc);
+our $error = 'FAC error';
+
+# load common code, local config : $HOME/lib/fex.ph
+require "$FEXLIB/fex.pp" or http_die("cannot load $FEXLIB/fex.pp - $!\n");
+
+my @http_auth = ();
+my $ra = $ENV{REMOTE_ADDR}||0;
+
+if (not @admin_hosts or not ipin($ra,@admin_hosts)) {
+  html_error($error,"Administration from your host ($ra) is not allowed.");
+}
+
+html_error($error,"\$admin not configured in $FEXLIB/fex.ph\n") unless $admin;
+
+chdir $spooldir or http_die("$spooldir - $!");
+chomp($admin_pw = slurp("$admin/@")||'');
+html_error($error,"no F*EX account for admin $admin\n") unless $admin_pw;
+
+# redirect to https if configured
+if (0 and open my $x,'/etc/xinetd.d/fexs') {
+  while (<$x>) {
+    if (/^\s*disable\s*=\s*no/) {
+      nvt_print(
+        "HTTP/1.1 301 Moved Permanently",
+        "Location: https://$hostname$ENV{REQUEST_URI}",
+        'Content-Length: 0',
+        ''
+      );
+      exit;
+    }
+  }
+  close $x;
+}
+
+# authentication
+&require_akey;
+
+my $fup = $durl;
+$fup =~ s:/fop:/fup:;
+
+my $http_client = $ENV{HTTP_USER_AGENT} || '';
+
+# here is chosen which files to save with backup function
+my @backup_files = qw(
+  htdocs/index.html
+  lib/fex.ph
+  lib/fup.pl
+  spool/*@*/@*
+);
+
+# backup goes first
+if (defined param("action") and param("action") eq "backup") { &backup }
+
+http_header('200 OK');
+
+$_ = html_header("F*EX Admin Control for $hostname");
+s:</h1>: (<a href="?action=logout">logout</a>)</h1>:;
+print;
+
+my $nav_user = 
+  li("<a href=\"?action=create\">Create new user</a>") . "\n" .
+  li("<a href=\"?action=change-auth\">Change user auth-ID</a>") . "\n" .
+  li("<a href=\"?action=edit\">Edit user restrictions file</a>") . "\n" .
+  li("<a href=\"?action=delete\">Delete existing user</a>") . "\n" .
+  li("<a href=\"?action=quota\">Manage disk quota</a>") . "\n";
+
+my $nav_log = 
+  li("<a href=\"?action=fup.log\">Get fup.log</a>") . "\n" .
+  li("<a href=\"?action=fop.log\">Get fop.log</a>") . "\n" .
+  li("<a href=\"?action=error.log\">Get error.log</a>") . "\n";
+
+if (-f 'fexsrv.log') {
+  $nav_log =
+    li("<a href=\"?action=watch\">Watch logfile</a>") . "\n" .
+    li("<a href=\"?action=fexsrv.log\">Get fexsrv.log</a>") . "\n" .
+  $nav_log;
+}
+
+my $nav_backup = 
+  li("<a href=\"?action=backup\">Download backup<br>(config only)</a>") . "\n" .
+  li("<a href=\"?action=restore\">Restore backup</a>") . "\n";
+
+my $nav_show =
+  li("<a href=\"?action=list\">List spooled files</a>") . "\n" .
+  li("<a href=\"?action=showquota\">Show quotas (sender/recipient)</a>") . "\n" .
+  li("<a href=\"?action=showconfig\">Show server config</a>") . "\n" .
+  li("<a href=\"?action=userconfig\">Show user config</a>") . "\n";
+  
+my $nav_edit =  
+  li("<a href=\"?action=editconfig\">Edit config</a>") . "\n" .
+  li("<a href=\"?action=editindex\">Edit index.html</a>") . "\n";
+
+#print table({-border=>"0"},Tr({-valign=>"top"},[td([ul($nav_user), ul($nav_log), ul($nav_backup), ul($nav_other)])])), "\n";
+#print "\n", hr, "\n" ;
+print table({-border=>"0"},
+       th({},["manage user","show","log files","edit","backup"]),
+       Tr({-valign=>"top"},[td([
+               ul($nav_user),
+               ul($nav_show),
+               ul($nav_log),
+               ul($nav_edit),
+               ul($nav_backup)
+])])), "\n";
+print "<hr>\n";
+
+my @user_items = &userList;
+if (my $action = param("action")) {
+    if    ($action eq "create")      { &createUserForm } 
+    elsif ($action eq "change-auth") { &changeAuthForm } 
+    elsif ($action eq "edit")        { &editRestrictionsForm } 
+    elsif ($action eq "delete")      { &deleteUserForm } 
+    elsif ($action eq "quota")       { &changeQuotaForm } 
+    elsif ($action eq "list")        { &listFiles }
+    elsif ($action eq "showquota")   { &showQuota } 
+    elsif ($action eq "showconfig")  { &showConfig } 
+    elsif ($action eq "userconfig")  { &userConfigForm } 
+    elsif ($action eq "watch")       { &watchLog } 
+    elsif ($action eq "fexsrv.log")  { &getlog("fexsrv.log") } 
+    elsif ($action eq "fup.log")     { &getlog("fup.log") }
+    elsif ($action eq "fop.log")     { &getlog("fop.log") } 
+    elsif ($action eq "error.log")   { &getlog("error.log") } 
+    elsif ($action eq "editconfig")  { &editFile("$FEXLIB/fex.ph") } 
+    elsif ($action eq "editindex")   { &editFile("$docdir/index.html") } 
+    elsif ($action eq "backup")      { &backup } 
+    elsif ($action eq "restore")     { &restoreForm } 
+    else                             { http_die("STOP TRYING TO CHEAT ME!\n") }
+}
+
+if (defined param("createUser")) {
+    createUser(param("createUser"), param("authID"));
+
+} elsif (defined param("changeAuthUser")) {
+    if (param("changeAuthUser") =~ /^#.*/) {
+       &changeAuthForm;
+    } else {
+       changeUser(param("changeAuthUser"), param("authID"));
+    }
+
+} elsif (defined param("showUserConfig")) {
+    if (param("showUserConfig") =~ /^#.*/) {
+       &userConfigForm;
+    } else {
+       showUserConfig(param("showUserConfig"));
+    }
+
+} elsif (defined param("deleteUser")) {
+    if (param("deleteUser") =~ /^#.*/) {
+       &deleteUserForm;
+    } else {
+       deleteUser(param("deleteUser"));
+    }
+
+} elsif (defined param("userQuota")) {
+    if (param("userQuota") =~ /^#.*/) {
+       &changeQuotaForm;
+    } else {
+       if (defined param("remove quota")) {
+           $user = param("userQuota");
+           deleteFiles("$spooldir/$user/\@QUOTA");
+       } else {
+           alterQuota(param("userQuota"), param("recipientQuota"), param("senderQuota"));
+       }
+    }
+
+} elsif (defined param("editUser")) {
+    if (param("editUser") =~ /^#.*/) {
+       &editRestrictionsForm;
+    } else {
+       if (defined param("delete file")) {
+           $user = param("editUser");
+           deleteFiles("$spooldir/$user/\@ALLOWED_RECIPIENTS");
+       } else {
+           editUser(param("editUser"));
+       }
+    }
+
+} elsif (defined param("contentBox") && defined param("ar")) {
+    saveFile(param("contentBox"), param("ar"));
+
+} elsif (defined param("upload_archive")) {
+    restore(param("upload_archive"));
+}
+
+print end_html();
+exit;
+
+
+#######
+# declaration of formular functions
+#######
+
+# formular for creating new users
+# required arguments: -
+sub createUserForm {
+    my $nameRow = "\n" . td(["user:", textfield(-size=>80, -name=>"createUser")]);
+    my $authRow = "\n" . td(["auth-ID:", textfield(-size=>80, -name=>"authID")]);
+    print "\n", h3("Create new user");
+    print "\n", start_form(-name=>"create", -method=>"POST");
+    print "\n", table(Tr([$nameRow, $authRow]));
+    print "\n", submit('create user'), br;
+    print "\n", end_form;
+}
+
+# formular for changing auth-id of an user
+# required arguments: -
+sub changeAuthForm {
+    my $nameRow = "\n" . td(["user:", popup_menu(-name=>"changeAuthUser", -values=>\@user_items)]);
+    my $authRow = "\n" . td(["new auth-ID:", textfield(-size=>80, -name=>"authID")]);
+    print "\n", h3("change auth-ID");
+    print "\n", start_form(-name=>"change-auth", -method=>"POST");
+    print "\n", table(Tr([$nameRow, $authRow]));
+    print "\n", submit('change'), br;
+    print "\n", end_form;
+}
+
+# formular choosing user, whose config files shall be shown
+# required arguments: -
+sub userConfigForm {
+    my $nameRow = "\n". td(["user:", popup_menu(-name=>"showUserConfig", -values=>\@user_items)]);
+    print "\n", h3("Show user config files");
+    print "\n", start_form(-name=>"showUserConfig", -method=>"POST");
+    print "\n", table(Tr([$nameRow]));
+    print "\n", submit('show config files'), br;
+    print "\n", end_form;
+}
+
+# formular for choosing user, whose restriction file shall be edited
+# required arguments: -
+sub editRestrictionsForm {
+    my $nameRow = "\n" . td(["user:", popup_menu(-name=>"editUser", -values=>\@user_items)]);
+    print "\n", h3("Edit user restriction file");
+    print "\n", start_form(-name=>"edit", -method=>"POST");
+    print "\n", table(Tr([$nameRow]));
+    print "\n", submit('edit file');
+    print "\n", submit('delete file'), br;
+    print "\n", end_form;
+}
+
+# formular for choosing user, who shall be removed
+# required arguments: - 
+sub deleteUserForm {
+    my $nameRow = "\n". td(["user:", popup_menu(-name=>"deleteUser", -values=>\@user_items)]);
+    print "\n", h3("Delete existing user");
+    print "\n", start_form(-name=>"deleteUser", -method=>"POST");
+    print "\n", table(Tr([$nameRow]));
+    print "\n", submit('delete user'), br;
+
+    print "\n", end_form;
+}
+
+# formular for changing an user's quota file
+# required arguments: -
+sub changeQuotaForm {
+    my ($rquota,$squota) = '';
+    $rquota = param("rquota") if defined param("rquota");
+    $squota = param("squota") if defined param("squota");
+    my $dropdownMenu;
+    if (defined param("user")) {
+       $dropdownMenu = "<select name=\"userQuota\">\n";
+       foreach (@user_items) {
+           if ($_ eq param("user")) {
+               $dropdownMenu .= "<option value=\"$_\" selected>$_</option>";
+           } else {
+               $dropdownMenu .= "<option value=\"$_\">$_</option>";
+           }
+       }
+       $dropdownMenu .= "</select>\n";
+    } else {
+       $dropdownMenu = popup_menu(-name=>"userQuota", -values=>\@user_items);
+    }
+    my $nameRow = "\n" . td(["user:", $dropdownMenu]);
+    my $recipientRow = "\n" . td(["new quota for recipient:", textfield(-size=>20, -name=>"recipientQuota", -value=>$rquota). " MB (optional)"]);
+    my $senderRow = "\n" . td (["new quota for sender:", textfield(-size=>20, -name=>"senderQuota", -value=>$squota). " MB (optional)"]);
+    print "\n", h3("Manage disk quota");
+    print "\n", start_form(-name=>"manageQuota", -method=>"POST");
+    print "\n", table(Tr([$nameRow, $recipientRow, $senderRow]));
+    print "\n", submit('change quota');
+    print "\n", submit('remove quota'), br;
+    print "\n", end_form;
+}
+
+# formular for choosing backup file to restore
+# required arguments: -
+sub restoreForm {
+    print h2("restore config");
+    print "please specify the backup-archive you want to restore:";
+    print "\n", start_form(-name=>"restoreFile", -method=>"POST");
+    print "\n", filefield(-name=>"upload_archive", -size=>"80"), br;
+    print "\n", submit('restore');
+    print "\n", end_form;
+}
+
+
+#######
+# declaration user functions
+#######
+
+# function for creating new users
+# required arguments: username, auth-id
+sub createUser {
+    my ($user,$id) = @_;
+    my $idf;
+  
+    $id or http_die("not enough arguments in createUser");
+  
+    $user = lc $user;
+    $user =~ s:/::g;
+    $user =~ s:^[.@]+::;
+    $user =~ s:@+$::;
+
+    if ($user !~ /@/) {
+        if ($mdomain) {
+            $user .= '@'.$mdomain;
+        } else {
+            error("Missing domain part in user address");
+        }
+    }
+  
+    unless (-d "$spooldir/$user") {
+        mkdir "$spooldir/$user",0755 
+          or http_die("cannot mkdir $spooldir/$user - $!\n");
+    }
+  
+    $idf = "$spooldir/$user/@";
+
+    if (-f $idf) {
+       error("There is already an user $user!");       
+    }
+  
+    open $idf,'>',$idf or http_die("cannot write $idf - $!\n");
+    print {$idf} $id,"\n";
+    close $idf or http_die("cannot write $idf - $!\n");
+    print "<code>\n";
+    printf "%s?from=%s&ID=%s<br>\n",$fup,$user,$id;
+    printf "%s/%s<p>\n",$fup,b64("from=$user&id=$id");
+    print "</code>\n";
+    notifyUser($user,$id);
+    print "An information e-mail to $user has been sent.\n";
+}
+
+# function for changing an user's auth-ID
+# required arguments: username, auth-id
+sub changeUser {
+    my ($user,$id) = @_;
+    defined($id) or http_die("not enough arguments in changeUser.\n");
+
+    $user .= '@'.$mdomain if $mdomain and $user !~ /@/;
+    my $idf = "$spooldir/$user/@";
+    print "<code>\n";
+    print "$idf<p>";
+
+    open $idf,'>',$idf or http_die("cannot write $idf - $!\n");
+    print {$idf} $id,"\n";
+    close $idf or http_die("cannot write $idf - $!\n");
+    printf "%s?from=%s&ID=%s<br>\n",$fup,$user,$id;
+    printf "%s/%s\n",$fup,b64("from=$user&id=$id");
+    print "</code><p>\n";
+    notifyUser($user,$id,"change-auth");
+    print "An information e-mail to $user has been sent.\n";
+}
+
+# function for showing an user's config files
+# required arguments: username
+sub showUserConfig {
+    http_die("not enough arguments in showUserConfig!\n") unless (my $user = $_[0]);
+   
+    chdir "$spooldir/$user" or http_die("could not change directory $spooldir/$user - $!");
+    print h2("Config files of <code>$user</code>");
+
+    foreach my $file (glob('.auto @* @GROUP/*')) {
+       if (-f $file and not -l $file and $file !~ /.*~$/) {
+           print h3($file), "\n";
+           open $file,'<',$file or http_die("cannot open $file - $!");
+            # print "<table border=1><tr><td>\n";
+            dumpfile($file);
+            # print "</tr></table>\n";
+           close $file;
+       }
+    }
+}
+
+# function for editing an user's recipient/sender restrictions
+# required arguments: username
+sub editUser {
+    http_die("not enough arguments in editUser.\n") unless (my $user = $_[0]);
+    my @content;
+    http_die("no user $user") unless -d "$spooldir/$user";
+    my $ar = "$spooldir/$user/\@ALLOWED_RECIPIENTS";
+    unless (-f $ar) {
+       print "yeah!";
+        open F,">$ar" or http_die("cannot open $ar - $!");
+        print F<<EOD;
+# Restrict allowed recipients. Only those listed here are allowed.
+# Make this file COMPLETLY empty if you want to disable the restriction.
+# An allowed recipient is an e-mail address, you can use * as wildcard.
+# Example: *\@flupp.org
+EOD
+        close F;
+    }
+    open my $file,'<',$ar or http_die("cannot open $ar - $!");
+    while (<$file>) {
+        push @content, $_;
+    }
+    close $file or http_die("cannot write $file - $!\n");
+    print "\nedit file:", br;
+    print "\n", start_form(-name=>"editRestrictions", -method=>"POST");
+    print "\n", textarea(-name=>'contentBox', -default=>join('',@content), -rows=>10, -columns=>80), br;
+    print "\n", hidden(-name=>'ar', -default=>"$ar",);
+    print "\n", submit('save changes');
+    print "\n", end_form;
+}
+
+# function for deleting files
+# required arguments: list of Files
+sub deleteFiles {
+    http_die("not enough arguments in deleteFiles.\n") unless (my @files = @_);
+    
+    foreach (@files) {
+       if (-e $_) {
+           if (unlink $_) {
+               print "file has been deleted: $_\n", br;
+           } else {
+               print "file could not be deleted: $_ - $!\n", br;
+           }
+       } else {
+           print "file does not exists: $_\n", br;
+       }
+    }
+}
+
+# function for saving a single file
+# required arguments: content, location
+sub saveFile {
+    http_die("not enough arguments in saveFile.\n") unless (my ($rf,$ar) = @_);
+
+    if ($ar eq "$FEXLIB/fex.ph") {
+        open my $conf,">${ar}_new" or http_die("cannot open ${ar}_new - $!");
+        print {$conf} $rf;
+        close $conf or http_die("cannot write $conf - $!\n");;
+        my $status = `perl -c $FEXLIB/fex.ph_new 2>&1`;
+        if ($status =~ /syntax OK/ ) {
+             unlink "${ar}_new";
+        } else {
+            pq(qq(
+              'No valid syntax in configuration file:'
+              '<p>'
+              '<pre>$status</pre>'
+            ));
+            &editFile("$FEXLIB/fex.ph_new");
+            exit;
+        }
+    }
+    open my $file,">$ar" or http_die("cannot open $ar - $!");
+    print {$file} $rf;
+    close $file or http_die("cannot write $file - $!\n");;
+    print "The following data has been saved:\n<p>\n";
+    open $file,'<',$ar or http_die("cannot open $ar - $!");
+    if ($ar =~ /\.html$/) {
+       print while <$file>;
+    } else {
+        print "<pre>\n";
+       print while <$file>;
+    }
+    close $file or http_die("cannot write $file - $!\n");;
+}
+
+# function for deleting existing user
+# required arguments: username
+sub deleteUser {
+    http_die("not enough arguments in createUser.\n") unless (my $user = $_[0]);
+
+    $idf = "$spooldir/$user/\@";
+    http_die("no such user $user\n") unless -f $idf;
+    unlink $idf or http_die("cannot remove $idf - $!\n");
+    unlink "$spooldir/$user/\@ALLOWED_RECIPIENTS";
+    print "$user deleted\n";
+}
+
+# function for saving quota information for one single user
+# required arguments: username, recipient-quota, sender-quota
+sub alterQuota {
+    http_die("not enough arguments in createUser.\n") unless (my ($user,$rq,$sq) = @_);
+
+    $user .= '@'.$mdomain if $mdomain and $user !~ /@/;
+    unless (-d "$spooldir/$user") {
+        http_die("$user is not a regular F*EX user\n");
+    }
+
+    $rquota = $squota = '';
+    $qf = "$spooldir/$user/\@QUOTA";
+    if (open $qf,'<',$qf) {
+        while (<$qf>) {
+            s/#.*//;
+            $rquota = $1 if /recipient.*?(\d+)/i;
+            $squota = $1 if /sender.*?(\d+)/i;
+        }
+        close $qf or http_die("cannot write $qf - $!\n");
+    }
+
+    open $qf,'>',$qf or http_die("cannot open $qf - $!\n");
+    if(defined($rq) && $rq ne "") {
+        $rquota = $1 if $rq =~ /(\d+)/i;
+    }
+    if(defined($sq) && $sq ne "") {
+        $squota = $1 if $sq =~ /(\d+)/i;
+    }    
+    print {$qf} "recipient:$rquota\n" if $rquota =~ /\d/;
+    print {$qf} "sender:$squota\n" if $squota =~ /\d/;
+    close $qf or http_die("cannot write $qf - $!\n");
+
+    $rquota = $recipient_quota if $rquota !~ /\d/;
+    $squota = $sender_quota    if $squota !~ /\d/;
+    print h3("New quotas for $user");
+    print "recipient quota: $rquota MB\n", br;
+    print "sender quota:   $squota MB\n", br;
+}
+
+# function for listing f*exed files
+# required arguments: -
+sub listFiles {
+    print h3("List current files"),"\n";
+    my ($file,$dkey);
+    chdir $spooldir or http_die("$spooldir - $!\n");
+    print "<code>\n";
+    foreach $file (glob "*/*/*") {
+        if (-s "$file/data" and  $dkey = readlink("$file/dkey") and -l ".dkeys/$dkey") {
+          ($to,$from,$file) = split "/",$file;
+          $file = html_quote($file);
+          print "$from --> $to : $durl/$dkey/$file<br>\n";
+        }
+    }
+    print "</code>\n";
+}
+
+# function for watching the fex-logfile
+# required arguments: -
+sub watchLog {
+  if (-f 'fexsrv.log') {
+    print h2("polling fexsrv.log"),"\n";
+    open my $log,"$FEXHOME/bin/logwatch|" 
+      or http_die("cannot run $FEXHOME/bin/logwatch - $!\n");
+    dumpfile($log);
+  } else {
+    print h2("no fexsrv.log"),"\n";
+  }
+}
+
+# function for showing logfiles
+# required arguments: logfile-name
+sub getlog {
+    my $log = shift or http_die("not enough arguments in getLog");
+
+    print h2("show $log"),"\n";
+    if (open $log,"$logdir/$log") {
+        dumpfile($log);
+        close $log;
+    } else {
+        http_die("cannot open $logdir/$log - $!\n");
+    }
+}
+
+# function for creating a new backup file
+# required arguments: -
+sub backup {
+    my @d = localtime time;
+    my $date = sprintf('%d%02d%02d',$d[5]+1900,$d[4]+1,$d[3]);
+    my $backup = "backup/config-$date.tar";
+    my $http_client = $ENV{HTTP_USER_AGENT} || '';
+    my $size;
+
+    my $home = $FEXHOME;
+    $home = $1 if $ENV{VHOST} and $ENV{VHOST} =~ /:(.+)/;
+  
+    chdir $home or http_die("$home - $!\n");
+
+    unless (-d "backup") {
+       mkdir "backup",0700 or http_die("cannot mkdir backup - $!\n");
+    }
+
+    system "tar -cf $backup @backup_files 2>/dev/null";
+
+    $size = -s $backup or http_die("backup file empty\n");
+
+    open $backup,'<',$backup or http_die("cannot open $backup - $!\n");
+
+    nvt_print(
+        'HTTP/1.1 200 OK',
+        "Content-Length: $size",
+        "Content-Type: application/octet-stream; filename=fex-backup-$date.tar",
+        "Content-Disposition: attachment; filename=\"fex-backup-$date.tar\"",
+       "",
+       );
+    while (read($backup,my $b,$bs)) {
+       print $b or last;
+    }
+
+    exit;
+}
+
+# function for restoring an old configuration file
+# required arguments: uploaded archive
+sub restore {
+    http_die("not enough arguments in restore!\n") unless (my $archive_file = $_[0]);
+    my $restore = "backup.tar";
+
+    my $home = $FEXHOME;
+    $home = $1 if $ENV{VHOST} and $ENV{VHOST} =~ /:(.+)/;
+
+    chdir $home or http_die("$home - $!\n");
+
+    open $restore,'>',$restore or http_die("cannot open $restore - $!");
+
+    my $data;
+    while(read $archive_file,$data,$bs) {
+       print {$restore} $data;
+    }
+    close $restore or http_die("cannot write $restore - $!");
+    if (-s $restore) {
+       print "file upload successful, saving actual config in $home/backup/failsave.tar\n", br;
+       system "tar -cf $home/backup/failsave.tar @backup_files 2>/dev/null";
+       print "starting restore:\n<p><pre>\n";
+        system "tar -xvf $restore";
+        unlink $restore;
+    } else {
+       http_die("upload error - no file data received\n");
+    }
+}
+
+# function for editing a text-file
+# required arguments: filepath, filename
+sub editFile {
+    my $ar = shift;
+    my $file;
+    local $/;
+  
+    open $ar,'<',$ar or http_die("cannot open $ar - $!");
+    $file = <$ar>;
+    close $ar;
+
+    print start_form(-name=>"editFile", -method=>"POST"),"\n";
+    print textarea(-name=>'contentBox', -default=>$file, -rows=>26, -columns=>80), br,"\n";
+    print hidden(-name=>'ar', -default=>"$ar"),"\n";
+    print submit('save changes'),"\n";
+    print end_form(),"\n";
+}
+
+# function for showing all users' quotas
+# required arguments: -
+sub showQuota {
+    my @table_content;
+    my $table_head;
+
+    print h2("Show quotas (domain sorted, values in MB)");
+    foreach (@user_items) {
+       if (s/###\s*//g) {
+           $table_head = th({}, ["\@$_","sender","sender (used)","recipient","recipient (used)"]);
+           if (@table_content) {
+               print table({-border=>1},Tr([@table_content])), "\n<p>\n";
+               @table_content = '';
+           }
+           push @table_content, $table_head;
+       } else {
+           my $rquota = $recipient_quota;
+           my $squota = $sender_quota;
+           my $rquota_used = 0;
+           my $squota_used = 0;
+           my $user = $_;
+           ($squota,$squota_used) = check_sender_quota($user);
+           ($rquota,$rquota_used) = check_recipient_quota($user);
+           s/\@.*//;
+           push @table_content, 
+              "<td><a href=\"?action=quota&user=$user&rquota=$rquota&squota=$squota\">$_</a></td>".
+              "<td align=\"right\">$squota</td>".
+              "<td align=\"right\">$squota_used</td>".
+              "<td align=\"right\">$rquota</td>".
+              "<td align=\"right\">$rquota_used</td>";
+       }
+    }
+    print table({-border=>1},Tr([@table_content])), "\n";
+}
+
+# function for showing fex-server configuration
+# required arguments: -
+sub showConfig {
+    print h3("Show config");
+    print table({},Tr([
+                   td(["spooldir:",        $spooldir       ]),
+                   td(["logdir:",          $logdir         ]),
+                   td(["docdir:",          $docdir         ]),
+                   td(["durl:",            $durl           ]),
+                   td(["mdomain:",         $mdomain||''    ]),
+                   td(["autodelete:",      $autodelete     ]),
+                   td(["keep:",            $keep_default   ]),
+                   td(["recipient_quota:", $recipient_quota]),
+                   td(["sender_quota:",    $sender_quota   ]),
+                   td(["admin:",           $admin          ])
+    ]));
+}
+
+# require authentification
+sub require_akey {
+  my $id;
+  my $rid;
+  my $action;
+
+  $action = param("action");
+  if ($action and $action eq 'logout') {
+    nvt_print(
+      "HTTP/1.1 301 Moved Permanently",
+      "Location: /fac",
+      'Content-Length: 0',
+      "Set-Cookie: akey=; Max-Age=0; Discard",
+      ''
+    );
+    &reexec;
+  }
+
+  $rid = slurp("$admin/@") or html_error($error,"no F*EX account for $admin");
+  chomp $rid;
+
+  $id = param("id");  
+
+  if ($id) {
+    # correct auth-ID?
+    if ($id eq $rid) {
+      $akey = md5_hex("$admin:$rid");
+      return;
+    }
+  } elsif ($akey) {
+    # correct akey?
+    return if $akey eq md5_hex("$admin:$rid");
+  }  
+
+  http_header('200 OK');
+  print html_header("F*EX Admin Control for $hostname");
+
+  if ($akey) {
+    pq(qq(
+      '<font color="red"><h3>'
+      '  wrong akey for <code>$admin</code>'
+      '</h3></font>'
+    ));
+  }
+
+  if ($id and $id ne $rid) {
+    pq(qq(
+      '<font color="red"><h3>'
+      '  wrong auth-ID for <code>$admin</code>'
+      '</h3></font>'
+    ));
+  }
+
+  pq(qq(
+    '<form action="/fac" '
+    '      method="post" '
+    '      enctype="multipart/form-data">'
+    '  auth-ID for <code>$admin</code>:'
+    '  <input type="password" name="id" size="16" autocomplete="off">'
+    '</form>'
+  ));
+  exit;
+}
+
+
+# function for checking simple HTTP authentication
+# (not used any more, replaced with require_akey)
+sub require_auth {
+  if ($ENV{HTTP_AUTHORIZATION} and $ENV{HTTP_AUTHORIZATION} =~ /Basic\s+(.+)/) 
+  { @http_auth = split(':',decode_b64($1)) }
+  if (
+    @http_auth != 2 
+    or $http_auth[0] !~ /^(fexmaster|admin|\Q$admin\E)$/
+    or $http_auth[1] ne $admin_pw
+  ) {
+    http_header(
+      '401 Authorization Required',
+      "WWW-Authenticate: Basic realm=$admin F*EX admin authentification",
+      'Content-Length: 0',
+    );
+    # control back to fexsrv for further HTTP handling
+    &reexec;
+  }
+}
+
+# function for sending notification mails to an user
+# required arguments: username, auth-id, message-type
+sub notifyUser {
+    http_die("not enough arguments in createUser.\n") unless (my ($user,$id) = @_);
+    my $type = $_[2];
+    my $message = 'A F*EX account has been created for you. Use';
+
+    if (defined($type) and $type eq "change-auth") {
+       $message = 'New auth-ID for your F*EX account has been set. Use'
+    }
+
+    $user .= '@'.$mdomain if $mdomain and $user !~ /@/;
+    open my $mail,'|-',$sendmail,'-f',$admin,$user,$bcc
+       or http_die("cannot start sendmail - $!\n");
+    pq($mail,qq(
+        'From: $admin'
+        'To: $user'
+        'Subject: your F*EX account on $hostname'
+        'X-Mailer: F*EX'
+        ''
+        '$message'
+        ''
+        '$ENV{PROTO}://$ENV{HTTP_HOST}/fup?from=$user'
+        'auth-ID: $id'
+        ''
+        'See http://$ENV{HTTP_HOST}/index.html for more information about F*EX.'
+        ''
+        'Questions? ==> F*EX admin: $admin'
+    ));
+    close $mail
+       or http_die("cannot send notification e-mail (sendmail error $!)\n");
+}
+
+# sort key is the (inverse) domain
+# required arguments: list of usernames (e-mail addresses)
+sub domainsort {
+#    http_die("not enough arguments in domainsort.\n") unless (my @d = @_);
+    my @d = @_;
+    local $_;
+
+    foreach (@d) {
+       s/ //g;
+       s/^/ /;
+       s/\./,/ while /\..*@/;
+       s/@/@./;
+       $_ = join('.',reverse(split /\./));
+    }
+
+    @d = sort { lc $a cmp lc $b } @d;
+
+    foreach (@d) {
+       $_ = join('.',reverse(split /\./));
+       s/,/./g;
+       s/@\./@/;
+    }
+
+    return @d;
+}
+
+# function for creating a sorted list of all users
+# required arguments: -
+sub userList {
+    my @u;
+    my $d = '';
+
+    foreach (domainsort(grep { s:/@:: } glob('*@*/@'))) {
+       s/ //g;
+        /@(.+)/;
+        if ($1 ne $d) {
+            push @u,"### $1 ###";
+       }
+       push @u,$_;
+        $d = $1;
+    }
+    return @u;
+}
+
+
+sub dumpfile {
+  my $file = shift;
+  
+  print "<pre>\n";
+  while (<$file>) {
+    s/&/&amp;/g;
+    s/</&lt;/g;
+    print or exit;
+  }
+  print "\n</pre>\n";
+}
+
+
+sub error {
+    print join("\n",@_),"\n";
+    print end_html();
+    exit;
+}
diff --git a/cgi-bin/foc b/cgi-bin/foc
new file mode 100755 (executable)
index 0000000..e5eea41
--- /dev/null
@@ -0,0 +1,243 @@
+#!/usr/bin/perl -wT
+
+# FEX CGI for (user) operation control
+#
+# Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
+#
+
+use CGI         qw(:standard);
+use CGI::Carp  qw(fatalsToBrowser);
+use Fcntl      qw(:flock);
+use Digest::MD5        qw(md5_hex);
+
+$CGI::LIST_CONTEXT_WARN = 0;
+$CGI::LIST_CONTEXT_WARN = 0;
+
+# add fex lib
+($FEXLIB) = $ENV{FEXLIB} =~ /(.+)/;
+die "$0: no $FEXLIB\n" unless -d $FEXLIB;
+
+our ($FEXHOME,$mdomain,$nomail,$faillog);
+our $akey = '';
+
+# load common code, local config : $HOME/lib/fex.ph
+require "$FEXLIB/fex.pp" or die "$0: cannot load $FEXLIB/fex.pp - $!\n";
+
+my $error = 'F*EX operation control ERROR';
+
+chdir $spooldir or die "$spooldir - $!\n";
+
+$akeydir = "$spooldir/.akeys"; 
+$user = $id = '';
+
+# look for CGI parameters
+foreach my $v (param) {
+  my $vv = param($v);
+  debuglog("Param: $v=\"$vv\"");
+  if ($v =~ /^akey$/i and $vv =~ /^(\w+)$/) { 
+    $akey = $1;
+  } elsif ($v =~ /^(from|user)$/i) {
+    $user = normalize_email($vv);
+    $user .= '@'.$mdomain if $mdomain and $user !~ /@/;
+  } elsif ($v =~ /^id$/i) {
+    $id = checkchars($vv);
+  }
+}
+
+if ($akey and not $user and not $id) {
+  if (open $akey,'<',"$akeydir/$akey/@" and $id = getline($akey)) {
+    close $akey;
+    $user = readlink "$akeydir/$akey"
+      or http_die("internal server error: no $akey symlink");
+    $user =~ s:.*/::;
+    $user = untaint($user);
+    if ($akey ne md5_hex("$user:$id")) {
+      $user = $id = '';
+    }
+  }
+}
+
+$head = "$ENV{SERVER_NAME} F*EX operation control";
+
+# display HTML form and request user data
+if ($user and $id) {
+  my $idf;
+  unless (open $idf,'<',"$user/@") {
+    faillog("user $from, id $id");
+    html_error($error,"wrong user or auth-ID");
+  }
+  &check_status($user);
+  if (-e "$user/\@CAPTIVE") { html_error($error,"captive user") }
+  $rid = getline($idf);
+  close $idf;
+  if ($id eq $rid) {
+    unless ($akey) {
+      $akey = untaint(md5_hex("$user:$id"));
+      unlink "$akeydir/$akey";
+      symlink "../$user","$akeydir/$akey";
+    }
+  } else {
+    faillog("user $from, id $id");
+    html_error($error,"wrong user or auth-ID");
+  }
+  unlink $faillog if $faillog;
+  http_header("200 OK");
+  print html_header($head);
+  # authorized login URL
+  my $url = "$ENV{PROTO}://$ENV{HTTP_HOST}/fup/".b64("from=$user&id=$id");
+  pq(qq(
+    '<h2>for user <a href="$url">$user</a></h2>'
+    '<table>'
+  ));
+  ($quota,$du) = check_sender_quota($user);
+  if ($quota) {
+    pq(qq(
+      <tr title="You as the sender have a server disk quota of $quota MB and currently using $du MB">
+        <td>sender quota (used):<td align=\"right\">$quota ($du) MB
+      </tr>
+    ));
+  }
+  ($quota,$du) = check_recipient_quota($user);
+  if ($quota) {
+    pq(qq(
+      <tr title="You as the recipient have a server disk quota of $quota MB and currently using $du MB">
+        <td>recipient quota (used):<td align=\"right\">$quota ($du) MB
+      </tr>
+    ));
+  }
+  pq(qq(
+    '</table>'
+    '<p><hr><p>'
+    '<a href="/fup?akey=$akey&command=LISTRECEIVED">'
+    'Retrieve a list of all your received files</a> in F*EX spool.'
+  ));
+  pq(qq(
+    '<p><hr><p>'
+    '<form action="/fuc?akey=$akey"'
+    '      method="post"'
+    '      accept-charset="UTF-8"'
+    '      enctype="multipart/form-data">'
+    '  <input type="hidden" name="user" value="$user">'
+    '  <input type="hidden" name="id"   value="$id">'
+    '  <script>function show_id() {return(alert("auth-ID: $id"));}</script>'
+    '  Change your <a href="" onclick="show_id();" title="$id">auth-ID</a> to'
+    '  <input type="text"   name="nid"  size="16">'
+    '  <input type="submit" value="remember it!">'
+  ));
+  if (-s "$user/\@ALLOWED_RECIPIENTS") {
+    # pq(qq(
+    #  '  (You are a restricted user)';
+    #  '  <p>'
+    # ));
+    #    '<p><hr><p>'
+    #    '<a href="/fup?akey=$akey&command=LISTSENT">'
+    #    'Show download URLs of files you have sent</a>.'
+    unless ($nomail) {
+      pq(qq(
+        '<p><hr><p>'
+        '<a href="/fup?akey=$akey&command=RENOTIFY">'
+        'Resend notification e-mails for files you have sent</a>.'
+      ));
+    }
+  } else {
+    pq(qq(
+      '<p><hr><p>'
+      '<a href="/fup?akey=$akey&command=LIST&to=*">'
+      'Forward a copy of a file</a> you already have uploaded to another recipient.'
+      '<p><hr><p>'
+      '<a href="/rup?akey=$akey">'
+      'Redirect files</a> you have uploaded to a wrong or misspelled recipient.'
+    ));
+    unless ($nomail) {
+      pq(qq(
+        '<p><hr><p>'
+        '<a href="/fup?akey=$akey&command=RENOTIFY">'
+        'Resend notification e-mails for files you have sent</a>.'
+      ));
+    }
+    pq(qq(
+      '<p><hr><p>'
+      '  Create a subuser who can send you files. Enter his e-mail address:<br>'
+      '  <input type="text" name="subuser" size="60">'
+      '  <input type="checkbox" name="otuser" value="once">for only one upload<br>'
+    ));
+    if ($nomail) {
+      pq(qq(
+        '  <input type="submit" value="create subuser">'
+      ));
+    } else {
+      pq(qq(
+        '  Comment to send with information e-mail:<br>'
+        '  <input type="text" name="comment" size="80"><br>'
+        '  <input type="submit" value="create subuser and send e-mail">'
+      ));
+    }
+    pq(qq(
+      '  <p><hr><p>'
+      '  <a href="/fuc?akey=$akey">Manage your subusers and groups</a>'
+      '  <p><hr><p>'
+      '  <a href="/fuc?ab=load&akey=$akey">Edit your address book</a>'
+    ));
+    pq(qq(
+      '  <p><hr><p>'
+      '  <a href="/fuc?disclaimer=CHANGE&akey=$akey">'
+      '  Change the disclaimer</a> to be sent with notification e-mail.'
+    ));
+  }
+  pq(qq(
+    '  <p><hr><p>'
+    '  <a href="/fuc?encryption=CHANGE&akey=$akey">'
+    '  (De)activate e-mail encryption</a>.'
+  )) if -s "$ENV{HOME}/.gnupg/pubring.gpg";
+  if ((readlink "$user/\@NOTIFICATION"||'') =~ /short/i) {
+    pq(qq(
+      '  <p><hr><p>'
+      '  Get <a href="/fuc?notification=detailed&akey=$akey">detailed</a> notification e-mails (current setting: <em>brief</em>).'
+    ));
+  } else {
+    pq(qq(
+      '  <p><hr><p>'
+      '  Get <a href="/fuc?notification=short&akey=$akey">brief</a> notification e-mails (current setting: <em>detailed</em>).'
+    ));
+  }
+  if ((readlink "$user/\@REMINDER"||'') =~ /no/i) {
+    pq(qq(
+      '  <p><hr><p>'
+      '  Get <a href="/fuc?reminder=yes&akey=$akey">reminder</a> notification e-mails (current setting: <em>no reminders</em>).'
+    ));
+  } else {
+    pq(qq(
+      '  <p><hr><p>'
+      '  Get <a href="/fuc?reminder=no&akey=$akey">no reminder</a> notification e-mails (current setting: <em>send reminders</em>).'
+    ));
+  }
+  if (-e "$user/\@MIME") {
+    pq(qq(
+      '  <p><hr><p>'
+      '  <a href="/fuc?mime=no&akey=$akey">Save</a> files after download (current setting: <em>display</em>).'
+    ));
+  } else {
+    pq(qq(
+      '  <p><hr><p>'
+      '  <a href="/fuc?mime=yes&akey=$akey">Display</a> files when downloading with web browser (current setting: <em>save</em>).'
+    ));
+  }
+  pq(qq(
+    '  <p><hr><p>'
+    '  <a href="/fup?akey=$akey">Back to fup (upload page)</a>'
+    '</form>'
+  ));
+  print &logout;
+  print "</body></html>\n";
+  exit;
+}
+
+my $login = -x "$FEXHOME/login" ? 'login' : 'fup';
+nvt_print(
+  "HTTP/1.1 302 Found",
+  "Location: $ENV{PROTO}://$ENV{HTTP_HOST}/$login",
+  'Expires: 0',
+  'Content-Length: 0',
+  ''
+);
+&reexec;
diff --git a/cgi-bin/fop b/cgi-bin/fop
new file mode 100755 (executable)
index 0000000..4370fb6
--- /dev/null
@@ -0,0 +1,1087 @@
+#!/usr/bin/perl -wT
+
+# F*EX CGI for download
+#
+# Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
+#
+
+use CGI                qw':standard';
+use CGI::Carp          qw'fatalsToBrowser';
+use Fcntl              qw':flock :seek';
+use Cwd                        qw'abs_path';
+use File::Basename;
+use IO::Handle;
+use Encode;
+
+# add fex lib
+($FEXLIB) = $ENV{FEXLIB} =~ /(.+)/;
+die "$0: no $FEXLIB\n" unless -d $FEXLIB;
+
+our $error = 'F*EX download ERROR';
+our $head = "$ENV{SERVER_NAME} F*EX download";
+# import from fex.pp
+our ($spooldir,$tmpdir,$logdir,$skeydir,$dkeydir,$durl);
+our ($bs,$fop_auth,$timeout,$keep_default);
+our ($limited_download,$admin,$akey,$adlm,$amdl);
+our (@file_link_dirs);
+
+# load common code, local config : $HOME/lib/fex.ph
+require "$FEXLIB/fex.pp" or die "$0: cannot load $FEXLIB/fex.pp - $!\n";
+
+my $ra = $ENV{REMOTE_ADDR}||0;
+if (@download_hosts and not ipin($ra,@download_hosts)) {
+  http_die(
+    "Downloads from your host ($ra) are not allowed.",
+    "Contact $ENV{SERVER_ADMIN} for details."
+  );
+}
+
+&check_maint;
+
+# call localized fop if available
+if ($0 !~ m{/locale/.*/fop} and my $lang = $ENV{HTTP_ACCEPT_LANGUAGE}) {
+  if ($lang =~ /^de/ and $0 =~ m{(.*)/cgi-bin/fop}) {
+    my $fop = "$1/locale/deutsch/cgi-bin/fop";
+    exec $fop if -x $fop;
+  }
+}
+
+my $log = "$logdir/fop.log";
+
+chdir $spooldir or die "$spooldir - $!\n";
+
+my $http_client = $ENV{HTTP_USER_AGENT} || '';
+
+$file = $ENV{PATH_INFO} || '';
+http_die('no file name') unless $file;
+$file =~ s:%3F:/?/:g; # escape '?' for URL-decoding
+$file =~ s/%([\dA-F]{2})/unpack("a",pack("H2",$1))/ge;
+$file =~ s:/\?/:%3F:g; # deescape '?'
+$file =~ s:/\.\.:/__:g;
+$file =~ s:^/+::;
+$file = untaint($file);
+
+# secure mode with HTTP authorization?
+if ($fop_auth) {
+  @http_auth = ();
+  if ($ENV{HTTP_AUTHORIZATION} and $ENV{HTTP_AUTHORIZATION} =~ /Basic\s+(.+)/) {
+    @http_auth = split(':',decode_b64($1));
+  }
+  if (@http_auth != 2) {
+    &require_auth;
+  }
+  &check_auth($file,@http_auth);
+}
+
+# download-URL-scheme /$dkey/$file ?
+if ($file =~ m:^([^/]+)/[^/]+$:) {
+  $dkey = $1;
+  if ($link = readlink("$dkeydir/$dkey")) {
+    if ($link !~ s:^\.\./::) {
+      http_die("internal error on dkey for $link");
+    }
+    $file = untaint($link);
+  } else {
+    http_die("no such file $file");
+  }
+} else {
+  # download-URL-scheme /$to/$from/$file
+  $file =~ s/\?.*//;
+
+  if ($ENV{REQUEST_METHOD} eq 'GET' and $file =~ m:.+/(.+)/.+:) {
+    $from = lc $1;
+    if (-s "$from/\@ALLOWED_RECIPIENTS") { 
+      http_die("$from is a restricted user");
+    }
+  }
+    
+  # add mail-domain to addresses if necessary
+  if ($mdomain and $file =~ s:(.+)/(.+)/(.+):$3:) {
+    $to   = lc $1;
+    $from = lc $2;
+    $to   .= '@'.$hostname if $to   eq 'anonymous';
+    $from .= '@'.$hostname if $from eq 'anonymous';
+    $to   .= '@'.$mdomain if -d "$to\@$mdomain";
+    $from .= '@'.$mdomain if -d "$from\@$mdomain";
+    if ($ENV{REQUEST_METHOD} eq 'GET' and -s "$from/\@ALLOWED_RECIPIENTS") {
+      http_die("$from is a restricted user");
+    }
+    $file = "$to/$from/$file";
+  }
+}
+
+if ($file and $file =~ m:(.+)/(.+)/.+:) {
+  $to   = $1;
+  $from = $2;
+  # afex!
+  if ($from =~ s/^(anonymous).*/$1/) {
+    if (@anonymous_upload and ipin($ra,@anonymous_upload) or $dkey) {
+      $anonymous = $from;
+    } else {
+      http_header('403 Forbidden');
+      print html_header($head),
+        "You have no permission to request the URI $ENV{REQUEST_URI}\n",
+        "</body></html>\n";
+      exit;
+    }
+  }
+} else {
+  http_die("unknown query format");
+}
+
+$data = "$file/data";
+
+# open $file,$file; print Digest::MD5->new->addfile($file)->hexdigest;
+
+# request with ?query-parameter ?
+if ($qs = $ENV{QUERY_STRING}) {
+
+  http_die("\"$1\" is not allowed in URL") if $qs =~ /([<>\%\'\"])/;
+
+  # workaround for broken F*IX
+  $qs =~ s/&ID=skey:\w+//;
+  
+  # subuser with skey?
+  if ($qs =~ s/&*SKEY=([\w:]+)//i) {
+    $skey = $1;
+    # encrypted skey?
+    if ($skey =~ s/^MD5H:(.+)/$1/) {
+      # lookup real skey
+      foreach my $s (glob "$skeydir/*") {
+        $s =~ s:.*/::;
+        if ($skey eq md5_hex($s.$ENV{SID})) {
+          $skey = $s;
+          last;
+        }
+      }
+    }
+    if (open $skey,'<',"$skeydir/$skey") {
+      $from = $to = '';
+      while (<$skey>) {
+        $from = lc($1) if /^from=(.+)/;
+        $to   = lc($1) if /^to=(.+)/;
+      }
+      close $skey;
+      if ($from and $to) {
+        $file =~ s:.*/:$to/$from/:;
+      } else {
+        http_die("INTERNAL ERROR: missing data in $skeydir/$skey");
+      }
+    } else {
+      debuglog("SKEY=$skey");
+      http_die("wrong SKEY authentification");
+    }
+  }
+  
+  # group member with gkey?
+  if ($qs =~ s/&*GKEY=([\w:]+)//i) {
+    $gkey = $1;
+    # encrypted gkey?
+    if ($gkey =~ s/^MD5H:(.+)/$1/) {
+      # lookup real gkey
+      foreach my $g (glob "$gkeydir/*") {
+        $g =~ s:.*/::;
+        if ($gkey eq md5_hex($g.$ENV{SID})) {
+          $gkey = $g;
+          last;
+        }
+      }
+    }
+    if (open $gkey,'<',"$gkeydir/$gkey") {
+      $from = $to = '';
+      while (<$gkey>) {
+        $from  = lc($1) if /^from=(.+)/;
+        $group = lc($1) if /^to=\@(.+)/;
+      }
+      close $gkey;
+      if ($from and $group and open $group,'<',"$from/\@GROUP/$group") {
+        while (<$group>) {
+          s/#.*//;
+          s/\s//g;
+          if (/(.+):/) {
+            my $to = $1;
+            $file =~ s:.*/:$to/$from/:;
+            last;
+          }
+        }
+        close $group;
+      } else {
+        http_die("INTERNAL ERROR: missing data in $gkeydir/$gkey");
+      }
+    } else {
+      debuglog("GKEY=$gkey");
+      http_die("wrong GKEY authentification");
+    }
+  }
+  
+  # check for ID in query
+  elsif ($qs =~ s/\&*\bID=([^&]+)//i) {
+    $id = $1;
+    $fop_auth = 0;
+    
+    if ($id eq 'PUBLIC') {
+      http_header('403 Forbidden');
+      exit;
+    }
+
+    if ($file =~ m:^(.+)/(.+)/(.+):) {
+      $to   = $1;
+      $from = $2;
+      $to   =~ s/,+/,/g;
+      $to   =~ s/\s//g;
+      $from =~ s/\s//g;
+      if ($mdomain and $from ne 'anonymous') {
+        $to   .= '@'.$mdomain if $to   !~ /@/;
+        $from .= '@'.$mdomain if $from !~ /@/;
+      }
+      $to   = lc $to;
+      $from = lc $from;
+    } else {
+      http_die("unknown file query format");
+    }
+
+    # public or anonymous recipient? (needs no auth-ID for sender)
+    if ($anonymous or $id eq 'PUBLIC' and 
+        @public_recipients and grep /^\Q$to\E$/i,@public_recipients) {
+      $rid = $id;
+    } else {
+      open my $idf,'<',"$from/@" or http_die("unknown user $from");
+      $rid = getline($idf);
+      close $idf;
+      $rid = sidhash($rid,$id);
+    }
+      
+    unless ($id eq $rid) {
+      debuglog("real id=$rid, id sent by user=$id");
+      http_die("wrong auth-ID");
+    }
+    
+    # set akey link for HTTP sessions
+    # (need original id for consistant non-moving akey)
+    if (-d $akeydir and open $idf,'<',"$from/@" and my $id = getline($idf)) {
+      $akey = untaint(md5_hex("$from:$id"));
+      unlink "$akeydir/$akey";
+      symlink "../$from","$akeydir/$akey";
+    }
+    
+    my %to;
+    COLLECTTO: foreach my $to (split(',',$to)) {
+      if ($to !~ /.@./ and open my $AB,'<',"$from/\@ADDRESS_BOOK") {
+        while (<$AB>) {
+          s/\s*#.*//;
+          s/^\s+//;
+          next unless $_;
+          if (/^\s*([\S]+)\s+([\S]+)/) {
+            my ($alias,$address) = ($1,$2);
+            if ($to =~ /^\Q$alias\E$/i) {
+              foreach my $to (split(",",$address)) {
+                $to .= '@'.$mdomain if $mdomain and $to !~ /@/;
+                $to{$to} = lc $to; # ignore dupes
+              }
+              next COLLECTTO;
+            }
+          }
+        }
+      } elsif ($to =~ /^\@(.+)/) {
+        my $group = "$from/\@GROUP/$1";
+        if (not -l $group and open $group) {
+          while (<$group>) {
+            s/#.*//;
+            s/\s//g;
+            if (/(.+\@[w.-]+):.+/) {
+              $to{$1} = lc $1; # ignore dupes
+            }
+          }
+          close $group;
+        }
+      } else {
+        $to .= '@'.$mdomain if $mdomain and $to !~ /.@./;
+        $to{$to} = lc $to; # ignore dupes
+      }
+    }
+    foreach $to (keys %to) {
+      # if (-e "$to/\@CAPTIVE") { http_die("$to is CAPTIVE") }
+      unless (-d $to or checkaddress($to)) {
+        http_die("$to is not a legal e-mail address");
+      }
+    }
+      
+  }
+  
+  if ($qs =~ /\&?KEEP=(\d+)/i) {
+    $keep = $1;
+    $filename = filename($file);
+    check_captive($file);
+    if  (-f $data) {
+      unlink "$file/keep";
+      if (symlink $keep,"$file/keep") {
+        http_header('200 OK');
+        print html_header($head),
+              "<h3>set keep=$keep for $filename</h3>\n",
+              "</body></html>\n";
+      } else {
+        http_header('599 internal error');
+        print html_header($head),
+              "<h3>$filename - $!</h3>\n",
+              "</body></html>\n";
+      }
+    } else {
+      http_header('404 File not found');
+      print html_header($head),
+            "<h3>$filename not found</h3>\n",
+            "</body></html>\n";
+    }
+    exit;
+  } elsif ($qs =~ s/\&?KEEP//i) { 
+    check_captive($file);
+    $autodelete = 'NO';
+  }
+  
+  if ($qs =~ s/\&?FILEID=(\w+)//i) { $fileid = $1 }
+
+  if ($qs =~ s/\&?IGNOREWARNING//i) { $ignorewarning = 1 }
+  
+  if ($qs eq 'LIST') {
+    http_header('200 OK','Content-Type: text/plain');
+    print "$file :\n";
+    chdir $file and exec '/client/bin/l';
+    exit;
+  }
+
+  # copy file to yourself
+  if ($qs eq 'COPY') {
+    unless (-f "$file/data") {
+      http_die("File not found.");
+    }
+    ($to,$from,$file) = split('/',$file);
+    unless ("$to/@") {
+      # http_header('403 Forbidden');
+      # print html_header($head),
+      #  "You have no permission to copy a file.\n",
+      #  "</body></html>\n";
+      http_die("You have no permission to copy a file.");
+    }
+    if (-s "$to/\@ALLOWED_RECIPIENTS") {
+      http_die("You are a restricted user.");
+    }
+    if (-e "$to/$to/$file/data") {
+      # http_header('409 File Exists');
+      # print html_header($head),
+      #   "File $file already exists in your outgoing spool.\n",
+      #   "</body></html>\n";
+      http_die("File $file already exists in your outgoing spool.");
+    }
+    mkdirp("$to/$to/$file");
+    link "$to/$from/$file/data","$to/$to/$file/data" 
+      or http_die("cannot link to $to/$to/$file/data - $!\n");
+    my $fkey = copy("$to/$from/$file/filename","$to/$to/$file/filename");
+    open my $notify,'>',"$to/$to/$file/notify";
+    close $notify;
+    my $dkey = randstring(8);
+    unlink "$to/$to/$file/dkey","$dkeydir/$dkey";
+    symlink "../$to/$to/$file","$dkeydir/$dkey";
+    symlink $dkey,"$to/$to/$file/dkey";
+    http_header('200 OK',"Location: $durl/$dkey/$fkey");
+    print html_header($head),
+      "File $file copied to yourself.\n",
+      "</body></html>\n";
+    exit;
+  }
+  
+  # ex and hopp?
+  if ($qs =~ s/(^|&)DELETE//i) {
+    if (unlink $data) {
+      $filename = filename($file);
+      if (open my $log,'>',"$file/error") {
+        printf {$log} "%s has been deleted by %s at %s\n",
+                      $filename,$ENV{REMOTE_ADDR},isodate(time);
+        close $log;
+      }
+      if (open $log,'>>',$log) {
+        printf {$log}
+               "%s [%s_%s] %s %s deleted\n",
+               isodate(time),$$,$ENV{REQUESTCOUNT},$ra,encode_Q($file);
+        close $log;
+      }
+      http_header('200 OK',"X-File: $file");
+      print html_header($head),
+            "<h3>$filename deleted</h3>\n",
+            "</body></html>\n";
+      exit;
+    } else { 
+      http_die("no such file");
+    }
+    exit;
+  } 
+  
+  # wipe out!? (for anonymous upload)
+  if ($qs =~ s/(^|&)PURGE//i) {
+    $filename = filename($file);
+    if (@anonymous_upload and ipin($ra,@anonymous_upload)) {
+      unlink "$dkeydir/$dkey" if $dkey;
+      if (rmrf($file)) {
+        if (open $log,'>>',$log) {
+          printf {$log}
+                 "%s [%s_%s] %s %s purged\n",
+                 isodate(time),$$,$ENV{REQUESTCOUNT},$ra,encode_Q($file);
+          close $log;
+        }
+        http_header('200 OK',"X-File: $file");
+        print html_header($head),
+          "<h3>$filename purged</h3>\n",
+          "</body></html>\n";
+      } else { 
+        http_die("no such file");
+      }
+    } else { 
+      http_die("you are not allowed to purge $filename");
+    }
+    exit;
+  } 
+  
+  # request for file size?
+  if ($qs eq '?') {
+    sendsize($file);
+    # control back to fexsrv for further HTTP handling
+    &reexec;
+  }
+
+  # fallback
+  if ($qs) {
+    http_die("unknown query format $qs");
+  }
+
+}
+
+unless ($id and $rid and $id eq $rid or $dkey or $anonymous) {
+  http_die("wrong parameter $file");
+}
+
+unless ($to) {
+  http_die("internal error: unknown recipient");
+}
+
+unless ($from) {
+  http_die("internal error: unknown sender");
+}
+
+&check_status($from);
+
+# server based ip restrictions
+if (@download_hosts and not ipin($ra,@download_hosts)) {
+  http_die(
+    "Downloads from your host ($ra) are not allowed.",
+    "Contact $ENV{SERVER_ADMIN} for details."
+  );
+}
+
+# user based ip restrictions
+unless (check_rhosts("$to/\@DOWNLOAD_HOSTS")) {
+  http_die("You are not allowed to download from IP $ra");
+}
+
+# file based ip restrictions
+unless (check_rhosts("$file/restrictions")) {
+  http_die("Download of files from external user $from is restricted "
+          ."to internal hosts. Your IP $ra is not allowed.");
+}
+
+# set time mark for this access
+if ($file =~ m:(.+?)/:) {
+  my $user = $1;
+  my $time = untaint(time);
+  utime $time,$time,$user;
+}
+
+# reget or range?
+if ($range = $ENV{HTTP_RANGE}) {
+  $seek = $1 if $range =~ /^bytes=(\d+)-/i;
+  $stop = $1 if $range =~ /^bytes=\d*-(\d+)/i;
+} else {
+  $seek = 0;
+  $stop = 0;
+}
+
+if (not $autodelete or $autodelete ne 'NO') {
+  $autodelete = readlink "$file/autodelete" || 'YES';
+}
+  
+if ($from and $file eq "$from/$from/ADDRESS_BOOK") {
+  if (open my $AB,'<',"$from/\@ADDRESS_BOOK") {
+    my $ab = '';
+    while (<$AB>) {
+      s/^\s+//;
+      s/\s+$//;
+      s/[\r\n]//g;
+      $ab .= $_."\r\n";
+    }
+    close $AB;
+    nvt_print(
+      'HTTP/1.1 200 OK',
+      'Content-Length: ' . length($ab),
+      'Content-Type: text/plain',
+      ''
+    );
+    print $ab;
+  } else {
+    nvt_print(
+      'HTTP/1.1 404 No address book found',
+      'Content-Length: 0',
+      ''
+    );
+  }
+  # control back to fexsrv for further HTTP handling
+  &reexec;
+}
+
+if (-f $data) {
+  # already downloaded?
+  if ($limited_download and $limited_download !~ /^n/i
+      and $from ne $to                    # fex to yourself is ok!
+      and $to !~ /$amdl/                  # allowed multi download recipients
+      and $from !~ /^_?fexmail/                  # fexmail is ok!
+      and $to !~ /^_?fexmail/            # fexmail is ok!
+      and $to !~ /^anonymous/            # anonymous fex is ok!
+      and $http_client !~ /$adlm/         # allowed download managers
+      and $file !~ /\/STDFEX$/            # xx is ok!
+      and (slurp("$file/comment")||'') !~ /^!\*!/ # multi download allow flag
+      and not($dkey and ($ENV{HTTP_COOKIE}||'') =~ /dkey=$dkey/)
+      and open $file,'<',"$file/download") 
+  {
+    $_ = <$file> || '';
+    close $file;
+    chomp;
+    if ($ra) {
+      # allow downloads from same ip
+      $_ = '' if $ra eq $_;
+      # allow downloads from sender ip
+      $_ = '' if (readlink("$file/ip")||'') eq $ra;
+    }
+    if ($_) {
+      s/(.+) ([\w.:]+)$/by $2 at $1/;
+      $file = filename($file);
+      http_die("$file has already been downloaded $_");
+    }
+  }
+  $sb = sendfile($file,$seek,$stop);
+  shutdown(STDOUT,2);
+} elsif (-l $data) {
+  # $file =~ s:.*/::;
+  http_die("<code>$file</code> has been withdrawn");
+} elsif (open $errf,'<',"$file/error" and $err = getline($errf)) {
+  fdlog($log,$file,0,0);
+  http_die($err);
+} else {
+  fdlog($log,$file,0,0);
+  if ($file =~ /^anonymous.*afex_\d+\.tar$/) {
+    # should be extra handled...
+  }
+  http_die("no such file $file");
+}
+
+debuglog(sprintf("%s %s %d %d %d",
+         isodate(time),$file,$sb||0,$seek,-s $data||0));
+
+if ($sb+$seek == -s $data) {
+  
+  # note successfull download
+  $download = "$file/download";
+  if (open $download,'>>',$download) {
+    printf {$download} "%s %s\n",isodate(time),$ENV{REMOTE_ADDR};
+    close $download;
+  }
+  
+  # delete file after grace period
+  if ($autodelete eq 'YES') {
+    $grace_time = 60 unless defined $grace_time;
+    for (;;) {
+      my $utime = (stat $data)[8] || 0;
+      my $dtime = (stat $download)[8] || 0;
+      exit if $utime > $dtime;
+      last if time > $dtime+$grace_time;
+      sleep 10;
+    }
+    unlink $data;
+    my $error = "$file/error";
+    if (open $error,'>',$error) {
+      printf {$error} "%s has been autodeleted after download from %s at %s\n",
+                      filename($file),$ENV{REMOTE_ADDR},isodate(time);
+      close $error;
+    }
+  }
+  
+}
+
+exit;
+  
+
+sub sendfile {
+  my ($file,$seek,$stop) = @_;
+  my ($filename,$size,$total_size,$fileid,$filetype);
+  my ($data,$download,$header,$buf,$range,$s,$b,$t0);
+  my $type = '';
+  
+  # swap to and from for special senders, see fup storage swap!
+  $file =~ s:^(_?anonymous_.*)/(anonymous.*)/:$2/$1/:;
+  $file =~ s:^(_?fexmail_.*)/(fexmail.*)/:$2/$1/:;
+  
+  $data     = $file.'/data';
+  $download = $file.'/download';
+  $header   = $file.'/header';
+  
+  # fallback defaults, should be set later with better values
+  $filename = filename($file);
+  $total_size = -s $data || 0;
+
+  # file link?
+  if (-l $data) {
+    unless (-f $data and -r $data) {
+      http_die("<code>$file</code> has been withdrawn");
+    }
+    $data = abs_path($data);
+    my $fok;
+    foreach (@file_link_dirs) {
+      my $dir = abs_path($_);
+      $fok = $data if $data =~ /^\Q$dir\//;
+    }
+    unless ($fok) {
+      http_die("no permission to download <code>$file</code>");
+    }
+  } else {
+    unless (-f $data and -r $data) {
+      http_die("<code>$file</code> has gone");
+    }
+  }
+
+  if ($ENV{REQUEST_METHOD} eq 'GET') {
+    debuglog("Exp: FROM=\"$from\"","Exp: TO=\"$to\"");
+    open $data,$data and flock($data,LOCK_EX|LOCK_NB);
+    # security check: must be regular file after abs_path()
+    if (-l $data) {
+      http_die("no permission to download <code>$file</code>");
+    }
+    # HTTP Range download suckers are already rejected by fexsrv
+    unless ($range = $ENV{HTTP_RANGE}) {
+      # download lock
+      open $download,'>>',$download or die "$download - $!\n";
+      if ($file =~ m:(.+?)/(.+?)/: and $1 ne $2) {
+        # only one concurrent download is allowed if sender <> recipient
+        flock($download,LOCK_EX|LOCK_NB) or
+          http_die("$file locked: a download is already in progress");
+      }
+    }
+    $size = $total_size - $seek - ($stop ? $total_size-$stop-1 : 0);
+  } elsif ($ENV{REQUEST_METHOD} eq 'HEAD') { 
+    $size = -s $data || 0;
+  } else { 
+    http_die("unknown HTTP request method $ENV{REQUEST_METHOD}");
+  }
+  
+  # read MIME entity header (what the client said)
+  if (open $header,'<',$header) {
+    while (<$header>) {
+      if (/^Content-Type: (.+)/i) {
+        $type = $1;
+        last;
+      }
+    }
+    close $header;
+    $type =~ s/\s//g;
+  }
+  
+  $fileid = readlink "$file/id" || '';
+  
+  # determine own MIME entity header for download
+  my $mime = $file;
+  $mime =~ s:/.*:/\@MIME:;
+  my $mt = $ENV{FEXHOME}.'/etc/mime.types';
+  if (($type =~ /x-mime/i or -e $mime) and open $mt,'<',$mt) {
+    $type = 'application/octet-stream';
+    MIMETYPES: while (<$mt>) {
+      chomp;
+      s/#.*//;
+      s/^\s+//;
+      my ($mt,@ft) = split;
+      foreach my $ft (@ft) {
+        if ($filename =~ /\.\Q$ft\E$/i) {
+          $type = $mt;
+          last MIMETYPES;
+        }
+      }
+    }
+    close $mt;
+  }
+  # reset to default MIME type
+  else { $type = 'application/octet-stream' }
+  
+  # HTML is not allowed for security reasons! (embedded javascript, etc)
+  $type =~ s/html/plain/i;
+
+  debuglog("download with $http_client");
+
+  if ($seek or $stop) {
+    if ($size < 0) {
+      http_header('416 Requested Range Not Satisfiable');
+      exit;
+    }
+    if ($stop) {
+      $range = sprintf("bytes %s-%s/%s",$seek,$stop,$total_size);
+    } else {
+      $range = sprintf("bytes %s-%s/%s",$seek,$total_size-1,$total_size);
+    }
+    nvt_print(
+      'HTTP/1.1 206 Partial Content',
+      "Content-Length: $size",
+      "Content-Range: $range",
+      "Content-Type: $type",
+    );
+    if ($http_client !~ /MSIE/) {
+      nvt_print("Cache-Control: no-cache");
+      if ($type eq 'application/octet-stream') {
+        nvt_print("Content-Disposition: attachment; filename=\"$filename\"");
+      }
+    }
+    nvt_print('');
+  } else {
+    # another stupid IE bug-workaround 
+    # http://drupal.org/node/163445
+    # http://support.microsoft.com/kb/323308
+    if ($http_client =~ /MSIE/) {
+      # $type = 'application/x-msdownload';
+      if ($ignorewarning) {
+        $type .= "; filename=$filename";
+        nvt_print(
+          'HTTP/1.1 200 OK',
+          "Content-Length: $size",
+          "Content-Type: $type",
+#         "Pragma: no-cache",
+#         "Cache-Control: no-store",
+          "Content-Disposition: attachment; filename=\"$filename\"",
+          "Connection: close",
+        );
+#        nvt_print('','HTTP/1.1 200 OK',"Content-Length: $size","Content-Type: $type"); exit;
+      } else {
+        http_header('200 OK');
+        print html_header($head);
+        pq(qq(
+          '<h2>Internet Explorer warning</h2>'
+          'Using Microsoft Internet Explorer for download will probably'
+          'lead to problems, because it is not Internet compatible (RFC 2616).'
+          '<p>'
+          'We recommend <a href="http://firefox.com">Firefox</a>'
+          '<p>'
+          'If you really want to continue with Internet Explorer, then'
+          '<a href="$ENV{REQUEST_URL}?IGNOREWARNING">'
+          'click here with your right mouse button and select "save as"'
+          '</a>'
+          '<p>'
+          'See also <a href="/FAQ/user.html">F*EX user FAQ</a>.'
+          '</body></html>'
+        ));
+        &reexec;
+      }
+    } else {
+      nvt_print(
+        'HTTP/1.1 200 OK',
+        "Content-Length: $size",
+        "Content-Type: $type",
+        "Cache-Control: no-cache",
+        "Connection: close",
+      );
+      if ($type eq 'application/octet-stream') {
+        nvt_print(qq'Content-Disposition: attachment; filename="$filename"');
+      }
+    }
+
+    nvt_print("X-Size: $total_size");
+    nvt_print("X-File-ID: $fileid") if $fileid;
+    # if ((`file "$file/data" 2>/dev/null` || '') =~ m{.*/data:\s(.+)}) {
+    #  nvt_print("X-File-Type: $1");
+    # }
+    if ($dkey = $dkey||readlink "$file/dkey") {
+      my $ma = (readlink "$file/keep"||$keep_default)*60*60*24;
+      nvt_print("Set-Cookie: dkey=$dkey; Max-Age=$ma; Path=$ENV{REQUEST_URI}");
+    }
+    nvt_print('');
+  }
+
+  if ($ENV{REQUEST_METHOD} eq 'HEAD') {
+    # control back to fexsrv for further HTTP handling
+    &reexec;
+  }
+  
+  if ($ENV{REQUEST_METHOD} eq 'GET') {
+
+    if (@throttle) {
+      my $to = $file;
+      $to =~ s:/.*::;
+      foreach (@throttle) {
+        if (/(.+):(\d+)$/) {
+          my $throttle = $1;
+          my $limit = $2;
+          # throttle ip address?
+          if ($throttle =~ /^[\d.-]+$/) {
+            if (ipin($ra,$throttle)) {
+              $bwl = $limit;
+              last;
+            }
+          } 
+          # throttle e-mail address?
+          else {
+            # allow wildcard *, but not regexps
+            $throttle =~ quotemeta $throttle;
+            $throttle =~ s/\*/.*/g;
+            if ($to =~ /$throttle$/) {
+              $bwl = $limit;
+              last;
+            }
+          }
+        }
+      }
+    }
+    
+    foreach my $sig (keys %SIG) { local $SIG{$sig} = \&sigexit }
+    local $SIG{ALRM} = sub { die "TIMEOUT\n" };
+
+    seek $data,$seek,0;
+
+    $t0 = time;
+    $s = $b = 0;
+
+    # sysread/syswrite because of speed
+    while ($s < $size and $b = sysread($data,$buf,$bs)) {
+      # last chunk for HTTP Range?
+      if ($stop and $s+$b > $size) {
+        $b = $size-$s;
+        $buf = substr($buf,0,$b)
+      }
+      $s += $b;      
+      alarm($timeout*10);
+      syswrite STDOUT,$buf or last; # client still alive?
+      if ($bwl) {
+        alarm(0);
+        sleep 1 while $s/(time-$t0||1)/1024 > $bwl;
+      }
+    }
+    
+    close $data;
+    alarm(0);
+    
+    fdlog($log,$file,$s,$size);
+  }
+  close $download;
+  
+  return $s;
+}
+
+
+sub sendsize {
+  my ($path) = @_;
+  my ($file,$upload,$to,$from,$dkey);
+  my $size = 0;
+  local $_;
+  
+  $path =~ s:^/::;
+  ($to,$from,$file) = split('/',$path);
+  $to =~ s/,.*//;
+  $to   = lc $to;
+  $from = lc $from;
+  
+  # swap to and from for special senders, see fup storage swap!
+  ($from,$to) = ($to,$from) if $from =~ /^(fexmail|anonymous)/;
+
+  $to   .= '@'.$hostname if $to   eq 'anonymous';
+  $from .= '@'.$hostname if $from eq 'anonymous';
+
+  $to   .= '@'.$mdomain if -d "$to\@$mdomain";
+  $from .= '@'.$mdomain if -d "$from\@$mdomain";
+
+  $file =~ s/%([A-F0-9]{2})/chr(hex($1))/ge;
+  $file = urlencode($file);
+
+  if ($to eq '*' and $fileid) {
+    foreach my $fd (glob "*/$from/$file") {
+      if (-f "$fd/data" 
+          and -l "$fd/id" and readlink "$fd/id" eq $fileid
+          and $dkey = readlink "$fd/dkey") {
+        $to = $fd;
+        $to =~ s:/.*::;
+        last;
+      }
+    }
+  } elsif ($to !~ /@/ and open my $AB,'<',"$from/\@ADDRESS_BOOK") {
+    while (<$AB>) {
+      s/\s*#.*//;
+      $_ = lc $_;
+      my ($alias,$address) = split;
+      if ($address) {
+        $address =~ s/,.*//;
+        $address .= '@'.$mdomain if $mdomain and $address !~ /@/;
+        if ($to eq $alias) {
+          $to = $address;
+          last;
+        }
+      }
+    }
+    close $AB;
+  }
+  
+  if (-f "$to/$from/$file/data") {
+    $dkey = readlink "$to/$from/$file/dkey";
+    $fkey = slurp("$to/$from/$file/filename")||$file;
+  }
+  
+  $upload = -s "$to/$from/$file/upload" || -s "$to/$from/$file/data" || 0;
+  $size = readlink "$to/$from/$file/size" || 0;
+  $fileid = readlink "$to/$from/$file/id" || '';
+
+  nvt_print('HTTP/1.1 200 OK');
+  nvt_print("Server: fexsrv");
+  nvt_print("Content-Length: $upload");
+  nvt_print("X-Original-Recipient: $to");
+  if ($dkey and not -s "$from/\@ALLOWED_RECIPIENTS") {
+    nvt_print("X-DKEY: $dkey");
+    nvt_print("X-Location: $durl/$dkey/$fkey") if $fkey;
+  }
+  nvt_print("X-Size: $size");
+  nvt_print("X-File-ID: $fileid") if $fileid;
+  nvt_print("X-Features: $ENV{FEATURES}");
+  nvt_print('');
+}
+
+
+sub check_rhosts {
+  my $ipr = shift;
+  my @hosts;
+  local $_;
+
+  if (open $ipr,$ipr) {
+    while (<$ipr>) {
+      chomp;
+      s/#.*//;
+      s/\s//g;
+      if ($_ eq '@LOCAL_RHOSTS') {
+        push @hosts,@local_rhosts if @local_rhosts;
+      } elsif (/\w/) {
+        push @hosts,$_;
+      }
+    }
+    close $ipr;
+    if (@hosts and not ipin($ra,@hosts)) {
+      return 0;
+    }
+  }
+  return 1;
+}
+
+
+sub require_auth {
+  http_header(
+    '401 Authorization Required',
+    'WWW-Authenticate: Basic realm="'.$ENV{SERVER_NAME}.' F*EX download"',
+    'Content-Length: 0',
+  );
+  # control back to fexsrv for further HTTP handling
+  &reexec;
+}
+
+
+sub check_auth {
+  my ($path,$user,$auth) = @_;
+  my ($to,$from,$file,$dkey);
+  my ($id,$idf);
+  my ($subuser,$subid);
+  my $auth_ok = 0;
+  local $_;
+
+  if ($path =~ m:(.+)/(.+)/(.+):) {
+    ($to,$from,$file) = ($1,$2,$3);
+  } elsif ($path =~ m:(.+)/(.+):) {  
+    ($dkey,$file) = ($1,$2);
+    $path = readlink "$dkeydir/$dkey" or http_die('no such file');
+    (undef,$to,$from,$file) = split('/',$path);
+  } else { 
+    http_die("wrong URL format for download");
+  }
+
+  $to   .= '@'.$mdomain if $mdomain and $to   !~ /@/;
+  $from .= '@'.$mdomain if $mdomain and $from !~ /@/;
+
+  $to   = lc $to;
+  $from = lc $from;
+
+  # auth user match to in download URL?
+  if ($to ne $user and "$to\@$mdomain" ne $user and $to ne "$user@$mdomain") {
+    debuglog("mismatch: to=$to, auth user=$user");
+    &require_auth;
+  }
+
+  # check for real user
+  if (open $idf,'<',"$to/@") {
+    $id = getline($idf);
+    close $idf;
+    unless ($id and $id eq $auth) {
+      debuglog("$user mismatch: id=$id, auth=$auth");
+      &require_auth;
+    }
+  } 
+  # check for sub user
+  elsif (open $idf,'<',"$from/\@SUBUSER") {
+    while (<$idf>) {
+      chomp;
+      s/#.*//;
+      ($subuser,$subid) = split ':';
+      if ($subid and $subid eq $auth 
+          and ($user eq $subuser 
+               or $subuser eq '*@*'
+               or $subuser =~ /^\*\@(.+)/ and $user =~ /\@\Q$1\E$/i
+               or $subuser =~ /(.+)\@\*$/ and $user =~ /^\Q$1\E\@/i)) {
+        $auth_ok = 1;
+        last;
+      }
+    }
+    close $idf;
+    unless ($auth_ok) {
+      debuglog("no matching $user in $from/\@SUBUSER");
+      &require_auth;
+    }
+  } else {
+    debuglog("no $to/@ and no $from/@");
+    &require_auth;
+  }
+  
+}
+
+
+sub check_captive {
+  my $to = shift;
+  $to =~ s:/.*::;
+  $to .= '@'.$mdomain if $mdomain and -d "$to\@$mdomain";
+  if (-e "$to/\@CAPTIVE") {
+    http_die("$to is CAPTIVE - no URL parameters allowed");
+  }
+}
+
+
+sub sigexit {
+  my ($sig) = @_;
+  my $msg;
+  
+  $msg = @_ ? "@_" : '???';
+  $msg =~ s/\n/ /g;
+  $msg =~ s/\s+$//;
+
+  errorlog("$file caught SIGNAL $msg");
+
+  # sigpipe means: client has terminated
+  # this event will be handled further by sendfile(), do not terminate here
+  if ($sig ne 'PIPE') {
+    $SIG{__DIE__} = '';
+    if ($sig eq 'DIE') {
+      shift;
+      die "$msg\n";
+    } else {
+      die "SIGNAL $msg\n";
+    }
+  }
+}
diff --git a/cgi-bin/fuc b/cgi-bin/fuc
new file mode 100755 (executable)
index 0000000..864a3de
--- /dev/null
@@ -0,0 +1,1222 @@
+#!/usr/bin/perl -wT
+
+# FEX CGI for user control 
+# (subuser, groups, address book, one time upload key, auth-ID, etc)
+#
+# Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
+#
+
+use CGI         qw(:standard);
+use CGI::Carp  qw(fatalsToBrowser);
+use Fcntl      qw(:flock);
+use Digest::MD5        qw(md5_hex);
+
+$CGI::LIST_CONTEXT_WARN = 0;
+$CGI::LIST_CONTEXT_WARN = 0;
+
+# add fex lib
+($FEXLIB) = $ENV{FEXLIB} =~ /(.+)/;
+die "$0: no $FEXLIB\n" unless -d $FEXLIB;
+
+# import from fex.pp
+our ($FEXHOME);
+our ($mdomain,$admin,$hostname,$sendmail,$akeydir,$skeydir,$docdir,$durl,$bcc);
+our ($nomail,$faillog);
+our $akey = '';
+
+# load common code, local config : $HOME/lib/fex.ph
+require "$FEXLIB/fex.pp" or die "$0: cannot load $FEXLIB/fex.pp - $!\n";
+
+my ($CASE,$ESAC);
+
+my $error = 'F*EX user config ERROR';
+my $head = "$ENV{SERVER_NAME} F*EX user config";
+
+my $fup = $durl;
+$fup =~ s:/fop:/fup:;
+
+chdir $spooldir or die "$spooldir - $!\n";
+
+my $user = my $id = my $nid = my $ssid = my $comment = '';
+my $notification = my $reminder = my $disclaimer = '';
+my $encryption = my $pubkey = my $mime = '';
+
+$akey = ''; # delete akey cookie
+
+my $qs = $ENV{QUERY_STRING};
+if ($qs) {
+  if ($qs =~ /akey=(\w+)/i) { $akey = $1 }
+  if ($qs =~ /ab=load/)     { $ab = 'load' }
+}
+
+# look for CGI POST parameters
+foreach my $v (param) {
+  my $vv = param($v);
+  debuglog("Param: $v=\"$vv\"");
+  if ($v =~ /^akey$/i) {
+    $akey = $1 if $vv =~ /^(\w+)$/;
+    next;
+  }
+  $CASE =
+    $v =~ /^user$/i            ? $user         = normalize_email($vv):
+    $v =~ /^subuser$/i         ? $subuser      = normalize_email($vv):
+    $v =~ /^otuser$/i          ? $otuser       = normalize_email($vv):
+    $v =~ /^notify$/i          ? $notify       = normalize_email($vv):
+    $v =~ /^notification$/i    ? $notification = checkchars('parameter',$vv):
+    $v =~ /^disclaimer$/i      ? $disclaimer   = $vv:
+    $v =~ /^encryption$/i      ? $encryption   = checkchars('parameter',$vv):
+    $v =~ /^pubkey$/i          ? $pubkey       = $vv:
+    $v =~ /^reminder$/i                ? $reminder     = checkchars('parameter',$vv):
+    $v =~ /^mime$/i            ? $mime         = checkchars('parameter',$vv):
+    $v =~ /^comment$/i         ? $comment      = decode_utf8(normalize($vv)):
+    $v =~ /^id$/i              ? $id           = checkchars('auth-ID',$vv):
+    $v =~ /^nid$/i             ? $nid          = checkchars('auth-ID',$vv):
+    $v =~ /^ssid$/i            ? $ssid         = $vv:
+    $v =~ /^group$/i           ? $group        = checkchars('group',$vv):
+    $v =~ /^ab$/i              ? $ab           = $vv:
+    $v =~ /^gm$/i              ? $gm           = $vv:
+    $v =~ /^show$/i            ? $tools        = checkchars('parameter',$vv):
+  $ESAC;
+}
+
+$group = lc $group if $group and $group ne 'NEW';
+$group = '' if $nomail;
+$user .= '@'.$mdomain if $mdomain and $user !~ /@/;
+
+$nomail = $comment if $comment =~ /NOMAIL|!#!/;
+
+if ($akey) {
+
+  # sid is not set with web browser
+  my $idf = "$akeydir/$akey/@";
+    
+  if (open $akey,'<',$idf and $id = getline($akey)) {
+    close $akey;
+    $idf =~ /(.*)\/\@/;
+    $user = readlink $1 
+      or http_die("internal server error: no $akey symlink $1");
+    $user =~ s:.*/::;
+    $user = untaint($user);
+    if ($akey ne md5_hex("$user:$id")) {
+      $user = $id = '';
+    }
+  }
+}
+
+&check_status($user) if $user;
+
+if ($user and $akey and $qs and $qs =~ /info=(.+?)&skey=(.+)/) {
+  $subuser = $1;
+  $skey = $2;
+  notify_subuser($user,$subuser,"$fup?skey=$skey",$comment);
+  http_header("200 OK");
+  print html_header($head);
+  pq(qq(
+    'An information e-mail has been sent to your subuser $subuser'
+    '<p><a href="javascript:history.back()">Go back</a>'
+    '</body></html>'
+  ));
+  exit;
+}
+
+
+if ($user and $id) {
+  if (-e "$user/\@CAPTIVE") { html_error($error,"captive user") }  
+  unless (open $idf,'<',"$user/@") {
+    faillog("user $from, id $id");
+    html_error($error,"wrong user or auth-ID");
+  }
+  $rid = getline($idf);
+  close $idf;
+  if ($id eq $rid) {
+    unless ($akey) {
+      $akey = untaint(md5_hex("$user:$id"));
+      unlink "$akeydir/$akey";
+      symlink "../$user","$akeydir/$akey";
+    }
+  } else {
+    faillog("user $from, id $id");
+    html_error($error,"wrong user or auth-ID");
+  }
+} else {
+  my $login = -x "$FEXHOME/login" ? 'login' : 'fup';
+  nvt_print(
+    "HTTP/1.1 302 Found",
+    "Location: $ENV{PROTO}://$ENV{HTTP_HOST}/$login",
+    'Expires: 0',
+    'Content-Length: 0',
+    ''
+  );
+  &reexec;
+}
+
+# empty POST? ==> back to foc
+if ($ENV{REQUEST_METHOD} eq 'POST' and not 
+    ($subuser or $notify or $nid or $ssid or $group or $ab or $gm or $tools
+     or $disclaimer or $encryption or $pubkey)) 
+{
+  nvt_print(
+    "HTTP/1.1 302 Found",
+    "Location: $ENV{PROTO}://$ENV{HTTP_HOST}/foc",
+    'Expires: 0',
+    'Content-Length: 0',
+    ''
+  );
+  &reexec;
+}
+
+unlink $faillog if $faillog;
+
+http_header("200 OK");
+print html_header($head);
+# foreach $v (keys %ENV) { print $v,' = "',$ENV{$v},"\"<br>\n" };
+
+if ($gm and not $group) {
+  pq(qq(
+    '<h2>ERROR: no group name specified</h2>'
+    '</body></html>'
+  ));
+  exit;
+}
+
+if ($tools) {
+  pq(qq(
+    'To use one of the following F*EX clients you must configure them after'
+    'download:'
+    '<p>'
+    '<table border=1>'
+    '  <tr><th align=left>F*EX server:<td><code>$ENV{PROTO}://$ENV{HTTP_HOST}</code></tr>'
+    '  <tr><th align=left>Proxy:<td>(your web proxy address, may be empty)</tr>'
+    '  <tr><th align=left>User:<td><code>$user</code></tr>'
+    '  <tr><th align=left>Auth-ID:<td><code>$id</code></tr>'
+    '</table>'
+  ));
+  if (open $tools,"$docdir/tools.html") {
+    while (<$tools>) {
+      while (/\$([\w_]+)\$/) {
+        my $var = $1;
+        my $env = $ENV{$var} || '';
+        s/\$$var\$/$env/g;
+      };
+      print;
+    }
+  }
+  exit;
+}
+
+if ($group) {
+  &handle_group;
+}
+
+# create one time upload key
+if ($subuser and $otuser) {
+  $otuser = $subuser;
+  if ($otuser !~ /^[^@]+@[\w.-]+[a-z]$/) {
+    pq(qq(
+      '<code>$otuser</code> is not a valid e-mail address'
+      '<p><a href="javascript:history.back()">Go back</a>'
+      '</body></html>'
+    ));
+    exit;
+  }
+  my $okey = randstring(8);
+  my $okeyd = "$user/\@OKEY";
+  mkdir $okeyd;
+  symlink $otuser,"$okeyd/$okey" 
+    or http_die("cannot create OKEY $okeyd/$okey : $!\n");
+  my $url = "$fup?to=$user&okey=$okey";
+  pq(qq(
+    'A one time upload URL for <code>$otuser</code> has been created:'
+    '<p>'
+    '<code>$url</code>'
+  ));
+  unless ($nomail) {
+    &notify_otuser($user,$otuser,$url,$comment);
+    pq(qq(
+      '<p>'
+      'and an information e-mail has been sent to this address.'
+      '<p>'
+    ));
+  }
+  pq(qq(
+    '<a href="/foc?akey=$akey">back to F*EX operation control</a>'
+    '</body></html>'
+  ));
+  exit;
+}
+
+# direct single subuser entry
+if ($subuser and not $otuser) {
+  if (-f "$subuser/@") {
+    pq(qq(
+      '<code>$subuser</code> is already a registered F*EX full user'
+      '<p><a href="javascript:history.back()">Go back</a>'
+      '</body></html>'
+    ));
+    exit;
+  }
+  if ($subuser !~ /^[^@]+@[\w.-]+[a-z]$/) {
+    pq(qq(
+      '<code>$subuser</code> is not a valid e-mail address'
+      '<p><a href="javascript:history.back()">Go back</a>'
+      '</body></html>'
+    ));
+    exit;
+  }
+  $skey = '';
+  if (open $idf,'<',"$user/\@SUBUSER") {
+    while (<$idf>) {
+      chomp;
+      if (/^\Q$subuser:/) {
+        $skey = md5_hex("$user:$_");
+        last;
+      }
+    }
+    close $idf;
+  }
+  if ($skey) {
+    my $url = "$fup?skey=$skey";
+    if ($nomail) {
+      pq(qq(
+        '$subuser is already your subuser and has access URL:'
+        '<p>'
+        '<code>$url</code>'
+      ));
+    } else {
+      pq(qq(
+        '<a href=\"/fuc?akey=$akey&info=$subuser&skey=$skey\">$subuser</a>'
+        'is already your subuser and has access URL:'
+        '<p>'
+        '<code>$url</code>'
+        '<p>'
+        "Click on the subuser's e-mail address link to send him an"
+        "information e-mail by the F*EX server.<p>"
+      ));
+    }
+  } else {
+    my $sid = randstring(8);
+    my $skey = mkskey($user,$subuser,$sid);
+    $url = "$fup?skey=$skey";
+    open $idf,'>>',"$user/\@SUBUSER" or die "$user/\@SUBUSER - $!\n";
+    print {$idf} "$subuser:$sid\n";
+    close $idf;
+    pq(qq(
+      'Your subuser upload URL is:'
+      '<p>'
+      '<code>$url</code>'
+    ));
+    unless ($nomail) {
+      &notify_subuser($user,$subuser,$url,$comment);
+      pq(qq(
+        '<p>'
+        'An information e-mail has been sent to $subuser'
+      ));
+    }
+  }
+  print "</body></html>\n";
+  exit;
+}
+
+# modify addressbook
+if ($user and $akey and defined $ab) {
+  if ($ab eq 'load') {
+    $ab = '';
+    if (open $ab,'<',"$user/\@ADDRESS_BOOK") {
+      undef $/;
+      $_ = <$ab>;
+      s/\s*$/\n/;
+      close $ab;
+      $ab = html_quote($_);
+    }
+    my $rows = ($ab =~ tr/\n//) + 5;
+    pq(qq(
+      '<h2>Edit address book</h2>'
+      '<table border=0>'
+      '  <tr align="left"><th>Entry:<th>alias<th>e-mail address<th># optional comment</tr>'
+      '  <tr align="left"><td>Example:<td><code>Framstag</code><td><code>framstag\@rus.uni-stuttgart.de</code><td><code># Ulli Horlacher</code></tr>'
+      '</table>'
+      '<form action="$ENV{SCRIPT_NAME}"'
+      '      method="post"'
+      '      accept-charset="UTF-8"'
+      '      enctype="multipart/form-data">'
+      '  <input type="hidden" name="akey" value="$akey">'
+      '  <textarea name="ab" cols="160" rows="$rows">$ab</textarea><br>'
+      '  <input type="submit" value="submit">'
+      '</form>'
+      '<p>'
+      'You may use these alias names as F*EX recipient addresses on '
+      '<a href="/fup?akey=$akey">fup</a>'
+      '<p>'
+      'Alternatively you can fex a file ADDRESS_BOOK to yourself '
+      '($user) containing your alias definitions.'
+      '<p>'
+      '<a href="/foc?akey=$akey">back to F*EX operation control</a>'
+      '</body></html>'
+    ));
+  } else {
+    $ab =~ s/[\r<>]//g;
+    $ab =~ s/\s*$/\n/;
+    
+    foreach (split(/\n/,$ab)) {
+      s/^\s+//;
+      s/\s+$//;
+      if (s/\s*(#.*)//) { $comment = $1 }
+      else              { $comment = '' }
+      next if /^\s*$/;
+      @options = ();
+      push @options,$1 if s/(autodelete=\w+)//i;
+      push @options,$1 if s/(keep=\d+)//i;
+      s/[,\s]+$//;
+      if (s/([\S]+)\s+(\S+)//) {
+        $alias = $1;
+        $address = $2;
+        $options = join(',',@options);
+        push @abt,"<tr><td>$alias<td>$address<td>$options<td>$comment</tr>\n";
+      } else {
+        push @badalias,$_;
+      }
+    }
+    
+    if (@badalias) {
+      print "<h2>ERROR: bad aliases:</h2>\n<ul>";
+      foreach my $ba (@badalias) { print "<li>$ba" }
+      pq(qq(
+        '</ul>'
+        '<p>'
+        'Not in format: <code>alias e-mail-address</code>'
+        '<p>'
+        '<a href="javascript:history.back()">Go back</a>'
+        '</body></html>'
+      ));
+      exit;
+    }
+    
+    open my $AB,'>',"$user/\@ADDRESS_BOOK" 
+      or http_die("cannot open $user/\@ADDRESS_BOOK - $!\n");
+    print {$AB} $ab;
+    close $AB;
+    pq(qq(
+      '<h2><a href ="/fuc?AB=load&akey=$akey">address book</a></h2>'
+      '<table border=1>'
+      '<tr><th>alias<th>e-mail address<th>options<th>comment</tr>'
+      '@abt'
+      '</table>'
+      '<p>'
+      '<a href="/foc?akey=$akey">back to F*EX operation control</a>'
+      '<p>'
+      '<a href="/fup?akey=$akey">back to fup (F*EX upload)</a>'
+      '</body></html>'
+    ));
+  }
+  exit;
+}
+
+if ($user and $notification eq 'detailed') {
+  unlink "$user/\@NOTIFICATION";
+  pq(qq(
+    '<h3>Notification e-mails now come in detailed format.<h3>'
+    '<p>'
+    '<a href="/foc?akey=$akey">back to F*EX operation control</a>'
+    '</body></html>'
+  ));
+  &reexec;
+}
+
+if ($user and $mime eq 'yes') {
+  open $mime,'>',"$user/\@MIME" or http_die("cannot write $user/\@MIME - $!\n");
+  close $mime;
+  pq(qq(
+    '<h3>Downloads will now be displayed (if possible).<h3>'
+    '<p>'
+    '<a href="/foc?akey=$akey">back to F*EX operation control</a>'
+    '</body></html>'
+  ));
+  &reexec;
+}
+
+if ($user and $mime eq 'no') {
+  unlink "$user/\@MIME";
+  pq(qq(
+    '<h3>Downloads will now be saved.<h3>'
+    '<p>'
+    '<a href="/foc?akey=$akey">back to F*EX operation control</a>'
+    '</body></html>'
+  ));
+  &reexec;
+}
+
+if ($user and $notification eq 'short') {
+  unlink "$user/\@NOTIFICATION";
+  symlink "short","$user/\@NOTIFICATION";
+  pq(qq(
+    '<h3>Notification e-mails now come in short format.<h3>'
+    '<p>'
+    '<a href="/foc?akey=$akey">back to F*EX operation control</a>'
+    '</body></html>'
+  ));
+  &reexec;
+}
+
+if ($user and $disclaimer) {
+  my $df = "$user/\@DISCLAIMER";
+  if ($disclaimer =~ /^[\s\"]*DEFAULT[\s\"]*$/i) {
+    unlink $df;
+    pq(qq(
+      '<h3>E-mail disclaimer reset to default.</h3>'
+      '<p>'
+      '<a href="/foc?akey=$akey">back to F*EX operation control</a>'
+      '</body></html>'
+    ));
+  } elsif ($disclaimer eq 'CHANGE') {
+    $disclaimer = slurp($df) || '';
+    $disclaimer =~ s/&/&amp;/g;
+    $disclaimer =~ s/</&lt;/g;
+    pq(qq(
+      '<form action="$ENV{SCRIPT_NAME}"'
+      '      method="post"'
+      '      accept-charset="UTF-8"'
+      '      enctype="multipart/form-data">'
+      '  <input type="hidden" name="akey" value="$akey">'
+      '  <p><hr><p>'
+      '  Disclaimer to be sent with download notification e-mail:<br>'
+      '  <textarea name="disclaimer" cols="80" rows="10">$disclaimer</textarea><br>'
+      '  <input type="submit" value="save">'
+      '  or <a href="$ENV{SCRIPT_NAME}?akey=$akey&disclaimer=DEFAULT">'
+      '  reset the disclaimer to default</a>.'
+      '</form>'
+      '</body></html>'
+    ));
+    exit;
+  } else {
+    $disclaimer =~ s/^\s+//;
+    $disclaimer =~ s/\s+$/\n/;
+    open $df,'>',$df or http_die("cannot write $df - $!\n");
+    print {$df} $disclaimer;
+    close $df;
+    $disclaimer =~ s/&/&amp;/g;
+    $disclaimer =~ s/</&lt;/g;
+    pq(qq(
+      '<h2>E-mail disclaimer changed to:</h2>'
+      '<pre>'
+      '$disclaimer'
+      '</pre>'
+      '<p>'
+      '<a href="/foc?akey=$akey">back to F*EX operation control</a>'
+      '</body></html>'
+    ));
+  }
+
+  &reexec;
+}
+
+if ($user and $pubkey) {
+  my $gf = "$user/\@GPG";
+  my $pk;
+  local $/;
+  local $_;
+  
+  open $gf,">$gf.pk" or http_die("cannot write $gf - $!\n");
+  print {$gf} <$pubkey>;
+  close $gf;
+  unlink $gf;
+  system "gpg --batch --no-default-keyring --keyring $gf --import".
+         "< $gf.pk >/dev/null 2>&1";
+  if (`gpg --batch <$gf 2>/dev/null` =~ /^pub\s.*<\Q$user\E>/sm) {
+    $pk = `gpg --batch <$gf 2>&1`;
+    $pk =~ s/&/&amp;/g;
+    $pk =~ s/</&lt;/g;
+    pq(qq(
+      '<h2>E-mails to you will be encrypted with the PGP/GPG key:</h2>'
+      '<pre>'
+      '$pk'
+      '</pre>'
+      '<p>'
+      '<a href="/foc?akey=$akey">back to F*EX operation control</a>'
+      '</body></html>'
+    ));
+    unlink "$gf.pk","$gf~";
+  } else {
+    $pk = `gpg --batch <$gf.pk 2>&1`;
+    $pk =~ s/&/&amp;/g;
+    $pk =~ s/</&lt;/g;
+    pq(qq(
+      '<h2>Your uploaded file does not contain a PGP/GPG public key for'
+      '    <code>$user</code></h2>'
+      '<pre>'
+      '$pk'
+      '</pre>'
+      '<p>'
+      '<a href="javascript:history.back()">back</a>'                                                     
+      '</body></html>'
+    ));
+  }
+  &reexec;
+}
+
+if ($user and $encryption) {
+  my $gf = "$user/\@GPG";
+  
+  unless(-s "$ENV{HOME}/.gnupg/pubring.gpg") {
+    html_error($error,"no GPG support activated");
+  }
+
+  if ($encryption eq 'DELETE') {
+    unlink $gf;
+    pq(qq(
+      '<h3>PGP/GPG key deleted.</h3>'
+      '<h3>E-mails to you will be sent not encrypted.</h3>'
+      '<p>'
+      '<a href="/foc?akey=$akey">back to F*EX operation control</a>'
+      '</body></html>'
+    ));
+  } elsif ($encryption eq 'CHANGE') {
+    pq(qq(
+      '<form action="$ENV{SCRIPT_NAME}"'
+      '      method="post"'
+      '      accept-charset="UTF-8"'
+      '      enctype="multipart/form-data">'
+      '  <input type="hidden" name="akey" value="$akey">'
+      '  Select your PGP/GPG public key file(*):<br>'
+      '  <input type="file" name="pubkey" size="80">'
+      '  <p>'
+      '  and <input type="submit" value="upload">'
+      '</form>'
+    ));
+    if (-f $gf) {
+      my $g = `gpg < $gf`;
+      $g =~ s/</&lt;/g;
+      pq(qq(
+        'or <a href="$ENV{SCRIPT_NAME}?akey=$akey&encryption=DELETE">'
+        'delete your already uploaded public key</a>:'
+        '<pre>'
+        '$g'
+        '</pre>'
+        '<p><hr><p>'
+        '(*) To extract and verify your GPG public key use:'
+        '<pre>'
+        'gpg -a --export $user > pubkey.gpg'
+        'gpg < pubkey.gpg'
+        '</pre>'
+      ));
+    }
+    print "</body></html>\n";
+    exit;
+  }
+
+  &reexec;
+}
+
+if ($user and $reminder eq 'yes') {
+  unlink "$user/\@REMINDER";
+  pq(qq(
+    '<h3>You will now get reminder notification e-mails.<h3>'
+    '<p>'
+    '<a href="/foc?akey=$akey">back to F*EX operation control</a>'
+    '</body></html>'
+  ));
+  &reexec;
+}
+
+if ($user and $reminder eq 'no') {
+  unlink "$user/\@REMINDER";
+  symlink "no","$user/\@REMINDER";
+  pq(qq(
+    '<h3>You will now get no reminder notification e-mails.<h3>'
+    '<p>'
+    '<a href="/foc?akey=$akey">back to F*EX operation control</a>'
+    '</body></html>'
+  ));
+  &reexec;
+}
+
+if ($nid) {
+  $nid =~ s/^\s+//;
+  $nid =~ s/\s+$//;
+  
+  $nid = randstring(6) if $nid eq '?';
+  
+  open $idf,'>',"$user/@" or die "$user/@ - $!\n";
+  print {$idf} $nid,"\n";
+  close $idf;
+  $akey = untaint(md5_hex("$user:$nid"));
+  unlink "$akeydir/$akey";
+  symlink "../$user","$akeydir/$akey";
+  
+  pq(qq(
+    '<h3>new auth-ID "<code>$nid</code>" for $user saved</h3>'
+    '<a href="/foc?akey=$akey">back to F*EX operation control</a>'
+    '</body></html>'
+  ));
+  exit;
+}
+
+# empty subuser list POST
+if (defined(param('ssid')) and $ssid =~ /^\s*$/) {
+  unlink "$user/\@SUBUSER";
+  pq(qq(
+    '<h2>All subusers deleted</h2>\n<ul>'
+    '<a href="/foc?akey=$akey">back to F*EX operation control</a>'
+    '</body></html>'
+  ));
+  exit;
+}
+
+# update sub-users
+if ($ssid) {
+  my ($subuser,$subid,$skey);
+  
+  # delete old skeys
+  if (open $idf,'<',"$user/\@SUBUSER") {
+    while (<$idf>) {
+      s/#.*//;
+      if (/(.+\@.+):(.+)/) {
+        ($subuser,$subid) = ($1,$2);
+        $skey = md5_hex("$user:$subuser:$subid");
+        unlink "$skeydir/$skey";
+        unlink "$subuser/\@MAINUSER/$user";
+      }
+    }
+    close $idf;
+  }
+
+  $ssid = strip($ssid);
+
+  # collect (new) subusers
+  foreach (split("\n",$ssid)) {
+    s/#.*//;
+    s/\s//g;
+    if (/(.+\@[\w.-]+)/) {
+      $subuser = lc $1;
+      push @badaddress,$subuser unless checkaddress($subuser);
+    }
+  }
+  
+  if (@badaddress) {
+    print "<h2>ERROR: bad addresses:</h2>\n<ul>";
+    foreach my $ba (@badaddress) { print "<li>$ba" }
+    pq(qq(
+      '</ul>'
+      '<a href="javascript:history.back()">Go back</a>'
+      '</body></html>'
+    ));
+    exit;
+  }
+  
+  if ($ssid =~ /\S\@\w/) {
+    open $idf,'>',"$user/\@SUBUSER" or die "$user/\@SUBUSER - $!\n";
+    print "Your subusers upload URLs are:<p><code>\n";
+    print "<table>\n";
+    foreach (split("\n",$ssid)) {
+      s/#.*//;
+      s/\s//g;
+      if (/(\S+\@[\w.-]+)/) {
+        $subuser = lc $1;
+        if (/:(.+)/) { $subid = $1 }
+        else         { $subid = randstring(8) }
+        print {$idf} "$subuser:$subid\n";
+        $skey = mkskey($user,$subuser,$subid);
+        print "  <tr><td><a href=\"/fuc?akey=$akey&info=$subuser&skey=$skey\">$subuser</a> :",
+              "<td>$fup?skey=$skey</tr>\n";
+      }
+    }
+    pq(qq(
+      "</table>\n</code><p>"
+      "You have to give these URLs to your subusers for fexing files to you."
+      "<br>"
+      "Or click on the subuser's e-mail address link to send him an"
+      "information e-mail by the F*EX server.<p>"
+    ));
+  }
+  print "<a href=\"/foc?akey=$akey\">back to F*EX operation control</a>\n";
+  print "</body></html>\n"; 
+  close $idf;
+  exit;
+}
+
+if (open my $subuser,'<',"$user/\@SUBUSER") {
+  local $/;
+  $ssid = <$subuser> || '';
+  close $subuser;
+}
+
+# display HTML form and request user data
+pq(qq(
+  '<form action="$ENV{SCRIPT_NAME}"'
+  '      method="post"'
+  '      accept-charset="UTF-8"'
+  '      enctype="multipart/form-data">'
+  '  <input type="hidden" name="akey" value="$akey">'
+));
+
+# pq(qq(
+#   '  <input type="hidden" name="user" value="$user">'
+#   '  <input type="hidden" name="id"   value="$id">'
+#   '  Your F*EX account: <b>$user:$id</b><p>'
+#   '  New auth-ID: <input type="text" name="nid" value="$id">'
+#   '  (Remember your auth-ID when you change it!)'
+# ));
+
+if (-s "$user/\@ALLOWED_RECIPIENTS") {
+  pq(qq(
+    '  <p>'
+    '  <input type="submit" value="submit">'
+    '</form>'
+    '</body></html>'
+  ));
+  exit;
+}
+
+if ($ssid) {
+  $ssid = html_quote(strip($ssid));
+}
+
+pq(qq(
+  '  <p><hr><p>'
+  '  Allow special senders (= subusers) to fex files to you:<br>'
+  '  <textarea name="ssid" cols="60" rows="10">$ssid</textarea><br>'
+  '  <input type="submit" value="save and show upload URLs">'
+  '</form>'
+  '<p>'
+  '<table border=0>'
+  '  <tr align="left"><td>This list has entries in format:<td>&lt;e-mail address>:&lt;encryption-ID><td></tr>'
+  '  <tr align="left"><td>Example:<td><code>framstag\@rus.uni-stuttgart.de:schwuppdiwupp</code><td></tr>'
+  '</table>'
+  '<p>'
+  'These special senders may fex files <em>only</em> to you!<br>'
+  'It is not necessary to add regular fex users to your list,'
+  'because they already can fex.'
+  '<p>'
+  'The encryption-ID is necessary to generate a unique upload URL for this subuser.<br>'
+  'If you omit the encryption-ID a random one will be used.'
+));
+
+unless ($nomail) {
+  pq(qq(
+    '<p><hr><p>'
+    '<h3 title="A F*EX group is similar to a mailing list, but for files">'
+    'Edit your F*EX groups:</h3>'
+    '<ul>'
+  ));
+
+  foreach $group (glob "$user/\@GROUP/*") {
+    if (-f $group and not -l $group and $group !~ /~$/) {
+      $group =~ s:.*/::;
+      print "  <li><a href=\"$ENV{SCRIPT_NAME}?akey=$akey&group=$group\">\@$group</a>\n";
+    }
+  }
+
+  pq(qq(
+    '  <li><a href="$ENV{SCRIPT_NAME}?akey=$akey&group=NEW"><em>new group</em></a>'
+    '</ul>'
+  ));
+}
+
+pq(qq(
+  '<p><hr><p>'
+  '<a href="/foc?akey=$akey">back to F*EX operation control</a>'
+  '</body></html>'
+));
+
+exit;
+
+
+sub strip {
+  local $_ = shift;
+  s/[ \t]+//g;
+  s/\s*[\r\n]+\s*/\n/g;
+  return $_;
+}
+
+sub notify_otuser {
+  my ($user,$otuser,$url,$comment) = @_;
+  my $server = $hostname || $mdomain;
+  my $sf;
+  
+  return if $nomail;
+  
+  $user .= '@'.$mdomain if $mdomain and $user !~ /@/;
+  $sf = $sender_from ? $sender_from : $user;
+  open my $mail,'|-',$sendmail,'-f',$sf,$otuser,$bcc
+    or http_die("cannot start sendmail - $!\n");
+  pq($mail,qq(
+    'From: $sf ($user via F*EX service $server)'
+    'To: $otuser'
+    'Subject: Your upload URL'
+    'X-Mailer: F*EX'
+    ''
+    'This is an automatically generated e-mail.'
+    ''
+    'Use'
+    ''
+    '$url'
+    ''
+    'to upload one file to $user'
+    ''
+    '$comment'
+    ''
+    'Questions? ==> F*EX admin: $admin'
+  ));
+  close $mail
+    or http_die("cannot send notification e-mail (sendmail error $!)\n");
+}
+
+
+sub notify_subuser {
+  my ($user,$subuser,$url,$comment) = @_;
+  my $server = $hostname || $mdomain;
+  my $sf;
+  
+  return if $nomail;
+  
+  $user .= '@'.$mdomain if $mdomain and $user !~ /@/;
+  $sf = $sender_from ? $sender_from : $user;
+  open my $mail,'|-',$sendmail,'-f',$sf,$subuser,$user,$bcc
+    or http_die("cannot start sendmail - $!\n");
+  pq($mail,qq(
+    'From: $sf ($user via F*EX service $server)'
+    'To: $subuser'
+    'Cc: $user'
+    'Subject: Your F*EX account on $server'
+    'X-Mailer: F*EX'
+    ''
+    'This is an automatically generated e-mail.'
+    ''
+    'A F*EX (File EXchange) account has been created for you on $server'
+    'Use'
+    ''
+    '$url'
+    ''
+    'to upload files to $user'
+    ''
+    'See http://$ENV{HTTP_HOST}/index.html for more information about F*EX.'
+    ''
+    '$comment'
+    ''
+    'Questions? ==> F*EX admin: $admin'
+  ));
+  close $mail
+    or http_die("cannot send notification e-mail (sendmail error $!)\n");
+}
+
+
+sub notify_groupmember {
+  my ($user,$gm,$group,$id,$url) = @_;
+  my $server = $hostname || $mdomain;
+  my $sf;
+  
+  $user .= '@'.$mdomain if $mdomain and $user !~ /@/;
+  $sf = $sender_from ? $sender_from : $user;
+  open my $mail,'|-',$sendmail,'-f',$sf,$gm,$user,$bcc
+    or http_die("cannot start sendmail - $!\n");
+  pq($mail,qq(
+    'From: $sf ($user via F*EX service $hostname)'
+    'To: $gm'
+    'Cc: $user'
+    'Subject: Your F*EX account on $server'
+    'X-Mailer: F*EX'
+    ''
+    'A F*EX (File EXchange) account has been created for you on $server'
+    'Use'
+    ''
+    '$url'
+    ''
+    'to upload files to F*EX group "$group"'
+    ''
+    'See http://$ENV{HTTP_HOST}/ for more information about F*EX.'
+    ''
+    'Questions? ==> F*EX admin: $admin'
+  ));
+  close $mail
+    or http_die("cannot send notification e-mail (sendmail error $!)\n");
+}
+
+
+sub mkskey {
+  my ($user,$subuser,$id) = @_;
+  my $skey = md5_hex("$user:$subuser:$id");
+  
+  open my $skf,'>',"$skeydir/$skey" or die "$skeydir/$skey - $!\n";
+  print {$skf} "from=$subuser\n",
+               "to=$user\n",
+               "id=$id\n";
+  close $skf or die "$skeydir/$skey - $!\n";
+  mkdirp("$subuser/\@MAINUSER");
+  symlink $skey,"$subuser/\@MAINUSER/$user";
+  return $skey;
+}
+
+
+sub mkgkey {
+  my ($user,$group,$gm,$id) = @_;
+  my $gkey = untaint(md5_hex("$user:$group:$gm:$id"));
+  
+  open my $gkf,'>',"$gkeydir/$gkey" or die "$gkeydir/$gkey - $!\n";
+  print {$gkf} "from=$gm\n",
+               "to=\@$group\n",
+               "user=$user\n",
+               "id=$id\n";
+  close $gkf or die "$gkeydir/$gkey - $!\n";
+  return $gkey;
+}
+
+
+sub handle_group {
+  my ($gf,$gd,$gl,$gid,$gkey);
+  
+  $group =~ s/^@+//;
+  $group =~ s:[/&<>]::g;
+
+  # $notify is group member
+  if ($notify) {
+    $gf = untaint("$notify/\@GROUP/$group");
+    unless ($_ = readlink $gf) {
+      pq(qq(
+        '<h2>ERROR: cannot read $gf - $!</h2>'
+        '</body></html>'
+      ));
+      exit;
+    }
+    if (m{([^/]+\@[\w.-]+)/}) {
+      $user = lc $1;
+    } else {
+      pq(qq(
+        '<h2>INTERNAL ERROR: groupfile = $gf</h2>'
+        '</body></html>'
+      ));
+      exit;
+    }
+    if (open $gf,'<',$gf) {
+      while (<$gf>) {
+        if (/\Q$notify\E:(\S+)/i) {
+          $gid = $1;
+          last;
+        }
+      }
+      close $gf;
+    } else {
+      pq(qq(
+        '<h2>ERROR: cannot open $gf - $!</h2>'
+        '</body></html>'
+      ));
+      exit;
+    }
+    unless ($gid) {
+      pq(qq(
+        '<h2>ERROR: $notify not found in $gf</h2>'
+        '</body></html>'
+      ));
+      exit;
+    }
+    $gkey = untaint(md5_hex("$user:$group:$notify:$gid"));
+    notify_groupmember(
+      $user,
+      $notify,
+      $group,
+      $gid,
+#      "$ENV{PROTO}://$ENV{HTTP_HOST}/fup?from=$notify&to=\@$group"
+      "$fup?gkey=$gkey"
+    );
+    pq(qq(
+      '<h2>Notification e-mail to $notify has been sent</h2>'
+      '<p><a href="javascript:history.back()">Go back</a>'
+      '</body></html>'
+    ));
+    exit;
+  }
+
+  $gf = untaint("$user/\@GROUP/$group");
+  
+  if (defined $gm) {
+    if ($gm =~ /\S/) {
+      foreach (split /\n/,$gm) {
+        s/#.*//;
+        s/\s//g;
+        next if /^\w+=./;
+        next if /^$/;
+        if (s/:.+//) {
+          if (/(.+@[\w\.-]+)/ and checkaddress($_)) {
+            push @gm,lc $1;
+          } else {
+            push @badaddress,$_;
+          }
+        } else {
+          push @badformat,$_;
+        }
+      }
+      if (@badformat) {
+        print "<h2>ERROR: lines not in format &lt;e-mail address>:&lt;encryption-ID></h2>\n<ul>";
+        foreach my $ba (@badformat) { print "<li>$ba" }
+        print "</ul>\n";
+      }
+      if (@badaddress) {
+        print "<h2>ERROR: bad addresses:</h2>\n<ul>";
+        foreach my $ba (@badaddress) { print "<li>$ba" }
+        print "</ul>\n";
+      }
+      if (@badformat or @badaddress) {   
+        pq(qq(
+          '<a href="javascript:history.back()">Go back</a>'
+          '</body></html>'
+        ));
+        exit;
+      }
+      $gd = "$user/\@GROUP";
+      unless (-d $gd or mkdir $gd,0700) {
+        print "<h2>ERROR: cannot create $gd - $!</h2>\n";
+        print "</body></html>\n";
+        exit;
+      }
+      if (-l $gf) {
+        if ($_ = readlink $gf and m{([^/]+\@[\w.-]+)/}) {
+          $user = $1;
+          pq(qq(
+            '<h2>ERROR: you are already in group \@$group owned by $user</h2>'
+            '<a href="javascript:history.back()">Go back</a>'
+            'and enter another group name'
+            '</body></html>'
+          ));
+        } else {
+          print "<h2>INTERNAL ERROR: $gf is a symlink. but not readable</h2>\n";
+          print "</body></html>\n";
+        }
+        exit;
+      }
+      # delete old gkeys
+      if (open $gf,'<',$gf) {
+        # delete old group links and gkeys
+        while (<$gf>) {
+          s/#.*//;
+          if (/(.+\@.+):(.+)/) {
+            $gkey = untaint(md5_hex("$user:$group:$1:$2"));
+            unlink "$gkeydir/$gkey";
+            unlink "$1/\@GROUP/$group" if -l "$1/\@GROUP/$group";
+          }
+        }
+        close $gf;
+      }
+      # write new group file and gkeys
+      if (open $gf,'>',$gf) {
+        $gm =~ s/[\r\n]+/\n/g;
+        foreach (split /\n/,$gm) {
+          print {$gf} "$_\n";
+          s/#.*//;
+          s/\s//g;
+          if (/^\s*([^\/]+):(.+)/) {
+            mkgkey(lc $user,$group,lc $1,$2);
+          }
+        }
+        close $gf;
+      } else {
+        print "<h2>ERROR: cannot write $gf - $!</h2>\n";
+        print "</body></html>\n";
+        exit;
+      }
+      if (@gm) {
+        foreach $gm (@gm) {
+          next if $gm eq $user;
+          unless (-d $gm or mkdir $gm,0700) {
+            print "<h2>ERROR: cannot create $gm - $!</h2>\n";
+            print "</body></html>\n";
+            exit;
+          }
+          $gd = "$gm/\@GROUP";
+          unless (-d $gd or mkdir $gd,0700) {
+            print "<h2>ERROR: cannot create $gd - $!</h2>\n";
+            print "</body></html>\n";
+            exit;
+          }
+          $gl = "$gm/\@GROUP/$group";
+          unless (-l $gl or symlink "../../$user/\@GROUP/$group",$gl) {
+            print "<h2>ERROR: cannot create $gl - $!</h2>\n";
+            print "</body></html>\n";
+            exit;
+          }
+        }
+        pq(qq(
+          '<h2>Group \@$group has members:</h2>'
+          '<ul>'
+        ));
+        foreach $gm (@gm) {
+          if ($gm ne $user) {
+            print "  <li><a href=\"$ENV{SCRIPT_NAME}?akey=$akey&group=$group&notify=$gm\">$gm</a>\n";
+          }
+        }
+        pq(qq(
+          '</ul>'
+          '(click address to send a notification e-mail to this user)'
+        ));
+      } else {
+        print "<h2>Group \@$group has no members</h2>\n";
+      }
+      pq(qq(
+        '<p>'
+        '<a href="/foc?akey=$akey">back to F*EX operation control</a>'
+      ));
+      print end_html();
+      exit;
+    } else {
+      # no group members -> delete group file
+      unlink $gf;
+    }
+  } else {
+    $gm = '';
+    pq(qq(
+      '<h2>Edit F*EX group</h2>'
+      'A F*EX group is similar to a mailing list, but for files:<br>'
+      'When a member fexes a file to this list, '
+      'then all other members will receive it.'
+      '<p>'
+      '<form action="$ENV{SCRIPT_NAME}"'
+      '      method="post"'
+      '      accept-charset="UTF-8"'
+      '      enctype="multipart/form-data">'
+      '  <input type="hidden" name="akey" value="$akey">'
+    ));
+    if ($group eq 'NEW') {
+      pq(qq(
+        '  <font color="red">'
+        '  New group name: <input type="text" name="group"> (You MUST fill out this field!)'
+        '  </font>'
+      ));
+    } else {
+      if (open $gf,'<',$gf) {
+        local $/;
+        $gm = <$gf>||'';
+      }
+      close $gf;
+      pq(qq(
+        '  <input type="hidden" name="group" value="$group">'
+        '  F*EX group <b>\@$group</b>:'
+      ));
+    }
+    my $rows = ($gm =~ tr/\n//) + 5;
+    pq(qq(
+      '  <br><textarea name="gm" cols="80" rows="$rows">$gm</textarea><br>'
+      '  <input type="submit" value="submit">'
+      '</form>'
+      '<p>'
+      '<table border=0>'
+      '  <tr align="left"><td>This list must have entries in format:<td>&lt;e-mail address>:&lt;encryption-ID><td></tr>'
+      '  <tr align="left"><td>Example:<td><code>framstag\@rus.uni-stuttgart.de:schwuppdiwupp</code><td></tr>'
+      '</table>'
+      '<p>'
+      'The encryption-ID is necessary to generate a unique upload URL for this subuser.'
+      'You can name any existing e-mail address.'
+    ));
+    if (open my $ab,'<',"$user/\@ADDRESS_BOOK") {
+      pq(qq(
+        "<p><hr><p>"
+        "<h3>Your address book:</h3>"
+        "<pre>"
+      ));
+      while (<$ab>) {
+        s/#.*//;
+        print "$1\n" if /([\S]+\@[\S]+)/;
+      }
+      close $ab;
+      print "</pre>\n";
+    }
+    print "</body></html>\n";
+    exit;
+  }
+}
diff --git a/cgi-bin/fup b/cgi-bin/fup
new file mode 100755 (executable)
index 0000000..d43cda0
--- /dev/null
@@ -0,0 +1,3038 @@
+#!/usr/bin/perl -wT
+
+# F*EX CGI for upload
+#
+# Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
+#
+# Contribs:
+#      Sebastian Zaiser <szcode@arcor.de> (upload status)
+#
+
+use Encode;
+use Fcntl              qw':flock :seek :mode';
+use IO::Handle;
+use Digest::MD5                qw'md5_hex';
+use CGI::Carp          qw'fatalsToBrowser';
+use Cwd                        qw'abs_path';
+
+use constant DS => 60*60*24;
+use constant M  => 1024*1024;
+
+# add fex lib
+die "$0: no \$FEXLIB\n" unless $ENV{FEXLIB};
+(our $FEXLIB) = $ENV{FEXLIB} =~ /(.+)/;
+die "$0: no $FEXLIB\n" unless -d $FEXLIB;
+
+$| = 1;
+
+our $debug;
+our $ndata = 0;
+our $error = 'F*EX upload ERROR';
+our $head = "$ENV{SERVER_NAME} F*EX upload";
+our $autodelete = 'YES';
+our $locale;
+
+# import from fex.ph
+our (@locales,@throttle,$bcc,$keep_max,$nomail,$nostore,$overwrite);
+our (@local_domains,@local_rdomains,@local_hosts,@local_rhosts,);
+our (@registration_hosts,@demo,@file_link_dirs);
+
+# import from fex.pp
+our ($FEXHOME);
+our ($spooldir,$durl,$tmpdir,$logdir,$docdir,$hostname,$admin,$fra);
+our ($keep_default,$recipient_quota,$sender_quota);
+our ($sendmail,$mdomain,$fop_auth,$mail_auth,$faillog);
+our ($dkeydir,$ukeydir,$akeydir,$skeydir,$gkeydir,$xkeydir);
+our $akey = '';
+our $dkey = '';
+our $skey = '';
+our $gkey = '';
+
+our $seek = 0;         # already sent bytes (from previous upload)
+our $filesize = 0;     # total file size
+our $fpsize = 0;       # file part size (MIME-part)
+
+my $data;
+my $boundary;
+my $rb = 0;            # read bytes, totally
+my $rid = '';          # real ID
+my @header;            # HTTP entity header
+my $fileid;            # file ID
+my $captive;
+my $muser;             # main user fur sub or group user
+  
+# load common code, local config: $FEXLIB/fex.ph
+require "$FEXLIB/fex.pp" or die "$0: cannot load $FEXLIB/fex.pp - $!\n";
+
+# load fup local config
+our ($info_1,$info_2,$info_login);
+
+$locale = $ENV{LOCALE} || 'english';
+foreach my $pl (
+  "/var/lib/fex/locale/$locale/lib/fup.pl", 
+  "$FEXLIB/fup.pl",
+) {
+  if (-f $pl) {
+    require $pl or die "$0: cannot load $FEXLIB/fup.pl - $!\n";
+    last;
+  }
+}
+
+&check_camel unless $sid;
+
+chdir $spooldir or http_die("$spooldir - $!\n");
+
+my $log = "$logdir/fup.log";
+
+my $http_client = $ENV{HTTP_USER_AGENT} || '';
+my $cl = $ENV{X_CONTENT_LENGTH} || $ENV{CONTENT_LENGTH} || 0;
+
+$fra .= '/'.$ENV{HTTP_X_FORWARDED_FOR} if $ENV{HTTP_X_FORWARDED_FOR};
+
+$from = $to = $id = $file = $fkey = $comment = $command = $bwlimit = '';
+$filename = $okey = $addto = $replyto = $submit = '';
+@to = ();
+$data = '';
+$locale = untaint($ENV{LOCALE}||'');
+
+my $ra = $ENV{REMOTE_ADDR}||0;
+if (@upload_hosts and not ipin($ra,@upload_hosts)) {
+  http_die(
+    "Uploads from your host ($ra) are not allowed.",
+    "Contact $ENV{SERVER_ADMIN} for details."
+  );
+}
+
+&check_maint;
+
+&parse_request; # showstatus will not come back!
+
+if ($addto) {
+  my %to;
+  foreach $to (@to) { $to{$to} = 1 }
+  push @to,$addto unless $to{$addto};
+  if ($submit and @to == 1) { $addto = '' }
+}
+
+$to = join(',',@to);
+
+$uid = randstring(8) unless $uid; # upload ID
+
+# user requests for forgotten ID
+$id_forgotten = $id if $id =~ /^"?\?"?$/;
+if ($from and $id_forgotten and $mail_authid and not ($fop_auth or $nomail)) {
+  &check_status($from);
+  &id_forgotten;
+  exit;
+}
+
+# public recipients? (needs no auth-ID for sender)
+if ($to and $id and $id eq 'PUBLIC' and @public_recipients) {
+  
+  unless ($from) {
+    http_die("missing sender e-mail address");
+  }
+  # must use $param{FROM} for checking because $from is expanded with $mdomain
+  unless (checkaddress(despace($param{FROM}))) {
+    http_die("<code>$param{FROM}</code> is not a valid e-mail address");
+  }
+  foreach my $to (@to) {
+    unless (grep /^\Q$to\E$/i,@public_recipients) {
+      http_die("<code>$to</code> is not a valid recipient");
+    }
+  }
+  $restricted = $public = $rid = $id;
+}
+
+# anonymous upload from enabled IP?
+if ($from =~ /^anonymous@/ and 
+    @anonymous_upload and ipin($ra,@anonymous_upload)) {
+  $id = $rid = $anonymous = 'anonymous';
+  if ($to =~ /^anonymous/) {
+    @to = ($to);
+    $autodelete{$to} = $autodelete = 'NO'; 
+  }
+  $nomail = $anonymous;
+}
+
+$comment = 'NOMAIL' if $nomail and not $comment;
+
+# one time token
+if ($okey) {
+  $to = "@to" or http_die("no recipient specified");
+  $from = readlink "$to/\@OKEY/$okey" 
+    or http_die("no upload key \"<code>$okey</code>\" - ".
+                "request another one from <code>$to</code>");
+  $from = untaint($from);
+}
+
+&check_status($from) if $from;
+
+# look for regular sender ID
+if ($id and $from and not ($public or $anonymous or $okey)) {
+  if (open $from,'<',"$from/\@") {
+    # chomp($rid = <$from> || '');
+    $rid = getline($from);
+    close $from;
+    $rid = sidhash($rid,$id);
+    # set time mark for successfull access
+    if ($id eq $rid) {
+      my $time = untaint(time);
+      utime $time,$time,$from;
+    }
+  } else {
+    my $error = $!;
+    # if recipient (to) is specified, we have to look for subusers later, too
+    unless (@to) {
+      fuplog("ERROR: $spooldir/$from/\@ $error");
+      debuglog("cannot open $spooldir/$from/\@ : $error");
+      faillog("user $from, id $id");
+      http_die("wrong user or auth-ID");
+    }
+  }
+}
+
+# check regular ID
+if ($from and $id and not ($gkey or $skey or $public or $okey)) {
+  if ($rid and $rid eq $id) {
+    # set akey link for HTTP sessions
+    # (need original id for consistant non-moving akey)
+    if (-d $akeydir and open $idf,'<',"$from/@" and my $id = getline($idf)) {
+      $akey = untaint(md5_hex("$from:$id"));
+      mksymlink("$akeydir/$akey","../$from");
+      # show URL from fexsend
+      if ($from eq $to and $comment eq '*') {
+        mksymlink("$akeydir/$akey","../$from");
+      }
+    }
+    $captive = -e "$from/\@CAPTIVE";
+  } else {
+    fuplog("ERROR: wrong auth-ID for $from");
+    debuglog("id sent by user $from=$id, real id=$rid");
+    faillog("user $from, id $id");
+    http_die("Wrong user or auth-ID");
+  }
+}
+
+# forward a copy of a file to another recipient
+if ($akey and $dkey and $command eq 'FORWARD') {
+  my $file = untaint(readlink "$dkeydir/$dkey"||'');
+  http_die("unknown dkey <code>$dkey></code>") unless $file;
+  $file =~ s:^\.\./::;
+  forward($file);
+  exit;
+}
+
+# modify file parameter
+if ($akey and $dkey and $command eq 'MODIFY') {
+  my $file = untaint(readlink "$dkeydir/$dkey"||'');
+  http_die("unknown dkey <code>$dkey</code>") unless $file;
+  $file =~ s:^\.\./::;
+  modify($file);
+  exit;
+}
+
+# copy file from incoming to outgoing spool
+if ($akey and $dkey and $command eq 'COPY') {
+  unless ($file = readlink "$dkeydir/$dkey") {
+    http_die("No such file with DKEY=$dkey");
+  }
+  if ($file =~ m:../(.+)/(.+)/(.+):) {
+    ($to,$from,$file) = ($1,$2,$3);
+  } else {
+    http_die("Bad DKEY $dkey -> $file");
+  }
+  unless (-f "$to/$from/$file/data") {
+    http_die("File not found");
+  }
+  if (-e "$to/$to/$file/data") {
+    http_die("File $file already exists in your outgoing spool") 
+      if (readlink("$to/$to/$file/id")||$to) ne 
+         (readlink("$to/$from/$file/id")||$from);
+  } else {
+    mkdirp("$to/$to/$file");
+    link "$to/$from/$file/data","$to/$to/$file/data" 
+      or http_die("cannot link to $to/$to/$file/data - $!\n");
+    copy("$to/$from/$file/filename","$to/$to/$file/filename");
+    copy("$to/$from/$file/id","$to/$to/$file/id");
+    open $file,'>',"$to/$to/$file/notify";
+    close $file;
+    open $file,'>',"$to/$to/$file/download";
+    print {$file} "$to\n";
+    close $file;
+    $dkey = randstring(8);
+    unlink "$to/$to/$file/dkey","$to/$to/$file/keep","$dkeydir/$dkey";
+    symlink "../$to/$to/$file","$dkeydir/$dkey";
+    symlink $dkey,"$to/$to/$file/dkey";
+  }
+  nvt_print(
+    "HTTP/1.1 302 Found",
+    "Location: $ENV{PROTO}://$ENV{HTTP_HOST}/rup?akey=$akey&oto=$to&file=$file",
+    'Content-Length: 0',
+    ''
+  );
+  &reexec;
+}
+
+# delete file without download
+if ($akey and $dkey and $command eq 'DELETE') {
+  $del = untaint(readlink "$dkeydir/$dkey"||'');
+  http_die("unknown dkey <code>$dkey</code>") unless $del;
+  $del =~ s:^\.\./::;
+  $filename = filename($del);
+  if (unlink("$del/data") or unlink("$del/upload")) {
+    if (open F,'>',"$del/error") {
+      printf F "%s has been deleted by %s at %s\n",
+               $filename,$ENV{REMOTE_ADDR},isodate(time);
+      close F;
+    }
+    # http_header('200 OK');
+    # print html_header($head);
+    # print "<h3>$filename deleted</h3>\n";
+    nvt_print(
+      "HTTP/1.1 302 Found",
+      "Location: $ENV{PROTO}://$ENV{HTTP_HOST}/fup?akey=$akey&command=LISTRECEIVED",
+      'Content-Length: 0',
+      ""
+    );
+    &reexec;
+  } else { 
+    my $s = $!;
+    http_header('404 Not Found');
+    print html_header($head);
+    print "<h3>$filename not deleted ($s)</h3>\n";
+    print "<a href=\"/fup?akey=$akey&command=LISTRECEIVED\">continue</a>\n" if $akey;
+    print "</body></html>\n";
+  }
+  exit;
+}
+
+# special commands
+if (($from and $id and $rid eq $id or $gkey or $skey) and $command) {
+                                                                     
+  if ($command eq 'CHECKQUOTA') {
+    http_die("illegal command \"$command\"") if $public or $anonymous;
+    nvt_print('HTTP/1.1 204 OK');
+    # nvt_print("X-SID: $ENV{SID}") if $ENV{SID};
+    ($quota,$du) = check_sender_quota($muser||$from);
+    nvt_print("X-Sender-Quota: $quota $du")    if $quota;
+    ($quota,$du) = check_recipient_quota($muser||$from);
+    nvt_print("X-Recipient-Quota: $quota $du") if $quota;
+    nvt_print('');
+    exit;
+  }
+
+  if ($command eq 'LISTSETTINGS') {
+    http_die("illegal command \"$command\"") if $public or $anonymous;
+    nvt_print('HTTP/1.1 204 OK');
+    # nvt_print("X-SID: $ENV{SID}") if $ENV{SID};
+    ($quota,$du) = check_sender_quota($muser||$from);
+    nvt_print("X-Sender-Quota: $quota $du")    if $quota;
+    ($quota,$du) = check_recipient_quota($muser||$from);
+    nvt_print("X-Recipient-Quota: $quota $du") if $quota;
+    $autodelete = lc(readlink "$from/\@AUTODELETE" || $autodelete);
+    nvt_print("X-Autodelete: $autodelete");
+    $keep = readlink "$from/\@KEEP" || $keep;
+    nvt_print("X-Default-Keep: $keep");
+    $locale = readlink "$from/\@LOCALE" || $default_locale || 'english';
+    nvt_print("X-Default-Locale: $locale");
+    $mime = -e "$from/\@MIME" ? 'yes' : 'no';
+    nvt_print("X-MIME: $mime");
+    nvt_print('');
+    exit;
+  }
+
+  if ($command eq 'RENOTIFY') {
+    http_die("illegal command \"$command\"") if $public or $anonymous;
+    my $nfile = '';
+    if ($dkey) {
+      # resend notification e-mail
+      $file = readlink("$dkeydir/$dkey")
+        or html_error($error,"illegal DKEY $dkey");
+      $file =~ s:^../::;
+      $file = untaint($file);
+      unlink "$file/download"; # re-allow download from any ip address
+      notify_locale($dkey,'new');
+      http_header(
+        '200 OK',
+        "X-Notify: $file",
+      );
+      $nfile = $file;
+    } else {
+      http_header('200 OK');
+    }
+    print html_header($head);
+    # list sent files
+    print "<h3>Files from $from, ",
+          "click on the file name to resend a notification e-mail:</h3>\n",
+          "<pre>\n";
+    foreach $file (glob "*/$from/*") {
+      next if $file =~ m:/STDFEX$:;
+      next if $file =~ m:(.+?)/: and -l $1;
+      $size = -s "$file/data";
+      next unless $size;
+      $size = int($size/M+0.5);
+      $filename = $comment = '';
+      my $rto = $file;
+      $rto =~ s:/.*::;
+      if ($dkey = readlink "$file/dkey") {
+        if ($rto ne $to) {
+          $to = $rto;
+          print "\nto $to :\n";
+        }
+        if (open $file,'<',"$file/filename") {
+          $filename = <$file>;
+          close $file;
+        }
+        if ($filename and length $filename) { 
+          $filename = html_quote($filename);
+        } else { 
+          $filename = '???';
+        }
+        if (open $file,'<',"$file/comment") {
+          $comment = untaint(html_quote(getline($file)));
+          close $file;
+        }
+        my $rkeep = untaint(readlink "$file/keep"||$keep_default)
+                    - int((time-mtime("$file/filename"))/DS);
+        if ($comment =~ /NOMAIL/ or 
+           (readlink "$to/\@NOTIFICATION"||'') =~ /^no/i) {
+          printf "%8s MB [%s d] %s/%s/%s\n",
+                 $size,
+                 $rkeep,
+                 $durl,
+                 $dkey,
+                 urlencode(basename($file));
+        } else {
+          printf "%8s MB [%s d] <a href=\"%s\">%s</a>%s %s\n",
+                 $size,
+                 $rkeep,
+                 untaint("/fup?akey=$akey&dkey=$dkey&command=RENOTIFY"),
+                 $filename,
+                 $comment ? qq' "$comment"' : '',
+                 $file eq $nfile ? 
+                   " &rarr; notification e-mail has been resent" :
+                   "";
+        }
+      }
+    }
+    pq(qq(
+      '</pre>'
+      '<p><a href="/foc?akey=$akey">back to F*EX operation control</a>'
+      '</body></html>'
+    ));
+    exit;
+  } 
+
+  if ($command =~ /^LIST(RECEIVED)?$/) {
+    http_die("illegal command \"$command\"") if $public or $anonymous;
+    # list sent files
+    if ($to and $param{'TO'} eq '*') {
+      http_header('200 OK');
+      print html_header($head);
+#            "(Format: [size] [rest keep time] [filename] [comment])<p>\n",
+      print "<h3>Files from $from:</h3>\n",
+            "<pre>\n";
+      foreach $file (glob "*/$from/*") {
+        next if $file =~ m:/STDFEX$:;
+        next if $file =~ m:(.+?)/: and -l $1;
+        $size = -s "$file/data";
+        next unless $size;
+        $size = int($size/M+0.5);
+        $filename = $comment = '';
+        my $rto = $file;
+        $rto =~ s:/.*::;
+        if ($dkey = readlink "$file/dkey") {
+          if ($rto ne $to) {
+            $to = $rto;
+            print "\nto $to :\n";
+          }
+          if (open $file,'<',"$file/filename") {
+            $filename = <$file>;
+            close $file;
+          }
+          if ($filename and length $filename) { 
+            $filename = html_quote($filename);
+          } else { 
+            $filename = '???';
+          }
+          if (open $file,'<',"$file/comment") {
+            $comment = untaint(html_quote(getline($file)));
+            close $file;
+          }
+          my $rkeep = untaint(readlink "$file/keep"||$keep_default) 
+                      - int((time-mtime("$file/filename"))/DS);
+          printf "%8s MB [%s d] <a href=\"%s\">%s</a>%s\n",
+                 $size,
+                 $rkeep,
+                 untaint("/fup?akey=$akey&dkey=$dkey&command=FORWARD"),
+                 $filename,
+                 $comment?qq( "$comment"):'';
+        }
+      }
+      pq(qq(
+        '</pre>'
+        '<p><a href="javascript:history.back()">back to F*EX operation control</a>'
+        '</body></html>'
+      ));
+    } 
+    # list received files
+    else {
+      $to = $from;
+      http_header('200 OK');
+      print html_header($head);
+#            "(Format: [size] [rest keep time] [URL] [comment])<p>\n",
+      print "<h3>Files for $to (*):</h3>\n",
+            "<pre>\n";
+      foreach $from (glob "$to/*") {
+        next if $from =~ /[A-Z]/;
+        $from =~ s:.*/::;
+        $url = '';
+        foreach $file (glob "$to/$from/*") {
+          next if $file =~ /\/STDFEX$/;
+          $filename = $comment = '';
+          $size = -s "$file/data";
+          next unless $size;
+          $size = int($size/M+0.5);
+          if ($dkey = readlink "$file/dkey") {
+            print "\nfrom $from :\n" unless $url;
+            $file =~ m:.*/(.+):;
+            $url = "$durl/$dkey/$1";
+            unless (-l "$dkeydir/$dkey") {
+              symlink untaint("../$file"),untaint("$dkeydir/$dkey");
+            }
+            if (open $file,'<',"$file/filename") {
+              $filename = <$file>;
+              close $file;
+            }
+            if ($filename and length $filename) { 
+              $filename = html_quote($filename);
+            } else { 
+              $filename = '???';
+            }
+            if (open $file,'<',"$file/comment") {
+              $comment = untaint(html_quote(getline($file)));
+              $comment = ' "'.$comment.'"';
+              close $file;
+            }
+            my $rkeep = untaint(readlink "$file/keep"||$keep_default) 
+                        - int((time-mtime("$file/filename"))/DS);
+            printf "[<a href=\"/fup?akey=%s&dkey=%s&command=DELETE\">delete</a>] ",
+                   $akey,$dkey;
+            printf "[<a href=\"/fup?akey=%s&dkey=%s&command=COPY\">forward</a>] ",
+                   $akey,$dkey;
+            printf "%8s MB (%s d) <a href=\"%s\">%s</a>%s\n",
+                   $size,$rkeep,$url,$filename,$comment;
+          }
+        }
+      }
+      pq(qq(
+        '</pre>'
+        '(*) Files for other e-mail addresses you own will not be listed here!<p>'
+        '<a href="/foc?akey=$akey">back to F*EX operation control</a>'
+        '</body></html>'
+      ));
+    }
+    exit;
+  } 
+      
+  if ($command eq 'LISTSENT') {
+    http_die("illegal command \"$command\"") if $public or $anonymous;
+    # show download URLs 
+    http_header('200 OK');
+    print html_header($head);
+    print "<h2>Download URLs of files you have sent\n";
+    foreach $to (glob "*/$from") {
+      if (@files = glob "$to/*/data") {
+        $to =~ s:/.*::;
+        print "<h3>to <code>$to</code> :</h3>\n";
+        print "<pre>\n";
+        foreach $file (@files) {
+          $file =~ s:/data::;
+          next if $file =~ /\/STDFEX$/;
+          $dkey = readlink "$file/dkey" or next;
+          $file =~ s:.*/::;
+          print "$ENV{PROTO}://$ENV{HTTP_HOST}/fop/$dkey/$file\n";
+        }
+        print "</pre>\n";
+      }
+    }
+    pq(qq(
+      '</pre>'
+      '<p><a href="javascript:history.back()">back to F*EX operation control</a>'
+      '</body></html>'
+    ));
+    exit;
+  }
+      
+  if ($command eq 'FOPLOG') {
+    http_die("illegal command \"$command\"") if $public or $anonymous;
+    if (open my $log,"$logdir/fop.log") {
+      http_header('200 OK');
+      while (<$log>) {
+        next if /\/STDFEX\s/;
+        if (s:^([^/]+)/$from/:$1 :) {
+          if (s:(\d+)/(\d+)$:$1: and $1 and $1 == $2) {
+            s/ \[[\d_]+\]//;
+            print;
+          }
+        }
+      }
+    }
+    exit;
+  }
+  
+  if ($command eq 'RECEIVEDLOG') {
+    http_die("illegal command \"$command\"") if $public or $anonymous;
+    if (open my $fuplog,"$logdir/fup.log") {
+      http_header('200 OK');
+      while (<$fuplog>) {
+        next if /\sSTDFEX\s/;
+        if (/\d+$/) { 
+          my @F = split;
+          if ($F[5] eq $to) {
+            s/ \[[\d_]+\]//;
+            print;
+          }
+        }
+      }
+    }
+    exit;
+  }
+
+  if ($command eq 'SENDLOG') {
+    http_die("illegal command \"$command\"") if $public or $anonymous;
+    if (open my $fuplog,"$logdir/fup.log") {
+      http_header('200 OK');
+      while (<$fuplog>) {
+        next if /\sSTDFEX\s/;
+        if (/(\S+\@\S+)/ and $1 eq $from) { 
+          s/ \[[\d_]+\]//;
+          print;
+        }
+      }
+    }
+    exit;
+  }
+
+  if (@to and $command eq 'CHECKRECIPIENT') {
+    http_die("illegal command \"$command\"") if $public or $anonymous;
+    check_rr($from,@to);
+    nvt_print('HTTP/1.1 204 OK');
+    nvt_print("X-SID: $sid") if $sid;
+    foreach my $to (@group?@group:@to) {
+      # my $options = sprintf "(autodelete=%s,keep=%s,locale=%s)",
+      # readlink "$to/\@LOCALE"||$locale||$locale{$to}||$default_locale;
+      my $options = sprintf "(autodelete=%s,keep=%s,locale=%s,notification=%s)",
+        $autodelete{$to}||$autodelete,
+        $keep{$to}||$keep_default,
+        readlink("$to/\@LOCALE")||$default_locale,
+        readlink("$to/\@NOTIFICATION")||'full';
+      nvt_print("X-Recipient: $to $options");
+    }
+    nvt_print('');
+    # control back to fexsrv for further HTTP handling
+    &reexec;
+  }
+
+  if ($file and @to and $command eq 'DELETE') {
+    http_die("illegal command \"$command\"") if $public or $anonymous;
+    foreach (@group?@group:@to) {
+      my $to = $_;
+      $to =~ s/:\w+=.*//; # remove options from address
+      $del = "$to/$from/$fkey";
+      # swap to and from for special senders, see fup storage swap!
+      $del = "$from/$to/$fkey" if $from =~ /^(fexmail|anonymous)/;
+
+      $del =~ s:^/+::;
+      if ($del =~ /\/\./) {
+        http_die("illegal parameter <code>$del</code>");
+      }
+      $del = untaint($del);
+      
+      if (unlink("$del/data") or unlink("$del/upload")) {
+        if (open F,'>',"$del/error") {
+          print F "$file has been deleted by $from\n";
+          close F;
+        }
+        http_header('200 OK',"X-File: $del");
+        print html_header($head);
+        print "<h3>$file deleted</h3>\n";
+      } else { 
+        http_header("404 Not Found");
+        print html_header($head);
+        print "<h3>$file not deleted</h3>\n";
+      }
+      if ($akey) {
+        printf "<a href=\"/fup?akey=%s&to=%s&command=LISTRECEIVED\">continue</a>\n",
+               $akey,$to;
+      }
+      print "</body></html>\n";
+    }
+    exit;
+  }
+
+}
+
+# ip restrictions
+if ($from and $id and $rid eq $id and open my $ipr,"$from/\@UPLOAD_HOSTS") {
+  my @hosts;
+  while (<$ipr>) {
+    chomp;
+    s/#.*//;
+    push @hosts,$_ if /\w/;
+  }
+  close $ipr;
+  unless (@hosts and ipin($ra,@hosts)) {
+    http_die("<code>$from</code> is not allowed to upload from IP $ra");
+  }
+}
+
+# quotas 
+if ($from and $id and $rid eq $id and @to and not $flink and not $seek) {
+  my ($quota,$du);
+  
+  # check sender quota
+  ($quota,$du) = check_sender_quota($muser||$from);
+  if ($quota and $du+$cl/M > $quota) {
+    http_die("you are overquota");
+  }
+  
+  # check recipient quota
+  foreach my $to (@to) {
+    ($quota,$du) = check_recipient_quota($to);
+    if ($quota and $du+$cl/M > $quota) {
+      http_die("$to cannot receive files: is overquota");
+    }
+  }
+
+}
+
+# check recipients restriction
+if ($id and $id eq $rid and $from and @to and not $public) {
+  check_rr($from,@to);
+}
+
+# on secure mode "fop authorization" also check if recipient(s) exists
+# (= has a F*EX ID)
+if (not $addto and $fop_auth and $id and $id eq $rid and $from and @to) {
+  my ($to_reg,$idf,$subuser);
+  foreach (@to) {
+    my $to = $_;
+    $to =~ s/:\w+=.*//; # remove options from address
+    $to_reg = 0;
+    # full user?
+    if (open $idf,'<',"$to/@") {
+      $to_reg = getline($idf);
+      close $idf;
+    } 
+    # sub user?
+    elsif (open $idf,'<',"$from/\@SUBUSER") {
+      while (<$idf>) {
+        s/#.*//;
+        next unless /:/;
+        chomp;
+        ($subuser) = split ':';
+        if ($subuser eq $to or $subuser eq '*@*'
+            or $subuser =~ /^\*\@(.+)/ and $to =~ /\@\Q$1\E$/i
+            or $subuser =~ /(.+)\@\*$/ and $to =~ /^\Q$1\E\@/i) {
+          $to_reg = $_;
+          last;
+        }
+      }
+      close $idf;
+    }
+    unless ($to_reg) {
+      http_die("recipient <code>$to</code> is not a registered F*EX full or sub user");
+    }
+  }
+}
+
+$to = join(',',@to);
+  
+if ($to =~ /^@(.+)/) {
+  if ($nomail) {
+    http_die("server runs in NOMAIL mode - groups ($to) are not allowed");
+  }
+  my $gf = "$from/\@GROUP/$1";
+  if (open $gf,'<',$gf) {
+    while (<$gf>) {
+      s/#.*//;
+      push @group,$1 if /(.+@.+):/;
+    }
+  }
+  close $gf;
+  $group = $to;
+}
+
+if ($redirect) {
+  nvt_print(
+    "HTTP/1.1 302 Found",
+    "Location: $ENV{PROTO}://$ENV{HTTP_HOST}/$redirect?akey=$akey",
+    'Content-Length: 0',
+    ""
+  );
+  &reexec;
+}
+
+if ($from and $id and $id eq $rid and $faillog) {
+  unlink $faillog;
+}
+
+# display HTML form and request user data
+unless ($file) {
+
+  if ($test) { $cgi = $test } 
+  else       { $cgi = $ENV{SCRIPT_NAME} }
+  $cgi = 'fup';
+  
+  # delete old cookies on logout referer
+  my @cookies;
+  if ($logout and my $cookie = $ENV{HTTP_COOKIE}) {
+    while ($cookie =~ s/(\w+key)=\w+//) {
+      push @cookies,"Set-Cookie: $1=; Max-Age=0; Discard";
+    }
+  }
+  
+  # save default locale for this user
+  if (($akey or $skey or $gkey) and $from and -d $from) {
+    if (not $locale and ($ENV{HTTP_COOKIE}||'') =~ /\blocale=(\w+)/) {
+      $locale = $1;
+    }
+    mksymlink("$from/\@LOCALE",$locale) if $locale;
+  }
+
+  http_header('200 OK',@cookies);
+  # print html_header($head,'<img src="/fex_small.gif">');
+  print html_header($head);
+    
+  if ($http_client =~ /(Konqueror|w3m)/) {
+    pq(qq(
+      '<p><hr><p>'
+      '<center>'
+      '<h3>Your client seems to be "$1" which is incompatible with F*EX and will probably not work!</h3>'
+      'We recommend firefox.'
+      '</center>'
+      '<p><hr><p>'
+    ));
+  }
+
+  # default "fex yourself" setting?
+  if ($from and $id and $id eq $rid and not $addto 
+      and not ($gkey or $skey or $okey or $public or $anonymous)
+      and (not @to or "@to" eq $from)
+      and -f "$from/\@FEXYOURSELF")
+  { 
+    @to = ($from);
+    $nomail = 'fexyourself';
+  }
+
+  # ask for recipient address(es)
+  elsif ($from and $id and $id eq $rid and ($addto or not $submit or not @to)
+         and not ($gkey or $skey or $okey or $public or $anonymous))
+  {
+    present_locales('/fup');
+    
+    @ab = ("<option></option>");
+    
+    # select menu from server address book
+    if (open my $ab,'<',"$from/\@ADDRESS_BOOK") {
+      while (<$ab>) {
+        s/#.*//g;
+        if (/(\S+)[=\s]+(\S+@[\w.-]+\S*)/) {
+          $_ = "$1 &lt;$2>";
+          s/,.*/,.../g;
+          push @ab,"<option>$_</option>";
+        }
+      }
+      close $ab;
+    }
+    
+    unless (@to) {
+      unless ($nomail) {
+        foreach (glob "$from/\@GROUP/*") {
+          if (-f and not -l) {
+            s:.*/::;
+            push @ab,"<option>\@$_</option>" unless /~$/;
+          }
+        }
+      }
+    }
+      
+    my $ab64 = b64("from=$from&id=$id");
+#     '<form class="uploadform" name="upload"'
+    pq(qq(
+      '<form name="upload"'
+      '      action="/fup"'
+      '      method="post"'
+      '      accept-charset="UTF-8"'
+      '      enctype="multipart/form-data">'
+      '  <input type="hidden" name="from" value="$from">'
+      '  <input type="hidden" name="id"   value="$id">'
+      '  <table border="1">'
+      '    <tr><td>sender:   <td><a href="/fup/$ab64">$from</a></tr>'
+      '    <tr title="e-mail address or alias"><td>recipient(s):'
+      '        <td><input type="text" name="to" size="96" value="$to"><br>'
+    ));
+    if (grep /@/,@ab) {
+      pq(qq(
+        '        or select from your address book:'
+        '        <select name="addto" size="1">@ab</select>'
+        '        and'
+        '        <input type="submit" name="addsubmit" value="add to recipients list">'
+      ));
+    }
+    pq(qq(
+      '    </tr>'
+      '  </table>'
+      '  <p>'
+    ));
+    my $rr = "$from/\@ALLOWED_RECIPIENTS";
+    if (-s $rr and open $rr,'<',$rr) {
+      pq(qq(
+        'You are a restricted user and may only fex to these recipients:<p>'
+        '<pre>'
+      ));
+      while (<$rr>) {
+        chomp;
+        s/#.*//;
+        s/\s//g;
+        next unless $_;
+        if (/^\@LOCAL_RDOMAINS/) {
+          foreach my $rd (@local_rdomains) {
+            print "*\@$rd\n";
+          }
+        } else {
+          print "$_\n";
+        }
+      }
+      print "</pre><p>\n";
+      close $rr;
+    }
+    pq(qq(
+      '  <input type="submit" name="submit" value="check recipient(s) and continue">'
+      '  or <input type="submit" name="fexyourself" value="fex yourself">'
+      '</form>'
+      '<p>'
+    ));
+    if ($akey and -f "$from/\@" and not $captive ) {
+      pq(qq(
+        '<a href="/foc?akey=$akey">user config & operation control</a>'
+      ));
+    }
+    
+    if ($from eq $admin ) {
+      pq(qq(
+        '<p>'
+        '<a href="/fac">server config & admin control</a>'
+      ));
+    }
+    
+    if (0 and -f "$docdir/FIX.jar") {
+      print "<p>\n";
+      if    ($public) { print "<a href=\"/fix?from=$from&id=$public&to=$to\">" }
+      elsif ($skey)   { print "<a href=\"/fix?skey=$skey&to=$to\">" }
+      elsif ($gkey)   { print "<a href=\"/fix?gkey=$gkey&to=$to\">" }
+      else            { print "<a href=\"/fix?akey=$akey\">" }
+      print "Alternate Java client</a> (for files > 2 GB or sending of more than one file)\n";
+    }
+    print &logout;
+    if (-x "$FEXHOME/cgi-bin/login") {
+      print $info_login||$info_1;
+    }
+    print "</body></html>\n";
+    exit;
+  } 
+  
+  # ask for filename
+  if ($from and ($id or $okey)) {
+    $to = $group if $group;
+    present_locales($ENV{REQUEST_URI}) if $skey or $gkey or $okey;
+    pq(qq(
+      '<script type="text/javascript">'
+      '  function showstatus() {'
+      '    var file  = document.forms["upload"].elements["file"].value;'
+      '    if (file != "") {'
+      '      window.open('
+      "        '$ENV{PROTO}://$ENV{HTTP_HOST}/$cgi?showstatus=$uid',"
+      "        'fup_status',"
+      "        'width=700,height=500'"
+      '      );'
+      '      return true;'
+      '    }'
+      '    return false;'
+      '  }'
+      ''
+      '  function checkupload() {'
+      '    var file  = document.forms["upload"].elements["file"].value;'
+      '    if (file == "") { alert("No file selected"); }'
+      '  }'
+      ''
+      '  function reportsize() {'
+      '    var form = document.forms["upload"];'
+      '    var filesize = form.file.files[0].size;'
+      '    // alert(filesize + " bytes");'
+      '    form.elements["filesize"].value = filesize;'
+      '    filesize = filesize.toString();'
+      '    filesize = filesize.replace(/(\\d)(?=(\\d\\d\\d)+(?!\\d))/g,"\$1,");'
+      '    document.getElementById("filesize").innerHTML = filesize + " bytes";'
+      '  }'
+      '</script>'
+    ));
+    pq(qq(
+      '<form name="upload"'
+      '      action="/fup"'
+      '      method="post"'
+      '      accept-charset="UTF-8"'
+      '      enctype="multipart/form-data"'
+      '      onsubmit="return showstatus();">'
+      '  <input type="hidden" name="uid"      value="$uid">'
+      '  <input type="hidden" name="from"     value="$from">'
+      '  <input type="hidden" name="filesize" value="">'
+    ));
+    
+    if ($public) {
+      my $toh = join('<br>',@to);
+      pq(qq(
+        '  <input type="hidden" name="id" value="$public">'
+        '  <input type="hidden" name="to" value="$to">'
+        '  <table border="1">'
+        '    <tr><td>sender:   <td><code>$from</code></tr>'
+        '    <tr><td>recipient:<td><code>$toh</code></tr>'
+      ));
+    } elsif ($okey) {
+      pq(qq(
+        '  <input type="hidden" name="okey" value="$okey">'
+        '  <input type="hidden" name="to" value="$to">'
+        '  <table border="1">'
+        '    <tr><td>sender:   <td>$from</tr>'
+        '    <tr><td>recipient:<td>$to</tr>'
+      ));
+    } elsif ($skey) {
+      pq(qq(
+        '  <input type="hidden" name="skey" value="$skey">'
+        '  <table border="1">'
+        '    <tr><td>sender:   <td>$from</tr>'
+        '    <tr><td>recipient:<td>$to</tr>'
+      ));
+    } elsif (@group) {
+      if ($gkey) {
+        pq(qq(
+          '  <input type="hidden" name="gkey" value="$gkey">'
+        ));
+      }
+      my $toh = "group $group:<ul>";
+      my $toc = join(',',@group);
+      foreach my $gm (@group) { $toh .= "<li>$gm" }
+      $toh .= "</ul>";
+      pq(qq(
+        '  <input type="hidden" name="id" value="$id">'
+        '  <table border="1">'
+        '    <tr><td>sender:<td>$from</tr>'
+        '    <tr><td>recipient(s):'
+        '        <td><input type="hidden" name="to" value="$toc">$toh</tr>'
+      ));
+    } else {
+      my $toc = join(',',@to);
+      my $toh = join('<br>',@to);
+      pq(qq(
+        '  <input type="hidden" name="akey" value="$akey">'
+        '  <table border="1">'
+        '    <tr><td>sender:<td>$from</tr>'
+      ));
+      if ($anonymous) {
+        pq(qq(
+          '    <tr><td>recipient:'
+          '        <td><input type="hidden" name="to" value="$toc">$toh</tr>'
+        ));
+      } else {
+        pq(qq(
+          '    <tr><td><a href="/fup?akey=$akey&to=$toc">recipient(s)</a>:'
+          '        <td><input type="hidden" name="to" value="$toc">$toh</tr>'
+        ));
+      }
+    }
+    
+    $autodelete = lc $autodelete;
+    $keep = $keep_default unless $keep;
+    my ($quota,$du) = check_sender_quota($muser||$from);
+    $quota = $quota 
+           ? "<tr><td>sender quota (used):<td>$quota ($du) MB</tr>" 
+           : '';
+    
+    $bwl = qq'<td><input type="text" name="bwlimit" size="8" value="$bwlimit"> kB/s';
+    if (@throttle) {
+      foreach (@throttle) {
+        if (/\[?(.+?)\]?:(\d+)$/) {
+          my $throttle = $1;
+          my $limit = $2;
+          # throttle ip address?
+          if ($throttle =~ /^[\w:.-]+$/) {
+            if (ipin($ra,$throttle)) {
+              $bwl = qq'<td><input type="hidden" name="bwlimit" value="$limit"> $limit kB/s';
+              last;
+            }
+          } 
+          # throttle e-mail address?
+          else {
+            # allow wildcard *, but not regexps
+            $throttle =~ quotemeta $throttle;
+            $throttle =~ s/\*/.*/g;
+            if ($from =~ /^$throttle$/i) {
+              $bwl = qq'<td><input type="hidden" name="bwlimit" value="$limit"> $limit kB/s';
+              last;
+            }
+          }
+        }
+      }
+    }
+    
+    $autodelete = $autodelete{$to} if $autodelete{$to};
+    
+    my $adt = '';
+    for ($autodelete) {
+         if (/yes/i)   { $adt = 'delete file after download' } 
+      elsif (/no/i)    { $adt = 'do not delete file after download' }
+      elsif (/delay/i) { $adt = 'delete file after download with delay' } 
+      elsif (/^\d+$/)  { $adt = "delete file $autodelete days after download" }
+    }
+
+    my $ctr = my $ktr = '';
+    if ($nomail) {
+      $ctr = qq'<td><input type="hidden" name="comment" value="$comment">'
+            .qq'<em>no notification e-mail will be send</em>';
+      $ktr = qq'<input type="text" name="keep" size="2" value="$keep"> days</tr>';
+      $ktr = qq'$keep days<input type="hidden" name="keep" value="$keep"></tr>';
+    } else {
+      $ctr = qq'<td><input type="text" name="comment" size="80" value="$comment">';
+      $ktr = qq'$keep days<input type="hidden" name="keep" value="$keep"></tr>';
+    }
+    if ($captive) {
+      $ktr = qq'$keep days<input type="hidden" name="keep" value="$keep"></tr>';
+    }
+    
+    pq(qq(
+      '    <tr title="$adt"><td>autodelete:<td>$adt</tr>'
+      '    <input type="hidden" name="autodelete" value="$autodelete">'
+      '    <tr title="keep file max $keep days, then delete it"><td>keep:<td>'
+      '    $ktr'
+      '    $quota'
+      '    <tr title="optional, full speed if empty"><td>bandwith limit:'
+      '      $bwl'
+      '    </tr>'
+      '    <tr title="optional, will be included in notification e-mail"><td>comment:'
+      '      $ctr'
+      '    </tr>'
+      '    <tr title="If you want to send more than one file, then put them in a zip or tar archive">'
+      '        <td>file:'
+      '        <td><input type="file" name="file" size="80" value="$file" onchange="reportsize();">'
+      '    </tr>'
+      '    <tr><td>file size:<td id="filesize"></td></tr>'
+      '  </table>'
+      '  <p>
+      '  <input type="submit" value="upload" onclick="checkupload()">'
+      '<p>'
+      '</form>'
+    ));
+    if ($akey and -f "$from/\@" and not $captive) {
+      print "<p>\n",
+            "<a href=\"/foc?akey=$akey\">user config & operation control</a>\n";
+    }
+    if ($from eq $admin ) {
+      pq(qq(
+        '<p>'
+        '<a href="/fac">server config & admin control</a>'
+      ));
+    }
+    if (0 and -f "$docdir/FIX.jar" and not $okey) {
+      print "<p>\n";
+      if    ($public) { print "<a href=\"/fix?from=$from&id=$public&to=$to\">" }
+      elsif ($skey)   { print "<a href=\"/fix?skey=$skey&to=$to\">" }
+      elsif ($gkey)   { print "<a href=\"/fix?gkey=$gkey&to=$to\">" }
+      else            { print "<a href=\"/fix?akey=$akey&to=$to\">" }
+      print "Alternate Java client</a> (for files > 2 GB or sending of more than one file)\n";
+    }
+    print &logout;
+    print $info_2;
+    # printf "<hr><pre>%s</pre>\n",$ENV{HTTP_HEADER};
+    print "</body></html>\n";
+    exit;
+  }
+
+  present_locales('/fup');
+
+  if ($ENV{REQUEST_METHOD} eq 'POST') {
+    pq(qq(
+      '<font color="red"><h3>'
+      '  You have to fill out this form completely to continue.'
+      '</h3></font>'
+    ));
+  }
+
+  pq(qq(
+    '<form action="/fup"'
+    '      method="post"'
+    '      accept-charset="ISO-8859-1"'
+    '      enctype="multipart/form-data">'
+    '  <table>'
+    '    <tr><td>sender:'
+    '        <td><input type="text"     name="from" size="40" value="$from"></tr>'
+    '    <tr><td>auth-ID:'
+    '        <td><input type="password" name="id"   size="16" value="$id" autocomplete="off"></tr>'
+    '  </table>'
+  ));
+  if ($mail_authid and not ($fop_auth or $nomail)) {
+#    pq(qq(
+#      'If you enter "?" as your auth-ID then it will be sent by e-mail to you.'
+#      '<p>'
+#    ));
+    pq(qq(
+      '  <input type="checkbox" name="ID_forgotten" value="ID_forgotten">'
+      '  I have lost my auth-ID! Send it to me by e-mail! '
+      '  (you must fill out sender field above)'
+    ));
+  }
+  pq(qq(
+    '  <p><input type="submit" value="check ID and continue"><p>'
+  ));
+  if (not $nomail and (
+    @local_domains and @local_hosts and ipin($ra,@local_hosts)
+    or @local_rdomains and @local_rhosts and
+       (not @registration_hosts or ipin($ra,@registration_hosts)) 
+    or @demo
+  )) {
+    pq(qq(
+      'You can <a href="/fur">register yourself</a> '
+      'if you do not have a F*EX account yet.<p>'
+    ));
+  }
+  if (@anonymous_upload and ipin($ra,@anonymous_upload)) {
+    my $a = 'anonymous_'.int(rand(999999));
+    pq(qq(
+      'You may also use <a href="/fup?from=anonymous&to=$a">anonymous upload</a>'
+    ));
+  }
+  # if (-f "$docdir/sup.html") {
+  #  pq(qq(
+  #    '<br>'
+  #    'You may also use <a href="/sup.html">simple upload</a>'
+  #  ));
+  # }
+  print "</form>\n";
+    
+  print $info_1;
+
+  if ($debug and $debug>1) {
+    print "<hr>\n<pre>\n";
+    foreach $v (sort keys %ENV) {
+      print "$v = $ENV{$v}\n";
+    }
+    print "</pre>\n";
+  }
+  
+  print "</body></html>\n";
+  exit;
+}
+
+# from sup.html
+if ($from and $file and not @to) {
+  check_rr($from,$from);
+  @to = ($from);
+  $sup = 'fexyourself';
+}
+
+# all these variables should be defined here, but just to be sure...
+http_die("no file specified")      unless $file;
+http_die("no sender specified")     unless $from;
+http_die("no recipient specified")  unless @to;
+unless ($okey and -l "$to/\@OKEY/$okey") {
+  http_die("no auth-ID specified") unless $id;
+  unless ($rid eq $id or $gkey or $skey) {
+    faillog("user $from, id $id");
+    http_die("wrong auth-ID specified");
+  }
+}
+
+&check_status($from);
+
+if (@throttle) {
+  foreach (@throttle) {
+    if (/(.+):(\d+)$/) {
+      my $throttle = $1;
+      my $limit = $2;
+      if (not $bwlimit or $limit < $bwlimit) {
+        # throttle ip address?
+        if ($throttle =~ /^[\d.-]+$/) {
+          if (ipin($ra,$throttle)) {
+            $bwlimit = $limit;
+            last;
+          }
+        }
+        # throttle e-mail address?
+        else {
+          # allow wildcard *, but not regexps
+          $throttle =~ quotemeta $throttle;
+          $throttle =~ s/\*/.*/g;
+          if ($from =~ /^$throttle$/i) {
+            $bwlimit = $limit;
+            last;
+          }
+        }
+      }
+    }
+  }
+}
+
+# address rewriting for storage (swap sender and recipient), see also fop!
+if (not ($skey or $gkey) and $from =~ /^(anonymous|fexmail)/) {
+  ($from,@to) = ("@to",$from);
+}
+
+if (not $anonymous and $overwrite =~ /^n/i) {
+  foreach $to (@to) {
+    if (-f "$to/$from/$fkey/data") {
+      http_die("<code>$file</code> already exists for <code>$to</code>");
+    }
+  }
+}
+
+# additional last check
+foreach $to (@to) {
+  checkaddress($to) or 
+    http_die("<code>$to</code> is not a valid e-mail address");
+}
+
+$to = join(',',@to);
+
+# file overwriting for anonymous is only possible if his client has the 
+# download cookie - else request purging
+if ($anonymous and not $seek and my $dkey = readlink "$to/$from/$fkey/dkey") {
+  if ($overwrite =~ /^n/i) {
+    http_die("<code>$file</code> already exists for <code>$to</code>");
+  }
+  if ($ENV{HTTP_COOKIE} !~ /$dkey/) {
+    my $purge = "/fop/$dkey/$dkey?purge";
+    # http_die("$file already exists $dkey:$ENV{HTTP_COOKIE}:");
+    http_die("<code>$file</code> already exists - <a href=\"$purge\">purge it?!</a>");
+  }
+}
+
+if (@group) {
+  @to = @group;
+  $comment = "[$group] $comment";
+} elsif ($public) {
+  $comment .= ' (public upload)';
+}
+
+# file data still waits on STDIN ... get it now!
+&get_file;
+
+if ($to eq $from and $file eq 'ADDRESS_BOOK') {
+  unlink "$from/\@ADDRESS_BOOK";
+  rename "$from/$from/ADDRESS_BOOK/upload","$from/\@ADDRESS_BOOK"
+    or http_die("cannot save $from/\@ADDRESS_BOOK - $!\n");
+  http_header('200 OK');
+  print html_header($head);
+  print "address book updated",
+        "</body></html>\n";
+  exit;
+}
+
+# finalize upload
+unless ($nostore) {
+  foreach (@group?@group:@to) {
+    my $to = $_;
+    $to =~ s/:\w+=.*//; # remove options from address
+    $filed     = "$to/$from/$fkey";
+    $save      = "$filed/data";
+    $upload    = "$filed/upload";
+    $download  = "$filed/download";
+    $dkey{$to} = readlink "$filed/dkey";
+    $overwrite{$to}++ if -f $save and not -f $download;
+    unlink $save,$download;
+    rename $upload,$save or http_die("cannot rename $upload to $save - $!\n");
+    
+    # log dkey
+    my $dlog = "$logdir/dkey.log";
+    if (open $dlog,'>>',$dlog) {
+      flock $dlog,LOCK_EX;
+      seek $dlog,0,SEEK_END;
+      printf {$dlog} "%s %s %s %s %s\n",
+                     isodate(time),$dkey{$to},$from,$to,$fkey;
+      close $dlog;
+    }
+    
+    # send notification e-mails if necessary
+    if (not $nomail and (readlink "$to/\@NOTIFICATION"||'') !~ /^no/i
+        and ($comment or not $overwrite{$to})) {
+      notify_locale($dkey{$to},'new');
+      debuglog("notify $filed [$filename] '$comment'");
+    }
+  }
+}
+
+# send HTTP status
+$HTTP_HEADER = 'HTTP/1.1 200 OK';
+if ($nostore) {
+  nvt_print($HTTP_HEADER,'Content-Type: text/html','');
+  exit if $http_client =~ /^fexsend/;
+} elsif ($file eq 'STDFEX') {
+  nvt_print($HTTP_HEADER,'');
+  exit;
+} else {
+  nvt_print($HTTP_HEADER);
+  if ($xkey and not $restricted) {
+    my $x = "$durl//$xkey";
+    $x =~ s:/fop::;
+    nvt_print("X-Location: $x");
+  }
+  if ($anonymous) {
+    my $dkey = $dkey{$to};
+    my $cookie = $dkey;
+    $cookie = $1 if $ENV{HTTP_COOKIE} =~ /anonymous=([\w:]+)/;
+    $cookie .= ':'.$dkey if $cookie !~ /$dkey/;
+    nvt_print("Set-Cookie: anonymous=$cookie");
+    $keep{$to} = readlink("$to/\@KEEP")||$keep_default;
+  }
+  foreach (@group?@group:@to) {
+    my $to = $_;
+    $to =~ s/:\w+=.*//; # remove options from address
+    my $file = "$to/$from/$fkey";
+    my $options = sprintf "(autodelete=%s,keep=%s,locale=%s,notification=%s)",
+      readlink("$file/autodelete")||$autodelete,
+      readlink("$file/keep")||readlink("$to/\@KEEP")||$keep_default,
+      readlink("$to/\@LOCALE")||readlink("$file/locale")||$default_locale,
+      readlink("$to/\@NOTIFICATION")||'full';
+    nvt_print("X-Recipient: $to $options");
+    nvt_print("X-Location: $durl/$dkey{$to}/$fkey") unless $restricted;
+  }
+  if ($http_client =~ /^(fexsend|schwuppdiwupp)/) {
+    nvt_print('');
+    exit;
+  } else {
+    nvt_print('Content-Type: text/html','');
+  }
+}
+
+# send HTML report
+print html_header($head);
+
+if ($nostore) {
+  printf "%s (%s MB) received\n",$file,$ndata/M;
+} elsif (not $restricted and ($anonymous or $from eq $to)) {
+  my $size = $ndata<2*1024 ? sprintf "%s B",$ndata:
+             $ndata<2*M           ? sprintf "%s kB",int($ndata/1024):
+                             sprintf "%s MB",int($ndata/M);
+  pq(qq(
+    '<code>$file</code> ($size) received and saved<p>'
+    'Download URL for copy & paste:'
+    '<h2>$durl/$dkey{$to}/$fkey</h2>'
+    'Link is valid for $keep{$to} days!<p>'
+  ));
+} else {
+  if ($ndata<2*1024) {
+    print "<code>$file</code> ($ndata B) received and saved<p>\n";
+    if (not $boring and not $seek) {
+      print "Ehh... $ndata <b>BYTES</b>?! You are kidding?<p>\n";
+    }
+  } elsif ($ndata<2*M) {
+    $ndata = int($ndata/1024);
+    print "<code>$file</code> ($ndata kB) received and saved<p>\n";
+    if ($ndata<1024 and not ($boring or $seek)) {
+      print "Using F*EX for less than 1 MB: ",
+        "ever heard of MIME e-mail? &#9786;<p>\n";
+    }
+  } else {
+    $ndata = int($ndata/M);
+    print "<code>$file</code> ($ndata MB) received and saved<p>\n";
+  }
+  print "<ul>\n";
+  foreach $to (@to) {
+    print "<li>";
+    if ($nomail or $nomail{$to}) {
+      if ($restricted) {
+        rmrf("$to/$from/$fkey");
+        print "<code>$file</code> removed because you are a restricted user ".
+              "and recipient $to cannot receive e-mail<p>\n";
+      } else {
+        pq(qq(
+          '$to cannot receive e-mail &rarr;'
+          '<h3><font color="red">'
+          '  No notification e-mail has been sent to $to!'
+          '</font></h3>'
+          'Download URL for copy & paste:'
+        ));
+        if ($xkey) {
+          my $x = "$durl{$to}//$xkey";
+          $x =~ s:/fop::;
+          print "<h2><code>$x</code></h2>\n";
+        } else {
+          print "<h2>$durl/$dkey{$to}/$fkey</h2>\n";
+          print "Link is valid for $keep{$to} days!<p>\n";
+        }
+      }
+    } elsif ($overwrite{$to} and not $comment) { 
+      print "(old <code>$file</code> for $to overwritten)<p>\n" 
+    } else { 
+      print "$to notified<p>\n"
+    }
+  }
+  print "</ul>\n";
+}
+
+if ($okey) {
+  unlink "$to/\@OKEY/$okey";
+} elsif (not $anonymous and not $sup) {
+  print "<a href=\"/fup?submit=again";
+  if    ($public) { print "&from=$from&to=$to&id=$id" }
+  elsif ($skey)   { print "&skey=$skey" }
+  elsif ($gkey)   { print "&gkey=$gkey" }
+  elsif ($akey)   { print "&akey=$akey&to=$to" }
+  print "&bwlimit=$bwlimit&autodelete=$autodelete&keep=$keep\">";
+  print "send another file</a>\n";
+  if ($http_client !~ /fexsend/ and $http_client =~ /Linux/i) {
+    print qq'<p>Hi Linux-user, try <a href="/FAQ/user.html#Why_should_I_use_a_special_F_EX_client">fexsend</a>! &#9786;<p>\n';
+  }
+  print &logout;
+}
+
+print "</body></html>\n";
+exit;
+
+
+# parse GET and POST requests
+sub parse_request {
+  my %to;
+  my ($to,$dkey);
+  my ($x,$k,$v);
+  my $qs = $ENV{QUERY_STRING};
+  local $_;
+
+  # get JUP parameters from environment (HTTP headers)
+  while (($k,$v) = each %ENV) {
+    if ($k =~ s/^FEX_//) {
+      setparam($k,$v);
+    }
+  }
+  
+  # decode base64 PATH_INFO to QUERY_STRING
+  if ($ENV{PATH_INFO} =~ m:^/(\w+=*)$:) {
+    if ($qs) {
+      $qs = sprintf("%s&%s",decode_b64($1),$qs);
+    } else {
+      $qs = decode_b64($1);
+    }
+  }
+
+  # parse HTTP QUERY_STRING (parameter=value pairs)
+  if ($qs) {
+    foreach (split '&',$qs) {
+      if (s/^(\w+)=//) {
+        my $x = $1;
+        # decode URL-encoding
+        s/%([a-f0-9]{2})/chr(hex($1))/gie;
+        setparam($x,$_); 
+      }
+    }
+  }
+
+  # HTTP redirect does not work correctly with opera!
+  # ==> locale handling is now done by fexsrv
+  if (0 and $locale) {
+    nvt_print(
+      "HTTP/1.1 302 Found",
+      "Location: $ENV{PROTO}://$ENV{HTTP_HOST}/fup",
+      "Set-Cookie: locale=$locale",
+      'Expires: 0',
+      'Content-Length: 0',
+      ''
+    );
+    &reexec;
+  }
+  
+  if ($showstatus) {
+    &showstatus;
+    exit;
+  }
+  
+  # check for akey, gkey and skey (from HTTP GET)
+  &check_keys;
+  
+  if ($ENV{REQUEST_METHOD} eq 'POST' and $cl) {
+    foreach $sig (keys %SIG) {
+      if ($sig !~ /^(CHLD|CLD)$/) {
+        $SIG{$sig} = \&sigexit;
+      }
+    }
+    $SIG{PIPE} = 'IGNORE' if $ENV{PROTO} eq 'https'; # stunnel workaround
+    $SIG{__DIE__} = \&sigdie;
+    http_die("invalid Content-Length header \"$cl\"") if $cl !~ /^-?\d+$/;
+    debuglog($0);
+    debuglog(sprintf("awaiting %d bytes from %s %s",
+                     $cl,$ENV{REMOTE_ADDR}||'',$ENV{REMOTE_HOST}||''),"\n");
+
+    &check_space($cl) if $cl > 0;
+    
+    $SIG{ALRM} = sub { die "TIMEOUT\n" };
+    alarm($timeout);
+    binmode(STDIN,':raw');
+    
+    if (defined($ENV{FEX_FILENAME})) {
+      # JUP via HTTP header
+      $file = $param{'FILE'} = $ENV{FEX_FILENAME};
+      $fileid = $ENV{FEX_FILEID} || 0;
+      $fpsize = $ENV{X_CONTENT_LENGTH} || 0;
+      $boundary = '';
+    } elsif ($contentlength) {
+      # JUP via URL parameter
+      $fpsize = $contentlength;
+      $boundary = '';
+    } else {
+      # FUP
+      if ($ENV{CONTENT_TYPE} =~ /boundary=\"?([\w\-\+\/_]+)/) {
+        $boundary = $1;
+      } else {
+        http_die("malformed HTTP POST (no boundary found)");
+      }
+    
+      READPOST: while (&nvt_read) {
+        # the file itself - *must* be last part of POST!
+        if (/^Content-Disposition:\s*form-data;\s*name="file";\s*filename="(.+)"/i) {
+          push @header,$_;
+          $file = $param{'FILE'} = $1;
+          while (&nvt_read) {
+            last if /^\s*$/;
+            $fileid = $1 if /^X-File-ID:\s*(.+)/;
+            $fpsize = $1 if /^Content-Length:\s*(\d+)/;
+            $flink  = $1 if /^Content-Location:\s*(\/.+)/;
+            push @header,$_;
+          }
+          # STDIN is now at begin of file, will be read later with get_file()
+          last; 
+        }
+        # all other parameters
+        if (/^Content-Disposition:\s*form-data;\s*name="([a-z]\w*)"/i) {
+          my $x = $1;
+          nvt_skip_to('^\s*$');
+          &nvt_read;
+          setparam($x,$_);
+          NEXTPART: while (&nvt_read) {
+            last READPOST if /^--\Q$boundary--/;
+            last NEXTPART if /^--\Q$boundary/;
+          }
+        }
+      }
+    }
+    
+    if (length($file)) {
+      $file =~ s/%(\d+)/chr($1)/ge;
+      $file = untaint(strip_path(normalize($file)));
+      $file =~ s/[\\\/<>]/_/g; # filter out dangerous chars
+      $file =~ s/^\|//;        # filter out dangerous chars
+      $file =~ s/\|$//;        # filter out dangerous chars
+      $filename = $file;
+      $fkey = urlencode($file);
+    }
+
+    # check for akey, gkey and skey (from HTTP POST)
+    &check_keys;
+
+  }
+
+  if ($from) {
+    $from .= '@'.$mdomain if $mdomain and $from !~ /@/;
+    if ($from ne 'anonymous' and not checkaddress($from)) {
+      http_die("<code>$from</code> is not a valid e-mail address");
+    }
+    $from = untaint($from);
+  }
+
+  # collect multiple addresses and check for aliases (not group)
+  if (@to and "@to" !~ /^@[\w-]+$/ 
+      and not ($gkey or $addto or $command =~ /^LIST(RECEIVED)?$/)) 
+  {
+        
+    # read address book
+    if ($from and open my $AB,'<',"$from/\@ADDRESS_BOOK") {
+      my ($alias,$address,$autodelete,$locale,$keep);
+      while (<$AB>) {
+        s/#.*//;
+        $_ = lc $_;
+        if (s/^\s*(\S+)[=\s]+(\S+)//) {
+          ($alias,$address) = ($1,$2);
+          $autodelete = $locale = $keep = '';
+          $autodelete = $1 if /autodelete=(\w+)/;
+          $locale     = $1 if /locale=(\w+)/;
+          $keep       = $1 if /keep=(\d+)/;
+          foreach my $address (split(",",$address)) {
+            $address .= '@'.$mdomain if $mdomain and $address !~ /@/;
+            push @{$ab{$alias}},$address;
+            $autodelete{$alias} = $autodelete;
+            $keep{$alias}       = $keep;
+            $locale{$alias}     = $locale;
+          }
+        }
+      }
+      close $AB;
+    }
+
+    # look for recipient's options and eliminate dupes
+    %to = ();
+    foreach (@to) {
+     my $to = $_;
+     # address book alias?
+      if ($ab{$to}) {
+        foreach (@{$ab{$to}}) {
+          my $address = $_;
+          $address .= '@'.$mdomain if $mdomain and $address !~ /@/;
+          $to{$address} = $address; # ignore dupes
+          if ($specific{'autodelete'}) {
+            $autodelete{$address} = $specific{'autodelete'};
+          } elsif ($autodelete{$to}) {
+            $autodelete{$address} = $autodelete{$to};
+          } else {
+            $autodelete{$address} = readlink "$address/\@AUTODELETE" 
+                                    || $autodelete;
+          }
+          if ($_ = readlink "$address/\@LOCALE") {
+            $locale{$address} = $_;
+          } elsif ($locale{$to}) {
+            $locale{$address} = $locale{$to};
+          } else {
+            $locale{$address} = $locale ;
+          }
+          unless ($locale{$address}) {
+            $locale{$address} = $default_locale || 'english';
+          }
+          if ($specific{'keep'}) { $keep{$address} = $specific{'keep'} }
+          elsif ($keep{$to})     { $keep{$address} = $keep{$to} }
+        }
+      } else {
+        $to = expand($to);
+        $to{$to} = $to; # ignore dupes
+        unless ($autodelete{$to}) {
+          $autodelete{$to} = readlink "$to/\@AUTODELETE" || $autodelete;
+        }
+        $autodelete{$to} = $specific{'autodelete'}  if $specific{'autodelete'};
+        $keep{$to} = $keep_default;
+        $keep{$to} = $keep                          if $keep;
+        $keep{$to} = untaint(readlink "$to/\@KEEP") if -l "$to/\@KEEP";
+        $keep{$to} = $specific{'keep'}              if $specific{'keep'};
+        # recipient specific parameters
+        $keep{$to}       = $1 if $to =~ /:keep=(\d+)/i;
+        $autodelete{$to} = $1 if $to =~ /:autodelete=(\w+)/i;
+      }
+      if (-e "$to/\@CAPTIVE") {
+        my $v;
+        $v = readlink "$to/\@AUTODELETE" and $autodelete{$to} = $v;
+        $v = readlink "$to/\@KEEP"       and $keep{$to}       = $v;
+      }
+    }
+    @to = keys %to;
+    
+    if (scalar(@to) == 1) {
+      $to = "@to";        
+      $keep       = $keep{$to}       if $keep{$to};
+      $autodelete = $autodelete{$to} if $autodelete{$to};
+    }
+        
+    # check recipients and eliminate dupes
+    %to = ();
+    foreach $to (@to) {
+      if ($to eq 'anonymous') {
+        $to{$to} = $to;
+      } else {
+        if ($to =~ /^@(.+)/) {
+          http_die("You cannot send to more than one group") if @to > 1;
+          http_die("Group <code>$to</code> does not exist") unless -f "$from/\@GROUP/$1";
+        } else {
+          $to .= '@'.$mdomain if $mdomain and $to !~ /@/;
+          if (checkaddress($to)) {
+            $to{$to} = untaint($to);
+          } else {
+            http_die("<code>$to</code> is not a valid e-mail address");
+          }
+        }
+      }
+    }
+    @to = values %to;
+  }
+
+  foreach $to (@to) {
+    unless (checkforbidden($to)) {
+      http_die("<code>$to</code> is not allowed");
+    }
+  }
+}
+
+
+# show the status progress bar
+sub showstatus {
+  my $wclose;
+  my ($upload,$data,$sfile,$ukey,$file);
+  my ($nsize,$tsize);
+  my ($t0,$t1,$t2,$tt,$ts,$tm);
+  my ($osize,$percent,$npercent);
+  local $_;
+  
+  $wclose = '<p><a href="#" onclick="window.close()">close</a>'."\n".
+            '</body></html>'."\n";
+  $ukey   = "$ukeydir/$uid";
+  $upload = "$ukey/upload";
+  $data   = "$ukey/data";
+  $sfile  = "$ukey/size";
+  for (1..$timeout) {
+    sleep 1;
+    $tsize = readlink $sfile and last;
+    # upload error?
+    # remark: stupid Internet Explorer *needs* the error represented in this 
+    # asynchronous popup window, because it cannot display the error in the
+    # main window on HTTP POST!
+    if (-f $ukey and open $ukey,'<',$ukey or 
+        -f "$ukey/error" and open $ukey,'<',"$ukey/error") {
+      undef $/;
+      unlink $ukey;
+      html_error($error,<$ukey> || 'unknown');
+    }
+  }
+  # unlink $sfile;
+  
+  if (defined $tsize and $tsize == 0) {
+    print "<script type='text/javascript'>window.close()</script>\n";
+    exit;
+  }
+  unless ($tsize) {
+    html_error($error,
+               "no file data received - does your file exist or is it >2GB?")
+  }
+  html_error($error,"file size unknown") unless $tsize =~ /^\d+$/;
+  
+  http_header('200 OK');
+  if (open $ukey,'<',"$ukey/filename") {
+    local $/;
+    $file = <$ukey>;
+    close $ukey;
+  }
+  http_die("no filename?!") unless $file;
+  
+  my $ssize = $tsize;
+  if ($ssize<2097152) {
+    $ssize = sprintf "%d kB",int($ssize/1024);
+  } else {
+    $ssize = sprintf "%d MB",int($ssize/1048576);
+  }
+  
+  pq(qq(
+    "<html><body>"
+    "<center>"
+    "<h1>Upload Status for<br><code>$file ($ssize)</code></h1>"
+    '<img src="/action-fex-camel.gif" id="afc">'
+    "</center>"
+    "<input type='text' id='percent' style='margin-left:1ex;color:black;background:transparent;border:none;width:32ex;' disabled='true' value='0%'>"
+    "<div style='border:1px solid black;width:100%;height:20px;'>"
+    "<div style='float:left;width:0%;background:black;height:20px;' id='bar'>"
+    "</div></div>"
+  ));
+    
+  # wait for upload file
+  for (1..9) {
+    last if -f $upload or -f $data;
+    sleep 1;
+  }
+  unless (-f $upload or -f $data) {
+    print "<p><H3>ERROR: no upload received</H3>\n";
+    print $wclose;
+    exit;
+  }
+  
+  $SIG{ALRM} = sub { die "TIMEOUT in showstatus: no (more) data received\n" };
+  alarm($timeout*2);
+  
+  $t0 = $t1 = time;
+  $osize = $percent = $npercent = 0;
+  
+  for ($percent = 0; $percent<100; sleep(1)) {
+    $t2 = time;
+    $nsize = -s $upload;
+    if (defined $nsize) {
+      if ($nsize<$osize) {
+        print "<p><h3>ABORTED</h3>\n";
+        print $wclose;
+        exit;
+      }
+      if ($nsize>$osize) {
+        alarm($timeout*2);
+        $osize = $nsize;
+      }
+      $npercent = int($nsize*100/$tsize);
+      $showsize = calcsize($tsize,$nsize);
+    } else {
+      $npercent = 100;
+      $showsize = calcsize($tsize,$tsize);
+    }
+    # hint: for ISDN (or even slower) links, 5 s tcp delay is minimum
+    # so, updating more often is contra-productive
+    if ($t2>$t1+5 or $npercent>$percent) {
+      $percent = $npercent;
+      $t1 = $t2; 
+      $tm = int(($t2-$t0)/60);
+      $ts = $t2-$t0-$tm*60;
+      $tt = sprintf("%d:%02d",$tm,$ts);
+      pq(qq(
+        "<script type='text/javascript'>"
+        "  document.getElementById('bar').style.width = '$percent%';"
+        "  document.getElementById('percent').value = '$showsize, $tt, $percent %';"
+        "</script>"
+      )) or last;
+    }
+  }
+  
+  alarm(0);
+  if ($npercent == 100) {
+    print "<h3>file successfully transferred</h3>\n";
+  } else {
+    print "<h3>file transfer aborted</h3>\n";
+  }
+  pq(qq(
+    "<script type='text/javascript'>"
+    "  document.getElementById('afc').src='/logo.jpg'"
+    "</script>"
+  ));
+  print $wclose;
+  unlink $ukey;
+  exit;
+}
+
+
+# get file from post request
+sub get_file {
+  my ($to,$filed,$upload,$nupload,$speed,$download);
+  my ($b,$n,$uss);
+  my $dkey;
+  my ($fh,$filesize);
+  my ($t0,$tt);
+  my $fb = 0;          # file bytes
+  my $ebl = 0;         # end boundary length
+
+  # FUP, not JUP
+  if ($boundary) {
+    $ebl = length($boundary)+8; # 8: 2 * CRLF + 2 * "--"
+  }
+
+  unless ($nostore) {
+
+    # download already in progress?
+    foreach $to (@to) {
+      $to =~ s/:\w+=.*//; # remove options from address
+      $filed = "$to/$from/$fkey";
+      $download = "$filed/download";
+      if (-f $download and open $download,'>>',$download) {
+        flock($download,LOCK_EX|LOCK_NB) or
+          http_die("<code>$filed</code> locked: a download is currently in progress");
+      }
+    }
+    
+    # prepare upload
+    foreach $to (@to) {
+      $to =~ s/:\w+=.*//; # remove options from address
+      $filed = "$to/$from/$fkey";
+      $nupload = "$filed/upload"; # upload for next recipient
+      mkdirp($filed);
+      
+      # upload already prepared (for first recipient)?
+      if ($upload) {
+        # link upload for next recipient
+        unless ($upload eq $nupload or
+                -r $upload and -r $nupload and
+                (stat $upload)[1] == (stat $nupload)[1]) 
+        {
+          unlink $nupload;
+          link $upload,$nupload;
+        }
+      } 
+      
+      # first recipient => create upload
+      else {
+        $upload = $nupload;
+        unlink "$ukeydir/$uid";
+        if ($flink) {
+          if ($seek) {
+            http_die("cannot resume on link upload");
+          }
+          &nvt_read and $flink = $_;
+          if ($flink !~ /^\//) {
+            http_die("no file link name ($flink)");
+          }
+          $flink = abs_path($flink);
+          my $fok;
+          foreach (@file_link_dirs) {
+            my $dir = abs_path($_);
+            $fok = $flink if $flink =~ /^\Q$dir\//;
+          }
+          unless ($fok) {
+            http_die("<code>$flink</code> not allowed for linking");
+          }
+          my @s = stat($flink);
+          unless (@s and ($s[2] & S_IROTH) and -r $flink) {
+            http_die("cannot read <code>$flink</code>");
+          }
+          unless (-f $flink and not -l $flink) {
+            http_die("<code>$flink</code> is not a regular file");
+          }
+          # http_die("DEBUG: flink = $flink");
+          &nvt_read;
+          &nvt_read if /^$/;
+          unless (/^--\Q$boundary--/) {
+            http_die("found no MIME end boundary in upload ($_)");
+          }
+          unlink $upload;
+          symlink untaint($flink),$upload;
+        } else {
+          unlink $upload if -l $upload;
+          open $upload,'>>',$upload or http_die("cannot write $upload - $!");
+          flock($upload,LOCK_EX|LOCK_NB) or
+            http_die("<code>$file</code> locked: a transfer is already in progress");
+          unless ($seek) {
+            seek $upload,0,0;
+            truncate $upload,0;
+          }
+          # already uploaded file data size
+          $uss = -s $upload;
+          # provide upload ID symlink for showstatus
+          symlink "../$filed","$ukeydir/$uid";
+        }
+      }
+      
+      unlink "$filed/autodelete",
+             "$filed/error",
+             "$filed/restrictions",
+             "$filed/locale",
+             "$filed/keep",
+             "$filed/header",
+             "$filed/id",
+             "$filed/ip",
+             "$filed/speed",
+             "$filed/replyto",
+             "$filed/useragent",
+             "$filed/comment",
+             "$filed/notify";
+      unlink "$filed/size" unless $seek;
+    
+      # showstatus needs file name and size
+      # fexsend needs full file size (+$seek)
+      $fh = "$filed/filename";
+      open $fh,'>',$fh or die "cannot write $fh - $!\n";
+      print {$fh} $filename;
+      close $fh;
+      if ($::filesize > 0 or $cl > 0) {
+        if ($::filesize > 0) { $filesize = $fpsize || $::filesize }
+        else                 { $filesize = $cl-$rb-$ebl+$seek }
+        # new file
+        unless ($seek) {
+          if ($::filesize > 0) {
+            # total file size as reported by POST
+            mksymlink("$filed/size",$::filesize) 
+              or die "cannot write $filed/size - $!\n";
+          } else {
+            # file size as counted
+            mksymlink("$filed/size",$filesize) 
+              or die "cannot write $filed/size - $!\n";
+          }
+        }
+      }
+    
+      $autodelete{$to} = $autodelete unless $autodelete{$to};
+      if ($autodelete{$to} =~ /^(DELAY|NO|\d+)$/i) {
+        mksymlink("$filed/autodelete",$autodelete{$to});
+      }
+
+      if (my $keep = $keep{$to} || $::keep) {
+        mksymlink("$filed/keep",$keep);
+      }
+      mksymlink("$filed/id",$fileid) if $fileid;
+      mksymlink("$filed/ip",$ra)     if $ra;
+      if ($http_client and open $http_client,'>',"$filed/useragent") {
+        print {$http_client} $http_client,"\n";
+        close $http_client;
+      }
+      if ($_ = readlink "$to/\@LOCALE") {
+        # mksymlink("$filed/locale",$_);
+      } elsif ($locale{$to}) {
+        mksymlink("$filed/locale",$locale{$to});
+      } elsif ($locale and $locale ne $default_locale) {
+        mksymlink("$filed/locale",$locale);
+      }
+      if ($replyto and $replyto =~ /.@./) {
+        mksymlink("$filed/replyto",$replyto);
+      }
+    
+      my $arh = "$from/\@ALLOWED_RHOSTS";
+      if (-s $arh) {
+        copy($arh,"$filed/restrictions");
+      }
+      
+      if (@header and open $fh,'>',"$filed/header") {
+        print {$fh} join("\n",@header),"\n";
+        close $fh;
+      }
+    
+      if ((readlink "$to/\@NOTIFICATION"||'') =~ /^no/i) {
+        $nomail{$to} = 'NOTIFICATION';
+      }
+
+      if ($nomail) {
+        open $fh,'>',"$filed/notify" and close $fh;
+      } 
+      if ($comment) {
+        if (open $fh,'>',"$filed/comment") {
+          print {$fh} encode_utf8($comment);
+          close $fh;
+        }
+      }
+
+      # provide download ID key
+      unless ($dkey = readlink("$filed/dkey") and -l "$dkeydir/$dkey") {
+        $dkey = randstring(8);
+        unlink "$dkeydir/$dkey";
+        symlink "../$filed","$dkeydir/$dkey" 
+          or http_die("cannot symlink $dkeydir/$dkey ($!)");
+        unlink "$filed/dkey";
+        symlink $dkey,"$filed/dkey";
+      }
+    
+    }
+
+    # extra download (XKEY)?
+    if ($anonymous and $fkey =~ /^afex_\d/ or
+        $from eq "@to" and $comment =~ s:^//(.*)$:NOMAIL:) 
+    {
+      $xkey = $1||$fkey;
+      $nomail = $comment;
+      my $x = "$xkeydir/$xkey";
+      unless (-l $x and readlink($x) eq "../$from/$from/$fkey") {
+        if (-e $x) {
+          http_die("extra download key $xkey already exists");
+        }
+        symlink "../$from/$from/$fkey",$x 
+          or http_die("cannot symlink $x - $!\n");
+        unlink "$x/xkey";
+        symlink $xkey,"$x/xkey";
+      }
+    }
+    
+  }
+  
+  # file link?
+  if ($flink) {
+    # upload link has been already created, no data to read any more
+    $to = join(',',@to);
+    fuplog($to,$fkey,0);
+    debuglog("upload link successfull, dkey=$dkey");
+  }
+
+  # regular file
+  else {
+
+    # at last, read (real) file data
+    $t0 = time();
+  
+    # streaming data?
+    if ($cl == -1) {
+      alarm($timeout*2);
+      # read until EOF, including MIME end boundary
+      while ($n = read(STDIN,$_,$bs)) {
+        $rb += $n;
+        $fb += $n;
+        syswrite $upload,$_ unless $nostore;
+        alarm($timeout*2);
+      }
+      # size of transferred file, without end boundary
+      $ndata = untaint($fb-$ebl);
+    } 
+    
+    # normal file with known file size
+    else {
+      
+      if ($fpsize) {
+        debuglog(sprintf("still awaiting %d+%d = %d bytes",
+                 $fpsize,$ebl,$fpsize+$ebl));
+        $cl = $rb+$fpsize+$ebl; # recalculate CONTENT_LENGTH
+      } else {
+        if ($::filesize) {
+          $cl = $rb+$::filesize+$ebl; # recalculate CONTENT_LENGTH
+        }
+        debuglog(sprintf("still awaiting %d-%d = %d bytes",
+                         $cl,$rb,$cl-$rb));
+      }
+      # read until end boundary, not EOF
+      while ($rb < $cl-$ebl) {
+        $b = $cl-$ebl-$rb; 
+        $b = $bs if $b > $bs;
+        # max wait for 1 kB/s, but at least 10 s
+        # $timeout = $b/1024;
+        # $timeout = 10 if $timeout < 10;
+        alarm($timeout);
+        if ($n = read(STDIN,$_,$b)) {
+          $rb += $n;
+          $fb += $n;
+          # syswrite is much faster than print
+          syswrite $upload,$_ unless $nostore;
+          if ($bwlimit) {
+            alarm(0);
+            $tt = (time-$t0) || 1;
+            while ($rb/$tt/1024 > $bwlimit) {
+              sleep 1;
+              $tt = time-$t0;
+            }
+          }
+          # debuglog($_);
+        } else {
+          last;
+        }
+      }
+      # read end boundary - F*IX is broken!
+      if ($ebl and $http_client !~ /F\*IX/) {
+        $_ = <STDIN>;
+        $_ = <STDIN>||'';
+        unless (/^--\Q$boundary--/) {
+          http_die("found no MIME end boundary in upload ($_)");
+        }
+      }
+      $rb += $ebl;
+      $ndata = untaint($fb);
+    } 
+
+    alarm(0);
+  
+    unless ($nostore) {
+      close $upload; # or die "cannot close $upload - $!\n";;
+  
+      # throuput in kB/s
+      $tt = (time-$t0) || 1;
+      mksymlink("$filed/speed",int($fb/1024/$tt));
+      
+      unless ($ndata) {
+        http_die(
+          "No file data received!".
+          " File name correct?".
+          " File too big (browser-limit: 2 GB!)?"
+        );
+      }
+      
+      $to = join(',',@to);
+    
+      # streaming upload?
+      if ($cl == -1) {
+      
+        open $upload,'<',$upload or http_die("internal error - cannot read upload");
+        seek $upload,$ndata+2,0;
+        $_ = <$upload>||'';
+        unless (/^--\Q$boundary--/) {
+          http_die("found no MIME end boundary in upload ($_)");
+        }
+        close $upload;
+        truncate $upload,$ndata;
+        
+      } else {
+      
+        # truncate boundary string
+        # truncate $upload,$ndata+$uss if -s $upload > $ndata+$uss;
+      
+        # incomplete?
+        if ($cl != $rb) {
+          fuplog($to,$fkey,$ndata,'(aborted)');
+          if ($fpsize) {
+            http_die("read $rb bytes, but Content-Length announces $fpsize bytes");
+          } else {
+            http_die("read $rb bytes, but CONTENT_LENGTH announces $cl bytes");
+          }
+        }
+      
+        # multipost, not complete
+        if ($::filesize > -s $upload) {
+          http_header('206 Partial OK');
+          exit;
+        }
+      
+        # save error?
+        if (-s $upload > ($::filesize||$filesize)) {
+          fuplog($to,$fkey,$ndata,'(write error: upload > filesize)');
+          http_die("internal server error while writing file data");
+        }
+      
+      }
+      fuplog($to,$fkey,$ndata);
+      debuglog("upload successfull, dkey=$dkey");
+    }
+  }
+}
+
+
+# check recipients restriction
+sub check_rr {
+  my $from = shift;
+  my @to = @_;
+  my $rr = "$from/\@ALLOWED_RECIPIENTS";
+  my ($allowed,$to,$ar,$rd);
+  
+  if (-s $rr and open $rr,'<',$rr) {
+
+    $restricted = $rr;
+
+    foreach (@to) {
+      my $to = $_;
+      $allowed = 0;
+      seek $rr,0,0;
+      while (<$rr>) {
+        chomp;
+        s/#.*//;
+        s/\s//g;
+        
+        if (/^\@LOCAL_RDOMAINS/) {
+          $ar = '(@';
+          foreach (@local_rdomains) {
+            my $rd = $_;
+            # allow wildcard *, but not regexps
+            $rd =~ s/\./\\./g;
+            $rd =~ s/\*/[\\w.-]+/g;
+            $ar .= '|[^\@]+\@' . $rd;
+          }
+          $ar .= ')';
+        } else {
+          # allow wildcard *, but not regexps
+          $ar = quotemeta $_;
+          $ar =~ s/\\\*/[^@]*/g;
+        }
+        
+        if ($to =~ /^$ar$/i) {
+          $allowed = 1;
+          last;
+        }
+        
+      }
+      
+      unless ($allowed) {
+        fuplog("ERROR: $from not allowed to fex to $to");
+        debuglog("$to not in $spooldir/$from/\@ALLOWED_RECIPIENTS");
+        http_die("You ($from) are not allowed to fex to $to");
+      }
+    }
+    
+    close $rr;
+  }
+}
+
+
+# add domain to user if necessary
+sub expand {
+  my @users = @_;
+  my @ua;
+  
+  foreach (@users) {
+    my $u = $_;
+    if ($u =~ /^anonymous(_\d+)?$/) { 
+      $u = "$u\@$hostname";
+    }
+    if ($u eq 'nettest') { 
+      if ($mdomain and -d "$u\@$mdomain") {
+        $u .= "\@$mdomain"
+      } elsif (-d "$u\@$hostname") {
+        $u .= "\@$hostname"    
+      }
+    }
+    if    ($u =~ /@/)          { push @ua,$u } 
+    elsif ($mdomain)           { push @ua,"$u\@$mdomain" } 
+    elsif (-d "$u\@$hostname") { push @ua,"$u\@$hostname" } 
+    else                       { push @ua,$u }
+  }
+  
+  return wantarray ? @ua : join(',',@ua);
+}
+
+
+# forward-copy (bounce) an already uploaded file
+sub forward {
+  my $file = shift;
+  my ($nfile,$to,$AB);
+  my ($filename);
+  my (%to);
+
+  http_die("no file data for <code>$file</code>") unless -f "$file/data";
+
+  if (@to) {
+
+    # check recipients restriction
+    check_rr($from,@to);
+
+    # read aliases from address book
+    if (open $AB,'<',"$from/\@ADDRESS_BOOK") {
+      while (<$AB>) {
+        s/#.*//;
+        $_ = lc $_;
+        if (s/^\s*(\S+)[=\s]+(\S+)//) {
+          my ($alias,$address) = ($1,$2);
+          foreach my $address (split(",",$address)) {
+            $address .= '@'.$mdomain if $mdomain and $address !~ /@/;
+            push @{$ab{$alias}},$address;
+          }
+        }
+      }
+      close $AB;
+    }
+
+    # collect addresses
+    foreach (@to) {
+      my $to = $_;
+      if ($ab{$to}) {
+        foreach my $address (@{$ab{$to}}) {
+          $to{$address} = $address;
+        }
+      } else {
+        $to .= '@'.$mdomain if $mdomain and $to !~ /@/;
+        $to{$to} = $to;
+      }
+    }
+
+    http_header('200 OK');
+    print html_header($head);
+
+    @to = keys %to;
+    
+    foreach (@to) {
+      my $to = $_;
+      $to =~ s/:\w+=.*//; # remove options from address
+      $nfile = $file;
+      $nfile =~ s:.*?/:$to/:;
+      next if $nfile eq $file;
+      mkdirp($nfile);
+      http_die("cannot create directory $nfile") unless -d $nfile;
+      unlink "$nfile/data",
+             "$nfile/upload",
+             "$nfile/download",
+             "$nfile/autodelete",
+             "$nfile/error",
+             "$nfile/restrictions",
+             "$nfile/keep",
+             "$nfile/header",
+             "$nfile/id",
+             "$nfile/speed",
+             "$nfile/comment",
+             "$nfile/replyto",
+             "$nfile/notify";
+      if ($comment) {
+        open $comment,'>',"$nfile/comment";
+        print {$comment} $comment;
+        close $comment;
+      }
+      if ($autodelete =~ /^(DELAY|NO|\d+)$/i) {
+        symlink($autodelete,"$nfile/autodelete");
+      }
+      symlink($keep||$keep_default,         "$nfile/keep");
+                    copy("$file/id",        "$nfile/id");
+                    copy("$file/ip",        "$nfile/ip");
+                    copy("$file/speed",     "$nfile/speed");
+                    copy("$file/replyto",   "$nfile/replyto");
+      $filename   = copy("$file/filename",  "$nfile/filename");
+      link               "$file/data",      "$nfile/data"
+        or die http_die("cannot create $nfile/data - $!");
+      unless ($dkey = readlink("$nfile/dkey") and -l "$dkeydir/$dkey") {
+        $dkey = randstring(8);
+        unlink "$dkeydir/$dkey";
+        symlink "../$nfile","$dkeydir/$dkey" 
+          or http_die("cannot symlink $dkeydir/$dkey");
+        unlink "$nfile/dkey";
+        symlink $dkey,"$nfile/dkey" 
+          or http_die("cannot create $nfile/dkey - $!");
+      }
+      
+      if ($nomail or $nomail{$to}) {
+        if ($filename) {
+          my $url = "$durl/$dkey/".normalize_filename($filename);
+          pq(qq(
+            'Download-URL for $to:<br>'
+            '<code>$url</code>'
+            '<p>'
+          ));
+        }
+      } else {
+        notify_locale($dkey,'new');
+        fuplog($to,urlencode($filename),"(forwarded)");
+        if ($filename) {
+          pq(qq(
+            'File "$filename" copy-forwarded to $to and notified.'
+            '<p>'
+          ));
+        }
+      }
+    }
+    pq(qq(
+      '<a href="/foc?akey=$akey">back to F*EX operation control</a>'
+      '</body></html>'
+    ));
+  } else {
+    $filename = filename($file);
+    http_header('200 OK');
+    print html_header($head);
+    pq(qq(
+      '<form name="upload"'
+      '      action="/fup"'
+      '      method="post"'
+      '      accept-charset="UTF-8"'
+      '      enctype="multipart/form-data">'
+      '  <input type="hidden" name="akey"    value="$akey">'
+      '  <input type="hidden" name="dkey"    value="$dkey">'
+      '  <input type="hidden" name="command" value="FORWARD">'
+      '  forward a copy of "<code>$filename</code>" to:<br>'
+      '  <input type="text" name="to" size="80">'
+      '</form>'
+      '</body></html>'
+    ));
+  }
+}
+
+
+# modify file parameter
+sub modify {
+  my $file = shift;
+  my $filename = filename($file);
+  my $dkey = readlink "$file/$dkey";
+  my $to;
+  my @parameter;
+
+  http_die("no file data for <code>$file</code>") unless -f "$file/data";
+
+  $to = $file;
+  $to =~ s:/.*::;
+  if ($specific{'keep'}) {
+    mksymlink("$file/keep",$keep);
+    utime time,time,"$file/filename";
+    push @parameter,'KEEP';
+  }
+  if ($specific{'autodelete'}) {
+    mksymlink("$file/autodelete",$autodelete);
+    push @parameter,'AUTODELETE';
+  }
+  if ($comment) {
+    if (open $comment,'>',"$file/comment") {
+      print {$comment} $comment;
+      close $comment;
+    }
+    notify_locale($dkey,'new');
+    push @parameter,'COMMENT';
+  }
+  http_header('200 OK');
+  print "Parameter ".join(',',@parameter)." modified for $filename for $to\n";
+}
+
+
+sub calcsize {
+  my ($tsize,$nsize) = @_;
+  if ($tsize<2097152) {
+    return sprintf "%d kB",int($nsize/1024);
+  } else {
+    return sprintf "%d MB",int($nsize/1048576);
+  }
+}
+
+
+# read one line from STDIN (net socket) and assign it to $_
+# returns number of read bytes
+sub nvt_read {
+  my $len = 0;
+
+  if (defined ($_ = <STDIN>)) {
+    debuglog($_);
+    $len = length;
+    $rb += $len;
+    s/\r?\n//;
+  }
+  return $len;
+}
+
+
+# read forward to given pattern
+sub nvt_skip_to {
+  my $pattern = shift;
+
+  while (&nvt_read) { return if /$pattern/ }
+}
+
+
+# set parameter variables
+sub setparam {
+  my ($v,$vv) = @_;
+  my ($idf,$to);
+  
+  $v = uc(despace($v));
+
+#  if ($vv =~ /([<>])/) {
+#    http_die(sprintf("\"&#%s;\" is not allowed in parameter $v",ord($1)));
+#  }
+
+  $param{$v} = $vv;
+  if ($v eq 'LOGOUT') {
+    $logout = $v;
+    # skey and gkey are persistant!
+    $akey = $1 if $ENV{QUERY_STRING} =~ /AKEY:(\w+)/i;
+    unlink "$akeydir/$akey";
+    $login = $FEXHOME.'/cgi-bin/login';
+    if (-x $login) {
+      $login = readlink $login || 'login';
+      nvt_print(
+        "HTTP/1.1 302 Found",
+        "Location: $ENV{PROTO}://$ENV{HTTP_HOST}/$login",
+        'Content-Length: 0',
+        ""
+      );
+      &reexec;
+    }
+  } elsif ($v eq 'LOCALE' and $vv =~ /^(\w+)$/) {
+    $locale = $1;
+  } elsif ($v eq 'REDIRECT' and $vv =~ /^([\w?=]+)$/) {
+    $redirect = $1;
+  } elsif (($v eq 'KEY' or $v eq 'SKEY') and $vv =~ /^([\w:]+)$/) { 
+    $skey = $1;
+    $restricted = $v;
+  } elsif ($v eq 'GKEY' and $vv =~ /^([\w:]+)$/) { 
+    $gkey = $1 unless $nomail;
+    $restricted = $v;
+  } elsif ($v eq 'DKEY' and $vv =~ /^(\w+)$/) { 
+    $dkey = $1;
+  } elsif ($v eq 'AKEY' and $vv =~ /^(\w+)$/) { 
+    $akey = $1;
+  } elsif ($v eq 'FROM' or $v eq 'USER') { 
+    $from = normalize_email($vv);
+    $from = untaint(expand($from));
+    checkchars('from address',$from);
+    checkaddress($from) or http_die("FROM $from is no legal e-mail address");
+  } elsif ($v eq 'REPLYTO') { 
+    $replyto = normalize_email($vv);
+    checkchars('replyto address',$replyto);
+    checkaddress($replyto) or 
+      http_die("REPLYTO $replyto is no legal e-mail address");
+  } elsif ($v eq 'ADDTO') {
+    $vv =~ s/\s.*//;
+    $addto = normalize_email($vv);
+  } elsif ($v eq 'SUBMIT') {
+    $submit = decode_utf8(normalize($vv));
+  } elsif ($v eq 'FEXYOURSELF') {
+    $submit = $vv;
+    @to = ($from);
+  } elsif ($v eq 'TO') {
+    # extract AUTODELETE and KEEP options
+    if ($vv =~ s/[\s,]+AUTODELETE=(\w+)//i) {
+      $specific{'autodelete'} = $autodelete = uc($1);
+    }
+    if ($vv =~ s/[\s,]+KEEP=(\d+)//i) {
+      $keep = $1;
+      $keep = $keep_max if $keep_max and $keep > $keep_max;
+      $specific{'keep'} = $keep;
+    }
+    $to        = normalize(lc($vv));
+    $to        =~ s/[\n\s;,]+/,/g;
+    if ($from) {
+      if ($to eq '.') {
+        $to = $from;
+      }
+      if ($to eq '//') {
+        $to = $from;
+        $comment = '//';
+      }
+    }
+    checkchars('to address',$to);
+    push @to,split(',',$to);
+  } elsif ($v eq 'ID') {
+    $id        = despace($vv);
+    checkchars('auth-ID',$id);
+  } elsif ($v eq 'TCE') {
+    $test = despace($vv);
+  } elsif ($v eq 'OKEY' and $vv =~ /^(\w+)$/) {
+    $okey = $1;
+    $restricted = $v;
+  } elsif ($v eq 'FILEID' and $vv =~ /^(\w+)$/) {
+    $fileid = $1;
+  } elsif ($v eq 'CONTENTLENGTH' and $vv =~ /^(\d+)$/) {
+    $contentlength = $1;
+  } elsif ($v eq 'FILE' or $v eq 'FILENAME') {
+    $file = strip_path(normalize($vv));
+  } elsif ($v eq 'UID' and $vv =~ /^(\w+)$/) {
+    $uid = $1;
+  } elsif ($v eq 'ID_FORGOTTEN') {
+    $id_forgotten = $vv;
+  } elsif ($v eq 'SHOWSTATUS' and $vv =~ /^(\w+)$/) {
+    $showstatus = $uid = $1;
+  } elsif ($v eq 'COMMENT') {
+    $comment = decode_utf8(normalize($vv));
+    $comment =~ s/^\s*!\.!/!SHORTMAIL!/;
+    $comment =~ s/^!#!/!NOMAIL!/;
+    $comment =~ s/^!-!/!NOSTORE!/;
+    $nomail = $comment if $comment =~ /NOMAIL/;
+    $nostore = $nomail = $comment if $comment =~ /NOSTORE/;
+    $bcc .= " $from"   if $comment =~ s/\s*!bcc!?\s*//i;
+    # backward compatibility
+    foreach my $cmd (qw(
+      DELETE LIST CHECKQUOTA CHECKRECIPIENT RECEIVEDLOG SENDLOG FOPLOG FORWARD
+    )) { $command = $comment if $comment eq $cmd }
+  } elsif ($v eq 'COMMAND') {
+    $command = normalize($vv);
+  } elsif ($v eq 'BWLIMIT' and $vv =~ /^(\d+)$/) {
+    $bwlimit = $1;
+  } elsif ($v eq 'SEEK' and $vv =~ /^(\d+)$/) {
+    $seek = $1;
+  } elsif ($v eq 'FILESIZE' and $vv =~ /^(\d+)$/) {
+    $filesize = $1; # complete filesize! 
+    &check_space($filesize-$seek);
+  } elsif ($v eq 'AUTODELETE' and $vv =~ /^(\w+)$/) {
+    $specific{'autodelete'} = $autodelete = uc($1);
+  } elsif ($v eq 'KEEP' and $vv =~ /^(\d+)$/) {
+    $keep = $1;
+    $keep = $keep_max if $keep_max and $keep > $keep_max;
+    $specific{'keep'} = $keep;
+  } elsif ($v eq 'TIMEOUT' and $vv =~ /^(\d+)$/) {
+    $specific{'timeout'} = $timeout = $1;     
+  }
+}
+
+
+sub id_forgotten {
+  my ($id,$to,$subuser,$gm,$skey,$gkey,$url,$fup);
+  
+  return if $nomail;
+  
+  $fup = $durl;
+  $fup =~ s:/fop:/fup:;
+  
+  # full user
+  if (open $from,'<',"$from/\@") {
+    $id = getline($from);
+    close $from;
+  }
+  if ($id) {
+    $url = "$fup/".b64("from=$from&id=$id");
+    mail_forgotten($from,qqq(qq(
+      'Your reqested F*EX auth-ID for $fup?from=$from is:'
+      '$id'
+      ''
+      'Or use:'
+      '$url'
+    )));
+    exit;
+  }
+  
+  # sub user
+  foreach my $skey (glob("$skeydir/*")) {
+    if (-f $skey and open $skey,'<',$skey) {
+      while (<$skey>) {
+        $_ = lc;
+        if (/^(\w+)=(.+)/) {
+          $subuser = $2 if $1 eq 'from';
+          $to     = $2 if $1 eq 'to';
+        }
+      }
+      close $skey;
+    }
+    if ($from and $to and $from eq $subuser) {
+      $skey =~ s:.*/::;
+      mail_forgotten($subuser,qqq(qq(
+        'Your reqested F*EX login is:'
+        ''
+        '$fup?skey=$skey'
+      )));
+      exit;
+    }
+  }
+  
+  # group user
+  foreach my $gkey (glob("$gkeydir/*")) {
+    if (-f $gkey and open $gkey,'<',$gkey) {
+      while (<$gkey>) {
+        $_ = lc;
+        if (/^(\w+)=(.+)/) {
+          $gm = $2 if $1 eq 'from';
+          $to = $2 if $1 eq 'to';
+        }
+      }
+      close $gkey;
+    }
+    if ($gm and $to and $from eq $gm) {
+      $gkey =~ s:.*/::;
+      mail_forgotten($gm,qqq(qq(
+        'Your reqested F*EX login is:'
+        ''
+        '$fup?gkey=$gkey'
+      )));
+      exit;
+    }
+  }
+  http_die("<code>$from</code> is not a F*EX user on this server");
+}
+
+
+sub mail_forgotten {
+  my $user = shift;
+  my @msg = @_;
+  local *P;
+
+  return if $nomail;
+
+  open P,'|-',$sendmail,$user,$bcc or http_die("cannot start sendmail - $!\n");
+  pq(P,qq(
+    'From: $admin'
+    'To: $user'
+    'Subject: F*EX service $hostname'
+    'X-Mailer: F*EX'
+    ''
+  ));
+  print P @msg;
+  close P or http_die("cannot send mail - $!\n");
+  http_header('200 OK');
+  print html_header($head);
+  print "<h3>Mail has been sent to you ($from)</h3>\n";
+  print "</body></html>\n";
+}
+
+
+# lookup akey, skey and gkey (full and sub user and group)
+sub check_keys {
+
+  # only one key can be valid
+  $akey = $gkey = '' if $skey;
+  $akey = $skey = '' if $gkey;
+
+  if ($skey) {
+    # encrypted SKEY?
+    if ($skey =~ s/^MD5H:(.+)/$1/) {
+      # search real SKEY
+      foreach my $s (glob "$skeydir/*") {
+        $s =~ s:.*/::;
+        if ($skey eq md5_hex($s.$sid)) {
+          $skey = $s;
+          last;
+        }
+      }
+    }
+    if (open $skey,'<',"$skeydir/$skey") {
+      $akey = $gkey = '';
+      while (<$skey>) {
+        if (/^(\w+)=(.+)/) {
+          $from = $2          if lc($1) eq 'from';
+          @to = ($muser = $2) if lc($1) eq 'to';
+          $rid = $id = $2     if lc($1) eq 'id';
+        }
+      }
+      close $skey;
+    } else {
+      # $skey = '';
+      http_die("invalid SKEY <code>$skey</code>");
+    }
+  }
+
+  if ($gkey) {
+    # encrypted GKEY?
+    if ($gkey =~ s/^MD5H:(.+)/$1/) {
+      # search real GKEY
+      foreach my $g (glob "$gkeydir/*") {
+        $g =~ s:.*/::;
+        if ($gkey eq md5_hex($g.$sid)) {
+          $gkey = $g;
+          last;
+        }
+      }
+    }
+    if (open $gkey,'<',"$gkeydir/$gkey") {
+      $akey = $skey = '';
+      while (<$gkey>) {
+        if (/^(\w+)=(.+)/) {
+          $from        = $2 if lc($1) eq 'from';
+          $to = $muser = $2 if lc($1) eq 'to';
+          $rid = $id   = $2 if lc($1) eq 'id';
+          # $user      = $2 if lc($1) eq 'user';
+        }
+      }
+      close $gkey;
+      @to = ($to);
+    } else {
+      # $gkey = '';
+      http_die("invalid GKEY <code>$gkey</code>");
+    }
+  }
+
+  if ($akey and not $id) {
+    my $idf;
+
+    # sid is not set with web browser
+    # akey with sid is set with schwuppdiwupp & co
+    $idf = "$akeydir/$akey/@";
+    
+    if (open $idf,'<',$idf and $id = getline($idf)) {
+      close $idf;
+      $from = readlink "$akeydir/$akey"
+        or http_die("internal server error: no $akey symlink");
+      $from =~ s:.*/::;
+      $from = untaint($from);
+      if ($akey ne md5_hex("$from:$id")) {
+        $from = $id = '';
+      }
+    } else {
+      $akey = '';
+    }
+  }
+
+}
+
+
+# check if there is enough space on spool
+sub check_space {
+  my $req = shift;
+  my ($df,$free,$uprq);
+  local *P;
+  
+  if (open $df,"df -k $spooldir|") {
+    while (<$df>) {
+      if (/^.+?\s+\d+\s+\d+\s+(\d+)/ and $req/1024 > $1) {
+        $free = int($1/1024);
+        $uprq = int($req/M);
+        if (not $nomail and open P,"|$sendmail -t") {
+          pq(P,qq(
+            'From: $admin'
+            'To: $admin'
+            'Subject: F*EX spool out of space'
+            ''
+            'F*EX spool $spooldir on $ENV{SERVER_NAME} is out of space.'
+            ''
+            'Current free space: $free MB'
+            'Upload request: $uprq MB'
+          ));
+          close P;
+        }
+        debuglog("aborting because not enough free space in spool ($free MB)");
+        http_die("not enough free space for this upload");
+      }
+    }
+    close $df;
+  }
+}
+
+
+# global substitution as a function like in gawk
+sub gsub { 
+  local $_ = shift;
+  my ($p,$r) = @_; 
+  s/$p/$r/g; 
+  return $_;
+}
+
+
+# standard log
+sub fuplog {
+  my $msg = "@_";
+  
+  $msg =~ s/\n/ /g;
+  $msg =~ s/\s+$//;
+  
+  if (open $log,'>>',$log) {
+    flock $log,LOCK_EX;
+    seek $log,0,SEEK_END;
+    printf {$log} "%s [%s_%s] %s (%s) %s\n",
+                  isodate(time),$$,$ENV{REQUESTCOUNT},$from,$fra,$msg;
+    close $log;
+  }
+}
+
+
+sub sigdie {
+  local $_ = shift;
+  chomp;
+  sigexit('DIE',$_);
+}
+
+
+sub sigexit {
+  my ($sig) = @_;
+  my $msg;
+  my $to = join(',',@to);
+
+  $SIG{__DIE__} = 'DEFAULT';
+  foreach (keys %SIG) { $SIG{$_} = 'DEFAULT' }
+
+  $msg = @_ ? "@_" : '???';
+  $msg =~ s/\n/ /g;
+  $msg =~ s/\s+$//;
+  
+  if (open $log,'>>',$log) {
+    printf {$log} 
+           "%s %s (%s) %s %s caught SIGNAL %s %s\n",
+           isodate(time),
+           $from||'-',
+           $fra||'-',
+           $to||'-',
+           encode_Q($file||'-'),
+           $msg,
+           $rb?"(after $rb bytes)":"";
+    close $log;
+  }
+  if ($sig eq 'DIE') {
+    shift;
+    die "$msg\n";
+  } else {
+    die "SIGNAL $msg\n";
+  }
+}
+
+
+sub mtime {
+  my @s = lstat shift;
+  return @s ? $s[9] : undef;
+}
+
+
+sub present_locales {
+  my $url = shift;
+  my @locales = @::locales; # from fex.ph
+  my ($locale,$lang);
+  
+  if ($url =~ /\?/) { 
+    $url .= "&";
+    $url =~ s/locale=\w+&//g;
+  } else { 
+    $url .= "?";
+  }
+  
+  if (@locales) {
+    map { $_ = "$FEXHOME/locale/$_" } @locales;
+  } else {
+    @locales = glob "$FEXHOME/locale/*";
+  }
+
+  if (@locales > 1) {
+    print "<h3>";
+    foreach (@locales) {
+      $locale = $_;
+      if (-x "$locale/cgi-bin/fup") {
+        $lang = "$locale/lang.html";
+        $locale =~ s:.*/::;
+        if (open $lang,'<',$lang and $lang = getline($lang)) {
+          close $lang;
+        } else {
+          $lang = $locale;
+        }
+        print "<a href=\"${url}locale=$locale\">$lang</a> ";
+      }
+    }
+    print "</h3>\n";
+  }
+}
+
+
+sub check_camel {
+  my ($logo,$camel);
+  local $/;
+  
+  if (open $logo,"$docdir/logo.jpg") {
+    $camel = md5_hex(<$logo>) eq 'ad8a95bba8dd1a61d70bd38611bc2059';
+  }
+  if ($camel and open $logo,"$docdir/action-fex-camel.gif") {
+    $camel = md5_hex(<$logo>) eq '1f3d7acc70377496f95c5adddaf4ca7b';
+  }
+  http_die("Missing camel") unless $camel;
+}
diff --git a/cgi-bin/fur b/cgi-bin/fur
new file mode 100755 (executable)
index 0000000..3d91f55
--- /dev/null
@@ -0,0 +1,416 @@
+#!/usr/bin/perl -wT
+
+# FEX CGI for user registration
+#
+# Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
+#
+
+use CGI         qw(:standard);
+use CGI::Carp  qw(fatalsToBrowser);
+use Fcntl      qw(:flock :seek :mode);
+
+$CGI::LIST_CONTEXT_WARN = 0;
+$CGI::LIST_CONTEXT_WARN = 0;
+
+# import from fex.ph
+our (@local_hosts,@local_domains,@local_rhosts,@local_rdomains);
+our (@registration_hosts,@registration_domains);
+our ($usage_conditions);
+
+# import from fex.pp
+our ($mdomain,$logdir,$spooldir,$fra,$hostname,$sendmail,$admin,$bcc);
+
+our $error = "F*EX user registration ERROR";
+
+my $ra = $ENV{REMOTE_ADDR}||0;
+
+my ($CASE,$ESAC);
+
+# add fex lib
+(our $FEXLIB) = $ENV{FEXLIB} =~ /(.+)/;
+die "$0: no $FEXLIB\n" unless -d $FEXLIB;
+
+# load common code, local config: $HOME/lib/fex.ph
+require "$FEXLIB/fex.pp" or die "$0: cannot load $FEXLIB/fex.pp - $!\n";
+
+my $log = "$logdir/fur.log";
+my $head = "$ENV{SERVER_NAME} F*EX user registration";
+
+chdir $spooldir or die "$spooldir - $!\n";
+
+my $user = my $id = my $verify = '';
+
+&check_maint;
+
+unless (@local_domains or @local_rdomains) {
+  html_error($error,
+    "No domains for registrations are defined.",
+    "Contact $ENV{SERVER_ADMIN} for details."
+  );
+}
+
+# look for CGI parameters
+foreach my $v (param) {
+  my $vv = despace(param($v));
+  debuglog("Param: $v=\"$vv\"");
+  $CASE =
+    $v =~ /^user$/i    ? $user         = normalize_address($vv):
+    $v =~ /^exuser$/i  ? $exuser       = normalize_address($vv):
+    $v =~ /^demouser$/i        ? $demouser     = normalize_address($vv):
+    $v =~ /^verify$/i  ? $verify       = lc(checkchars('URL-parameter',$vv)):
+    $v =~ /^confirm$/i ? $confirm      = checkchars('URL-parameter',$vv):
+    $v =~ /^domain$/i  ? $domain       = lc(checkchars('URL-parameter',$vv)):
+  $ESAC;
+}
+
+if ($confirm) {
+  if ($confirm =~ /^(\w+)$/i) {
+    $confirm = $1;
+  } else {
+    http_die("illegal registration key");
+  }
+  open $confirm,"<.reg/$confirm" or http_die("no registration key $confirm");
+  $user = untaint(getline($confirm));
+  $id   = getline($confirm);
+  close $confirm;
+  # unlink ".reg/$confirm";
+  unless ($user and $id) {
+    http_die("no registration data for key $confirm");
+  }
+  unless (-f "$user/.auto") {
+    http_die("registration expired");
+  }
+  # if (-f "$user/@") { http_die("$user is already activated") }
+  open $user,'>',"$user/@" or http_die("open $user/@ - $!\n");
+  print {$user} $id,"\n";  
+  close $user or http_die("close $user/@ - $!\n");
+  
+  http_header("200 OK");
+  print html_header($head);
+  my $url = "$ENV{PROTO}://$ENV{HTTP_HOST}/fup/" . b64("from=$user&id=$id");
+  pq(qq(
+    '<h3>Your registration was successful. Your new F*EX account is:</h3>'
+    '<p>'
+    '<code><a href="$url">$url</a></code>'
+    '<p>'
+    '(bookmark this URL!)'
+    '<p>'
+    'or you can use:'
+    '<p>'
+    '<table>
+    '  <tr><td>URL:<td><code><b>$ENV{PROTO}://$ENV{HTTP_HOST}/fup/</code></b></tr>'
+    '  <tr><td>Sender:<td><code><b>$user</code></b></tr>'
+    '  <tr><td>auth-ID:<td><code><b>$id</code></b></tr>'
+    '</table>
+    '</body></html>'
+  ));
+  furlog("confirm: account $user created");
+  exit;
+}
+
+
+unless ($user or $exuser or $demouser) {
+  http_header("200 OK");
+  print html_header($head);
+  pq(qq(
+    '<form action="$ENV{SCRIPT_NAME}"'
+    '      method="post"'
+    '      accept-charset="UTF-8"'
+    '      enctype="multipart/form-data">'
+  ));
+  
+  if (@local_domains and @local_hosts and ipin($ra,@local_hosts)) {
+    $reg = $ra;
+    if (grep(/\*/,@local_domains)) {
+      pq(qq(
+        '  new user (may send to internal or external users):<br>'
+        '  <input type="text" name="user" size="80" value="$user">'
+        '<p>'
+        '  allowed domains are:'
+        '<pre>'
+      ));
+      foreach my $ld (@local_domains) {
+        print "  $ld\n";
+      }
+      print "</pre>\n";
+    } else {
+      if ($mdomain and not grep /^\Q$mdomain\E$/i,@local_domains) {
+        unshift @local_domains,$mdomain;
+      }
+      my @mydomains = map { "\t<option>$_</option>\n" } @local_domains;
+      pq(qq(
+        '  new user (may send to internal or external users):<br>'
+        '  <input type="text" name="user" size="40" value="$user">\@<select name="domain" size="1">@mydomains</select>'
+      ));
+    }
+  }
+  
+  if (@local_rdomains and @local_rhosts and 
+      (not @registration_hosts or ipin($ra,@registration_hosts))) {
+    print "   <p>or<p>\n" if $reg;
+    $reg = $ra;
+    pq(qq(
+      '  new external user (may send only to internal users):<br>'
+      '  <input type="text" name="exuser" size="80">'
+      '  <p>'
+    ));
+  }
+  
+  if (@demo) {
+    print "   <p>or<p>\n" if $reg;
+    $reg = $ra;
+    local $_ = sprintf "with %d MB quota and %d day%s account life time",
+      @demo,$demo[1]>1 ? 's' : '';
+    pq(qq(
+      '  new demo user ($_):<br>'
+      '  <input type="text" name="demouser" size="80">'
+      '  <p>'
+    ));
+  }
+  
+  if ($reg) {
+    pq(qq(
+      '  <p>'
+      '  you must enter your e-mail address and <input type="submit" value="submit">'
+      '</form>'
+      '<p>'
+    ));
+    if (@local_rdomains) {
+      pq(qq(
+        '<p><hr><p>'
+        'internal domains are:'
+        '<pre>'
+      ));
+      foreach my $lrd (@local_rdomains) {
+        print "  $lrd\n";
+      }
+    }
+    pq(qq(
+      '</pre>'
+      '<p><hr><p>'
+      '<a href="http://fex.rus.uni-stuttgart.de/users.html">User types overview</a>'
+      '</body></html>'
+    ));
+  } else {
+    html_error($error,
+      "Registrations from your host ($ra) are not allowed.",
+      "Contact $ENV{SERVER_ADMIN} for details."
+    );
+  }
+  exit;
+}
+
+if ($exuser) {
+  unless (@local_rdomains) {
+    http_die("no \@local_rdomains");
+  }
+  if (@registration_hosts and not ipin($ra,@registration_hosts)) {
+    html_error($error,
+      "Registrations from your host ($ra) are not allowed.",
+      "Contact $ENV{SERVER_ADMIN} for details."
+    );
+  }
+  if ($exuser =~ /\@(.+)/) {
+    my $exd = $1;
+    if (@registration_domains and
+        not grep /^\Q$exd\E$/i,@registration_domains) {
+      html_error($error,
+        "Your domain <code>$exd</code> is not allowed for registration.",
+        "Contact $ENV{SERVER_ADMIN} for details."
+      );
+    }
+  } else {
+    html_error($error,"<code>$exuser</code> is not an email address");
+  }
+  $user = $exuser;
+} elsif ($demouser) {
+  $user = $demouser;
+} elsif ($user) {
+  unless (@local_domains) {
+    html_error($error,
+      "No local domains for registration are defined.",
+      "Contact $ENV{SERVER_ADMIN} for details."
+    );
+  }
+  my $mydomains = join('|',@local_domains);
+  $mydomains =~ s/\./\\./g;
+  $mydomains =~ s/\*/.*/g;
+  $mydomains .= "|$mdomain" if $mdomain;
+  $user .= '@'.$domain if $domain and $user !~ /@/;
+  # $user .= '@'.$mdomain if $mdomain and $user !~ /@/;
+  
+  unless (@local_hosts and ipin($ra,@local_hosts)) {
+    html_error($error,
+      "Registrations from your host ($ra) are not allowed.",
+      "Contact $ENV{SERVER_ADMIN} for details."
+    );
+  }
+  if ("@local_domains" ne "*" and $user !~ /\@($mydomains)$/i) {
+    html_error($error,
+      "Illegal domain for username.",
+      "Contact $ENV{SERVER_ADMIN} for details."
+    );
+  }
+} else {
+  html_error($error,"No user type found.");
+}
+
+unless (checkforbidden($user)) {
+  html_error($error,"<code>$user</code> is not allowed");
+}
+unless (checkaddress($user)) {
+  html_error($error,"<code>$user</code> is not a valid e-mail address");
+}
+$user = untaint($user);
+
+if (-f "$user/@") {
+  html_error(
+    $error,
+    "you are already registered".
+    " (<a href=\"/fup?from=$user&ID_forgotten=1\">I have lost my auth-ID</a>)"
+  ); 
+}
+
+unless (-d $user) {
+  mkdir $user,0770 or http_die("mkdir $user - $!\n");
+}
+
+if ($exuser) {
+  my $rf;
+  # recipients e-mail address restrictions
+  $rf = "$exuser/\@ALLOWED_RECIPIENTS";
+  open $rf,'>',$rf or http_die("cannot write $rf - $!\n");
+  print {$rf} "\@LOCAL_RDOMAINS\n";
+  print {$rf} "# See also file \@ALLOWED_RHOSTS\n";
+  close $rf;
+  # recipients ip restrictions
+  $rf = "$exuser/\@ALLOWED_RHOSTS";
+  open $rf,'>',$rf or http_die("cannot write $rf - $!\n");
+  print {$rf} "\@LOCAL_RHOSTS\n";
+  close $rf;
+  if (open $user,'>',"$user/.auto") {
+    print {$user} "fur:external\n";  
+    close $user;
+  }
+} elsif ($demouser) {
+  my $quota = "$demouser/\@QUOTA";
+  open $quota,'>',$quota or http_die("cannot write $quota - $!\n");
+  printf {$quota} "recipient:%d\n",$demo[0];
+  printf {$quota} "sender:%d\n",$demo[0];
+  close $quota;
+  if (open $user,'>',"$user/.auto") {
+    print {$user} "fur:demo\n";  
+    close $user;
+  }
+  open $demouser,'>',"$demouser/.demo" and close $demouser;
+} else {
+  if (open $user,'>',"$user/.auto") {
+    print {$user} "fur:internal\n";  
+    close $user;
+  }
+}
+
+$id = randstring(6);
+
+if ("@local_domains" eq "*") {
+  open $id,'>',"$user/@" or http_die("open $user/@ - $!\n");
+  print {$id} $id,"\n";  
+  close $id or http_die("close $user/@ - $!\n");
+  http_header("200 OK");
+  print html_header($head);
+  $uid = "from=$user&id=$id";
+  $b64 = b64($uid);
+  pq(qq(
+    'Account created:'
+    '<pre>'
+    '$ENV{PROTO}://$ENV{HTTP_HOST}/fup?$uid'
+    '$ENV{PROTO}://$ENV{HTTP_HOST}/fup/$b64'
+    '</pre>'
+    '</body></html>'
+  ));
+  exit;
+}
+
+# from fexsend
+if ($verify eq 'no') {
+  open $id,'>',"$user/@" or http_die("open $user/@ - $!\n");
+  print {$id} $id,"\n";  
+  close $id or http_die("close $user/@ - $!\n");
+  http_header("200 OK",'Content-Type: text/plain');
+  print "$ENV{PROTO}://$ENV{HTTP_HOST}/fup?from=$user&ID=$id\n";
+  furlog("direct: account $user created");
+  if ($bcc and open my $mail,"|$sendmail '$bcc' 2>>$log") {
+    pq($mail,qq(
+      'From: fex'
+      'To: $bcc'
+      'Subject: F*EX user registration'
+      ''
+      '$user has been auto-registrated with verify=no'
+    ));
+    close $mail;
+  } else {
+    furlog("ERROR: cannot run sendmail - $!\n");
+  }
+  exit;
+}
+
+unless (-d '.reg') {
+  mkdir '.reg',0770 or http_die("mkdir .reg - $!\n");
+}
+$reg = randstring(8);
+open $reg,'>',".reg/$reg" or http_die("open .reg/$reg - $!\n");
+print {$reg} $user,"\n",$id,"\n";
+close $reg or http_die("close .reg/$reg - $!\n");
+
+open my $mail,'|-',$sendmail,$user,$bcc
+  or http_die("cannot start sendmail - $!\n");
+pq($mail,qq(
+  'From: $admin'
+  'To: $user'
+  'Subject: F*EX user registration request'
+  ''
+  'To activate your new F*EX account go to this URL:'
+  ''
+  '$ENV{PROTO}://$ENV{HTTP_HOST}/fur?confirm=$reg'
+  ''
+));
+if ($usage_conditions and open $usage_conditions,$usage_conditions) {
+  print {$mail} "The conditions of usage are:\n\n";
+  print {$mail} $_ while <$usage_conditions>;
+  close $usage_conditions;
+}
+close $mail or http_die("cannot send mail - $!\n");
+
+http_header("200 OK");
+print html_header($head);
+print "confirmation e-mail has been sent to <code>$user</code>\n";
+print "</body></html>\n"; 
+furlog("confirmation request mailed to $user");
+exit;
+
+
+# standard log
+sub furlog {
+  my $msg = "@_";
+  
+  $msg =~ s/\n/ /g;
+  $msg =~ s/\s+$//;
+  
+  if (open $log,'>>',$log) {
+    flock $log,LOCK_EX;
+    seek $log,0,SEEK_END;
+    printf {$log} "%s [%s_%s] %s %s\n",
+                  isodate(time),$$,$ENV{REQUESTCOUNT},$fra,$msg;
+    close $log;
+  }
+}
+
+sub normalize_address {
+  my $a = shift;
+
+  $a = lc(normalize(despace($a)));
+  checkchars('address',$a);
+  $a =~ s:/:_:g;
+  $a =~ s:^\.:_:;
+  return untaint($a);
+}
diff --git a/cgi-bin/pup b/cgi-bin/pup
new file mode 100755 (executable)
index 0000000..4ddac70
--- /dev/null
@@ -0,0 +1,198 @@
+#!/usr/bin/perl -wT
+
+# F*EX CGI for public upload
+#
+# Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
+#
+
+use CGI::Carp qw(fatalsToBrowser);
+
+# add fex lib
+(our $FEXLIB) = $ENV{FEXLIB} =~ /(.+)/;
+die "$0: no $FEXLIB\n" unless -d $FEXLIB;
+
+$| = 1;
+
+our $error = 'F*EX public upload ERROR';
+our $head = "$ENV{SERVER_NAME} F*EX public upload";
+our $locale = '';
+
+# import from fex.ph
+our (@public_recipients);
+
+# import from fex.pp
+our ($FEXHOME);
+
+# load common code, local config: $FEXLIB/fex.ph
+require "$FEXLIB/fex.pp" or die "$0: cannot load $FEXLIB/fex.pp - $!\n";
+
+$from = $to = '';
+
+chdir $spooldir or http_die("$spooldir - $!\n");
+
+&check_maint;
+
+my $qs = $ENV{QUERY_STRING};
+(my $multi) = $qs =~ s/(^|&)multi//;
+  
+# parse HTTP QUERY_STRING (parameter=value pairs)
+if ($qs) {
+  foreach (split '&',$qs) {
+    if (s/^(\w+)=//) {
+      my $x = $1;
+      # decode URL-encoding
+      s/%([a-f0-9]{2})/chr(hex($1))/gie;
+      if (/([<>\'\`\"\000-\040])/) {
+        http_die(sprintf(
+          "\"&#%s;\" is not allowed in URL parameter",
+          ord($1)
+        ));
+      }
+      setparam($x,$_); 
+    }
+  }
+}
+
+# parse HTTP POST body
+if ($ENV{REQUEST_METHOD} eq 'POST') {
+  if ($ENV{CONTENT_TYPE} =~ /boundary=\"?([\w\-\+\/_]+)/) {
+    $boundary = $1;
+  } else {
+    http_die("malformed HTTP POST (no boundary found)");
+  }
+
+  binmode(STDIN,':raw');
+    
+  READPOST: while (&nvt_read) {
+    if (/^Content-Disposition:\s*form-data;\s*name="([a-z]\w*)"/i) {
+      my $x = $1;
+      while (&nvt_read) { last if /^\s*$/ }
+      &nvt_read;
+      setparam($x,$_);
+      NEXTPART: while (&nvt_read) {
+        last READPOST if /^--\Q$boundary--/;
+        last NEXTPART if /^--\Q$boundary/;
+      }
+    }
+  }
+}
+
+unless (@public_recipients) {
+  html_error($error,"No public recipients defined by administrator");
+}
+
+if ($to and not grep /^\Q$to\E$/i,@public_recipients) {
+  html_error($error,"$to is not a public recipient");
+}
+
+if ($to and $from and checkaddress($from)) {
+  nvt_print(
+    "HTTP/1.1 302 Found",
+    "Location: $ENV{PROTO}://$ENV{HTTP_HOST}/fup?from=$from&to=$to&id=PUBLIC",
+    'Content-Length: 0',
+    ""
+  );
+  exec($FEXHOME.'/bin/fexsrv') if $ENV{KEEP_ALIVE};
+  exit;
+}
+  
+http_header('200 ok');
+print html_header($head);
+
+my @locales;
+foreach my $locale (glob "$FEXHOME/locale/*") {
+  if (-f "$locale/cgi-bin/pup") {
+    my $langf = "$locale/lang.html";
+    $locale =~ s:.*/::;
+    $lang = $locale;
+    if (open $langf,'<',$langf) {
+      $lang = getline($langf);
+      close $langf;
+    }
+    push @locales,"<a href=\"/pup?to=$to&locale=$locale\">$lang</a>";
+  }
+}
+print "<h3>@locales</h3>\n" if @locales > 1;
+
+
+pq(qq(
+  '<form name="upload"'
+  '      action="/fup"'
+  '      method="post"'
+  '      accept-charset="UTF-8"'
+  '      enctype="multipart/form-data">'
+  '  <input type="hidden" name="id" value="PUBLIC">'
+  '  <input type="hidden" name="autodelete" value="no">'
+  '  <table border="1">'
+));
+
+if ($from) {
+  pq(qq(
+    '    <tr><td>your e-mail address:<td>$from</tr>'
+    '    <input type="hidden" name="from" value="$from">'
+  ));
+} else {
+  pq(qq(
+    '    <tr><td>your e-mail address:<td><input type="text" name="from" size="80"></tr>'
+  ));
+}
+
+if ($to) {
+  pq(qq(
+    '    <tr><td>recipient:<td>$to</tr>'
+    '    <input type="hidden" name="to" value="$to">'
+  ));
+} else {
+  if ($multi) {
+    foreach my $pr (@public_recipients) {
+      push @pr,qq(<input type="checkbox" name="to" value="$pr">)
+               ."<code>$pr</code><br>";
+    }
+    pq(qq(
+      '    <tr><td>recipient:<td>@pr</tr>'
+    ));
+  } else {
+    foreach my $pr (@public_recipients) {
+      push @pr,"<option>$pr</option>";
+    }
+    pq(qq(
+      '    <tr><td>recipient:<td><select name="to" size="1">@pr</select></tr>'
+    ));
+  }
+}
+
+pq(qq(
+  '  </table>'
+  '  <p>'
+  '  <input type="submit" name="continue" value="continue">'
+  '</form>'
+));
+
+
+# set parameter variables
+sub setparam {
+  my ($v,$vv) = @_;
+  
+  $v = uc(despace($v));
+  if ($v eq 'LOCALE' and $vv =~ /^(\w+)$/) {
+    $locale = $1;
+  } elsif ($v eq 'FROM') { 
+    $from = normalize_email($vv);
+  } elsif ($v eq 'TO') {
+    $to        = normalize_email($vv);
+  }
+}
+
+
+# read one line from STDIN (net socket) and assign it to $_
+# returns number of read bytes
+sub nvt_read {
+  my $len = 0;
+
+  if (defined ($_ = <STDIN>)) {
+    debuglog($_);
+    $len = length;
+    s/\r?\n//;
+  }
+  return $len;
+}
diff --git a/cgi-bin/rup b/cgi-bin/rup
new file mode 100755 (executable)
index 0000000..10a78b6
--- /dev/null
@@ -0,0 +1,250 @@
+#!/usr/bin/perl -wT
+
+# FEX CGI for redirect uploaded files
+#
+# Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
+#
+
+use Fcntl      qw(:flock :seek :mode);
+use CGI         qw(:standard);
+use CGI::Carp  qw(fatalsToBrowser);
+use Fcntl      qw(:flock);
+use Digest::MD5        qw(md5_hex);
+
+# add fex lib
+(our $FEXLIB) = $ENV{FEXLIB} =~ /(.+)/;
+die "$0: no $FEXLIB\n" unless -d $FEXLIB;
+
+our ($keep_default,$dkeydir,$akeydir,$mdomain,$logdir,$fra);
+our $akey = '';
+
+# load common code, local config : $HOME/lib/fex.ph
+require "$FEXLIB/fex.pp" or die "$0: cannot load $FEXLIB/fex.pp - $!\n";
+
+our $error = 'F*EX redirect ERROR';
+
+chdir $spooldir or die "$spooldir - $!\n";
+
+my $log = "$logdir/rup.log";
+
+$from = $id = $oto = $nto = $file = '';
+
+# look for CGI parameters
+foreach my $v (param) {
+  $vv = param($v);
+  $vv =~ s/[<>\'\`\"\000-\037]//g;
+  if ($v =~ /^akey$/i and $vv =~ /^(\w+)$/) { 
+    $akey = $1;
+  } elsif ($v =~ /^(from|user)$/i) {
+    $from = normalize_address($vv);
+    $from .= '@'.$mdomain if $mdomain and $from !~ /@/;
+  } elsif ($v =~ /^id$/i) {
+    $id = despace($vv);
+  } elsif ($v =~ /^file$/i) {
+    $vv =~ s:/:_:g;
+    $file = untaint(normalize($vv));
+  } elsif ($v =~ /^oto$/i) {
+    $oto = normalize_address($vv);
+    $oto .= '@'.$mdomain if $mdomain and $oto !~ /@/;
+  } elsif ($v =~ /^nto$/i) {
+    $nto = normalize_address($vv);
+  }
+}
+
+if ($akey and not $from) {
+  if (open $akey,'<',"$akeydir/$akey/@" and $id = getline($akey)) {
+    close $akey;
+    $from = readlink "$akeydir/$akey";
+    $from =~ s:.*/::;
+    $from = untaint($from);
+    if ($akey ne md5_hex("$from:$id")) {
+      $from = $id = '';
+    }
+  }
+}
+
+if ($from and -s "$from/\@ALLOWED_RECIPIENTS") {
+  http_die("You are a restricted user");
+}
+
+if ($from and $id) {
+  open F,'<',"$from/@" or http_die("wrong user or auth-ID");
+  chomp($rid = <F>);
+  close F;
+  http_die("wrong user or auth-ID") if $id ne $rid;
+  unless ($akey) {
+    $akey = untaint(md5_hex("$from:$id"));
+    unlink "$akeydir/$akey";
+    symlink "../$from","$akeydir/$akey";
+  }
+} else {
+  http_die("wrong user or auth-ID");
+}
+
+if ($oto and not glob("$oto/$from/*")) {
+  http_die("$oto has no no files in spool from you ($from)");
+}
+
+# display HTML form and request user data
+unless ($from and $id and $file and $oto and $nto) {
+  $head = "$ENV{SERVER_NAME} F*EX redirect";
+  http_header("200 OK");
+  print html_header($head);
+  pq(qq(
+    '<form action="$ENV{SCRIPT_NAME}" method="post"'
+    ' accept-charset="UTF-8" enctype="multipart/form-data">'
+    '  <table>'
+  ));
+  if ($akey) {
+    print "<input type=\"hidden\" name=\"akey\"   value=\"$akey\">\n";
+  } else {
+    pq(qq(
+      '    <tr><td>sender:'
+      '        <td><input type="text"     name="from" size="80" value="$from"></tr>'
+      '    <tr><td>auth-ID:'
+      '        <td><input type="password" name="id"   size="16" value="$id"></tr>'
+    ));
+  }
+  if ($oto) {
+    pq(qq(
+      '    <tr><td>old (wrong) recipient:<td>$oto</tr>'
+      '        <input type="hidden" name="oto"   value="$oto">'
+    ));
+  } else {
+    pq(qq(
+      '    <tr><td>old (wrong) recipient:'
+      '        <td><input type="text"     name="oto"  size="80" value="$oto"></tr>'
+    ));
+  }
+  if ($from and $oto) {
+    pq(qq(
+      '    <tr><td>new recipient:'
+      '        <td><input type="text"     name="nto"  size="80" value="$nto"></tr>'
+      '    <tr><td>filename: <td><select name="file" size="1">'
+    ));
+    if ($file) {
+      print "\t<option>$file</option>\n";
+    } else {
+      foreach my $file (glob "$oto/$from/*/data") {
+        next if $file =~ m:/STDFEX/:;
+        $file =~ s:/data$::;
+        if ($filename = filename($file)) {
+          print "\t<option>$filename</option>\n";
+        }
+      }
+    }
+    print "    </tr>\n";
+  }
+  pq(qq(
+    '  </table>'
+    '  <p>'
+    '  <input type="submit" value="submit">'
+    '</form>'
+    <p>
+    '<a href="/foc?akey=$akey">back to F*EX operation control</a>'
+    '</body></html>'
+  ));
+  exit;
+}
+
+if ($nto) {
+  
+  # read aliases from address book
+  if (open my $AB,'<',"$from/\@ADDRESS_BOOK") {
+    while (<$AB>) {
+      s/#.*//;
+      $_ = lc $_;
+      if (s/^\s*(\S+)[=\s]+(\S+)//) {
+        my ($alias,$address) = ($1,$2);
+        if ($nto eq $alias) {
+          $nto = $address;
+          last;
+        }
+      }
+    }
+    close $AB;
+  }
+
+  $nto .= '@'.$mdomain if $mdomain and $nto !~ /@/ and $nto =~ /\w/;
+  checkaddress($nto) or http_die("$nto is not a valid e-mail address");
+} else {
+  http_die("no new recipient given");
+}
+
+if ($oto and $nto and $oto eq $nto) {
+  http_die("new recipient must be other than old recipient");
+}
+
+$fkey = urlencode($file);
+unless (-s "$oto/$from/$fkey/data") {
+  http_die("no upload data found for $file for $oto");
+}
+
+if (not -e "$oto/$from/$fkey/data") {
+  if (my $download = slurp("$oto/$from/$fkey/download")) {
+    chomp $download;
+    http_die("$file already downloaded by $download");
+  }
+  http_die("$file has gone");
+}
+
+mkdirp("$nto/$from");
+rmrf("$nto/$from/$fkey");
+
+if (rename "$oto/$from/$fkey","$nto/$from/$fkey") {
+  mkdirp("$oto/$from/$fkey");
+  if (open $fkey,'>',"$oto/$from/$fkey/error") {
+    print {$fkey} "$from has removed $file\n";
+    close $fkey;
+  }
+  unlink "$nto/$from/$fkey/dkey";
+  unlink "$nto/$from/$fkey/notify";
+  unlink "$nto/$from/$fkey/error";
+  unlink "$nto/$from/$fkey/download";
+  if (slurp("$oto/$from/$fkey/$comment") =~ 'NOMAIL') {
+    unlink "$nto/$from/$fkey/comment";
+  }
+  $dkey = randstring(8);
+  symlink $dkey,"$nto/$from/$fkey/dkey";
+  symlink "../$nto/$from/$fkey","$dkeydir/$dkey";
+  $filename = filename("$nto/$from/$fkey") || $fkey;
+  notify_locale($dkey,'new');
+  ruplog("$oto/$from/$fkey ==> $nto");
+  http_header("200 OK");
+  print html_header('F*EX redirect');
+  pq(qq(
+    '<h3>F*EX redirect</h3>'
+    'notification of file upload \"$filename\" sent to $nto'
+    '<p>'
+    '<a href="/foc?akey=$akey">back to F*EX operation control</a>'
+    '</body></html>'
+  ));
+} else {
+  http_die("redirect $nto/$from/$fkey failed : $!")
+}
+
+exit;
+
+
+sub normalize_address {
+  local $_ = shift;
+  s/[<>;,\s\|\/\'\`\"\000-\037]//g;
+  $_ = untaint($_);
+}
+
+
+# standard log
+sub ruplog {
+  my $msg = "@_";
+  
+  $msg =~ s/\n/ /g;
+  $msg =~ s/\s+$//;
+  
+  if (open $log,'>>',$log) {
+    flock $log,LOCK_EX;
+    seek $log,0,SEEK_END;
+    printf {$log} "%s [%s_%s] (%s) %s\n",
+                  isodate(time),$$,$ENV{REQUESTCOUNT},$fra,$msg;
+    close $log;
+  }
+}
diff --git a/cgi-bin/sex b/cgi-bin/sex
new file mode 100755 (executable)
index 0000000..d483ebe
--- /dev/null
@@ -0,0 +1,236 @@
+#!/usr/bin/perl -wT
+
+# CGI for stream exchange
+#
+# Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
+
+use Fcntl      qw':flock :seek :mode';
+use POSIX      qw'mkfifo';
+use Digest::MD5 qw'md5_hex';
+
+# add fex lib
+(our $FEXLIB) = $ENV{FEXLIB} =~ /(.+)/;
+die "$0: no $FEXLIB\n" unless -d $FEXLIB;
+
+$| = 1;
+
+# import from fex.pp
+our ($tmpdir,$logdir,$timeout,$fra,$bs);
+
+# load common code, local config: $HOME/lib/fex.ph
+require "$FEXLIB/fex.pp" or die "$0: cannot load $FEXLIB/fex.pp - $!\n";
+
+chdir $spooldir or error(500,"$spooldir - $!");
+
+my $debuglog = "$tmpdir/sex.log";
+my $ra = $ENV{REMOTE_ADDR}||0;
+$fra .= '/'.$ENV{HTTP_X_FORWARDED_FOR} if $ENV{HTTP_X_FORWARDED_FOR};
+$timeout *= 10;
+
+# normal / public :
+# in normal mode the recipient needs authentification, not the sender
+# in public mode the sender needs authentification, not the recipient
+
+$user = $id = $pmode = $type = '';
+$stream = 'STDSTR';
+$mode = $ENV{REQUEST_METHOD} eq 'POST' ? 'PUSH' : 'POP';
+
+# parse HTTP QUERY_STRING
+if (my $qs = $ENV{QUERY_STRING}) {
+  $qs = decode_b64($qs) if $qs =~ /^\w+=*$/;
+  foreach (split '&',$qs) { setparam(split '=',"$_=") };
+}
+
+unless ($user) { error(400,"Missing user") }
+if ($mdomain and $user !~ /@/) { $user .= '@'.$mdomain }
+if ($user =~ /^anonymous/) {
+  if (@anonymous_upload and ipin($ra,@anonymous_upload)) {
+    mkdirp($user);
+  } else {
+    error(403,"Forbidden");
+  }
+} else {
+  unless (-f "$user/@") { error(404,"Unknown user $user") }
+}
+chdir $user or error(500,"$user - $!");
+
+$stream = "STREAM/$stream";
+
+if ($mode eq 'PUSH') {
+  if ($pmode eq 'PUBLIC') {
+    &authentificate;
+    $stream =~ s:/STDSTR:/PUBLIC:;
+  }
+  mkdirp($stream);
+  my $fifo = "$stream/fifo";
+  unless (-p $fifo) {
+    mkfifo($fifo,0600) or error(503,"Cannot create $fifo : $!");
+  }
+
+  sexlog($mode);
+
+  my $lock = "$stream/lock";
+  open $lock,'>>',$lock or error(503,"Cannot open $lock : $!");
+  flock $lock,LOCK_EX|LOCK_NB or error(409,"$stream already in use");
+  
+  chmod 0600,$fifo;
+  unlink "$stream/mode";
+  unlink "$stream/type";
+  symlink $pmode,"$stream/mode" if $pmode;
+  symlink $type, "$stream/type" if $type;
+
+  $SIG{PIPE} = sub { 
+    sleep 1; 
+    rmrf($stream); 
+    exit; 
+  };
+  $SIG{ALRM} = sub { 
+    syswrite STDOUT,"."; 
+    exit if $!; 
+    $ALARM = 1; 
+  };
+  syswrite STDOUT,"HTTP/1.9 199 Hold on"; 
+  for (my $i=0;$i<$timeout;$i++) {
+    alarm(1);
+    $ALARM = 0;
+    # will hang until $stream is opend for reading by another process
+    open $fifo,'>',$fifo and last;
+    unless ($ALARM) { error(503,"Cannot open $fifo : $!") }
+  }
+  alarm(0);
+  syswrite STDOUT,"\r\n"; 
+  
+  unless (fileno $fifo) { 
+    rmrf($stream);
+    error(504,"Timeout");
+  }
+  
+  header('200 OK');
+
+  $B = 0;
+  $shutdown = sub { sexlog($B); rmrf($stream); exit; };
+  $SIG{PIPE} = sub { sleep 1; &$shutdown; };
+  # syswrite $fifo,$data if $data;
+  while ($b = sysread(STDIN,$_,$bs)) {
+    $B += $b;
+    syswrite $fifo,$_ or die $!;
+  }
+
+  &$shutdown;
+}
+elsif ($mode eq 'POP') {
+  $stream =~ s:/STDSTR:/PUBLIC: if $id eq 'public';
+  unless ($id eq 'public' and (readlink "$stream/mode"||'') eq 'PUBLIC' 
+          or $user =~ /^anonymous/) {
+    &authentificate;
+  }
+  error(503,"No $stream for $user") unless -d $stream;
+  $type = readlink "$stream/type" || '';
+  $SIG{ALRM} = sub { error(504,"Timeout") };
+  alarm($timeout);
+  my $fifo = "$stream/fifo";
+  if (-e $fifo and not -r $fifo) { error(503,"$stream already in use") }
+  open $fifo,'<',$fifo or error(503,"Cannot open $fifo : $!");
+  chmod 0,$fifo;
+  alarm(0);
+  header('200 OK',$type);
+  sexlog($mode);
+  
+  while (sysread($fifo,$_,$bs)) {
+    syswrite STDOUT,$_ or die $!;
+  }
+  exit;
+  
+} 
+else {
+  error(405,"Unknown Request");
+}
+
+exit;
+
+
+sub setparam {
+  my ($v,$vv) = @_;
+  
+  $v = uc(despace($v));
+  $vv = untaint(normalize($vv));
+  # $param{$v} = $vv;
+  if    ($v eq 'USER') { $user = lc(despace($vv)) } 
+  elsif ($v eq 'ID') { $id = despace($vv) } 
+  elsif ($v eq 'MODE') { $pmode = uc(despace($vv)) } 
+  elsif ($v eq 'TYPE') { $type = uc(despace($vv)) } 
+  elsif ($v eq 'STREAM') { $stream = normalize_filename($vv) }
+  elsif ($v eq 'BS' and $vv =~ /(\d+)/) { $bs = $1 } 
+  elsif ($v eq 'TIMEOUT' and $vv =~ /(\d+)/) { $timeout        = $1 }
+  elsif ($v eq 'ANONYMOUS') { $id = $user ='anonymous'; $stream = $vv; }
+}
+
+sub sexlog {
+  if (open my $log,'>>',"$logdir/sex.log") {
+    flock $log,LOCK_EX;
+    seek $log,0,SEEK_END;
+    printf {$log} "%s [%s_%s] %s (%s) %s\n",
+                  isodate(time),$$,$ENV{REQUESTCOUNT},$user,$fra,"@_";
+    close $log;
+  }
+}
+
+sub sigdie {
+  local $_ = shift;
+  chomp;
+  sigexit('DIE',$_);
+}
+
+sub sigexit {
+  my ($sig) = @_;
+  if (open my $log,'>>',"$logdir/sex.log") {
+    printf {$log} "%s %s (%s) caught SIGNAL %s\n",
+                  isodate(time),$user||'-',$fra||'-',"@_";
+    close $log;
+  }
+  if ($sig eq 'DIE') {
+    shift;
+    die "@_\n";
+  } else {
+    die "SIGNAL @_\n";
+  }
+}
+
+sub error {
+  nvt_print("HTTP/1.1 @_");
+  exit;
+}
+
+sub header {
+  my ($status,$type) = @_;
+
+  return if $HTTP_HEADER;
+  $HTTP_HEADER = $status;
+
+  nvt_print("HTTP/1.1 $status");
+  if ($mode eq 'POP') {
+    nvt_print("Server: sexsrv");
+    if ($type eq 'GZIP') {
+      nvt_print("Content-Type: application/gzip");
+    } else {
+      nvt_print("Content-Type: application/binary");
+    }
+    nvt_print("Expires: 0");
+    nvt_print("Cache-Control: no-cache");
+    nvt_print("Connection: close");
+  }
+  nvt_print("");
+}
+
+sub authentificate {
+  my $rid;
+
+  error(400,"Missing auth-ID") unless $id;
+  open $id,'<','@' or error(401,"$user/@ - $!");
+  chomp($rid = <$id>||'');
+  close $id;
+  if ($rid and $sid and $id =~ /^(MD5H:)/) {
+    $rid = $1 . md5_hex($rid.$sid);
+  }
+  error(401,"Wrong auth-ID") if $rid ne $id;
+}
diff --git a/doc/Changes b/doc/Changes
new file mode 100644 (file)
index 0000000..6074da1
--- /dev/null
@@ -0,0 +1,805 @@
+2015-01-17 new fex.ph config variable $mail_authid (default yes)
+2015-01-16 fixed bug no notfication for still existing file (overwrite)
+2015-01-15 fixed bug no locale reminder notfication
+           fixed bug wrong result for recipients with NOTIFICATION=no
+2015-01-13 fexsend: added option -N resend notification email
+           resending notification email deletes download ip restriction
+           fup: fixed bug sending to groups broken
+2015-01-10 fexsend: added option -S show server/user settings
+           fup: added command LISTSETTINGS
+2015-01-09 foc: added save-or-display (MIME) option for download
+2015-01-04 fexsend: fixed bug dies too early on multiple files and one
+                    file has been already transfered
+2014-12-25 fexget,fexsend,sexsend: use default SSL_cipher_list
+                                   DEFAULT:!3DES:!MD5
+2014-12-24 fexget,fexsend,sexsend: evaluate environment variables SSLVERIFY
+                                   SSLVERSION SSLCAPATH SSLCAFILE SSLCIPHERLIST
+           fexget,fexsend,sexsend: use TLS, not SSL
+2014-12-23 fexsend: $HOME/.fex/config with $opt_* and %alias variables
+           fexget: $HOME/.fex/config with $opt_* and %autoview variables
+2014-12-19 fur: fixed bug race condition with fex_cleanup (external->internal)
+2014-12-17 install/update: fixed bug some spool files are owned by user root
+2014-12-16 fexsrv: fixed bug handling of User-Agent FDM
+2014-12-09 added l ll lf to distribution
+           fexwall: also mail to sub and group users
+2014-12-03 fup: remove file after upload if restricted user has set NOMAIL
+           fup: fixed bug wrong message "user notified" if NOMAIL
+2014-12-02 fup: also check recipient restrictions on command CHECKRECIPIENT
+2014-11-24 fexget: autoview gif jpg png tif after download
+2014-11-20 count unfinished upload size into quota, too
+           fixed bug wrong quota calculation on SysV UNIX like Solaris
+2014-11-18 fexsend: added environment variables SSLVERIFY SSLCAPATH SSLCAFILE
+2014-11-18 dop: added HTTP basic authentication for htdoc directory with 
+                .htauth file
+2014-11-14 ignore @forbidden_recipients if $SPOOL/$USER exists
+           (admin has created user)
+2014-11-11 fup: fixed bug groups from other users in address book selection
+           fup: added useragent to $SPOOL/$TO/$FROM/$FILE/
+2014-11-10 fup: present locales in recipient query form, too
+2014-11-07 FAQ: added text anchor URLs
+2014-11-03 added missing fexget fexsend sexget sexsend for tools.html
+2014-10-23 fexsend: on multiple recipients check only the first for resume
+2014-10-14 fac: added option -L (list files detailed)
+2014-10-01 fex_cleanup: fixed bug wrong default spool for virtual hosts
+2014-09-19 sex: added transfered bytes to sex.log
+2014-09-17 fup: fixed bug no locales presentation
+2014-09-14 fex_cleanup: send new release notification to $admin
+2014-09-11 dop: exclude .* and *~ from stream files
+2014-09-01 fup: upload status bar waits longer, until $timeout
+2014-08-27 fex_cleanup: use wget for new release dedection
+2014-08-18 fexsend: workaround for stunnel bug (options -s and -g)
+           fex_cleanup,fexsend: always restrict permissions on fexsend id-file
+2014-08-16 fac: added options -P and -E (more examples)
+2014-08-15 install: fixed bug wrong owner in spool
+           fex_cleanup: do not terminate on error, but print warning
+2014-08-13 fexsrv,fexsend,fexget: reenabled IPv6 support
+2014-08-10 fac: added option -/ to set new admin
+2014-08-08 moved $admin_pw from fex.ph to auth-ID for user $admin
+           fex_cleanup: fixed bug in notify_newrelease
+           fac(CGI): switched from HTTP basic authorization to auth-ID/akey
+           fexsend: always use CHECKRECIPIENT, not only for aliases
+2014-08-07 dop: generate on-the-fly gzipped documents if requested
+2014-08-06 fixed bug install script dysfunctional (permission, hostname)
+2014-07-25 reenabled vhost support
+2014-07-13 dop: in directory index, list only files which are readable by
+                group or other
+2014-07-10 fup: fixed bug wildcards not working in @forbidden_recipients
+2014-06-25 fup,fop: sender can download the files he has sent, too,
+                    if he uses the same ip for upload and download
+2014-06-19 fac(CGI): @admin_hosts is now mandatory in fex.ph
+2014-06-12 fur: reallow registration of internal and external users from
+                the same ip range
+2014-06-05 new fex.ph config variable @forbidden_hosts
+2014-06-03 fuc: fixed bug cannot edit and save groups
+2014-05-26 fuc: ignore akey cookie to prevent cross-site request forgery
+2014-05-26 fup,foc,fuc,rup,pup: better parameter filtering to prevent
+                                cross-site scripting attacks 
+2014-05-25 fup: fixed bug insecure dependency when forwarding a file
+                to a user which has set a default keep value
+2014-05-23 fexget: fixed bug download fails on big file and slow disk
+2014-05-12 set Reply-To in notification emails for @remote_domains
+2014-05-03 fup: fixed bug wrong (old) keep time on forword-copy (bounce)
+2014-04-10 fexsend: added "exclude from archive" option -#
+2014-03-28 fexsend: do not copy "NOMAIL" comment in forward
+2014-03-28 fexget: fixed bug server timeout because of storage check
+2014-03-07 new fex.ph config variable $disclaimer to be appended to every email
+2014-03-05 added bin/fexwall and doc/newfeatures
+2014-03-04 fuc,foc: added PGP/GPG email encryption option
+2014-02-26 fex.pp: fixed bug $keep_default ignored
+2014-02-13 fur,fex.ph: config variable @local_domains may contain wildcard *
+                       in domain names
+2014-01-03 fexget: checks storage capacity before download
+2013-11-26 dop: substitute $variable$ in HTML document only if there is a
+                corresponding environment variable
+2013-10-15 fac: fixed bug wrong output with option -l
+2013-10-14 fexget: use archive name as default extraction directory
+2013-10-09 fixed bug afex URL for only one download availably
+2013-10-07 fup: fixed bug no download links on multiple NOMAIL recipients
+2013-09-23 fexsend: fixed bug option -c sends uncompressed file
+2013-09-19 sub and group users have the same quota amount like their main user
+2013-09-18 fixed bug permission denied for locale htdocs
+           anonymous user now with hostname domain instead of mail domain
+2013-09-17 dop: set locale cookie, too
+2013-09-15 foc: added "Change the disclaimer" option
+2013-09-12 fup: expand domainless address with server hostname if such a user 
+                exists (needed for fbm/nettest)
+2013-09-09 new FAQ design (questions first, then Q+A)
+2013-09-04 fac: modify $hostname when vhost fex.ph is created
+           fuc: recognize comment=NOMAIL
+2013-08-26 fexsend: always show download URL if recipient is "."
+2013-08-20 fup,fop: fixed bug no DELETE and RESUME for fexmail and anonymous 
+                    users (because of storage swap)
+2013-08-18 sexsend: fixed bug data corruption when using https
+           fop,fup: fixed bug bad file locking when using multiple recipients
+           fexsend: fixed bug hangs on server error when sending archive
+2013-08-17 fixed bug $sender_from ignored
+           fup,foc: added notification email resending on user request
+2013-08-16 fac: fixed bug wrong output order for option -l
+2013-08-14 fop: fixed bug no multiple downloads for fexmail
+           fac: added option -M for resending notification emails
+2013-08-09 afex: fixed bug ID for input
+2013-08-06 fexsrv: always includes "Server: fexsrv" in HTTP reply
+           fexsend: terminates if no fexsrv HTTP reply 
+2013-07-28 dop: added .htaccessfrom support
+2013-07-27 fex.ph,dop: security enhancement: 
+                       static documents must be in @doc_dirs
+           dop: documents with leading . are not allowed
+2013-07-25 fexsend: added option -= to upload a file with another name
+2013-07-21 fixed bug environment variables are cut at newline
+2013-07-18 fup,fexsend: use header Content-Location instead of Content-Type for
+                        file linking
+2013-07-15 fup: modifying keep references actual time, not upload date
+           fexsend: fixed bug no feedback on option -x -k (modify keep)
+2013-07-13 fup: fixed bug user specific keep and autodelete defaults are ignored
+2013-07-12 fup,fop: added file link support
+           fexsend: added option -/ for file linking
+2013-07-09 fexget: added option -P proxy:port
+2013-06-28 new all-in-one FAQ
+2013-06-27 fup: to/from storage swap for fexmail and anonymous users
+           fup: anonymous recipient with random number
+2013-06-26 fop: allow multiple downloads from same ip
+           fup,fac: extended "fex yourself" support
+           added sup.html
+2013-06-22 fop,fexsend: Location output for fexmail for already transfered files
+2013-06-19 fexget: fixed bug cannot download MIME file
+2013-06-15 fex.ph: new config variable $notify_newrelease checks
+                   fex.rus.uni-stuttgart.de for new release and sends mail
+2013-06-12 fac(CGI): fixed bug wrong spooldir for virtual server
+2013-06-11 fex.ph: new config variable $usage_conditions for registrations mails
+           fop: fixed bug fexmail download possible only once
+2013-06-10 fac: added option -D to disable a user 
+                (with hooks in fop,fop.fuc,foc,fur)
+2013-06-03 fuc: fixed bug user can modify his auth-ID to an illegal value
+2013-05-30 install: force creation of $admin_pw
+           fex_cleanup: fixed bug wrong fexadmin fexid for reactivation emails
+2013-05-25 added afex and asex to distribution
+2013-05-23 dop: fixed bug no output on file.stream 
+2013-05-22 install script installs as user fex (and not as root)
+2013-05-19 security patch: config variable @local_rhosts restricts download of
+                           files from new external users to specific ip ranges
+           use <from> and <to> syntax in notification email header
+           added support for $max_fail_handler()
+2013-05-18 fac: fixed bug option -rd does not work
+           fac: added option -R
+2013-05-16 fop: fexmail support (multiple downloads allowed)
+2013-05-09 fup: fixed bug anonymous only works if $mdomain is defined
+           fop: fixed bug anonymous only works if recipient host is in
+                @anonymous_upload list 
+2013-05-07 fup: fixed bug multiple Location HTTP headers generate an error with
+                some web browsers 
+2013-05-03 added support for axel download accelerator (multiple HTTP Range)
+2013-05-02 add X-FEX-File-ID header to notification emails
+2013-04-23 dop: fixed bug HTTP 301 redirection timeout on symlinks
+2013-04-05 fexsend: fixed bug server timeout when sending huge ZIPs
+2013-02-20 fac: added -m maintenance mode
+2013-02-17 fup: fixed bug stored comment in spool not in UTF8
+           fop: fixed bug file deletion also deletes fop.log
+2013-02-16 fur,fex.ph: allow "*" for local domains self registration
+           fex.ph: new config variable @registration_hosts
+           fex.ph: new config variable @admin_hosts
+2013-02-11 fur: fixed bug insecure dependency with exuser
+2013-01-31 receiving of reminder emails is user configurable
+2013-01-24 fup: decode UTF8 comment
+2013-01-07 added X-Frame-Options header to prevent clickjacking
+2012-12-26 fup,fexsend: added replyto option
+2012-12-21 dop: download manager prevention:
+                no concurrent downloads from same client with HTTP Range
+2012-12-17 fop: fixed bug ip based download restriction
+2012-12-16 added name based virtual host server
+2012-12-15 fexsrv: HTTP error logging to error.log
+2012-12-07 fex.ph: added optional config variable @durl
+2012-12-04 new feature forward a file from incoming spool
+2012-11-21 fac: show more user information
+2012-11-20 added CAPTIVE user mode
+2012-11-19 fup: with comment NOSTORE file will not be stored ==> benchmarking
+2012-11-14 fexsend: fixed bug gkey and skey URL recipients not working
+2012-11-12 fexsend: 7z archives without compression
+2012-11-08 fop: fixed bug cannot use "?" in file name with fexsend
+2012-11-07 fixed security bug restricted user can redirect files
+2012-11-06 fup: show download-URL after upload if sender = recipient
+           fup,fop,fac: added user up/download IP restriction by admin
+2012-11-05 added HTTP Strict Transport Security if $force_https is enabled
+           fixed bug afex accessible via xkey from everywhere
+2012-11-02 fup: fixed bug one time upload URL gives "no recipient specified" 
+                error
+2012-11-01 fup: fixed bug public upload always gives error
+2012-10-16 fop,fup: added afex support
+           fup: accept recipients . and //
+2012-10-15 sex,sexsend: added anonymous mode (no auth-ID necessary)
+2012-10-14 fop,fup: added support for fexsend anonymous mode
+2012-10-11 fex.ph: added optional config variable $overwrite
+2012-10-10 fup: anonymous upload with non-anonymous recipient
+2012-09-30 fup: fixed bug groups not working any more (NOMAIL)
+2012-09-19 fup: logout functions respects login CGI (or symlink)
+           foc: detailed/brief notification mail configuration
+2012-09-18 fexsend: added options -. and -n
+           fup: added shortmail option in comment
+2012-09-17 added mailmode configuration option in fex.ph
+           fup: shows download-URL if NOMAIL
+2012-09-15 sex: public URL parameter may be in base64 format, too
+2012-09-10 dop: added more security checks
+2012-09-01 dop: added streaming document output
+2012-08-30 sex: fixed bug second receiving client corrupts the stream
+2012-08-26 Changed licence from AGPL to Perl Artistic
+2012-08-21 schwuppdiwupp: error handling on network failures for Windows
+           schwuppdiwupp: removed Tk::FileSelect for Windows
+2012-07-11 fop: fixed bug no multiple downloads for anonymous uploads
+2012-07-10 fixed French, Spanish and Czech localization (code syntax) bugs
+2012-07-09 fup: anonymous upload with modifyable keep option and multiple
+                downloads
+2012-07-05 fexsrv: added camel easteregg
+2012-07-02 fup: added optional anonymous upload with fex.ph variable
+                @anonymous_upload 
+           fup: fixed bug throttle 0 configuration is ignored
+2012-07-01 fexsend: optional argument '@' as files/recipients separator
+           fexsend: fixed bug notification email for recipient '.'
+2012-06-21 dop: inside HTML documents: #include "file" 
+2012-06-06 fexget: new option -+
+2012-06-05 fexsend: new option -+
+2012-06-01 fup: show transfered size instead of total size in status window
+2012-05-04 fexsrv: added bunny easteregg
+2012-05-02 fexsrv: allow HTTP 1.0 with Range header (wget!)
+           fexsrv: disallow negative value in Range (client signed int bug)
+2012-04-26 Changed licence from GPL to AGPL
+2012-04-07 foc: check new address book for syntax errors
+2012-04-06 foc: added comment field for new subuser information
+2012-04-04 removed F*IX because of too many bugs and no maintainer any more
+2012-03-05 fup: fixed bug shell wildcards in recipient address are expanded to
+                known users from spool
+           fex.ph: added optional config variable @locales
+2012-03-01 dop: delivers MIME type text/plain if "?!" is appended to URL
+2012-02-20 foc: show auth-ID after click on link
+2012-02-07 fop: MIME-type text/html is no longer possible for security reasons
+2012-02-04 added optional french localization
+2012-02-03 fixed bug 0.0.0.0 not recognized as ip address
+          fex.ph: added optional config variable $keep_max
+2012-02-02 HTTP parameter filtering to prevent cross-site scripting attacks
+2012-02-01 config variable @throttle may also contains ip addresses
+2012-01-25 pup: locale selection in native language, default autodelete=no
+           fup: "send another file" with same keep and autodelete parameters
+2012-01-17 fixed bug reactivation.txt in czech instead english
+2012-01-06 fup: fixed bug show wrong remaining keep days
+2012-01-02 fup.pl: fixed bug bad FAQ link
+2011-12-31 fex_cleanup: fixed bug notification emails not localized
+2011-12-30 fup: additional dkey.log
+2011-12-05 fup: respect @throttle config for all clients
+2011-11-29 dop: delivers text files (scripts!) without x-bit as "text/plain"
+2011-11-15 fup: fixed bug with $autodelete = $NUMBER;
+2011-11-08 fac: added option -S statistics
+2011-11-02 fup: show remaining keep time in files listing
+2011-10-28 fup: fixed bug uninitialized value when using copy-forward 
+2011-10-13 fup: FILESIZE hack for firefox 7 to support uploads > 4 GB
+2011-10-05 fup: fixed bug wrong sender quota calculation
+2011-10-03 fex.ph: added configuration variables @upload_hosts @download_hosts
+2011-09-30 fexsend: added option -g for sending encrypted files
+2011-09-21 fexsrv: use $default_locale if client sends illegal locale cookie
+2011-09-20 fex.ph: added optional config variable $boring for unhumorous mode
+2011-09-19 fur: $USER/.auto contains additional info about account creation
+2011-09-07 fac(CGI): fixed bug infinitve loop in watch logfile
+2011-09-06 fup,fac,fur: new additional login URL type: 
+                        http://FEXSERVER/fup/B64ID
+           fup: show "or select from address book" only if there are entries
+2011-09-05 fexsrv: fixed bug locale cookie not fetched on http://cgi?parameter
+2011-09-01 rup: fixed bug cannot find files (no more SID in akeys directory)
+2011-08-30 fex.ph: new config variable @forbidden_recipients
+2011-08-29 fexsend: accept file number for delete option -d, too
+           dop: fixed bug no text document output if external file command is 
+                non-GNU
+2011-08-26 added one time upload OKEY
+2011-08-13 fex_cleanup: fixed bug comment missing in reminder email
+2011-08-11 dop: #if ... #else ... #elseif ... #endif inside HTML documents
+           dop: show HTML sourcecode if "!" is appended to URL
+2011-08-10 fex_cleanup: delete obsolete users, too, via fex.ph $account_expire
+           new FAQ design (with Javascript/CSS)
+2011-08-09 fup: show error on invalid SKEY or GKEY
+           fuc: fixed bug subuser and groupuser not lowercase forced
+           address book may also contain option locale=<languange>
+2011-08-08 fex_cleanup: auto-expire user accounts with fex.ph variable 
+                        $account_expire
+           fexsend,fup: allow forward with locale
+           dop: extra security check: files from lib and spool are not allowed
+2011-08-07 fup: subusers and groupusers can also select a locale
+           fup: if user selects a locale login, save it as default locale
+                (does not affect fexsend and schwuppdiwupp)
+           notification emails come in default locale
+2011-08-03 fexsend: fixed bug uninitialized value when using chunked mode
+           fexsend: added undocumented option -F female mode
+2011-07-31 fexsend: added option -s streaming data
+           fup: accept streaming data
+2011-07-30 fexsend: -a tar archives no longer use a intermediate transferfile,
+                    but send via pipe (streaming)
+           fexsend: fixed bug no resume on -a archives
+           fexsend: always ask server if file already has been uploaded
+           fup: more information on F*EX clients download and configuration
+2011-07-27 fup: if comment contains "!bcc!" then sender will get a bcc 
+                of notification email
+2011-07-26 fexget: added option -X do not extract archive file
+           fexget: added option -a get all files
+           fexsrv: fixed bug uninitialized value when using a reverse proxy
+           fex_cleanup: fixed bug notify reminder email not localized
+2011-07-22 fac(CGI): fixed bug displaying < and & in logfiles
+           fac(CGI): added getting error.log
+           fop: allow multiple downloads from any client if sender = recipient
+2011-07-16 added doc/reverse_proxy 
+2011-07-14 added optional czech localization
+2011-07-01 FAQ.html reformated
+2011-06-30 translate install job 20 times faster
+2011-06-27 added robots.txt to disallow web robots indexing
+2011-06-22 fup: disable HTML code in file listing (filename & comment)
+2011-06-21 added optional italian localization
+2011-06-17 fixed bug $bcc is ignored
+2011-06-16 fexsend,fexget: better reverse proxy support 
+                           (always send Host header)
+           added optional galician localization
+2011-06-15 fup: fixed bug always keep_default days in notification email
+2011-06-14 fexsend: transparent proxy detection (and support)
+           fixed bug $docdir ignored
+2011-06-10 fex.ph: new config variable $bcc for notification emails
+2011-06-09 set Reply-To in all notification emails
+           fup: fixed security bug everyone can upload files with empty auth-ID
+2011-06-05 fup: fixed bug insecure dependency in printf on "forward file"
+2011-06-03 fup,fop: added throttle bandwith limit option (fex.ph)
+2011-06-02 fup: added bandwith limit option
+2011-06-01 added PID and request-number to the logs
+           fex_cleanup: fixed bug no expire with AUTODELETE=NO
+2011-05-31 support for FEXLIB /usr/local/share/fex/lib /usr/share/fex/lib
+2011-05-30 fup,fexsend: added option -x to modify file parameters
+2011-05-29 fup,fexsend: forward files with new comment and keep time
+           rup: add mdomain to addresses without domain
+2011-05-18 fup: fixed bug restricted users can forward files to anybody
+2011-05-17 fixed bug access problems with AKEYs: now use SID instead of SIP
+2011-05-11 added helper script mksgkeys (regenerates missing SKEYs and GKEYs)
+2011-05-10 fex_cleanup: cleanup ADDRESS_BOOK file upload
+           fex.ph,fex_cleanup: AUTODELETE=NUMBER ==> 
+                               delete file on next NUMBER day after download
+2011-05-09 fac,fup: added user specific autodelete default
+           fac,fex_cleanup: added user specific keep default
+2011-04-27 fexsend: fixed bug archiv.zip not working on Windows
+           dop: added index function for htdoc directory with .htindex file
+2011-04-25 fexsend: better proxy support (non-persistent connections)
+2011-04-24 xx: better ESXi support (heuristic guessing of tar format)
+2011-04-22 sexsend: base64 support for $FEXID and $FEXXX
+2011-04-01 fexsend: continue without SID if SID is not available
+2011-03-26 fexsrv: deactivate header_hook (inquisition) if request is a 
+                   regular fop request
+2011-03-18 fexsend: base64 support for $FEXID and $FEXXX
+2011-03-09 fexget,fexsend: fixed bug no file listing for https
+2011-03-04 removed "use Switch" because of warnings with perl 5.12
+2011-02-28 sexsend: added option -g show transfer rate
+2011-02-24 dop: evaluate <<perl-code>> inside html documents
+2011-02-21 fexsend,fup: added option fexsend -U show authorized (login) URL
+2011-02-18 do not modify download URL protocol if $dkey is set in fex.ph
+           fac(CGI): fixed bug uninitialized value $server
+           URLs in notification emails are derived from config variable $durl
+2011-02-17 fup: fixed bug access denied with SKEY
+2011-02-08 fup,fop,fuc: fixed bug access problems with sip in AKEYs
+           fup: fixed bug no notification email for multiple recipients
+2011-02-07 fexsend,fop: do not send same file (filename and mtime) twice
+2011-02-06 fup: fixed bug no notification email after first failed upload
+2011-01-31 schwuppdiwupp: added ISO-8859-1 support
+2011-01-30 schwuppdiwupp: added running camel
+2011-01-28 schwuppdiwupp: added chunksize to proxy options
+           schwuppdiwupp: fixed bug timeout when using address book
+           fexsend: fixed bug chunksize 0
+2011-01-27 schwuppdiwupp: added advanced preferences Proxy and TMPDIR
+2011-01-26 fex_cleanup: fixed bug uninitialized value in debuglog
+           fex_cleanup: added option -v
+           fexsend,fexget,sexsend: added option -V show version
+           schwuppdiwupp: added drag&drop support for windows
+           schwuppdiwupp: added 7zG support
+2011-01-25 fuc: fixed bug cannot delete all subusers
+           schwuppdiwupp: fixed bug 7-zip not found
+           schwuppdiwupp: added drive letters in directory selection
+2011-01-24 fop: IE bug workaround to store *.exe files
+2011-01-18 schwuppdiwupp: added tar, zip and 7z container
+2011-01-17 fexsend: fixed bug option -l not working with https URL
+2011-01-16 fup: added 7zip hint in notification emails
+2011-01-13 schwuppdiwupp: fixed bug no transfer at all when comment is set
+2011-01-12 rup: added logging
+           rup: wrong recipient cannot download file, but will get an error
+           fac(CGI): fixed bug $server not declared
+2011-01-07 fexget: keep file permission in overwrite mode
+2011-01-04 fex_cleanup: fixed bug autodelete after partial download
+2010-12-26 fex_cleanup: fixed bug too early expire for forwarded files
+2010-12-10 new config variable @public_recipients for new CGI pup 
+           (public upload) - upload without auth-ID
+2010-12-09 fex.ph,fur: new config variable @local_rdomain for self-
+                       registration of restricted external-to-internal users
+2010-12-08 fup,foc: no access to foc for restricted users
+2010-12-02 fup: if there is a cgi-bin/login it will be called on "logout"
+2010-11-24 fex.ph: new config variable $default_locale
+           dop: auto-expires every document (to prevent browser caching)
+2010-11-16 fexsrv: better error handling if CGI is not executable
+2010-11-09 new SKEYs and GKEYs, because old ones could be not unique
+           access for subuser only with SKEY
+           access for groupuser only with GKEY
+2010-11-07 fup,fuc: added GKEY
+           fexsend: SKEY or GKEY URLs can be recipients, too
+2010-11-04 fexget: added (hidden) option -K
+2010-11-03 fexsend: fixed bug proxy usage failed
+2010-11-02 fop: fixed bug corrupted download with Internet Explorer 
+2010-10-25 fop,fup: better locking: no uploading is possible while a
+                    download is in progress for the same file
+2010-10-24 fix,fop: fixed bug subuser not working (SKEY problem)
+           xx: added locking
+2010-10-23 xx,fop: added xx :slot option (multiple storage slots)
+2010-10-20 fup,fop,fexsend: fexsend for subuser with SKEY
+2010-10-19 fup: expires *KEY cookies on logout
+2010-10-12 fup: fixed bug wrong interpretation of SKEY parameter+cookie
+2010-10-05 fexend,fup: support of http reverse proxy
+2010-09-27 fup: fixed bug missing Content-Type in upload status report
+2010-09-20 fexsrv: IPv6 http support
+2010-09-15 fac(CGI): fixed bug cannot delete and (re)create user
+           fac(CGI): fixed bug cannot create user who was a recipient
+2010-09-12 fexsend,fop: fixed bug resuming upload does not work with alias
+           fup: fixed bug resuming upload handles autodelete and keep
+                parameters incorrectly
+2010-09-07 fac(CGI): fixed bug not working if there is no user at all
+2010-09-05 fop: extra parameter keep=days
+2010-08-31 install: fixed bug wrong ownership for spool files
+2010-08-25 perl 5.8 required
+           fexget: -s can write to named pipe or character special file
+2010-08-21 fop: $limited_download checks dkey cookie instead of client IP
+2009-08-20 removed mma
+2010-08-18 fex_cleanup: fixed bug not expiring
+2010-08-17 fac: fixed bug accept users without domain
+           install: fixed bug empty $admin_pw
+2010-08-15 fex.ph: optional fix address $sender_from (instead of F*EX user) in
+                   notification email From 
+2010-08-14 added optional spanish localization
+2010-08-12 fup: speedup 90%
+           fop: speedup 20%
+           fop: better fexget compatibility 
+                (close connection after file delivery)
+2010-08-11 fop: fixed IE download bug (missing header separating line)
+           fop: fixed 1 min delay bug on AUTODELETE=YES
+2010-08-08 sex: support for compressed streams
+           sex,sexsend: removed unneccesary text mode (option -t)
+           sex,sexsend: speedup factor 5
+           added sexxx
+2010-08-06 sex: fixed various bugs in client and server
+           fac(CGI): fixed bug AKEY not working
+2010-08-03 xx: no user inquiry for postprocessing if output is a pipe
+2010-08-02 added optional german localization
+2010-07-31 separated subusers in extra file $SPOOL/$USER/@SUBUSER
+2010-07-25 fop: log also aborted downloads
+2010-07-23 added fac CGI
+2010-07-18 fexsrv,fup,fexsend: extra XKEY download with short // URL
+           fexsend: fixed bug CHECKRECIPIENT not working
+2010-07-16 added cookie support (for AKEY and SKEY)
+           fup: fixed bug showstatus window too small for close button
+2010-07-13 schwuppdiwupp: added CHECKRECIPIENT
+2010-07-12 fop,xx: allow several concurrent downloads of STDFEX
+2010-07-10 fop: workaround for stupid IE download bug
+2010-07-03 fex.ph,fex.pp: new config variable @remote_domains
+2010-07-01 fexsrv,fex.ph: new config variable $force_https
+2010-06-29 fop: new config variable $limited_download with default NO 
+                => allow multiple downloads through proxy farm (varying IPs)
+           fop: note every successful download in spool file "download"
+2010-06-25 fexget: fixed bug download status info update too often
+2010-06-23 fur: better sendmail clone compatibility: 
+                use space instead of comma as address separator
+2010-06-19 fexget: new option -o overwrite mode
+           fexget: use ./$file.tmp for downloading instead of $HOME/.fex/tmp/
+2010-06-16 schwuppdiwupp: edit and select address book entries
+2010-06-15 rup: fixed bug case sensitive recipient address
+2010-06-12 fop: send X-File-ID on HEAD request, too
+           fexget: added support of X-File-ID
+2010-06-11 schwuppdiwupp: (chunked) multi-POST for proxy with 4 GB limit
+           schwuppdiwupp: X-File-ID support
+2010-06-08 fup,fexsend: (chunked) multi-POST for proxy with 4 GB limit
+2010-06-06 fup,fop,fexsend: protocol extension X-File-ID (contains mtime
+                            of file) is the successor of X-Size for more 
+                            reliable resume function 
+2010-06-02 schwuppdiwupp: added proxy support
+2010-05-31 fexsend: fixed bug windows path elements in filename
+2010-05-30 better server proxy support: 
+           AKEY and SKEY no longer rely on client ip 
+2010-05-29 fexsend: added proxy support
+2010-05-28 fur: allow registration confirmation more than once
+2010-05-27 fexsend: added option -b bounce (copy-forward)
+2010-05-26 fup,foc: added copy-forward feature
+2010-05-20 fexsend: fixed bug uninitialized value with option -@
+           fur: fixed bug $main::admin not declared
+2010-05-17 fexsend: added option -H for hints
+           fexsend: added option -A for edit server address book
+2010-05-16 fexsend: added HTTPS proxy support
+           fup: fixed bug uninitialized value (line 1059)
+2010-05-13 fup: fixed bug ignored KEEP and AUTODELETE options for groups
+2010-05-12 fup: fixed bug ignored autodelete option from ADDRESSBOOK
+2010-04-30 fup: fixed bug uninitialized value with CHECKRECIPIENT
+           fexsend: no SID for https
+2010-04-28 fup: fixed bug case sensitiv group addresses
+2010-04-27 fexsend: fixed bug ignored server address book options
+           fexsend: displays recipients and options before starting post
+           fup: fixed bug ignored server address book autodelete option
+2010-04-26 fexsrv: log all HTTP headers (no more ignore list)
+2010-04-25 fexsrv: accept HTTP header with continuation lines
+2010-04-22 fex.pp: added htdocs/header.html support
+           fex.ph: added variable @H1_extra organization link and logo
+2010-04-20 fexsrv,fexsend: HTTP header X-Timeout (info server->client)
+           fexsrv: logging with locking
+2010-04-19 fexsend: removed broken option -A and replaced it with more
+                    flexible feature "." for recipient address
+           fexsend: fixed bug dies if sender is subuser (ADDRESS_BOOK error)
+           fup: fixed bug no COMMENT in notification email
+           dop: fixed bug error output with non GNU file command
+2010-04-17 fexsend,fexget: added option -i for alternative accounts or servers
+2010-04-12 fexsend: new verbose output format --> <--
+2010-04-11 fexsend: added option -Q quota query
+2010-04-09 fup,fac: added quota support
+2010-03-25 fup: fixed bug "Insecure dependency" when using AKEY parameter
+                (eg: sending a second file)
+           fup_template.html: fixed bug upload status window always shows
+                              "ERROR: no file data received"
+2010-03-24 fexsend: fixed bug dies if there is no server address book
+2010-03-22 FIX.jar: fixed bug interpret HTTP response "200 OK" as error.
+2010-03-20 fup,fop: set mtime on user directory for last successfull access
+2010-03-19 fexsend: fixed bug abort on short address if there is no server
+                    address book
+           fex_cleanup: better cleanup for dkeys directory
+2010-03-18 fup: fixed bug cannot DELETE with group recipient
+2010-03-17 fup: fixed bug wrong success message on aborted uploads
+           fop: fixed bug cannot handle @group names
+2010-03-16 fup,fuc: fixed bug mixed case in F*EX group names and addresses
+2010-03-14 fex.pp: do not send notification emails on empty files
+          fup,fuc: added F*EX groups
+2010-03-12 fup,fop: fixed bug case sensitiv FROM and TO addresses
+2010-03-05 fup: fixed bug aliases are not accepted with fop_auth
+2010-03-04 fexsrv: use CGI login if it exists as start-page
+2010-02-26 fexsend: first check server address book, then mutt aliases
+           fop: do not terminate session after ADDRESSBOOK request
+2010-02-18 fexget: fixed bug always append existing file, ask for overwriting
+2010-02-08 fexsrv: fixed bug uninitialized value in substitution (line 229)
+           fex.pp: better qmail compatibility (space separated addresses)
+2010-02-07 fac: fixed bug uninitialized $EDITOR environment variable
+2009-12-28 fup,fop,fexsend: protocol extension X-Size for more reliable
+                            resume function (checks size of file)
+2009-12-09 FIX.jar: can send more than one file (in zip archive)
+2009-12-04 fex_cleanup: fixed bug send unnecessary reminder on AUTODELETE=NO
+2009-11-26 fexsend,fexget,fup: added -z option for logfile inquiry
+2009-11-13 fup,fuc: keep and autodelete options in server address book
+2009-11-12 fexsrv: support for HTTP/1.0 persistant connections (for proxys)
+                   PID:SID in fexsrv.log CONNECT string (debugging help)
+2009-11-06 added error.log
+2009-11-03 fex_cleanup: also cleanup $SPOOL/.reg directory
+2009-10-23 fup: added X-FEX-Filesize header in notification email
+2009-10-18 fup: also test if $FROM is a valid email address
+2009-10-11 fex_cleanup: fixed bug no cleanup at all on AUTODELETE=NO
+2009-09-25 fex.pp: added X-Mailer in sent mails to please stupid spamfilter at
+                   hotmail or gmail
+2009-09-02 fur,fex.pp: fixed bug not removing quote chars in qqq strings
+2009-08-28 rup: fixed bug UTF-8 file names
+2009-08-25 FIX.jar: accepts parameters TO, AKEY and SKEY (for subusers)
+2009-08-24 fex_cleanup: fixed bug warning unitialized variable
+2009-08-17 fex_cleanup: fixed bug typo mitime --> mtime
+2009-08-12 fup: fixed bug "send another file" for subusers
+2009-08-10 fexsend: fixed bug timeout on big archives
+2009-07-27 to and from addresses in spool are now always localpart@domain,
+           install contains automatic spool converter
+           fup: fixed bug short aliases address list mismatch
+2009-07-24 fup,fex_cleanup: fixed bug delete all files for multiple
+                            recipients after any download
+           fexget: fixed bug delete local file before download
+2009-07-20 fup: added autodelete and keep hack for HTML form
+2009-07-18 fup: fixed bug leading . in file directory name
+           fup: added CHECKRECIPIENT support
+           fup: code cleanup, new 3-stage user interface
+           fexsend: added CHECKRECIPIENT feature
+2009-07-17 fexget: fixed bug wrong UTF8 handling
+2009-07-16 fop: fixed bug sending wrong file size if TO or FROM has
+                uppercase chars ==> resuming upload did not work
+2009-07-11 fup: can select more than one address from address book
+2009-07-08 fup,fex.pp: fixed bug wrong download URLs
+2009-07-07 new spool directory layout $TO/$FROM/urlencode($FILENAME)
+           fup: be more restrictive in accepting (illegal) parameters values
+           fup,fuc: subuser access key name is now SKEY (KEY is depreciated)
+           rup: new HTML layout, fixed bug in file select box
+2009-07-06 fup: substitute all control characters in file name and comment 
+                with "_"
+2009-07-02 better install script, guesses IP
+2009-06-29 changed spool directory layout 
+           $TO/$FROM/$FILE --> $TO/$FROM/md5h($FILENAME)
+           to avoid filename collisions
+2009-06-28 added mailman authorization mma
+           better address-book integration in fup
+2009-06-26 FIX.jar: fixed several bugs, now working with Windows Vista, too
+2009-06-25 added fup_template.html as an example for customizing upload page
+2009-06-22 fup,fexsend,fexget: LIST also shows COMMENT
+2009-06-10 default timeout 10 s --> 30 s
+2009-06-01 fexsend: show transfer status on STDERR (also for xx)
+2009-05-31 fexsend,fexget: show transfer rate (kB/s)
+2009-05-29 fexsend: auto-reconnect after SIGPIPE (link failure, etc)
+2009-05-26 fexsrv: ignore HTML anchors in GET requests (from stupid msnbot)
+2009-05-17 fup: check if there is enough free space in spool
+2009-04-07 new perl based install; requires server IP for xinetd binding
+2009-03-25 fexget: fixed bug saving failed if on other partition then FEXHOME
+           fexget: fixed bug calculated wrong transfer rate
+           fexget: changed default answers to more secure values 
+2009-03-24 fexsend: new option -l for listing sent files
+           fup: support for listing sent files
+           fex.pp: default charset is now UTF-8 in HTTP reply
+2009-03-16 fur: fixed bug no lower case transformation for user and domain
+2009-03-05 fop: fixed bug no parallel download possible on multiple recipients
+2009-03-03 dop: send Last-Modified HTTP header (java needs it)
+2009-02-27 fuc: send information emails to sub-users by click-on-address
+2009-02-22 fop: fixed bug download failed without FROM parameter
+2009-02-20 test for /usr/lib/sendmail and /usr/sbin/sendmail
+2009-02-18 fop: fixed bug file size query for alias recipient
+           fexget: added option -a to get address-book from server
+2009-02-17 fup,fuc: better linking
+2009-02-14 fup: first send notification emails, then send HTTP 200 OK to client
+           fup: accept ADDRESS_BOOK as upload
+2009-02-13 fup,foc,fuc: added ADDRESS_BOOK support
+           added fix and FIX.jar (Java applet client)
+2009-02-11 fop: fixed bug file size request with multiple $to gives always 0
+                (no upload resume possible with multiple recipients)
+           fop: check for valid recipient address (in file path)
+                ==> early abort possible when client uses illegal address for 
+                    upload (resume-HEAD-request)
+2009-02-10 fur: catch errors from sendmail(clone) and save them to $log
+2009-02-09 fexsrv: fixed bug wrong log sequence in debug files
+2009-02-04 fup: fixed bug cannot delete files by web interface
+2009-02-03 fexsrv: fixed bug wrong download URL by HTTP_HOST from client request
+2009-01-31 fexsrv: fixed bug handling of missing trailing / in doc requests
+2009-01-30 rup: fixed bug wrong download URL in notification email
+2009-01-26 fexsend: archive format 7z and zip with default compression
+           fup,fuc,foc,rup: link to F*EX start page in top header
+2009-01-21 fuc: URL for subusers with KEY parameter
+           fup.fuc,foc: fixed bug wrong AKEY lookup
+2009-01-20 fexsrv: better handling of URLs with trailing / (==> index.html)
+           fop: fixed bug endless loop with fop_auth mode
+2009-01-13 fup,fop: support for MIME-file types
+           fexsend: added option -M for MIME-file to be displayed in webbrowser
+                    on download
+2009-01-04 fup: increase minimum timeout to 10 s
+2008-12-26 fup: do not allow re-upload (overwrite) if file is in download
+                process
+2008-12-23 sexsend,sexget: added HTTPS/SSL support
+2008-12-21 fup: fixed bug removing old autodelete and error files failed
+2008-12-20 added logwatch
+2008-12-18 fexget: fixed bug responsiveness on slow links 
+           fexget: fixed bug save file name for archives
+2008-12-12 fexget: better responsiveness on slow links (modem, ISDN)
+           fup: added warning for incompatible clients (konqueror, etc)
+2008-12-11 fexsend: allow comments in ID file
+2008-12-03 fup,fex.pp: fixed bug UTF-8 subject in notfication email
+2008-12-02 fexsend: better responsiveness on slow links (modem, ISDN)
+          fop: send UTF-8 filename in HTTP header Content-Disposition
+           fexget: save original filename (parse HTTP header)
+2008-11-28 fexserv: added special FlashGet (download sucker) brake
+          html error messages now with HTTP_HOST und server-time info
+2008-11-27 added htdocs/version and htdocs/tools.html
+           added fexsend, fexget, sexsend, sexget to htdocs/download
+           dop: fixed bug symlink of symlink leads to hangup
+           fop: teergrub download managers and other suckers
+2008-11-26 fop: with URL parameter ?KEEP file can be downloaded more than once
+           fexget: added option -k for keep on server
+2008-11-24 fex_cleanup: fixed bug $autodelete not defined
+           fexget: added HTTPS/SSL support
+2008-11-22 fexsrv: reject requests with IP hostnames in HTTP Host header
+2008-11-21 fex.ph,fop: $autodelete="DELAY" allows file download many times
+                       (but only from same IP and until next fex_cleanup run)
+           fup,fop: fixed bug options keep and delete autodelay do not
+                    work with spool on NFS
+2008-11-20 fexsend: added HTTPS/SSL support
+           fex.ph: added config variable $autodelete
+           fup: fixed bug subuser cannot send files
+2008-11-19 use md5-hash of $from:$id instead of URL parameters FROM=$from&ID=$id
+           fac: set correct exit status
+2008-11-16 fup: fixed bug DELETE not working 
+           install: do not overwrite lib/fup.pl (perhaps contains site config)
+2008-11-15 fex_cleanup: clean up $SPOOL/.ukeys/, too
+2008-11-14 fup: show "user config" link only after authorization
+2008-11-13 foc,fuc,fup: quick sub-user creation with auto-notification.
+2008-11-10 fexget: fixed bug query <> instead of <STDIN>
+2008-10-29 fup: do not require HTTP authorization if request already
+                contains ID (methode used by xx)
+2008-10-28 fex.pp: fixed bug $warning not defined
+           fup: fixed bug do not allow subuser in fop_auth mode
+2008-10-27 install: do not overwrite existing htdoc/index.html
+           fup: fixed bug resend (SEEK) leads to HTTP error 666
+2008-10-26 fexsrv: accept HTTP request with absolute URLs (http://...), too
+2008-10-23 fexsrv: fixed bug continue connect logfile entry
+2008-10-17 fexsrv: fixed bug keep_alive with HTTP/1.0
+           fexsrv: fixed bug wrong warning in debug mode with empty line
+2008-10-07 fexsrv: moved TIMEOUT message to debug.log
+2008-10-06 dop: fixed bug opening file (did not deliver any file!)
+           dop: implemented HTTP keep-alive (delivering more than one
+                document per session)
+2008-10-04 if config variable $fop_auth is set, download requires
+           authentication and upload is restricted to registered users
+2008-10-02 dop: declare exectuable scripts as application/octet-stream
+           fup: added link to Windows client schwuppdiwupp.exe
+2008-09-29 write upload speed to upload directory
+2008-09-17 fac: fixed bug locating FEXLIB
+2008-09-12 fup: added config lib/fup.pl
+2008-09-05 fexsend: added rudimentary windows support
+2008-09-03 dop: redirect on relative non-parent symlinks
+2008-08-31 added fur (F*EX User (auto-) Registration)
+2008-08-25 will die when no hostname is available
+2008-08-21 added fexsend to htdocs/download
+           fup: added ID mail sendback option
+2008-08-20 fac: added -l option
+2008-08-19 fexsrv: fixed bug SSL handling
+2008-08-15 fup,fuc,fexsrv: dynamic protocol detection (HTTP/HTTPS)
+2008-08-14 fup: fixed bug login possible with wrong login data (but no upload)
+2008-08-13 fup: showstatus terminates immediately when empty file was uploaded
+           fup: showstatus shows error message on illegal recipient address
+                or when no file was uploaded 
+                (nececessary for stupid Internet Explorer!)
+2008-08-11 splitted debugfiles with time stamp in filename 
+           fex_cleanup: clean up aborted uploads, .ukeys/ and .debug/, too
+           fexsend,fexget: allow more than one file (with all options)
+2008-08-08 fup: eliminate superfluous newlines in logfile on error handling
+           changed bareword filehandles to indirect filehandles
+2008-08-06 fup: decode %NUMBERs in file names from POST
+2008-08-02 fexsend: bug fix -A option and argument handling
+2008-08-01 fup: regular users can change the recipient in the upload form,
+                sub users can not
+2008-07-31 fup: fixed bug internet explorer not showing upload status window
+           fup: fixed bug with id / special id / real id mixup
+           fuc: nearly complete rewrite, better user interface
+2008-07-30 fup: fixed bug when account is a symlink
+           fup: fixed bug in authentication of subusers
+2008-07-03 fop: workaround for Internet Explorer download bug
+2008-07-02 fup,fop: switched default charset from ISO-8859-1 to UTF-8
+           fup: uid for showstatus synchronization
+2008-06-21 fexget: downloading without wget, file number as argument
+2008-06-20 fexget,fop: added DELETE option
+           fexsend: send more files in one run
+2008-05-30 added missing sex to distribution
+           fexsend: added -A archiv to yourself option
+2008-05-28 fup: fixed bug in LIST and DELETE commands
+2008-05-27 fexsrv: correct HTTP redirect on missing trailing / in URL
+2008-05-26 sex,sexsend: better public mode
+2008-05-24 sex,sexsend: added text mode option
+2008-05-23 added missing foc and rup to distribution
+2008-05-20 fexsend: fixed bug in list parsing (-l option)
+          dop: fixed bug in file type determining on symbolic links
+2008-05-15 fexsrv,dop: fixed bug in HTTP keep_alive multi-requests
+2008-05-02 fexsrv,dop: support for HTTP keep_alive multi-requests
+           fexsrv: more robust header parsing (ignore superfluous spaces)
+2008-04-28 added support for HTTP keep_alive
+          fexsrv: added SID (session ID) support
+          fexsend: encrypt ID with MD5 and SID
+2008-04-20 added foc and rup
+          fop: return apropriate error message when file has been (auto)deleted
+                or is expired; error message is kept 3*keep_default days
+2008-04-19 install: do not overwrite old fex.ph, but create fex.ph_new instead
+2008-04-18 fup: fixed bug filename with path in notification email
+2008-04-16 fexsrv,fop,dop: implemented HTTP HEAD
+2008-04-14 renamed cgilaunch to fexsrv
+           fup: do not send notify-mail if file already exists (overwrite mode)
+           fup: do not accept file if authentication fails
+           fup,fop,fexsend: new secure download URL scheme with random dkey
+2008-04-11 fup: fixed bug in upload bar with 8-bit file names
+           fex_cleanup: fixed bug not removing aborted uploads
+2008-04-10 added F*EX camel logo
+2008-04-09 added dop (generic document output)
+          install: better infos
+2008-04-08 renamed confusing ID to auth-ID (request by chris@citecs.de)
+           fuc: fixed bug with more than 1 sub-user
+2008-04-07 fup: readded keep parameter (code got lost sometime?)
+          fup: added sender restriction (ALLOWED_RECIPIENTS)
+          fac: added restriction option -r and delete user option -d
+2008-04-06 fup: use Net::DNS instead of external host command
+          fup: more debuglog, fixed wrong error messages
+          added doc/concept doc/FAQ 
+2008-04-02 install: better error handling (patch by chris@citecs.de)
+           more docs and improved logging
+2008-04-01 cgilaunch: fixed bug in determing REMOTE_HOST when using stunnel
+          fexget: added -s streaming option
+           sex,sexsend: added public mode
+2008-03-31 changed project name to F*EX because of name collision with
+          http://freshmeat.net/projects/fex/
+          added sex, sexsend and sexget to distribution
+2008-03-28 xx: changed syntax, now compatible to zz
+          added zz to distribution
+2008-03-27 fup: fixed bug in mail address verification
+2008-03-24 fup,fexsend: show transfer rate in kB if filesize < 2 MB
+          fup: code-cleanup, more comments
+           fex.pp: umask 077
+2008-03-23 fup: fixed bug in using multiple recipients
+2008-03-22 first public release
+2006-11-?? first code
diff --git a/doc/Contribs b/doc/Contribs
new file mode 100644 (file)
index 0000000..9dc8d78
--- /dev/null
@@ -0,0 +1,62 @@
+Beate Herrmann <beate@fex.rus.uni-stuttgart.de>:
+  - artwork 
+
+Sebastian Zaiser <szcode@arcor.de>:
+  - upload status window
+
+Bernd Strehhuber <Bernd.Strehhuber@planb.de>:
+  - dynamic protocol detection
+
+Kurt Jaeger <pi@nepustil.net>:
+  - ipv6 support
+  
+Andre Hafner <andrehafner@gmx.net>:
+  - web admin interface fac
+  - VM
+
+Martin Lauer <martinlauer@gmx.net>:
+  - FAQ Javascript Code
+
+Dominik Greibl <dgreibl@gmx.de>:
+  - java client F*IX
+
+Sebastian Roth <roth@hdm-stuttgart.de>:
+  - many bugfixes and extensions (zip, proxy-support, etc) for F*IX
+
+Daniel Dieckmann <Daniel_Dieckmann@genua.de>
+  - fexget proxy support
+
+Kilian Krause <kilian@debian.org>
+  - Debian package maintainer
+  - SSL security enhancements
+  - annoying, but useful requests ;-)
+
+Hanno Hirsch <superhanno@gmx.de>:
+  - bug hunting
+
+Nicola Fioravanti <muuratsalo@gmail.com> (http://revshell.com):
+  - Penetration tests and security debugging
+
+Waldemar Bronsch <aw.bronsch@gmx.de>:
+  - German localization
+  
+Tobias Gunkel <tobias.gunkel@qumido.de>:
+  - German localization
+
+Hans-Georg Bickel <bickelhg@gmx.de>:
+  - Swabian localization
+  
+Francisco Ruiz <fjrbas@yahoo.es>:
+  - Spanish localization
+
+Anton Meixome <meixome@certima.net>:
+  - Galician localization
+  
+Vanni Piagno <vpiagno@gmail.com>:
+  - Italian localization
+  
+Michal Simunek <michal.simunek@gmail.com>:
+  - Czech localization
+
+Jean-Baptiste Denis <jbdenis@pasteur.fr>:
+  - French localization
diff --git a/doc/IPv6 b/doc/IPv6
new file mode 100644 (file)
index 0000000..3861b17
--- /dev/null
+++ b/doc/IPv6
@@ -0,0 +1,17 @@
+To enable IPv6 on the server you need:
+
+  - The Perl module Socket6
+    You can install it on Debian or Ubuntu with: 
+    apt-get install libsocket6-perl
+
+  - Add to /etc/xinetd.d/fex the line:
+       flags = IPv6
+    and remove the "bind" option line.
+    Restart xinetd.
+
+
+To enable IPv6 for fexsend, fexget and sexsend you need:
+
+  - The Perl module Net::INET6Glue::INET_is_INET6
+    You can install it on Debian or Ubuntu with: 
+    apt-get install libnet-inet6glue-perl
diff --git a/doc/Licence b/doc/Licence
new file mode 100644 (file)
index 0000000..4108934
--- /dev/null
@@ -0,0 +1,120 @@
+F*EX (Frams' Fast File EXchange) is a service to send big (large, huge,
+giant, ...) files from a user A to a user B via internet.
+
+Copyright (C) 2006-2014 Ullrich Horlacher <framstag@rus.uni-stuttgart.de>
+
+YOU ARE NOT ALLOWED TO USE THIS SOFTWARE FOR MILITARY PURPOSES OR WITHIN
+MILITARY ORGANIZATIONS! THIS INCLUDES ALSO MILITARY RESEARCH AND
+EDUCATION, PARAMILITARY AND TERRORISTIC ORGANIZATIONS!
+
+This program is free software: you can redistribute it and/or modify it
+under the terms of the Perl Artistic Licence:
+
+"Package" refers to the collection of files distributed by the Copyright
+Holder, and derivatives of that collection of files created through
+textual modification.
+
+"Standard Version" refers to such a Package if it has not been modified,
+or has been modified in accordance with the wishes of the Copyright Holder
+as specified below.
+
+"Copyright Holder" is whoever is named in the copyright or copyrights for
+the package.
+
+"You" is you, if you're thinking about copying or distributing this
+Package.
+
+"Reasonable copying fee" is whatever you can justify on the basis of media
+cost, duplication charges, time of people involved, and so on. (You will
+not be required to justify it to the Copyright Holder, but only to the
+computing community at large as a market that must bear the fee.)
+
+"Freely Available" means that no fee is charged for the item itself,
+though there may be fees involved in handling the item. It also means that
+recipients of the item may redistribute it under the same conditions they
+received it.
+
+1. You may make and give away verbatim copies of the source form of the
+   Standard Version of this Package without restriction, provided that you
+   duplicate all of the original copyright notices and associated
+   disclaimers.
+
+2. You may apply bug fixes, portability fixes and other modifications
+   derived from the Public Domain or from the Copyright Holder. A Package
+   modified in such a way shall still be considered the Standard Version.
+
+3. You may otherwise modify your copy of this Package in any way, provided
+   that you insert a prominent notice in each changed file stating how and
+   when you changed that file, and provided that you do at least ONE of
+   the following:
+
+a) place your modifications in the Public Domain or otherwise make them
+   Freely Available, such as by posting said modifications to Usenet or an
+   equivalent medium, or placing the modifications on a major archive site
+   such as uunet.uu.net, or by allowing the Copyright Holder to include
+   your modifications in the Standard Version of the Package.
+
+b) use the modified Package only within your corporation or organization.
+
+c) make other distribution arrangements with the Copyright Holder.
+
+4. You may distribute the programs of this Package in object code or
+   executable form, provided that you do at least ONE of the following:
+
+a) distribute a Standard Version of the executables and library files,
+   together with instructions (in the manual page or equivalent) on where
+   to get the Standard Version.
+
+b) accompany the distribution with the machine-readable source of the
+   Package with your modifications.
+
+c) give non-standard executables non-standard names, and clearly document
+   the differences in manual pages (or equivalent), together with
+   instructions on where to get the Standard Version.
+
+d) make other distribution arrangements with the Copyright Holder.
+
+5. You may charge a reasonable copying fee for any distribution of this
+   Package. You may charge any fee you choose for support of this Package.
+   You may not charge a fee for this Package itself. However, you may
+   distribute this Package in aggregate with other (possibly commercial)
+   programs as part of a larger (possibly commercial) software
+   distribution provided that you do not advertise this Package as a
+   product of your own. You may embed this Package's interpreter within an
+   executable of yours (by linking); this shall be construed as a mere
+   form of aggregation, provided that the complete Standard Version of the
+   interpreter is so embedded.
+
+6. The scripts and library files supplied as input to or produced as
+   output from the programs of this Package do not automatically fall
+   under the copyright of this Package, but belong to whoever generated
+   them, and may be sold commercially, and may be aggregated with this
+   Package. If such scripts or library files are aggregated with this
+   Package via the so-called "undump" or "unexec" methods of producing a
+   binary executable image, then distribution of such an image shall
+   neither be construed as a distribution of this Package nor shall it
+   fall under the restrictions of Paragraphs 3 and 4, provided that you do
+   not represent such an executable image as a Standard Version of this
+   Package.
+
+7. C subroutines (or comparably compiled subroutines in other languages)
+   supplied by you and linked into this Package in order to emulate
+   subroutines and variables of the language defined by this Package shall
+   not be considered part of this Package, but are the equivalent of input
+   as in Paragraph 6, provided these subroutines do not change the
+   language in any way that would cause it to fail the regression tests
+   for the language.
+
+8. Aggregation of this Package with a commercial distribution is always
+   permitted provided that the use of this Package is embedded; that is,
+   when no overt attempt is made to make this Package's interfaces visible
+   to the end user of the commercial distribution. Such use shall not be
+   construed as a distribution of this Package.
+
+9. The name of the Copyright Holder may not be used to endorse or promote
+   products derived from this software without specific prior written
+   permission.
+
+10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
+    WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+    MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/doc/SEX b/doc/SEX
new file mode 100644 (file)
index 0000000..b7a4a7d
--- /dev/null
+++ b/doc/SEX
@@ -0,0 +1,119 @@
+SEX is a F*EX companion: Stream EXchange
+
+You can imagine SEX as network wide UNIX pipes with a relay between. This
+can be usefull for piping data from A to B where A and B cannot establish
+a direct connection, but both can connect by HTTP to the SEX-server. SEX
+is a synchronous service in conjuction to F*EX which is asynchronous.
+
+For seamless integration into the UNIX tool chain, there are the
+shell-tools sexsend and sexget.
+
+Using web browsers for sexing will not work, because they cannot handle
+streams in a pipe. So far, there are only the UNIX clients. Feel free to
+implement other clients for other operating systems :-)
+
+Authentication is the same as with F*EX.
+
+Example:
+
+root@lanldap:/var/log: tail -f syslog | sexsend framstag@rus.uni-stuttgart.de
+
+framstag@blubb:/tmp: sexget | grep ldap
+
+
+If you need encryption, then use the standard UNIX toolbox and add a
+appropriate program to the pipe, for example: openssl bf
+
+
+Tips for using SEX for file exchange:
+
+I have a lot of (administrative) accounts on even more hosts, where I have
+to exchange a lot or big files. These hosts often cannot make direct
+tcp-connects to each other, because of IP-filters, firewalls or missing
+server software. Only outgoing connections are possible.
+
+Therefore NFS, ssh or even ftp are no option.
+
+Since today my solution was F*EX:
+
+1) on source host send the files within a container to the F*EX-server:
+
+       fexsend -a container.tar file1 file2 ... .
+
+2) read or copy the FEXURL with mouse
+
+3) on destination host get container.tar from F*EX-server and extract it:
+
+       fexget FEXURL 
+       tar xvf container.tar
+       rm container.tar
+
+
+Backdraws:
+
+- a lot of manually work
+- creation of container.tar needs (a lot of) time
+- container.tar needs (too much) disk space on source and destination host
+  and on the F*EX-Server
+  
+
+Small solution:
+
+Usage of xx (wrapper for fexsend) eliminates manually handling of
+container.tar. But it will be still created, only automatically.
+The ressource problems are still there.
+
+
+The new sexy solution:
+
+I use SEX (Stream EXchange) instead of FEX (File EXchange)!
+
+There is no more a temporary container.tar, but the files are transfered
+as a stream with the new client sexxx!
+
+An example (*):
+
+On the source host:
+
+       root@obertux:~/bin# sexxx .
+       streaming:
+       ./
+       ./autopatch
+       ./subsys
+       ./pwsync
+       ./cspread
+       ./fspread
+       ./spread
+       ./iptconf
+       ./esxbackup
+       ./esxlist
+       ./getxx
+       ./rscreen
+
+
+On the destination host:
+
+       framstag@flupp:/tmp: sexxx
+        extracting from stream:
+       drwxr-xr-x root/root         0 2010-08-06 12:48 ./
+       -rwxr-xr-x framstag/users  170 2010-01-20 13:26 ./autopatch
+       -rwxr-xr-x root/root       336 2006-02-02 15:15 ./subsys
+       -rwxr-xr-x root/root       278 2010-01-18 23:38 ./pwsync
+       lrwxrwxrwx root/root         0 2009-11-03 15:49 ./cspread -> spread
+       lrwxrwxrwx root/root         0 2009-11-03 15:49 ./fspread -> spread
+       -rwxr-xr-x root/root     10354 2010-02-12 14:49 ./spread
+       -rwxr-xr-x root/root      1380 2010-01-14 08:54 ./iptconf
+       -rwxr-xr-x root/root       116 2010-05-04 18:36 ./esxbackup
+       -rwxr-xr-x root/root       344 2010-05-06 13:33 ./esxlist
+       -rwxr-xr-x root/root        93 2010-08-06 12:32 ./getxx
+       -rwxr-xr-x 1000/users      173 2009-11-26 13:40 ./rscreen
+
+
+Now I can transfer any files from a new installed (VM) server to my
+admin workstation behin a firewall. Or the other direction.
+
+
+(*) The example above has only a few kB, but one can use sexxx for many GB
+    or even TB!
+    On my VMs I get a throughput of 90 MB/s.
+
diff --git a/doc/SSL b/doc/SSL
new file mode 100644 (file)
index 0000000..fccd448
--- /dev/null
+++ b/doc/SSL
@@ -0,0 +1,61 @@
+# http://www.stunnel.org/faq/certs.html
+
+# execute this as root!
+
+mkdir /home/fex/etc
+cd /home/fex/etc/
+
+openssl req -new -x509 -days 9999 -nodes -out stunnel.pem -keyout stunnel.pem
+# see http://www.infodrom.org/Debian/tips/stunnel.html
+dd if=/dev/urandom count=2 | openssl dhparam -rand - 1024 >> stunnel.pem
+openssl x509 -text -in stunnel.pem
+chmod 600 stunnel.pem
+
+cat <<EOD>stunnel.conf
+cert = /home/fex/etc/stunnel.pem
+sslVersion = all
+TIMEOUTclose = 1
+exec = perl
+execargs = perl -T /home/fex/bin/fexsrv stunnel
+EOD
+
+case $(lsb_release -a 2>/dev/null) in 
+  *CentOS*) echo 'fips = no' >>stunnel.conf;;
+esac
+
+chown -R fex .
+
+stunnel=$(which stunnel4)
+if [ -z "$stunnel" ]; then
+  echo "no stunnel found" >&2
+else
+
+  cat <<EOD>/etc/xinetd.d/fexs
+# default: on
+# description: fex web server with SSL
+# note: only possible on port 443!
+service fexs
+{
+        socket_type     = stream
+        wait            = no
+        type            = unlisted
+        protocol        = tcp
+        port            = 443
+        cps             = 5 10
+        user            = fex
+        groups          = yes
+        server          = $stunnel
+        server_args     = /home/fex/etc/stunnel.conf
+        nice            = 0
+        disable         = no
+}
+EOD
+
+  /etc/init.d/xinetd restart
+  echo 'To enforce https, add to fex.ph:'
+  echo '$force_https = 1;'
+
+fi
+
+# Hint: on some systems stunnel works not well with xinetd
+#       you can also run stunnel without xinetd, in server daemon mode
diff --git a/doc/concept b/doc/concept
new file mode 100644 (file)
index 0000000..ead5387
--- /dev/null
@@ -0,0 +1,697 @@
+F*EX (Frams' Fast File EXchange) is a service to send big (large, huge, giant,
+...) files from a user A to a user B, anywhere on the internet.
+
+The sender uploads the file to the F*EX-server and the recipient automatically
+gets a notification email with a download-URL.
+
+Main features of F*EX
+
+  * file transfer of virtually unlimited file size
+  * recipient and sender only need an email program and a web browser - of any
+    kind, they do not have to install any software
+  * RESEND and REGET for resuming after link failures at last sent byte
+  * auto-notification of recipient
+  * auto-deletion after download
+  * auto-deletion after expiration date (default: 5 days)
+  * full-users can create one time upload URLs for foreign users
+  * full-users can create subusers, who can send only to this full-user
+  * full-users can create groups, an analogy to mailing lists, but for files
+  * self registration possible for internal and external users
+  * maintenance-free: no admin interaction necessary 
+  * sending to multiple recipients needs storage on the server only once
+  * quotas for sending and receiving
+  * F*EX is a HTTP web-service and needs no firewall-tunnels
+  * support for (personal or public) streams, too (SEX : Stream EXchange)
+  * for real UNIX users, there are the shell programs fexsend and fexget to
+    avoid annoying web browser usage and for full scripting support
+  * adminstration by CLI or Web
+  * about 10 times faster than apache
+  * authentification modules for LDAP, RADIUS, POP, mailman
+  * server based user address books
+  * (reverse) proxy support
+  * "public upload" similar to "anonymous ftp" possible
+  * "anonymous upload" without registration for enabled IP addresses possible
+  * localization for many languages
+  * authentification by local user database, RADIUS, LDAP, mailman or POP
+  * user and admin can throttle bandwith usage
+  * admin can restrict access based on email or IP addresses
+  * server available for UNIX and Windows hosts
+  * protocol and source-code free available
+
+The end user normally uses F*EX with his web browser and the URLs
+http://YOURFEXSERVER/fup (file upload) and http://YOURFEXSERVER/fop (file
+download). 
+
+
+F*EX is not an anonymous service (exception: "public upload" and
+"anonymous upload", see later).
+The admin must register the user with his email address and auth-ID
+string (low security password). This task can be done either with the CLI
+program "fac" (F*EX admin control) or http://YOURFEXSERVER/fac
+
+Alternativly the users can register theirselves with http://YOURFEXSERVER/fur
+(F*EX user registration), if the admin allows them to do so. This is done by
+setting the variables @local_domains and @local_hosts in FEXHOME/lib/fex.ph
+Example: 
+
+       @local_hosts = qw(127.0.0.1 10.10.100.0-10.10.255.255);
+       @local_domains = qw(rus.uni-stuttgart.de flupp.org);
+
+You can also use wildcard "*" in a domain, for example:
+
+       @local_domains = qw(uni-stuttgart.de *.uni-stuttgart.de);
+
+Then all subdomains are allowed.
+
+
+In addition the administrator can allow external users to fex (only) to
+internal users. The F*EX functioning scheme is close related to email
+where any external user can send email to an internal user. This is also
+possible with F*EX. These users are then called "restricted users". The
+administrator has to define via configuration variable @local_rdomains
+the local recipient domains. For example:
+
+@local_rdomains = qw(YOURDOMAIN *.YOURDOMAIN OTHERDOMAIN);
+
+
+F*EX full users can create one time upload URLs with
+http://YOURFEXSERVER/fuc 
+
+With such a URL a foreign user can send this F*EX full user a single file.
+
+F*EX full users can theirselves register "subusers" with
+http://YOURFEXSERVER/fuc 
+
+Subusers can only fex to their full-user, not to any others, and they
+cannot create other subusers.
+
+
+F*EX full users also can create groups, an analogy to mailing lists, but
+for files: every member of a group can send files to the group address.
+
+
+The administrator can set with fac the CAPTIVE flag for a full user. Then
+he cannot change anymore his default settings (KEEP, AUTODELETE, etc)
+
+
+The F*EX user is the same as the "sender" in the fup CGI and the "from"
+parameter in the F*EX URL. 
+
+The (confusing) naming scheme is historically based :-)
+
+
+The recipient (normally) does not need any registration. He authenticates
+himself with his unique download-URL which he gets in the notification
+email.
+
+
+You do not need to build F*EX URLs manually, they are generated by the
+F*EX programs. 
+
+
+A F*EX session is based on unique URL parameters or POST variables (FROM,
+TO, ID, etc). For security reasons the URL parameters FROM and ID become a
+MD5-hashed access key link (AKEY for regular users, SKEY for subusers,
+GKEY for groupusers), generated by the CGIs, to prevent that a third
+person could easily read these parameters from the user webbrowser URL
+address field. AKEY and SKEY are used as optional cookies, too, for
+extending the session life.
+
+A SKEY is made of md5_hex("$mainuser:$subuser:$subuserid")
+A GKEY is made of md5_hex("$mainuser:$groupname:$groupuser:$groupuserid")
+
+Note: the AKEY, SKEY and GKEY always can be stolen by a network sniffer!
+If you need true security, then you have to use https (SSL) instead of
+http! 
+
+After download the file will be deleted after a grace time of 1 minute.
+This grace time allows a recipient to get the file again if he had
+problems in saving it. 
+
+With the fexsend client the sender can change this behavior:
+
+option -D means "delay autodelete": do not delete the the file directly
+after download, but with the nightly fex_cleanup cronjob. More downloads
+are possible only from the same client (identified by cookie or ip
+address). 
+
+option -K means "keep file": do not delete the file after download, but
+only after expiration date (normally 5 days). More downloads are possible
+only from the same client (identified by cookie or ip address).
+
+
+If you fex a file to yourself (sender = recipient), then the resulting
+download link is valid for any client and can be downloaded everywhere
+and more than once (until expiration date).
+
+If you want "delay autodelete" to be the default behaviour for all users
+and each transfer then set
+       $autodelete = 'DELAY'; # or 'NO' for no autodelete
+in FEXHOME/lib/fex.ph
+
+In addition, you can add to the "Recipient(s)" field of the fup CGI:
+":autodelete=delay" or ":autodelete=no" or ":keep=x" (where x is the number
+of days). Example:
+Recipient(s): framstag@rus.uni-stuttgart.de:keep=10
+
+These options are also possible in the server address book (see CGI fuc).
+
+If you need more security, then set in fex.ph:
+$fop_auth = 'yes';
+$force_https = 'yes';
+
+With $fop_auth upload is restricted to registered users and download
+requires (HTTP) authorization. The credentials are the F*EX user email
+and auth-ID.
+See extra documentation SSL for using https.
+
+If you want to have encrypted emails, then you need a GPG public key for 
+the user fex. Create it with "gpg --gen-key" (use fex@YOURFEXSERVER as the
+user or the $bcc address from fex.ph if you have set it, see below). Next,
+the user has to upload his public key via webinterface "user config &
+operation control" and "Manage e-mail encryption".
+
+"Public upload" is a feature similar to anonymous ftp: the sender does not
+to be registered, but may send only to predefined recipients via
+http://YOURFEXSERVER/pup
+
+The administrator has to define via configuration variable
+@public_recipients the email addresses for which anyone can upload files
+without registration (auth-ID is not necessary). For example:
+
+@public_recipients = qw(fexmaster@YOURDOMAIN bunny@OTHERDOMAIN);
+
+With "anonymous upload" one can use F*EX without registration or
+authentication. The administrator has to define via configuration variable
+@anonymous_upload for which IP addresses this feature is available.
+
+The administrator can globally forbid certain recipient address via fex.ph
+configuration (example):
+
+@forbidden_recipients = qw(nobody@* *@microsoft.com);
+
+The administrator can also forbid a user to fex to any recipient address,
+but the allowed ones with: fac -r USER
+
+
+By standard installation the base directory FEXHOME is the same as the
+login HOME of user fex, but you can move it if you want. FEXHOME is
+determined by the full path of fexsrv as configured in
+/etc/xinetd.d/fex . Change this when you move FEXHOME!
+
+You can also add (name based) virtual hosts with fac.
+
+Do not give write permission to any other user to any file in FEXHOME or
+below!
+
+
+FEXHOME contains:
+
+       spool/                          spool directory and user data 
+        htdocs/                                directory for generic download files
+        bin/                           directory for programs
+        cgi-bin/                       directory for CGI programs
+       lib/                            directory for library and config files
+        doc/                           additional documentation
+        locale/                                alternative language installation
+       
+
+Files in spool:
+
+       cleanup.log                     log of daily cleanup cronjob
+       dkey.log                        log of download keys
+       dop.log                         log of HTTP document output
+       error.log                       log of errors
+       fexsrv.log                      log of all HTTP requests
+       fop.log                         log of file downloads
+       fup.log                         log of file uploads
+       fur.log                         log of user self registrations
+       sex.log                         log of stream exchanges
+       $user/@                         regular user auth-ID
+       $user/@SUBUSER                  subuser addresses and IDs
+       $user/@ALLOWED_RECIPIENTS       recipients restrictions for this user
+       $user/@ALLOWED_RHOSTS           recipient's hosts restrictions
+       $user/@UPLOAD_HOSTS             upload hosts restrictions
+       $user/@DOWNLOAD_HOSTS           download hosts restrictions
+       $user/@ADDRESS_BOOK             users recipient address book
+       $user/@GROUP                    directory of F*EX user groups
+       $user/@OKEY                     directory with one time upload keys
+       $user/@QUOTA                    sender and recipient quotas
+       $user/@AUTODELETE               autodelete default
+       $user/@KEEP                     keep default
+       $user/@LOCALE                   locale default
+       $user/@CAPTIVE                  user must not change his settings
+       $user/@FEXYOURSELF              user can only fex to himself via 
+                                        web interface
+       $to/$from/$file/upload          file data in upload progress
+       $to/$from/$file/filename        original file name
+       $to/$from/$file/size            original file size
+       $to/$from/$file/useragent       HTTP header User-Agent
+       $to/$from/$file/data            file data after complete upload
+       $to/$from/$file/keep            keep time (autoexpire) in days
+       $to/$from/$file/autodelete      autodelete option: YES NO or DELAY
+       $to/$from/$file/comment         comment in notification email
+       $to/$from/$file/id              file ID (optional)
+       $to/$from/$file/ip              sender ip address
+       $to/$from/$file/notify          reminder notify flag
+       $to/$from/$file/error           error message if file has gone
+       $to/$from/$file/download        log of successful downloads
+       $to/$from/$file/restrictions    IP based download restrictions
+                                        (see $user/@ALLOWED_RHOSTS)
+       $to/$from/$file/dkey            download key
+       $to/$from/$file/locale          locale
+
+As you see, the first directory sometimes means sender and sometimes means
+recipient! It depends on the case of usage (up/download).
+
+BEWARE: for the special senders fexmail and anonymous $to and $from are
+swapped (by fup and fop)! This is to prevent overcrowding of the toplevel
+spool directory with _fexmail_$RANDOM and _anonymous_$RANDOM recipients.
+
+
+A registered full F*EX user is identified by the file $spooldir/$from/@
+Only if this file contains his auth-ID this user is able to send files to
+others. Otherwise he is just an unpriviledged recipient.
+
+You can customize the upload CGI fup by editing FEXHOME/lib/fup.pl
+
+Additional directories in spool:
+
+        .dkeys/                                download keys lookup directory
+        .ukeys/                                upload keys lookup directory
+        .akeys/                                authentication keys lookup directory
+        .skeys/                                subuser keys lookup directory
+        .gkeys/                                groupuser keys lookup directory
+        .xkeys/                                extra download keys lookup directory
+        .debug/                                directory for debug output (optional)
+        .reg/                          temporary data for user selfregistration
+
+The upload keys (UKEY) are necessary to synchronize the file upload by
+HTTP POST with the javascript upload status bar which is an asynchronous
+HTTP GET. The GET process needs to know the corresponding POST process.
+This is done via UKEY, a unique upload identifier.
+
+The download key (DKEY) is a unique identifier for - guess what -
+downloading. It also prevents an attacker to get the file, because only
+the recipient knows the DKEY as part of the download URL from the
+notification email. 
+
+XKEY is an optional extra download key to have a short download URL in
+shape http://YOURFEXSERVER//XKEY
+The XKEY always starts with // and is provided as a COMMENT by the client.
+Example: fexsend schwuppdiwupp.jpg //
+The user has to realize that such URLs have very low security.
+
+If you need to trace a F*EX request, then set 
+       $debug = 1;
+in fex.ph and look in ~/spool/.debug/ for the correspondening files.
+
+The user fex also gets a Bcc of all sent notification emails. This is a
+help for debugging purposes. If you want these Bcc mails to another
+address then set in fex.ph:
+
+       $bcc = 'other@address';
+
+The sender also get a Bcc if he put the string "!bcc!" in the comment
+field.
+
+If you have users within another domain with its own MTA and those users
+want to fex each other, then their MTA probably will reject the
+notification email. To prevent this problem you can set in fex.ph
+@remote_domains = qw(other_domain1 other_domain2 ...);
+In this case "From: $admin" will be used in the notification emails.
+
+F*EX comes with its own web server: fexsrv
+Standard web servers like apache have been proven problematic, either in
+speed or because of a 2-GB-limit. 
+
+It is not possible to use the F*EX CGIs with an alternative web server,
+because the F*EX CGIs need special fexsrv features.
+
+xinetd starts fexsrv for every new connection, which then executes the
+CGIs fup (file upload), fop (file output), fuc (fex user control), foc
+(fex operation control), fac (fex admin control), fur (fex user
+registration) and sex (stream exchange). 
+
+
+SEX has the opposite authorization model of FEX: The sender does not
+need any registration. He can send a stream to any registered SEX user,
+who must identify himself with his auth-ID. This is because a stream does
+not cost resources when it is not bound to a recipient. It will block
+otherwise. The client programs for sexing are sexsend and sexget. You can
+call them also fuckyou and fuckme :-)
+
+
+With the F*EX client fexsend you can have a streaming file transfer with 
+spooling: on client side there is no temporary buffer file (archive), but
+the data is sent directly to the F*EX server. This saves time and disk
+space on the client.
+
+Example for a streaming archive upload:
+
+  fexsend -a music+video.tar *mp3 *avi webmaster@flupp.org
+
+Example for a streaming data upload:
+
+  fpg fex /var/log/* | fexsend -s logs fexmaster@flupp.org
+
+The data streaming is implemented via a HTTP extension: the header
+Content-Length is given -1 which means: unknown length, read until EOF.
+
+For streaming receiving you can use "fexget -s-" or "wget -O-".
+
+
+fexsrv also can do generic document output (via dop) like a normal web
+server. For this, your files must be under FEXHOME/htdocs and they must
+not have the same name as the CGIs under FEXHOME/cgi-bin, because the CGIs
+have priority.
+
+For security reasons, documents to be delivered by dop:
+- the file must be readable by group or world
+- the file must be in FEXHOME/htdocs or a directory specified by @doc_dirs
+- the filename must not start with a "."
+- the filename must not contain a "@"
+- the filename must not end with "~"
+
+To enable HTTP basic authentication, write your access token to a file
+named .htauth which will protect all files in this directory. An user will
+be prompted for this password by his web browser.
+
+To restrict the access to specific client IP addresses, put these IPs into
+a file named .htaccessfrom which will protect all files below this
+directory. You can name single IPs, also as IP ranges (example:
+129.69.0.0-129.69.255.255)
+
+dop is not a regular CGI program (like fup or fop), but a sub-program of
+fexsrv. 
+
+*.html files may contain $VARIABLES$ which will be substituted with the
+value of the corresponding environment variable. See example
+$SERVER_ADMIN$ in FEXHOME/htdocs/index.html
+
+*.html files may contain <<perl-code>> (even multiline) which will be
+evaluated. See example FEXHOME/htdocs/dynamic.html
+This perl-code must not contain '>>' strings itself!
+Pay attention: do not place security relevant data inside << >> because it
+will be delivered to the client if the URL ends with '!'! See example:
+http://fex.rus.uni-stuttgart.de/index.html
+http://fex.rus.uni-stuttgart.de/index.html!
+
+If you want to hide your code, then you have to write a CGI script and
+place it into the cgi-bin directory. Files there will never be delivered
+by dop. You also can do "chmod o-r file.html" to prevent source code
+delivery.
+
+*.html files may contain conditional blocks:
+
+#if (perl-expression)
+   HTML...
+#elseif (perl-expression)
+   HTML...
+#else
+   HTML...
+#endif
+
+which will be evaluated at run-time (#else and #elseif are optional).
+
+*.html files may contain:
+#include "FILE"
+which means: load FILE from current directory at this place in code.
+
+dop also can display a directory listing if this directory contains a file
+named .htindex
+To restrict the listing to specific files, put a matching regular
+expression into .htindex
+Example: echo 'pdf$' > /home/fex/htdocs/doc/.htindex
+
+dop can output gzipped files on-the-fly as a streaming document. For
+example: you have /fex/home/htdocs/big.file but a client requests
+http://YOURFEXSERVER/big.file.gz then a corresponding big.file.gz will be
+delivered.
+
+dop can also output tar, tgz or zip archives on-the-fly as a streaming
+document. Just create a symbolic link with files (or directories) you want
+in this archive and enclose them with ":". The symlink name must end with
+".stream". Example:
+
+http://fex.rus.uni-stuttgart.de:8080/download/sex.tar
+
+fex@fex:~/htdocs/download: ls -l sex*
+lrwxrwxrwx 1 fex fex     7 2011-01-26 11:46 sexget -> sexsend
+-rwxr-xr-x 1 fex fex 12110 2012-08-22 09:55 sexsend
+lrwxrwxrwx 1 fex fex    22 2012-09-01 12:13 sex.stream -> :sexsend:sexget:sexxx:
+lrwxrwxrwx 1 fex fex     7 2012-09-01 11:28 sexxx -> sexsend
+
+It is also possible to create this stream file as a regular file. Then the
+content must be the file names you want in the streaming archive.
+
+Note: you may only use relative paths and without "../" elements.
+
+Note: Files beginning with a . or ending with ~ will not be included in
+the download stream.
+
+cronjob fex_cleanup is run once a day and deletes expired uploads, removes
+inactive accounts and does some other spool houskeeping. See: crontab -l
+
+To detect inactive users you can put in fex.ph:
+$account_expire = "365:notify";
+
+This sends an email to $admin when an user is inactive for 365 days.
+
+Or to delete inactive users you can put in fex.ph:
+$account_expire = "365:delete";
+
+This deletes user accounts automatically which have been inactive for 365
+days. 
+
+Of course you can use any number of days.
+
+Inactive users will get an account reactivation request before.
+
+The address book may contain the optional parameters autodelete and
+keep. Address book example entry:
+
+flupp framstag@flupp.org keep=99 autodelete=no
+
+BUT: these address book parameters are only accepted when using the
+fexsend or schwuppdiwupp client! They will be ignored when using a
+webbrowser!
+
+Nevertheless you may add autodelete and keep parameters using a
+webbrowser, but then they are valid for ALL addresses in the "Recipient"
+field!
+
+The clients fexsend and schwuppdiwupp can MD5-encrypt the auth-ID together
+with a session-ID. The session-ID itself is provided by the server after
+an initial "GET SID" HTTP request. This is not possible through a proxy,
+because most proxies do not support persistant tcp sessions.
+
+See fex-client_2.pdf for the F*EX protocol specification.
+
+To understand and trace the F*EX protocol you can use fexsend with the -v
+option. 
+
+Example (--> means send to server, <-- means receive from server):
+
+framstag@fex:~: fexsend -v X.png framstag@flupp.org
+ID data from /home/framstag/.fex/id: http://fex.rus.uni-stuttgart.de framstag@rus.uni-stuttgart.de XXXX
+Server/User: http://fex.rus.uni-stuttgart.de/framstag@rus.uni-stuttgart.de
+TCPCONNECT to fex.rus.uni-stuttgart.de
+--> GET SID HTTP/1.1
+-->
+<-- HTTP/1.1 201 8p2Y2qa2
+<-- X-Features: SID,CHECKRECIPIENT,GROUPS,QUOTA,FILEID,MULTIPOST,XKEY
+<-- X-SID: 8p2Y2qa2
+<-- X-Timeout: 30
+<-- Content-Length: 0
+<-- 
+--> HEAD /fop/framstag@flupp.org/framstag@fex.rus.uni-stuttgart.de/X.png??ID=MD5H:226e896d0adab86892957aa8158b37ba HTTP/1.1
+-->
+<-- HTTP/1.1 200 OK
+<-- Content-Length: 0
+<-- X-Size: 0
+<-- X-Features: SID,CHECKRECIPIENT,GROUPS,QUOTA,FILEID,MULTIPOST,XKEY
+<-- 
+--> POST /fup HTTP/1.1
+--> Host: fex.rus.uni-stuttgart.de
+--> User-Agent: fexsend (Ubuntu 8.04.4 LTS)
+--> Content-Length: 149935162
+--> Content-Type: multipart/form-data; boundary=JhUOtQ3sgV5ZcHJzrTny523nBFqgUNvSAOUHoRMTdZfGpAjs
+--> 
+--> --JhUOtQ3sgV5ZcHJzrTny523nBFqgUNvSAOUHoRMTdZfGpAjs
+--> Content-Disposition: form-data; name="FROM"
+--> 
+--> framstag@fex.rus.uni-stuttgart.de
+--> --JhUOtQ3sgV5ZcHJzrTny523nBFqgUNvSAOUHoRMTdZfGpAjs
+--> Content-Disposition: form-data; name="TO"
+--> 
+--> framstag@flupp.org
+--> --JhUOtQ3sgV5ZcHJzrTny523nBFqgUNvSAOUHoRMTdZfGpAjs
+--> Content-Disposition: form-data; name="ID"
+--> 
+--> MD5H:226e896d0adab86892957aa8158b37ba
+--> --JhUOtQ3sgV5ZcHJzrTny523nBFqgUNvSAOUHoRMTdZfGpAjs
+--> Content-Disposition: form-data; name="FILESIZE"
+--> 
+--> 149934400
+--> --JhUOtQ3sgV5ZcHJzrTny523nBFqgUNvSAOUHoRMTdZfGpAjs
+--> Content-Disposition: form-data; name="FILE"; filename="X.png"
+--> Content-Type: application/octet-stream
+--> Content-Length: 149934400
+--> X-File-ID: 1283077463
+--> 
+--> (file content)
+--> --JhUOtQ3sgV5ZcHJzrTny523nBFqgUNvSAOUHoRMTdZfGpAjs--
+<-- Location: http://fex.rus.uni-stuttgart.de/fop/CoVhikzk/X.png
+<-- X-Recipient: framstag@flupp.org (autodelete=YES,keep=5)
+<-- 
+
+Comment on the HEAD request above:
+The client fexsend sends it to request whether the file has been sent
+before and if it was successful or not. On the later case, the server
+would reply how many bytes has already been received and the client then
+can send only the missing part. Normally the answer to the HEAD request is
+0 (see above), which means: nothing of this file has been received so far.
+
+But this is only half of the truth :-)
+I have omitted in the example above the requests CHECKRECIPIENT and
+GET ADDRESS_BOOK, to keep it simple in the first run.
+CHECKRECIPIENT is a HTTP POST request to check whether the recpient's
+email address is valid to the server.
+GET ADDRESS_BOOK is a HTTP GET request to check if the recipient with a
+short address (= address without @) is an alias in the senders server
+address book. 
+Please use "fexsend -v" by yourself to see the whole protocol dialoge.
+
+Many HTTP proxies have a POST limit, which is often at 2 GB, but sometimes
+lower. To overcome this limitation a F*EX client (like fexsend) may
+include in the POST the HTTP MIME part "filesize" which indicates the full
+file size. All incoming file POSTs with this file name will be appended
+until the full file size is reached. Of course the client has to POST all
+parts of this file, in the correct order, in asking the server how many
+bytes has already been received. Example:
+
+framstag@fex:/tmp: fexsend -vP wwwproxy.uni-stuttgart.de.de:8080:1024 2GB.tmp .
+ID data from /home/framstag/.fex/id: http://fex.rus.uni-stuttgart.de:8080 framstag@rus.uni-stuttgart.de xxxxxx
+Server/User: http://fex.rus.uni-stuttgart.de:8080/framstag@rus.uni-stuttgart.de
+TCPCONNECT to wwwproxy.uni-stuttgart.de.de:8080
+--> HEAD http://fex.rus.uni-stuttgart.de:8080/fop/framstag@rus.uni-stuttgart.de/framstag@rus.uni-stuttgart.de/2GB.tmp??&ID=xxxxxx HTTP/1.1
+-->
+<-- HTTP/1.1 200 OK
+<-- X-Size: 0
+<-- Content-Length: 0
+<-- Proxy-Connection: Keep-Alive
+<-- Connection: Keep-Alive
+<-- Date: Tue, 08 Jun 2010 15:04:13 GMT
+<--
+TCPCONNECT to wwwproxy.uni-stuttgart.de.de:8080
+--> POST http://fex.rus.uni-stuttgart.de:8080/fup HTTP/1.1
+--> Host: fex.rus.uni-stuttgart.de:8080
+--> User-Agent: fexsend (Ubuntu 8.04.4 LTS)
+--> Content-Length: 1073677143
+--> Content-Type: multipart/form-data; boundary=gqhqeU7FCHMl4ohxJ5knK1y9rF75xYTvabPvZTeWnAhaoDsK
+-->
+--> --gqhqeU7FCHMl4ohxJ5knK1y9rF75xYTvabPvZTeWnAhaoDsK
+--> Content-Disposition: form-data; name="FROM"
+-->
+--> framstag@rus.uni-stuttgart.de
+--> --gqhqeU7FCHMl4ohxJ5knK1y9rF75xYTvabPvZTeWnAhaoDsK
+--> Content-Disposition: form-data; name="TO"
+-->
+--> framstag@rus.uni-stuttgart.de
+--> --gqhqeU7FCHMl4ohxJ5knK1y9rF75xYTvabPvZTeWnAhaoDsK
+--> Content-Disposition: form-data; name="ID"
+-->
+--> xxxxxx
+--> --gqhqeU7FCHMl4ohxJ5knK1y9rF75xYTvabPvZTeWnAhaoDsK
+--> Content-Disposition: form-data; name="COMMENT"
+-->
+--> NOMAIL
+--> --gqhqeU7FCHMl4ohxJ5knK1y9rF75xYTvabPvZTeWnAhaoDsK
+--> Content-Disposition: form-data; name="FILESIZE"
+-->
+--> 2147483748
+--> --gqhqeU7FCHMl4ohxJ5knK1y9rF75xYTvabPvZTeWnAhaoDsK
+--> Content-Disposition: form-data; name="FILE"; filename="2GB.tmp"
+--> Content-Type: application/octet-stream
+--> Content-Length: 1073676288
+--> X-File-ID: 1275549886
+-->
+--> (file content)
+--> --gqhqeU7FCHMl4ohxJ5knK1y9rF75xYTvabPvZTeWnAhaoDsK--
+2GB.tmp : 1023 MB in 73 s (14363 kB/s), chunk #1 : 1023 MB
+<-- HTTP/1.1 206 Partial OK
+<-- X-Message: Partial OK
+<-- Server: fexsrv
+<-- Expires: 0
+<-- Cache-Control: no-cache
+<-- Content-Type: text/html; charset=UTF-8
+<-- Connection: close
+<-- Date: Tue, 08 Jun 2010 15:05:29 GMT
+<--
+TCPCONNECT to wwwproxy.uni-stuttgart.de.de:8080
+--> HEAD http://fex.rus.uni-stuttgart.de:8080/fop/framstag@rus.uni-stuttgart.de/framstag@rus.uni-stuttgart.de/2GB.tmp??&ID=xxxxxx HTTP/1.1
+-->
+<-- HTTP/1.1 200 OK
+<-- X-Size: 2147483748
+<-- X-File-ID: 1275549886
+<-- Content-Length: 1073676288
+<-- Proxy-Connection: Keep-Alive
+<-- Connection: Keep-Alive
+<-- Date: Tue, 08 Jun 2010 15:05:29 GMT
+<--
+TCPCONNECT to wwwproxy.uni-stuttgart.de.de:8080
+--> POST http://fex.rus.uni-stuttgart.de:8080/fup HTTP/1.1
+--> Host: fex.rus.uni-stuttgart.de:8080
+--> User-Agent: fexsend (Ubuntu 8.04.4 LTS)
+--> Content-Length: 1073677254
+--> Content-Type: multipart/form-data; boundary=LofreCkx1GmgWNCHZ9exBQoQhiEF7pXSaPp2KUQVPlnarPAw
+-->
+--> --LofreCkx1GmgWNCHZ9exBQoQhiEF7pXSaPp2KUQVPlnarPAw
+--> Content-Disposition: form-data; name="FROM"
+-->
+--> framstag@rus.uni-stuttgart.de
+--> --LofreCkx1GmgWNCHZ9exBQoQhiEF7pXSaPp2KUQVPlnarPAw
+--> Content-Disposition: form-data; name="TO"
+-->
+--> framstag@rus.uni-stuttgart.de
+--> --LofreCkx1GmgWNCHZ9exBQoQhiEF7pXSaPp2KUQVPlnarPAw
+--> Content-Disposition: form-data; name="ID"
+-->
+--> xxxxxx
+--> --LofreCkx1GmgWNCHZ9exBQoQhiEF7pXSaPp2KUQVPlnarPAw
+--> Content-Disposition: form-data; name="COMMENT"
+-->
+--> NOMAIL
+--> --LofreCkx1GmgWNCHZ9exBQoQhiEF7pXSaPp2KUQVPlnarPAw
+--> Content-Disposition: form-data; name="SEEK"
+-->
+--> 1073676288
+--> --LofreCkx1GmgWNCHZ9exBQoQhiEF7pXSaPp2KUQVPlnarPAw
+--> Content-Disposition: form-data; name="FILESIZE"
+-->
+--> 2147483748
+--> --LofreCkx1GmgWNCHZ9exBQoQhiEF7pXSaPp2KUQVPlnarPAw
+--> Content-Disposition: form-data; name="FILE"; filename="2GB.tmp"
+--> Content-Type: application/octet-stream
+--> Content-Length: 1073676288
+--> X-File-ID: 1275549886
+-->
+--> (file content)
+--> --LofreCkx1GmgWNCHZ9exBQoQhiEF7pXSaPp2KUQVPlnarPAw--
+2GB.tmp : 0 MB in 1 s (128 kB/s), total 2048 MB
+
+
+The default character set for F*EX is UTF-8, because most clients seem to
+use it. The problem is, it is nowhere defined which character set HTTP
+uses itself.
+
+Important for programmers: The perl variables of the F*EX CGI's (fup, fop,
+etc) have their UTF-8 flag turned off. This means, they contain UTF-8 data
+in binary representation.
+
+
+I have additional authentication modules for RADIUS, LDAP, mailman and
+POP. Please email me if you are interested.
+
+framstag@rus.uni-stuttgart.de
diff --git a/doc/debugging b/doc/debugging
new file mode 100644 (file)
index 0000000..d81dfe2
--- /dev/null
@@ -0,0 +1,16 @@
+If your F*EX installation does not work as expected then first look in:
+
+$FEXHOME/spool/fexsrv.log      (log from fexsrv)
+$FEXHOME/spool/fup.log         (log from fup CGI)
+$FEXHOME/spool/fop.log         (log from fop CGI)
+
+If this does not help, set in $FEXHOME/lib/fex.ph :
+
+$debug = 1;
+
+then you will get in $FEXHOME/spool/.debug/ for each download a log file
+with detailed debugging output.
+
+If this does not help at all, contact me :-)
+framstag@rus.uni-stuttgart.de
+
diff --git a/doc/fex-client_1.pdf b/doc/fex-client_1.pdf
new file mode 100644 (file)
index 0000000..a574738
Binary files /dev/null and b/doc/fex-client_1.pdf differ
diff --git a/doc/fex-client_2.pdf b/doc/fex-client_2.pdf
new file mode 100644 (file)
index 0000000..1bbd79c
Binary files /dev/null and b/doc/fex-client_2.pdf differ
diff --git a/doc/installation b/doc/installation
new file mode 100644 (file)
index 0000000..0e63e12
--- /dev/null
@@ -0,0 +1,92 @@
+Prerequisites:
+==============
+
+To install the F*EX server, you need a UNIX system with perl, xinetd and
+/usr/lib/sendmail (either original or a clone like postfix or exim). 
+Your perl must have the following standard core modules:
+
+CGI
+CGI::Carp
+Digest::MD5
+Encode
+Fcntl
+File::Basename
+Getopt::Std
+IO::Handle
+POSIX
+
+Optional are the non-standard perl modules:
+
+Net::DNS               (for better domain error handling)
+Socket6                (for ipv6 support)
+Net::INET6Glue         (for ipv6 support)
+IO::Socket::INET6      (for ipv6 support)
+
+On Debian or Ubuntu Linux simply execute as root:
+
+  apt-get install xinetd perl-modules libnet-dns-perl libnet-inet6glue-perl
+  test -x /usr/lib/sendmail || apt-get install postfix
+
+Do not forget to open port 80 on your firewalls and routers for incoming
+connections!
+
+To install F*EX, simply run "./install", then edit lib/fex.ph and set your
+local config.  If you want to upgrade from a previous F*EX version, you
+also can run "./install", no old config files will be overwritten. Also
+index.html is kept.
+   
+Alternativly use "./install -p 8888" to install F*EX on port 8888 instead
+on default port 80. 
+
+Run "./install" again for installing optional localizations (languages).
+
+If the install-script does not work for you (no xinetd, no GNU tools, etc)
+here is what is to be done manually for installing:
+
+echo "fex 80/tcp" >> /etc/services
+echo "fex stream tcp nowait fex /home/fex/bin/fexsrv fexsrv" >> /etc/inetd.conf
+# restart inetd or reboot
+useradd -s /bin/bash -c "File EXchange" -m fex
+cd FEXSOURCEDIR
+chown -R fex .
+su - fex
+cd FEXSOURCEDIR
+rsync -av bin cgi-bin lib etc htdocs doc $HOME
+cd $HOME
+mkdir spool
+chmod 700 spool
+vi lib/fex.ph
+# set your config
+crontab -e
+       3 3 * * * exec /home/fex/bin/fex_cleanup
+
+Hints:
+======
+
+F*EX runs with its own uid (default: fex), which will be created by the
+install script.
+
+F*EX uses a directory (FEXHOME) containing anything needed to run fex.
+Default is FEXHOME=/home/fex, the home directory of the "fex" user.
+
+If you want fex to be installed elsewhere create a new "fex" user account
+BEFORE running ./install, e.g.:
+
+adduser --system --group \
+  --gecos "File EXchange" \
+  --home /srv/fex \
+  --shell /bin/bash \
+  fex
+./install
+
+If you run F*EX behind a HTTP reverse proxy, then write to lib/fex.ph :
+       $reverse_proxy_ip = 'internal-IP.address.of.proxy';
+       $durl = 'http://your.proxy.with.domain/fop';
+Be aware that most proxies have a 2 GB file size limit (signed int 32 bit bug)!
+Of course you have to configure your reverse proxy correctly, especially
+the URL rewrite rules. See the manual of your reverse proxy.
+
+If you want to use https, you need the stunnel package. See file SSL for
+more information.
+
+On problems contact the author: framstag@rus.uni-stuttgart.de
diff --git a/doc/new b/doc/new
new file mode 100644 (file)
index 0000000..de4bd97
--- /dev/null
+++ b/doc/new
@@ -0,0 +1,7 @@
+New release on http://fex.rus.uni-stuttgart.de/fex.html
+                                                           
+Important changes:
+
+- workaround for upload problem with chaching proxies
+
+- some small bug fixes
diff --git a/doc/newfeatures b/doc/newfeatures
new file mode 100644 (file)
index 0000000..d28f95a
--- /dev/null
@@ -0,0 +1,47 @@
+New features for users
+----------------------
+
+2015-01-12:
+
+- user configuration: save-or-display (MIME) for download
+
+- fexsend has new option -S show server/user settings
+
+- fexsend has new option -N resend notification email
+
+                                                  
+2014-12-24:
+
+- the CLI clients respect the environment variables SSLVERIFY SSLVERSION
+  SSLCAPATH SSLCAFILE SSLCIPHERLIST to enhance HTTPS security
+
+- new $HOME/.fex/config for the CLI clients, you can set there the variables
+  $opt_* %autoview %alias
+  see "fexsend -H" and "fexget -H" for details
+
+- fexget autoviews images after download
+
+
+2014-08-06:
+
+- you as the sender can download the file, too, without auto-deleting it
+
+
+2014-03-05:
+
+- email enryption option: you can upload your PGP/GPG public key
+  via "user config & operation control" and "Manage e-mail encryption"
+
+- fexget checks local storage capacity before download
+
+- new FAQ http://fex.rus.uni-stuttgart.de/FAQ/
+
+- notification e-mail resend option via "user config & operation control"
+
+
+2013-08-05:
+
+- allow multiple downloads from same ip
+
+- fexget proxy support
+
diff --git a/doc/reverse_proxy b/doc/reverse_proxy
new file mode 100644 (file)
index 0000000..05c740d
--- /dev/null
@@ -0,0 +1,32 @@
+You can prosecute F*EX behind a reverse proxy, but then you are limited to
+the capabilities of this proxy. For example, if you use apache, then you
+have a 2 GB upload limitation and a throughput penality.
+
+Example for apache reverse proxy:
+
+On http://tandem-fahren.de:80  there is an apache
+On http://tandem-fahren.de:488 there is a F*EX server
+tandem-fahren.de has ip address 78.46.33.142
+
+If you want to contact the F*EX server via alias URL
+http://tandem-fahren.de/fex/ then you have to put in httpd.conf:
+
+        Redirect      /fex /fex/
+        ProxyRequests Off
+        ProxyPass     /fex/ http://tandem-fahren.de:488/
+        <Location /fex/>
+          ProxyPassReverse   http://tandem-fahren.de:488/
+          SetOutputFilter    proxy-html
+          ProxyHTMLExtended  On
+          ProxyHTMLURLMap    http://tandem-fahren.de:488/ /fex/
+          ProxyHTMLURLMap    / /fex/
+          ProxyHTMLURLMap    /fex/fex/fex/ /fex/
+        </Location>
+
+And write to lib/fex.ph :
+       $hostname = 'tandem-fahren.de';
+        $reverse_proxy_ip = '78.46.33.142';
+        $durl = 'http://tandem-fahren.de/fex/fop';
+
+
+See also: http://8d.no/fexproxy.html
diff --git a/doc/version b/doc/version
new file mode 100644 (file)
index 0000000..733bf4c
--- /dev/null
@@ -0,0 +1 @@
+fex-20150120
diff --git a/doc/xx b/doc/xx
new file mode 100644 (file)
index 0000000..3a99684
--- /dev/null
+++ b/doc/xx
@@ -0,0 +1,99 @@
+With xx you have a network wide shell based clip board for files and
+STDIN/STDOUT: easy data exchange between different accounts on different
+hosts. 
+
+  usage: send file(s):          xx file...
+     or: send STDIN:            xx -
+     or: send pipe:             ... | xx
+     or: get file(s) or STDIN:  xx
+     or: get file(s) quickly:   xx --
+
+
+Pipe example:
+
+  framstag@tandem:~: uname -a | xx
+  /home/framstag/.fex/tmp/STDFEX : 0 kB in 1 s (0 kB/s)
+  
+  
+  framstag@fex:~: xx
+  transfered: 0 MB (100%)
+  Linux tandem 2.6.24-28-server #1 SMP Wed Aug 18 22:01:20 UTC 2010 i686 GNU/Linux
+
+
+File transfer example:
+
+  framstag@tandem:~: xx /boot/
+  making tar transfer file /home/framstag/.fex/tmp/STDFEX :
+  /boot/
+  /boot/initrd.img-2.6.24-27-generic
+  /boot/vmlinuz-2.6.24-28-generic
+  (...)
+  /boot/System.map-2.6.24-27-generic
+  /home/framstag/.fex/tmp/STDFEX : 34 MB in 2 s (17605 kB/s)
+  
+  
+  framstag@fex:/tmp: xx
+  transfered: 34 MB (100%)
+  Files in transfer-container:
+  
+  drwxr-xr-x root/root         0 2010-10-20 07:37 boot/
+  -rw-r--r-- root/root   7905085 2010-05-25 15:38 boot/initrd.img-2.6.24-27-generic
+  -rw-r--r-- root/root   1927544 2010-10-16 20:38 boot/vmlinuz-2.6.24-28-generic
+  (...)
+  -rw-r--r-- root/root    906803 2010-03-24 14:33 boot/System.map-2.6.24-27-generic
+  
+  Extract these files? 
+
+
+One problem are shared system accounts like root where different persons
+use them: xx (like fexsend and fexget) uses $HOME/.fex/id which contains
+personal data.
+
+But you can put the ID authorization in the environment variable FEXID.
+
+fexsend on your local account gives you a string for "cut and paste", example: 
+
+  framstag@moep:~: fexsend -I
+  export FEXID='fex.rus.uni-stuttgart.de:8080 framstag jmBhf9ht'
+  history -d $(history 1);history -d $(history 1)
+
+You then copy these two lines with your mouse into the root account:
+
+  root@tandem:~# export FEXID'=fex.rus.uni-stuttgart.de:8080 framstag jmBhf9ht'
+  root@tandem:~# history -d $(history 1);history -d $(history 1)
+
+(The second line deletes these two lines from the bash history)
+
+Now you can use xx (or fexsend and fexget) as normal. Example:
+
+  root@tandem:~# grep sshd /var/log/daemon | xx
+  transfered: 265 kB in 1 s (265 kB/s)
+  
+  framstag@moep:~: xx | wc -l
+  434279
+
+
+After logging out from root@tandem the F*EX authorization data has gone.
+
+
+Internal details:
+
+The intermediate transfer file container is $HOME/.fex/tmp/STDFEX
+
+When transfering files tar/gzip format is used, whereas STDIN (pipe) will
+be transfered uncompressed. Tip: If you want to transfer files uncompress,
+then use: tar cvf - your files... | xx
+
+With default usage only one "storage slot" is available: STDFEX
+But you can use as many "storage slots" as you want by using "xx :TAG" 
+Examples:
+
+  uname -a | xx
+  
+  grep sshd /var/log/daemon | xx :log
+  
+  xx :log | grep obertux  
+  
+  xx :config /boot /etc /root
+  
+  xx :config --
diff --git a/etc/mime.types b/etc/mime.types
new file mode 100644 (file)
index 0000000..328d9c8
--- /dev/null
@@ -0,0 +1,36 @@
+application/ogg                                        ogg ogx
+application/pdf                                        pdf
+application/postscript                         ps ai eps
+application/x-dvi                              dvi
+audio/basic                                    au snd
+audio/mpeg                                     mpga mpega mp2 mp3 m4a
+audio/ogg                                      oga spx
+audio/x-ms-wma                                 wma
+audio/x-wav                                    wav
+image/gif                                      gif
+image/jpeg                                     jpeg jpg jpe
+image/pcx                                      pcx
+image/png                                      png
+image/svg+xml                                  svg svgz
+image/tiff                                     tiff tif
+image/x-ms-bmp                                 bmp
+image/x-portable-anymap                                pnm
+image/x-portable-bitmap                                pbm
+image/x-portable-graymap                       pgm
+image/x-portable-pixmap                                ppm
+image/x-rgb                                    rgb
+image/x-xbitmap                                        xbm
+image/x-xpixmap                                        xpm
+image/x-xwindowdump                            xwd
+text/plain                                     asc txt text pot
+video/3gpp                                     3gp
+video/dl                                       dl
+video/dv                                       dif dv
+video/fli                                      fli
+video/gl                                       gl
+video/mpeg                                     mpeg mpg mpe
+video/mp4                                      mp4
+video/ogg                                      ogv
+video/quicktime                                        qt mov
+video/x-msvideo                                        avi
+video/x-sgi-movie                              movie
diff --git a/etc/xinetd_fex b/etc/xinetd_fex
new file mode 100644 (file)
index 0000000..c25a3db
--- /dev/null
@@ -0,0 +1,20 @@
+# default: on
+# description: F*EX web server
+#
+service fex
+{
+       socket_type             = stream
+       wait                    = no
+        type                   = unlisted
+       protocol                = tcp
+        bind                   = ADDRESS
+       port                    = PORT
+        cps                    = 10 2
+       user                    = fex
+       groups                  = yes
+       server                  = FEXHOME/bin/fexsrv
+       nice                    = 0
+       disable                 = no
+}
+# configuration for https/SSL see /home/fex/doc/SSL
+# configuration for IPv6 see /home/fex/doc/IPv6
diff --git a/htdocs/FAQ/FAQ.html b/htdocs/FAQ/FAQ.html
new file mode 120000 (symlink)
index 0000000..64233a9
--- /dev/null
@@ -0,0 +1 @@
+index.html
\ No newline at end of file
diff --git a/htdocs/FAQ/admin.faq b/htdocs/FAQ/admin.faq
new file mode 100644 (file)
index 0000000..35b8b3c
--- /dev/null
@@ -0,0 +1,156 @@
+Q: I cannot install a web server like fexsrv, because I have no root permissions. Is there a pure-CGI-version of F*EX which runs with an apache web server?
+A: F*EX is hard bound to fexsrv for several reasons (performance, file size limit, session concept, etc) and cannot be run as CGI under apache. But you might have a look at
+
+   * http://gpl.univ-avignon.fr/filez/
+   * http://freshmeat.net/projects/eventh/
+   * http://www.schaarwaechter.de/sp/projekte/dateiaustausch.html (German only!) 
+
+   which implement a file exchange as pure CGIs, but with a 2 GB file size limit, which F*EX does not have.
+
+Q: I have already a webserver (apache) running. How can I install F*EX in parallel?
+A: You have to use a different port or ip for F*EX, because you cannot run two services on the same ip:port combination.
+   For example port 88, use "./install -p 88"
+   For another ip you can create a new virtual interface or use a virtual machine.
+   See the documentation of your UNIX.
+
+Q: Ok, I have installed F*EX. What now?
+A: Become user <code>fex</code> and create some users with <code>fac</code>, example:
+   <pre>
+     /home/fex/bin/fac -u memyselfandi@my.do.main secret-auth-id
+   </pre>
+   Then log in using the web interface: http://YOURFEXSERVER/
+   
+   ... and join the F*EX mailing list! &#9786;
+   https://listserv.uni-stuttgart.de/mailman/listinfo/fex
+
+Q: What is /home/fex/bin/fac and /home/fex/cgi-bin/fac ?
+A: fac stands for F*EX Admin Control
+
+   /home/fex/bin/fac is the (full) CLI version
+   
+   /home/fex/cgi-bin/fac is the (restricted) web version. You have to call it with your webbrowser: 
+   http://YOURFEXSERVER/fac
+
+Q: F*EX is not working at all! I cannot connect to it with my web browser!
+A: Check your routing, ipfilters and firewall setup.
+   Also check if xinetd is running. If it is linked with tcp-wrapper, configure it correctly (hosts.allow).
+   F*EX needs port 80/tcp for HTTP and optionally port 443/tcp for HTTPS.
+
+Q: What is the difference between all these user types (full, sub, group, external, ...)?
+A: See http://fex.rus.uni-stuttgart.de/users.html
+
+Q: How can I integrate F*EX in the existing user management at my site?
+A: F*EX has several authentification modules: local, RADIUS, LDAP, mailman and POP.
+   For the last 4 please contact <framstag@rus.uni-stuttgart.de>
+
+Q: Can I make an alias address for an user?
+A: F*EX users are defined by a directory in the spool. Therefore execute:
+
+   <code>
+   cd /home/fex/spool
+   ln -s user@do.main alias@other.address
+   </code>
+   
+   Now the user is known as user@do.main and alias@other.address
+
+Q: I want that all my local users can use F*EX. How?
+A: Let them register themselves with http://YOURFEXSERVER/fur
+
+   You have to edit /home/fex/lib/fex.ph and set (example):
+   <pre>
+     @local_hosts = qw(127.0.0.1 10.10.100.0-10.10.255.255);
+     @local_domains = qw(flupp.org ulm.sub.net);
+   </pre>
+   Or you can allow anonymous upload for your LAN users with fex.ph variable @anonymous_upload
+   Example:
+   <pre>
+     @anonymous_upload = qw(10.10.100.0-10.10.200.255 129.69.1.11);
+   </pre>
+   (Of course you have to use your real local hosts/networks!)
+
+Q: I want that external users can fex to my local users. How?
+A: Let them register themselves with http://YOURFEXSERVER/fur
+
+   You have to edit /home/fex/lib/fex.ph and set (example):
+   <pre>
+     # your local receiving domains
+     @local_rdomains = qw(flupp.org *.flupp.org);
+            
+     # your local receiving hosts
+     @local_rhosts = qw(127.0.0.1 129.69.0.0-129.69.255.255 176.9.84.26);
+   </pre>
+   
+   Or you can manually create a restricted external user with (example):
+   <pre>
+     /home/fex/bin/fac -u framstag@rus.uni-stuttgart.de hoppla
+     /home/fex/bin/fac -R framstag@rus.uni-stuttgart.de
+   </pre>
+
+Q: How can I change user settings like quota, restrictions or keep time?
+A: Use <code>/home/fex/bin/fac</code>
+
+Q: How can I delete or temporarly disable a user?
+A: Use <code>/home/fex/bin/fac</code>
+
+Q: I have BIG files already on the fexserver host. Can I upload just a
+   link instead of the whole file?
+A: Set in fex.ph:
+
+   <code>
+   @file_link_dirs = qw(/directory/with/big/files);
+   </code>
+   <p>
+   and use:
+   <p>
+   <code>
+   fexsend -/ /directory/with/big/files/BIG.file recipient@wherever
+   </code>
+
+Q: I want the Bcc mails to fex (admin user) to be sent to another address.
+A: Set variable $bcc in /home/fex/lib/fex.ph
+
+Q: I need more security! How can I enable (https) encryption?
+A: Read doc/SSL and also look for "fop_auth" in doc/concept 
+   (doc is a local directory in your installation or online http://fex.rus.uni-stuttgart.de/doc/)
+   For email encryption see http://fex.rus.uni-stuttgart.de/gpg.html
+
+Q: I need a corporate identity look. How can I configure F*EX in this way?
+A: * See variable @H1_extra in /home/fex/lib/fex.ph and you can add HTML code to /home/fex/htdocs/header.html
+   * See /home/fex/htdocs/fup_template.html, modify it to your needs and use it as your start-page.
+   * Contact <fex@nepustil.net> http://www.nepustil.net/ if you need more customization.
+
+Q: F*EX is too complicated for my tie users. I need a simplified upload form.
+A: See /home/fex/htdocs/fup_template.html and /home/fex/htdocs/sup.html 
+   or use public upload, see http://fex.rus.uni-stuttgart.de/usecases/foreign.html
+
+Q: F*EX is still too complicated! I need something more simplified.
+A: Try http://fex.rus.uni-stuttgart.de/fstools/woos.html or use F*EX mail (see next question).
+
+Q: Can I integrate F*EX in my users MUAs (thunderbird, outlook, etc)?
+A: See http://fex.rus.uni-stuttgart.de/usecases/BIGMAIL.html
+
+Q: Can I get a localized version in my native languange?
+A: With your help, yes. Please contact <framstag@rus.uni-stuttgart.de>
+
+Q: I need ACLs for group access, a file browser and integration in my
+   native file system.
+A: This is beyond the scope of F*EX, which is designed for efficient file transfer only.
+
+Q: Feature/design XY is missing.
+A: Contact the author <framstag@rus.uni-stuttgart.de>
+
+Q: How can I get fup as start page?
+A: Execute:
+
+   <code>
+   cd /home/fex/cgi-bin
+   ln -s fup login
+   </code>
+
+Q: How can I use my own local FAQ?
+A: Put your FAQ into file /home/fex/htdocs/FAQ/local.faq and execute:
+
+   <code>
+   cd /home/fex/htdocs/FAQ
+   ln -sf local.html index.html
+   </code>
diff --git a/htdocs/FAQ/admin.html b/htdocs/FAQ/admin.html
new file mode 100644 (file)
index 0000000..a096645
--- /dev/null
@@ -0,0 +1,12 @@
+<html>
+<head><title>F*EX FAQ</title></head>
+<body>
+
+## <pre>
+## << while (($v,$vv) = each %ENV) { print "$v = $vv\n" } >>
+## </pre>
+
+<< require "./faq.pl" or print $! >>
+
+</body>
+</html>
diff --git a/htdocs/FAQ/all.html b/htdocs/FAQ/all.html
new file mode 100644 (file)
index 0000000..a096645
--- /dev/null
@@ -0,0 +1,12 @@
+<html>
+<head><title>F*EX FAQ</title></head>
+<body>
+
+## <pre>
+## << while (($v,$vv) = each %ENV) { print "$v = $vv\n" } >>
+## </pre>
+
+<< require "./faq.pl" or print $! >>
+
+</body>
+</html>
diff --git a/htdocs/FAQ/faq.pl b/htdocs/FAQ/faq.pl
new file mode 100644 (file)
index 0000000..6cadd49
--- /dev/null
@@ -0,0 +1,142 @@
+package FAQ;
+
+my ($faq,$var,$env,$q,$a,$c,$s,$t,$n);
+my (@faq,%Q,%A,@s);
+my @sections = qw'Meta User Admin Misc';
+
+@faq = ($faq) = $ENV{PATH_INFO} =~ /(\w+).html/;
+@faq = map {lc} @sections if $faq eq 'all';
+
+print "<style type=text/css><!-- h2,h3 {font-weight:normal} --></style>\n";
+
+print '<h1><a name="top" href="/index.html">F*EX</a> ';
+printf "Frequently Asked Questions: %s</h1>\n",ucfirst($faq);
+
+if ($faq ne 'local') {
+  print "<h3>Sections: ";
+  foreach $s (@sections,'All') {
+    if ($s =~ /$faq/i) {
+      print "<b>$s</b>\n";
+    } else {
+      printf "[<a href=\"%s.html\">%s</a>]\n",lc($s),$s;
+    }
+  }
+  print "</h3>\n";
+}
+
+print "<p><hr><p>\n";
+print "<table>\n";
+
+foreach my $faq (@faq) {
+  open $faq,"$faq.faq" or next;
+  local $/ = "Q:";
+  local $_ = <$faq>;
+  while (<$faq>) {
+    chomp;
+    while (/\$([\w_]+)\$/) {
+      $var = $1;
+      $env = $ENV{$var} || '';
+      # s/\$$var\$/<code>$env<\/code>/g;
+      s/\$$var\$/$env/g;
+    };
+    ($q,$a) = split /A:\s*/;
+    $q =~ s/[\s\n]+$//;
+    $q =~ s/^\s+//;
+    $q =~ s! (/\w[\S]+/[\S]+)! <code>$1</code>!g;
+    $a =~ s/[\s\n]+$/\n/;
+    $a =~ s/^\s+//;
+    while ($a =~ s/^(\s*)\*/$1<ul>\n$1<li>/m) { 
+      while ($a =~ s/(<li>.*\n\s*)\*/$1<li>/g) {}
+      $a =~ s:(.*\n)(\s*)(<li>[^\n]+\n):$1$2$3$2</ul>\n:s
+    }
+    $a =~ s/\n\n/\n<p>\n/g;
+    $a =~ s/([^>\n\\])\n/$1<br>\n/g;
+    $a =~ s/<pre>(.+?)<\/pre>/pre($1)/ges;
+    $a =~ s/\\\n/\n/g;
+#    $a =~ s/^\s*<br>\s*//mg;
+    $a =~ s/<([^\s<>\@]+\@[\w.-]+)>/<a href="mailto:$1">&lt;$1><\/a>/g;
+    $a =~ s! (/\w[\S]+/[\S]+)! <code>$1</code>!g;
+    $a =~ s!(https?://[\w-]+\.[^\s<>()]+)!<a href="$1">[$1]</a>!g or
+    $a =~ s!(https?://[^\s<>()]+)!<code>$1</code></a>!g;
+    push @{$Q{$faq}},$q;
+    push @{$A{$faq}},$a;
+  }
+  close $faq;
+}
+
+print "<table>\n";
+
+foreach $s (sections($faq)) {
+
+  $c = lc $s;
+  $s = '' if $s eq 'Local';
+  $t = '';
+  $t = $s if $faq eq 'all';
+
+  for ($n = 0; $n < scalar(@{$Q{$c}}); $n++) {
+    $q = ${Q{$c}[$n]};
+    $qa = anchor($q);
+    printf '<tr valign=top><th align=left>'.
+           '<a href="#%s%d" style="text-decoration: none">'.
+           '<font color="black">%s&nbsp;Q%d</a>:'.
+           '<td><a href="#%s">%s</a></tr>'."\n",
+           $t,$n+1,$s,$n+1,$qa,$q;
+  }
+}
+
+print "</table>\n";
+print "<p><hr><p>\n";
+
+foreach $s (sections($faq)) {
+
+  $c = lc $s;
+  $s = '' if $s eq 'Local';
+  $t = '';
+  $t = $s if $faq eq 'all';
+
+  for ($n = 0; $n < scalar(@{$Q{$c}}); $n++) {
+    $q = ${Q{$c}[$n]};
+    $qa = anchor($q);
+    print "<p>\n";
+    print "<table>\n";
+    printf "<tr valign=top><th>".
+           "<a name=\"%s%d\">%s&nbsp;Q%d:</a>".
+           "<a name=\"%s\"></a>".
+           "<td><b>%s</b></tr>\n",
+           $t,$n+1,$s,$n+1,$qa,$q;
+    printf "<tr valign=top><th>%s&nbsp;A%d:<td>\n%s</tr>\n",
+           $s,$n+1,${A{$c}[$n]};
+    print "</table>\n";
+    print "[<a href=\"#top\">&uarr;&nbsp;Questions</a>]\n";
+  }
+}
+
+print "<pre>\n";
+print "\n" x 99;
+print "</pre>\n";
+
+
+sub sections {
+  my $faq = shift;
+  if ($faq eq 'all') {
+    return @sections;
+  } else {
+    return ucfirst($faq);
+  }
+}
+
+sub pre {
+  local $_ = shift;
+  s/<br>//g;
+  s/\s+$//;
+  return "<pre>$_</pre>\n";
+}
+
+sub anchor {
+  local $_ = shift;
+  s/<.+?>//g;
+  s/\(.+?\)//g;
+  s/\W/_/g;
+  s/_+$//;
+  return $_;
+}
diff --git a/htdocs/FAQ/index.html b/htdocs/FAQ/index.html
new file mode 120000 (symlink)
index 0000000..1dadbb5
--- /dev/null
@@ -0,0 +1 @@
+meta.html
\ No newline at end of file
diff --git a/htdocs/FAQ/local.faq b/htdocs/FAQ/local.faq
new file mode 100644 (file)
index 0000000..dbb1324
--- /dev/null
@@ -0,0 +1,7 @@
+Q: How can I use my own local FAQ?
+A: Put your FAQ into file /home/fex/htdocs/FAQ/local.faq and execute:
+
+   <code>
+   cd /home/fex/htdocs/FAQ
+   ln -sf local.html index.html
+   </code>
diff --git a/htdocs/FAQ/local.html b/htdocs/FAQ/local.html
new file mode 100644 (file)
index 0000000..a096645
--- /dev/null
@@ -0,0 +1,12 @@
+<html>
+<head><title>F*EX FAQ</title></head>
+<body>
+
+## <pre>
+## << while (($v,$vv) = each %ENV) { print "$v = $vv\n" } >>
+## </pre>
+
+<< require "./faq.pl" or print $! >>
+
+</body>
+</html>
diff --git a/htdocs/FAQ/meta.faq b/htdocs/FAQ/meta.faq
new file mode 100644 (file)
index 0000000..2bda920
--- /dev/null
@@ -0,0 +1,83 @@
+Q: What is so special about F*EX?
+A: See feature list http://fex.rus.uni-stuttgart.de/features.html
+   and use cases http://fex.rus.uni-stuttgart.de/usecases/
+
+Q: Why not use one of the commercial services like DropLoad, ALLPeers, YouSendIt, etc?
+A: * They have a file size limit of 2 GB or even less.
+   * Their security and privacy status is unknown (ever heard of "Snowden & NSA"?).
+   * They are not open source based.
+   * There are no UNIX (CLI) clients for them.
+   * They need java, active-X, flash or other evil plugins.
+   * It is unknown how long they will exist - DropLoad and ALLPeers already have terminated their business.
+   
+Q: Why name "F*EX" and not shortly "FEX"?
+A: At publication time there was already an (older) program named "FEX" listed on freshmeat.net.
+
+Q: Who is the author?
+A: The main author is Ulli Horlacher <framstag@rus.uni-stuttgart.de><br>
+   But there are also a lot of contributors.
+
+Q: Why a camel as the logo?
+A: The logo was inspired by the Perl camel, but it is based on a Steiff plush camel, which rides with us on our racing tandem.
+   The logo was drawn by my stoker Beate.
+   http://fex.rus.uni-stuttgart.de/Vortrag/tosa.html
+
+Q: What do I need to install F*EX?
+A: A UNIX or Windows server with a DNS entry, smtp for outgoing email and one open and free incoming tcp port.
+   You must have administrative rights (UNIX: root) on this server and a basic understanding of UNIX and networking.
+
+Q: What means DNS and smtp? What is a tcp port?
+A: Do not install F*EX. It is beyond your horizon.
+
+Q: Does F*EX support IPv6?
+A: Yes.
+
+Q: Can I run F*EX on Windows?
+A: On client side all operating systems are supported, even Windows.
+   If you want to run a F*EX server on Windows, then see http://fex.rus.uni-stuttgart.de/fexwix.html
+
+Q: Where can I get the F*EX sources?
+A: F*EX server for UNIX: http://fex.rus.uni-stuttgart.de/fex.html
+
+Q: I do not want to install a F*EX server of my own, but where can I use it?
+A: Contact <fex@nepustil.net> http://www.nepustil.net/ for F*EX hosting.
+
+Q: The F*EX server is all in Perl?! Isn't Perl too slow for this job?
+A: fex.rus.uni-stuttgart.de runs on an office PC and F*EX is able to handle uploads with more than 300 MB/s.
+   Try this with an ordinary webserver like Apache!
+
+Q: Which licence does F*EX have? And why?
+A: Perl Artistic free software with a special anti-military clause: 
+   http://fex.rus.uni-stuttgart.de/doc/Licence
+   "I want peace on earth and goodwill towards men"
+   http://www.youtube.com/watch?v=JHU0HinVhYc
+
+Q: Is there a F*EX mailing list?
+A: https://listserv.uni-stuttgart.de/mailman/listinfo/fex
+
+Q: Where can I get commercial support for F*EX?
+A: Contact <fex@nepustil.net> http://www.nepustil.net/
+
+Q: Who else is using F*EX?
+A: For example:
+   * German Aerospace Center http://fex.dlr.de
+   * European Commission Institute for Energy and Transport http://fex.jrc.nl
+   * High Performance Computing Center Stuttgart http://fex.hlrs.de
+   * Swiss National Supercomputing Centre http://fex.cscs.ch
+   * Centre National de la Recherche Scientifique (French National Center for Scientific Research) http://bigfiles.cnrs-gif.fr
+   * Institut Pasteur http://dl.pasteur.fr
+   * Palo Alto Research Center (Xerox PARC) http://parcftp.parc.com
+   * Baden-Württembergs extended LAN http://fex.belwue.de
+   * Deutsche Kinemathek Museum f&uuml;r Film und Fernsehen http://upload.deutsche-kinemathek.de
+
+Q: Is F*EX secure?
+A: F*EX is written in Perl which does not have common security problems with buffer overruns, segmentation faults, heap and stack corruption.
+   F*EX does not use an extra web server or a (SQL) database, so no typical web attacks are possible.
+   F*EX was analysed by security company revshell.com and labeled as "secure".
+   F*EX comes with source code, so everybody can verify it.
+   
+Q: Feature/design XY is missing.
+A: Contact the author <framstag@rus.uni-stuttgart.de>
+
+Q: I have more/other questions than in this document!
+A: Ask the author <framstag@rus.uni-stuttgart.de>
diff --git a/htdocs/FAQ/meta.html b/htdocs/FAQ/meta.html
new file mode 100644 (file)
index 0000000..a096645
--- /dev/null
@@ -0,0 +1,12 @@
+<html>
+<head><title>F*EX FAQ</title></head>
+<body>
+
+## <pre>
+## << while (($v,$vv) = each %ENV) { print "$v = $vv\n" } >>
+## </pre>
+
+<< require "./faq.pl" or print $! >>
+
+</body>
+</html>
diff --git a/htdocs/FAQ/misc.faq b/htdocs/FAQ/misc.faq
new file mode 100644 (file)
index 0000000..ddd5251
--- /dev/null
@@ -0,0 +1,17 @@
+Q: F*EX is great! Can I join the developing team? What needs to be done?
+A: Contact the author <framstag@rus.uni-stuttgart.de>
+
+   Requested features are:
+   <ul>
+     <li>testers for MacOS, AIX and other UNIXes
+     <li>a new maintainer for the Java client F*IX
+     <li>an Android or iOS client
+     <li>a F*EX plugin for thunderbird or outlook
+         see thunderbird's filelink
+         https://support.mozillamessaging.com/en-US/kb/filelink-large-attachments
+         https://developer.mozilla.org/en/Thunderbird/Filelink_Providers
+     <li>more (other) languange support (japanese, bavarian, klingon ...) 
+   </ul>
+
+Q: Can I donate something for F*EX?
+A: If you have a pet animal, fex a cute video of it to the author <framstag@rus.uni-stuttgart.de>
diff --git a/htdocs/FAQ/misc.html b/htdocs/FAQ/misc.html
new file mode 100644 (file)
index 0000000..a096645
--- /dev/null
@@ -0,0 +1,12 @@
+<html>
+<head><title>F*EX FAQ</title></head>
+<body>
+
+## <pre>
+## << while (($v,$vv) = each %ENV) { print "$v = $vv\n" } >>
+## </pre>
+
+<< require "./faq.pl" or print $! >>
+
+</body>
+</html>
diff --git a/htdocs/FAQ/user.faq b/htdocs/FAQ/user.faq
new file mode 100644 (file)
index 0000000..5c62df9
--- /dev/null
@@ -0,0 +1,136 @@
+Q: What is the "auth-ID"?
+A: The auth-ID is an internal identification which authentificates the user. It will be first generated by the admin or the automatic registration process and can later be modified by you, the user. Think of some kind of a low security password.
+
+Q: What is the difference between all these user types (full, sub, group, external, ...)?
+A: See http://fex.rus.uni-stuttgart.de/users.html
+
+Q: I have uploaded a HUGE file but misspelled my recipient's address. Now I have got an error bounce email. Must I re-upload the HUGE file?
+A: No, it is not necessary. You can redirect the file with "user config & operation control"
+
+Q: I have uploaded a HUGE file but forgot another recipient. Must I re-upload the HUGE file?
+A: No, it is not necessary. You can forward-copy the file with "user config & operation control"
+
+Q: Can I use an HTTP proxy?
+A: Yes, but pay attention:
+   Some HTTP proxies like privoxy delay the upload progress bar!
+   You may want to disable proxying $SERVER_NAME$ if you run into this problem.
+
+Q: My recipient has lost the notification email with the download-URL. What can I do?
+A: You can resend the notification email via "user config & operation control"
+
+Q: Why should I use a special F*EX client?
+A: When you are using F*EX with your webbrowser, you are limited to its restrictions.
+   With a special F*EX client http://$HTTP_HOST$/tools.html you can
+
+   * resume an aborted transfer
+   * send several files or even whole directory trees at once
+   * stream files
+   * transfer files via command line
+   * use an Internet clipboard http://fex.rus.uni-stuttgart.de/usecases/xx.html
+   * do much more :-)
+
+Q: How can I upload several files at once?
+A: Put your files in an archive file (ZIP). Your web browser cannot do that.
+   Or you can use a F*EX client, see http://$HTTP_HOST$/tools.html
+
+Q: I cannot upload files > 2 GB with my web browser!?
+A: Many web browsers have bugs in their HTML-FORM implementation. The limit mostly is 2 GB, sometimes 4 GB.
+   Firefox and Google Chrome have no limitation.
+   But remember: No web browser is able to resume an interrupted upload. You need a special F*EX client like fexsend or schwuppdiwupp for resuming, see http://$HTTP_HOST$/tools.html
+
+Q: I need to send a file bigger than my quota allows. What can I do?
+A: Simply ask $SERVER_ADMIN$ to raise your quota.
+
+Q: Why is the upload status window empty and I cannot see the progress bar?
+A: Most probably you are using a (enforced) web proxy, which cannot handle dynamic HTML pages.
+   A workaround is using Google Chrome, which shows the upload status by itself.
+
+Q: My download was aborted before it was finished. Can I resume the download?
+A: F*EX supports resuming at download, but your client also has to support this feature.
+   Firefox e.g. is missing this HTTP feature, you need an other client like opera, wget or fexget.
+
+Q: My upload was aborted before it was finished. Can I resume the upload?
+A: F*EX supports resuming at upload, but your client also has to support it.
+   No web browser has this feature, you need a special F*EX client like fexsend or schwuppdiwupp,
+   see http://$HTTP_HOST$/tools.html
+
+Q: Can I integrate F*EX in my mail program (thunderbird, outlook, etc)?
+A: Yes, if your mail admin has set up a "fexmail" smtp relay.
+   http://fex.rus.uni-stuttgart.de/usecases/BIGMAIL.html
+
+Q: Can I use a download manager/accelerator?
+A: Generally, no, because they suck: they are not RFC compliant and produce a LOT of unnecessary server load.
+   But there is one exception: axel http://axel.alioth.debian.org/
+
+Q: When I hit [ESC] in firefox the upload is canceled. Why?
+A: This is a built-in feature of firefox: ESC terminates the current operation.
+   Simple solution: do not hit ESC in Firefox.
+   Complex solution: ask the Firefox developers to add keyboard configuration.
+
+Q: Sending as a F*EX user is easy, but how to receive files from others, outside?
+A: Register them as your subusers, create a F*EX group or a one-time upload key with "user config & operation control"
+   See also http://fex.rus.uni-stuttgart.de/usecases/foreign.html
+
+Q: Sometimes I can download a file more than once, especially when I repeat it quickly. Is the autodelete feature buggy?
+A: The F*EX server has a grace time of 1 minute after first sucessfully download in which the file is still available. This is necessary because of some stupid "download managers" which request the file several times at once. Otherwise they would report an error to the user.
+
+   Your fexmaster has set AUTODELETE=DELAY as default, which means that the autodelete cleanup process is called once a day.
+   
+   Power users (use the source, Luke!) can set a "do not delete after download" flag.
+
+Q: I have uploaded a file to a list of recipients. Will the file be deleted after the first recipient has dowloaded it?
+A: No. Every recipient gets his own copy of the file which is independant from the others.
+
+Q: The default keep time is too short for me (sender), I need more. How can I set it?
+A: Use fexsend, ask $SERVER_ADMIN$ or read the source code :-)
+
+Q: The default keep time is too short for me (recipient), I need more. How can I set it?
+A: Ask $SERVER_ADMIN$ to raise your default KEEP value.
+
+Q: I forgot to download a file. Now it is expired. How can I obtain it nevertheless?
+A: An expired file is definitively deleted. Even the admin cannot restore it.
+   You must re-request it from the sender.
+
+Q: I have sent a second file with the same name, but the recpient has not received a second notification email. Why?
+A: A file with the same name to the same recpient overwrites the first one if it is still there (no download so far).
+   A second notification email of the same file(name) is not suggestive.
+
+Q: How can I sent a more compact notification email?
+A: Let your comment start with "!.!", then the notification email will contain only download-URL, size and comment.
+
+Q: How can I suppress the automatic notification email?
+A: Use "!#!" as comment, then no notification email will be sent.
+   Of course you then have to inform the recipient manually.
+
+Q: Can I get a copy of the notification email?
+A: Add "!bcc!" to the comment field on upload.
+
+Q: Can I get a notification email on download?
+A: No.
+   Such a feature is in conflict with German and European privacy laws and will not be implemented.
+   With email you also have no acknowledgement of receipt.
+
+Q: Can I have encrypted emails?
+A: See http://fex.rus.uni-stuttgart.de/gpg.html
+
+Q: I cannot download files with Internet Explorer, it tells me "Cannot open Internet site". What shall I do?
+A: Use Firefox or any other Internet-compatible web browser, that Internet Explorer is not.
+   This is one of the many bugs of Internet Explorer.
+   See also http://support.microsoft.com/kb/323308
+
+Q: I cannot login with Internet Explorer, it tells me "This page can't be displayed". What shall I do?
+A: Use Firefox or any other Internet-compatible web browser, that Internet Explorer is not.
+   This is one of the many bugs of Internet Explorer.
+
+Q: I have recived a "file.7z". How can I extract it on my Mac?
+A: For example with "Stuffit Expander":
+   https://itunes.apple.com/us/app/stuffit-expander/id405580712?mt=12
+   http://my.smithmicro.com/stuffit-expander-mac-download.html
+
+Q: How can I prevent the fexsend error <code>SSL3_GET_SERVER_CERTIFICATE:certificate verify failed</code>?
+A: Set the environment variable <code>SSLVERIFY=0</code>
+
+   Rationale: 
+   Your openssl library cannot resolve the SSL certification path. 
+   With <code>SSLVERIFY=0</code> you tell openssl to ignore certification verification.
+   Yes, this is a crude workaround :-}
diff --git a/htdocs/FAQ/user.html b/htdocs/FAQ/user.html
new file mode 100644 (file)
index 0000000..a096645
--- /dev/null
@@ -0,0 +1,12 @@
+<html>
+<head><title>F*EX FAQ</title></head>
+<body>
+
+## <pre>
+## << while (($v,$vv) = each %ENV) { print "$v = $vv\n" } >>
+## </pre>
+
+<< require "./faq.pl" or print $! >>
+
+</body>
+</html>
diff --git a/htdocs/Licence b/htdocs/Licence
new file mode 100644 (file)
index 0000000..4108934
--- /dev/null
@@ -0,0 +1,120 @@
+F*EX (Frams' Fast File EXchange) is a service to send big (large, huge,
+giant, ...) files from a user A to a user B via internet.
+
+Copyright (C) 2006-2014 Ullrich Horlacher <framstag@rus.uni-stuttgart.de>
+
+YOU ARE NOT ALLOWED TO USE THIS SOFTWARE FOR MILITARY PURPOSES OR WITHIN
+MILITARY ORGANIZATIONS! THIS INCLUDES ALSO MILITARY RESEARCH AND
+EDUCATION, PARAMILITARY AND TERRORISTIC ORGANIZATIONS!
+
+This program is free software: you can redistribute it and/or modify it
+under the terms of the Perl Artistic Licence:
+
+"Package" refers to the collection of files distributed by the Copyright
+Holder, and derivatives of that collection of files created through
+textual modification.
+
+"Standard Version" refers to such a Package if it has not been modified,
+or has been modified in accordance with the wishes of the Copyright Holder
+as specified below.
+
+"Copyright Holder" is whoever is named in the copyright or copyrights for
+the package.
+
+"You" is you, if you're thinking about copying or distributing this
+Package.
+
+"Reasonable copying fee" is whatever you can justify on the basis of media
+cost, duplication charges, time of people involved, and so on. (You will
+not be required to justify it to the Copyright Holder, but only to the
+computing community at large as a market that must bear the fee.)
+
+"Freely Available" means that no fee is charged for the item itself,
+though there may be fees involved in handling the item. It also means that
+recipients of the item may redistribute it under the same conditions they
+received it.
+
+1. You may make and give away verbatim copies of the source form of the
+   Standard Version of this Package without restriction, provided that you
+   duplicate all of the original copyright notices and associated
+   disclaimers.
+
+2. You may apply bug fixes, portability fixes and other modifications
+   derived from the Public Domain or from the Copyright Holder. A Package
+   modified in such a way shall still be considered the Standard Version.
+
+3. You may otherwise modify your copy of this Package in any way, provided
+   that you insert a prominent notice in each changed file stating how and
+   when you changed that file, and provided that you do at least ONE of
+   the following:
+
+a) place your modifications in the Public Domain or otherwise make them
+   Freely Available, such as by posting said modifications to Usenet or an
+   equivalent medium, or placing the modifications on a major archive site
+   such as uunet.uu.net, or by allowing the Copyright Holder to include
+   your modifications in the Standard Version of the Package.
+
+b) use the modified Package only within your corporation or organization.
+
+c) make other distribution arrangements with the Copyright Holder.
+
+4. You may distribute the programs of this Package in object code or
+   executable form, provided that you do at least ONE of the following:
+
+a) distribute a Standard Version of the executables and library files,
+   together with instructions (in the manual page or equivalent) on where
+   to get the Standard Version.
+
+b) accompany the distribution with the machine-readable source of the
+   Package with your modifications.
+
+c) give non-standard executables non-standard names, and clearly document
+   the differences in manual pages (or equivalent), together with
+   instructions on where to get the Standard Version.
+
+d) make other distribution arrangements with the Copyright Holder.
+
+5. You may charge a reasonable copying fee for any distribution of this
+   Package. You may charge any fee you choose for support of this Package.
+   You may not charge a fee for this Package itself. However, you may
+   distribute this Package in aggregate with other (possibly commercial)
+   programs as part of a larger (possibly commercial) software
+   distribution provided that you do not advertise this Package as a
+   product of your own. You may embed this Package's interpreter within an
+   executable of yours (by linking); this shall be construed as a mere
+   form of aggregation, provided that the complete Standard Version of the
+   interpreter is so embedded.
+
+6. The scripts and library files supplied as input to or produced as
+   output from the programs of this Package do not automatically fall
+   under the copyright of this Package, but belong to whoever generated
+   them, and may be sold commercially, and may be aggregated with this
+   Package. If such scripts or library files are aggregated with this
+   Package via the so-called "undump" or "unexec" methods of producing a
+   binary executable image, then distribution of such an image shall
+   neither be construed as a distribution of this Package nor shall it
+   fall under the restrictions of Paragraphs 3 and 4, provided that you do
+   not represent such an executable image as a Standard Version of this
+   Package.
+
+7. C subroutines (or comparably compiled subroutines in other languages)
+   supplied by you and linked into this Package in order to emulate
+   subroutines and variables of the language defined by this Package shall
+   not be considered part of this Package, but are the equivalent of input
+   as in Paragraph 6, provided these subroutines do not change the
+   language in any way that would cause it to fail the regression tests
+   for the language.
+
+8. Aggregation of this Package with a commercial distribution is always
+   permitted provided that the use of this Package is embedded; that is,
+   when no overt attempt is made to make this Package's interfaces visible
+   to the end user of the commercial distribution. Such use shall not be
+   construed as a distribution of this Package.
+
+9. The name of the Copyright Holder may not be used to endorse or promote
+   products derived from this software without specific prior written
+   permission.
+
+10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
+    WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+    MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/htdocs/SEX.html b/htdocs/SEX.html
new file mode 100644 (file)
index 0000000..81b83ad
--- /dev/null
@@ -0,0 +1,64 @@
+<HTML> 
+<HEAD><TITLE>Stream EXchange</TITLE></HEAD>
+<BODY>
+<center></center>
+<h1>Stream EXchange (SEX)</h1>
+
+You can imagine SEX as network wide UNIX pipes with a relay between or as
+network based FIFOs.<br>
+This can be useful for piping data from A to B where
+A and B cannot establish a direct connection, but both can connect to the
+SEX-server by HTTP.<br>
+SEX is a synchronous service in conjuction to F*EX
+which is asynchronous.
+<p>
+For seamless integration into the UNIX tool chain, there are the
+shell-tools <a href="download/sex.tar">sexsend and sexget</a>.
+<p>
+Using web browsers for sexing will not work, because they cannot handle
+streams in a pipe. So far, there are only the UNIX clients. Feel free to
+implement other clients for other operating systems :-)
+<p>
+Authentication is the same as with <a href="/">F*EX</a>.
+<p>
+Example:
+<p>
+<PRE>
+  root@lanldap:/var/log: tail -f syslog | sexsend framstag@rus.uni-stuttgart.de
+
+  framstag@blubb:/tmp: sexget | grep ldap
+</PRE>
+<p>
+If you need encryption, then use the standard UNIX toolbox and add an
+appropriate program to the pipe, for example: openssl bf
+
+<h3>public SEX</h3>
+The recipient of regular SEX must be a registered F*EX user.<br>
+But you can also offer streams to non-registered user.<br>
+This is called "<em>public SEX</em>", example:
+<p>
+<PRE>
+  framstag@flupp: cal | sexsend public
+  http://fex.rus.uni-stuttgart.de/sex?user=framstag@rus.uni-stuttgart.de&ID=public
+  http://fex.rus.uni-stuttgart.de/sex?dXNlcj1mcmFtc3RhZ0BydXMudW5pLXN0dXR0Z2FydC5kZSZJRD1wdWJsaWM=
+</PRE>
+<p>
+sexsend then displays two URLs onder which one can get the stream.
+
+<h3>SEX - all inclusive</h3>
+To transfer files by streaming you can use xx-rated SEX:
+<p>
+<PRE>
+  root@vms2:/data/VM# <a href="/download/fuck.tar">sexxx</a> flupp
+  streaming:
+  flupp/
+  flupp/vmware.log
+  flupp/flupp.nvram
+  flupp/flupp.vmxf
+  flupp/flupp.vmx
+  flupp/flupp.vmdk
+  4976 MB 11640 kB/s
+</PRE>
+
+</BODY>
+</HTML>
diff --git a/htdocs/action-fex-camel.gif b/htdocs/action-fex-camel.gif
new file mode 100644 (file)
index 0000000..c9fcc28
Binary files /dev/null and b/htdocs/action-fex-camel.gif differ
diff --git a/htdocs/download/fexget b/htdocs/download/fexget
new file mode 100755 (executable)
index 0000000..034ced1
--- /dev/null
@@ -0,0 +1,1074 @@
+#!/usr/bin/perl -w
+
+# CLI client for the FEX service for retrieving files
+#
+# see also: fexsend
+#
+# Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
+#
+# Perl Artistic Licence
+
+use 5.006;
+use strict qw'vars subs';
+use Config;
+use POSIX;
+use Encode;
+use Getopt::Std;
+use File::Basename;
+use Socket;
+use IO::Handle;
+use IO::Socket::INET;
+use Time::HiRes 'time';
+use constant k => 2**10;
+use constant M => 2**20;
+
+eval 'use Net::INET6Glue::INET_is_INET6';
+
+$| = 1;
+
+our $SH;
+our ($fexhome,$idf,$tmpdir,$windoof,$useragent);
+our ($xv,%autoview);
+our $bs = 2**16; # blocksize for tcp-reading and writing file
+our $version = 20150120;
+our $CTYPE = 'ISO-8859-1';
+our $fexsend = $ENV{FEXSEND} || 'fexsend';
+
+my %SSL = (SSL_version => 'TLSv1');
+my $sigpipe;
+
+# inquire default character set
+# cannot use "use I18N::Langinfo" because of no windows support!
+eval {
+  local $^W = 0;
+  require I18N::Langinfo;
+  I18N::Langinfo->import(qw'langinfo CODESET');
+  $CTYPE = langinfo(CODESET());
+};
+
+if ($Config{osname} =~ /^mswin/i) {
+  $windoof = $Config{osname};
+  $ENV{HOME} = $ENV{USERPROFILE};
+  $fexhome = $ENV{FEXHOME} || $ENV{HOME}.'/fex';
+  $tmpdir = $ENV{FEXTMP} || $ENV{TMP} || "$fexhome/tmp";
+  $idf = "$fexhome/id";
+  $useragent = sprintf("fexget-$version (%s %s)",
+                       $Config{osname},$Config{archname});
+  $SSL{SSL_verify_mode} = 0;
+  chdir $ENV{USERPROFILE}.'\Desktop';
+  # open XX,'>XXXXXX';close XX;
+} else {
+  $0 =~ s:(.*)/:: and $ENV{PATH} .= ":$1";
+  $fexhome = $ENV{FEXHOME} || $ENV{HOME}.'/.fex';
+  $tmpdir = $ENV{FEXTMP} || "$fexhome/tmp";
+  $idf = "$fexhome/id";
+  $_ = `(lsb_release -d||uname -a)2>/dev/null`||'';
+  chomp;
+  s/^Description:\s+//;
+  $useragent = "fexget-$version ($_)";
+}
+
+if (-f ($_ = '/etc/fex/config.pl')) {
+  eval { require } or warn $@;
+}
+
+my $usage = <<EOD;
+usage: $0 [-v] [-m limit] [-s filename] [-o] [-k] [-X] [-P proxy:port] F*EX-URL(s)
+   or: $0 [-v] -d F*EX-URL(s)
+   or: $0 [-v] -f F*EX-URL(s) e-mail-address
+   or: $0 [-v] -a
+   or: $0 -l [-i tag]
+   or: $0 -H
+options: -v verbose mode
+         -m limit kB/s
+         -s save to filename (-s- means: write to STDOUT/pipe)
+         -o overwrite existing file
+        -k keep on server after download
+        -X do not extract archive files or autoview file
+        -d delete without download
+        -f forward a file to another recipient
+         -a get all files (implies -X)
+        -l list files on server
+         -i tag alternate server/account, see: $fexsend -h
+         -P use Proxy for connection to the F*EX server
+         -H show hints and examples
+argument: F*EX-URL may be file number (see: $0 -l)
+EOD
+
+my $hints = <<'EOD';
+When you download a file with extension .jpg .gif .png or .tif an image viewer
+will be started. This can be xv or xdg-open.
+In $HOME/.fex/config.pl you can set your prefered autoview applications:
+
+%autoview = (
+  '\.(gif|jpg|png|tiff?)' => 'my_prefered_image_viewer',
+  '\.(avi|mp4|mov)'       => 'vlc -f',
+  '\.pdf'                 => 'evince',
+);
+
+For HTTPS you can set the environment variables:
+SSLVERIFY=1                 # activate server identity verification
+SSLVERSION=TLSv1            # this is the default
+SSLCAPATH=/etc/ssl/certs    # path to trusted (root) certificates
+SSLCAFILE=/etc/ssl/cert.pem # file with trusted (root) certificates
+SSLCIPHERLIST=HIGH:!3DES    # see http://www.openssl.org/docs/apps/ciphers.html
+
+You can set these environment variables also in $HOME/.fex/config.pl, as well as
+the $opt_* variables, e.g.:
+  
+$ENV{SSLVERSION} = 'TLSv1';
+${'opt_+'} = 1;
+$opt_m = 200;
+EOD
+
+if ($windoof and not @ARGV and not $ENV{PROMPT}) {
+  # restart with cmd.exe to have mouse cut+paste
+  my $cmd = "cmd /k \"$0\"";
+  # print "$cmd\n";
+  exec $cmd;
+  exit;
+}
+
+my $atype = '\.(tgz|tar|zip|7z)$';
+my $proxy = '';
+my $proxy_prefix = '';
+my $chunksize;
+
+our ($opt_h,$opt_v,$opt_l,$opt_d,$opt_m,$opt_z,$opt_K,$opt_o,$opt_a);
+our ($opt_s,$opt_k,$opt_i,$opt_V,$opt_X,$opt_f,$opt_P,$opt_L,$opt_H);
+$opt_m = $opt_h = $opt_v = $opt_l = $opt_d = $opt_K = $opt_o = $opt_a = 0;
+$opt_V = $opt_X = $opt_f = $opt_L = $opt_H = 0;
+${'opt_+'} = 0;
+$opt_s = $opt_k = $opt_i = $opt_P = '';
+$_ = "$fexhome/config.pl"; require if -f;
+getopts('hvVHlLdkzoaXf+m:s:i:K:P:') or die $usage;
+$opt_k = '?KEEP' if $opt_k;
+
+if ($opt_m =~ /(\d+)/) {
+  $opt_m = $1
+} else {
+  $opt_m = 0
+}
+
+print "Version: $version\n" if $opt_V;
+die $usage                  if $opt_h;
+if ($opt_H) {
+  print $hints;
+  exit;
+}
+
+# set SSL/TLS options
+$SSL{SSL_verify_mode} = $ENV{SSLVERIFY} if defined($ENV{SSLVERIFY});
+foreach my $opt (qw(
+  SSL_version
+  SSL_cipher_list 
+  SSL_verify_mode 
+  SSL_ca_path 
+  SSL_ca_file)
+) {
+  my $env = uc($opt);
+  $env =~ s/_//g;
+  $SSL{$opt} = $ENV{$env} if defined($ENV{$env});
+}
+
+if ($SSL{SSL_verify_mode}) {
+  &search_ca;
+  unless ($SSL{SSL_ca_path} or $SSL{SSL_ca_file}) {
+    die "$0: \$SSLVERIFYMODE, but not valid \$SSLCAPATH or \$SSLCAFILE\n";
+  }
+} elsif (defined($SSL{SSL_verify_mode})) {
+  # user has set SSLVERIFY=0 !
+} else {
+  &search_ca;
+  $SSL{SSL_verify_mode} = 1 if $SSL{SSL_ca_path} or $SSL{SSL_ca_file};
+}
+
+sub search_ca {
+  local $_;
+  return if $SSL{SSL_ca_file} or $SSL{SSL_ca_path};
+  foreach (qw(/etc/ssl/certs/ca-certificates.crt)) {
+    if (-f) {
+      $SSL{SSL_ca_file} = $_;
+      return;
+    }
+  }
+  foreach (qw(/etc/ssl/certs /etc/pki/tls/certs)) {
+    if (-f) {
+      $SSL{SSL_ca_path} = $_;
+      return;
+    }
+  }
+}
+
+my $ffl = "$tmpdir/fexget";            # F*EX files list (cache)
+
+my @rcamel = (
+'\e[A
+(_*)  _  _     
+   \\\\/ \\/ \\
+    \  __  )=*
+    //\\\\//\\\\   
+',
+'\e[A     \\\\/\\\\/ 
+',
+'\e[A    //\\\\//\\\\
+');
+
+# get fexlog
+if ($opt_z) {
+  my $cmd = "$fexsend -Z";
+  $cmd .= " -i $opt_i" if $opt_i;
+  warn "$cmd\n" if $opt_v;
+  exec $cmd;
+  die "$0: cannot run $cmd : $!\n";
+}
+
+if ($opt_l) {
+  &list;
+  exit;
+}
+
+if ($opt_L) {
+  my $cmd = "$fexsend -L";
+  $cmd .= " -i $opt_i" if $opt_i;
+  warn "$cmd\n" if $opt_v;
+  exec $cmd;
+  die "$0: cannot run $cmd : $!\n";
+}
+
+if ($opt_P) {
+  if ($opt_P =~ /^([\w.-]+:\d+)(:(\d+))?/) {
+    $proxy = $1;
+    $chunksize = $3 || 0;
+  } else {
+    die "$0: proxy must be: SERVER:PORT\n";
+  }
+}
+
+if ($opt_a) {
+  $opt_X = $opt_a;
+  die $usage if @ARGV;
+  &list;
+  print "\n";
+  if (open $ffl,$ffl) {
+    while (<$ffl>) {
+      push @ARGV,$1 if /^\s+(\d+)/;
+    }
+    close $ffl;
+  }
+} else {
+  unless (@ARGV) {
+    if ($windoof) {
+      my $url;
+      for (;;) {
+        print "download-URL: ";
+        chomp($url = <STDIN>);
+        if ($url =~ /^http/) {
+          @ARGV = ($url);
+          last;
+        }
+      }
+    } else {
+      die $usage;
+    }
+  }
+}
+
+my ($file,%files,$download,$server,$port,$fop);
+
+if ($opt_f) {
+  unless ($ENV{FEXID} or -f $ENV{HOME}.'/.fex/id') {
+    die "$0: no local FEXID\n";
+  }
+  $opt_f = pop(@ARGV);
+  if ($opt_f =~ /^\d+$|^https?:/) {
+    die "$0: $opt_f is not an e-mail address\n";
+  }
+}
+
+URL: foreach my $url (@ARGV) {
+
+  # do not overrun server
+  sleep 1 if $fop;
+
+  if ($url !~ /^http/) {
+    unless (%files) {
+      open $ffl,$ffl or die "$0: no $ffl, use first: $0 -l\n";
+      my $from = '';
+      while (<$ffl>) {
+        if (/^from (.+) :$/) {
+          $from = $1;
+        } elsif (/^\s*(\d+)\)\s+\d+ MB.* (http\S+)/) {
+          push @{$files{all}},$2;
+          push @{$files{$from}},$2;
+        }
+      }
+      close $ffl;
+    }
+
+    if ($url =~ /^(\d+)$/) {
+      $url = ${files{all}}[$1-1] or die "$0: unknown file number\n";
+    }
+  }
+
+  if ($url =~ m{^http(s?)://([\w\.\-]+)(:(\d+))?(/.*fop/\S+)}) {
+    $server = $2;
+    $port   = $4 || ($1?443:80);
+    $fop    = $5;
+  } else {
+    die "$0: unknown F*EX URL $url\n";
+  }
+
+  if ($proxy) {
+    if    ($port == 80)   { $proxy_prefix = "http://$server" }
+    elsif ($port == 443)  { $proxy_prefix = "" }
+    else                  { $proxy_prefix = "http://$server:$port" }
+  }
+
+  serverconnect($server,$port);
+
+  if ($opt_f) {
+    forward($url);
+    next;
+  }
+
+  if ($opt_d) {
+    my @r = del($url);
+    $_ = shift @r;
+    if (/^HTTP.* 200/) {
+      ($file) = grep { $_ = $1 if /^X-File:\s+(.+)/ } @r;
+      $file = $url unless $file;
+      $file =~ s:.*/::;
+      printf "%s deleted\n",urldecode($file);
+    } else {
+      s:HTTP/[\d\. ]+::;
+      die "$0: server response: $_";
+    }
+    next;
+  }
+
+  if ($opt_K) {
+    my @r = keep($url);
+    $_ = shift @r;
+    if (/^HTTP.* 200/) {
+      $file = $url;
+      $file =~ s:.*/::;
+      print "$file kept\n";
+    } else {
+      s:HTTP/[\d\. ]+::;
+      die "$0: server response: $_";
+    }
+    next;
+  }
+
+  $download = download($server,$port,$fop);
+  exit if $opt_s eq '-';
+  unlink $download unless -s $download;
+  exit 2 unless -f $download;
+  
+  if ($windoof) {
+    print "READY\n";
+    exit;
+  }
+
+  if (not $opt_X and $download =~ /\.gpg$/) {
+    if (-t) {
+      print "decrypt \"$download\"? ";
+      $_ = <STDIN>||'y';
+      unless (/^[y\n]/i) {
+        print "keeping \"$download\"\n";
+        exit;
+      }
+    }
+    if (system('gpg',$download) == 0) {
+      unlink $download;
+      $download =~ s/\.gpg$//;
+    }
+  }
+
+  unless ($opt_X) {
+    
+    foreach my $a (keys %autoview) {
+      if ($download =~ /$a$/i and $autoview{$a}) {
+        printf "run \"%s %s\" [Yn] ? ",$autoview{$a},basename($download);
+        $_ = <STDIN>||'';
+        system sprintf("%s %s",$autoview{$a},quote($download)) if /^y|^$/i;
+        next URL;
+      }
+    }
+    
+    if ($ENV{DISPLAY} and $download =~ /\.(gif|jpg|png|tiff?)$/i) {
+      # see also mimeopen and xdg-mime
+      if (my $xv = $xv || pathsearch('xv') || pathsearch('xdg-open')) {
+        printf "run \"%s %s\" [Yn] ? ",basename($xv),basename($download);
+        $_ = <STDIN>||'';
+        system $xv,$download if /^y|^$/i;
+        next URL;
+      }
+    }
+  
+    if ($download =~ /$atype/) {
+      if    ($download =~ /\.(tgz|tar.gz)$/)  { extract('tar tvzf','tar xvzf') }
+      elsif ($download =~ /\.tar$/)           { extract('tar tvf','tar xvf') } 
+      elsif ($download =~ /\.zip$/i)          { extract('unzip -l','unzip') } 
+      elsif ($download =~ /\.7z$/i)           { extract('7z l','7z x') }
+      else { die "$0: unknown archive \"$download\"\n" }
+      if ($? == 0) {
+        unlink $download;
+      } else {
+        die "$0: keeping \"$download\"\n";
+      }
+    }
+  }
+
+}
+
+exit;
+
+sub extract {
+  my $l = shift;
+  my $x = shift;
+  my $d = $download;
+  my $xd = '.';
+  local $_;
+  
+  if (-t and not $windoof) {
+    print "Files in archive:\n";
+    system(split(' ',$l),$download);
+    $d =~ s:.*/:./:;
+    $d =~ s/\.[^.]+$//;
+    for (;;) {
+      $xd = inquire("extract to directory (Ctrl-C to keep archive): ",$d);
+      last if $xd =~ s:^(\./*)*!?$:./:;
+      if ($xd eq '-') {
+        print "keeping $download\n";
+        exit;
+      }    
+      if ($xd !~ s/!$//) {
+        if (-d $xd) {
+          print "directory $xd does already exist, add \"!\" to overwrite\n";
+          redo;
+        }
+        unless (mkdir $xd) {
+          print "cannot mkdir $xd - $!\n";
+          redo;
+        }
+      }
+      unless (chdir $xd) {
+        print "cannot chdir $xd - $!\n";
+        redo;
+      }
+      last;
+    }
+  }
+  print "extracting to $xd :\n";
+  system(split(' ',$x),$download);
+}
+
+sub del {
+  my $url = shift;
+  my ($server,$port);
+  my $del;
+  my @r;
+
+  if ($url =~ m{^http(s?)://([\w\.\-]+)(:(\d+))?(/fop/.+)}) {
+    $server = $2;
+    $port   = $4 || ($1?443:80);
+    $del    = $5.'?DELETE';
+  } else {
+    die "$0: unknown F*EX URL $url\n";
+  }
+
+  sendheader("$server:$port","GET $del HTTP/1.1","User-Agent: $useragent");
+  while (<$SH>) {
+    s/\r//;
+    last if /^\n/; # ignore HTML output
+    warn "<-- $_" if $opt_v;
+    push @r,$_;
+  }
+  die "$0: no response from fex server $server\n" unless @r;
+  return @r;
+}
+
+
+sub forward {
+  my $url = shift;
+  my ($server,$port);
+  my ($uri,$dkey,$list,$cmd,$n);
+  my @r;
+
+  if ($url =~ m{^http(s?)://([\w\.\-]+)(:(\d+))?(/fop/.+)}) {
+    $server = $2;
+    $port   = $4 || ($1?443:80);
+    $uri    = $5;
+  } else {
+    die "$0: unknown F*EX URL $url\n";
+  }
+
+  sendheader(
+    "$server:$port",
+    "GET $uri?COPY HTTP/1.1",
+    "User-Agent: $useragent",
+  );
+  
+  $_ = <$SH>;
+  die "$0: no reply from fex server $server\n" unless $_;
+  warn "<-- $_" if $opt_v;
+  
+  unless (/^HTTP.*200/) {
+    s/^HTTP.... \d+ //;
+    die "$0: $_";
+  }
+  
+  while (<$SH>) {
+    s/\r//;
+    last if /^\n/; # ignore HTML output
+    $dkey = $1 if /^Location:.*\/(\w+)\/.+/;
+    warn "<-- $_" if $opt_v;
+  }
+
+  $cmd = 'fexsend -l >/dev/null 2>&1';
+  print "$cmd\n" if $opt_v;
+  system 'fexsend -l >/dev/null 2>&1';
+  $list = $ENV{HOME}.'/.fex/tmp/fexlist';
+  open $list,$list or die "$0: cannot open $list - $!\n";
+  while (<$list>) {
+    if (/^\s+(\d+)\) (\w+)/ and $2 eq $dkey) {
+      $n = $1;
+      $cmd = "fexsend -b $n $opt_f";
+      print "$cmd\n" if $opt_v;
+      system $cmd;
+      last;
+    }
+  }
+  close $list;
+  
+  if ($n) {
+    $cmd = "fexsend -d $n >/dev/null 2>&1";
+    print "$cmd\n" if $opt_v;
+    system $cmd;
+  } else {
+    warn "$0: forwarding failed\n";
+  }
+}
+
+
+sub keep {
+  my $url = shift;
+  my ($server,$port);
+  my $keep;
+  my (@hh,@r);
+
+  if ($url =~ m{^http(s?)://([\w\.\-]+)(:(\d+))?(/fop/.+)}) {
+    $server = $2;
+    $port   = $4 || ($1?443:80);
+    $keep    = "$5?KEEP=$opt_K";
+  } else {
+    die "$0: unknown F*EX URL $url\n";
+  }
+
+  push @hh,"GET $keep HTTP/1.1",
+           "Host: $server:$port",
+           "User-Agent: $useragent",
+           "";
+
+  foreach (@hh) {
+    warn $_,"\n" if $opt_v;
+    print $SH $_,"\r\n";
+  }
+  while (<$SH>) {
+    s/\r//;
+    last if /^\n/;
+    push @r,$_;
+  }
+  die "$0: no response from fex server $server\n" unless @r;
+  grep { warn "\t$_" } @r if $opt_v;
+  return @r;
+}
+
+
+sub download {
+  my ($server,$port,$fop,$nocheck) = @_;
+  my ($file,$download,$ssl,$pipe,$filesize,$checkstorage);
+  my (@hh,@r);
+  my ($t0,$t1,$t2,$tt,$tm,$ts,$kBs,$b,$bt,$tb,$B,$buf);
+  my $length = 0;
+  my $seek = 0;
+  my $tc = 0;
+  local $_;
+  local *X;
+
+  if ($opt_s) {
+    $file = $opt_s;
+    if ($opt_s eq '-') {
+      $pipe = $download = $opt_s;
+    } elsif (-p $opt_s or -c $opt_s) {
+      $download = $opt_s;
+    } else {
+      $download = $file.'.tmp';
+      $seek = -s $download || 0;
+    }
+  } else {
+    # ask server for real file name
+    serverconnect($server, $port);
+    sendheader("$server:$port","HEAD $proxy_prefix$fop HTTP/1.1","User-Agent: $useragent");
+    my $reply = $_ = <$SH>;
+    unless (defined $_ and /\w/) {
+      die "$0: no response from server\n";
+    }
+    warn "<-- $_" if $opt_v;
+    unless (/^HTTP\/[\d.]+ 200/) {
+      s:HTTP/[\d. ]+::;
+      die "$0: server response: $_";
+    }
+    while (<$SH>) {
+      s/\r//;
+      warn "<-- $_" if $opt_v;
+      last if /^\r?\n/;
+      if (/^Content-Disposition: attachment; filename="(.+)"/i) {
+        $file = locale(decode_utf8($1));
+          $file =~ s:.*/::;
+      }
+    }
+    unless ($file) {
+      $file = $fop;
+      $file =~ s:.*/::;
+    }
+    $download = $file.'.tmp';
+    $seek = -s $download || 0;
+  }
+
+  push @hh,"GET $proxy_prefix$fop$opt_k HTTP/1.1",
+           "User-Agent: $useragent",
+           "Connection: close";
+  push @hh,"Range: bytes=$seek-" if $seek;
+
+  # HTTPS needs a new connection for actually downloading the file
+  serverconnect($server,$port) if $opt_P and $port == 443;
+  sendheader("$server:$port",@hh);
+  $_ = <$SH>;
+  die "$0: no response from fex server $server\n" unless $_;
+  s/\r//;
+
+  if (/^HTTP\/[\d.]+ 2/) {
+    warn "<-- $_" if $opt_v;
+    while (<$SH>) {
+      s/\r//;
+      warn "<-- $_" if $opt_v;
+      last if /^\r?\n/;
+      if (/^Content-length:\s*(\d+)/i) {
+        $length = $1;
+      } elsif (/^X-Size: (\d+)/i) {
+        $filesize = $1;
+      }
+    }
+  } else {
+    s/HTTP\/[\d.]+ \d+ //;
+    die "$0: bad server reply: $_";
+  }
+
+  if ($pipe) {
+    *X = *STDOUT;
+  } else {
+    if ($opt_s and $opt_s eq $download) {
+      open X,'>',$download or die "$0: cannot write to \"$download\" - $!\n";
+      $checkstorage = $filesize unless $nocheck;
+    } else {
+      if (-e $file and not $opt_o) {
+        die "$0: destination file \"$file\" does already exist\n";
+      }
+      if ($seek) {
+        open X,'>>',$download or die "$0: cannot write to \"$download\" - $!\n";
+      } else {
+        open X,'>',$download or die "$0: cannot write to \"$download\" - $!\n";
+        $checkstorage = $filesize unless $nocheck;
+      }
+    }
+    if ($checkstorage and not $nocheck) {
+      $t0 = time;
+      my $n = 0;
+      print STDERR "checking storage...\r";
+      $buf = '.' x M;
+      while (-s $download < $checkstorage) {
+        syswrite X,$buf or do {
+          unlink $download;
+          die "\n$0: cannot write $download - $!\n";
+        };
+        $n++;
+        print STDERR "checking storage... ".$n." MB\r";
+      }
+      close X or do {
+        unlink $download;
+        die "\n$0: cannot write $download - $!\n";
+      };
+      print STDERR "checking storage... ".$n." MB ok!\n";
+      unlink $download;
+      if (time-$t0 < 25) {
+        open X,'>',$download or die "$0: cannot write to \"$download\" - $!\n";
+      } else {
+        # retry after timeout
+        return(download($server,$port,$fop,'nocheck'))
+      }
+    }
+  }
+
+  $t0 = $t1 = $t2 = int(time);
+  $tb = $B = 0;
+  printf STDERR "resuming at byte %s\n",$seek if $seek;
+  print $rcamel[0] if ${'opt_+'};
+  while ($B < $length and $b = read $SH,$buf,$bs) {
+    syswrite X,$buf;
+    $B += $b;
+    $tb += $b;
+    $bt += $b;
+    $t2 = time;
+    if (${'opt_+'} and int($t2*10)>$tc) {
+      print $rcamel[$tc%2+1];
+      $tc = int($t2*10);
+    }
+    if (int($t2) > $t1) {
+      $kBs = int($bt/k/($t2-$t1));
+      $kBs = int($tb/k/($t2-$t0)) if $kBs < 10;
+      $t1 = $t2;
+      $bt = 0;
+      # smaller block size is better on slow links
+      $bs = 4096 if $bs>4096 and $tb/($t2-$t0)<65536;
+      if ($tb<10*M) {
+        printf STDERR "%s: %d kB (%d%%) %d kB/s \r",
+                      $download,
+                      int(($tb+$seek)/k),
+                      int(($tb+$seek)/($length+$seek)*100),
+                      $kBs;
+      } else {
+        printf STDERR "%s: %d MB (%d%%) %d kB/s        \r",
+                      $download,
+                      int(($tb+$seek)/M),
+                      int(($tb+$seek)/($length+$seek)*100),
+                      $kBs;
+      }
+    }
+    if ($opt_m) {
+      if ($t2 == $t0 and $B > $opt_m*k) {
+        print "\nsleeping...\r" if $opt_v;
+        sleep 1;
+      } else {
+        while ($t2 > $t0 and $tb/k/($t2-$t0) > $opt_m) {
+          print "\nsleeping...\r" if $opt_v;
+          sleep 1;
+          $t2 = time;
+        }
+      }
+    }
+  }
+  close $SH;
+  close X;
+  
+  print $rcamel[2] if ${'opt_+'};
+
+  $tt = $t2-$t0;
+  $tm = int($tt/60);
+  $ts = $tt-$tm*60;
+  $kBs = int($tb/k/($tt||1));
+  if ($seek) {
+    printf STDERR "$file: %d MB, last %d MB in %d s (%d kB/s)      \n",
+                  int(($tb+$seek)/M),int($tb/M),$tt,$kBs;
+  } else {
+    printf STDERR "$file: %d MB in %d s (%d kB/s)      \n",
+                  int($tb/M),$tt,$kBs;
+  }
+
+  if ($tb != $length) {
+    if ($windoof) {
+      exec "\"$0\" @ARGV";
+      exit;
+    } else {
+      die "$0: $server annouced $length bytes, but only $tb bytes has been read\n";
+    }
+  }
+
+  unless ($pipe or -p $download or -c $download) {
+    my @s = stat $file if -e $file;
+    rename $download,$file
+      or die "$0: cannot rename \"$download\" to \"$file\" - $!\n";
+    chmod $s[2],$file if @s;
+  }
+
+  return sprintf("%s/%s",getcwd(),$file);
+}
+
+
+sub list {
+  my $cmd = "$fexsend -L";
+  $cmd .= " -i $opt_i" if $opt_i;
+  if ($opt_v) {
+    $cmd .= " -v";
+    warn "$cmd\n";
+  }
+  open $cmd,"$cmd|" or die "$0: cannot run $cmd : $!\n";
+  open $ffl,'>',$ffl or die "$0: cannot open $ffl : $!\n";
+  my $n;
+  while (<$cmd>) {
+    if (/\d MB .*http/) {
+      $n++;
+      printf {$ffl} "%4d) %s",$n,$_;
+      s:http[^\"]*/::;
+      printf        "%4d) %s",$n,$_;
+    } else {
+      print;
+      print {$ffl} $_;
+    }
+  }
+}
+
+
+sub locale {
+  my $string = shift;
+
+  if ($CTYPE) {
+    if ($CTYPE =~ /UTF-?8/i) {
+      return $string;
+    } elsif (grep { $CTYPE =~ /^$_$/i } Encode->encodings()) {
+      return encode($CTYPE,$string);
+    } else {
+      return encode('ISO-8859-1',$string);
+    }
+  }
+
+  return $string;
+}
+
+
+sub pathsearch {
+  my $prg = shift;
+  
+  foreach my $dir (split(':',$ENV{PATH})) {
+    return "$dir/$prg" if -x "$dir/$prg";
+  }
+}
+
+    
+sub quote {
+  local $_ = shift;
+  s/([^\w¡-ÿ_%\/=~:.,-])/\\$1/g;
+  return $_;
+}
+
+    
+{
+  my $tty;
+
+  sub inquire {
+    my $prompt = shift;
+    my $default = shift;
+    local $| = 1;
+    local $_;
+
+    if (defined $default) {
+      unless ($tty) {
+        chomp($tty = `tty 2>/dev/null`);
+        eval { local $^W; require "sys/ioctl.ph"; };
+      }
+
+      if (defined(&TIOCSTI) and $tty and open($tty,'>',$tty)) {
+        print $prompt;
+        foreach my $a (split("",$default)) { ioctl($tty,&TIOCSTI,$a) } 
+        chomp($_ = <STDIN>||'');
+      } else {
+        $prompt =~ s/([\?:=]\s*)/ [$default]$1/ or $prompt .= " [$default]";
+        print $prompt;
+        chomp($_ = <STDIN>||'');
+        $_ = $default unless length;
+      }
+    } else {
+      print $prompt;
+      chomp($_ = <STDIN>||'');
+    }
+
+    return $_;
+  }    
+}    
+
+
+### common functions ###
+
+
+sub mtime {
+  my @d = localtime((stat shift)[9]);
+  return sprintf('%d%02d%02d',$d[5]+1900,$d[4]+1,$d[3]);
+}
+
+
+sub urldecode {
+  local $_ = shift;
+  s/\%([a-f\d]{2})/chr(hex($1))/ige;
+  return $_;
+}
+
+
+sub get_ssl_env {
+  # set SSL/TLS options
+  $SSL{SSL_verify_mode} = $ENV{SSLVERIFY} if defined($ENV{SSLVERIFY});
+  foreach my $opt (qw(
+    SSL_version
+    SSL_cipher_list 
+    SSL_verify_mode 
+    SSL_ca_path 
+    SSL_ca_file)
+  ) {
+    my $env = uc($opt);
+    $env =~ s/_//g;
+    $SSL{$opt} = $ENV{$env} if defined($ENV{$env});
+  }
+
+  if ($SSL{SSL_verify_mode}) {
+    &search_ca;
+    unless ($SSL{SSL_ca_path} or $SSL{SSL_ca_file}) {
+      die "$0: \$SSLVERIFYMODE, but not valid \$SSLCAPATH or \$SSLCAFILE\n";
+    }
+  } elsif (defined($SSL{SSL_verify_mode})) {
+    # user has set SSLVERIFY=0 !
+  } else {
+    &search_ca;
+    $SSL{SSL_verify_mode} = 1 if $SSL{SSL_ca_path} or $SSL{SSL_ca_file};
+  }
+}
+
+sub search_ca {
+  local $_;
+  return if $SSL{SSL_ca_file} or $SSL{SSL_ca_path};
+  foreach (qw(/etc/ssl/certs/ca-certificates.crt)) {
+    if (-f) {
+      $SSL{SSL_ca_file} = $_;
+      return;
+    }
+  }
+  foreach (qw(/etc/ssl/certs /etc/pki/tls/certs)) {
+    if (-f) {
+      $SSL{SSL_ca_path} = $_;
+      return;
+    }
+  }
+}
+
+
+sub serverconnect {
+  my ($server,$port) = @_;
+  my $connect = "CONNECT $server:$port HTTP/1.1";
+  local $_;
+  
+  if ($opt_v and $port == 443 and %SSL) {
+    foreach my $v (keys %SSL) {
+      printf "%s => %s\n",$v,$SSL{$v};
+    }
+  }
+  
+  if ($proxy) {
+    tcpconnect(split(':',$proxy));
+    if ($port == 443) {
+      printf "--> %s\n",$connect if $opt_v;
+      nvtsend($connect,"");
+      $_ = <$SH>;
+      s/\r//;
+      printf "<-- $_"if $opt_v;
+      unless (/^HTTP.1.. 200/) {
+        die "$0: proxy error : $_";
+      }
+      eval "use IO::Socket::SSL";
+      die "$0: cannot load IO::Socket::SSL\n" if $@;
+      $SH = IO::Socket::SSL->start_SSL($SH,%SSL);
+    }
+  } else {
+    tcpconnect($server,$port);
+  }
+#  if ($port == 443 and $opt_v) {
+#    printf "%s\n",$SH->get_cipher();
+#  }
+}
+
+
+# set up tcp/ip connection
+sub tcpconnect {
+  my ($server,$port) = @_;
+  
+  if ($SH) {
+    close $SH;
+    undef $SH;
+  }
+  
+  if ($port == 443) {
+    # eval "use IO::Socket::SSL qw(debug3)";
+    eval "use IO::Socket::SSL";
+    die "$0: cannot load IO::Socket::SSL\n" if $@;
+    $SH = IO::Socket::SSL->new(
+      PeerAddr => $server,
+      PeerPort => $port,
+      Proto    => 'tcp',
+      %SSL
+    );
+  } else {
+    $SH = IO::Socket::INET->new(
+      PeerAddr => $server,
+      PeerPort => $port,
+      Proto    => 'tcp',
+    );
+  }
+  
+  if ($SH) {
+    autoflush $SH 1;
+  } else {
+    die "$0: cannot connect $server:$port - $@\n";
+  }
+  
+  print "TCPCONNECT to $server:$port\n" if $opt_v;
+}
+
+
+sub sendheader {
+  my $sp = shift;
+  my @head = @_;
+  my $head;
+  
+  push @head,"Host: $sp";
+  
+  foreach $head (@head) {
+    print "--> $head\n" if $opt_v;
+    print {$SH} $head,"\r\n";
+  }
+  print "-->\n" if $opt_v;
+  print {$SH} "\r\n";
+}
+
+
+sub nvtsend {
+  local $SIG{PIPE} = sub { $sigpipe = "@_" };
+  
+  $sigpipe = '';
+  
+  die "$0: internal error: no active network handle\n" unless $SH;
+  die "$0: remote host has closed the link\n" unless $SH->connected;
+  
+  foreach my $line (@_) {
+    print {$SH} $line,"\r\n";
+    if ($sigpipe) {
+      undef $SH;
+      return 0;
+    }
+  }
+  
+  return 1;
+}
+
+
+# from MIME::Base64::Perl
+sub encode_b64 {
+  my $res = "";
+  my $eol = "\n";
+  my $padding;
+  
+  pos($_[0]) = 0;
+  $res = join '',map(pack('u',$_)=~ /^.(\S*)/, ($_[0]=~/(.{1,45})/gs));
+  $res =~ tr|` -_|AA-Za-z0-9+/|;
+  $padding = (3-length($_[0])%3)%3;
+  $res =~ s/.{$padding}$/'=' x $padding/e if $padding;
+  return $res;
+}
diff --git a/htdocs/download/fexsend b/htdocs/download/fexsend
new file mode 100755 (executable)
index 0000000..607d139
--- /dev/null
@@ -0,0 +1,3055 @@
+#!/usr/bin/perl -w
+
+# CLI client for the F*EX service (send, list, delete)
+#
+# see also: fexget
+#
+# Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
+#
+# Perl Artistic Licence
+
+use 5.006;
+use strict qw'vars subs';
+use Encode;
+use Config;
+use Socket;
+use IO::Handle;
+use IO::Socket::INET;
+use Getopt::Std;
+use File::Basename;
+use Cwd qw'abs_path';
+use Fcntl qw':flock :mode';
+use Digest::MD5 qw'md5_hex';  # encrypted ID / SID
+use Time::HiRes qw'time';
+# use Smart::Comments;
+use constant k => 2**10;
+use constant M => 2**20;
+
+eval 'use Net::INET6Glue::INET_is_INET6';
+
+&update if "@ARGV" eq 'UPDATE';
+
+$| = 1;
+
+our ($SH,$fexhome,$idf,$tmpdir,$windoof,$useragent,$editor,$nomail);
+our ($anonymous,$public);
+our ($tpid,$frecipient);
+our ($FEXID,$FEXXX,$HOME);
+our (%alias);
+our $chunksize = 0;
+our $version = 20150120;
+our $_0 = $0;
+our $DEBUG;
+
+my %SSL = (SSL_version => 'TLSv1');
+my $sigpipe;
+
+if ($Config{osname} =~ /^mswin/i) {
+  $windoof = $Config{osname};
+  $HOME = $ENV{USERPROFILE};
+  $fexhome = $ENV{FEXHOME} || $HOME.'\fex';
+  $tmpdir = $ENV{FEXTMP} || $ENV{TEMP} || "$fexhome\\tmp";
+  $idf = "$fexhome\\id";
+  $editor = $ENV{EDITOR} || 'notepad.exe';
+  $useragent = sprintf("fexsend-$version (%s %s)",
+                       $Config{osname},$Config{archname});
+  $SSL{SSL_verify_mode} = 0;
+} else {
+  $0 =~ s:.*/::;
+  $HOME = (getpwuid($<))[7]||$ENV{HOME};
+  $fexhome = $HOME.'/.fex';
+  $tmpdir = $ENV{FEXTMP} || "$fexhome/tmp";
+  $idf = "$fexhome/id";
+  $editor = $ENV{EDITOR} || 'vi';
+  $_ = `(lsb_release -d||uname -a)2>/dev/null`||'';
+  chomp;
+  s/^Description:\s+//;
+  $useragent = "fexsend-$version ($_)";
+  chmod 0600,$idf;
+}
+
+if (-f ($_ = '/etc/fex/config.pl')) {
+  eval { require } or warn $@;
+}
+
+my $from = '';
+my $to = '';
+my $id = '';
+my $skey = '';
+my $gkey = '';
+my $atype = '';                # archive type
+my $fexcgi;            # F*EX CGI URL
+my @files;             # files to send
+my %AB = ();           # server based address book
+my ($server,$port,$sid);
+my $proxy = '';
+my $proxy_prefix = '';
+my $features = ''; 
+my $timeout = 30;      # server timeout
+my $fexlist = "$tmpdir/fexlist";
+my ($usage,$hints);
+my $xx = $0 =~ /^xx/;
+
+if ($xx) {
+  $usage = "usage: send file(s):               xx [:slot] file...\n".
+           "   or: send STDIN:                 xx [:slot] -\n".
+           "   or: send pipe:                  ... | xx [:slot] \n".
+           "   or: get file(s) or STDIN:       xx [:slot] \n".
+           "   or: get file(s) no-questions:   xx [:slot] --\n".
+           "examples: dmesg | xx\n".
+           "          xx project\n".
+           "          xx --\n".
+           "          xx :conf /etc /boot\n";
+} else {
+  $usage = <<EOD;
+usage: $0 [options] file(s) [@] recipient(s)
+   or: $0 [special options]
+   or: $0 -f \# recipient(s)
+   or: $0 -x \# [-C -k -D -K -S]
+options: -v           verbose mode
+         -d           delete file on fex server
+         -c           compress file
+         -g           encrypt file with gpg
+         -m limit     limit throughput (kB/s)
+         -i tag       use ID data [tag] from ID file
+         -C comment   add comment to notification e-mail
+         -k max       keep file max days on fex server
+         -D           delay auto-delete after download
+         -K           no auto-delete after download
+         -M           MIME-file (to be displayed in recipient\'s webbrowser)
+         -o           overwrite mode, do not resume
+         -a archive   put files in archive (.zip .7z .tar .tgz)
+         -s stream    read data from pipe and upload it with stream name
+special options: -I      initialize ID file or show ID
+                 -I tag  add alternate ID data (secondary logins) to ID file
+                 -l      list sent files numbered (# needed for -f -x -d -N)
+                 -f \#    forward already uploaded file to another recipient
+                 -x \#    modify options -C -k -D -K for already uploaded file
+                 -d \#    delete file on fex server
+                 -N \#    resend notification e-mail
+                 -Q      check quotas
+                 -A      edit server address book (aliases)
+                 -S      show server/user settings and auth-ID
+                 -H      show hints, examples and more options
+                 -V      show version
+                 (\# is a file number, see output from $0 -l)
+examples: $0 visualization.mpg framstag\@rus.uni-stuttgart.de
+          $0 -a images.zip *.jpg webmaster\@flupp.org,metoo
+          lshw | $0 -s hardware.list admin\@flupp.org
+EOD
+#   or: $0 -R FEX-URL e-mail
+#         -R FEX mail  self-register your e-mail address at FEX server
+
+  $hints = <<EOD;
+$0 hints and more options:
+  
+usage: $0 [options] file recipient(s)
+
+Recipient can be a comma separated address list. Example:
+  $0 big.file framstag\@rus.uni-stuttgart.de,webmaster\@flupp.org
+
+Recipient can be an alias from your server address book 
+(use "$0 -A" to edit it). Example:
+  $0 big.file framstag
+
+Recipient can be a SKEY URL, which you have received from a regular F*EX user.
+When using this URL you are a subuser of this full user and the file will be 
+sent to him. Example:
+  $0 big.file http://fex.rus.uni-stuttgart.de/fup?skey=4285f8cdd881626524fba686d5f0a83a
+
+Recipient can be a GKEY URL, which you have received from a regular F*EX user.
+Using this URL you are a member of his group and the file will be sent to all
+members of this group. Example:
+  $0 big.file http://fex.rus.uni-stuttgart.de/fup?gkey=50d26547b1e8c1110beb8748fc1d9444
+
+When you use "FEX-URL/anonymous" as recipient and your F*EX administrator has 
+allowed anonymous upload for your IP address then no auth-ID is needed.
+    
+"." as recipient means fex to yourself and show immediately the download URL 
+(no notification e-mail will be sent). Example:
+  $0 software.tar .
+
+"//" as recipient means fex to yourself and create extra short download URL.
+Example:
+  $0 software.tar //
+
+If you want a Bcc of the notification e-mail then add '!bcc!' to the comment:
+fexsend -C '!bcc! for me and you' ...
+
+Additional special options:
+
+  -. sends a short instead of a detailed notification e-mail
+  -/ does not upload the file, but tells the server to link it
+  -= uses an alias name as file name
+  -# excludes files (# is list separator) from archive -a
+  -n sends no notification e-mail, but shows the download URL immediately
+  -q is quiet mode
+  -r ADDRESS sets e-mail Reply-To ADDRESS
+  -F activates female mode
+  -U show authorized URL
+  -+ is an undocumented feature - test it :-)
+    
+To manage your subuser and groups or forward or redirect files, use a 
+webbrowser with the URL from "$0 -U", e.g.:  firefox \$($0 -U)
+
+If you want to copy-forward an already uploaded file to another recipient,
+then you first have to query the file number with:
+  $0 -l
+and then copy-forward it with:
+  $0 -b # other\@address
+Where # is the file number.
+
+If you want to modify the keep time, comment or auto-delete behaviour of an
+already uploaded file then you first have to query the file number with:
+  $0 -l
+and then for example set the keep time to 30 days with:
+  $0 -x # -k 30
+Where # is the file number.
+
+With option -a you can send several files or whole directories within a single
+archive file. The archive types tar and tgz are build on-the-fly (streaming) 
+whereas archive types zip and 7z need a temporary archive file on local disk.
+
+With option -s you can send any data coming from a pipe (STDIN) as a file
+without wasting local disc space.
+With option -X you can specify any parameter, e.g.: -X autodelete=yes
+
+For HTTPS you can set the environment variables:
+SSLVERIFY=1                 # activate server identity verification
+SSLVERSION=TLSv1            # this is the default
+SSLCAPATH=/etc/ssl/certs    # path to trusted (root) certificates
+SSLCAFILE=/etc/ssl/cert.pem # file with trusted (root) certificates
+SSLCIPHERLIST=HIGH:!3DES    # see http://www.openssl.org/docs/apps/ciphers.html
+  
+Partner program xx is an internet clipboard. See: xx -h
+  
+Partner program fexget is for downloading. See: fexget -h
+  
+For temporary usage of a HTTP proxy use: 
+  $0 -P your_proxy:port:chunksize_in_MB file recipient
+Example:
+  $0 -P wwwproxy.uni-stuttgart.de.de:8080:1024 4GB.tar .
+  
+For temporary usage of an alternative F*EX server or user use: 
+  FEXID="FEXSERVER USER AUTHID" $0 file recipient
+Example:
+  FEXID="fex.flupp.org gaga\@flupp.org blubb" $0 big.file framstag\@rus.uni-stuttgart.de
+
+You can define aliases (and optional fexsend options) in \$HOME/.fex/config.pl:
+  %alias = (
+    'alias1' => 'user1\@domain1.org',
+    'alias2' => 'user2\@domain2.org',
+    'both'   => 'user1\@domain1.org,user2\@domain2.org',
+    'extra'  => 'extra\@special.net:-i other -K -k 30',
+  );
+
+fexsend also respects aliases in $HOME/.mutt/aliases
+The alias priority is (descending):
+\$HOME/.fex/config.pl
+\$HOME/.mutt/aliases 
+fexserver address book  
+
+In \$HOME/.fex/config.pl you can also set the SSL* environment variables and the
+\$opt_* variables, e.g.:
+  
+\$ENV{SSLVERSION} = 'TLSv1';
+\${'opt_+'} = 1;
+\$opt_m = 200;
+EOD
+}
+
+my @rcamel = (
+'\e[A
+     _  _  c*_)
+    / \/ \//
+ *=(  __  /
+    \\\\/\\\\/
+',
+'\e[A    \\\\/\\\\/ 
+',
+'\e[A   //\\\\//\\\\
+');
+
+autoflush STDERR;
+
+if ($windoof and not @ARGV and not $ENV{PROMPT}) {
+  # restart with cmd.exe to have mouse cut+paste
+  exec qw'cmd /k',$0,'-W';
+  exit;
+}
+
+unless (-d $fexhome) {
+  mkdir $fexhome,0700 or die "$0: cannot create FEXHOME $fexhome - $!\n";
+}
+
+unless (-d $tmpdir) {
+  mkdir $tmpdir,0700 or die "$0: cannot create tmpdir $tmpdir - $!\n";
+}
+
+my @_ARGV = @ARGV; # save arguments
+
+our ($opt_q,$opt_h,$opt_H,$opt_v,$opt_m,$opt_c,$opt_k,$opt_d,$opt_l,$opt_I,
+     $opt_K,$opt_D,$opt_u,$opt_f,$opt_a,$opt_C,$opt_R,$opt_M,$opt_L,$opt_Q,
+     $opt_A,$opt_i,$opt_z,$opt_Z,$opt_b,$opt_P,$opt_x,$opt_X,$opt_V,$opt_U,
+     $opt_s,$opt_o,$opt_g,$opt_F,$opt_n,$opt_r,$opt_S,$opt_N);
+
+if ($xx) {
+  $opt_q = 1 if @ARGV and $ARGV[-1] eq '--' and pop @ARGV or not -t STDOUT;
+  $opt_h = $opt_v = $opt_m = $opt_I = 0;
+  $opt_X = '';
+  $_ = "$fexhome/config.pl"; require if -f;
+  getopts('hvIm:') or die $usage;
+} else {
+  $opt_h = $opt_v = $opt_m = $opt_c = $opt_k = $opt_d = $opt_l = $opt_I = 0;
+  $opt_H = $opt_K = $opt_D = $opt_R = $opt_M = $opt_L = $opt_Q = $opt_A = 0;
+  $opt_x = $opt_o = $opt_g = $opt_V = $opt_U = $opt_F = $opt_n = $opt_q = 0;
+  $opt_S = $opt_N = 0;
+  ${'opt_@'} = ${'opt_!'} = ${'opt_+'} = ${'opt_.'} = ${'opt_/'} = 0;
+  ${'opt_='} = ${'opt_#'} = '';
+  $opt_u = $opt_f = $opt_a = $opt_C = $opt_i = $opt_b = $opt_P = $opt_X = '';
+  $opt_s = $opt_r = '';
+  $_ = "$fexhome/config.pl"; require if -f;
+  getopts('hHvcdognVDKlILUARWMFzZqQS@!+./r:m:k:u:f:a:s:C:i:b:P:x:X:N:=:#:') 
+    or die $usage;
+
+  if ($opt_H) {
+    print $hints;
+    exit;
+  }
+  
+  if ($opt_V) {
+    print "Version: $version\n";
+  }
+  
+  if ($opt_K and $opt_D) {
+    die "$0: you cannot use both options -D and -K\n";
+  }
+
+  if ($opt_a and $opt_c) {
+    die "$0: you cannot use both options -a and -c\n";
+  }
+
+  if ($opt_a and $opt_s) {
+    die "$0: you cannot use both options -a and -s\n";
+  }
+
+  if ($opt_g and $opt_c) {
+    $opt_c = 0;
+  }
+
+  $opt_f ||= $opt_b;
+  if ($opt_f and $opt_f !~ /^\d+$/) {
+    die "$0: option -f needs a number, see $0 -l\n";
+  }
+
+  if ($opt_I and $opt_R) {
+    die "$0: you cannot use both options -I and -R\n";
+  }
+
+  # $opt_C is COMMENT command in F*EX protocol
+  $opt_C =    
+    ($opt_d)           ? 'DELETE':
+    ($opt_l or $opt_L) ? 'LIST':
+    ($opt_Q)           ? 'CHECKQUOTA':
+    ($opt_S)           ? 'LISTSETTINGS':
+    ($opt_Z)           ? 'RECEIVEDLOG':
+    ($opt_z)           ? 'SENDLOG':
+    (${'opt_!'})       ? 'FOPLOG':
+  $opt_C;
+  
+  $opt_D =     
+    ($opt_D) ? 'DELAY':
+    ($opt_K) ? 'NO':
+  $opt_D;
+}
+
+&get_ssl_env;
+
+if ($opt_h) {
+  female_mode("show help?") if $opt_F;
+  print $usage;
+  exit;
+}
+
+
+if ($opt_R) {
+  &register;
+  exit;
+}
+
+
+die $usage if $opt_m and $opt_m !~ /^\d+/;
+
+if ($opt_P) { 
+  if ($opt_P =~ /^([\w.-]+:\d+)(:(\d+))?/) {
+    $proxy = $1;
+    $chunksize = $3 || 0;
+  } else {
+    die "$0: proxy must be: SERVER:PORT\n";
+  }
+}
+
+if ($FEXID = $ENV{FEXID}) {
+  $FEXID = decode_b64($FEXID) if $FEXID !~ /\s/;
+  ($fexcgi,$from,$id) = split(/\s+/,$FEXID);
+} else {
+  if ($windoof and not -f $idf) { &init_id }
+  if (open $idf,$idf) {
+    &get_id($idf);
+    close $idf;
+  }
+}
+
+if ($xx) {
+  # convert old idxx file
+  if ($idf and open $idf,$idf.'xx') {
+    &get_id($idf);
+    close $idf;
+    if (open $idf,'>>',$idf) {
+      print {$idf} "\n[xx]\n",
+                   "$fexcgi\n",
+                   "$from\n",
+                   "$id\n";
+      close $idf;
+      unlink $idf.'xx';
+    }
+  }
+  
+  # special xx ID?
+  if ($FEXXX = $ENV{FEXXX}) {
+    $FEXXX = decode_b64($FEXXX) if $FEXXX !~ /\s/;
+    ($fexcgi,$from,$id) = split(/\s+/,$FEXXX);
+  } elsif (open $idf,$idf) {
+    while (<$idf>) {
+      if (/^\[xx\]/) {
+        $proxy = $proxy_prefix = '';
+        &get_id($idf);
+        last;
+      }
+    }
+    close $idf;
+  }
+  
+} else {
+
+  # alternativ ID?
+  if ($opt_i) {
+    $proxy = $proxy_prefix = '';
+    open $idf,$idf or die "$0: cannot open $idf - $!\n";
+    while (<$idf>) {
+      if (/^\[$opt_i\]/) {
+        &get_id($idf);
+        last;
+      }
+    }
+    close $idf;
+    die "$0: no [$opt_i] in $idf\n" unless $_;
+  }
+}
+
+if ($opt_I) {
+  if ($xx) { &show_id } 
+  else     { &init_id }
+  exit;
+}
+
+if (@ARGV > 1 and $ARGV[-1] =~ /(^|\/)anonymous/) {
+  $fexcgi = $1 if $ARGV[-1] =~ s:(.+)/::;
+  die "usage: $0 [options] file FEXSERVER/anonymous\n" unless $fexcgi;
+  $anonymous = $from = 'anonymous';
+  $sid = $id = 'ANONYMOUS';
+} elsif (@ARGV > 1 and $id eq 'PUBLIC') {
+  $public = $sid = $id;
+} elsif (@ARGV > 1 and $ARGV[-1] =~ m{^(https?://[\w.-]+(:\d+)?/fup\?[sg]key=\w+)}) {
+  $fexcgi = $1;
+  $skey = $1 if $fexcgi =~ /skey=(\w+)/;
+  $gkey = $1 if $fexcgi =~ /gkey=(\w+)/;
+} else {
+
+  $fexcgi = $opt_u if $opt_u;
+  
+  if (not -e $idf and not ($fexcgi and $from and $id)) {
+    die "$0: no ID file $idf found, use \"fexsend -I\" to create it\n";
+  }
+  
+  unless ($fexcgi) {
+    die "$0: no FEX URL found, use \"$0 -u URL\" or \"$0 -I\"\n";
+  }
+  
+  unless ($from and $id) {
+    die "$0: no sender found, use \"$0 -f FROM:ID\" or \"$0 -I\"\n";
+  }
+
+  if ($fexcgi !~ /^http/) {
+    if ($fexcgi =~ /:443/) { $fexcgi = "https://$fexcgi" }
+    else                   { $fexcgi = "http://$fexcgi" }
+  }
+
+}
+
+$server = $fexcgi;
+
+$port = 80;
+$port = 443 if $server =~ s{https://}{};
+$port = $1  if $server =~ s/:(\d+)//;
+
+if (0 and $port == 443) {
+  $opt_s and die "$0: cannot use -s with https due to stunnel bug\n"; 
+  $opt_g and die "$0: cannot use -g with https due to stunnel bug\n"; 
+}
+
+$server =~ s{http://}{};
+$server =~ s{/.*}{};
+
+# $chunksize = 4*k unless $chunksize;
+$chunksize *= M;
+
+if ($proxy) {
+  if    ($port == 80)  { $proxy_prefix = "http://$server" }
+  elsif ($port != 443) { $proxy_prefix = "http://$server:$port" }
+}
+
+# xx: special file exchange between own accounts
+if ($xx) {
+  my $transferfile = "$tmpdir/STDFEX";
+  # slot?
+  if ($0 eq 'xxx') {
+    $transferfile = "$tmpdir/xx:xxx";
+  } elsif (@ARGV and $ARGV[0] =~ /^:([\w.=+-]+)$/) {
+    $transferfile = "$tmpdir/xx:$1";
+    shift @ARGV;
+  }
+  open my $lock,'>>',$transferfile 
+    or die "$0: cannot write $transferfile - $!\n";
+  flock($lock,LOCK_EX|LOCK_NB)
+    or die "$0: $transferfile is locked by another process\n";
+  truncate $transferfile,0;
+  if (not @ARGV and -t) {
+    &get_xx($transferfile);
+  } else {
+    &send_xx($transferfile);
+  }
+  exit;
+} 
+
+# regular fexsend
+
+&inquire if $windoof and not @ARGV and not
+            ($opt_l or $opt_L or $opt_Q or $opt_A or $opt_U or $opt_I or
+             $opt_f or $opt_x or $opt_N);
+
+if (${'opt_.'}) {
+  $opt_C = "!SHORTMAIL! $opt_C";
+}
+
+if ($opt_n or $opt_C =~ /NOMAIL|!#!/) {
+  $nomail = 'NOMAIL';
+}
+
+unless ($skey or $gkey or $anonymous) {
+  if (not $opt_q and (
+    $opt_f||$opt_x||$opt_Q||$opt_l||$opt_L||$opt_U||$opt_z||$opt_Z||$opt_A
+    ||$opt_d||${'opt_!'}||${'opt_@'})
+  ) { warn "Server/User: $fexcgi/$from\n" }
+}
+
+if    ($opt_V and not @ARGV)           { exit }
+if    ($opt_f)                                 { &forward } 
+elsif ($opt_x)                                 { &modify } 
+elsif ($opt_N)                                 { &renotify } 
+elsif ($opt_Q)                                 { &query_quotas } 
+elsif ($opt_S)                                 { &query_settings } 
+elsif ($opt_l or $opt_L)               { &list } 
+elsif ($opt_U)                         { &show_URL } 
+elsif ($opt_z or $opt_Z or ${'opt_!'}) { &get_log } 
+elsif ($opt_A)                         { edit_address_book($from) }
+elsif (${'opt_@'})                     { &show_address_book } 
+elsif ($opt_d and $anonymous)          { &purge }
+elsif ($opt_d and $ARGV[-1] =~ /^\d+$/)        { &delete }
+else                                   { &send_fex }
+
+exit;
+
+
+# initialize ID file or show ID
+sub init_id {
+  my $tag;
+  my $proxy = '';
+  
+  if ($opt_I) {
+    $tag = shift @ARGV;
+    die $usage if @ARGV;
+  }
+  
+  $fexcgi = $from = $id = '';
+  
+  unless (-d $fexhome) {
+    mkdir $fexhome,0700 or die "$0: cannot create FEXHOME $fexhome - $!\n";
+  }
+
+  # show ID
+  if (not $tag and open $idf,$idf) {
+    if ($opt_i) {
+      while (<$idf>) {
+        last if /^\[$opt_i\]/;
+      }
+    }
+    $fexcgi = <$idf>;
+    $from   = <$idf>;
+    $id     = <$idf>;
+    close $idf;
+    if ($id) {
+      chomp($fexcgi,$from,$id);
+      $FEXID = encode_b64("$fexcgi $from $id");
+      if (-t STDIN) {
+        print "# hint: to edit the ID file $idf use \"$0 -I .\" #\n";
+        print "export FEXID=$FEXID\n";
+        print "history -d \$((HISTCMD-1));history -d \$((HISTCMD-1))\n";
+      } else {
+        print "FEXID=$FEXID\n";
+      }
+      exit;
+    } else {
+      die "$0: no ID data found\n";
+    }
+  }
+
+  if ($tag and $tag eq '.') { exec $ENV{EDITOR}||'vi',$idf }
+  
+  if ($tag) { print "F*EX server URL for [$tag]: " }
+  else      { print "F*EX server URL: " }
+  $fexcgi = <STDIN>;
+  $fexcgi =~ s/[\s\n]//g;
+  die "you MUST provide a FEX-URL!\n" unless $fexcgi;
+  if ($fexcgi =~ /\?/) {
+    $from = $1 if $fexcgi =~ /\bfrom=(.+?)(&|$)/i;
+    $id   = $1 if $fexcgi =~ /\bid=(.+?)(&|$)/i;
+    $skey = $1 if $fexcgi =~ /\bskey=(.+?)(&|$)/i;
+    $gkey = $1 if $fexcgi =~ /\bgkey=(.+?)(&|$)/i;
+    $fexcgi =~ s/\?.*//;
+  }
+  unless ($fexcgi =~ /^[_:=\w\-\.\/\@\%]+$/) {
+    die "\"$fexcgi\" is not a legal FEX-URL!\n";
+  }
+  $fexcgi =~ s:/fup/*$::;
+  print "proxy address (hostname:port or empty if none): ";
+  $proxy = <STDIN>;
+  $proxy =~ s/[\s\n]//g;
+  if ($proxy =~ /^[\w.-]+:\d+$/) { 
+    $proxy = "!$proxy";
+  } elsif ($proxy =~ /\S/) { 
+    die "wrong proxy address format\n";
+  } else { 
+    $proxy = "";
+  }
+  if ($proxy) {
+    print "proxy POST limit in MB (use 2048 if unknown): ";
+    $_ = <STDIN>;
+    if (/(\d+)/) {
+      $proxy .= "[$1]";
+    }
+  }
+  if ($skey) {
+    $from = 'SUBUSER';
+    $id = $skey;
+  } elsif ($gkey) {
+    $from = 'GROUPMEMBER';
+    $id = $gkey;
+  } else {
+    unless ($from) {
+      print "Your e-mail address as registered at $fexcgi: ";
+      $from = <STDIN>;
+      $from =~ s/[\s\n]//g;
+      die "you MUST provide your e-mail address!\n" unless $from;
+    }
+    unless ($from =~ /^[_:=\w\-\.\/\@\%\+]+$/) {
+      die "\"$from\" is not a legal e-mail address!\n";
+    }
+    unless ($id) {
+      print "Your auth-ID for $from at $fexcgi: ";
+      $id = <STDIN>;
+      $id =~ s/[\s\n]//g;
+      die "you MUST provide your ID!\n" unless $id;
+    }
+  }
+  if (open $idf,'>>',$idf) {
+    print {$idf} "\n[$tag]\n" if $tag and -s $idf;
+    print {$idf} "$fexcgi$proxy\n",
+                 "$from\n",
+                 "$id\n";
+    close $idf;
+    print "data written to $idf\n";
+  } else {
+    die "$0: cannot write to $idf - $!\n";
+  }
+}
+
+
+sub show_id {
+  my ($fexcgi,$from,$id);
+  if (open $idf,$idf) {
+    $fexcgi = <$idf>;
+    $from   = <$idf>;
+    $id     = <$idf>;
+    while (<$idf>) {
+      if (/^\[xx\]/) {
+        $fexcgi = <$idf>;
+        $from   = <$idf>;
+        $id     = <$idf>;
+      }
+    }
+    close $idf;
+    die "$0: too few data in $idf" unless defined $id;
+    chomp($fexcgi);
+    chomp($from);
+    chomp($id);
+    $FEXXX = encode_b64("$fexcgi $from $id");
+    if (-t STDIN) {
+      print "export FEXXX=$FEXXX\n";
+      print "history -d \$((HISTCMD-1));history -d \$((HISTCMD-1))\n";
+    } else {
+      print "FEXXX=$FEXXX\n";
+    }
+  } else {
+    die "$0: cannot read $idf - $!\n";
+  }
+}
+
+
+sub register {
+  my $fs = shift @ARGV or die $usage;
+  my $mail = shift @ARGV or die $usage;
+  my $port;
+  my ($server,$user,$id);
+
+  die "$0: $idf does already exist\n" if -e $idf;
+
+  if ($fs =~ /^https/) {
+    die "$0: cannot handle https at this time\n";
+  }
+
+  $fs =~ s{^http://}{};
+  $fs =~ s{/.*}{};
+  if ($fs =~ s/:(\d+)//) { $port = $1 }
+  else                   { $port = 80 }
+
+  tcpconnect($fs,$port);
+  sendheader("$fs:$port","GET $proxy_prefix/fur?user=$mail&verify=no HTTP/1.1");
+  http_response();
+
+  while (<$SH>) {
+    s/\r//;
+    printf "<-- $_"if $opt_v;
+    last if /^\s*$/;
+  }
+
+  while (<$SH>) {
+    s/\r//;
+    printf "<-- $_"if $opt_v;
+    if (m{http://(.*)/fup\?from=(.+)&ID=(.+)}) {
+      $server = $1;
+      $user = $2;
+      $id = $3;
+
+      if (open F,">$idf") {
+        print F "$server\n",
+                "$user\n",
+                "$id\n";
+        close F;
+        chmod 0600,$idf;
+        print "user data written to $idf\n";
+        print "you can now fex!\n";
+        exit;
+      } else {
+        die "$0: cannot write to $idf - $!\n";
+      }
+    }
+  }
+
+  die "$0: no account data received from F*EX server\n";
+
+}
+
+
+sub send_xx {
+  my $transferfile = shift;
+  my $file = '';
+  my (@r,@tar);
+  
+  $SIG{PIPE} = $SIG{INT} = sub {
+    unlink $transferfile;
+    exit 3;
+  };
+  
+  if ($0 eq 'xxx') { @tar = qw'tar -cv' }
+  else             { @tar = qw'tar -cvz' }
+
+  if (-t) {
+    if ("@ARGV" eq '-') {
+      # store STDIN to transfer file
+      shelldo("cat >> $transferfile");
+    } elsif (@ARGV) {
+      print "making tar transfer file $transferfile :\n";
+      # single file? then add this directly 
+      if (scalar @ARGV == 1) {
+        my ($dir,$file);
+        # strip path if not ending with /
+        if ($ARGV[0] =~ m:(.+)/(.+): and $2 !~ m:/$:) {
+          ($dir,$file) = ($1,$2);
+          chdir $dir or die "$0: $dir - $!\n";
+        } else {
+          $file = $ARGV[0];
+        }
+        if (-l $file) {
+          shelldo(@tar,qw'--dereference -f',$transferfile,$file);
+        } else {
+          shelldo(@tar,'-f',$transferfile,$file);
+        }
+      } else {
+        shelldo(@tar,'-f',$transferfile,@ARGV);
+      }
+      if ($?) {
+        unlink $transferfile;
+        if ($? == 2) {
+          die "$0: interrupted making tar transfer file\n";
+        } else {
+          die "$0: error while making tar transfer file\n";
+        }
+      }
+    }
+  } else {
+    # write input from pipe to transfer file
+    shelldo("cat >> $transferfile");
+  }
+
+  die "$0: no transfer file\n" unless -s $transferfile;
+  
+  serverconnect($server,$port);
+  query_sid($server,$port);
+  
+  @r = formdatapost(
+    from       => $from,
+    to         => $from,
+    id         => $sid,
+    file       => $transferfile,
+    comment    => 'NOMAIL',
+    autodelete => $transferfile =~ /STDFEX/ ? 'NO' : 'DELAY',
+  );
+  
+  # open P,'|w3m -T text/html -dump' or die "$0: w3m - $!\n";
+  # print P @r;
+  http_response(@r);
+  if ($transferfile =~ /:/ and $0 ne 'xxx') {
+    if ("@r" =~ /\s(X-)?Location: (http.*)\s/) {
+      print "wget -O- $2 | tar xvzf -\n";
+    }
+  }
+  
+  unlink $transferfile;
+}
+
+
+sub query_quotas {
+  my (@r,$r);
+  local $_;
+
+  female_mode("query quotas?") if $opt_F;
+
+  @r = formdatapost(
+    from       => $from,
+    to         => $from,
+    id         => $sid,
+    command    => $opt_C, 
+  );
+  die "$0: no response from fex server $server\n" unless @r;
+  $_ = shift @r;
+  unless (/^HTTP.* 2/) {
+    s:HTTP/[\d\. ]+::;
+    die "$0: server response: $_\n";
+  }
+  if (($_) = grep(/^X-Sender-Quota/,@r) and /(\d+)\s+(\d+)/) {
+    print "sender quota (used): $1 ($2) MB\n";
+  } else {
+    print "sender quota: unlimited\n";
+  }
+  if (($_) = grep(/^X-Recipient-Quota/,@r) and /(\d+)\s+(\d+)/) {
+    print "recipient quota (used): $1 ($2) MB\n";
+  } else {
+    print "recipient quota: unlimited\n";
+  }
+}
+
+
+sub query_settings {
+  my (@r,$r);
+  local $_;
+
+  female_mode("query settings?") if $opt_F;
+
+  if ($FEXID) {
+    print "ID data from \$FEXID\n";
+  } elsif (-f $idf) {
+    print "ID data from $idf\n";
+  } else {
+    die "$0: found no ID\n";
+  }
+  print "server: $fexcgi\n";
+  print "user: $from\n";
+  print "auth-ID: $id\n";
+  print "login URL: ";
+  &show_URL;
+  
+  @r = formdatapost(
+    from       => $from,
+    to         => $from,
+    id         => $sid,
+    command    => $opt_C, 
+  );
+  die "$0: no response from fex server $server\n" unless @r;
+  $_ = shift @r;
+  unless (/^HTTP.* 2/) {
+    s:HTTP/[\d\. ]+::;
+    die "$0: server response: $_\n";
+  }
+  if (($_) = grep(/^X-Autodelete/,@r) and /:\s+(\w+)/) {
+    print "autodelete: $1\n";
+  }
+  if (($_) = grep(/^X-Default-Keep/,@r) and /(\d+)/) {
+    print "default keep: $1 days\n";
+  }
+  if (($_) = grep(/^X-Default-Locale/,@r) and /:\s+(\w+)/) {
+    print "default locale: $1\n";
+  }
+  if (($_) = grep(/^X-MIME/,@r) and /:\s+(\w+)/) {
+    print "display file with browser: $1\n";
+  }
+  if (($_) = grep(/^X-Sender-Quota/,@r) and /(\d+)\s+(\d+)/) {
+    print "sender quota (used): $1 ($2) MB\n";
+  } else {
+    print "sender quota: unlimited\n";
+  }
+  if (($_) = grep(/^X-Recipient-Quota/,@r) and /(\d+)\s+(\d+)/) {
+    print "recipient quota (used): $1 ($2) MB\n";
+  } else {
+    print "recipient quota: unlimited\n";
+  }
+}
+
+
+# list spool
+sub list {
+  my (@r,$r);
+  my ($data,$dkey,$n);
+  local $_;
+
+  female_mode("list spooled files?") if $opt_F;
+
+  if ($opt_l and $n = shift @ARGV and $n =~ /^\d+$/) {
+    open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
+    while (<$fexlist>) {
+      if (/^\s*(\d+)\) (\w+) (.+)/ and $1 eq $n) {
+        serverconnect($server,$port) unless $SH;
+        sendheader(
+          "$server:$port",
+          "GET $proxy_prefix/fop/$2/$2?LIST HTTP/1.1",
+          "User-Agent: $useragent",
+        );
+        $_ = <$SH>||'';
+        s/\r//;
+        print "<-- $_" if $opt_v;
+        if (/^HTTP.* 200/) {
+          print "<-- $_" if $opt_v;
+          while (<$SH>) {
+            s/\r//;
+            if (/^\n/) {
+              print;
+              print while <$SH>;
+            }
+          }
+        } elsif (s:HTTP/[\d\. ]+::) {
+          die "$0: server response: $_";
+        } else {
+          die "$0: no response from fex server $server\n";
+        }
+        exit;
+      }
+    }
+    die "$0: file \#$n not found in fexlist\n";
+  } else {
+    @r = formdatapost(
+      from     => $from,
+      to       => $opt_l ? '*' : $from,
+      command  => $opt_C, 
+    );
+  }
+  die "$0: no response from fex server $server\n" unless @r;
+  $_ = shift @r;
+  unless (/^HTTP.* 200/) {
+    s:HTTP/[\d\. ]+::;
+    die "$0: server response: $_\n";
+  }
+  
+  # list sent files
+  if ($opt_l) {
+    open $fexlist,">$fexlist" or die "$0: cannot write $fexlist - $!\n";
+    foreach (@r) {
+      next unless /<pre>/ or $data;
+      $data = 1;
+      last if m:</pre>:;
+      if (/<a href=".*dkey=(\w+).*?">/) { $dkey = $1 }
+      else                              { $dkey = '' }
+#      $_ = encode_utf8($_);
+      s/<.*?>//g;
+      if (/^(to .* :)/) {
+        print "\n$1\n";
+        print {$fexlist} "\n$1\n";
+      } elsif (m/(\d+) MB (.+)/) {
+        $n++;
+        printf "%4s) %8d MB %s\n","#$n",$1,$2;
+        printf {$fexlist} "%3d) %s %s\n",$n,$dkey,$2;
+      }
+    }
+    close $fexlist;
+  } 
+  
+  # list received files
+  if ($opt_L) {
+    foreach (@r) {
+      next unless /<pre>/ or $data;
+      $data = 1;
+      next if m:<pre>:;
+      last if m:</pre>:;
+      if (/(from .* :)/) {
+        print "\n$1\n";
+      }
+      if (m{(\d+) (MB.*)<a href="(https?://.*/fop/\w+/.+)">(.+)</a>( ".*")?}) {
+        printf "%8d %s%s%s\n",$1,$2,$3,($5||'');
+      }
+    }
+  }
+}
+
+
+sub show_URL {
+  printf "%s/fup/%s\n",$fexcgi,encode_b64("from=$from&id=$id");
+}
+
+
+sub get_log {
+  my (@r);
+  local $_;
+  
+  @r = formdatapost(
+    from       => $from,
+    to         => $from,
+    id         => $sid,
+    command    => $opt_C, 
+  );
+  die "$0: no response from fex server $server\n" unless @r;
+  $_ = shift @r;
+  unless (/^HTTP.* 200/) {
+    s:HTTP/[\d\. ]+::;
+    die "$0: server response: $_\n";
+  }
+  while (shift @r) {}
+  foreach (@r) { print "$_\n" }
+}
+
+
+sub show_address_book {
+  my (%AB,@r);
+  my $alias;
+  local $_;
+  
+  %AB = query_address_book($server,$port,$from);
+  foreach $alias (sort keys %AB) {
+    next if $alias eq 'ADDRESS_BOOK';
+    $_ = sprintf "%s = %s (%s) # %s\n",
+                 $alias,
+                 $AB{$alias},
+                 $AB{$alias}->{options},
+                 $AB{$alias}->{comment};
+    s/ \(\)//;
+    s/ \# $//;
+    print;
+  }
+}
+
+
+sub purge {
+  die "$0: not yet implemented\n";
+}
+
+
+sub delete {
+  my ($to,$file);
+
+  while (@ARGV) {
+    $opt_d = shift @ARGV;
+    die "$usage: $0 -d #\n" if $opt_d !~ /^\d+$/;
+  
+    open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
+    while (<$fexlist>) {
+      if (/^to (.+\@.+) :/) {
+        $to = $1;
+      } elsif (/^\s*(\d+)\) (\w+) (.+)/ and $1 eq $opt_d) {
+        serverconnect($server,$port) unless $SH;
+        sendheader(
+          "$server:$port",
+          "GET $proxy_prefix/fop/$2/$2?DELETE HTTP/1.1",
+          "User-Agent: $useragent",
+        );
+        $_ = <$SH>||'';
+        s/\r//;
+        print "<-- $_" if $opt_v;
+        if (/^HTTP.* 200/) {
+          while (<$SH>) {
+            s/\r//;
+            last if /^\n/; # ignore HTML output
+            print "<-- $_" if $opt_v;
+            if (/^X-File:.*\/(.+)/) {
+              printf "%s deleted\n",decode_utf8(urldecode($1));
+            }
+          }
+          undef $SH;
+        } elsif (s:HTTP/[\d\. ]+::) {
+          die "$0: server response: $_";
+        } else {
+          die "$0: no response from fex server $server\n";
+        }
+        last;
+      }
+    }
+    close $fexlist;
+    sleep 1; # do not overrun server
+  }
+
+  exit;
+}
+
+
+sub send_fex {
+  my @to;
+  my $file = '';
+  my @files = ();
+  my ($data,$aname,$alias);
+  my (@r,$r);
+  my $ma = $HOME.'/.mutt/aliases';
+  my $t0 = time;
+  my $transferfile;
+  my @transferfiles;
+  local $_;
+  
+  if ($from =~ /^SUBUSER|GROUPMEMBER$/) {
+    $to = '_';
+  } else {
+    # look for single @ in arguments
+    for (my $i=1; $i<$#ARGV; $i++) {
+      if ($ARGV[$i] eq '@') {
+        $ARGV[$i] = join(',',@ARGV[$i+1 .. $#ARGV]);
+        $#ARGV = $i;
+        last;
+      }
+    }
+    $to = pop @ARGV or die $usage;
+    if ($to eq '.') {
+      $to = $from;
+      $nomail = $opt_C ||= 'NOMAIL';
+    }
+    if ($to eq ':') {
+      $to = $from;
+      $nomail = $opt_C ||= 'NOMAIL';
+    }
+    if ($opt_g and $to =~ /,/) {
+      die "$0: encryption is supported to only one recipient\n";
+    }
+    if ($to =~ m{^https?://.*/fup\?skey=(\w+)}) {
+      $from = 'SUBUSER';
+      $to = '_';
+      $id = $1;
+    }
+    if ($to =~ m{^https?://.*/fup\?gkey=(\w+)}) {
+      $from = 'GROUPMEMBER';
+      $to = '_';
+      $id = $1;
+    }
+  }
+  @to = split(',',lc($to));
+  
+  die $usage unless @ARGV or $opt_a or $opt_s;
+  die $usage if $opt_s and @ARGV;
+
+  # early serverconnect necessary for X-Features info
+  serverconnect($server,$port);
+
+  if ($anonymous) {
+    my $aok;
+    sendheader("$server:$port","OPTIONS FEX HTTP/1.1");
+    $_ = <$SH>||'';
+    s/\r//;
+    die "$0: no response from fex server $server\n" unless $_;
+    print "<-- $_" if $opt_v;
+    if (/^HTTP.* 201/) {
+      while (<$SH>) {
+        s/\r//;
+        print "<-- $_" if $opt_v;
+        last unless /\w/;
+        $aok = $_ if /X-Features:.*ANONYMOUS/;
+      }
+      die "$0: no anonymous support on server $server\n" unless $aok;
+    } else {
+      die "$0: bad response from server $server : $_\n";
+    }
+  } elsif ($public) {
+  } else {
+    
+    query_sid($server,$port);
+    
+    if ($from eq 'SUBUSER') {
+      $skey = $sid;
+      # die "skey=$skey\nid=$id\nsid=$sid\n";
+    }
+
+    if ($from eq 'GROUPMEMBER') {
+      $gkey = $sid;
+    }
+    
+    if ($to eq '.') {
+      @to = ($from);
+      $opt_C ||= 'NOMAIL';
+    } elsif ($to =~ m:^(//.*):) {
+      my $xkey = $1;
+      if ($features =~ /XKEY/) {
+        @to = ($from);
+        $opt_C = $xkey;
+      } else {
+        die "$0: server does not support XKEY\n";
+      }
+    } elsif (grep /^[^@]*$/,@to and not $skey and not $gkey) {
+      %AB = query_address_book($server,$port,$from);
+      if ($proxy) {
+        serverconnect($server,$port);
+        query_sid($server,$port);
+      }
+      foreach $to (@to) {
+        # alias in local config?
+        if ($alias{$to}) {
+          if ($alias{$to} =~ /(.+?):(.+)/) {
+            my $ato = $1;
+            my $opt = $2;
+            my @argv = @_ARGV;
+            pop @argv;
+            # special extra upload
+            system $0,split(/\s/,$opt),@argv,$ato;
+            $to = '';
+          } else {
+            $to = $alias{$to};
+          }
+        }
+        # alias in server address book?
+        elsif ($AB{$to}) {  
+          # do not substitute alias with expanded addresses because then 
+          # keep and autodelete options from address book will get lost
+          # $to = $AB{$to};
+        } 
+        # look for mutt aliases
+        elsif ($to !~ /@/ and $to ne $from and open $ma,$ma) {
+          $alias = $to;
+          while (<$ma>) {
+            if (/^alias \Q$to\E\s/i) {
+              chomp;
+              s/\s*#.*//;
+              s/\(.*?\)//;
+              s/\s+$//;
+              s/.*\s+//;
+              s/[<>]//g;
+              if (/,/) {
+                warn "$0: ignoring mutt multi-alias $to = $alias\n";
+                last;
+              }
+              if (/@/) {
+                $alias = $_;
+                warn "$0: found mutt alias $to = $alias\n";
+                last;
+              }
+            }
+          }
+          close $ma;
+          $to = $alias;
+        }
+      }
+    }
+  
+    $to = join(',',grep /./,@to) or exit;
+    warn "Server/User: $fexcgi/$from\n" unless $opt_q;
+  
+    if (
+      not $skey and not $gkey
+      and $features =~ /CHECKRECIPIENT/ 
+      and $opt_C !~ /^(DELETE|LIST|RECEIVEDLOG|SENDLOG|FOPLOG)$/
+    ) {
+      checkrecipient($from,$to);
+      if ($proxy) {
+        serverconnect($server,$port);
+        query_sid($server,$port);
+      }
+    }
+  }
+
+  if (@ARGV > 1 and not ($opt_a or $opt_s or $opt_d)) {
+    print "Archive name (name.tar, name.tgz or name.zip) or [ENTER] to send file for file:\n";
+    $opt_a = <STDIN>;
+    $opt_a =~ s/^\s+//;
+    $opt_a =~ s/\s+$//;
+  }
+
+  if ($opt_s) {
+    $opt_s =~ s/^=//;
+    $opt_s =~ s:.*/::;
+    $opt_s =~ s/[^\w_.+-]/_/g;
+    @files = ($opt_s);
+  } elsif ($opt_a) {
+    $opt_a =~ s/^=//;
+    $opt_a =~ s:.*/::;
+    $opt_a =~ s/[^\w_.+-]/_/g;
+    if ($opt_a =~ /(.+)\.(zip|tar|tgz|7z)$/) {
+      $aname = $1;
+      $atype = $2;
+    } else {
+      die "$0: archive name must be one of ".
+          "$opt_a.tar $opt_a.tgz $opt_a.zip\n";
+    }
+    # no file argument left?
+    unless (@ARGV) {
+      # use file name as archive name
+      push @ARGV,$aname;
+      $opt_a =~ s:/+$::g;
+      $opt_a =~ s:.*/::g;
+    }
+    foreach my $file (@ARGV) {
+      die "$0: cannot read $file\n" unless -l $file or -r $file;
+    }
+    $opt_a .= ".$atype" if $opt_a !~ /\.$atype$/;
+    $transferfile = "$tmpdir/$opt_a";
+    unlink $transferfile;
+    print "Making fex archive ($opt_a):\n";
+    if ($atype eq 'zip') {
+      if ($windoof) {
+        # if ($opt_c) { system(qw'7z a -tzip',$transferfile,@ARGV) }
+        # else        { system(qw'7z a -tzip -mm=copy',$transferfile,@ARGV) }
+        system(qw'7z a -tzip',$transferfile,@ARGV);
+        @files = ($transferfile);
+      } else {
+        # zip archives must be < 2 GB, so split as necessary
+        @files = zipsplit($transferfile,@ARGV);
+        if (scalar(@files) == 1) {
+          $transferfile = $files[0];
+          $transferfile =~ s/_1.zip$/.zip/;
+          rename $files[0],$transferfile;
+          @files = ($transferfile);
+        }
+      }
+      @transferfiles =  @files;
+    } elsif ($atype eq '7z') {
+      # http://www.7-zip.org/
+      my @X = (); # exclude list
+      if (${'opt_#'}) {
+        foreach my $x (split('#',${'opt_#'})) {
+          push @X,"-x!$x";
+        }
+      }
+      if ($opt_c) { system(qw'7z a',@X,$transferfile,@ARGV) }
+      else        { system(qw'7z a -t7z -mx0',@X,$transferfile,@ARGV) }
+      @transferfiles = @files = ($transferfile);
+    } elsif ($atype eq 'tar') {
+      if ($windoof) {
+        system(qw'7z a -ttar',$transferfile,@ARGV);
+        @transferfiles = @files = ($transferfile);
+      } else {
+        ## tar is now handled by formdatapost()
+        # system(qw'tar cvf',$transferfile,@ARGV);
+        @files = ($opt_a);
+      }
+    } elsif ($atype eq 'tgz') {
+      if ($windoof) {
+        die "$0: archive type tgz not available, use tar, zip or 7z\n";
+      } else {
+        ## tgz is now handled by formdatapost()
+        # system(qw'tar cvzf',$transferfile,@ARGV);
+        @files = ($opt_a);
+      }
+    } else {
+      die "$0: unknown archive format \"$atype\"\n";
+    }
+    
+    if (@transferfiles) {
+      
+      # error in making transfer archive?
+      if ($?) {
+        unlink @transferfiles;
+        die "$0: $! - aborting upload\n";
+      }
+      
+      # maybe timeout, so make new connect
+      if (time-$t0 >= $timeout) {
+        serverconnect($server,$port);
+        query_sid($server,$port) unless $anonymous;
+      }
+      
+    }
+    
+  } else {
+    
+    unless (@ARGV) {
+      if ($windoof) {
+        &inquire;
+      } else {
+        die $usage;
+      }
+    }
+    
+    foreach (@ARGV) {
+      my $file = $_;
+      unless ($opt_d) {
+        unless (-f $file) {
+          if (-e $file) {
+            die "$0: $file is not a regular file, try option -a\n"
+          } else {
+            die "$0: $file does not exist\n";
+          }
+        }
+        die "$0: cannot read $file\n" unless -r $file;
+      }
+      push @files,$file;
+    }
+  }
+
+  if (${'opt_/'}) {
+    foreach my $file (@files) {
+      my @s = stat($file);
+      unless (@s and ($s[2] & S_IROTH) and -r $file) {
+        die "$0: $file is not world readable\n";
+      }
+    }
+  }
+  
+  foreach my $file (@files) {
+    sleep 1;    # do not overrun server!
+    unless (-s $file or $opt_d or $opt_a or $opt_s) {
+      die "$0: cannot send empty file $file\n";
+    }
+    female_mode("send file $file?") if $opt_F;
+    @r = formdatapost(
+      from             => $from,
+      to               => $to,
+      replyto          => $opt_r,
+      id               => $sid,
+      file             => $file,
+      keep             => $opt_k,
+      comment          => $opt_C,
+      autodelete       => $opt_D, 
+    );
+
+    if (not @r or not grep /\w/,@r) {
+      die "$0: no response from server\n";
+    }
+    if (($r) = grep /^ERROR:/,@r) {
+      if ($anonymous and $r =~ /purge it/) {
+        die "$0: file is already on server for $to - use another anonymous recipent\n";
+      } else {
+        $r =~ s/.*?:\s*//;
+        $r =~ s/<.+?>//g;
+        die "$0: server error: $r\n";
+      }
+    }
+    if (($r) = grep /<h3>\Q$file/,@r) {
+      $r =~ s/<.+?>//g;
+      print "$r\n";
+    }
+    if ($opt_a !~ /^afex_\d+\.tar$/ and $file !~ /afex_\d+\.tar$/) {
+      # print grep({s/^(X-Recipient:.*\((.+)\))/Parameters: $2\n/i} @r);
+      my $nonot = 0;
+      my ($recipient,$location);
+      foreach (@r) {
+        if (/^(X-)?(Recipient.*)/i) {
+          $recipient = $2;
+          if (/notification=no/i) { $nonot = 1 }
+          else                    { $nonot = 0 }
+        }
+        if (/^(X-)?(Location.*)/i) {
+          $location = $2;
+          if ($from eq $to or $from =~ /^\Q$to\E@/i 
+              or $nomail or $anonymous or $nonot) {
+            print "$recipient\n";
+            print "$location\n";
+          }
+        }
+      }
+    }
+  }
+  
+  # delete transfer tmp file
+  unlink $transferfile if $transferfile;
+}
+
+
+sub forward {
+  my (@r);
+  my ($to,$n,$dkey,$file,$req);
+  my $status = 1;
+  local $_;
+  
+  # look for single @ in arguments
+  for (my $i=1; $i<$#ARGV; $i++) {
+    if ($ARGV[$i] eq '@') {
+      $ARGV[$i] = join(',',@ARGV[$i+1 .. $#ARGV]);
+      $#ARGV = $i;
+      last;
+    }
+  }
+
+  # if ($windoof and not @ARGV) { &inquire }
+  $to = pop @ARGV or die $usage;
+  $to = $from if $to eq '.';
+
+  open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
+  while (<$fexlist>) {
+    if (/^\s*(\d+)\) (\w+) \[\d+ d\] (.+)/ and $1 eq $opt_f) {
+      $n = $1;
+      $dkey = $2;
+      $file = $3;
+      if ($file =~ s/ "(.*)"$//) {
+        $opt_C ||= $1 if $1 ne 'NOMAIL';
+      }
+      last;
+    }
+  }
+  close $fexlist;
+  
+  unless ($n) {
+    die "$0: file #$opt_f not found in fexlist\n";
+  }
+
+  female_mode("forward file #$opt_f?") if $opt_F;
+
+  serverconnect($server,$port);
+  query_sid($server,$port);
+  
+  $req = "GET $proxy_prefix/fup?"
+        ."from=$from&ID=$sid&to=$to&dkey=$dkey&command=FORWARD";
+  $req .= "&comment=$opt_C"    if $opt_C;
+  $req .= "&keep=$opt_k"       if $opt_k;
+  $req .= "&autodelete=$opt_D" if $opt_D;
+  $req .= "&$opt_X"            if $opt_X;
+  $req .= " HTTP/1.1";
+  sendheader("$server:$port",$req);
+  http_response();
+  while (<$SH>) { 
+    if ($opt_v) {
+      print;
+      $status = 0 if /\Q"$file"/;
+    } else {
+      if (/\Q"$file"/) {
+        print;
+        $status = 0;
+      }
+    }
+  }
+  
+  if ($status) {
+    die "$0: server failed, rerun command with option -v\n";
+  }
+  exit;
+}
+
+
+sub renotify {
+  my (@r);
+  my ($to,$n,$dkey,$file,$req,$recipient);
+  local $_;
+
+  die $usage if @ARGV;
+
+  open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
+  while (<$fexlist>) {
+    if (/^\s*(\d+)\) (\w+) \[\d+ d\] (.+)/ and $1 eq $opt_N) {
+      $n = $1;
+      $dkey = $2;
+      last;
+    }
+  }
+  close $fexlist;
+  
+  unless ($n) {
+    die "$0: file #$opt_N not found in fexlist\n";
+  }
+
+  female_mode("resend notification for file #$opt_N?") if $opt_F;
+
+  serverconnect($server,$port);
+  query_sid($server,$port);
+  
+  $req = "GET $proxy_prefix/fup?"
+        ."from=$from&ID=$sid&dkey=$dkey&command=RENOTIFY"
+        ." HTTP/1.1";
+  sendheader("$server:$port",$req);
+  http_response();
+  while (<$SH>) {
+    s/\r//;
+    print "<-- $_" if $opt_v;
+    last if /^\s*$/;
+    if (/^X-Notify: (.+)\/(.+)\/(.+)/) {
+      $recipient = $1;
+      $file = $3;
+    }
+  }
+  
+  if ($file) {
+    print "notification e-mail for $file has been resent to $recipient\n";
+  } else {
+    if ($opt_v) {
+      die "$0: server failed\n";
+    } else {
+      die "$0: server failed, rerun command with option -v\n";
+    }
+  }
+  
+  exit;
+}
+
+
+sub modify {
+  my (@r);
+  my ($n,$dkey,$file,$req);
+  local $_;
+  
+  die $usage if @ARGV;
+  die $usage unless $opt_C or $opt_k or $opt_D;
+  
+  open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
+  while (<$fexlist>) {
+    if (/^\s*(\d+)\) (\w+) \[\d+ d\] (.+)/ and $1 eq $opt_x) {
+      $n = $1;
+      $dkey = $2;
+      $file = $3;
+      $file =~ s/ "(.*)"$//;
+      last;
+    }
+  }
+  close $fexlist;
+  
+  unless ($n) {
+    die "$0: file #$opt_x not found in fexlist\n";
+  }
+
+  female_mode("modify file #$opt_x?") if $opt_F;
+  
+  serverconnect($server,$port);
+  query_sid($server,$port);
+  
+  $req = "GET $proxy_prefix/fup?"
+        ."from=$from&ID=$sid&dkey=$dkey&command=MODIFY";
+  $req .= "&comment=$opt_C"    if $opt_C;
+  $req .= "&keep=$opt_k"       if $opt_k;
+  $req .= "&autodelete=$opt_D" if $opt_D;
+  $req .= " HTTP/1.1";
+  sendheader("$server:$port",$req);
+  http_response();
+  while (<$SH>) { 
+    if ($opt_v) {
+      print "<-- $_";
+    } else {
+      print if /\Q$file/;
+    }
+  }
+  
+  exit;
+}
+
+
+sub get_xx {
+  my $transferfile = shift;
+  my $ft = '';
+  local $_;
+  
+  # get transfer file from FEX server
+  unless ($SH) {
+    serverconnect($server,$port);
+    query_sid($server,$port);
+  }
+  
+  xxget($from,$sid,$transferfile);
+  
+  # empty file?
+  unless (-s $transferfile) {
+    unlink $transferfile;
+    exit;
+  }
+  
+  # no further processing if delivering to pipe
+  exec 'cat',$transferfile unless -t STDOUT;
+  
+  if ($ft = `file $transferfile 2>/dev/null`) {
+    if ($ft =~ /compressed/) {
+      rename $transferfile,"$transferfile.gz";
+      shelldo(ws("gunzip $transferfile.gz"));
+    }
+    $ft = `file $transferfile`;
+  } 
+  # file command failed, so we look ourself into the file...
+  elsif (open $transferfile,$transferfile) {
+    read $transferfile,$_,4;
+    close $transferfile;
+    # gzip magic?
+    if (/\x1F\x8B\x08\x00/) {
+      rename $transferfile,"$transferfile.gz";
+      shelldo(ws("gunzip $transferfile.gz"));
+      # assuming tar
+      $ft = 'tar archive';
+    }
+  }
+  if ($ft =~ /tar archive/) {
+    rename $transferfile,"$transferfile.tar";
+    $transferfile .= '.tar';
+    if ($opt_q) {
+      $_ = 'y';
+    } else {
+      print "Files in transfer-container:\n\n";
+      shelldo(ws("tar tvf $transferfile"));
+      print "\nExtract these files? [Yn] ";
+      $_ = <STDIN>;
+    }
+    if (/^n/i) {
+      print "keeping $transferfile\n";
+    } else {
+      system("tar xvf $transferfile && rm $transferfile");
+      die "$0: error while untaring, see $transferfile\n" if -f $transferfile;
+    }
+  } else {
+    exec 'cat',$transferfile;
+  }
+  exit;
+}
+
+
+sub formdatapost {
+  my %P = @_; 
+  my ($boundary,$filename,$filesize,$length,$buf,$file,$fpsize,$resume,$seek);
+  my ($flink);
+  my (@hh,@hb,@r,@pv,$to);
+  my ($bytes,$t,$bt);
+  my ($t0,$t1,$t2,$tt,$tc);
+  my $bs = 2**16;        # blocksize for reading and sending file
+  my $fileid = int(time);
+  my $chunk = 0;
+  my $connection = '';
+  my $pct = '';
+  my ($tar,$aname,$atype,$tarlist,$tarerror,$location,$transferfile);
+  local $_;
+
+  if (defined($file = $P{file})) {
+    
+    $to = $AB{$P{to}} || $P{to}; # for gpg
+    
+    # special file: stream from STDIN
+    if ($opt_s) {
+      $filename = encode_utf8($file);
+      $filesize = -1;
+    }
+    
+    # compression?
+    if ($opt_c) {
+      my ($if,$of);
+      $if = $file;
+      $if =~ s/([^_\w\.\-])/\\$1/g;
+      $transferfile = $tmpdir . '/' . basename($file) . '.gz';
+      $of = $transferfile;
+      $of =~ s/([^_\w\.\-])/\\$1/g;
+      shelldo("gzip <$if>$of");
+      $filesize = -s $transferfile;
+      die "$0: cannot gzip $file\n" unless $filesize;
+      $file = $transferfile;
+    } 
+    
+    # special file: tar-on-the-fly
+    if (not $windoof and $opt_a and $file =~ /(.+)\.(tar|tgz)$/) {
+      $aname = $1;
+      $atype = $2;
+      $tarlist  = "$tmpdir/$aname.list";
+      $tarerror = "$tmpdir/$aname.error";
+      $tar = 'tar -cv';
+      $tar .= 'z' if $atype eq 'tgz';
+      if (`tar --help 2>/dev/null` =~ /--index-file/) {
+        $tar .= " --index-file=$tarlist -f-";
+      } else {
+        $tar .= " -f-";
+      }
+      if (${'opt_#'}) {
+        foreach my $x (split('#',${'opt_#'})) {
+          $tar .= " --exclude=$x";
+        }
+      }
+      foreach (@ARGV) {
+        $file = $_;
+        $file =~ s/([^\w\-\@\#%,.=+~_:])/\\$1/g;
+        $tar .= ' '.$file;
+      }
+      # print "calculating archive size... ";
+      open $tar,"$tar 2>$tarerror|" or die "$0: cannot run tar - $!\n";
+      $t0 = int(time) if -t STDOUT;
+      while ($b = read $tar,$_,$bs) {
+        $filesize += $b;
+        if ($t0) {
+          $t1 = int(time);
+          if ($t1>$t0) {
+            printf "Archive size: %d MB\r",int($filesize/M);
+            $t0 = $t1;
+          }
+        }
+      }
+      printf "Archive size: %d MB\n",int($filesize/M) if -t STDOUT;
+      unless (close $tar) {
+        $_ = '';
+        if (open $tarerror,$tarerror) {
+          local $/;
+          $_ = <$tarerror>;
+          close $tarerror;
+        }
+        unlink $tarlist,$tarerror;
+        die "$0: tar error:\n$_";
+      }
+      $file = "$aname.$atype";
+      $filename = encode_utf8($file);
+      undef $SH; # force reconnect (timeout!)
+    } 
+    
+    # single file
+    else {
+      $filename = encode_utf8(${'opt_='} || $file);
+    
+      if ($windoof) {
+        $filename =~ s/^[a-z]://;
+        $filename =~ s/.*\\//;
+      }
+      $filename =~ s:.*/::;
+      $filename =~ s:[\r\n]+: :g;
+      if ($opt_d) {
+        $filesize = 0;
+      } elsif (not $opt_g and not $opt_s) {
+        $filesize = -s $file or die "$0: $file is empty or not readable\n";
+      }
+    }
+
+    $filename .= '.gpg' if $opt_g;
+
+    unless ($opt_d) {
+      if ($opt_g) {
+        $filesize = -1;
+        $fileid = int(time);
+      } else {
+        if ($opt_a) {
+          $fileid = md5_hex(fmd(@ARGV));
+        } else {
+          $fileid = fileid($file);
+        }
+      }
+    }
+  
+  } else {
+    $file = $filename = '';
+    $filesize = 0;
+  }
+
+  FORMDATAPOST:
+    
+  @hh = (); # HTTP header
+  @hb = (); # HTTP body
+  @r = ();
+  $seek = 0;
+  $resume = '';
+  $chunk++;
+
+  unless ($SH) {
+    serverconnect($server,$port);
+    query_sid($server,$port) unless $anonymous;
+  }
+  
+  $P{id} = $sid; # ugly hack!
+  
+  # ask server if this file has been already sent
+  if ($file and not $xx and not 
+      ($opt_s or $opt_g or $opt_o or $opt_d or $opt_l or $opt_L or ${'opt_/'}))
+  {
+    ($seek,$location) = query_file($server,$port,$frecipient||$P{to},$P{from},
+                                   $P{id},$filename,$fileid);
+    if ($filesize == $seek) {
+      print "Location: $location\n" if $location and $nomail;
+      warn "$0: $file has been already transferred\n";
+      return $file;
+    } elsif ($seek and $seek < $filesize) {
+      $resume = " (resuming at byte $seek)";
+    } elsif ($filesize <= $seek) {
+      $seek = 0;
+    }
+    if ($proxy) {
+      sleep 1;    # do not overrun proxy
+      serverconnect($server,$port);
+    }
+  }
+  
+  # file part size
+  if ($chunksize and $proxy and $port != 443 
+      and $filesize - $seek > $chunksize - $bs) {
+    if ($features !~ /MULTIPOST/) {
+      die sprintf("$0: server does not support chunked multi-POST needed for"
+                  ." files > %d MB via proxy\n",$chunksize/M);
+    }
+    $opt_o = 0; # no overwriting mode for next chunks
+    $fpsize = $chunksize - $bs;
+  } else {
+    $fpsize = $filesize - $seek;
+  }
+
+  $boundary = randstring(48);
+  
+  $P{seek} = $seek;
+  $P{filesize} = $filesize;
+
+  # send HTTP POST variables
+  if ($skey) {
+    $P{skey} = $skey;
+    @pv = qw'from to skey keep autodelete comment seek filesize';
+  } elsif ($gkey) {
+    $P{gkey} = $gkey;
+    @pv = qw'from to gkey keep autodelete comment seek filesize';
+  } else {
+    @pv = qw'from to id replyto keep autodelete comment command seek filesize';
+  }
+  foreach my $v (@pv) {
+    if ($P{$v}) {
+      my $name = uc($v);
+      push @hb,"--$boundary";
+      push @hb,"Content-Disposition: form-data; name=\"$name\"";
+      push @hb,"";
+      push @hb,encode_utf8($P{$v});
+    }
+  }
+  
+  # at last, POST the file
+  if ($file) {
+    push @hb,"--$boundary";
+    push @hb,"Content-Disposition: form-data; name=\"FILE\"; filename=\"$filename\"";
+    unless ($opt_d) {
+      if ($opt_M) { push @hb,"Content-Type: application/x-mime" }
+      else        { push @hb,"Content-Type: application/octet-stream" }
+      if (${'opt_/'}) {
+        $flink = abs_path($file);
+        push @hb,"Content-Location: $flink";
+      } else {
+        # push @hb,"Content-Length: " . ((-s $file||0) - $seek); # optional header!
+        push @hb,"Content-Length: $fpsize"; # optional header! NOT filesize!
+        push @hb,"X-File-ID: $fileid";
+      }
+      push @hb,"";
+    }
+    push @hb,"";
+    # prevent proxy chunked mode reply
+    $connection = "close";
+  }
+
+  push @hb,"--$boundary--";
+
+  if ($fpsize < 0) {
+    $length = $fpsize;
+  } else {
+    $length = length(join('',@hb)) + scalar(@hb)*2 + $fpsize;
+  }
+
+  if ($file and not $opt_d) {
+    if ($flink) { $hb[-2] = $flink }
+    else        { $hb[-2] = '(file content)' }
+  }
+  # any other extra URL arguments
+  my $opt_X = '';
+  $opt_X = "?$::opt_X" if $::opt_X and $file;
+
+  # HTTP header
+  push @hh,"POST $proxy_prefix/fup$opt_X HTTP/1.1";
+  push @hh,"Host: $server:$port";
+  push @hh,"User-Agent: $useragent";
+  push @hh,"Content-Length: $length";
+  push @hh,"Content-Type: multipart/form-data; boundary=$boundary";
+  push @hh,"Connection: $connection" if $connection;
+  push @hh,'';
+
+  if ($opt_v) {
+    print "--> $_\n" foreach (@hh,@hb);
+  }
+
+  $SIG{PIPE} = \&sigpipehandler;
+#    foreach $sig (keys %SIG) {
+#      eval '$SIG{$sig} = sub { print "\n!!! SIGNAL '.$sig.' !!!\n"; exit; }';
+#    }
+
+  if ($file) {
+    pop @hb;
+    pop @hb unless $flink;
+    nvtsend(@hh,@hb) or do {
+      warn "$0: server has closed the connection, reconnecting...\n";
+      sleep 3;
+      goto FORMDATAPOST; # necessary: new $sid ==> new @hh
+    };
+    
+    unless ($opt_d or $flink) {
+      
+      $t0 = $t2 = int(time);
+      $tt = $t0-1;
+      $t1 = 0;
+      $tc = 0;
+      
+      if ($opt_s) {
+        if ($opt_g) {
+          open $file,"gpg -e -r $to|" or die "$0: cannot run gpg - $!\n";
+        } else {
+          open $file,'>&=STDIN' or die "$0: cannot open STDIN - $!\n";
+        }
+      } elsif ($tar) {
+        if ($opt_g) {
+          open $file,"$tar|gpg -e -r $to|" or die "$0: cannot run tar&gpg - $!\n";
+        } else {
+          open $file,"$tar|" or die "$0: cannot run tar - $!\n";
+        }
+        if (-t STDOUT) {
+          $tpid = fork();
+          if (defined $tpid and $tpid == 0) {
+            sleep 1;
+            if (open $tarlist,$tarlist) {
+              # print "\n$tar|\n"; system "ls -l $tarlist";
+              while ($tarlist) {
+                while (<$tarlist>) {
+                  print ' 'x(length($file)+40),"\r",$_;
+                }
+                sleep 1;
+              }
+            }
+            exit;
+          }
+          $SIG{CHLD} = 'IGNORE';
+        }
+        if ($seek) {
+          print "Fast forward to byte $seek (resuming)\n";
+          readahead($file,$seek);
+        }
+      } else {
+        if ($opt_g) {
+          my $fileq = $file;
+          $fileq =~ s/([^\w\-\@\#%,.=+~_:])/\\$1/g;
+          open $file,"gpg -e -r $to <$fileq|" or die "$0: cannot run gpg - $!\n";
+        } else {
+          open $file,$file or die "$0: cannot read $file - $!\n";
+          seek $file,$seek,0;
+        }
+        binmode $file;
+      }
+      
+      $bytes = 0;
+      autoflush $SH 0;
+      
+      print $rcamel[0] if ${'opt_+'};
+
+      while (my $b = read $file,$buf,$bs) {
+        print {$SH} $buf or &sigpipehandler;
+        $bytes += $b;
+        if ($filesize > 0 and $bytes+$seek > $filesize) {
+          die "$0: $file filesize has grown while uploading\n";
+        }
+        $bt += $b;
+        $t2 = time;
+        if (${'opt_+'} and int($t2*10)>$tc) {
+          print $rcamel[$tc%2+1];
+          $tc = int($t2*10);
+        }
+        if (not $opt_q and -t STDOUT and int($t2)>$t1) {
+          &sigpipehandler unless $SH->connected;
+          # smaller block size is better on slow links
+          $bs = 4096 if $t1 and $bs>4096 and $bytes/($t2-$t0)<65536;
+          if ($filesize > 0) {
+            $pct = sprintf "(%d%%)",int(($bytes+$seek)/$filesize*100);
+          }
+          if ($bytes>2*M and $bs>4096) {
+            printf STDERR "%s: %d MB of %d MB %s %d kB/s        \r",
+                   $opt_s||$opt_a||$file,
+                   int(($bytes+$seek)/M),
+                   int($filesize/M),
+                   $pct,
+                   int($bt/k/($t2-$tt));
+          } else {
+            printf STDERR "%s: %d kB of %d MB %s %d kB/s        \r",
+                   $opt_s||$opt_a||$file,
+                   int(($bytes+$seek)/k),
+                   int($filesize/M),
+                   $pct,
+                   int($bt/k/($t2-$tt));
+          }
+          $t1 = $t2;
+          # time window for transfer rate calculation
+          if ($t2-$tt>10) {
+            $bt = 0;
+            $tt = $t2;
+          }
+        }
+        last if $filesize > 0 and $bytes >= $fpsize;
+        sleep 1 while ($opt_m and $bytes/k/(time-$t0||1) > $opt_m);
+      }
+      close $file; # or die "$0: error while reading $file - $!\n";
+      $tt = ($t2-$t0)||1;
+      
+      print $rcamel[2] if ${'opt_+'};
+      
+      # terminate tar verbose output job
+      if ($tpid) {
+        sleep 2;
+        kill 9,$tpid;
+        unlink $tarlist;
+      }
+    
+      unless ($opt_q) {
+        if (not $chunksize and $bytes+$seek < $filesize) {
+          die "$0: $file filesize has shrunk while uploading\n";
+        }
+        
+        if ($seek or $chunksize and $chunksize < $filesize) {
+          if ($fpsize>2*M) {
+            printf STDERR "%s: %d MB in %d s (%d kB/s)",
+                           $opt_s||$opt_a||$file,
+                           int($bytes/M),
+                           $tt,
+                           int($bytes/k/$tt);
+            if ($bytes+$seek == $filesize) {
+              printf STDERR ", total %d MB\n",int($filesize/M);
+            } else {
+              printf STDERR ", chunk #%d : %d MB\n",
+                            $chunk,int(($bytes+$seek)/M);
+            }
+          } else {
+            printf STDERR "%s: %d kB in %d s (%d kB/s)",
+                          $opt_s||$opt_a||$file,
+                          int($bytes/k),
+                          $tt,
+                          int($bytes/k/$tt);
+            if ($bytes+$seek == $filesize) {
+              printf STDERR ", total %d kB\n",int($filesize/k);
+            } else {
+              printf STDERR ", chunk #%d : %d kB\n",
+                            $chunk,int(($bytes+$seek)/k);
+            }
+          }
+        } else {
+          if ($bytes>2*M) {
+            printf STDERR "%s: %d MB in %d s (%d kB/s)        \n",
+                          $opt_s||$opt_a||$file,
+                          int($bytes/M),
+                          $tt,
+                          int($bytes/k/$tt);
+          } else {
+            printf STDERR "%s: %d kB in %d s (%d kB/s)        \n",
+                          $opt_s||$opt_a||$file,
+                          int($bytes/k),
+                          $tt,
+                          int($bytes/k/$tt);
+          }
+        }
+        
+        if (-t STDOUT and not ($opt_s or $opt_g)) {
+          print STDERR "waiting for server ok..."
+        }
+      }
+    }
+    
+    autoflush $SH 1;
+    print {$SH} "\r\n--$boundary--\r\n";
+
+    # special handling of streaming file because of stunnel tcp shutdown bug
+    if ($opt_s or $opt_g) {
+      close $SH;
+      sleep 1;
+      serverconnect($server,$port);
+      query_sid($server,$port) unless $anonymous;
+      ($seek,$location) = query_file($server,$port,$P{to},$P{from},$sid,
+                                     $filename,$fileid);
+      if ($seek != $bytes) {
+        die "$0: streamed $bytes bytes but server received $seek bytes\n";
+      }
+      return "X-Location: $location\n";
+    }
+    
+    if ($flink) {
+      $bytes = -s $flink;
+      if ($bytes>2*M) {
+        printf STDERR "%s: %d MB\n",$flink,int($bytes/M);
+      } else {
+        printf STDERR "%s: %d kB\n",$flink,int($bytes/k);
+      }
+    }
+  } else {
+    autoflush $SH 1;
+    nvtsend(@hh,@hb);
+  }
+
+  # SuSe: Can't locate object method "BINMODE" via package "IO::Socket::SSL::SSL_HANDLE"
+  # binmode $SH,':utf8'; 
+  
+  if (not $opt_q and $file and -t STDOUT) {
+    print STDERR "\r                         \r";
+  }
+  while (<$SH>) {
+    s/[\r\n]+//;
+    print "<-- $_\n" if $opt_v;
+    last if @r and $r[0] =~ / 204 / and /^$/ or /<\/html>/i;
+    push @r,decode_utf8($_);
+  }
+  
+  if ($file) {
+    close $SH;
+    undef $SH;
+    if ($proxy and $fpsize+$seek < $filesize) {
+      goto FORMDATAPOST;
+    }
+  }
+  
+  return @r;
+}
+
+
+sub randstring {
+    my $n = shift;
+    my @rc = ('A'..'Z','a'..'z',0..9 );
+    my $rn = @rc;
+    my $rs;
+
+    for (1..$n) { $rs .= $rc[int(rand($rn))] };
+    return $rs;
+}
+
+
+sub zipsplit {
+  my $zipbase = shift;
+  my @files = @_;
+  my @zipfiles = ();
+  my $file;
+  my ($zsize,$size,$n);
+
+  $zipbase =~ s/\.zip$//;
+  map { s/([^_\w\+\-\.])/\\$1/g } @files;
+
+  open my $ff,"find @files|" or die "$0: cannot search for @_ - $!\n";
+  @files = ();
+
+  zipfile: for (;;) {
+    $n++;
+    if ($n eq 10) {
+      unlink @zipfiles;
+      die "$0: too many zip-archives\n";
+    }
+    $zsize = 0;
+    while ($file = <$ff>) {
+      chomp $file;
+      # next if -l $file or not -f $file;
+      next unless -f $file;
+      $size = -s $file;
+      if ($size > 2147480000) {
+        unlink @zipfiles;
+        die "$0: $file too big for zip\n";
+      }
+      if ($zsize + $size > 2147000000) {
+        push @zipfiles,zip($zipbase.'_'.$n.'.zip',@files);
+        @files = ($file);
+        next zipfile;
+      } else {
+        push @files,$file;
+        $zsize += $size;
+      }
+    }
+    close $ff;
+    last;
+  }
+  push @zipfiles,zip($zipbase.'_'.$n.'.zip',@files);
+  return @zipfiles;
+}
+
+
+sub zip {
+  no strict 'refs';
+  my $zip = shift;
+  my $cmd;
+  local $_;
+
+  unlink $zip;
+  # if ($opt_c) { $cmd = "zip -@ $zip" }
+  # else        { $cmd = "zip -0 -@ $zip" }
+  $cmd = "zip -@ $zip";
+  if (${'opt_#'}) {
+    ${'opt_#'} =~ s/#/ /g;
+    $cmd .= " -x ".${'opt_#'};
+  }
+  print $cmd,"\n" if $opt_v;
+  open $cmd,"|$cmd" or die "$0: cannot create $zip - $!\n";
+  foreach (@_) { 
+    print {$cmd} $_."\n";
+    print "  $_\n" if $opt_v;
+  }
+  close $cmd or die "$0: zip failed - $!\n";
+
+  return $zip;
+}
+
+
+sub getline {
+  my $file = shift;
+  local $_;
+  
+  while (<$file>) {
+    chomp;
+    s/^#.*//;
+    s/\s+#.*//;
+    s/^\s+//;
+    s/\s+$//;
+    return $_ if length($_);
+  }
+  return '';
+}
+
+
+sub query_file {
+  my ($server,$port,$to,$from,$id,$filename,$fileid) = @_;
+  my $seek = 0;
+  my $qfileid = '';
+  my ($head,$location);
+  my ($response,$fexsrv);
+  local $_;
+  
+  $to =~ s/,.*//;
+  $to =~ s/:\w+=.*//;
+  $to = $AB{$to} if $AB{$to};
+  $filename =~ s/([^_=:,;<>()+.\w\-])/'%'.uc(unpack("H2",$1))/ge; # urlencode
+  if ($skey) {
+    $head = "HEAD $proxy_prefix/fop/$to/$from/$filename??SKEY=$id HTTP/1.1";
+  } elsif ($gkey) {
+    $head = "HEAD $proxy_prefix/fop/$to/$from/$filename??GKEY=$id HTTP/1.1";
+  } else {
+    $head = "HEAD $proxy_prefix/fop/$to/$from/$filename??ID=$id HTTP/1.1";
+  }
+  sendheader("$server:$port",$head);
+  $_ = <$SH>;
+  unless (defined $_ and /\w/) {
+    die "$0: no response from server\n";
+  }
+  s/\r//;
+  print "<-- $_" if $opt_v;
+  unless (/^HTTP.* 200/) {
+    s:HTTP/[\d\. ]+::;
+    $response = $_;
+    while (<$SH>) {
+      s/\r//;
+      print "<-- $_" if $opt_v;
+      $fexsrv = $_ if /^(Server: fexsrv|X-Features:)/;
+      last if /^\s*$/;
+    }
+    die "$0: no fexserver at $server:$port\n" unless $fexsrv;
+    die "$0: server response: $response";
+  }
+  while (<$SH>) {
+    s/\r//;
+    print "<-- $_" if $opt_v;
+    last if /^$/;
+    if (/^Content-Length:\s+(\d+)/)    { $seek = $1 }
+    if (/^X-File-ID:\s+(.+)/)          { $qfileid = $1 }
+    if (/^X-Features:\s+(.+)/)         { $features = $1 }
+    if (/^X-Location:\s+(.+)/)         { $location = $1 }
+  }
+
+  # return true seek only if file is identified
+  $seek = 0 if $qfileid and $qfileid ne $fileid;
+  
+  return ($seek,$location);
+}
+
+
+sub edit_address_book {
+  my ($user) = @_;
+  my $alias;
+  my $ab = "$fexhome/ADDRESS_BOOK";
+  my (%AB,@r);
+  local $_;
+  
+  die "$0: address book not available for subusers\n"      if $skey;
+  die "$0: address book not available for group members\n" if $gkey;
+
+  female_mode("edit your address book?") if $opt_F;
+
+  %AB = query_address_book($server,$port,$user);
+  if ($AB{ADDRESS_BOOK} !~ /\w/) {
+    $AB{ADDRESS_BOOK} = 
+      "# Format: alias e-mail-address # Comment\n".
+      "# Example:\n".
+      "framstag framstag\@rus.uni-stuttgart.de\n";
+  }
+  open $ab,">$ab" or die "$0: cannot write to $ab - $!\n";
+  print {$ab} $AB{ADDRESS_BOOK};
+  close $ab;
+  
+  system $editor,$ab;
+  exit unless -s $ab;
+
+  $opt_o = $opt_A;
+  
+  serverconnect($server,$port);
+  query_sid($server,$port);
+  
+  @r = formdatapost(
+       from            => $user,
+        to             => $user,
+        id             => $sid,
+        file           => $ab,
+  );
+  
+  unlink $ab,$ab.'~';
+}
+
+
+sub query_address_book {
+  my ($server,$port,$user) = @_;
+  my ($req,$alias,$address,$options,$comment,$cl,$ab,$b);
+  my %AB;
+  local $_;
+
+  unless ($SH) {
+    serverconnect($server,$port);
+    query_sid($server,$port);
+  }
+  
+  $req = "GET $proxy_prefix/fop/$user/$user/ADDRESS_BOOK?ID=$sid HTTP/1.1";
+  sendheader("$server:$port",$req);
+  $_ = <$SH>;
+  unless (defined $_ and /\w/) {
+    die "$0: no response from server\n";
+  }
+  s/\r//;
+  print "<-- $_" if $opt_v;
+  unless (/^HTTP.* 200/) {
+    if (/^HTTP.* 404/) {
+      while (<$SH>) { last if /^\r?\n/ }
+      return;
+    } else {
+      # s:HTTP/[\d\. ]+::;
+      # die "$0: server response: $_";
+      close $SH;
+      undef $SH;
+      return ();
+    }
+  }
+  while (<$SH>) {
+    s/\r//;
+    print "<-- $_" if $opt_v;
+    last if /^$/;
+    $cl = $1 if /^Content-Length: (\d+)/;
+  }
+  
+  if ($cl) {
+    while (<$SH>) {
+      $b += length;
+      $ab .= $_;
+      s/[\r\n]//g;
+      s/^\s+//;
+      s/\s+$//;
+      print "<-- $_\n" if $opt_v;
+      s/\s*#\s*(.*)//;
+      if ($_) {
+        $comment = $1||'';
+        ($alias,$address,$options) = split;
+        if ($address) {
+          if ($options) { $options =~ s/[()]//g }
+          else          { $options = '' }
+          $AB{$alias} = $address;
+          $AB{$alias}->{options} = $options||'';
+          $AB{$alias}->{comment} = $comment||'';
+          if ($options and $options =~ /keep=(\d+)/i) {
+            $AB{$alias}->{keep} = $1;
+          }
+          if ($options and $options =~ /autodelete=(\w+)/i) {
+            $AB{$alias}->{autodelete} = $1;
+          }
+        }
+      }
+      last if $b >= $cl;
+    }
+  }
+  
+  $AB{ADDRESS_BOOK} = $ab;
+  
+  return %AB;
+}
+
+
+# sets global $sid $features $timeout # ugly hack! :-}
+sub query_sid {
+  my ($server,$port) = @_;
+  my ($req,$fexsrv);
+  local $_;
+
+  $sid = $id;
+
+  if ($port eq 443) {
+    return if $features;    # early return if we know enough
+    $req = "OPTIONS FEX HTTP/1.1";
+  } elsif ($proxy) {
+    return if $features;    # early return if we know enough
+    $req = "GET $proxy_prefix/SID HTTP/1.1";
+  } else {
+    $req = "GET SID HTTP/1.1";
+  }
+
+  sendheader("$server:$port",$req,"User-Agent: $useragent");
+  $_ = <$SH>;
+  unless (defined $_ and /\w/) {
+    print "\n" if $opt_v;
+    die "$0: no response from server\n";
+  }
+  s/\r//;
+  print "<-- $_" if $opt_v;
+    
+  if (/^HTTP.* [25]0[01] /) {
+    if (not $proxy and $port ne 443 and /^HTTP.* 201 (.+)/) {
+      $sid = 'MD5H:'.md5_hex($id.$1);
+    }
+    while (<$SH>) {
+      s/\r//;
+      print "<-- $_" if $opt_v;
+      $features = $1 if /^X-Features: (.+)/;
+      $timeout = $1  if /^X-Timeout: (\d+)/;
+      last if /^\n/;
+    }
+  } elsif (/^HTTP.* 301 /) {
+    while (<$SH>) { last if /Location/ }
+    die "$0: cannot use $server:$port because server has a redirection to\n".$_;
+  } else {
+    # no SID support - perhaps transparent web proxy?
+    while (<$SH>) {
+      s/\r//;
+      print "<-- $_" if $opt_v;
+      $fexsrv = $_ if /^(Server: fexsrv|X-Features:)/;
+      last if /^\s*$/;
+    }
+    die "$0: no fexserver at $server:$port\n" unless $fexsrv;
+    serverconnect($server,$port);
+    $sid = $id;
+  }
+  
+  # warn "proxy: $proxy\n";
+  if ($proxy) {
+    serverconnect($server,$port);
+    $sid = $id;
+  }
+  
+}
+
+
+sub xxget {
+  my ($from,$id,$save) = @_;
+  my $bs = 4096;
+  my $xx = $save;
+  my ($url,$B,$b,$t0,$t1,$cl);
+  my ($ts,$tso);
+  local $_;
+
+  $xx =~ s:.*/::;
+  $url = "$proxy_prefix/fop/$from/$from/$xx?ID=$id";
+
+  sendheader("$server:$port","GET $url HTTP/1.0","User-Agent: $useragent");
+  http_response();
+  while (<$SH>) {
+    s/\r//;
+    print "<-- $_" if $opt_v;
+    $cl = $1 if /^Content-Length:\s(\d+)/;
+    # $ft = $1 if /^X-File-Type:\s(.+)/;
+    last if /^$/;
+  }
+
+  die "$0: no Content-Length in server-reply\n" unless $cl;
+  
+  open F,">$save" or die "$0: cannot write to $save - $!\n";
+  binmode F;
+  
+  $t0 = $t1 = int(time);
+  $tso = '';
+  
+  while ($b = read($SH,$_,$bs)) {
+    $B += $b;
+    print F;
+    if (int(time) > $t1) {
+      $t1 = int(time);
+      $ts = ts($B,$cl);
+      if ($ts ne $tso) {
+        print STDERR $ts,"\r";
+        $tso = $ts;
+      }
+    }
+    sleep 1 while ($opt_m and $B/k/(time-$t0||1) > $opt_m);
+  }
+  
+  print STDERR ts($B,$cl),"\n";
+  close F;
+}
+
+
+# transfer status
+sub ts {
+  my ($b,$tb) = @_;
+  return sprintf("transferred: %d MB (%d%%)",int($b/M),int($b/$tb*100));
+}
+  
+
+sub sigpipehandler {
+  $SIG{ALRM} = sub { };
+  if (fileno $SH) {
+    alarm(1);
+    @_ = <$SH>;
+    alarm(0);
+    kill 9,$tpid if $tpid;
+    if (@_ and $opt_v) {
+      die "\n$0: ($$) server error: @_\n";
+    }
+    if (@_ and $_[0] =~ /^HTTP.* \d+ (.*)/) {
+      die "\n$0: server error: $1\n";
+    }
+  }
+  $timeout *= 2;
+  warn "\n$0: connection to $server died\n";
+  warn "retrying after $timeout seconds...\n";
+  sleep $timeout;
+  if ($windoof) { exec $^X,$0,@_ARGV }
+  else          { exec $_0,@_ARGV }
+  die $!;
+}
+
+
+sub checkrecipient {
+  my ($from,$to) = @_;
+  my @r;
+  local $_;
+  
+  @r = formdatapost(
+       from    => $from,
+        to     => $to,
+        id     => $sid,
+        command        => 'CHECKRECIPIENT',
+  );
+
+  $_ = shift @r or die "$0: no reply from server\n";
+
+  if (/ 2\d\d /) {
+    foreach (@r) {
+      last if /^$/;
+      if (s/X-(Recipient: .+)/$1\n/) {
+        s/autodelete=\w+/autodelete=$opt_D/ if $opt_D;
+        s/keep=\d+/keep=$opt_k/             if $opt_k;
+        print;
+        $frecipient ||= (split)[1];
+      }
+    }
+  } else {
+    http_response($_,@r);
+  }
+}
+
+
+# get ID data from ID file
+sub get_id {
+  my $idf = shift;
+
+  $fexcgi = getline($idf) || die "$0: no FEX-URL in $idf\n";
+  $from   = getline($idf) || die "$0: no FROM in $idf\n";
+  $id     = getline($idf) || die "$0: no ID in $idf\n";
+  if ($fexcgi =~ s/!([\w.-]+:\d+)(:(\d+))?//) {
+    $proxy = $1;
+    $chunksize = $3 || 0;
+  }
+  unless ($fexcgi =~ /^[_:=\w\-\.\/\@\%]+$/) {
+    die "$0: illegal FEX-URL \"$fexcgi\" in $idf\n";
+  }
+  unless ($from =~ /^[_:=\w\-\.\/\@\%\+]+$/) {
+    die "$0: illegal FROM \"$from\" in $idf\n";
+  }
+  $fexcgi =~ s:/+$::;
+}
+
+
+# for windows
+sub inquire {
+  my ($file,$to);
+  for (;;) {
+    print "file to send: ";
+    chomp($file = <STDIN>);
+    $file =~ s/^\"//;
+    $file =~ s/\"$//;
+    last if -e $file;
+    warn "$file does not exist\n";
+  }
+  print "recipient (e-mail address): ";
+  chomp($to = <STDIN>);
+  die $usage unless $to;
+  unless ($opt_n) {
+    print "comment: ";
+    chomp($opt_C = <STDIN>);
+  }
+  @ARGV = ($file,$to);
+}
+
+
+sub shelldo {
+  if (system(@_) < 0) { die "failed: @_\n" }
+}
+
+
+# emulate seek on a pipe
+sub readahead {
+  my $fh = shift; # filehandle
+  my $ba = shift; # bytes ahead
+  my $bs = 2**16;
+  my $s = 0;
+  my $n;
+  local $_;
+  
+  while ($s < $ba) { 
+    $n = $ba-$s;
+    $n = $bs if $n > $bs; 
+    $s += read $fh,$_,$n; 
+  }
+}
+
+
+# fileid is inode and mtime
+sub fileid {
+  my @s = stat(shift);
+  return @s ? $s[1].$s[9] : int(time);
+}
+
+
+# collect file meta data (filename, inode, mtime)
+sub fmd {
+  my @files = @_;
+  my ($file,$dir);
+  my $fmd = '';
+  
+  foreach $file (@files) {
+    if (not -l $file and -d $file) {
+      $dir = $file;
+      if (opendir $dir,$dir) {
+        while (defined ($file = readdir($dir))) {
+          next if $file eq '..';
+          if ($file eq '.') {
+            $fmd .= $file.fileid($dir);
+          } else {
+            $fmd .= fmd("$dir/$file");
+          }
+        }
+        closedir $dir;
+      }
+    } else {
+      $fmd .= $file.fileid($file);
+    }
+  }
+  
+  return $fmd;
+}
+
+
+# from MIME::Base64::Perl
+sub decode_b64 {
+  local $_ = shift;
+  my $uu = '';
+  my ($i,$l);
+  
+  tr|A-Za-z0-9+=/||cd;
+  s/=+$//;
+  tr|A-Za-z0-9+/| -_|;
+  return "" unless length;
+
+  $l = (length)-60;
+  for ($i = 0; $i <= $l; $i += 60) {
+    $uu .= "M" . substr($_,$i,60);
+  }
+  $_ = substr($_,$i);
+  if (length) {
+    $uu .= chr(32+(length)*3/4) . $_;
+  }
+  return unpack("u",$uu);
+}
+
+
+sub female_mode {
+  local $_;
+  if (open my $tty,'/dev/tty') {
+    print "@_\n";
+    print "  [y] yes\n",
+          "  [n] no\n",
+          "  [p] perhaps - don't know\n",
+          "your choice: ";
+    $_ = <$tty> || '';
+    close $tty;
+    if (/^y/i) { return }
+    if (/^n/i) { exit }
+    if (/^p/i) { int(rand(2)) ? return : exit }
+    female_mode(@_);
+  }
+}
+
+
+sub http_response {
+  local $_ = shift || <$SH>;
+  my @r = @_;
+  my $error;
+
+  $_ = <$SH> unless $_;
+  unless (defined $_ and /\w/) {
+    die "$0: no response from server\n";
+  }
+  s/\r?\n//;
+  # CGI fatalsToBrowser
+  if (/^HTTP.* 500/) {
+    @r = <$SH> unless @r;
+    @r = ()    unless @r;
+    die "$0: server error: $_\n@r\n";
+  }
+  unless (/^HTTP.* 200/) {
+    $error = $_;
+    $error =~ s/HTTP.[\s\d.]+//;
+    if ($opt_v) {
+      print "<-- $_";
+      print "<-- $_" while <$SH>;
+    }
+    die "$0: server error: $error\n";
+  }
+
+  print "<-- $_\n" if $opt_v;
+  return $_;
+}
+
+
+sub ws {
+  local $_ = shift;
+  return split;
+}
+
+
+sub update {
+  my $cfb = '### common functions ###';
+  my $cfc;
+  
+  local $/;
+  
+  open $0,$0 or die "cannot read $0 - $!\n";
+  $_ = <$0>;
+  close $0;
+  s/.*\n$cfb\n//s;
+  $cfc = $_;
+  
+  foreach my $p (qw(fexget sexsend)) {
+    open $p,$p or die "cannot read $p - $!\n";
+    $_ = <$p>;
+    close $p;
+    s/\n$cfb.*/\n$cfb\n$cfc/s;
+    system "vv -s $p";
+    open $p,'>',$p or die "cannot write $p - $!\n";
+    print {$p} $_;
+    close $p;
+  }
+
+  exec "l $0 fexget sexsend";
+  exit;
+}
+
+### common functions ###
+
+
+sub mtime {
+  my @d = localtime((stat shift)[9]);
+  return sprintf('%d%02d%02d',$d[5]+1900,$d[4]+1,$d[3]);
+}
+
+
+sub urldecode {
+  local $_ = shift;
+  s/\%([a-f\d]{2})/chr(hex($1))/ige;
+  return $_;
+}
+
+
+sub get_ssl_env {
+  # set SSL/TLS options
+  $SSL{SSL_verify_mode} = $ENV{SSLVERIFY} if defined($ENV{SSLVERIFY});
+  foreach my $opt (qw(
+    SSL_version
+    SSL_cipher_list 
+    SSL_verify_mode 
+    SSL_ca_path 
+    SSL_ca_file)
+  ) {
+    my $env = uc($opt);
+    $env =~ s/_//g;
+    $SSL{$opt} = $ENV{$env} if defined($ENV{$env});
+  }
+
+  if ($SSL{SSL_verify_mode}) {
+    &search_ca;
+    unless ($SSL{SSL_ca_path} or $SSL{SSL_ca_file}) {
+      die "$0: \$SSLVERIFYMODE, but not valid \$SSLCAPATH or \$SSLCAFILE\n";
+    }
+  } elsif (defined($SSL{SSL_verify_mode})) {
+    # user has set SSLVERIFY=0 !
+  } else {
+    &search_ca;
+    $SSL{SSL_verify_mode} = 1 if $SSL{SSL_ca_path} or $SSL{SSL_ca_file};
+  }
+}
+
+sub search_ca {
+  local $_;
+  return if $SSL{SSL_ca_file} or $SSL{SSL_ca_path};
+  foreach (qw(/etc/ssl/certs/ca-certificates.crt)) {
+    if (-f) {
+      $SSL{SSL_ca_file} = $_;
+      return;
+    }
+  }
+  foreach (qw(/etc/ssl/certs /etc/pki/tls/certs)) {
+    if (-f) {
+      $SSL{SSL_ca_path} = $_;
+      return;
+    }
+  }
+}
+
+
+sub serverconnect {
+  my ($server,$port) = @_;
+  my $connect = "CONNECT $server:$port HTTP/1.1";
+  local $_;
+  
+  if ($opt_v and $port == 443 and %SSL) {
+    foreach my $v (keys %SSL) {
+      printf "%s => %s\n",$v,$SSL{$v};
+    }
+  }
+  
+  if ($proxy) {
+    tcpconnect(split(':',$proxy));
+    if ($port == 443) {
+      printf "--> %s\n",$connect if $opt_v;
+      nvtsend($connect,"");
+      $_ = <$SH>;
+      s/\r//;
+      printf "<-- $_"if $opt_v;
+      unless (/^HTTP.1.. 200/) {
+        die "$0: proxy error : $_";
+      }
+      eval "use IO::Socket::SSL";
+      die "$0: cannot load IO::Socket::SSL\n" if $@;
+      $SH = IO::Socket::SSL->start_SSL($SH,%SSL);
+    }
+  } else {
+    tcpconnect($server,$port);
+  }
+#  if ($port == 443 and $opt_v) {
+#    printf "%s\n",$SH->get_cipher();
+#  }
+}
+
+
+# set up tcp/ip connection
+sub tcpconnect {
+  my ($server,$port) = @_;
+  
+  if ($SH) {
+    close $SH;
+    undef $SH;
+  }
+  
+  if ($port == 443) {
+    # eval "use IO::Socket::SSL qw(debug3)";
+    eval "use IO::Socket::SSL";
+    die "$0: cannot load IO::Socket::SSL\n" if $@;
+    $SH = IO::Socket::SSL->new(
+      PeerAddr => $server,
+      PeerPort => $port,
+      Proto    => 'tcp',
+      %SSL
+    );
+  } else {
+    $SH = IO::Socket::INET->new(
+      PeerAddr => $server,
+      PeerPort => $port,
+      Proto    => 'tcp',
+    );
+  }
+  
+  if ($SH) {
+    autoflush $SH 1;
+  } else {
+    die "$0: cannot connect $server:$port - $@\n";
+  }
+  
+  print "TCPCONNECT to $server:$port\n" if $opt_v;
+}
+
+
+sub sendheader {
+  my $sp = shift;
+  my @head = @_;
+  my $head;
+  
+  push @head,"Host: $sp";
+  
+  foreach $head (@head) {
+    print "--> $head\n" if $opt_v;
+    print {$SH} $head,"\r\n";
+  }
+  print "-->\n" if $opt_v;
+  print {$SH} "\r\n";
+}
+
+
+sub nvtsend {
+  local $SIG{PIPE} = sub { $sigpipe = "@_" };
+  
+  $sigpipe = '';
+  
+  die "$0: internal error: no active network handle\n" unless $SH;
+  die "$0: remote host has closed the link\n" unless $SH->connected;
+  
+  foreach my $line (@_) {
+    print {$SH} $line,"\r\n";
+    if ($sigpipe) {
+      undef $SH;
+      return 0;
+    }
+  }
+  
+  return 1;
+}
+
+
+# from MIME::Base64::Perl
+sub encode_b64 {
+  my $res = "";
+  my $eol = "\n";
+  my $padding;
+  
+  pos($_[0]) = 0;
+  $res = join '',map(pack('u',$_)=~ /^.(\S*)/, ($_[0]=~/(.{1,45})/gs));
+  $res =~ tr|` -_|AA-Za-z0-9+/|;
+  $padding = (3-length($_[0])%3)%3;
+  $res =~ s/.{$padding}$/'=' x $padding/e if $padding;
+  return $res;
+}
diff --git a/htdocs/download/sex.stream b/htdocs/download/sex.stream
new file mode 120000 (symlink)
index 0000000..8350da9
--- /dev/null
@@ -0,0 +1 @@
+:sexsend:sexget:
\ No newline at end of file
diff --git a/htdocs/download/sexget b/htdocs/download/sexget
new file mode 120000 (symlink)
index 0000000..73cf1eb
--- /dev/null
@@ -0,0 +1 @@
+sexsend
\ No newline at end of file
diff --git a/htdocs/download/sexsend b/htdocs/download/sexsend
new file mode 100755 (executable)
index 0000000..7340e49
--- /dev/null
@@ -0,0 +1,723 @@
+#!/usr/bin/perl -w
+
+# client for stream exchange of the FEX service
+#
+# Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
+#
+# Perl Artistic Licence
+
+# sexsend / sexget / sexxx
+
+use Getopt::Std;
+use Socket;
+use IO::Handle;
+use IO::Socket::INET;
+use Digest::MD5 qw(md5_hex);  # encypted ID / SID 
+
+use constant k => 2**10;
+use constant M => 2**20;
+
+eval 'use Net::INET6Glue::INET_is_INET6';
+
+our $version = 20150120;
+
+my %SSL = (SSL_version => 'TLSv1');
+my $sigpipe;
+
+if (-f ($_ = '/etc/fex/config.pl')) {
+  eval { require } or warn $@;
+}
+
+$0 =~ s:.*/::;
+$| = 1;
+
+# sexsend is default
+$usage = 
+  "usage: ... | $0 [options] [SEX-URL/]recipient [stream]\n".
+  "options: -v           verbose mode\n".
+  "         -g           show transfer rate\n".
+  "         -V           show version\n".
+  "         -t timeout   timeout in s (waiting for recipient)\n".
+  "special: recipient may be \"public\" or \"anonymous\" or \".\"\n".
+  "see also: sexget, sexxx\n".
+  "example: tail -f /var/log/syslog | $0 fex.flupp.org/admin log\n";
+
+if ($0 eq 'sexget' or $0 eq 'fuckme') {
+  $usage = 
+    "usage: $0 [options] [[SEX-URL/]user:ID] [stream]\n".
+    "options: -v           verbose mode\n".
+    "         -g           show transfer rate\n".
+    "         -V           show version\n".
+    "arguments: user:ID    use this user & ID\n".
+    "                      (ID may be \"public\" or user:ID may be \"anonymous\")\n".
+    "           stream     name of the stream\n".
+    "see also: sexsend, sexxx\n".
+    "example: $0 log | grep kernel\n";
+}
+
+if ($0 eq 'sexxx') {
+  $usage = 
+    "usage: $0 [-v] [-g] [-c] [-u [SEX-URL/]user] [-s stream] [files...]\n".
+    "usage: $0 [-v] [-g]      [-u [SEX-URL/]user] [-s stream] | ...\n".
+    "options: -v               verbose mode\n".
+    "         -g               show transfer rate\n".
+    "         -q               quiet mode\n".
+    "         -c               compress files\n".
+    "         -u SEX-URL/user  SEX-URL and user (default: use FEXID/FEXXX)\n".
+    "         -s stream        stream name (default: xx)\n".
+    "see also: sexsend, sexget\n".
+    "examples: $0 -s config /etc /usr/local/etc\n".
+    "          $0 > backup.tar\n";
+}
+
+$fexhome = $ENV{FEXHOME} || $ENV{HOME}.'/.fex';
+$user = $id = '';
+$type = $timeout = $stream = $mode = '';
+$idf = "$fexhome/id";
+$bs = $ENV{BS} || 2**16; # I/O blocksize
+
+# server URL, user and auth-ID
+if ($FEXID = $ENV{FEXID}) {
+  $FEXID = decode_b64($FEXID) if $FEXID !~ /\s/;
+  ($fexcgi,$user,$id) = split(/\s+/,$FEXID);
+} else {
+  if (open $idf,$idf) {
+    chomp($fexcgi = <$idf>) or die "$0: no FEX-URL in $idf\n";
+    chomp($user = <$idf>)   or die "$0: no FROM in $idf\n";
+    chomp($id = <$idf>)     or die "$0: no ID in $idf\n";
+    close $idf;
+    despace($fexcgi,$user,$id);
+    unless ($fexcgi =~ /^[_:=\w\-\.\/\@\%]+$/) {
+      die "$0: illegal FEX-URL \"$fexcgi\" in $idf\n";
+    }
+    unless ($user =~ /^[_:=\w\-\.\/\@\%\+]+$/) {
+      die "$0: illegal FROM \"$user\" in $idf\n";
+    }
+  }
+}
+
+$opt_h = $opt_v = $opt_V = $opt_q = 0;
+$opt_u = $opt_s = $opt_c = $opt_t = '';
+
+$_ = "$fexhome/config.pl"; require if -f;
+
+if ($0 eq 'sexxx') {
+  
+  # xx server URL, user and auth-ID
+  if ($FEXXX = $ENV{FEXXX}) {
+    $FEXXX = decode_b64($FEXXX) if $FEXXX !~ /\s/;
+    ($fexcgi,$user,$id) = split(/\s+/,$FEXXX);
+  } elsif (open $idf,$idf) {
+    while (<$idf>) {
+      if (/^\[xx\]/) {
+        chomp($fexcgi = <$idf>) or die "$0: no xx FEX-URL in $idf\n";
+        chomp($user = <$idf>)   or die "$0: no xx FROM in $idf\n";
+        chomp($id = <$idf>)     or die "$0: no xx ID in $idf\n";
+        last;
+      }
+    }
+    close $idf;
+  }
+  
+  getopts('hgvcu:s:') or die $usage;
+  die $usage if $opt_h;
+  die $usage unless -t;
+
+  if ($opt_c) {
+    $opt_c = 'z';
+    $type = '&type=GZIP';
+  }
+
+  if ($opt_u) {
+    $fexcgi = $1 if $opt_u =~ s:(.+)/::;
+    $user = $opt_u;
+  }
+
+  unless ($fexcgi) {
+    die "$0: no xx user found, use \"$0 -u SEX-URL/user\"\n";
+  }
+
+  unless ($user) {
+    die "$0: no xx user found, use \"$0 -u user\"\n";
+  }
+  
+} elsif ($0 eq 'sexget' or $0 eq 'fuckme') {
+  getopts('hgvVdu:') or die $usage;
+  die $usage if $opt_h;
+
+
+  if ($opt_V) {
+    print "Version: $version\n";
+    exit unless @ARGV;
+  }
+  
+  if (not $opt_u and @ARGV and $ARGV[0] =~ m{^anonymous|/|:}) {
+    $opt_u = shift @ARGV;
+  }
+  
+  if ($opt_u) {
+    $fexcgi = $1 if $opt_u =~ s:(.+)/::;
+    ($user,$id) = split(':',$opt_u);
+    if ($user =~ /^anonymous/) {
+      $anonymous = $user;
+    } elsif (not $id) {
+      die $usage;
+    }
+  }
+
+  unless ($fexcgi) {
+    die "$0: no SEX URL found, use \"$0 -u SEX-URL/recipient\" or \"fexsend -I\"\n";
+  }
+  
+  unless ($user) {
+    die "$0: no recipient found, use \"$0 -u SEX-URL/recipient\" or \"fexsend -I\"\n";
+  }
+  
+} else { # sexsend
+  
+  $opt_g = 1;
+  getopts('hguvqVTt:') or die $usage;
+  die $usage if $opt_h;
+
+  if ($opt_V) {
+    print "Version: $version\n";
+    exit unless @ARGV;
+  }
+  
+  if ($opt_t and $opt_t =~ /^\d+$/) {
+    $timeout = "&timeout=$opt_t";
+  }
+
+  my $save_user = $user;
+  $user = shift or die $usage;
+  $fexcgi = $1 if $user =~ s:(.+)/::;
+  
+  if ($user =~ /^anonymous/) {
+    die "$0: need SEX-URL with anonymous SEX\n" unless $fexcgi;
+    $mode = 'anonymous';
+  } elsif ($user eq 'public') {
+    unless ($id) {
+      die "$0: public SEX not possible without FEXID, set it with \"fexsend -I\"\n";
+    }
+    $mode = $user;
+    $user = $save_user;
+  } elsif ($user eq '.') {
+    open $idf,$idf or die "$0: no $idf\n";
+    $_ = <$idf>;
+    $user = <$idf>||'';
+    chomp $user;
+  } else {
+    unless ($fexcgi) {
+      die "$0: no SEX URL found, use \"$0 SEX-URL/recipient\" or \"fexsend -I\"\n";
+    }
+  }
+  
+}
+
+&get_ssl_env;
+
+$fexcgi =~ s(^http://)()i;
+$fexcgi =~ s(/fup.*)();
+$server = $fexcgi;
+
+if    ($server =~ s(^https://)()i) { $port = 443 } 
+elsif ($server =~ /:(\d+)/)        { $port = $1 } 
+else                               { $port = 80 }    
+
+$server =~ s([:/].*)();
+
+## set up tcp/ip connection
+# $iaddr = gethostbyname($server) 
+#          or die "$0: cannot find ip-address for $server $!\n";
+# socket(SH,PF_INET,SOCK_STREAM,getprotobyname('tcp')) or die "$0: socket $!\n";
+# connect(SH,sockaddr_in($port,$iaddr)) or die "$0: connect $!\n";
+# warn "connecting $server:$port user=$user\n";
+if ($port == 443) {
+  if ($opt_v and %SSL) {
+    foreach my $v (keys %SSL) {
+      printf "%s => %s\n",$v,$SSL{$v};
+    }
+  }
+  eval "use IO::Socket::SSL";
+  die "$0: cannot load IO::Socket::SSL\n" if $@;
+  $SH = IO::Socket::SSL->new(                                                  
+    PeerAddr => $server,                                                       
+    PeerPort => $port,                                                         
+    Proto    => 'tcp',
+    %SSL
+  );                                                                           
+} else {                                                                       
+  $SH = IO::Socket::INET->new(
+    PeerAddr => $server,
+    PeerPort => $port,
+    Proto    => 'tcp',                                                         
+  );                                                                           
+}
+
+die "cannot connect $server:$port - $!\n" unless $SH;                          
+warn "TCPCONNECT to $server:$port\n" if $opt_v;
+
+# autoflush $SH 1;
+autoflush STDERR;
+
+$SIG{PIPE} = \&sigpipehandler;
+
+if ($0 eq 'sexget' or $0 eq 'fuckme') {
+  $stream = "&stream=" . shift if @ARGV;
+  if ($anonymous) {
+    $cid = 'anonymous';
+  } elsif ($id eq 'public') {
+    $cid = 'public';
+  } else {
+    $cid = query_sid($server,$port,$id);
+  }
+  request("GET /sex?BS=$bs&user=$user&ID=$cid$stream HTTP/1.0");
+  transfer($SH,STDOUT);
+  # print while sysread $SH,$_,$bs;
+  exit;
+}
+
+if ($0 eq 'sexxx') {
+  $stream = "&stream=" . ($opt_s || 'xx');
+  if (@ARGV) {
+    warn "streaming:\n";
+    open my $tar,'-|','tar',"cv${opt_c}f",'-',@ARGV or die "$0: cannot run tar - $!\n";
+    request("POST /sex?BS=$bs&user=$user$type$stream HTTP/1.0");
+    transfer($tar,$SH);
+    # while (read $tar,$_,$bs) { syswrite $SH,$_ }
+  } else {
+    $cid = query_sid($server,$port,$id);
+    request("GET /sex?BS=$bs&user=$user&ID=$cid$stream HTTP/1.0");
+    $opt_c = 'z' if $H{'CONTENT-TYPE'} =~ /gzip/i;
+    if (-t STDOUT) {
+      print "extracting from stream:\n";
+      open $out,"|tar xv${opt_c}f -" or die "$0: cannot run tar - $!\n";
+    } else {
+      if ($opt_c) {
+        open $out,"|gzip -d" or die "$0: cannot run gunzip - $!\n";
+      } else {
+        $out = *STDOUT;
+      }
+    }
+    print {$out} $_ while sysread $SH,$_,$bs;
+  }
+  exit;
+}
+
+# sexsend
+$stream = "&stream=" . shift if @ARGV;
+
+if ($mode eq 'anonymous') {
+  unless ($opt_q) {
+    print "http://$server:$port/sex?user=$user&ID=anonymous$stream\n";
+    printf "http://$server:$port/sex?%s\n",
+           encode_b64("user=$user&ID=anonymous$stream");
+  }
+  $mode = "&mode=anonymous";
+} elsif ($mode eq 'public') {
+  die "$0: need user/ID when sending to public, set it with fexsend -I\n" unless $user and $id;
+  unless ($opt_q) {
+    print "http://$server:$port/sex?user=$user&ID=public$stream\n";
+    printf "http://$server:$port/sex?%s\n",
+           encode_b64("user=$user&ID=public$stream");
+  }
+  $cid = query_sid($server,$port,$id);
+  $mode = "&ID=$cid&mode=public";
+} else {
+  # $user = checkalias($user) unless $opt_d;
+}
+
+request("POST /sex?BS=$bs&user=$user$mode$type$timeout$stream HTTP/1.0");
+print STDERR "==> (streaming ...)\n" if $opt_v;
+
+transfer(STDIN,$SH);
+  
+exit;
+
+
+sub transfer {
+  my $source = shift;
+  my $destination = shift;
+  my ($t0,$t1,$tt);
+  my ($B,$b,$bt);
+  
+  $t0 = $t2 = time;
+  $tt = $t0-1;
+  $t1 = 0;
+
+  while ($b = sysread $source,$_,$bs) {
+    print {$destination} $_ or die "$0: link failure - $!\n";
+    $B += $b;
+    $bt += $b;
+    $t2 = time;
+    if ($t2>$t1) {
+      if ($opt_g) {
+        if ($B>2*M) {
+          printf STDERR "%d MB %d kB/s        \r",
+            int($B/M),int($bt/k/($t2-$tt));
+        } else {
+          printf STDERR "%d kB %d kB/s        \r",
+            int($B/k),int($bt/k/($t2-$tt));
+        }
+      }
+      $t1 = $t2;
+      if ($t2-$tt>10) {
+        sleep 1; # be nice to bandwith
+        $bt = 0;
+        $tt = $t2;
+      }
+    }
+  }
+
+  die "$0: no stream data\n" unless $B;
+  
+  $tt = (time-$t0)||1;
+  
+  if ($opt_v or $opt_g) {
+    if ($B>2097152) {
+      printf STDERR "transfered: %d MB in %d s with %d kB/s\n",
+        int($B/1048576),$tt,int($B/1024/$tt);
+    } elsif($B>2048) {
+      printf STDERR "transfered: %d kB in %d s with %d kB/s\n",
+        int($B/1024),$tt,int($B/1024/$tt);
+    } else {
+      printf STDERR "transfered: %d B in %d s with %d kB/s\n",
+        $B,$tt,int($B/1024/$tt);
+    }
+  }
+  
+}
+
+
+sub request {
+  my $req = shift;
+  
+  print STDERR "==> $req\n" if $opt_v;
+  syswrite $SH,"$req\r\n\r\n";
+  for (;;) {
+    unless (defined($_ = &getline)) {
+      die "$0: server has closed the connection\n";
+    }
+    if (/^HTTP\/[\d\.]+ 200/) {
+      print STDERR "<== $_" if $opt_v;
+      last;
+    } elsif (/^HTTP\/[\d\.]+ 199/) {
+      print STDERR "<== $_" if $opt_v;
+    } else {
+      if ($opt_v) {
+        print STDERR "<== $_";
+        exit 3;
+      } else {
+        s:^HTTP/[ \d\.]+::;
+        s/\r//;
+        die "$0: server response: $_";
+      }
+    }
+  }
+  while (defined($_ = &getline)) {
+    last if /^\s*$/;
+    $H{uc($1)} = $2 if /(.+):\s*(.+)/;
+    print STDERR "<== $_" if $opt_v;
+  }
+}
+
+# check for (mutt) alias
+sub checkalias {
+  my $to = shift;
+  if ($to !~ /@/ and open F,$ENV{HOME}.'/.mutt/aliases') {
+    while (<F>) {
+      next if /,/;
+      if (/^alias $to\s/i) {
+        chomp;
+        s/\s*#.*//;
+        s/\s+$//;
+        s/.*\s+//;
+        s/<//;
+        s/>//;
+        $to = $_;
+        warn "$0: found alias, using address $to\n";
+        die unless $to;
+        last;
+      }
+    }
+    close F;
+  }
+  return $to;
+}
+
+sub despace {
+  foreach (@_) {
+    s/^\s+//;
+    s/\s+$//;
+  }
+}
+
+sub query_sid {
+  my ($server,$port,$id) = @_;
+  my $req;
+  local $_;
+  
+  $req = "GET SID HTTP/1.1";
+  print STDERR "==> $req\n" if $opt_v;
+  syswrite $SH,"$req\r\n\r\n";
+  $_ = &getline;
+  unless (defined $_ and /\w/) { 
+    print STDERR "\n" if $opt_v;
+    die "$0: no response from server\n";
+  }
+  s/\r//;
+  if (/^HTTP.* 201 (.+)/) {
+    print STDERR "<== $_" if $opt_v;
+    $id = 'MD5H:'.md5_hex($id.$1);
+    while (defined($_ = &getline)) { 
+      s/\r//;
+      last if /^\n/;
+      print STDERR "<== $_" if $opt_v;
+    }
+  } else {
+    die "$0: $server does not support session ID\n";
+  }
+  return $id;
+}
+
+sub sigpipehandler { 
+  local $_ = '';
+  $SIG{ALRM} = sub { };
+  alarm(1);
+  $_ = &getline||'';
+  if (/^HTTP.* \d+ (.*)/) {
+    if ($opt_v) {
+      die "\n$0: server error: @_\n";
+    } else {
+      die "\n$0: server error: $1\n";
+    }
+  } else {
+    die "\n$0: got SIGPIPE (server closed connection)\n";
+  }
+}
+
+# read one text line from $SH;
+sub getline {
+  my $line = '';
+  my $c;
+
+  local $SIG{ALRM} = sub { die "$0: timeout while waiting for server reply\n" };
+  alarm($opt_t||300);
+  
+  # must use sysread to avoid perl line buffering
+  while (sysread $SH,$c,1) {
+    $line .= $c;
+    last if $c eq "\n";
+  }
+  
+  alarm(0);
+  
+  return $line;
+}
+
+# from MIME::Base64::Perl
+sub decode_b64 {
+  local $_ = shift;
+  my $uu = '';
+  my ($i,$l);
+  
+  tr|A-Za-z0-9+=/||cd;
+  s/=+$//;
+  tr|A-Za-z0-9+/| -_|;
+  return "" unless length;
+
+  $l = (length) - 60;
+  for ($i = 0; $i <= $l; $i += 60) {
+    $uu .= "M" . substr($_,$i,60);
+  }
+  $_ = substr($_,$i);
+  if (length) {
+    $uu .= chr(32 + (length)*3/4) . $_;
+  }
+  return unpack ("u",$uu);
+}
+
+
+### common functions ###
+
+
+sub mtime {
+  my @d = localtime((stat shift)[9]);
+  return sprintf('%d%02d%02d',$d[5]+1900,$d[4]+1,$d[3]);
+}
+
+
+sub urldecode {
+  local $_ = shift;
+  s/\%([a-f\d]{2})/chr(hex($1))/ige;
+  return $_;
+}
+
+
+sub get_ssl_env {
+  # set SSL/TLS options
+  $SSL{SSL_verify_mode} = $ENV{SSLVERIFY} if defined($ENV{SSLVERIFY});
+  foreach my $opt (qw(
+    SSL_version
+    SSL_cipher_list 
+    SSL_verify_mode 
+    SSL_ca_path 
+    SSL_ca_file)
+  ) {
+    my $env = uc($opt);
+    $env =~ s/_//g;
+    $SSL{$opt} = $ENV{$env} if defined($ENV{$env});
+  }
+
+  if ($SSL{SSL_verify_mode}) {
+    &search_ca;
+    unless ($SSL{SSL_ca_path} or $SSL{SSL_ca_file}) {
+      die "$0: \$SSLVERIFYMODE, but not valid \$SSLCAPATH or \$SSLCAFILE\n";
+    }
+  } elsif (defined($SSL{SSL_verify_mode})) {
+    # user has set SSLVERIFY=0 !
+  } else {
+    &search_ca;
+    $SSL{SSL_verify_mode} = 1 if $SSL{SSL_ca_path} or $SSL{SSL_ca_file};
+  }
+}
+
+sub search_ca {
+  local $_;
+  return if $SSL{SSL_ca_file} or $SSL{SSL_ca_path};
+  foreach (qw(/etc/ssl/certs/ca-certificates.crt)) {
+    if (-f) {
+      $SSL{SSL_ca_file} = $_;
+      return;
+    }
+  }
+  foreach (qw(/etc/ssl/certs /etc/pki/tls/certs)) {
+    if (-f) {
+      $SSL{SSL_ca_path} = $_;
+      return;
+    }
+  }
+}
+
+
+sub serverconnect {
+  my ($server,$port) = @_;
+  my $connect = "CONNECT $server:$port HTTP/1.1";
+  local $_;
+  
+  if ($opt_v and $port == 443 and %SSL) {
+    foreach my $v (keys %SSL) {
+      printf "%s => %s\n",$v,$SSL{$v};
+    }
+  }
+  
+  if ($proxy) {
+    tcpconnect(split(':',$proxy));
+    if ($port == 443) {
+      printf "--> %s\n",$connect if $opt_v;
+      nvtsend($connect,"");
+      $_ = <$SH>;
+      s/\r//;
+      printf "<-- $_"if $opt_v;
+      unless (/^HTTP.1.. 200/) {
+        die "$0: proxy error : $_";
+      }
+      eval "use IO::Socket::SSL";
+      die "$0: cannot load IO::Socket::SSL\n" if $@;
+      $SH = IO::Socket::SSL->start_SSL($SH,%SSL);
+    }
+  } else {
+    tcpconnect($server,$port);
+  }
+#  if ($port == 443 and $opt_v) {
+#    printf "%s\n",$SH->get_cipher();
+#  }
+}
+
+
+# set up tcp/ip connection
+sub tcpconnect {
+  my ($server,$port) = @_;
+  
+  if ($SH) {
+    close $SH;
+    undef $SH;
+  }
+  
+  if ($port == 443) {
+    # eval "use IO::Socket::SSL qw(debug3)";
+    eval "use IO::Socket::SSL";
+    die "$0: cannot load IO::Socket::SSL\n" if $@;
+    $SH = IO::Socket::SSL->new(
+      PeerAddr => $server,
+      PeerPort => $port,
+      Proto    => 'tcp',
+      %SSL
+    );
+  } else {
+    $SH = IO::Socket::INET->new(
+      PeerAddr => $server,
+      PeerPort => $port,
+      Proto    => 'tcp',
+    );
+  }
+  
+  if ($SH) {
+    autoflush $SH 1;
+  } else {
+    die "$0: cannot connect $server:$port - $@\n";
+  }
+  
+  print "TCPCONNECT to $server:$port\n" if $opt_v;
+}
+
+
+sub sendheader {
+  my $sp = shift;
+  my @head = @_;
+  my $head;
+  
+  push @head,"Host: $sp";
+  
+  foreach $head (@head) {
+    print "--> $head\n" if $opt_v;
+    print {$SH} $head,"\r\n";
+  }
+  print "-->\n" if $opt_v;
+  print {$SH} "\r\n";
+}
+
+
+sub nvtsend {
+  local $SIG{PIPE} = sub { $sigpipe = "@_" };
+  
+  $sigpipe = '';
+  
+  die "$0: internal error: no active network handle\n" unless $SH;
+  die "$0: remote host has closed the link\n" unless $SH->connected;
+  
+  foreach my $line (@_) {
+    print {$SH} $line,"\r\n";
+    if ($sigpipe) {
+      undef $SH;
+      return 0;
+    }
+  }
+  
+  return 1;
+}
+
+
+# from MIME::Base64::Perl
+sub encode_b64 {
+  my $res = "";
+  my $eol = "\n";
+  my $padding;
+  
+  pos($_[0]) = 0;
+  $res = join '',map(pack('u',$_)=~ /^.(\S*)/, ($_[0]=~/(.{1,45})/gs));
+  $res =~ tr|` -_|AA-Za-z0-9+/|;
+  $padding = (3-length($_[0])%3)%3;
+  $res =~ s/.{$padding}$/'=' x $padding/e if $padding;
+  return $res;
+}
diff --git a/htdocs/dynamic.html b/htdocs/dynamic.html
new file mode 100644 (file)
index 0000000..ba9391a
--- /dev/null
@@ -0,0 +1,35 @@
+<html>
+<ul>
+<li> <<our $y = 1>>
+<li> <<$y+1>>
+<li> <<print "3">>
+</ul>
+<hr>
+<pre>
+<<`date +"%Y-%m-%d %H:%M:%S"`>>
+</pre>
+<hr>
+<p>
+
+#if $y>2
+  <h1>xxx</h1>
+#elseif 0
+  <h1>000</h1>
+#elseif 4>3
+  <h1>###############</h1>
+#else
+  <h1>zzz</h1>
+#endif
+<p><hr><p>
+<pre>
+$PATH=$PATH$
+URL parameter=$QUERY_STRING$
+</pre>
+<p><hr><p>
+
+<pre>
+<<`env`>>
+</pre>
+<p><hr><p>
+see <a href="<<local $_="$ENV{REQUEST_URI}?";s/\?.*/!/;print>>">sourcecode</a>
+</html>
diff --git a/htdocs/favicon.ico b/htdocs/favicon.ico
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/htdocs/features.html b/htdocs/features.html
new file mode 100644 (file)
index 0000000..a68df17
--- /dev/null
@@ -0,0 +1,69 @@
+<HTML> \r
+<HEAD><TITLE>F*EX feature list</TITLE></HEAD>\r
+<BODY>\r
+<h1><a href="/">F*EX</a> feature list</h1>\r
+<ul>\r
+  <li>file transfer of virtually unlimited file size\r
+  <li>recipient and sender only need an e-mail program and a web browser -\r
+      of any kind, they do not have to install any software<br>\r
+      (the F*EX server itself is UNIX based)\r
+  <li>RESEND and REGET for resuming after link failures at last sent byte\r
+  <li>auto-notification of recipient\r
+  <li>auto-deletion after download\r
+  <li>auto-deletion after expiration date (default: 5 days)\r
+  <li>full-users can create one time upload URLs for foreign users\r
+  <li>full-users can create sub-users, who can send only to this full-user\r
+  <li>full-users can create groups, an analogy to mailing lists, but for files\r
+  <li>admin can allow (internal or external) user self-registration\r
+  <li>admin can allow upload to public recipients without authentification\r
+  <li>admin can allow upload for LAN users without registration \r
+      (<a href="/usecases/anonymous.html">anonymous upload</a>)\r
+  <li>user can forward a file to a second recipient without re-uploading it\r
+  <li>user can forward a received file without download\r
+  <li>adminstration by CLI or Web\r
+  <li>server based user address books\r
+  <li>user and admin can throttle bandwith usage\r
+  <li>admin can restrict access based on e-mail or ip addresses \r
+  <li>sending to multiple recipients needs storage on the server only once\r
+  <li>quotas for sending and receiving\r
+  <li>support for named based virtual hosts \r
+      (multiple server instances with different hostnames on same IP)\r
+  <li>support for streams, including streaming recursive file transfer\r
+  <li>support for file linking (upload just a link, not the file itself)\r
+  <li>integrated web server with special features:\r
+      <ul>\r
+        <li>HTML with inline perl code\r
+        <li>HTML with conditional if..then..elseif..end blocks\r
+        <li>HTML with include statement\r
+        <li>on-the-fly zip, tar and tgz streaming output \r
+        <li>(restricted) directory file viewer\r
+      </ul>\r
+  <li>for real UNIX users, there are the shell programs \r
+       <a href="/fstools/fexsend.html">fexsend</a> and\r
+       <a href="/fstools/fexget.html">fexget</a><br>\r
+       to avoid annoying web browser usage and with a lot of additional\r
+       features,<br>\r
+       also there is an <a href="/usecases/xx.html">Internet clipboard</a>\r
+       and <a href="/usecases/anonymous.html">anonymous usage</a>\r
+  <li><a href="/fex.html">protocol and source-code free available</a>\r
+  <li>localization for <a href="http://fex.rus.uni-stuttgart.de:8080/">\r
+      many languanges</a> available\r
+  <li>optional authentification by LDAP, RADIUS, POP, IMAP, mailman\r
+  <li>server available for UNIX and Windows hosts\r
+  <li>about 10 times faster than apache\r
+  <li><b>very</b> low memory usage\r
+  <li>(reverse) proxy support\r
+  <li>F*EX is a HTTP web-service and needs no firewall-tunnels\r
+  <li>works with NAT or DHCP clients, too\r
+  <li><a href="/usecases/BIGMAIL.html">postfix filter</a> available to send\r
+      e-mails with attachments of <b>any</b> size\r
+  <li>maintenance-free: no admin interaction necessary \r
+  <li>minimal software & hardware requirements for the server\r
+  <li>no external database necessary, but possible (LDAP)\r
+  <li>full IPv6 support\r
+  <li>easy server installation, no installation necessary on client side\r
+  <li>great <a href="/FAQ/">FAQ</a>, <a href="/usecases/">use cases</a> \r
+      and detailed <a href="/doc/concept">internal documentation</a>\r
+</ul>\r
+</BODY>\r
+</HTML> \r
diff --git a/htdocs/fup_template.html b/htdocs/fup_template.html
new file mode 100644 (file)
index 0000000..7d9fd30
--- /dev/null
@@ -0,0 +1,52 @@
+<html>
+<head>
+  <meta http-equiv="expires" content="0">
+  <title>F*EX upload template</title>
+</head>
+<body>
+<h1><a href="/">F*EX</a> upload template</h1>
+<p><hr><p>
+<script type="text/javascript">
+  function showstatus() {
+    var file = document.forms["upload"].elements["file"].value;
+    if (file != "") {
+      window.open(
+        '$PROTO$://$HTTP_HOST$/fup?showstatus=$RANDOM$',
+        'fup_status',
+        'width=700,height=500'
+      );
+      return true;
+    } else {
+      return false;
+    }
+  }
+</script>
+<form name="upload"
+      action="/fup"
+      method="post" 
+      accept-charset="UTF-8" 
+      enctype="multipart/form-data" 
+      onsubmit="return showstatus();">
+  <input type="hidden" name="uid" value="$RANDOM$">
+  <table>
+    <tr><td>sender:      <td><input type="text"     name="from"    size="80" value="">(e-mail address)</tr>
+    <tr><td>auth-ID:     <td><input type="password" name="id"      size="16" value=""></tr>
+    <tr><td>recipient(s):<td><input type="text"     name="to"      size="80" value="">(e-mail address)</tr>
+    <tr><td>comment:     <td><input type="text"     name="comment" size="80" value="">(optional)</tr>
+    <tr><td>file name:   <td><input type="file"     name="file"    size="80" value=""></tr>
+  </table> 
+  <p>
+  <input type="submit" value="submit">
+</form>
+<p>
+<p><hr><p>
+After "submit" you will see an upload progress bar 
+(if you have javascript enabled and popups allowed).
+<p>
+<em>NOTE: Most web browsers cannot upload files > 2 GB!</em><br>
+If your file is greater you have to use a special <a href="tools.html">F*EX client</a>.<br>
+You also need one for resuming interrupted uploads. Your web browser cannot do this.
+<p><hr><p>
+<address>Contact: <a href="mailto:$SERVER_ADMIN$">fexmaster</a></address>
+</body>
+</html>
diff --git a/htdocs/index.html b/htdocs/index.html
new file mode 100644 (file)
index 0000000..548e82a
--- /dev/null
@@ -0,0 +1,140 @@
+<HTML> 
+<HEAD><TITLE>F*EX - File EXchange</TITLE></HEAD>
+<BODY>
+<center>
+<h1>F*EX - Frams' Fast File EXchange</h1>
+<IMG SRC="logo.jpg">
+</center>
+<p>
+F*EX (Frams' Fast File EXchange) is a service to send big (large, huge,
+giant, ...) files from a user A to a user B.
+<p>
+The sender uploads the file to the F*EX server using a 
+<a href="/fup">WWW upload form</a> and the recipient
+automatically gets a notification e-mail with a download-URL. 
+<p>
+
+You say:
+<blockquote><em>
+  Why do I need another service for file transfer?!<br>
+  I have e-mail, ftp, ssh and even sendfile!
+</em></blockquote>
+<p>
+I say:
+<blockquote><em>
+  You still need F*EX :-)
+</em></blockquote>
+<p>
+  
+For example, you want to send your friend your last holiday video (1 GB).
+You have the following possibilities (and problems):<p>
+
+<ul>
+  <li><h3>sending a DVD by postal service</h3><p>
+      Out of the question - we live in the year 
+      <<my @x=gmtime(time); $x[5]-69;>> 
+      after invention of the internet!
+      Sending media (hardware) is for grandpas.
+      <p>
+  <li><h3>using e-mail</h3><p>
+      Most e-mail servers have a limit of 10 MB per e-mail and a
+      storage-quota of a few 100 MB per user or even less.
+      <p>
+  <li><h3>uucp</h3><p>
+      Why not cuneiform writing on stone plates?
+      <p>
+  <li><h3>ssh</h3><p>
+      You have your friends password or you are willing to give him yours
+      - just for a file transfer?
+      <p>
+  <li><h3>ftp</h3><p>
+      <ul>
+        <li>Using which ftp server?
+        <li>Using which account, which password?
+        <li>You do not mind sending unencrypted passwords and files over the insecure internet?
+        <li>Your proxy supports passive-ftp?
+        <li>In case of anonymous ftp:
+          <ul>
+            <li>Does it allow 1 GB uploads?
+            <li>Does it allow to delete the upload afterwards?
+            <li>Who else can download your file?
+            <li>Who else can delete your file?
+            <li>You have to send your friend the download-URL, he has to
+                inform you about the successful download, you have to delete 
+                it afterwards.<br>
+                All in all: a pain in the ass.
+          </ul>
+      </ul>
+      <p>
+  <li><h3>http</h3><p>
+      <ul>
+        <li>No default upload and user management - must be programmed
+        <li>No auto-notification
+        <li>No auto-deletion
+        <li>Very few http servers can handle files greater than 2 GB
+      </ul>
+      <p>
+  <li><h3><a href ="http://fex.rus.uni-stuttgart.de/saft/">
+          sendfile</a></h3><p>
+      <ul>
+        <li>You run UNIX and have sendfile installed?
+        <li>Your recipient runs UNIX and has sendfile installed?
+        <li>Neither you nor your recipient has a firewall blocking port 487?
+      </ul>
+      <p>
+  <li><h3>commercial services like DropLoad, ALLPeers, YouSendIt, etc</h3><p>
+      <ul>
+        <li>What limit do they have (most: &lt; 2 GB)?
+        <li>What about security and privacy: will your files be save and secure?
+        <li>Are they open source based or closed proprietary?
+        <li>Are they accessible with any browser or do you need java, active-X, flash or other evil plugins?
+        <li>Will they exist longer than a few months?<br>
+           (DropLoad, ALLPeers and drop.io already have terminated their business)
+      </ul>
+      <p>
+</ul>
+<p>
+If you have answered only once "no" to the questions above, then you need
+F*EX.
+<p>
+<h2>Main features of F*EX</h2>
+<p>
+<ul>
+  <li>file transfer of virtually unlimited file size
+  <li>recipient and sender only need an e-mail program and a web browser -
+      of any kind, they do not have to install any software
+  <li>RESEND and REGET for resuming after link failures at last sent byte
+  <li>auto-notification of recipient
+  <li>auto-deletion after download
+  <li>auto-deletion after expiration date (default: <<$keep>> days)
+  <li>full-users can create one time upload URLs for foreign users
+  <li>full-users can create sub-users, who can send only to this full-user
+  <li>full-users can create groups, an analogy to mailing lists, but for files
+  <li>maintenance-free: no admin interaction necessary besides creating new F*EX accounts
+  <li>Sending to multiple recipients needs storage on the server only once
+  <li>F*EX is a HTTP web-service and needs no firewall-tunnels
+  <li>support for streams, too
+  <li>for real UNIX users, there are the shell programs fexsend and fexget to
+      avoid annoying web browser usage
+  <li>protocol and source-code free available
+</ul>
+
+<h2>Let's talk about SEX</h2>
+F*EX has a companion: Stream EXchange (SEX).<p>
+You can imagine SEX as network wide UNIX pipes with a relay between.
+This can be useful for piping data from user A to user B where A and B cannot
+establish a direct connection, but both can connect by HTTP to the
+SEX server. For seamless integration into the UNIX tool chain, there are
+the shell-tools sexsend and sexget.
+<p>
+Authentication is the same as with F*EX.
+
+<h2>Still questions?</h2>
+See the <a href="/FAQ/FAQ.html">FAQ</a>
+and the <a href="http://fex.rus.uni-stuttgart.de/usecases/">use cases</a>.
+
+<p>
+<ADDRESS>contact: <A HREF="mailto:$SERVER_ADMIN$">fexmaster</A></ADDRESS>
+
+</BODY>
+</HTML> 
diff --git a/htdocs/logo.jpg b/htdocs/logo.jpg
new file mode 100644 (file)
index 0000000..4aeaa63
Binary files /dev/null and b/htdocs/logo.jpg differ
diff --git a/htdocs/robots.txt b/htdocs/robots.txt
new file mode 100644 (file)
index 0000000..1f53798
--- /dev/null
@@ -0,0 +1,2 @@
+User-agent: *
+Disallow: /
diff --git a/htdocs/small_logo.jpg b/htdocs/small_logo.jpg
new file mode 100644 (file)
index 0000000..7b2f738
Binary files /dev/null and b/htdocs/small_logo.jpg differ
diff --git a/htdocs/sup.html b/htdocs/sup.html
new file mode 100644 (file)
index 0000000..23cffb1
--- /dev/null
@@ -0,0 +1,61 @@
+<html>
+<head>
+  <meta http-equiv="expires" content="0">
+  <title>F*EX simple upload</title>
+</head>
+<body>
+<h1><a href="/">F*EX</a> simple upload</h1>
+<p><hr><p>
+<script type="text/javascript">
+  function showstatus() {
+    var file = document.forms["upload"].elements["file"].value;
+    if (file != "") {
+      window.open(
+        '$PROTO$://$HTTP_HOST$/fup?showstatus=$RANDOM$',
+        'fup_status',
+        'width=700,height=500'
+      );
+      return true;
+    } else {
+      return false;
+    }
+  }
+</script>
+<form name="upload"
+      action="/fup"
+      method="post" 
+      accept-charset="UTF-8" 
+      enctype="multipart/form-data" 
+      onsubmit="return showstatus();">
+  <input type="hidden" name="uid"     value="$RANDOM$">
+  <input type="hidden" name="comment" value="!SHORTMAIL!">
+  <table>
+    <tr><td>your e-mail address:
+        <td><input type="text"     name="from" size="80" value="">
+    </tr>
+    <tr><td>your auth-ID:   
+        <td><input type="password" name="id"   size="16" value="">
+    </tr>
+    <tr><td>your file:
+        <td><input type="file"     name="file" size="80" value="">
+    </tr>
+  </table> 
+  <p>
+  <input type="submit" value="submit">
+</form>
+<p>
+<p><hr><p>
+After "submit" you will see an upload progress bar 
+(if you have javascript enabled and popups allowed).
+<p>
+If you have lost your auth-ID use "?" as auth-ID and select a small dummy file.
+Your auth-ID will be sent by e-mail to you.
+<p>
+You can also use the <a href="/fup">regular upload form</a> 
+(with more features).
+<p>
+<em>NOTE: Only Firefox or Google Chrome can upload files > 2 GB!</em><br>
+<p><hr><p>
+<address>Contact: <a href="mailto:$SERVER_ADMIN$">fexmaster</a></address>
+</body>
+</html>
diff --git a/htdocs/tools.html b/htdocs/tools.html
new file mode 100644 (file)
index 0000000..fd29c2c
--- /dev/null
@@ -0,0 +1,40 @@
+<HTML> 
+<HEAD><TITLE>F*EX tools</TITLE></HEAD>
+<BODY>
+<center></center>
+<h1> <a href="/">F*EX</a> tools</h1>
+
+<table border=1>
+<tr><td><a href="/download/fexsend">fexsend</a>
+    <td>UNIX CLI client for sending files (with many 
+        <a href="http://fex.rus.uni-stuttgart.de/fstools/fexsend.html">
+        additional features</a>)</tr>
+<tr><td><a href="/download/fexget">fexget</a>
+    <td>UNIX CLI client for receiving files (with many 
+        <a href="http://fex.rus.uni-stuttgart.de/fstools/fexget.html">
+        additional features</a>)</tr>
+<tr><td><a href="http://fex.rus.uni-stuttgart.de/download/fexget.exe">fexget</a>
+    <td>Windows CLI client for receiving files
+<tr><td><a href="/download/sex.tar">sexsend, sexget</a>
+    <td>UNIX CLI clients for sending and receiving streams</tr>
+<tr><td><a href="http://fex.rus.uni-stuttgart.de/download/schwuppdiwupp.exe">schwuppdiwupp</a>
+    <td>Windows GUI client for sending files</tr>
+<tr><td><a href="http://fex.rus.uni-stuttgart.de/download/macschwupp.tar">schwuppdiwupp</a>
+    <td>Macintosh GUI client for sending files</tr>
+</table>
+<p>
+In opposite to most web browsers all these clients can handle files
+greater than 2 GB and are able to resume interrupted up/downloads.
+<p>
+Hint for UNIX users: 
+<pre>  wget -qO- http://$HTTP_HOST$/xx.tar | tar xvf -</pre>
+installs fexsend fexget and
+<a href="http://fex.rus.uni-stuttgart.de/usecases/anonymous.html">xx</a>.
+<pre>  wget -qO- http://$HTTP_HOST$/afs.tar | tar xvf -</pre>
+also installs the client programs for
+<a href="/SEX.html">Stream EXchange</a>
+and
+<a href="http://fex.rus.uni-stuttgart.de/usecases/anonymous.html">anonymous usage</a>.
+
+</BODY>
+</HTML>
diff --git a/htdocs/version b/htdocs/version
new file mode 100644 (file)
index 0000000..733bf4c
--- /dev/null
@@ -0,0 +1 @@
+fex-20150120
diff --git a/install b/install
new file mode 100755 (executable)
index 0000000..7ffdfc0
--- /dev/null
+++ b/install
@@ -0,0 +1,514 @@
+#!/usr/bin/perl -w
+
+use 5.006;
+use Getopt::Std;
+use File::Basename;
+use Socket;
+use IO::Socket::INET;
+use Digest::MD5        'md5_hex';
+
+$ENV{PATH} .= ':/sbin:/usr/sbin';
+
+$usage = "usage: $0 [-p port] [IP-address]\n";
+$xinetd = '/etc/xinetd.d/fex';
+
+umask 022;
+
+if ($<) {
+  die "you must be root to install F*EX\n";
+}
+
+goto INSTALL if $0 =~ /upgrade$/;
+
+$fex = 'fex.rus.uni-stuttgart.de';
+if (system("host $fex >/dev/null") != 0) {
+  die "host $fex is not resolvable - check /etc/resolv.conf\n";
+}
+
+if (`uname` =~ /^SunOS/) {
+  die "Solaris is currently not supported. "
+     ."Please contact framstag\@rus.uni-stuttgart.de for details.\n";
+}
+
+$opt_p = 80;
+
+if (open $xinetd,$xinetd) {
+  while (<$xinetd>) {
+    if (/^\s*port\s*=\s*(\d+)/) {
+      $opt_p = $fexport = $1;
+    }
+    if (/^\s*bind\s*=\s*([\d.]+)/) {
+      $fexip = $ip = $1;
+    }
+  }
+  close $xinetd;
+}
+
+getopts('p:') or die $usage;
+
+$arg = shift;
+if ($arg and -f "locale/$arg/lib/fup.pl") {
+  exec 'locale/translate',$arg;
+} else {
+  $ip = $arg || $fexip || 0;
+}
+
+
+# if (not $ip and open P,"ifconfig 2>/dev/null |") {
+if (not $ip and open P,'host $(hostname)|') {
+  $guessed_ip = 0;
+  while (<P>) {
+    if (/(\d+\.\d+\.\d+\.\d+)/) { 
+      $guessed_ip = $1;
+      last;
+    }
+  }
+  close P;
+  print "Your IP [$guessed_ip] : ";
+  chomp($ip = <STDIN>);
+  $ip ||= $guessed_ip;
+}
+
+$ip =~ /^\d+\.\d+\.\d+\.\d+$/ or die $usage;
+
+($hostname) = gethostbyaddr(gethostbyname($ip),AF_INET);
+die "cannot find hostname for IP $ip\n" unless $hostname;
+
+print "checking prerequisites\n";
+
+if (`which xinetd` =~ m{^/}) {
+  print "found xinetd\n";
+} else {
+  print "xinetd executable NOT found\n";
+  $premiss++;
+}
+
+foreach (qw'/usr/lib/sendmail /usr/sbin/sendmail') {
+  if (-x) {
+    $sendmail = $_;
+    print "found $sendmail\n";
+    last;
+  }
+}
+unless ($sendmail) {
+  print "sendmail NOT found\n";
+  $premiss++;  
+}
+
+if ($premiss) {
+  print "installation aborted, nothing has been touched yet\n";
+  print "what now? ==> see doc/installation\n";
+  exit 1;
+}
+
+unless ($fexport) {
+  
+  $SH = IO::Socket::INET->new(
+    PeerAddr => $ip,
+    PeerPort => $opt_p,
+    Proto    => 'tcp',
+  );
+  
+  if ($SH) {
+    print "There is already a tcp-service running on $ip:$opt_p !\n";
+    print "Select another port for F*EX by running $0 -p OTHERPORT $ip\n";
+    print "or an alternative IP-address by running $0 OTHERADDRESS\n";
+    exit 5;
+  }
+}
+
+print "prerequisites checked, ok\n";
+
+unless (getpwnam('fex')) {
+  print "creating user fex\n";
+  system 'useradd -s /bin/bash -c "File EXchange" -m fex';
+  exit $? if $?;
+}
+
+if (open F,'/etc/passwd') {
+  while (<F>) {
+    $fexbash = $_ if /^fex:.*\/bash/;
+  }
+  close F;
+}
+unless ($fexbash) {
+  die "no bash login shell for user fex\n";
+}
+
+INSTALL:
+
+umask 077;
+
+@FEX = getpwnam('fex') or die "no user fex\n";
+$FEXHOME  = $FEX[7];
+
+die "no HOME directory for user fex\n" unless -d $FEXHOME;
+
+print "Installing:\n";
+
+@save = (
+  "lib/fex.ph",
+  "lib/fup.pl",
+  "lib/reactivation.txt",
+  "etc/mime.types",
+  "htdocs/index.html",
+  "htdocs/robots.txt",
+  "htdocs/FAQ/local.faq",
+);
+
+foreach $s (@save) {
+  $f = "$FEXHOME/$s";
+  if (-e $f) {
+    $fs = $f.'_save';
+    rename $f,$fs and print "$f --> $fs\n";
+  }
+}
+
+cpav(qw'bin cgi-bin lib etc htdocs doc',$FEXHOME);
+unlink "$FEXHOME/doc/License";
+unlink "$FEXHOME/htdocs/License";
+
+$hl = "$FEXHOME/htdocs/locale";
+unless (-d $hl) { mkdir $hl or die "$0: cannot mkdir $hl - $!\n" }
+
+if  (-d "$FEXHOME/spool") {
+  warn "checking spool ...\n";
+  &convert_spool;
+  system "chown -R fex $spooldir/";
+} else {
+  $newinstall = $FEXHOME;
+  chmod 0700,$FEXHOME;
+  mkdir "$FEXHOME/spool",0700 or die "cannot mkdir $FEXHOME/spool - $!\n";
+  mkdir "$FEXHOME/spool/.error",0700;
+  system "chown -R fex $FEXHOME/spool";
+}
+
+foreach $s (@save) {
+  $f = "$FEXHOME/$s";
+  $fs = $f.'_save';
+  $fn = $f.'_new';
+  if (-e $fs) {
+    system "rm -rf $fn";
+    rename $f,$fn and print "$f --> $fn\n";
+    rename $fs,$f and print "$fs --> $f\n";
+  }
+}
+
+system(qw'perl -p -i -e',
+  's:href="/?FAQ.html":href="/FAQ/FAQ.html":',
+  "$FEXHOME/lib/fup.pl"
+);
+  
+$fph = "$FEXHOME/lib/fex.ph";
+open $fph,$fph or die "cannot read $fph - $!\n";
+while (<$fph>) {
+  s/'MYHOSTNAME.MYDOMAIN'/'$hostname'/;
+  $conf .= $_;
+}
+close $fph;
+
+eval $conf;
+$spooldir ||= "$FEXHOME/spool";
+
+$fid = "$FEXHOME/.fex/id";
+$aa = "$spooldir/$admin/@";
+
+if ($newinstall) {
+  print "\n";
+  for (;;) {
+    print "Server hostname [$hostname] : ";
+    $_ = <STDIN>;
+    s/\s//g;
+    $hostname = $_ if $_;
+    last if gethostbyname($hostname);
+    print "No DNS for $hostname\n";
+  }
+  for (;;) {
+    print "F*EX admin [$admin] : ";
+    $_ = <STDIN>;
+    s/\s//g;
+    $admin = $_ if $_;
+    last if $admin =~ /.\@./;
+    print "admin must be a valid email address!\n";
+  }
+  while (not $admin_pw) {
+    print "F*EX admin password: ";
+    $admin_pw = <STDIN>;
+    $admin_pw =~ s/\s//g;
+  }
+  mkfid();
+  print "(admin password is in $aa)\n";
+  $conf =~ s/^\s*\$hostname\s*=.*/\$hostname = '$hostname';/m;
+  $conf =~ s/^\s*\$admin\s*=.*/\$admin = '$admin';/m;
+} else {
+  if ($admin_pw) {
+    print "\nFound old \$admin_pw in $fph !\n";
+    print "This is no longer supported for security reason.\n";
+    if (open $aa,$aa) {
+      $_ = <$aa>||'';
+      chomp;
+      close $aa;
+      if ($_ ne $admin_pw) {
+        print "\nYou have to delete \$admin_pw in $fph and run\n";
+        print "$FEXHOME/bin/fac -u $admin $admin_pw\n";
+        print "\nThen rerun $0\n";
+        exit 2;
+      }
+    }
+    mkfid();
+    print "\$admin_pw is transfered to auth-ID in $aa\n\n";
+    $conf =~ s/^\s*(\$admin_pw)\s*=.*/# $1 is now auth_ID of user \$admin/m;
+  }
+}
+
+sub mkfid {
+  my $ad = dirname($aa);
+  mkdir $ad;
+  open $aa,'>',$aa or die "$0: cannot create $aa - $!\n";
+  print {$aa} "$admin_pw\n";
+  close $aa;
+  my $fd = dirname($fid);
+  mkdir $fd;
+  rename $fid,$fid.'_save';
+  open $fid,'>',$fid or die "$0: cannot create $fid - $!\n";
+  print {$fid} "$hostname:$opt_p\n";
+  print {$fid} "$admin\n";
+  print {$fid} "$admin_pw\n";
+  close $fid;
+  chmod 0700,$fd;
+  system "chown -R fex $fd $ad";
+}
+
+open $fph,">$fph.new" or die "$0: cannot write $fph.new - $!\n";
+print {$fph} $conf;
+close $fph;
+system "chown fex $fph.new";
+rename "$fph.new",$fph or die "$0: cannot rename $fph.new to $fph - $!\n"; 
+
+do $fph or die "$0: error in new $fph - $!\n";
+
+rename "locale/deutsch","locale/german"  if -d "locale/deutsch";
+rename "locale/espanol","locale/spanish" if -d "locale/espanol";
+
+if (@locales = glob "locale/*/lib/fup.pl") {
+  foreach (@locales) {
+    m{locale/(.+?)/} and $locale = $1;
+    if (-f "$FEXHOME/$_") { 
+      system 'locale/translate',$locale;
+      system "chown -R fex $FEXHOME/locale/$locale";
+      $hl = "$FEXHOME/htdocs/locale/$locale";
+      symlink "$FEXHOME/locale/$locale/htdocs",$hl unless -l $hl;
+    } else { 
+      push @nlocales,"./install $1\n";
+    }
+  }
+  if (@nlocales) {
+    if (glob "$FEXHOME/locale/*/lib/fup.pl") {
+      print "\nTo install another localized version, type:\n";
+    } else {
+      print "\nTo install a localized version, type:\n";
+    }
+    print @nlocales;
+  }
+}
+
+$fph = "$FEXHOME/lib/fex.ph";
+do $fph;
+
+unless (-f $xinetd) {
+  my $xc = '/etc/xinetd.conf';
+  if (open $xc,$xc) {
+    while (<$xc>) {
+      if (/^\s*only_from/) {
+        print "WARNING: found \"only_from\" in $xc : fexsrv is restricted!\n";
+      }
+    }
+    close $xc;
+  }
+  if (-d '/etc/xinetd.d') {
+    unless (-f $xinetd) {
+      open $xinetd,">$xinetd" or die "cannot write $xinetd - $!\n";
+      open F,'etc/xinetd_fex' or die "cannot read etc/xinetd_fex - $!\n";
+      while (<F>) {
+        s/FEXHOME/$FEXHOME/;
+        s/PORT/$opt_p/;
+        s/ADDRESS/$ip/;
+        print {$xinetd} $_;
+      }
+      close F;
+      close $xinetd;
+      system qw'/etc/init.d/xinetd restart';
+      print "WARNING: cannot restart xinetd\n" if $?;
+    }
+  } else {
+    print "WARNING: No /etc/xinetd.d found.\n";
+    print "WARNING: You have to install etc/xinetd_fex manually.\n";
+  }
+
+  $crontab = `crontab -u fex -l 2>/dev/null`;
+  if ($crontab !~ /fex_cleanup/) {
+    open $crontab,">fex.cron" or die "cannot create fex.cron - $!\n";
+    print {$crontab} $crontab,"\n";
+    print {$crontab} " 3 3 * * * exec $FEXHOME/bin/fex_cleanup\n";
+    close $crontab;
+    system qw(crontab -u fex fex.cron);
+  }
+
+  system "chown -R fex:root $FEXHOME $FEXHOME/spool/";
+  system "chmod -R go-r $FEXHOME/lib $FEXHOME/cgi-bin $FEXHOME/spool/";
+
+  print "\n";
+  print "Now check configuration file $FEXHOME/lib/fex.ph and run\n";
+  print "$FEXHOME/bin/fac for further configuration and user management.\n";
+  print "(You can do this as user \"fex\")\n";
+} else {
+  
+  system "chmod -R go-r $FEXHOME/lib $FEXHOME/cgi-bin";
+  
+  print "\n";
+  print "F*EX update installed.\n";
+  print "You can inform your users about the new features with:\n";
+  print "$FEXHOME/bin/fexwall 'new F*EX features on $hostname' ".
+        "< $FEXHOME/doc/newfeatures\n";
+}
+
+if (@local_rdomains and not @local_rhosts) {
+  print "\nWARNING:\n";
+  print "In $fph you have @local_rdomains but not @local_rhosts!\n";
+  print "Selfregistrating of external users will not work!\n";
+  print "See ${fph}_new/\n";
+}
+
+if (`$sendmail -h 2>&1` =~ /exim/ and 
+    `grep trusted_users /etc/exim4/exim4.conf 2>/dev/null` !~ /\bfex\b/) {
+  print "\nWARNING:\n";
+  print "$sendmail is exim\n";
+  print "You MUST set in your exim4.conf:\n";
+  print "trusted_users = mail : uucp : fex\n";
+}
+exit;
+
+
+sub convert_spool {
+  my ($f,$d,$to,$from,$link);
+  
+  local $) = $FEX[3];
+  local $> = $FEX[2];
+
+  our ($spooldir,$skeydir,$gkeydir);
+  $ENV{FEXLIB} = $FEXLIB = "$FEXHOME/lib";
+  require "$FEXLIB/fex.pp" or die "$0: cannot load $FEXLIB/fex.pp - $!\n";
+
+  # User --> user@maildomain
+  if ($mdomain) {
+    foreach $f (glob "$spooldir/.dkeys/*") {
+      if ($link = readlink $f) {
+        (undef,$to,$from,$file) = split('/',$link);
+        if ($file) {
+          $to   .= '@'.$mdomain if $to   !~ /@/;
+          $from .= '@'.$mdomain if $from !~ /@/;
+          if ($link ne "../$to/$from/$file") {
+            symlink "../$to/$from/$file",$f;
+          }
+        }
+      }
+    }
+  }
+
+  # fix spool layout: FROM and TO must have domains and must be lower case
+  foreach $d ((glob "$spooldir/*/*"),(glob "$spooldir/*")) {
+    if (not -l $d and -d $d and $d =~ m:(.+)/(.+):) {
+      $p = $1;
+      $b = $2;
+      if ($b !~ /^@/ and $b !~ /^[A-Z_-]+$/) {
+        if ($mdomain and $b !~ /@/) {
+          rename $d,sprintf("%s/%s@%s",$p,lc($b),$mdomain);
+        } elsif ($b ne lc($b)) {
+          rename $d,sprintf("%s/%s",$p,lc($b));
+        }
+      }
+    }
+  }
+
+  # split auth-ID and subuser file: @ --> @ @SUBUSER
+  foreach my $u (glob "$spooldir/*@*") {
+    next if -f "$u/\@SUBUSER";
+    open my $idf,"$u/\@" or next;
+    $id = <$idf>;
+    if (defined ($su = <$idf>) and $su =~ /\w/
+        and open my $suf,">$u/\@SUBUSER") {
+      print {$suf} $su;
+      while (defined ($su = <$idf>)) { print {$suf} $su }
+      close $suf;
+      close $idf;
+      if (open my $idf,">$u/\@") {
+        print {$idf} $id;
+        close $idf;
+      }
+    }
+  }
+
+  # create new SKEYs
+  foreach my $sf (glob "$spooldir/*/\@SUBUSER") {
+    $user = (split '/',$sf)[-2];
+    if (open $sf,$sf) {
+      while (<$sf>) {
+        s/#.*//;
+        if (/(.+\@.+):(.+)/) {
+          ($subuser,$id) = ($1,$2);
+          next if $subuser =~ /\*/;
+          $skey = md5_hex("$user:$subuser:$id");
+          if (open $skey,'>',"$skeydir/$skey") {
+            print {$skey} "from=$subuser\n",
+                          "to=$user\n",
+                          "id=$id\n";
+            close $skey;
+          }
+          mkdirp("$spooldir/$subuser/\@MAINUSER");
+          symlink $skey,"$spooldir/$subuser/\@MAINUSER/$user";
+        }
+      }
+    }
+    close $sf;
+  }
+
+  # create new GKEYs
+  foreach my $gf (glob "$spooldir/*/\@GROUP/*") {
+    next unless -f $gf;
+    $group = (split '/',$gf)[-1];
+    $user  = (split '/',$gf)[-3];
+    if (open $gf,$gf) {
+      while (<$gf>) {
+        s/#.*//;
+        if (/(.+\@.+):(.+)/) {
+          ($gm,$id) = ($1,$2);
+          $gkey = md5_hex("$user:$group:$gm:$id");
+          if (open $gkey,'>',"$gkeydir/$gkey") {
+            print {$gkey} "from=$gm\n",
+                          "to=\@$group\n",
+                          "user=$user\n",
+                          "id=$id\n";
+            close $gkey;
+          }
+          mkdirp("$spooldir/$gm/\@GROUP");
+          symlink "../../$user/\@GROUP/$group","$spooldir/$gm/\@GROUP/$group";
+        }
+      }
+    }
+    close $gf;
+  }
+}
+
+sub cpav {
+  my $dd = pop @_;
+  local *P;
+  
+  die "cpav: $dd is not a directory" unless -d $dd;
+  open P,"tar cf - @_ | su -c 'cd $dd; umask 022; tar xvf - 2>&1' fex |" 
+    or die "cpav: cannot tar - $!\n";
+  while (<P>) {
+    chomp;
+    print "$_ --> $dd/$_\n" unless /\/$/;
+  }
+  close P;
+}
diff --git a/lib/dop b/lib/dop
new file mode 100755 (executable)
index 0000000..df9511b
--- /dev/null
+++ b/lib/dop
@@ -0,0 +1,614 @@
+#!/usr/bin/perl -wT
+
+# F*EX document output
+#
+# is a subprogram of fexsrv! do not run it directly!
+#
+# Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
+#
+
+use File::Basename;
+use CGI::Carp  qw(fatalsToBrowser);
+use Fcntl      qw(:flock :seek :mode);
+use POSIX      qw(strftime locale_h);
+use Cwd        qw(getcwd abs_path);
+
+# import from fex.pp
+our ($bs,$tmpdir,@doc_dirs);
+
+my $log = "$logdir/dop.log";
+
+# POSIX time format needed for HTTP header
+setlocale(LC_TIME,'POSIX');
+
+sub dop {
+  my $doc = shift;
+  my $source = shift;
+  my $seek = 0;
+  my $stop = 0;
+  my ($link,$host,$path,$range);
+  
+  our $error = 'F*EX document output ERROR';
+  
+  security_check($doc);
+  
+  # reget?
+  if ($range = $ENV{HTTP_RANGE}) {
+    $seek = $1 if $range =~ /^bytes=(\d+)-/i;
+    $stop = $1 if $range =~ /^bytes=\d*-(\d+)/i;
+  }
+
+  # redirect on relative symlinks without "../" 
+  if ($link = readlink($doc) and 
+      $link !~ m:^/: and $link !~ m:\.\./: and $link !~ /^:.+:$/) {
+    $path = $ENV{REQUEST_URI};
+    $path =~ s:[^/]*$::;
+    $doc = "$path/$link";
+    $doc =~ s:/+:/:g;
+    $doc =~ s:^/::;
+    $host = $ENV{HTTP_HOST} || $hostname;
+    nvt_print(
+      "HTTP/1.1 301 Moved Permanently",
+      "Location: $ENV{PROTO}://$host/$doc",
+      "Content-Length: 0",
+      "Connection: close",
+      ""
+    );
+    &reexec;
+  }
+
+  # watchdog documents
+  if (@wdd and $wdd and grep { $doc =~ /$_/ } @wdd) { &$wdd($doc) }
+
+  my $dir = untaint(getcwd());
+  chdir(dirname($doc));
+  http_output($doc,$seek,$stop);
+  chdir($dir);
+}
+
+sub http_output {
+  my ($file,$seek,$stop) = @_;
+  my ($filename,$files,$streamfile,$size,$total_size);
+  my ($data,$type);
+  my ($var,$env,$con);
+  my @files;
+  my $htmldoc = '';
+  my $htauth;
+  my @s;
+  my $s = 0;
+  my $b = 0;
+  my $http_client = $ENV{HTTP_USER_AGENT} || '';
+  local $_;
+
+  # extra security check: document must not be in lib or spool directory
+  if (path_match($file,$FEXLIB) or path_match($file,$spooldir)) {
+    http_error(403);
+  }
+
+  security_check($file);
+  $htauth = dirname($file).'/.htauth';
+  require_auth($htauth,$file) if -f $htauth;
+
+  if (-f $file) {
+    # normal file
+    open $file,'<',$file or http_error(400);
+    security_check($file);
+  } elsif ($file =~ /(.+)\.gz$/ and -f $1) {
+    @files = ($1);
+    open $file,'-|',qw'gzip -c',@files or http_error(503);
+  } elsif ($file =~ /(.+)\.tgz$/ and -f "$1.tar") {
+    @files = ("$1.tar");
+    open $file,'-|',qw'gzip -c',@files or http_error(503);
+  } elsif ($file =~ /(.+)\.(tar|tgz|zip)$/ and 
+           @s = lstat($streamfile = "$1.stream") and $s[4] == $<)
+  {
+    # streaming file (only if it is owned by user fex)
+    chdir dirname($file);
+    security_check($file);
+    if (-l $streamfile and readlink($streamfile) =~ /^:(.+):$/) {
+      # special symlink pointer file for streaming
+      @files = split(/:/,$1);
+    } elsif (open $streamfile,$streamfile) {
+      # special streaming file
+      while (<$streamfile>) {
+        chomp;
+        if (/^(\/.*):/) {
+          chdir $1;
+          security_check($1);
+        } else {
+          push @files,$_;
+        }
+      }
+    } else {
+      http_error(503);
+    }
+    close $streamfile;
+    foreach (@files) {
+      if (/^\// or /\.\.\//) { 
+        # absolute path or relative path with parent directory is not allowed
+        http_error(403);
+      }
+      if (@s = stat($_) and not($s[2] & S_IRGRP) or not -r $_) { 
+        # file must be readable by user and group
+        http_error(403);
+      }
+    }
+    http_error(416) if $ENV{HTTP_RANGE};
+    close STDERR;
+    if    ($file =~ /\.tar$/) { @a = qw'tar --exclude *~ --exclude .* -cf -' } 
+    elsif ($file =~ /\.tgz$/) { @a = qw'tar --exclude *~ --exclude .* -czf -' }
+    elsif ($file =~ /\.zip$/) { @a = qw'zip -x *~ */.*/* @ -rq -' }
+    else { http_error(400) }
+    open $file,'-|',@a,@files or http_error(503);
+  } else {
+    http_error(404);
+  }
+  
+  $type = 'application/octet-stream';
+  if    ($file =~ /\.html$/)   { $type = 'text/html' } 
+  # elsif ($file =~ /\.txt$/)  { $type = 'text/plain' }
+  elsif ($file =~ /\.css$/)    { $type = 'text/css' }
+  elsif ($file =~ /\.js$/)     { $type = 'text/javascript' }
+  elsif ($file =~ /\.ps$/)     { $type = 'application/postscript' }
+  elsif ($file =~ /\.pdf$/)    { $type = 'application/pdf' }
+  elsif ($file =~ /\.jpg$/)    { $type = 'image/jpeg' }
+  elsif ($file =~ /\.png$/)    { $type = 'image/png' }
+  elsif ($file =~ /\.gif$/)    { $type = 'image/gif' }
+  elsif ($file !~ /\.(tar|tgz|zip|jar|rar|arj|7z|bz2?|gz)$/) {
+    my $qfile = untaint(abs_path($file));
+    $qfile =~ s/([^\/\.\+\w!=,_-])/\\$1/g;
+    $_ = `file $qfile`;
+    if (/HTML/) {
+      $type = 'text/html';
+    } elsif (/text/i and not -x $file) {
+      $type = 'text/plain';
+      if    (/\sASCII\s/)    { $type .= "; charset=us-ascii" }
+      elsif (/(ISO-[\w-]+)/) { $type .= "; charset=".lc($1) }
+      else                   { $type .= "; charset=utf-8" }
+    }
+  }
+
+  # show sourcecode if URL ends with '!'
+  # to avoid this for a HTML file, simple do a: chmod o-r file
+  if ($type eq 'text/html') {
+    if ($htmlsource) {
+      if (@s = stat($file) and $s[2] & S_IROTH) {
+        $type = 'text/plain';
+      } else {
+        http_error(403);
+      }
+    }
+  } elsif ($ENV{'QUERY_STRING'} eq '!') {
+    $type = 'text/plain';
+  }
+      
+  
+  if ($type eq 'text/html') {
+    $seek = $stop = 0;
+    local $^W = 0;
+    local $/;
+    $htmldoc = <$file>;
+    while ($htmldoc =~ s/\n##.*?\n/\n/) {};
+    # evaluate #if ... #else ... #elseif ... #endif blocks
+    my $mark = randstring(16);
+    while ($htmldoc =~ s/\n(#if\s+(.+?)\n.+?\n)#endif/\n$mark/s) {
+      $_ = $1;
+      # if block
+      if (eval $2) {
+        s/#if.*\n//;
+        s/\n#else.*//s;
+        $htmldoc =~ s/$mark/$_/;
+      } else {
+        # elseif blocks
+        while (s/.*?\n#elseif\s+(.+?)\n//s) {
+          if (eval $1) {
+            s/\n#else.*//s;
+            $htmldoc =~ s/$mark/$_/;
+          }
+        }
+        # else block left?
+        if ($htmldoc =~ /$mark/) {
+          s/.*\n#else\s*\n//s or $_ = '';
+          $htmldoc =~ s/$mark/$_/;
+        }
+      }
+    };
+    # evaluate #include
+    while ($htmldoc =~ s/\n#include "(.*?)"/\n$mark/s) {
+      my $file = $1;
+      my $include = '';
+      if (open $file,$file) {
+        $include = <$file>;
+        close $file;
+      }
+      $dynamic = $htmldoc =~ s/$mark/$include/;
+    }
+    # evaluate <<perl-code>>
+    while ($htmldoc =~ /<<(.+?)>>/s) {
+      local $pc = $1;
+      local $__ = '';
+      tie *STDOUT => "Buffer",\$__;
+      $__ .= eval $pc;
+      untie *STDOUT;
+      $dynamic = $htmldoc =~ s/<<(.+?)>>/$__/s;
+    };
+    # substitute $variable$ with value from environment (if present)
+    while ($htmldoc =~ /\$([\w_]+)\$/g) {
+      $var = $1;
+      if (defined($env = $ENV{$var})) {
+        $htmldoc =~ s/\$$var\$/$env/g;
+      }
+    };
+    $total_size = $size = $s = length($htmldoc);
+  } else {
+    if (@files) {
+      $size = 0;
+    } else {
+      $total_size = -s $file || 0;
+      $size = $total_size - $seek - ($stop ? $total_size-$stop-1 : 0);
+    }
+  }
+
+  if ($size < 0) {
+    http_header('416 Requested Range Not Satisfiable');
+    exit;
+  }
+  
+  alarm($timeout*10);
+  
+  if ($seek or $stop) {
+    my $range;
+    if ($stop) {
+      $range = sprintf("bytes %s-%s/%s",$seek,$stop,$total_size);
+    } else {
+      $range = sprintf("bytes %s-%s/%s",$seek,$total_size-1,$total_size);
+    }
+    nvt_print(
+      'HTTP/1.1 206 Partial Content',
+      'Server: fexsrv',
+      "Content-Length: $size",
+      "Content-Range: $range",
+      "Content-Type: $type",
+      '',
+    );
+  } else {
+    # streaming?
+    if (@files) {
+      nvt_print(
+        'HTTP/1.1 200 OK',
+        'Server: fexsrv',
+        "Expires: 0",
+        "Content-Type: $type",
+        '',
+      );
+    } else {
+      # Java (clients) needs Last-Modified header!
+      # if there are locale versions, use actual time for Last-Modified
+      # to enforce reload of page
+      $file =~ m{/htdocs/(.+)};
+      my @lfiles = glob("$FEXHOME/locale/*/htdocs/$1");
+      my $date = ($dynamic or @lfiles > 1) ?
+                 strftime("%a, %d %b %Y %T GMT",gmtime(time)) :
+                 http_date($file);
+      nvt_print(
+        'HTTP/1.1 200 OK',
+        'Server: fexsrv',
+        "Last-Modified: $date",
+        "Expires: 0",
+        "Content-Length: $size",
+        "Content-Type: $type",
+      );
+      nvt_print("Set-Cookie: locale=$locale") if $use_cookies and $locale;
+      nvt_print('');
+    }
+  }
+
+  if ($ENV{REQUEST_METHOD} eq 'GET') {
+    if ($type eq 'text/html') {
+      alarm($timeout*10);
+      print $htmldoc;
+    } else {
+      # binary data # can be stream!
+      seek $file,$seek,0 if $seek;
+      while ($b = read($file,$data,$bs)) {
+        if ($stop and $s+$b > $size) {
+          $b = $size-$s;
+          $data = substr($data,0,$b)
+        }
+        $s += $b;      
+        alarm($timeout*10);
+        print $data or last;
+      }
+    }
+    fdlog($log,$file,$s,$size) if $s;
+  }
+  
+  alarm(0);
+  close $file;
+  exit if @files; # streaming end
+  return $s;
+}
+
+
+# show directory index
+sub showindex {
+  my $dir = shift;
+  my ($htmldoc,$size);
+  my @links = ();
+  my @dirs = ();
+  my @files = ();
+  my $uri = $ENV{REQUEST_URI};
+  my $allowed;
+  my ($htindex,$htauth);
+  local $_;
+  
+  $uri =~ s:/+$::;
+  $dir =~ s:/+$::;
+
+  security_check($dir);
+  
+  $htindex = "$dir/.htindex";
+  $htauth  = "$dir/.htauth";
+  
+  open $htindex,$htindex or http_error(403);
+  require_auth($htauth,$dir) if -f $htauth;
+  
+  # .htindex may contain listing regexp
+  chomp ($allowed = <$htindex>||'.');
+  close $htindex;
+  
+  opendir $dir,$dir or http_error(503);
+  while (defined($_ = readdir $dir)) {
+    next if /^[.#]/ or /~$/;
+    if (@s = lstat "$dir/$_" and ($s[2] & (S_IRGRP|S_IROTH))) {
+      if    (-l _) { push @links,$_ }
+      elsif (-d _) { push @dirs,$_ }
+      elsif (-f _) { push @files,$_ }
+    }
+  }
+  closedir $dir;
+
+  # parent directory listable?
+  if ($uri =~ m:(/.+)/.+: and -f "$dir/../.htindex") {
+    unshift @dirs,$1;
+  }
+
+  # first the (sub)directories
+  $htmldoc = "<HTML>\n<h1>$uri/</h1>\n";
+  foreach my $d (sort @dirs) {
+    if ($d =~ m:^/: and -f "$d/.htindex") {
+      $htmldoc .= "<h3><a href=\"$d/\">$d/</a></h3>\n";
+    } elsif (-f "$dir/$d/.htindex") {
+      $htmldoc .= "<h3><a href=\"$uri/$d/\">$uri/$d/</a></h3>\n";
+    }
+  }
+  
+#  # then the symlinks
+#  $htmldoc .= "\n<pre>\n";
+#  my $link;
+#  foreach my $l (sort @links) {
+#    if ($l =~ /$allowed/ and $link = readlink "$dir/$l" and $link =~ /^[^.\/]/) {
+#      $htmldoc .= "$l -> <a href=\"$link\">$dir/$link</a>\n";
+#    }
+#  }
+  
+  # then the files
+  $htmldoc .= "\n<pre>\n";
+  foreach my $f (sort @files) {
+    if ($f =~ /$allowed/) {
+      $htmldoc .= sprintf "%20s %20s <a href=\"%s/%s\">%s</a>\n",
+                          isodate(mtime("$dir/$f")),
+                          d3(-s "$dir/$f"||0),
+                          $uri,urlencode($f),$f;
+    }
+  }
+  $htmldoc .= "</pre>\n</HTML>\n";
+  
+  $size = length($htmldoc);
+  nvt_print(
+    'HTTP/1.1 200 OK',
+    'Server: fexsrv',
+    "Content-Length: $size",
+    "Content-Type: text/html",
+    '',
+  );
+  print $htmldoc;
+  fdlog($log,"$dir/",$size,$size);
+}
+
+
+sub mtime {
+  return (lstat shift)[9];
+}
+
+
+sub d3 {
+  local $_ = shift;
+  while (s/(\d)(\d\d\d\b)/$1,$2/) {};
+  return $_;
+}
+
+
+sub http_date {
+  my $file = shift;
+  my @stat;
+  
+  if (@stat = stat($file)) {
+    return strftime("%a, %d %b %Y %T GMT",gmtime($stat[9]));
+  } else {
+    return 0;
+  }
+}
+
+
+sub path_match {
+  my $p1 = abs_path(shift);
+  my $p2 = abs_path(shift);
+
+  if (defined $p1 and defined $p2) {
+    return 1 if $p1          =~ /^\Q$p2/;
+    return 2 if dirname($p1) =~ /^\Q$p2/;
+  }
+  return 0;
+}
+
+
+# return real file name (from symlink)
+sub realfilename {
+  my $file = shift;
+  
+  return '' unless -e $file;
+  
+  if (-l $file) {
+    return realfilename(readlink($file));
+  } else {
+    return $file;
+  }
+}
+
+
+sub security_check {
+  my $file = shift; # can be directory, too!
+  my @s;
+
+  # client ip allowed?
+  access_check($file);
+
+  # documents with leading . are not allowed
+  if (abs_path($file) =~ /\/\./) {
+    errorlog("$file with leading .");
+    http_error(403);
+  }
+
+  if (-f $file) {
+
+    # document filename must not contain @
+    if (realfilename($file) =~ /@/ or abs_path($file) =~ /@/) {
+      errorlog("$file contains @");
+      http_error(403);
+    }
+  
+    # document filename must not end with ~
+    if (realfilename($file) =~ /~$/) {
+      errorlog("$file ends with ~");
+      http_error(403);
+    }
+  
+    # file must be group or world readable
+    if (@s = stat($file) and not($s[2] & (S_IRGRP|S_IROTH))) {
+      errorlog("$file not group or world readable");
+      http_error(403);
+    }
+
+    # symlink to regular file and symlink owned by root or fex? ==> ok!
+    if (-l $file and path_match(dirname($file),$docdir)) {
+      @s = lstat($file);
+      return if $s[4] == 0 or $s[4] == $<;
+    }
+    
+  }
+  
+  # file in allowed directory? ==> ok!
+  foreach my $dir (@doc_dirs) {
+    return if path_match($file,$dir);
+  }
+  
+  errorlog("$file not in \@doc_dirs");
+  http_error(403);
+}
+
+# security check: client ip allowed?
+sub access_check {
+  my $file = abs_path(shift);
+  my $dir = $file;
+  my $af;
+  local $_;
+
+  $dir .= '/x' if -d $dir;
+  
+  while ($dir = dirname($dir) and $dir ne '/') {
+    $af = "$dir/.htaccessfrom";
+    if (open $af,$af) {
+      while (<$af>) {
+        s/\s//g;
+        if (ipin($ra,$_)) {
+          close $af;
+          return;
+        }
+      }
+      errorlog("no access to $file by $af");
+      http_error(403);
+    }
+  }
+    
+}
+
+# HTTP Basic authentication
+sub require_auth {
+  my $htauth = shift;
+  my $doc = shift;
+  my ($realm,$auth);
+  my @http_auth;
+  my $uri = $ENV{REQUEST_URI} || '/';
+  
+  $uri =~ s/\/index\.html$//;
+  $uri =~ s/\/$//;
+
+  if (-d $doc or $doc =~ /\/index\.html$/) {
+    $realm = $uri;
+  } else {
+    $realm = dirname($uri);
+  }
+  
+  $auth = slurp($htauth);
+  unless ($auth and $realm) {
+    http_header("200 OK");
+    print html_header("$ENV{SERVER_NAME} no authentication");
+    pq(qq(
+      '<h3><code>$htauth</code> missing</h3>'
+      '</body></html>'
+    ));
+    exit;
+  }
+  chomp $auth;
+  
+  if ($ENV{HTTP_AUTHORIZATION} and $ENV{HTTP_AUTHORIZATION} =~ /Basic\s+(.+)/) 
+  { @http_auth = split(':',decode_b64($1)) }
+  if (@http_auth != 2 or $http_auth[1] ne $auth) {
+    http_header(
+      '401 Authorization Required',
+      "WWW-Authenticate: Basic realm=\"$realm\"",
+      'Content-Length: 0',
+    );
+    # control back to fexsrv for further HTTP handling
+    &reexec;
+  }
+}
+
+
+# function for <<perl-code>> inside HTML documents
+sub out {
+  $__ .= join('',@_);
+  return '';
+}
+
+# tie STDOUT to buffer variable (redefining print)
+package Buffer;
+
+sub TIEHANDLE { 
+  my ($class,$buffer) = @_; 
+  bless $buffer,$class; 
+}
+
+sub PRINT { 
+  my $buffer = shift; 
+  $$buffer .= $_ foreach @_; 
+}
+
+sub PRINTF { 
+  my $buffer = shift; 
+  my $fmt = shift @_;
+  $$buffer .= sprintf($fmt,@_);
+}
+
+1;
diff --git a/lib/fex.ph b/lib/fex.ph
new file mode 100644 (file)
index 0000000..f7d26e5
--- /dev/null
@@ -0,0 +1,156 @@
+## your F*EX server host name (with domain)
+$hostname = 'MYHOSTNAME.MYDOMAIN';
+
+## admin email address used in notification emails
+## to change it, you must call: fac -/ admin-email-address auth-id
+$admin = 'fex@'.$hostname;
+
+## server admin email address shown on web page 
+$ENV{SERVER_ADMIN} = $admin;
+
+## restrict web administration to ip range(s)
+@admin_hosts = qw(127.0.0.1 10.0.0.0-10.10.255.255);
+
+## Bcc address for notification emails
+$bcc = 'fex';
+
+## send notifications about new F*EX releases
+$notify_newrelease = $admin;
+
+## optional: download-URLs sent in notification emails
+# @durl = qw(
+#   http://MYFEXSERVER/fop 
+#   https://MYFEXSERVER/fop 
+#   http://MYPROXY/fex/fop
+# );
+
+## On AUTO mode the fexserver sends notification emails automatically.
+## On MANUAL mode the user must notify the recipients manually.
+# $mailmode = 'MANUAL';
+$mailmode = 'AUTO';
+
+## optional: your mail domain
+## if set it will be used as domain for every user without domain
+## local_user ==> local_user@$mdomain
+## if not set, addresses without domains produce an error
+# $mdomain = 'MY.MAIL.DOMAIN';
+# $admin = 'fexmaster@'.$mdomain;
+
+## optional: static address (instead of F*EX user) in notification email From
+## BEWARE: if set, mail error bounces will not go to the real sender, but
+##         to this address!
+# $sender_from = $admin;
+
+## optional HTML header extra link and logo
+# @H1_extra = qw(http://www.MY.ORG http://www.MY.ORG/logo.gif);
+
+## disclaimer to be appended to every notification email
+# $disclaimer = 'powered by camelcraft!';
+
+## optional: suppress funny messages
+# $boring = 1;
+
+# locales to present (must be installed!)
+# if empty, present all installed locales
+# @locales = qw(english swabian);
+
+## default locale: which languange is used in first place
+# $default_locale = 'swabian';
+
+## where to store the files and logs, must be writable for user fex!
+# $spooldir = "$ENV{HOME}/spool";
+# $logdir = $spooldir;
+
+## Default quota in MB for recipient; 0 means "no quota"
+$recipient_quota = 0; 
+
+## Default quota in MB for sender; 0 means "no quota"
+$sender_quota = 0; 
+
+## Expiration: keep files that number of days (default)
+$keep = 5; 
+
+## Expiration: keep files that number of days (maximum)
+$keep_max = 99;
+
+## Autodelete: delete files after download (automatically)
+##     YES     ==> immediatelly (1 minute grace time)
+##     DELAY   ==> after download at next fex_cleanup cronjob run 
+##      2       ==> 2 days after download (can be any number!)
+##     NO      ==> keep until expiration date (see $keep)
+$autodelete = 'YES';
+
+## if the file has been already downloaded then subsequentials
+## downloads are only allowed from the same client (uses cookies)
+## to prevent unwanted file sharing
+$limited_download = 'YES';
+
+## Allow or disallow overwriting of files
+$overwrite = 'YES';
+
+## Allow user requests for forgotten auth-IDs (then send by email)
+$mail_authid = 'YES';
+                                                  
+## optional: from which hosts and for which mail domains users may 
+##           register themselves as full users (must set both!)
+# @local_hosts = qw(127.0.0.1 ::1 10.10.100.0-10.10.200.255 129.69.1.129);
+# @local_domains = qw(uni-stuttgart.de flupp.org);
+# @local_domains = qw(uni-stuttgart.de *.uni-stuttgart.de);
+
+## optional: external users may register themselves as restricted users
+##           for local receiving domains and hosts (must set both!)
+# @local_rdomains = qw(flupp.org *.flupp.org);
+# @local_rhosts = qw(10.0.0.0-10.0.255.255 129.69.1.129);
+## optional: allow restricted user registration only by certain domains
+# @registration_domains = qw(belwue.de ietf.org);
+## optional: allow restricted user registration only by certain hosts
+# @registration_hosts = qw(129.69.0.0-129.69.255.255 176.9.84.26);
+
+## optional: for certain remote domains do not use sender address in 
+##           notfication email From, because their MTA will probably 
+##           reject it if From and To contain their domain name.
+##           Instead use $admin for From. See also $sender_from
+# @remote_domains = qw(flupp.org);
+
+## optional: allow public upload via http://$hostname/pup for
+# @public_recipients = qw(fexmaster@rus.uni-stuttgart.de);
+
+## optional: allow anonymous upload without authentication for these IP ranges
+# @anonymous_upload = qw(127.0.0.1 ::1 10.10.100.0-10.10.200.255 129.69.1.129);
+
+## optional: forbidden addresses
+# @forbidden_recipients = qw(nobody@* *@listserv*);
+
+## optional: forbidden ip addresses for CGIs
+# @forbidden_hosts = qw(64.124.0.0-64.125.255.255);
+
+## optional: restrict upload to these IP ranges
+# @upload_hosts = qw(127.0.0.1 ::1 10.10.100.0-10.10.200.255 129.69.1.129);
+
+## optional: restrict download to these address ranges
+# @download_hosts = qw(127.0.0.1 10.10.100.0-10.10.200.255 129.69.1.129);
+
+## optional: throttle bandwith for certain addresses (in kB/s)
+##           0 means : full speed
+##           first match wins
+# @throttle = qw(
+#      framstag@*:0 microsoft.com:100 
+#      127.0.0.1:0 202.0.0.0-211.255.255.255:1024
+#      [::1]:0 [fe00::0-fe00::ffff]:0
+# );
+
+## optional: expire user accounts after x days of inactivity
+##           delete=wipe out, notify=send mail to fex admin
+# $account_expire = "100:delete";
+# $account_expire = "365:notify";
+
+## optional: allowed directories for file linking (see fexsend)
+# @file_link_dirs = qw(/sw /nfs/home/exampleuser);
+
+## optional: allow additional directories with static documents
+##           ($docdir (/home/fex/htdocs) is always allowed implicitly)
+# @doc_dirs = qw(/sw /nfs/home/exampleuser/htdocs);
+
+## optional: text file with your conditions of using
+## will be append to registrations request replies
+# $usage_conditions = "$docdir/usage_conditions.txt";
diff --git a/lib/fex.pp b/lib/fex.pp
new file mode 100644 (file)
index 0000000..bb72a4e
--- /dev/null
@@ -0,0 +1,1449 @@
+#  -*- perl -*-
+
+use 5.008;
+use Fcntl              qw':flock :seek :mode';
+use IO::Handle;
+use IPC::Open3;
+use Encode;
+use Digest::MD5        qw'md5_hex';
+use File::Basename;
+use Sys::Hostname;
+use Symbol             qw'gensym';
+
+# set and untaint ENV if not in CLI (fexsrv provides clean ENV)
+unless (-t) {
+  foreach my $v (keys %ENV) {
+    ($ENV{$v}) = ($ENV{$v} =~ /(.*)/s);
+  }
+  $ENV{PATH}     = '/usr/local/bin:/bin:/usr/bin';
+  $ENV{IFS}      = " \t\n";
+  $ENV{BASH_ENV} = '';
+}
+
+unless ($FEXLIB = $ENV{FEXLIB} and -d $FEXLIB) {
+  die "$0: found no FEXLIB - fexsrv needs full path\n"
+}
+
+$FEXLIB =~ s:/+:/:g;
+$FEXLIB =~ s:/$::;
+
+# $FEXHOME is top-level directory of F*EX installation or vhost
+# $ENV{HOME} is login-directory of user fex
+# in default-installation both are equal, but they may differ
+$FEXHOME = $ENV{FEXHOME} or $ENV{FEXHOME} = $FEXHOME = dirname($FEXLIB);
+
+umask 077;
+
+# defaults
+$hostname = gethostname();
+$tmpdir = $ENV{TMPDIR} || '/var/tmp';
+$spooldir = $FEXHOME.'/spool';
+$docdir = $FEXHOME.'/htdocs';
+$logdir = $spooldir;
+$autodelete = 'YES';
+$overwrite = 'YES';
+$limited_download = 'YES';     # multiple downloads only from same client
+$keep = 5;                     # days
+$recipient_quota = 0;          # MB
+$sender_quota = 0;             # MB
+$timeout = 30;                 # seconds
+$bs = 2**16;                   # I/O blocksize
+$use_cookies = 1;
+$sendmail = '/usr/lib/sendmail';
+$sendmail = '/usr/sbin/sendmail' unless -x $sendmail;
+$mailmode = 'auto';
+$bcc = 'fex';
+$default_locale = '';
+$fop_auth = 0;
+$mail_authid = 'yes';
+$force_https = 0;
+$debug = 0;
+
+$FHS = -f '/etc/fex/fex.ph' and -d '/usr/share/fex/lib';
+# Debian FHS
+if ($FHS) {
+  $ENV{FEXHOME} = $FEXHOME = '/usr/share/fex';
+  $spooldir = '/var/spool/fex';
+  $logdir = '/var/log/fex';
+  $docdir = '/var/lib/fex/htdocs';
+  $notify_newrelease = '';
+}
+  
+# allowed download managers (HTTP User-Agent)
+$adlm = '^(Axel|fex)';
+
+# allowed multi download recipients
+$amdl = '^(anonymous|_fexmail_)';
+
+# local config
+require "$FEXLIB/fex.ph" or die "$0: cannot load $FEXLIB/fex.ph - $!";
+
+$fop_auth      = 0 if $fop_auth        =~ /no/i;
+$mail_authid   = 0 if $mail_authid     =~ /no/i;
+$force_https   = 0 if $force_https     =~ /no/i;
+$debug         = 0 if $debug           =~ /no/i;
+  
+# check for name based virtual host
+$vhost = vhost($ENV{'HTTP_HOST'});
+
+push @doc_dirs,$docdir;
+foreach my $ld (glob "$FEXHOME/locale/*/htdocs") {
+  push @doc_dirs,$ld;
+}
+
+$nomail = ($mailmode =~ /^MANUAL|nomail$/i);
+
+if (not $nomail and not -x $sendmail) {
+  http_die("found no sendmail\n");
+}
+http_die("cannot determine the server hostname") unless $hostname;
+
+$ENV{PROTO} = 'http' unless $ENV{PROTO};
+$keep = $keep_default ||= $keep || 5;
+$fra = $ENV{REMOTE_ADDR} || '';
+$sid = $ENV{SID} || '';
+  
+mkdirp($dkeydir = "$spooldir/.dkeys"); # download keys
+mkdirp($ukeydir = "$spooldir/.ukeys"); # upload keys
+mkdirp($akeydir = "$spooldir/.akeys"); # authentification keys
+mkdirp($skeydir = "$spooldir/.skeys"); # subuser authentification keys
+mkdirp($gkeydir = "$spooldir/.gkeys"); # group authentification keys
+mkdirp($xkeydir = "$spooldir/.xkeys"); # extra download keys
+mkdirp($lockdir = "$spooldir/.locks"); # download lock files
+
+if (my $ra = $ENV{REMOTE_ADDR} and $max_fail) {
+  mkdirp("$spooldir/.fail");
+  $faillog = "$spooldir/.fail/$ra";
+}
+
+unless ($admin) {
+  $admin = $ENV{SERVER_ADMIN} ? $ENV{SERVER_ADMIN} : 'fex@'.$hostname;
+}
+
+# $ENV{SERVER_ADMIN} may be set empty in fex.ph!
+$ENV{SERVER_ADMIN} = $admin unless defined $ENV{SERVER_ADMIN};
+
+$mdomain ||= '';
+
+if ($use_cookies) {
+  if (my $cookie = $ENV{HTTP_COOKIE}) {
+    if    ($cookie =~ /\bakey=(\w+)/) { $akey = $1 }
+    # elsif ($cookie =~ /\bskey=(\w+)/) { $skey = $1 }
+  }
+}
+
+if (@locales) {
+  if ($default_locale and not grep /^$default_locale$/,@locales) {
+    push @locales,$default_locale;
+  }
+  if (@locales == 1) {
+    $default_locale = $locales[0];
+  }
+}
+
+$default_locale ||= 'english';
+
+unless ($durl) {
+  my $host = '';
+  my $port = 0;
+  
+  ($host,$port) = split(':',$ENV{HTTP_HOST}||'');
+  $host = $hostname;
+  
+  unless ($port) {
+    $port = 80;
+    if (open my $xinetd,'<',"/etc/xinetd.d/fex") {
+      while (<$xinetd>) {
+        if (/^\s*port\s*=\s*(\d+)/) {
+          $port = $1;
+          last;
+        }
+      }
+      close $xinetd;
+    }
+  }
+  
+  # use same protocal as uploader for download
+  if ($ENV{PROTO} eq 'https' and $port == 443 or $port == 80) {
+    $durl = "$ENV{PROTO}://$host/fop";
+  } else {
+    $durl = "$ENV{PROTO}://$host:$port/fop";
+  }
+}
+
+@durl = ($durl) unless @durl;
+
+
+sub reexec {
+  exec($FEXHOME.'/bin/fexsrv') if $ENV{KEEP_ALIVE};
+  exit;
+}
+
+
+sub jsredirect {
+  $url = shift;
+  $cont = shift || 'request accepted: continue';
+  
+  http_header('200 ok');
+  print html_header($head||$ENV{SERVER_NAME});
+  pq(qq(
+    '<script type="text/javascript">'
+    '  window.location.replace("$url");'
+    '</script>'
+    '<noscript>'
+    '  <h3><a href="$url">$cont</a></h3>'
+    '</noscript>'
+    '</body></html>'
+  ));
+  &reexec;
+}
+
+
+sub debug {
+  print header(),"<pre>\n";
+  print "file = $file\n";
+  foreach $v (keys %ENV) {
+    print $v,' = "',$ENV{$v},"\"\n";
+  }
+  print "</pre><p>\n";
+}
+
+
+sub nvt_print {
+  foreach (@_) { syswrite STDOUT,"$_\r\n" }
+}
+
+
+sub html_quote {
+  local $_ = shift;
+  
+  s/&/&amp;/g;
+  s/</&lt;/g;
+  s/\"/&quot;/g;
+  
+  return $_;
+}
+
+
+
+sub http_header {
+  
+  my $status = shift;
+  my $msg = $status;
+
+  return if $HTTP_HEADER;
+  $HTTP_HEADER = $status;
+  
+  $msg =~ s/^\d+\s*//;
+
+  nvt_print("HTTP/1.1 $status");
+  nvt_print("X-Message: $msg");
+  # nvt_print("X-SID: $ENV{SID}") if $ENV{SID};
+  nvt_print("Server: fexsrv");
+  nvt_print("Expires: 0");
+  nvt_print("Cache-Control: no-cache");
+  # http://en.wikipedia.org/wiki/Clickjacking
+  nvt_print("X-Frame-Options: SAMEORIGIN");
+  if ($force_https) {
+    # https://www.owasp.org/index.php/HTTP_Strict_Transport_Security
+    nvt_print("Strict-Transport-Security: max-age=2851200");
+  }
+  if ($use_cookies) {
+    if ($akey) {
+      nvt_print("Set-Cookie: akey=$akey; Max-Age=9999; Discard");
+    }
+    # if ($skey) {
+    #   nvt_print("Set-Cookie: skey=$skey; Max-Age=9999; Discard");
+    # }
+    if ($locale) {
+      nvt_print("Set-Cookie: locale=$locale");
+    }
+  }
+  unless (grep /^Content-Type:/i,@_) {
+    # nvt_print("Content-Type: text/html; charset=ISO-8859-1");
+    nvt_print("Content-Type: text/html; charset=UTF-8");
+  }
+
+  nvt_print(@_,'');
+}
+
+
+sub html_header {
+  my $title = shift;
+  my $header = 'header.html';
+  my $head;
+
+  # http://www.w3.org/TR/html401/struct/global.html
+  # http://www.w3.org/International/O-charset
+  $head = qqq(qq(
+    '<html>'
+    '<head>'
+    '  <meta http-equiv="expires" content="0">'
+    '  <meta http-equiv="Content-Type" content="text/html;charset=utf-8">'
+    '  <title>$title</title>'
+    '</head>'
+  ));
+  # '<!-- <style type="text/css">\@import "/fex.css";</style> -->'
+  
+  if ($0 =~ /fexdev/) { $head .= "<body bgcolor=\"pink\">\n" } 
+  else                { $head .= "<body>\n" }
+  
+  $title =~ s:F\*EX:<a href="/index.html">F*EX</a>:;
+
+  if (open $header,'<',"$docdir/$header") {
+    $head .= $_ while <$header>;
+    close $header;
+  }
+  
+  $head .= &$prolog($title) if defined($prolog);
+  
+  if (@H1_extra) {
+    $head .= sprintf(
+      '<h1><a href="%s"><img align=center src="%s" border=0></a>%s</h1>',
+      $H1_extra[0],$H1_extra[1]||'',$title
+    );
+  } else {
+    $head .= "<h1>$title</h1>";
+  }
+  $head .= "\n";
+  
+  return $head;
+}
+
+
+sub html_error {
+  my $error = shift;
+  my $msg = "@_";
+  my @msg = @_;
+  my $isodate = isodate(time);
+  
+  $msg =~ s/[\s\n]+/ /g;
+  $msg =~ s/<.+?>//g; # remove HTML
+  map { s/<script.*?>//gi } @msg;
+  
+  errorlog($msg);
+  
+  # cannot send standard HTTP Status-Code 400, because stupid 
+  # Internet Explorer then refuses to display HTML body!
+  http_header("666 Bad Request - $msg");
+  print html_header($error);
+  print 'ERROR: ',join("<p>\n",@msg),"\n";
+  pq(qq(
+    '<p><hr><p>'
+    '<address>
+    '  $ENV{HTTP_HOST}'
+    '  $isodate'
+    '  <a href="mailto:$ENV{SERVER_ADMIN}">$ENV{SERVER_ADMIN}</a>'
+    '</address>'
+    '</body></html>'
+  ));
+  exit;
+}
+
+
+sub http_die {
+  
+  # not in CGI mode
+  die "$0: @_\n" unless $ENV{GATEWAY_INTERFACE};
+  
+  debuglog(@_);
+  
+  # create special error file on upload
+  if ($uid) {
+    my $ukey = "$spooldir/.ukeys/$uid";
+    $ukey .= "/error" if -d $ukey;
+    unlink $ukey;
+    if (open $ukey,'>',$ukey) {
+      print {$ukey} join("\n",@_),"\n";
+      close $ukey;
+    }
+  }
+  
+  html_error($error||'',@_);
+}
+
+
+sub check_maint {
+  if (my $status = readlink '@MAINTENANCE') {
+    my $isodate = isodate(time);
+    http_header('666 MAINTENANCE');
+    print html_header($head||'');
+    pq(qq(
+      "<center>"
+      "<h1>Server is in maintenance mode</h1>"
+      "<h3>($status)</h3>"
+      "</center>"
+      "<p><hr><p>"
+      "<address>$ENV{HTTP_HOST} $isodate</address>"
+      "</body></html>"
+    ));
+    exit;
+  }
+}
+
+
+sub check_status {
+  my $user = shift;
+  
+  $user = lc $user;
+  $user .= '@'.$mdomain if $mdomain and $user !~ /@/;
+
+  if (-e "$user/\@DISABLED") {
+    my $isodate = isodate(time);
+    http_header('666 DISABLED');
+    print html_header($head);
+    pq(qq(
+      "<h3>$user is disabled</h3>"
+      "Contact $ENV{SERVER_ADMIN} for details"
+      "<p><hr><p>"
+      "<address>$ENV{HTTP_HOST} $isodate</address>"
+      "</body></html>"
+    ));
+    exit;
+  }
+}
+
+
+sub isodate {
+  my @d = localtime shift;
+  return sprintf('%d-%02d-%02d %02d:%02d:%02d',
+                 $d[5]+1900,$d[4]+1,$d[3],$d[2],$d[1],$d[0]);
+}
+
+
+sub encode_Q {
+  my $s = shift;
+  $s =~ s{([\=\x00-\x20\x7F-\xA0])}{sprintf("=%02X",ord($1))}eog;
+  return $s;
+}  
+
+
+# from MIME::Base64::Perl
+sub decode_b64 {
+  local $_ = shift;
+  my $uu = '';
+  my ($i,$l);
+
+  tr|A-Za-z0-9+=/||cd;
+  s/=+$//;
+  tr|A-Za-z0-9+/| -_|;
+  return '' unless length;
+  $l = (length)-60;
+  for ($i = 0; $i <= $l; $i += 60) {
+    $uu .= "M" . substr($_,$i,60);
+  }
+  $_ = substr($_,$i);
+  $uu .= chr(32+(length)*3/4) . $_ if $_;
+  return unpack ("u",$uu);
+}
+
+
+# short base64 encoding
+sub b64 {
+  local $_ = '';
+  my $x = 0;
+  
+  pos($_[0]) = 0;
+  $_ = join '',map(pack('u',$_)=~ /^.(\S*)/, ($_[0]=~/(.{1,45})/gs));
+  tr|` -_|AA-Za-z0-9+/|;
+  $x = (3 - length($_[0]) % 3) % 3;
+  s/.{$x}$//;
+  
+  return $_;
+}
+
+
+# simulate a "rm -rf", but never removes '..'
+# return number of removed files
+sub rmrf {
+  my @files = @_;
+  my $dels = 0;
+  my ($file,$dir);
+  local *D;
+  local $_;
+  
+  foreach (@files) {
+    next if /(^|\/)\.\.$/;
+    /(.*)/; $file = $1;
+    if (-d $file and not -l $file) {
+      $dir = $file;
+      opendir D,$dir or next;
+      while ($file = readdir D) {
+        next if $file eq '.' or $file eq '..';
+        $dels += rmrf("$dir/$file");
+      }
+      closedir D;
+      rmdir $dir and $dels++;
+    } else {
+      unlink $file and $dels++;
+    }
+  }
+  return $dels;
+}
+
+
+sub gethostname {
+  my $hostname = hostname;
+  my $domain;
+  local $_;
+
+  unless ($hostname) {
+    $_ = `hostname 2>/dev/null`;
+    $hostname = /(.+)/ ? $1 : '';
+  }
+  if ($hostname !~ /\./ and open my $rc,'/etc/resolv.conf') {
+    while (<$rc>) {
+      if (/^\s*domain\s+([\w.-]+)/) {
+        $domain = $1;
+        last;
+      }
+      if (/^\s*search\s+([\w.-]+)/) {
+        $domain = $1;
+      }
+    }
+    close $rc;
+    $hostname .= ".$domain" if $domain;
+  }
+  if ($hostname !~ /\./ and $admin and $admin =~ /\@([\w.-]+)/) {
+    $hostname .= '.'.$1;
+  }
+  
+  return $hostname;
+}
+
+
+# strip off path names (Windows or UNIX)
+sub strip_path {
+  local $_ = shift;
+  
+  s/.*\\// if /^([A-Z]:)?\\/;
+  s:.*/::;
+  
+  return $_;
+}
+
+
+# substitute all critcal chars
+sub normalize {
+  local $_ = shift;
+  
+  return '' unless defined $_;
+  
+  # we need perl native utf8 (see perldoc utf8)
+  $_ = decode_utf8($_) unless utf8::is_utf8($_);
+
+  s/[\r\n\t]+/ /g;
+  s/[\x00-\x1F\x80-\x9F]/_/g;
+  s/^\s+//;
+  s/\s+$//;
+  
+  return encode_utf8($_);
+}
+
+
+# substitute all critcal chars
+sub normalize_html {
+  local $_ = shift;
+  
+  return '' unless defined $_;
+  
+  $_ = normalize($_);
+  s/[\"<>]//g;
+  
+  return $_;
+}
+
+
+
+# substitute all critcal chars with underscore
+sub normalize_filename {
+  local $_ = shift;
+
+  return $_ unless $_;
+
+  # we need native utf8
+  $_ = decode_utf8($_) unless utf8::is_utf8($_);
+  $_ = strip_path($_);
+  
+  # substitute all critcal chars with underscore
+  s/[^a-zA-Z0-9_=.+-]/_/g;
+  s/^\./_/;
+  
+  return encode_utf8($_);
+}
+
+
+sub normalize_email {
+  local $_ = lc shift;
+  
+  s/[^\w_.+=!~#^\@\-]//g;
+  s/^\./_/;
+  /(.*)/;
+  return $1;
+}
+
+
+sub untaint {
+  local $_ = shift;
+  /(.*)/s;
+  return $1;
+}
+
+
+sub checkchars {
+  my $input = shift;
+  local $_ = shift;
+  
+  if (/^([|+.])/) {
+    http_die("\"$1\" is not allowed at beginning of $input");
+  }
+  if (/([\/\"\'\\<>;])/) {
+    http_die(sprintf("\"&#%s;\" is not allowed in %s",ord($1),$input));
+  }
+  if (/(\|)$/) {
+    http_die("\"$1\" is not allowed at end of $input");
+  }
+  if (/[\000-\037]/) {
+    http_die("control characters are not allowed in $input");
+  }
+  /(.*)/;
+  return $1;
+}
+
+
+sub checkaddress {
+  my $a = shift;
+  my $re;
+  local $_;
+  local ($domain,$dns);
+  
+  $a =~ s/:\w+=.*//; # remove options from address
+  
+  return $a if $a eq 'anonymous';
+  
+  $re = '^[.@]|@.*@|local(host|domain)$|["\'\`\|\s()<>/;,]';
+  if ($a =~ /$re/i) {
+    debuglog("$a has illegal syntax ($re)");
+    return '';
+  }
+  $re = '^[!^=~#_:.+*{}\w\-\[\]]+\@(\w[.\w\-]*\.[a-z]+)$';
+  if ($a =~ /$re/i) {
+    $domain = $dns = $1;
+    { 
+      local $SIG{__DIE__} = sub { die "\n" };
+      eval q{
+        use Net::DNS;
+        $dns = Net::DNS::Resolver->new->query($domain)||mx($domain);
+        unless ($dns or mx('uni-stuttgart.de')) {
+          http_die("Internal error: bad resolver");
+        }
+      } 
+    };
+    if ($dns) {
+      return untaint($a);
+    } else {
+      debuglog("no A or MX DNS record found for $domain");
+      return '';
+    }
+  } else {
+    debuglog("$a does not match e-mail regexp ($re)");
+    return '';
+  }
+}
+
+
+# check forbidden addresses
+sub checkforbidden {
+  my $a = shift;
+  my ($fr,$pr);
+  local $_;
+
+  $a .= '@'.$mdomain if $mdomain and $a !~ /@/;
+  return $a if -d "$spooldir/$a"; # ok, if user already exists
+  if (@forbidden_recipients) {
+    foreach (@forbidden_recipients) {
+      $fr = quotemeta;
+      $fr =~ s/\\\*/.*/g; # allow wildcard *
+      # skip public recipients
+      if (@public_recipients) {
+        foreach $pr (@public_recipients) {
+          return $a if $a eq lc $pr;
+        }
+      }
+      return '' if $a =~ /^$fr$/i;
+    }
+  }
+  return $a;
+}
+
+
+sub randstring {
+  my $n = shift;
+  my @rc = ('A'..'Z','a'..'z',0..9 ); 
+  my $rn = @rc; 
+  my $rs;
+  
+  for (1..$n) { $rs .= $rc[int(rand($rn))] };
+  return $rs;
+}
+
+
+# emulate mkdir -p
+sub mkdirp {
+  my $dir = shift;
+  my $pdir;
+  
+  return if -d $dir;
+  $dir =~ s:/+$::;
+  http_die("cannot mkdir /\n") unless $dir;
+  $pdir = $dir;
+  if ($pdir =~ s:/[^/]+$::) {
+    mkdirp($pdir) unless -d $pdir;
+  }
+  unless (-d $dir) {
+    mkdir $dir,0770 or http_die("mkdir $dir - $!\n");
+  }
+}
+
+
+# hash with SID
+sub sidhash {
+  my ($rid,$id) = @_;
+
+  if ($rid and $ENV{SID} and $id =~ /^MD5H:/) {
+    $rid = 'MD5H:'.md5_hex($rid.$ENV{SID});
+  }
+  return $rid;
+}
+
+
+# test if ip is in iplist (ipv4/ipv6)
+# iplist is an array with ips and ip-ranges
+sub ipin {
+  my ($ip,@list) = @_;
+  my ($i,$ia,$ib);
+
+  $ipe = lc(ipe($ip));
+  map { lc } @list;
+  
+  foreach $i (@list) {
+    if ($ip =~ /\./ and $i =~ /\./ or $ip =~ /:/ and $i =~ /:/) {
+      if ($i =~ /(.+)-(.+)/) {
+        ($ia,$ib) = ($1,$2);
+        $ia = ipe($ia);
+        $ib = ipe($ib);
+        return $ip if $ipe ge $ia and $ipe le $ib;
+      } else {
+        return $ip if $ipe eq ipe($i);
+      }
+    }
+  }
+  return '';
+}
+
+# ip expand (ipv4/ipv6)
+sub ipe {
+  local $_ = shift;
+
+  if (/^\d+\.\d+\.\d+\.\d+$/) {
+    s/\b(\d\d?)\b/sprintf "%03d",$1/ge;
+  } elsif (/^[:\w]+:\w+$/) {
+    s/\b(\w+)\b/sprintf "%04s",$1/ge;
+    s/^:/0000:/;
+    while (s/::/::0000:/) { last if length > 39 }
+    s/::/:/;
+  } else {
+    $_ = '';
+  }
+  return $_;
+}
+
+
+sub filename {
+  my $file = shift;
+  my $filename;
+
+  if (open $file,'<',"$file/filename") {
+    $filename = <$file>||'';
+    chomp $filename;
+    close $file;
+  }
+  
+  unless ($filename) {
+    $filename = $file;
+    $filename =~ s:.*/::;
+  }
+  
+  return $filename;
+}
+
+
+sub urlencode {
+  local $_ = shift;
+  s/(^[.~]|[^\w.,=:~^+-])/sprintf "%%%X",ord($1)/ge;
+  return $_;
+}
+
+
+# file and document log
+sub fdlog {
+  my ($log,$file,$s,$size) = @_;
+  my $ra;
+  
+  if (open $log,'>>',$log) {
+    flock $log,LOCK_EX;
+    seek $log,0,SEEK_END;
+    $ra = $ENV{REMOTE_ADDR}||'-';
+    $ra .= '/'.$ENV{HTTP_X_FORWARDED_FOR} if $ENV{HTTP_X_FORWARDED_FOR};
+    $ra =~ s/\s//g;
+    $file =~ s:/data$::;
+    printf {$log} 
+           "%s [%s_%s] %s %s %s/%s\n",
+           isodate(time),$$,$ENV{REQUESTCOUNT},$ra,encode_Q($file),$s,$size;
+    close $log;
+  }
+}
+
+
+# extra debug log
+sub debuglog {
+  my $prg = $0;
+  local $_;
+  
+  return unless $debug and @_;
+  unless ($debuglog and fileno $debuglog) {
+    mkdir "$logdir/.debug",0770 unless -d "$logdir/.debug";
+    $prg =~ s:.*/::;
+    $prg = untaint($prg);
+    $debuglog = sprintf("%s/.debug/%s_%s_%s.%s",
+                        $logdir,time,$$,$ENV{REQUESTCOUNT}||0,$prg);
+    $debuglog =~ s/\s/_/g;
+    # open $debuglog,'>>:encoding(UTF-8)',$debuglog or return;
+    open $debuglog,'>>',$debuglog or return;
+    autoflush $debuglog 1;
+    # printf {$debuglog} "\n### %s ###\n",isodate(time);
+  }
+  while ($_ = shift @_) {
+    s/\n*$/\n/;
+    s/<.+?>//g; # remove HTML
+    print {$debuglog} $_;
+    print "DEBUG: $_" if -t;
+  }
+}
+
+
+# extra debug log
+sub errorlog {
+  my $prg = $0;
+  my $log = "$logdir/error.log";
+  my $msg = "@_";
+
+  $prg =~ s:.*/::;
+  $msg =~ s/[\r\n]+$//;
+  $msg =~ s/[\r\n]+/ /;
+  $msg =~ s/\s*<p>.*//;
+
+  if (open $log,'>>',$log) {
+    flock $log,LOCK_EX;
+    seek $log,0,SEEK_END;
+    $ra = $ENV{REMOTE_ADDR}||'-';
+    $ra .= '/'.$ENV{HTTP_X_FORWARDED_FOR} if $ENV{HTTP_X_FORWARDED_FOR};
+    $ra =~ s/\s//g;
+    printf {$log} "%s %s %s %s\n",isodate(time),$prg,$ra,$msg;
+    close $log;
+  }
+}
+
+
+# failed authentification log
+sub faillog {
+  my $request = shift;
+  my $n = 1;
+
+  if ($faillog and $max_fail_handler and open $faillog,"+>>$faillog") {
+    flock($faillog,LOCK_EX);
+    seek $faillog,0,SEEK_SET;
+    $n++ while <$faillog>;
+    printf {$faillog} "%s %s\n",isodate(time),$request;
+    close $faillog;
+    &$max_fail_handler($ENV{REMOTE_ADDR}) if $n > $max_fail;
+  }
+}
+
+# remove all white space
+sub despace {
+  local $_ = shift;
+  s/\s//g;
+  return $_;
+}
+
+
+# superquoting
+sub qqq {
+  local $_ = shift;
+  my ($s,$i,@s);
+  my $q = "[\'\"]"; # quote delimiter chars " and '
+
+  # remove first newline and look for default indention
+  s/^(\«(\d+)?)?\n//;
+  $i = ' ' x ($2||0);
+
+  # remove trailing spaces at end
+  s/[ \t]*\»?$//;
+
+  @s = split "\n";
+
+  # first line have a quote delimiter char?
+  if (/^\s+$q/) {
+    # remove heading spaces and delimiter chars
+    foreach (@s) {
+      s/^\s*$q//;
+      s/$q\s*$//;
+    }
+  } else {
+    # find the line with the fewest heading spaces (and count them)
+    # (beware of tabs!)
+    $s = length;
+    foreach (@s) {
+      if (/^( *)\S/ and length($1) < $s) { $s = length($1) };
+    }
+    # adjust indention
+    foreach (@s) {
+      s/^ {$s}/$i/;
+    }
+  }
+
+  return join("\n",@s)."\n";
+}
+
+
+# print superquoted
+sub pq {
+  my $H = STDOUT;
+  if (@_ > 1 and defined fileno $_[0]) { $H = shift }
+  print {$H} qqq(@_);
+}
+
+
+# check sender quota
+sub check_sender_quota {
+  my $sender = shift;
+  my $squota = $sender_quota||0;
+  my $du = 0;
+  my ($file,$size,%file,$data);
+  local $_;
+  
+  if (open $qf,'<',"$sender/\@QUOTA") {
+    while (<$qf>) {
+      s/#.*//;
+      $squota = $1 if /sender.*?(\d+)/i;
+    }
+    close $qf;
+  }
+  
+  foreach $file (glob "*/$sender/*") {
+    $data = "$file/data";
+    if (not -l $data and $size = -s $data) {
+      # count hard links only once (= same inode)
+      my $i = (stat($data))[1]||0;
+      unless ($file{$i}) {
+        $du += $size;
+        $file{$i} = $i;
+      }
+    } elsif (-f "$file/upload" and $size = readlink "$file/size") {
+      $du += $size;
+    }
+  }
+  
+  return($squota,int($du/1024/1024));
+}
+
+
+# check recipient quota
+sub check_recipient_quota {
+  my $recipient = shift;
+  my $rquota = $recipient_quota||0;
+  my $du = 0;
+  my ($file,$size);
+  local $_;
+  
+  if (open my $qf,'<',"$recipient/\@QUOTA") {
+    while (<$qf>) {
+      s/#.*//;
+      $rquota = $1 if /recipient.*?(\d+)/i;
+    }
+    close $qf;
+  }
+  
+  foreach $file (glob "$recipient/*/*") {
+    if (-f "$file/upload" and $size = readlink "$file/size") {
+      $du += $size;
+    } elsif (not -l "$file/data" and $size = -s "$file/data") {
+      $du += $size;
+    }
+  }
+  
+  return($rquota,int($du/1024/1024));
+}
+
+
+sub getline {
+  my $file = shift;
+  local $_;
+  chomp($_ = <$file>||'');
+  return $_;
+}
+
+
+# (shell) wildcard matching
+sub wcmatch {
+  local $_ = shift;
+  my $p = quotemeta shift;
+  
+  $p =~ s/\\\*/.*/g;
+  $p =~ s/\\\?/./g;
+  $p =~ s/\\\[/[/g;
+  $p =~ s/\\\]/]/g;
+
+  return /$p/;
+}
+
+  
+sub logout {
+  my $logout;
+  if    ($skey) { $logout = "/fup?logout=skey:$skey" }
+  elsif ($gkey) { $logout = "/fup?logout=gkey:$gkey" }
+  elsif ($akey) { $logout = "/fup?logout=akey:$akey" }
+  else          { $logout = "/fup?logout" }
+  return qqq(qq(
+    '<p>'
+    '<form name="logout" action="$logout">'
+    '  <input type="submit" name="logout" value="logout">'
+    '</form>'
+    '<p>'
+  ));
+}
+
+
+# print data dump of global or local variables in HTML
+# input musst be a string, eg: '%ENV'
+sub DD {
+  my $v = shift; 
+  local $_;
+
+  $n =~ s/.//;
+  $_ = eval(qq(use Data::Dumper;Data::Dumper->Dump([\\$v])));
+  s/\$VAR1/$v/;
+  s/&/&amp;/g;
+  s/</&lt;/g;
+  print "<pre>\n$_\n</pre>\n";
+}
+  
+# make symlink
+sub mksymlink {
+  my ($file,$link) = @_;
+  unlink $file;
+  return symlink untaint($link),$file;
+}
+
+
+# copy file (and modify) or symlink
+# returns chomped file contents or link name
+# preserves permissions and time stamps
+sub copy {
+  my ($from,$to,$mod) = @_;
+  my $link;
+  local $/;
+  local $_;
+  
+  $to .= '/'.basename($from) if -d $to;
+
+  if (defined($link = readlink $from)) {
+    mksymlink($to,$link);
+    return $link;
+  } else {
+    open $from,'<',$from or return;
+    open $to,'>',$to or return;
+    $_ = <$from>;
+    close $from;
+    eval $mod if $mod;
+    print {$to} $_;
+    close $to or http_die("internal error: $to - $!");
+    if (my @s = stat($from)) { 
+      chmod $s[2],$to;
+      utime @s[8,9],$to unless $mod;
+    }
+    chomp;
+    return $_;
+  }
+}
+
+
+sub slurp {
+  my $file = shift;
+  local $_;
+  local $/;
+  
+  if (open $file,$file) {
+    $_ = <$file>;
+    close $file;
+  }
+
+  return $_;
+}
+
+
+# name based virtual host?
+sub vhost {
+  my $hh = shift; # HTTP_HOST
+  my $vhost;
+  my $locale = $ENV{LOCALE};
+
+  # memorized vhost? (default is in fex.ph)
+  %vhost = split(':',$ENV{VHOST}) if $ENV{VHOST};
+    
+  if (%vhost and $hh and $hh =~ s/^([\w\.-]+).*/$1/) {
+    if ($vhost = $vhost{$hh} and -f "$vhost/lib/fex.ph") {
+      $ENV{VHOST} = "$hh:$vhost"; # memorize vhost for next run
+      $ENV{FEXLIB} = $FEXLIB = "$vhost/lib";
+      $logdir = $spooldir    = "$vhost/spool";
+      $docdir                = "$vhost/htdocs";
+      if ($locale and -e "$vhost/locale/$locale/lib/fex.ph") {
+        $ENV{FEXLIB} = $FEXLIB = "$vhost/locale/$locale/lib";
+      }
+      require "$FEXLIB/fex.ph" or die "$0: cannot load $FEXLIB/fex.ph - $!";
+      $ENV{SERVER_NAME} = $hostname;
+      @doc_dirs = ($docdir);
+      foreach my $ld (glob "$FEXHOME/locale/*/htdocs") {
+        push @doc_dirs,$ld;
+      }
+      return $vhost;
+    }
+  }
+}
+
+
+sub gpg_encrypt {
+  my ($plain,$to,$keyring,$from) = @_;
+  my ($pid,$pi,$po,$pe,$enc,$err);
+  local $_;
+  
+  $pe = gensym;
+  
+  $pid = open3($po,$pi,$pe,
+    "gpg --batch --trust-model always --keyring $keyring".
+    "    -a -e -r $bcc -r $to"
+  ) or return;
+  
+  print {$po} $plain;
+  close $po;
+    
+  $enc .= $_ while <$pi>;
+  $err .= $_ while <$pe>;
+  errorlog("($from --> $to) $err") if $err;
+  
+  close $pi;
+  close $pe;
+  waitpid($pid,0);
+  
+  return $enc;
+}
+
+
+# extract locale functions into hash of subroutine references
+# e.g. \&german ==> $notify{german}
+sub locale_functions {
+  my $locale = shift;
+  local $/;
+  local $_;
+  
+  if ($locale and open my $fexpp,"$FEXHOME/locale/$locale/lib/fex.pp") {
+    $_ = <$fexpp>;
+    s/.*\n(\#\#\# locale functions)/$1/s;
+    # sub xx {} ==> xx{$locale} = sub {}
+    s/\nsub (\w+)/\n\$$1\{$locale\} = sub/gs; 
+    s/\n}\n/\n};\n/gs;
+    eval $_;
+    close $fexpp;
+  }
+}
+
+sub notify_locale {
+  my $dkey = shift;
+  my $status = shift || 'new';
+  my ($to,$keep,$locale,$file,$filename,$comment,$autodelete,$replyto,$mtime);
+  local $_;
+
+  if ($dkey =~ m:/.+/.+/:) {
+    $file = $dkey;
+    $dkey = readlink("$file/dkey");
+  } else {
+    $file = readlink("$dkeydir/$dkey") 
+      or http_die("internal error: no DKEY $DKEY");
+  }
+  $file =~ s:^../::;
+  $filename = filename($file);
+  $to = $file;
+  $to =~ s:/.*::;
+  $mtime = mtime("$file/data") or http_die("internal error: no $file/data");
+  $comment = slurp("$file/comment") || '';
+  $replyto = readlink "$file/replyto" || '';
+  $autodelete = readlink "$file/autodelete" 
+             || readlink "$to/\@AUTODELETE" 
+             || $::autodelete;
+  $keep = readlink "$file/keep" 
+       || readlink "$to/\@KEEP" 
+       || $keep_default;
+  
+  $locale = readlink "$to/\@LOCALE" || readlink "$file/locale" || 'english';
+  $_ = untaint("$FEXHOME/locale/$locale/lib/lf.pl");
+  require if -f;
+  unless ($notify{$locale}) {
+    $locale = 'english';
+    $notify{$locale} ||= \&notify;
+  }
+  return &{$notify{$locale}}(
+    status     => $status,
+    dkey       => $dkey,
+    filename   => $filename,
+    keep       => $keep-int((time-$mtime)/DS),
+    comment    => $comment,
+    autodelete => $autodelete,
+    replyto    => $replyto,
+  );
+}
+
+### locale functions ###
+# will be extracted by install process and saved in $FEXHOME/lib/lf.pl
+# you cannot modify them here without re-installing!
+
+sub notify {
+  # my ($status,$dkey,$filename,$keep,$warn,$comment,$autodelete) = @_;
+  my %P = @_;
+  my ($to,$from,$file,$mimefilename,$receiver,$warn,$comment,$autodelete);
+  my ($size,$bytes,$days,$header,$data,$replyto);
+  my ($mfrom,$mto,$dfrom,$dto);
+  my $index;
+  my $fileid = 0;
+  my $fua = $ENV{HTTP_USER_AGENT}||'';
+  my $warning = '';
+  my $disclaimer = '';
+  my $download = '';
+  my $keyring;
+  my $boundary = randstring(16);
+  my ($body,$enc_body);
+
+  return if $nomail;
+  
+  $warn = $P{warn}||2;
+  $comment = encode_utf8($P{comment}||'');
+  $comment =~ s/^!\*!//; # multi download allow flag
+  $autodelete = $P{autodelete}||$::autodelete;
+  $index = $durl;
+  $index =~ s/fop/index.html/;
+
+  (undef,$to,$from,$file) = split('/',untaint(readlink("$dkeydir/$P{dkey}")));
+  $filename = strip_path($P{filename});
+  $mfrom = $from;
+  $mto = $to;
+  $mfrom .= '@'.$mdomain if $mdomain and $mfrom !~ /@/;
+  $mto .=   '@'.$mdomain if $mdomain and $mto   !~ /@/;
+  $keyring = $to.'/@GPG';
+  # $to = '' if $to eq $from; # ???
+  $replyto = $P{replyto}||$mfrom;
+  $header = "From: <$mfrom> ($mfrom via F*EX service $hostname)\n";
+  $header .= "Reply-To: <$replyto>\n" if $replyto ne $mfrom;
+  $header .= "To: <$mto>\n";
+  $data = "$dkeydir/$P{dkey}/data";
+  $size = $bytes = -s $data;
+  return unless $size;
+  $warning = 
+    "Please avoid download with Internet Explorer, ".
+    "because it has too many bugs.\n".
+    "We recommend Firefox or wget.";
+  if ($filename =~ /\.(tar|zip|7z|arj|rar)$/) {
+    $warning .= "\n\n".
+      "$filename is a container file.\n".
+      "You can unpack it for example with 7zip ".
+      "(http://www.7-zip.org/download.html)";
+  }
+  if ($limited_download =~ /^y/i) {
+    $warning .= "\n\n".
+      'This download link only works for you, you cannot distribute it.';
+  }
+  if ($size < 2048) {
+    $size = "$size Bytes";
+  } elsif ($size/1024 < 2048) {
+    $size = int($size/1024)." kB";
+  } else {
+    $size = int($size/1024/1024)." MB";
+  }
+  if ($autodelete eq 'YES') {
+    $autodelete = "WARNING: After download (or view with a web browser!), "
+                . "the file will be deleted!";
+  } elsif ($autodelete eq 'DELAY') {
+    $autodelete = "WARNING: When you download the file it will be deleted "
+                . "soon afterwards!";
+  } else {
+    $autodelete = '';
+  }
+  $mimefilename = $filename;
+  if ($mimefilename =~ s{([_\?\=\x00-\x1F\x7F-\xFF])}{sprintf("=%02X",ord($1))}eog) {
+    $mimefilename =~ s/ /_/g;
+    $mimefilename = '=?UTF-8?Q?'.$mimefilename.'?=';
+  }
+  
+  unless ($fileid = readlink("$dkeydir/$P{dkey}/id")) {
+    my @s = stat($data);
+    $fileid =  @s ? $s[1].$s[9] : 0;
+  }
+  
+  if ($P{status} eq 'new') {
+    $days = $P{keep};
+    $header .= "Subject: F*EX-upload: $mimefilename\n";
+  } else {
+    $days = $warn;
+    $header .= "Subject: reminder F*EX-upload: $mimefilename\n";
+  }
+  $header .= "X-FEX-Client-Address: $fra\n" if $fra;
+  $header .= "X-FEX-Client-Agent: $fua\n"   if $fua;
+  foreach my $u (@durl) {
+    my $durl = sprintf("%s/%s/%s",$u,$P{dkey},normalize_filename($filename));
+    $header .= "X-FEX-URL: $durl\n" unless -s $keyring;
+    $download .= "$durl\n";
+  }
+  $header .= 
+    "X-FEX-Filesize: $bytes\n".
+    "X-FEX-File-ID: $fileid\n".
+    "X-FEX-Fexmaster: $ENV{SERVER_ADMIN}\n".
+    "X-Mailer: F*EX\n".
+    "MIME-Version: 1.0\n";
+  if ($comment =~ s/^\[(\@(.*?))\]\s*//) { 
+    $receiver = "group $1";
+    if ($_ = readlink "$from/\@GROUP/$2" and m:^../../(.+?)/:) {
+      $receiver .= " (maintainer: $1)";
+    }
+  } else { 
+    $receiver = 'you';
+  }
+  if ($days == 1) { $days .= " day" }
+  else            { $days .= " days" }
+  
+  # explicite sender set in fex.ph?
+  if ($sender_from) {
+    map { s/^From: <$mfrom/From: <$sender_from/ } $header;
+    open $sendmail,'|-',$sendmail,$mto,$bcc
+      or http_die("cannot start sendmail - $!\n");
+  } else {
+    # for special remote domains do not use same domain in From, 
+    # because remote MTA will probably reject this e-mail
+    $dfrom = $1 if $mfrom =~ /@(.+)/;
+    $dto   = $1 if $mto   =~ /@(.+)/;
+    if ($dfrom and $dto and @remote_domains and 
+        grep { 
+          $dfrom =~ /(^|\.)$_$/ and $dto =~ /(^|\.)$_$/ 
+        } @remote_domains) 
+    {
+      $header =~ s/(From: <)\Q$mfrom\E(.*?)\n/$1$admin$2\nReply-To: $mfrom\n/;
+      open $sendmail,'|-',$sendmail,$mto,$bcc
+        or http_die("cannot start sendmail - $!\n");
+    } else {
+      open $sendmail,'|-',$sendmail,'-f',$mfrom,$mto,$bcc
+        or http_die("cannot start sendmail - $!\n");
+    }
+  }
+  if ($comment =~ s/^!(shortmail|\.)!\s*//i 
+    or (readlink "$to/\@NOTIFICATION"||'') =~ /short/i
+  ) {
+    $body = qqq(qq(
+      '$comment'
+      ''
+      '$download'
+      '$size'
+    ));
+  } else {
+    $comment = "Comment: $comment\n" if $comment;
+    $disclaimer = slurp("$from/\@DISCLAIMER") || qqq(qq(
+      '$warning'
+      ''
+      'F*EX is not an archive, it is a transfer system for personal files.'
+      'For more information see $index'
+      ''
+      'Questions? ==> F*EX admin: $admin'
+    ));
+    $disclaimer .= "\n" . $::disclaimer if $::disclaimer;
+    $body = qqq(qq(
+      '$from has uploaded the file'
+      '  "$filename"'
+      '($size) for $receiver. Use'
+      ''
+      '$download'
+      'to download this file within $days.'
+      ''
+      '$comment'
+      '$autodelete'
+      ''
+      '$disclaimer'
+    ));
+  }
+  if (-s $keyring) {
+    $enc_body = gpg_encrypt($body,$to,$keyring,$from);
+  }
+  if ($enc_body) {
+    # RFC3156
+    $header .= qqq(qq(
+      'Content-Type: multipart/encrypted; protocol="application/pgp-encrypted";'
+      '\tboundary="$boundary"'
+      'Content-Disposition: inline'
+    ));
+    $body = qqq(qq(
+      '--$boundary'
+      'Content-Type: application/pgp-encrypted'
+      'Content-Disposition: attachment'
+      ''
+      'Version: 1'
+      ''
+      '--$boundary'
+      'Content-Type: application/octet-stream'
+      'Content-Disposition: inline; filename="fex.pgp"'
+      ''
+      '$enc_body'
+      '--$boundary--'
+    ));
+  } else {
+    $header .=
+      "Content-Type: text/plain; charset=UTF-8\n".
+      "Content-Transfer-Encoding: 8bit\n";
+  }
+  print {$sendmail} $header,"\n",$body;
+  close $sendmail
+    or $! and http_die("cannot send notification e-mail (sendmail error $!)\n");
+  return $to;
+}
+
+
+sub reactivation {
+  my ($expire,$user) = @_;
+  my $fexsend = "$FEXHOME/bin/fexsend";
+
+  return if $nomail;
+  
+  if (-x $fexsend) {
+    $fexsend .= " -M -D -k 30 -C"
+               ." 'Your F*EX account has been inactive for $expire days,"
+               ." you must download this file to reactivate it."
+               ." Otherwise your account will be deleted.'"
+               ." $FEXLIB/reactivation.txt $user";
+    # on error show STDOUT and STDERR
+    system "$fexsend >/dev/null 2>&1";
+    if ($?) {
+      warn "$fexsend\n";
+      system $fexsend;
+    }
+  } else {
+    warn "$0: cannot execute $fexsend for reactivation()\n";
+  }
+}
+
+1;
diff --git a/lib/fup.pl b/lib/fup.pl
new file mode 100644 (file)
index 0000000..dd53f4c
--- /dev/null
@@ -0,0 +1,40 @@
+# config for F*EX CGI fup
+
+$info_1 = $info_login = <<EOD;
+<p><hr><p>
+<a href="/">F*EX (File EXchange)</a>
+is a service to send big (large, huge, giant, ...) files.
+<p>
+The sender (you) uploads the file to the F*EX server and the recipient automatically gets
+a notification e-mail with a download-URL.<br>
+After download or after $keep_default days the server deletes the file.
+F*EX is not an archive!
+<p>
+See also <a href="/FAQ/">questions & answers</a> and
+<a href="http://fex.rus.uni-stuttgart.de/usecases/">use cases</a>.
+<p><hr><p>
+<address>
+  <a href="mailto:$ENV{SERVER_ADMIN}">$ENV{SERVER_ADMIN}</a><br>
+</address>
+EOD
+
+$info_2 = <<EOD;
+<p><hr><p>
+After submission you will see an upload progress bar 
+(if you have javascript enabled and popups allowed).
+<p>
+<em>NOTE: Many web browsers cannot upload files > 2 GB!</em><br>
+If your file is larger you have to use a special <a href="/fuc?show=tools">F*EX client</a>
+or Firefox or Google Chrome which have no size limit.<br>
+You also need a <a href="/fuc?show=tools">F*EX client</a> for resuming interrupted uploads. Your web browser cannot do this.
+<p>
+If you want to send more than one file, then put them in a zip or tar archive, 
+e.g. with <a href="http://www.7-zip.org/download.html">7-Zip</a>.
+<p>
+See also the <a href="/FAQ/user.html">FAQ<a> and
+<a href="http://fex.rus.uni-stuttgart.de/usecases/">use cases</a>.
+<p><hr><p>
+<address>
+  <a href="mailto:$ENV{SERVER_ADMIN}">$ENV{SERVER_ADMIN}</a><br>
+</address>
+EOD
diff --git a/lib/reactivation.txt b/lib/reactivation.txt
new file mode 100644 (file)
index 0000000..1e55dc0
--- /dev/null
@@ -0,0 +1 @@
+Your F*EX account has been successfully reactivated.
diff --git a/locale/czech/htdocs/index.html b/locale/czech/htdocs/index.html
new file mode 100644 (file)
index 0000000..45cc933
--- /dev/null
@@ -0,0 +1,139 @@
+<HTML> 
+  <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+  <meta http-equiv="Content-Language" content="cs"/>
+<HEAD><TITLE>F*EX - File EXchange</TITLE></HEAD>
+<BODY>
+<center>
+<h1>F*EX - Frams' Fast File EXchange</h1>
+<IMG SRC="logo.jpg">
+</center>
+<p>
+F*EX (Frams' Fast File EXchange) je služba pro odesílání velkých 
+souborů uživatelem A uživateli B.
+<p>
+Odesilatel pomocí <a href="/fup">webového formuláře pro nahrání</a> nahraje 
+soubor na F*EX server a ten příjemci automaticky odešle 
+e-mail s oznámením a s URL ke stažení.
+<p>
+
+Říkáte si:
+<blockquote><em>
+  Proč potřebuji další službu pro přenos souborů?!<br>
+  Mám e-mail, ftp, ssh a v neposlední řadě i sendfile!
+</em></blockquote>
+<p>
+Já pravím:
+<blockquote><em>
+  Stále potřebujete F*EX :-)
+</em></blockquote>
+<p>
+  
+Například chcete poslat svému známému video z vaší poslední dovolené (1 GB).
+Máte následující možnosti (a s tím související problémy):<p>
+
+<ul>
+  <li><h3>poslat DVD poštou</h3><p>
+      Nepřichází v úvahu - žijeme 
+      <<my @x=gmtime(time); $x[5]-69;>> let
+      od roku, kdy byl vynalezen Internet!
+      Posílání paměťových médií je pro staříky.
+      <p>
+  <li><h3>použít e-mail</h3><p>
+      Většina e-mailových serverů má limit pro přílohu 10 MB a
+      velikost schránky kolem 100 MB a někdy i méně.
+      <p>
+  <li><h3>uucp</h3><p>
+      Proč rovnou nezůstat u psaní klínovým písmem na kamenné destičky?
+      <p>
+  <li><h3>ssh</h3><p>
+      Vy máte heslo svého známého, nebo se mu chystáte dát to své
+      jen kvůli přenosu souboru?
+      <p>
+  <li><h3>ftp</h3><p>
+      <ul>
+        <li>Pomocí jakého ftp serveru?
+        <li>Pomocí jakého účtu, jakého hesla?
+        <li>Vám nevadí posílání nešifrovaného hesla a souborů přes nezabezpečený Internet?
+        <li>Podporuje vaše proxy pasivní režim pro ftp?
+        <li>V případě anonymního ftp:
+          <ul>
+            <li>Umožňuje nahrát 1 GB?
+            <li>Umožňuje po nahrání soubor smazat?
+            <li>Kdo další si váš soubor může stáhnout?
+            <li>Kdo další může váš soubor smazat?
+            <li>Svému známému musíte poslat URL, kde si soubor může stáhnout, on vás musí
+                informovat, že si soubor stáhl, vy jej pak 
+                musíte smazat.<br>
+                Sečteno a podrženo: Otrava.
+          </ul>
+      </ul>
+      <p>
+  <li><h3>http</h3><p>
+      <ul>
+        <li>Žádné výchozí nahrávání a správa uživatelů - musí se naprogramovat
+        <li>Žádné automatické oznamování
+        <li>Žádné automatické mazání
+        <li>Velice málo http serverů umí zpracovat soubory větší než 2 GB
+      </ul>
+      <p>
+  <li><h3>sendfile</h3><p>
+      <ul>
+        <li>Používáte UNIX a máte nainstalovaný sendfile?
+        <li>Používá příjemce UNIX a má nainstalovaný sendfile?
+        <li>Nemáte vy ani příjemce firewall, který blokuje port 487?
+      </ul>
+      <p>
+  <li><h3>komerční služby jako jsou DropLoad, ALLPeers, YouSendIt, etd.</h3><p>
+      <ul>
+        <li>Jaký mají limit (většinou méně než 2 GB)?
+        <li>Co bezpečnost a ochrana osobních údajů: Budou vaše soubory skutečně uloženy a v bezpečí?
+        <li>Jsou provozovány na open-source, nebo na uzavřeném proprietálním softwaru?
+        <li>Jsou přístupné se všemi prohlížeči, nebo potřebujete Javu, Active-X, Flash či jiné hnusné zásuvné moduly?
+        <li>Budou existovat více, jak pár měsíců?<br>
+           (DropLoad, ALLPeers a drop.io své služby již ukončili)
+      </ul>
+      <p>
+</ul>
+<p>
+Pokud jste byť na jedinou z výše uvedených otázek odpověděli "ne", potom potřebujete 
+F*EX.
+<p>
+<h2>Hlavní funkce F*EX</h2>
+<p>
+<ul>
+  <li>přenos souborů s prakticky neomezenou velikostí
+  <li>příjemci a uživatelé potřebují pouze program pro elektronickou poštu a webový prohlížeč
+      jakéhokoli typu, nemusí instalovat žádný další software
+  <li>ZNOVU ODESLÁNÍ a ZNOVU STAŽENÍ pro pokračování přesně tam, kde došlo k selhání spojení
+  <li>automatické oznamování pro příjemce
+  <li>automatické mazání po stažení
+  <li>automatické mazání po době platnosti (výchozí: 5 dnů)
+  <li>plnohodnotní uživatelé si mohou vytvářet poduživatelé, kteří mohou odesílat pouze jim
+  <li>plnohodnotní uživatelé mohou vytvářet skupiny, obdoba poštovních konferencí s tím rozdílem, že se jedná o soubory
+  <li>bezúdržbovost: ze strany správce nejsou nutné žádné zásahy s vyjímkou vytváření nových F*EX účtů
+  <li>odesílání více příjemcům spotřebuje na serveru pouze místo pro jednoho příjemce
+  <li>F*EX je webová služba pro protokol HTTP a nepotřebuje žádné tunely ve firewallu
+  <li>podporuje také vysílání
+  <li>pro skalní UNIXové uživatele jsou k dispozici programy pro příkazovou řádku fexsend a fexget pro
+      vyhnutí se použití otravných webových prohlížečů
+  <li>protokol i zdrojový kód jsou volně dostupné (Perl Artistic)
+</ul>
+
+<h2>Promluvme si o SEXu</h2>
+F*EX má společníka: Stream EXchange (SEX).<p>
+SEX si můžete představit jako bránu mezi UNIXovými síťovými rourami.
+Ta může být užitečná k odesílání dat uživatelem A uživateli B, kde A a B mezi sebou
+nemohou navázat přímé spojení, ale oba dva se mohou připojit pomocí HTTP k 
+SEX serveru. Pro snadné začlenění do řady UNIXových nástrojů slouží nástroje
+pro příkazovou řádku sexsend a sexget.
+<p>
+Ověřování je stejné jako u F*EX.
+
+<h2>Máte otázky?</h2>
+Přečtěte si <a href="/FAQ.html">FAQ</a>
+
+<p>
+<ADDRESS>kontaktujte: <A HREF="mailto:$SERVER_ADMIN$">fexmaster</A></ADDRESS>
+
+</BODY>
+</HTML> 
diff --git a/locale/czech/lang.html b/locale/czech/lang.html
new file mode 100644 (file)
index 0000000..cd83f0f
--- /dev/null
@@ -0,0 +1 @@
+&#269;e&scaron;tina
diff --git a/locale/czech/lib/fup.pl b/locale/czech/lib/fup.pl
new file mode 100644 (file)
index 0000000..9bf5dd9
--- /dev/null
@@ -0,0 +1,35 @@
+# config for F*EX CGI fup
+
+$info_1 = $info_login = <<EOD;
+<p><hr><p>
+<a href="/">F*EX (File EXchange)</a>
+je služba pro odesílání velkých souborů.
+<p>
+Odesilatel (vy) nahraje soubor na F*EX server a ten příjemci autimaticky odešle 
+e-mail s oznámením a s URL ke stažení.<br>
+Po jeho stažení, nebo po uplynutí $keep_default dnů, server soubor smaže.
+F*EX není archiv!
+<p>
+Přečtěte si také <a href="/FAQ.html">Otázky a odpovědi (FAQ)</a>.
+<p><hr><p>
+<address>
+  <a href="mailto:$ENV{SERVER_ADMIN}">$ENV{SERVER_ADMIN}</a><br>
+</address>
+EOD
+
+$info_2 = <<EOD;
+<p><hr><p>
+Po potrzení uvidíte lištu s průběhem nahrávání 
+(pouze, pokud máte povolen javascript a povolené automatické otevírání oken).
+<p>
+<em>POZNÁMKA: Většina webových prohlížečů neumožňuje nahrávat soubory větší než 2 GB!</em><br>
+Pokud je váš soubor větší, použíjte speciálního <a href="/fuc?show=tools">klienta pro F*EX</a>.<br>
+Potřebuvoat budete také klient pro obnovení nahrávání v případě přerušení. Váš webový prohlížeč toto neumožňuje.
+<p>
+UPOZORNĚNÍ: Některé HTTP proxy servery, jako je třeba privoxy, spomalují průběh nahrávání!<br>
+V případě, že se s tímto pborlémem setkáte, bude zřejmě třeba zakázat používání proxy pro $ENV{SERVER_NAME}.
+<p>
+Pro uživatele Firefoxu: Nemačkejte klávesu ESC, protože jinak se stahování přeruší!
+<p>
+Přečtěte si také <a href="/FAQ.html">FAQ<a>.
+EOD
diff --git a/locale/czech/lib/reactivation.txt b/locale/czech/lib/reactivation.txt
new file mode 100644 (file)
index 0000000..6fe52c6
--- /dev/null
@@ -0,0 +1 @@
+Váš F*EX účet byl úspěšně znovu aktivován
diff --git a/locale/debian_translate b/locale/debian_translate
new file mode 100755 (executable)
index 0000000..b295e96
--- /dev/null
@@ -0,0 +1,132 @@
+#!/usr/bin/perl -w
+
+# translate english F*EX files into locale languange, debian special
+
+use File::Basename;
+use File::Copy;
+
+@trfiles = qw(
+  lib/fex.pp
+  cgi-bin/foc
+  cgi-bin/fop
+  cgi-bin/fuc
+  cgi-bin/fup
+  cgi-bin/fur
+  cgi-bin/rup
+  cgi-bin/pup
+);
+
+@cpfiles = qw(
+  htdocs/index.html
+  htdocs/FAQ.html
+  lib/fup.pl
+  lang.html
+);
+
+$source = shift;  
+$FEXHOME = shift;
+$dest = shift;
+
+$tr = "$source/locale/translations";
+
+mkdirp($dest);
+unless (-l "$dest/english") {
+  symlink '..',"$dest/english" 
+    or die "$0: cannot symlink $dest/english - $!\n";
+}
+
+foreach $lang (glob "$source/locale/*/lib/fup.pl") {
+  $lang =~ s:.*/locale/::;
+  $lang =~ s:/.*::;
+  next if $lang eq 'english';
+  next if $lang =~ /_$/;
+
+  print "\n$lang:\n";
+  open $tr,$tr or die "$0: cannot open $tr - $!\n";
+  %T = ();
+  $n = $ll = 0;
+
+  while (<$tr>) {
+    $n++;
+    last if /^\s*$/;
+    if (/^#\s*([\w-]+):/ and $lang eq $1) {
+      $ll = $n;
+    }
+  }
+
+  next unless $ll;
+
+  while (<$tr>) {
+    next if /^#/;
+    next if /^\s*$/;
+    chomp;
+    unless (/[a-z]/i) {
+      die "$0: syntax error in $tr line $. : no text\n";
+    }
+    $e = $_;
+    for my $l (2 .. $ll) { 
+      $_ = <$tr>||'';
+      chomp;
+      unless (/[a-z]/i) {
+        die "$0: syntax error in $tr line $. : no text\n";
+      }
+    }
+    $T{$e} = $_;
+    while (<$tr>) { last if /^\s*$/ }
+  }
+  
+  close $tr;
+  @E = sort {length $b <=> length $a} keys %T;
+
+  foreach $file (@trfiles) {
+    local $/;
+    mkdirp("$dest/$lang/".dirname($file));
+    $fe = "$source/$file";
+    $ft = "$dest/$lang/$file";
+    open $fe,"<$fe" or die "$0: cannot read $fe - $!\n";
+    open $ft,">$ft" or die "$0: cannot write $ft - $!\n";
+    $_ = <$fe>;
+    close $fe;
+    foreach $e (@E) {
+      s/\Q$e/$T{$e}/g;
+    }
+    print {$ft} $_;
+    close $ft;
+    chmod((stat $fe)[2],$ft);
+    print "$ft written\n";
+  }
+
+  foreach $file (@cpfiles) {
+    $fs = "$source/locale/$lang/$file";
+    $fd = "$dest/$lang/$file";
+    mkdirp(dirname($fd));
+    $fd .= '_new' if -f $fd;
+    if (copy($fs,$fd)) {
+      chmod((stat $fs)[2],$fd);
+      print "$fd written\n";
+    }
+  }
+
+  foreach $fs (qw(dop fex.ph)) {
+    $fd = "$dest/$lang/lib/$fs";
+    symlink "$FEXHOME/lib/$fs",$fd or die "$0: cannot symlink $fd - $!\n";
+    print "$fd linked\n";
+  }
+}
+
+# emulate mkdir -p
+sub mkdirp {
+  my $dir = shift;
+  my $pdir;
+  
+  return if -d $dir;
+  $dir =~ s:/+$::;
+  die "$0: cannot mkdir /\n" unless $dir;
+  $pdir = $dir;
+  if ($pdir =~ s:/[^/]+$::) {
+    mkdirp($pdir) unless -d $pdir;
+  }
+  unless (-d $dir) {
+    mkdir $dir,0755 or die "$0: mkdir $dir - $!\n";
+  }
+}
diff --git a/locale/french/htdocs/FAQ/FAQ.html b/locale/french/htdocs/FAQ/FAQ.html
new file mode 100644 (file)
index 0000000..a096645
--- /dev/null
@@ -0,0 +1,12 @@
+<html>
+<head><title>F*EX FAQ</title></head>
+<body>
+
+## <pre>
+## << while (($v,$vv) = each %ENV) { print "$v = $vv\n" } >>
+## </pre>
+
+<< require "./faq.pl" or print $! >>
+
+</body>
+</html>
diff --git a/locale/french/htdocs/FAQ/admin.faq b/locale/french/htdocs/FAQ/admin.faq
new file mode 100644 (file)
index 0000000..6f8c75d
--- /dev/null
@@ -0,0 +1,47 @@
+<html><pre>
+
+Q: Je ne peux pas installer un serveur web comme fexsrv parce que je n'ai pas les permissions root. Est-ce qu'il existe une version pure CGI de F*EX qui puisse tourner avec un server Apache ?
+A: F*EX est fortement lié à fexsrv pour plusieurs raisons (performance, limite de la taille des fichiers, session, etc...) et ne peut pas tourner comme un CGI sous Apache. Mais vous pouvez jeter un oeil à 
+<ul>
+<li>http://gpl.univ-avignon.fr/filez/
+<li>http://freshmeat.net/projects/eventh/
+<li>http://www.schaarwaechter.de/sp/projekte/dateiaustausch.html (en Allemand) 
+</ul>
+qui implementent le transfert de fichier avec des CGIs pures, mais avec une limite de 2GB.
+
+Q: F*EX ne fonctionne pas du tout ! Je ne peux pas m'y connecter avec mon navigateur web !
+A: Vérifier votre routage, ipfilters et la configuration de votre pare-feux.
+   Vérifier également si votre xinetd est linké à tcp-wrapper et configuré convenablement (hosts.allow).
+   F*EX a besoin du port 80/tcp pour HTTP et optionnellement du port 443/tcp pour HTTPS.
+
+Q: F*EX est trop complexe ! J'ai besoin de quelque chose encore plus simple.
+A: Essayez http://www.home.unix-ag.org/simon/woof.html
+
+Q: Comment puis-je intégrer F*EX à un système d'annuaire ou de gestion d'identité ?
+A: F*EX a plusieurs modules d'authentification: local, RADIUS, LDAP, mailman et POP.
+   Pour les quatre derniers, contacter framstag@rus.uni-stuttgart.de
+
+Q: Je veux que tous mes utilisateurs locaux puissent utiliser F*EX. Comment je fais ?
+A: Ils doivent s'enregistrer eux-mêmes avec http://$HTTP_HOST$/fur<p>
+   Il faut éditer lib/fex.ph et configurer (exemple):
+<pre>
+ @local_hosts = qw(127.0.0.1 10.10.100.0-10.10.255.255);
+ @local_domains = qw(flupp.org ulm.sub.net);
+</pre>
+(Bien entendu, vous devez utiliser les paramètres propres à votre réseau !)
+
+Q: J'ai besoin de plus de sécurité ! Comment activer HTTPS ?
+A: Lisez doc/SSL and jetez un oeil à "fop_auth" dans doc/concept
+
+Q: J'ai besoin d'un look adapté à mon image. Comment puis-je configurer F*EX en ce sens ?
+A: Voir la variable @H1_extra dans fex.ph et aussi l'ajout possible de code HTML à htdocs/header.html<p>
+   Voir htdocs/fup_template.html, le modifier selon vos besoins et l'utiliser comme page de démarrage.
+
+Q: F*EX est trop compliqué pour mes utilisateurs. J'ai besoin d'un formulaire d'upload simplifié.
+A: Voir htdocs/fup_template.html
+
+Q: Je veux que les mails Bcc destinés à F*EX (utilisateur admin) soient envoyés à une autre adresse.
+A: Positionnez la variable $bcc dans fex.ph
+
+Q: Puis-je avoir une version localisé dans ma langue ?
+A: Avec votre aide, oui. Merci de contacter framstag@rus.uni-stuttgart.de
diff --git a/locale/french/htdocs/FAQ/admin.html b/locale/french/htdocs/FAQ/admin.html
new file mode 100644 (file)
index 0000000..a096645
--- /dev/null
@@ -0,0 +1,12 @@
+<html>
+<head><title>F*EX FAQ</title></head>
+<body>
+
+## <pre>
+## << while (($v,$vv) = each %ENV) { print "$v = $vv\n" } >>
+## </pre>
+
+<< require "./faq.pl" or print $! >>
+
+</body>
+</html>
diff --git a/locale/french/htdocs/FAQ/all.html b/locale/french/htdocs/FAQ/all.html
new file mode 100644 (file)
index 0000000..a096645
--- /dev/null
@@ -0,0 +1,12 @@
+<html>
+<head><title>F*EX FAQ</title></head>
+<body>
+
+## <pre>
+## << while (($v,$vv) = each %ENV) { print "$v = $vv\n" } >>
+## </pre>
+
+<< require "./faq.pl" or print $! >>
+
+</body>
+</html>
diff --git a/locale/french/htdocs/FAQ/faq.pl b/locale/french/htdocs/FAQ/faq.pl
new file mode 100644 (file)
index 0000000..7770549
--- /dev/null
@@ -0,0 +1,119 @@
+package FAQ;
+
+my ($faq,$var,$env,$q,$a,$c,$s,$t,$n);
+my (@faq,%Q,%A,@s);
+my @sections = qw'Meta User Admin Misc';
+
+@faq = ($faq) = $ENV{PATH_INFO} =~ /(\w+).html/;
+@faq = map {lc} @sections if $faq eq 'all';
+
+print "<style type=text/css><!-- h2,h3 {font-weight:normal} --></style>\n";
+
+print '<h1><a name="top" href="/index.html">F*EX</a> ',
+      " Frequently Asked Questions</h1>\n";
+
+if ($faq ne 'local') {
+  print "<h3>\n";
+  foreach $s (@sections,'All') {
+    if ($s =~ /$faq/i) {
+      print "<b>$s</b>\n";
+    } else {
+      printf "[<a href=\"%s.html\">%s</a>]\n",lc($s),$s;
+    }
+  }
+  print "</h3>\n";
+}
+
+print "<table>\n";
+
+foreach my $faq (@faq) {
+  open $faq,"$faq.faq" or next;
+  local $/ = "Q:";
+  local $_ = <$faq>;
+  while (<$faq>) {
+    chomp;
+    while (/\$([\w_]+)\$/) {
+      $var = $1;
+      $env = $ENV{$var} || '';
+      # s/\$$var\$/<code>$env<\/code>/g;
+      s/\$$var\$/$env/g;
+    };
+    ($q,$a) = split /A:\s*/;
+    $q =~ s/[\s\n]+$//;
+    $q =~ s! (/\w[\S]+/[\S]+)! <code>$1</code>!g;
+    $a =~ s/[\s\n]+$/\n/;
+    while ($a =~ s/^(\s*)\*/$1<ul>\n$1<li>/m) { 
+      while ($a =~ s/(<li>.*\n\s*)\*/$1<li>/g) {}
+      $a =~ s:(.*\n)(\s*)(<li>[^\n]+\n):$1$2$3$2</ul>\n:s
+    }
+    $a =~ s/\n\n/\n<p>\n/g;
+    $a =~ s/([^>\n\\])\n/$1<br>\n/g;
+    $a =~ s/<pre>(.+?)<\/pre>/pre($1)/ges;
+    $a =~ s/\\\n/\n/g;
+#    $a =~ s/^\s*<br>\s*//mg;
+    $a =~ s/<([^\s<>\@]+\@[\w.-]+)>/<a href="mailto:$1">&lt;$1><\/a>/g;
+    $a =~ s! (/\w[\S]+/[\S]+)! <code>$1</code>!g;
+    $a =~ s!(https?://[\w-]+\.[^\s<>]+)!<a href="$1">[$1]</a>!g or
+    $a =~ s!(https?://[^\s<>]+)!<code>$1</code></a>!g;
+    push @{$Q{$faq}},$q;
+    push @{$A{$faq}},$a;
+  }
+  close $faq;
+}
+
+print "<table>\n";
+
+foreach $s (sections($faq)) {
+
+  $c = lc $s;
+  $s = '' if $s eq 'Local';
+  $t = '';
+  $t = $s if $faq eq 'all';
+
+  for ($n = 0; $n < scalar(@{$Q{$c}}); $n++) {
+    printf "<tr><th align=left>%s Q%d:<td> <a href=\"#%s%d\">%s</tr>\n",
+           $s,$n+1,$t,$n+1,${Q{$c}[$n]};
+  }
+}
+
+print "</table>\n";
+
+foreach $s (sections($faq)) {
+
+  $c = lc $s;
+  $s = '' if $s eq 'Local';
+  $t = '';
+  $t = $s if $faq eq 'all';
+
+  for ($n = 0; $n < scalar(@{$Q{$c}}); $n++) {
+    print "<p><hr><p>\n";
+    print "<table>\n";
+    printf "<tr><th><a name=\"%s%d\">%s&nbsp;Q%d:</a><td>%s</tr>\n",
+           $t,$n+1,$s,$n+1,${Q{$c}[$n]};
+    printf "<tr valign=top><th>%s&nbsp;A%d:<td>\n%s</tr>\n",
+           $s,$n+1,${A{$c}[$n]};
+    print "<tr><th>[<a href=\"#top\">Top</a>]<td></tr>\n";
+    print "</table>\n";
+  }
+}
+
+print "<pre>\n";
+print "\n" x 99;
+print "</pre>\n";
+
+
+sub sections {
+  my $faq = shift;
+  if ($faq eq 'all') {
+    return @sections;
+  } else {
+    return ucfirst($faq);
+  }
+}
+
+sub pre {
+  local $_ = shift;
+  s/<br>//g;
+  s/\s+$//;
+  return "<pre>$_</pre>\n";
+}
diff --git a/locale/french/htdocs/FAQ/index.html b/locale/french/htdocs/FAQ/index.html
new file mode 120000 (symlink)
index 0000000..1dadbb5
--- /dev/null
@@ -0,0 +1 @@
+meta.html
\ No newline at end of file
diff --git a/locale/french/htdocs/FAQ/local.faq b/locale/french/htdocs/FAQ/local.faq
new file mode 100644 (file)
index 0000000..8479331
--- /dev/null
@@ -0,0 +1,7 @@
+Q: Comment puis-je utiliser ma propre FAQ ?
+A: Placer votre FAQ dans le fichier /home/fex/htdocs/FAQ/local.faq et exécuter :
+
+   <code>
+   cd /home/fex/htdocs/FAQ
+   ln -sf local.html index.html
+   </code>
diff --git a/locale/french/htdocs/FAQ/local.html b/locale/french/htdocs/FAQ/local.html
new file mode 100644 (file)
index 0000000..a096645
--- /dev/null
@@ -0,0 +1,12 @@
+<html>
+<head><title>F*EX FAQ</title></head>
+<body>
+
+## <pre>
+## << while (($v,$vv) = each %ENV) { print "$v = $vv\n" } >>
+## </pre>
+
+<< require "./faq.pl" or print $! >>
+
+</body>
+</html>
diff --git a/locale/french/htdocs/FAQ/meta.faq b/locale/french/htdocs/FAQ/meta.faq
new file mode 100644 (file)
index 0000000..53b9153
--- /dev/null
@@ -0,0 +1,48 @@
+<html><pre>
+
+Q: Pourquoi "F*EX" comme nom et pas simplement "FEX"
+A: À l'époque de l'annonce du logiciel, il existait déja un programme (plus ancien) nommé "FEX" sur 
+   freshmeat.net.
+
+Q: Pourquoi ne pas utiliser un service commercial comme DropLoad, ALLPeers, YouSendIt, etc ?
+A: <ul>
+   <li>Ils ont une limite de 2GB ou moins.
+   <li>Leur politique de sécurité et de confidentialité est inconnue.
+   <li>Ils ne sont pas opensource.
+   <li>Il n'y a pas d'interface en ligne de commande (CLI) UNIX.
+   <li>Ils ont besoin de java, active-X, flash ou d'autres plugins esotériques.
+   <li>On ne sait pas combien de temps ils vont exister - DropLoad and ALLPeers ont déja cessé leur activité.
+   </ul>
+   
+Q: Pourquoi un chameau comme logo ?
+A: Le logo est inspiré du chameau de Perl, mais il est basé sur un chameau "Steiff plush"
+   qui courent avec nous lors de nos courses de tandem. Le logo a été dessiné par mon "chauffeur" Beate.
+   http://fex.rus.uni-stuttgart.de/Vortrag/tosa.html
+
+Q: Où puis-je récupérer les sources de F*EX ?
+A: http://fex.rus.uni-stuttgart.de/fex.html
+
+Q: Perl n'est-il pas trop lent pour faire tout ça ?
+A: fex.rus.uni-stuttgart.de tourne sur un ordinateur de bureau et est capable de gérer des uploads
+   à plus de 300 MB/s. Essayer donc ça avec un serveur web généraliste comme Apache !
+
+Q: De quoi ais-je besoin de pour installer F*EX ?
+A: D'une machine UNIX avec une entrée DNS et un service smtp fonctionnel. Vous devez être root sur la machine.
+
+Q: Qu'est-ce que DNS et smtp ?
+A: N'installez pas F*EX. Ce n'est pas fait pour vous.
+
+Q: Qui est l'auteur ?
+A: Ulli Horlacher framstag@rus.uni-stuttgart.de
+
+Q: Quel est la licence de F*EX ?
+A: Perl Artistic free software
+
+Q: Est-ce qu'il y a une mailing list F*EX ?
+A: https://listserv.uni-stuttgart.de/mailman/listinfo/fex
+
+Q: Où puis-je avoir un support commercial pour F*EX ?
+A: Contacter fex@nepustil.net http://www.nepustil.net/
+
+Q: J'ai encore des questions !
+A: Demandez directement à l'auteur framstag@rus.uni-stuttgart.de
diff --git a/locale/french/htdocs/FAQ/meta.html b/locale/french/htdocs/FAQ/meta.html
new file mode 100644 (file)
index 0000000..a096645
--- /dev/null
@@ -0,0 +1,12 @@
+<html>
+<head><title>F*EX FAQ</title></head>
+<body>
+
+## <pre>
+## << while (($v,$vv) = each %ENV) { print "$v = $vv\n" } >>
+## </pre>
+
+<< require "./faq.pl" or print $! >>
+
+</body>
+</html>
diff --git a/locale/french/htdocs/FAQ/misc.faq b/locale/french/htdocs/FAQ/misc.faq
new file mode 100644 (file)
index 0000000..c7f9941
--- /dev/null
@@ -0,0 +1,12 @@
+<html><pre>
+
+Q: F*EX est un très bon logiciel ! Est-ce que je peux rejoindre l'équipe de développement ? Qu'est-ce qu'il y a à faire ?
+A: Contacter l'auteur<p>
+
+Les fonctionnalités souhaitées sont :
+<ul>
+  <li>des testeurs pour MacOS, AIX et les autres UNIX
+  <li>un nouveau mainteneur pour le client Java F*EX
+  <li>un plugin F*EX pour thunderbird ou outlook
+  <li>plus de langages supportés (japonais, klingon ...)
+</ul>
diff --git a/locale/french/htdocs/FAQ/misc.html b/locale/french/htdocs/FAQ/misc.html
new file mode 100644 (file)
index 0000000..a096645
--- /dev/null
@@ -0,0 +1,12 @@
+<html>
+<head><title>F*EX FAQ</title></head>
+<body>
+
+## <pre>
+## << while (($v,$vv) = each %ENV) { print "$v = $vv\n" } >>
+## </pre>
+
+<< require "./faq.pl" or print $! >>
+
+</body>
+</html>
diff --git a/locale/french/htdocs/FAQ/user.faq b/locale/french/htdocs/FAQ/user.faq
new file mode 100644 (file)
index 0000000..dab0a9d
--- /dev/null
@@ -0,0 +1,60 @@
+<html><pre>
+
+Q: Qu'est-ce que l'auth-ID ?
+A: L'auth-ID est un moyen d'authentification interne qui permet d'authentifier l'utilisateur. Il sera d'abord généré par l'admin ou le processus d'enregistrement automatique et pourra être modifié plus tard par vous, l'utilisateur. Vous pouvez y penser comme à un mot de passe à faible sécurité.
+
+Q: Puis-je utiliser un proxy HTTP ?
+A: Oui, mais attention:
+   Certain proxy http comme HTTP introduise un délai dans l'affichage de la barre de progression lors d'un upload !
+   Vous pouvez essayer de désactiver le proxy pour $SERVER_NAME si vous rencontrez ce problème.
+
+Q: J'ai uploadé un fichier très volumineux mais j'ai fait une faute dans l'adresse du destinataire. J'ai reçu un message d'erreur par mail. Dois-je uploader à nouveau ce fichier ?
+A: Non, cela n'est pas nécessaire. Vous pouvez rediriger un fichier via le menu "Configuration utilisateur & Gestion"
+
+Q: Mon destinataire a perdu son e-mail de notification avec l'URL de téléchargement. Que puis-je faire ?
+A: Vous pouvez vous faire suivre une nouvelle copie du fichier via le menu "Configuration utilisateur & Gestion".
+   Vous recevrez un nouveau message de notification que vous pourrez envoyer à votre destinataire par la poste ou un fax :-)
+
+Q: Je ne peux pas uploader de fichiers > 2 GB avec mon navigateur internet !?
+A: Tous les navigateurs web que je connais ont des bugs dans leur implémentation de HTML-FORM. La plupart du temps, la limite est de 2 GB, parfois 4 GB.
+   Vous devez utiliser un client F*EX spécial pour uploader des fichiers > 2 GB, voir http://$HTTP_HOST$/tools.html
+   Firefox 7 (et suivant) est une exception : F*EX contourne des failles dans l'implémentation de l'upload afin de pouvoir uploader des fichiers de n'importe quelle taille.
+
+Q: Mon téléchargement a été interrompu avant la fin. Puis-je reprendre le téléchargement là où il s'est arrêté ?
+A: F*EX support la reprise de téléchargement, mais votre client doit aussi le supporter.
+   Firefox ne possède pas cette capacité HTTP, vous devez utiliser une autre client comme opera, wget ou fexget.
+
+Q: Mon upload a été interrompu avant la fin. Puis-je reprendre l'upload là où il s'est arrêté ?
+A: F*EX supporte la reprise d'upload, mais votre client doit aussi le supporter.
+   Aucun navigateur web n'a cette capacité, vous devez utiliser un autre client comme fexsend, schwuppdiwupp ou F*IX,
+   voir http://$HTTP_HOST$/tools.html
+
+Q: Mon nvaigateur web ne peut pas démarrer le client java F*IX, avec comme message d'erreur : "found no java runtime environment, cannot start F*IX upload applet"
+A: un plugin java pour votre navigateur est manquant. Sur Debian et Ubuntu, vous pouvez l'installer avec :
+   sudo aptitude install sun-java6-plugin
+
+Q: Lorsque j'appuie sur [ESC] dans firefox, l'upload est annulé. Pourquoi ?
+A: C'est une fonctionnalité intégré à Firefox. ESC termine l'opération courante.
+   Solution simple: ne pas appuyer sur ESC dans Firefox.
+   Solution complexe: demander aux développeurs de Firefox d'ajouter la configuration du clavier.
+
+Q: Firefox 3 se bloque lors de l'upload d'un fichier et je ne vois pas la barre de progression ?
+A: C'est un bug connu dans Firefox 3.6.23. Solution: passer à Firefox 7.
+
+Q: Envoyer un fichier en tant qu'utilisateur F*EX est facile, mais comment recevoir des fichiers de personnes extérieures ?
+A: Vous pouvez les enregistrer comme sous-utilisateurs ou créer un group F*EX via le menu "Configuration utilisateur & Gestion"
+
+Q: Parfois je peux télécharger un fichier plus d'une fois, surtout lorsque je le fais rapidement. Est-ce que la fonction de suppression automatique est défectueuse ?
+A: Le serveur F*EX a un délai d'une minute après le premier téléchargement réussi pendant lequel le fichier reste disponible. Cela est nécessaire
+car certains "gestionnaires de téléchargements" lancent plusieurs téléchargements du même fichier à la fois. Sinon, ils afficheraient une erreur à l'utilisateur. Votre administrateur F*EX a laissé AUTODELETE=DELAY par défaut, ce qui signifie que le nettoyage automatique est appelé une fois par jour.<p>
+   Les utilisateurs avancés (use the source, Luke !) peuvent activer l'option "ne pas supprimer après téléchargement".
+
+Q: La rétentation par défaut est trop courte pour moi, j'ai besoin de plus. Comment puis-je l'augmenter ?
+A: Utilisez fexsend, demandez à votre fexmaster ou bien lisez le code source :-)
+
+Q: J'ai envoyé un second fichier avec le même nom, mais le destinataire n'a pas reçu deuxième message de confirmation. Pourquoi ?
+A: Un fichier avec le même nom et le même destinataire écrase le premier si il est toujours présent, un deuxième e-mail de notification pour un même fichier n'a pas tellement de sens.
+   
+Q: Je ne peux pas télécharger des fichiers avec Internet Explorer, il me dit : "Cannot open Internet site". Que dois-je faire ?
+A: Utilisez Firefox ou tout autre navigateur compatible Internet, ce qui n'est pas le cas d'Internet Explorer. Cela fait parti des nombreux bugs d'Internet Explorer.
+
diff --git a/locale/french/htdocs/FAQ/user.html b/locale/french/htdocs/FAQ/user.html
new file mode 100644 (file)
index 0000000..a096645
--- /dev/null
@@ -0,0 +1,12 @@
+<html>
+<head><title>F*EX FAQ</title></head>
+<body>
+
+## <pre>
+## << while (($v,$vv) = each %ENV) { print "$v = $vv\n" } >>
+## </pre>
+
+<< require "./faq.pl" or print $! >>
+
+</body>
+</html>
diff --git a/locale/french/htdocs/index.html b/locale/french/htdocs/index.html
new file mode 100644 (file)
index 0000000..925a3a1
--- /dev/null
@@ -0,0 +1,131 @@
+<HTML> 
+<HEAD>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<TITLE>F*EX - File EXchange</TITLE></HEAD>
+<BODY>
+<center>
+<h1>F*EX - Frams' Fast File EXchange</h1>
+<IMG SRC="logo.jpg">
+</center>
+<p>
+F*EX (Frams' Fast File EXchange) est un service pour envoyer des fichiers
+volumineux d'un utilisateur A à un utilisateur B.
+<p>
+L'expéditeur upload le fichier au serveur F*EX en utilisant
+<a href="/fup">un formulaire WWW</a> et le destinataire reçoit automatiquement
+une notification.
+<p>
+
+Vous vous dîtes:
+<blockquote><em>
+  Pourquoi aurais -je besoin d'un autre service de transfert de fichier ?!<br>
+  J'ai déja l'e-mail, le ftp, ssh et même sendfile !
+</em></blockquote>
+<p>
+Je réponds:
+<blockquote><em>
+  Vous avez toujours besoin de F*EX :-)
+</em></blockquote>
+<p>
+  
+Par exemple, vous voulez envoyer à vos amis la dernière vidéo de vos vacances (1 GB).
+Vous avez les possibilités suivantes (et les problèmes):<p>
+
+<ul>
+  <li><h3>envoyer un DVD par la voie postale</h3><p>
+      Hors de question - nous vivons en <<my @x=gmtime(time); $x[5]-69;>> après l'invention d'Internet. C'est pour les grand-pères.
+      <p>
+  <li><h3>utiliser l'e-mail</h3><p>
+      La plupart des serveurs e-mail ont une limite de 10 MB par message et très souvent un quota d'utilisation de quelques centaines de méga-octets.
+      <p>
+  <li><h3>uucp</h3><p>
+      Et pourquoi pas du marbre et un burin ?
+      <p>
+  <li><h3>ssh</h3><p>
+      Vous avez le mot de passe de vos amis ou bien vous êtes prêts à leur donne le votre, juste pour un transfert de fichier ?
+      <p>
+  <li><h3>ftp</h3><p>
+      <ul>
+        <li>Quel serveur ?
+        <li>Quel compte, quel mot de passe ?
+        <li>Vous ne vous souciez pas d'envoyer des mots de passes non chiffrés et des fichiers sur un Internet non sécurisé ?
+        <li>Est-ce que votre proxy support le ftp passif ?
+        <li>Dans le cas d'un ftp anonyme :
+          <ul>
+            <li>Est-ce qu'il permet des uploads de 1GB ?
+            <li>Est-ce qu'il permet de supprimer le fichier uploadé ?
+            <li>Qui peut télécharger votre fichier ?
+            <li>Qui peut supprimer votre fichier ?
+            <li>Vous devez envoyer à votre ami l'URL de téléchargement, il doit vous informer
+                que le download s'est bien passé et vous devez supprimer le fichier
+                .<br>
+                C'est plutôt pénible.
+          </ul>
+      </ul>
+      <p>
+  <li><h3>http</h3><p>
+      <ul>
+        <li>Pas d'upload par défaut et de gestion des utilisateurs. Cela doit être programmé
+       <li>Pas de notification automatique
+       <li>Pas d'effacement automatique
+       <li>Peu de serveur http peuvent gérer des fichiers de plus de 2GB
+      </ul>
+      <p>
+  <li><h3>sendfile</h3><p>
+      <ul>
+       <li>Vous utilisez UNIX et sendfile est installé ?
+       <li>Votre destinataire utilise UNIX et a sendfile d'installé ?
+       <li>Ni vous ou votre destinataire n'a le port 487 bloqué par un firewall ?
+      </ul>
+      <p>
+  <li><h3>Des services commerciaux comme DropLoad, ALLPeers, YouSendIt, etc</h3><p>
+      <ul>
+        <li>Quel limite ont-ils (le plupart: &lt; 2 GB)?
+       <li>Quid de la sécurité et de la confidentialité : est-ce vos fichiers seront entre de bonnes mains ?
+       <li>Est-ce qu'ils sont opensource ou propriétaire ?
+       <li>Est-ce qu'ils sont accessibles avec n'importe quel navigateur ou bien avez-vous besoin de java, active-X, flash ou d'autres plugins ésotériques ?
+       <li>Est-ce qu'ils vont encore exister d'ici quelques mois?<br>
+           (DropLoad, ALLPeers et drop.io ont déja mis la clé sous la porte)
+      </ul>
+      <p>
+</ul>
+<p>
+Si vous avez répondu "non" à une seule de ces questions, alors vous avez besoin de F*EX
+<p>
+<h2>Principales fonctionnalités de F*EX</h2>
+<p>
+<ul>
+  <li>transfert de fichier de taille virtuellement infini
+  <li>le destinataire et l'expéditeur ont uniquement besoin d'un e-mail et d'un navigateur web, ils n'ont pas besoin d'installer de logiciels.
+  <li>reprise d'upload et de téléchargement en cas de défaillance
+  <li>notification automatique du destinataire
+  <li>suppression automatique après un téléchargement
+  <li>suppression automatique après un délai ajustable (par défaut: 5 jours)
+  <li>les utilisateurs F*EX peuvent créer des URLs d'upload à usage unique pour des utilisateurs extérieurs
+  <li>les utilisateurs F*EX peuvent créer des sous-utilisateurs qui ne pourront envoyer des fichiers uniquement à cet utilisateur
+  <li>les utilisateurs F*EX peuvent créer des groupes, similaire à des listes de diffusions, mais pour des fichiers
+  <li>maintenance réduite au minimum : pas d'interventations de l'administrateur nécessaires mise à part la création de nouveaux utilisateurs
+  <li>l'envoi à plusieurs destinataires ne nécessite pas le stockage d'autant de fichiers sur le serveur
+  <li>F*EX est un service web HTTP qui ne nécessite pas de contourner les parre-feux. 
+  <li>support des flux
+  <li>pour les vrais utilisateurs UNIX, il existe les programmes shell fexsend et fexget afin d'éviter l'utilisation ennuyeuse de l'interface web
+  <li>protocole et code source librement disponible (Perl Artistic)
+</ul>
+
+<h2>Parlons de SEX</h2>
+F*EX a un ami : Stream EXchange (SEX).<p>
+Vous pouvez imager SEX comme un tube UNIX réseau s'appuyant sur un relais.
+Cela peut s'avérer utile pour envoyer des données d'un utilisateur A à un utilisateur B lorsque A et B ne peuvent établir
+une connexion directe, mais que chacun peut accéder en HTTP au serveur SEX. Pour une intégration sans peine avec les outils UNIX,
+il y a les commandes shell sexsend et sexget.
+<p>
+L'authentifaction est la même qu'avec F*EX
+
+<h2>Encore des  questions?</h2>
+Voir la <a href="/FAQ/FAQ.html">FAQ</a>
+
+<p>
+<ADDRESS>contact: <A HREF="mailto:$SERVER_ADMIN$">fexmaster</A></ADDRESS>
+
+</BODY>
+</HTML> 
diff --git a/locale/french/lang.html b/locale/french/lang.html
new file mode 100644 (file)
index 0000000..75cddf3
--- /dev/null
@@ -0,0 +1 @@
+fran&ccedil;ais
diff --git a/locale/french/lib/fup.pl b/locale/french/lib/fup.pl
new file mode 100644 (file)
index 0000000..fb72a49
--- /dev/null
@@ -0,0 +1,32 @@
+# config for F*EX CGI fup
+
+$info_1 = $info_login = <<EOD;
+<p><hr><p>
+<a href="/">F*EX (File EXchange)</a>
+est un service pour envoyer des fichiers très volumineux (grand, énorme, géant, ...).
+<p>
+L'expéditeur (vous) upload le fichier vers un serveur F*EX et le destinataire reçoit automatiquement un message
+de notification par mail avec l'URL de téléchargement.<br>
+Après un téléchargement ou après $keep_default jours, le serveur efface le fichier.
+F*EX n'est pas un système d'archivage!
+<p>
+Voir les <a href="/FAQ/FAQ.html">questions & réponses</a>.
+<p><hr><p>
+<address>
+  <a href="mailto:$ENV{SERVER_ADMIN}">$ENV{SERVER_ADMIN}</a><br>
+</address>
+EOD
+
+$info_2 = <<EOD;
+<p><hr><p>
+Après soumission de votre fichier pour l'upload, vous verrez une barre de progression
+(si vous avez javascript activé et que les popups sont autorisés)
+<p>
+<em>REMARQUE: La plupart des navigateurs ne peuvent pas uploader des fichiers > 2 GB!</em><br>
+Si votre fichier est plus gros, vous devez utiliser un <a href="/fuc?show=tools">client F*EX spécial</a>.<br>
+Vous devez aussi en utiliser un pour la reprise d'upload interrompu. Votre navigateur ne peut pas le faire.
+<p>
+Pour les utilisateurs de Firefox : ne pas appuyer sur [ESC] parce que cela stoppera l'upload !
+<p>
+Voir aussi la <a href="/FAQ/FAQ.html">FAQ<a>.
+EOD
diff --git a/locale/french/lib/reactivation.txt b/locale/french/lib/reactivation.txt
new file mode 100644 (file)
index 0000000..f00dc9e
--- /dev/null
@@ -0,0 +1 @@
+Votre compte F*EX a été activé avec succès.
diff --git a/locale/galician/htdocs/FAQ.html b/locale/galician/htdocs/FAQ.html
new file mode 100644 (file)
index 0000000..b1ed318
--- /dev/null
@@ -0,0 +1,264 @@
+<HTML>
+<PRE>
+Cuesti&oacute;ns meta:
+===============
+
+<a name="1"><a href="#1">Q</a>: Por que o nome &quot;F*EX&quot; e non directamente &quot;FEX&quot;?
+
+A: No momento de facerse p&uacute;blico xa hab&iacute;a unha (antigo) aplicativo chamado &quot;FEX&quot; en
+   freshmeat.net.
+
+
+<a name="2"><a href="#2">Q</a>: Por que non usar un servizo comercial como
+   DropLoad, ALLPeers, YouSendIt, etc?
+
+A: Te&ntilde;en un l&iacute;mite de 2 GB ou menor.
+   A s&uacute;a seguranza e privacidade son desco&ntilde;ecidas.
+   Non se basean en c&oacute;digo fonte aberto.
+   Non hai clientes de UNIX (CLI) para eles.
+   Necesitan java, active-X, flash ou outros endemo&ntilde;ados complementos.
+   Desco&ntilde;&eacute;cese canto tempo van funcionar - DropLoad e ALLPeers xa pecharon
+   as s&uacute;as portas.
+
+
+<a name="3"><a href="#3">Q</a>: Por que un camelo no logo?
+
+A: O logo inspirouse no camelo de Perl, pero base&aacute;se en Steiff, un camelo de peluxe,
+   co cal constitu&iacute;mos o noso t&aacute;ndem. O logo foi debuxado
+   por Beate
+   <a href="http://fex.rus.uni-stuttgart.de/Vortrag/tosa.html">http://fex.rus.uni-stuttgart.de/Vortrag/tosa.html</a>
+
+
+<a name="4"><a href="#4">Q</a>: Onde podo eu obter as fontes de F*EX?
+
+A: <a href="http://fex.rus.uni-stuttgart.de/fex.html">http://fex.rus.uni-stuttgart.de/fex.html</a>
+
+
+<a name="5"><a href="#5">Q</a>: Que necesito para instalar F*EX?
+
+A: Un servidor UNIX con entrada no DNS e smtp para o correo de sa&iacute;da.
+   E debe ser root nese servidor.
+
+
+<a name="6"><a href="#6">Q</a>: Que &eacute; o DNS e smtp?
+
+A: Non instale F*EX. Est&aacute; al&eacute;n das s&uacute;as posibilidade.
+
+
+<a name="7"><a href="#7">Q</a>: Quen e o autor?
+
+A: Ulli Horlacher &lt;<a href="mailto:framstag@rus.uni-stuttgart.de">framstag@rus.uni-stuttgart.de</a>&gt;
+
+
+<a name="8"><a href="#8">Q</a>: Que licenza ten F*EX?
+
+A: De software libre, Perl Artistic, vexa <a href="http://fex.rus.uni-stuttgart.de/doc/Licence">http://fex.rus.uni-stuttgart.de/doc/Licence</a>
+
+
+<a name="9"><a href="#9">Q</a>: Hai unha lista de correo de F*EX?
+
+A: <a href="https://listserv.uni-stuttgart.de/mailman/listinfo/fex">https://listserv.uni-stuttgart.de/mailman/listinfo/fex</a>
+
+
+<a name="10"><a href="#10">Q</a>: Onde podo obter asistencia comercial para F*EX?
+
+A: Contacto <a href="mailto:fex@nepustil.net">fex@nepustil.net</a> <a href="http://www.nepustil.net/">http://www.nepustil.net/</a>
+
+
+<a name="11"><a href="#11">Q</a>: Te&ntilde;eo m&aacute;is/outras preguntas diferentes!
+
+A: Preguntarlle ao autor &lt;<a href="mailto:framstag@rus.uni-stuttgart.de">framstag@rus.uni-stuttgart.de</a>&gt;
+
+
+
+Preguntas de usuario:
+=====================
+
+<a name="12"><a href="#12">Q</a>: Que &eacute; o &quot;auth-ID&quot;?
+
+A: O auth-ID &eacute; unha identificaci&oacute;n interna que autentica o usuario.
+   Ser&aacute; a primeira xerada polo admin ou o proceso de rexistro autom&aacute;tico
+   e pode despois modificalo vostede, o usuario. Tr&aacute;tase dun contrasinal
+   de baixa seguranza.
+
+
+<a name="13"><a href="#13">Q</a>: Podo usar un proxy HTTP?
+
+A: Si.
+
+
+<a name="14"><a href="#14">Q</a>: Carguei un ficheiro ENORME pero equivoqueime ao escribir o enderezo do destinatario. Agora te&ntilde;o
+   un erro de entrega de correo. Debo volver cargar ese ENORME ficheiro?
+
+A: Non, non fai falta. Pode redirixir o ficheiro con
+   <a href="http://$HTTP_HOST$/rup">http://$HTTP_HOST$/rup</a>
+
+
+<a name="15"><a href="#15">Q</a>: Cargue un ficheiro ENORME pero esquec&iacute;n outro destinatario.
+   Debo volver cargar ese ENORME ficheiro?
+
+A: Non, non &eacute; necesario. Pode reenviar copia do ficheiro con
+   <a href="http://$HTTP_HOST$/foc">http://$HTTP_HOST$/foc</a>
+
+
+<a name="16"><a href="#16">Q</a>: Non podo cargar ficheiros &gt; 2 GB co meu navegador web!?
+
+A: Todos os navegadores te&ntilde;en defectos na implementaci&oacute;n dos
+   seus formularios HTML. O l&iacute;mite m&aacute;is frecuente &eacute; de 2 GB, &aacute;s veces 4 GB.
+
+   Debe usar un cliente especial de F*EX para cargar ficheiros &gt; 2 GB, vexa
+   <a href="http://$HTTP_HOST$/tools.html">http://$HTTP_HOST$/tools.html</a>
+
+
+<a name="17"><a href="#17">Q</a>: A mi&ntilde;a descarga abortouse antes de rematar. Podo retomar esa descarga?
+
+A: F*EX permite retomar e descargar, pero o seu cliente tam&eacute;n debe permitir
+   esta funcionalidade. Firefox por exemplo carece desta funcionalidade HTTP, necesitar&aacute;
+   outro cliente como Opera, wget ou fexget.
+
+
+<a name="18"><a href="#18">Q</a>: A mi&ntilde;a carga foi abortada antes de rematar. Podo retomar esa carga?
+
+A: F*EX permite retomar a carga, pero o seu cliente debe permitir vbnm, 1tupports resuming at upload, but your client also has to support it.
+   No web browser has this feature, you need a special F*EX client like
+   fexsend, schwuppdiwupp or F*IX.
+   See <a href="http://$HTTP_HOST$/tools.html">http://$HTTP_HOST$/tools.html</a>
+
+
+<a name="19"><a href="#19">Q</a>: O meu navegador non pode iniciar o cliente java F*IX, que indica:
+   &quot;non se atopou un entorno de execuci&oacute;n java, non se pode iniciar F*IX o miniaplicativo de carga&quot;
+
+A: Non se localiza un complemento java do navegador. En Debian e Ubuntu pode
+   instalalo con: &quot;sudo aptitude install sun-java6-plugin&quot;
+
+
+<a name="20"><a href="#20">Q</a>: Cando premo[ESC] no Firefox a carga canc&eacute;lase. Por que?
+
+A: Esta &eacute; unha funcionalidade interna do Firefox: ESC termina a operaci&oacute;n actual.
+   Soluci&oacute;n simple: non prema ESC no Firefox.
+   Soluci&oacute;n complexa: p&iacute;dalles aos desenvolvedores do Firefox que engadan un atallo de teclado.
+
+
+<a name="21"><a href="#21">Q</a>: O env&iacute;o como un usuario F*EX &eacute; doado, pero como recibir ficheiros doutros,
+   de f&oacute;ra?
+
+A: Rex&iacute;streos como subusuarios ou cree un grupo F*EX
+   con <a href="http://$HTTP_HOST$/fuc">http://$HTTP_HOST$/fuc</a>
+
+
+<a name="22"><a href="#22">Q</a>: &Aacute;s veces podo descargar un ficheiro m&aacute;is dunha vez, especialamente cando
+   o repito rapidamente. Est&aacute; a funcionalidade de auteliminaci&oacute;n defectuosa?
+
+A: O servidor F*EX ten un per&iacute;od de gracia de 1 minuto despois da primeira
+   descarga correcta no cal o ficheiro a&iacute;nda est&aacute; dispo&ntilde;&iacute;bel. Isto &eacute; necesario
+   porque alg&uacute;ns &quot;xestores de descargas&quot; est&uacute;pidos solicitan o ficheiro
+   varias veces &aacute; vez. Caso contrario poder&iacute;an indicar un erro ao usuario.
+
+A: O seu fexmaster ten un conxunto AUTODELETE=DELAY como predeterminado, o que significa que
+   o proceso de autolimpeza real&iacute;zase unha vez ao d&iacute;a.
+
+A: Os &quot;Power users&quot; ou usuarios con permisos (usa a forza, Luke!) poden establecer unha marca &quot;non eliminar ata
+   despois da descarga&quot;.
+
+
+<a name="23"><a href="#23">Q</a>: O per&iacute;odo de reserva predeterminado &eacute; demasiado curto para min, c&oacute;mpreme m&aacute;is. Como podo estabelecelo?
+
+A: Use fexsend, p&iacute;dallo ao seu fexmaster ou lea o c&oacute;digo fonte :-)
+
+
+<a name="24"><a href="#24">Q</a>: Non podo descargar ficheiros con Internet Explorer, dime &quot;Non se pode
+   abrir o sitio da Internet&quot;. Que deber&iacute;a facer?
+
+A: Use Firefox ou calquera outro navegador compat&iacute;bel con Internet, xa que Internet
+   Explorer non o &eacute;. Este &eacute; un de tanto defectos do Internet Explorer.
+
+
+
+
+Preguntas de administrador:
+===========================
+
+<a name="25"><a href="#25">Q</a>: Non podo instalar un servidor web como fexsrv, porque non te&ntilde;o permisos de root
+   Hai algunha versi&oacute;n en puro CGI de F*EX que funcione cun servidor web Apache?
+
+A: F*EX est&aacute; intimamente unido a fexsrv por varias raz&oacute;ns (rendemento, l&iacute;mite de
+   tama&ntilde;o de ficheiro, concepto de sesi&oacute;nt, etc.) e non se pode executar como CGI
+   con Apache.
+   Pero p&oacute;delle botar un ollo a
+   <a href="http://gpl.univ-avignon.fr/filez/">http://gpl.univ-avignon.fr/filez/</a>
+   <a href="http://freshmeat.net/projects/eventh/">http://freshmeat.net/projects/eventh/</a>
+   <a href="http://www.schaarwaechter.de/sp/projekte/dateiaustausch.html">http://www.schaarwaechter.de/sp/projekte/dateiaustausch.html</a> (s&oacute; en alem&aacute;n!)
+   que implementa un intercambio de ficheiros como puros CGI, pero cun tama&ntilde;o l&iacute;mite de 2 GB por ficheiro.
+
+
+<a name="26"><a href="#26">Q</a>: F*EX non funciona! Non podo conectarme co meu navegador web!
+
+A: Comprobe o seu enrutamento, filtros ip e configuraci&oacute;n de devasa.
+   Tam&eacute;n comprobe se o seu xinetd est&aacute; ligado cun tcp-wrapper e config&uacute;reo
+   correctamente (hosts.allow).
+   F*EX necesita o porto 80/tcp (HTTP) e o porto opcional 443/tcp (HTTPS).
+
+
+<a name="27"><a href="#27">Q</a>: F*EX &eacute; complicado de m&aacute;is! Necesito algo m&aacute;is simplificado.
+
+A: Probe <a href="http://www.home.unix-ag.org/simon/woof.html">http://www.home.unix-ag.org/simon/woof.html</a>
+
+
+<a name="28"><a href="#28">Q</a>: Como podo integrar F*EX no xestor de usuarios do meu sitio?
+
+A: F*EX ten varios m&oacute;dulos de autenticaci&oacute;n:
+   local, RADIUS, LDAP, mailman e POP
+   Para os 4 &uacute;ltimos, contacte por favor co autor <a href="mailto:framstag@rus.uni-stuttgart.de">framstag@rus.uni-stuttgart.de</a>
+
+
+<a name="29"><a href="#29">Q</a>: Que que todos os meus usuarios locais poidan usar F*EX automatimente. Como?
+
+A: Perm&iacute;taos rexistrarse a si mesmos con http://yourfexserver/fur
+   Ten que editar lib/fex.ph e establecer (exemplo):
+   @local_hosts = qw(127.0.0.1 10.10.100.0-10.10.255.255);
+   @local_domains = qw(flupp.org ulm.sub.net);
+   (Por suposto, ten que engadir os seus servidores/redes locais reais!)
+
+
+<a name="30"><a href="#30">Q</a>: Necesito m&aacute;is seguranza! Como podo activar HTTPS?
+
+A: Lea doc/SSL e busque &quot;fop_auth&quot; en doc/concept
+
+
+<a name="31"><a href="#31">Q</a>: Necesito un aspecto corporativo. Como podo configurar F*EX neste sentido?
+
+A: Vexa a vari&aacute;bel @H1_extra en fex.ph e pode engadir o c&oacute;digo HTML a
+   htdocs/header.html
+
+A: Vexa htdocs/fup_template.html, modif&iacute;queo &aacute;s s&uacute;as necesidades e &uacute;seo como a s&uacute;a
+   p&aacute;xina de inicio.
+
+
+<a name="32"><a href="#32">Q</a>: F*EX &eacute; demasiado complicado para os meus pobres usuarios. Necesito un formulario de
+   carga simmplificado.
+
+A: Vexa htdocs/fup_template.html
+
+
+<a name="33"><a href="#33">Q</a>: Quero que os correos Ccc a (ao usuario admin) se env&iacute;en a outro enderezo.
+
+A: Use procmail ou escriba OTHERADDRESS en /home/fex/.forward
+
+
+<a name="34"><a href="#34">Q</a>: Podo obter unha versi&oacute;n localizada no meu idioma?
+
+A: Sempre coa s&uacute;a axuda, si. Contacte co autor <a href="mailto:framstag@rus.uni-stuttgart.de">framstag@rus.uni-stuttgart.de</a>
+
+
+Preguntas en miscel&aacute;nea:
+========================
+
+<a name="35"><a href="#35">Q</a>: F*EX &eacute; fant&aacute;stico! Podo unirme ao equipo de desenvolvemento? Que hai que facer?
+
+A: Contacte co autor <a href="mailto:framstag@rus.uni-stuttgart.de">framstag@rus.uni-stuttgart.de</a>
+   As funcionalidades buscadas son:
+
+     - un complemento F*EX para o Thunderbird ou o Outlook
+     - m&aacute;is (outras) idiomas dispo&ntilde;&iacute;beis
+
+</PRE></HTML>
diff --git a/locale/galician/htdocs/index.html b/locale/galician/htdocs/index.html
new file mode 100644 (file)
index 0000000..3bea7b5
--- /dev/null
@@ -0,0 +1,133 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
+<head><title>F*EX - File EXchange</title></head><body>
+<center>
+<h1>F*EX - Frams' Fast File EXchange</h1>
+<img src="logo.jpg">
+</center>
+<p>
+F*EX (Frams' Fast File EXchange) é un servizo para enviar voluminosos
+(grandes, enormes, xigantes, ...) ficheiros dun usuario A a un usuario
+B.
+</p><p>
+O remisor carga o ficheiro no servidor F*EX usando <a href="/fup">un formulario WWW</a> e o receptor automaticamente obtén unha notificación vía correo cun enderezo URL para descargar. 
+</p><p>
+
+Vostede dirá:
+</p><blockquote><em>
+  Por que necesito outro servizo para transferencia de ficheiros?!<br>Teño correo, ftp, ssh e mesmo sendfile!
+</em></blockquote>
+<p>
+Ao que respondo:
+</p><blockquote><em>
+  Aínda así necesita F*EX :-)
+</em></blockquote>
+<p>
+  
+Por exemplo, quere enviar ao seu amigo o video das últimas vacacións (1 GB).&nbsp; Ten as seguintes posibilidades (e problemas):</p><p>
+
+</p><ul>
+  <li><h3>o envío dun DVD vía correo postal</h3><p>
+      Nin o vou considerar - vivimos na época actual 
+      <<my @x=gmtime(time); $x[5]-69;>> 
+      despois da invención da Internet!
+      O envío de soportes multimedia (físicos) é para avoíñas.
+      </my></p><p>
+  </p></li><li><h3>o uso do correo</h3><p>
+A maior parte dos servidores de correo teñen un límite de 10 MB por
+mensaxe e unha cota de uso de disco duns poucos 100 MB por usuario ou
+mesmo menor. </p><p>
+  </p></li><li><h3>uucp</h3><p>E por que non escritura cuneiforme en laxes de pedra?
+      </p><p>
+  </p></li><li><h3>ssh</h3><p>Ou ben tes os contrasinais dos teus amigos ou ben terás que darlles a túa
+      - fai falta iso só para transferir un ficheiro?
+      </p><p>
+  </p></li><li><h3>ftp</h3><p>
+      </p><ul>
+        <li>Usando que servidor ftp?
+        </li><li>Usando que conta, que contrasinal?
+        </li><li>Non se lle pasará pola cabeza enviar contrasinais sen cifrar e ficheiros na incerta Internet?
+        </li><li>O seu proxy permite o ftp pasivo?
+        </li><li>No caso de ftp anónimo:
+          <ul>
+            <li>Permite cargas de 1 GB?
+            </li><li>Permite eliminar a carga despois?
+            </li><li>Quen pode descargar ademais o seu ficheiro?
+            </li><li>Quen pode eliminar o seu ficheiro?
+            </li><li>Ten
+que enviarlle ao seu amigo/a o URL de descarga, el ten que informarte
+de que o descargou correctamente, despois ten vostede que eliminalo.<br>
+                Definitivamente: unha merda pinchada nun pao.
+          </li></ul>
+      </li></ul>
+      <p>
+  </p></li><li><h3>http</h3><p>
+      </p><ul>
+        <li>Nin carga predeterminada nin xestión de usuario - debe ser programada</li>
+      <li>Sen autonotificación
+        </li>
+<li>Sen autoeliminado
+        </li><li>Moi poucos servidores http poden xestionar ficheiros superiores a 2 GB
+      </li></ul>
+      <p>
+  </p></li><li><h3>sendfile</h3><p>
+      </p><ul>
+        <li>Executas un UNIX e tes o sendfile instalado?
+        </li><li>O teu receptor executa UNIX e ten o sendfile instalado?
+        </li><li>Nin ti nin o teu receptor ten unha devasa que bloquee o porto 487?
+      </li></ul>
+      <p>
+  </p></li><li><h3>servizos comerciais como DropLoad, ALLPeers, YouSendIt, etc</h3><p>
+      </p><ul>
+        <li>Que límite teñen (maiormente: &lt; 2 GB)?
+        </li><li>Como andan de seguranza e privacidade: estarán os seus ficheiros gardados e seguros?
+        </li><li>Están baseados en software aberto ou en privativo?
+        </li><li>Están accesíbeis con calquera outro navegador ou cómpre usar java, active-X, flash ou outros complementos endemoñados?
+        </li><li>Funcionarán alén duns poucos meses?<br>
+           (DropLoad, ALLPeers e drop.io xa pecharon as súas portas)
+      </li></ul>
+      <p>
+</p></li></ul>
+<p>
+Se respondeu só unha vez "non" ás cuestións anteriores, entón necesita F*EX.
+</p><p>
+</p><h2>Principais funcionalidades de F*EX</h2>
+<p>
+</p><ul>
+  <li>transferencia de ficheiro ou&nbsp; tamaño virtualmente ilimitada
+  </li><li>receptor e remitente só necesitan un programa de correo e un navegador web -
+      de calquer clase, non teñen que instalar ningún software
+  </li><li>REENVIAR and REOBTER para retomar despois de fallos de ligazón ata que se envíe o último byte
+  </li><li>autonotificación de receptor
+  </li><li>autoeliminación despois da descarga
+  </li><li>autoeliminación despois dunha data de expiración (predeterminada: 5 días)
+  </li><li>os usuarios con plenos dereitos poden crear subusuarios, que poden envíar soamente a este usuario pleno
+  </li><li>os usuarios con plenos dereitos poden crear grupos, en analoxía coas listas de correo, pero para ficheiros
+  </li><li>sen mantemetno: non se necesita interacción acompañando a creación de novas contas con F*EX
+  </li><li>o envío a múltiples receptores necesita capacidade de almacenamento no servidor só unha vez
+  </li><li>F*EX é un servizo web por HTTP e non necesita túneles nin devasas
+  </li><li>permite a distribución de fluxos, tamén
+  </li><li>para usuarios reais de UNIX, hai aplicativos da shell como fexsend e fexget que evitan o uso impertinente de navegador
+  </li><li>o protocolo e o código fonte están dispoñíbeis libremente (Perl Artistic)
+</li></ul>
+
+<h2>Falemos de SEX</h2>
+F*EX ten un compañeiro: Stream EXchange (SEX).<p>Pode
+imaxinar SEX como unha rede de tubarías UNIX cun reenviador (relay)
+entre elas.
+Isto pode ser moi práctico para intercambiar datos entre o usuario A e
+o usuario B alí onde A e B non poden establecer unha conexión directa,
+pero ambos poden conectar vía HTTP ao servidor
+SEX. Para unha integración perfecta na cadea de ferramentas UNIX, hai
+ferramentas de shell como sexsend e sexget.
+</p><p>
+A autenticación é a mesma que con F*EX.
+
+</p><h2>Quedanlle preguntas?</h2>Vexa a <a href="/FAQ.html">FAQ</a>
+
+<p>
+</p><address>contacto: <a href="mailto:$SERVER_ADMIN$">fexmaster</a></address>
+
+</body></html>
diff --git a/locale/galician/lang.html b/locale/galician/lang.html
new file mode 100644 (file)
index 0000000..c835df4
--- /dev/null
@@ -0,0 +1 @@
+galego
diff --git a/locale/galician/lib/fup.pl b/locale/galician/lib/fup.pl
new file mode 100644 (file)
index 0000000..fb730ce
--- /dev/null
@@ -0,0 +1,35 @@
+# config for F*EX CGI fup
+
+$info_1 = $info_login = <<EOD;
+<p><hr><p>
+<a href="/">F*EX (File EXchange)</a>
+é un servizo para enviar (grandes, enormes, xigantes, ...) ficheiros.
+<p>
+O remitente (vostede) carga o ficheiro no servidor F*EX e o receptor obtén automaticamente
+unha notificación vía correo cun enderezo URL para descargalo.<br>
+Despois de descargalo ou tras $keep_default días o servidor elimina o ficheiro.
+F*EX non é un arquivo!
+<p>
+Vexa máis información en <a href="/FAQ.html">preguntas e respostas</a>.
+<p><hr><p>
+<address>
+  <a href="mailto:$ENV{SERVER_ADMIN}">$ENV{SERVER_ADMIN}</a><br>
+</address>
+EOD
+
+$info_2 = <<EOD;
+<p><hr><p>
+Despois de remitilo verá unha barra de progreso de carga 
+(se ten o javascript activado e permite as xanelas emerxentes).
+<p>
+<em>NOTA: A maior parte dos navegadores non poden cargar ficheiros > 2 GB!</em><br>
+Se o seu ficheiro é maior, ten que usar un <a href="/fuc?show=tools">cliente F*EX</a> especial.<br>
+Tamén pode necesitalo para retomar cargas interrompidas. O seu navegador non pode facelo.
+<p>
+Aviso: algúns proxies HTTP como privoxy retardan a barra de progreso de carga!<br>
+Pode querer desactivar a intermediación do proxy $ENV{SERVER_NAME} se se encontra con este problema.
+<p>
+Usuarios do Firefox: non prema en [ESC] porque isto abortará a carga!
+<p>
+Vexa máis información na <a href="/FAQ.html">FAQ<a>.
+EOD
diff --git a/locale/galician/lib/reactivation.txt b/locale/galician/lib/reactivation.txt
new file mode 100644 (file)
index 0000000..7289245
--- /dev/null
@@ -0,0 +1 @@
+A súa conta F*EX reactivouse correctamente
diff --git a/locale/german/htdocs/FAQ.html b/locale/german/htdocs/FAQ.html
new file mode 100644 (file)
index 0000000..43d4140
--- /dev/null
@@ -0,0 +1,260 @@
+<HTML>
+<h3>Dieses Dokument ist veraltet. Bitte benutzen Sie die
+    <a href="/FAQ/meta.html?locale=english">englische FAQ</a>.</h3>
+<PRE>
+Allgemeine Fragen:
+==================
+
+F: Warum der Name &quot;F*EX&quot; und nicht einfach &quot;FEX&quot;?
+
+A: Zur Zeit der Ver&ouml;ffentlichung existierte bereits ein (&auml;lteres) Programm 
+   namens &quot;FEX&quot; auf freshmeat.net.
+   
+
+F: Warum nicht einen kommerziellen Dienst benutzen wie
+   DropLoad, ALLPeers, YouSendIt, etc?
+   
+A: Solche Dienste haben ein Limit von 2 GB oder sogar weniger.
+   Es ist unklar wie es bei ihnen mit Sicherheit und Datenschutz aussieht.
+   Sie basieren nicht auf Open Source.
+   Es gibt keine UNIX (CLI) Clients f&uuml;r sie.
+   Sie brauchen Java, Active-X, Flash oder andere b&ouml;sartige Plugins.
+   Es ist unklar wie lange sie noch existieren werden - DropLoad und ALLPeers
+   haben bereits aufgeh&ouml;rt zu existieren.
+
+
+F: Warum ein Kamel als Logo?
+
+A: Das Logo war inspiriert durch das Perl Kamel, zu Grunde liegt aber ein 
+   Pl&uuml;schkamel von Steiff welches auf unserem Renn-Tandem mitf&auml;hrt. 
+   Das Logo wurde von meiner Stokerin Beate gezeichnet, siehe
+   <a href="http://fex.rus.uni-stuttgart.de/Vortrag/tosa.html">http://fex.rus.uni-stuttgart.de/Vortrag/tosa.html</a>
+
+
+F: Wo kann ich die F*EX Sourcen bekommen?
+
+A: <a href="http://fex.rus.uni-stuttgart.de/fex.html">http://fex.rus.uni-stuttgart.de/fex.html</a>
+
+
+F: Was braucht man um F*EX installieren zu k&ouml;nnen?
+
+A: Ein UNIX Server mit DNS Eintrag und smtp f&uuml;r ausgehende E-Mail.
+   Und Sie m&uuml;ssen root auf diesem Server sein.
+   
+
+F: Was ist DNS und smtp?
+
+A: Installieren Sie kein F*EX. Es ist jenseits Ihres Horizonts.
+
+
+F: Wer ist der Autor?
+
+A: Ulli Horlacher &lt;<a href="mailto:framstag@rus.uni-stuttgart.de">framstag@rus.uni-stuttgart.de</a>&gt;
+
+
+F: Unter welcher Lizenz l&auml;uft F*EX?
+
+A: Perl Artistic Freie Software, siehe <a href="http://fex.rus.uni-stuttgart.de/doc/Licence">http://fex.rus.uni-stuttgart.de/doc/Licence</a>
+
+
+F: Gibt es eine F*EX Mailing Liste?
+
+A: <a href="https://listserv.uni-stuttgart.de/mailman/listinfo/fex">https://listserv.uni-stuttgart.de/mailman/listinfo/fex</a>
+
+
+F: Wo kann ich einen kommerziellen Support f&uuml;r F*EX bekommen?
+
+A: Nehmen Sie Kontakt auf mit <a href="mailto:fex@nepustil.net">fex@nepustil.net</a> <a href="http://www.nepustil.net/">http://www.nepustil.net/</a>
+
+
+F: Ich habe noch weitere Fragen bez&uuml;glich dieses Dokuments!
+
+A: Fragen Sie den Autor &lt;<a href="mailto:framstag@rus.uni-stuttgart.de">framstag@rus.uni-stuttgart.de</a>&gt;
+
+
+Benutzerfragen:
+===============
+
+<a name="1"><a href="#1">Q</a>: Was ist die &quot;auth-ID&quot;?
+
+A: Mit Hilfe der auth-ID werden Sie auf dem Server als ein authorisierter 
+   Benutzer identifiziert. Die auth-ID wird zuerst vom Administrator oder 
+   durch automatische Registrierung vergeben und kann sp&auml;ter durch Sie 
+   ge&auml;ndert werden. Bitte beachten Sie dass man hier keine wertvollen
+   Passw&ouml;rter verwenden sollte weil diese unverschl&uuml;sselt auf dem 
+   Server liegen.
+
+
+F: Kann ich einen HTTP-Proxy verwenden?
+
+A: Ja
+
+
+F: Ich habe eine RIESIGE Datei an eine falsche Adresse versendet. Jetzt 
+   bekomme ich eine Fehler-Meldung vom System. Mu&szlig; ich die RIESIGE Datei
+   noch mal hochhladen?
+
+A: Nein, das ist nicht n&ouml;tig. Sie k&ouml;nnen die Datei einfach weiterleiten 
+   mit Hilfe von <a href="http://$HTTP_HOST$/rup">http://$HTTP_HOST$/rup</a>
+
+
+F: Ich habe eine RIESIGE Datei versendet doch leider habe ich noch einen 
+   Empf&auml;nger vergessen. Mu&szlig; ich die RIESIGE Datei noch mal hochladen?
+
+A: Nein, das ist nicht n&ouml;tig. Sie k&ouml;nnen die Datei einfach kopieren und
+   weiterleiten mit Hilfe von <a href="http://$HTTP_HOST$/foc">http://$HTTP_HOST$/foc</a>
+
+
+F: Warum kann ich keine Dateien gr&ouml;&szlig;er als 2 GB mittels Web-Browser 
+   hochladen?
+
+A: Alle Web-Browser die mir bekannt sind haben Fehler in ihrer HTML-FORM
+   Implementierung. Dadurch ergibt sich der Limit von 2 GB, manchmal 4 GB.
+
+   Sie m&uuml;ssen einen speziellen F*EX Client benutzen um Dateien gr&ouml;&szlig;er 
+   als 2 GB hochzuladen, siehe <a href="http://$HTTP_HOST$/tools.html">http://$HTTP_HOST$/tools.html</a>
+
+
+F: Mein Download brach fr&uuml;hzeitig ab. Kann ich ihn wieder aufnehmen?
+
+A: F*EX unterst&uuml;tzt Download-Wiederaufnahme, aber Ihr Client muss es ebenso 
+   unterst&uuml;tzen. Firefox zB fehlt diese HTTP Eigenschaft, daher ben&ouml;tigen 
+   Sie einen anderen Client wie opera, wget oder fexget.
+
+
+F: Mein Upload brach fr&uuml;hzeitig ab. Kann ich ihn wieder aufnehmen?
+
+A: F*EX unterst&uuml;tzt Upload-Wiederaufnahme, aber Ihr Client muss es ebenso 
+   unterst&uuml;tzen. Kein Web-Browser hat diese Unterst&uuml;tzung, Sie 
+   ben&ouml;tigen df&uuml;r einen speziellen F*EX Client wie
+   fexsend, schwuppdiwupp oder F*IX.
+   Siehe <a href="http://$HTTP_HOST$/tools.html">http://$HTTP_HOST$/tools.html</a>
+
+
+F: Wenn ich die Taste [ESC] in Firefox dr&uuml;cke, dann wird der Upload 
+   abgebrochen. Warum?
+
+A: Das ist die eingebaute Funktion von Firefox: ESC unterbricht die 
+   laufende Operation. 
+   Einfache L&ouml;sung des Problems: 
+   [ESC] sollte man nicht dr&uuml;cken.
+   Komplizierte L&ouml;sung: 
+   bitten Sie die Firefox-Entwickler, eine Tastatur-Konfiguration hinzuzuf&uuml;gen
+
+
+F: Daten mit F*EX zu versenden ist einfach, wie kann ich aber Daten
+   von anderen (von der Aussenwelt) empfangen?
+
+A: Registrieren Sie sie als Ihre Subuser oder legen Sie eine F*EX Gruppe an.
+   
+
+F: Es kommt manchmal vor dass ich eine Datei mehrmals herunterladen kann, 
+   insbesondere wenn ich die Downloads schnell wiederhole. Ist etwa das 
+   automatische L&ouml;schen fehlerhaft?
+
+A: Der F*EX-Server wartet 1 Minute lang nach dem ersten erfolgreichen Download.
+   In dieser Zeit kann man die Datei nochmal herunterladen. Diese Wartezeit 
+   ist notwendig weil manche idiotische &quot;Download Manager&quot; die Datei gleich 
+   mehrmals anfordern. Ohne die Wartezeit w&uuml;rden sie dem Benutzer einen
+   Fehler melden.
+
+A: Ihr Fexmaster hat die Variable AUTODELETE auf DELAY gesetzt. Das hat
+   zur Folge dass das automatische L&ouml;schen nur einmal t&auml;glich stattfindet.
+
+A: Power-Benutzer (sehen Sie die Software-Quelltexte!) k&ouml;nnen den Flag
+   &quot;nach dem Download nicht l&ouml;schen&quot; setzen.
+
+
+F: Die Standard Aufbewahrungszeit ist mir zur kurz. Wie kann ich diesen Wert 
+   erh&ouml;hen?
+
+A: Benutzen Sie fexsend, fragen Sie Ihren Fexmaster oder lesen Sie die 
+   Quelltexte :-)
+
+
+F: Ich kann Dateien mit dem Internet Explorer nicht herunterladen, es
+   meldet mir &quot;Die Internet-Seite ist nicht verf&uuml;gbar&quot;. Was soll ich tun?
+
+A: Benutzen Sie Firefox oder anderen Internet-kompatiblen Web-Browser, was der 
+   Internet Explorer eben nicht ist. Das ist nur einer seiner vielen Bugs.
+
+
+Administrator Fragen:
+=====================
+
+F: Ich kann keinen Web-Server wie fexsrv aufsetzen, mir fehlen dazu die
+   Rechte. Gibt es vielleicht eine reine CGI-Version von F*EX die
+   unter dem Apache Web-Server l&auml;uft?
+
+A: fexsrv ist ein fester Bestandteil von F*EX, dazu gibt es viele Gr&uuml;nde 
+   (Leistung, maximale Dateigr&ouml;&szlig;e, Session Konzept usw). So kann F*EX nicht 
+   als CGI unter Apache laufen, Sie k&ouml;nnten sich aber das hier anschauen:
+   
+   <a href="http://gpl.univ-avignon.fr/filez/">http://gpl.univ-avignon.fr/filez/</a>
+   <a href="http://freshmeat.net/projects/eventh/">http://freshmeat.net/projects/eventh/</a>
+   <a href="http://www.schaarwaechter.de/sp/projekte/dateiaustausch.html">http://www.schaarwaechter.de/sp/projekte/dateiaustausch.html</a>
+   
+   Das sind einfache CGI-Implementierungen von Daten-&Uuml;bertragungssystemen,
+   jedoch begrenzt auf 2 GB.
+   
+
+F: F*EX funktioniert &uuml;berhaupt nicht! Es kommt keine Verbindung zum Server mit 
+   meinem Web-Browser zustande!
+
+A: Pr&uuml;fen Sie Ihr Routing, IP-Filter und die Firewall-Einstellungen.
+   Pr&uuml;fen Sie auch ob Ihr xinetd mit dem TCP-Wrapper verlinkt ist und
+   konfigurieren Sie es richtig (hosts.allow).
+   F*EX braucht den Port 80/tcp (HTTP) und optional den Port 443/tcp
+
+
+F: F*EX ist mir zu kompliziert! Ich brauche was einfacheres!
+
+A: Probieren Sie <a href="http://www.home.unix-ag.org/simon/woof.html">http://www.home.unix-ag.org/simon/woof.html</a>
+
+F: Wie kann ich F*EX in das bestehende Benutzer-Management in meinem Netzwerk 
+   integrieren?
+
+A: F*EX hat verschiedene Authentifizierungs-Module:
+   lokal, RADIUS, LDAP, mailman und POP
+   Bez&uuml;glich der letzten 4 kontaktieren Sie bitte den Autor.
+
+
+F: Ich m&ouml;chte dass alle meine lokalen Benutzer automatisch F*EX benutzen 
+   k&ouml;nnen. Wie geht das?
+
+A: Lassen Sie sie sich selbst registrieren.
+   Dazu m&uuml;ssen Sie lib/fex.ph editieren (Beispiel):
+   
+   @local_hosts = qw(127.0.0.1 10.10.100.0-10.10.255.255);
+   @local_domains = qw(flupp.org ulm.sub.net);
+
+   Nat&uuml;rlich m&uuml;ssen Sie Ihre echten Hosts/Networks eigeben!
+
+
+F: Ich brauche mehr Sicherheit! Wie kann ich HTTPS benutzen?
+
+A: Lesen Sie doc/SSL und schauen Sie auch nach &quot;fop_auth&quot; in doc/concept 
+
+
+F: Die F*EX-Startseite sollte an unsere Firmen-Seiten angepa&szlig;t werden. 
+   Wie kann ich F*EX entsprechend konfigurieren?
+
+A: Siehe Variable @H1_extra in fex.ph, ausserdem k&ouml;nnen Sie htdocs/header.html
+   hinzuf&uuml;gen.
+
+A: Nehmen Sie fup_template.html, modifizieren Sie die Vorlage nach Ihren 
+   W&uuml;nschen und nutzen Sie diese als Ihre Startseite.
+
+
+Sonstige Fragen:
+================
+
+F: F*EX ist gro&szlig;artig! Wie kann ich mich dem Entwicklungs-Team anschlie&szlig;en? 
+   Was mu&szlig; noch gemacht werden?
+
+A: Kontaktieren Sie den Autor wegen:
+
+   - ein F*EX plugin f&uuml;r Outlook
+   
+   - weiteren &Uuml;bersetzungen in andere Sprachen - Latein w&auml;re toll! :-) 
+</PRE></HTML>
diff --git a/locale/german/htdocs/index.html b/locale/german/htdocs/index.html
new file mode 100644 (file)
index 0000000..7e969bc
--- /dev/null
@@ -0,0 +1,135 @@
+<HTML> 
+<HEAD><TITLE>F*EX - File EXchange</TITLE></HEAD>
+<BODY>
+<center>
+<h1>F*EX - Frams' Fast File EXchange</h1>
+<IMG SRC="logo.jpg">
+</center>
+<p>
+F*EX (Frams' Fast File EXchange) ist ein Dienst, um gro&szlig;e (sehr gro&szlig;e, riesige,
+gigantische, ...) Dateien von User A zu User B zu senden.
+<p>
+Der Absender l&auml;dt die Datei &uuml;ber ein  
+<a href="/fup">WWW upload Formular</a> auf den F*EX Server hoch und der Empf&auml;nger 
+bekommt automatisch eine Benachrichtigungs-E-Mail mit der Download-URL.
+<p>
+
+Sie fragen vielleicht:
+<blockquote><em>
+  Warum brauche ich noch einen Service f&uuml;r Dateitranfer?!<br>
+  Ich habe E-Mail, FTP, SSH und sogar sendfile!
+</em></blockquote>
+<p>
+Ich sage:
+<blockquote><em>
+  Sie brauchen F*EX trotzdem :-)
+</em></blockquote>
+<p>
+  
+Sie m&ouml;chten zum Beispiel einem Freund das letztes Urlaubsvideo senden (1 GB).
+Sie haben dazu folgende M&ouml;glichkeiten (und Probleme):<p>
+
+<ul>
+  <li><h3>eine DVD per Briefpost verschicken</h3><p>
+      Steht au&szlig;er Frage - wir leben im Jahr 
+      <<my @x=gmtime(time); $x[5]-69;>> 
+      nach der Erfindung des Internet!
+      Versand von (Hardware-)Medien ist etwas f&uuml;r Opas.
+      <p>
+  <li><h3>E-Mail benutzen</h3><p>
+      Die meisten E-Mail Server haben ein Limit von 10 MB pro E-Mail
+      und ein Speicherkontingent von wenigen 100 MB pro User oder sogar weniger.
+      <p>
+  <li><h3>UUCP</h3><p>
+      Warum nicht gleich Keilschrift-Zeichen in Tonplatten eindr&uuml;cken?
+      <p>
+  <li><h3>SSH</h3><p>
+      Sie haben das Passwort Ihres Freundes oder sind gewillt, ihm Ihres zu geben
+      - nur f&uuml;r einen Datei-Transfer?
+      <p>
+  <li><h3>FTP</h3><p>
+      <ul>
+        <li>Welchen FTP Server benutzen?
+        <li>Welchen Account benutzen, welches Passwort?
+        <li>St&ouml;rt es Sie unverschl&uuml;sselte Passw&ouml;rter &uuml;ber das unsichere Internet zu &uuml;bertragen?
+        <li>Unterst&uuml;tzt Ihr Proxy passives FTP?
+        <li>Im Fall von anonymen FTP:
+          <ul>
+            <li>Erlaubt es 1 GB Uploads?
+            <li>Kann man den Upload nach dem Transfer wieder l&ouml;schen?
+            <li>Wer kann Ihre Datei noch downloaden?
+            <li>Wer noch kann Ihre Datei l&ouml;schen?
+            <li>Sie m&uuml;ssen Ihrem Freund die Download-URL zukommen lassen und er
+                muss Sie &uuml;ber den erfolgreichen Download informieren, danach m&uuml;ssen Sie die Datei l&ouml;schen.<br>
+                Alles in Allem: eine nervige Sache.
+          </ul>
+      </ul>
+      <p>
+  <li><h3>HTTP</h3><p>
+      <ul>
+        <li>Es gibt keine standardm&auml;&szlig;ige Upload- und User-Verwaltung (muss erst programmiert werden)
+        <li>Keine Auto-Benachrichtigung
+        <li>Kein automatisches L&ouml;schen
+        <li>Sehr wenige HTTP Server k&ouml;nnen mit Dateien gr&ouml;&szlig;er 2 GB umgehen
+      </ul>
+      <p>
+  <li><h3>sendfile</h3><p>
+      <ul>
+        <li>Sie benutzen UNIX und haben sendfile installiert?
+        <li>Ihr Empf&auml;nger benutzt UNIX und hat sendfile installiert?
+        <li>Weder Ihr Empf&auml;nger noch Sie selbst haben eine Firewall die Port 487 blockiert?
+      </ul>
+      <p>
+  <li><h3>Kommerzielle Dienste wie DropLoad, ALLPeers, YouSendIt, etc</h3><p>
+      <ul>
+        <li>Welche Beschr&auml;nkungen haben sie? (meist: &lt; 2 GB)?
+        <li>Was ist mit Sicherheit und Privatsph&auml;re: werden Ihre Dateien sicher und gesch&uuml;tzt sein?
+        <li>Basieren sie auf Open Source- oder propriet&auml;rer Software?
+        <li>Sind sie mit jedem Browser benutzbar oder braucht man Java, Active-X, Flash oder andere &uuml;ble Plugins?
+        <li>Werden sie l&auml;nger als ein paar Monate existieren?<br>
+           (DropLoad und ALLPeers haben ihren Betrieb schon eingestellt)
+      </ul>
+      <p>
+</ul>
+<p>
+Wenn Sie auch nur eine der Fragen von oben mit "Nein" beantwortet haben, dann brauchen Sie F*EX.
+<p>
+<h2>Hauptmerkmale von F*EX</h2>
+<p>
+<ul>
+  <li>Datei-Transfer mit praktisch unbegrenzter Dateigr&ouml;&szlig;e
+  <li>Empf&auml;nger und Absender brauchen lediglich ein E-Mail Programm und einen Web-Browser -
+      es muss keine zus&auml;tzliche Software installiert werden
+  <li>RESEND und REGET f&uuml;r Wiederaufnahme bei Link Fehlern beim letzten gesendeten Byte
+  <li>Auto-Benachrichtigung der Empf&auml;nger
+  <li>automatisches L&ouml;schen nach Download
+  <li>automatisches L&ouml;schen nach Ablaufdatum (Standard: 5 Tage)
+  <li>Full-User k&ouml;nnen Sub-User erstellen, die dann nur an den Full-User senden d&uuml;rfen
+  <li>Full-User k&ouml;nnen Gruppen erstellen, eine Analogie zu Mailinglisten, aber f&uuml;r Dateien
+  <li>wartungsfrei: bis auf die Erstellung neuer F*EX Accounts ist keine Interaktion eines Admins erforderlich
+  <li>F&uuml;r das Senden an mehrere Empf&auml;nger wird auf dem Server nur einmal Speicherplatz verbraucht
+  <li>F*EX ist ein HTTP-Web-Dienst und ben&ouml;tigt keine Firewall-Tunnel
+  <li>Unterst&uuml;tzung f&uuml;r Streams
+  <li>f&uuml;r UNIX Benutzer gibt es die Shell-Programme fexsend und fexget, um den l&auml;stigen Gebrauch von Webbrowsern zu umgehen
+  <li>Protokoll und Quellcode sind frei verf&uuml;gbar (Perl Artistic)
+</ul>
+
+<h2>Lassen Sie uns &uuml;ber SEX sprechen</h2>
+F*EX hat einen Begleiter: Stream EXchange (SEX).<p>
+Man kann sich SEX als netzwerkweite UNIX Pipes mit einem Relay dazwischen vorstellen.
+Das kann hilfreich sein, wenn man Daten von User A zu User B leiten will,
+ohne dass A und B eine direkte Verbindung zueinander aufbauen k&ouml;nnen, aber beide sich
+&uuml;ber HTTP zum SEX Server verbinden k&ouml;nnen.
+F&uuml;r eine nahtlose Integration in die UNIX Programm Reihe,
+gibt es die Shell-Programme sexsend und sexget.
+<p>
+Die Authentifizierung ist dieselbe wie bei F*EX.
+
+<h2>Noch Fragen?</h2>
+Siehe <a href="/FAQ.html">FAQ (Fragen und Antworten)</a>
+
+<p>
+<ADDRESS>Kontakt: <A HREF="mailto:$SERVER_ADMIN$">fexmaster</A></ADDRESS>
+
+</BODY>
+</HTML> 
diff --git a/locale/german/lang.html b/locale/german/lang.html
new file mode 100644 (file)
index 0000000..bf777b7
--- /dev/null
@@ -0,0 +1 @@
+deutsch
diff --git a/locale/german/lib/fup.pl b/locale/german/lib/fup.pl
new file mode 100644 (file)
index 0000000..ddbda20
--- /dev/null
@@ -0,0 +1,46 @@
+# lokale Konfiguration fuer F*EX CGI fup
+
+$info_1 = $info_login = <<EOD;
+<p><hr><p>
+<a href="/index.html">F*EX (File EXchange)</a>
+ist ein Dienst um gro&szlig;e (sehr gro&szlig;e, riesige, gigantische, ...) Dateien zu senden.
+<p>
+Der Absender (Sie) l&auml;dt eine Datei auf den F*EX Server hoch und der
+Empf&auml;nger bekommt automatisch eine Benachrichtigungs-E-Mail mit der Download-URL.<br> 
+Nach dem Download oder nach $keep_default Tagen l&ouml;scht der Server die Datei.<br>
+F*EX ist kein Archiv!
+<p>
+Um es zu nutzen, geben Sie Ihre Empf&auml;nger E-Mail Adresse(n) in die Felder oben ein.
+<p>
+Noch immer verwirrt?<br>
+Testen Sie F*EX, indem Sie eine Datei an sich selbst senden
+(Absender = Empf&auml;nger = Ihre E-Mail Adresse).
+<p>
+Siehe auch <a href="/FAQ.html">Fragen &amp; Antworten</a>.
+<p><hr><p>
+<address>
+  <a href="mailto:$ENV{SERVER_ADMIN}">$ENV{SERVER_ADMIN}</a><br>
+</address>
+EOD
+
+$info_2 = <<EOD;
+<p><hr><p>
+Nach dem Abschicken sehen Sie einen Upload Fortschrittsbalken
+(wenn Sie Javascript aktiviert haben und Popups erlauben).
+<p>
+<em>Bemerkung: Viele Webbrowser k&ouml;nnen keine Dateien hochladen,
+die gr&ouml;&szlig;er als 2 GB sind!</em><br> 
+Wenn Ihre Datei gr&ouml;&szlig;er ist, m&uuml;ssen Sie einen speziellen 
+<a href="/fuc?show=tools">F*EX client</a> nutzen.<br>
+Sie brauchen ihn au&szlig;erdem, wenn Sie abgebrochene Uploads
+wiederaufnehmen wollen. Ihr Webbrowser ist dazu nicht in der Lage.
+<p>
+Wenn Sie mehr als eine Datei verschicken wollen, dann verpacken Sie sie vorher in ein zip oder tar Archiv,
+z.B. mit <a href="http://www.7-zip.org/download.html">7-Zip</a>.
+<p>
+Siehe auch <a href="/FAQ.html">FAQ<a>.
+<p><hr><p>
+<address>
+  <a href="mailto:$ENV{SERVER_ADMIN}">$ENV{SERVER_ADMIN}</a><br>
+</address>
+EOD
diff --git a/locale/german/lib/reactivation.txt b/locale/german/lib/reactivation.txt
new file mode 100644 (file)
index 0000000..413dace
--- /dev/null
@@ -0,0 +1 @@
+Ihr F*EX Account wurde erfolgreich reaktiviert.
diff --git a/locale/italian/htdocs/FAQ.html b/locale/italian/htdocs/FAQ.html
new file mode 100644 (file)
index 0000000..2872df9
--- /dev/null
@@ -0,0 +1,297 @@
+<HTML>
+<h3>Questo documento non &egrave; aggiornato. Si prega di utilizzare il
+    <a href="/FAQ/meta.html?locale=english">inglese FAQ</a>.</h3>
+<PRE>
+<PRE>
+Domande Generiche:
+==================
+
+<a name="1"><a href="#1">Q</a>: Perche' il nome e' &quot;F*EX&quot; invece del piu' corto &quot;FEX&quot;?
+
+A: Al momento della pubblicazione c'era gia' un (vecchio) programma
+   nominato &quot;FEX&quot; su freashmeat.net
+
+
+<a name="2"><a href="#2">Q</a>: Perche' non usare un servizio commerciale come
+   DropLoad, ALLPeers, YouSendIt, ecc. ?
+
+A: Tutti hanno un limite di 2 GB o meno.
+   I loro livelli di sicurezza e privacy sono sconosciuti.
+   Non sono servizi basati su software open-source.
+   Non ci sono client UNIX (a linea di comando) per loro.
+   Hanno bisogno di java, Active-X, Flash o altri plug-in.
+   Non si sa quanto tempo sopravviveranno - DropLoad e ALLPeers hanno
+   gia' concluso la loro attivita'.
+
+
+<a name="3"><a href="#3">Q</a>: Perche' c'e' un cammello nel logo?
+
+A: Il logo e' stato ispirato al cammello del Perl, ma e' basato sul
+   cammello di peluche della Steiff che ci portiamo insieme con noi
+   nelle nostre corse con il tandem.
+   Il logo e' stato disegnato dal mio &quot;fuochista&quot; Beate
+   <a href="http://fex.rus.uni-stuttgart.de/Vortrag/tosa.html">http://fex.rus.uni-stuttgart.de/Vortrag/tosa.html</a>
+
+
+<a name="4"><a href="#4">Q</a>: Dovo posso prendere i sorgenti di F*EX ?
+
+A: <a href="http://fex.rus.uni-stuttgart.de/fex.html">http://fex.rus.uni-stuttgart.de/fex.html</a>
+
+
+<a name="5"><a href="#5">Q</a>: Dove posso installare F*EX ?
+
+A: In un host UNIX con una entry DNS ed un SMTP per la spedizione
+   delle e-mail. Su questo server bisogna avere i privilegi di root.
+
+
+<a name="6"><a href="#6">Q</a>: Cosa sono DNS e SMTP?
+
+A: Lascia perdere F*EX. E' al di la' delle tue possibilita'.
+
+
+<a name="7"><a href="#7">Q</a>: Chi e' l'autore ?
+
+A: Ulli Horlacher &lt;<a href="mailto:framstag@rus.uni-stuttgart.de">framstag@rus.uni-stuttgart.de</a>&gt;
+
+
+<a name="8"><a href="#8">Q</a>: Che tipo di licenza ha F*EX ?
+
+A: Perl Artistic free software, vedete <a href="http://fex.rus.uni-stuttgart.de/doc/Licence">http://fex.rus.uni-stuttgart.de/doc/Licence</a>
+
+
+<a name="9"><a href="#9">Q</a>: C'e' una mailing list per F*EX ?
+
+A: <a href="https://listserv.uni-stuttgart.de/mailman/listinfo/fex">https://listserv.uni-stuttgart.de/mailman/listinfo/fex</a>
+
+
+<a name="10"><a href="#10">Q</a>: Dove posso trovare un servizio commerciale per F*EX ?
+
+A: Contattate <a href="mailto:fex@nepustil.net">fex@nepustil.net</a> <a href="http://www.nepustil.net/">http://www.nepustil.net/</a>
+
+
+<a name="11"><a href="#11">Q</a>: Ho altre domande che non trovo in questo documento.
+
+A: Chiedete all'autore &lt;<a href="mailto:framstag@rus.uni-stuttgart.de">framstag@rus.uni-stuttgart.de</a>&gt;
+
+
+
+Domande Utente:
+===============
+
+<a name="12"><a href="#12">Q</a>: Cos'e' il campo &quot;auth-ID&quot; ?
+
+A: Il campo auth-ID e' un identificativo interno che individua l'utente;
+   e' lo username.
+   E' creato la prima volta o dall'amministratore o attraverso il processo
+   di registrazione automatico e puo' essere successivamente modificato
+   da te (che sei l'utente).
+   Pensa come se fosse una password a bassa sicurezza.
+
+<a name="13"><a href="#13">Q</a>: Posso usare un proxy HTTP ?
+
+A: Si'.
+
+
+<a name="14"><a href="#14">Q</a>: Ho caricato un file ENORME, ma ho fatto un errore di battitura
+   nell'indirizzo del destinatario. Ora mi trovo con una e-mail
+   di errore.
+   Devo effettuare di nuovo il caricamento di questo ENORME file?
+
+A: No, non e' necessario. Puoi ridirigere il file con
+   <a href="http://$HTTP_HOST$/rup">http://$HTTP_HOST$/rup</a>
+
+
+<a name="15"><a href="#15">Q</a>: Ho caricato un file ENORME ma ho dimenticato uno dei destinatari.
+   Devo ricaricarlo?
+
+
+A: No, non e' necessario. Puo' inoltrare il file con
+   <a href="http://$HTTP_HOST$/foc">http://$HTTP_HOST$/foc</a>
+
+
+<a name="16"><a href="#16">Q</a>: Posso caricare file di dimensioni &gt; di 2 GB con il mio browser !?
+
+A: Tutti i browser che conosco hanno dei bug nella loro implementazione
+   delle FORM HTML.
+   Il limite e' per lo piu' di 2 GB, molto spesso e' di 4 GB.
+   Dovete usare uno speciale client F*EX per caricare file &gt; 2GB,
+   vedete <a href="http://$HTTP_HOST$/tools.html">http://$HTTP_HOST$/tools.html</a>
+
+
+<a name="17"><a href="#17">Q</a>: Il mio download si e' bloccato prima di finire.
+   Posso riprenderlo?
+
+A: F*EX supporta il recupero del download ma anche il vostro client deve
+   supportare questa funzione. Firefox, per esempio, non ha questa funzione,
+   serve un'altro client come Opera, Wget o Fexget.
+
+
+<a name="18"><a href="#18">Q</a>: Il caricamento del mio file si e' interrotto prima di finire.
+   Posso riprenderlo?
+
+A: F*EX supporta la ripresa del caricamento interrotto di un file ma anche
+   il client deve supportarlo. Nessun browser WEB ha questa funzionalita',
+   avete bisogno di uno speciale client F*EX come fexsend, schwuppdiwupp o F*IX.
+   Vedete <a href="http://$HTTP_HOST$/tools.html">http://$HTTP_HOST$/tools.html</a>
+
+
+<a name="19"><a href="#19">Q</a>: Il mio browser WEB non puo' eseguire il client java F*IX
+   in quanto non trova l'ambiente java runtime e quindi
+   non puo' caricare la applet F*IX
+
+
+A: Manca il plug-in java per il vostro browser WEB. Sui sistemi Debian e
+   Ubuntu potete installarlo con questo comando:
+   &quot;sudo aptitude install sun-java6-plugin&quot;
+
+
+<a name="20"><a href="#20">Q</a>: Quando schiaccio [ESC] in firefox il caricamento si cancella. Perche'?
+
+A: Si tratta di una caratteristica di firefox: il tasto ESC termina
+   l'operazione corrente.
+   Soluzione semplica: non schiacciate ESC in Firefox.
+   Soluzione complessa: chiedete agli sviluppatori di Firefox di aggiungere
+   la configurazione della tastiera.
+
+
+<a name="21"><a href="#21">Q</a>: Spedire come utente F*EX e' facile, ma come posso
+   ricevere i file da altri utenti all'esterno?
+
+A: Registrali come tuoi &quot;sotto-utenti&quot; oppure crea un gruppo F*EX
+   con <a href="http://$HTTP_HOST$/fuc">http://$HTTP_HOST$/fuc</a>
+
+
+<a name="22"><a href="#22">Q</a>: Qualche volta posso scaricare un file piu' di una volta,
+   specialmente quando ripeto l'operazione velocemente.
+   La funziona di autodelete e' poco affidabile?
+
+A: Il server F*EX rende disponibile il file scaricato ancora per un
+   periodo di 1 minuto. Questo e' necessario per evitare che alcuni
+   &quot;download managers&quot; piuttosto stupidi diano errore in quanto
+   richiedono il file parecchie volte di seguito.
+
+A: Il vostro fexmaster ha impostato la variabile AUTODELETE=DELAY
+   come default, il che significa che il processo di
+   autocancellazione e' eseguita una volta al giorno.
+
+A: Certi utenti (usate i sorgenti!!!) possono impostare il flag
+   &quot;non cancellare dopo il download&quot;.
+
+
+<a name="23"><a href="#23">Q</a>: La finestra temporale prima che il file sia cancellato e' troppo
+   stretta per me, ho bisogno di allargarla. Come posso fare?
+
+A: Usate fexsend, chiedete al vostro fexmaster oppure leggete il
+   codice sorgente :-)
+
+
+<a name="24"><a href="#24">Q</a>: Durante il download di un file con Internet Explorer
+   mi dice &quot;Non posso aprire il sito Internet&quot;. Cosa posso fare?
+
+A: Usate Firefox o qualsiasi altro browser WEB compatibile con Internet,
+   dato che Internet Explorer non lo e'. Questo e' uno dei tanti bug di
+   Internet Explorer.
+
+
+
+
+Domande Amministrative:
+=======================
+
+<a name="25"><a href="#25">Q</a>: Non posso installare un server web come fexsrv perche'
+   non ho i permessi di root. Esiste una pura versione CGI
+   di F*EX eseguibile con un web server Apache ?
+
+A: F*EX e' rigidamente vincolato a fexsrv per diverse ragioni (performance,
+   limite dimensione file, concetto di sessione, etc) e non puo' essere
+   eseguito come una CGI sotto apache.
+   Potete dare un'occhiata a
+   <a href="http://gpl.univ-avignon.fr/filez/">http://gpl.univ-avignon.fr/filez/</a>
+   <a href="http://freshmeat.net/projects/eventh/">http://freshmeat.net/projects/eventh/</a>
+   <a href="http://www.schaarwaechter.de/sp/projekte/dateiaustausch.html">http://www.schaarwaechter.de/sp/projekte/dateiaustausch.html</a> (Solo in Tedesco!)
+   il quale implementa uno scambio file come pura CGI, ma ha il limite dei 2 GB.
+
+
+<a name="26"><a href="#26">Q</a>: F*EX non funziona per nulla!
+   Non mi posso connettere con il mio browser web!
+
+A: Controllate il routing, le impostazioni del firewall e di ipfilters.
+   Controllate anche che il vostro xinetd sia collegato con il
+   wrapper-tcp e sia configurato correttamente (hosts.allow).
+   F*EX necessita della porta 80/tcp (HTTP) e opzionalmente
+   della 443/tcp (HTTPS).
+
+
+<a name="27"><a href="#27">Q</a>: F*EX e' troppo complicato!
+   Mi serve qualcosa di piu' semplice.
+
+A: Provate <a href="http://www.home.unix-ag.org/simon/woof.html">http://www.home.unix-ag.org/simon/woof.html</a>
+
+
+<a name="28"><a href="#28">Q</a>: Come posso integrare F*EX nell'ambiente di gestione
+   dell'utente esistente nel mio sito?
+
+A: F*EX ha diversi moduli di autenticazione:
+   locale, RADIUS, LDAP, mailman e POP
+   Per gli ultimi 4 si prega di contattare l'autore
+   <a href="mailto:framstag@rus.uni-stuttgart.de">framstag@rus.uni-stuttgart.de</a>
+
+
+<a name="29"><a href="#29">Q</a>: Vorrei che i miei utenti locali usassero
+   F*EX automaticamente. Come??
+
+A: Lasciate che si registrino da soli con http://yourfexserver/fur
+   Dovete editare il file lib/fex.ph ed impostare (esempio):
+   @local_hosts = qw(127.0.0.1 10.10.100.0-10.10.255.255);
+   @local_domains = qw(flupp.org ulm.sub.net);
+   (Ovviamente dovete iunserire i vostri dati reali!)
+
+
+<a name="30"><a href="#30">Q</a>: Ho bisogno di maggior sicurezza! Come posso abilitare HTTPS??
+
+A: Leggete la documentazione doc/SSL ed anche cercate &quot;fop_auth&quot; in doc/concept
+
+
+<a name="31"><a href="#31">Q</a>: Ho bisogno di un look aziendale.
+   Come posso configurare F*EX per questo?
+
+A: Potete Guardare la variabile @H1_extra nel file fex.ph ed aggiungere
+   codice HTML al file htdocs/header.html
+
+A: Guardate il file htdocs/fup_template.html, modificatelo secondo i vostri
+   bisogni ed utilizzatelo come start-page.
+
+
+<a name="32"><a href="#32">Q</a>: F*EX e' troppo complicato per i miei utenti.
+   Ho bisogno di una form semplificata.
+
+A: Guardate il file htdocs/fup_template.html
+
+
+<a name="33"><a href="#33">Q</a>: Vorrei che le e-mail BCC indirizzate all'amministratore
+   fex fossero spedite ad altro indirizzo.
+
+A: Usate procmail o scrivete OTHERADDRESS in /home/fex/.forward
+
+
+<a name="34"><a href="#34">Q</a>: Posso avere una versione localizzata
+   nella mia lingua??
+
+A: Con il vostro aiuto, si'. Per favore contattate l'autore a
+   <a href="mailto:framstag@rus.uni-stuttgart.de">framstag@rus.uni-stuttgart.de</a>
+
+
+Domande varie:
+==============
+
+<a name="35"><a href="#35">Q</a>: F*EX e' ottimo! Posso lavorare con
+   il team di sviluppo?
+   Che cosa c'e' bisogno di fare?
+
+A: Contattate l'autore <a href="mailto:framstag@rus.uni-stuttgart.de">framstag@rus.uni-stuttgart.de</a>
+   Funzionalita' richieste:
+
+     - Un plug-in F*EX per thunderbird o outlook
+     - Supporto in altre lingue
+
+</PRE></HTML>
diff --git a/locale/italian/htdocs/index.html b/locale/italian/htdocs/index.html
new file mode 100644 (file)
index 0000000..43170ca
--- /dev/null
@@ -0,0 +1,128 @@
+<HTML> 
+<HEAD><TITLE>F*EX - File EXchange</TITLE></HEAD>
+<BODY>
+<center>
+<h1>F*EX - Frams' Fast File EXchange</h1>
+<IMG SRC="logo.jpg">
+</center>
+<p>
+F*EX (Frams' Fast File EXchange) e' un servizio per spedire grossi (grandi, enormi, giganti, ...) file da un utente A ad un utente B.
+<p>
+Il mittente carica il file nel server F*EX usando una
+<a href="/fup">form WWW di carico</a> mentre al destinatario viene recapitata una e-mail di notifica con un link per effettuare il download. 
+<p>
+
+Direte:
+<blockquote><em>
+  Per quale motivo ho bisogno di un altro servizio per effettuare il trasferimento dei files?!<br>
+  Per questo ho gia' la posta elettronica(e-mail), l'ftp, l'ssh ed anche il comando sendfile!
+</em></blockquote>
+<p>
+Io dico:
+<blockquote><em>
+  Nonostante tutto hai bisogno di F*EX :-)
+</em></blockquote>
+<p>
+  
+Supponiamo, per esempio, che tu voglia spedire ai tuoi amici il video delle tue ultime vacanze (1 GB). Hai le seguenti possibilita' (e problemi):<p>
+
+<ul>
+  <li><h3>spedire un DVD con il servizio postale</h3><p>
+      Fuori discussione - viviamo nell'anno
+      <<my @x=gmtime(time);print $x[5]-69>> 
+      dopo l'invenzione di Internet!
+      Spedire supporti hardware e' da trogloditi.
+      <p>
+  <li><h3>usare la posta elettronica (e-mail)</h3><p>
+      Gran parte dei server di posta elettronica hanno un limite di 10 MB per ognuna delle e-mail e la quota totale e' di meno di 100 MB per utente.
+      <p>
+  <li><h3>uucp</h3><p>
+      Perche' non usare la scrittura cuneiforme su una lastra di pietra?
+      <p>
+  <li><h3>ssh</h3><p>
+      Avete la password dei vostri amici oppure siete disposti a dargli la vostra?
+      - solo per trasferire un file?
+      <p>
+  <li><h3>ftp</h3><p>
+      <ul>
+        <li>Quale server ftp usare?
+        <li>Quale utente usare, quale password?
+        <li>Non vi dispiace mandare password e files non crittografati su Internet (insicura per definizione)?
+        <li>Il vostro proxy supporta l'ftp passivo?
+        <li>In caso di ftp anonimo:
+          <ul>
+            <li>Permette di caricare file da 1 GB?
+            <li>Permette di cancellare il file caricato subito dopo?
+            <li>Chi altri puo' fare il download del vostro file?
+            <li>Chi altri puo' cancellare il vostro file?
+            <li>Dovete mandare ai vostri amici il link per lo scarico, loro devono informarvi di avere effettuato il download del file e voi dovete cancellarlo subito dopo.<br>
+                In poche parole: una rottura di scatole.
+          </ul>
+      </ul>
+      <p>
+  <li><h3>http</h3><p>
+      <ul>
+        <li>Non esiste un programma di default - deve essere costruito.
+        <li>Non esiste l'auto-notifica.
+        <li>Non esiste l'auto-cancellazione.
+        <li>Pochissimi server HTTP possono trattare file piu' grandi di 2 GB.
+      </ul>
+      <p>
+  <li><h3>sendfile</h3><p>
+      <ul>
+        <li>Avete UNIX ed avete sendfile installato?
+        <li>Il vostro destinatario ha UNIX ed ha sendfile installato?
+        <li>Sicuri che ne' voi, ne' il vostro destinatario avete un firewall che blocca la porta 487?
+      </ul>
+      <p>
+  <li><h3>servizi commerciali come DropLoad, ALLPeers, YouSendIt, eccetera</h3><p>
+      <ul>
+        <li>Quale limite hanno (gran parte: &lt; 2 GB)?
+        <li>Cosa dire di sicurezza e privacy: i vostri file saranno salvi e sicuri?
+        <li>Sono basati su servizi open source o su proprietari?
+        <li>Sono accessibili con qualsiasi browser oppure hanno bisogno di java, active-X, flash o qualche altro plug-in?
+        <li>Sopravviveranno per piu' di qualche mese?<br>
+           (Per esempio servizi quali DropLoad, ALLPeers e drop.io hanno gia' terminato la loro attivita')
+      </ul>
+      <p>
+</ul>
+<p>
+Se avete risposto "no" ad almeno ad una delle domande, avete bisogno di 
+F*EX.
+<p>
+<h2>Principali caratteristiche di F*EX</h2>
+<p>
+<ul>
+  <li>trasferimento file di dimensioni virtualmente illimitate.
+  <li>il destinatario ed il mittente hanno bisogno solo di un programma di posta elettronica e di un browser WEB 
+      di qualsiasi tipo: non devono installare nulla.
+  <li>funzioni RESEND e REGET per riprendere l'esecuzione dopo un blocco della connessione all'ultimo byte.
+  <li>auto-notifica del destinatario.
+  <li>auto-cancellazione dopo il download.
+  <li>auto-cancellazione dopo un periodo di 5 giorni (per default).
+  <li>gli utenti "full" possono creare "sottoutenti" o utenti "limitati" i quali possono spedire solo a questo server.
+  <li>gli utenti "full" possono creare gruppi in analogia a quanto succede per le liste di distribuzione, solo che viene fatto per i file.
+  <li>manutenzione semplice: non e' necessario l'intervento dell'amministratore per la creazione di nuovi utenti F*EX.
+  <li>l'invio di file a piu' destinatari occupa nel server solo lo spazio di una sola spedizione.
+  <li>F*EX e' un web-service di tipo HTTP e non ha bisogno di "tunnels" nel firewall.
+  <li>sono supportati anche i flussi (stream).
+  <li>per gli utenti che conoscono UNIX, ci sono gli script-shell fexsend e fexget
+      che evitano l'uso di un browser-WEB.
+  <li>i protocolli ed il codice sorgente sono liberamente disponibili (Perl Artistic).
+</ul>
+
+<h2>Parliamo di SEX</h2>
+F*EX ha un compagno: Stream EXchange (SEX).<p>
+Potete immaginare  che SEX sia una specie di lunga pipe UNIX con un relay in mezzo.
+Questo servizio puo' essere utilizzato per il travaso di dati da un utente A ad un utente B quando A e B non possono stabilire una connessione diretta ma entrambi possono stabilire una connessione HTTP con il server SEX. Per una completa integrazione con i tool di concatenazione dei comandi di UNIX, ci sono gli script-shell sexsend and sexget.
+<p>
+L'autenticazione e' la stessa utilizzata da F*EX.
+
+<h2>Altre domande?</h2>
+Vedi le <a href="/FAQ.html">FAQ</a>
+
+<p>
+<ADDRESS>contattate: <A HREF="mailto:$SERVER_ADMIN$">fexmaster</A></ADDRESS>
+
+</BODY>
+</HTML> 
diff --git a/locale/italian/lang.html b/locale/italian/lang.html
new file mode 100644 (file)
index 0000000..acb1394
--- /dev/null
@@ -0,0 +1 @@
+italiano
diff --git a/locale/italian/lib/fup.pl b/locale/italian/lib/fup.pl
new file mode 100644 (file)
index 0000000..7c44cd4
--- /dev/null
@@ -0,0 +1,35 @@
+# configurazione per F*EX CGI fup
+
+$info_1 = $info_login = <<EOD;
+<p><hr><p>
+<a href="/">F*EX (File EXchange)</a>
+e' un servizio per spedire grossi (grandi, enormi, giganti, ...) file.
+<p>
+Il mittente (tu) carica il file nel server F*EX ed automaticamente il destinatario si vede recapitare
+una e-mail di notifica con il link per effettuare il download.<br>
+Dopo il download o dopo $keep_default giorni, il server cancella il file.
+F*EX non e' un sistema di archiviazione!
+<p>
+Guardate anche <a href="/FAQ.html">domande e risposte (Q&A)</a>.
+<p><hr><p>
+<address>
+  <a href="mailto:$ENV{SERVER_ADMIN}">$ENV{SERVER_ADMIN}</a><br>
+</address>
+EOD
+
+$info_2 = <<EOD;
+<p><hr><p>
+Dopo il caricamento del file vedrete una barra di avanzamento 
+(bisogna avere javascript abilitato ed i popup abilitati).
+<p>
+<em>NOTA: Parte dei browser-WEB non possono caricare file > 2 GB!</em><br>
+Se il tuo file e' piu' grande devi usare un <a href="/fuc?show=tools">client F*EX particolare</a>.<br>
+Hai bisogno anche di un tool per recuperare i download interrotti. Il tuo browser-WEB non puo' farlo.
+<p>
+Attenzione: alcuni proxy HTTP come privoxy ritardano lo stattttto della barra di avanzamento!<br>
+Potresti voler disabilitare il proxying $ENV{SERVER_NAME} se ti capita di incorrere in questo problema.
+<p>
+Utenti Firefox: non digitare [ESC] perche' questo interrompera' il caricamento!
+<p>
+Vedere anche <a href="/FAQ.html">FAQ<a>.
+EOD
diff --git a/locale/italian/lib/reactivation.txt b/locale/italian/lib/reactivation.txt
new file mode 100644 (file)
index 0000000..53d5052
--- /dev/null
@@ -0,0 +1 @@
+Il tuo account F*EX è stato riattivato con successo.
diff --git a/locale/spanish/htdocs/FAQ.html b/locale/spanish/htdocs/FAQ.html
new file mode 100644 (file)
index 0000000..31e61dc
--- /dev/null
@@ -0,0 +1,254 @@
+<HTML>
+<h3>Este documento est&aacute; obsoleto. Por favor, use
+   <a href="/FAQ/meta.html?locale=english">FAQ en ingl&eacute;s</a>.</h3>
+<PRE>
+Meta preguntas:
+===============
+
+P: &iquest;Por qu&eacute; el nombre de F*EX y no uno m&aacute;s corto?
+
+R: En el momento de la publicaci&oacute;n hab&iacute;a ya un programa llamado &quot;FEX&quot; activo 
+   (m&aacute;s viejo) freshmeat.net.
+
+
+P: &iquest;Por qu&eacute; no emplear un servicio comercial
+   DropLoad, ALLPeers, YouSendIt, etc?
+   
+R: Tienen un l&iacute;mite de 2GB incluso &oacute; menos.
+   Su estado de privacidad y seguridad es desconocido.
+   No est&aacute;n basados en software abierto.
+   No existe ning&uacute;n cliente UNIX (CLI) para ellos.
+   Necesitan java, active-X, flash u otros plugins endemeniados.
+   Se desconoce cuanto durar&aacute;n - DropLoad y ALLPeers
+   ha finalizado sus asuntos.
+
+
+P: &iquest;Por qu el camello como logo?
+
+R: El logo fue inspirado por el camello de Perl, pero est basado en un Steiff
+   el camello de peluche, que correo con con nosotros on nuestro tamdem de 
+   carrelas.  El logo fue dibujado por Beate.
+
+
+P: &iquest;D&oacute;nde puedo conseguir los fuentes de F*EX?
+
+R: <a href="http://fex.rus.uni-stuttgart.de/fex.html">http://fex.rus.uni-stuttgart.de/fex.html</a>
+
+
+P: &iquest;Que hace falta para instalar F*EX?
+
+R: Un servidor UNIX con una entrega DNS y smtp para correo saliendo.
+   Y hace falta estar root en este ordenador.
+
+
+P: &iquest;Que es DNS y smtp?
+
+R: Que no instale F*EX. Es allende de su horizonte.
+
+
+P: &iquest;Qui&eacute;n es el autor?
+
+A: Ulli Horlacher &lt;<a href="mailto:framstag@rus.uni-stuttgart.de">framstag@rus.uni-stuttgart.de</a>&gt;
+
+
+P: &iquest;Qu&eacute; licencia tiene F*EX?
+
+R: Software libre Perl Artistic, vea <a href="http://fex.rus.uni-stuttgart.de/doc/Licence">http://fex.rus.uni-stuttgart.de/doc/Licence</a>
+
+
+P: &iquest;Hay una lista de correo de F*EX?
+
+A: <a href="https://listserv.uni-stuttgart.de/mailman/listinfo/fex">https://listserv.uni-stuttgart.de/mailman/listinfo/fex</a>
+
+
+P: &iquest;D&oacute;nde puede conseguir apoyo comercial para F*EX?
+
+R: Contacte con <a href="mailto:fex@nepustil.net">fex@nepustil.net</a> <a href="http://www.nepustil.net/">http://www.nepustil.net/</a>
+
+
+P: &iexcl;Tengo m&aacute;s/otras preguntas de las que aparecen en este documento!
+
+R: Pregunte al autor &lt;<a href="mailto:framstag@rus.uni-stuttgart.de">framstag@rus.uni-stuttgart.de</a>&gt;
+
+
+Preguntas de los usuarios:
+==========================
+
+P: &iquest;Qu&eacute; es el &quot;auth-ID&quot;?
+
+R: El auth-ID es una identificaci&oacute;n interna que autentica al usuario.
+   La primera vez es generado por el administrador o por el proceso de registro
+   autom&aacute;tico y pude modificarlo m&aacute;s tarde. Piense en &eacute;l como
+   una especie de clave poco segura.
+
+
+P: &iquest;Puedo usar un proxy HTTP?
+
+R: S&iacute;
+
+
+P: He subido un fichero ENORME pero me he confundido al escribir la direcci&oacute;n 
+   de correo electr&oacute;nico del destinatario.
+   Acabo de recibir un correo de error de rebote. 
+   &iquest;Tengo que volver a subir el fichero ENORME?
+
+R: No, no es necesario. Puede redirigir el fichero con
+   <a href="http://$HTTP_HOST$/rup">http://$HTTP_HOST$/rup</a>
+
+
+P: He subido un fichero ENORME pero olvid&eacute; poner a un destinatario.
+   &iquest;Tengo que volver a subir el fichero ENORME?
+
+R: No, no es necesario. Puede reenviar una copia del fichero con
+   <a href="http://$HTTP_HOST$/foc">http://$HTTP_HOST$/foc</a>
+
+
+P: &iquest;&iexcl;No puede subir ficheros &gt; 2 GB con mi navegador web!?
+
+R: Todos los navegadores web que conozco tienen errores en su implementaci&oacute;n
+   de HTML-FORM. El l&iacute;mite en la mayor&iacute;a es de 2 GB, algunas veces 4 GB.
+   Tiene que usar un cliente especial de F*EX para subir ficheros &gt; 2 GB, vea
+   <a href="http://$HTTP_HOST$/tools.html">http://$HTTP_HOST$/tools.html</a>
+
+
+P: Mi descarga abort&oacute; antes de que acabara. &iquest;Puedo continuar la descarga?
+
+R: F*EX soporta la continuaci&oacute;n de la descarga, pero su cliente debe soportar
+   tambi&eacute;n esta caracter&iacute;stica. Firefox, por ejemplo, no tiene esta 
+   caracter&iacute;stica HTTP, necesita otro cliente como opera, wget o fexget
+
+
+P: Mi subida abort&oacute; antes de que finalizara. &iquest;Puedo continuar la subida?
+
+R: F*EX soporta la continuaci&oacute;n de la subida, pero su cliente tambi&eacute;n debe 
+   soportarla. Ning&uacute;n navegador web tiene esta caracter&iacute;stica, necesita un 
+   cliente de F*EX especial como fexsend, schwuppdiwupp o F*IX
+   Vea <a href="http://$HTTP_HOST$/tools.html">http://$HTTP_HOST$/tools.html</a>
+
+
+P: Cuando pulso [ESC] en firefox la subida se cancela. &iquest;Por qu&eacute;?
+
+R: Esta es una caracter&iacute;stica de firefox: ESC finaliza la operaci&oacute;n en curso.
+   Soluci&oacute;n sencilla: no pulse ESC en Firefox
+   Soluci&oacute;n compleja: pida a los desarrolladores de Firefox que a&ntilde;adan una 
+                      configuraci&oacute;n del teclado
+
+
+P: Enviar como usario F*EX es f&aacute;cil, pero &iquest;como pueden recibirse fichero de 
+   otros desde fuera?
+
+R: Registrelos como su subusuarios o cre un grupo F*EX
+
+
+P: A veces puedo descargar un fichero m&aacute;s de una vez, especialmente cuando
+   lo repito r&aacute;pidamente. &iquest;Tiene errores la caracter de autoborrado?
+
+R: El servidor F*EX tiene un tiempo de gracia de 1 minuto tras la primera
+   descarga con exito durante el cual el fichero est&aacute; disponible todav&iacute;a.  
+   Este es necesario porque algunos &quot;administradores de descargas&quot; est&uacute;pidos 
+   solicitan el fichero varias al mismo tiempo.  De otra manera, informan al 
+   usuario de un error.
+
+R: Su fexmaster ha establecido AUTODELETE=DELAY por defecto, lo que significa 
+   que el proceso de limpieza del autoborrado se ejecuta una vez al d&iacute;a
+
+R: Los usuario normales (use los fuentes, Luke!) pueden establecer una bandera
+   de &quot;no borrar tras la descarga&quot;
+
+
+P: El tiempo por defecto para mantener el fichero en el sistema es desmasiado 
+   corto para m&iacute;, necesito m&aacute;s. &iquest;C&oacute;mo puedo cambiarlo?
+
+R: Use fexsend, pregunte a su fexmaster o lea el c&oacute;digo fuente
+
+
+P: No puede descargar ficheros con Internet Explorer, me dice &quot;No puedo
+   abrir el sistio de Internet&quot;. &iquest;Qu&eacute; deber&iacute;a hacer?
+
+R: Use Firefox o cualquier otro navegador web compatible con Internet, puesto 
+   que Internet Explorer no lo es. Este es uno de los muchos errores de 
+   Internet Explorer.
+
+
+Preguntas del administrador:
+============================
+
+P: No puedo instalar un servidor web como fexsrv, porque no tengo permisos
+   de root. &iquest;Existe una versi&oacute;n CGI pura de F*EX que funcione con un
+   servidor web apache?
+
+R: F*EX est&aacute; &iacute;ntimamente unido a fexsrv por varias razones (prestaciones,
+   limitaci&oacute;n en el tama&ntilde;o del fichero, el concepto de sesi&oacute;n, etc.) y no 
+   puede ejecutarse como CGI desde apache.
+   Pero puede echar un vistazo a
+   
+   <a href="http://gpl.univ-avignon.fr/filez/">http://gpl.univ-avignon.fr/filez/</a>
+   <a href="http://freshmeat.net/projects/eventh/">http://freshmeat.net/projects/eventh/</a>
+
+   que implementa un intercambio de ficheros con CGIs puros, pero ambos est&aacute;n 
+   limitados a 2 GB.
+   
+
+P: &iexcl;F*EX no funciona en absoluto! 
+   &iexcl;No puedo conectarme a &eacute;l con mi navegador web!
+
+R: Compruebe el encaminamiento, los filtros IP y la configuraci&oacute;n del 
+   cortafuegos. Compruebe tambi&eacute;n si su xinetd est&aacute; enlazado con tcp-wrapper 
+   y config&uacute;relo correctamente (hosts.allow).
+
+   F*EX necesita el puerto 80/tcp (HTTP) y opcionalmente el puerto 443/tcp
+   (HTTPS).
+
+
+P: &iexcl;F*EX es muy complicado! Necesito algo m&aacute;s sencillo.
+
+R: Pruebe <a href="http://www.home.unix-ag.org/simon/woof.html">http://www.home.unix-ag.org/simon/woof.html</a>
+
+
+P: &iquest;Como puedo integrar F*EX la administraci&oacute;n de usuario existente de mi 
+   sitio?
+
+R: F*EX tiene varios m&oacute;dulos de autenticaci&oacute;n:
+   local, RADIUS, LDAP, mailman y POP
+   Para los &uacute;ltimos 4 p&oacute;ngase en contacto con el autor por favor.
+   
+
+P: Quiero que todos mis usuarios locales use F*EX autom&aacute;ticamente. 
+   &iquest;C&oacute;mo puedo hacerlo?
+
+R: D&eacute;jelos autoregistrarse.
+   Tiene que editar el fichero lib/fex.ph y establecer (ejemplo):
+
+   @local_hosts = qw(127.0.0.1 10.10.100.0-10.10.255.255);
+   @local_domains = qw(flupp.org ulm.sub.net);
+
+   &iexcl;Desde luego tiene que a&ntilde;adir su hosts/redes locales reales!
+
+
+P: &iexcl;Necesito m&aacute;s seguridad! &iquest;C&oacute;mo activo HTTPS?
+
+R: Lea doc/SSL y mire busque tambi&eacute;n &quot;fop_auth&quot; e doc/concept
+
+
+P: &iexcl;Necesito que tenga una imagen corporativa. &iquest;Como configuro F*EX para que 
+   la tenga?
+
+R: Vea la variable @H1_extra en fex.ph y puede a&ntilde;adir htdocs/header.html
+
+R: Vea fup_template.html, modif&iacute;quela seg&uacute;n sus necesidades y usela como 
+   p&aacute;gina de inicio
+
+
+Otras preguntas:
+================
+
+P: &iexcl;F*EX es estupendo! &iquest;Puedo unirme al equipo de desarrollo? 
+   &iquest;Qu&eacute; hay que hacer?
+
+R: Contacte con el autor
+
+   Caracter&iacute;sticas no presente:
+
+   - otro cliente de subida que se integre mejor en Windows
+     arrastrar y soltar, libro de direcciones de Outlook, etc.
+</PRE></HTML>
diff --git a/locale/spanish/htdocs/index.html b/locale/spanish/htdocs/index.html
new file mode 100644 (file)
index 0000000..da71e28
--- /dev/null
@@ -0,0 +1,136 @@
+<HTML> 
+<HEAD><TITLE>F*EX - File EXchange</TITLE></HEAD>
+<BODY>
+<center>
+<h1>F*EX - Frams' Fast File EXchange</h1>
+<IMG SRC="logo.jpg">
+</center>
+<p>
+F*EX (Frams' Fast File EXchange) es un servicio para enviar ficheros (grandes, enormes,
+gigantes, ...) de un usuario A a otro B.
+<p>
+El remitente sube el fichero al servidor F*EX usando
+<a href="/fup">el formulario de subida WWW</a> y el destinatario
+autom&aacute;ticamente recibe una notifiaci&oacute;n por correo electr&oacute;nico con la URL de desacarga.
+<p>
+
+Usted dir&aacute;:
+<blockquote><em>
+  &iexcl;&iquest;Por qu&eacute; necesito de otro servicio de transferencia de ficheros?!<br>
+  &iexcl;Tengo el correo electr&oacute;nico, ftp, ssh e incluso sendfile!
+</em></blockquote>
+<p>
+Yo le respondo:
+<blockquote><em>
+  Aun as&iacute; necesita F*EX :-)
+</em></blockquote>
+<p>
+  
+Por ejemplo, quiere enviar a un amigo su &uacute;ltimo video de las vacaciones (1 GB).
+Tiene las siguientes posibilidades (y problemas):<p>
+
+<ul>
+  <li><h3>enviar un DVD por correo postal</h3><p>
+      Sin comentarios - &iexcl;vivimos en el a&ntilde;o
+      <<my @x=gmtime(time); $x[5]-69;>> 
+      tras la invenci&oacute;n de internet!
+      Enviar DVDs/CDs es para los abuelos.
+      <p>
+  <li><h3>usando el correo electr&oacute;nico</h3><p>
+      La mayor&iacute;a de los servidores de correo tienen una l&iacute;mite de 10 MB por mensaje de correo electr&oacute;nico
+      y una cuota de almacenamiento de uno 100 MB por usuario e incluso menos.
+      <p>
+  <li><h3>uucp</h3><p>
+      &iquest;Por qu&eacute; no escritura cuneiforme sobre planchas de piedra?
+      <p>
+  <li><h3>ssh</h3><p>
+      &iquest;Tiene la contrase&ntilde;a de su amigo o est&aacute; deseando darle la suya tan s&oacute;lo para la transferencia de un fichero?
+      <p>
+  <li><h3>ftp</h3><p>
+      <ul>
+        <li>Usando &iquest;qu&eacute; servidor FTP?
+        <li>Usando &iquest;qu&eacute; cuenta y con qu&eacute; contrase&ntilde;a?
+        <li>&iquest;No le importa enviar contrase&ntilde;as sin cifrar y ficheros a trav&eacute;s de internet que no es seguro?
+        <li>&iquest;Su proxy soporta ftp pasivo?
+        <li>En caso de usar ftp an&oacute;nimo:
+          <ul>
+            <li>&iquest;Permite subi 1 GB?
+            <li>&iquest;Permite borrar los ficheros subidos despu&eacute;s?
+            <li>&iquest;Qui&eacute;n m&aacute;s puede descargar su fichero?
+            <li>&iquest;Qui&eacute;n m&aacute;s puede borrar su fichero?
+            <li>Tiene que enviarle a su amigo la URL de descarga, &eacute;l tiene que 
+                informarle de que la descarga ha finalizado sin problemas y
+                tiene que borrar el fichero despu&eacute;s.<br>
+                En resumen: un grano en el culo.
+          </ul>
+      </ul>
+      <p>
+  <li><h3>http</h3><p>
+      <ul>
+        <li>No hay una administraci&oacute;n de ficheros subidos y usuarios - hay que programarla
+        <li>No hay autonotificaci&oacute;n
+        <li>No hay autoborrado
+        <li>Muy pocos servidores http pueden manejar ficheros de m&aacute;s de 2 GB
+      </ul>
+      <p>
+  <li><h3>sendfile</h3><p>
+      <ul>
+        <li>&iquest;Tiene UNIX y tiene sendfile instalado?
+        <li>&iquest;Su destinatario tiene UNIX y tiene sendfile instalado?
+        <li>&iquest;Ni usted ni su destinatario tiene un cortafuegos que bloquee el puerto 487?
+      </ul>
+      <p>
+  <li><h3>servicios comerciales como DropLoad, ALLPeers, YouSendIt, etc.</h3><p>
+      <ul>
+        <li>&iquest;Qu&eacute; limite tienen (la mayor&iacute;a: &lt; 2 GB)?
+        <li>&iquest;Qu&eacute; pasa con la seguridad y la privacidad: estar&aacute;n sus ficheros a salvo?
+        <li>&iquest;Est&aacute;n basados en software abierto o propietario?
+        <li>&iquest;Son accesible con cualquier navegador o necesita java, active-X, flash u otro plugins?
+        <li>&iquest;Sobrevir&aacute; m&aacute;s de unos cuantos meses?<br>
+           (DropLoad y ALLPeers ya han finalizado su actividad)
+      </ul>
+      <p>
+</ul>
+<p>
+Si ha contestado s&oacute;lo una vez "no" a las preguntas anteriores, entonces necesita
+F*EX.
+<p>
+<h2>Principales caracter&iacute;sticas de F*EX</h2>
+<p>
+<ul>
+  <li>transferencia de ficheros de tama&ntilde;o virtualmente ilimitado
+  <li>el destinatario y el remitente solo necesitan un programa de correo electr&oacute;nico y un navegador web -
+      de cualquier tipo, no necesitan instalar ning&uacute;n software
+  <li>RESEND y REGET para continuar las descargas interrumpidas por fallos en el enlace
+  <li>autonotificaci&oacute;n al destinatario
+  <li>autoborrado tras la descarga
+  <li>autoborrado tras la fecha de expiraci&oacute;n (por defecto: 5 d&iacute;as)
+  <li>los usuarios normales pueden crear subusuarios, que pueden enviar solo al usuario normal que lo defini&oacute;
+  <li>los usuarios normales puede crear grupos, una anolog&iacute;a de las listas de correo, pero para ficheros
+  <li>sin mantenimiento: no es necesaria la intervenci&oacute;n del administrador m&aacute;s all&aacute; de crear la cuentas en F*EX
+  <li>para enviar a m&uacute;ltiples destinatarios solo es necesario subir el fichero una vez
+  <li>F*EX es un servicio web HTTP y no necesita que se creen t&uacute;neles en los cortafuegos
+  <li>tambi&eacute;n soporta streams
+  <li>los usuarios UNIX reales, puede usar los programas para el shell fexsend y fexget para
+      evitar el uso del navegador web
+  <li>el protocolo y c&oacute;digo fuente est&aacute;n disponibles (Perl Artistic)
+</ul>
+
+<h2>Hablemos de SEX</h2>
+F*EX tiene un acompa&ntilde;ante: Stream EXchange (SEX).<p>
+Puede imaginar SEX como si fueran tuber&iacute;a de UNIX a trav&eacute;s de la red con un relay en medio.
+Esto puede ser &uacute;til para entubar datos del usuario A a otro B, donde A y B no pueden
+establecer una conexi&oacute;n directa, per ambos puede conectar por HTTP a servidor SEX.
+Las herramientas del shell sexsend y sexget permiten integrarlo f&aacute;cilmente entre
+el conjunto de herramientas de UNIX.
+<p>
+La autenticaci&oacute;n es igual que en F*EX.
+
+<h2>&iquest;Todav&iacute;a tiene m&aacute;s preguntas?</h2>
+Vea la <a href="/FAQ.html">FAQ</a>
+
+<p>
+<ADDRESS>contacto: <A HREF="mailto:$SERVER_ADMIN$">Administrador de F*EX</A></ADDRESS>
+
+</BODY>
+</HTML> 
diff --git a/locale/spanish/lang.html b/locale/spanish/lang.html
new file mode 100644 (file)
index 0000000..49201c4
--- /dev/null
@@ -0,0 +1 @@
+espa&ntilde;ol
diff --git a/locale/spanish/lib/fup.pl b/locale/spanish/lib/fup.pl
new file mode 100644 (file)
index 0000000..9367991
--- /dev/null
@@ -0,0 +1,35 @@
+# config for F*EX CGI fup
+
+$info_1 = $info_login = <<EOD;
+<p><hr><p>
+<a href="/">F*EX (File EXchange)</a>
+es un servicio para enviar ficheros (grandes, enormes, gigantes, ...).
+<p>
+El remitente (usted) sube el fichero al servidor F*EX y el destinatario recibe autom&aacute;ticamente
+una notificaci&oacute;n por correo electr&oacute;nico con una URL de descarga.<br>
+Tras la descarga o tras $keep_default d&iacute;as el servidor borra el fichero.
+&iexcl;F*EX no es un archivador!
+<p>
+Vea tambi&eacute;n <a href="/FAQ.html">preguntas y respuestas</a>.
+<p><hr><p>
+<address>
+  <a href="mailto:$ENV{SERVER_ADMIN}">$ENV{SERVER_ADMIN}</a><br>
+</address>
+EOD
+
+$info_2 = <<EOD;
+<p><hr><p>
+Tras pulsar el bot&oacute;n de enviar ver&aacute; una barra de progreso de la subida
+(si tiene javascript activado y permite las ventanas emergentes)
+<p>
+<em>NOTE: &iexcl;La mayor&iacute;a de los navegadores web no pueden subir ficheros > 2 GB!</em><br>
+Si su fichero es m&aacute;s grande tiene que usar <a href="/fuc?show=tools">un cliente de F*EX</a> especial.<br>
+Tambi&eacute;n necesita uno para poder continuar con una subida interrumpida. Su navegador no puede hacerlo.
+<p>
+Aviso: &iexcl;algunos proxys HTTP como privoy retrasan la barra de progreso de la subida!<br>
+Quiz&aacute; quiera desabilitar el proxy para $ENV{SERVER_NAME} si se encuentra con problemas.
+<p>
+Usuarios de firefox: &iexcl;no pulse [ESC] porque aborta la subida!
+<p>
+Vea tambi&eacute;n la <a href="/FAQ.html">FAQ<a>.
+EOD
diff --git a/locale/spanish/lib/reactivation.txt b/locale/spanish/lib/reactivation.txt
new file mode 100644 (file)
index 0000000..f469da1
--- /dev/null
@@ -0,0 +1 @@
+Su cuenta de F*EX ha sido reactivado con exito.
diff --git a/locale/translate b/locale/translate
new file mode 100755 (executable)
index 0000000..041f4b7
--- /dev/null
@@ -0,0 +1,205 @@
+#!/usr/bin/perl -w
+
+# translate english F*EX files into locale languange
+
+use File::Basename;
+use File::Copy;
+
+@trfiles = qw(
+  lib/fex.pp
+  cgi-bin/foc
+  cgi-bin/fop
+  cgi-bin/fuc
+  cgi-bin/fup
+  cgi-bin/fur
+  cgi-bin/rup
+  cgi-bin/pup
+  htdocs/FAQ/FAQ.html
+);
+
+#  htdocs/FAQ/meta.faq
+#  htdocs/FAQ/user.faq
+#  htdocs/FAQ/admin.faq
+#  htdocs/FAQ/misc.faq
+
+@cpfiles = qw(
+  lang.html
+  htdocs/index.html
+  htdocs/FAQ
+  htdocs/FAQ.html
+  lib/fup.pl
+);
+
+@FEX = getpwnam('fex') or die "$0: no user fex\n";
+$> = $FEX[2];
+$) = $FEX[3];
+
+$FEXHOME = $ENV{FEXHOME} || $FEX[7];
+
+# require "$FEXHOME/lib/fex.pp" 
+#   or die "$0: cannot load $FEXHOME/lib/fex.pp - $!\n";
+
+$tr = "locale/translations";
+
+$lang = shift or &usage;
+exit if $lang eq 'english';
+
+open $tr,$tr or die "$0: cannot open $tr - $!\n";
+
+while (<$tr>) {
+  $n++;
+  last if /^\s*$/;
+  if (/^#\s*([\w-]+):/ and $lang eq $1) {
+    $ll = $n;
+  }
+}
+
+&usage unless $ll;
+
+while (<$tr>) {
+  next if /^#/;
+  next if /^\s*$/;
+  chomp;
+  unless (/[a-z]/i) {
+    die "$0: syntax error in $tr line $. : no text\n";
+  }
+  $e = $_;
+  for my $l (2 .. $ll) { 
+    $_ = <$tr>||'';
+    chomp;
+    unless (/[a-z]/i) {
+      die "$0: syntax error in $tr line $. : no text\n";
+    }
+  }
+  $T{$e} = $_;
+  while (<$tr>) { last if /^\s*$/ }
+}
+  
+close $tr;
+@E = sort {length $b <=> length $a} keys %T;
+
+mkdir "$FEXHOME/locale";
+$_ = "$FEXHOME/locale/english";
+symlink '..',$_ unless -e;
+
+foreach $file (@trfiles) {
+  local $/;
+  mkdirp("$FEXHOME/locale/$lang/".dirname($file));
+  $fe = "$FEXHOME/$file";
+  $ft = "$FEXHOME/locale/$lang/$file";
+  open $fe,"<$fe" or die "$0: cannot read $fe - $!\n";
+  unlink $ft; # beware symlink!
+  open $ft,">$ft" or die "$0: cannot write $ft - $!\n";
+  $_ = <$fe>;
+  close $fe;
+  foreach $e (@E) {
+    s/\Q$e/$T{$e}/g;
+  }
+  print {$ft} $_;
+  close $ft;
+  chmod((stat $fe)[2],$ft);
+  print "$ft written\n";
+}
+
+foreach $file (@cpfiles) {
+  my $fs = "locale/$lang/$file";
+  if (-e $fs) {
+    my $fd = "$FEXHOME/$fs";
+    mkdirp(dirname($fd));
+    if (-f $fs) {
+      $fd .= '_new' if -e $fd;
+      if (copy($fs,$fd)) {
+        chmod((stat $fs)[2],$fd);
+        print "$fd written\n";
+      }
+    } else {
+      if (-f $fd) {
+        my $fds = $fd.'_save';
+        my $fdn = $fd.'_new';
+        system "rm -rf $fds $fdn";
+        rename $fd,$fds;
+        system "tar cf - $fs | (cd $FEXHOME; tar xf -)";
+        rename $fd,$fdn;
+        rename $fds,$fd;
+        print "$fdn written\n";
+      } else {
+        system "tar cf - $fs | (cd $FEXHOME; tar xf -)";
+        print "$fd written\n";
+      }
+    }
+  }
+}
+
+foreach $fs (qw(fex.ph dop)) {
+  $fd = "$FEXHOME/locale/$lang/lib/$fs";
+  symlink "../../../lib/$fs",$fd and print "$fd linked\n";
+}
+
+unless (-f "$FEXHOME/locale/$lang/htdocs/FAQ/meta.faq") {
+  unlink "$FEXHOME/locale/$lang/htdocs/FAQ/FAQ.html";
+  rmdir  "$FEXHOME/locale/$lang/htdocs/FAQ";
+}
+
+make_lf($lang);
+make_lf('english'); # unless -f "$FEXHOME/lib/lf.pl";
+
+exit;
+
+
+sub make_lf {
+  my $lang = shift;
+  my $fexpp = "$FEXHOME/locale/$lang/lib/fex.pp";
+  my $lf = "$FEXHOME/locale/$lang/lib/lf.pl";
+
+  open $fexpp,$fexpp or die "$0: no $fexpp - $!\n";
+  unlink $lf;
+  open $lf,'>',$lf or die "$0: cannot write $lf - $!\n";
+  print {$lf} "### auto-generated by install/translate - DO NOT EDIT! ###\n\n";
+  local $/;
+  $_ = <$fexpp>;
+  s/.*\n(\#\#\# locale functions)/$1/s;
+  s/\nsub (\w+)/\n\$$1\{$lang\} = sub/gs;
+  s/\n\}\n/\n\};\n/gs;
+  print {$lf} $_;
+  close $lf;
+  close $fexpp;
+}
+
+  
+  
+# emulate mkdir -p
+sub mkdirp {
+  my $dir = shift;
+  my $pdir;
+  
+  return if -d $dir;
+  $dir =~ s:/+$::;
+  die "$0: cannot mkdir /\n" unless $dir;
+  $pdir = $dir;
+  if ($pdir =~ s:/[^/]+$::) {
+    mkdirp($pdir) unless -d $pdir;
+  }
+  unless (-d $dir) {
+    mkdir $dir,0770 or die "$0: mkdir $dir - $!\n";
+  }
+}
+
+
+sub usage {
+  my @langs;
+
+  open $tr,$tr or die "$0: cannot open $tr - $!\n";
+
+  while (<$tr>) {
+    last if /^\s*$/;
+    push @langs,$1 if /^#\s*([\w-]+):/;
+  }
+  
+  foreach my $lang (@langs) {
+    if (not -l "locale/$lang" and -f "locale/$lang/lib/fup.pl") {
+      print "usage: $0 $lang\n";
+    }
+  }
+  
+  exit 1;
+}
diff --git a/locale/translations b/locale/translations
new file mode 100644 (file)
index 0000000..1e31a14
--- /dev/null
@@ -0,0 +1,2852 @@
+# english:     Ulli Horlacher <framstag@rus.uni-stuttgart.de>
+# german:      Waldemar Bronsch <aw.bronsch@gmx.de>     
+# swabian:     Hans-Georg Bickel <bickelhg@gmx.de>     
+# spanish:     Francisco Ruiz <fjrbas@yahoo.es>
+# galician:    Anton Meixome <meixome@certima.net>
+# italian:     Vanni Piagno <vpiagno@gmail.com>,<vannipiagno@libero.it>
+# czech:       Michal Simunek <michal.simunek@gmail.com>
+# french:      Jean-Baptiste Denis <jbd@jbdenis.net>
+
+F*EX operation control ERROR
+F*EX Bedienungssteuerungs-Fehler
+F*EX Fehler bei dr Bedienongsschdeuerong
+Error del control de operaciones de F*EX
+Erro do control de operaci&oacute;ns do F*EX
+Errore di controllo delle operazioni di F*EX
+Chyba při řízení provozu F*EX
+Erreur de gestion F*EX
+
+back to F*EX operation control
+Zur&uuml;ck zur F*EX-Bedienungssteuerung
+Zrigg zur Bedienongsschdeuerong
+Volver al control de operaciones de F*EX
+Volver ao control de operaci&oacute;ns do F*EX
+Torna al controllo delle operazioni di F*EX
+Zpět na řízení provozu F*EX
+Retour à la gestion de F*EX
+
+wrong user or auth-ID
+Falscher Benutzer oder falsche auth-ID
+Benutzr odr auth-ID isch falsch
+usuario o auth-ID err&oacute;neo
+o usuario ou o auth-ID son incorrectos
+utente o auth-ID sbagliato
+chybný uživatel nebo ověřovací ID
+utilisateur ou auth-ID invalide
+
+operation control
+Bedienungssteuerung
+Bedienongsschdeuerong
+control de operaciones
+control de operaci&oacute;ns
+controllo delle operazioni
+Řízení provozu
+gestion
+
+for user
+F&uuml;r Benutzer
+Fir dr Benutzr
+para el usuario
+para o usuario
+per l'utente
+pro uživatele
+pour l'utilisateur
+
+You as the sender have a server disk quota of $quota MB and currently using
+Sie als Absender haben auf dem Server insgesamt $quota MB Speicherplatz und Sie benutzen momentan
+Du als Absender hosch uffm Server alles en allem $quota MB Platz zom Schbeichra ond Du benutzsch dovo grad
+Usted como emisario tiene una cuota de disco en el servidor de $quota MB y actualmente en uso
+Vostede como remitente ten unha cota de disco do servidor que est&aacute; utilizando actualmente de $quota MB
+Tu in qualita' di mittente hai una quota disco nel server di $quota MB ed attualmente stai usando
+Jako odesilatel máte na serveru k dispozici $quota MB. Nyní využíváte
+Vous disposez de $quota MB d'espace disque en tant qu'expéditeur et vous utilisez actuellement
+
+sender quota (used)
+Absender Speicherplatz (benutzt)
+Absender Platz zom Schbeichra (benutzt)
+cuota de env&iacute;o (usada)
+cota de remitente (usada)
+quota mittente (in uso)
+limit pro odesilatele (využito)
+quota expéditeur (utilisé)
+
+recipient quota (used):
+Empf&auml;nger Speicherplatz (benutzt):
+Empf&auml;nger Platz zom Schbeichra (benutzt):
+quota de recepci&oacute;n (usada):
+cota de destinatario (usada):
+quota destinatario (in uso):
+limit pro příjemce (využito):
+quota destinataire (utilisé):
+
+Retrieve a list of all your received files</a> in F*EX spool
+Zeige die Liste aller empfangenen Dateien</a>
+Zeig mol a Lischde von &auml;lle empfangene Dateia</a>
+Ver la lista de todos los ficheros</a> recibidos en la cola de F*EX
+Ver a lista de todos os ficheiros</a> recibidos na cola de F*EX
+Richiama una lista di tutti i tuoi file ricevuti</a> nello spool di F*EX
+Načíst seznam všech souborů</a> čekajících ve frontě
+Récupérer une liste de tous les fichiers que vous avez reçus</a>
+
+Show download URLs of files you have sent
+Liste Download URLs von gesendeten Dateien auf
+Zoig Ronderlad URLs von verschickte Dateia
+Mostrar direcciones URL de los archivos enviados
+Amosar os URL dos ficheiros enviados
+Mostra scarica gli URL dei file inviati
+Zobrazit URL pro stažení souborů, které jste odeslali
+Lister les URLs des fichiers que vous avez envoyés
+
+Forward a copy of a file</a> you already have uploaded to another recipient
+Eine Kopie der Datei weiterleiten</a>, welche Sie bereits f&uuml;r einen anderen Empf&auml;nger hochgeladen haben
+A Kobie von der Datei weiderloide</a>, die Du scho fier en andra Empf&auml;nger nuffglade hosch
+Reenviar una copia de un fichero </a> que ha subido a otro destinatario
+Reenviar unha copia dun ficheiro </a> que cargou a outro destinatario
+Inoltra una copia di un file</a> che hai gia' caricato per un altro destinatario
+Přidat příjemce</a> k již nahranému souboru
+Reexpédier une copie d'un fichier</a> que vous avez déja uploadé à un autre destinataire
+
+Redirect files</a> you have uploaded to a wrong or misspelled recipient
+Leite Dateien weiter</a>, die an eine falsche oder ung&uuml;ltige E-Mail Adresse gegangen sind
+Dateia weiderloide</a>, die an a falsche oder ogildige E-Mail-Adress gange send
+Redirigir ficheros</a> que ha subido y enviado a un destinatario equivocado o mal escrito
+Redirixir ficheiros</a> que cargou e enviou a un destinatario equivocado ou mal escrito
+Inoltra file</a> che hai caricato ad un destinatario sbagliato o di cui hai sbagliato l'indirizzo
+Přesměrovat nahrané soubory</a> pro nesprávného či chybně uvedeného příjemce
+Rediriger un fichier</a> que vous avez déja uploadé vers un mauvais destinataire
+
+Resend notification e-mails for files you have sent
+Benachrichtigungs E-Mails nochmals versenden
+Sag dem Empf&auml;ngr no amole Bscheid
+Reenviar emails de recuerdo
+Resend notification e-mails for files you have sent
+Rispedisci e-mail di notifica per file che hai spedito
+Znovu odeslat zprávy s upozorněním pro soubory, které ste odeslali
+Renvoyer les messages de notifications pour les fichiers que vous avez envoyés
+
+Create a subuser who can send you files. Enter his e-mail address:
+Subuser anlegen, der Ihnen Dateien schicken kann. Geben Sie seine E-Mail Adresse ein:
+Subuser olega, der dir Dateia schicka ko. Gib sei E-Mail Adress ei:
+Crear un subuser que pueda enviar archivos. Escriba su direcci&oacute;n de correo electr&oacute;nico:
+Crear un subusuario que poida enviarlle ficheiros. Escriba o seu enderezo de correo:
+Crea un sottoutente che possa spedirti dei file. Inserisci il suo indirizzo e-mail:
+Vytvořit poduživatele, který vám může zasílat soubory. Zadejte jeho e-mailovou adresu:
+Créer un utilisateur qui peut vous envoyer des fichiers. Saisir son adresse électronique:
+
+for only one upload
+nur f&uuml;r einen Upload
+bloss fir oimal Nufflada
+por s&oacute;lo una carga
+Soamente para un envío
+per un solo upload
+pouze pro jedno nahrání
+pour un upload unique
+
+Comment to send with information e-mail:
+Kommentar, der mit der Informations E-Mail mitgeschickt wird:
+Kommentar, der mit dr Enformations E-Mail mitgschickt wird:
+Comentario para enviar la informaci&oacute;n por e-mail:
+Comentario que se enviará xunto coa información do correo:
+Commenta per spedire con un'e-mail di notifica:
+V informačním e-mailu odeslat komentář:
+Commentaire à envoyer avec le message d'information:
+
+create subuser and send e-mail
+Subuser anlegen und E-Mail verschicken
+Subuser olegea ond E-Mail verschicka
+crear subuser y enviar e-mail
+Crear un subusuario en enviar un correo
+crea un sottoutente se spedisci l'e-mail
+vytvořit poduživatele a odeslat e-mail
+créer un utilisateur et envoyer un message électronique
+
+You can <a href="/fur">register yourself
+Sie k&ouml;nnen sich <a href="/fur">selbst registrieren
+Du kosch De <a href="/fur">selber regischdriera
+Puede <a href="/fur">autoregistrarse
+Podo <a href="/fur">autorexistrarse
+Puoi <a href="/fur">registrarti
+Můžete <a href="/fur">se zaregistrovat
+Vous pouvez vous <a href="/fur">enregistrer
+
+if you do not have a F*EX account yet
+wenn Sie noch keinen F*EX Account haben
+wenn De no koi F*EX Konto hosch
+si no tiene todav&iacute;a una cuenta en F*EX
+se non ten a&iacute;nda unha conta no F*EX
+se non hai ancora il tuo account F*EX
+pokud ještě nemáte svůj F*EX účet
+si vous n'avez pas déjà un compte F*EX
+
+You may also use <a href="/fup?from=anonymous&to=$a">anonymous upload</a>
+Sie k&ouml;nnen auch <a href="/fup?from=anonymous&to=$a">anonymen Upload</a> verwenden
+Du kosch au <a href="/fup?from=anonymous&to=$a">anonymes Nufflada</a> nemma
+Se tambien puede usar <a href="/fup?from=anonymous&to=$a">anonymous upload</a>
+Tamén pode usar <a href="/fup?from=anonymous&to=$a">o envío anónimo</a>
+Potresti anhe utilizzare <a href="/fup?from=anonymous&to=$a">anonymous upload</a>
+Můžete také využít <a href="/fup?from=anonymous&to=$a">anonymnínahrávání</a>
+Vous pouvez aussi utiliser <a href="/fup?from=anonymous&to=$a">l'upload anonyme</a>
+
+You may also use <a href="/sup.html">simple upload</a>
+Sie k&ouml;nnen auch <a href="/sup.html">vereinfachten Upload</a> verwenden
+Du kosch au <a href="/sup.html">oifachs Nufflada</a> nemma
+Puede tambien usar <a href="/sup.html">subir simplificado</a>
+You may also use <a href="/sup.html">simple upload</a>
+Potresti anche usare <a href="/sup.html">caricamento semplice</a>
+Můžete také použít <a href="/sup.html">jednoduché nahrávání</a>.
+Vous pouvez également utiliser <a href="/sup.html">l'upload simple</a>
+
+<code>$file</code> already exists for
+<code>$file</code> existiert bereits f&uuml;r
+<code>$file</code> gibts scho fir
+<code>$file</code> ya existe para
+xa existe <code>$file</code> para
+<code>$file</code> esiste giàper
+<code>$file</code> již existuje pro
+Le fichier <code>$file</code> existe déjàpour
+
+<code>$file</code> already exists - <a href=\"$purge\">purge it?!</a>
+<code>$file</code> existiert bereits - <a href=\"$purge\">entfernen?!</a>
+<code>$file</code> gibts scho - <a href=\"$purge\">wegschmeissa?!</a>
+<code>$file</code> ya existe - <a href=\"$purge\">&iexcl;&iquest;Purgarlo?!</a>
+xa existe <code>$file</code> - <a href=\"$purge\">purgalo?!</a>
+<code>$file</code> esiste già - <a href=\"$purge\">lo vuoi eliminare?!</a>
+<code>$file</code> již existuje - <a href=\"$purge\">odstranit jej?!</a>
+Le fichier <code>$file</code> existe déjà - <a href=\"$purge\">Effacer?!</a>
+
+Manage your subusers and groups
+Subusers und Gruppen verwalten
+Onderbenutzr ond Grubba verwalde
+Gestione sus subusuarios y grupos
+Xestione os seus subusuarios e grupos
+Gestisci i tuoi sotto-utenti e gruppi
+Spravovat své poduživatele a skupiny
+Gérer vos sous-utilisateurs et vos groupes
+
+Change the disclaimer</a> to be sent with notification e-mail
+&Auml;nderung des Disclaimers</a> der in der Benachrichtigungs E-Mail mitversendet wird
+&Auml;nder dr Disclaimer</a> von dr Benochrichtigongs E-Mail
+Cambiar renuncia de email</a> enviado con emails de notificación
+Change the disclaimer</a> to be sent with notification e-mail
+Cambia il disclaimer <a/> affinchè venga spedito assieme all'e-mail di notifica
+Změnit odesílání zřeknutí se odpovědnosti</a> u správy s upozorněním
+Changer la clause de non-responsabilité</a> à envoyer avec le message de notification
+
+Change your <a href="" onclick="show_id();" title="$id">auth-ID</a> to
+&Auml;ndern Sie Ihre <a href="" onclick="show_id();" title="$id">auth-ID</a> in
+&Auml;ndr die <a href="" onclick="show_id();" title="$id">auth-ID</a> noch
+Cambie su <a href="" onclick="show_id();" title="$id">auth-ID</a> en
+Cambie o seu <a href="" onclick="show_id();" title="$id">auth-ID</a> en
+Cambia il tuo <a href="" onclick="show_id();" title="$id">auth-ID</a> in
+Změnit si ověřovací ID <a href="" onclick="show_id();" title="$id">auth-ID</a> na
+Changer votre <a href="" onclick="show_id();" title="$id">auth-ID</a> pour
+
+remember it
+merken
+aber ned vergessa, gell
+recordar
+rec&oacute;rdeo
+ricordati
+zapamatovat
+souvenez-vous en
+
+Edit your address book
+Ihr Adressbuch editieren
+Dei Adressb&uuml;chle bearbeide
+Edite su libro de direcciones
+Edite a s&uacute;a axenda de enderezos
+Edita la tua rubrica
+Upravit adresář
+Éditez votre carnet d'adresses
+
+Get <a href="/fuc?notification=detailed&akey=$akey">detailed</a> notification e-mails (current setting: <em>brief</em>)
+<a href="/fuc?notification=detailed&akey=$akey">Ausf&uuml;rliche</a> Benachrichtigungs E-Mails bekommen (aktuelle Einstellung: <em>kurz</em>)
+<a href="/fuc?notification=detailed&akey=$akey">Langs Gschw&auml;tz</a> en de Benochrichtigongs E-Mails (aktuelle Eischtellong: <em>kurz</em>)
+Recibir <a href="/fuc?notification=detailed&akey=$akey">detallado</a> e-mails de notificación (configuración actual: <em>corto</em>)
+Obter unha <a href="/fuc?notification=detailed&akey=$akey">detallada</a> notificación para os correos (axuste actual: <em>breve</em>)
+Avere <a href="/fuc?notification=detailed&akey=$akey">dettagliata</a> di notifica e-mail (impostazione attuale: <em>breve</em>)
+Dostávat <a href="/fuc?notification=detailed&akey=$akey">podrobné</a> e-maily s oznámením (současné nastavení: <em>zkrácené</em>)
+Recevoir des emails de notifications <a href="/fuc?notification=detailed&akey=$akey">détaillés</a> (configuration courante: <em>simple</em>)
+
+Get <a href="/fuc?notification=short&akey=$akey">brief</a> notification e-mails (current setting: <em>detailed</em>)
+<a href="/fuc?notification=short&akey=$akey">Kurze</a> Benachrichtigungs E-Mails bekommen (aktuelle Einstellung: <em>detailliert</em>)
+<a href="/fuc?notification=short&akey=$akey">Machs kurz</a> en de Benochrichtigongs E-Mails (aktuelle Eischtellong: <em>langs Gschw&auml;tz</em>)
+Recibir <a href="/fuc?notification=short&akey=$akey">corto</a> e-mails de notificación (configuración actual: <em>detallado</em>)
+Obter unha <a href="/fuc?notification=short&akey=$akey">breve</a> notificación para os correos (axuste actual: <em>detallado</em>)
+Avere <a href="/fuc?notification=short&akey=$akey">breve</a> notifica e-mail (impostazione attuale: <em>dettagliata</em>)
+Dostávat e-maily s upozorněním v <a href="/fuc?notification=short&akey=$akey">zkráceném</a> formátu (současné nastavení: <em>detailed</em>)
+Recevoir des emails de notifications <a href="/fuc?notification=short&akey=$akey">simple</a> (configuration courante: <em>détaillé</em>)
+
+Get <a href="/fuc?reminder=yes&akey=$akey">reminder</a> notification e-mails (current setting: <em>no reminders</em>)
+<a href="/fuc?reminder=yes&akey=$akey">Erinnerung</a> E-Mails erhalten (aktuelle Einstellung: <em>keine</em>)
+<a href="/fuc?reminder=yes&akey=$akey">Drodenka</a> E-Mails erhalta (aktuelle Eischtellong: <em>koine</em>)
+<a href="/fuc?reminder=yes&akey=$akey">Recibir</a> emails de recuerdo (configuración actual: <em>ningunos</em>)
+Get <a href="/fuc?reminder=yes&akey=$akey">reminder</a> notification e-mails (current setting: <em>no reminders</em>)
+Imposta<a href="/fuc?reminder=yes&akey=$akey">un Promemoria</a> per l'e-mail di notifica (Impostazione attuale: <em>nessun promemoria</em>)
+Odesílat <a href="/fuc?reminder=yes&akey=$akey">zprávy s připomenutím</a> (současné nastavení: <em>žádná upozornění</em>)
+Recevoir <a href="/fuc?reminder=yes&akey=$akey">les rappels</a> par e-mail (configuration actuelle: <em>pas de rappels</em>)
+
+Get <a href="/fuc?reminder=no&akey=$akey">no reminder</a> notification e-mails (current setting: <em>send reminders</em>)
+<a href="/fuc?reminder=no&akey=$akey">Keine Erinnerung</a> E-Mails erhalten (aktuelle Einstellung: <em>Erinnerung E-Mails werden verschickt</em>)
+<a href="/fuc?reminder=no&akey=$akey">Koine Drodenka</a> E-Mails erhalta (aktuelle Eischtellong: <em>Drodenka E-Mails werdad verschickt</em>)
+<a href="/fuc?reminder=no&akey=$akey">No recibir</a> emails de recuerdo (configuración actual: <em>Se eniva emails de recuerdo</em>)
+Get <a href="/fuc?reminder=no&akey=$akey">no reminder</a> notification e-mails (current setting: <em>send reminders</em>)
+Imposta <a href="/fuc?reminder=no&akey=$akey">nessun promemoria</a> per l'e-mail di notifica (Impostazione attuale: <em>spedisci promemoria</em>)
+Neodesílat <a href="/fuc?reminder=no&akey=$akey">žádná připomenutí</a> (současné nastavení: <em>odeslat upozornění</em>)
+Ne pas recevoir <a href="/fuc?reminder=no&akey=$akey">les rappels</a> par e-mail (configuration actuelle: <em>rappels envoyés</em>)
+
+Save</a> files after download (current setting: <em>display</em>
+Speichere</a> Dateien nach dem download (aktuelle Einstellung: <em>anzeigen</em>
+Schpeicher</a> die Dateia nochm Ronderlada (aktuelle Eischtellong: <em>ozeiga</em>
+Save</a> downloads (current setting: <em>display</em>
+Save</a> downloads (current setting: <em>display</em>
+Save</a> downloads (current setting: <em>display</em>
+Save</a> downloads (current setting: <em>display</em>
+Save</a> downloads (current setting: <em>display</em>
+
+Display</a> files when downloading with web browser (current setting: <em>save</em>
+Anzeige</a> von Dateien direkt beim download (aktuelle Einstellung: <em>abspeichern</em>
+Ozeiga</a> von Dateia beim Ronderlada (aktuelle Eischtellong: <em>abschpeichra</em>
+Display</a> downloads (current setting: <em>save</em>
+Display</a> downloads (current setting: <em>save</em>
+Display</a> downloads (current setting: <em>save</em>
+Display</a> downloads (current setting: <em>save</em>
+Display</a> downloads (current setting: <em>save</em>
+
+You will now get no reminder notification e-mails
+Sie werden nun keine Erinnerung E-Mails erhalten
+Du wirsch jetzt koine Drodenka E-Mails meh bekomma
+Desde ahora ya no se recibe emails de recuerdo
+You will now get no reminder notification e-mails
+Non otterrai ora alcun promemoria di notifica e-mail
+Nyní vám nebudou odesílány žádné zprávy s připomenutím
+Vous n'allez plus recevoir d'emails de notifications
+
+You will now get reminder notification e-mails
+Sie werden nun Erinnerung E-Mails erhalten
+Du wirsch jetzt Drodenka E-Mails bekomma
+Desde ahora se recibe emails de recuerdo
+You will now get reminder notification e-mails
+Otterrai ora promemoria di notifica e-mail
+Nyní vám budou odesílány zprávy s připomenutím
+Vous allez maintenant recevoir des rappels par e-mail
+
+Notification e-mails now come in detailed format
+Benachrichtigungs E-Mails kommen ab jetzt in detailliertem Format
+Benochrichtigongs E-Mails kommet ab jetzt mit langem Gschw&auml;tz
+E-mails de notificación desde ahora datallados
+Os correos de notificación virán a partir de agora en formato detallado
+Notifica e-mail ora sono disponibili in formato dettagliato
+E-maily s upozorněním jsou nyní odesílány v podrobném formátu
+Les emails de notifications sont maintenant au format détaillé
+
+Notification e-mails now come in short format
+Benachrichtigungs E-Mails kommen ab jetzt in kurzem Format
+Benochrichtigongs E-Mails kommet ab jetzt em kurzem Format
+E-mails de notificación desde ahora cortos
+Os correo de notificación virán a partir de agora en formato curto
+Notifica e-mail ora sono disponibili in formato breve
+E-maily s upozorněním jsou nyní odesílány ve zkráceném formátu
+Les emails de notifications sont maintenant au format simple
+
+Downloads will now be saved
+Downloads werden nun gespeichert
+Downloads werdet ab jetzt gschpeichert
+Descargas están guardadas
+Downloads will now be saved
+I downloads saranno ora salvati
+Stahované soubory se nyní uloží
+Les téléchargements vont maintenant être sauvés
+
+Downloads will now be displayed (if possible)
+Downloads werden nun angezeigt (wenn m&ouml;glich)
+Downloads werdet ab jetzt ozeigt (wenns ghot)
+Descargas están indicadas ahora (si posible)
+Downloads will now be displayed (if possible)
+I downloads saranno mostrati (se possibile)
+Stahované soubory se nyní zobrazí (je-li to možné)
+Les téléchargements cont maintenant être affichés (si possible)
+
+E-mail disclaimer reset to default
+Der E-Mail Disclaimer wurde auf Standard zur&uuml;ckgesetzt
+Dr E-Mail Oh&auml;ngsl isch wieder orginal
+Renuncia de email reinicializado a defecto
+E-mail disclaimer reset to default
+E-mail disclaimer ripristinato su predefinito
+Zřeknutí se odpovědnosti ve zprávě bylo nastaveno na výchozí hodnotu
+Clause de non-responsabilité par défaut
+
+Disclaimer to be sent with download notification e-mail:
+In Benachrichtigungs E-Mail angeh&auml;ngter Disclaimer:
+Disclaimer en dr Benochrichtigongs E-Mail:
+Renuncia de email enviado con emails de notificación para descargar:
+Disclaimer to be sent with download notification e-mail:
+Disclaimer spedito con il download dell'e-mail di notifica:
+Zřeknutí se odpovědnosti odesílané společně se zprávou s připomenutím stažení:
+Claude de non-responsabilité à envoyer avec le message de notification:
+
+reset the disclaimer to default
+den Disclaimer zur&uuml;cksetzen
+dr Disclaimer zricksetza
+Reinicializar renuncia de email a defecto
+reset the disclaimer to default
+ripristina il disclaimer su predefinito
+obnovit výchozí zřeknutí se odpovědnosti
+remise de la clause de non-responsabilité à sa valeur par défaut
+
+E-mail disclaimer changed to:
+Der E-Mail Disclaimer wurde ge&auml;ndert auf:
+Dr E-Mail Disclaimer isch jetzt:
+Renuncia de email cambiado á:
+E-mail disclaimer changed to:
+E-mail disclaimer modificato in:
+Zřeknutí se odpovědnosti ve zprávě bylo změněno na:
+Clause de non-responsabilité de l'email:
+
+Back to fup (upload page)
+Zur&uuml;ck zu fup (Upload Seite)
+Zrigg zu fup (Nufflad-Seite)
+Volver a fup (p&acute;gina de carga)
+Volver a fup (p&acute;xina de carga)
+Indietro a fup (pagina di caricamento)
+Zpět na fup (stránku pro nahrávání)
+Retour à fup (page d'upload)
+
+Download URLs of files you have sent
+Download URLs von gesendeten Dateien
+Ronderlad URLs von verschickte Dateia
+URLs de descarga de los archivos enviados
+Descargar os URL dos ficheiros enviados
+Scarica URL dei file che sono stati inviati
+URL pro stažení souborů, které jste odeslali
+URLs de téléchargement que vous avez envoyés
+
+<h3>to <code>
+<h3>an <code>
+<h3>an <code>
+<h3>vez <code>
+<h3>para <code>
+<h3>per <code>
+<h3>pro <code>
+<h3>pour <code>
+
+reminder F*EX-upload
+Erinnerung F*EX-upload
+Ned vergessa F*EX-upload
+recuerdo del F*EX-upload
+recordatorio do envío F*EX
+ricordo F*EX-upload
+Oznámení o nahraném souboru na F*EX
+Pense-bête F*EX-upload
+
+value="continue"
+value="weiter"
+value="weidr ghods"
+value="continuar"
+value="continuar"
+value="continua"
+value="pokračovat"
+value="continuer"
+
+>User:
+>Benutzer:
+>Benutzr:
+>Usuario:
+>Usuario:
+>Utente:
+>Uživatel:
+>Utilisateur:
+
+user config ERROR
+ERROR Benutzer-Einstellungen
+ERROR Benutzr-Eischdellunga
+ERROR en la confiuraci&oacute;n del usuario
+Produciuse un ERRO na configuaci&oacute;n do usuario
+ERRORE di configurazione utente
+CHYBA nastavení uživatele
+ERREUR de configuration utilisateur
+
+F*EX user config
+F*EX Benutzer-Einstellungen
+F*EX Benutzr-Eischdellunga
+configuraci&oacute;n del usuario F*EX
+configuraci&oacute;n do usuario F*EX
+Configurazione utente F*EX
+Nastavení F*EX uživatele
+Configuration utilisateur F*EX
+
+An information e-mail has been sent to your subuser
+Eine Benachrichtiguns-E-Mail wurde gesendet zu Ihrem Subuser
+Dein Onderbenutzr isch per E-Mail benochrichdigt worde
+Un correo electr&oacute;nico informativo se ha enviado a su subusuario
+Un correo electr&oacute;nico informativo enviouse ao seu subusuario
+Una e-mail di informazione e' stata spedita al tuo sotto-utente
+Vašemu poduživateli byl odeslán informační e-mail
+Un message d'information a été envoyé à votre sous-utilisateur
+
+A one time upload URL for <code>$otuser</code> has been created
+Eine einmal g&uuml;ltige upload URL f&uuml;r <code>$otuser</code> wurde angelegt
+A oimol giltig nufflad URL fir <code>$otuser</code> isch oglegt worda
+Un URL para subir una vez <code>$otuser</code> ha sido creado
+Un enderezo URL de único uso para <code>$otuser</code> acaba de se crear
+E' stato creato un URL di caricamento utilizzabile una sola volta per l'utente <code>$otuser</code>
+Jednorázová URL pro nahrání pro <code>$otuser</code> byla vytvořena
+Une URL d'upload à usage unique pour <code>$otuser</code> a été créée
+
+and an information e-mail has been sent to this address
+und eine Informations-E-Mail wurde an diese Adresse verschickt
+ond a Was-macha-musch-E-Mail isch do no verschickt worda
+y un email de información ha sido enviado a esta dirección
+e un correo informativo acaba de se enviar a este enderezo
+e una e-mail di informazioni è stata spedita a questo indirizzo
+a informační e-mail byl odeslán na tuto adresu
+et un message d'information a été envoyé à cette adresse
+
+wrong user or auth-ID
+Falscher Benutzer oder falsche auth-ID
+Benutzr oddr auth-ID send falsch
+usuario o auth-ID err&oacute;neo
+o usuario ou o auth-ID son incorrectos
+utente o auth-ID sbagliato
+chybný uživatel nebo ověřovací ID
+utilisateur ou auth-ID invalide
+
+no group name specified
+kein Gruppen-Name angegeben
+Seggl, Du hosch koin Grubbenoma ogebe
+nombre de grupo sin especificar
+non se especificou o nome do grupo
+nome gruppo non specificato
+nebyl uveden žádný název skupiny
+pas de nom de groupe spécifié
+
+is already a registered F*EX full user
+ist bereits ein registrierter F*EX Voll-Benutzer
+isch scho l&auml;ngscht als Vollbenutzr bei F*EX regischdriert
+ya es un usuario normal registrado en F*EX
+xa &eacute; un usuario completo rexistrado en F*EX
+e' gia' un utente F*EX completo registrato 
+je již zaregistrovaným plnohodnotným F*EX uživatelem 
+est déja enregistré en tant qu'utilisateur F*EX
+
+Your subuser upload URL is:
+Ihr Subuser Upload-URL ist:
+Dei Onderbenutzr Nufflad-URL isch:
+La URL de subida de su subusuario es:
+O seu enderezo URL de carga como subusuario:
+L'URL per il caricamento del tuo sotto-utente e':
+URL pro nahrání pro vašeho poduživatele je:
+L'URL d'upload de votre sous-utilisateur est:
+
+Your subusers upload URLs are:
+Die Upload-URLs f&uuml;r Ihre Subusers sind:
+Dui Nufflad-URLs fier Deine Onderbenutzr send:
+Las URLs de sus subusuarios son:
+Os URL de carga dos seus subusuarios
+Gli URL per il caricamento dei tuoi sotto-utenti sono:
+URL pro nahrání pro vaše poduživatele jsou:
+Les URLs d'upload de vos sous-utilisateurs sont:
+
+An information e-mail has been sent to
+Eine Benachrichtigungs-E-Mail wurde gesendet an
+A Benochrichdigongs-E-Mail isch gschickt worde an
+Un correo electr&oacute;nico informativo se ha enviado a
+Enviouse un correo electr&oacute;nico informativo 
+Una e-mail di informazione e' stata spedita a
+Informační e-mail byl odeslán na
+Un message d'information a été envoyé à
+
+Edit address book
+Adressbuch editieren
+Adressb&uuml;chle bearbeita
+Editar el libro de direcciones
+Editar a axenda de enderezos
+Edita la rubrica
+Upravit adresář
+Éditez le carnet d'adresse
+
+Entry:<th>alias<th>e-mail address<th># optional comment
+Eintrag:<th>Alias<th>E-Mail Adresse<th># optionaler Kommentar
+Eidrag:<th>Alias<th>E-Mail Adress<th># Kommendar (kosch macha oddr bleiba lassa)
+Entrada:<th>alias<th>direcci&oacute;n de correo electr&oacute;nico<th># comentario opcional
+Entrada:<th>alias<th>enderezo de correo electr&oacute;nico<th># comentario opcional
+Entrata:<th>alias<th>indirizzo e-mail<th># commento opzionale
+Položka:<th>alias<th>e-mailová adresa<th> volitelný komentář
+Entrée:<th>alias<th>adresse e-mail<th># comment optionnel
+
+Example:
+Beispiel:
+Beischbiel:
+Ejemplo:
+Exemplo:
+Esempio:
+Příklad
+Exemple:
+
+value="submit"
+value="Absenden"
+value="Verschicke"
+valor="enviar"
+valor="enviar"
+value="sottometti"
+value="potvrdit"
+value="soumettre"
+
+You may use these alias names as F*EX recipient addresses on 
+Sie k&ouml;nnen diese Alias-Namen als F*EX Empf&auml;nger-Adressen auf
+Du kosch selle Alias-Nome als F*EX Empf&auml;nger-Adresse uff
+Puede usar estos alias como direcciones de destinatarios en F*EX en
+Pode usar estes alias como enderezos de destinatarios do F*EX en
+Potresti usare questo alias come destinatario di F*EX
+Tyto aliasy můžete používat jako adresy F*EX příjemce na
+Vous pouvez utiliser ces alias comme destinataires F*EX sur
+
+'<a href="/fup?akey=$akey">fup</a>'
+'<a href="/fup?akey=$akey">fup</a> benutzen.'
+'<a href="/fup?akey=$akey">fup</a> nemme.'
+'<a href="/fup?akey=$akey">fup</a>'
+'<a href="/fup?akey=$akey">fup</a>'
+'<a href="/fup?akey=$akey">fup</a>'
+'<a href="/fup?akey=$akey">fup</a>'
+'<a href="/fup?akey=$akey">fup</a>'
+
+Alternatively you can fex a file ADDRESS_BOOK to yourself 
+Alternativ k&ouml;nnen Sie die Datei ADDRESS_BOOK an sich selbst 
+Du kosch au dui Datei ADDRESS_BOOK an De selber
+Tambi&eacute;n puede enviar v&iacute;a fex un fichero ADDRESS_BOOK a si mismo
+Tam&eacute;n pode enviar v&iacute;a fex un ficheiro ADDRESS_BOOK a si mesmo
+Alternativamente puoi inviare un file ADDRESS_BOOK a te stesso
+Nebo si můžete poslat soubor sami sobě
+Vous pouvez aussi vous envoyer un fichier ADDRESS_BOOK
+
+containing your alias definitions
+fexen, welche Ihre Alias-Definitionen enth&auml;lt
+fexe, wo Deine Alias-Definitione dren send
+que contenga sus definiciones de alias
+que conte&ntilde;a as s&uacute;as definici&oacute;ns de alias
+contiene le tue definizioni degli alias
+obsahuje definované aliasy
+contenant vos alias
+
+alias<th>e-mail address<th>options<th>comment
+Alias<th>E-Mail Adresse<th>Optionen<th>Kommentar
+Alias<th>E-Mail Adresse<th>Optionen<th>Kommendar
+alias<th>direcci&oacute;n de correo electr&oacute;nico<th>opciones<th>comentar
+alias<th>enderezo de correo electr&oacute;nico<th>opci&oacute;ns<th>comentar
+alias<th>indirizzo e-mail<th>opzioni<th>commento
+alias<th>e-mailová adresa<th>volby<th>komentář
+alias<th>adresse e-mail<th>options<th>commentaire
+
+back to fup (F*EX upload)
+zur&uuml;ck zu fup (F*EX Upload)
+zrigg zu fup (F*EX Nufflade)
+volver a fup (subida a F*EX)
+volver a fup (subida a F*EX)
+torna a fup (caricamento F*EX)
+zpět na fup (F*EX nahrání)
+retour à fup (F*EX upload)
+
+You have to give these URLs to your subusers for fexing files to you
+Sie m&uuml;ssen diese URLs Ihren Subusers geben, damit sie Dateien an Sie fexen k&ouml;nnen
+Du musch selle URLs Deine Onderbenutzr gebba, damit se Dateia an Di fexe kenne
+Tiene que dar estas URLs a sus subusuarios para que puedan enviarle ficheros v&iacute;a F*EX
+Ten que dar estes URL aos seus subusuarios para que poidan enviarlle ficheiros v&iacute;a F*EX
+Devi dare questi URL ai tuoi sotto-utenti per spedirti file
+Aby vám mohli poduživatelé odesílat soubory, sdělte jim tyto URL
+Vous devez donner ces URLs à vos utilisateurs pour qu'ils puissent vous envoyer des fichiers
+
+Or click on the subuser's e-mail address link to send him an
+Oder klicken Sie auf den E-Mail Adressen-Link des Subusers, um ihm eine
+Oddr klick halt uff de E-Mail Adresse-Link von Deim Onderbenutzr, no kosch ihm a
+O pinche en el enlace a la direcci&oacute;n de correo electr&oacute;nico del subusuario para enviarle un
+Ou prema sobre a ligaz&oacute;n do enderezo de correo do subusuario para enviarlle un
+O clicca nel link dell'indirizzo e-mail del sotto-utente per spedirgli un
+Nebo klikněte na odkaz s e-mailovou adresou poduživatele k odeslání
+Ou cliquer sur l'adresse de messagerie de votre sous-utilisateurs pour lui envoyer un
+
+information e-mail by the F*EX server
+Benachrichtigungs-E-Mail mit Hilfe des F*EX-Servers zu senden
+Benochrichdigongs-E-Mail ieber de F*EX-Server schicke
+correo electr&oacute;nico informativo del servidor de F*EX
+correo electr&oacute;nico informativo v&iacute;a servidor do F*EX
+informazione spedita dal server F*EX
+informačního e-mailu pomocí F*EX serveru
+message d'information du server F*EX
+
+Your F*EX account:
+Ihr F*EX Account:
+Dei F*EX Konto:
+Su cuenta en F*EX:
+A s&uacute;a conta no F*EX:
+Il tuo utente F*EX:
+Váš F*EX účet:
+Votre compte F*EX:
+
+New auth-ID:
+Neue auth-ID:
+Neie auth-ID:
+Nuevo auth-ID:
+Novo auth-ID:
+Nuovo auth-ID:
+Nové ověřovací ID:
+Nouvel auth-ID:
+
+(Remember your auth-ID when you change it!)
+(Unbedingt die auth-ID merken wenn sie ge&auml;ndert wird!)
+(Merk Dir obedingd Dei auth-ID wenn Du se g&auml;ndert hosch!)
+(Recuerde su auth-ID cuando lo cambie)
+(Lembre o seu auth-ID cando o cambie)
+(Ricordati il tuo auth-ID quando lo cambi!)
+(Zapamatujte si své ověřovací ID, pokud jej změníte!)
+(Vous devez vous souvenir de votre auth-ID lorsque vous le changez)
+
+Allow special senders (= subusers) to fex files to you:
+Ihren ausgew&auml;hlten Partnern (= Subusers) erlauben, Daten an Sie zu senden:
+Deim erlesene Freindeskreis (= Onderbenutzr) erlaube, Data an Di zom schicka:
+Permitir a remitentes especiales (= subusuario) que le env&iacute;n v&iacute;a F*EX ficheros a usted:
+Permitirlles a remitentes especiais (= subusuarios) enviarlle a vostede ficheiros por F*EX:
+Permetti a utenti speciali (= sotto-utenti) di spedirti file:
+Povolit speciální odesilatele (= poduživatelé), kteří vám mohou zasílat soubory:
+Autoriser des expéditeurs particuliers (= sous-utilisateurs) à vous envoyer des fichiers:
+
+Allow
+Erlaube 
+Erlaub
+Permitir
+Permitir
+Permetti
+Povolit
+Autoriser
+
+save and show upload URLs
+speichern und upload URLs anzeigen
+abschpeichra ond nufflad URLs ozeiga
+guardar y mostrar las direcciones URL de carga
+gardar e amosar URL enviado
+salva e mostra gli indirizzi di upload
+uložit a zobrazit URL pro nahrání
+sauvegarder et lister les URLs d'upload
+
+This list has entries in format:<td>&lt;e-mail address>:&lt;encryption-ID>
+Diese Liste besteht aus Eintr&auml;gen im Format:<td>&lt;E-Mail Adresse>:&lt;encryption-ID>
+Sell Lischt beschdoht aus Eidragonge vom Format:<td>&lt;E-Mail Adress>:&lt;encryption-ID>
+Esta lista tiene las entradas en formato:<td>&lt;e-mail address>:&lt;encryption-ID>
+Esta lista ten entradas co formato :<td>&lt;enderezo de correo>:&lt;ID-cifrado>
+Questa lista ha i contenuti nel formato: <td>&lt;e-mail address>:&lt;encryption-ID>
+Položky seznamu jsou ve formátu:<td>&lt;e-mailová adresa>:&lt;Å¡ifrovacíID>
+Les entrées de cette liste ont comme format:<td>&lt;adresse e-mail>:&lt;encryption-ID>
+
+These special senders may fex files <em>only</em> to you!
+Diese besonderen Sender k&ouml;nnen Dateien <em>nur</em> an Sie fexen!
+Selle Absender send was Bsonders ond kenne Dateia <em>blo&szlig;</em> an dich fexe!
+&iexcl;Estos remitentes especiales pueden enviar ficheros v&iacute;a F*EX <em>solo</em> a usted!
+Estes remitentes especiais poden enviar ficheiros v&iacute;a F*EX <em>s&oacute;</em> a vostede!
+Questi utenti speciali potrebbero spedire file <em>solo</em> a te!
+Tito speciální odesilatelé mohou odesílat soubory <em>pouze</em> vám!
+Ces expéditeurs particuliers peuvent <em>seulement<em> vous envoyer des fichiers
+
+It is not necessary to add regular fex users to your list,
+Es ist nicht notwendig, regul&auml;re FEX Benutzer zu Ihrer Liste hinzuzuf&uuml;gen
+'S isch net nedich, da&szlig; de regul&auml;re F*EX Benutzr uff Dei Lischde setzsch
+No es necesario a&ntilde;adir usarios de F*EX normales a su lista,
+Non &eacute; necesario engadir usuarios de F*EX normais &aacute; s&uacute;a lista,
+Non e' necessario aggiungere utenti regolari fex alla tua lista,
+Do tohoto seznamu není třeba přidávat běžné F*EX uživatele,
+Il n'est pas nécessaire d'ajouter des utilisateurs de F*EX à votre liste,
+
+because they already can fex
+weil diese bereits auch so fexen k&ouml;nnen
+weil dui kenned sowieso scho fexe
+porque ellos ya pueden enviar v&iacute;a F*EX
+porque eles xa poden enviar v&iacute;a F*EX
+perche' possono gia' spedire con F*EX
+protože ti vždy mohou odesílat soubory
+parce qu'ils peuvent déja utiliser fex
+
+The encryption-ID is necessary to generate a unique upload URL for this subuser
+Die encryption-ID wird f&uuml;r um eine eindeutige upload URL f&uuml;r diesen Subuser zu erzeugen
+Die encryption-ID isch notwendig um a eindeutig upload URL f&uuml;r den Subuser zom macha
+Hace falta una ID de codificaci&oacute;n para generarle a este sub-usuario un inequ&iacute;voco URL para subir
+Fai falta un ID de codificaci&oacute;n para xerarlle a este subusuario un inequ&iacute;voco URL para subir
+L'encryption-ID e' necessario per generare un unico URL di caricamento per questo sotto-utente
+Poduživatel musí mít k vytvoření unikátní URL pro nahrávání šifrovací ID
+L'encryption-ID est nécessaire pour générer une URL d'upload unique pour cet utilisateur
+
+If you omit the encryption-ID a random one will be used
+Wenn Sie die encryption-ID weglassen, wird eine zuf&auml;llige gew&auml;hlt
+Wenn Du die encryption-ID wegl&auml;scht, no wird a zuf&auml;lliga gnomma
+Si se omite el cifrado-ID un nombre cualquiera se utilizar&aacute;
+Se omite o ID-cifrado, utilizarase un aleatorio
+Se tu ometti l'ID crittato uno casuale verrà utilizzato
+Vynecháte-li šifrovací ID, použije se náhodně vygerenrované
+Si vous ne mentionnez pas l'encryption-ID, il sera généré aléatoirement
+
+A F*EX group is similar to a mailing list, but for files
+Eine F*EX Gruppe ist einem E-Mail-Verteiler &auml;hnlich, allerdings eben f&uuml;r Dateien
+A F*EX Grupp isch so was wie en E-Mail-Verdoiler, halt fier Dateia
+Un grupo F*EX es similar a una lista de correo, pero para ficheros
+Un grupo F*EX sem&eacute;llase a unha lista de correo, pero para ficheiros
+Un gruppo F*EX e' simile ad una lista di distribuzione, ma per i file
+F*EX skupina je podobná poštovní konferenci s tím rozdílem, že se odesílají soubory
+Un groupe F*EX est similaire à une adresse de diffusion (mailing-list), mais pour les fichiers
+
+Edit your F*EX groups:
+Editieren Sie Ihre F*EX Gruppen:
+Bearbeit Deine F*EX-Gruppa:
+Edite sus grupos F*EX:
+Edite os seus grupos F*EX:
+Edita i tuoi gruppi F*EX:
+Upravit F*EX skupiny:
+Éditez vos groupes F*EX:
+
+new group
+Neue Gruppe
+Neie Grupp
+Grupo nuevo
+Novo grupo
+nuovo gruppo
+nová skupina
+nouveau groupe
+
+Your F*EX account on
+Ihr F*EX Account auf
+Dei F*EX Konto auf
+Su cuenta F*EX
+A s&uacute;a conta F*EX
+Il tuo utente F*EX
+Váš účet pro F*EX
+Votre compte F*EX sur
+
+Your upload URL
+Ihre upload URL
+Dei nufflad URL
+su URL de subir
+O seu enderezo URL de carga
+Il tuo URL di caricamento
+URL pro nahrání
+Votre URL d'upload
+
+to upload one file to $user
+um eine Datei an $user zu schicken
+om a Datei an $user zom schicka
+para subir un fichero a $user
+para cargarlle un ficheiro a $user
+per caricare un file per $user
+jednoho souboru uživateli $user
+pour uploader un fichier à $user
+
+to upload files to $user
+um Dateien an $user zu schicken
+om Dateia an $user zom schicka
+para subir ficheros a $user
+para subir ficheiros a $user
+per caricare file a $user
+pro nahrávání souborů uživateli $user
+pour uploader des fichiers à $user
+
+to upload files to F*EX group "$group"
+um Dateien fuer F*EX-Gruppe "$group" bereitzustellen
+wenn De Dateia fier dui F*EX-Grupp "$group" nufflade willsch
+para subir ficheros al grupo F*EX "$group"
+para subir ficheiros ao grupo de F*EX "$group"
+per caricare file al gruppo F*EX "$group"
+pro nahrávání souborů F*EX skupině "$group"
+pour uploader des fichiers au groupe F*EX "$group"
+
+See http://$ENV{HTTP_HOST}/index.html for more information about
+Siehe http://$ENV{HTTP_HOST}/index.html fuer mehr Informationen ueber
+Guck halt uff http://$ENV{HTTP_HOST}/index.html wenn De meh wisse willsch ieber
+Vea http://$ENV{HTTP_HOST}/index.html para obtener m&aacute;s informaci&oacute;n sobre
+V&eacute;xase http://$ENV{HTTP_HOST}/index.html para obter m&aacute;s informaci&oacute;n sobre
+Vedi http://$ENV{HTTP_HOST}/index.html per ulteriori informazioni
+Více informací o svém účtu získáte na http://$ENV{HTTP_HOST}/index.html
+Voir http://$ENV{HTTP_HOST}/index.html pour plus d'informations sur
+
+Questions? ==> F*EX admin: $admin
+Fragen? ==> Kontaktieren Sie den F*EX Administrator: $admin
+Froga? ==> Belaeschtig ruhig dr F*EX Adminischdrator: $admin
+Preguntas? ==> Administrador de F*EX: $admin
+Preguntas? ==> Administrador do F*EX: $admin
+Domande? ==> Amministratore F*EX: $admin
+Máte otázky? ==> Kontaktujte F*EX správce: $admin
+Questions? ==> Administrateur F*EX: $admin
+
+Your F*EX account on
+Ihr F*EX Account auf
+Dei F*EX Konto auf
+Su cuenta F*EX  en
+A s&uacute; conta F*EX  en
+Il tuo account F*EX su
+Váš F*EX účet na
+Votre compte F*EX sur
+
+A F*EX (File EXchange) account has been created for you on
+Ein F*EX (File EXchange) Account ist fuer Sie angelegt worden auf
+Du hosch jetzt a F*EX (File EXchange) Konto auf
+Una cuenta F*EX (File EXchange) se ha creado para usted en
+Cre&oacute;uselle a vostede unha conta F*EX (File EXchange) 
+Un account F*EX (File EXchange) e' stato creato per te su
+Byl vám vytvořen F*EX (File EXchange) účet na
+Un compte F*EX a été créé pour vous sur
+
+'Use
+'Bitte benutzen Sie
+'Nemm
+'Usa
+'Use
+'Usa
+'Používání
+'Utilisez
+
+See http://$ENV{HTTP_HOST}/ for more information about
+Siehe http://$ENV{HTTP_HOST}/ fuer mehr Informationen ueber
+Guck halt uff http://$ENV{HTTP_HOST}/index.html wenn De meh wisse willsch ieber
+Vea http://$ENV{HTTP_HOST}/ para obtener m&aacute;s informaci&oacute;n sobre
+V&eacute;xase http://$ENV{HTTP_HOST}/ para obter m&aacute;s informaci&oacute;n sobre
+Vedi http://$ENV{HTTP_HOST}/ per ulteriori informazioni
+Informace, jak používat F*EX, naleznete na http://$ENV{HTTP_HOST}/
+Voir http://$ENV{HTTP_HOST}/ pour plus d'informations sur
+
+$notify not found in $gf
+$notify nicht gefunden in $gf
+$notify net gfonda in $gf
+$notify no se encontr&oacute; en $gf
+$notify non se atopou en $gf
+$notify non trovato in $gf
+$notify nebyl v $gf nalezen
+$notify non trouvé dans $gf
+
+Notification e-mail to $notify has been sent
+Benachrichtigungs-E-Mail an $notify wurde gesendet
+Benochrichdigongs-E-Mail an $notify isch verschickt worda
+Correo electr&oacute;nico de notificaci&oacute;n enviado a $notify
+Enviouse un correo de notificaci&oacute;n a $notify
+E-mail di notifica e' stata spedita a $notify
+E-mail s oznámením pro $notify byl odeslán
+Un message d'information a été envoyé à $notify
+
+bad addresses:</h2>\n<ul>
+Fehlerhafte Adressen:</h2>\n<ul>
+Was send au des fier Adresse? Die send grottefalsch::</h2>\n<ul>
+direcci&oacute;n err&oacute;nea: </h2>\n<ul>
+Os enderezos son incorrectos:</h2>\n<ul>
+indirizzo errato:</h2>\n<ul>
+chybné adresy:</h2>\n<ul>
+mauvaises adresses:</h2>\n<ul>
+
+you are already in group \@$group owned by $user
+Sie sind bereits in der Gruppe \@$group welche dem Benutzer $user geh&ouml;rt
+Du bisch doch scho l&auml;ngscht in derra Grupp \@$group von dem Benutzr $user
+ya pertenece al grupo \@$group propiedad de $user
+xa pertence ao grupo \@$group propiedade de $user
+sei gia' nel gruppo \@$group di proprieta' di $user
+ve skupině \@$group vlastněné uživatelem $user se již nacházíte 
+vous êtes déja dans le groupe \@$group de l'utilisateur $user
+
+Go back
+Zur&uuml;ck
+No mol von vorne
+Volver
+Volver
+Torna indietro
+Vrátit se
+Retour
+
+and enter another group name
+und geben Sie einen anderen Namen f&uuml;r die Gruppe ein
+ond geb en andra Noma fier dui Grupp ei
+y introduzca un nuevo grupo
+e introduza un novo grupo
+e inserisci un'altro nome del gruppo
+a zadat jiný název skupiny
+et saisissez un autre nom de groupe
+
+Group \@$group has members:
+Die Gruppe \@$group hat folgende Mitglieder:
+Zur Grupp \@$group ghered:
+Los miembros del grupo \@$group son:
+Os membros do grupo \@$group son:
+Il gruppo \@$group ha i componenti:
+Členové skupiny \@$group:
+Le groupe \@$group a comme membres:
+
+(click address to send a notification e-mail to this user)
+(Klicken Sie auf die Adresse um eine Benachrichtigungs-E-Mail an diesen Benutzer zu senden)
+(Schieb Dei Maus uff d' Adress ond klick. Dann wird a Benochrichdigongs-E-Mail an den Benutzr gschickt)
+(pinche en la direcci&oacute;n para enviar correo electr&oacute;nico de notificaci&oacute;n a este usuario)
+(prema sobre o enderezo de correo electr&oacute;nico de notificaci&oacute;n a este usuario)
+(clicca l'indirizzo per spedire una e-mail di notifica a questo utente) 
+(pro odeslání oznamovacího e-mailu uživateli, klikněte na jeho adresu)
+(cliquer sur l'adresse pour prevenir l'utilisateur avec un message)
+
+Edit F*EX group
+F*EX-Gruppe editieren
+F*EX-Grupp bearbeide
+Editar el grupo F*EX
+Editar o grupo F*EX
+Edita il gruppo F*EX
+Upravit F*EX skupinu
+Éditez le groupe F*EX
+
+A F*EX group is similar to a mailing list, but for files:
+Eine F*EX-Gruppe ist einem E-Mail-Verteiler &auml;hnlich, hier geht es jedoch um Daten-Verteilung:
+A F*EX-Grupp isch so was wie en E-Mail-Verdoiler, allerdengs werde do Date verdoilt:
+Un grupo F*EX es similar a una lista de correo, pero para ficheros:
+Un grupo F*EX &eacute; semellante a unha lista de correo, pero para ficheiros:
+Un gruppo F*EX e' simile ad una lista di distribuzione, ma per i file:
+F*EX skupina je podobná poštovní konferenci s tím rozdílem, že se odesílají soubory:
+Un groupe F*EX est similaire à une liste de diffusion (mailing-list), mais pour des fichiers
+
+When a member fexes a file to this list, 
+Wenn ein Mitglied eine Datei f&uuml;r die Gruppe bereitstellt, 
+Wenn oi Mitglied a Datei fier die reschdlich Grupp nuffl&auml;d
+Cuando un miembro env&iacute;a v&iacute;a F*EX un fichero a esta lista,
+Cando un membro env&iacute;a v&iacute;a F*EX un ficheiro a esta lista,
+Quando un componente spedisce un file a questa lista,
+Odešle-li člen soubor do konference,
+Lorsque un membre envoie un fichier à cette liste
+
+then all other members will receive it
+dann werden alle anderen Mitglieder diese Datei erhalten
+no krieget &auml;lle andere wo in derre Grupp send, selle Datei
+entonces todos los dem&aacute;s miembros lo recibir&aacute;n
+ent&oacute;n todos os dem&aacute;is membros o recibir&aacute;n
+allora tutti gli altri componenti la riceveranno
+obdrží jej všichni členové
+tous les autres membres vont le recevoir
+
+New group name: <input type="text" name="group"> (You MUST fill out this field!)
+Neuer Name f&uuml;r die Gruppe: <input type="text" name="group"> (Sie M&Uuml;SSEN dieses Feld ausf&uuml;llen!)
+Neier Nome fier d' Grupp: <input type="text" name="group"> (Des Feld MUSCH Du ausfille!)
+Nuevo nombre de grupo: <input type="text" name="group"> (&iexcl;DEBE rellenar este campo!)
+Novo nome de grupo: <input type="text" name="group"> (DEBE cubrir este campo!)
+Nuovo nome del gruppo: <input type="text" name="group"> (DEVI compilare questo campo!)
+Název nové skupiny: <input type="text" name="group"> (Toto políčko MUSÍTE vyplnit!)
+Nouveau nom du groupe: <input type="text" name="group"> (Ce champ ne peut pas être vide)
+
+This list has entries in format:<td>&lt;e-mail address>
+Diese Liste besteht aus Eintr&auml;ge in folgendem Format haben:<td>&lt;E-Mail Adresse>
+Die Lischde bschdohd aus Eitr&auml;g em Format:<td>&lt;E-Mail Adresse>
+Esta lista debe tener las entradas en el formato: <td>&lt;direcci&oacute;n de correo electr&oacute;nico>
+Esta lista debe ter as entradas no formato: <td>enderezo de correo electr&oacute;nico> 
+Questa lista deve avere le righe in questo formato:<td>&lt;indirizzo e-mail>
+Položky seznamu musí být ve formátu:<td>&lt;e-mailová adresa>
+Cette liste doit avoir des entrées de la forme:<td>&lt;Adresse e-mail>
+You can name any existing e-mail address
+Sie k&ouml;nnen hier eine beliebige g&uuml;ltige E-Mail Adresse angeben
+Du kosch do a giltige E-Mail-Adress nach Deim Guschto eigebba
+Puede nombrar cualquier direcci&oacute;n de correo electr&oacute;nico
+Pode nomear calquera enderezo de correo electr&oacute;nico
+Puoi nominare qualsiasi indirizzo e-mail esistente
+Jakoukoli existující e-mailovou adresu můžete pojmenovat
+Vous pouvez désigner n'importe quelle adresse e-mail existante
+
+delete file after download
+Datei nach dem Download l&ouml;schen
+Datei nochm Ronderlada l&ouml;scha
+borrar el fichero tras su descarga
+borrar o ficheiro trala s&uacute;a descarga
+cancella il file dopo il download
+smazat soubor po stažení
+effacer le fichier après le téléchargement
+
+do not delete file after download
+Datei nach dem Download nicht l&ouml;schen
+Datei nochm Ronderlada net l&ouml;scha
+no borrar el fichero tras su descarga
+non eliminar o ficheiro despois da descarga
+non cancellare il file dopo il download
+nemazat soubor po stažení
+ne pas supprimer le fichier après le téléchargement
+
+delete file after download with delay
+Datei nach dem Download mit Verz&ouml;gerung l&ouml;schen
+Datei nochm Ronderlada erscht schp&auml;ter l&ouml;scha
+borrar el fichero tras su descarga con retardo
+borrar o ficheiro tras a s&uacute;a descarga con atraso
+cancella il file dopo il download con ritardo
+smazat soubor po stažení až po
+effacer le fichier après un délai à la suite du téléchargement
+
+delete file $autodelete days after download
+L&ouml;sche Datei $autodelete Tage nach dem Download
+L&ouml;sch Datei $autodelete Dag nochm ronderlada
+borrar archivo $autodelete dias despues del descargar
+delete file $autodelete days after download
+cancella file $autodelete giorni dopo il download
+smazat soubor po $autodelete dnech po stažení
+effacer $autodelete jours après le téléchargement
+
+F*EX service
+F*EX-Service
+F*EX-Dienscht
+Servicio F*EX
+Servizo F*EX
+servizio F*EX
+Služba F*EX
+Service F*EX
+
+Your reqested F*EX auth-ID for $fup?from=$from is:
+Ihre angefragte F*EX auth-ID fuer $fup?from=$from ist:
+Dei nochgfrogte F*EX auth-ID fir $fup?from=$from isch:
+Su auth-ID de F*EX solicitada para $fup?from=$from es:
+O seu auth-ID de F*EX solicitado para $fup?from=$from &eacute;:
+Il tuo auth-ID di F*EX che hai richiesto per $fup?from=$from e':
+Požadované ověřovací ID pro $fup?from=$from je:
+Votre auth-ID F*EX pour $fup?from=$from est:
+
+Or use:
+Oder Sie verwenden:
+Odr du nemsch:
+O use:
+Or use:
+Oppure usa
+Nebo pou¾ijte:
+Ou utilisez:
+
+Your reqested F*EX login is:
+Ihr angefragter F*EX login ist:
+Dei agfragte F*EX login isch:
+Su solicitada login de F*EX es:
+A seu login solicitado de de F*EX &eacute;:
+Il tuo login F*EX richiesto e':
+Požadované přihlašovací údaje pro F*EX:
+Votre login F*EX est:
+
+Mail has been sent to you
+Mail wurde gesendet an Sie
+Mail isch an Dich gschickt worde
+El mensaje de correo se le ha enviado 
+A mensaxe de correo xa se lle enviou
+Ti e' stata spedita una e-mail
+Zpráva vám byla odeslána
+Le message vous a été envoyé
+
+You are not in this group
+Sie sind nicht in dieser Gruppe
+Du ghersch net zu derra Grupp
+Nos es miembro de este grupo
+Non &eacute; membro deste grupo
+Non sei in questo gruppo
+Nejste členem této skupiny
+Vous n'êtes pas dans ce groupe
+
+Recipient group has no members
+Empf&auml;nger-Gruppe hat keine Mitglieder
+Dui Grupp wo des no soll hot koine Miglieder
+El grupo destinatario no tiene miembros
+O grupo de destinatarios non ten membros
+I gruppi di distribuzione non hanno membri
+Skupina příjemců nemá žádné členy
+Le groupe destinataire n'a pas de membre
+
+No such group
+Diese Gruppe existiert nicht
+Selle Grupp gibt's net. Huatsempel
+No existe tal grupo
+Non existe tal grupo
+Non esiste questo gruppo
+Tato skupina neexistuje
+Groupe non trouvé
+
+unknown dkey
+Unbekannter dkey
+Den dkey kenn i net
+dkey desconocido
+dkey desco&ntilde;ecido
+dkey sconosciuto
+neznámý dkey
+dkey inconnue
+
+has been deleted by %s at %s
+wurde gel&ouml;scht von %s um %s
+isch gl&ouml;scht worde von %s um %s
+ha sido borrado por %s a las %s
+eliminado por %s &eacute;s %s
+e' stato cancellato da %s a %s
+byl smazán uživatelem %s dne %s
+a été effacé par %s le %s
+
+$filename deleted
+$filename gel&ouml;scht
+$filename gl&ouml;scht
+$filename borrado
+$filename eliminado
+$filename cancellato
+$filename byl smazán
+$filename effacé
+
+$filename not deleted ($s)
+$filename nicht gel&ouml;scht ($s)
+$filename net gl&ouml;scht
+$filename no borrado ($s)
+$filename non eliminado ($s)
+$filename non cancellato ($s)
+$filename nebyl smazán ($s)
+$filename non effacé ($s)
+
+>continue
+>weiter
+>Weiterschaffe
+>continuar
+>continuar
+>continua
+>pokračovat
+>continuer
+
+Files from
+Dateien von
+Dateia von
+Ficheros de
+Ficheiros de
+File da
+Soubory od
+Fichiers de
+
+notification e-mail has been resent
+Benachrichtigungs-E-Mail wurde erneut verschickt
+no amole Bscheid gsagt
+Email de notificación reenviado
+notification e-mail has been resent
+E-mail di notifica è stata rispedita
+Zpráva s upozorněním byla obnovena
+l'email de notification a été réémis
+
+click on the file name to resend a notification e-mail
+klicken Sie auf den Dateinamen um die Benachrichtigungs-E-Mail erneut zu verschicken
+klick uf dr Dateinama, om dem Empf&auml;ngr no amole Bscheid saga
+haga clic en el nombre de archivo para reenviar
+click on the file name to resend a notification e-mail
+clicca sul nome del file per rispedire un'e-mail di notifica
+pro opětovné odeslání zprávy s upozorněním klikněte na název souboru
+cliquer sur le nom du fichier pour envoyer à nouveau un email de notification
+
+Files for $to (*):
+Dateien f&uuml;r $to (*):
+Dateia fier $to (*):
+Ficheros para %to (*):
+Ficheiros para %to (*):
+File per $to (*):
+Soubory pro $to (*):
+Fichiers pour $to (*):
+
+\nfrom $from 
+\nVon $from 
+\nVon $from 
+\nde $from
+\nde $from
+\nda $from
+\nod $from 
+\nde $from 
+
+>delete<
+>l&ouml;schen<
+>l&ouml;scha<
+>borrar<
+>eliminar<
+>cancella<
+>smazat<
+>effacer<
+
+>forward<
+>weiterleiten<
+>weiterleita<
+>reenviar<
+>forward<
+>prosegui<
+>pøedat<
+>faire suivre<
+
+Files for other e-mail addresses you own will not be listed here!
+Dateien f&uuml;r Ihre andere E-Mail Adressen werden hier nicht aufgelistet!
+Dateia fier Deine andre E-Mail Adressa werded dohanne net uffglischtet!
+&iexcl;Los ficheros para otras direcciones de correo electr&oacute;nico suyas no son listadas aqu&iacute;!
+Os ficheiros para outros enderezos de correo electr&oacute;nico que te&ntilde; non se listan aqu&iacute;!
+File per altri indirizzi di e-mail che ti appartengono non saranno elencati qui!
+Soubory pro další e-mailové adresy, které vlastníte, zde nebudou uvedeny!
+Les fichiers associés à vos autres adresses ne vont pas être listés ici!
+
+you are overquota
+Sie haben Ihr Speichervolumen &uuml;berzogen
+Dei Schbeicherplatz isch rappelvoll
+ha excedido su cuota
+sobrepasou a s&uacute; cota
+Hai superato la quota
+překročili jste limit
+vous avez dépassé votre quota
+
+cannot receive files: is overquota
+Kann Dateien nicht annehmen: Speichervolumen &uuml;berzogen
+I ko die Dateia net onemme: Dein Schbeicher isch scho vollgschdopft
+no puede recibir ficheros: ha excedido su cuota
+non pode recibir ficheiros: sobrepasou a s&uacute;a cota
+non e' possibile ricevere il file: eccede la quota assegnata 
+soubory nelze přijmout: překračují limit
+ne peut pas recevoir de fichiers: quota dépassé
+
+You ($from) are not allowed to fex to $to
+Sie ($from) d&uuml;rfen nicht fexen an $to
+Du ($from) derfsch net fexa an $to
+Usted ($from) no puede enviar v&iacute;a F*EX a $to
+Vostede ($from) non est&aacute; autorizado para enviar, v&iacute;a F*EX, a $to
+Tu ($from) non hai il permesso di spedire via F*EX a $to
+Vy ($from), nemáte dovoleno odesílat soubory pro $to
+Vous ($from) n'êtes pas autorisé à envoyer un fichier à $to
+
+recipient <code>$to</code> is not a registered F*EX full or sub user
+Emp&auml;nger $to ist kein registrierter F*EX Voll- oder Subuser
+Dr Empf&auml;nger $to isch koin regischtrierder F*EX Voll-  oddr Onderbenutzr
+el destinatario $to no es un usuario normal de F*EX registrado o un subusuario
+o destinatario $to non &eacute; un usuario normal de F*EX rexistrado ou un subusuario
+il destinatario $to non e' un utente o sotto-utente F*EX registrato
+příjemce $to není zaregistrovaným plnohodnotným uživatelem či poduživatelem F*EX
+le destinataire <code>$to</code> n'est pas un utilisateur ou un sous-utilisateur F*EX
+
+no upload key
+kein upload-Einmalschl&uuml;ssel
+koi nufflad-Oimolschlissel
+ningun llave para subir
+non se cargou a chave
+no chiave di caricamento
+žádný klíč pro nahrání
+pas de clé d'upload
+
+request another one from
+fordern Sie einen neuen an von
+frog noch'm Neua bei
+pida otro de
+solicitar outra en
+richiedine un altro da
+požádat o jiný uživatele
+demander un autre de
+
+Your client seems to be "$1" which is incompatible with F*EX and will probably not work
+Ihr Client scheint "$1" zu sein, das ist aber inkompatibel mit F*EX und wird vermutlich nicht funktionieren
+Dein Client isch scheints "$1", des isch abbr inkomatibel mit F*EX ond so wird des wahrscheinlich nix
+Su cliente parece ser "$1" que es incompatible con F*EX y probablemente no funcionar&aacute;
+O seu cliente parece ser "$1" que &eacute; incompat&iacute;bel con F*EX e probablemente non funcionar&aacute;
+Il vostro client sembra essere "$1", che e' incompatibile con F*EX e probabilmente non funzionera'
+Zřejmě používáte prohlížeč "$1", který není kompatibilní s F*EX a pravděpodobně nebude správně fungovat
+Votre client semble être "$1" qui est incompatible avec F*EX et ne va sans doute pas fonctionner
+
+We recommend firefox
+Wir empfehlen \"Firefox\"
+Mir empfehled \"Firefox\"
+Recomendamos firefox
+Recomendamos firefox
+Raccomandiamo firefox
+Doporučujeme používat Firefox
+Nous recommandons firefox
+
+sender:
+Absender:
+Absendr:
+remitente:
+remitente:
+mittente:
+odesilatel:
+expéditeur:
+
+recipient:
+Empf&auml;nger:
+Empf&auml;ngr:
+destinatario:
+destinatario:
+destinatario:
+příjemce:
+destinataire:
+
+recipient(s):
+Empf&auml;nger:
+Empf&auml;ngr:
+destinatario(s):
+destinatario(s):
+destinatario(i):
+příjemce(i):
+destinataire(s):
+
+recipient(s)<
+Empf&auml;nger<
+Empf&auml;ngr<
+destinatario(s)<
+destinatario(s)<
+destinatario(i)<
+příjemce(i)<
+destinataire(s)<
+
+e-mail address or alias
+E-Mail Adresse oder Alias
+E-Mail Adress oddr Alias
+direcci&oacute;n de correo electr&oacute;nico o alias
+enderezo de correo electr&oacute;nico ou alias
+indirizzo e-mail o alias
+e-mailová adresa či alias
+adresse e-mail ou alias
+
+or select from your address book
+oder aus Ihrem Adressbuch w&auml;hlen
+oddr aus Deim Adressb&uuml;chle raussuche
+o seleccione de su libro de direcciones
+ou seleccione da s&uacute;a axenda de enderezos
+o seleziona dal tuo rubrica
+nebo si vyberte ze svého adresáře
+ou sélectionner de votre carnet d'adresses
+
+and'
+und'
+ond'
+y'
+e'
+e
+a'
+et'
+
+add to recipients list
+zur Empf&auml;nger-Liste hinzuf&uuml;gen
+uff d' Empf&auml;ngerlischte setze
+a&ntilde;ada a la lista de destinatarios
+engadir &aacute; lista de destinatarios
+aggiungi alla lista di distribuzione
+přidejte jej (je) do seznamu příjemců
+ajouter à la liste des destinataires
+
+check recipient(s) and continue
+Empf&auml;nger &uuml;berpr&uuml;fen und fortsetzen
+Empf&auml;nger ieberpriafe ond weitermache
+compruebe el/los destinatario(s) y contin&uacute;e
+comprobe o/os destinatario(s) e contin&uacute;e
+controlla i/il destinatari(o) e continua
+zkontrolovat příjemce a pokračovat
+vérifier le ou les destinataires et continuer
+
+>or<
+>oder<
+>odr<
+>o<
+>or<
+>oppure<
+>nebo<
+>ou<
+
+ or <
+ oder <
+ odr <
+ o <
+ or <
+ oppure <
+ nebo <
+ ou <
+
+and <
+und <
+ond <
+y <
+e <
+e <
+a <
+et <
+
+You are a restricted user and may only fex to these recipients:
+Sie sind ein eingeschr&auml;nkter Benutzer und k&ouml;nnen nur an diese Empf&auml;nger fexen:
+Du bisch a eigeschr&auml;nktr Benutzr ond kosch bloss an die Empf&auml;ngr fexa:
+Usted es un usuario restringido y solo puede enviar a estos destinatarios:
+You are a restricted user and may only fex to these recipients:
+Sei un utente limitato e puoi inviare solo a questi destinatari:
+Jste uživatel s omezením a můžete odesílat pouze těmto příjemcům:
+Vous êtes un utilisateur restreint et vous ne pouvez utiliser fex que pour ces destinataires:
+
+fex yourself
+eigene Adresse verwenden
+fex dir selbr
+usar la propria dirección
+fex yourself
+fex te stesso
+zaslat sobě
+à votre adresse
+
+>user config & operation control
+>Benutzer Bedienungssteuerung
+>Benutzr Bdienongssteierong
+>configuraci&oacute;n de usuario y control de operaci&oacute;n
+>configuraci&oacute;n de usuario e control de operaci&oacute;n
+>configurazion utente & controllo operazioni
+>nastavení uživatele a řízení provozu
+>configuration utilisateur et gestion
+
+Alternate Java client</a> (for files > 2 GB or sending of more than one file)
+Alternativer Java Client</a> (f&uuml;r Dateien gr&ouml;&szlig;er als 2 GB oder zum Senden von mehr als einer Datei)
+Alternativer Java Client</a> (fier Dateia wo gr&ouml;&szlig;er send als 2 GB oder zum Sende von meh als oiner Datei)
+Cliente java alternativo</a> (par ficheros > 2 GB o env&iacute;o de m&aacute;s de un fichero)
+Cliente java alternativo</a> (para ficheiros > 2 GB ou env&iacute;o de m&aacute;s dun fichero)
+Client java alternativo</a> (per file > 2 GB o per spedizioni di piu' di un file)
+Alternativní Java klient</a> (pro soubory větší než 2 GB či pro odesílání více než jednoho souboru)
+Client Java alternatif</a> (pour les fichiers > 2 GB ou envoyer plusieurs fichiers d'un coup)
+
+You have to fill out this form completely to continue
+Sie m&uuml;ssen dieses Formular komplett ausf&uuml;llen um fortzufahren
+Du musch des Formular ganz ausff&uuml;lla sonsch kosch ned weitrmacha
+Hace falta cumplimentar este formulario completamente antes de continuar
+Fai falta cubrir este formulario completamente antes de continuar
+Devi riempire completamente questo form per continuare
+Pro pokračování musíte kompletně vyplnit formulář
+Merci de compléter ce formulaire pour continuer
+
+sender quota (used):<td>$quota ($du) MB
+Absender Quota (benutzt):<td>$quota ($du) MB
+Absendr Quota (benutzt):<td>$quota ($du) MB
+cuota de remitente (usada):<td>$quota ($du) MB
+cota de remitente (usada):<td>$quota ($du) MB
+quota utente (usata):<td>$quota ($du) MB
+limit pro odesílání (used):<td>$quota ($du) MB
+quota expéditeur (utilisé):<td>$quota ($du) MB
+
+autodelete:
+Automatisches L&ouml;schen:
+Automadischs L&ouml;sche:
+autoborrar:
+autoeliminaci&oacute;n:
+autocancellazione:
+automatické mazání:
+suppression automatique
+
+keep file max $keep days, then delete it
+Behalte die Datei f&uuml;r max. $keep Tage, dann l&ouml;sche sie
+Bhalt die Datei fier h&ouml;chschdens $keep Tage, dann kosch se l&ouml;sche
+conserve el fichero un m&aacute;ximo de $keep d&iacute;as, a continuaci&oacute;n borrelo
+conserve o ficheiro un m&aacute;ximo de $keep d&iacute;as, a seguir b&oacute;rreo
+conserva il file al massimo per $keep giorni, quindi cancellalo
+uchovat soubor maximálně $keep dnů, pak jej smazat
+garder les fichiers au maximum $keep jours et les supprimer
+
+keep:
+Bleibt auf Server:
+Bleibt uffm Server:
+conserve:
+gardar:
+mantieni:
+uchovat:
+conserver:
+
+$keep days
+$keep Tage
+$keep Dag
+$keep d&iacute;as
+$keep d&iacute;as
+$keep giorni
+$keep dnů
+$keep jours
+
+comment:
+Kommentar:
+Kommendar:
+comentario:
+comentario:
+commento:
+komentář:
+commentaire:
+
+bandwith limit
+Bandbreitenbeschr&auml;nkung
+Bandbroidebschr&auml;nkong
+anchura de banda
+largo de banda
+limite di banda
+omezení rychlosti
+limite de bande passante
+
+optional, full speed if empty
+optional, volle Geschwindigkeit wenn leer
+optional, volle Lotte wenn leer
+opcional, velocidad maximal en caso de vacia
+opcional, velocidade m&aacute;xima cando baleira
+opzionale, massima velocita' se vuoto
+volitelné, je-li prázdné, využije se plná rychlost
+optionnel, pas de limite si non renseignée
+
+optional, will be included in notification e-mail
+optional, wird in Benachrichtigungs-E-Mail mitgeschickt
+optional, isch no en dr Benochrichtigongs-Mail mit drbei
+opcional, será enviado por email de notificación
+optional, will be included in notification e-mail
+opzionale, sarà incluso nell'e-mail di notifica
+volitelné, připojí se ke zprávě s upozorněním
+optionnel, sera inclus dans votre email de notification
+
+no notification e-mail will be send
+es wird keine Benachrichtigungs-E-Mail verschickt
+s\'wird koi Benochrichtigongs-Mail verschickt
+No se enviará ningún correo de notificación
+non se enviará notificación de correo
+nessuna notificazione via e-mail verrà spedita
+žádný e-mail s upozorněním se neodešle
+aucun message de notification ne sera envoyé
+
+If you want to send more than one file, then put them in a zip or tar archive
+Wenn Sie mehr als eine Datei senden m&ouml;chten, dann erzeugen Sie ein zip oder tar Archiv
+Wenn De meh als oi Datei verschigge willsch, no mach a zip oddr a tar Archiv
+Si desea enviar m&aacute;s de un fichero p&oacute;ngalos en un archivo zip
+De querer enviar m&aacute;s dun fichero p&oacute;&ntilde;aos nun arquivo zip
+Se vuoi spedire piu' di un file, mettili in un archivio zip o tar
+Chcete-li odeslat více jak jeden soubor, zkomprimujte jej do zip nebo tar archivu
+Si vous voulez envoyer plus d'un fichier, mettez les dans un zip ou une archive tar
+
+file:
+Datei:
+Datei:
+fichero:
+ficheiro:
+file:
+soubor:
+fichier:
+
+user config & operation control
+Benutzer Konfigurations- und Bedienungssteuerung
+Benutzr Konfigurations- ond Bedienongssteierong
+configuraci&oacute;n de usuario & control de operaciones
+configuraci&oacute;n de usuario e control de operaci&oacute;ns
+configurazione utente & controllo operazioni
+nastavení uživatele a řízení provozu
+configuration utilisateur & gestion
+
+I have lost my auth-ID! Send it to me by e-mail! 
+Habe meine auth-ID vergessen! Bitte mir diese per E-Mail zuschicken! 
+I hann mei auth-ID vrschlampt! Schick se mir no mol per E-Mail!
+&iexcl;He perdido mi auth-ID! &iexcl;Env&iacute;emelo por correo electr&oacute;nico!
+Perd&iacute;n o meu auth-ID! &iexcl;Env&iacute;emo por correo electr&oacute;nico!
+Ho perso il mio auth-ID! Spediscimelo via e-mail!
+Zapomněl jsem své ověřovací ID! Odešli mi jej na e-mail!
+J'ai perdu mon auth-ID ! Le recevoir par e-mail !
+
+I have lost my auth-ID
+Habe meine auth-ID vergessen
+I hann mei auth-ID vrschlampt
+He perdido mi auth-ID
+Perd&iacute;n o meu auth-ID
+Ho perso il mio auth-ID
+Zapomněl jsem své ověřovací ID
+J'ai perdu mon auth-ID
+
+you must fill out sender field above
+Sie m&uuml;ssen oben dazu das \"Absender\"-Feld ausf&uuml;llen
+Du musch drzu do obe des  \"Absendr\"-Feld ausfille
+debe rellenar el campo remitente anterior
+debe cubrir o campo remitente anterior
+devi riempire il campo mittente sopra
+musíte vyplnit políčko odesilatel výše
+vous devez renseigner le champ expéditeur
+
+check ID and continue
+weiter
+weider
+comprobar el ID y continuar
+comprobar o ID e continuar
+controlla ID e continua
+ověřit ID a pokračovat
+vérifier l'ID et continuer
+
+no file specified
+Keine Datei angegeben
+Koi Datei ogebbe
+no se espcific&oacute; el fichero
+non se espcific&oacute;u o ficheiro
+nessun file specificato
+nebyl uveden soubor
+aucun fichier spécifié
+
+no sender specified
+Kein Absender angegeben
+Koin Absendr ogebbe
+no se especific&oacute; el remitente
+non se especific&oacute;u o remitente
+nessun mittente specificato
+nebyl uveden odesilatel
+aucun expéditeur spécifié
+
+no recipient specified
+Kein Empf&auml;nger angegeben
+Koin Empf&auml;ngr ogebbe
+no se especific&oacute; el destinatario
+non se especific&oacute; o destinatario
+nessun destinatario specificato
+nebyl uveden příjemce
+aucun destinataire spécifié
+
+wrong auth-ID specified
+Falsche auth-ID angegeben
+Falsch auth-ID ogebbe
+auth-ID especificado err&oacute;neo
+o auth-ID especificado &eacute; incorrecto
+auth-ID indicato sbagliato
+bylo uvedeno chybné ověřovací ID
+auth-ID erroné
+
+address book updated
+Adressbuch aktualisiert
+Adressb&uuml;chle isch uffm neueschte Schdand
+libro de direcciones actualizado
+axenda de enderezos actualizada
+rubrica aggiornata
+adresář byl aktualizován
+carnet d'adresses mis à jour
+
+cannot rename $upload to
+Konnte $upload nicht umbenennen in
+I hab $upload net umbenenne k&ouml;nne in
+no se puede renombrar $upload a
+non se pode renomear $upload como
+non posso rinominare $upload in
+nepodařilo se přejmenovat $upload na
+ne peut pas renommer $upload en
+
+No file data received
+Keine Daten erhalten
+Koine Date kriegt
+No se ha recibido ning&uacute;n fichero de datos
+Non se recibiu ning&uacute;n ficheiro de datos
+Nessun file di dati ricevuto
+Data souboru nebyla přijata
+aucune donnée reçue
+
+File name correct?
+Stimmt der Dateiname?
+Isch der Dateinome richdig?
+&iquest;Nombre de fichero correcto?
+Nome de ficheiro correcto?
+Nome file corretto?
+Je název souboru správný?
+Est-ce que le nom du fichier est correct ?
+
+File too big (browser-limit: 2 GB!)?
+Datei zu gro&szlig; (Browser-Limit: 2 GB!)?
+Die Datei isch zu gro&szlig; (Browser-Limit: 2 GB!)?
+&iquest;Fichero demasiado grande (l&iacute;mite del navegador: &iexcl;2GB!)?
+Ficheiro demasiado grande (l&iacute;mite do navegador: 2GB!)?
+File troppo grande (limite-browser: 2 GB!)?
+Není soubor příliš velký (limit prohlížeče: 2 GB!)?
+Fichier trop volumineux (limite du navigateur: 2GB!)?
+
+is not allowed at beginning of 
+ist nicht erlaubt am Anfang von 
+des derfsch net mache am Ofang von
+no se permite al principio de
+non se permite ao principio de
+non e' consentito all'inizio di
+není povoleno na začátku
+n'est pas autorisé au début de
+
+is not allowed in 
+ist nicht erlaubt in 
+des derfsch net mache in
+no se permite en
+non se permite en
+non e' consentito in
+není povoleno v
+n'est pas autorisé dans
+
+is not allowed
+ist nicht erlaubt
+des derfsch net mache
+no se permite
+non se permite
+non e' consentito
+není povoleno
+n'est pas autorisé
+
+is not allowed at end of 
+ist nicht erlaubt am Ende von 
+des derfsch net mache am End von
+no se permite al final de
+non se permite ao final de
+non e' consentito alla fine di
+není povoleno na konci
+n'est pas autorisé à la fin de 
+
+invalid Content-Length
+Ung&uuml;ltiger Content-Length
+Ung&uuml;ltiger Content-Length
+Content-Length inv&aacute;lido
+O Content-Length &eacute; incorrecto
+Content-Length non valido
+Content-Length není platné
+Content-Length invalide
+
+not enough free space for this upload
+Nicht genug freier Speicher f&uuml;r diesen Upload
+'S isch net gnug Schbeicher frei om des nuffzulade
+no hay espacio libre suficiente para la subida
+non hai espazo libre suficiente para a subida
+non c'e' abbastanza spazio per questo caricamento
+pro nahrání není dost místa
+pas assez d'espace libre pour cet upload
+
+missing sender e-mail address
+E-Mail Adresse vom Sender fehlt
+koi E-Mail Adress vom Sender
+falta la direcci&oacute;n del remitente
+falt o enderezo do remitente
+manca l'indirizzo e-mail del mittente
+chybí e-mailová adresa odesilatele
+e-mail de l'expéditeur manquant
+
+is not a valid e-mail address
+ist keine g&uuml;ltige E-Mail Adresse
+des isch koi giltige E-Mail Adress
+no es una direcci&oacute;n de correo electr&oacute;nico v&aacute;lido
+non &eacute; un enderezo de correo electr&oacute;nico correcto
+non e' un indirizzo e-mail di posta valido
+není platná e-mailová adresa
+n'est pas une adresse e-mail valide
+
+no file data received - does your file exist or is it >2GB
+Keinen Datensatz erhalten - existiert die Datei wirklich oder ist es gr&ouml;&szlig;er als 2 GB
+I hab koine Date kriegt - Gibt's dui Datei ieberhaupt oddr isch se gr&ouml;&szlig;er als 2 GB
+no se ha recibido ning&uacute;n fichero - existe su fichero o es >2GB
+non se recibiu ning&uacute;n ficheiro - existe o seu ficheiro ou &eacute; >2GB
+non e' stato ricevuto nessun file - il tuo file esiste o e' >2GB
+data souboru nebyla přijata - zkontrolujte, zda soubor existuje nebo není větší než 2 GB
+pas de donnée reçue - est-ce que votre fichier existe or est-il >2GB
+
+file size unknown
+Dateigr&ouml;&szlig;e unbekannt
+Koi Ohnung, wie gro&szlig; dui Datei isch
+tama&ntilde;o del fichero desconocido
+tama&ntilde;o do ficheiro desco&ntilde;ecido
+dimensione file sconosciuta
+neznámá velikost souboru
+taille du fichier inconnue
+
+file size
+Dateigr&ouml;&szlig;e
+So fett isch des Doil
+tama&ntilde;o del fichero
+tama&ntilde;o do ficheiro
+dimensione file
+velikost souboru
+taille du fichier
+
+No file selected
+Keine Datei ausgew&auml;hlt
+Koi Datei ausgw&auml;hlt
+Ningún archivo seleccionado
+No file selected
+Nessun file selezionato
+Žádný soubor nebyl vybrán
+Aucun fichier sélectionné
+
+no filename?!
+Kein Dateiname?!
+Koin Dateinome?!
+&iexcl;&iquest;no hay nombre de fichero?!
+non hai nome de ficheiro?!
+manca il nome del file ?!
+zadali jste název souboru?!
+pas de nom de fichier ?!
+
+transfer is already in progress
+ein Transfer l&auml;uft gerade
+'S isch grad en Transfer am laufe
+la transferencia ya est&aacute; en curso
+a transferencia xa est&aacute; en marcha
+il trasferimento e' gia' in corso
+přenos již probíhá
+le transfert est déjà en cours.
+
+a download is currently in progress
+ein Download l&auml;uft gerade
+'S wird grad was ronderglade
+una descarga est&aacute; ahora en curso
+unha descarga est&aacute; agora en marcha
+un download e' al momento in corso
+probíhá stahování
+un téléchargement est en cours
+
+no file data for
+Keine Datei-Daten f&uuml;r
+Koine Datei-Date fier
+no ha fichero para
+non hai ficheiro para
+nessun file per
+žádná data souboru pro
+pas de données pour
+
+File "$filename" copy-forwarded to $to and notified
+Datei "$filename" wurde kopiert und an $to weitergeleitet. $to wurde benachrichtigt
+Datei "$filename" isch kopiert ond an $to weitergleided worde. $to isch benochrichdigt
+Fichero "$filename" copiada reenviada a $to y notificado
+Ficheiro "$filename" copiada reenviada a $to e notificada
+File "$filename" copia inoltrata a $to e notificata
+Soubor "$filename" kopie byla předána na $to a oznamovací e-mail byl odeslán
+Le fichier "$filename" a été retransmis à $to et un message a été envoyé
+
+forward a copy of "<code>$filename</code>" to:
+Eine Kopie von "<code>$filename</code>" weiterleiten an:
+A Kopie von "<code>$filename</code>" weiterschicke an:
+reenviada una copia de "<code>$filename</code>" a:
+reenviada unha copia de "<code>$filename</code>" a:
+inoltra una copia di "<code>$filename</code>" a:
+předat kopii souboru "<code>$filename</code>" na:
+Transmettre une copie de "<code>$filename</code>" à:
+
+F*EX user registration ERROR
+F*EX-Benutzer Registrierungsfehler
+F*EX-Benutzr Regischdrierongsfehler
+ERROR en el registro del usuario F*EX
+ERRO no rexistro do usuario F*EX
+ERRORE di registrazione utente F*EX
+Chyba při registraci F*EX uživatele
+ERREUR lors de l'enregistrement de l'utilisateur F*EX
+
+F*EX user registration
+F*EX-Benutzer Registrierung
+F*EX-Benutzr Regischdrierong
+registro del usuario de F*EX
+rexistro do usuario de F*EX
+registrazione utente F*EX
+registrace F*EX uživatele
+Enregistrement d'un utilisateur F*EX
+
+illegal registration key
+Illegaler Registrierungs-Schl&uuml;ssel
+Der Regischdrierongsschlissel gildet net
+clave de registro ilegal
+chave de rexistro ilegal
+chiave di registrazione illegale
+neplatný registrační klíč
+Clé d'enregistrement illégale
+
+no registration key
+Kein Registrierungs-Schl&uuml;ssel
+Koin Regischdrierongsschlissel
+sin clave de registro
+sen chave de rexistro
+non c'e' la chiave di registrazione
+žádný registrační klíč
+pas de clé d'enregistrement
+
+no registration data for key
+Keine Registrierungs-Daten f&uuml;r Schl&uuml;ssel
+Koine Regischdrierongsdate fier den Schlissel
+no hay datos de registro para la clave
+non hai datos de rexistro para a chave
+non ci sono dati di registrazione per la chiave
+žádná registrační data pro klíč
+pas de données d'enregistrement pour la clé
+
+Your registration was successful. Your new F*EX account is:
+Ihre Registrierung war erfolgreich. Ihr neuer F*EX-Account ist:
+Du hoschs gschafft. Dein neies F*EX-Konto isch:
+Su registro tuvo existo. Su nueva cuenta F*EX es:
+O rexistro foi correcto. A s&uacute;a nova conta F*EX &eacute;:
+La tua registrazione e' stata eseguita con successo. Il tuo nuovo utente F*EX e':
+Registrace proběhla úspěšně. Váš nový F*EX účet je:
+Votre enregistrement s'est bien déroulé. Votre nouveau compte F*EX est:
+
+bookmark this URL
+nehmen Sie diese URL in ihre Bookmarks auf
+des musch en deine Bookmarks aufnehma
+Crear marcador de este URL
+engadir este enderezo URL aos marcadores
+inserisci questo URL nei preferiti
+přidat URL k oblíbeným
+sauvegarder cette URL
+
+or you can use
+oder Sie verwenden
+oder du nemsch
+o puede usar
+ou pode utilizar
+o puoi usare
+nebo můžete
+ou vous pouvez utiliser
+
+Your domain <code>$exd</code> is not allowed for registration
+Ihre Domain <code>$exd</code> ist nicht zugelassen f&uuml;r Registrierung
+Dei Domain <code>$exd</code> ghod ned for d'Regischtrierong
+Su dominio <code>$exd</code> no está permitido a registrar
+Your domain <code>$exd</code> is not allowed for registration
+Il tuo dominio <code>$exd</code> non è permesso per la registrazione
+Registrace z domény <code>$exd</code> není povolena
+Votre domaine <code>$exd</code> n'est pas autorisé à l'enregistrement
+
+Registrations from your host ($ra) are not allowed
+Registrierungen von Ihrem Host ($ra) sind nicht erlaubt
+Regischdrieronga von Deim Host ($ra) send net geschdaddet
+Los registros desde su equipo ($ra) no est&aacute;n permitidos
+Os rexistros desde o seu equipo ($ra) non est&aacute;n permitidos
+Registrazioni dal tuo host ($ra) non sono permesse
+Registrace z vašeho počítače ($ra) nejsou povoleny
+Les enregistrements à partir de votre poste ($ra) ne sont pas autorisés
+
+Contact $ENV{SERVER_ADMIN} for details
+Kontaktieren Sie $ENV{SERVER_ADMIN} f&uuml;r Details
+Wend De an $ENV{SERVER_ADMIN}, wenn De N&auml;heres wisse willsch
+Contacte con $ENV{SERVER_ADMIN} para obtner m&aacute;s detalles
+Contacte con $ENV{SERVER_ADMIN} para obter m&aacute;s detalles
+Contatta $ENV{SERVER_ADMIN} per i dettagli
+Pro více informací kontaktujte $ENV{SERVER_ADMIN}
+Contacter $ENV{SERVER_ADMIN} pour des détails supplémentaires
+
+$user is disabled
+$user ist gesperrt
+$user isch gschperrt
+$user está bloqueado
+$user is disabled
+$user è disabilitato
+$user je zakázán
+$user est désactivé
+
+or<p>
+oder<p>
+odr<p>
+o<p>
+ou<p>
+o<p>
+nebo<p>
+ou<p>
+
+you are already registered
+Sie sind bereits registriert
+Du bisch doch scho l&auml;ngscht regischdriert
+ya est&aacute; registrado
+xa est&aacute; rexistrado
+sei gia' registrato
+již jste zaregistrovaní
+Vous êtes déjà enregistré
+
+new user (may send to internal or external users)
+neuer Benutzer (darf an interne oder externe Benutzer senden)
+neur Benutzr (darf an intern odr extern Benutzr senda)
+nuevo usuario (permitido a enviar a usuarios internos o externos)
+novo usuario (permitido enviar a usuarios internos ou externos)
+nuovo utente (potresti spedire ad utenti interni o esterni)
+nový uživatel (může odesílat interním či externím uživatelům)
+nouvel utilisateur (peut envoyer à des utilisateurs internes ou externes)
+
+new external user (may send only to internal users)
+neuer externer Benutzer (darf nur an interne Benutzer senden)
+neur externer Benutzr (darf bloss an interne Benutzr senda)
+nuevo usuario externo (solamente permitido a enviar a usuarios internos)   
+novo usuario externo (soamente pode enviar a usuarios internos)
+nuovo utente esterno (potrebbe spedire solo a utenti interni)
+nový externí uživatel (může odesílat pouze interním uživatelům)
+nouvel utilisateur externe (peut seulement envoyer à des utilisateurs internes)
+
+internal domains are
+interne Domains sind
+interne Domains senn
+dominio internos 
+son dominios internos
+domini interni sono
+interní domény jsou
+les domaines internes sont
+
+allowed domains are:
+zugelassene Domains sind:
+die Domains darfsch benutza:
+domínios permitidos son:
+allowed domains are:
+domini consentiti sono:
+povolené domény jsou:
+les domaines autorisés sont:
+
+you must enter your e-mail address and
+Sie m&uuml;ssen Ihre E-Mail Adresse eingeben
+Du musch dei E-Mail Adress ogeba
+hace falta introducir su direcci&oacute;n de correo electr&oacute;nico
+debe escribir o seu enderezo de correo electr&oacute;nico e
+devi inserire un indirizzo e-mail e 
+musíte zadat svou e-mailovou adresu a
+vous devez donner votre adresse e-mail et
+
+your e-mail address
+Ihre E-Mail Adresse
+dei E-Mail Adress
+s&uacute; direcci&oacute;n de correo electr&oacute;nico
+o s&eacute;u enderezo de correo electr&oacute;nico
+il tuo indirizzo e-mail
+vaše e-mailová adresa
+votre adresse e-mail
+
+F*EX user registration
+F*EX-Benutzer Registrierung
+F*EX-Benutzrregischdrierong
+Registro del usuario de F*EX
+Rexistro do usuario de F*EX
+Registrazione utente F*EX
+Registrace F*EX uživatele
+Enregistrement F*EX
+
+$user has been auto-registrated with
+$user wurde auto-registriert mit
+$user isch auto-regischdriert worde mit
+$user ha sido autoregistrado con
+$user foi autorexistrado con
+$user e' stato autoregistrato con
+$user byl automaticky zaregistrován s
+l'utilisateur $user a été automatiquement enregistré avec
+
+F*EX user registration request
+F*EX-Benutzer Registrierungs-Anfrage
+F*EX-Benutzrregischdrierongsofrog
+Petici&oacute;n de registro de usuario F*EX
+Petici&oacute;n de rexistro de usuario F*EX
+Richiesta registrazione utente F*EX
+Požadavek na zaregistrování F*EX uživatele
+Demande d'enregistrement d'utilisateur F*EX
+
+To activate your new F*EX account go to this URL:
+Um Ihren neuen F*EX-Account zu aktivieren oeffnen Sie diese URL:
+Om Die neis F*EX-Konto zu aktiviere, gang uff die URL:
+Para activar su nueva cuenta en F*EX vaya a esta URL:
+Para activar a s&uacute;a nova conta en F*EX vaia a este URL:
+Per attivare il tuo nuovo utente F*EX vai a questo URL:
+Pro aktivaci svého nového F*EX účtu přejděte na tuto URL:
+Pour activer votre nouveau compte F*EX, rendez vous à cette URL:
+
+The conditions of usage are:
+Die Benutzungsbedingungen sind:
+Die Mitmachregla senn:
+Los términos de uso son:
+The conditions of usage are:
+Le condizioni d'uso sono:
+Podmínky používání jsou:
+Les conditions d'usage sont:
+
+confirmation e-mail has been sent to <code>$user
+Best&auml;tigungs-E-Mail wurde gesendet an Benutzer <code>$user
+Beschd&auml;digongs-E-Mail isch gschickt worde an Benutzr <code>$user
+Un correo electr&oacute;nico de confirmaci&oacute;n se le ha enviado a <code>$user
+Envi&oacute;uselle un correo electr&oacute;nico de confirmaci&oacute;n a <code>$user
+e-mail di conferma e' stata spedita a <code>$user
+potvrzovací e-mail byl odeslán uživateli <code>$user
+Un message de confirmation a été envoyé à <code>$user
+
+This is an automatically generated e-mail
+Dies ist eine maschinell generierte E-Mail
+Des isch a Automata-E-Mail
+Este e-mail ha sido generado automaticamente
+Este correo foi xerado automaticamente
+Questa e' una e-mail generata automaticamente
+Toto je automaticky vytvořený e-mail
+Ceci est un message généré automatiquement
+
+Illegal domain for username
+Illegale Domain fuuml;r Benutzernamen
+Illegale Domain fier de Benutzrnome
+Dominio ilegal para el nombre de usuario
+O dominio deste nome de usuario &eacute; irregular
+Dominio illegale per l'utente
+Neplatná doména pro uživatelské jméno
+Domaine invalide pour l'utilisateur
+
+is not an email address
+ist keine E-Mail-Adresse
+isch koi E-Mail-Adress
+no es una dirección email
+is not an email address
+non è un indirizzo e-mail
+není e-mailová adresa
+n'est pas une adresse électronique
+
+F*EX redirect ERROR
+F*EX Umadressierungs-FEHLER
+F*EX Bei der Omadressierong isch was schiefgloffe
+ERROR de redirecci&oacute;n de F*EX
+Produciuse un ERRO de redirecci&oacute;n de F*EX
+ERRORE di inoltro F*EX
+CHYBA při přesměrování F*EX
+ERREUR de redirection F*EX
+
+wrong user or auth-ID
+Falscher Benutzer oder falsche auth-ID
+Benutzr oddr auth-ID send falsch
+usuario o auth-ID err&oacute;neo
+o usuario ou o auth-ID son incorrectos
+utente o auth-ID sbagliato
+chybný uživatel nebo ověřovací ID
+utilisateur ou auth-ID invalide
+
+has no no files in spool from you
+hat keine Dateien in spool von Ihnen
+hat von Dir koine Dateia em spool
+no hay ficheros en la cola que haya enviado usted
+non hai ficheiros na cola da s&uacute;a parte
+non ci sono file nello spool per te
+nemá od vás ve frontě žádné soubory
+n'a pas de fichiers venant de vous
+
+F*EX redirect
+F*EX Umadressierung
+F*EX Omadressierong
+redirecci&oacute;n F*EX
+redirecci&oacute;n F*EX
+inoltro F*EX
+Přesměrování F*EX
+Redirection F*EX
+
+old (wrong) recipient:
+Alter (falscher) Empf&auml;nger:
+Alder (falscher) Empf&auml;ngr:
+viejo (err&oacute;neo) destinatario:
+antigo (incorrecto) destinatario:
+vecchio (sbagliato) destinatario:
+původní (chybný) příjemce:
+ancien (invalide) destinataire:
+
+new recipient:
+Neuer Empf&auml;nger:
+Neier Empf&auml;ngr:
+nuevo destinatario:
+novo destinatario:
+nuovo destinatario:
+nový příjemce:
+nouveau destinataire:
+
+filename:
+Dateiname:
+Nome von der Datei:
+nombre de fichero:
+nome de fichero:
+nome file:
+název souboru:
+nom de fichier:
+
+no upload data found for $file for $oto
+Keine bereitgestellen Daten gefunden f&uuml;r $file f&uuml;r $oto
+Koine nuffgladene Date gefonde fierr $file fier $oto
+no hay datos subidos para $file para $oto
+non se cargaron datos de $file para $oto
+nessun file di dati da caricare per $file per $oto
+u $oto nebyla pro soubor $file nalezena žádná nahraná data
+pas de données trouvées pour le fichier $file pour $oto
+
+notification of file upload \"$filename\" sent to $nto
+Benachrichtigung vom Datei-Upload \"$filename\" gesendet an $nto
+Benochrichdigong iber die nuffgladene Datei \"$filename\" gschickt an $nto
+notificaci&oacute;n de las subida del fichero \"$filename\" enviado a $nto
+enviada a notificaci&oacute;n da carga do ficheiro \"$filename\" enviado a $nto
+notifica del file caricato \"$filename\" spedita a $nto
+oznámení o nahrání souboru \"$filename\" odesláno na $nto
+un message d'information concernant le fichier uploadé \"$filename\" a été envoyé à $nto
+
+redirect $nto/$from/$fkey failed
+Umadressierung $nto/$from/$fkey fehlgeschlagen
+Bei dr omadressierung $nto/$from/$fkey isch was schiefgloffe
+redirecci&oacute;n a $nto/$from/$fkey  fall&oacute;
+fallou a redirecci&oacute;n a $nto/$from/$fkey
+inoltro $nto/$from/$fkey fallito
+přesměrování $nto/$from/$fkey selhalo
+la redirection vers $nto/$from/$fkey a échouée
+
+Please avoid download with Internet Explorer,
+Falls Sie zum Download Internet Explorer benutzen, koennte es
+Falls Du zom Ronderlada Internet Explorer nemmsch, waer's meglich, dass
+Por favor, evite usar Internet Explorer para las descargas,
+Por favor, evite usar Internet Explorer para as descargas,
+Per favore evita di fare il download con Internet Explorer
+Nestahujte prosím pomocí prohlížeče Internet Explorer,
+Merci d'éviter d'utiliser Internet Explorer,
+
+because it has too many bugs
+unter Umstaenden zu Problemen kommen
+Du a Problem hosch
+porque tiene demasiados errores
+porque ten demasiados defectos
+perche' ha troppi bug
+protože má příliš mnoho chyb
+parce qu'il a beaucoup trop de problèmes
+
+We recommend Firefox or wget
+Wir empfehlen Ihnen Firefox oder wget
+Nemm lieber Firefox oder wget
+Recomendamos Firefox o wget
+Recomendámoslle Firefox ou wget
+Raccomandiamo di usare Firefox o wget
+Doporučujeme použít Firefox či wget
+Nous reconmmandons Firefox ou wget
+
+After download (or view with a web browser!), 
+Nach Download (oder Ansicht mit dem Web Browser!) 
+Nachm Ronderlada (oddr wenn Du se mit Deim Webbrowser oguckt hosch!) 
+Tras la descarga,
+Tras a descarga,
+Dopo il download (o dopo essere stato visto con un browser WEB!)
+Po stažení (či zobrazení v prohlížeči!),
+Après le téléchargement (ou visualisation avec un navigateur web) 
+
+the file will be deleted!
+wird die Datei geloescht!
+wird dui Datei gloescht!
+¡el fichero se borrará!
+o ficheiro borrarase!
+il file sara' cancellato
+bude soubor smazán!
+le fichier a été effacé !
+
+When you download the file it will be deleted 
+Wenn Sie die Datei herunterladen dann wird diese eine kurze Zeit 
+Wenn Du die Datei ronderlaedsch, wird se bald drnoch 
+Cuando descargue el fichero se borrará
+Cando descargue o fichero, este borrarase;
+Quando scarichi il file sara' cancellato
+Jakmile si soubor stáhnete, bude po té 
+Lorsque vous téléchargez un fichier, celui-ci sera effacé 
+
+soon afterwards!
+danach geloescht werden!
+gloescht!
+en breve!
+moi axi&ntilde;a!
+poco dopo!
+ihned smazán!
+dans les instants qui suivent !
+
+is a container file
+ist ein Container
+isch a Schachtel
+es un archivo contenedor
+é un ficheiro contedor
+e' un archivio
+je soubor archivu
+est une archive
+
+You can unpack it for example with 7zip
+Sie koennen es z.B. mit 7zip oeffnen
+Du kosch's z.B. mit 7zip aufmacha
+Se puede descompactarlo con 7zip
+P&oacute;dese descomprimir con 7zip
+Puoi decomprimerlo con 7zip
+Rozbalit jej můžete pomocí programu 7zip
+Vous pourrez l'ouvrir avec 7zip par exemple
+
+This download link only works for you, you cannot distribute it
+Dieser Download Link funktioniert nur fuer Sie, Sie koennen ihn nicht weitergeben
+Sell Ronderlad Link isch fei bloss fir Di, du kosch\'n ned weitergaeba
+Este hipervínculo funciona solamente para tí, no puedes compartirlo
+Esta ligazón de descarga só funciona para vostede, non a difunda
+Questo link di download funziona solo per voi, non è possibile distribuire
+Odkaz je určen pouze vám, nemůžete jej dále šířit
+le lien de téléchargement ne fonctionnera que vous pour vous, vous ne pourrez pas le diffuser
+
+$receiver = 'you
+$receiver = 'Sie
+$receiver = 'Di
+$receiver = 'usted
+$receiver = 'vostede
+$receiver = 'te
+$receiver = 'vy
+$receiver = 'vous
+
+Comment:
+Kommentar:
+Kommendar:
+Comentario:
+Comentario:
+Commento:
+Komentář:
+Commentaire:
+
+" day"
+" Tag"
+" Dag"
+" día"
+" día"
+" giorno"
+" den"
+" jour"
+
+" days"
+" Tagen"
+" Dag"
+" días"
+" días"
+" giorni"
+" dnů"
+" jours"
+
+> days<
+> Tagen<
+> Dag<
+> días<
+> días<
+> giorni<
+> dnů<
+> jours<
+
+has uploaded the file
+hat die Datei
+hod dui Datei
+ha subido el fichero
+cargou o ficheiro
+ha caricato il file
+nahrál soubor
+a uploadé un fichier
+
+for $receiver. Use
+fuer $receiver bereitgestellt. Verwenden Sie die URL
+fier $receiver bereitgschdellt. Nemm
+para $receiver. Use
+para $receiver. Use
+per $receiver. Usa
+pro $receiver. Stáhnout si jej můžete zde
+pour $receiver. Utilisez
+
+to download this file within $days
+um die Datei innerhalb von $days herunterzuladen
+damit Du dui Datei ennerhalb von $days ronderlade kosch
+para descargar este fichero antes de $days
+para descargar este fichero antes de $days
+per scaricare questo file entro $days
+během $days
+pour télécharger ce fichier d'ici $days
+
+F*EX is not an archive, it is a transfer system for personal files
+F*EX ist kein Archiv, sondern ein Dateien-Uebertragungssystem
+F*EX isch koi Archiv sondern a Syschtem zom Dateia iebertraga
+F*EX no es un archivo, es un sistema personal de transferencia de ficheros
+F*EX non é un arquivo, é un sistema persoal de transferencia de ficheiros
+F*EX non e' un sistema di archiviazione, e' un sistema personale di trasferimento di file 
+F*EX není archiv, je to systém pro přenos souborů pro osobní použití
+F*EX n'est pas un système d'archivage, c'est un système de transfert de fichiers personnels
+
+For more information see $index
+Weitere Informationen finden Sie unter $index
+Gugsch au do: $index
+Para más informaciones vir a $index
+Para máis información, vexa $index
+Per ulteriori informazioni, vedere $index
+Pro více informací se podívejte na $index
+Voir $index pour plus d'informations
+
+No notification e-mail has been sent to $to
+Es wurde keine Benachrichtigungs-E-Mail an $to verschickt
+$to isch ned benochritigt worda
+No se ha enviado ning&uacute;n correo electr&oacute;nico de notificaci&oacute;n a $to
+No notification e-mail has been sent to $to
+Nessuna e-mail di notifica è stata inviata a $to
+Žádný e-mail s oznámením nebyl $to odeslán
+Aucun email de notification n'a été envoyé à $to
+
+Ehh... $ndata <b>BYTES</b>?! You are kidding
+Moment... $ndata <b>BYTES</b>?! Das ist wohl ein Witz
+Moment amole $ndata <b>BYTES</b>?! Wilsch me verarsche
+Un momento... &iexcl;&iquest;$ndata <b>BYTES</b>?! Est&aacute; bromeando
+Un intreo... <b>BYTES</b>?! Est&aacute; de broma
+Ehh... $ndata <b>BYTES</b>?! Stai scherzando
+Moment... $ndata <b>BYTŮ</b>?! Děláte si legraci
+Eh... $ndata <b>BYTES</b>?! Vous rigolez
+
+Using F*EX for less than 
+Verwendung von F*EX f&uuml;r weniger als 
+Du nemsch F*EX fir wenigr als 
+Usar F*EX por menos de
+Usar F*EX para menos de 
+Usare F*EX per meno di
+Používat F*EX pro tak malý soubor
+Utiliser F*EX pour moins de 
+
+ever heard of MIME e-mail
+Schon mal was von MIME E-Mail geh&ouml;rt
+Hosch scho mol was von MIME E-Mail gh&ouml;rt
+ha o&iacute;do hablar del correo electr&oacute;nico MIME
+non lle ser&aacute; mellor usar o correo electr&oacute;nico MIME?
+mai sentito parlare delle e-mail MIME
+určitě jste někdy slyšeli o e-mailu
+Vous avez déja entendu parlé de l'e-mail
+
+received and saved
+empfangen und abgespeichert
+empfanga ond abgschpeichert
+recibido y guardado
+recibido e gardado
+ricevuto e salvato
+přijato a uloženo
+reçu et sauvegardé
+
+Download URL for copy & paste
+Download URL zum Kopieren
+Nonderlad URL zom Kopiera
+URL de descarga para copiar y pegar
+Descargar o URL para copiar e pegar
+Scarica l'URL per il copia ed incolla
+URL pro stažení okopírovat a vložit
+URL de téléchargement pour copier/coller
+
+Link is valid for $keep{$to} days
+Dieser Link ist f&uuml;r $keep{$to} Tage g&uuml;ltig
+Sell Link isch fir $keep{$to} Dag giltig
+El enlace es válido durante $keep{$to} días
+A ligazón é válida durante $keep{$to} días
+Il link è valido per $keep{$to} giorni
+Odkaz je platný $keep{$to} dny(ů)
+Le lien restera valide $keep{$to} jours
+
+old <code>$file</code> for $to overwritten
+vorhandenes <code>$file</code> f&uuml;r $to &uuml;berschrieben
+alts <code>$file</code> fir $to iberschrieba
+El anterior <code>$file</code> para $to se ha sobreescrito
+Sobrescribiuse o anterior <code>$file</code> para $to
+vecchio file <code>$file</code> per $to sovrascritto
+původní soubor <code>$file</code> pro $to byl nahrazen
+<code>$file</code> pour $to reécrit.
+
+$to notified
+$to benachrichtigt
+$to bscheid gsagt
+$to ha sido notificado
+$to foi notificado
+$to notificato
+$to byl informován
+$to prévenu
+
+<code>$file</code> removed because you are a restricted user
+<code>$file</code> wurde gel&ouml;scht weil Sie ein eingeschr&auml;nkter Benutzer sind 
+<code>$file</code> isch wieder gl&ouml;scht worda weil du a bschr&auml;nkter Benutzer bisch
+<code>$file</code> se ha borrado porque usted es un usuario restringido
+<code>$file</code> removed because you are a restricted user
+<code>$file</code> rimosso perchè sei un utente limitato
+<code>$file</code> odstraněn, protože jste uživatel s omezením
+<code>$file</code> supprimé car vous êtes un utilisateur restreint
+
+and recipient $to cannot receive e-mail
+und Empf&auml;nger $to keine E-Mail empfangen kann
+ond Empf&auml;ngr $to ko koi E-Mail empfanga
+y el destinatario $to no puede recibir correos electr&oacute;nicos
+and recipient $to cannot receive e-mail
+ed il destinatario $to non può ricevere e-mail
+a příjemce $to nemůže přijímat poštu
+et le destinataire $to ne peut pas recevoir d'email
+
+send another file
+eine weitere Datei schicken
+a weitere Datei schicka
+enviar otro fichero
+enviar outro fichero
+spedire un altro file
+odeslat další soubor
+envoyer un autre fichier
+
+Upload Status for
+Upload Status f&uuml;r
+Nufflad Schtatus fier
+Estado del proceso de subida para
+Estado do proceso de carga de
+Stato del processo di caricamento per
+Stav nahrávání pro
+Status d'upload pour
+
+file successfully transferred
+Datei erfolgreich &uuml;bertragen
+Datei nufflada hot klappt
+fichero transferido con &ecaute;xito
+o ficheiro transferiuse correctamente
+file trasferito con successo
+soubor byl úspěšně přenesen
+fichier transféré avec succès
+
+>close<
+>schlie&szlig;en<
+>fertig<
+>cerrar<
+>cerrar<
+>chiudi<
+>zavřít<
+>fermer<
+
+"upload"
+"upload"
+"nufflada"
+"subir"
+"cargar"
+"caricamento"
+"nahrát"
+"upload"
+
+value="logout"
+value="logout"
+value="adele"
+value="salir"
+value="sa&iacute;r"
+value="uscita"
+value="odhlásit"
+value="déconnexion"
+
+Using Microsoft Internet Explorer for download will probably
+Die Verwendung von Microsoft Internet Explorer f&uuml;r den Download wird wahrscheinlich
+Des Verwenda vom Microsoft Internet Explorer fier dr Download wird wahrscheinlich
+Usar Microsoft Internet Explorer para realizar las descargas probablemente
+Usar Microsoft Internet Explorer para realizar as descargas probablemente
+Usare Microsoft Internet Explorer per il download probabilmente
+Používání aplikace Microsoft Internet Explorer pro stahování pravděpodobně
+Utiliser Microsoft Internet Explorer pour le téléchargement
+
+lead to problems, because it is not Internet compatible (RFC 2616)
+zu Problemen f&uuml;hren, weil er nicht Internet kompatibel (RFC 2616) ist
+zu Problem fiehra, weil der hald ned Internet kompatibel (RFC 2616) isch
+le puede dar problemas, porque no es compatible con el est&aacute;ndar de Internet (RFC 2616)
+traer&aacute; problemas porque non &eacute; compat&iacute;bel co est&aacute;ndar de Internet (RFC 2616)
+genera problemi in quanto non e' compatibile con Internet (RFC 2616)
+povede k problémům, protože není Internetově kompatibilní (RFC 2616)
+entraînera probablement des problèmes, parce qu'il n'est pas compatible avec Internet (RFC 2616)
+
+We recommend
+Wir empfehlen
+Mir empfehlad
+Recomendamos
+Recomend&aacute;moslle
+Si raccomanda
+Doporučujeme
+Nous recommandons
+
+WARNING:
+WARNUNG:
+OBACHT:
+ADVERTIMIENTO:
+ATENCI&Oacute;N:
+ATTENZIONE:
+UPOZORNĚNÍ:
+ATTENTION:
+
+If you really want to continue with Internet Explorer, then
+Wenn Sie wirklich mit Internet Explorer weitermachen wollen, dann
+Wenn wirklich mitm Internet Explorer weiterwurschtla wilsch, no
+Si de verdad quiere continuar con Internet Explorer,
+Si de todas maneiras quere continuar co Internet Explorer,
+Se vuoi davvero continuare con Internet Explorer allora,
+Chcete-li skutečně pokračovat s aplikací Internet Explorer, potom
+Si vous souhaitez vraiment continuer avec Internet Explorer, alors
+
+click here with your right mouse button and select "save as"
+klicken Sie hier mit der rechten Maustaste und w&auml;hlen Sie an "speichern unter"
+klicksch hier mit dr rechta Maustascht ond w&auml;hlsch "speichern unter" aus
+pulse aqu&iacute; con el bot&oacute;n derecho del rat&oacute;n y seleccione "guardar como"
+prema aqu&iacute; co bot&oacute;n dereito do rat&oacute;n e seleccione "gardar como"
+cliccare qui con il tasto destro del mouse e selezionare "salva come"
+zde klikněte pravým tlačítkem myši a vyberte "Uložit jako"
+cliquez ici avec le bouton droit de la souris et sélectionner "save as"
+
+Meta questions
+Meta Fragen
+Meta Froga
+Meta Preguntas
+Meta Preguntas
+Meta Domande
+Všeobecné Máte otázky
+Questions générales
+
+User questions
+Benutzer Fragen
+Benutzr Froga
+Usario Preguntas
+Usuario Preguntas
+Utente Domande
+Uživatel Máte otázky
+Questions utilisateurs
+
+Admin questions
+Administratoren Fragen
+Adminischdrator Froga
+Admin Preguntas
+Admin Preguntas
+Amministratore Domande
+Správce Máte otázky
+Questions administrateur
+
+Misc questions
+Sonstige Fragen
+Vermischte Froga
+Misc Preguntas
+Misc Preguntas
+Misc Domande
+Misc Máte otázky
+Questions diverses
+
+Your F*EX account has been inactive for $expire days
+Ihr F*EX Account ist seit $expire Tagen inaktiv
+Du hosch dei F*EX Konto seit $expire Tag nemme bnutzt
+Su cuenta de F*EX ha estado inactivo $expire dias
+A súa conta F*EX estivo inactiva durante $expire día
+Il tuo account F*EX è stato inattivo per $expire giorni
+Váš F*EX účet byl neaktivní $expire dnů
+Votre compte F*EX a été inactif pendant $expire days
+
+you must download this file to reactivate it
+Sie muessen diese Datei herunterladen um ihn zu reaktivieren
+Du musch sell Datei ronderlada drmit weiter mitmacha derfsch
+hace falta descargar este fichero para reactivarla
+debe descargar este ficheiro para reactivala
+devi scaricare questo file per riattivarlo
+pro jeho opětovnou aktivaci si musíte stáhnout tento soubor
+vous devez télécharger ce fichier pour le réactiver
+
+Otherwise your account will be deleted
+Ansonsten wird Ihr Account geloescht
+Ansoschda bisch den Account los
+De lo contrario, su cuenta será eliminada
+Otherwise your account will be deleted
+Altrimenti il tuo account verrà eliminato
+Jinak bude váš účet smazán
+Sinon votre compte sera supprimé
+
+Account created:
+Account angelegt:
+Konto oglegt:
+Cuenta creada:
+Account created:
+Account creato:
+Účet vytvořen:
+Compte créé:
+
+(De)activate e-mail encryption
+E-Mail Verschl&uuml;sselung (de)aktivieren
+E-Mail Verschl&uuml;sslong (de)aktiviera
+(Des)activar codificación de email
+(De)activate e-mail encryption
+(Dis)attiva la crittografia delle e-mail
+Deaktivovat šifrování pošty
+(Dés)activer le chiffrement des emails
+
+PGP/GPG key deleted
+PGP/GPG key gel&ouml;scht
+PGP/GPG key gl&ouml;scht
+Código PGP/GPG borrado
+PGP/GPG key deleted
+PGP/GPG chiave cancellata
+PGP/GPG klíč byl smazán
+Clé PGP/GPG supprimé
+
+E-mails to you will be sent not encrypted
+E-Mails an Sie werden nun unverschl&uuml;sslt verschickt
+Du kriegsch E-Mails jetzt overschl&uuml;sslt
+Emails para usted son enviados no codificado a partir de ahora
+E-mails to you will be sent not encrypted
+E-mail verso di te saranno spedite non crittografate
+Zprávy vám budou odesílány v nešifrované podobě
+Les emails que vous recevrez ne seront pas chiffrés
+
+E-mails to you will be encrypted with the PGP/GPG key:
+E-Mails an Sie werden nun verschl&uuml;sslt mit dem PGP/GPG key:
+Du kriegsch E-Mails jetzt verschl&uuml;sslt mit dem PGP/GPG key:
+Emails para usted son enviados codificado por código PGP/GPG:
+E-mails to you will be encrypted with the PGP/GPG key:
+E-mail verso di te saranno crittografate con una chiave PGP/GPG:
+Zprávy vám budou odesílány šifrované s PGP/GPG klíčem:
+Les emails que vous recevrez seront chiffrés avec la clé PGP/GPG:
+
+Your uploaded file does not contain a PGP/GPG public key for
+Die hochgeladene Datei enth&auml;lt keinen PGP/GPG public key f&uuml;r
+En der nuffgladana Datei isch koi PGP/GPG public key fir
+El archivo cargado no contiene ningún código PGP/GPG público
+Your uploaded file does not contain a PGP/GPG public key for
+Il tuo file caricato non contiene una chiave pubblica PGP/GPG per
+Nahraný soubor neobsahuje veřejný PGP/GPG klíč pro
+Votre fichier uploadé ne contient pas de clé publique PGP/GPG pour
+
+Select your PGP/GPG public key file
+W&auml;hlen Sie Ihre PGP/GPG public key Datei aus
+W&auml;hl Dei PGP/GPG public key Datei aus
+Elija su archivo con el código público
+Select your PGP/GPG public key file
+Seleziona il tuo file della chiave pubblica PGP/GPG
+Vyberte soubor s PGP/GPG veřejným klíčem
+Sélectionnez le fichier contenant votre clé publique PGP/GPG
+
+delete your already uploaded public key
+l&ouml;schen Sie Ihren abgelegten public key
+l&ouml;sch Dein abglegta public key
+borre su código público guardado
+delete your already uploaded public key
+cancella la tua chiave pubblica già caricata
+smazat předchozí nahraný veřejný klíč
+suprrimer votre clé publique déjà uploadée
+
+To extract and verify your GPG public key use:
+Um Ihren GPG public key zu extrahieren und verifizieren benutzen Sie:
+Om dein GPG public key zom extrahiera ond nochgucka nemsch am beschta:
+Para extraer y verificar su código publico use:
+To extract and verify your GPG public key use:
+Per estrarre e verificare la tua chiave pubblica GPG usa:
+K rozbalení a ověření svého PGP/GPG veřejného klíče použijte:
+Pour extraire et vérifier votre clé publique GPG, utilisez: 
+
+ERROR: no upload received
+FEHLER: es wurde kein Upload empfangen
+FEHLER: do isch fei nix okomma
+ERROR: ningún subir recibido
+ERROR: no upload received
+ERRORE: nessun caricamento ricevuto
+CHYBA: žádný soubor nebyl nahrán
+ERREUR: aucun upload reçu
+
+You cannot send to more than one group
+Sie k&ouml;nnen nicht an mehrere Gruppen senden
+Du kansch net an mehrere Gruppa senda
+No puede enviar a varios grupos
+You cannot send to more than one group
+Non puoi spedire a più di un gruppo
+Nemůžete odesílat více než jedné skupině
+Vous ne pouvez pas envoyer à plus d'un groupe
+
+file transfer aborted
+Dateitransfer abgebrochen
+Dateitransfer abbrocha
+Trasmisión del archivo aborto
+file transfer aborted
+trasferimento file interrotto
+přenos souboru přerušen
+transfert de fichier abandonné
+
+<code>$from</code> is not allowed to upload from IP $ra
+<code>$from</code> darf nicht von IP-Adresse $ra hochladen
+<code>$from</code> darf ned von IP-Adress $ra hochlada
+<code>$from</code> no está permitido a subir de esta dirección IP $ra
+<code>$from</code> is not allowed to upload from IP $ra
+<code>$from</code> non è consentito caricarlo da IP $ra
+<code>$from</code> z IP adresy $ra není dovoleno nahrávat soubory
+<code>$from</code> n'est pas autorisé à uploader vers l'IP $ra
+
+Group <code>$to</code> does not exist
+Gruppe <code>$to</code> existiert nicht
+Grupp <code>$to</code> gibts net
+Grupo <code>$to</code> no existe
+Group <code>$to</code> does not exist
+Il gruppo <code>$to</code> non esiste
+Skupina <code>$to</code> neexistuje
+Le groupe <code>$to</code> n'existe pas
+
+server runs in NOMAIL mode - groups ($to) are not allowed
+Server l&auml;uft im NOMAIL Modus - Gruppen ($to) sind nicht erlaubt
+Server l&auml;uft em NOMAIL Modus - Gruppa ($to) senn net erlaubt
+Servidor está en modo NOMAIL - grupos ($to) no están permitidos
+server runs in NOMAIL mode - groups ($to) are not allowed
+server eseguto in modalità NOMAIL - i gruppi ($to) non sono permessi
+Server běží v režimu NOMAIL - skupiny ($to) nejsou povoleny
+Le serveur tourne en mode NOMAIL - les groupes ($to) ne sont pas autorisés
+
+File not found
+Datei nicht gefunden
+Datei net gfonda
+Archivo no encontrado
+File not found
+File non trovato
+Soubor nenalezen
+Fichier introuvable
+
+<code>$to</code> is not a valid recipient
+<code>$to</code> ist kein g&uuml;ltiger Empf&auml;nger
+<code>$to</code> isch koi g&uuml;ltigr Empf&auml;ngr
+<code>$to</code> no es un destinario válido
+<code>$to</code> is not a valid recipient
+<code>$to</code> non è un destinatario valido
+<code>$to</code> není platný příjemce
+<code>$to</code> n'est pas un destinataire valide
+
+File $file already exists in your outgoing spool
+Datei $file existiert bereits im ausgehenden Spool
+Datei $file gibts fei scho em nausganganda Spool
+Archivo $file ya existe en sú spool saliente
+File $file already exists in your outgoing spool
+Il file $file esiste già nel tuo spool in uscita
+Soubor $file se již ve frontě k odeslání nachází
+Le fichier $file exite déjà dans votre spool sortant
diff --git a/upgrade b/upgrade
new file mode 120000 (symlink)
index 0000000..f7ffc47
--- /dev/null
+++ b/upgrade
@@ -0,0 +1 @@
+install
\ No newline at end of file