php-imap icon indicating copy to clipboard operation
php-imap copied to clipboard

Move message fails silently

Open EthraZa opened this issue 4 years ago • 12 comments

Move message fails silently.

$messagge->move('INBOX.Trash');   // fails silently.

Here is the why:

move method at https://github.com/Webklex/php-imap/blob/master/src/Message.php#L901 does

$status = $this->client->getConnection()->examineFolder($folder_path);

if (isset($status["uidnext"])) {

but, "uidnext" does no exists for me.

dd($this->client->getConnection()->examineFolder('INBOX.Trash'));

array:4 [
  "flags" => array:1 [
    0 => array:6 [
      0 => "\Draft"
      1 => "\Answered"
      2 => "\Flagged"
      3 => "\Deleted"
      4 => "\Seen"
      5 => "\Recent"
    ]
  ]
  "exists" => "1"
  "recent" => "0"
  "uidvalidity" => 1444046641
]

Now next line is 917:

return null;

That lets me wondering what just happened that message was not moved and no exception was raised. :)

EthraZa avatar Feb 02 '21 18:02 EthraZa

Hi @EthraZa , thanks for your report.

This is actually intended - Providers such as Microsoft do their own little thing and don't provide the next uid. The message should be moved regardless. There is just no way to find out were it went after it got moved...

Best regards,

Webklex avatar Feb 03 '21 01:02 Webklex

Hi. This is my own very old mail server running Postfix and Courier imap. :)

The message is not being moved at all, because it is not passing by the IF statement.

    public function move($folder_path, $expunge = false) {
        $this->client->openFolder($folder_path);
        $status = $this->client->getConnection()->examineFolder($folder_path);

        if (isset($status["uidnext"])) {    // <-- YOU SHALL NOT PASS
          // Would Move Message Here
        }

        return null;
    }

To it to move the message the IF would need to be something like:

if (isset($status["uidnext"]) || isset($status["uidvalidity"])) {
    $next_uid = $status["uidnext"] ?? $status["uidvalidity"];

I just don't know if you can simple replace "uidnext" with "uidvalidity" at $next_uid.

EthraZa avatar Feb 03 '21 11:02 EthraZa

Hi @EthraZa , haha, sorry my bad :)

I'll look into it. I don't think uidvalidity will work - isn't that just the highest possible uid?

Best regards,

Webklex avatar Feb 03 '21 11:02 Webklex

According to https://www.php.net/manual/en/function.imap-status.php:

SA_UIDNEXT - set $status->uidnext to the next uid to be used in the mailbox
SA_UIDVALIDITY - set $status->uidvalidity to a constant that changes when uids for the mailbox may no longer be valid 

But I see that the moveMessage don't need it to happen, it will be used only on fetchNewMail. I'm not sure why fetchNewMail is needed there, at move and copy methods, so I can't opine on how to solve this.

EthraZa avatar Feb 03 '21 11:02 EthraZa

Yes you are right! Could you test it?

Update Message::class @ Line 901-918:

public function move($folder_path, $expunge = false, $fetch_new = true) {
        $this->client->openFolder($folder_path);

        /** @var Folder $folder */
        $folder = $this->client->getFolderByPath($folder_path);

        $this->client->openFolder($this->folder_path);
        if ($this->client->getConnection()->moveMessage($folder->path, $this->getSequenceId(), null, $this->sequence === IMAP::ST_UID) == true) {
            if ($fetch_new) {
                $status = $this->client->getConnection()->examineFolder($folder_path);
                if (isset($status["uidnext"])) {
                    return $this->fetchNewMail($folder, $status["uidnext"], "moved", $expunge);
                }
                return null;
            }
            return true;
        }

        return $fetch_new ? null : false;
    }

...and call:

$check = $message->move("INBOX.somewhere", false, false);

Best regards,

Webklex avatar Feb 03 '21 11:02 Webklex

Didn't work.

...
$this->client->openFolder($this->folder_path);
dd([$this->client->getConnection()->moveMessage($folder->path, $this->getSequenceId(), null, $this->sequence === IMAP::ST_UID), $folder->path, $this->getSequenceId(), null, $this->sequence === IMAP::ST_UID]);
if (...
^ array:5 [
  0 => false
  1 => "INBOX.Trash"
  2 => 3
  3 => null
  4 => false
]

The moveMessage itself is returning false (at 0).

ImapProtocol.php / moveMessage:

dd([$this->requestAndResponse($command, [$set, $this->escapeString($folder)], true), $command, $set, $folder]);
^ array:4 [
  0 => false
  1 => "MOVE"
  2 => 3
  3 => "INBOX.Trash"
]

ImapProtocol.php / requestAndResponse:

dd([$this->sendRequest($command, $tokens, $tag), $this->readResponse($tag, $dontParse), $command, $tokens, $tag, $dontParse]);

$command == anything

^ array:6 [
  0 => null
  1 => true
  2 => "LOGIN"
  3 => array:2 [
    0 => ""EMAIL@DOMAIN""
    1 => ""PASSWORD""
  ]
  4 => "TAG1"
  5 => true
]
^ array:6 [
  0 => null
  1 => array:1 [
    0 => "BYE Courier-IMAP server shutting down\r\n"
  ]
  2 => "LOGOUT"
  3 => []
  4 => "TAG2"
  5 => true
]

$command == "MOVE"

^ array:6 [
  0 => null
  1 => false
  2 => "MOVE"
  3 => array:2 [
    0 => 3
    1 => ""INBOX.Trash""
  ]
  4 => "TAG18"
  5 => true
]

EthraZa avatar Feb 03 '21 12:02 EthraZa

Hi @EthraZa , thanks for the detailed dumps. Unfortunately I don't see the problem which could cause this...

Could you provide me a limited test account, so I can "play" with it over the weekend? If it is possible, please mail the details to [email protected].

Best regards,

Webklex avatar Feb 03 '21 13:02 Webklex

Sure, just sent an email.

Thank you.

EthraZa avatar Feb 03 '21 13:02 EthraZa

Hi @EthraZa , your mail never made it into my mailbox - if my mailserver rejected it, you've should have received a message :)

Best regards,

Webklex avatar Feb 17 '21 16:02 Webklex

Just saw that and resent the email, now as my gmail account.

EthraZa avatar Mar 31 '21 19:03 EthraZa

Hi @EthraZa, I'm running into an issue similar to yours, on an old on-premise Courier server. move() always fails; copy() apparently is randomly failing. Did you find any workaround?

Thanks

eleftrik avatar Feb 18 '22 10:02 eleftrik

Hi. Unfortunately no. I had to move away from this old server. But in the new ones it's not that better, because of other problems I can't move or delete messages, I just set them as read and let other proccess move it.

EthraZa avatar Feb 18 '22 12:02 EthraZa