#!/usr/bin/perl # Copyright 2002 by Paolo Piselli # This is a modified version of the bestcoastswing.com bulletin-board database. # It is meant for example purposes only and is not for distribution. # the root bulletin board directory $bbs_root = "XXXXXXXX/"; # bulletin board datafiles # contains number of forums - for id generation $bbs_datafile = $bbs_root."XXXXXXXX"; # the bulletin board data file contains the following fields: # nforums - number of forums in bbs # list of forums in "forumID=name" format $bbs_forumsfile = $bbs_root."XXXXXXXX"; # the root forum directory $forum_root = $bbs_root."forumID/"; # forum data files $forum_datafile = $forum_root."XXXXXXXX"; $forum_topicsfile = $forum_root."forum_topics.dat"; # the forum datafile contains the following fields: # forumid - the ID of this forum # forumname - the name of this forum # ntopics - number of topics in this forum # nposts - the number of posts in this forum # last-post - time() of last post # the root topic directory $topic_root = $forum_root."topicID/"; # topic datafiles $topic_datafile = $topic_root."XXXXXXXX"; $topic_postsfile = $topic_root."XXXXXXXX"; # the topic datafile contains the following fields: # forumid - the parent forum of this topic # topicid - the ID of this topic # topicname - the name of this topic # poster - userID of original poster # nposts - numper of posts in this topic # last-post - time() of last post in this topic # anonymous - if equals "true" poster name is kept hidden # post files $post_datafile = $topic_root."XXXXXXXX"; $post_textfile = $topic_root."XXXXXXXX"; # the post datafile contains the following fields: # forumid - the parent forum of this post # topicid - the parent topic of this post # postid - the ID of this post # post-time - the time() that this post was submitted # poster - the userID of the poster of this message # anonymous - if is "true" poster name is kept hidden # the post text file contains the following field: # body - the message body of the post # bbs functions # BBSGetForums() - returns hash with forums names indexed by forum ids # BBSIsAdmin( userID ) - returns true if user is a BBS admin # forum functions # ForumAdd( forum_name ) - returns forumid of new forum # ForumDelete( forumID ) - deletes forum # ForumGet( forumID ) - returns a hash_ref with forum info # ForumSet( forumID, forum_ref ) - sets forum info # ForumGetTopics( forumID ) - returns hash of topic update times by topicIDs # ForumIsAdmin( forumID, userID ) - returns true if user can admin this forum # ForumGetNewTopicID( forumID ) # ForumIncrementPosts( forumID ) # ForumSetLastPostTime( forumID, post-time ) # topic functions # TopicAdd( forumID, topic_name, post_ref, post_text_ref) - adds a topic # TopicDelete( forumID, topicID ) # TopicGet( forumID, topicID ) - returns hash_ref with topic info # TopicSet( forumID, topicID, topic_ref ) - sets topic info # TopicGetPosts( forumID, topicID ) - returns hash of post times by postIDs # TopicGetNewPostID( forumID, topicID ) # TopicSetLastPostTime( forumID, topicID, post-time ) # post functions # PostAdd( forumID, topicID, post_ref, post_text_ref ) - posts message # PostDelete( forumID, topicID, postID ) - # PostGet( forumID, topicID, postID ) - returns hash_ref with post info # PostGetText( forumID, topicID, postID ) - returns post text # PostSet( forumID, topicID, postID, post_ref ) - sets post info # PostSetText( forumID, topicID, postID, post_text_ref ) - sets post text # PostIsAdmin( forumID, topicID, postID, userID ) - returns true or false # BBS FUNCTIONS # function to return forums names and ids sub BBSGetForums { my( $forums_ref ); open( BBSFORUMS, "<".$bbs_forumsfile ); $forums_ref = ReadKeyValues( BBSFORUMS ); close( BBSFORUMS ); return $forums_ref; } # function to determine if user is a bbs admin sub BBSIsAdmin { my( $userID ) = @_; if( UserIsSuper( $userID ) ) { return 1; } return 0; } # FORUM FUNCTIONS # function to add a forum to the BBS sub ForumAdd { my( $forum_name ) = @_; my( $forum_dir, $forum_num, $forumID, $forums_ref, %forum ); # get new ID and lock bbs database open( BBSDATA, "<".$bbs_datafile ); flock( BBSDATA, 8 ); $bbs_data_ref = ReadKeyValues( BBSDATA ); $forum_num = ${$bbs_data_ref}{"nforums"}; $forumID = sprintf( "%08d", $forum_num ); ${$bbs_data_ref}{"nforums"}++; open( BBSDATA, ">".$bbs_datafile ); WriteKeyValues( BBSDATA, $bbs_data_ref ); # create new forum record $forum{"forumid"} = $forumID; $forum{"forumname"} = $forum_name; $forum{"ntopics"} = 0; $forum{"nposts"} = 0; $forum{"last-post"} = time(); $forum_dir = $forum_root; $forum_dir =~ s/forumID/forum$forumID/g; mkdir $forum_dir, 0755; ForumSet( $forumID, \%forum ); # also adds forum to bbs forums file # release lock on bbs database flock( BBSDATA, 2 ); close( BBSDATA ); return $forumID; } # function to delete a forum sub ForumDelete { my( $forumID ) = @_; my( $forum_topcis, $forum_data, $topicID, $topics_ref ); # delete all this forum's topics $topics_ref = ForumGetTopics( $forumID ); foreach $topicID (keys %{$topics_ref}) { TopicDelete( $forumID, $topicID ); } # delete data files $forum_data = $forum_datafile; $forum_data =~ s/forumID/forum$forumID/g; $forum_topics = $forum_topicsfile; $forum_topics =~ s/forumID/forum$forumID/g; unlink $forum_data, $forum_topics; # remove directory $forum_dir = $forum_root; $forum_dir =~ s/forumID/forum$forumID/g; rmdir $forum_dir; # remove the forum from the forum names file open( BBSFORUMS, "<".$bbs_forumsfile ); $forums_ref = ReadKeyValues( BBSFORUMS ); delete ${$forums_ref}{$forumID}; open( BBSFORUMS, ">".$bbs_forumsfile ); WriteKeyValues( BBSFORUMS, $forums_ref ); close( BBSFORUMS ); } # function to get forum hash ref sub ForumGet { my( $forumID ) = @_; my( $forum_data, $forum_ref ); $forum_data = $forum_datafile; $forum_data =~ s/forumID/forum$forumID/g; open( FORUMDATA, "<".$forum_data ); flock( FORUMDATA, 8 ); $forum_ref = ReadKeyValues( FORUMDATA ); flock( FORUMDATA, 2 ); close( FORUMDATA ); return $forum_ref; } # function to set forum info sub ForumSet { my( $forumID, $forum_ref ) = @_; my( $forum_data, $old_ref, $forums_ref ); $old_ref = ForumGet( $forumID ); $forum_data = $forum_datafile; $forum_data =~ s/forumID/forum$forumID/g; open( FORUMDATA, ">".$forum_data ); flock( FORUMDATA, 8 ); WriteKeyValues( FORUMDATA, $forum_ref ); flock( FORUMDATA, 2 ); close( FORUMDATA ); # if the name has changed, update the bbs forums file if( ${$forum_ref}{"forumname"} ne ${$old_ref}{"forumname"} ) { # add this forum to the forum names file open( BBSFORUMS, "<".$bbs_forumsfile ); $forums_ref = ReadKeyValues( BBSFORUMS ); ${$forums_ref}{$forumID} = ${$forum_ref}{"forumname"}; open( BBSFORUMS, ">".$bbs_forumsfile ); WriteKeyValues( BBSFORUMS, $forums_ref ); close( BBSFORUMS ); } } # function to get this forum's topics and topic ids sub ForumGetTopics { my( $forumID ) = @_; my( $topics_ref ); $forum_topics = $forum_topicsfile; $forum_topics =~ s/forumID/forum$forumID/g; open( FTOPICS, "<".$forum_topics ); $topics_ref = ReadKeyValues( FTOPICS ); close( FTOPICS ); return $topics_ref; } # function to test if a given forum iss administered by this user sub ForumIsAdmin { my( $forumID, $userID ) = @_; if( BBSIsAdmin( $userID ) ) { return 1; } return 0; } # function to atomically get a new topic id sub ForumGetNewTopicID { my( $forumID ) = @_; my( $forum_data, $topic_num, $topicID, $forum_ref ); $forum_data = $forum_datafile; $forum_data =~ s/forumID/forum$forumID/g; open( FORUMDATA, "<".$forum_data ); flock( FORUMDATA, 8 ); $forum_ref = ReadKeyValues( FORUMDATA ); $topic_num = ${$forum_ref}{"ntopics"}; $topicID = sprintf( "%08d", $topic_num ); ${$forum_ref}{"ntopics"}++; open( FORUMDATA, ">".$forum_data ); WriteKeyValues( FORUMDATA, $forum_ref ); flock( FORUMDATA, 2 ); close( FORUMDATA ); return $topicID; } # function to increment the number of posts in this forum sub ForumIncrementPosts { my( $forumID ) = @_; my( $forum_data, $forum_ref ); $forum_data = $forum_datafile; $forum_data =~ s/forumID/forum$forumID/g; open( FDATA, "<".$forum_data ); flock( FDATA, 8 ); $forum_ref = ReadKeyValues( FDATA ); ${$forum_ref}{"nposts"}++; open( FDATA, ">".$forum_data ); WriteKeyValues( FDATA, $forum_ref ); flock( FDATA, 2 ); close( FDATA ); } # function to set the last post time of this forum sub ForumSetLastPostTime { my( $forumID, $post_time ) = @_; my( $forum_ref, $forum_data ); $forum_data = $forum_datafile; $forum_data =~ s/forumID/forum$forumID/g; open( FORUMDATA, "<".$forum_data ); flock( FORUMDATA, 8 ); $forum_ref = ReadKeyValues( FORUMDATA ); ${$forum_ref}{"last-post"} = $post_time; open( FORUMDATA, ">".$forum_data ); WriteKeyValues( FORUMDATA, $forum_ref ); flock( FORUMDATA, 2 ); close( FORUMDATA ); } # TOPIC FUNCTIONS # function to add a topic to the BBS sub TopicAdd { my( $forumID, $topic_name, $post_ref, $post_text ) = @_; my( $topic_dir, $topic_num, $topicID, $postID, $topics_ref, %topic ); # get new ID and lock bbs database $topicID = ForumGetNewTopicID( $forumID ); # create new topic record $topic{"poster"} = ${$post_ref}{"poster"}; $topic{"topicid"} = $topicID; $topic{"forumid"} = $forumID; $topic{"topicname"} = $topic_name; $topic{"nposts"} = 0; $topic{"last-post"} = time(); $topic_dir = $topic_root; $topic_dir =~ s/forumID/forum$forumID/g; $topic_dir =~ s/topicID/topic$topicID/g; mkdir $topic_dir, 0755; TopicSet( $forumID, $topicID, \%topic ); # also adds topic to forum topics file $postID = PostAdd( $forumID, $topicID, $post_ref ); PostSetText( $forumID, $topicID, $postID, $post_text ); return $topicID; } # function to delete a topic sub TopicDelete { my( $forumID, $topicID ) = @_; my( $forum_topics, $topic_posts, $topic_data, $postID, $posts_ref ); # delete all this topic's posts $posts_ref = TopicGetPosts( $forumID, $topicID ); foreach $postID (keys %{$posts_ref}) { PostDelete( $forumID, $topicID, $postID ); } # delete data files $topic_data = $topic_datafile; $topic_data =~ s/forumID/forum$forumID/g; $topic_data =~ s/topicID/topic$topicID/g; $topic_posts = $topic_postsfile; $topic_posts =~ s/forumID/forum$forumID/g; $topic_posts =~ s/topicID/topic$topicID/g; unlink $topic_data, $topic_posts; # remove directory $topic_dir = $topic_root; $topic_dir =~ s/forumID/forum$forumID/g; $topic_dir =~ s/topicID/topic$topicID/g; rmdir $topic_dir; # remove the topic from the topic names file $forum_topics = $forum_topicsfile; $forum_topics =~ s/forumID/forum$forumID/g; open( FORUMTOPICS, "<".$forum_topics ); $topics_ref = ReadKeyValues( FORUMTOPICS ); delete ${$topics_ref}{$topicID}; open( FORUMTOPICS, ">".$forum_topics ); WriteKeyValues( FORUMTOPICS, $topics_ref ); close( FORUMTOPICS ); } # function to get topic hash ref sub TopicGet { my( $forumID, $topicID ) = @_; my( $topic_data, $topic_ref ); $topic_data = $topic_datafile; $topic_data =~ s/forumID/forum$forumID/g; $topic_data =~ s/topicID/topic$topicID/g; open( TOPICDATA, "<".$topic_data ); flock( TOPICDATA, 8 ); $topic_ref = ReadKeyValues( TOPICDATA ); flock( TOPICDATA, 2 ); close( TOPICDATA ); return $topic_ref; } # function to set topic info sub TopicSet { my( $forumID, $topicID, $topic_ref ) = @_; my( $forum_topics, $topic_data, $old_ref, $topics_ref ); $old_ref = TopicGet( $forumID, $topicID ); $topic_data = $topic_datafile; $topic_data =~ s/forumID/forum$forumID/g; $topic_data =~ s/topicID/topic$topicID/g; open( TOPICDATA, ">".$topic_data ); flock( TOPICDATA, 8 ); WriteKeyValues( TOPICDATA, $topic_ref ); flock( TOPICDATA, 2 ); close( TOPICDATA ); # if the name has changed, update the forum topics file if( ${$topic_ref}{"topicname"} ne ${$old_ref}{"topicname"} ) { # add this topic to the topic names file $forum_topics = $forum_topicsfile; $forum_topics =~ s/forumID/forum$forumID/g; open( FORUMTOPICS, "<".$forum_topics ); $topics_ref = ReadKeyValues( FORUMTOPICS ); ${$topics_ref}{$topicID} = ${$topic_ref}{"topicname"}; open( FORUMTOPICS, ">".$forum_topics ); WriteKeyValues( FORUMTOPICS, $topics_ref ); close( FORUMTOPICS ); } } # function to get this topic's posts and post ids sub TopicGetPosts { my( $forumID, $topicID ) = @_; my( $posts_ref ); $topic_posts = $topic_postsfile; $topic_posts =~ s/forumID/forum$forumID/g; $topic_posts =~ s/topicID/topic$topicID/g; open( POSTS, "<".$topic_posts ); $posts_ref = ReadKeyValues( POSTS ); close( POSTS ); return $posts_ref; } # function to atomically get a new post id sub TopicGetNewPostID { my( $forumID, $topicID ) = @_; my( $post_num, $postID, $topic_ref, $topic_data ); $topic_data = $topic_datafile; $topic_data =~ s/forumID/forum$forumID/g; $topic_data =~ s/topicID/topic$topicID/g; open( TOPICDATA, "<".$topic_data ); flock( TOPICDATA, 8 ); $topic_ref = ReadKeyValues( TOPICDATA ); $post_num = ${$topic_ref}{"nposts"}; $postID = sprintf( "%08d", $post_num ); ${$topic_ref}{"nposts"}++; open( TOPICDATA, ">".$topic_data ); WriteKeyValues( TOPICDATA, $topic_ref ); flock( TOPICDATA, 2 ); close( TOPICDATA ); ForumIncrementPosts( $forumID ); return $postID; } # function to set the last-post field sub TopicSetLastPostTime { my( $forumID, $topicID, $post_time ) = @_; my( $topic_ref, $topic_data ); $topic_data = $topic_datafile; $topic_data =~ s/forumID/forum$forumID/g; $topic_data =~ s/topicID/topic$topicID/g; open( TOPICDATA, "<".$topic_data ); flock( TOPICDATA, 8 ); $topic_ref = ReadKeyValues( TOPICDATA ); ${$topic_ref}{"last-post"} = $post_time; open( TOPICDATA, ">".$topic_data ); WriteKeyValues( TOPICDATA, $topic_ref ); flock( TOPICDATA, 2 ); close( TOPICDATA ); ForumSetLastPostTime( $forumID, $post_time ); } # POST FUNCTIONS # function to add a post to the BBS sub PostAdd { my( $forumID, $topicID, $post_ref ) = @_; my( $post_dir, $post_num, $postID, $posts_ref, %post ); # get new ID and lock bbs database $postID = TopicGetNewPostID( $forumID, $topicID ); # create new post record ${$post_ref}{"postid"} = $postID; ${$post_ref}{"topicid"} = $topicID; ${$post_ref}{"forumid"} = $forumID; ${$post_ref}{"post-time"} = time(); PostSet( $forumID, $topicID, $postID, $post_ref ); # also adds post to forum posts file TopicSetLastPostTime( $forumID, $topicID, ${$post_ref}{"post-time"} ); return $postID; } # function to delete a post sub PostDelete { my( $forumID, $topicID, $postID ) = @_; my( $topic_posts, $post_data, $post_text, $posts_ref ); # delete data files $post_data = $post_datafile; $post_data =~ s/forumID/forum$forumID/g; $post_data =~ s/topicID/topic$topicID/g; $post_data =~ s/postID/post$postID/g; $post_text = $post_textfile; $post_text =~ s/forumID/forum$forumID/g; $post_text =~ s/topicID/topic$topicID/g; $post_text =~ s/postID/post$postID/g; unlink $post_data, $post_text; # remove the post from the post names file $topic_posts = $topic_postsfile; $topic_posts =~ s/forumID/forum$forumID/g; $topic_posts =~ s/topicID/topic$topicID/g; open( TOPICPOSTS, "<".$topic_posts ); $posts_ref = ReadKeyValues( TOPICPOSTS ); delete ${$posts_ref}{$postID}; open( TOPICPOSTS, ">".$topic_posts ); WriteKeyValues( TOPICPOSTS, $posts_ref ); close( TOPICPOSTS ); } # function to get post hash ref sub PostGet { my( $forumID, $topicID, $postID ) = @_; my( $post_data, $post_ref ); $post_data = $post_datafile; $post_data =~ s/forumID/forum$forumID/g; $post_data =~ s/topicID/topic$topicID/g; $post_data =~ s/postID/post$postID/g; open( POSTDATA, "<".$post_data ); flock( POSTDATA, 8 ); $post_ref = ReadKeyValues( POSTDATA ); flock( POSTDATA, 2 ); close( POSTDATA ); return $post_ref; } # function to set post info sub PostSet { my( $forumID, $topicID, $postID, $post_ref ) = @_; my( $topic_posts, $post_data, $old_ref, $posts_ref ); $old_ref = PostGet( $forumID, $topicID, $postID ); $post_data = $post_datafile; $post_data =~ s/forumID/forum$forumID/g; $post_data =~ s/topicID/topic$topicID/g; $post_data =~ s/postID/post$postID/g; open( POSTDATA, ">".$post_data ); flock( POSTDATA, 8 ); WriteKeyValues( POSTDATA, $post_ref ); flock( POSTDATA, 2 ); close( POSTDATA ); # if the name has changed, update the forum posts file if( ${$post_ref}{"poster"} ne ${$old_ref}{"poster"} ) { # add this post to the post names file $topic_posts = $topic_postsfile; $topic_posts =~ s/forumID/forum$forumID/g; $topic_posts =~ s/topicID/topic$topicID/g; open( TOPICPOSTS, "<".$topic_posts ); $posts_ref = ReadKeyValues( TOPICPOSTS ); ${$posts_ref}{$postID} = ${$post_ref}{"postname"}; open( TOPICPOSTS, ">".$topic_posts ); WriteKeyValues( TOPICPOSTS, $posts_ref ); close( TOPICPOSTS ); } } # function to get post text sub PostGetText { my( $forumID, $topicID, $postID ) = @_; my( $post_text, $text_ref ); $post_text = $post_textfile; $post_text =~ s/forumID/forum$forumID/g; $post_text =~ s/topicID/topic$topicID/g; $post_text =~ s/postID/post$postID/g; open( POSTDATA, "<".$post_text ); flock( POSTDATA, 8 ); $text_ref = ReadTextValues( POSTDATA ); flock( POSTDATA, 2 ); close( POSTDATA ); return $text_ref; } # function to set post text sub PostSetText { my( $forumID, $topicID, $postID, $text_ref ) = @_; my( $post_text ); $post_text = $post_textfile; $post_text =~ s/forumID/forum$forumID/g; $post_text =~ s/topicID/topic$topicID/g; $post_text =~ s/postID/post$postID/g; open( POSTDATA, ">".$post_text ); flock( POSTDATA, 8 ); WriteTextValues( POSTDATA, $text_ref ); flock( POSTDATA, 2 ); close( POSTDATA ); } # function to determine if a user can edit a post sub PostIsAdmin { my( $forumID, $topicID, $postID, $userID ) = @_; if( UserIsSuper( $userID ) ) { return 1; } $post_ref = PostGet( $forumID, $topicID, $postID ); if( $userID eq ${$post_ref}{"poster"} ) { return 1; } return 0; }