Index: libs/libmythtv/cardutil.h
===================================================================
--- libs/libmythtv/cardutil.h	(revision 10403)
+++ libs/libmythtv/cardutil.h	(working copy)
@@ -84,6 +84,7 @@
         FIREWIRE,
         HDHOMERUN,
         CRC_IP,
+        FREEBOX,
     };
 
     static enum CARD_TYPES toCardType(const QString &name)
@@ -114,6 +115,8 @@
             return HDHOMERUN;
         if ("CRC_IP" == name)
             return CRC_IP;
+        if ("FREEBOX" == name)
+            return FREEBOX;
         return ERROR_UNKNOWN;
     }
 
@@ -134,7 +137,7 @@
     static bool         IsCardTypePresent(const QString &strType);
 
     static QString      GetRawCardType(uint cardid, uint sourceid)
-        { return get_on_source("cardtype", cardid, sourceid); }
+        { return get_on_source("cardtype", cardid, sourceid).upper(); }
     static QString      GetVideoDevice(uint cardid, uint sourceid)
         { return get_on_source("videodevice", cardid, sourceid); }
     static bool         GetVBIDevice(uint cardid, uint sourceid)
@@ -145,7 +148,7 @@
         { return get_on_source("dbox2_port", cardid, sourceid).toUInt(); }
 
     static QString      GetRawCardType(uint cardid, const QString &input)
-        { return get_on_input("cardtype", cardid, input); }
+        { return get_on_input("cardtype", cardid, input).upper(); }
     static QString      GetVideoDevice(uint cardid, const QString &input)
         { return get_on_input("videodevice", cardid, input); }
     static QString      GetVBIDevice(uint cardid, const QString &input)
Index: libs/libmythtv/freeboxchannelfetcher.h
===================================================================
--- libs/libmythtv/freeboxchannelfetcher.h	(revision 10403)
+++ libs/libmythtv/freeboxchannelfetcher.h	(working copy)
@@ -1,19 +1,63 @@
 #ifndef _FREEBOXCHANNELFETCHER_H_
 #define _FREEBOXCHANNELFETCHER_H_
 
+// POSIX headers
+#include <pthread.h>
+
+// Qt headers
+#include <qobject.h>
+#include <qmutex.h>
+
+// MythTV headers
 #include "freeboxchannelinfo.h"
 
-class FreeboxChannelFetcher
+class FreeboxChannelFetcher : public QObject
 {
-  private:
-    FreeboxChannelFetcher();
+    Q_OBJECT
+
+    friend void *run_scan_thunk(void *param);
+
+  public:
+    FreeboxChannelFetcher(unsigned _sourceid, unsigned _cardid);
     ~FreeboxChannelFetcher();
 
-  public:
-    static QString DownloadPlaylist(const QString& url);
-    static fbox_chan_map_t ParsePlaylist(const QString& rawdata);
+    /// Stops the scanning thread running
+    void Stop(void);
+    /// Scans the given frequency list, implies starting the thread()
+    bool Scan(void);
+
+    static QString DownloadPlaylist(const QString &url, bool inQtThread);
+    static fbox_chan_map_t ParsePlaylist(
+        const QString &rawdata, FreeboxChannelFetcher *fetcher = NULL);
+
+  signals:
+    /** \brief Tells listener how far along we are from 0..100%
+     *  \param p percentage completion
+     */
+    void ServiceScanPercentComplete(int p);
+    /** \brief Status message from the scanner
+     *  \param status The latest status message
+     */ 
+    void ServiceScanUpdateText(const QString &status);
+    /// Signals that the scan is complete
+    void ServiceScanComplete(void);
+
+  private:
+    void SetTotalNumChannels(uint val) { chan_cnt = (val) ? val : 1; }
+    void SetNumChannelsParsed(uint);
+    void SetNumChannelsInserted(uint);
+    void SetMessage(const QString &status);
+    void RunScan(void);
+
+    uint      sourceid;
+    uint      cardid;
+    uint      chan_cnt;
+    bool      thread_running;
+    bool      stop_now;
+    pthread_t thread;
+    QMutex    lock;
 };
 
-#endif//_FREEBOXCHANNELFETCHER_H_
+#endif //_FREEBOXCHANNELFETCHER_H_
 
 /* vim: set expandtab tabstop=4 shiftwidth=4: */
Index: libs/libmythtv/freeboxchannel.cpp
===================================================================
--- libs/libmythtv/freeboxchannel.cpp	(revision 10403)
+++ libs/libmythtv/freeboxchannel.cpp	(working copy)
@@ -34,7 +34,8 @@
     
     if (m_freeboxchannels.empty())
     {
-        QString content = FreeboxChannelFetcher::DownloadPlaylist(m_videodev);
+        QString content = FreeboxChannelFetcher::DownloadPlaylist(
+            m_videodev, true);
         m_freeboxchannels = FreeboxChannelFetcher::ParsePlaylist(content);
         VERBOSE(VB_IMPORTANT, LOC + QString("Loaded %1 channels from %2")
             .arg(m_freeboxchannels.size())
Index: libs/libmythtv/scanwizardhelpers.h
===================================================================
--- libs/libmythtv/scanwizardhelpers.h	(revision 10403)
+++ libs/libmythtv/scanwizardhelpers.h	(working copy)
@@ -219,6 +219,8 @@
         FullTransportScan,
         // Scan of one transport already in the database
         TransportScan,
+        // Freebox import of channels from M3U URL
+        FreeBoxImport,
         // Imports lists from dvb-utils scanners
         Import
     };
Index: libs/libmythtv/scanwizardscanner.cpp
===================================================================
--- libs/libmythtv/scanwizardscanner.cpp	(revision 10403)
+++ libs/libmythtv/scanwizardscanner.cpp	(working copy)
@@ -61,6 +61,8 @@
 #include "hdhrsignalmonitor.h"
 #endif
 
+#include "freeboxchannelfetcher.h"
+
 #define LOC QString("SWizScan: ")
 #define LOC_ERR QString("SWizScan, Error: ")
 
@@ -103,6 +105,7 @@
       log(new LogList()),
       channel(NULL),                popupProgress(NULL),
       scanner(NULL),                analogScanner(NULL),
+      freeboxScanner(NULL),
       nScanType(-1),
       nMultiplexToTuneTo(0),        nVideoSource(0),
       frequency(0),                 modulation("8vsb")
@@ -134,6 +137,15 @@
         analogScanner = NULL;
     }
 #endif
+
+#ifdef USING_FREEBOX
+    if (freeboxScanner)
+    {
+        freeboxScanner->Stop();
+        delete freeboxScanner;
+        freeboxScanner = NULL;
+    }
+#endif
 }
 
 void ScanWizardScanner::customEvent(QCustomEvent *e)
@@ -410,6 +422,11 @@
             startChan["modulation"]);
 #endif // USING_DVB
     }
+    else if (nScanType == ScanTypeSetting::FreeBoxImport)
+    {
+        do_scan = false;
+        ScanFreeBox(cardid, nVideoSource);
+    }
     else
     {
         do_scan = false;
@@ -630,6 +647,33 @@
 #endif
 }
 
+void ScanWizardScanner::ScanFreeBox(uint cardid, uint sourceid)
+{
+#ifdef USING_FREEBOX
+    //Create an analog scan object
+    freeboxScanner = new FreeboxChannelFetcher(sourceid, cardid);
+    popupProgress  = new ScanProgressPopup(this, false);
+
+    connect(freeboxScanner, SIGNAL(ServiceScanComplete(void)),
+            this,           SLOT(  scanComplete(void)));
+    connect(freeboxScanner, SIGNAL(ServiceScanUpdateText(const QString&)),
+            this,           SLOT(  updateText(const QString&)));
+    connect(freeboxScanner, SIGNAL(ServiceScanPercentComplete(int)),
+            this,           SLOT(  serviceScanPctComplete(int)));
+
+    popupProgress->progress(0);
+    popupProgress->exec(this);
+
+    if (!freeboxScanner->Scan())
+    {
+        MythPopupBox::showOkPopup(gContext->GetMainWindow(),
+                                  tr("ScanWizard"),
+                                  tr("Error starting scan"));
+        cancelScan();
+    }
+#endif // USING_FREEBOX
+}
+
 void ScanWizardScanner::HandleTuneComplete(void)
 {
     VERBOSE(VB_SIPARSER, LOC + "HandleTuneComplete()");
Index: libs/libmythtv/freeboxchannelfetcher.cpp
===================================================================
--- libs/libmythtv/freeboxchannelfetcher.cpp	(revision 10403)
+++ libs/libmythtv/freeboxchannelfetcher.cpp	(working copy)
@@ -1,17 +1,257 @@
+// -*- Mode: c++ -*-
+
+// Std C headers
+#include <cmath>
+
+// MythTV headers
+#include "mythcontext.h"
+#include "httpcomms.h"
+#include "cardutil.h"
+#include "channelutil.h"
 #include "freeboxchannelfetcher.h"
 
-#include "libmyth/httpcomms.h"
-#include "libmyth/mythcontext.h"
-
 #define LOC QString("FBChanFetch: ")
 #define LOC_ERR QString("FBChanFetch, Error: ")
 
-QString FreeboxChannelFetcher::DownloadPlaylist(const QString& url)
+static bool parse_chan_info(
+    const QString &line1, QString &channum, QString &name);
+
+FreeboxChannelFetcher::FreeboxChannelFetcher(unsigned _sourceid,
+                                             unsigned _cardid) :
+    sourceid(_sourceid),    cardid(_cardid),
+    chan_cnt(1),            thread_running(false),
+    stop_now(false),        lock(false)
 {
-    QString rwUrl(url);
-    return QString::fromUtf8(HttpComms::getHttp(rwUrl));
 }
 
+FreeboxChannelFetcher::~FreeboxChannelFetcher()
+{
+    do
+    {
+        Stop();
+        usleep(5000);
+    }
+    while (thread_running);
+}
+
+void FreeboxChannelFetcher::Stop(void)
+{
+    lock.lock();
+
+    if (thread_running)
+    {
+        stop_now = true;
+        lock.unlock();
+
+        pthread_join(thread, NULL);
+        return;
+    }
+
+    lock.unlock();
+}
+
+bool FreeboxChannelFetcher::Scan(void)
+{
+    lock.lock();
+    do { lock.unlock(); Stop(); lock.lock(); } while (thread_running);
+
+    // Should now have lock and no thread should be running.
+
+    stop_now = false;
+
+    pthread_create(&thread, NULL, run_scan_thunk, this);
+
+    while (!thread_running && !stop_now)
+        usleep(5000);
+
+    lock.unlock();
+
+    return thread_running;
+}
+
+void *run_scan_thunk(void *param)
+{
+    FreeboxChannelFetcher *chanscan = (FreeboxChannelFetcher*) param;
+    chanscan->RunScan();
+
+    return NULL;
+}
+
+void FreeboxChannelFetcher::RunScan(void)
+{
+    thread_running = true;
+
+    // Step 1/4 : Get info from DB
+    QString url = CardUtil::GetVideoDevice(cardid, sourceid);
+
+    if (stop_now || url.isEmpty())
+    {
+        thread_running = false;
+        return;
+    }
+
+    VERBOSE(VB_CHANNEL, QString("Playlist URL: %1").arg(url));
+
+    // Step 2/4 : Download
+    emit ServiceScanPercentComplete(5);
+    emit ServiceScanUpdateText(tr("Downloading Playlist"));
+
+    QString playlist = DownloadPlaylist(url, false);
+
+    if (stop_now || playlist.isEmpty())
+    {
+        thread_running = false;
+        return;
+    }
+
+    // Step 3/4 : Process
+    emit ServiceScanPercentComplete(35);
+    emit ServiceScanUpdateText(tr("Processing Playlist"));
+
+    const fbox_chan_map_t channels = ParsePlaylist(playlist, this);
+
+    // Step 4/4 : Finish up
+    emit ServiceScanUpdateText(tr("Adding Channels"));
+    SetTotalNumChannels(channels.size());
+    fbox_chan_map_t::const_iterator it = channels.begin();    
+    for (uint i = 1; it != channels.end(); ++it, ++i)
+    {
+        QString channum = it.key();
+        QString name    = (*it).m_name;
+        QString msg     = tr("Channel #%1 : %2").arg(channum).arg(name);
+
+        int chanid = ChannelUtil::GetChanID(sourceid, channum);
+        if (chanid <= 0)
+        { 
+            emit ServiceScanUpdateText(tr("Adding %1").arg(msg));
+            chanid = ChannelUtil::CreateChanID(sourceid, channum);
+            ChannelUtil::CreateChannel(
+                0, sourceid, chanid, name, name, channum,
+                0, 0, 0, false, false, false, 0);
+        }
+        else
+        {
+            emit ServiceScanUpdateText(tr("Updating %1").arg(msg));
+            ChannelUtil::UpdateChannel(
+                0, sourceid, chanid, name, name, channum, 0, 0, 0, 0);
+        }
+
+        SetNumChannelsInserted(i);
+    }
+
+    emit ServiceScanUpdateText(tr("Done"));
+    emit ServiceScanUpdateText("");
+    emit ServiceScanPercentComplete(100);
+    emit ServiceScanComplete();
+
+    thread_running = false;
+}
+
+void FreeboxChannelFetcher::SetNumChannelsParsed(uint val)
+{
+    uint minval = 35, range = 70 - minval;
+    uint pct = minval + (uint) truncf((((float)val) / chan_cnt) * range);
+    emit ServiceScanPercentComplete(pct);
+}
+
+void FreeboxChannelFetcher::SetNumChannelsInserted(uint val)
+{
+    uint minval = 70, range = 100 - minval;
+    uint pct = minval + (uint) truncf((((float)val) / chan_cnt) * range);
+    emit ServiceScanPercentComplete(pct);
+}
+
+void FreeboxChannelFetcher::SetMessage(const QString &status)
+{
+    emit ServiceScanUpdateText(status);
+}
+
+QString FreeboxChannelFetcher::DownloadPlaylist(const QString &url,
+                                                bool inQtThread)
+{
+    QString redirected_url = url;
+
+    QString tmp = HttpComms::getHttp(
+        redirected_url,
+        10000 /* ms        */, 3     /* retries      */,
+        3     /* redirects */, true  /* allow gzip   */,
+        NULL  /* login     */, inQtThread);
+
+    if (redirected_url != url)
+    {
+        VERBOSE(VB_CHANNEL, QString("Channel URL redirected to %1")
+                .arg(redirected_url));
+    }
+
+    return QString::fromUtf8(tmp);
+}
+
+fbox_chan_map_t FreeboxChannelFetcher::ParsePlaylist(
+    const QString &rawdata, FreeboxChannelFetcher *fetcher)
+{
+    fbox_chan_map_t chanmap;
+
+    // Verify header is ok
+    QString header = rawdata.section("\n", 0, 0);
+    if (header != "#EXTM3U")
+    {
+        VERBOSE(VB_IMPORTANT, LOC_ERR +
+                QString("Invalid channel list header (%1)").arg(header));
+
+        if (fetcher)
+            fetcher->SetMessage(tr("ERROR: M3U channel list is malformed"));
+
+        return chanmap;
+    }
+
+    // estimate number of channels
+    for (uint i = 1; fetcher; i += 2)
+    {
+        QString url = rawdata.section("\n", i+1, i+1);
+        if (url.isEmpty())
+        {
+            fetcher->SetTotalNumChannels(i>>1);
+
+            VERBOSE(VB_CHANNEL, "Estimating there are "<<(i>>1)
+                    <<" channels in playlist");
+
+            break;
+        }
+    }
+
+    // Parse each channel
+    for (int i = 1; true; i += 2)
+    {
+        QString tmp = rawdata.section("\n", i+0, i+0);
+        QString url = rawdata.section("\n", i+1, i+1);
+        if (tmp.isEmpty() || url.isEmpty())
+            break;
+
+        QString channum, name;
+        QString msg = tr("Encountered malformed channel");
+        if (parse_chan_info(tmp, channum, name))
+        {
+            chanmap[channum] = FreeboxChannelInfo(name, url);
+
+            msg = tr("Parsing Channel #%1 : %2 : %3")
+                .arg(channum).arg(name).arg(url);
+
+            VERBOSE(VB_CHANNEL, msg);
+
+            msg = QString::null; // don't tell fetcher
+        }
+
+        if (fetcher)
+        {
+            if (!msg.isEmpty())
+                fetcher->SetMessage(msg);
+            fetcher->SetNumChannelsParsed(1+(i>>1));
+        }
+    }
+
+    return chanmap;
+}
+
 static bool parse_chan_info(const QString &line1,
                             QString &channum, QString &name)
 {
@@ -63,36 +303,4 @@
     return true;
 }
 
-fbox_chan_map_t FreeboxChannelFetcher::ParsePlaylist(const QString& rawdata)
-{
-    fbox_chan_map_t chanmap;
-
-    // Verify header is ok
-    QString header = rawdata.section("\n", 0, 0);
-    if (header != "#EXTM3U")
-    {
-        VERBOSE(VB_IMPORTANT, LOC_ERR +
-                QString("Invalid channel list header (%1)").arg(header));
-
-        return chanmap;
-    }
-
-    // Parse each channel
-    for (int i = 1; true; i += 2)
-    {
-        QString tmp = rawdata.section("\n", i+0, i+0);
-        QString url = rawdata.section("\n", i+1, i+1);
-        if (tmp.isEmpty() || url.isEmpty())
-            break;
-
-        QString channum, name;
-        if (parse_chan_info(tmp, channum, name))
-        {
-            chanmap[channum] = FreeboxChannelInfo(name, url);
-        }
-    }
-
-    return chanmap;
-}
-
 /* vim: set expandtab tabstop=4 shiftwidth=4: */
Index: libs/libmythtv/scanwizardscanner.h
===================================================================
--- libs/libmythtv/scanwizardscanner.h	(revision 10403)
+++ libs/libmythtv/scanwizardscanner.h	(working copy)
@@ -43,6 +43,7 @@
 
 class ScanWizard;
 class AnalogScan;
+class FreeboxChannelFetcher;
 class LogList;
 class SIScan;
 class ScanProgressPopup;
@@ -82,6 +83,7 @@
     void PreScanCommon(uint cardid, uint sourceid);
     void TunedScanCommon(uint cardid, uint sourceid, bool ok);
     void ScanAnalog(uint cardid, uint sourceid);
+    void ScanFreeBox(uint cardid, uint sourceid);
 
     void dvbLock(int);
     void dvbSNR(int);
@@ -101,6 +103,7 @@
 
     SIScan            *scanner;
     AnalogScan        *analogScanner;
+    FreeboxChannelFetcher *freeboxScanner;
 
     int                nScanType;
     int                nMultiplexToTuneTo;
Index: libs/libmythtv/scanwizardhelpers.cpp
===================================================================
--- libs/libmythtv/scanwizardhelpers.cpp	(revision 10403)
+++ libs/libmythtv/scanwizardhelpers.cpp	(working copy)
@@ -58,6 +58,12 @@
 # endif // USING_IVTV
 #endif // USING_V4L
 
+#ifdef USING_FREEBOX
+    if (!cardTypes.isEmpty())
+        cardTypes += ",";
+    cardTypes += "'FREEBOX'";
+#endif // USING_FREEBOX
+
 #ifdef USING_HDHOMERUN
     if (!cardTypes.isEmpty())
         cardTypes += ",";
@@ -335,6 +341,10 @@
 #endif
 */
         break;
+    case CardUtil::FREEBOX:
+        addSelection(tr("M3U Import"),
+                     QString::number(FreeBoxImport), true);
+        return;
     case CardUtil::ERROR_PROBE:
         addSelection(QObject::tr("Failed to probe the card"),
                      QString::number(Error_Probe), true);
@@ -422,6 +432,8 @@
               wizard->paneSingle);
     addTarget(QString::number(ScanTypeSetting::FullTransportScan),
               scanAllTransports);
+    addTarget(QString::number(ScanTypeSetting::FreeBoxImport),
+              new BlankSetting());
     addTarget(QString::number(ScanTypeSetting::Import),
               filename);
 }
Index: libs/libmyth/httpcomms.cpp
===================================================================
--- libs/libmyth/httpcomms.cpp	(revision 10403)
+++ libs/libmyth/httpcomms.cpp	(working copy)
@@ -141,10 +141,10 @@
     
     VERBOSE(VB_NETWORK, QString("done: %1 bytes").arg(m_data.size()));
 
-    m_done = true;
-
     if (m_timer)
         m_timer->stop();
+
+    m_done = true;
 }
 
 void HttpComms::stateChanged(int state)
@@ -260,10 +260,15 @@
 }
 
 
-// getHttp - static function for grabbing http data for a url
-//           this is a synchronous function, it will block according to the vars
-QString HttpComms::getHttp(QString& url, int timeoutMS, int maxRetries, 
-                           int maxRedirects, bool allowGzip,  Credentials* webCred)
+/** \fn HttpComms::getHttp(QString&,int,int,int,bool,Credentials*,bool)
+ *  \brief Static function for grabbing http data for a url.
+ *
+ *   This is a synchronous function, it will block according to the vars.
+ */
+QString HttpComms::getHttp(QString     &url,
+                           int          timeoutMS,    int  maxRetries, 
+                           int          maxRedirects, bool allowGzip,
+                           Credentials *webCred,      bool isInQtEventThread)
 {
     int redirectCount = 0;
     int timeoutCount = 0;
@@ -294,7 +299,8 @@
 
         while (!httpGrabber->isDone())
         {
-            qApp->processEvents();
+            if (isInQtEventThread)
+                qApp->processEvents();
             usleep(10000);
         }
 
Index: libs/libmyth/httpcomms.h
===================================================================
--- libs/libmyth/httpcomms.h	(revision 10403)
+++ libs/libmyth/httpcomms.h	(working copy)
@@ -52,7 +52,8 @@
     static QString getHttp(QString& url, int timeoutMS = 10000, 
                            int maxRetries = 3, int maxRedirects = 3,
                            bool allowGzip = false,
-                           Credentials* webCred = NULL);
+                           Credentials* webCred = NULL,
+                           bool isInQtEventThread = true);
     
     static bool getHttpFile(const QString& file, QString& url, int timeoutMS = 10000,
                             int maxRetries = 3, int maxRedirects = 3, 
