Example #1
0
 public function write_data($sqlite3)
 {
     /**
      * Write data to database table "fqdns".
      */
     if ($this->fqdn !== '') {
         if (($fid = $sqlite3->querySingle('SELECT fid FROM fqdns WHERE fqdn = \'' . $this->fqdn . '\'')) === false) {
             output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
         }
         if (is_null($fid)) {
             $sqlite3->exec('INSERT INTO fqdns (fid, fqdn, tld) VALUES (NULL, \'' . $this->fqdn . '\', \'' . $this->tld . '\')') or output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
             $fid = $sqlite3->lastInsertRowID();
         }
     }
     /**
      * Write data to database tables "urls" and "uid_urls".
      */
     if (($lid = $sqlite3->querySingle('SELECT lid FROM urls WHERE url = \'' . $sqlite3->escapeString($this->url) . '\'')) === false) {
         output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
     }
     if (is_null($lid)) {
         $sqlite3->exec('INSERT INTO urls (lid, url' . ($this->fqdn !== '' ? ', fid' : '') . ') VALUES (NULL, \'' . $sqlite3->escapeString($this->url) . '\'' . ($this->fqdn !== '' ? ', ' . $fid : '') . ')') or output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
         $lid = $sqlite3->lastInsertRowID();
     }
     foreach ($this->uses as $key => $values) {
         $sqlite3->exec('INSERT INTO uid_urls (uid, lid, datetime) VALUES ((SELECT uid FROM uid_details WHERE csnick = \'' . $values[1] . '\'), ' . $lid . ', DATETIME(\'' . $values[0] . '\'))') or output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
     }
 }
Example #2
0
 public function write_data($sqlite3)
 {
     /**
      * Write data to database table "words".
      */
     $sqlite3->exec('INSERT OR IGNORE INTO words (word, length, total) VALUES (\'' . $sqlite3->escapeString($this->word) . '\', ' . $this->length . ', ' . $this->total . ')') or output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
     $sqlite3->exec('UPDATE words SET total = total + ' . $this->total . ' WHERE CHANGES() = 0 AND word = \'' . $sqlite3->escapeString($this->word) . '\'') or output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
 }
Example #3
0
 /**
  * Write data to database tables "topics" and "uid_topics".
  */
 public function write_data($sqlite3)
 {
     if (($tid = $sqlite3->querySingle('SELECT tid FROM topics WHERE topic = \'' . $sqlite3->escapeString($this->topic) . '\'')) === false) {
         output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
     }
     if (is_null($tid)) {
         $sqlite3->exec('INSERT INTO topics (tid, topic) VALUES (NULL, \'' . $sqlite3->escapeString($this->topic) . '\')') or output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
         $tid = $sqlite3->lastInsertRowID();
     }
     foreach ($this->uses as $key => $values) {
         $sqlite3->exec('INSERT INTO uid_topics (uid, tid, datetime) VALUES ((SELECT uid FROM uid_details WHERE csnick = \'' . $values[1] . '\'), ' . $tid . ', DATETIME(\'' . $values[0] . '\'))') or output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
     }
 }
 /**
  * Parse a line for various chat data.
  */
 protected function parse_line($line)
 {
     /**
      * "Normal" lines.
      */
     if (preg_match('/^\\[(?<time>\\d{2}:\\d{2}(:\\d{2})?)\\] (?<nick>\\S+): (?<line>.+)$/', $line, $matches)) {
         $this->set_normal($matches['time'], $matches['nick'], $matches['line']);
         /**
          * "Join" lines.
          */
     } elseif (preg_match('/^\\[(?<time>\\d{2}:\\d{2}(:\\d{2})?)\\] (?<nick>\\S+) has joined the channel$/', $line, $matches)) {
         $this->set_join($matches['time'], $matches['nick']);
         /**
          * "Part" lines.
          */
     } elseif (preg_match('/^\\[(?<time>\\d{2}:\\d{2}(:\\d{2})?)\\] (?<nick>\\S+) has left the channel$/', $line, $matches)) {
         $this->set_part($matches['time'], $matches['nick']);
         /**
          * Skip everything else.
          */
     } elseif ($line !== '') {
         output::output('debug', __METHOD__ . '(): skipping line ' . $this->linenum . ': \'' . $line . '\'');
     }
 }
Example #5
0
 private function make_table_people_timeofday($sqlite3)
 {
     /**
      * Only create the table if there is activity from users other than bots and
      * excluded users.
      */
     if (($total = $sqlite3->querySingle('SELECT SUM(l_total) FROM ruid_lines JOIN uid_details ON ruid_lines.ruid = uid_details.uid WHERE status NOT IN (3,4)')) === false) {
         output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
     }
     if (empty($total)) {
         return null;
     }
     $high_value = 0;
     $times = ['night', 'morning', 'afternoon', 'evening'];
     foreach ($times as $time) {
         $query = $sqlite3->query('SELECT csnick, l_' . $time . ' FROM ruid_lines JOIN uid_details ON ruid_lines.ruid = uid_details.uid WHERE status NOT IN (3,4) AND l_' . $time . ' != 0 ORDER BY l_' . $time . ' DESC, ruid_lines.ruid ASC LIMIT ' . $this->maxrows_people_timeofday) or output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
         $i = 0;
         while ($result = $query->fetchArray(SQLITE3_ASSOC)) {
             $i++;
             ${$time}[$i]['lines'] = $result['l_' . $time];
             ${$time}[$i]['user'] = $result['csnick'];
             if (${$time}[$i]['lines'] > $high_value) {
                 $high_value = ${$time}[$i]['lines'];
             }
         }
     }
     $tr0 = '<colgroup><col class="pos"><col class="c"><col class="c"><col class="c"><col class="c">';
     $tr1 = '<tr><th colspan="5">Most Talkative People by Time of Day';
     $tr2 = '<tr><td class="pos"><td class="k">Night<br>0h - 5h<td class="k">Morning<br>6h - 11h<td class="k">Afternoon<br>12h - 17h<td class="k">Evening<br>18h - 23h';
     $trx = '';
     for ($i = 1; $i <= $this->maxrows_people_timeofday; $i++) {
         if (!isset($night[$i]['lines']) && !isset($morning[$i]['lines']) && !isset($afternoon[$i]['lines']) && !isset($evening[$i]['lines'])) {
             break;
         }
         $trx .= '<tr><td class="pos">' . $i;
         foreach ($times as $time) {
             if (!isset(${$time}[$i]['lines'])) {
                 $trx .= '<td class="v">';
             } else {
                 $width = round(${$time}[$i]['lines'] / $high_value * 190);
                 if ($width !== (double) 0) {
                     $trx .= '<td class="v">' . htmlspecialchars(${$time}[$i]['user']) . ' - ' . number_format(${$time}[$i]['lines']) . '<br><div class="' . $this->color[$time] . '" style="width:' . $width . 'px"></div>';
                 } else {
                     $trx .= '<td class="v">' . htmlspecialchars(${$time}[$i]['user']) . ' - ' . number_format(${$time}[$i]['lines']);
                 }
             }
         }
     }
     return '<table class="ppl-tod">' . $tr0 . $tr1 . $tr2 . $trx . '</table>' . "\n";
 }
Example #6
0
 /**
  * Read the settings from the configuration file and put them into $settings[]
  * so they can be passed along to other classes.
  */
 private function read_config($file)
 {
     if (($rp = realpath($file)) === false) {
         output::output('critical', __METHOD__ . '(): no such file: \'' . $file . '\'');
     }
     if (($fp = fopen($rp, 'rb')) === false) {
         output::output('critical', __METHOD__ . '(): failed to open file: \'' . $rp . '\'');
     }
     while (!feof($fp)) {
         $line = trim(fgets($fp));
         if (preg_match('/^\\s*(?<setting>\\w+)\\s*=\\s*"\\s*(?<value>([^\\s"]+(\\s+[^\\s"]+)*))\\s*"/', $line, $matches)) {
             $this->settings[$matches['setting']] = $matches['value'];
         }
     }
     fclose($fp);
     /**
      * Exit if any crucial setting is missing.
      */
     foreach ($this->settings_list_required as $key) {
         if (!array_key_exists($key, $this->settings)) {
             output::output('critical', __METHOD__ . '(): missing required setting: \'' . $key . '\'');
         }
     }
     /**
      * If set, override variables listed in $settings_list[].
      */
     foreach ($this->settings_list as $key => $type) {
         if (!array_key_exists($key, $this->settings)) {
             continue;
         }
         /**
          * Do some explicit type casting because everything is initially a string.
          */
         if ($type === 'string') {
             $this->{$key} = $this->settings[$key];
         } elseif ($type === 'int' && preg_match('/^\\d+$/', $this->settings[$key])) {
             $this->{$key} = (int) $this->settings[$key];
         } elseif ($type === 'bool') {
             if (strtolower($this->settings[$key]) === 'true') {
                 $this->{$key} = true;
             } elseif (strtolower($this->settings[$key]) === 'false') {
                 $this->{$key} = false;
             }
         }
     }
 }
 /**
  * Parse a line for various chat data.
  */
 protected function parse_line($line)
 {
     /**
      * "Normal" lines.
      */
     if (preg_match('/^\\S{3} \\d{2} (?<time>\\d{2}:\\d{2}(:\\d{2})?) <(?<nick>\\S+)> (?<line>.+)$/', $line, $matches)) {
         $this->set_normal($matches['time'], $matches['nick'], $matches['line']);
         /**
          * "Join" lines.
          */
     } elseif (preg_match('/^\\S{3} \\d{2} (?<time>\\d{2}:\\d{2}(:\\d{2})?) \\* (?<nick>\\S+) \\(\\S+\\) has joined [#&!+]\\S+$/', $line, $matches)) {
         $this->set_join($matches['time'], $matches['nick']);
         /**
          * "Quit" lines.
          */
     } elseif (preg_match('/^\\S{3} \\d{2} (?<time>\\d{2}:\\d{2}(:\\d{2})?) \\* (?<nick>\\S+) has quit \\(.*\\)$/', $line, $matches)) {
         $this->set_quit($matches['time'], $matches['nick']);
         /**
          * "Mode" lines.
          */
     } elseif (preg_match('/^\\S{3} \\d{2} (?<time>\\d{2}:\\d{2}(:\\d{2})?) \\* (?<nick_performing>\\S+) (?<modesign>gives|removes) (?<mode>channel operator status|voice) (to|from) (?<nicks_undergoing>\\S+( \\S+)*)$/', $line, $matches)) {
         $nicks_undergoing = explode(' ', $matches['nicks_undergoing']);
         if ($matches['modesign'] === 'gives') {
             $modesign = '+';
         } else {
             $modesign = '-';
         }
         if ($matches['mode'] === 'channel operator status') {
             $mode = 'o';
         } else {
             $mode = 'v';
         }
         foreach ($nicks_undergoing as $nick_undergoing) {
             $this->set_mode($matches['time'], $matches['nick_performing'], $nick_undergoing, $modesign . $mode);
         }
         /**
          * "Nickchange" lines.
          */
     } elseif (preg_match('/^\\S{3} \\d{2} (?<time>\\d{2}:\\d{2}(:\\d{2})?) \\* (?<nick_performing>\\S+) is now known as (?<nick_undergoing>\\S+)$/', $line, $matches)) {
         $this->set_nickchange($matches['time'], $matches['nick_performing'], $matches['nick_undergoing']);
         /**
          * "Part" lines.
          */
     } elseif (preg_match('/^\\S{3} \\d{2} (?<time>\\d{2}:\\d{2}(:\\d{2})?) \\* (?<nick>\\S+) \\(\\S+\\) has left [#&!+]\\S+( \\(.*\\))?$/', $line, $matches)) {
         $this->set_part($matches['time'], $matches['nick']);
         /**
          * "Topic" lines.
          */
     } elseif (preg_match('/^\\S{3} \\d{2} (?<time>\\d{2}:\\d{2}(:\\d{2})?) \\* (?<nick>\\S+) has changed the topic to: (?<line>.+)$/', $line, $matches)) {
         $this->set_topic($matches['time'], $matches['nick'], $matches['line']);
         /**
          * "Kick" lines.
          */
     } elseif (preg_match('/^\\S{3} \\d{2} (?<time>\\d{2}:\\d{2}(:\\d{2})?) \\* (?<line>(?<nick_performing>\\S+) has kicked (?<nick_undergoing>\\S+) from [#&!+]\\S+ \\(.*\\))$/', $line, $matches)) {
         $this->set_kick($matches['time'], $matches['nick_performing'], $matches['nick_undergoing'], $matches['line']);
         /**
          * Skip everything else.
          */
     } elseif ($line !== '') {
         output::output('debug', __METHOD__ . '(): skipping line ' . $this->linenum . ': \'' . $line . '\'');
     }
 }
Example #8
0
 public function write_data($sqlite3)
 {
     /**
      * Write data to database table "uid_details".
      */
     if (($result = $sqlite3->querySingle('SELECT uid, firstseen FROM uid_details WHERE csnick = \'' . $sqlite3->escapeString($this->csnick) . '\'', true)) === false) {
         output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
     }
     if (empty($result)) {
         $sqlite3->exec('INSERT INTO uid_details (uid, csnick' . ($this->firstseen !== '' ? ', firstseen, lastseen' : '') . ') VALUES (NULL, \'' . $sqlite3->escapeString($this->csnick) . '\'' . ($this->firstseen !== '' ? ', DATETIME(\'' . $this->firstseen . '\'), DATETIME(\'' . $this->lastseen . '\')' : '') . ')') or output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
         $uid = $sqlite3->lastInsertRowID();
     } else {
         $uid = $result['uid'];
         /**
          * Only update $firstseen if the value stored in the database is zero. We're
          * parsing logs in chronological order so the stored value of $firstseen can
          * never be lower and the value of $lastseen can never be higher than the parsed
          * values. (We are not going out of our way to deal with possible DST nonsense.)
          * Secondly, only update $csnick if the nick was seen. We want to avoid it from
          * being overwritten by a lowercase $prevnick (streak code) or weirdly cased
          * nick due to a slap.
          */
         if ($this->firstseen !== '') {
             $sqlite3->exec('UPDATE uid_details SET csnick = \'' . $sqlite3->escapeString($this->csnick) . '\'' . ($result['firstseen'] === '0000-00-00 00:00:00' ? ', firstseen = DATETIME(\'' . $this->firstseen . '\')' : '') . ', lastseen = DATETIME(\'' . $this->lastseen . '\') WHERE uid = ' . $uid) or output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
         }
     }
     /**
      * Write data to database table "uid_activity".
      */
     if ($this->l_total !== 0) {
         $queryparts = $this->get_queryparts($sqlite3, ['l_night', 'l_morning', 'l_afternoon', 'l_evening', 'l_total']);
         $sqlite3->exec('INSERT OR IGNORE INTO uid_activity (uid, date, ' . implode(', ', $queryparts['columns']) . ') VALUES (' . $uid . ', \'' . substr($this->firstseen, 0, 10) . '\', ' . implode(', ', $queryparts['values']) . ')') or output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
         $sqlite3->exec('UPDATE uid_activity SET ' . implode(', ', $queryparts['update-assignments']) . ' WHERE CHANGES() = 0 AND uid = ' . $uid . ' AND date = \'' . substr($this->firstseen, 0, 10) . '\'') or output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
     }
     /**
      * Write data to database table "uid_events".
      */
     $queryparts = $this->get_queryparts($sqlite3, ['m_op', 'm_opped', 'm_voice', 'm_voiced', 'm_deop', 'm_deopped', 'm_devoice', 'm_devoiced', 'joins', 'parts', 'quits', 'kicks', 'kicked', 'nickchanges', 'topics', 'ex_kicks', 'ex_kicked']);
     if (!empty($queryparts)) {
         $sqlite3->exec('INSERT OR IGNORE INTO uid_events (uid, ' . implode(', ', $queryparts['columns']) . ') VALUES (' . $uid . ', ' . implode(', ', $queryparts['values']) . ')') or output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
         $sqlite3->exec('UPDATE uid_events SET ' . implode(', ', $queryparts['update-assignments']) . ' WHERE CHANGES() = 0 AND uid = ' . $uid) or output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
     }
     /**
      * Try to pick the longest unique line from each of the quote stacks.
      */
     $types = ['ex_actions', 'ex_uppercased', 'ex_exclamations', 'ex_questions', 'quote'];
     foreach ($types as $type) {
         if (empty($this->{$type . '_stack'})) {
             continue;
         }
         /**
          * rsort() sorts a multidimensional array on the first value of each contained
          * array, highest to lowest.
          */
         rsort($this->{$type . '_stack'});
         $this->{$type} = $this->{$type . '_stack'}[0]['line'];
         if (($type === 'ex_questions' || $type === 'ex_exclamations') && $this->{$type} === $this->ex_uppercased && count($this->{$type . '_stack'}) > 1) {
             for ($i = 1, $j = count($this->{$type . '_stack'}); $i < $j; $i++) {
                 if ($this->{$type . '_stack'}[$i]['line'] !== $this->ex_uppercased) {
                     $this->{$type} = $this->{$type . '_stack'}[$i]['line'];
                     break;
                 }
             }
         } elseif ($type === 'quote' && ($this->quote === $this->ex_uppercased || $this->quote === $this->ex_exclamations || $this->quote === $this->ex_questions) && count($this->quote_stack) > 1) {
             for ($i = 1, $j = count($this->quote_stack); $i < $j; $i++) {
                 if ($this->quote_stack[$i]['line'] !== $this->ex_uppercased && $this->quote_stack[$i]['line'] !== $this->ex_exclamations && $this->quote_stack[$i]['line'] !== $this->ex_questions) {
                     $this->quote = $this->quote_stack[$i]['line'];
                     break;
                 }
             }
         }
     }
     /**
      * Write data to database table "uid_lines".
      */
     $queryparts = $this->get_queryparts($sqlite3, ['l_00', 'l_01', 'l_02', 'l_03', 'l_04', 'l_05', 'l_06', 'l_07', 'l_08', 'l_09', 'l_10', 'l_11', 'l_12', 'l_13', 'l_14', 'l_15', 'l_16', 'l_17', 'l_18', 'l_19', 'l_20', 'l_21', 'l_22', 'l_23', 'l_night', 'l_morning', 'l_afternoon', 'l_evening', 'l_total', 'l_mon_night', 'l_mon_morning', 'l_mon_afternoon', 'l_mon_evening', 'l_tue_night', 'l_tue_morning', 'l_tue_afternoon', 'l_tue_evening', 'l_wed_night', 'l_wed_morning', 'l_wed_afternoon', 'l_wed_evening', 'l_thu_night', 'l_thu_morning', 'l_thu_afternoon', 'l_thu_evening', 'l_fri_night', 'l_fri_morning', 'l_fri_afternoon', 'l_fri_evening', 'l_sat_night', 'l_sat_morning', 'l_sat_afternoon', 'l_sat_evening', 'l_sun_night', 'l_sun_morning', 'l_sun_afternoon', 'l_sun_evening', 'urls', 'words', 'characters', 'monologues', 'slaps', 'slapped', 'exclamations', 'questions', 'actions', 'uppercased', 'quote', 'ex_exclamations', 'ex_questions', 'ex_actions', 'ex_uppercased']);
     if (!empty($queryparts)) {
         $sqlite3->exec('INSERT OR IGNORE INTO uid_lines (uid, ' . implode(', ', $queryparts['columns']) . ($this->lasttalked !== '' ? ', lasttalked' : '') . ') VALUES (' . $uid . ', ' . implode(', ', $queryparts['values']) . ($this->lasttalked !== '' ? ', DATETIME(\'' . $this->lasttalked . '\')' : '') . ')') or output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
         $sqlite3->exec('UPDATE uid_lines SET ' . implode(', ', $queryparts['update-assignments']) . ($this->lasttalked !== '' ? ', lasttalked = DATETIME(\'' . $this->lasttalked . '\')' : '') . ' WHERE CHANGES() = 0 AND uid = ' . $uid) or output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
         /**
          * Update $topmonologue separately as we want to keep the highest value instead
          * of the sum.
          */
         if ($this->topmonologue !== 0) {
             if (($topmonologue = $sqlite3->querySingle('SELECT topmonologue FROM uid_lines WHERE uid = ' . $uid)) === false) {
                 output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
             }
             if ($this->topmonologue > $topmonologue) {
                 $sqlite3->exec('UPDATE uid_lines SET topmonologue = ' . $this->topmonologue . ' WHERE uid = ' . $uid) or output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
             }
         }
     }
     /**
      * Write data to database table "uid_smileys".
      */
     $queryparts = $this->get_queryparts($sqlite3, ['s_01', 's_02', 's_03', 's_04', 's_05', 's_06', 's_07', 's_08', 's_09', 's_10', 's_11', 's_12', 's_13', 's_14', 's_15', 's_16', 's_17', 's_18', 's_19', 's_20', 's_21', 's_22', 's_23', 's_24', 's_25', 's_26', 's_27', 's_28', 's_29', 's_30', 's_31', 's_32', 's_33', 's_34', 's_35', 's_36', 's_37', 's_38', 's_39', 's_40', 's_41', 's_42', 's_43', 's_44', 's_45', 's_46', 's_47', 's_48', 's_49', 's_50']);
     if (!empty($queryparts)) {
         $sqlite3->exec('INSERT OR IGNORE INTO uid_smileys (uid, ' . implode(', ', $queryparts['columns']) . ') VALUES (' . $uid . ', ' . implode(', ', $queryparts['values']) . ')') or output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
         $sqlite3->exec('UPDATE uid_smileys SET ' . implode(', ', $queryparts['update-assignments']) . ' WHERE CHANGES() = 0 AND uid = ' . $uid) or output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
     }
 }
 /**
  * Parse a line for various chat data.
  */
 protected function parse_line($line)
 {
     /**
      * "Normal" lines.
      */
     if (preg_match('/^\\d{4}-\\d{2}-\\d{2}T(?<time>\\d{2}:\\d{2}:\\d{2}) <(?<nick>\\S+)> (?<line>.+)$/', $line, $matches)) {
         $this->set_normal($matches['time'], $matches['nick'], $matches['line']);
         /**
          * "Join" lines.
          */
     } elseif (preg_match('/^\\d{4}-\\d{2}-\\d{2}T(?<time>\\d{2}:\\d{2}:\\d{2}) \\*\\*\\* (?<nick>\\S+) has joined [#&!+]\\S+$/', $line, $matches)) {
         $this->set_join($matches['time'], $matches['nick']);
         /**
          * "Quit" lines.
          */
     } elseif (preg_match('/^\\d{4}-\\d{2}-\\d{2}T(?<time>\\d{2}:\\d{2}:\\d{2}) \\*\\*\\* (?<nick>\\S+) has quit IRC$/', $line, $matches)) {
         $this->set_quit($matches['time'], $matches['nick']);
         /**
          * "Mode" lines.
          */
     } elseif (preg_match('/^\\d{4}-\\d{2}-\\d{2}T(?<time>\\d{2}:\\d{2}:\\d{2}) \\*\\*\\* (?<nick_performing>\\S+) sets mode: (?<modes>[-+][ov]+([-+][ov]+)?) (?<nicks_undergoing>\\S+( \\S+)*)$/', $line, $matches)) {
         $modenum = 0;
         $nicks_undergoing = explode(' ', $matches['nicks_undergoing']);
         for ($i = 0, $j = strlen($matches['modes']); $i < $j; $i++) {
             $mode = substr($matches['modes'], $i, 1);
             if ($mode === '-' || $mode === '+') {
                 $modesign = $mode;
             } else {
                 $this->set_mode($matches['time'], $matches['nick_performing'], $nicks_undergoing[$modenum], $modesign . $mode);
                 $modenum++;
             }
         }
         /**
          * "Action" and "slap" lines.
          */
     } elseif (preg_match('/^\\d{4}-\\d{2}-\\d{2}T(?<time>\\d{2}:\\d{2}:\\d{2}) \\* (?<line>(?<nick_performing>\\S+) ((?<slap>[sS][lL][aA][pP][sS]( (?<nick_undergoing>\\S+)( .+)?)?)|(.+)))$/', $line, $matches)) {
         if (!empty($matches['slap'])) {
             $this->set_slap($matches['time'], $matches['nick_performing'], !empty($matches['nick_undergoing']) ? $matches['nick_undergoing'] : null);
         }
         $this->set_action($matches['time'], $matches['nick_performing'], $matches['line']);
         /**
          * "Nickchange" lines.
          */
     } elseif (preg_match('/^\\d{4}-\\d{2}-\\d{2}T(?<time>\\d{2}:\\d{2}:\\d{2}) \\*\\*\\* (?<nick_performing>\\S+) is now known as (?<nick_undergoing>\\S+)$/', $line, $matches)) {
         $this->set_nickchange($matches['time'], $matches['nick_performing'], $matches['nick_undergoing']);
         /**
          * "Part" lines.
          */
     } elseif (preg_match('/^\\d{4}-\\d{2}-\\d{2}T(?<time>\\d{2}:\\d{2}:\\d{2}) \\*\\*\\* (?<nick>\\S+) has left [#&!+]\\S+$/', $line, $matches)) {
         $this->set_part($matches['time'], $matches['nick']);
         /**
          * "Topic" lines.
          */
     } elseif (preg_match('/^\\d{4}-\\d{2}-\\d{2}T(?<time>\\d{2}:\\d{2}:\\d{2}) \\*\\*\\* (?<nick>\\S+) changes topic to "(?<line>.+)"$/', $line, $matches)) {
         if ($matches['line'] !== ' ') {
             $this->set_topic($matches['time'], $matches['nick'], $matches['line']);
         }
         /**
          * "Kick" lines.
          */
     } elseif (preg_match('/^\\d{4}-\\d{2}-\\d{2}T(?<time>\\d{2}:\\d{2}:\\d{2}) \\*\\*\\* (?<line>(?<nick_undergoing>\\S+) was kicked by (?<nick_performing>\\S+) \\(.*\\))$/', $line, $matches)) {
         $this->set_kick($matches['time'], $matches['nick_performing'], $matches['nick_undergoing'], $matches['line']);
         /**
          * Skip everything else.
          */
     } elseif ($line !== '') {
         output::output('debug', __METHOD__ . '(): skipping line ' . $this->linenum . ': \'' . $line . '\'');
     }
 }
Example #10
0
 /**
  * Normalize and validate a URL and return an array with its elements.
  */
 public static function get_elements($url)
 {
     /**
      * Assemble the regular expression if not already done so.
      */
     if (self::$regexp_complete === '') {
         $domain = '(?<domain>[a-z0-9]([a-z0-9-]{0,61}?[a-z0-9]|[a-z0-9]{0,62})?(\\.[a-z0-9]([a-z0-9-]{0,61}?[a-z0-9]|[a-z0-9]{0,62})?)*)';
         $tld = '(?<tld>\\.[a-z0-9]([a-z0-9-]{0,61}?[a-z0-9]|[a-z0-9]{0,62})?)';
         $fqdn = '(?<fqdn>' . $domain . $tld . ')\\.?';
         $ipv4address = '(?<ipv4address>(25[0-5]|(2[0-4]|1[0-9]|[1-9])?[0-9])(\\.(25[0-5]|(2[0-4]|1[0-9]|[1-9])?[0-9])){3})';
         $port = '(?<port>(6553[0-5]|(655[0-2]|(65[0-4]|(6[0-4]|[1-5][0-9]|[1-9])[0-9]|[1-9])[0-9]|[1-9])?[0-9]))';
         $authority = '(?<authority>(' . $ipv4address . '|' . $fqdn . ')(:' . $port . ')?)';
         $unreserved = '[a-z0-9_.~-]';
         $pct_encoded = '%[0-9a-f]{2}';
         $sub_delims = '[!$&\'()*+,;=]';
         $pchar = '(' . $unreserved . '|' . $pct_encoded . '|' . $sub_delims . '|[:@])';
         $fragment = '(?<fragment>(#(' . $pchar . '|[\\/?])*)?)';
         $path = '(?<path>(\\/\\/?(' . $pchar . '+\\/?)*)?)';
         $query = '(?<query>(\\?(' . $pchar . '|[\\/?])*)?)';
         $scheme = '(?<scheme>https?:\\/\\/)';
         self::$regexp_callback = '/^' . $scheme . '?' . $authority . '/i';
         self::$regexp_complete = '/^(?<url>' . $scheme . '?' . $authority . $path . $query . $fragment . ')$/i';
         /**
          * Read "tlds-alpha-by-domain.txt" and put all TLDs in an array against which we
          * can validate found URLs. If the aforementioned file does not exist or fails
          * to be read, the TLD check will not be done. This would be an unexpected and
          * undesired exception though.
          */
         if (($tlds = file(__DIR__ . '/tlds-alpha-by-domain.txt')) === false) {
             output::output('notice', __METHOD__ . '(): failed to open file: \'tlds-alpha-by-domain.txt\', tld validation disabled');
         } else {
             foreach ($tlds as $tld) {
                 $tld = trim($tld);
                 if ($tld !== '' && strpos($tld, '#') === false) {
                     self::$valid_tlds[] = '.' . strtolower($tld);
                 }
             }
         }
     }
     /**
      * Convert scheme and authority to lower case.
      */
     $url = preg_replace_callback(self::$regexp_callback, function ($matches) {
         return strtolower($matches[0]);
     }, $url);
     /**
      * Validate and further process the URL.
      */
     if (!preg_match(self::$regexp_complete, $url, $matches)) {
         return false;
     }
     /**
      * Verify if the TLD is valid. If the validation array is empty we skip this
      * step.
      */
     if (!empty(self::$valid_tlds) && !empty($matches['tld']) && !in_array($matches['tld'], self::$valid_tlds)) {
         return false;
     }
     /**
      * The maximum allowed length of the FQDN (root domain excluded) is 254
      * characters.
      */
     if (strlen($matches['fqdn']) > 254) {
         return false;
     }
     /**
      * If the URL has no scheme, http:// is assumed. Update the elements.
      */
     if (empty($matches['scheme'])) {
         $matches['scheme'] = 'http://';
         $matches['url'] = 'http://' . $matches['url'];
     }
     /**
      * Create and return an array with all the elements of this URL.
      */
     $elements = ['url', 'scheme', 'authority', 'ipv4address', 'fqdn', 'domain', 'tld', 'path', 'query', 'fragment'];
     foreach ($elements as $element) {
         if (empty($matches[$element])) {
             /**
              * Always pass along an empty string for nonexistent elements.
              */
             $urldata[$element] = '';
         } else {
             $urldata[$element] = $matches[$element];
         }
     }
     /**
      * Make sure the only numeric element isn't passed along as a string.
      */
     if (empty($matches['port'])) {
         $urldata['port'] = 0;
     } else {
         $urldata['port'] = (int) $matches['port'];
     }
     return $urldata;
 }
 /**
  * Parse a line for various chat data.
  */
 protected function parse_line($line)
 {
     /**
      * "Normal" lines.
      */
     if (preg_match('/^\\[(?<time>\\d{2}:\\d{2}(:\\d{2})?)\\] <(?<nick>\\S+)> (?<line>.+)$/', $line, $matches)) {
         $this->set_normal($matches['time'], $matches['nick'], $matches['line']);
         /**
          * "Join" lines.
          */
     } elseif (preg_match('/^\\[(?<time>\\d{2}:\\d{2}(:\\d{2})?)\\] (?<nick>\\S+) \\(\\S+\\) joined [#&!+]\\S+\\.$/', $line, $matches)) {
         $this->set_join($matches['time'], $matches['nick']);
         /**
          * "Quit" lines.
          */
     } elseif (preg_match('/^\\[(?<time>\\d{2}:\\d{2}(:\\d{2})?)\\] (?<nick>\\S+) \\(\\S+\\) left irc:( .+)?$/', $line, $matches)) {
         $this->set_quit($matches['time'], $matches['nick']);
         /**
          * "Mode" lines.
          */
     } elseif (preg_match('/^\\[(?<time>\\d{2}:\\d{2}(:\\d{2})?)\\] [#&!+]\\S+: mode change \'(?<modes>[-+][ov]+([-+][ov]+)?) (?<nicks_undergoing>\\S+( \\S+)*)\' by (?<nick_performing>\\S+?)(!(\\S+)?)?$/', $line, $matches)) {
         $modenum = 0;
         $nicks_undergoing = explode(' ', $matches['nicks_undergoing']);
         for ($i = 0, $j = strlen($matches['modes']); $i < $j; $i++) {
             $mode = substr($matches['modes'], $i, 1);
             if ($mode === '-' || $mode === '+') {
                 $modesign = $mode;
             } else {
                 $this->set_mode($matches['time'], $matches['nick_performing'], $nicks_undergoing[$modenum], $modesign . $mode);
                 $modenum++;
             }
         }
         /**
          * "Action" and "slap" lines.
          */
     } elseif (preg_match('/^\\[(?<time>\\d{2}:\\d{2}(:\\d{2})?)\\] Action: (?<line>(?<nick_performing>\\S+) ((?<slap>[sS][lL][aA][pP][sS]( (?<nick_undergoing>\\S+)( .+)?)?)|(.+)))$/', $line, $matches)) {
         if (!empty($matches['slap'])) {
             $this->set_slap($matches['time'], $matches['nick_performing'], !empty($matches['nick_undergoing']) ? $matches['nick_undergoing'] : null);
         }
         $this->set_action($matches['time'], $matches['nick_performing'], $matches['line']);
         /**
          * "Nickchange" lines.
          */
     } elseif (preg_match('/^\\[(?<time>\\d{2}:\\d{2}(:\\d{2})?)\\] Nick change: (?<nick_performing>\\S+) -> (?<nick_undergoing>\\S+)$/', $line, $matches)) {
         $this->set_nickchange($matches['time'], $matches['nick_performing'], $matches['nick_undergoing']);
         /**
          * "Part" lines.
          */
     } elseif (preg_match('/^\\[(?<time>\\d{2}:\\d{2}(:\\d{2})?)\\] (?<nick>\\S+) \\(\\S+\\) left [#&!+]\\S+( \\(.*\\))?\\.$/', $line, $matches)) {
         $this->set_part($matches['time'], $matches['nick']);
         /**
          * "Topic" lines.
          */
     } elseif (preg_match('/^\\[(?<time>\\d{2}:\\d{2}(:\\d{2})?)\\] Topic changed on [#&!+]\\S+ by (?<nick>\\S+?)(!(\\S+)?)?: (?<line>.+)$/', $line, $matches)) {
         $this->set_topic($matches['time'], $matches['nick'], $matches['line']);
         /**
          * "Kick" lines.
          */
     } elseif (preg_match('/^\\[(?<time>\\d{2}:\\d{2}(:\\d{2})?)\\] (?<line>(?<nick_undergoing>\\S+) kicked from [#&!+]\\S+ by (?<nick_performing>\\S+):( .+)?)$/', $line, $matches)) {
         $this->set_kick($matches['time'], $matches['nick_performing'], $matches['nick_undergoing'], $matches['line']);
         /**
          * Eggdrop logs repeated lines (case insensitive matches) in the format: "Last message repeated NUM
          * time(s).". We process the previous line NUM times.
          */
     } elseif (preg_match('/^\\[(?<time>\\d{2}:\\d{2}(:\\d{2})?)\\] Last message repeated (?<num>\\d+) time\\(s\\)\\.$/', $line, $matches)) {
         /**
          * Prevent the parser from repeating a preceding repeat line. Also, skip processing if we find a
          * repeat line on the first line of the logfile. We can't look back across files.
          */
         if ($this->linenum === 1 || $this->repeatlock) {
             return null;
         }
         $this->linenum--;
         $this->repeatlock = true;
         output::output('debug', __METHOD__ . '(): repeating line ' . $this->linenum . ': ' . $matches['num'] . ' time' . ($matches['num'] !== '1' ? 's' : ''));
         for ($i = 1, $j = (int) $matches['num']; $i <= $j; $i++) {
             $this->parse_line($this->prevline);
         }
         $this->linenum++;
         $this->repeatlock = false;
         /**
          * Skip everything else.
          */
     } elseif ($line !== '') {
         output::output('debug', __METHOD__ . '(): skipping line ' . $this->linenum . ': \'' . $line . '\'');
     }
 }
Example #12
0
 /**
  * Make the alias with the most lines the new registered nick for the user or
  * bot it is linked to.
  */
 private function register_most_active_alias($sqlite3)
 {
     $query = $sqlite3->query('SELECT status, csnick, ruid, (SELECT uid_details.uid AS uid FROM uid_details JOIN uid_lines ON uid_details.uid = uid_lines.uid WHERE ruid = t1.ruid ORDER BY l_total DESC, uid ASC LIMIT 1) AS newruid FROM uid_details AS t1 WHERE status IN (1,3,4) AND newruid IS NOT NULL AND ruid != newruid') or output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
     if (($result = $query->fetchArray(SQLITE3_ASSOC)) === false) {
         return null;
     }
     $query->reset();
     while ($result = $query->fetchArray(SQLITE3_ASSOC)) {
         $registered = $result['csnick'];
         if (($alias = $sqlite3->querySingle('SELECT csnick FROM uid_details WHERE uid = ' . $result['newruid'])) === false) {
             output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
         }
         $sqlite3->exec('UPDATE uid_details SET ruid = ' . $result['newruid'] . ', status = ' . $result['status'] . ' WHERE uid = ' . $result['newruid']) or output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
         $sqlite3->exec('UPDATE uid_details SET ruid = ' . $result['newruid'] . ', status = 2 WHERE ruid = ' . $result['ruid']) or output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
         output::output('debug', __METHOD__ . '(): \'' . $alias . '\' set to new registered for \'' . $registered . '\'');
     }
 }
Example #13
0
 public function make_table($sqlite3)
 {
     /**
      * Detect which class to use. Class medium should be set explicitly by setting
      * $medium to true.
      */
     if ($this->medium) {
         $class = 'medium';
     } elseif (array_key_exists('v3', $this->keys)) {
         $class = 'large';
     } else {
         $class = 'small';
     }
     /**
      * Run the "total" query if present.
      */
     if (!empty($this->queries['total'])) {
         if (($this->total = $sqlite3->querySingle($this->queries['total'])) === false) {
             output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
         }
     }
     /**
      * Create the table head.
      */
     if ($class === 'small') {
         $tr0 = '<colgroup><col class="c1"><col class="pos"><col class="c2">';
         $tr1 = '<tr><th colspan="3">' . (!empty($this->total) ? '<span class="title">' . $this->head . '</span><span class="title-right">' . number_format($this->total) . ' Total</span>' : $this->head);
         $tr2 = '<tr><td class="k1">' . $this->keys['k1'] . '<td class="pos"><td class="k2">' . $this->keys['k2'];
         $trx = '';
     } else {
         $tr0 = '<colgroup><col class="c1"><col class="pos"><col class="c2"><col class="c3">';
         $tr1 = '<tr><th colspan="4">' . (!empty($this->total) ? '<span class="title">' . $this->head . '</span><span class="title-right">' . number_format($this->total) . ' Total</span>' : $this->head);
         $tr2 = '<tr><td class="k1">' . $this->keys['k1'] . '<td class="pos"><td class="k2">' . $this->keys['k2'] . '<td class="k3">' . $this->keys['k3'];
         $trx = '';
     }
     /**
      * Run the "main" query and structure the table contents.
      */
     $i = 0;
     $query = $sqlite3->query($this->queries['main']) or output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
     while ($result = $query->fetchArray(SQLITE3_ASSOC)) {
         $i++;
         foreach ($this->keys as $key => $type) {
             /**
              * Skip irrelevant keys.
              */
             if (strpos($key, 'v') === false) {
                 continue;
             }
             switch ($type) {
                 case 'string':
                     ${$key} = htmlspecialchars($result[$key]);
                     break;
                 case 'int':
                     ${$key} = number_format($result[$key]);
                     break;
                 case 'float':
                     ${$key} = number_format($result[$key], $this->decimals) . ($this->percentage ? '%' : '');
                     break;
                 case 'date':
                     ${$key} = date('j M \'y', strtotime($result[$key]));
                     break;
                 case 'date-norepeat':
                     ${$key} = date('j M \'y', strtotime($result[$key]));
                     if (!empty($prevdate) && ${$key} === $prevdate) {
                         ${$key} = '';
                     } else {
                         $prevdate = ${$key};
                     }
                     break;
                 case 'url':
                     ${$key} = '<a href="' . htmlspecialchars($result[$key]) . '">' . htmlspecialchars($result[$key]) . '</a>';
                     break;
                 case 'string-url':
                     ${$key} = $this->find_urls($result[$key]);
                     break;
                 case 'userstats':
                     ${$key} = '<a href="user.php?cid=' . urlencode($this->cid) . '&amp;nick=' . urlencode($result[$key]) . '">' . htmlspecialchars($result[$key]) . '</a>';
                     break;
             }
         }
         if ($class === 'small') {
             $trx .= '<tr><td class="v1">' . $v1 . '<td class="pos">' . $i . '<td class="v2">' . $v2;
         } else {
             /**
              * Class v3a doesn't use ellipsis.
              */
             $trx .= '<tr><td class="v1">' . $v1 . '<td class="pos">' . $i . '<td class="v2">' . $v2 . '<td class="' . ($this->v3a ? 'v3a' : 'v3') . '">' . $v3;
         }
     }
     if ($i < $this->minrows) {
         return null;
     }
     for ($i; $i < $this->maxrows; $i++) {
         if ($class === 'small') {
             $trx .= '<tr><td class="v1"><td class="pos">&nbsp;<td class="v2">';
         } else {
             $trx .= '<tr><td class="v1"><td class="pos">&nbsp;<td class="v2"><td class="v3">';
         }
     }
     return '<table class="' . $class . '">' . $tr0 . $tr1 . $tr2 . $trx . '</table>' . "\n";
 }
 /**
  * Parse a line for various chat data.
  */
 protected function parse_line($line)
 {
     /**
      * "Normal" lines.
      */
     if (preg_match('/^\\[(?<time>\\d{2}:\\d{2}(:\\d{2})?)\\] (?<nick>\\S+): (?<line>.+)$/', $line, $matches)) {
         $this->set_normal($matches['time'], $matches['nick'], $matches['line']);
         /**
          * "Join" lines.
          */
     } elseif (preg_match('/^\\[(?<time>\\d{2}:\\d{2}(:\\d{2})?)\\] (?<nick>\\S+) \\(\\S+\\) joined the channel\\.$/', $line, $matches)) {
         $this->set_join($matches['time'], $matches['nick']);
         /**
          * "Quit" lines.
          */
     } elseif (preg_match('/^\\[(?<time>\\d{2}:\\d{2}(:\\d{2})?)\\] (?<nick>\\S+) \\(\\S+\\) left IRC\\. \\(.*\\)$/', $line, $matches)) {
         $this->set_quit($matches['time'], $matches['nick']);
         /**
          * "Mode" lines.
          */
     } elseif (preg_match('/^\\[(?<time>\\d{2}:\\d{2}(:\\d{2})?)\\] (?<nick_performing>\\S+) sets mode (?<modes>[-+][ov]+([-+][ov]+)?) (?<nicks_undergoing>\\S+( \\S+)*)$/', $line, $matches)) {
         $modenum = 0;
         $nicks_undergoing = explode(' ', $matches['nicks_undergoing']);
         for ($i = 0, $j = strlen($matches['modes']); $i < $j; $i++) {
             $mode = substr($matches['modes'], $i, 1);
             if ($mode === '-' || $mode === '+') {
                 $modesign = $mode;
             } else {
                 $this->set_mode($matches['time'], $matches['nick_performing'], $nicks_undergoing[$modenum], $modesign . $mode);
                 $modenum++;
             }
         }
         /**
          * "Nickchange" lines.
          */
     } elseif (preg_match('/^\\[(?<time>\\d{2}:\\d{2}(:\\d{2})?)\\] (?<nick_performing>\\S+) is now known as (?<nick_undergoing>\\S+)$/', $line, $matches)) {
         $this->set_nickchange($matches['time'], $matches['nick_performing'], $matches['nick_undergoing']);
         /**
          * "Part" lines.
          */
     } elseif (preg_match('/^\\[(?<time>\\d{2}:\\d{2}(:\\d{2})?)\\] (?<nick>\\S+) \\(\\S+\\) left the channel\\.( \\(.*\\))?$/', $line, $matches)) {
         $this->set_part($matches['time'], $matches['nick']);
         /**
          * "Topic" lines.
          */
     } elseif (preg_match('/^\\[(?<time>\\d{2}:\\d{2}(:\\d{2})?)\\] (?<nick>\\S+) changed the topic to (?<line>.+)$/', $line, $matches)) {
         $this->set_topic($matches['time'], $matches['nick'], $matches['line']);
         /**
          * "Kick" lines.
          */
     } elseif (preg_match('/^\\[(?<time>\\d{2}:\\d{2}(:\\d{2})?)\\] (?<line>(?<nick_performing>\\S+) kicked (?<nick_undergoing>\\S+) from the channel\\. \\(.*\\))$/', $line, $matches)) {
         $this->set_kick($matches['time'], $matches['nick_performing'], $matches['nick_undergoing'], $matches['line']);
         /**
          * Skip everything else.
          */
     } elseif ($line !== '') {
         output::output('debug', __METHOD__ . '(): skipping line ' . $this->linenum . ': \'' . $line . '\'');
     }
 }
Example #15
0
 public function write_data($sqlite3)
 {
     /**
      * If there are no nicks there is no data.
      */
     if (empty($this->nick_objs)) {
         return false;
     }
     output::output('notice', __METHOD__ . '(): writing data to database');
     $sqlite3->exec('BEGIN TRANSACTION') or output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
     /**
      * Write channel totals to database.
      */
     if ($this->l_total !== 0) {
         $queryparts = $this->get_queryparts($sqlite3, ['l_00', 'l_01', 'l_02', 'l_03', 'l_04', 'l_05', 'l_06', 'l_07', 'l_08', 'l_09', 'l_10', 'l_11', 'l_12', 'l_13', 'l_14', 'l_15', 'l_16', 'l_17', 'l_18', 'l_19', 'l_20', 'l_21', 'l_22', 'l_23', 'l_night', 'l_morning', 'l_afternoon', 'l_evening', 'l_total']);
         $sqlite3->exec('INSERT OR IGNORE INTO channel_activity (date, ' . implode(', ', $queryparts['columns']) . ') VALUES (\'' . $this->date . '\', ' . implode(', ', $queryparts['values']) . ')') or output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
         $sqlite3->exec('UPDATE channel_activity SET ' . implode(', ', $queryparts['update-assignments']) . ' WHERE CHANGES() = 0 AND date = \'' . $this->date . '\'') or output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
     }
     /**
      * Write user data to database. User data should be written prior to topic and
      * URL data.
      */
     foreach ($this->nick_objs as $nick) {
         $nick->write_data($sqlite3);
     }
     /**
      * Write topic data to database.
      */
     foreach ($this->topic_objs as $topic) {
         $topic->write_data($sqlite3);
     }
     /**
      * Write URL data to database.
      */
     foreach ($this->url_objs as $url) {
         $url->write_data($sqlite3);
     }
     /**
      * Write word data to database.
      */
     foreach ($this->word_objs as $word) {
         $word->write_data($sqlite3);
     }
     /**
      * Write streak data (history) to database.
      */
     if ($this->l_total !== 0) {
         $sqlite3->exec('DELETE FROM streak_history') or output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
         $sqlite3->exec('INSERT INTO streak_history (prevnick, streak) VALUES (\'' . $sqlite3->escapeString($this->prevnick) . '\', ' . $this->streak . ')') or output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
     }
     $sqlite3->exec('COMMIT') or output::output('critical', basename(__FILE__) . ':' . __LINE__ . ', sqlite3 says: ' . $sqlite3->lastErrorMsg());
     return true;
 }