| @@ -151,8 +151,35 @@ | ||
| 151 | 151 | |
| 152 | 152 | return $headers; |
| 153 | 153 | } |
| 154 | 154 | |
| 155 | + /** | |
| 156 | + * Picks the requested message in a fetch result. | |
| 157 | + * The result is keyed by sequence number, and it may also contain unsolicited FETCH responses | |
| 158 | + * the server sent for other messages (flag updates for instance), so we can't simply take the first one. | |
| 159 | + * | |
| 160 | + * @return object|null | |
| 161 | + */ | |
| 162 | + private static function findMessage($messages, $messageNum, bool $isUid) | |
| 163 | + { | |
| 164 | + if (empty($messages) || !is_array($messages)) { | |
| 165 | + return null; | |
| 166 | + } | |
| 167 | + | |
| 168 | + if (!$isUid) { | |
| 169 | + return $messages[$messageNum] ?? null; | |
| 170 | + } | |
| 171 | + | |
| 172 | + // On a UID FETCH the server has to include the UID in its response, that's the only reliable way to identify the message | |
| 173 | + foreach ($messages as $message) { | |
| 174 | + if (isset($message->uid) && intval($message->uid) === intval($messageNum)) { | |
| 175 | + return $message; | |
| 176 | + } | |
| 177 | + } | |
| 178 | + | |
| 179 | + return null; | |
| 180 | + } | |
| 181 | + | |
| 155 | 182 | public static function body($imap, $messageNum, $flags = 0) |
| 156 | 183 | { |
| 157 | 184 | if (!is_a($imap, Connection::class)) { |
| 158 | 185 | return Errors::invalidImapConnection(debug_backtrace(), 1, false); |
| @@ -164,9 +191,15 @@ | ||
| 164 | 191 | $isUid = boolval($flags & FT_UID); |
| 165 | 192 | |
| 166 | 193 | $messages = $client->fetch($imap->getMailboxName(), $messageNum, $isUid, ['BODY[TEXT]']); |
| 167 | 194 | |
| 168 | - return $messages[$messageNum]->bodypart['TEXT']; | |
| 195 | + $message = self::findMessage($messages, $messageNum, $isUid); | |
| 196 | + | |
| 197 | + if ($message === null) { | |
| 198 | + return false; | |
| 199 | + } | |
| 200 | + | |
| 201 | + return $message->bodypart['TEXT'] ?? false; | |
| 169 | 202 | } |
| 170 | 203 | |
| 171 | 204 | public static function fetchBody($imap, $messageNum, $section, $flags = 0) |
| 172 | 205 | { |
| @@ -176,22 +209,29 @@ | ||
| 176 | 209 | |
| 177 | 210 | $client = $imap->getClient(); |
| 178 | 211 | #$client->setDebug(true); |
| 179 | 212 | |
| 213 | + if ($messageNum <= 0 || !is_int($messageNum)) { | |
| 214 | + trigger_error(Errors::badMessageNumber(debug_backtrace(), 1), E_USER_WARNING); | |
| 215 | + | |
| 216 | + return false; | |
| 217 | + } | |
| 218 | + | |
| 180 | 219 | $isUid = boolval($flags & FT_UID); |
| 181 | 220 | $messages = $client->fetch($imap->getMailboxName(), $messageNum, $isUid, ['BODY['.$section.']']); |
| 182 | 221 | |
| 183 | - if (empty($messages)) { | |
| 184 | - trigger_error(Errors::badMessageNumber(debug_backtrace(), 1), E_USER_WARNING); | |
| 222 | + $message = self::findMessage($messages, $messageNum, $isUid); | |
| 185 | 223 | |
| 224 | + // The message may have been expunged, the section may not exist, or the connection may have dropped | |
| 225 | + if ($message === null) { | |
| 186 | 226 | return false; |
| 187 | 227 | } |
| 188 | 228 | |
| 189 | 229 | if ($section) { |
| 190 | - return $messages[$messageNum]->bodypart[$section]; | |
| 230 | + return $message->bodypart[$section] ?? false; | |
| 191 | 231 | } |
| 192 | 232 | |
| 193 | - return $messages[$messageNum]->body; | |
| 233 | + return $message->body ?? false; | |
| 194 | 234 | } |
| 195 | 235 | |
| 196 | 236 | public static function fetchMime($imap, $messageNum, $section, $flags = 0) |
| 197 | 237 | { |
| @@ -212,17 +252,19 @@ | ||
| 212 | 252 | |
| 213 | 253 | $sectionKey = $section.'.MIME'; |
| 214 | 254 | $messages = $client->fetch($imap->getMailboxName(), $messageNum, $isUid, ['BODY['.$sectionKey.']']); |
| 215 | 255 | |
| 216 | - if (empty($messages)) { | |
| 217 | - return ""; | |
| 256 | + $message = self::findMessage($messages, $messageNum, $isUid); | |
| 257 | + | |
| 258 | + if ($message === null) { | |
| 259 | + return ''; | |
| 218 | 260 | } |
| 219 | 261 | |
| 220 | - if ($section && isset($messages[$messageNum]->bodypart[$sectionKey])) { | |
| 221 | - return $messages[$messageNum]->bodypart[$sectionKey]; | |
| 262 | + if ($section && isset($message->bodypart[$sectionKey])) { | |
| 263 | + return $message->bodypart[$sectionKey]; | |
| 222 | 264 | } |
| 223 | 265 | |
| 224 | - return $messages[$messageNum]->body; | |
| 266 | + return $message->body ?? ''; | |
| 225 | 267 | } |
| 226 | 268 | |
| 227 | 269 | public static function saveBody($imap, $file, $messageNum, $section = "", $flags = 0) |
| 228 | 270 | { |
| @@ -231,9 +273,15 @@ | ||
| 231 | 273 | #$client->setDebug(true); |
| 232 | 274 | |
| 233 | 275 | $messages = $client->fetch($imap->getMailboxName(), $messageNum, false, ['BODY['.$section.']']); |
| 234 | 276 | |
| 235 | - $body = $section ? $messages[$messageNum]->bodypart[$section] : $messages[$messageNum]->body; | |
| 277 | + $message = self::findMessage($messages, $messageNum, false); | |
| 278 | + | |
| 279 | + if ($message === null) { | |
| 280 | + return false; | |
| 281 | + } | |
| 282 | + | |
| 283 | + $body = $section ? ($message->bodypart[$section] ?? '') : ($message->body ?? ''); | |
| 236 | 284 | |
| 237 | 285 | return file_put_contents($file, $body); |
| 238 | 286 | } |
| 239 | 287 | |