Select Git revision
nettle-benchmark.c
Forked from
Nettle / nettle
Source project has a limited visibility.
text.c 50.13 KiB
/*
* $Id: text.c,v 0.27 1994/02/20 16:20:20 ceder Exp $
* Copyright (C) 1991 Lysator Academic Computer Association.
*
* 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.
*
* Please mail bug reports to bug-lyskom@lysator.liu.se.
*/
/*
* text.c
*
* All atomic calls that deals with texts.
*/
static char *rcsid = "$Id: text.c,v 0.27 1994/02/20 16:20:20 ceder Exp $";
#include "rcs.h"
USE(rcsid);
#include <errno.h>
#include <stdio.h>
#include <time.h>
#include <setjmp.h>
#include <sys/types.h>
#ifdef HAVE_STDARG_H
# include <stdarg.h>
#endif
#include <sys/types.h>
#include "server/smalloc.h"
#include "s-string.h"
#include "misc-types.h"
#include "kom-types.h"
#include "services.h"
#include "manipulate.h"
#include "lyskomd.h"
#include "kom-errno.h"
#include "config.h"
#include "com.h"
#include "connections.h"
#include "internal-connections.h"
#include "cache.h"
#include "log.h"
#include "minmax.h"
#include "admin.h"
#include "send-async.h"
#include "param.h"
#include "kom-memory.h"
/*
* 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)
{
Text_list * t;
time( & conf_c->last_written );
/* Add number last on the text_list */
t = &(conf_c->texts);
t->texts = srealloc(t->texts, ++(t->no_of_texts) * sizeof(Text_no));
t->texts[ t->no_of_texts - 1 ] = text_no;
return t->first_local_no + t->no_of_texts - 1;
}
/*
* Set the global text_no of a certain local text_no if that local text_no
* exists in the conf. No action is taken if the local text_no doesn't exist.
* This function is probably only used to set the number to zero when a text
* is deleted.
*/
static void
set_loc_no (Conference * conf_c,
Local_text_no loc_no,
Text_no text_no)
{
if ( loc_no < conf_c->texts.first_local_no
|| loc_no > conf_c->texts.first_local_no + conf_c->texts.no_of_texts )
{
return; /* Doesn't exist in conf.stat. */
}
conf_c->texts.texts[ loc_no - conf_c->texts.first_local_no ] = text_no;
return;
}
/*
* Count how many recipients and cc_recipients a text has.
*/
static int
count_recipients( Text_stat *t_stat )
{
int n = 0;
Misc_info * misc;
for ( misc = t_stat->misc_items;
misc < t_stat->misc_items + t_stat->no_of_misc; misc++ )
{
if ( misc->type == recpt || misc->type == cc_recpt )
{
n++;
}
}
return n;
}
/*
* Count how many footnotes a text has.
*/
static int
count_footn( Text_stat *t_stat )
{
int n = 0;
Misc_info * misc;
for ( misc = t_stat->misc_items;
misc < t_stat->misc_items + t_stat->no_of_misc; misc++ )
{
if ( misc->type == footn_in )
{
n++;
}
}
return n;
}
/*
* Count how many commments a text has.
*/
static int
count_comment( Text_stat *t_stat )
{
int n = 0;
Misc_info * misc;
for ( misc = t_stat->misc_items;
misc < t_stat->misc_items + t_stat->no_of_misc; misc++ )
{
if ( misc->type == comm_in )
{
n++;
}
}
return n;
}
/*
* Check if ACTPERS is allowed to read this text.
* Returns TRUE if he is allowed to read it.
*/
/*
* Check if CONF_NO is a recipient of the text whose text_stat is given.
*/
static Bool
is_recipient(Conf_no conf_no,
Text_stat * t_stat)
{
int i;
for ( i = 0; i < t_stat->no_of_misc; i++ )
{
switch( t_stat->misc_items[ i ].type )
{
case recpt:
if ( t_stat->misc_items[ i ].datum.recipient == conf_no )
{
return TRUE;
}
break;
case cc_recpt:
if ( t_stat->misc_items[ i ].datum.cc_recipient == conf_no )
{
return TRUE;
}
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:
restart_kom("is_recipient(): illegal misc_item\n");
#endif
}
}
return FALSE;
}
/*
* Check if comment is a comment to parent.
*/
static Bool
is_comment_to(Text_no comment,
Text_stat *parent)
{
int i;
for ( i = 0; i < parent->no_of_misc; i++ )
{
switch( parent->misc_items[ i ].type )
{
case comm_in:
if ( parent->misc_items[ i ].datum.commented_in == comment )
return TRUE;
break;
default:
break;
}
}
return FALSE;
}
/*
* Check if footnote is a footnote to parent.
*/
static Bool
is_footnote_to(Text_no footnote,
Text_stat *parent)
{
int i;
for ( i = 0; i < parent->no_of_misc; i++ )
{
switch( parent->misc_items[ i ].type )
{
case footn_in:
if ( parent->misc_items[ i ].datum.footnoted_in == footnote )
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. */
Conference * conf_c) /* May be NULL */
{
int i;
Access acc;
CHK_LOGIN(0);
if ( conf_c == NULL )
GET_C_STAT(conf_c, conf_no, 0);
for ( i=0; i < param.max_super_conf_loop; i++)
{
acc = access_perm (conf_no, conf_c, ACTPERS, ACT_P);
if ( acc <= none )
return 0;
if (conf_c->permitted_submitters == 0
|| acc == unlimited
|| locate_membership( conf_c->permitted_submitters, ACT_P) != NULL)
{
return conf_no;
}
if ((conf_no = conf_c->super_conf) == 0)
return 0;
GET_C_STAT(conf_c, conf_no, 0);
}
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);
ADD_MISC(foot_s, footn_to, footnote_to, text);
ADD_MISC(text_s, footn_in, footnoted_in, footnote);
mark_text_as_changed( footnote );
mark_text_as_changed( text );
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);
ADD_MISC(comm_s, comm_to, comment_to, text);
ADD_MISC(text_s, comm_in, commented_in, comment);
mark_text_as_changed( comment );
mark_text_as_changed( text );
return OK;
}
/*
* Say that RECEIVER is a recipient of TEXT.
*/
static Success
do_add_recpt(Text_no text,
Text_stat * text_s, /* May be NULL */
Conf_no receiver)
{
Conference *rece_c;
if ( text_s == NULL )
{
GET_T_STAT(text_s, text, FAILURE);
}
GET_C_STAT(rece_c, receiver, FAILURE);
ADD_MISC(text_s, recpt, recipient, receiver);
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;
}
static Success
do_add_cc_recpt(Text_no new_text,
Text_stat *text_s, /* May be NULL */
Conf_no receiver)
{
Conference *rece_c;
if ( text_s == NULL )
GET_T_STAT(text_s, new_text, FAILURE);
GET_C_STAT(rece_c, receiver, FAILURE);
ADD_MISC(text_s, cc_recpt, cc_recipient, receiver);
ADD_MISC(text_s, loc_no, local_no, add_text_in_conf(rece_c, new_text));
mark_text_as_changed( new_text );
mark_conference_as_changed( receiver );
return OK;
}
/*
* Return number of lines in a text
*/
static u_short
count_lines( String str )
{
u_short l = 0;
while ( str.len-- > 0 )
{
if( *str.string++ == '\n' )
{
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
do_delete_misc (u_short * no_of_misc,
Misc_info * misc,
int loc)
{
int del = 1; /* Number of items to delete. */
/* Always delete at least one item. */
Bool ready;
/* Check range of loc */
if ( loc < 0 || loc >= *no_of_misc )
{
restart_kom("do_delete_misc() - loc out of range");
}
ready = FALSE;
while ( ready == FALSE && loc + del < *no_of_misc )
{
switch ( misc[ loc + del ].type )
{
case loc_no:
case rec_time:
case sent_by:
case sent_at:
del++;
break;
case recpt:
case cc_recpt:
case footn_to:
case footn_in:
case comm_to:
case comm_in:
ready = TRUE;
break;
#ifndef COMPILE_CHECKS
default:
restart_kom("do_delete_misc() - illegal misc");
#endif
}
}
*no_of_misc -= del;
/* Move items beyond the deleted ones. */
while ( loc < *no_of_misc )
{
misc[ loc ] = misc[ loc + del ];
loc++;
}
}
/*
* Delete leading zeroes from the text_list.
* Returns TRUE if some zeroes was deleted.
*/
static Bool
adjust_text_list(Text_list *text_list)
{
u_long zeroes;
u_long i;
for (zeroes = 0;
zeroes < text_list->no_of_texts && text_list->texts[ zeroes ] == 0;
zeroes++)
;
if ( zeroes > 0 )
{
text_list->no_of_texts -= zeroes;
text_list->first_local_no += zeroes;
for ( i = 0; i < text_list->no_of_texts; i++)
text_list->texts[ i ] = text_list->texts[ i + zeroes ];
text_list->texts = srealloc(text_list->texts,
(text_list->no_of_texts
* sizeof(Text_no)));
}
return zeroes > 0;
}
/*
* Delete a recipient from a text. Does not fail if the recipient doesn't
* exist - that is not an error.
*/
static Success
do_sub_recpt (Text_no text_no,
Text_stat * text_s, /* May be NULL */
Conf_no conf_no,
Conference * conf_s ) /* May be NULL */
{
int i;
if ( text_s == NULL )
GET_T_STAT(text_s, text_no, FAILURE);
if ( conf_s == NULL )
{
conf_s = cached_get_conf_stat( conf_no ); /* Might still be NULL. */
}
for ( i = 0; i < text_s->no_of_misc; i++ )
{
switch ( text_s->misc_items[ i ].type)
{
case recpt:
if ( text_s->misc_items[ i ].datum.recipient == conf_no )
{
if ( conf_s != NULL )
{
/* Only if the conference exists: */
set_loc_no( conf_s,
text_s->misc_items[ i+1 ].datum.local_no, 0 );
adjust_text_list (&conf_s->texts);
mark_conference_as_changed (conf_no);
}
do_delete_misc ( &text_s->no_of_misc, text_s->misc_items, i );
mark_text_as_changed (text_no);
return OK;
}
break;
case cc_recpt:
if ( text_s->misc_items[ i ].datum.cc_recipient == conf_no )
{
if ( conf_s != NULL )
{
set_loc_no( conf_s,
text_s->misc_items[ i+1 ].datum.local_no, 0 );
mark_conference_as_changed( conf_no );
}
do_delete_misc( &text_s->no_of_misc, text_s->misc_items, i );
mark_text_as_changed( text_no );
return OK;
}
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:
log("%s: do_sub_recpt(): bad misc_item.\n", __FILE__);
break;
#endif
}
}
kom_errno = KOM_NOT_RECIPIENT;
return FAILURE;
}
/*
* Delete the link between comment and comment_to.
*/
static void
do_sub_comment (Text_no comment, /* The comment. */
Text_stat * text_s,
Text_no comment_to, /* The commented. */
Text_stat * parent_s )
{
int i;
Bool ready;
if ( text_s == NULL )
VOID_GET_T_STAT(text_s, comment);
if ( parent_s == NULL )
VOID_GET_T_STAT(parent_s, comment_to);
for ( ready = FALSE, i = 0; !ready && i < text_s->no_of_misc; i++ )
{
switch ( text_s->misc_items[ i ].type)
{
case comm_to:
if ( text_s->misc_items[ i ].datum.comment_to == comment_to )
{
do_delete_misc( &text_s->no_of_misc, text_s->misc_items, i );
mark_text_as_changed( comment );
ready = TRUE;
}
break;
default:
break;
}
}
if ( !ready )
restart_kom("do_sub_comment(): part 1 failed.\n");
for ( ready = FALSE, i = 0; !ready && i < parent_s->no_of_misc; i++ )
{
switch ( parent_s->misc_items[ i ].type)
{
case comm_in:
if ( parent_s->misc_items[ i ].datum.commented_in == comment )
{
do_delete_misc (&parent_s->no_of_misc,
parent_s->misc_items, i );
mark_text_as_changed( comment_to );
ready = TRUE;
}
break;
default:
break;
}
}
if ( !ready )
restart_kom("do_sub_comment(): part 2 failed.\n");
return;
}
/*
* Delete the link between footnote and footnote_to.
*/
static void
do_sub_footnote (Text_no footnote,
Text_stat * text_s,
Text_no footnote_to,
Text_stat * parent_s )
{
int i;
Bool ready;
if ( text_s == NULL )
VOID_GET_T_STAT(text_s, footnote);
if ( parent_s == NULL )
VOID_GET_T_STAT(parent_s, footnote_to);
for ( ready = FALSE, i = 0; !ready && i < text_s->no_of_misc; i++ )
{
switch ( text_s->misc_items[ i ].type)
{
case footn_to:
if ( text_s->misc_items[ i ].datum.footnote_to == footnote_to )
{
do_delete_misc( &text_s->no_of_misc, text_s->misc_items, i );
mark_text_as_changed( footnote );
ready = TRUE;
}
break;
default:
break;
}
}
if ( !ready )
restart_kom("do_sub_footnote(): part 1 failed.\n");
for ( ready = FALSE, i = 0; !ready && i < parent_s->no_of_misc; i++ )
{
switch ( parent_s->misc_items[ i ].type)
{
case footn_in:
if ( parent_s->misc_items[ i ].datum.footnoted_in == footnote )
{
do_delete_misc (&parent_s->no_of_misc,
parent_s->misc_items, i );
mark_text_as_changed( footnote_to );
ready = TRUE;
}
break;
default:
break;
}
}
if ( !ready )
restart_kom("do_sub_footnote(): part 2 failed.\n");
return;
}
/*
* Who is sender of misc_item I? Returns 0 if there is no sent_by misc_item.
*/
static Pers_no
sender(Text_stat * t_stat,
int i)
{
for (i++ ; i < t_stat->no_of_misc; i++)
{
switch ( t_stat->misc_items[ i ].type )
{
case sent_by:
return t_stat->misc_items[ i ].datum.sender;
case recpt:
case cc_recpt:
case comm_to:
case comm_in:
case footn_to:
case footn_in:
case sent_at:
return 0; /* No sender. */
case loc_no:
case rec_time:
break; /* These may come before a sent_by. */
#ifndef COMPILE_CHECKS
default:
log("ERROR: sender(): Illegal misc_item found.\n");
return 0;
#endif
}
}
return 0; /* No sender. */
}
/*
* Check if ACTPERS has sent this text to conference CONF_NO
*/
static Bool
is_sender(Text_stat * text_s,
Conf_no conf_no)
{
int i;
if ( !ACTPERS )
{
return FALSE;
}
for ( i = 0; i < text_s->no_of_misc; i++ )
{
switch( text_s->misc_items[ i ].type )
{
case recpt:
if ( text_s->misc_items[ i ].datum.recipient == conf_no
&& sender ( text_s, i ) == ACTPERS )
{
return TRUE;
}
break;
case cc_recpt:
if ( text_s->misc_items[ i ].datum.cc_recipient == conf_no
&& sender ( text_s, i ) == ACTPERS )
{
return TRUE;
}
break;
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:
restart_kom("is_sender(): Illegal misc_item found.\n");
#endif
}
}
return FALSE;
}
/*
* Check if ACTPERS has sent this text as a comment to parent.
*/
static Bool
is_comm_sender(Text_stat * text_s,
Text_no parent)
{
int i;
if ( !ACTPERS )
{
return FALSE;
}
for ( i = 0; i < text_s->no_of_misc; i++ )
{
switch( text_s->misc_items[ i ].type )
{
case comm_to:
if ( text_s->misc_items[ i ].datum.comment_to == parent
&& sender ( text_s, i ) == ACTPERS )
{
return TRUE;
}
break;
case recpt:
case cc_recpt:
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:
restart_kom("is_comm_sender(): Illegal misc_item found.\n");
#endif
}
}
return FALSE;
}
/*
* Check if ACTPERS is allowed to add a footnote to a text. Sets errno if
* there is an error.
*/
static Success
check_footn(Text_stat * t_stat)
{
if ( t_stat->author != ACTPERS )
{
kom_errno = KOM_NOT_AUTHOR;
return FAILURE;
}
if ( count_footn( t_stat ) >= param.max_foot )
{
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
check_comm(Text_stat * t_stat)
{
if ( count_comment( t_stat ) >= param.max_comm )
{
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,
Person *pers_p, /* May be NULL. */
Text_no text_no)
{
Mark *mp;
Mark *result = NULL;
int i;
Mark_list mlist;
if ( pers_p == NULL )
GET_P_STAT(pers_p, pers_no, NULL);
mlist = pers_p->marks;
for ( i = mlist.no_of_marks, mp = mlist.marks;
i > 0 && result == NULL;
i--, mp++ )
{
if ( mp->text_no == text_no )
result = mp;
}
return result;
}
/*
* Skip one misc-item with all additional data.
*
* This is only used from get_text_stat.
*/
static void
skip_recp (Misc_info ** misc,
Text_stat * text_stat )
{
Bool ready = FALSE;
++(*misc);
while ( !ready && *misc < text_stat->misc_items + text_stat->no_of_misc )
{
switch ( (*misc)->type )
{
case loc_no:
case rec_time:
case sent_by:
case sent_at:
++(*misc);
break;
case recpt:
case cc_recpt:
case comm_to:
case comm_in:
case footn_to:
case footn_in:
ready = TRUE;
break;
#ifndef COMPILE_CHECKS
default:
restart_kom("skip_recp() - illegal misc\n");
#endif
}
}
}
/*
* End of static functions.
*/
/*
* Functions that are exported to the rest of the server.
*/
/*
* Check if ACTPERS is allowed to read this text.
* Returns TRUE if he is allowed to read it.
*/
Bool
text_read_access(Text_no text_no,
Text_stat * text_stat)
{
int i;
Misc_info *misc;
Conference *recipient;
/*
* Nope, people who aren't logged in, should NOT be
* allowed to read any texts but motd_of_lyskom!
*/
if ( text_no == kom_info.motd_of_lyskom )
return TRUE;
CHK_LOGIN(FALSE);
if ( text_stat == NULL )
GET_T_STAT(text_stat, text_no, FALSE);
if ( ENA(wheel, 10))
return TRUE;
if ( ACTPERS )
{
if ( text_stat->author == ACTPERS )
return TRUE;
/* Check if ACTPERS or current working conference is a recipient */
for (i = text_stat->no_of_misc, misc = text_stat->misc_items;
i; i--, misc++)
{
if ( misc->type == recpt
&& (misc->datum.recipient == active_connection->cwc
|| misc->datum.recipient == ACTPERS ))
{
return TRUE;
}
if ( misc->type == cc_recpt
&& (misc->datum.cc_recipient == active_connection->cwc
|| misc->datum.cc_recipient == ACTPERS ))
{
return TRUE;
}
}
/* Check if ACTPERS is member in any of the recipients */
for (i = text_stat->no_of_misc, misc = text_stat->misc_items;
i; i--, misc++)
{
if ( misc->type == recpt
&& locate_membership( misc->datum.recipient, ACT_P) != NULL)
{
return TRUE;
}
if ( misc->type == cc_recpt
&& locate_membership( misc->datum.cc_recipient, ACT_P) != NULL)
{
return TRUE;
}
}
if ( locate_mark(ACTPERS, ACT_P, text_no) != NULL )
{
return TRUE;
}
}
/* Check if any of the recipients is an open conference, */
/* or if ACTPERS is a supervisor. (Note: ACTPERS is not */
/* supervisor of anything if he isn't logged in.) */
for (i = text_stat->no_of_misc, misc = text_stat->misc_items;
i; i--, misc++)
{
if ( misc->type == recpt
&& (recipient =
cached_get_conf_stat( misc->datum.recipient )) != NULL )
{
if ( !recipient->type.rd_prot
|| is_supervisor(misc->datum.recipient, recipient,
ACTPERS, ACT_P) == TRUE)
{
return TRUE;
}
}
if ( misc->type == cc_recpt
&& (recipient =
cached_get_conf_stat( misc->datum.cc_recipient )) != NULL )
{
if ( !recipient->type.rd_prot
|| is_supervisor(misc->datum.cc_recipient, recipient,
ACTPERS, ACT_P) == TRUE)
{
return TRUE;
}
}
}
return FALSE;
}
/*
* Delete a text from the database. Deletes all "links" to/from other
* texts/persons/conferences. (e. g. comm_to/comm_in)
*/
Success
do_delete_text(Text_no text_no,
Text_stat *text_s)
{
Person *author;
Bool found = FALSE;
u_long i;
if ( text_s == NULL )
GET_T_STAT(text_s, text_no, FAILURE);
if ((author = cached_get_person_stat (text_s->author)) != NULL)
{
/* Delete this text from created_texts. */
for ( i = 0; i < author->created_texts.no_of_texts; i++ )
if ( author->created_texts.texts[ i ] == text_no )
{
author->created_texts.texts[ i ] = 0;
adjust_text_list (&author->created_texts);
mark_person_as_changed (text_s->author);
found = TRUE;
break;
}
if ( !found )
log("ERROR: %s: do_delete_text(): %s",
__FILE__, "text not found in authors created_texts.");
}
while ( text_s->no_of_misc > 0 )
{
switch ( text_s->misc_items[ 0 ].type )
{
case recpt:
if ( do_sub_recpt(text_no, text_s,
text_s->misc_items[ 0 ].datum.recipient, NULL)
!= OK )
restart_kom("do_delete_text(): error pos 1.\n");
break;
case cc_recpt:
if ( do_sub_recpt(text_no, text_s,
text_s->misc_items[ 0 ].datum.cc_recipient, NULL)
!= OK )
restart_kom("do_delete_text(): error pos 2.\n");
break;
case comm_to:
do_sub_comment(text_no, text_s,
text_s->misc_items[ 0 ].datum.comment_to, NULL);
break;
case comm_in:
do_sub_comment(text_s->misc_items[ 0 ].datum.commented_in, NULL,
text_no, text_s);
break;
case footn_to:
do_sub_footnote(text_no, text_s,
text_s->misc_items[ 0 ].datum.footnote_to, NULL);
break;
case footn_in:
do_sub_footnote(text_s->misc_items[ 0 ].datum.footnoted_in, NULL,
text_no, text_s);
break;
case loc_no:
case rec_time:
case sent_by:
case sent_at:
restart_kom("do_delete_text(): Illegal misc-item syntax.\n");
#ifndef COMPILE_CHECKS
default:
restart_kom("do_delete_text(): Illegal misc-item.\n");
#endif
}
}
cached_delete_text(text_no);
return OK;
}
/*
* Atomic calls.
*/
/*
* Calls to handle marks.
*
* Marks are secret. No else can know what you have marked.
*/
/*
* Get text_nos of all marked texts.
*/
extern Success
get_marks( Mark_list *result )
{
CHK_LOGIN(FAILURE);
*result = ACT_P->marks;
return OK;
}
/*
* Get the text. The text will not be marked as read until you
* explicitly mark_as_read() it. start_char = 0 && end_char = END_OF_STRING
* gives the entire text.
*/
extern Success
get_text (Text_no text_no,
String_size start_char,
String_size end_char,
String * result)
{
Text_stat * text_s;
GET_T_STAT(text_s, text_no, FAILURE);
/* Check if ACTPERS has read acess to the text */
if ( text_read_access( text_no, text_s ) != TRUE )
{
kom_errno = KOM_NO_SUCH_TEXT;
err_stat = text_no;
return FAILURE;
}
*result = cached_get_text( text_no );
if ( start_char > result->len )
{
kom_errno = KOM_INDEX_OUT_OF_RANGE;
return FAILURE;
}
/* Should use something in s-string.c to do this. +++ */
result->string += start_char;
result->len = min ( result->len - 1, end_char ) + 1 - start_char;
if ( ACTPERS )
{
++ACT_P->no_of_text_fetches;
mark_person_as_changed( ACTPERS );
}
return OK;
}
/*
* Check if person is a member in any of the recipients or cc_recipients
* of text_s
*/
static Bool
is_member_in_recpt(Person *person,
Text_stat *text_s)
{
int i;
for ( i = 0; i < text_s->no_of_misc; i++ )
{
switch(text_s->misc_items[ i ].type)
{
case recpt:
if ( locate_membership(text_s->misc_items[ i ].datum.recipient,
person) != NULL )
{
return TRUE;
}
break;
case cc_recpt:
if ( locate_membership(text_s->misc_items[ i ].datum.cc_recipient,
person) != NULL )
{
return TRUE;
}
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:
log("%s: is_member_in_recpt(): bad misc_item.\n", __FILE__);
break;
#endif
}
}
return FALSE;
}
/*
* Copy the text_stat original into result, but only those part
* that viewer is allowed to see. (Censor away information about
* conferences that viewer is not allowed to know about).
*
* All memory is allocated via tmp_alloc().
*/
static void
filter_secret_info(Text_stat *result,
Text_stat *original,
Pers_no viewer,
Person *viewer_p) /* May be NULL. */
{
Misc_info * orig;
Misc_info * copy; /* Censored Misc_infos */
/* ^^^ Possible optimisation:
No need to copy unless there is a secret conf among the recipients. */
*result = *original;
result->misc_items = tmp_alloc(result->no_of_misc * sizeof ( Misc_info));
result->no_of_misc = 0;
copy = result->misc_items;
orig = original->misc_items;
while ( orig < original->misc_items + original->no_of_misc )
{
switch( orig->type )
{
case recpt:
if ( (fast_access_perm (orig->datum.recipient, viewer, viewer_p)
<= none )
&& !ENA(admin, 4))
{
skip_recp ( &orig, original );
}
else
{
*copy++ = *orig++;
++result->no_of_misc;
}
break;
case cc_recpt:
if ( (fast_access_perm (orig->datum.cc_recipient, viewer, viewer_p)
<= none)
&& !ENA(admin, 4))
{
skip_recp ( &orig, original );
}
else
{
*copy++ = *orig++;
++result->no_of_misc;
}
break;
case loc_no:
case rec_time:
case comm_to:
case comm_in:
case footn_to:
case footn_in:
case sent_by:
case sent_at:
*copy++ = *orig++;
++result->no_of_misc;
break;
#ifndef COMPILE_CHECKS
default:
restart_kom("filter_secret_info() - illegal misc_item!\n");
#endif
}
}
}
/*
* Get text status.
*
* If there are recipients of the text that are secret confs
* those misc-items will be censored.
*/
extern Success
get_text_stat (Text_no text_no,
Text_stat *result )
{
Text_stat * text_stat;
GET_T_STAT(text_stat, text_no, FAILURE);
if ( !text_read_access(text_no, text_stat) && !ENA(admin, 2))
{
kom_errno = KOM_NO_SUCH_TEXT;
return FAILURE;
}
filter_secret_info(result, text_stat, ACTPERS, ACT_P);
return OK;
}
/*
* Functions local to create_text:
*/
/*
* Check that the recipient or cc_recipient at LOC is not already a
* recipient or cc_recipient of this text.
*/
static Success
check_double_subm (Misc_info * misc,
int loc,
Conf_no addressee)
{
int j;
for ( j = 0; j < loc; j++)
{
switch ( misc[ j ].type )
{
case recpt:
if ( misc[ j ].datum.recipient == addressee )
return FAILURE;
break;
case cc_recpt:
if ( misc[ j ].datum.cc_recipient == addressee )
return FAILURE;
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:
log(__FILE__, ": check_double_subm(): bad misc_item.\n");
break;
#endif
}
}
return OK;
}
/*
* Check that none of the first 'pos' misc_items pointed to by misc
* is a comment or footnote to text forbidden.
*/
static Success
check_double_comm (Misc_info *misc,
int pos,
Text_no forbidden)
{
for(;pos > 0; pos--, misc++)
{
switch(misc->type)
{
case comm_to:
if ( misc->datum.comment_to == forbidden )
return FAILURE;
break;
case footn_to:
if ( misc->datum.footnote_to == forbidden )
return FAILURE;
break;
case recpt:
case cc_recpt:
case comm_in:
case footn_in:
case loc_no:
case rec_time:
case sent_by:
case sent_at:
break;
#ifndef COMPILE_CHECKS
default:
log("%s: check_double_subm(): bad misc_item.\n", __FILE__);
break;
#endif
}
}
return OK;
}
/*
* Check that all misc_items are legal. Return OK / FAILURE.
* Update recpt & cc_recpt fields if the text goes to a super_conf.
* Signal an error if a conference is recipient more than once, or if
* a text is a comment to the same text more than once.
*/
static Success
create_text_check_misc (u_short * no_of_misc,
Misc_info * misc )
{
int i;
Text_stat * parent;
Conf_no addressee;
for ( i = 0; i < *no_of_misc; i++)
{
err_stat = i; /* In case of an error, err_stat indicates which
misc_item caused the error. */
switch( misc[ i ].type )
{
case footn_to:
/* Check that ACTPERS is the author to the text. */
GET_T_STAT( parent, misc[ i ].datum.footnote_to, FAILURE);
if ( check_footn(parent) == FAILURE )
return FAILURE;
if ( check_double_comm(misc, i, misc[ i ].datum.footnote_to)
!= OK )
{
kom_errno = KOM_ILL_MISC;
return FAILURE;
}
break;
case comm_to:
/* Check that the text exists. */
GET_T_STAT( parent, misc[ i ].datum.comment_to, FAILURE);
if ( check_comm(parent) == FAILURE )
return FAILURE;
if ( check_double_comm(misc, i, misc[ i ].datum.comment_to)
!= OK )
{
kom_errno = KOM_ILL_MISC;
return FAILURE;
}
break;
case recpt:
/* Check that ACTPERS has write access to the conference or a
superconference. */
/* Superconfs are recursive */
addressee = submit_to( misc[ i ].datum.recipient, NULL);
/* Update in case of super_conf */
if ((misc[ i ].datum.recipient = addressee) == 0)
{
kom_errno = KOM_ACCESS;
return FAILURE;
}
/* Check that this recipient is not already a recipient. */
if ( check_double_subm(misc, i, addressee) != OK )
{
kom_errno = KOM_ILL_MISC;
return FAILURE;
}
break;
case cc_recpt:
/* Check that ACTPERS has write access to the conference or a
superconference. */
/* Superconfs are recursive */
addressee = submit_to( misc[ i ].datum.cc_recipient, NULL);
/* Update in case of super_conf */
if ((misc[ i ].datum.cc_recipient = addressee) == 0)
{
kom_errno = KOM_ACCESS;
return FAILURE;
}
/* Check that this recipient is not already a recipient. */
if (check_double_subm(misc, i, addressee) != OK)
{
kom_errno = KOM_ILL_MISC;
return FAILURE;
}
break;
case loc_no: /* Ignore loc_no */
break;
case comm_in:
case footn_in:
case rec_time:
case sent_by:
case sent_at:
/* Fall through */
#ifndef COMPILE_CHECKS
default:
#endif
kom_errno = KOM_ILL_MISC;
return FAILURE;
}
}
return OK;
}
/*
* Fix all double references. Eg if misc contains comm_to, add a comm_in field.
* No access-permission checking is done.
*/
static Success
create_text_add_miscs(Text_no new_text,
int no_of_misc,
Misc_info * misc)
{
int i;
for ( i = 0; i < no_of_misc; i++)
{
err_stat = i; /* In case of an error, err_stat indicates which
misc_item caused the error. */
switch( misc[ i ].type )
{
case footn_to:
if ( do_add_footnote(new_text, misc[ i ].datum.footnote_to) != OK)
return FAILURE;
break;
case comm_to:
if ( do_add_comment(new_text, misc[ i ].datum.comment_to) != OK )
return FAILURE;
break;
case recpt:
if ( do_add_recpt(new_text, NULL, misc[ i ].datum.recipient) != OK)
return FAILURE;
break;
case cc_recpt:
if ( do_add_cc_recpt(new_text, NULL,
misc[ i ].datum.cc_recipient) !=OK )
return FAILURE;
break;
case loc_no: /* Ignore loc_no. */
break;
case comm_in:
case footn_in:
case rec_time:
case sent_by:
case sent_at:
/* Fall through */
#ifndef COMPILE_CHECKS
default:
#endif
restart_kom("create_text_add_misc() - illegal Info_type");
}
}
return OK;
}
/*
* Send an asynchronous message, but filter any secret information.
*/
static void
send_async_new_text (Text_no text_no,
Text_stat *text_s)
{
Connection *cptr;
Session_no i = 0;
Text_stat filtered;
init_text_stat(&filtered);
while ( (i = traverse_connections(i)) != 0 )
{
cptr = get_conn_by_number(i);
/*
* filter_secret_info copies the text-stat to filtered, but
* since it is allocated with tmp_alloc we do not need to free
* the memory now.
*/
filter_secret_info(&filtered, text_s,
cptr->pers_no, cptr->person);
if ( cptr->person != NULL
&& is_member_in_recpt (cptr->person, &filtered) == TRUE)
{
async_new_text (cptr, text_no, &filtered);
}
}
}
/*
* Special case handling of special text numbers.
*
* In the KOMmunity, there has arisen an odd phenomenon known as
* "jubel" to the local citizens of LysKOM. There are several kinds
* of "jubel"; a text number divisible by 1000 is one famous such.
*
* There is a feeling that jubels, at least the more prominent jubels,
* should not be created by automatic postings. This code is
* primarily aimed at stopping such things, but it can have other, eh,
* interesting uses as well...
*/
struct jubel {
struct jubel *next;
Pers_no bad_guy;
Text_no divisor; /* 0 is used to represent infinity */
Text_no remainder;
};
static struct jubel *jubel_root = NULL;
/*
* register_jubel(char*) - tell lyskomd about a jubel, and a
* person which is not allowed to create that jubel. The argument is
* a string consisting of two or three numbers:
* persno textno
* persno divisor remainder
* meaning that PERSNO is not allowed to create text TEXTNO, or any
* text number which fulfills the relation textno%DIVISOR==REMAINDER.
*/
void
register_jubel(Pers_no pno,
Text_no divis, /* 0 if no division should be made. */
Text_no tno)
{
struct jubel *j = smalloc(sizeof(struct jubel));
j->next = jubel_root;
j->bad_guy = pno;
j->divisor = divis;
j->remainder = tno;
jubel_root = j;
}
/* Free the jubel list */
void
free_all_jubel(void)
{
struct jubel *a;
struct jubel *b;
a = jubel_root;
while (a != NULL)
{
b = a;
a = a->next;
sfree(b);
}
jubel_root = NULL;
}
/* Check if it is ok for ACTPERS to create the next text. */
static Bool
ok_to_create_next_text(void)
{
struct jubel *j;
Text_no next_tno;
Bool is_jubel = FALSE;
next_tno = query_next_text_num();
for (j = jubel_root; j != NULL; j = j->next)
{
if ((j->divisor == 0 && next_tno == j->remainder)
|| (j->divisor != 0 && next_tno%j->divisor == j->remainder))
{
if (ACTPERS == j->bad_guy)
{
log("Stopped person %d from creating jubel %lu",
ACTPERS, next_tno);
return FALSE;
}
else
is_jubel = TRUE;
}
}
if (is_jubel)
log("Granted jubel %lu to person %d.\n", next_tno, ACTPERS);
return TRUE;
}
/*
* Create a text.
*
* The recipients may change. See doc for set_permitted_submitters.
*
* The only allowed Misc_items are recpt, cc_recpt, comm_to and footn_to.
* loc_no are allowed, but ignored.
*
* Returns text_no of the created text, or 0 if there was an error.
*/
extern Text_no
create_text(const String message,
u_short no_of_misc,
Misc_info * misc )
{
Text_no text;
Text_stat * t_stat;
extern int errno;
CHK_LOGIN(0);
/* Check the length of the text. */
if ( s_strlen (message) > param.text_len )
{
kom_errno = KOM_LONG_STR;
return 0;
}
/* Check that the author is allowed to write this text number */
if (ok_to_create_next_text() == FALSE)
{
kom_errno = KOM_TEMPFAIL;
return 0;
}
/* Check all misc-items */
if ( create_text_check_misc(&no_of_misc, misc) != OK
|| (text = cached_create_text( message )) == 0)
{
return 0;
}
if ( (t_stat = cached_get_text_stat( text )) == NULL )
{
restart_kom("%s.\nText == %d, kom_errno == %d, errno == %d\n",
"create_text: can't get text-stat of newly created text",
text, kom_errno, errno);
}
t_stat->author = ACTPERS;
t_stat->creation_time = time(NULL);
t_stat->no_of_lines = count_lines( message );
t_stat->no_of_chars = s_strlen( message );
if ( create_text_add_miscs(text, no_of_misc, misc) != OK )
{
log("ERROR: create_text(): can't add miscs.\n");
return 0;
}
ACT_P->created_texts.texts
= srealloc(ACT_P->created_texts.texts,
++(ACT_P->created_texts.no_of_texts) * sizeof(Text_no));
ACT_P->created_texts.texts[ ACT_P->created_texts.no_of_texts - 1 ] = text;
ACT_P->created_lines += t_stat->no_of_lines;
ACT_P->created_bytes += t_stat->no_of_chars;
mark_person_as_changed (ACTPERS);
mark_text_as_changed (text);
send_async_new_text (text, t_stat); /* Send asynchronous message. */
return text;
}
/*
* Create an anonymous text.
*
* This is just like create_text, but the author of the text is set to
* Pers_no 0, to guarantee that the author is anonymous.
*
* (This should really be called create_ftp_text (in analogy with
* change-conference which was truncated to CC, which was transmuted
* into pepsi) but public demand said NO!)
*
* Returns text_no of the created text, or 0 if there was an error.
*/
extern Text_no
create_anonymous_text(const String message,
u_short no_of_misc,
Misc_info * misc )
{
Text_no text;
Text_stat * t_stat;
extern int errno;
CHK_LOGIN(0);
/* Check the length of the text. */
if ( s_strlen (message) > param.text_len )
{
kom_errno = KOM_LONG_STR;
return 0;
}
/* Check that the author is allowed to write this text number */
if (ok_to_create_next_text() == FALSE)
{
kom_errno = KOM_TEMPFAIL;
return 0;
}
/* Check all misc-items */
if ( create_text_check_misc(&no_of_misc, misc) != OK
|| (text = cached_create_text( message )) == 0)
{
return 0;
}
if ( (t_stat = cached_get_text_stat( text )) == NULL )
{
restart_kom("%screated text.\nText = %d, kom_errno = %d, errno = %d\n",
"create_anonymous_text: can't get text-stat of newly ",
text, kom_errno, errno);
}
t_stat->author = 0;
t_stat->creation_time = time(NULL);
t_stat->no_of_lines = count_lines( message );
t_stat->no_of_chars = s_strlen( message );
if ( create_text_add_miscs(text, no_of_misc, misc) != OK )
{
log("ERROR: create_text(): can't add miscs.\n");
return 0;
}
mark_text_as_changed (text);
/* Don't add this person to create - we want true anonymity! */
send_async_new_text (text, t_stat);
return text;
}
/*
* Delete a text.
*
* Only a supervisor of the author may delete a text.
*/
extern Success
delete_text( Text_no text_no )
{
Text_stat *text_s;
CHK_LOGIN(FAILURE);
GET_T_STAT(text_s, text_no, FAILURE);
if ( !is_supervisor(text_s->author, NULL, ACTPERS, ACT_P)
&& !ENA(admin, 5) )
{
kom_errno = (text_read_access(text_no, text_s)
? KOM_NOT_AUTHOR : KOM_NO_SUCH_TEXT);
return FAILURE;
}
return do_delete_text(text_no, text_s);
}
/*
* Lookup a text according to creation-time.
* The text-no of the text created closest before TIME is returned.
* The text text-no might not be readable.
*/
extern Success
get_last_text(struct tm *wanted_time,
Text_no *result)
{
struct tm *texttime;
Text_no lower = 0;
Text_no higher = query_next_text_num() - 1;
Text_stat *text_stat = NULL;
Text_no try;
Text_no middle;
/*
* We search for the text in the interval [lower, higher]
* (inclusive). Middle is set to the middle (rounded towards
* infinity), and we search from there and upward until we find a
* text.
*/
while (lower < higher)
{
middle = (lower + higher)/2 + 1; /* Binary search */
try = middle;
text_stat = NULL;
while (text_stat == NULL && try <= higher)
text_stat = cached_get_text_stat (try++);
if ( text_stat == NULL )
higher = middle - 1;
else
{
texttime = localtime(&text_stat->creation_time);
if (texttime->tm_year > wanted_time->tm_year
|| texttime->tm_mon > wanted_time->tm_mon
|| texttime->tm_mday > wanted_time->tm_mday
|| texttime->tm_hour > wanted_time->tm_hour
|| texttime->tm_min > wanted_time->tm_min
|| texttime->tm_sec > wanted_time->tm_sec)
{
lower = try - 1;
}
else /* The intervall is "halved" */
higher = middle - 1;
}
}
*result = lower;
return OK;
}
/*
* Return next existing text-no.
*/
extern Success
find_next_text_no (Text_no start,
Text_no *result)
{
Text_no highest = query_next_text_num();
Text_stat *text_s;
while (++start < highest)
{
text_s = cached_get_text_stat(start);
if (text_s != NULL && text_read_access(start, text_s) == TRUE)
{
*result = start;
return OK;
}
}
kom_errno = KOM_NO_SUCH_TEXT;
return FAILURE;
}
/*
* Return next/previous existing text-no.
*/
extern Success
find_previous_text_no (Text_no start,
Text_no *result)
{
Text_stat *text_s;
while (start-- > 0)
{
text_s = cached_get_text_stat(start);
if (text_s != NULL && text_read_access(start, text_s) == TRUE)
{
*result = start;
return OK;
}
}
kom_errno = KOM_NO_SUCH_TEXT;
return FAILURE;
}
/*
* Add a recipient to a text.
*/
extern Success
add_recipient( Text_no text_no,
Conf_no conf_no,
Info_type type ) /* recpt or cc_recpt */
{
Text_stat * t_stat;
Conference * conf_c;
CHK_LOGIN(FAILURE);
GET_T_STAT(t_stat, text_no, FAILURE);
GET_C_STAT(conf_c, conf_no, FAILURE);
if ( !text_read_access(text_no, t_stat ) && !ENA(admin, 4))
{
kom_errno = KOM_NO_SUCH_TEXT;
return FAILURE;
}
if ( is_recipient(conf_no, t_stat) )
{
kom_errno = KOM_ALREADY_RECIPIENT;
return FAILURE;
}
if ( count_recipients( t_stat ) >= param.max_recipients )
{
kom_errno = KOM_RECIPIENT_LIMIT;
return FAILURE;
}
switch ( type )
{
case recpt:
if ( do_add_recpt(text_no, t_stat, conf_no) != OK)
return FAILURE;
break;
case cc_recpt:
if ( do_add_cc_recpt(text_no, t_stat, conf_no) != OK)
return FAILURE;
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:
/* Fall through */
#ifndef COMPILE_CHECKS
default:
#endif
kom_errno = KOM_ILLEGAL_INFO_TYPE;
return FAILURE;
}
if ( t_stat->author != ACTPERS )
ADD_MISC(t_stat, sent_by, sender, ACTPERS);
ADD_MISC(t_stat, sent_at, sent_at, time(NULL));
mark_text_as_changed( text_no );
return OK;
}
/*
* Subtract a recipient from a text.
*
* This may be done by
* a) a supervisor of the author.
* b) a supervisor of the recipient.
* c) the sender of the text to the recipient
*/
extern Success
sub_recipient( Text_no text_no,
Conf_no conf_no)
{
Text_stat * text_s;
Conference * conf_c;
CHK_LOGIN(FAILURE);
GET_T_STAT(text_s, text_no, FAILURE);
if ( !text_read_access(text_no, text_s ) && !ENA(admin, 4))
{
kom_errno = KOM_NO_SUCH_TEXT;
return FAILURE;
}
if ( !is_recipient( conf_no, text_s ) )
{
kom_errno = KOM_NOT_RECIPIENT;
return FAILURE;
}
GET_C_STAT(conf_c, conf_no, FAILURE);
if ( !is_supervisor (text_s->author, NULL, ACTPERS, ACT_P)
&& !is_supervisor (conf_no, conf_c, ACTPERS, ACT_P)
&& !is_sender (text_s, conf_no) )
{
kom_errno = KOM_PERM;
return FAILURE;
}
return do_sub_recpt( text_no, text_s, conf_no, conf_c );
}
/*
* Add a comment-link between two existing texts.
*/
extern Success
add_comment(Text_no comment,
Text_no comment_to)
{
Text_stat *child_s, *parent_s;
CHK_LOGIN(FAILURE);
/* The following code can not cope with a text that is a comment
to itself. That is considered to be a bug. Work around it for
now, until a proper misc-info-list handling package is written.
FIXME +++ /// */
if (comment == comment_to)
{
kom_errno = KOM_INDEX_OUT_OF_RANGE;
return FAILURE;
}
GET_T_STAT(child_s, comment, FAILURE);
GET_T_STAT(parent_s, comment_to, FAILURE);
if ( check_comm(parent_s) != OK )
return FAILURE;
if ( !text_read_access(comment, child_s) )
{
kom_errno = KOM_NO_SUCH_TEXT;
return FAILURE;
}
/* Check if already comment */
if ( is_comment_to(comment, parent_s) )
{
kom_errno = KOM_ALREADY_COMMENT;
return FAILURE;
}
if ( do_add_comment(comment, comment_to) != OK)
return FAILURE;
if ( child_s->author != ACTPERS )
ADD_MISC(child_s, sent_by, sender, ACTPERS);
ADD_MISC(child_s, sent_at, sent_at, time(NULL));
mark_text_as_changed (comment);
return OK;
}
/*
* Delete a comment-link between two texts.
*
* This can be done by:
* a) a supervisor of any of the authors.
* b) the sender of the comment.
*/
extern Success
sub_comment( Text_no comment, /* 'comment' is no longer a comment */
Text_no parent ) /* to 'parent' */
{
Text_stat * text_s;
Text_stat * parent_s;
CHK_LOGIN(FAILURE);
GET_T_STAT(text_s, comment, FAILURE);
GET_T_STAT(parent_s, parent, FAILURE);
if ( !text_read_access(comment, text_s ) && !ENA(admin, 4))
{
kom_errno = KOM_NO_SUCH_TEXT;
return FAILURE;
}
if ( !text_read_access(parent, parent_s ) && !ENA(admin, 4))
{
kom_errno = KOM_NO_SUCH_TEXT;
return FAILURE;
}
if ( !is_comment_to( comment, parent_s ) )
{
kom_errno = KOM_NOT_COMMENT;
return FAILURE;
}
if ( !is_supervisor (text_s->author, NULL, ACTPERS, ACT_P)
&& !is_supervisor (parent_s->author, NULL, ACTPERS, ACT_P)
&& !is_comm_sender (text_s, parent) )
{
kom_errno = KOM_PERM;
return FAILURE;
}
do_sub_comment( comment, text_s, parent, parent_s );
return OK;
}
/*
* Add a footnote-link between two existing texts. Only the author
* may do this. The texts must have the same author.
*/
extern Success
add_footnote( Text_no footnote,
Text_no footnote_to )
{
Text_stat *text_s, *parent_s;
CHK_LOGIN(FAILURE);
GET_T_STAT(text_s, footnote, FAILURE);
GET_T_STAT(parent_s, footnote_to, FAILURE);
/* The following code can not cope with a text that is a footnote
to itself. That is considered to be a bug. Work around it for
now, until a proper misc-info-list handling package is written.
FIXME /// +++ */
if (footnote == footnote_to)
{
kom_errno = KOM_INDEX_OUT_OF_RANGE;
return FAILURE;
}
if ( !text_read_access(footnote, text_s)
|| !text_read_access(footnote_to, parent_s) )
{
kom_errno = KOM_NO_SUCH_TEXT;
return FAILURE;
}
if ( check_footn(parent_s) != OK )
return FAILURE;
if ( text_s->author != parent_s->author )
{
kom_errno = KOM_NOT_AUTHOR;
return FAILURE;
}
/* Check if already footnote */
if ( is_footnote_to(footnote, parent_s) )
{
kom_errno = KOM_ALREADY_FOOTNOTE;
return FAILURE;
}
if ( do_add_footnote(footnote, footnote_to) != OK)
return FAILURE;
ADD_MISC(text_s, sent_at, sent_at, time(NULL));
mark_text_as_changed( footnote );
return OK;
}
/*
* Delete a footnote-link between two texts.
* Only the author may do this.
*/
extern Success
sub_footnote( Text_no footnote, /* 'footnote' is no longer a */
Text_no parent ) /* footnote to 'parent' */
{
Text_stat * text_s;
Text_stat * parent_s;
CHK_LOGIN(FAILURE);
GET_T_STAT(text_s, footnote, FAILURE);
GET_T_STAT(parent_s, parent, FAILURE);
if ( !text_read_access(footnote, text_s ) && !ENA(admin, 4))
{
kom_errno = KOM_NO_SUCH_TEXT;
return FAILURE;
}
if ( !text_read_access(parent, parent_s ) && !ENA(admin, 4))
{
kom_errno = KOM_NO_SUCH_TEXT;
return FAILURE;
}
if ( !is_footnote_to( footnote, parent_s ) )
{
kom_errno = KOM_NOT_FOOTNOTE;
return FAILURE;
}
if ( text_s->author != ACTPERS && parent_s->author != ACTPERS )
{
kom_errno = KOM_PERM;
return FAILURE;
}
do_sub_footnote( footnote, text_s, parent, parent_s );
return OK;
}
/*
* Get mapping from Local_text_no to (global) Text_no for part of
* a conference.
*
* BUG: You should be allowed to get the mapping if it is a letterbox
* which is not unread_is_secret. +++ (Really? I don't think so. /ceder)
*/
extern Success
get_map (Conf_no conf_no,
Local_text_no first_local_no,
Local_text_no no_of_texts,
Text_list * result)
{
Conference * conf_c;
Local_text_no highest_wanted_no, highest;
Access acc;
CHK_LOGIN(FAILURE);
GET_C_STAT(conf_c, conf_no, FAILURE);
acc = access_perm (conf_no, conf_c, ACTPERS, ACT_P);
if ( acc <= none )
{
kom_errno = KOM_UNDEF_CONF;
return FAILURE;
}
if ( acc == read_protected )
{
kom_errno = KOM_ACCESS;
return FAILURE;
}
highest = conf_c->texts.first_local_no + conf_c->texts.no_of_texts;
if ( first_local_no >= highest )
{
kom_errno = KOM_NO_SUCH_LOCAL_TEXT;
return FAILURE;
}
result->first_local_no = max(conf_c->texts.first_local_no,
first_local_no);
highest_wanted_no = min(highest, first_local_no + no_of_texts);
if ( highest_wanted_no >= result->first_local_no )
result->no_of_texts = highest_wanted_no - result->first_local_no;
else
result->no_of_texts = 0;
result->texts = &conf_c->texts.texts[ result->first_local_no
- conf_c->texts.first_local_no ];
return OK;
}