Browse code

Fixed install process - step 1 - for PHP 8.1

Jimmi08 authored on 2023/02/19 12:22:59
Showing 1 changed files
... ...
@@ -1,571 +1,571 @@
1
-<?php
2
-// ----------------------------------------------------------------------
3
-// Copyright (c) 2007 by Tammy Keefer
4
-// Based on eFiction 1.1
5
-// Copyright (C) 2003 by Rebecca Smallwood.
6
-// http://efiction.sourceforge.net/
7
-// ----------------------------------------------------------------------
8
-// LICENSE
9
-//
10
-// This program is free software; you can redistribute it and/or
11
-// modify it under the terms of the GNU General Public License (GPL)
12
-// as published by the Free Software Foundation; either version 2
13
-// of the License, or (at your option) any later version.
14
-//
15
-// This program is distributed in the hope that it will be useful,
16
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
17
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
-// GNU General Public License for more details.
19
-//
20
-// To read the license please visit http://www.gnu.org/copyleft/gpl.html
21
-// ----------------------------------------------------------------------
22
-
23
-if(!defined("_CHARSET")) exit( );
24
-
25
-// Validates emails
26
-function validEmail($str) {
27
-	return (bool) preg_match('/^[-_a-z0-9\'+*$^&%=~!?{}]++(?:\.[-_a-z0-9\'+*$^&%=~!?{}]+)*@(?:(?![-.])[-a-z0=9.]+(?<![-.])\.[a-z]{2,6}|\d{1,3}(?:\.\d{1,3}){3})(?::\d++)?$/iD', $str);
28
-}
29
-
30
-// Function used to add and remove stories from category counts
31
-function categoryitems($catid, $value)
32
-{
33
-	//add or subtract one to the current categories
34
-	//find out current category's parent
35
-	if(is_array($catid)) $cats = $catid;
36
-	else $cats = array($catid);
37
-	$cats = array_filter($cats, "isNumber");
38
-	$catquery = dbquery("SELECT catid, parentcatid, leveldown FROM ".TABLEPREFIX."fanfiction_categories WHERE FIND_IN_SET(catid, '$catid') GROUP BY catid");
39
-	while(isset($catquery)) {
40
-		while($cat = dbassoc($catquery)) {
41
-			$pcats = array();
42
-			if($cat['leveldown'] > 0) $pcats[] = $cat['parentcatid'];
43
-			if(!in_array($cat['catid'], $cats)) $cats[] = $cat['catid'];
44
-		}
45
-		if(count($pcats) > 0) $catquery = dbquery("SELECT catid, parentcatid, leveldown FROM ".TABLEPREFIX."fanfiction_categories WHERE FIND_IN_SET(catid, '".implode($pcats)."') GROUP BY catid");
46
-		else unset($catquery);
47
-	}
48
-	dbquery("UPDATE ".TABLEPREFIX."fanfiction_categories SET numitems = (numitems + $value) WHERE FIND_IN_SET(catid, '".implode(",", $cats)."')");
49
-}
50
-
51
-// Function to recurse through categories to build a list of the category and all it's sub-categories.
52
-function recurseCategories($catid) {
53
-	global $catlist;
54
-	$$catid = $catlist;
55
-	$categorylist[] = $catid;
56
-	foreach($$catid as $cat => $info) {
57
-		if($info['pid'] == $catid) {
58
-			$categorylist = array_merge($categorylist, recurseCategories($cat));
59
-		}
60
-	}
61
-	return $categorylist;
62
-}
63
-
64
-// Captcha script validation
65
-function captcha_confirm() {
66
-	if(empty($_SESSION[SITEKEY.'_digit'])) return false;
67
-	$digit = $_SESSION[SITEKEY.'_digit'];
68
-	$userdigit = $_POST['userdigit'];
69
-	unset($_SESSION[SITEKEY.'_digit']);
70
-	if($digit == md5(SITEKEY.$userdigit) && $userdigit > 1) return true;
71
-	return false;
72
-}
73
-
74
-// Sanitizes user input to help prevent XSS attacks 
75
-function descript($text) {
76
-	// Convert problematic ascii characters to their true values
77
-	$search = array("40","41","58","65","66","67","68","69","70",
78
-		"71","72","73","74","75","76","77","78","79","80","81",
79
-		"82","83","84","85","86","87","88","89","90","97","98",
80
-		"99","100","101","102","103","104","105","106","107",
81
-		"108","109","110","111","112","113","114","115","116",
82
-		"117","118","119","120","121","122");
83
-	
84
-	$replace = array("(",")",":","a","b","c","d","e","f","g","h",
85
-		"i","j","k","l","m","n","o","p","q","r","s","t","u",
86
-		"v","w","x","y","z","a","b","c","d","e","f","g","h",
87
-		"i","j","k","l","m","n","o","p","q","r","s","t","u",
88
-		"v","w","x","y","z");
89
-
90
-	$entities = count($search);
91
-	
92
-	for ($i=0;$i < $entities;$i++) $text = preg_replace("#(&\#)(0*".$search[$i]."+);*#si", $replace[$i], $text);
93
-
94
-	// the following is based on code from bitflux (http://blog.bitflux.ch/wiki/)	
95
-	// Kill hexadecimal characters completely
96
-	$text = preg_replace('#(&\#x)([0-9A-F]+);*#si', "", $text);
97
-
98
-	// remove any attribute starting with "on" or xmlns
99
-
100
-	$text = preg_replace('#(<[^>]+[\\"\'\s])(onmouseover|onmousedown|onmouseup|onmouseout|onmousemove|onclick|ondblclick|onload|xmlns)[^>]*>#iU', ">", $text);
101
-
102
-	// remove javascript: and vbscript: protocol
103
-	
104
-	$text = preg_replace('#([a-z]*)=([\`\'\"]*)script:#iU', '$1=$2nojscript...', $text);
105
-	$text = preg_replace('#([a-z]*)=([\`\'\"]*)javascript:#iU', '$1=$2nojavascript...', $text);
106
-	$text = preg_replace('#([a-z]*)=([\'\"]*)vbscript:#iU', '$1=$2novbscript...', $text);
107
-
108
-	//<span style="width: expression(alert('Ping!'));"></span> (only affects ie...)
109
-	$text = preg_replace('#(<[^>]+)style=([\`\'\"]*).*expression\([^>]*>#iU', "$1>", $text);
110
-	$text = preg_replace('#(<[^>]+)style=([\`\'\"]*).*behaviour\([^>]*>#iU', "$1>", $text);
111
-	return $text;
112
-}
113
-
114
-// Call this function when the user tries to do something they shouldn't have access to.
115
-function accessDenied($str = ""){
116
-	global $tpl, $output;
117
-
118
-	if(!empty($str)) $output = write_error($str);
119
-	else $output = write_error(_NOTAUTHORIZED);
120
-	if(!empty($tpl)) {
121
-		$tpl->assign("output", $output);
122
-		$tpl->printToScreen( );
123
-		dbclose( );
124
-	}
125
-	else echo $output;
126
-	exit( );
127
-}
128
-
129
-// Call this function when something causes an error and the script has to die.
130
-function errorExit( $msg = ""){
131
-	global $tpl, $output;
132
-
133
-	$output .= write_error(_ERROR.(!empty($msg) ? " " : "").$msg);
134
-	$tpl->assign("output", $output);
135
-	$tpl->printToScreen( );
136
-	dbclose( );
137
-	exit( );
138
-}
139
-// The next three functions are used to calculate the series reviews and rating
140
-
141
-function storiesInSeries($thisseries) {
142
-	$storylist = array( );
143
-	if(!isNumber($thisseries)) return $storylist;
144
-	$serieslist = array( );
145
-	$stinseries = dbquery("SELECT sid, subseriesid FROM ".TABLEPREFIX."fanfiction_inseries WHERE seriesid = '$thisseries'");
146
-	while($st = dbassoc($stinseries)) { 
147
-		if(!empty($st['sid'])) $storylist[] = $st['sid'];
148
-		else if(!empty($st['subseriesid'])) $serieslist[] = $st['subseriesid'];
149
-	}
150
-	if($serieslist) {
151
-		foreach($serieslist as $s) {
152
-			$storylist = array_merge($storylist, storiesInSeries($s));
153
-		}
154
-	}
155
-	return $storylist;
156
-}
157
-function subseriesList($thisseries) {
158
-	$serieslist = array( );
159
-	if(!isNumber($thisseries)) return $serieslist;
160
-	$stinseries = dbquery("SELECT subseriesid FROM ".TABLEPREFIX."fanfiction_inseries WHERE seriesid = '$thisseries'");
161
-	while($st = dbassoc($stinseries)) {
162
-		$serieslist[] = $st['subseriesid']; 
163
-		$serieslist = array_merge($serieslist, subseriesList($st['subseriesid']));
164
-	}
165
-	return $serieslist;
166
-}
167
-
168
-function seriesreview($thisseries) {
169
-
170
-	if(!isNumber($thisseries)) return;
171
-	$storylist = storiesInSeries($thisseries);
172
-	$serieslist = subseriesList($thisseries);
173
-$newrating = dbquery("SELECT AVG(rating) as totalreviews FROM ".TABLEPREFIX."fanfiction_reviews 
174
-	WHERE ((item = '$thisseries' AND type = 'SE')".
175
-	(count($storylist) > 0 ? " OR (FIND_IN_SET(item, '".(implode(",", $storylist))."') > 0 AND type = 'ST')" : "").
176
-	(count($serieslist) > 0 ? " OR (FIND_IN_SET(item, '".(implode(",", $serieslist))."') > 0 AND type = 'SE')" : "").
177
-	") AND rating != '-1'");
178
-list($totalreviews) = dbrow($newrating);
179
-$newcount = dbquery("SELECT count(reviewid) as totalcount FROM ".TABLEPREFIX."fanfiction_reviews 
180
-	WHERE ((item = '$thisseries' AND type = 'SE')".
181
-	(count($storylist) > 0 ? " OR (FIND_IN_SET(item, '".(implode(",", $storylist))."') > 0 AND type = 'ST')" : "").
182
-	(count($serieslist) > 0 ? " OR (FIND_IN_SET(item, '".(implode(",", $serieslist))."') > 0 AND type = 'SE')" : "").
183
-	") AND review != 'No Review'");
184
-list($totalcount) = dbrow($newcount);
185
-if($totalcount) $update = dbquery("UPDATE ".TABLEPREFIX."fanfiction_series SET rating = '".round($totalreviews)."', reviews = '$totalcount' WHERE seriesid = '$thisseries'");
186
-$parentq = dbquery("SELECT seriesid FROM ".TABLEPREFIX."fanfiction_inseries WHERE subseriesid = '$thisseries' AND seriesid != '$thisseries'");
187
-while($parent2 = dbassoc($parentq)) { seriesreview($parent2['seriesid']); }
188
-
189
-}
190
-
191
-// Per a suggestion from jrabbit, we'll use this function to optimize some queries 
192
-function findclause($field,$set) {
193
-  if (empty($set)) {
194
-    return "1 = 0";
195
-  }
196
-  if(is_array($set)) $set = implode(",", $set);
197
-  if (strpos($set,',')>0) {
198
-    return "FIND_IN_SET($field,'$set') > 0";
199
-  }
200
-  return "$field='$set'";
201
-}
202
-
203
-// Added 3.3
204
-function nl2br2($string) {
205
-	$string = str_replace(array("\r\n", "\r", "\n"), "<br />", $string);
206
-	return $string;
207
-}
208
-
209
-// Formats the text of the story when displayed on screen.
210
-function format_story($text) {
211
-      $text = trim($text);
212
-      if(strpos($text, "<br>") === false && strpos($text, "<p>") === false && strpos($text, "<br />") === false) $text = nl2br2($text);
213
-      if(_CHARSET != "ISO-8859-1" && _CHARSET != "US-ASCII") return stripslashes($text);
214
-      $badwordchars = array(chr(212), chr(213), chr(210), chr(211), chr(209), chr(208), chr(201), chr(145), chr(146), chr(147), chr(148), chr(151), chr(150), chr(133));
215
-      $fixedwordchars = array('&#8216;', '&#8217;', '&#8220;', '&#8221;', '&#8212;', '&#8211;', '&#8230;', '&#8216;', '&#8217;', '&#8220;', '&#8221;', '&#8212;', '&#8211;',  '&#8230;' );
216
-      $text = str_replace($badwordchars,$fixedwordchars,stripslashes($text));
217
-      return $text;
218
-}
219
-
220
-// Function to Spam-protect emails.  Is called for the IM fields in the user's profile.
221
-function format_email($text) {
222
-	$search = array('@', '.');
223
-	$replace = array(" [AT] ", " [DOT] ");
224
-	return str_replace($search, $replace, $text);
225
-}
226
-
227
-// Function to format a URL into a clickable link
228
-function format_link($text, $title = "", $target = 0) {
229
-	if(empty($title)) $title = $text;
230
-	if(strpos($text, "http://") === false) $text = "http://".$text;
231
-	$text = "<a href='$text'".($target ? " target='_blank'" : "").">$title</a>";
232
-	return $text;
233
-}
234
-
235
-// Because this is used in places other than the listings of stories, we're setting it up as a function to be called as needed.
236
-function title_link($stories) {
237
-	global $ratingslist, $ageconsent, $disablepopups;
238
-
239
-	$rating = $stories['rid'];
240
-	$warningtext = !empty($ratingslist[$rating]['warningtext']) ? addslashes(strip_tags($ratingslist[$rating]['warningtext'])) : "";
241
-		if(empty($ratingslist[$rating]['ratingwarning']))
242
-			$title = "<a href=\""._BASEDIR."viewstory.php?sid=".$stories['sid']."\">".$stories['title']."</a>";
243
-		else {
244
-			$warning = "";
245
-			$warninglevel = sprintf("%03b", $ratingslist[$rating]['ratingwarning']);
246
-			if($warninglevel[2] && !isset($_SESSION[SITEKEY."_warned"][$rating])) {
247
-				$location = "viewstory.php?sid=".$stories['sid']."&amp;warning=$rating";
248
-				$warning = $warningtext;
249
-			}
250
-			if($warninglevel[1] && !$ageconsent && empty($_SESSION[SITEKEY.'_ageconsent'])) {
251
-				$location = "viewstory.php?sid=".$stories['sid']."&amp;ageconsent=ok&amp;warning=$rating";
252
-				$warning = _AGECHECK." - "._AGECONSENT." ".$warningtext." -- 1";
253
-			}
254
-			if($warninglevel[0] && !isMEMBER) {
255
-				$location = "user.php?action=login&amp;sid=".$stories['sid'];
256
-				$warning = _RUSERSONLY." - $warningtext";		
257
-			}
258
-			if(!empty($warning)) {
259
-				$warning = preg_replace("@'@", "\'", $warning);
260
-				$title = "<a href=\"javascript:if(confirm('".$warning."')) location = '"._BASEDIR."$location'\">".$stories['title']."</a>";
261
-			}
262
-			else $title = "<a href=\""._BASEDIR."viewstory.php?sid=".$stories['sid']."\">".$stories['title']."</a>";
263
-		}
264
-	return $title;
265
-}
266
-
267
-// Same with the author list
268
-function author_link($stories) {
269
-	if(is_array($stories['coauthors'])) {
270
-		$authlink[] = "<a href=\""._BASEDIR."viewuser.php?uid=".$stories['uid']."\">".$stories['penname']."</a>";
271
-		$coauth = dbquery("SELECT "._PENNAMEFIELD." as penname, co.uid FROM ".TABLEPREFIX."fanfiction_coauthors AS co LEFT JOIN "._AUTHORTABLE." ON co.uid = "._UIDFIELD." WHERE co.sid = '".$stories['sid']."'");
272
-		foreach($stories['coauthors'] AS $k => $v) {
273
-			$authlink[] = "<a href=\""._BASEDIR."viewuser.php?uid=".$k."\">".$v."</a>";
274
-		}
275
-	}
276
-	return isset($authlink) ? implode(", ", $authlink) : "<a href=\""._BASEDIR."viewuser.php?uid=".$stories['uid']."\">".$stories['penname']."</a>";
277
-}
278
-
279
-// Used to truncate text (summaries in blocks for example) to a set length.  An improvement on the old version as this keeps words intact
280
-function truncate_text($str, $n = 75, $delim='...') { 
281
-   $len = strlen($str);
282
-   if($len > $n) {
283
-        $pos = strpos($str, " ", $n);
284
-	if($pos) $str = trim(substr($str, 0, $pos), "\n\t\.,"). $delim;
285
-  }
286
-  return closetags($str);
287
-} 
288
-
289
-// A helper function for the truncate_text function.  This will close all open tags
290
-function closetags($html){
291
-
292
-  $donotclose=array('br','img','input', 'hr');
293
-  preg_match_all("#<([a-z]+)( .*)?(?!/)>#iU",$html,$result);
294
-  $openedtags=$result[1];
295
-
296
-  preg_match_all("#</([a-z]+)>#iU",$html,$result);
297
-  $closedtags=$result[1];
298
-  $len_opened = count($openedtags);
299
-  if(count($closedtags) == $len_opened){
300
-	  return $html;
301
-  }
302
-
303
-  $openedtags = array_reverse($openedtags);
304
-
305
-  for($i=0;$i < $len_opened;$i++) {
306
-
307
-    if (!in_array($openedtags[$i],$closedtags) && !in_array($openedtags[$i], $donotclose)){
308
-      $html .= '</'.$openedtags[$i].'>';
309
-    } 
310
-    else {
311
-      unset($closedtags[array_search($openedtags[$i],$closedtags)]);
312
-    }
313
-  }
314
-  return $html;
315
-}
316
-
317
-//  Builds the naughty word list for the site's censor
318
-function build_word($myword) {
319
-	$letters =preg_split('//',$myword, -1, PREG_SPLIT_NO_EMPTY);
320
-	$word = "";
321
-	foreach($letters as $letter) { 
322
-		if($word != "") $word .= "\W+";
323
-		$word .= $letter;
324
-	}
325
-	return $word;
326
-
327
-}
328
-
329
-// Finds forbidden words in text.  Returns true if a forbidden word is found
330
-function find_naughty($text) {
331
-	global $words;
332
-	if(!count($words)) return false;
333
-	$naughty = 0;
334
-	for($i = 0; $i < sizeof($words); $i++) {
335
-		if(strpos($words[$i], "*") === false) {
336
-			// check for whole word
337
-			if(preg_match('/(\s|^)+(('.$words[$i].')|('.build_word($words[$i]).')+(s|es)?)+(\W|$)+/i', $text, $match)) {
338
-				echo $words[$i] ." 0=".$match[0];
339
-				$naughty = 1; 
340
-			}
341
-		}
342
-		if(strpos($words[$i], "*") !== false && strpos($words[$i], "*") == 0 && !$naughty) {
343
-			// remove the * from the beginning of the word
344
-			$word = substr($words[$i], 1, strlen($words[$i]));
345
-			if(strrpos($word, "*") == strlen($word) - 1) $word = substr($word, 0, strlen($word) - 1);
346
-			// check for whole word plus word as suffix
347
-			if(preg_match('/[\s|^]+\w*('.$word.'|\W'.build_word($word).')+(s|es)?(\W|$)/i', $text, $match)) $naughty = 1; 
348
-		}
349
-		if(strrpos($words[$i], "*") == strlen($words[$i]) - 1 && !$naughty) {
350
-			// remove the * from the end of the word
351
-			$word = substr($words[$i], 0, strlen($words[$i]) - 1);
352
-			if(strpos($word, "*") !== false && strpos($word, "*") == 0) $word = substr($word, 1, strlen($word));
353
-			// check for whole word plus word as prefix
354
-			if(preg_match('/(\s|^)('.$word.'|'.build_word($word).'\W)+\w*(s|es)?(\W|$)/i', $text, $match)) $naughty = 1; 
355
-		}
356
-		if($naughty) break;  // no sense continuing down the list and this will make it run faster
357
-	}
358
-	return $naughty;
359
-}
360
-
361
-// Replaces forbidden words in text.  The forbidden word is replaced with the first letter and trailing ***
362
-function replace_naughty($text) {
363
-	global $words;
364
-
365
-	if(!count($words)) return $text;
366
-	$i = 0;
367
-	for($j = 0; $j < sizeof($words); $j++) {
368
-		if(strpos($words[$j], "*") === false) {
369
-			$replace[$i] = str_pad($words[$j]{0}, strlen($words[$j]), "*");
370
-			$naughtywords[$i] = '/\b('.$words[$j].'\b)|('.build_word($words[$j]).')\b/i';
371
-			$i++;
372
-		}
373
-
374
-		if(strpos($words[$j], "*") !== false && strpos($words[$j], "*") == 0) {
375
-			$word = substr($words[$j], 1, strlen($words[$j]));
376
-			if(strrpos($word, "*") == strlen($word) - 1) $word = substr($word, 0, strlen($word) - 1);
377
-			$naughtywords[$i] = '/(\s|^)+(\w*)+('.$word.'|'.build_word($word).')/i';
378
-			$replace[$i] = "$1$2".str_pad(substr($word, 0, 1), strlen($word), "*");
379
-			$i++;
380
-		}
381
-		if(strrpos($words[$j], "*") == strlen($words[$j]) - 1) {
382
-			$word = substr($words[$j], 0, strlen($words[$j]) - 1);
383
-			if(strpos($word, "*") !== false && strpos($word, "*") == 0) $word = substr($word, 1, strlen($word));
384
-			$replace[$i] = '$1'.str_pad(substr($word, 0, 1), strlen($word), "*").'$3';
385
-			$naughtywords[$i] = '/(.*)('.$word.'|'.build_word($word).')(\s|\w)/i';
386
-			$i++;
387
-		}
388
-	}
389
-	$text = preg_replace($naughtywords, $replace, $text);
390
-	return $text;
391
-}
392
-
393
-// Format for messages sent back from various forms and actions 
394
-function write_message($str) {
395
-	return "<div style='text-align: center; margin: 1em;'>$str</div>";
396
-}
397
-
398
-// Formats error messages sent back from various forms and actions
399
-function write_error($str) {
400
-	return "<div style='text-align: center; margin: 1em;' class='errortext'>$str</div>";
401
-}
402
-
403
-// Checks that the given $num is actually a number.  Used to help prevent XSS attacks.
404
-function isNumber($num) {
405
-	if(empty($num)) return false;
406
-	if(!is_string($num)) return false;
407
-	return preg_match("/^[0-9]+$/", $num);
408
-}
409
-
410
-// May be needed for sites that have bridged the authors table
411
-function check_prefs($uid) {
412
-	$test = dbquery("SELECT uid FROM ".TABLEPREFIX."fanfiction_authorprefs WHERE uid = '$uid'");
413
-	if(dbnumrows($test)) return true;
414
-	else return false;
415
-}
416
-
417
-// Function builds the alphabet links on various pages.
418
-function build_alphalinks($url, $let) {
419
-	global $alphabet;
420
-
421
-	$alpha = "<div id=\"alphabet\">";
422
-	foreach( $alphabet as $link ) {
423
-		// Build a link that calls a function with ($link and 1 (page number) )
424
-		$alpha .= "<a href=\"{$url}let=$link\"".($let == $link ? " id='currentletter'" : "").">$link</a> \n";
425
-	}
426
-	$alpha .= "</div>";
427
-	return $alpha;
428
-}
429
-
430
-// Function builds the pagination links
431
-function build_pagelinks($url, $total, $offset = 0, $columns = 1) {
432
-	global $itemsperpage, $linkstyle, $linkrange;
433
-	$pages = "";
434
-	$itemsperpage = $itemsperpage * $columns;
435
-
436
-	if($itemsperpage >= $total) return;
437
-
438
-	if(empty($linkrange)) $linkrange = 4;
439
-
440
-	$totpages = floor($total/$itemsperpage) + ($total % $itemsperpage ? 1 : 0);
441
-	$curpage = floor($offset/$itemsperpage) + 1;
442
-	if(!$linkstyle) $startrange = $curpage;
443
-	else {
444
-		if($totpages <= $linkrange || $curpage == 1) $startrange = 1;
445
-		else if($curpage >= $totpages - floor($linkrange / 2) + 1) $startrange = $totpages - $linkrange;
446
-		else $startrange = $curpage - floor($linkrange / 2) > 0 ? $curpage - floor($linkrange / 2) : 1;
447
-	}
448
-	if($startrange >= $totpages - $linkrange ) $startrange = $totpages - $linkrange > 0 ? $totpages - $linkrange : 1;
449
-	$stoprange = $totpages > $startrange + $linkrange ? $startrange + $linkrange : $totpages + 1;
450
-	if($curpage > 1 && $linkstyle != 1) $pages .= "<a href='".$url."offset=".( $offset - $itemsperpage)."' id='plprev'>["._PREVIOUS."]</a> ";
451
-	if($startrange > 1 && $linkstyle > 0) $pages .= "<a href='".$url."offset=0'>1</a><span class='ellipses'>...</span>";
452
-	for($x = $startrange; $x < $stoprange; $x++) {
453
-		$pages .= "<a href='".$url."offset=".(($x - 1) * $itemsperpage)."'".($x == $curpage ? "id='currentpage'" : "").">".$x."</a> \n";
454
-	}
455
-	if($stoprange < $totpages && $linkstyle > 0) $pages .= "<span class='ellipses'>...</span> <a href='".$url."offset=".(($totpages - 1) * $itemsperpage)."'>$totpages</a>\n";
456
-	if ($curpage < $totpages && $linkstyle != 1) $pages .=  " <a href='".$url."offset=".($offset+$itemsperpage)."' id='plnext'>["._NEXT."]</a>";
457
-	return "<div id=\"pagelinks\">$pages</div>";
458
-}
459
-
460
-// Function that returns the ratings picks 
461
-function ratingpics($rating) {
462
-	global $ratings, $like, $dislike, $star, $halfstar;
463
-	$ratingpics = "";
464
-	if($ratings == "2") {
465
-		if($rating >= 0.5)
466
-			$ratingpics = ($like ? $like : "<img src=\""._BASEDIR."images/like.gif\" alt=\""._LIKED."\">");
467
-		else if(($rating < 0.5) && ($rating > 0))
468
-			$ratingpics = ($dislike ? $dislike :"<img src=\""._BASEDIR."images/dislike.gif\" alt=\""._DISLIKED."\">");
469
-		else $ratingpics = "";
470
-	}
471
-	if($ratings == "1") {
472
-		global $star, $halfstar;
473
-		if($rating > 0) {
474
-			for($x = 0; $x < ($rating / 2) - .5; $x++) {
475
-				$ratingpics .= ($star ? $star  : "<img src=\""._BASEDIR."images/star.gif\" alt=\"star\">");
476
-			}
477
-			if($rating % 2 != 0) $ratingpics .= ($halfstar ? $halfstar  : "<img src=\""._BASEDIR."images/starhalf.gif\" alt=\"half-star\">");
478
-		}
479
-		else $ratingpics = "";
480
-	}
481
-	if(!empty($ratingpics)) return "<span style='white-space: nowrap;'>$ratingpics</span>"; // the no-wrap style will keep the stars together
482
-	else return;
483
-}
484
-
485
-// This function builds the list of category links (including the breadcrumb depending on settings)
486
-function catlist($catid) {
487
-	global $extendcats, $catlist, $action;
488
-
489
-	if(!is_array($catid)) $catid = explode(",", $catid);
490
-	$categorylinks = array();
491
-	foreach($catid as $cat) {
492
-		if(empty($catlist[$cat])) continue;
493
-		if($extendcats) {
494
-			unset($link);
495
-			$thiscat = $cat;
496
-			while(isset($thiscat)) {
497
-				if(isset($link)) $link = " > ".$link;
498
-				else $link = "";
499
-				if($action != "printable") $link = "<a href='"._BASEDIR."browse.php?type=categories&amp;catid=$thiscat'>".$catlist[$thiscat]['name']."</a>".$link;
500
-				else $link = $catlist[$thiscat]['name'].$link;
501
-				if($catlist[$thiscat]['pid'] == -1) unset($thiscat);
502
-				else $thiscat = $catlist[$thiscat]['pid'];
503
-			}
504
-			$categorylinks[] = $link;
505
-		}
506
-		else $categorylinks[] = "<a href='"._BASEDIR."browse.php?type=categories&amp;catid=$cat'>".$catlist[$cat]['name']."</a>";
507
-	}
508
-	return implode(", ", $categorylinks);
509
-}
510
-
511
-// This function builds the list of character links
512
-function charlist($characters) {
513
-	global $charlist, $action;
514
-
515
-	if(!is_array($characters)) $characters = explode(",", $characters);
516
-	$charlinks = array( );
517
-	foreach($characters as $c) {
518
-		if(empty($charlist[$c]['name'])) continue;
519
-		if($action != "printable") $charlinks[] = "<a href='"._BASEDIR."browse.php?type=characters&amp;charid=$c'>".$charlist[$c]['name']."</a>";
520
-		else $charlinks[] = $charlist[$c]['name'];
521
-	}
522
-	return implode(", ", $charlinks);
523
-}
524
-
525
-// Most of the pages that list stories and series use this fuction.  This handles showing the series and stories and pagination of the two together when needed
526
-function search($storyquery, $countquery, $pagelink = "search.php?", $pagetitle = 0) {
527
-	global $tpl, $new, $ratingslist, $itemsperpage, $reviewsallowed, $output, $dateformat, $current, $featured, $favorites, $retired, $ageconsent, $classtypelist, $classlist, $offset, $recentdays;
528
-     
529
-	$count = dbquery($countquery);
530
-	list($numrows) = dbrow($count);
531
-	if($numrows) {
532
-		$tpl->assign("output", ($pagetitle ? "<div id=\"pagetitle\">$pagetitle</div>" : ""));
533
-		$tpl->newBlock("listings");
534
-		if(!$ratingslist) {
535
-			$ratlist = dbquery("SELECT * FROM ".TABLEPREFIX."fanfiction_ratings");
536
-			while($rate = dbassoc($ratlist)) {
537
-				$ratings[$rate['rid']] = array("rating" => $rate['rating'], "ratingwarning" => $rate['ratingwarning'], "warningtext" => $rate['warningtext']);
538
-			}
539
-		}
540
-		$tpl->newBlock("listings");
541
-		$tpl->gotoBlock("listings");
542
-		$tpl->assign("stories",  "<div class=\"sectionheader\">"._STORIES."</div>");
543
-		$storyquery .= " LIMIT $offset, $itemsperpage";
544
-		$result3 = dbquery($storyquery);     
545
-		$count = 0;                     
546
-		while($stories = dbassoc($result3)) {       
547
-			$tpl->newBlock("storyblock");
548
-			include(_BASEDIR."includes/storyblock.php"); 
549
-		}
550
-		$tpl->gotoBlock("_ROOT");		
551
-	}
552
-	else {
553
-		$tpl->newBlock("listings");
554
-		$tpl->assign("pagelinks", write_message(_NORESULTS));
555
-	}
556
-	if($numrows > $itemsperpage) {
557
-		$termArray = array_merge($_GET, $_POST);
558
-		$terms = array();
559
-		foreach($termArray as $term => $value) {
560
-			if($term == "submit" || $term == "go" || $term == "offset" || ($term != "complete" &&empty($value))) continue;
561
-			$terms[] = "$term=".(is_array($value) ? implode(",", $value) : $value);
562
-		}
563
-		$terms = implode("&amp;", $terms);
564
-		$terms .= "&amp;";
565
-		$tpl->gotoBlock("listings");
566
-		$tpl->assign( "pagelinks", build_pagelinks($pagelink.$terms, $numrows, $offset));
567
-	}
568
-	$tpl->gotoBlock("_ROOT");
569
-	return $numrows;
570
-}
1
+<?php
2
+// ----------------------------------------------------------------------
3
+// Copyright (c) 2007 by Tammy Keefer
4
+// Based on eFiction 1.1
5
+// Copyright (C) 2003 by Rebecca Smallwood.
6
+// http://efiction.sourceforge.net/
7
+// ----------------------------------------------------------------------
8
+// LICENSE
9
+//
10
+// This program is free software; you can redistribute it and/or
11
+// modify it under the terms of the GNU General Public License (GPL)
12
+// as published by the Free Software Foundation; either version 2
13
+// of the License, or (at your option) any later version.
14
+//
15
+// This program is distributed in the hope that it will be useful,
16
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
17
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
+// GNU General Public License for more details.
19
+//
20
+// To read the license please visit http://www.gnu.org/copyleft/gpl.html
21
+// ----------------------------------------------------------------------
22
+
23
+if(!defined("_CHARSET")) exit( );
24
+
25
+// Validates emails
26
+function validEmail($str) {
27
+	return (bool) preg_match('/^[-_a-z0-9\'+*$^&%=~!?{}]++(?:\.[-_a-z0-9\'+*$^&%=~!?{}]+)*@(?:(?![-.])[-a-z0=9.]+(?<![-.])\.[a-z]{2,6}|\d{1,3}(?:\.\d{1,3}){3})(?::\d++)?$/iD', $str);
28
+}
29
+
30
+// Function used to add and remove stories from category counts
31
+function categoryitems($catid, $value)
32
+{
33
+	//add or subtract one to the current categories
34
+	//find out current category's parent
35
+	if(is_array($catid)) $cats = $catid;
36
+	else $cats = array($catid);
37
+	$cats = array_filter($cats, "isNumber");
38
+	$catquery = dbquery("SELECT catid, parentcatid, leveldown FROM ".TABLEPREFIX."fanfiction_categories WHERE FIND_IN_SET(catid, '$catid') GROUP BY catid");
39
+	while(isset($catquery)) {
40
+		while($cat = dbassoc($catquery)) {
41
+			$pcats = array();
42
+			if($cat['leveldown'] > 0) $pcats[] = $cat['parentcatid'];
43
+			if(!in_array($cat['catid'], $cats)) $cats[] = $cat['catid'];
44
+		}
45
+		if(count($pcats) > 0) $catquery = dbquery("SELECT catid, parentcatid, leveldown FROM ".TABLEPREFIX."fanfiction_categories WHERE FIND_IN_SET(catid, '".implode($pcats)."') GROUP BY catid");
46
+		else unset($catquery);
47
+	}
48
+	dbquery("UPDATE ".TABLEPREFIX."fanfiction_categories SET numitems = (numitems + $value) WHERE FIND_IN_SET(catid, '".implode(",", $cats)."')");
49
+}
50
+
51
+// Function to recurse through categories to build a list of the category and all it's sub-categories.
52
+function recurseCategories($catid) {
53
+	global $catlist;
54
+	$$catid = $catlist;
55
+	$categorylist[] = $catid;
56
+	foreach($$catid as $cat => $info) {
57
+		if($info['pid'] == $catid) {
58
+			$categorylist = array_merge($categorylist, recurseCategories($cat));
59
+		}
60
+	}
61
+	return $categorylist;
62
+}
63
+
64
+// Captcha script validation
65
+function captcha_confirm() {
66
+	if(empty($_SESSION[SITEKEY.'_digit'])) return false;
67
+	$digit = $_SESSION[SITEKEY.'_digit'];
68
+	$userdigit = $_POST['userdigit'];
69
+	unset($_SESSION[SITEKEY.'_digit']);
70
+	if($digit == md5(SITEKEY.$userdigit) && $userdigit > 1) return true;
71
+	return false;
72
+}
73
+
74
+// Sanitizes user input to help prevent XSS attacks 
75
+function descript($text) {
76
+	// Convert problematic ascii characters to their true values
77
+	$search = array("40","41","58","65","66","67","68","69","70",
78
+		"71","72","73","74","75","76","77","78","79","80","81",
79
+		"82","83","84","85","86","87","88","89","90","97","98",
80
+		"99","100","101","102","103","104","105","106","107",
81
+		"108","109","110","111","112","113","114","115","116",
82
+		"117","118","119","120","121","122");
83
+	
84
+	$replace = array("(",")",":","a","b","c","d","e","f","g","h",
85
+		"i","j","k","l","m","n","o","p","q","r","s","t","u",
86
+		"v","w","x","y","z","a","b","c","d","e","f","g","h",
87
+		"i","j","k","l","m","n","o","p","q","r","s","t","u",
88
+		"v","w","x","y","z");
89
+
90
+	$entities = count($search);
91
+	
92
+	for ($i=0;$i < $entities;$i++) $text = preg_replace("#(&\#)(0*".$search[$i]."+);*#si", $replace[$i], $text);
93
+
94
+	// the following is based on code from bitflux (http://blog.bitflux.ch/wiki/)	
95
+	// Kill hexadecimal characters completely
96
+	$text = preg_replace('#(&\#x)([0-9A-F]+);*#si', "", $text);
97
+
98
+	// remove any attribute starting with "on" or xmlns
99
+
100
+	$text = preg_replace('#(<[^>]+[\\"\'\s])(onmouseover|onmousedown|onmouseup|onmouseout|onmousemove|onclick|ondblclick|onload|xmlns)[^>]*>#iU', ">", $text);
101
+
102
+	// remove javascript: and vbscript: protocol
103
+	
104
+	$text = preg_replace('#([a-z]*)=([\`\'\"]*)script:#iU', '$1=$2nojscript...', $text);
105
+	$text = preg_replace('#([a-z]*)=([\`\'\"]*)javascript:#iU', '$1=$2nojavascript...', $text);
106
+	$text = preg_replace('#([a-z]*)=([\'\"]*)vbscript:#iU', '$1=$2novbscript...', $text);
107
+
108
+	//<span style="width: expression(alert('Ping!'));"></span> (only affects ie...)
109
+	$text = preg_replace('#(<[^>]+)style=([\`\'\"]*).*expression\([^>]*>#iU', "$1>", $text);
110
+	$text = preg_replace('#(<[^>]+)style=([\`\'\"]*).*behaviour\([^>]*>#iU', "$1>", $text);
111
+	return $text;
112
+}
113
+
114
+// Call this function when the user tries to do something they shouldn't have access to.
115
+function accessDenied($str = ""){
116
+	global $tpl, $output;
117
+
118
+	if(!empty($str)) $output = write_error($str);
119
+	else $output = write_error(_NOTAUTHORIZED);
120
+	if(!empty($tpl)) {
121
+		$tpl->assign("output", $output);
122
+		$tpl->printToScreen( );
123
+		dbclose( );
124
+	}
125
+	else echo $output;
126
+	exit( );
127
+}
128
+
129
+// Call this function when something causes an error and the script has to die.
130
+function errorExit( $msg = ""){
131
+	global $tpl, $output;
132
+
133
+	$output .= write_error(_ERROR.(!empty($msg) ? " " : "").$msg);
134
+	$tpl->assign("output", $output);
135
+	$tpl->printToScreen( );
136
+	dbclose( );
137
+	exit( );
138
+}
139
+// The next three functions are used to calculate the series reviews and rating
140
+
141
+function storiesInSeries($thisseries) {
142
+	$storylist = array( );
143
+	if(!isNumber($thisseries)) return $storylist;
144
+	$serieslist = array( );
145
+	$stinseries = dbquery("SELECT sid, subseriesid FROM ".TABLEPREFIX."fanfiction_inseries WHERE seriesid = '$thisseries'");
146
+	while($st = dbassoc($stinseries)) { 
147
+		if(!empty($st['sid'])) $storylist[] = $st['sid'];
148
+		else if(!empty($st['subseriesid'])) $serieslist[] = $st['subseriesid'];
149
+	}
150
+	if($serieslist) {
151
+		foreach($serieslist as $s) {
152
+			$storylist = array_merge($storylist, storiesInSeries($s));
153
+		}
154
+	}
155
+	return $storylist;
156
+}
157
+function subseriesList($thisseries) {
158
+	$serieslist = array( );
159
+	if(!isNumber($thisseries)) return $serieslist;
160
+	$stinseries = dbquery("SELECT subseriesid FROM ".TABLEPREFIX."fanfiction_inseries WHERE seriesid = '$thisseries'");
161
+	while($st = dbassoc($stinseries)) {
162
+		$serieslist[] = $st['subseriesid']; 
163
+		$serieslist = array_merge($serieslist, subseriesList($st['subseriesid']));
164
+	}
165
+	return $serieslist;
166
+}
167
+
168
+function seriesreview($thisseries) {
169
+
170
+	if(!isNumber($thisseries)) return;
171
+	$storylist = storiesInSeries($thisseries);
172
+	$serieslist = subseriesList($thisseries);
173
+$newrating = dbquery("SELECT AVG(rating) as totalreviews FROM ".TABLEPREFIX."fanfiction_reviews 
174
+	WHERE ((item = '$thisseries' AND type = 'SE')".
175
+	(count($storylist) > 0 ? " OR (FIND_IN_SET(item, '".(implode(",", $storylist))."') > 0 AND type = 'ST')" : "").
176
+	(count($serieslist) > 0 ? " OR (FIND_IN_SET(item, '".(implode(",", $serieslist))."') > 0 AND type = 'SE')" : "").
177
+	") AND rating != '-1'");
178
+list($totalreviews) = dbrow($newrating);
179
+$newcount = dbquery("SELECT count(reviewid) as totalcount FROM ".TABLEPREFIX."fanfiction_reviews 
180
+	WHERE ((item = '$thisseries' AND type = 'SE')".
181
+	(count($storylist) > 0 ? " OR (FIND_IN_SET(item, '".(implode(",", $storylist))."') > 0 AND type = 'ST')" : "").
182
+	(count($serieslist) > 0 ? " OR (FIND_IN_SET(item, '".(implode(",", $serieslist))."') > 0 AND type = 'SE')" : "").
183
+	") AND review != 'No Review'");
184
+list($totalcount) = dbrow($newcount);
185
+if($totalcount) $update = dbquery("UPDATE ".TABLEPREFIX."fanfiction_series SET rating = '".round($totalreviews)."', reviews = '$totalcount' WHERE seriesid = '$thisseries'");
186
+$parentq = dbquery("SELECT seriesid FROM ".TABLEPREFIX."fanfiction_inseries WHERE subseriesid = '$thisseries' AND seriesid != '$thisseries'");
187
+while($parent2 = dbassoc($parentq)) { seriesreview($parent2['seriesid']); }
188
+
189
+}
190
+
191
+// Per a suggestion from jrabbit, we'll use this function to optimize some queries 
192
+function findclause($field,$set) {
193
+  if (empty($set)) {
194
+    return "1 = 0";
195
+  }
196
+  if(is_array($set)) $set = implode(",", $set);
197
+  if (strpos($set,',')>0) {
198
+    return "FIND_IN_SET($field,'$set') > 0";
199
+  }
200
+  return "$field='$set'";
201
+}
202
+
203
+// Added 3.3
204
+function nl2br2($string) {
205
+	$string = str_replace(array("\r\n", "\r", "\n"), "<br />", $string);
206
+	return $string;
207
+}
208
+
209
+// Formats the text of the story when displayed on screen.
210
+function format_story($text) {
211
+      $text = trim($text);
212
+      if(strpos($text, "<br>") === false && strpos($text, "<p>") === false && strpos($text, "<br />") === false) $text = nl2br2($text);
213
+      if(_CHARSET != "ISO-8859-1" && _CHARSET != "US-ASCII") return stripslashes($text);
214
+      $badwordchars = array(chr(212), chr(213), chr(210), chr(211), chr(209), chr(208), chr(201), chr(145), chr(146), chr(147), chr(148), chr(151), chr(150), chr(133));
215
+      $fixedwordchars = array('&#8216;', '&#8217;', '&#8220;', '&#8221;', '&#8212;', '&#8211;', '&#8230;', '&#8216;', '&#8217;', '&#8220;', '&#8221;', '&#8212;', '&#8211;',  '&#8230;' );
216
+      $text = str_replace($badwordchars,$fixedwordchars,stripslashes($text));
217
+      return $text;
218
+}
219
+
220
+// Function to Spam-protect emails.  Is called for the IM fields in the user's profile.
221
+function format_email($text) {
222
+	$search = array('@', '.');
223
+	$replace = array(" [AT] ", " [DOT] ");
224
+	return str_replace($search, $replace, $text);
225
+}
226
+
227
+// Function to format a URL into a clickable link
228
+function format_link($text, $title = "", $target = 0) {
229
+	if(empty($title)) $title = $text;
230
+	if(strpos($text, "http://") === false) $text = "http://".$text;
231
+	$text = "<a href='$text'".($target ? " target='_blank'" : "").">$title</a>";
232
+	return $text;
233
+}
234
+
235
+// Because this is used in places other than the listings of stories, we're setting it up as a function to be called as needed.
236
+function title_link($stories) {
237
+	global $ratingslist, $ageconsent, $disablepopups;
238
+
239
+	$rating = $stories['rid'];
240
+	$warningtext = !empty($ratingslist[$rating]['warningtext']) ? addslashes(strip_tags($ratingslist[$rating]['warningtext'])) : "";
241
+		if(empty($ratingslist[$rating]['ratingwarning']))
242
+			$title = "<a href=\""._BASEDIR."viewstory.php?sid=".$stories['sid']."\">".$stories['title']."</a>";
243
+		else {
244
+			$warning = "";
245
+			$warninglevel = sprintf("%03b", $ratingslist[$rating]['ratingwarning']);
246
+			if($warninglevel[2] && !isset($_SESSION[SITEKEY."_warned"][$rating])) {
247
+				$location = "viewstory.php?sid=".$stories['sid']."&amp;warning=$rating";
248
+				$warning = $warningtext;
249
+			}
250
+			if($warninglevel[1] && !$ageconsent && empty($_SESSION[SITEKEY.'_ageconsent'])) {
251
+				$location = "viewstory.php?sid=".$stories['sid']."&amp;ageconsent=ok&amp;warning=$rating";
252
+				$warning = _AGECHECK." - "._AGECONSENT." ".$warningtext." -- 1";
253
+			}
254
+			if($warninglevel[0] && !isMEMBER) {
255
+				$location = "user.php?action=login&amp;sid=".$stories['sid'];
256
+				$warning = _RUSERSONLY." - $warningtext";		
257
+			}
258
+			if(!empty($warning)) {
259
+				$warning = preg_replace("@'@", "\'", $warning);
260
+				$title = "<a href=\"javascript:if(confirm('".$warning."')) location = '"._BASEDIR."$location'\">".$stories['title']."</a>";
261
+			}
262
+			else $title = "<a href=\""._BASEDIR."viewstory.php?sid=".$stories['sid']."\">".$stories['title']."</a>";
263
+		}
264
+	return $title;
265
+}
266
+
267
+// Same with the author list
268
+function author_link($stories) {
269
+	if(is_array($stories['coauthors'])) {
270
+		$authlink[] = "<a href=\""._BASEDIR."viewuser.php?uid=".$stories['uid']."\">".$stories['penname']."</a>";
271
+		$coauth = dbquery("SELECT "._PENNAMEFIELD." as penname, co.uid FROM ".TABLEPREFIX."fanfiction_coauthors AS co LEFT JOIN "._AUTHORTABLE." ON co.uid = "._UIDFIELD." WHERE co.sid = '".$stories['sid']."'");
272
+		foreach($stories['coauthors'] AS $k => $v) {
273
+			$authlink[] = "<a href=\""._BASEDIR."viewuser.php?uid=".$k."\">".$v."</a>";
274
+		}
275
+	}
276
+	return isset($authlink) ? implode(", ", $authlink) : "<a href=\""._BASEDIR."viewuser.php?uid=".$stories['uid']."\">".$stories['penname']."</a>";
277
+}
278
+
279
+// Used to truncate text (summaries in blocks for example) to a set length.  An improvement on the old version as this keeps words intact
280
+function truncate_text($str, $n = 75, $delim='...') { 
281
+   $len = strlen($str);
282
+   if($len > $n) {
283
+        $pos = strpos($str, " ", $n);
284
+	if($pos) $str = trim(substr($str, 0, $pos), "\n\t\.,"). $delim;
285
+  }
286
+  return closetags($str);
287
+} 
288
+
289
+// A helper function for the truncate_text function.  This will close all open tags
290
+function closetags($html){
291
+
292
+  $donotclose=array('br','img','input', 'hr');
293
+  preg_match_all("#<([a-z]+)( .*)?(?!/)>#iU",$html,$result);
294
+  $openedtags=$result[1];
295
+
296
+  preg_match_all("#</([a-z]+)>#iU",$html,$result);
297
+  $closedtags=$result[1];
298
+  $len_opened = count($openedtags);
299
+  if(count($closedtags) == $len_opened){
300
+	  return $html;
301
+  }
302
+
303
+  $openedtags = array_reverse($openedtags);
304
+
305
+  for($i=0;$i < $len_opened;$i++) {
306
+
307
+    if (!in_array($openedtags[$i],$closedtags) && !in_array($openedtags[$i], $donotclose)){
308
+      $html .= '</'.$openedtags[$i].'>';
309
+    } 
310
+    else {
311
+      unset($closedtags[array_search($openedtags[$i],$closedtags)]);
312
+    }
313
+  }
314
+  return $html;
315
+}
316
+
317
+//  Builds the naughty word list for the site's censor
318
+function build_word($myword) {
319
+	$letters =preg_split('//',$myword, -1, PREG_SPLIT_NO_EMPTY);
320
+	$word = "";
321
+	foreach($letters as $letter) { 
322
+		if($word != "") $word .= "\W+";
323
+		$word .= $letter;
324
+	}
325
+	return $word;
326
+
327
+}
328
+
329
+// Finds forbidden words in text.  Returns true if a forbidden word is found
330
+function find_naughty($text) {
331
+	global $words;
332
+	if(!count($words)) return false;
333
+	$naughty = 0;
334
+	for($i = 0; $i < sizeof($words); $i++) {
335
+		if(strpos($words[$i], "*") === false) {
336
+			// check for whole word
337
+			if(preg_match('/(\s|^)+(('.$words[$i].')|('.build_word($words[$i]).')+(s|es)?)+(\W|$)+/i', $text, $match)) {
338
+				echo $words[$i] ." 0=".$match[0];
339
+				$naughty = 1; 
340
+			}
341
+		}
342
+		if(strpos($words[$i], "*") !== false && strpos($words[$i], "*") == 0 && !$naughty) {
343
+			// remove the * from the beginning of the word
344
+			$word = substr($words[$i], 1, strlen($words[$i]));
345
+			if(strrpos($word, "*") == strlen($word) - 1) $word = substr($word, 0, strlen($word) - 1);
346
+			// check for whole word plus word as suffix
347
+			if(preg_match('/[\s|^]+\w*('.$word.'|\W'.build_word($word).')+(s|es)?(\W|$)/i', $text, $match)) $naughty = 1; 
348
+		}
349
+		if(strrpos($words[$i], "*") == strlen($words[$i]) - 1 && !$naughty) {
350
+			// remove the * from the end of the word
351
+			$word = substr($words[$i], 0, strlen($words[$i]) - 1);
352
+			if(strpos($word, "*") !== false && strpos($word, "*") == 0) $word = substr($word, 1, strlen($word));
353
+			// check for whole word plus word as prefix
354
+			if(preg_match('/(\s|^)('.$word.'|'.build_word($word).'\W)+\w*(s|es)?(\W|$)/i', $text, $match)) $naughty = 1; 
355
+		}
356
+		if($naughty) break;  // no sense continuing down the list and this will make it run faster
357
+	}
358
+	return $naughty;
359
+}
360
+
361
+// Replaces forbidden words in text.  The forbidden word is replaced with the first letter and trailing ***
362
+function replace_naughty($text) {
363
+	global $words;
364
+
365
+	if(!count($words)) return $text;
366
+	$i = 0;
367
+	for($j = 0; $j < sizeof($words); $j++) {
368
+		if(strpos($words[$j], "*") === false) {
369
+			$replace[$i] = str_pad($words[$j][0], strlen($words[$j]), "*");
370
+			$naughtywords[$i] = '/\b('.$words[$j].'\b)|('.build_word($words[$j]).')\b/i';
371
+			$i++;
372
+		}
373
+
374
+		if(strpos($words[$j], "*") !== false && strpos($words[$j], "*") == 0) {
375
+			$word = substr($words[$j], 1, strlen($words[$j]));
376
+			if(strrpos($word, "*") == strlen($word) - 1) $word = substr($word, 0, strlen($word) - 1);
377
+			$naughtywords[$i] = '/(\s|^)+(\w*)+('.$word.'|'.build_word($word).')/i';
378
+			$replace[$i] = "$1$2".str_pad(substr($word, 0, 1), strlen($word), "*");
379
+			$i++;
380
+		}
381
+		if(strrpos($words[$j], "*") == strlen($words[$j]) - 1) {
382
+			$word = substr($words[$j], 0, strlen($words[$j]) - 1);
383
+			if(strpos($word, "*") !== false && strpos($word, "*") == 0) $word = substr($word, 1, strlen($word));
384
+			$replace[$i] = '$1'.str_pad(substr($word, 0, 1), strlen($word), "*").'$3';
385
+			$naughtywords[$i] = '/(.*)('.$word.'|'.build_word($word).')(\s|\w)/i';
386
+			$i++;
387
+		}
388
+	}
389
+	$text = preg_replace($naughtywords, $replace, $text);
390
+	return $text;
391
+}
392
+
393
+// Format for messages sent back from various forms and actions 
394
+function write_message($str) {
395
+	return "<div style='text-align: center; margin: 1em;'>$str</div>";
396
+}
397
+
398
+// Formats error messages sent back from various forms and actions
399
+function write_error($str) {
400
+	return "<div style='text-align: center; margin: 1em;' class='errortext'>$str</div>";
401
+}
402
+
403
+// Checks that the given $num is actually a number.  Used to help prevent XSS attacks.
404
+function isNumber($num) {
405
+	if(empty($num)) return false;
406
+	if(!is_string($num)) return false;
407
+	return preg_match("/^[0-9]+$/", $num);
408
+}
409
+
410
+// May be needed for sites that have bridged the authors table
411
+function check_prefs($uid) {
412
+	$test = dbquery("SELECT uid FROM ".TABLEPREFIX."fanfiction_authorprefs WHERE uid = '$uid'");
413
+	if(dbnumrows($test)) return true;
414
+	else return false;
415
+}
416
+
417
+// Function builds the alphabet links on various pages.
418
+function build_alphalinks($url, $let) {
419
+	global $alphabet;
420
+
421
+	$alpha = "<div id=\"alphabet\">";
422
+	foreach( $alphabet as $link ) {
423
+		// Build a link that calls a function with ($link and 1 (page number) )
424
+		$alpha .= "<a href=\"{$url}let=$link\"".($let == $link ? " id='currentletter'" : "").">$link</a> \n";
425
+	}
426
+	$alpha .= "</div>";
427
+	return $alpha;
428
+}
429
+
430
+// Function builds the pagination links
431
+function build_pagelinks($url, $total, $offset = 0, $columns = 1) {
432
+	global $itemsperpage, $linkstyle, $linkrange;
433
+	$pages = "";
434
+	$itemsperpage = $itemsperpage * $columns;
435
+
436
+	if($itemsperpage >= $total) return;
437
+
438
+	if(empty($linkrange)) $linkrange = 4;
439
+
440
+	$totpages = floor($total/$itemsperpage) + ($total % $itemsperpage ? 1 : 0);
441
+	$curpage = floor($offset/$itemsperpage) + 1;
442
+	if(!$linkstyle) $startrange = $curpage;
443
+	else {
444
+		if($totpages <= $linkrange || $curpage == 1) $startrange = 1;
445
+		else if($curpage >= $totpages - floor($linkrange / 2) + 1) $startrange = $totpages - $linkrange;
446
+		else $startrange = $curpage - floor($linkrange / 2) > 0 ? $curpage - floor($linkrange / 2) : 1;
447
+	}
448
+	if($startrange >= $totpages - $linkrange ) $startrange = $totpages - $linkrange > 0 ? $totpages - $linkrange : 1;
449
+	$stoprange = $totpages > $startrange + $linkrange ? $startrange + $linkrange : $totpages + 1;
450
+	if($curpage > 1 && $linkstyle != 1) $pages .= "<a href='".$url."offset=".( $offset - $itemsperpage)."' id='plprev'>["._PREVIOUS."]</a> ";
451
+	if($startrange > 1 && $linkstyle > 0) $pages .= "<a href='".$url."offset=0'>1</a><span class='ellipses'>...</span>";
452
+	for($x = $startrange; $x < $stoprange; $x++) {
453
+		$pages .= "<a href='".$url."offset=".(($x - 1) * $itemsperpage)."'".($x == $curpage ? "id='currentpage'" : "").">".$x."</a> \n";
454
+	}
455
+	if($stoprange < $totpages && $linkstyle > 0) $pages .= "<span class='ellipses'>...</span> <a href='".$url."offset=".(($totpages - 1) * $itemsperpage)."'>$totpages</a>\n";
456
+	if ($curpage < $totpages && $linkstyle != 1) $pages .=  " <a href='".$url."offset=".($offset+$itemsperpage)."' id='plnext'>["._NEXT."]</a>";
457
+	return "<div id=\"pagelinks\">$pages</div>";
458
+}
459
+
460
+// Function that returns the ratings picks 
461
+function ratingpics($rating) {
462
+	global $ratings, $like, $dislike, $star, $halfstar;
463
+	$ratingpics = "";
464
+	if($ratings == "2") {
465
+		if($rating >= 0.5)
466
+			$ratingpics = ($like ? $like : "<img src=\""._BASEDIR."images/like.gif\" alt=\""._LIKED."\">");
467
+		else if(($rating < 0.5) && ($rating > 0))
468
+			$ratingpics = ($dislike ? $dislike :"<img src=\""._BASEDIR."images/dislike.gif\" alt=\""._DISLIKED."\">");
469
+		else $ratingpics = "";
470
+	}
471
+	if($ratings == "1") {
472
+		global $star, $halfstar;
473
+		if($rating > 0) {
474
+			for($x = 0; $x < ($rating / 2) - .5; $x++) {
475
+				$ratingpics .= ($star ? $star  : "<img src=\""._BASEDIR."images/star.gif\" alt=\"star\">");
476
+			}
477
+			if($rating % 2 != 0) $ratingpics .= ($halfstar ? $halfstar  : "<img src=\""._BASEDIR."images/starhalf.gif\" alt=\"half-star\">");
478
+		}
479
+		else $ratingpics = "";
480
+	}
481
+	if(!empty($ratingpics)) return "<span style='white-space: nowrap;'>$ratingpics</span>"; // the no-wrap style will keep the stars together
482
+	else return;
483
+}
484
+
485
+// This function builds the list of category links (including the breadcrumb depending on settings)
486
+function catlist($catid) {
487
+	global $extendcats, $catlist, $action;
488
+
489
+	if(!is_array($catid)) $catid = explode(",", $catid);
490
+	$categorylinks = array();
491
+	foreach($catid as $cat) {
492
+		if(empty($catlist[$cat])) continue;
493
+		if($extendcats) {
494
+			unset($link);
495
+			$thiscat = $cat;
496
+			while(isset($thiscat)) {
497
+				if(isset($link)) $link = " > ".$link;
498
+				else $link = "";
499
+				if($action != "printable") $link = "<a href='"._BASEDIR."browse.php?type=categories&amp;catid=$thiscat'>".$catlist[$thiscat]['name']."</a>".$link;
500
+				else $link = $catlist[$thiscat]['name'].$link;
501
+				if($catlist[$thiscat]['pid'] == -1) unset($thiscat);
502
+				else $thiscat = $catlist[$thiscat]['pid'];
503
+			}
504
+			$categorylinks[] = $link;
505
+		}
506
+		else $categorylinks[] = "<a href='"._BASEDIR."browse.php?type=categories&amp;catid=$cat'>".$catlist[$cat]['name']."</a>";
507
+	}
508
+	return implode(", ", $categorylinks);
509
+}
510
+
511
+// This function builds the list of character links
512
+function charlist($characters) {
513
+	global $charlist, $action;
514
+
515
+	if(!is_array($characters)) $characters = explode(",", $characters);
516
+	$charlinks = array( );
517
+	foreach($characters as $c) {
518
+		if(empty($charlist[$c]['name'])) continue;
519
+		if($action != "printable") $charlinks[] = "<a href='"._BASEDIR."browse.php?type=characters&amp;charid=$c'>".$charlist[$c]['name']."</a>";
520
+		else $charlinks[] = $charlist[$c]['name'];
521
+	}
522
+	return implode(", ", $charlinks);
523
+}
524
+
525
+// Most of the pages that list stories and series use this fuction.  This handles showing the series and stories and pagination of the two together when needed
526
+function search($storyquery, $countquery, $pagelink = "search.php?", $pagetitle = 0) {
527
+	global $tpl, $new, $ratingslist, $itemsperpage, $reviewsallowed, $output, $dateformat, $current, $featured, $favorites, $retired, $ageconsent, $classtypelist, $classlist, $offset, $recentdays;
528
+     
529
+	$count = dbquery($countquery);
530
+	list($numrows) = dbrow($count);
531
+	if($numrows) {
532
+		$tpl->assign("output", ($pagetitle ? "<div id=\"pagetitle\">$pagetitle</div>" : ""));
533
+		$tpl->newBlock("listings");
534
+		if(!$ratingslist) {
535
+			$ratlist = dbquery("SELECT * FROM ".TABLEPREFIX."fanfiction_ratings");
536
+			while($rate = dbassoc($ratlist)) {
537
+				$ratings[$rate['rid']] = array("rating" => $rate['rating'], "ratingwarning" => $rate['ratingwarning'], "warningtext" => $rate['warningtext']);
538
+			}
539
+		}
540
+		$tpl->newBlock("listings");
541
+		$tpl->gotoBlock("listings");
542
+		$tpl->assign("stories",  "<div class=\"sectionheader\">"._STORIES."</div>");
543
+		$storyquery .= " LIMIT $offset, $itemsperpage";
544
+		$result3 = dbquery($storyquery);     
545
+		$count = 0;                     
546
+		while($stories = dbassoc($result3)) {       
547
+			$tpl->newBlock("storyblock");
548
+			include(_BASEDIR."includes/storyblock.php"); 
549
+		}
550
+		$tpl->gotoBlock("_ROOT");		
551
+	}
552
+	else {
553
+		$tpl->newBlock("listings");
554
+		$tpl->assign("pagelinks", write_message(_NORESULTS));
555
+	}
556
+	if($numrows > $itemsperpage) {
557
+		$termArray = array_merge($_GET, $_POST);
558
+		$terms = array();
559
+		foreach($termArray as $term => $value) {
560
+			if($term == "submit" || $term == "go" || $term == "offset" || ($term != "complete" &&empty($value))) continue;
561
+			$terms[] = "$term=".(is_array($value) ? implode(",", $value) : $value);
562
+		}
563
+		$terms = implode("&amp;", $terms);
564
+		$terms .= "&amp;";
565
+		$tpl->gotoBlock("listings");
566
+		$tpl->assign( "pagelinks", build_pagelinks($pagelink.$terms, $numrows, $offset));
567
+	}
568
+	$tpl->gotoBlock("_ROOT");
569
+	return $numrows;
570
+}
571 571
 ?>
572 572
\ No newline at end of file
Browse code

attempt to fix national characters coding

Jimmi08 authored on 2020/01/15 12:04:08
Showing 1 changed files
... ...
@@ -525,7 +525,7 @@ function charlist($characters) {
525 525
 // Most of the pages that list stories and series use this fuction.  This handles showing the series and stories and pagination of the two together when needed
526 526
 function search($storyquery, $countquery, $pagelink = "search.php?", $pagetitle = 0) {
527 527
 	global $tpl, $new, $ratingslist, $itemsperpage, $reviewsallowed, $output, $dateformat, $current, $featured, $favorites, $retired, $ageconsent, $classtypelist, $classlist, $offset, $recentdays;
528
-
528
+     
529 529
 	$count = dbquery($countquery);
530 530
 	list($numrows) = dbrow($count);
531 531
 	if($numrows) {
... ...
@@ -541,9 +541,9 @@ function search($storyquery, $countquery, $pagelink = "search.php?", $pagetitle
541 541
 		$tpl->gotoBlock("listings");
542 542
 		$tpl->assign("stories",  "<div class=\"sectionheader\">"._STORIES."</div>");
543 543
 		$storyquery .= " LIMIT $offset, $itemsperpage";
544
-		$result3 = dbquery($storyquery);
545
-		$count = 0;
546
-		while($stories = dbassoc($result3)) { 
544
+		$result3 = dbquery($storyquery);     
545
+		$count = 0;                     
546
+		while($stories = dbassoc($result3)) {       
547 547
 			$tpl->newBlock("storyblock");
548 548
 			include(_BASEDIR."includes/storyblock.php"); 
549 549
 		}
Browse code

Initial uplad w/ php7 fixes

Rainer Volkrodt authored on 2016/03/12 15:54:02
Showing 1 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,571 @@
1
+<?php
2
+// ----------------------------------------------------------------------
3
+// Copyright (c) 2007 by Tammy Keefer
4
+// Based on eFiction 1.1
5
+// Copyright (C) 2003 by Rebecca Smallwood.
6
+// http://efiction.sourceforge.net/
7
+// ----------------------------------------------------------------------
8
+// LICENSE
9
+//
10
+// This program is free software; you can redistribute it and/or
11
+// modify it under the terms of the GNU General Public License (GPL)
12
+// as published by the Free Software Foundation; either version 2
13
+// of the License, or (at your option) any later version.
14
+//
15
+// This program is distributed in the hope that it will be useful,
16
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
17
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
+// GNU General Public License for more details.
19
+//
20
+// To read the license please visit http://www.gnu.org/copyleft/gpl.html
21
+// ----------------------------------------------------------------------
22
+
23
+if(!defined("_CHARSET")) exit( );
24
+
25
+// Validates emails
26
+function validEmail($str) {
27
+	return (bool) preg_match('/^[-_a-z0-9\'+*$^&%=~!?{}]++(?:\.[-_a-z0-9\'+*$^&%=~!?{}]+)*@(?:(?![-.])[-a-z0=9.]+(?<![-.])\.[a-z]{2,6}|\d{1,3}(?:\.\d{1,3}){3})(?::\d++)?$/iD', $str);
28
+}
29
+
30
+// Function used to add and remove stories from category counts
31
+function categoryitems($catid, $value)
32
+{
33
+	//add or subtract one to the current categories
34
+	//find out current category's parent
35
+	if(is_array($catid)) $cats = $catid;
36
+	else $cats = array($catid);
37
+	$cats = array_filter($cats, "isNumber");
38
+	$catquery = dbquery("SELECT catid, parentcatid, leveldown FROM ".TABLEPREFIX."fanfiction_categories WHERE FIND_IN_SET(catid, '$catid') GROUP BY catid");
39
+	while(isset($catquery)) {
40
+		while($cat = dbassoc($catquery)) {
41
+			$pcats = array();
42
+			if($cat['leveldown'] > 0) $pcats[] = $cat['parentcatid'];
43
+			if(!in_array($cat['catid'], $cats)) $cats[] = $cat['catid'];
44
+		}
45
+		if(count($pcats) > 0) $catquery = dbquery("SELECT catid, parentcatid, leveldown FROM ".TABLEPREFIX."fanfiction_categories WHERE FIND_IN_SET(catid, '".implode($pcats)."') GROUP BY catid");
46
+		else unset($catquery);
47
+	}
48
+	dbquery("UPDATE ".TABLEPREFIX."fanfiction_categories SET numitems = (numitems + $value) WHERE FIND_IN_SET(catid, '".implode(",", $cats)."')");
49
+}
50
+
51
+// Function to recurse through categories to build a list of the category and all it's sub-categories.
52
+function recurseCategories($catid) {
53
+	global $catlist;
54
+	$$catid = $catlist;
55
+	$categorylist[] = $catid;
56
+	foreach($$catid as $cat => $info) {
57
+		if($info['pid'] == $catid) {
58
+			$categorylist = array_merge($categorylist, recurseCategories($cat));
59
+		}
60
+	}
61
+	return $categorylist;
62
+}
63
+
64
+// Captcha script validation
65
+function captcha_confirm() {
66
+	if(empty($_SESSION[SITEKEY.'_digit'])) return false;
67
+	$digit = $_SESSION[SITEKEY.'_digit'];
68
+	$userdigit = $_POST['userdigit'];
69
+	unset($_SESSION[SITEKEY.'_digit']);
70
+	if($digit == md5(SITEKEY.$userdigit) && $userdigit > 1) return true;
71
+	return false;
72
+}
73
+
74
+// Sanitizes user input to help prevent XSS attacks 
75
+function descript($text) {
76
+	// Convert problematic ascii characters to their true values
77
+	$search = array("40","41","58","65","66","67","68","69","70",
78
+		"71","72","73","74","75","76","77","78","79","80","81",
79
+		"82","83","84","85","86","87","88","89","90","97","98",
80
+		"99","100","101","102","103","104","105","106","107",
81
+		"108","109","110","111","112","113","114","115","116",
82
+		"117","118","119","120","121","122");
83
+	
84
+	$replace = array("(",")",":","a","b","c","d","e","f","g","h",
85
+		"i","j","k","l","m","n","o","p","q","r","s","t","u",
86
+		"v","w","x","y","z","a","b","c","d","e","f","g","h",
87
+		"i","j","k","l","m","n","o","p","q","r","s","t","u",
88
+		"v","w","x","y","z");
89
+
90
+	$entities = count($search);
91
+	
92
+	for ($i=0;$i < $entities;$i++) $text = preg_replace("#(&\#)(0*".$search[$i]."+);*#si", $replace[$i], $text);
93
+
94
+	// the following is based on code from bitflux (http://blog.bitflux.ch/wiki/)	
95
+	// Kill hexadecimal characters completely
96
+	$text = preg_replace('#(&\#x)([0-9A-F]+);*#si', "", $text);
97
+
98
+	// remove any attribute starting with "on" or xmlns
99
+
100
+	$text = preg_replace('#(<[^>]+[\\"\'\s])(onmouseover|onmousedown|onmouseup|onmouseout|onmousemove|onclick|ondblclick|onload|xmlns)[^>]*>#iU', ">", $text);
101
+
102
+	// remove javascript: and vbscript: protocol
103
+	
104
+	$text = preg_replace('#([a-z]*)=([\`\'\"]*)script:#iU', '$1=$2nojscript...', $text);
105
+	$text = preg_replace('#([a-z]*)=([\`\'\"]*)javascript:#iU', '$1=$2nojavascript...', $text);
106
+	$text = preg_replace('#([a-z]*)=([\'\"]*)vbscript:#iU', '$1=$2novbscript...', $text);
107
+
108
+	//<span style="width: expression(alert('Ping!'));"></span> (only affects ie...)
109
+	$text = preg_replace('#(<[^>]+)style=([\`\'\"]*).*expression\([^>]*>#iU', "$1>", $text);
110
+	$text = preg_replace('#(<[^>]+)style=([\`\'\"]*).*behaviour\([^>]*>#iU', "$1>", $text);
111
+	return $text;
112
+}
113
+
114
+// Call this function when the user tries to do something they shouldn't have access to.
115
+function accessDenied($str = ""){
116
+	global $tpl, $output;
117
+
118
+	if(!empty($str)) $output = write_error($str);
119
+	else $output = write_error(_NOTAUTHORIZED);
120
+	if(!empty($tpl)) {
121
+		$tpl->assign("output", $output);
122
+		$tpl->printToScreen( );
123
+		dbclose( );
124
+	}
125
+	else echo $output;
126
+	exit( );
127
+}
128
+
129
+// Call this function when something causes an error and the script has to die.
130
+function errorExit( $msg = ""){
131
+	global $tpl, $output;
132
+
133
+	$output .= write_error(_ERROR.(!empty($msg) ? " " : "").$msg);
134
+	$tpl->assign("output", $output);
135
+	$tpl->printToScreen( );
136
+	dbclose( );
137
+	exit( );
138
+}
139
+// The next three functions are used to calculate the series reviews and rating
140
+
141
+function storiesInSeries($thisseries) {
142
+	$storylist = array( );
143
+	if(!isNumber($thisseries)) return $storylist;
144
+	$serieslist = array( );
145
+	$stinseries = dbquery("SELECT sid, subseriesid FROM ".TABLEPREFIX."fanfiction_inseries WHERE seriesid = '$thisseries'");
146
+	while($st = dbassoc($stinseries)) { 
147
+		if(!empty($st['sid'])) $storylist[] = $st['sid'];
148
+		else if(!empty($st['subseriesid'])) $serieslist[] = $st['subseriesid'];
149
+	}
150
+	if($serieslist) {
151
+		foreach($serieslist as $s) {
152
+			$storylist = array_merge($storylist, storiesInSeries($s));
153
+		}
154
+	}
155
+	return $storylist;
156
+}
157
+function subseriesList($thisseries) {
158
+	$serieslist = array( );
159
+	if(!isNumber($thisseries)) return $serieslist;
160
+	$stinseries = dbquery("SELECT subseriesid FROM ".TABLEPREFIX."fanfiction_inseries WHERE seriesid = '$thisseries'");
161
+	while($st = dbassoc($stinseries)) {
162
+		$serieslist[] = $st['subseriesid']; 
163
+		$serieslist = array_merge($serieslist, subseriesList($st['subseriesid']));
164
+	}
165
+	return $serieslist;
166
+}
167
+
168
+function seriesreview($thisseries) {
169
+
170
+	if(!isNumber($thisseries)) return;
171
+	$storylist = storiesInSeries($thisseries);
172
+	$serieslist = subseriesList($thisseries);
173
+$newrating = dbquery("SELECT AVG(rating) as totalreviews FROM ".TABLEPREFIX."fanfiction_reviews 
174
+	WHERE ((item = '$thisseries' AND type = 'SE')".
175
+	(count($storylist) > 0 ? " OR (FIND_IN_SET(item, '".(implode(",", $storylist))."') > 0 AND type = 'ST')" : "").
176
+	(count($serieslist) > 0 ? " OR (FIND_IN_SET(item, '".(implode(",", $serieslist))."') > 0 AND type = 'SE')" : "").
177
+	") AND rating != '-1'");
178
+list($totalreviews) = dbrow($newrating);
179
+$newcount = dbquery("SELECT count(reviewid) as totalcount FROM ".TABLEPREFIX."fanfiction_reviews 
180
+	WHERE ((item = '$thisseries' AND type = 'SE')".
181
+	(count($storylist) > 0 ? " OR (FIND_IN_SET(item, '".(implode(",", $storylist))."') > 0 AND type = 'ST')" : "").
182
+	(count($serieslist) > 0 ? " OR (FIND_IN_SET(item, '".(implode(",", $serieslist))."') > 0 AND type = 'SE')" : "").
183
+	") AND review != 'No Review'");
184
+list($totalcount) = dbrow($newcount);
185
+if($totalcount) $update = dbquery("UPDATE ".TABLEPREFIX."fanfiction_series SET rating = '".round($totalreviews)."', reviews = '$totalcount' WHERE seriesid = '$thisseries'");
186
+$parentq = dbquery("SELECT seriesid FROM ".TABLEPREFIX."fanfiction_inseries WHERE subseriesid = '$thisseries' AND seriesid != '$thisseries'");
187
+while($parent2 = dbassoc($parentq)) { seriesreview($parent2['seriesid']); }
188
+
189
+}
190
+
191
+// Per a suggestion from jrabbit, we'll use this function to optimize some queries 
192
+function findclause($field,$set) {
193
+  if (empty($set)) {
194
+    return "1 = 0";
195
+  }
196
+  if(is_array($set)) $set = implode(",", $set);
197
+  if (strpos($set,',')>0) {
198
+    return "FIND_IN_SET($field,'$set') > 0";
199
+  }
200
+  return "$field='$set'";
201
+}
202
+
203
+// Added 3.3
204
+function nl2br2($string) {
205
+	$string = str_replace(array("\r\n", "\r", "\n"), "<br />", $string);
206
+	return $string;
207
+}
208
+
209
+// Formats the text of the story when displayed on screen.
210
+function format_story($text) {
211
+      $text = trim($text);
212
+      if(strpos($text, "<br>") === false && strpos($text, "<p>") === false && strpos($text, "<br />") === false) $text = nl2br2($text);
213
+      if(_CHARSET != "ISO-8859-1" && _CHARSET != "US-ASCII") return stripslashes($text);
214
+      $badwordchars = array(chr(212), chr(213), chr(210), chr(211), chr(209), chr(208), chr(201), chr(145), chr(146), chr(147), chr(148), chr(151), chr(150), chr(133));
215
+      $fixedwordchars = array('&#8216;', '&#8217;', '&#8220;', '&#8221;', '&#8212;', '&#8211;', '&#8230;', '&#8216;', '&#8217;', '&#8220;', '&#8221;', '&#8212;', '&#8211;',  '&#8230;' );
216
+      $text = str_replace($badwordchars,$fixedwordchars,stripslashes($text));
217
+      return $text;
218
+}
219
+
220
+// Function to Spam-protect emails.  Is called for the IM fields in the user's profile.
221
+function format_email($text) {
222
+	$search = array('@', '.');
223
+	$replace = array(" [AT] ", " [DOT] ");
224
+	return str_replace($search, $replace, $text);
225
+}
226
+
227
+// Function to format a URL into a clickable link
228
+function format_link($text, $title = "", $target = 0) {
229
+	if(empty($title)) $title = $text;
230
+	if(strpos($text, "http://") === false) $text = "http://".$text;
231
+	$text = "<a href='$text'".($target ? " target='_blank'" : "").">$title</a>";
232
+	return $text;
233
+}
234
+
235
+// Because this is used in places other than the listings of stories, we're setting it up as a function to be called as needed.
236
+function title_link($stories) {
237
+	global $ratingslist, $ageconsent, $disablepopups;
238
+
239
+	$rating = $stories['rid'];
240
+	$warningtext = !empty($ratingslist[$rating]['warningtext']) ? addslashes(strip_tags($ratingslist[$rating]['warningtext'])) : "";
241
+		if(empty($ratingslist[$rating]['ratingwarning']))
242
+			$title = "<a href=\""._BASEDIR."viewstory.php?sid=".$stories['sid']."\">".$stories['title']."</a>";
243
+		else {
244
+			$warning = "";
245
+			$warninglevel = sprintf("%03b", $ratingslist[$rating]['ratingwarning']);
246
+			if($warninglevel[2] && !isset($_SESSION[SITEKEY."_warned"][$rating])) {
247
+				$location = "viewstory.php?sid=".$stories['sid']."&amp;warning=$rating";
248
+				$warning = $warningtext;
249
+			}
250
+			if($warninglevel[1] && !$ageconsent && empty($_SESSION[SITEKEY.'_ageconsent'])) {
251
+				$location = "viewstory.php?sid=".$stories['sid']."&amp;ageconsent=ok&amp;warning=$rating";
252
+				$warning = _AGECHECK." - "._AGECONSENT." ".$warningtext." -- 1";
253
+			}
254
+			if($warninglevel[0] && !isMEMBER) {
255
+				$location = "user.php?action=login&amp;sid=".$stories['sid'];
256
+				$warning = _RUSERSONLY." - $warningtext";		
257
+			}
258
+			if(!empty($warning)) {
259
+				$warning = preg_replace("@'@", "\'", $warning);
260
+				$title = "<a href=\"javascript:if(confirm('".$warning."')) location = '"._BASEDIR."$location'\">".$stories['title']."</a>";
261
+			}
262
+			else $title = "<a href=\""._BASEDIR."viewstory.php?sid=".$stories['sid']."\">".$stories['title']."</a>";
263
+		}
264
+	return $title;
265
+}
266
+
267
+// Same with the author list
268
+function author_link($stories) {
269
+	if(is_array($stories['coauthors'])) {
270
+		$authlink[] = "<a href=\""._BASEDIR."viewuser.php?uid=".$stories['uid']."\">".$stories['penname']."</a>";
271
+		$coauth = dbquery("SELECT "._PENNAMEFIELD." as penname, co.uid FROM ".TABLEPREFIX."fanfiction_coauthors AS co LEFT JOIN "._AUTHORTABLE." ON co.uid = "._UIDFIELD." WHERE co.sid = '".$stories['sid']."'");
272
+		foreach($stories['coauthors'] AS $k => $v) {
273
+			$authlink[] = "<a href=\""._BASEDIR."viewuser.php?uid=".$k."\">".$v."</a>";
274
+		}
275
+	}
276
+	return isset($authlink) ? implode(", ", $authlink) : "<a href=\""._BASEDIR."viewuser.php?uid=".$stories['uid']."\">".$stories['penname']."</a>";
277
+}
278
+
279
+// Used to truncate text (summaries in blocks for example) to a set length.  An improvement on the old version as this keeps words intact
280
+function truncate_text($str, $n = 75, $delim='...') { 
281
+   $len = strlen($str);
282
+   if($len > $n) {
283
+        $pos = strpos($str, " ", $n);
284
+	if($pos) $str = trim(substr($str, 0, $pos), "\n\t\.,"). $delim;
285
+  }
286
+  return closetags($str);
287
+} 
288
+
289
+// A helper function for the truncate_text function.  This will close all open tags
290
+function closetags($html){
291
+
292
+  $donotclose=array('br','img','input', 'hr');
293
+  preg_match_all("#<([a-z]+)( .*)?(?!/)>#iU",$html,$result);
294
+  $openedtags=$result[1];
295
+
296
+  preg_match_all("#</([a-z]+)>#iU",$html,$result);
297
+  $closedtags=$result[1];
298
+  $len_opened = count($openedtags);
299
+  if(count($closedtags) == $len_opened){
300
+	  return $html;
301
+  }
302
+
303
+  $openedtags = array_reverse($openedtags);
304
+
305
+  for($i=0;$i < $len_opened;$i++) {
306
+
307
+    if (!in_array($openedtags[$i],$closedtags) && !in_array($openedtags[$i], $donotclose)){
308
+      $html .= '</'.$openedtags[$i].'>';
309
+    } 
310
+    else {
311
+      unset($closedtags[array_search($openedtags[$i],$closedtags)]);
312
+    }
313
+  }
314
+  return $html;
315
+}
316
+
317
+//  Builds the naughty word list for the site's censor
318
+function build_word($myword) {
319
+	$letters =preg_split('//',$myword, -1, PREG_SPLIT_NO_EMPTY);
320
+	$word = "";
321
+	foreach($letters as $letter) { 
322
+		if($word != "") $word .= "\W+";
323
+		$word .= $letter;
324
+	}
325
+	return $word;
326
+
327
+}
328
+
329
+// Finds forbidden words in text.  Returns true if a forbidden word is found
330
+function find_naughty($text) {
331
+	global $words;
332
+	if(!count($words)) return false;
333
+	$naughty = 0;
334
+	for($i = 0; $i < sizeof($words); $i++) {
335
+		if(strpos($words[$i], "*") === false) {
336
+			// check for whole word
337
+			if(preg_match('/(\s|^)+(('.$words[$i].')|('.build_word($words[$i]).')+(s|es)?)+(\W|$)+/i', $text, $match)) {
338
+				echo $words[$i] ." 0=".$match[0];
339
+				$naughty = 1; 
340
+			}
341
+		}
342
+		if(strpos($words[$i], "*") !== false && strpos($words[$i], "*") == 0 && !$naughty) {
343
+			// remove the * from the beginning of the word
344
+			$word = substr($words[$i], 1, strlen($words[$i]));
345
+			if(strrpos($word, "*") == strlen($word) - 1) $word = substr($word, 0, strlen($word) - 1);
346
+			// check for whole word plus word as suffix
347
+			if(preg_match('/[\s|^]+\w*('.$word.'|\W'.build_word($word).')+(s|es)?(\W|$)/i', $text, $match)) $naughty = 1; 
348
+		}
349
+		if(strrpos($words[$i], "*") == strlen($words[$i]) - 1 && !$naughty) {
350
+			// remove the * from the end of the word
351
+			$word = substr($words[$i], 0, strlen($words[$i]) - 1);
352
+			if(strpos($word, "*") !== false && strpos($word, "*") == 0) $word = substr($word, 1, strlen($word));
353
+			// check for whole word plus word as prefix
354
+			if(preg_match('/(\s|^)('.$word.'|'.build_word($word).'\W)+\w*(s|es)?(\W|$)/i', $text, $match)) $naughty = 1; 
355
+		}
356
+		if($naughty) break;  // no sense continuing down the list and this will make it run faster
357
+	}
358
+	return $naughty;
359
+}
360
+
361
+// Replaces forbidden words in text.  The forbidden word is replaced with the first letter and trailing ***
362
+function replace_naughty($text) {
363
+	global $words;
364
+
365
+	if(!count($words)) return $text;
366
+	$i = 0;
367
+	for($j = 0; $j < sizeof($words); $j++) {
368
+		if(strpos($words[$j], "*") === false) {
369
+			$replace[$i] = str_pad($words[$j]{0}, strlen($words[$j]), "*");
370
+			$naughtywords[$i] = '/\b('.$words[$j].'\b)|('.build_word($words[$j]).')\b/i';
371
+			$i++;
372
+		}
373
+
374
+		if(strpos($words[$j], "*") !== false && strpos($words[$j], "*") == 0) {
375
+			$word = substr($words[$j], 1, strlen($words[$j]));
376
+			if(strrpos($word, "*") == strlen($word) - 1) $word = substr($word, 0, strlen($word) - 1);
377
+			$naughtywords[$i] = '/(\s|^)+(\w*)+('.$word.'|'.build_word($word).')/i';
378
+			$replace[$i] = "$1$2".str_pad(substr($word, 0, 1), strlen($word), "*");
379
+			$i++;
380
+		}
381
+		if(strrpos($words[$j], "*") == strlen($words[$j]) - 1) {
382
+			$word = substr($words[$j], 0, strlen($words[$j]) - 1);
383
+			if(strpos($word, "*") !== false && strpos($word, "*") == 0) $word = substr($word, 1, strlen($word));
384
+			$replace[$i] = '$1'.str_pad(substr($word, 0, 1), strlen($word), "*").'$3';
385
+			$naughtywords[$i] = '/(.*)('.$word.'|'.build_word($word).')(\s|\w)/i';
386
+			$i++;
387
+		}
388
+	}
389
+	$text = preg_replace($naughtywords, $replace, $text);
390
+	return $text;
391
+}
392
+
393
+// Format for messages sent back from various forms and actions 
394
+function write_message($str) {
395
+	return "<div style='text-align: center; margin: 1em;'>$str</div>";
396
+}
397
+
398
+// Formats error messages sent back from various forms and actions
399
+function write_error($str) {
400
+	return "<div style='text-align: center; margin: 1em;' class='errortext'>$str</div>";
401
+}
402
+
403
+// Checks that the given $num is actually a number.  Used to help prevent XSS attacks.
404
+function isNumber($num) {
405
+	if(empty($num)) return false;
406
+	if(!is_string($num)) return false;
407
+	return preg_match("/^[0-9]+$/", $num);
408
+}
409
+
410
+// May be needed for sites that have bridged the authors table
411
+function check_prefs($uid) {
412
+	$test = dbquery("SELECT uid FROM ".TABLEPREFIX."fanfiction_authorprefs WHERE uid = '$uid'");
413
+	if(dbnumrows($test)) return true;
414
+	else return false;
415
+}
416
+
417
+// Function builds the alphabet links on various pages.
418
+function build_alphalinks($url, $let) {
419
+	global $alphabet;
420
+
421
+	$alpha = "<div id=\"alphabet\">";
422
+	foreach( $alphabet as $link ) {
423
+		// Build a link that calls a function with ($link and 1 (page number) )
424
+		$alpha .= "<a href=\"{$url}let=$link\"".($let == $link ? " id='currentletter'" : "").">$link</a> \n";
425
+	}
426
+	$alpha .= "</div>";
427
+	return $alpha;
428
+}
429
+
430
+// Function builds the pagination links
431
+function build_pagelinks($url, $total, $offset = 0, $columns = 1) {
432
+	global $itemsperpage, $linkstyle, $linkrange;
433
+	$pages = "";
434
+	$itemsperpage = $itemsperpage * $columns;
435
+
436
+	if($itemsperpage >= $total) return;
437
+
438
+	if(empty($linkrange)) $linkrange = 4;
439
+
440
+	$totpages = floor($total/$itemsperpage) + ($total % $itemsperpage ? 1 : 0);
441
+	$curpage = floor($offset/$itemsperpage) + 1;
442
+	if(!$linkstyle) $startrange = $curpage;
443
+	else {
444
+		if($totpages <= $linkrange || $curpage == 1) $startrange = 1;
445
+		else if($curpage >= $totpages - floor($linkrange / 2) + 1) $startrange = $totpages - $linkrange;
446
+		else $startrange = $curpage - floor($linkrange / 2) > 0 ? $curpage - floor($linkrange / 2) : 1;
447
+	}
448
+	if($startrange >= $totpages - $linkrange ) $startrange = $totpages - $linkrange > 0 ? $totpages - $linkrange : 1;
449
+	$stoprange = $totpages > $startrange + $linkrange ? $startrange + $linkrange : $totpages + 1;
450
+	if($curpage > 1 && $linkstyle != 1) $pages .= "<a href='".$url."offset=".( $offset - $itemsperpage)."' id='plprev'>["._PREVIOUS."]</a> ";
451
+	if($startrange > 1 && $linkstyle > 0) $pages .= "<a href='".$url."offset=0'>1</a><span class='ellipses'>...</span>";
452
+	for($x = $startrange; $x < $stoprange; $x++) {
453
+		$pages .= "<a href='".$url."offset=".(($x - 1) * $itemsperpage)."'".($x == $curpage ? "id='currentpage'" : "").">".$x."</a> \n";
454
+	}
455
+	if($stoprange < $totpages && $linkstyle > 0) $pages .= "<span class='ellipses'>...</span> <a href='".$url."offset=".(($totpages - 1) * $itemsperpage)."'>$totpages</a>\n";
456
+	if ($curpage < $totpages && $linkstyle != 1) $pages .=  " <a href='".$url."offset=".($offset+$itemsperpage)."' id='plnext'>["._NEXT."]</a>";
457
+	return "<div id=\"pagelinks\">$pages</div>";
458
+}
459
+
460
+// Function that returns the ratings picks 
461
+function ratingpics($rating) {
462
+	global $ratings, $like, $dislike, $star, $halfstar;
463
+	$ratingpics = "";
464
+	if($ratings == "2") {
465
+		if($rating >= 0.5)
466
+			$ratingpics = ($like ? $like : "<img src=\""._BASEDIR."images/like.gif\" alt=\""._LIKED."\">");
467
+		else if(($rating < 0.5) && ($rating > 0))
468
+			$ratingpics = ($dislike ? $dislike :"<img src=\""._BASEDIR."images/dislike.gif\" alt=\""._DISLIKED."\">");
469
+		else $ratingpics = "";
470
+	}
471
+	if($ratings == "1") {
472
+		global $star, $halfstar;
473
+		if($rating > 0) {
474
+			for($x = 0; $x < ($rating / 2) - .5; $x++) {
475
+				$ratingpics .= ($star ? $star  : "<img src=\""._BASEDIR."images/star.gif\" alt=\"star\">");
476
+			}
477
+			if($rating % 2 != 0) $ratingpics .= ($halfstar ? $halfstar  : "<img src=\""._BASEDIR."images/starhalf.gif\" alt=\"half-star\">");
478
+		}
479
+		else $ratingpics = "";
480
+	}
481
+	if(!empty($ratingpics)) return "<span style='white-space: nowrap;'>$ratingpics</span>"; // the no-wrap style will keep the stars together
482
+	else return;
483
+}
484
+
485
+// This function builds the list of category links (including the breadcrumb depending on settings)
486
+function catlist($catid) {
487
+	global $extendcats, $catlist, $action;
488
+
489
+	if(!is_array($catid)) $catid = explode(",", $catid);
490
+	$categorylinks = array();
491
+	foreach($catid as $cat) {
492
+		if(empty($catlist[$cat])) continue;
493
+		if($extendcats) {
494
+			unset($link);
495
+			$thiscat = $cat;
496
+			while(isset($thiscat)) {
497
+				if(isset($link)) $link = " > ".$link;
498
+				else $link = "";
499
+				if($action != "printable") $link = "<a href='"._BASEDIR."browse.php?type=categories&amp;catid=$thiscat'>".$catlist[$thiscat]['name']."</a>".$link;
500
+				else $link = $catlist[$thiscat]['name'].$link;
501
+				if($catlist[$thiscat]['pid'] == -1) unset($thiscat);
502
+				else $thiscat = $catlist[$thiscat]['pid'];
503
+			}
504
+			$categorylinks[] = $link;
505
+		}
506
+		else $categorylinks[] = "<a href='"._BASEDIR."browse.php?type=categories&amp;catid=$cat'>".$catlist[$cat]['name']."</a>";
507
+	}
508
+	return implode(", ", $categorylinks);
509
+}
510
+
511
+// This function builds the list of character links
512
+function charlist($characters) {
513
+	global $charlist, $action;
514
+
515
+	if(!is_array($characters)) $characters = explode(",", $characters);
516
+	$charlinks = array( );
517
+	foreach($characters as $c) {
518
+		if(empty($charlist[$c]['name'])) continue;
519
+		if($action != "printable") $charlinks[] = "<a href='"._BASEDIR."browse.php?type=characters&amp;charid=$c'>".$charlist[$c]['name']."</a>";
520
+		else $charlinks[] = $charlist[$c]['name'];
521
+	}
522
+	return implode(", ", $charlinks);
523
+}
524
+
525
+// Most of the pages that list stories and series use this fuction.  This handles showing the series and stories and pagination of the two together when needed
526
+function search($storyquery, $countquery, $pagelink = "search.php?", $pagetitle = 0) {
527
+	global $tpl, $new, $ratingslist, $itemsperpage, $reviewsallowed, $output, $dateformat, $current, $featured, $favorites, $retired, $ageconsent, $classtypelist, $classlist, $offset, $recentdays;
528
+
529
+	$count = dbquery($countquery);
530
+	list($numrows) = dbrow($count);
531
+	if($numrows) {
532
+		$tpl->assign("output", ($pagetitle ? "<div id=\"pagetitle\">$pagetitle</div>" : ""));
533
+		$tpl->newBlock("listings");
534
+		if(!$ratingslist) {
535
+			$ratlist = dbquery("SELECT * FROM ".TABLEPREFIX."fanfiction_ratings");
536
+			while($rate = dbassoc($ratlist)) {
537
+				$ratings[$rate['rid']] = array("rating" => $rate['rating'], "ratingwarning" => $rate['ratingwarning'], "warningtext" => $rate['warningtext']);
538
+			}
539
+		}
540
+		$tpl->newBlock("listings");
541
+		$tpl->gotoBlock("listings");
542
+		$tpl->assign("stories",  "<div class=\"sectionheader\">"._STORIES."</div>");
543
+		$storyquery .= " LIMIT $offset, $itemsperpage";
544
+		$result3 = dbquery($storyquery);
545
+		$count = 0;
546
+		while($stories = dbassoc($result3)) { 
547
+			$tpl->newBlock("storyblock");
548
+			include(_BASEDIR."includes/storyblock.php"); 
549
+		}
550
+		$tpl->gotoBlock("_ROOT");		
551
+	}
552
+	else {
553
+		$tpl->newBlock("listings");
554
+		$tpl->assign("pagelinks", write_message(_NORESULTS));
555
+	}
556
+	if($numrows > $itemsperpage) {
557
+		$termArray = array_merge($_GET, $_POST);
558
+		$terms = array();
559
+		foreach($termArray as $term => $value) {
560
+			if($term == "submit" || $term == "go" || $term == "offset" || ($term != "complete" &&empty($value))) continue;
561
+			$terms[] = "$term=".(is_array($value) ? implode(",", $value) : $value);
562
+		}
563
+		$terms = implode("&amp;", $terms);
564
+		$terms .= "&amp;";
565
+		$tpl->gotoBlock("listings");
566
+		$tpl->assign( "pagelinks", build_pagelinks($pagelink.$terms, $numrows, $offset));
567
+	}
568
+	$tpl->gotoBlock("_ROOT");
569
+	return $numrows;
570
+}
571
+?>
0 572
\ No newline at end of file