Completed
Push — master ( 5d1966...635f68 )
by Michael
01:56
created

ipnppd.php (9 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 47 and the first side effect is on line 33.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/************************************************************************/
3
/* Donations - Paypal financial management module for Xoops 2           */
4
/* Copyright (c) 2016 XOOPS Project                                     */
5
/* http://dev.xoops.org/modules/xfmod/project/?group_id=1060            */
6
/*
7
/************************************************************************/
8
/*                                                                      */
9
/* Based on NukeTreasury for PHP-Nuke - by Dave Lawrence AKA Thrash     */
10
/* NukeTreasury - Financial management for PHP-Nuke                     */
11
/* Copyright (c) 2004 by Dave Lawrence AKA Thrash                       */
12
/*                       [email protected]                         */
13
/*                       [email protected]                          */
14
/*                                                                      */
15
/************************************************************************/
16
/*                                                                      */
17
/* This program is free software; you can redistribute it and/or modify */
18
/* it under the terms of the GNU General Public License as published by */
19
/* the Free Software Foundation; either version 2 of the License.       */
20
/*                                                                      */
21
/* This program is distributed in the hope that it will be useful, but  */
22
/* WITHOUT ANY WARRANTY; without even the implied warranty of           */
23
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU     */
24
/* General Public License for more details.                             */
25
/*                                                                      */
26
/* You should have received a copy of the GNU General Public License    */
27
/* along with this program; if not, write to the Free Software          */
28
/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  */
29
/* USA                                                                  */
30
/************************************************************************/
31
32
//$xoopsOption['nocommon'] = 1;
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
33
include __DIR__ . '/header.php';
34
require_once __DIR__ . '/class/utility.php';
35
require_once XOOPS_ROOT_PATH . '/class/xoopsformloader.php';
36
37
$tr_config  = XdonationsUtility::getConfigInfo();
38
$paypal_url = explode('|', $tr_config['paypal_url']);
39
$paypal_url = $paypal_url[0];
40
//determine the currency
41
$PP_CURR_CODE = explode('|', $tr_config['pp_curr_code']); // [USD,GBP,JPY,CAD,EUR]
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
42
$PP_CURR_CODE = $PP_CURR_CODE[0];
43
$currencySign = XdonationsUtility::defineCurrency($PP_CURR_CODE);
44
45
$pp_varlist = XdonationsUtility::runSimpleQuery($xoopsDB->prefix('donations_transactions'), '', '', array('id'));
46
47
define('_ERR', 1);
48
define('_INF', 2);
49
$ERR    = 0;
50
$log    = '';
51
$loglvl = $tr_config['ipn_dbg_lvl'];
52
53
// creates a log file in the XOOPS uploads directory
54
$lpFile = XOOPS_UPLOAD_PATH . '/xdonations_ipn.log';
55
if (false != ($lp = fopen($lpFile, 'wb+'))) {
56
    dprt(_MD_XDONATION_LOGFILE_CREATED, _INF);
57
} else {
58
    dprt(_MD_XDONATION_LOGFILE_NOT_CREATED, _ERR);
59
}
60
dprt(date('r'), _INF);
61
$dbg = (isset($_GET['dbg']) && $_GET['dbg']) ? true : false;
62
63
if ($dbg) {
64
    dprt(_MD_XDONATION_DEBUGACTIVE, _INF);
65
    echo _MD_XDONATION_DEBUGHEADER;
66
    $pp_varlist['receiver_email'] = $tr_config['receiver_email'];
67
}
68
69
// read the post from PayPal system and add 'cmd'
70
$req = 'cmd=_notify-validate';
71
72
foreach ($_POST as $key => $value) {
73
    $value = urlencode(stripslashes($value));
74
    $req   .= "&$key=$value";
75
}
76
77
// post back to PayPal system to validate
78
dprt(_MD_XDONATION_OPENCONN, _INF);
79
$fp = fsockopen($paypal_url, 80, $errno, $errstr, 30);
80
81
if (!$fp) { // HTTP ERROR
82
    //TODO: use CURL if fsockopen fails
83
    dprt('<style="color: #00CC00;">' . _MD_XDONATION_CONNFAIL . '</span>', _ERR);
84
    die(sprintf(_MD_XDONATION_POSTBACK_FAIL, $errno, $errstr));
85
} else {
86
    dprt(_MD_XDONATION_POSTBACK_OK, _INF);
87
    $header = "POST /cgi-bin/webscr HTTP/1.0\r\n";
88
    $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
89
    $header .= 'Content-Length: ' . strlen($req) . "\r\n\r\n";
90
}
91
92
// assign posted variables to local variables
93
$pp_varname = array_values(array_intersect(array_keys($pp_varlist), array_keys($_POST)));
94
for ($i = 0, $iMax = count($pp_varname); $i < $iMax; ++$i) {
95
    $pp_varlist[$pp_varname[$i]] = $_POST[$pp_varname[$i]];
96
}
97
98
if ('' == $pp_varlist['payment_date']) { //set blank date to proper format
99
    $pp_varlist['payment_date'] = '0000-00-00 00:00:00';
100
}
101
102
$writeOk = fwrite($fp, $header . $req);
103
if (!$writeOk) { // HTTP ERROR
104
    dprt('<style="font-weight: bold; color: #00CC00;">' . _MD_XDONATION_WRITEFAIL . '</span>', _ERR);
105
    die(sprintf(_MD_XDONATION_ERR_FAILED_WRITE, $errno, $errstr));
106
} else {
107
    dprt(_MD_XDONATION_WRITEOK, _INF);
108
}
109
110
// Perform PayPal email account verification
111
if (!$dbg && (strcasecmp($pp_varlist['business'], $tr_config['receiver_email']) != 0)) {
112
    dprt(sprintf(_MD_XDONATION_BUSINVALID, $pp_varlist['business']), _ERR);
113
    dprt(sprintf(_MD_XDONATION_RCVINVALID, $pp_varlist['receiver_email']), _ERR);
114
    $ERR = 1;
115
}
116
117
$insertSQL = '';
118
// Look for duplicate txn_id's
119
if ($pp_varlist['txn_id']) {
120
    $sql        = 'SELECT COUNT(*) FROM ' . $xoopsDB->prefix('donations_transactions') . " WHERE txn_id = '" . addslashes($pp_varlist['txn_id']) . "'";
121
    $Recordset1 = $xoopsDB->query($sql);
122
    list($NumDups) = $xoopsDB->fetchRow($Recordset1);
123
}
124
if ($pp_varlist['parent_txn_id']) {
125
    $parent_sql            = 'SELECT * FROM ' . $xoopsDB->prefix('donations_transactions') . " WHERE txn_id = '" . addslashes($pp_varlist['parent_txn_id']) . "'";
126
    $parent_Recordset1     = $xoopsDB->query($parent_sql);
127
    $parent_row_Recordset1 = $xoopsDB->fetchArray($parent_Recordset1);
128
    $parent_NumDups        = $xoopsDB->getRowsNum($parent_Recordset1);
129
}
130
131
while (!$dbg && !$ERR && !feof($fp)) {
132
    $res = fgets($fp, 1024);
133
    if (strcmp($res, 'VERIFIED') == 0) {
134
        // Ok - PayPal has told us we have a valid IPN here
135
        dprt(_MD_XDONATION_VERIFIED, _INF);
136
137
        // Check for a reversal for a refund
138
        if (strcmp($pp_varlist['payment_status'], 'Refunded') == 0
139
            || strcmp($pp_varlist['txn_type'], 'Reversal') == 0) {
140
            // Verify the reversal
141
            dprt(_MD_XDONATION_REFUND, _INF);
142
            if (($parent_NumDups == 0) || strcmp($parent_row_Recordset1['payment_status'], 'Completed')
143
                || (strcmp($parent_row_Recordset1['txn_type'], 'web_accept') != 0
144
                    && strcmp($parent_row_Recordset1['txn_type'], 'send_money') != 0)) {
145
                // This is an error.  A reversal implies a pre-existing completed transaction
146
                dprt(_MD_XDONATION_TRANSMISSING, _ERR);
147
                foreach ($_POST as $key => $val) {
148
                    dprt("$key => $val", _ERR);
149
                }
150
                break;
151
            }
152
            if (1 != $parent_NumDups) {
153
                dprt(_MD_XDONATION_MULTITXNS, _ERR);
154
                foreach ($_POST as $key => $val) {
155
                    dprt("$key => $val", _ERR);
156
                }
157
                break;
158
            }
159
160
            /* TODO: Need to add info to database.  If user donates, then cancels a subsequent
161
             * donation then they are removed from group - need a counter to see how many times a
162
             * user has donated and only remove from group if 'everything' donated has been reversed.
163
             */
164
165
            // remove xoopsUsers (not anon) from group selected by Admin in config
166
            if (!empty($pp_varlist['custom'])) {
167
                $memberHandler = xoops_getHandler('member');
168
                $edituser      = $memberHandler->getUser($pp_varlist['custom']);
169
170
                // remove the user from the specified group
171
                if ($tr_config['assign_group']) {  // admin has selected a group in admin
172
                    $groupHandler = xoops_getHandler('group');
173
                    $validGroup   = $groupHandler->get((int)$tr_config['assign_group']); // make sure this is a valid group id
174
                    if ($validGroup) {
175
                        $thisUserGroups = $memberHandler->getGroupsByUser((int)$edituser->getVar('uid'));
176
                        if (!in_array($validGroup->getVar('groupid'), $thisUserGroups)) {
177
178
                            // now find out if user is in the group
179
                            $isMember = $memberHandler->getGroupsByUser($edituser->getVar('uid'));
180
                            if ($isMember) {
181
                                $success = $memberHandler->removeUserFromGroup($tr_config['assign_group'], $edituser->getVar('uid'));
182 View Code Duplication
                                if ($success) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
183
                                    dprt('User ' . $edituser->getVar('uname') . ' was removed from the ' . $validGroup->getVar('name') . ' group', _INF);
184
                                } else {
185
                                    dprt('User ' . $edituser->getVar('uname') . ' could not be removed from the ' . $validGroup->getVar('name') . ' group', _ERR);
186
                                }
187
                            }
188
                        } else {
189
                            dprt($edituser->getVar('uname') . ' was not in the ' . $validGroup->getVar('name') . ' group', _INF);
190
                        }
191
                    } else {
192
                        dprt('Group isn\'t valid - change Admin configs option<br>', _ERR);
193
                    }
194
                }
195
                /*
196
                 * TODO: Need to add a db table variable to 'demote' the user's rank back to where
197
                 * it was prior to the reversal
198
                 */
199
            }
200
            $pp_varlist['payment_date'] = strftime('%Y-%m-%d %H:%M:%S', strtotime($pp_varlist['payment_date']));
201
            $field_values               = $field_names = '';
202 View Code Duplication
            for ($i = 0, $iMax = count($pp_varname); $i < $iMax; ++$i) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
203
                if (0 != $i) {
204
                    $field_names  .= ',';
205
                    $field_values .= ',';
206
                }
207
                $field_names  .= '`' . $pp_varname[$i] . '`';
208
                $field_values .= "'" . $pp_varlist[$pp_varname[$i]] . "'";
209
            }
210
            $insertSQL = 'INSERT INTO ' . $xoopsDB->prefix('donations_transactions') . " ($field_names) VALUES ($field_values)";
211
212
            // We're cleared to add this record
213
            dprt($insertSQL, _INF);
214
            $Result1 = $xoopsDB->queryF($insertSQL);
215
            dprt('SQL result = ' . $Result1, _INF);
216
217
            break;
218
            // Look for a normal payment
219
        } elseif ((strcmp($pp_varlist['payment_status'], 'Completed') == 0)
220
                  && ((strcmp($pp_varlist['txn_type'], 'web_accept') == 0)
221
                      || (strcmp($pp_varlist['txn_type'], 'send_money') == 0))) {
222
            dprt(_MD_XDONATION_NORMAL_TXN, _INF);
223
            if ($lp) {
224
                fwrite($lp, $pp_varlist['payer_email'] . ' ' . $pp_varlist['payment_status'] . ' ' . $pp_varlist['payment_date'] . "\n");
225
            }
226
227
            if ($NumDups != 0) { // Check for a duplicate txn_id
228
                dprt(_MD_XDONATION_DUPLICATETXN, _ERR);
229
                foreach ($_POST as $key => $val) {
230
                    dprt("$key => $val", _ERR);
231
                }
232
                break;
233
            }
234
235
            $pp_varlist['payment_date'] = strftime('%Y-%m-%d %H:%M:%S', strtotime($pp_varlist['payment_date']));
236
            $field_values               = $field_names = '';
237 View Code Duplication
            for ($i = 0, $iMax = count($pp_varname); $i < $iMax; ++$i) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
238
                if ($i != 0) {
239
                    $field_names  .= ',';
240
                    $field_values .= ',';
241
                }
242
                $field_names  .= '`' . $pp_varname[$i] . '`';
243
                $field_values .= "'" . $pp_varlist[$pp_varname[$i]] . "'";
244
            }
245
            $insertSQL = 'INSERT INTO ' . $xoopsDB->prefix('donations_transactions') . " ($field_names) VALUES ($field_values)";
246
247
            // We're cleared to add this record
248
            dprt($insertSQL, _INF);
249
            $Result1 = $xoopsDB->queryF($insertSQL);
250
            dprt('SQL result = ' . $Result1, _INF);
251
252
            // add xoopsUsers (not anon) to group if selected by Admin in config
253
            if (!empty($pp_varlist['custom'])
254
                && (($pp_varlist['option_selection1'] === 'Yes')
255
                    || ($tr_config['don_forceadd'] == '1'))) {
256
                $memberHandler = xoops_getHandler('member');
257
                $edituser      = $memberHandler->getUser($pp_varlist['custom']);
258
259
                // add the user to specified group
260
                if ($tr_config['assign_group']) {  // config option set to add users to a group
261
                    $groupHandler = xoops_getHandler('group');
262
                    $validGroup   = $groupHandler->get((int)$tr_config['assign_group']); // make sure this is a valid group id
263
                    if ($validGroup) {
264
                        $thisUserGroups = $memberHandler->getGroupsByUser((int)$edituser->getVar('uid'));
265
                        if (!in_array($validGroup->getVar('groupid'), $thisUserGroups)) {
266
                            $success = $memberHandler->addUserToGroup($validGroup->getVar('groupid'), $edituser->getVar('uid'));
267 View Code Duplication
                            if ($success) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
268
                                dprt('User ' . $edituser->getVar('uname') . ' was added to the ' . $validGroup->getVar('name') . ' group', _INF);
269
                            } else {
270
                                dprt('User ' . $edituser->getVar('uname') . ' could not be addded to the ' . $validGroup->getVar('name') . ' group', _ERR);
271
                            }
272
                        } else {
273
                            dprt($edituser->getVar('uname') . ' is already in the ' . $validGroup->getVar('name') . ' group', _INF);
274
                        }
275
                    } else {
276
                        dprt('Group isn\'t valid - change Admin configs option<br>', _ERR);
277
                    }
278
                }
279
280
                // add the user to a specific rank
281
                if ($tr_config['assign_rank']) {  // config option set to add users to a rank
282
                    $urank = $edituser->rank();
283
                    if (0 != $urank['id']) {
284
                        $result = $xoopsDB->query('SELECT COUNT(*) FROM ' . $xoopsDB->prefix('ranks') . " WHERE rank_id='" . $urank['id'] . "' AND rank_special=0");
285
                        list($rank_check) = $xoopsDB->fetchRow($result);
286
                        $xoopsDB->freeRecordSet($result);
287
                    } else {
288
                        $rank_check = 1;
289
                    }
290
                    if ($rank_check > 0) {
291
                        // set user's new rank
292
                        $edituser->setVar('rank', $tr_config['assign_rank']);
293
                        $memberHandler->insertUser($edituser);
294
                    }
295
                }
296
            }
297
            break;
298
        } else { // We're not interested in this transaction, so we're done
299
            dprt(_MD_XDONATION_NOTINTERESTED, _ERR);
300
            foreach ($_POST as $key => $val) {
301
                dprt("$key => $val", _ERR);
302
            }
303
            dprt('pp_varlist:', _ERR);
304
            foreach ($pp_varlist as $key => $val) {
0 ignored issues
show
The expression $pp_varlist of type array|false is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
305
                dprt("$key => $val", _ERR);
306
            }
307
            dprt('strcmp payment_status: ' . strcmp($pp_varlist['payment_status'], 'Completed'), _ERR);
308
            dprt('strcmp txn_type: ' . strcmp($pp_varlist['txn_type'], 'web_accept'), _ERR);
309
            dprt('strcmp txn_type: ' . strcmp($pp_varlist['txn_type'], 'send_money'), _ERR);
310
            break;
311
        }
312
    } elseif (strcmp($res, 'INVALID') == 0) {
313
        // log for manual investigation
314
        dprt(_MD_XDONATION_INVALIDIPN, _ERR);
315
        foreach ($_POST as $key => $val) {
316
            dprt("$key => $val", _ERR);
317
        }
318
        break;
319
    } else {
320
        dprt(_MD_XDONATION_ERR_UNKNOWN_IPN_STAT, _ERR);
321
    }
322
}
323
324
if ($dbg) {
325
    $sql = 'SELECT * FROM ' . $xoopsDB->prefix('donations_transactions') . ' LIMIT 10';
326
    dprt(_MD_XDONATION_EXECUTING_QUERY, _INF);
327
    $Result1 = $xoopsDB->query($sql);
328
    if ($Result1) {
329
        dprt('<span style="font-weight: bold; color: #00CC00;">' . _MD_XDONATION_DEBUGPASS . '</span>', _INF);
330
    } else {
331
        dprt('<span style="font-weight: bold; color: #CC0000;">' . _MD_XDONATION_DEBUGFAIL . '</span>', _ERR);
332
    }
333
    dprt(sprintf(_MD_XDONATION_RCVEMAIL, $tr_config['receiver_email']), _INF);
334
}
335
336
if ($log) {
337
    dprt('<br>' . _MD_XDONATION_LOGBEGIN . "<br>\n", _INF);
338
    // Insert the log entry
339
    $currentDate = strftime('%Y-%m-%d %H:%M:%S', time());
340
    $paymentDate = isset($_POST['payment_date']) ? strftime('%Y-%m-%d %H:%M:%S', strtotime($_POST['payment_date'])) : $currentDate;
341
    $sql         = 'INSERT INTO ' . $xoopsDB->prefix('donations_translog') . " VALUES (NULL,'{$currentDate}', '{$paymentDate}','" . addslashes($log) . "')";
342
    $Result1     = $xoopsDB->queryF($sql);
343
344
    // Clear out old log entries
345
    $sql     = 'SELECT COUNT(*) FROM ' . $xoopsDB->prefix('donations_translog');
346
    $Result1 = $xoopsDB->query($sql);
347
    list($countLogs) = $xoopsDB->fetchRow($Result1);
348
    if ($countLogs == $tr_config['ipn_log_entries']) {
349
        $sql     = 'DELETE FROM ' . $xoopsDB->prefix('donations_translog');
350
        $Result1 = $xoopsDB->queryF($sql);
351
        if (false === $Result1) {
352
            dprt(_MD_XDONATION_ERR_TXN_NOCLEAR, _ERR);
353
        } else {
354
            dprt(_MD_XDONATION_ERR_TXN_CLEAR, _INF);
355
        }
356
    }
357
}
358
359
fclose($fp);
360
if ($lp) {
361
    fwrite($lp, _MD_XDONATION_EXITING . "\n");
362
    fclose($lp);
363
}
364
365
if ($dbg) {
366
    echo '<hr />';
367
    echo _MD_XDONATION_IFNOERROR . "<br>\n";
368
    echo "<a href='javascript:window.close();'>Close Window</a>";
369
}
370
371
/**
372
 *
373
 * Debug Message Store/Display
374
 * @param string $str  string to store/display
375
 * @param int    $clvl error reporting level (1 = error, 2= info)
376
 */
377
function dprt($str, $clvl)
378
{
379
    global $dbg, $lp, $log, $loglvl;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
380
381
    if (isset($dbg) && $dbg) {
382
        echo $str . '<br>';
383
    }
384
385
    if (isset($loglvl) && ($clvl <= $loglvl)) {
386
        $log .= $str . "\n";
387
        if (isset($lp) && $lp) {
388
            fwrite($lp, strip_tags($str) . "\r\n");
389
        }
390
    }
391
}
392