Repository: python-pillow/Pillow

Problem statement:
MPO seek error
### What did you do?
I perform an `Image.seek` operation.

### What did you expect to happen?
I expect a successful function invocation.

### What actually happened?
I get an error:

```
~/.local/share/virtualenvs/muffinchihuahua-VbII3yUl/lib/python3.7/site-packages/PIL/MpoImagePlugin.py in seek(self, frame)
     89         if "parsed_exif" in self.info:
     90             del self.info["parsed_exif"]
---> 91         if i16(self.fp.read(2)) == 0xFFE1:  # APP1
     92             n = i16(self.fp.read(2)) - 2
     93             self.info["exif"] = ImageFile._safe_read(self.fp, n)

~/.local/share/virtualenvs/muffinchihuahua-VbII3yUl/lib/python3.7/site-packages/PIL/_binary.py in i16be(c, o)
     75
     76 def i16be(c, o=0):
---> 77     return unpack_from(">H", c, o)[0]
     78
     79

error: unpack_from requires a buffer of at least 2 bytes
```

### What are your OS, Python and Pillow versions?

* OS: macOs Mojave 10.14.6
* Python: 3.7
* Pillow: 6.1.0

```python
import urllib.request
from PIL import Image

urllib.request.urlretrieve('https://srv-file7.gofile.io/download/X30JBL/137.DSC_0733-3-blueberry-muffins-.jpg', 'muffin.jpg')

im=Image.open('muffin.jpg')
im.seek(0)
im.seek(1)
```



Hints:
If I navigate to https://srv-file7.gofile.io/download/X30JBL/137.DSC_0733-3-blueberry-muffins-.jpg in my browser, I get 'You are not authorized to download this file.'
Hmm that's bizarre @radarhere. My browser (Chrome) just downloads the file. Same for Python if I run the above.
Could you just attach the file in a GitHub comment?
Yup, right here @radarhere 

![muffin](https://user-images.githubusercontent.com/1505227/65825093-81281380-e227-11e9-8d6a-0ee326d766db.jpg)

The updated snippet using the above GitHub attachment:

```python
import urllib.request
from PIL import Image

urllib.request.urlretrieve('https://user-images.githubusercontent.com/1505227/65825093-81281380-e227-11e9-8d6a-0ee326d766db.jpg', 'muffin.jpg')

im=Image.open('muffin.jpg')
im.seek(0)
im.seek(1)

```
Investigating, the offsets for the three images within the file are 0, 7353813 and 7386069. However, the file only has a size of 125510.

So my simple theory is that this image is just truncated - it should be at least 7mb, but it's only 123kb. We could add a clearer error message, but I'm not seeing a way to get the image data.

Feel free to prove me wrong by demonstrating a way to extract the image using another program, ideally exiftool.

Patch:
diff --git a/src/PIL/MpoImagePlugin.py b/src/PIL/MpoImagePlugin.py
--- a/src/PIL/MpoImagePlugin.py
+++ b/src/PIL/MpoImagePlugin.py
@@ -82,7 +82,10 @@ def seek(self, frame):
         self.offset = self.__mpoffsets[frame]
 
         self.fp.seek(self.offset + 2)  # skip SOI marker
-        if i16(self.fp.read(2)) == 0xFFE1:  # APP1
+        segment = self.fp.read(2)
+        if not segment:
+            raise ValueError("No data found for frame")
+        if i16(segment) == 0xFFE1:  # APP1
             n = i16(self.fp.read(2)) - 2
             self.info["exif"] = ImageFile._safe_read(self.fp, n)
 


Test patch:
diff --git a/Tests/images/sugarshack_no_data.mpo b/Tests/images/sugarshack_no_data.mpo
new file mode 100644
Binary files /dev/null and b/Tests/images/sugarshack_no_data.mpo differ
diff --git a/Tests/test_file_mpo.py b/Tests/test_file_mpo.py
--- a/Tests/test_file_mpo.py
+++ b/Tests/test_file_mpo.py
@@ -113,6 +113,13 @@ def test_mp_offset(self):
             self.assertEqual(mpinfo[45056], b"0100")
             self.assertEqual(mpinfo[45057], 2)
 
+    def test_mp_no_data(self):
+        # This image has been manually hexedited to have the second frame
+        # beyond the end of the file
+        with Image.open("Tests/images/sugarshack_no_data.mpo") as im:
+            with self.assertRaises(ValueError):
+                im.seek(1)
+
     def test_mp_attribute(self):
         for test_file in test_files:
             with Image.open(test_file) as im: