commit
c18995e99a
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,2 +1,5 @@
|
||||
/public/include/config/global.inc.php
|
||||
/public/templates/compile/*.php
|
||||
/cronjobs/logs/*.txt
|
||||
/cronjobs/logs/*.txt.*.gz
|
||||
/public/templates/cache/*.php
|
||||
|
||||
@ -54,7 +54,7 @@ consider following this contribution guide!
|
||||
* [General GitHub documentation][2]
|
||||
* [GitHub pull request documentation][3]
|
||||
|
||||
[1]: https://github.com/TheSerapher/php-mmcfe-ng/issues "Issue"
|
||||
[1]: https://github.com/TheSerapher/php-mpos/issues "Issue"
|
||||
[2]: http://help.github.com/ "GitHub documentation"
|
||||
[3]: http://help.github.com/send-pull-requests/ "GitHub pull request documentation"
|
||||
[4]: https://github.com/signup/free "GitHub account"
|
||||
|
||||
122
POOLS.md
Normal file
122
POOLS.md
Normal file
@ -0,0 +1,122 @@
|
||||
Pools running MPOS
|
||||
==================
|
||||
|
||||
If you are not sure if you want to use `MPOS` on your own pool,
|
||||
maybe this list will push you over the edge of decision making. Some
|
||||
small and large pools are already running on it and have succesfully
|
||||
tested it on various coins.
|
||||
|
||||
These tables represent their users pools. Be aware that all values are
|
||||
as of this writing and may have changed since then.
|
||||
|
||||
|
||||
### vias79
|
||||
|
||||
Vias is using MPOS with a small group of people to mine various coins.
|
||||
They have succesfully mined blocks on each of those pools listed.
|
||||
All pools are running on Stratum only.
|
||||
|
||||
| Pool URL | Coin | Avg. Hashrate | Avg. Active Workers | Notes |
|
||||
| -------- | ---- | ------------: | -----------------: | ----- |
|
||||
| http://wdc.nordicminers.eu | Worldcoin | n/a | n/a | |
|
||||
| http://lky.nordicminers.eu | Luckycoin | n/a | n/a | |
|
||||
| http://fst.nordicminers.eu | Fastcoin | n/a | n/a | |
|
||||
| http://dgc.nordicminers.eu | Digitalcoin | n/a | n/a | |
|
||||
| http://ezc.nordicminers.eu | Ezcoin | n/a | n/a | |
|
||||
| http://sbc.nordicminers.eu | Stablecoin | n/a | n/a | |
|
||||
| http://mnc.nordicminers.eu | Mincoin | n/a | n/a | |
|
||||
| http://arg.nordicminers.eu | Argentum | n/a | n/a | |
|
||||
| http://mem.nordicminers.eu | Memecoin | n/a | n/a | |
|
||||
| http://frk.nordicminers.eu | Franko | n/a | n/a | |
|
||||
| http://pxc.nordicminers.eu | Phenixcoin | n/a | n/a | |
|
||||
|
||||
### WKNiGHT
|
||||
|
||||
WKNiGHT was an early adopter of `MPOS`. He has been around since a first stable release
|
||||
which only featured proportional payouts. He successfully moved to PPS since then and is
|
||||
running more or less without any issues (related to `MPOS` that is ;-)). He is also running
|
||||
the most powerful pool!
|
||||
|
||||
| Pool URL | Coin | Avg. Hashrate | Avg. Active Workers | Notes |
|
||||
| -------- | ---- | ------------: | ------------------: | ----- |
|
||||
| http://www.ejpool.info | Litecoin | 155 MHash | 120 | |
|
||||
|
||||
### Obigal
|
||||
|
||||
Small Time Miners are running various stratum only pools for different coins.
|
||||
|
||||
| Pool URL | Coin | Avg. Hashrate | Avg. Active Workers | Notes |
|
||||
| -------- | ---- | ------------: | ------------------- | ----- |
|
||||
| http://meg.smalltimeminer.com | Megacoin | 5 - 10 MHash | n/a | |
|
||||
| http://flo.smalltimeminer.com | Florincoin | 5 - 6 MHash | n/a | |
|
||||
| http://alf.smalltimeminer.com | Alphacoin | 2 - 4 MHash | n/a | |
|
||||
| http://cgb.smalltimeminer.com | Cryptobullion | 2 - 4 MHash | n/a | PoS/PoW type coin
|
||||
|
||||
### Feeleep75
|
||||
|
||||
| Pool URL | Coin | Avg. Hashrate | Avg. Active Workers | Notes |
|
||||
| -------- | ---- | ------------: | ------------------: | ----- |
|
||||
| http://bot.coinmine.pl | Bottlecaps | 3 - 50 MHash | n/a | PoS/PoW type coin |
|
||||
| http://yacp.coinmine.pl | YaCoin | 19 MHash | n/a | |
|
||||
|
||||
### LiteSaber
|
||||
|
||||
| Pool URL | Coin | Avg. Hashrate | Avg. Active Workers | Notes |
|
||||
| -------- | ---- | ------------- | ------------------: | ----- |
|
||||
| http://coinhuntr.com | Litecoin | 200 MHash | 250 | Custom Frontend template |
|
||||
|
||||
### Sheinsha
|
||||
|
||||
| Pool URL | Coin | Avg. Hashrate | Avg. Active Workers | Notes |
|
||||
| -------- | ---- | ------------: | ------------------: | ----- |
|
||||
| http://str.minar.cc | StarCoin | 35 MHash | 35 | PPLNS+VARDIFF, PoS/PoW type coin |
|
||||
| http://dmd.minar.cc | DiamondCoin | 90 MHash | 65 | PPLNS+VARDIFF, PoS/PoW type coin |
|
||||
| http://cgb.minar.cc | CryptogenicBullion | 18 MHash | 15 | PPLNS+VARDIFF, PoS/PoW type coin |
|
||||
| http://phs.minar.cc | Philosopherstone Coin | 170 MHash | 130 | PPLNS+VARDIFF, PoS/PoW type coin |
|
||||
|
||||
### IainKay & Nushor
|
||||
|
||||
| Pool URL | Coin | Avg. Hashrate | Avg. Active Workers | Notes |
|
||||
| -------- | ---- | ------------- | ------------------: | ----- |
|
||||
| http://ltc.nushor.net | Litecoin | 130 MHash | 300 | Succesfully migrated from `mmcfe` |
|
||||
|
||||
### nrpatten
|
||||
|
||||
| Pool URL | Coin | Avg. Hashrate | Avg. Active Workers | Notes |
|
||||
| -------- | ---- | ------------: | ------------------: | ----- |
|
||||
| http://www.litecoinfor.me | Litecoin | 0 | 0 | |
|
||||
| http://www.fastcoinfor.me | Fastcoin | 0.830 MHash | 2 | |
|
||||
|
||||
### ZC
|
||||
|
||||
| Pool URL | Coin | Avg. Hashrate | Avg. Active Workers | Notes |
|
||||
| -------- | ---- | ------------: | ------------------: | ----- |
|
||||
| https://ltc.hashfaster.com | LTC | 70 MHash | 80 | Custom Template |
|
||||
|
||||
### nutnut
|
||||
|
||||
|
||||
| Pool URL | Coin | Avg. Hashrate | Avg. Active Workers | Notes |
|
||||
| -------- | ---- | ------------: | ------------------: | ----- |
|
||||
| http://ftc.nut2pools.com | Feathercoin | 45-50Mhs | 25 workers | New style, PPLNS |
|
||||
| http://wdc.nut2pools.com | Worldcoin | 3.5 Mhs | 3 workers | New style, PPLNS |
|
||||
| http://pxc.nut2pools.com | Phenixcoin | 0 | 0 | New style | PPLNS |
|
||||
|
||||
### ahmedbodi
|
||||
|
||||
|
||||
| Pool URL | Coin | Avg. Hashrate | Avg. Active Workers | Notes |
|
||||
| -------- | ---- | ------------: | ------------------: | ----- |
|
||||
| http://gme.crypto-expert.com | Gamecoin | 1.5Mhs | 3 workers | Custom Template, Prop |
|
||||
| http://orb.crypto-expert.com | Orbitcoin | 0.2 Mhs | 1 workers | Custom Template, PPLNS |
|
||||
| http://src.crypto-expert.com | Securecoin | 25 | 20 | Custom Template, Prop |
|
||||
| http://dgc.crypto-expert.com | Digitalcoin | 0 | 0 | Custom Template, Prop |
|
||||
| http://arg.crypto-expert.com | Argentum | 0 | 0 | Custom Template, Prop|
|
||||
| http://crypto-expert.com/TIX | Tix | 0.5 | 1 | Custom Template, Prop|
|
||||
|
||||
### Neozonz
|
||||
|
||||
|
||||
| Pool URL | Coin | Avg. Hashrate | Avg. Active Workers | Notes |
|
||||
| -------- | ---- | ------------: | ------------------: | ----- |
|
||||
| https://www.mine-litecoin.com | Litecoin | 10Mhs | 75 workers | Custom Template, Prop |
|
||||
90
README.md
90
README.md
@ -1,16 +1,11 @@
|
||||
Description
|
||||
===========
|
||||
|
||||
mmcFE-ng is a web frontend for Pooled LTC Mining. A pool using this interface is running at http://pool.grewe.ca
|
||||
MPOS is a web based Mining Portal for various crypto currencies. A few pools using this interface are running at:
|
||||
|
||||
The web frontend layout is based on mmcFE, the original work by Greedi:
|
||||
https://github.com/Greedi/mmcFE
|
||||
|
||||
After working a few days trying to learn to run my own pool and the
|
||||
systems behind it I figured I'd look a bit deeper in the code to
|
||||
understand how it works. While doing so I also migrated the existing
|
||||
code over to my own framework so maintenance would be easier in the
|
||||
future.
|
||||
* http://ltc.pool.grewe.ca
|
||||
* http://fst.pool.grewe.ca
|
||||
* http://nvc.pool.grewe.ca
|
||||
|
||||
**NOTE**: This project is still under development and commits are happening on a daily basis.
|
||||
I do not recommend using this for a live setup as of yet. Wait for the later Release Candidate
|
||||
@ -21,7 +16,17 @@ Donations
|
||||
|
||||
I was hoping to keep this out of the README but apparently people remove or change the LTC address
|
||||
at the bottom of the page. For those of you finding my project and are willing to appreciate the work
|
||||
with some hard earned LTC feel free to donate to my LTC address: `Lge95QR2frp9y1wJufjUPCycVsg5gLJPW8`
|
||||
with some hard earned coins feel free to donate:
|
||||
|
||||
* LTC address: `Lge95QR2frp9y1wJufjUPCycVsg5gLJPW8`
|
||||
* FTC address: `6jDgGaUzMVyac5uqBhJCMiFMKCtH1LagTA`
|
||||
* NVC address: `4Guct6z7NVPVALHRAVn517TTmvqQve4WYr`
|
||||
* FST address: `g17CfFHqNqR5JnUjtG8RNBYh2WrhEirV67`
|
||||
|
||||
Website Footer
|
||||
==============
|
||||
|
||||
When you decide to use `MPOS` please be so kind and leave the footer intact. You are not the author of the software and should honor those that have worked on it. I don't mind changing the LTC donation address at the bottom, but keep in mind who really wrote this software and would deserve those ;-).
|
||||
|
||||
Donors
|
||||
======
|
||||
@ -31,6 +36,13 @@ These people have supported this project with a donation:
|
||||
* [obigal](https://github.com/obigal)
|
||||
* [vias](https://github.com/vias79)
|
||||
* [WKNiGHT](https://github.com/WKNiGHT-)
|
||||
* [ZC](https://github.com/zccopwrx)
|
||||
* Nutnut
|
||||
|
||||
Pools running MPOS
|
||||
==================
|
||||
|
||||
You can find a list of active pools [here](POOLS.md).
|
||||
|
||||
Requirements
|
||||
============
|
||||
@ -40,6 +52,11 @@ It should also work on any related distribution (RHEL, Debian).
|
||||
For support on how to get `litecoind` or `pushpoold` to work, please ask
|
||||
in the appropriate forums.
|
||||
|
||||
Be aware that `MPOS` is **only** for pooled mining. Solo Mining is not
|
||||
supported. They will never match an upstream share, solo miners do not create
|
||||
any shares, only blocks. Expect weird behavior if trying to mix them. See #299
|
||||
for full information.
|
||||
|
||||
* Apache2
|
||||
* libapache2-mod-php5
|
||||
* PHP 5.4+
|
||||
@ -57,33 +74,44 @@ Features
|
||||
|
||||
The following feature have been implemented so far:
|
||||
|
||||
* Fully re-written GUI with [Smarty][2] templates
|
||||
* Mobile WebUI
|
||||
* **NEW** VARDIFF Support
|
||||
* Reward Systems
|
||||
* Propotional
|
||||
* PPS
|
||||
* (Planned) PPLNS
|
||||
* Use of memcache for statistics instead of a cronjob
|
||||
* PPLNS
|
||||
* Statistics are cached in Memcache by Cronjob for quick data access
|
||||
* **NEW** New Theme
|
||||
* **NEW** Live Dashboard
|
||||
* **NEW** AJAX Support
|
||||
* **NEW** Overhauled API
|
||||
* Web User accounts
|
||||
* Re-Captcha protected registration form
|
||||
* Worker accounts
|
||||
* Worker activity (live, past 10 minutes)
|
||||
* Worker hashrates (live, past 10 minutes)
|
||||
* Worker activity
|
||||
* Worker hashrates
|
||||
* Pool statistics
|
||||
* Minimal Block statistics
|
||||
* Pool donations
|
||||
* Pool fees
|
||||
* Block Bonus Payouts
|
||||
* Manual payout
|
||||
* Auto payout
|
||||
* Transaction list (confirmed and unconfirmed)
|
||||
* Transaction list
|
||||
* Admin Panel
|
||||
* Cron Monitoring Overview
|
||||
* User Listing including statistics
|
||||
* Wallet information
|
||||
* (Planned) News Posts
|
||||
* (Planned) Pool Donations
|
||||
* User Transactions
|
||||
* News Posts
|
||||
* Pool Settings
|
||||
* Notification system
|
||||
* IDLE Workers
|
||||
* New blocks found in pool
|
||||
* Auto Payout
|
||||
* Manual Payout
|
||||
* User-to-user Invitation System
|
||||
* Support for various Scrypt based coins via config
|
||||
* MNC
|
||||
* LTC
|
||||
@ -92,9 +120,28 @@ The following feature have been implemented so far:
|
||||
Installation
|
||||
============
|
||||
|
||||
Please take a look at the [Quick Start Guide](https://github.com/TheSerapher/php-mmcfe-ng/wiki/Quick-Start-Guide). This will give you
|
||||
an idea how to setup `mmcfe-ng`.
|
||||
Please take a look at the [Quick Start Guide](https://github.com/TheSerapher/php-mpos/wiki/Quick-Start-Guide). This will give you
|
||||
an idea how to setup `MPOS`.
|
||||
|
||||
Customization
|
||||
=============
|
||||
|
||||
This project was meant to allow users to easily cusomize the system and templates. Hence no upstream framework was used to keep it as simple as possible.
|
||||
If you are just using the system, there will be no need to adjust anything. Things will work out of the box! But if you plan on creating
|
||||
your own theme, things are pretty easy:
|
||||
|
||||
* Create a new theme folder in `public/templates/`
|
||||
* Create a new site_assets folder in `public/site_assets`
|
||||
* Create your own complete custom template or copy from an existing one
|
||||
* Change your theme in the `Admin Panel` and point it to the newly created folder
|
||||
|
||||
The good thing with this approach: You can keep the backend code updated! Since your new theme will never conflict with existing themes, a simple git pull will
|
||||
keep your installation updated. You decide which new feature you'd like to integrate on your own theme. Bugfixes to the code will work out of the box!
|
||||
|
||||
Other customizations are also possible but will require merging changes together. Usually users would not need to change the backend code unless they wish to work
|
||||
on non-existing features in `MPOS`. For the vast majority, adjusting themes should be enough to highlight your pool from others.
|
||||
|
||||
In all that, I humbly ask to keep the `MPOS` author reference and Github URL intact.
|
||||
Contributing
|
||||
============
|
||||
|
||||
@ -107,7 +154,7 @@ You can contribute to this project in different ways:
|
||||
Contact
|
||||
=======
|
||||
|
||||
You can find me on Freenode.net, #mmcfe-ng.
|
||||
You can find me on Freenode.net, #MPOS.
|
||||
|
||||
License and Author
|
||||
==================
|
||||
@ -127,4 +174,5 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
|
||||
[1]: https://github.com/TheSerapher/php-mmcfe-ng/issues "Issue"
|
||||
[1]: https://github.com/TheSerapher/php-mpos/issues "Issue"
|
||||
[2]: http://www.smarty.net/docs/en/ "Smarty"
|
||||
|
||||
39
cronjobs/archive_cleanup.php
Executable file
39
cronjobs/archive_cleanup.php
Executable file
@ -0,0 +1,39 @@
|
||||
#!/usr/bin/php
|
||||
<?php
|
||||
|
||||
/*
|
||||
|
||||
Copyright:: 2013, Sebastian Grewe
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
// Change to working directory
|
||||
chdir(dirname(__FILE__));
|
||||
|
||||
// Include all settings and classes
|
||||
require_once('shared.inc.php');
|
||||
|
||||
// If we don't keep archives, delete some now to release disk space
|
||||
if (!$share->purgeArchive()) {
|
||||
$log->logError("Failed to delete archived shares, not critical but should be checked!");
|
||||
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
|
||||
$monitoring->setStatus($cron_name . "_message", "message", "Failed to delete archived shares");
|
||||
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Cron cleanup and monitoring
|
||||
require_once('cron_end.inc.php');
|
||||
?>
|
||||
@ -19,71 +19,86 @@ limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
// Change to working directory
|
||||
chdir(dirname(__FILE__));
|
||||
|
||||
// Include all settings and classes
|
||||
require_once('shared.inc.php');
|
||||
|
||||
if ($bitcoin->can_connect() !== true) {
|
||||
verbose("Unable to connect to RPC server, exiting");
|
||||
exit(1);
|
||||
if ($setting->getValue('disable_ap') == 1) {
|
||||
$log->logInfo(" auto payout disabled via admin panel");
|
||||
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
|
||||
$monitoring->setStatus($cron_name . "_message", "message", "Auto-Payout disabled");
|
||||
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
|
||||
$monitoring->setStatus($cron_name . "_endtime", "date", time());
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// Mark this job as active
|
||||
$setting->setValue('auto_payout_active', 1);
|
||||
if ($bitcoin->can_connect() !== true) {
|
||||
$log->logFatal(" unable to connect to RPC server, exiting");
|
||||
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
|
||||
$monitoring->setStatus($cron_name . "_message", "message", "Unable to connect to RPC server");
|
||||
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Fetch all users with setup AP
|
||||
$users = $user->getAllAutoPayout();
|
||||
|
||||
// Quick summary
|
||||
if (count($users) > 0) $log->logInfo(" found " . count($users) . " queued payout(s)");
|
||||
|
||||
// Go through users and run transactions
|
||||
if (! empty($users)) {
|
||||
verbose("UserID\tUsername\tBalance\tThreshold\tAddress\t\t\t\t\tStatus\n\n");
|
||||
$log->logInfo("\tUserID\tUsername\tBalance\tThreshold\tAddress");
|
||||
|
||||
foreach ($users as $aUserData) {
|
||||
$aBalance = $transaction->getBalance($aUserData['id']);
|
||||
$dBalance = $aBalance['confirmed'];
|
||||
verbose($aUserData['id'] . "\t" . $aUserData['username'] . "\t" . $dBalance . "\t" . $aUserData['ap_threshold'] . "\t\t" . $aUserData['coin_address'] . "\t");
|
||||
$log->logInfo("\t" . $aUserData['id'] . "\t" . $aUserData['username'] . "\t" . $dBalance . "\t" . $aUserData['ap_threshold'] . "\t\t" . $aUserData['coin_address']);
|
||||
|
||||
// Only run if balance meets threshold and can pay the potential transaction fee
|
||||
if ($dBalance > $aUserData['ap_threshold'] && $dBalance > $config['txfee']) {
|
||||
// Validate address against RPC
|
||||
try {
|
||||
$bitcoin->validateaddress($aUserData['coin_address']);
|
||||
$aStatus = $bitcoin->validateaddress($aUserData['coin_address']);
|
||||
if (!$aStatus['isvalid']) {
|
||||
$log->logError('Failed to verify this users coin address, skipping payout');
|
||||
continue;
|
||||
}
|
||||
} catch (BitcoinClientException $e) {
|
||||
verbose("VERIFY FAILED\n");
|
||||
$log->logError('Failed to verifu this users coin address, skipping payout');
|
||||
continue;
|
||||
}
|
||||
|
||||
// Send balance, fees are reduced later by RPC Server
|
||||
try {
|
||||
$bitcoin->sendtoaddress($aUserData['coin_address'], $dBalance);
|
||||
$bitcoin->sendtoaddress($aUserData['coin_address'], $dBalance - $config['txfee']);
|
||||
} catch (BitcoinClientException $e) {
|
||||
verbose("SEND FAILED\n");
|
||||
$log->logError('Failed to send requested balance to coin address, please check payout process');
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create transaction record
|
||||
if ($transaction->addTransaction($aUserData['id'], $dBalance - $config['txfee'], 'Debit_AP', NULL, $aUserData['coin_address']) && $transaction->addTransaction($aUserData['id'], $config['txfee'], 'TXFee', NULL, $aUserData['coin_address'])) {
|
||||
// Mark all older transactions as archived
|
||||
if (!$transaction->setArchived($aUserData['id'], $transaction->insert_id))
|
||||
$log->logError('Failed to mark transactions for user #' . $aUserData['id'] . ' prior to #' . $transaction->insert_id . ' as archived');
|
||||
// Notify user via mail
|
||||
$aMailData['email'] = $user->getUserEmail($user->getUserName($aUserData['id']));
|
||||
$aMailData['subject'] = 'Auto Payout Completed';
|
||||
$aMailData['amount'] = $dBalance;
|
||||
if (!$notification->sendNotification($aUserData['id'], 'auto_payout', $aMailData)) {
|
||||
verbose("NOTIFY FAILED\n");
|
||||
} else {
|
||||
verbose("OK\n");
|
||||
}
|
||||
if (!$notification->sendNotification($aUserData['id'], 'auto_payout', $aMailData))
|
||||
$log->logError('Failed to send notification email to users address: ' . $aMailData['email']);
|
||||
} else {
|
||||
verbose("FAILED\n");
|
||||
$log->logError('Failed to add new Debit_AP transaction in database for user ' . $user->getUserName($aUserData['id']));
|
||||
}
|
||||
|
||||
} else {
|
||||
verbose("SKIPPED\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
verbose("No user has configured their AP > 0\n");
|
||||
$log->logDebug(" no user has configured their AP > 0");
|
||||
}
|
||||
|
||||
// Mark this job as inactive
|
||||
$setting->setValue('auto_payout_active', 0);
|
||||
|
||||
// Cron cleanup and monitoring
|
||||
require_once('cron_end.inc.php');
|
||||
?>
|
||||
|
||||
@ -19,37 +19,44 @@ limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
// Change to working directory
|
||||
chdir(dirname(__FILE__));
|
||||
|
||||
// Include all settings and classes
|
||||
require_once('shared.inc.php');
|
||||
|
||||
if ( $bitcoin->can_connect() !== true ) {
|
||||
verbose("Failed to connect to RPC server\n");
|
||||
$log->logFatal("Failed to connect to RPC server\n");
|
||||
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
|
||||
$monitoring->setStatus($cron_name . "_message", "message", "Unable to connect to RPC server");
|
||||
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Fetch all unconfirmed blocks
|
||||
$aAllBlocks = $block->getAllUnconfirmed($config['confirmations']);
|
||||
|
||||
verbose("ID\tBlockhash\tConfirmations\t\n");
|
||||
$log->logInfo("ID\tHeight\tBlockhash\tConfirmations");
|
||||
foreach ($aAllBlocks as $iIndex => $aBlock) {
|
||||
$aBlockInfo = $bitcoin->query('getblock', $aBlock['blockhash']);
|
||||
// Fetch this blocks transaction details to find orphan blocks
|
||||
$aTxDetails = $bitcoin->query('gettransaction', $aBlockInfo['tx'][0]);
|
||||
verbose($aBlock['id'] . "\t" . $aBlock['blockhash'] . "\t" . $aBlock['confirmations'] . " -> " . $aBlockInfo['confirmations'] . "\t");
|
||||
$log->logInfo($aBlock['id'] . "\t" . $aBlock['height'] . "\t" . $aBlock['blockhash'] . "\t" . $aBlock['confirmations'] . " -> " . $aBlockInfo['confirmations']);
|
||||
if ($aTxDetails['details'][0]['category'] == 'orphan') {
|
||||
// We have an orphaned block, we need to invalidate all transactions for this one
|
||||
if ($transaction->setOrphan($aBlock['id']) && $block->setConfirmations($aBlock['id'], -1)) {
|
||||
verbose("ORPHAN\n");
|
||||
if ($block->setConfirmations($aBlock['id'], -1)) {
|
||||
$log->logInfo(" Block marked as orphan");
|
||||
} else {
|
||||
verbose("ORPHAN_ERR");
|
||||
$log->logError(" Block became orphaned but unable to update database entries");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if ($aBlock['confirmations'] == $aBlockInfo['confirmations']) {
|
||||
verbose("SKIPPED\n");
|
||||
} else if ($block->setConfirmations($aBlock['id'], $aBlockInfo['confirmations'])) {
|
||||
verbose("UPDATED\n");
|
||||
} else {
|
||||
verbose("ERROR\n");
|
||||
$log->logDebug(' No update needed');
|
||||
} else if (!$block->setConfirmations($aBlock['id'], $aBlockInfo['confirmations'])) {
|
||||
$log->logError(' Failed to update block confirmations');
|
||||
}
|
||||
}
|
||||
|
||||
require_once('cron_end.inc.php');
|
||||
?>
|
||||
|
||||
28
cronjobs/cron_end.inc.php
Normal file
28
cronjobs/cron_end.inc.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
|
||||
Copyright:: 2013, Sebastian Grewe
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
// Monitoring cleanup and status update
|
||||
$monitoring->setStatus($cron_name . "_message", "message", "OK");
|
||||
$monitoring->setStatus($cron_name . "_status", "okerror", 0);
|
||||
$monitoring->setStatus($cron_name . "_runtime", "time", microtime(true) - $cron_start[$cron_name]);
|
||||
$monitoring->setStatus($cron_name . "_endtime", "date", time());
|
||||
// Mark cron as running for monitoring
|
||||
$monitoring->setStatus($cron_name . '_active', "yesno", 0);
|
||||
?>
|
||||
6
cronjobs/etc/logrotate.conf
Normal file
6
cronjobs/etc/logrotate.conf
Normal file
@ -0,0 +1,6 @@
|
||||
"./logs/*.txt" {
|
||||
copytruncate
|
||||
rotate 7
|
||||
compress
|
||||
daily
|
||||
}
|
||||
@ -19,114 +19,126 @@ limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
// Change to working directory
|
||||
chdir(dirname(__FILE__));
|
||||
|
||||
// Include all settings and classes
|
||||
require_once('shared.inc.php');
|
||||
|
||||
// Fetch our last block found from the DB as a starting point
|
||||
$aLastBlock = @$block->getLast();
|
||||
$strLastBlockHash = $aLastBlock['blockhash'];
|
||||
if (!$strLastBlockHash) {
|
||||
$strLastBlockHash = '';
|
||||
}
|
||||
if (!$strLastBlockHash) $strLastBlockHash = '';
|
||||
|
||||
// Fetch all transactions since our last block
|
||||
if ( $bitcoin->can_connect() === true ){
|
||||
$aTransactions = $bitcoin->query('listsinceblock', $strLastBlockHash);
|
||||
} else {
|
||||
verbose("Aborted: " . $bitcoin->can_connect() . "\n");
|
||||
$log->logFatal('Unable to conenct to RPC server backend');
|
||||
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
|
||||
$monitoring->setStatus($cron_name . "_message", "message", "Unable to connect to RPC server");
|
||||
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Nothing to do so bail out
|
||||
if (empty($aTransactions['transactions'])) {
|
||||
verbose("No new RPC transactions since last block\n");
|
||||
$log->logDebug('No new RPC transactions since last block');
|
||||
} else {
|
||||
// Table header
|
||||
verbose("Blockhash\t\tHeight\tAmount\tConfirmations\tDiff\t\tTime\t\t\tStatus\n");
|
||||
$log->logInfo("Blockhash\t\tHeight\tAmount\tConfirmations\tDiff\t\tTime");
|
||||
|
||||
// Let us add those blocks as unaccounted
|
||||
foreach ($aTransactions['transactions'] as $iIndex => $aData) {
|
||||
if ( $aData['category'] == 'generate' || $aData['category'] == 'immature' ) {
|
||||
$aBlockInfo = $bitcoin->query('getblock', $aData['blockhash']);
|
||||
$config['reward_type'] == 'block' ? $aData['amount'] = $aData['amount'] : $aData['amount'] = $config['reward'];
|
||||
$aData['height'] = $aBlockInfo['height'];
|
||||
$aData['difficulty'] = $aBlockInfo['difficulty'];
|
||||
verbose(substr($aData['blockhash'], 0, 15) . "...\t" .
|
||||
$log->logInfo(substr($aData['blockhash'], 0, 15) . "...\t" .
|
||||
$aData['height'] . "\t" .
|
||||
$aData['amount'] . "\t" .
|
||||
$aData['confirmations'] . "\t\t" .
|
||||
$aData['difficulty'] . "\t" .
|
||||
strftime("%Y-%m-%d %H:%M:%S", $aData['time']) . "\t");
|
||||
if ( $block->addBlock($aData) ) {
|
||||
verbose("Added\n");
|
||||
} else {
|
||||
verbose("Failed" . "\n");
|
||||
strftime("%Y-%m-%d %H:%M:%S", $aData['time']));
|
||||
if ( ! empty($aBlockInfo['flags']) && preg_match('/proof-of-stake/', $aBlockInfo['flags']) ) {
|
||||
$log->logInfo("Block above with height " . $aData['height'] . " not added to database, proof-of-stake block!");
|
||||
continue;
|
||||
}
|
||||
if (!$block->addBlock($aData) ) {
|
||||
$log->logFatal('Unable to add this block to database: ' . $aData['height']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
verbose("\n");
|
||||
// Now with our blocks added we can scan for their upstream shares
|
||||
$aAllBlocks = $block->getAllUnaccounted('ASC');
|
||||
$aAllBlocks = $block->getAllUnsetShareId('ASC');
|
||||
if (empty($aAllBlocks)) {
|
||||
verbose("No new unaccounted blocks found\n");
|
||||
$log->logDebug('No new blocks without share_id found in database');
|
||||
} else {
|
||||
// Loop through our unaccounted blocks
|
||||
verbose("\nBlock ID\tBlock Height\tShare ID\tShares\tFinder\t\t\tStatus\n");
|
||||
$log->logInfo("Block ID\t\tHeight\tAmount\tShare ID\tShares\tFinder\tType");
|
||||
foreach ($aAllBlocks as $iIndex => $aBlock) {
|
||||
if (empty($aBlock['share_id'])) {
|
||||
// Fetch this blocks upstream ID
|
||||
if ($share->setUpstream($block->getLastUpstreamId())) {
|
||||
$aBlockInfo = $bitcoin->query('getblock', $aBlock['blockhash']);
|
||||
if ($share->setUpstream($aBlockInfo, $block->getLastUpstreamId())) {
|
||||
$iCurrentUpstreamId = $share->getUpstreamId();
|
||||
$iAccountId = $user->getUserId($share->getUpstreamFinder());
|
||||
} else {
|
||||
verbose("\nUnable to fetch blocks upstream share. Aborting!\n");
|
||||
verbose($share->getError() . "\n");
|
||||
$log->logFatal('Unable to fetch blocks upstream share, aborted:' . $share->getError());
|
||||
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
|
||||
$monitoring->setStatus($cron_name . "_message", "message", "Unable to fetch blocks " . $aBlock['height'] . " upstream share: " . $share->getError());
|
||||
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Fetch share information
|
||||
if (!$iPreviousShareId = $block->getLastShareId()) {
|
||||
$iPreviousShareId = 0;
|
||||
verbose("\nUnable to find highest share ID found so far\n");
|
||||
verbose("If this is your first block, this is normal\n\n");
|
||||
$log->logInfo('Unable to find highest share ID found so far, if this is your first block, this is normal.');
|
||||
}
|
||||
$iRoundShares = $share->getRoundShares($iPreviousShareId, $iCurrentUpstreamId);
|
||||
|
||||
// Store new information
|
||||
$strStatus = "OK";
|
||||
if (!$block->setShareId($aBlock['id'], $iCurrentUpstreamId))
|
||||
$strStatus = "Share ID Failed";
|
||||
$log->logError('Failed to update share ID in database for block ' . $aBlock['height']);
|
||||
if (!$block->setFinder($aBlock['id'], $iAccountId))
|
||||
$strStatus = "Finder Failed";
|
||||
$log->logError('Failed to update finder account ID in database for block ' . $aBlock['height']);
|
||||
if (!$block->setShares($aBlock['id'], $iRoundShares))
|
||||
$strStatus = "Shares Failed";
|
||||
$log->logError('Failed to update share count in database for block ' . $aBlock['height']);
|
||||
if ($config['block_bonus'] > 0 && !$transaction->addTransaction($iAccountId, $config['block_bonus'], 'Bonus', $aBlock['id'])) {
|
||||
$strStatus = "Bonus Failed";
|
||||
$log->logError('Failed to create Bonus transaction in database for user ' . $user->getUserName($iAccountId) . ' for block ' . $aBlock['height']);
|
||||
}
|
||||
|
||||
verbose(
|
||||
$log->logInfo(
|
||||
$aBlock['id'] . "\t\t"
|
||||
. $aBlock['height'] . "\t\t"
|
||||
. $aBlock['amount'] . "\t"
|
||||
. $iCurrentUpstreamId . "\t\t"
|
||||
. $iRoundShares . "\t"
|
||||
. "[$iAccountId] " . $user->getUserName($iAccountId) . "\t\t"
|
||||
. $strStatus
|
||||
. "\n"
|
||||
. "[$iAccountId] " . $user->getUserName($iAccountId) . "\t"
|
||||
. $share->share_type
|
||||
);
|
||||
|
||||
// Notify users
|
||||
$aAccounts = $notification->getNotificationAccountIdByType('new_block');
|
||||
if (is_array($aAccounts)) {
|
||||
foreach ($aAccounts as $aData) {
|
||||
$aMailData['height'] = $aBlock['height'];
|
||||
$aMailData['subject'] = 'New Block';
|
||||
$aMailData['email'] = $user->getUserEmail($user->getUserName($aData['account_id']));
|
||||
$aMailData['shares'] = $iRoundShares;
|
||||
$notification->sendNotification($aData['account_id'], 'new_block', $aMailData);
|
||||
if ($setting->getValue('disable_notifications') != 1) {
|
||||
// Notify users
|
||||
$aAccounts = $notification->getNotificationAccountIdByType('new_block');
|
||||
if (is_array($aAccounts)) {
|
||||
foreach ($aAccounts as $aData) {
|
||||
$aMailData['height'] = $aBlock['height'];
|
||||
$aMailData['subject'] = 'New Block';
|
||||
$aMailData['email'] = $user->getUserEmail($user->getUserName($aData['account_id']));
|
||||
$aMailData['shares'] = $iRoundShares;
|
||||
if (!$notification->sendNotification($aData['account_id'], 'new_block', $aMailData))
|
||||
$log->logError('Failed to notify user of new found block: ' . $user->getUserName($aData['account_id']));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
require_once('cron_end.inc.php');
|
||||
?>
|
||||
|
||||
11
cronjobs/logrotate.sh
Executable file
11
cronjobs/logrotate.sh
Executable file
@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Find scripts path
|
||||
if [[ -L $0 ]]; then
|
||||
CRONHOME=$( dirname $( readlink $0 ) )
|
||||
else
|
||||
CRONHOME=$( dirname $0 )
|
||||
fi
|
||||
|
||||
cd $CRONHOME
|
||||
logrotate etc/logrotate.conf
|
||||
1
cronjobs/logs/README.md
Normal file
1
cronjobs/logs/README.md
Normal file
@ -0,0 +1 @@
|
||||
Logging directory for cronjobs.
|
||||
102
cronjobs/manual_payout.php
Executable file
102
cronjobs/manual_payout.php
Executable file
@ -0,0 +1,102 @@
|
||||
#!/usr/bin/php
|
||||
<?php
|
||||
|
||||
/*
|
||||
|
||||
Copyright:: 2013, Sebastian Grewe
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
// Change to working directory
|
||||
chdir(dirname(__FILE__));
|
||||
|
||||
// Include all settings and classes
|
||||
require_once('shared.inc.php');
|
||||
|
||||
if ($setting->getValue('disable_mp') == 1) {
|
||||
$log->logInfo(" auto payout disabled via admin panel");
|
||||
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
|
||||
$monitoring->setStatus($cron_name . "_message", "message", "Auto-Payout disabled");
|
||||
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
|
||||
$monitoring->setStatus($cron_name . "_endtime", "date", time());
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if ($bitcoin->can_connect() !== true) {
|
||||
$log->logFatal(" unable to connect to RPC server, exiting");
|
||||
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
|
||||
$monitoring->setStatus($cron_name . "_message", "message", "Unable to connect to RPC server");
|
||||
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Fetch outstanding payout requests
|
||||
$aPayouts = $oPayout->getUnprocessedPayouts();
|
||||
|
||||
if (count($aPayouts) > 0) {
|
||||
$log->logInfo("\tAccount ID\tUsername\tBalance\t\tCoin Address");
|
||||
foreach ($aPayouts as $aData) {
|
||||
$aBalance = $transaction->getBalance($aData['account_id']);
|
||||
$dBalance = $aBalance['confirmed'];
|
||||
$aData['coin_address'] = $user->getCoinAddress($aData['account_id']);
|
||||
$aData['username'] = $user->getUserName($aData['account_id']);
|
||||
if ($dBalance > $config['txfee']) {
|
||||
$log->logInfo("\t" . $aData['account_id'] . "\t\t" . $aData['username'] . "\t" . $dBalance . "\t\t" . $aData['coin_address']);
|
||||
try {
|
||||
$aStatus = $bitcoin->validateaddress($aData['coin_address']);
|
||||
if (!$aStatus['isvalid']) {
|
||||
$log->logError('Failed to verify this users coin address, skipping payout');
|
||||
continue;
|
||||
}
|
||||
} catch (BitcoinClientException $e) {
|
||||
$log->logError('Failed to verify this users coin address, skipping payout');
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
$bitcoin->sendtoaddress($aData['coin_address'], $dBalance - $config['txfee']);
|
||||
} catch (BitcoinClientException $e) {
|
||||
$log->logError('Failed to send requested balance to coin address, please check payout process');
|
||||
continue;
|
||||
}
|
||||
// To ensure we don't run this transaction again, lets mark it completed
|
||||
if (!$oPayout->setProcessed($aData['id'])) {
|
||||
$log->logFatal('unable to mark transactions ' . $aData['id'] . ' as processed.');
|
||||
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
|
||||
$monitoring->setStatus($cron_name . "_message", "message", "Unable set payout as processed");
|
||||
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ($transaction->addTransaction($aData['account_id'], $dBalance - $config['txfee'], 'Debit_MP', NULL, $aData['coin_address']) && $transaction->addTransaction($aData['account_id'], $config['txfee'], 'TXFee', NULL, $aData['coin_address'])) {
|
||||
// Mark all older transactions as archived
|
||||
if (!$transaction->setArchived($aData['account_id'], $transaction->insert_id))
|
||||
$log->logError('Failed to mark transactions for #' . $aData['account_id'] . ' prior to #' . $transaction->insert_id . ' as archived');
|
||||
// Notify user via mail
|
||||
$aMailData['email'] = $user->getUserEmail($user->getUserName($aData['account_id']));
|
||||
$aMailData['subject'] = 'Manual Payout Completed';
|
||||
$aMailData['amount'] = $dBalance;
|
||||
$aMailData['payout_id'] = $aData['id'];
|
||||
if (!$notification->sendNotification($aData['account_id'], 'manual_payout', $aMailData))
|
||||
$log->logError('Failed to send notification email to users address: ' . $aMailData['email']);
|
||||
} else {
|
||||
$log->logError('Failed to add new Debit_MP transaction in database for user ' . $user->getUserName($aData['account_id']));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
require_once('cron_end.inc.php');
|
||||
?>
|
||||
@ -19,38 +19,61 @@ limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
// Change to working directory
|
||||
chdir(dirname(__FILE__));
|
||||
|
||||
// Include all settings and classes
|
||||
require_once('shared.inc.php');
|
||||
|
||||
if ($setting->getValue('disable_notifications') == 1) {
|
||||
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
|
||||
$monitoring->setStatus($cron_name . "_message", "message", "Cron disabled by admin");
|
||||
$monitoring->setStatus($cron_name . "_status", "okerror", 0);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
$log->logDebug(" IDLE Worker Notifications ...");
|
||||
// Find all IDLE workers
|
||||
$aWorkers = $worker->getAllIdleWorkers();
|
||||
if (empty($aWorkers)) {
|
||||
verbose("No idle workers found\n");
|
||||
$log->logDebug(" no idle workers found\n");
|
||||
} else {
|
||||
$log->logInfo(" found " . count($aWorkers) . " IDLE workers\n");
|
||||
foreach ($aWorkers as $aWorker) {
|
||||
$aData = $aWorker;
|
||||
$aData['username'] = $user->getUserName($aWorker['account_id']);
|
||||
$aData['subject'] = 'IDLE Worker : ' . $aWorker['username'];
|
||||
$aData['worker'] = $aWorker['username'];
|
||||
$aData['email'] = $user->getUserEmail($aData['username']);
|
||||
$log->logInfo(" " . $aWorker['username'] . "...");
|
||||
if (!$notification->sendNotification($aWorker['account_id'], 'idle_worker', $aData))
|
||||
verbose($notification->getError() . "\n");
|
||||
$log->logError(" Failed sending notifications: " . $notification->getError() . "\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$log->logDebug(" Reset IDLE Worker Notifications ...");
|
||||
// We notified, lets check which recovered
|
||||
$aNotifications = $notification->getAllActive('idle_worker');
|
||||
if (!empty($aNotifications)) {
|
||||
$log->logInfo(" found " . count($aNotifications) . " active notification(s)\n");
|
||||
foreach ($aNotifications as $aNotification) {
|
||||
$aData = json_decode($aNotification['data'], true);
|
||||
$aWorker = $worker->getWorker($aData['id']);
|
||||
if ($aWorker['active'] == 1) {
|
||||
$log->logInfo(" " . $aWorker['username'] . " ...");
|
||||
if ($aWorker['hashrate'] > 0) {
|
||||
if ($notification->setInactive($aNotification['id'])) {
|
||||
verbose("Marked notification " . $aNotification['id'] . " as inactive\n");
|
||||
$log->logInfo(" updated #" . $aNotification['id'] . " for " . $aWorker['username'] . " as inactive\n");
|
||||
} else {
|
||||
verbose("Failed to set notification inactive for " . $aWorker['username'] . "\n");
|
||||
$log->logInfo(" failed to update #" . $aNotification['id'] . " for " . $aWorker['username'] . "\n");
|
||||
}
|
||||
} else {
|
||||
$log->logInfo(" still inactive\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$log->logDebug(" no active IDLE worker notifications\n");
|
||||
}
|
||||
|
||||
require_once('cron_end.inc.php');
|
||||
?>
|
||||
|
||||
198
cronjobs/pplns_payout.php
Executable file
198
cronjobs/pplns_payout.php
Executable file
@ -0,0 +1,198 @@
|
||||
#!/usr/bin/php
|
||||
<?php
|
||||
|
||||
/*
|
||||
|
||||
Copyright:: 2013, Sebastian Grewe
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
// Change to working directory
|
||||
chdir(dirname(__FILE__));
|
||||
|
||||
// Include all settings and classes
|
||||
require_once('shared.inc.php');
|
||||
|
||||
// Check if we are set as the payout system
|
||||
if ($config['payout_system'] != 'pplns') {
|
||||
$log->logInfo("Please activate this cron in configuration via payout_system = pplns");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// Fetch all unaccounted blocks
|
||||
$aAllBlocks = $block->getAllUnaccounted('ASC');
|
||||
if (empty($aAllBlocks)) {
|
||||
$log->logDebug("No new unaccounted blocks found");
|
||||
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
|
||||
$monitoring->setStatus($cron_name . "_message", "message", "No new unaccounted blocks");
|
||||
$monitoring->setStatus($cron_name . "_status", "okerror", 0);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
foreach ($aAllBlocks as $iIndex => $aBlock) {
|
||||
// We support some dynamic share targets but fall back to our fixed value
|
||||
// Re-calculate after each run due to re-targets in this loop
|
||||
if ($config['pplns']['shares']['type'] == 'blockavg' && $block->getBlockCount() > 0) {
|
||||
$pplns_target = round($block->getAvgBlockShares($aBlock['height'], $config['pplns']['blockavg']['blockcount']));
|
||||
} else {
|
||||
$pplns_target = $config['pplns']['shares']['default'];
|
||||
}
|
||||
|
||||
if (!$aBlock['accounted']) {
|
||||
$iPreviousShareId = @$aAllBlocks[$iIndex - 1]['share_id'] ? $aAllBlocks[$iIndex - 1]['share_id'] : 0;
|
||||
$iCurrentUpstreamId = $aBlock['share_id'];
|
||||
if (!is_numeric($iCurrentUpstreamId)) {
|
||||
$log->logFatal("Block " . $aBlock['height'] . " has no share_id associated with it, not going to continue");
|
||||
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
|
||||
$monitoring->setStatus($cron_name . "_message", "message", "Block " . $aBlock['height'] . " has no share_id associated with it");
|
||||
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
|
||||
exit(1);
|
||||
}
|
||||
$iRoundShares = $share->getRoundShares($iPreviousShareId, $aBlock['share_id']);
|
||||
$iNewRoundShares = 0;
|
||||
$config['reward_type'] == 'block' ? $dReward = $aBlock['amount'] : $dReward = $config['reward'];
|
||||
$aRoundAccountShares = $share->getSharesForAccounts($iPreviousShareId, $aBlock['share_id']);
|
||||
|
||||
if ($iRoundShares >= $pplns_target) {
|
||||
$log->logDebug("Matching or exceeding PPLNS target of $pplns_target with $iRoundShares");
|
||||
$iMinimumShareId = $share->getMinimumShareId($pplns_target, $aBlock['share_id']);
|
||||
// We need to go one ID lower due to `id >` or we won't match if minimum share ID == $aBlock['share_id']
|
||||
$aAccountShares = $share->getSharesForAccounts($iMinimumShareId - 1, $aBlock['share_id']);
|
||||
if (empty($aAccountShares)) {
|
||||
$log->logFatal("No shares found for this block, aborted! Block Height : " . $aBlock['height']);
|
||||
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
|
||||
$monitoring->setStatus($cron_name . "_message", "message", "No shares found for this block: " . $aBlock['height']);
|
||||
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
|
||||
exit(1);
|
||||
}
|
||||
foreach($aAccountShares as $key => $aData) {
|
||||
$iNewRoundShares += $aData['valid'];
|
||||
}
|
||||
$log->logInfo('Adjusting round target to PPLNS target ' . $iNewRoundShares);
|
||||
$iRoundShares = $iNewRoundShares;
|
||||
} else {
|
||||
$log->logDebug("Not able to match PPLNS target of $pplns_target with $iRoundShares");
|
||||
// We need to fill up with archived shares
|
||||
// Grab the full current round shares since we didn't match target
|
||||
$aAccountShares = $aRoundAccountShares;
|
||||
if (empty($aAccountShares)) {
|
||||
$log->logFatal("No shares found for this block, aborted! Block height: " . $aBlock['height']);
|
||||
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
|
||||
$monitoring->setStatus($cron_name . "_message", "message", "No shares found for this block: " . $aBlock['height']);
|
||||
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Grab only the most recent shares from Archive that fill the missing shares
|
||||
$log->logInfo('Fetching ' . ($pplns_target - $iRoundShares) . ' additional shares from archive');
|
||||
if (!$aArchiveShares = $share->getArchiveShares($pplns_target - $iRoundShares)) {
|
||||
$log->logError('Failed to fetch shares from archive, setting target to round total');
|
||||
$pplns_target = $iRoundShares;
|
||||
} else {
|
||||
// Add archived shares to users current shares, if we have any in archive
|
||||
if (is_array($aArchiveShares)) {
|
||||
$log->logDebug('Found shares in archive to match PPLNS target, calculating per-user shares');
|
||||
foreach($aAccountShares as $key => $aData) {
|
||||
if (array_key_exists($aData['username'], $aArchiveShares)) {
|
||||
$log->logDebug('Found user ' . $aData['username'] . ' in archived shares');
|
||||
$log->logDebug(' valid : ' . $aAccountShares[$key]['valid'] . ' + ' . $aArchiveShares[$aData['username']]['valid'] . ' = ' . ($aAccountShares[$key]['valid'] + $aArchiveShares[$aData['username']]['valid']) );
|
||||
$log->logDebug(' invalid : ' . $aAccountShares[$key]['invalid'] . ' + ' . $aArchiveShares[$aData['username']]['invalid'] . ' = ' . ($aAccountShares[$key]['invalid'] + $aArchiveShares[$aData['username']]['invalid']) );
|
||||
$aAccountShares[$key]['valid'] += $aArchiveShares[$aData['username']]['valid'];
|
||||
$aAccountShares[$key]['invalid'] += $aArchiveShares[$aData['username']]['invalid'];
|
||||
}
|
||||
}
|
||||
}
|
||||
// We tried to fill up to PPLNS target, now we need to check the actual shares to properly payout users
|
||||
foreach($aAccountShares as $key => $aData) {
|
||||
$iNewRoundShares += $aData['valid'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We filled from archive but still are not able to match PPLNS target, re-adjust
|
||||
if ($iRoundShares < $iNewRoundShares) {
|
||||
$log->logInfo('Adjusting round target to ' . $iNewRoundShares);
|
||||
$iRoundShares = $iNewRoundShares;
|
||||
}
|
||||
|
||||
// Table header for account shares
|
||||
$log->logInfo("ID\tUsername\tValid\tInvalid\tPercentage\tPayout\t\tDonation\tFee");
|
||||
|
||||
// Loop through all accounts that have found shares for this round
|
||||
foreach ($aAccountShares as $key => $aData) {
|
||||
// Payout based on PPLNS target shares, proportional payout for all users
|
||||
$aData['percentage'] = round(( 100 / $iRoundShares) * $aData['valid'], 8);
|
||||
$aData['payout'] = round(( $aData['percentage'] / 100 ) * $dReward, 8);
|
||||
// Defaults
|
||||
$aData['fee' ] = 0;
|
||||
$aData['donation'] = 0;
|
||||
|
||||
if ($config['fees'] > 0 && $aData['no_fees'] == 0)
|
||||
$aData['fee'] = round($config['fees'] / 100 * $aData['payout'], 8);
|
||||
// Calculate donation amount, fees not included
|
||||
$aData['donation'] = round($user->getDonatePercent($user->getUserId($aData['username'])) / 100 * ( $aData['payout'] - $aData['fee']), 8);
|
||||
|
||||
// Verbose output of this users calculations
|
||||
$log->logInfo($aData['id'] . "\t" .
|
||||
$aData['username'] . "\t" .
|
||||
$aData['valid'] . "\t" .
|
||||
$aData['invalid'] . "\t" .
|
||||
number_format($aData['percentage'], 8) . "\t" .
|
||||
number_format($aData['payout'], 8) . "\t" .
|
||||
number_format($aData['donation'], 8) . "\t" .
|
||||
number_format($aData['fee'], 8));
|
||||
|
||||
// Add full round share statistics, not just PPLNS
|
||||
foreach ($aRoundAccountShares as $key => $aRoundData) {
|
||||
if ($aRoundData['username'] == $aData['username'])
|
||||
if (!$statistics->updateShareStatistics($aRoundData, $aBlock['id']))
|
||||
$log->logError('Failed to update share statistics for ' . $aData['username']);
|
||||
}
|
||||
|
||||
// Add new credit transaction
|
||||
if (!$transaction->addTransaction($aData['id'], $aData['payout'], 'Credit', $aBlock['id']))
|
||||
$log->logFatal('Failed to insert new Credit transaction to database for ' . $aData['username']);
|
||||
// Add new fee debit for this block
|
||||
if ($aData['fee'] > 0 && $config['fees'] > 0)
|
||||
if (!$transaction->addTransaction($aData['id'], $aData['fee'], 'Fee', $aBlock['id']))
|
||||
$log->logFatal('Failed to insert new Fee transaction to database for ' . $aData['username']);
|
||||
// Add new donation debit
|
||||
if ($aData['donation'] > 0)
|
||||
if (!$transaction->addTransaction($aData['id'], $aData['donation'], 'Donation', $aBlock['id']))
|
||||
$log->logFatal('Failed to insert new Donation transaction to database for ' . $aData['username']);
|
||||
}
|
||||
|
||||
// Move counted shares to archive before this blockhash upstream share
|
||||
if (!$share->moveArchive($iCurrentUpstreamId, $aBlock['id'], $iPreviousShareId))
|
||||
$log->logError('Failed to copy shares to archive table');
|
||||
// Delete all accounted shares
|
||||
if (!$share->deleteAccountedShares($iCurrentUpstreamId, $iPreviousShareId)) {
|
||||
$log->logFatal("Failed to delete accounted shares from $iPreviousShareId to $iCurrentUpstreamId, aborting!");
|
||||
exit(1);
|
||||
}
|
||||
// Mark this block as accounted for
|
||||
if (!$block->setAccounted($aBlock['id'])) {
|
||||
$log->logFatal("Failed to mark block as accounted! Aborting!");
|
||||
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
|
||||
$monitoring->setStatus($cron_name . "_message", "message", "Failed to mark block " . $aBlock['height'] . " as accounted");
|
||||
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
require_once('cron_end.inc.php');
|
||||
?>
|
||||
@ -19,26 +19,54 @@ limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
// Change to working directory
|
||||
chdir(dirname(__FILE__));
|
||||
|
||||
// Include all settings and classes
|
||||
require_once('shared.inc.php');
|
||||
|
||||
|
||||
// Check if we are set as the payout system
|
||||
if ($config['payout_system'] != 'pps') {
|
||||
verbose("Please activate this cron in configuration via payout_system = pps\n");
|
||||
$log->logInfo("Please activate this cron in configuration via payout_system = pps\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// Fetch all transactions since our last block
|
||||
if ( $bitcoin->can_connect() === true ){
|
||||
$dDifficulty = $bitcoin->getdifficulty();
|
||||
if (is_array($dDifficulty) && array_key_exists('proof-of-work', $dDifficulty))
|
||||
$dDifficulty = $dDifficulty['proof-of-work'];
|
||||
} else {
|
||||
verbose("Aborted: " . $bitcoin->can_connect() . "\n");
|
||||
$log->logFatal("Aborted: " . $bitcoin->can_connect() . "\n");
|
||||
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
|
||||
$monitoring->setStatus($cron_name . "_message", "message", "Unable to connect to RPC server");
|
||||
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Value per share calculation
|
||||
$pps_value = number_format(round(50 / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']), 12) ,12);
|
||||
// We support some dynamic reward targets but fall back to our fixed value
|
||||
// Re-calculate after each run due to re-targets in this loop
|
||||
if ($config['pps']['reward']['type'] == 'blockavg' && $block->getBlockCount() > 0) {
|
||||
$pps_reward = round($block->getAvgBlockReward($config['pps']['blockavg']['blockcount']));
|
||||
$log->logInfo("PPS reward using block average, amount: " . $pps_reward . "\tdifficulty: " . $dDifficulty);
|
||||
} else {
|
||||
if ($config['pps']['reward']['type'] == 'block') {
|
||||
if ($aLastBlock = $block->getLast()) {
|
||||
$pps_reward = $aLastBlock['amount'];
|
||||
$log->logInfo("PPS reward using last block, amount: " . $pps_reward . "\tdifficulty: " . $dDifficulty);
|
||||
} else {
|
||||
$pps_reward = $config['pps']['reward']['default'];
|
||||
$log->logInfo("PPS reward using default, amount: " . $pps_reward . "\tdifficulty: " . $dDifficulty);
|
||||
}
|
||||
} else {
|
||||
$pps_reward = $config['pps']['reward']['default'];
|
||||
$log->logInfo("PPS reward fixed default, amount: " . $pps_reward . "\tdifficulty: " . $dDifficulty);
|
||||
}
|
||||
}
|
||||
|
||||
// Per-share value to be paid out to users
|
||||
$pps_value = round($pps_reward / (pow(2,32) * $dDifficulty) * pow(2, $config['pps_target']), 12);
|
||||
|
||||
// Find our last share accounted and last inserted share for PPS calculations
|
||||
$iPreviousShareId = $setting->getValue('pps_last_share_id');
|
||||
@ -47,81 +75,90 @@ $iLastShareId = $share->getLastInsertedShareId();
|
||||
// Check for all new shares, we start one higher as our last accounted share to avoid duplicates
|
||||
$aAccountShares = $share->getSharesForAccounts($iPreviousShareId + 1, $iLastShareId);
|
||||
|
||||
verbose("ID\tUsername\tInvalid\tValid\t\tPPS Value\t\tPayout\t\tDonation\tFee\t\tStatus\n");
|
||||
$log->logInfo("ID\tUsername\tInvalid\tValid\t\tPPS Value\t\tPayout\t\tDonation\tFee");
|
||||
|
||||
foreach ($aAccountShares as $aData) {
|
||||
// Take our valid shares and multiply by per share value
|
||||
$aData['payout'] = number_format(round($aData['valid'] * $pps_value, 8), 8);
|
||||
$aData['payout'] = round($aData['valid'] * $pps_value, 8);
|
||||
|
||||
// Defaults
|
||||
$aData['fee' ] = 0;
|
||||
$aData['donation'] = 0;
|
||||
|
||||
// Calculate block fees
|
||||
if ($config['fees'] > 0)
|
||||
$aData['fee'] = number_format(round($config['fees'] / 100 * $aData['payout'], 8), 8);
|
||||
if ($config['fees'] > 0 && $aData['no_fees'] == 0)
|
||||
$aData['fee'] = round($config['fees'] / 100 * $aData['payout'], 8);
|
||||
// Calculate donation amount
|
||||
$aData['donation'] = number_format(round($user->getDonatePercent($user->getUserId($aData['username'])) / 100 * ( $aData['payout'] - $aData['fee']), 8), 8);
|
||||
$aData['donation'] = round($user->getDonatePercent($user->getUserId($aData['username'])) / 100 * ( $aData['payout'] - $aData['fee']), 8);
|
||||
|
||||
verbose($aData['id'] . "\t" .
|
||||
$log->logInfo($aData['id'] . "\t" .
|
||||
$aData['username'] . "\t" .
|
||||
$aData['invalid'] . "\t" .
|
||||
$aData['valid'] . "\t*\t" .
|
||||
$pps_value . "\t=\t" .
|
||||
$aData['payout'] . "\t" .
|
||||
$aData['donation'] . "\t" .
|
||||
$aData['fee'] . "\t");
|
||||
number_format($pps_value, 12) . "\t=\t" .
|
||||
number_format($aData['payout'], 8) . "\t" .
|
||||
number_format($aData['donation'], 8) . "\t" .
|
||||
number_format($aData['fee'], 8));
|
||||
|
||||
$strStatus = "OK";
|
||||
// Add new credit transaction
|
||||
if (!$transaction->addTransaction($aData['id'], $aData['payout'], 'Credit_PPS'))
|
||||
$strStatus = "Transaction Failed";
|
||||
$log->logError('Failed to add Credit_PPS transaction in database');
|
||||
// Add new fee debit for this block
|
||||
if ($aData['fee'] > 0 && $config['fees'] > 0)
|
||||
if (!$transaction->addTransaction($aData['id'], $aData['fee'], 'Fee_PPS'))
|
||||
$strStatus = "Fee Failed";
|
||||
$log->logError('Failed to add Fee_PPS transaction in database');
|
||||
// Add new donation debit
|
||||
if ($aData['donation'] > 0)
|
||||
if (!$transaction->addTransaction($aData['id'], $aData['donation'], 'Donation_PPS'))
|
||||
$strStatus = "Donation Failed";
|
||||
verbose($strStatus . "\n");
|
||||
$log->logError('Failed to add Donation_PPS transaction in database');
|
||||
}
|
||||
|
||||
// Store our last inserted ID for the next run
|
||||
$setting->setValue('pps_last_share_id', $iLastShareId);
|
||||
|
||||
verbose("\n\n------------------------------------------------------------------------------------\n\n");
|
||||
|
||||
// Fetch all unaccounted blocks
|
||||
$aAllBlocks = $block->getAllUnaccounted('ASC');
|
||||
if (empty($aAllBlocks)) {
|
||||
verbose("No new unaccounted blocks found\n");
|
||||
}
|
||||
if (empty($aAllBlocks)) $log->logDebug("No new unaccounted blocks found");
|
||||
|
||||
// Go through blocks and archive/delete shares that have been accounted for
|
||||
foreach ($aAllBlocks as $iIndex => $aBlock) {
|
||||
// If we are running through more than one block, check for previous share ID
|
||||
$iLastBlockShare = @$aAllBlocks[$iIndex - 1]['share_id'] ? @$aAllBlocks[$iIndex - 1]['share_id'] : 0;
|
||||
if (!is_numeric($aBlock['share_id'])) {
|
||||
$log->logFatal("Block " . $aBlock['height'] . " has no share_id associated with it, not going to continue");
|
||||
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
|
||||
$monitoring->setStatus($cron_name . "_message", "message", "Block " . $aBlock['height'] . " has no share_id associated with it");
|
||||
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
|
||||
exit(1);
|
||||
}
|
||||
// Per account statistics
|
||||
$aAccountShares = $share->getSharesForAccounts(@$iLastBlockShare, $aBlock['share_id']);
|
||||
foreach ($aAccountShares as $key => $aData) {
|
||||
if (!$statistics->updateShareStatistics($aData, $aBlock['id']))
|
||||
verbose("Failed to update stats for this block on : " . $aData['username'] . "\n");
|
||||
$log->logError("Failed to update stats for this block on : " . $aData['username']);
|
||||
}
|
||||
// Move shares to archive
|
||||
if ($config['archive_shares'] && $aBlock['share_id'] < $iLastShareId) {
|
||||
if ($aBlock['share_id'] < $iLastShareId) {
|
||||
if (!$share->moveArchive($aBlock['share_id'], $aBlock['id'], @$iLastBlockShare))
|
||||
verbose("Archving failed\n");
|
||||
$log->logError("Archving failed");
|
||||
}
|
||||
// Delete shares
|
||||
if ($aBlock['share_id'] < $iLastShareId && !$share->deleteAccountedShares($aBlock['share_id'], $iLastBlockShare)) {
|
||||
verbose("\nERROR : Failed to delete accounted shares from " . $aBlock['share_id'] . " to " . $iLastBlockShare . ", aborting!\n");
|
||||
$log->logFatal("Failed to delete accounted shares from " . $aBlock['share_id'] . " to " . $iLastBlockShare . ", aborting!");
|
||||
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
|
||||
$monitoring->setStatus($cron_name . "_message", "message", "Failed to delete accounted shares from " . $aBlock['share_id'] . " to " . $iLastBlockShare);
|
||||
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
|
||||
exit(1);
|
||||
}
|
||||
// Mark this block as accounted for
|
||||
if (!$block->setAccounted($aBlock['id'])) {
|
||||
verbose("\nERROR : Failed to mark block as accounted! Aborting!\n");
|
||||
$log->logFatal("Failed to mark block as accounted! Aborting!");
|
||||
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
|
||||
$monitoring->setStatus($cron_name . "_message", "message", "Failed to mark block " . $aBlock['height'] . " as accounted");
|
||||
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
require_once('cron_end.inc.php');
|
||||
?>
|
||||
|
||||
@ -19,93 +19,108 @@ limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
// Change to working directory
|
||||
chdir(dirname(__FILE__));
|
||||
|
||||
// Include all settings and classes
|
||||
require_once('shared.inc.php');
|
||||
|
||||
// Check if we are set as the payout system
|
||||
if ($config['payout_system'] != 'prop') {
|
||||
verbose("Please activate this cron in configuration via payout_system = prop\n");
|
||||
$log->logInfo("Please activate this cron in configuration via payout_system = prop");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// Fetch all unaccounted blocks
|
||||
$aAllBlocks = $block->getAllUnaccounted('ASC');
|
||||
if (empty($aAllBlocks)) {
|
||||
verbose("No new unaccounted blocks found\n");
|
||||
$log->logDebug('No new unaccounted blocks found in database');
|
||||
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
|
||||
$monitoring->setStatus($cron_name . "_message", "message", "No new unaccounted blocks");
|
||||
$monitoring->setStatus($cron_name . "_status", "okerror", 0);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
// Table header for account shares
|
||||
$log->logInfo("ID\tUsername\tValid\tInvalid\tPercentage\tPayout\t\tDonation\tFee");
|
||||
foreach ($aAllBlocks as $iIndex => $aBlock) {
|
||||
if (!$aBlock['accounted']) {
|
||||
$iPreviousShareId = @$aAllBlocks[$iIndex - 1]['share_id'] ? $aAllBlocks[$iIndex - 1]['share_id'] : 0;
|
||||
$iCurrentUpstreamId = $aBlock['share_id'];
|
||||
$aAccountShares = $share->getSharesForAccounts($iPreviousShareId, $aBlock['share_id']);
|
||||
$iRoundShares = $share->getRoundShares($iPreviousShareId, $aBlock['share_id']);
|
||||
$config['reward_type'] == 'block' ? $dReward = $aBlock['amount'] : $dReward = $config['reward'];
|
||||
|
||||
if (empty($aAccountShares)) {
|
||||
verbose("\nNo shares found for this block\n\n");
|
||||
sleep(2);
|
||||
continue;
|
||||
$log->logFatal('No shares found for this block, aborted: ' . $aBlock['height']);
|
||||
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
|
||||
$monitoring->setStatus($cron_name . "_message", "message", "No shares found for this block, aborted: " . $aBlock['height']);
|
||||
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Table header for account shares
|
||||
verbose("ID\tUsername\tValid\tInvalid\tPercentage\tPayout\t\tDonation\tFee\t\tStatus\n");
|
||||
|
||||
// Loop through all accounts that have found shares for this round
|
||||
foreach ($aAccountShares as $key => $aData) {
|
||||
// Payout based on shares, PPS system
|
||||
$aData['percentage'] = number_format(round(( 100 / $iRoundShares ) * $aData['valid'], 8), 8);
|
||||
$aData['payout'] = number_format(round(( $aData['percentage'] / 100 ) * $config['reward'], 8), 8);
|
||||
$aData['percentage'] = round(( 100 / $iRoundShares ) * $aData['valid'], 8);
|
||||
$aData['payout'] = round(( $aData['percentage'] / 100 ) * $dReward, 8);
|
||||
// Defaults
|
||||
$aData['fee' ] = 0;
|
||||
$aData['donation'] = 0;
|
||||
|
||||
if ($config['fees'] > 0)
|
||||
$aData['fee'] = number_format(round($config['fees'] / 100 * $aData['payout'], 8), 8);
|
||||
if ($config['fees'] > 0 && $aData['no_fees'] == 0)
|
||||
$aData['fee'] = round($config['fees'] / 100 * $aData['payout'], 8);
|
||||
// Calculate donation amount, fees not included
|
||||
$aData['donation'] = number_format(round($user->getDonatePercent($user->getUserId($aData['username'])) / 100 * ( $aData['payout'] - $aData['fee']), 8), 8);
|
||||
$aData['donation'] = round($user->getDonatePercent($user->getUserId($aData['username'])) / 100 * ( $aData['payout'] - $aData['fee']), 8);
|
||||
|
||||
// Verbose output of this users calculations
|
||||
verbose($aData['id'] . "\t" .
|
||||
$aData['username'] . "\t" .
|
||||
$aData['valid'] . "\t" .
|
||||
$aData['invalid'] . "\t" .
|
||||
$aData['percentage'] . "\t" .
|
||||
$aData['payout'] . "\t" .
|
||||
$aData['donation'] . "\t" .
|
||||
$aData['fee'] . "\t");
|
||||
$log->logInfo($aData['id'] . "\t" .
|
||||
$aData['username'] . "\t" .
|
||||
$aData['valid'] . "\t" .
|
||||
$aData['invalid'] . "\t" .
|
||||
number_format($aData['percentage'], 8) . "\t" .
|
||||
number_format($aData['payout'], 8) . "\t" .
|
||||
number_format($aData['donation'], 8) . "\t" .
|
||||
number_format($aData['fee']), 8);
|
||||
|
||||
$strStatus = "OK";
|
||||
// Update user share statistics
|
||||
if (!$statistics->updateShareStatistics($aData, $aBlock['id']))
|
||||
$strStatus = "Stats Failed";
|
||||
$log->logFatal('Failed to update share statistics for ' . $aData['username']);
|
||||
// Add new credit transaction
|
||||
if (!$transaction->addTransaction($aData['id'], $aData['payout'], 'Credit', $aBlock['id']))
|
||||
$strStatus = "Transaction Failed";
|
||||
$log->logFatal('Failed to insert new Credit transaction to database for ' . $aData['username']);
|
||||
// Add new fee debit for this block
|
||||
if ($aData['fee'] > 0 && $config['fees'] > 0)
|
||||
if (!$transaction->addTransaction($aData['id'], $aData['fee'], 'Fee', $aBlock['id']))
|
||||
$strStatus = "Fee Failed";
|
||||
$log->logFatal('Failed to insert new Fee transaction to database for ' . $aData['username']);
|
||||
// Add new donation debit
|
||||
if ($aData['donation'] > 0)
|
||||
if (!$transaction->addTransaction($aData['id'], $aData['donation'], 'Donation', $aBlock['id']))
|
||||
$strStatus = "Donation Failed";
|
||||
verbose("\t$strStatus\n");
|
||||
$log->logFatal('Failed to insert new Donation transaction to database for ' . $aData['username']);
|
||||
}
|
||||
|
||||
// Move counted shares to archive before this blockhash upstream share
|
||||
if ($config['archive_shares']) $share->moveArchive($iCurrentUpstreamId, $aBlock['id'], $iPreviousShareId);
|
||||
if (!$share->moveArchive($iCurrentUpstreamId, $aBlock['id'], $iPreviousShareId))
|
||||
$log->logError('Failed to copy shares to archive');
|
||||
// Delete all accounted shares
|
||||
if (!$share->deleteAccountedShares($iCurrentUpstreamId, $iPreviousShareId)) {
|
||||
verbose("\nERROR : Failed to delete accounted shares from $iPreviousShareId to $iCurrentUpstreamId, aborting!\n");
|
||||
$log->logFatal('Failed to delete accounted shares from ' . $iPreviousShareId . ' to ' . $iCurrentUpstreamId . ', aborted');
|
||||
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
|
||||
$monitoring->setStatus($cron_name . "_message", "message", "Failed to delete accounted shares from " . $iPreviousShareId . " to " . $iCurrentUpstreamId);
|
||||
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
|
||||
exit(1);
|
||||
}
|
||||
// Mark this block as accounted for
|
||||
if (!$block->setAccounted($aBlock['id'])) {
|
||||
verbose("\nERROR : Failed to mark block as accounted! Aborting!\n");
|
||||
$log->logFatal('Failed to mark block as accounted! Aborted.');
|
||||
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
|
||||
$monitoring->setStatus($cron_name . "_message", "message", "Failed to mark block " . $aBlock['height'] . " as accounted");
|
||||
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
verbose("------------------------------------------------------------------------\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
require_once('cron_end.inc.php');
|
||||
?>
|
||||
|
||||
@ -9,27 +9,64 @@
|
||||
# PHP Detections, if this fails hard code it
|
||||
PHP_BIN=$( which php )
|
||||
|
||||
# Path to PID file, needs to be writable by user running this
|
||||
PIDFILE='/tmp/mmcfe-ng-cron.pid'
|
||||
|
||||
# Location of our cronjobs, assume current directory
|
||||
CRONHOME='.'
|
||||
|
||||
# List of cruns to execute
|
||||
CRONS="findblock.php proportional_payout.php pps_payout.php blockupdate.php auto_payout.php tickerupdate.php notifications.php statistics.php"
|
||||
|
||||
# Additional arguments to pass to cronjobs
|
||||
CRONARGS="-v"
|
||||
CRONS="findblock.php proportional_payout.php pplns_payout.php pps_payout.php blockupdate.php manual_payout.php auto_payout.php tickerupdate.php notifications.php statistics.php archive_cleanup.php"
|
||||
|
||||
# Output additional runtime information
|
||||
VERBOSE="0"
|
||||
|
||||
# Base path for PIDFILE, (full path).
|
||||
BASEPATH="/tmp"
|
||||
|
||||
# Subfolder for PIDFILE, so it's path will be unique in a multipool server.
|
||||
# Path relative to BASEPATH.
|
||||
# Eg. SUBFOLDER="LTC"
|
||||
SUBFOLDER=""
|
||||
|
||||
################################################################
|
||||
# #
|
||||
# You probably don't need to change anything beyond this point #
|
||||
# #
|
||||
################################################################
|
||||
|
||||
# My own name
|
||||
ME=$( basename $0 )
|
||||
|
||||
# Overwrite some settings via command line arguments
|
||||
while getopts "hvp:d:" opt; do
|
||||
case "$opt" in
|
||||
h|\?)
|
||||
echo "Usage: $0 [-v] [-p PHP_BINARY] [-d SUBFOLDER]";
|
||||
exit 0
|
||||
;;
|
||||
v) VERBOSE=1 ;;
|
||||
p) PHP_BIN=$OPTARG ;;
|
||||
d) SUBFOLDER=$OPTARG ;;
|
||||
:)
|
||||
echo "Option -$OPTARG requires an argument." >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Path to PID file, needs to be writable by user running this
|
||||
PIDFILE="${BASEPATH}/${SUBFOLDER}/${ME}.pid"
|
||||
# Clean PIDFILE path
|
||||
PIDFILE=$(readlink -m "$PIDFILE")
|
||||
|
||||
# Create folders recursively if necessary
|
||||
if ! $(mkdir -p $( dirname $PIDFILE)); then
|
||||
echo "Error creating PIDFILE path: $( dirname $PIDFILE )"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Find scripts path
|
||||
if [[ -L $0 ]]; then
|
||||
CRONHOME=$( dirname $( readlink $0 ) )
|
||||
else
|
||||
CRONHOME=$( dirname $0 )
|
||||
fi
|
||||
|
||||
# Change working director to CRONHOME
|
||||
if ! cd $CRONHOME 2>/dev/null; then
|
||||
echo "Unable to change to working directory \$CRONHOME: $CRONHOME"
|
||||
@ -66,8 +103,8 @@ fi
|
||||
echo $PID > $PIDFILE
|
||||
|
||||
for cron in $CRONS; do
|
||||
[[ $VERBOSE == 1 ]] && echo "Running $cron, see output below for details"
|
||||
$PHP_BIN $cron $CRONARGS
|
||||
[[ $VERBOSE == 1 ]] && echo "Running $cron, check logfile for details"
|
||||
$PHP_BIN $cron
|
||||
done
|
||||
|
||||
# Remove pidfile
|
||||
|
||||
@ -22,6 +22,16 @@ limitations under the License.
|
||||
// We need to find our include files so set this properly
|
||||
define("BASEPATH", "../public/");
|
||||
|
||||
/*****************************************************
|
||||
* No need to change beyond this point *
|
||||
*****************************************************/
|
||||
|
||||
// Used in autoloading of API class, adding it to stop PHP warnings
|
||||
$dStartTime = microtime(true);
|
||||
|
||||
// Our cron name
|
||||
$cron_name = basename($_SERVER['PHP_SELF'], '.php');
|
||||
|
||||
// Our security check
|
||||
define("SECURITY", 1);
|
||||
|
||||
@ -31,16 +41,15 @@ require_once(BASEPATH . 'include/config/global.inc.php');
|
||||
// We include all needed files here, even though our templates could load them themself
|
||||
require_once(INCLUDE_DIR . '/autoloader.inc.php');
|
||||
|
||||
// Parse command line
|
||||
$options = getopt("v");
|
||||
if (array_key_exists('v', $options)) {
|
||||
define("VERBOSE", true);
|
||||
} else {
|
||||
define("VERBOSE", false);
|
||||
}
|
||||
// Load 3rd party logging library for running crons
|
||||
$log = new KLogger ( 'logs/' . $cron_name . '.txt' , KLogger::INFO );
|
||||
$log->LogDebug('Starting ' . $cron_name);
|
||||
|
||||
// Command line cron functions only
|
||||
function verbose($msg) {
|
||||
if (VERBOSE) echo $msg;
|
||||
}
|
||||
// Load the start time for later runtime calculations for monitoring
|
||||
$cron_start[$cron_name] = microtime(true);
|
||||
|
||||
// Mark cron as running for monitoring
|
||||
$log->logDebug('Marking cronjob as running for monitoring');
|
||||
$monitoring->setStatus($cron_name . '_active', 'yesno', 1);
|
||||
$monitoring->setStatus($cron_name . '_starttime', 'date', time());
|
||||
?>
|
||||
|
||||
@ -19,31 +19,35 @@ limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
// Change to working directory
|
||||
chdir(dirname(__FILE__));
|
||||
|
||||
// Include all settings and classes
|
||||
require_once('shared.inc.php');
|
||||
|
||||
// Fetch all cachable values but disable fetching from cache
|
||||
$statistics->setGetCache(false);
|
||||
|
||||
// Since fetching from cache is disabled, overwrite our stats
|
||||
if (!$statistics->getRoundShares())
|
||||
verbose("Unable to fetch and store current round shares\n");
|
||||
if (!$statistics->getTopContributors('shares'))
|
||||
verbose("Unable to fetch and store top share contributors\n");
|
||||
if (!$statistics->getTopContributors('hashes'))
|
||||
verbose("Unable to fetch and store top hashrate contributors\n");
|
||||
if (!$statistics->getCurrentHashrate())
|
||||
verbose("Unable to fetch and store pool hashrate\n");
|
||||
// Admin specific statistics, we cache the global query due to slowness
|
||||
if (!$statistics->getAllUserStats('%'))
|
||||
verbose("Unable to fetch and store admin panel full user list\n");
|
||||
|
||||
// Per user share statistics based on all shares submitted
|
||||
$stmt = $mysqli->prepare("SELECT DISTINCT SUBSTRING_INDEX( `username` , '.', 1 ) AS username FROM " . $share->getTableName());
|
||||
if ($stmt && $stmt->execute() && $result = $stmt->get_result()) {
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
if (!$statistics->getUserShares($user->getUserId($row['username'])))
|
||||
verbose("Failed to fetch and store user stats for " . $row['username'] . "\n");
|
||||
}
|
||||
}
|
||||
$start = microtime(true);
|
||||
if ( ! $aAllUserShares = $statistics->getAllUserShares() )
|
||||
$log->logError('getAllUserShares update failed');
|
||||
$log->logInfo("getAllUserShares " . number_format(microtime(true) - $start, 2) . " seconds");
|
||||
|
||||
$start = microtime(true);
|
||||
if (!$statistics->getTopContributors('hashes'))
|
||||
$log->logError("getTopContributors hashes update failed");
|
||||
$log->logInfo("getTopContributors hashes " . number_format(microtime(true) - $start, 2) . " seconds");
|
||||
|
||||
$start = microtime(true);
|
||||
if (!$statistics->getCurrentHashrate())
|
||||
$log->logError("getCurrentHashrate update failed");
|
||||
$log->logInfo("getCurrentHashrate " . number_format(microtime(true) - $start, 2) . " seconds");
|
||||
|
||||
/*
|
||||
// Admin specific statistics, we cache the global query due to slowness
|
||||
$start = microtime(true);
|
||||
if (!$statistics->getAllUserStats('%'))
|
||||
$log->logError("getAllUserStats update failed");
|
||||
$log->logInfo("getAllUserStats " . number_format(microtime(true) - $start, 2) . " seconds");
|
||||
*/
|
||||
|
||||
require_once('cron_end.inc.php');
|
||||
?>
|
||||
|
||||
@ -19,18 +19,22 @@ limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
// Change to working directory
|
||||
chdir(dirname(__FILE__));
|
||||
|
||||
// Include all settings and classes
|
||||
require_once('shared.inc.php');
|
||||
|
||||
// Include additional file not set in autoloader
|
||||
require_once(BASEPATH . CLASS_DIR . '/tools.class.php');
|
||||
require_once(CLASS_DIR . '/tools.class.php');
|
||||
|
||||
verbose("Running updates\n");
|
||||
if ($aData = $tools->getApi($config['price']['url'], $config['price']['target'])) {
|
||||
if (!$setting->setValue('price', $aData['ticker']['last']))
|
||||
verbose("ERR Table update failed");
|
||||
if ($price = $tools->getPrice()) {
|
||||
$log->logInfo("Price update: found $price as price");
|
||||
if (!$setting->setValue('price', $price))
|
||||
$log->logError("unable to update value in settings table");
|
||||
} else {
|
||||
verbose("ERR Failed download JSON data from " . $config['price']['url'].$config['price']['target'] . "\n");
|
||||
$log->logFatal("failed to fetch API data: " . $tools->getError());
|
||||
}
|
||||
|
||||
require_once('cron_end.inc.php');
|
||||
?>
|
||||
|
||||
@ -1,18 +1,56 @@
|
||||
<?php
|
||||
|
||||
// Default classes
|
||||
require_once(CLASS_DIR . '/debug.class.php');
|
||||
require_once(CLASS_DIR . '/bitcoin.class.php');
|
||||
require_once(CLASS_DIR . '/statscache.class.php');
|
||||
require_once(CLASS_DIR . '/bitcoinwrapper.class.php');
|
||||
require_once(INCLUDE_DIR . '/lib/KLogger.php');
|
||||
require_once(INCLUDE_DIR . '/database.inc.php');
|
||||
require_once(INCLUDE_DIR . '/config/memcache_keys.inc.php');
|
||||
|
||||
// We need to load these two first
|
||||
require_once(CLASS_DIR . '/base.class.php');
|
||||
require_once(CLASS_DIR . '/setting.class.php');
|
||||
|
||||
// We need this one in here to properly set our theme
|
||||
require_once(INCLUDE_DIR . '/lib/Mobile_Detect.php');
|
||||
|
||||
// Detect device
|
||||
if ($detect->isMobile() && $setting->getValue('website_mobile_theme')) {
|
||||
// Set to mobile theme
|
||||
$setting->getValue('website_mobile_theme') ? $theme = $setting->getValue('website_mobile_theme') : $theme = 'mobile';
|
||||
} else {
|
||||
// Use configured theme, fallback to default theme
|
||||
$setting->getValue('website_theme') ? $theme = $setting->getValue('website_theme') : $theme = 'mpos';
|
||||
}
|
||||
define('THEME', $theme);
|
||||
|
||||
// Load smarty now that we have our theme defined
|
||||
require_once(INCLUDE_DIR . '/smarty.inc.php');
|
||||
// Load classes that need the above as dependencies
|
||||
|
||||
// Load everything else in proper order
|
||||
require_once(CLASS_DIR . '/mail.class.php');
|
||||
require_once(CLASS_DIR . '/tokentype.class.php');
|
||||
require_once(CLASS_DIR . '/token.class.php');
|
||||
require_once(CLASS_DIR . '/payout.class.php');
|
||||
require_once(CLASS_DIR . '/block.class.php');
|
||||
|
||||
// We require the block class to properly grab the round ID
|
||||
require_once(CLASS_DIR . '/statscache.class.php');
|
||||
|
||||
require_once(CLASS_DIR . '/bitcoin.class.php');
|
||||
require_once(CLASS_DIR . '/bitcoinwrapper.class.php');
|
||||
require_once(CLASS_DIR . '/monitoring.class.php');
|
||||
require_once(CLASS_DIR . '/user.class.php');
|
||||
require_once(CLASS_DIR . '/invitation.class.php');
|
||||
require_once(CLASS_DIR . '/share.class.php');
|
||||
require_once(CLASS_DIR . '/worker.class.php');
|
||||
require_once(CLASS_DIR . '/statistics.class.php');
|
||||
require_once(CLASS_DIR . '/roundstats.class.php');
|
||||
require_once(CLASS_DIR . '/transaction.class.php');
|
||||
require_once(CLASS_DIR . '/setting.class.php');
|
||||
require_once(CLASS_DIR . '/mail.class.php');
|
||||
require_once(CLASS_DIR . '/notification.class.php');
|
||||
require_once(CLASS_DIR . '/news.class.php');
|
||||
require_once(CLASS_DIR . '/api.class.php');
|
||||
require_once(INCLUDE_DIR . '/lib/Michelf/Markdown.php');
|
||||
require_once(INCLUDE_DIR . '/lib/scrypt.php');
|
||||
|
||||
|
||||
?>
|
||||
|
||||
66
public/include/classes/api.class.php
Normal file
66
public/include/classes/api.class.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
/**
|
||||
* Helper class for our API
|
||||
**/
|
||||
class Api extends Base {
|
||||
private $api_version = '1.0.0';
|
||||
|
||||
function setStartTime($dStartTime) {
|
||||
$this->dStartTime = $dStartTime;
|
||||
}
|
||||
function isActive($error=true) {
|
||||
if (!$this->setting->getValue('disable_api')) {
|
||||
return true;
|
||||
} else {
|
||||
if ($error == true) {
|
||||
header('HTTP/1.1 501 Not implemented');
|
||||
die('501 Not implemented');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create API json object from input array
|
||||
* @param data Array data to create JSON for
|
||||
* @param force bool Enforce a JSON object
|
||||
* @return string JSON object
|
||||
**/
|
||||
function get_json($data, $force=false) {
|
||||
return json_encode(
|
||||
array( $_REQUEST['action'] => array(
|
||||
'version' => $this->api_version,
|
||||
'runtime' => (microtime(true) - $this->dStartTime) * 1000,
|
||||
'data' => $data
|
||||
)), $force ? JSON_FORCE_OBJECT : 0
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check user access level to the API call
|
||||
**/
|
||||
function checkAccess($user_id, $get_id=NULL) {
|
||||
if ( ! $this->user->isAdmin($user_id) && (!empty($get_id) && $get_id != $user_id)) {
|
||||
// User is NOT admin and tries to access an ID that is not their own
|
||||
header("HTTP/1.1 401 Unauthorized");
|
||||
die("Access denied");
|
||||
} else if ($this->user->isAdmin($user_id) && !empty($get_id)) {
|
||||
// User is an admin and tries to fetch another users data
|
||||
$id = $get_id;
|
||||
// Is it a username or a user ID
|
||||
ctype_digit($_REQUEST['id']) ? $id = $get_id : $id = $this->user->getUserId($get_id);
|
||||
} else {
|
||||
$id = $user_id;
|
||||
}
|
||||
return $id;
|
||||
}
|
||||
}
|
||||
|
||||
$api = new Api();
|
||||
$api->setConfig($config);
|
||||
$api->setUser($user);
|
||||
$api->setSetting($setting);
|
||||
$api->setStartTime($dStartTime);
|
||||
122
public/include/classes/base.class.php
Normal file
122
public/include/classes/base.class.php
Normal file
@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
|
||||
// Our base class that defines
|
||||
// some cross-class functions.
|
||||
class Base {
|
||||
private $sError = '';
|
||||
private $values = array(), $types = '';
|
||||
|
||||
public function setDebug($debug) {
|
||||
$this->debug = $debug;
|
||||
}
|
||||
public function setMysql($mysqli) {
|
||||
$this->mysqli = $mysqli;
|
||||
}
|
||||
public function setMail($mail) {
|
||||
$this->mail = $mail;
|
||||
}
|
||||
public function setSmarty($smarty) {
|
||||
$this->smarty = $smarty;
|
||||
}
|
||||
public function setUser($user) {
|
||||
$this->user = $user;
|
||||
}
|
||||
public function setConfig($config) {
|
||||
$this->config = $config;
|
||||
}
|
||||
public function setToken($token) {
|
||||
$this->token = $token;
|
||||
}
|
||||
public function setBlock($block) {
|
||||
$this->block = $block;
|
||||
}
|
||||
public function setSetting($setting) {
|
||||
$this->setting = $setting;
|
||||
}
|
||||
public function setBitcoin($bitcoin) {
|
||||
$this->bitcoin = $bitcoin;
|
||||
}
|
||||
public function setTokenType($tokentype) {
|
||||
$this->tokentype = $tokentype;
|
||||
}
|
||||
public function setErrorMessage($msg) {
|
||||
$this->sError = $msg;
|
||||
}
|
||||
public function getError() {
|
||||
return $this->sError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single row from the table
|
||||
* @param value string Value to search for
|
||||
* @param search Return column to search for
|
||||
* @param field string Search column
|
||||
* @param type string Type of value
|
||||
* @return array Return result
|
||||
**/
|
||||
protected function getSingle($value, $search='id', $field='id', $type="i") {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$stmt = $this->mysqli->prepare("SELECT $search FROM $this->table WHERE $field = ? LIMIT 1");
|
||||
if ($this->checkStmt($stmt)) {
|
||||
$stmt->bind_param($type, $value);
|
||||
$stmt->execute();
|
||||
$stmt->bind_result($retval);
|
||||
$stmt->fetch();
|
||||
$stmt->close();
|
||||
return $retval;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function checkStmt($bState) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
if ($bState ===! true) {
|
||||
$this->debug->append("Failed to prepare statement: " . $this->mysqli->error);
|
||||
$this->setErrorMessage('Internal application Error');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Update a single row in a table
|
||||
* @param userID int Account ID
|
||||
* @param field string Field to update
|
||||
* @return bool
|
||||
**/
|
||||
protected function updateSingle($id, $field, $table='') {
|
||||
if (empty($table)) $table = $this->table;
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$stmt = $this->mysqli->prepare("UPDATE $table SET " . $field['name'] . " = ? WHERE id = ? LIMIT 1");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param($field['type'].'i', $field['value'], $id) && $stmt->execute())
|
||||
return true;
|
||||
$this->debug->append("Unable to update " . $field['name'] . " with " . $field['value'] . " for ID $id");
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* We may need to generate our bind_param list
|
||||
**/
|
||||
public function addParam($type, &$value) {
|
||||
$this->values[] = $value;
|
||||
$this->types .= $type;
|
||||
}
|
||||
public function getParam() {
|
||||
$array = array_merge(array($this->types), $this->values);
|
||||
// Clear the data
|
||||
$this->values = NULL;
|
||||
$this->types = NULL;
|
||||
// See here why we need this: http://stackoverflow.com/questions/16120822/mysqli-bind-param-expected-to-be-a-reference-value-given
|
||||
if (strnatcmp(phpversion(),'5.3') >= 0) {
|
||||
$refs = array();
|
||||
foreach($array as $key => $value)
|
||||
$refs[$key] = &$array[$key];
|
||||
return $refs;
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
?>
|
||||
@ -25,19 +25,40 @@ class BitcoinWrapper extends BitcoinClient {
|
||||
public function getblockcount() {
|
||||
$this->oDebug->append("STA " . __METHOD__, 4);
|
||||
if ($data = $this->memcache->get(__FUNCTION__)) return $data;
|
||||
return $this->memcache->setCache(__FUNCTION__, parent::getblockcount());
|
||||
return $this->memcache->setCache(__FUNCTION__, parent::getblockcount(), 30);
|
||||
}
|
||||
public function getdifficulty() {
|
||||
$this->oDebug->append("STA " . __METHOD__, 4);
|
||||
if ($data = $this->memcache->get(__FUNCTION__)) return $data;
|
||||
return $this->memcache->setCache(__FUNCTION__, parent::getdifficulty());
|
||||
$data = parent::getdifficulty();
|
||||
// Check for PoS/PoW coins
|
||||
if (is_array($data) && array_key_exists('proof-of-work', $data))
|
||||
$data = $data['proof-of-work'];
|
||||
return $this->memcache->setCache(__FUNCTION__, $data, 30);
|
||||
}
|
||||
public function getestimatedtime($iCurrentPoolHashrate) {
|
||||
$this->oDebug->append("STA " . __METHOD__, 4);
|
||||
if ($iCurrentPoolHashrate == 0) return 0;
|
||||
if ($data = $this->memcache->get(__FUNCTION__)) return $data;
|
||||
$dDifficulty = parent::getdifficulty();
|
||||
return $this->memcache->setCache(__FUNCTION__, $dDifficulty * pow(2,32) / $iCurrentPoolHashrate);
|
||||
$dDifficulty = $this->getdifficulty();
|
||||
return $this->memcache->setCache(__FUNCTION__, $dDifficulty * pow(2,32) / $iCurrentPoolHashrate, 30);
|
||||
}
|
||||
public function getnetworkhashps() {
|
||||
$this->oDebug->append("STA " . __METHOD__, 4);
|
||||
if ($data = $this->memcache->get(__FUNCTION__)) return $data;
|
||||
try {
|
||||
$dNetworkHashrate = $this->query('getmininginfo');
|
||||
if (is_array($dNetworkHashrate) && array_key_exists('networkhashps', $dNetworkHashrate)) {
|
||||
$dNetworkHashrate = $dNetworkHashrate['networkhashps'];
|
||||
} else if (is_array($dNetworkHashrate) && array_key_exists('hashespersec', $dNetworkHashrate)) {
|
||||
$dNetworkHashrate = $dNetworkHashrate['hashespersec'];
|
||||
} else if (is_array($dNetworkHashrate) && array_key_exists('netmhashps', $dNetworkHashrate)) {
|
||||
$dNetworkHashrate = $dNetworkHashrate['netmhashps'] * 1000 * 1000;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
return $this->memcache->setCache(__FUNCTION__, $dNetworkHashrate, 30);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -10,9 +10,10 @@ class Block {
|
||||
// This defines each block
|
||||
public $height, $blockhash, $confirmations, $time, $accounted;
|
||||
|
||||
public function __construct($debug, $mysqli, $salt) {
|
||||
public function __construct($debug, $mysqli, $config) {
|
||||
$this->debug = $debug;
|
||||
$this->mysqli = $mysqli;
|
||||
$this->config = $config;
|
||||
$this->debug->append("Instantiated Block class", 2);
|
||||
}
|
||||
|
||||
@ -43,6 +44,18 @@ class Block {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific block, by block height
|
||||
* @param height int Block Height
|
||||
* @return data array Block information from DB
|
||||
**/
|
||||
public function getBlock($height) {
|
||||
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE height = ? LIMIT 1");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $height) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $result->fetch_assoc();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get our last, highest share ID inserted for a block
|
||||
* @param none
|
||||
@ -55,6 +68,18 @@ class Block {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all blocks without a share ID
|
||||
* @param order string Sort order, default ASC
|
||||
* @return data array Array with database fields as keys
|
||||
**/
|
||||
public function getAllUnsetShareId($order='ASC') {
|
||||
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE ISNULL(share_id) ORDER BY height $order");
|
||||
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $result->fetch_all(MYSQLI_ASSOC);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all unaccounted blocks
|
||||
* @param order string Sort order, default ASC
|
||||
@ -62,12 +87,44 @@ class Block {
|
||||
**/
|
||||
public function getAllUnaccounted($order='ASC') {
|
||||
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE accounted = 0 ORDER BY height $order");
|
||||
if ($this->checkStmt($stmt)) {
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$stmt->close();
|
||||
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $result->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total amount of blocks in our table
|
||||
* @param noone
|
||||
* @return data int Count of rows
|
||||
**/
|
||||
public function getBlockCount() {
|
||||
$stmt = $this->mysqli->prepare("SELECT COUNT(id) AS blocks FROM $this->table");
|
||||
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return (int)$result->fetch_object()->blocks;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch our average share count for the past N blocks
|
||||
* @param limit int Maximum blocks to check
|
||||
* @return data float Float value of average shares
|
||||
**/
|
||||
public function getAvgBlockShares($height, $limit=1) {
|
||||
$stmt = $this->mysqli->prepare("SELECT AVG(x.shares) AS average FROM (SELECT shares FROM $this->table WHERE height <= ? ORDER BY height DESC LIMIT ?) AS x");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $height, $limit) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return (float)$result->fetch_object()->average;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch our average rewards for the past N blocks
|
||||
* @param limit int Maximum blocks to check
|
||||
* @return data float Float value of average shares
|
||||
**/
|
||||
public function getAvgBlockReward($limit=1) {
|
||||
$stmt = $this->mysqli->prepare("SELECT AVG(x.amount) AS average FROM (SELECT amount FROM $this->table ORDER BY height DESC LIMIT ?) AS x");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $limit) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return (float)$result->fetch_object()->average;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -76,15 +133,10 @@ class Block {
|
||||
* @param confirmations int Required confirmations to consider block confirmed
|
||||
* @return data array Array with database fields as keys
|
||||
**/
|
||||
public function getAllUnconfirmed($confirmations='120') {
|
||||
$stmt = $this->mysqli->prepare("SELECT id, blockhash, confirmations FROM $this->table WHERE confirmations < ? AND confirmations > -1");
|
||||
if ($this->checkStmt($stmt)) {
|
||||
$stmt->bind_param("i", $confirmations);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$stmt->close();
|
||||
public function getAllUnconfirmed($confirmations=120) {
|
||||
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE confirmations < ? AND confirmations > -1");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param("i", $confirmations) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $result->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -228,4 +280,4 @@ class Block {
|
||||
}
|
||||
|
||||
// Automatically load our class for furhter usage
|
||||
$block = new Block($debug, $mysqli, SALT);
|
||||
$block = new Block($debug, $mysqli, $config);
|
||||
|
||||
146
public/include/classes/invitation.class.php
Normal file
146
public/include/classes/invitation.class.php
Normal file
@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
class Invitation extends Base {
|
||||
var $table = 'invitations';
|
||||
|
||||
/**
|
||||
* Fetch invitations for one account
|
||||
* @param account_id int Account ID
|
||||
* @return mixed Array on success, bool on failure
|
||||
**/
|
||||
public function getInvitations($account_id) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE account_id = ?");
|
||||
if ($stmt && $stmt->bind_param('i', $account_id) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $result->fetch_all(MYSQLI_ASSOC);
|
||||
$this->setErrorMessage('Unable to fetch invitiations send from your account');
|
||||
$this->debug->append('Failed to fetch invitations from database: ' . $this->mysqli->errro);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count invitations sent by an account_id
|
||||
* @param account_id integer Account ID
|
||||
* @return mixes Integer on success, boolean on failure
|
||||
**/
|
||||
public function getCountInvitations($account_id) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$stmt = $this->mysqli->prepare("SELECT count(id) AS total FROM $this->table WHERE account_id = ?");
|
||||
if ($stmt && $stmt->bind_param('i', $account_id) && $stmt->execute() && $stmt->bind_result($total) && $stmt->fetch())
|
||||
return $total;
|
||||
$this->setErrorMessage('Unable to fetch invitiations send from your account');
|
||||
$this->debug->append('Failed to fetch invitations from database: ' . $this->mysqli->errro);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific invitation by email address
|
||||
* Used to ensure no invitation was already sent
|
||||
* @param strEmail string Email address to check for
|
||||
* @return bool boolean true of ralse
|
||||
**/
|
||||
public function getByEmail($strEmail) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
return $this->getSingle($strEmail, 'id', 'email', 's');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific token by token ID
|
||||
* Used to match an invitation against a token
|
||||
* @param token_id integer Token ID stored in invitation
|
||||
* @return data mixed Invitation ID on success, false on error
|
||||
**/
|
||||
public function getByTokenId($token_id) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
return $this->getSingle($token_id, 'id', 'token_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an invitation as activated by the invitee
|
||||
* @param token_id integer Token to activate
|
||||
* @return bool boolean true or false
|
||||
**/
|
||||
public function setActivated($token_id) {
|
||||
if (!$iInvitationId = $this->getByTokenId($token_id)) {
|
||||
$this->setErrorMessage('Unable to convert token ID to invitation ID');
|
||||
return false;
|
||||
}
|
||||
$field = array('name' => 'is_activated', 'type' => 'i', 'value' => 1);
|
||||
return $this->updateSingle($iInvitationId, $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new invitation to the database
|
||||
* @param account_id integer Account ID to bind the invitation to
|
||||
* @param email string Email address the invite was sent to
|
||||
* @param token_id integer Token ID used during invitation
|
||||
* @return bool boolean True of false
|
||||
**/
|
||||
public function createInvitation($account_id, $email, $token_id) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$stmt = $this->mysqli->prepare("INSERT INTO $this->table ( account_id, email, token_id ) VALUES ( ?, ?, ?)");
|
||||
if ($stmt && $stmt->bind_param('isi', $account_id, $email, $token_id) && $stmt->execute())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Send an invitation out to a user
|
||||
* Uses the mail class to send mails
|
||||
* @param account_id integer Sending account ID
|
||||
* @param aData array Data array including mail information
|
||||
* @return bool boolean True or false
|
||||
**/
|
||||
public function sendInvitation($account_id, $aData) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
// Check data input
|
||||
if (empty($aData['email']) || !filter_var($aData['email'], FILTER_VALIDATE_EMAIL)) {
|
||||
$this->setErrorMessage( 'Invalid e-mail address' );
|
||||
return false;
|
||||
}
|
||||
if (preg_match('/[^a-z_\.\!\?\-0-9 ]/i', $aData['message'])) {
|
||||
$this->setErrorMessage('Message may only contain alphanumeric characters');
|
||||
return false;
|
||||
}
|
||||
// Ensure this invitation does not exist yet nor do we have an account with that email
|
||||
if ($this->user->getEmail($aData['email'])) {
|
||||
$this->setErrorMessage('This email is already registered as an account');
|
||||
return false;
|
||||
}
|
||||
if ($this->getByEmail($aData['email'])) {
|
||||
$this->setErrorMessage('A pending invitation for this address already exists');
|
||||
return false;
|
||||
}
|
||||
if (!$aData['token'] = $this->token->createToken('invitation', $account_id)) {
|
||||
$this->setErrorMessage('Unable to generate invitation token: ' . $this->token->getError());
|
||||
return false;
|
||||
}
|
||||
$aData['username'] = $this->user->getUserName($account_id);
|
||||
$aData['subject'] = 'Pending Invitation';
|
||||
if ($this->mail->sendMail('invitations/body', $aData)) {
|
||||
$aToken = $this->token->getToken($aData['token']);
|
||||
if (!$this->createInvitation($account_id, $aData['email'], $aToken['id'])) {
|
||||
$this->setErrorMessage('Unable to create invitation record');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
$this->setErrorMessage('Unable to send email to recipient');
|
||||
}
|
||||
$this->setErrorMessage('Unable to send invitation');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Instantiate class
|
||||
$invitation = new invitation();
|
||||
$invitation->setDebug($debug);
|
||||
$invitation->setMysql($mysqli);
|
||||
$invitation->setMail($mail);
|
||||
$invitation->setUser($user);
|
||||
$invitation->setToken($oToken);
|
||||
$invitation->setConfig($config);
|
||||
|
||||
?>
|
||||
@ -4,30 +4,7 @@
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
|
||||
class Mail {
|
||||
private $sError = '';
|
||||
|
||||
public function setDebug($debug) {
|
||||
$this->debug = $debug;
|
||||
}
|
||||
public function setMysql($mysqli) {
|
||||
$this->mysqli = $mysqli;
|
||||
}
|
||||
public function setSmarty($smarty) {
|
||||
$this->smarty = $smarty;
|
||||
}
|
||||
public function setUser($user) {
|
||||
$this->user = $user;
|
||||
}
|
||||
public function setConfig($config) {
|
||||
$this->config = $config;
|
||||
}
|
||||
public function setErrorMessage($msg) {
|
||||
$this->sError = $msg;
|
||||
}
|
||||
public function getError() {
|
||||
return $this->sError;
|
||||
}
|
||||
class Mail extends Base {
|
||||
function checkStmt($bState) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
if ($bState ===! true) {
|
||||
@ -38,22 +15,59 @@ class Mail {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mail form contact site admin
|
||||
* @param senderName string senderName
|
||||
* @param senderEmail string senderEmail
|
||||
* @param senderSubject string senderSubject
|
||||
* @param senderMessage string senderMessage
|
||||
* @param email string config Email address
|
||||
* @param subject string header subject
|
||||
* @return bool
|
||||
**/
|
||||
public function contactform($senderName, $senderEmail, $senderSubject, $senderMessage) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
if (preg_match('/[^a-z_\.\!\?\-0-9\\s ]/i', $senderName)) {
|
||||
$this->setErrorMessage('Username may only contain alphanumeric characters');
|
||||
return false;
|
||||
}
|
||||
if (empty($senderEmail) || !filter_var($senderEmail, FILTER_VALIDATE_EMAIL)) {
|
||||
$this->setErrorMessage( 'Invalid e-mail address' );
|
||||
return false;
|
||||
}
|
||||
if (preg_match('/[^a-z_\.\!\?\-0-9\\s ]/i', $senderSubject)) {
|
||||
$this->setErrorMessage('Subject may only contain alphanumeric characters');
|
||||
return false;
|
||||
}
|
||||
if (strlen(strip_tags($senderMessage)) < strlen($senderMessage)) {
|
||||
$this->setErrorMessage('Your message may only contain alphanumeric characters');
|
||||
return false;
|
||||
}
|
||||
$aData['senderName'] = $senderName;
|
||||
$aData['senderEmail'] = $senderEmail;
|
||||
$aData['senderSubject'] = $senderSubject;
|
||||
$aData['senderMessage'] = $senderMessage;
|
||||
$aData['email'] = $this->setting->getValue('website_email');
|
||||
$aData['subject'] = 'Contact From';
|
||||
if ($this->sendMail('contactform/body', $aData)) {
|
||||
return true;
|
||||
} else {
|
||||
$this->setErrorMessage( 'Unable to send email' );
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function sendMail($template, $aData) {
|
||||
$this->smarty->assign('WEBSITENAME', $this->config['website']['name']);
|
||||
$this->smarty->assign('WEBSITENAME', $this->setting->getValue('website_name'));
|
||||
$this->smarty->assign('SUBJECT', $aData['subject']);
|
||||
$this->smarty->assign('DATA', $aData);
|
||||
$headers = 'From: Website Administration <' . $this->config['website']['email'] . ">\n";
|
||||
$headers = 'From: Website Administration <' . $this->setting->getValue('website_email') . ">\n";
|
||||
$headers .= "MIME-Version: 1.0\n";
|
||||
$headers .= "Content-Type: text/html; charset=ISO-8859-1\r\n";
|
||||
if (mail($aData['email'],
|
||||
$this->smarty->fetch(BASEPATH . 'templates/mail/subject.tpl'),
|
||||
$this->smarty->fetch(BASEPATH . 'templates/mail/' . $template . '.tpl'),
|
||||
$headers)) {
|
||||
return true;
|
||||
} else {
|
||||
$this->setErrorMessage("Unable to send mail");
|
||||
return false;
|
||||
}
|
||||
if (mail($aData['email'], $this->smarty->fetch(BASEPATH . 'templates/mail/subject.tpl'), $this->smarty->fetch(BASEPATH . 'templates/mail/' . $template . '.tpl'), $headers))
|
||||
return true;
|
||||
$this->setErrorMessage('Unable to send mail');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -64,4 +78,5 @@ $mail->setDebug($debug);
|
||||
$mail->setMysql($mysqli);
|
||||
$mail->setSmarty($smarty);
|
||||
$mail->setConfig($config);
|
||||
$mail->setSetting($setting);
|
||||
?>
|
||||
|
||||
49
public/include/classes/monitoring.class.php
Normal file
49
public/include/classes/monitoring.class.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
|
||||
class Monitoring {
|
||||
public function __construct($debug, $mysqli) {
|
||||
$this->debug = $debug;
|
||||
$this->mysqli = $mysqli;
|
||||
$this->table = 'monitoring';
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a value from our table
|
||||
* @param name string Setting name
|
||||
* @return value string Value
|
||||
**/
|
||||
public function getStatus($name) {
|
||||
$query = $this->mysqli->prepare("SELECT * FROM $this->table WHERE name = ? LIMIT 1");
|
||||
if ($query && $query->bind_param('s', $name) && $query->execute() && $result = $query->get_result()) {
|
||||
return $result->fetch_assoc();
|
||||
} else {
|
||||
$this->debug->append("Failed to fetch variable $name from $this->table");
|
||||
return false;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert or update a setting
|
||||
* @param name string Name of the variable
|
||||
* @param value string Variable value
|
||||
* @return bool
|
||||
**/
|
||||
public function setStatus($name, $type, $value) {
|
||||
$stmt = $this->mysqli->prepare("
|
||||
INSERT INTO $this->table (name, type, value)
|
||||
VALUES (?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE value = ?
|
||||
");
|
||||
if ($stmt && $stmt->bind_param('ssss', $name, $type, $value, $value) && $stmt->execute())
|
||||
return true;
|
||||
$this->debug->append("Failed to set $name to $value");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$monitoring = new Monitoring($debug, $mysqli);
|
||||
102
public/include/classes/news.class.php
Normal file
102
public/include/classes/news.class.php
Normal file
@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
|
||||
class News extends Base {
|
||||
var $table = 'news';
|
||||
|
||||
public function getActive($id) {
|
||||
$this->debug->append("STA " . __METHOD__, 5);
|
||||
return $this->getSingle($id, 'active', 'id');
|
||||
}
|
||||
|
||||
public function toggleActive($id) {
|
||||
$this->debug->append("STA " . __METHOD__, 5);
|
||||
$field = array('name' => 'active', 'type' => 'i', 'value' => !$this->getActive($id));
|
||||
return $this->updateSingle($id, $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all active news
|
||||
**/
|
||||
public function getAllActive() {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$stmt = $this->mysqli->prepare("SELECT n.*, a.username AS author FROM $this->table AS n LEFT JOIN " . $this->user->getTableName() . " AS a ON a.id = n.account_id WHERE active = 1 ORDER BY time DESC");
|
||||
if ($stmt && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $result->fetch_all(MYSQLI_ASSOC);
|
||||
// Catchall
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all news
|
||||
**/
|
||||
public function getAll() {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$stmt = $this->mysqli->prepare("SELECT n.*, a.username AS author FROM $this->table AS n LEFT JOIN " . $this->user->getTableName() . " AS a ON a.id = n.account_id ORDER BY time DESC");
|
||||
if ($stmt && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $result->fetch_all(MYSQLI_ASSOC);
|
||||
// Catchall
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific news entry
|
||||
**/
|
||||
public function getEntry($id) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE id = ?");
|
||||
if ($stmt && $stmt->bind_param('i', $id) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $result->fetch_assoc();
|
||||
// Catchall
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a news entry
|
||||
**/
|
||||
public function updateNews($id, $header, $content, $active=0) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$stmt = $this->mysqli->prepare("UPDATE $this->table SET content = ?, header = ?, active = ? WHERE id = ?");
|
||||
if ($stmt && $stmt->bind_param('ssii', $content, $header, $active, $id) && $stmt->execute() && $stmt->affected_rows == 1)
|
||||
return true;
|
||||
$this->setErrorMessage("Failed to update news entry $id");
|
||||
return false;
|
||||
}
|
||||
|
||||
public function deleteNews($id) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
if (!is_int($id)) return false;
|
||||
$stmt = $this->mysqli->prepare("DELETE FROM $this->table WHERE id = ?");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $id) && $stmt->execute() && $stmt->affected_rows == 1)
|
||||
return true;
|
||||
$this->setErrorMessage("Failed to delete news entry $id");
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new mews entry to the table
|
||||
* @param type string Type of the notification
|
||||
* @return bool
|
||||
**/
|
||||
public function addNews($account_id, $aData, $active=false) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
if (empty($aData['header'])) return false;
|
||||
if (empty($aData['content'])) return false;
|
||||
if (!is_int($account_id)) return false;
|
||||
$stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id, header, content, active) VALUES (?,?,?,?)");
|
||||
if ($stmt && $stmt->bind_param('issi', $account_id, $aData['header'], $aData['content'], $active) && $stmt->execute())
|
||||
return true;
|
||||
$this->debug->append("Failed to add news: " . $this->mysqli->error);
|
||||
$this->setErrorMessage("Unable to add new news: " . $this->mysqli->error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$news = new News();
|
||||
$news->setDebug($debug);
|
||||
$news->setMysql($mysqli);
|
||||
$news->setUser($user);
|
||||
?>
|
||||
@ -17,21 +17,6 @@ class Notification extends Mail {
|
||||
return $this->updateSingle($id, $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a single row in a table
|
||||
* @param userID int Account ID
|
||||
* @param field string Field to update
|
||||
* @return bool
|
||||
**/
|
||||
private function updateSingle($id, $field, $table='') {
|
||||
if (empty($table)) $table = $this->table;
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$stmt = $this->mysqli->prepare("UPDATE $table SET " . $field['name'] . " = ? WHERE id = ? LIMIT 1");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param($field['type'].'i', $field['value'], $id) && $stmt->execute())
|
||||
return true;
|
||||
$this->debug->append("Unable to update " . $field['name'] . " with " . $field['value'] . " for ID $id");
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* We check our notification table for existing data
|
||||
* so we can avoid duplicate entries
|
||||
@ -99,12 +84,16 @@ class Notification extends Mail {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$stmt = $this->mysqli->prepare("SELECT * FROM $this->tableSettings WHERE account_id = ?");
|
||||
if ($stmt && $stmt->bind_param('i', $account_id) && $stmt->execute() && $result = $stmt->get_result()) {
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$aData[$row['type']] = $row['active'];
|
||||
if ($result->num_rows > 0) {
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$aData[$row['type']] = $row['active'];
|
||||
}
|
||||
return $aData;
|
||||
}
|
||||
return $aData;
|
||||
}
|
||||
// Catchall
|
||||
$this->setErrorMessage('Unable to fetch notification settings');
|
||||
$this->debug->append('Failed fetching notification settings for ' . $account_id . ': ' . $this->mysqli->error);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -173,11 +162,17 @@ class Notification extends Mail {
|
||||
// Check if this user wants strType notifications
|
||||
$stmt = $this->mysqli->prepare("SELECT account_id FROM $this->tableSettings WHERE type = ? AND active = 1 AND account_id = ?");
|
||||
if ($stmt && $stmt->bind_param('si', $strType, $account_id) && $stmt->execute() && $stmt->bind_result($id) && $stmt->fetch()) {
|
||||
if ($stmt->close() && $this->sendMail('notifications/' . $strType, $aMailData) && $this->addNotification($account_id, $strType, $aMailData))
|
||||
if ($stmt->close() && $this->sendMail('notifications/' . $strType, $aMailData) && $this->addNotification($account_id, $strType, $aMailData)) {
|
||||
return true;
|
||||
} else {
|
||||
$this->setErrorMessage('SendMail call failed: ' . $this->mail->getError());
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$this->setErrorMessage('User disabled ' . $strType . ' notifications');
|
||||
return false;
|
||||
}
|
||||
$this->setErrorMessage('Error sending mail notification');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -187,5 +182,5 @@ $notification->setDebug($debug);
|
||||
$notification->setMysql($mysqli);
|
||||
$notification->setSmarty($smarty);
|
||||
$notification->setConfig($config);
|
||||
|
||||
$notification->setSetting($setting);
|
||||
?>
|
||||
|
||||
63
public/include/classes/payout.class.php
Normal file
63
public/include/classes/payout.class.php
Normal file
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
class Payout Extends Base {
|
||||
var $table = 'payouts';
|
||||
|
||||
/**
|
||||
* Check if the user has an active payout request already
|
||||
* @param account_id int Account ID
|
||||
* @return boolean bool True of False
|
||||
**/
|
||||
public function isPayoutActive($account_id) {
|
||||
$stmt = $this->mysqli->prepare("SELECT id FROM $this->table WHERE completed = 0 AND account_id = ? LIMIT 1");
|
||||
if ($stmt && $stmt->bind_param('i', $account_id) && $stmt->execute( )&& $stmt->store_result() && $stmt->num_rows > 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all new, unprocessed payout requests
|
||||
* @param none
|
||||
* @return data Associative array with DB Fields
|
||||
**/
|
||||
public function getUnprocessedPayouts() {
|
||||
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE completed = 0");
|
||||
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $result->fetch_all(MYSQLI_ASSOC);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new payout request
|
||||
* @param account_id Account ID
|
||||
* @return data mixed Inserted ID or false
|
||||
**/
|
||||
public function createPayout($account_id=NULL) {
|
||||
$stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id) VALUES (?)");
|
||||
if ($stmt && $stmt->bind_param('i', $account_id) && $stmt->execute()) {
|
||||
return $stmt->insert_id;
|
||||
}
|
||||
$this->setErrorMessage('Unable to create new payout request');
|
||||
$this->debug->append('Failed to create new payout request in database: ' . $this->mysqli->error);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a payout as processed
|
||||
* @param id int Payout ID
|
||||
* @return boolean bool True or False
|
||||
**/
|
||||
public function setProcessed($id) {
|
||||
$stmt = $this->mysqli->prepare("UPDATE $this->table SET completed = 1 WHERE id = ?");
|
||||
if ($stmt && $stmt->bind_param('i', $id) && $stmt->execute())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$oPayout = new Payout();
|
||||
$oPayout->setDebug($debug);
|
||||
$oPayout->setMysql($mysqli);
|
||||
160
public/include/classes/roundstats.class.php
Normal file
160
public/include/classes/roundstats.class.php
Normal file
@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
|
||||
class RoundStats {
|
||||
private $sError = '';
|
||||
private $tableTrans = 'transactions';
|
||||
private $tableStats = 'statistics_shares';
|
||||
private $tableBlocks = 'blocks';
|
||||
private $tableUsers = 'accounts';
|
||||
|
||||
public function __construct($debug, $mysqli, $config) {
|
||||
$this->debug = $debug;
|
||||
$this->mysqli = $mysqli;
|
||||
$this->config = $config;
|
||||
$this->debug->append("Instantiated RoundStats class", 2);
|
||||
}
|
||||
|
||||
// get and set methods
|
||||
private function setErrorMessage($msg) {
|
||||
$this->sError = $msg;
|
||||
}
|
||||
public function getError() {
|
||||
return $this->sError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get next block for round stats
|
||||
**/
|
||||
public function getNextBlock($iHeight=0) {
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT height
|
||||
FROM $this->tableBlocks
|
||||
WHERE height > ?
|
||||
ORDER BY height ASC
|
||||
LIMIT 1");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $iHeight) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $result->fetch_object()->height;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get prev block for round stats
|
||||
**/
|
||||
public function getPreviousBlock($iHeight=0) {
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT height
|
||||
FROM $this->tableBlocks
|
||||
WHERE height < ?
|
||||
ORDER BY height DESC
|
||||
LIMIT 1");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $iHeight) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $result->fetch_object()->height;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get details for block height
|
||||
* @param height int Block Height
|
||||
* @return data array Block information from DB
|
||||
**/
|
||||
public function getDetailsForBlockHeight($iHeight=0, $isAdmin=0) {
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
b.id, height, blockhash, amount, confirmations, difficulty, FROM_UNIXTIME(time) as time, shares,
|
||||
IF(a.is_anonymous, IF( ? , a.username, 'anonymous'), a.username) AS finder
|
||||
FROM $this->tableBlocks as b
|
||||
LEFT JOIN $this->tableUsers AS a ON b.account_id = a.id
|
||||
WHERE b.height = ? LIMIT 1");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $isAdmin, $iHeight) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $result->fetch_assoc();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get shares statistics for round block height
|
||||
* @param height int Block Height
|
||||
* @return data array Block information from DB
|
||||
**/
|
||||
public function getRoundStatsForAccounts($iHeight=0, $isAdmin=0) {
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
IF(a.is_anonymous, IF( ? , a.username, 'anonymous'), a.username) AS username,
|
||||
s.valid,
|
||||
s.invalid
|
||||
FROM $this->tableStats AS s
|
||||
LEFT JOIN $this->tableBlocks AS b ON s.block_id = b.id
|
||||
LEFT JOIN $this->tableUsers AS a ON a.id = s.account_id
|
||||
WHERE b.height = ?
|
||||
GROUP BY username ASC
|
||||
ORDER BY valid DESC
|
||||
");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $isAdmin, $iHeight) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $result->fetch_all(MYSQLI_ASSOC);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all transactions for round block height for admin
|
||||
* @param height int Block Height
|
||||
* @return data array Block round transactions
|
||||
**/
|
||||
public function getAllRoundTransactions($iHeight=0, $admin) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
t.id AS id,
|
||||
IF(a.is_anonymous, IF( ? , a.username, 'anonymous'), a.username) AS username,
|
||||
t.type AS type,
|
||||
t.amount AS amount
|
||||
FROM $this->tableTrans AS t
|
||||
LEFT JOIN $this->tableBlocks AS b ON t.block_id = b.id
|
||||
LEFT JOIN $this->tableUsers AS a ON t.account_id = a.id
|
||||
WHERE b.height = ?
|
||||
ORDER BY id ASC");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $admin, $iHeight) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $result->fetch_all(MYSQLI_ASSOC);
|
||||
$this->debug->append('Unable to fetch transactions');
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get transactions for round block height user id
|
||||
* @param height int Block Height
|
||||
* @param id int user id
|
||||
* @return data array Block round transactions for user id
|
||||
**/
|
||||
public function getUserRoundTransactions($iHeight=0, $id=0) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
t.id AS id,
|
||||
a.username AS username,
|
||||
t.type AS type,
|
||||
t.amount AS amount
|
||||
FROM $this->tableTrans AS t
|
||||
LEFT JOIN $this->tableBlocks AS b ON t.block_id = b.id
|
||||
LEFT JOIN $this->tableUsers AS a ON t.account_id = a.id
|
||||
WHERE b.height = ? AND a.id = ?
|
||||
ORDER BY id ASC");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $iHeight, $id) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $result->fetch_all(MYSQLI_ASSOC);
|
||||
$this->debug->append('Unable to fetch transactions');
|
||||
return false;
|
||||
}
|
||||
|
||||
private function checkStmt($bState) {
|
||||
if ($bState ===! true) {
|
||||
$this->debug->append("Failed to prepare statement: " . $this->mysqli->error);
|
||||
$this->setErrorMessage('Internal application Error');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$roundstats = new RoundStats($debug, $mysqli, $config);
|
||||
@ -5,10 +5,9 @@ if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
|
||||
class Setting {
|
||||
public function __construct($debug, $mysqli, $salt) {
|
||||
public function __construct($debug, $mysqli) {
|
||||
$this->debug = $debug;
|
||||
$this->mysqli = $mysqli;
|
||||
$this->salt = $salt;
|
||||
$this->table = 'settings';
|
||||
}
|
||||
|
||||
@ -47,9 +46,8 @@ class Setting {
|
||||
if ($stmt && $stmt->bind_param('sss', $name, $value, $value) && $stmt->execute())
|
||||
return true;
|
||||
$this->debug->append("Failed to set $name to $value");
|
||||
echo $this->mysqli->error;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$setting = new Setting($debug, $mysqli, SALT);
|
||||
$setting = new Setting($debug, $mysqli);
|
||||
|
||||
@ -11,11 +11,14 @@ class Share {
|
||||
private $oUpstream;
|
||||
private $iLastUpstreamId;
|
||||
// This defines each share
|
||||
public $rem_host, $username, $our_result, $upstream_result, $reason, $solution, $time;
|
||||
public $rem_host, $username, $our_result, $upstream_result, $reason, $solution, $time, $difficulty;
|
||||
|
||||
public function __construct($debug, $mysqli, $salt) {
|
||||
public function __construct($debug, $mysqli, $user, $block, $config) {
|
||||
$this->debug = $debug;
|
||||
$this->mysqli = $mysqli;
|
||||
$this->user = $user;
|
||||
$this->config = $config;
|
||||
$this->block = $block;
|
||||
$this->debug->append("Instantiated Share class", 2);
|
||||
}
|
||||
|
||||
@ -67,10 +70,10 @@ class Share {
|
||||
**/
|
||||
public function getRoundShares($previous_upstream=0, $current_upstream) {
|
||||
$stmt = $this->mysqli->prepare("SELECT
|
||||
count(id) as total
|
||||
ROUND(IFNULL(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)), 0) / POW(2, (" . $this->config['difficulty'] . " - 16)), 8) AS total
|
||||
FROM $this->table
|
||||
WHERE our_result = 'Y'
|
||||
AND id BETWEEN ? AND ?
|
||||
AND id > ? AND id <= ?
|
||||
");
|
||||
if ($this->checkStmt($stmt)) {
|
||||
$stmt->bind_param('ii', $previous_upstream, $current_upstream);
|
||||
@ -86,47 +89,106 @@ class Share {
|
||||
* Fetch all shares grouped by accounts to count share per account
|
||||
* @param previous_upstream int Previous found share accepted by upstream to limit results
|
||||
* @param current_upstream int Current upstream accepted share
|
||||
* @param limit int Limit to this amount of shares for PPLNS
|
||||
* @return data array username, valid and invalid shares from account
|
||||
**/
|
||||
public function getSharesForAccounts($previous_upstream=0, $current_upstream) {
|
||||
$stmt = $this->mysqli->prepare("SELECT
|
||||
a.id,
|
||||
validT.account AS username,
|
||||
sum(validT.valid) as valid,
|
||||
IFNULL(sum(invalidT.invalid),0) as invalid
|
||||
FROM
|
||||
(
|
||||
SELECT DISTINCT
|
||||
SUBSTRING_INDEX( `username` , '.', 1 ) as account,
|
||||
COUNT(id) AS valid
|
||||
FROM $this->table
|
||||
WHERE id BETWEEN ? AND ?
|
||||
AND our_result = 'Y'
|
||||
GROUP BY account
|
||||
) validT
|
||||
LEFT JOIN
|
||||
(
|
||||
SELECT DISTINCT
|
||||
SUBSTRING_INDEX( `username` , '.', 1 ) as account,
|
||||
COUNT(id) AS invalid
|
||||
FROM $this->table
|
||||
WHERE id BETWEEN ? AND ?
|
||||
AND our_result = 'N'
|
||||
GROUP BY account
|
||||
) invalidT
|
||||
ON validT.account = invalidT.account
|
||||
INNER JOIN accounts a ON a.username = validT.account
|
||||
GROUP BY a.username DESC");
|
||||
if ($this->checkStmt($stmt)) {
|
||||
$stmt->bind_param('iiii', $previous_upstream, $current_upstream, $previous_upstream, $current_upstream);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$stmt->close();
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
a.id,
|
||||
SUBSTRING_INDEX( s.username , '.', 1 ) as username,
|
||||
a.no_fees AS no_fees,
|
||||
ROUND(IFNULL(SUM(IF(our_result='Y', IF(s.difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty), 0)), 0) / POW(2, (" . $this->config['difficulty'] . " - 16)), 8) AS valid,
|
||||
ROUND(IFNULL(SUM(IF(our_result='N', IF(s.difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty), 0)), 0) / POW(2, (" . $this->config['difficulty'] . " - 16)), 8) AS invalid
|
||||
FROM $this->table AS s
|
||||
LEFT JOIN " . $this->user->getTableName() . " AS a
|
||||
ON a.username = SUBSTRING_INDEX( s.username , '.', 1 )
|
||||
WHERE s.id > ? AND s.id <= ?
|
||||
GROUP BY username DESC
|
||||
");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $previous_upstream, $current_upstream) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $result->fetch_all(MYSQLI_ASSOC);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the highest available share ID
|
||||
**/
|
||||
function getMaxShareId() {
|
||||
$stmt = $this->mysqli->prepare("SELECT MAX(id) AS id FROM $this->table");
|
||||
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $result->fetch_object()->id;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the highest available share ID from archive
|
||||
**/
|
||||
function getMaxArchiveShareId() {
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT MAX(share_id) AS share_id FROM $this->tableArchive
|
||||
");
|
||||
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $result->fetch_object()->share_id;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* We need a certain amount of valid archived shares
|
||||
* param left int Left/lowest share ID
|
||||
* param right int Right/highest share ID
|
||||
* return array data Returns an array with usernames as keys for easy access
|
||||
**/
|
||||
function getArchiveShares($iCount) {
|
||||
$iMinId = $this->getMinArchiveShareId($iCount);
|
||||
$iMaxId = $this->getMaxArchiveShareId();
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
a.id,
|
||||
SUBSTRING_INDEX( s.username , '.', 1 ) as account,
|
||||
a.no_fees AS no_fees,
|
||||
ROUND(IFNULL(SUM(IF(our_result='Y', IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty), 0)), 0) / POW(2, (" . $this->config['difficulty'] . " - 16)), 8) AS valid,
|
||||
ROUND(IFNULL(SUM(IF(our_result='N', IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty), 0)), 0) / POW(2, (" . $this->config['difficulty'] . " - 16)), 8) AS invalid
|
||||
FROM $this->tableArchive AS s
|
||||
LEFT JOIN " . $this->user->getTableName() . " AS a
|
||||
ON a.username = SUBSTRING_INDEX( s.username , '.', 1 )
|
||||
WHERE s.share_id > ? AND s.share_id <= ?
|
||||
GROUP BY account DESC");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param("ii", $iMinId, $iMaxId) && $stmt->execute() && $result = $stmt->get_result()) {
|
||||
$aData = NULL;
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$aData[$row['account']] = $row;
|
||||
}
|
||||
if (is_array($aData)) return $aData;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* We keep shares only up to a certain point
|
||||
* This can be configured by the user.
|
||||
* @return return bool true or false
|
||||
**/
|
||||
public function purgeArchive() {
|
||||
if ($this->config['payout_system'] == 'pplns') {
|
||||
// Fetch our last block so we can go back configured rounds
|
||||
$aLastBlock = $this->block->getLast();
|
||||
// Fetch the block we need to find the share_id
|
||||
$aBlock = $this->block->getBlock($aLastBlock['height'] - $this->config['archive']['maxrounds']);
|
||||
// Now that we know our block, remove those shares
|
||||
$stmt = $this->mysqli->prepare("DELETE FROM $this->tableArchive WHERE block_id < ? AND time < DATE_SUB(now(), INTERVAL ? MINUTE)");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $aBlock['id'], $this->config['archive']['maxage']) && $stmt->execute())
|
||||
return true;
|
||||
} else {
|
||||
// We are not running pplns, so we just need to keep shares of the past <interval> minutes
|
||||
$stmt = $this->mysqli->prepare("DELETE FROM $this->tableArchive WHERE time < DATE_SUB(now(), INTERVAL ? MINUTE)");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $this->config['archive']['maxage']) && $stmt->execute())
|
||||
return true;
|
||||
}
|
||||
// Catchall
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move accounted shares to archive table, this step is optional
|
||||
* @param previous_upstream int Previous found share accepted by upstream to limit results
|
||||
@ -135,10 +197,11 @@ class Share {
|
||||
* @return bool
|
||||
**/
|
||||
public function moveArchive($current_upstream, $block_id, $previous_upstream=0) {
|
||||
$archive_stmt = $this->mysqli->prepare("INSERT INTO $this->tableArchive (share_id, username, our_result, upstream_result, block_id, time)
|
||||
SELECT id, username, our_result, upstream_result, ?, time
|
||||
FROM $this->table
|
||||
WHERE id BETWEEN ? AND ?");
|
||||
$archive_stmt = $this->mysqli->prepare("
|
||||
INSERT INTO $this->tableArchive (share_id, username, our_result, upstream_result, block_id, time, difficulty)
|
||||
SELECT id, username, our_result, upstream_result, ?, time, IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty) AS difficulty
|
||||
FROM $this->table
|
||||
WHERE id > ? AND id <= ?");
|
||||
if ($this->checkStmt($archive_stmt) && $archive_stmt->bind_param('iii', $block_id, $previous_upstream, $current_upstream) && $archive_stmt->execute()) {
|
||||
$archive_stmt->close();
|
||||
return true;
|
||||
@ -148,7 +211,7 @@ class Share {
|
||||
}
|
||||
|
||||
public function deleteAccountedShares($current_upstream, $previous_upstream=0) {
|
||||
$stmt = $this->mysqli->prepare("DELETE FROM $this->table WHERE id BETWEEN ? AND ?");
|
||||
$stmt = $this->mysqli->prepare("DELETE FROM $this->table WHERE id > ? AND id <= ?");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $previous_upstream, $current_upstream) && $stmt->execute())
|
||||
return true;
|
||||
// Catchall
|
||||
@ -177,16 +240,76 @@ class Share {
|
||||
* @param last int Skips all shares up to last to find new share
|
||||
* @return bool
|
||||
**/
|
||||
public function setUpstream($last=0) {
|
||||
public function setUpstream($aBlock, $last=0) {
|
||||
// Many use stratum, so we create our stratum check first
|
||||
$version = pack("I*", sprintf('%08d', $aBlock['version']));
|
||||
$previousblockhash = pack("H*", swapEndian($aBlock['previousblockhash']));
|
||||
$merkleroot = pack("H*", swapEndian($aBlock['merkleroot']) );
|
||||
$time = pack("I*", $aBlock['time']);
|
||||
$bits = pack("H*", swapEndian($aBlock['bits']));
|
||||
$nonce = pack("I*", $aBlock['nonce']);
|
||||
$header_bin = $version . $previousblockhash . $merkleroot . $time . $bits . $nonce;
|
||||
$header_hex = implode(unpack("H*", $header_bin));
|
||||
|
||||
// Stratum supported blockhash solution entry
|
||||
$stmt = $this->mysqli->prepare("SELECT SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id FROM $this->table WHERE solution = ? LIMIT 1");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('s', $aBlock['hash']) && $stmt->execute() && $result = $stmt->get_result()) {
|
||||
$this->oUpstream = $result->fetch_object();
|
||||
$this->share_type = 'startum_blockhash';
|
||||
if (!empty($this->oUpstream->account) && is_int($this->oUpstream->id))
|
||||
return true;
|
||||
}
|
||||
|
||||
// Stratum scrypt hash check
|
||||
$scrypt_hash = swapEndian(bin2hex(Scrypt::calc($header_bin, $header_bin, 1024, 1, 1, 32)));
|
||||
$stmt = $this->mysqli->prepare("SELECT SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id FROM $this->table WHERE solution = ? LIMIT 1");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('s', $scrypt_hash) && $stmt->execute() && $result = $stmt->get_result()) {
|
||||
$this->oUpstream = $result->fetch_object();
|
||||
$this->share_type = 'startum_solution';
|
||||
if (!empty($this->oUpstream->account) && is_int($this->oUpstream->id))
|
||||
return true;
|
||||
}
|
||||
|
||||
// Failed to fetch via startum solution, try pushpoold
|
||||
// Fallback to pushpoold solution type
|
||||
$ppheader = sprintf('%08d', $aBlock['version']) . word_reverse($aBlock['previousblockhash']) . word_reverse($aBlock['merkleroot']) . dechex($aBlock['time']) . $aBlock['bits'] . dechex($aBlock['nonce']);
|
||||
$stmt = $this->mysqli->prepare("SELECT SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id FROM $this->table WHERE solution LIKE CONCAT(?, '%') LIMIT 1");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('s', $ppheader) && $stmt->execute() && $result = $stmt->get_result()) {
|
||||
$this->oUpstream = $result->fetch_object();
|
||||
$this->share_type = 'pp_solution';
|
||||
if (!empty($this->oUpstream->account) && is_int($this->oUpstream->id))
|
||||
return true;
|
||||
}
|
||||
|
||||
// Still no match, try upstream result with timerange
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id
|
||||
FROM $this->table
|
||||
WHERE upstream_result = 'Y'
|
||||
AND id > ?
|
||||
AND UNIX_TIMESTAMP(time) >= ?
|
||||
AND UNIX_TIMESTAMP(time) <= ( ? + 60 )
|
||||
ORDER BY id ASC LIMIT 1");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $last) && $stmt->execute() && $result = $stmt->get_result()) {
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('iii', $last, $aBlock['time'], $aBlock['time']) && $stmt->execute() && $result = $stmt->get_result()) {
|
||||
$this->oUpstream = $result->fetch_object();
|
||||
$this->share_type = 'upstream_share';
|
||||
if (!empty($this->oUpstream->account) && is_int($this->oUpstream->id))
|
||||
return true;
|
||||
}
|
||||
|
||||
// We failed again, now we take ANY result matching the timestamp
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id
|
||||
FROM $this->table
|
||||
WHERE our_result = 'Y'
|
||||
AND id > ?
|
||||
AND UNIX_TIMESTAMP(time) >= ?
|
||||
ORDER BY id ASC LIMIT 1");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $last, $aBlock['time']) && $stmt->execute() && $result = $stmt->get_result()) {
|
||||
$this->oUpstream = $result->fetch_object();
|
||||
$this->share_type = 'any_share';
|
||||
if (!empty($this->oUpstream->account) && is_int($this->oUpstream->id))
|
||||
return true;
|
||||
}
|
||||
@ -194,6 +317,48 @@ class Share {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the lowest needed share ID from shares
|
||||
**/
|
||||
function getMinimumShareId($iCount, $current_upstream) {
|
||||
// We don't use baseline here to be more accurate
|
||||
$iCount = $iCount * pow(2, ($this->config['difficulty'] - 16));
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT MIN(b.id) AS id FROM
|
||||
(
|
||||
SELECT id, @total := @total + IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty) AS total
|
||||
FROM $this->table, (SELECT @total := 0) AS a
|
||||
WHERE our_result = 'Y'
|
||||
AND id <= ? AND @total < ?
|
||||
ORDER BY id DESC
|
||||
) AS b
|
||||
WHERE total <= ?
|
||||
");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('iii', $current_upstream, $iCount, $iCount) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $result->fetch_object()->id;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the lowest needed share ID from archive
|
||||
**/
|
||||
function getMinArchiveShareId($iCount) {
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT MIN(b.share_id) AS share_id FROM
|
||||
(
|
||||
SELECT share_id, @total := @total + (IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty) / POW(2, (" . $this->config['difficulty'] . " - 16))) AS total
|
||||
FROM $this->tableArchive, (SELECT @total := 0) AS a
|
||||
WHERE our_result = 'Y'
|
||||
AND @total < ?
|
||||
ORDER BY share_id DESC
|
||||
) AS b
|
||||
WHERE total <= ?
|
||||
");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $iCount, $iCount) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $result->fetch_object()->share_id;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function
|
||||
**/
|
||||
@ -207,4 +372,4 @@ class Share {
|
||||
}
|
||||
}
|
||||
|
||||
$share = new Share($debug, $mysqli, SALT);
|
||||
$share = new Share($debug, $mysqli, $user, $block, $config);
|
||||
|
||||
@ -61,7 +61,11 @@ class Statistics {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
if ($data = $this->memcache->get(__FUNCTION__ . $limit)) return $data;
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT b.*, a.username as finder
|
||||
SELECT
|
||||
b.*,
|
||||
a.username AS finder,
|
||||
a.is_anonymous AS is_anonymous,
|
||||
ROUND((difficulty * 65535) / POW(2, (" . $this->config['difficulty'] . " -16)), 0) AS estshares
|
||||
FROM " . $this->block->getTableName() . " AS b
|
||||
LEFT JOIN " . $this->user->getTableName() . " AS a
|
||||
ON b.account_id = a.id
|
||||
@ -95,14 +99,25 @@ class Statistics {
|
||||
* @param none
|
||||
* @return data object Return our hashrateas an object
|
||||
**/
|
||||
public function getCurrentHashrate() {
|
||||
public function getCurrentHashrate($interval=600) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
if ($this->getGetCache() && $data = $this->memcache->get(__FUNCTION__)) return $data;
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT ROUND(COUNT(id) * POW(2, " . $this->config['difficulty'] . ")/600/1000) AS hashrate FROM " . $this->share->getTableName() . " WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE)
|
||||
");
|
||||
SELECT
|
||||
(
|
||||
(
|
||||
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536 / ? / 1000), 0) AS hashrate
|
||||
FROM " . $this->share->getTableName() . "
|
||||
WHERE time > DATE_SUB(now(), INTERVAL ? SECOND)
|
||||
) + (
|
||||
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536 / ? / 1000), 0) AS hashrate
|
||||
FROM " . $this->share->getArchiveTableName() . "
|
||||
WHERE time > DATE_SUB(now(), INTERVAL ? SECOND)
|
||||
)
|
||||
) AS hashrate
|
||||
FROM DUAL");
|
||||
// Catchall
|
||||
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result() ) return $this->memcache->setCache(__FUNCTION__, $result->fetch_object()->hashrate);
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('iiii', $interval, $interval, $interval, $interval) && $stmt->execute() && $result = $stmt->get_result() ) return $this->memcache->setCache(__FUNCTION__, $result->fetch_object()->hashrate);
|
||||
$this->debug->append("Failed to get hashrate: " . $this->mysqli->error);
|
||||
return false;
|
||||
}
|
||||
@ -112,13 +127,24 @@ class Statistics {
|
||||
* @param none
|
||||
* @return data object Our share rate in shares per second
|
||||
**/
|
||||
public function getCurrentShareRate() {
|
||||
public function getCurrentShareRate($interval=600) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
if ($data = $this->memcache->get(__FUNCTION__)) return $data;
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT ROUND(COUNT(id) / 600, 2) AS sharerate FROM " . $this->share->getTableName() . " WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE)
|
||||
");
|
||||
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result() ) return $this->memcache->setCache(__FUNCTION__, $result->fetch_object()->sharerate);
|
||||
SELECT
|
||||
(
|
||||
(
|
||||
SELECT ROUND(COUNT(id) / ?, 2) AS sharerate
|
||||
FROM " . $this->share->getTableName() . "
|
||||
WHERE time > DATE_SUB(now(), INTERVAL ? SECOND)
|
||||
) + (
|
||||
SELECT ROUND(COUNT(id) / ?, 2) AS sharerate
|
||||
FROM " . $this->share->getArchiveTableName() . "
|
||||
WHERE time > DATE_SUB(now(), INTERVAL ? SECOND)
|
||||
)
|
||||
) AS sharerate
|
||||
FROM DUAL");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('iiii', $interval, $interval, $interval, $interval) && $stmt->execute() && $result = $stmt->get_result() ) return $this->memcache->setCache(__FUNCTION__, $result->fetch_object()->sharerate);
|
||||
// Catchall
|
||||
$this->debug->append("Failed to fetch share rate: " . $this->mysqli->error);
|
||||
return false;
|
||||
@ -131,24 +157,83 @@ class Statistics {
|
||||
**/
|
||||
public function getRoundShares() {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
if ($this->getGetCache() && $data = $this->memcache->get(__FUNCTION__)) return $data;
|
||||
// Try the statistics cron cache, then function cache, then fallback to SQL
|
||||
if ($data = $this->memcache->get(STATISTICS_ALL_USER_SHARES)) {
|
||||
$this->debug->append("Found data in statistics cache", 2);
|
||||
$total = array('valid' => 0, 'invalid' => 0);
|
||||
foreach ($data['data'] as $aUser) {
|
||||
$total['valid'] += $aUser['valid'];
|
||||
$total['invalid'] += $aUser['invalid'];
|
||||
}
|
||||
return $total;
|
||||
}
|
||||
if ($data = $this->memcache->get(STATISTICS_ROUND_SHARES)) {
|
||||
$this->debug->append("Found data in local cache", 2);
|
||||
return $data;
|
||||
}
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
( SELECT IFNULL(count(id), 0)
|
||||
ROUND(IFNULL(SUM(IF(our_result='Y', IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty), 0)), 0) / POW(2, (" . $this->config['difficulty'] . " - 16)), 0) AS valid,
|
||||
ROUND(IFNULL(SUM(IF(our_result='N', IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty), 0)), 0) / POW(2, (" . $this->config['difficulty'] . " - 16)), 0) AS invalid
|
||||
FROM " . $this->share->getTableName() . "
|
||||
WHERE UNIX_TIMESTAMP(time) >IFNULL((SELECT MAX(time) FROM blocks),0)
|
||||
AND our_result = 'Y' ) as valid,
|
||||
( SELECT IFNULL(count(id), 0)
|
||||
FROM " . $this->share->getTableName() . "
|
||||
WHERE UNIX_TIMESTAMP(time) >IFNULL((SELECT MAX(time) FROM blocks),0)
|
||||
AND our_result = 'N' ) as invalid");
|
||||
WHERE UNIX_TIMESTAMP(time) > IFNULL((SELECT MAX(time) FROM " . $this->block->getTableName() . "), 0)");
|
||||
if ( $this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result() )
|
||||
return $this->memcache->setCache(__FUNCTION__, $result->fetch_assoc());
|
||||
return $this->memcache->setCache(STATISTICS_ROUND_SHARES, $result->fetch_assoc());
|
||||
// Catchall
|
||||
$this->debug->append("Failed to fetch round shares: " . $this->mysqli->error);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get amount of shares for a all users
|
||||
* Used in statistics cron to refresh memcache data
|
||||
* @param account_id int User ID
|
||||
* @return data array invalid and valid share counts
|
||||
**/
|
||||
public function getAllUserShares() {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
if (! $data = $this->memcache->get(STATISTICS_ALL_USER_SHARES)) {
|
||||
$data['share_id'] = 0;
|
||||
$data['data'] = array();
|
||||
}
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
ROUND(IFNULL(SUM(IF(our_result='Y', IF(s.difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty), 0)), 0) / POW(2, (" . $this->config['difficulty'] . " - 16)), 0) AS valid,
|
||||
ROUND(IFNULL(SUM(IF(our_result='N', IF(s.difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty), 0)), 0) / POW(2, (" . $this->config['difficulty'] . " - 16)), 0) AS invalid,
|
||||
u.id AS id,
|
||||
u.donate_percent AS donate_percent,
|
||||
u.is_anonymous AS is_anonymous,
|
||||
u.username AS username
|
||||
FROM " . $this->share->getTableName() . " AS s,
|
||||
" . $this->user->getTableName() . " AS u
|
||||
WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 )
|
||||
AND UNIX_TIMESTAMP(s.time) > IFNULL(
|
||||
(
|
||||
SELECT MAX(b.time)
|
||||
FROM " . $this->block->getTableName() . " AS b
|
||||
) ,0 )
|
||||
AND s.id > ?
|
||||
GROUP BY u.id");
|
||||
if ($stmt && $stmt->bind_param('i', $data['share_id']) && $stmt->execute() && $result = $stmt->get_result()) {
|
||||
$data_new = array();
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
if (! array_key_exists($row['id'], $data['data'])) {
|
||||
$data['data'][$row['id']] = $row;
|
||||
} else {
|
||||
$data['data'][$row['id']]['valid'] += $row['valid'];
|
||||
$data['data'][$row['id']]['invalid'] += $row['invalid'];
|
||||
$data['data'][$row['id']]['donate_percent'] = $row['donate_percent'];
|
||||
$data['data'][$row['id']]['is_anonymous'] = $row['is_anonymous'];
|
||||
}
|
||||
}
|
||||
$data['share_id'] = $this->share->getMaxShareId();
|
||||
return $this->memcache->setCache(STATISTICS_ALL_USER_SHARES, $data);
|
||||
}
|
||||
// Catchall
|
||||
$this->debug->append("Unable to fetch all users round shares: " . $this->mysqli->error);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get amount of shares for a specific user
|
||||
* @param account_id int User ID
|
||||
@ -156,28 +241,20 @@ class Statistics {
|
||||
**/
|
||||
public function getUserShares($account_id) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
if ($this->getGetCache() && $data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data;
|
||||
// Dual-caching, try statistics cron first, then fallback to local, then fallbock to SQL
|
||||
if ($data = $this->memcache->get(STATISTICS_ALL_USER_SHARES)) return @$data['data'][$account_id];
|
||||
if ($data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data;
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
(
|
||||
SELECT COUNT(s.id)
|
||||
FROM " . $this->share->getTableName() . " AS s,
|
||||
" . $this->user->getTableName() . " AS u
|
||||
WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 )
|
||||
AND UNIX_TIMESTAMP(s.time) >IFNULL((SELECT MAX(b.time) FROM " . $this->block->getTableName() . " AS b),0)
|
||||
AND our_result = 'Y'
|
||||
AND u.id = ?
|
||||
) AS valid,
|
||||
(
|
||||
SELECT COUNT(s.id)
|
||||
FROM " . $this->share->getTableName() . " AS s,
|
||||
" . $this->user->getTableName() . " AS u
|
||||
WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 )
|
||||
AND UNIX_TIMESTAMP(s.time) >IFNULL((SELECT MAX(b.time) FROM " . $this->block->getTableName() . " AS b),0)
|
||||
AND our_result = 'N'
|
||||
AND u.id = ?
|
||||
) AS invalid");
|
||||
if ($stmt && $stmt->bind_param("ii", $account_id, $account_id) && $stmt->execute() && $result = $stmt->get_result())
|
||||
ROUND(IFNULL(SUM(IF(our_result='Y', IF(s.difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty), 0)), 0) / POW(2, (" . $this->config['difficulty'] . " - 16)), 0) AS valid,
|
||||
ROUND(IFNULL(SUM(IF(our_result='N', IF(s.difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty), 0)), 0) / POW(2, (" . $this->config['difficulty'] . " - 16)), 0) AS invalid
|
||||
FROM " . $this->share->getTableName() . " AS s,
|
||||
" . $this->user->getTableName() . " AS u
|
||||
WHERE
|
||||
u.username = SUBSTRING_INDEX( s.username, '.', 1 )
|
||||
AND UNIX_TIMESTAMP(s.time) >IFNULL((SELECT MAX(b.time) FROM " . $this->block->getTableName() . " AS b),0)
|
||||
AND u.id = ?");
|
||||
if ($stmt && $stmt->bind_param("i", $account_id) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $this->memcache->setCache(__FUNCTION__ . $account_id, $result->fetch_assoc());
|
||||
// Catchall
|
||||
$this->debug->append("Unable to fetch user round shares: " . $this->mysqli->error);
|
||||
@ -196,18 +273,18 @@ class Statistics {
|
||||
a.id AS id,
|
||||
a.is_admin as is_admin,
|
||||
a.is_locked as is_locked,
|
||||
a.no_fees as no_fees,
|
||||
a.username AS username,
|
||||
a.donate_percent AS donate_percent,
|
||||
a.email AS email,
|
||||
COUNT(s.id) AS shares
|
||||
ROUND(IFNULL(SUM(IF(s.difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)), 0) / POW(2, (" . $this->config['difficulty'] . " - 16)), 0) AS shares
|
||||
FROM " . $this->user->getTableName() . " AS a
|
||||
LEFT JOIN " . $this->share->getTableName() . " AS s
|
||||
ON a.username = SUBSTRING_INDEX( s.username, '.', 1 )
|
||||
WHERE
|
||||
a.username LIKE ?
|
||||
GROUP BY username
|
||||
ORDER BY username
|
||||
");
|
||||
ORDER BY username");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('s', $filter) && $stmt->execute() && $result = $stmt->get_result()) {
|
||||
return $this->memcache->setCache(__FUNCTION__ . $filter, $result->fetch_all(MYSQLI_ASSOC));
|
||||
}
|
||||
@ -218,17 +295,28 @@ class Statistics {
|
||||
* @param account_id integer User ID
|
||||
* @return data integer Current Hashrate in khash/s
|
||||
**/
|
||||
public function getUserHashrate($account_id) {
|
||||
public function getUserHashrate($account_id, $interval=600) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
if ($data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data;
|
||||
if ($this->getGetCache() && $data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data;
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT ROUND(COUNT(s.id) * POW(2, " . $this->config['difficulty'] . ")/600/1000) AS hashrate
|
||||
FROM " . $this->share->getTableName() . " AS s,
|
||||
" . $this->user->getTableName() . " AS u
|
||||
WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 )
|
||||
AND s.time > DATE_SUB(now(), INTERVAL 10 MINUTE)
|
||||
AND u.id = ?");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param("i", $account_id) && $stmt->execute() && $result = $stmt->get_result() )
|
||||
SELECT
|
||||
(
|
||||
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536 / ? / 1000), 0) AS hashrate
|
||||
FROM " . $this->share->getTableName() . " AS s,
|
||||
" . $this->user->getTableName() . " AS u
|
||||
WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 )
|
||||
AND s.time > DATE_SUB(now(), INTERVAL ? SECOND)
|
||||
AND u.id = ?
|
||||
) + (
|
||||
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536 / ? / 1000), 0) AS hashrate
|
||||
FROM " . $this->share->getArchiveTableName() . " AS s,
|
||||
" . $this->user->getTableName() . " AS u
|
||||
WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 )
|
||||
AND s.time > DATE_SUB(now(), INTERVAL ? SECOND)
|
||||
AND u.id = ?
|
||||
) AS hashrate
|
||||
FROM DUAL");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param("iiiiii", $interval, $interval, $account_id, $interval, $interval, $account_id) && $stmt->execute() && $result = $stmt->get_result() )
|
||||
return $this->memcache->setCache(__FUNCTION__ . $account_id, $result->fetch_object()->hashrate);
|
||||
// Catchall
|
||||
$this->debug->append("Failed to fetch hashrate: " . $this->mysqli->error);
|
||||
@ -240,17 +328,30 @@ class Statistics {
|
||||
* @param account_id integer User ID
|
||||
* @return data integer Current Sharerate in shares/s
|
||||
**/
|
||||
public function getUserSharerate($account_id) {
|
||||
public function getUserSharerate($account_id, $interval=600) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
if ($data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data;
|
||||
if ($this->getGetCache() && $data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data;
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT COUNT(s.id)/600 AS sharerate
|
||||
FROM " . $this->share->getTableName() . " AS s,
|
||||
" . $this->user->getTableName() . " AS u
|
||||
WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 )
|
||||
AND s.time > DATE_SUB(now(), INTERVAL 10 MINUTE)
|
||||
AND u.id = ?");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param("i", $account_id) && $stmt->execute() && $result = $stmt->get_result() )
|
||||
SELECT
|
||||
(
|
||||
(
|
||||
SELECT COUNT(s.id) / ? AS sharerate
|
||||
FROM " . $this->share->getTableName() . " AS s,
|
||||
" . $this->user->getTableName() . " AS u
|
||||
WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 )
|
||||
AND s.time > DATE_SUB(now(), INTERVAL ? SECOND)
|
||||
AND u.id = ?
|
||||
) + (
|
||||
SELECT COUNT(s.id) / ? AS sharerate
|
||||
FROM " . $this->share->getArchiveTableName() . " AS s,
|
||||
" . $this->user->getTableName() . " AS u
|
||||
WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 )
|
||||
AND s.time > DATE_SUB(now(), INTERVAL ? SECOND)
|
||||
AND u.id = ?
|
||||
)
|
||||
) AS sharerate
|
||||
FROM DUAL");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param("iiiiii", $interval, $interval, $account_id, $interval, $interval, $account_id) && $stmt->execute() && $result = $stmt->get_result() )
|
||||
return $this->memcache->setCache(__FUNCTION__ . $account_id, $result->fetch_object()->sharerate);
|
||||
// Catchall
|
||||
$this->debug->append("Failed to fetch sharerate: " . $this->mysqli->error);
|
||||
@ -266,11 +367,11 @@ class Statistics {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
if ($data = $this->memcache->get(__FUNCTION__ . $worker_id)) return $data;
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT ROUND(COUNT(s.id) * POW(2,21)/600/1000) AS hashrate
|
||||
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536 / 600 / 1000), 0) AS hashrate
|
||||
FROM " . $this->share->getTableName() . " AS s,
|
||||
" . $this->user->getTableName() . " AS u
|
||||
WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 )
|
||||
AND s.time > DATE_SUB(now(), INTERVAL 10 MINUTE)
|
||||
AND s.time > DATE_SUB(now(), INTERVAL 600 SECOND)
|
||||
AND u.id = ?");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param("i", $account_id) && $stmt->execute() && $result = $stmt->get_result() )
|
||||
return $this->memcache->setCache(__FUNCTION__ . $worker_id, $result->fetch_object()->hashrate);
|
||||
@ -290,11 +391,32 @@ class Statistics {
|
||||
if ($this->getGetCache() && $data = $this->memcache->get(__FUNCTION__ . $type . $limit)) return $data;
|
||||
switch ($type) {
|
||||
case 'shares':
|
||||
if ($data = $this->memcache->get(STATISTICS_ALL_USER_SHARES)) {
|
||||
// Use global cache to build data
|
||||
$max = 0;
|
||||
foreach($data['data'] as $key => $aUser) {
|
||||
$shares[$key] = $aUser['valid'];
|
||||
$username[$key] = $aUser['username'];
|
||||
}
|
||||
array_multisort($shares, SORT_DESC, $username, SORT_ASC, $data['data']);
|
||||
foreach ($data['data'] as $key => $aUser) {
|
||||
$data_new[$key]['shares'] = $aUser['valid'];
|
||||
$data_new[$key]['account'] = $aUser['username'];
|
||||
$data_new[$key]['donate_percent'] = $aUser['donate_percent'];
|
||||
$data_new[$key]['is_anonymous'] = $aUser['is_anonymous'];
|
||||
}
|
||||
return $data_new;
|
||||
}
|
||||
// No cached data, fallback to SQL and cache in local cache
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
COUNT(id) AS shares,
|
||||
SUBSTRING_INDEX( username, '.', 1 ) AS account
|
||||
FROM " . $this->share->getTableName() . "
|
||||
a.donate_percent AS donate_percent,
|
||||
a.is_anonymous AS is_anonymous,
|
||||
ROUND(IFNULL(SUM(IF(s.difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)), 0) / POW(2, (" . $this->config['difficulty'] . " - 16)), 0) AS shares,
|
||||
SUBSTRING_INDEX( s.username, '.', 1 ) AS account
|
||||
FROM " . $this->share->getTableName() . " AS s
|
||||
LEFT JOIN " . $this->user->getTableName() . " AS a
|
||||
ON SUBSTRING_INDEX( s.username, '.', 1 ) = a.username
|
||||
WHERE our_result = 'Y'
|
||||
GROUP BY account
|
||||
ORDER BY shares DESC
|
||||
@ -307,17 +429,24 @@ class Statistics {
|
||||
|
||||
case 'hashes':
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
ROUND(COUNT(id) * POW(2," . $this->config['difficulty'] . ")/600/1000,2) AS hashrate,
|
||||
SUBSTRING_INDEX( username, '.', 1 ) AS account
|
||||
FROM " . $this->share->getTableName() . "
|
||||
WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE)
|
||||
AND our_result = 'Y'
|
||||
SELECT
|
||||
a.donate_percent AS donate_percent,
|
||||
a.is_anonymous AS is_anonymous,
|
||||
IFNULL(ROUND(SUM(t1.difficulty) * 65536/600/1000, 2), 0) AS hashrate,
|
||||
SUBSTRING_INDEX( t1.username, '.', 1 ) AS account
|
||||
FROM
|
||||
(
|
||||
SELECT IFNULL(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty), 0) AS difficulty, username FROM " . $this->share->getTableName() . " WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE) AND our_result = 'Y'
|
||||
UNION ALL
|
||||
SELECT IFNULL(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty), 0) AS difficulty, username FROM " . $this->share->getArchiveTableName() ." WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE) AND our_result = 'Y'
|
||||
) AS t1
|
||||
LEFT JOIN " . $this->user->getTableName() . " AS a
|
||||
ON SUBSTRING_INDEX( t1.username, '.', 1 ) = a.username
|
||||
GROUP BY account
|
||||
ORDER BY hashrate DESC LIMIT ?");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param("i", $limit) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $this->memcache->setCache(__FUNCTION__ . $type . $limit, $result->fetch_all(MYSQLI_ASSOC));
|
||||
$this->debug->append("Fetching shares failed: ");
|
||||
$this->debug->append("Fetching shares failed: " . $this->mysqli->error);
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
@ -333,7 +462,7 @@ class Statistics {
|
||||
if ($data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data;
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
ROUND(COUNT(s.id) * POW(2, " . $this->config['difficulty'] . ") / 3600 / 1000) AS hashrate,
|
||||
IFNULL(ROUND(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)) * 65536/3600/1000), 0) AS hashrate,
|
||||
HOUR(s.time) AS hour
|
||||
FROM " . $this->share->getTableName() . " AS s, accounts AS a
|
||||
WHERE time < NOW() - INTERVAL 1 HOUR
|
||||
@ -341,19 +470,29 @@ class Statistics {
|
||||
AND a.username = SUBSTRING_INDEX( s.username, '.', 1 )
|
||||
AND a.id = ?
|
||||
GROUP BY HOUR(time)
|
||||
");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param("i", $account_id) && $stmt->execute() && $result = $stmt->get_result()) {
|
||||
$aData = array();
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$aData[$row['hour']] = $row['hashrate'];
|
||||
}
|
||||
UNION ALL
|
||||
SELECT
|
||||
IFNULL(ROUND(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)) * 65536/3600/1000), 0) AS hashrate,
|
||||
HOUR(s.time) AS hour
|
||||
FROM " . $this->share->getArchiveTableName() . " AS s, accounts AS a
|
||||
WHERE time < NOW() - INTERVAL 1 HOUR
|
||||
AND time > NOW() - INTERVAL 25 HOUR
|
||||
AND a.username = SUBSTRING_INDEX( s.username, '.', 1 )
|
||||
AND a.id = ?
|
||||
GROUP BY HOUR(time)");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $account_id, $account_id) && $stmt->execute() && $result = $stmt->get_result()) {
|
||||
$iStartHour = date('G');
|
||||
// Initilize array
|
||||
for ($i = 0; $i < 24; $i++) $aData[($iStartHour + $i) % 24] = 0;
|
||||
// Fill data
|
||||
while ($row = $result->fetch_assoc()) $aData[$row['hour']] = $row['hashrate'];
|
||||
return $this->memcache->setCache(__FUNCTION__ . $account_id, $aData);
|
||||
}
|
||||
// Catchall
|
||||
$this->debug->append("Failed to fetch hourly hashrate: " . $this->mysqli->error);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* get Hourly hashrate for the pool
|
||||
* @param none
|
||||
@ -364,18 +503,27 @@ class Statistics {
|
||||
if ($this->getGetCache() && $data = $this->memcache->get(__FUNCTION__)) return $data;
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
ROUND(COUNT(s.id) * POW(2, " . $this->config['difficulty'] . ") / 3600 / 1000) AS hashrate,
|
||||
IFNULL(ROUND(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)) * 65536/3600/1000), 0) AS hashrate,
|
||||
HOUR(s.time) AS hour
|
||||
FROM " . $this->share->getTableName() . " AS s
|
||||
WHERE time < NOW() - INTERVAL 1 HOUR
|
||||
AND time > NOW() - INTERVAL 25 HOUR
|
||||
GROUP BY HOUR(time)
|
||||
");
|
||||
UNION ALL
|
||||
SELECT
|
||||
IFNULL(ROUND(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)) * 65536/3600/1000), 0) AS hashrate,
|
||||
HOUR(s.time) AS hour
|
||||
FROM " . $this->share->getArchiveTableName() . " AS s
|
||||
WHERE time < NOW() - INTERVAL 1 HOUR
|
||||
AND time > NOW() - INTERVAL 25 HOUR
|
||||
GROUP BY HOUR(time)");
|
||||
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) {
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$aData[$row['hour']] = $row['hashrate'];
|
||||
}
|
||||
return $this->memcache->setCache(__FUNCTION__, @$aData);
|
||||
$iStartHour = date('G');
|
||||
// Initilize array
|
||||
for ($i = 0; $i < 24; $i++) $aData[($iStartHour + $i) % 24] = 0;
|
||||
// Fill data
|
||||
while ($row = $result->fetch_assoc()) $aData[$row['hour']] = (int) $row['hashrate'];
|
||||
return $this->memcache->setCache(__FUNCTION__, $aData);
|
||||
}
|
||||
// Catchall
|
||||
$this->debug->append("Failed to fetch hourly hashrate: " . $this->mysqli->error);
|
||||
|
||||
@ -9,12 +9,24 @@ if (!defined('SECURITY'))
|
||||
* Can be enabled or disabled through site configuration
|
||||
* Also sets a default time if no time is passed to it to enforce caching
|
||||
**/
|
||||
class StatsCache extends Memcached {
|
||||
class StatsCache {
|
||||
private $cache, $round;
|
||||
|
||||
public function __construct($config, $debug) {
|
||||
$this->config = $config;
|
||||
$this->debug = $debug;
|
||||
if (! $config['memcache']['enabled'] ) $this->debug->append("Not storing any values in memcache");
|
||||
return parent::__construct();
|
||||
if (! $config['memcache']['enabled'] ) {
|
||||
$this->debug->append("Not storing any values in memcache");
|
||||
} else {
|
||||
$this->cache = new Memcached();
|
||||
}
|
||||
}
|
||||
|
||||
public function setRound($round_id) {
|
||||
$this->round = $round_id;
|
||||
}
|
||||
public function getRound() {
|
||||
return $this->round;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -25,8 +37,8 @@ class StatsCache extends Memcached {
|
||||
if (! $this->config['memcache']['enabled']) return false;
|
||||
if (empty($expiration))
|
||||
$expiration = $this->config['memcache']['expiration'] + rand( -$this->config['memcache']['splay'], $this->config['memcache']['splay']);
|
||||
$this->debug->append("Storing " . $this->config['memcache']['keyprefix'] . "$key with expiration $expiration", 3);
|
||||
return parent::set($this->config['memcache']['keyprefix'] . $key, $value, $expiration);
|
||||
$this->debug->append("Storing " . $this->getRound() . '_' . $this->config['memcache']['keyprefix'] . "$key with expiration $expiration", 3);
|
||||
return $this->cache->set($this->getRound() . '_' . $this->config['memcache']['keyprefix'] . $key, $value, $expiration);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -35,8 +47,8 @@ class StatsCache extends Memcached {
|
||||
**/
|
||||
public function get($key, $cache_cb = NULL, &$cas_token = NULL) {
|
||||
if (! $this->config['memcache']['enabled']) return false;
|
||||
$this->debug->append("Trying to fetch key " . $this->config['memcache']['keyprefix'] . "$key from cache", 3);
|
||||
if ($data = parent::get($this->config['memcache']['keyprefix'].$key)) {
|
||||
$this->debug->append("Trying to fetch key " . $this->getRound() . '_' . $this->config['memcache']['keyprefix'] . "$key from cache", 3);
|
||||
if ($data = $this->cache->get($this->getRound() . '_' . $this->config['memcache']['keyprefix'].$key)) {
|
||||
$this->debug->append("Found key in cache", 3);
|
||||
return $data;
|
||||
} else {
|
||||
@ -56,7 +68,22 @@ class StatsCache extends Memcached {
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is invoked if the called method was not realised in this class
|
||||
**/
|
||||
public function __call($name, $arguments) {
|
||||
if (! $this->config['memcache']['enabled']) return false;
|
||||
//Invoke method $name of $this->cache class with array of $arguments
|
||||
return call_user_func_array(array($this->cache, $name), $arguments);
|
||||
}
|
||||
}
|
||||
|
||||
$memcache = new StatsCache($config, $debug);
|
||||
$memcache->addServer($config['memcache']['host'], $config['memcache']['port']);
|
||||
// Now we can set our additional key prefix
|
||||
if ($aTmpBlock = $block->getLast()) {
|
||||
$iRoundId = $aTmpBlock['id'];
|
||||
} else {
|
||||
$iRoundId = 0;
|
||||
}
|
||||
$memcache->setRound($iRoundId);
|
||||
|
||||
60
public/include/classes/token.class.php
Normal file
60
public/include/classes/token.class.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
class Token Extends Base {
|
||||
var $table = 'tokens';
|
||||
|
||||
/**
|
||||
* Fetch a token from our table
|
||||
* @param name string Setting name
|
||||
* @return value string Value
|
||||
**/
|
||||
public function getToken($strToken) {
|
||||
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE token = ? LIMIT 1");
|
||||
if ($stmt && $stmt->bind_param('s', $strToken) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $result->fetch_assoc();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new token
|
||||
* @param name string Name of the variable
|
||||
* @param value string Variable value
|
||||
* @return mixed Token string on success, false on failure
|
||||
**/
|
||||
public function createToken($strType, $account_id=NULL) {
|
||||
$strToken = hash('sha256', $account_id.$strType.microtime());
|
||||
if (!$iToken_id = $this->tokentype->getTypeId($strType)) {
|
||||
$this->setErrorMessage('Invalid token type: ' . $strType);
|
||||
return false;
|
||||
}
|
||||
$stmt = $this->mysqli->prepare("
|
||||
INSERT INTO $this->table (token, type, account_id)
|
||||
VALUES (?, ?, ?)
|
||||
");
|
||||
if ($stmt && $stmt->bind_param('sii', $strToken, $iToken_id, $account_id) && $stmt->execute())
|
||||
return $strToken;
|
||||
$this->setErrorMessage('Unable to create new token');
|
||||
$this->debug->append('Failed to create new token in database: ' . $this->mysqli->error);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a used token
|
||||
* @param token string Token name
|
||||
* @return bool
|
||||
**/
|
||||
public function deleteToken($token) {
|
||||
$stmt = $this->mysqli->prepare("DELETE FROM $this->table WHERE token = ? LIMIT 1");
|
||||
if ($stmt && $stmt->bind_param('s', $token) && $stmt->execute())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$oToken = new Token();
|
||||
$oToken->setDebug($debug);
|
||||
$oToken->setMysql($mysqli);
|
||||
$oToken->setTokenType($tokentype);
|
||||
21
public/include/classes/tokentype.class.php
Normal file
21
public/include/classes/tokentype.class.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
|
||||
class Token_Type Extends Base {
|
||||
var $table = 'token_types';
|
||||
/**
|
||||
* Return ID for specific token
|
||||
* @param strName string Token Name
|
||||
* @return mixed ID on success, false on failure
|
||||
**/
|
||||
public function getTypeId($strName) {
|
||||
return $this->getSingle($strName, 'id', 'name', 's');
|
||||
}
|
||||
}
|
||||
|
||||
$tokentype = new Token_Type();
|
||||
$tokentype->setDebug($debug);
|
||||
$tokentype->setMysql($mysqli);
|
||||
@ -9,11 +9,7 @@ if (!defined('SECURITY'))
|
||||
* Implements some common cron tasks outside
|
||||
* the scope of our web application
|
||||
**/
|
||||
class Tools {
|
||||
public function __construct($debug) {
|
||||
$this->debug = $debug;
|
||||
}
|
||||
|
||||
class Tools extends Base {
|
||||
/**
|
||||
* Fetch JSON data from an API
|
||||
* @param url string API URL
|
||||
@ -21,13 +17,13 @@ class Tools {
|
||||
* @param auth array Optional authentication data to be sent with
|
||||
* @return dec array JSON decoded PHP array
|
||||
**/
|
||||
public function getApi($url, $target, $auth=NULL) {
|
||||
private function getApi($url, $target, $auth=NULL) {
|
||||
static $ch = null;
|
||||
static $ch = null;
|
||||
if (is_null($ch)) {
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; BTCE PHP client; '.php_uname('s').'; PHP/'.phpversion().')');
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; PHP client; '.php_uname('s').'; PHP/'.phpversion().')');
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_URL, $url . $target);
|
||||
// curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
|
||||
@ -41,6 +37,54 @@ class Tools {
|
||||
if (!$dec) throw new Exception('Invalid data received, please make sure connection is working and requested API exists');
|
||||
return $dec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect the API to properly extract information
|
||||
* @param url string API URL
|
||||
* @return data string API type
|
||||
**/
|
||||
private function getApiType($url) {
|
||||
if (preg_match('/coinchoose.com/', $url)) {
|
||||
return 'coinchose';
|
||||
} else if (preg_match('/btc-e.com/', $url)) {
|
||||
return 'btce';
|
||||
} else if (preg_match('/cryptsy.com/', $url)) {
|
||||
return 'cryptsy';
|
||||
}
|
||||
$this->setErrorMessage("API URL unknown");
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract price information from API data
|
||||
**/
|
||||
public function getPrice() {
|
||||
$aData = $this->getApi($this->config['price']['url'], $this->config['price']['target']);
|
||||
$strCurrency = $this->config['currency'];
|
||||
// Check the API type for configured URL
|
||||
if (!$strApiType = $this->getApiType($this->config['price']['url']))
|
||||
return false;
|
||||
// Extract price depending on API type
|
||||
switch ($strApiType) {
|
||||
case 'coinchose':
|
||||
foreach ($aData as $aItem) {
|
||||
if($strCurrency == $aItem[0])
|
||||
return $aItem['price'];
|
||||
}
|
||||
break;
|
||||
case 'btce':
|
||||
return $aData['ticker']['last'];
|
||||
break;
|
||||
case 'cryptsy':
|
||||
return $aData['return']['markets'][$strCurrency]['lasttradeprice'];
|
||||
break;
|
||||
}
|
||||
// Catchall, we have no data extractor for this API url
|
||||
$this->setErrorMessage("Undefined API to getPrice() on URL " . $this->config['price']['url']);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$tools = new Tools($debug);
|
||||
$tools = new Tools();
|
||||
$tools->setDebug($debug);
|
||||
$tools->setConfig($config);
|
||||
|
||||
@ -4,29 +4,13 @@
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
|
||||
class Transaction {
|
||||
private $sError = '';
|
||||
private $table = 'transactions';
|
||||
private $tableBlocks = 'blocks';
|
||||
|
||||
public function __construct($debug, $mysqli, $config, $block) {
|
||||
$this->debug = $debug;
|
||||
$this->mysqli = $mysqli;
|
||||
$this->config = $config;
|
||||
$this->block = $block;
|
||||
$this->debug->append("Instantiated Transaction class", 2);
|
||||
}
|
||||
|
||||
// get and set methods
|
||||
private function setErrorMessage($msg) {
|
||||
$this->sError = $msg;
|
||||
}
|
||||
public function getError() {
|
||||
return $this->sError;
|
||||
}
|
||||
class Transaction extends Base {
|
||||
private $sError = '', $table = 'transactions';
|
||||
public $num_rows = 0, $insert_id = 0;
|
||||
|
||||
/**
|
||||
* Add a new transaction to our class table
|
||||
* We also store the inserted ID in case the user needs it
|
||||
* @param account_id int Account ID to book transaction for
|
||||
* @param amount float Coin amount
|
||||
* @param type string Transaction type [Credit, Debit_AP, Debit_MP, Fee, Donation, Orphan_Credit, Orphan_Fee, Orphan_Donation]
|
||||
@ -36,84 +20,199 @@ class Transaction {
|
||||
**/
|
||||
public function addTransaction($account_id, $amount, $type='Credit', $block_id=NULL, $coin_address=NULL) {
|
||||
$stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id, amount, block_id, type, coin_address) VALUES (?, ?, ?, ?, ?)");
|
||||
if ($this->checkStmt($stmt)) {
|
||||
$stmt->bind_param("idiss", $account_id, $amount, $block_id, $type, $coin_address);
|
||||
if ($stmt->execute()) {
|
||||
$this->setErrorMessage("Failed to store transaction");
|
||||
$stmt->close();
|
||||
return true;
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param("idiss", $account_id, $amount, $block_id, $type, $coin_address) && $stmt->execute()) {
|
||||
$this->insert_id = $stmt->insert_id;
|
||||
return true;
|
||||
}
|
||||
$this->setErrorMessage("Failed to store transaction");
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark transactions of a user as archived
|
||||
* @param account_id int Account ID
|
||||
* @param txid int Transaction ID to start from
|
||||
* @param bool boolean True or False
|
||||
**/
|
||||
public function setArchived($account_id, $txid) {
|
||||
$stmt = $this->mysqli->prepare("
|
||||
UPDATE $this->table AS t
|
||||
LEFT JOIN " . $this->block->getTableName() . " AS b
|
||||
ON b.id = t.block_id
|
||||
SET t.archived = 1
|
||||
WHERE ( t.account_id = ? AND t.id <= ? AND b.confirmations >= ? )
|
||||
OR ( t.account_id = ? AND t.id <= ? AND t.type IN ( 'Credit_PPS', 'Donation_PPS', 'Fee_PPS', 'TXFee', 'Debit_MP', 'Debit_AP' ) )");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('iiiii', $account_id, $txid, $this->config['confirmations'], $account_id, $txid) && $stmt->execute())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a transaction summary by type with total amounts
|
||||
* @param account_id int Account ID, NULL for all
|
||||
* @return data array type and total
|
||||
**/
|
||||
public function getTransactionSummary($account_id=NULL) {
|
||||
$sql = "SELECT SUM(t.amount) AS total, t.type AS type FROM $this->table AS t";
|
||||
if (!empty($account_id)) {
|
||||
$sql .= " WHERE t.account_id = ? ";
|
||||
$this->addParam('i', $account_id);
|
||||
}
|
||||
$sql .= " GROUP BY t.type";
|
||||
$stmt = $this->mysqli->prepare($sql);
|
||||
if (!empty($account_id)) {
|
||||
if (!($this->checkStmt($stmt) && call_user_func_array( array($stmt, 'bind_param'), $this->getParam()) && $stmt->execute()))
|
||||
return false;
|
||||
$result = $stmt->get_result();
|
||||
} else {
|
||||
if (!($this->checkStmt($stmt) && $stmt->execute()))
|
||||
return false;
|
||||
$result = $stmt->get_result();
|
||||
}
|
||||
if ($result) {
|
||||
$aData = NULL;
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$aData[$row['type']] = $row['total'];
|
||||
}
|
||||
return $aData;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sometimes transactions become orphans when a block associated to them is orphaned
|
||||
* Updates the transaction types to Orphan_<type>
|
||||
* @param block_id int Orphaned block ID
|
||||
* @return bool
|
||||
**/
|
||||
public function setOrphan($block_id) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$aOrphans = array(
|
||||
'Credit' => 'Orphan_Credit',
|
||||
'Fee' => 'Orphan_Fee',
|
||||
'Donation' => 'Orphan_Donation',
|
||||
'Bonus' => 'Orphan_Bonus'
|
||||
);
|
||||
foreach ($aOrphans as $from => $to) {
|
||||
$stmt = $this->mysqli->prepare("
|
||||
UPDATE $this->table
|
||||
SET type = '$to'
|
||||
WHERE type = '$from'
|
||||
AND block_id = ?
|
||||
");
|
||||
if (!($this->checkStmt($stmt) && $stmt->bind_param('i', $block_id) && $stmt->execute())) {
|
||||
$this->debug->append("Failed to set orphan $from => $to transactions for $block_id");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all transactions from start for account_id
|
||||
* @param account_id int Account ID
|
||||
* @param start int Starting point, id of transaction
|
||||
* @param filter array Filter to limit transactions
|
||||
* @param limit int Only display this many transactions
|
||||
* @param account_id int Account ID
|
||||
* @return data array Database fields as defined in SELECT
|
||||
**/
|
||||
public function getTransactions($account_id, $start=0) {
|
||||
public function getTransactions($start=0, $filter=NULL, $limit=30, $account_id=NULL) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$stmt = $this->mysqli->prepare("
|
||||
$sql = "
|
||||
SELECT
|
||||
SQL_CALC_FOUND_ROWS
|
||||
t.id AS id,
|
||||
a.username as username,
|
||||
t.type AS type,
|
||||
t.amount AS amount,
|
||||
t.coin_address AS coin_address,
|
||||
t.timestamp AS timestamp,
|
||||
b.height AS height,
|
||||
b.blockhash AS blockhash,
|
||||
b.confirmations AS confirmations
|
||||
FROM transactions AS t
|
||||
LEFT JOIN blocks AS b ON t.block_id = b.id
|
||||
WHERE t.account_id = ?
|
||||
ORDER BY id DESC");
|
||||
if ($this->checkStmt($stmt)) {
|
||||
if(!$stmt->bind_param('i', $account_id)) return false;
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
FROM $this->table AS t
|
||||
LEFT JOIN " . $this->block->getTableName() . " AS b ON t.block_id = b.id
|
||||
LEFT JOIN " . $this->user->getTableName() . " AS a ON t.account_id = a.id";
|
||||
if (!empty($account_id)) {
|
||||
$sql .= " WHERE ( t.account_id = ? ) ";
|
||||
$this->addParam('i', $account_id);
|
||||
}
|
||||
if (is_array($filter)) {
|
||||
$aFilter = array();
|
||||
foreach ($filter as $key => $value) {
|
||||
if (!empty($value)) {
|
||||
switch ($key) {
|
||||
case 'type':
|
||||
$aFilter[] = "( t.type = ? )";
|
||||
$this->addParam('s', $value);
|
||||
break;
|
||||
case 'status':
|
||||
switch ($value) {
|
||||
case 'Confirmed':
|
||||
if (empty($filter['type']) || ($filter['type'] != 'Debit_AP' && $filter['type'] != 'Debit_MP' && $filter['type'] != 'TXFee' && $filter['type'] != 'Credit_PPS' && $filter['type'] != 'Fee_PPS' && $filter['type'] != 'Donation_PPS')) {
|
||||
$aFilter[] = "( b.confirmations >= " . $this->config['confirmations'] . " OR ISNULL(b.confirmations) )";
|
||||
}
|
||||
break;
|
||||
case 'Unconfirmed':
|
||||
$aFilter[] = "( b.confirmations < " . $this->config['confirmations'] . " AND b.confirmations >= 0 )";
|
||||
break;
|
||||
case 'Orphan':
|
||||
$aFilter[] = "( b.confirmations = -1 )";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'account':
|
||||
$aFilter[] = "( LOWER(a.username) = LOWER(?) )";
|
||||
$this->addParam('s', $value);
|
||||
break;
|
||||
case 'address':
|
||||
$aFilter[] = "( t.coin_address = ? )";
|
||||
$this->addParam('s', $value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($aFilter)) {
|
||||
empty($account_id) ? $sql .= " WHERE " : $sql .= " AND ";
|
||||
$sql .= implode(' AND ', $aFilter);
|
||||
}
|
||||
}
|
||||
$sql .= "
|
||||
ORDER BY id DESC
|
||||
LIMIT ?,?
|
||||
";
|
||||
// Add some other params to query
|
||||
$this->addParam('i', $start);
|
||||
$this->addParam('i', $limit);
|
||||
$stmt = $this->mysqli->prepare($sql);
|
||||
if ($this->checkStmt($stmt) && call_user_func_array( array($stmt, 'bind_param'), $this->getParam()) && $stmt->execute() && $result = $stmt->get_result()) {
|
||||
// Fetch matching row count
|
||||
$num_rows = $this->mysqli->prepare("SELECT FOUND_ROWS() AS num_rows");
|
||||
if ($num_rows->execute() && $row_count = $num_rows->get_result()->fetch_object()->num_rows)
|
||||
$this->num_rows = $row_count;
|
||||
return $result->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
$this->debug->append('Unable to fetch transactions');
|
||||
$this->debug->append('Failed to fetch transactions: ' . $this->mysqli->error);
|
||||
return false;
|
||||
}
|
||||
|
||||
private function checkStmt($bState) {
|
||||
if ($bState ===! true) {
|
||||
$this->debug->append("Failed to prepare statement: " . $this->mysqli->error);
|
||||
$this->setErrorMessage('Internal application Error');
|
||||
return false;
|
||||
/**
|
||||
* Get all different transaction types
|
||||
* @return mixed array/bool Return types on succes, false on failure
|
||||
**/
|
||||
public function getTypes() {
|
||||
$stmt = $this->mysqli->prepare("SELECT DISTINCT type FROM $this->table");
|
||||
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) {
|
||||
$aData = array('' => '');
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$aData[$row['type']] = $row['type'];
|
||||
}
|
||||
return $aData;
|
||||
}
|
||||
return true;
|
||||
$this->debug->append('Failed to fetch transaction types: ' . $this->mysqli->error);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all donation transactions
|
||||
* Used on donors page
|
||||
* return data array Donors and amounts
|
||||
**/
|
||||
public function getDonations() {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
SUM(t.amount) AS donation,
|
||||
a.username AS username,
|
||||
a.is_anonymous AS is_anonymous,
|
||||
a.donate_percent AS donate_percent
|
||||
FROM $this->table AS t
|
||||
LEFT JOIN " . $this->user->getTableName() . " AS a
|
||||
ON t.account_id = a.id
|
||||
LEFT JOIN blocks AS b
|
||||
ON t.block_id = b.id
|
||||
WHERE
|
||||
(
|
||||
( t.type = 'Donation' AND b.confirmations >= " . $this->config['confirmations'] . " ) OR
|
||||
t.type = 'Donation_PPS'
|
||||
)
|
||||
GROUP BY a.username
|
||||
");
|
||||
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $result->fetch_all(MYSQLI_ASSOC);
|
||||
$this->debug->append("Failed to fetch website donors: " . $this->mysqli->error);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -125,31 +224,16 @@ class Transaction {
|
||||
public function getLockedBalance() {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT ROUND(IFNULL(t1.credit, 0) - IFNULL(t2.debit, 0) - IFNULL(t3.other, 0), 8) AS balance
|
||||
FROM
|
||||
(
|
||||
SELECT sum(t.amount) AS credit
|
||||
FROM $this->table AS t
|
||||
LEFT JOIN " . $this->block->getTableName() . " AS b ON t.block_id = b.id
|
||||
WHERE (
|
||||
( t.type IN ('Credit','Bonus') AND b.confirmations >= ? ) OR
|
||||
( t.type = 'Credit_PPS' )
|
||||
)
|
||||
) AS t1,
|
||||
(
|
||||
SELECT sum(t.amount) AS debit
|
||||
FROM $this->table AS t
|
||||
WHERE t.type IN ('Debit_MP', 'Debit_AP')
|
||||
) AS t2,
|
||||
(
|
||||
SELECT sum(t.amount) AS other
|
||||
FROM " . $this->table . " AS t
|
||||
LEFT JOIN " . $this->block->getTableName() . " AS b ON t.block_id = b.id
|
||||
WHERE (
|
||||
( t.type IN ('Donation','Fee') AND b.confirmations >= ? ) OR
|
||||
t.type IN ('Donation_PPS','Fee_PPS','TXFee')
|
||||
)
|
||||
) AS t3");
|
||||
SELECT
|
||||
ROUND((
|
||||
SUM( IF( ( t.type IN ('Credit','Bonus') AND b.confirmations >= ? ) OR t.type = 'Credit_PPS', t.amount, 0 ) ) -
|
||||
SUM( IF( t.type IN ('Debit_MP', 'Debit_AP'), t.amount, 0 ) ) -
|
||||
SUM( IF( ( t.type IN ('Donation','Fee') AND b.confirmations >= ? ) OR ( t.type IN ('Donation_PPS', 'Fee_PPS', 'TXFee') ), t.amount, 0 ) )
|
||||
), 8) AS balance
|
||||
FROM $this->table AS t
|
||||
LEFT JOIN " . $this->block->getTableName() . " AS b
|
||||
ON t.block_id = b.id
|
||||
WHERE archived = 0");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $this->config['confirmations'], $this->config['confirmations']) && $stmt->execute() && $stmt->bind_result($dBalance) && $stmt->fetch())
|
||||
return $dBalance;
|
||||
// Catchall
|
||||
@ -159,7 +243,7 @@ class Transaction {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an accounts total balance
|
||||
* Get an accounts total balance, ignore archived entries
|
||||
* @param account_id int Account ID
|
||||
* @return data float Credit - Debit - Fees - Donation
|
||||
**/
|
||||
@ -167,68 +251,35 @@ class Transaction {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
ROUND(IFNULL(t1.credit, 0) - IFNULL(t2.debit, 0) - IFNULL(t3.other, 0), 8) AS confirmed,
|
||||
ROUND(IFNULL(t4.credit, 0) - IFNULL(t5.other, 0), 8) AS unconfirmed
|
||||
FROM
|
||||
(
|
||||
SELECT sum(t.amount) AS credit
|
||||
FROM $this->table AS t
|
||||
LEFT JOIN " . $this->block->getTableName() . " AS b ON t.block_id = b.id
|
||||
WHERE
|
||||
(
|
||||
( t.type IN ('Credit','Bonus') AND b.confirmations >= ? ) OR
|
||||
( t.type = 'Credit_PPS' )
|
||||
)
|
||||
AND t.account_id = ?
|
||||
) AS t1,
|
||||
(
|
||||
SELECT sum(t.amount) AS debit
|
||||
FROM $this->table AS t
|
||||
WHERE t.type IN ('Debit_MP', 'Debit_AP')
|
||||
AND t.account_id = ?
|
||||
) AS t2,
|
||||
(
|
||||
SELECT sum(t.amount) AS other
|
||||
FROM $this->table AS t
|
||||
LEFT JOIN " . $this->block->getTableName() . " AS b ON t.block_id = b.id
|
||||
WHERE
|
||||
(
|
||||
( t.type IN ('Donation','Fee') AND b.confirmations >= ? ) OR
|
||||
( t.type IN ('Donation_PPS', 'Fee_PPS', 'TXFee') )
|
||||
)
|
||||
AND t.account_id = ?
|
||||
) AS t3,
|
||||
(
|
||||
SELECT sum(t.amount) AS credit
|
||||
FROM $this->table AS t
|
||||
LEFT JOIN " . $this->block->getTableName() . " AS b ON t.block_id = b.id
|
||||
WHERE
|
||||
t.type IN ('Credit','Bonus') AND b.confirmations < ?
|
||||
AND t.account_id = ?
|
||||
) AS t4,
|
||||
(
|
||||
SELECT sum(t.amount) AS other
|
||||
FROM $this->table AS t
|
||||
LEFT JOIN " . $this->block->getTableName() . " AS b ON t.block_id = b.id
|
||||
WHERE
|
||||
(
|
||||
t.type IN ('Donation','Fee') AND b.confirmations < ?
|
||||
)
|
||||
AND t.account_id = ?
|
||||
) AS t5
|
||||
IFNULL(ROUND((
|
||||
SUM( IF( ( t.type IN ('Credit','Bonus') AND b.confirmations >= ? ) OR t.type = 'Credit_PPS', t.amount, 0 ) ) -
|
||||
SUM( IF( t.type IN ('Debit_MP', 'Debit_AP'), t.amount, 0 ) ) -
|
||||
SUM( IF( ( t.type IN ('Donation','Fee') AND b.confirmations >= ? ) OR ( t.type IN ('Donation_PPS', 'Fee_PPS', 'TXFee') ), t.amount, 0 ) )
|
||||
), 8), 0) AS confirmed,
|
||||
IFNULL(ROUND((
|
||||
SUM( IF( t.type IN ('Credit','Bonus') AND b.confirmations < ? AND b.confirmations >= 0, t.amount, 0 ) ) -
|
||||
SUM( IF( t.type IN ('Donation','Fee') AND b.confirmations < ? AND b.confirmations >= 0, t.amount, 0 ) )
|
||||
), 8), 0) AS unconfirmed,
|
||||
IFNULL(ROUND((
|
||||
SUM( IF( t.type IN ('Credit','Bonus') AND b.confirmations = -1, t.amount, 0) ) -
|
||||
SUM( IF( t.type IN ('Donation','Fee') AND b.confirmations = -1, t.amount, 0) )
|
||||
), 8), 0) AS orphaned
|
||||
FROM $this->table AS t
|
||||
LEFT JOIN " . $this->block->getTableName() . " AS b
|
||||
ON t.block_id = b.id
|
||||
WHERE t.account_id = ?
|
||||
AND archived = 0
|
||||
");
|
||||
if ($this->checkStmt($stmt)) {
|
||||
$stmt->bind_param("iiiiiiiii", $this->config['confirmations'], $account_id, $account_id, $this->config['confirmations'], $account_id, $this->config['confirmations'], $account_id, $this->config['confirmations'], $account_id);
|
||||
if (!$stmt->execute()) {
|
||||
$this->debug->append("Unable to execute statement: " . $stmt->error);
|
||||
$this->setErrorMessage("Fetching balance failed");
|
||||
}
|
||||
$result = $stmt->get_result();
|
||||
$stmt->close();
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param("iiiii", $this->config['confirmations'], $this->config['confirmations'], $this->config['confirmations'], $this->config['confirmations'], $account_id) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $result->fetch_assoc();
|
||||
}
|
||||
$this->debug->append('Failed to fetch users balance: ' . $this->mysqli->error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$transaction = new Transaction($debug, $mysqli, $config, $block);
|
||||
$transaction = new Transaction();
|
||||
$transaction->setDebug($debug);
|
||||
$transaction->setMysql($mysqli);
|
||||
$transaction->setConfig($config);
|
||||
$transaction->setBlock($block);
|
||||
$transaction->setUser($user);
|
||||
|
||||
@ -9,7 +9,6 @@ class User {
|
||||
private $userID = false;
|
||||
private $table = 'accounts';
|
||||
private $user = array();
|
||||
private $tableAccountBalance = 'accountBalance';
|
||||
|
||||
public function __construct($debug, $mysqli, $salt, $config) {
|
||||
$this->debug = $debug;
|
||||
@ -20,38 +19,59 @@ class User {
|
||||
}
|
||||
|
||||
// get and set methods
|
||||
public function setMail($mail) {
|
||||
$this->mail = $mail;
|
||||
}
|
||||
public function setToken($token) {
|
||||
$this->token = $token;
|
||||
}
|
||||
public function setBitcoin($bitcoin) {
|
||||
$this->bitcoin = $bitcoin;
|
||||
}
|
||||
public function setSetting($setting) {
|
||||
$this->setting = $setting;
|
||||
}
|
||||
private function setErrorMessage($msg) {
|
||||
$this->sError = $msg;
|
||||
}
|
||||
public function getError() {
|
||||
return $this->sError;
|
||||
}
|
||||
private function getHash($string) {
|
||||
return hash('sha256', $string.$this->salt);
|
||||
}
|
||||
public function getUserName($id) {
|
||||
return $this->getSingle($id, 'username', 'id');
|
||||
}
|
||||
public function getUserNameByEmail($email) {
|
||||
return $this->getSingle($email, 'username', 'email', 's');
|
||||
}
|
||||
public function getUserId($username) {
|
||||
return $this->getSingle($username, 'id', 'username', 's');
|
||||
}
|
||||
public function getUserEmail($username) {
|
||||
return $this->getSingle($username, 'email', 'username', 's');
|
||||
}
|
||||
public function getUserNoFee($id) {
|
||||
return $this->getSingle($id, 'no_fees', 'id');
|
||||
}
|
||||
public function getUserAdmin($id) {
|
||||
return $this->getSingle($id, 'is_admin', 'id');
|
||||
}
|
||||
public function getUserLocked($id) {
|
||||
return $this->getSingle($id, 'is_locked', 'id');
|
||||
}
|
||||
public function getUserToken($id) {
|
||||
return $this->getSingle($id, 'token', 'id');
|
||||
}
|
||||
public function getUserIp($id) {
|
||||
return $this->getSingle($id, 'loggedIp', 'id');
|
||||
}
|
||||
public function getEmail($email) {
|
||||
return $this->getSingle($email, 'email', 'email', 's');
|
||||
}
|
||||
public function getUserFailed($id) {
|
||||
return $this->getSingle($id, 'failed_logins', 'id');
|
||||
}
|
||||
public function getIdFromToken($token) {
|
||||
return $this->getSingle($token, 'id', 'token', 's');
|
||||
public function isNoFee($id) {
|
||||
return $this->getUserNoFee($id);
|
||||
}
|
||||
public function isLocked($id) {
|
||||
return $this->getUserLocked($id);
|
||||
@ -59,6 +79,10 @@ class User {
|
||||
public function isAdmin($id) {
|
||||
return $this->getUserAdmin($id);
|
||||
}
|
||||
public function changeNoFee($id) {
|
||||
$field = array('name' => 'no_fees', 'type' => 'i', 'value' => !$this->isNoFee($id));
|
||||
return $this->updateSingle($id, $field);
|
||||
}
|
||||
public function changeLocked($id) {
|
||||
$field = array('name' => 'is_locked', 'type' => 'i', 'value' => !$this->isLocked($id));
|
||||
return $this->updateSingle($id, $field);
|
||||
@ -67,11 +91,7 @@ class User {
|
||||
$field = array('name' => 'is_admin', 'type' => 'i', 'value' => !$this->isAdmin($id));
|
||||
return $this->updateSingle($id, $field);
|
||||
}
|
||||
public function setUserToken($id) {
|
||||
$field = array('name' => 'token', 'type' => 's', 'value' => hash('sha256', $id.time().$this->salt));
|
||||
return $this->updateSingle($id, $field);
|
||||
}
|
||||
private function setUserFailed($id, $value) {
|
||||
public function setUserFailed($id, $value) {
|
||||
$field = array( 'name' => 'failed_logins', 'type' => 'i', 'value' => $value);
|
||||
return $this->updateSingle($id, $field);
|
||||
}
|
||||
@ -105,15 +125,25 @@ class User {
|
||||
public function checkLogin($username, $password) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$this->debug->append("Checking login for $username with password $password", 2);
|
||||
if (empty($username) || empty($password)) {
|
||||
$this->setErrorMessage("Invalid username or password.");
|
||||
return false;
|
||||
}
|
||||
if (filter_var($username, FILTER_VALIDATE_EMAIL)) {
|
||||
$this->debug->append("Username is an e-mail", 2);
|
||||
if (!$username = $this->getUserNameByEmail($username)) {
|
||||
$this->setErrorMessage("Invalid username or password.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if ($this->isLocked($this->getUserId($username))) {
|
||||
$this->setErrorMessage("Account is locked. Please contact site support.");
|
||||
return false;
|
||||
}
|
||||
if ( $this->checkUserPassword($username, $password)) {
|
||||
if ($this->checkUserPassword($username, $password)) {
|
||||
$this->createSession($username);
|
||||
$this->setUserFailed($this->getUserId($username), 0);
|
||||
$this->setUserIp($this->getUserId($username), $_SERVER['REMOTE_ADDR']);
|
||||
return true;
|
||||
if ($this->setUserIp($this->getUserId($username), $_SERVER['REMOTE_ADDR']))
|
||||
return true;
|
||||
}
|
||||
$this->setErrorMessage("Invalid username or password");
|
||||
if ($id = $this->getUserId($username))
|
||||
@ -132,7 +162,7 @@ class User {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$this->debug->append("Confirming PIN for $userId and pin $pin", 2);
|
||||
$stmt = $this->mysqli->prepare("SELECT pin FROM $this->table WHERE id=? AND pin=? LIMIT 1");
|
||||
$pin_hash = hash('sha256', $pin.$this->salt);
|
||||
$pin_hash = $this->getHash($pin);
|
||||
$stmt->bind_param('is', $userId, $pin_hash);
|
||||
$stmt->execute();
|
||||
$stmt->bind_result($row_pin);
|
||||
@ -181,7 +211,6 @@ class User {
|
||||
return $result->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
$this->debug->append("Unable to fetch users with AP set");
|
||||
echo $this->mysqli->error;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -251,8 +280,8 @@ class User {
|
||||
$this->setErrorMessage( 'New password is too short, please use more than 8 chars' );
|
||||
return false;
|
||||
}
|
||||
$current = hash('sha256', $current.$this->salt);
|
||||
$new = hash('sha256', $new1.$this->salt);
|
||||
$current = $this->getHash($current);
|
||||
$new = $this->getHash($new1);
|
||||
$stmt = $this->mysqli->prepare("UPDATE $this->table SET pass = ? WHERE ( id = ? AND pass = ? )");
|
||||
if ($this->checkStmt($stmt)) {
|
||||
$stmt->bind_param('sis', $new, $userID, $current);
|
||||
@ -274,19 +303,25 @@ class User {
|
||||
* @param donat float donation % of income
|
||||
* @return bool
|
||||
**/
|
||||
public function updateAccount($userID, $address, $threshold, $donate, $email) {
|
||||
public function updateAccount($userID, $address, $threshold, $donate, $email, $is_anonymous) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$bUser = false;
|
||||
|
||||
// number validation checks
|
||||
if ($threshold < $this->config['ap_threshold']['min'] && $threshold != 0) {
|
||||
if (!is_numeric($threshold)) {
|
||||
$this->setErrorMessage('Invalid input for auto-payout');
|
||||
return false;
|
||||
} else if ($threshold < $this->config['ap_threshold']['min'] && $threshold != 0) {
|
||||
$this->setErrorMessage('Threshold below configured minimum of ' . $this->config['ap_threshold']['min']);
|
||||
return false;
|
||||
} else if ($threshold > $this->config['ap_threshold']['max']) {
|
||||
$this->setErrorMessage('Threshold above configured maximum of ' . $this->config['ap_threshold']['max']);
|
||||
return false;
|
||||
}
|
||||
if ($donate < 0) {
|
||||
if (!is_numeric($donate)) {
|
||||
$this->setErrorMessage('Invalid input for donation');
|
||||
return false;
|
||||
} else if ($donate < 0) {
|
||||
$this->setErrorMessage('Donation below allowed 0% limit');
|
||||
return false;
|
||||
} else if ($donate > 100) {
|
||||
@ -297,13 +332,29 @@ class User {
|
||||
$this->setErrorMessage('Invalid email address');
|
||||
return false;
|
||||
}
|
||||
/* if ($this->bitcoin->can_connect() === true && !empty($address)) {
|
||||
try {
|
||||
$aStatus = $this->bitcoin->validateaddress($address);
|
||||
if (!$aStatus['isvalid']) {
|
||||
$this->setErrorMessage('Invalid coin address');
|
||||
return false;
|
||||
}
|
||||
} catch (BitcoinClientException $e) {
|
||||
$this->setErrorMessage('Unable to verify coin address');
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$this->setErrorMessage('Unable to connect to RPC server for coin address validation');
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
// Number sanitizer, just in case we fall through above
|
||||
$threshold = min($this->config['ap_threshold']['max'], max(0, floatval($threshold)));
|
||||
$donate = min(100, max(0, floatval($donate)));
|
||||
|
||||
// We passed all validation checks so update the account
|
||||
$stmt = $this->mysqli->prepare("UPDATE $this->table SET coin_address = ?, ap_threshold = ?, donate_percent = ?, email = ? WHERE id = ?");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('sddsi', $address, $threshold, $donate, $email, $userID) && $stmt->execute())
|
||||
$stmt = $this->mysqli->prepare("UPDATE $this->table SET coin_address = ?, ap_threshold = ?, donate_percent = ?, email = ?, is_anonymous = ? WHERE id = ?");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('sddsii', $address, $threshold, $donate, $email, $is_anonymous, $userID) && $stmt->execute())
|
||||
return true;
|
||||
// Catchall
|
||||
$this->setErrorMessage('Failed to update your account');
|
||||
@ -336,9 +387,10 @@ class User {
|
||||
private function checkUserPassword($username, $password) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$user = array();
|
||||
$password_hash = $this->getHash($password);
|
||||
$stmt = $this->mysqli->prepare("SELECT username, id, is_admin FROM $this->table WHERE username=? AND pass=? LIMIT 1");
|
||||
if ($this->checkStmt($stmt)) {
|
||||
$stmt->bind_param('ss', $username, hash('sha256', $password.$this->salt));
|
||||
$stmt->bind_param('ss', $username, $password_hash);
|
||||
$stmt->execute();
|
||||
$stmt->bind_result($row_username, $row_id, $row_admin);
|
||||
$stmt->fetch();
|
||||
@ -369,12 +421,24 @@ class User {
|
||||
* @param none
|
||||
* @return true
|
||||
**/
|
||||
public function logoutUser() {
|
||||
public function logoutUser($from="") {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
// Unset all of the session variables
|
||||
$_SESSION = array();
|
||||
// As we're killing the sesison, also kill the cookie!
|
||||
if (ini_get("session.use_cookies")) {
|
||||
$params = session_get_cookie_params();
|
||||
setcookie(session_name(), '', time() - 42000, $params["path"], $params["domain"], $params["secure"], $params["httponly"]);
|
||||
}
|
||||
// Destroy the session.
|
||||
session_destroy();
|
||||
// Enforce generation of a new Session ID and delete the old
|
||||
session_regenerate_id(true);
|
||||
// Enforce a page reload
|
||||
header("Location: index.php");
|
||||
// Enforce a page reload and point towards login with referrer included, if supplied
|
||||
$location = @$_SERVER['HTTPS'] ? 'https' . '://' . $_SERVER['SERVER_NAME'] . $_SERVER['PHP_SELF'] : 'http' . '://' . $_SERVER['SERVER_NAME'] . $_SERVER['PHP_SELF'];
|
||||
if (!empty($from)) $location .= '?page=login&to=' . urlencode($from);
|
||||
// if (!headers_sent()) header('Location: ' . $location);
|
||||
exit('<meta http-equiv="refresh" content="0; url=' . $location . '"/>');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -396,7 +460,7 @@ class User {
|
||||
$this->debug->append("Fetching user information for user id: $userID");
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
id, username, pin, api_key, is_admin, email,
|
||||
id, username, pin, api_key, is_admin, is_anonymous, email, no_fees,
|
||||
IFNULL(donate_percent, '0') as donate_percent, coin_address, ap_threshold
|
||||
FROM $this->table
|
||||
WHERE id = ? LIMIT 0,1");
|
||||
@ -424,8 +488,20 @@ class User {
|
||||
* @param email2 string Email confirmation
|
||||
* @return bool
|
||||
**/
|
||||
public function register($username, $password1, $password2, $pin, $email1='', $email2='') {
|
||||
public function register($username, $password1, $password2, $pin, $email1='', $email2='', $strToken='') {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
if (strlen($username > 40)) {
|
||||
$this->setErrorMessage('Username exceeding character limit');
|
||||
return false;
|
||||
}
|
||||
if (preg_match('/[^a-z_\-0-9]/i', $username)) {
|
||||
$this->setErrorMessage('Username may only contain alphanumeric characters');
|
||||
return false;
|
||||
}
|
||||
if ($this->getEmail($email1)) {
|
||||
$this->setErrorMessage( 'This e-mail address is already taken' );
|
||||
return false;
|
||||
}
|
||||
if (strlen($password1) < 8) {
|
||||
$this->setErrorMessage( 'Password is too short, minimum of 8 characters required' );
|
||||
return false;
|
||||
@ -446,28 +522,70 @@ class User {
|
||||
$this->setErrorMessage( 'Invalid PIN' );
|
||||
return false;
|
||||
}
|
||||
$apikey = hash("sha256",$username.$salt);
|
||||
if ($this->mysqli->query("SELECT id FROM $this->table LIMIT 1")->num_rows > 0) {
|
||||
$stmt = $this->mysqli->prepare("
|
||||
INSERT INTO $this->table (username, pass, email, pin, api_key)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
");
|
||||
} else {
|
||||
$stmt = $this->mysqli->prepare("
|
||||
INSERT INTO $this->table (username, pass, email, pin, api_key, is_admin)
|
||||
VALUES (?, ?, ?, ?, ?, 1)
|
||||
");
|
||||
}
|
||||
if ($this->checkStmt($stmt)) {
|
||||
$stmt->bind_param('sssss', $username, hash("sha256", $password1.$this->salt), $email1, hash("sha256", $pin.$this->salt), $apikey);
|
||||
if (!$stmt->execute()) {
|
||||
$this->setErrorMessage( 'Unable to register' );
|
||||
if ($stmt->sqlstate == '23000') $this->setErrorMessage( 'Username already exists' );
|
||||
echo $this->mysqli->error;
|
||||
if (isset($strToken) && !empty($strToken)) {
|
||||
$aToken = $this->token->getToken($strToken);
|
||||
// Circle dependency, so we create our own object here
|
||||
$invitation = new Invitation();
|
||||
$invitation->setMysql($this->mysqli);
|
||||
$invitation->setDebug($this->debug);
|
||||
$invitation->setUser($this);
|
||||
$invitation->setConfig($this->config);
|
||||
if (!$invitation->setActivated($aToken['id'])) {
|
||||
$this->setErrorMessage('Unable to activate your invitation');
|
||||
return false;
|
||||
}
|
||||
$stmt->close();
|
||||
return true;
|
||||
if (!$this->token->deleteToken($strToken)) {
|
||||
$this->setErrorMessage('Unable to remove used token');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if ($this->mysqli->query("SELECT id FROM $this->table LIMIT 1")->num_rows > 0) {
|
||||
! $this->setting->getValue('accounts_confirm_email_disabled') ? $is_locked = 1 : $is_locked = 0;
|
||||
$is_admin = 0;
|
||||
$stmt = $this->mysqli->prepare("
|
||||
INSERT INTO $this->table (username, pass, email, pin, api_key, is_locked)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
");
|
||||
} else {
|
||||
$is_locked = 0;
|
||||
$is_admin = 1;
|
||||
$stmt = $this->mysqli->prepare("
|
||||
INSERT INTO $this->table (username, pass, email, pin, api_key, is_admin, is_locked)
|
||||
VALUES (?, ?, ?, ?, ?, 1, ?)
|
||||
");
|
||||
}
|
||||
|
||||
// Create hashed strings using original string and salt
|
||||
$password_hash = $this->getHash($password1);
|
||||
$pin_hash = $this->getHash($pin);
|
||||
$apikey_hash = $this->getHash($username);
|
||||
$username_clean = strip_tags($username);
|
||||
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('sssssi', $username_clean, $password_hash, $email1, $pin_hash, $apikey_hash, $is_locked) && $stmt->execute()) {
|
||||
if (! $this->setting->getValue('accounts_confirm_email_enabled') && $is_admin != 1) {
|
||||
if ($token = $this->token->createToken('confirm_email', $stmt->insert_id)) {
|
||||
$aData['username'] = $username_clean;
|
||||
$aData['token'] = $token;
|
||||
$aData['email'] = $email1;
|
||||
$aData['subject'] = 'E-Mail verification';
|
||||
if (!$this->mail->sendMail('register/confirm_email', $aData)) {
|
||||
$this->setErrorMessage('Unable to request email confirmation: ' . $this->mail->getError());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
$this->setErrorMessage('Failed to create confirmation token');
|
||||
$this->debug->append('Unable to create confirm_email token: ' . $this->token->getError());
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
$this->setErrorMessage( 'Unable to register' );
|
||||
$this->debug->append('Failed to insert user into DB: ' . $this->mysqli->error);
|
||||
if ($stmt->sqlstate == '23000') $this->setErrorMessage( 'Username or email already registered' );
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -479,9 +597,9 @@ class User {
|
||||
* @param new2 string New password verification
|
||||
* @return bool
|
||||
**/
|
||||
public function useToken($token, $new1, $new2) {
|
||||
public function resetPassword($token, $new1, $new2) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
if ($id = $this->getIdFromToken($token)) {
|
||||
if ($aToken = $this->token->getToken($token)) {
|
||||
if ($new1 !== $new2) {
|
||||
$this->setErrorMessage( 'New passwords do not match' );
|
||||
return false;
|
||||
@ -490,51 +608,47 @@ class User {
|
||||
$this->setErrorMessage( 'New password is too short, please use more than 8 chars' );
|
||||
return false;
|
||||
}
|
||||
$new = hash('sha256', $new1.$this->salt);
|
||||
$stmt = $this->mysqli->prepare("UPDATE $this->table SET pass = ?, token = NULL WHERE id = ? AND token = ?");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('sis', $new, $id, $token) && $stmt->execute() && $stmt->affected_rows === 1) {
|
||||
return true;
|
||||
$new_hash = $this->getHash($new1);
|
||||
$stmt = $this->mysqli->prepare("UPDATE $this->table SET pass = ? WHERE id = ?");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('si', $new_hash, $aToken['account_id']) && $stmt->execute() && $stmt->affected_rows === 1) {
|
||||
if ($this->token->deleteToken($aToken['token'])) {
|
||||
return true;
|
||||
} else {
|
||||
$this->setErrorMessage('Unable to invalidate used token');
|
||||
}
|
||||
} else {
|
||||
$this->setErrorMessage('Unable to set new password');
|
||||
}
|
||||
} else {
|
||||
$this->setErrorMessage("Unable find user for your token");
|
||||
return false;
|
||||
$this->setErrorMessage('Invalid token');
|
||||
}
|
||||
$this->debug->append('Failed to update password:' . $this->mysqli->error);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset a password by sending a password reset mail
|
||||
* @param username string Username to reset password for
|
||||
* @param smarty object Smarty object for mail templating
|
||||
* @return bool
|
||||
**/
|
||||
public function resetPassword($username, $smarty) {
|
||||
public function initResetPassword($username) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
// Fetch the users mail address
|
||||
if (!$email = $this->getUserEmail($username)) {
|
||||
if (empty($username)) {
|
||||
$this->serErrorMessage("Username must not be empty");
|
||||
return false;
|
||||
}
|
||||
if (!$aData['email'] = $this->getUserEmail($username)) {
|
||||
$this->setErrorMessage("Unable to find a mail address for user $username");
|
||||
return false;
|
||||
}
|
||||
if (!$this->setUserToken($this->getUserId($username))) {
|
||||
$this->setErrorMessage("Unable to setup token for password reset");
|
||||
if (!$aData['token'] = $this->token->createToken('password_reset', $this->getUserId($username))) {
|
||||
$this->setErrorMessage('Unable to setup token for password reset');
|
||||
return false;
|
||||
}
|
||||
// Send password reset link
|
||||
if (!$token = $this->getUserToken($this->getUserId($username))) {
|
||||
$this->setErrorMessage("Unable fetch token for password reset");
|
||||
return false;
|
||||
}
|
||||
$smarty->assign('TOKEN', $token);
|
||||
$smarty->assign('USERNAME', $username);
|
||||
$smarty->assign('SUBJECT', 'Password Reset Request');
|
||||
$smarty->assign('WEBSITENAME', $this->config['website']['name']);
|
||||
$headers = 'From: Website Administration <' . $this->config['website']['email'] . ">\n";
|
||||
$headers .= "MIME-Version: 1.0\n";
|
||||
$headers .= "Content-Type: text/html; charset=ISO-8859-1\r\n";
|
||||
if (mail($email,
|
||||
$smarty->fetch('templates/mail/subject.tpl'),
|
||||
$smarty->fetch('templates/mail/body.tpl'),
|
||||
$headers)) {
|
||||
$aData['username'] = $username;
|
||||
$aData['subject'] = 'Password Reset Request';
|
||||
if ($this->mail->sendMail('password/reset', $aData)) {
|
||||
return true;
|
||||
} else {
|
||||
$this->setErrorMessage("Unable to send mail to your address");
|
||||
@ -550,15 +664,21 @@ class User {
|
||||
* @param none
|
||||
* @return bool
|
||||
**/
|
||||
public function isAuthenticated() {
|
||||
public function isAuthenticated($logout=true) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
if (@$_SESSION['AUTHENTICATED'] == true && ! $this->isLocked($_SESSION['USERDATA']['id']) && $this->getUserIp($_SESSION['USERDATA']['id']) == $_SERVER['REMOTE_ADDR'])
|
||||
return true;
|
||||
if (@$_SESSION['AUTHENTICATED'] == true &&
|
||||
!$this->isLocked($_SESSION['USERDATA']['id']) &&
|
||||
$this->getUserIp($_SESSION['USERDATA']['id']) == $_SERVER['REMOTE_ADDR']
|
||||
) return true;
|
||||
// Catchall
|
||||
$this->logoutUser();
|
||||
if ($logout == true) $this->logoutUser($_SERVER['REQUEST_URI']);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Make our class available automatically
|
||||
$user = new User($debug, $mysqli, SALT, $config);
|
||||
$user->setMail($mail);
|
||||
$user->setToken($oToken);
|
||||
$user->setBitcoin($bitcoin);
|
||||
$user->setSetting($setting);
|
||||
|
||||
@ -42,14 +42,22 @@ class Worker {
|
||||
**/
|
||||
public function updateWorkers($account_id, $data) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
if (!is_array($data)) {
|
||||
$this->setErrorMessage('No workers to update');
|
||||
return false;
|
||||
}
|
||||
$username = $this->user->getUserName($account_id);
|
||||
$iFailed = 0;
|
||||
foreach ($data as $key => $value) {
|
||||
// Prefix the WebUser to Worker name
|
||||
$value['username'] = "$username." . $value['username'];
|
||||
$stmt = $this->mysqli->prepare("UPDATE $this->table SET password = ?, username = ?, monitor = ? WHERE account_id = ? AND id = ?");
|
||||
if ( ! ( $this->checkStmt($stmt) && $stmt->bind_param('ssiii', $value['password'], $value['username'], $value['monitor'], $account_id, $key) && $stmt->execute()) )
|
||||
if ('' === $value['username'] || '' === $value['password']) {
|
||||
$iFailed++;
|
||||
} else {
|
||||
// Prefix the WebUser to Worker name
|
||||
$value['username'] = "$username." . $value['username'];
|
||||
$stmt = $this->mysqli->prepare("UPDATE $this->table SET password = ?, username = ?, monitor = ? WHERE account_id = ? AND id = ?");
|
||||
if ( ! ( $this->checkStmt($stmt) && $stmt->bind_param('ssiii', $value['password'], $value['username'], $value['monitor'], $account_id, $key) && $stmt->execute()) )
|
||||
$iFailed++;
|
||||
}
|
||||
}
|
||||
if ($iFailed == 0)
|
||||
return true;
|
||||
@ -67,14 +75,15 @@ class Worker {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT account_id, id, username
|
||||
FROM " . $this->table . "
|
||||
WHERE monitor = 1 AND ( SELECT SIGN(COUNT(id)) FROM " . $this->share->getTableName() . " WHERE username = $this->table.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)) = 0");
|
||||
|
||||
FROM " . $this->table . " AS w
|
||||
WHERE monitor = 1
|
||||
AND (
|
||||
SELECT IFNULL(SUM(IF(our_result = 'Y', 1, 0)), 0) FROM " . $this->share->getTableName() . " WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)
|
||||
) = 0");
|
||||
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $result->fetch_all(MYSQLI_ASSOC);
|
||||
// Catchall
|
||||
$this->setErrorMessage("Unable to fetch IDLE, monitored workers");
|
||||
echo $this->mysqli->error;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -87,15 +96,38 @@ class Worker {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT id, username, password, monitor,
|
||||
( SELECT SIGN(COUNT(id)) FROM " . $this->share->getTableName() . " WHERE username = $this->table.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)) AS active,
|
||||
( SELECT ROUND(COUNT(id) * POW(2, " . $this->config['difficulty'] . ")/600/1000) FROM " . $this->share->getTableName() . " WHERE username = $this->table.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)) AS hashrate
|
||||
FROM $this->table
|
||||
( SELECT COUNT(id) FROM " . $this->share->getTableName() . " WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)) AS count_all,
|
||||
( SELECT COUNT(id) FROM " . $this->share->getArchiveTableName() . " WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)) AS count_all_archive,
|
||||
(
|
||||
SELECT
|
||||
IFNULL(IF(our_result='Y', ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536/600/1000), 0), 0) AS hashrate
|
||||
FROM " . $this->share->getTableName() . "
|
||||
WHERE
|
||||
username = w.username
|
||||
AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)
|
||||
) + (
|
||||
SELECT
|
||||
IFNULL(IF(our_result='Y', ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536/600/1000), 0), 0) AS hashrate
|
||||
FROM " . $this->share->getArchiveTableName() . "
|
||||
WHERE
|
||||
username = w.username
|
||||
AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)
|
||||
) AS hashrate,
|
||||
(
|
||||
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) / count_all, 2), 0)
|
||||
FROM " . $this->share->getTableName() . "
|
||||
WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)
|
||||
) + (
|
||||
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) / count_all_archive, 2), 0)
|
||||
FROM " . $this->share->getArchiveTableName() . "
|
||||
WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)
|
||||
) AS difficulty
|
||||
FROM $this->table AS w
|
||||
WHERE id = ?
|
||||
");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $id) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $result->fetch_assoc();
|
||||
// Catchall
|
||||
echo $this->mysqli->error;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -108,9 +140,33 @@ class Worker {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT id, username, password, monitor,
|
||||
( SELECT SIGN(COUNT(id)) FROM " . $this->share->getTableName() . " WHERE username = $this->table.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)) AS active,
|
||||
( SELECT ROUND(COUNT(id) * POW(2, " . $this->config['difficulty'] . ")/600/1000) FROM " . $this->share->getTableName() . " WHERE username = $this->table.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)) AS hashrate
|
||||
FROM $this->table
|
||||
( SELECT COUNT(id) FROM " . $this->share->getTableName() . " WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)) AS count_all,
|
||||
( SELECT COUNT(id) FROM " . $this->share->getArchiveTableName() . " WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)) AS count_all_archive,
|
||||
(
|
||||
SELECT
|
||||
IFNULL(IF(our_result='Y', ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536/600/1000), 0), 0) AS hashrate
|
||||
FROM " . $this->share->getTableName() . "
|
||||
WHERE
|
||||
username = w.username
|
||||
AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)
|
||||
) + (
|
||||
SELECT
|
||||
IFNULL(IF(our_result='Y', ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536/600/1000), 0), 0) AS hashrate
|
||||
FROM " . $this->share->getArchiveTableName() . "
|
||||
WHERE
|
||||
username = w.username
|
||||
AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)
|
||||
) AS hashrate,
|
||||
(
|
||||
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) / count_all, 2), 0)
|
||||
FROM " . $this->share->getTableName() . "
|
||||
WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)
|
||||
) + (
|
||||
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) / count_all_archive, 2), 0)
|
||||
FROM " . $this->share->getArchiveTableName() . "
|
||||
WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)
|
||||
) AS difficulty
|
||||
FROM $this->table AS w
|
||||
WHERE account_id = ?");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $account_id) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $result->fetch_all(MYSQLI_ASSOC);
|
||||
@ -127,15 +183,9 @@ class Worker {
|
||||
**/
|
||||
public function getCountAllActiveWorkers() {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$stmt = $this->mysqli->prepare("SELECT COUNT(DISTINCT username) AS total FROM " . $this->share->getTableName() . " WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE)");
|
||||
if ($this->checkStmt($stmt)) {
|
||||
if (!$stmt->execute()) {
|
||||
return false;
|
||||
}
|
||||
$result = $stmt->get_result();
|
||||
$stmt->close();
|
||||
$stmt = $this->mysqli->prepare("SELECT IFNULL(IF(our_result='Y', COUNT(DISTINCT username), 0), 0) AS total FROM " . $this->share->getTableName() . " WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE)");
|
||||
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $result->fetch_object()->total;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -150,6 +200,10 @@ class Worker {
|
||||
**/
|
||||
public function addWorker($account_id, $workerName, $workerPassword) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
if ('' === $workerName || '' === $workerPassword) {
|
||||
$this->setErrorMessage('Worker name and/or password may not be empty');
|
||||
return false;
|
||||
}
|
||||
$username = $this->user->getUserName($account_id);
|
||||
$workerName = "$username.$workerName";
|
||||
$stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id, username, password) VALUES(?, ?, ?)");
|
||||
@ -177,7 +231,6 @@ class Worker {
|
||||
if ($this->checkStmt($stmt)) {
|
||||
$stmt->bind_param('ii', $account_id, $id);
|
||||
if ($stmt->execute() && $stmt->affected_rows == 1) {
|
||||
$stmt->close;
|
||||
return true;
|
||||
} else {
|
||||
$this->setErrorMessage( 'Unable to delete worker' );
|
||||
|
||||
253
public/include/config/admin_settings.inc.php
Normal file
253
public/include/config/admin_settings.inc.php
Normal file
@ -0,0 +1,253 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
// Load a list of themes available
|
||||
$aTmpThemes = glob(THEME_DIR . '/*');
|
||||
$aThemes = array();
|
||||
foreach ($aTmpThemes as $dir) {
|
||||
if (basename($dir) != 'cache' && basename($dir) != 'compile' && basename($dir) != 'mail') $aThemes[basename($dir)] = basename($dir);
|
||||
}
|
||||
|
||||
// Load the settings available in this system
|
||||
$aSettings['website'][] = array(
|
||||
'display' => 'Maintenance Mode', 'type' => 'select',
|
||||
'options' => array( 0 => 'No', 1 => 'Yes' ),
|
||||
'default' => 0,
|
||||
'name' => 'maintenance', 'value' => $setting->getValue('maintenance'),
|
||||
'tooltip' => 'Enable or Disable maintenance mode. Only admins can still login.'
|
||||
);
|
||||
$aSettings['website'][] = array(
|
||||
'display' => 'Message of the Day', 'type' => 'text',
|
||||
'size' => 25,
|
||||
'default' => '',
|
||||
'name' => 'system_motd', 'value' => $setting->getValue('system_motd'),
|
||||
'tooltip' => 'Display a message of the day as information popup if set.'
|
||||
);
|
||||
$aSettings['website'][] = array(
|
||||
'display' => 'Website Name', 'type' => 'text',
|
||||
'size' => 25,
|
||||
'default' => 'The Pool',
|
||||
'name' => 'website_name', 'value' => $setting->getValue('website_name'),
|
||||
'tooltip' => 'The name of you pool page, displayed in the header of the page.'
|
||||
);
|
||||
$aSettings['website'][] = array(
|
||||
'display' => 'Website Title', 'type' => 'text',
|
||||
'size' => 25,
|
||||
'default' => 'The Pool - Mining Evolved',
|
||||
'name' => 'website_title', 'value' => $setting->getValue('website_title'),
|
||||
'tooltip' => 'The title of you pool page, displayed in the browser window header.'
|
||||
);
|
||||
$aSettings['website'][] = array(
|
||||
'display' => 'Website Slogan', 'type' => 'text',
|
||||
'size' => 25,
|
||||
'default' => 'Resistance is Futile',
|
||||
'name' => 'website_slogan', 'value' => $setting->getValue('website_slogan'),
|
||||
'tooltip' => 'The slogan of you pool page, displayed in the browser window header.'
|
||||
);
|
||||
$aSettings['website'][] = array(
|
||||
'display' => 'Website e-mail', 'type' => 'text',
|
||||
'size' => 25,
|
||||
'default' => 'test@example.com',
|
||||
'name' => 'website_email', 'value' => $setting->getValue('website_email'),
|
||||
'tooltip' => 'The email address for your pool, used in mail templates and notifications.'
|
||||
);
|
||||
$aSettings['website'][] = array(
|
||||
'display' => 'Website theme', 'type' => 'select',
|
||||
'options' => $aThemes,
|
||||
'default' => 'mpos',
|
||||
'name' => 'website_theme', 'value' => $setting->getValue('website_theme'),
|
||||
'tooltip' => 'The default theme used on your pool.'
|
||||
);
|
||||
$aSettings['website'][] = array(
|
||||
'display' => 'Website mobile theme', 'type' => 'select',
|
||||
'options' => $aThemes,
|
||||
'default' => 'mobile',
|
||||
'name' => 'website_mobile_theme', 'value' => $setting->getValue('website_mobile_theme'),
|
||||
'tooltip' => 'The mobile theme used for your pool.'
|
||||
);
|
||||
$aSettings['website'][] = array(
|
||||
'display' => 'Blockexplorer URL', 'type' => 'text',
|
||||
'size' => 50,
|
||||
'default' => 'http://explorer.litecoin.net/block/',
|
||||
'name' => 'website_blockexplorer_url', 'value' => $setting->getValue('website_blockexplorer_url'),
|
||||
'tooltip' => 'URL to the blockexplorer website for your blockchain. Will append the blockhash to the URL. Leave empty to disabled this.'
|
||||
);
|
||||
$aSettings['website'][] = array(
|
||||
'display' => 'Disable Blockexplorer', 'type' => 'select',
|
||||
'options' => array( 0 => 'No', 1 => 'Yes' ),
|
||||
'default' => 0,
|
||||
'name' => 'website_blockexplorer_disabled', 'value' => $setting->getValue('website_blockexplorer_disabled'),
|
||||
'tooltip' => 'Enabled or disable the blockexplorer URL feature. Will remove any links using the blockexplorer URL.'
|
||||
);
|
||||
$aSettings['website'][] = array(
|
||||
'display' => 'Chaininfo URL', 'type' => 'text',
|
||||
'size' => 50,
|
||||
'default' => 'http://allchains.info',
|
||||
'name' => 'website_chaininfo_url', 'value' => $setting->getValue('website_chaininfo_url'),
|
||||
'tooltip' => 'URL to the chaininfo website for your blockchain. Leave empty to disabled this.'
|
||||
);
|
||||
$aSettings['website'][] = array(
|
||||
'display' => 'Disable Chaininfo', 'type' => 'select',
|
||||
'options' => array( 0 => 'No', 1 => 'Yes' ),
|
||||
'default' => 0,
|
||||
'name' => 'website_chaininfo_disabled', 'value' => $setting->getValue('website_chaininfo_disabled'),
|
||||
'tooltip' => 'Enabled or disable the chainfo URL feature. Will remove any links using the chaininfo URL.'
|
||||
);
|
||||
$aSettings['wallet'][] = array(
|
||||
'display' => 'Cold Coins', 'type' => 'text',
|
||||
'size' => 6,
|
||||
'default' => 0,
|
||||
'name' => 'wallet_cold_coins', 'value' => $setting->getValue('wallet_cold_coins'),
|
||||
'tooltip' => 'Amount of coins held in a pools cold wallet.'
|
||||
);
|
||||
$aSettings['statistics'][] = array(
|
||||
'display' => 'Ajax Refresh Interval', 'type' => 'select',
|
||||
'options' => array('5' => '5', '10' => '10', '15' => '15', '30' => '30', '60' => '60' ),
|
||||
'default' => 10,
|
||||
'name' => 'statistics_ajax_refresh_interval', 'value' => $setting->getValue('statistics_ajax_refresh_interval'),
|
||||
'tooltip' => 'How often to refresh data via ajax in seconds.'
|
||||
);
|
||||
$aSettings['statistics'][] = array(
|
||||
'display' => 'Ajax Data Interval', 'type' => 'select',
|
||||
'options' => array('60' => '1', '300' => '5', '600' => '10'),
|
||||
'default' => 300,
|
||||
'name' => 'statistics_ajax_data_interval', 'value' => $setting->getValue('statistics_ajax_data_interval'),
|
||||
'tooltip' => 'Time in minutes, interval for hashrate and sharerate calculations. Higher intervals allow for better accuracy at a higer server load.'
|
||||
);
|
||||
$aSettings['statistics'][] = array(
|
||||
'display' => 'Block Statistics Count', 'type' => 'text',
|
||||
'size' => 25,
|
||||
'default' => 20,
|
||||
'name' => 'statistics_block_count', 'value' => $setting->getValue('statistics_block_count'),
|
||||
'tooltip' => 'Blocks to fetch for the block statistics page.'
|
||||
);
|
||||
$aSettings['statistics'][] = array(
|
||||
'display' => 'Pool Hashrate Modifier', 'type' => 'select',
|
||||
'options' => array( '1' => 'KH/s', '0.001' => 'MH/s', '0.000001' => 'GH/s' ),
|
||||
'default' => '1',
|
||||
'name' => 'statistics_pool_hashrate_modifier', 'value' => $setting->getValue('statistics_pool_hashrate_modifier'),
|
||||
'tooltip' => 'Auto-adjust displayed pool hashrates to a certain limit.'
|
||||
);
|
||||
$aSettings['statistics'][] = array(
|
||||
'display' => 'Network Hashrate Modifier', 'type' => 'select',
|
||||
'options' => array( '1' => 'KH/s', '0.001' => 'MH/s', '0.000001' => 'GH/s' ),
|
||||
'default' => '1',
|
||||
'name' => 'statistics_network_hashrate_modifier', 'value' => $setting->getValue('statistics_network_hashrate_modifier'),
|
||||
'tooltip' => 'Auto-adjust displayed network hashrates to a certain limit.'
|
||||
);
|
||||
$aSettings['statistics'][] = array(
|
||||
'display' => 'Personal Hashrate Modifier', 'type' => 'select',
|
||||
'options' => array( '1' => 'KH/s', '0.001' => 'MH/s', '0.000001' => 'GH/s' ),
|
||||
'default' => '1',
|
||||
'name' => 'statistics_personal_hashrate_modifier', 'value' => $setting->getValue('statistics_personal_hashrate_modifier'),
|
||||
'tooltip' => 'Auto-adjust displayed personal hashrates to a certain limit.'
|
||||
);
|
||||
$aSettings['acl'][] = array(
|
||||
'display' => 'Pool Statistics', 'type' => 'select',
|
||||
'options' => array( 0 => 'Private', 1 => 'Public'),
|
||||
'default' => 1,
|
||||
'name' => 'acl_pool_statistics', 'value' => $setting->getValue('acl_pool_statistics'),
|
||||
'tooltip' => 'Make the pool statistics page private (users only) or public.'
|
||||
);
|
||||
$aSettings['acl'][] = array(
|
||||
'display' => 'Block Statistics', 'type' => 'select',
|
||||
'options' => array( 0 => 'Private', 1 => 'Public'),
|
||||
'default' => 1,
|
||||
'name' => 'acl_block_statistics', 'value' => $setting->getValue('acl_block_statistics'),
|
||||
'tooltip' => 'Make the block statistics page private (users only) or public.'
|
||||
);
|
||||
$aSettings['acl'][] = array(
|
||||
'display' => 'Round Statistics', 'type' => 'select',
|
||||
'options' => array( 0 => 'Private', 1 => 'Public'),
|
||||
'default' => 1,
|
||||
'name' => 'acl_round_statistics', 'value' => $setting->getValue('acl_round_statistics'),
|
||||
'tooltip' => 'Make the round statistics page private (users only) or public.'
|
||||
);
|
||||
$aSettings['acl'][] = array(
|
||||
'display' => 'Round Transactions', 'type' => 'select',
|
||||
'options' => array( 0 => 'Admins', 1 => 'Public'),
|
||||
'default' => 0,
|
||||
'name' => 'acl_round_transactions', 'value' => $setting->getValue('acl_round_transactions'),
|
||||
'tooltip' => 'Display all transactions regardless of admin status.'
|
||||
);
|
||||
$aSettings['system'][] = array(
|
||||
'display' => 'Disable e-mail confirmations', 'type' => 'select',
|
||||
'options' => array( 0 => 'No', 1 => 'Yes' ),
|
||||
'default' => 0,
|
||||
'name' => 'accounts_confirm_email_disabled', 'value' => $setting->getValue('accounts_confirm_email_disabled'),
|
||||
'tooltip' => 'Should users supply a valid e-mail address upon registration. Requires them to confirm the address before accounts are activated.'
|
||||
);
|
||||
$aSettings['system'][] = array(
|
||||
'display' => 'Disable registrations', 'type' => 'select',
|
||||
'options' => array( 0 => 'No', 1 => 'Yes' ),
|
||||
'default' => 0,
|
||||
'name' => 'lock_registration', 'value' => $setting->getValue('lock_registration'),
|
||||
'tooltip' => 'Enable or Disable registrations. Useful to create an invitation only pool.'
|
||||
);
|
||||
$aSettings['system'][] = array(
|
||||
'display' => 'Disable Invitations', 'type' => 'select',
|
||||
'options' => array( 0 => 'No', 1 => 'Yes' ),
|
||||
'default' => 0,
|
||||
'name' => 'disable_invitations', 'value' => $setting->getValue('disable_invitations'),
|
||||
'tooltip' => 'Enable or Disable invitations. Users will not be able to invite new users via email if disabled.'
|
||||
);
|
||||
$aSettings['system'][] = array(
|
||||
'display' => 'Disable Manual Payouts', 'type' => 'select',
|
||||
'options' => array( 0 => 'No', 1 => 'Yes' ),
|
||||
'default' => 0,
|
||||
'name' => 'disable_mp', 'value' => $setting->getValue('disable_mp'),
|
||||
'tooltip' => 'Enable or Disable the manual payout processing. Users will not be able to withdraw any funds if disabled.'
|
||||
);
|
||||
$aSettings['system'][] = array(
|
||||
'display' => 'Disable Automatic Payouts', 'type' => 'select',
|
||||
'options' => array( 0 => 'No', 1 => 'Yes' ),
|
||||
'default' => 0,
|
||||
'name' => 'disable_ap', 'value' => $setting->getValue('disable_ap'),
|
||||
'tooltip' => 'Enable or Disable the automatic payout processing. Users exceeding their thresholds will not be paid out if disabled.'
|
||||
);
|
||||
$aSettings['system'][] = array(
|
||||
'display' => 'Disable notifications', 'type' => 'select',
|
||||
'options' => array( 0 => 'No', 1 => 'Yes' ),
|
||||
'default' => 0,
|
||||
'name' => 'disable_notifications', 'value' => $setting->getValue('disable_notifications'),
|
||||
'tooltip' => 'Enable or Disable system notifications. This includes new found blocks, monitoring and all other notifications.'
|
||||
);
|
||||
$aSettings['system'][] = array(
|
||||
'display' => 'Disable API', 'type' => 'select',
|
||||
'options' => array( 0 => 'No', 1 => 'Yes' ),
|
||||
'default' => 0,
|
||||
'name' => 'disable_api', 'value' => $setting->getValue('disable_api'),
|
||||
'tooltip' => 'Enable or Disable the pool wide API functions. See API reference on Github for details.'
|
||||
);
|
||||
$aSettings['system'][] = array(
|
||||
'display' => 'Disable Contactform', 'type' => 'select',
|
||||
'options' => array( 0 => 'No', 1 => 'Yes' ),
|
||||
'default' => 0,
|
||||
'name' => 'disable_contactform', 'value' => $setting->getValue('disable_contactform'),
|
||||
'tooltip' => 'Enable or Disable Contactform. Users will not be able to use the contact form.'
|
||||
);
|
||||
$aSettings['recaptcha'][] = array(
|
||||
'display' => 'Enable re-Captcha', 'type' => 'select',
|
||||
'options' => array( 0 => 'No', 1 => 'Yes' ),
|
||||
'default' => 0,
|
||||
'name' => 'recaptcha_enabled', 'value' => $setting->getValue('recaptcha_enabled'),
|
||||
'tooltip' => 'Enable or Disable re-Captcha. This will require user input on registraion and other forms.'
|
||||
);
|
||||
$aSettings['recaptcha'][] = array(
|
||||
'display' => 're-Captcha Private Key', 'type' => 'text',
|
||||
'size' => 25,
|
||||
'default' => 'YOUR_PRIVATE_KEY',
|
||||
'name' => 'recaptcha_private_key', 'value' => $setting->getValue('recaptcha_private_key'),
|
||||
'tooltip' => '.'
|
||||
);
|
||||
$aSettings['recaptcha'][] = array(
|
||||
'display' => 're-Captcha Public Key', 'type' => 'text',
|
||||
'size' => 25,
|
||||
'default' => 'YOUR_PUBLIC_KEY',
|
||||
'name' => 'recaptcha_public_key', 'value' => $setting->getValue('recaptcha_public_key'),
|
||||
'tooltip' => 'Your public key as given by your re-Captcha account.'
|
||||
);
|
||||
|
||||
?>
|
||||
@ -1,10 +1,6 @@
|
||||
<?php
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
|
||||
// What is our overall theme
|
||||
define('THEME', 'mmcFE');
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
// Our include directory for additional features
|
||||
define('INCLUDE_DIR', BASEPATH . 'include');
|
||||
@ -15,73 +11,406 @@ define('CLASS_DIR', INCLUDE_DIR . '/classes');
|
||||
// Our pages directory which takes care of
|
||||
define('PAGES_DIR', INCLUDE_DIR . '/pages');
|
||||
|
||||
// Our theme folder holding all themes
|
||||
define('THEME_DIR', BASEPATH . 'templates');
|
||||
|
||||
// Set debugging level for our debug class
|
||||
define('DEBUG', 0);
|
||||
|
||||
// SALT used to hash passwords
|
||||
define('SALT', 'PLEASEMAKEMESOMETHINGRANDOM');
|
||||
|
||||
$config = array(
|
||||
'price' => array(
|
||||
'url' => 'https://btc-e.com/api/2',
|
||||
'target' => '/ltc_usd/ticker',
|
||||
'currency' => 'USD' // Used in ministats template
|
||||
),
|
||||
'ap_threshold' => array(
|
||||
'min' => 1,
|
||||
'max' => 250
|
||||
),
|
||||
'website' => array(
|
||||
'registration' => true, // Allow new users to register
|
||||
'name' => 'The Pool',
|
||||
'slogan' => 'Resistance is futile',
|
||||
'email' => 'test@example.com', // Mail address used for notifications
|
||||
),
|
||||
// See: http://www.google.com/recaptcha
|
||||
'recaptcha' => array(
|
||||
'enabled' => false, // Enable re-captcha during registraion
|
||||
'public_key' => 'YOUR_PUBLIC_RECAPTCHA_KEY',
|
||||
'private_key' => 'YOUR_PRIVATE_RECAPTCHA_KEY'
|
||||
),
|
||||
'currency' => 'LTC', // Currency name to be used on website
|
||||
'txfee' => 0.1, // Default tx fee added by RPC server
|
||||
'block_bonus' => 0,
|
||||
'payout_system' => 'prop', // Set your payout here so template changes are activated
|
||||
'archive_shares' => true, // Store accounted shares in archive table?
|
||||
'blockexplorer' => 'http://explorer.litecoin.net/search?q=', // URL for block searches, prefixed to each block number
|
||||
'chaininfo' => 'http://allchains.info', // Link to Allchains for Difficulty information
|
||||
'fees' => 0,
|
||||
'difficulty' => '20', // Target difficulty for this pool as set in pushpoold json
|
||||
'reward' => '50', // Reward for finding blocks, fixed value but changes someday
|
||||
'confirmations' => '120', // Confirmations per block needed to credit transactions
|
||||
'memcache' => array(
|
||||
'enabled' => true,
|
||||
'host' => 'localhost', // Memcache Host
|
||||
'port' => 11211, // Memcache Port
|
||||
'keyprefix' => 'mmcfe_ng_', // Prefix for all keys
|
||||
'expiration'=> '90', // Cache time
|
||||
'splay' => '15' // Splay time
|
||||
),
|
||||
'wallet' => array(
|
||||
'type' => 'http', // http or https are supported
|
||||
'host' => 'localhost:9332',
|
||||
'username' => 'litecoinrpc',
|
||||
'password' => 'somepass'
|
||||
),
|
||||
'cashout' => array(
|
||||
'min_balance' => 0.0 // Minimal balance to cash out
|
||||
),
|
||||
'cookie' => array(
|
||||
'path' => '/',
|
||||
'name' => 'POOLERCOOKIE',
|
||||
'domain' => ''
|
||||
),
|
||||
'cache' => 0, // 1 to enable smarty cache in templates/cache
|
||||
'db' => array(
|
||||
'host' => 'localhost',
|
||||
'user' => 'someuser',
|
||||
'pass' => 'somepass',
|
||||
'port' => '3306',
|
||||
'name' => 'litecoin',
|
||||
),
|
||||
);
|
||||
/**
|
||||
* Database configuration
|
||||
*
|
||||
* A MySQL database backend is required for MPOS.
|
||||
* Also ensure the database structure is imported!
|
||||
* The SQL file should be included in this project under the `sql` directory
|
||||
*
|
||||
* Default:
|
||||
* host = 'localhost'
|
||||
* port = 3306
|
||||
* user = 'someuser'
|
||||
* pass = 'somepass'
|
||||
* name = 'mpos'
|
||||
**/
|
||||
$config['db']['host'] = 'localhost';
|
||||
$config['db']['user'] = 'someuser';
|
||||
$config['db']['pass'] = 'somepass';
|
||||
$config['db']['port'] = 3306;
|
||||
$config['db']['name'] = 'mpos';
|
||||
|
||||
/**
|
||||
* Local wallet RPC configuration
|
||||
*
|
||||
* MPOS uses the RPC backend to fetch transactions, blocks
|
||||
* and various other things. They need to match your coind RPC
|
||||
* configuration.
|
||||
*
|
||||
* Default:
|
||||
* type = 'http'
|
||||
* host = 'localhost:19334'
|
||||
* username = 'testnet'
|
||||
* password = 'testnet'
|
||||
**/
|
||||
$config['wallet']['type'] = 'http';
|
||||
$config['wallet']['host'] = 'localhost:19334';
|
||||
$config['wallet']['username'] = 'testnet';
|
||||
$config['wallet']['password'] = 'testnet';
|
||||
|
||||
/**
|
||||
* API configuration to fetch prices for set currency
|
||||
*
|
||||
* Explanation:
|
||||
* MPOS will try to fetch the current exchange rates
|
||||
* from this API URL/target. Currently btc-e and coinchoose
|
||||
* are supported in MPOS. If you want to remove the trade
|
||||
* header just set currency to an empty string.
|
||||
*
|
||||
* Default (btc-e.com):
|
||||
* url = `https://btc-e.com`
|
||||
* target = `/api/2/ltc_usd/ticker`
|
||||
* currency = `USD`
|
||||
*
|
||||
* Optional (coinchoose.com):
|
||||
* url = `http://www.coinchoose.com`
|
||||
* target = `/api.php`
|
||||
* currency = `BTC`
|
||||
*
|
||||
* Optional (cryptsy.com):
|
||||
* url = `http://pubapi.cryptsy.com`
|
||||
* currency = `BTC`
|
||||
* target = `/api.php?method=marketdata`
|
||||
**/
|
||||
$config['price']['url'] = 'https://btc-e.com';
|
||||
$config['price']['target'] = '/api/2/ltc_usd/ticker';
|
||||
$config['price']['currency'] = 'USD';
|
||||
|
||||
|
||||
/**
|
||||
* Automatic payout thresholds
|
||||
*
|
||||
* These values define the min and max settings
|
||||
* that can be entered by a user.
|
||||
* Defaults:
|
||||
* `min` = `1`
|
||||
* `max` = `250`
|
||||
**/
|
||||
$config['ap_threshold']['min'] = 1;
|
||||
$config['ap_threshold']['max'] = 250;
|
||||
|
||||
|
||||
/**
|
||||
* Account specific settings
|
||||
*
|
||||
* Explanation
|
||||
* Invitations will allow your users to invite new members to join the pool.
|
||||
* After sending a mail to the invited user, they can register using the token
|
||||
* created. Invitations can be enabled and disabled through the admin panel.
|
||||
* Sent invitations are listed on the account invitations page.
|
||||
*
|
||||
* You can limit the number of registrations send per account via configuration
|
||||
* variable.
|
||||
*
|
||||
* Options:
|
||||
* count : Maximum invitations a user is able to send
|
||||
*
|
||||
* Defaults:
|
||||
* count : 5
|
||||
**/
|
||||
$config['accounts']['invitations']['count'] = 5;
|
||||
|
||||
// Currency system used in this pool, default: `LTC`
|
||||
$config['currency'] = 'LTC';
|
||||
|
||||
/**
|
||||
* Default transaction fee to apply to user transactions
|
||||
*
|
||||
* Explanation
|
||||
* The coin daemon applies transcation fees to young coins.
|
||||
* Since we are unable to find out what the exact fee was we set
|
||||
* a default value here which is applied to both manual and auto payouts.
|
||||
* If this is not set, no fee is applied in the transactions history but
|
||||
* the user might still see them when the coins arrive.
|
||||
*
|
||||
* Default:
|
||||
* txfee = 0.1
|
||||
**/
|
||||
$config['txfee'] = 0.1;
|
||||
|
||||
// Payout a block bonus to block finders, default: 0 (disabled)
|
||||
// This bonus is paid by the pool operator, it is not deducted from the block payout!
|
||||
$config['block_bonus'] = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Payout sytem in use
|
||||
*
|
||||
* This will modify some templates and activate the
|
||||
* appropriate crons. Only ONE payout system at a time
|
||||
* is supported!
|
||||
*
|
||||
* Available options:
|
||||
* prop: Proportional payout system
|
||||
* pps : Pay Per Share payout system
|
||||
* pplns : Pay Per Last N Shares payout system
|
||||
*
|
||||
* Default:
|
||||
* prop
|
||||
**/
|
||||
$config['payout_system'] = 'prop';
|
||||
|
||||
/**
|
||||
* Archiving configuration for debugging
|
||||
*
|
||||
* Explanation:
|
||||
* By default, we don't need to archive for a long time. PPLNS and Hashrate
|
||||
* calculations rely on this archive, but all shares past a certain point can
|
||||
* safely be deleted.
|
||||
*
|
||||
* To ensure we have enough shares on stack for PPLNS, this
|
||||
* is set to the past 10 rounds. Even with lucky ones in between those should
|
||||
* fit the PPLNS target. On top of that, even if we have more than 10 rounds,
|
||||
* we still keep the last maxage shares to ensure we can calculate hashrates.
|
||||
* Both conditions need to be met in order for shares to be purged from archive.
|
||||
*
|
||||
* Proportional mode will only keep the past 24 hours. These are required for
|
||||
* hashrate calculations to work past a round, hence 24 hours was selected as
|
||||
* the default. You may want to increase the time for debugging, then add any
|
||||
* integer reflecting minutes of shares to keep.
|
||||
*
|
||||
* Availabe Options:
|
||||
* maxrounds : PPLNS, keep shares for maxrounds
|
||||
* maxage : PROP and PPLNS, delete shares older than maxage minutes
|
||||
*
|
||||
* Default:
|
||||
* maxrounds = 10
|
||||
* maxage = 60 * 24 (24h)
|
||||
**/
|
||||
$config['archive']['maxrounds'] = 10;
|
||||
$config['archive']['maxage'] = 60 * 24;
|
||||
|
||||
// Pool fees applied to users in percent, default: 0 (disabled)
|
||||
$config['fees'] = 0;
|
||||
|
||||
/**
|
||||
* PPLNS requires some settings to run properly. First we need to define
|
||||
* a default shares count that is applied if we don't have a proper type set.
|
||||
* Different dynamic types can be applied, or you can run a fixed scheme.
|
||||
*
|
||||
* Explanation
|
||||
*
|
||||
* PPLNS can run on two different payouts: fixed and blockavg. Each one
|
||||
* defines a different PPLNS target.
|
||||
*
|
||||
* Fixed means we will be looking at the shares setup in the default
|
||||
* setting. There is no automatic adjustments to the PPLNS target,
|
||||
* all users will be paid out proportionally to that target.
|
||||
*
|
||||
* Blockavg will look at the last blockcount blocks shares and take
|
||||
* the average as the PPLNS target. This will be automatically adjusted
|
||||
* when difficulty changes and more blocks are available. This keeps the
|
||||
* target dynamic but still traceable.
|
||||
*
|
||||
* If you use the fixed type it will use $config['pplns']['shares']['default']
|
||||
* for target calculations, if you use blockavg type it will use
|
||||
* $config['pplns']['blockavg']['blockcount'] blocks average for target
|
||||
* calculations.
|
||||
*
|
||||
* default : Default target shares for PPLNS
|
||||
* type : Payout type used in PPLNS
|
||||
* blockcount : Amount of blocks to check for avg shares
|
||||
*
|
||||
* Available Options:
|
||||
* default : amount of shares, integeger
|
||||
* type : blockavg or fixed
|
||||
* blockcount : amount of blocks, any integer
|
||||
*
|
||||
* Defaults:
|
||||
* default = 4000000
|
||||
* type = `blockavg`
|
||||
* blockcount = 10
|
||||
**/
|
||||
$config['pplns']['shares']['default'] = 4000000;
|
||||
$config['pplns']['shares']['type'] = 'blockavg';
|
||||
$config['pplns']['blockavg']['blockcount'] = 10;
|
||||
|
||||
// Pool target difficulty as set in pushpoold configuration file
|
||||
// Please also read this for stratum: https://github.com/TheSerapher/php-mpos/wiki/FAQ
|
||||
$config['difficulty'] = 20;
|
||||
|
||||
|
||||
/**
|
||||
* This defines how rewards are paid to users.
|
||||
*
|
||||
* Explanation:
|
||||
*
|
||||
* Proportional + PPLNS Payout System
|
||||
* When running a pool on fixed mode, each block will be paid
|
||||
* out as defined in `reward`. If you wish to pass transaction
|
||||
* fees inside discovered blocks on to user, set this to `block`.
|
||||
* This is really helpful for altcoins with dynamic block values!
|
||||
*
|
||||
* PPS Payout System
|
||||
* If set to `fixed`, all PPS values are based on the `reward` setting.
|
||||
* If you set it to `block` you will calculate the current round based
|
||||
* on the previous block value. The idea is to pass the block of the
|
||||
* last round on to the users. If no previous block is found, PPS value
|
||||
* will fall back to the fixed value set in `reward`. Ensure you don't
|
||||
* overpay users in the first round!
|
||||
*
|
||||
* Available options:
|
||||
* reward_type:
|
||||
* fixed : Fixed value according to `reward` setting
|
||||
* block : Dynamic value based on block amount
|
||||
* reward:
|
||||
* float value : Any value of your choice but should reflect base block values
|
||||
*
|
||||
* Default:
|
||||
* reward_type = `fixed`
|
||||
* reward = 50
|
||||
*
|
||||
**/
|
||||
$config['reward_type'] = 'fixed';
|
||||
$config['reward'] = 50;
|
||||
|
||||
// Confirmations per block required to credit transactions, default: 120
|
||||
$config['confirmations'] = 120;
|
||||
// Confirmations per block required in network to confirm its transactions, default: 120
|
||||
$config['network_confirmations'] = 120;
|
||||
|
||||
/**
|
||||
* Available pps options:
|
||||
* reward_type:
|
||||
* fixed : Fixed value according to `reward` setting
|
||||
* blockavg : Dynamic value based on average of x number of block rewards
|
||||
* block : Dynamic value based on LAST block amount
|
||||
* reward:
|
||||
* float value : Any value of your choice but should reflect base block values
|
||||
* blockcount : amount of blocks to average, any integer
|
||||
* Default:
|
||||
* pps_reward_type = `fixed` default $config['pps']['reward']['default']
|
||||
* reward = 50
|
||||
*
|
||||
**/
|
||||
$config['pps']['reward']['default'] = 50;
|
||||
$config['pps']['reward']['type'] = 'blockavg';
|
||||
$config['pps']['blockavg']['blockcount'] = 10;
|
||||
|
||||
// pps base payout target, default 16 = difficulty 1 shares for vardiff
|
||||
// (1/(65536 * difficulty) * reward) = (reward / (pow(2,32) * difficulty) * pow(2, 16))
|
||||
$config['pps_target'] = 16; // do not change unless you know what it does
|
||||
|
||||
/**
|
||||
* Memcache configuration
|
||||
*
|
||||
* To disable memcache set option $config['memcache']['enabled'] = false
|
||||
* After disable memcache installation of memcache is not required.
|
||||
*
|
||||
* Please note that a memcache is greatly increasing performance
|
||||
* when combined with the `statistics.php` cronjob. Disabling this
|
||||
* is not recommended in a live environment!
|
||||
*
|
||||
* Explanations
|
||||
* enabled : Disable (false) memcache for debugging or enable (true) it
|
||||
* host : Host IP or hostname
|
||||
* port : memcache port
|
||||
* keyprefix : Must be changed for multiple MPOS instances on one host
|
||||
* expiration : Default expiration time in seconds of all cached keys.
|
||||
* Increase if caches expire too fast.
|
||||
* splay : Default randomizer for expiration times.
|
||||
* This will spread expired keys across `splay` seconds.
|
||||
*
|
||||
* Default:
|
||||
* enabled = `true`
|
||||
* host = `localhost`
|
||||
* port = 11211
|
||||
* keyprefix = `mpos_`
|
||||
* expiration = 90
|
||||
* splay = 15
|
||||
**/
|
||||
$config['memcache']['enabled'] = true;
|
||||
$config['memcache']['host'] = 'localhost';
|
||||
$config['memcache']['port'] = 11211;
|
||||
$config['memcache']['keyprefix'] = 'mpos_';
|
||||
$config['memcache']['expiration'] = 90;
|
||||
$config['memcache']['splay'] = 15;
|
||||
|
||||
|
||||
/**
|
||||
* Cookie configiration
|
||||
*
|
||||
* You can configure the cookie behaviour to secure your cookies more than the PHP defaults
|
||||
*
|
||||
* For multiple installations of MPOS on the same domain you must change the cookie path.
|
||||
*
|
||||
* Explanation:
|
||||
* duration:
|
||||
* the amount of time, in seconds, that a cookie should persist in the users browser.
|
||||
* 0 = until closed; 1440 = 24 minutes. Check your php.ini 'session.gc_maxlifetime' value
|
||||
* and ensure that it is at least the duration specified here.
|
||||
*
|
||||
* domain:
|
||||
* the only domain name that may access this cookie in the browser
|
||||
*
|
||||
* path:
|
||||
* the highest path on the domain that can access this cookie; i.e. if running two pools
|
||||
* from a single domain you might set the path /ltc/ and /ftc/ to separate user session
|
||||
* cookies between the two.
|
||||
*
|
||||
* httponly:
|
||||
* marks the cookie as accessible only through the HTTP protocol. The cookie can't be
|
||||
* accessed by scripting languages, such as JavaScript. This can help to reduce identity
|
||||
* theft through XSS attacks in most browsers.
|
||||
*
|
||||
* secure:
|
||||
* marks the cookie as accessible only through the HTTPS protocol. If you have a SSL
|
||||
* certificate installed on your domain name then this will stop a user accidently
|
||||
* accessing the site over a HTTP connection, without SSL, exposing their session cookie.
|
||||
*
|
||||
* Default:
|
||||
* duration = '1440'
|
||||
* domain = ''
|
||||
* path = '/'
|
||||
* httponly = true
|
||||
* secure = false
|
||||
**/
|
||||
$config['cookie']['duration'] = '1440';
|
||||
$config['cookie']['domain'] = '';
|
||||
$config['cookie']['path'] = '/';
|
||||
$config['cookie']['httponly'] = true;
|
||||
$config['cookie']['secure'] = false;
|
||||
|
||||
/**
|
||||
* Enable or disable the Smarty cache
|
||||
*
|
||||
* Explanation:
|
||||
* Smarty implements a file based cache for all HTML output generated
|
||||
* from dynamic scripts. It can be enabled to cache the HTML data on disk,
|
||||
* future request are served from those cache files.
|
||||
*
|
||||
* This may or may not work as expected, in general Memcache is used to cache
|
||||
* all data so rendering the page should not take too long anyway.
|
||||
*
|
||||
* You can test this out and enable (1) this setting but it's not guaranteed to
|
||||
* work with MPOS.
|
||||
*
|
||||
* Ensure that the folder `templates/cache` is writable by the webserver!
|
||||
*
|
||||
* cache = Enable/Disable the cache
|
||||
* cache_lifetime = Time to keep files in seconds before updating them
|
||||
*
|
||||
* Options:
|
||||
* cache:
|
||||
* 0 = disabled
|
||||
* 1 = enabled
|
||||
* cache_lifetime:
|
||||
* time in seconds
|
||||
*
|
||||
* Defaults:
|
||||
* cache = 0, disabled
|
||||
* cache_lifetime = 30 seconds
|
||||
**/
|
||||
$config['smarty']['cache'] = 0;
|
||||
$config['smarty']['cache_lifetime'] = 30;
|
||||
?>
|
||||
|
||||
5
public/include/config/memcache_keys.inc.php
Normal file
5
public/include/config/memcache_keys.inc.php
Normal file
@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
define('STATISTICS_ALL_USER_SHARES', 'STATISTICS_ALL_USER_SHARES');
|
||||
define('STATISTICS_ROUND_SHARES', 'STATISTICS_ROUND_SHARES');
|
||||
?>
|
||||
148
public/include/lib/KLogger.php
Executable file
148
public/include/lib/KLogger.php
Executable file
@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
/* Finally, A light, permissions-checking logging class.
|
||||
*
|
||||
* Author : Kenneth Katzgrau < katzgrau@gmail.com >
|
||||
* Date : July 26, 2008
|
||||
* Comments : Originally written for use with wpSearch
|
||||
* Website : http://codefury.net
|
||||
* Version : 1.0
|
||||
*
|
||||
* Usage:
|
||||
* $log = new KLogger ( "log.txt" , KLogger::INFO );
|
||||
* $log->LogInfo("Returned a million search results"); //Prints to the log file
|
||||
* $log->LogFATAL("Oh dear."); //Prints to the log file
|
||||
* $log->LogDebug("x = 5"); //Prints nothing due to priority setting
|
||||
*/
|
||||
|
||||
class KLogger
|
||||
{
|
||||
|
||||
const DEBUG = 1; // Most Verbose
|
||||
const INFO = 2; // ...
|
||||
const WARN = 3; // ...
|
||||
const ERROR = 4; // ...
|
||||
const FATAL = 5; // Least Verbose
|
||||
const OFF = 6; // Nothing at all.
|
||||
|
||||
const LOG_OPEN = 1;
|
||||
const OPEN_FAILED = 2;
|
||||
const LOG_CLOSED = 3;
|
||||
|
||||
/* Public members: Not so much of an example of encapsulation, but that's okay. */
|
||||
public $Log_Status = KLogger::LOG_CLOSED;
|
||||
public $DateFormat = "Y-m-d G:i:s";
|
||||
public $MessageQueue;
|
||||
|
||||
private $log_file;
|
||||
private $priority = KLogger::INFO;
|
||||
|
||||
private $file_handle;
|
||||
|
||||
public function __construct( $filepath , $priority )
|
||||
{
|
||||
if ( $priority == KLogger::OFF ) return;
|
||||
|
||||
$this->log_file = $filepath;
|
||||
$this->MessageQueue = array();
|
||||
$this->priority = $priority;
|
||||
|
||||
if ( file_exists( $this->log_file ) )
|
||||
{
|
||||
if ( !is_writable($this->log_file) )
|
||||
{
|
||||
$this->Log_Status = KLogger::OPEN_FAILED;
|
||||
$this->MessageQueue[] = "The file exists, but could not be opened for writing. Check that appropriate permissions have been set.";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $this->file_handle = fopen( $this->log_file , "a" ) )
|
||||
{
|
||||
$this->Log_Status = KLogger::LOG_OPEN;
|
||||
$this->MessageQueue[] = "The log file was opened successfully.";
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->Log_Status = KLogger::OPEN_FAILED;
|
||||
$this->MessageQueue[] = "The file could not be opened. Check permissions.";
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
if ( $this->file_handle )
|
||||
fclose( $this->file_handle );
|
||||
}
|
||||
|
||||
public function LogInfo($line)
|
||||
{
|
||||
$this->Log( $line , KLogger::INFO );
|
||||
}
|
||||
|
||||
public function LogDebug($line)
|
||||
{
|
||||
$this->Log( $line , KLogger::DEBUG );
|
||||
}
|
||||
|
||||
public function LogWarn($line)
|
||||
{
|
||||
$this->Log( $line , KLogger::WARN );
|
||||
}
|
||||
|
||||
public function LogError($line)
|
||||
{
|
||||
$this->Log( $line , KLogger::ERROR );
|
||||
}
|
||||
|
||||
public function LogFatal($line)
|
||||
{
|
||||
$this->Log( $line , KLogger::FATAL );
|
||||
}
|
||||
|
||||
public function Log($line, $priority)
|
||||
{
|
||||
if ( $this->priority <= $priority )
|
||||
{
|
||||
$status = $this->getTimeLine( $priority );
|
||||
$this->WriteFreeFormLine ( "$status $line \n" );
|
||||
}
|
||||
}
|
||||
|
||||
public function WriteFreeFormLine( $line )
|
||||
{
|
||||
if ( $this->Log_Status == KLogger::LOG_OPEN && $this->priority != KLogger::OFF )
|
||||
{
|
||||
if (fwrite( $this->file_handle , $line ) === false) {
|
||||
$this->MessageQueue[] = "The file could not be written to. Check that appropriate permissions have been set.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getTimeLine( $level )
|
||||
{
|
||||
$time = date( $this->DateFormat );
|
||||
|
||||
switch( $level )
|
||||
{
|
||||
case KLogger::INFO:
|
||||
return "$time - INFO -->";
|
||||
case KLogger::WARN:
|
||||
return "$time - WARN -->";
|
||||
case KLogger::DEBUG:
|
||||
return "$time - DEBUG -->";
|
||||
case KLogger::ERROR:
|
||||
return "$time - ERROR -->";
|
||||
case KLogger::FATAL:
|
||||
return "$time - FATAL -->";
|
||||
default:
|
||||
return "$time - LOG -->";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
3094
public/include/lib/Michelf/Markdown.php
Normal file
3094
public/include/lib/Michelf/Markdown.php
Normal file
File diff suppressed because it is too large
Load Diff
40
public/include/lib/Michelf/MarkdownExtra.php
Normal file
40
public/include/lib/Michelf/MarkdownExtra.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
#
|
||||
# Markdown Extra - A text-to-HTML conversion tool for web writers
|
||||
#
|
||||
# PHP Markdown Extra
|
||||
# Copyright (c) 2004-2013 Michel Fortin
|
||||
# <http://michelf.com/projects/php-markdown/>
|
||||
#
|
||||
# Original Markdown
|
||||
# Copyright (c) 2004-2006 John Gruber
|
||||
# <http://daringfireball.net/projects/markdown/>
|
||||
#
|
||||
namespace Michelf;
|
||||
|
||||
|
||||
# Just force Michelf/Markdown.php to load. This is needed to load
|
||||
# the temporary implementation class. See below for details.
|
||||
\Michelf\Markdown::MARKDOWNLIB_VERSION;
|
||||
|
||||
#
|
||||
# Markdown Extra Parser Class
|
||||
#
|
||||
# Note: Currently the implementation resides in the temporary class
|
||||
# \Michelf\MarkdownExtra_TmpImpl (in the same file as \Michelf\Markdown).
|
||||
# This makes it easier to propagate the changes between the three different
|
||||
# packaging styles of PHP Markdown. Once this issue is resolved, the
|
||||
# _MarkdownExtra_TmpImpl will disappear and this one will contain the code.
|
||||
#
|
||||
|
||||
class MarkdownExtra extends \Michelf\_MarkdownExtra_TmpImpl {
|
||||
|
||||
### Parser Implementation ###
|
||||
|
||||
# Temporarily, the implemenation is in the _MarkdownExtra_TmpImpl class.
|
||||
# See note above.
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
814
public/include/lib/Mobile_Detect.php
Normal file
814
public/include/lib/Mobile_Detect.php
Normal file
@ -0,0 +1,814 @@
|
||||
<?php
|
||||
/**
|
||||
* MIT License
|
||||
* ===========
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*
|
||||
* @author Serban Ghita <serbanghita@gmail.com>
|
||||
* Victor Stanciu <vic.stanciu@gmail.com> (until v. 1.0)
|
||||
* @license MIT License https://github.com/serbanghita/Mobile-Detect/blob/master/LICENSE.txt
|
||||
* @link Official page: http://mobiledetect.net
|
||||
* GitHub Repository: https://github.com/serbanghita/Mobile-Detect
|
||||
* Google Code Old Page: http://code.google.com/p/php-mobile-detect/
|
||||
* @version 2.6.2
|
||||
*/
|
||||
|
||||
class Mobile_Detect {
|
||||
|
||||
protected $scriptVersion = '2.6.2';
|
||||
|
||||
// External info.
|
||||
protected $userAgent = null;
|
||||
protected $httpHeaders;
|
||||
|
||||
// Arrays holding all detection rules.
|
||||
protected $mobileDetectionRules = null;
|
||||
protected $mobileDetectionRulesExtended = null;
|
||||
// Type of detection to use.
|
||||
protected $detectionType = 'mobile'; // mobile, extended @todo: refactor this.
|
||||
|
||||
// List of mobile devices (phones)
|
||||
protected $phoneDevices = array(
|
||||
'iPhone' => '\biPhone.*Mobile|\biPod|\biTunes',
|
||||
'BlackBerry' => 'BlackBerry|\bBB10\b|rim[0-9]+',
|
||||
'HTC' => 'HTC|HTC.*(Sensation|Evo|Vision|Explorer|6800|8100|8900|A7272|S510e|C110e|Legend|Desire|T8282)|APX515CKT|Qtek9090|APA9292KT|HD_mini|Sensation.*Z710e|PG86100|Z715e|Desire.*(A8181|HD)|ADR6200|ADR6425|001HT|Inspire 4G|Android.*\bEVO\b',
|
||||
'Nexus' => 'Nexus One|Nexus S|Galaxy.*Nexus|Android.*Nexus.*Mobile',
|
||||
// @todo: Is 'Dell Streak' a tablet or a phone? ;)
|
||||
'Dell' => 'Dell.*Streak|Dell.*Aero|Dell.*Venue|DELL.*Venue Pro|Dell Flash|Dell Smoke|Dell Mini 3iX|XCD28|XCD35|\b001DL\b|\b101DL\b|\bGS01\b',
|
||||
'Motorola' => 'Motorola|\bDroid\b.*Build|DROIDX|Android.*Xoom|HRI39|MOT-|A1260|A1680|A555|A853|A855|A953|A955|A956|Motorola.*ELECTRIFY|Motorola.*i1|i867|i940|MB200|MB300|MB501|MB502|MB508|MB511|MB520|MB525|MB526|MB611|MB612|MB632|MB810|MB855|MB860|MB861|MB865|MB870|ME501|ME502|ME511|ME525|ME600|ME632|ME722|ME811|ME860|ME863|ME865|MT620|MT710|MT716|MT720|MT810|MT870|MT917|Motorola.*TITANIUM|WX435|WX445|XT300|XT301|XT311|XT316|XT317|XT319|XT320|XT390|XT502|XT530|XT531|XT532|XT535|XT603|XT610|XT611|XT615|XT681|XT701|XT702|XT711|XT720|XT800|XT806|XT860|XT862|XT875|XT882|XT883|XT894|XT909|XT910|XT912|XT928',
|
||||
'Samsung' => 'Samsung|SGH-I337|BGT-S5230|GT-B2100|GT-B2700|GT-B2710|GT-B3210|GT-B3310|GT-B3410|GT-B3730|GT-B3740|GT-B5510|GT-B5512|GT-B5722|GT-B6520|GT-B7300|GT-B7320|GT-B7330|GT-B7350|GT-B7510|GT-B7722|GT-B7800|GT-C3010|GT-C3011|GT-C3060|GT-C3200|GT-C3212|GT-C3212I|GT-C3262|GT-C3222|GT-C3300|GT-C3300K|GT-C3303|GT-C3303K|GT-C3310|GT-C3322|GT-C3330|GT-C3350|GT-C3500|GT-C3510|GT-C3530|GT-C3630|GT-C3780|GT-C5010|GT-C5212|GT-C6620|GT-C6625|GT-C6712|GT-E1050|GT-E1070|GT-E1075|GT-E1080|GT-E1081|GT-E1085|GT-E1087|GT-E1100|GT-E1107|GT-E1110|GT-E1120|GT-E1125|GT-E1130|GT-E1160|GT-E1170|GT-E1175|GT-E1180|GT-E1182|GT-E1200|GT-E1210|GT-E1225|GT-E1230|GT-E1390|GT-E2100|GT-E2120|GT-E2121|GT-E2152|GT-E2220|GT-E2222|GT-E2230|GT-E2232|GT-E2250|GT-E2370|GT-E2550|GT-E2652|GT-E3210|GT-E3213|GT-I5500|GT-I5503|GT-I5700|GT-I5800|GT-I5801|GT-I6410|GT-I6420|GT-I7110|GT-I7410|GT-I7500|GT-I8000|GT-I8150|GT-I8160|GT-I8320|GT-I8330|GT-I8350|GT-I8530|GT-I8700|GT-I8703|GT-I8910|GT-I9000|GT-I9001|GT-I9003|GT-I9010|GT-I9020|GT-I9023|GT-I9070|GT-I9100|GT-I9103|GT-I9220|GT-I9250|GT-I9300|GT-I9505|GT-M3510|GT-M5650|GT-M7500|GT-M7600|GT-M7603|GT-M8800|GT-M8910|GT-N7000|GT-S3110|GT-S3310|GT-S3350|GT-S3353|GT-S3370|GT-S3650|GT-S3653|GT-S3770|GT-S3850|GT-S5210|GT-S5220|GT-S5229|GT-S5230|GT-S5233|GT-S5250|GT-S5253|GT-S5260|GT-S5263|GT-S5270|GT-S5300|GT-S5330|GT-S5350|GT-S5360|GT-S5363|GT-S5369|GT-S5380|GT-S5380D|GT-S5560|GT-S5570|GT-S5600|GT-S5603|GT-S5610|GT-S5620|GT-S5660|GT-S5670|GT-S5690|GT-S5750|GT-S5780|GT-S5830|GT-S5839|GT-S6102|GT-S6500|GT-S7070|GT-S7200|GT-S7220|GT-S7230|GT-S7233|GT-S7250|GT-S7500|GT-S7530|GT-S7550|GT-S7562|GT-S8000|GT-S8003|GT-S8500|GT-S8530|GT-S8600|SCH-A310|SCH-A530|SCH-A570|SCH-A610|SCH-A630|SCH-A650|SCH-A790|SCH-A795|SCH-A850|SCH-A870|SCH-A890|SCH-A930|SCH-A950|SCH-A970|SCH-A990|SCH-I100|SCH-I110|SCH-I400|SCH-I405|SCH-I500|SCH-I510|SCH-I515|SCH-I600|SCH-I730|SCH-I760|SCH-I770|SCH-I830|SCH-I910|SCH-I920|SCH-LC11|SCH-N150|SCH-N300|SCH-R100|SCH-R300|SCH-R351|SCH-R400|SCH-R410|SCH-T300|SCH-U310|SCH-U320|SCH-U350|SCH-U360|SCH-U365|SCH-U370|SCH-U380|SCH-U410|SCH-U430|SCH-U450|SCH-U460|SCH-U470|SCH-U490|SCH-U540|SCH-U550|SCH-U620|SCH-U640|SCH-U650|SCH-U660|SCH-U700|SCH-U740|SCH-U750|SCH-U810|SCH-U820|SCH-U900|SCH-U940|SCH-U960|SCS-26UC|SGH-A107|SGH-A117|SGH-A127|SGH-A137|SGH-A157|SGH-A167|SGH-A177|SGH-A187|SGH-A197|SGH-A227|SGH-A237|SGH-A257|SGH-A437|SGH-A517|SGH-A597|SGH-A637|SGH-A657|SGH-A667|SGH-A687|SGH-A697|SGH-A707|SGH-A717|SGH-A727|SGH-A737|SGH-A747|SGH-A767|SGH-A777|SGH-A797|SGH-A817|SGH-A827|SGH-A837|SGH-A847|SGH-A867|SGH-A877|SGH-A887|SGH-A897|SGH-A927|SGH-B100|SGH-B130|SGH-B200|SGH-B220|SGH-C100|SGH-C110|SGH-C120|SGH-C130|SGH-C140|SGH-C160|SGH-C170|SGH-C180|SGH-C200|SGH-C207|SGH-C210|SGH-C225|SGH-C230|SGH-C417|SGH-C450|SGH-D307|SGH-D347|SGH-D357|SGH-D407|SGH-D415|SGH-D780|SGH-D807|SGH-D980|SGH-E105|SGH-E200|SGH-E315|SGH-E316|SGH-E317|SGH-E335|SGH-E590|SGH-E635|SGH-E715|SGH-E890|SGH-F300|SGH-F480|SGH-I200|SGH-I300|SGH-I320|SGH-I550|SGH-I577|SGH-I600|SGH-I607|SGH-I617|SGH-I627|SGH-I637|SGH-I677|SGH-I700|SGH-I717|SGH-I727|SGH-i747M|SGH-I777|SGH-I780|SGH-I827|SGH-I847|SGH-I857|SGH-I896|SGH-I897|SGH-I900|SGH-I907|SGH-I917|SGH-I927|SGH-I937|SGH-I997|SGH-J150|SGH-J200|SGH-L170|SGH-L700|SGH-M110|SGH-M150|SGH-M200|SGH-N105|SGH-N500|SGH-N600|SGH-N620|SGH-N625|SGH-N700|SGH-N710|SGH-P107|SGH-P207|SGH-P300|SGH-P310|SGH-P520|SGH-P735|SGH-P777|SGH-Q105|SGH-R210|SGH-R220|SGH-R225|SGH-S105|SGH-S307|SGH-T109|SGH-T119|SGH-T139|SGH-T209|SGH-T219|SGH-T229|SGH-T239|SGH-T249|SGH-T259|SGH-T309|SGH-T319|SGH-T329|SGH-T339|SGH-T349|SGH-T359|SGH-T369|SGH-T379|SGH-T409|SGH-T429|SGH-T439|SGH-T459|SGH-T469|SGH-T479|SGH-T499|SGH-T509|SGH-T519|SGH-T539|SGH-T559|SGH-T589|SGH-T609|SGH-T619|SGH-T629|SGH-T639|SGH-T659|SGH-T669|SGH-T679|SGH-T709|SGH-T719|SGH-T729|SGH-T739|SGH-T746|SGH-T749|SGH-T759|SGH-T769|SGH-T809|SGH-T819|SGH-T839|SGH-T919|SGH-T929|SGH-T939|SGH-T959|SGH-T989|SGH-U100|SGH-U200|SGH-U800|SGH-V205|SGH-V206|SGH-X100|SGH-X105|SGH-X120|SGH-X140|SGH-X426|SGH-X427|SGH-X475|SGH-X495|SGH-X497|SGH-X507|SGH-X600|SGH-X610|SGH-X620|SGH-X630|SGH-X700|SGH-X820|SGH-X890|SGH-Z130|SGH-Z150|SGH-Z170|SGH-ZX10|SGH-ZX20|SHW-M110|SPH-A120|SPH-A400|SPH-A420|SPH-A460|SPH-A500|SPH-A560|SPH-A600|SPH-A620|SPH-A660|SPH-A700|SPH-A740|SPH-A760|SPH-A790|SPH-A800|SPH-A820|SPH-A840|SPH-A880|SPH-A900|SPH-A940|SPH-A960|SPH-D600|SPH-D700|SPH-D710|SPH-D720|SPH-I300|SPH-I325|SPH-I330|SPH-I350|SPH-I500|SPH-I600|SPH-I700|SPH-L700|SPH-M100|SPH-M220|SPH-M240|SPH-M300|SPH-M305|SPH-M320|SPH-M330|SPH-M350|SPH-M360|SPH-M370|SPH-M380|SPH-M510|SPH-M540|SPH-M550|SPH-M560|SPH-M570|SPH-M580|SPH-M610|SPH-M620|SPH-M630|SPH-M800|SPH-M810|SPH-M850|SPH-M900|SPH-M910|SPH-M920|SPH-M930|SPH-N100|SPH-N200|SPH-N240|SPH-N300|SPH-N400|SPH-Z400|SWC-E100|SCH-i909|GT-N7100|SCH-I535',
|
||||
'LG' => '\bLG\b;|(LG|LG-)?(C800|C900|E400|E610|E900|E-900|F160|F180K|F180L|F180S|730|855|L160|LS840|LS970|LU6200|MS690|MS695|MS770|MS840|MS870|MS910|P500|P700|P705|VM696|AS680|AS695|AX840|C729|E970|GS505|272|C395|E739BK|E960|L55C|L75C|LS696|LS860|P769BK|P350|P870|UN272|US730|VS840|VS950|LN272|LN510|LS670|LS855|LW690|MN270|MN510|P509|P769|P930|UN200|UN270|UN510|UN610|US670|US740|US760|UX265|UX840|VN271|VN530|VS660|VS700|VS740|VS750|VS910|VS920|VS930|VX9200|VX11000|AX840A|LW770|P506|P925|P999)',
|
||||
'Sony' => 'sony|SonyEricsson|SonyEricssonLT15iv|LT18i|E10i',
|
||||
'Asus' => 'Asus.*Galaxy',
|
||||
'Palm' => 'PalmSource|Palm', // avantgo|blazer|elaine|hiptop|plucker|xiino ; @todo - complete the regex.
|
||||
'Vertu' => 'Vertu|Vertu.*Ltd|Vertu.*Ascent|Vertu.*Ayxta|Vertu.*Constellation(F|Quest)?|Vertu.*Monika|Vertu.*Signature', // Just for fun ;)
|
||||
// @ref: http://www.pantech.co.kr/en/prod/prodList.do?gbrand=VEGA (PANTECH)
|
||||
// Most of the VEGA devices are legacy. PANTECH seem to be newer devices based on Android.
|
||||
'Pantech' => 'PANTECH|IM-A850S|IM-A840S|IM-A830L|IM-A830K|IM-A830S|IM-A820L|IM-A810K|IM-A810S|IM-A800S|IM-T100K|IM-A725L|IM-A780L|IM-A775C|IM-A770K|IM-A760S|IM-A750K|IM-A740S|IM-A730S|IM-A720L|IM-A710K|IM-A690L|IM-A690S|IM-A650S|IM-A630K|IM-A600S|VEGA PTL21|PT003|P8010|ADR910L|P6030|P6020|P9070|P4100|P9060|P5000|CDM8992|TXT8045|ADR8995|IS11PT|P2030|P6010|P8000|PT002|IS06|CDM8999|P9050|PT001|TXT8040|P2020|P9020|P2000|P7040|P7000|C790',
|
||||
// @ref: http://www.fly-phone.com/devices/smartphones/ ; Included only smartphones.
|
||||
'Fly' => 'IQ230|IQ444|IQ450|IQ440|IQ442|IQ441|IQ245|IQ256|IQ236|IQ255|IQ235|IQ245|IQ275|IQ240|IQ285|IQ280|IQ270|IQ260|IQ250',
|
||||
// Added simvalley mobile just for fun. They have some interesting devices.
|
||||
// @ref: http://www.simvalley.fr/telephonie---gps-_22_telephonie-mobile_telephones_.html
|
||||
'SimValley' => '\b(SP-80|XT-930|SX-340|XT-930|SX-310|SP-360|SP60|SPT-800|SP-120|SPT-800|SP-140|SPX-5|SPX-8|SP-100|SPX-8|SPX-12)\b',
|
||||
// @Tapatalk is a mobile app; @ref: http://support.tapatalk.com/threads/smf-2-0-2-os-and-browser-detection-plugin-and-tapatalk.15565/#post-79039
|
||||
'GenericPhone' => 'Tapatalk|PDA;|PPC;|SAGEM|mmp|pocket|psp|symbian|Smartphone|smartfon|treo|up.browser|up.link|vodafone|wap|nokia|Series40|Series60|S60|SonyEricsson|N900|MAUI.*WAP.*Browser|LG-P500'
|
||||
);
|
||||
// List of tablet devices.
|
||||
protected $tabletDevices = array(
|
||||
'iPad' => 'iPad|iPad.*Mobile', // @todo: check for mobile friendly emails topic.
|
||||
'NexusTablet' => '^.*Android.*Nexus(((?:(?!Mobile))|(?:(\s(7|10).+))).)*$',
|
||||
'SamsungTablet' => 'SAMSUNG.*Tablet|Galaxy.*Tab|SC-01C|GT-P1000|GT-P1003|GT-P1010|GT-P3105|GT-P6210|GT-P6800|GT-P6810|GT-P7100|GT-P7300|GT-P7310|GT-P7500|GT-P7510|SCH-I800|SCH-I815|SCH-I905|SGH-I957|SGH-I987|SGH-T849|SGH-T859|SGH-T869|SPH-P100|GT-P3100|GT-P3108|GT-P3110|GT-P5100|GT-P5110|GT-P6200|GT-P7320|GT-P7511|GT-N8000|GT-P8510|SGH-I497|SPH-P500|SGH-T779|SCH-I705|SCH-I915|GT-N8013|GT-P3113|GT-P5113|GT-P8110|GT-N8010|GT-N8005|GT-N8020|GT-P1013|GT-P6201|GT-P7501|GT-N5100|GT-N5110|SHV-E140K|SHV-E140L|SHV-E140S|SHV-E150S|SHV-E230K|SHV-E230L|SHV-E230S|SHW-M180K|SHW-M180L|SHW-M180S|SHW-M180W|SHW-M300W|SHW-M305W|SHW-M380K|SHW-M380S|SHW-M380W|SHW-M430W|SHW-M480K|SHW-M480S|SHW-M480W|SHW-M485W|SHW-M486W|SHW-M500W|GT-I9228|SCH-P739|SCH-I925',
|
||||
// @reference: http://www.labnol.org/software/kindle-user-agent-string/20378/
|
||||
'Kindle' => 'Kindle|Silk.*Accelerated',
|
||||
// Only the Surface tablets with Windows RT are considered mobile.
|
||||
// @ref: http://msdn.microsoft.com/en-us/library/ie/hh920767(v=vs.85).aspx
|
||||
'SurfaceTablet' => 'Windows NT [0-9.]+; ARM;',
|
||||
'AsusTablet' => 'Transformer|TF101',
|
||||
'BlackBerryTablet' => 'PlayBook|RIM Tablet',
|
||||
'HTCtablet' => 'HTC Flyer|HTC Jetstream|HTC-P715a|HTC EVO View 4G|PG41200',
|
||||
'MotorolaTablet' => 'xoom|sholest|MZ615|MZ605|MZ505|MZ601|MZ602|MZ603|MZ604|MZ606|MZ607|MZ608|MZ609|MZ615|MZ616|MZ617',
|
||||
'NookTablet' => 'Android.*Nook|NookColor|nook browser|BNRV200|BNRV200A|BNTV250|BNTV250A|LogicPD Zoom2',
|
||||
// @ref: http://www.acer.ro/ac/ro/RO/content/drivers
|
||||
// @ref: http://www.packardbell.co.uk/pb/en/GB/content/download (Packard Bell is part of Acer)
|
||||
'AcerTablet' => 'Android.*\b(A100|A101|A110|A200|A210|A211|A500|A501|A510|A511|A700|A701|W500|W500P|W501|W501P|W510|W511|W700|G100|G100W|B1-A71)\b',
|
||||
// @ref: http://eu.computers.toshiba-europe.com/innovation/family/Tablets/1098744/banner_id/tablet_footerlink/
|
||||
// @ref: http://us.toshiba.com/tablets/tablet-finder
|
||||
// @ref: http://www.toshiba.co.jp/regza/tablet/
|
||||
'ToshibaTablet' => 'Android.*(AT100|AT105|AT200|AT205|AT270|AT275|AT300|AT305|AT1S5|AT500|AT570|AT700|AT830)|TOSHIBA.*FOLIO',
|
||||
// @ref: http://www.nttdocomo.co.jp/english/service/developer/smart_phone/technical_info/spec/index.html
|
||||
'LGTablet' => '\bL-06C|LG-V900|LG-V909\b',
|
||||
// Prestigio Tablets http://www.prestigio.com/support
|
||||
'PrestigioTablet' => 'PMP3170B|PMP3270B|PMP3470B|PMP7170B|PMP3370B|PMP3570C|PMP5870C|PMP3670B|PMP5570C|PMP5770D|PMP3970B|PMP3870C|PMP5580C|PMP5880D|PMP5780D|PMP5588C|PMP7280C|PMP7280|PMP7880D|PMP5597D|PMP5597|PMP7100D|PER3464|PER3274|PER3574|PER3884|PER5274|PER5474',
|
||||
'YarvikTablet' => 'Android.*(TAB210|TAB211|TAB224|TAB250|TAB260|TAB264|TAB310|TAB360|TAB364|TAB410|TAB411|TAB420|TAB424|TAB450|TAB460|TAB461|TAB464|TAB465|TAB467|TAB468)',
|
||||
'MedionTablet' => 'Android.*\bOYO\b|LIFE.*(P9212|P9514|P9516|S9512)|LIFETAB',
|
||||
'ArnovaTablet' => 'AN10G2|AN7bG3|AN7fG3|AN8G3|AN8cG3|AN7G3|AN9G3|AN7dG3|AN7dG3ST|AN7dG3ChildPad|AN10bG3|AN10bG3DT',
|
||||
// @reference: http://wiki.archosfans.com/index.php?title=Main_Page
|
||||
'ArchosTablet' => 'Android.*ARCHOS|\b101G9\b|\b80G9\b',
|
||||
// @reference: http://en.wikipedia.org/wiki/NOVO7
|
||||
'AinolTablet' => 'NOVO7|Novo7Aurora|Novo7Basic|NOVO7PALADIN',
|
||||
// @todo: inspect http://esupport.sony.com/US/p/select-system.pl?DIRECTOR=DRIVER
|
||||
// @ref: Readers http://www.atsuhiro-me.net/ebook/sony-reader/sony-reader-web-browser
|
||||
// @ref: http://www.sony.jp/support/tablet/
|
||||
'SonyTablet' => 'Sony.*Tablet|Xperia Tablet|Sony Tablet S|SO-03E|SGPT12|SGPT121|SGPT122|SGPT123|SGPT111|SGPT112|SGPT113|SGPT211|SGPT213|SGP311|SGP312|SGP321|EBRD1101|EBRD1102|EBRD1201',
|
||||
// @ref: db + http://www.cube-tablet.com/buy-products.html
|
||||
'CubeTablet' => 'Android.*(K8GT|U9GT|U10GT|U16GT|U17GT|U18GT|U19GT|U20GT|U23GT|U30GT)|CUBE U8GT',
|
||||
// @ref: http://www.cobyusa.com/?p=pcat&pcat_id=3001
|
||||
'CobyTablet' => 'MID1042|MID1045|MID1125|MID1126|MID7012|MID7014|MID7034|MID7035|MID7036|MID7042|MID7048|MID7127|MID8042|MID8048|MID8127|MID9042|MID9740|MID9742|MID7022|MID7010',
|
||||
// @ref: http://pdadb.net/index.php?m=pdalist&list=SMiT (NoName Chinese Tablets)
|
||||
// @ref: http://www.imp3.net/14/show.php?itemid=20454
|
||||
'SMiTTablet' => 'Android.*(\bMID\b|MID-560|MTV-T1200|MTV-PND531|MTV-P1101|MTV-PND530)',
|
||||
// @ref: http://www.rock-chips.com/index.php?do=prod&pid=2
|
||||
'RockChipTablet' => 'Android.*(RK2818|RK2808A|RK2918|RK3066)|RK2738|RK2808A',
|
||||
// @ref: http://www.telstra.com.au/home-phone/thub-2/
|
||||
'TelstraTablet' => 'T-Hub2',
|
||||
// @ref: http://www.fly-phone.com/devices/tablets/ ; http://www.fly-phone.com/service/
|
||||
'FlyTablet' => 'IQ310|Fly Vision',
|
||||
// @ref: http://www.bqreaders.com/gb/tablets-prices-sale.html
|
||||
'bqTablet' => 'bq.*(Elcano|Curie|Edison|Maxwell|Kepler|Pascal|Tesla|Hypatia|Platon|Newton|Livingstone|Cervantes|Avant)',
|
||||
// @ref: http://www.huaweidevice.com/worldwide/productFamily.do?method=index&directoryId=5011&treeId=3290
|
||||
// @ref: http://www.huaweidevice.com/worldwide/downloadCenter.do?method=index&directoryId=3372&treeId=0&tb=1&type=software (including legacy tablets)
|
||||
'HuaweiTablet' => 'MediaPad|IDEOS S7|S7-201c|S7-202u|S7-101|S7-103|S7-104|S7-105|S7-106|S7-201|S7-Slim',
|
||||
// Nec or Medias Tab
|
||||
'NecTablet' => '\bN-06D|\bN-08D',
|
||||
// Pantech Tablets: http://www.pantechusa.com/phones/
|
||||
'PantechTablet' => 'Pantech.*P4100',
|
||||
// Broncho Tablets: http://www.broncho.cn/ (hard to find)
|
||||
'BronchoTablet' => 'Broncho.*(N701|N708|N802|a710)',
|
||||
// @ref: http://versusuk.com/support.html
|
||||
'VersusTablet' => 'TOUCHPAD.*[78910]',
|
||||
// @ref: http://www.zync.in/index.php/our-products/tablet-phablets
|
||||
'ZyncTablet' => 'z1000|Z99 2G|z99|z930|z999|z990|z909|Z919|z900',
|
||||
// @ref: http://www.positivoinformatica.com.br/www/pessoal/tablet-ypy/
|
||||
'PositivoTablet' => 'TB07STA|TB10STA|TB07FTA|TB10FTA',
|
||||
// @ref: https://www.nabitablet.com/
|
||||
'NabiTablet' => 'Android.*\bNabi',
|
||||
'KoboTablet' => 'Kobo Touch|\bK080\b|\bVox\b Build|\bArc\b Build',
|
||||
// French Danew Tablets http://www.danew.com/produits-tablette.php
|
||||
'DanewTablet' => 'DSlide.*\b(700|701R|702|703R|704|802|970|971|972|973|974|1010|1012)\b',
|
||||
// Texet Tablets and Readers http://www.texet.ru/tablet/
|
||||
'TexetTablet' => 'NaviPad|TB-772A|TM-7045|TM-7055|TM-9750|TM-7016|TM-7024|TM-7026|TM-7041|TM-7043|TM-7047|TM-8041|TM-9741|TM-9747|TM-9748|TM-9751|TM-7022|TM-7021|TM-7020|TM-7011|TM-7010|TM-7023|TM-7025|TM-7037W|TM-7038W|TM-7027W|TM-9720|TM-9725|TM-9737W|TM-1020|TM-9738W|TM-9740|TM-9743W|TB-807A|TB-771A|TB-727A|TB-725A|TB-719A|TB-823A|TB-805A|TB-723A|TB-715A|TB-707A|TB-705A|TB-709A|TB-711A|TB-890HD|TB-880HD|TB-790HD|TB-780HD|TB-770HD|TB-721HD|TB-710HD|TB-434HD|TB-860HD|TB-840HD|TB-760HD|TB-750HD|TB-740HD|TB-730HD|TB-722HD|TB-720HD|TB-700HD|TB-500HD|TB-470HD|TB-431HD|TB-430HD|TB-506|TB-504|TB-446|TB-436|TB-416|TB-146SE|TB-126SE',
|
||||
// @note: Avoid detecting 'PLAYSTATION 3' as mobile.
|
||||
'PlaystationTablet' => 'Playstation.*(Portable|Vita)',
|
||||
// @ref: http://www.galapad.net/product.html
|
||||
'GalapadTablet' => 'Android.*\bG1\b',
|
||||
'GenericTablet' => 'Android.*\b97D\b|Tablet(?!.*PC)|ViewPad7|MID7015|BNTV250A|LogicPD Zoom2|\bA7EB\b|CatNova8|A1_07|CT704|CT1002|\bM721\b|hp-tablet|rk30sdk',
|
||||
);
|
||||
// List of mobile Operating Systems.
|
||||
protected $operatingSystems = array(
|
||||
'AndroidOS' => 'Android',
|
||||
'BlackBerryOS' => 'blackberry|\bBB10\b|rim tablet os',
|
||||
'PalmOS' => 'PalmOS|avantgo|blazer|elaine|hiptop|palm|plucker|xiino',
|
||||
'SymbianOS' => 'Symbian|SymbOS|Series60|Series40|SYB-[0-9]+|\bS60\b',
|
||||
// @reference: http://en.wikipedia.org/wiki/Windows_Mobile
|
||||
'WindowsMobileOS' => 'Windows CE.*(PPC|Smartphone|Mobile|[0-9]{3}x[0-9]{3})|Window Mobile|Windows Phone [0-9.]+|WCE;',
|
||||
// @reference: http://en.wikipedia.org/wiki/Windows_Phone
|
||||
// http://wifeng.cn/?r=blog&a=view&id=106
|
||||
// http://nicksnettravels.builttoroam.com/post/2011/01/10/Bogus-Windows-Phone-7-User-Agent-String.aspx
|
||||
'WindowsPhoneOS' => 'Windows Phone OS|XBLWP7|ZuneWP7',
|
||||
'iOS' => '\biPhone.*Mobile|\biPod|\biPad',
|
||||
// http://en.wikipedia.org/wiki/MeeGo
|
||||
// @todo: research MeeGo in UAs
|
||||
'MeeGoOS' => 'MeeGo',
|
||||
// http://en.wikipedia.org/wiki/Maemo
|
||||
// @todo: research Maemo in UAs
|
||||
'MaemoOS' => 'Maemo',
|
||||
'JavaOS' => 'J2ME/|Java/|\bMIDP\b|\bCLDC\b',
|
||||
'webOS' => 'webOS|hpwOS',
|
||||
'badaOS' => '\bBada\b',
|
||||
'BREWOS' => 'BREW',
|
||||
);
|
||||
// List of mobile User Agents.
|
||||
protected $userAgents = array(
|
||||
// @reference: https://developers.google.com/chrome/mobile/docs/user-agent
|
||||
'Chrome' => '\bCrMo\b|CriOS|Android.*Chrome/[.0-9]* (Mobile)?',
|
||||
'Dolfin' => '\bDolfin\b',
|
||||
'Opera' => 'Opera.*Mini|Opera.*Mobi|Android.*Opera|Mobile.*OPR/[0-9.]+',
|
||||
'Skyfire' => 'Skyfire',
|
||||
'IE' => 'IEMobile|MSIEMobile',
|
||||
'Firefox' => 'fennec|firefox.*maemo|(Mobile|Tablet).*Firefox|Firefox.*Mobile',
|
||||
'Bolt' => 'bolt',
|
||||
'TeaShark' => 'teashark',
|
||||
'Blazer' => 'Blazer',
|
||||
// @reference: http://developer.apple.com/library/safari/#documentation/AppleApplications/Reference/SafariWebContent/OptimizingforSafarioniPhone/OptimizingforSafarioniPhone.html#//apple_ref/doc/uid/TP40006517-SW3
|
||||
'Safari' => 'Version.*Mobile.*Safari|Safari.*Mobile',
|
||||
// @ref: http://en.wikipedia.org/wiki/Midori_(web_browser)
|
||||
//'Midori' => 'midori',
|
||||
'Tizen' => 'Tizen',
|
||||
'UCBrowser' => 'UC.*Browser|UCWEB',
|
||||
// @ref: https://github.com/serbanghita/Mobile-Detect/issues/7
|
||||
'DiigoBrowser' => 'DiigoBrowser',
|
||||
// http://www.puffinbrowser.com/index.php
|
||||
'Puffin' => 'Puffin',
|
||||
// @ref: http://mercury-browser.com/index.html
|
||||
'Mercury' => '\bMercury\b',
|
||||
// @reference: http://en.wikipedia.org/wiki/Minimo
|
||||
// http://en.wikipedia.org/wiki/Vision_Mobile_Browser
|
||||
'GenericBrowser' => 'NokiaBrowser|OviBrowser|OneBrowser|TwonkyBeamBrowser|SEMC.*Browser|FlyFlow|Minimo|NetFront|Novarra-Vision'
|
||||
);
|
||||
// Utilities.
|
||||
protected $utilities = array(
|
||||
// Experimental. When a mobile device wants to switch to 'Desktop Mode'.
|
||||
// @ref: http://scottcate.com/technology/windows-phone-8-ie10-desktop-or-mobile/
|
||||
// @ref: https://github.com/serbanghita/Mobile-Detect/issues/57#issuecomment-15024011
|
||||
'DesktopMode' => 'WPDesktop',
|
||||
'TV' => 'SonyDTV115', // experimental
|
||||
'WebKit' => '(webkit)[ /]([\w.]+)',
|
||||
'Bot' => 'Googlebot|DoCoMo|YandexBot|bingbot|ia_archiver|AhrefsBot|Ezooms|GSLFbot|WBSearchBot|Twitterbot|TweetmemeBot|Twikle|PaperLiBot|Wotbox|UnwindFetchor|facebookexternalhit',
|
||||
'MobileBot' => 'Googlebot-Mobile|DoCoMo|YahooSeeker/M1A1-R2D2',
|
||||
);
|
||||
// Properties list.
|
||||
// @reference: http://user-agent-string.info/list-of-ua#Mobile Browser
|
||||
const VER = '([\w._\+]+)';
|
||||
protected $properties = array(
|
||||
|
||||
// Build
|
||||
'Mobile' => 'Mobile/[VER]',
|
||||
'Build' => 'Build/[VER]',
|
||||
'Version' => 'Version/[VER]',
|
||||
'VendorID' => 'VendorID/[VER]',
|
||||
|
||||
// Devices
|
||||
'iPad' => 'iPad.*CPU[a-z ]+[VER]',
|
||||
'iPhone' => 'iPhone.*CPU[a-z ]+[VER]',
|
||||
'iPod' => 'iPod.*CPU[a-z ]+[VER]',
|
||||
//'BlackBerry' => array('BlackBerry[VER]', 'BlackBerry [VER];'),
|
||||
'Kindle' => 'Kindle/[VER]',
|
||||
|
||||
// Browser
|
||||
'Chrome' => array('Chrome/[VER]', 'CriOS/[VER]', 'CrMo/[VER]'),
|
||||
'Dolfin' => 'Dolfin/[VER]',
|
||||
// @reference: https://developer.mozilla.org/en-US/docs/User_Agent_Strings_Reference
|
||||
'Firefox' => 'Firefox/[VER]',
|
||||
'Fennec' => 'Fennec/[VER]',
|
||||
// @reference: http://msdn.microsoft.com/en-us/library/ms537503(v=vs.85).aspx
|
||||
'IE' => array('IEMobile/[VER];', 'IEMobile [VER]', 'MSIE [VER];'),
|
||||
// http://en.wikipedia.org/wiki/NetFront
|
||||
'NetFront' => 'NetFront/[VER]',
|
||||
'NokiaBrowser' => 'NokiaBrowser/[VER]',
|
||||
'Opera' => array( ' OPR/[VER]', 'Opera Mini/[VER]', 'Version/[VER]' ),
|
||||
'UC Browser' => 'UC Browser[VER]',
|
||||
// @note: Safari 7534.48.3 is actually Version 5.1.
|
||||
// @note: On BlackBerry the Version is overwriten by the OS.
|
||||
'Safari' => array( 'Version/[VER]', 'Safari/[VER]' ),
|
||||
'Skyfire' => 'Skyfire/[VER]',
|
||||
'Tizen' => 'Tizen/[VER]',
|
||||
'Webkit' => 'webkit[ /][VER]',
|
||||
|
||||
// Engine
|
||||
'Gecko' => 'Gecko/[VER]',
|
||||
'Trident' => 'Trident/[VER]',
|
||||
'Presto' => 'Presto/[VER]',
|
||||
|
||||
// OS
|
||||
'iOS' => ' \bOS\b [VER] ',
|
||||
'Android' => 'Android [VER]',
|
||||
'BlackBerry' => array('BlackBerry[\w]+/[VER]', 'BlackBerry.*Version/[VER]', 'Version/[VER]'),
|
||||
'BREW' => 'BREW [VER]',
|
||||
'Java' => 'Java/[VER]',
|
||||
// @reference: http://windowsteamblog.com/windows_phone/b/wpdev/archive/2011/08/29/introducing-the-ie9-on-windows-phone-mango-user-agent-string.aspx
|
||||
// @reference: http://en.wikipedia.org/wiki/Windows_NT#Releases
|
||||
'Windows Phone OS' => array( 'Windows Phone OS [VER]', 'Windows Phone [VER]'),
|
||||
'Windows Phone' => 'Windows Phone [VER]',
|
||||
'Windows CE' => 'Windows CE/[VER]',
|
||||
// http://social.msdn.microsoft.com/Forums/en-US/windowsdeveloperpreviewgeneral/thread/6be392da-4d2f-41b4-8354-8dcee20c85cd
|
||||
'Windows NT' => 'Windows NT [VER]',
|
||||
'Symbian' => array('SymbianOS/[VER]', 'Symbian/[VER]'),
|
||||
'webOS' => array('webOS/[VER]', 'hpwOS/[VER];'),
|
||||
|
||||
|
||||
);
|
||||
|
||||
function __construct(){
|
||||
|
||||
$this->setHttpHeaders();
|
||||
$this->setUserAgent();
|
||||
|
||||
$this->setMobileDetectionRules();
|
||||
$this->setMobileDetectionRulesExtended();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the current script version.
|
||||
* This is useful for the demo.php file,
|
||||
* so people can check on what version they are testing
|
||||
* for mobile devices.
|
||||
*/
|
||||
public function getScriptVersion(){
|
||||
|
||||
return $this->scriptVersion;
|
||||
|
||||
}
|
||||
|
||||
public function setHttpHeaders($httpHeaders = null){
|
||||
|
||||
if(!empty($httpHeaders)){
|
||||
$this->httpHeaders = $httpHeaders;
|
||||
} else {
|
||||
foreach($_SERVER as $key => $value){
|
||||
if(substr($key,0,5)=='HTTP_'){
|
||||
$this->httpHeaders[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function getHttpHeaders(){
|
||||
|
||||
return $this->httpHeaders;
|
||||
|
||||
}
|
||||
|
||||
public function setUserAgent($userAgent = null){
|
||||
|
||||
if(!empty($userAgent)){
|
||||
$this->userAgent = $userAgent;
|
||||
} else {
|
||||
$this->userAgent = isset($this->httpHeaders['HTTP_USER_AGENT']) ? $this->httpHeaders['HTTP_USER_AGENT'] : null;
|
||||
|
||||
if(empty($this->userAgent)){
|
||||
$this->userAgent = isset($this->httpHeaders['HTTP_X_DEVICE_USER_AGENT']) ? $this->httpHeaders['HTTP_X_DEVICE_USER_AGENT'] : null;
|
||||
}
|
||||
// Header can occur on devices using Opera Mini (can expose the real device type). Let's concatenate it (we need this extra info in the regexes).
|
||||
if(!empty($this->httpHeaders['HTTP_X_OPERAMINI_PHONE_UA'])){
|
||||
$this->userAgent .= ' '.$this->httpHeaders['HTTP_X_OPERAMINI_PHONE_UA'];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function getUserAgent(){
|
||||
|
||||
return $this->userAgent;
|
||||
|
||||
}
|
||||
|
||||
function setDetectionType($type = null){
|
||||
|
||||
$this->detectionType = (!empty($type) ? $type : 'mobile');
|
||||
|
||||
}
|
||||
|
||||
public function getPhoneDevices(){
|
||||
|
||||
return $this->phoneDevices;
|
||||
|
||||
}
|
||||
|
||||
public function getTabletDevices(){
|
||||
|
||||
return $this->tabletDevices;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Method sets the mobile detection rules.
|
||||
*
|
||||
* This method is used for the magic methods $detect->is*()
|
||||
*/
|
||||
public function setMobileDetectionRules(){
|
||||
// Merge all rules together.
|
||||
$this->mobileDetectionRules = array_merge(
|
||||
$this->phoneDevices,
|
||||
$this->tabletDevices,
|
||||
$this->operatingSystems,
|
||||
$this->userAgents
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Method sets the mobile detection rules + utilities.
|
||||
* The reason this is separate is because utilities rules
|
||||
* don't necessary imply mobile.
|
||||
*
|
||||
* This method is used inside the new $detect->is('stuff') method.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function setMobileDetectionRulesExtended(){
|
||||
|
||||
// Merge all rules together.
|
||||
$this->mobileDetectionRulesExtended = array_merge(
|
||||
$this->phoneDevices,
|
||||
$this->tabletDevices,
|
||||
$this->operatingSystems,
|
||||
$this->userAgents,
|
||||
$this->utilities
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getRules()
|
||||
{
|
||||
|
||||
if($this->detectionType=='extended'){
|
||||
return $this->mobileDetectionRulesExtended;
|
||||
} else {
|
||||
return $this->mobileDetectionRules;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the HTTP headers for signs of mobile.
|
||||
* This is the fastest mobile check possible; it's used
|
||||
* inside isMobile() method.
|
||||
* @return boolean
|
||||
*/
|
||||
public function checkHttpHeadersForMobile(){
|
||||
|
||||
if(
|
||||
isset($this->httpHeaders['HTTP_ACCEPT']) &&
|
||||
(strpos($this->httpHeaders['HTTP_ACCEPT'], 'application/x-obml2d') !== false || // Opera Mini; @reference: http://dev.opera.com/articles/view/opera-binary-markup-language/
|
||||
strpos($this->httpHeaders['HTTP_ACCEPT'], 'application/vnd.rim.html') !== false || // BlackBerry devices.
|
||||
strpos($this->httpHeaders['HTTP_ACCEPT'], 'text/vnd.wap.wml') !== false ||
|
||||
strpos($this->httpHeaders['HTTP_ACCEPT'], 'application/vnd.wap.xhtml+xml') !== false) ||
|
||||
isset($this->httpHeaders['HTTP_X_WAP_PROFILE']) || // @todo: validate
|
||||
isset($this->httpHeaders['HTTP_X_WAP_CLIENTID']) ||
|
||||
isset($this->httpHeaders['HTTP_WAP_CONNECTION']) ||
|
||||
isset($this->httpHeaders['HTTP_PROFILE']) ||
|
||||
isset($this->httpHeaders['HTTP_X_OPERAMINI_PHONE_UA']) || // Reported by Nokia devices (eg. C3)
|
||||
isset($this->httpHeaders['HTTP_X_NOKIA_IPADDRESS']) ||
|
||||
isset($this->httpHeaders['HTTP_X_NOKIA_GATEWAY_ID']) ||
|
||||
isset($this->httpHeaders['HTTP_X_ORANGE_ID']) ||
|
||||
isset($this->httpHeaders['HTTP_X_VODAFONE_3GPDPCONTEXT']) ||
|
||||
isset($this->httpHeaders['HTTP_X_HUAWEI_USERID']) ||
|
||||
isset($this->httpHeaders['HTTP_UA_OS']) || // Reported by Windows Smartphones.
|
||||
isset($this->httpHeaders['HTTP_X_MOBILE_GATEWAY']) || // Reported by Verizon, Vodafone proxy system.
|
||||
isset($this->httpHeaders['HTTP_X_ATT_DEVICEID']) || // Seend this on HTC Sensation. @ref: SensationXE_Beats_Z715e
|
||||
//HTTP_X_NETWORK_TYPE = WIFI
|
||||
( isset($this->httpHeaders['HTTP_UA_CPU']) &&
|
||||
$this->httpHeaders['HTTP_UA_CPU'] == 'ARM' // Seen this on a HTC.
|
||||
)
|
||||
){
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic overloading method.
|
||||
*
|
||||
* @method boolean is[...]()
|
||||
* @param string $name
|
||||
* @param array $arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call($name, $arguments)
|
||||
{
|
||||
|
||||
$this->setDetectionType('mobile');
|
||||
|
||||
$key = substr($name, 2);
|
||||
return $this->matchUAAgainstKey($key);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a detection rule that matches the current User-agent.
|
||||
*
|
||||
* @param null $userAgent deprecated
|
||||
* @return boolean
|
||||
*/
|
||||
private function matchDetectionRulesAgainstUA($userAgent = null){
|
||||
|
||||
// Begin general search.
|
||||
foreach($this->getRules() as $_regex){
|
||||
if(empty($_regex)){ continue; }
|
||||
if( $this->match($_regex, $userAgent) ){
|
||||
//var_dump( $_regex );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for a certain key in the rules array.
|
||||
* If the key is found the try to match the corresponding
|
||||
* regex agains the User-agent.
|
||||
*
|
||||
* @param string $key
|
||||
* @param null $userAgent deprecated
|
||||
* @return mixed
|
||||
*/
|
||||
private function matchUAAgainstKey($key, $userAgent = null){
|
||||
|
||||
// Make the keys lowercase so we can match: isIphone(), isiPhone(), isiphone(), etc.
|
||||
$key = strtolower($key);
|
||||
$_rules = array_change_key_case($this->getRules());
|
||||
|
||||
if(array_key_exists($key, $_rules)){
|
||||
if(empty($_rules[$key])){ return null; }
|
||||
return $this->match($_rules[$key], $userAgent);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the device is mobile.
|
||||
* Returns true if any type of mobile device detected, including special ones
|
||||
* @param null $userAgent deprecated
|
||||
* @param null $httpHeaders deprecated
|
||||
* @return bool
|
||||
*/
|
||||
public function isMobile($userAgent = null, $httpHeaders = null) {
|
||||
|
||||
if($httpHeaders){ $this->setHttpHeaders($httpHeaders); }
|
||||
if($userAgent){ $this->setUserAgent($userAgent); }
|
||||
|
||||
$this->setDetectionType('mobile');
|
||||
|
||||
if ($this->checkHttpHeadersForMobile()) {
|
||||
return true;
|
||||
} else {
|
||||
return $this->matchDetectionRulesAgainstUA();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the device is a tablet.
|
||||
* Return true if any type of tablet device is detected.
|
||||
*
|
||||
* @param null $userAgent deprecated
|
||||
* @param null $httpHeaders deprecated
|
||||
* @return bool
|
||||
*/
|
||||
public function isTablet($userAgent = null, $httpHeaders = null) {
|
||||
|
||||
$this->setDetectionType('mobile');
|
||||
|
||||
foreach($this->tabletDevices as $_regex){
|
||||
if($this->match($_regex, $userAgent)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks for a certain property in the
|
||||
* userAgent.
|
||||
* @todo: The httpHeaders part is not yet used.
|
||||
*
|
||||
* @param $key
|
||||
* @param string $userAgent deprecated
|
||||
* @param string $httpHeaders deprecated
|
||||
* @return bool|int|null
|
||||
*/
|
||||
public function is($key, $userAgent = null, $httpHeaders = null){
|
||||
|
||||
|
||||
// Set the UA and HTTP headers only if needed (eg. batch mode).
|
||||
if($httpHeaders) $this->setHttpHeaders($httpHeaders);
|
||||
if($userAgent) $this->setUserAgent($userAgent);
|
||||
|
||||
$this->setDetectionType('extended');
|
||||
|
||||
return $this->matchUAAgainstKey($key);
|
||||
|
||||
}
|
||||
|
||||
public function getOperatingSystems(){
|
||||
|
||||
return $this->operatingSystems;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Some detection rules are relative (not standard),
|
||||
* because of the diversity of devices, vendors and
|
||||
* their conventions in representing the User-Agent or
|
||||
* the HTTP headers.
|
||||
*
|
||||
* This method will be used to check custom regexes against
|
||||
* the User-Agent string.
|
||||
*
|
||||
* @param $regex
|
||||
* @param string $userAgent
|
||||
* @return bool
|
||||
*
|
||||
* @todo: search in the HTTP headers too.
|
||||
*/
|
||||
function match($regex, $userAgent=null){
|
||||
|
||||
// Escape the special character which is the delimiter.
|
||||
$regex = str_replace('/', '\/', $regex);
|
||||
|
||||
return (bool)preg_match('/'.$regex.'/is', (!empty($userAgent) ? $userAgent : $this->userAgent));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the properties array.
|
||||
* @return array
|
||||
*/
|
||||
function getProperties(){
|
||||
|
||||
return $this->properties;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the version number.
|
||||
*
|
||||
* @param $ver
|
||||
* @return int
|
||||
*/
|
||||
function prepareVersionNo($ver){
|
||||
|
||||
$ver = str_replace(array('_', ' ', '/'), array('.', '.', '.'), $ver);
|
||||
$arrVer = explode('.', $ver, 2);
|
||||
if(isset($arrVer[1])){
|
||||
$arrVer[1] = @str_replace('.', '', $arrVer[1]); // @todo: treat strings versions.
|
||||
}
|
||||
$ver = (float)implode('.', $arrVer);
|
||||
|
||||
return $ver;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the version of the given property in the User-Agent.
|
||||
* Will return a float number. (eg. 2_0 will return 2.0, 4.3.1 will return 4.31)
|
||||
*
|
||||
* @param string $propertyName
|
||||
* @return mixed $version
|
||||
*/
|
||||
function version($propertyName, $type = 'text'){
|
||||
|
||||
if(empty($propertyName)){ return false; }
|
||||
if( !in_array($type, array('text', 'float')) ){ $type = 'text'; }
|
||||
|
||||
$properties = $this->getProperties();
|
||||
|
||||
// Check if the property exists in the properties array.
|
||||
if( array_key_exists($propertyName, $properties) ){
|
||||
|
||||
// Prepare the pattern to be matched.
|
||||
// Make sure we always deal with an array (string is converted).
|
||||
$properties[$propertyName] = (array)$properties[$propertyName];
|
||||
|
||||
foreach($properties[$propertyName] as $propertyMatchString){
|
||||
|
||||
$propertyPattern = str_replace('[VER]', self::VER, $propertyMatchString);
|
||||
|
||||
// Escape the special character which is the delimiter.
|
||||
$propertyPattern = str_replace('/', '\/', $propertyPattern);
|
||||
|
||||
// Identify and extract the version.
|
||||
preg_match('/'.$propertyPattern.'/is', $this->userAgent, $match);
|
||||
|
||||
if(!empty($match[1])){
|
||||
$version = ( $type == 'float' ? $this->prepareVersionNo($match[1]) : $match[1] );
|
||||
return $version;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
function mobileGrade(){
|
||||
|
||||
$isMobile = $this->isMobile();
|
||||
|
||||
if(
|
||||
// Apple iOS 3.2-5.1 - Tested on the original iPad (4.3 / 5.0), iPad 2 (4.3), iPad 3 (5.1), original iPhone (3.1), iPhone 3 (3.2), 3GS (4.3), 4 (4.3 / 5.0), and 4S (5.1)
|
||||
$this->version('iPad')>=4.3 ||
|
||||
$this->version('iPhone')>=3.1 ||
|
||||
$this->version('iPod')>=3.1 ||
|
||||
|
||||
// Android 2.1-2.3 - Tested on the HTC Incredible (2.2), original Droid (2.2), HTC Aria (2.1), Google Nexus S (2.3). Functional on 1.5 & 1.6 but performance may be sluggish, tested on Google G1 (1.5)
|
||||
// Android 3.1 (Honeycomb) - Tested on the Samsung Galaxy Tab 10.1 and Motorola XOOM
|
||||
// Android 4.0 (ICS) - Tested on a Galaxy Nexus. Note: transition performance can be poor on upgraded devices
|
||||
// Android 4.1 (Jelly Bean) - Tested on a Galaxy Nexus and Galaxy 7
|
||||
( $this->version('Android')>2.1 && $this->is('Webkit') ) ||
|
||||
|
||||
// Windows Phone 7-7.5 - Tested on the HTC Surround (7.0) HTC Trophy (7.5), LG-E900 (7.5), Nokia Lumia 800
|
||||
$this->version('Windows Phone OS')>=7.0 ||
|
||||
|
||||
// Blackberry 7 - Tested on BlackBerry® Torch 9810
|
||||
// Blackberry 6.0 - Tested on the Torch 9800 and Style 9670
|
||||
$this->version('BlackBerry')>=6.0 ||
|
||||
// Blackberry Playbook (1.0-2.0) - Tested on PlayBook
|
||||
$this->match('Playbook.*Tablet') ||
|
||||
|
||||
// Palm WebOS (1.4-2.0) - Tested on the Palm Pixi (1.4), Pre (1.4), Pre 2 (2.0)
|
||||
( $this->version('webOS')>=1.4 && $this->match('Palm|Pre|Pixi') ) ||
|
||||
// Palm WebOS 3.0 - Tested on HP TouchPad
|
||||
$this->match('hp.*TouchPad') ||
|
||||
|
||||
// Firefox Mobile (12 Beta) - Tested on Android 2.3 device
|
||||
( $this->is('Firefox') && $this->version('Firefox')>=12 ) ||
|
||||
|
||||
// Chrome for Android - Tested on Android 4.0, 4.1 device
|
||||
( $this->is('Chrome') && $this->is('AndroidOS') && $this->version('Android')>=4.0 ) ||
|
||||
|
||||
// Skyfire 4.1 - Tested on Android 2.3 device
|
||||
( $this->is('Skyfire') && $this->version('Skyfire')>=4.1 && $this->is('AndroidOS') && $this->version('Android')>=2.3 ) ||
|
||||
|
||||
// Opera Mobile 11.5-12: Tested on Android 2.3
|
||||
( $this->is('Opera') && $this->version('Opera Mobi')>11 && $this->is('AndroidOS') ) ||
|
||||
|
||||
// Meego 1.2 - Tested on Nokia 950 and N9
|
||||
$this->is('MeeGoOS') ||
|
||||
|
||||
// Tizen (pre-release) - Tested on early hardware
|
||||
$this->is('Tizen') ||
|
||||
|
||||
// Samsung Bada 2.0 - Tested on a Samsung Wave 3, Dolphin browser
|
||||
// @todo: more tests here!
|
||||
$this->is('Dolfin') && $this->version('Bada')>=2.0 ||
|
||||
|
||||
// UC Browser - Tested on Android 2.3 device
|
||||
( ($this->is('UC Browser') || $this->is('Dolfin')) && $this->version('Android')>=2.3 ) ||
|
||||
|
||||
// Kindle 3 and Fire - Tested on the built-in WebKit browser for each
|
||||
( $this->match('Kindle Fire') ||
|
||||
$this->is('Kindle') && $this->version('Kindle')>=3.0 ) ||
|
||||
|
||||
// Nook Color 1.4.1 - Tested on original Nook Color, not Nook Tablet
|
||||
$this->is('AndroidOS') && $this->is('NookTablet') ||
|
||||
|
||||
// Chrome Desktop 11-21 - Tested on OS X 10.7 and Windows 7
|
||||
$this->version('Chrome')>=11 && !$isMobile ||
|
||||
|
||||
// Safari Desktop 4-5 - Tested on OS X 10.7 and Windows 7
|
||||
$this->version('Safari')>=5.0 && !$isMobile ||
|
||||
|
||||
// Firefox Desktop 4-13 - Tested on OS X 10.7 and Windows 7
|
||||
$this->version('Firefox')>=4.0 && !$isMobile ||
|
||||
|
||||
// Internet Explorer 7-9 - Tested on Windows XP, Vista and 7
|
||||
$this->version('MSIE')>=7.0 && !$isMobile ||
|
||||
|
||||
// Opera Desktop 10-12 - Tested on OS X 10.7 and Windows 7
|
||||
// @reference: http://my.opera.com/community/openweb/idopera/
|
||||
$this->version('Opera')>=10 && !$isMobile
|
||||
|
||||
|
||||
){
|
||||
return 'A';
|
||||
}
|
||||
|
||||
if(
|
||||
// Blackberry 5.0: Tested on the Storm 2 9550, Bold 9770
|
||||
$this->version('BlackBerry')>=5 && $this->version('BlackBerry')<6 ||
|
||||
|
||||
//Opera Mini (5.0-6.5) - Tested on iOS 3.2/4.3 and Android 2.3
|
||||
( $this->version('Opera Mini')>=5.0 && $this->version('Opera Mini')<=6.5 &&
|
||||
($this->version('Android')>=2.3 || $this->is('iOS')) ) ||
|
||||
|
||||
// Nokia Symbian^3 - Tested on Nokia N8 (Symbian^3), C7 (Symbian^3), also works on N97 (Symbian^1)
|
||||
$this->match('NokiaN8|NokiaC7|N97.*Series60|Symbian/3') ||
|
||||
|
||||
// @todo: report this (tested on Nokia N71)
|
||||
$this->version('Opera Mobi')>=11 && $this->is('SymbianOS')
|
||||
|
||||
){
|
||||
return 'B';
|
||||
}
|
||||
|
||||
if(
|
||||
// Blackberry 4.x - Tested on the Curve 8330
|
||||
$this->version('BlackBerry')<5.0 ||
|
||||
// Windows Mobile - Tested on the HTC Leo (WinMo 5.2)
|
||||
$this->match('MSIEMobile|Windows CE.*Mobile') || $this->version('Windows Mobile')<=5.2
|
||||
|
||||
|
||||
){
|
||||
|
||||
return 'C';
|
||||
|
||||
}
|
||||
|
||||
// All older smartphone platforms and featurephones - Any device that doesn't support media queries will receive the basic, C grade experience
|
||||
return 'C';
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
$detect = new Mobile_Detect;
|
||||
536
public/include/lib/scrypt.php
Normal file
536
public/include/lib/scrypt.php
Normal file
@ -0,0 +1,536 @@
|
||||
<?php
|
||||
/**
|
||||
* Zend Framework (http://framework.zend.com/)
|
||||
*
|
||||
* @link http://github.com/zendframework/zf2 for the canonical source repository
|
||||
* @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Scrypt key derivation function
|
||||
*
|
||||
* @see http://www.tarsnap.com/scrypt.html
|
||||
* @see https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01
|
||||
*/
|
||||
abstract class Scrypt
|
||||
{
|
||||
/**
|
||||
* Execute the scrypt algorithm
|
||||
*
|
||||
* @param string $password
|
||||
* @param string $salt
|
||||
* @param integer $n CPU cost
|
||||
* @param integer $r Memory cost
|
||||
* @param integer $p parallelization cost
|
||||
* @param integer $length size of the output key
|
||||
* @return string
|
||||
*/
|
||||
public static function calc($password, $salt, $n, $r, $p, $length)
|
||||
{
|
||||
if ($n == 0 || ($n & ($n - 1)) != 0) {
|
||||
throw new Exception\InvalidArgumentException("N must be > 0 and a power of 2");
|
||||
}
|
||||
if ($n > PHP_INT_MAX / 128 / $r) {
|
||||
throw new Exception\InvalidArgumentException("Parameter n is too large");
|
||||
}
|
||||
if ($r > PHP_INT_MAX / 128 / $p) {
|
||||
throw new Exception\InvalidArgumentException("Parameter r is too large");
|
||||
}
|
||||
|
||||
if (extension_loaded('Scrypt')) {
|
||||
if ($length < 16) {
|
||||
throw new Exception\InvalidArgumentException("Key length is too low, must be greater or equal to 16");
|
||||
}
|
||||
return self::hex2bin(scrypt($password, $salt, $n, $r, $p, $length));
|
||||
}
|
||||
|
||||
$b = Pbkdf2::calc('sha256', $password, $salt, 1, $p * 128 * $r);
|
||||
|
||||
$s = '';
|
||||
for ($i = 0; $i < $p; $i++) {
|
||||
$s .= self::scryptROMix(substr($b, $i * 128 * $r, 128 * $r), $n, $r);
|
||||
}
|
||||
|
||||
return Pbkdf2::calc('sha256', $password, $s, 1, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* scryptROMix
|
||||
*
|
||||
* @param string $b
|
||||
* @param integer $n
|
||||
* @param integer $r
|
||||
* @return string
|
||||
* @see https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01#section-4
|
||||
*/
|
||||
protected static function scryptROMix($b, $n, $r)
|
||||
{
|
||||
$x = $b;
|
||||
$v = array();
|
||||
for ($i = 0; $i < $n; $i++) {
|
||||
$v[$i] = $x;
|
||||
$x = self::scryptBlockMix($x, $r);
|
||||
}
|
||||
for ($i = 0; $i < $n; $i++) {
|
||||
$j = self::integerify($x) % $n;
|
||||
$t = $x ^ $v[$j];
|
||||
$x = self::scryptBlockMix($t, $r);
|
||||
}
|
||||
return $x;
|
||||
}
|
||||
|
||||
/**
|
||||
* scryptBlockMix
|
||||
*
|
||||
* @param string $b
|
||||
* @param integer $r
|
||||
* @return string
|
||||
* @see https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01#section-3
|
||||
*/
|
||||
protected static function scryptBlockMix($b, $r)
|
||||
{
|
||||
$x = substr($b, -64);
|
||||
$even = '';
|
||||
$odd = '';
|
||||
$len = 2 * $r;
|
||||
|
||||
for ($i = 0; $i < $len; $i++) {
|
||||
if (PHP_INT_SIZE === 4) {
|
||||
$x = self::salsa208Core32($x ^ substr($b, 64 * $i, 64));
|
||||
} else {
|
||||
$x = self::salsa208Core64($x ^ substr($b, 64 * $i, 64));
|
||||
}
|
||||
if ($i % 2 == 0) {
|
||||
$even .= $x;
|
||||
} else {
|
||||
$odd .= $x;
|
||||
}
|
||||
}
|
||||
return $even . $odd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Salsa 20/8 core (32 bit version)
|
||||
*
|
||||
* @param string $b
|
||||
* @return string
|
||||
* @see https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01#section-2
|
||||
* @see http://cr.yp.to/salsa20.html
|
||||
*/
|
||||
protected static function salsa208Core32($b)
|
||||
{
|
||||
$b32 = array();
|
||||
for ($i = 0; $i < 16; $i++) {
|
||||
list(, $b32[$i]) = unpack("V", substr($b, $i * 4, 4));
|
||||
}
|
||||
|
||||
$x = $b32;
|
||||
for ($i = 0; $i < 8; $i += 2) {
|
||||
$a = ($x[ 0] + $x[12]);
|
||||
$x[ 4] ^= ($a << 7) | ($a >> 25) & 0x7f;
|
||||
$a = ($x[ 4] + $x[ 0]);
|
||||
$x[ 8] ^= ($a << 9) | ($a >> 23) & 0x1ff;
|
||||
$a = ($x[ 8] + $x[ 4]);
|
||||
$x[12] ^= ($a << 13) | ($a >> 19) & 0x1fff;
|
||||
$a = ($x[12] + $x[ 8]);
|
||||
$x[ 0] ^= ($a << 18) | ($a >> 14) & 0x3ffff;
|
||||
$a = ($x[ 5] + $x[ 1]);
|
||||
$x[ 9] ^= ($a << 7) | ($a >> 25) & 0x7f;
|
||||
$a = ($x[ 9] + $x[ 5]);
|
||||
$x[13] ^= ($a << 9) | ($a >> 23) & 0x1ff;
|
||||
$a = ($x[13] + $x[ 9]);
|
||||
$x[ 1] ^= ($a << 13) | ($a >> 19) & 0x1fff;
|
||||
$a = ($x[ 1] + $x[13]);
|
||||
$x[ 5] ^= ($a << 18) | ($a >> 14) & 0x3ffff;
|
||||
$a = ($x[10] + $x[ 6]);
|
||||
$x[14] ^= ($a << 7) | ($a >> 25) & 0x7f;
|
||||
$a = ($x[14] + $x[10]);
|
||||
$x[ 2] ^= ($a << 9) | ($a >> 23) & 0x1ff;
|
||||
$a = ($x[ 2] + $x[14]);
|
||||
$x[ 6] ^= ($a << 13) | ($a >> 19) & 0x1fff;
|
||||
$a = ($x[ 6] + $x[ 2]);
|
||||
$x[10] ^= ($a << 18) | ($a >> 14) & 0x3ffff;
|
||||
$a = ($x[15] + $x[11]);
|
||||
$x[ 3] ^= ($a << 7) | ($a >> 25) & 0x7f;
|
||||
$a = ($x[ 3] + $x[15]);
|
||||
$x[ 7] ^= ($a << 9) | ($a >> 23) & 0x1ff;
|
||||
$a = ($x[ 7] + $x[ 3]);
|
||||
$x[11] ^= ($a << 13) | ($a >> 19) & 0x1fff;
|
||||
$a = ($x[11] + $x[ 7]);
|
||||
$x[15] ^= ($a << 18) | ($a >> 14) & 0x3ffff;
|
||||
$a = ($x[ 0] + $x[ 3]);
|
||||
$x[ 1] ^= ($a << 7) | ($a >> 25) & 0x7f;
|
||||
$a = ($x[ 1] + $x[ 0]);
|
||||
$x[ 2] ^= ($a << 9) | ($a >> 23) & 0x1ff;
|
||||
$a = ($x[ 2] + $x[ 1]);
|
||||
$x[ 3] ^= ($a << 13) | ($a >> 19) & 0x1fff;
|
||||
$a = ($x[ 3] + $x[ 2]);
|
||||
$x[ 0] ^= ($a << 18) | ($a >> 14) & 0x3ffff;
|
||||
$a = ($x[ 5] + $x[ 4]);
|
||||
$x[ 6] ^= ($a << 7) | ($a >> 25) & 0x7f;
|
||||
$a = ($x[ 6] + $x[ 5]);
|
||||
$x[ 7] ^= ($a << 9) | ($a >> 23) & 0x1ff;
|
||||
$a = ($x[ 7] + $x[ 6]);
|
||||
$x[ 4] ^= ($a << 13) | ($a >> 19) & 0x1fff;
|
||||
$a = ($x[ 4] + $x[ 7]);
|
||||
$x[ 5] ^= ($a << 18) | ($a >> 14) & 0x3ffff;
|
||||
$a = ($x[10] + $x[ 9]);
|
||||
$x[11] ^= ($a << 7) | ($a >> 25) & 0x7f;
|
||||
$a = ($x[11] + $x[10]);
|
||||
$x[ 8] ^= ($a << 9) | ($a >> 23) & 0x1ff;
|
||||
$a = ($x[ 8] + $x[11]);
|
||||
$x[ 9] ^= ($a << 13) | ($a >> 19) & 0x1fff;
|
||||
$a = ($x[ 9] + $x[ 8]);
|
||||
$x[10] ^= ($a << 18) | ($a >> 14) & 0x3ffff;
|
||||
$a = ($x[15] + $x[14]);
|
||||
$x[12] ^= ($a << 7) | ($a >> 25) & 0x7f;
|
||||
$a = ($x[12] + $x[15]);
|
||||
$x[13] ^= ($a << 9) | ($a >> 23) & 0x1ff;
|
||||
$a = ($x[13] + $x[12]);
|
||||
$x[14] ^= ($a << 13) | ($a >> 19) & 0x1fff;
|
||||
$a = ($x[14] + $x[13]);
|
||||
$x[15] ^= ($a << 18) | ($a >> 14) & 0x3ffff;
|
||||
}
|
||||
for ($i = 0; $i < 16; $i++) {
|
||||
$b32[$i] = $b32[$i] + $x[$i];
|
||||
}
|
||||
$result = '';
|
||||
for ($i = 0; $i < 16; $i++) {
|
||||
$result .= pack("V", $b32[$i]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Salsa 20/8 core (64 bit version)
|
||||
*
|
||||
* @param string $b
|
||||
* @return string
|
||||
* @see https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01#section-2
|
||||
* @see http://cr.yp.to/salsa20.html
|
||||
*/
|
||||
protected static function salsa208Core64($b)
|
||||
{
|
||||
$b32 = array();
|
||||
for ($i = 0; $i < 16; $i++) {
|
||||
list(, $b32[$i]) = unpack("V", substr($b, $i * 4, 4));
|
||||
}
|
||||
|
||||
$x = $b32;
|
||||
for ($i = 0; $i < 8; $i += 2) {
|
||||
$a = ($x[ 0] + $x[12]) & 0xffffffff;
|
||||
$x[ 4] ^= ($a << 7) | ($a >> 25);
|
||||
$a = ($x[ 4] + $x[ 0]) & 0xffffffff;
|
||||
$x[ 8] ^= ($a << 9) | ($a >> 23);
|
||||
$a = ($x[ 8] + $x[ 4]) & 0xffffffff;
|
||||
$x[12] ^= ($a << 13) | ($a >> 19);
|
||||
$a = ($x[12] + $x[ 8]) & 0xffffffff;
|
||||
$x[ 0] ^= ($a << 18) | ($a >> 14);
|
||||
$a = ($x[ 5] + $x[ 1]) & 0xffffffff;
|
||||
$x[ 9] ^= ($a << 7) | ($a >> 25);
|
||||
$a = ($x[ 9] + $x[ 5]) & 0xffffffff;
|
||||
$x[13] ^= ($a << 9) | ($a >> 23);
|
||||
$a = ($x[13] + $x[ 9]) & 0xffffffff;
|
||||
$x[ 1] ^= ($a << 13) | ($a >> 19);
|
||||
$a = ($x[ 1] + $x[13]) & 0xffffffff;
|
||||
$x[ 5] ^= ($a << 18) | ($a >> 14);
|
||||
$a = ($x[10] + $x[ 6]) & 0xffffffff;
|
||||
$x[14] ^= ($a << 7) | ($a >> 25);
|
||||
$a = ($x[14] + $x[10]) & 0xffffffff;
|
||||
$x[ 2] ^= ($a << 9) | ($a >> 23);
|
||||
$a = ($x[ 2] + $x[14]) & 0xffffffff;
|
||||
$x[ 6] ^= ($a << 13) | ($a >> 19);
|
||||
$a = ($x[ 6] + $x[ 2]) & 0xffffffff;
|
||||
$x[10] ^= ($a << 18) | ($a >> 14);
|
||||
$a = ($x[15] + $x[11]) & 0xffffffff;
|
||||
$x[ 3] ^= ($a << 7) | ($a >> 25);
|
||||
$a = ($x[ 3] + $x[15]) & 0xffffffff;
|
||||
$x[ 7] ^= ($a << 9) | ($a >> 23);
|
||||
$a = ($x[ 7] + $x[ 3]) & 0xffffffff;
|
||||
$x[11] ^= ($a << 13) | ($a >> 19);
|
||||
$a = ($x[11] + $x[ 7]) & 0xffffffff;
|
||||
$x[15] ^= ($a << 18) | ($a >> 14);
|
||||
$a = ($x[ 0] + $x[ 3]) & 0xffffffff;
|
||||
$x[ 1] ^= ($a << 7) | ($a >> 25);
|
||||
$a = ($x[ 1] + $x[ 0]) & 0xffffffff;
|
||||
$x[ 2] ^= ($a << 9) | ($a >> 23);
|
||||
$a = ($x[ 2] + $x[ 1]) & 0xffffffff;
|
||||
$x[ 3] ^= ($a << 13) | ($a >> 19);
|
||||
$a = ($x[ 3] + $x[ 2]) & 0xffffffff;
|
||||
$x[ 0] ^= ($a << 18) | ($a >> 14);
|
||||
$a = ($x[ 5] + $x[ 4]) & 0xffffffff;
|
||||
$x[ 6] ^= ($a << 7) | ($a >> 25);
|
||||
$a = ($x[ 6] + $x[ 5]) & 0xffffffff;
|
||||
$x[ 7] ^= ($a << 9) | ($a >> 23);
|
||||
$a = ($x[ 7] + $x[ 6]) & 0xffffffff;
|
||||
$x[ 4] ^= ($a << 13) | ($a >> 19);
|
||||
$a = ($x[ 4] + $x[ 7]) & 0xffffffff;
|
||||
$x[ 5] ^= ($a << 18) | ($a >> 14);
|
||||
$a = ($x[10] + $x[ 9]) & 0xffffffff;
|
||||
$x[11] ^= ($a << 7) | ($a >> 25);
|
||||
$a = ($x[11] + $x[10]) & 0xffffffff;
|
||||
$x[ 8] ^= ($a << 9) | ($a >> 23);
|
||||
$a = ($x[ 8] + $x[11]) & 0xffffffff;
|
||||
$x[ 9] ^= ($a << 13) | ($a >> 19);
|
||||
$a = ($x[ 9] + $x[ 8]) & 0xffffffff;
|
||||
$x[10] ^= ($a << 18) | ($a >> 14);
|
||||
$a = ($x[15] + $x[14]) & 0xffffffff;
|
||||
$x[12] ^= ($a << 7) | ($a >> 25);
|
||||
$a = ($x[12] + $x[15]) & 0xffffffff;
|
||||
$x[13] ^= ($a << 9) | ($a >> 23);
|
||||
$a = ($x[13] + $x[12]) & 0xffffffff;
|
||||
$x[14] ^= ($a << 13) | ($a >> 19);
|
||||
$a = ($x[14] + $x[13]) & 0xffffffff;
|
||||
$x[15] ^= ($a << 18) | ($a >> 14);
|
||||
}
|
||||
for ($i = 0; $i < 16; $i++) {
|
||||
$b32[$i] = ($b32[$i] + $x[$i]) & 0xffffffff;
|
||||
}
|
||||
$result = '';
|
||||
for ($i = 0; $i < 16; $i++) {
|
||||
$result .= pack("V", $b32[$i]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Integerify
|
||||
*
|
||||
* Integerify (B[0] ... B[2 * r - 1]) is defined as the result
|
||||
* of interpreting B[2 * r - 1] as a little-endian integer.
|
||||
* Each block B is a string of 64 bytes.
|
||||
*
|
||||
* @param string $b
|
||||
* @return integer
|
||||
* @see https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01#section-4
|
||||
*/
|
||||
protected static function integerify($b)
|
||||
{
|
||||
$v = 'v';
|
||||
if (PHP_INT_SIZE === 8) {
|
||||
$v = 'V';
|
||||
}
|
||||
list(,$n) = unpack($v, substr($b, -64));
|
||||
return $n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert hex string in a binary string
|
||||
*
|
||||
* @param string $hex
|
||||
* @return string
|
||||
*/
|
||||
protected static function hex2bin($hex)
|
||||
{
|
||||
if (version_compare(PHP_VERSION, '5.4') >= 0) {
|
||||
return hex2bin($hex);
|
||||
}
|
||||
$len = strlen($hex);
|
||||
$result = '';
|
||||
for ($i = 0; $i < $len; $i+=2) {
|
||||
$result .= chr(hexdec($hex[$i] . $hex[$i+1]));
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Zend Framework (http://framework.zend.com/)
|
||||
*
|
||||
* @link http://github.com/zendframework/zf2 for the canonical source repository
|
||||
* @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* PKCS #5 v2.0 standard RFC 2898
|
||||
*/
|
||||
class Pbkdf2
|
||||
{
|
||||
/**
|
||||
* Generate the new key
|
||||
*
|
||||
* @param string $hash The hash algorithm to be used by HMAC
|
||||
* @param string $password The source password/key
|
||||
* @param string $salt
|
||||
* @param integer $iterations The number of iterations
|
||||
* @param integer $length The output size
|
||||
* @throws Exception\InvalidArgumentException
|
||||
* @return string
|
||||
*/
|
||||
public static function calc($hash, $password, $salt, $iterations, $length)
|
||||
{
|
||||
if (!Hmac::isSupported($hash)) {
|
||||
throw new Exception\InvalidArgumentException("The hash algorithm $hash is not supported by " . __CLASS__);
|
||||
}
|
||||
|
||||
$num = ceil($length / Hmac::getOutputSize($hash, Hmac::OUTPUT_BINARY));
|
||||
$result = '';
|
||||
for ($block = 1; $block <= $num; $block++) {
|
||||
$hmac = hash_hmac($hash, $salt . pack('N', $block), $password, Hmac::OUTPUT_BINARY);
|
||||
$mix = $hmac;
|
||||
for ($i = 1; $i < $iterations; $i++) {
|
||||
$hmac = hash_hmac($hash, $hmac, $password, Hmac::OUTPUT_BINARY);
|
||||
$mix ^= $hmac;
|
||||
}
|
||||
$result .= $mix;
|
||||
}
|
||||
return substr($result, 0, $length);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Zend Framework (http://framework.zend.com/)
|
||||
*
|
||||
* @link http://github.com/zendframework/zf2 for the canonical source repository
|
||||
* @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* PHP implementation of the RFC 2104 Hash based Message Authentication Code
|
||||
*/
|
||||
class Hmac
|
||||
{
|
||||
const OUTPUT_STRING = false;
|
||||
const OUTPUT_BINARY = true;
|
||||
|
||||
/**
|
||||
* Last algorithm supported
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected static $lastAlgorithmSupported;
|
||||
|
||||
/**
|
||||
* Performs a HMAC computation given relevant details such as Key, Hashing
|
||||
* algorithm, the data to compute MAC of, and an output format of String,
|
||||
* or Binary.
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $hash
|
||||
* @param string $data
|
||||
* @param bool $output
|
||||
* @throws Exception\InvalidArgumentException
|
||||
* @return string
|
||||
*/
|
||||
public static function compute($key, $hash, $data, $output = self::OUTPUT_STRING)
|
||||
{
|
||||
|
||||
if (empty($key)) {
|
||||
throw new Exception\InvalidArgumentException('Provided key is null or empty');
|
||||
}
|
||||
|
||||
if (!$hash || ($hash !== static::$lastAlgorithmSupported && !static::isSupported($hash))) {
|
||||
throw new Exception\InvalidArgumentException(
|
||||
"Hash algorithm is not supported on this PHP installation; provided '{$hash}'"
|
||||
);
|
||||
}
|
||||
|
||||
return hash_hmac($hash, $data, $key, $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the output size according to the hash algorithm and the output format
|
||||
*
|
||||
* @param string $hash
|
||||
* @param bool $output
|
||||
* @return integer
|
||||
*/
|
||||
public static function getOutputSize($hash, $output = self::OUTPUT_STRING)
|
||||
{
|
||||
return strlen(static::compute('key', $hash, 'data', $output));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the supported algorithm
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getSupportedAlgorithms()
|
||||
{
|
||||
return hash_algos();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the hash algorithm supported?
|
||||
*
|
||||
* @param string $algorithm
|
||||
* @return bool
|
||||
*/
|
||||
public static function isSupported($algorithm)
|
||||
{
|
||||
if ($algorithm === static::$lastAlgorithmSupported) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (in_array(strtolower($algorithm), hash_algos(), true)) {
|
||||
static::$lastAlgorithmSupported = $algorithm;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the cache of last algorithm supported
|
||||
*/
|
||||
public static function clearLastAlgorithmCache()
|
||||
{
|
||||
static::$lastAlgorithmSupported = null;
|
||||
}
|
||||
}
|
||||
function swapEndian($input){
|
||||
$output = "";
|
||||
for($i=0;$i< strlen($input);$i+=2){
|
||||
$output .= substr($input, -($i+2), 2);
|
||||
|
||||
|
||||
}
|
||||
return $output;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*for($i=0;$i < 200;$i++){
|
||||
$value = Scrypt::calc($i, $i, 1024, 1, 1, 32);
|
||||
echo "scrypt ".$i." hash:". bin2hex($value)."<br/>";
|
||||
}*/
|
||||
/*
|
||||
$i = pack("H*", "01000000f615f7ce3b4fc6b8f61e8f89aedb1d0852507650533a9e3b10b9bbcc30639f279fcaa86746e1ef52d3edb3c4ad8259920d509bd073605c9bf1d59983752a6b06b817bb4ea78e011d012d59d4");
|
||||
|
||||
$value = Scrypt::calc($i, $i, 1024, 1, 1, 32);
|
||||
echo "scrypt ".$i." hash:". bin2hex($value)."<br/>";
|
||||
print_r( swapEndian(bin2hex($value)));
|
||||
*/
|
||||
|
||||
|
||||
// Function used for pushpoold solution checks
|
||||
function word_reverse($str) {
|
||||
$ret = '';
|
||||
while (strlen($str) > 0) {
|
||||
$ret .= substr($str, -8, 8);
|
||||
$str = substr($str, 0, -8);
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
?>
|
||||
8
public/include/pages/about/api.inc.php
Normal file
8
public/include/pages/about/api.inc.php
Normal file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
// Tempalte specifics
|
||||
$smarty->assign("CONTENT", "default.tpl");
|
||||
?>
|
||||
11
public/include/pages/about/donors.inc.php
Normal file
11
public/include/pages/about/donors.inc.php
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
$aDonors = $transaction->getDonations();
|
||||
|
||||
// Tempalte specifics
|
||||
$smarty->assign("DONORS", $aDonors);
|
||||
$smarty->assign("CONTENT", "default.tpl");
|
||||
?>
|
||||
17
public/include/pages/account/confirm.inc.php
Normal file
17
public/include/pages/account/confirm.inc.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
// Confirm an account by token
|
||||
if (!isset($_GET['token']) || empty($_GET['token'])) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Missing token', 'TYPE' => 'errormsg');
|
||||
} else if (!$aToken = $oToken->getToken($_GET['token'])) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to activate your account. Invalid token', 'TYPE' => 'errormsg');
|
||||
} else {
|
||||
$user->changeLocked($aToken['account_id']);
|
||||
$oToken->deleteToken($aToken['token']);
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Account activated. Please login.');
|
||||
}
|
||||
$smarty->assign('CONTENT', 'default.tpl');
|
||||
?>
|
||||
@ -10,57 +10,29 @@ if ($user->isAuthenticated()) {
|
||||
} else {
|
||||
switch (@$_POST['do']) {
|
||||
case 'cashOut':
|
||||
if ($setting->getValue('manual_payout_active') == 1) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'A manual payout is in progress. Please try again later.', 'TYPE' => 'errormsg');
|
||||
if ($setting->getValue('disable_mp') == 1) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Manual payouts are disabled.', 'TYPE' => 'info');
|
||||
} else {
|
||||
$setting->setValue('manual_payout_active', 1);
|
||||
$continue = true;
|
||||
$aBalance = $transaction->getBalance($_SESSION['USERDATA']['id']);
|
||||
$dBalance = $aBalance['confirmed'];
|
||||
$sCoinAddress = $user->getCoinAddress($_SESSION['USERDATA']['id']);
|
||||
// Ensure we can cover the potential transaction fee
|
||||
if ($dBalance > $config['txfee']) {
|
||||
if ($bitcoin->can_connect() === true) {
|
||||
try {
|
||||
$bitcoin->validateaddress($sCoinAddress);
|
||||
} catch (BitcoinClientException $e) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Invalid payment address: ' . $sUserSendAddress, 'TYPE' => 'errormsg');
|
||||
$continue = false;
|
||||
}
|
||||
if ($continue == true) {
|
||||
// Send balance to address, mind fee for transaction!
|
||||
try {
|
||||
if ($setting->getValue('auto_payout_active') == 0) {
|
||||
$bitcoin->sendtoaddress($sCoinAddress, $dBalance);
|
||||
} else {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Auto-payout active, please contact site support immidiately to revoke invalid transactions.', 'TYPE' => 'errormsg');
|
||||
$continue = false;
|
||||
}
|
||||
} catch (BitcoinClientException $e) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Failed to send ' . $config['currency'] . ', please contact site support immidiately', 'TYPE' => 'errormsg');
|
||||
$continue = false;
|
||||
}
|
||||
}
|
||||
// Set balance to 0, add to paid out, insert to ledger
|
||||
if ($continue == true && $transaction->addTransaction($_SESSION['USERDATA']['id'], $dBalance - $config['txfee'], 'Debit_MP', NULL, $sCoinAddress) && $transaction->addTransaction($_SESSION['USERDATA']['id'], $config['txfee'], 'TXFee', NULL, $sCoinAddress) ) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Transaction completed', 'TYPE' => 'success');
|
||||
$aMailData['email'] = $user->getUserEmail($user->getUserName($_SESSION['USERDATA']['id']));
|
||||
$aMailData['amount'] = $dBalance;
|
||||
$aMailData['subject'] = 'Manual Payout Completed';
|
||||
$notification->sendNotification($_SESSION['USERDATA']['id'], 'manual_payout', $aMailData);
|
||||
if (!$oPayout->isPayoutActive($_SESSION['USERDATA']['id'])) {
|
||||
if ($iPayoutId = $oPayout->createPayout($_SESSION['USERDATA']['id'])) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Created new manual payout request with ID #' . $iPayoutId);
|
||||
} else {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Failed to create manual payout request.', 'TYPE' => 'errormsg');
|
||||
}
|
||||
} else {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to litecoind RPC service', 'TYPE' => 'errormsg');
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'You already have one active manual payout request.', 'TYPE' => 'errormsg');
|
||||
}
|
||||
} else {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Insufficient funds, you need more than ' . $config['txfee'] . ' ' . $conifg['currency'] . ' to cover transaction fees', 'TYPE' => 'errormsg');
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Insufficient funds, you need more than ' . $config['txfee'] . ' ' . $config['currency'] . ' to cover transaction fees', 'TYPE' => 'errormsg');
|
||||
}
|
||||
$setting->setValue('manual_payout_active', 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'updateAccount':
|
||||
if ($user->updateAccount($_SESSION['USERDATA']['id'], $_POST['paymentAddress'], $_POST['payoutThreshold'], $_POST['donatePercent'], $_POST['email'])) {
|
||||
if ($user->updateAccount($_SESSION['USERDATA']['id'], $_POST['paymentAddress'], $_POST['payoutThreshold'], $_POST['donatePercent'], $_POST['email'], $_POST['is_anonymous'])) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Account details updated', 'TYPE' => 'success');
|
||||
} else {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Failed to update your account: ' . $user->getError(), 'TYPE' => 'errormsg');
|
||||
|
||||
25
public/include/pages/account/invitations.inc.php
Normal file
25
public/include/pages/account/invitations.inc.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
if ($user->isAuthenticated()) {
|
||||
if (!$setting->getValue('disable_invitations')) {
|
||||
if ($invitation->getCountInvitations($_SESSION['USERDATA']['id']) >= $config['accounts']['invitations']['count']) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'You have exceeded the allowed invitations of ' . $config['accounts']['invitations']['count'], 'TYPE' => 'errormsg');
|
||||
} else if (isset($_POST['do']) && $_POST['do'] == 'sendInvitation') {
|
||||
if ($invitation->sendInvitation($_SESSION['USERDATA']['id'], $_POST['data'])) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Invitation sent');
|
||||
} else {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to send invitation to recipient: ' . $invitation->getError(), 'TYPE' => 'errormsg');
|
||||
}
|
||||
}
|
||||
$aInvitations = $invitation->getInvitations($_SESSION['USERDATA']['id']);
|
||||
$smarty->assign('INVITATIONS', $aInvitations);
|
||||
} else {
|
||||
$aInvitations = array();
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Invitations are disabled', 'TYPE' => 'errormsg');
|
||||
}
|
||||
}
|
||||
$smarty->assign('CONTENT', 'default.tpl');
|
||||
?>
|
||||
@ -3,23 +3,28 @@
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
if ($user->isAuthenticated()) {
|
||||
if (@$_REQUEST['do'] == 'save') {
|
||||
if ($notification->updateSettings($_SESSION['USERDATA']['id'], $_REQUEST['data'])) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Updated notification settings');
|
||||
} else {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Failed to update settings', 'TYPE' => 'errormsg');
|
||||
if ($setting->getValue('disable_notifications') == 1) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Notification system disabled by admin.', 'TYPE' => 'info');
|
||||
$smarty->assign('CONTENT', 'empty');
|
||||
} else {
|
||||
if (@$_REQUEST['do'] == 'save') {
|
||||
if ($notification->updateSettings($_SESSION['USERDATA']['id'], $_REQUEST['data'])) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Updated notification settings');
|
||||
} else {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Failed to update settings', 'TYPE' => 'errormsg');
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch notifications
|
||||
$aNotifications = $notification->getNofifications($_SESSION['USERDATA']['id']);
|
||||
if (!$aNotifications) $_SESSION['POPUP'][] = array('CONTENT' => 'Could not find any notifications', 'TYPE' => 'errormsg');
|
||||
|
||||
// Fetch user notification settings
|
||||
$aSettings = $notification->getNotificationSettings($_SESSION['USERDATA']['id']);
|
||||
|
||||
$smarty->assign('NOTIFICATIONS', $aNotifications);
|
||||
$smarty->assign('SETTINGS', $aSettings);
|
||||
$smarty->assign('CONTENT', 'default.tpl');
|
||||
}
|
||||
|
||||
// Fetch notifications
|
||||
$aNotifications = $notification->getNofifications($_SESSION['USERDATA']['id']);
|
||||
if (!$aNotifications) $_SESSION['POPUP'][] = array('CONTENT' => 'Could not find any notifications', 'TYPE' => 'errormsg');
|
||||
|
||||
// Fetch user notification settings
|
||||
$aSettings = $notification->getNotificationSettings($_SESSION['USERDATA']['id']);
|
||||
|
||||
$smarty->assign('NOTIFICATIONS', $aNotifications);
|
||||
$smarty->assign('SETTINGS', $aSettings);
|
||||
$smarty->assign('CONTENT', 'default.tpl');
|
||||
}
|
||||
?>
|
||||
|
||||
6
public/include/pages/account/qrcode.inc.php
Normal file
6
public/include/pages/account/qrcode.inc.php
Normal file
@ -0,0 +1,6 @@
|
||||
<?php
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
if ($user->isAuthenticated()) $smarty->assign("CONTENT", "default.tpl");
|
||||
?>
|
||||
13
public/include/pages/account/reset_failed.inc.php
Normal file
13
public/include/pages/account/reset_failed.inc.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
if ($user->isAuthenticated()) {
|
||||
// Reset failed login counter
|
||||
$user->setUserFailed($_SESSION['USERDATA']['id'], 0);
|
||||
header("Location: " . $_SERVER['HTTP_REFERER']);
|
||||
}
|
||||
// Somehow we still need to load this empty template
|
||||
$smarty->assign("CONTENT", "empty");
|
||||
?>
|
||||
@ -3,9 +3,19 @@
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
if ($user->isAuthenticated()) {
|
||||
$aTransactions = $transaction->getTransactions($_SESSION['USERDATA']['id']);
|
||||
$iLimit = 30;
|
||||
empty($_REQUEST['start']) ? $start = 0 : $start = $_REQUEST['start'];
|
||||
$aTransactions = $transaction->getTransactions($start, @$_REQUEST['filter'], $iLimit, $_SESSION['USERDATA']['id']);
|
||||
$aTransactionSummary = $transaction->getTransactionSummary($_SESSION['USERDATA']['id']);
|
||||
$iCountTransactions = $transaction->num_rows;
|
||||
$aTransactionTypes = $transaction->getTypes();
|
||||
if (!$aTransactions) $_SESSION['POPUP'][] = array('CONTENT' => 'Could not find any transaction', 'TYPE' => 'errormsg');
|
||||
$smarty->assign('LIMIT', $iLimit);
|
||||
$smarty->assign('TRANSACTIONS', $aTransactions);
|
||||
$smarty->assign('CONTENT', 'default.tpl');
|
||||
$smarty->assign('SUMMARY', $aTransactionSummary);
|
||||
$smarty->assign('TRANSACTIONTYPES', $aTransactionTypes);
|
||||
$smarty->assign('TXSTATUS', array('' => '', 'Confirmed' => 'Confirmed', 'Unconfirmed' => 'Unconfirmed', 'Orphan' => 'Orphan'));
|
||||
$smarty->assign('COUNTTRANSACTIONS', $iCountTransactions);
|
||||
}
|
||||
$smarty->assign('CONTENT', 'default.tpl');
|
||||
?>
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
@ -20,7 +19,7 @@ if ($user->isAuthenticated()) {
|
||||
}
|
||||
break;
|
||||
case 'update':
|
||||
if ($worker->updateWorkers($_SESSION['USERDATA']['id'], $_POST['data'])) {
|
||||
if ($worker->updateWorkers($_SESSION['USERDATA']['id'], @$_POST['data'])) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Worker updated');
|
||||
} else {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => $worker->getError(), 'TYPE' => 'errormsg');
|
||||
|
||||
43
public/include/pages/admin/monitoring.inc.php
Normal file
43
public/include/pages/admin/monitoring.inc.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
// Check user to ensure they are admin
|
||||
if (!$user->isAuthenticated() || !$user->isAdmin($_SESSION['USERDATA']['id'])) {
|
||||
header("HTTP/1.1 404 Page not found");
|
||||
die("404 Page not found");
|
||||
}
|
||||
|
||||
// Default crons to monitor
|
||||
$aCrons = array('statistics','auto_payout','manual_payout','archive_cleanup','blockupdate','findblock','notifications','tickerupdate');
|
||||
|
||||
// Special cases, only add them if activated
|
||||
switch ($config['payout_system']) {
|
||||
case 'pplns':
|
||||
$aCrons[] = $config['payout_system'] . '_payout';
|
||||
break;
|
||||
case 'pps':
|
||||
$aCrons[] = $config['payout_system'] . '_payout';
|
||||
break;
|
||||
case 'prop':
|
||||
$aCrons[] = 'proportional_payout';
|
||||
break;
|
||||
}
|
||||
|
||||
// Data array for template
|
||||
foreach ($aCrons as $strCron) {
|
||||
$aCronStatus[$strCron] = array(
|
||||
'exit' => $monitoring->getStatus($strCron . '_status'),
|
||||
'active' => $monitoring->getStatus($strCron . '_active'),
|
||||
'runtime' => $monitoring->getStatus($strCron . '_runtime'),
|
||||
'starttime' => $monitoring->getStatus($strCron . '_starttime'),
|
||||
'endtime' => $monitoring->getStatus($strCron . '_endtime'),
|
||||
'message' => $monitoring->getStatus($strCron . '_message'),
|
||||
);
|
||||
}
|
||||
$smarty->assign("CRONSTATUS", $aCronStatus);
|
||||
|
||||
// Tempalte specifics
|
||||
$smarty->assign("CONTENT", "default.tpl");
|
||||
?>
|
||||
43
public/include/pages/admin/news.inc.php
Normal file
43
public/include/pages/admin/news.inc.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
// Check user to ensure they are admin
|
||||
if (!$user->isAuthenticated() || !$user->isAdmin($_SESSION['USERDATA']['id'])) {
|
||||
header("HTTP/1.1 404 Page not found");
|
||||
die("404 Page not found");
|
||||
}
|
||||
|
||||
// Include markdown library
|
||||
use \Michelf\Markdown;
|
||||
|
||||
if (@$_REQUEST['do'] == 'toggle_active')
|
||||
if ($news->toggleActive($_REQUEST['id']))
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'News entry changed', 'TYPE' => 'success');
|
||||
|
||||
if (@$_REQUEST['do'] == 'add') {
|
||||
if ($news->addNews($_SESSION['USERDATA']['id'], $_POST['data'])) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'News entry added', 'TYPE' => 'success');
|
||||
} else {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Failed to add new entry: ' . $news->getError(), 'TYPE' => 'errormsg');
|
||||
}
|
||||
}
|
||||
|
||||
if (@$_REQUEST['do'] == 'delete') {
|
||||
if ($news->deleteNews((int)$_REQUEST['id'])) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Succesfully removed news entry', 'TYPE' => 'success');
|
||||
} else {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Failed to delete entry: ' . $news->getError(), 'TYPE' => 'errormsg');
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch all news
|
||||
$aNews = $news->getAll();
|
||||
foreach ($aNews as $key => $aData) {
|
||||
// Transform Markdown content to HTML
|
||||
$aNews[$key]['content'] = Markdown::defaultTransform($aData['content']);
|
||||
}
|
||||
$smarty->assign("NEWS", $aNews);
|
||||
$smarty->assign("CONTENT", "default.tpl");
|
||||
?>
|
||||
27
public/include/pages/admin/news_edit.inc.php
Normal file
27
public/include/pages/admin/news_edit.inc.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
// Check user to ensure they are admin
|
||||
if (!$user->isAuthenticated() || !$user->isAdmin($_SESSION['USERDATA']['id'])) {
|
||||
header("HTTP/1.1 404 Page not found");
|
||||
die("404 Page not found");
|
||||
}
|
||||
|
||||
// Include markdown library
|
||||
use \Michelf\Markdown;
|
||||
|
||||
if (@$_REQUEST['do'] == 'save') {
|
||||
if ($news->updateNews($_REQUEST['id'], $_REQUEST['header'], $_REQUEST['content'], $_REQUEST['active'])) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'News updated', 'TYPE' => 'success');
|
||||
} else {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'News update failed: ' . $news->getError(), 'TYPE' => 'errormsg');
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch news entry
|
||||
$aNews = $news->getEntry($_REQUEST['id']);
|
||||
$smarty->assign("NEWS", $aNews);
|
||||
$smarty->assign("CONTENT", "default.tpl");
|
||||
?>
|
||||
27
public/include/pages/admin/settings.inc.php
Normal file
27
public/include/pages/admin/settings.inc.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
// Check user to ensure they are admin
|
||||
if (!$user->isAuthenticated() || !$user->isAdmin($_SESSION['USERDATA']['id'])) {
|
||||
header("HTTP/1.1 404 Page not found");
|
||||
die("404 Page not found");
|
||||
}
|
||||
|
||||
if (@$_REQUEST['do'] == 'save' && !empty($_REQUEST['data'])) {
|
||||
foreach($_REQUEST['data'] as $var => $value) {
|
||||
$setting->setValue($var, $value);
|
||||
}
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Settings updated');
|
||||
}
|
||||
|
||||
// Load our available settings from configuration
|
||||
require_once(INCLUDE_DIR . '/config/admin_settings.inc.php');
|
||||
|
||||
// Load onto the template
|
||||
$smarty->assign("SETTINGS", $aSettings);
|
||||
|
||||
// Tempalte specifics
|
||||
$smarty->assign("CONTENT", "default.tpl");
|
||||
?>
|
||||
41
public/include/pages/admin/transactions.inc.php
Normal file
41
public/include/pages/admin/transactions.inc.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
// Check user to ensure they are admin
|
||||
if (!$user->isAuthenticated() || !$user->isAdmin($_SESSION['USERDATA']['id'])) {
|
||||
header("HTTP/1.1 404 Page not found");
|
||||
die("404 Page not found");
|
||||
}
|
||||
|
||||
if (!$smarty->isCached('master.tpl', $smarty_cache_key)) {
|
||||
$iLimit = 30;
|
||||
$debug->append('No cached version available, fetching from backend', 3);
|
||||
empty($_REQUEST['start']) ? $start = 0 : $start = $_REQUEST['start'];
|
||||
$aTransactions = $transaction->getTransactions($start, @$_REQUEST['filter'], $iLimit);
|
||||
$aTransactionSummary = $transaction->getTransactionSummary();
|
||||
$iCountTransactions = $transaction->num_rows;
|
||||
$aTransactionTypes = $transaction->getTypes();
|
||||
if (!$aTransactions) $_SESSION['POPUP'][] = array('CONTENT' => 'Could not find any transaction', 'TYPE' => 'errormsg');
|
||||
$smarty->assign('LIMIT', $iLimit);
|
||||
$smarty->assign('TRANSACTIONS', $aTransactions);
|
||||
$smarty->assign('SUMMARY', $aTransactionSummary);
|
||||
$smarty->assign('TRANSACTIONTYPES', $aTransactionTypes);
|
||||
$smarty->assign('TXSTATUS', array('' => '', 'Confirmed' => 'Confirmed', 'Unconfirmed' => 'Unconfirmed', 'Orphan' => 'Orphan'));
|
||||
$smarty->assign('COUNTTRANSACTIONS', $iCountTransactions);
|
||||
} else {
|
||||
$debug->append('Using cached page', 3);
|
||||
}
|
||||
|
||||
// Gernerate the GET URL for filters
|
||||
if (isset($_REQUEST['filter'])) {
|
||||
$strFilters = '';
|
||||
foreach (@$_REQUEST['filter'] as $filter => $value) {
|
||||
$filter = "filter[$filter]";
|
||||
$strFilters .= "&$filter=$value";
|
||||
}
|
||||
$smarty->assign('FILTERS', $strFilters);
|
||||
}
|
||||
$smarty->assign('CONTENT', 'default.tpl');
|
||||
?>
|
||||
@ -11,16 +11,19 @@ if (!$user->isAuthenticated() || !$user->isAdmin($_SESSION['USERDATA']['id'])) {
|
||||
|
||||
$aRoundShares = $statistics->getRoundShares();
|
||||
|
||||
// Change account lock
|
||||
if (@$_POST['do'] == 'lock') {
|
||||
switch (@$_POST['do']) {
|
||||
case 'lock':
|
||||
$supress_master = 1;
|
||||
$user->changeLocked($_POST['account_id']);
|
||||
}
|
||||
|
||||
// Change account admin
|
||||
if (@$_POST['do'] == 'admin') {
|
||||
break;
|
||||
case 'fee':
|
||||
$supress_master = 1;
|
||||
$user->changeNoFee($_POST['account_id']);
|
||||
break;
|
||||
case 'admin':
|
||||
$supress_master = 1;
|
||||
$user->changeAdmin($_POST['account_id']);
|
||||
break;
|
||||
}
|
||||
|
||||
if (@$_POST['query']) {
|
||||
@ -34,10 +37,17 @@ if (@$_POST['query']) {
|
||||
$aBalance = $transaction->getBalance($aUser['id']);
|
||||
$aUser['balance'] = $aBalance['confirmed'];
|
||||
$aUser['hashrate'] = $statistics->getUserHashrate($aUser['id']);
|
||||
$aUser['payout']['est_block'] = round(( (int)$aUser['shares'] / (int)$aRoundShares['valid'] ) * (int)$config['reward'], 3);
|
||||
$aUser['payout']['est_fee'] = round(($config['fees'] / 100) * $aUser['payout']['est_block'], 3);
|
||||
$aUser['payout']['est_donation'] = round((( $aUser['donate_percent'] / 100) * ($aUser['payout']['est_block'] - $aUser['payout']['est_fee'])), 3);
|
||||
$aUser['payout']['est_payout'] = round($aUser['payout']['est_block'] - $aUser['payout']['est_donation'] - $aUser['payout']['est_fee'], 3);
|
||||
if ($aUser['shares'] > 0) {
|
||||
$aUser['payout']['est_block'] = round(( (int)$aUser['shares'] / (int)$aRoundShares['valid'] ) * (int)$config['reward'], 3);
|
||||
$aUser['payout']['est_fee'] = round(($config['fees'] / 100) * $aUser['payout']['est_block'], 3);
|
||||
$aUser['payout']['est_donation'] = round((( $aUser['donate_percent'] / 100) * ($aUser['payout']['est_block'] - $aUser['payout']['est_fee'])), 3);
|
||||
$aUser['payout']['est_payout'] = round($aUser['payout']['est_block'] - $aUser['payout']['est_donation'] - $aUser['payout']['est_fee'], 3);
|
||||
} else {
|
||||
$aUser['payout']['est_block'] = 0;
|
||||
$aUser['payout']['est_fee'] = 0;
|
||||
$aUser['payout']['est_donation'] = 0;
|
||||
$aUser['payout']['est_payout'] = 0;
|
||||
}
|
||||
$aUsers[$iKey] = $aUser;
|
||||
}
|
||||
// Assign our variables
|
||||
|
||||
@ -9,16 +9,44 @@ if (!$user->isAuthenticated() || !$user->isAdmin($_SESSION['USERDATA']['id'])) {
|
||||
die("404 Page not found");
|
||||
}
|
||||
|
||||
if ($bitcoin->can_connect() === true){
|
||||
$dBalance = $bitcoin->query('getbalance');
|
||||
if (!$smarty->isCached('master.tpl', $smarty_cache_key)) {
|
||||
$debug->append('No cached version available, fetching from backend', 3);
|
||||
if ($bitcoin->can_connect() === true){
|
||||
$dBalance = $bitcoin->query('getbalance');
|
||||
$dGetInfo = $bitcoin->query('getinfo');
|
||||
if (is_array($dGetInfo) && array_key_exists('newmint', $dGetInfo)) {
|
||||
$dNewmint = $dGetInfo['newmint'];
|
||||
} else {
|
||||
$dNewmint = -1;
|
||||
}
|
||||
} else {
|
||||
$dBalance = 0;
|
||||
$dNewmint = -1;
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to wallet RPC service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg');
|
||||
}
|
||||
// Fetch unconfirmed amount from blocks table
|
||||
empty($config['network_confirmations']) ? $confirmations = 120 : $confirmations = $config['network_confirmations'];
|
||||
$aBlocksUnconfirmed = $block->getAllUnconfirmed($confirmations);
|
||||
$dBlocksUnconfirmedBalance = 0;
|
||||
if (!empty($aBlocksUnconfirmed))
|
||||
foreach ($aBlocksUnconfirmed as $aData) $dBlocksUnconfirmedBalance += $aData['amount'];
|
||||
|
||||
// Fetch locked balance from transactions
|
||||
$dLockedBalance = $transaction->getLockedBalance();
|
||||
|
||||
// Cold wallet balance
|
||||
if (! $dColdCoins = $setting->getValue('wallet_cold_coins')) $dColdCoins = 0;
|
||||
} else {
|
||||
$dBalance = 0;
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to litecoind RPC service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg');
|
||||
$debug->append('Using cached page', 3);
|
||||
}
|
||||
|
||||
$smarty->assign("UNCONFIRMED", $dBlocksUnconfirmedBalance);
|
||||
$smarty->assign("BALANCE", $dBalance);
|
||||
$smarty->assign("LOCKED", $transaction->getLockedBalance());
|
||||
$smarty->assign("COLDCOINS", $dColdCoins);
|
||||
$smarty->assign("LOCKED", $dLockedBalance);
|
||||
$smarty->assign("NEWMINT", $dNewmint);
|
||||
|
||||
// Tempalte specifics
|
||||
$smarty->assign("CONTENT", "default.tpl");
|
||||
|
||||
?>
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
// Check if the API is activated
|
||||
$api->isActive();
|
||||
|
||||
// Check for valid API key
|
||||
$id = $user->checkApiKey($_REQUEST['api_key']);
|
||||
|
||||
@ -1,23 +1,22 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
// Check if the API is activated
|
||||
$api->isActive();
|
||||
|
||||
// Check user token
|
||||
$id = $user->checkApiKey($_REQUEST['api_key']);
|
||||
$user_id = $api->checkAccess($user->checkApiKey($_REQUEST['api_key']), @$_REQUEST['id']);
|
||||
|
||||
if ($bitcoin->can_connect() === true){
|
||||
if (!$iBlock = $memcache->get('iBlock')) {
|
||||
$iBlock = $bitcoin->query('getblockcount');
|
||||
$memcache->set('iBlock', $iBlock);
|
||||
}
|
||||
$iBlock = $bitcoin->getblockcount();
|
||||
} else {
|
||||
$iBlock = 0;
|
||||
}
|
||||
|
||||
// Output JSON format
|
||||
echo json_encode(array('getblockcount' => $iBlock));
|
||||
echo $api->get_json($iBlock);
|
||||
|
||||
// Supress master template
|
||||
$supress_master = 1;
|
||||
|
||||
@ -1,19 +1,19 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
// Check if the API is activated
|
||||
$api->isActive();
|
||||
|
||||
// Check user token
|
||||
$id = $user->checkApiKey($_REQUEST['api_key']);
|
||||
$user_id = $api->checkAccess($user->checkApiKey($_REQUEST['api_key']), @$_REQUEST['id']);
|
||||
|
||||
// Set a sane limit, overwrite with URL parameter
|
||||
$iLimit = 10;
|
||||
if (@$_REQUEST['limit'])
|
||||
$iLimit = $_REQUEST['limit'];
|
||||
// Check how many blocks to fetch
|
||||
$setting->getValue('statistics_block_count') ? $iLimit = $setting->getValue('statistics_block_count') : $iLimit = 20;
|
||||
|
||||
// Output JSON format
|
||||
echo json_encode(array('getblocksfound' => $statistics->getBlocksFound($iLimit)));
|
||||
echo $api->get_json($statistics->getBlocksFound($iLimit));
|
||||
|
||||
// Supress master template
|
||||
$supress_master = 1;
|
||||
|
||||
@ -1,14 +1,16 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
// Check if the API is activated
|
||||
$api->isActive();
|
||||
|
||||
// Check user token
|
||||
$id = $user->checkApiKey($_REQUEST['api_key']);
|
||||
$user_id = $api->checkAccess($user->checkApiKey($_REQUEST['api_key']), @$_REQUEST['id']);
|
||||
|
||||
// Output JSON format
|
||||
echo json_encode(array('getcurrentworkers' => $worker->getCountAllActiveWorkers()));
|
||||
echo $api->get_json($worker->getCountAllActiveWorkers());
|
||||
|
||||
// Supress master template
|
||||
$supress_master = 1;
|
||||
|
||||
57
public/include/pages/api/getdashboarddata.inc.php
Normal file
57
public/include/pages/api/getdashboarddata.inc.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
// Check if the API is activated
|
||||
$api->isActive();
|
||||
|
||||
// Check user token and access level permissions
|
||||
$user_id = $api->checkAccess($user->checkApiKey($_REQUEST['api_key']), @$_REQUEST['id']);
|
||||
|
||||
// Fetch RPC information
|
||||
if ($bitcoin->can_connect() === true) {
|
||||
$dNetworkHashrate = $bitcoin->getnetworkhashps();
|
||||
$dDifficulty = $bitcoin->getdifficulty();
|
||||
$iBlock = $bitcoin->getblockcount();
|
||||
} else {
|
||||
$dNetworkHashrate = 0;
|
||||
$dDifficulty = 1;
|
||||
$iBlock = 0;
|
||||
}
|
||||
|
||||
// Some settings
|
||||
if ( ! $interval = $setting->getValue('statistics_ajax_data_interval')) $interval = 300;
|
||||
if ( ! $dPoolHashrateModifier = $setting->getValue('statistics_pool_hashrate_modifier') ) $dPoolHashrateModifier = 1;
|
||||
if ( ! $dPersonalHashrateModifier = $setting->getValue('statistics_personal_hashrate_modifier') ) $dPersonalHashrateModifier = 1;
|
||||
if ( ! $dNetworkHashrateModifier = $setting->getValue('statistics_network_hashrate_modifier') ) $dNetworkHashrateModifier = 1;
|
||||
|
||||
// Fetch raw data
|
||||
$statistics->setGetCache(false);
|
||||
$dPoolHashrate = $statistics->getCurrentHashrate($interval);
|
||||
if ($dPoolHashrate > $dNetworkHashrate) $dNetworkHashrate = $dPoolHashrate;
|
||||
$dPersonalHashrate = $statistics->getUserHashrate($user_id, $interval);
|
||||
$dPersonalSharerate = $statistics->getUserSharerate($user_id, $interval);
|
||||
$statistics->setGetCache(true);
|
||||
|
||||
// Use caches for this one
|
||||
$aUserRoundShares = $statistics->getUserShares($user_id);
|
||||
$aRoundShares = $statistics->getRoundShares();
|
||||
|
||||
// Apply pool modifiers
|
||||
$dPersonalHashrateAdjusted = $dPersonalHashrate * $dPersonalHashrateModifier;
|
||||
$dPoolHashrateAdjusted = $dPoolHashrate * $dPoolHashrateModifier;
|
||||
$dNetworkHashrateAdjusted = $dNetworkHashrate / 1000 * $dNetworkHashrateModifier;
|
||||
|
||||
// Output JSON format
|
||||
$data = array(
|
||||
'raw' => array( 'personal' => array( 'hashrate' => $dPersonalHashrate ), 'pool' => array( 'hashrate' => $dPoolHashrate ), 'network' => array( 'hashrate' => $dNetworkHashrate / 1000 ) ),
|
||||
'personal' => array ( 'hashrate' => $dPersonalHashrateAdjusted, 'sharerate' => $dPersonalSharerate, 'shares' => $aUserRoundShares, 'balance' => $transaction->getBalance($user_id)),
|
||||
'pool' => array( 'hashrate' => $dPoolHashrateAdjusted, 'shares' => $aRoundShares ),
|
||||
'network' => array( 'hashrate' => $dNetworkHashrateAdjusted, 'difficulty' => $dDifficulty, 'block' => $iBlock ),
|
||||
);
|
||||
echo $api->get_json($data);
|
||||
|
||||
// Supress master template
|
||||
$supress_master = 1;
|
||||
?>
|
||||
@ -1,24 +1,19 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
// Check if the API is activated
|
||||
$api->isActive();
|
||||
|
||||
// Check user token
|
||||
$id = $user->checkApiKey($_REQUEST['api_key']);
|
||||
$user_id = $api->checkAccess($user->checkApiKey($_REQUEST['api_key']), @$_REQUEST['id']);
|
||||
|
||||
// Fetch data from litecoind
|
||||
if ($bitcoin->can_connect() === true){
|
||||
if (!$dDifficulty = $memcache->get('dDifficulty')) {
|
||||
$memcache->set('dDifficulty', $dDifficulty);
|
||||
$dDifficulty = $bitcoin->query('getdifficulty');
|
||||
}
|
||||
} else {
|
||||
$iDifficulty = 1;
|
||||
}
|
||||
// Fetch data from wallet
|
||||
$bitcoin->can_connect() === true ? $dDifficulty = $bitcoin->getdifficulty() : $iDifficulty = 1;
|
||||
|
||||
// Output JSON format
|
||||
echo json_encode(array('getdifficulty' => $dDifficulty));
|
||||
echo $api->get_json($dDifficulty);
|
||||
|
||||
// Supress master template
|
||||
$supress_master = 1;
|
||||
|
||||
@ -1,17 +1,20 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
// Check if the API is activated
|
||||
$api->isActive();
|
||||
|
||||
// Check user token
|
||||
$id = $user->checkApiKey($_REQUEST['api_key']);
|
||||
$user_id = $api->checkAccess($user->checkApiKey($_REQUEST['api_key']), @$_REQUEST['id']);
|
||||
|
||||
// Estimated time to find the next block
|
||||
$iCurrentPoolHashrate = $statistics->getCurrentHashrate() * 1000;
|
||||
$bitcoin->can_connect() === true ? $dEstimatedTime = $bitcoin->getestimatedtime($iCurrentPoolHashrate) : $dEstimatedTime = 0;
|
||||
|
||||
// Output JSON format
|
||||
echo json_encode(array('getestimatedtime' => $bitcoin->getestimatedtime($iCurrentPoolHashrate)));
|
||||
echo $api->get_json($dEstimatedTime);
|
||||
|
||||
// Supress master template
|
||||
$supress_master = 1;
|
||||
|
||||
22
public/include/pages/api/gethourlyhashrates.inc.php
Normal file
22
public/include/pages/api/gethourlyhashrates.inc.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
// Check if the API is activated
|
||||
$api->isActive();
|
||||
|
||||
// Check user token
|
||||
$user_id = $api->checkAccess($user->checkApiKey($_REQUEST['api_key']), @$_REQUEST['id']);
|
||||
|
||||
// Output JSON format
|
||||
$data = array(
|
||||
'mine' => $statistics->getHourlyHashrateByAccount($id),
|
||||
'pool' => $statistics->getHourlyHashrateByPool()
|
||||
);
|
||||
|
||||
echo $api->json($data);
|
||||
|
||||
// Supress master template
|
||||
$supress_master = 1;
|
||||
?>
|
||||
@ -1,14 +1,23 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
// Check if the API is activated
|
||||
$api->isActive();
|
||||
|
||||
// Check user token
|
||||
$id = $user->checkApiKey($_REQUEST['api_key']);
|
||||
$user_id = $api->checkAccess($user->checkApiKey($_REQUEST['api_key']), @$_REQUEST['id']);
|
||||
|
||||
// Fetch settings
|
||||
if ( ! $interval = $setting->getValue('statistics_ajax_data_interval')) $interval = 300;
|
||||
|
||||
// Output JSON format
|
||||
echo json_encode(array('getpoolhashrate' => $statistics->getCurrentHashrate()));
|
||||
$statistics->setGetCache(false);
|
||||
$dPoolHashrate = $statistics->getCurrentHashrate($interval);
|
||||
$statistics->setGetCache(true);
|
||||
|
||||
echo $api->get_json($dPoolHashrate);
|
||||
|
||||
// Supress master template
|
||||
$supress_master = 1;
|
||||
|
||||
@ -1,11 +1,16 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
// Check if the API is activated
|
||||
$api->isActive();
|
||||
|
||||
// Check user token
|
||||
$id = $user->checkApiKey($_REQUEST['api_key']);
|
||||
$user_id = $api->checkAccess($user->checkApiKey($_REQUEST['api_key']), @$_REQUEST['id']);
|
||||
|
||||
// Fetch settings
|
||||
if ( ! $interval = $setting->getValue('statistics_ajax_data_interval')) $interval = 300;
|
||||
|
||||
// Output JSON format
|
||||
echo json_encode(array('getpoolsharerate' => $statistics->getCurrentShareRate()));
|
||||
|
||||
67
public/include/pages/api/getpoolstatus.inc.php
Normal file
67
public/include/pages/api/getpoolstatus.inc.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
// Check if the API is activated
|
||||
$api->isActive();
|
||||
|
||||
// Check user token
|
||||
$user_id = $api->checkAccess($user->checkApiKey($_REQUEST['api_key']), @$_REQUEST['id']);
|
||||
|
||||
// Fetch last block information
|
||||
$aLastBlock = $block->getLast();
|
||||
|
||||
// Efficiency
|
||||
$aShares = $statistics->getRoundShares();
|
||||
$aShares['valid'] > 0 ? $dEfficiency = round((100 - (100 / $aShares['valid'] * $aShares['invalid'])), 2) : $dEfficiency = 0;
|
||||
|
||||
// Fetch RPC data
|
||||
if ($bitcoin->can_connect() === true){
|
||||
$dDifficulty = $bitcoin->getdifficulty();
|
||||
$iBlock = $bitcoin->getblockcount();
|
||||
$dNetworkHashrate = $bitcoin->getnetworkhashps();
|
||||
} else {
|
||||
$dDifficulty = 1;
|
||||
$iBlock = 0;
|
||||
$dNetworkHashrate = 0;
|
||||
}
|
||||
|
||||
// Estimated time to find the next block
|
||||
$iCurrentPoolHashrate = $statistics->getCurrentHashrate();
|
||||
|
||||
// Avoid confusion, ensure our nethash isn't higher than poolhash
|
||||
if ($iCurrentPoolHashrate > $dNetworkHashrate) $dNetworkHashrate = $iCurrentPoolHashrate;
|
||||
|
||||
// Time in seconds, not hours, using modifier in smarty to translate
|
||||
$iCurrentPoolHashrate > 0 ? $iEstTime = $dDifficulty * pow(2,32) / ($iCurrentPoolHashrate * 1000) : $iEstTime = 0;
|
||||
$iEstShares = (pow(2, 32 - $config['difficulty']) * $dDifficulty);
|
||||
|
||||
// Time since last
|
||||
$now = new DateTime( "now" );
|
||||
if (!empty($aLastBlock)) {
|
||||
$dTimeSinceLast = ($now->getTimestamp() - $aLastBlock['time']);
|
||||
} else {
|
||||
$dTimeSinceLast = 0;
|
||||
}
|
||||
|
||||
// Output JSON format
|
||||
$data = array(
|
||||
'hashrate' => $iCurrentPoolHashrate,
|
||||
'efficiency' => $dEfficiency,
|
||||
'workers' => $worker->getCountAllActiveWorkers(),
|
||||
'currentnetworkblock' => $iBlock,
|
||||
'nextnetworkblock' => $iBlock + 1,
|
||||
'lastblock' => $aLastBlock['height'],
|
||||
'networkdiff' => $dDifficulty,
|
||||
'esttime' => $iEstTime,
|
||||
'estshares' => $iEstShares,
|
||||
'timesincelast' => $dTimeSinceLast,
|
||||
'nethashrate' => $dNetworkHashrate
|
||||
);
|
||||
|
||||
echo $api->get_json($data);
|
||||
|
||||
// Supress master template
|
||||
$supress_master = 1;
|
||||
?>
|
||||
@ -1,25 +1,23 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
// Check if the API is activated
|
||||
$api->isActive();
|
||||
|
||||
// Check user token
|
||||
$id = $user->checkApiKey($_REQUEST['api_key']);
|
||||
$user_id = $api->checkAccess($user->checkApiKey($_REQUEST['api_key']), @$_REQUEST['id']);
|
||||
|
||||
// Fetch our last block found
|
||||
$aBlocksFoundData = $statistics->getBlocksFound(1);
|
||||
|
||||
// Time since last block
|
||||
$now = new DateTime( "now" );
|
||||
if (!empty($aBlocksFoundData)) {
|
||||
$dTimeSinceLast = ($now->getTimestamp() - $aBlocksFoundData[0]['time']);
|
||||
} else {
|
||||
$dTimeSinceLast = 0;
|
||||
}
|
||||
! empty($aBlocksFoundData) ? $dTimeSinceLast = ($now->getTimestamp() - $aBlocksFoundData[0]['time']) : $dTimeSinceLast = 0;
|
||||
|
||||
// Output JSON format
|
||||
echo json_encode(array('gettimesincelastblock' => $dTimeSinceLast));
|
||||
echo $api->get_json($dTimeSinceLast);
|
||||
|
||||
// Supress master template
|
||||
$supress_master = 1;
|
||||
|
||||
17
public/include/pages/api/getuserbalance.inc.php
Normal file
17
public/include/pages/api/getuserbalance.inc.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
// Check if the API is activated
|
||||
$api->isActive();
|
||||
|
||||
// Check user token
|
||||
$user_id = $api->checkAccess($user->checkApiKey($_REQUEST['api_key']), @$_REQUEST['id']);
|
||||
|
||||
// Output JSON format
|
||||
echo $api->get_json($transaction->getBalance($user_id));
|
||||
|
||||
// Supress master template
|
||||
$supress_master = 1;
|
||||
?>
|
||||
25
public/include/pages/api/getuserhashrate.inc.php
Normal file
25
public/include/pages/api/getuserhashrate.inc.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
// Check if the API is activated
|
||||
$api->isActive();
|
||||
|
||||
// Check user token
|
||||
$user_id = $api->checkAccess($user->checkApiKey($_REQUEST['api_key']), @$_REQUEST['id']);
|
||||
|
||||
// Fetch some settings
|
||||
if ( ! $interval = $setting->getValue('statistics_ajax_data_interval')) $interval = 300;
|
||||
|
||||
// Gather un-cached data
|
||||
$statistics->setGetCache(false);
|
||||
$hashrate = $statistics->getUserHashrate($user_id, $interval);
|
||||
$statistics->setGetCache(true);
|
||||
|
||||
// Output JSON
|
||||
echo $api->get_json($hashrate);
|
||||
|
||||
// Supress master template
|
||||
$supress_master = 1;
|
||||
?>
|
||||
25
public/include/pages/api/getusersharerate.inc.php
Normal file
25
public/include/pages/api/getusersharerate.inc.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
// Check if the API is activated
|
||||
$api->isActive();
|
||||
|
||||
// Check user token
|
||||
$user_id = $api->checkAccess($user->checkApiKey($_REQUEST['api_key']), @$_REQUEST['id']);
|
||||
|
||||
// Fetch settings
|
||||
if ( ! $interval = $setting->getValue('statistics_ajax_data_interval')) $interval = 300;
|
||||
|
||||
// Gather un-cached data
|
||||
$statistics->setGetCache(false);
|
||||
$sharerate = $statistics->getUserSharerate($user_id, $interval);
|
||||
$statistics->setGetCache(true);
|
||||
|
||||
// Output JSON format
|
||||
echo $api->get_json($sharerate);
|
||||
|
||||
// Supress master template
|
||||
$supress_master = 1;
|
||||
?>
|
||||
@ -1,28 +1,22 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
// Check if the API is activated
|
||||
$api->isActive();
|
||||
|
||||
// Check user token
|
||||
$id = $user->checkApiKey($_REQUEST['api_key']);
|
||||
|
||||
// We have to check if that user is admin too
|
||||
if ( ! $user->isAdmin($id) ) {
|
||||
header("HTTP/1.1 401 Unauthorized");
|
||||
die("Access denied");
|
||||
}
|
||||
|
||||
// Is it a username or a user ID
|
||||
ctype_digit($_REQUEST['id']) ? $username = $user->getUserName($_REQUEST['id']) : $username = $_REQUEST['id'];
|
||||
ctype_digit($_REQUEST['id']) ? $id = $_REQUEST['id'] : $id = $user->getUserId($_REQUEST['id']);
|
||||
$user_id = $api->checkAccess($user->checkApiKey($_REQUEST['api_key']), @$_REQUEST['id']);
|
||||
|
||||
// Output JSON format
|
||||
echo json_encode(array('getuserstatus' => array(
|
||||
'username' => $username,
|
||||
'shares' => $statistics->getUserShares($id),
|
||||
'hashrate' => $statistics->getUserHashrate($id)
|
||||
)));
|
||||
$data = array(
|
||||
'username' => $user->getUsername($user_id),
|
||||
'shares' => $statistics->getUserShares($user_id),
|
||||
'hashrate' => $statistics->getUserHashrate($user_id),
|
||||
'sharerate' => $statistics->getUserSharerate($user_id)
|
||||
);
|
||||
echo $api->get_json($data);
|
||||
|
||||
// Supress master template
|
||||
$supress_master = 1;
|
||||
|
||||
@ -1,23 +1,16 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
// Check if the API is activated
|
||||
$api->isActive();
|
||||
|
||||
// Check user token
|
||||
$id = $user->checkApiKey($_REQUEST['api_key']);
|
||||
|
||||
// We have to check if that user is admin too
|
||||
if ( ! $user->isAdmin($id) ) {
|
||||
header("HTTP/1.1 401 Unauthorized");
|
||||
die("Access denied");
|
||||
}
|
||||
|
||||
// Is it a username or a user ID
|
||||
ctype_digit($_REQUEST['id']) ? $id = $_REQUEST['id'] : $id = $user->getUserId($_REQUEST['id']);
|
||||
$user_id = $api->checkAccess($user->checkApiKey($_REQUEST['api_key']), @$_REQUEST['id']);
|
||||
|
||||
// Output JSON format
|
||||
echo json_encode(array('getuserworkers' => $worker->getWorkers($id)));
|
||||
echo $api->get_json($worker->getWorkers($user_id));
|
||||
|
||||
// Supress master template
|
||||
$supress_master = 1;
|
||||
|
||||
@ -1,23 +1,27 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
// {"pool_name":"Pool-X.eu","hashrate":"511128.99","workers":"2104","shares_this_round":92450,"last_block":"365294","network_hashrate":17327056.06}
|
||||
// Check if the API is activated
|
||||
$api->isActive();
|
||||
|
||||
// Fetch last block information
|
||||
$aLastBlock = $block->getLast();
|
||||
$aShares = $statistics->getRoundShares();
|
||||
|
||||
// RPC Calls
|
||||
$bitcoin->can_connect() === true ? $dNetworkHashrate = $bitcoin->getnetworkhashps() : $dNetworkHashrate = 0;
|
||||
|
||||
// Backwards compatible with the existing services
|
||||
echo json_encode(
|
||||
array(
|
||||
'pool_name' => $config['website']['name'],
|
||||
'pool_name' => $setting->getValue('website_name'),
|
||||
'hashrate' => $statistics->getCurrentHashrate(),
|
||||
'workers' => $worker->getCountAllActiveWorkers(),
|
||||
'shares_this_round' => $aShares['valid'],
|
||||
'last_block' => $aLastBlock['height'],
|
||||
'network_hashrate' => '0'
|
||||
'network_hashrate' => $dNetworkHashrate
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
18
public/include/pages/contactform.inc.php
Normal file
18
public/include/pages/contactform.inc.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
if ($setting->getValue('disable_contactform')) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Contactform is currently disabled. Please try again later.', 'TYPE' => 'errormsg');
|
||||
$smarty->assign("CONTENT", "disabled.tpl");
|
||||
} else {
|
||||
if ($setting->getValue('recaptcha_enabled')) {
|
||||
require_once(INCLUDE_DIR . '/lib/recaptchalib.php');
|
||||
$smarty->assign("RECAPTCHA", recaptcha_get_html($setting->getValue('recaptcha_public_key')));
|
||||
}
|
||||
|
||||
// Tempalte specifics
|
||||
$smarty->assign("CONTENT", "default.tpl");
|
||||
}
|
||||
?>
|
||||
51
public/include/pages/contactform/contactform.inc.php
Normal file
51
public/include/pages/contactform/contactform.inc.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
|
||||
if ($setting->getValue('recaptcha_enabled')) {
|
||||
// Load re-captcha specific data
|
||||
require_once(INCLUDE_DIR . '/lib/recaptchalib.php');
|
||||
$rsp = recaptcha_check_answer (
|
||||
$setting->getValue('recaptcha_private_key'),
|
||||
$_SERVER["REMOTE_ADDR"],
|
||||
$_POST["recaptcha_challenge_field"],
|
||||
$_POST["recaptcha_response_field"]
|
||||
);
|
||||
}
|
||||
|
||||
if ($setting->getValue('disable_contactform')) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Contactform is currently disabled. Please try again later.', 'TYPE' => 'errormsg');
|
||||
} else {
|
||||
// Check if recaptcha is enabled, process form data if valid
|
||||
if($setting->getValue('recaptcha_enabled') && $_POST["recaptcha_response_field"] && $_POST["recaptcha_response_field"]!=''){
|
||||
if ($rsp->is_valid) {
|
||||
$smarty->assign("RECAPTCHA", recaptcha_get_html($setting->getValue('recaptcha_public_key')));
|
||||
if ($mail->contactform($_POST['senderName'], $_POST['senderEmail'], $_POST['senderSubject'], $_POST['senderMessage'])) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Thanks for sending your message! We will get back to you shortly');
|
||||
} else {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'There was a problem sending your message. Please try again.' . $user->getError(), 'TYPE' => 'errormsg');
|
||||
}
|
||||
} else {
|
||||
$smarty->assign("RECAPTCHA", recaptcha_get_html($setting->getValue('recaptcha_public_key'), $rsp->error));
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Invalid Captcha, please try again. (' . $rsp->error . ')', 'TYPE' => 'errormsg');
|
||||
}
|
||||
// Empty captcha
|
||||
} else if ($setting->getValue('recaptcha_enabled')) {
|
||||
$smarty->assign("RECAPTCHA", recaptcha_get_html($setting->getValue('recaptcha_public_key'), $rsp->error));
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Empty Captcha, please try again.', 'TYPE' => 'errormsg');
|
||||
// Captcha disabled
|
||||
} else {
|
||||
if ($mail->contactform($_POST['senderName'], $_POST['senderEmail'], $_POST['senderSubject'], $_POST['senderMessage'])) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Thanks for sending your message! We will get back to you shortly');
|
||||
} else {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'There was a problem sending your message. Please try again. ' . $user->getError(), 'TYPE' => 'errormsg');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tempalte specifics
|
||||
$smarty->assign("CONTENT", "default.tpl");
|
||||
|
||||
?>
|
||||
50
public/include/pages/dashboard.inc.php
Normal file
50
public/include/pages/dashboard.inc.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
if ($user->isAuthenticated()) {
|
||||
if (! $interval = $setting->getValue('statistics_ajax_data_interval')) $interval = 300;
|
||||
// Defaults to get rid of PHP Notice warnings
|
||||
$dDifficulty = 1;
|
||||
$aRoundShares = 1;
|
||||
|
||||
// Only run these if the user is logged in
|
||||
$aRoundShares = $statistics->getRoundShares();
|
||||
if ($bitcoin->can_connect() === true) {
|
||||
$dDifficulty = $bitcoin->query('getdifficulty');
|
||||
if (is_array($dDifficulty) && array_key_exists('proof-of-work', $dDifficulty))
|
||||
$dDifficulty = $dDifficulty['proof-of-work'];
|
||||
}
|
||||
|
||||
// Always fetch this since we need for ministats header
|
||||
$aRoundShares = $statistics->getRoundShares();
|
||||
if ($bitcoin->can_connect() === true) {
|
||||
$dDifficulty = $bitcoin->query('getdifficulty');
|
||||
if (is_array($dDifficulty) && array_key_exists('proof-of-work', $dDifficulty))
|
||||
$dDifficulty = $dDifficulty['proof-of-work'];
|
||||
try { $dNetworkHashrate = $bitcoin->query('getnetworkhashps') / 1000; } catch (Exception $e) {
|
||||
// Maybe we are SHA
|
||||
try { $dNetworkHashrate = $bitcoin->query('gethashespersec') / 1000; } catch (Exception $e) {
|
||||
$dNetworkHashrate = 0;
|
||||
}
|
||||
$dNetworkHashrate = 0;
|
||||
}
|
||||
} else {
|
||||
$dNetworkHashrate = 0;
|
||||
}
|
||||
|
||||
// Fetch some data
|
||||
if (!$iCurrentActiveWorkers = $worker->getCountAllActiveWorkers()) $iCurrentActiveWorkers = 0;
|
||||
$iCurrentPoolHashrate = $statistics->getCurrentHashrate();
|
||||
$iCurrentPoolShareRate = $statistics->getCurrentShareRate();
|
||||
|
||||
// Avoid confusion, ensure our nethash isn't higher than poolhash
|
||||
if ($iCurrentPoolHashrate > $dNetworkHashrate) $dNetworkHashrate = $iCurrentPoolHashrate;
|
||||
|
||||
// Make it available in Smarty
|
||||
$smarty->assign('INTERVAL', $interval / 60);
|
||||
$smarty->assign('CONTENT', 'default.tpl');
|
||||
}
|
||||
|
||||
?>
|
||||
@ -1,9 +1,26 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
// Tempalte specifics
|
||||
// Include markdown library
|
||||
use \Michelf\Markdown;
|
||||
|
||||
if (!$smarty->isCached('master.tpl', $smarty_cache_key)) {
|
||||
$debug->append('No cached version available, fetching from backend', 3);
|
||||
// Fetch active news to display
|
||||
$aNews = $news->getAllActive();
|
||||
if (is_array($aNews)) {
|
||||
foreach ($aNews as $key => $aData) {
|
||||
// Transform Markdown content to HTML
|
||||
$aNews[$key]['content'] = Markdown::defaultTransform($aData['content']);
|
||||
}
|
||||
}
|
||||
$smarty->assign("NEWS", $aNews);
|
||||
} else {
|
||||
$debug->append('Using cached page', 3);
|
||||
}
|
||||
|
||||
// Load news entries for Desktop site and unauthenticated users
|
||||
$smarty->assign("CONTENT", "default.tpl");
|
||||
?>
|
||||
|
||||
@ -1,13 +1,19 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
if ( $user->checkLogin($_POST['username'],$_POST['password']) ) {
|
||||
header('Location: index.php?page=home');
|
||||
} else {
|
||||
if ($setting->getValue('maintenance') && !$user->isAdmin($user->getUserId($_POST['username']))) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'You are not allowed to login during maintenace.', 'TYPE' => 'info');
|
||||
} else if ($user->checkLogin(@$_POST['username'], @$_POST['password']) ) {
|
||||
empty($_POST['to']) ? $to = $_SERVER['PHP_SELF'] : $to = $_POST['to'];
|
||||
$location = @$_SERVER['HTTPS'] === true ? 'https' : 'http' . '://' . $_SERVER['SERVER_NAME'] . $to;
|
||||
if (!headers_sent()) header('Location: ' . $location);
|
||||
exit('<meta http-equiv="refresh" content="0; url=' . $location . '"/>');
|
||||
} else if (@$_POST['username'] && @$_POST['password']) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to login: '. $user->getError(), 'TYPE' => 'errormsg');
|
||||
}
|
||||
|
||||
// Load login template
|
||||
$smarty->assign('CONTENT', 'default.tpl');
|
||||
?>
|
||||
|
||||
@ -6,5 +6,6 @@ if (!defined('SECURITY'))
|
||||
|
||||
// This probably (?) never fails
|
||||
$user->logoutUser();
|
||||
header('Location: index.php?page=home');
|
||||
$smarty->assign("CONTENT", "default.tpl");
|
||||
// header('Location: index.php?page=home');
|
||||
?>
|
||||
|
||||
@ -1,9 +1,21 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
// Include markdown library
|
||||
use \Michelf\Markdown;
|
||||
|
||||
// Fetch active news to display
|
||||
$aNews = $news->getAllActive();
|
||||
if (is_array($aNews)) {
|
||||
foreach ($aNews as $key => $aData) {
|
||||
// Transform Markdown content to HTML
|
||||
$aNews[$key]['content'] = Markdown::defaultTransform($aData['content']);
|
||||
}
|
||||
}
|
||||
|
||||
// Tempalte specifics
|
||||
$smarty->assign("NEWS", $aNews);
|
||||
$smarty->assign("CONTENT", "default.tpl");
|
||||
?>
|
||||
|
||||
@ -4,14 +4,14 @@
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
|
||||
if ($_POST['do'] == 'useToken') {
|
||||
if ($user->useToken($_POST['token'], $_POST['newPassword'], $_POST['newPassword2'])) {
|
||||
if (isset($_POST['do']) && $_POST['do'] == 'resetPassword') {
|
||||
if ($user->resetPassword($_POST['token'], $_POST['newPassword'], $_POST['newPassword2'])) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Password reset complete! Please login.');
|
||||
} else {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => $user->getError(), 'TYPE' => 'errormsg');
|
||||
}
|
||||
}
|
||||
|
||||
// Tempalte specifics
|
||||
$smarty->assign("CONTENT", "default.tpl");
|
||||
|
||||
?>
|
||||
|
||||
@ -5,7 +5,7 @@ if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
|
||||
// Process password reset request
|
||||
if ($user->resetPassword($_POST['username'], $smarty)) {
|
||||
if ($user->initResetPassword($_POST['username'], $smarty)) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Please check your mail account to finish your password reset');
|
||||
} else {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => $user->getError(), 'TYPE' => 'errormsg');
|
||||
|
||||
@ -3,15 +3,17 @@
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
if (!$config['website']['registration']) {
|
||||
if ($setting->getValue('lock_registration') && $setting->getValue('disable_invitations')) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Account registration is currently disabled. Please try again later.', 'TYPE' => 'errormsg');
|
||||
$smarty->assign("CONTENT", "disabled.tpl");
|
||||
} else if ($setting->getValue('lock_registration') && !$setting->getValue('disable_invitations') && !isset($_GET['token'])) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Only invited users are allowed to register.', 'TYPE' => 'errormsg');
|
||||
$smarty->assign("CONTENT", "disabled.tpl");
|
||||
} else {
|
||||
if ($config['recaptcha']['enabled']) {
|
||||
require_once(INCLUDE_DIR . '/recaptchalib.php');
|
||||
$smarty->assign("RECAPTCHA", recaptcha_get_html($config['recaptcha']['public_key']));
|
||||
if ($setting->getValue('recaptcha_enabled')) {
|
||||
require_once(INCLUDE_DIR . '/lib/recaptchalib.php');
|
||||
$smarty->assign("RECAPTCHA", recaptcha_get_html($setting->getValue('recaptcha_public_key')));
|
||||
}
|
||||
// Tempalte specifics
|
||||
$smarty->assign("CONTENT", "default.tpl");
|
||||
}
|
||||
?>
|
||||
|
||||
@ -2,44 +2,48 @@
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
if ($config['recaptcha']['enabled']) {
|
||||
if ($setting->getValue('recaptcha_enabled')) {
|
||||
// Load re-captcha specific data
|
||||
require_once(INCLUDE_DIR . '/recaptchalib.php');
|
||||
require_once(INCLUDE_DIR . '/lib/recaptchalib.php');
|
||||
$rsp = recaptcha_check_answer (
|
||||
$config['recaptcha']['private_key'],
|
||||
$setting->getValue('recaptcha_private_key'),
|
||||
$_SERVER["REMOTE_ADDR"],
|
||||
$_POST["recaptcha_challenge_field"],
|
||||
$_POST["recaptcha_response_field"]
|
||||
);
|
||||
}
|
||||
|
||||
// Check if recaptcha is enabled, process form data if valid
|
||||
if($config['recaptcha']['enabled'] && $_POST["recaptcha_response_field"] && $_POST["recaptcha_response_field"]!=''){
|
||||
if ($rsp->is_valid) {
|
||||
$smarty->assign("RECAPTCHA", recaptcha_get_html($config['recaptcha']['public_key']));
|
||||
if (!$config['website']['registration']) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Account registration is currently disabled. Please try again later.', 'TYPE' => 'errormsg');
|
||||
} else if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2']) && $config['website']['registration']) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Account created, please login');
|
||||
if ($setting->getValue('disable_invitations') && $setting->getValue('lock_registration')) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Account registration is currently disabled. Please try again later.', 'TYPE' => 'errormsg');
|
||||
} else if ($setting->getValue('lock_registration') && !$setting->getValue('disable_invitations') && !isset($_POST['token'])) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Only invited users are allowed to register.', 'TYPE' => 'errormsg');
|
||||
} else {
|
||||
// Check if recaptcha is enabled, process form data if valid
|
||||
if($setting->getValue('recaptcha_enabled') && $_POST["recaptcha_response_field"] && $_POST["recaptcha_response_field"]!=''){
|
||||
if ($rsp->is_valid) {
|
||||
$smarty->assign("RECAPTCHA", recaptcha_get_html($setting->getValue('recaptcha_public_key')));
|
||||
isset($_POST['token']) ? $token = $_POST['token'] : $token = '';
|
||||
if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2'], $token)) {
|
||||
! $setting->getValue('accounts_confirm_email_disabled') ? $_SESSION['POPUP'][] = array('CONTENT' => 'Please check your mailbox to activate this account') : $_SESSION['POPUP'][] = array('CONTENT' => 'Account created, please login');
|
||||
} else {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to create account: ' . $user->getError(), 'TYPE' => 'errormsg');
|
||||
}
|
||||
} else {
|
||||
$smarty->assign("RECAPTCHA", recaptcha_get_html($setting->getValue('recaptcha_public_key'), $rsp->error));
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Invalid Captcha, please try again. (' . $rsp->error . ')', 'TYPE' => 'errormsg');
|
||||
}
|
||||
// Empty captcha
|
||||
} else if ($setting->getValue('recaptcha_enabled')) {
|
||||
$smarty->assign("RECAPTCHA", recaptcha_get_html($setting->getValue('recaptcha_public_key'), $rsp->error));
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Empty Captcha, please try again.', 'TYPE' => 'errormsg');
|
||||
// Captcha disabled
|
||||
} else {
|
||||
isset($_POST['token']) ? $token = $_POST['token'] : $token = '';
|
||||
if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2'], $token)) {
|
||||
! $setting->getValue('accounts_confirm_email_disabled') ? $_SESSION['POPUP'][] = array('CONTENT' => 'Please check your mailbox to activate this account') : $_SESSION['POPUP'][] = array('CONTENT' => 'Account created, please login');
|
||||
} else {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to create account: ' . $user->getError(), 'TYPE' => 'errormsg');
|
||||
}
|
||||
} else {
|
||||
$smarty->assign("RECAPTCHA", recaptcha_get_html($config['recaptcha']['public_key'], $rsp->error));
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Invalid Captcha, please try again. (' . $rsp->error . ')', 'TYPE' => 'errormsg');
|
||||
}
|
||||
// Empty captcha
|
||||
} else if ($config['recaptcha']['enabled']) {
|
||||
$smarty->assign("RECAPTCHA", recaptcha_get_html($config['recaptcha']['public_key'], $rsp->error));
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Empty Captcha, please try again.', 'TYPE' => 'errormsg');
|
||||
// Captcha disabled
|
||||
} else {
|
||||
if (!$config['website']['registration']) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Account registration is currently disabled. Please try again later.', 'TYPE' => 'errormsg');
|
||||
} else if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2']) && $config['website']['registration']) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Account created, please login');
|
||||
} else {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to create account: ' . $user->getError(), 'TYPE' => 'errormsg');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -4,15 +4,21 @@
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
|
||||
if ($bitcoin->can_connect() === true){
|
||||
$dDifficulty = $bitcoin->query('getdifficulty');
|
||||
$iBlock = $bitcoin->query('getblockcount');
|
||||
if (!$smarty->isCached('master.tpl', $smarty_cache_key)) {
|
||||
$debug->append('No cached version available, fetching from backend', 3);
|
||||
if ($bitcoin->can_connect() === true){
|
||||
$dDifficulty = $bitcoin->getdifficulty();
|
||||
$iBlock = $bitcoin->getblockcount();
|
||||
} else {
|
||||
$dDifficulty = 1;
|
||||
$iBlock = 0;
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to litecoind RPC service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg');
|
||||
}
|
||||
$smarty->assign("CURRENTBLOCK", $iBlock);
|
||||
$smarty->assign("DIFFICULTY", $dDifficulty);
|
||||
} else {
|
||||
$dDifficulty = 1;
|
||||
$iBlock = 0;
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to litecoind RPC service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg');
|
||||
$debug->append('Using cached page', 3);
|
||||
}
|
||||
|
||||
$smarty->assign("CURRENTBLOCK", $iBlock);
|
||||
$smarty->assign("DIFFICULTY", $dDifficulty);
|
||||
$smarty->assign("CONTENT", "default.tpl");
|
||||
?>
|
||||
|
||||
@ -2,16 +2,24 @@
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
if (!$user->isAuthenticated()) header("Location: index.php?page=home");
|
||||
|
||||
// Grab the last blocks found
|
||||
$iLimit = 30;
|
||||
$aBlocksFoundData = $statistics->getBlocksFound($iLimit);
|
||||
$aBlockData = $aBlocksFoundData[0];
|
||||
if (!$smarty->isCached('master.tpl', $smarty_cache_key)) {
|
||||
$debug->append('No cached version available, fetching from backend', 3);
|
||||
// Grab the last blocks found
|
||||
$setting->getValue('statistics_block_count') ? $iLimit = $setting->getValue('statistics_block_count') : $iLimit = 20;
|
||||
$aBlocksFoundData = $statistics->getBlocksFound($iLimit);
|
||||
|
||||
// Propagate content our template
|
||||
$smarty->assign("BLOCKSFOUND", $aBlocksFoundData);
|
||||
$smarty->assign("BLOCKLIMIT", $iLimit);
|
||||
// Propagate content our template
|
||||
$smarty->assign("BLOCKSFOUND", $aBlocksFoundData);
|
||||
$smarty->assign("BLOCKLIMIT", $iLimit);
|
||||
} else {
|
||||
$debug->append('Using cached page', 3);
|
||||
}
|
||||
|
||||
$smarty->assign("CONTENT", "default.tpl");
|
||||
if ($setting->getValue('acl_block_statistics')) {
|
||||
$smarty->assign("CONTENT", "default.tpl");
|
||||
} else if ($user->isAuthenticated()) {
|
||||
$smarty->assign("CONTENT", "default.tpl");
|
||||
}
|
||||
?>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user