00001
00002 #include <unistd.h>
00003 #include <signal.h>
00004
00005
00006 #include <qdom.h>
00007 #include <qstring.h>
00008 #include <qregexp.h>
00009 #include <qfile.h>
00010 #include <qfileinfo.h>
00011
00012 #include <iostream>
00013 #include <cstdlib>
00014 using namespace std;
00015
00016
00017 #include "mythcontext.h"
00018 #include "mythdbcon.h"
00019
00020
00021 #include "fillutil.h"
00022 #include "icondata.h"
00023
00024 const char * const IM_DOC_TAG = "iconmappings";
00025
00026 const char * const IM_CS_TO_NET_TAG = "callsigntonetwork";
00027 const char * const IM_CS_TAG = "callsign";
00028
00029 const char * const IM_NET_TAG = "network";
00030
00031 const char * const IM_NET_TO_URL_TAG = "networktourl";
00032 const char * const IM_NET_URL_TAG = "url";
00033
00034 const char * const BASEURLMAP_START = "mythfilldatabase.urlmap.";
00035
00036 const char * const IM_BASEURL_TAG = "baseurl";
00037 const char * const IM_BASE_STUB_TAG = "stub";
00038
00039 class DOMException
00040 {
00041 private:
00042 QString message;
00043
00044 protected:
00045 void setMessage(const QString &mes)
00046 {
00047 message = mes;
00048 }
00049
00050 public:
00051 DOMException() : message("Unknown DOMException") {}
00052 virtual ~DOMException() {}
00053 DOMException(const QString &mes) : message(mes) {}
00054 QString getMessage()
00055 {
00056 return message;
00057 }
00058 };
00059
00060 class DOMBadElementConversion : public DOMException
00061 {
00062 public:
00063 DOMBadElementConversion()
00064 {
00065 setMessage("Unknown DOMBadElementConversion");
00066 }
00067 DOMBadElementConversion(const QString &mes) : DOMException(mes) {}
00068 DOMBadElementConversion(const QDomNode &node)
00069 {
00070 setMessage(QString("Unable to convert node: '%1' to QDomElement.")
00071 .arg(node.nodeName()));
00072 }
00073 };
00074
00075 class DOMUnknownChildElement : public DOMException
00076 {
00077 public:
00078 DOMUnknownChildElement()
00079 {
00080 setMessage("Unknown DOMUnknownChildElement");
00081 }
00082 DOMUnknownChildElement(const QString &mes) : DOMException(mes) {}
00083 DOMUnknownChildElement(const QDomElement &e, QString child_name)
00084 {
00085 setMessage(QString("Unknown child element '%1' of: '%2'")
00086 .arg(child_name)
00087 .arg(e.tagName()));
00088 }
00089 };
00090
00091 QDomElement nodeToElement(QDomNode &node)
00092 {
00093 QDomElement retval = node.toElement();
00094 if (retval.isNull())
00095 {
00096 throw DOMBadElementConversion(node);
00097 }
00098 return retval;
00099 }
00100
00101 QString expandURLString(const QString &url)
00102 {
00103 QRegExp expandtarget("\\[([^\\]]+)\\]");
00104 QString retval = url;
00105
00106 int found_at = 0;
00107 int start_index = 0;
00108 while (found_at != -1)
00109 {
00110 found_at = expandtarget.search(retval, start_index);
00111 if (found_at != -1)
00112 {
00113 QString no_mapping("no_URL_mapping");
00114 QString search_string = expandtarget.cap(1);
00115 QString expanded_text = gContext->GetSetting(
00116 QString(BASEURLMAP_START) + search_string, no_mapping);
00117 if (expanded_text != no_mapping)
00118 {
00119 retval.replace(found_at, expandtarget.matchedLength(),
00120 expanded_text);
00121 }
00122 else
00123 {
00124 start_index = found_at + expandtarget.matchedLength();
00125 }
00126 }
00127 }
00128
00129 return retval;
00130 }
00131
00132 QString getNamedElementText(const QDomElement &e,
00133 const QString &child_element_name)
00134 {
00135 QDomNode child_node = e.namedItem(child_element_name);
00136 if (child_node.isNull())
00137 {
00138 throw DOMUnknownChildElement(e, child_element_name);
00139 }
00140 QDomElement element = nodeToElement(child_node);
00141 return element.text();
00142 }
00143
00144 void RunSimpleQuery(const QString &query)
00145 {
00146 MSqlQuery q(MSqlQuery::InitCon());
00147 if (!q.exec(query))
00148 MythContext::DBError("RunSimpleQuery ", q);
00149 }
00150
00151 void IconData::UpdateSourceIcons(int sourceid)
00152 {
00153 VERBOSE(VB_GENERAL,
00154 QString("Updating icons for sourceid: %1").arg(sourceid));
00155
00156 QString fileprefix = SetupIconCacheDirectory();
00157
00158 MSqlQuery query(MSqlQuery::InitCon());
00159 query.prepare("SELECT ch.chanid, nim.url "
00160 "FROM (channel ch, callsignnetworkmap csm) "
00161 "RIGHT JOIN networkiconmap nim ON csm.network = nim.network "
00162 "WHERE ch.callsign = csm.callsign AND "
00163 "(icon = :NOICON OR icon = '') AND ch.sourceid = :SOURCEID");
00164 query.bindValue(":SOURCEID", sourceid);
00165 query.bindValue(":NOICON", "none");
00166
00167 if (!query.exec())
00168 MythContext::DBError("Looking for icons to fetch", query);
00169
00170 if (query.isActive() && query.size() > 0)
00171 {
00172 while (query.next())
00173 {
00174 QString icon_url = expandURLString(query.value(1).toString());
00175 QFileInfo qfi(icon_url);
00176 QFile localfile(fileprefix + "/" + qfi.fileName());
00177 if (!localfile.exists())
00178 {
00179 QString icon_get_command =
00180 QString("wget --timestamping --directory-prefix=%1 '%2'")
00181 .arg(fileprefix).arg(icon_url);
00182
00183 if ((print_verbose_messages & VB_GENERAL) == 0)
00184 icon_get_command += " > /dev/null 2> /dev/null";
00185 VERBOSE(VB_GENERAL,
00186 QString("Attempting to fetch icon with: %1")
00187 .arg(icon_get_command));
00188
00189 system(icon_get_command);
00190 }
00191
00192 if (localfile.exists())
00193 {
00194 int chanid = query.value(0).toInt();
00195
00196 VERBOSE(VB_GENERAL, QString("Updating channel icon for "
00197 "chanid: %1")
00198 .arg(chanid));
00199
00200 MSqlQuery icon_update_query(MSqlQuery::InitCon());
00201 icon_update_query.prepare("UPDATE channel SET icon = :ICON "
00202 "WHERE chanid = :CHANID AND sourceid = :SOURCEID");
00203 icon_update_query.bindValue(":ICON", localfile.name());
00204 icon_update_query.bindValue(":CHANID", query.value(0).toInt());
00205 icon_update_query.bindValue(":SOURCEID", sourceid);
00206
00207 if (!icon_update_query.exec())
00208 MythContext::DBError("Setting the icon file name",
00209 icon_update_query);
00210 }
00211 else
00212 {
00213 VERBOSE(VB_IMPORTANT, QString(
00214 "Error retrieving icon from '%1' to file '%2'")
00215 .arg(icon_url)
00216 .arg(localfile.name()));
00217 }
00218 }
00219 }
00220 }
00221
00222 void IconData::ImportIconMap(const QString &filename)
00223 {
00224
00225 VERBOSE(VB_GENERAL, QString("Importing icon mapping from %1...")
00226 .arg(filename));
00227
00228 QFile xml_file;
00229
00230 if (dash_open(xml_file, filename, IO_ReadOnly))
00231 {
00232 QDomDocument doc;
00233 QString de_msg;
00234 int de_ln = 0;
00235 int de_column = 0;
00236 if (doc.setContent(&xml_file, false, &de_msg, &de_ln, &de_column))
00237 {
00238 MSqlQuery nm_query(MSqlQuery::InitCon());
00239 nm_query.prepare("REPLACE INTO networkiconmap(network, url) "
00240 "VALUES(:NETWORK, :URL)");
00241 MSqlQuery cm_query(MSqlQuery::InitCon());
00242 cm_query.prepare("REPLACE INTO callsignnetworkmap(callsign, "
00243 "network) VALUES(:CALLSIGN, :NETWORK)");
00244 MSqlQuery su_query(MSqlQuery::InitCon());
00245 su_query.prepare("UPDATE settings SET data = :URL "
00246 "WHERE value = :STUBNAME");
00247 MSqlQuery si_query(MSqlQuery::InitCon());
00248 si_query.prepare("INSERT INTO settings(value, data) "
00249 "VALUES(:STUBNAME, :URL)");
00250
00251 QDomElement element = doc.documentElement();
00252
00253 QDomNode node = element.firstChild();
00254 while (!node.isNull())
00255 {
00256 try
00257 {
00258 QDomElement e = nodeToElement(node);
00259 if (e.tagName() == IM_NET_TO_URL_TAG)
00260 {
00261 QString net = getNamedElementText(e, IM_NET_TAG);
00262 QString u = getNamedElementText(e, IM_NET_URL_TAG);
00263
00264 nm_query.bindValue(":NETWORK", net.stripWhiteSpace());
00265 nm_query.bindValue(":URL", u.stripWhiteSpace());
00266 if (!nm_query.exec())
00267 MythContext::DBError(
00268 "Inserting network->url mapping", nm_query);
00269 }
00270 else if (e.tagName() == IM_CS_TO_NET_TAG)
00271 {
00272 QString cs = getNamedElementText(e, IM_CS_TAG);
00273 QString net = getNamedElementText(e, IM_NET_TAG);
00274
00275 cm_query.bindValue(":CALLSIGN", cs.stripWhiteSpace());
00276 cm_query.bindValue(":NETWORK", net.stripWhiteSpace());
00277 if (!cm_query.exec())
00278 MythContext::DBError("Inserting callsign->network "
00279 "mapping", cm_query);
00280 }
00281 else if (e.tagName() == IM_BASEURL_TAG)
00282 {
00283 MSqlQuery *qr = &si_query;
00284
00285 QString st(BASEURLMAP_START);
00286 st += getNamedElementText(e, IM_BASE_STUB_TAG);
00287 QString u = getNamedElementText(e, IM_NET_URL_TAG);
00288
00289 MSqlQuery qc(MSqlQuery::InitCon());
00290 qc.prepare("SELECT COUNT(*) FROM settings "
00291 "WHERE value = :STUBNAME");
00292 qc.bindValue(":STUBNAME", st);
00293 qc.exec();
00294 if (qc.isActive() && qc.size() > 0)
00295 {
00296 qc.first();
00297 if (qc.value(0).toInt() != 0)
00298 {
00299 qr = &su_query;
00300 }
00301 }
00302
00303 qr->bindValue(":STUBNAME", st);
00304 qr->bindValue(":URL", u);
00305
00306 if (!qr->exec())
00307 MythContext::DBError(
00308 "Inserting callsign->network mapping", *qr);
00309 }
00310 }
00311 catch (DOMException &e)
00312 {
00313 VERBOSE( VB_IMPORTANT, QString("Error while processing "
00314 "%1: %2")
00315 .arg(node.nodeName())
00316 .arg(e.getMessage()));
00317 }
00318 node = node.nextSibling();
00319 }
00320 }
00321 else
00322 {
00323 VERBOSE(VB_IMPORTANT, QString("Error unable to set document "
00324 "content: %1:%2c%3 %4")
00325 .arg(filename)
00326 .arg(de_ln)
00327 .arg(de_column)
00328 .arg(de_msg));
00329 }
00330 }
00331 else
00332 {
00333 VERBOSE(VB_IMPORTANT, QString("Error unable to open '%1' for reading.")
00334 .arg(filename));
00335 }
00336 }
00337
00338 void IconData::ExportIconMap(const QString &filename)
00339 {
00340
00341 VERBOSE(VB_GENERAL, QString("Exporting icon mapping to %1...")
00342 .arg(filename));
00343
00344 QFile xml_file(filename);
00345 if (dash_open(xml_file, filename, IO_WriteOnly))
00346 {
00347 QTextStream os(&xml_file);
00348 os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
00349 os << "<!-- generated by mythfilldatabase -->\n";
00350
00351 QDomDocument iconmap;
00352 QDomElement roote = iconmap.createElement(IM_DOC_TAG);
00353
00354 MSqlQuery query(MSqlQuery::InitCon());
00355 query.exec("SELECT * FROM callsignnetworkmap ORDER BY callsign;");
00356
00357 if (query.isActive() && query.size() > 0)
00358 {
00359 while (query.next())
00360 {
00361 QDomElement cs2nettag = iconmap.createElement(IM_CS_TO_NET_TAG);
00362 QDomElement cstag = iconmap.createElement(IM_CS_TAG);
00363 QDomElement nettag = iconmap.createElement(IM_NET_TAG);
00364 QDomText cs_text = iconmap.createTextNode(
00365 query.value(1).toString());
00366 QDomText net_text = iconmap.createTextNode(
00367 query.value(2).toString());
00368
00369 cstag.appendChild(cs_text);
00370 nettag.appendChild(net_text);
00371
00372 cs2nettag.appendChild(cstag);
00373 cs2nettag.appendChild(nettag);
00374
00375 roote.appendChild(cs2nettag);
00376 }
00377 }
00378
00379 query.exec("SELECT * FROM networkiconmap ORDER BY network;");
00380 if (query.isActive() && query.size() > 0)
00381 {
00382 while (query.next())
00383 {
00384 QDomElement net2urltag = iconmap.createElement(
00385 IM_NET_TO_URL_TAG);
00386 QDomElement nettag = iconmap.createElement(IM_NET_TAG);
00387 QDomElement urltag = iconmap.createElement(IM_NET_URL_TAG);
00388 QDomText net_text = iconmap.createTextNode(
00389 query.value(1).toString());
00390 QDomText url_text = iconmap.createTextNode(
00391 query.value(2).toString());
00392
00393 nettag.appendChild(net_text);
00394 urltag.appendChild(url_text);
00395
00396 net2urltag.appendChild(nettag);
00397 net2urltag.appendChild(urltag);
00398
00399 roote.appendChild(net2urltag);
00400 }
00401 }
00402
00403 query.prepare("SELECT value,data FROM settings WHERE value "
00404 "LIKE :URLMAP");
00405 query.bindValue(":URLMAP", QString(BASEURLMAP_START) + "%");
00406 query.exec();
00407 if (query.isActive() && query.size() > 0)
00408 {
00409 QRegExp baseax("\\.([^\\.]+)$");
00410 while (query.next())
00411 {
00412 QString base_stub = query.value(0).toString();
00413 if (baseax.search(base_stub) != -1)
00414 {
00415 base_stub = baseax.cap(1);
00416 }
00417
00418 QDomElement baseurltag = iconmap.createElement(IM_BASEURL_TAG);
00419 QDomElement stubtag = iconmap.createElement(
00420 IM_BASE_STUB_TAG);
00421 QDomElement urltag = iconmap.createElement(IM_NET_URL_TAG);
00422 QDomText base_text = iconmap.createTextNode(base_stub);
00423 QDomText url_text = iconmap.createTextNode(
00424 query.value(1).toString());
00425
00426 stubtag.appendChild(base_text);
00427 urltag.appendChild(url_text);
00428
00429 baseurltag.appendChild(stubtag);
00430 baseurltag.appendChild(urltag);
00431
00432 roote.appendChild(baseurltag);
00433 }
00434 }
00435
00436 iconmap.appendChild(roote);
00437 iconmap.save(os, 4);
00438 }
00439 else
00440 {
00441 VERBOSE(VB_IMPORTANT, QString("Error unable to open '%1' for writing."));
00442 }
00443 }
00444
00445 void IconData::ResetIconMap(bool reset_icons)
00446 {
00447 MSqlQuery query(MSqlQuery::InitCon());
00448 query.prepare("DELETE FROM settings WHERE value LIKE :URLMAPLIKE");
00449 query.bindValue(":URLMAPLIKE", QString(BASEURLMAP_START) + '%');
00450 if (!query.exec())
00451 MythContext::DBError("ResetIconMap", query);
00452
00453 RunSimpleQuery("TRUNCATE TABLE callsignnetworkmap;");
00454 RunSimpleQuery("TRUNCATE TABLE networkiconmap");
00455
00456 if (reset_icons)
00457 {
00458 RunSimpleQuery("UPDATE channel SET icon = 'none'");
00459 }
00460 }