text.c 72.9 KB
Newer Older
Linus Tolke's avatar
Linus Tolke committed
1
/*
2
 * $Id: text.c,v 0.111 2003/08/16 15:32:33 ceder Exp $
Per Cederqvist's avatar
Per Cederqvist committed
3
 * Copyright (C) 1991-2002  Lysator Academic Computer Association.
Linus Tolke's avatar
Linus Tolke committed
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 *
 * This file is part of the LysKOM server.
 * 
 * LysKOM is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by 
 * the Free Software Foundation; either version 1, or (at your option) 
 * any later version.
 * 
 * LysKOM is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with LysKOM; see the file COPYING.  If not, write to
 * Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
 * or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
 * MA 02139, USA.
 *
Per Cederqvist's avatar
Per Cederqvist committed
23
 * Please report bugs at http://bugzilla.lysator.liu.se/. 
Linus Tolke's avatar
Linus Tolke committed
24
 */
Per Cederqvist's avatar
Per Cederqvist committed
25
26
27
28
29
/*
 * text.c
 *
 * All atomic calls that deals with texts.
 */
Per Cederqvist's avatar
Per Cederqvist committed
30

David Byers's avatar
David Byers committed
31
32
33
34
35

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

36
#include <assert.h>
Per Cederqvist's avatar
Per Cederqvist committed
37
38
#include <errno.h>
#include <stdio.h>
39
#include "timewrap.h"
Per Cederqvist's avatar
Per Cederqvist committed
40
#include <setjmp.h>
Per Cederqvist's avatar
Per Cederqvist committed
41
#include <sys/types.h>
Per Cederqvist's avatar
Per Cederqvist committed
42
#include <sys/types.h>
Per Cederqvist's avatar
Per Cederqvist committed
43

44
#include "aux.h"
Per Cederqvist's avatar
Per Cederqvist committed
45
#include "server/smalloc.h"
46
47
#include "s-string.h"
#include "misc-types.h"
Per Cederqvist's avatar
Per Cederqvist committed
48
49
#include "kom-types.h"
#include "services.h"
David Byers's avatar
Server    
David Byers committed
50
51
52
#include "com.h"
#include "async.h"
#include "connections.h"
53
#include "kom-errno.h"
Per Cederqvist's avatar
Per Cederqvist committed
54
#include "manipulate.h"
Per Cederqvist's avatar
Per Cederqvist committed
55
#include "lyskomd.h"
David Byers's avatar
David Byers committed
56
#include "kom-config.h"
Per Cederqvist's avatar
Per Cederqvist committed
57
#include "internal-connections.h"
Per Cederqvist's avatar
Per Cederqvist committed
58
59
60
61
#include "cache.h"
#include "log.h"
#include "minmax.h"
#include "admin.h"
Per Cederqvist's avatar
Per Cederqvist committed
62
#include "send-async.h"
63
#include "param.h"
Per Cederqvist's avatar
Per Cederqvist committed
64
#include "kom-memory.h"
65
#include "aux-items.h"
66
#include "local-to-global.h"
67
#include "server-time.h"
68
#include "text.h"
69
#include "string-malloc.h"
70
#include "stats.h"
Per Cederqvist's avatar
Per Cederqvist committed
71

72
73
74
75
76
77

/*
 * Forward declarations
 */

static Text_no
Per Cederqvist's avatar
Per Cederqvist committed
78
79
80
81
82
83
do_create_text(const String   message,
               u_short	      no_of_misc,
               Misc_info     *misc,
               Aux_item_list *aux,
               Bool           anonymous,
               Text_stat    **ret_stat);
84

85
86
87
88
89
static void
filter_secret_info(Text_stat *result,
		   const Text_stat *original,
                   const Connection *viewer_conn,
                   Bool output_bcc);
Per Cederqvist's avatar
Per Cederqvist committed
90

91
92
static Bool
interested_party(const Connection *cptr,
93
		 Text_no tno,
94
		 const Text_stat *text_s);
Per Cederqvist's avatar
Per Cederqvist committed
95
96
97
98
99
100
101
102
103
104
105
106
107
108
/*
 * Static functions
 */


/*
 * Add text_no to the list of texts in a conference. Return the local number
 * the text gets in this conference.
 */

static Local_text_no
add_text_in_conf(Conference * conf_c,
		 Text_no      text_no)
{
109
    Local_text_no res;
Per Cederqvist's avatar
Per Cederqvist committed
110

111
    conf_c->last_written = current_time.tv_sec;
Per Cederqvist's avatar
Per Cederqvist committed
112
113
    
    /* Add number last on the text_list */
114
115
116
    res = l2g_first_appendable_key(&conf_c->texts);
    l2g_append(&conf_c->texts, res, text_no);
    return res;
Per Cederqvist's avatar
Per Cederqvist committed
117
118
119
120
121
122
123
124
}


/*
 * Count how many recipients and cc_recipients a text has.
 */

static int
Per Cederqvist's avatar
Per Cederqvist committed
125
count_recipients(const Text_stat  *t_stat)
Per Cederqvist's avatar
Per Cederqvist committed
126
127
{
    int 	 n = 0;
Per Cederqvist's avatar
Per Cederqvist committed
128
    Misc_info  * end = t_stat->misc_items + t_stat->no_of_misc;
Per Cederqvist's avatar
Per Cederqvist committed
129
130
    Misc_info  * misc;

Per Cederqvist's avatar
Per Cederqvist committed
131
132
133
134
    for (misc = t_stat->misc_items; misc < end; misc++)
	if (misc->type == recpt
	    || misc->type == cc_recpt
	    || misc->type == bcc_recpt)
Per Cederqvist's avatar
Per Cederqvist committed
135
136
137
138
139
140
141
142
143
144
145
146
	{
	    n++;
	}

    return n;
}

/*
 * Count how many footnotes a text has.
 */

static int
Per Cederqvist's avatar
Per Cederqvist committed
147
count_footn(const Text_stat *t_stat)
Per Cederqvist's avatar
Per Cederqvist committed
148
149
{
    int 	 n = 0;
Per Cederqvist's avatar
Per Cederqvist committed
150
    Misc_info  * end = t_stat->misc_items + t_stat->no_of_misc;
Per Cederqvist's avatar
Per Cederqvist committed
151
152
    Misc_info  * misc;

Per Cederqvist's avatar
Per Cederqvist committed
153
    for (misc = t_stat->misc_items; misc < end; misc++)
Per Cederqvist's avatar
Per Cederqvist committed
154
155
156
157
158
159
160
161
162
163
164
	if ( misc->type == footn_in )
	    n++;

    return n;
}

/*
 * Count how many commments a text has.
 */

static int
Per Cederqvist's avatar
Per Cederqvist committed
165
count_comment(const Text_stat *t_stat)
Per Cederqvist's avatar
Per Cederqvist committed
166
167
{
    int 	 n = 0;
Per Cederqvist's avatar
Per Cederqvist committed
168
    Misc_info  * end = t_stat->misc_items + t_stat->no_of_misc;
Per Cederqvist's avatar
Per Cederqvist committed
169
170
    Misc_info  * misc;

Per Cederqvist's avatar
Per Cederqvist committed
171
    for (misc = t_stat->misc_items; misc < end; misc++)
Per Cederqvist's avatar
Per Cederqvist committed
172
173
174
175
176
177
178
179
180
181
182
	if ( misc->type == comm_in )
	    n++;

    return n;
}


/*
 * Check if CONF_NO is a recipient of the text whose text_stat is given.
 */

Per Cederqvist's avatar
Per Cederqvist committed
183
static int
Per Cederqvist's avatar
Per Cederqvist committed
184
185
find_recipient(Conf_no	        conf_no,
	       const Text_stat *t_stat)
Per Cederqvist's avatar
Per Cederqvist committed
186
187
188
{
    int i;
    
Per Cederqvist's avatar
Per Cederqvist committed
189
    for (i = 0; i < t_stat->no_of_misc; i++)
Per Cederqvist's avatar
Per Cederqvist committed
190
    {
Per Cederqvist's avatar
Per Cederqvist committed
191
	switch (t_stat->misc_items[i].type)
Per Cederqvist's avatar
Per Cederqvist committed
192
193
	{
	case recpt:
194
195
	case cc_recpt:
	case bcc_recpt:
Per Cederqvist's avatar
Per Cederqvist committed
196
	    if (t_stat->misc_items[i].datum.recipient == conf_no)
197
		return i;
Per Cederqvist's avatar
Per Cederqvist committed
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
	    break;
	    
	case rec_time:
	case comm_to:
	case comm_in:
	case footn_to:
	case footn_in:
	case sent_by:
	case sent_at:
	case loc_no:
	    break;
	    
#ifndef COMPILE_CHECKS
	default:
#endif
213
214
        case unknown_info:
	    restart_kom("find_recipient(): illegal misc_item\n");
Per Cederqvist's avatar
Per Cederqvist committed
215
216
217
	}
    }

218
    return -1;
Per Cederqvist's avatar
Per Cederqvist committed
219
220
221
222
223
224
225
}


/*
 * Check if comment is a comment to parent.
 */
static Bool
Per Cederqvist's avatar
Per Cederqvist committed
226
227
is_comment_to(Text_no          comment,
	      const Text_stat *parent)
Per Cederqvist's avatar
Per Cederqvist committed
228
229
230
{
    int i;

Per Cederqvist's avatar
Per Cederqvist committed
231
    for (i = 0; i < parent->no_of_misc; i++)
Per Cederqvist's avatar
Per Cederqvist committed
232
    {
Per Cederqvist's avatar
Per Cederqvist committed
233
	switch (parent->misc_items[i].type)
Per Cederqvist's avatar
Per Cederqvist committed
234
235
	{
	case comm_in:
236
	    if (parent->misc_items[i].datum.text_link == comment)
Per Cederqvist's avatar
Per Cederqvist committed
237
238
239
240
241
242
243
244
245
246
247
248
249
250
		return TRUE;
	    break;
	default:
	    break;
	}
    }

    return FALSE;
}

/*
 * Check if footnote is a footnote to parent.
 */
static Bool
Per Cederqvist's avatar
Per Cederqvist committed
251
252
is_footnote_to(Text_no          footnote,
	       const Text_stat *parent)
Per Cederqvist's avatar
Per Cederqvist committed
253
254
255
{
    int i;

Per Cederqvist's avatar
Per Cederqvist committed
256
    for (i = 0; i < parent->no_of_misc; i++)
Per Cederqvist's avatar
Per Cederqvist committed
257
    {
Per Cederqvist's avatar
Per Cederqvist committed
258
	switch (parent->misc_items[i].type)
Per Cederqvist's avatar
Per Cederqvist committed
259
260
	{
	case footn_in:
261
	    if (parent->misc_items[i].datum.text_link == footnote)
Per Cederqvist's avatar
Per Cederqvist committed
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
		return TRUE;
	    break;
	default:
	    break;
	}
    }

    return FALSE;
}
/*
 * Return the conference which the text goes to. This is normally conf_no, but
 * it may be a super_conf. If ACTPERS is not allowed to submit a text to
 * conf_no or a super_conf, return 0.
 */
static Conf_no
submit_to(Conf_no	conf_no, /* The conference the user is trying to */
	  			 /* submit a text to. */
Per Cederqvist's avatar
Per Cederqvist committed
279
	  const Conference  *conf_c)	 /* May be NULL */
Per Cederqvist's avatar
Per Cederqvist committed
280
281
{
    int i;
282
    enum access acc;
Per Cederqvist's avatar
Per Cederqvist committed
283
    
David Byers's avatar
Server    
David Byers committed
284
    CHK_CONNECTION(0);
Per Cederqvist's avatar
Per Cederqvist committed
285
286
    CHK_LOGIN(0);
    
Per Cederqvist's avatar
Per Cederqvist committed
287
    if (conf_c == NULL)
Per Cederqvist's avatar
Per Cederqvist committed
288
289
	GET_C_STAT(conf_c, conf_no, 0);

Per Cederqvist's avatar
Per Cederqvist committed
290
    for (i=0; i < param.max_super_conf_loop; i++)
Per Cederqvist's avatar
Per Cederqvist committed
291
    {
292
	acc = access_perm(conf_no, active_connection, unlimited);
Per Cederqvist's avatar
Per Cederqvist committed
293
	
Per Cederqvist's avatar
Per Cederqvist committed
294
	if (acc <= none)
David Byers's avatar
David Byers committed
295
296
297
        {
            err_stat = conf_no;
            kom_errno = KOM_UNDEF_CONF;
Per Cederqvist's avatar
Per Cederqvist committed
298
	    return 0;
David Byers's avatar
David Byers committed
299
        }
Per Cederqvist's avatar
Per Cederqvist committed
300
301
302
	
	if (conf_c->permitted_submitters == 0
	    || acc == unlimited
Per Cederqvist's avatar
Per Cederqvist committed
303
	    || locate_membership(conf_c->permitted_submitters, ACT_P) != NULL)
Per Cederqvist's avatar
Per Cederqvist committed
304
305
306
307
308
	{
	    return conf_no;
	}


David Byers's avatar
David Byers committed
309
310
311
312
	if (conf_c->super_conf == 0)
        {
            err_stat = conf_no;
            kom_errno = KOM_ACCESS;
Per Cederqvist's avatar
Per Cederqvist committed
313
	    return 0;
David Byers's avatar
David Byers committed
314
315
        }
        conf_no = conf_c->super_conf;
Per Cederqvist's avatar
Per Cederqvist committed
316
317
318
319
	
	GET_C_STAT(conf_c, conf_no, 0);
    }

David Byers's avatar
David Byers committed
320
321
    err_stat = conf_no;
    kom_errno = KOM_ACCESS;
Per Cederqvist's avatar
Per Cederqvist committed
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
    return 0;
}


/*
 * Say that FOOTNOTE is a footnote to TEXT.
 */

static Success
do_add_footnote(Text_no footnote,
		Text_no text)
{
    Text_stat *foot_s, *text_s;

    GET_T_STAT(foot_s, footnote, FAILURE);
    GET_T_STAT(text_s, text, FAILURE);

339
340
    ADD_MISC(foot_s, footn_to, text_link, text);
    ADD_MISC(text_s, footn_in, text_link, footnote);
Per Cederqvist's avatar
Per Cederqvist committed
341

Per Cederqvist's avatar
Per Cederqvist committed
342
343
    mark_text_as_changed(footnote);
    mark_text_as_changed(text);
Per Cederqvist's avatar
Per Cederqvist committed
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
    
    return OK;
}


/*
 * Say that COMMENT is a comment to TEXT.
 */

static Success
do_add_comment(Text_no comment,
	       Text_no text)
{
    Text_stat *comm_s, *text_s;
    
    GET_T_STAT(comm_s, comment, FAILURE);
    GET_T_STAT(text_s, text, FAILURE);

362
363
    ADD_MISC(comm_s, comm_to, text_link, text);
    ADD_MISC(text_s, comm_in, text_link, comment);
Per Cederqvist's avatar
Per Cederqvist committed
364

Per Cederqvist's avatar
Per Cederqvist committed
365
366
    mark_text_as_changed(comment);
    mark_text_as_changed(text);
Per Cederqvist's avatar
Per Cederqvist committed
367
368
369
370
371
372
373
374
375
    
    return OK;
}

    
/*
 * Say that RECEIVER is a recipient of TEXT.
 */
static Success
376
377
378
379
do_add_recipient(Text_no        text,
		 Text_stat    * text_s, /* May be NULL */
		 Conf_no        receiver,
		 enum info_type recv_type)
Per Cederqvist's avatar
Per Cederqvist committed
380
381
382
{
    Conference *rece_c;

383
384
385
386
    assert(recv_type == recpt
	   || recv_type == cc_recpt
	   || recv_type == bcc_recpt);

Per Cederqvist's avatar
Per Cederqvist committed
387
    if (text_s == NULL)
Per Cederqvist's avatar
Per Cederqvist committed
388
389
390
391
392
393
    {
	GET_T_STAT(text_s, text, FAILURE);
    }
    
    GET_C_STAT(rece_c, receiver, FAILURE);

394
    ADD_MISC(text_s, recv_type, recipient, receiver);
Per Cederqvist's avatar
Per Cederqvist committed
395
396
397
398
399
400
401
402
    ADD_MISC(text_s, loc_no, local_no, add_text_in_conf(rece_c, text));

    mark_text_as_changed(text);
    mark_conference_as_changed(receiver);
    
    return OK;
}

403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
static Bool
is_member_in(const Person *person,
	     Conf_no conf_no,
	     Bool skip_passive)
{
    Membership *mship;

    if ((mship = locate_membership(conf_no, person)) != NULL)
    {
	if (!(mship->type.passive && skip_passive))
	    return TRUE;
    }
    return FALSE;
}

David Byers's avatar
David Byers committed
418
419
420


/*
421
422
423
 * Check if person is a member in any of the recipients,
 * cc_recipients or bcc_recipients of text_s.  If skip_passive is
 * true, ignore passive memberships.
David Byers's avatar
David Byers committed
424
425
 */
static  Bool
Per Cederqvist's avatar
Per Cederqvist committed
426
427
428
is_member_in_recpt(const Person    *person,
		   const Text_stat *text_s,
                   Bool             skip_passive)
David Byers's avatar
David Byers committed
429
430
431
{
    int i;

Per Cederqvist's avatar
Per Cederqvist committed
432
    for (i = 0; i < text_s->no_of_misc; i++)
David Byers's avatar
David Byers committed
433
    {
Per Cederqvist's avatar
Per Cederqvist committed
434
	switch(text_s->misc_items[i].type)
David Byers's avatar
David Byers committed
435
436
437
438
	{
	case recpt:
	case cc_recpt:
	case bcc_recpt:
439
440
441
	    if (is_member_in(person, text_s->misc_items[i].datum.recipient,
			     skip_passive) == TRUE)
		return TRUE;
David Byers's avatar
David Byers committed
442
443
444
445
446
447
448
449
450
451
452
453
454
455
	    break;

	case comm_to:
	case comm_in:
	case footn_to:
	case footn_in:
	case loc_no:
	case rec_time:
	case sent_by:
	case sent_at:
	    break;

#ifndef COMPILE_CHECKS
	default:
456
457
#endif
        case unknown_info:
458
	    kom_log("is_member_in_recpt(): bad misc_item.\n");
David Byers's avatar
David Byers committed
459
460
461
462
463
464
465
	    break;
	}
    }

    return FALSE;
}

466
467
468
469
470
471
472
473
474
475
476
477
static  Bool
is_member_in_recpt_of(const Person  *person,
		      const Text_no  text_no,
		      Bool           skip_passive)
{
    Text_stat *text_s;

    GET_T_STAT(text_s, text_no, FALSE);
    return is_member_in_recpt(person, text_s, skip_passive);
}


478
479
480
481
482
483
484
static void
report_bad_aux(Text_no tno,
	       String *data,
	       int tag,
	       const char *reason)
{
    static Text_no last = 0;
485
    char *d;
486
487
488
489
490

    if (tno == last)
	return;

    last = tno;
491
    d= s_crea_c_str(*data);
492
493
494
495
496
497
    kom_log("Bad aux-item %d found in text %ld: \"%s\": %s.\n",
	    tag, (unsigned long)tno, d, reason);
    string_free(d);
}


498
499
500
501
502
503
504
/*
 * Check if person is a member in any of the recipients of a text that
 * is linked to this text.  If skip_passive is true, ignore passive
 * memberships.
 */
static  Bool
is_member_in_linked_recpt(const Person    *person,
505
			  Text_no          tno,
506
507
508
509
510
511
			  const Text_stat *text_s,
			  Bool             skip_passive)
{
    int i;
    Text_no   linked_nr;
    String *data;
512
#if 0
513
    String copy;
514
#endif
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
    String_size end;

    for (i = 0; i < text_s->no_of_misc; i++)
    {
	switch(text_s->misc_items[i].type)
	{
	case comm_to:
	case comm_in:
	case footn_to:
	case footn_in:
	    linked_nr = text_s->misc_items[i].datum.text_link;
	    if (is_member_in_recpt_of(person, linked_nr, skip_passive) == TRUE)
		return TRUE;
	    break;

	case recpt:
	case cc_recpt:
	case bcc_recpt:
	case loc_no:
	case rec_time:
	case sent_by:
	case sent_at:
	    break;

#ifndef COMPILE_CHECKS
	default:
#endif
        case unknown_info:
	    kom_log("is_member_in_linked_recpt(): bad misc_item.\n");
	    break;
	}
    }

    for (i = 0; i < text_s->aux_item_list.length; i++)
    {
	data = &text_s->aux_item_list.items[i].data;
	switch (text_s->aux_item_list.items[i].tag)
	{
	case aux_cross_reference:
554
555
556
	    /* FIXME (bug 23): Until there is a mirroring aux-item,
	        there is no point doing this. */
#if 0
557
558
	    /* Data is "T", optional space, a text number, a space, and
	       some junk we don't care about. */
559
560
561
562
563
564
	    if (s_strlen(*data) < 2)
	    {
		report_bad_aux(tno, data, "cross-reference", "too short");
		continue;
	    }
	    if (data->string[0] != 'T')
565
566
567
568
		continue;
	    copy = s_fsubstr(*data, 1, END_OF_STRING);
	    linked_nr = s_strtol(copy, &end);
	    if (end == -1)
569
570
571
	    {
		report_bad_aux(tno, data, "cross-reference",
			       "no number found");
572
		continue;
573
574
575
576
577
	    }
	    if (end < s_strlen(copy) && copy.string[end] != ' ')
	    {
		report_bad_aux(tno, data, "cross-reference",
			       "trailing garbage");
578
		continue;
579
	    }
580
581
582
583

	    if (is_member_in_recpt_of(person, linked_nr, skip_passive) == TRUE)
		return TRUE;

584
#endif
585
	    break;
586

587
588
589
590
591
592
	case aux_mx_mime_belongs_to:
	case aux_mx_mime_part_in:
	    /* Data is a single text number. */

	    linked_nr = s_strtol(*data, &end);
	    if (end == -1)
593
594
595
	    {
		report_bad_aux(tno, data, text_s->aux_item_list.items[i].tag,
			       "bad number");
596
		continue;
597
	    }
598
	    if (end != s_strlen(*data))
599
600
601
	    {
		report_bad_aux(tno, data, text_s->aux_item_list.items[i].tag,
			       "trailing garbage");
602
		continue;
603
	    }
604
605
606

	    if (is_member_in_recpt_of(person, linked_nr, skip_passive) == TRUE)
		return TRUE;
607
	    break;
608
609
610
611
612
613

	case aux_faq_for_conf:
	    /* Data is a conference number.  0 means the entire system.  */
	    linked_nr = s_strtol(*data, &end);
	    if (end == -1)
		continue;
614
	    if (end != s_strlen(*data))
615
616
617
618
619
		continue;

	    if (linked_nr == 0
		|| is_member_in(person, linked_nr, skip_passive) == TRUE)
		return TRUE;
620
	    break;
621
622
623
624
625
626
627
628
629

	default:
	    break;
	}
    }
    
    return FALSE;
}

David Byers's avatar
David Byers committed
630

Per Cederqvist's avatar
Per Cederqvist committed
631
632
633
634
635
/*
 * Return number of lines in a text
 */

static u_short
Per Cederqvist's avatar
Per Cederqvist committed
636
count_lines(String str)
Per Cederqvist's avatar
Per Cederqvist committed
637
638
639
{
    u_short l = 0;

Per Cederqvist's avatar
Per Cederqvist committed
640
641
    while (str.len-- > 0)
	if(*str.string++ == '\n')
Per Cederqvist's avatar
Per Cederqvist committed
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
	    l++;

    return l;
}


/*
 * Delete misc_info at location loc.
 * If it is a recpt, cc_recpt, comm_to or footn_to delete any
 * loc_no, rec_time, sent_by or sent_at that might follow it.
 *
 * Note that the Misc_info is not reallocated.
 */

static void
Per Cederqvist's avatar
Per Cederqvist committed
657
658
659
do_delete_misc(u_short	 * no_of_misc,
	       Misc_info * misc,
	       int	   loc)
Per Cederqvist's avatar
Per Cederqvist committed
660
661
662
{
    int del = 1;		/* Number of items to delete. */
    				/* Always delete at least one item. */
663
    Bool ready = FALSE;
Per Cederqvist's avatar
Per Cederqvist committed
664
665
666
    
    /* Check range of loc */

Per Cederqvist's avatar
Per Cederqvist committed
667
    if (loc < 0 || loc >= *no_of_misc)
Per Cederqvist's avatar
Per Cederqvist committed
668
669
	restart_kom("do_delete_misc() - loc out of range");
    
Per Cederqvist's avatar
Per Cederqvist committed
670
    while (ready == FALSE && loc + del < *no_of_misc)
Per Cederqvist's avatar
Per Cederqvist committed
671
    {
Per Cederqvist's avatar
Per Cederqvist committed
672
	switch (misc[loc+del].type)
Per Cederqvist's avatar
Per Cederqvist committed
673
674
675
676
677
678
679
680
681
682
	{
	case loc_no:
	case rec_time:
	case sent_by:
	case sent_at:
	    del++;
	    break;

	case recpt:
	case cc_recpt:
683
        case bcc_recpt:
Per Cederqvist's avatar
Per Cederqvist committed
684
685
686
687
688
689
690
691
692
693
	case footn_to:
	case footn_in:
	case comm_to:
	case comm_in:
	    ready = TRUE;
	    break;
	    
#ifndef COMPILE_CHECKS
	default:
#endif
694
695
        case unknown_info:
	    restart_kom("do_delete_misc() - illegal misc");
Per Cederqvist's avatar
Per Cederqvist committed
696
697
698
699
700
701
702
	}
    }

    *no_of_misc -= del;

    /* Move items beyond the deleted ones. */

Per Cederqvist's avatar
Per Cederqvist committed
703
    while (loc < *no_of_misc)
Per Cederqvist's avatar
Per Cederqvist committed
704
    {
Per Cederqvist's avatar
Per Cederqvist committed
705
	misc[loc] = misc[loc+del];
Per Cederqvist's avatar
Per Cederqvist committed
706
707
708
709
	loc++;
    }
}

David Byers's avatar
David Byers committed
710
711

static void
Per Cederqvist's avatar
Per Cederqvist committed
712
713
714
send_async_sub_recipient(Text_no          text_no,
			 const Text_stat *text_s,
			 Conf_no          conf_no,
715
			 enum info_type   type)
David Byers's avatar
David Byers committed
716
717
718
719
{
    Connection *cptr;
    Session_no i = 0;

720
    while ((i = traverse_connections(i)) != 0)
721
    {
722
	cptr = get_conn_by_number(i);
723
	if (interested_party(cptr, text_no, text_s)
724
	    && text_read_access(cptr, text_no, text_s))
725
726
727
728
	{
	    Text_stat copy;

	    filter_secret_info(&copy, text_s, cptr, TRUE);
729
	    if (find_recipient(conf_no, &copy) != -1)
730
731
732
		async_sub_recipient(cptr, text_no, conf_no, type);
	}
    }
David Byers's avatar
David Byers committed
733
734
}

735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
/*
 * Delete the recipient ``conf_no'' from text ``text_no''.
 * The misc-item is locate at position ``loc'' in the miscinfolist.
 * ``text_s'' and ``conf_s'' must be supplied.  An async-sub-recipient
 * message will be sent if ``want_async'' is true.
 */
static Success
perform_subtraction(Text_no     text_no,
		    Text_stat  *text_s,
		    Conf_no     conf_no,
		    Conference *conf_s, /* NULL if the conf_no doesn't exist */
		    int         loc,
		    Bool        want_async)
{
    if (conf_s != NULL)
    {
	/* Only if the conference exists: */
	if (text_s->misc_items[loc+1].type == loc_no)
	{
	    l2g_delete(&conf_s->texts,
		       text_s->misc_items[loc+1].datum.local_no);
Per Cederqvist's avatar
Per Cederqvist committed
756
	    mark_conference_as_changed(conf_no);
757
758
759
760
761
762
763
764
765
766
767
768
769
	}
	else
	{
	    kom_log("Bad misc-info-list detected in perform_subtraction()"
		    " for text number %lu.\n", (unsigned long)text_no);
	}
    }

    if (want_async)
	send_async_sub_recipient(text_no, text_s, conf_no,
				 text_s->misc_items[loc].type);

    do_delete_misc(&text_s->no_of_misc, text_s->misc_items, loc);
Per Cederqvist's avatar
Per Cederqvist committed
770
    mark_text_as_changed(text_no);
771
772
773
774

    return OK;
}

Per Cederqvist's avatar
Per Cederqvist committed
775
776
/*
 * Delete a recipient from a text. Does not fail if the recipient doesn't
777
778
 * exist - that is not an error.  (The recipients of a text are not
 * removed when a conference is deleted.)
Per Cederqvist's avatar
Per Cederqvist committed
779
780
 */
static Success
Per Cederqvist's avatar
Per Cederqvist committed
781
782
783
784
785
do_sub_recpt(Text_no	    text_no,
	     Text_stat    * text_s,  /* May be NULL */
	     Conf_no        conf_no,
	     Conference   * conf_s,  /* May be NULL */
	     Bool           want_async)
Per Cederqvist's avatar
Per Cederqvist committed
786
787
788
{
    int i;

789
    if (text_s == NULL)
Per Cederqvist's avatar
Per Cederqvist committed
790
791
	GET_T_STAT(text_s, text_no, FAILURE);
    
792
    if (conf_s == NULL)
Per Cederqvist's avatar
Per Cederqvist committed
793
	conf_s = cached_get_conf_stat(conf_no); /* Might still be NULL. */
Per Cederqvist's avatar
Per Cederqvist committed
794
    	
795
    for (i = 0; i < text_s->no_of_misc; i++)
Per Cederqvist's avatar
Per Cederqvist committed
796
    {
797
	switch (text_s->misc_items[i].type)
Per Cederqvist's avatar
Per Cederqvist committed
798
799
800
	{
	case recpt:
	case cc_recpt:
801
	case bcc_recpt:
802
	    if (text_s->misc_items[i].datum.recipient == conf_no)
803
804
		return perform_subtraction(text_no, text_s, conf_no, conf_s,
					   i, want_async);
805
806
	    break;

Per Cederqvist's avatar
Per Cederqvist committed
807
808
809
810
811
812
813
814
815
816
817
818
	case comm_to:
	case comm_in:
	case footn_to:
	case footn_in:
	case loc_no:
	case rec_time:
	case sent_by:
	case sent_at:
	    break;
	    
#ifndef COMPILE_CHECKS
	default:
819
820
#endif
        case unknown_info:
821
	    kom_log("do_sub_recpt(): bad misc_item.\n");
Per Cederqvist's avatar
Per Cederqvist committed
822
823
824
825
	    break;
	}
    }

826
    err_stat = conf_no;
Per Cederqvist's avatar
Per Cederqvist committed
827
828
829
830
    kom_errno = KOM_NOT_RECIPIENT;
    return FAILURE;
}

831
832
833
834

/* OTHER is a INFO_TYPE to text number TEXT (whose text status is
   TEXT_S).  This function removes that misc-info from TEXT_S.  */
static void
835
836
837
838
remove_misc_item(Text_no        text,
		 Text_stat     *text_s,
		 enum info_type info_type,
		 Text_no        other)
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
{
    int i;

    assert(info_type == comm_to || info_type == comm_in
	   || info_type == footn_to || info_type == footn_in);

    for (i = 0; i < text_s->no_of_misc; i++)
    {
	if (text_s->misc_items[i].type == info_type
	    && text_s->misc_items[i].datum.text_link == other)
	{
	    do_delete_misc(&text_s->no_of_misc, text_s->misc_items, i);
	    mark_text_as_changed(text);
	    return;
	}
    }

    restart_kom("remove_misc_item() failed.\n");
}

Per Cederqvist's avatar
Per Cederqvist committed
859
860
861
862
/*
 * Delete the link between comment and comment_to.
 */
static void
Per Cederqvist's avatar
Per Cederqvist committed
863
864
865
866
do_sub_comment(Text_no	      comment,    /* The comment. */
	       Text_stat    * text_s,
	       Text_no	      comment_to, /* The commented. */
	       Text_stat    * parent_s )
Per Cederqvist's avatar
Per Cederqvist committed
867
{
Per Cederqvist's avatar
Per Cederqvist committed
868
    if (text_s == NULL)
Per Cederqvist's avatar
Per Cederqvist committed
869
	VOID_GET_T_STAT(text_s, comment);
Per Cederqvist's avatar
Per Cederqvist committed
870

Per Cederqvist's avatar
Per Cederqvist committed
871
    if (parent_s == NULL)
Per Cederqvist's avatar
Per Cederqvist committed
872
	VOID_GET_T_STAT(parent_s, comment_to);
Per Cederqvist's avatar
Per Cederqvist committed
873
    
874
875
    remove_misc_item(comment, text_s, comm_to, comment_to);
    remove_misc_item(comment_to, parent_s, comm_in, comment);
Per Cederqvist's avatar
Per Cederqvist committed
876
877
878
879
880
881
882
}


/*
 * Delete the link between footnote and footnote_to.
 */
static void
883
do_sub_footnote(Text_no        footnote,
Per Cederqvist's avatar
Per Cederqvist committed
884
885
		Text_stat    * text_s,
		Text_no	       footnote_to,
Per Cederqvist's avatar
Per Cederqvist committed
886
		Text_stat    * parent_s)
Per Cederqvist's avatar
Per Cederqvist committed
887
{
Per Cederqvist's avatar
Per Cederqvist committed
888
    if (text_s == NULL)
Per Cederqvist's avatar
Per Cederqvist committed
889
	VOID_GET_T_STAT(text_s, footnote);
Per Cederqvist's avatar
Per Cederqvist committed
890

Per Cederqvist's avatar
Per Cederqvist committed
891
    if (parent_s == NULL)
Per Cederqvist's avatar
Per Cederqvist committed
892
	VOID_GET_T_STAT(parent_s, footnote_to);
Per Cederqvist's avatar
Per Cederqvist committed
893

894
895
    remove_misc_item(footnote, text_s, footn_to, footnote_to);
    remove_misc_item(footnote_to, parent_s, footn_in, footnote);
Per Cederqvist's avatar
Per Cederqvist committed
896
897
898
899
900
901
902
}


/*
 * Who is sender of misc_item I? Returns 0 if there is no sent_by misc_item.
 */
static Pers_no
Per Cederqvist's avatar
Per Cederqvist committed
903
904
sender(const Text_stat *t_stat,
       int i)
Per Cederqvist's avatar
Per Cederqvist committed
905
906
907
{
    for (i++ ; i < t_stat->no_of_misc; i++)
    {
Per Cederqvist's avatar
Per Cederqvist committed
908
	switch (t_stat->misc_items[i].type)
Per Cederqvist's avatar
Per Cederqvist committed
909
910
	{
	case sent_by:
Per Cederqvist's avatar
Per Cederqvist committed
911
	    return t_stat->misc_items[i].datum.sender;
Per Cederqvist's avatar
Per Cederqvist committed
912
913
914

	case recpt:
	case cc_recpt:
915
        case bcc_recpt:
Per Cederqvist's avatar
Per Cederqvist committed
916
917
918
919
	case comm_to:
	case comm_in:
	case footn_to:
	case footn_in:
Per Cederqvist's avatar
Per Cederqvist committed
920
	case sent_at:
Per Cederqvist's avatar
Per Cederqvist committed
921
922
923
924
925
926
927
928
929
	    return 0;		/* No sender. */

	case loc_no:
	case rec_time:
	    break;		/* These may come before a sent_by. */

#ifndef COMPILE_CHECKS
	default:
#endif
930
931
        case unknown_info:
	    restart_kom("ERROR: sender(): Illegal misc_item found.\n");
Per Cederqvist's avatar
Per Cederqvist committed
932
933
934
935
936
937
938
939
940
941
	}
    }
    return 0;			/* No sender. */
}


/*
 * Check if ACTPERS has sent this text to conference CONF_NO
 */
static Bool
Per Cederqvist's avatar
Per Cederqvist committed
942
943
is_sender(const Text_stat *text_s,
	  Conf_no conf_no)
Per Cederqvist's avatar
Per Cederqvist committed
944
945
946
{
    int i;

Per Cederqvist's avatar
Per Cederqvist committed
947
    if (!ACTPERS)
Per Cederqvist's avatar
Per Cederqvist committed
948
949
	return FALSE;
    
Per Cederqvist's avatar
Per Cederqvist committed
950
    for (i = 0; i < text_s->no_of_misc; i++)
Per Cederqvist's avatar
Per Cederqvist committed
951
    {
Per Cederqvist's avatar
Per Cederqvist committed
952
	switch (text_s->misc_items[i].type)
Per Cederqvist's avatar
Per Cederqvist committed
953
954
955
	{
	case recpt:
	case cc_recpt:
956
	case bcc_recpt:
957
	    if (text_s->misc_items[i].datum.recipient == conf_no
Per Cederqvist's avatar
Per Cederqvist committed
958
		&& sender(text_s, i) == ACTPERS)
959
960
961
962
963
	    {
		return TRUE;
	    }
	    break;

Per Cederqvist's avatar
Per Cederqvist committed
964
965
966
967
968
969
970
971
972
973
974
975
976
	case loc_no:
	case rec_time:
	case comm_to:
	case comm_in:
	case footn_to:
	case footn_in:
	case sent_by:
	case sent_at:
	    break;

#ifndef COMPILE_CHECKS
	default:
#endif
977
978
        case unknown_info:
	    restart_kom("is_sender(): Illegal misc_item found.\n");
Per Cederqvist's avatar
Per Cederqvist committed
979
980
981
982
983
984
985
986
987
988
989
	}
    }

    return FALSE;
}


/*
 * Check if ACTPERS has sent this text as a comment to parent.
 */
static Bool
Per Cederqvist's avatar
Per Cederqvist committed
990
991
is_comm_sender(const Text_stat *text_s,
	       Text_no parent)
Per Cederqvist's avatar
Per Cederqvist committed
992
993
994
{
    int i;

Per Cederqvist's avatar
Per Cederqvist committed
995
996
997
998
    if (!ACTPERS)
 	return FALSE;
     
    for (i = 0; i < text_s->no_of_misc; i++)
Per Cederqvist's avatar
Per Cederqvist committed
999
    {
Per Cederqvist's avatar
Per Cederqvist committed
1000
	switch (text_s->misc_items[i].type)
Per Cederqvist's avatar
Per Cederqvist committed
1001
1002
	{
	case comm_to:
1003
	    if (text_s->misc_items[i].datum.text_link == parent
Per Cederqvist's avatar
Per Cederqvist committed
1004
		&& sender(text_s, i) == ACTPERS)
Per Cederqvist's avatar
Per Cederqvist committed
1005
1006
1007
1008
1009
1010
1011
1012
	    {
		return TRUE;
	    }
	    break;


	case recpt:
	case cc_recpt:
1013
        case bcc_recpt:
Per Cederqvist's avatar
Per Cederqvist committed
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
	case loc_no:
	case rec_time:
	case comm_in:
	case footn_to:
	case footn_in:
	case sent_by:
	case sent_at:
	    break;

#ifndef COMPILE_CHECKS
	default:
#endif
1026
1027
        case unknown_info:
	    restart_kom("is_comm_sender(): Illegal misc_item found.\n");
Per Cederqvist's avatar
Per Cederqvist committed
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
	}
    }

    return FALSE;
}

    
/*
 * Check if ACTPERS is allowed to add a footnote to a text. Sets errno if
 * there is an error.
1038
1039
 *
 * Note: Caller must set err_stat
Per Cederqvist's avatar
Per Cederqvist committed
1040
1041
1042
 */

static Success
Per Cederqvist's avatar
Per Cederqvist committed
1043
check_footn(const Text_stat * t_stat)
Per Cederqvist's avatar
Per Cederqvist committed
1044
{
Per Cederqvist's avatar
Per Cederqvist committed
1045
1046
    if (active_connection == NULL
	|| t_stat->author != ACTPERS)
Per Cederqvist's avatar
Per Cederqvist committed
1047
1048
1049
1050
1051
    {
	kom_errno = KOM_NOT_AUTHOR;
	return FAILURE;
    }

Per Cederqvist's avatar
Per Cederqvist committed
1052
    if (count_footn(t_stat) >= param.max_foot)
Per Cederqvist's avatar
Per Cederqvist committed
1053
    {
1054
        err_stat = 0;
Per Cederqvist's avatar
Per Cederqvist committed
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
	kom_errno = KOM_FOOT_LIMIT;
	return FAILURE;
    }

    return OK;
}

/*
 * Check if ACTPERS is allowed to add a comment to a text. Sets errno if
 * there is an error. Note that it is allowed to comment a text even if
 * you are not allowed to read it.
 */

static Success
Per Cederqvist's avatar
Per Cederqvist committed
1069
check_comm(const Text_stat * t_stat)
Per Cederqvist's avatar
Per Cederqvist committed
1070
{
Per Cederqvist's avatar
Per Cederqvist committed
1071
    if (count_comment(t_stat) >= param.max_comm)
Per Cederqvist's avatar
Per Cederqvist committed
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
    {
	kom_errno = KOM_COMM_LIMIT;
	return FAILURE;
    }

    return OK;
}


/*
 * Return a pointer to a Mark if pers_no has marked the text text_no.
 * Otherwise, return NULL.
 */
static Mark *
locate_mark(Pers_no pers_no,
Per Cederqvist's avatar
Per Cederqvist committed
1087
	    const Person *pers_p,	/* May be NULL. */
Per Cederqvist's avatar
Per Cederqvist committed
1088
1089
1090
1091
1092
1093
1094
	    Text_no text_no)
{
    Mark *mp;
    Mark *result = NULL;
    int i;
    Mark_list mlist;

Per Cederqvist's avatar
Per Cederqvist committed
1095
    if (pers_p == NULL)
Per Cederqvist's avatar
Per Cederqvist committed
1096
1097
1098
1099
	GET_P_STAT(pers_p, pers_no, NULL);

    mlist = pers_p->marks;
    
Per Cederqvist's avatar
Per Cederqvist committed
1100
    for (i = mlist.no_of_marks, mp = mlist.marks;
Per Cederqvist's avatar
Per Cederqvist committed
1101
	 i > 0 && result == NULL;
Per Cederqvist's avatar
Per Cederqvist committed
1102
	 i--, mp++)
Per Cederqvist's avatar
Per Cederqvist committed
1103
    {
Per Cederqvist's avatar
Per Cederqvist committed
1104
	if (mp->text_no == text_no)
Per Cederqvist's avatar
Per Cederqvist committed
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
	    result = mp;
    }

    return result;
}

/*
 * Skip one misc-item with all additional data.
 * 
 * This is only used from get_text_stat.
 */
static void
Per Cederqvist's avatar
Per Cederqvist committed
1117
1118
skip_recp(const Misc_info **misc,
	  const Text_stat  *text_stat)
Per Cederqvist's avatar
Per Cederqvist committed
1119
1120
1121
1122
{
    Bool ready = FALSE;
    
    ++(*misc);
Per Cederqvist's avatar
Per Cederqvist committed
1123
    while (!ready && *misc < text_stat->misc_items + text_stat->no_of_misc)
Per Cederqvist's avatar
Per Cederqvist committed
1124
    {
Per Cederqvist's avatar
Per Cederqvist committed
1125
	switch ((*misc)->type)
Per Cederqvist's avatar
Per Cederqvist committed
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
	{
	case loc_no:
	case rec_time:
	case sent_by:
	case sent_at:
	    ++(*misc);
	    break;

	case recpt:
	case cc_recpt:
1136
        case bcc_recpt:
Per Cederqvist's avatar
Per Cederqvist committed
1137
1138
1139
1140
1141
1142
1143
1144
1145
	case comm_to:
	case comm_in:
	case footn_to:
	case footn_in:
	    ready = TRUE;
	    break;
#ifndef COMPILE_CHECKS
	default:
#endif
1146
1147
        case unknown_info:
	    restart_kom("skip_recp() - illegal misc\n");
Per Cederqvist's avatar
Per Cederqvist committed
1148
1149
1150
1151
	}
    }
}

1152
1153

/*
David Byers's avatar
David Byers committed
1154
1155
1156
1157
  Return TRUE if VIEWER sent the misc-item that starts at
  MISC. END is a pointer one beyond the end of the misc-info
  list. VIEWER_P is the pers-stat of VIEWER or NULL.
*/
1158

David Byers's avatar
David Byers committed
1159
static Bool
Per Cederqvist's avatar
Per Cederqvist committed
1160
1161
1162
1163
recp_sent_by(const Misc_info *misc,
	     const Misc_info *end,
             Pers_no viewer,
	     const Person *viewer_p)
David Byers's avatar
David Byers committed
1164
{
Per Cederqvist's avatar
Per Cederqvist committed
1165
    misc++;
David Byers's avatar
David Byers committed
1166
    while (misc < end)
1167
    {
David Byers's avatar
David Byers committed
1168
	switch (misc->type)
1169
	{
David Byers's avatar
David Byers committed
1170
1171
	case sent_by:
            /* Found the sent-by. Check it out. */
Per Cederqvist's avatar
Per Cederqvist committed
1172
            return is_supervisor(misc->datum.sender, viewer, viewer_p);
David Byers's avatar
David Byers committed
1173

David Byers's avatar
David Byers committed
1174
1175
1176
1177
1178
	case loc_no:
	case rec_time:
	case sent_at:
            /* Just skip this */
            break;
1179
1180
	case recpt:
	case cc_recpt:
David Byers's avatar
David Byers committed
1181
        case bcc_recpt:
1182
1183
1184
1185
	case comm_to:
	case comm_in:
	case footn_to:
	case footn_in:
David Byers's avatar
David Byers committed
1186
1187
            /* Found the next "major" item without finding a sent-by. */
            return FALSE;
David Byers's avatar
David Byers committed
1188

1189
1190
1191
#ifndef COMPILE_CHECKS
	default:
#endif
1192
1193
        case unknown_info:
	    restart_kom("recp_sent_by() - illegal misc\n");
David Byers's avatar
David Byers committed
1194
        }
Per Cederqvist's avatar
Per Cederqvist committed
1195
        misc++;
1196
1197
    }

David Byers's avatar
David Byers committed
1198
    /* No sent-by found. Return false. */
1199
1200
1201
1202
1203
1204
    return FALSE;
}


/*
 * Copy the text_stat original into result, but only those part
1205
 * that viewer is allowed to see.  (Censor away information about
1206
1207
1208
1209
1210
1211
 * conferences that viewer is not allowed to know about).
 *
 * All memory is allocated via tmp_alloc().
 */
static void
filter_secret_info(Text_stat *result,
Per Cederqvist's avatar
Per Cederqvist committed
1212
1213
1214
		   const Text_stat *original,
                   const Connection *viewer_conn,
                   Bool output_bcc)
1215
{
Per Cederqvist's avatar
Per Cederqvist committed
1216
1217
1218
1219
    const Misc_info *orig;
    Misc_info	    *copy;		/* Censored Misc_infos */
    Pers_no          viewer;
    const Person    *viewer_p;
1220

1221
    /* FIXME (bug 177): Possible optimisation:
1222
1223
1224
       No need to copy unless there is a secret conf among the recipients. */

    *result = *original;
David Byers's avatar
Server    
David Byers committed
1225
1226
    viewer = viewer_conn->pers_no;
    viewer_p = viewer_conn->person;
1227
1228
1229

    filter_aux_item_list(&original->aux_item_list,
                         &result->aux_item_list,
David Byers's avatar
Server    
David Byers committed
1230
                         viewer_conn);
1231

Per Cederqvist's avatar
Per Cederqvist committed
1232
    result->misc_items = tmp_alloc(result->no_of_misc * sizeof(Misc_info));
1233
1234
1235
1236
    result->no_of_misc = 0;
    copy = result->misc_items;
    orig = original->misc_items;

Per Cederqvist's avatar
Per Cederqvist committed
1237
    while (orig < original->misc_items + original->no_of_misc)
1238
    {
Per Cederqvist's avatar
Per Cederqvist committed
1239
	switch (orig->type)
1240
1241
1242
	{
	case recpt:
	case cc_recpt:
1243
	    if (!has_access(orig->datum.recipient, viewer_conn, read_protected)
David Byers's avatar
Server    
David Byers committed
1244
		&& !ENA_C(viewer_conn, admin, 4))