]> code.citadel.org Git - citadel.git/blob - citadel/serv_expire.c
room expires
[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 struct PurgeList {
47         struct PurgeList *next;
48         char name[ROOMNAMELEN]; /* use the larger of username or roomname */
49         };
50
51 struct PurgeList *UserPurgeList = NULL;
52 struct PurgeList *RoomPurgeList = NULL;
53
54 extern struct CitContext *ContextList;
55
56 #define MODULE_NAME     "Expire old messages, users, rooms"
57 #define MODULE_AUTHOR   "Art Cancro"
58 #define MODULE_EMAIL    "ajc@uncnsrd.mt-kisco.ny.us"
59 #define MAJOR_VERSION   0
60 #define MINOR_VERSION   1
61
62 static struct DLModule_Info info = {
63         MODULE_NAME,
64         MODULE_AUTHOR,
65         MODULE_EMAIL,
66         MAJOR_VERSION,
67         MINOR_VERSION
68         };
69
70 void DoPurgeMessages(struct quickroom *qrbuf) {
71         struct ExpirePolicy epbuf;
72         long delnum;
73         time_t xtime, now;
74         char msgid[64];
75         int a;
76
77         time(&now);
78         GetExpirePolicy(&epbuf, qrbuf);
79         
80         /*
81         lprintf(9, "ExpirePolicy for <%s> is <%d> <%d>\n",
82                 qrbuf->QRname, epbuf.expire_mode, epbuf.expire_value);
83         */
84
85         /* If the room is set to never expire messages ... do nothing */
86         if (epbuf.expire_mode == EXPIRE_NEXTLEVEL) return;
87         if (epbuf.expire_mode == EXPIRE_MANUAL) return;
88
89         begin_critical_section(S_QUICKROOM);
90         get_msglist(qrbuf);
91         
92         /* Nothing to do if there aren't any messages */
93         if (CC->num_msgs == 0) {
94                 end_critical_section(S_QUICKROOM);
95                 return;
96                 }
97
98         /* If the room is set to expire by count, do that */
99         if (epbuf.expire_mode == EXPIRE_NUMMSGS) {
100                 while (CC->num_msgs > epbuf.expire_value) {
101                         delnum = MessageFromList(0);
102                         lprintf(5, "Expiring message %ld\n", delnum);
103                         cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
104                         memcpy(&CC->msglist[0], &CC->msglist[1],
105                                 (sizeof(long)*(CC->num_msgs - 1)));
106                         CC->num_msgs = CC->num_msgs - 1;
107                         }
108                 }
109
110         /* If the room is set to expire by age... */
111         if (epbuf.expire_mode == EXPIRE_AGE) {
112                 for (a=0; a<(CC->num_msgs); ++a) {
113                         delnum = MessageFromList(a);
114                         sprintf(msgid, "%ld", delnum);
115                         xtime = output_message(msgid, MT_DATE, 0, 0);
116
117                         if ((xtime > 0L)
118                            && (now - xtime > (time_t)(epbuf.expire_value * 86400L))) {
119                                 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
120                                 SetMessageInList(a, 0L);
121                                 lprintf(5, "Expiring message %ld\n", delnum);
122                                 }
123                         }
124                 }
125         CC->num_msgs = sort_msglist(CC->msglist, CC->num_msgs);
126         put_msglist(qrbuf);
127         end_critical_section(S_QUICKROOM);
128         }
129
130 void PurgeMessages(void) {
131         lprintf(5, "PurgeMessages() called\n");
132         ForEachRoom(DoPurgeMessages);
133         }
134
135
136 void DoPurgeRooms(struct quickroom *qrbuf) {
137         time_t now, age;
138         struct PurgeList *pptr;
139
140         /* Any of these attributes render a room non-purgable */
141         if (qrbuf->QRflags & QR_PERMANENT) return;
142         if (qrbuf->QRflags & QR_DIRECTORY) return;
143         if (qrbuf->QRflags & QR_NETWORK) return;
144         if (qrbuf->QRflags & QR_MAILBOX) return;
145         if (is_noneditable(qrbuf)) return;
146
147         /* Otherwise, check the date of last modification */
148         time(&now);
149         age = now - (qrbuf->QRmtime);
150         lprintf(9, "<%s> is <%ld> seconds old\n", qrbuf->QRname, age);
151         if ( (qrbuf->QRmtime > 0L)
152            && (age > (time_t)(config.c_roompurge * 86400L))) {
153                 
154                 pptr = (struct PurgeList *) malloc(sizeof(struct PurgeList));
155                 pptr->next = RoomPurgeList;
156                 strcpy(pptr->name, qrbuf->QRname);
157                 RoomPurgeList = pptr;
158
159                 }
160         }
161
162
163 int PurgeRooms(void) {
164         struct PurgeList *pptr;
165         int num_rooms_purged = 0;
166         struct quickroom qrbuf;
167
168         lprintf(5, "PurgeRooms() called\n");
169         if (config.c_roompurge > 0) {
170                 ForEachRoom(DoPurgeRooms);
171                 }
172
173         while (RoomPurgeList != NULL) {
174                 if (getroom(&qrbuf, RoomPurgeList->name) == 0) {
175                         delete_room(&qrbuf);
176                         }
177                 pptr = RoomPurgeList->next;
178                 free(RoomPurgeList);
179                 RoomPurgeList = pptr;
180                 ++num_rooms_purged;
181                 }
182
183         lprintf(5, "Purged %d rooms.\n", num_rooms_purged);
184         return(num_rooms_purged);
185         }
186
187
188 void do_user_purge(struct usersupp *us) {
189         int purge;
190         time_t now;
191         time_t purge_time;
192         struct PurgeList *pptr;
193
194         /* Set purge time; if the user overrides the system default, use it */
195         if (us->USuserpurge > 0) {
196                 purge_time = ((time_t)us->USuserpurge) * 86400L;
197                 }
198         else {
199                 purge_time = ((time_t)config.c_userpurge) * 86400L;
200                 }
201
202         /* The default rule is to not purge. */
203         purge = 0;
204
205         /* If the user hasn't called in two months, his/her account
206          * has expired, so purge the record.
207          */
208         now = time(NULL);
209         if ((now - us->lastcall) > purge_time) purge = 1;
210
211         /* If the user set his/her password to 'deleteme', he/she
212          * wishes to be deleted, so purge the record.
213          */
214         if (!strcasecmp(us->password, "deleteme")) purge = 1;
215
216         /* If the record is marked as permanent, don't purge it.
217          */
218         if (us->flags & US_PERM) purge = 0;
219
220         /* If the access level is 0, the record should already have been
221          * deleted, but maybe the user was logged in at the time or something.
222          * Delete the record now.
223          */
224         if (us->axlevel == 0) purge = 1;
225
226         /* 0 calls is impossible.  If there are 0 calls, it must
227          * be a corrupted record, so purge it.
228          */
229         if (us->timescalled == 0) purge = 1;
230
231         if (purge == 1) {
232                 pptr = (struct PurgeList *) malloc(sizeof(struct PurgeList));
233                 pptr->next = UserPurgeList;
234                 strcpy(pptr->name, us->fullname);
235                 UserPurgeList = pptr;
236                 }
237
238         }
239
240
241
242 int PurgeUsers(void) {
243         struct PurgeList *pptr;
244         int num_users_purged = 0;
245
246         lprintf(5, "PurgeUsers() called\n");
247         if (config.c_userpurge > 0) {
248                 ForEachUser(do_user_purge);
249                 }
250
251         while (UserPurgeList != NULL) {
252                 purge_user(UserPurgeList->name);
253                 pptr = UserPurgeList->next;
254                 free(UserPurgeList);
255                 UserPurgeList = pptr;
256                 ++num_users_purged;
257                 }
258
259         lprintf(5, "Purged %d users.\n", num_users_purged);
260         return(num_users_purged);
261         }
262
263
264
265 int PurgeVisits(void) {
266         struct cdbdata *cdbvisit;
267         struct visit vbuf;
268         int purged = 0;
269
270         struct quickroom qr;
271         struct usersupp us;
272
273         cdb_rewind(CDB_VISIT);
274         while(cdbvisit = cdb_next_item(CDB_VISIT), cdbvisit != NULL) {
275                 memset(&vbuf, 0, sizeof(struct visit));
276                 memcpy(&vbuf, cdbvisit->ptr,
277                         ( (cdbvisit->len > sizeof(struct visit)) ?
278                         sizeof(struct visit) : cdbvisit->len) );
279                 cdb_free(cdbvisit);
280
281                 ++purged;
282                 }
283         return(purged);
284         }
285
286
287 void cmd_expi(char *argbuf) {
288         char cmd[256];
289         int retval;
290
291
292         if ((!(CC->logged_in))&&(!(CC->internal_pgm))) {
293                 cprintf("%d Not logged in.\n",ERROR+NOT_LOGGED_IN);
294                 return;
295                 }
296
297         if ((!is_room_aide()) && (!(CC->internal_pgm)) ) {
298                 cprintf("%d Higher access required.\n",
299                         ERROR+HIGHER_ACCESS_REQUIRED);
300                 return;
301                 }
302
303         extract(cmd, argbuf, 0);
304         if (!strcasecmp(cmd, "users")) {
305                 retval = PurgeUsers();
306                 cprintf("%d Purged %d users.\n", OK, retval);
307                 return;
308                 }
309         else if (!strcasecmp(cmd, "messages")) {
310                 PurgeMessages();
311                 cprintf("%d Finished purging messages.\n", OK);
312                 return;
313                 }
314         else if (!strcasecmp(cmd, "rooms")) {
315                 retval = PurgeRooms();
316                 cprintf("%d Purged %d rooms.\n", OK, retval);
317                 return;
318                 }
319         else if (!strcasecmp(cmd, "visits")) {
320                 retval = PurgeVisits();
321                 cprintf("%d There are %d visits...\n", OK, retval);
322                 }
323         else if (!strcasecmp(cmd, "defrag")) {
324                 defrag_databases();
325                 cprintf("%d Defragmented the databases.\n", OK);
326                 }
327         else {
328                 cprintf("%d Invalid command.\n", ERROR+ILLEGAL_VALUE);
329                 return;
330                 }
331         }
332
333
334
335 struct DLModule_Info *Dynamic_Module_Init(void)
336 {
337    CtdlRegisterProtoHook(cmd_expi, "EXPI", "Expire old system objects");
338    return &info;
339 }