LCOV - code coverage report
Current view: top level - snapwebsites - snap_child.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1 2364 0.1 %
Date: 2019-12-15 17:13:15 Functions: 2 155 1.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Snap Websites Server -- snap websites serving children
       2             : // Copyright (c) 2011-2019  Made to Order Software Corp.  All Rights Reserved
       3             : //
       4             : // https://snapwebsites.org/
       5             : // contact@m2osw.com
       6             : //
       7             : // This program is free software; you can redistribute it and/or modify
       8             : // it under the terms of the GNU General Public License as published by
       9             : // the Free Software Foundation; either version 2 of the License, or
      10             : // (at your option) any later version.
      11             : //
      12             : // This program is distributed in the hope that it will be useful,
      13             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15             : // GNU General Public License for more details.
      16             : //
      17             : // You should have received a copy of the GNU General Public License
      18             : // along with this program; if not, write to the Free Software
      19             : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
      20             : 
      21             : 
      22             : // self
      23             : //
      24             : #include "snapwebsites/snap_child.h"
      25             : 
      26             : 
      27             : // snapwebsites lib
      28             : //
      29             : #include "snapwebsites/compression.h"
      30             : #include "snapwebsites/flags.h"
      31             : #include "snapwebsites/http_strings.h"
      32             : #include "snapwebsites/log.h"
      33             : #include "snapwebsites/mail_exchanger.h"
      34             : #include "snapwebsites/mkgmtime.h"
      35             : #include "snapwebsites/process.h"
      36             : #include "snapwebsites/qdomhelpers.h"
      37             : #include "snapwebsites/qlockfile.h"
      38             : #include "snapwebsites/snap_image.h"
      39             : #include "snapwebsites/snapwebsites.h"
      40             : #include "snapwebsites/snap_lock.h"
      41             : #include "snapwebsites/snap_magic.h"
      42             : 
      43             : 
      44             : // snapdev lib
      45             : //
      46             : #include <snapdev/not_used.h>
      47             : 
      48             : 
      49             : // dbproxy lib
      50             : //
      51             : #include <libdbproxy/exception.h>
      52             : 
      53             : 
      54             : // Qt Serialization lib
      55             : //
      56             : #include <QtSerialization/QSerialization.h>
      57             : 
      58             : 
      59             : // libutf8 lib
      60             : //
      61             : #include <libutf8/libutf8.h>
      62             : 
      63             : 
      64             : // tld lib
      65             : //
      66             : #include <libtld/tld.h>
      67             : 
      68             : 
      69             : // C++ lib
      70             : //
      71             : #include <sstream>
      72             : 
      73             : 
      74             : // C lib
      75             : //
      76             : #include <errno.h>
      77             : #include <signal.h>
      78             : #include <stdio.h>
      79             : #include <wait.h>
      80             : #include <sys/prctl.h>
      81             : #include <sys/syscall.h>
      82             : 
      83             : 
      84             : // Qt lib
      85             : //
      86             : #include <QDirIterator>
      87             : 
      88             : 
      89             : // last include
      90             : //
      91             : #include <snapdev/poison.h>
      92             : 
      93             : 
      94             : 
      95             : 
      96             : namespace snap
      97             : {
      98             : 
      99             : 
     100             : 
     101             : 
     102             : /** \class snap_child
     103             :  * \brief Child process class.
     104             :  *
     105             :  * This class handles child objects that process queries from the Snap
     106             :  * CGI tool.
     107             :  *
     108             :  * The children appear in the Snap Server and themselves. The server is
     109             :  * the parent that handles the lifetime of the child. The parent also
     110             :  * holds the child process identifier and it waits on the child for its
     111             :  * death.
     112             :  *
     113             :  * The child itself has its f_child_pid set to zero.
     114             :  *
     115             :  * Some of the functions will react with an error if called from the
     116             :  * wrong process (i.e. parent calling a child process function and vice
     117             :  * versa.)
     118             :  */
     119             : 
     120             : /** \fn int64_t get_start_date() const
     121             :  * \brief Retrieve the date when the child process started.
     122             :  *
     123             :  * This function returns the date, in micro seconds (seconds x 1,000,000)
     124             :  * when the child was forked from the server.
     125             :  *
     126             :  * In some situation, it may be useful to reset this time to the clock.
     127             :  * In most cases this is done in backends. This is done by calling
     128             :  * init_start_date().
     129             :  *
     130             :  * \sa init_start_date()
     131             :  * \sa get_start_time()
     132             :  */
     133             : 
     134             : /** \fn time_t get_start_time() const
     135             :  * \brief Retrieve the date when the child process started in seconds.
     136             :  *
     137             :  * This function returns the date in seconds (same as a Unix date)
     138             :  * when the child was forked from the server.
     139             :  *
     140             :  * This is the same date as get_start_date() would returned, divided
     141             :  * by 1 million (rounded down).
     142             :  *
     143             :  * \sa get_start_date()
     144             :  */
     145             : 
     146             : namespace
     147             : {
     148             : 
     149             : 
     150             : /** \brief Retrieve the current thread identifier.
     151             :  *
     152             :  * This function retrieves the current thread identifier.
     153             :  *
     154             :  * \return The thread identifier, which is a pid_t specific to each thread
     155             :  *         of a process.
     156             :  */
     157           0 : pid_t gettid()
     158             : {
     159           0 :     return syscall(SYS_gettid);
     160             : }
     161             : 
     162             : 
     163             : // list of plugins that we cannot do without
     164             : char const * g_minimum_plugins[] =
     165             : {
     166             :     "attachment",
     167             :     "content",
     168             :     "editor",
     169             :     "filter",
     170             :     "form", // this one will be removed completely (phased out)
     171             :     "info",
     172             :     "javascript",
     173             :     "layout",
     174             :     "links",
     175             :     "list",
     176             :     "listener",
     177             :     "locale",
     178             :     "menu",
     179             :     "messages",
     180             :     "mimetype", // this should not be required
     181             :     "output",
     182             :     "password",
     183             :     "path",
     184             :     "permissions",
     185             :     "sendmail",
     186             :     "server_access",
     187             :     "sessions",
     188             :     "taxonomy",
     189             :     "users",
     190             :     "users_ui"
     191             : };
     192             : 
     193             : char const * g_week_day_name[] =
     194             : {
     195             :     "Sunday", "Monday", "Tuesday", "Wedneday", "Thursday", "Friday", "Saturday"
     196             : };
     197             : int const g_week_day_length[] = { 6, 6, 7, 8, 8, 6, 8 }; // strlen() of g_week_day_name's
     198             : char const * g_month_name[] =
     199             : {
     200             :     "January", "February", "Marsh", "April", "May", "June",
     201             :     "July", "August", "September", "October", "November", "December"
     202             : };
     203             : int const g_month_length[] = { 7, 8, 5, 5, 3, 4, 4, 6, 9, 7, 8, 8 }; // strlen() of g_month_name
     204             : int const g_month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
     205             : signed char const g_timezone_adjust[26] =
     206             : {
     207             :     /* A */ -1,
     208             :     /* B */ -2,
     209             :     /* C */ -3,
     210             :     /* D */ -4,
     211             :     /* E */ -5,
     212             :     /* F */ -6,
     213             :     /* G */ -7,
     214             :     /* H */ -8,
     215             :     /* I */ -9,
     216             :     /* J */ 0, // not used
     217             :     /* K */ -10,
     218             :     /* L */ -11,
     219             :     /* M */ -12,
     220             :     /* N */ 1,
     221             :     /* O */ 2,
     222             :     /* P */ 3,
     223             :     /* Q */ 4,
     224             :     /* R */ 5,
     225             :     /* S */ 6,
     226             :     /* T */ 7,
     227             :     /* U */ 8,
     228             :     /* V */ 9,
     229             :     /* W */ 10,
     230             :     /* X */ 11,
     231             :     /* Y */ 12,
     232             :     /* Z */ 0, // Zulu time is zero
     233             : };
     234             : 
     235             : 
     236             : 
     237             : // valid language names
     238             : // a full language definition is xx_YY where xx is the two letter name
     239             : // of a language and YY is a two letter name of a country; we also support
     240             : // 3 letter language names, and full country names (for now)
     241             : snap_child::language_name_t const g_language_names[] =
     242             : {
     243             :     {
     244             :         "Abkhaz",
     245             :         u8"\u0430\u04A7\u0441\u0443\u0430 \u0431\u044B\u0437\u0448\u04D9\u0430, \u0430\u04A7\u0441\u0448\u04D9\u0430",
     246             :         { 'a', 'b', '\0' },
     247             :         ",abk,abks,"
     248             :     },
     249             :     {
     250             :         "Afar",
     251             :         u8"Afaraf",
     252             :         { 'a', 'a', '\0' },
     253             :         ",aar,aars,"
     254             :     },
     255             :     {
     256             :         "Afrikaans",
     257             :         u8"Afrikaans",
     258             :         { 'a', 'f', '\0' },
     259             :         ",afr,afrs,"
     260             :     },
     261             :     {
     262             :         "Akan",
     263             :         u8"Akan",
     264             :         { 'a', 'k', '\0' },
     265             :         ",aka,"
     266             :     },
     267             :     {
     268             :         "Albanian",
     269             :         u8"gjuha shqipe",
     270             :         { 's', 'q', '\0' },
     271             :         ",sqi,alb,"
     272             :     },
     273             :     {
     274             :         "Amharic",
     275             :         u8"\u12A0\u121B\u122D\u129B",
     276             :         { 'a', 'm', '\0' },
     277             :         ",amh,"
     278             :     },
     279             :     {
     280             :         "Arabic",
     281             :         u8"\u0627\u0644\u0639\u0631\u0628\u064A\u0629",
     282             :         { 'a', 'r', '\0' },
     283             :         ",ara,"
     284             :     },
     285             :     {
     286             :         "Aragonese",
     287             :         u8"aragon\u00E9s",
     288             :         { 'a', 'n', '\0' },
     289             :         ",arg,"
     290             :     },
     291             :     {
     292             :         "Armenian",
     293             :         u8"\u0540\u0561\u0575\u0565\u0580\u0565\u0576",
     294             :         { 'h', 'y', '\0' },
     295             :         ",hye,arm,"
     296             :     },
     297             :     {
     298             :         "Assamese",
     299             :         u8"\u0985\u09B8\u09AE\u09C0\u09AF\u09BC\u09BE",
     300             :         { 'a', 's', '\0' },
     301             :         ",asm,"
     302             :     },
     303             :     {
     304             :         "Avaric",
     305             :         u8"\u0430\u0432\u0430\u0440 \u043C\u0430\u0446\u04C0, \u043C\u0430\u0433\u04C0\u0430\u0440\u0443\u043B \u043C\u0430\u0446\u04C0",
     306             :         { 'a', 'v', '\0' },
     307             :         ",ava,"
     308             :     },
     309             :     {
     310             :         "Avestan",
     311             :         u8"avesta",
     312             :         { 'a', 'e', '\0' },
     313             :         ",ave,"
     314             :     },
     315             :     {
     316             :         "Aymara",
     317             :         u8"aymar aru",
     318             :         { 'a', 'y', '\0' },
     319             :         ",aym,"
     320             :     },
     321             :     {
     322             :         "Azerbaijani",
     323             :         u8"az\u0259rbaycan dili",
     324             :         { 'a', 'z', '\0' },
     325             :         ",aze,"
     326             :     },
     327             :     {
     328             :         "Bambara",
     329             :         u8"bamanankan",
     330             :         { 'b', 'm', '\0' },
     331             :         ",bam,"
     332             :     },
     333             :     {
     334             :         "Bashkir",
     335             :         u8"\u0431\u0430\u0448\u04A1\u043E\u0440\u0442 \u0442\u0435\u043B\u0435",
     336             :         { 'b', 'a', '\0' },
     337             :         ",bak,"
     338             :     },
     339             :     {
     340             :         "Basque",
     341             :         u8"euskara",
     342             :         { 'e', 'u', '\0' },
     343             :         ",eus,baq,"
     344             :     },
     345             :     {
     346             :         "Belarusian",
     347             :         u8"\u0431\u0435\u043B\u0430\u0440\u0443\u0441\u043A\u0430\u044F \u043C\u043E\u0432\u0430",
     348             :         { 'b', 'e', '\0' },
     349             :         ",bel,"
     350             :     },
     351             :     {
     352             :         "Bengali",
     353             :         u8"\u09AC\u09BE\u0982\u09B2\u09BE",
     354             :         { 'b', 'n', '\0' },
     355             :         ",ben,"
     356             :     },
     357             :     {
     358             :         "Bihari",
     359             :         u8"\u092D\u094B\u091C\u092A\u0941\u0930\u0940",
     360             :         { 'b', 'h', '\0' },
     361             :         ",bih,"
     362             :     },
     363             :     {
     364             :         "Bislama",
     365             :         u8"Bislama",
     366             :         { 'b', 'i', '\0' },
     367             :         ",bis,"
     368             :     },
     369             :     {
     370             :         "Bosnian",
     371             :         u8"bosanski jezik",
     372             :         { 'b', 's', '\0' },
     373             :         ",bos,boss,"
     374             :     },
     375             :     {
     376             :         "Breton",
     377             :         u8"brezhoneg",
     378             :         { 'b', 'r', '\0' },
     379             :         ",bre,"
     380             :     },
     381             :     {
     382             :         "Bulgarian",
     383             :         u8"\u0431\u044A\u043B\u0433\u0430\u0440\u0441\u043A\u0438 \u0435\u0437\u0438\u043A",
     384             :         { 'b', 'g', '\0' },
     385             :         ",bul,buls,"
     386             :     },
     387             :     {
     388             :         "Burmese",
     389             :         u8"\u1017\u1019\u102C\u1005\u102C",
     390             :         { 'm', 'y', '\0' },
     391             :         ",mya,bur,"
     392             :     },
     393             :     {
     394             :         "Catalan; Valencian",
     395             :         u8"catal\u00E0, valanci\u00E0",
     396             :         { 'c', 'a', '\0' },
     397             :         ",cat,"
     398             :     },
     399             :     {
     400             :         "Chamorro",
     401             :         u8"Chamoru",
     402             :         { 'c', 'h', '\0' },
     403             :         ",cha,"
     404             :     },
     405             :     {
     406             :         "Chechen",
     407             :         u8"\u043D\u043E\u0445\u0447\u0438\u0439\u043D \u043C\u043E\u0442\u0442",
     408             :         { 'c', 'e', '\0' },
     409             :         ",che,"
     410             :     },
     411             :     {
     412             :         "Chichewa; Chewa; Nyanja",
     413             :         u8"chiChe\u0175a, chinyanja",
     414             :         { 'n', 'y', '\0' },
     415             :         ",nya,"
     416             :     },
     417             :     {
     418             :         "Chinese",
     419             :         u8"\u4E2D\u6587 (Zh\u014Dngw\u00E9n), \u6C49\u8BED, \u6F22\u8A9E",
     420             :         { 'z', 'h', '\0' },
     421             :         ",zho,chi,"
     422             :     },
     423             :     {
     424             :         "Chuvash",
     425             :         u8"\u0447\u04D1\u0432\u0430\u0448 \u0447\u04D7\u043B\u0445\u0438",
     426             :         { 'c', 'v', '\0' },
     427             :         ",chv,"
     428             :     },
     429             :     {
     430             :         "Cornish",
     431             :         u8"Kernewek",
     432             :         { 'k', 'w', '\0' },
     433             :         ",cor,"
     434             :     },
     435             :     {
     436             :         "Corsican",
     437             :         u8"corsu, lingua corsa",
     438             :         { 'c', 'o', '\0' },
     439             :         ",cos,"
     440             :     },
     441             :     {
     442             :         "Cree",
     443             :         u8"\u14C0\u1426\u1403\u152D\u140D\u140F\u1423",
     444             :         { 'c', 'r', '\0' },
     445             :         ",cre,"
     446             :     },
     447             :     {
     448             :         "Croatian",
     449             :         u8"hrvatski jezik",
     450             :         { 'h', 'r', '\0' },
     451             :         ",hrv,"
     452             :     },
     453             :     {
     454             :         "Czech",
     455             :         u8"\u010De\u0161tina, \u010Desk\u00FD jazyk",
     456             :         { 'c', 's', '\0' },
     457             :         ",ces,cze,"
     458             :     },
     459             :     {
     460             :         "Danish",
     461             :         u8"dansk",
     462             :         { 'd', 'a', '\0' },
     463             :         ",dan,"
     464             :     },
     465             :     {
     466             :         "Divehi; Dhivehi; Maldivian",
     467             :         u8"\u078B\u07A8\u0788\u07AC\u0780\u07A8",
     468             :         { 'd', 'v', '\0' },
     469             :         ",div,"
     470             :     },
     471             :     {
     472             :         "Dutch",
     473             :         u8"Nederlands, Vlaams",
     474             :         { 'n', 'l', '\0' },
     475             :         ",nld,dut,"
     476             :     },
     477             :     {
     478             :         "Dzongkha",
     479             :         u8"\u0F62\u0FAB\u0F7C\u0F44\u0F0B\u0F41",
     480             :         { 'd', 'z', '\0' },
     481             :         ",dzo,"
     482             :     },
     483             :     {
     484             :         "English",
     485             :         u8"English",
     486             :         { 'e', 'n', '\0' },
     487             :         ",eng,engs,"
     488             :     },
     489             :     {
     490             :         "Esperanto",
     491             :         u8"Esperanto",
     492             :         { 'e', 'o', '\0' },
     493             :         ",epo,"
     494             :     },
     495             :     {
     496             :         "Estonian",
     497             :         u8"eesti, eesti keel",
     498             :         { 'e', 't', '\0' },
     499             :         ",est,"
     500             :     },
     501             :     {
     502             :         "Ewe",
     503             :         u8"E\u028Begbe",
     504             :         { 'e', 'e', '\0' },
     505             :         ",ewe,"
     506             :     },
     507             :     {
     508             :         "Faroese",
     509             :         u8"f\u00F8royskt",
     510             :         { 'f', 'o', '\0' },
     511             :         ",fao,"
     512             :     },
     513             :     {
     514             :         "Fijian",
     515             :         u8"vosa Vakaviti",
     516             :         { 'f', 'j', '\0' },
     517             :         ",fij,"
     518             :     },
     519             :     {
     520             :         "Finnish",
     521             :         u8"suomi, suomen kieli",
     522             :         { 'f', 'i', '\0' },
     523             :         ",fin,"
     524             :     },
     525             :     {
     526             :         "French",
     527             :         u8"fran\u00E7ais, langue fran\u00E7aise",
     528             :         { 'f', 'r', '\0' },
     529             :         ",fra,fras,"
     530             :     },
     531             :     {
     532             :         "Fula; Fulah; Pulaar; Pular",
     533             :         u8"Fulfulde, Pulaar, Pular",
     534             :         { 'f', 'f', '\0' },
     535             :         ",ful,"
     536             :     },
     537             :     {
     538             :         "Galician",
     539             :         u8"galego",
     540             :         { 'g', 'l', '\0' },
     541             :         ",glg,"
     542             :     },
     543             :     {
     544             :         "Georgian",
     545             :         u8"\u10E5\u10D0\u10E0\u10D7\u10E3\u10DA\u10D8",
     546             :         { 'k', 'a', '\0' },
     547             :         ",kat,geo,"
     548             :     },
     549             :     {
     550             :         "German",
     551             :         u8"Deutsch",
     552             :         { 'd', 'e', '\0' },
     553             :         ",deu,ger,deus,"
     554             :     },
     555             :     {
     556             :         "Greek, Modern",
     557             :         u8"\u03B5\u03BB\u03BB\u03B7\u03BD\u03B9\u03BA\u03AC",
     558             :         { 'e', 'l', '\0' },
     559             :         ",ell,gre,ells,"
     560             :     },
     561             :     {
     562             :         u8"Guaran\u00ED",
     563             :         u8"Ava\u00F1e'\u1EBD",
     564             :         { 'g', 'n', '\0' },
     565             :         ",grn,"
     566             :     },
     567             :     {
     568             :         "Gujarati",
     569             :         u8"\u0A97\u0AC1\u0A9C\u0AB0\u0ABE\u0AA4\u0AC0",
     570             :         { 'g', 'u', '\0' },
     571             :         ",guj,"
     572             :     },
     573             :     {
     574             :         "Haitian; Haitian Creole",
     575             :         u8"Krey\u00F2l ayisyen",
     576             :         { 'h', 't', '\0' },
     577             :         ",hat,"
     578             :     },
     579             :     {
     580             :         "Hausa",
     581             :         u8"Hause, \u0647\u064E\u0648\u064F\u0633\u064E",
     582             :         { 'h', 'a', '\0' },
     583             :         ",hau,"
     584             :     },
     585             :     {
     586             :         "Hebrew (modern)",
     587             :         u8"\u05E2\u05D1\u05E8\u05D9\u05EA",
     588             :         { 'h', 'e', '\0' },
     589             :         ",heb,"
     590             :     },
     591             :     {
     592             :         "Herero",
     593             :         u8"Otjiherero",
     594             :         { 'h', 'z', '\0' },
     595             :         ",her,"
     596             :     },
     597             :     {
     598             :         "Hindi",
     599             :         u8"\u0939\u093F\u0928\u094D\u0926\u0940, \u0939\u093F\u0902\u0926\u0940",
     600             :         { 'h', 'i', '\0' },
     601             :         ",hin,"
     602             :     },
     603             :     {
     604             :         "Hiri Motu",
     605             :         u8"Hiri Motu",
     606             :         { 'h', 'o', '\0' },
     607             :         ",hmo,"
     608             :     },
     609             :     {
     610             :         "Hungarian",
     611             :         u8"magyar",
     612             :         { 'h', 'u', '\0' },
     613             :         ",hun,"
     614             :     },
     615             :     {
     616             :         "Interlingua",
     617             :         u8"Interlingua",
     618             :         { 'i', 'a', '\0' },
     619             :         ",ina,"
     620             :     },
     621             :     {
     622             :         "Indonesian",
     623             :         u8"Bahasa Indonesia",
     624             :         { 'i', 'd', '\0' },
     625             :         ",ind,"
     626             :     },
     627             :     {
     628             :         "Interlingue",
     629             :         u8"Interlingue",
     630             :         { 'i', 'e', '\0' },
     631             :         ",ile,"
     632             :     },
     633             :     {
     634             :         "Irish",
     635             :         u8"Gaeilge",
     636             :         { 'g', 'a', '\0' },
     637             :         ",gle,"
     638             :     },
     639             :     {
     640             :         "Igbo",
     641             :         u8"As\u1E85s\u1E85 Igbo",
     642             :         { 'i', 'g', '\0' },
     643             :         ",ibo,"
     644             :     },
     645             :     {
     646             :         "Inupiaq",
     647             :         u8"I\u00F1upiaq, I\u00F1upiatun",
     648             :         { 'i', 'k', '\0' },
     649             :         ",ipk,"
     650             :     },
     651             :     {
     652             :         "Ido",
     653             :         u8"Ido",
     654             :         { 'i', 'o', '\0' },
     655             :         ",ido,"
     656             :     },
     657             :     {
     658             :         "Icelandic",
     659             :         u8"\u00CDslenska",
     660             :         { 'i', 's', '\0' },
     661             :         ",isl,ice,"
     662             :     },
     663             :     {
     664             :         "Italian",
     665             :         u8"italiano",
     666             :         { 'i', 't', '\0' },
     667             :         ",ita,itas,"
     668             :     },
     669             :     {
     670             :         "Inuktitut",
     671             :         u8"\u1403\u14C4\u1483\u144E\u1450\u1466",
     672             :         { 'i', 'u', '\0' },
     673             :         ",iku,"
     674             :     },
     675             :     {
     676             :         "Japanese",
     677             :         u8"\u65E5\u672C\u8A9E (\u306B\u307B\u3093\u3054)",
     678             :         { 'j', 'a', '\0' },
     679             :         ",jpn,"
     680             :     },
     681             :     {
     682             :         "Javanese",
     683             :         u8"basa Jawa",
     684             :         { 'j', 'v', '\0' },
     685             :         ",jav,"
     686             :     },
     687             :     {
     688             :         "Kalaallisut, Greenlandic",
     689             :         u8"kalaallisut, kalaallit oqaasii",
     690             :         { 'k', 'l', '\0' },
     691             :         ",kal,"
     692             :     },
     693             :     {
     694             :         "Kannada",
     695             :         u8"\u0C95\u0CA8\u0CCD\u0CA8\u0CA1",
     696             :         { 'k', 'n', '\0' },
     697             :         ",kan,"
     698             :     },
     699             :     {
     700             :         "Kanuri",
     701             :         u8"Kanuri",
     702             :         { 'k', 'r', '\0' },
     703             :         ",kau,"
     704             :     },
     705             :     {
     706             :         "Kashmiri",
     707             :         u8"\u0915\u0936\u094D\u092E\u0940\u0930\u0940, \u0643\u0634\u0645\u064A\u0631\u064A",
     708             :         { 'k', 's', '\0' },
     709             :         ",kas,"
     710             :     },
     711             :     {
     712             :         "Kazakh",
     713             :         u8"\u049B\u0430\u0437\u0430\u049B \u0442\u0456\u043B\u0456",
     714             :         { 'k', 'k', '\0' },
     715             :         ",kaz,"
     716             :     },
     717             :     {
     718             :         "Khmer",
     719             :         u8"\u1781\u17D2\u1798\u17C2\u179A, \u1781\u17C1\u1798\u179A\u1797\u17B6\u179F\u17B6, \u1797\u17B6\u179F\u17B6\u1781\u17D2\u1798\u17C2\u179A",
     720             :         { 'k', 'm', '\0' },
     721             :         ",khm,"
     722             :     },
     723             :     {
     724             :         "Kikuyu, Gikuyu",
     725             :         u8"G\u0129k\u0169y\u0169",
     726             :         { 'k', 'i', '\0' },
     727             :         ",kik,"
     728             :     },
     729             :     {
     730             :         "Kinyarwanda",
     731             :         u8"Ikinyarwanda",
     732             :         { 'r', 'w', '\0' },
     733             :         ",kin,"
     734             :     },
     735             :     {
     736             :         "Kyrgyz",
     737             :         u8"\u041A\u044B\u0440\u0433\u044B\u0437\u0447\u0430, \u041A\u044B\u0440\u0433\u044B\u0437 \u0442\u0438\u043B\u0438",
     738             :         { 'k', 'y', '\0' },
     739             :         ",kir,"
     740             :     },
     741             :     {
     742             :         "Komi",
     743             :         u8"\u043A\u043E\u043C\u0438 \u043A\u044B\u0432",
     744             :         { 'k', 'v', '\0' },
     745             :         ",kom,"
     746             :     },
     747             :     {
     748             :         "Kongo",
     749             :         u8"KiKongo",
     750             :         { 'k', 'g', '\0' },
     751             :         ",kon,"
     752             :     },
     753             :     {
     754             :         "Korean",
     755             :         u8"\uD55C\uAD6D\uC5B4 (\u97D3\u570B\u8A9E), \uC870\uC120\uC5B4 (\u671D\u9BAE\u8A9E)",
     756             :         { 'k', 'o', '\0' },
     757             :         ",kor,"
     758             :     },
     759             :     {
     760             :         "Kurdish",
     761             :         u8"Kurd\u00EE, \u0643\u0648\u0631\u062F\u06CC",
     762             :         { 'k', 'u', '\0' },
     763             :         ",kur,"
     764             :     },
     765             :     {
     766             :         "Kwanyama, Kuanyama",
     767             :         u8"Kuanyama",
     768             :         { 'k', 'j', '\0' },
     769             :         ",kua,"
     770             :     },
     771             :     {
     772             :         "Latin",
     773             :         u8"latine, lingua latina",
     774             :         { 'l', 'a', '\0' },
     775             :         ",lat,lats,"
     776             :     },
     777             :     {
     778             :         "Luxembourgish, Letzeburgesch",
     779             :         u8"L\u00EBtzebuergesch",
     780             :         { 'l', 'b', '\0' },
     781             :         ",ltz,"
     782             :     },
     783             :     {
     784             :         "Ganda",
     785             :         u8"Luganda",
     786             :         { 'l', 'g', '\0' },
     787             :         ",lug,"
     788             :     },
     789             :     {
     790             :         "Limburgish, Limburgan, Limburger",
     791             :         u8"Limburgs",
     792             :         { 'l', 'i', '\0' },
     793             :         ",lim,"
     794             :     },
     795             :     {
     796             :         "Lingala",
     797             :         u8"Ling\u00E1la",
     798             :         { 'l', 'n', '\0' },
     799             :         ",lin,"
     800             :     },
     801             :     {
     802             :         "Lao",
     803             :         u8"\u0E9E\u0EB2\u0EAA\u0EB2\u0EA5\u0EB2\u0EA7",
     804             :         { 'l', 'o', '\0' },
     805             :         ",lao,"
     806             :     },
     807             :     {
     808             :         "Lithuanian",
     809             :         u8"lietuvi\u0173 kalba",
     810             :         { 'l', 't', '\0' },
     811             :         ",lit,"
     812             :     },
     813             :     {
     814             :         "Luba-Katanga",
     815             :         u8"Tshiluba",
     816             :         { 'l', 'u', '\0' },
     817             :         ",lub,"
     818             :     },
     819             :     {
     820             :         "Latvian",
     821             :         u8"latvie\u0161u valoda",
     822             :         { 'l', 'v', '\0' },
     823             :         ",lav,"
     824             :     },
     825             :     {
     826             :         "Manx",
     827             :         u8"Gaelg, Gailck",
     828             :         { 'g', 'v', '\0' },
     829             :         ",glv,"
     830             :     },
     831             :     {
     832             :         "Macedonian",
     833             :         u8"\u043C\u0430\u043A\u0435\u0434\u043E\u043D\u0441\u043A\u0438 \u0458\u0430\u0437\u0438\u043A",
     834             :         { 'm', 'k', '\0' },
     835             :         ",mkd,mac,"
     836             :     },
     837             :     {
     838             :         "Malagasy",
     839             :         u8"fiteny malagasy",
     840             :         { 'm', 'g', '\0' },
     841             :         ",mlg,"
     842             :     },
     843             :     {
     844             :         "Malay",
     845             :         u8"bahasa Melayu, \u0628\u0647\u0627\u0633 \u0645\u0644\u0627\u064A\u0648",
     846             :         { 'm', 's', '\0' },
     847             :         ",msa,may,"
     848             :     },
     849             :     {
     850             :         "Malayalam",
     851             :         u8"\u0D2E\u0D32\u0D2F\u0D3E\u0D33\u0D02",
     852             :         { 'm', 'l', '\0' },
     853             :         ",mal,"
     854             :     },
     855             :     {
     856             :         "Maltese",
     857             :         u8"Malti",
     858             :         { 'm', 't', '\0' },
     859             :         ",mlt,"
     860             :     },
     861             :     {
     862             :         u8"M\u0101ori",
     863             :         u8"te reo M\u0101ori",
     864             :         { 'm', 'i', '\0' },
     865             :         ",mri,mao,"
     866             :     },
     867             :     {
     868             :         u8"Marathi (Mar\u0101\u1E6Dh\u012B)",
     869             :         u8"\u092E\u0930\u093E\u0920\u0940",
     870             :         { 'm', 'r', '\0' },
     871             :         ",mar,"
     872             :     },
     873             :     {
     874             :         "Marshallese",
     875             :         u8"Kajin M\u0327aje\u013C",
     876             :         { 'm', 'h', '\0' },
     877             :         ",mah,"
     878             :     },
     879             :     {
     880             :         "Mongolian",
     881             :         u8"\u043C\u043E\u043D\u0433\u043E\u043B",
     882             :         { 'm', 'n', '\0' },
     883             :         ",mon,"
     884             :     },
     885             :     {
     886             :         "Nauru",
     887             :         u8"Ekakair\u0169 Naoero",
     888             :         { 'n', 'a', '\0' },
     889             :         ",nau,"
     890             :     },
     891             :     {
     892             :         "Navajo, Navaho",
     893             :         u8"Din\u00E9 bizaad, Din\u00E9k\u02BCeh\u01F0\u00ED",
     894             :         { 'n', 'v', '\0' },
     895             :         ",nav,"
     896             :     },
     897             :     {
     898             :         "Norwegian Bokm\u00E5l",
     899             :         u8"Norsk bokm\u00E5l",
     900             :         { 'n', 'b', '\0' },
     901             :         ",nob,"
     902             :     },
     903             :     {
     904             :         "North Ndebele",
     905             :         u8"isiNdebele",
     906             :         { 'n', 'd', '\0' },
     907             :         ",nde,"
     908             :     },
     909             :     {
     910             :         "Nepali",
     911             :         u8"\u0928\u0947\u092A\u093E\u0932\u0940",
     912             :         { 'n', 'e', '\0' },
     913             :         ",nep,"
     914             :     },
     915             :     {
     916             :         "Ndonga",
     917             :         u8"Owambo",
     918             :         { 'n', 'g', '\0' },
     919             :         ",ndo,"
     920             :     },
     921             :     {
     922             :         "Norwegian Nynorsk",
     923             :         u8"Norsk nynorsk",
     924             :         { 'n', 'n', '\0' },
     925             :         ",nno,"
     926             :     },
     927             :     {
     928             :         "Norwegian",
     929             :         u8"Norsk",
     930             :         { 'n', 'o', '\0' },
     931             :         ",nor,"
     932             :     },
     933             :     {
     934             :         "Nuosu",
     935             :         u8"\uA188\uA320\uA4BF Nuosuhxop",
     936             :         { 'i', 'i', '\0' },
     937             :         ",iii,"
     938             :     },
     939             :     {
     940             :         "South Ndebele",
     941             :         u8"isiNdebele",
     942             :         { 'n', 'r', '\0' },
     943             :         ",nbl,"
     944             :     },
     945             :     {
     946             :         "Occitan",
     947             :         u8"occitan, lenga d'\u00F2c",
     948             :         { 'o', 'c', '\0' },
     949             :         ",oci,"
     950             :     },
     951             :     {
     952             :         "Ojibwe, Ojibwa",
     953             :         u8"\u140A\u14C2\u1511\u14C8\u142F\u14A7\u140E\u14D0",
     954             :         { 'o', 'j', '\0' },
     955             :         ",oji,"
     956             :     },
     957             :     {
     958             :         "Old Church Slavonic, Church Slavic, Church Slavonic, Old Bulgarian, Old Slavonic",
     959             :         u8"\u0469\u0437\u044B\u043A\u044A \u0441\u043B\u043E\u0432\u0463\u043D\u044C\u0441\u043A\u044A",
     960             :         { 'c', 'u', '\0' },
     961             :         ",chu,"
     962             :     },
     963             :     {
     964             :         "Oromo",
     965             :         u8"Afaan Oromoo",
     966             :         { 'o', 'm', '\0' },
     967             :         ",orm,"
     968             :     },
     969             :     {
     970             :         "Oriya",
     971             :         u8"\u0B13\u0B21\u0B3C\u0B3F\u0B06",
     972             :         { 'o', 'r', '\0' },
     973             :         ",ori,"
     974             :     },
     975             :     {
     976             :         "Ossetian, Ossetic",
     977             :         u8"\u0438\u0440\u043E\u043D \u00E6\u0432\u0437\u0430\u0433",
     978             :         { 'o', 's', '\0' },
     979             :         ",oss,"
     980             :     },
     981             :     {
     982             :         "Panjabi, Punjabi",
     983             :         u8"\u0A2A\u0A70\u0A1C\u0A3E\u0A2C\u0A40, \u067E\u0646\u062C\u0627\u0628\u06CC",
     984             :         { 'p', 'a', '\0' },
     985             :         ",pan,"
     986             :     },
     987             :     {
     988             :         "P\u0101li",
     989             :         u8"\u092A\u093E\u0934\u093F",
     990             :         { 'p', 'i', '\0' },
     991             :         ",pli,"
     992             :     },
     993             :     {
     994             :         "Persian (Farsi)",
     995             :         u8"\u0641\u0627\u0631\u0633\u06CC",
     996             :         { 'f', 'a', '\0' },
     997             :         ",fas,per,"
     998             :     },
     999             :     {
    1000             :         "Polish",
    1001             :         u8"j\u0119zyk polski, polszczyzna",
    1002             :         { 'p', 'l', '\0' },
    1003             :         ",pol,pols,"
    1004             :     },
    1005             :     {
    1006             :         "Pashto, Pushto",
    1007             :         u8"\u067E\u069A\u062A\u0648",
    1008             :         { 'p', 's', '\0' },
    1009             :         ",pus,"
    1010             :     },
    1011             :     {
    1012             :         "Portuguese",
    1013             :         u8"portugu\u00EAs",
    1014             :         { 'p', 't', '\0' },
    1015             :         ",por,"
    1016             :     },
    1017             :     {
    1018             :         "Quechua",
    1019             :         u8"Runa Simi, Kichwa",
    1020             :         { 'q', 'u', '\0' },
    1021             :         ",que,"
    1022             :     },
    1023             :     {
    1024             :         "Romansh",
    1025             :         u8"rumantsch grischun",
    1026             :         { 'r', 'm', '\0' },
    1027             :         ",roh,"
    1028             :     },
    1029             :     {
    1030             :         "Kirundi",
    1031             :         u8"Ikirundi",
    1032             :         { 'r', 'n', '\0' },
    1033             :         ",run,"
    1034             :     },
    1035             :     {
    1036             :         "Romanian",
    1037             :         u8"limba rom\u00E2n\u0103",
    1038             :         { 'r', 'o', '\0' },
    1039             :         ",ron,rum,"
    1040             :     },
    1041             :     {
    1042             :         "Russian",
    1043             :         u8"\u0440\u0443\u0441\u0441\u043A\u0438\u0439 \u044F\u0437\u044B\u043A",
    1044             :         { 'r', 'u', '\0' },
    1045             :         ",rus,"
    1046             :     },
    1047             :     {
    1048             :         "Sanskrit (Sa\u1E41sk\u1E5Bta)",
    1049             :         u8"\u0938\u0902\u0938\u094D\u0915\u0943\u0924\u092E\u094D",
    1050             :         { 's', 'a', '\0' },
    1051             :         ",san,"
    1052             :     },
    1053             :     {
    1054             :         "Sardinian",
    1055             :         u8"sardu",
    1056             :         { 's', 'c', '\0' },
    1057             :         ",srd,"
    1058             :     },
    1059             :     {
    1060             :         "Sindhi",
    1061             :         u8"\u0938\u093F\u0928\u094D\u0927\u0940, \u0633\u0646\u068C\u064A\u060C \u0633\u0646\u062F\u06BE\u06CC",
    1062             :         { 's', 'd', '\0' },
    1063             :         ",snd,"
    1064             :     },
    1065             :     {
    1066             :         "Northern Sami",
    1067             :         u8"Davvis\u00E1megiella",
    1068             :         { 's', 'e', '\0' },
    1069             :         ",sme,"
    1070             :     },
    1071             :     {
    1072             :         "Samoan",
    1073             :         u8"gagana fa'a Samoa",
    1074             :         { 's', 'm', '\0' },
    1075             :         ",smo,"
    1076             :     },
    1077             :     {
    1078             :         "Sango",
    1079             :         u8"y\u00E2ng\u00E2 t\u00EE s\u00E3ng\u00F6",
    1080             :         { 's', 'g', '\0' },
    1081             :         ",sag,"
    1082             :     },
    1083             :     {
    1084             :         "Serbian",
    1085             :         u8"\u0441\u0440\u043F\u0441\u043A\u0438 \u0458\u0435\u0437\u0438\u043A",
    1086             :         { 's', 'r', '\0' },
    1087             :         ",srp,"
    1088             :     },
    1089             :     {
    1090             :         "Scottish Gaelic; Gaelic",
    1091             :         u8"G\u00E0idhlig",
    1092             :         { 'g', 'd', '\0' },
    1093             :         ",gla,"
    1094             :     },
    1095             :     {
    1096             :         "Shona",
    1097             :         u8"chiShona",
    1098             :         { 's', 'n', '\0' },
    1099             :         ",sna,"
    1100             :     },
    1101             :     {
    1102             :         "Sinhala, Sinhalese",
    1103             :         u8"\u0DC3\u0DD2\u0D82\u0DC4\u0DBD",
    1104             :         { 's', 'i', '\0' },
    1105             :         ",sin,"
    1106             :     },
    1107             :     {
    1108             :         "Slovak",
    1109             :         u8"sloven\u010Dina, slovensk\u00FD jazyk",
    1110             :         { 's', 'k', '\0' },
    1111             :         ",slk,slo,"
    1112             :     },
    1113             :     {
    1114             :         "Slovene",
    1115             :         u8"slovenski jezik, sloven\u0161\u010Dina",
    1116             :         { 's', 'l', '\0' },
    1117             :         ",slv,"
    1118             :     },
    1119             :     {
    1120             :         "Somali",
    1121             :         u8"Soomaaliga, af Soomaali",
    1122             :         { 's', 'o', '\0' },
    1123             :         ",som,"
    1124             :     },
    1125             :     {
    1126             :         "Southern Sotho",
    1127             :         u8"Sesotho",
    1128             :         { 's', 't', '\0' },
    1129             :         ",sot,"
    1130             :     },
    1131             :     {
    1132             :         "South Azerbaijani",
    1133             :         u8"\u062A\u0648\u0631\u06A9\u062C\u0647",
    1134             :         { 'a', 'z', '\0' },
    1135             :         ",azb,"
    1136             :     },
    1137             :     {
    1138             :         "Spanish; Castilian",
    1139             :         u8"espa\u00F1ol, castellano",
    1140             :         { 'e', 's', '\0' },
    1141             :         ",spa,"
    1142             :     },
    1143             :     {
    1144             :         "Sundanese",
    1145             :         u8"Basa Sunda",
    1146             :         { 's', 'u', '\0' },
    1147             :         ",sun,"
    1148             :     },
    1149             :     {
    1150             :         "Swahili",
    1151             :         u8"Kiswahili",
    1152             :         { 's', 'w', '\0' },
    1153             :         ",swa,"
    1154             :     },
    1155             :     {
    1156             :         "Swati",
    1157             :         u8"SiSwati",
    1158             :         { 's', 's', '\0' },
    1159             :         ",ssw,"
    1160             :     },
    1161             :     {
    1162             :         "Swedish",
    1163             :         u8"Svenska",
    1164             :         { 's', 'v', '\0' },
    1165             :         ",swe,"
    1166             :     },
    1167             :     {
    1168             :         "Tamil",
    1169             :         u8"\u0BA4\u0BAE\u0BBF\u0BB4\u0BCD",
    1170             :         { 't', 'a', '\0' },
    1171             :         ",tam,"
    1172             :     },
    1173             :     {
    1174             :         "Telugu",
    1175             :         u8"\u0C24\u0C46\u0C32\u0C41\u0C17\u0C41",
    1176             :         { 't', 'e', '\0' },
    1177             :         ",tel,"
    1178             :     },
    1179             :     {
    1180             :         "Tajik",
    1181             :         u8"\u0442\u043E\u04B7\u0438\u043A\u04E3, to\u011Fik\u012B, \u062A\u0627\u062C\u06CC\u06A9\u06CC",
    1182             :         { 't', 'g', '\0' },
    1183             :         ",tgk,"
    1184             :     },
    1185             :     {
    1186             :         "Thai",
    1187             :         u8"\u0E44\u0E17\u0E22",
    1188             :         { 't', 'h', '\0' },
    1189             :         ",tha,"
    1190             :     },
    1191             :     {
    1192             :         "Tigrinya",
    1193             :         u8"\u1275\u130D\u122D\u129B",
    1194             :         { 't', 'i', '\0' },
    1195             :         ",tir,"
    1196             :     },
    1197             :     {
    1198             :         "Tibetan Standard, Tibetan, Central",
    1199             :         u8"\u0F56\u0F7C\u0F51\u0F0B\u0F61\u0F72\u0F42",
    1200             :         { 'b', 'o', '\0' },
    1201             :         ",bod,tib,"
    1202             :     },
    1203             :     {
    1204             :         "Turkmen",
    1205             :         u8"T\u00FCrkmen, \u0422\u04AF\u0440\u043A\u043C\u0435\u043D",
    1206             :         { 't', 'k', '\0' },
    1207             :         ",tuk,"
    1208             :     },
    1209             :     {
    1210             :         "Tagalog",
    1211             :         u8"Wikang Tagalog, \u170F\u1712\u1703\u1705\u1714 \u1706\u1704\u170E\u1713\u1704\u1714",
    1212             :         { 't', 'l', '\0' },
    1213             :         ",tgl,"
    1214             :     },
    1215             :     {
    1216             :         "Tswana",
    1217             :         u8"Setswana",
    1218             :         { 't', 'n', '\0' },
    1219             :         ",tsn,"
    1220             :     },
    1221             :     {
    1222             :         "Tonga (Tonga Islands)",
    1223             :         u8"faka Tonga",
    1224             :         { 't', 'o', '\0' },
    1225             :         ",ton,"
    1226             :     },
    1227             :     {
    1228             :         "Turkish",
    1229             :         u8"T\u00FCrk\u00E7e",
    1230             :         { 't', 'r', '\0' },
    1231             :         ",tur,"
    1232             :     },
    1233             :     {
    1234             :         "Tsonga",
    1235             :         u8"Xitsonga",
    1236             :         { 't', 's', '\0' },
    1237             :         ",tso,"
    1238             :     },
    1239             :     {
    1240             :         "Tatar",
    1241             :         u8"\u0442\u0430\u0442\u0430\u0440 \u0442\u0435\u043B\u0435, tatar tele",
    1242             :         { 't', 't', '\0' },
    1243             :         ",tat,"
    1244             :     },
    1245             :     {
    1246             :         "Twi",
    1247             :         u8"Twi",
    1248             :         { 't', 'w', '\0' },
    1249             :         ",twi,"
    1250             :     },
    1251             :     {
    1252             :         "Tahitian",
    1253             :         u8"Reo Tahiti",
    1254             :         { 't', 'y', '\0' },
    1255             :         ",tah,"
    1256             :     },
    1257             :     {
    1258             :         "Uyghur, Uighur",
    1259             :         u8"Uy\u01A3urq\u0259, \u0626\u06C7\u064A\u063A\u06C7\u0631\u0686\u06D5",
    1260             :         { 'u', 'g', '\0' },
    1261             :         ",uig,"
    1262             :     },
    1263             :     {
    1264             :         "Ukrainian",
    1265             :         u8"\u0443\u043A\u0440\u0430\u0457\u043D\u0441\u044C\u043A\u0430 \u043C\u043E\u0432\u0430",
    1266             :         { 'u', 'k', '\0' },
    1267             :         ",ukr,"
    1268             :     },
    1269             :     {
    1270             :         "Urdu",
    1271             :         u8"\u0627\u0631\u062F\u0648",
    1272             :         { 'u', 'r', '\0' },
    1273             :         ",urd,"
    1274             :     },
    1275             :     {
    1276             :         "Uzbek",
    1277             :         u8"O\u2018zbek, \u040E\u0437\u0431\u0435\u043A, \u0623\u06C7\u0632\u0628\u06D0\u0643",
    1278             :         { 'u', 'z', '\0' },
    1279             :         ",uzb,"
    1280             :     },
    1281             :     {
    1282             :         "Venda",
    1283             :         u8"Tshiven\u1E13a",
    1284             :         { 'v', 'e', '\0' },
    1285             :         ",ven,"
    1286             :     },
    1287             :     {
    1288             :         "Vietnamese",
    1289             :         u8"Ti\u1EBFng Vi\u1EC7t",
    1290             :         { 'v', 'i', '\0' },
    1291             :         ",vie,"
    1292             :     },
    1293             :     {
    1294             :         "Volap\u00FCk",
    1295             :         u8"Volap\u00FCk",
    1296             :         { 'v', 'o', '\0' },
    1297             :         ",vol,"
    1298             :     },
    1299             :     {
    1300             :         "Walloon",
    1301             :         u8"walon",
    1302             :         { 'w', 'a', '\0' },
    1303             :         ",wln,"
    1304             :     },
    1305             :     {
    1306             :         "Welsh",
    1307             :         u8"Cymraeg",
    1308             :         { 'c', 'y', '\0' },
    1309             :         ",cym,wel,"
    1310             :     },
    1311             :     {
    1312             :         "Wolof",
    1313             :         u8"Wollof",
    1314             :         { 'w', 'o', '\0' },
    1315             :         ",wol,"
    1316             :     },
    1317             :     {
    1318             :         "Western Frisian",
    1319             :         u8"Frysk",
    1320             :         { 'f', 'y', '\0' },
    1321             :         ",fry,"
    1322             :     },
    1323             :     {
    1324             :         "Xhosa",
    1325             :         u8"isiXhosa",
    1326             :         { 'x', 'o', '\0' },
    1327             :         ",xho,"
    1328             :     },
    1329             :     {
    1330             :         "Undefined",
    1331             :         u8"undefined",
    1332             :         { 'x', 'x', '\0' },
    1333             :         ",,"
    1334             :     },
    1335             :     {
    1336             :         "Yiddish",
    1337             :         u8"\u05D9\u05D9\u05B4\u05D3\u05D9\u05E9",
    1338             :         { 'y', 'i', '\0' },
    1339             :         ",yid,"
    1340             :     },
    1341             :     {
    1342             :         "Yoruba",
    1343             :         u8"Yor\u00F9b\u00E1",
    1344             :         { 'y', 'o', '\0' },
    1345             :         ",yor,"
    1346             :     },
    1347             :     {
    1348             :         "Zhuang, Chuang",
    1349             :         u8"Sa\u026F cue\u014B\u0185, Saw cuengh",
    1350             :         { 'z', 'a', '\0' },
    1351             :         ",zha,"
    1352             :     },
    1353             :     {
    1354             :         "Zulu",
    1355             :         u8"isiZulu",
    1356             :         { 'z', 'u', '\0' },
    1357             :         ",zul,"
    1358             :     },
    1359             :     {
    1360             :         nullptr,
    1361             :         nullptr,
    1362             :         { '\0', '\0', '\0' },
    1363             :         nullptr
    1364             :     }
    1365             : };
    1366             : 
    1367             : // this is the list of 2 letter country names
    1368             : snap_child::country_name_t const g_country_names[] =
    1369             : {
    1370             :     {
    1371             :         { 'A', 'D', '\0' },
    1372             :         "Andorra"
    1373             :     },
    1374             :     {
    1375             :         { 'A', 'E', '\0' },
    1376             :         "United Arab Emirates"
    1377             :     },
    1378             :     {
    1379             :         { 'A', 'F', '\0' },
    1380             :         "Afghanistan"
    1381             :     },
    1382             :     {
    1383             :         { 'A', 'G', '\0' },
    1384             :         "Antigua and Barbuda"
    1385             :     },
    1386             :     {
    1387             :         { 'A', 'I', '\0' },
    1388             :         "Anguilla"
    1389             :     },
    1390             :     {
    1391             :         { 'A', 'L', '\0' },
    1392             :         "Albania"
    1393             :     },
    1394             :     {
    1395             :         { 'A', 'M', '\0' },
    1396             :         "Armenia"
    1397             :     },
    1398             :     {
    1399             :         { 'A', 'O', '\0' },
    1400             :         "Angola"
    1401             :     },
    1402             :     {
    1403             :         { 'A', 'Q', '\0' },
    1404             :         "Antarctica"
    1405             :     },
    1406             :     {
    1407             :         { 'A', 'R', '\0' },
    1408             :         "Argentina"
    1409             :     },
    1410             :     {
    1411             :         { 'A', 'S', '\0' },
    1412             :         "American Samoa"
    1413             :     },
    1414             :     {
    1415             :         { 'A', 'T', '\0' },
    1416             :         "Austria"
    1417             :     },
    1418             :     {
    1419             :         { 'A', 'U', '\0' },
    1420             :         "Australia"
    1421             :     },
    1422             :     {
    1423             :         { 'A', 'W', '\0' },
    1424             :         "Aruba"
    1425             :     },
    1426             :     {
    1427             :         { 'A', 'X', '\0' },
    1428             :         u8"\00C5land Islands"
    1429             :     },
    1430             :     {
    1431             :         { 'A', 'Z', '\0' },
    1432             :         "Azerbaijan"
    1433             :     },
    1434             :     {
    1435             :         { 'B', 'A', '\0' },
    1436             :         "Bosnia and Herzegovina"
    1437             :     },
    1438             :     {
    1439             :         { 'B', 'B', '\0' },
    1440             :         "Barbados"
    1441             :     },
    1442             :     {
    1443             :         { 'B', 'D', '\0' },
    1444             :         "Bangladesh"
    1445             :     },
    1446             :     {
    1447             :         { 'B', 'E', '\0' },
    1448             :         "Belgium"
    1449             :     },
    1450             :     {
    1451             :         { 'B', 'F', '\0' },
    1452             :         "Burkina Faso"
    1453             :     },
    1454             :     {
    1455             :         { 'B', 'G', '\0' },
    1456             :         "Bulgaria"
    1457             :     },
    1458             :     {
    1459             :         { 'B', 'H', '\0' },
    1460             :         "Bahrain"
    1461             :     },
    1462             :     {
    1463             :         { 'B', 'I', '\0' },
    1464             :         "Burundi"
    1465             :     },
    1466             :     {
    1467             :         { 'B', 'J', '\0' },
    1468             :         "Benin"
    1469             :     },
    1470             :     {
    1471             :         { 'B', 'L', '\0' },
    1472             :         u8"Saint Barth\u00E9lemy"
    1473             :     },
    1474             :     {
    1475             :         { 'B', 'M', '\0' },
    1476             :         "Bermuda"
    1477             :     },
    1478             :     {
    1479             :         { 'B', 'N', '\0' },
    1480             :         "Brunei Darussalam"
    1481             :     },
    1482             :     {
    1483             :         { 'B', 'O', '\0' },
    1484             :         "Bolivia, Plurinational State of"
    1485             :     },
    1486             :     {
    1487             :         { 'B', 'Q', '\0' },
    1488             :         "Bonaire, Sint Eustatius and Saba"
    1489             :     },
    1490             :     {
    1491             :         { 'B', 'R', '\0' },
    1492             :         "Brazil"
    1493             :     },
    1494             :     {
    1495             :         { 'B', 'S', '\0' },
    1496             :         "Bahamas"
    1497             :     },
    1498             :     {
    1499             :         { 'B', 'T', '\0' },
    1500             :         "Bhutan"
    1501             :     },
    1502             :     {
    1503             :         { 'B', 'V', '\0' },
    1504             :         "Bouvet Island"
    1505             :     },
    1506             :     {
    1507             :         { 'B', 'W', '\0' },
    1508             :         "Botswana"
    1509             :     },
    1510             :     {
    1511             :         { 'B', 'Y', '\0' },
    1512             :         "Belarus"
    1513             :     },
    1514             :     {
    1515             :         { 'B', 'Z', '\0' },
    1516             :         "Belize"
    1517             :     },
    1518             :     {
    1519             :         { 'C', 'A', '\0' },
    1520             :         "Canada"
    1521             :     },
    1522             :     {
    1523             :         { 'C', 'C', '\0' },
    1524             :         "Cocos (Keeling) Islands"
    1525             :     },
    1526             :     {
    1527             :         { 'C', 'D', '\0' },
    1528             :         "Congo, the Democratic Republic of the"
    1529             :     },
    1530             :     {
    1531             :         { 'C', 'F', '\0' },
    1532             :         "Central African Republic"
    1533             :     },
    1534             :     {
    1535             :         { 'C', 'G', '\0' },
    1536             :         "Congo"
    1537             :     },
    1538             :     {
    1539             :         { 'C', 'H', '\0' },
    1540             :         "Switzerland"
    1541             :     },
    1542             :     {
    1543             :         { 'C', 'I', '\0' },
    1544             :         "C\u00F4te d'Ivoire"
    1545             :     },
    1546             :     {
    1547             :         { 'C', 'K', '\0' },
    1548             :         "Cook Islands"
    1549             :     },
    1550             :     {
    1551             :         { 'C', 'L', '\0' },
    1552             :         "Chile"
    1553             :     },
    1554             :     {
    1555             :         { 'C', 'M', '\0' },
    1556             :         "Cameroon"
    1557             :     },
    1558             :     {
    1559             :         { 'C', 'N', '\0' },
    1560             :         "China"
    1561             :     },
    1562             :     {
    1563             :         { 'C', 'O', '\0' },
    1564             :         "Colombia"
    1565             :     },
    1566             :     {
    1567             :         { 'C', 'R', '\0' },
    1568             :         "Costa Rica"
    1569             :     },
    1570             :     {
    1571             :         { 'C', 'U', '\0' },
    1572             :         "Cuba"
    1573             :     },
    1574             :     {
    1575             :         { 'C', 'V', '\0' },
    1576             :         "Cape Verde"
    1577             :     },
    1578             :     {
    1579             :         { 'C', 'W', '\0' },
    1580             :         "Cura\u00E7ao"
    1581             :     },
    1582             :     {
    1583             :         { 'C', 'X', '\0' },
    1584             :         "Christmas Island"
    1585             :     },
    1586             :     {
    1587             :         { 'X', 'Y', '\0' },
    1588             :         "Cyprus"
    1589             :     },
    1590             :     {
    1591             :         { 'C', 'Z', '\0' },
    1592             :         "Czech Republic"
    1593             :     },
    1594             :     {
    1595             :         { 'D', 'E', '\0' },
    1596             :         "Germany"
    1597             :     },
    1598             :     {
    1599             :         { 'D', 'J', '\0' },
    1600             :         "Djibouti"
    1601             :     },
    1602             :     {
    1603             :         { 'D', 'K', '\0' },
    1604             :         "Denmark"
    1605             :     },
    1606             :     {
    1607             :         { 'D', 'M', '\0' },
    1608             :         "Dominica"
    1609             :     },
    1610             :     {
    1611             :         { 'D', 'O', '\0' },
    1612             :         "Dominican Republic"
    1613             :     },
    1614             :     {
    1615             :         { 'D', 'Z', '\0' },
    1616             :         "Algeria"
    1617             :     },
    1618             :     {
    1619             :         { 'E', 'C', '\0' },
    1620             :         "Ecuador"
    1621             :     },
    1622             :     {
    1623             :         { 'E', 'E', '\0' },
    1624             :         "Estonia"
    1625             :     },
    1626             :     {
    1627             :         { 'E', 'G', '\0' },
    1628             :         "Egypt"
    1629             :     },
    1630             :     {
    1631             :         { 'E', 'H', '\0' },
    1632             :         "Western Sahara"
    1633             :     },
    1634             :     {
    1635             :         { 'E', 'R', '\0' },
    1636             :         "Eritrea"
    1637             :     },
    1638             :     {
    1639             :         { 'E', 'S', '\0' },
    1640             :         "Spain"
    1641             :     },
    1642             :     {
    1643             :         { 'E', 'T', '\0' },
    1644             :         "Ethiopia"
    1645             :     },
    1646             :     {
    1647             :         { 'F', 'I', '\0' },
    1648             :         "Finland"
    1649             :     },
    1650             :     {
    1651             :         { 'F', 'J', '\0' },
    1652             :         "Fiji"
    1653             :     },
    1654             :     {
    1655             :         { 'F', 'K', '\0' },
    1656             :         "Falkland Islands (Malvinas)"
    1657             :     },
    1658             :     {
    1659             :         { 'F', 'M', '\0' },
    1660             :         "Micronesia, Federated States of"
    1661             :     },
    1662             :     {
    1663             :         { 'F', 'O', '\0' },
    1664             :         "Faroe Islands"
    1665             :     },
    1666             :     {
    1667             :         { 'F', 'R', '\0' },
    1668             :         "France"
    1669             :     },
    1670             :     {
    1671             :         { 'G', 'A', '\0' },
    1672             :         "Gabon"
    1673             :     },
    1674             :     {
    1675             :         { 'G', 'B', '\0' },
    1676             :         "United Kingdom"
    1677             :     },
    1678             :     {
    1679             :         { 'G', 'D', '\0' },
    1680             :         "Grenada"
    1681             :     },
    1682             :     {
    1683             :         { 'G', 'E', '\0' },
    1684             :         "Georgia"
    1685             :     },
    1686             :     {
    1687             :         { 'G', 'F', '\0' },
    1688             :         "French Guiana"
    1689             :     },
    1690             :     {
    1691             :         { 'G', 'G', '\0' },
    1692             :         "Guernsey"
    1693             :     },
    1694             :     {
    1695             :         { 'G', 'H', '\0' },
    1696             :         "Ghana"
    1697             :     },
    1698             :     {
    1699             :         { 'G', 'I', '\0' },
    1700             :         "Gibraltar"
    1701             :     },
    1702             :     {
    1703             :         { 'G', 'L', '\0' },
    1704             :         "Greenland"
    1705             :     },
    1706             :     {
    1707             :         { 'G', 'M', '\0' },
    1708             :         "Gambia"
    1709             :     },
    1710             :     {
    1711             :         { 'G', 'N', '\0' },
    1712             :         "Guinea"
    1713             :     },
    1714             :     {
    1715             :         { 'G', 'P', '\0' },
    1716             :         "Guadeloupe"
    1717             :     },
    1718             :     {
    1719             :         { 'G', 'Q', '\0' },
    1720             :         "Equatorial Guinea"
    1721             :     },
    1722             :     {
    1723             :         { 'G', 'R', '\0' },
    1724             :         "Greece"
    1725             :     },
    1726             :     {
    1727             :         { 'G', 'S', '\0' },
    1728             :         "South Georgia and the South Sandwich Islands"
    1729             :     },
    1730             :     {
    1731             :         { 'G', 'T', '\0' },
    1732             :         "Guatemala"
    1733             :     },
    1734             :     {
    1735             :         { 'G', 'U', '\0' },
    1736             :         "Guam"
    1737             :     },
    1738             :     {
    1739             :         { 'G', 'W', '\0' },
    1740             :         "Guinea-Bissau"
    1741             :     },
    1742             :     {
    1743             :         { 'G', 'Y', '\0' },
    1744             :         "Guyana"
    1745             :     },
    1746             :     {
    1747             :         { 'H', 'K', '\0' },
    1748             :         "Hong Kong"
    1749             :     },
    1750             :     {
    1751             :         { 'H', 'M', '\0' },
    1752             :         "Heard Island and McDonald Islands"
    1753             :     },
    1754             :     {
    1755             :         { 'H', 'N', '\0' },
    1756             :         "Honduras"
    1757             :     },
    1758             :     {
    1759             :         { 'H', 'R', '\0' },
    1760             :         "Croatia"
    1761             :     },
    1762             :     {
    1763             :         { 'H', 'T', '\0' },
    1764             :         "Haiti"
    1765             :     },
    1766             :     {
    1767             :         { 'H', 'U', '\0' },
    1768             :         "Hungary"
    1769             :     },
    1770             :     {
    1771             :         { 'I', 'D', '\0' },
    1772             :         "Indonesia"
    1773             :     },
    1774             :     {
    1775             :         { 'I', 'E', '\0' },
    1776             :         "Ireland"
    1777             :     },
    1778             :     {
    1779             :         { 'I', 'L', '\0' },
    1780             :         "Israel"
    1781             :     },
    1782             :     {
    1783             :         { 'I', 'M', '\0' },
    1784             :         "Isle of Man"
    1785             :     },
    1786             :     {
    1787             :         { 'I', 'N', '\0' },
    1788             :         "India"
    1789             :     },
    1790             :     {
    1791             :         { 'I', 'O', '\0' },
    1792             :         "British Indian Ocean Territory"
    1793             :     },
    1794             :     {
    1795             :         { 'I', 'Q', '\0' },
    1796             :         "Iraq"
    1797             :     },
    1798             :     {
    1799             :         { 'I', 'R', '\0' },
    1800             :         "Iran, Islamic Republic of"
    1801             :     },
    1802             :     {
    1803             :         { 'I', 'S', '\0' },
    1804             :         "Iceland"
    1805             :     },
    1806             :     {
    1807             :         { 'I', 'T', '\0' },
    1808             :         "Italy"
    1809             :     },
    1810             :     {
    1811             :         { 'J', 'E', '\0' },
    1812             :         "Jersey"
    1813             :     },
    1814             :     {
    1815             :         { 'J', 'M', '\0' },
    1816             :         "Jamaica"
    1817             :     },
    1818             :     {
    1819             :         { 'J', 'O', '\0' },
    1820             :         "Jordan"
    1821             :     },
    1822             :     {
    1823             :         { 'J', 'P', '\0' },
    1824             :         "Japan"
    1825             :     },
    1826             :     {
    1827             :         { 'K', 'E', '\0' },
    1828             :         "Kenya"
    1829             :     },
    1830             :     {
    1831             :         { 'K', 'G', '\0' },
    1832             :         "Kyrgyzstan"
    1833             :     },
    1834             :     {
    1835             :         { 'K', 'H', '\0' },
    1836             :         "Cambodia"
    1837             :     },
    1838             :     {
    1839             :         { 'K', 'I', '\0' },
    1840             :         "Kiribati"
    1841             :     },
    1842             :     {
    1843             :         { 'K', 'M', '\0' },
    1844             :         "Comoros"
    1845             :     },
    1846             :     {
    1847             :         { 'K', 'N', '\0' },
    1848             :         "Saint Kitts and Nevis"
    1849             :     },
    1850             :     {
    1851             :         { 'K', 'P', '\0' },
    1852             :         "Korea, Democratic People's Republic of"
    1853             :     },
    1854             :     {
    1855             :         { 'K', 'R', '\0' },
    1856             :         "Korea, Republic of"
    1857             :     },
    1858             :     {
    1859             :         { 'K', 'W', '\0' },
    1860             :         "Kuwait"
    1861             :     },
    1862             :     {
    1863             :         { 'K', 'Y', '\0' },
    1864             :         "Cayman Islands"
    1865             :     },
    1866             :     {
    1867             :         { 'K', 'Z', '\0' },
    1868             :         "Kazakhstan"
    1869             :     },
    1870             :     {
    1871             :         { 'L', 'A', '\0' },
    1872             :         "Lao People's Democratic Republic"
    1873             :     },
    1874             :     {
    1875             :         { 'L', 'B', '\0' },
    1876             :         "Lebanon"
    1877             :     },
    1878             :     {
    1879             :         { 'L', 'C', '\0' },
    1880             :         "Saint Lucia"
    1881             :     },
    1882             :     {
    1883             :         { 'L', 'I', '\0' },
    1884             :         "Liechtenstein"
    1885             :     },
    1886             :     {
    1887             :         { 'L', 'K', '\0' },
    1888             :         "Sri Lanka"
    1889             :     },
    1890             :     {
    1891             :         { 'L', 'R', '\0' },
    1892             :         "Liberia"
    1893             :     },
    1894             :     {
    1895             :         { 'L', 'S', '\0' },
    1896             :         "Lesotho"
    1897             :     },
    1898             :     {
    1899             :         { 'L', 'T', '\0' },
    1900             :         "Lithuania"
    1901             :     },
    1902             :     {
    1903             :         { 'L', 'U', '\0' },
    1904             :         "Luxembourg"
    1905             :     },
    1906             :     {
    1907             :         { 'L', 'V', '\0' },
    1908             :         "Latvia"
    1909             :     },
    1910             :     {
    1911             :         { 'L', 'Y', '\0' },
    1912             :         "Libya"
    1913             :     },
    1914             :     {
    1915             :         { 'M', 'A', '\0' },
    1916             :         "Morocco"
    1917             :     },
    1918             :     {
    1919             :         { 'M', 'C', '\0' },
    1920             :         "Monaco"
    1921             :     },
    1922             :     {
    1923             :         { 'M', 'D', '\0' },
    1924             :         "Moldova, Republic of"
    1925             :     },
    1926             :     {
    1927             :         { 'M', 'E', '\0' },
    1928             :         "Montenegro"
    1929             :     },
    1930             :     {
    1931             :         { 'M', 'F', '\0' },
    1932             :         "Saint Martin (French part)"
    1933             :     },
    1934             :     {
    1935             :         { 'M', 'G', '\0' },
    1936             :         "Madagascar"
    1937             :     },
    1938             :     {
    1939             :         { 'M', 'H', '\0' },
    1940             :         "Marshall Islands"
    1941             :     },
    1942             :     {
    1943             :         { 'M', 'K', '\0' },
    1944             :         "Macedonia, the former Yugoslav Republic of"
    1945             :     },
    1946             :     {
    1947             :         { 'M', 'L', '\0' },
    1948             :         "Mali"
    1949             :     },
    1950             :     {
    1951             :         { 'M', 'M', '\0' },
    1952             :         "Myanmar"
    1953             :     },
    1954             :     {
    1955             :         { 'M', 'N', '\0' },
    1956             :         "Mongolia"
    1957             :     },
    1958             :     {
    1959             :         { 'M', 'O', '\0' },
    1960             :         "Macao"
    1961             :     },
    1962             :     {
    1963             :         { 'M', 'P', '\0' },
    1964             :         "Northern Mariana Islands"
    1965             :     },
    1966             :     {
    1967             :         { 'M', 'Q', '\0' },
    1968             :         "Martinique"
    1969             :     },
    1970             :     {
    1971             :         { 'M', 'R', '\0' },
    1972             :         "Mauritania"
    1973             :     },
    1974             :     {
    1975             :         { 'M', 'D', '\0' },
    1976             :         "Montserrat"
    1977             :     },
    1978             :     {
    1979             :         { 'M', 'T', '\0' },
    1980             :         "Malta"
    1981             :     },
    1982             :     {
    1983             :         { 'M', 'U', '\0' },
    1984             :         "Mauritius"
    1985             :     },
    1986             :     {
    1987             :         { 'M', 'V', '\0' },
    1988             :         "Maldives"
    1989             :     },
    1990             :     {
    1991             :         { 'M', 'W', '\0' },
    1992             :         "Malawi"
    1993             :     },
    1994             :     {
    1995             :         { 'M', 'X', '\0' },
    1996             :         "Mexico"
    1997             :     },
    1998             :     {
    1999             :         { 'M', 'Y', '\0' },
    2000             :         "Malaysia"
    2001             :     },
    2002             :     {
    2003             :         { 'M', 'Z', '\0' },
    2004             :         "Mozambique"
    2005             :     },
    2006             :     {
    2007             :         { 'N', 'A', '\0' },
    2008             :         "Namibia"
    2009             :     },
    2010             :     {
    2011             :         { 'N', 'C', '\0' },
    2012             :         "New Caledonia"
    2013             :     },
    2014             :     {
    2015             :         { 'N', 'E', '\0' },
    2016             :         "Niger"
    2017             :     },
    2018             :     {
    2019             :         { 'N', 'F', '\0' },
    2020             :         "Norfolk Island"
    2021             :     },
    2022             :     {
    2023             :         { 'N', 'G', '\0' },
    2024             :         "Nigeria"
    2025             :     },
    2026             :     {
    2027             :         { 'N', 'I', '\0' },
    2028             :         "Nicaragua"
    2029             :     },
    2030             :     {
    2031             :         { 'N', 'L', '\0' },
    2032             :         "Netherlands"
    2033             :     },
    2034             :     {
    2035             :         { 'N', 'O', '\0' },
    2036             :         "Norway"
    2037             :     },
    2038             :     {
    2039             :         { 'N', 'P', '\0' },
    2040             :         "Nepal"
    2041             :     },
    2042             :     {
    2043             :         { 'N', 'R', '\0' },
    2044             :         "Nauru"
    2045             :     },
    2046             :     {
    2047             :         { 'N', 'U', '\0' },
    2048             :         "Niue"
    2049             :     },
    2050             :     {
    2051             :         { 'N', 'Z', '\0' },
    2052             :         "New Zealand"
    2053             :     },
    2054             :     {
    2055             :         { 'O', 'M', '\0' },
    2056             :         "Oman"
    2057             :     },
    2058             :     {
    2059             :         { 'P', 'A', '\0' },
    2060             :         "Panama"
    2061             :     },
    2062             :     {
    2063             :         { 'P', 'E', '\0' },
    2064             :         "Peru"
    2065             :     },
    2066             :     {
    2067             :         { 'P', 'F', '\0' },
    2068             :         "French Polynesia"
    2069             :     },
    2070             :     {
    2071             :         { 'P', 'G', '\0' },
    2072             :         "Papua New Guinea"
    2073             :     },
    2074             :     {
    2075             :         { 'P', 'H', '\0' },
    2076             :         "Philippines"
    2077             :     },
    2078             :     {
    2079             :         { 'P', 'K', '\0' },
    2080             :         "Pakistan"
    2081             :     },
    2082             :     {
    2083             :         { 'P', 'L', '\0' },
    2084             :         "Poland"
    2085             :     },
    2086             :     {
    2087             :         { 'P', 'M', '\0' },
    2088             :         "Saint Pierre and Miquelon"
    2089             :     },
    2090             :     {
    2091             :         { 'P', 'N', '\0' },
    2092             :         "Pitcairn"
    2093             :     },
    2094             :     {
    2095             :         { 'P', 'R', '\0' },
    2096             :         "Puerto Rico"
    2097             :     },
    2098             :     {
    2099             :         { 'P', 'S', '\0' },
    2100             :         "Palestine, State of"
    2101             :     },
    2102             :     {
    2103             :         { 'P', 'T', '\0' },
    2104             :         "Portugal"
    2105             :     },
    2106             :     {
    2107             :         { 'P', 'W', '\0' },
    2108             :         "Palau"
    2109             :     },
    2110             :     {
    2111             :         { 'P', 'Y', '\0' },
    2112             :         "Paraguay"
    2113             :     },
    2114             :     {
    2115             :         { 'Q', 'A', '\0' },
    2116             :         "Qatar"
    2117             :     },
    2118             :     {
    2119             :         { 'R', 'E', '\0' },
    2120             :         "R\u00E9union"
    2121             :     },
    2122             :     {
    2123             :         { 'R', 'O', '\0' },
    2124             :         "Romania"
    2125             :     },
    2126             :     {
    2127             :         { 'R', 'S', '\0' },
    2128             :         "Serbia"
    2129             :     },
    2130             :     {
    2131             :         { 'R', 'U', '\0' },
    2132             :         "Russian Federation"
    2133             :     },
    2134             :     {
    2135             :         { 'R', 'W', '\0' },
    2136             :         "Rwanda"
    2137             :     },
    2138             :     {
    2139             :         { 'S', 'A', '\0' },
    2140             :         "Saudi Arabia"
    2141             :     },
    2142             :     {
    2143             :         { 'S', 'B', '\0' },
    2144             :         "Solomon Islands"
    2145             :     },
    2146             :     {
    2147             :         { 'S', 'C', '\0' },
    2148             :         "Seychelles"
    2149             :     },
    2150             :     {
    2151             :         { 'S', 'D', '\0' },
    2152             :         "Sudan"
    2153             :     },
    2154             :     {
    2155             :         { 'S', 'E', '\0' },
    2156             :         "Sweden"
    2157             :     },
    2158             :     {
    2159             :         { 'S', 'G', '\0' },
    2160             :         "Singapore"
    2161             :     },
    2162             :     {
    2163             :         { 'S', 'H', '\0' },
    2164             :         "Saint Helena, Ascension and Tristan da Cunha"
    2165             :     },
    2166             :     {
    2167             :         { 'S', 'I', '\0' },
    2168             :         "Slovenia"
    2169             :     },
    2170             :     {
    2171             :         { 'S', 'J', '\0' },
    2172             :         "Svalbard and Jan Mayen"
    2173             :     },
    2174             :     {
    2175             :         { 'S', 'K', '\0' },
    2176             :         "Slovakia"
    2177             :     },
    2178             :     {
    2179             :         { 'S', 'L', '\0' },
    2180             :         "Sierra Leone"
    2181             :     },
    2182             :     {
    2183             :         { 'S', 'M', '\0' },
    2184             :         "San Marino"
    2185             :     },
    2186             :     {
    2187             :         { 'S', 'N', '\0' },
    2188             :         "Senegal"
    2189             :     },
    2190             :     {
    2191             :         { 'S', 'O', '\0' },
    2192             :         "Somalia"
    2193             :     },
    2194             :     {
    2195             :         { 'S', 'R', '\0' },
    2196             :         "Suriname"
    2197             :     },
    2198             :     {
    2199             :         { 'S', 'S', '\0' },
    2200             :         "South Sudan"
    2201             :     },
    2202             :     {
    2203             :         { 'S', 'T', '\0' },
    2204             :         "Sao Tome and Principe"
    2205             :     },
    2206             :     {
    2207             :         { 'S', 'V', '\0' },
    2208             :         "El Salvador"
    2209             :     },
    2210             :     {
    2211             :         { 'S', 'X', '\0' },
    2212             :         "Sint Maarten (Dutch part)"
    2213             :     },
    2214             :     {
    2215             :         { 'S', 'Y', '\0' },
    2216             :         "Syrian Arab Republic"
    2217             :     },
    2218             :     {
    2219             :         { 'S', 'Z', '\0' },
    2220             :         "Swaziland"
    2221             :     },
    2222             :     {
    2223             :         { 'T', 'C', '\0' },
    2224             :         "Turks and Caicos Islands"
    2225             :     },
    2226             :     {
    2227             :         { 'T', 'D', '\0' },
    2228             :         "Chad"
    2229             :     },
    2230             :     {
    2231             :         { 'T', 'F', '\0' },
    2232             :         "French Southern Territories"
    2233             :     },
    2234             :     {
    2235             :         { 'T', 'G', '\0' },
    2236             :         "Togo"
    2237             :     },
    2238             :     {
    2239             :         { 'T', 'H', '\0' },
    2240             :         "Thailand"
    2241             :     },
    2242             :     {
    2243             :         { 'T', 'J', '\0' },
    2244             :         "Tajikistan"
    2245             :     },
    2246             :     {
    2247             :         { 'T', 'K', '\0' },
    2248             :         "Tokelau"
    2249             :     },
    2250             :     {
    2251             :         { 'T', 'L', '\0' },
    2252             :         "Timor-Leste"
    2253             :     },
    2254             :     {
    2255             :         { 'T', 'M', '\0' },
    2256             :         "Turkmenistan"
    2257             :     },
    2258             :     {
    2259             :         { 'T', 'N', '\0' },
    2260             :         "Tunisia"
    2261             :     },
    2262             :     {
    2263             :         { 'T', 'O', '\0' },
    2264             :         "Tonga"
    2265             :     },
    2266             :     {
    2267             :         { 'T', 'R', '\0' },
    2268             :         "Turkey"
    2269             :     },
    2270             :     {
    2271             :         { 'T', 'T', '\0' },
    2272             :         "Trinidad and Tobago"
    2273             :     },
    2274             :     {
    2275             :         { 'T', 'V', '\0' },
    2276             :         "Tuvalu"
    2277             :     },
    2278             :     {
    2279             :         { 'T', 'W', '\0' },
    2280             :         "Taiwan, Province of China"
    2281             :     },
    2282             :     {
    2283             :         { 'T', 'Z', '\0' },
    2284             :         "Tanzania, United Republic of"
    2285             :     },
    2286             :     {
    2287             :         { 'U', 'A', '\0' },
    2288             :         "Ukraine"
    2289             :     },
    2290             :     {
    2291             :         { 'U', 'G', '\0' },
    2292             :         "Uganda"
    2293             :     },
    2294             :     {
    2295             :         { 'U', 'M', '\0' },
    2296             :         "United States Minor Outlying Islands"
    2297             :     },
    2298             :     {
    2299             :         { 'U', 'S', '\0' },
    2300             :         "United States"
    2301             :     },
    2302             :     {
    2303             :         { 'U', 'Y', '\0' },
    2304             :         "Uruguay"
    2305             :     },
    2306             :     {
    2307             :         { 'U', 'Z', '\0' },
    2308             :         "Uzbekistan"
    2309             :     },
    2310             :     {
    2311             :         { 'V', 'A', '\0' },
    2312             :         "Holy See (Vatican City State)"
    2313             :     },
    2314             :     {
    2315             :         { 'V', 'C', '\0' },
    2316             :         "Saint Vincent and the Grenadines"
    2317             :     },
    2318             :     {
    2319             :         { 'V', 'E', '\0' },
    2320             :         "Venezuela, Bolivarian Republic of"
    2321             :     },
    2322             :     {
    2323             :         { 'V', 'G', '\0' },
    2324             :         "Virgin Islands, British"
    2325             :     },
    2326             :     {
    2327             :         { 'V', 'I', '\0' },
    2328             :         "Virgin Islands, U.S."
    2329             :     },
    2330             :     {
    2331             :         { 'V', 'N', '\0' },
    2332             :         "Viet Nam"
    2333             :     },
    2334             :     {
    2335             :         { 'V', 'U', '\0' },
    2336             :         "Vanuatu"
    2337             :     },
    2338             :     {
    2339             :         { 'W', 'F', '\0' },
    2340             :         "Wallis and Futuna"
    2341             :     },
    2342             :     {
    2343             :         { 'W', 'S', '\0' },
    2344             :         "Samoa"
    2345             :     },
    2346             :     {
    2347             :         { 'Y', 'E', '\0' },
    2348             :         "Yemen"
    2349             :     },
    2350             :     {
    2351             :         { 'Y', 'T', '\0' },
    2352             :         "Mayotte"
    2353             :     },
    2354             :     {
    2355             :         { 'Z', 'A', '\0' },
    2356             :         "South Africa"
    2357             :     },
    2358             :     {
    2359             :         { 'Z', 'M', '\0' },
    2360             :         "Zambia"
    2361             :     },
    2362             :     {
    2363             :         { 'Z', 'W', '\0' },
    2364             :         "Zimbabwe"
    2365             :     },
    2366             :     {
    2367             :         { 'Z', 'Z', '\0' },
    2368             :         "Unknown" // unknown, invalid, undefined
    2369             :     },
    2370             :     {
    2371             :         { '\0', '\0', '\0' },
    2372             :         nullptr
    2373             :     }
    2374             : };
    2375             : 
    2376             : 
    2377             : } // no name namespace
    2378             : 
    2379             : 
    2380             : 
    2381             : /** \brief Get a composed version of this locale.
    2382             :  *
    2383             :  * The locale can be empty, just a language name, or a language
    2384             :  * named, an underscore, and a country name.
    2385             :  *
    2386             :  * \return The composed locale.
    2387             :  */
    2388           0 : QString snap_child::locale_info_t::get_composed() const
    2389             : {
    2390           0 :     if(f_country.isEmpty())
    2391             :     {
    2392           0 :         return f_language;
    2393             :     }
    2394             : 
    2395           0 :     return QString("%1_%2").arg(f_language).arg(f_country);
    2396             : }
    2397             : 
    2398             : 
    2399             : 
    2400             : /** \brief Save the file data in the post_file_t object.
    2401             :  *
    2402             :  * This function makes a copy of the data in the post_file_t
    2403             :  * object. Note that since we're using a QByteArray, the
    2404             :  * real copy is done only on write and since we do not modify
    2405             :  * the buffer, it should only copy the buffer pointer, not
    2406             :  * the content.
    2407             :  *
    2408             :  * The function also computes the "real" MIME type using the
    2409             :  * magic library.
    2410             :  *
    2411             :  * \param[in] data  The data of the file to save in this object.
    2412             :  *
    2413             :  * \sa snap::get_mime_type();
    2414             :  */
    2415           0 : void snap_child::post_file_t::set_data(QByteArray const & data)
    2416             : {
    2417           0 :     f_data = data;
    2418           0 :     f_size = data.size();
    2419             : 
    2420             :     // namespace required otherwise we'd call the get_mime_type
    2421             :     // function of the post_file_t class!
    2422           0 :     f_mime_type = snap::get_mime_type(data);
    2423             : 
    2424             : //printf("mime: %s\n", f_mime_type.toUtf8().data());
    2425           0 : }
    2426             : 
    2427             : 
    2428             : /** \brief Retrieve the basename.
    2429             :  *
    2430             :  * This function returns the filename without any path.
    2431             :  * The extension is not removed.
    2432             :  *
    2433             :  * \return The filename without a path
    2434             :  */
    2435           0 : QString snap_child::post_file_t::get_basename() const
    2436             : {
    2437           0 :     int const last_slash(f_filename.lastIndexOf('/'));
    2438           0 :     if(last_slash >= 0)
    2439             :     {
    2440           0 :         return f_filename.mid(last_slash + 1);
    2441             :     }
    2442           0 :     return f_filename;
    2443             : }
    2444             : 
    2445             : 
    2446             : /** \brief Get the size of the buffer.
    2447             :  *
    2448             :  * This function retrieves the real size of the data buffer.
    2449             :  * When loading an attachment from Cassandra, it is possible to
    2450             :  * ask the system to not load the data to save time (i.e. if you
    2451             :  * are just looking into showing a list of attachments and you
    2452             :  * do not need the actual data...) In that case the load_attachment()
    2453             :  * function of the content plugin only sets the size and no data.
    2454             :  *
    2455             :  * This function makes sure that the correct size gets returned.
    2456             :  *
    2457             :  * \return The size of the data.
    2458             :  */
    2459           0 : int snap_child::post_file_t::get_size() const
    2460             : {
    2461           0 :     int size(f_data.size());
    2462           0 :     if(size == 0)
    2463             :     {
    2464           0 :         size = f_size;
    2465             :     }
    2466           0 :     return size;
    2467             : }
    2468             : 
    2469             : 
    2470             : /** \brief Initialize a child process.
    2471             :  *
    2472             :  * This function initializes a child process object. At this point it
    2473             :  * marked as a parent process child instance (i.e. calling child process
    2474             :  * functions will generate an error.)
    2475             :  *
    2476             :  * Whenever the parent Snap Server receives a connect from the Snap CGI
    2477             :  * tool, it calls the process() function which creates the child and starts
    2478             :  * processing the TCP request.
    2479             :  *
    2480             :  * Note that at this point there is not communication between the parent
    2481             :  * and child processes other than the child process death that the parent
    2482             :  * acknowledge at some point.
    2483             :  */
    2484           0 : snap_child::snap_child(server_pointer_t s)
    2485             :     : f_server(s)
    2486             :     , f_start_date(0)
    2487             :     , f_output()            // -Weffc++ forces us to have this one here
    2488             :     , f_messenger_runner(this)
    2489           0 :     , f_messenger_thread("background messenger connection thread", &f_messenger_runner)
    2490             : {
    2491           0 : }
    2492             : 
    2493             : 
    2494             : /** \brief Clean up a child process.
    2495             :  *
    2496             :  * This function cleans up a child process. For the parent this means waiting
    2497             :  * on the child, assuming that the child process was successfully started.
    2498             :  * This also means that the function may block until the child dies...
    2499             :  */
    2500           0 : snap_child::~snap_child()
    2501             : {
    2502             :     // detach or wait till it dies?
    2503           0 :     if(f_child_pid > 0)
    2504             :     {
    2505             :         int status;
    2506           0 :         wait(&status);
    2507             :     }
    2508             :     //else
    2509             :     //{ // this is the child process deleting itself
    2510             :     //    ...
    2511             :     //    if(f_socket != -1)
    2512             :     //    {
    2513             :     //        // this is automatic anyway (we're in Unix)
    2514             :     //        // and if not already cleared, we've got more serious problems
    2515             :     //        // (see the process() function for more info)
    2516             :     //        close(f_socket);
    2517             :     //    }
    2518             :     //}
    2519           0 : }
    2520             : 
    2521             : /** \brief Get the current date.
    2522             :  *
    2523             :  * PLEASE consider use get_start_date() instead of the current date.
    2524             :  * 99.999% of the time, you do NOT want to use the current date or
    2525             :  * all the dates you will manipulate will be different.
    2526             :  *
    2527             :  * In some really rare circumstance, you may want to make use of the
    2528             :  * "exact" current date instead of the start date. This function
    2529             :  * can be used in those rare cases.
    2530             :  *
    2531             :  * This function does not modify the start date of the current process.
    2532             :  *
    2533             :  * \sa init_start_date()
    2534             :  * \sa get_start_date()
    2535             :  * \sa get_start_time()
    2536             :  */
    2537           0 : int64_t snap_child::get_current_date()
    2538             : {
    2539             :     struct timeval tv;
    2540           0 :     if(gettimeofday(&tv, nullptr) != 0)
    2541             :     {
    2542           0 :         int const err(errno);
    2543           0 :         SNAP_LOG_FATAL("gettimeofday() failed with errno: ")(err);
    2544           0 :         throw std::runtime_error("gettimeofday() failed");
    2545             :     }
    2546           0 :     return static_cast<int64_t>(tv.tv_sec) * static_cast<int64_t>(1000000)
    2547           0 :          + static_cast<int64_t>(tv.tv_usec);
    2548             : }
    2549             : 
    2550             : 
    2551             : /** \brief Reset the start date to now.
    2552             :  *
    2553             :  * This function is called by the processing functions to reset the
    2554             :  * start date. This is important because child objects may be reused
    2555             :  * multiple times instead of allocated and deallocated by the server.
    2556             :  *
    2557             :  * Also, if you are writing a backend process, it should call this function
    2558             :  * on each loop to make sure it gets updated. This is quite important because
    2559             :  * you cannot be in control of all the functions that are going to be called
    2560             :  * from your process and some of them may actually be using this value
    2561             :  * without you having control over it.
    2562             :  */
    2563           0 : void snap_child::init_start_date()
    2564             : {
    2565           0 :     f_start_date = get_current_date();
    2566           0 : }
    2567             : 
    2568             : 
    2569             : /** \brief Change the current timezone to the newly specified timezone.
    2570             :  *
    2571             :  * \warning
    2572             :  * This is a low level function to set a timezone. You are actually
    2573             :  * expected to call the locale::set_timezone() event instead. That way
    2574             :  * you will be setup with the right timezone as expected by the various
    2575             :  * plugins.
    2576             :  *
    2577             :  * We force the server to run using the UTC timezone. This function can
    2578             :  * be used to change the timezone to a user timezone.
    2579             :  *
    2580             :  * To restore the timezone to the default server timezone, you may call this
    2581             :  * function with an empty string.
    2582             :  *
    2583             :  * Source:
    2584             :  * http://stackoverflow.com/questions/10378093/c-api-to-return-timestring-in-specified-time-zone/10378513#10378513
    2585             :  *
    2586             :  * \param[in] timezone  Name of the timezone as understood by tzset(3).
    2587             :  */
    2588           0 : void snap_child::set_timezone(QString const& timezone)
    2589             : {
    2590             :     // force new timezone
    2591           0 :     setenv("TZ", timezone.toUtf8().data(), 1);
    2592           0 :     tzset();
    2593           0 : }
    2594             : 
    2595             : 
    2596             : /** \brief Change the current locale to the newly specified locale.
    2597             :  *
    2598             :  * \warning
    2599             :  * You want to look into the locale plugin instead. This function
    2600             :  * currently does nothing because it is very sensitive to the name
    2601             :  * of the locale. See the locale::set_locale() function instead.
    2602             :  *
    2603             :  * We force the server to run using the C locale. This function can
    2604             :  * be used to change the locale to a user locale or the website locale.
    2605             :  *
    2606             :  * To restore the locale to the default server locale, you may call this
    2607             :  * function with an empty string.
    2608             :  *
    2609             :  * \note
    2610             :  * The locale is not set to the empty string. This would set the locale
    2611             :  * to the current $LANG parameter and since we have no control over that
    2612             :  * parameter, we do not use it. It could otherwise have side effects over
    2613             :  * our server which we do not want to have. Instead we use "C" as the
    2614             :  * default locale. It may not do what you expected, but we can be sure to
    2615             :  * control what happens in that situation.
    2616             :  *
    2617             :  * Source:
    2618             :  * http://stackoverflow.com/questions/10378093/c-api-to-return-timestring-in-specified-time-zone/10378513#10378513
    2619             :  *
    2620             :  * \todo
    2621             :  * It looks like we cannot use this one unless we compile all the locales
    2622             :  * when the ICU library does not require such (TBD). Also the basic locale
    2623             :  * requires the encoding which we could force to UTF-8, but we cannot
    2624             :  * assume that every server would have UTF-8... So at this point this
    2625             :  * function does nothing. You are expected to call locale functions
    2626             :  * instead in order to get the correct string formatting behavior.
    2627             :  *
    2628             :  * \param[in] locale  Name of the locale as understood by setlocale(3).
    2629             :  */
    2630           0 : void snap_child::set_locale(QString const & locale)
    2631             : {
    2632           0 :     NOTUSED(locale);
    2633             :     // force new locale
    2634             :     // Note: empty ("") is like using $LANG to setup the locale
    2635             : //std::cerr << "***\n*** set_locale(" << locale << ")\n***\n";
    2636             :     //if(locale.isEmpty())
    2637             :     //{
    2638             :     //    // use "C" as the default (which is different from "" which
    2639             :     //    // uses $LANG as the locale and since that could be tainted
    2640             :     //    // or have unwanted side effects, we do not use it.)
    2641             :     //    if(std::setlocale(LC_ALL, "C.UTF-8"))
    2642             :     //    {
    2643             :     //        // it looks like C.UTF-8 is available, use that instead
    2644             :     //        // so we avoid downgrading to just an ANSII console
    2645             :     //        std::locale::global(std::locale("C.UTF-8"));
    2646             :     //    }
    2647             :     //    else
    2648             :     //    {
    2649             :     //        std::locale::global(std::locale("C"));
    2650             :     //    }
    2651             :     //}
    2652             :     //else
    2653             :     //{
    2654             :     //    // this function throws if the locale() is not available...
    2655             :     //    std::locale::global(std::locale(locale.toUtf8().data()));
    2656             :     //}
    2657           0 : }
    2658             : 
    2659             : 
    2660           0 : void snap_child::connect_messenger()
    2661             : {
    2662           0 :     auto server( f_server.lock() );
    2663           0 :     f_communicator = snap_communicator::instance();
    2664             : 
    2665           0 :     QString communicator_addr("127.0.0.1");
    2666           0 :     int communicator_port(4040);
    2667             :     tcp_client_server::get_addr_port
    2668           0 :             ( QString::fromUtf8(server->get_parameters()("snapcommunicator", "local_listen").c_str())
    2669             :             , communicator_addr
    2670             :             , communicator_port
    2671             :             , "tcp"
    2672             :             );
    2673             : 
    2674             :     // Create a new child_messenger object
    2675             :     //
    2676           0 :     f_messenger.reset(new child_messenger(this, communicator_addr.toUtf8().data(), communicator_port ));
    2677           0 :     f_messenger->set_name("child_messenger");
    2678           0 :     f_messenger->set_priority(50);
    2679             : 
    2680             :     // Add it into the instance list.
    2681             :     //
    2682           0 :     f_communicator->add_connection( f_messenger );
    2683             : 
    2684             :     // Add this to the logging facility so we can broadcast logs to snaplog via snapcommunicator.
    2685             :     //
    2686           0 :     server->configure_messenger_logging(
    2687           0 :         std::static_pointer_cast<snap_communicator::snap_tcp_client_permanent_message_connection>( f_messenger )
    2688             :     );
    2689             : 
    2690           0 :     if(!f_messenger_thread.start())
    2691             :     {
    2692           0 :         SNAP_LOG_ERROR("The thread used to run the background connection process did not start.");
    2693             :     }
    2694           0 : }
    2695             : 
    2696             : 
    2697           0 : void snap_child::stop_messenger()
    2698             : {
    2699           0 :     if(f_communicator)
    2700             :     {
    2701           0 :         if(f_messenger)
    2702             :         {
    2703             :             // since we have the messenger, we should unregister
    2704             :             // but the truth is that the following would just cache
    2705             :             // the message and exepect the `snap_communicator::run()`
    2706             :             // to process the message at leisure...
    2707             :             //
    2708             :             // but since we are exiting, we ignore that step at the
    2709             :             // moment...
    2710             :             //
    2711             :             //snap::snap_communicator_message unregister_snapchild;
    2712             :             //unregister_snapchild.set_command("UNREGISTER");
    2713             :             //unregister_snapchild.add_parameter("service", f_service_name);
    2714             :             //send_message(unregister_snapchild);
    2715             : 
    2716             :             // This causes the background thread to stop.
    2717             :             //
    2718           0 :             f_messenger->mark_done();
    2719           0 :             f_communicator->remove_connection( f_messenger );
    2720           0 :             f_messenger.reset();
    2721             :         }
    2722             : 
    2723           0 :         f_communicator.reset();
    2724             :     }
    2725             : 
    2726           0 :     f_messenger_thread.stop();
    2727           0 : }
    2728             : 
    2729             : 
    2730             : /** \brief Initialize the child_messenger connection.
    2731             :  *
    2732             :  * This function initializes the child_messenger connection. It saves
    2733             :  * a pointer to the main Snap! server so it can react appropriately
    2734             :  * whenever a message is received.
    2735             :  *
    2736             :  * \param[in] s           A pointer to the server so we can send messages there.
    2737             :  * \param[in] addr        The address of the snapcommunicator server.
    2738             :  * \param[in] port        The port of the snapcommunicator server.
    2739             :  * \param[in] use_thread  If set to true, a thread is being used to connect to the server.
    2740             :  */
    2741           0 : snap_child::child_messenger::child_messenger(snap_child * s, std::string const & addr, int port )
    2742             :     : snap_tcp_client_permanent_message_connection
    2743             :       (   addr
    2744             :         , port
    2745             :         , tcp_client_server::bio_client::mode_t::MODE_PLAIN
    2746             :         , snap_communicator::snap_tcp_client_permanent_message_connection::DEFAULT_PAUSE_BEFORE_RECONNECTING
    2747             :         , true /*use_thread*/
    2748             :       )
    2749             :     , f_child(s)
    2750           0 :     , f_service_name( QString("snap_child_%1").arg(gettid()) )
    2751             : {
    2752           0 : }
    2753             : 
    2754             : 
    2755             : /** \brief Process a message we just received.
    2756             :  *
    2757             :  * This function is called whenever the snapcommunicator received and
    2758             :  * decided to forward a message to us.
    2759             :  *
    2760             :  * \param[in] message  The message we just received.
    2761             :  */
    2762           0 : void snap_child::child_messenger::process_message(snap_communicator_message const & message)
    2763             : {
    2764           0 :     QString const command(message.get_command());
    2765           0 :     if(command == "HELP")
    2766             :     {
    2767             :         // snapcommunicator wants us to tell it what commands
    2768             :         // we accept
    2769           0 :         snap_communicator_message commands_message;
    2770           0 :         commands_message.set_command("COMMANDS");
    2771           0 :         commands_message.add_parameter("list", "HELP,QUITTING,READY,STOP,UNKNOWN");
    2772           0 :         send_message(commands_message);
    2773           0 :         return;
    2774             :     }
    2775           0 :     else if(command == "QUITTING")
    2776             :     {
    2777           0 :         SNAP_LOG_WARNING("We received the QUITTING command.");
    2778           0 :         f_child->stop_messenger();
    2779           0 :         return;
    2780             :     }
    2781           0 :     else if(command == "READY")
    2782             :     {
    2783             :         // the REGISTER worked, wait for the HELP message
    2784           0 :         return;
    2785             :     }
    2786           0 :     else if(command == "STOP")
    2787             :     {
    2788           0 :         SNAP_LOG_WARNING("we received the STOP command.");
    2789           0 :         f_child->stop_messenger();
    2790           0 :         return;
    2791             :     }
    2792           0 :     else if(command == "UNKNOWN")
    2793             :     {
    2794             :         // we sent a command that Snap! Communicator did not understand
    2795             :         //
    2796           0 :         SNAP_LOG_ERROR("we sent unknown command \"")(message.get_parameter("command"))("\" and probably did not get the expected result.");
    2797           0 :         return;
    2798             :     }
    2799             : }
    2800             : 
    2801             : 
    2802             : /** \brief Process was just connected.
    2803             :  *
    2804             :  * This callback happens whenever a new connection is established.
    2805             :  * It sends a REGISTER command to the snapcommunicator. The READY
    2806             :  * reply will be received when process_message() gets called. At
    2807             :  * that point we are fully registered.
    2808             :  *
    2809             :  * This callback happens first so if we lose our connection to
    2810             :  * the snapcommunicator server, it will re-register the snapserver
    2811             :  * again as expected.
    2812             :  */
    2813           0 : void snap_child::child_messenger::process_connected()
    2814             : {
    2815           0 :     snap_tcp_client_permanent_message_connection::process_connected();
    2816             : 
    2817           0 :     snap::snap_communicator_message register_snapchild;
    2818           0 :     register_snapchild.set_command("REGISTER");
    2819           0 :     register_snapchild.add_parameter("service", f_service_name);
    2820           0 :     register_snapchild.add_parameter("version", snap::snap_communicator::VERSION);
    2821           0 :     send_message(register_snapchild);
    2822           0 : }
    2823             : 
    2824             : 
    2825           0 : snap_child::messenger_runner::messenger_runner( snap_child* sc )
    2826             :     : snap_runner("background snap_tcp_client_permanent_message_connection for asynchroneous connections")
    2827           0 :     , f_child(sc)
    2828             : {
    2829           0 : }
    2830             : 
    2831             : 
    2832           0 : void snap_child::messenger_runner::run()
    2833             : {
    2834           0 :     f_child->f_communicator->run();
    2835           0 : }
    2836             : 
    2837             : 
    2838             : /** \brief Fork the child process.
    2839             :  *
    2840             :  * Use this method to fork child processes. If SNAP_NO_FORK is on,
    2841             :  * then respect the "--nofork" command line option.
    2842             :  *
    2843             :  * The main process should not be running any threads. At this point
    2844             :  * we only check and post a warning if the number of threads is not
    2845             :  * exactly 1.
    2846             :  *
    2847             :  * In case the snap logger uses threads (i.e. when using a SocketAppender)
    2848             :  * we stop the logger before calling fork() and then we re-establish the
    2849             :  * logger on return, whether the fork() worked or not.
    2850             :  *
    2851             :  * The return value is exactly the same as what the fork() function would
    2852             :  * otherwise return.
    2853             :  *
    2854             :  * \exception snap_logic_exception
    2855             :  * This exception is raised if the server weak pointer cannot be locked.
    2856             :  *
    2857             :  * \return The child PID, 0 for the child process, -1 if an error occurs.
    2858             :  */
    2859           0 : pid_t snap_child::fork_child()
    2860             : {
    2861           0 :     server::pointer_t server(get_server());
    2862             : 
    2863             : #ifdef SNAP_NO_FORK
    2864             :     if(server->nofork())
    2865             :     {
    2866             :         // simulate a working fork, we return as the child
    2867             :         return 0;
    2868             :     }
    2869             : #endif
    2870             : 
    2871             :     // generate a warning about having more than one thread at this point
    2872             :     // (it is a huge potential for a crash or lock up, hence the test)
    2873             :     // See: https://snapwebsites.org/journal/2015/06/using-threads-server-uses-fork-they-dont-mix-well
    2874             :     // We send the log after we re-established it (See below)
    2875             :     //
    2876           0 :     size_t const count(server->thread_count());
    2877             : 
    2878           0 :     pid_t const parent_pid(getpid());
    2879             : 
    2880           0 :     pid_t const p(fork());
    2881             : 
    2882           0 :     if(p != 0)
    2883             :     {
    2884           0 :         if(count != 1)
    2885             :         {
    2886           0 :             SNAP_LOG_WARNING("snap_child::fork_child(): The number of threads before the fork() to create a snap_child is ")(count)(" when it should be 1.");
    2887             : 
    2888             :             // TODO: look into having a flag concept in a contrib lib.
    2889             :             //
    2890             :             // this is a rather important bug, only developers have a chance
    2891             :             // to fix it, though... (hence the rather low priority)
    2892             :             //
    2893           0 :             SNAP_FLAG_UP(
    2894             :                           "snap_child"
    2895             :                         , "create-child"
    2896             :                         , "thread"
    2897             :                         , "snap_child::fork_child() called with more than one thread running; a developer has to look into why this would happen.")
    2898           0 :                     ->set_priority(35)
    2899           0 :                     .set_manual_down(true)
    2900           0 :                     .add_tag("thread")
    2901           0 :                     .add_tag("fork")
    2902           0 :                     .save();
    2903             :         }
    2904             :     }
    2905             :     else
    2906             :     {
    2907             :         // auto-kill child if parent dies
    2908             :         //
    2909             :         // TODO: ameliorate by using a "catch SIGHUP" in children via
    2910             :         //       the signal class of snapcommunicator
    2911             :         //
    2912             :         //       ameliorate by using a SIGUSR1 or SIGUSR2 and implement
    2913             :         //       a way for a possible clean exit (i.e. if child is still
    2914             :         //       working, give it a chance, then after X seconds, still
    2915             :         //       force a kill)
    2916             :         //
    2917             :         try
    2918             :         {
    2919           0 :             process::set_process_name("snap_child");
    2920             : 
    2921           0 :             prctl(PR_SET_PDEATHSIG, SIGHUP);
    2922             : 
    2923             :             // Since we are in the child instance, we don't want the same object as was running in the server.
    2924             :             // So we need to create a new snapcommunicator connection, register it, and add it
    2925             :             // into the logging facility.
    2926             :             //
    2927             :             // At this point we are marking this call off because it breaks
    2928             :             // the entire child environment. We'll have to find a way to
    2929             :             // just send messages to snapcommunicator without the need
    2930             :             // for the complicated messages going back and forth or pile
    2931             :             // up all the messages and have them handled at the end only
    2932             :             // (i.e. no threads need in that case...)
    2933             :             //
    2934             :             //connect_messenger();
    2935             : 
    2936             :             // always reconfigure the logger in the child
    2937           0 :             logging::reconfigure();
    2938             : 
    2939           0 :             SNAP_LOG_TRACE("snap_child::fork_child() just hooked up logging! -- child of ")(getppid());
    2940             : 
    2941             :             // it could be that the prctrl() was made after the true parent died...
    2942             :             // so we have to test the PID of our parent
    2943             :             //
    2944           0 :             if(getppid() != parent_pid)
    2945             :             {
    2946           0 :                 SNAP_LOG_FATAL("snap_child::fork_child() lost parent too soon and did not receive SIGHUP; quit immediately.");
    2947           0 :                 exit(1);
    2948           0 :                 NOTREACHED();
    2949             :             }
    2950             :         }
    2951           0 :         catch( snap_exception const & except )
    2952             :         {
    2953           0 :             SNAP_LOG_FATAL("snap_child::fork_child(): snap_exception caught: ")(except.what());
    2954           0 :             exit(1);
    2955           0 :             NOTREACHED();
    2956             :         }
    2957           0 :         catch( std::exception const & std_except )
    2958             :         {
    2959             :             // the snap_logic_exception is not a snap_exception
    2960             :             // and other libraries may generate other exceptions
    2961             :             // (i.e. libtld, C++ cassandra driver...)
    2962             :             //
    2963           0 :             SNAP_LOG_FATAL("snap_child::fork_child(): std::exception caught: ")(std_except.what());
    2964           0 :             exit(1);
    2965           0 :             NOTREACHED();
    2966             :         }
    2967           0 :         catch( ... )
    2968             :         {
    2969           0 :             SNAP_LOG_FATAL("snap_child::fork_child(): unknown exception caught!");
    2970           0 :             exit(1);
    2971           0 :             NOTREACHED();
    2972             :         }
    2973             :     }
    2974             : 
    2975           0 :     return p;
    2976             : }
    2977             : 
    2978             : 
    2979           0 : void snap_child::output_session_log( QString const& what )
    2980             : {
    2981           0 :     QString const method(snapenv(get_name(name_t::SNAP_NAME_CORE_REQUEST_METHOD)));
    2982           0 :     QString const agent(snapenv(get_name(name_t::SNAP_NAME_CORE_HTTP_USER_AGENT)));
    2983           0 :     QString const ip(snapenv(get_name(name_t::SNAP_NAME_CORE_REMOTE_ADDR)));
    2984           0 :     SNAP_LOG_INFO("------------------------------------ ")(ip)
    2985           0 :             (" ")(what)(" snap_child session (")(method)(" ")(f_uri.get_uri())(") with ")(agent);
    2986           0 : }
    2987             : 
    2988             : 
    2989             : /** \brief Process a request from the Snap CGI tool.
    2990             :  *
    2991             :  * The process function accepts a socket that was just connected.
    2992             :  * Only the parent (Snap Server) can call this function. Assuming
    2993             :  * that (1) the parent is calling and (2) that this snap_child
    2994             :  * object is not already in use, then the function forks a new
    2995             :  * process (the child).
    2996             :  *
    2997             :  * The parent acknowledge by saving the new process identifier
    2998             :  * and closing its copy of the TCP socket.
    2999             :  *
    3000             :  * If the fork() call fails (returning -1) then the parent process
    3001             :  * writes an HTTP error in the socket (503 Service Unavailable).
    3002             :  *
    3003             :  * \param[in] client  The client connection to attach to this child.
    3004             :  *
    3005             :  * \return true if the child process was successfully created.
    3006             :  */
    3007           0 : bool snap_child::process(tcp_client_server::bio_client::pointer_t client)
    3008             : {
    3009           0 :     if(f_is_child)
    3010             :     {
    3011             :         // this is a bug! die() on the spot
    3012             :         // (here we ARE in the child process!)
    3013             :         //
    3014           0 :         die(
    3015             :             http_code_t::HTTP_CODE_SERVICE_UNAVAILABLE,
    3016             :             "Server Bug",
    3017             :             "Your Snap! server detected a serious problem. Please check your logs for more information.",
    3018             :             "snap_child::process() was called from a child process.");
    3019           0 :         return false;
    3020             :     }
    3021             : 
    3022           0 :     if(f_child_pid != 0)
    3023             :     {
    3024             :         // this is a bug!
    3025             :         //
    3026             :         // WARNING: At this point we CANNOT call the die() function
    3027             :         //          (we are not the child and have the wrong socket)
    3028             :         //
    3029           0 :         SNAP_LOG_FATAL("BUG: snap_child::process() called when the process is still in use.");
    3030           0 :         return false;
    3031             :     }
    3032             : 
    3033             :     // to avoid the fork use --nofork on the command line
    3034             :     //
    3035           0 :     pid_t const p(fork_child());
    3036           0 :     if(p != 0)
    3037             :     {
    3038             :         // parent process
    3039           0 :         if(p == -1)
    3040             :         {
    3041             :             // WARNING: At this point we CANNOT call the die() function
    3042             :             //          (we are not the child and have the wrong socket)
    3043           0 :             SNAP_LOG_FATAL("snap_child::process() could not create child process, dropping connection.");
    3044           0 :             return false;
    3045             :         }
    3046             : 
    3047             :         // save the process identifier since it worked
    3048           0 :         f_child_pid = p;
    3049             : 
    3050             :         // socket is now the responsibility of the child process
    3051             :         // the accept() call in the parent will close it though
    3052           0 :         return true;
    3053             :     }
    3054             : 
    3055           0 :     f_client = client;
    3056             : 
    3057             :     try
    3058             :     {
    3059           0 :         f_ready = false;
    3060             : 
    3061           0 :         init_start_date();
    3062             : 
    3063             :         // child process
    3064           0 :         f_is_child = true;
    3065             : 
    3066           0 :         read_environment();         // environment to QMap<>
    3067           0 :         setup_uri();                // the raw URI
    3068             : 
    3069             :         // keep that one in release so we can at least know of all
    3070             :         // the hits to the server
    3071             :         //
    3072           0 :         output_session_log( "new" );
    3073             : 
    3074             :         // now we connect to the DB
    3075             :         // move all possible work that does not required the DB before
    3076             :         // this line so we avoid a network connection altogether
    3077             :         //
    3078           0 :         NOTUSED(connect_cassandra(true));  // since we pass 'true', the returned value will always be true
    3079             : 
    3080           0 :         canonicalize_domain();      // using the URI, find the domain core::rules and start the canonicalization process
    3081           0 :         canonicalize_website();     // using the canonicalized domain, find the website core::rules and continue the canonicalization process
    3082             : 
    3083             :         // check whether this website has a redirect and apply it if necessary
    3084             :         // (not a full 301, just show site B instead of site A)
    3085             :         //
    3086           0 :         site_redirect();
    3087             : 
    3088             :         // start the plugins and their initialization
    3089             :         //
    3090           0 :         snap_string_list list_of_plugins(init_plugins(true));
    3091             : 
    3092             :         // run updates if any
    3093             :         //
    3094           0 :         if(f_is_being_initialized)
    3095             :         {
    3096           0 :             update_plugins(list_of_plugins);
    3097             :         }
    3098             :         else
    3099             :         {
    3100             :             // TODO: to be ameliorated big time:
    3101             :             //     a) we should present a really nice page and not a die()
    3102             :             //     b) we should have a state held by snaplock which will
    3103             :             //        be way faster than the database (but we have to
    3104             :             //        reinitialize that state on a reboot...)
    3105             :             //
    3106           0 :             libdbproxy::value const state(get_site_parameter(get_name(name_t::SNAP_NAME_CORE_SITE_STATE)));
    3107           0 :             if(state.nullValue())
    3108             :             {
    3109             :                 // after the very first installation, it is always defined
    3110             :                 //
    3111           0 :                 die(http_code_t::HTTP_CODE_SERVICE_UNAVAILABLE, "",
    3112             :                     "The website is being initialized. Please try again in a moment. Thank you.",
    3113             :                     "The site state is undefined. It is not yet initialized.");
    3114           0 :                 NOTREACHED();
    3115             :             }
    3116           0 :             if(state.stringValue() != "ready")
    3117             :             {
    3118             :                 // at this point, we expect "ready" or "updating"
    3119             :                 //
    3120           0 :                 die(http_code_t::HTTP_CODE_SERVICE_UNAVAILABLE, "",
    3121             :                     "The website is being updated. Please try again in a moment. Thank you.",
    3122           0 :                     QString("The site state is \"%1\", expected \"ready\"."
    3123             :                            " It either was not yet initialized or it is being"
    3124             :                            " updated right now (or the update process crashed?)")
    3125           0 :                                     .arg(state.stringValue()));
    3126           0 :                 NOTREACHED();
    3127             : 
    3128             :                 // double protection, this statement is not reachable
    3129             :                 return false;
    3130             :             }
    3131             :         }
    3132             : 
    3133           0 :         canonicalize_options();    // find the language, branch, and revision specified by the user
    3134             : 
    3135             :         // finally, "execute" the page being accessed
    3136             :         //
    3137           0 :         execute();
    3138             : 
    3139             :         // we could delete ourselves but really only the socket is an
    3140             :         // object that needs to get cleaned up properly and it is done
    3141             :         // in the exit() function.
    3142             :         //delete this;
    3143           0 :         output_session_log("done");
    3144           0 :         exit(0);
    3145           0 :         NOTREACHED();
    3146             :     }
    3147           0 :     catch( snap_lock_failed_exception const & except )
    3148             :     {
    3149           0 :         SNAP_LOG_FATAL("snap_child::process(): snap_lock_failed_exception caught: ")(except.what());
    3150             : 
    3151             :         // die with a "server locked" error instead of a "random" 500
    3152             :         //
    3153           0 :         die(http_code_t::HTTP_CODE_LOCKED, "",
    3154             :             "One of the resources you tried to access is currently locked. Please try again in a moment.",
    3155           0 :             QString("A lock failed (%1)").arg(except.what()));
    3156           0 :         NOTREACHED();
    3157             :     }
    3158           0 :     catch( snap_exception const & except )
    3159             :     {
    3160           0 :         SNAP_LOG_FATAL("snap_child::process(): snap_exception caught: ")(except.what());
    3161             :     }
    3162           0 :     catch( libexcept::exception_t const & e )
    3163             :     {
    3164           0 :         SNAP_LOG_FATAL("snap_child::process(): libexcept::exception_t caught: ")(e.what());
    3165           0 :         for( auto const & stack_string : e.get_stack_trace() )
    3166             :         {
    3167           0 :             SNAP_LOG_ERROR("libexcept(): backtrace=")( stack_string );
    3168             :         }
    3169             :     }
    3170           0 :     catch( libdbproxy::exception const & e )
    3171             :     {
    3172           0 :         SNAP_LOG_FATAL("snap_child::process(): libdbproxy::exception caught: ")(e.what());
    3173           0 :         for( auto const & stack_string : e.get_stack_trace() )
    3174             :         {
    3175           0 :             SNAP_LOG_ERROR("exception(): backtrace=")( stack_string );
    3176             :         }
    3177             :     }
    3178           0 :     catch( std::exception const & std_except )
    3179             :     {
    3180             :         // the snap_logic_exception is not a snap_exception
    3181             :         // and other libraries may generate other exceptions
    3182             :         // (i.e. libtld, C++ cassandra driver...)
    3183             :         //
    3184           0 :         SNAP_LOG_FATAL("snap_child::process(): std::exception caught: ")(std_except.what());
    3185             :     }
    3186           0 :     catch( ... )
    3187             :     {
    3188           0 :         SNAP_LOG_FATAL("snap_child::process(): unknown exception caught!");
    3189             :     }
    3190             : 
    3191           0 :     exit(1);
    3192           0 :     NOTREACHED();
    3193             : 
    3194             :     // compiler expects a return
    3195             :     return false;
    3196             : }
    3197             : 
    3198             : 
    3199             : /** \brief Get a shared pointer copy of the server.
    3200             :  *
    3201             :  * This function returns a shared pointer of the server.
    3202             :  *
    3203             :  * The children only have a weak pointer back to the server. This function
    3204             :  * attempts a lock. If the lock fails, then the function throws.
    3205             :  *
    3206             :  * \return The pointer to the server.
    3207             :  */
    3208           0 : server::pointer_t snap_child::get_server() const
    3209             : {
    3210           0 :     server::pointer_t server(f_server.lock());
    3211           0 :     if(!server)
    3212             :     {
    3213           0 :         throw snap_logic_exception("could not lock server pointer in snap_child::get_server()");
    3214             :     }
    3215             : 
    3216           0 :     return server;
    3217             : }
    3218             : 
    3219             : 
    3220             : /** \brief Retrieve the child process identifier.
    3221             :  *
    3222             :  * Once a child process started, it gets assigned a pid_t value. In
    3223             :  * the parent process, this value can be retrieved using this function.
    3224             :  * In the child process, just use getpid().
    3225             :  *
    3226             :  * \return This snap_child process identifier.
    3227             :  */
    3228           0 : pid_t snap_child::get_child_pid() const
    3229             : {
    3230           0 :     return f_child_pid;
    3231             : }
    3232             : 
    3233             : 
    3234             : /** \brief Kill running process.
    3235             :  *
    3236             :  * Use this method to stop a child process. It first sends the SIGTERM
    3237             :  * message, then if the status doesn't change after a brief interval,
    3238             :  * SIGKILL is used.
    3239             :  *
    3240             :  * \sa check_status()
    3241             :  */
    3242           0 : void snap_child::kill()
    3243             : {
    3244           0 :     if( f_child_pid == 0 )
    3245             :     {
    3246             :         // Child is not running
    3247             :         //
    3248           0 :         return;
    3249             :     }
    3250             : 
    3251           0 :     ::kill( f_child_pid, SIGTERM );
    3252           0 :     int timeout = 5;
    3253           0 :     while( check_status() == status_t::SNAP_CHILD_STATUS_RUNNING )
    3254             :     {
    3255           0 :         sleep( 1 );
    3256           0 :         if( --timeout <= 0 )
    3257             :         {
    3258           0 :             ::kill( f_child_pid, SIGKILL );
    3259           0 :             break;
    3260             :         }
    3261             :     }
    3262             : }
    3263             : 
    3264             : 
    3265             : /** \brief Check the status of the child process.
    3266             :  *
    3267             :  * This function checks whether the child is still running or not.
    3268             :  * The function returns the current status such as running,
    3269             :  * or ready (to process a request.)
    3270             :  *
    3271             :  * The child process is not expected to call this function. It knows
    3272             :  * it is running if it can anyway.
    3273             :  *
    3274             :  * The parent uses the wait() instruction to check the current status
    3275             :  * if the process is running (f_child_pid is not zero.)
    3276             :  *
    3277             :  * \return The status of the child.
    3278             :  */
    3279           0 : snap_child::status_t snap_child::check_status()
    3280             : {
    3281           0 :     if(f_is_child)
    3282             :     {
    3283             :         // XXX -- call die() instead
    3284           0 :         SNAP_LOG_FATAL("snap_child::check_status() was called from the child process.");
    3285           0 :         return status_t::SNAP_CHILD_STATUS_RUNNING;
    3286             :     }
    3287             : 
    3288           0 :     if(f_child_pid != 0)
    3289             :     {
    3290             :         int status;
    3291           0 :         pid_t const r = waitpid(f_child_pid, &status, WNOHANG);
    3292           0 :         if(r == static_cast<pid_t>(-1))
    3293             :         {
    3294           0 :             int const e(errno);
    3295           0 :             SNAP_LOG_FATAL("a waitpid() returned an error (")(e)(" -- ")(strerror(e))(")");
    3296             :         }
    3297           0 :         else if(r == f_child_pid)
    3298             :         {
    3299             :             // the status of our child changed
    3300             : #pragma GCC diagnostic push
    3301             : #pragma GCC diagnostic ignored "-Wold-style-cast"
    3302           0 :             if(WIFEXITED(status))
    3303             :             {
    3304             :                 // stopped with exit() or return in main()
    3305             :                 // TODO: log info if exit() != 0?
    3306             :             }
    3307           0 :             else if(WIFSIGNALED(status))
    3308             :             {
    3309             :                 // stopped because of a signal
    3310           0 :                 SNAP_LOG_ERROR("child process ")(f_child_pid)(" exited after it received signal #")(WTERMSIG(status));
    3311             :             }
    3312             :             // else -- other statuses are ignored for now
    3313             : #pragma GCC diagnostic pop
    3314             : 
    3315             :             // mark that child as done
    3316           0 :             f_child_pid = 0;
    3317             :         }
    3318           0 :         else if(r != 0)
    3319             :         {
    3320             :             // TODO: throw instead?
    3321           0 :             SNAP_LOG_ERROR("waitpid() returned ")(r)(", we expected -1, 0 or ")(f_child_pid)(" instead.");
    3322             :         }
    3323             :     }
    3324             : 
    3325           0 :     return f_child_pid == 0 ? status_t::SNAP_CHILD_STATUS_READY : status_t::SNAP_CHILD_STATUS_RUNNING;
    3326             : }
    3327             : 
    3328             : 
    3329             : /** \brief Read the command and eventually the environment sent by snap.cgi
    3330             :  *
    3331             :  * The socket starts with a one line command. The command may be followed
    3332             :  * by additional entries such as the Apache environment when the Snap CGI
    3333             :  * connects to us.
    3334             :  *
    3335             :  * When the environment is defined, it is saved in a map so all the other
    3336             :  * functions can later retrieve those values from the child. Note that at
    3337             :  * this point the script does not tweak that data.
    3338             :  *
    3339             :  * To make sure that the entire environment is sent, snap.cgi starts the
    3340             :  * feed with "\#START\\n" and terminate it with "\#END\\n".
    3341             :  *
    3342             :  * Note that unless we are receiving the Apache environment from the snap.cgi
    3343             :  * tool, we do NOT return. This is important because when returning we start
    3344             :  * generating a web page which is not what we want for the other instructions
    3345             :  * such as the \#INFO.
    3346             :  *
    3347             :  * \section commands Understood Commands
    3348             :  *
    3349             :  * \li \#START
    3350             :  *
    3351             :  * Start passing the environment to the server.
    3352             :  *
    3353             :  * \li \#INFO
    3354             :  *
    3355             :  * Request for information about the server. The result is an environment
    3356             :  * like variable/value pairs. Mainly versions are returned in that buffer.
    3357             :  * Use the \#STATS for statistics information.
    3358             :  *
    3359             :  * \li \#STATS
    3360             :  *
    3361             :  * Request for statistics about this server instance. The result is an
    3362             :  * environment like variable/value pairs. This command generates values
    3363             :  * such as the total number of requests received, the number of children
    3364             :  * currently running, etc.
    3365             :  */
    3366           0 : void snap_child::read_environment()
    3367             : {
    3368           0 :     class read_env
    3369             :     {
    3370             :     public:
    3371           0 :         read_env(snap_child * snap, tcp_client_server::bio_client::pointer_t client, environment_map_t & env, environment_map_t & browser_cookies, environment_map_t & post, post_file_map_t & files)
    3372           0 :             : f_snap(snap)
    3373             :             , f_client(client)
    3374             :             , f_env(env)
    3375             :             , f_browser_cookies(browser_cookies)
    3376             :             , f_post(post)
    3377           0 :             , f_files(files)
    3378             :         {
    3379           0 :         }
    3380             : 
    3381             :         read_env(read_env const & rhs) = delete;
    3382             :         read_env & operator = (read_env const & rhs) = delete;
    3383             : 
    3384           0 :         void die(QString const & details) const
    3385             :         {
    3386           0 :             f_snap->die(http_code_t::HTTP_CODE_SERVICE_UNAVAILABLE, "",
    3387             :                 "Unstable network connection",
    3388           0 :                 QString("an error occurred while reading the environment from the socket in the server child process (%1).").arg(details));
    3389           0 :             NOTREACHED();
    3390             :         }
    3391             : 
    3392           0 :         char getc() const
    3393             :         {
    3394             :             char c;
    3395             : 
    3396             :             //if(f_unget != '\0')
    3397             :             //{
    3398             :             //    c = f_unget;
    3399             :             //    f_unget = '\0';
    3400             :             //    return c;
    3401             :             //}
    3402             : 
    3403             :             // this read blocks, so we read just 1 char. because we
    3404             :             // want to stop calling read() as soon as possible (otherwise
    3405             :             // we would be blocked here forever)
    3406           0 :             if(f_client->read(&c, 1) != 1)
    3407             :             {
    3408           0 :                 int const e(errno);
    3409           0 :                 die(QString("I/O error, errno: %1").arg(e));
    3410           0 :                 NOTREACHED();
    3411             :             }
    3412           0 :             return c;
    3413             :         }
    3414             : 
    3415             :         //void ungetc(char c)
    3416             :         //{
    3417             :         //    if(f_unget != '\0')
    3418             :         //    {
    3419             :         //        die("too many unget() called");
    3420             :         //        NOTREACHED();
    3421             :         //    }
    3422             :         //    f_unget = c;
    3423             :         //}
    3424             : 
    3425           0 :         void start_process()
    3426             :         {
    3427             :             // #INFO
    3428           0 :             if(f_name == "#INFO")
    3429             :             {
    3430           0 :                 f_snap->snap_info();
    3431           0 :                 NOTREACHED();
    3432             :             }
    3433             : 
    3434             :             // #STATS
    3435           0 :             if(f_name == "#STATS")
    3436             :             {
    3437           0 :                 f_snap->snap_statistics();
    3438           0 :                 NOTREACHED();
    3439             :             }
    3440             : 
    3441             :             // #INIT
    3442           0 :             if(f_name == "#INIT")
    3443             :             {
    3444             :                 // user wants to initialize a website
    3445           0 :                 f_snap->mark_for_initialization();
    3446             :             }
    3447             :             else
    3448             :             {
    3449             :                 // #START
    3450           0 :                 if(f_name != "#START")
    3451             :                 {
    3452           0 :                     die("#START or other supported command missing.");
    3453           0 :                     NOTREACHED();
    3454             :                 }
    3455             :             }
    3456             :             // TODO add support for a version: #START=1.2 or #INIT=1.2
    3457             :             //      so that way the server can cleanly "break" if the
    3458             :             //      snap.cgi or other client version is not compatible
    3459             : 
    3460           0 :             f_started = true;
    3461           0 :             f_name.clear();
    3462           0 :             f_value.clear();
    3463           0 :         }
    3464             : 
    3465           0 :         void process_post_variable()
    3466             :         {
    3467             :             // here we have a set of post environment variables (header)
    3468             :             // and the f_post_content which represents the value of the field
    3469             :             //
    3470             :             // Content-Disposition: form-data; name="field-name"
    3471             :             // Content-Type: image/gif
    3472             :             // Content-Transfer-Encoding: binary
    3473             :             //
    3474             :             // The Content-Type cannot be used with plain variables. We
    3475             :             // distinguish plain variables from files as the
    3476             :             // Content-Disposition includes a filename="..." parameter.
    3477             :             //
    3478           0 :             if(!f_post_environment.contains("CONTENT-DISPOSITION"))
    3479             :             {
    3480           0 :                 die("multipart posts must have a Content-Disposition header to be considered valid.");
    3481           0 :                 NOTREACHED();
    3482             :             }
    3483             :             // TODO: verify and if necessary fix this as the ';' could I think
    3484             :             //       appear in a string; looking at the docs, I'm not too sure
    3485             :             //       but it looks like we would need to support the
    3486             :             //       extended-value and extended-other-values as defined in
    3487             :             //       http://tools.ietf.org/html/rfc2184
    3488           0 :             snap_string_list disposition(f_post_environment["CONTENT-DISPOSITION"].split(";"));
    3489           0 :             if(disposition.size() < 2)
    3490             :             {
    3491           0 :                 die(QString("multipart posts Content-Disposition must at least include \"form-data\" and a name parameter, \"%1\" is not valid.")
    3492           0 :                             .arg(f_post_environment["CONTENT-DISPOSITION"]));
    3493           0 :                 NOTREACHED();
    3494             :             }
    3495           0 :             if(disposition[0].trimmed() != "form-data")
    3496             :             {
    3497             :                 // not happy if we don't get form-data parts
    3498           0 :                 die(QString("multipart posts Content-Disposition must be a \"form-data\", \"%1\" is not valid.")
    3499           0 :                             .arg(f_post_environment["CONTENT-DISPOSITION"]));
    3500           0 :                 NOTREACHED();
    3501             :             }
    3502             :             // retrieve all the parameters, then keep those we want to keep
    3503           0 :             int const max_strings(disposition.size());
    3504           0 :             environment_map_t params;
    3505           0 :             for(int i(1); i < max_strings; ++i)
    3506             :             {
    3507             :                 // each parameter is name=<value>
    3508           0 :                 snap_string_list nv(disposition[i].split('='));
    3509           0 :                 if(nv.size() != 2)
    3510             :                 {
    3511           0 :                     die(QString("parameter %1 in this multipart posts Content-Disposition does not include an equal character so \"%1\" is not valid.")
    3512           0 :                                 .arg(i)
    3513           0 :                                 .arg(f_post_environment["CONTENT-DISPOSITION"]));
    3514           0 :                     NOTREACHED();
    3515             :                 }
    3516           0 :                 nv[0] = nv[0].trimmed().toLower(); // case insensitive
    3517           0 :                 nv[1] = nv[1].trimmed();
    3518           0 :                 if(nv[1].startsWith("\"")
    3519           0 :                 && nv[1].endsWith("\""))
    3520             :                 {
    3521           0 :                     nv[1] = nv[1].mid(1, nv[1].length() - 2);
    3522             :                 }
    3523           0 :                 params[nv[0]] = nv[1];
    3524             :             }
    3525           0 :             if(!params.contains("name"))
    3526             :             {
    3527           0 :                 die(QString("multipart posts Content-Disposition must include a name=\"...\" parameter, \"%1\" is not valid.")
    3528           0 :                             .arg(f_post_environment["CONTENT-DISPOSITION"]));
    3529           0 :                 NOTREACHED();
    3530             :             }
    3531             :             // remove the ending \r\n
    3532           0 :             if(f_post_content.right(2) == "\r\n")
    3533             :             {
    3534           0 :                 f_post_content.resize(f_post_content.size() - 2);
    3535             :             }
    3536           0 :             else if(f_post_content.right(1) == "\n"
    3537           0 :                  || f_post_content.right(1) == "\r")
    3538             :             {
    3539           0 :                 f_post_content.resize(f_post_content.size() - 1);
    3540             :             }
    3541           0 :             f_name = params["name"];
    3542           0 :             if(params.contains("filename"))
    3543             :             {
    3544             :                 // make sure the filename is unique otherwise we'd overwrite
    3545             :                 // the previous file with the same name...
    3546             :                 //
    3547           0 :                 QString filename(params["filename"]);
    3548             : 
    3549             :                 // this is a file so we want to save it in the f_file and
    3550             :                 // not in the f_post although we do create an f_post entry
    3551             :                 // with the filename
    3552             :                 //
    3553           0 :                 if(f_post.contains(f_name))
    3554             :                 {
    3555           0 :                     die(QString("multipart post variable \"%1\" defined twice")
    3556           0 :                                 .arg(f_name));
    3557           0 :                     NOTREACHED();
    3558             :                 }
    3559           0 :                 f_post[f_name] = filename;
    3560             : 
    3561             :                 // an empty filename means no file was uploaded (which
    3562             :                 // is fine for optional fields or fields that are already
    3563             :                 // attached to a file anyway.)
    3564             :                 //
    3565           0 :                 if(!filename.isEmpty())
    3566             :                 {
    3567           0 :                     post_file_t & file(f_files[f_name]);
    3568           0 :                     file.set_name(f_name);
    3569           0 :                     file.set_filename(filename);
    3570           0 :                     ++f_post_index; // 1-based
    3571           0 :                     file.set_index(f_post_index);
    3572           0 :                     file.set_data(f_post_content);
    3573           0 :                     if(params.contains("creation-date"))
    3574             :                     {
    3575           0 :                         file.set_creation_time(string_to_date(params["creation-date"]));
    3576             :                     }
    3577           0 :                     if(params.contains("modification-date"))
    3578             :                     {
    3579           0 :                         file.set_modification_time(string_to_date(params["modification-date"]));
    3580             :                     }
    3581             :                     // Content-Type is actually expected on this side
    3582           0 :                     if(f_post_environment.contains("CONTENT-TYPE"))
    3583             :                     {
    3584           0 :                         file.set_original_mime_type(f_post_environment["CONTENT-TYPE"]);
    3585             :                     }
    3586             :                     //if(params.contains("description"))
    3587             :                     //{
    3588             :                     //    file.set_description(string_to_date(params["description"]));
    3589             :                     //}
    3590             :                     // for images also get the dimensions (width x height)
    3591             :                     // note that some images are not detected properly by the
    3592             :                     // magic library so we ignore the MIME type here
    3593           0 :                     snap_image info;
    3594           0 :                     if(info.get_info(f_post_content))
    3595             :                     {
    3596           0 :                         if(info.get_size() > 0)
    3597             :                         {
    3598           0 :                             smart_snap_image_buffer_t buffer(info.get_buffer(0));
    3599           0 :                             file.set_image_width(buffer->get_width());
    3600           0 :                             file.set_image_height(buffer->get_height());
    3601           0 :                             file.set_mime_type(buffer->get_mime_type());
    3602             :                         }
    3603             :                     }
    3604             : #ifdef DEBUG
    3605           0 : SNAP_LOG_TRACE() << " f_files[\"" << f_name << "\"] = \"...\" (Filename: \"" << filename
    3606           0 :     << "\" MIME: " << file.get_mime_type() << ", size: " << f_post_content.size() << ")";
    3607             : #endif
    3608             :                 }
    3609             :             }
    3610             :             else
    3611             :             {
    3612             :                 // this is a simple parameter
    3613           0 :                 if(f_post_environment.contains("CONTENT-TYPE"))
    3614             :                 {
    3615             :                     // XXX accept a few valid types? it should not be necessary...
    3616             :                     // the character encoding is defined as the form, page,
    3617             :                     // or UTF-8 encoding; Content-Type not permitted here!
    3618           0 :                     die(QString("multipart posts Content-Type is not allowed with simple parameters."));
    3619           0 :                     NOTREACHED();
    3620             :                 }
    3621             :                 // TODO verify that the content of a post just needs to be
    3622             :                 //      decoded or whether it already is UTF-8 as required
    3623             :                 //      to be saved in f_post
    3624           0 :                 if(f_post.contains(f_name))
    3625             :                 {
    3626           0 :                     die(QString("multipart post variable \"%1\" defined twice")
    3627           0 :                                 .arg(f_name));
    3628           0 :                     NOTREACHED();
    3629             :                 }
    3630             :                 // append a '\0' so we can call is_valid_utf8()
    3631           0 :                 char const nul('\0');
    3632           0 :                 f_post_content.append(nul);
    3633           0 :                 if(!libutf8::is_valid_utf8(f_post_content.data())) // TODO: see whether controls should be forbidden (except tabs?)
    3634             :                 {
    3635           0 :                     f_snap->die(http_code_t::HTTP_CODE_BAD_REQUEST, "Invalid Form Content",
    3636             :                         "Your form includes characters that are not compatible with the UTF-8 encoding. Try to avoid special characters and try again. If you are using Internet Explorer, know that older versions may not be compatible with international characters.",
    3637             :                         "is_valid_utf8() returned false against the user's content");
    3638           0 :                     NOTREACHED();
    3639             :                 }
    3640             :                 // make sure to view the input as UTF-8 characters
    3641           0 :                 f_post[f_name] = QString::fromUtf8(f_post_content.data(), f_post_content.size() - 1); //snap_uri::urldecode(f_post_content, true);?
    3642             : #ifdef DEBUG
    3643             : //SNAP_LOG_TRACE() << " f_post[\"" << f_name << "\"] = \"" << f_post_content.data() << "\"\n";
    3644             : #endif
    3645             :             }
    3646           0 :         }
    3647             : 
    3648           0 :         bool process_post_line()
    3649             :         {
    3650             :             // found a marker?
    3651           0 :             if(f_post_line.length() >= f_boundary.length())
    3652             :             {
    3653           0 :                 if(f_post_line == f_end_boundary)
    3654             :                 {
    3655           0 :                     if(f_post_first)
    3656             :                     {
    3657           0 :                         die("got end boundary without a start");
    3658           0 :                         NOTREACHED();
    3659             :                     }
    3660           0 :                     process_post_variable();
    3661           0 :                     return true;
    3662             :                 }
    3663             : 
    3664           0 :                 if(f_post_line == f_boundary)
    3665             :                 {
    3666             :                     // got the first boundary yet?
    3667           0 :                     if(f_post_first)
    3668             :                     {
    3669             :                         // we got the first boundary
    3670           0 :                         f_post_first = false;
    3671           0 :                         return false;
    3672             :                     }
    3673           0 :                     process_post_variable();
    3674             : 
    3675             :                     // on next line, we are reading a new header
    3676           0 :                     f_post_header = true;
    3677             : 
    3678             :                     // we are done with those in this iteration
    3679           0 :                     f_post_environment.clear();
    3680           0 :                     f_post_content.clear();
    3681           0 :                     return false;
    3682             :                 }
    3683             :             }
    3684             : 
    3685           0 :             if(f_post_first)
    3686             :             {
    3687           0 :                 die("the first POST boundary is missing.");
    3688           0 :                 NOTREACHED();
    3689             :             }
    3690             : 
    3691           0 :             if(f_post_header)
    3692             :             {
    3693           0 :                 if(f_post_line.isEmpty() || (f_post_line.size() == 1 && f_post_line.at(0) == '\r'))
    3694             :                 {
    3695             :                     // end of the header
    3696           0 :                     f_post_header = false;
    3697           0 :                     return false;
    3698             :                 }
    3699           0 :                 char const nul('\0');
    3700           0 :                 f_post_line.append(nul);
    3701           0 :                 if(!libutf8::is_valid_ascii(f_post_line.data())) // TODO: see whether controls should be forbidden (except tabs?)
    3702             :                 {
    3703           0 :                     f_snap->die(http_code_t::HTTP_CODE_BAD_REQUEST, "Invalid Form Content",
    3704             :                         "Your multi-part form header includes characters that are not compatible with the ASCII encoding.",
    3705             :                         "is_valid_ascii() returned false against a line of the user's multipart form header");
    3706           0 :                     NOTREACHED();
    3707             :                 }
    3708             : 
    3709             :                 // we got a header (Blah: value)
    3710           0 :                 QString const line(f_post_line);
    3711             : #ifdef DEBUG
    3712             : //SNAP_LOG_TRACE(" ++ header line [\n")(line.trimmed())("\n] ")(line.size());
    3713             : #endif
    3714           0 :                 if(isspace(line.at(0).unicode()))
    3715             :                 {
    3716             :                     // continuation of the previous header, concatenate
    3717           0 :                     f_post_environment[f_name] += " " + line.trimmed();
    3718             :                 }
    3719             :                 else
    3720             :                 {
    3721             :                     // new header
    3722           0 :                     int const p(line.indexOf(':'));
    3723           0 :                     if(p == -1)
    3724             :                     {
    3725           0 :                         die("invalid header variable name/value pair, no ':' found.");
    3726           0 :                         NOTREACHED();
    3727             :                     }
    3728             :                     // render name case insensitive
    3729           0 :                     f_name = line.mid(0, p).trimmed().toUpper();
    3730             :                     // TODO: verify that f_name is a valid header name
    3731           0 :                     f_post_environment[f_name] = line.mid(p + 1).trimmed();
    3732             :                 }
    3733             :             }
    3734             :             else
    3735             :             {
    3736             :                 // this is content for the current variable
    3737           0 :                 f_post_content += f_post_line;
    3738           0 :                 f_post_content += '\n'; // the '\n' was not added to f_post_line
    3739             :             }
    3740             : 
    3741           0 :             return false;
    3742             :         }
    3743             : 
    3744           0 :         void process_post()
    3745             :         {
    3746             :             // one POST per request!
    3747           0 :             if(f_has_post)
    3748             :             {
    3749           0 :                 die("at most 1 #POST is accepted in the environment.");
    3750           0 :                 NOTREACHED();
    3751             :             }
    3752           0 :             f_has_post = true;
    3753             : 
    3754           0 :             if(!f_env.contains("CONTENT_TYPE")
    3755           0 :             || !f_env["CONTENT_TYPE"].startsWith("multipart/form-data"))
    3756             :             {
    3757             :                 // standard post, just return and let the main loop
    3758             :                 // handle the name/value pairs
    3759           0 :                 return;
    3760             :             }
    3761             : 
    3762             :             // multi-part posts require special handling
    3763             :             // (i.e. these are not simple VAR=VALUE)
    3764             :             //
    3765             :             // the POST is going to be multiple lines with
    3766             :             // \r characters included! We read then all
    3767             :             // up to the closing boundary
    3768             :             //
    3769             :             // Example of such a variable:
    3770             :             // CONTENT_TYPE=multipart/form-data; boundary=---------5767747
    3771             :             //
    3772             :             // IMPORTANT NOTE:
    3773             :             // Sub-parts are NOT supported in HTML POST messages. This is
    3774             :             // clearly mentioned in HTML5 documentations:
    3775             :             // http://www.w3.org/html/wg/drafts/html/master/forms.html#multipart-form-data
    3776             : 
    3777             :             // 1. Get the main boundary from the CONTENT_TYPE
    3778           0 :             snap_string_list content_info(f_env["CONTENT_TYPE"].split(';'));
    3779           0 :             QString boundary;
    3780           0 :             int const max_strings(content_info.size());
    3781           0 :             for(int i(1); i < max_strings; ++i)
    3782             :             {
    3783           0 :                 QString param(content_info[i].trimmed());
    3784           0 :                 if(param.startsWith("boundary="))
    3785             :                 {
    3786           0 :                     boundary = param.mid(9).trimmed();
    3787           0 :                     break;
    3788             :                 }
    3789             :             }
    3790           0 :             if(boundary.isEmpty())
    3791             :             {
    3792           0 :                 die("multipart POST does not include a valid boundary.");
    3793           0 :                 NOTREACHED();
    3794             :             }
    3795           0 :             f_boundary = ("--" + boundary).toLatin1();
    3796           0 :             f_end_boundary = f_boundary;
    3797           0 :             f_end_boundary.append("--\r", 3);
    3798           0 :             f_boundary += '\r';
    3799             :             //f_post_first = true; -- function cannot be called more than once
    3800             :             //f_post_header = true;
    3801             : 
    3802             :             for(;;)
    3803             :             {
    3804           0 :                 char const c(getc());
    3805           0 :                 if(c == '\n')
    3806             :                 {
    3807           0 :                     if(process_post_line())
    3808             :                     {
    3809           0 :                         return;
    3810             :                     }
    3811           0 :                     f_post_line.clear();
    3812             :                 }
    3813             :                 else
    3814             :                 {
    3815           0 :                     f_post_line += c;
    3816             :                 }
    3817           0 :             }
    3818             :         }
    3819             : 
    3820           0 :         void process_line()
    3821             :         {
    3822             :             // not started yet? check low level commands then
    3823           0 :             if(!f_started)
    3824             :             {
    3825           0 :                 start_process();
    3826           0 :                 return;
    3827             :             }
    3828             : 
    3829             :             // got to the end?
    3830           0 :             if(f_name == "#END")
    3831             :             {
    3832           0 :                 f_running = false;
    3833           0 :                 return;
    3834             :             }
    3835             : 
    3836             :             // got a POST?
    3837           0 :             if(f_name == "#POST")
    3838             :             {
    3839           0 :                 process_post();
    3840           0 :                 return;
    3841             :             }
    3842             : 
    3843           0 :             if(f_name.isEmpty())
    3844             :             {
    3845           0 :                 die("empty lines are not accepted in the child environment.");
    3846           0 :                 NOTREACHED();
    3847             :             }
    3848           0 :             if(f_has_post)
    3849             :             {
    3850           0 :                 f_post[f_name] = snap_uri::urldecode(f_value, true);
    3851             : #ifdef DEBUG
    3852             : //SNAP_LOG_TRACE("(simple) f_post[\"")(f_name)("\"] = \"")(f_value)("\" (\"")(f_post[f_name])("\");");
    3853             : #endif
    3854             :             }
    3855             :             else
    3856             :             {
    3857           0 :                 if(f_name == "HTTP_COOKIE")
    3858             :                 {
    3859             :                     // special case for cookies
    3860           0 :                     snap_string_list cookies(f_value.split(';', QString::SkipEmptyParts));
    3861             : #ifdef DEBUG
    3862             : //SNAP_LOG_TRACE(" HTTP_COOKIE = [\"")(f_value)("\"]");
    3863             : #endif
    3864           0 :                     int const max_strings(cookies.size());
    3865           0 :                     for(int i(0); i < max_strings; ++i)
    3866             :                     {
    3867           0 :                         QString const name_value(cookies[i]);
    3868           0 :                         snap_string_list nv(name_value.trimmed().split('=', QString::SkipEmptyParts));
    3869           0 :                         if(nv.size() == 2)
    3870             :                         {
    3871             :                             // XXX check with other systems to see
    3872             :                             //     whether urldecode() is indeed
    3873             :                             //     necessary here
    3874           0 :                             QString const cookie_name(snap_uri::urldecode(nv[0], true));
    3875           0 :                             QString const cookie_value(snap_uri::urldecode(nv[1], true));
    3876             : //std::cerr << " f_browser_cookies[\"" << cookie_name << "\"] = \"" << cookie_value << "\"; (\"" << cookies[i] << "\")\n";
    3877           0 :                             if(f_browser_cookies.contains(cookie_name))
    3878             :                             {
    3879             :                                 // This happens when you have a cookie with the
    3880             :                                 // same name defined with multiple domains,
    3881             :                                 // multiple paths, multiple expiration dates...
    3882             :                                 //
    3883             :                                 // Unfortunately, we are not told which one is
    3884             :                                 // which when we reach this line of code, yet
    3885             :                                 // the last one will be the most qualified one
    3886             :                                 // according to most browsers and servers
    3887             :                                 //
    3888             :                                 // http://tools.ietf.org/html/rfc6265#section-5.4
    3889             :                                 //
    3890           0 :                                 SNAP_LOG_DEBUG(QString("cookie \"%1\" defined twice")
    3891           0 :                                             .arg(cookie_name));
    3892             :                             }
    3893           0 :                             f_browser_cookies[cookie_name] = cookie_value;
    3894             :                         }
    3895             :                     }
    3896             :                 }
    3897             :                 else
    3898             :                 {
    3899             :                     // TODO: verify that f_name is a valid header name
    3900           0 :                     f_env[f_name] = f_value;
    3901             : #ifdef DEBUG
    3902             : //SNAP_LOG_TRACE(" f_env[\"")(f_name)("\"] = \"")(f_value)("\"");
    3903             : #endif
    3904             :                 }
    3905             :             }
    3906             :         }
    3907             : 
    3908           0 :         void run()
    3909             :         {
    3910           0 :             bool reading_name(true);
    3911           0 :             do
    3912             :             {
    3913           0 :                 char const c = getc();
    3914           0 :                 if(c == '=' && reading_name)
    3915             :                 {
    3916           0 :                     reading_name = false;
    3917             :                 }
    3918           0 :                 else if(c == '\n')
    3919             :                 {
    3920             : #ifdef DEBUG
    3921             : //SNAP_LOG_TRACE("f_name=")(f_name)(", f_value=\"")(f_value)("\" when reading the \\n");
    3922             : #endif
    3923           0 :                     process_line();
    3924             : 
    3925             :                     // clear for next line
    3926           0 :                     f_name.clear();
    3927           0 :                     f_value.clear();
    3928           0 :                     reading_name = true;
    3929             :                 }
    3930           0 :                 else if(c == '\r')
    3931             :                 {
    3932           0 :                     die("got a \\r character in the environment (not in a multi-part POST)");
    3933           0 :                     NOTREACHED();
    3934             :                 }
    3935           0 :                 else if(reading_name)
    3936             :                 {
    3937           0 :                     if(isspace(c))
    3938             :                     {
    3939           0 :                         die("spaces are not allowed in environment variable names");
    3940           0 :                         NOTREACHED();
    3941             :                     }
    3942           0 :                     f_name += c;
    3943             :                 }
    3944             :                 else
    3945             :                 {
    3946           0 :                     f_value += c;
    3947             :                 }
    3948             :             }
    3949           0 :             while(f_running);
    3950           0 :         }
    3951             : 
    3952           0 :         bool has_post() const
    3953             :         {
    3954           0 :             return f_has_post;
    3955             :         }
    3956             : 
    3957             : #ifdef DEBUG
    3958           0 :         void output_debug_log()
    3959             :         {
    3960           0 :             std::stringstream ss;
    3961           0 :             ss << "post:" << std::endl;
    3962           0 :             for( auto const & pair : f_post.toStdMap() )
    3963             :             {
    3964           0 :                 ss << pair.first << ": " << pair.second << std::endl;
    3965             :             }
    3966             : 
    3967             :             //SNAP_LOG_DEBUG( ss.str().c_str() );
    3968           0 :         }
    3969             : #endif
    3970             : 
    3971             :     private:
    3972             :         mutable snap_child *        f_snap = nullptr;
    3973             :         tcp_client_server::bio_client::pointer_t
    3974             :                                     f_client = tcp_client_server::bio_client::pointer_t();
    3975             :         //char                        f_unget = 0;
    3976             :         bool                        f_running = true;
    3977             :         bool                        f_started = false;
    3978             : 
    3979             :         environment_map_t &         f_env;
    3980             :         environment_map_t &         f_browser_cookies;
    3981             :         QString                     f_name = QString();
    3982             :         QString                     f_value = QString();
    3983             : 
    3984             :         bool                        f_has_post = false;
    3985             :         environment_map_t &         f_post;
    3986             :         post_file_map_t &           f_files;
    3987             :         bool                        f_post_first = true;
    3988             :         bool                        f_post_header = true;
    3989             :         QByteArray                  f_post_line = QByteArray();
    3990             :         QByteArray                  f_post_content = QByteArray();
    3991             :         QByteArray                  f_boundary = QByteArray();
    3992             :         QByteArray                  f_end_boundary = QByteArray();
    3993             :         environment_map_t           f_post_environment = environment_map_t();
    3994             :         uint32_t                    f_post_index = 0;
    3995             :     };
    3996             : 
    3997             :     // reset the old environment
    3998           0 :     f_env.clear();
    3999           0 :     f_post.clear();
    4000           0 :     f_files.clear();
    4001             : 
    4002             : #ifdef DEBUG
    4003           0 :     SNAP_LOG_TRACE("Read environment variables including POST data.");
    4004             : #endif
    4005             : 
    4006           0 :     read_env r(this, f_client, f_env, f_browser_cookies, f_post, f_files);
    4007             : #ifdef DEBUG
    4008           0 :     r.output_debug_log();
    4009             : #endif
    4010           0 :     r.run();
    4011           0 :     f_has_post = r.has_post();
    4012             : 
    4013             : #ifdef DEBUG
    4014           0 :     SNAP_LOG_TRACE("Done reading environment variables.")(f_has_post ? " (This request includes a POST)" : "");
    4015             : #endif
    4016             : 
    4017           0 :     trace("#START\n");
    4018           0 : }
    4019             : 
    4020             : 
    4021             : /** \brief Mark the server for initialization.
    4022             :  *
    4023             :  * This special flag allows us to initialize the plugins without having
    4024             :  * to actually try to access a specific website.
    4025             :  */
    4026           0 : void snap_child::mark_for_initialization()
    4027             : {
    4028           0 :     f_is_being_initialized = true;
    4029           0 : }
    4030             : 
    4031             : 
    4032             : /** \brief Write data to the output socket.
    4033             :  *
    4034             :  * This function writes data to the output socket.
    4035             :  *
    4036             :  * \exception runtime_error
    4037             :  * This exception is thrown if the write() fails writing all the bytes. This
    4038             :  * generally means the client closed the socket early.
    4039             :  *
    4040             :  * \param[in] data  The data pointer.
    4041             :  * \param[in] size  The number of bytes to write from data.
    4042             :  */
    4043           0 : void snap_child::write(char const * data, ssize_t size)
    4044             : {
    4045           0 :     if(!f_client)
    4046             :     {
    4047             :         // this happens from backends that do not have snap.cgi running
    4048           0 :         return;
    4049             :     }
    4050             : 
    4051           0 :     if(f_client->write(data, size) != size)
    4052             :     {
    4053           0 :         SNAP_LOG_FATAL("error while sending data to a client.");
    4054             :         // XXX throw? we cannot call die() because die() calls write()!
    4055           0 :         throw std::runtime_error("error while sending data to the client");
    4056             :     }
    4057             : }
    4058             : 
    4059             : 
    4060             : /** \brief Write a string to the socket.
    4061             :  *
    4062             :  * This function is an overload of the write() data buffer. It writes the
    4063             :  * specified string using strlen() to obtain the length. The string must be
    4064             :  * a valid null terminated C string.
    4065             :  *
    4066             :  * \param[in] str  The string to write to the socket.
    4067             :  */
    4068           0 : void snap_child::write(char const *str)
    4069             : {
    4070           0 :     write(str, strlen(str));
    4071           0 : }
    4072             : 
    4073             : 
    4074             : /** \brief Write a QString to the socket.
    4075             :  *
    4076             :  * This function is an overload that writes the QString to the socket. This
    4077             :  * QString is transformed to UTF-8 before being processed.
    4078             :  *
    4079             :  * \param[in] str  The QString to write to the socket.
    4080             :  */
    4081           0 : void snap_child::write(QString const& str)
    4082             : {
    4083           0 :     QByteArray a(str.toUtf8());
    4084           0 :     write(a.data(), a.size());
    4085           0 : }
    4086             : 
    4087             : 
    4088             : /** \brief Generate the Snap information buffer and return it.
    4089             :  *
    4090             :  * This function prints out information about the Snap! Server.
    4091             :  * This means writing information about all the different libraries
    4092             :  * in use such as their version, name, etc.
    4093             :  */
    4094           0 : void snap_child::snap_info()
    4095             : {
    4096           0 :     QString version;
    4097             : 
    4098             :     // getting started
    4099           0 :     write("#START\n");
    4100             : 
    4101             :     // the library (server) version
    4102           0 :     write("VERSION=" SNAPWEBSITES_VERSION_STRING "\n");
    4103             : 
    4104             :     // operating system
    4105           0 :     version = "OS=";
    4106             : #ifdef Q_OS_LINUX
    4107           0 :     version += "Linux";
    4108             : #else
    4109             : #error "Unsupported operating system."
    4110             : #endif
    4111           0 :     version += "\n";
    4112           0 :     write(version);
    4113             : 
    4114             :     // the Qt versions
    4115           0 :     write("QT=" QT_VERSION_STR "\n");
    4116           0 :     version = "RUNTIME_QT=";
    4117           0 :     version += qVersion();
    4118           0 :     version += "\n";
    4119           0 :     write(version);
    4120             : 
    4121             :     // the libtld version
    4122           0 :     write("LIBTLD=" LIBTLD_VERSION "\n");
    4123           0 :     version = "RUNTIME_LIBTLD=";
    4124           0 :     version += tld_version();
    4125           0 :     version += "\n";
    4126           0 :     write(version);
    4127             : 
    4128             :     // the libQtCassandra version
    4129           0 :     version = "LIBQTCASSANDRA=";
    4130           0 :     version += libdbproxy::LIBDBPROXY_LIBRARY_VERSION_STRING;
    4131           0 :     version += "\n";
    4132           0 :     write(version);
    4133           0 :     version = "RUNTIME_LIBQTCASSANDRA=";
    4134           0 :     version += libdbproxy::libdbproxy::version();
    4135           0 :     version += "\n";
    4136           0 :     write(version);
    4137             : 
    4138             :     // the libQtSerialization version
    4139           0 :     version = "LIBQTSERIALIZATION=";
    4140           0 :     version += QtSerialization::QT_SERIALIZATION_LIBRARY_VERSION_STRING;
    4141           0 :     version += "\n";
    4142           0 :     write(version);
    4143           0 :     version = "RUNTIME_LIBQTSERIALIZATION=";
    4144           0 :     version += QtSerialization::QLibraryVersion();
    4145           0 :     version += "\n";
    4146           0 :     write(version);
    4147             : 
    4148             :     // since we do not have an environment we cannot connect
    4149             :     // to the Cassandra cluster...
    4150             : 
    4151             :     // done
    4152           0 :     write("#END\n");
    4153             : 
    4154           0 :     exit(1);
    4155           0 : }
    4156             : 
    4157             : 
    4158             : /** \brief Return the current stats in name/value pairs format.
    4159             :  *
    4160             :  * This command returns the server statistics.
    4161             :  */
    4162           0 : void snap_child::snap_statistics()
    4163             : {
    4164           0 :     server::pointer_t server(get_server());
    4165             : 
    4166           0 :     QString s;
    4167             : 
    4168             :     // getting started
    4169           0 :     write("#START\n");
    4170             : 
    4171             :     // the library (server) version
    4172           0 :     write("VERSION=" SNAPWEBSITES_VERSION_STRING "\n");
    4173             : 
    4174             :     // the number of connections received by the server up until this child fork()'ed
    4175           0 :     s = "CONNECTIONS_COUNT=";
    4176           0 :     s += QString::number(server->connections_count());
    4177           0 :     s += "\n";
    4178           0 :     write(s);
    4179             : 
    4180             :     // done
    4181           0 :     write("#END\n");
    4182             : 
    4183           0 :     exit(1);
    4184           0 : }
    4185             : 
    4186             : 
    4187             : /** \brief Setup the URI from the environment.
    4188             :  *
    4189             :  * This function gets the different variables from the environment
    4190             :  * it just received from the snap.cgi script and builds the corresponding
    4191             :  * Snap URI object with it. This will then be used to determine the
    4192             :  * domain and finally the website.
    4193             :  */
    4194           0 : void snap_child::setup_uri()
    4195             : {
    4196             :     // PROTOCOL
    4197           0 :     if(f_env.count("HTTPS") == 1)
    4198             :     {
    4199           0 :         if(f_env["HTTPS"] == "on")
    4200             :         {
    4201           0 :             f_uri.set_protocol("https");
    4202             :         }
    4203             :         else
    4204             :         {
    4205           0 :             f_uri.set_protocol("http");
    4206             :         }
    4207             :     }
    4208             :     else
    4209             :     {
    4210           0 :         f_uri.set_protocol("http");
    4211             :     }
    4212             : 
    4213             :     // HOST (domain name including all sub-domains)
    4214           0 :     if(f_env.count("HTTP_HOST") != 1)
    4215             :     {
    4216             :         // this was tested in snap.cgi, but who knows who connected to us...
    4217             :         //
    4218           0 :         die(http_code_t::HTTP_CODE_SERVICE_UNAVAILABLE, "",
    4219             :                     "HTTP_HOST is required but not defined in your request.",
    4220             :                     "HTTP_HOST was not defined in the user request");
    4221           0 :         NOTREACHED();
    4222             :     }
    4223           0 :     QString host(f_env["HTTP_HOST"]);
    4224           0 :     int const port_pos(host.indexOf(':'));
    4225           0 :     if(port_pos != -1)
    4226             :     {
    4227             :         // remove the port information
    4228           0 :         host = host.left(port_pos);
    4229             :     }
    4230           0 :     if(host.isEmpty())
    4231             :     {
    4232           0 :         die(http_code_t::HTTP_CODE_SERVICE_UNAVAILABLE, "",
    4233             :                     "HTTP_HOST is required but is empty in your request.",
    4234             :                     "HTTP_HOST was defined but there was no domain name");
    4235           0 :         NOTREACHED();
    4236             :     }
    4237           0 :     f_uri.set_domain(host);
    4238             : 
    4239             :     // PORT
    4240           0 :     if(f_env.count("SERVER_PORT") != 1)
    4241             :     {
    4242           0 :         die(http_code_t::HTTP_CODE_SERVICE_UNAVAILABLE, "",
    4243             :                     "SERVER_PORT is required but not defined in your request.",
    4244             :                     "SERVER_PORT was not defined in the user request");
    4245           0 :         NOTREACHED();
    4246             :     }
    4247           0 :     f_uri.set_port(f_env["SERVER_PORT"]);
    4248             : 
    4249             :     // QUERY STRING
    4250           0 :     if(f_env.count("QUERY_STRING") == 1)
    4251             :     {
    4252           0 :         f_uri.set_query_string(f_env["QUERY_STRING"]);
    4253             :     }
    4254             : 
    4255             :     // REQUEST URI
    4256           0 :     if(f_env.count(snap::get_name(name_t::SNAP_NAME_CORE_REQUEST_URI)) != 1)
    4257             :     {
    4258           0 :         die(http_code_t::HTTP_CODE_SERVICE_UNAVAILABLE, "",
    4259             :                      "REQUEST_URI is required but not defined in your request.",
    4260             :                      "REQUEST_URI was not defined in the user's request");
    4261           0 :         NOTREACHED();
    4262             :     }
    4263             :     // For some reasons I was thinking that the q=... was necessary to
    4264             :     // find the correct REQUEST_URI -- it is only if you want to allow
    4265             :     // for snap.cgi?q=... syntax in the client's agent, otherwise it
    4266             :     // is totally useless; since we do not want the ugly snap.cgi syntax
    4267             :     // we completely removed the need for it
    4268             :     //
    4269             :     // Get the path from the REQUEST_URI
    4270             :     // note that it includes the query string when there is one so we must
    4271             :     // make sure to remove that part if defined
    4272             :     //
    4273           0 :     QString path;
    4274             :     {
    4275           0 :         int const p = f_env[snap::get_name(name_t::SNAP_NAME_CORE_REQUEST_URI)].indexOf('?');
    4276           0 :         if(p == -1)
    4277             :         {
    4278           0 :             path = f_env[snap::get_name(name_t::SNAP_NAME_CORE_REQUEST_URI)];
    4279             :         }
    4280             :         else
    4281             :         {
    4282           0 :             path = f_env[snap::get_name(name_t::SNAP_NAME_CORE_REQUEST_URI)].mid(0, p);
    4283             :         }
    4284             :     }
    4285             : 
    4286           0 :     QString extension;
    4287           0 :     if(path != "." && path != "..")
    4288             :     {
    4289             :         // TODO: make the size (2048) a variable? (parameter in
    4290             :         //       snapserver.conf)
    4291             :         //
    4292             :         // I'm not totally sure this test is really necessary,
    4293             :         // but it probably won't hurt for a while (Drupal was
    4294             :         // limiting to 128 in the database and that was way
    4295             :         // too small, but 2048 is the longest you can use
    4296             :         // with Internet Explorer)
    4297             :         //
    4298           0 :         if(path.length() > 2048)
    4299             :         {
    4300             :             // See SNAP-99 for more info about this limit
    4301             :             //
    4302             :             // TBD: maybe we should redirect instead of dying in this case?
    4303             :             //
    4304           0 :             die(http_code_t::HTTP_CODE_REQUEST_URI_TOO_LONG, "",
    4305             :                          "The path of this request is too long.",
    4306             :                          "We accept paths up to 2048 characters.");
    4307           0 :             NOTREACHED();
    4308             :         }
    4309           0 :         f_uri.set_path(path);
    4310           0 :         int limit(path.lastIndexOf('/'));
    4311           0 :         if(limit < 0)
    4312             :         {
    4313           0 :             limit = 0;
    4314             :         }
    4315           0 :         int const ext(path.lastIndexOf('.'));
    4316           0 :         if(ext > limit)
    4317             :         {
    4318           0 :             extension = path.mid(ext);
    4319             :             // check for a compression and include that and
    4320             :             // the previous extension
    4321           0 :             if(extension == ".gz"       // gzip
    4322           0 :             || extension == ".Z"        // Unix compress
    4323           0 :             || extension == ".bz2")     // bzip2
    4324             :             {
    4325             :                 // we generally expect .gz but we have to take
    4326             :                 // whatever extension the user added to make sure
    4327             :                 // we send the file in the right format
    4328             :                 // we will also need to use the Accept-Encoding
    4329             :                 // and make use of the Content-Encoding
    4330             :                 // TODO: make use of extension instead of Accept-Encoding
    4331           0 :                 f_uri.set_option("compression", extension);
    4332           0 :                 int const real_ext(path.lastIndexOf('.', ext - 1));
    4333           0 :                 if(real_ext > limit)
    4334             :                 {
    4335             :                     // retrieve the extension without the compression
    4336           0 :                     extension = path.mid(real_ext, real_ext - ext);
    4337             :                 }
    4338             :                 else
    4339             :                 {
    4340             :                     // do not view a compression extension by itself
    4341             :                     // as an extension
    4342             :                     //
    4343           0 :                     extension.clear();
    4344             :                 }
    4345             :             }
    4346             :         }
    4347             :     }
    4348           0 :     f_uri.set_option("extension", extension);
    4349             : 
    4350             : //std::cerr << "    set path to: [" << path << "]\n";
    4351             : //
    4352             : //std::cerr << "        original [" << f_uri.get_original_uri() << "]\n";
    4353             : //std::cerr << "             uri [" << f_uri.get_uri() << "] + #! [" << f_uri.get_uri(true) << "]\n";
    4354             : //std::cerr << "        protocol [" << f_uri.protocol() << "]\n";
    4355             : //std::cerr << "     full domain [" << f_uri.full_domain() << "]\n";
    4356             : //std::cerr << "top level domain [" << f_uri.top_level_domain() << "]\n";
    4357             : //std::cerr << "          domain [" << f_uri.domain() << "]\n";
    4358             : //std::cerr << "     sub-domains [" << f_uri.sub_domains() << "]\n";
    4359             : //std::cerr << "            port [" << f_uri.get_port() << "]\n";
    4360             : //std::cerr << "            path [" << f_uri.path() << "] \n";
    4361             : //std::cerr << "    query string [" << f_uri.query_string() << "]\n";
    4362           0 : }
    4363             : 
    4364             : 
    4365             : /** \brief Change the main path.
    4366             :  *
    4367             :  * This function allows a plugin to change the main path. This is very
    4368             :  * practical to allow one to redirect without changing the path the
    4369             :  * user sees in his browser. Such has to be done in the
    4370             :  * on_check_for_redirect() signal of your module, very early on before
    4371             :  * the path gets used (except in other redirect functions).
    4372             :  *
    4373             :  * The path change should not modified anything else in the URI (i.e.
    4374             :  * options, query string, etc.)
    4375             :  *
    4376             :  * \param[in] path  The new path to save in f_uri.
    4377             :  */
    4378           0 : void snap_child::set_uri_path(QString const & path)
    4379             : {
    4380           0 :     f_uri.set_path(path);
    4381           0 : }
    4382             : 
    4383             : 
    4384             : /** \brief Get a copy of the URI information.
    4385             :  *
    4386             :  * This function returns a constant reference (i.e. a read-only "copy")
    4387             :  * of the URI used to access the server. This is the request we want to
    4388             :  * answer.
    4389             :  *
    4390             :  * \return The URI reference.
    4391             :  */
    4392           0 : snap_uri const & snap_child::get_uri() const
    4393             : {
    4394           0 :     return f_uri;
    4395             : }
    4396             : 
    4397             : 
    4398             : /** \brief Retrieve the current action.
    4399             :  *
    4400             :  * This function is used to retrieve the action defined in the query string.
    4401             :  * This value is changed with what will be used just before the permission
    4402             :  * verification process starts.
    4403             :  *
    4404             :  * Trying to retrieve the action too soon may give you an invalid value.
    4405             :  * Note that if you are generating the page contents, then you are past
    4406             :  * the verification process so this action value was corrected as required.
    4407             :  *
    4408             :  * \return  The name of the action used to check this page.
    4409             :  */
    4410           0 : QString snap_child::get_action() const
    4411             : {
    4412           0 :     server::pointer_t server(get_server());
    4413           0 :     return f_uri.query_option(server->get_parameter("qs_action"));
    4414             : }
    4415             : 
    4416             : 
    4417             : /** \brief Define the action.
    4418             :  *
    4419             :  * This function is used to save the action being used to check the
    4420             :  * main page. The actions are defined in the database. These are
    4421             :  * "view", "edit", "delete", "administer", etc.
    4422             :  *
    4423             :  * Any action can be set, the permission plugin verifies that it exists
    4424             :  * and that the user has permissions before actually apply the action.
    4425             :  *
    4426             :  * \param[in] action  The action used to check the page.
    4427             :  */
    4428           0 : void snap_child::set_action(QString const& action)
    4429             : {
    4430           0 :     server::pointer_t server(get_server());
    4431           0 :     f_uri.set_query_option(server->get_parameter("qs_action"), action);
    4432           0 : }
    4433             : 
    4434             : 
    4435             : /** \brief Check that the email is legal.
    4436             :  *
    4437             :  * A legal email has a legal name on the left of the @ character
    4438             :  * and a valid domain and TLD. We do not accept emails that represent
    4439             :  * local userbox (i.e. webmaster, root, etc. by themselves) because
    4440             :  * from a website these do not really make sense.
    4441             :  *
    4442             :  * If the email address is considered valid, knowing that we also check
    4443             :  * the Top Level Domain name (TLD), then we further send a request to
    4444             :  * the domain name handling that domain to make sure we can get information
    4445             :  * about the MX record. If the domain does not provide an MX record, then
    4446             :  * there is no emails to be sent.
    4447             :  *
    4448             :  * \note
    4449             :  * If max > 1 you probably want to keep allow_example_domain set to false
    4450             :  * otherwise you may get verified_email_t::VERIFIED_EMAIL_MIXED as a result
    4451             :  * which means you cannot know which email is an example and which is not.
    4452             :  *
    4453             :  * \todo
    4454             :  * Determine whether email addresses with the domain name of "example"
    4455             :  * are allowed. In some cases they are (i.e. we have a customers that
    4456             :  * has to create some pages linked to an email and at times those are
    4457             :  * not real email and thus we use `name@example.com` for those...)
    4458             :  * Especially, if someone registers an account with such an email it
    4459             :  * is totally useless and we should prevent it.
    4460             :  *
    4461             :  * \todo
    4462             :  * We want to count the number of times a certain IP address tries to
    4463             :  * verify an email address and fails. That way we can block them after
    4464             :  * X attempts. Because really someone should not be able to fail to
    4465             :  * enter their email address so many times.
    4466             :  *
    4467             :  * \exception users_exception_invalid_email
    4468             :  * This function does not return if the email is invalid. Instead it throws.
    4469             :  * We may later want to have a version that returns true if valid, false
    4470             :  * otherwise. At the same time, we generate three different errors here.
    4471             :  *
    4472             :  * \param[in] email  The email to verify.
    4473             :  * \param[in] max  The maximum number of emails supported. May be 0, usually 1.
    4474             :  * \param[in] allow_example_domain  Whether the "example" domain is allowed.
    4475             :  *
    4476             :  * \return Whether the email is considered valid, invalid, is an example
    4477             :  *         or a mix of valid and example emails.
    4478             :  */
    4479           0 : snap_child::verified_email_t snap_child::verify_email(QString const & email, size_t const max, bool allow_example_domain)
    4480             : {
    4481             :     // is there an actual email?
    4482             :     // (we may want to remove standalone and duplicated commas too)
    4483           0 :     if(email.isEmpty())
    4484             :     {
    4485           0 :         if(max == 0)
    4486             :         {
    4487           0 :             return verified_email_t::VERIFIED_EMAIL_EMPTY;
    4488             :         }
    4489           0 :         throw snap_child_exception_invalid_email("no email defined");
    4490             :     }
    4491             : 
    4492             :     // check the email name, domain, and TLD
    4493           0 :     tld_email_list tld_emails;
    4494           0 :     if(tld_emails.parse(email.toUtf8().data(), 0) != TLD_RESULT_SUCCESS)
    4495             :     {
    4496           0 :         throw snap_child_exception_invalid_email("invalid user email");
    4497             :     }
    4498             : 
    4499             :     // make sure there is a limit to the number of emails, if max was set
    4500             :     // to 1 we expect exactly one email (we previously eliminated the
    4501             :     // case where there would be none)
    4502             :     //
    4503           0 :     if(static_cast<size_t>(tld_emails.count()) > max)
    4504             :     {
    4505           0 :         throw snap_child_exception_invalid_email(QString("too many emails, excepted up to %1 got %2 instead.").arg(max).arg(tld_emails.count()));
    4506             :     }
    4507             : 
    4508             :     // if the email string is not empty, then there has to be at least one
    4509             :     // email otherwise the parse() function should have returned an error
    4510             :     //
    4511           0 :     if(tld_emails.count() == 0)
    4512             :     {
    4513           0 :         throw snap_child_exception_invalid_email(QString("no emails, even though \"%1\" is not empty.").arg(email));
    4514             :     }
    4515             : 
    4516           0 :     libdbproxy::table::pointer_t mx_table(get_table(get_name(name_t::SNAP_NAME_MX)));
    4517             : 
    4518           0 :     verified_email_t result(verified_email_t::VERIFIED_EMAIL_UNKNOWN);
    4519             : 
    4520             :     // function to set the result to either STANDARD or MIXED
    4521             :     // (we use a lambda because we use that twice)
    4522             :     //
    4523           0 :     auto set_result = [&result]()
    4524           0 :     {
    4525           0 :         switch(result)
    4526             :         {
    4527           0 :         case verified_email_t::VERIFIED_EMAIL_UNKNOWN:
    4528           0 :             result = verified_email_t::VERIFIED_EMAIL_STANDARD;
    4529           0 :         case verified_email_t::VERIFIED_EMAIL_STANDARD:
    4530           0 :             break;
    4531             : 
    4532           0 :         default:
    4533           0 :             result = verified_email_t::VERIFIED_EMAIL_MIXED;
    4534           0 :             break;
    4535             : 
    4536             :         }
    4537           0 :     };
    4538             : 
    4539             : 
    4540             :     // finally, check that the MX record exists for that email address, if
    4541             :     // not then we can immediately say its wrong; not that if multiple emails
    4542             :     // are defined, you get one throw if any one of them is wrong...
    4543             :     //
    4544           0 :     tld_email_list::tld_email_t e;
    4545           0 :     while(tld_emails.next(e))
    4546             :     {
    4547           0 :         if(allow_example_domain)
    4548             :         {
    4549           0 :             tld_object const d(e.f_domain.c_str());
    4550           0 :             if(d.domain_only() == "example")
    4551             :             {
    4552             :                 // in this case a domain named "example" is considered valid
    4553             :                 // (TODO the libtld should tell us whether this is true
    4554             :                 // or not because this does not apply to all TLDs...)
    4555             :                 //
    4556             :                 // Note: we do not save such in our database, no need.
    4557             :                 //
    4558           0 :                 switch(result)
    4559             :                 {
    4560           0 :                 case verified_email_t::VERIFIED_EMAIL_UNKNOWN:
    4561           0 :                     SNAP_LOG_TRACE("domain \"")(e.f_domain)("\" is considered to represent an example email.");
    4562           0 :                     result = verified_email_t::VERIFIED_EMAIL_EXAMPLE;
    4563           0 :                 case verified_email_t::VERIFIED_EMAIL_EXAMPLE:
    4564           0 :                     break;
    4565             : 
    4566           0 :                 default:
    4567           0 :                     result = verified_email_t::VERIFIED_EMAIL_MIXED;
    4568           0 :                     break;
    4569             : 
    4570             :                 }
    4571           0 :                 continue;
    4572             :             }
    4573             :         }
    4574             : 
    4575           0 :         libdbproxy::row::pointer_t row(mx_table->getRow(QString::fromUtf8(e.f_domain.c_str())));
    4576           0 :         libdbproxy::value last_checked_value(row->getCell(QString(get_name(name_t::SNAP_NAME_CORE_MX_LAST_CHECKED)))->getValue());
    4577           0 :         if(last_checked_value.size() == sizeof(int64_t))
    4578             :         {
    4579           0 :             time_t const last_update(last_checked_value.int64Value());
    4580           0 :             if(f_start_date < last_update + 86400LL * 1000000LL)
    4581             :             {
    4582           0 :                 libdbproxy::value result_value(row->getCell(QString(get_name(name_t::SNAP_NAME_CORE_MX_RESULT)))->getValue());
    4583           0 :                 if(result_value.safeSignedCharValue())
    4584             :                 {
    4585             :                     // considered valid without the need to check again
    4586             :                     //
    4587           0 :                     SNAP_LOG_TRACE("domain \"")(e.f_domain)("\" is considered valid (saved in mx table)");
    4588           0 :                     set_result();
    4589           0 :                     continue;
    4590             :                 }
    4591             :                 // this is not valid
    4592             :                 //
    4593           0 :                 throw snap_child_exception_invalid_email(QString("domain \"%1\" from email \"%2\" does not provide an MX record (from cache).")
    4594           0 :                         .arg(QString::fromUtf8(e.f_domain.c_str()))
    4595           0 :                         .arg(QString::fromUtf8(e.f_canonicalized_email.c_str())));
    4596             :             }
    4597             :         }
    4598           0 :         row->getCell(QString(get_name(name_t::SNAP_NAME_CORE_MX_LAST_CHECKED)))->setValue(f_start_date);
    4599           0 :         mail_exchangers exchangers(e.f_domain);
    4600           0 :         if(!exchangers.domain_found())
    4601             :         {
    4602           0 :             signed char const failed(0);
    4603           0 :             row->getCell(QString(get_name(name_t::SNAP_NAME_CORE_MX_RESULT)))->setValue(failed);
    4604           0 :             throw snap_child_exception_invalid_email(QString("domain \"%1\" from email \"%2\" does not exist.")
    4605           0 :                         .arg(QString::fromUtf8(e.f_domain.c_str()))
    4606           0 :                         .arg(QString::fromUtf8(e.f_canonicalized_email.c_str())));
    4607             :         }
    4608           0 :         if(exchangers.size() == 0)
    4609             :         {
    4610           0 :             signed char const failed(0);
    4611           0 :             row->getCell(QString(get_name(name_t::SNAP_NAME_CORE_MX_RESULT)))->setValue(failed);
    4612           0 :             throw snap_child_exception_invalid_email(QString("domain \"%1\" from email \"%2\" does not provide an MX record.")
    4613           0 :                         .arg(QString::fromUtf8(e.f_domain.c_str()))
    4614           0 :                         .arg(QString::fromUtf8(e.f_canonicalized_email.c_str())));
    4615             :         }
    4616           0 :         signed char const succeeded(1);
    4617           0 :         row->getCell(QString(get_name(name_t::SNAP_NAME_CORE_MX_RESULT)))->setValue(succeeded);
    4618             : 
    4619           0 :         SNAP_LOG_TRACE("domain \"")(e.f_domain)("\" was just checked and is considered valid (it has an MX record.)");
    4620             : 
    4621             :         // TODO: if we have the timeout, we could save the time when
    4622             :         //       the data goes out of date (instead of using exactly
    4623             :         //       1 day as we do now) although we probably should use
    4624             :         //       a minimum of 1 day anyway
    4625             : 
    4626             :         // TODO: also save the MX info (once we are to make use of any of it...)
    4627             : 
    4628           0 :         set_result();
    4629             :     }
    4630             : 
    4631           0 :     return result;
    4632             : }
    4633             : 
    4634             : 
    4635             : /** \brief Connect to the Cassandra database system.
    4636             :  *
    4637             :  * This function connects to the Cassandra database system and
    4638             :  * returns only if the connection succeeds. If it fails, it logs
    4639             :  * the fact and sends an error back to the user.
    4640             :  *
    4641             :  * By default the function calls die() on error. If you would
    4642             :  * prefer to have the function return with false, then set the
    4643             :  * child parameter to false.
    4644             :  *
    4645             :  * \param[in] child  Whether a child is calling this function.
    4646             :  *
    4647             :  * \return true if the connection succeeded, false if child is false
    4648             :  *         and the connection failed, does not return if child is true
    4649             :  *         and the connection failed.
    4650             :  */
    4651           0 : bool snap_child::connect_cassandra(bool child)
    4652             : {
    4653             :     // Cassandra already exists?
    4654           0 :     if(f_cassandra)
    4655             :     {
    4656           0 :         if(!child)
    4657             :         {
    4658           0 :             SNAP_LOG_DEBUG("snap_child::connect_cassandra() already considered connected.");
    4659             : 
    4660             :             // here we return true since the Cassandra connection is
    4661             :             // already in place, valid and well
    4662             :             //
    4663           0 :             return true;
    4664             :         }
    4665           0 :         die(http_code_t::HTTP_CODE_SERVICE_UNAVAILABLE, "",
    4666             :                 "Our database is being initialized more than once.",
    4667             :                 "The connect_cassandra() function cannot be called more than once.");
    4668           0 :         NOTREACHED();
    4669             :     }
    4670             : 
    4671             :     // retrieve the address:port info
    4672             :     //
    4673             :     // WARNING: server->get_snapdbproxy_addr/port() do not work in
    4674             :     //          snapbackend so we do it this way instead.
    4675             :     //
    4676           0 :     QString snapdbproxy_addr("127.0.0.1");
    4677           0 :     int snapdbproxy_port(4042);
    4678           0 :     snap_config config("snapdbproxy");
    4679           0 :     tcp_client_server::get_addr_port(config["listen"], snapdbproxy_addr, snapdbproxy_port, "tcp");
    4680             : 
    4681             :     // connect to Cassandra
    4682           0 :     f_cassandra = libdbproxy::libdbproxy::create();
    4683           0 :     bool connected(false);
    4684             :     try
    4685             :     {
    4686           0 :         connected = f_cassandra->connect(snapdbproxy_addr, snapdbproxy_port);
    4687             : 
    4688             :         // the connet() calls disconnect() which resets the consistency so
    4689             :         // if we really want QUORUM we have to set it again after the
    4690             :         // connect() call
    4691             :         //
    4692           0 :         f_cassandra->setDefaultConsistencyLevel(libdbproxy::CONSISTENCY_LEVEL_QUORUM);
    4693             :     }
    4694           0 :     catch(std::exception const & e)
    4695             :     {
    4696           0 :         connected = false; // make double sure this is still false
    4697           0 :         SNAP_LOG_FATAL("Could not connect to the snapdbproxy server (")(snapdbproxy_addr)(":")(snapdbproxy_port)("). Reason: ")(e.what());
    4698             :     }
    4699           0 :     if(!connected)
    4700             :     {
    4701             :         // cassandra is not valid, get rid of it
    4702           0 :         f_cassandra.reset();
    4703           0 :         if(!child)
    4704             :         {
    4705           0 :             SNAP_LOG_WARNING("snap_child::connect_cassandra() could not connect to snapdbproxy.");
    4706           0 :             return false;
    4707             :         }
    4708           0 :         die(http_code_t::HTTP_CODE_SERVICE_UNAVAILABLE, "",
    4709             :                 "Our database system is temporarilly unavailable.",
    4710             :                 "Could not connect to Cassandra");
    4711           0 :         NOTREACHED();
    4712             :     }
    4713             : 
    4714             : // WARNING: The f_casssandra->contexts() function should not be used anymore
    4715             : //          (only to check whether the context exists,) because the context
    4716             : //          is normally created by snapmanager[.cgi] now.
    4717           0 : SNAP_LOG_WARNING("snap_child::connect_cassandra() should not have to call contexts() anymore...");
    4718             : 
    4719             :     try
    4720             :     {
    4721             :         // select the Snap! context
    4722           0 :         f_cassandra->getContexts();
    4723           0 :         QString const context_name(get_name(name_t::SNAP_NAME_CONTEXT));
    4724           0 :         f_context = f_cassandra->findContext(context_name);
    4725           0 :         if(!f_context)
    4726             :         {
    4727             :             // we connected to the database, but it is not properly initialized!?
    4728             :             //
    4729           0 :             f_cassandra.reset();
    4730           0 :             if(!child)
    4731             :             {
    4732           0 :                 SNAP_LOG_WARNING("snap_child::connect_cassandra() could not read the context.");
    4733           0 :                 return false;
    4734             :             }
    4735           0 :             die(http_code_t::HTTP_CODE_SERVICE_UNAVAILABLE,
    4736             :                     "",
    4737             :                     "Our database system does not seem to be properly installed.",
    4738           0 :                     QString("The child process connected to Cassandra but it could not find the \"%1\" context.").arg(context_name));
    4739           0 :             NOTREACHED();
    4740             :         }
    4741             :     }
    4742           0 :     catch(std::exception const & e)
    4743             :     {
    4744           0 :         SNAP_LOG_FATAL("Connected to snapdbproxy server, but could not gather the Cassandra metadata (")(snapdbproxy_addr)(":")(snapdbproxy_port)("). Reason: ")(e.what());
    4745             : 
    4746             :         // we connected to the database, but it is not properly initialized!?
    4747             :         //
    4748           0 :         f_context.reset();  // just in case, also reset the context (it should already be null here)
    4749           0 :         f_cassandra.reset();
    4750           0 :         if(!child)
    4751             :         {
    4752           0 :             SNAP_LOG_WARNING("snap_child::connect_cassandra() could not read the context metadata.");
    4753           0 :             return false;
    4754             :         }
    4755           0 :         die(http_code_t::HTTP_CODE_SERVICE_UNAVAILABLE,
    4756             :                 "",
    4757             :                 "Our database system is not currently available.",
    4758             :                 "The child process connected to Cassandra through snapdbproxy, but it could not find retrieve the context metadata.");
    4759           0 :         NOTREACHED();
    4760             :     }
    4761             : 
    4762             :     // TBD -- Is that really the right place for this?
    4763             :     //        (in this way it is done once for any plugin using
    4764             :     //        the snap expression system; but it looks like a hack!)
    4765           0 :     snap_expr::expr::set_cassandra_context(f_context);
    4766             : 
    4767           0 :     return true;
    4768             : }
    4769             : 
    4770             : 
    4771             : /** \brief Completely disconnect from cassandra.
    4772             :  *
    4773             :  * Whenever we receive a NOCASSANDRA event in a backend, we want to
    4774             :  * disconnect completely. We will then reconnect once we receive the
    4775             :  * CASSANDRAREADY message.
    4776             :  */
    4777           0 : void snap_child::disconnect_cassandra()
    4778             : {
    4779           0 :     snap_expr::expr::set_cassandra_context(nullptr);
    4780             : 
    4781             :     // we must get rid of the site table otherwise it will hold a shared
    4782             :     // pointer to the context and the context to the libdbproxy object
    4783             :     //
    4784           0 :     reset_sites_table();
    4785             : 
    4786             :     // make sure the context cache is cleared too
    4787             :     //
    4788           0 :     f_context.reset();
    4789             : 
    4790           0 :     f_cassandra.reset();
    4791           0 : }
    4792             : 
    4793             : 
    4794             : /** \brief Retrieve the pointer to a table.
    4795             :  *
    4796             :  * This function is generally used by plugins to retrieve tables they
    4797             :  * use to manage their data.
    4798             :  *
    4799             :  * \exception snap_child_exception_no_cassandra
    4800             :  * If this function gets called when the Cassandra context is not yet
    4801             :  * defined, this exception is raised.
    4802             :  *
    4803             :  * \exception snap_child_exception_table_missing
    4804             :  * The table must already exists or this exception is raised. Tables
    4805             :  * are created by running the snapcreatetables tool before starting
    4806             :  * the snapserver.
    4807             :  *
    4808             :  * \param[in] table_name  The name of the table to create.
    4809             :  */
    4810           0 : libdbproxy::table::pointer_t snap_child::get_table(QString const & table_name)
    4811             : {
    4812           0 :     if(f_context == nullptr)
    4813             :     {
    4814           0 :         throw snap_child_exception_no_cassandra("table \"" + table_name + "\" cannot be determined, we have no context.");
    4815             :     }
    4816             : 
    4817           0 :     libdbproxy::table::pointer_t table(f_context->findTable(table_name));
    4818           0 :     if(table == nullptr)
    4819             :     {
    4820           0 :         throw snap_child_exception_table_missing("table \"" + table_name + "\" does not exist.");
    4821             :     }
    4822             : 
    4823           0 :     return table;
    4824             : }
    4825             : 
    4826             : 
    4827             : /** \brief Canonalize the domain information.
    4828             :  *
    4829             :  * This function uses the URI to find the domain core::rules and
    4830             :  * start the canonicalization process.
    4831             :  *
    4832             :  * The canonicalized domain is a domain name with sub-domains that
    4833             :  * are required. All the optional sub-domains will be removed.
    4834             :  *
    4835             :  * All the variables are saved as options in the f_uri object.
    4836             :  *
    4837             :  * \todo
    4838             :  * The functionality of this function needs to be extracted so it becomes
    4839             :  * available to others (i.e. probably moved to snap_uri.cpp) that way we
    4840             :  * can write tools that show the results of this parser.
    4841             :  */
    4842           0 : void snap_child::canonicalize_domain()
    4843             : {
    4844             :     // retrieve domain table
    4845           0 :     QString const table_name(get_name(name_t::SNAP_NAME_DOMAINS));
    4846           0 :     libdbproxy::table::pointer_t table(f_context->getTable(table_name));
    4847             : 
    4848             :     // row for that domain exists?
    4849             :     //
    4850           0 :     f_domain_key = f_uri.domain() + f_uri.top_level_domain();
    4851           0 :     if(!table->exists(f_domain_key))
    4852             :     {
    4853             :         // this domain doesn't exist; i.e. that's a 404
    4854             :         //
    4855           0 :         die(http_code_t::HTTP_CODE_NOT_FOUND,
    4856             :                     "Domain Not Found",
    4857             :                     "This website does not exist. Please check the URI and make corrections as required.",
    4858           0 :                     "User attempt to access \"" + f_domain_key + "\" which is not defined as a domain.");
    4859           0 :         NOTREACHED();
    4860             :     }
    4861             : 
    4862             :     // get the core::rules
    4863             :     //
    4864           0 :     libdbproxy::value value(table->getRow(f_domain_key)->getCell(QString(get_name(name_t::SNAP_NAME_CORE_RULES)))->getValue());
    4865           0 :     if(value.nullValue())
    4866             :     {
    4867             :         // Null value means an empty string or undefined column and either
    4868             :         // way it's wrong here
    4869             :         //
    4870           0 :         die(http_code_t::HTTP_CODE_NOT_FOUND,
    4871             :                     "Domain Not Found",
    4872             :                     "This website does not exist. Please check the URI and make corrections as required.",
    4873           0 :                     "User attempt to access domain \"" + f_domain_key + "\" which does not have a valid core::rules entry.");
    4874           0 :         NOTREACHED();
    4875             :     }
    4876             : 
    4877             :     // parse the rules to our domain structures
    4878             :     //
    4879           0 :     domain_rules r;
    4880             :     // QBuffer takes a non-const QByteArray so we have to create a copy
    4881           0 :     QByteArray data(value.binaryValue());
    4882           0 :     QBuffer in(&data);
    4883           0 :     in.open(QIODevice::ReadOnly);
    4884           0 :     QtSerialization::QReader reader(in);
    4885           0 :     r.read(reader);
    4886             : 
    4887             :     // we add a dot because the list of variables are expected to
    4888             :     // end with a dot, but only if sub_domains is not empty
    4889             :     //
    4890           0 :     QString sub_domains(f_uri.sub_domains());
    4891           0 :     if(!sub_domains.isEmpty())
    4892             :     {
    4893           0 :         sub_domains += ".";
    4894             :     }
    4895           0 :     int const max_rules(r.size());
    4896           0 :     for(int i = 0; i < max_rules; ++i)
    4897             :     {
    4898           0 :         QSharedPointer<domain_info> info(r[i]);
    4899             : 
    4900             :         // build the regex (TODO: pre-compile the regex?
    4901             :         // the problem is the var. name versus data parsed)
    4902             :         //
    4903           0 :         QString re;
    4904           0 :         int vmax(info->size());
    4905           0 :         for(int v = 0; v < vmax; ++v)
    4906             :         {
    4907           0 :             QSharedPointer<domain_variable> var(info->get_variable(v));
    4908             : 
    4909             :             // put parameters between () so we get the data in
    4910             :             // variables (options) later
    4911           0 :             re += "(" + var->get_value() + ")";
    4912           0 :             if(!var->get_required())
    4913             :             {
    4914             :                 // optional sub-domain
    4915           0 :                 re += "?";
    4916             :             }
    4917             :         }
    4918           0 :         QRegExp regex(re);
    4919           0 :         if(regex.exactMatch(sub_domains))
    4920             :         {
    4921             :             // we found the domain!
    4922             :             //
    4923           0 :             snap_string_list captured(regex.capturedTexts());
    4924           0 :             QString canonicalized;
    4925             : 
    4926             :             // note captured[0] is the full matching pattern, we ignore it
    4927           0 :             for(int v = 0; v < vmax; ++v)
    4928             :             {
    4929           0 :                 QSharedPointer<domain_variable> var(info->get_variable(v));
    4930             : 
    4931           0 :                 QString sub_domain_value(captured[v + 1]);
    4932             :                 // remove the last dot because in most cases we do not want it
    4933             :                 // in the variable even if it were defined in the regex
    4934           0 :                 if(!sub_domain_value.isEmpty() && sub_domain_value.right(1) == ".")
    4935             :                 {
    4936           0 :                     sub_domain_value = sub_domain_value.left(sub_domain_value.length() - 1);
    4937             :                 }
    4938             : 
    4939           0 :                 if(var->get_required())
    4940             :                 {
    4941             :                     // required, use default if empty
    4942             :                     // TODO -- test that one, it seems that && should be used, not ||
    4943           0 :                     if(sub_domain_value.isEmpty()
    4944           0 :                     || var->get_type() == domain_variable::DOMAIN_VARIABLE_TYPE_WEBSITE)
    4945             :                     {
    4946           0 :                         sub_domain_value = var->get_default();
    4947             :                     }
    4948           0 :                     f_uri.set_option(var->get_name(), sub_domain_value);
    4949             : 
    4950             :                     // these make up the final canonicalized domain name
    4951           0 :                     canonicalized += snap_uri::urlencode(sub_domain_value, ".");
    4952             :                 }
    4953           0 :                 else if(!sub_domain_value.isEmpty())
    4954             :                 {
    4955             :                     // optional sub-domain, set only if not empty
    4956           0 :                     if(var->get_type() == domain_variable::DOMAIN_VARIABLE_TYPE_WEBSITE)
    4957             :                     {
    4958           0 :                         sub_domain_value = var->get_default();
    4959             :                     }
    4960           0 :                     f_uri.set_option(var->get_name(), sub_domain_value);
    4961             :                 }
    4962             :                 else
    4963             :                 {
    4964             :                     // optional with a default, use it
    4965           0 :                     sub_domain_value = var->get_default();
    4966           0 :                     if(!sub_domain_value.isEmpty())
    4967             :                     {
    4968           0 :                         f_uri.set_option(var->get_name(), sub_domain_value);
    4969             :                     }
    4970             :                 }
    4971             :             }
    4972             : 
    4973             :             // now we've got the website key
    4974           0 :             if(canonicalized.isEmpty())
    4975             :             {
    4976           0 :                 f_website_key = f_domain_key;
    4977             :             }
    4978             :             else
    4979             :             {
    4980           0 :                 f_website_key = canonicalized + "." + f_domain_key;
    4981             :             }
    4982           0 :             return;
    4983             :         }
    4984             :     }
    4985             : 
    4986             :     // no domain match, we're dead meat
    4987           0 :     die(http_code_t::HTTP_CODE_NOT_FOUND, "Domain Not Found", "This website does not exist. Please check the URI and make corrections as required.", "The domain \"" + f_uri.full_domain() + "\" did not match any domain name defined in your Snap! system. Should you remove it from your DNS?");
    4988             : }
    4989             : 
    4990             : 
    4991             : /** \brief Finish the canonicalization process.
    4992             :  *
    4993             :  * The function reads the website core::rules and continue the parsing process
    4994             :  * of the URI.
    4995             :  *
    4996             :  * The sub-domain and domain canonicalization was accomplished in the previous
    4997             :  * process: canonicalize_domain(). This is not done again in the websites.
    4998             :  *
    4999             :  * This process includes the following checks:
    5000             :  *
    5001             :  * 1. Protocol
    5002             :  * 2. Port
    5003             :  * 3. Query String
    5004             :  * 4. Path
    5005             :  *
    5006             :  * The protocol, port, and query strings are checked as they are found in the
    5007             :  * website variables of the core::rules.
    5008             :  *
    5009             :  * The path is checked once all the variables were checked and if the protocol,
    5010             :  * port, and query strings were all matching as expected. If any one of them
    5011             :  * does not match then we don't need to check the path.
    5012             :  *
    5013             :  * \note
    5014             :  * As the checks of the protocol, port, and query strings are found, we cannot
    5015             :  * put them in the options just yet since if the path check fails, then
    5016             :  * another entry could be the proper match and that other entry may have
    5017             :  * completely different variables.
    5018             :  *
    5019             :  * \todo
    5020             :  * The functionality of this function needs to be extracted so it becomes
    5021             :  * available to others (i.e. probably moved to snap_uri.cpp) that way we
    5022             :  * can write tools that show the results of this parser.
    5023             :  */
    5024           0 : void snap_child::canonicalize_website()
    5025             : {
    5026             :     // retrieve website table
    5027             :     //
    5028           0 :     QString const table_name(get_name(name_t::SNAP_NAME_WEBSITES));
    5029           0 :     libdbproxy::table::pointer_t table(f_context->getTable(table_name));
    5030             : 
    5031             :     // row for that website exists?
    5032             :     //
    5033           0 :     if(!table->exists(f_website_key))
    5034             :     {
    5035             :         // this website doesn't exist; i.e. that's a 404
    5036           0 :         die(http_code_t::HTTP_CODE_NOT_FOUND, "Website Not Found", "This website does not exist. Please check the URI and make corrections as required.", "User attempt to access \"" + f_website_key + "\" which was not defined as a website.");
    5037           0 :         NOTREACHED();
    5038             :     }
    5039             : 
    5040             :     // get the core::rules
    5041             :     //
    5042           0 :     libdbproxy::value value(table->getRow(f_website_key)->getCell(QString(get_name(name_t::SNAP_NAME_CORE_RULES)))->getValue());
    5043           0 :     if(value.nullValue())
    5044             :     {
    5045             :         // Null value means an empty string or undefined column and either
    5046             :         // way it's wrong here
    5047             :         //
    5048           0 :         die(http_code_t::HTTP_CODE_NOT_FOUND, "Website Not Found", "This website does not exist. Please check the URI and make corrections as required.", "User attempt to access website \"" + f_website_key + "\" which does not have a valid core::rules entry.");
    5049           0 :         NOTREACHED();
    5050             :     }
    5051             : 
    5052             :     // parse the rules to our website structures
    5053           0 :     website_rules r;
    5054           0 :     QByteArray data(value.binaryValue());
    5055           0 :     QBuffer in(&data);
    5056           0 :     in.open(QIODevice::ReadOnly);
    5057           0 :     QtSerialization::QReader reader(in);
    5058           0 :     r.read(reader);
    5059             : 
    5060             :     // we check decoded paths
    5061           0 :     QString uri_path(f_uri.path(false));
    5062           0 :     int const max_rules(r.size());
    5063           0 :     for(int i = 0; i < max_rules; ++i)
    5064             :     {
    5065           0 :         QSharedPointer<website_info> info(r[i]);
    5066             : 
    5067             :         // build the regex (TODO: pre-compile the regex?
    5068             :         // the problem is the var. name versus data parsed)
    5069           0 :         QString protocol("http");
    5070           0 :         QString port("80");
    5071           0 :         QMap<QString, QString> query;
    5072           0 :         QString re_path;
    5073           0 :         int vmax(info->size());
    5074           0 :         bool matching(true);
    5075           0 :         for(int v = 0; matching && v < vmax; ++v)
    5076             :         {
    5077           0 :             QSharedPointer<website_variable> var(info->get_variable(v));
    5078             : 
    5079             :             // put parameters between () so we get the data in
    5080             :             // variables (options) later
    5081           0 :             QString param_value("(" + var->get_value() + ")");
    5082           0 :             switch(var->get_part())
    5083             :             {
    5084           0 :             case website_variable::WEBSITE_VARIABLE_PART_PATH:
    5085           0 :                 re_path += param_value;
    5086           0 :                 if(!var->get_required())
    5087             :                 {
    5088             :                     // optional sub-domain
    5089           0 :                     re_path += "?";
    5090             :                 }
    5091           0 :                 break;
    5092             : 
    5093           0 :             case website_variable::WEBSITE_VARIABLE_PART_PORT:
    5094             :             {
    5095           0 :                 QRegExp regex(param_value);
    5096           0 :                 if(!regex.exactMatch(QString("%1").arg(f_uri.get_port())))
    5097             :                 {
    5098           0 :                     matching = false;
    5099           0 :                     break;
    5100             :                 }
    5101           0 :                 snap_string_list captured(regex.capturedTexts());
    5102           0 :                 port = captured[1];
    5103             :             }
    5104           0 :                 break;
    5105             : 
    5106           0 :             case website_variable::WEBSITE_VARIABLE_PART_PROTOCOL:
    5107             :             {
    5108           0 :                 QRegExp regex(param_value);
    5109             :                 // the case of the protocol in the regex doesn't matter
    5110             :                 // TODO (TBD):
    5111             :                 // Although I'm not 100% sure this is correct, we may
    5112             :                 // instead want to use lower case in the source
    5113           0 :                 regex.setCaseSensitivity(Qt::CaseInsensitive);
    5114           0 :                 if(!regex.exactMatch(f_uri.protocol()))
    5115             :                 {
    5116           0 :                     matching = false;
    5117           0 :                     break;
    5118             :                 }
    5119           0 :                 snap_string_list captured(regex.capturedTexts());
    5120           0 :                 protocol = captured[1];
    5121             :             }
    5122           0 :                 break;
    5123             : 
    5124           0 :             case website_variable::WEBSITE_VARIABLE_PART_QUERY:
    5125             :             {
    5126             :                 // the query string parameters are not ordered...
    5127             :                 // the variable name is 1 to 1 what is expected on the URI
    5128           0 :                 QString name(var->get_name());
    5129           0 :                 if(f_uri.has_query_option(name))
    5130             :                 {
    5131             :                     // make sure it matches first
    5132           0 :                     QRegExp regex(param_value);
    5133           0 :                     if(!regex.exactMatch(f_uri.query_option(name)))
    5134             :                     {
    5135           0 :                         matching = false;
    5136           0 :                         break;
    5137             :                     }
    5138           0 :                     snap_string_list captured(regex.capturedTexts());
    5139           0 :                     query[name] = captured[1];
    5140             :                 }
    5141           0 :                 else if(var->get_required())
    5142             :                 {
    5143             :                     // if required then we want to use the default
    5144           0 :                     query[name] = var->get_default();
    5145           0 :                 }
    5146             :             }
    5147           0 :                 break;
    5148             : 
    5149           0 :             default:
    5150           0 :                 throw std::runtime_error("unknown part specified in website_variable::f_part");
    5151             : 
    5152             :             }
    5153             :         }
    5154           0 :         if(!matching)
    5155             :         {
    5156             :             // one of protocol, port, or query string failed
    5157             :             // (path is checked below)
    5158             :             //
    5159           0 :             continue;
    5160             :         }
    5161             :         // now check the path, if empty assume it matches and
    5162             :         // also we have no extra options
    5163             :         //
    5164           0 :         QString canonicalized_path;
    5165           0 :         if(!re_path.isEmpty())
    5166             :         {
    5167             :             // match from the start, but it doesn't need to match the whole path
    5168             :             //
    5169           0 :             QRegExp regex("^" + re_path);
    5170           0 :             if(regex.indexIn(uri_path) != -1)
    5171             :             {
    5172             :                 // we found the site including a path!
    5173             :                 //
    5174             :                 // TODO: should we keep the length of the captured data and
    5175             :                 //       remove it from the path sent down the road?
    5176             :                 //       (note: if you have a path such as /blah/foo and
    5177             :                 //       you remove it, then what looks like /robots.txt
    5178             :                 //       is really /blah/foo/robots.txt which is wrong.)
    5179             :                 //       However, if the path is only used for options such
    5180             :                 //       as languages, those options should be removed from
    5181             :                 //       the original path.
    5182             :                 //
    5183           0 :                 snap_string_list captured(regex.capturedTexts());
    5184             : 
    5185             :                 // note captured[0] is the full matching pattern, we ignore it
    5186             :                 //
    5187           0 :                 for(int v = 0; v < vmax; ++v)
    5188             :                 {
    5189           0 :                     QSharedPointer<website_variable> var(info->get_variable(v));
    5190             : 
    5191           0 :                     if(var->get_part() == website_variable::WEBSITE_VARIABLE_PART_PATH)
    5192             :                     {
    5193           0 :                         QString path_value(captured[v + 1]);
    5194             : 
    5195           0 :                         if(var->get_required())
    5196             :                         {
    5197             :                             // required, use default if empty
    5198             :                             //
    5199           0 :                             if(path_value.isEmpty()
    5200           0 :                             || var->get_type() == website_variable::WEBSITE_VARIABLE_TYPE_WEBSITE)
    5201             :                             {
    5202           0 :                                 path_value = var->get_default();
    5203             :                             }
    5204           0 :                             f_uri.set_option(var->get_name(), path_value);
    5205             : 
    5206             :                             // these make up the final canonicalized domain name
    5207             :                             //
    5208           0 :                             canonicalized_path += "/" + snap_uri::urlencode(path_value, "~");
    5209             :                         }
    5210           0 :                         else if(!path_value.isEmpty())
    5211             :                         {
    5212             :                             // optional path, set only if not empty
    5213             :                             //
    5214           0 :                             if(var->get_type() == website_variable::WEBSITE_VARIABLE_TYPE_WEBSITE)
    5215             :                             {
    5216           0 :                                 path_value = var->get_default();
    5217             :                             }
    5218           0 :                             f_uri.set_option(var->get_name(), path_value);
    5219             :                         }
    5220             :                         else
    5221             :                         {
    5222             :                             // optional with a default, use it
    5223             :                             //
    5224           0 :                             path_value = var->get_default();
    5225           0 :                             if(!path_value.isEmpty())
    5226             :                             {
    5227           0 :                                 f_uri.set_option(var->get_name(), path_value);
    5228             :                             }
    5229             :                         }
    5230             :                     }
    5231             :                 }
    5232             :             }
    5233             :             else
    5234             :             {
    5235           0 :                 matching = false;
    5236             :             }
    5237             :         }
    5238             : 
    5239           0 :         if(matching)
    5240             :         {
    5241             :             // now we've got the protocol, port, query strings, and paths
    5242             :             // so we can build the final URI that we'll use as the site key
    5243             :             //
    5244           0 :             QString canonicalized;
    5245           0 :             f_uri.set_option("protocol", protocol);
    5246           0 :             canonicalized += protocol + "://" + f_website_key;
    5247           0 :             f_uri.set_option("port", port);
    5248           0 :             if(port.toInt() != snap_uri::protocol_to_port(protocol))
    5249             :             {
    5250           0 :                 canonicalized += ":" + port;
    5251             :             }
    5252           0 :             if(canonicalized_path.isEmpty())
    5253             :             {
    5254           0 :                 canonicalized += "/";
    5255             :             }
    5256             :             else
    5257             :             {
    5258           0 :                 canonicalized += canonicalized_path;
    5259             :             }
    5260           0 :             QString canonicalized_query;
    5261           0 :             for(QMap<QString, QString>::const_iterator it(query.begin()); it != query.end(); ++it)
    5262             :             {
    5263           0 :                 f_uri.set_query_option(it.key(), it.value());
    5264           0 :                 if(!canonicalized_query.isEmpty())
    5265             :                 {
    5266           0 :                     canonicalized_query += "&";
    5267             :                 }
    5268           0 :                 canonicalized_query += snap_uri::urlencode(it.key()) + "=" + snap_uri::urlencode(it.value());
    5269             :             }
    5270           0 :             if(!canonicalized_query.isEmpty())
    5271             :             {
    5272           0 :                 canonicalized += "?" + canonicalized_query;
    5273             :             }
    5274             :             // now we've got the site key
    5275           0 :             f_site_key = canonicalized;
    5276           0 :             f_original_site_key = f_site_key; // in case of a redirect...
    5277           0 :             f_site_key_with_slash = f_site_key;
    5278           0 :             if(f_site_key.right(1) != "/")
    5279             :             {
    5280           0 :                 f_site_key_with_slash += "/";
    5281             :             }
    5282           0 :             return;
    5283             :         }
    5284             :     }
    5285             : 
    5286             :     // no website match, we're dead meat
    5287           0 :     die(http_code_t::HTTP_CODE_NOT_FOUND, "Website Not Found", "This website does not exist. Please check the URI and make corrections as required.", "The website \"" + f_website_key + "\" did not match any website defined in your Snap! system. Should you remove it from your DNS?");
    5288             : }
    5289             : 
    5290             : 
    5291             : /** \brief Determine the language, branch, revision, and compression.
    5292             :  *
    5293             :  * This function takes parameters as specified by the client and determines
    5294             :  * the language, branch, revision, and compression that are to be used to
    5295             :  * select the data to be returned to the client.
    5296             :  *
    5297             :  * In most cases visitors can only see the branch and revision that are
    5298             :  * marked as current. Editors can see all branches and revisions. Everyone
    5299             :  * can view any language, and translators (who are editors as well) can
    5300             :  * create new pages in a the languages they were assigned.
    5301             :  *
    5302             :  * The compression information is canonicalized here so we do not have to
    5303             :  * do it over and over again. It initializes the f_compressions vector
    5304             :  * with the very first entry privileged, as the favored entry. The last
    5305             :  * entry may be the special value COMPRESSION_INVALID in which case the
    5306             :  * 406 error (HTTP_CODE_NOT_ACCEPTABLE) should be generated.
    5307             :  *
    5308             :  * \todo
    5309             :  * The user may select a preferred language in his account. That has
    5310             :  * to be used before the browser information. However, the options
    5311             :  * (domain, path, or GET variable,) still have higher priority.
    5312             :  * Unfortunately, although plugins are already initialized, whether
    5313             :  * the user is logged in is not yet known so we cannot check from
    5314             :  * this function.
    5315             :  */
    5316           0 : void snap_child::canonicalize_options()
    5317             : {
    5318             :     // *** LANGUAGE / COUNTRY ***
    5319             :     // first take care of the language
    5320             : 
    5321             :     // transform the language specified by the browser in an array
    5322           0 :     http_strings::WeightedHttpString languages(snapenv(get_name(name_t::SNAP_NAME_CORE_HTTP_ACCEPT_LANGUAGE)));
    5323           0 :     http_strings::WeightedHttpString::part_t::vector_t const & browser_languages(languages.get_parts());
    5324           0 :     if(!browser_languages.isEmpty())
    5325             :     {
    5326             :         // not sorted by default, make sure it gets sorted
    5327             :         //
    5328           0 :         languages.sort_by_level();
    5329             : 
    5330           0 :         int const max_languages(browser_languages.size());
    5331           0 :         for(int i(0); i < max_languages; ++i)
    5332             :         {
    5333           0 :             QString browser_lang(browser_languages[i].get_name());
    5334           0 :             QString browser_country;
    5335           0 :             if(verify_locale(browser_lang, browser_country, false))
    5336             :             {
    5337             :                 // only keep those that are considered valid (they all should
    5338             :                 // be, but in case a hacker or strange client access our
    5339             :                 // systems...)
    5340           0 :                 locale_info_t l;
    5341           0 :                 l.set_language(browser_lang);
    5342           0 :                 l.set_country(browser_country);
    5343           0 :                 f_browser_locales.push_back(l);
    5344             :                 // added in order
    5345             :             }
    5346             :             // else -- browser sent us something we don't understand
    5347             :         }
    5348             :     }
    5349             : 
    5350           0 :     server::pointer_t server(get_server());
    5351           0 :     QString const qs_lang(server->get_parameter("qs_language"));
    5352           0 :     QString lang(f_uri.query_option(qs_lang));
    5353           0 :     QString country;
    5354             : 
    5355           0 :     if(!lang.isEmpty())
    5356             :     {
    5357             :         // user specified locale
    5358           0 :         verify_locale(lang, country, true);
    5359             :     }
    5360             :     //else
    5361             :     //{
    5362             :     //    // the lang option in the URI is defined from:
    5363             :     //    //
    5364             :     //    //   1. a sub-domain name
    5365             :     //    //   2. a section in the path
    5366             :     //    //   3. a query string parameter
    5367             :     //    //
    5368             :     //    // and that's it, but we also want to support another set of
    5369             :     //    // languages: the ones defined by the browser variable named:
    5370             :     //    //          HTTP_ACCEPT_LANGUAGE
    5371             :     //    //
    5372             :     //    // the problem we have here is that the browser variable defines
    5373             :     //    // a set of supported languages and all have a right to be used
    5374             :     //    // in a visitor's request, example:
    5375             :     //    //
    5376             :     //    //   HTTP_ACCEPT_LANGUAGE=en-us,en;q=0.8,fr-fr;q=0.5,fr;q=0.3
    5377             :     //    //
    5378             :     //    // To resolve this issue, we do not handle the browser language
    5379             :     //    // here, instead of do it in the snap_child::get_language() function,
    5380             :     //    // which is important because a plugin may force the language too.
    5381             :     //    //
    5382             :     //    if(!f_browser_locales.isEmpty())
    5383             :     //    {
    5384             :     //        // language and country already broken up
    5385             :     //        lang = f_browser_locales[0].get_language();
    5386             :     //        country = f_browser_locales[0].get_country();
    5387             :     //    }
    5388             :     //    else
    5389             :     //    {
    5390             :     //        lang = "xx";
    5391             :     //    }
    5392             :     //}
    5393             : 
    5394             :     // *** BRANCH / REVISION ***
    5395             :     // now take care of the branch and revision
    5396             : 
    5397             :     // current or working branch (working_branch=1)
    5398           0 :     QString const qs_working_branch(server->get_parameter("qs_working_branch"));
    5399           0 :     QString const working_branch_entry(f_uri.query_option(qs_working_branch));
    5400           0 :     bool const working_branch(!working_branch_entry.isEmpty());
    5401             : 
    5402             :     // branch (branch=<branch>)
    5403           0 :     QString const qs_branch(server->get_parameter("qs_branch"));
    5404           0 :     QString branch(f_uri.query_option(qs_branch));
    5405             : 
    5406             :     // rev (rev=<branch>.<revision>)
    5407           0 :     QString const qs_rev(server->get_parameter("qs_rev"));
    5408           0 :     QString rev(f_uri.query_option(qs_rev));
    5409             : 
    5410             :     // revision (revision=<revision>)
    5411           0 :     QString const qs_revision(server->get_parameter("qs_revision"));
    5412           0 :     QString revision(f_uri.query_option(qs_revision));
    5413             : 
    5414           0 :     if(branch.isEmpty() && revision.isEmpty())
    5415             :     {
    5416           0 :         if(!rev.isEmpty())
    5417             :         {
    5418           0 :             snap_string_list r(rev.split('.'));
    5419           0 :             branch = r[0];
    5420           0 :             int const size(r.size());
    5421           0 :             if(size != 1)
    5422             :             {
    5423           0 :                 if(size == 2)
    5424             :                 {
    5425           0 :                     branch = r[0];
    5426           0 :                     revision = r[1];
    5427             :                 }
    5428             :                 else
    5429             :                 {
    5430             :                     // refuse branch/revision + rev definitions together
    5431           0 :                     die(http_code_t::HTTP_CODE_BAD_REQUEST, "Invalid Revision",
    5432           0 :                             QString("The revision (%1) is not valid. It is expected to be a branch number, a period (.), and a revision number.").arg(rev),
    5433             :                             "We found a number of period other than 1 or 2.");
    5434           0 :                     NOTREACHED();
    5435             :                 }
    5436             :             }
    5437             :         }
    5438             :         // else use the default
    5439             :     }
    5440           0 :     else if(!rev.isEmpty())
    5441             :     {
    5442             :         // refuse branch/revision + rev definitions together
    5443           0 :         die(http_code_t::HTTP_CODE_BAD_REQUEST, "Invalid Revision",
    5444             :                 "You defined a rev parameter along a branch and/or revision, which is not supported. Remove one or the other to access your page.",
    5445           0 :                 QString("We only accept a branch+revision (%1.%2) or a rev (%3).").arg(branch).arg(revision).arg(rev));
    5446           0 :         NOTREACHED();
    5447             :     }
    5448             : 
    5449           0 :     snap_version::version_number_t branch_value(static_cast<snap_version::basic_version_number_t>(snap_version::SPECIAL_VERSION_UNDEFINED));
    5450           0 :     snap_version::version_number_t revision_value(static_cast<snap_version::basic_version_number_t>(snap_version::SPECIAL_VERSION_UNDEFINED));
    5451           0 :     if(!branch.isEmpty())
    5452             :     {
    5453             :         // now verify that both are formed of digits only
    5454             :         bool ok;
    5455           0 :         branch_value = branch.toInt(&ok, 10);
    5456           0 :         if(!ok)
    5457             :         {
    5458             :             // if defined the branch needs to be a valid decimal number
    5459           0 :             die(http_code_t::HTTP_CODE_BAD_REQUEST, "Invalid Branch",
    5460           0 :                     QString("Branch number \"%1\" is invalid. Only digits are expected.").arg(branch),
    5461             :                     "The user did not give us a number as the branch number.");
    5462           0 :             NOTREACHED();
    5463             :         }
    5464             :     }
    5465           0 :     if(!revision.isEmpty())
    5466             :     {
    5467             :         bool ok;
    5468           0 :         revision_value = revision.toInt(&ok, 10);
    5469           0 :         if(!ok)
    5470             :         {
    5471             :             // if defined the revision needs to be a valid decimal number
    5472           0 :             die(http_code_t::HTTP_CODE_BAD_REQUEST, "Invalid Revision",
    5473           0 :                     QString("Revision number \"%1\" is invalid. Only digits are expected.").arg(revision),
    5474             :                     "The user did not give us a number as the revision number.");
    5475           0 :             NOTREACHED();
    5476             :         }
    5477             :     }
    5478             : 
    5479             :     // *** COMPRESSION ***
    5480             :     //
    5481             :     // compression is called "Encoding" in the HTTP reference
    5482           0 :     http_strings::WeightedHttpString encodings(snapenv("HTTP_ACCEPT_ENCODING"));
    5483             : 
    5484             :     // server defined compression is named "*" (i.e. server chooses)
    5485             :     // so first we check for gzip because we support that compression
    5486             :     // and that is our favorite for now (will change later with sdpy
    5487             :     // support eventually...)
    5488           0 :     compression_vector_t compressions;
    5489           0 :     bool got_gzip(false);
    5490           0 :     float const gzip_level(std::max(std::max(encodings.get_level("gzip"), encodings.get_level("x-gzip")), encodings.get_level("*")));
    5491           0 :     float const deflate_level(encodings.get_level("deflate"));
    5492           0 :     if(gzip_level > 0.0f && gzip_level >= deflate_level)
    5493             :     {
    5494           0 :         compressions.push_back(compression_t::COMPRESSION_GZIP);
    5495           0 :         got_gzip = true;
    5496             :     }
    5497             : 
    5498             :     // now check all the other encodings and add them
    5499           0 :     http_strings::WeightedHttpString::part_t::vector_t browser_compressions(encodings.get_parts());
    5500           0 :     std::stable_sort(browser_compressions.begin(), browser_compressions.end());
    5501           0 :     int const max_compressions(browser_compressions.size());
    5502           0 :     for(int i(0); i < max_compressions; ++i)
    5503             :     {
    5504           0 :         QString encoding_name(browser_compressions[i].get_name());
    5505             : #pragma GCC diagnostic push
    5506             : #pragma GCC diagnostic ignored "-Wfloat-equal"
    5507           0 :         if(browser_compressions[i].get_level() == 0.0f)
    5508             : #pragma GCC diagnostic pop
    5509             :         {
    5510           0 :             if(encoding_name == "identity")
    5511             :             {
    5512             :                 // if you reach this entry, generate a 406!
    5513             :                 // this means the user will not accept uncompressed data
    5514           0 :                 compressions.push_back(compression_t::COMPRESSION_INVALID);
    5515             :             }
    5516             :         }
    5517             :         else
    5518             :         {
    5519           0 :             if(encoding_name == "gzip"
    5520           0 :             || encoding_name == "x-gzip"
    5521           0 :             || encoding_name == "*")
    5522             :             {
    5523             :                 // since we support multiple name we make use of a
    5524             :                 // flag to avoid adding gzip more than once
    5525           0 :                 if(!got_gzip)
    5526             :                 {
    5527           0 :                     compressions.push_back(compression_t::COMPRESSION_GZIP);
    5528           0 :                     got_gzip = true;
    5529             :                 }
    5530             :             }
    5531           0 :             else if(encoding_name == "deflate")
    5532             :             {
    5533           0 :                 compressions.push_back(compression_t::COMPRESSION_DEFLATE);
    5534             :             }
    5535           0 :             else if(encoding_name == "bz2")
    5536             :             {
    5537             :                 // not yet implemented though...
    5538           0 :                 compressions.push_back(compression_t::COMPRESSION_BZ2);
    5539             :             }
    5540           0 :             else if(encoding_name == "sdch")
    5541             :             {
    5542             :                 // not yet implemented though...
    5543           0 :                 compressions.push_back(compression_t::COMPRESSION_SDCH);
    5544             :             }
    5545           0 :             else if(encoding_name == "identity")
    5546             :             {
    5547             :                 // identity is acceptable
    5548           0 :                 compressions.push_back(compression_t::COMPRESSION_IDENTITY);
    5549             :             }
    5550             :             // else -- we do not support yet...
    5551             :         }
    5552             :     }
    5553             : 
    5554             :     // *** CACHE CONTROL ***
    5555           0 :     cache_control_settings cache_control;
    5556           0 :     cache_control.set_max_age(cache_control_settings::IGNORE_VALUE); // defaults to 0 which is not correct for client's cache information
    5557           0 :     cache_control.set_cache_info(snapenv("HTTP_CACHE_CONTROL"), false);
    5558             : 
    5559             :     // *** SAVE RESULTS ***
    5560           0 :     f_language = lang;
    5561           0 :     f_country = country;
    5562             :     //f_language_key = f_language;
    5563             :     //if(!f_country.isEmpty())
    5564             :     //{
    5565             :     //    f_language_key += "_";
    5566             :     //    f_language_key += f_country;
    5567             :     //}
    5568             : 
    5569             :     // TBD: add support for long revisions? (for JS and CSS files)
    5570           0 :     f_working_branch = working_branch;
    5571           0 :     f_branch = branch_value;
    5572           0 :     f_revision = revision_value;
    5573           0 :     f_revision_key = QString("%1.%2").arg(f_branch).arg(f_revision);
    5574             : 
    5575           0 :     f_compressions.swap(compressions);
    5576           0 :     f_client_cache_control = cache_control;
    5577           0 : }
    5578             : 
    5579             : 
    5580             : /** \brief Verify the locale defined in \p lang.
    5581             :  *
    5582             :  * This function cuts the \p lang string in two strings: the language
    5583             :  * name and one country.
    5584             :  *
    5585             :  * In most cases, when you call this function the language  parameter
    5586             :  * (\p lang) includes the entire locale such as: de_DE or am-BY. It is
    5587             :  * not mandatory so the country can also be define in which case it
    5588             :  * should not appear in the \p lang parameter.
    5589             :  *
    5590             :  * \param[in,out] lang  The language to be verified.
    5591             :  * \param[out] country  The resulting country if defined in \p lang.
    5592             :  * \param[in] generate_errors  Whether to call die() on errors.
    5593             :  *
    5594             :  * \return true if no errors were detected.
    5595             :  */
    5596           0 : bool snap_child::verify_locale(QString & lang, QString & country, bool generate_errors)
    5597             : {
    5598           0 :     country.clear();
    5599             : 
    5600             :     // search for a '-' or '_' separator as in:
    5601             :     //
    5602             :     //    fr-ca   (browser locale)
    5603             :     //    en_US   (Unix environment locale)
    5604             :     //
    5605             :     // initialize with 'ptr - 1' so we can ++ on entry of the while loop
    5606           0 :     QChar const * s(lang.data() - 1);
    5607             :     ushort c;
    5608           0 :     do
    5609             :     {
    5610           0 :         ++s;
    5611           0 :         c = s->unicode();
    5612             :     }
    5613           0 :     while(c != '\0' && c != '-' && c != '_');
    5614             : 
    5615             :     // if not '\0' then we have a country specified
    5616           0 :     if(c != '\0')
    5617             :     {
    5618           0 :         if(!country.isEmpty())
    5619             :         {
    5620           0 :             if(generate_errors)
    5621             :             {
    5622             :                 // country should always be empty on entry
    5623           0 :                 die(http_code_t::HTTP_CODE_BAD_REQUEST, "Country Defined Twice",
    5624           0 :                         QString("Country is defined twice.").arg(lang).arg(c),
    5625             :                         "This one may be a programmer mistake. The country parameter was not an empty string on entry of this function.");
    5626           0 :                 NOTREACHED();
    5627             :             }
    5628           0 :             return false;
    5629             :         }
    5630           0 :         int const pos(static_cast<int>(s - lang.data()));
    5631           0 :         country = lang.mid(pos + 1);
    5632           0 :         lang = lang.left(pos);
    5633           0 :         if(lang.isEmpty())
    5634             :         {
    5635           0 :             if(generate_errors)
    5636             :             {
    5637             :                 // we do not accept entries such as "-br" or "_RU"
    5638           0 :                 die(http_code_t::HTTP_CODE_NOT_FOUND, "Language Not Found",
    5639           0 :                         QString("Language in the locale specification \"%1\" is not defined which is not supported.").arg(lang),
    5640             :                         "Prevented user from accessing the website with no language specified, even though he specified a language entry.");
    5641           0 :                 NOTREACHED();
    5642             :             }
    5643           0 :             return false;
    5644             :         }
    5645           0 :         if(country.isEmpty())
    5646             :         {
    5647           0 :             if(generate_errors)
    5648             :             {
    5649             :                 // we do not accept entries such as "br-" or "ru_"
    5650           0 :                 die(http_code_t::HTTP_CODE_NOT_FOUND, "Country Not Found",
    5651           0 :                         QString("Country in the locale specification \"%1\" is not defined. Remove '%2' if you do not want to include a country.").arg(lang).arg(c),
    5652             :                         "Prevented user from accessing the website with no country specified, even though he specified a country entry.");
    5653           0 :                 NOTREACHED();
    5654             :             }
    5655           0 :             return false;
    5656             :         }
    5657             :     }
    5658             : 
    5659           0 :     if(lang.isEmpty())
    5660             :     {
    5661             :         // use the default
    5662           0 :         lang = "xx";
    5663             :     }
    5664             :     else
    5665             :     {
    5666           0 :         if(!verify_language_name(lang))
    5667             :         {
    5668           0 :             if(generate_errors)
    5669             :             {
    5670             :                 // language is not valid; generate an error
    5671           0 :                 die(http_code_t::HTTP_CODE_NOT_FOUND, "Language Not Found",
    5672           0 :                         QString("Language in the locale specification \"%1\" is not currently considered a known or valid language name.").arg(lang),
    5673             :                         "Prevented user from accessing the website with an invalid language.");
    5674           0 :                 NOTREACHED();
    5675             :             }
    5676           0 :             return false;
    5677             :         }
    5678             :     }
    5679             : 
    5680             :     // country can be empty, that is fine; in most cases we recommend users
    5681             :     // to not use the country name in their language
    5682           0 :     if(!country.isEmpty())
    5683             :     {
    5684           0 :         if(country.contains('.'))
    5685             :         {
    5686           0 :             if(generate_errors)
    5687             :             {
    5688             :                 // charset not supported here for now
    5689           0 :                 die(http_code_t::HTTP_CODE_NOT_FOUND, "Country Not Found",
    5690           0 :                         QString("Locale specification \"%1\" seems to include a charset which is not legal at this time.").arg(lang),
    5691             :                         "Prevented user from accessing the website because a charset was specified with the language.");
    5692           0 :                 NOTREACHED();
    5693             :             }
    5694           0 :             return false;
    5695             :         }
    5696           0 :         if(!verify_country_name(country))
    5697             :         {
    5698           0 :             if(generate_errors)
    5699             :             {
    5700             :                 // abbreviation not found
    5701           0 :                 die(http_code_t::HTTP_CODE_NOT_FOUND, "Country Not Found",
    5702           0 :                         QString("Country in locale specification \"%1\" does not look like a valid country name.").arg(lang),
    5703             :                         "Prevented user from accessing the website with an invalid country.");
    5704           0 :                 NOTREACHED();
    5705             :             }
    5706           0 :             return false;
    5707             :         }
    5708             :     }
    5709             : 
    5710           0 :     return true;
    5711             : }
    5712             : 
    5713             : 
    5714             : /** \brief Verify that a name is a language name.
    5715             :  *
    5716             :  * This function checks whether \p lang is a valid language name. If so
    5717             :  * then the function returns true and \p lang is set to the corresponding
    5718             :  * 2 letter abbreviation for that country.
    5719             :  *
    5720             :  * Note that calling the function with an empty string will make it
    5721             :  * return false.
    5722             :  *
    5723             :  * \warning
    5724             :  * The input string is changed even if the language is not found.
    5725             :  *
    5726             :  * \param[in,out] lang  The language being checked.
    5727             :  *
    5728             :  * \return true if the language is valid, false in all other cases.
    5729             :  */
    5730           0 : bool snap_child::verify_language_name(QString & lang)
    5731             : {
    5732             :     // TODO: make use of a fully optimized search with a binary search like
    5733             :     //       capability (i.e. a switch on a per character basis in a
    5734             :     //       sub-function generated using the language table)
    5735             :     //
    5736           0 :     lang = lang.toLower();
    5737           0 :     if(lang.length() == 2)
    5738             :     {
    5739             :         // we get the unicode value even though in the loop we compare
    5740             :         // against ASCII; if l0 or l1 are characters outside of the ASCII
    5741             :         // character set, then the loop will fail, simply
    5742           0 :         ushort const l0(lang[0].unicode());
    5743           0 :         ushort const l1(lang[1].unicode());
    5744           0 :         for(language_name_t const *l(g_language_names); l->f_short_name[0]; ++l)
    5745             :         {
    5746           0 :             if(l0 == l->f_short_name[0]
    5747           0 :             && l1 == l->f_short_name[1])
    5748             :             {
    5749             :                 // got it!
    5750           0 :                 return true;
    5751             :             }
    5752             :         }
    5753             :     }
    5754             :     else
    5755             :     {
    5756             :         // TBD: do we really want to support long names?
    5757           0 :         QString lang_with_commas("," + lang + ",");
    5758           0 :         QByteArray lang_with_commas_buffer(lang_with_commas.toUtf8());
    5759           0 :         char const * lwc(lang_with_commas_buffer.data());
    5760           0 :         for(language_name_t const * l(g_language_names); l->f_language; ++l)
    5761             :         {
    5762             :             // the multi-name is already semi-optimize so we test it too
    5763             :             // TBD -- this should maybe not be checked at all
    5764           0 :             if(strstr(l->f_other_names, lwc) != nullptr)
    5765             :             {
    5766           0 :                 lang = l->f_short_name;
    5767           0 :                 return true;
    5768             :             }
    5769             :         }
    5770             :     }
    5771             : 
    5772             :     // not found
    5773           0 :     return false;
    5774             : }
    5775             : 
    5776             : 
    5777             : /** \brief Verify that a name is a country name.
    5778             :  *
    5779             :  * This function checks whether \p country is a valid country name. If so
    5780             :  * then the function returns true and \p country is set to the corresponding
    5781             :  * 2 letter abbreviation for that country.
    5782             :  *
    5783             :  * Note that calling the function with an empty string will make it
    5784             :  * return false.
    5785             :  *
    5786             :  * \warning
    5787             :  * The input string is changed even if the country is not found.
    5788             :  *
    5789             :  * \param[in,out] country  The country being checked.
    5790             :  *
    5791             :  * \return true if the country is valid, false in all other cases.
    5792             :  */
    5793           0 : bool snap_child::verify_country_name(QString & country)
    5794             : {
    5795           0 :     country = country.toUpper();
    5796           0 :     if(country.length() == 2)
    5797             :     {
    5798             :         // check abbreviations only
    5799           0 :         ushort const c0 = country[0].unicode();
    5800           0 :         ushort const c1 = country[1].unicode();
    5801           0 :         for(country_name_t const *c(g_country_names); c->f_abbreviation[0]; ++c)
    5802             :         {
    5803           0 :             if(c0 == c->f_abbreviation[0] && c1 == c->f_abbreviation[1])
    5804             :             {
    5805             :                 // found it!
    5806           0 :                 return true;
    5807             :             }
    5808             :         }
    5809             :     }
    5810             :     else
    5811             :     {
    5812             :         // check full names only
    5813             :         //
    5814             :         // TODO: this could be fully optimized with a set of switch
    5815             :         //       statements and the full country name should be checked
    5816             :         //       without the part after the comma, then in full with the
    5817             :         //       part after the comma put in the front of the other
    5818             :         //       part; i.e. "Bolivia, Plurinational State of" should
    5819             :         //       be checked as any one of the following four entries:
    5820             :         //
    5821             :         //           BO  (abbreviation)
    5822             :         //           Bolivia
    5823             :         //           Plurinational State of Bolivia
    5824             :         //           Bolivia, Plurinational State of
    5825             :         //
    5826             :         // TBD -- I'm not so sure we really want to support full names
    5827           0 :         for(country_name_t const *c(g_country_names); c->f_abbreviation[0]; ++c)
    5828             :         {
    5829           0 :             if(QString(c->f_name).toUpper() == country)
    5830             :             {
    5831           0 :                 country = c->f_abbreviation;
    5832           0 :                 return true;
    5833             :             }
    5834             :         }
    5835             :     }
    5836             : 
    5837           0 :     return false;
    5838             : }
    5839             : 
    5840             : 
    5841             : /** \brief Check whether a site needs to be redirected.
    5842             :  *
    5843             :  * This function verifies the site we just discovered to see whether
    5844             :  * the user requested a redirect. If so, then we replace the
    5845             :  * f_site_key accordingly.
    5846             :  *
    5847             :  * Note that this is not a 301 redirect, just an internal remap from
    5848             :  * site A to site B.
    5849             :  */
    5850           0 : void snap_child::site_redirect()
    5851             : {
    5852           0 :     libdbproxy::value redirect(get_site_parameter(get_name(name_t::SNAP_NAME_CORE_REDIRECT)));
    5853           0 :     if(redirect.nullValue())
    5854             :     {
    5855             :         // no redirect
    5856           0 :         return;
    5857             :     }
    5858             : 
    5859             :     // redirect now
    5860           0 :     f_site_key = redirect.stringValue();
    5861             : 
    5862             :     // TBD -- should we also redirect the f_domain_key and f_website_key?
    5863             : 
    5864             :     // the site table is the old one, we want to switch to the new one
    5865           0 :     reset_sites_table();
    5866             : }
    5867             : 
    5868             : 
    5869             : /** \brief Reset the site table so one can make sure to use the latest version.
    5870             :  *
    5871             :  * In a backend that does not restart all the time, we may need to reset
    5872             :  * the site table.
    5873             :  *
    5874             :  * This function clears the memory cache of the existing site table, if
    5875             :  * one exists in memory, and then it resets the site table pointer.
    5876             :  * The next time a user calls the set_site_parameter() or get_site_parameter()
    5877             :  * a new site table object is created and filled as required.
    5878             :  */
    5879           0 : void snap_child::reset_sites_table()
    5880             : {
    5881           0 :     f_sites_table.reset();
    5882           0 : }
    5883             : 
    5884             : 
    5885             : /** \brief Redirect the user to a new page.
    5886             :  *
    5887             :  * This function forcibly redirects a user to a new page. If the path
    5888             :  * includes a protocol (is a full URI) then it is used as is. If
    5889             :  * the path includes no protocol, the current site key is prepended.
    5890             :  *
    5891             :  * The HTTP code can be specified. By default, 301 is assumed because
    5892             :  * that's the most prominent redirect code used. If a page is used to
    5893             :  * redirect dynamically, make sure to use 302 or 303 instead. You can
    5894             :  * safely use one of the following codes:
    5895             :  *
    5896             :  * \li HTTP_CODE_MOVED_PERMANENTLY (301)
    5897             :  * \li HTTP_CODE_FOUND (302)
    5898             :  * \li HTTP_CODE_SEE_OTHER (303) -- POST becomes GET
    5899             :  * \li HTTP_CODE_TEMPORARY_REDIRECT (307) -- keep same method
    5900             :  * \li HTTP_CODE_PERMANENT_REDIRECT (308) -- keep same method
    5901             :  *
    5902             :  * The path may include a query string and an anchor.
    5903             :  *
    5904             :  * \note
    5905             :  * This function does not allow redirecting an application which used a
    5906             :  * method other than GET, HEAD, and POST. Applications are expected to
    5907             :  * get their URI right.
    5908             :  *
    5909             :  * \warning
    5910             :  * The function does not return since after sending a redirect to a client
    5911             :  * there is nothing more you can do. So if you need to save some data, make
    5912             :  * sure to do it before this call. Note also that this function calls the
    5913             :  * attach_to_session() signal so any plugin that was not really done has a
    5914             :  * chance to save its data until the next connection arrives.
    5915             :  *
    5916             :  * \param[in] path  The full URI or the local website path to redirect the
    5917             :  *                  user browser to.
    5918             :  * \param[in] http_code  The code used to send the redirect.
    5919             :  * \param[in] reason_brief  A brief explanation for the redirection.
    5920             :  * \param[in] reason  The long version of the explanation for the redirection.
    5921             :  */
    5922           0 : void snap_child::page_redirect(QString const & path, http_code_t http_code, QString const & reason_brief, QString const & reason)
    5923             : {
    5924           0 :     if(f_site_key_with_slash.isEmpty())
    5925             :     {
    5926           0 :         die(http_code_t::HTTP_CODE_INTERNAL_SERVER_ERROR, "Initialization Mismatch",
    5927             :                 "An internal server error was detected while initializing the process.",
    5928             :                 "The server snap_child::page_redirect() function was called before the website got canonicalized.");
    5929           0 :         NOTREACHED();
    5930             :     }
    5931           0 :     QString const method(snapenv(get_name(name_t::SNAP_NAME_CORE_REQUEST_METHOD)));
    5932           0 :     if(method != "GET"
    5933           0 :     && method != "POST"
    5934           0 :     && method != "HEAD")
    5935             :     {
    5936           0 :         die(http_code_t::HTTP_CODE_FORBIDDEN, "Method Not Allowed",
    5937           0 :                 QString("Prevented a redirect when using method %1.").arg(method),
    5938             :                 "We do not currently support redirecting users for methods other than GET, POST, and HEAD.");
    5939           0 :         NOTREACHED();
    5940             :     }
    5941             : 
    5942           0 :     switch(http_code)
    5943             :     {
    5944           0 :     case http_code_t::HTTP_CODE_MOVED_PERMANENTLY:
    5945             :     case http_code_t::HTTP_CODE_FOUND:
    5946             :     case http_code_t::HTTP_CODE_SEE_OTHER:
    5947             :     case http_code_t::HTTP_CODE_TEMPORARY_REDIRECT:
    5948             :     case http_code_t::HTTP_CODE_PERMANENT_REDIRECT:
    5949           0 :         break;
    5950             : 
    5951           0 :     default:
    5952           0 :         die(http_code_t::HTTP_CODE_FORBIDDEN, "Error Code Not Allowed For Redirect",
    5953           0 :                 QString("Prevented a redirect using HTTP code %1.").arg(static_cast<int>(http_code)),
    5954             :                 "We limit the redirect to using 301, 302, 303, 307, and 308.");
    5955           0 :         NOTREACHED();
    5956             : 
    5957             :     }
    5958             : 
    5959           0 :     if(path.contains('\n') || path.contains('\r'))
    5960             :     {
    5961             :         // if the path includes a \n or \r then the user could inject
    5962             :         // a header which could have all sorts of effects we don't even
    5963             :         // want to think about! just deny it...
    5964           0 :         die(http_code_t::HTTP_CODE_INTERNAL_SERVER_ERROR, "Hack Prevention",
    5965             :                 "Server prevented a potential hack from being applied.",
    5966           0 :                 "The server snap_child::page_redirect() function was called with a path that includes \n or \r and refused processing it: \"" + path + "\"");
    5967           0 :         NOTREACHED();
    5968             :     }
    5969             : 
    5970           0 :     snap_uri uri;
    5971           0 :     if(!uri.set_uri(path))
    5972             :     {
    5973             :         // in most cases it fails because the protocol is missing
    5974           0 :         QString local_path(path);
    5975           0 :         canonicalize_path(local_path);
    5976           0 :         if(!uri.set_uri(get_site_key_with_slash() + local_path))
    5977             :         {
    5978           0 :             die(http_code_t::HTTP_CODE_ACCESS_DENIED, "Invalid URI",
    5979             :                     "The server prevented a redirect because it could not understand the destination URI.",
    5980           0 :                     "The server snap_child::page_redirect() function was called with a path that it did not like: \"" + path + "\"");
    5981           0 :             NOTREACHED();
    5982             :         }
    5983             :     }
    5984             : 
    5985           0 :     attach_to_session();
    5986             : 
    5987             :     // redirect the user to the specified path
    5988           0 :     QString http_name;
    5989           0 :     define_http_name(http_code, http_name);
    5990             : 
    5991           0 :     set_header("Status", QString("%1 %2")
    5992           0 :                     .arg(static_cast<int>(http_code))
    5993           0 :                     .arg(http_name), HEADER_MODE_REDIRECT);
    5994             : 
    5995           0 :     server::pointer_t server(get_server());
    5996           0 :     snap_string_list const show_redirects(server->get_parameter("show_redirects").split(","));
    5997             : 
    5998           0 :     if(!show_redirects.contains("refresh-only"))
    5999             :     {
    6000             :         // the get_uri() returns an HTTP encoded string
    6001           0 :         set_header("Location", uri.get_uri(), HEADER_MODE_REDIRECT);
    6002             :     }
    6003             : 
    6004             :     // also the default is already text/html we force it again in case this
    6005             :     // function is called after someone changed this header
    6006           0 :     set_header(get_name(name_t::SNAP_NAME_CORE_CONTENT_TYPE_HEADER), "text/html; charset=utf-8", HEADER_MODE_EVERYWHERE);
    6007             : 
    6008             :     // compute the body
    6009             :     // (TBD: should we support getting the content of a page? since 99.9999% of
    6010             :     // the time this content is ignored, I would say no.)
    6011             :     //
    6012           0 :     QString body;
    6013             :     {
    6014           0 :         QDomDocument doc;
    6015             :         // html
    6016           0 :         QDomElement html(doc.createElement("html"));
    6017           0 :         doc.appendChild(html);
    6018             :         // html/head
    6019           0 :         QDomElement head(doc.createElement("head"));
    6020           0 :         html.appendChild(head);
    6021             :         // html/head/meta[@http-equiv=...][@content=...]
    6022           0 :         QDomElement meta_locale(doc.createElement("meta"));
    6023           0 :         head.appendChild(meta_locale);
    6024           0 :         meta_locale.setAttribute("http-equiv", get_name(name_t::SNAP_NAME_CORE_CONTENT_TYPE_HEADER));
    6025           0 :         meta_locale.setAttribute("content", "text/html; charset=utf-8");
    6026             :         // html/head/title/...
    6027           0 :         QDomElement title(doc.createElement("title"));
    6028           0 :         head.appendChild(title);
    6029           0 :         QDomText title_text(doc.createTextNode(reason_brief));
    6030           0 :         title.appendChild(title_text);
    6031             :         // html/head/meta[@http-equiv=...][@content=...]
    6032           0 :         if(show_redirects.contains("refresh-only")
    6033           0 :         || !show_redirects.contains("no-refresh"))
    6034             :         {
    6035           0 :             QDomElement meta_refresh(doc.createElement("meta"));
    6036           0 :             head.appendChild(meta_refresh);
    6037           0 :             meta_refresh.setAttribute("http-equiv", "Refresh");
    6038           0 :             int timeout(0);
    6039           0 :             if(show_redirects.contains("one-minute"))
    6040             :             {
    6041           0 :                 timeout = 60;
    6042             :             }
    6043           0 :             meta_refresh.setAttribute("content", QString("%1; url=%2").arg(timeout).arg(uri.get_uri()));
    6044             :         }
    6045             :         // html/head/meta[@http-equiv=...][@content=...]
    6046           0 :         QDomElement meta_robots(doc.createElement("meta"));
    6047           0 :         head.appendChild(meta_robots);
    6048           0 :         meta_robots.setAttribute("name", "ROBOTS");
    6049           0 :         meta_robots.setAttribute("content", "NOINDEX");
    6050             :         // html/body
    6051           0 :         QDomElement body_tag(doc.createElement("body"));
    6052           0 :         html.appendChild(body_tag);
    6053             : 
    6054             :         // include an actual body?
    6055           0 :         if(show_redirects.contains("include-body"))
    6056             :         {
    6057             :             // html/body/h1
    6058           0 :             QDomElement h1(doc.createElement("h1"));
    6059           0 :             body_tag.appendChild(h1);
    6060           0 :             QDomText h1_text(doc.createTextNode(reason_brief));
    6061           0 :             h1.appendChild(h1_text);
    6062             :             // html/body/p
    6063           0 :             QDomElement p(doc.createElement("p"));
    6064           0 :             body_tag.appendChild(p);
    6065           0 :             QDomText p_text1(doc.createTextNode(QString("%1 New location: ").arg(reason)));
    6066           0 :             p.appendChild(p_text1);
    6067             :             // html/body/a
    6068           0 :             QDomElement a(doc.createElement("a"));
    6069           0 :             a.setAttribute("href", uri.get_uri());
    6070           0 :             p.appendChild(a);
    6071           0 :             QDomText a_text(doc.createTextNode(uri.get_uri()));
    6072           0 :             a.appendChild(a_text);
    6073             :             // html/body/p (text after anchor)
    6074           0 :             QDomText p_text2(doc.createTextNode("."));
    6075           0 :             p.appendChild(p_text2);
    6076             :         }
    6077             : 
    6078             :         // save the result
    6079           0 :         body = doc.toString(-1);
    6080             :     }
    6081             : 
    6082           0 :     output_result(HEADER_MODE_REDIRECT, body.toUtf8());
    6083             : 
    6084             :     // XXX should we exit with 1 in this case?
    6085           0 :     exit(0);
    6086           0 :     NOTREACHED();
    6087             : }
    6088             : 
    6089             : 
    6090             : /** \brief Attach variables to this session.
    6091             :  *
    6092             :  * Once in a while a plugin creates a form that is intermediary. In this
    6093             :  * case the session variables need to be saved and this function is called.
    6094             :  *
    6095             :  * Note that you may want to look into not detaching the variable(s) if at
    6096             :  * all possible.
    6097             :  */
    6098           0 : void snap_child::attach_to_session()
    6099             : {
    6100           0 :     server::pointer_t server(get_server());
    6101           0 :     server->attach_to_session();
    6102           0 : }
    6103             : 
    6104             : 
    6105             : /** \brief Load a file.
    6106             :  *
    6107             :  * This signal is sent to all the plugins so one can load the file as
    6108             :  * defined in the file filename.
    6109             :  *
    6110             :  * The function returns true if the file was loaded successfully.
    6111             :  *
    6112             :  * \param[in,out] file  The file that is to be loaded.
    6113             :  *
    6114             :  * \return true if the file was found, false otherwise.
    6115             :  */
    6116           0 : bool snap_child::load_file(post_file_t & file)
    6117             : {
    6118           0 :     server::pointer_t server(get_server());
    6119           0 :     bool found(false);
    6120           0 :     server->load_file(file, found);
    6121           0 :     return found;
    6122             : }
    6123             : 
    6124             : 
    6125             : /** \brief Retrieve an environment variable.
    6126             :  *
    6127             :  * This function can be used to read an environment variable. It will make
    6128             :  * sure, in most cases, that the variable is not tented.
    6129             :  *
    6130             :  * At this point only the variables defined in the HTTP request are available.
    6131             :  * Any other variable name will return an empty string.
    6132             :  *
    6133             :  * The SERVER_PROTOCOL variable can be retrieved at any time, even before we
    6134             :  * read the environment. This is done so we can call the die() function and
    6135             :  * return with a valid protocol and version.
    6136             :  *
    6137             :  * \param[in] name  The name of the variable to retrieve.
    6138             :  *
    6139             :  * \return The value of the specified variable.
    6140             :  */
    6141           0 : QString snap_child::snapenv(QString const & name) const
    6142             : {
    6143           0 :     if(name == get_name(name_t::SNAP_NAME_CORE_SERVER_PROTOCOL))
    6144             :     {
    6145             :         // SERVER PROTOCOL
    6146           0 :         if(false == f_fixed_server_protocol)
    6147             :         {
    6148           0 :             f_fixed_server_protocol = true;
    6149             :             // Drupal does the following
    6150             :             // TBD can the SERVER_PROTOCOL really be wrong?
    6151           0 :             if(f_env.count(get_name(name_t::SNAP_NAME_CORE_SERVER_PROTOCOL)) != 1)
    6152             :             {
    6153             :                 // if undefined, set a default protocol
    6154           0 :                 const_cast<snap_child *>(this)->f_env[get_name(name_t::SNAP_NAME_CORE_SERVER_PROTOCOL)] = "HTTP/1.0";
    6155             :             }
    6156             :             else
    6157             :             {
    6158             :                 // note that HTTP/0.9 could be somewhat supported but that's
    6159             :                 // most certainly totally useless
    6160           0 :                 if("HTTP/1.0" != f_env.value(get_name(name_t::SNAP_NAME_CORE_SERVER_PROTOCOL))
    6161           0 :                 && "HTTP/1.1" != f_env.value(get_name(name_t::SNAP_NAME_CORE_SERVER_PROTOCOL)))
    6162             :                 {
    6163             :                     // environment is no good!?
    6164           0 :                     const_cast<snap_child *>(this)->f_env[get_name(name_t::SNAP_NAME_CORE_SERVER_PROTOCOL)] = "HTTP/1.0";
    6165             :                 }
    6166             :             }
    6167             :         }
    6168           0 :         return f_env.value(get_name(name_t::SNAP_NAME_CORE_SERVER_PROTOCOL), "HTTP/1.0");
    6169             :     }
    6170             : 
    6171           0 :     return f_env.value(name, "");
    6172             : }
    6173             : 
    6174             : 
    6175             : /** \brief Check whether a POST variable was defined.
    6176             :  *
    6177             :  * Check whether the named POST variable was defined. This can be useful if
    6178             :  * you have some optional fields in a form. Also in some places where the
    6179             :  * code does not know about all the widgets.
    6180             :  *
    6181             :  * Note that the functions that directly access the post environment should
    6182             :  * not be used by most as the form plugin already does what is necessary.
    6183             :  *
    6184             :  * \param[in] name  The name of the POST variable to fetch.
    6185             :  *
    6186             :  * \return true if the value is defined, false otherwise.
    6187             :  */
    6188           0 : bool snap_child::postenv_exists(QString const & name) const
    6189             : {
    6190           0 :     return f_post.contains(name);
    6191             : }
    6192             : 
    6193             : 
    6194             : /** \brief Retrieve a POST variable.
    6195             :  *
    6196             :  * Return the content of one of the POST variables. Post variables are defined
    6197             :  * only if the method used to access the site was a POST.
    6198             :  *
    6199             :  * \warning
    6200             :  * This function returns the RAW data from a POST. You should instead use the
    6201             :  * data returned by your form which will have been validated and fixed up as
    6202             :  * required (decoded, etc.)
    6203             :  *
    6204             :  * \param[in] name  The name of the POST variable to fetch.
    6205             :  * \param[in] default_value  The value to return if the POST variable is not defined.
    6206             :  *
    6207             :  * \return The value of that POST variable or the \p default_value.
    6208             :  */
    6209           0 : QString snap_child::postenv(QString const & name, QString const & default_value) const
    6210             : {
    6211           0 :     return f_post.value(name, default_value);
    6212             : }
    6213             : 
    6214             : 
    6215             : /** \brief Replace the value of a POST variable.
    6216             :  *
    6217             :  * Once in a while you may want to change a POST variable. One reason might
    6218             :  * be to apply a filter on a field before it gets saved in the database. This
    6219             :  * is accessible by any plugin so any one of those can do some work as
    6220             :  * required.
    6221             :  *
    6222             :  * \param[in] name  The name of the POST variable to fetch.
    6223             :  * \param[in] value  The new value for this POST variable.
    6224             :  */
    6225           0 : void snap_child::replace_postenv(QString const & name, QString const & value)
    6226             : {
    6227           0 :     f_post[name] = value;
    6228           0 : }
    6229             : 
    6230             : 
    6231             : /** \brief Check whether a file from the POST request is defined.
    6232             :  *
    6233             :  * This function is expected to be called to verify that a file was
    6234             :  * indeed uploaded for the named widget.
    6235             :  *
    6236             :  * If this function returns false, then the postfile() function should not
    6237             :  * be called.
    6238             :  *
    6239             :  * \param[in] name  The id (name) of the Input File widget.
    6240             :  *
    6241             :  * \sa postfile()
    6242             :  */
    6243           0 : bool snap_child::postfile_exists(QString const & name) const
    6244             : {
    6245             :     // the QMap only returns a reference if this is not constant
    6246             :     //
    6247           0 :     return f_files.contains(name) && f_files[name].get_size() != 0;
    6248             : }
    6249             : 
    6250             : 
    6251             : /** \brief Retrieve a file from the POST.
    6252             :  *
    6253             :  * This function can be called if this request included a POST with a
    6254             :  * file attached.
    6255             :  *
    6256             :  * Note that the files are saved by widget identifier. This means if
    6257             :  * you check a post with postenv("file") (which returns the filename),
    6258             :  * then you can get the actual file with postfile("file").
    6259             :  *
    6260             :  * \note
    6261             :  * If the file was not sent by the browser, then this function creates
    6262             :  * an entry in the global table which is probably not what you want.
    6263             :  * Make sure to call the postfile_exists() function first.
    6264             :  *
    6265             :  * \param[in] name  The id (name) of the Input File widget.
    6266             :  *
    6267             :  * \sa postfile_exists()
    6268             :  */
    6269           0 : snap_child::post_file_t const & snap_child::postfile(QString const & name) const
    6270             : {
    6271             :     // the QMap only returns a reference if this is not constant
    6272           0 :     return const_cast<snap_child *>(this)->f_files[name];
    6273             : }
    6274             : 
    6275             : 
    6276             : /** \brief Check whether a cookie was sent to us by the browser.
    6277             :  *
    6278             :  * This function checks whether a cookie was defined and returned by the
    6279             :  * browser. This is different from testing whether the value returned by
    6280             :  * cookie() is an empty string.
    6281             :  *
    6282             :  * \note
    6283             :  * Doing a set_cookie() does not interfere with this list of cookies
    6284             :  * which represent the list of cookies the browser sent to us.
    6285             :  *
    6286             :  * \param[in] name  The name of the cookie to retrieve.
    6287             :  *
    6288             :  * \return true if the cookie was sent to us, false otherwise.
    6289             :  */
    6290           0 : bool snap_child::cookie_is_defined(QString const & name) const
    6291             : {
    6292           0 :     return f_browser_cookies.contains(name);
    6293             : }
    6294             : 
    6295             : 
    6296             : /** \brief Return the contents of a cookie.
    6297             :  *
    6298             :  * This function returns the contents of the named cookie.
    6299             :  *
    6300             :  * Note that this function is not the counterpart of the set_cookie()
    6301             :  * function. The set_cookie() accepts an http_cookie object, whereas
    6302             :  * this function only returns a string (because that's all we get
    6303             :  * from the browser.)
    6304             :  *
    6305             :  * \param[in] name  The name of the cookie to retrieve.
    6306             :  *
    6307             :  * \return The content of the cookie, an empty string if the cookie is not defined.
    6308             :  */
    6309           0 : QString snap_child::cookie(QString const & name) const
    6310             : {
    6311           0 :     if(f_browser_cookies.contains(name))
    6312             :     {
    6313           0 :         return f_browser_cookies[name];
    6314             :     }
    6315           0 :     return QString();
    6316             : }
    6317             : 
    6318             : 
    6319             : /** \brief Make sure to clean up then exit the child process.
    6320             :  *
    6321             :  * This function cleans up the child and then calls the
    6322             :  * server::exit() function to give the server a chance to
    6323             :  * also clean up. Then it exists by calling the exit(3)
    6324             :  * function of the C library.
    6325             :  *
    6326             :  * \param[in] code  The exit code, generally 0 or 1.
    6327             :  */
    6328           0 : void snap_child::exit(int code)
    6329             : {
    6330             :     // if we have a messenger, make sure to get rid of it
    6331             :     //
    6332           0 :     stop_messenger();
    6333             : 
    6334             :     // make sure the socket data is pushed to the caller
    6335             :     //
    6336           0 :     f_client.reset();
    6337             : 
    6338             :     // after we close the socket the answer is sent to the client so
    6339             :     // we can take a little time to gather some statistics.
    6340             :     //
    6341           0 :     server::pointer_t server( f_server.lock() );
    6342           0 :     if(server)
    6343             :     {
    6344           0 :         server->udp_rusage("snap_child");
    6345             :     }
    6346             : 
    6347           0 :     server::exit(code);
    6348             :     NOTREACHED();
    6349             : }
    6350             : 
    6351             : 
    6352             : /** \brief Check whether the server was started in debug mode.
    6353             :  *
    6354             :  * With this function any plugin can determine whether the server was
    6355             :  * started with the --debug command line option and act accordingly
    6356             :  * (i.e. show a certain number of debug in stdout or stderr).
    6357             :  *
    6358             :  * It should not be used to display debug data in the HTML output.
    6359             :  * Other parameters can be used for that purpose (specifically, that
    6360             :  * very plugin can make use of its own debug parameter for that because
    6361             :  * having the debug turned on for all modules would be a killer.)
    6362             :  *
    6363             :  * \return true if the --debug (-d) command line option was used to start
    6364             :  *         the server
    6365             :  */
    6366           0 : bool snap_child::is_debug() const
    6367             : {
    6368           0 :     server::pointer_t server(get_server());
    6369           0 :     return server->is_debug();
    6370             : }
    6371             : 
    6372             : 
    6373             : /** \brief This function tells you whether the child is ready or not.
    6374             :  *
    6375             :  * The child prepares the database using a lot of complicated add_content()
    6376             :  * calls in the content plugin. Once all those calls were made, the system
    6377             :  * finally tells the world that it is ready and then it triggers the
    6378             :  * execute() signal.
    6379             :  *
    6380             :  * The flag is false up until just before the execute() function of
    6381             :  * the server gets called. It is a good way to know whether you are
    6382             :  * still in the initialization process or you are already working
    6383             :  * from within the path plugin (which is the one plugin that captures
    6384             :  * the execute() signal).
    6385             :  *
    6386             :  * \return true if the snap_child object triggered the execute() signal.
    6387             :  */
    6388           0 : bool snap_child::is_ready() const
    6389             : {
    6390           0 :     return f_ready;
    6391             : }
    6392             : 
    6393             : 
    6394             : /** \brief Retrieve the library (server) version.
    6395             :  *
    6396             :  * Retrieve the version of the snapwebsites library.
    6397             :  *
    6398             :  * \return A pointer to the version of the running server library.
    6399             :  */
    6400           0 : char const * snap_child::get_running_server_version()
    6401             : {
    6402           0 :     return server::version();
    6403             : }
    6404             : 
    6405             : 
    6406             : /** \brief Check whether the plugin is considered a Core Plugin.
    6407             :  *
    6408             :  * Plugins can be forced defined by the administrator in the
    6409             :  * snapserver.conf under the name "plugins". In that case,
    6410             :  * only those specific plugins are loaded. This is quite practicle
    6411             :  * if a plugin is generating problems and you cannot otherwise
    6412             :  * check out your website.
    6413             :  *
    6414             :  * The list of plugins can be soft defined in the default_plugins
    6415             :  * variable. This variable is used by new websites until the
    6416             :  * user adds and removes plugins to his website by editing the
    6417             :  * list of plugins via the Plugin Selector ("/admin/plugin").
    6418             :  *
    6419             :  * However, in all cases, the system will not work if you do not
    6420             :  * have a certain number of low level plugins running such as the
    6421             :  * content and users plugins. These are considered Core Plugins.
    6422             :  * This function returns true whenever the specified \p name
    6423             :  * represents a Core Plugin.
    6424             :  *
    6425             :  * \param[in] name  The name of the plugin to check.
    6426             :  *
    6427             :  * \return true if the plugin is a Core Plugin.
    6428             :  */
    6429           0 : bool snap_child::is_core_plugin(QString const & name) const
    6430             : {
    6431             :     // a special case because "server" is not listed in the g_minimum_plugins
    6432             :     // list (because it is already loaded since it is an object in the library)
    6433             :     //
    6434           0 :     if(name == "server")
    6435             :     {
    6436           0 :         return true;
    6437             :     }
    6438             : 
    6439             :     // TODO: make sure the table is sorted alphabetically and use
    6440             :     //       a binary search
    6441             :     //
    6442           0 :     for(size_t i(0); i < sizeof(g_minimum_plugins) / sizeof(g_minimum_plugins[0]); ++i)
    6443             :     {
    6444           0 :         if(name == g_minimum_plugins[i])
    6445             :         {
    6446           0 :             return true;
    6447             :         }
    6448             :     }
    6449             : 
    6450           0 :     return false;
    6451             : }
    6452             : 
    6453             : 
    6454             : /** \brief Retrieve a server parameter.
    6455             :  *
    6456             :  * This function calls the get_parameter() function of the server. This
    6457             :  * gives you access to all the parameters defined in the server
    6458             :  * configuration file.
    6459             :  *
    6460             :  * This gives you access to parameters such as the qs_action and
    6461             :  * the default_plugins list.
    6462             :  *
    6463             :  * \param[in] name  The name of the parameter to retrieve.
    6464             :  */
    6465           0 : QString snap_child::get_server_parameter(QString const & name)
    6466             : {
    6467             : #ifdef DEBUG
    6468           0 :     if(name.isEmpty())
    6469             :     {
    6470           0 :         throw snap_logic_exception("get_server_parameter() called with an empty string as the name of the parameter to be retrieved");
    6471             :     }
    6472             : #endif
    6473           0 :     server::pointer_t server(get_server());
    6474           0 :     return server->get_parameter(name);
    6475             : }
    6476             : 
    6477             : 
    6478             : /** \brief Retrieve the path to the snapwebsites data folder.
    6479             :  *
    6480             :  * This function retrieve the path to the data used by various processes
    6481             :  * to save data accross runs.
    6482             :  *
    6483             :  * For example, we save our counter.u64 used to generate unique numbers
    6484             :  * on any one computer.
    6485             :  *
    6486             :  * By default the path is `"/var/lib/snapwebsites"`.
    6487             :  *
    6488             :  * \note
    6489             :  * The function never returns an empty path.
    6490             :  *
    6491             :  * \return The path to the data directory.
    6492             :  */
    6493           0 : QString snap_child::get_data_path()
    6494             : {
    6495             :     // get the data_path variable from the configuration file
    6496             :     //
    6497           0 :     server::pointer_t server(get_server());
    6498           0 :     QString path(server->get_parameter(get_name(name_t::SNAP_NAME_CORE_DATA_PATH)));
    6499             : 
    6500             :     // if not defined by end user, return the default value
    6501             :     //
    6502           0 :     return path.isEmpty()
    6503             :                 ? QString("/var/lib/snapwebsites")
    6504           0 :                 : path;
    6505             : }
    6506             : 
    6507             : 
    6508             : /** \brief Retrieve the path to the list data.
    6509             :  *
    6510             :  * This function retrieve the path to the data used by the list environment.
    6511             :  * The list plugin and backends make use of this path to handle the
    6512             :  * journal and database that they manage.
    6513             :  *
    6514             :  * By default the path is `"/var/lib/snapwebsites/list"`.
    6515             :  *
    6516             :  * \return The path to the list data directory.
    6517             :  */
    6518           0 : QString snap_child::get_list_data_path()
    6519             : {
    6520             :     // try the most specific path first
    6521             :     //
    6522           0 :     QString path(get_server_parameter(get_name(name_t::SNAP_NAME_CORE_LIST_DATA_PATH)));
    6523             : 
    6524           0 :     if(path.isEmpty())
    6525             :     {
    6526             :         // if the most specific is not defined, then maybe the basic
    6527             :         // data_path is, we need to add "list" at the end, though
    6528             :         //
    6529           0 :         path = get_data_path();
    6530             : 
    6531             :         // get_data_path() never returns an empty path so no need to test
    6532             :         //
    6533           0 :         path += "/list";
    6534             :     }
    6535             : 
    6536             :     // if not defined by end user, return the default value
    6537             :     //
    6538           0 :     return path;
    6539             : }
    6540             : 
    6541             : 
    6542             : /** \brief Change the status of the user.
    6543             :  *
    6544             :  * This message is used to send a copy of the user status whenever it
    6545             :  * changes.
    6546             :  *
    6547             :  * \param[in] status  The new user status.
    6548             :  * \param[in] id  The user identifier.
    6549             :  */
    6550           0 : void snap_child::user_status(user_status_t status, user_identifier_t id)
    6551             : {
    6552           0 :     server::pointer_t server(get_server());
    6553           0 :     return server->user_status(status, id);
    6554             : }
    6555             : 
    6556             : 
    6557             : /** \brief Improve signature to place at the bottom of a page.
    6558             :  *
    6559             :  * This signal can be used to generate a signature to place at the bottom
    6560             :  * of a page. In most cases, this very simple signature function is only
    6561             :  * used on error pages, although plugins that generate rather generic
    6562             :  * content are welcome to use it too.
    6563             :  *
    6564             :  * \param[in] path  The path to the page that is being generated.
    6565             :  * \param[in] doc  The document holding the data.
    6566             :  * \param[in,out] signature_tag  The signature tag which is to be improved.
    6567             :  */
    6568           0 : void snap_child::improve_signature(QString const & path, QDomDocument doc, QDomElement signature_tag)
    6569             : {
    6570           0 :     server::pointer_t server(get_server());
    6571           0 :     return server->improve_signature(path, doc, signature_tag);
    6572             : }
    6573             : 
    6574             : 
    6575             : /** \brief Retreive a website wide parameter.
    6576             :  *
    6577             :  * This function reads a column from the sites table using the site key as
    6578             :  * defined by the canonicalization process. The function cannot be called
    6579             :  * before the canonicalization process ends.
    6580             :  *
    6581             :  * The table is opened once and remains opened so calling this function
    6582             :  * many times is not a problem. Also the libQtCassandra library caches
    6583             :  * all the data. Reading the same field multiple times is not a concern
    6584             :  * at all.
    6585             :  *
    6586             :  * If the value is undefined, the result is a null value.
    6587             :  *
    6588             :  * \param[in] name  The name of the parameter to retrieve.
    6589             :  *
    6590             :  * \return The content of the row as a Cassandra value.
    6591             :  */
    6592           0 : libdbproxy::value snap_child::get_site_parameter(QString const & name)
    6593             : {
    6594             :     // retrieve site table if not there yet
    6595           0 :     if(!f_sites_table)
    6596             :     {
    6597           0 :         QString const table_name(get_name(name_t::SNAP_NAME_SITES));
    6598           0 :         libdbproxy::table::pointer_t table(f_context->findTable(table_name));
    6599           0 :         if(!table)
    6600             :         {
    6601             :             // the whole table is still empty
    6602           0 :             libdbproxy::value value;
    6603           0 :             return value;
    6604             :         }
    6605           0 :         f_sites_table = table;
    6606             :     }
    6607             : 
    6608           0 :     if(!f_sites_table->exists(f_site_key))
    6609             :     {
    6610             :         // an empty value is considered to be a null value
    6611           0 :         libdbproxy::value value;
    6612           0 :         return value;
    6613             :     }
    6614           0 :     libdbproxy::row::pointer_t row(f_sites_table->getRow(f_site_key));
    6615           0 :     if(!row->exists(name))
    6616             :     {
    6617             :         // an empty value is considered to be a null value
    6618           0 :         libdbproxy::value value;
    6619           0 :         return value;
    6620             :     }
    6621             : 
    6622           0 :     return row->getCell(name)->getValue();
    6623             : }
    6624             : 
    6625             : 
    6626             : /** \brief Save a website wide parameter.
    6627             :  *
    6628             :  * This function writes a column to the sites table using the site key as
    6629             :  * defined by the canonicalization process. The function cannot be called
    6630             :  * before the canonicalization process ends.
    6631             :  *
    6632             :  * The table is opened once and remains opened so calling this function
    6633             :  * many times is not a problem.
    6634             :  *
    6635             :  * If the value was still undefined, then it is created.
    6636             :  *
    6637             :  * \param[in] name  The name of the parameter to save.
    6638             :  * \param[in] value  The new value for this parameter.
    6639             :  */
    6640           0 : void snap_child::set_site_parameter(QString const & name, libdbproxy::value const & value)
    6641             : {
    6642             :     // retrieve site table if not there yet
    6643             :     //
    6644           0 :     if(!f_sites_table)
    6645             :     {
    6646             :         // get a pointer to the "sites" table
    6647             :         //
    6648           0 :         f_sites_table = get_table(get_name(name_t::SNAP_NAME_SITES));
    6649             :     }
    6650             : 
    6651           0 :     f_sites_table->getRow(f_site_key)->getCell(name)->setValue(value);
    6652           0 : }
    6653             : 
    6654             : 
    6655             : /** \brief Retrieve a current copy of the output buffer.
    6656             :  *
    6657             :  * This function returns a copy of the current snap_child output buffer.
    6658             :  *
    6659             :  * The buffer cannot be changed using the returned buffer. Use the
    6660             :  * write() functions to appened to the buffer.
    6661             :  *
    6662             :  * \return A copy of the output buffer.
    6663             :  */
    6664           0 : QByteArray snap_child::get_output() const
    6665             : {
    6666           0 :     return f_output.buffer();
    6667             : }
    6668             : 
    6669             : 
    6670             : /** \brief Write the buffer to the output.
    6671             :  *
    6672             :  * This function writes the specified buffer (array of bytes) to
    6673             :  * the output of the snap child. When the execute function returns
    6674             :  * from running all the plugins, the data in the buffer is sent to
    6675             :  * Apache (through snap.cgi).
    6676             :  *
    6677             :  * This function is most often used when the process is replying with
    6678             :  * data other than text (i.e. images, PDF documents, etc.)
    6679             :  *
    6680             :  * \param[in] data  The array of byte to append to the buffer.
    6681             :  */
    6682           0 : void snap_child::output(QByteArray const & data)
    6683             : {
    6684           0 :     f_output.write(data);
    6685           0 : }
    6686             : 
    6687             : 
    6688             : /** \brief Write the string to the output buffer.
    6689             :  *
    6690             :  * This function writes the specified string to the output buffer
    6691             :  * of the snap child. When the execute function returns from running
    6692             :  * all the plugins, the data in the buffer is sent to Apache.
    6693             :  *
    6694             :  * The data is always written in UTF-8.
    6695             :  *
    6696             :  * \param[in] data  The string data to append to the buffer.
    6697             :  */
    6698           0 : void snap_child::output(QString const & data)
    6699             : {
    6700           0 :     f_output.write(data.toUtf8());
    6701           0 : }
    6702             : 
    6703             : 
    6704             : /** \brief Write the string to the output buffer.
    6705             :  *
    6706             :  * This function writes the specified string to the output buffer
    6707             :  * of the snap child. When the execute function returns from running
    6708             :  * all the plugins, the data in the buffer is sent to Apache.
    6709             :  *
    6710             :  * The data is viewed as UTF-8 characters and it is sent as is to the
    6711             :  * buffer.
    6712             :  *
    6713             :  * \param[in] data  The string data to append to the buffer.
    6714             :  */
    6715           0 : void snap_child::output(std::string const & data)
    6716             : {
    6717           0 :     f_output.write(data.c_str(), data.length());
    6718           0 : }
    6719             : 
    6720             : 
    6721             : /** \brief Write the string to the output buffer.
    6722             :  *
    6723             :  * This function writes the specified string to the output buffer
    6724             :  * of the snap child. When the execute function returns from running
    6725             :  * all the plugins, the data in the buffer is sent to Apache.
    6726             :  *
    6727             :  * The data is viewed as UTF-8 characters and it is sent as is to the
    6728             :  * buffer.
    6729             :  *
    6730             :  * \param[in] data  The string data to append to the buffer.
    6731             :  */
    6732           0 : void snap_child::output(char const * data)
    6733             : {
    6734           0 :     f_output.write(data);
    6735           0 : }
    6736             : 
    6737             : 
    6738             : /** \brief Write the string to the output buffer.
    6739             :  *
    6740             :  * This function writes the specified string to the output buffer
    6741             :  * of the snap child. When the execute function returns from running
    6742             :  * all the plugins, the data in the buffer is sent to Apache.
    6743             :  *
    6744             :  * The data is viewed as UTF-8 characters and it is sent as is to the
    6745             :  * buffer.
    6746             :  *
    6747             :  * \param[in] data  The string data to append to the buffer.
    6748             :  */
    6749           0 : void snap_child::output(wchar_t const * data)
    6750             : {
    6751           0 :     f_output.write(QString::fromWCharArray(data).toUtf8());
    6752           0 : }
    6753             : 
    6754             : 
    6755             : /** \brief Check whether someone wrote any output yet.
    6756             :  *
    6757             :  * This function checks whether any output was written or not.
    6758             :  *
    6759             :  * \return true if the output buffer is still empty.
    6760             :  */
    6761           0 : bool snap_child::empty_output() const
    6762             : {
    6763           0 :     return f_output.buffer().size() == 0;
    6764             : }
    6765             : 
    6766             : 
    6767             : /** \brief Trace in case we are initializing the website.
    6768             :  *
    6769             :  * While initializing (when a request was sent using #INIT instead of
    6770             :  * #START) then we immediately send data back using the trace() functions.
    6771             :  *
    6772             :  * \param[in] data  The data to send to the listener.
    6773             :  *                  Generally a printable string.
    6774             :  */
    6775           0 : void snap_child::trace(QString const & data)
    6776             : {
    6777           0 :     trace(std::string(data.toUtf8().data()));
    6778           0 : }
    6779             : 
    6780             : 
    6781             : /** \brief Trace in case we are initializing the website.
    6782             :  *
    6783             :  * While initializing (when a request was sent using #INIT instead of
    6784             :  * #START) then we immediately send data back using the trace() functions.
    6785             :  *
    6786             :  * \param[in] data  The data to send to the listener.
    6787             :  *                  Generally a printable string.
    6788             :  */
    6789           0 : void snap_child::trace(std::string const & data)
    6790             : {
    6791           0 :     if(f_is_being_initialized)
    6792             :     {
    6793             :         // keep a copy in the server logs too
    6794             :         //
    6795           0 :         if(data.back() == '\n')
    6796             :         {
    6797           0 :             SNAP_LOG_INFO("trace() from installation: ")(data.substr(0, data.length() - 1));
    6798             :         }
    6799             :         else
    6800             :         {
    6801           0 :             SNAP_LOG_INFO("trace() from installation: ")(data);
    6802             :         }
    6803             : 
    6804           0 :         write(data.c_str(), data.size());
    6805             :     }
    6806           0 : }
    6807             : 
    6808             : 
    6809             : /** \brief Trace in case we are initializing the website.
    6810             :  *
    6811             :  * While initializing (when a request was sent using #INIT instead of
    6812             :  * #START) then we immediately send data back using the trace() functions.
    6813             :  *
    6814             :  * \param[in] data  The data to send to the listener.
    6815             :  *                  Generally a printable string.
    6816             :  */
    6817           0 : void snap_child::trace(char const * data)
    6818             : {
    6819           0 :     trace(std::string(data));
    6820           0 : }
    6821             : 
    6822             : 
    6823             : /** \brief Generate an HTTP error and exit the child process.
    6824             :  *
    6825             :  * This function kills the child process after sending an HTTP
    6826             :  * error message to the user and to the logger.
    6827             :  *
    6828             :  * The \p err_name parameter is optional in that it can be set to
    6829             :  * the empty string ("") and let the die() function make use of
    6830             :  * the default error message for the specified \p err_code.
    6831             :  *
    6832             :  * The error description message can include HTML tags to change
    6833             :  * the basic format of the text (i.e. bold, italic, underline, and
    6834             :  * other inline tags.) The message is printed inside a paragraph
    6835             :  * tag (<p>) and thus it should not include block tags.
    6836             :  * The message is expected to be UTF-8 encoded, although in general
    6837             :  * it should be in English so only using ASCII.
    6838             :  *
    6839             :  * The \p err_details parameter is the message to write to the
    6840             :  * log. It should be as detailed as possible so it makes it
    6841             :  * easy to know what's wrong and eventually needs attention.
    6842             :  *
    6843             :  * \note
    6844             :  * You can trick the description paragraph by adding a closing
    6845             :  * paragraph tag (</p>) at the start and an opening paragraph
    6846             :  * tag (<p>) at the end of your description.
    6847             :  *
    6848             :  * \warning
    6849             :  * This function does NOT return. It calls exit(1) once done.
    6850             :  *
    6851             :  * \param[in] err_code  The error code such as 501 or 503.
    6852             :  * \param[in] err_name  The name of the error such as "Service Not Available".
    6853             :  * \param[in] err_description  HTML message about the problem.
    6854             :  * \param[in] err_details  Server side text message with details that are logged only.
    6855             :  */
    6856           0 : void snap_child::die(http_code_t err_code, QString err_name, QString const & err_description, QString const & err_details)
    6857             : {
    6858           0 :     if(f_died)
    6859             :     {
    6860             :         // avoid loops
    6861             :         //
    6862           0 :         return;
    6863             :     }
    6864           0 :     f_died = true;
    6865             : 
    6866             :     try
    6867             :     {
    6868             :         // define a default error name if undefined
    6869             :         //
    6870           0 :         define_http_name(err_code, err_name);
    6871             : 
    6872             :         // log the error
    6873             :         //
    6874           0 :         SNAP_LOG_FATAL("snap child process: ")(err_details)(" (")(static_cast<int>(err_code))(" ")(err_name)(": ")(err_description)(")");
    6875             : 
    6876           0 :         if(f_is_being_initialized)
    6877             :         {
    6878             :             // send initialization process the info about the error
    6879             :             //
    6880           0 :             trace(QString("Error: die() called: %1 (%2 %3: %4)\n").arg(err_details).arg(static_cast<int>(err_code)).arg(err_name).arg(err_description));
    6881           0 :             trace("#END\n");
    6882             :         }
    6883             :         else
    6884             :         {
    6885             :             // On error we do not return the HTTP protocol, only the Status field
    6886             :             // it just needs to be first to make sure it works right
    6887             :             //
    6888             :             // IMPORTANT NOTE: we WANT the header to be set when we call
    6889             :             //                 the attach_to_session() function so someone
    6890             :             //                 can at least peruse it
    6891             :             //
    6892           0 :             set_header(get_name(name_t::SNAP_NAME_CORE_STATUS_HEADER),
    6893           0 :                        QString("%1 %2").arg(static_cast<int>(err_code)).arg(err_name),
    6894             :                        HEADER_MODE_ERROR);
    6895             : 
    6896             :             // Make sure that the session is re-attached
    6897             :             //
    6898           0 :             if(f_cassandra)
    6899             :             {
    6900           0 :                 attach_to_session();
    6901             :             }
    6902             : 
    6903             :             // content type is HTML, we reset this header because it could have
    6904             :             // been changed to something else and prevent the error from showing
    6905             :             // up in the browser
    6906             :             //
    6907           0 :             set_header(get_name(name_t::SNAP_NAME_CORE_CONTENT_TYPE_HEADER),
    6908             :                        "text/html; charset=utf8",
    6909             :                        HEADER_MODE_EVERYWHERE);
    6910             : 
    6911             :             // TODO: the HTML could also come from a user defined
    6912             :             //       page so that way it can get a translated
    6913             :             //       message without us having to do anything
    6914             :             //       (but probably only for 403 and 404 pages?)
    6915             : 
    6916           0 :             QString const html(error_body(err_code, err_name, err_description));
    6917             : 
    6918             :             // In case someone changed the cache controls, make sure the
    6919             :             // cache is turned off and errors are public
    6920             :             //
    6921           0 :             f_server_cache_control.set_no_cache(true);
    6922           0 :             f_page_cache_control.set_no_cache(true);
    6923             : 
    6924             :             // in case there are any cookies, send them along too
    6925             :             //
    6926           0 :             output_result(HEADER_MODE_ERROR, html.toUtf8());
    6927             :         }
    6928             :     }
    6929           0 :     catch(std::exception const & e)
    6930             :     {
    6931             :         // ignore all errors because at this point we must die quickly.
    6932           0 :         SNAP_LOG_FATAL("snap_child.cpp:die(): try/catch caught an exception. What: ")(e.what());
    6933             :     }
    6934           0 :     catch(...)
    6935             :     {
    6936             :         // ignore all errors because at this point we must die quickly.
    6937           0 :         SNAP_LOG_FATAL("snap_child.cpp:die(): try/catch caught an exception");
    6938             :     }
    6939             : 
    6940             :     // exit with an error
    6941           0 :     exit(1);
    6942             : }
    6943             : 
    6944             : 
    6945             : /** \brief Create the body of an HTTP error.
    6946             :  *
    6947             :  * This function generates an error page for an HTTP error. Thus far,
    6948             :  * it is used by the die() function and an equivalent in the attachment
    6949             :  * plugin.
    6950             :  *
    6951             :  * \param[in] err_code  The error code such as 501 or 503.
    6952             :  * \param[in] err_name  The name of the error such as "Service Not Available".
    6953             :  * \param[in] err_description  HTML message about the problem.
    6954             :  */
    6955           0 : QString snap_child::error_body(http_code_t err_code, QString const & err_name, QString const & err_description)
    6956             : {
    6957           0 :     QString const title(QString("%1 %2").arg(static_cast<int>(err_code)).arg(err_name));
    6958             : 
    6959             :     // html
    6960           0 :     QDomDocument doc;
    6961           0 :     QDomElement html(doc.createElement("html"));
    6962           0 :     doc.appendChild(html);
    6963             : 
    6964             :     // html/head
    6965           0 :     QDomElement head(doc.createElement("head"));
    6966           0 :     html.appendChild(head);
    6967             : 
    6968             :     // html/head/meta[@http-equiv=...][@content=...]
    6969           0 :     QDomElement meta_locale(doc.createElement("meta"));
    6970           0 :     head.appendChild(meta_locale);
    6971           0 :     meta_locale.setAttribute("http-equiv", get_name(name_t::SNAP_NAME_CORE_CONTENT_TYPE_HEADER));
    6972           0 :     meta_locale.setAttribute("content", "text/html; charset=utf-8");
    6973             : 
    6974             :     // html/head/meta[@name=...][@content=...]
    6975           0 :     QDomElement meta_robots(doc.createElement("meta"));
    6976           0 :     head.appendChild(meta_robots);
    6977           0 :     meta_robots.setAttribute("name", "ROBOTS");
    6978           0 :     meta_robots.setAttribute("content", "NOINDEX");
    6979             : 
    6980             :     // html/head/title/...
    6981           0 :     QDomElement title_tag(doc.createElement("title"));
    6982           0 :     head.appendChild(title_tag);
    6983           0 :     snap_dom::append_plain_text_to_node(title_tag, title);
    6984             : 
    6985             :     // html/head/style/...
    6986           0 :     QDomElement style_tag(doc.createElement("style"));
    6987           0 :     head.appendChild(style_tag);
    6988           0 :     snap_dom::append_plain_text_to_node(style_tag, "body{font-family:sans-serif}");
    6989             : 
    6990             :     // html/body
    6991           0 :     QDomElement body_tag(doc.createElement("body"));
    6992           0 :     html.appendChild(body_tag);
    6993             : 
    6994             :     // html/body/h1/...
    6995           0 :     QDomElement h1_tag(doc.createElement("h1"));
    6996           0 :     h1_tag.setAttribute("class", "error");
    6997           0 :     body_tag.appendChild(h1_tag);
    6998           0 :     snap_dom::append_plain_text_to_node(h1_tag, title);
    6999             : 
    7000             :     // html/body/p[@class=description]/...
    7001           0 :     QDomElement description_tag(doc.createElement("p"));
    7002           0 :     description_tag.setAttribute("class", "description");
    7003           0 :     body_tag.appendChild(description_tag);
    7004             :     // the description may include HTML tags
    7005           0 :     snap_dom::insert_html_string_to_xml_doc(description_tag, err_description);
    7006             : 
    7007             :     // html/body/p[@class=signature]/...
    7008           0 :     QDomElement signature_tag(doc.createElement("p"));
    7009           0 :     signature_tag.setAttribute("class", "signature");
    7010           0 :     body_tag.appendChild(signature_tag);
    7011             : 
    7012             :     // now generate the signature tag anchors
    7013           0 :     QString const site_key(get_site_key());
    7014           0 :     if(f_cassandra)
    7015             :     {
    7016           0 :         libdbproxy::value const site_name(get_site_parameter(get_name(name_t::SNAP_NAME_CORE_SITE_NAME)));
    7017           0 :         QDomElement a_tag(doc.createElement("a"));
    7018           0 :         a_tag.setAttribute("class", "home");
    7019           0 :         a_tag.setAttribute("target", "_top");
    7020           0 :         a_tag.setAttribute("href", site_key);
    7021           0 :         signature_tag.appendChild(a_tag);
    7022           0 :         snap_dom::append_plain_text_to_node(a_tag, site_name.stringValue());
    7023             : 
    7024           0 :         improve_signature(f_uri.path(), doc, signature_tag);
    7025             :     }
    7026           0 :     else if(!site_key.isEmpty())
    7027             :     {
    7028           0 :         QDomElement a_tag(doc.createElement("a"));
    7029           0 :         a_tag.setAttribute("class", "home");
    7030           0 :         a_tag.setAttribute("target", "_top");
    7031           0 :         a_tag.setAttribute("href", site_key);
    7032           0 :         signature_tag.appendChild(a_tag);
    7033           0 :         snap_dom::append_plain_text_to_node(a_tag, site_key);
    7034             : 
    7035           0 :         improve_signature(f_uri.path(), doc, signature_tag);
    7036             :     }
    7037             :     // else -- no signature...
    7038             : 
    7039           0 :     return doc.toString(-1);
    7040             : }
    7041             : 
    7042             : 
    7043             : /** \brief Ensure that the http_name variable is not empty.
    7044             :  *
    7045             :  * This function sets the content of the \p http_name variable if empty. It
    7046             :  * uses the \p http_code value to define a default message in \p http_name.
    7047             :  *
    7048             :  * If the \p http_name string is not empty then it is not modified.
    7049             :  *
    7050             :  * \param[in] http_code  The code used to determine the http_name value.
    7051             :  * \param[in,out] http_name  The http request name to set if not already defined.
    7052             :  */
    7053           0 : void snap_child::define_http_name(http_code_t http_code, QString & http_name)
    7054             : {
    7055           0 :     if(http_name.isEmpty())
    7056             :     {
    7057           0 :         switch(http_code)
    7058             :         {
    7059             :         // 1xx
    7060           0 :         case http_code_t::HTTP_CODE_CONTINUE:                               http_name = "Continue"; break;
    7061           0 :         case http_code_t::HTTP_CODE_SWITCHING_PROTOCOLS:                    http_name = "Switching Protocols"; break;
    7062           0 :         case http_code_t::HTTP_CODE_PROCESSING:                             http_name = "Processing"; break;
    7063             : 
    7064             :         // 2xx
    7065           0 :         case http_code_t::HTTP_CODE_OK:                                     http_name = "OK"; break;
    7066           0 :         case http_code_t::HTTP_CODE_CREATED:                                http_name = "Created"; break;
    7067           0 :         case http_code_t::HTTP_CODE_ACCEPTED:                               http_name = "Accepted"; break;
    7068           0 :         case http_code_t::HTTP_CODE_NON_AUTHORITATIVE_INFORMATION:          http_name = "Non-Authoritative Information"; break;
    7069           0 :         case http_code_t::HTTP_CODE_NO_CONTENT:                             http_name = "No Content"; break;
    7070           0 :         case http_code_t::HTTP_CODE_RESET_CONTENT:                          http_name = "Reset Content"; break;
    7071           0 :         case http_code_t::HTTP_CODE_PARTIAL_CONTENT:                        http_name = "Partial Content"; break;
    7072           0 :         case http_code_t::HTTP_CODE_MULTI_STATUS:                           http_name = "Multi-Status"; break;
    7073           0 :         case http_code_t::HTTP_CODE_ALREADY_REPORTED:                       http_name = "Already Reported"; break;
    7074           0 :         case http_code_t::HTTP_CODE_IM_USED:                                http_name = "Instance-Manipulation Used"; break;
    7075             : 
    7076             :         // 3xx
    7077           0 :         case http_code_t::HTTP_CODE_MULTIPLE_CHOICE:                        http_name = "Multiple Choice"; break;
    7078           0 :         case http_code_t::HTTP_CODE_MOVED_PERMANENTLY:                      http_name = "Moved Permanently"; break;
    7079           0 :         case http_code_t::HTTP_CODE_FOUND:                                  http_name = "Found"; break;
    7080           0 :         case http_code_t::HTTP_CODE_SEE_OTHER:                              http_name = "See Other"; break; // POST becomes GET
    7081           0 :         case http_code_t::HTTP_CODE_NOT_MODIFIED:                           http_name = "Not Modified"; break;
    7082           0 :         case http_code_t::HTTP_CODE_USE_PROXY:                              http_name = "Use Proxy"; break;
    7083           0 :         case http_code_t::HTTP_CODE_SWITCH_PROXY:                           http_name = "Switch Proxy"; break;
    7084           0 :         case http_code_t::HTTP_CODE_TEMPORARY_REDIRECT:                     http_name = "Temporary Redirect"; break; // keep same method
    7085           0 :         case http_code_t::HTTP_CODE_PERMANENT_REDIRECT:                     http_name = "Permanent Redirect"; break; // keep same method
    7086             : 
    7087             :         // 4xx
    7088           0 :         case http_code_t::HTTP_CODE_BAD_REQUEST:                            http_name = "Bad Request"; break;
    7089           0 :         case http_code_t::HTTP_CODE_UNAUTHORIZED:                           http_name = "Unauthorized"; break;
    7090           0 :         case http_code_t::HTTP_CODE_PAYMENT_REQUIRED:                       http_name = "Payment Required"; break;
    7091           0 :         case http_code_t::HTTP_CODE_FORBIDDEN:                              http_name = "Forbidden"; break;
    7092           0 :         case http_code_t::HTTP_CODE_NOT_FOUND:                              http_name = "Not Found"; break;
    7093           0 :         case http_code_t::HTTP_CODE_METHOD_NOT_ALLOWED:                     http_name = "Method Not Allowed"; break;
    7094           0 :         case http_code_t::HTTP_CODE_NOT_ACCEPTABLE:                         http_name = "Not Acceptable"; break;
    7095           0 :         case http_code_t::HTTP_CODE_PROXY_AUTHENTICATION_REQUIRED:          http_name = "Proxy Authentication Required"; break;
    7096           0 :         case http_code_t::HTTP_CODE_REQUEST_TIMEOUT:                        http_name = "Request Timeout"; break;
    7097           0 :         case http_code_t::HTTP_CODE_CONFLICT:                               http_name = "Conflict"; break;
    7098           0 :         case http_code_t::HTTP_CODE_GONE:                                   http_name = "Gone"; break;
    7099           0 :         case http_code_t::HTTP_CODE_LENGTH_REQUIRED:                        http_name = "Length Required"; break;
    7100           0 :         case http_code_t::HTTP_CODE_PRECONDITION_FAILED:                    http_name = "Precondition Failed"; break;
    7101           0 :         case http_code_t::HTTP_CODE_REQUEST_ENTITY_TOO_LARGE:               http_name = "Request Entity Too Large"; break;
    7102           0 :         case http_code_t::HTTP_CODE_REQUEST_URI_TOO_LONG:                   http_name = "Request-URI Too Long"; break;
    7103           0 :         case http_code_t::HTTP_CODE_UNSUPPORTED_MEDIA_TYPE:                 http_name = "Unsupported Media Type"; break;
    7104           0 :         case http_code_t::HTTP_CODE_REQUESTED_RANGE_NOT_SATISFIABLE:        http_name = "Requested Range Not Satisfiable"; break;
    7105           0 :         case http_code_t::HTTP_CODE_EXPECTATION_FAILED:                     http_name = "Expectation Failed"; break;
    7106           0 :         case http_code_t::HTTP_CODE_I_AM_A_TEAPOT:                          http_name = "I'm a teapot"; break;
    7107           0 :         case http_code_t::HTTP_CODE_ENHANCE_YOUR_CALM:                      http_name = "Enhance Your Calm"; break;
    7108             :         //case http_code_t::HTTP_CODE_METHOD_FAILURE:                         http_name = "Method Failure"; break;
    7109           0 :         case http_code_t::HTTP_CODE_UNPROCESSABLE_ENTITY:                   http_name = "Unprocessable Entity"; break;
    7110           0 :         case http_code_t::HTTP_CODE_LOCKED:                                 http_name = "Locked"; break;
    7111           0 :         case http_code_t::HTTP_CODE_FAILED_DEPENDENCY:                      http_name = "Failed Dependency"; break;
    7112           0 :         case http_code_t::HTTP_CODE_UNORDERED_COLLECTION:                   http_name = "Unordered Collection"; break;
    7113           0 :         case http_code_t::HTTP_CODE_UPGRADE_REQUIRED:                       http_name = "Upgrade Required"; break;
    7114           0 :         case http_code_t::HTTP_CODE_PRECONDITION_REQUIRED:                  http_name = "Precondition Required"; break;
    7115           0 :         case http_code_t::HTTP_CODE_TOO_MANY_REQUESTS:                      http_name = "Too Many Requests"; break;
    7116           0 :         case http_code_t::HTTP_CODE_REQUEST_HEADER_FIELDS_TOO_LARGE:        http_name = "Request Header Fields Too Large"; break;
    7117           0 :         case http_code_t::HTTP_CODE_NO_RESPONSE:                            http_name = "No Response"; break;
    7118           0 :         case http_code_t::HTTP_CODE_RETRY_WITH:                             http_name = "Retry With"; break;
    7119           0 :         case http_code_t::HTTP_CODE_BLOCKED_BY_WINDOWS_PARENTAL_CONTROLS:   http_name = "Blocked by Windows Parental Controls"; break;
    7120           0 :         case http_code_t::HTTP_CODE_UNAVAILABLE_FOR_LEGAL_REASONS:          http_name = "Unavailable For Legal Reasons"; break;
    7121             :         //case http_code_t::HTTP_CODE_REDIRECT:                               http_name = "Redirect"; break;
    7122           0 :         case http_code_t::HTTP_CODE_REQUEST_HEADER_TOO_LARGE:               http_name = "Request Header Too Large"; break;
    7123           0 :         case http_code_t::HTTP_CODE_CERT_ERROR:                             http_name = "Cert Error"; break;
    7124           0 :         case http_code_t::HTTP_CODE_NO_CERT:                                http_name = "No Cert"; break;
    7125           0 :         case http_code_t::HTTP_CODE_HTTP_TO_HTTPS:                          http_name = "HTTP to HTTPS"; break;
    7126           0 :         case http_code_t::HTTP_CODE_TOKEN_EXPIRED:                          http_name = "Token Expired"; break;
    7127           0 :         case http_code_t::HTTP_CODE_CLIENT_CLOSED_REQUEST:                  http_name = "Client Closed Request"; break;
    7128             :         //case http_code_t::HTTP_CODE_TOKEN_REQUIRED:                         http_name = "Token Required"; break;
    7129             : 
    7130             :         // 5xx
    7131           0 :         case http_code_t::HTTP_CODE_INTERNAL_SERVER_ERROR:                  http_name = "Internal Server Error"; break;
    7132           0 :         case http_code_t::HTTP_CODE_NOT_IMPLEMENTED:                        http_name = "Not Implemented"; break;
    7133           0 :         case http_code_t::HTTP_CODE_BAD_GATEWAY:                            http_name = "Bad Gateway"; break;
    7134           0 :         case http_code_t::HTTP_CODE_SERVICE_UNAVAILABLE:                    http_name = "Service Unavailable"; break;
    7135           0 :         case http_code_t::HTTP_CODE_GATEWAY_TIMEOUT:                        http_name = "Gateway Timeout"; break;
    7136           0 :         case http_code_t::HTTP_CODE_HTTP_VERSION_NOT_SUPPORTED:             http_name = "HTTP Version Not Supported"; break;
    7137           0 :         case http_code_t::HTTP_CODE_VARIANTS_ALSO_NEGOTIATES:               http_name = "Variants Also Negotiates"; break;
    7138           0 :         case http_code_t::HTTP_CODE_INSUFFICIANT_STORAGE:                   http_name = "Insufficiant Storage"; break;
    7139           0 :         case http_code_t::HTTP_CODE_LOOP_DETECTED:                          http_name = "Loop Detected"; break;
    7140           0 :         case http_code_t::HTTP_CODE_BANDWIDTH_LIMIT_EXCEEDED:               http_name = "Bandwidth Limit Exceeded"; break;
    7141           0 :         case http_code_t::HTTP_CODE_NOT_EXTENDED:                           http_name = "Not Extended"; break;
    7142           0 :         case http_code_t::HTTP_CODE_NETWORK_AUTHENTICATION_REQUIRED:        http_name = "Network Authentication Required"; break;
    7143           0 :         case http_code_t::HTTP_CODE_ACCESS_DENIED:                          http_name = "Access Denied"; break;
    7144           0 :         case http_code_t::HTTP_CODE_NETWORK_READ_TIMEOUT_ERROR:             http_name = "Network read timeout error"; break;
    7145           0 :         case http_code_t::HTTP_CODE_NETWORK_CONNECT_TIMEOUT_ERROR:          http_name = "Network connect timeout error"; break;
    7146             : 
    7147           0 :         default: // plugins can always use other codes
    7148           0 :             http_name = "Unknown HTTP Code";
    7149           0 :             break;
    7150             : 
    7151             :         }
    7152             :     }
    7153           0 : }
    7154             : 
    7155             : /** \brief Set an HTTP header.
    7156             :  *
    7157             :  * This function sets the specified HTTP header to the specified value.
    7158             :  * This function overwrites the existing value if any. To append to the
    7159             :  * existing value, use the append_header() function instead. Note that
    7160             :  * append only works with fields that supports lists (comma separated
    7161             :  * values, etc.)
    7162             :  *
    7163             :  * The value is trimmed of LWS (SP, HT, CR, LF) characters on both ends.
    7164             :  * Also, if the value includes CR or LF characters, it must be followed
    7165             :  * by at least one SP or HT. Note that all CR are transformed to LF and
    7166             :  * double LFs are replaced by one LF.
    7167             :  *
    7168             :  * The definition of an HTTP header is message-header as found
    7169             :  * in the snippet below:
    7170             :  *
    7171             :  * \code
    7172             :  *     OCTET          = <any 8-bit sequence of data>
    7173             :  *     CHAR           = <any US-ASCII character (octets 0 - 127)>
    7174             :  *     CTL            = <any US-ASCII control character
    7175             :  *                      (octets 0 - 31) and DEL (127)>
    7176             :  *     CR             = <US-ASCII CR, carriage return (13)>
    7177             :  *     LF             = <US-ASCII LF, linefeed (10)>
    7178             :  *     SP             = <US-ASCII SP, space (32)>
    7179             :  *     HT             = <US-ASCII HT, horizontal-tab (9)>
    7180             :  *     CRLF           = CR LF
    7181             :  *     LWS            = [CRLF] 1*( SP | HT )
    7182             :  *     TEXT           = <any OCTET except CTLs,
    7183             :  *                      but including LWS>
    7184             :  *     token          = 1*<any CHAR except CTLs or separators>
    7185             :  *     separators     = "(" | ")" | "<" | ">" | "@"
    7186             :  *                    | "," | ";" | ":" | "\" | <">
    7187             :  *                    | "/" | "[" | "]" | "?" | "="
    7188             :  *                    | "{" | "}" | SP | HT
    7189             :  *     message-header = field-name ":" [ field-value ]
    7190             :  *     field-name     = token
    7191             :  *     field-value    = *( field-content | LWS )
    7192             :  *     field-content  = <the OCTETs making up the field-value
    7193             :  *                      and consisting of either *TEXT or combinations
    7194             :  *                      of token, separators, and quoted-string>
    7195             :  * \endcode
    7196             :  *
    7197             :  * To remove a header, set the value to the empty string.
    7198             :  *
    7199             :  * References: http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html
    7200             :  * and http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html
    7201             :  *
    7202             :  * When adding a header, it is expected to only be used when no error
    7203             :  * occurs (HEADER_MODE_NO_ERROR). However, in some circumstances it
    7204             :  * is useful to send additional headers with errors or redirects.
    7205             :  * These headers can use a different mode so they appear in those other
    7206             :  * locations.
    7207             :  *
    7208             :  * \note
    7209             :  * The key of the f_header map is the name in lowercase. For this
    7210             :  * reason we save the field name as defined by the user in the
    7211             :  * value as expected in the final result (i.e. "Blah: " + value.)
    7212             :  *
    7213             :  * \todo
    7214             :  * Added a separate function so we can add multiple HTTP Link entries.
    7215             :  *
    7216             :  * \param[in] name  The name of the header.
    7217             :  * \param[in] value  The value to assign to that header. Set to an empty
    7218             :  *                   string to remove the header.
    7219             :  * \param[in] modes  Where the header will be used.
    7220             :  */
    7221           0 : void snap_child::set_header(QString const & name, QString const & value, header_mode_t modes)
    7222             : {
    7223             :     {
    7224             :         // name cannot include controls or separators and only CHARs
    7225           0 :         std::vector<wchar_t> ws;
    7226           0 :         ws.resize(name.length());
    7227           0 :         name.toWCharArray(&ws[0]);
    7228           0 :         int p(name.length());
    7229           0 :         while(p > 0)
    7230             :         {
    7231           0 :             --p;
    7232           0 :             wchar_t wc(ws[p]);
    7233           0 :             bool valid(true);
    7234           0 :             if(wc < 0x21 || wc > 0x7E)
    7235             :             {
    7236           0 :                 valid = false;
    7237             :             }
    7238           0 :             else switch(wc)
    7239             :             {
    7240           0 :             case L'(': case L')': case L'<': case L'>': case L'@':
    7241             :             case L',': case L';': case L':': case L'\\': case L'"':
    7242             :             case L'/': case L'[': case L']': case L'?': case L'=':
    7243             :             case L'{': case L'}': // SP & HT are checked in previous if()
    7244           0 :                 valid = false;
    7245           0 :                 break;
    7246             : 
    7247           0 :             default:
    7248             :                 //valid = true; -- default to true
    7249           0 :                 break;
    7250             : 
    7251             :             }
    7252           0 :             if(!valid)
    7253             :             {
    7254             :                 // more or less ASCII except well defined separators
    7255           0 :                 throw snap_child_exception_invalid_header_field_name(QString("header field name \"%1\" is not valid, found unwanted character: '%2'").arg(name).arg(QChar(wc)));
    7256             :             }
    7257             :         }
    7258             :     }
    7259             : 
    7260           0 :     QString v;
    7261             :     {
    7262             :         // value cannot include controls except LWS (\r, \n and \t)
    7263           0 :         std::vector<wchar_t> ws;
    7264           0 :         ws.resize(value.length());
    7265           0 :         value.toWCharArray(&ws[0]);
    7266           0 :         int const max_length(value.length());
    7267           0 :         wchar_t lc(L'\0');
    7268           0 :         for(int p(0); p < max_length; ++p)
    7269             :         {
    7270           0 :             wchar_t wc(ws[p]);
    7271           0 :             if((wc < 0x20 || wc == 127) && wc != L'\r' && wc != L'\n' && wc != L'\t')
    7272             :             {
    7273             :                 // refuse controls except \r, \n, \t
    7274           0 :                 throw snap_child_exception_invalid_header_value("header field value \"" + value + "\" is not valid, found unwanted character: '" + QChar(wc) + "'");
    7275             :             }
    7276             :             // we MUST have a space or tab after a newline
    7277           0 :             if(wc == L'\r' || wc == L'\n')
    7278             :             {
    7279             :                 // if p + 1 == max_length then the user supplied the ending "\r\n"
    7280           0 :                 if(p + 1 < max_length)
    7281             :                 {
    7282           0 :                     if(ws[p] != L' ' && ws[p] != L'\t' && ws[p] != L'\r' && ws[p] != L'\n')
    7283             :                     {
    7284             :                         // missing space or tab after a "\r\n" sequence
    7285             :                         // (we also accept \r or \n although empty lines are
    7286             :                         // forbidden but we'll remove them anyway)
    7287           0 :                         throw snap_child_exception_invalid_header_value("header field value \"" + value + "\" is not valid, found a \\r pr \\n not followed by a space");
    7288             :                     }
    7289             :                 }
    7290             :             }
    7291           0 :             if(v.isEmpty() && (wc == L' ' || wc == L'\t' || wc == L'\r' || wc == L'\n'))
    7292             :             {
    7293             :                 // trim on the left (that's easy and fast to do here)
    7294           0 :                 continue;
    7295             :             }
    7296           0 :             if(wc == L'\r')
    7297             :             {
    7298           0 :                 wc = L'\n';
    7299             :             }
    7300           0 :             if(lc == L'\n' && wc == L'\n')
    7301             :             {
    7302             :                 // do not double '\n' (happens when user sends us "\r\n")
    7303           0 :                 continue;
    7304             :             }
    7305           0 :             v += QChar(wc);
    7306           0 :             lc = wc;
    7307             :         }
    7308             :         // remove ending spaces which would otherwise cause problems in
    7309             :         // the HTTP header
    7310           0 :         while(!v.isEmpty())
    7311             :         {
    7312           0 :             QChar c(v.right(1)[0]);
    7313             :             // we skip the '\r' because those were removed anyway
    7314           0 :             if(c != ' ' && c != '\t' /*&& c != '\r'*/ && c != '\n')
    7315             :             {
    7316           0 :                 break;
    7317             :             }
    7318           0 :             v.remove(v.length() - 1, 1);
    7319             :         }
    7320             :     }
    7321             : 
    7322           0 :     if(v.isEmpty())
    7323             :     {
    7324           0 :         f_header.remove(name.toLower());
    7325             :     }
    7326             :     else
    7327             :     {
    7328             :         // Note that even the Status needs to be a field
    7329             :         // because we are using Apache and they expect such
    7330           0 :         http_header_t header;
    7331           0 :         header.f_header = name + ": " + v;
    7332           0 :         header.f_modes = modes;
    7333           0 :         f_header[name.toLower()] = header;
    7334             :     }
    7335           0 : }
    7336             : 
    7337             : 
    7338             : /** \brief Check whether a header is defined.
    7339             :  *
    7340             :  * This function searches for the specified name in the list of
    7341             :  * headers and returns true if it finds it.
    7342             :  *
    7343             :  * \warning
    7344             :  * Cookies are headers, but these are managed using the cookie manager
    7345             :  * which offers functions such as set_cookie(), cookie_is_defined(),
    7346             :  * and cookie().
    7347             :  *
    7348             :  * \warning
    7349             :  * Links are headers, but these are managed using the link manager
    7350             :  * which offers functions such as set_link(), link_is_defined(),
    7351             :  * and link().
    7352             :  *
    7353             :  * \param[in] name  Name of the header to check for.
    7354             :  *
    7355             :  * \return false if the header was not defined yet, true otherwise.
    7356             :  */
    7357           0 : bool snap_child::has_header(QString const & name) const
    7358             : {
    7359           0 :     return f_header.find(name.toLower()) != f_header.end();
    7360             : }
    7361             : 
    7362             : 
    7363             : /** \brief Retrieve the current value of the given header.
    7364             :  *
    7365             :  * This function returns the value of the specified header, if
    7366             :  * it exists. You may want to first call has_header() to know
    7367             :  * whether the header exists. It is not an error to get a header
    7368             :  * that was not yet defined, you get an empty string as a result.
    7369             :  *
    7370             :  * \note
    7371             :  * We only return the value of the header even though the
    7372             :  * header field name is included in the f_header value,
    7373             :  * we simply skip that information.
    7374             :  *
    7375             :  * \param[in] name  The name of the header to query.
    7376             :  *
    7377             :  * \return The value of this header, "" if undefined.
    7378             :  */
    7379           0 : QString snap_child::get_header(QString const & name) const
    7380             : {
    7381           0 :     header_map_t::const_iterator it(f_header.find(name.toLower()));
    7382           0 :     if(it == f_header.end())
    7383             :     {
    7384             :         // it is not defined
    7385           0 :         return "";
    7386             :     }
    7387             : 
    7388             :     // return the value without the field
    7389           0 :     return it->f_header.mid(name.length() + 2);
    7390             : }
    7391             : 
    7392             : 
    7393             : /** \brief Output the HTTP headers.
    7394             :  *
    7395             :  * This function prints the HTTP headers to the output.
    7396             :  *
    7397             :  * The headers are defined with a mode (a set of flags really) which
    7398             :  * can be used to tell the server when such and such header is to
    7399             :  * be output.
    7400             :  *
    7401             :  * Note that the Cookies headers are never printed by this function.
    7402             :  *
    7403             :  * \note
    7404             :  * Headers are NOT encoded in UTF-8, we output them as Latin1, this is
    7405             :  * VERY important; headers are checked at the time you do the set_header
    7406             :  * to ensure that only Latin1 characters are used.
    7407             :  *
    7408             :  * \todo
    7409             :  * Any header that a path other than the default (see the die() and
    7410             :  * page_redirect() functions) uses should not be printed by this
    7411             :  * function. At this point there is no real protection against that
    7412             :  * yet it should be protected. An idea is for us to change all those
    7413             :  * functions to use the set_header() first, then call this function
    7414             :  * because that way the set_header() will have overwritten whatever
    7415             :  * other plugins would have defined there.
    7416             :  *
    7417             :  * \param[in] modes  Print the headers for these modes.
    7418             :  */
    7419           0 : void snap_child::output_headers(header_mode_t modes)
    7420             : {
    7421             :     // The Cache-Control information are not output until here because
    7422             :     // we have a couple of settings (page and server) and it is just
    7423             :     // way to complicated to recompute the correct caches each time
    7424             :     //
    7425           0 :     set_cache_control();
    7426             : 
    7427             :     // Output the status first (we may want to order the HTTP header
    7428             :     // fields by type and output them ordered by type as defined in
    7429             :     // the HTTP reference chapter 4.2)
    7430             :     //
    7431           0 :     if(has_header(get_name(name_t::SNAP_NAME_CORE_STATUS_HEADER))
    7432           0 :     && (f_header["status"].f_modes & modes) != 0)
    7433             :     {
    7434             :         // If status is defined, it should not be 200
    7435             :         //
    7436           0 :         write((f_header["status"].f_header + "\n").toLatin1().data());
    7437             :     }
    7438             : 
    7439             :     // Now output all the other headers except the cookies
    7440             :     //
    7441           0 :     for(header_map_t::const_iterator it(f_header.begin());
    7442           0 :                                      it != f_header.end();
    7443             :                                      ++it)
    7444             :     {
    7445           0 :         if((it.value().f_modes & modes) != 0 && it.key() != "status")
    7446             :         {
    7447           0 :             write((it.value().f_header + "\n").toLatin1().data());
    7448             :         }
    7449             :     }
    7450             : 
    7451             :     // Output the links
    7452             :     //
    7453           0 :     output_http_links(modes);
    7454             : 
    7455             :     // Finally output the cookies
    7456             :     //
    7457           0 :     output_cookies();
    7458             : 
    7459             :     // Done with the headers
    7460           0 :     write("\n");
    7461           0 : }
    7462             : 
    7463             : 
    7464             : /** \brief Add the HTTP link to this snap_child.
    7465             :  *
    7466             :  * This function saves the specified link to the snap_child object.
    7467             :  *
    7468             :  * If the link was already defined, then it gets overwritten by the
    7469             :  * new link. (i.e. the "rel" parameter is used to distinguish between
    7470             :  * each link and only one can exist with a given name.)
    7471             :  *
    7472             :  * \param[in] link  The link to be added to this snap_child.
    7473             :  */
    7474           0 : void snap_child::add_http_link(http_link const & link)
    7475             : {
    7476           0 :     f_http_links[link.get_name()] = link;
    7477           0 : }
    7478             : 
    7479             : 
    7480             : /** \brief Check whether a link is defined.
    7481             :  *
    7482             :  * This function searches the list of links defined in snap_child, if
    7483             :  * defined, the function returns true.
    7484             :  *
    7485             :  * \param[in] name  The name of the link (i.e. the "rel" parameter.)
    7486             :  *
    7487             :  * \return true if the snap_child is defined.
    7488             :  */
    7489           0 : bool snap_child::http_link_is_defined(std::string const & name)
    7490             : {
    7491           0 :     return f_http_links.find(name) != f_http_links.end();
    7492             : }
    7493             : 
    7494             : 
    7495             : /** \brief Get an HTTP link.
    7496             :  *
    7497             :  * This function returns a constant reference to the named HTTP link.
    7498             :  *
    7499             :  * If the link does not exist, then the function throws. To avoid the
    7500             :  * throw, use the http_link_is_defined() first and if false avoid
    7501             :  * calling this function.
    7502             :  *
    7503             :  * \param[in] name  The name of the link to retrieve a reference to.
    7504             :  *
    7505             :  * \return A reference to the named link.
    7506             :  */
    7507           0 : http_link const & snap_child::get_http_link(std::string const & name)
    7508             : {
    7509           0 :     auto const & l(f_http_links.find(name));
    7510           0 :     if(l == f_http_links.end())
    7511             :     {
    7512           0 :         throw snap_child_exception_invalid_header_field_name(QString("link \"%1\" is not defined, you could check first using http_link_is_defined()").arg(QString::fromUtf8(name.c_str())));
    7513             :     }
    7514           0 :     return l->second;
    7515             : }
    7516             : 
    7517             : 
    7518             : /** \brief Transform the HTTP links to a header.
    7519             :  *
    7520             :  * This function generates the "Link: ..." header from the various
    7521             :  * links plugins added to snap_child.
    7522             :  *
    7523             :  * The data is directly send to the output with the write() function.
    7524             :  *
    7525             :  * \param[in] modes  Print the headers for these modes.
    7526             :  */
    7527           0 : void snap_child::output_http_links(header_mode_t modes)
    7528             : {
    7529             :     // completely empty? avoid generating strings
    7530             :     //
    7531           0 :     if(f_http_links.empty())
    7532             :     {
    7533           0 :         return;
    7534             :     }
    7535             : 
    7536             :     // geneteate the "Link: ..." string
    7537             :     //
    7538           0 :     std::string result(get_name(name_t::SNAP_NAME_CORE_HTTP_LINK_HEADER));
    7539           0 :     result += ": ";
    7540             : 
    7541           0 :     char const * sep = "";
    7542           0 :     for(auto const & l : f_http_links)
    7543             :     {
    7544           0 :         if(modes == HEADER_MODE_EVERYWHERE          // allow anything
    7545           0 :         || (modes & ~HEADER_MODE_NO_ERROR) == 0     // normal circumstances
    7546           0 :         || (l.second.get_redirect() && (modes & HEADER_MODE_REDIRECT) != 0))       // allowed in redirects
    7547             :         {
    7548           0 :             result += sep;
    7549           0 :             result += l.second.to_http_header();
    7550             : 
    7551           0 :             sep = ", ";
    7552             :         }
    7553             :     }
    7554             : 
    7555             :     // got any links?
    7556             :     // if not, then don't emit the header
    7557             :     //
    7558           0 :     if(*sep != '\0')
    7559             :     {
    7560           0 :         result += "\n";
    7561           0 :         write(result.c_str());
    7562             :     }
    7563             : }
    7564             : 
    7565             : 
    7566             : /** \brief Add a cookie.
    7567             :  *
    7568             :  * This function adds a cookie to send to the user.
    7569             :  *
    7570             :  * Contrary to most other headers, there may be more than one cookie
    7571             :  * in a reply and the set_header() does not support such. Plus cookies
    7572             :  * have a few other parameters so this function is used to save those
    7573             :  * in a separate vector of cookies.
    7574             :  *
    7575             :  * The input cookie information is copied in the vector of cookies so
    7576             :  * you can modify it.
    7577             :  *
    7578             :  * The same cookie can be redefined multiple times. Calling the function
    7579             :  * again overwrites a previous call with the same "name" parameter.
    7580             :  *
    7581             :  * \param[in] cookie_info  The cookie value, expiration, etc.
    7582             :  *
    7583             :  * \sa cookie()
    7584             :  * \sa cookie_is_defined()
    7585             :  * \sa output_cookies()
    7586             :  */
    7587           0 : void snap_child::set_cookie(http_cookie const & cookie_info)
    7588             : {
    7589           0 :     f_cookies[cookie_info.get_name()] = cookie_info;
    7590             : 
    7591             :     // TODO: the privacy-policy URI should be locked if we want this to
    7592             :     //       continue to work forever (or have a way to retrieve and
    7593             :     //       cache that value; the snapserver.conf is probably not a
    7594             :     //       good idea because you could have thousands of websites
    7595             :     //       all with a different path)
    7596           0 :     f_cookies[cookie_info.get_name()].set_comment_url(f_site_key_with_slash + "terms-and-conditions/privacy-policy");
    7597           0 : }
    7598             : 
    7599             : 
    7600             : /** \brief Ask for the cookies to not be output.
    7601             :  *
    7602             :  * When implementing an interface such as REST or SOAP you do not want
    7603             :  * to output the cookies. It would not be safe to send cookies to the
    7604             :  * browser. This function should be called by any such implementation
    7605             :  * to make sure that the cookies do not go back out.
    7606             :  */
    7607           0 : void snap_child::set_ignore_cookies()
    7608             : {
    7609           0 :     f_ignore_cookies = true;
    7610           0 : }
    7611             : 
    7612             : 
    7613             : /** \brief Output the cookies in your header.
    7614             :  *
    7615             :  * Since we generate HTTP headers in different places but still want to
    7616             :  * always generate the cookies if possible (if they are available) we
    7617             :  * have this function to add the cookies.
    7618             :  *
    7619             :  * This function directly outputs the cookies to the socket of the snap.cgi
    7620             :  * tool.
    7621             :  *
    7622             :  * \sa cookie()
    7623             :  * \sa set_cookie()
    7624             :  * \sa cookie_is_defined()
    7625             :  */
    7626           0 : void snap_child::output_cookies()
    7627             : {
    7628           0 :     if(f_ignore_cookies)
    7629             :     {
    7630           0 :         return;
    7631             :     }
    7632             : 
    7633           0 :     if(!f_cookies.isEmpty())
    7634             :     {
    7635           0 :         for(cookie_map_t::const_iterator it(f_cookies.begin());
    7636           0 :                                          it != f_cookies.end();
    7637             :                                          ++it)
    7638             :         {
    7639             :             // the to_http_header() ensures only ASCII characters
    7640             :             // are used so we can use toLatin1() below
    7641             :             //
    7642           0 :             QString cookie_header(it.value().to_http_header() + "\n");
    7643             : //SNAP_LOG_DEBUG("snap child output cookie = [")(cookie_header.toLatin1().data())("]?");
    7644             : 
    7645           0 :             write(cookie_header.toLatin1().data());
    7646             :         }
    7647             :     }
    7648             : }
    7649             : 
    7650             : 
    7651             : /** \brief Generate a unique number.
    7652             :  *
    7653             :  * This function uses a counter in a text file to generate a unique number.
    7654             :  * The file is a 64 bit long number (binary) which gets locked to ensure
    7655             :  * that the number coming out is unique.
    7656             :  *
    7657             :  * The resulting number is composed of the server name a dash and the
    7658             :  * unique number generated from the unique number file.
    7659             :  *
    7660             :  * At this point it is not expected that we'd ever run out of unique
    7661             :  * numbers. 2^64 per server is a really large number. However, you do
    7662             :  * want to limit calls as much as possible (if you can reuse the same
    7663             :  * number or check all possibilities that could cause an error before
    7664             :  * getting the unique numbers so as to avoid wasting too many of them.)
    7665             :  *
    7666             :  * The server name is expected to be a unique name defined in the settings.
    7667             :  * (the .conf file for the server.)
    7668             :  *
    7669             :  * \todo
    7670             :  * All the servers in a given realm should all be given a unique name and
    7671             :  * information about the other servers (i.e. at least the address of one
    7672             :  * other server) so that way all the servers can communicate and make sure
    7673             :  * that their name is indeed unique.
    7674             :  *
    7675             :  * \return A string with \<server name\>-\<unique number\>
    7676             :  */
    7677           0 : QString snap_child::get_unique_number()
    7678             : {
    7679           0 :     server::pointer_t server(get_server());
    7680           0 :     QString const data_path(get_data_path());
    7681             : 
    7682           0 :     quint64 c(0);
    7683             :     {
    7684           0 :         const QString name(data_path + "/counter.u64");
    7685           0 :         QLockFile counter(name);
    7686           0 :         if(!counter.open(QIODevice::ReadWrite))
    7687             :         {
    7688           0 :             throw snap_child_exception_unique_number_error("count not open counter file \"" + name + "\"");
    7689             :         }
    7690             :         // the very first time the size is zero (empty)
    7691           0 :         if(counter.size() != 0)
    7692             :         {
    7693           0 :             if(counter.read(reinterpret_cast<char *>(&c), sizeof(c)) != sizeof(c))
    7694             :             {
    7695           0 :                 throw snap_child_exception_unique_number_error("count not read the counter file \"" + name + "\"");
    7696             :             }
    7697             :         }
    7698           0 :         ++c;
    7699           0 :         counter.reset();
    7700           0 :         if(counter.write(reinterpret_cast<char *>(&c), sizeof(c)) != sizeof(c))
    7701             :         {
    7702           0 :             throw snap_child_exception_unique_number_error("count not write to the counter file \"" + name + "\"");
    7703             :         }
    7704             :         // close the file now; we do not want to hold the file for too long
    7705             :     }
    7706           0 :     return QString("%1-%2").arg(server->get_server_name().c_str()).arg(c);
    7707             : }
    7708             : 
    7709             : /** \brief Initialize the plugins.
    7710             :  *
    7711             :  * Each site may make use of a different set of plugins. This function
    7712             :  * gathers the list of available plugins and loads them as expected.
    7713             :  *
    7714             :  * The bare minimum is hard coded here in order to ensure that minimum
    7715             :  * functionality of a website. At this time, this list is:
    7716             :  *
    7717             :  * \li path
    7718             :  * \li filter
    7719             :  * \li robotstxt
    7720             :  *
    7721             :  * \param[in] add_defaults  Whether the default Snap! plugins are to be
    7722             :  *                          added to the list of plugins (important for
    7723             :  *                          the Snap! server itself, not so much for other
    7724             :  *                          servers using plugins.)
    7725             :  */
    7726           0 : snap_string_list snap_child::init_plugins(bool const add_defaults, QString const & introducer)
    7727             : {
    7728           0 :     server::pointer_t server(get_server());
    7729             : 
    7730             :     // load the plugins for this website
    7731             :     //
    7732           0 :     bool need_cleanup(true);
    7733           0 :     QString site_plugins(server->get_parameter(get_name(name_t::SNAP_NAME_CORE_PARAM_PLUGINS))); // forced by .conf?
    7734           0 :     if(site_plugins.isEmpty())
    7735             :     {
    7736             :         // maybe user defined his list of plugins in his website
    7737             :         //
    7738           0 :         libdbproxy::value plugins(get_site_parameter(get_name(name_t::SNAP_NAME_CORE_PLUGINS)));
    7739           0 :         site_plugins = plugins.stringValue();
    7740           0 :         if(site_plugins.isEmpty())
    7741             :         {
    7742             :             // if the list of plugins is empty in the site parameters
    7743             :             // then get the default from the server configuration
    7744             :             //
    7745           0 :             site_plugins = server->get_parameter(get_name(name_t::SNAP_NAME_CORE_PARAM_DEFAULT_PLUGINS));
    7746             :         }
    7747             :         else
    7748             :         {
    7749             :             // we assume that the list of plugins in the database is already
    7750             :             // cleaned up and thus avoid an extra loop (see below)
    7751             :             //
    7752           0 :             need_cleanup = false;
    7753             :         }
    7754             :     }
    7755           0 :     snap_string_list list_of_plugins(site_plugins.split(',', QString::SkipEmptyParts));
    7756             : 
    7757             :     // clean up the list
    7758             :     //
    7759           0 :     if(need_cleanup)
    7760             :     {
    7761           0 :         if(introducer.isEmpty())
    7762             :         {
    7763           0 :             for(int i(0); i < list_of_plugins.length(); ++i)
    7764             :             {
    7765           0 :                 list_of_plugins[i] = list_of_plugins[i].trimmed();
    7766           0 :                 if(list_of_plugins.at(i).isEmpty())
    7767             :                 {
    7768             :                     // remove parts that the trimmed() rendered empty
    7769             :                     //
    7770           0 :                     list_of_plugins.removeAt(i);
    7771           0 :                     --i;
    7772             :                 }
    7773             :             }
    7774             :         }
    7775             :         else
    7776             :         {
    7777           0 :             for(int i(0); i < list_of_plugins.length(); ++i)
    7778             :             {
    7779           0 :                 list_of_plugins[i] = list_of_plugins[i].trimmed();
    7780           0 :                 if(list_of_plugins.at(i).isEmpty())
    7781             :                 {
    7782             :                     // remove parts that the trimmed() rendered empty
    7783             :                     //
    7784           0 :                     list_of_plugins.removeAt(i);
    7785           0 :                     --i;
    7786             :                 }
    7787             :                 else
    7788             :                 {
    7789           0 :                     list_of_plugins[i] = introducer + "_" + list_of_plugins[i];
    7790             :                 }
    7791             :             }
    7792             :         }
    7793             :     }
    7794             : 
    7795             :     // ensure a certain minimum number of plugins
    7796             :     //
    7797           0 :     if(add_defaults)
    7798             :     {
    7799           0 :         for(size_t i(0); i < sizeof(g_minimum_plugins) / sizeof(g_minimum_plugins[0]); ++i)
    7800             :         {
    7801           0 :             if(!list_of_plugins.contains(g_minimum_plugins[i]))
    7802             :             {
    7803           0 :                 list_of_plugins << g_minimum_plugins[i];
    7804             :             }
    7805             :         }
    7806             :     }
    7807             : 
    7808             :     // load the plugins
    7809             :     //
    7810           0 :     QString const plugins_path( server->get_parameter("plugins_path") );
    7811           0 :     if( plugins_path.isEmpty() )
    7812             :     {
    7813             :         // Sanity check
    7814             :         //
    7815           0 :         die( http_code_t::HTTP_CODE_SERVICE_UNAVAILABLE
    7816             :            , "Plugin path not configured"
    7817             :            , "Server cannot find any plugins because the path is not properly configured."
    7818             :            , "An error occured loading the server plugins (plugins_path parameter in snapserver.conf is undefined)."
    7819             :            );
    7820           0 :         NOTREACHED();
    7821             :     }
    7822             : 
    7823           0 :     if(!snap::plugins::load(plugins_path, this, std::static_pointer_cast<snap::plugins::plugin>(server), list_of_plugins, introducer))
    7824             :     {
    7825           0 :         die( http_code_t::HTTP_CODE_SERVICE_UNAVAILABLE
    7826             :            , "Plugin Unavailable"
    7827             :            , "Server encountered problems with its plugins."
    7828             :            , "An error occurred while loading the server plugins. See other errors from the plugin implementation for details."
    7829             :            );
    7830           0 :         NOTREACHED();
    7831             :     }
    7832             :     // at this point each plugin was allocated through their factory
    7833             :     // but they are not really usable yet because we did not initialize them
    7834             : 
    7835             :     // now boot the plugin system (send signals)
    7836             :     //
    7837           0 :     server->init();
    7838             : 
    7839           0 :     return list_of_plugins;
    7840             : }
    7841             : 
    7842             : 
    7843             : /** \brief Run all the updates as required.
    7844             :  *
    7845             :  * This function checks when the updates were last run. If never, then it
    7846             :  * runs the update immediately. Otherwise, it waits at least 10 minutes
    7847             :  * between running again to avoid overloading the server. We may increase
    7848             :  * (i.e. wait more than 10 minutes) that amount of time as we get a better
    7849             :  * feel of the necessity.
    7850             :  *
    7851             :  * The update is done by going through all the modules and checking their
    7852             :  * modification date and time. If newer than what was registered for
    7853             :  * them so far, then we call their do_update() function. When it never
    7854             :  * ran, the modification date and time is always seen as \em newer and
    7855             :  * thus all updates are run.
    7856             :  *
    7857             :  * \todo
    7858             :  * We may want to look into a way to "install" a plugin which would have
    7859             :  * the side effect of setting a flag requesting an update instead of
    7860             :  * relying on the plugin .so file modification date and other such
    7861             :  * tricks. A clear signal sent via a command line tool or directly
    7862             :  * from a website could be a lot more effective.
    7863             :  *
    7864             :  * \todo
    7865             :  * The current implementation makes use of a "plugin threshold" which
    7866             :  * is wrong because when you update plugin A with a threshold P, then
    7867             :  * later update plugin B with a threshold Q and Q < P, plugin B does
    7868             :  * not get updated (we'd have to test to prove this, but from what I
    7869             :  * can see in the algorithm that specific case is not assume to happen
    7870             :  * because we're expected to upgrade all the plugins at the same time
    7871             :  * which unfortunately is never the case if you keep the server running
    7872             :  * while upgrading your installation--which is not really recommended,
    7873             :  * but I'm sure we'll be doing that all the time...) At this point I
    7874             :  * removed the test so whether Q = P, Q < P or Q > P has no more
    7875             :  * effect on the update process.
    7876             :  *
    7877             :  * \param[in] list_of_plugins  The list of plugin names that were loaded
    7878             :  *                             for this run.
    7879             :  */
    7880           0 : void snap_child::update_plugins(snap_string_list const & list_of_plugins)
    7881             : {
    7882           0 :     SNAP_LOG_INFO("update_plugins() called with \"")(list_of_plugins.join(", "))("\"");
    7883             : 
    7884             :     // system updates run at most once every 10 minutes
    7885           0 :     QString const core_last_updated(get_name(name_t::SNAP_NAME_CORE_LAST_UPDATED));
    7886           0 :     QString const core_last_dynamic_update(get_name(name_t::SNAP_NAME_CORE_LAST_DYNAMIC_UPDATE));
    7887           0 :     libdbproxy::value last_updated(get_site_parameter(core_last_updated));
    7888           0 :     if(last_updated.nullValue())
    7889             :     {
    7890             :         // use an "old" date (631152000)
    7891           0 :         last_updated.setInt64Value(SNAP_UNIX_TIMESTAMP(1990, 1, 1, 0, 0, 0) * 1000000LL);
    7892             :     }
    7893             :     //int64_t const last_update_timestamp(last_updated.int64Value());
    7894             :     // 10 min. elapsed since last update?
    7895             :     //if(is_debug() // force update in debug mode so we don't have to wait 10 min.!
    7896             :     //|| f_start_date - static_cast<int64_t>(last_update_timestamp) > static_cast<int64_t>(10 * 60 * 1000000))
    7897             :     {
    7898             :         // this can be called more than once in debug mode whenever multiple
    7899             :         // files are being loaded for a page being accessed (i.e. the main
    7900             :         // page and then the .js, .css, .jpg, etc.)
    7901             :         //
    7902             :         // if is_debug() returns false, it should be useless unless the
    7903             :         // process takes over 10 minutes
    7904           0 :         snap_lock lock(QString("%1#snap-child-updating").arg(get_site_key_with_slash()), 60 * 60); // lock for up to 1h
    7905             : 
    7906             :         // set the state to "updating" if it currently is "ready"
    7907           0 :         libdbproxy::value state(get_site_parameter(get_name(name_t::SNAP_NAME_CORE_SITE_STATE)));
    7908           0 :         if(state.nullValue())
    7909             :         {
    7910             :             // set the state to "initializing if it is undefined
    7911             :             //
    7912           0 :             state.setStringValue("initializing");
    7913           0 :             set_site_parameter(get_name(name_t::SNAP_NAME_CORE_SITE_STATE), state);
    7914             :         }
    7915           0 :         else if(state.stringValue() == "ready"
    7916           0 :              || state.stringValue() == "updating"       // check this one in case a previous update failed
    7917           0 :              || state.stringValue() == "initializing")  // check this one in case a previous initialization failed
    7918             :         {
    7919             :             // set the state to "initializing if it is undefined
    7920             :             //
    7921           0 :             state.setStringValue("updating");
    7922           0 :             set_site_parameter(get_name(name_t::SNAP_NAME_CORE_SITE_STATE), state);
    7923             : 
    7924             :             // in this case we want existing user requests to have a chance
    7925             :             // to end before we proceed with the update
    7926             :             //
    7927             :             // right now, this is a really ugly way of doing things we
    7928             :             // should instead be told once the last user left
    7929             :             //
    7930           0 :             sleep(10); // yeah... that's a hack which should work 99% of the time though
    7931             :         }
    7932             :         else
    7933             :         {
    7934             :             // unknown state... what to do? what to do?
    7935             :             //
    7936           0 :             SNAP_LOG_ERROR("Updating website failed as we do not understand its current state: \"")(state.stringValue())("\".");
    7937           0 :             return;
    7938             :         }
    7939             : 
    7940             :         // we do not allow a null value in the core::site_name field,
    7941             :         // so we set it on initialization, user can replace the name
    7942             :         // later from the UI
    7943             :         //
    7944           0 :         libdbproxy::value site_name(get_site_parameter(get_name(name_t::SNAP_NAME_CORE_SITE_NAME)));
    7945           0 :         if(site_name.nullValue())
    7946             :         {
    7947           0 :             site_name.setStringValue("Website Name");
    7948           0 :             set_site_parameter(get_name(name_t::SNAP_NAME_CORE_SITE_NAME), site_name);
    7949             :         }
    7950             : 
    7951             :         // save that last time we checked for an update
    7952             :         //
    7953           0 :         last_updated.setInt64Value(f_start_date);
    7954           0 :         QString const core_plugin_threshold(get_name(name_t::SNAP_NAME_CORE_PLUGIN_THRESHOLD));
    7955           0 :         set_site_parameter(core_last_updated, last_updated);
    7956           0 :         libdbproxy::value threshold(get_site_parameter(core_plugin_threshold));
    7957           0 :         if(threshold.nullValue())
    7958             :         {
    7959             :             // same old date...
    7960           0 :             threshold.setInt64Value(SNAP_UNIX_TIMESTAMP(1990, 1, 1, 0, 0, 0) * 1000000LL);
    7961             :         }
    7962           0 :         int64_t const plugin_threshold(threshold.int64Value());
    7963           0 :         int64_t new_plugin_threshold(plugin_threshold);
    7964             : 
    7965             :         // first run through the plugins to know whether one or more
    7966             :         // has changed since the last website update
    7967           0 :         for(snap_string_list::const_iterator it(list_of_plugins.begin());
    7968           0 :                 it != list_of_plugins.end();
    7969             :                 ++it)
    7970             :         {
    7971           0 :             QString const plugin_name(*it);
    7972           0 :             plugins::plugin * p(plugins::get_plugin(plugin_name));
    7973           0 :             if(p != nullptr)
    7974             :             {
    7975           0 :                 SNAP_LOG_INFO("update_plugins() called with \"")(plugin_name)("\"");
    7976           0 :                 trace(QString("Updating plugin \"%1\"\n").arg(plugin_name));
    7977             : 
    7978             :                 // the plugin changed, we want to call do_update() on it!
    7979           0 :                 if(p->last_modification() > new_plugin_threshold)
    7980             :                 {
    7981           0 :                     new_plugin_threshold = p->last_modification();
    7982             :                 }
    7983             :                 // run the updates as required
    7984             :                 // we have a date/time for each plugin since each has
    7985             :                 // its own list of date/time checks
    7986           0 :                 QString const specific_param_name(QString("%1::%2").arg(core_last_updated).arg(plugin_name));
    7987           0 :                 libdbproxy::value specific_last_updated(get_site_parameter(specific_param_name));
    7988           0 :                 int64_t const old_last_updated(specific_last_updated.safeInt64Value());
    7989           0 :                 if(specific_last_updated.nullValue())
    7990             :                 {
    7991             :                     // use an "old" date (631152000)
    7992           0 :                     specific_last_updated.setInt64Value(SNAP_UNIX_TIMESTAMP(1990, 1, 1, 0, 0, 0) * 1000000LL);
    7993             :                 }
    7994             :                 try
    7995             :                 {
    7996           0 :                     specific_last_updated.setInt64Value(p->do_update(old_last_updated));
    7997             : 
    7998             :                     // avoid the database access if the value did not change
    7999             :                     //
    8000           0 :                     if(specific_last_updated.int64Value() != old_last_updated)
    8001             :                     {
    8002           0 :                         set_site_parameter(specific_param_name, specific_last_updated);
    8003             :                     }
    8004             :                 }
    8005           0 :                 catch(std::exception const & e)
    8006             :                 {
    8007           0 :                     SNAP_LOG_ERROR("Updating ")(plugin_name)(" failed with an exception: ")(e.what());
    8008             :                 }
    8009             :             }
    8010             :         }
    8011             : 
    8012             :         // this finishes the content.xml updates
    8013           0 :         SNAP_LOG_INFO("update_plugins() finalize the content.xml (a.k.a. static) update.");
    8014           0 :         finish_update();
    8015             : 
    8016             :         // now allow plugins to have a more dynamic set of updates
    8017           0 :         for(snap_string_list::const_iterator it(list_of_plugins.begin());
    8018           0 :                 it != list_of_plugins.end();
    8019             :                 ++it)
    8020             :         {
    8021           0 :             QString const plugin_name(*it);
    8022           0 :             plugins::plugin * p(plugins::get_plugin(plugin_name));
    8023           0 :             if(p != nullptr)
    8024             :             {
    8025           0 :                 trace(QString("Dynamically update plugin \"%1\"\n").arg(plugin_name));
    8026             : 
    8027             :                 // run the updates as required
    8028             :                 // we have a date/time for each plugin since each has
    8029             :                 // its own list of date/time checks
    8030           0 :                 QString const specific_param_name(QString("%1::%2").arg(core_last_dynamic_update).arg(plugin_name));
    8031           0 :                 libdbproxy::value specific_last_updated(get_site_parameter(specific_param_name));
    8032           0 :                 int64_t const old_last_updated(specific_last_updated.safeInt64Value());
    8033           0 :                 if(specific_last_updated.nullValue())
    8034             :                 {
    8035             :                     // use an "old" date (631152000)
    8036           0 :                     specific_last_updated.setInt64Value(SNAP_UNIX_TIMESTAMP(1990, 1, 1, 0, 0, 0) * 1000000LL);
    8037             :                 }
    8038             :                 // IMPORTANT: Note that we save the newest date found in the
    8039             :                 //            do_update() to make 100% sure we catch all the
    8040             :                 //            updates every time (using "now" would often mean
    8041             :                 //            missing many updates!)
    8042             :                 try
    8043             :                 {
    8044           0 :                     specific_last_updated.setInt64Value(p->do_dynamic_update(old_last_updated));
    8045             : 
    8046             :                     // avoid the database access if the value did not change
    8047             :                     //
    8048           0 :                     if(specific_last_updated.int64Value() != old_last_updated)
    8049             :                     {
    8050           0 :                         set_site_parameter(specific_param_name, specific_last_updated);
    8051             :                     }
    8052             :                 }
    8053           0 :                 catch(std::exception const & e)
    8054             :                 {
    8055           0 :                     SNAP_LOG_ERROR("Dynamically updating ")(plugin_name)(" failed with an exception: ")(e.what());
    8056             :                 }
    8057             :             }
    8058             :         }
    8059             : 
    8060             :         // save the new threshold if larger
    8061             :         //
    8062           0 :         if(new_plugin_threshold > plugin_threshold)
    8063             :         {
    8064           0 :             set_site_parameter(core_plugin_threshold, new_plugin_threshold);
    8065             :         }
    8066             : 
    8067             :         // always mark when the site was last updated
    8068             :         //
    8069             :         // get the date right now, DO NOT RESET START DATE (f_start_date)
    8070             :         //
    8071           0 :         set_site_parameter(get_name(name_t::SNAP_NAME_CORE_SITE_READY), get_current_date());
    8072             : 
    8073             :         // change the state while still locked
    8074             :         //
    8075           0 :         state.setStringValue("ready");
    8076           0 :         set_site_parameter(get_name(name_t::SNAP_NAME_CORE_SITE_STATE), state);
    8077             :     }
    8078             : }
    8079             : 
    8080             : 
    8081             : /** \brief After adding content, call this function to save it.
    8082             :  *
    8083             :  * When we add content with the add_xml() and add_xml_dom() functions,
    8084             :  * the snap_child object records the fact and is then capable of knowing
    8085             :  * that it needs to be saved.
    8086             :  *
    8087             :  * All the adds are expected to be called before the data gets saved
    8088             :  * which allows us to not have to know in which order the XML files need
    8089             :  * to be to add all of their contents at once. In other words, the save
    8090             :  * function will automatically be capable of figuring out the right order
    8091             :  * if it has all the data, hence this function getting called last.
    8092             :  *
    8093             :  * \note
    8094             :  * This was separated so the layouts can be added at any time since we
    8095             :  * do not activate all the layouts automatically in all of our users'
    8096             :  * websites.
    8097             :  */
    8098           0 : void snap_child::finish_update()
    8099             : {
    8100             :     // if content was prepared for the database, save it now
    8101           0 :     if(f_new_content)
    8102             :     {
    8103           0 :         f_new_content = false;
    8104           0 :         server::pointer_t server(get_server());
    8105           0 :         trace("Save content in database.\n");
    8106           0 :         server->save_content();
    8107           0 :         trace("Initialization content now saved in database.\n");
    8108             :     }
    8109           0 : }
    8110             : 
    8111             : 
    8112             : /** \brief Called whenever a plugin prepares some content to the database.
    8113             :  *
    8114             :  * This function is called by the content plugin whenever one of its
    8115             :  * add_...() function is called. This way the child knows that it has
    8116             :  * to request the content to save the resulting content.
    8117             :  *
    8118             :  * The flag is first checked after the updates are run and the save is
    8119             :  * called then. The check is done again at the end of the execute function
    8120             :  * just in case some dynamic data was added while we were running.
    8121             :  */
    8122           0 : void snap_child::new_content()
    8123             : {
    8124           0 :     f_new_content = true;
    8125           0 : }
    8126             : 
    8127             : 
    8128             : /** \brief Canonalize a path or URL for this plugin.
    8129             :  *
    8130             :  * This function is used to canonicalize the paths used to check
    8131             :  * URLs. This is used against the paths offered by other plugins
    8132             :  * and the paths arriving from the HTTP server. This way, we know
    8133             :  * that two paths will match 1 to 1.
    8134             :  *
    8135             :  * The canonicalization is done in place.
    8136             :  *
    8137             :  * Note that the canonicalization needs to occur before the
    8138             :  * regular expresions are checked. Also, internal paths that
    8139             :  * include regular expressions are not getting canonicalized
    8140             :  * since we may otherwise break the regular expression
    8141             :  * (i.e. unwillingly remove periods and slashes.)
    8142             :  * This can explain why one of your paths doesn't work right.
    8143             :  *
    8144             :  * The function is really fast if the path is already canonicalized.
    8145             :  *
    8146             :  * \note
    8147             :  * There is one drawback with "fixing" the URL from the user.
    8148             :  * Two paths that look different will return the same page.
    8149             :  * Instead we probably want to return an error (505 or 404
    8150             :  * or 302.) This may be an dynamic setting too.
    8151             :  *
    8152             :  * \note
    8153             :  * The remove() function on a QString is faster than most other
    8154             :  * options because it is directly applied to the string data.
    8155             :  *
    8156             :  * \param[in,out] path  The path to canonicalize.
    8157             :  */
    8158           0 : void snap_child::canonicalize_path(QString & path)
    8159             : {
    8160             :     // we get the length on every loop because it could be reduced!
    8161           0 :     int i(0);
    8162           0 :     while(i < path.length())
    8163             :     {
    8164           0 :         switch(path[i].cell())
    8165             :         {
    8166           0 :         case '\\':
    8167           0 :             path[i] = '/';
    8168           0 :             break;
    8169             : 
    8170           0 :         case ' ':
    8171             :         case '+':
    8172             :         //case '_': -- this should probably be a flag?
    8173           0 :             path[i] = '-';
    8174           0 :             break;
    8175             : 
    8176           0 :         default:
    8177             :             // other characters are kept as is
    8178           0 :             break;
    8179             : 
    8180             :         }
    8181             :         // here we do not have to check for \ since we just replaced it with /
    8182           0 :         if(i == 0 && (path[0].cell() == '.' || path[0].cell() == '/' /*|| path[0].cell() == '\\'*/))
    8183             :         {
    8184           0 :             do
    8185             :             {
    8186           0 :                 path.remove(0, 1);
    8187             :             }
    8188           0 :             while(!path.isEmpty() && (path[0].cell() == '.' || path[0].cell() == '/' || path[0].cell() == '\\'));
    8189             :             // however, in the while we do since later characters were not yet
    8190             :             // modified to just '/'
    8191             :         }
    8192           0 :         else if(path[i].cell()  == '/' && i + 1 < path.length())
    8193             :         {
    8194           0 :             if(path[i + 1].cell() == '/')
    8195             :             {
    8196             :                 // remove double '/' in filename
    8197           0 :                 path.remove(i + 1, 1);
    8198             :             }
    8199           0 :             else if(path[i + 1].cell() == '.')
    8200             :             {
    8201             :                 // Unix hidden files are forbidden (., .. and .<name>)
    8202             :                 // (here we remove 1 period, on next loop we may remove others)
    8203           0 :                 path.remove(i + 1, 1);
    8204             :             }
    8205             :             else
    8206             :             {
    8207           0 :                 ++i;
    8208             :             }
    8209             :         }
    8210           0 :         else if((path[i].cell() == '.' || path[i].cell() == '-' || path[i].cell() == '/') && i + 1 >= path.length())
    8211             :         {
    8212             :             // Filename cannot end with a period, dash (space,) or slash
    8213           0 :             path.remove(i, 1);
    8214             :         }
    8215             :         else
    8216             :         {
    8217           0 :             ++i;
    8218             :         }
    8219             :     }
    8220           0 : }
    8221             : 
    8222             : 
    8223             : /** \brief We're ready to execute the page, do so.
    8224             :  *
    8225             :  * This time we're ready to execute the page the user is trying
    8226             :  * to access.
    8227             :  *
    8228             :  * The function first prepares the HTTP request which includes
    8229             :  * setting up default headers and the output buffer.
    8230             :  *
    8231             :  * Note that by default we expect text/html in the output. If a
    8232             :  * different type of data is being processed, you are responsible
    8233             :  * for changing the Content-type field.
    8234             :  *
    8235             :  * \todo
    8236             :  * Take the Origin header in account. If it is not the right
    8237             :  * origin, especially for log in, registration, and related
    8238             :  * parges, then we may want to generate an error.
    8239             :  */
    8240           0 : void snap_child::execute()
    8241             : {
    8242             :     // prepare the output buffer
    8243             :     // reserver 64Kb at once to avoid many tiny realloc()
    8244             :     //
    8245           0 :     f_output.buffer().reserve(64 * 1024);
    8246           0 :     f_output.open(QIODevice::ReadWrite);
    8247             : 
    8248             :     // TODO if the client says HTTP/1.0 and offers an Upgrade of 1.1, then
    8249             :     //      we should force switch to 1.1 with a 101 response about here
    8250             :     // TBD Apache may already take care of such things
    8251             :     // TBD It may also be used to switch between HTTP and SHTTP
    8252             :     //
    8253             :     //if(snapenv("SERVER_PROTOCOL").toUpper() == "HTTP/1.0")
    8254             :     //{
    8255             :     //    // if the Upgrade header was specified check whether it includes
    8256             :     //    // HTTP/1.1 as one of the supported protocols
    8257             :     //    // Note: we may want to make use of the http_string::WeightedHttpString
    8258             :     //    //       object to split that client parameter
    8259             :     //    if(snapenv("?REQUEST_UPGRADE?").simplified().replace(QRegExp("[ \t]?,[ \t]?"), ",").toLower().split(',', QString::SkipEmptyParts).contains("HTTP/1.1"))
    8260             :     //    {
    8261             :     //        set_header("Status", "HTTP/1.1 101 Switching Protocols");
    8262             :     //    }
    8263             :     //}
    8264             : 
    8265             :     // TODO: Check the cache request status from the client, if not defined
    8266             :     //       or set to "max-age=0" or some other such value, then check whether
    8267             :     //       the current page is cached and can safely be resent to the client
    8268             :     //       (i.e. a public page without form...) if so send the cached version
    8269             :     //       which will allow us to avoid all the processing.
    8270             :     //
    8271             :     // Note: the cached versions are saved really only if the page is
    8272             :     //       public, mostly non-dynamic, and has no forms other than Search
    8273             :     //       and similar...
    8274             : 
    8275             :     // prepare the default headers
    8276             :     // Status is set to HTTP/1.1 or 1.0 depending on the incoming protocol
    8277             :     // DO NOT PUT A STATUS OF 200 FOR FastCGI TAKES CARE OF IT
    8278             :     // Sending a status of 200 to Apache results in a status of 500 Internal Server Error
    8279             :     //
    8280             :     //set_header("Status", QString("%1 200 OK").arg(snapenv("SERVER_PROTOCOL")));
    8281             : 
    8282             :     // Normally Apache overwrites this information
    8283             :     //
    8284           0 :     set_header("Server", "Snap! C++", HEADER_MODE_EVERYWHERE);
    8285             : 
    8286             :     // The Date field is added by Apache automatically
    8287             :     // adding it here generates a 500 Internal Server Error
    8288             :     //
    8289             :     //QDateTime date(QDateTime().toUTC());
    8290             :     //date.setTime_t(f_start_date / 1000000); // micro-seconds
    8291             :     //set_header("Date", date.toString("ddd, dd MMM yyyy hh:mm:ss' GMT'"));
    8292             : 
    8293             :     // cache controls are now defined in f_server_cache_control
    8294             :     // and f_page_cache_control; the defaults are not exactly what
    8295             :     // we want, so we change them in the f_page_cache_control
    8296             :     //
    8297             :     //set_cache_control(0, false, true);
    8298           0 :     f_page_cache_control.set_no_store(true);
    8299           0 :     f_page_cache_control.set_must_revalidate(true);
    8300             : 
    8301             :     // We are snapwebsites
    8302             :     //
    8303           0 :     set_header(get_name(name_t::SNAP_NAME_CORE_X_POWERED_BY_HEADER), "snapwebsites", HEADER_MODE_EVERYWHERE);
    8304             : 
    8305             :     // By default we expect [X]HTML 5 in the output
    8306             :     //
    8307           0 :     set_header(get_name(name_t::SNAP_NAME_CORE_CONTENT_TYPE_HEADER), "text/html; charset=utf-8", HEADER_MODE_EVERYWHERE);
    8308             : 
    8309             :     // XXX I moved that up here from just before sending the output because
    8310             :     //     it seems that all answers should use this flag (because even pages
    8311             :     //     that represent errors may end up reusing the same connection.)
    8312             :     //
    8313           0 :     QString const connection(snapenv("HTTP_CONNECTION"));
    8314             : //std::cout << "HTTP_CONNECTION=[" << connection << "]\n";
    8315           0 :     if(connection.toLower() == "keep-alive")
    8316             :     {
    8317           0 :         set_header("Connection", "keep-alive");
    8318             :     }
    8319             :     else
    8320             :     {
    8321           0 :         set_header("Connection", "close");
    8322             :     }
    8323             : 
    8324             :     // Let the caches know that the cookie changes all the time
    8325             :     // (the content is likely to change too, but it could still be cached)
    8326             :     // TBD -- I'm not entirely sure that this is smart; another default is
    8327             :     //        to use "Vary: *" so all fields are considered as varying.
    8328             :     //set_header("Vary", "Cookie");
    8329             : 
    8330           0 :     if(f_uri.protocol() == "https")
    8331             :     {
    8332             :         // this is used by different load balancers as an indication that
    8333             :         // the request is secure
    8334             :         //
    8335           0 :         set_header("Front-End-Https", "on", HEADER_MODE_EVERYWHERE);
    8336             :     }
    8337             : 
    8338             :     // give a chance to the system to use cookies such as the
    8339             :     // cookie used to mark a user as logged in to kick in early
    8340             :     //
    8341           0 :     server::pointer_t server(get_server());
    8342           0 :     server->process_cookies();
    8343             : 
    8344             :     // the cookie handling may generate an immediate response in which case
    8345             :     // we want to exit ASAP and not execute anything
    8346             :     //
    8347           0 :     if(f_output.buffer().size() == 0)
    8348             :     {
    8349             :         class attach_session
    8350             :         {
    8351             :         public:
    8352           0 :             attach_session(server::pointer_t server)
    8353           0 :                 : f_server(server)
    8354             :             {
    8355             :                 // let plugins detach whatever data they attached to the user session
    8356             :                 // (note: nothing to do with the fork() which was called a while back)
    8357             :                 //
    8358           0 :                 server->detach_from_session();
    8359           0 :             }
    8360             : 
    8361           0 :             ~attach_session()
    8362           0 :             {
    8363             :                 // TODO: look into having this call to the exit() function too
    8364             :                 //       since it should be called no matter what--at that
    8365             :                 //       point we will need a flag because we can't call the
    8366             :                 //       function more than once
    8367             :                 //
    8368             :                 // now that execution is over, we want to re-attach whatever did
    8369             :                 // not make it in this session (i.e. a message that was posted
    8370             :                 // after messages were added to the current page, or this page
    8371             :                 // did not make use of the messages that were detached earlier)
    8372             :                 //
    8373           0 :                 f_server->attach_to_session();
    8374           0 :             }
    8375             : 
    8376             :         private:
    8377             :             server::pointer_t   f_server = server::pointer_t();
    8378             :         };
    8379             : 
    8380             :         // detach/re-attach the session using this RAII class within
    8381             :         // the following block
    8382             :         {
    8383           0 :             attach_session raii_session(server);
    8384             : 
    8385           0 :             f_ready = true;
    8386             : 
    8387             :             // generate the output
    8388             :             //
    8389             :             // on_execute() is defined in the path plugin which retrieves the
    8390             :             // path::primary_owner of the content that match f_uri.path() and
    8391             :             // then calls the corresponding on_path_execute() function of that
    8392             :             // primary owner
    8393             :             //
    8394           0 :             server->execute(f_uri.path());
    8395             :         }
    8396             : 
    8397           0 :         if(f_output.buffer().size() == 0)
    8398             :         {
    8399             :             // somehow nothing was output...
    8400             :             // (that should not happen because the path::on_execute() function
    8401             :             // checks and generates a Page Not Found on empty content)
    8402             :             //
    8403           0 :             die(http_code_t::HTTP_CODE_NOT_FOUND, "Page Empty",
    8404             :                 "Somehow this page could not be generated.",
    8405             :                 "the execute() command ran but the output is empty (this is never correct with HTML data, it could be with text/plain responses though)");
    8406           0 :             NOTREACHED();
    8407             :         }
    8408             :     }
    8409             : 
    8410           0 :     if(f_is_being_initialized)
    8411             :     {
    8412           0 :         trace("Initialization succeeded.\n");
    8413           0 :         trace("#END\n");
    8414             : 
    8415             :         // TODO: should we also sent the headers and output buffer?
    8416             :         //       we could send that righ after the #END mark
    8417             :         //       (this could be done by removing the if/else so the
    8418             :         //       output_result() function gets called either way!)
    8419             :     }
    8420             :     else
    8421             :     {
    8422             :         // created a page, output it now
    8423             :         //
    8424           0 :         output_result(HEADER_MODE_NO_ERROR, f_output.buffer());
    8425             :     }
    8426           0 : }
    8427             : 
    8428             : 
    8429             : /** \brief Output the resulting buffer.
    8430             :  *
    8431             :  * This function takes all the data generated by the different processes
    8432             :  * run and generates the final output.
    8433             :  *
    8434             :  * It must be used by all the functions that are ready to send their
    8435             :  * result to the client (assuming the default scheme cannot be used,
    8436             :  * somehow.) This is important because this function makes sure to:
    8437             :  *
    8438             :  * \li Signal all plugins of the event,
    8439             :  * \li Only send the headers if the method was HEAD,
    8440             :  * \li Answer AJAX requests as expected.
    8441             :  *
    8442             :  * \param[in] modes  Print the headers for these modes.
    8443             :  * \param[in] output_data  The output being process.
    8444             :  */
    8445           0 : void snap_child::output_result(header_mode_t modes, QByteArray output_data)
    8446             : {
    8447             :     // give plugins a chance to tweak the output one more time
    8448           0 :     server::pointer_t server(get_server());
    8449           0 :     server->output_result(f_uri.path(), output_data);
    8450             : 
    8451             :     // Handling the compression has to be done before defining the
    8452             :     // Content-Length header since that represents the compressed
    8453             :     // data and not the full length
    8454             : 
    8455             :     // TODO make use of the pre-canonicalized compression information
    8456             :     //      (but also look into the identity handling... at this point
    8457             :     //      we do not generate a 406 if identity is defined and set to
    8458             :     //      zero if another compression is available...)
    8459             : 
    8460             :     // TODO when downloading a file (an attachment) that's already
    8461             :     //      compressed we need to specify the compression of the output
    8462             :     //      buffer so that way here we can "adjust" the compression as
    8463             :     //      required -- some of that is already done/supported TBD
    8464             : 
    8465             :     // was the output buffer generated from an already compressed file?
    8466             :     // if so, then skip the encoding handling
    8467           0 :     QString const current_content_encoding(get_header("Content-Encoding"));
    8468           0 :     if(current_content_encoding.isEmpty())
    8469             :     {
    8470             :         // TODO image file formats that are already compressed should not be
    8471             :         //      recompressed (i.e. JPEG, GIF, PNG...)
    8472             : 
    8473             :         // at this point we only attempt to compress known text formats
    8474             :         // (should we instead have a list of mime types that we do not want to
    8475             :         // compress? before, attempting to compress the "wrong" files would
    8476             :         // make the browsers fail badly... but today not so much.)
    8477             :         //
    8478           0 :         QString const content_type(get_header(get_name(name_t::SNAP_NAME_CORE_CONTENT_TYPE_HEADER)));
    8479           0 :         if(content_type.startsWith("text/plain;")
    8480           0 :         || content_type.startsWith("text/html;")
    8481           0 :         || content_type.startsWith("text/xml;")
    8482           0 :         || content_type.startsWith("text/css;")
    8483           0 :         || content_type.startsWith("text/javascript;"))
    8484             :         {
    8485             :             // TODO add compression capabilities with bz2, lzma and sdch as
    8486             :             //      may be supported by the browser (although sdch is not
    8487             :             //      possible here as long as we require/use Apache2)
    8488           0 :             http_strings::WeightedHttpString encodings(snapenv("HTTP_ACCEPT_ENCODING"));
    8489             : 
    8490             :             // it looks like some browsers use "x-gzip" instead of
    8491             :             // plain "gzip"; also our default (i.e. "*") is gzip so
    8492             :             // check that value here too
    8493             :             //
    8494           0 :             float const gzip_level(std::max(std::max(encodings.get_level("gzip"), encodings.get_level("x-gzip")), encodings.get_level("*")));
    8495             : 
    8496             :             // plain zlib data is named "deflate"
    8497           0 :             float const deflate_level(encodings.get_level("deflate"));
    8498             : 
    8499           0 :             if(gzip_level > 0.0f && gzip_level >= deflate_level)
    8500             :             {
    8501             :                 // browser asked for gzip with higher preference
    8502           0 :                 QString compressor("gzip");
    8503           0 :                 output_data = compression::compress(compressor, output_data, 100, true);
    8504           0 :                 if(compressor == "gzip")
    8505             :                 {
    8506             :                     // compression succeeded
    8507           0 :                     set_header("Content-Encoding", "gzip", HEADER_MODE_EVERYWHERE);
    8508           0 :                 }
    8509             :             }
    8510           0 :             else if(deflate_level > 0.0f)
    8511             :             {
    8512           0 :                 QString compressor("deflate");
    8513           0 :                 output_data = compression::compress(compressor, output_data, 100, true);
    8514           0 :                 if(compressor == "deflate")
    8515             :                 {
    8516             :                     // compression succeeded
    8517           0 :                     set_header("Content-Encoding", "deflate", HEADER_MODE_EVERYWHERE);
    8518             :                 }
    8519             :             }
    8520             :             else
    8521             :             {
    8522             :                 // Code 406 is in the spec. (RFC2616) but frankly?!
    8523           0 :                 float const identity_level(encodings.get_level("identity"));
    8524             : #pragma GCC diagnostic push
    8525             : #pragma GCC diagnostic ignored "-Wfloat-equal"
    8526           0 :                 if(!f_died && identity_level == 0.0f)
    8527             : #pragma GCC diagnostic pop
    8528             :                 {
    8529           0 :                     die(http_code_t::HTTP_CODE_NOT_ACCEPTABLE, "No Acceptable Compression Encoding",
    8530             :                         "Your client requested a compression that we do not offer and it does not accept content without compression.",
    8531             :                         "a client requested content with Accept-Encoding: identity;q=0 and no other compression we understand");
    8532           0 :                     NOTREACHED();
    8533             :                 }
    8534             :                 // The "identity" SHOULD NOT be used with the Content-Encoding
    8535             :                 // (RFC 2616 -- https://tools.ietf.org/html/rfc2616)
    8536             :                 //set_header("Content-Encoding", "identity", HEADER_MODE_EVERYWHERE);
    8537             :             }
    8538             :         }
    8539             :         else
    8540             :         {
    8541             :             // note that "html"-output is NOT html in this case!
    8542             :             // (most likely an image... but any document really)
    8543             :         }
    8544             :     }
    8545             : 
    8546           0 :     QString const size(QString("%1").arg(output_data.size()));
    8547           0 :     set_header("Content-Length", size, HEADER_MODE_EVERYWHERE);
    8548             : 
    8549           0 :     output_headers(modes);
    8550             : 
    8551             :     // write the body in all circumstances unless
    8552             :     // the method is HEAD and the request did not fail
    8553             :     //
    8554             :     // IMPORTANT NOTE: it looks like Apache2 removes the body no matter what
    8555             :     //                 which is probably sensible... if a HEAD is used it is
    8556             :     //                 not a browser anyway
    8557           0 :     if(snapenv(get_name(name_t::SNAP_NAME_CORE_REQUEST_METHOD)) != "HEAD"
    8558             :     /*|| (modes & HEADER_MODE_NO_ERROR) == 0 -- not working... */)
    8559             :     {
    8560           0 :         write(output_data, output_data.size());
    8561             : // Warning: do not use this std::cout if you allow compression... 8-)
    8562             : //std::cout << "output [" << output_data.data() << "] [" << output_data.size() << "]\n";
    8563             :     }
    8564           0 : }
    8565             : 
    8566             : 
    8567             : /** \brief Process the post if there was one.
    8568             :  *
    8569             :  * This function processes the post, as in checks all the validity of
    8570             :  * all parameters and save the data in the database, and then returns.
    8571             :  *
    8572             :  * \note
    8573             :  * If the post was successful it is possible that the function generates
    8574             :  * an redirect and then exit immediately.
    8575             :  */
    8576           0 : void snap_child::process_post()
    8577             : {
    8578           0 :     if(f_has_post)
    8579             :     {
    8580           0 :         server::pointer_t server(get_server());
    8581           0 :         server->process_post(f_uri.path());
    8582             :     }
    8583           0 : }
    8584             : 
    8585             : 
    8586             : /** \brief Get the language defined by the user or browser.
    8587             :  *
    8588             :  * This function retrieves the language to be used in this
    8589             :  * request:
    8590             :  *
    8591             :  * \li the user defined language (sub-domain, path, or a GET variable)
    8592             :  * \li the preferred language of that user (user account)
    8593             :  * \li the language that the browser sent (as a last resort.)
    8594             :  *
    8595             :  * The user preferred language requires the user to be logged in. This
    8596             :  * call only works 100% correctly only if made the server::execute()
    8597             :  * call started (f_ready is true in snap_child).
    8598             :  *
    8599             :  * The language information is expected to be used to return the
    8600             :  * proper content of a page. However, you probably want to use the
    8601             :  * get_language_key() function instead as it will properly concatenate
    8602             :  * the language and the country exactly as required.
    8603             :  *
    8604             :  * If no language were defined, this function returns the "default"
    8605             :  * (as in neutral) language which is "xx". This may be returned if
    8606             :  * the function is called to early and no browser defaults were
    8607             :  * found in the request.
    8608             :  *
    8609             :  * \note
    8610             :  * The test to know whether the user has a preferred language selection
    8611             :  * is done in this function because the canonicalize_options() function
    8612             :  * runs too soon for such that query to function then (i.e. the plugins
    8613             :  * are defined, but the process_cookies() signal was not yet sent.)
    8614             :  *
    8615             :  * \return The language (2 letter canonicalized language name.)
    8616             :  */
    8617           0 : QString snap_child::get_language()
    8618             : {
    8619             :     // was the language defined as a sub-domain, a path segment, or
    8620             :     // a query string parameter? if so f_language is not empty
    8621           0 :     if(f_language.isEmpty())
    8622             :     {
    8623           0 :         get_plugins_locales();
    8624           0 :         if(!f_plugins_locales.isEmpty())
    8625             :         {
    8626             :             // a plugin defined a language
    8627             :             //
    8628           0 :             f_language = f_plugins_locales[0].get_language();
    8629           0 :             f_country = f_plugins_locales[0].get_country();
    8630             :         }
    8631           0 :         else if(!f_browser_locales.isEmpty())
    8632             :         {
    8633             :             // use client locale as provided by the browser
    8634             :             //
    8635           0 :             f_language = f_browser_locales[0].get_language();
    8636           0 :             f_country = f_browser_locales[0].get_country();
    8637             :         }
    8638             :         else
    8639             :         {
    8640             :             // no language defined, use the "no language" default in that case
    8641             :             //
    8642           0 :             f_language = "xx";
    8643             :         }
    8644             :     }
    8645             : 
    8646           0 :     return f_language;
    8647             : }
    8648             : 
    8649             : 
    8650             : /** \brief Retrieve all the languages in the order of preference.
    8651             :  *
    8652             :  * This function returns a vector of languages in the preference order
    8653             :  * which can be used to search for a match.
    8654             :  *
    8655             :  * \todo
    8656             :  * Look into whether we could have a vector of references instead because
    8657             :  * right now we copy all the languages twice!
    8658             :  *
    8659             :  * \todo
    8660             :  * Look into converting all the language/location to use the
    8661             :  * WeightedHttpStrings so we can have additional parameters
    8662             :  * attached to each language.
    8663             :  *
    8664             :  * \return An array of locales (language/country pairs).
    8665             :  */
    8666           0 : snap_child::locale_info_vector_t snap_child::get_all_locales()
    8667             : {
    8668           0 :     if(f_all_locales.isEmpty())
    8669             :     {
    8670             :         // first we want to check with the f_language and f_country
    8671             :         //
    8672           0 :         if(!f_language.isEmpty())
    8673             :         {
    8674           0 :             locale_info_t lang;
    8675           0 :             lang.set_language(f_language);
    8676           0 :             lang.set_country(f_country);
    8677             : 
    8678           0 :             f_all_locales.push_back(lang);
    8679             :         }
    8680             : 
    8681             :         // next we want to add the plugin definitions because a logged
    8682             :         // in user (or known, at least) who has preferences, we have to
    8683             :         // respect those preferences
    8684             :         //
    8685           0 :         get_plugins_locales();
    8686           0 :         for(auto const & l : f_plugins_locales)
    8687             :         {
    8688           0 :             if(std::find(f_all_locales.begin(), f_all_locales.end(), l) == f_all_locales.end())
    8689             :             {
    8690             :                 // not in the list yet, add it
    8691             :                 //
    8692           0 :                 f_all_locales.push_back(l);
    8693             :             }
    8694             :         }
    8695             : 
    8696             :         // next we use the browser defined languages
    8697             :         //
    8698           0 :         for(auto const & l : f_browser_locales)
    8699             :         {
    8700           0 :             if(std::find(f_all_locales.begin(), f_all_locales.end(), l) == f_all_locales.end())
    8701             :             {
    8702             :                 // not in the list yet, add it
    8703             :                 //
    8704           0 :                 f_all_locales.push_back(l);
    8705             :             }
    8706             :         }
    8707             : 
    8708             :         // now we want to re-add all those languages but without a country
    8709             :         // it is important before we add tests for default languages
    8710             :         //
    8711           0 :         if(!f_language.isEmpty()
    8712           0 :         && !f_country.isEmpty())
    8713             :         {
    8714           0 :             locale_info_t l;
    8715           0 :             l.set_language(f_language);
    8716           0 :             if(std::find(f_all_locales.begin(), f_all_locales.end(), l) == f_all_locales.end())
    8717             :             {
    8718           0 :                 f_all_locales.push_back(l);
    8719             :             }
    8720             :         }
    8721             : 
    8722           0 :         for(auto p : f_plugins_locales)
    8723             :         {
    8724           0 :             if(!p.get_country().isEmpty())
    8725             :             {
    8726           0 :                 locale_info_t l;
    8727           0 :                 l.set_language(p.get_language());
    8728           0 :                 f_all_locales.push_back(l);
    8729             :             }
    8730             :         }
    8731             : 
    8732           0 :         for(auto p : f_browser_locales)
    8733             :         {
    8734           0 :             if(!p.get_country().isEmpty())
    8735             :             {
    8736           0 :                 locale_info_t l;
    8737           0 :                 l.set_language(p.get_language());
    8738           0 :                 if(std::find(f_all_locales.begin(), f_all_locales.end(), l) == f_all_locales.end())
    8739             :                 {
    8740           0 :                     f_all_locales.push_back(l);
    8741             :                 }
    8742             :             }
    8743             :         }
    8744             : 
    8745             :         // finally, we add a few defaults in case no other languages
    8746             :         // matches we want to have those fallbacks
    8747             :         //
    8748             :         {
    8749             :             // Any English
    8750             :             //
    8751           0 :             locale_info_t l;
    8752           0 :             l.set_language("en");
    8753           0 :             if(std::find(f_all_locales.begin(), f_all_locales.end(), l) == f_all_locales.end())
    8754             :             {
    8755           0 :                 f_all_locales.push_back(l);
    8756             :             }
    8757             :         }
    8758             :         {
    8759             :             // Try without a language ("neutral" for pages like a photo)
    8760             :             //
    8761           0 :             locale_info_t l;
    8762           0 :             l.set_language("xx");
    8763           0 :             if(std::find(f_all_locales.begin(), f_all_locales.end(), l) == f_all_locales.end())
    8764             :             {
    8765           0 :                 f_all_locales.push_back(l);
    8766             :             }
    8767             :         }
    8768             :         {
    8769             :             // Finally we want to try with no language / no country
    8770             :             // (it should never already be in the list?!)
    8771             :             //
    8772           0 :             locale_info_t l;
    8773           0 :             if(std::find(f_all_locales.begin(), f_all_locales.end(), l) == f_all_locales.end())
    8774             :             {
    8775           0 :                 f_all_locales.push_back(l);
    8776             :             }
    8777             :         }
    8778             :     }
    8779             : 
    8780           0 :     return f_all_locales;
    8781             : }
    8782             : 
    8783             : 
    8784             : /** \brief Get the name of the country.
    8785             :  *
    8786             :  * This function returns the name of the country linked with the language
    8787             :  * that the user requested. For example, in the locale "en_US" the country
    8788             :  * code is US. This is what this function returns.
    8789             :  *
    8790             :  * In most cases this function is only used internally when getting the
    8791             :  * language key. You should not make use of this information as meaning
    8792             :  * that the user is from that country.
    8793             :  *
    8794             :  * \warning
    8795             :  * You should always call get_language() FIRST because it will define
    8796             :  * the country if the language was not defined as a sub-domain, a path
    8797             :  * segment, or a query string parameter.
    8798             :  *
    8799             :  * \return The country (2 letter canonicalized country name.)
    8800             :  *
    8801             :  * \sa get_language()
    8802             :  * \sa get_language_key()
    8803             :  */
    8804           0 : QString snap_child::get_country() const
    8805             : {
    8806           0 :     return f_country;
    8807             : }
    8808             : 
    8809             : 
    8810             : /** \brief The "language" (or locale) the user or browser defined.
    8811             :  *
    8812             :  * This function returns the language and country merged as a one key.
    8813             :  * The name of the language and the name of the country are always
    8814             :  * exactly 2 characters. The language name is in lowercase and the
    8815             :  * country is in uppercase. The two names are separated by an
    8816             :  * underscore (_).
    8817             :  *
    8818             :  * For example, English in the USA would be returned as: "en_US".
    8819             :  * German in Germany is defined as "de_DE".
    8820             :  *
    8821             :  * \return The language key or "locale" for this request.
    8822             :  */
    8823           0 : QString snap_child::get_language_key()
    8824             : {
    8825           0 :     if(f_language_key.isEmpty())
    8826             :     {
    8827           0 :         f_language_key = get_language();
    8828           0 :         QString country(get_country());
    8829           0 :         if(!country.isEmpty())
    8830             :         {
    8831           0 :             f_language_key += '_';
    8832           0 :             f_language_key += country;
    8833             :         }
    8834             :     }
    8835           0 :     return f_language_key;
    8836             : }
    8837             : 
    8838             : 
    8839             : /** \brief Get the list of locales defined by plugins.
    8840             :  *
    8841             :  * At this point the main plugin defining locales to be used is the users
    8842             :  * plugin which offers the user to edit a set his settings and as one
    8843             :  * of the entries offers the user to enter one or more locales.
    8844             :  *
    8845             :  * The array of user locales is properly defined only if the f_ready
    8846             :  * flag is true. Otherwise it won't be proper since the user won't already
    8847             :  * be logged in.
    8848             :  *
    8849             :  * \return An array of locales.
    8850             :  */
    8851           0 : snap_child::locale_info_vector_t const& snap_child::get_plugins_locales()
    8852             : {
    8853           0 :     if(f_plugins_locales.isEmpty())
    8854             :     {
    8855           0 :         if(!f_ready)
    8856             :         {
    8857             :             // I do not think this happens, but just in case warn myself
    8858             :             //
    8859           0 :             SNAP_LOG_WARNING("get_plugins_locales() called before f_ready was set to true");
    8860             :         }
    8861             : 
    8862             :         // retrieve the locales from all the plugins
    8863             :         // we expect a string of locales defined as weighted HTTP strings
    8864             :         // remember that without a proper weight the algorithm uses 1.0
    8865             :         //
    8866           0 :         server::pointer_t server(get_server());
    8867           0 :         http_strings::WeightedHttpString locales;
    8868           0 :         server->define_locales(locales);
    8869             : 
    8870           0 :         http_strings::WeightedHttpString::part_t::vector_t const & plugins_languages(locales.get_parts());
    8871           0 :         if(!plugins_languages.isEmpty())
    8872             :         {
    8873             :             // not sorted by default, make sure it gets sorted
    8874             :             // (if empty we can avoid this call and since the plugins_languages
    8875             :             // parameter is a reference, we will still see the result)
    8876             :             //
    8877           0 :             locales.sort_by_level();
    8878             : 
    8879           0 :             int const max_languages(plugins_languages.size());
    8880           0 :             for(int i(0); i < max_languages; ++i)
    8881             :             {
    8882           0 :                 QString plugins_lang(plugins_languages[i].get_name());
    8883           0 :                 QString plugins_country;
    8884           0 :                 if(verify_locale(plugins_lang, plugins_country, false))
    8885             :                 {
    8886             :                     // only keep those that are considered valid (they all should
    8887             :                     // be, but in case a hacker or strange client access our
    8888             :                     // systems...)
    8889             :                     //
    8890           0 :                     locale_info_t l;
    8891           0 :                     l.set_language(plugins_lang);
    8892           0 :                     l.set_country(plugins_country);
    8893           0 :                     f_plugins_locales.push_back(l);
    8894             :                     // added in order
    8895             :                 }
    8896             :                 else
    8897             :                 {
    8898             :                     // plugin sent us something we do not understand
    8899             :                     //
    8900           0 :                     SNAP_LOG_WARNING(QString("plugin locale \"%1\" does not look like a legal locale, ignore")
    8901           0 :                                                 .arg(plugins_languages[i].get_name()));
    8902             :                 }
    8903             :             }
    8904             :         }
    8905             :     }
    8906             : 
    8907           0 :     return f_plugins_locales;
    8908             : }
    8909             : 
    8910             : 
    8911             : /** \brief Retrieve the list of locales as defined in the browser.
    8912             :  *
    8913             :  * This function returns the array of locales as defined in the user's
    8914             :  * browser. This is considered the fallback in case the user did not
    8915             :  * force a language in his request (sub-domain, path segment, or query
    8916             :  * string parameter.)
    8917             :  *
    8918             :  * \return A constant reference to the browser locales.
    8919             :  */
    8920           0 : snap_child::locale_info_vector_t const & snap_child::get_browser_locales() const
    8921             : {
    8922           0 :     return f_browser_locales;
    8923             : }
    8924             : 
    8925             : 
    8926             : /** \brief Check whether the current (false) or current working (true) branch should be used.
    8927             :  *
    8928             :  * This function returns true if the user requested the working branch.
    8929             :  *
    8930             :  * \return true if the current working branch should be used.
    8931             :  */
    8932           0 : bool snap_child::get_working_branch() const
    8933             : {
    8934           0 :     return f_working_branch;
    8935             : }
    8936             : 
    8937             : 
    8938             : /** \brief Retrieve the branch number to be accessed.
    8939             :  *
    8940             :  * This function returns SPECIAL_VERSION_UNDEFINED (which is -1) if the
    8941             :  * branch was not defined by the user.
    8942             :  */
    8943           0 : snap_version::version_number_t snap_child::get_branch() const
    8944             : {
    8945           0 :     return f_branch;
    8946             : }
    8947             : 
    8948             : 
    8949             : /** \brief Get the revision number that the user defined.
    8950             :  *
    8951             :  * This function returns zero if the user has not set the revision.
    8952             :  * Note that zero is also a valid revision.
    8953             :  */
    8954           0 : snap_version::version_number_t snap_child::get_revision() const
    8955             : {
    8956           0 :     return f_revision;
    8957             : }
    8958             : 
    8959             : 
    8960             : /** \brief Retrieve the full revision key.
    8961             :  *
    8962             :  * The branch and revision numbers combined with a period as the separator
    8963             :  * represents the revision key of this request.
    8964             :  *
    8965             :  * Note that if the user did not specify a revision, then the string
    8966             :  * returned is just the branch. If no branch and no revision were
    8967             :  * specified, then the key is empty and the default for that page is to
    8968             :  * be used.
    8969             :  *
    8970             :  * \return A string defined as "\<branch>.\<revision>".
    8971             :  */
    8972           0 : QString snap_child::get_revision_key() const
    8973             : {
    8974           0 :     return f_revision_key;
    8975             : }
    8976             : 
    8977             : 
    8978             : /** \brief Get compression information.
    8979             :  *
    8980             :  * This function retrieves a reference to the array of compressions accepted
    8981             :  * by the client. This can be used by plugins that generate the output
    8982             :  * directly and the snap_child functions won't get a chance to compress
    8983             :  * the data later or for such a plugin to let the snap_child object
    8984             :  * know that the data is pre-compressed (i.e. when we pre-compress data
    8985             :  * with gzip we can just return that data, it means we read less from
    8986             :  * the database and we may have compressed that data on a backend
    8987             :  * server leaving the front end running full speed.)
    8988             :  *
    8989             :  * The array may end with COMPRESSION_INVALID in which case one of the
    8990             :  * compressions defined before MUST have been a match. If not, then the
    8991             :  * file cannot be returned to the user and a 406 error
    8992             :  * (HTTP_CODE_NOT_ACCEPTABLE) should be returned to the client.
    8993             :  *
    8994             :  * \return The array of compressions expected by the client, preferred first.
    8995             :  */
    8996           0 : snap_child::compression_vector_t snap_child::get_compression() const
    8997             : {
    8998           0 :     return f_compressions;
    8999             : }
    9000             : 
    9001             : 
    9002             : /** \brief Convert a time/date value to a string.
    9003             :  *
    9004             :  * This function transform a date such as the content::modified field
    9005             :  * to a format that is useful to the XSL parser. It supports various
    9006             :  * formats:
    9007             :  *
    9008             :  * \li DATE_FORMAT_SHORT -- YYYY-MM-DD
    9009             :  * \li DATE_FORMAT_LONG  -- YYYY-MM-DDTHH:MM:SSZ
    9010             :  * \li DATE_FORMAT_TIME  -- HH:MM:SS
    9011             :  * \li DATE_FORMAT_EMAIL -- dd MMM yyyy hh:mm:ss +0000
    9012             :  * \li DATE_FORMAT_HTTP  -- ddd, dd MMM yyyy hh:mm:ss +0000
    9013             :  *
    9014             :  * The long format includes the time.
    9015             :  *
    9016             :  * The HTTP format uses the day and month names in English only since
    9017             :  * the HTTP protocol only expects English.
    9018             :  *
    9019             :  * The date is always output as UTC (opposed to local time.)
    9020             :  *
    9021             :  * \note
    9022             :  * In order to display a date to the end user (in the HTML for the users)
    9023             :  * you may want to setup the timezone information first and then use
    9024             :  * the various functions supplied by the locale plugin to generate the
    9025             :  * date. The locale plugin also supports formatting numbers, time,
    9026             :  * messages, etc. going both ways (from the formatted data to internal
    9027             :  * data and vice versa.) There is also JavaScript support for editor
    9028             :  * widgets.
    9029             :  *
    9030             :  * \param[in] v  A 64 bit time / date value in microseconds, although we
    9031             :  *               really only use precision to the second.
    9032             :  * \param[in] date_format  Which format should be used.
    9033             :  *
    9034             :  * \return The formatted date and time.
    9035             :  */
    9036           0 : QString snap_child::date_to_string(int64_t v, date_format_t date_format)
    9037             : {
    9038             :     // go to seconds
    9039           0 :     time_t seconds(v / 1000000);
    9040             : 
    9041             :     struct tm time_info;
    9042           0 :     gmtime_r(&seconds, &time_info);
    9043             : 
    9044             :     char buf[256];
    9045           0 :     buf[0] = '\0';
    9046             : 
    9047           0 :     switch(date_format)
    9048             :     {
    9049           0 :     case date_format_t::DATE_FORMAT_SHORT:
    9050           0 :         strftime(buf, sizeof(buf), "%Y-%m-%d", &time_info);
    9051           0 :         break;
    9052             : 
    9053           0 :     case date_format_t::DATE_FORMAT_SHORT_US:
    9054           0 :         strftime(buf, sizeof(buf), "%m-%d-%Y", &time_info);
    9055           0 :         break;
    9056             : 
    9057           0 :     case date_format_t::DATE_FORMAT_LONG:
    9058             :         // TBD do we want the Z when generating time for HTML headers?
    9059             :         // (it is useful for the sitemap.xml at this point)
    9060           0 :         strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%SZ", &time_info);
    9061           0 :         break;
    9062             : 
    9063           0 :     case date_format_t::DATE_FORMAT_TIME:
    9064           0 :         strftime(buf, sizeof(buf), "%H:%M:%S", &time_info);
    9065           0 :         break;
    9066             : 
    9067           0 :     case date_format_t::DATE_FORMAT_EMAIL:
    9068             :         { // dd MMM yyyy hh:mm:ss +0000
    9069             :             // do it manually so the date is ALWAYS in English
    9070           0 :             return QString("%1 %2 %3 %4:%5:%6 +0000")
    9071           0 :                 .arg(time_info.tm_mday, 2, 10, QChar('0'))
    9072           0 :                 .arg(QString::fromLatin1(g_month_name[time_info.tm_mon], 3))
    9073           0 :                 .arg(time_info.tm_year + 1900, 4, 10, QChar('0'))
    9074           0 :                 .arg(time_info.tm_hour, 2, 10, QChar('0'))
    9075           0 :                 .arg(time_info.tm_min, 2, 10, QChar('0'))
    9076           0 :                 .arg(time_info.tm_sec, 2, 10, QChar('0'));
    9077             :         }
    9078             :         break;
    9079             : 
    9080           0 :     case date_format_t::DATE_FORMAT_HTTP:
    9081             :         { // ddd, dd MMM yyyy hh:mm:ss GMT
    9082             :             // do it manually so the date is ALWAYS in English
    9083           0 :             return QString("%1, %2 %3 %4 %5:%6:%7 +0000")
    9084           0 :                 .arg(QString::fromLatin1(g_week_day_name[time_info.tm_wday], 3))
    9085           0 :                 .arg(time_info.tm_mday, 2, 10, QChar('0'))
    9086           0 :                 .arg(QString::fromLatin1(g_month_name[time_info.tm_mon], 3))
    9087           0 :                 .arg(time_info.tm_year + 1900, 4, 10, QChar('0'))
    9088           0 :                 .arg(time_info.tm_hour, 2, 10, QChar('0'))
    9089           0 :                 .arg(time_info.tm_min, 2, 10, QChar('0'))
    9090           0 :                 .arg(time_info.tm_sec, 2, 10, QChar('0'));
    9091             :         }
    9092             :         break;
    9093             : 
    9094             :     }
    9095             : 
    9096           0 :     return buf;
    9097             : }
    9098             : 
    9099             : 
    9100             : /** \brief Convert a date from a string to a time_t.
    9101             :  *
    9102             :  * This function transforms a date received by the client to a Unix
    9103             :  * time_t value. We programmed our own because several fields are
    9104             :  * optional and the strptime() function does not support such. Also
    9105             :  * the strptime() uses the locale() for the day and month check
    9106             :  * which is not expected for HTTP. The QDateTime object has similar
    9107             :  * flaws.
    9108             :  *
    9109             :  * The function supports the RFC822, RFC850, and ANSI formats. On top
    9110             :  * of these formats, the function understands the month name in full,
    9111             :  * and the week day name and timezone parameters are viewed as optional.
    9112             :  *
    9113             :  * See the document we used to make sure we'd support pretty much all the
    9114             :  * dates that a client might send to us:
    9115             :  * http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1
    9116             :  *
    9117             :  * Formats that we support:
    9118             :  *
    9119             :  * \code
    9120             :  *      YYYY-MM-DD
    9121             :  *      DD-MMM-YYYY HH:MM:SS TZ
    9122             :  *      DD-MMM-YYYY HH:MM:SS TZ
    9123             :  *      WWW, DD-MMM-YYYY HH:MM:SS TZ
    9124             :  *      MMM-DD-YYYY HH:MM:SS TZ
    9125             :  *      WWW MMM-DD HH:MM:SS YYYY
    9126             :  * \endcode
    9127             :  *
    9128             :  * The month and weekday may be a 3 letter abbreviation or the full English
    9129             :  * name. The month must use letters. We support the ANSI format which must
    9130             :  * start with the month or week name in letters. We distinguish the ANSI
    9131             :  * format from the other RFC-2616 date if it starts with a week day and is
    9132             :  * followed by a space, or it directly starts with the month name.
    9133             :  *
    9134             :  * The year may be 2 or 4 digits.
    9135             :  *
    9136             :  * The timezone is optional. It may use a space or a + or a - as a separator.
    9137             :  * The timezone may be an abbreviation or a 4 digit number.
    9138             :  *
    9139             :  * \param[in] date  The date to convert to a time_t.
    9140             :  *
    9141             :  * \return The date and time as a Unix time_t number, -1 if the convertion fails.
    9142             :  */
    9143           0 : time_t snap_child::string_to_date(QString const & date)
    9144             : {
    9145           0 :     struct parser_t
    9146             :     {
    9147           0 :         parser_t(QString const & date)
    9148           0 :             : f_date(date.simplified().toLower().toUtf8())
    9149           0 :             , f_s(f_date.data())
    9150             :         {
    9151           0 :         }
    9152             : 
    9153             :         parser_t(parser_t const & rhs) = delete;
    9154             :         parser_t & operator = (parser_t const & rhs) = delete;
    9155             : 
    9156           0 :         void skip_spaces()
    9157             :         {
    9158           0 :             while(isspace(*f_s))
    9159             :             {
    9160           0 :                 ++f_s;
    9161             :             }
    9162           0 :         }
    9163             : 
    9164           0 :         bool parse_week_day()
    9165             :         {
    9166             :             // day         =  "Mon"  / "Tue" /  "Wed"  / "Thu"
    9167             :             //             /  "Fri"  / "Sat" /  "Sun"
    9168           0 :             if(f_s[0] == 'm' && f_s[1] == 'o' && f_s[2] == 'n')
    9169             :             {
    9170           0 :                 f_time_info.tm_wday = 1;
    9171             :             }
    9172           0 :             else if(f_s[0] == 't' && f_s[1] == 'u' && f_s[2] == 'e')
    9173             :             {
    9174           0 :                 f_time_info.tm_wday = 2;
    9175             :             }
    9176           0 :             else if(f_s[0] == 'w' && f_s[1] == 'e' && f_s[2] == 'd')
    9177             :             {
    9178           0 :                 f_time_info.tm_wday = 3;
    9179             :             }
    9180           0 :             else if(f_s[0] == 't' && f_s[1] == 'h' && f_s[2] == 'u')
    9181             :             {
    9182           0 :                 f_time_info.tm_wday = 4;
    9183             :             }
    9184           0 :             else if(f_s[0] == 'f' && f_s[1] == 'r' && f_s[2] == 'i')
    9185             :             {
    9186           0 :                 f_time_info.tm_wday = 5;
    9187             :             }
    9188           0 :             else if(f_s[0] == 's' && f_s[1] == 'a' && f_s[2] == 't')
    9189             :             {
    9190           0 :                 f_time_info.tm_wday = 6;
    9191             :             }
    9192           0 :             else if(f_s[0] == 's' && f_s[1] == 'u' && f_s[2] == 'n')
    9193             :             {
    9194           0 :                 f_time_info.tm_wday = 0;
    9195             :             }
    9196             :             else
    9197             :             {
    9198           0 :                 return false;
    9199             :             }
    9200             : 
    9201             :             // check whether the other characters follow
    9202           0 :             if(strncmp(f_s + 3, g_week_day_name[f_time_info.tm_wday] + 3, g_week_day_length[f_time_info.tm_wday] - 3) == 0)
    9203             :             {
    9204             :                 // full day (RFC850)
    9205           0 :                 f_s += g_week_day_length[f_time_info.tm_wday];
    9206             :             }
    9207             :             else
    9208             :             {
    9209           0 :                 f_s += 3;
    9210             :             }
    9211             : 
    9212           0 :             return true;
    9213             :         }
    9214             : 
    9215           0 :         bool parse_month()
    9216             :         {
    9217             :             // month       =  "Jan"  /  "Feb" /  "Mar"  /  "Apr"
    9218             :             //             /  "May"  /  "Jun" /  "Jul"  /  "Aug"
    9219             :             //             /  "Sep"  /  "Oct" /  "Nov"  /  "Dec"
    9220           0 :             if(f_s[0] == 'j' && f_s[1] == 'a' && f_s[2] == 'n')
    9221             :             {
    9222           0 :                 f_time_info.tm_mon = 0;
    9223             :             }
    9224           0 :             else if(f_s[0] == 'f' && f_s[1] == 'e' && f_s[2] == 'b')
    9225             :             {
    9226           0 :                 f_time_info.tm_mon = 1;
    9227             :             }
    9228           0 :             else if(f_s[0] == 'm' && f_s[1] == 'a' && f_s[2] == 'r')
    9229             :             {
    9230           0 :                 f_time_info.tm_mon = 2;
    9231             :             }
    9232           0 :             else if(f_s[0] == 'a' && f_s[1] == 'p' && f_s[2] == 'r')
    9233             :             {
    9234           0 :                 f_time_info.tm_mon = 3;
    9235             :             }
    9236           0 :             else if(f_s[0] == 'm' && f_s[1] == 'a' && f_s[2] == 'y')
    9237             :             {
    9238           0 :                 f_time_info.tm_mon = 4;
    9239             :             }
    9240           0 :             else if(f_s[0] == 'j' && f_s[1] == 'u' && f_s[2] == 'n')
    9241             :             {
    9242           0 :                 f_time_info.tm_mon = 5;
    9243             :             }
    9244           0 :             else if(f_s[0] == 'j' && f_s[1] == 'u' && f_s[2] == 'l')
    9245             :             {
    9246           0 :                 f_time_info.tm_mon = 6;
    9247             :             }
    9248           0 :             else if(f_s[0] == 'a' && f_s[1] == 'u' && f_s[2] == 'g')
    9249             :             {
    9250           0 :                 f_time_info.tm_mon = 7;
    9251             :             }
    9252           0 :             else if(f_s[0] == 's' && f_s[1] == 'e' && f_s[2] == 'p')
    9253             :             {
    9254           0 :                 f_time_info.tm_mon = 8;
    9255             :             }
    9256           0 :             else if(f_s[0] == 'o' && f_s[1] == 'c' && f_s[2] == 't')
    9257             :             {
    9258           0 :                 f_time_info.tm_mon = 9;
    9259             :             }
    9260           0 :             else if(f_s[0] == 'n' && f_s[1] == 'o' && f_s[2] == 'v')
    9261             :             {
    9262           0 :                 f_time_info.tm_mon = 10;
    9263             :             }
    9264           0 :             else if(f_s[0] == 'd' && f_s[1] == 'e' && f_s[2] == 'c')
    9265             :             {
    9266           0 :                 f_time_info.tm_mon = 11;
    9267             :             }
    9268             :             else
    9269             :             {
    9270           0 :                 return false;
    9271             :             }
    9272             : 
    9273             :             // check whether the other characters follow
    9274           0 :             if(strncmp(f_s + 3, g_month_name[f_time_info.tm_mon] + 3, g_month_length[f_time_info.tm_mon] - 3) == 0)
    9275             :             {
    9276             :                 // full month (not in the specs)
    9277           0 :                 f_s += g_month_length[f_time_info.tm_mon];
    9278             :             }
    9279             :             else
    9280             :             {
    9281           0 :                 f_s += 3;
    9282             :             }
    9283             : 
    9284           0 :             skip_spaces();
    9285           0 :             return true;
    9286             :         }
    9287             : 
    9288           0 :         bool integer(unsigned int min_len, unsigned int max_len, unsigned int min_value, unsigned int max_value, int & result)
    9289             :         {
    9290           0 :             unsigned int u_result = 0;
    9291           0 :             unsigned int count(0);
    9292           0 :             for(; *f_s >= '0' && *f_s <= '9'; ++f_s, ++count)
    9293             :             {
    9294           0 :                 u_result = u_result * 10 + *f_s - '0';
    9295             :             }
    9296           0 :             if(count < min_len || count > max_len
    9297           0 :             || u_result < min_value || u_result > max_value)
    9298             :             {
    9299           0 :                 result = static_cast<int>(u_result);
    9300           0 :                 return false;
    9301             :             }
    9302           0 :             result = static_cast<int>(u_result);
    9303           0 :             return true;
    9304             :         }
    9305             : 
    9306           0 :         bool parse_time()
    9307             :         {
    9308           0 :             if(!integer(2, 2, 0, 23, f_time_info.tm_hour))
    9309             :             {
    9310           0 :                 return false;
    9311             :             }
    9312           0 :             if(*f_s != ':')
    9313             :             {
    9314           0 :                 return false;
    9315             :             }
    9316           0 :             ++f_s;
    9317           0 :             if(!integer(2, 2, 0, 59, f_time_info.tm_min))
    9318             :             {
    9319           0 :                 return false;
    9320             :             }
    9321           0 :             if(*f_s != ':')
    9322             :             {
    9323           0 :                 return false;
    9324             :             }
    9325           0 :             ++f_s;
    9326           0 :             if(!integer(2, 2, 0, 60, f_time_info.tm_sec))
    9327             :             {
    9328           0 :                 return false;
    9329             :             }
    9330           0 :             skip_spaces();
    9331           0 :             return true;
    9332             :         }
    9333             : 
    9334           0 :         bool parse_timezone()
    9335             :         {
    9336             :             // any timezone?
    9337           0 :             if(*f_s == '\0')
    9338             :             {
    9339           0 :                 return true;
    9340             :             }
    9341             : 
    9342             :             // XXX not too sure that the zone is properly handled at this point
    9343             :             // (i.e. should I do += or -=, it may be wrong in many places...)
    9344             :             //
    9345             :             // The newest HTTP format is to only support "+/-####"
    9346             :             //
    9347             :             // zone        =  "UT"  / "GMT"
    9348             :             //             /  "EST" / "EDT"
    9349             :             //             /  "CST" / "CDT"
    9350             :             //             /  "MST" / "MDT"
    9351             :             //             /  "PST" / "PDT"
    9352             :             //             /  1ALPHA
    9353             :             //             / ( ("+" / "-") 4DIGIT )
    9354           0 :             if((f_s[0] == 'u' && f_s[1] == 't' && f_s[2] == '\0')                 // UT
    9355           0 :             || (f_s[0] == 'u' && f_s[1] == 't' && f_s[2] == 'c' && f_s[3] == '\0')  // UTC (not in the spec...)
    9356           0 :             || (f_s[0] == 'g' && f_s[1] == 'm' && f_s[2] == 't' && f_s[3] == '\0')) // GMT
    9357             :             {
    9358             :                 // no adjustment for UTC (GMT)
    9359             :             }
    9360           0 :             else if(f_s[0] == 'e' && f_s[1] == 's' && f_s[2] == 't' && f_s[3] == '\0') // EST
    9361             :             {
    9362           0 :                 f_time_info.tm_hour -= 5;
    9363             :             }
    9364           0 :             else if(f_s[0] == 'e' && f_s[1] == 'd' && f_s[2] == 't' && f_s[3] == '\0') // EDT
    9365             :             {
    9366           0 :                 f_time_info.tm_hour -= 4;
    9367             :             }
    9368           0 :             else if(f_s[0] == 'c' && f_s[1] == 's' && f_s[2] == 't' && f_s[3] == '\0') // CST
    9369             :             {
    9370           0 :                 f_time_info.tm_hour -= 6;
    9371             :             }
    9372           0 :             else if(f_s[0] == 'c' && f_s[1] == 'd' && f_s[2] == 't' && f_s[3] == '\0') // CDT
    9373             :             {
    9374           0 :                 f_time_info.tm_hour -= 5;
    9375             :             }
    9376           0 :             else if(f_s[0] == 'm' && f_s[1] == 's' && f_s[2] == 't' && f_s[3] == '\0') // MST
    9377             :             {
    9378           0 :                 f_time_info.tm_hour -= 7;
    9379             :             }
    9380           0 :             else if(f_s[0] == 'm' && f_s[1] == 'd' && f_s[2] == 't' && f_s[3] == '\0') // MDT
    9381             :             {
    9382           0 :                 f_time_info.tm_hour -= 6;
    9383             :             }
    9384           0 :             else if(f_s[0] == 'p' && f_s[1] == 's' && f_s[2] == 't' && f_s[3] == '\0') // PST
    9385             :             {
    9386           0 :                 f_time_info.tm_hour -= 8;
    9387             :             }
    9388           0 :             else if(f_s[0] == 'p' && f_s[1] == 'd' && f_s[2] == 't' && f_s[3] == '\0') // PDT
    9389             :             {
    9390           0 :                 f_time_info.tm_hour -= 7;
    9391             :             }
    9392           0 :             else if(f_s[0] >= 'a' && f_s[0] <= 'z' && f_s[0] != 'j' && f_s[1] == '\0')
    9393             :             {
    9394           0 :                 f_time_info.tm_hour += g_timezone_adjust[f_s[0] - 'a'];
    9395             :             }
    9396           0 :             else if((f_s[0] == '+' || f_s[0] == '-')
    9397           0 :                   && f_s[1] >= '0' && f_s[1] <= '9'
    9398           0 :                   && f_s[2] >= '0' && f_s[2] <= '9'
    9399           0 :                   && f_s[3] >= '0' && f_s[3] <= '9'
    9400           0 :                   && f_s[4] >= '0' && f_s[4] <= '9'
    9401           0 :                   && f_s[5] == '\0')
    9402             :             {
    9403           0 :                 f_time_info.tm_hour += ((f_s[1] - '0') * 10 + f_s[2] - '0') * (f_s[0] == '+' ? 1 : -1);
    9404           0 :                 f_time_info.tm_min  += ((f_s[3] - '0') * 10 + f_s[4] - '0') * (f_s[0] == '+' ? 1 : -1);
    9405             :             }
    9406             :             else
    9407             :             {
    9408             :                 // invalid time zone
    9409           0 :                 return false;
    9410             :             }
    9411             : 
    9412             :             // WARNING: the time zone doesn't get skipped!
    9413           0 :             return true;
    9414             :         }
    9415             : 
    9416           0 :         bool parse_ansi()
    9417             :         {
    9418           0 :             skip_spaces();
    9419           0 :             if(!parse_month())
    9420             :             {
    9421           0 :                 return false;
    9422             :             }
    9423           0 :             if(!integer(1, 2, 1, 31, f_time_info.tm_mday))
    9424             :             {
    9425           0 :                 return false;
    9426             :             }
    9427           0 :             skip_spaces();
    9428           0 :             if(!parse_time())
    9429             :             {
    9430           0 :                 return false;
    9431             :             }
    9432           0 :             if(!integer(2, 4, 0, 3000, f_time_info.tm_year))
    9433             :             {
    9434           0 :                 return false;
    9435             :             }
    9436           0 :             skip_spaces();
    9437           0 :             return parse_timezone();
    9438             :         }
    9439             : 
    9440           0 :         bool parse_us()
    9441             :         {
    9442           0 :             skip_spaces();
    9443           0 :             if(!parse_month())
    9444             :             {
    9445           0 :                 return false;
    9446             :             }
    9447           0 :             skip_spaces();
    9448           0 :             if(!integer(1, 2, 1, 31, f_time_info.tm_mday))
    9449             :             {
    9450           0 :                 return false;
    9451             :             }
    9452           0 :             skip_spaces();
    9453           0 :             if(!integer(2, 4, 0, 3000, f_time_info.tm_year))
    9454             :             {
    9455           0 :                 return false;
    9456             :             }
    9457           0 :             skip_spaces();
    9458           0 :             return parse_time();
    9459             :         }
    9460             : 
    9461           0 :         bool parse()
    9462             :         {
    9463             :             // support for YYYY-MM-DD
    9464           0 :             if(f_date.size() == 10
    9465           0 :             && f_s[4] == '-'
    9466           0 :             && f_s[7] == '-')
    9467             :             {
    9468           0 :                 if(!integer(4, 4, 0, 3000, f_time_info.tm_year))
    9469             :                 {
    9470           0 :                     return false;
    9471             :                 }
    9472           0 :                 if(*f_s != '-')
    9473             :                 {
    9474           0 :                     return false;
    9475             :                 }
    9476           0 :                 ++f_s;
    9477           0 :                 if(!integer(2, 2, 1, 12, f_time_info.tm_mon))
    9478             :                 {
    9479           0 :                     return false;
    9480             :                 }
    9481           0 :                 --f_time_info.tm_mon; // expect 0 to 11 in final structure
    9482           0 :                 if(*f_s != '-')
    9483             :                 {
    9484           0 :                     return false;
    9485             :                 }
    9486           0 :                 ++f_s;
    9487           0 :                 if(!integer(2, 2, 1, 31, f_time_info.tm_mday))
    9488             :                 {
    9489           0 :                     return false;
    9490             :                 }
    9491           0 :                 return true;
    9492             :             }
    9493             : 
    9494             :             // week day (optional in RFC822)
    9495           0 :             if(*f_s >= 'a' && *f_s <= 'z')
    9496             :             {
    9497           0 :                 if(!parse_week_day())
    9498             :                 {
    9499             :                     // maybe that was the month, not the day
    9500             :                     // if the time is last, we have a preprocessor date/time
    9501             :                     // the second test is needed because the string gets
    9502             :                     // simplified and thus numbers 1 to 9 generate a string
    9503             :                     // one shorter
    9504           0 :                     if((strlen(f_s) == 11 + 1 + 8
    9505           0 :                      && f_s[11 + 1 + 8 - 6] == ':'
    9506           0 :                      && f_s[11 + 1 + 8 - 3] == ':')
    9507           0 :                     ||
    9508           0 :                        (strlen(f_s) == 10 + 1 + 8
    9509           0 :                      && f_s[10 + 1 + 8 - 6] == ':'
    9510           0 :                      && f_s[10 + 1 + 8 - 3] == ':'))
    9511             :                     {
    9512           0 :                         return parse_us();
    9513             :                     }
    9514           0 :                     return parse_ansi();
    9515             :                 }
    9516             : 
    9517           0 :                 if(f_s[0] == ' ')
    9518             :                 {
    9519             :                     // the ANSI format is completely random!
    9520           0 :                     return parse_ansi();
    9521             :                 }
    9522             : 
    9523           0 :                 if(f_s[0] != ',')
    9524             :                 {
    9525           0 :                     return false;
    9526             :                 }
    9527           0 :                 ++f_s; // skip the comma
    9528           0 :                 skip_spaces();
    9529             :             }
    9530             : 
    9531           0 :             if(!integer(1, 2, 1, 31, f_time_info.tm_mday))
    9532             :             {
    9533           0 :                 return false;
    9534             :             }
    9535             : 
    9536           0 :             if(*f_s == '-')
    9537             :             {
    9538           0 :                 ++f_s;
    9539             :             }
    9540           0 :             skip_spaces();
    9541             : 
    9542           0 :             if(!parse_month())
    9543             :             {
    9544           0 :                 return false;
    9545             :             }
    9546           0 :             if(*f_s == '-')
    9547             :             {
    9548           0 :                 ++f_s;
    9549           0 :                 skip_spaces();
    9550             :             }
    9551           0 :             if(!integer(2, 4, 0, 3000, f_time_info.tm_year))
    9552             :             {
    9553           0 :                 return false;
    9554             :             }
    9555           0 :             skip_spaces();
    9556           0 :             if(!parse_time())
    9557             :             {
    9558           0 :                 return false;
    9559             :             }
    9560             : 
    9561           0 :             return parse_timezone();
    9562             :         }
    9563             : 
    9564             :         struct tm       f_time_info = tm();
    9565             :         QByteArray      f_date = QByteArray();
    9566             :         char const *    f_s = nullptr;
    9567           0 :     } parser(date);
    9568             : 
    9569           0 :     if(!parser.parse())
    9570             :     {
    9571           0 :         return -1;
    9572             :     }
    9573             : 
    9574             :     // 2 digit year?
    9575             :     // How to handle this one? At this time I do not expect our software
    9576             :     // to work beyond 2070 which is probably short sighted (ha! ha!)
    9577             :     // However, that way we avoid calling time() and transform that in
    9578             :     // a tm structure and check that date
    9579           0 :     if(parser.f_time_info.tm_year < 100)
    9580             :     {
    9581           0 :         parser.f_time_info.tm_year += 1900;
    9582           0 :         if(parser.f_time_info.tm_year < 1970)
    9583             :         {
    9584           0 :             parser.f_time_info.tm_year += 100;
    9585             :         }
    9586             :     }
    9587             : 
    9588             :     // make sure the day is valid for that month/year
    9589           0 :     if(parser.f_time_info.tm_mday > last_day_of_month(parser.f_time_info.tm_mon + 1, parser.f_time_info.tm_year))
    9590             :     {
    9591           0 :         return -1;
    9592             :     }
    9593             : 
    9594             :     // now we have a time_info which is fully adjusted except for DST...
    9595             :     // let's make time
    9596           0 :     parser.f_time_info.tm_year -= 1900;
    9597           0 :     return mkgmtime(&parser.f_time_info);
    9598             : }
    9599             : 
    9600             : 
    9601             : /** \brief From a month and year, get the last day of the month.
    9602             :  *
    9603             :  * This function gives you the number of the last day of the month.
    9604             :  * In all cases, except February, it returns 30 or 31.
    9605             :  *
    9606             :  * For the month of February, we first compute the leap year flag.
    9607             :  * If the year is a leap year, then it returns 29, otherwise it
    9608             :  * returns 28.
    9609             :  *
    9610             :  * The leap year formula is:
    9611             :  *
    9612             :  * \code
    9613             :  *      leap = !(year % 4) && (year % 100 || !(year % 400));
    9614             :  * \endcode
    9615             :  *
    9616             :  * \warning
    9617             :  * This function throws if called with September 1752 because the
    9618             :  * month has missing days within the month (days 3 to 13).
    9619             :  *
    9620             :  * \param[in] month  A number from 1 to 12 representing a month.
    9621             :  * \param[in] year  A year, including the century.
    9622             :  *
    9623             :  * \return Last day of month, 30, 31, or in February, 28 or 29.
    9624             :  */
    9625           0 : int snap_child::last_day_of_month(int month, int year)
    9626             : {
    9627           0 :     if(month < 1 || month > 12)
    9628             :     {
    9629           0 :         throw snap_logic_exception(QString("last_day_of_month called with %1 as the month number").arg(month));
    9630             :     }
    9631             : 
    9632           0 :     if(month == 2)
    9633             :     {
    9634             :         // special case for February
    9635             :         //
    9636             :         // The time when people switch from Julian to Gregorian is country
    9637             :         // dependent, Great Britain changed on September 2, 1752, but some
    9638             :         // countries changed as late as 1952...
    9639             :         //
    9640             :         // For now, we use the GB date. Once we have a valid way to handle
    9641             :         // this with the locale, we can look into updating the code. That
    9642             :         // being said, it should not matter too much because most dates on
    9643             :         // the Internet are past 2000.
    9644             :         //
    9645           0 :         if(year <= 1752)
    9646             :         {
    9647           0 :             return year % 4 == 0 ? 29 : 28;
    9648             :         }
    9649           0 :         return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) ? 29 : 28;
    9650             :     }
    9651             : 
    9652           0 :     if(month == 9 && year == 1752)
    9653             :     {
    9654             :         // we cannot handle this nice one here, days 3 to 13 are missing on
    9655             :         // this month... (to adjust the calendar all at once!)
    9656           0 :         throw snap_logic_exception(QString("last_day_of_month called with %1 as the year number").arg(year));
    9657             :     }
    9658             : 
    9659           0 :     return g_month_days[month - 1];
    9660             : }
    9661             : 
    9662             : 
    9663             : /** \brief Get a list of all language names.
    9664             :  *
    9665             :  * This function returns the current list of language names as defined
    9666             :  * in ISO639 and similar documents.
    9667             :  *
    9668             :  * The table returned ends with a nullptr entry. You may use it in this
    9669             :  * way:
    9670             :  *
    9671             :  * \code
    9672             :  * for(snap_child::language_name_t const *l(snap_child::get_languages()); l->f_name; ++l)
    9673             :  * \endcode
    9674             :  *
    9675             :  * See also https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
    9676             :  *
    9677             :  * \return A pointer to the table of languages.
    9678             :  */
    9679           0 : snap_child::language_name_t const * snap_child::get_languages()
    9680             : {
    9681           0 :     return g_language_names;
    9682             : }
    9683             : 
    9684             : 
    9685             : /** \brief Get a list of all country names.
    9686             :  *
    9687             :  * This function returns the current list of country names as defined in
    9688             :  * ISO3166 and similar documents.
    9689             :  *
    9690             :  * See also https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
    9691             :  *
    9692             :  * \return A pointer to the table of countries.
    9693             :  */
    9694           0 : snap_child::country_name_t const * snap_child::get_countries()
    9695             : {
    9696           0 :     return g_country_names;
    9697             : }
    9698             : 
    9699             : 
    9700             : /** \brief Send the backend_process() signal to all plugins.
    9701             :  *
    9702             :  * This function sends the server::backend_process() signal to all
    9703             :  * the currently registered plugins.
    9704             :  *
    9705             :  * This is called by the content plugin whenever the action is set
    9706             :  * to "snapbackend" which is the default when no action was specified
    9707             :  * and someone started the snapbackend process:
    9708             :  *
    9709             :  * \code
    9710             :  *      snapbackend
    9711             :  *        [or]
    9712             :  *      snapbackend --action snapbackend
    9713             :  * \endcode
    9714             :  */
    9715           0 : void snap_child::backend_process()
    9716             : {
    9717           0 :     server::pointer_t server(get_server());
    9718           0 :     server->backend_process();
    9719           0 : }
    9720             : 
    9721             : 
    9722             : /** \brief Send a PING message to the specified service.
    9723             :  *
    9724             :  * This function sends a PING message to the specified service.
    9725             :  * This is used to wake up a backend process after you saved
    9726             :  * data in the Cassandra cluster. That backend can then "slowly"
    9727             :  * process the data further.
    9728             :  *
    9729             :  * The message is sent using a UDP packet. It is sent to the
    9730             :  * Snap! Communicator running on the same server as this
    9731             :  * child.
    9732             :  *
    9733             :  * Remember that UDP is not reliable so we do not in any way
    9734             :  * guarantee that this goes anywhere. The function returns no
    9735             :  * feedback at all. We do not wait for a reply since at the time
    9736             :  * we send the message the listening server may be busy. The
    9737             :  * idea of this ping is just to make sure that if the backend is
    9738             :  * sleeping at the time, it wakes up sooner rather than later
    9739             :  * so it can immediately start processing the data we just added
    9740             :  * to Cassandra.
    9741             :  *
    9742             :  * The \p service_name is the name of the backend as it appears
    9743             :  * when you run the following command:
    9744             :  *
    9745             :  * \code
    9746             :  *      snapbackend --action list
    9747             :  * \endcode
    9748             :  *
    9749             :  * At time of writing, we have the following action backends:
    9750             :  *
    9751             :  * \li images::images
    9752             :  * \li list::listjournal
    9753             :  * \li list::pagelist
    9754             :  * \li sendmail::sendmail
    9755             :  *
    9756             :  * Note that the CRON like backend is not listed here. It does not itself
    9757             :  * use a name. However, the list of backend will be longer, only most of
    9758             :  * the other names are just functions (actions) that can be run once and
    9759             :  * not an actual backend that will run until disabled.
    9760             :  *
    9761             :  * For example, the "permissions::makeroot" is used to mark a registered
    9762             :  * use as a root user on Snap! That action does not accept a PING since
    9763             :  * it runs once and quits immediately.
    9764             :  *
    9765             :  * \param[in] service_name  The name of the backend (service) to ping.
    9766             :  */
    9767           0 : void snap_child::udp_ping(char const * service_name)
    9768             : {
    9769           0 :     server::pointer_t server(get_server());
    9770             : 
    9771             :     // the URI to be used with PING is the website URI, not the full page
    9772             :     // URI (otherwise it does not work as expected)
    9773             :     //
    9774           0 :     server->udp_ping_server(service_name, f_uri.get_website_uri());
    9775           0 : }
    9776             : 
    9777             : 
    9778             : /** \brief Check a tag
    9779             :  *
    9780             :  * This function determines whether the input named tag is an inline tag.
    9781             :  * Note that CSS can transform an inline tag in a block so the result of
    9782             :  * this function are only relatively correct.
    9783             :  *
    9784             :  * The function returns true for what is considered neutral tags. For example,
    9785             :  * the \<map\> and \<script\> tags are viewed as neutral. They do not affect
    9786             :  * the output just by their presence in the flow. (Although a script may
    9787             :  * affect the flow at run time by writing to it.)
    9788             :  *
    9789             :  * \param[in] tag   The name of the tag in a C-string.
    9790             :  * \param[in] length  Use -1 if \p tag is null terminated, otherwise the length of the string.
    9791             :  *
    9792             :  * \return true if the tag is considered to be inline by default.
    9793             :  */
    9794           0 : bool snap_child::tag_is_inline(char const * tag, int length)
    9795             : {
    9796           0 :     if(tag == nullptr)
    9797             :     {
    9798           0 :         throw snap_logic_exception("tag_is_inline() cannot be called with nullptr as the tag pointer");
    9799             :     }
    9800             : 
    9801           0 :     if(length < 0)
    9802             :     {
    9803           0 :         length = static_cast<int>(strlen(tag));
    9804             :     }
    9805             : 
    9806           0 :     switch(tag[0])
    9807             :     {
    9808           0 :     case 'a':
    9809             :         // <a>, <abbr>, <acronym>, <area>
    9810           0 :         if(length == 1
    9811           0 :         || strncmp(tag + 1, "bbr", length) == 0
    9812           0 :         || strncmp(tag + 1, "cronym", length) == 0   // deprecated in HTML 5
    9813           0 :         || strncmp(tag + 1, "rea", length) == 0)
    9814             :         {
    9815           0 :             return true;
    9816             :         }
    9817           0 :         break;
    9818             : 
    9819           0 :     case 'b':
    9820             :         // <b>, <basefont>, <bb>, <bdi>, <bdo>, <bgsound>, <big>, <blink>, <br>, <button>
    9821           0 :         if(length == 1
    9822           0 :         || strncmp(tag + 1, "asefont", length) == 0      // <basefont> deprecated in HTML 4.01
    9823           0 :         || (length == 2 && (tag[1] == 'b' || tag[1] == 'r'))
    9824           0 :         || strncmp(tag + 1, "di", length) == 0
    9825           0 :         || strncmp(tag + 1, "do", length) == 0
    9826           0 :         || strncmp(tag + 1, "ig", length) == 0           // <big> deprecated in HTML 5
    9827           0 :         || strncmp(tag + 1, "gsound", length) == 0       // <bgsound> deprecated in HTML 5
    9828           0 :         || strncmp(tag + 1, "link", length) == 0         // <blink> deprecated in HTML 5
    9829           0 :         || strncmp(tag + 1, "utton", length) == 0)
    9830             :         {
    9831           0 :             return true;
    9832             :         }
    9833           0 :         break;
    9834             : 
    9835           0 :     case 'c':
    9836             :         // <cite>, <code>, <command>
    9837           0 :         if(strncmp(tag + 1, "ite", length) == 0
    9838           0 :         || strncmp(tag + 1, "ode", length) == 0
    9839           0 :         || strncmp(tag + 1, "ommand", length) == 0)
    9840             :         {
    9841           0 :             return true;
    9842             :         }
    9843           0 :         break;
    9844             : 
    9845           0 :     case 'd':
    9846             :         // <data>, <del>, <dfn>
    9847           0 :         if(strncmp(tag + 1, "ata", length) == 0
    9848           0 :         || strncmp(tag + 1, "el", length) == 0
    9849           0 :         || strncmp(tag + 1, "fn", length) == 0)
    9850             :         {
    9851           0 :             return true;
    9852             :         }
    9853           0 :         break;
    9854             : 
    9855           0 :     case 'e':
    9856             :         // <em>
    9857           0 :         if(length == 2 && tag[1] == 'm')
    9858             :         {
    9859           0 :             return true;
    9860             :         }
    9861           0 :         break;
    9862             : 
    9863           0 :     case 'f':
    9864             :         // <font>
    9865           0 :         if(strncmp(tag + 1, "ont", length) == 0)         // <font> deprecated in HTML 4.01
    9866             :         {
    9867           0 :             return true;
    9868             :         }
    9869           0 :         break;
    9870             : 
    9871           0 :     case 'i':
    9872             :         // <i>, <img>, <ins>, <isindex>
    9873           0 :         if(tag[1] == '\0'
    9874           0 :         || strncmp(tag + 1, "mg", length) == 0
    9875           0 :         || strncmp(tag + 1, "ns", length) == 0
    9876           0 :         || strncmp(tag + 1, "sindex", length) == 0)      // <isindex> deprecated in HTML 4.01
    9877             :         {
    9878           0 :             return true;
    9879             :         }
    9880           0 :         break;
    9881             : 
    9882           0 :     case 'k':
    9883             :         // <kbd>
    9884           0 :         if(tag[1] == 'b' && tag[2] == 'd' && tag[3] == '\0')
    9885             :         {
    9886           0 :             return true;
    9887             :         }
    9888           0 :         break;
    9889             : 
    9890           0 :     case 'l':
    9891             :         // <label>
    9892           0 :         if(strncmp(tag + 1, "abel", length) == 0)
    9893             :         {
    9894           0 :             return true;
    9895             :         }
    9896           0 :         break;
    9897             : 
    9898           0 :     case 'm':
    9899             :         // <map>, <mark>, <meter>
    9900           0 :         if((tag[1] == 'a' && tag[2] == 'p' && tag[3] == '\0')
    9901           0 :         || strncmp(tag + 1, "ark", length) == 0
    9902           0 :         || strncmp(tag + 1, "eter", length) == 0)
    9903             :         {
    9904           0 :             return true;
    9905             :         }
    9906           0 :         break;
    9907             : 
    9908           0 :     case 'n':
    9909             :         // <nobr>
    9910           0 :         if(strncmp(tag + 1, "obr", length) == 0)     // <nobr> deprecated in HTML 5
    9911             :         {
    9912           0 :             return true;
    9913             :         }
    9914           0 :         break;
    9915             : 
    9916           0 :     case 'p':
    9917             :         // <progress>
    9918           0 :         if(strncmp(tag + 1, "rogress", length) == 0)
    9919             :         {
    9920           0 :             return true;
    9921             :         }
    9922           0 :         break;
    9923             : 
    9924           0 :     case 'q':
    9925             :         // <q>
    9926           0 :         if(tag[1] == '\0')
    9927             :         {
    9928           0 :             return true;
    9929             :         }
    9930           0 :         break;
    9931             : 
    9932           0 :     case 'r':
    9933             :         // <rb>, <rp>, <rt>, <rtc>, <ruby>
    9934           0 :         if(((tag[1] == 'b' || tag[1] == 'p' || tag[1] == 't') && tag[2] == '\0')
    9935           0 :         || (tag[1] == 't' || tag[2] == 'c' || tag[3] == '\0')
    9936           0 :         || strncmp(tag + 1, "uby", length) == 0)
    9937             :         {
    9938           0 :             return true;
    9939             :         }
    9940           0 :         break;
    9941             : 
    9942           0 :     case 's':
    9943             :         // <s>, <samp>, <script>, <select>, <small>, <span>, <strike>, <strong>, <style>, <sub>, <sup>
    9944           0 :         if(length == 1                              // <s> deprecated in HTML 4.01
    9945           0 :         || strncmp(tag + 1, "amp", length) == 0
    9946           0 :         || strncmp(tag + 1, "cript", length) == 0
    9947           0 :         || strncmp(tag + 1, "elect", length) == 0
    9948           0 :         || strncmp(tag + 1, "mall", length) == 0
    9949           0 :         || strncmp(tag + 1, "pan", length) == 0
    9950           0 :         || strncmp(tag + 1, "trike", length) == 0   // <strike> depreacated in HTML 4.01
    9951           0 :         || strncmp(tag + 1, "trong", length) == 0
    9952           0 :         || strncmp(tag + 1, "tyle", length) == 0
    9953           0 :         || strncmp(tag + 1, "ub", length) == 0
    9954           0 :         || strncmp(tag + 1, "up", length) == 0)
    9955             :         {
    9956           0 :             return true;
    9957             :         }
    9958           0 :         break;
    9959             : 
    9960           0 :     case 't':
    9961             :         // <time>, <tt>
    9962           0 :         if(strncmp(tag + 1, "ime", length) == 0
    9963           0 :         || (length == 2 && tag[1] == 't'))      // <tt> deprecated in HTML 5
    9964             :         {
    9965           0 :             return true;
    9966             :         }
    9967           0 :         break;
    9968             : 
    9969           0 :     case 'u':
    9970             :         // <u>
    9971           0 :         if(length == 1)                  // <u> deprecated in HTML 4.01
    9972             :         {
    9973           0 :             return true;
    9974             :         }
    9975           0 :         break;
    9976             : 
    9977           0 :     case 'v':
    9978             :         // <var>
    9979           0 :         if(length == 3 && tag[1] == 'a' && tag[2] == 'r')
    9980             :         {
    9981           0 :             return true;
    9982             :         }
    9983           0 :         break;
    9984             : 
    9985           0 :     case 'w':
    9986             :         // <wbr>
    9987           0 :         if(length == 3 && tag[1] == 'b' && tag[2] == 'r')  // somehow <wbr> is marked as obsolete in HTML 5... TBD
    9988             :         {
    9989           0 :             return true;
    9990             :         }
    9991           0 :         break;
    9992             : 
    9993             :     }
    9994             : 
    9995           0 :     return false;
    9996             : }
    9997             : 
    9998             : 
    9999             : /** \brief Debug function
   10000             :  *
   10001             :  * This function is just for debug purposes. It can be used to make sure
   10002             :  * that the resources you are expecting to exist are indeed available.
   10003             :  * You may find it with the wrong path, for example.
   10004             :  *
   10005             :  * \param[in] out  An output stream such as std::err.
   10006             :  */
   10007           0 : void snap_child::show_resources(std::ostream & out)
   10008             : {
   10009           0 :     QDirIterator it(":", QDirIterator::Subdirectories);
   10010           0 :     while(it.hasNext())
   10011             :     {
   10012           0 :         out << it.next() << "\n";
   10013             :     }
   10014           0 : }
   10015             : 
   10016             : 
   10017           0 : void snap_child::extract_resource(QString const & resource_name, QString const & output_filename)
   10018             : {
   10019             :     // TBD: should we make sure this is a resource?
   10020           0 :     QFile resource(resource_name);
   10021           0 :     if(!resource.open(QIODevice::ReadOnly))
   10022             :     {
   10023           0 :         die(snap_child::http_code_t::HTTP_CODE_INTERNAL_SERVER_ERROR,
   10024             :                 "Resource Unavailable",
   10025           0 :                 QString("Somehow resource \"%1\" could not be loaded.").arg(resource_name),
   10026             :                 "The resource name is wrong, maybe the ':' is missing at the start?");
   10027           0 :         NOTREACHED();
   10028             :     }
   10029             : 
   10030             :     // read the entire file
   10031           0 :     QByteArray const data(resource.readAll());
   10032             : 
   10033             :     // create the output file
   10034           0 :     QFile out(output_filename);
   10035           0 :     if(!out.open(QIODevice::WriteOnly))
   10036             :     {
   10037           0 :         die(snap_child::http_code_t::HTTP_CODE_INTERNAL_SERVER_ERROR,
   10038             :                 "I/O Error",
   10039           0 :                 QString("Somehow we could not create output file \"%1\".").arg(output_filename),
   10040             :                 "The resource name is wrong, maybe the ':' is missing at the start?");
   10041           0 :         NOTREACHED();
   10042             :     }
   10043             : 
   10044             :     // save the resource
   10045           0 :     out.write(data);
   10046             : 
   10047             :     // Qt closes both files automatically
   10048           0 : }
   10049             : 
   10050             : 
   10051           6 : } // namespace snap
   10052             : 
   10053             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13