Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding support to keep original Filename of Attachment and open more flexibility #645

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "php-imap/php-imap",
"name": "fglueck/php-imap",
"description": "Manage mailboxes, filter/get/delete emails in PHP (supports IMAP/POP3/NNTP)",
"keywords": [
"PHP",
Expand Down
77 changes: 63 additions & 14 deletions src/PhpImap/Mailbox.php
Original file line number Diff line number Diff line change
Expand Up @@ -156,16 +156,21 @@ class Mailbox
/** @var string */
protected $mailboxFolder;

/** @var bool|false */
protected $attachmentFilenameMode = false;

public const ATTACH_FILE_NAME_RANDOM = 1; // Filename is unique (random)
public const ATTACH_FILE_NAME_ORIGINAL = 2; // Filename is Attachment-Filename
public const ATTACH_FILE_NAME_ITTERATED = 3; // Filename is Attachment-Filename but if allready exists it will be extend by Number like: Filename (1).ext
public const ATTACH_FILE_NAME_TRANSFER = 3; // Filename is Attachment-Filename but if allready exists in current transfer-job it will be extend by Number like: Filename (1).ext if file exists from prior transfer it will be overwritten
static $fileNameStack = [];

/** @var int */
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/** @var int */
/** @var int */

protected $attachmentsFilenameMode = self::ATTACH_FILE_NAME_RANDOM;
/** @var resource|null */
private $imapStream;

/**
* @throws InvalidParameterException
*/
public function __construct(string $imapPath, string $login, string $password, string $attachmentsDir = null, string $serverEncoding = 'UTF-8', bool $trimImapPath = true, bool $attachmentFilenameMode = false)
public function __construct(string $imapPath, string $login, string $password, string $attachmentsDir = null, string $serverEncoding = 'UTF-8', bool $trimImapPath = true, bool $attachmentFilenameMode = self::ATTACH_FILE_NAME_RANDOM)
{
$this->imapPath = (true == $trimImapPath) ? \trim($imapPath) : $imapPath;
$this->imapLogin = \trim($login);
Expand Down Expand Up @@ -326,7 +331,20 @@ public function setAttachmentsIgnore(bool $attachmentsIgnore): void
{
$this->attachmentsIgnore = $attachmentsIgnore;
}


/**
* Set $this->setAttachmentsRandomFilename param.
*
* @param int $random ATTACH_FILE_NAME_RANDOM, ATTACH_FILE_NAME_ORIGINAL, ATTACH_FILE_NAME_ITTERATED, ATTACH_FILE_NAME_TRANSFER
*
* @return Mailbox
*/
public function setAttachmentsFilenameMode(int $mode) : Mailbox
{
$this->attachmentsFilenameMode = $mode;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I miss a check here, which ensures, that only supported modes can be set. Something like this:

Suggested change
$this->attachmentsFilenameMode = $mode;
if (!defined($mode))
{
throw new UnexpectedValueException("The defined mode '$mode' is not supported. Supported modes: ATTACH_FILE_NAMERANDOM, ATTACH_FILE_NAMEORIGINAL");
}
$this->attachmentsFilenameMode = $mode;

Note: I haven't tested this code.

return $this;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need this return here? Wouldn't be a void function enough?

}

/**
* Get $this->attachmentsIgnore param.
*
Expand Down Expand Up @@ -547,7 +565,7 @@ public function disconnect(): void
}

/**
* Sets 'expunge on disconnect' parameter.
* Sets 'expunge on disconnect' parameter.
*/
public function setExpungeOnDisconnect(bool $isEnabled): void
{
Expand Down Expand Up @@ -671,7 +689,7 @@ public function searchMailboxFrom(string $criteria, string $sender, string ...$s
/**
* Search the mailbox for emails from multiple, specific senders whilst not using server encoding.
*
* @see Mailbox::searchMailboxFromWithOrWithoutDisablingServerEncoding()
* @see Mailbox::searchMailboxFromWithOrWithoutDisablingServerEncoding()
*
* @return int[]
*
Expand Down Expand Up @@ -1400,26 +1418,57 @@ public function downloadAttachment(DataPartInfo $dataInfo, array $params, object

$attachmentsDir = $this->getAttachmentsDir();

if (null != $attachmentsDir) {
if (true == $this->getAttachmentFilenameMode()) {
$fileSysName = $attachment->name;
} else {
$fileSysName = \bin2hex(\random_bytes(16)).'.bin';
if (null !== $attachmentsDir) {
switch($this->attachmentsFilenameMode) {
case self::ATTACH_FILE_NAME_ORIGINAL:
$fileSysName = $this->sanitizeFileName($fileName);
break;
case self::ATTACH_FILE_NAME_ITTERATED:
$fileSysName = $this->getNewFileSysName($this->sanitizeFileName($fileName));
break;
case self::ATTACH_FILE_NAME_TRANSFER:
$fileSysName = $this->getNewFileName($this->sanitizeFileName($fileName));
break;
case self::ATTACH_FILE_NAME_RANDOM:
default:
$fileSysName = \bin2hex(\random_bytes(16)).'.bin';
}

$filePath = $attachmentsDir.DIRECTORY_SEPARATOR.$fileSysName;

if (\strlen($filePath) > self::MAX_LENGTH_FILEPATH) {
$ext = \pathinfo($filePath, PATHINFO_EXTENSION);
$filePath = \substr($filePath, 0, self::MAX_LENGTH_FILEPATH - 1 - \strlen($ext)).'.'.$ext;
}

$attachment->setFilePath($filePath);
$attachment->saveToDisk();
}

return $attachment;
}
public function sanitizeFileName(string $fileName) : string {
return preg_replace('/([^\p{L}\s\d\-_~,;:\[\]\(\).\'])/is', '-', $fileName);
}
public function getNewFileSysName(string $fileSysName) : string {
$i = 1;
while(file_exists($this->getAttachmentsDir().DIRECTORY_SEPARATOR.$fileSysName)) {
$frag = pathinfo($fileSysName);
$ext = empty($frag['extension']) ? '' : ".{$frag['extension']}";
$fileSysName = "{$frag['filename']} ({$i}){$ext}";
$i++;
}
return $fileSysName;
}
public function getNewFileName(string $fileName) : string {
$i = 1;
while(!in_array($fileName, self::$fileNameStack, true)) {
$frag = pathinfo($fileName);
$ext = empty($frag['extension']) ? '' : ".{$frag['extension']}";
$fileName = "{$frag['filename']} ({$i}){$ext}";
$i++;
}
return $fileName;
}

/**
* Converts a string to UTF-8.
Expand Down