edff5d7c6147c62a0a957f86ce457b5052a66c7a
[citadel.git] / citadel / euidindex.c
1 /* 
2  * Index messages by EUID per room.
3  *
4  * Copyright (c) 1987-2017 by the citadel.org team
5  *
6  * This program is open source software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License, version 3.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  */
14
15 #include "sysdep.h"
16 #include <stdio.h>
17 #include <libcitadel.h>
18
19 #include "citserver.h"
20 #include "room_ops.h"
21
22 /*
23  * The structure of an euidindex record *key* is:
24  *
25  * |----room_number----|----------EUID-------------|
26  *    (sizeof long)       (actual length of euid)
27  *
28  *
29  * The structure of an euidindex record *value* is:
30  *
31  * |-----msg_number----|----room_number----|----------EUID-------------|
32  *    (sizeof long)       (sizeof long)       (actual length of euid)
33  *
34  */
35
36 /*
37  * Return nonzero if the supplied room is one which should have
38  * an EUID index.
39  */
40 int DoesThisRoomNeedEuidIndexing(struct ctdlroom *qrbuf) {
41
42         switch(qrbuf->QRdefaultview) {
43                 case VIEW_BBS:          return(0);
44                 case VIEW_MAILBOX:      return(0);
45                 case VIEW_ADDRESSBOOK:  return(1);
46                 case VIEW_DRAFTS:       return(0);
47                 case VIEW_CALENDAR:     return(1);
48                 case VIEW_TASKS:        return(1);
49                 case VIEW_NOTES:        return(1);
50                 case VIEW_WIKI:         return(1);
51                 case VIEW_WIKIMD:       return(1);
52                 case VIEW_BLOG:         return(1);
53         }
54         
55         return(0);
56 }
57
58
59 /*
60  * Locate a message in a given room with a given euid, and return
61  * its message number.
62  */
63 long locate_message_by_euid(char *euid, struct ctdlroom *qrbuf) {
64         return CtdlLocateMessageByEuid (euid, qrbuf);
65 }
66
67
68 long CtdlLocateMessageByEuid(char *euid, struct ctdlroom *qrbuf) {
69         char *key;
70         int key_len;
71         struct cdbdata *cdb_euid;
72         long msgnum = (-1L);
73
74         syslog(LOG_DEBUG, "euidindex: searching for EUID <%s> in <%s>", euid, qrbuf->QRname);
75
76         key_len = strlen(euid) + sizeof(long) + 1;
77         key = malloc(key_len);
78         memcpy(key, &qrbuf->QRnumber, sizeof(long));
79         strcpy(&key[sizeof(long)], euid);
80
81         cdb_euid = cdb_fetch(CDB_EUIDINDEX, key, key_len);
82         free(key);
83
84         if (cdb_euid == NULL) {
85                 msgnum = (-1L);
86         }
87         else {
88                 /* The first (sizeof long) of the record is what we're
89                  * looking for.  Throw away the rest.
90                  */
91                 memcpy(&msgnum, cdb_euid->ptr, sizeof(long));
92                 cdb_free(cdb_euid);
93         }
94         syslog(LOG_DEBUG, "euidindex: returning msgnum = %ld", msgnum);
95         return(msgnum);
96 }
97
98
99 /*
100  * Store the euid index for a message, which has presumably just been
101  * stored in this room by the caller.
102  */
103 void index_message_by_euid(char *euid, struct ctdlroom *qrbuf, long msgnum) {
104         char *key;
105         int key_len;
106         char *data;
107         int data_len;
108
109         syslog(LOG_DEBUG, "euidindex: ndexing message #%ld <%s> in <%s>", msgnum, euid, qrbuf->QRname);
110
111         key_len = strlen(euid) + sizeof(long) + 1;
112         key = malloc(key_len);
113         memcpy(key, &qrbuf->QRnumber, sizeof(long));
114         strcpy(&key[sizeof(long)], euid);
115
116         data_len = sizeof(long) + key_len;
117         data = malloc(data_len);
118
119         memcpy(data, &msgnum, sizeof(long));
120         memcpy(&data[sizeof(long)], key, key_len);
121
122         cdb_store(CDB_EUIDINDEX, key, key_len, data, data_len);
123         free(key);
124         free(data);
125 }
126
127
128 /*
129  * Called by rebuild_euid_index_for_room() to index one message.
130  */
131 void rebuild_euid_index_for_msg(long msgnum, void *userdata) {
132         struct CtdlMessage *msg = NULL;
133
134         msg = CtdlFetchMessage(msgnum, 0, 1);
135         if (msg == NULL) return;
136         if (!CM_IsEmpty(msg, eExclusiveID)) {
137                 index_message_by_euid(msg->cm_fields[eExclusiveID], &CC->room, msgnum);
138         }
139         CM_Free(msg);
140 }
141
142
143 void rebuild_euid_index_for_room(struct ctdlroom *qrbuf, void *data) {
144         static struct RoomProcList *rplist = NULL;
145         struct RoomProcList *ptr;
146         struct ctdlroom qr;
147
148         /* Lazy programming here.  Call this function as a CtdlForEachRoom backend
149          * in order to queue up the room names, or call it with a null room
150          * to make it do the processing.
151          */
152         if (qrbuf != NULL) {
153                 ptr = (struct RoomProcList *)
154                         malloc(sizeof (struct RoomProcList));
155                 if (ptr == NULL) return;
156
157                 safestrncpy(ptr->name, qrbuf->QRname, sizeof ptr->name);
158                 ptr->next = rplist;
159                 rplist = ptr;
160                 return;
161         }
162
163         while (rplist != NULL) {
164                 if (CtdlGetRoom(&qr, rplist->name) == 0) {
165                         if (DoesThisRoomNeedEuidIndexing(&qr)) {
166                                 syslog(LOG_DEBUG,
167                                         "euidindex: rebuilding EUID index for <%s>",
168                                         rplist->name);
169                                 CtdlUserGoto(rplist->name, 0, 0, NULL, NULL, NULL, NULL);
170                                 CtdlForEachMessage(MSGS_ALL, 0L, NULL, NULL, NULL,
171                                         rebuild_euid_index_for_msg, NULL);
172                         }
173                 }
174                 ptr = rplist;
175                 rplist = rplist->next;
176                 free(ptr);
177         }
178 }
179
180
181 /*
182  * Globally rebuild the EUID indices in every room.
183  */
184 void rebuild_euid_index(void) {
185         cdb_trunc(CDB_EUIDINDEX);               /* delete the old indices */
186         CtdlForEachRoom(rebuild_euid_index_for_room, NULL);     /* enumerate rm names */
187         rebuild_euid_index_for_room(NULL, NULL);        /* and index them */
188 }
189
190
191 /*
192  * Server command to fetch a message number given an euid.
193  */
194 void cmd_euid(char *cmdbuf) {
195         char euid[256];
196         long msgnum;
197         struct cdbdata *cdbfr;
198         long *msglist = NULL;
199         int num_msgs = 0;
200         int i;
201
202         if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
203
204         extract_token(euid, cmdbuf, 0, '|', sizeof euid);
205         msgnum = CtdlLocateMessageByEuid(euid, &CC->room);
206         if (msgnum <= 0L) {
207                 cprintf("%d not found\n", ERROR + MESSAGE_NOT_FOUND);
208                 return;
209         }
210
211         cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->room.QRnumber, sizeof(long));
212         if (cdbfr != NULL) {
213                 num_msgs = cdbfr->len / sizeof(long);
214                 msglist = (long *) cdbfr->ptr;
215                 for (i = 0; i < num_msgs; ++i) {
216                         if (msglist[i] == msgnum) {
217                                 cdb_free(cdbfr);
218                                 cprintf("%d %ld\n", CIT_OK, msgnum);
219                                 return;
220                         }
221                 }
222                 cdb_free(cdbfr);
223         }
224
225         cprintf("%d not found\n", ERROR + MESSAGE_NOT_FOUND);
226 }
227
228
229 CTDL_MODULE_INIT(euidindex)
230 {
231         if (!threading) {
232                 CtdlRegisterProtoHook(cmd_euid, "EUID", "Perform operations on Extended IDs for messages");
233         }
234         /* return our Subversion id for the Log */
235         return "euidindex";
236 }