From: fextracker Date: Wed, 18 Feb 2015 15:48:45 +0000 (+0100) Subject: Original release 20150120 X-Git-Tag: 20150120 X-Git-Url: https://git.treefish.org/fex.git/commitdiff_plain/20150120 Original release 20150120 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 <from> and <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=<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 < 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 <<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 --> <-- 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 <> instead of <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 < 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 --- 7fa382617fbaccc0ce522b2b3adbbee9db5ad227 diff --git a/README b/README new file mode 100644 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 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 +# +# 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 ((||'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 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 index 0000000..c15ece3 --- /dev/null +++ b/bin/ezz @@ -0,0 +1,56 @@ +#!/bin/sh + +ZZ=${ZZ:-$HOME/.zz} + +usage() { + exec 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 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 +# + +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}<$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 < +# +# 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 = </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 index 0000000..c54b2e0 --- /dev/null +++ b/bin/fex_cleanup @@ -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 +# + +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} = \¬ify; +$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 () { + 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 () { + 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 () { + 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 () { + 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 index 0000000..034ced1 --- /dev/null +++ b/bin/fexget @@ -0,0 +1,1074 @@ +#!/usr/bin/perl -w + +# CLI client for the FEX service for retrieving files +# +# see also: fexsend +# +# Author: Ulli Horlacher +# +# 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 = < '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 = ( +' +(_*) _ _ + \\\\/ \\/ \\ + \ __ )=* + //\\\\//\\\\ +', +' \\\\/\\\\/ +', +' //\\\\//\\\\ +'); + +# 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 = ); + 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\"? "; + $_ = ||'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); + $_ = ||''; + 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); + $_ = ||''; + 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($_ = ||''); + } else { + $prompt =~ s/([\?:=]\s*)/ [$default]$1/ or $prompt .= " [$default]"; + print $prompt; + chomp($_ = ||''); + $_ = $default unless length; + } + } else { + print $prompt; + chomp($_ = ||''); + } + + 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 index 0000000..607d139 --- /dev/null +++ b/bin/fexsend @@ -0,0 +1,3055 @@ +#!/usr/bin/perl -w + +# CLI client for the F*EX service (send, list, delete) +# +# see also: fexget +# +# Author: Ulli Horlacher +# +# 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 = < '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 = ( +' + _ _ c*_) + / \/ \// + *=( __ / + \\\\/\\\\/ +', +' \\\\/\\\\/ +', +' //\\\\//\\\\ +'); + +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) { + ®ister; + 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 = ; + $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 = ; + $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): "; + $_ = ; + 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 = ; + $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 = ; + $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 /
/ or $data;
+      $data = 1;
+      last if m:
:; + if (//) { $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 /
/ or $data;
+      $data = 1;
+      next if m:
:;
+      last if m:
:; + if (/(from .* :)/) { + print "\n$1\n"; + } + if (m{(\d+) (MB.*)
(.+)( ".*")?}) { + 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 = ; + $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 /

\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] "; + $_ = ; + } + 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 = ); + $file =~ s/^\"//; + $file =~ s/\"$//; + last if -e $file; + warn "$file does not exist\n"; + } + print "recipient (e-mail address): "; + chomp($to = ); + die $usage unless $to; + unless ($opt_n) { + print "comment: "; + chomp($opt_C = ); + } + @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 index 0000000..27e3318 --- /dev/null +++ b/bin/fexsrv @@ -0,0 +1,821 @@ +#!/usr/bin/perl -wT + +# fexsrv : web server for F*EX service +# +# Author: Ulli Horlacher +# + +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("
$header
"); + + $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("<") } + 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',); + 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( + "
", + "
fexsrv at $hostname:$port
", + "", + ); + 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", + "", + '', + "", + "$error", + "", + "

$error

", + ); +} + + +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 +# + +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 = ; +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 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 +# +# 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 } 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 = ||''); + } + } + $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 <) { + 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 index 0000000..6deec68 --- /dev/null +++ b/bin/mksgkeys @@ -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 index 0000000..73cf1eb --- /dev/null +++ b/bin/sexget @@ -0,0 +1 @@ +sexsend \ No newline at end of file diff --git a/bin/sexsend b/bin/sexsend new file mode 100755 index 0000000..7340e49 --- /dev/null +++ b/bin/sexsend @@ -0,0 +1,723 @@ +#!/usr/bin/perl -w + +# client for stream exchange of the FEX service +# +# Author: Ulli Horlacher +# +# 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 () { + next if /,/; + if (/^alias $to\s/i) { + chomp; + 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 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 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 index 0000000..48a5a9f --- /dev/null +++ b/bin/zz @@ -0,0 +1,55 @@ +#!/bin/sh + +# to use zz with vim, write to your .vimrc: +# +# noremap zz> :w !zz +# noremap zz< :r !zz + +ZZ=${ZZ:-$HOME/.zz} + +if [ "X$*" = X-h -o "X$*" = X--help ]; then + 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 index 0000000..1470b83 --- /dev/null +++ b/cgi-bin/fac @@ -0,0 +1,902 @@ +#!/usr/bin/perl -w + +# F*EX CGI for administration +# +# Author: Andre Hafner +# + +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:

: (logout):; +print; + +my $nav_user = + li("Create new user") . "\n" . + li("Change user auth-ID") . "\n" . + li("Edit user restrictions file") . "\n" . + li("Delete existing user") . "\n" . + li("Manage disk quota") . "\n"; + +my $nav_log = + li("Get fup.log") . "\n" . + li("Get fop.log") . "\n" . + li("Get error.log") . "\n"; + +if (-f 'fexsrv.log') { + $nav_log = + li("Watch logfile") . "\n" . + li("Get fexsrv.log") . "\n" . + $nav_log; +} + +my $nav_backup = + li("Download backup
(config only)
") . "\n" . + li("Restore backup") . "\n"; + +my $nav_show = + li("List spooled files") . "\n" . + li("Show quotas (sender/recipient)") . "\n" . + li("Show server config") . "\n" . + li("Show user config") . "\n"; + +my $nav_edit = + li("Edit config") . "\n" . + li("Edit index.html") . "\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 "
\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 = "\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 "\n"; + printf "%s?from=%s&ID=%s
\n",$fup,$user,$id; + printf "%s/%s

\n",$fup,b64("from=$user&id=$id"); + print "\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 "\n"; + print "$idf

"; + + 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
\n",$fup,$user,$id; + printf "%s/%s\n",$fup,b64("from=$user&id=$id"); + print "

\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 $user"); + + 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 "
\n"; + dumpfile($file); + # print "
\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<) { + 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:' + '

' + '

$status
' + )); + &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

\n"; + open $file,'<',$ar or http_die("cannot open $ar - $!"); + if ($ar =~ /\.html$/) { + print while <$file>; + } else { + print "

\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 "\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
\n"; + } + } + print "
\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

\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

\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, + "$_". + "$squota". + "$squota_used". + "$rquota". + "$rquota_used"; + } + } + 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( + '

' + ' wrong akey for $admin' + '

' + )); + } + + if ($id and $id ne $rid) { + pq(qq( + '

' + ' wrong auth-ID for $admin' + '

' + )); + } + + pq(qq( + '
' + ' auth-ID for $admin:' + ' ' + '
' + )); + 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 "
\n";
+  while (<$file>) {
+    s/&/&/g;
+    s/\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
index 0000000..e5eea41
--- /dev/null
+++ b/cgi-bin/foc
@@ -0,0 +1,243 @@
+#!/usr/bin/perl -wT
+
+# FEX CGI for (user) operation control
+#
+# Author: Ulli Horlacher 
+#
+
+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(
+    '

for user $user

' + '' + )); + ($quota,$du) = check_sender_quota($user); + if ($quota) { + pq(qq( + + + )); + } + ($quota,$du) = check_recipient_quota($user); + if ($quota) { + pq(qq( + + + )); + } + pq(qq( + '
sender quota (used):$quota ($du) MB +
recipient quota (used):$quota ($du) MB +
' + '


' + '' + 'Retrieve a list of all your received files in F*EX spool.' + )); + pq(qq( + '


' + '

' + ' ' + ' ' + ' ' + ' Change your auth-ID to' + ' ' + ' ' + )); + if (-s "$user/\@ALLOWED_RECIPIENTS") { + # pq(qq( + # ' (You are a restricted user)'; + # '

' + # )); + # '


' + # '' + # 'Show download URLs of files you have sent.' + unless ($nomail) { + pq(qq( + '


' + '' + 'Resend notification e-mails for files you have sent.' + )); + } + } else { + pq(qq( + '


' + '' + 'Forward a copy of a file you already have uploaded to another recipient.' + '


' + '' + 'Redirect files you have uploaded to a wrong or misspelled recipient.' + )); + unless ($nomail) { + pq(qq( + '


' + '' + 'Resend notification e-mails for files you have sent.' + )); + } + pq(qq( + '


' + ' Create a subuser who can send you files. Enter his e-mail address:
' + ' ' + ' for only one upload
' + )); + if ($nomail) { + pq(qq( + ' ' + )); + } else { + pq(qq( + ' Comment to send with information e-mail:
' + '
' + ' ' + )); + } + pq(qq( + '


' + ' Manage your subusers and groups' + '


' + ' Edit your address book' + )); + pq(qq( + '


' + ' ' + ' Change the disclaimer to be sent with notification e-mail.' + )); + } + pq(qq( + '


' + ' ' + ' (De)activate e-mail encryption.' + )) if -s "$ENV{HOME}/.gnupg/pubring.gpg"; + if ((readlink "$user/\@NOTIFICATION"||'') =~ /short/i) { + pq(qq( + '


' + ' Get detailed notification e-mails (current setting: brief).' + )); + } else { + pq(qq( + '


' + ' Get brief notification e-mails (current setting: detailed).' + )); + } + if ((readlink "$user/\@REMINDER"||'') =~ /no/i) { + pq(qq( + '


' + ' Get reminder notification e-mails (current setting: no reminders).' + )); + } else { + pq(qq( + '


' + ' Get no reminder notification e-mails (current setting: send reminders).' + )); + } + if (-e "$user/\@MIME") { + pq(qq( + '


' + ' Save files after download (current setting: display).' + )); + } else { + pq(qq( + '


' + ' Display files when downloading with web browser (current setting: save).' + )); + } + pq(qq( + '


' + ' Back to fup (upload page)' + '

' + )); + print &logout; + print "\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 index 0000000..4370fb6 --- /dev/null +++ b/cgi-bin/fop @@ -0,0 +1,1087 @@ +#!/usr/bin/perl -wT + +# F*EX CGI for download +# +# Author: Ulli Horlacher +# + +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", + "\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), + "

set keep=$keep for $filename

\n", + "\n"; + } else { + http_header('599 internal error'); + print html_header($head), + "

$filename - $!

\n", + "\n"; + } + } else { + http_header('404 File not found'); + print html_header($head), + "

$filename not found

\n", + "\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", + # "\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", + # "\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", + "\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), + "

$filename deleted

\n", + "\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), + "

$filename purged

\n", + "\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("$file 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("$file 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 $file"); + } + } else { + unless (-f $data and -r $data) { + http_die("$file 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 $file"); + } + # 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( + '

Internet Explorer warning

' + 'Using Microsoft Internet Explorer for download will probably' + 'lead to problems, because it is not Internet compatible (RFC 2616).' + '

' + 'We recommend Firefox' + '

' + 'If you really want to continue with Internet Explorer, then' + '' + 'click here with your right mouse button and select "save as"' + '' + '

' + 'See also F*EX user FAQ.' + '' + )); + &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 index 0000000..864a3de --- /dev/null +++ b/cgi-bin/fuc @@ -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 +# + +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' + '

Go back' + '' + )); + 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},"\"
\n" }; + +if ($gm and not $group) { + pq(qq( + '

ERROR: no group name specified

' + '' + )); + exit; +} + +if ($tools) { + pq(qq( + 'To use one of the following F*EX clients you must configure them after' + 'download:' + '

' + '' + ' ' + ' ' + ' ' + ' ' + '
F*EX server:$ENV{PROTO}://$ENV{HTTP_HOST}
Proxy:(your web proxy address, may be empty)
User:$user
Auth-ID:$id
' + )); + 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( + '$otuser is not a valid e-mail address' + '

Go back' + '' + )); + 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 $otuser has been created:' + '

' + '$url' + )); + unless ($nomail) { + ¬ify_otuser($user,$otuser,$url,$comment); + pq(qq( + '

' + 'and an information e-mail has been sent to this address.' + '

' + )); + } + pq(qq( + 'back to F*EX operation control' + '' + )); + exit; +} + +# direct single subuser entry +if ($subuser and not $otuser) { + if (-f "$subuser/@") { + pq(qq( + '$subuser is already a registered F*EX full user' + '

Go back' + '' + )); + exit; + } + if ($subuser !~ /^[^@]+@[\w.-]+[a-z]$/) { + pq(qq( + '$subuser is not a valid e-mail address' + '

Go back' + '' + )); + 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:' + '

' + '$url' + )); + } else { + pq(qq( + '$subuser' + 'is already your subuser and has access URL:' + '

' + '$url' + '

' + "Click on the subuser's e-mail address link to send him an" + "information e-mail by the F*EX server.

" + )); + } + } 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:' + '

' + '$url' + )); + unless ($nomail) { + ¬ify_subuser($user,$subuser,$url,$comment); + pq(qq( + '

' + 'An information e-mail has been sent to $subuser' + )); + } + } + print "\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( + '

Edit address book

' + '' + ' ' + ' ' + '
Entry:aliase-mail address# optional comment
Example:Framstagframstag\@rus.uni-stuttgart.de# Ulli Horlacher
' + '
' + ' ' + '
' + ' ' + '
' + '

' + 'You may use these alias names as F*EX recipient addresses on ' + 'fup' + '

' + 'Alternatively you can fex a file ADDRESS_BOOK to yourself ' + '($user) containing your alias definitions.' + '

' + 'back to F*EX operation control' + '' + )); + } 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,"$alias$address$options$comment\n"; + } else { + push @badalias,$_; + } + } + + if (@badalias) { + print "

ERROR: bad aliases:

\n
    "; + foreach my $ba (@badalias) { print "
  • $ba" } + pq(qq( + '
' + '

' + 'Not in format: alias e-mail-address' + '

' + 'Go back' + '' + )); + exit; + } + + open my $AB,'>',"$user/\@ADDRESS_BOOK" + or http_die("cannot open $user/\@ADDRESS_BOOK - $!\n"); + print {$AB} $ab; + close $AB; + pq(qq( + '

address book

' + '' + '' + '@abt' + '
aliase-mail addressoptionscomment
' + '

' + 'back to F*EX operation control' + '

' + 'back to fup (F*EX upload)' + '' + )); + } + exit; +} + +if ($user and $notification eq 'detailed') { + unlink "$user/\@NOTIFICATION"; + pq(qq( + '

Notification e-mails now come in detailed format.

' + '

' + 'back to F*EX operation control' + '' + )); + &reexec; +} + +if ($user and $mime eq 'yes') { + open $mime,'>',"$user/\@MIME" or http_die("cannot write $user/\@MIME - $!\n"); + close $mime; + pq(qq( + '

Downloads will now be displayed (if possible).

' + '

' + 'back to F*EX operation control' + '' + )); + &reexec; +} + +if ($user and $mime eq 'no') { + unlink "$user/\@MIME"; + pq(qq( + '

Downloads will now be saved.

' + '

' + 'back to F*EX operation control' + '' + )); + &reexec; +} + +if ($user and $notification eq 'short') { + unlink "$user/\@NOTIFICATION"; + symlink "short","$user/\@NOTIFICATION"; + pq(qq( + '

Notification e-mails now come in short format.

' + '

' + 'back to F*EX operation control' + '' + )); + &reexec; +} + +if ($user and $disclaimer) { + my $df = "$user/\@DISCLAIMER"; + if ($disclaimer =~ /^[\s\"]*DEFAULT[\s\"]*$/i) { + unlink $df; + pq(qq( + '

E-mail disclaimer reset to default.

' + '

' + 'back to F*EX operation control' + '' + )); + } elsif ($disclaimer eq 'CHANGE') { + $disclaimer = slurp($df) || ''; + $disclaimer =~ s/&/&/g; + $disclaimer =~ s/' + ' ' + '


' + ' Disclaimer to be sent with download notification e-mail:
' + '
' + ' ' + ' or ' + ' reset the disclaimer to default.' + '' + '' + )); + 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/&/&/g; + $disclaimer =~ s/E-mail disclaimer changed to:' + '

'
+      '$disclaimer'
+      '
' + '

' + 'back to F*EX operation control' + '' + )); + } + + &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/&/&/g; + $pk =~ s/E-mails to you will be encrypted with the PGP/GPG key:' + '

'
+      '$pk'
+      '
' + '

' + 'back to F*EX operation control' + '' + )); + unlink "$gf.pk","$gf~"; + } else { + $pk = `gpg --batch <$gf.pk 2>&1`; + $pk =~ s/&/&/g; + $pk =~ s/Your uploaded file does not contain a PGP/GPG public key for' + ' $user' + '

'
+      '$pk'
+      '
' + '

' + 'back' + '' + )); + } + &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( + '

PGP/GPG key deleted.

' + '

E-mails to you will be sent not encrypted.

' + '

' + 'back to F*EX operation control' + '' + )); + } elsif ($encryption eq 'CHANGE') { + pq(qq( + '

' + ' ' + ' Select your PGP/GPG public key file(*):
' + ' ' + '

' + ' and ' + '

' + )); + if (-f $gf) { + my $g = `gpg < $gf`; + $g =~ s/' + 'delete your already uploaded public key:' + '
'
+        '$g'
+        '
' + '


' + '(*) To extract and verify your GPG public key use:' + '

'
+        'gpg -a --export $user > pubkey.gpg'
+        'gpg < pubkey.gpg'
+        '
' + )); + } + print "\n"; + exit; + } + + &reexec; +} + +if ($user and $reminder eq 'yes') { + unlink "$user/\@REMINDER"; + pq(qq( + '

You will now get reminder notification e-mails.

' + '

' + 'back to F*EX operation control' + '' + )); + &reexec; +} + +if ($user and $reminder eq 'no') { + unlink "$user/\@REMINDER"; + symlink "no","$user/\@REMINDER"; + pq(qq( + '

You will now get no reminder notification e-mails.

' + '

' + 'back to F*EX operation control' + '' + )); + &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( + '

new auth-ID "$nid" for $user saved

' + 'back to F*EX operation control' + '' + )); + exit; +} + +# empty subuser list POST +if (defined(param('ssid')) and $ssid =~ /^\s*$/) { + unlink "$user/\@SUBUSER"; + pq(qq( + '

All subusers deleted

\n
    ' + 'back to F*EX operation control' + '' + )); + 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 "

    ERROR: bad addresses:

    \n
      "; + foreach my $ba (@badaddress) { print "
    • $ba" } + pq(qq( + '
    ' + 'Go back' + '' + )); + exit; + } + + if ($ssid =~ /\S\@\w/) { + open $idf,'>',"$user/\@SUBUSER" or die "$user/\@SUBUSER - $!\n"; + print "Your subusers upload URLs are:

    \n"; + print "\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 " \n"; + } + } + pq(qq( + "
    $subuser :", + "$fup?skey=$skey
    \n

    " + "You have to give these URLs to your subusers for fexing files to you." + "
    " + "Or click on the subuser's e-mail address link to send him an" + "information e-mail by the F*EX server.

    " + )); + } + print "back to F*EX operation control\n"; + print "\n"; + close $idf; + exit; +} + +if (open my $subuser,'<',"$user/\@SUBUSER") { + local $/; + $ssid = <$subuser> || ''; + close $subuser; +} + +# display HTML form and request user data +pq(qq( + '

    ' + ' ' +)); + +# pq(qq( +# ' ' +# ' ' +# ' Your F*EX account: $user:$id

    ' +# ' New auth-ID: ' +# ' (Remember your auth-ID when you change it!)' +# )); + +if (-s "$user/\@ALLOWED_RECIPIENTS") { + pq(qq( + '

    ' + ' ' + '

    ' + '' + )); + exit; +} + +if ($ssid) { + $ssid = html_quote(strip($ssid)); +} + +pq(qq( + '


    ' + ' Allow special senders (= subusers) to fex files to you:
    ' + '
    ' + ' ' + '' + '

    ' + '' + ' ' + ' ' + '
    This list has entries in format:<e-mail address>:<encryption-ID>
    Example:framstag\@rus.uni-stuttgart.de:schwuppdiwupp
    ' + '

    ' + 'These special senders may fex files only to you!
    ' + 'It is not necessary to add regular fex users to your list,' + 'because they already can fex.' + '

    ' + 'The encryption-ID is necessary to generate a unique upload URL for this subuser.
    ' + 'If you omit the encryption-ID a random one will be used.' +)); + +unless ($nomail) { + pq(qq( + '


    ' + '

    ' + 'Edit your F*EX groups:

    ' + '
      ' + )); + + foreach $group (glob "$user/\@GROUP/*") { + if (-f $group and not -l $group and $group !~ /~$/) { + $group =~ s:.*/::; + print "
    • \@$group\n"; + } + } + + pq(qq( + '
    • new group' + '
    ' + )); +} + +pq(qq( + '


    ' + 'back to F*EX operation control' + '' +)); + +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( + '

    ERROR: cannot read $gf - $!

    ' + '' + )); + exit; + } + if (m{([^/]+\@[\w.-]+)/}) { + $user = lc $1; + } else { + pq(qq( + '

    INTERNAL ERROR: groupfile = $gf

    ' + '' + )); + exit; + } + if (open $gf,'<',$gf) { + while (<$gf>) { + if (/\Q$notify\E:(\S+)/i) { + $gid = $1; + last; + } + } + close $gf; + } else { + pq(qq( + '

    ERROR: cannot open $gf - $!

    ' + '' + )); + exit; + } + unless ($gid) { + pq(qq( + '

    ERROR: $notify not found in $gf

    ' + '' + )); + 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( + '

    Notification e-mail to $notify has been sent

    ' + '

    Go back' + '' + )); + 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 "

    ERROR: lines not in format <e-mail address>:<encryption-ID>

    \n
      "; + foreach my $ba (@badformat) { print "
    • $ba" } + print "
    \n"; + } + if (@badaddress) { + print "

    ERROR: bad addresses:

    \n
      "; + foreach my $ba (@badaddress) { print "
    • $ba" } + print "
    \n"; + } + if (@badformat or @badaddress) { + pq(qq( + 'Go back' + '' + )); + exit; + } + $gd = "$user/\@GROUP"; + unless (-d $gd or mkdir $gd,0700) { + print "

    ERROR: cannot create $gd - $!

    \n"; + print "\n"; + exit; + } + if (-l $gf) { + if ($_ = readlink $gf and m{([^/]+\@[\w.-]+)/}) { + $user = $1; + pq(qq( + '

    ERROR: you are already in group \@$group owned by $user

    ' + 'Go back' + 'and enter another group name' + '' + )); + } else { + print "

    INTERNAL ERROR: $gf is a symlink. but not readable

    \n"; + print "\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 "

    ERROR: cannot write $gf - $!

    \n"; + print "\n"; + exit; + } + if (@gm) { + foreach $gm (@gm) { + next if $gm eq $user; + unless (-d $gm or mkdir $gm,0700) { + print "

    ERROR: cannot create $gm - $!

    \n"; + print "\n"; + exit; + } + $gd = "$gm/\@GROUP"; + unless (-d $gd or mkdir $gd,0700) { + print "

    ERROR: cannot create $gd - $!

    \n"; + print "\n"; + exit; + } + $gl = "$gm/\@GROUP/$group"; + unless (-l $gl or symlink "../../$user/\@GROUP/$group",$gl) { + print "

    ERROR: cannot create $gl - $!

    \n"; + print "\n"; + exit; + } + } + pq(qq( + '

    Group \@$group has members:

    ' + '
      ' + )); + foreach $gm (@gm) { + if ($gm ne $user) { + print "
    • $gm\n"; + } + } + pq(qq( + '
    ' + '(click address to send a notification e-mail to this user)' + )); + } else { + print "

    Group \@$group has no members

    \n"; + } + pq(qq( + '

    ' + 'back to F*EX operation control' + )); + print end_html(); + exit; + } else { + # no group members -> delete group file + unlink $gf; + } + } else { + $gm = ''; + pq(qq( + '

    Edit F*EX group

    ' + 'A F*EX group is similar to a mailing list, but for files:
    ' + 'When a member fexes a file to this list, ' + 'then all other members will receive it.' + '

    ' + '

    ' + ' ' + )); + if ($group eq 'NEW') { + pq(qq( + ' ' + ' New group name: (You MUST fill out this field!)' + ' ' + )); + } else { + if (open $gf,'<',$gf) { + local $/; + $gm = <$gf>||''; + } + close $gf; + pq(qq( + ' ' + ' F*EX group \@$group:' + )); + } + my $rows = ($gm =~ tr/\n//) + 5; + pq(qq( + '

    ' + ' ' + '
    ' + '

    ' + '' + ' ' + ' ' + '
    This list must have entries in format:<e-mail address>:<encryption-ID>
    Example:framstag\@rus.uni-stuttgart.de:schwuppdiwupp
    ' + '

    ' + '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( + "


    " + "

    Your address book:

    " + "
    "
    +      ));
    +      while (<$ab>) {
    +        s/#.*//;
    +        print "$1\n" if /([\S]+\@[\S]+)/;
    +      }
    +      close $ab;
    +      print "
    \n"; + } + print "\n"; + exit; + } +} diff --git a/cgi-bin/fup b/cgi-bin/fup new file mode 100755 index 0000000..d43cda0 --- /dev/null +++ b/cgi-bin/fup @@ -0,0 +1,3038 @@ +#!/usr/bin/perl -wT + +# F*EX CGI for upload +# +# Author: Ulli Horlacher +# +# Contribs: +# Sebastian Zaiser (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("$param{FROM} is not a valid e-mail address"); + } + foreach my $to (@to) { + unless (grep /^\Q$to\E$/i,@public_recipients) { + http_die("$to 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 \"$okey\" - ". + "request another one from $to"); + $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 $dkey>") 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 $dkey") 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 $dkey") 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 "

    $filename deleted

    \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 "

    $filename not deleted ($s)

    \n"; + print "continue\n" if $akey; + print "\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 "

    Files from $from, ", + "click on the file name to resend a notification e-mail:

    \n", + "
    \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] %s%s %s\n",
    +                 $size,
    +                 $rkeep,
    +                 untaint("/fup?akey=$akey&dkey=$dkey&command=RENOTIFY"),
    +                 $filename,
    +                 $comment ? qq' "$comment"' : '',
    +                 $file eq $nfile ? 
    +                   " → notification e-mail has been resent" :
    +                   "";
    +        }
    +      }
    +    }
    +    pq(qq(
    +      '
    ' + '

    back to F*EX operation control' + '' + )); + 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])

    \n", + print "

    Files from $from:

    \n", + "
    \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] %s%s\n",
    +                 $size,
    +                 $rkeep,
    +                 untaint("/fup?akey=$akey&dkey=$dkey&command=FORWARD"),
    +                 $filename,
    +                 $comment?qq( "$comment"):'';
    +        }
    +      }
    +      pq(qq(
    +        '
    ' + '

    back to F*EX operation control' + '' + )); + } + # list received files + else { + $to = $from; + http_header('200 OK'); + print html_header($head); +# "(Format: [size] [rest keep time] [URL] [comment])

    \n", + print "

    Files for $to (*):

    \n", + "
    \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 "[delete] ",
    +                   $akey,$dkey;
    +            printf "[forward] ",
    +                   $akey,$dkey;
    +            printf "%8s MB (%s d) %s%s\n",
    +                   $size,$rkeep,$url,$filename,$comment;
    +          }
    +        }
    +      }
    +      pq(qq(
    +        '
    ' + '(*) Files for other e-mail addresses you own will not be listed here!

    ' + 'back to F*EX operation control' + '' + )); + } + 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 "

    Download URLs of files you have sent\n"; + foreach $to (glob "*/$from") { + if (@files = glob "$to/*/data") { + $to =~ s:/.*::; + print "

    to $to :

    \n"; + print "
    \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 "
    \n"; + } + } + pq(qq( + '
' + '

back to F*EX operation control' + '' + )); + 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 $del"); + } + $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 "

$file deleted

\n"; + } else { + http_header("404 Not Found"); + print html_header($head); + print "

$file not deleted

\n"; + } + if ($akey) { + printf "continue\n", + $akey,$to; + } + print "\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("$from 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 $to 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,''); + print html_header($head); + + if ($http_client =~ /(Konqueror|w3m)/) { + pq(qq( + '


' + '

' + '

Your client seems to be "$1" which is incompatible with F*EX and will probably not work!

' + 'We recommend firefox.' + '
' + '


' + )); + } + + # 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 = (""); + + # select menu from server address book + if (open my $ab,'<',"$from/\@ADDRESS_BOOK") { + while (<$ab>) { + s/#.*//g; + if (/(\S+)[=\s]+(\S+@[\w.-]+\S*)/) { + $_ = "$1 <$2>"; + s/,.*/,.../g; + push @ab,""; + } + } + close $ab; + } + + unless (@to) { + unless ($nomail) { + foreach (glob "$from/\@GROUP/*") { + if (-f and not -l) { + s:.*/::; + push @ab,"" unless /~$/; + } + } + } + } + + my $ab64 = b64("from=$from&id=$id"); +# '

' + ' ' + ' ' + ' ' + ' ' + ' ' + '
sender: $from
recipient(s):' + '
' + )); + if (grep /@/,@ab) { + pq(qq( + ' or select from your address book:' + ' ' + ' and' + ' ' + )); + } + pq(qq( + '
' + '

' + )); + 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:

' + '

'
+      ));
+      while (<$rr>) {
+        chomp;
+        s/#.*//;
+        s/\s//g;
+        next unless $_;
+        if (/^\@LOCAL_RDOMAINS/) {
+          foreach my $rd (@local_rdomains) {
+            print "*\@$rd\n";
+          }
+        } else {
+          print "$_\n";
+        }
+      }
+      print "

\n"; + close $rr; + } + pq(qq( + ' ' + ' or ' + '

' + '

' + )); + if ($akey and -f "$from/\@" and not $captive ) { + pq(qq( + 'user config & operation control' + )); + } + + if ($from eq $admin ) { + pq(qq( + '

' + 'server config & admin control' + )); + } + + if (0 and -f "$docdir/FIX.jar") { + print "

\n"; + if ($public) { print "" } + elsif ($skey) { print "" } + elsif ($gkey) { print "" } + else { print "" } + print "Alternate Java client (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 "\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( + '' + )); + pq(qq( + '

' + ' ' + ' ' + ' ' + )); + + if ($public) { + my $toh = join('
',@to); + pq(qq( + ' ' + ' ' + ' ' + ' ' + ' ' + )); + } elsif ($okey) { + pq(qq( + ' ' + ' ' + '
sender: $from
recipient:$toh
' + ' ' + ' ' + )); + } elsif ($skey) { + pq(qq( + ' ' + '
sender: $from
recipient:$to
' + ' ' + ' ' + )); + } elsif (@group) { + if ($gkey) { + pq(qq( + ' ' + )); + } + my $toh = "group $group:
    "; + my $toc = join(',',@group); + foreach my $gm (@group) { $toh .= "
  • $gm" } + $toh .= "
"; + pq(qq( + ' ' + '
sender: $from
recipient:$to
' + ' ' + ' ' + )); + } else { + my $toc = join(',',@to); + my $toh = join('
',@to); + pq(qq( + ' ' + '
sender:$from
recipient(s):' + ' $toh
' + ' ' + )); + if ($anonymous) { + pq(qq( + ' ' + )); + } else { + pq(qq( + ' ' + )); + } + } + + $autodelete = lc $autodelete; + $keep = $keep_default unless $keep; + my ($quota,$du) = check_sender_quota($muser||$from); + $quota = $quota + ? "" + : ''; + + $bwl = qq''; + $ktr = qq'$keep days'; + } else { + $ctr = qq''; + } + if ($captive) { + $ktr = qq'$keep days'; + } + + pq(qq( + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '
sender:$from
recipient:' + ' $toh
recipient(s):' + ' $toh
sender quota (used):$quota ($du) MB
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' $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' $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'' + .qq'no notification e-mail will be send'; + $ktr = qq' days
'; + $ktr = qq'$keep days
autodelete:$adt
keep:' + ' $ktr' + ' $quota' + '
bandwith limit:' + ' $bwl' + '
comment:' + ' $ctr' + '
file:' + ' ' + '
file size:
' + '

+ ' ' + '

' + '

' + )); + if ($akey and -f "$from/\@" and not $captive) { + print "

\n", + "user config & operation control\n"; + } + if ($from eq $admin ) { + pq(qq( + '

' + 'server config & admin control' + )); + } + if (0 and -f "$docdir/FIX.jar" and not $okey) { + print "

\n"; + if ($public) { print "" } + elsif ($skey) { print "" } + elsif ($gkey) { print "" } + else { print "" } + print "Alternate Java client (for files > 2 GB or sending of more than one file)\n"; + } + print &logout; + print $info_2; + # printf "


%s
\n",$ENV{HTTP_HEADER}; + print "\n"; + exit; + } + + present_locales('/fup'); + + if ($ENV{REQUEST_METHOD} eq 'POST') { + pq(qq( + '

' + ' You have to fill out this form completely to continue.' + '

' + )); + } + + pq(qq( + '
' + ' ' + ' ' + ' ' + '
sender:' + '
auth-ID:' + '
' + )); + 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.' +# '

' +# )); + pq(qq( + ' ' + ' I have lost my auth-ID! Send it to me by e-mail! ' + ' (you must fill out sender field above)' + )); + } + pq(qq( + '

' + )); + 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 register yourself ' + 'if you do not have a F*EX account yet.

' + )); + } + if (@anonymous_upload and ipin($ra,@anonymous_upload)) { + my $a = 'anonymous_'.int(rand(999999)); + pq(qq( + 'You may also use anonymous upload' + )); + } + # if (-f "$docdir/sup.html") { + # pq(qq( + # '
' + # 'You may also use simple upload' + # )); + # } + print "

\n"; + + print $info_1; + + if ($debug and $debug>1) { + print "
\n
\n";
+    foreach $v (sort keys %ENV) {
+      print "$v = $ENV{$v}\n";
+    }
+    print "
\n"; + } + + print "\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("$file already exists for $to"); + } + } +} + +# additional last check +foreach $to (@to) { + checkaddress($to) or + http_die("$to 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("$file already exists for $to"); + } + if ($ENV{HTTP_COOKIE} !~ /$dkey/) { + my $purge = "/fop/$dkey/$dkey?purge"; + # http_die("$file already exists $dkey:$ENV{HTTP_COOKIE}:"); + http_die("$file already exists - purge it?!"); + } +} + +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", + "\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( + '$file ($size) received and saved

' + 'Download URL for copy & paste:' + '

$durl/$dkey{$to}/$fkey

' + 'Link is valid for $keep{$to} days!

' + )); +} else { + if ($ndata<2*1024) { + print "$file ($ndata B) received and saved

\n"; + if (not $boring and not $seek) { + print "Ehh... $ndata BYTES?! You are kidding?

\n"; + } + } elsif ($ndata<2*M) { + $ndata = int($ndata/1024); + print "$file ($ndata kB) received and saved

\n"; + if ($ndata<1024 and not ($boring or $seek)) { + print "Using F*EX for less than 1 MB: ", + "ever heard of MIME e-mail? ☺

\n"; + } + } else { + $ndata = int($ndata/M); + print "$file ($ndata MB) received and saved

\n"; + } + print "

    \n"; + foreach $to (@to) { + print "
  • "; + if ($nomail or $nomail{$to}) { + if ($restricted) { + rmrf("$to/$from/$fkey"); + print "$file removed because you are a restricted user ". + "and recipient $to cannot receive e-mail

    \n"; + } else { + pq(qq( + '$to cannot receive e-mail →' + '

    ' + ' No notification e-mail has been sent to $to!' + '

    ' + 'Download URL for copy & paste:' + )); + if ($xkey) { + my $x = "$durl{$to}//$xkey"; + $x =~ s:/fop::; + print "

    $x

    \n"; + } else { + print "

    $durl/$dkey{$to}/$fkey

    \n"; + print "Link is valid for $keep{$to} days!

    \n"; + } + } + } elsif ($overwrite{$to} and not $comment) { + print "(old $file for $to overwritten)

    \n" + } else { + print "$to notified

    \n" + } + } + print "

\n"; +} + +if ($okey) { + unlink "$to/\@OKEY/$okey"; +} elsif (not $anonymous and not $sup) { + print ""; + print "send another file\n"; + if ($http_client !~ /fexsend/ and $http_client =~ /Linux/i) { + print qq'

Hi Linux-user, try fexsend! ☺

\n'; + } + print &logout; +} + +print "\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("$from 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 $to 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("$to is not a valid e-mail address"); + } + } + } + } + @to = values %to; + } + + foreach $to (@to) { + unless (checkforbidden($to)) { + http_die("$to 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 = '

close'."\n". + ''."\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 "\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( + "" + "

" + "

Upload Status for
$file ($ssize)

" + '' + "
" + "" + "
" + "
" + "
" + )); + + # wait for upload file + for (1..9) { + last if -f $upload or -f $data; + sleep 1; + } + unless (-f $upload or -f $data) { + print "

ERROR: no upload received

\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 "

ABORTED

\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( + "" + )) or last; + } + } + + alarm(0); + if ($npercent == 100) { + print "

file successfully transferred

\n"; + } else { + print "

file transfer aborted

\n"; + } + pq(qq( + "" + )); + 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("$filed 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("$flink not allowed for linking"); + } + my @s = stat($flink); + unless (@s and ($s[2] & S_IROTH) and -r $flink) { + http_die("cannot read $flink"); + } + unless (-f $flink and not -l $flink) { + http_die("$flink 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("$file 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/) { + $_ = ; + $_ = ||''; + 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 $file") 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:
' + '$url' + '

' + )); + } + } else { + notify_locale($dkey,'new'); + fuplog($to,urlencode($filename),"(forwarded)"); + if ($filename) { + pq(qq( + 'File "$filename" copy-forwarded to $to and notified.' + '

' + )); + } + } + } + pq(qq( + 'back to F*EX operation control' + '' + )); + } else { + $filename = filename($file); + http_header('200 OK'); + print html_header($head); + pq(qq( + '

' + ' ' + ' ' + ' ' + ' forward a copy of "$filename" to:
' + ' ' + '
' + '' + )); + } +} + + +# 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 $file") 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 ($_ = )) { + 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("$from 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 "

Mail has been sent to you ($from)

\n"; + print "\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 $skey"); + } + } + + 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 $gkey"); + } + } + + 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 "

"; + 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 "$lang "; + } + } + print "

\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 index 0000000..3d91f55 --- /dev/null +++ b/cgi-bin/fur @@ -0,0 +1,416 @@ +#!/usr/bin/perl -wT + +# FEX CGI for user registration +# +# Author: Ulli Horlacher +# + +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( + '

Your registration was successful. Your new F*EX account is:

' + '

' + '$url' + '

' + '(bookmark this URL!)' + '

' + 'or you can use:' + '

' + ' + ' ' + ' ' + ' ' + '
URL:$ENV{PROTO}://$ENV{HTTP_HOST}/fup/
Sender:$user
auth-ID:$id
+ '' + )); + furlog("confirm: account $user created"); + exit; +} + + +unless ($user or $exuser or $demouser) { + http_header("200 OK"); + print html_header($head); + pq(qq( + '

' + )); + + 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):
' + ' ' + '

' + ' allowed domains are:' + '

'
+      ));
+      foreach my $ld (@local_domains) {
+        print "  $ld\n";
+      }
+      print "
\n"; + } else { + if ($mdomain and not grep /^\Q$mdomain\E$/i,@local_domains) { + unshift @local_domains,$mdomain; + } + my @mydomains = map { "\t\n" } @local_domains; + pq(qq( + ' new user (may send to internal or external users):
' + ' \@' + )); + } + } + + if (@local_rdomains and @local_rhosts and + (not @registration_hosts or ipin($ra,@registration_hosts))) { + print "

or

\n" if $reg; + $reg = $ra; + pq(qq( + ' new external user (may send only to internal users):
' + ' ' + '

' + )); + } + + if (@demo) { + print "

or

\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 ($_):
' + ' ' + '

' + )); + } + + if ($reg) { + pq(qq( + '

' + ' you must enter your e-mail address and ' + '

' + '

' + )); + if (@local_rdomains) { + pq(qq( + '


' + 'internal domains are:' + '

'
+      ));
+      foreach my $lrd (@local_rdomains) {
+        print "  $lrd\n";
+      }
+    }
+    pq(qq(
+      '
' + '


' + 'User types overview' + '' + )); + } 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 $exd is not allowed for registration.", + "Contact $ENV{SERVER_ADMIN} for details." + ); + } + } else { + html_error($error,"$exuser 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,"$user is not allowed"); +} +unless (checkaddress($user)) { + html_error($error,"$user is not a valid e-mail address"); +} +$user = untaint($user); + +if (-f "$user/@") { + html_error( + $error, + "you are already registered". + " (I have lost my auth-ID)" + ); +} + +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:' + '

'
+    '$ENV{PROTO}://$ENV{HTTP_HOST}/fup?$uid'
+    '$ENV{PROTO}://$ENV{HTTP_HOST}/fup/$b64'
+    '
' + '' + )); + 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 $user\n"; +print "\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 index 0000000..4ddac70 --- /dev/null +++ b/cgi-bin/pup @@ -0,0 +1,198 @@ +#!/usr/bin/perl -wT + +# F*EX CGI for public upload +# +# Author: Ulli Horlacher +# + +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,"$lang"; + } +} +print "

@locales

\n" if @locales > 1; + + +pq(qq( + '
' + ' ' + ' ' + ' ' +)); + +if ($from) { + pq(qq( + ' ' + ' ' + )); +} else { + pq(qq( + ' ' + )); +} + +if ($to) { + pq(qq( + ' ' + ' ' + )); +} else { + if ($multi) { + foreach my $pr (@public_recipients) { + push @pr,qq() + ."$pr
"; + } + pq(qq( + ' ' + )); + } else { + foreach my $pr (@public_recipients) { + push @pr,""; + } + pq(qq( + ' ' + )); + } +} + +pq(qq( + '
your e-mail address:$from
your e-mail address:
recipient:$to
recipient:@pr
recipient:
' + '

' + ' ' + '

' +)); + + +# 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 ($_ = )) { + debuglog($_); + $len = length; + s/\r?\n//; + } + return $len; +} diff --git a/cgi-bin/rup b/cgi-bin/rup new file mode 100755 index 0000000..10a78b6 --- /dev/null +++ b/cgi-bin/rup @@ -0,0 +1,250 @@ +#!/usr/bin/perl -wT + +# FEX CGI for redirect uploaded files +# +# Author: Ulli Horlacher +# + +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 = ); + 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( + '
' + ' ' + )); + if ($akey) { + print "\n"; + } else { + pq(qq( + ' ' + ' ' + )); + } + if ($oto) { + pq(qq( + ' ' + ' ' + )); + } else { + pq(qq( + ' ' + )); + } + if ($from and $oto) { + pq(qq( + ' ' + ' \n"; + } + pq(qq( + '
sender:' + '
auth-ID:' + '
old (wrong) recipient:$oto
old (wrong) recipient:' + '
new recipient:' + '
filename:
' + '

' + ' ' + '

' +

+ 'back to F*EX operation control' + '' + )); + 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( + '

F*EX redirect

' + 'notification of file upload \"$filename\" sent to $nto' + '

' + 'back to F*EX operation control' + '' + )); +} 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 index 0000000..d483ebe --- /dev/null +++ b/cgi-bin/sex @@ -0,0 +1,236 @@ +#!/usr/bin/perl -wT + +# CGI for stream exchange +# +# Author: Ulli Horlacher + +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 index 0000000..6074da1 --- /dev/null +++ b/doc/Changes @@ -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 and 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= +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 <> 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 +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 index 0000000..9dc8d78 --- /dev/null +++ b/doc/Contribs @@ -0,0 +1,62 @@ +Beate Herrmann : + - artwork + +Sebastian Zaiser : + - upload status window + +Bernd Strehhuber : + - dynamic protocol detection + +Kurt Jaeger : + - ipv6 support + +Andre Hafner : + - web admin interface fac + - VM + +Martin Lauer : + - FAQ Javascript Code + +Dominik Greibl : + - java client F*IX + +Sebastian Roth : + - many bugfixes and extensions (zip, proxy-support, etc) for F*IX + +Daniel Dieckmann + - fexget proxy support + +Kilian Krause + - Debian package maintainer + - SSL security enhancements + - annoying, but useful requests ;-) + +Hanno Hirsch : + - bug hunting + +Nicola Fioravanti (http://revshell.com): + - Penetration tests and security debugging + +Waldemar Bronsch : + - German localization + +Tobias Gunkel : + - German localization + +Hans-Georg Bickel : + - Swabian localization + +Francisco Ruiz : + - Spanish localization + +Anton Meixome : + - Galician localization + +Vanni Piagno : + - Italian localization + +Michal Simunek : + - Czech localization + +Jean-Baptiste Denis : + - French localization diff --git a/doc/IPv6 b/doc/IPv6 new file mode 100644 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 index 0000000..4108934 --- /dev/null +++ b/doc/Licence @@ -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 + +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 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 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 <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 </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 index 0000000..ead5387 --- /dev/null +++ b/doc/concept @@ -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 <> (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 index 0000000..d81dfe2 --- /dev/null +++ b/doc/debugging @@ -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 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 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 index 0000000..0e63e12 --- /dev/null +++ b/doc/installation @@ -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 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 index 0000000..d28f95a --- /dev/null +++ b/doc/newfeatures @@ -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 index 0000000..05c740d --- /dev/null +++ b/doc/reverse_proxy @@ -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/ + + 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/ + + +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 index 0000000..733bf4c --- /dev/null +++ b/doc/version @@ -0,0 +1 @@ +fex-20150120 diff --git a/doc/xx b/doc/xx new file mode 100644 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 index 0000000..328d9c8 --- /dev/null +++ b/etc/mime.types @@ -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 index 0000000..c25a3db --- /dev/null +++ b/etc/xinetd_fex @@ -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 index 0000000..64233a9 --- /dev/null +++ b/htdocs/FAQ/FAQ.html @@ -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 index 0000000..35b8b3c --- /dev/null +++ b/htdocs/FAQ/admin.faq @@ -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 fex and create some users with fac, example: +

+     /home/fex/bin/fac -u memyselfandi@my.do.main secret-auth-id
+   
+ Then log in using the web interface: http://YOURFEXSERVER/ + + ... and join the F*EX mailing list! ☺ + 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 + +Q: Can I make an alias address for an user? +A: F*EX users are defined by a directory in the spool. Therefore execute: + + + cd /home/fex/spool + ln -s user@do.main alias@other.address + + + 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): +
+     @local_hosts = qw(127.0.0.1 10.10.100.0-10.10.255.255);
+     @local_domains = qw(flupp.org ulm.sub.net);
+   
+ Or you can allow anonymous upload for your LAN users with fex.ph variable @anonymous_upload + Example: +
+     @anonymous_upload = qw(10.10.100.0-10.10.200.255 129.69.1.11);
+   
+ (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): +
+     # 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);
+   
+ + Or you can manually create a restricted external user with (example): +
+     /home/fex/bin/fac -u framstag@rus.uni-stuttgart.de hoppla
+     /home/fex/bin/fac -R framstag@rus.uni-stuttgart.de
+   
+ +Q: How can I change user settings like quota, restrictions or keep time? +A: Use /home/fex/bin/fac + +Q: How can I delete or temporarly disable a user? +A: Use /home/fex/bin/fac + +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: + + + @file_link_dirs = qw(/directory/with/big/files); + +

+ and use: +

+ + fexsend -/ /directory/with/big/files/BIG.file recipient@wherever + + +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 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 + +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 + +Q: How can I get fup as start page? +A: Execute: + + + cd /home/fex/cgi-bin + ln -s fup login + + +Q: How can I use my own local FAQ? +A: Put your FAQ into file /home/fex/htdocs/FAQ/local.faq and execute: + + + cd /home/fex/htdocs/FAQ + ln -sf local.html index.html + diff --git a/htdocs/FAQ/admin.html b/htdocs/FAQ/admin.html new file mode 100644 index 0000000..a096645 --- /dev/null +++ b/htdocs/FAQ/admin.html @@ -0,0 +1,12 @@ + +F*EX FAQ + + +##

+## << while (($v,$vv) = each %ENV) { print "$v = $vv\n" } >>
+## 
+ +<< require "./faq.pl" or print $! >> + + + diff --git a/htdocs/FAQ/all.html b/htdocs/FAQ/all.html new file mode 100644 index 0000000..a096645 --- /dev/null +++ b/htdocs/FAQ/all.html @@ -0,0 +1,12 @@ + +F*EX FAQ + + +##
+## << while (($v,$vv) = each %ENV) { print "$v = $vv\n" } >>
+## 
+ +<< require "./faq.pl" or print $! >> + + + diff --git a/htdocs/FAQ/faq.pl b/htdocs/FAQ/faq.pl new file mode 100644 index 0000000..6cadd49 --- /dev/null +++ b/htdocs/FAQ/faq.pl @@ -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 "\n"; + +print '

F*EX '; +printf "Frequently Asked Questions: %s

\n",ucfirst($faq); + +if ($faq ne 'local') { + print "

Sections: "; + foreach $s (@sections,'All') { + if ($s =~ /$faq/i) { + print "$s\n"; + } else { + printf "[%s]\n",lc($s),$s; + } + } + print "

\n"; +} + +print "


\n"; +print "\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\$/$env<\/code>/g; + s/\$$var\$/$env/g; + }; + ($q,$a) = split /A:\s*/; + $q =~ s/[\s\n]+$//; + $q =~ s/^\s+//; + $q =~ s! (/\w[\S]+/[\S]+)! $1!g; + $a =~ s/[\s\n]+$/\n/; + $a =~ s/^\s+//; + while ($a =~ s/^(\s*)\*/$1
    \n$1
  • /m) { + while ($a =~ s/(
  • .*\n\s*)\*/$1
  • /g) {} + $a =~ s:(.*\n)(\s*)(
  • [^\n]+\n):$1$2$3$2
\n:s + } + $a =~ s/\n\n/\n

\n/g; + $a =~ s/([^>\n\\])\n/$1
\n/g; + $a =~ s/

(.+?)<\/pre>/pre($1)/ges;
+    $a =~ s/\\\n/\n/g;
+#    $a =~ s/^\s*
\s*//mg; + $a =~ s/<([^\s<>\@]+\@[\w.-]+)>/<$1><\/a>/g; + $a =~ s! (/\w[\S]+/[\S]+)! $1!g; + $a =~ s!(https?://[\w-]+\.[^\s<>()]+)![$1]!g or + $a =~ s!(https?://[^\s<>()]+)!$1!g; + push @{$Q{$faq}},$q; + push @{$A{$faq}},$a; + } + close $faq; +} + +print "
\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 ''."\n", + $t,$n+1,$s,$n+1,$qa,$q; + } +} + +print "
'. + ''. + '%s Q%d:'. + '%s
\n"; +print "


\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 "

\n"; + print "\n"; + printf "\n", + $t,$n+1,$s,$n+1,$qa,$q; + printf "\n", + $s,$n+1,${A{$c}[$n]}; + print "
". + "%s Q%d:". + "". + "%s
%s A%d:\n%s
\n"; + print "[↑ Questions]\n"; + } +} + +print "

\n";
+print "\n" x 99;
+print "
\n"; + + +sub sections { + my $faq = shift; + if ($faq eq 'all') { + return @sections; + } else { + return ucfirst($faq); + } +} + +sub pre { + local $_ = shift; + s/
//g; + s/\s+$//; + return "
$_
\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 index 0000000..1dadbb5 --- /dev/null +++ b/htdocs/FAQ/index.html @@ -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 index 0000000..dbb1324 --- /dev/null +++ b/htdocs/FAQ/local.faq @@ -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: + + + cd /home/fex/htdocs/FAQ + ln -sf local.html index.html + diff --git a/htdocs/FAQ/local.html b/htdocs/FAQ/local.html new file mode 100644 index 0000000..a096645 --- /dev/null +++ b/htdocs/FAQ/local.html @@ -0,0 +1,12 @@ + +F*EX FAQ + + +##
+## << while (($v,$vv) = each %ENV) { print "$v = $vv\n" } >>
+## 
+ +<< require "./faq.pl" or print $! >> + + + diff --git a/htdocs/FAQ/meta.faq b/htdocs/FAQ/meta.faq new file mode 100644 index 0000000..2bda920 --- /dev/null +++ b/htdocs/FAQ/meta.faq @@ -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
+ 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 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 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ü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 + +Q: I have more/other questions than in this document! +A: Ask the author diff --git a/htdocs/FAQ/meta.html b/htdocs/FAQ/meta.html new file mode 100644 index 0000000..a096645 --- /dev/null +++ b/htdocs/FAQ/meta.html @@ -0,0 +1,12 @@ + +F*EX FAQ + + +##
+## << while (($v,$vv) = each %ENV) { print "$v = $vv\n" } >>
+## 
+ +<< require "./faq.pl" or print $! >> + + + diff --git a/htdocs/FAQ/misc.faq b/htdocs/FAQ/misc.faq new file mode 100644 index 0000000..ddd5251 --- /dev/null +++ b/htdocs/FAQ/misc.faq @@ -0,0 +1,17 @@ +Q: F*EX is great! Can I join the developing team? What needs to be done? +A: Contact the author + + Requested features are: +
    +
  • testers for MacOS, AIX and other UNIXes +
  • a new maintainer for the Java client F*IX +
  • an Android or iOS client +
  • 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 +
  • more (other) languange support (japanese, bavarian, klingon ...) +
+ +Q: Can I donate something for F*EX? +A: If you have a pet animal, fex a cute video of it to the author diff --git a/htdocs/FAQ/misc.html b/htdocs/FAQ/misc.html new file mode 100644 index 0000000..a096645 --- /dev/null +++ b/htdocs/FAQ/misc.html @@ -0,0 +1,12 @@ + +F*EX FAQ + + +##
+## << while (($v,$vv) = each %ENV) { print "$v = $vv\n" } >>
+## 
+ +<< require "./faq.pl" or print $! >> + + + diff --git a/htdocs/FAQ/user.faq b/htdocs/FAQ/user.faq new file mode 100644 index 0000000..5c62df9 --- /dev/null +++ b/htdocs/FAQ/user.faq @@ -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 SSL3_GET_SERVER_CERTIFICATE:certificate verify failed? +A: Set the environment variable SSLVERIFY=0 + + Rationale: + Your openssl library cannot resolve the SSL certification path. + With SSLVERIFY=0 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 index 0000000..a096645 --- /dev/null +++ b/htdocs/FAQ/user.html @@ -0,0 +1,12 @@ + +F*EX FAQ + + +##
+## << while (($v,$vv) = each %ENV) { print "$v = $vv\n" } >>
+## 
+ +<< require "./faq.pl" or print $! >> + + + diff --git a/htdocs/Licence b/htdocs/Licence new file mode 100644 index 0000000..4108934 --- /dev/null +++ b/htdocs/Licence @@ -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 + +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 index 0000000..81b83ad --- /dev/null +++ b/htdocs/SEX.html @@ -0,0 +1,64 @@ + +Stream EXchange + +
+

Stream EXchange (SEX)

+ +You can imagine SEX as network wide UNIX pipes with a relay between or as +network based FIFOs.
+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.
+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 an +appropriate program to the pipe, for example: openssl bf + +

public SEX

+The recipient of regular SEX must be a registered F*EX user.
+But you can also offer streams to non-registered user.
+This is called "public SEX", example: +

+

+  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=
+
+

+sexsend then displays two URLs onder which one can get the stream. + +

SEX - all inclusive

+To transfer files by streaming you can use xx-rated SEX: +

+

+  root@vms2:/data/VM# sexxx flupp
+  streaming:
+  flupp/
+  flupp/vmware.log
+  flupp/flupp.nvram
+  flupp/flupp.vmxf
+  flupp/flupp.vmx
+  flupp/flupp.vmdk
+  4976 MB 11640 kB/s
+
+ + + diff --git a/htdocs/action-fex-camel.gif b/htdocs/action-fex-camel.gif new file mode 100644 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 index 0000000..034ced1 --- /dev/null +++ b/htdocs/download/fexget @@ -0,0 +1,1074 @@ +#!/usr/bin/perl -w + +# CLI client for the FEX service for retrieving files +# +# see also: fexsend +# +# Author: Ulli Horlacher +# +# 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 = < '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 = ( +' +(_*) _ _ + \\\\/ \\/ \\ + \ __ )=* + //\\\\//\\\\ +', +' \\\\/\\\\/ +', +' //\\\\//\\\\ +'); + +# 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 = ); + 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\"? "; + $_ = ||'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); + $_ = ||''; + 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); + $_ = ||''; + 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($_ = ||''); + } else { + $prompt =~ s/([\?:=]\s*)/ [$default]$1/ or $prompt .= " [$default]"; + print $prompt; + chomp($_ = ||''); + $_ = $default unless length; + } + } else { + print $prompt; + chomp($_ = ||''); + } + + 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 index 0000000..607d139 --- /dev/null +++ b/htdocs/download/fexsend @@ -0,0 +1,3055 @@ +#!/usr/bin/perl -w + +# CLI client for the F*EX service (send, list, delete) +# +# see also: fexget +# +# Author: Ulli Horlacher +# +# 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 = < '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 = ( +' + _ _ c*_) + / \/ \// + *=( __ / + \\\\/\\\\/ +', +' \\\\/\\\\/ +', +' //\\\\//\\\\ +'); + +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) { + ®ister; + 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 = ; + $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 = ; + $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): "; + $_ = ; + 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 = ; + $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 = ; + $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 /
/ or $data;
+      $data = 1;
+      last if m:
:; + if (//) { $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 /
/ or $data;
+      $data = 1;
+      next if m:
:;
+      last if m:
:; + if (/(from .* :)/) { + print "\n$1\n"; + } + if (m{(\d+) (MB.*)
(.+)( ".*")?}) { + 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 = ; + $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 /

\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] "; + $_ = ; + } + 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 = ); + $file =~ s/^\"//; + $file =~ s/\"$//; + last if -e $file; + warn "$file does not exist\n"; + } + print "recipient (e-mail address): "; + chomp($to = ); + die $usage unless $to; + unless ($opt_n) { + print "comment: "; + chomp($opt_C = ); + } + @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 index 0000000..8350da9 --- /dev/null +++ b/htdocs/download/sex.stream @@ -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 index 0000000..73cf1eb --- /dev/null +++ b/htdocs/download/sexget @@ -0,0 +1 @@ +sexsend \ No newline at end of file diff --git a/htdocs/download/sexsend b/htdocs/download/sexsend new file mode 100755 index 0000000..7340e49 --- /dev/null +++ b/htdocs/download/sexsend @@ -0,0 +1,723 @@ +#!/usr/bin/perl -w + +# client for stream exchange of the FEX service +# +# Author: Ulli Horlacher +# +# 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 () { + next if /,/; + if (/^alias $to\s/i) { + chomp; + 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 index 0000000..ba9391a --- /dev/null +++ b/htdocs/dynamic.html @@ -0,0 +1,35 @@ + +
    +
  • <> +
  • <<$y+1>> +
  • <> +
+
+
+<<`date +"%Y-%m-%d %H:%M:%S"`>>
+
+
+

+ +#if $y>2 +

xxx

+#elseif 0 +

000

+#elseif 4>3 +

###############

+#else +

zzz

+#endif +


+

+$PATH=$PATH$
+URL parameter=$QUERY_STRING$
+
+


+ +

+<<`env`>>
+
+


+see >">sourcecode + diff --git a/htdocs/favicon.ico b/htdocs/favicon.ico new file mode 100644 index 0000000..e69de29 diff --git a/htdocs/features.html b/htdocs/features.html new file mode 100644 index 0000000..a68df17 --- /dev/null +++ b/htdocs/features.html @@ -0,0 +1,69 @@ + +F*EX feature list + +

F*EX feature list

+
    +
  • file transfer of virtually unlimited file size +
  • recipient and sender only need an e-mail program and a web browser - + of any kind, they do not have to install any software
    + (the F*EX server itself is UNIX based) +
  • 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 sub-users, who can send only to this full-user +
  • full-users can create groups, an analogy to mailing lists, but for files +
  • admin can allow (internal or external) user self-registration +
  • admin can allow upload to public recipients without authentification +
  • admin can allow upload for LAN users without registration + (anonymous upload) +
  • user can forward a file to a second recipient without re-uploading it +
  • user can forward a received file without download +
  • adminstration by CLI or Web +
  • server based user address books +
  • user and admin can throttle bandwith usage +
  • admin can restrict access based on e-mail or ip addresses +
  • sending to multiple recipients needs storage on the server only once +
  • quotas for sending and receiving +
  • support for named based virtual hosts + (multiple server instances with different hostnames on same IP) +
  • support for streams, including streaming recursive file transfer +
  • support for file linking (upload just a link, not the file itself) +
  • integrated web server with special features: +
      +
    • HTML with inline perl code +
    • HTML with conditional if..then..elseif..end blocks +
    • HTML with include statement +
    • on-the-fly zip, tar and tgz streaming output +
    • (restricted) directory file viewer +
    +
  • for real UNIX users, there are the shell programs + fexsend and + fexget
    + to avoid annoying web browser usage and with a lot of additional + features,
    + also there is an Internet clipboard + and anonymous usage +
  • protocol and source-code free available +
  • localization for + many languanges available +
  • optional authentification by LDAP, RADIUS, POP, IMAP, mailman +
  • server available for UNIX and Windows hosts +
  • about 10 times faster than apache +
  • very low memory usage +
  • (reverse) proxy support +
  • F*EX is a HTTP web-service and needs no firewall-tunnels +
  • works with NAT or DHCP clients, too +
  • postfix filter available to send + e-mails with attachments of any size +
  • maintenance-free: no admin interaction necessary +
  • minimal software & hardware requirements for the server +
  • no external database necessary, but possible (LDAP) +
  • full IPv6 support +
  • easy server installation, no installation necessary on client side +
  • great FAQ, use cases + and detailed internal documentation +
+ + diff --git a/htdocs/fup_template.html b/htdocs/fup_template.html new file mode 100644 index 0000000..7d9fd30 --- /dev/null +++ b/htdocs/fup_template.html @@ -0,0 +1,52 @@ + + + + F*EX upload template + + +

F*EX upload template

+


+ +

+ + + + + + + +
sender: (e-mail address)
auth-ID:
recipient(s):(e-mail address)
comment: (optional)
file name:
+

+ +

+

+


+After "submit" you will see an upload progress bar +(if you have javascript enabled and popups allowed). +

+NOTE: Most web browsers cannot upload files > 2 GB!
+If your file is greater you have to use a special F*EX client.
+You also need one for resuming interrupted uploads. Your web browser cannot do this. +


+

Contact: fexmaster
+ + diff --git a/htdocs/index.html b/htdocs/index.html new file mode 100644 index 0000000..548e82a --- /dev/null +++ b/htdocs/index.html @@ -0,0 +1,140 @@ + +F*EX - File EXchange + +
+

F*EX - Frams' Fast File EXchange

+ +
+

+F*EX (Frams' Fast File EXchange) is a service to send big (large, huge, +giant, ...) files from a user A to a user B. +

+The sender uploads the file to the F*EX server using a +WWW upload form and the recipient +automatically gets a notification e-mail with a download-URL. +

+ +You say: +

+ Why do I need another service for file transfer?!
+ I have e-mail, ftp, ssh and even sendfile! +
+

+I say: +

+ You still need F*EX :-) +
+

+ +For example, you want to send your friend your last holiday video (1 GB). +You have the following possibilities (and problems):

+ +

    +
  • sending a DVD by postal service

    + Out of the question - we live in the year + <> + after invention of the internet! + Sending media (hardware) is for grandpas. +

    +

  • using e-mail

    + 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. +

    +

  • uucp

    + Why not cuneiform writing on stone plates? +

    +

  • ssh

    + You have your friends password or you are willing to give him yours + - just for a file transfer? +

    +

  • ftp

    +

      +
    • Using which ftp server? +
    • Using which account, which password? +
    • You do not mind sending unencrypted passwords and files over the insecure internet? +
    • Your proxy supports passive-ftp? +
    • In case of anonymous ftp: +
        +
      • Does it allow 1 GB uploads? +
      • Does it allow to delete the upload afterwards? +
      • Who else can download your file? +
      • Who else can delete your file? +
      • You have to send your friend the download-URL, he has to + inform you about the successful download, you have to delete + it afterwards.
        + All in all: a pain in the ass. +
      +
    +

    +

  • http

    +

      +
    • No default upload and user management - must be programmed +
    • No auto-notification +
    • No auto-deletion +
    • Very few http servers can handle files greater than 2 GB +
    +

    +

  • + sendfile

    +

      +
    • You run UNIX and have sendfile installed? +
    • Your recipient runs UNIX and has sendfile installed? +
    • Neither you nor your recipient has a firewall blocking port 487? +
    +

    +

  • commercial services like DropLoad, ALLPeers, YouSendIt, etc

    +

      +
    • What limit do they have (most: < 2 GB)? +
    • What about security and privacy: will your files be save and secure? +
    • Are they open source based or closed proprietary? +
    • Are they accessible with any browser or do you need java, active-X, flash or other evil plugins? +
    • Will they exist longer than a few months?
      + (DropLoad, ALLPeers and drop.io already have terminated their business) +
    +

    +

+

+If you have answered only once "no" to the questions above, then you need +F*EX. +

+

Main features of F*EX

+

+

    +
  • file transfer of virtually unlimited file size +
  • recipient and sender only need an e-mail 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: <<$keep>> days) +
  • full-users can create one time upload URLs for foreign users +
  • full-users can create sub-users, who can send only to this full-user +
  • full-users can create groups, an analogy to mailing lists, but for files +
  • maintenance-free: no admin interaction necessary besides creating new F*EX accounts +
  • Sending to multiple recipients needs storage on the server only once +
  • F*EX is a HTTP web-service and needs no firewall-tunnels +
  • support for streams, too +
  • for real UNIX users, there are the shell programs fexsend and fexget to + avoid annoying web browser usage +
  • protocol and source-code free available +
+ +

Let's talk about SEX

+F*EX has a companion: Stream EXchange (SEX).

+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. +

+Authentication is the same as with F*EX. + +

Still questions?

+See the FAQ +and the use cases. + +

+

contact: fexmaster
+ + + diff --git a/htdocs/logo.jpg b/htdocs/logo.jpg new file mode 100644 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 index 0000000..1f53798 --- /dev/null +++ b/htdocs/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: / diff --git a/htdocs/small_logo.jpg b/htdocs/small_logo.jpg new file mode 100644 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 index 0000000..23cffb1 --- /dev/null +++ b/htdocs/sup.html @@ -0,0 +1,61 @@ + + + + F*EX simple upload + + +

F*EX simple upload

+


+ +

+ + + + + + +
your e-mail address: + +
your auth-ID: + +
your file: + +
+

+ +

+

+


+After "submit" you will see an upload progress bar +(if you have javascript enabled and popups allowed). +

+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. +

+You can also use the regular upload form +(with more features). +

+NOTE: Only Firefox or Google Chrome can upload files > 2 GB!
+


+

Contact: fexmaster
+ + diff --git a/htdocs/tools.html b/htdocs/tools.html new file mode 100644 index 0000000..fd29c2c --- /dev/null +++ b/htdocs/tools.html @@ -0,0 +1,40 @@ + +F*EX tools + +
+

F*EX tools

+ + + + + + + +
fexsend + UNIX CLI client for sending files (with many + + additional features)
fexget + UNIX CLI client for receiving files (with many + + additional features)
fexget + Windows CLI client for receiving files +
sexsend, sexget + UNIX CLI clients for sending and receiving streams
schwuppdiwupp + Windows GUI client for sending files
schwuppdiwupp + Macintosh GUI client for sending files
+

+In opposite to most web browsers all these clients can handle files +greater than 2 GB and are able to resume interrupted up/downloads. +

+Hint for UNIX users: +

  wget -qO- http://$HTTP_HOST$/xx.tar | tar xvf -
+installs fexsend fexget and +xx. +
  wget -qO- http://$HTTP_HOST$/afs.tar | tar xvf -
+also installs the client programs for +Stream EXchange +and +anonymous usage. + + + diff --git a/htdocs/version b/htdocs/version new file mode 100644 index 0000000..733bf4c --- /dev/null +++ b/htdocs/version @@ -0,0 +1 @@ +fex-20150120 diff --git a/install b/install new file mode 100755 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 (

) { + if (/(\d+\.\d+\.\d+\.\d+)/) { + $guessed_ip = $1; + last; + } + } + close P; + print "Your IP [$guessed_ip] : "; + chomp($ip = ); + $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 () { + $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] : "; + $_ = ; + s/\s//g; + $hostname = $_ if $_; + last if gethostbyname($hostname); + print "No DNS for $hostname\n"; + } + for (;;) { + print "F*EX admin [$admin] : "; + $_ = ; + 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 = ; + $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 () { + 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 (

) { + chomp; + print "$_ --> $dd/$_\n" unless /\/$/; + } + close P; +} diff --git a/lib/dop b/lib/dop new file mode 100755 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 +# + +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 <> + 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 = "\n

$uri/

\n"; + foreach my $d (sort @dirs) { + if ($d =~ m:^/: and -f "$d/.htindex") { + $htmldoc .= "

$d/

\n"; + } elsif (-f "$dir/$d/.htindex") { + $htmldoc .= "

$uri/$d/

\n"; + } + } + +# # then the symlinks +# $htmldoc .= "\n
\n";
+#  my $link;
+#  foreach my $l (sort @links) {
+#    if ($l =~ /$allowed/ and $link = readlink "$dir/$l" and $link =~ /^[^.\/]/) {
+#      $htmldoc .= "$l -> $dir/$link\n";
+#    }
+#  }
+  
+  # then the files
+  $htmldoc .= "\n
\n";
+  foreach my $f (sort @files) {
+    if ($f =~ /$allowed/) {
+      $htmldoc .= sprintf "%20s %20s %s\n",
+                          isodate(mtime("$dir/$f")),
+                          d3(-s "$dir/$f"||0),
+                          $uri,urlencode($f),$f;
+    }
+  }
+  $htmldoc .= "
\n\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( + '

$htauth missing

' + '' + )); + 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 <> 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 index 0000000..f7d26e5 --- /dev/null +++ b/lib/fex.ph @@ -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 index 0000000..bb72a4e --- /dev/null +++ b/lib/fex.pp @@ -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( + '' + '' + '' + )); + &reexec; +} + + +sub debug { + print header(),"
\n";
+  print "file = $file\n";
+  foreach $v (keys %ENV) {
+    print $v,' = "',$ENV{$v},"\"\n";
+  }
+  print "

\n"; +} + + +sub nvt_print { + foreach (@_) { syswrite STDOUT,"$_\r\n" } +} + + +sub html_quote { + local $_ = shift; + + s/&/&/g; + s/' + '' + ' ' + ' ' + ' $title' + '' + )); + # '' + + if ($0 =~ /fexdev/) { $head .= "\n" } + else { $head .= "\n" } + + $title =~ s:F\*EX:F*EX:; + + if (open $header,'<',"$docdir/$header") { + $head .= $_ while <$header>; + close $header; + } + + $head .= &$prolog($title) if defined($prolog); + + if (@H1_extra) { + $head .= sprintf( + '

%s

', + $H1_extra[0],$H1_extra[1]||'',$title + ); + } else { + $head .= "

$title

"; + } + $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///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("

\n",@msg),"\n"; + pq(qq( + '


' + '

+ ' $ENV{HTTP_HOST}' + ' $isodate' + ' $ENV{SERVER_ADMIN}' + '
' + '' + )); + 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( + "
" + "

Server is in maintenance mode

" + "

($status)

" + "
" + "


" + "

$ENV{HTTP_HOST} $isodate
" + "" + )); + 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( + "

$user is disabled

" + "Contact $ENV{SERVER_ADMIN} for details" + "


" + "

$ENV{HTTP_HOST} $isodate
" + "" + )); + 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*

.*//; + + 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( + '

' + '

' + ' ' + '
' + '

' + )); +} + + +# 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/&/&/g; + s/\n$_\n

\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} ||= \¬ify; + } + 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 index 0000000..dd53f4c --- /dev/null +++ b/lib/fup.pl @@ -0,0 +1,40 @@ +# config for F*EX CGI fup + +$info_1 = $info_login = <

+F*EX (File EXchange) +is a service to send big (large, huge, giant, ...) files. +

+The sender (you) uploads the file to the F*EX server and the recipient automatically gets +a notification e-mail with a download-URL.
+After download or after $keep_default days the server deletes the file. +F*EX is not an archive! +

+See also questions & answers and +use cases. +


+

+ $ENV{SERVER_ADMIN}
+
+EOD + +$info_2 = <

+After submission you will see an upload progress bar +(if you have javascript enabled and popups allowed). +

+NOTE: Many web browsers cannot upload files > 2 GB!
+If your file is larger you have to use a special F*EX client +or Firefox or Google Chrome which have no size limit.
+You also need a F*EX client for resuming interrupted uploads. Your web browser cannot do this. +

+If you want to send more than one file, then put them in a zip or tar archive, +e.g. with 7-Zip. +

+See also the FAQ and +use cases. +


+

+ $ENV{SERVER_ADMIN}
+
+EOD diff --git a/lib/reactivation.txt b/lib/reactivation.txt new file mode 100644 index 0000000..1e55dc0 --- /dev/null +++ b/lib/reactivation.txt @@ -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 index 0000000..45cc933 --- /dev/null +++ b/locale/czech/htdocs/index.html @@ -0,0 +1,139 @@ + + + +F*EX - File EXchange + +
+

F*EX - Frams' Fast File EXchange

+ +
+

+F*EX (Frams' Fast File EXchange) je služba pro odesílání velkých +souborů uživatelem A uživateli B. +

+Odesilatel pomocí webového formuláře pro nahrání 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í. +

+ +Říkáte si: +

+ Proč potřebuji další službu pro přenos souborů?!
+ Mám e-mail, ftp, ssh a v neposlední řadě i sendfile! +
+

+Já pravím: +

+ Stále potřebujete F*EX :-) +
+

+ +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):

+ +

    +
  • poslat DVD poÅ¡tou

    + Nepřichází v úvahu - žijeme + <> let + od roku, kdy byl vynalezen Internet! + Posílání paměťových médií je pro staříky. +

    +

  • použít e-mail

    + 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ě. +

    +

  • uucp

    + Proč rovnou nezůstat u psaní klínovým písmem na kamenné destičky? +

    +

  • ssh

    + Vy máte heslo svého známého, nebo se mu chystáte dát to své + jen kvůli přenosu souboru? +

    +

  • ftp

    +

      +
    • Pomocí jakého ftp serveru? +
    • Pomocí jakého účtu, jakého hesla? +
    • Vám nevadí posílání neÅ¡ifrovaného hesla a souborů přes nezabezpečený Internet? +
    • Podporuje vaÅ¡e proxy pasivní režim pro ftp? +
    • V případě anonymního ftp: +
        +
      • Umožňuje nahrát 1 GB? +
      • Umožňuje po nahrání soubor smazat? +
      • Kdo další si váš soubor může stáhnout? +
      • Kdo další může váš soubor smazat? +
      • 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.
        + Sečteno a podrženo: Otrava. +
      +
    +

    +

  • http

    +

      +
    • Žádné výchozí nahrávání a správa uživatelů - musí se naprogramovat +
    • Žádné automatické oznamování +
    • Žádné automatické mazání +
    • Velice málo http serverů umí zpracovat soubory větší než 2 GB +
    +

    +

  • sendfile

    +

      +
    • Používáte UNIX a máte nainstalovaný sendfile? +
    • Používá příjemce UNIX a má nainstalovaný sendfile? +
    • Nemáte vy ani příjemce firewall, který blokuje port 487? +
    +

    +

  • komerční služby jako jsou DropLoad, ALLPeers, YouSendIt, etd.

    +

      +
    • Jaký mají limit (větÅ¡inou méně než 2 GB)? +
    • Co bezpečnost a ochrana osobních údajů: Budou vaÅ¡e soubory skutečně uloženy a v bezpečí? +
    • Jsou provozovány na open-source, nebo na uzavřeném proprietálním softwaru? +
    • Jsou přístupné se vÅ¡emi prohlížeči, nebo potřebujete Javu, Active-X, Flash či jiné hnusné zásuvné moduly? +
    • Budou existovat více, jak pár měsíců?
      + (DropLoad, ALLPeers a drop.io své služby již ukončili) +
    +

    +

+

+Pokud jste byť na jedinou z výše uvedených otázek odpověděli "ne", potom potřebujete +F*EX. +

+

Hlavní funkce F*EX

+

+

    +
  • přenos souborů s prakticky neomezenou velikostí +
  • 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 +
  • ZNOVU ODESLÁNÍ a ZNOVU STAŽENÍ pro pokračování přesně tam, kde doÅ¡lo k selhání spojení +
  • automatické oznamování pro příjemce +
  • automatické mazání po stažení +
  • automatické mazání po době platnosti (výchozí: 5 dnů) +
  • plnohodnotní uživatelé si mohou vytvářet poduživatelé, kteří mohou odesílat pouze jim +
  • plnohodnotní uživatelé mohou vytvářet skupiny, obdoba poÅ¡tovních konferencí s tím rozdílem, že se jedná o soubory +
  • bezúdržbovost: ze strany správce nejsou nutné žádné zásahy s vyjímkou vytváření nových F*EX účtů +
  • odesílání více příjemcům spotřebuje na serveru pouze místo pro jednoho příjemce +
  • F*EX je webová služba pro protokol HTTP a nepotřebuje žádné tunely ve firewallu +
  • podporuje také vysílání +
  • 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čů +
  • protokol i zdrojový kód jsou volně dostupné (Perl Artistic) +
+ +

Promluvme si o SEXu

+F*EX má společníka: Stream EXchange (SEX).

+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. +

+Ověřování je stejné jako u F*EX. + +

Máte otázky?

+Přečtěte si FAQ + +

+

kontaktujte: fexmaster
+ + + diff --git a/locale/czech/lang.html b/locale/czech/lang.html new file mode 100644 index 0000000..cd83f0f --- /dev/null +++ b/locale/czech/lang.html @@ -0,0 +1 @@ +čeština diff --git a/locale/czech/lib/fup.pl b/locale/czech/lib/fup.pl new file mode 100644 index 0000000..9bf5dd9 --- /dev/null +++ b/locale/czech/lib/fup.pl @@ -0,0 +1,35 @@ +# config for F*EX CGI fup + +$info_1 = $info_login = <

+F*EX (File EXchange) +je služba pro odesílání velkých souborů. +

+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í.
+Po jeho stažení, nebo po uplynutí $keep_default dnů, server soubor smaže. +F*EX není archiv! +

+Přečtěte si také Otázky a odpovědi (FAQ). +


+

+ $ENV{SERVER_ADMIN}
+
+EOD + +$info_2 = <

+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). +

+POZNÁMKA: Většina webových prohlížečů neumožňuje nahrávat soubory větší než 2 GB!
+Pokud je váš soubor větší, použíjte speciálního klienta pro F*EX.
+Potřebuvoat budete také klient pro obnovení nahrávání v případě přerušení. Váš webový prohlížeč toto neumožňuje. +

+UPOZORNĚNÍ: Některé HTTP proxy servery, jako je třeba privoxy, spomalují průběh nahrávání!
+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}. +

+Pro uživatele Firefoxu: Nemačkejte klávesu ESC, protože jinak se stahování přeruší! +

+Přečtěte si také FAQ. +EOD diff --git a/locale/czech/lib/reactivation.txt b/locale/czech/lib/reactivation.txt new file mode 100644 index 0000000..6fe52c6 --- /dev/null +++ b/locale/czech/lib/reactivation.txt @@ -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 index 0000000..b295e96 --- /dev/null +++ b/locale/debian_translate @@ -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 index 0000000..a096645 --- /dev/null +++ b/locale/french/htdocs/FAQ/FAQ.html @@ -0,0 +1,12 @@ + +F*EX FAQ + + +##

+## << while (($v,$vv) = each %ENV) { print "$v = $vv\n" } >>
+## 
+ +<< require "./faq.pl" or print $! >> + + + diff --git a/locale/french/htdocs/FAQ/admin.faq b/locale/french/htdocs/FAQ/admin.faq new file mode 100644 index 0000000..6f8c75d --- /dev/null +++ b/locale/french/htdocs/FAQ/admin.faq @@ -0,0 +1,47 @@ +
+
+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 à 
+
    +
  • http://gpl.univ-avignon.fr/filez/ +
  • http://freshmeat.net/projects/eventh/ +
  • http://www.schaarwaechter.de/sp/projekte/dateiaustausch.html (en Allemand) +
+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

+ Il faut éditer lib/fex.ph et configurer (exemple): +

+ @local_hosts = qw(127.0.0.1 10.10.100.0-10.10.255.255);
+ @local_domains = qw(flupp.org ulm.sub.net);
+
+(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

+ 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 index 0000000..a096645 --- /dev/null +++ b/locale/french/htdocs/FAQ/admin.html @@ -0,0 +1,12 @@ + +F*EX FAQ + + +##

+## << while (($v,$vv) = each %ENV) { print "$v = $vv\n" } >>
+## 
+ +<< require "./faq.pl" or print $! >> + + + diff --git a/locale/french/htdocs/FAQ/all.html b/locale/french/htdocs/FAQ/all.html new file mode 100644 index 0000000..a096645 --- /dev/null +++ b/locale/french/htdocs/FAQ/all.html @@ -0,0 +1,12 @@ + +F*EX FAQ + + +##
+## << while (($v,$vv) = each %ENV) { print "$v = $vv\n" } >>
+## 
+ +<< require "./faq.pl" or print $! >> + + + diff --git a/locale/french/htdocs/FAQ/faq.pl b/locale/french/htdocs/FAQ/faq.pl new file mode 100644 index 0000000..7770549 --- /dev/null +++ b/locale/french/htdocs/FAQ/faq.pl @@ -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 "\n"; + +print '

F*EX ', + " Frequently Asked Questions

\n"; + +if ($faq ne 'local') { + print "

\n"; + foreach $s (@sections,'All') { + if ($s =~ /$faq/i) { + print "$s\n"; + } else { + printf "[%s]\n",lc($s),$s; + } + } + print "

\n"; +} + +print "\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\$/$env<\/code>/g; + s/\$$var\$/$env/g; + }; + ($q,$a) = split /A:\s*/; + $q =~ s/[\s\n]+$//; + $q =~ s! (/\w[\S]+/[\S]+)! $1!g; + $a =~ s/[\s\n]+$/\n/; + while ($a =~ s/^(\s*)\*/$1
    \n$1
  • /m) { + while ($a =~ s/(
  • .*\n\s*)\*/$1
  • /g) {} + $a =~ s:(.*\n)(\s*)(
  • [^\n]+\n):$1$2$3$2
\n:s + } + $a =~ s/\n\n/\n

\n/g; + $a =~ s/([^>\n\\])\n/$1
\n/g; + $a =~ s/

(.+?)<\/pre>/pre($1)/ges;
+    $a =~ s/\\\n/\n/g;
+#    $a =~ s/^\s*
\s*//mg; + $a =~ s/<([^\s<>\@]+\@[\w.-]+)>/<$1><\/a>/g; + $a =~ s! (/\w[\S]+/[\S]+)! $1!g; + $a =~ s!(https?://[\w-]+\.[^\s<>]+)![$1]!g or + $a =~ s!(https?://[^\s<>]+)!$1!g; + push @{$Q{$faq}},$q; + push @{$A{$faq}},$a; + } + close $faq; +} + +print "
\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 "\n", + $s,$n+1,$t,$n+1,${Q{$c}[$n]}; + } +} + +print "
%s Q%d: %s
\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 "


\n"; + print "\n"; + printf "\n", + $t,$n+1,$s,$n+1,${Q{$c}[$n]}; + printf "\n", + $s,$n+1,${A{$c}[$n]}; + print "\n"; + print "
%s Q%d:%s
%s A%d:\n%s
[Top]
\n"; + } +} + +print "

\n";
+print "\n" x 99;
+print "
\n"; + + +sub sections { + my $faq = shift; + if ($faq eq 'all') { + return @sections; + } else { + return ucfirst($faq); + } +} + +sub pre { + local $_ = shift; + s/
//g; + s/\s+$//; + return "
$_
\n"; +} diff --git a/locale/french/htdocs/FAQ/index.html b/locale/french/htdocs/FAQ/index.html new file mode 120000 index 0000000..1dadbb5 --- /dev/null +++ b/locale/french/htdocs/FAQ/index.html @@ -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 index 0000000..8479331 --- /dev/null +++ b/locale/french/htdocs/FAQ/local.faq @@ -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 : + + + cd /home/fex/htdocs/FAQ + ln -sf local.html index.html + diff --git a/locale/french/htdocs/FAQ/local.html b/locale/french/htdocs/FAQ/local.html new file mode 100644 index 0000000..a096645 --- /dev/null +++ b/locale/french/htdocs/FAQ/local.html @@ -0,0 +1,12 @@ + +F*EX FAQ + + +##
+## << while (($v,$vv) = each %ENV) { print "$v = $vv\n" } >>
+## 
+ +<< require "./faq.pl" or print $! >> + + + diff --git a/locale/french/htdocs/FAQ/meta.faq b/locale/french/htdocs/FAQ/meta.faq new file mode 100644 index 0000000..53b9153 --- /dev/null +++ b/locale/french/htdocs/FAQ/meta.faq @@ -0,0 +1,48 @@ +
+
+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: 
    +
  • Ils ont une limite de 2GB ou moins. +
  • Leur politique de sécurité et de confidentialité est inconnue. +
  • Ils ne sont pas opensource. +
  • Il n'y a pas d'interface en ligne de commande (CLI) UNIX. +
  • Ils ont besoin de java, active-X, flash ou d'autres plugins esotériques. +
  • On ne sait pas combien de temps ils vont exister - DropLoad and ALLPeers ont déja cessé leur activité. +
+ +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 index 0000000..a096645 --- /dev/null +++ b/locale/french/htdocs/FAQ/meta.html @@ -0,0 +1,12 @@ + +F*EX FAQ + + +##
+## << while (($v,$vv) = each %ENV) { print "$v = $vv\n" } >>
+## 
+ +<< require "./faq.pl" or print $! >> + + + diff --git a/locale/french/htdocs/FAQ/misc.faq b/locale/french/htdocs/FAQ/misc.faq new file mode 100644 index 0000000..c7f9941 --- /dev/null +++ b/locale/french/htdocs/FAQ/misc.faq @@ -0,0 +1,12 @@ +
+
+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

+ +Les fonctionnalités souhaitées sont : +

    +
  • des testeurs pour MacOS, AIX et les autres UNIX +
  • un nouveau mainteneur pour le client Java F*EX +
  • un plugin F*EX pour thunderbird ou outlook +
  • plus de langages supportés (japonais, klingon ...) +
diff --git a/locale/french/htdocs/FAQ/misc.html b/locale/french/htdocs/FAQ/misc.html new file mode 100644 index 0000000..a096645 --- /dev/null +++ b/locale/french/htdocs/FAQ/misc.html @@ -0,0 +1,12 @@ + +F*EX FAQ + + +##
+## << while (($v,$vv) = each %ENV) { print "$v = $vv\n" } >>
+## 
+ +<< require "./faq.pl" or print $! >> + + + diff --git a/locale/french/htdocs/FAQ/user.faq b/locale/french/htdocs/FAQ/user.faq new file mode 100644 index 0000000..dab0a9d --- /dev/null +++ b/locale/french/htdocs/FAQ/user.faq @@ -0,0 +1,60 @@ +
+
+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.

+ 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 index 0000000..a096645 --- /dev/null +++ b/locale/french/htdocs/FAQ/user.html @@ -0,0 +1,12 @@ + +F*EX FAQ + + +##

+## << while (($v,$vv) = each %ENV) { print "$v = $vv\n" } >>
+## 
+ +<< require "./faq.pl" or print $! >> + + + diff --git a/locale/french/htdocs/index.html b/locale/french/htdocs/index.html new file mode 100644 index 0000000..925a3a1 --- /dev/null +++ b/locale/french/htdocs/index.html @@ -0,0 +1,131 @@ + + + +F*EX - File EXchange + +
+

F*EX - Frams' Fast File EXchange

+ +
+

+F*EX (Frams' Fast File EXchange) est un service pour envoyer des fichiers +volumineux d'un utilisateur A à un utilisateur B. +

+L'expéditeur upload le fichier au serveur F*EX en utilisant +un formulaire WWW et le destinataire reçoit automatiquement +une notification. +

+ +Vous vous dîtes: +

+ Pourquoi aurais -je besoin d'un autre service de transfert de fichier ?!
+ J'ai déja l'e-mail, le ftp, ssh et même sendfile ! +
+

+Je réponds: +

+ Vous avez toujours besoin de F*EX :-) +
+

+ +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):

+ +

    +
  • envoyer un DVD par la voie postale

    + Hors de question - nous vivons en <> après l'invention d'Internet. C'est pour les grand-pères. +

    +

  • utiliser l'e-mail

    + 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. +

    +

  • uucp

    + Et pourquoi pas du marbre et un burin ? +

    +

  • ssh

    + 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 ? +

    +

  • ftp

    +

      +
    • Quel serveur ? +
    • Quel compte, quel mot de passe ? +
    • Vous ne vous souciez pas d'envoyer des mots de passes non chiffrés et des fichiers sur un Internet non sécurisé ? +
    • Est-ce que votre proxy support le ftp passif ? +
    • Dans le cas d'un ftp anonyme : +
        +
      • Est-ce qu'il permet des uploads de 1GB ? +
      • Est-ce qu'il permet de supprimer le fichier uploadé ? +
      • Qui peut télécharger votre fichier ? +
      • Qui peut supprimer votre fichier ? +
      • 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 + .
        + C'est plutôt pénible. +
      +
    +

    +

  • http

    +

      +
    • Pas d'upload par défaut et de gestion des utilisateurs. Cela doit être programmé +
    • Pas de notification automatique +
    • Pas d'effacement automatique +
    • Peu de serveur http peuvent gérer des fichiers de plus de 2GB +
    +

    +

  • sendfile

    +

      +
    • Vous utilisez UNIX et sendfile est installé ? +
    • Votre destinataire utilise UNIX et a sendfile d'installé ? +
    • Ni vous ou votre destinataire n'a le port 487 bloqué par un firewall ? +
    +

    +

  • Des services commerciaux comme DropLoad, ALLPeers, YouSendIt, etc

    +

      +
    • Quel limite ont-ils (le plupart: < 2 GB)? +
    • Quid de la sécurité et de la confidentialité : est-ce vos fichiers seront entre de bonnes mains ? +
    • Est-ce qu'ils sont opensource ou propriétaire ? +
    • 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 ? +
    • Est-ce qu'ils vont encore exister d'ici quelques mois?
      + (DropLoad, ALLPeers et drop.io ont déja mis la clé sous la porte) +
    +

    +

+

+Si vous avez répondu "non" à une seule de ces questions, alors vous avez besoin de F*EX +

+

Principales fonctionnalités de F*EX

+

+

    +
  • transfert de fichier de taille virtuellement infini +
  • 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. +
  • reprise d'upload et de téléchargement en cas de défaillance +
  • notification automatique du destinataire +
  • suppression automatique après un téléchargement +
  • suppression automatique après un délai ajustable (par défaut: 5 jours) +
  • les utilisateurs F*EX peuvent créer des URLs d'upload à usage unique pour des utilisateurs extérieurs +
  • les utilisateurs F*EX peuvent créer des sous-utilisateurs qui ne pourront envoyer des fichiers uniquement à cet utilisateur +
  • les utilisateurs F*EX peuvent créer des groupes, similaire à des listes de diffusions, mais pour des fichiers +
  • maintenance réduite au minimum : pas d'interventations de l'administrateur nécessaires mise à part la création de nouveaux utilisateurs +
  • l'envoi à plusieurs destinataires ne nécessite pas le stockage d'autant de fichiers sur le serveur +
  • F*EX est un service web HTTP qui ne nécessite pas de contourner les parre-feux. +
  • support des flux +
  • pour les vrais utilisateurs UNIX, il existe les programmes shell fexsend et fexget afin d'éviter l'utilisation ennuyeuse de l'interface web +
  • protocole et code source librement disponible (Perl Artistic) +
+ +

Parlons de SEX

+F*EX a un ami : Stream EXchange (SEX).

+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. +

+L'authentifaction est la même qu'avec F*EX + +

Encore des questions?

+Voir la FAQ + +

+

contact: fexmaster
+ + + diff --git a/locale/french/lang.html b/locale/french/lang.html new file mode 100644 index 0000000..75cddf3 --- /dev/null +++ b/locale/french/lang.html @@ -0,0 +1 @@ +français diff --git a/locale/french/lib/fup.pl b/locale/french/lib/fup.pl new file mode 100644 index 0000000..fb72a49 --- /dev/null +++ b/locale/french/lib/fup.pl @@ -0,0 +1,32 @@ +# config for F*EX CGI fup + +$info_1 = $info_login = <

+F*EX (File EXchange) +est un service pour envoyer des fichiers très volumineux (grand, énorme, géant, ...). +

+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.
+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! +

+Voir les questions & réponses. +


+

+ $ENV{SERVER_ADMIN}
+
+EOD + +$info_2 = <

+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) +

+REMARQUE: La plupart des navigateurs ne peuvent pas uploader des fichiers > 2 GB!
+Si votre fichier est plus gros, vous devez utiliser un client F*EX spécial.
+Vous devez aussi en utiliser un pour la reprise d'upload interrompu. Votre navigateur ne peut pas le faire. +

+Pour les utilisateurs de Firefox : ne pas appuyer sur [ESC] parce que cela stoppera l'upload ! +

+Voir aussi la FAQ. +EOD diff --git a/locale/french/lib/reactivation.txt b/locale/french/lib/reactivation.txt new file mode 100644 index 0000000..f00dc9e --- /dev/null +++ b/locale/french/lib/reactivation.txt @@ -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 index 0000000..b1ed318 --- /dev/null +++ b/locale/galician/htdocs/FAQ.html @@ -0,0 +1,264 @@ + +

+Cuestións meta:
+===============
+
+Q: Por que o nome "F*EX" e non directamente "FEX"?
+
+A: No momento de facerse público xa había unha (antigo) aplicativo chamado "FEX" en
+   freshmeat.net.
+
+
+Q: Por que non usar un servizo comercial como
+   DropLoad, ALLPeers, YouSendIt, etc?
+
+A: Teñen un límite de 2 GB ou menor.
+   A súa seguranza e privacidade son descoñecidas.
+   Non se basean en código fonte aberto.
+   Non hai clientes de UNIX (CLI) para eles.
+   Necesitan java, active-X, flash ou outros endemoñados complementos.
+   Descoñécese canto tempo van funcionar - DropLoad e ALLPeers xa pecharon
+   as súas portas.
+
+
+Q: Por que un camelo no logo?
+
+A: O logo inspirouse no camelo de Perl, pero baseáse en Steiff, un camelo de peluxe,
+   co cal constituímos o noso tándem. O logo foi debuxado
+   por Beate
+   http://fex.rus.uni-stuttgart.de/Vortrag/tosa.html
+
+
+Q: Onde podo eu obter as fontes de F*EX?
+
+A: http://fex.rus.uni-stuttgart.de/fex.html
+
+
+Q: Que necesito para instalar F*EX?
+
+A: Un servidor UNIX con entrada no DNS e smtp para o correo de saída.
+   E debe ser root nese servidor.
+
+
+Q: Que é o DNS e smtp?
+
+A: Non instale F*EX. Está alén das súas posibilidade.
+
+
+Q: Quen e o autor?
+
+A: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
+
+
+Q: Que licenza ten F*EX?
+
+A: De software libre, Perl Artistic, vexa http://fex.rus.uni-stuttgart.de/doc/Licence
+
+
+Q: Hai unha lista de correo de F*EX?
+
+A: https://listserv.uni-stuttgart.de/mailman/listinfo/fex
+
+
+Q: Onde podo obter asistencia comercial para F*EX?
+
+A: Contacto fex@nepustil.net http://www.nepustil.net/
+
+
+Q: Teñeo máis/outras preguntas diferentes!
+
+A: Preguntarlle ao autor <framstag@rus.uni-stuttgart.de>
+
+
+
+Preguntas de usuario:
+=====================
+
+Q: Que é o "auth-ID"?
+
+A: O auth-ID é unha identificación interna que autentica o usuario.
+   Será a primeira xerada polo admin ou o proceso de rexistro automático
+   e pode despois modificalo vostede, o usuario. Trátase dun contrasinal
+   de baixa seguranza.
+
+
+Q: Podo usar un proxy HTTP?
+
+A: Si.
+
+
+Q: Carguei un ficheiro ENORME pero equivoqueime ao escribir o enderezo do destinatario. Agora teño
+   un erro de entrega de correo. Debo volver cargar ese ENORME ficheiro?
+
+A: Non, non fai falta. Pode redirixir o ficheiro con
+   http://$HTTP_HOST$/rup
+
+
+Q: Cargue un ficheiro ENORME pero esquecín outro destinatario.
+   Debo volver cargar ese ENORME ficheiro?
+
+A: Non, non é necesario. Pode reenviar copia do ficheiro con
+   http://$HTTP_HOST$/foc
+
+
+Q: Non podo cargar ficheiros > 2 GB co meu navegador web!?
+
+A: Todos os navegadores teñen defectos na implementación dos
+   seus formularios HTML. O límite máis frecuente é de 2 GB, ás veces 4 GB.
+
+   Debe usar un cliente especial de F*EX para cargar ficheiros > 2 GB, vexa
+   http://$HTTP_HOST$/tools.html
+
+
+Q: A miña descarga abortouse antes de rematar. Podo retomar esa descarga?
+
+A: F*EX permite retomar e descargar, pero o seu cliente tamén debe permitir
+   esta funcionalidade. Firefox por exemplo carece desta funcionalidade HTTP, necesitará
+   outro cliente como Opera, wget ou fexget.
+
+
+Q: A miñ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 http://$HTTP_HOST$/tools.html
+
+
+Q: O meu navegador non pode iniciar o cliente java F*IX, que indica:
+   "non se atopou un entorno de execución java, non se pode iniciar F*IX o miniaplicativo de carga"
+
+A: Non se localiza un complemento java do navegador. En Debian e Ubuntu pode
+   instalalo con: "sudo aptitude install sun-java6-plugin"
+
+
+Q: Cando premo[ESC] no Firefox a carga cancélase. Por que?
+
+A: Esta é unha funcionalidade interna do Firefox: ESC termina a operación actual.
+   Solución simple: non prema ESC no Firefox.
+   Solución complexa: pídalles aos desenvolvedores do Firefox que engadan un atallo de teclado.
+
+
+Q: O envío como un usuario F*EX é doado, pero como recibir ficheiros doutros,
+   de fóra?
+
+A: Rexístreos como subusuarios ou cree un grupo F*EX
+   con http://$HTTP_HOST$/fuc
+
+
+Q: Ás veces podo descargar un ficheiro máis dunha vez, especialamente cando
+   o repito rapidamente. Está a funcionalidade de auteliminación defectuosa?
+
+A: O servidor F*EX ten un períod de gracia de 1 minuto despois da primeira
+   descarga correcta no cal o ficheiro aínda está dispoñíbel. Isto é necesario
+   porque algúns "xestores de descargas" estúpidos solicitan o ficheiro
+   varias veces á vez. Caso contrario poderí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ízase unha vez ao día.
+
+A: Os "Power users" ou usuarios con permisos (usa a forza, Luke!) poden establecer unha marca "non eliminar ata
+   despois da descarga".
+
+
+Q: O período de reserva predeterminado é demasiado curto para min, cómpreme máis. Como podo estabelecelo?
+
+A: Use fexsend, pídallo ao seu fexmaster ou lea o código fonte :-)
+
+
+Q: Non podo descargar ficheiros con Internet Explorer, dime "Non se pode
+   abrir o sitio da Internet". Que debería facer?
+
+A: Use Firefox ou calquera outro navegador compatíbel con Internet, xa que Internet
+   Explorer non o é. Este é un de tanto defectos do Internet Explorer.
+
+
+
+
+Preguntas de administrador:
+===========================
+
+Q: Non podo instalar un servidor web como fexsrv, porque non teño permisos de root
+   Hai algunha versión en puro CGI de F*EX que funcione cun servidor web Apache?
+
+A: F*EX está intimamente unido a fexsrv por varias razóns (rendemento, límite de
+   tamaño de ficheiro, concepto de sesiónt, etc.) e non se pode executar como CGI
+   con Apache.
+   Pero pódelle botar un ollo a
+   http://gpl.univ-avignon.fr/filez/
+   http://freshmeat.net/projects/eventh/
+   http://www.schaarwaechter.de/sp/projekte/dateiaustausch.html (só en alemán!)
+   que implementa un intercambio de ficheiros como puros CGI, pero cun tamaño límite de 2 GB por ficheiro.
+
+
+Q: F*EX non funciona! Non podo conectarme co meu navegador web!
+
+A: Comprobe o seu enrutamento, filtros ip e configuración de devasa.
+   Tamén comprobe se o seu xinetd está ligado cun tcp-wrapper e configúreo
+   correctamente (hosts.allow).
+   F*EX necesita o porto 80/tcp (HTTP) e o porto opcional 443/tcp (HTTPS).
+
+
+Q: F*EX é complicado de máis! Necesito algo máis simplificado.
+
+A: Probe http://www.home.unix-ag.org/simon/woof.html
+
+
+Q: Como podo integrar F*EX no xestor de usuarios do meu sitio?
+
+A: F*EX ten varios módulos de autenticación:
+   local, RADIUS, LDAP, mailman e POP
+   Para os 4 últimos, contacte por favor co autor framstag@rus.uni-stuttgart.de
+
+
+Q: Que que todos os meus usuarios locais poidan usar F*EX automatimente. Como?
+
+A: Permí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!)
+
+
+Q: Necesito máis seguranza! Como podo activar HTTPS?
+
+A: Lea doc/SSL e busque "fop_auth" en doc/concept
+
+
+Q: Necesito un aspecto corporativo. Como podo configurar F*EX neste sentido?
+
+A: Vexa a variábel @H1_extra en fex.ph e pode engadir o código HTML a
+   htdocs/header.html
+
+A: Vexa htdocs/fup_template.html, modifíqueo ás súas necesidades e úseo como a súa
+   páxina de inicio.
+
+
+Q: F*EX é demasiado complicado para os meus pobres usuarios. Necesito un formulario de
+   carga simmplificado.
+
+A: Vexa htdocs/fup_template.html
+
+
+Q: Quero que os correos Ccc a (ao usuario admin) se envíen a outro enderezo.
+
+A: Use procmail ou escriba OTHERADDRESS en /home/fex/.forward
+
+
+Q: Podo obter unha versión localizada no meu idioma?
+
+A: Sempre coa súa axuda, si. Contacte co autor framstag@rus.uni-stuttgart.de
+
+
+Preguntas en miscelánea:
+========================
+
+Q: F*EX é fantástico! Podo unirme ao equipo de desenvolvemento? Que hai que facer?
+
+A: Contacte co autor framstag@rus.uni-stuttgart.de
+   As funcionalidades buscadas son:
+
+     - un complemento F*EX para o Thunderbird ou o Outlook
+     - máis (outras) idiomas dispoñíbeis
+
+
diff --git a/locale/galician/htdocs/index.html b/locale/galician/htdocs/index.html new file mode 100644 index 0000000..3bea7b5 --- /dev/null +++ b/locale/galician/htdocs/index.html @@ -0,0 +1,133 @@ + + + +F*EX - File EXchange +
+

F*EX - Frams' Fast File EXchange

+ +
+

+F*EX (Frams' Fast File EXchange) é un servizo para enviar voluminosos +(grandes, enormes, xigantes, ...) ficheiros dun usuario A a un usuario +B. +

+O remisor carga o ficheiro no servidor F*EX usando un formulario WWW e o receptor automaticamente obtén unha notificación vía correo cun enderezo URL para descargar. +

+ +Vostede dirá: +

+ Por que necesito outro servizo para transferencia de ficheiros?!
Teño correo, ftp, ssh e mesmo sendfile! +
+

+Ao que respondo: +

+ Aínda así necesita F*EX :-) +
+

+ +Por exemplo, quere enviar ao seu amigo o video das últimas vacacións (1 GB).  Ten as seguintes posibilidades (e problemas):

+ +

    +
  • o envío dun DVD vía correo postal

    + Nin o vou considerar - vivimos na época actual + <> + despois da invención da Internet! + O envío de soportes multimedia (físicos) é para avoíñas. +

    +

  • o uso do correo

    +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.

    +

  • uucp

    E por que non escritura cuneiforme en laxes de pedra? +

    +

  • ssh

    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? +

    +

  • ftp

    +

      +
    • Usando que servidor ftp? +
    • Usando que conta, que contrasinal? +
    • Non se lle pasará pola cabeza enviar contrasinais sen cifrar e ficheiros na incerta Internet? +
    • O seu proxy permite o ftp pasivo? +
    • No caso de ftp anónimo: +
        +
      • Permite cargas de 1 GB? +
      • Permite eliminar a carga despois? +
      • Quen pode descargar ademais o seu ficheiro? +
      • Quen pode eliminar o seu ficheiro? +
      • 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.
        + Definitivamente: unha merda pinchada nun pao. +
      +
    +

    +

  • http

    +

      +
    • Nin carga predeterminada nin xestión de usuario - debe ser programada
    • +
    • Sen autonotificación +
    • +
    • Sen autoeliminado +
    • Moi poucos servidores http poden xestionar ficheiros superiores a 2 GB +
    +

    +

  • sendfile

    +

      +
    • Executas un UNIX e tes o sendfile instalado? +
    • O teu receptor executa UNIX e ten o sendfile instalado? +
    • Nin ti nin o teu receptor ten unha devasa que bloquee o porto 487? +
    +

    +

  • servizos comerciais como DropLoad, ALLPeers, YouSendIt, etc

    +

      +
    • Que límite teñen (maiormente: < 2 GB)? +
    • Como andan de seguranza e privacidade: estarán os seus ficheiros gardados e seguros? +
    • Están baseados en software aberto ou en privativo? +
    • Están accesíbeis con calquera outro navegador ou cómpre usar java, active-X, flash ou outros complementos endemoñados? +
    • Funcionarán alén duns poucos meses?
      + (DropLoad, ALLPeers e drop.io xa pecharon as súas portas) +
    +

    +

+

+Se respondeu só unha vez "non" ás cuestións anteriores, entón necesita F*EX. +

+

Principais funcionalidades de F*EX

+

+

    +
  • transferencia de ficheiro ou  tamaño virtualmente ilimitada +
  • receptor e remitente só necesitan un programa de correo e un navegador web - + de calquer clase, non teñen que instalar ningún software +
  • REENVIAR and REOBTER para retomar despois de fallos de ligazón ata que se envíe o último byte +
  • autonotificación de receptor +
  • autoeliminación despois da descarga +
  • autoeliminación despois dunha data de expiración (predeterminada: 5 días) +
  • os usuarios con plenos dereitos poden crear subusuarios, que poden envíar soamente a este usuario pleno +
  • os usuarios con plenos dereitos poden crear grupos, en analoxía coas listas de correo, pero para ficheiros +
  • sen mantemetno: non se necesita interacción acompañando a creación de novas contas con F*EX +
  • o envío a múltiples receptores necesita capacidade de almacenamento no servidor só unha vez +
  • F*EX é un servizo web por HTTP e non necesita túneles nin devasas +
  • permite a distribución de fluxos, tamén +
  • para usuarios reais de UNIX, hai aplicativos da shell como fexsend e fexget que evitan o uso impertinente de navegador +
  • o protocolo e o código fonte están dispoñíbeis libremente (Perl Artistic) +
+ +

Falemos de SEX

+F*EX ten un compañeiro: Stream EXchange (SEX).

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. +

+A autenticación é a mesma que con F*EX. + +

Quedanlle preguntas?

Vexa a FAQ + +

+

contacto: fexmaster
+ + + diff --git a/locale/galician/lang.html b/locale/galician/lang.html new file mode 100644 index 0000000..c835df4 --- /dev/null +++ b/locale/galician/lang.html @@ -0,0 +1 @@ +galego diff --git a/locale/galician/lib/fup.pl b/locale/galician/lib/fup.pl new file mode 100644 index 0000000..fb730ce --- /dev/null +++ b/locale/galician/lib/fup.pl @@ -0,0 +1,35 @@ +# config for F*EX CGI fup + +$info_1 = $info_login = <

+F*EX (File EXchange) +é un servizo para enviar (grandes, enormes, xigantes, ...) ficheiros. +

+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.
+Despois de descargalo ou tras $keep_default días o servidor elimina o ficheiro. +F*EX non é un arquivo! +

+Vexa máis información en preguntas e respostas. +


+

+ $ENV{SERVER_ADMIN}
+
+EOD + +$info_2 = <

+Despois de remitilo verá unha barra de progreso de carga +(se ten o javascript activado e permite as xanelas emerxentes). +

+NOTA: A maior parte dos navegadores non poden cargar ficheiros > 2 GB!
+Se o seu ficheiro é maior, ten que usar un cliente F*EX especial.
+Tamén pode necesitalo para retomar cargas interrompidas. O seu navegador non pode facelo. +

+Aviso: algúns proxies HTTP como privoxy retardan a barra de progreso de carga!
+Pode querer desactivar a intermediación do proxy $ENV{SERVER_NAME} se se encontra con este problema. +

+Usuarios do Firefox: non prema en [ESC] porque isto abortará a carga! +

+Vexa máis información na FAQ. +EOD diff --git a/locale/galician/lib/reactivation.txt b/locale/galician/lib/reactivation.txt new file mode 100644 index 0000000..7289245 --- /dev/null +++ b/locale/galician/lib/reactivation.txt @@ -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 index 0000000..43d4140 --- /dev/null +++ b/locale/german/htdocs/FAQ.html @@ -0,0 +1,260 @@ + +

Dieses Dokument ist veraltet. Bitte benutzen Sie die + englische FAQ.

+
+Allgemeine Fragen:
+==================
+
+F: Warum der Name "F*EX" und nicht einfach "FEX"?
+
+A: Zur Zeit der Veröffentlichung existierte bereits ein (älteres) Programm 
+   namens "FEX" 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ür sie.
+   Sie brauchen Java, Active-X, Flash oder andere bösartige Plugins.
+   Es ist unklar wie lange sie noch existieren werden - DropLoad und ALLPeers
+   haben bereits aufgehört zu existieren.
+
+
+F: Warum ein Kamel als Logo?
+
+A: Das Logo war inspiriert durch das Perl Kamel, zu Grunde liegt aber ein 
+   Plüschkamel von Steiff welches auf unserem Renn-Tandem mitfährt. 
+   Das Logo wurde von meiner Stokerin Beate gezeichnet, siehe
+   http://fex.rus.uni-stuttgart.de/Vortrag/tosa.html
+
+
+F: Wo kann ich die F*EX Sourcen bekommen?
+
+A: http://fex.rus.uni-stuttgart.de/fex.html
+
+
+F: Was braucht man um F*EX installieren zu können?
+
+A: Ein UNIX Server mit DNS Eintrag und smtp für ausgehende E-Mail.
+   Und Sie mü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 <framstag@rus.uni-stuttgart.de>
+
+
+F: Unter welcher Lizenz läuft F*EX?
+
+A: Perl Artistic Freie Software, siehe http://fex.rus.uni-stuttgart.de/doc/Licence
+
+
+F: Gibt es eine F*EX Mailing Liste?
+
+A: https://listserv.uni-stuttgart.de/mailman/listinfo/fex
+
+
+F: Wo kann ich einen kommerziellen Support für F*EX bekommen?
+
+A: Nehmen Sie Kontakt auf mit fex@nepustil.net http://www.nepustil.net/
+
+
+F: Ich habe noch weitere Fragen bezüglich dieses Dokuments!
+
+A: Fragen Sie den Autor <framstag@rus.uni-stuttgart.de>
+
+
+Benutzerfragen:
+===============
+
+Q: Was ist die "auth-ID"?
+
+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äter durch Sie 
+   geändert werden. Bitte beachten Sie dass man hier keine wertvollen
+   Passwörter verwenden sollte weil diese unverschlü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ß ich die RIESIGE Datei
+   noch mal hochhladen?
+
+A: Nein, das ist nicht nötig. Sie können die Datei einfach weiterleiten 
+   mit Hilfe von http://$HTTP_HOST$/rup
+
+
+F: Ich habe eine RIESIGE Datei versendet doch leider habe ich noch einen 
+   Empfänger vergessen. Muß ich die RIESIGE Datei noch mal hochladen?
+
+A: Nein, das ist nicht nötig. Sie können die Datei einfach kopieren und
+   weiterleiten mit Hilfe von http://$HTTP_HOST$/foc
+
+
+F: Warum kann ich keine Dateien größ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üssen einen speziellen F*EX Client benutzen um Dateien größer 
+   als 2 GB hochzuladen, siehe http://$HTTP_HOST$/tools.html
+
+
+F: Mein Download brach frühzeitig ab. Kann ich ihn wieder aufnehmen?
+
+A: F*EX unterstützt Download-Wiederaufnahme, aber Ihr Client muss es ebenso 
+   unterstützen. Firefox zB fehlt diese HTTP Eigenschaft, daher benötigen 
+   Sie einen anderen Client wie opera, wget oder fexget.
+
+
+F: Mein Upload brach frühzeitig ab. Kann ich ihn wieder aufnehmen?
+
+A: F*EX unterstützt Upload-Wiederaufnahme, aber Ihr Client muss es ebenso 
+   unterstützen. Kein Web-Browser hat diese Unterstützung, Sie 
+   benötigen dfür einen speziellen F*EX Client wie
+   fexsend, schwuppdiwupp oder F*IX.
+   Siehe http://$HTTP_HOST$/tools.html
+
+
+F: Wenn ich die Taste [ESC] in Firefox drücke, dann wird der Upload 
+   abgebrochen. Warum?
+
+A: Das ist die eingebaute Funktion von Firefox: ESC unterbricht die 
+   laufende Operation. 
+   Einfache Lösung des Problems: 
+   [ESC] sollte man nicht drücken.
+   Komplizierte Lösung: 
+   bitten Sie die Firefox-Entwickler, eine Tastatur-Konfiguration hinzuzufü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ö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 "Download Manager" die Datei gleich 
+   mehrmals anfordern. Ohne die Wartezeit wü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öschen nur einmal täglich stattfindet.
+
+A: Power-Benutzer (sehen Sie die Software-Quelltexte!) können den Flag
+   "nach dem Download nicht löschen" setzen.
+
+
+F: Die Standard Aufbewahrungszeit ist mir zur kurz. Wie kann ich diesen Wert 
+   erhö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 "Die Internet-Seite ist nicht verfügbar". 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äuft?
+
+A: fexsrv ist ein fester Bestandteil von F*EX, dazu gibt es viele Gründe 
+   (Leistung, maximale Dateigröße, Session Konzept usw). So kann F*EX nicht 
+   als CGI unter Apache laufen, Sie könnten sich aber das hier anschauen:
+   
+   http://gpl.univ-avignon.fr/filez/
+   http://freshmeat.net/projects/eventh/
+   http://www.schaarwaechter.de/sp/projekte/dateiaustausch.html
+   
+   Das sind einfache CGI-Implementierungen von Daten-Übertragungssystemen,
+   jedoch begrenzt auf 2 GB.
+   
+
+F: F*EX funktioniert überhaupt nicht! Es kommt keine Verbindung zum Server mit 
+   meinem Web-Browser zustande!
+
+A: Prüfen Sie Ihr Routing, IP-Filter und die Firewall-Einstellungen.
+   Prü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 http://www.home.unix-ag.org/simon/woof.html
+
+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üglich der letzten 4 kontaktieren Sie bitte den Autor.
+
+
+F: Ich möchte dass alle meine lokalen Benutzer automatisch F*EX benutzen 
+   können. Wie geht das?
+
+A: Lassen Sie sie sich selbst registrieren.
+   Dazu mü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ürlich mü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 "fop_auth" in doc/concept 
+
+
+F: Die F*EX-Startseite sollte an unsere Firmen-Seiten angepaßt werden. 
+   Wie kann ich F*EX entsprechend konfigurieren?
+
+A: Siehe Variable @H1_extra in fex.ph, ausserdem können Sie htdocs/header.html
+   hinzufügen.
+
+A: Nehmen Sie fup_template.html, modifizieren Sie die Vorlage nach Ihren 
+   Wünschen und nutzen Sie diese als Ihre Startseite.
+
+
+Sonstige Fragen:
+================
+
+F: F*EX ist großartig! Wie kann ich mich dem Entwicklungs-Team anschließen? 
+   Was muß noch gemacht werden?
+
+A: Kontaktieren Sie den Autor wegen:
+
+   - ein F*EX plugin für Outlook
+   
+   - weiteren Übersetzungen in andere Sprachen - Latein wäre toll! :-) 
+
diff --git a/locale/german/htdocs/index.html b/locale/german/htdocs/index.html new file mode 100644 index 0000000..7e969bc --- /dev/null +++ b/locale/german/htdocs/index.html @@ -0,0 +1,135 @@ + +F*EX - File EXchange + +
+

F*EX - Frams' Fast File EXchange

+ +
+

+F*EX (Frams' Fast File EXchange) ist ein Dienst, um große (sehr große, riesige, +gigantische, ...) Dateien von User A zu User B zu senden. +

+Der Absender lädt die Datei über ein +WWW upload Formular auf den F*EX Server hoch und der Empfänger +bekommt automatisch eine Benachrichtigungs-E-Mail mit der Download-URL. +

+ +Sie fragen vielleicht: +

+ Warum brauche ich noch einen Service für Dateitranfer?!
+ Ich habe E-Mail, FTP, SSH und sogar sendfile! +
+

+Ich sage: +

+ Sie brauchen F*EX trotzdem :-) +
+

+ +Sie möchten zum Beispiel einem Freund das letztes Urlaubsvideo senden (1 GB). +Sie haben dazu folgende Möglichkeiten (und Probleme):

+ +

    +
  • eine DVD per Briefpost verschicken

    + Steht außer Frage - wir leben im Jahr + <> + nach der Erfindung des Internet! + Versand von (Hardware-)Medien ist etwas für Opas. +

    +

  • E-Mail benutzen

    + 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. +

    +

  • UUCP

    + Warum nicht gleich Keilschrift-Zeichen in Tonplatten eindrücken? +

    +

  • SSH

    + Sie haben das Passwort Ihres Freundes oder sind gewillt, ihm Ihres zu geben + - nur für einen Datei-Transfer? +

    +

  • FTP

    +

      +
    • Welchen FTP Server benutzen? +
    • Welchen Account benutzen, welches Passwort? +
    • Stört es Sie unverschlüsselte Passwörter über das unsichere Internet zu übertragen? +
    • Unterstützt Ihr Proxy passives FTP? +
    • Im Fall von anonymen FTP: +
        +
      • Erlaubt es 1 GB Uploads? +
      • Kann man den Upload nach dem Transfer wieder löschen? +
      • Wer kann Ihre Datei noch downloaden? +
      • Wer noch kann Ihre Datei löschen? +
      • Sie müssen Ihrem Freund die Download-URL zukommen lassen und er + muss Sie über den erfolgreichen Download informieren, danach müssen Sie die Datei löschen.
        + Alles in Allem: eine nervige Sache. +
      +
    +

    +

  • HTTP

    +

      +
    • Es gibt keine standardmäßige Upload- und User-Verwaltung (muss erst programmiert werden) +
    • Keine Auto-Benachrichtigung +
    • Kein automatisches Löschen +
    • Sehr wenige HTTP Server können mit Dateien größer 2 GB umgehen +
    +

    +

  • sendfile

    +

      +
    • Sie benutzen UNIX und haben sendfile installiert? +
    • Ihr Empfänger benutzt UNIX und hat sendfile installiert? +
    • Weder Ihr Empfänger noch Sie selbst haben eine Firewall die Port 487 blockiert? +
    +

    +

  • Kommerzielle Dienste wie DropLoad, ALLPeers, YouSendIt, etc

    +

      +
    • Welche Beschränkungen haben sie? (meist: < 2 GB)? +
    • Was ist mit Sicherheit und Privatsphäre: werden Ihre Dateien sicher und geschützt sein? +
    • Basieren sie auf Open Source- oder proprietärer Software? +
    • Sind sie mit jedem Browser benutzbar oder braucht man Java, Active-X, Flash oder andere üble Plugins? +
    • Werden sie länger als ein paar Monate existieren?
      + (DropLoad und ALLPeers haben ihren Betrieb schon eingestellt) +
    +

    +

+

+Wenn Sie auch nur eine der Fragen von oben mit "Nein" beantwortet haben, dann brauchen Sie F*EX. +

+

Hauptmerkmale von F*EX

+

+

    +
  • Datei-Transfer mit praktisch unbegrenzter Dateigröße +
  • Empfänger und Absender brauchen lediglich ein E-Mail Programm und einen Web-Browser - + es muss keine zusätzliche Software installiert werden +
  • RESEND und REGET für Wiederaufnahme bei Link Fehlern beim letzten gesendeten Byte +
  • Auto-Benachrichtigung der Empfänger +
  • automatisches Löschen nach Download +
  • automatisches Löschen nach Ablaufdatum (Standard: 5 Tage) +
  • Full-User können Sub-User erstellen, die dann nur an den Full-User senden dürfen +
  • Full-User können Gruppen erstellen, eine Analogie zu Mailinglisten, aber für Dateien +
  • wartungsfrei: bis auf die Erstellung neuer F*EX Accounts ist keine Interaktion eines Admins erforderlich +
  • Für das Senden an mehrere Empfänger wird auf dem Server nur einmal Speicherplatz verbraucht +
  • F*EX ist ein HTTP-Web-Dienst und benötigt keine Firewall-Tunnel +
  • Unterstützung für Streams +
  • für UNIX Benutzer gibt es die Shell-Programme fexsend und fexget, um den lästigen Gebrauch von Webbrowsern zu umgehen +
  • Protokoll und Quellcode sind frei verfügbar (Perl Artistic) +
+ +

Lassen Sie uns über SEX sprechen

+F*EX hat einen Begleiter: Stream EXchange (SEX).

+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önnen, aber beide sich +über HTTP zum SEX Server verbinden können. +Für eine nahtlose Integration in die UNIX Programm Reihe, +gibt es die Shell-Programme sexsend und sexget. +

+Die Authentifizierung ist dieselbe wie bei F*EX. + +

Noch Fragen?

+Siehe FAQ (Fragen und Antworten) + +

+

Kontakt: fexmaster
+ + + diff --git a/locale/german/lang.html b/locale/german/lang.html new file mode 100644 index 0000000..bf777b7 --- /dev/null +++ b/locale/german/lang.html @@ -0,0 +1 @@ +deutsch diff --git a/locale/german/lib/fup.pl b/locale/german/lib/fup.pl new file mode 100644 index 0000000..ddbda20 --- /dev/null +++ b/locale/german/lib/fup.pl @@ -0,0 +1,46 @@ +# lokale Konfiguration fuer F*EX CGI fup + +$info_1 = $info_login = <

+F*EX (File EXchange) +ist ein Dienst um große (sehr große, riesige, gigantische, ...) Dateien zu senden. +

+Der Absender (Sie) lädt eine Datei auf den F*EX Server hoch und der +Empfänger bekommt automatisch eine Benachrichtigungs-E-Mail mit der Download-URL.
+Nach dem Download oder nach $keep_default Tagen löscht der Server die Datei.
+F*EX ist kein Archiv! +

+Um es zu nutzen, geben Sie Ihre Empfänger E-Mail Adresse(n) in die Felder oben ein. +

+Noch immer verwirrt?
+Testen Sie F*EX, indem Sie eine Datei an sich selbst senden +(Absender = Empfänger = Ihre E-Mail Adresse). +

+Siehe auch Fragen & Antworten. +


+

+ $ENV{SERVER_ADMIN}
+
+EOD + +$info_2 = <

+Nach dem Abschicken sehen Sie einen Upload Fortschrittsbalken +(wenn Sie Javascript aktiviert haben und Popups erlauben). +

+Bemerkung: Viele Webbrowser können keine Dateien hochladen, +die größer als 2 GB sind!
+Wenn Ihre Datei größer ist, müssen Sie einen speziellen +F*EX client nutzen.
+Sie brauchen ihn außerdem, wenn Sie abgebrochene Uploads +wiederaufnehmen wollen. Ihr Webbrowser ist dazu nicht in der Lage. +

+Wenn Sie mehr als eine Datei verschicken wollen, dann verpacken Sie sie vorher in ein zip oder tar Archiv, +z.B. mit 7-Zip. +

+Siehe auch FAQ. +


+

+ $ENV{SERVER_ADMIN}
+
+EOD diff --git a/locale/german/lib/reactivation.txt b/locale/german/lib/reactivation.txt new file mode 100644 index 0000000..413dace --- /dev/null +++ b/locale/german/lib/reactivation.txt @@ -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 index 0000000..2872df9 --- /dev/null +++ b/locale/italian/htdocs/FAQ.html @@ -0,0 +1,297 @@ + +

Questo documento non è aggiornato. Si prega di utilizzare il + inglese FAQ.

+
+
+Domande Generiche:
+==================
+
+Q: Perche' il nome e' "F*EX" invece del piu' corto "FEX"?
+
+A: Al momento della pubblicazione c'era gia' un (vecchio) programma
+   nominato "FEX" su freashmeat.net
+
+
+Q: 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'.
+
+
+Q: 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 "fuochista" Beate
+   http://fex.rus.uni-stuttgart.de/Vortrag/tosa.html
+
+
+Q: Dovo posso prendere i sorgenti di F*EX ?
+
+A: http://fex.rus.uni-stuttgart.de/fex.html
+
+
+Q: 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.
+
+
+Q: Cosa sono DNS e SMTP?
+
+A: Lascia perdere F*EX. E' al di la' delle tue possibilita'.
+
+
+Q: Chi e' l'autore ?
+
+A: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
+
+
+Q: Che tipo di licenza ha F*EX ?
+
+A: Perl Artistic free software, vedete http://fex.rus.uni-stuttgart.de/doc/Licence
+
+
+Q: C'e' una mailing list per F*EX ?
+
+A: https://listserv.uni-stuttgart.de/mailman/listinfo/fex
+
+
+Q: Dove posso trovare un servizio commerciale per F*EX ?
+
+A: Contattate fex@nepustil.net http://www.nepustil.net/
+
+
+Q: Ho altre domande che non trovo in questo documento.
+
+A: Chiedete all'autore <framstag@rus.uni-stuttgart.de>
+
+
+
+Domande Utente:
+===============
+
+Q: Cos'e' il campo "auth-ID" ?
+
+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.
+
+Q: Posso usare un proxy HTTP ?
+
+A: Si'.
+
+
+Q: 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
+   http://$HTTP_HOST$/rup
+
+
+Q: Ho caricato un file ENORME ma ho dimenticato uno dei destinatari.
+   Devo ricaricarlo?
+
+
+A: No, non e' necessario. Puo' inoltrare il file con
+   http://$HTTP_HOST$/foc
+
+
+Q: Posso caricare file di dimensioni > 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 > 2GB,
+   vedete http://$HTTP_HOST$/tools.html
+
+
+Q: 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.
+
+
+Q: 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 http://$HTTP_HOST$/tools.html
+
+
+Q: 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:
+   "sudo aptitude install sun-java6-plugin"
+
+
+Q: 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.
+
+
+Q: Spedire come utente F*EX e' facile, ma come posso
+   ricevere i file da altri utenti all'esterno?
+
+A: Registrali come tuoi "sotto-utenti" oppure crea un gruppo F*EX
+   con http://$HTTP_HOST$/fuc
+
+
+Q: 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
+   "download managers" 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
+   "non cancellare dopo il download".
+
+
+Q: 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 :-)
+
+
+Q: Durante il download di un file con Internet Explorer
+   mi dice "Non posso aprire il sito Internet". 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:
+=======================
+
+Q: 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
+   http://gpl.univ-avignon.fr/filez/
+   http://freshmeat.net/projects/eventh/
+   http://www.schaarwaechter.de/sp/projekte/dateiaustausch.html (Solo in Tedesco!)
+   il quale implementa uno scambio file come pura CGI, ma ha il limite dei 2 GB.
+
+
+Q: 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).
+
+
+Q: F*EX e' troppo complicato!
+   Mi serve qualcosa di piu' semplice.
+
+A: Provate http://www.home.unix-ag.org/simon/woof.html
+
+
+Q: 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
+   framstag@rus.uni-stuttgart.de
+
+
+Q: 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!)
+
+
+Q: Ho bisogno di maggior sicurezza! Come posso abilitare HTTPS??
+
+A: Leggete la documentazione doc/SSL ed anche cercate "fop_auth" in doc/concept
+
+
+Q: 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.
+
+
+Q: F*EX e' troppo complicato per i miei utenti.
+   Ho bisogno di una form semplificata.
+
+A: Guardate il file htdocs/fup_template.html
+
+
+Q: 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
+
+
+Q: Posso avere una versione localizzata
+   nella mia lingua??
+
+A: Con il vostro aiuto, si'. Per favore contattate l'autore a
+   framstag@rus.uni-stuttgart.de
+
+
+Domande varie:
+==============
+
+Q: F*EX e' ottimo! Posso lavorare con
+   il team di sviluppo?
+   Che cosa c'e' bisogno di fare?
+
+A: Contattate l'autore framstag@rus.uni-stuttgart.de
+   Funzionalita' richieste:
+
+     - Un plug-in F*EX per thunderbird o outlook
+     - Supporto in altre lingue
+
+
diff --git a/locale/italian/htdocs/index.html b/locale/italian/htdocs/index.html new file mode 100644 index 0000000..43170ca --- /dev/null +++ b/locale/italian/htdocs/index.html @@ -0,0 +1,128 @@ + +F*EX - File EXchange + +
+

F*EX - Frams' Fast File EXchange

+ +
+

+F*EX (Frams' Fast File EXchange) e' un servizio per spedire grossi (grandi, enormi, giganti, ...) file da un utente A ad un utente B. +

+Il mittente carica il file nel server F*EX usando una +form WWW di carico mentre al destinatario viene recapitata una e-mail di notifica con un link per effettuare il download. +

+ +Direte: +

+ Per quale motivo ho bisogno di un altro servizio per effettuare il trasferimento dei files?!
+ Per questo ho gia' la posta elettronica(e-mail), l'ftp, l'ssh ed anche il comando sendfile! +
+

+Io dico: +

+ Nonostante tutto hai bisogno di F*EX :-) +
+

+ +Supponiamo, per esempio, che tu voglia spedire ai tuoi amici il video delle tue ultime vacanze (1 GB). Hai le seguenti possibilita' (e problemi):

+ +

    +
  • spedire un DVD con il servizio postale

    + Fuori discussione - viviamo nell'anno + <> + dopo l'invenzione di Internet! + Spedire supporti hardware e' da trogloditi. +

    +

  • usare la posta elettronica (e-mail)

    + 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. +

    +

  • uucp

    + Perche' non usare la scrittura cuneiforme su una lastra di pietra? +

    +

  • ssh

    + Avete la password dei vostri amici oppure siete disposti a dargli la vostra? + - solo per trasferire un file? +

    +

  • ftp

    +

      +
    • Quale server ftp usare? +
    • Quale utente usare, quale password? +
    • Non vi dispiace mandare password e files non crittografati su Internet (insicura per definizione)? +
    • Il vostro proxy supporta l'ftp passivo? +
    • In caso di ftp anonimo: +
        +
      • Permette di caricare file da 1 GB? +
      • Permette di cancellare il file caricato subito dopo? +
      • Chi altri puo' fare il download del vostro file? +
      • Chi altri puo' cancellare il vostro file? +
      • 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.
        + In poche parole: una rottura di scatole. +
      +
    +

    +

  • http

    +

      +
    • Non esiste un programma di default - deve essere costruito. +
    • Non esiste l'auto-notifica. +
    • Non esiste l'auto-cancellazione. +
    • Pochissimi server HTTP possono trattare file piu' grandi di 2 GB. +
    +

    +

  • sendfile

    +

      +
    • Avete UNIX ed avete sendfile installato? +
    • Il vostro destinatario ha UNIX ed ha sendfile installato? +
    • Sicuri che ne' voi, ne' il vostro destinatario avete un firewall che blocca la porta 487? +
    +

    +

  • servizi commerciali come DropLoad, ALLPeers, YouSendIt, eccetera

    +

      +
    • Quale limite hanno (gran parte: < 2 GB)? +
    • Cosa dire di sicurezza e privacy: i vostri file saranno salvi e sicuri? +
    • Sono basati su servizi open source o su proprietari? +
    • Sono accessibili con qualsiasi browser oppure hanno bisogno di java, active-X, flash o qualche altro plug-in? +
    • Sopravviveranno per piu' di qualche mese?
      + (Per esempio servizi quali DropLoad, ALLPeers e drop.io hanno gia' terminato la loro attivita') +
    +

    +

+

+Se avete risposto "no" ad almeno ad una delle domande, avete bisogno di +F*EX. +

+

Principali caratteristiche di F*EX

+

+

    +
  • trasferimento file di dimensioni virtualmente illimitate. +
  • 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. +
  • funzioni RESEND e REGET per riprendere l'esecuzione dopo un blocco della connessione all'ultimo byte. +
  • auto-notifica del destinatario. +
  • auto-cancellazione dopo il download. +
  • auto-cancellazione dopo un periodo di 5 giorni (per default). +
  • gli utenti "full" possono creare "sottoutenti" o utenti "limitati" i quali possono spedire solo a questo server. +
  • gli utenti "full" possono creare gruppi in analogia a quanto succede per le liste di distribuzione, solo che viene fatto per i file. +
  • manutenzione semplice: non e' necessario l'intervento dell'amministratore per la creazione di nuovi utenti F*EX. +
  • l'invio di file a piu' destinatari occupa nel server solo lo spazio di una sola spedizione. +
  • F*EX e' un web-service di tipo HTTP e non ha bisogno di "tunnels" nel firewall. +
  • sono supportati anche i flussi (stream). +
  • per gli utenti che conoscono UNIX, ci sono gli script-shell fexsend e fexget + che evitano l'uso di un browser-WEB. +
  • i protocolli ed il codice sorgente sono liberamente disponibili (Perl Artistic). +
+ +

Parliamo di SEX

+F*EX ha un compagno: Stream EXchange (SEX).

+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. +

+L'autenticazione e' la stessa utilizzata da F*EX. + +

Altre domande?

+Vedi le FAQ + +

+

contattate: fexmaster
+ + + diff --git a/locale/italian/lang.html b/locale/italian/lang.html new file mode 100644 index 0000000..acb1394 --- /dev/null +++ b/locale/italian/lang.html @@ -0,0 +1 @@ +italiano diff --git a/locale/italian/lib/fup.pl b/locale/italian/lib/fup.pl new file mode 100644 index 0000000..7c44cd4 --- /dev/null +++ b/locale/italian/lib/fup.pl @@ -0,0 +1,35 @@ +# configurazione per F*EX CGI fup + +$info_1 = $info_login = <

+F*EX (File EXchange) +e' un servizio per spedire grossi (grandi, enormi, giganti, ...) file. +

+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.
+Dopo il download o dopo $keep_default giorni, il server cancella il file. +F*EX non e' un sistema di archiviazione! +

+Guardate anche domande e risposte (Q&A). +


+

+ $ENV{SERVER_ADMIN}
+
+EOD + +$info_2 = <

+Dopo il caricamento del file vedrete una barra di avanzamento +(bisogna avere javascript abilitato ed i popup abilitati). +

+NOTA: Parte dei browser-WEB non possono caricare file > 2 GB!
+Se il tuo file e' piu' grande devi usare un client F*EX particolare.
+Hai bisogno anche di un tool per recuperare i download interrotti. Il tuo browser-WEB non puo' farlo. +

+Attenzione: alcuni proxy HTTP come privoxy ritardano lo stattttto della barra di avanzamento!
+Potresti voler disabilitare il proxying $ENV{SERVER_NAME} se ti capita di incorrere in questo problema. +

+Utenti Firefox: non digitare [ESC] perche' questo interrompera' il caricamento! +

+Vedere anche FAQ. +EOD diff --git a/locale/italian/lib/reactivation.txt b/locale/italian/lib/reactivation.txt new file mode 100644 index 0000000..53d5052 --- /dev/null +++ b/locale/italian/lib/reactivation.txt @@ -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 index 0000000..31e61dc --- /dev/null +++ b/locale/spanish/htdocs/FAQ.html @@ -0,0 +1,254 @@ + +

Este documento está obsoleto. Por favor, use + FAQ en inglés.

+
+Meta preguntas:
+===============
+
+P: ¿Por qué el nombre de F*EX y no uno más corto?
+
+R: En el momento de la publicación había ya un programa llamado "FEX" activo 
+   (más viejo) freshmeat.net.
+
+
+P: ¿Por qué no emplear un servicio comercial
+   DropLoad, ALLPeers, YouSendIt, etc?
+   
+R: Tienen un límite de 2GB incluso ó menos.
+   Su estado de privacidad y seguridad es desconocido.
+   No están basados en software abierto.
+   No existe ningún cliente UNIX (CLI) para ellos.
+   Necesitan java, active-X, flash u otros plugins endemeniados.
+   Se desconoce cuanto durarán - DropLoad y ALLPeers
+   ha finalizado sus asuntos.
+
+
+P: ¿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: ¿Dónde puedo conseguir los fuentes de F*EX?
+
+R: http://fex.rus.uni-stuttgart.de/fex.html
+
+
+P: ¿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: ¿Que es DNS y smtp?
+
+R: Que no instale F*EX. Es allende de su horizonte.
+
+
+P: ¿Quién es el autor?
+
+A: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
+
+
+P: ¿Qué licencia tiene F*EX?
+
+R: Software libre Perl Artistic, vea http://fex.rus.uni-stuttgart.de/doc/Licence
+
+
+P: ¿Hay una lista de correo de F*EX?
+
+A: https://listserv.uni-stuttgart.de/mailman/listinfo/fex
+
+
+P: ¿Dónde puede conseguir apoyo comercial para F*EX?
+
+R: Contacte con fex@nepustil.net http://www.nepustil.net/
+
+
+P: ¡Tengo más/otras preguntas de las que aparecen en este documento!
+
+R: Pregunte al autor <framstag@rus.uni-stuttgart.de>
+
+
+Preguntas de los usuarios:
+==========================
+
+P: ¿Qué es el "auth-ID"?
+
+R: El auth-ID es una identificación interna que autentica al usuario.
+   La primera vez es generado por el administrador o por el proceso de registro
+   automático y pude modificarlo más tarde. Piense en él como
+   una especie de clave poco segura.
+
+
+P: ¿Puedo usar un proxy HTTP?
+
+R: Sí
+
+
+P: He subido un fichero ENORME pero me he confundido al escribir la dirección 
+   de correo electrónico del destinatario.
+   Acabo de recibir un correo de error de rebote. 
+   ¿Tengo que volver a subir el fichero ENORME?
+
+R: No, no es necesario. Puede redirigir el fichero con
+   http://$HTTP_HOST$/rup
+
+
+P: He subido un fichero ENORME pero olvidé poner a un destinatario.
+   ¿Tengo que volver a subir el fichero ENORME?
+
+R: No, no es necesario. Puede reenviar una copia del fichero con
+   http://$HTTP_HOST$/foc
+
+
+P: ¿¡No puede subir ficheros > 2 GB con mi navegador web!?
+
+R: Todos los navegadores web que conozco tienen errores en su implementación
+   de HTML-FORM. El límite en la mayoría es de 2 GB, algunas veces 4 GB.
+   Tiene que usar un cliente especial de F*EX para subir ficheros > 2 GB, vea
+   http://$HTTP_HOST$/tools.html
+
+
+P: Mi descarga abortó antes de que acabara. ¿Puedo continuar la descarga?
+
+R: F*EX soporta la continuación de la descarga, pero su cliente debe soportar
+   también esta característica. Firefox, por ejemplo, no tiene esta 
+   característica HTTP, necesita otro cliente como opera, wget o fexget
+
+
+P: Mi subida abortó antes de que finalizara. ¿Puedo continuar la subida?
+
+R: F*EX soporta la continuación de la subida, pero su cliente también debe 
+   soportarla. Ningún navegador web tiene esta característica, necesita un 
+   cliente de F*EX especial como fexsend, schwuppdiwupp o F*IX
+   Vea http://$HTTP_HOST$/tools.html
+
+
+P: Cuando pulso [ESC] en firefox la subida se cancela. ¿Por qué?
+
+R: Esta es una característica de firefox: ESC finaliza la operación en curso.
+   Solución sencilla: no pulse ESC en Firefox
+   Solución compleja: pida a los desarrolladores de Firefox que añadan una 
+                      configuración del teclado
+
+
+P: Enviar como usario F*EX es fácil, pero ¿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ás de una vez, especialmente cuando
+   lo repito rápidamente. ¿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á disponible todavía.  
+   Este es necesario porque algunos "administradores de descargas" estú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ía
+
+R: Los usuario normales (use los fuentes, Luke!) pueden establecer una bandera
+   de "no borrar tras la descarga"
+
+
+P: El tiempo por defecto para mantener el fichero en el sistema es desmasiado 
+   corto para mí, necesito más. ¿Cómo puedo cambiarlo?
+
+R: Use fexsend, pregunte a su fexmaster o lea el código fuente
+
+
+P: No puede descargar ficheros con Internet Explorer, me dice "No puedo
+   abrir el sistio de Internet". ¿Qué deberí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. ¿Existe una versión CGI pura de F*EX que funcione con un
+   servidor web apache?
+
+R: F*EX está íntimamente unido a fexsrv por varias razones (prestaciones,
+   limitación en el tamaño del fichero, el concepto de sesión, etc.) y no 
+   puede ejecutarse como CGI desde apache.
+   Pero puede echar un vistazo a
+   
+   http://gpl.univ-avignon.fr/filez/
+   http://freshmeat.net/projects/eventh/
+
+   que implementa un intercambio de ficheros con CGIs puros, pero ambos están 
+   limitados a 2 GB.
+   
+
+P: ¡F*EX no funciona en absoluto! 
+   ¡No puedo conectarme a él con mi navegador web!
+
+R: Compruebe el encaminamiento, los filtros IP y la configuración del 
+   cortafuegos. Compruebe también si su xinetd está enlazado con tcp-wrapper 
+   y configúrelo correctamente (hosts.allow).
+
+   F*EX necesita el puerto 80/tcp (HTTP) y opcionalmente el puerto 443/tcp
+   (HTTPS).
+
+
+P: ¡F*EX es muy complicado! Necesito algo más sencillo.
+
+R: Pruebe http://www.home.unix-ag.org/simon/woof.html
+
+
+P: ¿Como puedo integrar F*EX la administración de usuario existente de mi 
+   sitio?
+
+R: F*EX tiene varios módulos de autenticación:
+   local, RADIUS, LDAP, mailman y POP
+   Para los últimos 4 póngase en contacto con el autor por favor.
+   
+
+P: Quiero que todos mis usuarios locales use F*EX automáticamente. 
+   ¿Cómo puedo hacerlo?
+
+R: Dé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);
+
+   ¡Desde luego tiene que añadir su hosts/redes locales reales!
+
+
+P: ¡Necesito más seguridad! ¿Cómo activo HTTPS?
+
+R: Lea doc/SSL y mire busque también "fop_auth" e doc/concept
+
+
+P: ¡Necesito que tenga una imagen corporativa. ¿Como configuro F*EX para que 
+   la tenga?
+
+R: Vea la variable @H1_extra en fex.ph y puede añadir htdocs/header.html
+
+R: Vea fup_template.html, modifíquela según sus necesidades y usela como 
+   página de inicio
+
+
+Otras preguntas:
+================
+
+P: ¡F*EX es estupendo! ¿Puedo unirme al equipo de desarrollo? 
+   ¿Qué hay que hacer?
+
+R: Contacte con el autor
+
+   Características no presente:
+
+   - otro cliente de subida que se integre mejor en Windows
+     arrastrar y soltar, libro de direcciones de Outlook, etc.
+
diff --git a/locale/spanish/htdocs/index.html b/locale/spanish/htdocs/index.html new file mode 100644 index 0000000..da71e28 --- /dev/null +++ b/locale/spanish/htdocs/index.html @@ -0,0 +1,136 @@ + +F*EX - File EXchange + +
+

F*EX - Frams' Fast File EXchange

+ +
+

+F*EX (Frams' Fast File EXchange) es un servicio para enviar ficheros (grandes, enormes, +gigantes, ...) de un usuario A a otro B. +

+El remitente sube el fichero al servidor F*EX usando +el formulario de subida WWW y el destinatario +automáticamente recibe una notifiación por correo electrónico con la URL de desacarga. +

+ +Usted dirá: +

+ ¡¿Por qué necesito de otro servicio de transferencia de ficheros?!
+ ¡Tengo el correo electrónico, ftp, ssh e incluso sendfile! +
+

+Yo le respondo: +

+ Aun así necesita F*EX :-) +
+

+ +Por ejemplo, quiere enviar a un amigo su último video de las vacaciones (1 GB). +Tiene las siguientes posibilidades (y problemas):

+ +

    +
  • enviar un DVD por correo postal

    + Sin comentarios - ¡vivimos en el año + <> + tras la invención de internet! + Enviar DVDs/CDs es para los abuelos. +

    +

  • usando el correo electrónico

    + La mayoría de los servidores de correo tienen una límite de 10 MB por mensaje de correo electrónico + y una cuota de almacenamiento de uno 100 MB por usuario e incluso menos. +

    +

  • uucp

    + ¿Por qué no escritura cuneiforme sobre planchas de piedra? +

    +

  • ssh

    + ¿Tiene la contraseña de su amigo o está deseando darle la suya tan sólo para la transferencia de un fichero? +

    +

  • ftp

    +

      +
    • Usando ¿qué servidor FTP? +
    • Usando ¿qué cuenta y con qué contraseña? +
    • ¿No le importa enviar contraseñas sin cifrar y ficheros a través de internet que no es seguro? +
    • ¿Su proxy soporta ftp pasivo? +
    • En caso de usar ftp anónimo: +
        +
      • ¿Permite subi 1 GB? +
      • ¿Permite borrar los ficheros subidos después? +
      • ¿Quién más puede descargar su fichero? +
      • ¿Quién más puede borrar su fichero? +
      • Tiene que enviarle a su amigo la URL de descarga, él tiene que + informarle de que la descarga ha finalizado sin problemas y + tiene que borrar el fichero después.
        + En resumen: un grano en el culo. +
      +
    +

    +

  • http

    +

      +
    • No hay una administración de ficheros subidos y usuarios - hay que programarla +
    • No hay autonotificación +
    • No hay autoborrado +
    • Muy pocos servidores http pueden manejar ficheros de más de 2 GB +
    +

    +

  • sendfile

    +

      +
    • ¿Tiene UNIX y tiene sendfile instalado? +
    • ¿Su destinatario tiene UNIX y tiene sendfile instalado? +
    • ¿Ni usted ni su destinatario tiene un cortafuegos que bloquee el puerto 487? +
    +

    +

  • servicios comerciales como DropLoad, ALLPeers, YouSendIt, etc.

    +

      +
    • ¿Qué limite tienen (la mayoría: < 2 GB)? +
    • ¿Qué pasa con la seguridad y la privacidad: estarán sus ficheros a salvo? +
    • ¿Están basados en software abierto o propietario? +
    • ¿Son accesible con cualquier navegador o necesita java, active-X, flash u otro plugins? +
    • ¿Sobrevirá más de unos cuantos meses?
      + (DropLoad y ALLPeers ya han finalizado su actividad) +
    +

    +

+

+Si ha contestado sólo una vez "no" a las preguntas anteriores, entonces necesita +F*EX. +

+

Principales características de F*EX

+

+

    +
  • transferencia de ficheros de tamaño virtualmente ilimitado +
  • el destinatario y el remitente solo necesitan un programa de correo electrónico y un navegador web - + de cualquier tipo, no necesitan instalar ningún software +
  • RESEND y REGET para continuar las descargas interrumpidas por fallos en el enlace +
  • autonotificación al destinatario +
  • autoborrado tras la descarga +
  • autoborrado tras la fecha de expiración (por defecto: 5 días) +
  • los usuarios normales pueden crear subusuarios, que pueden enviar solo al usuario normal que lo definió +
  • los usuarios normales puede crear grupos, una anología de las listas de correo, pero para ficheros +
  • sin mantenimiento: no es necesaria la intervención del administrador más allá de crear la cuentas en F*EX +
  • para enviar a múltiples destinatarios solo es necesario subir el fichero una vez +
  • F*EX es un servicio web HTTP y no necesita que se creen túneles en los cortafuegos +
  • también soporta streams +
  • los usuarios UNIX reales, puede usar los programas para el shell fexsend y fexget para + evitar el uso del navegador web +
  • el protocolo y código fuente están disponibles (Perl Artistic) +
+ +

Hablemos de SEX

+F*EX tiene un acompañante: Stream EXchange (SEX).

+Puede imaginar SEX como si fueran tubería de UNIX a través de la red con un relay en medio. +Esto puede ser útil para entubar datos del usuario A a otro B, donde A y B no pueden +establecer una conexión directa, per ambos puede conectar por HTTP a servidor SEX. +Las herramientas del shell sexsend y sexget permiten integrarlo fácilmente entre +el conjunto de herramientas de UNIX. +

+La autenticación es igual que en F*EX. + +

¿Todavía tiene más preguntas?

+Vea la FAQ + +

+

contacto: Administrador de F*EX
+ + + diff --git a/locale/spanish/lang.html b/locale/spanish/lang.html new file mode 100644 index 0000000..49201c4 --- /dev/null +++ b/locale/spanish/lang.html @@ -0,0 +1 @@ +español diff --git a/locale/spanish/lib/fup.pl b/locale/spanish/lib/fup.pl new file mode 100644 index 0000000..9367991 --- /dev/null +++ b/locale/spanish/lib/fup.pl @@ -0,0 +1,35 @@ +# config for F*EX CGI fup + +$info_1 = $info_login = <

+F*EX (File EXchange) +es un servicio para enviar ficheros (grandes, enormes, gigantes, ...). +

+El remitente (usted) sube el fichero al servidor F*EX y el destinatario recibe automáticamente +una notificación por correo electrónico con una URL de descarga.
+Tras la descarga o tras $keep_default días el servidor borra el fichero. +¡F*EX no es un archivador! +

+Vea también preguntas y respuestas. +


+

+ $ENV{SERVER_ADMIN}
+
+EOD + +$info_2 = <

+Tras pulsar el botón de enviar verá una barra de progreso de la subida +(si tiene javascript activado y permite las ventanas emergentes) +

+NOTE: ¡La mayoría de los navegadores web no pueden subir ficheros > 2 GB!
+Si su fichero es más grande tiene que usar un cliente de F*EX especial.
+También necesita uno para poder continuar con una subida interrumpida. Su navegador no puede hacerlo. +

+Aviso: ¡algunos proxys HTTP como privoy retrasan la barra de progreso de la subida!
+Quizá quiera desabilitar el proxy para $ENV{SERVER_NAME} si se encuentra con problemas. +

+Usuarios de firefox: ¡no pulse [ESC] porque aborta la subida! +

+Vea también la FAQ. +EOD diff --git a/locale/spanish/lib/reactivation.txt b/locale/spanish/lib/reactivation.txt new file mode 100644 index 0000000..f469da1 --- /dev/null +++ b/locale/spanish/lib/reactivation.txt @@ -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 index 0000000..041f4b7 --- /dev/null +++ b/locale/translate @@ -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 index 0000000..1e31a14 --- /dev/null +++ b/locale/translations @@ -0,0 +1,2852 @@ +# english: Ulli Horlacher +# german: Waldemar Bronsch +# swabian: Hans-Georg Bickel +# spanish: Francisco Ruiz +# galician: Anton Meixome +# italian: Vanni Piagno , +# czech: Michal Simunek +# french: Jean-Baptiste Denis + +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ó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ück zur F*EX-Bedienungssteuerung +Zrigg zur Bedienongsschdeuerong +Volver al control de operaciones de F*EX +Volver ao control de operació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ó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óns +controllo delle operazioni +Řízení provozu +gestion + +for user +Fü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á 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ío (usada) +cota de remitente (usada) +quota mittente (in uso) +limit pro odesilatele (využito) +quota expéditeur (utilisé) + +recipient quota (used): +Empfänger Speicherplatz (benutzt): +Empfänger Platz zom Schbeichra (benutzt): +quota de recepció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 in F*EX spool +Zeige die Liste aller empfangenen Dateien +Zeig mol a Lischde von älle empfangene Dateia +Ver la lista de todos los ficheros recibidos en la cola de F*EX +Ver a lista de todos os ficheiros recibidos na cola de F*EX +Richiama una lista di tutti i tuoi file ricevuti nello spool di F*EX +Načíst seznam vÅ¡ech souborů čekajících ve frontě +Récupérer une liste de tous les fichiers que vous avez reçus + +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 you already have uploaded to another recipient +Eine Kopie der Datei weiterleiten, welche Sie bereits für einen anderen Empfänger hochgeladen haben +A Kobie von der Datei weiderloide, die Du scho fier en andra Empfänger nuffglade hosch +Reenviar una copia de un fichero que ha subido a otro destinatario +Reenviar unha copia dun ficheiro que cargou a outro destinatario +Inoltra una copia di un file che hai gia' caricato per un altro destinatario +Přidat příjemce k již nahranému souboru +Reexpédier une copie d'un fichier que vous avez déja uploadé à un autre destinataire + +Redirect files you have uploaded to a wrong or misspelled recipient +Leite Dateien weiter, die an eine falsche oder ungültige E-Mail Adresse gegangen sind +Dateia weiderloide, die an a falsche oder ogildige E-Mail-Adress gange send +Redirigir ficheros que ha subido y enviado a un destinatario equivocado o mal escrito +Redirixir ficheiros que cargou e enviou a un destinatario equivocado ou mal escrito +Inoltra file che hai caricato ad un destinatario sbagliato o di cui hai sbagliato l'indirizzo +Přesměrovat nahrané soubory pro nesprávného či chybně uvedeného příjemce +Rediriger un fichier 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ä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ón de correo electró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ür einen Upload +bloss fir oimal Nufflada +por só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ó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 register yourself +Sie können sich selbst registrieren +Du kosch De selber regischdriera +Puede autoregistrarse +Podo autorexistrarse +Puoi registrarti +Můžete se zaregistrovat +Vous pouvez vous 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ía una cuenta en F*EX +se non ten aí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 anonymous upload +Sie können auch anonymen Upload verwenden +Du kosch au anonymes Nufflada nemma +Se tambien puede usar anonymous upload +Tamén pode usar o envío anónimo +Potresti anhe utilizzare anonymous upload +Můžete také využít anonymnínahrávání +Vous pouvez aussi utiliser l'upload anonyme + +You may also use simple upload +Sie können auch vereinfachten Upload verwenden +Du kosch au oifachs Nufflada nemma +Puede tambien usar subir simplificado +You may also use simple upload +Potresti anche usare caricamento semplice +Můžete také použít jednoduché nahrávání. +Vous pouvez également utiliser l'upload simple + +$file already exists for +$file existiert bereits für +$file gibts scho fir +$file ya existe para +xa existe $file para +$file esiste giàper +$file již existuje pro +Le fichier $file existe déjàpour + +$file already exists - purge it?! +$file existiert bereits - entfernen?! +$file gibts scho - wegschmeissa?! +$file ya existe - ¡¿Purgarlo?! +xa existe $file - purgalo?! +$file esiste già - lo vuoi eliminare?! +$file již existuje - odstranit jej?! +Le fichier $file existe déjà - Effacer?! + +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 to be sent with notification e-mail +Änderung des Disclaimers der in der Benachrichtigungs E-Mail mitversendet wird +Änder dr Disclaimer von dr Benochrichtigongs E-Mail +Cambiar renuncia de email enviado con emails de notificación +Change the disclaimer to be sent with notification e-mail +Cambia il disclaimer affinchè venga spedito assieme all'e-mail di notifica +Změnit odesílání zřeknutí se odpovědnosti u správy s upozorněním +Changer la clause de non-responsabilité à envoyer avec le message de notification + +Change your auth-ID to +Ändern Sie Ihre auth-ID in +Ändr die auth-ID noch +Cambie su auth-ID en +Cambie o seu auth-ID en +Cambia il tuo auth-ID in +Změnit si ověřovací ID auth-ID na +Changer votre auth-ID pour + +remember it +merken +aber ned vergessa, gell +recordar +recórdeo +ricordati +zapamatovat +souvenez-vous en + +Edit your address book +Ihr Adressbuch editieren +Dei Adressbüchle bearbeide +Edite su libro de direcciones +Edite a súa axenda de enderezos +Edita la tua rubrica +Upravit adresář +Éditez votre carnet d'adresses + +Get detailed notification e-mails (current setting: brief) +Ausfürliche Benachrichtigungs E-Mails bekommen (aktuelle Einstellung: kurz) +Langs Gschwätz en de Benochrichtigongs E-Mails (aktuelle Eischtellong: kurz) +Recibir detallado e-mails de notificación (configuración actual: corto) +Obter unha detallada notificación para os correos (axuste actual: breve) +Avere dettagliata di notifica e-mail (impostazione attuale: breve) +Dostávat podrobné e-maily s oznámením (současné nastavení: zkrácené) +Recevoir des emails de notifications détaillés (configuration courante: simple) + +Get brief notification e-mails (current setting: detailed) +Kurze Benachrichtigungs E-Mails bekommen (aktuelle Einstellung: detailliert) +Machs kurz en de Benochrichtigongs E-Mails (aktuelle Eischtellong: langs Gschwätz) +Recibir corto e-mails de notificación (configuración actual: detallado) +Obter unha breve notificación para os correos (axuste actual: detallado) +Avere breve notifica e-mail (impostazione attuale: dettagliata) +Dostávat e-maily s upozorněním v zkráceném formátu (současné nastavení: detailed) +Recevoir des emails de notifications simple (configuration courante: détaillé) + +Get reminder notification e-mails (current setting: no reminders) +Erinnerung E-Mails erhalten (aktuelle Einstellung: keine) +Drodenka E-Mails erhalta (aktuelle Eischtellong: koine) +Recibir emails de recuerdo (configuración actual: ningunos) +Get reminder notification e-mails (current setting: no reminders) +Impostaun Promemoria per l'e-mail di notifica (Impostazione attuale: nessun promemoria) +Odesílat zprávy s připomenutím (současné nastavení: žádná upozornění) +Recevoir les rappels par e-mail (configuration actuelle: pas de rappels) + +Get no reminder notification e-mails (current setting: send reminders) +Keine Erinnerung E-Mails erhalten (aktuelle Einstellung: Erinnerung E-Mails werden verschickt) +Koine Drodenka E-Mails erhalta (aktuelle Eischtellong: Drodenka E-Mails werdad verschickt) +No recibir emails de recuerdo (configuración actual: Se eniva emails de recuerdo) +Get no reminder notification e-mails (current setting: send reminders) +Imposta nessun promemoria per l'e-mail di notifica (Impostazione attuale: spedisci promemoria) +Neodesílat žádná připomenutí (současné nastavení: odeslat upozornění) +Ne pas recevoir les rappels par e-mail (configuration actuelle: rappels envoyés) + +Save files after download (current setting: display +Speichere Dateien nach dem download (aktuelle Einstellung: anzeigen +Schpeicher die Dateia nochm Ronderlada (aktuelle Eischtellong: ozeiga +Save downloads (current setting: display +Save downloads (current setting: display +Save downloads (current setting: display +Save downloads (current setting: display +Save downloads (current setting: display + +Display files when downloading with web browser (current setting: save +Anzeige von Dateien direkt beim download (aktuelle Einstellung: abspeichern +Ozeiga von Dateia beim Ronderlada (aktuelle Eischtellong: abschpeichra +Display downloads (current setting: save +Display downloads (current setting: save +Display downloads (current setting: save +Display downloads (current setting: save +Display downloads (current setting: save + +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ä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ö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ückgesetzt +Dr E-Mail Ohä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ä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ü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ä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ück zu fup (Upload Seite) +Zrigg zu fup (Nufflad-Seite) +Volver a fup (p´gina de carga) +Volver a fup (p´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 + +

to +

an +

an +

vez +

para +

per +

pro +

pour + +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ón del usuario +Produciuse un ERRO na configuació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ón del usuario F*EX +configuració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ónico informativo se ha enviado a su subusuario +Un correo electró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 $otuser has been created +Eine einmal gültige upload URL für $otuser wurde angelegt +A oimol giltig nufflad URL fir $otuser isch oglegt worda +Un URL para subir una vez $otuser ha sido creado +Un enderezo URL de único uso para $otuser acaba de se crear +E' stato creato un URL di caricamento utilizzabile una sola volta per l'utente $otuser +Jednorázová URL pro nahrání pro $otuser byla vytvořena +Une URL d'upload à usage unique pour $otuser 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ó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ängscht als Vollbenutzr bei F*EX regischdriert +ya es un usuario normal registrado en F*EX +xa é 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ü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ónico informativo se ha enviado a +Enviouse un correo electró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üchle bearbeita +Editar el libro de direcciones +Editar a axenda de enderezos +Edita la rubrica +Upravit adresář +Éditez le carnet d'adresse + +Entry:aliase-mail address# optional comment +Eintrag:AliasE-Mail Adresse# optionaler Kommentar +Eidrag:AliasE-Mail Adress# Kommendar (kosch macha oddr bleiba lassa) +Entrada:aliasdirección de correo electrónico# comentario opcional +Entrada:aliasenderezo de correo electrónico# comentario opcional +Entrata:aliasindirizzo e-mail# commento opzionale +Položka:aliase-mailová adresa volitelný komentář +Entrée:aliasadresse e-mail# 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önnen diese Alias-Namen als F*EX Empfänger-Adressen auf +Du kosch selle Alias-Nome als F*EX Empfä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 + +'fup' +'fup benutzen.' +'fup nemme.' +'fup' +'fup' +'fup' +'fup' +'fup' + +Alternatively you can fex a file ADDRESS_BOOK to yourself +Alternativ können Sie die Datei ADDRESS_BOOK an sich selbst +Du kosch au dui Datei ADDRESS_BOOK an De selber +También puede enviar vía fex un fichero ADDRESS_BOOK a si mismo +Tamén pode enviar ví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ält +fexe, wo Deine Alias-Definitione dren send +que contenga sus definiciones de alias +que conteña as súas definicións de alias +contiene le tue definizioni degli alias +obsahuje definované aliasy +contenant vos alias + +aliase-mail addressoptionscomment +AliasE-Mail AdresseOptionenKommentar +AliasE-Mail AdresseOptionenKommendar +aliasdirección de correo electrónicoopcionescomentar +aliasenderezo de correo electrónicoopciónscomentar +aliasindirizzo e-mailopzionicommento +aliase-mailová adresavolbykomentář +aliasadresse e-mailoptionscommentaire + +back to fup (F*EX upload) +zurü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üssen diese URLs Ihren Subusers geben, damit sie Dateien an Sie fexen kö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ía F*EX +Ten que dar estes URL aos seus subusuarios para que poidan enviarlle ficheiros ví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ón de correo electrónico del subusuario para enviarle un +Ou prema sobre a ligazó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ónico informativo del servidor de F*EX +correo electrónico informativo ví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ú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ändert wird!) +(Merk Dir obedingd Dei auth-ID wenn Du se gä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ä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ín ví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:<e-mail address>:<encryption-ID> +Diese Liste besteht aus Einträgen im Format:<E-Mail Adresse>:<encryption-ID> +Sell Lischt beschdoht aus Eidragonge vom Format:<E-Mail Adress>:<encryption-ID> +Esta lista tiene las entradas en formato:<e-mail address>:<encryption-ID> +Esta lista ten entradas co formato :<enderezo de correo>:<ID-cifrado> +Questa lista ha i contenuti nel formato: <e-mail address>:<encryption-ID> +Položky seznamu jsou ve formátu:<e-mailová adresa>:<Å¡ifrovacíID> +Les entrées de cette liste ont comme format:<adresse e-mail>:<encryption-ID> + +These special senders may fex files only to you! +Diese besonderen Sender können Dateien nur an Sie fexen! +Selle Absender send was Bsonders ond kenne Dateia bloß an dich fexe! +¡Estos remitentes especiales pueden enviar ficheros vía F*EX solo a usted! +Estes remitentes especiais poden enviar ficheiros vía F*EX a vostede! +Questi utenti speciali potrebbero spedire file solo a te! +Tito speciální odesilatelé mohou odesílat soubory pouze vám! +Ces expéditeurs particuliers peuvent seulement vous envoyer des fichiers + +It is not necessary to add regular fex users to your list, +Es ist nicht notwendig, reguläre FEX Benutzer zu Ihrer Liste hinzuzufügen +'S isch net nedich, daß de reguläre F*EX Benutzr uff Dei Lischde setzsch +No es necesario añadir usarios de F*EX normales a su lista, +Non é necesario engadir usuarios de F*EX normais á sú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önnen +weil dui kenned sowieso scho fexe +porque ellos ya pueden enviar vía F*EX +porque eles xa poden enviar ví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ür um eine eindeutige upload URL für diesen Subuser zu erzeugen +Die encryption-ID isch notwendig um a eindeutig upload URL für den Subuser zom macha +Hace falta una ID de codificación para generarle a este sub-usuario un inequívoco URL para subir +Fai falta un ID de codificación para xerarlle a este subusuario un inequí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ällige gewählt +Wenn Du die encryption-ID wegläscht, no wird a zufälliga gnomma +Si se omite el cifrado-ID un nombre cualquiera se utilizará +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 ähnlich, allerdings eben fü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é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ú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ás información sobre +Véxase http://$ENV{HTTP_HOST}/index.html para obter más informació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ú 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ó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ás información sobre +Véxase http://$ENV{HTTP_HOST}/ para obter más informació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ó 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ónico de notificación enviado a $notify +Enviouse un correo de notificació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:

\n
    +Fehlerhafte Adressen:

\n
    +Was send au des fier Adresse? Die send grottefalsch::

\n
    +dirección errónea:

\n
    +Os enderezos son incorrectos:

\n
    +indirizzo errato:

\n
    +chybné adresy:

\n
    +mauvaises adresses:

\n
    + +you are already in group \@$group owned by $user +Sie sind bereits in der Gruppe \@$group welche dem Benutzer $user gehört +Du bisch doch scho lä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ü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ü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ón para enviar correo electrónico de notificación a este usuario) +(prema sobre o enderezo de correo electrónico de notificació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 ä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 é 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ür die Gruppe bereitstellt, +Wenn oi Mitglied a Datei fier die reschdlich Grupp nuffläd +Cuando un miembro envía vía F*EX un fichero a esta lista, +Cando un membro envía ví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 älle andere wo in derre Grupp send, selle Datei +entonces todos los demás miembros lo recibirán +entón todos os demáis membros o recibirán +allora tutti gli altri componenti la riceveranno +obdrží jej vÅ¡ichni členové +tous les autres membres vont le recevoir + +New group name: (You MUST fill out this field!) +Neuer Name für die Gruppe: (Sie MÜSSEN dieses Feld ausfüllen!) +Neier Nome fier d' Grupp: (Des Feld MUSCH Du ausfille!) +Nuevo nombre de grupo: (¡DEBE rellenar este campo!) +Novo nome de grupo: (DEBE cubrir este campo!) +Nuovo nome del gruppo: (DEVI compilare questo campo!) +Název nové skupiny: (Toto políčko MUSÍTE vyplnit!) +Nouveau nom du groupe: (Ce champ ne peut pas être vide) + +This list has entries in format:<e-mail address> +Diese Liste besteht aus Einträge in folgendem Format haben:<E-Mail Adresse> +Die Lischde bschdohd aus Eiträg em Format:<E-Mail Adresse> +Esta lista debe tener las entradas en el formato: <dirección de correo electrónico> +Esta lista debe ter as entradas no formato: enderezo de correo electrónico> +Questa lista deve avere le righe in questo formato:<indirizzo e-mail> +Položky seznamu musí být ve formátu:<e-mailová adresa> +Cette liste doit avoir des entrées de la forme:<Adresse e-mail> + +You can name any existing e-mail address +Sie können hier eine beliebige gültige E-Mail Adresse angeben +Du kosch do a giltige E-Mail-Adress nach Deim Guschto eigebba +Puede nombrar cualquier dirección de correo electrónico +Pode nomear calquera enderezo de correo electró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öschen +Datei nochm Ronderlada löscha +borrar el fichero tras su descarga +borrar o ficheiro trala sú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öschen +Datei nochm Ronderlada net lö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ögerung löschen +Datei nochm Ronderlada erscht schpäter löscha +borrar el fichero tras su descarga con retardo +borrar o ficheiro tras a sú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ösche Datei $autodelete Tage nach dem Download +Lö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 é: +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 é: +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 é 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ä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ñecido +dkey sconosciuto +neznámý dkey +dkey inconnue + +has been deleted by %s at %s +wurde gelöscht von %s um %s +isch glöscht worde von %s um %s +ha sido borrado por %s a las %s +eliminado por %s é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öscht +$filename glöscht +$filename borrado +$filename eliminado +$filename cancellato +$filename byl smazán +$filename effacé + +$filename not deleted ($s) +$filename nicht gelöscht ($s) +$filename net glö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ä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ü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öschen< +>lö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ür Ihre andere E-Mail Adressen werden hier nicht aufgelistet! +Dateia fier Deine andre E-Mail Adressa werded dohanne net uffglischtet! +¡Los ficheros para otras direcciones de correo electrónico suyas no son listadas aquí! +Os ficheiros para outros enderezos de correo electrónico que teñ non se listan aquí! +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 überzogen +Dei Schbeicherplatz isch rappelvoll +ha excedido su cuota +sobrepasou a sú 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 ü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ú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ürfen nicht fexen an $to +Du ($from) derfsch net fexa an $to +Usted ($from) no puede enviar vía F*EX a $to +Vostede ($from) non está autorizado para enviar, ví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 $to is not a registered F*EX full or sub user +Empänger $to ist kein registrierter F*EX Voll- oder Subuser +Dr Empfä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 é 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 $to n'est pas un utilisateur ou un sous-utilisateur F*EX + +no upload key +kein upload-Einmalschlü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á +O seu cliente parece ser "$1" que é incompatíbel con F*EX e probablemente non funcionará +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änger: +Empfängr: +destinatario: +destinatario: +destinatario: +příjemce: +destinataire: + +recipient(s): +Empfänger: +Empfängr: +destinatario(s): +destinatario(s): +destinatario(i): +příjemce(i): +destinataire(s): + +recipient(s)< +Empfänger< +Empfä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ón de correo electrónico o alias +enderezo de correo electró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ählen +oddr aus Deim Adressbüchle raussuche +o seleccione de su libro de direcciones +ou seleccione da sú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änger-Liste hinzufügen +uff d' Empfängerlischte setze +añada a la lista de destinatarios +engadir á 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änger überprüfen und fortsetzen +Empfänger ieberpriafe ond weitermache +compruebe el/los destinatario(s) y continúe +comprobe o/os destinatario(s) e continú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änkter Benutzer und können nur an diese Empfänger fexen: +Du bisch a eigeschränktr Benutzr ond kosch bloss an die Empfä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ón de usuario y control de operación +>configuración de usuario e control de operación +>configurazion utente & controllo operazioni +>nastavení uživatele a řízení provozu +>configuration utilisateur et gestion + +Alternate Java client (for files > 2 GB or sending of more than one file) +Alternativer Java Client (für Dateien größer als 2 GB oder zum Senden von mehr als einer Datei) +Alternativer Java Client (fier Dateia wo größer send als 2 GB oder zum Sende von meh als oiner Datei) +Cliente java alternativo (par ficheros > 2 GB o envío de más de un fichero) +Cliente java alternativo (para ficheiros > 2 GB ou envío de más dun fichero) +Client java alternativo (per file > 2 GB o per spedizioni di piu' di un file) +Alternativní Java klient (pro soubory větší než 2 GB či pro odesílání více než jednoho souboru) +Client Java alternatif (pour les fichiers > 2 GB ou envoyer plusieurs fichiers d'un coup) + +You have to fill out this form completely to continue +Sie müssen dieses Formular komplett ausfüllen um fortzufahren +Du musch des Formular ganz ausffü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):$quota ($du) MB +Absender Quota (benutzt):$quota ($du) MB +Absendr Quota (benutzt):$quota ($du) MB +cuota de remitente (usada):$quota ($du) MB +cota de remitente (usada):$quota ($du) MB +quota utente (usata):$quota ($du) MB +limit pro odesílání (used):$quota ($du) MB +quota expéditeur (utilisé):$quota ($du) MB + +autodelete: +Automatisches Löschen: +Automadischs Lösche: +autoborrar: +autoeliminación: +autocancellazione: +automatické mazání: +suppression automatique + +keep file max $keep days, then delete it +Behalte die Datei für max. $keep Tage, dann lösche sie +Bhalt die Datei fier höchschdens $keep Tage, dann kosch se lösche +conserve el fichero un máximo de $keep días, a continuación borrelo +conserve o ficheiro un máximo de $keep días, a seguir bó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ías +$keep días +$keep giorni +$keep dnů +$keep jours + +comment: +Kommentar: +Kommendar: +comentario: +comentario: +commento: +komentář: +commentaire: + +bandwith limit +Bandbreitenbeschränkung +Bandbroidebschrä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á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ö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ás de un fichero póngalos en un archivo zip +De querer enviar más dun fichero póñ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ón de usuario & control de operaciones +configuración de usuario e control de operació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! +¡He perdido mi auth-ID! ¡Envíemelo por correo electrónico! +Perdín o meu auth-ID! ¡Envíemo por correo electró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í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üssen oben dazu das \"Absender\"-Feld ausfü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ó el fichero +non se espcificó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ó el remitente +non se especificóu o remitente +nessun mittente specificato +nebyl uveden odesilatel +aucun expéditeur spécifié + +no recipient specified +Kein Empfänger angegeben +Koin Empfängr ogebbe +no se especificó el destinatario +non se especificó 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óneo +o auth-ID especificado é incorrecto +auth-ID indicato sbagliato +bylo uvedeno chybné ověřovací ID +auth-ID erroné + +address book updated +Adressbuch aktualisiert +Adressbü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ö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ún fichero de datos +Non se recibiu ningú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? +¿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ß (Browser-Limit: 2 GB!)? +Die Datei isch zu groß (Browser-Limit: 2 GB!)? +¿Fichero demasiado grande (límite del navegador: ¡2GB!)? +Ficheiro demasiado grande (lí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ültiger Content-Length +Ungültiger Content-Length +Content-Length inválido +O Content-Length é incorrecto +Content-Length non valido +Content-Length není platné +Content-Length invalide + +not enough free space for this upload +Nicht genug freier Speicher fü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ó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ültige E-Mail Adresse +des isch koi giltige E-Mail Adress +no es una dirección de correo electrónico válido +non é un enderezo de correo electró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ößer als 2 GB +I hab koine Date kriegt - Gibt's dui Datei ieberhaupt oddr isch se größer als 2 GB +no se ha recibido ningún fichero - existe su fichero o es >2GB +non se recibiu ningún ficheiro - existe o seu ficheiro ou é >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öße unbekannt +Koi Ohnung, wie groß dui Datei isch +tamaño del fichero desconocido +tamaño do ficheiro descoñecido +dimensione file sconosciuta +neznámá velikost souboru +taille du fichier inconnue + +file size +Dateigröße +So fett isch des Doil +tamaño del fichero +tamaño do ficheiro +dimensione file +velikost souboru +taille du fichier + +No file selected +Keine Datei ausgewählt +Koi Datei ausgwä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?! +¡¿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äuft gerade +'S isch grad en Transfer am laufe +la transferencia ya está en curso +a transferencia xa está 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äuft gerade +'S wird grad was ronderglade +una descarga está ahora en curso +unha descarga está 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ü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 "$filename" to: +Eine Kopie von "$filename" weiterleiten an: +A Kopie von "$filename" weiterschicke an: +reenviada una copia de "$filename" a: +reenviada unha copia de "$filename" a: +inoltra una copia di "$filename" a: +předat kopii souboru "$filename" na: +Transmettre une copie de "$filename" à: + +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ü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ü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ür Schlü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úa nova conta F*EX é: +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 $exd is not allowed for registration +Ihre Domain $exd ist nicht zugelassen für Registrierung +Dei Domain $exd ghod ned for d'Regischtrierong +Su dominio $exd no está permitido a registrar +Your domain $exd is not allowed for registration +Il tuo dominio $exd non è permesso per la registrazione +Registrace z domény $exd není povolena +Votre domaine $exd 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án permitidos +Os rexistros desde o seu equipo ($ra) non está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ür Details +Wend De an $ENV{SERVER_ADMIN}, wenn De Näheres wisse willsch +Contacte con $ENV{SERVER_ADMIN} para obtner más detalles +Contacte con $ENV{SERVER_ADMIN} para obter má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

    +oder

    +odr

    +o

    +ou

    +o

    +nebo

    +ou

    + +you are already registered +Sie sind bereits registriert +Du bisch doch scho längscht regischdriert +ya está registrado +xa está 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üssen Ihre E-Mail Adresse eingeben +Du musch dei E-Mail Adress ogeba +hace falta introducir su dirección de correo electrónico +debe escribir o seu enderezo de correo electró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ú dirección de correo electrónico +o séu enderezo de correo electró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ón de registro de usuario F*EX +Petició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ú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 $user +Bestätigungs-E-Mail wurde gesendet an Benutzer $user +Beschdädigongs-E-Mail isch gschickt worde an Benutzr $user +Un correo electrónico de confirmación se le ha enviado a $user +Envióuselle un correo electrónico de confirmación a $user +e-mail di conferma e' stata spedita a $user +potvrzovací e-mail byl odeslán uživateli $user +Un message de confirmation a été envoyé à $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 é 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ón de F*EX +Produciuse un ERRO de redirecció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ó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ú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ón F*EX +redirección F*EX +inoltro F*EX +Přesměrování F*EX +Redirection F*EX + +old (wrong) recipient: +Alter (falscher) Empfänger: +Alder (falscher) Empfängr: +viejo (erróneo) destinatario: +antigo (incorrecto) destinatario: +vecchio (sbagliato) destinatario: +původní (chybný) příjemce: +ancien (invalide) destinataire: + +new recipient: +Neuer Empfänger: +Neier Empfä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ür $file fü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ón de las subida del fichero \"$filename\" enviado a $nto +enviada a notificació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ón a $nto/$from/$fkey falló +fallou a redirecció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ñ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ó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ún correo electrónico de notificació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 BYTES?! You are kidding +Moment... $ndata BYTES?! Das ist wohl ein Witz +Moment amole $ndata BYTES?! Wilsch me verarsche +Un momento... ¡¿$ndata BYTES?! Está bromeando +Un intreo... BYTES?! Está de broma +Ehh... $ndata BYTES?! Stai scherzando +Moment... $ndata BYTÅ®?! Děláte si legraci +Eh... $ndata BYTES?! Vous rigolez + +Using F*EX for less than +Verwendung von F*EX fü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ört +Hosch scho mol was von MIME E-Mail ghört +ha oído hablar del correo electrónico MIME +non lle será mellor usar o correo electró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ür $keep{$to} Tage gü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 $file for $to overwritten +vorhandenes $file für $to überschrieben +alts $file fir $to iberschrieba +El anterior $file para $to se ha sobreescrito +Sobrescribiuse o anterior $file para $to +vecchio file $file per $to sovrascritto +původní soubor $file pro $to byl nahrazen +$file 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 + +$file removed because you are a restricted user +$file wurde gelöscht weil Sie ein eingeschränkter Benutzer sind +$file isch wieder glöscht worda weil du a bschränkter Benutzer bisch +$file se ha borrado porque usted es un usuario restringido +$file removed because you are a restricted user +$file rimosso perchè sei un utente limitato +$file odstraněn, protože jste uživatel s omezením +$file supprimé car vous êtes un utilisateur restreint + +and recipient $to cannot receive e-mail +und Empfänger $to keine E-Mail empfangen kann +ond Empfängr $to ko koi E-Mail empfanga +y el destinatario $to no puede recibir correos electró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ü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 ü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ß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ír" +value="uscita" +value="odhlásit" +value="déconnexion" + +Using Microsoft Internet Explorer for download will probably +Die Verwendung von Microsoft Internet Explorer fü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ü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ándar de Internet (RFC 2616) +traerá problemas porque non é compatíbel co está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ámoslle +Si raccomanda +Doporučujeme +Nous recommandons + +WARNING: +WARNUNG: +OBACHT: +ADVERTIMIENTO: +ATENCIÓ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ählen Sie an "speichern unter" +klicksch hier mit dr rechta Maustascht ond wählsch "speichern unter" aus +pulse aquí con el botón derecho del ratón y seleccione "guardar como" +prema aquí co botón dereito do rató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üsselung (de)aktivieren +E-Mail Verschlü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öscht +PGP/GPG key glö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üsslt verschickt +Du kriegsch E-Mails jetzt overschlü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üsslt mit dem PGP/GPG key: +Du kriegsch E-Mails jetzt verschlü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ält keinen PGP/GPG public key fü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ählen Sie Ihre PGP/GPG public key Datei aus +Wä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öschen Sie Ihren abgelegten public key +lö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ö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é + +$from is not allowed to upload from IP $ra +$from darf nicht von IP-Adresse $ra hochladen +$from darf ned von IP-Adress $ra hochlada +$from no está permitido a subir de esta dirección IP $ra +$from is not allowed to upload from IP $ra +$from non è consentito caricarlo da IP $ra +$from z IP adresy $ra není dovoleno nahrávat soubory +$from n'est pas autorisé à uploader vers l'IP $ra + +Group $to does not exist +Gruppe $to existiert nicht +Grupp $to gibts net +Grupo $to no existe +Group $to does not exist +Il gruppo $to non esiste +Skupina $to neexistuje +Le groupe $to n'existe pas + +server runs in NOMAIL mode - groups ($to) are not allowed +Server läuft im NOMAIL Modus - Gruppen ($to) sind nicht erlaubt +Server lä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 + +$to is not a valid recipient +$to ist kein gültiger Empfänger +$to isch koi gültigr Empfängr +$to no es un destinario válido +$to is not a valid recipient +$to non è un destinatario valido +$to není platný příjemce +$to 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 index 0000000..f7ffc47 --- /dev/null +++ b/upgrade @@ -0,0 +1 @@ +install \ No newline at end of file