Do not attempt to search for database records of length 0.
[citadel.git] / citadel / euidindex.c
1 /* 
2  * Index messages by EUID per room.
3  *
4  * Copyright (c) 1987-2020 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_BLOG:         return(1);
52         }
53         
54         return(0);
55 }
56
57
58 /*
59  * Locate a message in a given room with a given euid, and return
60  * its message number.
61  */
62 long locate_message_by_euid(char *euid, struct ctdlroom *qrbuf) {
63         return CtdlLocateMessageByEuid (euid, qrbuf);
64 }
65
66
67 long CtdlLocateMessageByEuid(char *euid, struct ctdlroom *qrbuf) {
68         char *key;
69         int key_len;
70         struct cdbdata *cdb_euid;
71         long msgnum = (-1L);
72
73         syslog(LOG_DEBUG, "euidindex: searching for EUID <%s> in <%s>", euid, qrbuf->QRname);
74
75         key_len = strlen(euid) + sizeof(long) + 1;
76         key = malloc(key_len);
77         memcpy(key, &qrbuf->QRnumber, sizeof(long));
78         strcpy(&key[sizeof(long)], euid);
79
80         cdb_euid = cdb_fetch(CDB_EUIDINDEX, key, key_len);
81         free(key);
82
83         if (cdb_euid == NULL) {
84                 msgnum = (-1L);
85         }
86         else {
87                 /* The first (sizeof long) of the record is what we're
88                  * looking for.  Throw away the rest.
89                  */
90                 memcpy(&msgnum, cdb_euid->ptr, sizeof(long));
91                 cdb_free(cdb_euid);
92         }
93         syslog(LOG_DEBUG, "euidindex: returning msgnum = %ld", msgnum);
94         return(msgnum);
95 }
96
97
98 /*
99  * Store the euid index for a message, which has presumably just been
100  * stored in this room by the caller.
101  */
102 void index_message_by_euid(char *euid, struct ctdlroom *qrbuf, long msgnum) {
103         char *key;
104         int key_len;
105         char *data;
106         int data_len;
107
108         syslog(LOG_DEBUG, "euidindex: indexing message #%ld <%s> in <%s>", msgnum, euid, qrbuf->QRname);
109
110         key_len = strlen(euid) + sizeof(long) + 1;
111         key = malloc(key_len);
112         memcpy(key, &qrbuf->QRnumber, sizeof(long));
113         strcpy(&key[sizeof(long)], euid);
114
115         data_len = sizeof(long) + key_len;
116         data = malloc(data_len);
117
118         memcpy(data, &msgnum, sizeof(long));
119         memcpy(&data[sizeof(long)], key, key_len);
120
121         cdb_store(CDB_EUIDINDEX, key, key_len, data, data_len);
122         free(key);
123         free(data);
124 }
125
126
127 /*
128  * Called by rebuild_euid_index_for_room() to index one message.
129  */
130 void rebuild_euid_index_for_msg(long msgnum, void *userdata) {
131         struct CtdlMessage *msg = NULL;
132
133         msg = CtdlFetchMessage(msgnum, 0);
134         if (msg == NULL) return;
135         if (!CM_IsEmpty(msg, eExclusiveID)) {
136                 index_message_by_euid(msg->cm_fields[eExclusiveID], &CC->room, msgnum);
137         }
138         CM_Free(msg);
139 }
140
141
142 void rebuild_euid_index_for_room(struct ctdlroom *qrbuf, void *data) {
143         static struct RoomProcList *rplist = NULL;
144         struct RoomProcList *ptr;
145         struct ctdlroom qr;
146
147         /* Lazy programming here.  Call this function as a CtdlForEachRoom backend
148          * in order to queue up the room names, or call it with a null room
149          * to make it do the processing.
150          */
151         if (qrbuf != NULL) {
152                 ptr = (struct RoomProcList *)
153                         malloc(sizeof (struct RoomProcList));
154                 if (ptr == NULL) return;
155
156                 safestrncpy(ptr->name, qrbuf->QRname, sizeof ptr->name);
157                 ptr->next = rplist;
158                 rplist = ptr;
159                 return;
160         }
161
162         while (rplist != NULL) {
163                 if (CtdlGetRoom(&qr, rplist->name) == 0) {
164                         if (DoesThisRoomNeedEuidIndexing(&qr)) {
165                                 syslog(LOG_DEBUG,
166                                         "euidindex: rebuilding EUID index for <%s>",
167                                         rplist->name);
168                                 CtdlUserGoto(rplist->name, 0, 0, NULL, NULL, NULL, NULL);
169                                 CtdlForEachMessage(MSGS_ALL, 0L, NULL, NULL, NULL, rebuild_euid_index_for_msg, NULL);
170                         }
171                 }
172                 ptr = rplist;
173                 rplist = rplist->next;
174                 free(ptr);
175         }
176 }
177
178
179 /*
180  * Globally rebuild the EUID indices in every room.
181  */
182 void rebuild_euid_index(void) {
183         cdb_trunc(CDB_EUIDINDEX);               /* delete the old indices */
184         CtdlForEachRoom(rebuild_euid_index_for_room, NULL);     /* enumerate rm names */
185         rebuild_euid_index_for_room(NULL, NULL);        /* and index them */
186 }
187
188
189 /*
190  * Server command to fetch a message number given an euid.
191  */
192 void cmd_euid(char *cmdbuf) {
193         char euid[256];
194         long msgnum;
195         struct cdbdata *cdbfr;
196         long *msglist = NULL;
197         int num_msgs = 0;
198         int i;
199
200         if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
201
202         extract_token(euid, cmdbuf, 0, '|', sizeof euid);
203         msgnum = CtdlLocateMessageByEuid(euid, &CC->room);
204         if (msgnum <= 0L) {
205                 cprintf("%d not found\n", ERROR + MESSAGE_NOT_FOUND);
206                 return;
207         }
208
209         cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->room.QRnumber, sizeof(long));
210         if (cdbfr != NULL) {
211                 num_msgs = cdbfr->len / sizeof(long);
212                 msglist = (long *) cdbfr->ptr;
213                 for (i = 0; i < num_msgs; ++i) {
214                         if (msglist[i] == msgnum) {
215                                 cdb_free(cdbfr);
216                                 cprintf("%d %ld\n", CIT_OK, msgnum);
217                                 return;
218                         }
219                 }
220                 cdb_free(cdbfr);
221         }
222
223         cprintf("%d not found\n", ERROR + MESSAGE_NOT_FOUND);
224 }
225
226
227 CTDL_MODULE_INIT(euidindex)
228 {
229         if (!threading) {
230                 CtdlRegisterProtoHook(cmd_euid, "EUID", "Perform operations on Extended IDs for messages");
231         }
232         /* return our Subversion id for the Log */
233         return "euidindex";
234 }