source: trunk/athena/bin/discuss/server/coreutil.c @ 12439

Revision 12439, 18.9 KB checked in by kcr, 26 years ago (diff)
Autoconfiscation and cleanup.
Line 
1/*
2 *
3 *      Copyright (C) 1988, 1989 by the Massachusetts Institute of Technology
4 *      Developed by the MIT Student Information Processing Board (SIPB).
5 *      For copying information, see the file mit-copyright.h in this release.
6 *
7 */
8/*
9 *      $Id: coreutil.c,v 1.31 1999-02-02 20:40:41 kcr Exp $
10 *
11 *
12 * coreutil.c  -- These contain lower-layer, utility type routines to
13 *                be used by core.  These include things to handle the
14 *                in-memory superblock, and to open & close meetings.
15 *
16 */
17
18#ifndef lint
19#ifdef __STDC__
20const
21#endif
22static char rcsid_coreutil_c[] =
23    "$Id: coreutil.c,v 1.31 1999-02-02 20:40:41 kcr Exp $";
24#endif /* lint */
25
26#include <discuss/types.h>
27#include <discuss/dsc_et.h>
28#include "atom.h"
29#include "mtg.h"
30#include <discuss/tfile.h>
31#include <discuss/acl.h>
32#include "internal.h"
33#include "ansi.h"
34#if HAVE_ZEPHYR
35#include <zephyr/zephyr.h>
36#endif
37#include <stdio.h>
38#include <stdlib.h>
39#include <errno.h>
40#include <sys/types.h>
41#include <netdb.h>
42#include <sys/file.h>
43#include <sys/stat.h>
44#include <string.h>
45#if HAVE_FCNTL_H
46#include <fcntl.h>
47#endif
48
49/* global variables */
50char current_mtg [256] = "";    /* meeting that's opened */
51int u_trn_f,u_control_f,u_acl_f; /* UNIX file descriptors */
52bool nuclear = FALSE;           /* Using atomic reads/writes */
53afile a_control_f = NULL;       /* radioactive file descriptor */
54tfile abort_file = NULL;        /* close this on abort */
55bool  read_lock = FALSE;        /* have lock on u_control_f */
56bool  mtg_swapped = FALSE;      /* current meeting is swapped */
57dsc_acl   *mtg_acl = NULL;      /* current mtg acl */
58int     last_acl_mod;           /* last mod to ACL */
59mtg_super super;
60char *super_chairman;
61char *super_long_name;
62int has_privs = 0;              /* Has privileges (linked) */
63int no_nuke = 0;                /* Don't be atomic (linked) */
64#if HAVE_ZEPHYR
65int use_zephyr = 1;             /* Actually do use Zephyr. */
66#else
67int use_zephyr = 0;
68#endif
69
70
71/* EXTERNAL */
72extern off_t lseek();
73extern int errno;
74extern char rpc_caller [];
75
76/*
77 *
78 * new_string (s)  --   Routine to create a copy of the given string, using
79 *                      malloc.
80 *
81 */
82char *new_string (s)
83char *s;
84{
85     int len;
86     char *newstr;
87
88     len = strlen (s) + 1;
89     newstr = malloc ((unsigned)len);
90     (void) strcpy (newstr, s);
91     return (newstr);
92}
93
94/*
95 *
96 * open_mtg (mtg_name) -- Routine to open a meeting, if necessary.
97 *
98 */
99int open_mtg (mtg_name)
100char *mtg_name;
101{
102     char str[256];
103     int result;
104     int mtg_name_len;
105     trn_base tb;
106     int uid = (int)geteuid();
107     struct stat sb;
108
109     mtg_name_len = strlen (mtg_name);
110     if (mtg_name[0] != '/' || mtg_name_len == 0 || mtg_name_len > 168
111         || mtg_name [mtg_name_len-1] == '/') {
112          return (BAD_PATH);
113     }
114
115     /* Check for moved meeting */
116     (void) strcpy (str, mtg_name);
117     (void) strcat (str, "/forward");
118     if (!stat(str, &sb)) {
119          return(MTG_MOVED);
120     }
121
122     (void) strcpy (str, mtg_name);
123     (void) strcat (str, "/acl");
124
125     if (!strcmp (mtg_name, current_mtg)) {
126          /*
127           * is acl stale?
128           */
129          if (stat(str, &sb) >= 0)
130               if (sb.st_mtime <= last_acl_mod)
131                    return (0);                         /* that was easy */
132     }
133
134     if (current_mtg [0] != '\0') {             /* close previous meeting */
135          if (nuclear)
136               panic ("Nuclear flag error");    /* should never happen */
137          (void) close (u_trn_f);
138          (void) close (u_control_f);
139          current_mtg [0] = '\0';
140          acl_destroy(mtg_acl);
141     }
142
143     u_trn_f = u_control_f = u_acl_f = 0;
144
145     if ((u_acl_f = open(str, O_RDONLY, 0700)) < 0) {
146          if (errno == ENOENT)
147               result = NO_SUCH_MTG;
148          else if (errno == EACCES)
149               result = NO_ACCESS;
150          else
151               result = BAD_PATH;
152          goto punt;
153     }
154     if (!fis_owner (u_acl_f, uid)) {
155          result = NO_ACCESS;
156          goto punt;
157     }
158
159     mtg_acl = acl_read (u_acl_f);
160     (void) fstat(u_acl_f, &sb);
161     last_acl_mod = sb.st_mtime;
162     (void) close(u_acl_f);
163     u_acl_f = 0;
164
165     (void) strcpy (str, mtg_name);
166     (void) strcat (str, "/control");
167
168     if ((u_control_f = open(str, O_RDWR, 0700)) < 0) {
169          if (errno == ENOENT)
170               result = NO_SUCH_MTG;
171          else if (errno == EACCES)
172               result = NO_ACCESS;
173          else
174               result = BAD_PATH;
175          goto punt;
176     }
177
178     if (!fis_owner (u_control_f, uid)) {
179          result = NO_ACCESS;
180          goto punt;
181     }
182
183     (void) strcpy (str, mtg_name);
184     (void) strcat (str, "/transactions");
185     
186     if ((u_trn_f = open(str, O_RDWR, 0700)) < 0) {
187          if (errno == ENOENT)
188               result = NO_SUCH_MTG;
189          else if (errno == EACCES)
190               result = NO_ACCESS;
191          else
192               result = BAD_PATH;
193          goto punt;
194     }
195
196     if (!fis_owner (u_trn_f, uid)) {
197          result = NO_ACCESS;
198          goto punt;
199     }
200
201     read (u_trn_f, (char *) &tb, sizeof (trn_base));
202     if (tb.unique == TRN_BASE_UNIQUE_SWAP) {
203          swap_trn_base(&tb);
204          mtg_swapped = TRUE;
205     } else {
206          mtg_swapped = FALSE;
207     }
208
209     if (tb.unique != TRN_BASE_UNIQUE) {
210          result = INCONSISTENT;
211          goto punt;
212     }
213
214     (void) strcpy (current_mtg, mtg_name);
215
216     return (0);
217
218 punt:
219     if (u_trn_f)
220             (void) close(u_trn_f);
221     if (u_control_f)
222             (void) close(u_control_f);
223     if (u_acl_f)
224             (void) close(u_acl_f);
225     acl_destroy(mtg_acl);
226     mtg_acl = NULL;
227     return (result);
228}
229
230
231int read_super()
232{
233     if (nuclear) {
234          aread (a_control_f, (char *) &super, sizeof(super), 0);
235     } else {
236             lseek (u_control_f, (long)0, 0);
237             read (u_control_f, (char *) &super, sizeof (super));
238     }
239
240     if (!mtg_swapped) {
241          if (super.unique != MTG_SUPER_UNIQUE)
242               return (INCONSISTENT);
243     } else {
244          if (super.unique != MTG_SUPER_UNIQUE_SWAP)
245               return (INCONSISTENT);
246          swap_super(&super);
247     }
248
249     if (super.version != MTG_SUPER_1)
250          return (NEW_VERSION);
251
252     super_long_name = malloc ((unsigned)super.long_name_len);
253     super_chairman = malloc ((unsigned)super.chairman_len);
254
255     if (nuclear) {
256          aread (a_control_f, super_long_name, super.long_name_len, super.long_name_addr);
257          aread (a_control_f, super_chairman, super.chairman_len, super.chairman_addr);
258     } else {
259          lseek (u_control_f, (long)super.long_name_addr, 0);
260          read (u_control_f, super_long_name, super.long_name_len);
261          lseek (u_control_f, (long)super.chairman_addr, 0);
262          read (u_control_f, super_chairman, super.chairman_len);
263     }
264
265     return(0);
266}
267
268write_super ()
269{
270     int sc_len, sl_len;
271     int slong_name_len, slong_name_addr;
272     int schairman_len, schairman_addr;
273
274     sc_len = strlen (super_chairman)+1;
275     sl_len = strlen (super_long_name)+1;
276
277     if (sc_len != super.chairman_len || sl_len != super.long_name_len) {               /* reallocate things */
278          super.long_name_addr = sizeof (super);
279          super.long_name_len = sl_len;
280          super.chairman_addr = super.long_name_addr + super.long_name_len;
281          super.chairman_len = sc_len;
282     }
283
284     /* Copy into locals to avoid swapping */
285     slong_name_len = super.long_name_len;
286     slong_name_addr = super.long_name_addr;
287     schairman_len = super.chairman_len;
288     schairman_addr = super.chairman_addr;
289
290     if (mtg_swapped)
291          swap_super(&super);
292
293     if (nuclear) {
294          awrite (a_control_f, (char *) &super, sizeof (super), 0);
295          awrite (a_control_f, super_long_name, slong_name_len, slong_name_addr);
296          awrite (a_control_f, super_chairman, schairman_len, schairman_addr);
297     } else {
298          lseek (u_control_f, (long)0, 0);
299          write (u_control_f, (char *)  &super, sizeof (super));
300          lseek (u_control_f, (long)slong_name_addr, 0);
301          write (u_control_f, super_long_name, slong_name_len);
302          lseek (u_control_f, (long)schairman_addr, 0);
303          write (u_control_f, super_chairman, schairman_len);
304     }
305
306     free (super_chairman);
307     super_chairman = NULL;
308     free (super_long_name);
309     super_long_name = NULL;
310     super.unique = 0;                                  /* prevent accidents */
311
312     return;
313}
314
315/*
316 *
317 * forget_super () -- Routine to dump superblock, like when we just read
318 *                    it.  This frees any alloc'd storage.
319 *
320 */
321forget_super()
322{
323     if (super_long_name != NULL)
324          free(super_long_name);
325     super_long_name = NULL;
326
327     if (super_chairman != NULL)
328          free(super_chairman);
329     super_chairman = NULL;
330     super.unique = 0;
331}
332
333/*
334 *
335 * start_read () --     This reserves the meeting for non-destructive reading.
336 *                      Simply does an flock.
337 *
338 */
339start_read()
340{
341     struct flock lock;
342
343     if (!no_nuke) {
344          lock.l_type = F_RDLCK;
345          lock.l_start = 0;
346          lock.l_whence = 0;
347          lock.l_len = 0;
348          if (fcntl(u_control_f, F_SETLKW, &lock) < 0)
349               panic ("Cannot share lock");
350          read_lock = TRUE;
351     }
352}
353
354/*
355 *
356 * finish_read () --    This frees up our reservation on the meeting.  This
357 *                      does the opposite of start_read.
358 *
359 */
360finish_read()
361{
362    struct flock lock;
363
364     if (!no_nuke) {
365           lock.l_type = F_UNLCK;
366          lock.l_start = 0;
367          lock.l_whence = 0;
368          lock.l_len = 0;
369          fcntl(u_control_f, F_SETLK, &lock);
370          read_lock = 0;
371     }
372}
373
374/*
375 *
376 * chain_addr -- Returns address of given chain block.
377 *
378 */
379faddr chain_addr(trn)
380trn_nums trn;
381{
382     if (trn < super.lowest || trn > super.highest)
383          return (0);
384
385     return (super.chain_start + (trn-1) * sizeof (chain_blk));
386}
387
388int read_chain (trn, cb)
389trn_nums trn;
390chain_blk *cb;
391{
392     faddr cbaddr;
393
394     cbaddr = chain_addr (trn);
395     if (cbaddr == 0)
396          return (NO_SUCH_TRN);
397
398     if (nuclear)
399          aread (a_control_f, (char *) cb, sizeof (chain_blk), cbaddr);
400     else {
401          lseek (u_control_f, (long)cbaddr, 0);
402          read (u_control_f, (char *) cb, sizeof (chain_blk));
403     }
404
405     if (mtg_swapped)
406          swap_chain(cb);
407
408     if (cb -> unique != CHAIN_BLK_UNIQUE || cb -> current != trn)
409          return (INCONSISTENT);
410
411     if (cb -> version != CHAIN_BLK_1)
412          return (NEW_VERSION);
413
414     return (0);
415}
416
417int write_chain (cb)
418chain_blk *cb;
419{
420     faddr cbaddr;
421
422     cbaddr = chain_addr (cb -> current);
423     if (cbaddr == 0)
424          return (NO_SUCH_TRN);
425
426     if (mtg_swapped)
427          swap_chain(cb);
428
429     if (nuclear) {
430          if (awrite (a_control_f, (char *) cb, sizeof (chain_blk), cbaddr) != sizeof (chain_blk)) {
431               if (mtg_swapped)
432                    swap_chain(cb);
433               return (NO_WRITE);
434          }
435     }
436     else {
437          lseek (u_control_f, (long)cbaddr, 0);
438          if (write (u_control_f, (char *) cb, sizeof (chain_blk)) != sizeof (chain_blk)) {
439               if (mtg_swapped)
440                    swap_chain(cb);
441               return (NO_WRITE);
442          }
443     }
444
445     if (mtg_swapped)
446          swap_chain(cb);
447     return (0);
448}
449
450/*
451 *
452 * read_trn -- routine to read a transaction from the transaction file.
453 *
454 */
455int read_trn (trn_addr, th, th_subject, th_author, th_signature)
456faddr trn_addr;
457trn_hdr *th;
458char **th_subject, **th_author, **th_signature;
459{
460     char *author;
461
462     lseek (u_trn_f, (long)trn_addr, 0);
463     read (u_trn_f, (char *) th, sizeof (trn_hdr));
464
465     if (mtg_swapped)
466          swap_trn(th);
467
468     if (th -> unique != TRN_HDR_UNIQUE)
469          return (INCONSISTENT);
470
471     if (th -> version != TRN_HDR_1)
472          return(NEW_VERSION);
473
474     if (th_subject != NULL) {
475          *th_subject = malloc ((unsigned)(th -> subject_len));
476          lseek (u_trn_f, (long)(th -> subject_addr), 0);
477          read (u_trn_f, *th_subject, th -> subject_len);
478     }
479         
480     if (th_author != NULL || th_signature != NULL) {
481          author = malloc ((unsigned)(th -> author_len));
482          lseek (u_trn_f, (long)(th -> author_addr), 0);
483          read (u_trn_f, author, th -> author_len);
484     }
485
486     if (th_author != NULL)
487          *th_author = author;
488
489     if (th_signature != NULL) {
490          if (strlen(author)+1 == th -> author_len) {
491               *th_signature = new_string(author);      /* No signature, return author */
492          } else {
493               *th_signature = new_string(&author[strlen(author)+1]);
494          }
495          if (th_author == NULL)
496               free(author);
497     }
498               
499     return(0);
500}
501         
502
503/*
504 *
505 * core_abort () -- Routine for use by core routines to clean things up.
506 *
507 */
508core_abort ()
509{
510     int dummy;
511
512     if (nuclear) {
513          aabort(a_control_f);
514          nuclear = FALSE;
515     }
516
517     if (abort_file != NULL) {
518          tclose (abort_file,&dummy);
519          abort_file = NULL;
520     }
521
522     if (read_lock)
523          finish_read();
524
525     forget_super();
526
527     return;
528}
529
530/*
531 *
532 * fsize () -- Routine to find out the size of a file.
533 *
534 */
535fsize (d)
536int d;
537{
538     struct stat buf;
539
540     if (fstat (d, &buf) < 0)
541          return (0);
542
543     return (buf.st_size);
544}
545/*
546 *
547 * fis_owner () -- Routine to find out if uid is owner of file.
548 *
549 */
550fis_owner (d, uid)
551int d,uid;
552{
553     struct stat buf;
554
555     if (fstat (d, &buf) < 0)
556          return (0);
557
558     return (uid == buf.st_uid);
559}
560
561/*
562 *
563 * has_trn_access -- Routine to return true if current user can access
564 *                   transaction.
565 *
566 */
567bool has_trn_access(author,mode)
568char *author;
569char mode;
570{
571     char *test_modes;
572
573     if (has_privs)
574          return(TRUE);                         /* linked in */
575
576     switch (mode) {
577     case 'd':
578          if (!strcmp(author,rpc_caller)) {
579               test_modes = "o";
580          } else {
581               test_modes = "d";
582          }
583          return (acl_check(mtg_acl,rpc_caller,test_modes));
584
585     case 'r':
586          return (acl_check(mtg_acl,rpc_caller,"r") ||
587                  ((!strcmp (author, rpc_caller)) && acl_check(mtg_acl,rpc_caller,"o")));
588     default:
589          panic ("Invalid mode");
590          /*NOTREACHED*/
591     }
592}         
593/*
594 *
595 * has_mtg_access -- Routine to return true if current user can access
596 *                   mtg, with given mode.
597 *
598 */
599bool has_mtg_access(mode)
600char mode;
601{
602     char *test_modes;
603     char mode_str[3];
604
605     if (has_privs)                             /* linked in stuff */
606          return (TRUE);               
607
608     switch (mode) {
609     case 'w':
610     case 'a':
611     case 'r':
612     case 's':
613     case 'o':
614          mode_str[0] = mode;
615          mode_str[1] = '\0';
616          test_modes = mode_str;
617          break;
618
619     case 'c':
620          test_modes = "c";
621          break;
622
623     default:
624          panic("Invalid mode");
625     }
626
627     return(acl_check(mtg_acl,rpc_caller,test_modes));
628}         
629/*
630 *
631 * panic -- just a printf
632 *
633 */
634panic(str)
635char *str;
636{
637     printf("panic: %s\n",str);
638     perror("discuss");
639     exit(1);
640}
641
642#if HAVE_ZEPHYR
643/*
644 *
645 * mtg_znotify -- send off a Zephyr notification as appropriate
646 *
647 */
648
649static const char * this_host = (const char *) NULL;
650
651void mtg_znotify(mtg_name, subject, author, signature)
652        char *mtg_name, *subject, *author, *signature;
653{
654        register dsc_acl_entry *ae;
655        register int n;
656        ZNotice_t notice;
657        char *msglst[5],bfr[30],fullpath[256];
658        int code, list_size;
659
660        if (!use_zephyr)
661            return;
662
663        if (!this_host) {
664            /* perform initializations */
665            char *h;
666            char host[100];
667            struct hostent *hent;
668            if (gethostname(host,100) != 0)
669                return;
670            hent = (struct hostent *) gethostbyname(host);
671            if (hent == 0)
672                return;
673            h = (char *) malloc (strlen (hent->h_name) + 1);
674            if (!h)
675                return;
676            strcpy (h, hent->h_name);
677            this_host = h;
678            ZInitialize();
679        }
680
681        /* Set up the notice structure */
682        memset(&notice, 0, sizeof(notice));
683
684        sprintf(fullpath,"%s:%s", this_host, mtg_name);
685        ZOpenPort(NULL);
686        notice.z_kind = UNSAFE;
687        notice.z_port = 0;
688        notice.z_class = "DISCUSS";
689        notice.z_class_inst = fullpath;
690        notice.z_opcode = "NEW_TRN";
691        notice.z_sender = 0;
692        if (signature == NULL)
693             notice.z_default_format = "New transaction [$1] entered in $2\nFrom: $3\nSubject: $4";
694        else
695             notice.z_default_format = "New transaction [$1] entered in $2\nFrom: $3 ($5)\nSubject: $4";
696        msglst[0] = bfr;
697        sprintf(msglst[0],"%04d",super.highest);
698        msglst[1] = super_long_name;
699        msglst[2] = author;
700        msglst[3] = subject;
701        list_size = 4;
702        if (signature != NULL) {
703             msglst[4] = signature;
704             list_size = 5;
705        }
706
707        /* Does "*" have read access? If so, just send out a global
708         * notice.
709         */
710
711        /* XXX
712         * Check at some point for people who don't have access, etc.
713         */
714
715        for (ae = mtg_acl->acl_entries, n=mtg_acl->acl_length;
716             n;
717             ae++, n--) {
718                if ((strcmp("*", ae->principal) == 0) &&
719                    acl_is_subset("r", ae->modes))
720                        break;
721        }
722        if (n) {
723                notice.z_recipient = "";
724                /* We really don't care if it gets through... */
725                code = ZSendList(&notice,msglst,list_size,ZNOAUTH);
726                return;
727        }
728        for (ae = mtg_acl->acl_entries, n=mtg_acl->acl_length;
729             n;
730             ae++, n--) {
731                if (acl_is_subset("r", ae->modes)) {
732                        notice.z_recipient = ae->principal;
733                        ZSendList(&notice,msglst,list_size,ZNOAUTH);
734                }
735        }
736}
737#endif
738
739static unsigned long swap_32(val)
740unsigned long val;
741{
742     unsigned char b1 = (val >> 24) & 0xff;
743     unsigned char b2 = (val >> 16) & 0xff;
744     unsigned char b3 = (val >> 8) & 0xff;
745     unsigned char b4 = val & 0xff;
746
747     return ((b4 << 24) | (b3 << 16) | (b2 << 8) | b1);
748}
749
750static unsigned int swap_16(val)
751unsigned int val;
752{
753     unsigned char b1 = (val >> 8) & 0xff;
754     unsigned char b2 = val & 0xff;
755
756     return ((b2 << 8) | b1);
757}
758
759swap_super(superp)
760mtg_super *superp;
761{
762     superp->version = swap_32(superp->version);
763     superp->unique = swap_32(superp->unique);
764     superp->first = swap_32(superp->first);
765     superp->last = swap_32(superp->last);
766     superp->lowest = swap_32(superp->lowest);
767     superp->highest = swap_32(superp->highest);
768     superp->highest_chain = swap_32(superp->highest_chain);
769     superp->date_created = swap_32(superp->date_created);
770     superp->date_modified = swap_32(superp->date_modified);
771     superp->long_name_addr = swap_32(superp->long_name_addr);
772     superp->chairman_addr = swap_32(superp->chairman_addr);
773     superp->long_name_len = swap_16(superp->long_name_len);
774     superp->chairman_len = swap_16(superp->chairman_len);
775     superp->flags = swap_16(superp->flags);
776     superp->chain_start = swap_32(superp->chain_start);
777     superp->high_water = swap_32(superp->high_water);
778     superp->trn_fsize = swap_32(superp->trn_fsize);
779     superp->highest_trn_addr = swap_32(superp->highest_trn_addr);
780}
781
782swap_chain(cbp)
783chain_blk *cbp;
784{
785     cbp->version = swap_32(cbp->version);
786     cbp->unique = swap_32(cbp->unique);
787     cbp->current = swap_32(cbp->current);
788     cbp->prev = swap_32(cbp->prev);
789     cbp->next = swap_32(cbp->next);
790     cbp->pref = swap_32(cbp->pref);
791     cbp->nref = swap_32(cbp->nref);
792     cbp->trn_chain = swap_32(cbp->trn_chain);
793     cbp->trn_addr = swap_32(cbp->trn_addr);
794     cbp->flags = swap_16(cbp->flags);
795     cbp->filler = swap_16(cbp->filler);
796     cbp->chain_fref = swap_32(cbp->chain_fref);
797     cbp->chain_lref = swap_32(cbp->chain_lref);
798}
799
800swap_trn_base(tbp)
801trn_base *tbp;
802{
803     tbp->version = swap_32(tbp->version);
804     tbp->unique = swap_32(tbp->unique);
805     tbp->date_created = swap_32(tbp->date_created);
806     tbp->long_name_addr = swap_32(tbp->long_name_addr);
807     tbp->chairman_addr = swap_32(tbp->chairman_addr);
808     tbp->long_name_len = swap_16(tbp->long_name_len);
809     tbp->chairman_len = swap_16(tbp->chairman_len);
810     tbp->public_flag = swap_16(tbp->public_flag);
811}
812
813swap_trn(thp)
814trn_hdr *thp;
815{
816     thp->version = swap_32(thp->version);
817     thp->unique = swap_32(thp->unique);
818     thp->current = swap_32(thp->current);
819     thp->orig_pref = swap_32(thp->orig_pref);
820     thp->date_entered = swap_32(thp->date_entered);
821     thp->num_lines = swap_32(thp->num_lines);
822     thp->num_chars = swap_32(thp->num_chars);
823     thp->prev_trn = swap_32(thp->prev_trn);
824     thp->subject_addr = swap_32(thp->subject_addr);
825     thp->author_addr = swap_32(thp->author_addr);
826     thp->text_addr = swap_32(thp->text_addr);
827     thp->subject_len = swap_16(thp->subject_len);
828     thp->author_len = swap_16(thp->author_len);
829}
Note: See TracBrowser for help on using the repository browser.