b75420c688d187f68ee04666a7480eb5c9ac298a
[mmh] / bbl.c
1 /* bbl.c - ease the tasks of a BBleader */
2 #ifndef lint
3 static char ident[] = "@(#)$Id: bbl.c,v 2.4 1992/11/04 00:39:25 jromine Exp $";
4 #endif  lint
5
6 #include "../h/mh.h"
7 #include "../h/local.h"
8 #include "../zotnet/bboards.h"
9 #include <ctype.h>
10 #include <pwd.h>
11 #include <stdio.h>
12 #ifdef LOCALE
13 #include        <locale.h>
14 #endif
15
16 /* \f */
17
18 static struct swit switches[] = {
19 #define SHELLSW 0
20     "shell program", 0,
21
22 #define VERBSW  1
23     "verbose", 0,
24 #define NVERBSW 2
25     "noverbose", 0,
26
27 #define HELPSW  3
28     "help", 4,
29
30     NULL, NULL
31 };
32
33 /* \f */
34
35 static  int     verbosw = 0;
36
37 static  int     sub_ok = 0;
38
39 static  char   *bboards = BBOARDS;
40
41 static  char   *cwd = NULL;
42
43 static  char   *current_folder = NULL;
44
45 static  char   *bbfolder = NULL;
46 static  char    subfolder[BUFSIZ];
47
48 static  struct stat bbstat;
49 static  struct stat substat;
50
51 static  char   *shell = "/bin/sh";
52
53 static  struct bboard  *bb = NULL;
54
55
56 #ifndef __STDC__
57 #ifdef  SYS5
58 struct  passwd  *getpwnam (), *getpwuid ();
59 #endif  /* SYS5 */
60 #endif
61
62 /* \f */
63
64 /* ARGSUSED */
65
66 main (argc, argv)
67 int     argc;
68 char  **argv;
69 {
70     char   *cp,
71           **ap,
72           **argp,
73             buffer[80],
74            *arguments[MAXARGS];
75     struct passwd  *pw;
76
77 #ifdef LOCALE
78         setlocale(LC_ALL, "");
79 #endif
80     invo_name = r1bindex (argv[0], '/');
81     if ((cp = m_find (invo_name)) != NULL) {
82         ap = brkstring (cp = getcpy (cp), " ", "\n");
83         ap = copyip (ap, arguments);
84     }
85     else
86         ap = arguments;
87     (void) copyip (argv + 1, ap);
88     argp = arguments;
89
90     if ((shell = getenv ("SHELL")) == NULL)
91         if ((pw = getpwuid (getuid ())) != NULL
92                 && pw -> pw_shell
93                 && *pw -> pw_shell)
94             shell = getcpy (pw -> pw_shell);
95
96     if ((pw = getpwnam (bboards)) == NULL)
97         adios (NULLCP, "no entry for ~%s", bboards);
98     if (pw -> pw_uid != geteuid ())
99         adios (NULLCP, "not running setuid to %s", bboards);
100
101     current_folder = ((cp = m_find (pfolder)) || (cp = m_find (inbox)))
102             ? getcpy (cp) : defalt;
103
104 /* \f */
105
106     while (cp = *argp++) {
107         if (*cp == '-')
108             switch (smatch (++cp, switches)) {
109                 case AMBIGSW: 
110                     ambigsw (cp, switches);
111                     done (1);
112                 case UNKWNSW: 
113                     adios (NULLCP, "-%s unknown", cp);
114                 case HELPSW: 
115                     (void) sprintf (buffer, "%s [+folder] [switches] bboard",
116                             invo_name);
117                     help (buffer, switches);
118                     done (1);
119
120                 case SHELLSW: 
121                     if (!(shell = *argp++) || *shell == '-')
122                         adios (NULLCP, "missing argument to %s", argp[-2]);
123                     continue;
124
125                 case VERBSW: 
126                     verbosw++;
127                     continue;
128                 case NVERBSW: 
129                     verbosw = 0;
130                     continue;
131             }
132         if (*cp == '+')
133             if (bbfolder)
134                 adios (NULLCP, "only one folder at a time!");
135             else
136                 bbfolder = cp;
137         else
138             if (bb != NULL)
139                 adios (NULLCP, "only one BBoard a time!");
140             else
141                 if ((bb = getbbnam (cp)) == NULL
142                         && (bb = getbbaka (cp)) == NULL)
143                     adios (NULLCP, "no such BBoard as '%s'", cp);
144     }
145
146 /* \f */
147
148     if (!bb)
149         adios (NULLCP, "no BBoard specified");
150     if (!bbfolder)
151         bbfolder = "+bbl";
152     (void) sprintf (subfolder, "%s/arc", bbfolder);
153
154     if (!m_find ("path"))
155         free (path ("./", TFOLDER));
156     cwd = getcpy (pwd ());
157
158     process ();
159
160     m_replace (pfolder, current_folder);
161     m_update ();
162
163     done (0);
164 }
165
166 /* \f */
167
168 process () {
169     int     child_id;
170     char    buffer[BUFSIZ];
171
172     if (!ldrbb (bb) && !ldrchk (bb))
173         return;
174
175     if (stat (bb -> bb_file, &bbstat) == NOTOK)
176         adios (NULLCP, "no such file as %s", bb -> bb_file);
177
178     if (stat (bb -> bb_archive, &substat) != NOTOK
179             && substat.st_size > 0)
180         sub_ok++;
181 /*  else */
182     substat.st_mode = bbstat.st_mode;/* archive should always match */
183     substat.st_gid = bbstat.st_gid;/* actual bboard mode & gid */
184
185 /* do subfolder first, since you will lose otherwise... */
186     (void) sprintf (buffer, "Remove messages currently in %s? ", subfolder);
187     if (check_folder (subfolder) && getanswer (buffer))
188         rmf (subfolder);
189
190     (void) sprintf (buffer, "Remove messages currently in %s? ", bbfolder);
191     if (check_folder (bbfolder) && getanswer (buffer))
192         rmf (bbfolder);
193
194     switch (child_id = fork ()) {
195         case NOTOK: 
196             adios ("fork", "unable to");
197
198         case OK: 
199             do_child ();        /* NOTREACHED */
200
201         default: 
202             do_parent (child_id);
203             break;
204     }
205 }
206
207 /* \f */
208
209 int     check_folder (folder)
210 char   *folder;
211 {
212     char   *maildir;
213     struct stat st;
214     struct msgs *mp;
215
216     maildir = m_maildir (folder + 1);
217
218     if (stat (maildir, &st) == NOTOK)
219         return 0;
220
221     if ((st.st_mode & S_IFMT) != S_IFDIR)
222         adios (NULLCP, "not a directory '%s'", maildir);
223     check_mode (maildir, (st.st_mode | 0555) & 0777);
224
225     if (chdir (maildir) == NOTOK)
226         adios (maildir, "unable to change to");
227     if (!(mp = m_gmsg (folder + 1)))
228         adios (NULLCP, "unable to read %s", folder);
229
230     if (chdir (cwd) == NOTOK)
231         admonish (cwd, "could not change back to");
232     return (mp -> hghmsg != 0);
233 }
234
235 /* \f */
236
237 do_parent (child_id)
238 int     child_id;
239 {
240     int     zap = 0;
241     char    buffer[BUFSIZ];
242
243     if (pidwait (child_id, NOTOK) == NOTOK)
244         done (1);
245
246     (void) putchar ('\n');
247
248     (void) check_folder (bbfolder);
249     if (getanswer ("Incorporate changes? "))
250         update (&bbstat, bb -> bb_file, bbfolder, bb -> bb_info, bb -> bb_map);
251     (void) sprintf (buffer, "Remove %s? ", bbfolder);
252     if (getanswer (buffer))
253         zap++;
254
255     if (check_folder (subfolder)) {
256         if (getanswer ("Update archives? "))
257             update (&substat, bb -> bb_archive, subfolder, NULLCP, NULLCP);
258     (void) sprintf (buffer, "Remove %s? ", subfolder);
259     if (getanswer (buffer))
260         rmf (subfolder);
261     }
262     else
263         if (sub_ok
264                 && getanswer ("Remove archives? ")
265                 && getanswer ("Are you sure? "))
266             if (unlink (bb -> bb_archive) == NOTOK)
267                 admonish (bb -> bb_archive, "unable to remove %s");
268     if (zap)
269         rmf (bbfolder);
270 }
271
272 /* \f */
273
274 check_mode (dir, mode)
275 char   *dir;
276 unsigned int    mode;
277 {
278     int     child_id;
279     struct stat st;
280 #ifdef SYS5DIR
281     struct dirent  *dp;
282 #else  SYS5DIR
283     struct direct  *dp;
284 #endif SYS5DIR
285     DIR * dd;
286
287     if (verbosw)
288         fprintf (stderr, "chmod %o %s\n", mode, dir);
289
290     switch (child_id = fork ()) {
291         case NOTOK: 
292             adios ("fork", "unable to");
293
294         case OK: 
295             (void) setgid (getgid ());
296             (void) setuid (getuid ());
297
298             if (chmod (dir, (int) mode) == NOTOK)
299                 adios (dir, "unable to change mode of");
300             if (chdir (dir) == NOTOK)
301                 adios (dir, "unable to change to");
302             if ((dd = opendir (dir)) == NULL)
303                 adios (dir, "unable to read");
304             while (dp = readdir (dd))
305                 if (dp -> d_name[0] != '.') {
306                     if (stat (dp -> d_name, &st) == NOTOK) {
307                         admonish (dp -> d_name, "unable to stat");
308                         continue;
309                     }
310                     if (chmod (dp -> d_name, (int) ((st.st_mode | 0444) & 0777))
311                             == NOTOK)
312                         admonish (dp -> d_name, "unable to change mode of");
313                 }
314             closedir (dd);
315             done (0);
316
317         default: 
318             if (pidwait (child_id, OK))
319                 done (1);
320             break;
321     }
322 }
323
324 /* \f */
325
326 /* ARGSUSED */
327
328 update (stp, file, folder, info, map)
329 struct stat *stp;
330 char   *file,
331        *folder,
332        *info,
333        *map;
334 {
335     int     fd;
336     struct stat st;
337
338     if (stat (file, &st) != NOTOK
339             && st.st_mtime != stp -> st_mtime) {
340         printf ("File '%s' has changed...\n", file);
341         if (getanswer ("Append to it instead? "))
342             goto work;
343         else
344             if (!getanswer ("Still update it? "))
345                 return;
346     }
347     if ((fd = creat (file, BBMODE)) == NOTOK)
348         adios (file, "unable to re-create");
349     else {
350         (void) close (fd);
351         if (map)
352             (void) unlink (map);
353     }
354 #ifdef  notdef
355     if (info)
356         check_info (folder, info);
357 #endif  notdef
358
359 work: ;
360     pack (folder, file);
361     if (chmod (file, (int) (stp -> st_mode & 0777)) == NOTOK)
362         admonish (file, "unable to change mode of");
363     if (stat (file, &st) != NOTOK && st.st_gid != stp -> st_gid)
364         chgrp (file, stp -> st_gid);
365 }
366
367 /* \f */
368
369 #ifdef  notdef
370 check_info (folder, info)
371 char   *folder,
372        *info;
373 {
374     int     id,
375             state;
376     char   *hdrptr,
377            *maildir,
378            *msgnam,
379             posted[BUFSIZ],
380             name[NAMESZ],
381             buf[BUFSIZ];
382     struct msgs *mp;
383     FILE * fp;
384
385     if (chdir (maildir = m_maildir (folder + 1)) == NOTOK)
386         adios (maildir, "unable to change to");
387
388     if (!(mp = m_gmsg (folder + 1)))
389         adios (NULL, "unable to read %s", folder);
390     if (mp -> hghmsg) {
391         if ((fp = fopen (msgnam = m_name (mp -> hghmsg), "r")) == NULL)
392             adios (NULL, "unable to read message %s in %s",
393                     msgnam, folder);
394         id = 0;
395         posted[0] = NULL;
396         for (state = FLD;;) {
397             switch (state = m_getfld (state, name, buf, sizeof buf, fp)) {
398                 case FLD: 
399                 case FLDEOF: 
400                 case FLDPLUS: 
401                     hdrptr = add (buf, NULL);
402                     while (state == FLDPLUS) {
403                         state = m_getfld (state, name, buf, sizeof buf, fp);
404                         hdrptr = add (buf, hdrptr);
405                     }
406                     if (uleq (name, "BBoard-ID")) {
407                         id = atoi (buf);
408                         if (id > 0 && posted[0])
409                             break;
410                     }
411                     if (uleq (name, "BB-Posted")) {
412                         strncpy (posted, buf, sizeof posted - 2);
413                         if (posted[strlen (posted) - 1] == '\n')
414                             posted[strlen (posted) - 1] = NULL;
415                         if (id > 0 && posted[0])
416                             break;
417                     }
418                     continue;
419
420                 default: 
421                     admonish (NULL, "unable to find BBoard-info in message %s",
422                             msgnam);
423                     (void) fclose (fp);
424                     goto no_risk;
425             }
426             break;
427         }
428         (void) fclose (fp);
429
430         if (verbosw)
431             fprintf (stderr,
432                     "[ Highest message has %s%d and\n\t\t      %s%s ]\n",
433                     "BBoard-ID: ", id, "BB-Posted: ", posted);
434
435         if ((fp = lkfopen (info, "w")) == NULL)
436             adios (info, "unable to lock and fopen");
437         fprintf (fp, "%d\n%s\n", id, posted);
438         (void) lkfclose (fp, info);
439     }
440
441 no_risk: ;
442     if (chdir (cwd) == NOTOK)
443         admonish (cwd, "could not change back to");
444 }
445 #endif  notdef
446
447 /* \f */
448
449 pack (folder, file)
450 char   *folder,
451        *file;
452 {
453     int     child_id;
454
455     switch (child_id = fork ()) {
456         case NOTOK: 
457             admonish ("fork", "unable to");
458             return;
459
460         case OK: 
461             if (verbosw)
462                 fprintf (stderr, "pack %s -file %s\n", folder, file);
463
464             execlp (packproc, r1bindex (packproc, '/'),
465                     folder, "-file", file, NULLCP);
466             fprintf (stderr, "unable to exec ");
467             perror (packproc);
468             _exit (-1);
469
470         default: 
471             (void) pidXwait (child_id, packproc);
472             break;
473     }
474 }
475
476 /* \f */
477
478 chgrp (file, gid)
479 char   *file;
480 short   gid;
481 {
482     int     child_id;
483     char    group[BUFSIZ];
484
485     switch (child_id = fork ()) {
486         case NOTOK: 
487             admonish ("fork", "unable to");
488             return;
489
490         case OK: 
491             (void) setuid (geteuid ());/* make sure chgrp works */
492             (void) sprintf (group, "%d", gid);
493             if (verbosw)
494                 fprintf (stderr, "chgrp %s %s\n", group, file);
495
496             execlp ("/bin/chgrp", "chgrp", group, file, NULLCP);
497             fprintf (stderr, "unable to exec ");
498             perror ("/bin/chgrp");
499             _exit (-1);
500
501         default: 
502             (void) pidXwait (child_id, "chgrp");
503             break;
504     }
505 }
506
507 /* \f */
508
509 rmf (folder)
510 char   *folder;
511 {
512     int     child_id;
513
514     switch (child_id = fork ()) {
515         case NOTOK: 
516             admonish ("fork", "unable to");
517             return;
518
519         case OK: 
520             (void) setgid (getgid ());
521             (void) setuid (getuid ());
522             if (verbosw)
523                 fprintf (stderr, "rmf %s\n", folder);
524
525             execlp (rmfproc, r1bindex (rmfproc, '/'), folder, NULLCP);
526             fprintf (stderr, "unable to exec ");
527             perror (rmfproc);
528             _exit (-1);
529
530         default: 
531             (void) pidXwait (child_id, rmfproc);
532             break;
533     }
534 }
535
536 /* \f */
537
538 do_child () {
539     char    buffer[BUFSIZ];
540
541     (void) setgid (getgid ());          /* become the user, not bboards */
542     (void) setuid (getuid ());
543
544     inc (bb -> bb_file, bbfolder);
545     if (sub_ok)
546         inc (bb -> bb_archive, subfolder);
547 /*  else
548         create the folder */
549
550     if (verbosw)
551         (void) putchar ('\n');
552     printf ("[ Working folder is %s, Archive folder is %s ]\n",
553             bbfolder, subfolder);
554     printf ("[ Type CTRL-D to finish ]\n");
555
556     m_replace (pfolder, bbfolder + 1);
557     m_update ();
558
559     (void) sprintf (buffer, "=> %s: %s", invo_name, bb -> bb_name);
560     execlp (shell, buffer, NULLCP);
561     fprintf (stderr, "unable to exec ");
562     perror (shell);
563     _exit (-1);
564 }
565
566 /* \f */
567
568 inc (file, folder)
569 char   *file,
570        *folder;
571 {
572     int     child_id;
573
574     switch (child_id = fork ()) {
575         case NOTOK: 
576             adios ("fork", "unable to");
577
578         case OK: 
579             if (verbosw)
580                 fprintf (stderr, "inc %s -file %s -silent\n", folder, file);
581             execlp (incproc, r1bindex (incproc, '/'),
582                     folder, "-file", file, "-silent", NULLCP);
583             fprintf (stderr, "unable to exec ");
584             perror (incproc);
585             _exit (-1);
586
587         default: 
588             if (pidXwait (child_id, incproc))
589                 done (1);
590             break;
591     }
592 }