Completed
Push — master ( 625e32...22cdbd )
by Michael
02:37
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
include_once 'include/functions.php';
35
include_once XOOPS_ROOT_PATH . '/class/xoopsformloader.php';
36
37
$tr_config  = configInfo();
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    = defineCurrency($PP_CURR_CODE);
44
45
$pp_varlist = simple_query($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, 'w+'))) {
56
    dprt(_MD_DON_LOGFILE_CREATED, _INF);
57
} else {
58
    dprt(_MD_DON_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_DON_DEBUGACTIVE, _INF);
65
    echo _MD_DON_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_DON_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_DON_CONNFAIL . '</span>', _ERR);
84
    die(sprintf(_MD_DON_POSTBACK_FAIL, $errno, $errstr));
85
} else {
86
    dprt(_MD_DON_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_DON_WRITEFAIL . '</span>', _ERR);
105
    die(sprintf(_MD_DON_ERR_FAILED_WRITE, $errno, $errstr));
106
} else {
107
    dprt(_MD_DON_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_DON_BUSINVALID, $pp_varlist['business']), _ERR);
113
    dprt(sprintf(_MD_DON_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_DON_VERIFIED, _INF);
136
137
        // Check for a reversal for a refund
138
        if (strcmp($pp_varlist['payment_status'], 'Refunded') == 0 || strcmp($pp_varlist['txn_type'], 'Reversal') == 0) {
139
            // Verify the reversal
140
            dprt(_MD_DON_REFUND, _INF);
141
            if (($parent_NumDups == 0) || strcmp($parent_row_Recordset1['payment_status'], 'Completed') || (strcmp($parent_row_Recordset1['txn_type'], 'web_accept') != 0 && strcmp($parent_row_Recordset1['txn_type'], 'send_money') != 0)) {
142
                // This is an error.  A reversal implies a pre-existing completed transaction
143
                dprt(_MD_DON_TRANSMISSING, _ERR);
144
                foreach ($_POST as $key => $val) {
145
                    dprt("$key => $val", _ERR);
146
                }
147
                break;
148
            }
149
            if (1 != $parent_NumDups) {
150
                dprt(_MD_DON_MULTITXNS, _ERR);
151
                foreach ($_POST as $key => $val) {
152
                    dprt("$key => $val", _ERR);
153
                }
154
                break;
155
            }
156
157
            /* TODO: Need to add info to database.  If user donates, then cancels a subsequent
158
             * donation then they are removed from group - need a counter to see how many times a
159
             * user has donated and only remove from group if 'everything' donated has been reversed.
160
             */
161
162
            // remove xoopsUsers (not anon) from group selected by Admin in config
163
            if (!empty($pp_varlist['custom'])) {
164
                $member_handler = xoops_getHandler('member');
165
                $edituser       =& $member_handler->getUser($pp_varlist['custom']);
166
167
                // remove the user from the specified group
168
                if ($tr_config['assign_group']) {  // admin has selected a group in admin
169
                    $group_handler = xoops_getHandler('group');
170
                    $validGroup    = $group_handler->get((int)$tr_config['assign_group']); // make sure this is a valid group id
171
                    if ($validGroup) {
172
                        $thisUserGroups = $member_handler->getGroupsByUser((int)$edituser->getVar('uid'));
173
                        if (!in_array($validGroup->getVar('groupid'), $thisUserGroups)) {
174
175
                            // now find out if user is in the group
176
                            $isMember = $member_handler->getGroupsByUser($edituser->getVar('uid'));
177
                            if ($isMember) {
178
                                $success = $member_handler->removeUserFromGroup($tr_config['assign_group'], $edituser->getVar('uid'));
179 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...
180
                                    dprt('User ' . $edituser->getVar('uname') . ' was removed from the ' . $validGroup->getVar('name') . ' group', _INF);
181
                                } else {
182
                                    dprt('User ' . $edituser->getVar('uname') . ' could not be removed from the ' . $validGroup->getVar('name') . ' group', _ERR);
183
                                }
184
                            }
185
                        } else {
186
                            dprt($edituser->getVar('uname') . ' was not in the ' . $validGroup->getVar('name') . ' group', _INF);
187
                        }
188
                    } else {
189
                        dprt('Group isn\'t valid - change Admin configs option<br />', _ERR);
190
                    }
191
                }
192
                /*
193
                 * TODO: Need to add a db table variable to 'demote' the user's rank back to where
194
                 * it was prior to the reversal
195
                 */
196
            }
197
            $pp_varlist['payment_date'] = strftime('%Y-%m-%d %H:%M:%S', strtotime($pp_varlist['payment_date']));
198
            $field_values               = $field_names = '';
199 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...
200
                if (0 != $i) {
201
                    $field_names .= ',';
202
                    $field_values .= ',';
203
                }
204
                $field_names .= '`' . $pp_varname[$i] . '`';
205
                $field_values .= "'" . $pp_varlist[$pp_varname[$i]] . "'";
206
            }
207
            $insertSQL = 'INSERT INTO ' . $xoopsDB->prefix('donations_transactions') . " ($field_names) VALUES ($field_values)";
208
209
            // We're cleared to add this record
210
            dprt($insertSQL, _INF);
211
            $Result1 = $xoopsDB->queryF($insertSQL);
212
            dprt('SQL result = ' . $Result1, _INF);
213
214
            break;
215
            // Look for a normal payment
216
        } elseif ((strcmp($pp_varlist['payment_status'], 'Completed') == 0) && ((strcmp($pp_varlist['txn_type'], 'web_accept') == 0) || (strcmp($pp_varlist['txn_type'], 'send_money') == 0))) {
217
            dprt(_MD_DON_NORMAL_TXN, _INF);
218
            if ($lp) {
219
                fwrite($lp, $pp_varlist['payer_email'] . ' ' . $pp_varlist['payment_status'] . ' ' . $pp_varlist['payment_date'] . "\n");
220
            }
221
222
            if ($NumDups != 0) { // Check for a duplicate txn_id
223
                dprt(_MD_DON_DUPLICATETXN, _ERR);
224
                foreach ($_POST as $key => $val) {
225
                    dprt("$key => $val", _ERR);
226
                }
227
                break;
228
            }
229
230
            $pp_varlist['payment_date'] = strftime('%Y-%m-%d %H:%M:%S', strtotime($pp_varlist['payment_date']));
231
            $field_values               = $field_names = '';
232 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...
233
                if ($i != 0) {
234
                    $field_names .= ',';
235
                    $field_values .= ',';
236
                }
237
                $field_names .= '`' . $pp_varname[$i] . '`';
238
                $field_values .= "'" . $pp_varlist[$pp_varname[$i]] . "'";
239
            }
240
            $insertSQL = 'INSERT INTO ' . $xoopsDB->prefix('donations_transactions') . " ($field_names) VALUES ($field_values)";
241
242
            // We're cleared to add this record
243
            dprt($insertSQL, _INF);
244
            $Result1 = $xoopsDB->queryF($insertSQL);
245
            dprt('SQL result = ' . $Result1, _INF);
246
247
            // add xoopsUsers (not anon) to group if selected by Admin in config
248
            if (!empty($pp_varlist['custom']) && (($pp_varlist['option_selection1'] === 'Yes') || ($tr_config['don_forceadd'] == '1'))) {
249
                $member_handler = xoops_getHandler('member');
250
                $edituser       =& $member_handler->getUser($pp_varlist['custom']);
251
252
                // add the user to specified group
253
                if ($tr_config['assign_group']) {  // config option set to add users to a group
254
                    $group_handler = xoops_getHandler('group');
255
                    $validGroup    = $group_handler->get((int)$tr_config['assign_group']); // make sure this is a valid group id
256
                    if ($validGroup) {
257
                        $thisUserGroups = $member_handler->getGroupsByUser((int)$edituser->getVar('uid'));
258
                        if (!in_array($validGroup->getVar('groupid'), $thisUserGroups)) {
259
                            $success = $member_handler->addUserToGroup($validGroup->getVar('groupid'), $edituser->getVar('uid'));
260 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...
261
                                dprt('User ' . $edituser->getVar('uname') . ' was added to the ' . $validGroup->getVar('name') . ' group', _INF);
262
                            } else {
263
                                dprt('User ' . $edituser->getVar('uname') . ' could not be addded to the ' . $validGroup->getVar('name') . ' group', _ERR);
264
                            }
265
                        } else {
266
                            dprt($edituser->getVar('uname') . ' is already in the ' . $validGroup->getVar('name') . ' group', _INF);
267
                        }
268
                    } else {
269
                        dprt('Group isn\'t valid - change Admin configs option<br />', _ERR);
270
                    }
271
                }
272
273
                // add the user to a specific rank
274
                if ($tr_config['assign_rank']) {  // config option set to add users to a rank
275
                    $urank = $edituser->rank();
276
                    if (0 != $urank['id']) {
277
                        $result = $xoopsDB->query('SELECT COUNT(*) FROM ' . $xoopsDB->prefix('ranks') . " WHERE rank_id='" . $urank['id'] . "' AND rank_special=0");
278
                        list($rank_check) = $xoopsDB->fetchRow($result);
279
                        $xoopsDB->freeRecordSet($result);
280
                    } else {
281
                        $rank_check = 1;
282
                    }
283
                    if ($rank_check > 0) {
284
                        // set user's new rank
285
                        $edituser->setVar('rank', $tr_config['assign_rank']);
286
                        $member_handler->insertUser($edituser);
287
                    }
288
                }
289
            }
290
            break;
291
        } else { // We're not interested in this transaction, so we're done
292
            dprt(_MD_DON_NOTINTERESTED, _ERR);
293
            foreach ($_POST as $key => $val) {
294
                dprt("$key => $val", _ERR);
295
            }
296
            dprt('pp_varlist:', _ERR);
297
            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...
298
                dprt("$key => $val", _ERR);
299
            }
300
            dprt('strcmp payment_status: ' . strcmp($pp_varlist['payment_status'], 'Completed'), _ERR);
301
            dprt('strcmp txn_type: ' . strcmp($pp_varlist['txn_type'], 'web_accept'), _ERR);
302
            dprt('strcmp txn_type: ' . strcmp($pp_varlist['txn_type'], 'send_money'), _ERR);
303
            break;
304
        }
305
    } elseif (strcmp($res, 'INVALID') == 0) {
306
        // log for manual investigation
307
        dprt(_MD_DON_INVALIDIPN, _ERR);
308
        foreach ($_POST as $key => $val) {
309
            dprt("$key => $val", _ERR);
310
        }
311
        break;
312
    } else {
313
        dprt(_MD_DON_ERR_UNKNOWN_IPN_STAT, _ERR);
314
    }
315
}
316
317
if ($dbg) {
318
    $sql = 'SELECT * FROM ' . $xoopsDB->prefix('donations_transactions') . ' LIMIT 10';
319
    dprt(_MD_DON_EXECUTING_QUERY, _INF);
320
    $Result1 = $xoopsDB->query($sql);
321
    if ($Result1) {
322
        dprt("<span style=\"font-weight: bold; color: #00CC00;\">" . _MD_DON_DEBUGPASS . '</span>', _INF);
323
    } else {
324
        dprt("<span style=\"font-weight: bold; color: #CC0000;\">" . _MD_DON_DEBUGFAIL . '</span>', _ERR);
325
    }
326
    dprt(sprintf(_MD_DON_RCVEMAIL, $tr_config['receiver_email']), _INF);
327
}
328
329
if ($log) {
330
    dprt('<br />' . _MD_DON_LOGBEGIN . "<br />\n", _INF);
331
    // Insert the log entry
332
    $currentDate = strftime('%Y-%m-%d %H:%M:%S', time());
333
    $paymentDate = isset($_POST['payment_date']) ? strftime('%Y-%m-%d %H:%M:%S', strtotime($_POST['payment_date'])) : $currentDate;
334
    $sql         = 'INSERT INTO ' . $xoopsDB->prefix('donations_translog') . " VALUES (NULL,'{$currentDate}', '{$paymentDate}','" . addslashes($log) . "')";
335
    $Result1     = $xoopsDB->queryF($sql);
336
337
    // Clear out old log entries
338
    $sql     = 'SELECT COUNT(*) FROM ' . $xoopsDB->prefix('donations_translog');
339
    $Result1 = $xoopsDB->query($sql);
340
    list($countLogs) = $xoopsDB->fetchRow($Result1);
341
    if ($countLogs == $tr_config['ipn_log_entries']) {
342
        $sql     = 'DELETE FROM ' . $xoopsDB->prefix('donations_translog');
343
        $Result1 = $xoopsDB->queryF($sql);
344
        if (false === $Result1) {
345
            dprt(_MD_DON_ERR_TXN_NOCLEAR, _ERR);
346
        } else {
347
            dprt(_MD_DON_ERR_TXN_CLEAR, _INF);
348
        }
349
    }
350
}
351
352
fclose($fp);
353
if ($lp) {
354
    fwrite($lp, _MD_DON_EXITING . "\n");
355
    fclose($lp);
356
}
357
358
if ($dbg) {
359
    echo '<hr />';
360
    echo _MD_DON_IFNOERROR . "<br />\n";
361
    echo "<a href='javascript:window.close();'>Close Window</a>";
362
}
363
364
/**
365
 *
366
 * Debug Message Store/Display
367
 * @param string $str  string to store/display
368
 * @param int    $clvl error reporting level (1 = error, 2= info)
369
 */
370
function dprt($str, $clvl)
371
{
372
    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...
373
374
    if (isset($dbg) && $dbg) {
375
        echo $str . '<br />';
376
    }
377
378
    if (isset($loglvl) && ($clvl <= $loglvl)) {
379
        $log .= $str . "\n";
380
        if (isset($lp) && $lp) {
381
            fwrite($lp, strip_tags($str) . "\r\n");
382
        }
383
    }
384
}
385