* New UI for mailing list setup
[citadel.git] / citadel / serv_network.c
1 /*
2  * $Id$ 
3  *
4  * This module will eventually replace netproc and some of its utilities.  In
5  * the meantime, it serves as a mailing list manager.
6  *
7  * Copyright (C) 2000-2001 by Art Cancro and others.
8  * This code is released under the terms of the GNU General Public License.
9  *
10  */
11
12 #include "sysdep.h"
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <stdio.h>
16 #include <fcntl.h>
17 #include <signal.h>
18 #include <pwd.h>
19 #include <errno.h>
20 #include <sys/types.h>
21
22 #if TIME_WITH_SYS_TIME
23 # include <sys/time.h>
24 # include <time.h>
25 #else
26 # if HAVE_SYS_TIME_H
27 #  include <sys/time.h>
28 # else
29 #  include <time.h>
30 # endif
31 #endif
32
33 #include <sys/wait.h>
34 #include <string.h>
35 #include <limits.h>
36 #include "citadel.h"
37 #include "server.h"
38 #include "sysdep_decls.h"
39 #include "citserver.h"
40 #include "support.h"
41 #include "config.h"
42 #include "dynloader.h"
43 #include "room_ops.h"
44 #include "user_ops.h"
45 #include "policy.h"
46 #include "database.h"
47 #include "msgbase.h"
48 #include "tools.h"
49 #include "internet_addressing.h"
50 #include "serv_network.h"
51
52
53 void cmd_gnet(char *argbuf) {
54         char filename[SIZ];
55         char buf[SIZ];
56         FILE *fp;
57
58         if (CtdlAccessCheck(ac_room_aide)) return;
59         assoc_file_name(filename, &CC->quickroom, "netconfigs");
60         cprintf("%d Network settings for room #%ld <%s>\n",
61                 LISTING_FOLLOWS,
62                 CC->quickroom.QRnumber, CC->quickroom.QRname);
63
64         fp = fopen(filename, "r");
65         if (fp != NULL) {
66                 while (fgets(buf, sizeof buf, fp) != NULL) {
67                         buf[strlen(buf)-1] = 0;
68                         cprintf("%s\n", buf);
69                 }
70                 fclose(fp);
71         }
72
73         cprintf("000\n");
74 }
75
76
77 void cmd_snet(char *argbuf) {
78         char tempfilename[SIZ];
79         char filename[SIZ];
80         char buf[SIZ];
81         FILE *fp;
82
83         if (CtdlAccessCheck(ac_room_aide)) return;
84         safestrncpy(tempfilename, tmpnam(NULL), sizeof tempfilename);
85         assoc_file_name(filename, &CC->quickroom, "netconfigs");
86
87         fp = fopen(tempfilename, "w");
88         if (fp == NULL) {
89                 cprintf("%d Cannot open %s: %s\n",
90                         ERROR+INTERNAL_ERROR,
91                         tempfilename,
92                         strerror(errno));
93         }
94
95         cprintf("%d %s\n", SEND_LISTING, tempfilename);
96         while (client_gets(buf), strcmp(buf, "000")) {
97                 fprintf(fp, "%s\n", buf);
98         }
99         fclose(fp);
100
101         /* Now copy the temp file to its permanent location
102          * (We use /bin/mv instead of link() because they may be on
103          * different filesystems)
104          */
105         unlink(filename);
106         snprintf(buf, sizeof buf, "/bin/mv %s %s", tempfilename, filename);
107         system(buf);
108 }
109
110
111
112 /*
113  * Spools out one message from the list.
114  */
115 void network_spool_msg(long msgnum, void *userdata) {
116         struct SpoolControl *sc;
117         struct namelist *nptr;
118         int err;
119         char *instr = NULL;
120         size_t instr_len = SIZ;
121         struct CtdlMessage *imsg;
122
123         sc = (struct SpoolControl *)userdata;
124
125         /* If no recipients, bail out now.
126          * (May need to tweak this when we add other types of targets)
127          */
128         if (sc->listrecps == NULL) return;
129         
130         /* First, copy it to the spoolout room */
131         err = CtdlSaveMsgPointerInRoom(SMTP_SPOOLOUT_ROOM, msgnum, 0);
132         if (err != 0) return;
133
134         /* 
135          * Figure out how big a buffer we need to allocate
136          */
137         for (nptr = sc->listrecps; nptr != NULL; nptr = nptr->next) {
138                 instr_len = instr_len + strlen(nptr->name);
139         }
140
141         /*
142          * allocate...
143          */
144         lprintf(9, "Generating delivery instructions\n");
145         instr = mallok(instr_len);
146         if (instr == NULL) {
147                 lprintf(1, "Cannot allocate %d bytes for instr...\n",
148                         instr_len);
149                 abort();
150         }
151         sprintf(instr,
152                 "Content-type: %s\n\nmsgid|%ld\nsubmitted|%ld\n"
153                 "bounceto|postmaster@%s\n" ,
154                 SPOOLMIME, msgnum, time(NULL), config.c_fqdn );
155
156         /* Generate delivery instructions for each recipient */
157         for (nptr = sc->listrecps; nptr != NULL; nptr = nptr->next) {
158                 sprintf(&instr[strlen(instr)], "remote|%s|0||\n",
159                         nptr->name);
160         }
161
162         /*
163          * Generate a message from the instructions
164          */
165         imsg = mallok(sizeof(struct CtdlMessage));
166         memset(imsg, 0, sizeof(struct CtdlMessage));
167         imsg->cm_magic = CTDLMESSAGE_MAGIC;
168         imsg->cm_anon_type = MES_NORMAL;
169         imsg->cm_format_type = FMT_RFC822;
170         imsg->cm_fields['A'] = strdoop("Citadel");
171         imsg->cm_fields['M'] = instr;
172
173         /* Save delivery instructions in spoolout room */
174         CtdlSaveMsg(imsg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL);
175         CtdlFreeMessage(imsg);
176
177         /* update lastsent */
178         sc->lastsent = msgnum;
179 }
180
181
182
183
184 /*
185  * Batch up and send all outbound traffic from the current room
186  */
187 void network_spoolout_room(struct quickroom *qrbuf, void *data) {
188         char filename[SIZ];
189         char buf[SIZ];
190         char instr[SIZ];
191         FILE *fp;
192         struct SpoolControl sc;
193         /* struct namelist *digestrecps = NULL; */
194         struct namelist *nptr;
195
196         memcpy(&CC->quickroom, qrbuf, sizeof(struct quickroom));
197
198         memset(&sc, 0, sizeof(struct SpoolControl));
199         assoc_file_name(filename, &CC->quickroom, "netconfigs");
200
201         fp = fopen(filename, "r");
202         if (fp == NULL) {
203                 lprintf(7, "Outbound batch processing skipped for <%s>\n",
204                         CC->quickroom.QRname);
205                 return;
206         }
207
208         lprintf(5, "Outbound batch processing started for <%s>\n",
209                 CC->quickroom.QRname);
210
211         while (fgets(buf, sizeof buf, fp) != NULL) {
212                 buf[strlen(buf)-1] = 0;
213
214                 extract(instr, buf, 0);
215                 if (!strcasecmp(instr, "lastsent")) {
216                         sc.lastsent = extract_long(buf, 1);
217                 }
218                 else if (!strcasecmp(instr, "listrecp")) {
219                         nptr = (struct namelist *)
220                                 mallok(sizeof(struct namelist));
221                         nptr->next = sc.listrecps;
222                         extract(nptr->name, buf, 1);
223                         sc.listrecps = nptr;
224                 }
225
226
227         }
228         fclose(fp);
229
230
231         /* Do something useful */
232         CtdlForEachMessage(MSGS_GT, sc.lastsent, (-63), NULL, NULL,
233                 network_spool_msg, &sc);
234
235
236         /* Now rewrite the config file */
237         fp = fopen(filename, "w");
238         if (fp == NULL) {
239                 lprintf(1, "ERROR: cannot open %s: %s\n",
240                         filename, strerror(errno));
241         }
242         else {
243                 fprintf(fp, "lastsent|%ld\n", sc.lastsent);
244
245                 /* Write out the listrecps while freeing from memory at the
246                  * same time.  Am I clever or what?  :)
247                  */
248                 while (sc.listrecps != NULL) {
249                         fprintf(fp, "listrecp|%s\n", sc.listrecps->name);
250                         nptr = sc.listrecps->next;
251                         phree(sc.listrecps);
252                         sc.listrecps = nptr;
253                 }
254
255                 fclose(fp);
256         }
257
258         lprintf(5, "Outbound batch processing finished for <%s>\n",
259                 CC->quickroom.QRname);
260 }
261
262
263 /*
264  * network_do_queue()
265  * 
266  * Run through the rooms doing various types of network stuff.
267  */
268 void network_do_queue(void) {
269         static int doing_queue = 0;
270         static time_t last_run = 0L;
271
272 #define NETWORK_QUEUE_FREQUENCY 3600    /* one hour ... FIXME put in config */
273         /*
274          * Run no more frequently than once every n seconds
275          */
276         if ( (time(NULL) - last_run) < NETWORK_QUEUE_FREQUENCY ) return;
277
278         /*
279          * This is a simple concurrency check to make sure only one queue run
280          * is done at a time.  We could do this with a mutex, but since we
281          * don't really require extremely fine granularity here, we'll do it
282          * with a static variable instead.
283          */
284         if (doing_queue) return;
285         doing_queue = 1;
286         last_run = time(NULL);
287
288         /* 
289          * Go ahead and run the queue
290          */
291         lprintf(7, "network: processing outbound queue\n");
292         ForEachRoom(network_spoolout_room, NULL);
293         lprintf(7, "network: queue run completed\n");
294         doing_queue = 0;
295 }
296
297
298 char *Dynamic_Module_Init(void)
299 {
300         CtdlRegisterProtoHook(cmd_gnet, "GNET", "Get network config");
301         CtdlRegisterProtoHook(cmd_snet, "SNET", "Get network config");
302         CtdlRegisterSessionHook(network_do_queue, EVT_TIMER);
303         return "$Id$";
304 }