* make server_getln return the number of bytes it read
[citadel.git] / webcit / who.c
1 /*
2  * $Id$
3  */
4 /**
5  * \defgroup DislpayWho Display a list of all users currently logged on to the Citadel server.
6  * \ingroup WebcitDisplayItems
7  */
8 /*@{*/
9 #include "webcit.h"
10
11
12 typedef struct UserStateStruct {
13         char *UserName;
14         long UserNameLen;
15         char *Room;
16         long RoomLen;
17         char *Host;
18         long HostLen;
19         char *RealRoom;
20         long RealRoomLen;
21         char *RealHost;
22         long RealHostLen;
23         long LastActive;
24         int Session;
25         int Idle;
26         int SessionCount;
27 } UserStateStruct;
28
29 void DestroyUserStruct(void *vUser)
30 {
31         UserStateStruct *User = (UserStateStruct*) vUser;
32         free(User->UserName);
33         free(User->Room);
34         free(User->Host);
35         free(User->RealRoom);
36         free(User->RealHost);
37         free(User);
38 }
39
40 int CompareUserStruct(const void *VUser1, const void *VUser2)
41 {
42         const UserStateStruct *User1 = (UserStateStruct*) GetSearchPayload(VUser1);
43         const UserStateStruct *User2 = (UserStateStruct*) GetSearchPayload(VUser2);
44         
45         if (User1->Idle != User2->Idle)
46                 return User1->Idle > User2->Idle;
47         return strcasecmp(User1->UserName, User2->UserName);
48 }
49
50
51 int GetWholistSection(HashList *List, time_t now)
52 {
53         UserStateStruct *User, *OldUser;
54         char buf[SIZ], user[SIZ], room[SIZ], host[SIZ],
55                 realroom[SIZ], realhost[SIZ];
56         size_t BufLen;
57
58         serv_puts("RWHO");
59         serv_getln(buf, sizeof buf);
60         if (buf[0] == '1') {
61                 while (BufLen = serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
62                         User = (UserStateStruct*) malloc(sizeof(UserStateStruct));
63                         User->Session = extract_int(buf, 0);
64
65                         User->UserNameLen = extract_token(user, buf, 1, '|', sizeof user);
66                         User->UserName = malloc(User->UserNameLen + 1);
67                         memcpy(User->UserName, user, User->UserNameLen + 1);
68
69                         User->RoomLen = extract_token(room, buf, 2, '|', sizeof room);
70                         User->Room = malloc(User->RoomLen + 1);
71                         memcpy(User->Room, room, User->RoomLen + 1);
72
73                         User->HostLen = extract_token(host, buf, 3, '|', sizeof host);
74                         User->Host = malloc(User->HostLen + 1);
75                         memcpy(User->Host, host, User->HostLen + 1);
76
77                         User->RealRoomLen = extract_token(realroom, buf, 9, '|', sizeof realroom);
78                         User->RealRoom = malloc(User->RealRoomLen + 1);
79                         memcpy(User->RealRoom, realroom, User->RealRoomLen + 1);
80
81                         User->RealHostLen = extract_token(realhost, buf, 10, '|', sizeof realhost);
82                         User->RealHost = malloc(User->RealHostLen + 1);
83                         memcpy(User->RealHost, realhost, User->RealHostLen + 1);
84                         
85                         User->LastActive = extract_long(buf, 5);
86                         User->Idle = (now - User->LastActive) > 900L;
87                         User->SessionCount = 1;
88
89                         if (GetHash(List, User->UserName, User->UserNameLen, (void**)&OldUser)) {
90                                 OldUser->SessionCount++;
91                                 if (!User->Idle) {
92                                         OldUser->Idle = User->Idle;
93                                         OldUser->LastActive = User->LastActive;
94                                 }
95                                 DestroyUserStruct(User);
96                         }
97                         else
98                                 Put(List, User->UserName, User->UserNameLen, User, DestroyUserStruct);
99                 }
100                 SortByPayload(List, CompareUserStruct);
101                 return 1;
102         }
103         else
104                 return 0;
105 }
106
107 /**
108  * \brief Display inner div of Wholist
109  */
110 void who_inner_div(void) {
111         UserStateStruct *User;
112         char buf[SIZ];
113         struct wcsession *WCC = WC;     /* This is done to make it run faster; WC is a function */
114         HashList *List;
115         HashPos  *it;
116         char *UserName;
117         long len;
118         int sess;
119         time_t now;
120         int bg = 0;
121
122         wprintf("<table class=\"altern\">"
123                 "<tr>\n");
124         wprintf("<th class=\"edit_col\"> </th>\n");
125         wprintf("<th colspan=\"2\"> </th>\n");
126         wprintf("<th>%s</th>\n", _("User name"));
127         wprintf("<th>%s</th>", _("Room"));
128         wprintf("<th class=\"host_col\">%s</th>\n</tr>\n", _("From host"));
129
130         serv_puts("TIME");
131         serv_getln(buf, sizeof buf);
132
133         if (buf[0] == '2') {
134                 now = extract_long(&buf[4], 0);
135         }
136         else {
137                 now = time(NULL);
138         }
139
140         List = NewHash();
141
142         if (GetWholistSection(List, now)) {
143                 it = GetNewHashPos();
144                 while (GetNextHashPos(List, it, &len, &UserName, (void**)&User)) {
145
146                         bg = 1 - bg;
147                         wprintf("<tr class=\"%s\">",
148                                 (bg ? "even" : "odd")
149                         );
150
151
152                         wprintf("<td class=\"edit_col\">");
153                         if ((WCC->is_aide) &&
154                             (sess != WCC->ctdl_pid)) {
155                                 wprintf(" <a href=\"terminate_session?which_session=%d", sess);
156                                 wprintf("\" onClick=\"return ConfirmKill();\">%s</a>", _("(kill)"));
157                         }
158                         if (sess == WCC->ctdl_pid) {
159                                 wprintf(" <a href=\"edit_me\">%s</a>", _("(edit)"));
160                         }
161                         wprintf("</td>");
162
163                         /** (link to page this user) */
164                         wprintf("<td width=\"5%\"><a href=\"display_page?recp=");
165                         urlescputs(User->UserName);
166                         wprintf("\">"
167                                 "<img align=\"middle\" "
168                                 "src=\"static/citadelchat_24x.gif\" "
169                                 "alt=\"(p)\""
170                                 " border=\"0\" /></a> ");
171                         wprintf("</td>");
172
173                         /** (idle flag) */
174                         wprintf("<td width=\"5%\">");
175                         if (User->Idle) {
176                                 wprintf(" "
177                                         "<img align=\"middle\" "
178                                         "src=\"static/inactiveuser_24x.gif\" "
179                                         "alt=\"(%s %d %s)\" border=\"0\" />",
180                                         _("idle since"),
181                                         (now - User->LastActive) / 60,
182                                         _("Minutes")
183                                         );
184                                 
185                         }
186                         else {
187                                 wprintf(" "
188                                         "<img align=\"middle\" "
189                                         "src=\"static/activeuser_24x.gif\" "
190                                         "alt=\"(active)\" border=\"0\" />");
191                         }
192                         wprintf("</td>\n<td>");
193
194                         /** username (link to user bio/photo page) */
195                         wprintf("<a href=\"showuser?who=");
196                         urlescputs(User->UserName);
197                         wprintf("\">");
198                         escputs(User->UserName);
199                         if (User->SessionCount > 1)
200                                 wprintf(" [%d] ", User->SessionCount);
201                         wprintf("</a>");
202
203                         /** room */
204                         wprintf("</td>\n\t<td>");
205                         escputs(User->Room);
206                         if (!IsEmptyStr(User->RealRoom) ) {
207                                 wprintf("<br /><i>");
208                                 escputs(User->RealRoom);
209                                 wprintf("</i>");
210                         }
211                         wprintf("</td>\n\t<td class=\"host_col\">");
212
213                         /** hostname */
214                         escputs(User->Host);
215                         if (!IsEmptyStr(User->RealHost)) {
216                                 wprintf("<br /><i>");
217                                 escputs(User->RealHost);
218                                 wprintf("</i>");
219                         }
220                         wprintf("</td>\n</tr>");
221                 }
222                 DeleteHashPos(&it);
223         }
224         wprintf("</table>");
225         DeleteHash(&List);
226
227 }
228
229
230 /**
231  * \brief who is on?
232  */
233 void who(void)
234 {
235         char title[256];
236
237         output_headers(1, 1, 2, 0, 0, 0);
238
239         wprintf("<script type=\"text/javascript\">\n"
240                 "function ConfirmKill() { \n"
241                 "return confirm('%s');\n"
242                 "}\n"
243                 "</script>\n", _("Do you really want to kill this session?")
244         );
245
246         wprintf("<div id=\"banner\">\n");
247         wprintf("<div class=\"room_banner\">");
248         wprintf("<img src=\"static/usermanag_48x.gif\">");
249         wprintf("<h1>");
250         snprintf(title, sizeof title, _("Users currently on %s"), serv_info.serv_humannode);
251         escputs(title);
252         wprintf("</h1></div>");
253         wprintf("<ul class=\"room_actions\">\n");
254         wprintf("<li class=\"start_page\">");
255         offer_start_page();
256         wprintf("</li></ul>");
257         wprintf("</div>");
258
259         wprintf("<div id=\"content\" class=\"fix_scrollbar_bug who_is_online\">\n");
260         wprintf("<div class=\"box\">");
261         wprintf("<div class=\"boxlabel\">");    
262         snprintf(title, sizeof title, _("Users currently on %s"), serv_info.serv_humannode);
263         escputs(title);
264         wprintf("</div>");      
265         wprintf("<div class=\"boxcontent\">");
266         wprintf("<div id=\"who_inner\" >");
267         who_inner_div();
268         wprintf("</div>");
269
270         wprintf("<div class=\"instructions\">");
271         wprintf(_("Click on a name to read user info.  Click on %s "
272                 "to send an instant message to that user."),
273                 "<img align=\"middle\" src=\"static/citadelchat_16x.gif\" alt=\"(p)\" border=\"0\">"
274         );
275         wprintf("</div></div>\n");
276
277         /**
278          * JavaScript to make the ajax refresh happen:
279          * See http://www.sergiopereira.com/articles/prototype.js.html for info on Ajax.PeriodicalUpdater
280          * It wants: 1. The div being updated
281          *           2. The URL of the update source
282          *           3. Other flags (such as the HTTP method and the refresh frequency)
283          */
284         wprintf(
285                 "<script type=\"text/javascript\">                                      "
286                 " new Ajax.PeriodicalUpdater('who_inner', 'who_inner_html',     "
287                 "                            { method: 'get', frequency: 30 }  );       "
288                 "</script>                                                              \n"
289         );
290         wDumpContent(1);
291 }
292
293 /**
294  * \brief end session \todo what??? does this belong here? 
295  */
296 void terminate_session(void)
297 {
298         char buf[SIZ];
299
300         serv_printf("TERM %s", bstr("which_session"));
301         serv_getln(buf, sizeof buf);
302         who();
303 }
304
305
306 /**
307  * \brief Change your session info (fake roomname and hostname)
308  */
309 void edit_me(void)
310 {
311         char buf[SIZ];
312
313         if (!IsEmptyStr(bstr("change_room_name_button"))) {
314                 serv_printf("RCHG %s", bstr("fake_roomname"));
315                 serv_getln(buf, sizeof buf);
316                 http_redirect("who");
317         } else if (!IsEmptyStr(bstr("change_host_name_button"))) {
318                 serv_printf("HCHG %s", bstr("fake_hostname"));
319                 serv_getln(buf, sizeof buf);
320                 http_redirect("who");
321         } else if (!IsEmptyStr(bstr("change_user_name_button"))) {
322                 serv_printf("UCHG %s", bstr("fake_username"));
323                 serv_getln(buf, sizeof buf);
324                 http_redirect("who");
325         } else if (!IsEmptyStr(bstr("cancel_button"))) {
326                 http_redirect("who");
327         } else {
328                 output_headers(1, 1, 0, 0, 0, 0);
329
330                 wprintf("<div id=\"banner\">\n");
331                 wprintf("<table class=\"who_banner\"><tr><td>");
332                 wprintf("<span class=\"titlebar\">");
333                 wprintf(_("Edit your session display"));
334                 wprintf("</span></td></tr></table>\n");
335                 wprintf("</div>\n<div id=\"content\">\n");
336
337                 wprintf(_("This screen allows you to change the way your "
338                         "session appears in the 'Who is online' listing. "
339                         "To turn off any 'fake' name you've previously "
340                         "set, simply click the appropriate 'change' button "
341                         "without typing anything in the corresponding box. "));
342                 wprintf("<br />\n");
343
344                 wprintf("<form method=\"POST\" action=\"edit_me\">\n");
345                 wprintf("<input type=\"hidden\" name=\"nonce\" value=\"%ld\">\n", WC->nonce);
346
347                 wprintf("<table border=0 width=100%%>\n");
348
349                 wprintf("<tr><td><b>");
350                 wprintf(_("Room name:"));
351                 wprintf("</b></td>\n<td>");
352                 wprintf("<input type=\"text\" name=\"fake_roomname\" maxlength=\"64\">\n");
353                 wprintf("</td>\n<td align=center>");
354                 wprintf("<input type=\"submit\" name=\"change_room_name_button\" value=\"%s\">",
355                         _("Change room name"));
356                 wprintf("</td>\n</tr>\n");
357
358                 wprintf("<tr><td><b>");
359                 wprintf(_("Host name:"));
360                 wprintf("</b></td><td>");
361                 wprintf("<input type=\"text\" name=\"fake_hostname\" maxlength=\"64\">\n");
362                 wprintf("</td>\n<td align=center>");
363                 wprintf("<input type=\"submit\" name=\"change_host_name_button\" value=\"%s\">",
364                         _("Change host name"));
365                 wprintf("</td>\n</tr>\n");
366
367                 if (WC->is_aide) {
368                         wprintf("<tr><td><b>");
369                         wprintf(_("User name:"));
370                         wprintf("</b></td><td>");
371                         wprintf("<input type=\"text\" name=\"fake_username\" maxlength=\"64\">\n");
372                         wprintf("</td>\n<td align=center>");
373                         wprintf("<input type=\"submit\" name \"change_user_name_button\" value=\"%s\">",
374                                 _("Change user name"));
375                         wprintf("</td>\n</tr>\n");
376                 }
377                 wprintf("<tr><td> </td><td> </td><td align=center>");
378                 wprintf("<input type=\"submit\" name=\"cancel_button\" value=\"%s\">",
379                         _("Cancel"));
380                 wprintf("</td></tr></table>\n");
381                 wprintf("</form></center>\n");
382                 wDumpContent(1);
383         }
384 }
385
386 /**
387  * \brief Wholist section
388  */
389 void wholist_section(void) {
390         UserStateStruct *User;
391         HashList *List;
392         HashPos  *it;
393         char *UserName;
394         long len;
395         char buf[SIZ];
396         time_t now;
397
398         serv_puts("TIME");
399         serv_getln(buf, sizeof buf);
400         if (buf[0] == '2') {
401                 now = extract_long(&buf[4], 0);
402         }
403         else {
404                 now = time(NULL);
405         }
406
407         List = NewHash();
408
409         if (GetWholistSection(List, now)) {
410                 SortByPayload(List, CompareUserStruct);
411                 it = GetNewHashPos();
412                 while (GetNextHashPos(List, it, &len, &UserName, (void**)&User)) {
413                         if (strcmp(User->UserName, NLI)) {
414                                 wprintf("<li class=\"");
415                                 if (User->Idle) {
416                                         wprintf("inactiveuser");
417                                 }
418                                 else {
419                                         wprintf("activeuser");
420                                 }
421                                 wprintf("\"><a href=\"showuser?who=");
422                                 urlescputs(User->UserName);
423                                 wprintf("\">");
424                                 escputs(User->UserName);
425                                 wprintf("</a></li>");
426                         }
427                 }
428                 DeleteHashPos(&it);
429         }
430         DeleteHash(&List);
431 }
432
433
434
435 /*@}*/