1 // For license of this file, see <project-root-folder>/LICENSE.md.
2 
3 #include "network-web/webfactory.h"
4 
5 #include "gui/messagebox.h"
6 #include "miscellaneous/application.h"
7 #include "miscellaneous/iconfactory.h"
8 #include "network-web/cookiejar.h"
9 
10 #include <QDesktopServices>
11 #include <QProcess>
12 #include <QUrl>
13 
14 #if defined(USE_WEBENGINE)
15 #include "network-web/adblock/adblockicon.h"
16 #include "network-web/adblock/adblockmanager.h"
17 #include "network-web/networkurlinterceptor.h"
18 #include "network-web/urlinterceptor.h"
19 
20 #include <QWebEngineDownloadItem>
21 #include <QWebEngineProfile>
22 #include <QWebEngineScript>
23 #include <QWebEngineScriptCollection>
24 #include <QWebEngineUrlScheme>
25 #endif
26 
WebFactory(QObject * parent)27 WebFactory::WebFactory(QObject* parent)
28   : QObject(parent) {
29 #if defined(USE_WEBENGINE)
30   m_engineSettings = nullptr;
31   m_adBlock = new AdBlockManager(this);
32   m_urlInterceptor = new NetworkUrlInterceptor(this);
33 #endif
34 
35   m_cookieJar = new CookieJar(nullptr);
36 
37 #if defined(USE_WEBENGINE)
38 #if QT_VERSION >= 0x050D00 // Qt >= 5.13.0
39   QWebEngineProfile::defaultProfile()->setUrlRequestInterceptor(m_urlInterceptor);
40 #else
41   QWebEngineProfile::defaultProfile()->setRequestInterceptor(m_urlInterceptor);
42 #endif
43 #endif
44 }
45 
~WebFactory()46 WebFactory::~WebFactory() {
47 #if defined(USE_WEBENGINE)
48   if (m_engineSettings != nullptr && m_engineSettings->menu() != nullptr) {
49     m_engineSettings->menu()->deleteLater();
50   }
51 #endif
52 
53   if (m_cookieJar != nullptr) {
54     m_cookieJar->deleteLater();
55   }
56 }
57 
sendMessageViaEmail(const Message & message)58 bool WebFactory::sendMessageViaEmail(const Message& message) {
59   if (qApp->settings()->value(GROUP(Browser), SETTING(Browser::CustomExternalEmailEnabled)).toBool()) {
60     const QString browser = qApp->settings()->value(GROUP(Browser), SETTING(Browser::CustomExternalEmailExecutable)).toString();
61     const QString arguments = qApp->settings()->value(GROUP(Browser), SETTING(Browser::CustomExternalEmailArguments)).toString();
62 
63     return IOFactory::startProcessDetached(browser, {}, arguments.arg(message.m_title, stripTags(message.m_contents)));
64   }
65   else {
66     // Send it via mailto protocol.
67     // NOTE: http://en.wikipedia.org/wiki/Mailto
68     return QDesktopServices::openUrl(QSL("mailto:?subject=%1&body=%2").arg(QString(QUrl::toPercentEncoding(message.m_title)),
69                                                                            QString(QUrl::toPercentEncoding(stripTags(
70                                                                                                              message.m_contents)))));
71   }
72 }
73 
openUrlInExternalBrowser(const QString & url) const74 bool WebFactory::openUrlInExternalBrowser(const QString& url) const {
75   qDebugNN << LOGSEC_NETWORK << "We are trying to open URL" << QUOTE_W_SPACE_DOT(url);
76 
77   bool result = false;
78 
79   if (qApp->settings()->value(GROUP(Browser), SETTING(Browser::CustomExternalBrowserEnabled)).toBool()) {
80     const QString browser = qApp->settings()->value(GROUP(Browser), SETTING(Browser::CustomExternalBrowserExecutable)).toString();
81     const QString arguments = qApp->settings()->value(GROUP(Browser), SETTING(Browser::CustomExternalBrowserArguments)).toString();
82     auto nice_args = arguments.arg(url);
83 
84     qDebugNN << LOGSEC_NETWORK << "Arguments for external browser:" << QUOTE_W_SPACE_DOT(nice_args);
85 
86     result = IOFactory::startProcessDetached(browser, {}, nice_args);
87 
88     if (!result) {
89       qDebugNN << LOGSEC_NETWORK << "External web browser call failed.";
90     }
91   }
92   else {
93     result = QDesktopServices::openUrl(url);
94   }
95 
96   if (!result) {
97     // We display GUI information that browser was not probably opened.
98     MessageBox::show(qApp->mainFormWidget(),
99                      QMessageBox::Icon::Critical,
100                      tr("Navigate to website manually"),
101                      tr("%1 was unable to launch your web browser with the given URL, you need to open the "
102                         "below website URL in your web browser manually.").arg(QSL(APP_NAME)),
103                      {},
104                      url,
105                      QMessageBox::StandardButton::Ok);
106   }
107 
108   return result;
109 }
110 
stripTags(QString text)111 QString WebFactory::stripTags(QString text) {
112   return text.remove(QRegularExpression(QSL("<[^>]*>")));
113 }
114 
unescapeHtml(const QString & html)115 QString WebFactory::unescapeHtml(const QString& html) {
116   if (html.isEmpty()) {
117     return html;
118   }
119 
120   if (m_htmlNamedEntities.isEmpty()) {
121     generateUnescapes();
122   }
123 
124   QString output; output.reserve(html.size());
125 
126   // Traverse input HTML string and replace named/number entities.
127   for (int pos = 0; pos < html.size(); ) {
128     const QChar first = html.at(pos);
129 
130     if (first == QChar('&')) {
131       // We need to find ending ';'.
132       int pos_end = -1;
133 
134       // We're finding end of entity, but also we limit searching window to 10 characters.
135       for (int pos_find = pos; pos_find <= pos + 10 && pos_find < html.size(); pos_find++) {
136         if (html.at(pos_find) == QChar(';')) {
137           // We found end of the entity.
138           pos_end = pos_find;
139           break;
140         }
141       }
142 
143       if (pos_end + 1 > pos) {
144         // OK, we have entity.
145         if (html.at(pos + 1) == QChar('#')) {
146           // We have numbered entity.
147           uint number;
148           QString number_str;
149 
150           if (html.at(pos + 2) == QChar('x')) {
151             // base-16 number.
152             number_str = html.mid(pos + 3, pos_end - pos - 3);
153             number = number_str.toUInt(nullptr, 16);
154           }
155           else {
156             // base-10 number.
157             number_str = html.mid(pos + 2, pos_end - pos - 2);
158             number = number_str.toUInt();
159           }
160 
161           if (number > 0U) {
162             output.append(QChar(number));
163           }
164           else {
165             // Failed to convert to number, leave intact.
166             output.append(html.midRef(pos, pos_end - pos + 1));
167           }
168 
169           pos = pos_end + 1;
170           continue;
171         }
172         else {
173           // We have named entity.
174           auto entity_name = html.mid(pos + 1, pos_end - pos - 1);
175 
176           if (m_htmlNamedEntities.contains(entity_name)) {
177             // Entity found, proceed.
178             output.append(m_htmlNamedEntities.value(entity_name));
179           }
180           else {
181             // Entity NOT found, leave intact.
182             output.append('&');
183             output.append(entity_name);
184             output.append(';');
185           }
186 
187           pos = pos_end + 1;
188           continue;
189         }
190       }
191     }
192 
193     // No entity, normally append and continue.
194     output.append(first);
195     pos++;
196   }
197 
198   /*
199      qDebugNN << LOGSEC_CORE
200            << "Unescaped string" << QUOTE_W_SPACE(html)
201            << "to" << QUOTE_W_SPACE_DOT(output);
202    */
203 
204   return output;
205 }
206 
processFeedUriScheme(const QString & url)207 QString WebFactory::processFeedUriScheme(const QString& url) {
208   if (url.startsWith(QSL(URI_SCHEME_FEED))) {
209     return QSL(URI_SCHEME_HTTPS) + url.mid(QSL(URI_SCHEME_FEED).size());
210   }
211   else if (url.startsWith(QSL(URI_SCHEME_FEED_SHORT))) {
212     return url.mid(QSL(URI_SCHEME_FEED_SHORT).size());
213   }
214   else {
215     return url;
216   }
217 }
218 
updateProxy()219 void WebFactory::updateProxy() {
220   const QNetworkProxy::ProxyType selected_proxy_type = static_cast<QNetworkProxy::ProxyType>(qApp->settings()->value(GROUP(Proxy),
221                                                                                                                      SETTING(Proxy::Type)).
222                                                                                              toInt());
223 
224   if (selected_proxy_type == QNetworkProxy::NoProxy) {
225     qDebugNN << LOGSEC_NETWORK << "Disabling application-wide proxy completely.";
226 
227     QNetworkProxyFactory::setUseSystemConfiguration(false);
228     QNetworkProxy::setApplicationProxy(QNetworkProxy::ProxyType::NoProxy);
229   }
230   else if (selected_proxy_type == QNetworkProxy::ProxyType::DefaultProxy) {
231     qDebugNN << LOGSEC_NETWORK << "Using application-wide proxy to be system's default proxy.";
232     QNetworkProxyFactory::setUseSystemConfiguration(true);
233   }
234   else {
235     const Settings* settings = qApp->settings();
236     QNetworkProxy new_proxy;
237 
238     // Custom proxy is selected, set it up.
239     new_proxy.setType(selected_proxy_type);
240     new_proxy.setHostName(settings->value(GROUP(Proxy), SETTING(Proxy::Host)).toString());
241     new_proxy.setPort(quint16(settings->value(GROUP(Proxy), SETTING(Proxy::Port)).toInt()));
242     new_proxy.setUser(settings->value(GROUP(Proxy), SETTING(Proxy::Username)).toString());
243     new_proxy.setPassword(settings->password(GROUP(Proxy), SETTING(Proxy::Password)).toString());
244 
245     qWarningNN << LOGSEC_NETWORK
246                << "Activating application-wide custom proxy, address:"
247                << QUOTE_W_SPACE_COMMA(new_proxy.hostName())
248                << " type:"
249                << QUOTE_W_SPACE_DOT(new_proxy.type());
250 
251     QNetworkProxy::setApplicationProxy(new_proxy);
252   }
253 }
254 
255 #if defined(USE_WEBENGINE)
adBlock() const256 AdBlockManager* WebFactory::adBlock() const {
257   return m_adBlock;
258 }
259 
urlIinterceptor() const260 NetworkUrlInterceptor* WebFactory::urlIinterceptor() const {
261   return m_urlInterceptor;
262 }
263 
engineSettingsAction()264 QAction* WebFactory::engineSettingsAction() {
265   if (m_engineSettings == nullptr) {
266     m_engineSettings = new QAction(qApp->icons()->fromTheme(QSL("applications-internet")), tr("Web engine settings"), this);
267     m_engineSettings->setMenu(new QMenu());
268     createMenu(m_engineSettings->menu());
269     connect(m_engineSettings->menu(), &QMenu::aboutToShow, this, [this]() {
270       createMenu();
271     });
272   }
273 
274   return m_engineSettings;
275 }
276 
createMenu(QMenu * menu)277 void WebFactory::createMenu(QMenu* menu) {
278   if (menu == nullptr) {
279     menu = qobject_cast<QMenu*>(sender());
280 
281     if (menu == nullptr) {
282       return;
283     }
284   }
285 
286   menu->clear();
287   QList<QAction*> actions;
288 
289   actions << createEngineSettingsAction(tr("Auto-load images"), QWebEngineSettings::WebAttribute::AutoLoadImages);
290   actions << createEngineSettingsAction(tr("JS enabled"), QWebEngineSettings::WebAttribute::JavascriptEnabled);
291   actions << createEngineSettingsAction(tr("JS can open popup windows"), QWebEngineSettings::WebAttribute::JavascriptCanOpenWindows);
292   actions << createEngineSettingsAction(tr("JS can access clipboard"), QWebEngineSettings::WebAttribute::JavascriptCanAccessClipboard);
293   actions << createEngineSettingsAction(tr("Hyperlinks can get focus"), QWebEngineSettings::WebAttribute::LinksIncludedInFocusChain);
294   actions << createEngineSettingsAction(tr("Local storage enabled"), QWebEngineSettings::WebAttribute::LocalStorageEnabled);
295   actions << createEngineSettingsAction(tr("Local content can access remote URLs"), QWebEngineSettings::WebAttribute::LocalContentCanAccessRemoteUrls);
296   actions << createEngineSettingsAction(tr("XSS auditing enabled"), QWebEngineSettings::WebAttribute::XSSAuditingEnabled);
297   actions << createEngineSettingsAction(tr("Spatial navigation enabled"), QWebEngineSettings::WebAttribute::SpatialNavigationEnabled);
298   actions << createEngineSettingsAction(tr("Local content can access local files"), QWebEngineSettings::WebAttribute::LocalContentCanAccessFileUrls);
299   actions << createEngineSettingsAction(tr("Hyperlink auditing enabled"), QWebEngineSettings::WebAttribute::HyperlinkAuditingEnabled);
300   actions << createEngineSettingsAction(tr("Animate scrolling"), QWebEngineSettings::WebAttribute::ScrollAnimatorEnabled);
301   actions << createEngineSettingsAction(tr("Error pages enabled"), QWebEngineSettings::WebAttribute::ErrorPageEnabled);
302   actions << createEngineSettingsAction(tr("Plugins enabled"), QWebEngineSettings::WebAttribute::PluginsEnabled);
303   actions << createEngineSettingsAction(tr("Fullscreen enabled"), QWebEngineSettings::WebAttribute::FullScreenSupportEnabled);
304 
305 #if !defined(Q_OS_UNIX)
306   actions << createEngineSettingsAction(tr("Screen capture enabled"), QWebEngineSettings::WebAttribute::ScreenCaptureEnabled);
307   actions << createEngineSettingsAction(tr("WebGL enabled"), QWebEngineSettings::WebAttribute::WebGLEnabled);
308   actions << createEngineSettingsAction(tr("Accelerate 2D canvas"), QWebEngineSettings::WebAttribute::Accelerated2dCanvasEnabled);
309   actions << createEngineSettingsAction(tr("Print element backgrounds"), QWebEngineSettings::WebAttribute::PrintElementBackgrounds);
310   actions << createEngineSettingsAction(tr("Allow running insecure content"), QWebEngineSettings::WebAttribute::AllowRunningInsecureContent);
311   actions << createEngineSettingsAction(tr("Allow geolocation on insecure origins"), QWebEngineSettings::WebAttribute::AllowGeolocationOnInsecureOrigins);
312 #endif
313 
314   menu->addActions(actions);
315 }
316 
webEngineSettingChanged(bool enabled)317 void WebFactory::webEngineSettingChanged(bool enabled) {
318   const QAction* const act = qobject_cast<QAction*>(sender());
319 
320   QWebEngineSettings::WebAttribute attribute = static_cast<QWebEngineSettings::WebAttribute>(act->data().toInt());
321 
322   qApp->settings()->setValue(WebEngineAttributes::ID, QString::number(static_cast<int>(attribute)), enabled);
323   QWebEngineProfile::defaultProfile()->settings()->setAttribute(attribute, act->isChecked());
324 }
325 
createEngineSettingsAction(const QString & title,QWebEngineSettings::WebAttribute attribute)326 QAction* WebFactory::createEngineSettingsAction(const QString& title, QWebEngineSettings::WebAttribute attribute) {
327   auto* act = new QAction(title, m_engineSettings->menu());
328 
329   act->setData(attribute);
330   act->setCheckable(true);
331   act->setChecked(qApp->settings()->value(WebEngineAttributes::ID, QString::number(static_cast<int>(attribute)), true).toBool());
332   QWebEngineProfile::defaultProfile()->settings()->setAttribute(attribute, act->isChecked());
333   connect(act, &QAction::toggled, this, &WebFactory::webEngineSettingChanged);
334   return act;
335 }
336 
337 #endif
338 
cookieJar() const339 CookieJar* WebFactory::cookieJar() const {
340   return m_cookieJar;
341 }
342 
generateUnescapes()343 void WebFactory::generateUnescapes() {
344   m_htmlNamedEntities[QSL("AElig")] = 0x00c6;
345   m_htmlNamedEntities[QSL("AMP")] = 38;
346   m_htmlNamedEntities[QSL("Aacute")] = 0x00c1;
347   m_htmlNamedEntities[QSL("Acirc")] = 0x00c2;
348   m_htmlNamedEntities[QSL("Agrave")] = 0x00c0;
349   m_htmlNamedEntities[QSL("Alpha")] = 0x0391;
350   m_htmlNamedEntities[QSL("Aring")] = 0x00c5;
351   m_htmlNamedEntities[QSL("Atilde")] = 0x00c3;
352   m_htmlNamedEntities[QSL("Auml")] = 0x00c4;
353   m_htmlNamedEntities[QSL("Beta")] = 0x0392;
354   m_htmlNamedEntities[QSL("Ccedil")] = 0x00c7;
355   m_htmlNamedEntities[QSL("Chi")] = 0x03a7;
356   m_htmlNamedEntities[QSL("Dagger")] = 0x2021;
357   m_htmlNamedEntities[QSL("Delta")] = 0x0394;
358   m_htmlNamedEntities[QSL("ETH")] = 0x00d0;
359   m_htmlNamedEntities[QSL("Eacute")] = 0x00c9;
360   m_htmlNamedEntities[QSL("Ecirc")] = 0x00ca;
361   m_htmlNamedEntities[QSL("Egrave")] = 0x00c8;
362   m_htmlNamedEntities[QSL("Epsilon")] = 0x0395;
363   m_htmlNamedEntities[QSL("Eta")] = 0x0397;
364   m_htmlNamedEntities[QSL("Euml")] = 0x00cb;
365   m_htmlNamedEntities[QSL("GT")] = 62;
366   m_htmlNamedEntities[QSL("Gamma")] = 0x0393;
367   m_htmlNamedEntities[QSL("Iacute")] = 0x00cd;
368   m_htmlNamedEntities[QSL("Icirc")] = 0x00ce;
369   m_htmlNamedEntities[QSL("Igrave")] = 0x00cc;
370   m_htmlNamedEntities[QSL("Iota")] = 0x0399;
371   m_htmlNamedEntities[QSL("Iuml")] = 0x00cf;
372   m_htmlNamedEntities[QSL("Kappa")] = 0x039a;
373   m_htmlNamedEntities[QSL("LT")] = 60;
374   m_htmlNamedEntities[QSL("Lambda")] = 0x039b;
375   m_htmlNamedEntities[QSL("Mu")] = 0x039c;
376   m_htmlNamedEntities[QSL("Ntilde")] = 0x00d1;
377   m_htmlNamedEntities[QSL("Nu")] = 0x039d;
378   m_htmlNamedEntities[QSL("OElig")] = 0x0152;
379   m_htmlNamedEntities[QSL("Oacute")] = 0x00d3;
380   m_htmlNamedEntities[QSL("Ocirc")] = 0x00d4;
381   m_htmlNamedEntities[QSL("Ograve")] = 0x00d2;
382   m_htmlNamedEntities[QSL("Omega")] = 0x03a9;
383   m_htmlNamedEntities[QSL("Omicron")] = 0x039f;
384   m_htmlNamedEntities[QSL("Oslash")] = 0x00d8;
385   m_htmlNamedEntities[QSL("Otilde")] = 0x00d5;
386   m_htmlNamedEntities[QSL("Ouml")] = 0x00d6;
387   m_htmlNamedEntities[QSL("Phi")] = 0x03a6;
388   m_htmlNamedEntities[QSL("Pi")] = 0x03a0;
389   m_htmlNamedEntities[QSL("Prime")] = 0x2033;
390   m_htmlNamedEntities[QSL("Psi")] = 0x03a8;
391   m_htmlNamedEntities[QSL("QUOT")] = 34;
392   m_htmlNamedEntities[QSL("Rho")] = 0x03a1;
393   m_htmlNamedEntities[QSL("Scaron")] = 0x0160;
394   m_htmlNamedEntities[QSL("Sigma")] = 0x03a3;
395   m_htmlNamedEntities[QSL("THORN")] = 0x00de;
396   m_htmlNamedEntities[QSL("Tau")] = 0x03a4;
397   m_htmlNamedEntities[QSL("Theta")] = 0x0398;
398   m_htmlNamedEntities[QSL("Uacute")] = 0x00da;
399   m_htmlNamedEntities[QSL("Ucirc")] = 0x00db;
400   m_htmlNamedEntities[QSL("Ugrave")] = 0x00d9;
401   m_htmlNamedEntities[QSL("Upsilon")] = 0x03a5;
402   m_htmlNamedEntities[QSL("Uuml")] = 0x00dc;
403   m_htmlNamedEntities[QSL("Xi")] = 0x039e;
404   m_htmlNamedEntities[QSL("Yacute")] = 0x00dd;
405   m_htmlNamedEntities[QSL("Yuml")] = 0x0178;
406   m_htmlNamedEntities[QSL("Zeta")] = 0x0396;
407   m_htmlNamedEntities[QSL("aacute")] = 0x00e1;
408   m_htmlNamedEntities[QSL("acirc")] = 0x00e2;
409   m_htmlNamedEntities[QSL("acute")] = 0x00b4;
410   m_htmlNamedEntities[QSL("aelig")] = 0x00e6;
411   m_htmlNamedEntities[QSL("agrave")] = 0x00e0;
412   m_htmlNamedEntities[QSL("alefsym")] = 0x2135;
413   m_htmlNamedEntities[QSL("alpha")] = 0x03b1;
414   m_htmlNamedEntities[QSL("amp")] = 38;
415   m_htmlNamedEntities[QSL("and")] = 0x22a5;
416   m_htmlNamedEntities[QSL("ang")] = 0x2220;
417   m_htmlNamedEntities[QSL("apos")] = 0x0027;
418   m_htmlNamedEntities[QSL("aring")] = 0x00e5;
419   m_htmlNamedEntities[QSL("asymp")] = 0x2248;
420   m_htmlNamedEntities[QSL("atilde")] = 0x00e3;
421   m_htmlNamedEntities[QSL("auml")] = 0x00e4;
422   m_htmlNamedEntities[QSL("bdquo")] = 0x201e;
423   m_htmlNamedEntities[QSL("beta")] = 0x03b2;
424   m_htmlNamedEntities[QSL("brvbar")] = 0x00a6;
425   m_htmlNamedEntities[QSL("bull")] = 0x2022;
426   m_htmlNamedEntities[QSL("cap")] = 0x2229;
427   m_htmlNamedEntities[QSL("ccedil")] = 0x00e7;
428   m_htmlNamedEntities[QSL("cedil")] = 0x00b8;
429   m_htmlNamedEntities[QSL("cent")] = 0x00a2;
430   m_htmlNamedEntities[QSL("chi")] = 0x03c7;
431   m_htmlNamedEntities[QSL("circ")] = 0x02c6;
432   m_htmlNamedEntities[QSL("clubs")] = 0x2663;
433   m_htmlNamedEntities[QSL("cong")] = 0x2245;
434   m_htmlNamedEntities[QSL("copy")] = 0x00a9;
435   m_htmlNamedEntities[QSL("crarr")] = 0x21b5;
436   m_htmlNamedEntities[QSL("cup")] = 0x222a;
437   m_htmlNamedEntities[QSL("curren")] = 0x00a4;
438   m_htmlNamedEntities[QSL("dArr")] = 0x21d3;
439   m_htmlNamedEntities[QSL("dagger")] = 0x2020;
440   m_htmlNamedEntities[QSL("darr")] = 0x2193;
441   m_htmlNamedEntities[QSL("deg")] = 0x00b0;
442   m_htmlNamedEntities[QSL("delta")] = 0x03b4;
443   m_htmlNamedEntities[QSL("diams")] = 0x2666;
444   m_htmlNamedEntities[QSL("divide")] = 0x00f7;
445   m_htmlNamedEntities[QSL("eacute")] = 0x00e9;
446   m_htmlNamedEntities[QSL("ecirc")] = 0x00ea;
447   m_htmlNamedEntities[QSL("egrave")] = 0x00e8;
448   m_htmlNamedEntities[QSL("empty")] = 0x2205;
449   m_htmlNamedEntities[QSL("emsp")] = 0x2003;
450   m_htmlNamedEntities[QSL("ensp")] = 0x2002;
451   m_htmlNamedEntities[QSL("epsilon")] = 0x03b5;
452   m_htmlNamedEntities[QSL("equiv")] = 0x2261;
453   m_htmlNamedEntities[QSL("eta")] = 0x03b7;
454   m_htmlNamedEntities[QSL("eth")] = 0x00f0;
455   m_htmlNamedEntities[QSL("euml")] = 0x00eb;
456   m_htmlNamedEntities[QSL("euro")] = 0x20ac;
457   m_htmlNamedEntities[QSL("exist")] = 0x2203;
458   m_htmlNamedEntities[QSL("fnof")] = 0x0192;
459   m_htmlNamedEntities[QSL("forall")] = 0x2200;
460   m_htmlNamedEntities[QSL("frac12")] = 0x00bd;
461   m_htmlNamedEntities[QSL("frac14")] = 0x00bc;
462   m_htmlNamedEntities[QSL("frac34")] = 0x00be;
463   m_htmlNamedEntities[QSL("frasl")] = 0x2044;
464   m_htmlNamedEntities[QSL("gamma")] = 0x03b3;
465   m_htmlNamedEntities[QSL("ge")] = 0x2265;
466   m_htmlNamedEntities[QSL("gt")] = 62;
467   m_htmlNamedEntities[QSL("hArr")] = 0x21d4;
468   m_htmlNamedEntities[QSL("harr")] = 0x2194;
469   m_htmlNamedEntities[QSL("hearts")] = 0x2665;
470   m_htmlNamedEntities[QSL("hellip")] = 0x2026;
471   m_htmlNamedEntities[QSL("iacute")] = 0x00ed;
472   m_htmlNamedEntities[QSL("icirc")] = 0x00ee;
473   m_htmlNamedEntities[QSL("iexcl")] = 0x00a1;
474   m_htmlNamedEntities[QSL("igrave")] = 0x00ec;
475   m_htmlNamedEntities[QSL("image")] = 0x2111;
476   m_htmlNamedEntities[QSL("infin")] = 0x221e;
477   m_htmlNamedEntities[QSL("int")] = 0x222b;
478   m_htmlNamedEntities[QSL("iota")] = 0x03b9;
479   m_htmlNamedEntities[QSL("iquest")] = 0x00bf;
480   m_htmlNamedEntities[QSL("isin")] = 0x2208;
481   m_htmlNamedEntities[QSL("iuml")] = 0x00ef;
482   m_htmlNamedEntities[QSL("kappa")] = 0x03ba;
483   m_htmlNamedEntities[QSL("lArr")] = 0x21d0;
484   m_htmlNamedEntities[QSL("lambda")] = 0x03bb;
485   m_htmlNamedEntities[QSL("lang")] = 0x2329;
486   m_htmlNamedEntities[QSL("laquo")] = 0x00ab;
487   m_htmlNamedEntities[QSL("larr")] = 0x2190;
488   m_htmlNamedEntities[QSL("lceil")] = 0x2308;
489   m_htmlNamedEntities[QSL("ldquo")] = 0x201c;
490   m_htmlNamedEntities[QSL("le")] = 0x2264;
491   m_htmlNamedEntities[QSL("lfloor")] = 0x230a;
492   m_htmlNamedEntities[QSL("lowast")] = 0x2217;
493   m_htmlNamedEntities[QSL("loz")] = 0x25ca;
494   m_htmlNamedEntities[QSL("lrm")] = 0x200e;
495   m_htmlNamedEntities[QSL("lsaquo")] = 0x2039;
496   m_htmlNamedEntities[QSL("lsquo")] = 0x2018;
497   m_htmlNamedEntities[QSL("lt")] = 60;
498   m_htmlNamedEntities[QSL("macr")] = 0x00af;
499   m_htmlNamedEntities[QSL("mdash")] = 0x2014;
500   m_htmlNamedEntities[QSL("micro")] = 0x00b5;
501   m_htmlNamedEntities[QSL("middot")] = 0x00b7;
502   m_htmlNamedEntities[QSL("minus")] = 0x2212;
503   m_htmlNamedEntities[QSL("mu")] = 0x03bc;
504   m_htmlNamedEntities[QSL("nabla")] = 0x2207;
505   m_htmlNamedEntities[QSL("nbsp")] = 0x00a0;
506   m_htmlNamedEntities[QSL("ndash")] = 0x2013;
507   m_htmlNamedEntities[QSL("ne")] = 0x2260;
508   m_htmlNamedEntities[QSL("ni")] = 0x220b;
509   m_htmlNamedEntities[QSL("not")] = 0x00ac;
510   m_htmlNamedEntities[QSL("notin")] = 0x2209;
511   m_htmlNamedEntities[QSL("nsub")] = 0x2284;
512   m_htmlNamedEntities[QSL("ntilde")] = 0x00f1;
513   m_htmlNamedEntities[QSL("nu")] = 0x03bd;
514   m_htmlNamedEntities[QSL("oacute")] = 0x00f3;
515   m_htmlNamedEntities[QSL("ocirc")] = 0x00f4;
516   m_htmlNamedEntities[QSL("oelig")] = 0x0153;
517   m_htmlNamedEntities[QSL("ograve")] = 0x00f2;
518   m_htmlNamedEntities[QSL("oline")] = 0x203e;
519   m_htmlNamedEntities[QSL("omega")] = 0x03c9;
520   m_htmlNamedEntities[QSL("omicron")] = 0x03bf;
521   m_htmlNamedEntities[QSL("oplus")] = 0x2295;
522   m_htmlNamedEntities[QSL("or")] = 0x22a6;
523   m_htmlNamedEntities[QSL("ordf")] = 0x00aa;
524   m_htmlNamedEntities[QSL("ordm")] = 0x00ba;
525   m_htmlNamedEntities[QSL("oslash")] = 0x00f8;
526   m_htmlNamedEntities[QSL("otilde")] = 0x00f5;
527   m_htmlNamedEntities[QSL("otimes")] = 0x2297;
528   m_htmlNamedEntities[QSL("ouml")] = 0x00f6;
529   m_htmlNamedEntities[QSL("para")] = 0x00b6;
530   m_htmlNamedEntities[QSL("part")] = 0x2202;
531   m_htmlNamedEntities[QSL("percnt")] = 0x0025;
532   m_htmlNamedEntities[QSL("permil")] = 0x2030;
533   m_htmlNamedEntities[QSL("perp")] = 0x22a5;
534   m_htmlNamedEntities[QSL("phi")] = 0x03c6;
535   m_htmlNamedEntities[QSL("pi")] = 0x03c0;
536   m_htmlNamedEntities[QSL("piv")] = 0x03d6;
537   m_htmlNamedEntities[QSL("plusmn")] = 0x00b1;
538   m_htmlNamedEntities[QSL("pound")] = 0x00a3;
539   m_htmlNamedEntities[QSL("prime")] = 0x2032;
540   m_htmlNamedEntities[QSL("prod")] = 0x220f;
541   m_htmlNamedEntities[QSL("prop")] = 0x221d;
542   m_htmlNamedEntities[QSL("psi")] = 0x03c8;
543   m_htmlNamedEntities[QSL("quot")] = 34;
544   m_htmlNamedEntities[QSL("rArr")] = 0x21d2;
545   m_htmlNamedEntities[QSL("radic")] = 0x221a;
546   m_htmlNamedEntities[QSL("rang")] = 0x232a;
547   m_htmlNamedEntities[QSL("raquo")] = 0x00bb;
548   m_htmlNamedEntities[QSL("rarr")] = 0x2192;
549   m_htmlNamedEntities[QSL("rceil")] = 0x2309;
550   m_htmlNamedEntities[QSL("rdquo")] = 0x201d;
551   m_htmlNamedEntities[QSL("real")] = 0x211c;
552   m_htmlNamedEntities[QSL("reg")] = 0x00ae;
553   m_htmlNamedEntities[QSL("rfloor")] = 0x230b;
554   m_htmlNamedEntities[QSL("rho")] = 0x03c1;
555   m_htmlNamedEntities[QSL("rlm")] = 0x200f;
556   m_htmlNamedEntities[QSL("rsaquo")] = 0x203a;
557   m_htmlNamedEntities[QSL("rsquo")] = 0x2019;
558   m_htmlNamedEntities[QSL("sbquo")] = 0x201a;
559   m_htmlNamedEntities[QSL("scaron")] = 0x0161;
560   m_htmlNamedEntities[QSL("sdot")] = 0x22c5;
561   m_htmlNamedEntities[QSL("sect")] = 0x00a7;
562   m_htmlNamedEntities[QSL("shy")] = 0x00ad;
563   m_htmlNamedEntities[QSL("sigma")] = 0x03c3;
564   m_htmlNamedEntities[QSL("sigmaf")] = 0x03c2;
565   m_htmlNamedEntities[QSL("sim")] = 0x223c;
566   m_htmlNamedEntities[QSL("spades")] = 0x2660;
567   m_htmlNamedEntities[QSL("sub")] = 0x2282;
568   m_htmlNamedEntities[QSL("sube")] = 0x2286;
569   m_htmlNamedEntities[QSL("sum")] = 0x2211;
570   m_htmlNamedEntities[QSL("sup")] = 0x2283;
571   m_htmlNamedEntities[QSL("sup1")] = 0x00b9;
572   m_htmlNamedEntities[QSL("sup2")] = 0x00b2;
573   m_htmlNamedEntities[QSL("sup3")] = 0x00b3;
574   m_htmlNamedEntities[QSL("supe")] = 0x2287;
575   m_htmlNamedEntities[QSL("szlig")] = 0x00df;
576   m_htmlNamedEntities[QSL("tau")] = 0x03c4;
577   m_htmlNamedEntities[QSL("there4")] = 0x2234;
578   m_htmlNamedEntities[QSL("theta")] = 0x03b8;
579   m_htmlNamedEntities[QSL("thetasym")] = 0x03d1;
580   m_htmlNamedEntities[QSL("thinsp")] = 0x2009;
581   m_htmlNamedEntities[QSL("thorn")] = 0x00fe;
582   m_htmlNamedEntities[QSL("tilde")] = 0x02dc;
583   m_htmlNamedEntities[QSL("times")] = 0x00d7;
584   m_htmlNamedEntities[QSL("trade")] = 0x2122;
585   m_htmlNamedEntities[QSL("uArr")] = 0x21d1;
586   m_htmlNamedEntities[QSL("uacute")] = 0x00fa;
587   m_htmlNamedEntities[QSL("uarr")] = 0x2191;
588   m_htmlNamedEntities[QSL("ucirc")] = 0x00fb;
589   m_htmlNamedEntities[QSL("ugrave")] = 0x00f9;
590   m_htmlNamedEntities[QSL("uml")] = 0x00a8;
591   m_htmlNamedEntities[QSL("upsih")] = 0x03d2;
592   m_htmlNamedEntities[QSL("upsilon")] = 0x03c5;
593   m_htmlNamedEntities[QSL("uuml")] = 0x00fc;
594   m_htmlNamedEntities[QSL("weierp")] = 0x2118;
595   m_htmlNamedEntities[QSL("xi")] = 0x03be;
596   m_htmlNamedEntities[QSL("yacute")] = 0x00fd;
597   m_htmlNamedEntities[QSL("yen")] = 0x00a5;
598   m_htmlNamedEntities[QSL("yuml")] = 0x00ff;
599   m_htmlNamedEntities[QSL("zeta")] = 0x03b6;
600   m_htmlNamedEntities[QSL("zwj")] = 0x200d;
601   m_htmlNamedEntities[QSL("zwnj")] = 0x200c;
602 }
603