citadel
About: Citadel is an advanced messaging and collaboration system for groupware and BBS applications (preferred OS: Linux).
  Fossies Dox: citadel.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

serv_listdeliver.c
Go to the documentation of this file.
1/*
2 * This module delivers messages to mailing lists.
3 *
4 * Copyright (c) 2002-2021 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 <stdlib.h>
17#include <unistd.h>
18#include <stdio.h>
19#include <fcntl.h>
20#include <ctype.h>
21#include <signal.h>
22#include <pwd.h>
23#include <errno.h>
24#include <sys/types.h>
25#include <dirent.h>
26#include <time.h>
27#include <sys/wait.h>
28#include <string.h>
29#include <limits.h>
30#include <libcitadel.h>
31#include "citadel.h"
32#include "server.h"
33#include "citserver.h"
34#include "support.h"
35#include "config.h"
36#include "user_ops.h"
37#include "database.h"
38#include "msgbase.h"
39#include "internet_addressing.h"
40#include "clientsocket.h"
41#include "ctdl_module.h"
42
44
45
46// data passed back and forth between listdeliver_do_msg() and listdeliver_sweep_room()
47struct lddata {
48 long msgnum; // number of most recent message processed
49 char *netconf; // netconfig for this room (contains the recipients)
50};
51
52
53
54void listdeliver_do_msg(long msgnum, void *userdata) {
55 struct lddata *ld = (struct lddata *) userdata;
56 if (!ld) return;
57 char buf[SIZ];
58 char *ch;
59 char bounce_to[256];
60 int i = 0;
61
62 ld->msgnum = msgnum;
63 if (msgnum <= 0) return;
64
65 struct CtdlMessage *TheMessage = CtdlFetchMessage(msgnum, 1);
66 if (!TheMessage) return;
67
68 // If the subject line does not contain the name of the room, add it now.
69 if (!bmstrcasestr(TheMessage->cm_fields[eMsgSubject], CC->room.QRname)) {
70 snprintf(buf, sizeof buf, "[%s] %s", CC->room.QRname, TheMessage->cm_fields[eMsgSubject]);
71 CM_SetField(TheMessage, eMsgSubject, buf, strlen(buf));
72 }
73
74 // Reply-to: should be set so that replies come to the list.
75 snprintf(buf, sizeof buf, "room_%s@%s", CC->room.QRname, CtdlGetConfigStr("c_fqdn"));
76 for (ch=buf; *ch; ++ch) {
77 if (isspace(*ch)) *ch = '_';
78 }
79 CM_SetField(TheMessage, eReplyTo, buf, strlen(buf));
80
81 // With that out of the way, let's figure out who this message needs to be sent to.
82 char *recipients = malloc(strlen(ld->netconf));
83 if (recipients) {
84 recipients[0] = 0;
85
86 int config_lines = num_tokens(ld->netconf, '\n');
87 for (i=0; i<config_lines; ++i) {
88 extract_token(buf, ld->netconf, i, '\n', sizeof buf);
89 if (!strncasecmp(buf, "listrecp|", 9)) {
90 if (recipients[0] != 0) {
91 strcat(recipients, ",");
92 }
93 strcat(recipients, &buf[9]);
94 }
95 if (!strncasecmp(buf, "digestrecp|", 11)) {
96 if (recipients[0] != 0) {
97 strcat(recipients, ",");
98 }
99 strcat(recipients, &buf[11]);
100 }
101 }
102
103 // Where do we want bounces and other noise to be sent? Certainly not to the list members!
104 snprintf(bounce_to, sizeof bounce_to, "room_aide@%s", CtdlGetConfigStr("c_fqdn"));
105
106 // Now submit the message
107 struct recptypes *valid = validate_recipients(recipients, NULL, 0);
108 if (valid) {
109 valid->bounce_to = strdup(bounce_to);
110 valid->envelope_from = strdup(bounce_to);
111 CtdlSubmitMsg(TheMessage, valid, "");
112 free_recipients(valid);
113 }
114 }
115 CM_Free(TheMessage);
116}
117
118
119/*
120 * Sweep through one room looking for mailing list deliveries to do
121 */
122void listdeliver_sweep_room(char *roomname) {
123 char *netconfig = NULL;
124 char *newnetconfig = NULL;
125 long lastsent = 0;
126 char buf[SIZ];
127 int config_lines;
128 int i;
129 int number_of_messages_processed = 0;
130 int number_of_recipients = 0;
131 struct lddata ld;
132
133 if (CtdlGetRoom(&CC->room, roomname)) {
134 syslog(LOG_DEBUG, "listdeliver: no room <%s>", roomname);
135 return;
136 }
137
138 netconfig = LoadRoomNetConfigFile(CC->room.QRnumber);
139 if (!netconfig) {
140 return; // no netconfig, no processing, no problem
141 }
142
143 config_lines = num_tokens(netconfig, '\n');
144 for (i=0; i<config_lines; ++i) {
145 extract_token(buf, netconfig, i, '\n', sizeof buf);
146
147 if (!strncasecmp(buf, "lastsent|", 9)) {
148 lastsent = atol(&buf[9]);
149 }
150 else if ( (!strncasecmp(buf, "listrecp|", 9)) || (!strncasecmp(buf, "digestrecp|", 11)) ) {
151 ++number_of_recipients;
152 }
153 }
154
155 if (number_of_recipients > 0) {
156 syslog(LOG_DEBUG, "listdeliver: processing new messages in <%s> for <%d> recipients", CC->room.QRname, number_of_recipients);
157 ld.netconf = netconfig;
158 number_of_messages_processed = CtdlForEachMessage(MSGS_GT, lastsent, NULL, NULL, NULL, listdeliver_do_msg, &ld);
159 syslog(LOG_INFO, "listdeliver: processed <%d> messages in <%s> for <%d> recipients", number_of_messages_processed, CC->room.QRname, number_of_recipients);
160
161 if (number_of_messages_processed > 0) {
162 syslog(LOG_DEBUG, "listdeliver: new lastsent is %ld", ld.msgnum);
163
164 // Update this room's netconfig with the updated lastsent
166 netconfig = LoadRoomNetConfigFile(CC->room.QRnumber);
167 if (!netconfig) {
168 netconfig = strdup("");
169 }
170
171 // The new netconfig begins with the new lastsent directive
172 newnetconfig = malloc(strlen(netconfig) + 1024);
173 sprintf(newnetconfig, "lastsent|%ld\n", ld.msgnum);
174
175 // And then we append all of the old netconfig, minus the old lastsent. Also omit blank lines.
176 config_lines = num_tokens(netconfig, '\n');
177 for (i=0; i<config_lines; ++i) {
178 extract_token(buf, netconfig, i, '\n', sizeof buf);
179 if ( (!IsEmptyStr(buf)) && (strncasecmp(buf, "lastsent|", 9)) ) {
180 sprintf(&newnetconfig[strlen(newnetconfig)], "%s\n", buf);
181 }
182 }
183
184 // Write the new netconfig back to disk
185 SaveRoomNetConfigFile(CC->room.QRnumber, newnetconfig);
187 free(newnetconfig); // this was the new netconfig, free it because we're done with it
188 }
189 }
190 free(netconfig); // this was the old netconfig, free it even if we didn't do anything
191}
192
193
194/*
195 * Callback for listdeliver_sweep()
196 * Adds one room to the queue
197 */
198void listdeliver_queue_room(struct ctdlroom *qrbuf, void *data) {
199 Array *roomlistarr = (Array *)data;
200 array_append(roomlistarr, qrbuf->QRname);
201}
202
203
204/*
205 * Queue up the list of rooms so we can sweep them for mailing list delivery instructions
206 */
208 static time_t last_run = 0L;
209 int i = 0;
210
211 /*
212 * Run mailing list delivery no more frequently than once every 15 minutes (we should make this configurable)
213 */
214 if ( (time(NULL) - last_run) < 900 ) {
215 return;
216 }
217
218 /*
219 * This is a simple concurrency check to make sure only one listdeliver
220 * run is done at a time. We could do this with a mutex, but since we
221 * don't really require extremely fine granularity here, we'll do it
222 * with a static variable instead.
223 */
224 if (doing_listdeliver) return;
226
227 /*
228 * Go through each room looking for mailing lists to process
229 */
230 syslog(LOG_DEBUG, "listdeliver: sweep started");
231
232 Array *roomlistarr = array_new(ROOMNAMELEN); // we have to queue them
233 CtdlForEachRoom(listdeliver_queue_room, roomlistarr); // otherwise we get multiple cursors in progress
234
235 for (i=0; i<array_len(roomlistarr); ++i) {
236 listdeliver_sweep_room((char *)array_get_element_at(roomlistarr, i));
237 }
238
239 array_free(roomlistarr);
240 syslog(LOG_DEBUG, "listdeliver: ended");
241 last_run = time(NULL);
243}
244
245
246/*
247 * Module entry point
248 */
250{
251 if (!threading) {
253 }
254
255 /* return our module name for the log */
256 return "listsub";
257}
#define ROOMNAMELEN
Definition: citadel.h:56
char * CtdlGetConfigStr(char *key)
Definition: config.c:384
#define CC
Definition: context.h:140
void SaveRoomNetConfigFile(long roomnum, const char *raw_netconfig)
Definition: netconfig.c:50
int CtdlGetRoom(struct ctdlroom *qrbuf, const char *room_name)
Definition: room_ops.c:342
char * LoadRoomNetConfigFile(long roomnum)
Definition: netconfig.c:77
void CtdlRegisterSessionHook(void(*fcn_ptr)(void), int EventType, int Priority)
#define PRIO_AGGR
Definition: ctdl_module.h:73
void CtdlForEachRoom(ForEachRoomCallBack CB, void *in_data)
Definition: room_ops.c:606
#define CTDL_MODULE_INIT(module_name)
Definition: ctdl_module.h:50
struct recptypes * validate_recipients(char *supplied_recipients, const char *RemoteIdentifier, int Flags)
void free_recipients(struct recptypes *valid)
void CM_Free(struct CtdlMessage *msg)
Definition: msgbase.c:310
int CtdlForEachMessage(int mode, long ref, char *search_string, char *content_type, struct CtdlMessage *compare, ForEachMsgCallback CallBack, void *userdata)
Definition: msgbase.c:626
struct CtdlMessage * CtdlFetchMessage(long msgnum, int with_body)
Definition: msgbase.c:1135
long CtdlSubmitMsg(struct CtdlMessage *msg, struct recptypes *recps, const char *force)
Definition: msgbase.c:2603
void CM_SetField(struct CtdlMessage *Msg, eMsgField which, const char *buf, long length)
Definition: msgbase.c:142
@ MSGS_GT
Definition: msgbase.h:11
void * malloc(size_t)
void free(void *)
void listdeliver_sweep(void)
void listdeliver_queue_room(struct ctdlroom *qrbuf, void *data)
void listdeliver_do_msg(long msgnum, void *userdata)
void listdeliver_sweep_room(char *roomname)
int doing_listdeliver
struct ctdlroom qrbuf
Definition: serv_migrate.c:497
time_t last_run
@ eReplyTo
Definition: server.h:313
@ eMsgSubject
Definition: server.h:320
#define EVT_TIMER
Definition: server.h:224
@ S_NETCONFIGS
Definition: server.h:146
int snprintf(char *buf, size_t max, const char *fmt,...)
Definition: snprintf.c:69
char * cm_fields[256]
Definition: server.h:37
char QRname[128]
Definition: citadel.h:108
char * netconf
char * bounce_to
Definition: server.h:60
char * envelope_from
Definition: server.h:61
#define SIZ
Definition: sysconfig.h:33
void begin_critical_section(int which_one)
Definition: threads.c:67
void end_critical_section(int which_one)
Definition: threads.c:85