示例#1
0
        private void ComboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (comboBox1.SelectedIndex == -1)
            {
                return;
            }
            if (comboBox1.SelectedItem.GetType() == typeof(IPhoneBackup))
            {
                LoadCurrentBackup();
                return;
            }
            OpenFileDialog fd = new OpenFileDialog
            {
                Filter           = "iPhone Backup|Info.plist|All files (*.*)|*.*",
                FilterIndex      = 1,
                RestoreDirectory = true
            };

            if (fd.ShowDialog() == DialogResult.OK)
            {
                BeforeLoadManifest();
                IPhoneBackup b = LoadManifest(Path.GetDirectoryName(fd.FileName));
                if (b != null)
                {
                    b.custom = true;
                    comboBox1.Items.Insert(comboBox1.Items.Count - 1, b);
                    comboBox1.SelectedIndex = comboBox1.Items.Count - 2;
                }
            }
        }
        void start_Click(object sender, EventArgs e)
        {
            IPhoneBackup  backup   = SelectedBackup;
            ImageAnalyzer analyzer = new ImageAnalyzer(backup);

            analyzer.Show();
        }
        /// <summary>
        /// Copy files to desktop..
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void fileList_ItemDrag(object sender, ItemDragEventArgs e)
        {
            IPhoneBackup      backup = Model.Backup;
            List <IPhoneFile> files  = new List <IPhoneFile>();

            foreach (ListViewItem itm in fileList.SelectedItems)
            {
                files.Add(itm.Tag as IPhoneFile);
            }

            string        path      = Path.GetTempPath();
            List <string> filenames = new List <string>();

            Model.InvokeAsync(files, delegate(IPhoneFile file)
            {
                string source = Path.Combine(backup.Path, file.Key);
                string dest   = Path.Combine(path, file.Path.Replace("/", Path.DirectorySeparatorChar.ToString()));

                // If there is a folder structure
                // create it.
                int lastIndex = file.Path.LastIndexOf("/");
                if (lastIndex >= 0)
                {
                    string fileFolder = file.Path.Substring(0, lastIndex);
                    Directory.CreateDirectory(Path.Combine(path, fileFolder.Replace("/", Path.DirectorySeparatorChar.ToString())));
                }

                // Copy the file (overwrite)
                File.Copy(source, dest, true);
                filenames.Add(dest);
            }, "Export...");
            fileList.DoDragDrop(new DataObject(DataFormats.FileDrop, filenames.ToArray()), DragDropEffects.Copy);
        }
        /// <summary>
        /// Select a backup path
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog from = new FolderBrowserDialog();

            from.Description  = "Select a iDevice backup to inspect";
            from.SelectedPath = IPhoneBackup.DefaultPath;

            DialogResult result = from.ShowDialog(this);

            if (result == DialogResult.OK)
            {
                IPhoneBackup[] backups = IPhoneBackup.Load(from.SelectedPath);

                if (backups.Length < 1)
                {
                    MessageBox.Show("No backups found!");
                }
                else
                {
                    changeBackupToolStripMenuItem.DropDownItems.Clear();
                    ToolStripItem[] items = PopulateBackupChangeList(backups);
                    changeBackupToolStripMenuItem.DropDownItems.AddRange(items);

                    SelectBackupForm form = new SelectBackupForm(backups.ToArray());
                    form.ShowDialog(this);
                    if (form.Selected != null)
                    {
                        SelectBackup(form.Selected);
                    }
                }
            }
        }
        /// <summary>
        /// Export some files
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void toolExportBtn_Click(object sender, EventArgs e)
        {
            IPhoneBackup      backup = Model.Backup;
            List <IPhoneFile> files  = new List <IPhoneFile>();

            foreach (ListViewItem itm in fileList.SelectedItems)
            {
                files.Add(itm.Tag as IPhoneFile);
            }

            FolderBrowserDialog dialog = new FolderBrowserDialog();
            DialogResult        result = dialog.ShowDialog(this);

            if (result == DialogResult.OK)
            {
                string path = dialog.SelectedPath;
                Model.InvokeAsync(files, delegate(IPhoneFile file)
                {
                    string source = Path.Combine(backup.Path, file.Key);
                    string dest   = Path.Combine(path, file.Path.Replace("/", Path.DirectorySeparatorChar.ToString()));

                    // If there is a folder structur
                    // create it.
                    int lastIndex = file.Path.LastIndexOf("/");
                    if (lastIndex >= 0)
                    {
                        string fileFolder = file.Path.Substring(0, lastIndex);
                        Directory.CreateDirectory(Path.Combine(path, fileFolder.Replace("/", Path.DirectorySeparatorChar.ToString())));
                    }

                    // Copy the file (overwrite)
                    File.Copy(source, dest, true);
                }, "Copying..", Cursors.WaitCursor);
            }
        }
示例#6
0
        /// <summary>
        /// This returns a random file in a temporary location with the file
        /// name as Tuple.
        ///
        /// Item1 = correct fileName
        /// Item2 = the fileInfo about the random file created
        /// </summary>
        /// <param name="backup"></param>
        /// <param name="file"></param>
        /// <returns></returns>
        public Tuple <string, FileInfo> GetRandomFile(IPhoneBackup backup, IPhoneFile file)
        {
            FileInfo fileInfo = GetWorkingFile(backup, file, true);
            string   name     = GetFileName(file);

            return(Tuple.Create(name, fileInfo));
        }
 private void OnSelectedBackup(IPhoneBackup backup)
 {
     if (SelectedBackup != null)
     {
         SelectedBackup(this, new IPhoneBackupSelectedArgs(backup));
     }
 }
        public MMSAnalyzer(IPhoneBackup backup, IPhoneFile file)
        {
            _backup = backup;
            _file   = file;

            InitializeComponent();
        }
示例#9
0
        void export_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog dialog = new FolderBrowserDialog();

            dialog.Description = "Select a place to export the application data";
            if (dialog.ShowDialog(Model.Window) == DialogResult.OK)
            {
                IPhoneApp    app    = SelectedApp;
                IPhoneBackup backup = SelectedBackup;
                string       path   = dialog.SelectedPath;
                Model.InvokeAsync(app.Files, delegate(IPhoneFile file)
                {
                    string source = Path.Combine(backup.Path, file.Key);
                    string dest   = Path.Combine(path, app.Name, file.Path.Replace("/", Path.DirectorySeparatorChar.ToString()));

                    // If there is a folder structur
                    // create it.
                    int lastIndex = file.Path.LastIndexOf("/");
                    if (lastIndex >= 0)
                    {
                        string fileFolder = file.Path.Substring(0, lastIndex);
                        Directory.CreateDirectory(Path.Combine(path, app.Name, fileFolder.Replace("/", Path.DirectorySeparatorChar.ToString())));
                    }

                    // Copy the file (overwrite)
                    File.Copy(source, dest, true);
                }, "Export: " + app.Name, Cursors.WaitCursor);
            }
        }
示例#10
0
        /// <summary>
        /// Get a working file in the directory Temp/${InvokingClassName}/filename
        ///
        /// This to avoid confilcts but a will to retain the name
        /// </summary>
        /// <param name="backup"></param>
        /// <param name="file"></param>
        /// <returns></returns>
        public FileInfo GetWorkingFileCurrentClass(IPhoneBackup backup, IPhoneFile file)
        {
            StackTrace trace = new StackTrace();
            StackFrame frame = trace.GetFrame(1);
            Type       klass = frame.GetMethod().ReflectedType;

            return(GetWorkingFile(string.Format("${0}", klass.Name), backup, file, false));
        }
        void show_Click(object sender, EventArgs e)
        {
            IPhoneBackup backup      = SelectedBackup;
            IPhoneApp    app         = backup.GetApps().FirstOrDefault(t => t.Name == "System");
            IPhoneFile   dbFile      = app.Files.FirstOrDefault(t => t.Path.Contains("sms.db"));
            MMSAnalyzer  mmsAnalyzer = new MMSAnalyzer(backup, dbFile);

            mmsAnalyzer.Show();
        }
        private void listBackups_SelectedValueChanged(object sender, EventArgs e)
        {
            IPhoneBackup backup = listBackups.SelectedItem as IPhoneBackup;

            if (backup != null)
            {
                lblDate.Text   = backup.LastBackupDate;
                lblDevice.Text = backup.DisplayName;
            }
        }
        public HashInfo(IPhoneBackup backup, IPhoneFile file)
        {
            InitializeComponent();

            FileInfo fileInfo = FileManager.Current.GetOriginalFile(backup, file);

            Text             = "Hash information [" + file.Path + "]";
            lblFileName.Text = file.Path;
            lblMd5.Text      = Util.MD5File(fileInfo);
            lblSha1.Text     = Util.SHA1File(fileInfo);
        }
示例#14
0
 /// <summary>
 ///
 /// </summary>
 /// <param name="backup"></param>
 /// <param name="file"></param>
 /// <param name="dest"></param>
 public void Copy(IPhoneBackup backup, IPhoneFile file, string dest)
 {
     lock (_lock)
     {
         string src = Path.Combine(backup.Path, file.Key);
         dest = Path.Combine(dest, GetFileName(file));
         if (src != dest)
         {
             File.Copy(src, dest, true);
         }
     }
 }
示例#15
0
        /// <summary>
        /// Return a file copy from a temp folder
        ///
        /// The path will be /Temp/$$idb$/{dir}/{fileName} (or random i.e. Path.GetTempFileName())
        ///
        /// if verify == true the file from source and the destination hashes will be compared
        /// if the comparsion fail. An IOExcetion is raised.
        /// </summary>
        /// <param name="backup"></param>
        /// <param name="file"></param>
        /// <param name="random"></param>
        /// <param name="dir"></param>
        /// <param name="verify">Verify the files after copy (using md5)</param>
        /// <exception cref="IOException">
        /// Thrown if file copy failed or that the copy cannot be verified
        /// </exception>
        /// <returns></returns>
        public FileInfo GetWorkingFile(string dir, IPhoneBackup backup, IPhoneFile file, bool random, bool verify = true)
        {
            lock (_lock)
            {
                if (!_init)
                {
                    Init();
                }

                string tempPath = Path.Combine(Path.GetTempPath(), BasePath, dir);
                if (!Directory.Exists(tempPath))
                {
                    Directory.CreateDirectory(tempPath);
                }

                FileInfo dest = new FileInfo(Path.GetTempFileName());
                if (!random)
                {
                    dest = new FileInfo(Path.Combine(tempPath, GetFileName(file)));
                }

                FileInfo src = GetOriginalFile(backup, file);
                string   srcHash = "", destHash = "";
                if (verify)
                {
                    srcHash = Util.MD5File(src);
                }

                // Dont copy if it exist..
                if (File.Exists(dest.FullName) && Util.MD5File(dest) == srcHash)
                {
                    return(dest);
                }

                // if we dent copy to the src
                if (src != dest)
                {
                    File.Copy(src.FullName, dest.FullName, true);
                }

                if (verify)
                {
                    destHash = Util.MD5File(dest);
                    if (srcHash != destHash)
                    {
                        Clean(dest.FullName);
                        throw new IOException("File copy failed. Reason: Hashes do not match!!");
                    }
                }
                return(dest);
            }
        }
        void hashes_Click(object sender, EventArgs e)
        {
            hashes.Enabled = false;
            IPhoneBackup backup = SelectedBackup;

            if (backup == null)
            {
                return;
            }
            IEnumerable <IPhoneApp> apps = backup.GetApps();

            if (apps == null)
            {
                return;
            }

            IEnumerable <IPhoneFile> files = apps.SelectMany(x => x.Files != null ? x.Files : new List <IPhoneFile>());

            StringBuilder builder = new StringBuilder();

            builder.AppendLine("Row,DateTime,MD5,SHA1,DisplayName,FileName");
            int row = 0;

            Model.InvokeAsync(files, delegate(IPhoneFile file) // run
            {
                FileInfo fileInfo = FileManager.GetOriginalFile(backup, file);
                string md5        = Util.MD5File(fileInfo);
                string sha1       = Util.SHA1File(fileInfo);

                builder.AppendLine(string.Format("{0},{1},{2},{3},{4},{5}",
                                                 row++,
                                                 DateTime.Now.ToUniversalTime(),
                                                 md5,
                                                 sha1,
                                                 file.Path,
                                                 fileInfo.FullName));
            }, delegate() // Called when completed
            {
                SaveFileDialog fileSaver = new SaveFileDialog();
                fileSaver.AddExtension   = true;
                fileSaver.DefaultExt     = ".csv";
                fileSaver.Filter         = "Comma separated file|*.csv";

                if (fileSaver.ShowDialog() == DialogResult.OK)
                {
                    File.WriteAllText(fileSaver.FileName, builder.ToString());
                }

                hashes.Enabled = true;
            }, "Analyzing hashes");
        }
示例#17
0
        public SMSAnalyzer(IPhoneBackup backup, string smsDbFilePath)
        {
            _smsDbFilePath                = smsDbFilePath;
            _backup                       = backup;
            _worker                       = new BackgroundWorker();
            _worker.DoWork               += new DoWorkEventHandler(_worker_DoWork);
            _worker.RunWorkerCompleted   += new RunWorkerCompletedEventHandler(_worker_RunWorkerCompleted);
            _worker.WorkerReportsProgress = true;

            InitializeComponent();

            if (_backup != null)
            {
                this.Text += " [" + _backup.DisplayName + "]";
            }
        }
        void start_Click(object sender, EventArgs e)
        {
            IPhoneBackup backup = SelectedBackup;

            if (backup == null)
            {
                return;
            }

            IPhoneApp  app      = backup.GetApps().FirstOrDefault(t => t.Name == "System");
            IPhoneFile dbFile   = app.Files.FirstOrDefault(t => t.Path.Contains("sms.db"));
            FileInfo   fileInfo = FileManager.GetWorkingFile(backup, dbFile, true);

            SMSAnalyzer analyzer = new SMSAnalyzer(backup, fileInfo.FullName);

            analyzer.Show();
        }
        public ImageAnalyzer(IPhoneBackup backup)
        {
            _backup                            = backup;
            _worker                            = new BackgroundWorker();
            _worker.DoWork                    += new DoWorkEventHandler(_worker_DoWork);
            _worker.RunWorkerCompleted        += new RunWorkerCompletedEventHandler(_worker_RunWorkerCompleted);
            _worker.ProgressChanged           += new ProgressChangedEventHandler(_worker_ProgressChanged);
            _worker.WorkerReportsProgress      = true;
            _worker.WorkerSupportsCancellation = true;

            this.FormClosing += new FormClosingEventHandler(ImageAnalyzer_FormClosing);
            InitializeComponent();

            if (_backup != null)
            {
                this.Text += " [" + _backup.DisplayName + "]";
            }
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            folderList.Columns.Add("Display Name", 200);
            folderList.Columns.Add("Files");

            fileList.Columns.Add("Name", 300);
            fileList.Columns.Add("Size");
            fileList.Columns.Add("Date", 130);
            fileList.Columns.Add("Domain", 300);
            fileList.Columns.Add("Key", 250);

            lvwColumnSorter             = new ListViewColumnSorter();
            fileList.ListViewItemSorter = lvwColumnSorter;

            IPhoneBackup[]  backups = IPhoneBackup.Load(IPhoneBackup.DefaultPath);
            ToolStripItem[] items   = PopulateBackupChangeList(backups);
            changeBackupToolStripMenuItem.DropDownItems.AddRange(items);
            SelectBackup(backups.FirstOrDefault());
        }
示例#21
0
        private IPhoneBackup LoadManifest(string path)
        {
            IPhoneBackup backup   = null;
            string       filename = Path.Combine(path, "Info.plist");

            try
            {
                xdict dd = xdict.open(filename);
                if (dd != null)
                {
                    backup = new IPhoneBackup
                    {
                        path = path
                    };
                    foreach (xdictpair p in dd)
                    {
                        if (p.item.GetType() == typeof(string))
                        {
                            switch (p.key)
                            {
                            case "Device Name": backup.DeviceName = (string)p.item; break;

                            case "Display Name": backup.DisplayName = (string)p.item; break;

                            case "Last Backup Date":
                                DateTime.TryParse((string)p.item, out backup.LastBackupDate);
                                break;
                            }
                        }
                    }
                }
            }
            catch (InvalidOperationException ex)
            {
                MessageBox.Show(ex.InnerException.ToString());
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
            return(backup);
        }
        public void SelectBackup(IPhoneBackup backup)
        {
            folderList.Items.Clear();
            fileList.Items.Clear();

            OnSelectedBackup(backup);

            UpdateTitle(backup.DisplayName);

            FileManager      fm   = FileManager.Current;
            List <IPhoneApp> apps = backup.GetApps();

            foreach (var app in apps)
            {
                ListViewItem lvi = new ListViewItem();
                lvi.Tag  = app;
                lvi.Text = app.Name;
                lvi.SubItems.Add(app.Files != null ? app.Files.Count.ToString() : "N/A");
                folderList.Items.Add(lvi);
            }
        }
        void _worker_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;
            IPhoneBackup     backup = e.Argument as IPhoneBackup;

            IPhoneApp system = backup.GetApps().FirstOrDefault(app => app.Name == "System");
            IEnumerable <IPhoneFile> images = system.Files.Where(file => file.Domain == "MediaDomain" && file.Path.Contains("DCIM/100APPLE"));
            FileManager fm = FileManager.Current;

            ImageList imgList = new ImageList();

            imgList.ImageSize  = new Size(64, 64);
            imgList.ColorDepth = ColorDepth.Depth32Bit;

            List <ListViewItem> listViewItems = new List <ListViewItem>();
            int length = images.Count(), current = 0;

            foreach (IPhoneFile file in images)
            {
                if (worker.CancellationPending)
                {
                    return;
                }
                FileInfo     info = fm.GetWorkingFileCurrentClass(_backup, file);
                ListViewItem itm  = new ListViewItem();
                itm.Tag      = new Bitmap(info.FullName);
                itm.Text     = info.Name;
                itm.ImageKey = info.Name;

                imgList.Images.Add(info.Name, (Bitmap)itm.Tag);
                listViewItems.Add(itm);

                worker.ReportProgress(Util.Percent(current++, length));
            }

            e.Result = new { ImageList = imgList, ListViewItems = listViewItems.ToArray() };
        }
        /// <summary>
        /// View a file using a browser
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void toolShowBtn_Click(object sender, EventArgs e)
        {
            IPhoneFile   file   = (IPhoneFile)fileList.FocusedItem.Tag;
            IPhoneBackup backup = Model.Backup;

            FileManager filemanager = FileManager.Current;
            FileInfo    dest        = filemanager.GetWorkingFile(backup, file);

            IBrowsable browser = _browserManger.Get(dest.Extension);

            try
            {
                if (browser != null)
                {
                    Form form = browser.Open();
                    if (form != null)
                    {
                        if (browser.Modal)
                        {
                            form.ShowDialog(this);
                        }
                        else
                        {
                            form.Show();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.DebugException(ex.Message, ex);
                MessageBox.Show(string.Format("'{0}' could not be opened by '{1}'"
                                              + "\n\n'{2}'"
                                              + "\nStacktrace\n{3}", dest.Name, browser.Name,
                                              ex.Message, ex.StackTrace), ex.Message, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
示例#25
0
        private void LoadManifests()
        {
            comboBox1.Items.Clear();
            string s = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);

            s = MyPath.Combine(s, "Apple Computer", "MobileSync", "Backup");
            try
            {
                DirectoryInfo d = new DirectoryInfo(s);
                int           numberOfBackups = 0;
                foreach (DirectoryInfo sd in d.GetDirectories())
                {
                    IPhoneBackup backup = LoadManifest(sd.FullName);
                    if (backup != null)
                    {
                        comboBox1.Items.Add(backup);
                        ++numberOfBackups;
                    }
                }

                if (numberOfBackups == 0)
                {
                    System.Threading.Timer timer = null;
                    timer = new System.Threading.Timer((obj) =>
                    {
                        MessageBox.Show("没有找到iTunes备份文件夹,可能需要手动选择。", "提示");
                        timer.Dispose();
                    }, null, 100, System.Threading.Timeout.Infinite);
                }
            }
            catch (Exception ex)
            {
                listBox1.Items.Add(ex.ToString());
            }
            comboBox1.Items.Add("<选择其他备份文件夹...>");
        }
示例#26
0
 /// <summary>
 /// Select an iphone backup in the gui
 /// </summary>
 /// <param name="backup"></param>
 public void Select(IPhoneBackup backup)
 {
     _browser.SelectBackup(backup);
 }
 public IPhoneBackupSelectedArgs(IPhoneBackup selected)
 {
     Selected = selected;
 }
示例#28
0
 /// <summary>
 ///
 /// </summary>
 /// <param name="backup"></param>
 /// <param name="file"></param>
 /// <param name="dest"></param>
 public void Copy(IPhoneBackup backup, IPhoneFile file, FileInfo dest)
 {
     Copy(backup, file, dest.FullName);
 }
示例#29
0
 /// <summary>
 ///
 /// </summary>
 /// <param name="backup"></param>
 /// <param name="file"></param>
 /// <param name="random"></param>
 /// <returns></returns>
 public FileInfo GetWorkingFile(IPhoneBackup backup, IPhoneFile file, bool random)
 {
     return(GetWorkingFile("", backup, file, random));
 }
        void _analyze_Click(object sender, EventArgs e)
        {
            IPhoneBackup             backup = SelectedBackup;
            IPhoneApp                app    = backup.GetApps().FirstOrDefault(t => t.Name == "System");
            IPhoneFile               dbFile = app.Files.FirstOrDefault(t => t.Path.Contains("consolidated"));
            IEnumerable <IPhoneFile> imgs   = app.Files.Where(t => t.Path.Contains("DCIM/100APPLE") && t.Domain == "MediaDomain");
            FileInfo fileInfo = FileManager.GetWorkingFile(backup, dbFile);

            Model.InvokeAsync(delegate(object w, DoWorkEventArgs a)
            {
                BackgroundWorker worker = w as BackgroundWorker;
                if (!worker.CancellationPending)
                {
                    List <Location> locations = new List <Location>();
                    using (SQLiteConnection con = new SQLiteConnection(@"Data Source=" + fileInfo.FullName))
                    {
                        con.Open();
                        var cmd         = new SQLiteCommand();
                        cmd.Connection  = con;
                        cmd.CommandText = "SELECT * FROM WifiLocation;";

                        var reader = cmd.ExecuteReader();
                        while (reader.Read())
                        {
                            locations.Add(new Location
                            {
                                Name      = "[Wifi] MAC: " + reader.GetString(0),
                                Time      = new DateTime(2001, 1, 1, 0, 0, 0).AddSeconds(reader.GetDouble(1)),
                                Latitude  = reader.GetDouble(2),
                                Longitude = reader.GetDouble(3)
                            });
                        }
                        reader.Close();

                        worker.ReportProgress(33);

                        cmd.CommandText = "SELECT * FROM CellLocation";
                        reader          = cmd.ExecuteReader();
                        while (reader.Read())
                        {
                            locations.Add(new Location
                            {
                                Name = "[Cell] MCC: " + reader.GetInt32(0) + " MNC: " +
                                       reader.GetInt32(1) + " LAC: " +
                                       reader.GetInt32(2) + " CELL ID:" +
                                       reader.GetInt32(3),
                                Time      = new DateTime(2001, 1, 1, 0, 0, 0).AddSeconds(reader.GetDouble(4)),
                                Latitude  = reader.GetDouble(5),
                                Longitude = reader.GetDouble(6)
                            });
                        }

                        worker.ReportProgress(33);
                    }

                    //Gives incorrect results.. FIXIT
                    //int count = imgs.Count() + 66, current = 66;
                    //foreach (IPhoneFile file in imgs)
                    //{
                    //    FileInfo info = FileManager.GetWorkingFile(backup, file);
                    //    ExifTagCollection er = new ExifTagCollection(info.FullName);

                    //    //not working...
                    //    Location location = new Location();
                    //    double lat = 0, lon = 0;
                    //    foreach (ExifTag tag in er)
                    //    {
                    //        if (tag.FieldName == "GPSLatitude")
                    //        {
                    //            string dec = Regex.Match(tag.Value, @"\d+\.\d+").ToString();
                    //            double.TryParse(dec, out lat);
                    //        }
                    //        else if (tag.FieldName == "GPSLongitude")
                    //        {
                    //            string dec = Regex.Match(tag.Value, @"\d+\.\d+").ToString();
                    //            double.TryParse(dec, out lon);
                    //        }
                    //    }
                    //    location.Latitude = lat;
                    //    location.Longitude = lon;
                    //    location.Name = "[Image] " + info.Name;
                    //    locations.Add(location);

                    //    worker.ReportProgress(Util.Percent(current++, count));
                    //}

                    geKML kml = Location.ToKML(locations.OrderBy(x => x.Time));
                    a.Result  = kml;
                }
                else
                {
                    a.Cancel = true;
                    return;
                }
            },
                              delegate(object w, RunWorkerCompletedEventArgs a)
            {
                geKML kml = a.Result as geKML;

                FileDialog dialog = new SaveFileDialog();
                dialog.DefaultExt = "kmz";
                dialog.FileName   = "locations.kmz";
                dialog.Filter     = "Google earth|*.kmz";
                if (dialog.ShowDialog(Model.Window) == DialogResult.OK)
                {
                    using (FileStream s = new FileStream(dialog.FileName, FileMode.Create))
                    {
                        using (BinaryWriter wr = new BinaryWriter(s))
                        {
                            wr.Write(kml.ToKMZ());
                        }
                    }
                }
            }, "Analyzing location", false);
        }