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