Add HAVE_NO_PACKAGEKIT for better UI dev.
[meego-garage:garage-client-services.git] / src / garageclientservices.cpp
1 #include "garageclientservices.h"
2 #include <QTimer>
3 #include <QNetworkAccessManager>
4 #include <QNetworkProxy>
5 #include "errorhandler.h"
6 #include "applicationmanager.h"
7 #include <QFile>
8 #include <QDir>
9
10 extern "C" {
11 #include <proxy.h>
12 }
13
14 using namespace MeeGoGarage;
15
16 const char* GarageSettings::CommonGroup = "Common";
17 const char* GarageSettings::WebServicesGroup = "WebServices";
18 const char* GarageSettings::UpdateXmlModeKey = "UpdateXmlMode";
19 const char* GarageSettings::WebServiceBackendUrlKey = "WebServiceBackendUrl";
20
21 const char* GarageSettings::GarageLocalPathKey = "GarageLocalPath";
22 const char* GarageSettings::GarageLocalXmlFileNameKey = "GarageLocalXmlFileName";
23
24 const char* GarageSettings::GarageRemoteXmlProxyKey = "GarageRemoteXmlProxy";
25 const char* GarageSettings::GarageRemoteXmlUrlKey = "GarageRemoteXmlUrl";
26
27 const char* GarageSettings::GarageMetadataProxyKey = "GarageMetadataProxy";
28 const char* GarageSettings::ImageCacheFolderKey = "ImageCacheFolder";
29
30 const char* GarageSettings::ProjectUrlKey = "ProjectUrl";
31
32 QNetworkProxy *GarageClientServices::systemHttpProxy = NULL;
33
34 GarageClientServices* GarageClientServices::getInstance()
35 {
36     static GarageClientServices* instance = NULL;
37
38     if (instance == NULL) {
39         instance = new GarageClientServices();
40
41         // configure packagekit
42         //QStringList hints;
43         //hints << "locale=en_US.UTF-8";
44         //hints << "background=false";
45         //hints << "interactive=true";
46         //PackageKit::Client::instance()->setHints(hints);
47     }
48
49     return instance;
50 }
51
52 void GarageClientServices::finish()
53 {
54     ApplicationManager * appManager = ApplicationManager::instance();
55     if (appManager) {
56         appManager->saveStatus();
57     }
58 }
59
60 QNetworkAccessManager *GarageClientServices::getNetworkAccessManager()
61 {
62     return networkAccessManager;
63 }
64
65 bool GarageClientServices::convertUrlToProxy(const QUrl& url, QNetworkProxy &proxy)
66 {
67     Q_ASSERT(!url.toString().isEmpty() && !url.toString().isNull());
68
69     if (url.scheme() == "http") {
70         proxy.setType(QNetworkProxy::HttpProxy);
71         // The returned string from libproxy has format:
72         // http://[username:password@]proxy:port
73         proxy.setHostName(url.host());
74         proxy.setPort(url.port());
75         proxy.setUser(url.userName());
76         proxy.setPassword(url.password());
77     } else if (url.scheme() == "direct") {
78         proxy.setType(QNetworkProxy::NoProxy);
79     }
80
81     return true;
82 }
83
84 QNetworkProxy *GarageClientServices::getSystemHttpProxy(const QUrl& destUrl)
85 {
86     if (systemHttpProxy != NULL)
87         return systemHttpProxy;
88
89     systemHttpProxy = new QNetworkProxy(QNetworkProxy::DefaultProxy);
90     QUrl proxyUrl;
91
92     pxProxyFactory *proxyFactory = px_proxy_factory_new();
93     if (proxyFactory == NULL)
94         return systemHttpProxy;
95
96     char tempUrl[512];
97     strcpy(tempUrl, destUrl.toString().toUtf8().constData());
98
99     char **proxyArray = px_proxy_factory_get_proxies(proxyFactory, tempUrl);
100     if (proxyArray == NULL)
101         return systemHttpProxy;
102
103     qDebug() << "Dump proxy settings";
104
105     int i;
106     for (i = 0; proxyArray[i] != NULL; i++) {
107         qDebug() << "    " << proxyArray[i];
108
109         proxyUrl = QUrl(proxyArray[i]);
110         if (proxyUrl.scheme() == "http") {
111             break;
112         }
113     }
114
115     if (proxyArray[i] == NULL)
116         return systemHttpProxy;
117
118     for (i = 0; proxyArray[i] != NULL ; i++)
119         free(proxyArray[i]);
120     free(proxyArray);
121
122     convertUrlToProxy(proxyUrl, *systemHttpProxy);
123
124     return systemHttpProxy;
125 }
126
127 void GarageClientServices::configNetworkProxy(const QNetworkProxy& proxy)
128 {
129
130     networkAccessManager->setProxy(proxy);
131     QNetworkProxy::setApplicationProxy(proxy);
132
133     if (httpProxy == NULL) {
134         httpProxy = new QNetworkProxy(QNetworkProxy::DefaultProxy);
135     }
136
137     *httpProxy = proxy;
138 }
139
140 void GarageClientServices::configNetworkProxy()
141 {
142     //Set proxy to system proxy or restore user configured proxy
143     if (httpProxy != NULL) {
144         networkAccessManager->setProxy(*httpProxy);
145         QNetworkProxy::setApplicationProxy(*httpProxy);
146         return;
147     }
148
149     settings->beginGroup(GarageSettings::CommonGroup);
150     QNetworkProxy *systemProxy = getSystemHttpProxy(settings->value(GarageSettings::WebServiceBackendUrlKey, "http://meego.com").toString());
151     settings->endGroup();
152
153     configNetworkProxy(*systemProxy);
154 }
155
156 ErrorHandler* GarageClientServices::getErrorHandler()
157 {
158     return errorHandler;
159 }
160
161 GarageClientServices::GarageClientServices() :
162     catalog(NULL), networkAccessManager(0), webClient(0), httpProxy(0)
163 {
164     bool updateXmlFile = false;
165
166     if (!QFile::exists("/usr/share/garage-client-services/data/config.ini")) {
167         emit error(CriticalError, "The configuration file /usr/share/garage-client-services/data/config.ini is missed.");
168         return;
169     }
170
171     QSettings fix_settings("/usr/share/garage-client-services/data/config.ini", QSettings::IniFormat);
172     fix_settings.beginGroup(GarageSettings::CommonGroup);
173     QString localPath = fix_settings.value(GarageSettings::GarageLocalPathKey, "/.config/garage-client-services/").toString();
174     fix_settings.endGroup();
175
176     QString localInitFile = QDir::homePath() + localPath + "/config.ini";
177     if (!QFile::exists(localInitFile)) {
178         //copy the common file to home folder for writing
179         QString dirPath = QDir::homePath() + localPath;
180         QDir dir(dirPath);
181         if (!dir.exists()) {
182             if (!dir.mkpath(dirPath)) {
183                 qDebug() << "Create local configuration folder " << dirPath << " failed: ";
184                 emit error(CriticalError, "The local configuration file"
185                            + dirPath + "created failed");
186                 return;
187             }
188         }
189
190         QFile::copy("/usr/share/garage-client-services/data/config.ini",
191                     localInitFile);
192         qDebug() << "Created the local configuration file: " << localInitFile;
193     }
194
195     if (!QFile::exists(localInitFile)) {
196         emit error(CriticalError, "The local configuration file"
197                    + localInitFile + "is not available");
198         return;
199     }
200
201     settings = new QSettings(localInitFile, QSettings::IniFormat);
202
203     settings->beginGroup(GarageSettings::CommonGroup);
204     settings->setValue(GarageSettings::GarageLocalPathKey, QDir::homePath() + localPath);
205     qDebug() << "Configure the local path to " << QDir::homePath() + localPath;
206
207     errorHandler = new ErrorHandler(this);
208     connect(errorHandler, SIGNAL(error(GarageClientServices::ErrorCode, QString)),
209             SIGNAL(error(GarageClientServices::ErrorCode, QString)));
210
211     networkAccessManager = new QNetworkAccessManager(this);
212     configNetworkProxy();
213
214     QUrl metadataProxy = settings->value(GarageSettings::GarageMetadataProxyKey, "errorLink").toUrl();
215     if (metadataProxy.toString() != "errorLink") {
216         QNetworkProxy proxy;
217         if (convertUrlToProxy(metadataProxy, proxy)) {
218             configNetworkProxy(proxy);
219         }
220     }
221
222     QString updateXmlMode = settings->value(GarageSettings::UpdateXmlModeKey, "Disable").toString();
223     if (updateXmlMode == "WebService") {
224         //Get latest xml url and set to the ini file
225     } else if (updateXmlMode == "UseConfig") {
226         //If we need to update the xml file, we download it. TODO: webservcie
227         QUrl garageRemoteXmlUrl = settings->value(GarageSettings::GarageRemoteXmlUrlKey, "errorLink").toString();
228
229         if (garageRemoteXmlUrl.toString() != "errorLink") {
230             QUrl garageRemoteXmlProxy = settings->value(GarageSettings::GarageRemoteXmlProxyKey, "errorLink").toUrl();
231             if (garageRemoteXmlProxy.toString() != "errorLink") {
232                 QNetworkProxy proxy;
233                 if (convertUrlToProxy(garageRemoteXmlProxy, proxy)) {
234                     networkAccessManager->setProxy(proxy);
235                 }
236             }
237
238             QNetworkReply *networkReply = networkAccessManager->get(QNetworkRequest(garageRemoteXmlUrl));
239             connect(networkReply, SIGNAL(finished()), SLOT(garageXmlFileDownloaded()));
240             QObject::connect(networkReply, SIGNAL(error(QNetworkReply::NetworkError)),
241                              getErrorHandler(), SLOT(networkReplyError(QNetworkReply::NetworkError)));
242             
243             connect(networkReply, SIGNAL(error(QNetworkReply::NetworkError)),
244                              SLOT(networkReplyError(QNetworkReply::NetworkError)));
245
246             updateXmlFile = true;
247         }
248     }
249
250     settings->endGroup();
251
252     if (!updateXmlFile) {
253         QTimer *timer = new QTimer(this);
254         timer->setSingleShot(TRUE);
255         connect(timer, SIGNAL(timeout()), SLOT(initGarageServiceWithStaticXml()));
256         timer->start(0);
257     }
258
259     //TODO: start GCS initialization. Check repo and garage update from WebServer
260 #if 0 //TODO: QObjects created in different threads need better management
261     QFutureWatcher<void> *watcher = new QFutureWatcher<void>();
262     connect(watcher, SIGNAL(finished()), SLOT(initGarageServiceWithStaticXml()));
263     QFuture<void> future = QtConcurrent::run(this, &GarageClientServices::initGarageService);
264     watcher->setFuture(future);
265 #endif
266
267 }
268
269 Catalog *GarageClientServices::getCatalog()
270 {
271     return catalog;
272 }
273
274 void GarageClientServices::initGarageService()
275 {
276     //Initialization done, we are able to create catalog
277     catalog = new Catalog();
278
279     settings->beginGroup(GarageSettings::CommonGroup);
280     QString path = settings->value(GarageSettings::GarageLocalPathKey, "errorLink").toString();
281     QString filename = settings->value(GarageSettings::GarageLocalXmlFileNameKey,
282                                               "errorLink").toString();
283     settings->endGroup();
284
285     Q_ASSERT(path != "errorLink" && filename != "errorLink");
286
287     catalog->loadGarageSource(path + filename);
288     qDebug() << "Open the garage data xml: " << path + filename;
289
290     if (!QFile::exists(path + filename)) {
291         qDebug() << "The garage data xml is not available, check config.ini";
292         emit garageClientServicesInitialized(-1);
293     }
294
295     emit garageClientServicesInitialized(0);
296 }
297
298 void GarageClientServices::initGarageServiceWithStaticXml()
299 {
300     settings->beginGroup(GarageSettings::CommonGroup);
301     QString path = settings->value(GarageSettings::GarageLocalPathKey, "errorLink").toString();
302     QString filename = settings->value(GarageSettings::GarageLocalXmlFileNameKey,
303                                               "errorLink").toString();
304     settings->endGroup();
305
306     if (path == "errorLink" || filename == "errorLink") {
307         emit error(CriticalError, "The local xml path is not in config.ini.");
308         return;
309     }
310
311     if (!QFile::exists(path + filename)) {
312         emit error(CriticalError, "The local xml file" +  path + filename + " does not exist.");
313         return;
314     }
315
316     initGarageService();
317 }
318
319 QSettings * GarageClientServices::getSettings()
320 {
321     return settings;
322 }
323
324 QUrl GarageClientServices::getProjectUrl()
325 {
326     settings->beginGroup(GarageSettings::CommonGroup);
327     QString projectUrl = settings->value(GarageSettings::ProjectUrlKey, "errorLink").toString();
328     settings->endGroup();
329     if (projectUrl == "errorLink") {
330         qDebug() << "The ProjectUrl is not provided in " << settings->fileName();
331     }
332
333     return QUrl(projectUrl);
334 }
335
336 void GarageClientServices::garageXmlFileDownloaded()
337 {
338     QNetworkReply *networkReply = qobject_cast<QNetworkReply*>(sender());
339     networkReply->deleteLater();
340
341     if (networkReply->error() != QNetworkReply::NoError)
342         return;
343
344     settings->beginGroup(GarageSettings::CommonGroup);
345     QString path = settings->value(GarageSettings::GarageLocalPathKey, "errorLink").toString();
346     if (path == "errorLink") {
347         //No default path, set it
348         path = QDir::homePath() + "/.config/garage-client-services/";
349         settings->setValue(GarageSettings::GarageLocalPathKey, path);
350     }
351
352     QString filename = settings->value(GarageSettings::GarageLocalXmlFileNameKey, "errorLink").toString();
353     if (filename == "errorLink") {
354         filename = "garage_data.xml";
355         settings->setValue(GarageSettings::GarageLocalXmlFileNameKey, filename);
356     }
357     settings->endGroup();
358
359     QDir dir(path);
360     if (!dir.exists()) {
361         if (!dir.mkpath(path)) {
362             qDebug() << "Create local xml file dir " << path << " failed: ";
363             return;
364         }
365     }
366
367     QFile file(path + filename);
368     if (!file.open(QFile::WriteOnly)) {
369         qDebug() << "Create local xml file " << path + filename << " failed: " << file.errorString();
370         return;
371     }
372
373     file.write(networkReply->readAll());
374     file.close();
375
376     networkReply->deleteLater();
377
378     qDebug() << "Downloaded latest xml file to " << path + filename;
379
380     configNetworkProxy();
381
382     initGarageService();
383 }
384
385 void GarageClientServices::networkReplyError(QNetworkReply::NetworkError errorCode)
386 {
387     QNetworkReply *networkReply = qobject_cast<QNetworkReply*>(sender());
388
389     qDebug() << "Network Reply Error: " << errorCode << networkReply->errorString();
390
391     if (errorCode == QNetworkReply::NoError)
392         return;
393
394     //Offline mode, use static xml
395     initGarageServiceWithStaticXml();
396     
397 }
398