1<?php
2
3use dokuwiki\Extension\Plugin;
4use dokuwiki\Utf8\Clean;
5
6/**
7 * Display Fortune cookies
8 *
9 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
10 * @author     Andreas Gohr <andi@splitbrain.org>
11 */
12class helper_plugin_xfortune extends Plugin
13{
14    /**
15     * Get a random cookie properly escaped
16     *
17     * @param string $cookieID the media file id to the cookie file
18     * @param int $maxlines
19     * @return string
20     */
21    public static function getCookieHTML($cookieID, $maxlines = 0, $maxchars = 0)
22    {
23        if ($maxlines) {
24            $cookie = self::getSmallCookie($cookieID, $maxlines, $maxchars);
25        } else {
26            $cookie = self::getCookie($cookieID);
27        }
28
29        return nl2br(hsc($cookie));
30    }
31
32    /**
33     * Tries to find a cookie with a maximum number of lines
34     *
35     * gives up after a 100 tries
36     *
37     * @param $cookieID
38     * @param int $maxlines maximum lines to return
39     * @return string
40     */
41    public static function getSmallCookie($cookieID, $maxlines, $maxchars)
42    {
43        $runaway = 100;
44        $tries = 0;
45        if ($maxlines < 1) $maxlines = 1;
46        if ($maxchars < 1) $maxlines = 250;
47
48        do {
49            $cookie = self::getCookie($cookieID);
50            $lines = count(explode("\n", $cookie));
51        } while (($lines > $maxlines || strlen($cookie) > $maxchars) && $tries++ < $runaway);
52
53        return $cookie;
54    }
55
56    /**
57     * Get a file for the given ID
58     *
59     * If the ID ends with a colon a namespace is assumed and a random txt file is picked from there
60     *
61     * @param $cookieID
62     * @return string
63     * @throws Exception
64     */
65    public static function id2file($cookieID)
66    {
67        $file = mediaFN($cookieID);
68        $isns = is_dir($file);
69        if ($isns) $cookieID .= ':dir';
70
71        if (auth_quickaclcheck($cookieID) < AUTH_READ) throw new Exception("No read permissions for $cookieID");
72
73        if ($isns) {
74            $dir = $file;
75            $files = glob("$dir/*.txt");
76            if (!count($files))  throw new Exception("Could not find fortune files in $cookieID");
77            $file = $files[array_rand($files)];
78        }
79
80        // we now should have a valid file
81        if (!file_exists($file)) throw new Exception("No fortune file at $cookieID");
82
83        return $file;
84    }
85
86    /**
87     * Returns one random cookie
88     *
89     * @param string $cookieID the media file id to the cookie file
90     * @return string
91     */
92    public static function getCookie($cookieID)
93    {
94        try {
95            $file = self::id2file($cookieID);
96        } catch (Exception $e) {
97            return 'ERROR: ' . $e->getMessage();
98        }
99
100        $dim = filesize($file);
101        if ($dim < 2) return "ERROR: invalid cookie file $file";
102        mt_srand((double) microtime() * 1000000);
103        $rnd = mt_rand(0, $dim);
104
105        $fd = fopen($file, 'r');
106        if (!$fd) return "ERROR: reading cookie file $file failed";
107
108        // jump to random place in file
109        fseek($fd, $rnd);
110
111        $text = '';
112        $cookie = false;
113        while (true) {
114            $seek = ftell($fd);
115            $line = fgets($fd, 1024);
116
117            if ($seek == 0) {
118                // start of file always starts a cookie
119                $cookie = true;
120                if ($line == "%\n") {
121                    // ignore delimiter if exists
122                    continue;
123                } else {
124                    // part of the cookie
125                    $text .= $line;
126                    continue;
127                }
128            }
129
130            if (feof($fd)) {
131                if ($cookie) {
132                    // we had a cookie already, stop here
133                    break;
134                } else {
135                    // no cookie yet, wrap around
136                    fseek($fd, 0);
137                    continue;
138                }
139            }
140
141            if ($line == "%\n") {
142                if ($cookie) {
143                    // we had a cookie already, stop here
144                    break;
145                } elseif ($seek == $dim - 2) {
146                    // it's the end of file delimiter, wrap around
147                    fseek($fd, 0);
148                    continue;
149                } else {
150                    // start of the cookie
151                    $cookie = true;
152                    continue;
153                }
154            }
155
156            // part of the cookie?
157            if ($cookie) {
158                $text .= $line;
159            }
160        }
161        fclose($fd);
162
163        $text = trim($text);
164
165        // if it is not valid UTF-8 assume it's latin1
166        if (!Clean::isUtf8($text)) return utf8_encode($text);
167
168        return $text;
169    }
170}
171