Hi, there!
I have an incredibly urgent issue. I believe I've found quite a large bug/oversight within ATUM.
I am going to do my absolute best to give as much detail as possible to make your lives easier 🙂
Shortly after we started using ATUM on our live site, a site with over 18,000 images... we started experiencing some really really strange issues.
Seemingly at random, all of our images started disappearing out of our wp-content/uploads
directory. We would then have to re-upload all of our images via FTP... all 18k of them.
At first, we thought this was an errant CRON running. So we started looking closer at the CRONs and started trying to debug every single CRON running on the site. We didn't find anything.
Next, we thought maybe a corrupt transient was causing the issue, so we deleted all transients and the issue seemed to go away over the weekend.
Then... today all of the images started disappearing again. We worked closely with our premium hosting provider, Kinsta, to finally pinpoint the issue.
In order to hopefully "catch" the issue, we disabled PHP unlink()
on the server temporarily, thinking that whatever was deleting these images would probably be using PHP unlink()
to do so. By disabling it, we could ensure a PHP error would be thrown in the logs that would then be traceable, thus hopefully exposing the issue.
Finally... in the late afternoon today (03/25), we saw a PHP error pop up in the logs... right at the time a Purchase Order was being processed by our client. Here is the error:
2019/03/25 19:37:09 [error] 32564#32564: *159700 FastCGI sent in stderr: "PHP message: PHP Warning: unlink() has been disabled for security reasons in /www/batafeda_344/public/wp-content/plugins/atum-stock-manager-for-woocommerce/vendor/mpdf/mpdf/src/Cache.php on line 95" while reading response header from upstream, client: 76.190.79.10, server: thisisgoodmedicine.com, request: "GET /wp-admin/admin-ajax.php?action=atum_order_pdf&atum_order_id=11568&_wpnonce=c7b2e81734 HTTP/1.0", upstream: "fastcgi://unix:/var/run/php7.3-fpm-batafeda.sock:", host: "thisisgoodmedicine.com", referrer: "https://thisisgoodmedicine.com/wp-admin/edit.php?post_type=atum_purchase_order"
We then hopped into our editor to inspect this file and found the following method using unlink()
.
public function clearOld()
{
$iterator = new DirectoryIterator($this->basePath);
/** @var \DirectoryIterator $item */
foreach ($iterator as $item) {
if (!$item->isDot()
&& $item->isFile()
&& !$this->isDotFile($item)
&& $this->isOld($item)) {
unlink($item->getPathname());
}
}
}
This method is invoked in /www/batafeda_344/public/wp-content/plugins/atum-stock-manager-for-woocommerce/vendor/mpdf/mpdf/src/Mpdf.php
Then... we searched outside of the /vendor/
folder and found this file:
/www/batafeda_344/public/wp-content/plugins/atum-stock-manager-for-woocommerce/classes/PurchaseOrders/PurchaseOrders.php
In this file, a method called generate_order_pdf()
on line 484
instantiates Mpdf
on line 496
.
On line 499
, the following property is defined and passed to Mpdf
: 'tempDir' => $uploads['basedir']
$uploads
is defined on line 494
as wp_upload_dir()
So taking all of that, 'tempDir' => $uploads['basedir']
is really fully resolving to:
www\batafeda_344\public\wp-content\uploads
Reference here: https://developer.wordpress.org/reference/functions/wp_upload_dir/
This theoretically shouldn't be a problem, as most WordPress media files are housed in date-based subfolders as you know... but on our site... we opted not to do that... we have all of our images in the root. This setting can be seen in WP Admin -> Settings -> Media. See screenshot below.
https://d.pr/i/q3EhTg
So bringing this thing full circle...
public function clearOld()
{
$iterator = new DirectoryIterator($this->basePath);
/** @var \DirectoryIterator $item */
foreach ($iterator as $item) {
if (!$item->isDot()
&& $item->isFile()
&& !$this->isDotFile($item)
&& $this->isOld($item)) {
unlink($item->getPathname());
}
}
}
is running on the root uploads directory and clearing out any file that is in that directory that is NOT in a subfolder. Since all of our images are in the root, they are all getting deleted when generate_order_pdf()
is run.
We have confirmed this multiple times on local and development environments. Our host has also confirmed this is the issue after we tracked it down together over the course of a week and a half.
Moving Forward
I think a simple solution could be just to define a custom folder within uploads specific to ATUM Purchase Order PDFs. Like $uploads['basedir'] . '\atum-purchase-orders'
. This would scope the PDF generation function to that folder and prevent it from wiping out files in the root of the uploads dir. I don't know all of the ramifications of a change like this, but I am just throwing out ideas.
At this point, this is making ATUM pretty much unusable on our large eCommerce site, because we cannot process any new inventory without losing all of our images.
I hope this context is helpful. I'm more than willing to answer any additional questions needed, but hopefully, I have outlined the problem clearly and provided necessary details for you to recognize the issue and rapidly implement a solution.
Very much looking forward to hearing back from you on this.
Thank you!