bugfix + dead code
[banana.git] / include / spool.inc.php
1 <?php
2 /********************************************************************************
3 * include/spool.inc.php : spool subroutines
4 * -----------------------
5 *
6 * This file is part of the banana distribution
7 * Copyright: See COPYING files that comes with this distribution
8 ********************************************************************************/
9
10 if(!function_exists('_file_put_contents')) {
11 function file_put_contents($filename, $data) {
12 $fp = fopen($filename, 'w');
13 if(!$fp) {
14 trigger_error('file_put_contents cannot write in file '.$filename, E_USER_ERROR);
15 return;
16 }
17 fputs($fp, $data);
18 fclose($fp);
19
20 }
21 }
22
23
24 /** Class spoolhead
25 * class used in thread overviews
26 */
27 class SpoolHead
28 {
29 /** date (timestamp) */
30 var $date;
31 /** subject */
32 var $subject;
33 /** author */
34 var $from;
35 /** reference of parent */
36 var $parent;
37 /** paren is direct */
38 var $parent_direct;
39 /** array of children */
40 var $children = Array();
41 /** true if post is read */
42 var $isread;
43 /** number of posts deeper in this branch of tree */
44 var $desc;
45 /** same as desc, but counts only unread posts */
46 var $descunread;
47
48 /** constructor
49 * @param $_date INTEGER timestamp of post
50 * @param $_subject STRING subject of post
51 * @param $_from STRING author of post
52 * @param $_desc INTEGER desc value (1 for a new post)
53 * @param $_read BOOLEAN true if read
54 * @param $_descunread INTEGER descunread value (0 for a new post)
55 */
56
57 function SpoolHead($_date, $_subject, $_from, $_desc=1, $_read=true, $_descunread=0)
58 {
59 $this->date = $_date;
60 $this->subject = $_subject;
61 $this->from = $_from;
62 $this->desc = $_desc;
63 $this->isread = $_read;
64 $this->descunread = $_descunread;
65 }
66 }
67
68 /** Class spool
69 * builds and updates spool
70 */
71
72 define("BANANA_SPOOL_VERSION", '0.2');
73
74 class spool
75 {
76 var $version;
77 /** spool */
78 var $overview;
79 /** group name */
80 var $group;
81 /** array msgid => msgnum */
82 var $ids;
83 /** thread starts */
84 var $roots;
85
86 /** constructor
87 * @param $_nntp RESOURCE NNTP socket filehandle
88 * @param $_group STRING group name
89 * @param $_display INTEGER 1 => all posts, 2 => only threads with new posts
90 * @param $_since INTEGER time stamp (used for read/unread)
91 */
92
93 function spool(&$_nntp, $_group, $_display=0, $_since="")
94 {
95 global $news;
96
97 $do_save = false;
98
99 $spool_path = dirname(dirname(__FILE__)).'/spool';
100 $spoolfile = "$spool_path/spool-$_group.dat";
101
102 $groupinfo = $_nntp->group($_group);
103 $first = max($groupinfo[2]-$news['maxspool'], $groupinfo[1]);
104 $last = $groupinfo[2];
105
106 if (!$groupinfo) {
107 $this = null;
108 return false;
109 }
110 if (file_exists($spoolfile)) {
111 $this = unserialize(file_get_contents($spoolfile));
112 }
113
114 if ($this->version == BANANA_SPOOL_VERSION) {
115 $keys = array_values($this->ids);
116 rsort($keys);
117 // remove expired messages
118 for ($id = min(array_keys($this->overview)); $id<$first; $id++) {
119 $this->delid($id, false);
120 $do_save = true;
121 }
122 $start = max(array_keys($this->overview))+1;
123 } else {
124 unset($this->overview, $this->ids);
125 $this->group = $_group;
126 $this->version = BANANA_SPOOL_VERSION;
127 $start = $first;
128 }
129
130 if (($start<$last) && $groupinfo[0]) {
131 $do_save = true;
132
133 $dates = array_map("strtotime", $_nntp->xhdr("Date", "$start-$last"));
134 $subjects = array_map("headerdecode", $_nntp->xhdr("Subject", "$start-$last"));
135 $froms = array_map("headerdecode", $_nntp->xhdr("From", "$start-$last"));
136 $msgids = $_nntp->xhdr("Message-ID", "$start-$last");
137 $refs = $_nntp->xhdr("References", "$start-$last");
138
139 if (isset($this->ids)) {
140 $this->ids = array_merge($this->ids, array_flip($msgids));
141 } else {
142 $this->ids = array_flip($msgids);
143 }
144
145 foreach ($msgids as $id=>$msgid) {
146 $msg = new spoolhead($dates[$id], $subjects[$id], $froms[$id]);
147 $refs[$id] = str_replace("><", "> <", $refs[$id]);
148 $msgrefs = preg_split("/( |\t)/", strtr($refs[$id], $this->ids));
149 $parents = preg_grep("/^\d+$/", $msgrefs);
150 $msg->parent = array_pop($parents);
151 $msg->parent_direct = preg_match("/^\d+$/", array_pop($msgrefs));
152
153 if (isset($this->overview[$id])) {
154 $msg->desc = $this->overview[$id]->desc;
155 $msg->children = $this->overview[$id]->children;
156 }
157 $this->overview[$id] = $msg;
158
159 if ($p = $msg->parent) {
160 if (empty($this->overview[$p])) {
161 $this->overview[$p] = new spoolhead($dates[$p], $subjects[$p], $froms[$p], 1);
162 }
163 $this->overview[$msg->parent]->children[] = $id;
164
165 while ($p) {
166 $this->overview[$p]->desc += $msg->desc;
167 $p = $this->overview[$p]->parent;
168 }
169
170 }
171 }
172 }
173
174 if ($do_save) { $this->save($spoolfile); }
175
176 if ($_since) {
177 $newpostsids = $_nntp->newnews($_since, $_group);
178 if (sizeof($newpostsids)) {
179 $newpostsids = array_intersect($newpostsids, array_keys($this->ids));
180 if ($newpostsids && !is_null($newpostsids)) {
181 foreach ($newpostsids as $mid) {
182 $this->overview[$this->ids[$mid]]->isread = false;
183 $this->overview[$this->ids[$mid]]->descunread = 1;
184 $parentmid = $this->ids[$mid];
185 while (isset($parentmid)) {
186 $this->overview[$parentmid]->descunread ++;
187 $parentmid = $this->overview[$parentmid]->parent;
188 }
189 }
190 }
191 }
192 if (sizeof($newpostsids)>0) {
193 switch ($_display) {
194 case 1:
195 foreach ($this->roots as $k=>$i) {
196 if ($this->overview[$i]->descunread==0) {
197 $this->killdesc($i);
198 unset($this->roots[$k]);
199 }
200 }
201 break;
202 }
203 }
204 }
205
206 return true;
207 }
208
209 function save($file)
210 {
211 uasort($this->overview, "spoolcompare");
212
213 $this->roots = Array();
214 foreach($this->overview as $id=>$msg) {
215 if (is_null($msg->parent)) {
216 $this->roots[] = $id;
217 }
218 }
219
220 file_put_contents($file, serialize($this));
221 }
222
223 /** kill post and childrens
224 * @param $_id MSGNUM of post
225 */
226
227 function killdesc($_id)
228 {
229 if (sizeof($this->overview[$_id]->children)) {
230 foreach ($this->overview[$_id]->children as $c) {
231 $this->killdesc($c);
232 }
233 }
234 unset($this->overview[$_id]);
235 $msgid = array_search($_id, $this->ids);
236 if ($msgid) {
237 unset($this->ids[$msgid]);
238 }
239 }
240
241 /** delete a post from overview
242 * @param $_id MSGNUM of post
243 */
244
245 function delid($_id, $write=true)
246 {
247 if (isset($this->overview[$_id])) {
248 if (sizeof($this->overview[$_id]->parent)) {
249 $this->overview[$this->overview[$_id]->parent]->children =
250 array_diff($this->overview[$this->overview[$_id]->parent]->children, array($_id));
251 if (sizeof($this->overview[$_id]->children)) {
252 $this->overview[$this->overview[$_id]->parent]->children =
253 array_merge($this->overview[$this->overview[$_id]->parent]->children, $this->overview[$_id]->children);
254 foreach ($this->overview[$_id]->children as $c) {
255 $this->overview[$c]->parent = $this->overview[$_id]->parent;
256 $this->overview[$c]->parent_direct = false;
257 }
258 }
259 $p = $this->overview[$_id]->parent;
260 while ($p) {
261 $this->overview[$p]->desc--;
262 $p = $this->overview[$p]->parent;
263 }
264 } elseif (sizeof($this->overview[$_id]->children)) {
265 foreach ($this->overview[$_id]->children as $c) {
266 $this->overview[$c]->parent = null;
267 }
268 }
269 unset($this->overview[$_id]);
270 $msgid = array_search($_id, $this->ids);
271 if ($msgid) {
272 unset($this->ids[$msgid]);
273 }
274
275 if ($write) {
276 $this->save(dirname(dirname(__FILE__)).'/spool');
277 }
278 }
279 }
280
281 /** displays children tree of a post
282 * @param $_id INTEGER MSGNUM of post
283 * @param $_index INTEGER linear number of post in the tree
284 * @param $_first INTEGER linear number of first post displayed
285 * @param $_last INTEGER linear number of last post displayed
286 * @param $_ref STRING MSGNUM of current post
287 * @param $_pfx_node STRING prefix used for current node
288 * @param $_pfx_end STRING prefix used for children of current node
289 * @param $_head BOOLEAN true if first post in thread
290 */
291
292 function disp_desc($_id, $_index, $_first=0, $_last=0, $_ref="", $_pfx_node="", $_pfx_end="", $_head=true) {
293 global $css;
294 $spfx_f = '<img src="img/k1.gif" height="21" width="9" alt="o" />';
295 $spfx_n = '<img src="img/k2.gif" height="21" width="9" alt="*" />';
296 $spfx_Tnd = '<img src="img/T-direct.gif" height="21" width="12" alt="+" />';
297 $spfx_Lnd = '<img src="img/L-direct.gif" height="21" width="12" alt="`" />';
298 $spfx_snd = '<img src="img/s-direct.gif" height="21" width="5" alt="-" />';
299 $spfx_T = '<img src="img/T.gif" height="21" width="12" alt="+" />';
300 $spfx_L = '<img src="img/L.gif" height="21" width="12" alt="`" />';
301 $spfx_s = '<img src="img/s.gif" height="21" width="5" alt="-" />';
302 $spfx_e = '<img src="img/e.gif" height="21" width="12" alt="&nbsp;" />';
303 $spfx_I = '<img src="img/I.gif" height="21" width="12"alt="|" />';
304
305 if ($_index + $this->overview[$_id]->desc < $_first || $_index > $_last) {
306 return;
307 }
308
309 if ($_index>=$_first) {
310 $us = ($_index == $_ref);
311 $hc = empty($this->overview[$_id]->children);
312
313 echo '<tr class="'.($_index%2?$css["pair"]:$css["impair"])."\">\n";
314 echo "<td class=\"{$css['date']}\">"
315 .formatSpoolHeader("date", $this->overview[$_id]->date, $_id,
316 $this->group, $us, $this->overview[$_id]->isread)
317 ." </td>\n";
318 echo "<td class=\"{$css['subject']}\"><div class=\"{$css['tree']}\">$_pfx_node"
319 .($hc?($_head?$spfx_f:($this->overview[$_id]->parent_direct?$spfx_s:$spfx_snd)):$spfx_n)
320 ."</div>"
321 .formatSpoolHeader("subject", $this->overview[$_id]->subject, $_id,
322 $this->group, $us, $this->overview[$_id]->isread)
323 ." </td>\n";
324 echo "<td class=\"{$css['author']}\">"
325 .formatSpoolHeader("from", $this->overview[$_id]->from, $_id,
326 $this->group, $us, $this->overview[$_id]->isread)
327 ." </td>\n</tr>";
328 if ($hc) {
329 return true;
330 }
331 }
332
333 $index = $_index+1;
334
335 $children = $this->overview[$_id]->children;
336 while ($child = array_shift($children)) {
337 if ($index > $_last) { return; }
338 if ($index+$this->overview[$child]->desc >= $_first) {
339 if (sizeof($children)) {
340 $this->disp_desc($child, $index, $_first, $_last, $_ref, $_pfx_end.
341 ($this->overview[$child]->parent_direct?$spfx_T:$spfx_Tnd),
342 $_pfx_end.$spfx_I, false);
343 } else {
344 $this->disp_desc($child, $index, $_first, $_last, $_ref, $_pfx_end.
345 ($this->overview[$child]->parent_direct?$spfx_L:$spfx_Lnd),
346 $_pfx_end.$spfx_e, false);
347 }
348 }
349 $index += $this->overview[$child]->desc;
350 }
351 }
352
353 /** Displays overview
354 * @param $_first INTEGER MSGNUM of first post
355 * @param $_last INTEGER MSGNUM of last post
356 * @param $_ref STRING MSGNUM of current/selectionned post
357 */
358
359 function disp($_first=0, $_last=0, $_ref="") {
360 global $css;
361 $index = 1;
362 if (sizeof($this->overview)) {
363 foreach ($this->roots as $id) {
364 $this->disp_desc($id, $index, $_first, $_last, $_ref);
365 $index += $this->overview[$id]->desc ;
366 if ($index > $_last) {
367 break;
368 }
369 }
370 } else {
371 echo "<tr class=\"{$css['pair']}\">\n";
372 echo "\t<td colspan=\"3\">\n";
373 echo "\t\tNo post in this newsgroup\n";
374 echo "\t</td>\n";
375 echo "</tr>\n";
376 }
377 }
378
379 /** computes linear post index
380 * @param $_id INTEGER MSGNUM of post
381 * @return INTEGER linear index of post
382 */
383
384 function getndx($_id) {
385 $ndx = 1;
386 $id_cur = $_id;
387 while (true) {
388 $id_parent = $this->overview[$id_cur]->parent;
389 if (is_null($id_parent)) break;
390 $pos = array_search($id_cur, $this->overview[$id_parent]->children);
391
392 for ($i = 0; $i < $pos ; $i++) {
393 $ndx += $this->overview[$this->overview[$id_parent]->children[$i]]->desc;
394 }
395 $ndx++; //noeud père
396
397 $id_cur = $id_parent;
398 }
399
400 foreach ($this->roots as $i) {
401 if ($i==$id_cur) {
402 break;
403 }
404 $ndx += $this->overview[$i]->desc;
405 }
406 return $ndx;
407 }
408 }
409
410 ?>