3a9d1e9d8a4521b29aef78078cf60c457e215d81
[citadel.git] / citadel / serv_expire.c
1 /*
2  * serv_expire.c
3  *
4  * This module handles the expiry of old messages and the purging of old users.
5  *
6  */
7 /* $Id$ */
8
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <stdio.h>
12 #include <fcntl.h>
13 #include <signal.h>
14 #include <pwd.h>
15 #include <errno.h>
16 #include <sys/types.h>
17 #include <sys/time.h>
18 #include <sys/wait.h>
19 #include <string.h>
20 #include <limits.h>
21 #include <pthread.h>
22 #include "citadel.h"
23 #include "server.h"
24 #include <syslog.h>
25 #include <time.h>
26 #include "sysdep_decls.h"
27 #include "citserver.h"
28 #include "support.h"
29 #include "config.h"
30 #include "dynloader.h"
31 #include "room_ops.h"
32 #include "policy.h"
33 #include "database.h"
34 #include "msgbase.h"
35 #include "user_ops.h"
36 #include "control.h"
37
38
39 struct oldvisit {
40         char v_roomname[ROOMNAMELEN];
41         long v_generation;
42         long v_lastseen;
43         unsigned int v_flags;
44         };
45
46
47
48
49 struct PurgedUser {
50         struct PurgedUser *next;
51         char name[26];
52         };
53
54 struct PurgedUser *plist = NULL;
55
56 extern struct CitContext *ContextList;
57
58 #define MODULE_NAME     "Expire old messages, users, rooms"
59 #define MODULE_AUTHOR   "Art Cancro"
60 #define MODULE_EMAIL    "ajc@uncnsrd.mt-kisco.ny.us"
61 #define MAJOR_VERSION   0
62 #define MINOR_VERSION   1
63
64 static struct DLModule_Info info = {
65         MODULE_NAME,
66         MODULE_AUTHOR,
67         MODULE_EMAIL,
68         MAJOR_VERSION,
69         MINOR_VERSION
70         };
71
72 void DoPurgeMessages(struct quickroom *qrbuf) {
73         struct ExpirePolicy epbuf;
74         long delnum;
75         time_t xtime, now;
76         char msgid[64];
77         int a;
78
79         time(&now);
80         GetExpirePolicy(&epbuf, qrbuf);
81         
82         /*
83         lprintf(9, "ExpirePolicy for <%s> is <%d> <%d>\n",
84                 qrbuf->QRname, epbuf.expire_mode, epbuf.expire_value);
85         */
86
87         /* If the room is set to never expire messages ... do nothing */
88         if (epbuf.expire_mode == EXPIRE_NEXTLEVEL) return;
89         if (epbuf.expire_mode == EXPIRE_MANUAL) return;
90
91         begin_critical_section(S_QUICKROOM);
92         get_msglist(qrbuf);
93         
94         /* Nothing to do if there aren't any messages */
95         if (CC->num_msgs == 0) {
96                 end_critical_section(S_QUICKROOM);
97                 return;
98                 }
99
100         /* If the room is set to expire by count, do that */
101         if (epbuf.expire_mode == EXPIRE_NUMMSGS) {
102                 while (CC->num_msgs > epbuf.expire_value) {
103                         delnum = MessageFromList(0);
104                         lprintf(5, "Expiring message %ld\n", delnum);
105                         cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
106                         memcpy(&CC->msglist[0], &CC->msglist[1],
107                                 (sizeof(long)*(CC->num_msgs - 1)));
108                         CC->num_msgs = CC->num_msgs - 1;
109                         }
110                 }
111
112         /* If the room is set to expire by age... */
113         if (epbuf.expire_mode == EXPIRE_AGE) {
114                 for (a=0; a<(CC->num_msgs); ++a) {
115                         delnum = MessageFromList(a);
116                         sprintf(msgid, "%ld", delnum);
117                         xtime = output_message(msgid, MT_DATE, 0, 0);
118
119                         if ((xtime > 0L)
120                            && (now - xtime > (time_t)(epbuf.expire_value * 86400L))) {
121                                 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
122                                 SetMessageInList(a, 0L);
123                                 lprintf(5, "Expiring message %ld\n", delnum);
124                                 }
125                         }
126                 }
127         CC->num_msgs = sort_msglist(CC->msglist, CC->num_msgs);
128         put_msglist(qrbuf);
129         end_critical_section(S_QUICKROOM);
130         }
131
132 void PurgeMessages(void) {
133         lprintf(5, "PurgeMessages() called\n");
134         ForEachRoom(DoPurgeMessages);
135         }
136
137
138 void DoPurgeRooms(struct quickroom *qrbuf) {
139         lprintf(9, "%30s (%5ld) %s",
140                 qrbuf->QRname,
141                 qrbuf->QRnumber,
142                 asctime(localtime(&qrbuf->QRmtime)));
143         }
144
145
146 void PurgeRooms(void) {
147         ForEachRoom(DoPurgeRooms);
148         }
149
150
151 void do_user_purge(struct usersupp *us) {
152         int purge;
153         time_t now;
154         time_t purge_time;
155         struct PurgedUser *pptr;
156
157         /* Set purge time; if the user overrides the system default, use it */
158         if (us->USuserpurge > 0) {
159                 purge_time = ((time_t)us->USuserpurge) * 86400L;
160                 }
161         else {
162                 purge_time = ((time_t)config.c_userpurge) * 86400L;
163                 }
164
165         /* The default rule is to not purge. */
166         purge = 0;
167
168         /* If the user hasn't called in two months, his/her account
169          * has expired, so purge the record.
170          */
171         now = time(NULL);
172         if ((now - us->lastcall) > purge_time) purge = 1;
173
174         /* If the user set his/her password to 'deleteme', he/she
175          * wishes to be deleted, so purge the record.
176          */
177         if (!strcasecmp(us->password, "deleteme")) purge = 1;
178
179         /* If the record is marked as permanent, don't purge it.
180          */
181         if (us->flags & US_PERM) purge = 0;
182
183         /* If the access level is 0, the record should already have been
184          * deleted, but maybe the user was logged in at the time or something.
185          * Delete the record now.
186          */
187         if (us->axlevel == 0) purge = 1;
188
189         /* 0 calls is impossible.  If there are 0 calls, it must
190          * be a corrupted record, so purge it.
191          */
192         if (us->timescalled == 0) purge = 1;
193
194         if (purge == 1) {
195                 pptr = (struct PurgedUser *) malloc(sizeof(struct PurgedUser));
196                 pptr->next = plist;
197                 strcpy(pptr->name, us->fullname);
198                 plist = pptr;
199                 }
200
201         }
202
203
204
205 int PurgeUsers(void) {
206         struct PurgedUser *pptr;
207         int num_users_purged = 0;
208
209         lprintf(5, "PurgeUsers() called\n");
210         if (config.c_userpurge > 0) {
211                 ForEachUser(do_user_purge);
212                 }
213
214         while (plist != NULL) {
215                 purge_user(plist->name);
216                 pptr = plist->next;
217                 free(plist);
218                 plist = pptr;
219                 ++num_users_purged;
220                 }
221
222         lprintf(5, "Purged %d users.\n", num_users_purged);
223         return(num_users_purged);
224         }
225
226
227
228 int PurgeVisits(void) {
229         struct cdbdata *cdbvisit;
230         struct visit vbuf;
231         int purged = 0;
232
233         struct quickroom qr;
234         struct usersupp us;
235
236         cdb_rewind(CDB_VISIT);
237         while(cdbvisit = cdb_next_item(CDB_VISIT), cdbvisit != NULL) {
238                 memset(&vbuf, 0, sizeof(struct visit));
239                 memcpy(&vbuf, cdbvisit->ptr,
240                         ( (cdbvisit->len > sizeof(struct visit)) ?
241                         sizeof(struct visit) : cdbvisit->len) );
242                 cdb_free(cdbvisit);
243
244                 ++purged;
245                 }
246         return(purged);
247         }
248
249
250 void cmd_expi(char *argbuf) {
251         char cmd[256];
252         int retval;
253
254
255         if ((!(CC->logged_in))&&(!(CC->internal_pgm))) {
256                 cprintf("%d Not logged in.\n",ERROR+NOT_LOGGED_IN);
257                 return;
258                 }
259
260         if ((!is_room_aide()) && (!(CC->internal_pgm)) ) {
261                 cprintf("%d Higher access required.\n",
262                         ERROR+HIGHER_ACCESS_REQUIRED);
263                 return;
264                 }
265
266         extract(cmd, argbuf, 0);
267         if (!strcasecmp(cmd, "users")) {
268                 retval = PurgeUsers();
269                 cprintf("%d Purged %d users.\n", OK, retval);
270                 return;
271                 }
272         else if (!strcasecmp(cmd, "messages")) {
273                 PurgeMessages();
274                 cprintf("%d Finished purging messages.\n", OK);
275                 return;
276                 }
277         else if (!strcasecmp(cmd, "rooms")) {
278                 PurgeRooms();
279                 cprintf("%d Finished purging rooms.\n", OK);
280                 return;
281                 }
282         else if (!strcasecmp(cmd, "visits")) {
283                 retval = PurgeVisits();
284                 cprintf("%d There are %d visits...\n", OK, retval);
285                 }
286         else if (!strcasecmp(cmd, "defrag")) {
287                 defrag_databases();
288                 cprintf("%d Defragmented the databases.\n", OK);
289                 }
290         else {
291                 cprintf("%d Invalid command.\n", ERROR+ILLEGAL_VALUE);
292                 return;
293                 }
294         }
295
296
297
298 struct DLModule_Info *Dynamic_Module_Init(void)
299 {
300    CtdlRegisterProtoHook(cmd_expi, "EXPI", "Expire old system objects");
301    return &info;
302 }