4aadbeb884def4f96c314b8867fbcc2d762dafdf
[citadel.git] / webcit / bbsview_renderer.c
1 /* 
2  * $Id$
3  *
4  * BBS View renderer module for WebCit
5  *
6  * Copyright (c) 1996-2010 by the citadel.org team
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 3 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include "webcit.h"
24 #include "webserver.h"
25 #include "groupdav.h"
26
27 /*
28  * Data which gets passed around between the various functions in this module
29  */
30 struct bbsview {
31         long *msgs;             /* Array of msgnums for messages we are displaying */
32         int num_msgs;           /* Number of msgnums stored in 'msgs' */
33         int alloc_msgs;         /* Currently allocated size of array */
34         long lastseen;          /* msgnum of the last seen message in this room */
35 };
36
37
38 /*
39  * Attempt to determine the closest thing to the "last seen message number" using the
40  * results of the GTSN command
41  */
42 long bbsview_get_last_seen(void)
43 {
44         char buf[SIZ] = "0";
45
46         serv_puts("GTSN");
47         serv_getln(buf, sizeof buf);
48         if (buf[0] == '2') {
49
50                 char *comma_pos = strchr(buf, ',');     /* kill first comma and everything to its right */
51                 if (comma_pos) {
52                         *comma_pos = 0;
53                 }
54
55                 char *colon_pos = strchr(buf, ':');     /* kill first colon and everything to its left */
56                 if (colon_pos) {
57                         strcpy(buf, ++colon_pos);
58                 }
59         }
60
61         return(atol(buf));
62 }
63
64
65
66 /*
67  * Entry point for message read operations.
68  */
69 int bbsview_GetParamsGetServerCall(SharedMessageStatus *Stat, 
70                                    void **ViewSpecific, 
71                                    long oper, 
72                                    char *cmd, 
73                                    long len)
74 {
75         struct bbsview *BBS = malloc(sizeof(struct bbsview));
76         memset(BBS, 0, sizeof(struct bbsview));
77         *ViewSpecific = BBS;
78
79         Stat->startmsg = -1;
80         Stat->sortit = 1;
81         BBS->lastseen = bbsview_get_last_seen();        /* FIXME do something with this */
82         
83         rlid[oper].cmd(cmd, len);               /* this performs the server call to fetch the msg list */
84         
85         if (havebstr("maxmsgs")) {
86                 Stat->maxmsgs = ibstr("maxmsgs");
87         }
88         if (Stat->maxmsgs == 0) Stat->maxmsgs = DEFAULT_MAXMSGS;
89         Stat->num_displayed = DEFAULT_MAXMSGS;
90         
91         if (havebstr("startmsg")) {
92                 Stat->startmsg = lbstr("startmsg");
93         }
94
95         return 200;
96 }
97
98
99 /*
100  * begin_ajax_response() was moved from bbsview_LoadMsgFromServer() to here ...
101  */
102 int bbsview_PrintViewHeader(SharedMessageStatus *Stat, void **ViewSpecific)
103 {
104         if (WC->is_ajax) {
105                 begin_ajax_response();          /* for non-ajax, headers are output in messages.c */
106         }
107
108         return 200;
109 }
110
111
112 /*
113  * This function is called for every message in the list.
114  */
115 int bbsview_LoadMsgFromServer(SharedMessageStatus *Stat, 
116                               void **ViewSpecific, 
117                               message_summary* Msg, 
118                               int is_new, 
119                               int i)
120 {
121         struct bbsview *BBS = (struct bbsview *) *ViewSpecific;
122
123         if (BBS->alloc_msgs == 0) {
124                 BBS->alloc_msgs = Stat->maxmsgs;
125                 BBS->msgs = malloc(BBS->alloc_msgs * sizeof(long));
126         }
127
128         /* Theoretically this never happens because the initial allocation == maxmsgs */
129         if (BBS->num_msgs >= BBS->alloc_msgs) {
130                 BBS->alloc_msgs *= 2;
131                 BBS->msgs = realloc(BBS->msgs, (BBS->alloc_msgs * sizeof(long)));
132         }
133
134         BBS->msgs[BBS->num_msgs++] = Msg->msgnum;
135
136         return 200;
137 }
138
139 int bbsview_sortfunc(const void *s1, const void *s2) {
140         long l1;
141         long l2;
142
143         l1 = *(long *)(s1);
144         l2 = *(long *)(s2);
145
146         if (l1 > l2) return(+1);
147         if (l1 < l2) return(-1);
148         return(0);
149 }
150
151
152 int bbsview_RenderView_or_Tail(SharedMessageStatus *Stat, 
153                                void **ViewSpecific, 
154                                long oper)
155 {
156         struct bbsview *BBS = (struct bbsview *) *ViewSpecific;
157         int i;
158         const StrBuf *Mime;
159         char olderdiv[64];
160         char newerdiv[64];
161         int doing_older_messages = 0;
162         int doing_newer_messages = 0;
163
164         int increments[] = { 20, 50, 100 } ;
165 #define NUM_INCREMENTS  (sizeof(increments) / sizeof(int))
166
167         snprintf(olderdiv, sizeof olderdiv, "olderdiv%08lx%08x", time(NULL), rand());
168         snprintf(newerdiv, sizeof newerdiv, "newerdiv%08lx%08x", time(NULL), rand());
169
170         /* Determine whether we are in the middle of a 'click for older messages' or 'click for
171          * newer messages' operation.  If neither, then we are in the initial page load.
172          */
173         if (!strcasecmp(bstr("gt_or_lt"), "lt")) {
174                 doing_older_messages = 1;
175                 doing_newer_messages = 0;
176         }
177         else if (!strcasecmp(bstr("gt_or_lt"), "gt")) {
178                 doing_older_messages = 0;
179                 doing_newer_messages = 1;
180         }
181         else {
182                 doing_older_messages = 0;
183                 doing_newer_messages = 0;
184         }
185
186
187         /* Cut the message list down to the requested size */
188         if (Stat->nummsgs > 0) {
189                 lprintf(9, "sorting %d messages\n", BBS->num_msgs);
190                 qsort(BBS->msgs, (size_t)(BBS->num_msgs), sizeof(long), bbsview_sortfunc);
191
192                 /* Cut it down to 20 messages (or whatever value Stat->maxmsgs is set to) */
193
194                 if (BBS->num_msgs > Stat->maxmsgs) {
195
196                         if (doing_older_messages) {
197                                 /* LT ... cut it down to the LAST 20 messages received */
198                                 memcpy(&BBS->msgs[0], &BBS->msgs[BBS->num_msgs - Stat->maxmsgs],
199                                         (Stat->maxmsgs * sizeof(long))
200                                 );
201                                 BBS->num_msgs = Stat->maxmsgs;
202                         }
203                         else {
204                                 /* GT ... cut it down to the FIRST 20 messages received */
205                                 BBS->num_msgs = Stat->maxmsgs;
206                         }
207                 }
208         }
209
210
211         /* Supply the link to prepend the previous 20 messages */
212
213         if ((!WC->is_ajax) && (Stat->nummsgs == 0)) {
214                 wc_printf("<div id=\"%s\">", olderdiv);
215                 wc_printf("<div class=\"moreprompt\">");
216                 for (i=0; i<NUM_INCREMENTS; ++i) {
217                         wc_printf("<a href=\"javascript:moremsgs('%s', 'lt', %ld, %d);\">",
218                                 olderdiv,
219                                 LONG_MAX,
220                                 increments[i]
221                         );
222                         wc_printf("<span class=\"moreprompt_link\">&uarr; ");
223                         wc_printf(_("Previous %d"), increments[i]);
224                         wc_printf(" &uarr;</span>");
225                         wc_printf("</a>");
226                 }
227                 wc_printf("</div>");
228                 wc_printf("<div class=\"nomsgs\"><br><em>");
229                 wc_printf(_("No messages here."));
230                 wc_printf("</em><br></div>\n");
231                 wc_printf("</div>");
232         }
233         else if (doing_newer_messages == 0) {
234                 wc_printf("<div id=\"%s\">", olderdiv);
235                 wc_printf("<div class=\"moreprompt\">");
236                 if (Stat->nummsgs > 0) {
237                         for (i=0; i<NUM_INCREMENTS; ++i) {
238                                 wc_printf("<a href=\"javascript:moremsgs('%s', 'lt', %ld, %d);\">",
239                                         olderdiv,
240                                         BBS->msgs[0],
241                                         increments[i]
242                                 );
243                                 wc_printf("<span class=\"moreprompt_link\">&uarr; ");
244                                 wc_printf(_("Previous %d"), increments[i]);
245                                 wc_printf(" &uarr;</span>");
246                                 wc_printf("</a>");
247                         }
248                         wc_printf("</div>");
249                 }
250                 wc_printf("</div>");
251         }
252
253         /* Non-empty message set... */
254         if (Stat->nummsgs > 0) {
255                 /* Render the messages */
256         
257                 for (i=0; i<BBS->num_msgs; ++i) {
258                         read_message(WC->WBuf, HKEY("view_message"), BBS->msgs[i], NULL, &Mime);
259                 }
260
261         }
262
263
264         /* Supply the link to append the next 20 messages */
265
266         if (doing_older_messages == 0) {
267                 wc_printf("<div id=\"%s\">", newerdiv);
268                 if (Stat->nummsgs >= Stat->maxmsgs) {
269                         wc_printf("<div class=\"moreprompt\">");
270                         for (i=0; i<NUM_INCREMENTS; ++i) {
271                                 wc_printf("<a href=\"javascript:moremsgs('%s', 'gt', %ld, %d);\">",
272                                         newerdiv,
273                                         BBS->msgs[BBS->num_msgs-1],
274                                         increments[i]
275                                 );
276                                 wc_printf("<span class=\"moreprompt_link\">&darr; ");
277                                 wc_printf(_("Next %d"), increments[i]);
278                                 wc_printf(" &darr;</span>");
279                                 wc_printf("</a>");
280                         }
281                         wc_printf("</div>");
282                 }
283                 else {
284                         long gt = 0;    /* if new messages appear later, where will they begin? */
285                         if (Stat->nummsgs > 0) {
286                                 gt = BBS->msgs[BBS->num_msgs-1];
287                         }
288                         else {
289                                 gt = atol(bstr("gt"));
290                         }
291                         wc_printf("<a href=\"javascript:moremsgs('%s', 'gt', %ld, %ld);\">",
292                                 newerdiv,
293                                 gt,
294                                 Stat->maxmsgs
295                         );
296                         wc_printf("<div class=\"moreprompt\">");
297                         wc_printf("<span class=\"moreprompt_link\">&darr; ");
298                         wc_printf("%s", _("no more messages"));
299                         wc_printf(" &darr;</span>");
300                         wc_printf("</div>");
301                         wc_printf("</a>");
302                 }
303                 wc_printf("</div>");
304         }
305
306         /* Leave a little padding at the bottom, but only for the initial page load -- don't keep
307          * adding it every time we extend the visible message set.
308          */
309         if (!WC->is_ajax) {
310                 wc_printf("<br><br><br><br>");
311         }
312
313         return(0);
314 }
315
316
317 int bbsview_Cleanup(void **ViewSpecific)
318 {
319         struct bbsview *BBS = (struct bbsview *) *ViewSpecific;
320
321         if (BBS->alloc_msgs != 0) {
322                 free(BBS->msgs);
323         }
324         free(BBS);
325
326         if (WC->is_ajax) {
327                 end_ajax_response();
328                 WC->is_ajax = 0;
329         }
330         else {
331                 wDumpContent(1);
332         }
333         return 0;
334 }
335
336 void 
337 InitModule_BBSVIEWRENDERERS
338 (void)
339 {
340         RegisterReadLoopHandlerset(
341                 VIEW_BBS,
342                 bbsview_GetParamsGetServerCall,
343                 bbsview_PrintViewHeader,
344                 NULL, 
345                 bbsview_LoadMsgFromServer,
346                 bbsview_RenderView_or_Tail,
347                 bbsview_Cleanup);
348 }