├── .gitignore ├── LICENSE ├── README.md ├── board.sql ├── install.sh ├── proftpd.conf ├── rm.sh └── src ├── .htaccess ├── bootstrap ├── css │ ├── bootstrap-theme.css │ ├── bootstrap-theme.min.css │ ├── bootstrap.css │ └── bootstrap.min.css ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff └── js │ ├── bootstrap.js │ └── bootstrap.min.js ├── controllers ├── action.php ├── admin.php ├── boxe.php ├── compte.php ├── connect.php ├── downloads.php ├── layout │ ├── default.php │ └── external.php ├── messagerie.php ├── rss.php ├── stats.php └── torrents.php ├── core ├── bdd.php ├── controller.php ├── core.php ├── languages │ ├── en.php │ └── fr.php └── tools.php ├── favicon.ico ├── index.php ├── models ├── Transmission.php ├── bdecode.php ├── cloud.php ├── download.php ├── rpc.php ├── rss.php ├── rssm.php ├── stats.php ├── torrent.php ├── torrents.php ├── user.php ├── xfer.php └── zip.php ├── theme ├── css │ ├── facelist.css │ ├── facelist_ie.css │ ├── farbtastic.css │ ├── glyphicons.css │ ├── jquery.pnotify.default.css │ ├── jquery.pnotify.default.icons.css │ └── style.min.css ├── fonts │ ├── glyphicons-regular.eot │ ├── glyphicons-regular.eot@ │ ├── glyphicons-regular.svg │ ├── glyphicons-regular.ttf │ └── glyphicons-regular.woff ├── images │ ├── friendly-torrent-logo.png │ ├── home.png │ ├── loader.png │ ├── login-colors.jpg │ ├── token.gif │ ├── token_hover.gif │ ├── token_selected.gif │ └── token_x.gif └── scripts │ ├── autocomplete.js │ ├── farbtastic.js │ ├── flot │ ├── jquery.flot.js │ ├── jquery.flot.pie.js │ ├── jquery.flot.resize.js │ └── jquery.flot.tooltip.js │ ├── jquery-1.8.2.min.js │ ├── jquery.autocomplete.js │ ├── jquery.cookie.js │ ├── jquery.facelist.js │ ├── jquery.hammer.min.js │ ├── jquery.pnotify.min.js │ ├── jquery.tablesorter.min.js │ ├── load.js │ ├── plupload │ └── js │ │ ├── Descr.WD3 │ │ ├── jquery.plupload.queue │ │ ├── Descr.WD3 │ │ ├── css │ │ │ ├── Descr.WD3 │ │ │ └── jquery.plupload.queue.css │ │ ├── img │ │ │ ├── Descr.WD3 │ │ │ ├── backgrounds.gif │ │ │ ├── buttons-disabled.png │ │ │ ├── buttons.png │ │ │ ├── done.gif │ │ │ ├── error.gif │ │ │ └── transp50.png │ │ └── jquery.plupload.queue.js │ │ ├── plupload.flash.swf │ │ ├── plupload.full.js │ │ └── plupload.silverlight.xap │ └── script.php ├── tmp └── band.txt ├── tornado ├── BitTornado │ ├── BT1 │ │ ├── Choker.py │ │ ├── Connecter.py │ │ ├── Downloader.py │ │ ├── DownloaderFeedback.py │ │ ├── Encrypter.py │ │ ├── FileSelector.py │ │ ├── Filter.py │ │ ├── HTTPDownloader.py │ │ ├── NatCheck.py │ │ ├── PiecePicker.py │ │ ├── Rerequester.py │ │ ├── Statistics.py │ │ ├── Storage.py │ │ ├── StorageWrapper.py │ │ ├── StreamCheck.py │ │ ├── T2T.py │ │ ├── Uploader.py │ │ ├── __init__.py │ │ ├── btformats.py │ │ ├── fakeopen.py │ │ ├── makemetafile.py │ │ └── track.py │ ├── BTcrypto.py │ ├── ConfigDir.py │ ├── ConfigReader.py │ ├── ConnChoice.py │ ├── CreateIcons.py │ ├── CurrentRateMeasure.py │ ├── HTTPHandler.py │ ├── PSYCO.py │ ├── RateLimiter.py │ ├── RateMeasure.py │ ├── RawServer.py │ ├── ServerPortHandler.py │ ├── SocketHandler.py │ ├── __init__.py │ ├── bencode.py │ ├── bitfield.py │ ├── clock.py │ ├── download_bt1.py │ ├── inifile.py │ ├── iprangeparse.py │ ├── launchmanycore.py │ ├── natpunch.py │ ├── parseargs.py │ ├── parsedir.py │ ├── piecebuffer.py │ ├── selectpoll.py │ ├── subnetparse.py │ ├── torrentlistparse.py │ └── zurllib.py ├── btmakemetafile.py ├── btshowmetainfo.py ├── tftornado.py ├── tftornado.py.save └── tornado.pyproj ├── uploadify ├── jquery.uploadify.js ├── jquery.uploadify.min.js ├── uploadify.css └── uploadify.swf └── views ├── Admin ├── editUser.php ├── index.php └── newUser.php ├── Boxe └── index.php ├── Compte └── index.php ├── Connect └── index.php ├── Downloads └── index.php ├── Rss └── index.php ├── Stats └── stats.php ├── Torrents └── index.php ├── layout ├── default.php ├── external.php └── header.php └── messagerie ├── chat.php ├── index.php └── message.php /.gitignore: -------------------------------------------------------------------------------- 1 | ### PHPSTORM ### 2 | .idea 3 | 4 | ### MAC ### 5 | .com.apple.timemachine.supported 6 | 7 | ### CONFIG FOLDER ### 8 | /src/core/config/* -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![PreviewImage](http://cclleemm.github.io/FriendlyTorrent/github-page/img/logo-mini.png) FriendlyTorrent 2 | ======= 3 | 4 | FriendlyTorrent is a web PHP script to download torrents with a beautiful an intuitive web responsive interface. 5 | Moreover it allow you to explore and share download files "in the cloud" with your friends. 6 | This software is based on [Transmission](http://www.transmissionbt.com) torrent software. 7 | 8 | This is a BETA version, maybe you can help us ? :) [TODO List](https://github.com/Cclleemm/FriendlyTorrent/wiki/TODO) 9 | 10 | ![PreviewImage](http://cclleemm.github.io/FriendlyTorrent/github-page/img/home.png) 11 | 12 | ### What's included 13 | 14 | Within the download you'll find the following directories and files. You'll see something like this: 15 | 16 | ``` 17 | bootstrap/ 18 | ├── board.sql 19 | ├── install.sh 20 | └── src/ 21 | ├── bootstrap/ 22 | ├── controllers/ 23 | ├── core/ 24 | ├── models/ 25 | ├── theme/ 26 | ├── tmp/ 27 | ├── tornado/ 28 | ├── uploadify/ 29 | ├── views/ 30 | ├── .htaccess 31 | ├── favicon.ico 32 | └── index.php 33 | ``` 34 | 35 | 36 | ## Automatic installation (Ubuntu, Debian ...) 37 | * [Download the latest release](https://github.com/Cclleemm/FriendlyTorrent/archive/master.zip). 38 | * run the install wizard with this following commands 39 | 40 | ``` 41 | sudo chmod +x ./install.sh 42 | sudo ./install.sh 43 | ``` 44 | * Enable HTACESS for the web folder (vhost file). 45 | 46 | Solution to enable HTACCESS but it's not secure : 47 | 48 | ``` 49 | sudo nano /etc/apache2/apache2.conf 50 | ``` 51 | Change Directory directive 52 | ``` 53 | 54 | Options Indexes FollowSymLinks 55 | AllowOverride None 56 | Require all granted 57 | 58 | ``` 59 | 60 | To 61 | 62 | ``` 63 | 64 | Options Indexes FollowSymLinks 65 | AllowOverride All 66 | Require all granted 67 | 68 | ``` 69 | Then, restart the apache service 70 | 71 | ``` 72 | sudo service apache2 restart 73 | ``` 74 | 75 | Go to : http://localhost/ 76 | 77 | ## Manual installation 78 | 79 | ### Dependencies 80 | You must have installed this before : 81 | * Apache2 82 | * php5 83 | * mysql-server 84 | * libapache2-mod-php5 85 | * php5-curl 86 | * php5-mysql 87 | * memcached 88 | * php5-memcache 89 | * php5-memcached 90 | * transmission 91 | * transmission-daemon 92 | * zip 93 | 94 | ### Files Import 95 | Put all files of the `src/` folder in your web folder (for example in /var/www/). 96 | 97 | Create the download folder where all your downloads will be stored (ex : /var/downloads/) 98 | 99 | Create `.transfert` folder (ex : /var/downloads/.transferts/) 100 | 101 | Create user folder (ex : /var/downloads/user-name/) 102 | 103 | Give folders rights with `sudo chown www-data /var/downloads/ -R` 104 | 105 | Cofigure the `src/core/config/global.php` file with yours settings : 106 | ``` 107 | 112 | ``` 113 | 114 | ### MySQL Import 115 | Import the MySQL database `board.sql` 116 | 117 | Configure the `src/core/config/bdd.php` file with yours settings : 118 | ``` 119 | 126 | ``` 127 | 128 | Add user in the `users` table with MD5 encryption for the password : 129 | ``` 130 | insert into users(login,mail,password,boxe,couleur,lastScan,rss,admin,port) values('USERNAME','-','MD5_PASSWORD','/YOUR_DOWNLOADS_FLODER/USERNAME', '78ba00', 0, '', 1, 9091); 131 | ``` 132 | 133 | ### Apache Configuration 134 | 135 | Enable memcache in your `php.ini` file (/etc/php5/apache2/php.ini) adding line `extension=memcache.so` 136 | 137 | Enable HTACESS for the web folder. 138 | 139 | Solution to enable HTACCESS but it's not secure : 140 | 141 | ``` 142 | sudo nano /etc/apache2/apache2.conf 143 | ``` 144 | Change Directory directive 145 | ``` 146 | 147 | Options Indexes FollowSymLinks 148 | AllowOverride None 149 | Require all granted 150 | 151 | ``` 152 | 153 | To 154 | 155 | ``` 156 | 157 | Options Indexes FollowSymLinks 158 | AllowOverride All 159 | Require all granted 160 | 161 | ``` 162 | 163 | 164 | 165 | Add thoose lines in the cron table `/etc/cron.d/php5` 166 | ``` 167 | */1 * * * * www-data wget "http://localhost/action/refreshTorrent/" -O /dev/null' >> /etc/cron.d/php5 168 | ``` 169 | 170 | 171 | ### Initialization 172 | Restart cron `sudo /etc/init.d/cron restart` 173 | 174 | Restart Apache `sudo service apache2 reload` 175 | 176 | Stop transmission `sudo service transmission stop` 177 | 178 | Go to : http://localhost/ 179 | 180 | *** 181 | 182 | Read the [Official Website](http://cclleemm.github.io/FriendlyTorrent/) for more information. 183 | 184 | *** 185 | 186 | 187 | ## Community 188 | 189 | Keep track of development and community news. 190 | 191 | * Follow [@FriendlyTorrent on Twitter](http://twitter.com/friendlytorrent). 192 | * Read and subscribe to [The Official Website](http://friendlytorrent.com). 193 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | checkInstall() 4 | { 5 | echo "------------ Check for $1 depedency ------------" 6 | 7 | PKG_OK=$(dpkg-query -W --showformat='${Status}\n' $1|grep "install ok installed") 8 | echo Checking for $1: $PKG_OK 9 | if [ "" == "$PKG_OK" ]; then 10 | echo "No $1. Setting up $1." 11 | if [ $1 == "sudo" ]; then 12 | apt-get --force-yes --yes install $1 13 | else 14 | sudo apt-get --force-yes --yes install $1 15 | fi 16 | fi 17 | } 18 | 19 | checkInstall sudo 20 | checkInstall apache2 21 | a2enmod rewrite 22 | checkInstall php5 23 | checkInstall mysql-server 24 | checkInstall libapache2-mod-php5 25 | checkInstall php5-curl 26 | checkInstall php5-mysql 27 | checkInstall memcached 28 | checkInstall php5-memcache 29 | checkInstall php5-memcached 30 | checkInstall transmission 31 | checkInstall transmission-daemon 32 | checkInstall zip 33 | 34 | 35 | echo "----------------------------------------------------" 36 | echo "Welcome in the Friendly Torrent installation wizard" 37 | echo "----------------------------------------------------" 38 | 39 | #Ask for Website dir 40 | echo -n "Application language (fr or en) [default : en] " 41 | read language 42 | if [[ $language == "" ]] 43 | then 44 | language=en 45 | fi 46 | echo -n "Website directory [default : /var/www/] " 47 | read websiteFolder 48 | if [[ $websiteFolder == "" ]] 49 | then 50 | websiteFolder=/var/www/ 51 | fi 52 | 53 | cd src 54 | cp -R ./ $websiteFolder 1> /dev/null 55 | cd .. 56 | echo "Files copied" 57 | 58 | echo -n "Admin login : " 59 | read login 60 | echo -n "Admin password : " 61 | read -s pass 62 | echo "" 63 | 64 | #Ask for download storage folder 65 | echo -n "Downloads storage folder [default : /var/downloads/] " 66 | read dossierBoxes 67 | if [[ $dossierBoxes == "" ]] 68 | then 69 | dossierBoxes=/var/downloads/ 70 | fi 71 | if mkdir $dossierBoxes 72 | then 73 | echo "Folder created with success " 74 | else 75 | echo "Can't create this folder" 76 | fi 77 | 78 | mkdir "$dossierBoxes/$login/" 79 | mkdir "$dossierBoxes/.transferts/" 80 | sudo chown www-data $dossierBoxes -R 81 | 82 | echo "------------ PHP/Apache2 Configuration ------------ " 83 | echo 'extension=memcache.so' >> /etc/php5/apache2/php.ini 84 | echo "PHP Memcache extension enabled" 85 | echo '*/1 * * * * www-data wget "http://localhost/action/refreshTorrent/" -O /dev/null' >> /etc/cron.d/php5 86 | 87 | if [ -f "$websiteFolder/index.html" ]; then 88 | sudo mv "$websiteFolder/index.html" "$websiteFolder/index-old.html" 89 | 90 | fi 91 | 92 | echo "------------ MySQL configuration ------------ " 93 | 94 | while true; do 95 | echo -n "MySQL user : " 96 | read userBDD 97 | echo -n "MySQL password : " 98 | read -s passBDD 99 | echo "" 100 | echo -n "MySQL database: " 101 | read bdd 102 | mysql -u $userBDD -p$passBDD -e "CREATE DATABASE IF NOT EXISTS $bdd;" 103 | if mysql -u $userBDD -p$passBDD $bdd < board.sql 104 | then 105 | echo "Database importation succeed ! " 106 | break 107 | else 108 | echo "Can't import the database !" 109 | fi 110 | 111 | done 112 | 113 | md5=`echo -n $pass|md5sum|awk '{ print $1 }'` 114 | mysql -u $userBDD -p$passBDD $bdd << EOF 115 | insert into users(login,mail,password,boxe,couleur,lastScan,rss,admin,port) values('$login','-','$md5','$dossierBoxes/$login', '78ba00', 0, '', 1, 9091); 116 | EOF 117 | 118 | mkdir "$websiteFolder""core/config/" 119 | 120 | echo "" > "$websiteFolder""core/config/bdd.php" 130 | 131 | echo "" > "$websiteFolder""core/config/global.php" 136 | 137 | sudo service apache2 reload 138 | sudo service transmission-daemon stop 139 | sudo /etc/init.d/cron restart 140 | 141 | echo "----------------------------------------------------" 142 | echo "----- Thank you for installing Friendly Torrent ----" 143 | echo "---------- Don't forget to enable HTACCESS ---------" 144 | echo "----------- And go to : http://localhost -----------" 145 | echo "----------------------------------------------------" 146 | -------------------------------------------------------------------------------- /rm.sh: -------------------------------------------------------------------------------- 1 | sudo apt-get remove apache2 2 | sudo apt-get remove php5 3 | sudo apt-get remove mysql-server 4 | sudo apt-get remove libapache2-mod-php5 5 | sudo apt-get remove php5-mysql -------------------------------------------------------------------------------- /src/.htaccess: -------------------------------------------------------------------------------- 1 | RewriteEngine on 2 | Options +FollowSymlinks 3 | 4 | RewriteRule ^/$ index.php?p=accueil [L,QSA] 5 | RewriteRule ^$ index.php?p=accueil [L,QSA] 6 | 7 | RewriteRule ^connect/([a-zA-Z0-9\-\_\/]*)$ index.php?p=connect/$1 [L,QSA] 8 | RewriteRule ^boxe/([a-zA-Z0-9\-\_\/]*)$ index.php?p=boxe/$1 [L,QSA] 9 | RewriteRule ^action/([a-zA-Z0-9\-\_\/]*)$ index.php?p=action/$1 [L,QSA] 10 | RewriteRule ^downloads/(.*)$ index.php?p=downloads/index/$1 [L,QSA] 11 | RewriteRule ^torrents/([a-zA-Z0-9\-\_\/]*)$ index.php?p=torrents/$1 [L,QSA] 12 | RewriteRule ^compte/([a-zA-Z0-9\-\_\/]*)$ index.php?p=compte/$1 [L,QSA] 13 | RewriteRule ^gks/([a-zA-Z0-9\-\_\/]*)$ index.php?p=gks/$1 [L,QSA] 14 | RewriteRule ^messagerie/([a-zA-Z0-9\-\_\/]*)$ index.php?p=messagerie/$1 [L,QSA] 15 | RewriteRule ^rss/([a-zA-Z0-9\-\_\/]*)$ index.php?p=rss/$1 [L,QSA] 16 | RewriteRule ^admin/([a-zA-Z0-9\-\_\/]*)$ index.php?p=admin/$1 [L,QSA] 17 | RewriteRule ^stats/([a-zA-Z0-9\-\_\/]*)$ index.php?p=stats/$1 [L,QSA] -------------------------------------------------------------------------------- /src/bootstrap/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cclleemm/FriendlyTorrent/3ad1adf5357b430a1fe89bfa1bf3a52dd10f8e45/src/bootstrap/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /src/bootstrap/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cclleemm/FriendlyTorrent/3ad1adf5357b430a1fe89bfa1bf3a52dd10f8e45/src/bootstrap/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /src/bootstrap/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cclleemm/FriendlyTorrent/3ad1adf5357b430a1fe89bfa1bf3a52dd10f8e45/src/bootstrap/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /src/controllers/boxe.php: -------------------------------------------------------------------------------- 1 | LANG_TITLE); 16 | 17 | //////////////////////////////////// 18 | // ACTION INDEX // 19 | //////////////////////////////////// 20 | 21 | function index(){ 22 | $d = array(); 23 | 24 | Tools::scanBoxe(Core::idCo()); 25 | 26 | $boxe = new Cloud(); 27 | $d['boxe'] = $boxe->boxeData; 28 | 29 | $xfer = new Xfer(Core::idCo()); 30 | $xfer->scanTorrent(); 31 | $stats = $xfer->getStat(); 32 | 33 | $d['uploadTotal'] = $stats['up']; 34 | $d['downloadTotal'] = $stats['down']; 35 | 36 | $d['color'] = $d['boxe']['couleur']; 37 | 38 | $totalspace = disk_total_space(ROOT_DOWNLOADS); 39 | $freespace = disk_free_space(ROOT_DOWNLOADS); 40 | 41 | $free = 100; 42 | 43 | $d['pourcent'] = round((Tools::dirsize($boxe->boxeData['boxe']) / $totalspace)*100); 44 | $d['space'] = Tools::dirsize($boxe->boxeData['boxe']); 45 | 46 | $this->set($d); 47 | $this->render('index'); 48 | } 49 | 50 | function users($login){ 51 | $d = array(); 52 | 53 | $boxe = new Cloud($login); 54 | $d['boxe'] = $boxe->boxeData; 55 | 56 | Tools::scanBoxe($boxe->boxeData['id']); 57 | 58 | $xfer = new Xfer($boxe->boxeData['id']); 59 | $xfer->scanTorrent(); 60 | $stats = $xfer->getStat(); 61 | 62 | $d['uploadTotal'] = $stats['up']; 63 | $d['downloadTotal'] = $stats['down']; 64 | 65 | $this->title['users'] = $login."'s box"; 66 | $this->setTitle(); 67 | 68 | $totalspace = disk_total_space(ROOT_DOWNLOADS); 69 | $freespace = disk_free_space(ROOT_DOWNLOADS); 70 | 71 | $free = 100; 72 | 73 | $d['pourcent'] = round((Tools::dirsize($boxe->boxeData['boxe']) / $totalspace)*100); 74 | $d['space'] = Tools::dirsize($boxe->boxeData['boxe']); 75 | 76 | 77 | $d['color'] = $d['boxe']['couleur']; 78 | 79 | $this->set($d); 80 | $this->render('index'); 81 | } 82 | 83 | } 84 | ?> -------------------------------------------------------------------------------- /src/controllers/compte.php: -------------------------------------------------------------------------------- 1 | LANG_TITLE_MY_ACCOUNT); 16 | 17 | //////////////////////////////////// 18 | // ACTION INDEX // 19 | //////////////////////////////////// 20 | 21 | function index(){ 22 | $d = array(); 23 | 24 | $d['user'] = $this->user->userData; 25 | 26 | if($this->post['oldPass'] != "" && $this->post['newPass'] != "" ){ 27 | if(md5($this->post['oldPass']) == $d['user']['password']){ 28 | if($this->post['newPass'] == $this->post['newPass2']){ 29 | $this->user->changeMdp($this->post['newPass']); 30 | $d['java'] = ''; 31 | }else{ 32 | $d['error'] = LANG_PASSWORD_DIFERENT; 33 | } 34 | }else{ 35 | $d['error'] = LANG_OLD_PASSWORD_DIFERENT; 36 | } 37 | } 38 | 39 | if($this->post['rss'] != $d['user']['rss'] && $this->post['rss']){ 40 | $this->user->changeRss($this->post['rss']); 41 | $d['user']['rss'] = $this->post['rss']; 42 | } 43 | 44 | $this->set($d); 45 | $this->render('index'); 46 | } 47 | 48 | } 49 | ?> -------------------------------------------------------------------------------- /src/controllers/connect.php: -------------------------------------------------------------------------------- 1 | LANG_CONNECTION); 16 | 17 | //////////////////////////////////// 18 | // ACTION INDEX // 19 | //////////////////////////////////// 20 | 21 | function index(){ 22 | $d = array(); 23 | 24 | $this->setLayout('external'); 25 | 26 | $user = new User(); 27 | 28 | if($_POST['login'] != '' OR $_POST['mdp'] != ''){ 29 | if($user->connect($_POST['login'], $_POST['mdp'])){ 30 | header('Location: '.WEBROOT); 31 | } 32 | } 33 | 34 | 35 | $this->set($d); 36 | $this->render('index'); 37 | } 38 | 39 | } 40 | ?> -------------------------------------------------------------------------------- /src/controllers/layout/default.php: -------------------------------------------------------------------------------- 1 | models)){ 22 | foreach($this->models as $v){ 23 | $this->loadModel($v); 24 | } 25 | } 26 | 27 | parent::__construct(); 28 | } 29 | 30 | 31 | //////////////////////////////////// 32 | // LISTE DES VARS ENVOYE AU LAYOUT// 33 | // DES VAR ENVOYE // 34 | // AU LAYOUT // 35 | //////////////////////////////////// 36 | 37 | function vars(){ 38 | 39 | $tab = array(); 40 | 41 | $user = new User(); 42 | 43 | $tab['user'] = $user->userData; 44 | 45 | $sql = "SELECT users.* FROM users WHERE login != '".$tab['user']['login']."';"; 46 | $rst = $this->bdd->query($sql); 47 | 48 | $tab['other'] = $rst; 49 | 50 | $freespace = disk_free_space(ROOT_DOWNLOADS); 51 | $tab['freespace'] = $freespace; 52 | 53 | $this->set($tab); 54 | } 55 | 56 | 57 | //////////////////////////////////// 58 | // FONCTION DE DECLARATION // 59 | // VAR DU LAYOUT // 60 | //////////////////////////////////// 61 | 62 | function set($d){ 63 | $this->vars = array_merge($this->vars,$d); 64 | } 65 | 66 | 67 | 68 | //////////////////////////////////// 69 | // FONCTION DE Vu LAYOUT // 70 | //////////////////////////////////// 71 | 72 | function viewLayout($content){ 73 | 74 | $this->vars(); 75 | 76 | //Extraction pour le layout 77 | extract($this->vars); 78 | 79 | $content_for_layout = $content; 80 | 81 | //Affichage du layout 82 | require(ROOT.'views/layout/default.php'); 83 | 84 | } 85 | 86 | //////////////////////////////////// 87 | // FONCTION DE LOAD MODEL // 88 | //////////////////////////////////// 89 | 90 | function loadModel($name){ 91 | require_once(ROOT.'models/'.strtolower($name).'.php'); 92 | } 93 | 94 | } 95 | 96 | ?> -------------------------------------------------------------------------------- /src/controllers/layout/external.php: -------------------------------------------------------------------------------- 1 | models)){ 22 | foreach($this->models as $v){ 23 | $this->loadModel($v); 24 | } 25 | } 26 | 27 | parent::__construct(); 28 | } 29 | 30 | 31 | //////////////////////////////////// 32 | // LISTE DES VARS ENVOYE AU LAYOUT// 33 | // DES VAR ENVOYE // 34 | // AU LAYOUT // 35 | //////////////////////////////////// 36 | 37 | function vars(){ 38 | 39 | $tab = array(); 40 | $this->set($tab); 41 | } 42 | 43 | 44 | //////////////////////////////////// 45 | // FONCTION DE DECLARATION // 46 | // VAR DU LAYOUT // 47 | //////////////////////////////////// 48 | 49 | function set($d){ 50 | $this->vars = array_merge($this->vars,$d); 51 | } 52 | 53 | 54 | 55 | //////////////////////////////////// 56 | // FONCTION DE Vu LAYOUT // 57 | //////////////////////////////////// 58 | 59 | function viewLayout($content){ 60 | 61 | $this->vars(); 62 | 63 | //Extraction pour le layout 64 | extract($this->vars); 65 | 66 | $content_for_layout = $content; 67 | 68 | //Affichage du layout 69 | require(ROOT.'views/layout/external.php'); 70 | 71 | } 72 | 73 | //////////////////////////////////// 74 | // FONCTION DE LOAD MODEL // 75 | //////////////////////////////////// 76 | 77 | function loadModel($name){ 78 | require_once(ROOT.'models/'.strtolower($name).'.php'); 79 | } 80 | 81 | } 82 | 83 | ?> -------------------------------------------------------------------------------- /src/controllers/messagerie.php: -------------------------------------------------------------------------------- 1 | LANG_INBOX); 16 | 17 | //////////////////////////////////// 18 | // ACTION INDEX // 19 | //////////////////////////////////// 20 | 21 | function index(){ 22 | $d = array(); 23 | 24 | 25 | $sql = "SELECT users.login, users.id, messagerie.idUser, messagerie.idUserTarget, COUNT(messagerie.id) as nb FROM messagerie, users WHERE ((idUserTarget = '".Core::idCo()."' AND idUser != '".Core::idCo()."' AND users.id = messagerie.idUser) OR (idUser = '".Core::idCo()."' AND idUserTarget != '".Core::idCo()."' AND users.id = messagerie.idUserTarget) OR (idUser = '".Core::idCo()."' AND idUserTarget = '".Core::idCo()."' AND users.id = messagerie.idUser)) GROUP BY users.login ;"; 26 | $messages = $this->bdd->query($sql); 27 | 28 | $messagesFinal = array(); 29 | 30 | while($rslt = mysql_fetch_assoc($messages)){ 31 | $sql = "SELECT messagerie.text, messagerie.time, messagerie.idUserTarget, messagerie.seen FROM messagerie WHERE ((idUserTarget = '".$rslt['idUserTarget']."' AND idUser = '".$rslt['idUser']."') OR (idUser = '".$rslt['idUserTarget']."' AND idUserTarget = '".$rslt['idUser']."')) ORDER BY messagerie.time DESC LIMIT 0, 1;"; 32 | $message = $this->bdd->query($sql); 33 | $messageRst = mysql_fetch_assoc($message); 34 | 35 | $rslt['time'] = intval($messageRst['time']); 36 | $rslt['text'] = $messageRst['text']; 37 | 38 | if($messageRst['idUserTarget'] == Core::idCo()) 39 | $rslt['seen'] = $messageRst['seen']; 40 | else 41 | $rslt['seen'] = 1; 42 | 43 | $messagesFinal[] = $rslt; 44 | } 45 | usort($messagesFinal, function($a, $b) { 46 | 47 | if ($a['time'] == $b['time']) { 48 | return 0; 49 | } 50 | 51 | return ($a['time'] > $b['time']) ? -1 : 1; 52 | }); 53 | 54 | $d['messages'] = $messagesFinal; 55 | 56 | $this->set($d); 57 | $this->render('index'); 58 | } 59 | 60 | 61 | function chat($idUser){ 62 | $d = array(); 63 | 64 | $sql = "SELECT messagerie.id, messagerie.text, users.login, users.mail, messagerie.time, messagerie.seen FROM messagerie, users, users as ut WHERE users.id = messagerie.idUser AND ut.id = messagerie.idUserTarget AND (idUserTarget = '".Core::idCo()."' AND idUser = '".$idUser."' OR idUser = '".Core::idCo()."' AND idUserTarget = '".$idUser."') ORDER BY messagerie.time DESC;"; 65 | $d['messages'] = $this->bdd->query($sql); 66 | 67 | $sql = "UPDATE messagerie SET seen = 1 WHERE (idUserTarget = '".Core::idCo()."' AND idUser = '".$idUser."');"; 68 | $rst = $this->bdd->query($sql); 69 | 70 | $user = new User($idUser); 71 | 72 | $d['idUser'] = $user->userData['id']; 73 | 74 | $this->title['chat'] = LANG_CONVERSATION_WITH." ".$user->userData['login']; 75 | $this->setTitle(); 76 | 77 | $this->set($d); 78 | $this->render('chat'); 79 | } 80 | 81 | function nouveau($idRep){ 82 | $d = array(); 83 | 84 | $user = new User(); 85 | 86 | if($this->post['to_user'] && $this->post['message']){ 87 | 88 | //NEW message 89 | $sql = "INSERT INTO messagerie VALUES ('', '".Core::idCo()."', '".$this->post['to_user']."', '".addslashes($this->post['message'])."', 0, '".time()."');"; 90 | $this->bdd->query($sql); 91 | } 92 | header('Location: '.WEBROOT.'messagerie/chat/'.$this->post['to_user'].'/'); 93 | } 94 | 95 | function delete($id){ 96 | $sql = "DELETE FROM messagerie WHERE id = '".$id."';"; 97 | $this->bdd->query($sql); 98 | header('Location: '.WEBROOT.'messagerie/'); 99 | } 100 | 101 | } 102 | ?> -------------------------------------------------------------------------------- /src/controllers/rss.php: -------------------------------------------------------------------------------- 1 | LANG_RSS); 16 | 17 | //////////////////////////////////// 18 | // ACTION INDEX // 19 | //////////////////////////////////// 20 | 21 | function index(){ 22 | $d = array(); 23 | 24 | $d['lien'] = $this->user->userData['rss']; 25 | 26 | $this->set($d); 27 | $this->render('index'); 28 | } 29 | 30 | } 31 | ?> -------------------------------------------------------------------------------- /src/controllers/stats.php: -------------------------------------------------------------------------------- 1 | LANG_CHARTS); 17 | 18 | //////////////////////////////////// 19 | // ACTION INDEX // 20 | //////////////////////////////////// 21 | 22 | function index(){ 23 | $d = array(); 24 | 25 | $total = 0; 26 | $totalOff = 0; 27 | $me = 0; 28 | 29 | $sql = "SELECT * FROM torrents;"; 30 | $rst = $this->bdd->query($sql); 31 | 32 | while($rslt = mysql_fetch_assoc($rst)){ 33 | ++$totalOff; 34 | if($rslt['idBoxe'] == Core::idCo()) 35 | ++$me; 36 | } 37 | 38 | $d['total'] = $total; 39 | $d['totalOff'] = $totalOff; 40 | $d['me'] = $me; 41 | 42 | $totalspace = disk_total_space(ROOT_DOWNLOADS); 43 | $freespace = disk_free_space(ROOT_DOWNLOADS); 44 | 45 | // Ne rien changer par la suite 46 | $usedspace = $totalspace - $freespace; 47 | $pourcent = round(($usedspace / $totalspace)*100); 48 | 49 | $d['usedspace'] = $usedspace; 50 | $d['totalspace'] = $totalspace; 51 | $d['pourcent'] = $pourcent; 52 | $d['space'] = round(($freespace / $totalspace)*100); 53 | $d['free'] = Tools::convertFileSize($freespace); 54 | 55 | $sql = "SELECT users.login, users.mail, users.couleur, sessions.lastTime FROM sessions, users WHERE sessions.idUser = users.id AND lastTime >= '".(time()-(5*60))."' GROUP BY users.login ORDER BY sessions.lastTime DESC;"; 56 | $rst = $this->bdd->query($sql); 57 | 58 | $d['connect'] = $rst; 59 | 60 | 61 | $xfer = new Xfer(); 62 | $stats = $xfer->getStatTotal(); 63 | 64 | $d['uploadTotal'] = $stats['up']; 65 | $d['downloadTotal'] = $stats['down']; 66 | 67 | $this->set($d); 68 | $this->render('stats'); 69 | } 70 | 71 | } 72 | ?> -------------------------------------------------------------------------------- /src/controllers/torrents.php: -------------------------------------------------------------------------------- 1 | LANG_TITLE_MY_TORRENTS); 16 | 17 | //////////////////////////////////// 18 | // ACTION INDEX // 19 | //////////////////////////////////// 20 | 21 | function index(){ 22 | $d = array(); 23 | 24 | $this->set($d); 25 | $this->render('index'); 26 | } 27 | 28 | } 29 | ?> -------------------------------------------------------------------------------- /src/core/bdd.php: -------------------------------------------------------------------------------- 1 | server = $BDD_MYSQL_SERVER; 31 | $this->user = $BDD_MYSQL_LOGIN; 32 | $this->pass = $BDD_MYSQL_PASS; 33 | $this->database = $BDD_MYSQL_BDD; 34 | 35 | $this->memcache = new Memcache; 36 | $this->memcache->connect('localhost', 11211) or die ("Connexion impossible"); 37 | 38 | } 39 | 40 | //////////////////////////////////// 41 | //////////////////////////////////// 42 | // MYSQL // 43 | //////////////////////////////////// 44 | //////////////////////////////////// 45 | 46 | public function clear(){ 47 | $this->query = null; 48 | } 49 | 50 | public function getCache(){ 51 | return $this->memcache; 52 | } 53 | 54 | //Fonction pour se connecter à la BDD MYSQL 55 | public function open(){ 56 | // On vérifie avant si on est connecté ou pas 57 | if(!$this->connected){ 58 | $this->co = mysql_connect($this->server, $this->user, $this->pass); 59 | 60 | $this->connected = true; 61 | 62 | mysql_select_db($this->database); 63 | } 64 | } 65 | 66 | // Fonction pour faire une requete MYSQL AVEC Cache pour éviter de faire la même requête mysql plusieure fois 67 | public function query($sql){ 68 | 69 | if($this->query[($sql)] == NULL){ 70 | $this->open(); 71 | $temp = mysql_query($sql, $this->co) or die("Erreur: ".mysql_error()."
Requète: ".$sql); 72 | 73 | if($this->queryNb[($sql)]){ 74 | $this->queryNb[($sql)][0]++; 75 | $this->queryNb[($sql)][1] = ($time - $this->timeStart); 76 | }else 77 | $this->queryNb[($sql)] =array(1, ($time - $this->timeStart)); 78 | 79 | if($temp != 1 AND $temp != 0){ 80 | $this->query[($sql)] = $temp; 81 | 82 | $num_rows = mysql_num_rows($temp); 83 | if($num_rows != false AND $num_rows != 0){ 84 | mysql_data_seek($temp, 0); 85 | } 86 | } 87 | }else{ 88 | $temp = $this->query[($sql)]; 89 | $num_rows = mysql_num_rows($temp); 90 | if($num_rows != false AND $num_rows != 0){ 91 | mysql_data_seek($temp, 0); 92 | } 93 | } 94 | return $temp; 95 | } 96 | 97 | // Fonction pour faire une requete MYSQL SANS CACHE 98 | public function queryNoCache($sql){ 99 | $this->open(); 100 | $temp = mysql_query($sql, $this->co) or die("Erreur: ".mysql_error()."
Requète: ".$sql); 101 | return $temp; 102 | } 103 | 104 | public function data($source){ 105 | 106 | $result = array(); 107 | 108 | while($rslt=mysql_fetch_assoc($source)){ 109 | $return[]=$rslt; 110 | } 111 | 112 | return $return; 113 | } 114 | } 115 | 116 | ?> -------------------------------------------------------------------------------- /src/core/controller.php: -------------------------------------------------------------------------------- 1 | post = $_POST; 26 | } 27 | 28 | //Sert pour charger les models utilisé 29 | if(isset($this->models)){ 30 | foreach($this->models as $v){ 31 | $this->loadModel($v); 32 | } 33 | } 34 | 35 | if($notLayout == false){ 36 | $this->setLayout('default'); 37 | $this->notLayout = false; 38 | }else{ 39 | $this->notLayout = true; 40 | } 41 | 42 | if(Core::isCo()){ 43 | $this->user = new User(); 44 | } 45 | 46 | parent::__construct(); 47 | } 48 | 49 | function virtual($adresse){ 50 | 51 | $params = explode('/',$adresse); 52 | $controller = $params[0]; 53 | $action = $params[1] != '' ? $params[1] : 'index'; 54 | 55 | // Instancie le controller corespondant, ainsi que la fonction corespondant à l'action 56 | require(ROOT.'controllers/'.$controller.'.php'); 57 | $controller = new $controller(true); 58 | 59 | if(method_exists($controller, $action)){ 60 | unset($params[0]); unset($params[1]); 61 | $controller->action = $action; 62 | call_user_func_array(array($controller,$action),$params); 63 | } 64 | else{ 65 | return false; 66 | } 67 | } 68 | 69 | //////////////////////////////////// 70 | // FONCTION DE DECLARATION // 71 | // d'envoi de valeurs au render // 72 | //////////////////////////////////// 73 | 74 | function set($d){ 75 | $this->vars = array_merge($this->vars,$d); 76 | } 77 | 78 | //////////////////////////////////// 79 | // FONCTION DE DEFINITION // 80 | // DE LAYOUT // 81 | //////////////////////////////////// 82 | 83 | function setLayout($layout){ 84 | 85 | //Exception ajax 86 | if($layout != '' AND $this->notLayout == false){ 87 | $this->layout = $layout; 88 | 89 | include(ROOT.'controllers/layout/'.$this->layout.'.php'); 90 | 91 | $aff = 'Layout'.$this->layout; 92 | $this->Layout = ''; 93 | $this->Layout = new $aff; 94 | }else{ 95 | $this->Layout = ''; 96 | } 97 | } 98 | 99 | function setTitle(){ 100 | 101 | $d['title'] = $this->title[$this->action]; 102 | if(!$this->notLayout) 103 | $this->Layout->set($d); 104 | $this->set($d); 105 | 106 | } 107 | 108 | function setHeadMore($head){ 109 | if(!$this->notLayout){ 110 | $d['headMore'] = $head; 111 | $this->Layout->set($d); 112 | } 113 | } 114 | 115 | //////////////////////////////////// 116 | // FONCTION DE VU SANS LAYOUT // 117 | //////////////////////////////////// 118 | 119 | function viewSimple(){ 120 | extract($this->vars); 121 | $lang = $this->lang; 122 | 123 | ob_start(); 124 | require(ROOT.'views/'.get_class($this).'/'.$this->filename.'.php'); 125 | return ob_get_clean(); 126 | } 127 | 128 | //////////////////////////////////// 129 | // FONCTION D'AFFICHAGE // 130 | // affichage du render demandé // 131 | //////////////////////////////////// 132 | 133 | function render($filename){ 134 | 135 | $this->filename = $filename; 136 | 137 | //Si Pas de layout on met les varables en places et on affiche JUSTE le contenu ! 138 | if($this->layout==false){ 139 | echo $this->viewSimple(); 140 | if($this->title[$this->action] != ''){ 141 | echo ''; 142 | } 143 | 144 | }else{ // Sinon affichage spéciale sans layout 145 | $this->Layout->viewLayout($this->viewSimple()); 146 | } 147 | } 148 | 149 | //////////////////////////////////// 150 | // FONCTION DE LOAD MODEL // 151 | //////////////////////////////////// 152 | 153 | function loadModel($name){ 154 | require_once(ROOT.'models/'.strtolower($name).'.php'); 155 | 156 | // On instancie à la main ! 157 | //$this->$name = new $name(); 158 | } 159 | } 160 | ?> -------------------------------------------------------------------------------- /src/core/core.php: -------------------------------------------------------------------------------- 1 | bdd=new BDD; 35 | 36 | 37 | if($token){ 38 | $sql = "SELECT sessions.*, users.admin FROM sessions, users WHERE users.id = sessions.idUser AND sessions.cle = '".$token."';"; 39 | $query = $this->bdd->query($sql); 40 | $donnees = mysql_fetch_assoc($query); 41 | if($donnees['idUser']){ 42 | $this->co = true; 43 | $this->idUser = $donnees['idUser']; 44 | 45 | $sql = "UPDATE sessions SET lastTime = '".time()."' WHERE cle = '".$token."';"; 46 | $query = $this->bdd->query($sql); 47 | 48 | if($donnees['time'] < time()-(24*60*60)){ 49 | $sql = "UPDATE sessions SET time = '".time()."' WHERE cle = '".$token."';"; 50 | $query = $this->bdd->query($sql); 51 | 52 | setcookie('SEED_connect', $_COOKIE['SEED_connect'], (time() + 2592000), '/'); 53 | } 54 | 55 | $this->admin = $donnees['admin']; 56 | } 57 | } 58 | } 59 | /** 60 | * Méthode qui crée l'unique instance de la classe 61 | * si elle n'existe pas encore puis la retourne. 62 | * 63 | * @param void 64 | * @return Singleton 65 | */ 66 | public static function getInstance($token = '') { 67 | 68 | if(!$token) 69 | $token = $_COOKIE['SEED_connect']; 70 | 71 | if(is_null(self::$_instance)) { 72 | self::$_instance = new Core($token); 73 | } 74 | return self::$_instance; 75 | } 76 | 77 | ///////////////////// 78 | // METHODES STATIC // 79 | ///////////////////// 80 | 81 | static function isCo(){ 82 | $Core = Core::getInstance(); 83 | 84 | if($Core->co){ 85 | return true; 86 | }else{ 87 | return false; 88 | } 89 | } 90 | 91 | static function isAdmin(){ 92 | $Core = Core::getInstance(); 93 | if($Core->admin){ 94 | return true; 95 | }else{ 96 | return false; 97 | } 98 | } 99 | 100 | static function idCo(){ 101 | $Core = Core::getInstance(); 102 | if($Core->idUser){ 103 | return $Core->idUser; 104 | }else{ 105 | return false; 106 | } 107 | 108 | } 109 | } 110 | 111 | // PARENT A toutes les autres class ou lang et bdd est utile 112 | class Common { 113 | 114 | protected $bdd; 115 | 116 | function __construct(){ 117 | // on utilise la seule instance du coeur ... 118 | $Core = Core::getInstance(); 119 | 120 | 121 | //INITIALISATION de la base de donnée 122 | $this->bdd = $Core->bdd; 123 | 124 | } 125 | } 126 | 127 | ?> 128 | -------------------------------------------------------------------------------- /src/core/languages/en.php: -------------------------------------------------------------------------------- 1 | Administrator !"); 42 | 43 | 44 | //Boxes 45 | define("LANG_NAME", "Name"); 46 | define("LANG_INFO", "Info"); 47 | define("LANG_TYPE", "Type"); 48 | define("LANG_SIZE", "Size"); 49 | define("LANG_FILE", "File"); 50 | define("LANG_FOLDER", "Folder"); 51 | define("LANG_USE_ON_SERVER", "used on the server"); 52 | define("LANG_OF_SERVER", "of server"); 53 | define("LANG_TO_MUCH_SPACE", "Be careful, no a lot free space !"); 54 | define("LANG_DOWNLOADING", "DOWNLOADING"); 55 | define("LANG_NO_DOWNLOAD", "NO DL"); 56 | define("LANG_DOWNLOADING_DESC", "Downloading"); 57 | define("LANG_NO_DOWNLOAD_DESC", "You never download this file"); 58 | 59 | 60 | //Account 61 | define("LANG_BASIC_INFO", "Basics informations"); 62 | define("LANG_OLD_PASSWORD", "Old password"); 63 | define("LANG_NEW_PASSWORD", "New password"); 64 | define("LANG_LINK_RSS", "Link to RSS feed"); 65 | define("LANG_PROFIL_PICTURE", "Profil picture"); 66 | define("LANG_MANAGE_MY_PROFIL_PICTURE", "Manage my profil picture"); 67 | 68 | //Connect 69 | define("LANG_CONNECTION", "Log In !"); 70 | define("LANG_LOG_IN", "Log In"); 71 | 72 | //Download 73 | define("LANG_YOU_WILL_DOWNLOAD", "You are going to download the "); 74 | define("LANG_START_DOWNLOAD", "Start the download"); 75 | define("LANG_ERROR_INEXISTING", "Error : nothing to download !"); 76 | 77 | //INBOX 78 | define("LANG_REPLY", "Reply"); 79 | define("LANG_DELETE_MESSAGE", "Delete this message"); 80 | define("LANG_MESSAGE_SENDED", "Message sended !"); 81 | define("LANG_CONVERSATION_WITH", "Chat with "); 82 | 83 | 84 | //Charts 85 | define("LANG_NETWORK", "Network"); 86 | define("LANG_MY_NETWORK", "My Network transferts"); 87 | define("LANG_THIS_MONTH", "this month"); 88 | define("LANG_MONTH_UPLOAD_USE", "Data uploaded this month"); 89 | define("LANG_MONTH_DOWNLOAD_USE", "Data downloaded this month"); 90 | define("LANG_DISK_SPACE", "Disk space"); 91 | 92 | //Controllers actions 93 | define("LANG_NO_MORE_SPACE", "No more free space on the server"); 94 | define("LANG_CANT_START_TORRENT", "Can't start the torrent"); 95 | define("LANG_CANT_STOP_TORRENT", "Can't stop the torrent"); 96 | define("LANG_TORRENT_ADDED", "Torrent added successfully"); 97 | define("LANG_CANT_ADD_TORRENT", "Can't add this torrent"); 98 | define("LANG_CANT_ADD_TORRENT_ALREADY", "Can't add this torrent because you already downloaded it."); 99 | define("LANG_CANT_DELETE_TORRENT", "Can't delete the torrent"); 100 | define("LANG_CHOOSE_TORRENT", "Please choose a .torrent file !"); 101 | define("LANG_FREE_SPACE", "Free space"); 102 | 103 | //Controllers admin 104 | define("LANG_PASSWORD_DIFERENT", "New passwords are different"); 105 | define("LANG_OLD_PASSWORD_DIFERENT", "Incorrect old password"); 106 | define("LANG_CANT_CREATE_FOLDER", "Can't create boxe folder !"); 107 | 108 | //Account 109 | define("LANG_PASSWORD_CHANGED", "Password changed successfully !"); 110 | 111 | //Titles 112 | define("LANG_TITLE_ADMINISTRATION", "Administration panel"); 113 | define("LANG_TITLE_MY_ACCOUNT", "My account"); 114 | define("LANG_TITLE_MY_TORRENTS", "My torrents"); 115 | 116 | ?> -------------------------------------------------------------------------------- /src/core/languages/fr.php: -------------------------------------------------------------------------------- 1 | Administrateur !"); 42 | 43 | 44 | //Boxes 45 | define("LANG_NAME", "Nom"); 46 | define("LANG_INFO", "Info"); 47 | define("LANG_TYPE", "Type"); 48 | define("LANG_SIZE", "Taille"); 49 | define("LANG_FILE", "Fichier"); 50 | define("LANG_FOLDER", "Dossier"); 51 | define("LANG_USE_ON_SERVER", "utilisé sur le serveur"); 52 | define("LANG_OF_SERVER", "du serveur"); 53 | define("LANG_TO_MUCH_SPACE", "Attention, vous utilisez beaucoup d'espace !"); 54 | define("LANG_DOWNLOADING", "EN DL"); 55 | define("LANG_NO_DOWNLOAD", "NO DL"); 56 | define("LANG_DOWNLOADING_DESC", "En cours de téléchargement"); 57 | define("LANG_NO_DOWNLOAD_DESC", "Vous n'avez jamais téléchargé"); 58 | 59 | 60 | //Account 61 | define("LANG_BASIC_INFO", "Informations de bases"); 62 | define("LANG_OLD_PASSWORD", "Ancien mot de passe"); 63 | define("LANG_NEW_PASSWORD", "Nouveau mot de passe"); 64 | define("LANG_LINK_RSS", "Lien vers le Flux RSS"); 65 | define("LANG_PROFIL_PICTURE", "Avatar"); 66 | define("LANG_MANAGE_MY_PROFIL_PICTURE", "Gérer mon avatar"); 67 | 68 | //Connect 69 | define("LANG_CONNECTION", "Connexion"); 70 | define("LANG_LOG_IN", "Connectez-vous"); 71 | 72 | //Download 73 | define("LANG_YOU_WILL_DOWNLOAD", "Vous allez télécharger le "); 74 | define("LANG_START_DOWNLOAD", "Démarrer le téléchargement"); 75 | define("LANG_ERROR_INEXISTING", "Erreur : téléchargement inéxistant !"); 76 | 77 | //INBOX 78 | define("LANG_REPLY", "Répondre"); 79 | define("LANG_DELETE_MESSAGE", "Supprimer ce message"); 80 | define("LANG_MESSAGE_SENDED", "Envoi du message réussi !"); 81 | define("LANG_CONVERSATION_WITH", "Conversation avec"); 82 | 83 | 84 | //Charts 85 | define("LANG_NETWORK", "Transferts"); 86 | define("LANG_MY_NETWORK", "Mes transferts"); 87 | define("LANG_THIS_MONTH", "ce mois"); 88 | define("LANG_MONTH_UPLOAD_USE", "Utilisation de l'upload ce mois"); 89 | define("LANG_MONTH_DOWNLOAD_USE", "Utilisation du download ce mois"); 90 | define("LANG_DISK_SPACE", "Espace disque"); 91 | 92 | //Controllers actions 93 | define("LANG_NO_MORE_SPACE", "Il reste plus assez d'espace sur le serveur !"); 94 | define("LANG_CANT_START_TORRENT", "Le torrent ne peut être démarré"); 95 | define("LANG_CANT_STOP_TORRENT", "Le torrent ne peut être stoppé"); 96 | define("LANG_TORRENT_ADDED", "Ajout du torrent effectué"); 97 | define("LANG_CANT_ADD_TORRENT", "Impossible d'ajouter ce torrent"); 98 | define("LANG_CANT_ADD_TORRENT_ALREADY", "Ajout impossible de ce torrent car vous le téléchargez déjà !"); 99 | define("LANG_CANT_DELETE_TORRENT", "Le torrent ne peut être supprimé !"); 100 | define("LANG_CHOOSE_TORRENT", "Veuillez choisir un fichier .torrent !"); 101 | define("LANG_FREE_SPACE", "Espace libre"); 102 | 103 | //Controllers admin 104 | define("LANG_PASSWORD_DIFERENT", "Les nouveaux mot de passe le correspondent pas !"); 105 | define("LANG_OLD_PASSWORD_DIFERENT", "L'ancien mot de passe ne correspond pas !"); 106 | define("LANG_CANT_CREATE_FOLDER", "Problème de création du dossier de la boxe !"); 107 | 108 | //Account 109 | define("LANG_PASSWORD_CHANGED", "Le mot de passe a bien été changé !"); 110 | 111 | //Titles 112 | define("LANG_TITLE_ADMINISTRATION", "Administration"); 113 | define("LANG_TITLE_MY_ACCOUNT", "Mon compte"); 114 | define("LANG_TITLE_MY_TORRENTS", "Mes torrents"); 115 | 116 | ?> 117 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cclleemm/FriendlyTorrent/3ad1adf5357b430a1fe89bfa1bf3a52dd10f8e45/src/favicon.ico -------------------------------------------------------------------------------- /src/index.php: -------------------------------------------------------------------------------- 1 | action = $action; 65 | $controller->setTitle(); 66 | call_user_func_array(array($controller,$action),$params); 67 | //$controller->$action(); 68 | 69 | $paramsString = ''; 70 | foreach($params as $key=>$value){ 71 | $paramsString .= '/'.$value; 72 | } 73 | //$core->bdd->sendReport($nameController.'/'.$action.$paramsString); 74 | } 75 | else{ 76 | echo '/!\\ Error 404 /!\\'; 77 | } 78 | ?> -------------------------------------------------------------------------------- /src/models/cloud.php: -------------------------------------------------------------------------------- 1 | bdd->query($sql)); 34 | 35 | if($rslt['id'] != NULL){ 36 | $this->boxeData = $rslt; 37 | } 38 | } 39 | 40 | function scanDossier($rep){ 41 | 42 | $racine=@opendir($rep); 43 | $taille=0; 44 | 45 | while($dossier=@readdir($racine)){ 46 | if(!in_array($dossier, array("..", ".", ".config"))){ 47 | 48 | $stat = stat("$rep/$dossier"); 49 | 50 | 51 | 52 | if(is_dir("$rep/$dossier")){ 53 | $this->scanDossier("$rep/$dossier"); 54 | $type = 'FOLDER'; 55 | $taille = 0; 56 | }else{ 57 | $taille=@filesize("$rep/$dossier"); 58 | $tab = explode('.', $dossier); 59 | $ext = $tab[count($tab)-1]; 60 | $type = 'FILE'; 61 | } 62 | 63 | //if($stat['mtime'] >= $this->boxeData['lastScan']){ 64 | $sql = "SELECT * FROM files WHERE link = '".addslashes($rep).'/'.addslashes($dossier)."' AND type = '".$type."' AND idBoxe = '".$this->boxeData['id']."';"; 65 | $rst = mysql_query($sql); 66 | $rslt = mysql_fetch_assoc($rst); 67 | 68 | if(!$rslt['id']){ 69 | mysql_query('INSERT INTO files VALUES("", "'.addslashes($rep).'/'.addslashes($dossier).'", "'.$type.'", "'.$this->boxeData['id'].'", "'.$taille.'", "'.time().'");'); 70 | $id = mysql_insert_id(); 71 | } 72 | //} 73 | } 74 | } 75 | @closedir($racine); 76 | 77 | mysql_query('UPDATE users SET lastScan = "'.time().'" WHERE id = "'.$this->boxeData['id'].'"'); 78 | } 79 | 80 | function scanDossierSup(){ 81 | 82 | $sql = "SELECT * FROM files WHERE idBoxe = '".$this->boxeData['id']."'"; 83 | $rst = mysql_query($sql); 84 | while($rslt = mysql_fetch_assoc($rst)){ 85 | if (!file_exists(stripcslashes($rslt['link']))){ 86 | mysql_query('DELETE FROM files WHERE id = "'.$rslt['id'].'"'); 87 | mysql_query('DELETE FROM actions WHERE idFile = "'.$rslt['id'].'"'); 88 | mysql_query('DELETE FROM downloads WHERE idFichier = "'.$rslt['id'].'"'); 89 | } 90 | } 91 | } 92 | 93 | function scan(){ 94 | $this->scanDossier($this->boxeData['boxe']); 95 | $this->scanDossierSup(); 96 | } 97 | 98 | } 99 | 100 | ?> -------------------------------------------------------------------------------- /src/models/download.php: -------------------------------------------------------------------------------- 1 | idFile = $idFichier; 22 | 23 | if($id){ 24 | $sql = "SELECT * FROM downloads WHERE clef = '".$id."';"; 25 | $rst = $this->bdd->query($sql); 26 | $this->downloadData = mysql_fetch_assoc($rst); 27 | }else if($idFichier){ 28 | $sql = "SELECT * FROM downloads WHERE idFichier = '".$idFichier."';"; 29 | $rst = $this->bdd->query($sql); 30 | $this->downloadData = mysql_fetch_assoc($rst); 31 | if(!$this->downloadData){ 32 | $clef = $this->creatDownloads($idFichier); 33 | $this->bdd->clear(); 34 | $sql = "SELECT * FROM downloads WHERE idFichier = '".$idFichier."';"; 35 | $rst = $this->bdd->query($sql); 36 | $this->downloadData = mysql_fetch_assoc($rst); 37 | } 38 | } 39 | } 40 | 41 | function ifDownloaded(){ 42 | 43 | $sql = "SELECT * FROM checkDownload WHERE idFile = '".$this->idFile."' AND idUser = '".Core::idCo()."';"; 44 | $rst = $this->bdd->query($sql); 45 | $rslt = mysql_fetch_assoc($rst); 46 | 47 | if($rslt['idFile']){ 48 | return true; 49 | }else{ 50 | $sql45 = "SELECT * FROM files WHERE id = '".$this->idFile."';"; 51 | $rst45 = mysql_query($sql45); 52 | $rslt45 = mysql_fetch_assoc($rst45); 53 | $user = new User(); 54 | 55 | return Tools::getDownloadFtpLogUsers($rslt45['link'], $user->userLogin); 56 | } 57 | } 58 | 59 | function downloaded(){ 60 | if(Core::isCo()){ 61 | $sql = "INSERT INTO checkDownload VALUE('".$this->downloadData['idFichier']."', '".Core::idCo()."', '".time()."');"; 62 | $this->bdd->query($sql); 63 | }else{ 64 | $ip = $_SERVER['REMOTE_ADDR']; 65 | $sql = "SELECT * FROM sessions WHERE ip = '".$ip."' ORDER BY lastTime DESC LIMIT 0,1;"; 66 | $rst = $this->bdd->query($sql); 67 | if($rslt['idUser']){ 68 | $sql = "INSERT INTO checkDownload VALUE('".$this->downloadData['idFichier']."', '".$rslt['idUser']."', '".time()."');"; 69 | $this->bdd->query($sql); 70 | } 71 | } 72 | } 73 | 74 | function creatDownloads($idFichier){ 75 | 76 | $sql = "SELECT * FROM files WHERE id = '".$idFichier."';"; 77 | $rst = $this->bdd->query($sql); 78 | $rslt = mysql_fetch_assoc($rst); 79 | 80 | if($rslt['id']){ 81 | 82 | $clef = md5($rslt['link']); 83 | 84 | $link = $rslt['link']; 85 | 86 | $sql = "INSERT INTO downloads VALUE('".$clef."', '".$rslt['id']."', '".addslashes($link)."');"; 87 | $rst = $this->bdd->query($sql); 88 | 89 | return $clef; 90 | }else{ 91 | return false; 92 | } 93 | } 94 | 95 | } 96 | 97 | ?> -------------------------------------------------------------------------------- /src/models/rpc.php: -------------------------------------------------------------------------------- 1 | get(array(), $afields); 17 | $torrentlist = $response['arguments']['torrents']; 18 | 19 | if (!empty($torrentlist)) { 20 | foreach ($torrentlist as $aTorrent) { 21 | if ( $aTorrent['hashString'] == $transfer ) 22 | return $aTorrent; 23 | } 24 | } 25 | return false; 26 | } 27 | 28 | /** 29 | * checks if transfer is Transmission 30 | * 31 | * @param $transfer hash of the transfer 32 | * @return boolean 33 | */ 34 | function isTransmissionTransfer($transfer, $instance) { 35 | $aTorrent = getTransmissionTransfer($transfer, array(), $instance); 36 | return is_array($aTorrent); 37 | } 38 | 39 | /** 40 | * This method retrieves the current ID in transmission for the transfer that matches the $hash hash 41 | * 42 | * @return transmissionTransferId 43 | */ 44 | function getTransmissionTransferIdByHash($hash, $instance) { 45 | $transmissionTransferId = false; 46 | $rpc = $instance; 47 | $response = $rpc->get(array(), array('id','hashString')); 48 | if ( $response['result'] != "success" ) rpc_error("Getting ID for Hash failed: ".$response['result']); 49 | $torrentlist = $response['arguments']['torrents']; 50 | foreach ($torrentlist as $aTorrent) { 51 | if ( $aTorrent['hashString'] == $hash ) { 52 | $transmissionTransferId = $aTorrent['id']; 53 | break; 54 | } 55 | } 56 | return $transmissionTransferId; 57 | } 58 | 59 | /** 60 | * This method starts the Transmission transfer with the matching hash 61 | * 62 | * @return void 63 | */ 64 | function startTransmissionTransfer($hash,$startPaused=false,$params=array(), $instance) { 65 | 66 | $rpc = $instance; 67 | $transmissionId = getTransmissionTransferIdByHash($hash, $instance); 68 | $response = $rpc->set($transmissionId, array_merge(array("seedRatioMode" => 1), $params) ); 69 | $response = $rpc->start($transmissionId); 70 | if ( $response['result'] != "success" ) { 71 | rpc_error("Start failed", "", "", $response['result']); 72 | return false; 73 | } 74 | return true; 75 | } 76 | 77 | /** 78 | * This method stops the Transmission transfer with the matching hash 79 | * 80 | * @return boolean 81 | */ 82 | function stopTransmissionTransfer($hash, $instance) { 83 | 84 | $transmissionId = getTransmissionTransferIdByHash($hash, $instance); 85 | $response = $instance->stop($transmissionId); 86 | if ( $response['result'] != "success" ) return false; 87 | 88 | return true; 89 | } 90 | 91 | /** 92 | * convertTime 93 | * 94 | * @param $seconds 95 | * @return common time-delta-string 96 | */ 97 | function convertTime($seconds) { 98 | // sanity-check 99 | if ($seconds < -1) $seconds=0-$seconds; 100 | // one week is enough 101 | if ($seconds >= 604800) return '-'; 102 | // format time-delta 103 | $periods = array (/* 31556926, 2629743, 604800,*/ 86400, 3600, 60, 1); 104 | $seconds = floatval($seconds); 105 | $values = array(); 106 | $leading = true; 107 | foreach ($periods as $period) { 108 | $count = floor($seconds / $period); 109 | if ($leading) { 110 | if ($count == 0) 111 | continue; 112 | $leading = false; 113 | } 114 | array_push($values, ($count < 10) ? "0".$count : $count); 115 | $seconds = $seconds % $period; 116 | } 117 | return (empty($values)) ? "?" : implode(':', $values); 118 | } 119 | 120 | /** 121 | * This method deletes the Transmission transfer with the matching hash, without removing the data 122 | * 123 | * @return void 124 | * TODO: test delete :) 125 | */ 126 | function deleteTransmissionTransfer($hash, $deleteData = false, $rpc) { 127 | 128 | $transmissionId = getTransmissionTransferIdByHash($hash, $rpc); 129 | $response = $rpc->remove($transmissionId,$deleteData); 130 | if ( $response['result'] != "success" ) 131 | return false; 132 | 133 | return true; 134 | } 135 | 136 | /** 137 | * convertTimeText 138 | * 139 | * @param $seconds 140 | * @return textual remaining time 141 | */ 142 | function convertTimeText($seconds) { 143 | $hour_fmt = convertTime($seconds); 144 | if ($hour_fmt == '-') 145 | return '-'; 146 | $parts = explode(':',$hour_fmt); 147 | if (count($parts) >= 4) 148 | return $parts[0]."d."; 149 | elseif (count($parts) == 3) 150 | return $parts[0]."h."; 151 | elseif (count($parts) == 2) 152 | return $parts[0]."m."; 153 | else 154 | return $parts[0]."s."; 155 | } 156 | 157 | /** 158 | * Returns a string in format of TB, GB, MB, or kB depending on the size 159 | * 160 | * @param $inBytes 161 | * @return string 162 | */ 163 | function formatBytesTokBMBGBTB($inBytes) { 164 | if(!is_numeric($inBytes)) return ""; 165 | if ($inBytes > 1099511627776) 166 | return round($inBytes / 1099511627776, 2) . " TB"; 167 | elseif ($inBytes > 1073741824) 168 | return round($inBytes / 1073741824, 2) . " GB"; 169 | elseif ($inBytes > 1048576) 170 | return round($inBytes / 1048576, 1) . " MB"; 171 | elseif ($inBytes > 1024) 172 | return round($inBytes / 1024, 1) . " kB"; 173 | else 174 | return $inBytes . " B"; 175 | } 176 | ?> 177 | -------------------------------------------------------------------------------- /src/models/rss.php: -------------------------------------------------------------------------------- 1 | channel->item as $item){ 26 | $sql = "SELECT * FROM cacheRss WHERE url = '".addslashes($item->link)."';"; 27 | $rst = $this->bdd->query($sql); 28 | $rslt = mysql_fetch_assoc($rst); 29 | 30 | if($rslt['id']){ 31 | $id = $rslt['id']; 32 | $nameFile = $rslt['nameFile']; 33 | }else{ 34 | 35 | $h = get_headers($this->get_final_url($item->link)); 36 | 37 | foreach($h as $key => $value){ 38 | if(strpos($value,'filename') !== FALSE){ 39 | $e = explode("\"", $value); 40 | - $nameFile = $e[1]; 41 | } 42 | } 43 | 44 | if($nameFile){ 45 | $sqlI = "INSERT INTO cacheRss VALUE('', '".addslashes($item->link)."', '".addslashes($nameFile)."');"; 46 | $this->bdd->query($sqlI); 47 | $id = mysql_insert_id(); 48 | } 49 | } 50 | 51 | if($nameFile){ 52 | $nameFile = str_replace(".torrent", "", $nameFile); 53 | 54 | $user = new User(); 55 | 56 | $sql = "SELECT torrents.id, users.id as uid, users.login FROM torrents, users WHERE users.id = torrents.idBoxe AND name = '".addslashes($nameFile)."-".md5($user->userData['id'])."';"; 57 | $rst = $this->bdd->query($sql); 58 | $rslt = mysql_fetch_assoc($rst); 59 | 60 | if($rslt['id']){ 61 | if($rslt['uid'] == Core::idCo()){ 62 | $etat = "Téléchargement en cours"; 63 | $dl = false; 64 | }else{ 65 | $etat = "Téléchargement en cours par ".$rslt['login']; 66 | $dl = true; 67 | } 68 | }else{ 69 | $etat = "A télécharger"; 70 | $dl = true; 71 | } 72 | 73 | $this->rssData[] = array('name' => $item->title, 'id' => $id, 'etat' => $etat, 'isDwn' => $dl); 74 | }else{ 75 | $this->rssData[] = array('name' => array('Flux Rss incorrect'), 'id' => 0, 'etat' => 'Erreur', 'isDwn' => 0); 76 | } 77 | } 78 | } 79 | } 80 | 81 | /** 82 | * get_redirect_url() 83 | * Gets the address that the provided URL redirects to, 84 | * or FALSE if there's no redirect. 85 | * 86 | * @param string $url 87 | * @return string 88 | */ 89 | function get_redirect_url($url){ 90 | $redirect_url = null; 91 | 92 | $url_parts = @parse_url($url); 93 | if (!$url_parts) return false; 94 | if (!isset($url_parts['host'])) return false; //can't process relative URLs 95 | if (!isset($url_parts['path'])) $url_parts['path'] = '/'; 96 | 97 | $sock = fsockopen($url_parts['host'], (isset($url_parts['port']) ? (int)$url_parts['port'] : 80), $errno, $errstr, 30); 98 | if (!$sock) return false; 99 | 100 | $request = "HEAD " . $url_parts['path'] . (isset($url_parts['query']) ? '?'.$url_parts['query'] : '') . " HTTP/1.1\r\n"; 101 | $request .= 'Host: ' . $url_parts['host'] . "\r\n"; 102 | $request .= "Connection: Close\r\n\r\n"; 103 | fwrite($sock, $request); 104 | $response = ''; 105 | while(!feof($sock)) $response .= fread($sock, 8192); 106 | fclose($sock); 107 | 108 | if (preg_match('/^Location: (.+?)$/m', $response, $matches)){ 109 | if ( substr($matches[1], 0, 1) == "/" ) 110 | return $url_parts['scheme'] . "://" . $url_parts['host'] . trim($matches[1]); 111 | else 112 | return trim($matches[1]); 113 | 114 | } else { 115 | return false; 116 | } 117 | 118 | } 119 | 120 | /** 121 | * get_all_redirects() 122 | * Follows and collects all redirects, in order, for the given URL. 123 | * 124 | * @param string $url 125 | * @return array 126 | */ 127 | function get_all_redirects($url){ 128 | $redirects = array(); 129 | while ($newurl = $this->get_redirect_url($url)){ 130 | if (in_array($newurl, $redirects)){ 131 | break; 132 | } 133 | $redirects[] = $newurl; 134 | $url = $newurl; 135 | } 136 | return $redirects; 137 | } 138 | 139 | /** 140 | * get_final_url() 141 | * Gets the address that the URL ultimately leads to. 142 | * Returns $url itself if it isn't a redirect. 143 | * 144 | * @param string $url 145 | * @return string 146 | */ 147 | function get_final_url($url){ 148 | $redirects = $this->get_all_redirects($url); 149 | if (count($redirects)>0){ 150 | return array_pop($redirects); 151 | } else { 152 | return $url; 153 | } 154 | } 155 | 156 | } 157 | 158 | ?> 159 | -------------------------------------------------------------------------------- /src/models/rssm.php: -------------------------------------------------------------------------------- 1 | '; 25 | print_r($fluxrss); 26 | echo "";*/ 27 | 28 | foreach($fluxrss->channel->item as $item){ 29 | $sql = "SELECT * FROM cacheRss WHERE url = '".addslashes($item->link)."';"; 30 | $rst = $this->bdd->query($sql); 31 | $rslt = mysql_fetch_assoc($rst); 32 | 33 | if($rslt['id']){ 34 | $id = $rslt['id']; 35 | $nameFile = $rslt['nameFile']; 36 | }else{ 37 | $h = get_headers($item->link); 38 | $e = explode("\"", $h[3]); 39 | $e2 = explode("\"", $e[1]); 40 | $nameFile = $e2[0]; 41 | 42 | if($nameFile != NULL) { 43 | $sqlI = "INSERT INTO cacheRss VALUE('', '".addslashes($item->link)."', '".addslashes($nameFile)."');"; 44 | $this->bdd->query($sqlI); 45 | $id = mysql_insert_id(); 46 | } 47 | } 48 | 49 | $nameFile = str_replace(".torrent", "", $nameFile); 50 | 51 | $item->title=strip_tags($item->title); 52 | 53 | if($nameFile != NULL) { 54 | $user = new User(); 55 | 56 | $sql = "SELECT torrents.id, users.id as uid, users.login FROM torrents, users WHERE users.id = '".Core::idCo()."' AND users.id = torrents.idBoxe AND name = '".addslashes($nameFile)."-".md5($user->userData['id'])."';"; 57 | $rst = $this->bdd->query($sql); 58 | $rslt = mysql_fetch_assoc($rst); 59 | 60 | if($rslt['id']){ 61 | $etat = "Téléchargement en cours"; 62 | $dl = false; 63 | }else{ 64 | $etat = "A télécharger"; 65 | $dl = true; 66 | } 67 | 68 | $this->rssData[] = array('name' => $item->title, 'description' => $item->description, 'id' => $id, 'etat' => $etat, 'isDwn' => $dl); 69 | }else{ 70 | $this->rssData[] = array('name' => $item->title, 'description' => $item->description, 'id' => null, 'etat' => 'Fichier non torrent', 'isDwn' => false); 71 | } 72 | } 73 | } 74 | } 75 | } 76 | 77 | ?> -------------------------------------------------------------------------------- /src/models/torrents.php: -------------------------------------------------------------------------------- 1 | configRPC()); 26 | } catch (Exception $e) { 27 | echo "unable to connect to transmission-daemon\n"; 28 | return; 29 | } 30 | 31 | $tfs = $rpc->torrent_get_tf(); 32 | 33 | if (empty($tfs)) { 34 | return; 35 | } 36 | 37 | $sql = "SELECT hash, name FROM torrents WHERE idBoxe = '".$idUser."'"; 38 | 39 | $hashes = array("''"); 40 | foreach ($tfs as $hash => $t) { 41 | $hashes[] = "'".strtolower($hash)."'"; 42 | } 43 | $sql .= " AND hash IN (".implode(',',$hashes).")"; 44 | 45 | $rst = $this->bdd->query($sql); 46 | 47 | $hashes=array(); 48 | $sharekills=array(); 49 | while ($rslt = mysql_fetch_assoc($rst)) { 50 | $hash = strtolower($rslt['hash']); 51 | $hashes[$hash] = $rslt['name']; 52 | 53 | if (!isset($tfs[$hash])) { 54 | $sf = new StatFile(ROOT_DOWNLOADS.'.transferts/'.$rslt['name']); 55 | $sf->running = 0; 56 | $sf->write(); 57 | } 58 | } 59 | 60 | $totalRateUp = 0; 61 | $totalRateDown = 0; 62 | $nbUpdate=0; 63 | $missing=array(); 64 | foreach ($tfs as $hash => $t) { 65 | $transfer = $hashes[$hash]; 66 | 67 | //file_put_contents($cfg["path"].'.Transmission/'."updateStatFiles4.log",serialize($t)); 68 | $sf = new StatFile(ROOT_DOWNLOADS.'.transferts/'.$transfer); 69 | 70 | $sf->running = $t['running']; 71 | $sf->eta = $t['eta']; 72 | 73 | if ($sf->running) { 74 | 75 | if ($t['eta'] > 0) { 76 | $sf->time_left = convertTimeText($t['eta']); 77 | }else{ 78 | $sf->time_left = '-'; 79 | } 80 | 81 | $sf->percent_done = $t['percentDone']; 82 | $sf->sharing = round($t['sharing'],1); 83 | 84 | if ($t['status'] != 9 && $t['status'] != 5) { 85 | $sf->peers = $t['peers']; 86 | $sf->seeds = $t['seeds']; 87 | } 88 | 89 | if ($t['seeds'] >= 0) 90 | $sf->seeds = $t['seeds']; 91 | 92 | if ($t['peers'] >= 0) 93 | $sf->peers = $t['peers']; 94 | 95 | $sf->peersList = $t['peersList']; 96 | $sf->files = $t['files']; 97 | 98 | if ($t['cons'] >= 0) 99 | $sf->cons = $t['cons']; 100 | 101 | if ((float)$t['speedDown'] >= 0.0) 102 | $sf->down_speed = formatBytesTokBMBGBTB($t['speedDown'])."/s"; 103 | if ((float)$t['speedUp'] >= 0.0) 104 | $sf->up_speed = formatBytesTokBMBGBTB($t['speedUp'])."/s"; 105 | 106 | $totalRateUp += $t['speedUp']; 107 | $totalRateDown += $t['speedDown']; 108 | 109 | if ($t['status'] == 8) { 110 | $sf->percent_done = 100 + $t['sharing']; 111 | $sf->down_speed = " "; 112 | if (trim($sf->up_speed) == '') 113 | $sf->up_speed = " "; 114 | } 115 | if ($t['status'] == 9) { 116 | $sf->percent_done = 100 + $t['sharing']; 117 | $sf->up_speed = " "; 118 | $sf->down_speed = " "; 119 | } 120 | 121 | if($t['trackerStats'][0]['lastAnnounceSucceeded'] != 1){ 122 | $sf->time_left = $t['trackerStats'][0]['lastAnnounceResult']; 123 | $sf->error = 1; 124 | } 125 | 126 | /*echo '
';
127 | 					print_r($t);
128 | 					echo '
';*/ 129 | 130 | /*if($t['error']){ 131 | $sf->time_left = $t['errorString']; 132 | $sf->error = 1; 133 | }*/ 134 | 135 | } else { 136 | //Stopped or finished... 137 | 138 | $sf->down_speed = ""; 139 | $sf->up_speed = ""; 140 | $sf->peers = ""; 141 | $sf->time_left = "-"; 142 | if ($t['eta'] < -1) { 143 | $sf->time_left = "Done in ".convertTimeText($t['eta']); 144 | } elseif ($sf->percent_done >= 100 && strpos($sf->time_left, 'Done') === false && strpos($sf->time_left, 'Finished') === false) { 145 | $sf->percent_done = 100; 146 | $sf->time_left = "Done!"; 147 | } 148 | 149 | if ($sf->sharing == 0) 150 | $sf->sharing = round($t['sharing'],1); 151 | } 152 | 153 | $sf->downtotal = $t['downTotal']; 154 | $sf->uptotal = $t['upTotal']; 155 | 156 | if ($sf->size == 0) 157 | $sf->size = $t['size']; 158 | 159 | echo $sf->seeds.'|'; 160 | 161 | if ($sf->write()) { 162 | $nbUpdate++; 163 | } 164 | 165 | } 166 | 167 | $this->bdd->getCache()->set($idUser."Stats", array('rateUp' => $totalRateUp, 'rateDown' => $totalRateDown), false, 10); 168 | } 169 | 170 | function refresh(){ 171 | $sql = "SELECT id FROM users;"; 172 | $rst = $this->bdd->query($sql); 173 | while($rslt = mysql_fetch_assoc($rst)){ 174 | $this->updateStatFiles($rslt['id']); 175 | } 176 | } 177 | 178 | function myTorrents(){ 179 | 180 | $torrents = array(); 181 | 182 | $sql = "SELECT id FROM torrents WHERE idBoxe = '".Core::idCo()."' ORDER BY time DESC;"; 183 | $rst = $this->bdd->query($sql); 184 | while($rslt = mysql_fetch_assoc($rst)){ 185 | $torrent = new Torrent($rslt['id']); 186 | $torrents[] = $torrent; 187 | } 188 | 189 | return $torrents; 190 | } 191 | 192 | function torrents(){ 193 | 194 | $torrents = array(); 195 | 196 | $sql = "SELECT id FROM torrents ORDER BY time DESC;"; 197 | $rst = $this->bdd->query($sql); 198 | while($rslt = mysql_fetch_assoc($rst)){ 199 | $torrent = new Torrent($rslt['id']); 200 | $torrents[] = $torrent; 201 | } 202 | 203 | return $torrents; 204 | } 205 | 206 | } 207 | 208 | ?> -------------------------------------------------------------------------------- /src/models/xfer.php: -------------------------------------------------------------------------------- 1 | userId){ 24 | 25 | $year = date('Y'); 26 | $month = date('n'); 27 | 28 | $sql = "SELECT * FROM xferUser WHERE idUser = '".$user->userId."' AND `year` = '".$year."' AND `month` = '".$month."';"; 29 | $rst = $this->bdd->query($sql); 30 | $rslt = mysql_fetch_assoc($rst); 31 | 32 | if(!$rslt['idUser']){ 33 | $sql = "INSERT INTO xferUser VALUES ('".$user->userId."', '".$year."', '".$month."', '0', '0');"; 34 | $rst = $this->bdd->query($sql); 35 | } 36 | 37 | $this->xfer = $user->userId; 38 | } 39 | } 40 | } 41 | 42 | function addValue($up = 0, $down = 0){ 43 | $year = date('Y'); 44 | $month = date('n'); 45 | 46 | if($up != 0 || $down != 0){ 47 | $sql = "UPDATE xferUser SET totalUp = totalUp + '".$up."', totalDown = totalDown + '".$down."' WHERE idUser = '".$this->xfer."' AND year = '".$year."' AND month = '".$month."';"; 48 | $rst = $this->bdd->query($sql); 49 | } 50 | } 51 | 52 | function getStat($year = 0, $month = 0){ 53 | 54 | if(!$year) 55 | $year = date('Y'); 56 | if(!$month) 57 | $month = date('n'); 58 | 59 | $sql = "SELECT * FROM xferUser WHERE idUser = '".$this->xfer."' AND year = '".$year."' AND month = '".$month."';"; 60 | $rst = $this->bdd->query($sql); 61 | $rslt = mysql_fetch_assoc($rst); 62 | 63 | return (array('down' => $rslt['totalDown'], 'up' => $rslt['totalUp'])); 64 | } 65 | 66 | function getStatTotal($year = 0, $month = 0){ 67 | 68 | if(!$year) 69 | $year = date('Y'); 70 | if(!$month) 71 | $month = date('n'); 72 | 73 | $sql = "SELECT SUM(totalDown) as totalDown, SUM(totalUp) as totalUp FROM xferUser WHERE year = '".$year."' AND month = '".$month."';"; 74 | $rst = $this->bdd->query($sql); 75 | $rslt = mysql_fetch_assoc($rst); 76 | 77 | return (array('down' => $rslt['totalDown'], 'up' => $rslt['totalUp'])); 78 | } 79 | 80 | function scanTorrent($id = null){ 81 | 82 | if($id) 83 | $sqlPlus = ' id = "'.$id.'" AND'; 84 | else 85 | $sqlPlus = ''; 86 | 87 | $sql = "SELECT id, name FROM torrents WHERE".$sqlPlus." idBoxe = '".$this->xfer."';"; 88 | $rst = $this->bdd->query($sql); 89 | while($rslt = mysql_fetch_assoc($rst)){ 90 | 91 | $stat = new StatFile(ROOT_DOWNLOADS.'.transferts/'.$rslt['name']); 92 | 93 | if($stat->running == 1){ 94 | $sql45 = "SELECT * FROM xferTorrent WHERE idTorrent = '".$rslt['id']."';"; 95 | $rst45 = $this->bdd->query($sql45); 96 | $rslt45 = mysql_fetch_assoc($rst45); 97 | if(!$rslt45['idTorrent']){ 98 | $upAdd = $stat->uptotal; 99 | $downAdd = $stat->downtotal; 100 | 101 | $sql55 = "INSERT INTO xferTorrent VALUES ('".$rslt['id']."', '".$stat->uptotal."', '".$stat->downtotal."');"; 102 | $this->bdd->query($sql55); 103 | }else{ 104 | if($stat->uptotal != $rslt45['lastUp'] || $stat->downtotaltotal != $rslt45['lastDown']){ 105 | $upAdd = $stat->uptotal - $rslt45['lastUp']; 106 | $downAdd = $stat->downtotal - $rslt45['lastDown']; 107 | 108 | $sql55 = "UPDATE xferTorrent SET lastUp = '".$stat->uptotal."', lastDown = '".$stat->downtotal."' WHERE idTorrent = '".$rslt['id']."';"; 109 | $this->bdd->query($sql55); 110 | } 111 | } 112 | $this->addValue($upAdd, $downAdd); 113 | } 114 | } 115 | } 116 | 117 | function deleteTorrent($id){ 118 | $this->scanTorrent($id); 119 | $sql = "DELETE FROM xferTorrent WHERE idTorrent = '".$id."';"; 120 | $this->bdd->query($sql); 121 | } 122 | 123 | } 124 | 125 | ?> -------------------------------------------------------------------------------- /src/theme/css/facelist.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | /* TextboxList sample CSS */ 4 | ul.facelist { 5 | list-style: none; 6 | margin: 0; 7 | overflow: hidden; 8 | height: auto !important; 9 | height: 1%; 10 | padding: 4px 5px 0; 11 | width: 100%; 12 | } 13 | ul.facelist li { 14 | float: left; 15 | margin: 0 5px 4px 0; 16 | } 17 | ul.facelist li.token-input input { 18 | width: 150px; 19 | margin: 0; 20 | border: 1px solid #FFF; 21 | outline: 0; 22 | padding: 3px 0 2px; 23 | } /* no left/right padding here please */ 24 | 25 | ul.facelist li.token-input { 26 | margin: 0; 27 | width: 5px; 28 | } 29 | .token{ 30 | color:black; 31 | position: relative; 32 | background-color: #eff2f7; 33 | border: solid 1px #ccd5e4; 34 | -moz-border-radius: 3px; 35 | -webkit-border-radius: 3px; 36 | border-radius: 3px; 37 | -webkit-transition: background-color .2s ease-in; 38 | cursor: pointer; 39 | padding:2px 20px 2px 5px; 40 | } 41 | .tokenhover, 42 | .token:hover{ 43 | position: relative; 44 | background-color: #d8dfea; 45 | border: solid 1px #ccd5e4; 46 | } 47 | .token_selected:hover, 48 | .token_selected{ 49 | color:#fff; 50 | background-color: #5670a6; 51 | border: solid 1px #3b5998; 52 | } 53 | 54 | .token span.x, 55 | .token span.x_hover, 56 | .tokenhover span.x, 57 | .tokenhover span.x_hover{background-image:url('../images/token_x.gif');cursor:pointer;display:inline; width: 10px; height:9px; display: block; position:absolute; right: 0px; top: 3.5px;text-indent: -9999px;margin-right:5px;margin-top:3px;} 58 | 59 | 60 | 61 | /* Autocompleter */ 62 | 63 | #result_list { 64 | position: absolute; 65 | width: 177px; 66 | } 67 | #result_list .default { 68 | padding: 5px 7px; 69 | background: #eee; 70 | color:black; 71 | } 72 | #result_list ul { 73 | margin: 0; 74 | padding: 0; 75 | background: #fff; 76 | width: 177px; 77 | } 78 | #result_list ul li { 79 | padding: 5px 12px; 80 | margin: 0; 81 | list-style-type: none; 82 | border: 0px solid #ccc; 83 | border-width: 0 1px 1px; 84 | font: 11px "Lucida Grande", "Verdana"; 85 | color:black; 86 | width: 152px; 87 | background: #fff; 88 | } 89 | #result_list ul li em { 90 | font-weight: bold; 91 | font-style: normal; 92 | background: #dae1eb; 93 | color: blue; 94 | } 95 | #result_list ul li.auto-focus { 96 | background: #029ec6; 97 | } 98 | #result_list ul li.auto-focus em { 99 | background: none; 100 | } -------------------------------------------------------------------------------- /src/theme/css/facelist_ie.css: -------------------------------------------------------------------------------- 1 | .token{background-image:url('../images/facelist/token.gif');background-repeat:no-repeat;color:black;white-space:nowrap;position: relative;} 2 | .token span{background-image:url('../images/facelist/token.gif');background-position:top right;background-repeat:no-repeat;display:block;} 3 | .token span span{background-position:bottom right;} 4 | .token span span span{background-position:bottom left;} 5 | .token span span span span{background-image:none;padding:2px 20px 2px 5px; font-size: 12px;} 6 | 7 | .tokenhover, 8 | html div.tokenizer_locked .tokenhover span{background-image:url('../images/facelist/token.gif');} 9 | .token:hover, 10 | .token:hover span{background-image:url('../images/facelist/token_hover.gif');text-decoration:none;} 11 | .token_selected, 12 | .token_selected span, 13 | .token_selected:hover, 14 | .token_selected:hover span{background-image:url('../images/facelist/token_selected.gif');color:white;text-decoration:none;} -------------------------------------------------------------------------------- /src/theme/css/farbtastic.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Farbtastic Color Picker 1.2 3 | * © 2008 Steven Wittens 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | .farbtastic { 20 | position: relative; 21 | } 22 | .farbtastic * { 23 | position: absolute; 24 | cursor: crosshair; 25 | } 26 | .farbtastic, .farbtastic .wheel { 27 | width: 195px; 28 | height: 195px; 29 | } 30 | .farbtastic .color, .farbtastic .overlay { 31 | top: 47px; 32 | left: 47px; 33 | width: 101px; 34 | height: 101px; 35 | } 36 | .farbtastic .wheel { 37 | background: url(../images/wheel.png) no-repeat; 38 | width: 195px; 39 | height: 195px; 40 | } 41 | .farbtastic .overlay { 42 | background: url(../images/mask.png) no-repeat; 43 | } 44 | .farbtastic .marker { 45 | width: 17px; 46 | height: 17px; 47 | margin: -8px 0 0 -8px; 48 | overflow: hidden; 49 | background: url(../images/marker.png) no-repeat; 50 | } 51 | 52 | -------------------------------------------------------------------------------- /src/theme/css/jquery.pnotify.default.css: -------------------------------------------------------------------------------- 1 | /* 2 | Document : jquery.pnotify.default.css 3 | Created on : Nov 23, 2009, 3:14:10 PM 4 | Author : Hunter Perrin 5 | Version : 1.2.0 6 | Link : http://pinesframework.org/pnotify/ 7 | Description: 8 | Default styling for Pines Notify jQuery plugin. 9 | */ 10 | /* -- Notice */ 11 | .ui-pnotify { 12 | top: 25px; 13 | right: 25px; 14 | position: absolute; 15 | height: auto; 16 | /* Ensures notices are above everything */ 17 | z-index: 9999; 18 | } 19 | /* Hides position: fixed from IE6 */ 20 | html > body .ui-pnotify { 21 | position: fixed; 22 | } 23 | .ui-pnotify .ui-pnotify-shadow { 24 | -webkit-box-shadow: 0px 2px 10px rgba(50, 50, 50, 0.5); 25 | -moz-box-shadow: 0px 2px 10px rgba(50, 50, 50, 0.5); 26 | box-shadow: 0px 2px 10px rgba(50, 50, 50, 0.5); 27 | } 28 | .ui-pnotify-container { 29 | background-position: 0 0; 30 | padding: .8em; 31 | height: 100%; 32 | margin: 0; 33 | } 34 | .ui-pnotify-sharp { 35 | -webkit-border-radius: 0; 36 | -moz-border-radius: 0; 37 | border-radius: 0; 38 | } 39 | .ui-pnotify-closer, .ui-pnotify-sticker { 40 | float: right; 41 | margin-left: .2em; 42 | } 43 | .ui-pnotify-title { 44 | display: block; 45 | margin-bottom: .4em; 46 | font-size: 14px 47 | } 48 | .ui-pnotify-text { 49 | display: block; 50 | } 51 | .ui-pnotify-text a { 52 | color:black; 53 | } 54 | .ui-pnotify-icon, .ui-pnotify-icon span { 55 | display: block; 56 | float: left; 57 | margin-right: .2em; 58 | } 59 | /* -- History Pulldown */ 60 | .ui-pnotify-history-container { 61 | position: absolute; 62 | top: 0; 63 | right: 18px; 64 | width: 70px; 65 | border-top: none; 66 | padding: 0; 67 | -webkit-border-top-left-radius: 0; 68 | -moz-border-top-left-radius: 0; 69 | border-top-left-radius: 0; 70 | -webkit-border-top-right-radius: 0; 71 | -moz-border-top-right-radius: 0; 72 | border-top-right-radius: 0; 73 | /* Ensures history container is above notices. */ 74 | z-index: 10000; 75 | display:none; 76 | } 77 | .ui-pnotify-history-container .ui-pnotify-history-header { 78 | padding: 2px; 79 | } 80 | .ui-pnotify-history-container button { 81 | cursor: pointer; 82 | display: block; 83 | width: 100%; 84 | } 85 | .ui-pnotify-history-container .ui-pnotify-history-pulldown { 86 | display: block; 87 | margin: 0 auto; 88 | } -------------------------------------------------------------------------------- /src/theme/css/jquery.pnotify.default.icons.css: -------------------------------------------------------------------------------- 1 | /* 2 | Document : jquery.pnotify.default.icons.css 3 | Created on : Nov 24, 2009, 2:58:21 PM 4 | Author : Hunter Perrin 5 | Version : 1.2.0 6 | Link : http://pinesframework.org/pnotify/ 7 | Description: 8 | Pines Icon styling for Pines Notify. 9 | */ 10 | 11 | .ui-pnotify .picon { 12 | background-color: transparent; 13 | background-repeat: no-repeat; 14 | background-position: center center; 15 | width: 17px; 16 | height: 17px; 17 | } 18 | .ui-pnotify-title { 19 | line-height: 17px; 20 | min-height: 17px; 21 | } -------------------------------------------------------------------------------- /src/theme/fonts/glyphicons-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cclleemm/FriendlyTorrent/3ad1adf5357b430a1fe89bfa1bf3a52dd10f8e45/src/theme/fonts/glyphicons-regular.eot -------------------------------------------------------------------------------- /src/theme/fonts/glyphicons-regular.eot@: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cclleemm/FriendlyTorrent/3ad1adf5357b430a1fe89bfa1bf3a52dd10f8e45/src/theme/fonts/glyphicons-regular.eot@ -------------------------------------------------------------------------------- /src/theme/fonts/glyphicons-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cclleemm/FriendlyTorrent/3ad1adf5357b430a1fe89bfa1bf3a52dd10f8e45/src/theme/fonts/glyphicons-regular.ttf -------------------------------------------------------------------------------- /src/theme/fonts/glyphicons-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cclleemm/FriendlyTorrent/3ad1adf5357b430a1fe89bfa1bf3a52dd10f8e45/src/theme/fonts/glyphicons-regular.woff -------------------------------------------------------------------------------- /src/theme/images/friendly-torrent-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cclleemm/FriendlyTorrent/3ad1adf5357b430a1fe89bfa1bf3a52dd10f8e45/src/theme/images/friendly-torrent-logo.png -------------------------------------------------------------------------------- /src/theme/images/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cclleemm/FriendlyTorrent/3ad1adf5357b430a1fe89bfa1bf3a52dd10f8e45/src/theme/images/home.png -------------------------------------------------------------------------------- /src/theme/images/loader.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cclleemm/FriendlyTorrent/3ad1adf5357b430a1fe89bfa1bf3a52dd10f8e45/src/theme/images/loader.png -------------------------------------------------------------------------------- /src/theme/images/login-colors.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cclleemm/FriendlyTorrent/3ad1adf5357b430a1fe89bfa1bf3a52dd10f8e45/src/theme/images/login-colors.jpg -------------------------------------------------------------------------------- /src/theme/images/token.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cclleemm/FriendlyTorrent/3ad1adf5357b430a1fe89bfa1bf3a52dd10f8e45/src/theme/images/token.gif -------------------------------------------------------------------------------- /src/theme/images/token_hover.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cclleemm/FriendlyTorrent/3ad1adf5357b430a1fe89bfa1bf3a52dd10f8e45/src/theme/images/token_hover.gif -------------------------------------------------------------------------------- /src/theme/images/token_selected.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cclleemm/FriendlyTorrent/3ad1adf5357b430a1fe89bfa1bf3a52dd10f8e45/src/theme/images/token_selected.gif -------------------------------------------------------------------------------- /src/theme/images/token_x.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cclleemm/FriendlyTorrent/3ad1adf5357b430a1fe89bfa1bf3a52dd10f8e45/src/theme/images/token_x.gif -------------------------------------------------------------------------------- /src/theme/scripts/flot/jquery.flot.resize.js: -------------------------------------------------------------------------------- 1 | /* 2 | Flot plugin for automatically redrawing plots when the placeholder 3 | size changes, e.g. on window resizes. 4 | 5 | It works by listening for changes on the placeholder div (through the 6 | jQuery resize event plugin) - if the size changes, it will redraw the 7 | plot. 8 | 9 | There are no options. If you need to disable the plugin for some 10 | plots, you can just fix the size of their placeholders. 11 | */ 12 | 13 | 14 | /* Inline dependency: 15 | * jQuery resize event - v1.1 - 3/14/2010 16 | * http://benalman.com/projects/jquery-resize-plugin/ 17 | * 18 | * Copyright (c) 2010 "Cowboy" Ben Alman 19 | * Dual licensed under the MIT and GPL licenses. 20 | * http://benalman.com/about/license/ 21 | */ 22 | (function($,h,c){var a=$([]),e=$.resize=$.extend($.resize,{}),i,k="setTimeout",j="resize",d=j+"-special-event",b="delay",f="throttleWindow";e[b]=250;e[f]=true;$.event.special[j]={setup:function(){if(!e[f]&&this[k]){return false}var l=$(this);a=a.add(l);$.data(this,d,{w:l.width(),h:l.height()});if(a.length===1){g()}},teardown:function(){if(!e[f]&&this[k]){return false}var l=$(this);a=a.not(l);l.removeData(d);if(!a.length){clearTimeout(i)}},add:function(l){if(!e[f]&&this[k]){return false}var n;function m(s,o,p){var q=$(this),r=$.data(this,d);r.w=o!==c?o:q.width();r.h=p!==c?p:q.height();n.apply(this,arguments)}if($.isFunction(l)){n=l;return m}else{n=l.handler;l.handler=m}}};function g(){i=h[k](function(){a.each(function(){var n=$(this),m=n.width(),l=n.height(),o=$.data(this,d);if(m!==o.w||l!==o.h){n.trigger(j,[o.w=m,o.h=l])}});g()},e[b])}})(jQuery,this); 23 | 24 | 25 | (function ($) { 26 | var options = { }; // no options 27 | 28 | function init(plot) { 29 | function onResize() { 30 | var placeholder = plot.getPlaceholder(); 31 | 32 | // somebody might have hidden us and we can't plot 33 | // when we don't have the dimensions 34 | if (placeholder.width() == 0 || placeholder.height() == 0) 35 | return; 36 | 37 | plot.resize(); 38 | plot.setupGrid(); 39 | plot.draw(); 40 | } 41 | 42 | function bindEvents(plot, eventHolder) { 43 | plot.getPlaceholder().resize(onResize); 44 | } 45 | 46 | function shutdown(plot, eventHolder) { 47 | plot.getPlaceholder().unbind("resize", onResize); 48 | } 49 | 50 | plot.hooks.bindEvents.push(bindEvents); 51 | plot.hooks.shutdown.push(shutdown); 52 | } 53 | 54 | $.plot.plugins.push({ 55 | init: init, 56 | options: options, 57 | name: 'resize', 58 | version: '1.0' 59 | }); 60 | })(jQuery); 61 | -------------------------------------------------------------------------------- /src/theme/scripts/flot/jquery.flot.tooltip.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jquery.flot.tooltip 3 | * 4 | * desc: create tooltip with values of hovered point on the graph, 5 | support many series, time mode, stacking and pie charts 6 | you can set custom tip content (also with use of HTML tags) and precision of values 7 | * version: 0.4.4 8 | * author: Krzysztof Urbas @krzysu [myviews.pl] with help of @ismyrnow 9 | * website: https://github.com/krzysu/flot.tooltip 10 | * 11 | * released under MIT License, 2012 12 | */ 13 | 14 | (function ($) { 15 | var options = { 16 | tooltip: false, //boolean 17 | tooltipOpts: { 18 | content: "%s | X: %x | Y: %y.2", //%s -> series label, %x -> X value, %y -> Y value, %x.2 -> precision of X value, %p -> percent 19 | dateFormat: "%y-%0m-%0d", 20 | shifts: { 21 | x: 10, 22 | y: 20 23 | }, 24 | defaultTheme: true 25 | } 26 | }; 27 | 28 | var init = function(plot) { 29 | 30 | var tipPosition = {x: 0, y: 0}; 31 | var opts = plot.getOptions(); 32 | 33 | var updateTooltipPosition = function(pos) { 34 | tipPosition.x = pos.x; 35 | tipPosition.y = pos.y; 36 | }; 37 | 38 | var onMouseMove = function(e) { 39 | 40 | var pos = {x: 0, y: 0}; 41 | 42 | pos.x = e.pageX; 43 | pos.y = e.pageY; 44 | 45 | updateTooltipPosition(pos); 46 | }; 47 | 48 | var timestampToDate = function(tmst) { 49 | 50 | var theDate = new Date(tmst); 51 | 52 | return $.plot.formatDate(theDate, opts.tooltipOpts.dateFormat); 53 | }; 54 | 55 | plot.hooks.bindEvents.push(function (plot, eventHolder) { 56 | 57 | var to = opts.tooltipOpts; 58 | var placeholder = plot.getPlaceholder(); 59 | var $tip; 60 | 61 | if (opts.tooltip === false) return; 62 | 63 | if( $('#flotTip').length > 0 ){ 64 | $tip = $('#flotTip'); 65 | } 66 | else { 67 | $tip = $('
').attr('id', 'flotTip'); 68 | $tip.appendTo('body').hide().css({position: 'absolute'}); 69 | 70 | if(to.defaultTheme) { 71 | $tip.css({ 72 | 'background': '#fff', 73 | 'z-index': '100', 74 | 'padding': '0.4em 0.6em', 75 | 'border-radius': '0.5em', 76 | 'font-size': '0.8em', 77 | 'border': '1px solid #111' 78 | }); 79 | } 80 | } 81 | 82 | $(placeholder).bind("plothover", function (event, pos, item) { 83 | if (item) { 84 | var tipText; 85 | 86 | if(opts.xaxis.mode === "time" || opts.xaxes[0].mode === "time") { 87 | tipText = stringFormat(to.content, item, timestampToDate); 88 | } 89 | else { 90 | tipText = stringFormat(to.content, item); 91 | } 92 | 93 | $tip.html( tipText ).css({left: tipPosition.x + to.shifts.x, top: tipPosition.y + to.shifts.y}).show(); 94 | } 95 | else { 96 | $tip.hide().html(''); 97 | } 98 | }); 99 | 100 | eventHolder.mousemove(onMouseMove); 101 | }); 102 | 103 | var stringFormat = function(content, item, fnct) { 104 | 105 | var percentPattern = /%p\.{0,1}(\d{0,})/; 106 | var seriesPattern = /%s/; 107 | var xPattern = /%x\.{0,1}(\d{0,})/; 108 | var yPattern = /%y\.{0,1}(\d{0,})/; 109 | 110 | //percent match 111 | if( typeof (item.series.percent) !== 'undefined' ) { 112 | content = adjustValPrecision(percentPattern, content, item.series.percent); 113 | } 114 | //series match 115 | if( typeof(item.series.label) !== 'undefined' ) { 116 | content = content.replace(seriesPattern, item.series.label); 117 | } 118 | // xVal match 119 | if( typeof(fnct) === 'function' ) { 120 | content = content.replace(xPattern, fnct(item.series.data[item.dataIndex][0]) ); 121 | } 122 | else if( typeof item.series.data[item.dataIndex][0] === 'number' ) { 123 | content = adjustValPrecision(xPattern, content, item.series.data[item.dataIndex][0]); 124 | } 125 | // yVal match 126 | if( typeof item.series.data[item.dataIndex][1] === 'number' ) { 127 | content = adjustValPrecision(yPattern, content, item.series.data[item.dataIndex][1]); 128 | } 129 | 130 | return content; 131 | }; 132 | 133 | var adjustValPrecision = function(pattern, content, value) { 134 | 135 | var precision; 136 | if( content.match(pattern) !== 'null' ) { 137 | if(RegExp.$1 !== '') { 138 | precision = RegExp.$1; 139 | value = value.toFixed(precision) 140 | } 141 | content = content.replace(pattern, value); 142 | } 143 | 144 | return content; 145 | }; 146 | } 147 | 148 | $.plot.plugins.push({ 149 | init: init, 150 | options: options, 151 | name: 'tooltip', 152 | version: '0.4.4' 153 | }); 154 | })(jQuery); 155 | -------------------------------------------------------------------------------- /src/theme/scripts/jquery.cookie.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery Cookie Plugin v1.3.1 3 | * https://github.com/carhartl/jquery-cookie 4 | * 5 | * Copyright 2013 Klaus Hartl 6 | * Released under the MIT license 7 | */ 8 | (function (factory) { 9 | if (typeof define === 'function' && define.amd) { 10 | // AMD. Register as anonymous module. 11 | define(['jquery'], factory); 12 | } else { 13 | // Browser globals. 14 | factory(jQuery); 15 | } 16 | }(function ($) { 17 | 18 | var pluses = /\+/g; 19 | 20 | function raw(s) { 21 | return s; 22 | } 23 | 24 | function decoded(s) { 25 | return decodeURIComponent(s.replace(pluses, ' ')); 26 | } 27 | 28 | function converted(s) { 29 | if (s.indexOf('"') === 0) { 30 | // This is a quoted cookie as according to RFC2068, unescape 31 | s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\'); 32 | } 33 | try { 34 | return config.json ? JSON.parse(s) : s; 35 | } catch(er) {} 36 | } 37 | 38 | var config = $.cookie = function (key, value, options) { 39 | 40 | // write 41 | if (value !== undefined) { 42 | options = $.extend({}, config.defaults, options); 43 | 44 | if (typeof options.expires === 'number') { 45 | var days = options.expires, t = options.expires = new Date(); 46 | t.setDate(t.getDate() + days); 47 | } 48 | 49 | value = config.json ? JSON.stringify(value) : String(value); 50 | 51 | return (document.cookie = [ 52 | config.raw ? key : encodeURIComponent(key), 53 | '=', 54 | config.raw ? value : encodeURIComponent(value), 55 | options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE 56 | options.path ? '; path=' + options.path : '', 57 | options.domain ? '; domain=' + options.domain : '', 58 | options.secure ? '; secure' : '' 59 | ].join('')); 60 | } 61 | 62 | // read 63 | var decode = config.raw ? raw : decoded; 64 | var cookies = document.cookie.split('; '); 65 | var result = key ? undefined : {}; 66 | for (var i = 0, l = cookies.length; i < l; i++) { 67 | var parts = cookies[i].split('='); 68 | var name = decode(parts.shift()); 69 | var cookie = decode(parts.join('=')); 70 | 71 | if (key && key === name) { 72 | result = converted(cookie); 73 | break; 74 | } 75 | 76 | if (!key) { 77 | result[name] = converted(cookie); 78 | } 79 | } 80 | 81 | return result; 82 | }; 83 | 84 | config.defaults = {}; 85 | 86 | $.removeCookie = function (key, options) { 87 | if ($.cookie(key) !== undefined) { 88 | // Must not alter options, thus extending a fresh object... 89 | $.cookie(key, '', $.extend({}, options, { expires: -1 })); 90 | return true; 91 | } 92 | return false; 93 | }; 94 | 95 | })); 96 | -------------------------------------------------------------------------------- /src/theme/scripts/load.js: -------------------------------------------------------------------------------- 1 | var seedbox = new Object; 2 | seedbox.interval = null; 3 | CTRL = false; 4 | 5 | $(function() 6 | { 7 | 8 | // tooltips 9 | $('[data-toggle="tooltip"]').tooltip(); 10 | 11 | $('.popover-btn').popover(); 12 | 13 | 14 | var ctrlDown = false; 15 | var ctrlKey = 17, aKey = 65; 16 | 17 | $(document).keydown(function(e) 18 | { 19 | if (e.keyCode == ctrlKey) ctrlDown = true; 20 | }).keyup(function(e) 21 | { 22 | if (e.keyCode == ctrlKey) ctrlDown = false; 23 | }); 24 | 25 | 26 | $('body').click(function() { 27 | $('#context').hide(); 28 | }); 29 | 30 | $('#context').click(function(event){ 31 | event.stopPropagation(); 32 | }); 33 | 34 | //$("#ajaxContent").css('min-height', $(window).height()+'px'); 35 | }); 36 | 37 | function loadUpload(){ 38 | $('#file_upload').uploadify({ 39 | 'swf' : adresse+'/uploadify/uploadify.swf', 40 | 'uploader' : adresse+'/action/addTorrent/', 41 | 'buttonClass' : 'btn btn-success btn-sm', 42 | 'buttonText' : ' Ajouter', 43 | 'height' : 27, 44 | 'queueID' : 'queue', 45 | 'itemTemplate' : '
\ 46 |
${fileName}
\ 47 |
', 48 | 'onUploadSuccess' : function(file, data, response) { 49 | evalscript(data); 50 | } 51 | }); 52 | } -------------------------------------------------------------------------------- /src/theme/scripts/plupload/js/Descr.WD3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cclleemm/FriendlyTorrent/3ad1adf5357b430a1fe89bfa1bf3a52dd10f8e45/src/theme/scripts/plupload/js/Descr.WD3 -------------------------------------------------------------------------------- /src/theme/scripts/plupload/js/jquery.plupload.queue/Descr.WD3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cclleemm/FriendlyTorrent/3ad1adf5357b430a1fe89bfa1bf3a52dd10f8e45/src/theme/scripts/plupload/js/jquery.plupload.queue/Descr.WD3 -------------------------------------------------------------------------------- /src/theme/scripts/plupload/js/jquery.plupload.queue/css/Descr.WD3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cclleemm/FriendlyTorrent/3ad1adf5357b430a1fe89bfa1bf3a52dd10f8e45/src/theme/scripts/plupload/js/jquery.plupload.queue/css/Descr.WD3 -------------------------------------------------------------------------------- /src/theme/scripts/plupload/js/jquery.plupload.queue/css/jquery.plupload.queue.css: -------------------------------------------------------------------------------- 1 | /* 2 | Plupload 3 | ------------------------------------------------------------------- */ 4 | 5 | .plupload_button { 6 | display: -moz-inline-box; /* FF < 3*/ 7 | display: inline-block; 8 | font: normal 12px sans-serif; 9 | text-decoration: none; 10 | color: #42454a; 11 | border: 1px solid #bababa; 12 | padding: 2px 8px 3px 20px; 13 | margin-right: 4px; 14 | background: #f3f3f3 url('../img/buttons.png') no-repeat 0 center; 15 | outline: 0; 16 | 17 | /* Optional rounded corners for browsers that support it */ 18 | -moz-border-radius: 3px; 19 | -khtml-border-radius: 3px; 20 | -webkit-border-radius: 3px; 21 | border-radius: 3px; 22 | } 23 | 24 | .plupload_button:hover { 25 | color: #000; 26 | text-decoration: none; 27 | } 28 | 29 | .plupload_disabled, a.plupload_disabled:hover { 30 | color: #737373; 31 | border-color: #c5c5c5; 32 | background: #ededed url('../img/buttons-disabled.png') no-repeat 0 center; 33 | cursor: default; 34 | } 35 | 36 | .plupload_add { 37 | background-position: -181px center; 38 | } 39 | 40 | .plupload_wrapper { 41 | font: normal 11px Verdana,sans-serif; 42 | width: 100%; 43 | } 44 | 45 | .plupload_container { 46 | padding: 8px; 47 | background: url('../img/transp50.png'); 48 | /*-moz-border-radius: 5px;*/ 49 | } 50 | 51 | .plupload_container input { 52 | border: 1px solid #DDD; 53 | font: normal 11px Verdana,sans-serif; 54 | width: 98%; 55 | } 56 | 57 | .plupload_header {background: #2A2C2E url('../img/backgrounds.gif') repeat-x;} 58 | .plupload_header_content { 59 | background: url('../img/backgrounds.gif') no-repeat 0 -317px; 60 | min-height: 56px; 61 | padding-left: 60px; 62 | color: #FFF; 63 | } 64 | .plupload_header_title { 65 | font: normal 18px sans-serif; 66 | padding: 6px 0 3px; 67 | } 68 | .plupload_header_text { 69 | font: normal 12px sans-serif; 70 | } 71 | 72 | .plupload_filelist { 73 | margin: 0; 74 | padding: 0; 75 | list-style: none; 76 | } 77 | 78 | .plupload_scroll .plupload_filelist { 79 | height: 185px; 80 | background: #F5F5F5; 81 | overflow-y: scroll; 82 | } 83 | 84 | .plupload_filelist li { 85 | padding: 10px 8px; 86 | background: #F5F5F5 url('../img/backgrounds.gif') repeat-x 0 -156px; 87 | border-bottom: 1px solid #DDD; 88 | } 89 | 90 | .plupload_filelist_header, .plupload_filelist_footer { 91 | background: #DFDFDF; 92 | padding: 8px 8px; 93 | color: #42454A; 94 | } 95 | .plupload_filelist_header { 96 | border-top: 1px solid #EEE; 97 | border-bottom: 1px solid #CDCDCD; 98 | } 99 | 100 | .plupload_filelist_footer {border-top: 1px solid #FFF; height: 22px; line-height: 20px; vertical-align: middle;} 101 | .plupload_file_name {float: left; overflow: hidden} 102 | .plupload_file_status {color: #777;} 103 | .plupload_file_status span {color: #42454A;} 104 | .plupload_file_size, .plupload_file_status, .plupload_progress { 105 | float: right; 106 | width: 80px; 107 | } 108 | .plupload_file_size, .plupload_file_status, .plupload_file_action {text-align: right;} 109 | 110 | .plupload_filelist .plupload_file_name {width: 205px} 111 | 112 | .plupload_file_action { 113 | float: right; 114 | width: 16px; 115 | height: 16px; 116 | margin-left: 15px; 117 | } 118 | 119 | .plupload_file_action * { 120 | display: none; 121 | width: 16px; 122 | height: 16px; 123 | } 124 | 125 | li.plupload_uploading {background: #ECF3DC url('../img/backgrounds.gif') repeat-x 0 -238px;} 126 | li.plupload_done {color:#AAA} 127 | 128 | li.plupload_delete a { 129 | background: url('../img/delete.gif'); 130 | } 131 | 132 | li.plupload_failed a { 133 | background: url('../img/error.gif'); 134 | cursor: default; 135 | } 136 | 137 | li.plupload_done a { 138 | background: url('../img/done.gif'); 139 | cursor: default; 140 | } 141 | 142 | .plupload_progress, .plupload_upload_status { 143 | display: none; 144 | } 145 | 146 | .plupload_progress_container { 147 | margin-top: 3px; 148 | border: 1px solid #CCC; 149 | background: #FFF; 150 | padding: 1px; 151 | } 152 | .plupload_progress_bar { 153 | width: 0px; 154 | height: 7px; 155 | background: #CDEB8B; 156 | } 157 | 158 | .plupload_scroll .plupload_filelist_header .plupload_file_action, .plupload_scroll .plupload_filelist_footer .plupload_file_action { 159 | margin-right: 17px; 160 | } 161 | 162 | /* Floats */ 163 | 164 | .plupload_clear,.plupload_clearer {clear: both;} 165 | .plupload_clearer, .plupload_progress_bar { 166 | display: block; 167 | font-size: 0; 168 | line-height: 0; 169 | } 170 | 171 | li.plupload_droptext { 172 | background: transparent; 173 | text-align: center; 174 | vertical-align: middle; 175 | border: 0; 176 | line-height: 165px; 177 | } 178 | -------------------------------------------------------------------------------- /src/theme/scripts/plupload/js/jquery.plupload.queue/img/Descr.WD3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cclleemm/FriendlyTorrent/3ad1adf5357b430a1fe89bfa1bf3a52dd10f8e45/src/theme/scripts/plupload/js/jquery.plupload.queue/img/Descr.WD3 -------------------------------------------------------------------------------- /src/theme/scripts/plupload/js/jquery.plupload.queue/img/backgrounds.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cclleemm/FriendlyTorrent/3ad1adf5357b430a1fe89bfa1bf3a52dd10f8e45/src/theme/scripts/plupload/js/jquery.plupload.queue/img/backgrounds.gif -------------------------------------------------------------------------------- /src/theme/scripts/plupload/js/jquery.plupload.queue/img/buttons-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cclleemm/FriendlyTorrent/3ad1adf5357b430a1fe89bfa1bf3a52dd10f8e45/src/theme/scripts/plupload/js/jquery.plupload.queue/img/buttons-disabled.png -------------------------------------------------------------------------------- /src/theme/scripts/plupload/js/jquery.plupload.queue/img/buttons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cclleemm/FriendlyTorrent/3ad1adf5357b430a1fe89bfa1bf3a52dd10f8e45/src/theme/scripts/plupload/js/jquery.plupload.queue/img/buttons.png -------------------------------------------------------------------------------- /src/theme/scripts/plupload/js/jquery.plupload.queue/img/done.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cclleemm/FriendlyTorrent/3ad1adf5357b430a1fe89bfa1bf3a52dd10f8e45/src/theme/scripts/plupload/js/jquery.plupload.queue/img/done.gif -------------------------------------------------------------------------------- /src/theme/scripts/plupload/js/jquery.plupload.queue/img/error.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cclleemm/FriendlyTorrent/3ad1adf5357b430a1fe89bfa1bf3a52dd10f8e45/src/theme/scripts/plupload/js/jquery.plupload.queue/img/error.gif -------------------------------------------------------------------------------- /src/theme/scripts/plupload/js/jquery.plupload.queue/img/transp50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cclleemm/FriendlyTorrent/3ad1adf5357b430a1fe89bfa1bf3a52dd10f8e45/src/theme/scripts/plupload/js/jquery.plupload.queue/img/transp50.png -------------------------------------------------------------------------------- /src/theme/scripts/plupload/js/plupload.flash.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cclleemm/FriendlyTorrent/3ad1adf5357b430a1fe89bfa1bf3a52dd10f8e45/src/theme/scripts/plupload/js/plupload.flash.swf -------------------------------------------------------------------------------- /src/theme/scripts/plupload/js/plupload.silverlight.xap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cclleemm/FriendlyTorrent/3ad1adf5357b430a1fe89bfa1bf3a52dd10f8e45/src/theme/scripts/plupload/js/plupload.silverlight.xap -------------------------------------------------------------------------------- /src/tmp/band.txt: -------------------------------------------------------------------------------- 1 | a:4:{i:0;s:0:"";i:1;s:6:"518.74";i:2;s:8:"12255.25";s:4:"time";i:1371564058;}} -------------------------------------------------------------------------------- /src/tornado/BitTornado/BT1/Choker.py: -------------------------------------------------------------------------------- 1 | # Written by Bram Cohen 2 | # see LICENSE.txt for license information 3 | 4 | from random import randrange, shuffle 5 | from BitTornado.clock import clock 6 | try: 7 | True 8 | except: 9 | True = 1 10 | False = 0 11 | 12 | class Choker: 13 | def __init__(self, config, schedule, picker, done = lambda: False): 14 | self.config = config 15 | self.round_robin_period = config['round_robin_period'] 16 | self.schedule = schedule 17 | self.picker = picker 18 | self.connections = [] 19 | self.last_preferred = 0 20 | self.last_round_robin = clock() 21 | self.done = done 22 | self.super_seed = False 23 | self.paused = False 24 | schedule(self._round_robin, 5) 25 | 26 | def set_round_robin_period(self, x): 27 | self.round_robin_period = x 28 | 29 | def _round_robin(self): 30 | self.schedule(self._round_robin, 5) 31 | if self.super_seed: 32 | cons = range(len(self.connections)) 33 | to_close = [] 34 | count = self.config['min_uploads']-self.last_preferred 35 | if count > 0: # optimization 36 | shuffle(cons) 37 | for c in cons: 38 | i = self.picker.next_have(self.connections[c], count > 0) 39 | if i is None: 40 | continue 41 | if i < 0: 42 | to_close.append(self.connections[c]) 43 | continue 44 | self.connections[c].send_have(i) 45 | count -= 1 46 | for c in to_close: 47 | c.close() 48 | if self.last_round_robin + self.round_robin_period < clock(): 49 | self.last_round_robin = clock() 50 | for i in xrange(1, len(self.connections)): 51 | c = self.connections[i] 52 | u = c.get_upload() 53 | if u.is_choked() and u.is_interested(): 54 | self.connections = self.connections[i:] + self.connections[:i] 55 | break 56 | self._rechoke() 57 | 58 | def _rechoke(self): 59 | preferred = [] 60 | maxuploads = self.config['max_uploads'] 61 | if self.paused: 62 | for c in self.connections: 63 | c.get_upload().choke() 64 | return 65 | if maxuploads > 1: 66 | for c in self.connections: 67 | u = c.get_upload() 68 | if not u.is_interested(): 69 | continue 70 | if self.done(): 71 | r = u.get_rate() 72 | else: 73 | d = c.get_download() 74 | r = d.get_rate() 75 | if r < 1000 or d.is_snubbed(): 76 | continue 77 | preferred.append((-r, c)) 78 | self.last_preferred = len(preferred) 79 | preferred.sort() 80 | del preferred[maxuploads-1:] 81 | preferred = [x[1] for x in preferred] 82 | count = len(preferred) 83 | hit = False 84 | to_unchoke = [] 85 | for c in self.connections: 86 | u = c.get_upload() 87 | if c in preferred: 88 | to_unchoke.append(u) 89 | else: 90 | if count < maxuploads or not hit: 91 | to_unchoke.append(u) 92 | if u.is_interested(): 93 | count += 1 94 | hit = True 95 | else: 96 | u.choke() 97 | for u in to_unchoke: 98 | u.unchoke() 99 | 100 | def connection_made(self, connection, p = None): 101 | if p is None: 102 | p = randrange(-2, len(self.connections) + 1) 103 | self.connections.insert(max(p, 0), connection) 104 | self._rechoke() 105 | 106 | def connection_lost(self, connection): 107 | self.connections.remove(connection) 108 | self.picker.lost_peer(connection) 109 | if connection.get_upload().is_interested() and not connection.get_upload().is_choked(): 110 | self._rechoke() 111 | 112 | def interested(self, connection): 113 | if not connection.get_upload().is_choked(): 114 | self._rechoke() 115 | 116 | def not_interested(self, connection): 117 | if not connection.get_upload().is_choked(): 118 | self._rechoke() 119 | 120 | def set_super_seed(self): 121 | while self.connections: # close all connections 122 | self.connections[0].close() 123 | self.picker.set_superseed() 124 | self.super_seed = True 125 | 126 | def pause(self, flag): 127 | self.paused = flag 128 | self._rechoke() 129 | -------------------------------------------------------------------------------- /src/tornado/BitTornado/BT1/DownloaderFeedback.py: -------------------------------------------------------------------------------- 1 | # Written by Bram Cohen 2 | # see LICENSE.txt for license information 3 | 4 | from cStringIO import StringIO 5 | from urllib import quote 6 | from threading import Event 7 | 8 | try: 9 | True 10 | except: 11 | True = 1 12 | False = 0 13 | 14 | INIT_STATE = (('R','R+'),('L','L+')) 15 | 16 | class DownloaderFeedback: 17 | def __init__(self, choker, httpdl, add_task, upfunc, downfunc, 18 | ratemeasure, leftfunc, file_length, finflag, sp, statistics, 19 | statusfunc = None, interval = None): 20 | self.choker = choker 21 | self.httpdl = httpdl 22 | self.add_task = add_task 23 | self.upfunc = upfunc 24 | self.downfunc = downfunc 25 | self.ratemeasure = ratemeasure 26 | self.leftfunc = leftfunc 27 | self.file_length = file_length 28 | self.finflag = finflag 29 | self.sp = sp 30 | self.statistics = statistics 31 | self.lastids = [] 32 | self.spewdata = None 33 | self.doneprocessing = Event() 34 | self.doneprocessing.set() 35 | if statusfunc: 36 | self.autodisplay(statusfunc, interval) 37 | 38 | 39 | def _rotate(self): 40 | cs = self.choker.connections 41 | for id in self.lastids: 42 | for i in xrange(len(cs)): 43 | if cs[i].get_id() == id: 44 | return cs[i:] + cs[:i] 45 | return cs 46 | 47 | def spews(self): 48 | l = [] 49 | cs = self._rotate() 50 | self.lastids = [c.get_id() for c in cs] 51 | for c in cs: 52 | a = {} 53 | a['id'] = c.get_readable_id() 54 | a['ip'] = c.get_ip() 55 | a['optimistic'] = (c is self.choker.connections[0]) 56 | a['direction'] = INIT_STATE[c.is_locally_initiated()][c.is_encrypted()] 57 | u = c.get_upload() 58 | a['uprate'] = int(u.measure.get_rate()) 59 | a['uinterested'] = u.is_interested() 60 | a['uchoked'] = u.is_choked() 61 | d = c.get_download() 62 | a['downrate'] = int(d.measure.get_rate()) 63 | a['dinterested'] = d.is_interested() 64 | a['dchoked'] = d.is_choked() 65 | a['snubbed'] = d.is_snubbed() 66 | a['utotal'] = d.connection.upload.measure.get_total() 67 | a['dtotal'] = d.connection.download.measure.get_total() 68 | if len(d.connection.download.have) > 0: 69 | a['completed'] = float(len(d.connection.download.have)-d.connection.download.have.numfalse)/float(len(d.connection.download.have)) 70 | else: 71 | a['completed'] = 1.0 72 | a['speed'] = d.connection.download.peermeasure.get_rate() 73 | 74 | l.append(a) 75 | 76 | for dl in self.httpdl.get_downloads(): 77 | if dl.goodseed: 78 | a = {} 79 | a['id'] = 'http seed' 80 | a['ip'] = dl.baseurl 81 | a['optimistic'] = False 82 | a['direction'] = 'L' 83 | a['uprate'] = 0 84 | a['uinterested'] = False 85 | a['uchoked'] = False 86 | a['downrate'] = int(dl.measure.get_rate()) 87 | a['dinterested'] = True 88 | a['dchoked'] = not dl.active 89 | a['snubbed'] = not dl.active 90 | a['utotal'] = None 91 | a['dtotal'] = dl.measure.get_total() 92 | a['completed'] = 1.0 93 | a['speed'] = None 94 | 95 | l.append(a) 96 | 97 | return l 98 | 99 | 100 | def gather(self, displayfunc = None): 101 | s = {'stats': self.statistics.update()} 102 | if self.sp.isSet(): 103 | s['spew'] = self.spews() 104 | else: 105 | s['spew'] = None 106 | s['up'] = self.upfunc() 107 | if self.finflag.isSet(): 108 | s['done'] = self.file_length 109 | return s 110 | s['down'] = self.downfunc() 111 | obtained, desired = self.leftfunc() 112 | s['done'] = obtained 113 | s['wanted'] = desired 114 | if desired > 0: 115 | s['frac'] = float(obtained)/desired 116 | else: 117 | s['frac'] = 1.0 118 | if desired == obtained: 119 | s['time'] = 0 120 | else: 121 | s['time'] = self.ratemeasure.get_time_left(desired-obtained) 122 | return s 123 | 124 | 125 | def display(self, displayfunc): 126 | if not self.doneprocessing.isSet(): 127 | return 128 | self.doneprocessing.clear() 129 | stats = self.gather() 130 | if self.finflag.isSet(): 131 | displayfunc(dpflag = self.doneprocessing, 132 | upRate = stats['up'], 133 | statistics = stats['stats'], spew = stats['spew']) 134 | elif stats['time'] is not None: 135 | displayfunc(dpflag = self.doneprocessing, 136 | fractionDone = stats['frac'], sizeDone = stats['done'], 137 | downRate = stats['down'], upRate = stats['up'], 138 | statistics = stats['stats'], spew = stats['spew'], 139 | timeEst = stats['time']) 140 | else: 141 | displayfunc(dpflag = self.doneprocessing, 142 | fractionDone = stats['frac'], sizeDone = stats['done'], 143 | downRate = stats['down'], upRate = stats['up'], 144 | statistics = stats['stats'], spew = stats['spew']) 145 | 146 | 147 | def autodisplay(self, displayfunc, interval): 148 | self.displayfunc = displayfunc 149 | self.interval = interval 150 | self._autodisplay() 151 | 152 | def _autodisplay(self): 153 | self.add_task(self._autodisplay, self.interval) 154 | self.display(self.displayfunc) 155 | -------------------------------------------------------------------------------- /src/tornado/BitTornado/BT1/Filter.py: -------------------------------------------------------------------------------- 1 | class Filter: 2 | def __init__(self, callback): 3 | self.callback = callback 4 | 5 | def check(self, ip, paramslist, headers): 6 | 7 | def params(key, default = None, l = paramslist): 8 | if l.has_key(key): 9 | return l[key][0] 10 | return default 11 | 12 | return None 13 | -------------------------------------------------------------------------------- /src/tornado/BitTornado/BT1/StreamCheck.py: -------------------------------------------------------------------------------- 1 | # Written by Bram Cohen 2 | # see LICENSE.txt for license information 3 | 4 | from cStringIO import StringIO 5 | from binascii import b2a_hex 6 | from socket import error as socketerror 7 | from urllib import quote 8 | from traceback import print_exc 9 | import Connecter 10 | try: 11 | True 12 | except: 13 | True = 1 14 | False = 0 15 | 16 | DEBUG = False 17 | 18 | 19 | protocol_name = 'BitTorrent protocol' 20 | option_pattern = chr(0)*8 21 | 22 | def toint(s): 23 | return long(b2a_hex(s), 16) 24 | 25 | def tobinary(i): 26 | return (chr(i >> 24) + chr((i >> 16) & 0xFF) + 27 | chr((i >> 8) & 0xFF) + chr(i & 0xFF)) 28 | 29 | hexchars = '0123456789ABCDEF' 30 | hexmap = [] 31 | for i in xrange(256): 32 | hexmap.append(hexchars[(i&0xF0)/16]+hexchars[i&0x0F]) 33 | 34 | def tohex(s): 35 | r = [] 36 | for c in s: 37 | r.append(hexmap[ord(c)]) 38 | return ''.join(r) 39 | 40 | def make_readable(s): 41 | if not s: 42 | return '' 43 | if quote(s).find('%') >= 0: 44 | return tohex(s) 45 | return '"'+s+'"' 46 | 47 | def toint(s): 48 | return long(b2a_hex(s), 16) 49 | 50 | # header, reserved, download id, my id, [length, message] 51 | 52 | streamno = 0 53 | 54 | 55 | class StreamCheck: 56 | def __init__(self): 57 | global streamno 58 | self.no = streamno 59 | streamno += 1 60 | self.buffer = StringIO() 61 | self.next_len, self.next_func = 1, self.read_header_len 62 | 63 | def read_header_len(self, s): 64 | if ord(s) != len(protocol_name): 65 | print self.no, 'BAD HEADER LENGTH' 66 | return len(protocol_name), self.read_header 67 | 68 | def read_header(self, s): 69 | if s != protocol_name: 70 | print self.no, 'BAD HEADER' 71 | return 8, self.read_reserved 72 | 73 | def read_reserved(self, s): 74 | return 20, self.read_download_id 75 | 76 | def read_download_id(self, s): 77 | if DEBUG: 78 | print self.no, 'download ID ' + tohex(s) 79 | return 20, self.read_peer_id 80 | 81 | def read_peer_id(self, s): 82 | if DEBUG: 83 | print self.no, 'peer ID' + make_readable(s) 84 | return 4, self.read_len 85 | 86 | def read_len(self, s): 87 | l = toint(s) 88 | if l > 2 ** 23: 89 | print self.no, 'BAD LENGTH: '+str(l)+' ('+s+')' 90 | return l, self.read_message 91 | 92 | def read_message(self, s): 93 | if not s: 94 | return 4, self.read_len 95 | m = s[0] 96 | if ord(m) > 8: 97 | print self.no, 'BAD MESSAGE: '+str(ord(m)) 98 | if m == Connecter.REQUEST: 99 | if len(s) != 13: 100 | print self.no, 'BAD REQUEST SIZE: '+str(len(s)) 101 | return 4, self.read_len 102 | index = toint(s[1:5]) 103 | begin = toint(s[5:9]) 104 | length = toint(s[9:]) 105 | print self.no, 'Request: '+str(index)+': '+str(begin)+'-'+str(begin)+'+'+str(length) 106 | elif m == Connecter.CANCEL: 107 | if len(s) != 13: 108 | print self.no, 'BAD CANCEL SIZE: '+str(len(s)) 109 | return 4, self.read_len 110 | index = toint(s[1:5]) 111 | begin = toint(s[5:9]) 112 | length = toint(s[9:]) 113 | print self.no, 'Cancel: '+str(index)+': '+str(begin)+'-'+str(begin)+'+'+str(length) 114 | elif m == Connecter.PIECE: 115 | index = toint(s[1:5]) 116 | begin = toint(s[5:9]) 117 | length = len(s)-9 118 | print self.no, 'Piece: '+str(index)+': '+str(begin)+'-'+str(begin)+'+'+str(length) 119 | else: 120 | print self.no, 'Message '+str(ord(m))+' (length '+str(len(s))+')' 121 | return 4, self.read_len 122 | 123 | def write(self, s): 124 | while True: 125 | i = self.next_len - self.buffer.tell() 126 | if i > len(s): 127 | self.buffer.write(s) 128 | return 129 | self.buffer.write(s[:i]) 130 | s = s[i:] 131 | m = self.buffer.getvalue() 132 | self.buffer.reset() 133 | self.buffer.truncate() 134 | x = self.next_func(m) 135 | self.next_len, self.next_func = x 136 | -------------------------------------------------------------------------------- /src/tornado/BitTornado/BT1/Uploader.py: -------------------------------------------------------------------------------- 1 | # Written by Bram Cohen 2 | # see LICENSE.txt for license information 3 | 4 | from BitTornado.CurrentRateMeasure import Measure 5 | 6 | try: 7 | True 8 | except: 9 | True = 1 10 | False = 0 11 | 12 | class Upload: 13 | def __init__(self, connection, ratelimiter, totalup, choker, storage, 14 | picker, config): 15 | self.connection = connection 16 | self.ratelimiter = ratelimiter 17 | self.totalup = totalup 18 | self.choker = choker 19 | self.storage = storage 20 | self.picker = picker 21 | self.config = config 22 | self.max_slice_length = config['max_slice_length'] 23 | self.choked = True 24 | self.cleared = True 25 | self.interested = False 26 | self.super_seeding = False 27 | self.buffer = [] 28 | self.measure = Measure(config['max_rate_period'], config['upload_rate_fudge']) 29 | self.was_ever_interested = False 30 | if storage.get_amount_left() == 0: 31 | if choker.super_seed: 32 | self.super_seeding = True # flag, and don't send bitfield 33 | self.seed_have_list = [] # set from piecepicker 34 | self.skipped_count = 0 35 | else: 36 | if config['breakup_seed_bitfield']: 37 | bitfield, msgs = storage.get_have_list_cloaked() 38 | connection.send_bitfield(bitfield) 39 | for have in msgs: 40 | connection.send_have(have) 41 | else: 42 | connection.send_bitfield(storage.get_have_list()) 43 | else: 44 | if storage.do_I_have_anything(): 45 | connection.send_bitfield(storage.get_have_list()) 46 | self.piecedl = None 47 | self.piecebuf = None 48 | 49 | def got_not_interested(self): 50 | if self.interested: 51 | self.interested = False 52 | del self.buffer[:] 53 | self.piecedl = None 54 | if self.piecebuf: 55 | self.piecebuf.release() 56 | self.piecebuf = None 57 | self.choker.not_interested(self.connection) 58 | 59 | def got_interested(self): 60 | if not self.interested: 61 | self.interested = True 62 | self.was_ever_interested = True 63 | self.choker.interested(self.connection) 64 | 65 | def get_upload_chunk(self): 66 | if self.choked or not self.buffer: 67 | return None 68 | index, begin, length = self.buffer.pop(0) 69 | if self.config['buffer_reads']: 70 | if index != self.piecedl: 71 | if self.piecebuf: 72 | self.piecebuf.release() 73 | self.piecedl = index 74 | self.piecebuf = self.storage.get_piece(index, 0, -1) 75 | try: 76 | piece = self.piecebuf[begin:begin+length] 77 | assert len(piece) == length 78 | except: # fails if storage.get_piece returns None or if out of range 79 | self.connection.close() 80 | return None 81 | else: 82 | if self.piecebuf: 83 | self.piecebuf.release() 84 | self.piecedl = None 85 | piece = self.storage.get_piece(index, begin, length) 86 | if piece is None: 87 | self.connection.close() 88 | return None 89 | self.measure.update_rate(len(piece)) 90 | self.totalup.update_rate(len(piece)) 91 | return (index, begin, piece) 92 | 93 | def got_request(self, index, begin, length): 94 | if ( (self.super_seeding and not index in self.seed_have_list) 95 | or not self.interested or length > self.max_slice_length ): 96 | self.connection.close() 97 | return 98 | if not self.cleared: 99 | self.buffer.append((index, begin, length)) 100 | if not self.choked and self.connection.next_upload is None: 101 | self.ratelimiter.queue(self.connection) 102 | 103 | 104 | def got_cancel(self, index, begin, length): 105 | try: 106 | self.buffer.remove((index, begin, length)) 107 | except ValueError: 108 | pass 109 | 110 | def choke(self): 111 | if not self.choked: 112 | self.choked = True 113 | self.connection.send_choke() 114 | self.piecedl = None 115 | if self.piecebuf: 116 | self.piecebuf.release() 117 | self.piecebuf = None 118 | 119 | def choke_sent(self): 120 | del self.buffer[:] 121 | self.cleared = True 122 | 123 | def unchoke(self): 124 | if self.choked: 125 | self.choked = False 126 | self.cleared = False 127 | self.connection.send_unchoke() 128 | 129 | def disconnected(self): 130 | if self.piecebuf: 131 | self.piecebuf.release() 132 | self.piecebuf = None 133 | 134 | def is_choked(self): 135 | return self.choked 136 | 137 | def is_interested(self): 138 | return self.interested 139 | 140 | def has_queries(self): 141 | return not self.choked and len(self.buffer) > 0 142 | 143 | def get_rate(self): 144 | return self.measure.get_rate() 145 | 146 | -------------------------------------------------------------------------------- /src/tornado/BitTornado/BT1/__init__.py: -------------------------------------------------------------------------------- 1 | # placeholder -------------------------------------------------------------------------------- /src/tornado/BitTornado/BT1/btformats.py: -------------------------------------------------------------------------------- 1 | # Written by Bram Cohen 2 | # see LICENSE.txt for license information 3 | 4 | from types import StringType, LongType, IntType, ListType, DictType 5 | from re import compile 6 | 7 | reg = compile(r'^[^/\\.~][^/\\]*$') 8 | 9 | ints = (LongType, IntType) 10 | 11 | def check_info(info): 12 | if type(info) != DictType: 13 | raise ValueError, 'bad metainfo - not a dictionary' 14 | pieces = info.get('pieces') 15 | if type(pieces) != StringType or len(pieces) % 20 != 0: 16 | raise ValueError, 'bad metainfo - bad pieces key' 17 | piecelength = info.get('piece length') 18 | if type(piecelength) not in ints or piecelength <= 0: 19 | raise ValueError, 'bad metainfo - illegal piece length' 20 | name = info.get('name') 21 | if type(name) != StringType: 22 | raise ValueError, 'bad metainfo - bad name' 23 | if not reg.match(name): 24 | raise ValueError, 'name %s disallowed for security reasons' % name 25 | if info.has_key('files') == info.has_key('length'): 26 | raise ValueError, 'single/multiple file mix' 27 | if info.has_key('length'): 28 | length = info.get('length') 29 | if type(length) not in ints or length < 0: 30 | raise ValueError, 'bad metainfo - bad length' 31 | else: 32 | files = info.get('files') 33 | if type(files) != ListType: 34 | raise ValueError 35 | for f in files: 36 | if type(f) != DictType: 37 | raise ValueError, 'bad metainfo - bad file value' 38 | length = f.get('length') 39 | if type(length) not in ints or length < 0: 40 | raise ValueError, 'bad metainfo - bad length' 41 | path = f.get('path') 42 | if type(path) != ListType or path == []: 43 | raise ValueError, 'bad metainfo - bad path' 44 | for p in path: 45 | if type(p) != StringType: 46 | raise ValueError, 'bad metainfo - bad path dir' 47 | if not reg.match(p): 48 | raise ValueError, 'path %s disallowed for security reasons' % p 49 | for i in xrange(len(files)): 50 | for j in xrange(i): 51 | if files[i]['path'] == files[j]['path']: 52 | raise ValueError, 'bad metainfo - duplicate path' 53 | 54 | def check_message(message): 55 | if type(message) != DictType: 56 | raise ValueError 57 | check_info(message.get('info')) 58 | if type(message.get('announce')) != StringType: 59 | raise ValueError 60 | 61 | def check_peers(message): 62 | if type(message) != DictType: 63 | raise ValueError 64 | if message.has_key('failure reason'): 65 | if type(message['failure reason']) != StringType: 66 | raise ValueError 67 | return 68 | peers = message.get('peers') 69 | if type(peers) == ListType: 70 | for p in peers: 71 | if type(p) != DictType: 72 | raise ValueError 73 | if type(p.get('ip')) != StringType: 74 | raise ValueError 75 | port = p.get('port') 76 | if type(port) not in ints or p <= 0: 77 | raise ValueError 78 | if p.has_key('peer id'): 79 | id = p['peer id'] 80 | if type(id) != StringType or len(id) != 20: 81 | raise ValueError 82 | elif type(peers) != StringType or len(peers) % 6 != 0: 83 | raise ValueError 84 | interval = message.get('interval', 1) 85 | if type(interval) not in ints or interval <= 0: 86 | raise ValueError 87 | minint = message.get('min interval', 1) 88 | if type(minint) not in ints or minint <= 0: 89 | raise ValueError 90 | if type(message.get('tracker id', '')) != StringType: 91 | raise ValueError 92 | npeers = message.get('num peers', 0) 93 | if type(npeers) not in ints or npeers < 0: 94 | raise ValueError 95 | dpeers = message.get('done peers', 0) 96 | if type(dpeers) not in ints or dpeers < 0: 97 | raise ValueError 98 | last = message.get('last', 0) 99 | if type(last) not in ints or last < 0: 100 | raise ValueError 101 | -------------------------------------------------------------------------------- /src/tornado/BitTornado/BT1/fakeopen.py: -------------------------------------------------------------------------------- 1 | # Written by Bram Cohen 2 | # see LICENSE.txt for license information 3 | 4 | from string import join 5 | 6 | class FakeHandle: 7 | def __init__(self, name, fakeopen): 8 | self.name = name 9 | self.fakeopen = fakeopen 10 | self.pos = 0 11 | 12 | def flush(self): 13 | pass 14 | 15 | def close(self): 16 | pass 17 | 18 | def seek(self, pos): 19 | self.pos = pos 20 | 21 | def read(self, amount = None): 22 | old = self.pos 23 | f = self.fakeopen.files[self.name] 24 | if self.pos >= len(f): 25 | return '' 26 | if amount is None: 27 | self.pos = len(f) 28 | return join(f[old:], '') 29 | else: 30 | self.pos = min(len(f), old + amount) 31 | return join(f[old:self.pos], '') 32 | 33 | def write(self, s): 34 | f = self.fakeopen.files[self.name] 35 | while len(f) < self.pos: 36 | f.append(chr(0)) 37 | self.fakeopen.files[self.name][self.pos : self.pos + len(s)] = list(s) 38 | self.pos += len(s) 39 | 40 | class FakeOpen: 41 | def __init__(self, initial = {}): 42 | self.files = {} 43 | for key, value in initial.items(): 44 | self.files[key] = list(value) 45 | 46 | def open(self, filename, mode): 47 | """currently treats everything as rw - doesn't support append""" 48 | self.files.setdefault(filename, []) 49 | return FakeHandle(filename, self) 50 | 51 | def exists(self, file): 52 | return self.files.has_key(file) 53 | 54 | def getsize(self, file): 55 | return len(self.files[file]) 56 | 57 | def test_normal(): 58 | f = FakeOpen({'f1': 'abcde'}) 59 | assert f.exists('f1') 60 | assert not f.exists('f2') 61 | assert f.getsize('f1') == 5 62 | h = f.open('f1', 'rw') 63 | assert h.read(3) == 'abc' 64 | assert h.read(1) == 'd' 65 | assert h.read() == 'e' 66 | assert h.read(2) == '' 67 | h.write('fpq') 68 | h.seek(4) 69 | assert h.read(2) == 'ef' 70 | h.write('ghij') 71 | h.seek(0) 72 | assert h.read() == 'abcdefghij' 73 | h.seek(2) 74 | h.write('p') 75 | h.write('q') 76 | assert h.read(1) == 'e' 77 | h.seek(1) 78 | assert h.read(5) == 'bpqef' 79 | 80 | h2 = f.open('f2', 'rw') 81 | assert h2.read() == '' 82 | h2.write('mnop') 83 | h2.seek(1) 84 | assert h2.read() == 'nop' 85 | 86 | assert f.exists('f1') 87 | assert f.exists('f2') 88 | assert f.getsize('f1') == 10 89 | assert f.getsize('f2') == 4 90 | -------------------------------------------------------------------------------- /src/tornado/BitTornado/BTcrypto.py: -------------------------------------------------------------------------------- 1 | # Written by John Hoffman 2 | # based on code by Uoti Urpala 3 | # see LICENSE.txt for license information 4 | 5 | from __future__ import generators # for python 2.2 6 | from random import randrange,randint,seed 7 | try: 8 | from os import urandom 9 | except: 10 | seed() 11 | urandom = lambda x: ''.join([chr(randint(0,255)) for i in xrange(x)]) 12 | from hashlib import sha1 13 | 14 | try: 15 | True 16 | except: 17 | True = 1 18 | False = 0 19 | 20 | try: 21 | from Crypto.Cipher import ARC4 22 | CRYPTO_OK = True 23 | except: 24 | CRYPTO_OK = False 25 | 26 | KEY_LENGTH = 160 27 | DH_PRIME = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A63A36210000000000090563 28 | PAD_MAX = 200 # less than protocol maximum, and later assumed to be < 256 29 | DH_BYTES = 96 30 | 31 | def bytetonum(x): 32 | return long(x.encode('hex'), 16) 33 | 34 | def numtobyte(x): 35 | x = hex(x).lstrip('0x').rstrip('Ll') 36 | x = '0'*(192 - len(x)) + x 37 | return x.decode('hex') 38 | 39 | class Crypto: 40 | def __init__(self, initiator, disable_crypto = False): 41 | self.initiator = initiator 42 | self.disable_crypto = disable_crypto 43 | if not disable_crypto and not CRYPTO_OK: 44 | raise NotImplementedError, "attempt to run encryption w/ none installed" 45 | self.privkey = bytetonum(urandom(KEY_LENGTH/8)) 46 | self.pubkey = numtobyte(pow(2, self.privkey, DH_PRIME)) 47 | self.keylength = DH_BYTES 48 | self._VC_pattern = None 49 | 50 | def received_key(self, k): 51 | self.S = numtobyte(pow(bytetonum(k), self.privkey, DH_PRIME)) 52 | self.block3a = sha1('req1'+self.S).digest() 53 | self.block3bkey = sha1('req3'+self.S).digest() 54 | self.block3b = None 55 | 56 | def _gen_block3b(self, SKEY): 57 | a = sha1('req2'+SKEY).digest() 58 | return ''.join([ chr(ord(a[i])^ord(self.block3bkey[i])) 59 | for i in xrange(20) ]) 60 | 61 | def test_skey(self, s, SKEY): 62 | block3b = self._gen_block3b(SKEY) 63 | if block3b != s: 64 | return False 65 | self.block3b = block3b 66 | if not self.disable_crypto: 67 | self.set_skey(SKEY) 68 | return True 69 | 70 | def set_skey(self, SKEY): 71 | if not self.block3b: 72 | self.block3b = self._gen_block3b(SKEY) 73 | crypta = ARC4.new(sha1('keyA'+self.S+SKEY).digest()) 74 | cryptb = ARC4.new(sha1('keyB'+self.S+SKEY).digest()) 75 | if self.initiator: 76 | self.encrypt = crypta.encrypt 77 | self.decrypt = cryptb.decrypt 78 | else: 79 | self.encrypt = cryptb.encrypt 80 | self.decrypt = crypta.decrypt 81 | self.encrypt('x'*1024) # discard first 1024 bytes 82 | self.decrypt('x'*1024) 83 | 84 | def VC_pattern(self): 85 | if not self._VC_pattern: 86 | self._VC_pattern = self.decrypt('\x00'*8) 87 | return self._VC_pattern 88 | 89 | 90 | def read(self, s): 91 | self._read(self.decrypt(s)) 92 | 93 | def write(self, s): 94 | self._write(self.encrypt(s)) 95 | 96 | def setrawaccess(self, _read, _write): 97 | self._read = _read 98 | self._write = _write 99 | 100 | def padding(self): 101 | return urandom(randrange(PAD_MAX-16)+16) 102 | 103 | 104 | -------------------------------------------------------------------------------- /src/tornado/BitTornado/ConnChoice.py: -------------------------------------------------------------------------------- 1 | connChoices=( 2 | {'name':'automatic', 3 | 'rate':{'min':0, 'max':5000, 'def': 0}, 4 | 'conn':{'min':0, 'max':100, 'def': 0}, 5 | 'automatic':1}, 6 | {'name':'unlimited', 7 | 'rate':{'min':0, 'max':5000, 'def': 0, 'div': 50}, 8 | 'conn':{'min':4, 'max':100, 'def': 4}}, 9 | {'name':'dialup/isdn', 10 | 'rate':{'min':3, 'max': 8, 'def': 5}, 11 | 'conn':{'min':2, 'max': 3, 'def': 2}, 12 | 'initiate': 12}, 13 | {'name':'dsl/cable slow', 14 | 'rate':{'min':10, 'max': 48, 'def': 13}, 15 | 'conn':{'min':4, 'max': 20, 'def': 4}}, 16 | {'name':'dsl/cable fast', 17 | 'rate':{'min':20, 'max': 100, 'def': 40}, 18 | 'conn':{'min':4, 'max': 30, 'def': 6}}, 19 | {'name':'T1', 20 | 'rate':{'min':100, 'max': 300, 'def':150}, 21 | 'conn':{'min':4, 'max': 40, 'def':10}}, 22 | {'name':'T3+', 23 | 'rate':{'min':400, 'max':2000, 'def':500}, 24 | 'conn':{'min':4, 'max':100, 'def':20}}, 25 | {'name':'seeder', 26 | 'rate':{'min':0, 'max':5000, 'def':0, 'div': 50}, 27 | 'conn':{'min':1, 'max':100, 'def':1}}, 28 | {'name':'SUPER-SEED', 'super-seed':1} 29 | ) 30 | 31 | connChoiceList = map(lambda x:x['name'], connChoices) 32 | -------------------------------------------------------------------------------- /src/tornado/BitTornado/CreateIcons.py: -------------------------------------------------------------------------------- 1 | # Generated from bt_MakeCreateIcons - 05/10/04 22:15:33 2 | # T-0.3.0 (BitTornado) 3 | 4 | from binascii import a2b_base64 5 | from zlib import decompress 6 | from os.path import join 7 | 8 | icons = { 9 | "icon_bt.ico": 10 | "eJyt1K+OFEEQx/FaQTh5GDRZhSQpiUHwCrxCBYXFrjyJLXeXEARPsZqUPMm+" + 11 | "AlmP+PGtngoLDji69zMz2zt/qqtr1mxHv7621d4+MnvK/jl66Bl2drV+e7Wz" + 12 | "S/v12A7rY4fDtuvOwfF4tOPXo52/fLLz+WwpWd6nqRXHKXux39sTrtnjNd7g" + 13 | "PW7wGSd860f880kffjvJ2QYS1Zcw4AjcoaA5yRFIFDQXOgKJguZmjkCioB4T" + 14 | "Y2CqxpTXA7sHEgVNEC8RSBQ0gfk7xtknCupgk3EEEgXlNgFHIFHQTMoRSBQ0" + 15 | "E+1ouicKmsk7AomCJiGOQKKgSZIjkChoEucIJAqaZDoCiYImwb4iydULmqQ7" + 16 | "AomC1kLcEQ/jSBQ0i+MIJAqaBXMEElVdi9siOgKJgmZhfWWlVjTddXW/FtsR" + 17 | "SBQ0BeAIJAqaonAEEgVNoTgCiYKmeByBREHaqiVWRtSRrAJzBBIFTdE5AomC" + 18 | "phBPpxPP57dVkDfrTl063nUVnWe383fZx9tb3uN+o7U+BLDtuvcQm8d/27Y/" + 19 | "jO3o5/ay+YPv/+f6y30e1OyB7QcsGWFj", 20 | "icon_done.ico": 21 | "eJyt1K2OVEEQhuEaQbJyMWgyCklSEoPgFvYWKigsduRKbLndhCC4itGk5Erm" + 22 | "Fsh4xMdbfSoMOGDpnuf89Jyf6uqaMdvRr69ttbdPzJ6xf4Eeeo6dXa3vXu/s" + 23 | "0n49tsP62OGw7bpzcDwe7fj1aOcvn+x8PltKlg9pasVxyl7u9/aUe/Z4gxu8" + 24 | "xy0+44Rv/Yp/vujDbxc520Ci+hYGHIF7FDQXOQKJguZGRyBR0DzMEUgU1GNi" + 25 | "DEzVmPJ6YfdAoqAJ4hUCiYImMH/HOPtEQR1sMo5AoqDcJuAIJAqaSTkCiYJm" + 26 | "oh1N90RBM3lHIFHQJMQRSBQ0SXIEEgVN4hyBREGTTEcgUdAk2FckuXpBk3RH" + 27 | "IFHQWoh74mEciYJmcRyBREGzYI5AoqprcVtERyBR0Cysr6zUiqa7rh7WYjsC" + 28 | "iYKmAByBREFTFI5AoqApFEcgUdAUjyOQKEhbtcTKiDqSVWCOQKKgKTpHIFHQ" + 29 | "FOLpdOL9fLcK8nY9qUvHu66i8+x2/i77eHfH77h/0VofAth23Xuoz/+2bX8Y" + 30 | "29HP7WXzB+f/5/7Lcx7V7JHtB9dPG3I=", 31 | "black.ico": 32 | "eJzt1zsOgkAYReFLLCztjJ2UlpLY485kOS7DpbgESwqTcQZDghjxZwAfyfl0" + 33 | "LIieGzUWSom/pan840rHnbSUtPHHX9Je9+tAh2ybNe8TZZ/vk8ajJ4zl6JVJ" + 34 | "+xFx+0R03Djx1/2B8bcT9L/bt0+4Wq+4se8e/VTfMvGqb4n3nYiIGz+lvt9s" + 35 | "9EpE2T4xJN4xNFYWU6t+JWXuXDFzTom7SodSyi/S+iwtwjlJ80KaNY/C34rW" + 36 | "aT8nvK5uhF7ohn7Yqfb87kffLAAAAAAAAAAAAAAAAAAAGMUNy7dADg==", 37 | "blue.ico": 38 | "eJzt10EOwUAYhuGv6cLSTux06QD2dTM9jmM4iiNYdiEZ81cIFTWddtDkfbQW" + 39 | "De8XogtS5h9FIf+81H4jLSSt/ekvaavrdaCDez4SZV+PpPHoicBy9ErSfkQ8" + 40 | "fCI6Hjgx6f7A+McJ+r/t95i46xMP7bf8Uz9o4k0/XMT338voP5shK0MkjXcM" + 41 | "YSqam6Qunatyf7Nk7iztaqk8SaujNLfzIM0qKX88ZX8rWmf7Nfa+W8N61rW+" + 42 | "7TR7fverHxYAAAAAAAAAAAAAAAAAAIziApVZ444=", 43 | "green.ico": 44 | "eJzt1zEOgjAAheFHGBzdjJuMHsAdbybxNB7Do3gERwaT2mJIBCOWlqok/yc4" + 45 | "EP1fNDIoZfZRFLLPa5120krS1p72kvZ6XAeGHLtHouzrkTQePOFZDl5J2g+I" + 46 | "+08Exz0nZt2PjH+coP/bvveEaY2L+/VN13/1PSbe9v0FfP+jTP6ziVmJkTQ+" + 47 | "MISZaO6SujSmyu3dkpmbdKil8iptLtLSnWdpUUn58yn3t6J39l/j3tc2XM91" + 48 | "Xd/tNHt296sfFgAAAAAAAAAAAAAAAAAATOIOVLEoDg==", 49 | "red.ico": 50 | "eJzt10EOwUAYhuGv6cLSTux06QD2dTOO4xiO4giWXUjG/BVCRTuddtDkfbQW" + 51 | "De8XogtS5h9FIf+81GEjLSSt/ekvaavbdaCVez0SZd+PpPHoicBy9ErSfkQ8" + 52 | "fCI6Hjgx6f7AeOcE/d/2QyceesaD+g1/1u+e+NwPF/H99zL6z2bIyhBJ4y1D" + 53 | "mIb6LqlK5/a5v1syd5F2lVSepdVJmtt5lGZ7KX8+ZX8rGmfzNfa+e8N61rW+" + 54 | "7dR7fverHxYAAAAAAAAAAAAAAAAAAIziCpgs444=", 55 | "white.ico": 56 | "eJzt1zsOgkAYReFLKCztjJ2ULsAed6bLcRnuwYTaJVhSmIwzGBLEiD8D+EjO" + 57 | "p2NB9NyosVBK/C3L5B+XOmykhaS1P/6StrpfBzoUp6J5nyj7fJ80Hj1hLEev" + 58 | "TNqPiNsnouPGib/uD4y/naD/3b59wtV6xY199+in+paJV31LvO9ERNz4KfX9" + 59 | "ZqNXIsr2iSHxjqGxspha9Sspc+f2qXNK3FXalVJ+kVZnaR7OUZrtpbR5FP5W" + 60 | "tE77OeF1dSP0Qjf0w06153c/+mYBAAAAAAAAAAAAAAAAAMAobj//I7s=", 61 | "yellow.ico": 62 | "eJzt1zsOgkAYReFLKCztjJ2ULsAedybLcRkuxSVYUpiM82M0ihGHgVFJzidY" + 63 | "ED03vgqlzN+KQv5+qf1GWkha+9Nf0lbX60AX556ORNnXI2k8eiKwHL2StB8R" + 64 | "D5+IjgdOTLo/MP5xgv5v+8ETd/3iYf2W/+oHTLzth4t4/3sZ/WszZGWIpPGO" + 65 | "IUxE8yupS+eq3H9smTtLu1oqT9LqKM3tPEizSsofT9nfitbZfow979awnnWt" + 66 | "bzvNnt/96osFAAAAAAAAAAAAAAAAAACjuABhjmIs", 67 | "black1.ico": 68 | "eJzt0zEOgkAUANEhFpZSGTstTWzkVt5Cj8ZROAIHMNGPWBCFDYgxMZkHn2Iz" + 69 | "G5YCyOLKc+K54XSANbCPiSV2tOt/qjgW3XtSnN41FH/Qv29Jx/P7qefp7W8P" + 70 | "4z85HQ+9JRG/7BpTft31DPUKyiVcFjEZzQ/TTtdzrWnKmCr6evv780qSJEmS" + 71 | "JEmSJEmSJEmSpPnunVFDcA==", 72 | "green1.ico": 73 | "eJzt0zEKwkAQRuEXLCyTSuy0DHgxb6F4shzFI+QAgpkkFoombowIwvt2Z4vh" + 74 | "X5gtFrJYRUGca/Y7WAFlVLTY0vf/1elxTwqP3xoKf5B/vjIenp+fOs+r/LWT" + 75 | "/uQ34aGpUqQnv+1ygDqHagnHRVRG+2H6unfrtZkq6hz5evP7eSVJkiRJkiRJ" + 76 | "kiRJkiRJ0nwNoWQ+AA==", 77 | "yellow1.ico": 78 | "eJzt0zEKwkAQRuEXLCxNJXZaCl7MW8Sj5SgeIQcQ4oS1UDTJxkhAeN/ubDH8" + 79 | "C7PFQhGrLIlzx/kEW+AYFS0OpP6/atuXPSk8fKsv/EX+/cpweH5+6jyf8kn+" + 80 | "k0fCfVPlyE/+2q2CZgP1Gi6rqILuw6R69uh1mTrqGvlmv/y8kiRJkiRJkiRJ" + 81 | "kiRJkiRpvjsp9L8k", 82 | "alloc.gif": 83 | "eJxz93SzsEw0YRBh+M4ABi0MS3ue///P8H8UjIIRBhR/sjAyMDAx6IAyAihP" + 84 | "MHAcYWDlkPHYsOBgM4ewVsyJDQsPNzEoebF8CHjo0smjH3dmRsDjI33C7Dw3" + 85 | "MiYuOtjNyDShRSNwyemJguJJKhaGS32nGka61Vg2NJyYKRd+bY+nwtMzjbqV" + 86 | "Qh84gxMCJgnlL4vJuqJyaa5NfFLNLsNVV2a7syacfVWkHd4bv7RN1ltM7ejm" + 87 | "tMtNZ19Oyb02p8C3aqr3dr2GbXl/7fZyOej5rW653WZ7MzzHZV+v7O2/EZM+" + 88 | "Pt45kbX6ScWHNWfOilo3n5thucXv8org1XF3DRQYrAEWiVY3" 89 | } 90 | 91 | def GetIcons(): 92 | return icons.keys() 93 | 94 | def CreateIcon(icon, savedir): 95 | try: 96 | f = open(join(savedir,icon),"wb") 97 | f.write(decompress(a2b_base64(icons[icon]))) 98 | success = 1 99 | except: 100 | success = 0 101 | try: 102 | f.close() 103 | except: 104 | pass 105 | return success 106 | -------------------------------------------------------------------------------- /src/tornado/BitTornado/CurrentRateMeasure.py: -------------------------------------------------------------------------------- 1 | # Written by Bram Cohen 2 | # see LICENSE.txt for license information 3 | 4 | from clock import clock 5 | 6 | class Measure: 7 | def __init__(self, max_rate_period, fudge = 1): 8 | self.max_rate_period = max_rate_period 9 | self.ratesince = clock() - fudge 10 | self.last = self.ratesince 11 | self.rate = 0.0 12 | self.total = 0l 13 | 14 | def update_rate(self, amount): 15 | self.total += amount 16 | t = clock() 17 | self.rate = (self.rate * (self.last - self.ratesince) + 18 | amount) / (t - self.ratesince + 0.0001) 19 | self.last = t 20 | if self.ratesince < t - self.max_rate_period: 21 | self.ratesince = t - self.max_rate_period 22 | 23 | def get_rate(self): 24 | self.update_rate(0) 25 | return self.rate 26 | 27 | def get_rate_noupdate(self): 28 | return self.rate 29 | 30 | def time_until_rate(self, newrate): 31 | if self.rate <= newrate: 32 | return 0 33 | t = clock() - self.ratesince 34 | return ((self.rate * t) / newrate) - t 35 | 36 | def get_total(self): 37 | return self.total -------------------------------------------------------------------------------- /src/tornado/BitTornado/PSYCO.py: -------------------------------------------------------------------------------- 1 | # edit this file to enable/disable Psyco 2 | # psyco = 1 -- enabled 3 | # psyco = 0 -- disabled 4 | 5 | psyco = 0 6 | -------------------------------------------------------------------------------- /src/tornado/BitTornado/RateLimiter.py: -------------------------------------------------------------------------------- 1 | # Written by Bram Cohen 2 | # see LICENSE.txt for license information 3 | 4 | from traceback import print_exc 5 | from binascii import b2a_hex 6 | from clock import clock 7 | from CurrentRateMeasure import Measure 8 | from cStringIO import StringIO 9 | from math import sqrt 10 | 11 | try: 12 | True 13 | except: 14 | True = 1 15 | False = 0 16 | try: 17 | sum([1]) 18 | except: 19 | sum = lambda a: reduce(lambda x,y: x+y, a, 0) 20 | 21 | DEBUG = False 22 | 23 | MAX_RATE_PERIOD = 20.0 24 | MAX_RATE = 10e10 25 | PING_BOUNDARY = 1.2 26 | PING_SAMPLES = 7 27 | PING_DISCARDS = 1 28 | PING_THRESHHOLD = 5 29 | PING_DELAY = 5 # cycles 'til first upward adjustment 30 | PING_DELAY_NEXT = 2 # 'til next 31 | ADJUST_UP = 1.05 32 | ADJUST_DOWN = 0.95 33 | UP_DELAY_FIRST = 5 34 | UP_DELAY_NEXT = 2 35 | SLOTS_STARTING = 6 36 | SLOTS_FACTOR = 1.66/1000 37 | 38 | class RateLimiter: 39 | def __init__(self, sched, unitsize, slotsfunc = lambda x: None): 40 | self.sched = sched 41 | self.last = None 42 | self.unitsize = unitsize 43 | self.slotsfunc = slotsfunc 44 | self.measure = Measure(MAX_RATE_PERIOD) 45 | self.autoadjust = False 46 | self.upload_rate = MAX_RATE * 1000 47 | self.slots = SLOTS_STARTING # garbage if not automatic 48 | 49 | def set_upload_rate(self, rate): 50 | # rate = -1 # test automatic 51 | if rate < 0: 52 | if self.autoadjust: 53 | return 54 | self.autoadjust = True 55 | self.autoadjustup = 0 56 | self.pings = [] 57 | rate = MAX_RATE 58 | self.slots = SLOTS_STARTING 59 | self.slotsfunc(self.slots) 60 | else: 61 | self.autoadjust = False 62 | if not rate: 63 | rate = MAX_RATE 64 | self.upload_rate = rate * 1000 65 | self.lasttime = clock() 66 | self.bytes_sent = 0 67 | 68 | def queue(self, conn): 69 | assert conn.next_upload is None 70 | if self.last is None: 71 | self.last = conn 72 | conn.next_upload = conn 73 | self.try_send(True) 74 | else: 75 | conn.next_upload = self.last.next_upload 76 | self.last.next_upload = conn 77 | self.last = conn 78 | 79 | def try_send(self, check_time = False): 80 | t = clock() 81 | self.bytes_sent -= (t - self.lasttime) * self.upload_rate 82 | self.lasttime = t 83 | if check_time: 84 | self.bytes_sent = max(self.bytes_sent, 0) 85 | cur = self.last.next_upload 86 | while self.bytes_sent <= 0: 87 | bytes = cur.send_partial(self.unitsize) 88 | self.bytes_sent += bytes 89 | self.measure.update_rate(bytes) 90 | if bytes == 0 or cur.backlogged(): 91 | if self.last is cur: 92 | self.last = None 93 | cur.next_upload = None 94 | break 95 | else: 96 | self.last.next_upload = cur.next_upload 97 | cur.next_upload = None 98 | cur = self.last.next_upload 99 | else: 100 | self.last = cur 101 | cur = cur.next_upload 102 | else: 103 | self.sched(self.try_send, self.bytes_sent / self.upload_rate) 104 | 105 | def adjust_sent(self, bytes): 106 | self.bytes_sent = min(self.bytes_sent+bytes, self.upload_rate*3) 107 | self.measure.update_rate(bytes) 108 | 109 | 110 | def ping(self, delay): 111 | if DEBUG: 112 | print delay 113 | if not self.autoadjust: 114 | return 115 | self.pings.append(delay > PING_BOUNDARY) 116 | if len(self.pings) < PING_SAMPLES+PING_DISCARDS: 117 | return 118 | if DEBUG: 119 | print 'cycle' 120 | pings = sum(self.pings[PING_DISCARDS:]) 121 | del self.pings[:] 122 | if pings >= PING_THRESHHOLD: # assume flooded 123 | if self.upload_rate == MAX_RATE: 124 | self.upload_rate = self.measure.get_rate()*ADJUST_DOWN 125 | else: 126 | self.upload_rate = min(self.upload_rate, 127 | self.measure.get_rate()*1.1) 128 | self.upload_rate = max(int(self.upload_rate*ADJUST_DOWN),2) 129 | self.slots = int(sqrt(self.upload_rate*SLOTS_FACTOR)) 130 | self.slotsfunc(self.slots) 131 | if DEBUG: 132 | print 'adjust down to '+str(self.upload_rate) 133 | self.lasttime = clock() 134 | self.bytes_sent = 0 135 | self.autoadjustup = UP_DELAY_FIRST 136 | else: # not flooded 137 | if self.upload_rate == MAX_RATE: 138 | return 139 | self.autoadjustup -= 1 140 | if self.autoadjustup: 141 | return 142 | self.upload_rate = int(self.upload_rate*ADJUST_UP) 143 | self.slots = int(sqrt(self.upload_rate*SLOTS_FACTOR)) 144 | self.slotsfunc(self.slots) 145 | if DEBUG: 146 | print 'adjust up to '+str(self.upload_rate) 147 | self.lasttime = clock() 148 | self.bytes_sent = 0 149 | self.autoadjustup = UP_DELAY_NEXT 150 | 151 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /src/tornado/BitTornado/RateMeasure.py: -------------------------------------------------------------------------------- 1 | # Written by Bram Cohen 2 | # see LICENSE.txt for license information 3 | 4 | from clock import clock 5 | try: 6 | True 7 | except: 8 | True = 1 9 | False = 0 10 | 11 | FACTOR = 0.999 12 | 13 | class RateMeasure: 14 | def __init__(self): 15 | self.last = None 16 | self.time = 1.0 17 | self.got = 0.0 18 | self.remaining = None 19 | self.broke = False 20 | self.got_anything = False 21 | self.last_checked = None 22 | self.rate = 0 23 | self.lastten = False 24 | 25 | def data_came_in(self, amount): 26 | if not self.got_anything: 27 | self.got_anything = True 28 | self.last = clock() 29 | return 30 | self.update(amount) 31 | 32 | def data_rejected(self, amount): 33 | pass 34 | 35 | def get_time_left(self, left): 36 | t = clock() 37 | if not self.got_anything: 38 | return None 39 | if t - self.last > 15: 40 | self.update(0) 41 | try: 42 | remaining = left/self.rate 43 | if not self.lastten and remaining <= 10: 44 | self.lastten = True 45 | if self.lastten: 46 | return remaining 47 | delta = max(remaining/20,2) 48 | if self.remaining is None: 49 | self.remaining = remaining 50 | elif abs(self.remaining-remaining) > delta: 51 | self.remaining = remaining 52 | else: 53 | self.remaining -= t - self.last_checked 54 | except ZeroDivisionError: 55 | self.remaining = None 56 | if self.remaining is not None and self.remaining < 0.1: 57 | self.remaining = 0.1 58 | self.last_checked = t 59 | return self.remaining 60 | 61 | def update(self, amount): 62 | t = clock() 63 | t1 = int(t) 64 | l1 = int(self.last) 65 | for i in xrange(l1,t1): 66 | self.time *= FACTOR 67 | self.got *= FACTOR 68 | self.got += amount 69 | if t - self.last < 20: 70 | self.time += t - self.last 71 | self.last = t 72 | try: 73 | self.rate = self.got / self.time 74 | except ZeroDivisionError: 75 | pass 76 | -------------------------------------------------------------------------------- /src/tornado/BitTornado/__init__.py: -------------------------------------------------------------------------------- 1 | product_name = 'BitTornado' 2 | version_short = 'T-0.3.18' 3 | 4 | version = version_short+' ('+product_name+')' 5 | report_email = version_short+'@degreez.net' 6 | 7 | from types import StringType 8 | from hashlib import sha1 9 | from time import time, clock 10 | try: 11 | from os import getpid 12 | except ImportError: 13 | def getpid(): 14 | return 1 15 | 16 | mapbase64 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-' 17 | 18 | _idprefix = version_short[0] 19 | for subver in version_short[2:].split('.'): 20 | try: 21 | subver = int(subver) 22 | except: 23 | subver = 0 24 | _idprefix += mapbase64[subver] 25 | _idprefix += ('-' * (6-len(_idprefix))) 26 | _idrandom = [None] 27 | 28 | def resetPeerIDs(): 29 | try: 30 | f = open('/dev/urandom','rb') 31 | x = f.read(20) 32 | f.close() 33 | except: 34 | x = '' 35 | 36 | l1 = 0 37 | t = clock() 38 | while t == clock(): 39 | l1 += 1 40 | l2 = 0 41 | t = long(time()*100) 42 | while t == long(time()*100): 43 | l2 += 1 44 | l3 = 0 45 | if l2 < 1000: 46 | t = long(time()*10) 47 | while t == long(clock()*10): 48 | l3 += 1 49 | x += ( repr(time()) + '/' + str(time()) + '/' 50 | + str(l1) + '/' + str(l2) + '/' + str(l3) + '/' 51 | + str(getpid()) ) 52 | 53 | s = '' 54 | for i in sha1(x).digest()[-11:]: 55 | s += mapbase64[ord(i) & 0x3F] 56 | _idrandom[0] = s 57 | 58 | resetPeerIDs() 59 | 60 | def createPeerID(ins = '---'): 61 | assert type(ins) is StringType 62 | assert len(ins) == 3 63 | return _idprefix + ins + _idrandom[0] 64 | -------------------------------------------------------------------------------- /src/tornado/BitTornado/bitfield.py: -------------------------------------------------------------------------------- 1 | # Written by Bram Cohen, Uoti Urpala, and John Hoffman 2 | # see LICENSE.txt for license information 3 | 4 | try: 5 | True 6 | except: 7 | True = 1 8 | False = 0 9 | bool = lambda x: not not x 10 | 11 | try: 12 | sum([1]) 13 | negsum = lambda a: len(a)-sum(a) 14 | except: 15 | negsum = lambda a: reduce(lambda x,y: x+(not y), a, 0) 16 | 17 | def _int_to_booleans(x): 18 | r = [] 19 | for i in range(8): 20 | r.append(bool(x & 0x80)) 21 | x <<= 1 22 | return tuple(r) 23 | 24 | lookup_table = [] 25 | reverse_lookup_table = {} 26 | for i in xrange(256): 27 | x = _int_to_booleans(i) 28 | lookup_table.append(x) 29 | reverse_lookup_table[x] = chr(i) 30 | 31 | 32 | class Bitfield: 33 | def __init__(self, length = None, bitstring = None, copyfrom = None): 34 | if copyfrom is not None: 35 | self.length = copyfrom.length 36 | self.array = copyfrom.array[:] 37 | self.numfalse = copyfrom.numfalse 38 | return 39 | if length is None: 40 | raise ValueError, "length must be provided unless copying from another array" 41 | self.length = length 42 | if bitstring is not None: 43 | extra = len(bitstring) * 8 - length 44 | if extra < 0 or extra >= 8: 45 | raise ValueError 46 | t = lookup_table 47 | r = [] 48 | for c in bitstring: 49 | r.extend(t[ord(c)]) 50 | if extra > 0: 51 | if r[-extra:] != [0] * extra: 52 | raise ValueError 53 | del r[-extra:] 54 | self.array = r 55 | self.numfalse = negsum(r) 56 | else: 57 | self.array = [False] * length 58 | self.numfalse = length 59 | 60 | def __setitem__(self, index, val): 61 | val = bool(val) 62 | self.numfalse += self.array[index]-val 63 | self.array[index] = val 64 | 65 | def __getitem__(self, index): 66 | return self.array[index] 67 | 68 | def __len__(self): 69 | return self.length 70 | 71 | def tostring(self): 72 | booleans = self.array 73 | t = reverse_lookup_table 74 | s = len(booleans) % 8 75 | r = [ t[tuple(booleans[x:x+8])] for x in xrange(0, len(booleans)-s, 8) ] 76 | if s: 77 | r += t[tuple(booleans[-s:] + ([0] * (8-s)))] 78 | return ''.join(r) 79 | 80 | def complete(self): 81 | return not self.numfalse 82 | 83 | 84 | def test_bitfield(): 85 | try: 86 | x = Bitfield(7, 'ab') 87 | assert False 88 | except ValueError: 89 | pass 90 | try: 91 | x = Bitfield(7, 'ab') 92 | assert False 93 | except ValueError: 94 | pass 95 | try: 96 | x = Bitfield(9, 'abc') 97 | assert False 98 | except ValueError: 99 | pass 100 | try: 101 | x = Bitfield(0, 'a') 102 | assert False 103 | except ValueError: 104 | pass 105 | try: 106 | x = Bitfield(1, '') 107 | assert False 108 | except ValueError: 109 | pass 110 | try: 111 | x = Bitfield(7, '') 112 | assert False 113 | except ValueError: 114 | pass 115 | try: 116 | x = Bitfield(8, '') 117 | assert False 118 | except ValueError: 119 | pass 120 | try: 121 | x = Bitfield(9, 'a') 122 | assert False 123 | except ValueError: 124 | pass 125 | try: 126 | x = Bitfield(7, chr(1)) 127 | assert False 128 | except ValueError: 129 | pass 130 | try: 131 | x = Bitfield(9, chr(0) + chr(0x40)) 132 | assert False 133 | except ValueError: 134 | pass 135 | assert Bitfield(0, '').tostring() == '' 136 | assert Bitfield(1, chr(0x80)).tostring() == chr(0x80) 137 | assert Bitfield(7, chr(0x02)).tostring() == chr(0x02) 138 | assert Bitfield(8, chr(0xFF)).tostring() == chr(0xFF) 139 | assert Bitfield(9, chr(0) + chr(0x80)).tostring() == chr(0) + chr(0x80) 140 | x = Bitfield(1) 141 | assert x.numfalse == 1 142 | x[0] = 1 143 | assert x.numfalse == 0 144 | x[0] = 1 145 | assert x.numfalse == 0 146 | assert x.tostring() == chr(0x80) 147 | x = Bitfield(7) 148 | assert len(x) == 7 149 | x[6] = 1 150 | assert x.numfalse == 6 151 | assert x.tostring() == chr(0x02) 152 | x = Bitfield(8) 153 | x[7] = 1 154 | assert x.tostring() == chr(1) 155 | x = Bitfield(9) 156 | x[8] = 1 157 | assert x.numfalse == 8 158 | assert x.tostring() == chr(0) + chr(0x80) 159 | x = Bitfield(8, chr(0xC4)) 160 | assert len(x) == 8 161 | assert x.numfalse == 5 162 | assert x.tostring() == chr(0xC4) 163 | -------------------------------------------------------------------------------- /src/tornado/BitTornado/clock.py: -------------------------------------------------------------------------------- 1 | # Written by John Hoffman 2 | # see LICENSE.txt for license information 3 | 4 | from time import * 5 | import sys 6 | 7 | _MAXFORWARD = 100 8 | _FUDGE = 1 9 | 10 | class RelativeTime: 11 | def __init__(self): 12 | self.time = time() 13 | self.offset = 0 14 | 15 | def get_time(self): 16 | t = time() + self.offset 17 | if t < self.time or t > self.time + _MAXFORWARD: 18 | self.time += _FUDGE 19 | self.offset += self.time - t 20 | return self.time 21 | self.time = t 22 | return t 23 | 24 | if sys.platform != 'win32': 25 | _RTIME = RelativeTime() 26 | def clock(): 27 | return _RTIME.get_time() -------------------------------------------------------------------------------- /src/tornado/BitTornado/inifile.py: -------------------------------------------------------------------------------- 1 | # Written by John Hoffman 2 | # see LICENSE.txt for license information 3 | 4 | ''' 5 | reads/writes a Windows-style INI file 6 | format: 7 | 8 | aa = "bb" 9 | cc = 11 10 | 11 | [eee] 12 | ff = "gg" 13 | 14 | decodes to: 15 | d = { '': {'aa':'bb','cc':'11'}, 'eee': {'ff':'gg'} } 16 | 17 | the encoder can also take this as input: 18 | 19 | d = { 'aa': 'bb, 'cc': 11, 'eee': {'ff':'gg'} } 20 | 21 | though it will only decode in the above format. Keywords must be strings. 22 | Values that are strings are written surrounded by quotes, and the decoding 23 | routine automatically strips any. 24 | Booleans are written as integers. Anything else aside from string/int/float 25 | may have unpredictable results. 26 | ''' 27 | 28 | from cStringIO import StringIO 29 | from traceback import print_exc 30 | from types import DictType, StringType 31 | try: 32 | from types import BooleanType 33 | except ImportError: 34 | BooleanType = None 35 | 36 | try: 37 | True 38 | except: 39 | True = 1 40 | False = 0 41 | 42 | DEBUG = False 43 | 44 | def ini_write(f, d, comment=''): 45 | try: 46 | a = {'':{}} 47 | for k,v in d.items(): 48 | assert type(k) == StringType 49 | k = k.lower() 50 | if type(v) == DictType: 51 | if DEBUG: 52 | print 'new section:' +k 53 | if k: 54 | assert not a.has_key(k) 55 | a[k] = {} 56 | aa = a[k] 57 | for kk,vv in v: 58 | assert type(kk) == StringType 59 | kk = kk.lower() 60 | assert not aa.has_key(kk) 61 | if type(vv) == BooleanType: 62 | vv = int(vv) 63 | if type(vv) == StringType: 64 | vv = '"'+vv+'"' 65 | aa[kk] = str(vv) 66 | if DEBUG: 67 | print 'a['+k+']['+kk+'] = '+str(vv) 68 | else: 69 | aa = a[''] 70 | assert not aa.has_key(k) 71 | if type(v) == BooleanType: 72 | v = int(v) 73 | if type(v) == StringType: 74 | v = '"'+v+'"' 75 | aa[k] = str(v) 76 | if DEBUG: 77 | print 'a[\'\']['+k+'] = '+str(v) 78 | r = open(f,'w') 79 | if comment: 80 | for c in comment.split('\n'): 81 | r.write('# '+c+'\n') 82 | r.write('\n') 83 | l = a.keys() 84 | l.sort() 85 | for k in l: 86 | if k: 87 | r.write('\n['+k+']\n') 88 | aa = a[k] 89 | ll = aa.keys() 90 | ll.sort() 91 | for kk in ll: 92 | r.write(kk+' = '+aa[kk]+'\n') 93 | success = True 94 | except: 95 | if DEBUG: 96 | print_exc() 97 | success = False 98 | try: 99 | r.close() 100 | except: 101 | pass 102 | return success 103 | 104 | 105 | if DEBUG: 106 | def errfunc(lineno, line, err): 107 | print '('+str(lineno)+') '+err+': '+line 108 | else: 109 | errfunc = lambda lineno, line, err: None 110 | 111 | def ini_read(f, errfunc = errfunc): 112 | try: 113 | r = open(f,'r') 114 | ll = r.readlines() 115 | d = {} 116 | dd = {'':d} 117 | for i in xrange(len(ll)): 118 | l = ll[i] 119 | l = l.strip() 120 | if not l: 121 | continue 122 | if l[0] == '#': 123 | continue 124 | if l[0] == '[': 125 | if l[-1] != ']': 126 | errfunc(i,l,'syntax error') 127 | continue 128 | l1 = l[1:-1].strip().lower() 129 | if not l1: 130 | errfunc(i,l,'syntax error') 131 | continue 132 | if dd.has_key(l1): 133 | errfunc(i,l,'duplicate section') 134 | d = dd[l1] 135 | continue 136 | d = {} 137 | dd[l1] = d 138 | continue 139 | try: 140 | k,v = l.split('=',1) 141 | except: 142 | try: 143 | k,v = l.split(':',1) 144 | except: 145 | errfunc(i,l,'syntax error') 146 | continue 147 | k = k.strip().lower() 148 | v = v.strip() 149 | if len(v) > 1 and ( (v[0] == '"' and v[-1] == '"') or 150 | (v[0] == "'" and v[-1] == "'") ): 151 | v = v[1:-1] 152 | if not k: 153 | errfunc(i,l,'syntax error') 154 | continue 155 | if d.has_key(k): 156 | errfunc(i,l,'duplicate entry') 157 | continue 158 | d[k] = v 159 | if DEBUG: 160 | print dd 161 | except: 162 | if DEBUG: 163 | print_exc() 164 | dd = None 165 | try: 166 | r.close() 167 | except: 168 | pass 169 | return dd 170 | -------------------------------------------------------------------------------- /src/tornado/BitTornado/parseargs.py: -------------------------------------------------------------------------------- 1 | # Written by Bill Bumgarner and Bram Cohen 2 | # see LICENSE.txt for license information 3 | 4 | from types import * 5 | from cStringIO import StringIO 6 | 7 | 8 | def splitLine(line, COLS=80, indent=10): 9 | indent = " " * indent 10 | width = COLS - (len(indent) + 1) 11 | if indent and width < 15: 12 | width = COLS - 2 13 | indent = " " 14 | s = StringIO() 15 | i = 0 16 | for word in line.split(): 17 | if i == 0: 18 | s.write(indent+word) 19 | i = len(word) 20 | continue 21 | if i + len(word) >= width: 22 | s.write('\n'+indent+word) 23 | i = len(word) 24 | continue 25 | s.write(' '+word) 26 | i += len(word) + 1 27 | return s.getvalue() 28 | 29 | def formatDefinitions(options, COLS, presets = {}): 30 | s = StringIO() 31 | for (longname, default, doc) in options: 32 | s.write('--' + longname + ' \n') 33 | default = presets.get(longname, default) 34 | if type(default) in (IntType, LongType): 35 | try: 36 | default = int(default) 37 | except: 38 | pass 39 | if default is not None: 40 | doc += ' (defaults to ' + repr(default) + ')' 41 | s.write(splitLine(doc,COLS,10)) 42 | s.write('\n\n') 43 | return s.getvalue() 44 | 45 | 46 | def usage(str): 47 | raise ValueError(str) 48 | 49 | 50 | def defaultargs(options): 51 | l = {} 52 | for (longname, default, doc) in options: 53 | if default is not None: 54 | l[longname] = default 55 | return l 56 | 57 | 58 | def parseargs(argv, options, minargs = None, maxargs = None, presets = {}): 59 | config = {} 60 | longkeyed = {} 61 | for option in options: 62 | longname, default, doc = option 63 | longkeyed[longname] = option 64 | config[longname] = default 65 | for longname in presets.keys(): # presets after defaults but before arguments 66 | config[longname] = presets[longname] 67 | options = [] 68 | args = [] 69 | pos = 0 70 | while pos < len(argv): 71 | if argv[pos][:2] != '--': 72 | args.append(argv[pos]) 73 | pos += 1 74 | else: 75 | if pos == len(argv) - 1: 76 | usage('parameter passed in at end with no value') 77 | key, value = argv[pos][2:], argv[pos+1] 78 | pos += 2 79 | if not longkeyed.has_key(key): 80 | usage('unknown key --' + key) 81 | longname, default, doc = longkeyed[key] 82 | try: 83 | t = type(config[longname]) 84 | if t is NoneType or t is StringType: 85 | config[longname] = value 86 | elif t in (IntType, LongType): 87 | config[longname] = long(value) 88 | elif t is FloatType: 89 | config[longname] = float(value) 90 | else: 91 | assert 0 92 | except ValueError, e: 93 | usage('wrong format of --%s - %s' % (key, str(e))) 94 | for key, value in config.items(): 95 | if value is None: 96 | usage("Option --%s is required." % key) 97 | if minargs is not None and len(args) < minargs: 98 | usage("Must supply at least %d args." % minargs) 99 | if maxargs is not None and len(args) > maxargs: 100 | usage("Too many args - %d max." % maxargs) 101 | return (config, args) 102 | 103 | def test_parseargs(): 104 | assert parseargs(('d', '--a', 'pq', 'e', '--b', '3', '--c', '4.5', 'f'), (('a', 'x', ''), ('b', 1, ''), ('c', 2.3, ''))) == ({'a': 'pq', 'b': 3, 'c': 4.5}, ['d', 'e', 'f']) 105 | assert parseargs([], [('a', 'x', '')]) == ({'a': 'x'}, []) 106 | assert parseargs(['--a', 'x', '--a', 'y'], [('a', '', '')]) == ({'a': 'y'}, []) 107 | try: 108 | parseargs([], [('a', 'x', '')]) 109 | except ValueError: 110 | pass 111 | try: 112 | parseargs(['--a', 'x'], []) 113 | except ValueError: 114 | pass 115 | try: 116 | parseargs(['--a'], [('a', 'x', '')]) 117 | except ValueError: 118 | pass 119 | try: 120 | parseargs([], [], 1, 2) 121 | except ValueError: 122 | pass 123 | assert parseargs(['x'], [], 1, 2) == ({}, ['x']) 124 | assert parseargs(['x', 'y'], [], 1, 2) == ({}, ['x', 'y']) 125 | try: 126 | parseargs(['x', 'y', 'z'], [], 1, 2) 127 | except ValueError: 128 | pass 129 | try: 130 | parseargs(['--a', '2.0'], [('a', 3, '')]) 131 | except ValueError: 132 | pass 133 | try: 134 | parseargs(['--a', 'z'], [('a', 2.1, '')]) 135 | except ValueError: 136 | pass 137 | 138 | -------------------------------------------------------------------------------- /src/tornado/BitTornado/parsedir.py: -------------------------------------------------------------------------------- 1 | # Written by John Hoffman and Uoti Urpala 2 | # see LICENSE.txt for license information 3 | from bencode import bencode, bdecode 4 | from BT1.btformats import check_info 5 | from os.path import exists, isfile 6 | from hashlib import sha1 7 | import sys, os 8 | 9 | try: 10 | True 11 | except: 12 | True = 1 13 | False = 0 14 | 15 | NOISY = False 16 | 17 | def _errfunc(x): 18 | print ":: "+x 19 | 20 | def parsedir(directory, parsed, files, blocked, 21 | exts = ['.torrent'], return_metainfo = False, errfunc = _errfunc): 22 | if NOISY: 23 | errfunc('checking dir') 24 | dirs_to_check = [directory] 25 | new_files = {} 26 | new_blocked = {} 27 | torrent_type = {} 28 | while dirs_to_check: # first, recurse directories and gather torrents 29 | directory = dirs_to_check.pop() 30 | newtorrents = False 31 | for f in os.listdir(directory): 32 | newtorrent = None 33 | for ext in exts: 34 | if f.endswith(ext): 35 | newtorrent = ext[1:] 36 | break 37 | if newtorrent: 38 | newtorrents = True 39 | p = os.path.join(directory, f) 40 | new_files[p] = [(os.path.getmtime(p), os.path.getsize(p)), 0] 41 | torrent_type[p] = newtorrent 42 | if not newtorrents: 43 | for f in os.listdir(directory): 44 | p = os.path.join(directory, f) 45 | if os.path.isdir(p): 46 | dirs_to_check.append(p) 47 | 48 | new_parsed = {} 49 | to_add = [] 50 | added = {} 51 | removed = {} 52 | # files[path] = [(modification_time, size), hash], hash is 0 if the file 53 | # has not been successfully parsed 54 | for p,v in new_files.items(): # re-add old items and check for changes 55 | oldval = files.get(p) 56 | if not oldval: # new file 57 | to_add.append(p) 58 | continue 59 | h = oldval[1] 60 | if oldval[0] == v[0]: # file is unchanged from last parse 61 | if h: 62 | if blocked.has_key(p): # parseable + blocked means duplicate 63 | to_add.append(p) # other duplicate may have gone away 64 | else: 65 | new_parsed[h] = parsed[h] 66 | new_files[p] = oldval 67 | else: 68 | new_blocked[p] = 1 # same broken unparseable file 69 | continue 70 | if parsed.has_key(h) and not blocked.has_key(p): 71 | if NOISY: 72 | errfunc('removing '+p+' (will re-add)') 73 | removed[h] = parsed[h] 74 | to_add.append(p) 75 | 76 | to_add.sort() 77 | for p in to_add: # then, parse new and changed torrents 78 | new_file = new_files[p] 79 | v,h = new_file 80 | if new_parsed.has_key(h): # duplicate 81 | if not blocked.has_key(p) or files[p][0] != v: 82 | errfunc('**warning** '+ 83 | p +' is a duplicate torrent for '+new_parsed[h]['path']) 84 | new_blocked[p] = 1 85 | continue 86 | 87 | if NOISY: 88 | errfunc('adding '+p) 89 | try: 90 | ff = open(p, 'rb') 91 | d = bdecode(ff.read()) 92 | check_info(d['info']) 93 | h = sha1(bencode(d['info'])).digest() 94 | new_file[1] = h 95 | if new_parsed.has_key(h): 96 | errfunc('**warning** '+ 97 | p +' is a duplicate torrent for '+new_parsed[h]['path']) 98 | new_blocked[p] = 1 99 | continue 100 | 101 | a = {} 102 | a['path'] = p 103 | f = os.path.basename(p) 104 | a['file'] = f 105 | a['type'] = torrent_type[p] 106 | i = d['info'] 107 | l = 0 108 | nf = 0 109 | if i.has_key('length'): 110 | l = i.get('length',0) 111 | nf = 1 112 | elif i.has_key('files'): 113 | for li in i['files']: 114 | nf += 1 115 | if li.has_key('length'): 116 | l += li['length'] 117 | a['numfiles'] = nf 118 | a['length'] = l 119 | a['name'] = i.get('name', f) 120 | def setkey(k, d = d, a = a): 121 | if d.has_key(k): 122 | a[k] = d[k] 123 | setkey('failure reason') 124 | setkey('warning message') 125 | setkey('announce-list') 126 | if return_metainfo: 127 | a['metainfo'] = d 128 | except: 129 | errfunc('**warning** '+p+' has errors') 130 | new_blocked[p] = 1 131 | continue 132 | try: 133 | ff.close() 134 | except: 135 | pass 136 | if NOISY: 137 | errfunc('... successful') 138 | new_parsed[h] = a 139 | added[h] = a 140 | 141 | for p,v in files.items(): # and finally, mark removed torrents 142 | if not new_files.has_key(p) and not blocked.has_key(p): 143 | if NOISY: 144 | errfunc('removing '+p) 145 | removed[v[1]] = parsed[v[1]] 146 | 147 | if NOISY: 148 | errfunc('done checking') 149 | return (new_parsed, new_files, new_blocked, added, removed) 150 | 151 | -------------------------------------------------------------------------------- /src/tornado/BitTornado/piecebuffer.py: -------------------------------------------------------------------------------- 1 | # Written by John Hoffman 2 | # see LICENSE.txt for license information 3 | 4 | from array import array 5 | from threading import Lock 6 | # import inspect 7 | try: 8 | True 9 | except: 10 | True = 1 11 | False = 0 12 | 13 | DEBUG = False 14 | 15 | class SingleBuffer: 16 | def __init__(self, pool): 17 | self.pool = pool 18 | self.buf = array('c') 19 | 20 | def init(self): 21 | if DEBUG: 22 | print self.count 23 | ''' 24 | for x in xrange(6,1,-1): 25 | try: 26 | f = inspect.currentframe(x).f_code 27 | print (f.co_filename,f.co_firstlineno,f.co_name) 28 | del f 29 | except: 30 | pass 31 | print '' 32 | ''' 33 | self.length = 0 34 | 35 | def append(self, s): 36 | l = self.length+len(s) 37 | self.buf[self.length:l] = array('c',s) 38 | self.length = l 39 | 40 | def __len__(self): 41 | return self.length 42 | 43 | def __getslice__(self, a, b): 44 | if b > self.length: 45 | b = self.length 46 | if b < 0: 47 | b += self.length 48 | if a == 0 and b == self.length and len(self.buf) == b: 49 | return self.buf # optimization 50 | return self.buf[a:b] 51 | 52 | def getarray(self): 53 | return self.buf[:self.length] 54 | 55 | def release(self): 56 | if DEBUG: 57 | print -self.count 58 | self.pool.release(self) 59 | 60 | 61 | class BufferPool: 62 | def __init__(self): 63 | self.pool = [] 64 | self.lock = Lock() 65 | if DEBUG: 66 | self.count = 0 67 | 68 | def new(self): 69 | self.lock.acquire() 70 | if self.pool: 71 | x = self.pool.pop() 72 | else: 73 | x = SingleBuffer(self) 74 | if DEBUG: 75 | self.count += 1 76 | x.count = self.count 77 | x.init() 78 | self.lock.release() 79 | return x 80 | 81 | def release(self, x): 82 | self.pool.append(x) 83 | 84 | 85 | _pool = BufferPool() 86 | PieceBuffer = _pool.new 87 | -------------------------------------------------------------------------------- /src/tornado/BitTornado/selectpoll.py: -------------------------------------------------------------------------------- 1 | # Written by Bram Cohen 2 | # see LICENSE.txt for license information 3 | 4 | from select import select, error 5 | from time import sleep 6 | from types import IntType 7 | from bisect import bisect 8 | POLLIN = 1 9 | POLLOUT = 2 10 | POLLERR = 8 11 | POLLHUP = 16 12 | 13 | class poll: 14 | def __init__(self): 15 | self.rlist = [] 16 | self.wlist = [] 17 | 18 | def register(self, f, t): 19 | if type(f) != IntType: 20 | f = f.fileno() 21 | if (t & POLLIN): 22 | insert(self.rlist, f) 23 | else: 24 | remove(self.rlist, f) 25 | if (t & POLLOUT): 26 | insert(self.wlist, f) 27 | else: 28 | remove(self.wlist, f) 29 | 30 | def unregister(self, f): 31 | if type(f) != IntType: 32 | f = f.fileno() 33 | remove(self.rlist, f) 34 | remove(self.wlist, f) 35 | 36 | def poll(self, timeout = None): 37 | if self.rlist or self.wlist: 38 | try: 39 | r, w, e = select(self.rlist, self.wlist, [], timeout) 40 | except ValueError: 41 | return None 42 | else: 43 | sleep(timeout) 44 | return [] 45 | result = [] 46 | for s in r: 47 | result.append((s, POLLIN)) 48 | for s in w: 49 | result.append((s, POLLOUT)) 50 | return result 51 | 52 | def remove(list, item): 53 | i = bisect(list, item) 54 | if i > 0 and list[i-1] == item: 55 | del list[i-1] 56 | 57 | def insert(list, item): 58 | i = bisect(list, item) 59 | if i == 0 or list[i-1] != item: 60 | list.insert(i, item) 61 | 62 | def test_remove(): 63 | x = [2, 4, 6] 64 | remove(x, 2) 65 | assert x == [4, 6] 66 | x = [2, 4, 6] 67 | remove(x, 4) 68 | assert x == [2, 6] 69 | x = [2, 4, 6] 70 | remove(x, 6) 71 | assert x == [2, 4] 72 | x = [2, 4, 6] 73 | remove(x, 5) 74 | assert x == [2, 4, 6] 75 | x = [2, 4, 6] 76 | remove(x, 1) 77 | assert x == [2, 4, 6] 78 | x = [2, 4, 6] 79 | remove(x, 7) 80 | assert x == [2, 4, 6] 81 | x = [2, 4, 6] 82 | remove(x, 5) 83 | assert x == [2, 4, 6] 84 | x = [] 85 | remove(x, 3) 86 | assert x == [] 87 | 88 | def test_insert(): 89 | x = [2, 4] 90 | insert(x, 1) 91 | assert x == [1, 2, 4] 92 | x = [2, 4] 93 | insert(x, 3) 94 | assert x == [2, 3, 4] 95 | x = [2, 4] 96 | insert(x, 5) 97 | assert x == [2, 4, 5] 98 | x = [2, 4] 99 | insert(x, 2) 100 | assert x == [2, 4] 101 | x = [2, 4] 102 | insert(x, 4) 103 | assert x == [2, 4] 104 | x = [2, 3, 4] 105 | insert(x, 3) 106 | assert x == [2, 3, 4] 107 | x = [] 108 | insert(x, 3) 109 | assert x == [3] 110 | -------------------------------------------------------------------------------- /src/tornado/BitTornado/torrentlistparse.py: -------------------------------------------------------------------------------- 1 | # Written by John Hoffman 2 | # see LICENSE.txt for license information 3 | 4 | from binascii import unhexlify 5 | 6 | try: 7 | True 8 | except: 9 | True = 1 10 | False = 0 11 | 12 | 13 | # parses a list of torrent hashes, in the format of one hash per line in hex format 14 | 15 | def parsetorrentlist(filename, parsed): 16 | new_parsed = {} 17 | added = {} 18 | removed = parsed 19 | f = open(filename, 'r') 20 | while True: 21 | l = f.readline() 22 | if not l: 23 | break 24 | l = l.strip() 25 | try: 26 | if len(l) != 40: 27 | raise ValueError, 'bad line' 28 | h = unhexlify(l) 29 | except: 30 | print '*** WARNING *** could not parse line in torrent list: '+l 31 | if parsed.has_key(h): 32 | del removed[h] 33 | else: 34 | added[h] = True 35 | new_parsed[h] = True 36 | f.close() 37 | return (new_parsed, added, removed) 38 | 39 | -------------------------------------------------------------------------------- /src/tornado/BitTornado/zurllib.py: -------------------------------------------------------------------------------- 1 | # Written by John Hoffman 2 | # see LICENSE.txt for license information 3 | 4 | from httplib import HTTPConnection, HTTPSConnection, HTTPException 5 | from urlparse import urlparse 6 | from bencode import bdecode 7 | import socket 8 | from gzip import GzipFile 9 | from StringIO import StringIO 10 | from urllib import quote, unquote 11 | from __init__ import product_name, version_short 12 | 13 | VERSION = product_name+'/'+version_short 14 | MAX_REDIRECTS = 10 15 | 16 | 17 | class btHTTPcon(HTTPConnection): # attempt to add automatic connection timeout 18 | def connect(self): 19 | 20 | # proxy hack 21 | #self.host='127.0.0.1' 22 | #self.port=8888 #privoxy (def 8118) 23 | 24 | HTTPConnection.connect(self) 25 | try: 26 | self.sock.settimeout(30) 27 | except: 28 | pass 29 | 30 | class btHTTPScon(HTTPSConnection): # attempt to add automatic connection timeout 31 | def connect(self): 32 | HTTPSConnection.connect(self) 33 | try: 34 | self.sock.settimeout(30) 35 | except: 36 | pass 37 | 38 | class urlopen: 39 | def __init__(self, url): 40 | self.tries = 0 41 | self._open(url.strip()) 42 | self.error_return = None 43 | 44 | def _open(self, url): 45 | self.tries += 1 46 | if self.tries > MAX_REDIRECTS: 47 | raise IOError, ('http error', 500, 48 | "Internal Server Error: Redirect Recursion") 49 | (scheme, netloc, path, pars, query, fragment) = urlparse(url) 50 | if scheme != 'http' and scheme != 'https': 51 | raise IOError, ('url error', 'unknown url type', scheme, url) 52 | url = path 53 | if pars: 54 | url += ';'+pars 55 | if query: 56 | url += '?'+query 57 | # if fragment: 58 | try: 59 | if scheme == 'http': 60 | self.connection = btHTTPcon(netloc) 61 | else: 62 | self.connection = btHTTPScon(netloc) 63 | self.connection.request('GET', url, None, 64 | # proxy hack 65 | #self.connection.request('GET', scheme + '://' + netloc + url, None, 66 | { 'User-Agent': VERSION, 67 | 'Accept-Encoding': 'gzip' } ) 68 | self.response = self.connection.getresponse() 69 | except HTTPException, e: 70 | raise IOError, ('http error', str(e)) 71 | status = self.response.status 72 | if status in (300,301,302,303,307): 73 | try: 74 | self.connection.close() 75 | except: 76 | pass 77 | self._open(self.response.getheader('Location')) 78 | return 79 | if status != 200: 80 | try: 81 | data = self._read() 82 | d = bdecode(data) 83 | if d.has_key('failure reason'): 84 | self.error_return = data 85 | return 86 | except: 87 | pass 88 | raise IOError, ('http error', status, self.response.reason + ' ' + netloc) 89 | 90 | def read(self): 91 | if self.error_return: 92 | return self.error_return 93 | return self._read() 94 | 95 | def _read(self): 96 | data = self.response.read() 97 | if self.response.getheader('Content-Encoding','').find('gzip') >= 0: 98 | try: 99 | compressed = StringIO(data) 100 | f = GzipFile(fileobj = compressed) 101 | data = f.read() 102 | except: 103 | raise IOError, ('http error', 'got corrupt response') 104 | return data 105 | 106 | def close(self): 107 | self.connection.close() 108 | -------------------------------------------------------------------------------- /src/tornado/btmakemetafile.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Written by Bram Cohen 4 | # multitracker extensions by John Hoffman 5 | # see LICENSE.txt for license information 6 | 7 | # $Id$ 8 | 9 | from BitTornado import PSYCO 10 | if PSYCO.psyco: 11 | try: 12 | import psyco 13 | assert psyco.__version__ >= 0x010100f0 14 | psyco.full() 15 | except: 16 | pass 17 | 18 | from sys import argv, version, exit 19 | from os.path import split 20 | assert version >= '2', "Install Python 2.0 or greater" 21 | from BitTornado.BT1.makemetafile import make_meta_file, defaults, print_announcelist_details 22 | from BitTornado.parseargs import parseargs, formatDefinitions 23 | 24 | 25 | def prog(amount): 26 | print '%.1f%% complete\r' % (amount * 100), 27 | 28 | if len(argv) < 3: 29 | a,b = split(argv[0]) 30 | print 'Usage: ' + b + ' [file...] [params...]' 31 | print 32 | print formatDefinitions(defaults, 80) 33 | print_announcelist_details() 34 | print ('') 35 | exit(2) 36 | 37 | try: 38 | config, args = parseargs(argv[1:], defaults, 2, None) 39 | for file in args[1:]: 40 | make_meta_file(file, args[0], config, progress = prog) 41 | except ValueError, e: 42 | print 'error: ' + str(e) 43 | print 'run with no args for parameter explanations' 44 | -------------------------------------------------------------------------------- /src/tornado/btshowmetainfo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Written by Henry 'Pi' James and Loring Holden 4 | # modified for multitracker display by John Hoffman 5 | # see LICENSE.txt for license information 6 | 7 | # $Id$ 8 | 9 | from sys import * 10 | from os.path import * 11 | from hashlib import sha1 12 | from BitTornado.bencode import * 13 | 14 | NAME, EXT = splitext(basename(argv[0])) 15 | VERSION = '20030621' 16 | 17 | print '%s %s - decode BitTorrent metainfo files' % (NAME, VERSION) 18 | print 19 | 20 | if len(argv) == 1: 21 | print '%s file1.torrent file2.torrent file3.torrent ...' % argv[0] 22 | print 23 | exit(2) # common exit code for syntax error 24 | 25 | for metainfo_name in argv[1:]: 26 | metainfo_file = open(metainfo_name, 'rb') 27 | metainfo = bdecode(metainfo_file.read()) 28 | # print metainfo 29 | info = metainfo['info'] 30 | info_hash = sha1(bencode(info)) 31 | 32 | print 'metainfo file.: %s' % basename(metainfo_name) 33 | print 'info hash.....: %s' % info_hash.hexdigest() 34 | piece_length = info['piece length'] 35 | if info.has_key('length'): 36 | # let's assume we just have a file 37 | print 'file name.....: %s' % info['name'] 38 | file_length = info['length'] 39 | name ='file size.....:' 40 | else: 41 | # let's assume we have a directory structure 42 | print 'directory name: %s' % info['name'] 43 | print 'files.........: ' 44 | file_length = 0; 45 | for file in info['files']: 46 | path = '' 47 | for item in file['path']: 48 | if (path != ''): 49 | path = path + "/" 50 | path = path + item 51 | print ' %s (%d)' % (path, file['length']) 52 | file_length += file['length'] 53 | name ='archive size..:' 54 | piece_number, last_piece_length = divmod(file_length, piece_length) 55 | print '%s %i (%i * %i + %i)' \ 56 | % (name,file_length, piece_number, piece_length, last_piece_length) 57 | print 'announce url..: %s' % metainfo['announce'] 58 | if metainfo.has_key('announce-list'): 59 | list = [] 60 | for tier in metainfo['announce-list']: 61 | for tracker in tier: 62 | list+=[tracker,','] 63 | del list[-1] 64 | list+=['|'] 65 | del list[-1] 66 | liststring = '' 67 | for i in list: 68 | liststring+=i 69 | print 'announce-list.: %s' % liststring 70 | if metainfo.has_key('httpseeds'): 71 | list = [] 72 | for seed in metainfo['httpseeds']: 73 | list += [seed,'|'] 74 | del list[-1] 75 | liststring = '' 76 | for i in list: 77 | liststring+=i 78 | print 'http seeds....: %s' % liststring 79 | if metainfo.has_key('comment'): 80 | print 'comment.......: %s' % metainfo['comment'] 81 | -------------------------------------------------------------------------------- /src/uploadify/uploadify.css: -------------------------------------------------------------------------------- 1 | /* 2 | Uploadify 3 | Copyright (c) 2012 Reactive Apps, Ronnie Garcia 4 | Released under the MIT License 5 | */ 6 | 7 | .uploadify-queue { 8 | margin-bottom: 1em; 9 | } 10 | .uploadify-queue-item { 11 | background-color: #F5F5F5; 12 | -webkit-border-radius: 3px; 13 | -moz-border-radius: 3px; 14 | border-radius: 3px; 15 | font: 11px Verdana, Geneva, sans-serif; 16 | margin-top: 5px; 17 | max-width: 350px; 18 | padding: 10px; 19 | color:black; 20 | } 21 | .uploadify-error { 22 | background-color: #FDE5DD !important; 23 | } 24 | .uploadify-queue-item .cancel a { 25 | background: url('../img/uploadify-cancel.png') 0 0 no-repeat; 26 | float: right; 27 | height: 16px; 28 | text-indent: -9999px; 29 | width: 16px; 30 | } 31 | .uploadify-queue-item.completed { 32 | background-color: #E5E5E5; 33 | } 34 | .uploadify-progress { 35 | background-color: #E5E5E5; 36 | margin-top: 10px; 37 | width: 100%; 38 | } 39 | .uploadify-progress-bar { 40 | background-color: #0099FF; 41 | height: 3px; 42 | width: 1px; 43 | } -------------------------------------------------------------------------------- /src/uploadify/uploadify.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cclleemm/FriendlyTorrent/3ad1adf5357b430a1fe89bfa1bf3a52dd10f8e45/src/uploadify/uploadify.swf -------------------------------------------------------------------------------- /src/views/Admin/editUser.php: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | Attention ! '.$error.' 7 |
'; 8 | } 9 | ?> 10 | 11 |
12 | 13 |


14 | 15 |
16 | 17 |
18 | 19 |
20 |
21 | 22 |
23 | 24 |
25 |
26 | 27 |
28 |
29 | 30 |
31 | 32 |
33 | 34 |
35 |
36 | 37 | 40 |
-------------------------------------------------------------------------------- /src/views/Admin/index.php: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 |
7 | 8 |
9 | 12 | 13 | '.LANG_USER_EDITED.' 14 |
'; 15 | } 16 | 17 | if($_GET['alert'] == 'adminUserFail'){ 18 | echo '
19 | 20 | '.LANG_YOU_ARE_THE_LAST_ADMIN.' 21 |
'; 22 | } 23 | 24 | if($_GET['alert'] == 'newUser'){ 25 | echo '
26 | 27 | '.LANG_USER_CREATED.' 28 |
'; 29 | } 30 | ?> 31 |
32 | 33 |


34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | listPaiement() as $key => $rst){ 53 | $paiements .= Tools::date_fr_texte($rst['time'])." : ".$rst['nbrJours']." jours [".$rst['price']." euros]"; 54 | }*/ 55 | 56 | try { 57 | $rpc = new Transmission($user->configRPC()); 58 | } catch (Exception $e) { 59 | $rpc = false; 60 | } 61 | 62 | 63 | ($value['admin'] == 1)? $admin = LANG_YES : $admin = LANG_NO; 64 | ($value['admin'] == 1)? $adminBtn = '' : $adminBtn = ''; 65 | 66 | echo ' 67 | 68 | 69 | 70 | 71 | 82 | '; 83 | } 84 | ?> 85 | 86 |
'.$value['login'].''.$value['mail'].''.$value['port'].''.$admin.' 72 | 73 | '.$adminBtn; 74 | if (!$rpc) 75 | echo ' '; 76 | else if(!$rpc->isRunning()) 77 | echo ' '; 78 | else 79 | echo ' '; 80 | echo ' 81 |
87 | 88 | -------------------------------------------------------------------------------- /src/views/Admin/newUser.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | Attention ! '.$error.' 9 | '; 10 | } 11 | ?> 12 | 13 |
14 | 15 |


16 | 17 |
18 | 19 |
20 | 21 |
22 |
23 | 24 |
25 | 26 |
27 |
28 | 29 |
30 |
31 | 32 |
33 | 34 |
35 | 36 |
37 |
38 | 39 | 42 |
43 | -------------------------------------------------------------------------------- /src/views/Compte/index.php: -------------------------------------------------------------------------------- 1 |
2 |

3 |
4 | 5 | 8 | 9 | Attention ! '.$error.' 10 | '; 11 | } 12 | ?> 13 |
14 | 15 |
16 | 17 |


18 | 19 |
20 | 21 |
22 | 23 |
24 |
25 | 26 |
27 | 28 |
29 |
30 |
31 | 32 |
33 |
34 | 35 |


36 | 37 |
38 | 39 |
40 | 41 |
42 |
43 | 44 |


45 | 46 |
47 | 48 |
49 | 50 |
51 |
52 |
53 | 54 |
55 |
56 | 59 |
60 |
61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/views/Connect/index.php: -------------------------------------------------------------------------------- 1 | 43 | 44 |
45 |
46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 |
54 | -------------------------------------------------------------------------------- /src/views/Downloads/index.php: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | 6 | 38 |
39 |
40 |
41 |
-------------------------------------------------------------------------------- /src/views/Rss/index.php: -------------------------------------------------------------------------------- 1 |
2 |

3 |
4 | 5 |
6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
Statut
21 |
22 | 25 |
26 |

...


27 |
28 | 31 | 32 | -------------------------------------------------------------------------------- /src/views/Torrents/index.php: -------------------------------------------------------------------------------- 1 | 4 | 5 |
6 | 7 |

8 |
9 | 10 |
11 | 12 |
13 |
14 |
15 | 16 |
17 | 18 |
19 | 20 |
21 |

...


22 |
23 | 24 | 25 | 32 |
33 | -------------------------------------------------------------------------------- /src/views/layout/header.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cclleemm/FriendlyTorrent/3ad1adf5357b430a1fe89bfa1bf3a52dd10f8e45/src/views/layout/header.php -------------------------------------------------------------------------------- /src/views/messagerie/chat.php: -------------------------------------------------------------------------------- 1 |
2 | 3 | 11 |

12 |
13 | 14 |

15 | 16 |

17 | 22 |
class="comment-item">
26 | 30 | Il n\'y a encore aucun message !'; 31 | } 32 | ?> -------------------------------------------------------------------------------- /src/views/messagerie/index.php: -------------------------------------------------------------------------------- 1 |
2 |

3 |
4 | 5 |
6 | 7 |
8 | 11 | 12 | '.LANG_MESSAGE_SENDED.' 13 |
'; 14 | } 15 | ?> 16 |
17 | 18 | 19 | 20 | $rslt){ 22 | echo ' 27 | '; 28 | } 29 | ?> 30 | 31 |
'; 26 | echo ''.LANG_CONVERSATION_WITH.' '.$rslt['login'].' ('.$rslt['nb'].')'.Tools::debutchaine($rslt['text'], 50).' '.Tools::date_fr_texte($rslt['time']).'
32 | 33 | -------------------------------------------------------------------------------- /src/views/messagerie/message.php: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | 10 |
11 |

12 |
13 | 14 |
15 | <> 16 |
17 |
18 | 19 | 20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------