indent -kr -i8 -brf -bbb -fnc -l132 -nce on all of webcit-classic
[citadel.git] / webcit / bbsview_renderer.c
1
2 /* 
3  * BBS View renderer module for WebCit
4  *
5  * Note: we briefly had a dynamic UI for this.  I thought it was cool, but
6  * it was not received well by the user community.  If you want to play
7  * with it, go get commit dcf99fe61379b78436c387ea3f89ebfd4ffaf635 of
8  * bbsview_renderer.c and have fun.
9  *
10  * Copyright (c) 1996-2012 by the citadel.org team
11  *
12  * This program is open source software.  You can redistribute it and/or
13  * modify it under the terms of the GNU General Public License, version 3.
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
21 #define RANGE 5
22
23 #include "webcit.h"
24 #include "webserver.h"
25 #include "dav.h"
26
27 /*
28  * Data which gets passed around between the various functions in this module
29  *
30  */
31 struct bbsview {
32         long *msgs;             /* Array of msgnums for messages we are displaying */
33         int num_msgs;           /* Number of msgnums stored in 'msgs' */
34         long lastseen;          /* The number of the last seen message in this room */
35         int alloc_msgs;         /* Currently allocated size of array */
36         int requested_page;     /* Which page number did the user request? */
37         int num_pages;          /* Total number of pages in this room */
38         long start_reading_at;  /* Start reading at the page containing this message */
39 };
40
41
42 /*
43  * Attempt to determine the closest thing to the "last seen message number" using the
44  * results of the GTSN command
45  */
46 long bbsview_get_last_seen(void) {
47         char buf[SIZ] = "0";
48
49         serv_puts("GTSN");
50         serv_getln(buf, sizeof buf);
51         if (buf[0] == '2') {
52                 char *colon_pos;
53                 char *comma_pos;
54
55                 comma_pos = strchr(buf, ',');   /* kill first comma and everything to its right */
56                 if (comma_pos) {
57                         *comma_pos = 0;
58                 }
59
60                 colon_pos = strchr(buf, ':');   /* kill first colon and everything to its left */
61                 if (colon_pos) {
62                         strcpy(buf, ++colon_pos);
63                 }
64         }
65
66         return (atol(buf));
67 }
68
69
70
71 /*
72  * Entry point for message read operations.
73  */
74 int bbsview_GetParamsGetServerCall(SharedMessageStatus * Stat,
75                                    void **ViewSpecific, long oper, char *cmd, long len, char *filter, long flen) {
76         struct bbsview *BBS = malloc(sizeof(struct bbsview));
77         memset(BBS, 0, sizeof(struct bbsview));
78         *ViewSpecific = BBS;
79
80         Stat->startmsg = (-1);  /* not used here */
81         Stat->sortit = 1;       /* not used here */
82         Stat->num_displayed = DEFAULT_MAXMSGS;  /* not used here */
83         BBS->requested_page = 0;
84         BBS->lastseen = bbsview_get_last_seen();
85         BBS->start_reading_at = 0;
86
87         /* By default, the requested page is the first one. */
88         if (havebstr("start_reading_at")) {
89                 BBS->start_reading_at = lbstr("start_reading_at");
90                 BBS->requested_page = (-4);
91         }
92
93         /* However, if we are asked to start with a specific message number, make sure
94          * we start on the page containing that message
95          */
96
97         /* Or, if a specific page was requested, make sure we go there */
98         else if (havebstr("page")) {
99                 BBS->requested_page = ibstr("page");
100         }
101
102         /* Otherwise, if this is a "read new" operation, make sure we start on the page
103          * containing the first new message
104          */
105         else if (oper == 3) {
106                 BBS->requested_page = (-3);
107         }
108
109         if (havebstr("maxmsgs")) {
110                 Stat->maxmsgs = ibstr("maxmsgs");
111         }
112         if (Stat->maxmsgs == 0)
113                 Stat->maxmsgs = DEFAULT_MAXMSGS;
114
115         /* perform a "read all" call to fetch the message list -- we'll cut it down later */
116         rlid[2].cmd(cmd, len);
117
118         return 200;
119 }
120
121
122 /*
123  * This function is called for every message in the list.
124  */
125 int bbsview_LoadMsgFromServer(SharedMessageStatus * Stat, void **ViewSpecific, message_summary * Msg, int is_new, int i) {
126         struct bbsview *BBS = (struct bbsview *) *ViewSpecific;
127
128         if (BBS->alloc_msgs == 0) {
129                 BBS->alloc_msgs = 1000;
130                 BBS->msgs = malloc(BBS->alloc_msgs * sizeof(long));
131                 memset(BBS->msgs, 0, (BBS->alloc_msgs * sizeof(long)));
132         }
133
134         /* Check our buffer size */
135         if (BBS->num_msgs >= BBS->alloc_msgs) {
136                 BBS->alloc_msgs *= 2;
137                 BBS->msgs = realloc(BBS->msgs, (BBS->alloc_msgs * sizeof(long)));
138                 memset(&BBS->msgs[BBS->num_msgs], 0, ((BBS->alloc_msgs - BBS->num_msgs) * sizeof(long)));
139         }
140
141         BBS->msgs[BBS->num_msgs++] = Msg->msgnum;
142
143         return 200;
144 }
145
146
147 int bbsview_sortfunc(const void *s1, const void *s2) {
148         long l1;
149         long l2;
150
151         l1 = *(long *) (s1);
152         l2 = *(long *) (s2);
153
154         if (l1 > l2)
155                 return (+1);
156         if (l1 < l2)
157                 return (-1);
158         return (0);
159 }
160
161
162 int bbsview_RenderView_or_Tail(SharedMessageStatus * Stat, void **ViewSpecific, long oper) {
163         struct bbsview *BBS = (struct bbsview *) *ViewSpecific;
164         int i;
165         int seq;
166         const StrBuf *Mime;
167         int start_index = 0;
168         int end_index = 0;
169         int go_to_the_very_end = 0;
170
171         if (Stat->nummsgs > 0) {
172                 syslog(LOG_DEBUG, "sorting %d messages\n", BBS->num_msgs);
173                 qsort(BBS->msgs, (size_t) (BBS->num_msgs), sizeof(long), bbsview_sortfunc);
174         }
175
176         if ((BBS->num_msgs % Stat->maxmsgs) == 0) {
177                 BBS->num_pages = BBS->num_msgs / Stat->maxmsgs;
178         }
179         else {
180                 BBS->num_pages = (BBS->num_msgs / Stat->maxmsgs) + 1;
181         }
182
183         /* If the requested page number is -4,
184          * it means "whichever page on which msg#xxxxx starts"
185          * Change to the page number which contains that message.
186          */
187         if (BBS->requested_page == (-4)) {
188                 if (BBS->num_msgs == 0) {
189                         BBS->requested_page = 0;
190                 }
191                 else {
192                         for (i = 0; i < BBS->num_msgs; ++i) {
193                                 if ((BBS->msgs[i] >= BBS->start_reading_at)
194                                     && (BBS->requested_page == (-4))
195                                     ) {
196                                         BBS->requested_page = (i / Stat->maxmsgs);
197                                 }
198                         }
199                 }
200         }
201
202         /* If the requested page number is -3,
203          * it means "whichever page on which new messages start"
204          * Change that to an actual page number now.
205          */
206         if (BBS->requested_page == (-3)) {
207                 if (BBS->num_msgs == 0) {
208                         /*
209                          * The room is empty; just start at the top and leave it there.
210                          */
211                         BBS->requested_page = 0;
212                 }
213                 else if ((BBS->num_msgs > 0)
214                          && (BBS->lastseen <= BBS->msgs[0])
215                     ) {
216                         /*
217                          * All messages are new; this is probably the user's first visit to the room,
218                          * so start at the last page instead of showing ancient history.
219                          */
220                         BBS->requested_page = BBS->num_pages - 1;
221                         go_to_the_very_end = 1;
222                 }
223                 else {
224                         /*
225                          * Some messages are old and some are new.  Go to the start of new messages.
226                          */
227                         for (i = 0; i < BBS->num_msgs; ++i) {
228                                 if ((BBS->msgs[i] > BBS->lastseen)
229                                     && ((i == 0) || (BBS->msgs[i - 1] <= BBS->lastseen))
230                                     ) {
231                                         BBS->requested_page = (i / Stat->maxmsgs);
232                                 }
233                         }
234                 }
235         }
236
237         /* Still set to -3 ?  If so, that probably means that there are no new messages,
238          * so we'll go to the *end* of the final page.
239          */
240         if (BBS->requested_page == (-3)) {
241                 if (BBS->num_msgs == 0) {
242                         BBS->requested_page = 0;
243                 }
244                 else {
245                         BBS->requested_page = BBS->num_pages - 1;
246                 }
247         }
248
249         /* keep the requested page within bounds */
250         if (BBS->requested_page < 0)
251                 BBS->requested_page = 0;
252         if (BBS->requested_page >= BBS->num_pages)
253                 BBS->requested_page = BBS->num_pages - 1;
254
255         start_index = BBS->requested_page * Stat->maxmsgs;
256         if (start_index < 0)
257                 start_index = 0;
258         end_index = start_index + Stat->maxmsgs - 1;
259
260         for (seq = 0; seq < 3; ++seq) { /* cheap & sleazy way of rendering the page numbers twice */
261
262                 if ((seq == 1) && (Stat->nummsgs > 0)) {
263                         /* display the selected range of messages */
264
265                         for (i = start_index; (i <= end_index && i < BBS->num_msgs); ++i) {
266                                 if ((BBS->msgs[i] > BBS->lastseen)
267                                     && ((i == 0) || (BBS->msgs[i - 1] <= BBS->lastseen))
268                                     ) {
269                                         /* new messages start here */
270                                         do_template("start_of_new_msgs");
271                                         if (!go_to_the_very_end) {
272                                                 StrBufAppendPrintf(WC->trailing_javascript, "location.href=\"#newmsgs\";\n");
273                                         }
274                                 }
275                                 if (BBS->msgs[i] > 0L) {
276                                         read_message(WC->WBuf, HKEY("view_message"), BBS->msgs[i], NULL, &Mime, NULL);
277                                 }
278                                 if ((i == (BBS->num_msgs - 1))
279                                     && (BBS->msgs[i] <= BBS->lastseen)
280                                     ) {
281                                         /* no new messages */
282                                         do_template("no_new_msgs");
283                                         if (!go_to_the_very_end) {
284                                                 StrBufAppendPrintf(WC->trailing_javascript, "location.href=\"#nonewmsgs\";\n");
285                                         }
286                                 }
287                         }
288                 }
289
290                 else if ((seq == 0) || (seq == 2)) {
291                         int first;
292                         int last;
293                         /* Display the selecto-bar with the page numbers */
294
295                         wc_printf("<div class=\"moreprompt\">");
296                         if (seq == 2) {
297                                 wc_printf("<a name=\"end_of_msgs\">");
298                         }
299                         wc_printf(_("Go to page: "));
300                         if (seq == 2) {
301                                 wc_printf("</a>");
302                         }
303
304                         first = 0;
305                         last = BBS->num_pages - 1;
306
307                         for (i = 0; i <= last; ++i) {
308
309                                 if ((i == first)
310                                     || (i == last)
311                                     || (i == BBS->requested_page)
312                                     || (((BBS->requested_page - i) < RANGE)
313                                         && ((BBS->requested_page - i) > (0 - RANGE))
314                                     )
315                                     ) {
316
317                                         if ((i == last)
318                                             && (last - BBS->requested_page > RANGE)
319                                             ) {
320                                                 wc_printf("...&nbsp;");
321                                         }
322                                         if (i == BBS->requested_page) {
323                                                 wc_printf("[");
324                                         }
325                                         else {
326                                                 wc_printf("<a href=\"readfwd?go=");
327                                                 urlescputs(ChrPtr(WC->CurRoom.name));
328                                                 wc_printf("?start_reading_at=%ld\">", BBS->msgs[i * Stat->maxmsgs]
329                                                     );
330                                                 /* wc_printf("?page=%d\">", i); */
331                                                 wc_printf("<span class=\"moreprompt_link\">");
332                                         }
333                                         if ((i == first)
334                                             && (BBS->requested_page > (RANGE + 1))
335                                             ) {
336                                                 wc_printf(_("First"));
337                                         }
338                                         else if ((i == last)
339                                                  && (last - BBS->requested_page > RANGE)
340                                             ) {
341                                                 wc_printf(_("Last"));
342                                         }
343                                         else {
344                                                 wc_printf("%d", i + 1); /* change to one-based for display */
345                                         }
346                                         if (i == BBS->requested_page) {
347                                                 wc_printf("]");
348                                         }
349                                         else {
350                                                 wc_printf("</span>");
351                                                 wc_printf("</a>");
352                                         }
353                                         if ((i == first)
354                                             && (BBS->requested_page > (RANGE + 1))
355                                             ) {
356                                                 wc_printf("&nbsp;...");
357                                         }
358                                         if (i != last) {
359                                                 wc_printf("&nbsp;");
360                                         }
361                                 }
362                         }
363                         wc_printf("</div>\n");
364                 }
365         }
366
367         if (go_to_the_very_end) {
368                 StrBufAppendPrintf(WC->trailing_javascript, "location.href=\"#end_of_msgs\";\n");
369         }
370         return (0);
371 }
372
373
374 int bbsview_Cleanup(void **ViewSpecific) {
375         struct bbsview *BBS = (struct bbsview *) *ViewSpecific;
376
377         if (BBS->alloc_msgs != 0) {
378                 free(BBS->msgs);
379         }
380         free(BBS);
381
382         wDumpContent(1);
383         return 0;
384 }
385
386
387 void InitModule_BBSVIEWRENDERERS(void) {
388         RegisterReadLoopHandlerset(VIEW_BBS,
389                                    bbsview_GetParamsGetServerCall,
390                                    NULL, NULL, NULL, bbsview_LoadMsgFromServer, bbsview_RenderView_or_Tail, bbsview_Cleanup, NULL);
391 }