Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • osian/sucs-site
  • kais58/sucs-site
  • imranh/sucs-site
  • foshjedi2004/sucs-site
  • gigosaurus/sucs-site
  • matstn/sucs-site
  • ripp_/sucs-site
  • eggnog/sucs-site
  • sucssite/sucs-site
  • elbows/sucs-site
  • realitykiller/sucs-site
  • crox/sucs-site
  • vectre/sucs-site
  • welshbyte/sucs-site
  • paperclipman/sucs-site
15 results
Show changes
Showing
with 664 additions and 1994 deletions
<?php
/***********************************************************************
Copyright (C) 2002-2008 PunBB
This file is part of PunBB.
PunBB 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 2 of the License,
or (at your option) any later version.
PunBB 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston,
MA 02111-1307 USA
************************************************************************/
define('PUN_ROOT', './');
require PUN_ROOT.'include/common.php';
if ($pun_user['g_read_board'] == '0')
message($lang_common['No view']);
$id = isset($_GET['id']) ? intval($_GET['id']) : 0;
if ($id < 1)
message($lang_common['Bad request']);
// Load the viewforum.php language file
require PUN_ROOT.'lang/'.$pun_user['language'].'/forum.php';
// Fetch some info about the forum
$result = $db->query('SELECT f.forum_name, f.redirect_url, f.moderators, f.num_topics, f.sort_by, fp.post_topics FROM '.$db->prefix.'forums AS f LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND f.id='.$id) or error('Unable to fetch forum info', __FILE__, __LINE__, $db->error());
if (!$db->num_rows($result))
message($lang_common['Bad request']);
$cur_forum = $db->fetch_assoc($result);
// Is this a redirect forum? In that case, redirect!
if ($cur_forum['redirect_url'] != '')
{
header('Location: '.$cur_forum['redirect_url']);
pun_exit();
}
// Sort out who the moderators are and if we are currently a moderator (or an admin)
$mods_array = array();
if ($cur_forum['moderators'] != '')
$mods_array = unserialize($cur_forum['moderators']);
$is_admmod = ($pun_user['g_id'] == PUN_ADMIN || ($pun_user['g_id'] == PUN_MOD && array_key_exists($pun_user['username'], $mods_array))) ? true : false;
// Can we or can we not post new topics?
if (($cur_forum['post_topics'] == '' && $pun_user['g_post_topics'] == '1') || $cur_forum['post_topics'] == '1' || $is_admmod)
$post_link = "\t\t".'<p class="postlink conr"><a href="post.php?fid='.$id.'">'.$lang_forum['Post topic'].'</a></p>'."\n";
else
$post_link = '';
// Determine the topic offset (based on $_GET['p'])
$num_pages = ceil($cur_forum['num_topics'] / $pun_user['disp_topics']);
$p = (!isset($_GET['p']) || !is_numeric($_GET['p']) || $_GET['p'] <= 1 || $_GET['p'] > $num_pages) ? 1 : $_GET['p'];
$start_from = $pun_user['disp_topics'] * ($p - 1);
// Generate paging links
$paging_links = $lang_common['Pages'].': '.paginate($num_pages, $p, 'viewforum.php?id='.$id);
$page_title = pun_htmlspecialchars($pun_config['o_board_title'].' / '.$cur_forum['forum_name']);
define('PUN_ALLOW_INDEX', 1);
require PUN_ROOT.'header.php';
?>
<div class="linkst">
<div class="inbox">
<p class="pagelink conl"><?php echo $paging_links ?></p>
<?php echo $post_link ?>
<ul><li><a href="index.php"><?php echo $lang_common['Index'] ?></a>&nbsp;</li><li>&raquo;&nbsp;<?php echo pun_htmlspecialchars($cur_forum['forum_name']) ?></li></ul>
<div class="clearer"></div>
</div>
</div>
<div id="vf" class="blocktable">
<h2><span><?php echo pun_htmlspecialchars($cur_forum['forum_name']) ?></span></h2>
<div class="box">
<div class="inbox">
<table cellspacing="0">
<thead>
<tr>
<th class="tcl" scope="col"><?php echo $lang_common['Topic'] ?></th>
<th class="tc2" scope="col"><?php echo $lang_common['Replies'] ?></th>
<th class="tc3" scope="col"><?php echo $lang_forum['Views'] ?></th>
<th class="tcr" scope="col"><?php echo $lang_common['Last post'] ?></th>
</tr>
</thead>
<tbody>
<?php
// Fetch list of topics to display on this page
if ($pun_user['is_guest'] || $pun_config['o_show_dot'] == '0')
{
// Without "the dot"
$sql = 'SELECT id, poster, subject, posted, last_post, last_post_id, last_poster, num_views, num_replies, closed, sticky, moved_to FROM '.$db->prefix.'topics WHERE forum_id='.$id.' ORDER BY sticky DESC, '.(($cur_forum['sort_by'] == '1') ? 'posted' : 'last_post').' DESC LIMIT '.$start_from.', '.$pun_user['disp_topics'];
}
else
{
// With "the dot"
switch ($db_type)
{
case 'mysql':
case 'mysqli':
$sql = 'SELECT p.poster_id AS has_posted, t.id, t.subject, t.poster, t.posted, t.last_post, t.last_post_id, t.last_poster, t.num_views, t.num_replies, t.closed, t.sticky, t.moved_to FROM '.$db->prefix.'topics AS t LEFT JOIN '.$db->prefix.'posts AS p ON t.id=p.topic_id AND p.poster_id='.$pun_user['id'].' WHERE t.forum_id='.$id.' GROUP BY t.id ORDER BY sticky DESC, '.(($cur_forum['sort_by'] == '1') ? 'posted' : 'last_post').' DESC LIMIT '.$start_from.', '.$pun_user['disp_topics'];
break;
case 'sqlite':
$sql = 'SELECT p.poster_id AS has_posted, t.id, t.subject, t.poster, t.posted, t.last_post, t.last_post_id, t.last_poster, t.num_views, t.num_replies, t.closed, t.sticky, t.moved_to FROM '.$db->prefix.'topics AS t LEFT JOIN '.$db->prefix.'posts AS p ON t.id=p.topic_id AND p.poster_id='.$pun_user['id'].' WHERE t.id IN(SELECT id FROM '.$db->prefix.'topics WHERE forum_id='.$id.' ORDER BY sticky DESC, '.(($cur_forum['sort_by'] == '1') ? 'posted' : 'last_post').' DESC LIMIT '.$start_from.', '.$pun_user['disp_topics'].') GROUP BY t.id ORDER BY t.sticky DESC, t.last_post DESC';
break;
default:
$sql = 'SELECT p.poster_id AS has_posted, t.id, t.subject, t.poster, t.posted, t.last_post, t.last_post_id, t.last_poster, t.num_views, t.num_replies, t.closed, t.sticky, t.moved_to FROM '.$db->prefix.'topics AS t LEFT JOIN '.$db->prefix.'posts AS p ON t.id=p.topic_id AND p.poster_id='.$pun_user['id'].' WHERE t.forum_id='.$id.' GROUP BY t.id, t.subject, t.poster, t.posted, t.last_post, t.last_post_id, t.last_poster, t.num_views, t.num_replies, t.closed, t.sticky, t.moved_to, p.poster_id ORDER BY sticky DESC, '.(($cur_forum['sort_by'] == '1') ? 'posted' : 'last_post').' DESC LIMIT '.$start_from.', '.$pun_user['disp_topics'];
break;
}
}
$result = $db->query($sql) or error('Unable to fetch topic list', __FILE__, __LINE__, $db->error());
// If there are topics in this forum.
if ($db->num_rows($result))
{
while ($cur_topic = $db->fetch_assoc($result))
{
$icon_text = $lang_common['Normal icon'];
$item_status = '';
$icon_type = 'icon';
if ($cur_topic['moved_to'] == null)
$last_post = '<a href="viewtopic.php?pid='.$cur_topic['last_post_id'].'#p'.$cur_topic['last_post_id'].'">'.format_time($cur_topic['last_post']).'</a> <span class="byuser">'.$lang_common['by'].'&nbsp;'.pun_htmlspecialchars($cur_topic['last_poster']).'</span>';
else
$last_post = '&nbsp;';
if ($pun_config['o_censoring'] == '1')
$cur_topic['subject'] = censor_words($cur_topic['subject']);
if ($cur_topic['moved_to'] != 0)
$subject = $lang_forum['Moved'].': <a href="viewtopic.php?id='.$cur_topic['moved_to'].'">'.pun_htmlspecialchars($cur_topic['subject']).'</a> <span class="byuser">'.$lang_common['by'].'&nbsp;'.pun_htmlspecialchars($cur_topic['poster']).'</span>';
else if ($cur_topic['closed'] == '0')
$subject = '<a href="viewtopic.php?id='.$cur_topic['id'].'">'.pun_htmlspecialchars($cur_topic['subject']).'</a> <span class="byuser">'.$lang_common['by'].'&nbsp;'.pun_htmlspecialchars($cur_topic['poster']).'</span>';
else
{
$subject = '<a href="viewtopic.php?id='.$cur_topic['id'].'">'.pun_htmlspecialchars($cur_topic['subject']).'</a> <span class="byuser">'.$lang_common['by'].'&nbsp;'.pun_htmlspecialchars($cur_topic['poster']).'</span>';
$icon_text = $lang_common['Closed icon'];
$item_status = 'iclosed';
}
if (!$pun_user['is_guest'] && $cur_topic['last_post'] > $pun_user['last_visit'] && $cur_topic['moved_to'] == null)
{
$icon_text .= ' '.$lang_common['New icon'];
$item_status .= ' inew';
$icon_type = 'icon inew';
$subject = '<strong>'.$subject.'</strong>';
$subject_new_posts = '<span class="newtext">[&nbsp;<a href="viewtopic.php?id='.$cur_topic['id'].'&amp;action=new" title="'.$lang_common['New posts info'].'">'.$lang_common['New posts'].'</a>&nbsp;]</span>';
}
else
$subject_new_posts = null;
// Should we display the dot or not? :)
if (!$pun_user['is_guest'] && $pun_config['o_show_dot'] == '1')
{
if ($cur_topic['has_posted'] == $pun_user['id'])
$subject = '<strong>&middot;</strong>&nbsp;'.$subject;
else
$subject = '&nbsp;&nbsp;'.$subject;
}
if ($cur_topic['sticky'] == '1')
{
$subject = '<span class="stickytext">'.$lang_forum['Sticky'].': </span>'.$subject;
$item_status .= ' isticky';
$icon_text .= ' '.$lang_forum['Sticky'];
}
$num_pages_topic = ceil(($cur_topic['num_replies'] + 1) / $pun_user['disp_posts']);
if ($num_pages_topic > 1)
$subject_multipage = '[ '.paginate($num_pages_topic, -1, 'viewtopic.php?id='.$cur_topic['id']).' ]';
else
$subject_multipage = null;
// Should we show the "New posts" and/or the multipage links?
if (!empty($subject_new_posts) || !empty($subject_multipage))
{
$subject .= '&nbsp; '.(!empty($subject_new_posts) ? $subject_new_posts : '');
$subject .= !empty($subject_multipage) ? ' '.$subject_multipage : '';
}
?>
<tr<?php if ($item_status != '') echo ' class="'.trim($item_status).'"'; ?>>
<td class="tcl">
<div class="intd">
<div class="<?php echo $icon_type ?>"><div class="nosize"><?php echo trim($icon_text) ?></div></div>
<div class="tclcon">
<?php echo $subject."\n" ?>
</div>
</div>
</td>
<td class="tc2"><?php echo ($cur_topic['moved_to'] == null) ? $cur_topic['num_replies'] : '&nbsp;' ?></td>
<td class="tc3"><?php echo ($cur_topic['moved_to'] == null) ? $cur_topic['num_views'] : '&nbsp;' ?></td>
<td class="tcr"><?php echo $last_post ?></td>
</tr>
<?php
}
}
else
{
?>
<tr>
<td class="tcl" colspan="4"><?php echo $lang_forum['Empty forum'] ?></td>
</tr>
<?php
}
?>
</tbody>
</table>
</div>
</div>
</div>
<div class="linksb">
<div class="inbox">
<p class="pagelink conl"><?php echo $paging_links ?></p>
<?php echo $post_link ?>
<ul><li><a href="index.php"><?php echo $lang_common['Index'] ?></a>&nbsp;</li><li>&raquo;&nbsp;<?php echo pun_htmlspecialchars($cur_forum['forum_name']) ?></li></ul>
<div class="clearer"></div>
</div>
</div>
<?php
$forum_id = $id;
$footer_style = 'viewforum';
require PUN_ROOT.'footer.php';
<?php
/***********************************************************************
Copyright (C) 2002-2008 PunBB
This file is part of PunBB.
PunBB 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 2 of the License,
or (at your option) any later version.
PunBB 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston,
MA 02111-1307 USA
************************************************************************/
define('PUN_ROOT', './');
require PUN_ROOT.'include/common.php';
if ($pun_user['g_read_board'] == '0')
message($lang_common['No view']);
$action = isset($_GET['action']) ? $_GET['action'] : null;
$id = isset($_GET['id']) ? intval($_GET['id']) : 0;
$pid = isset($_GET['pid']) ? intval($_GET['pid']) : 0;
if ($id < 1 && $pid < 1)
message($lang_common['Bad request']);
// Load the viewtopic.php language file
require PUN_ROOT.'lang/'.$pun_user['language'].'/topic.php';
// If a post ID is specified we determine topic ID and page number so we can redirect to the correct message
if ($pid)
{
$result = $db->query('SELECT topic_id FROM '.$db->prefix.'posts WHERE id='.$pid) or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
if (!$db->num_rows($result))
message($lang_common['Bad request']);
$id = $db->result($result);
// Determine on what page the post is located (depending on $pun_user['disp_posts'])
$result = $db->query('SELECT id FROM '.$db->prefix.'posts WHERE topic_id='.$id.' ORDER BY posted') or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
$num_posts = $db->num_rows($result);
for ($i = 0; $i < $num_posts; ++$i)
{
$cur_id = $db->result($result, $i);
if ($cur_id == $pid)
break;
}
++$i; // we started at 0
$_GET['p'] = ceil($i / $pun_user['disp_posts']);
}
// If action=new, we redirect to the first new post (if any)
else if ($action == 'new' && !$pun_user['is_guest'])
{
$result = $db->query('SELECT MIN(id) FROM '.$db->prefix.'posts WHERE topic_id='.$id.' AND posted>'.$pun_user['last_visit']) or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
$first_new_post_id = $db->result($result);
if ($first_new_post_id)
header('Location: viewtopic.php?pid='.$first_new_post_id.'#p'.$first_new_post_id);
else // If there is no new post, we go to the last post
header('Location: viewtopic.php?id='.$id.'&action=last');
pun_exit();
}
// If action=last, we redirect to the last post
else if ($action == 'last')
{
$result = $db->query('SELECT MAX(id) FROM '.$db->prefix.'posts WHERE topic_id='.$id) or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
$last_post_id = $db->result($result);
if ($last_post_id)
{
header('Location: viewtopic.php?pid='.$last_post_id.'#p'.$last_post_id);
pun_exit();
}
}
// Fetch some info about the topic
if (!$pun_user['is_guest'])
$result = $db->query('SELECT t.subject, t.closed, t.num_replies, t.sticky, f.id AS forum_id, f.forum_name, f.moderators, fp.post_replies, s.user_id AS is_subscribed FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id LEFT JOIN '.$db->prefix.'subscriptions AS s ON (t.id=s.topic_id AND s.user_id='.$pun_user['id'].') LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND t.id='.$id.' AND t.moved_to IS NULL') or error('Unable to fetch topic info', __FILE__, __LINE__, $db->error());
else
$result = $db->query('SELECT t.subject, t.closed, t.num_replies, t.sticky, f.id AS forum_id, f.forum_name, f.moderators, fp.post_replies, 0 FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND t.id='.$id.' AND t.moved_to IS NULL') or error('Unable to fetch topic info', __FILE__, __LINE__, $db->error());
if (!$db->num_rows($result))
message($lang_common['Bad request']);
$cur_topic = $db->fetch_assoc($result);
// Sort out who the moderators are and if we are currently a moderator (or an admin)
$mods_array = ($cur_topic['moderators'] != '') ? unserialize($cur_topic['moderators']) : array();
$is_admmod = ($pun_user['g_id'] == PUN_ADMIN || ($pun_user['g_id'] == PUN_MOD && array_key_exists($pun_user['username'], $mods_array))) ? true : false;
// Can we or can we not post replies?
if ($cur_topic['closed'] == '0')
{
if (($cur_topic['post_replies'] == '' && $pun_user['g_post_replies'] == '1') || $cur_topic['post_replies'] == '1' || $is_admmod)
$post_link = '<a href="post.php?tid='.$id.'">'.$lang_topic['Post reply'].'</a>';
else
$post_link = '&nbsp;';
}
else
{
$post_link = $lang_topic['Topic closed'];
if ($is_admmod)
$post_link .= ' / <a href="post.php?tid='.$id.'">'.$lang_topic['Post reply'].'</a>';
}
// Determine the post offset (based on $_GET['p'])
$num_pages = ceil(($cur_topic['num_replies'] + 1) / $pun_user['disp_posts']);
$p = (!isset($_GET['p']) || !is_numeric($_GET['p']) || $_GET['p'] <= 1 || $_GET['p'] > $num_pages) ? 1 : $_GET['p'];
$start_from = $pun_user['disp_posts'] * ($p - 1);
// Generate paging links
$paging_links = $lang_common['Pages'].': '.paginate($num_pages, $p, 'viewtopic.php?id='.$id);
if ($pun_config['o_censoring'] == '1')
$cur_topic['subject'] = censor_words($cur_topic['subject']);
$quickpost = false;
if ($pun_config['o_quickpost'] == '1' &&
!$pun_user['is_guest'] &&
($cur_topic['post_replies'] == '1' || ($cur_topic['post_replies'] == '' && $pun_user['g_post_replies'] == '1')) &&
($cur_topic['closed'] == '0' || $is_admmod))
{
$required_fields = array('req_message' => $lang_common['Message']);
$quickpost = true;
}
if (!$pun_user['is_guest'] && $pun_config['o_subscriptions'] == '1')
{
if ($cur_topic['is_subscribed'])
// I apologize for the variable naming here. It's a mix of subscription and action I guess :-)
$subscraction = '<p class="subscribelink clearb">'.$lang_topic['Is subscribed'].' - <a href="misc.php?unsubscribe='.$id.'">'.$lang_topic['Unsubscribe'].'</a></p>'."\n";
else
$subscraction = '<p class="subscribelink clearb"><a href="misc.php?subscribe='.$id.'">'.$lang_topic['Subscribe'].'</a></p>'."\n";
}
else
$subscraction = '<div class="clearer"></div>'."\n";
$page_title = pun_htmlspecialchars($pun_config['o_board_title'].' / '.$cur_topic['subject']);
define('PUN_ALLOW_INDEX', 1);
require PUN_ROOT.'header.php';
?>
<div class="linkst">
<div class="inbox">
<p class="pagelink conl"><?php echo $paging_links ?></p>
<p class="postlink conr"><?php echo $post_link ?></p>
<ul><li><a href="index.php"><?php echo $lang_common['Index'] ?></a></li><li>&nbsp;&raquo;&nbsp;<a href="viewforum.php?id=<?php echo $cur_topic['forum_id'] ?>"><?php echo pun_htmlspecialchars($cur_topic['forum_name']) ?></a></li><li>&nbsp;&raquo;&nbsp;<?php echo pun_htmlspecialchars($cur_topic['subject']) ?></li></ul>
<div class="clearer"></div>
</div>
</div>
<?php
require PUN_ROOT.'include/parser.php';
$bg_switch = true; // Used for switching background color in posts
$post_count = 0; // Keep track of post numbers
// Retrieve the posts (and their respective poster/online status)
$result = $db->query('SELECT u.email, u.title, u.url, u.location, u.use_avatar, u.signature, u.email_setting, u.num_posts, u.registered, u.admin_note, p.id, p.poster AS username, p.poster_id, p.poster_ip, p.poster_email, p.message, p.hide_smilies, p.posted, p.edited, p.edited_by, g.g_id, g.g_user_title, o.user_id AS is_online FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'users AS u ON u.id=p.poster_id INNER JOIN '.$db->prefix.'groups AS g ON g.g_id=u.group_id LEFT JOIN '.$db->prefix.'online AS o ON (o.user_id=u.id AND o.user_id!=1 AND o.idle=0) WHERE p.topic_id='.$id.' ORDER BY p.id LIMIT '.$start_from.','.$pun_user['disp_posts'], true) or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
while ($cur_post = $db->fetch_assoc($result))
{
$post_count++;
$user_avatar = '';
$user_info = array();
$user_contacts = array();
$post_actions = array();
$is_online = '';
$signature = '';
// If the poster is a registered user.
if ($cur_post['poster_id'] > 1)
{
$username = '<a href="profile.php?id='.$cur_post['poster_id'].'">'.pun_htmlspecialchars($cur_post['username']).'</a>';
$user_title = get_title($cur_post);
if ($pun_config['o_censoring'] == '1')
$user_title = censor_words($user_title);
// Format the online indicator
$is_online = ($cur_post['is_online'] == $cur_post['poster_id']) ? '<strong>'.$lang_topic['Online'].'</strong>' : $lang_topic['Offline'];
/* begin SUCS avatar/hackergotchi hack
if ($pun_config['o_avatars'] == '1' && $cur_post['use_avatar'] == '1' && $pun_user['show_avatars'] != '0')
{
if ($img_size = @getimagesize($pun_config['o_avatars_dir'].'/'.$cur_post['poster_id'].'.gif'))
$user_avatar = '<img src="'.$pun_config['o_avatars_dir'].'/'.$cur_post['poster_id'].'.gif" '.$img_size[3].' alt="" />';
else if ($img_size = @getimagesize($pun_config['o_avatars_dir'].'/'.$cur_post['poster_id'].'.jpg'))
$user_avatar = '<img src="'.$pun_config['o_avatars_dir'].'/'.$cur_post['poster_id'].'.jpg" '.$img_size[3].' alt="" />';
*/
if ($img_size = @getimagesize("/var/www/sucssite/htdocs/pictures/people".'/'.$cur_post['username'].'.png')) {
$user_avatar = '<img src="/pictures/people/'.$cur_post['username'].'.png" '.$img_size[3].' alt="" />';
} else {
$user_avatar = '';
}
// We only show location, register date, post count and the contact links if "Show user info" is enabled
if ($pun_config['o_show_user_info'] == '1')
{
if ($cur_post['location'] != '')
{
if ($pun_config['o_censoring'] == '1')
$cur_post['location'] = censor_words($cur_post['location']);
$user_info[] = '<dd>'.$lang_topic['From'].': '.pun_htmlspecialchars($cur_post['location']);
}
//This is totally irrelavent to SUCS
//$user_info[] = '<dd>'.$lang_common['Registered'].': '.date($pun_config['o_date_format'], $cur_post['registered']);
if ($pun_config['o_show_post_count'] == '1' || $pun_user['g_id'] < PUN_GUEST)
$user_info[] = '<dd>'.$lang_common['Posts'].': '.$cur_post['num_posts'];
// Now let's deal with the contact links (E-mail and URL)
if (($cur_post['email_setting'] == '0' && !$pun_user['is_guest']) || $pun_user['g_id'] < PUN_GUEST)
$user_contacts[] = '<a href="mailto:'.$cur_post['email'].'">'.$lang_common['E-mail'].'</a>';
else if ($cur_post['email_setting'] == '1' && !$pun_user['is_guest'])
$user_contacts[] = '<a href="misc.php?email='.$cur_post['poster_id'].'">'.$lang_common['E-mail'].'</a>';
if ($cur_post['url'] != '')
$user_contacts[] = '<a href="'.pun_htmlspecialchars($cur_post['url']).'">'.$lang_topic['Website'].'</a>';
}
if ($pun_user['g_id'] < PUN_GUEST)
{
$user_info[] = '<dd>IP: <a href="moderate.php?get_host='.$cur_post['id'].'">'.$cur_post['poster_ip'].'</a>';
if ($cur_post['admin_note'] != '')
$user_info[] = '<dd>'.$lang_topic['Note'].': <strong>'.pun_htmlspecialchars($cur_post['admin_note']).'</strong>';
}
}
// If the poster is a guest (or a user that has been deleted)
else
{
$username = pun_htmlspecialchars($cur_post['username']);
$user_title = get_title($cur_post);
if ($pun_user['g_id'] < PUN_GUEST)
$user_info[] = '<dd>IP: <a href="moderate.php?get_host='.$cur_post['id'].'">'.$cur_post['poster_ip'].'</a>';
if ($pun_config['o_show_user_info'] == '1' && $cur_post['poster_email'] != '' && !$pun_user['is_guest'])
$user_contacts[] = '<a href="mailto:'.$cur_post['poster_email'].'">'.$lang_common['E-mail'].'</a>';
}
// Generation post action array (quote, edit, delete etc.)
if (!$is_admmod)
{
if (!$pun_user['is_guest'])
$post_actions[] = '<li class="postreport"><a href="misc.php?report='.$cur_post['id'].'">'.$lang_topic['Report'].'</a>';
if ($cur_topic['closed'] == '0')
{
if ($cur_post['poster_id'] == $pun_user['id'])
{
if ((($start_from + $post_count) == 1 && $pun_user['g_delete_topics'] == '1') || (($start_from + $post_count) > 1 && $pun_user['g_delete_posts'] == '1'))
$post_actions[] = '<li class="postdelete"><a href="delete.php?id='.$cur_post['id'].'">'.$lang_topic['Delete'].'</a>';
if ($pun_user['g_edit_posts'] == '1')
$post_actions[] = '<li class="postedit"><a href="edit.php?id='.$cur_post['id'].'">'.$lang_topic['Edit'].'</a>';
}
if (($cur_topic['post_replies'] == '' && $pun_user['g_post_replies'] == '1') || $cur_topic['post_replies'] == '1')
$post_actions[] = '<li class="postquote"><a href="post.php?tid='.$id.'&amp;qid='.$cur_post['id'].'">'.$lang_topic['Quote'].'</a>';
}
}
else
$post_actions[] = '<li class="postreport"><a href="misc.php?report='.$cur_post['id'].'">'.$lang_topic['Report'].'</a>'.$lang_topic['Link separator'].'</li><li class="postdelete"><a href="delete.php?id='.$cur_post['id'].'">'.$lang_topic['Delete'].'</a>'.$lang_topic['Link separator'].'</li><li class="postedit"><a href="edit.php?id='.$cur_post['id'].'">'.$lang_topic['Edit'].'</a>'.$lang_topic['Link separator'].'</li><li class="postquote"><a href="post.php?tid='.$id.'&amp;qid='.$cur_post['id'].'">'.$lang_topic['Quote'].'</a>';
// Switch the background color for every message.
$bg_switch = ($bg_switch) ? $bg_switch = false : $bg_switch = true;
$vtbg = ($bg_switch) ? ' roweven' : ' rowodd';
// Perform the main parsing of the message (BBCode, smilies, censor words etc)
$cur_post['message'] = parse_message($cur_post['message'], $cur_post['hide_smilies']);
// Do signature parsing/caching
if ($cur_post['signature'] != '' && $pun_user['show_sig'] != '0')
{
if (isset($signature_cache[$cur_post['poster_id']]))
$signature = $signature_cache[$cur_post['poster_id']];
else
{
$signature = parse_signature($cur_post['signature']);
$signature_cache[$cur_post['poster_id']] = $signature;
}
}
?>
<div id="p<?php echo $cur_post['id'] ?>" class="blockpost<?php echo $vtbg ?><?php if (($post_count + $start_from) == 1) echo ' firstpost'; ?>">
<h2><span><span class="conr">#<?php echo ($start_from + $post_count) ?>&nbsp;</span><a href="viewtopic.php?pid=<?php echo $cur_post['id'].'#p'.$cur_post['id'] ?>"><?php echo format_time($cur_post['posted']) ?></a></span></h2>
<div class="box">
<div class="inbox">
<div class="postleft">
<dl>
<dt><strong><?php echo $username ?></strong></dt>
<?php /*
<dd class="usertitle"><strong><?php echo $user_title ?></strong></dd> */ ?>
<dd class="postavatar"><?php echo $user_avatar ?></dd>
<?php if (count($user_info)) echo "\t\t\t\t\t".implode('</dd>'."\n\t\t\t\t\t", $user_info).'</dd>'."\n"; ?>
<?php if (count($user_contacts)) echo "\t\t\t\t\t".'<dd class="usercontacts">'.implode('&nbsp;&nbsp;', $user_contacts).'</dd>'."\n"; ?>
</dl>
</div>
<div class="postright">
<h3><?php if (($post_count + $start_from) > 1) echo ' Re: '; ?><?php echo pun_htmlspecialchars($cur_topic['subject']) ?></h3>
<div class="postmsg">
<?php echo $cur_post['message']."\n" ?>
<?php if ($cur_post['edited'] != '') echo "\t\t\t\t\t".'<p class="postedit"><em>'.$lang_topic['Last edit'].' '.pun_htmlspecialchars($cur_post['edited_by']).' ('.format_time($cur_post['edited']).')</em></p>'."\n"; ?>
</div>
<?php if ($signature != '') echo "\t\t\t\t".'<div class="postsignature"><hr />'.$signature.'</div>'."\n"; ?>
</div>
<div class="clearer"></div>
<div class="postfootleft"><?php if ($cur_post['poster_id'] > 1) echo '<p>'.$is_online.'</p>'; ?></div>
<div class="postfootright"><?php echo (count($post_actions)) ? '<ul>'.implode($lang_topic['Link separator'].'</li>', $post_actions).'</li></ul></div>'."\n" : '<div>&nbsp;</div></div>'."\n" ?>
</div>
</div>
</div>
<?php
}
?>
<div class="postlinksb">
<div class="inbox">
<p class="postlink conr"><?php echo $post_link ?></p>
<p class="pagelink conl"><?php echo $paging_links ?></p>
<ul><li><a href="index.php"><?php echo $lang_common['Index'] ?></a></li><li>&nbsp;&raquo;&nbsp;<a href="viewforum.php?id=<?php echo $cur_topic['forum_id'] ?>"><?php echo pun_htmlspecialchars($cur_topic['forum_name']) ?></a></li><li>&nbsp;&raquo;&nbsp;<?php echo pun_htmlspecialchars($cur_topic['subject']) ?></li></ul>
<?php echo $subscraction ?>
</div>
</div>
<?php
// Display quick post if enabled
if ($quickpost)
{
?>
<div class="blockform">
<h2><span><?php echo $lang_topic['Quick post'] ?></span></h2>
<div class="box">
<form method="post" action="post.php?tid=<?php echo $id ?>" onsubmit="this.submit.disabled=true;if(process_form(this)){return true;}else{this.submit.disabled=false;return false;}">
<div class="inform">
<fieldset>
<legend><?php echo $lang_common['Write message legend'] ?></legend>
<div class="infldset txtarea">
<input type="hidden" name="form_sent" value="1" />
<input type="hidden" name="form_user" value="<?php echo (!$pun_user['is_guest']) ? pun_htmlspecialchars($pun_user['username']) : 'Guest'; ?>" />
<label><textarea name="req_message" rows="7" cols="75" tabindex="1"></textarea></label>
<ul class="bblinks">
<li><a href="help.php#bbcode" onclick="window.open(this.href); return false;"><?php echo $lang_common['BBCode'] ?></a>: <?php echo ($pun_config['p_message_bbcode'] == '1') ? $lang_common['on'] : $lang_common['off']; ?></li>
<li><a href="help.php#img" onclick="window.open(this.href); return false;"><?php echo $lang_common['img tag'] ?></a>: <?php echo ($pun_config['p_message_img_tag'] == '1') ? $lang_common['on'] : $lang_common['off']; ?></li>
<li><a href="help.php#smilies" onclick="window.open(this.href); return false;"><?php echo $lang_common['Smilies'] ?></a>: <?php echo ($pun_config['o_smilies'] == '1') ? $lang_common['on'] : $lang_common['off']; ?></li>
</ul>
</div>
</fieldset>
</div>
<p><input type="submit" name="submit" tabindex="2" value="<?php echo $lang_common['Submit'] ?>" accesskey="s" /></p>
</form>
</div>
</div>
<?php
}
// Increment "num_views" for topic
$low_prio = ($db_type == 'mysql') ? 'LOW_PRIORITY ' : '';
$db->query('UPDATE '.$low_prio.$db->prefix.'topics SET num_views=num_views+1 WHERE id='.$id) or error('Unable to update topic', __FILE__, __LINE__, $db->error());
$forum_id = $cur_topic['forum_id'];
$footer_style = 'viewtopic';
require PUN_ROOT.'footer.php';
<?
function sanitizePhone($phone){
return preg_replace("/[ ()]/", "", $phone);
<?php
function sanitizePhone($phone)
{
return preg_replace("/[ ()]/", "", $phone);
}
function sanitizeAddress($address){
return str_replace(array("\r\n","\r"),array("\n","\n"),$address);
function sanitizeAddress($address)
{
return str_replace(array("\r\n", "\r"), array("\n", "\n"), $address);
}
function sanitizePostcode($postcode)
{
//force uppercase, remove any possible stupid spaces and add the single space in the correct place
$postcode = strtoupper($postcode);
$postcode = str_replace(" ", "", $postcode);
return substr_replace($postcode, " ", -3, 0);
}
?>
<?
<?php
// To use:
// include_once "session.php"
// $mysession = new Session;
//
//
// $mysession->loggedin is TRUE if they have logged in
//
// other attributes are :
......@@ -11,336 +11,348 @@
// lastseen - unix timestamp for their previous page access
// data - var/array for persistant data, commit by calling the 'save' method
// Session management and authentication mechanism.
class Session {
public $loggedin=FALSE; // Is this a valid logged in user ?
public $username=''; // Username
public $fullname; // Fullname
public $email=0; // Email waiting?
public $email_forward; // Email forwarded?
public $groups =array(); // users groups
public $data=''; // Var/array for session persistant data
public $token=''; // session identifier
public $logintime=''; // Time which user last gave us credentials
public $lastseen=''; // Time of last page request
private $timeout = 2880; // Idle timeout limit in minutes (session deleted), 2880 == 48 hours
private $anonymous_timeout = 120; // Idle timeout limit for sessions which aren't logged in (set lower to stop the session table getting swamped)
private $secure_timeout = 30; // Idle timeout limit in minutes (consider session less secure, require reauth for sensitive ops)
private $max_session_length = 11520; // maximum length of a session, 11520 == 8 days
private $table = "session"; // session storage table (const)
private $datahash=''; // hash of data field
// Create a new (insecure) session
private function newsession()
{
global $DB, $preferred_hostname;
$token = $this->genSessionID();
$DB->Execute("insert into {$this->table} (hash, lastseen, ip) values (?,NOW(),?)", array($token, $_SERVER['REMOTE_ADDR']));
setcookie("sucssite_session", $token, NULL, "/", $preferred_hostname);
// delete loggedin cookie if it exists
setcookie("sucssite_loggedin", FALSE, time(), "/");
$this->token = $token;
return;
}
public function isSecure()
{
global $DB;
// is user coming from the IP address they were when they logged in?
if ($detail['ip'] != $_SERVER['REMOTE_ADDR']) {
return false;
} elseif (time() > ($this->logintime + $this->secure_timeout)) {
// has it been too long since we last asked for credentials?
return false;
}
}
// Public Object constructor
function __construct()
{
global $DB, $preferred_hostname, $baseurl;
unset($token);
// if user requests a page via HTTP and claims to be logged in, bump them to HTTPS
if (!isset($_SERVER['HTTPS']) && (@$_COOKIE['sucssite_loggedin'] == "true")) {
header("HTTP/1.0 307 Temporary redirect");
header("Location: https://{$preferred_hostname}{$baseurl}{$_SERVER['PATH_INFO']}");
return;
}
// The possible form elements
$submit = @$_POST['Login'];
$logout = @$_POST['Logout'];
$session_user = strtolower(@$_POST['session_user']);
$session_pass = @$_POST['session_pass'];
// We havent logged them in yet
$this->loggedin = FALSE;
// Time out any old sessions
$DB->Execute(
"delete from {$this->table} where lastseen < NOW() - '{$this->timeout} minutes'::reltime ".
"or logintime < NOW() - '{$this->max_session_length} minutes'::reltime ".
"or (username IS NULL AND lastseen < NOW() - '{$this->anonymous_timeout} minutes'::reltime)"
);
// the possible token data passed from a form
if (isset($_REQUEST['token']))
$token = $_REQUEST['token'];
// Check if we were handed a specific token identifier
// Otherwise use the value from the cookie we gave out
if (!isset($token) && isset($_COOKIE['sucssite_session']))
$token=@$_COOKIE['sucssite_session'];
if (isset($token)) $this->token = $token;
// Log them out if they ask
if ($logout=="Logout") {
$this->logout();
return;
}
// Okay, so we still dont have a session id
// so issue a new one and go back to core
if (!isset($token))
{
$this->newsession();
return;
}
// Is this a login attempt ?
if ($submit != '' && $session_user != '' && $session_pass != '')
{
$this->session_init($session_user, $session_pass);
}
// Retrieve session information
$oldsess=$DB->GetAll("select * from {$this->table} where hash=?", array($this->token));
if (!$oldsess || count($oldsess) < 1) {
trigger_error("Session timed out", E_USER_NOTICE);
$this->newsession();
return;
}
// Extract detail of session for pass-back
$detail = $oldsess[0];
$this->data = unserialize((string)$detail['data']);
$this->lastseen = strtotime($detail['lastseen']);
$this->logintime = strtotime($detail['logintime']);
$this->datahash = md5(serialize($this->data));
// are we actually logged in, fill in more
if ($detail['username']) {
// Are we using HTTPS?
if (!isset($_SERVER['HTTPS'])) {
trigger_error("Insecure Connection", E_USER_NOTICE);
$this->loggedin = FALSE;
return;
}
$this->username=$detail['username'];
$this->fetch_detail($detail['username']);
$this->loggedin = TRUE;
}
// update time stamp
$DB->Execute( "update {$this->table} set lastseen=NOW() where hash=?", array($this->token));
// check to see if there any messages stored for this user
if (isset($this->data['messages'])) {
global $messages;
if (is_array($messages)) {
$messages += $this->data['messages'];
} else {
$messages = $this->data['messages'];
}
unset($this->data['messages']);
$this->save();
}
}
// generate a string suitable to be used as a session ID
private function genSessionID()
{
global $DB;
$try = 0;
$tt=date("D M d H:i:s Y");
$ip = $_SERVER['REMOTE_ADDR'];
$nonce = rand(); // this should stop session IDs being (easily) guessable by someone with the algorithm
do {
$token = md5("$ip$tt$nonce".$try++);
$old = $DB->GetAll("select hash from {$this->table} where hash=?", array($token));
} while ($old);
return $token;
}
// Public function: Store the session data away in the database
public function save( )
{
global $DB;
$newhash = md5(serialize($this->data));
if ($newhash == $this->datahash) {
// no change in data, dont save
return;
}
$DB->Execute("update {$this->table} set data=? where hash=?", array(serialize($this->data),$this->token));
}
// Public function: force a logout of the session
public function logout( )
{
global $DB;
$DB->Execute("delete from {$this->table} where hash=?", array($this->token));
$this->newsession();
$this->loggedin = FALSE;
setcookie("sucssite_loggedin", FALSE, time(), "/");
}
// Fill out any extra details we know about the user
private function fetch_detail( $user )
{
if (!($ldap = @ldap_connect("ldap://localhost"))) {
trigger_error("LDAP connect failed", E_USER_ERROR);
return FALSE;
}
$info = $this->ldap_getuser($ldap, $user);
if (!$info) return FALSE;
ldap_close($ldap);
// Check the user's email status
$mailstat = @stat("/var/spool/mail/".$user);
if ($mailstat[size]>0) {
if ($mailstat[mtime]>$mailstat[atime]) $this->email = 2;
else $this->email = 1;
}
if (file_exists($info['homedirectory'][0]."/.forward")) {
$forward = file($info['homedirectory'][0]."/.forward");
$this->email_forward = preg_replace("/\n/", "", $forward[0]);
}
$this->fullname = $info['cn'][0];
$this->groups = $info['grouplist'];
}
/* check using mod_auth_externals helper
private function check_pass($user, $pass)
{
if ($fd === FALSE) {
$this->errormsg = "Auth system error";
return FALSE;
}
fwrite($fd, "$user\n");
fwrite($fd, "$pass\n");
$ret = pclose($fd);
if ($ret == 0) return TRUE;
$this->autherror = "u='$user' p='$pass' ret=$ret";
$this->errormsg = "Invalid Username or Password";
return FALSE;
}
*/
// Get a users full record from ldap
private function ldap_getuser($ldap, $user)
{
// publically bind to find user
ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);
if (!($bind=@ldap_bind($ldap))) {
trigger_error("LDAP bind failed", E_USER_ERROR);
return NULL;
}
// find the user
if (!($search=@ldap_search($ldap, "dc=sucs,dc=org", "(&(uid=$user))"))) {
trigger_error("LDAP search fail", E_USER_ERROR);
return NULL;
}
$n = ldap_count_entries($ldap, $search);
if ($n < 1) {
trigger_error("Username or Password Incorrect", E_USER_WARNING);
return NULL;
}
$info = ldap_get_entries($ldap, $search);
if (($grpsearch=@ldap_search($ldap, "ou=Group,dc=sucs,dc=org", "memberuid=$user"))) {
$gn = ldap_count_entries($ldap,$grpsearch);
$gpile = ldap_get_entries($ldap, $grpsearch);
$glist=array();
for ($i=0;$i<$gn;$i++) {
$glist[ $gpile[$i]['cn'][0] ] = $gpile[$i]['gidnumber'][0];
}
$info[0]['grouplist'] = $glist;
}
return $info[0];
}
/* check using ldap directly */
public function check_pass($user, $pass)
{
// Open connection
if (!($ldap = @ldap_connect("ldap://localhost"))) {
trigger_error("LDAP connect failed", E_USER_ERROR);
return FALSE;
}
$info = $this->ldap_getuser($ldap, $user);
if (!$info) return FALSE;
$real = @ldap_bind($ldap, $info['dn'], $pass);
ldap_close($ldap);
if ($real) return TRUE;
trigger_error("Username or Password Incorrect", E_USER_WARNING);
return FALSE;
}
// Private function: process login form
private function session_init($user, $pass)
{
global $DB, $preferred_hostname;
// Check that this is a valid session start
// This prevents replay attacks
$sess = $DB->GetAll("select * from {$this->table} where hash=? and username is NULL", array($this->token));
if (!$sess || count($sess)<1) {
trigger_error("Invalid session, login again.", E_USER_WARNING);
return;
}
if (!$this->check_pass($user, $pass)) return;
$this->username = $user;
// the token has likely been used on an insecure connection
// so generate a new one with the secure flag set
$oldtoken = $this->token;
$this->token = $this->genSessionID();
setcookie("sucssite_session", $this->token, time() + $this->max_session_length * 60, "/", $preferred_hostname, TRUE);
// set a cookie as a hint that we're logged in
// this can be checked for to allow redirecting to SSL to get the secure cookie
setcookie("sucssite_loggedin", "true", time() + $this->max_session_length * 60, "/");
// Update the session, filling in the blanks
$DB->Execute("update {$this->table} set hash=?, username=?, logintime='NOW()', lastseen='NOW()', ip=? where hash=?",
array($this->token, $this->username, $_SERVER['REMOTE_ADDR'], $oldtoken));
// Return back to normal session retrieval
}
class Session
{
public $loggedin = FALSE; // Is this a valid logged in user ?
public $username = ''; // Username
public $fullname; // Fullname
public $email = 0; // Email waiting?
public $email_forward; // Email forwarded?
public $groups = array(); // users groups
public $printbalance; // printer balance
public $data = ''; // Var/array for session persistant data
public $token = ''; // session identifier
public $logintime = ''; // Time which user last gave us credentials
public $lastseen = ''; // Time of last page request
private $timeout = 2880; // Idle timeout limit in minutes (session deleted), 2880 == 48 hours
private $anonymous_timeout = 120; // Idle timeout limit for sessions which aren't logged in (set lower to stop the session table getting swamped)
private $secure_timeout = 30; // Idle timeout limit in minutes (consider session less secure, require reauth for sensitive ops)
private $max_session_length = 11520; // maximum length of a session, 11520 == 8 days
private $table = "session"; // session storage table (const)
private $datahash = ''; // hash of data field
// Create a new (insecure) session
private function newsession()
{
global $DB, $preferred_hostname, $dbname;
$token = $this->genSessionID();
$DB->Execute("insert into {$this->table} (hash, lastseen, ip) values (?,NOW(),?)", array($token, $_SERVER['REMOTE_ADDR']));
setcookie($dbname . "_session", $token, NULL, "/", $preferred_hostname);
// delete loggedin cookie if it exists
setcookie($dbname . "_loggedin", FALSE, time(), "/");
$this->token = $token;
return;
}
public function isSecure()
{
global $DB;
// is user coming from the IP address they were when they logged in?
if ($detail['ip'] != $_SERVER['REMOTE_ADDR']) {
return false;
} elseif (time() > ($this->logintime + $this->secure_timeout)) {
// has it been too long since we last asked for credentials?
return false;
}
}
// Public Object constructor
function __construct()
{
global $DB, $preferred_hostname, $baseurl, $dbname;
unset($token);
// if user requests a page via HTTP and claims to be logged in, bump them to HTTPS
if (!isset($_SERVER['HTTPS']) && (@$_COOKIE[$dbname . '_loggedin'] == "true")) {
header("HTTP/1.0 307 Temporary redirect");
header("Location: https://{$preferred_hostname}{$baseurl}{$_SERVER['PATH_INFO']}");
return;
}
// The possible form elements
$submit = @$_POST['Login'];
$logout = @$_POST['Logout'];
$session_user = strtolower(@$_POST['session_user']);
$session_pass = @$_POST['session_pass'];
// We havent logged them in yet
$this->loggedin = FALSE;
// Time out any old sessions
$DB->Execute(
"delete from {$this->table} where lastseen < NOW() - '{$this->timeout} minutes'::reltime " .
"or logintime < NOW() - '{$this->max_session_length} minutes'::reltime " .
"or (username IS NULL AND lastseen < NOW() - '{$this->anonymous_timeout} minutes'::reltime)"
);
// the possible token data passed from a form
if (isset($_REQUEST['token']))
$token = $_REQUEST['token'];
// Check if we were handed a specific token identifier
// Otherwise use the value from the cookie we gave out
if (!isset($token) && isset($_COOKIE[$dbname . '_session']))
$token = @$_COOKIE[$dbname . '_session'];
if (isset($token)) $this->token = $token;
// Log them out if they ask
if ($logout == "Logout") {
$this->logout();
return;
}
// Okay, so we still dont have a session id
// so issue a new one and go back to core
if (!isset($token)) {
$this->newsession();
return;
}
// Is this a login attempt ?
if ($submit != '' && $session_user != '' && $session_pass != '') {
// filter out everything but A-Z a-z 0-9 . - _ from username
$safeusername = preg_replace("/[^A-Za-z0-9\.\-\_]/", '', $session_user);
if ($safeusername != $session_user) {
trigger_error("Invalid username", E_USER_NOTICE);
$this->newsession();
return;
} elseif ($safeusername == $session_user) {
$this->session_init($safeusername, $session_pass);
}
}
// Retrieve session information
$oldsess = $DB->GetAll("select * from {$this->table} where hash=?", array($this->token));
if (!$oldsess || count($oldsess) < 1) {
trigger_error("Session timed out", E_USER_NOTICE);
$this->newsession();
return;
}
// Extract detail of session for pass-back
$detail = $oldsess[0];
$this->data = unserialize((string)$detail['data']);
$this->lastseen = strtotime($detail['lastseen']);
$this->logintime = strtotime($detail['logintime']);
$this->datahash = md5(serialize($this->data));
// are we actually logged in, fill in more
if ($detail['username']) {
// Are we using HTTPS?
if (!isset($_SERVER['HTTPS'])) {
trigger_error("Insecure Connection", E_USER_NOTICE);
$this->loggedin = FALSE;
return;
}
$this->username = $detail['username'];
$this->fetch_detail($detail['username']);
$this->loggedin = TRUE;
}
// update time stamp
$DB->Execute("update {$this->table} set lastseen=NOW() where hash=?", array($this->token));
// check to see if there any messages stored for this user
if (isset($this->data['messages'])) {
global $messages;
if (is_array($messages)) {
$messages += $this->data['messages'];
} else {
$messages = $this->data['messages'];
}
unset($this->data['messages']);
$this->save();
}
}
// generate a string suitable to be used as a session ID
private function genSessionID()
{
global $DB;
$try = 0;
$tt = date("D M d H:i:s Y");
$ip = $_SERVER['REMOTE_ADDR'];
$nonce = rand(); // this should stop session IDs being (easily) guessable by someone with the algorithm
do {
$token = md5("$ip$tt$nonce" . $try++);
$old = $DB->GetAll("select hash from {$this->table} where hash=?", array($token));
} while ($old);
return $token;
}
// Public function: Store the session data away in the database
public function save()
{
global $DB;
$newhash = md5(serialize($this->data));
if ($newhash == $this->datahash) {
// no change in data, dont save
return;
}
$DB->Execute("update {$this->table} set data=? where hash=?", array(serialize($this->data), $this->token));
}
// Public function: force a logout of the session
public function logout()
{
global $DB, $dbname;
$DB->Execute("delete from {$this->table} where hash=?", array($this->token));
$this->newsession();
$this->loggedin = FALSE;
setcookie($dbname . "_loggedin", FALSE, time(), "/");
}
// Fill out any extra details we know about the user
private function fetch_detail($user)
{
if (!($ldap = @ldap_connect("ldap://localhost"))) {
trigger_error("LDAP connect failed", E_USER_ERROR);
return FALSE;
}
$info = $this->ldap_getuser($ldap, $user);
if (!$info) return FALSE;
ldap_close($ldap);
// Check the user's email status
/*$mailstat = @stat("/var/spool/mail/".$user);
if ($mailstat[size]>0) {
if ($mailstat[mtime]>$mailstat[atime]) $this->email = 2;
else $this->email = 1;
}*/
// a sure-fire way to check to see if the user has any unread email
// the bash script returns 0 for no and 1 for yes, takes one arg, username
$this->email = shell_exec("../plugins/sucsunreadmail $user");
if (file_exists($info['homedirectory'][0] . "/.forward") && ! is_link($info['homedirectory'][0] . "/.forward")) {
$forward = file($info['homedirectory'][0] . "/.forward");
$this->email_forward = preg_replace("/\n/", "", $forward[0]);
}
$this->fullname = $info['cn'][0];
$this->groups = $info['grouplist'];
$db = new SQLite3('/etc/pykota/pykota.db');
$result = $db->query("SELECT balance FROM users WHERE username='$user';");
$this->printbalance = $result->fetchArray()[0];
}
/* check using mod_auth_externals helper
private function check_pass($user, $pass)
{
if ($fd === FALSE) {
$this->errormsg = "Auth system error";
return FALSE;
}
fwrite($fd, "$user\n");
fwrite($fd, "$pass\n");
$ret = pclose($fd);
if ($ret == 0) return TRUE;
$this->autherror = "u='$user' p='$pass' ret=$ret";
$this->errormsg = "Invalid Username or Password";
return FALSE;
}
*/
// Get a users full record from ldap
private function ldap_getuser($ldap, $user)
{
// publically bind to find user
ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);
if (!($bind = @ldap_bind($ldap))) {
trigger_error("LDAP bind failed", E_USER_ERROR);
return NULL;
}
// find the user
if (!($search = @ldap_search($ldap, "dc=sucs,dc=org", "(&(uid=$user))"))) {
trigger_error("LDAP search fail", E_USER_ERROR);
return NULL;
}
$n = ldap_count_entries($ldap, $search);
if ($n < 1) {
trigger_error("Username or Password Incorrect", E_USER_WARNING);
return NULL;
}
$info = ldap_get_entries($ldap, $search);
if (($grpsearch = @ldap_search($ldap, "ou=Group,dc=sucs,dc=org", "memberuid=$user"))) {
$gn = ldap_count_entries($ldap, $grpsearch);
$gpile = ldap_get_entries($ldap, $grpsearch);
$glist = array();
for ($i = 0; $i < $gn; $i++) {
$glist[$gpile[$i]['cn'][0]] = $gpile[$i]['gidnumber'][0];
}
$info[0]['grouplist'] = $glist;
}
return $info[0];
}
/* check using ldap directly */
public function check_pass($user, $pass)
{
// Open connection
if (!($ldap = @ldap_connect("ldap://localhost"))) {
trigger_error("LDAP connect failed", E_USER_ERROR);
return FALSE;
}
$info = $this->ldap_getuser($ldap, $user);
if (!$info) return FALSE;
$real = @ldap_bind($ldap, $info['dn'], $pass);
ldap_close($ldap);
if ($real) return TRUE;
trigger_error("Username or Password Incorrect", E_USER_WARNING);
return FALSE;
}
// Private function: process login form
private function session_init($user, $pass)
{
global $DB, $preferred_hostname, $dbname;
// Check that this is a valid session start
// This prevents replay attacks
$sess = $DB->GetAll("select * from {$this->table} where hash=? and username is NULL", array($this->token));
if (!$sess || count($sess) < 1) {
trigger_error("Invalid session, login again.", E_USER_WARNING);
return;
}
if (!$this->check_pass($user, $pass)) return;
$this->username = $user;
// the token has likely been used on an insecure connection
// so generate a new one with the secure flag set
$oldtoken = $this->token;
$this->token = $this->genSessionID();
setcookie($dbname . "_session", $this->token, time() + $this->max_session_length * 60, "/", $preferred_hostname, TRUE);
// set a cookie as a hint that we're logged in
// this can be checked for to allow redirecting to SSL to get the secure cookie
setcookie($dbname . "_loggedin", "true", time() + $this->max_session_length * 60, "/");
// Update the session, filling in the blanks
$DB->Execute("update {$this->table} set hash=?, username=?, logintime='NOW()', lastseen='NOW()', ip=? where hash=?",
array($this->token, $this->username, $_SERVER['REMOTE_ADDR'], $oldtoken));
// Return back to normal session retrieval
}
} // end of Class
<?php
// sorry
// gib errars plox
//error_reporting(E_ALL);
//ini_set('display_errors', 1);
// without this the entire thing doesn't work
include_once("../suapiv2-key.php");
//SUCS Org ID According to the SU
$orgid = "6613";
$apibaseurl = "http://su-apiv2.sucs.org/?apikey=${apikey}&orgid=${orgid}";
// Get the shit json the suapiv2 spits out
$ch = curl_init($apibaseurl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
$raw_data = curl_exec($ch);
curl_close($ch);
$formated_raw_data = json_decode($raw_data, true); //convert it into php arrays
/*
* $membership_data is an array where each type of memebrship is it;s own array
* Insdie each of those arrays the actual arrary of members is under ["Detail_Collection"]["Detail"]
* So $membership_data[0]["Detail_Collection"]["Detail"][0] will get you the array containing the
* first member in the data
*/
$membership_data = $formated_raw_data["table1"]["table1_Product_Collection"]["table1_Product"];
// make a new array that just contains *every* member no matter what they bought
$just_members = array();
function filter_array($data){
global $just_members;
// we have more than one type of member if this is true
if (count($data[0]["Detail_Collection"]["Detail"][0]["@attributes"]) == 7){
//split them up then feed them back
foreach ($data as $membershipType){
filter_array($membershipType);
}
// we have more then 1 of the same type of member if this is true
} else if (count($data["Detail_Collection"]["Detail"][0]["@attributes"]) == 7) {
//wack them onto the used array
foreach ($data["Detail_Collection"]["Detail"] as $member) {
array_push($just_members, $member["@attributes"]);
}
// we have 1 member :(
} else if (count($data["Detail_Collection"]["Detail"]["@attributes"]) == 7) {
array_push($just_members, $data["Detail_Collection"]["Detail"]["@attributes"]);
}
}
filter_array($membership_data);
/* You can now use $just_members to probe member detials. It's an array of arrays which each contain:
* transaction_id (recepit id)
* purchaser (full name)
* textbox6 (under 18 or not) NOT SURE OF THE FORMAT
* card_number (student number)
* shop_name (where they bought sucs memebrship)
* qty (how many sucs memebrships they bought)
* purchase_date (timestamp of when they bought memebrship)4
*/
/*
* Used by /susignup to verify that the stduent number and transaction id combo are valid
* returns true or false
*/
function check_su_sid_and_trans($sid, $transid)
{
global $just_members;
foreach ($just_members as $member) {
if ($sid == $member["card_number"] && $transid == $member["transaction_id"]) {
return true;
}
}
return false;
}
/*
* Used to verify taht a given stduent number has paid for membership via the su system.
* returns false or true
*/
function check_su_sid($sid)
{
global $just_members;
foreach ($just_members as $member) {
if ($sid == $member["card_number"]) {
return true;
}
}
return false;
}
?>
<?
<?php
require_once("validationData.php");
require_once("sanitization.php");
/* useful validation functions */
//check for a valid email address
function validEmail ($email)
function validEmail($email)
{
global $error;
//split user and domain
list($user,$domain) = explode("@", $email);
// check for bad characters, and check for zero length user & domain
if(!preg_match("/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$/i",$email) or !$user or !$domain )
{
$error = 'an invalid email address (syntax)';
return false;
}
// Syntax OK
// Check for an mail server
elseif(!getmxrr($domain,$mx) or !gethostbyname($domain))
{
$error = "no mail servers listed for '$domain'";
return false;
}
else
{
// Email address valid from technical point of view
return true;
}
}
// test whether a password is considered Strong Enough
// ideally we'd want to use cracklib or something here, but no RPM for the php bindings :-(
// dont use this, use weakPassword instead it uses cracklib
function strongPassword ($pass) {
global $error;
// you call this a password? my cat could bruteforce this.
if (strlen($pass) < 6) {
return false;
}
// start at 0, and increment for certain features
$score = 0;
// check for valid syntax
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$error = 'Invalid email address (syntax)';
return false;
}
// Syntax OK
// greater than 8 characters
if (strlen($pass) > 8) $score++;
// includes lowercase characters
if (preg_match("/[a-z]/", $pass)) $score++;
// includes uppercase characters
if (preg_match("/[A-Z]/", $pass)) $score++;
// includes digits
if (preg_match("/\d/", $pass)) $score++;
// includes "non-word" characters
if (preg_match("/\W/", $pass)) $score++;
// domain consists of any character after a '@' and cannot contain '@'
// therefore any character after the last '@' is part of the domain
$domain = substr($email, strrpos($email, '@') + 1);
// I reckons if it has at least 3 of the above it should be... adequate
// better if it checked for dictionary words too though
if ($score > 3) {
return true;
} else {
return false;
}
// Check for an mail server
if (!getmxrr($domain, $mx) or !gethostbyname($domain)) {
$error = "No mail servers listed for '$domain'";
return false;
} else {
// Email address valid from technical point of view
return true;
}
}
# Use cracklib to check for weak passwords.
......@@ -69,222 +35,187 @@ function strongPassword ($pass) {
# otherwise returns a string saying why its weak
function weakPassword($password)
{
// Try fedora then debian known paths
if (file_exists("/usr/sbin/cracklib-check"))
$cracklib = "/usr/sbin/cracklib-check";
else
if (file_exists("/usr/sbin/crack_testlib"))
$cracklib = "/usr/sbin/crack_testlib";
else
return "Cannot find cracklib";
// Try fedora then debian known paths
if (file_exists("/usr/sbin/cracklib-check"))
$cracklib = "/usr/sbin/cracklib-check";
else
if (file_exists("/usr/sbin/crack_testlib"))
$cracklib = "/usr/sbin/crack_testlib";
else
return "Cannot find cracklib";
$proc = proc_open($cracklib, array(0=>array("pipe","r"),1=>array("pipe","w")),$pipes,'/tmp/',NULL);
if (!is_resource($proc)) {
return "Cannot find cracklib";
}
fwrite($pipes[0], $password);
fclose($pipes[0]);
$last = "";
do {
$last = fgets($pipes[1]);
if ($last !== FALSE) $answer = trim($last);
} while ($last !== FALSE);
fclose($pipes[1]);
proc_close($proc);
$answer = substr(strrchr($answer,":"),2);
if (strtolower($answer) == "ok") return FALSE;
if ($answer == "") return("Empty password");
return $answer;
$proc = proc_open($cracklib, array(0 => array("pipe", "r"), 1 => array("pipe", "w")), $pipes, '/tmp/', NULL);
if (!is_resource($proc)) {
return "Cannot find cracklib";
}
fwrite($pipes[0], $password);
fclose($pipes[0]);
$last = "";
do {
$last = fgets($pipes[1]);
if ($last !== FALSE) $answer = trim($last);
} while ($last !== FALSE);
fclose($pipes[1]);
proc_close($proc);
$answer = substr(strrchr($answer, ":"), 2);
if (strtolower($answer) == "ok") return FALSE;
if ($answer == "") return ("Empty password");
return $answer;
}
// check if username is an alias
function isAlias($username){
$ok=false;
// check its not an alias
$aliasesfile = file ('/etc/aliases');
foreach ($aliasesfile as $aliasline)
{
if(trim($aliasline) && $aliasline[0]!="#")
{
$anAlias = explode(":", trim($aliasline));
if($anAlias[0] && !posix_getpwnam($anAlias[0]) && ($anAlias[0] == $username)){
$ok=true;
return true;
}
}
function isAlias($username)
{
$ok = false;
// check its not an alias
$aliasesfile = file('/etc/aliases');
foreach ($aliasesfile as $aliasline) {
if (trim($aliasline) && $aliasline[0] != "#") {
$anAlias = explode(":", trim($aliasline));
if ($anAlias[0] && !posix_getpwnam($anAlias[0]) && ($anAlias[0] == $username)) {
$ok = true;
return true;
}
}
return $ok;
}
return $ok;
}
//check if a user with a sid already exsists
function sidUsed($sid){
$sucsDB = NewADOConnection('postgres8');
$sucsDB->Connect('dbname=sucs');
$sucsDB->SetFetchMode(ADODB_FETCH_ASSOC);
$query = "SELECT * FROM members WHERE sid=?";
$data = $sucsDB->GetAll($query,$sid);
return (sizeof($data) > 0);
//check if a user with a sid already exists
function sidUsed($sid)
{
$sucsDB = NewADOConnection('postgres8');
$sucsDB->Connect('dbname=sucs');
$sucsDB->SetFetchMode(ADODB_FETCH_ASSOC);
$query = "SELECT * FROM members WHERE sid=?";
$data = $sucsDB->GetAll($query, $sid);
return (sizeof($data) > 0);
}
function validUsername ($username){
global $error;
// check if uname is sytactically valid
$syntax = preg_match("/^[a-z][a-z0-9_]*$/", $username);
if(!$syntax || (strlen($username) < 2)){
$error = "Usernames must start with a letter, only contain lowercase letter, numbers 0-9 and underscores (_) and be at least 2 characters.";
return false;
}
// check if the username already exsists
elseif(posix_getpwnam($username))
{
$error = "Username already taken";
return false;
}
// check if its a mail alias
elseif(isAlias($username)){
$error ="Username is a mail alias";
return false;
}
else{
return true;
}
}
function validSID($SID,$override){
global $error;
if($override){
if($SID==""){
$error = "You MUST provide some sort of student number";
return false;
}
else{
return true;
}
}
else{
if(!preg_match("/^[0-9]*$/", $SID) || strlen($SID) != 6){
$error = "Invalid student ID";
return false;
}
elseif(sidUsed($SID)){
$error = "A user with that student ID already exsists, email <a href=\"mailto:admin@sucs.org\">admin@sucs.org</a> if this is an error.";
return false;
}
elseif(lookupSID($SID)==" "){
$error = "Student not found, email<a href=\"mailto:admin@sucs.org\">admin@sucs.org</a> if this is an error.";
return false;
}
else{
return true;
}
}
function validUsername($username)
{
global $error;
// check if uname is syntactically valid
$syntax = preg_match("/^[a-z][a-z0-9_]*$/", $username);
if (!$syntax || (strlen($username) < 2)) {
$error = "Usernames must start with a letter, only contain lowercase letters, numbers 0-9 and underscores (_) and be at least 2 characters.";
return false;
} // check if the username already exists
elseif (posix_getpwnam($username)) {
$error = "Username already taken";
return false;
} // check if its a mail alias
elseif (isAlias($username)) {
$error = "Username is a mail alias";
return false;
} else {
return true;
}
}
function validRealName($realName,$override){
global $error;
if($override){
if($realName==""){
$error="You MUST provide some sort of name";
return false;
}
else{
return true;
}
}
else{
//check for enough names for real name (we insist on at least 2
if(count(explode(" ",$realName)) < 2)
{
$error = "Too few names given, please give at least two.";
return false;
}
//check for a sane realname, see comment below
elseif (!preg_match("/^([A-Z]([.]+ +[A-Z])*([\']+[A-Z])*[a-z]+[ -]*)+$/", $realName))
{
$error = "Name incorrectly formated, email <a href=\"mailto:admin@sucs.org\">admin@sucs.org</a> if this is an error.";
return false;
}
/*
* This should force sane real names, with capitals for the first letter of each word,
* Whist alowing for complex names such as Robin M. O'Leary
*
* break down of regexp
*
* (
* [A-Z] - start with a single capital
* ([.]+ +[A-Z])* - zero or more of, (at least one "." followed by at least one space then another single capital) //we dont expect people to have initals at the end of there names so this is alright
* ([\']+[A-Z])* - zero or more of, (at least one "'"s followed by a single capital letter)
* [a-z]+ - One or more lower case letters, this forces initals to be followed by a "."
*[ -]* - zero or more " "s or "-"s so double barreled names are supported
* )
*
* In its current state
* Robin M. O'Leary is valid
* Robin M O'Leary is not
* Robin M. OLeary is Not
* Robin M. O'LeaRy is valid (though its not ment to be.. bad side effect of not requireing at least one space...)
* BUT... this alows for McSmith's... which is rather nice :)... and of course delibrate
* RObin M O'Leary is not
*
*/
else{
return true;
}
}
function validSID($SID, $override)
{
global $error;
if ($override) {
if ($SID == "") {
$error = "You MUST provide some sort of student number";
return false;
} else {
return true;
}
} else {
if (!preg_match("/^[0-9]{6,}$/", $SID)) {
$error = "Invalid student ID";
return false;
} elseif (sidUsed($SID)) {
$error = "A user with that student ID already exists, email <a href=\"mailto:admin@sucs.org\">admin@sucs.org</a> if this is an error.";
return false;
} elseif (lookupSID($SID) == " ") {
$error = "Student not found, email <a href=\"mailto:admin@sucs.org\">admin@sucs.org</a> if this is an error.";
return false;
} else {
return true;
}
}
}
function validSocName($socname,$override){
global $error;
if($override){
if($socname==""){
$error="You MUST provide some sort of name";
return false;
}
else{
return true;
}
}
else{
if(!preg_match('/^[A-Z1-9]/',$socname) || strlen($socname) < 2){
$error = "Must start with a capital letter or a number and be more than 1 character";
return false;
}
else{
return true;
}
}
function validName($realName, $override)
{
global $error;
if ($override) {
if ($realName == "") {
$error = "You MUST provide some sort of name";
return false;
} else {
return true;
}
} else {
// names can legally be really weird so just check that it is at least 1 visible character
// followed by any number of non-control characters
$realName = trim($realName);
if (!preg_match("/^[[:graph:]][[:print:]]*$/", $realName)) {
$error = "Invalid name";
return false;
} else {
return true;
}
}
}
function validAddress($address){
global $error;
function validAddress($address)
{
global $error;
$address = sanitizeAddress($address);
$regex="/^([A-Z0-9]([[:alnum:]]|[ .\/'-])*\n)+[A-Z0-9]([[:alnum:]]|[ .\/'-])*$/";
if(!preg_match($regex,$address)){
$error = "Please supply at least two valid lines of address.";
return false;
}
else{
return true;
}
// check that they at least entered in something. Address doesn't need to be as strict when the postcode is.
$regex = "/^.{5,}+$/s";
if (!preg_match($regex, $address)) {
$error = "Please supply a valid address.";
return false;
} else {
return true;
}
}
function validPostcode($postcode)
{
$postcode = sanitizePostcode($postcode);
// matches all postcodes following the valid format described in a 2012 government published document
$postcodeRegex = "/^([A-Z](([0-9][0-9]?)|([A-Z][0-9][0-9]?)|([A-Z]?[0-9][A-Z])) ?[0-9][ABD-HJLNP-UW-Z]{2})$/";
if (!preg_match($postcodeRegex, $postcode)) {
return false;
} else {
return $postcode;
}
}
function validPhone($phone){
global $error;
$phone=sanitizePhone($phone);
if(!preg_match("/^\+?[0-9-]+$/",$phone)){
$error = "Must be all numbers";
return false;
}
return true;
function validPhone($phone)
{
global $error;
$phone = sanitizePhone($phone);
if (!preg_match("/^\+?[0-9-]{10,}$/", $phone)) {
$error = "Must be all numbers";
return false;
}
return true;
}
function validSignupEmail($email){
global $error;
if(preg_match('/@sucs\.org$/',$email)){
$error = "SUCS email addresses are not allowed";
return false;
}
elseif(!validEmail($email)){
return false;
}
else{
return true;
}
function validSignupEmail($email)
{
global $error;
if (preg_match('/@sucs\.$/', $email)) {
$error = "SUCS email addresses are not allowed";
return false;
} elseif (!validEmail($email)) {
return false;
} else {
return true;
}
}
?>
<?
// lookup real names from sid's useing campus ldap
function lookupSID($sid) {
$ds=ldap_connect("nds-stud.swan.ac.uk");
$sr=ldap_search($ds, "ou=Students,o=uws", "cn=".$sid);
$info = ldap_get_entries($ds, $sr);
ldap_unbind($ds);
return ucwords(strtolower($info[0]['givenname'][0]." ".$info[0]['sn'][0]));
<?php
// lookup real names from sid's using campus ldap
function lookupSID($sid)
{
$ds = ldap_connect("192.168.10.16");
ldap_set_option($ds, LDAP_OPT_NETWORK_TIMEOUT, 10); /* 10 second timeout */
ldap_bind($ds);
$sr = ldap_search($ds, "ou=Active,ou=Resources,o=Swansea", "EDUPERSONTARGETEDID=" . $sid);
$info = ldap_get_entries($ds, $sr);
ldap_unbind($ds);
return ucwords(strtolower($info[0]['givenname'][0] . " " . $info[0]['sn'][0]));
}
// lookup addresses from postcodes useing the univeritys website
function lookup_postcode($postcode = "")
// lookup addresses from postcodes using the university's website
function lookup_postcode($postcode)
{
$url = "https://intranet.swan.ac.uk/common/postcodeLookup.asp?pCode=".$postcode;
$referer = "https://intranet.swan.ac.uk/common/postcodeaddresslookup.asp";
$req = curl_init($url);
curl_setopt($req, CURLOPT_HEADER, false);
curl_setopt($req, CURLOPT_REFERER, $referer);
curl_setopt($req, CURLOPT_RETURNTRANSFER, true);
curl_setopt($req, CURLOPT_SSL_VERIFYPEER, false);
$page = curl_exec($req);
curl_close($req);
include_once "../paf-key.php";
$scrape = explode("returnAddress(\"", $page);
$url = "http://paf.sucs.org/?apikey=$apikey&postcode=" . rawurlencode($postcode);
$addresses = array();
for ($i = 1; $i < count($scrape); $i++) {
if (preg_match("/^[^,\"].+?\"/", $scrape[$i], $address)) {
$addr = str_replace("<BR>\"", "", $address[0]);
array_push($addresses, str_replace("<BR>", ", ", $addr));
}
}
return $addresses;
$req = curl_init($url);
curl_exec($req);
curl_close($req);
}
?>
# Replace index
IndexOptions DescriptionWidth=80
IndexOptions +SuppressHTMLPreamble
IndexIgnore header.html footer.html index.atom
HeaderName /code/venus/header.html
ReadmeName /code/venus/footer.html
AddDefaultCharset utf-8
# Redirect missing tgz and zip files to tarify.cgi
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-s
RewriteRule (.*)\.tgz$ tarify.cgi?dir=$1
RewriteCond %{REQUEST_FILENAME} !-s
RewriteRule (.*)\.zip$ tarify.cgi?dir=$1
RewriteCond %{REQUEST_FILENAME} !-s
RewriteRule (.*).atom$ bzr-feed.cgi?dir=$1
Sam Ruby <rubys@intertwingly.net>
This codebase represents a radical refactoring of Planet 2.0, which lists
the following authors:
Scott James Remnant <scott@netsplit.com>
Jeff Waugh <jdub@perkypants.org>
Planet is released under the same licence as Python, here it is:
A. HISTORY OF THE SOFTWARE
==========================
Python was created in the early 1990s by Guido van Rossum at Stichting Mathematisch Centrum (CWI) in the Netherlands as a successor of a language called ABC. Guido is Python's principal author, although it includes many contributions from others. The last version released from CWI was Python 1.2. In 1995, Guido continued his work on Python at the Corporation for National Research Initiatives (CNRI) in Reston, Virginia where he released several versions of the software. Python 1.6 was the last of the versions released by CNRI. In 2000, Guido and the Python core development team moved to BeOpen.com to form the BeOpen PythonLabs team. Python 2.0 was the first and only release from BeOpen.com.
Following the release of Python 1.6, and after Guido van Rossum left CNRI to work with commercial software developers, it became clear that the ability to use Python with software available under the GNU Public License (GPL) was very desirable. CNRI and the Free Software Foundation (FSF) interacted to develop enabling wording changes to the Python license. Python 1.6.1 is essentially the same as Python 1.6, with a few minor bug fixes, and with a different license that enables later versions to be GPL-compatible. Python 2.1 is a derivative work of Python 1.6.1, as well as of Python 2.0.
After Python 2.0 was released by BeOpen.com, Guido van Rossum and the other PythonLabs developers joined Digital Creations. All intellectual property added from this point on, starting with Python 2.1 and its alpha and beta releases, is owned by the Python Software Foundation (PSF), a non-profit modeled after the Apache Software Foundation. See http://www.python.org/psf/ for more information about the PSF.
Thanks to the many outside volunteers who have worked under Guido's direction to make these releases possible.
B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
===============================================================
PSF LICENSE AGREEMENT
---------------------
1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and the Individual or Organization ("Licensee") accessing and otherwise using Python 2.1.1 software in source or binary form and its associated documentation.
2. Subject to the terms and conditions of this License Agreement, PSF hereby grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python 2.1.1 alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright (c) 2001 Python Software Foundation; All Rights Reserved" are retained in Python 2.1.1 alone or in any derivative version prepared by Licensee.
3. In the event Licensee prepares a derivative work that is based on or incorporates Python 2.1.1 or any part thereof, and wants to make the derivative work available to others as provided herein, then Licensee hereby agrees to include in any such work a brief summary of the changes made to Python 2.1.1.
4. PSF is making Python 2.1.1 available to Licensee on an "AS IS" basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 2.1.1 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS.
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 2.1.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 2.1.1, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
6. This License Agreement will automatically terminate upon a material breach of its terms and conditions.
7. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between PSF and Licensee. This License Agreement does not grant permission to use PSF trademarks or trade name in a trademark sense to endorse or promote products or services of Licensee, or any third party.
8. By copying, installing or otherwise using Python 2.1.1, Licensee agrees to be bound by the terms and conditions of this License Agreement.
BEOPEN.COM TERMS AND CONDITIONS FOR PYTHON 2.0
----------------------------------------------
BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the Individual or Organization ("Licensee") accessing and otherwise using this software in source or binary form and its associated documentation ("the Software").
2. Subject to the terms and conditions of this BeOpen Python License Agreement, BeOpen hereby grants Licensee a non-exclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use the Software alone or in any derivative version, provided, however, that the BeOpen Python License is retained in the Software, alone or in any derivative version prepared by Licensee.
3. BeOpen is making the Software available to Licensee on an "AS IS" basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT INFRINGE ANY THIRD PARTY RIGHTS.
4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
5. This License Agreement will automatically terminate upon a material breach of its terms and conditions.
6. This License Agreement shall be governed by and interpreted in all respects by the law of the State of California, excluding conflict of law provisions. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between BeOpen and Licensee. This License Agreement does not grant permission to use BeOpen trademarks or trade names in a trademark sense to endorse or promote products or services of Licensee, or any third party. As an exception, the "BeOpen Python" logos available at http://www.pythonlabs.com/logos.html may be used according to the permissions granted on that web page.
7. By copying, installing or otherwise using the software, Licensee agrees to be bound by the terms and conditions of this License Agreement.
CNRI OPEN SOURCE GPL-COMPATIBLE LICENSE AGREEMENT
-------------------------------------------------
1. This LICENSE AGREEMENT is between the Corporation for National Research Initiatives, having an office at 1895 Preston White Drive, Reston, VA 20191 ("CNRI"), and the Individual or Organization ("Licensee") accessing and otherwise using Python 1.6.1 software in source or binary form and its associated documentation.
2. Subject to the terms and conditions of this License Agreement, CNRI hereby grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python 1.6.1 alone or in any derivative version, provided, however, that CNRI's License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) 1995-2001 Corporation for National Research Initiatives; All Rights Reserved" are retained in Python 1.6.1 alone or in any derivative version prepared by Licensee. Alternately, in lieu of CNRI's License Agreement, Licensee may substitute the following text (omitting the quotes): "Python 1.6.1 is made available subject to the terms and conditions in CNRI's License Agreement. This Agreement together with Python 1.6.1 may be located on the Internet using the following unique, persistent identifier (known as a handle): 1895.22/1013. This Agreement may also be obtained from a proxy server on the Internet using the following URL: http://hdl.handle.net/1895.22/1013".
3. In the event Licensee prepares a derivative work that is based on or incorporates Python 1.6.1 or any part thereof, and wants to make the derivative work available to others as provided herein, then Licensee hereby agrees to include in any such work a brief summary of the changes made to Python 1.6.1.
4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS.
5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
6. This License Agreement will automatically terminate upon a material breach of its terms and conditions.
7. This License Agreement shall be governed by the federal intellectual property law of the United States, including without limitation the federal copyright law, and, to the extent such U.S. federal law does not apply, by the law of the Commonwealth of Virginia, excluding Virginia's conflict of law provisions. Notwithstanding the foregoing, with regard to derivative works based on Python 1.6.1 that incorporate non-separable material that was previously distributed under the GNU General Public License (GPL), the law of the Commonwealth of Virginia shall govern this License Agreement only as to issues arising under or with respect to Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between CNRI and Licensee. This License Agreement does not grant permission to use CNRI trademarks or trade name in a trademark sense to endorse or promote products or services of Licensee, or any third party.
8. By clicking on the "ACCEPT" button where indicated, or by copying, installing or otherwise using Python 1.6.1, Licensee agrees to be bound by the terms and conditions of this License Agreement.
ACCEPT
CWI PERMISSIONS STATEMENT AND DISCLAIMER
----------------------------------------
Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, The Netherlands. All rights reserved.
Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Stichting Mathematisch Centrum or CWI not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission.
STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
\ No newline at end of file
Planet
------
Planet is a flexible feed aggregator. It downloads news feeds published by
web sites and aggregates their content together into a single combined feed,
latest news first. This version of Planet is named Venus as it is the
second major version. The first version is still in wide use and is
also actively being maintained.
It uses Mark Pilgrim's Universal Feed Parser to read from CDF, RDF, RSS and
Atom feeds; Leonard Richardson's Beautiful Soup to correct markup issues;
and either Tomas Styblo's templating engine or Daniel Viellard's implementation
of XSLT to output static files in any format you can dream up.
To get started, check out the documentation in the docs directory. If you have
any questions or comments, please don't hesitate to use the planet mailing list:
http://lists.planetplanet.org/mailman/listinfo/devel
Keywords: feed, blog, aggregator, RSS, RDF, Atom, OPML, Python
DeWitt Clinton - Mac OSX
Mary Gardiner - PythonPath
Elias Torres - FOAF OnlineAccounts
Jacques Distler - Template patches
Michael Koziarski - HTTP Auth fix
Brian Ewins - Win32 / Portalocker
Joe Gregorio - python versioning for filters, verbose tests, spider_threads
Harry Fuecks - Pipe characters in file names, filter bug
Eric van der Vlist - Filters to add language, category information
Chris Dolan - mkdir cache; default template_dirs; fix xsltproc
David Sifry - rss 2.0 xslt template based on http://atom.geekhood.net/
Morten Frederiksen - Support WordPress LinkManager OPML
Harry Fuecks - default item date to feed date
Antonio Cavedoni - Django templates
Morten Frederiksen - expungeCache
Lenny Domnitser - Coral CDN support for URLs with non-standard ports
Amit Chakradeo - Allow read-only files to be overwritten
Matt Brubeck - fix new_channel
Aristotle Pagaltzis - ensure byline_author filter doesn't drop foreign markup
This codebase represents a radical refactoring of Planet 2.0, which lists
the following contributors:
Patches and Bug Fixes
---------------------
Chris Dolan - fixes, exclude filtering, duplicate culling
David Edmondson - filtering
Lucas Nussbaum - locale configuration
David Pashley - cache code profiling and recursion fixing
Gediminas Paulauskas - days per page
Spycyroll Maintainers
---------------------
Vattekkat Satheesh Babu
Richard Jones
Garth Kidd
Eliot Landrum
Bryan Richard
TODO
====
* Allow display normalisation to specified timezone
Some Planet admins would like their feed to be displayed in the local
timezone, instead of UTC.
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN"
"http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script type="text/javascript" src="docs.js"></script>
<link rel="stylesheet" type="text/css" href="docs.css"/>
<title>Venus Configuration</title>
</head>
<body>
<h2>Configuration</h2>
<p>Configuration files are in <a href="http://docs.python.org/lib/module-
ConfigParser.html">ConfigParser</a> format which basically means the same
format as INI files, i.e., they consist of a series of
<code>[sections]</code>, in square brackets, with each section containing a
list of <code>name:value</code> pairs (or <code>name=value</code> pairs, if
you prefer).</p>
<p>You are welcome to place your entire configuration into one file.
Alternately, you may factor out the templating into a "theme", and
the list of subscriptions into one or more "reading lists".</p>
<h3 id="planet"><code>[planet]</code></h3>
<p>This is the only required section, which is a bit odd as none of the
parameters listed below are required. Even so, you really do want to
provide many of these, especially ones that identify your planet and
either (or both) of <code>template_files</code> and <code>theme</code>.</p>
<p>Below is a complete list of predefined planet configuration parameters,
including <del>ones not (yet) implemented by Venus</del> and <ins>ones that
are either new or implemented differently by Venus</ins>.</p>
<blockquote>
<dl class="compact code">
<dt>name</dt>
<dd>Your planet's name</dd>
<dt>link</dt>
<dd>Link to the main page</dd>
<dt>owner_name</dt>
<dd>Your name</dd>
<dt>owner_email</dt>
<dd>Your e-mail address</dd>
</dl>
<dl class="compact code">
<dt>cache_directory</dt>
<dd>Where cached feeds are stored</dd>
<dt>output_dir</dt>
<dd>Directory to place output files</dd>
</dl>
<dl class="compact code">
<dt><ins>output_theme</ins></dt>
<dd>Directory containing a <code>config.ini</code> file which is merged
with this one. This is typically used to specify templating and bill of
material information.</dd>
<dt>template_files</dt>
<dd>Space-separated list of output template files</dd>
<dt><ins>template_directories</ins></dt>
<dd>Space-separated list of directories in which <code>template_files</code>
can be found</dd>
<dt><ins>bill_of_materials</ins></dt>
<dd>Space-separated list of files to be copied as is directly from the <code>template_directories</code> to the <code>output_dir</code></dd>
<dt>filter</dt>
<dd>Regular expression that must be found in the textual portion of the entry</dd>
<dt>exclude</dt>
<dd>Regular expression that must <b>not</b> be found in the textual portion of the entry</dd>
<dt><ins>filters</ins></dt>
<dd>Space-separated list of <a href="filters.html">filters</a> to apply to
each entry</dd>
<dt><ins>filter_directories</ins></dt>
<dd>Space-separated list of directories in which <code>filters</code>
can be found</dd>
</dl>
<dl class="compact code">
<dt>items_per_page</dt>
<dd>How many items to put on each page. <ins>Whereas Planet 2.0 allows this to
be overridden on a per template basis, Venus currently takes the maximum value
for this across all templates.</ins></dd>
<dt><del>days_per_page</del></dt>
<dd>How many complete days of posts to put on each page This is the absolute, hard limit (over the item limit)</dd>
<dt>date_format</dt>
<dd><a href="http://docs.python.org/lib/module-time.html#l2h-2816">strftime</a> format for the default 'date' template variable</dd>
<dt>new_date_format</dt>
<dd><a href="http://docs.python.org/lib/module-time.html#l2h-2816">strftime</a> format for the 'new_date' template variable <ins>only applies to htmltmpl templates</ins></dd>
<dt><del>encoding</del></dt>
<dd>Output encoding for the file, Python 2.3+ users can use the special "xml" value to output ASCII with XML character references</dd>
<dt><del>locale</del></dt>
<dd>Locale to use for (e.g.) strings in dates, default is taken from your system</dd>
<dt>activity_threshold</dt>
<dd>If non-zero, all feeds which have not been updated in the indicated
number of days will be marked as inactive</dd>
</dl>
<dl class="compact code">
<dt>log_level</dt>
<dd>One of <code>DEBUG</code>, <code>INFO</code>, <code>WARNING</code>, <code>ERROR</code> or <code>CRITICAL</code></dd>
<dt><ins>log_format</ins></dt>
<dd><a href="http://docs.python.org/lib/node422.html">format string</a> to
use for logging output. Note: this configuration value is processed
<a href="http://docs.python.org/lib/ConfigParser-objects.html">raw</a></dd>
<dt>feed_timeout</dt>
<dd>Number of seconds to wait for any given feed</dd>
<dt>new_feed_items</dt>
<dd>Maximum number of items to include in the output from any one feed</dd>
<dt><ins>spider_threads</ins></dt>
<dd>The number of threads to use when spidering. When set to 0, the default,
no threads are used and spidering follows the traditional algorithm.</dd>
<dt><ins>http_cache_directory</ins></dt>
<dd>If <code>spider_threads</code> is specified, you can also specify a
directory to be used for an additional HTTP cache to front end the Venus
cache. If specified as a relative path, it is evaluated relative to the
<code>cache_directory</code>.</dd>
<dt><ins>cache_keep_entries</ins></dt>
<dd>Used by <code>expunge</code> to determine how many entries should be
kept for each source when expunging old entries from the cache directory.
This may be overriden on a per subscription feed basis.</dd>
</dl>
<p>Additional options can be found in
<a href="normalization.html#overrides">normalization level overrides</a>.</p>
</blockquote>
<h3 id="default"><code>[DEFAULT]</code></h3>
<p>Values placed in this section are used as default values for all sections.
While it is true that few values make sense in all sections; in most cases
unused parameters cause few problems.</p>
<h3 id="subscription"><code>[</code><em>subscription</em><code>]</code></h3>
<p>All sections other than <code>planet</code>, <code>DEFAULT</code>, or are
named in <code>[planet]</code>'s <code>filters</code> or
<code>templatefiles</code> parameters
are treated as subscriptions and typically take the form of a
<acronym title="Uniform Resource Identifier">URI</acronym>.</p>
<p>Parameters placed in this section are passed to templates. While
you are free to include as few or as many parameters as you like, most of
the predefined themes presume that at least <code>name</code> is defined.</p>
<p>The <code>content_type</code> parameter can be defined to indicate that
this subscription is a <em>reading list</em>, i.e., is an external list
of subscriptions. At the moment, three formats of reading lists are supported:
<code>opml</code>, <code>foaf</code>, <code>csv</code>, and
<code>config</code>. In the future,
support for formats like <code>xoxo</code> could be added.</p>
<p><a href="normalization.html#overrides">Normalization overrides</a> can
also be defined here.</p>
<h3 id="template"><code>[</code><em>template</em><code>]</code></h3>
<p>Sections which are listed in <code>[planet] template_files</code> are
processed as <a href="templates.html">templates</a>. With Planet 2.0,
it is possible to override parameters like <code>items_per_page</code>
on a per template basis, but at the current time Planet Venus doesn't
implement this.</p>
<p><ins><a href="filters.html">Filters</a> can be defined on a per-template basis, and will be used to post-process the output of the template.</ins></p>
<h3 id="filter"><code>[</code><em>filter</em><code>]</code></h3>
<p>Sections which are listed in <code>[planet] filters</code> are
processed as <a href="filters.html">filters</a>.</p>
<p>Parameters which are listed in this section are passed to the filter
in a language specific manner. Given the way defaults work, filters
should be prepared to ignore parameters that they didn't expect.</p>
</body>
</html>
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN"
"http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script type="text/javascript" src="docs.js"></script>
<link rel="stylesheet" type="text/css" href="docs.css"/>
<title>Contributing</title>
</head>
<body>
<h2>Contributing</h2>
<p>If you make changes to Venus, you have no obligation to share them.
And unlike systems based on <code>CVS</code> or <code>subversion</code>,
there is no notion of &ldquo;committers&rdquo; &mdash; everybody is
a peer.</p>
<p>If you should chose to share your changes, the steps outlined below may
increase your changes of your code being picked up.</p>
<h3>Documentation and Tests</h3>
<p>For best results, include both documentation and tests in your
contribution.</p>
<p>Documentation can be found in the <code>docs</code> directory. It is
straight XHTML.</p>
<p>Test cases can be found in the
<a href="http://localhost/~rubys/venus/tests/">tests</a> directory, and
make use of the
<a href="http://docs.python.org/lib/module-unittest.html">Python Unit testing framework</a>. To run them, simply enter:</p>
<blockquote><pre>python runtests.py</pre></blockquote>
<h3>Bzr</h3>
<p>If you have done a <a href="index.html">bzr get</a>, you have already set up
a repository. The only additional step you might need to do is to introduce
yourself to <a href="http://bazaar-vcs.org/">bzr</a>. Type in the following,
after replacing the <b>bold text</b> with your information:</p>
<blockquote><pre>bzr whoami '<b>Your Name</b> &lt;<b>youremail</b>@<b>example.com</b>&gt;'</pre></blockquote>
<p>Then, simply make the changes you like. When you are done, type:</p>
<blockquote><pre>bzr st</pre></blockquote>
<p>This will tell you which files you have modified, and which ones you may
have added. If you add files and you want them to be included, simply do a:</p>
<blockquote><pre>bzr add file1 file2...</pre></blockquote>
<p>You can also do a <code>bzr diff</code> to see if there are any changes
which you made that you don't want included. I can't tell you how many
debug print statements I have caught this way.</p>
<p>Next, type:</p>
<blockquote><pre>bzr commit</pre></blockquote>
<p>This will allow you to enter a comment describing your change. If your
repository is already on your web server, simple let others know where they
can find it. If not, you can simply ftp or scp the files to your web server
&mdash; no additional software needs to be installed on that machine.</p>
<h3>Telling others</h3>
<p>Once you have a change worth sharing, post a message on the
<a href="http://lists.planetplanet.org/mailman/listinfo/devel">mailing list</a>.</p>
<p>Also, consider setting up a <a href="http://bzr.mfd-consult.dk/bzr-feed/">bzr-feed</a> for your repository, so people who wish to do so can automatically
be notified of every change.</p>
<p>There now is even an nascent <a href="http://planet.intertwingly.net/venus/">planet</a> being formed which combines these feeds of changes. You can <a href="http://planet.intertwingly.net/venus/atom.xml">subscribe</a> to it too.</p>
</body>
</html>
body {
background-color: #fff;
color: #333;
font-family: 'Lucida Grande', Verdana, Geneva, Lucida, Helvetica, sans-serif;
font-size: small;
margin: 40px;
padding: 0;
}
a:link, a:visited {
background-color: transparent;
color: #333;
text-decoration: none !important;
border-bottom: 1px dotted #333 !important;
}
a:hover {
background-color: transparent;
color: #934;
text-decoration: none !important;
border-bottom: 1px dotted #993344 !important;
}
pre, code {
background-color: #FFF;
color: #00F;
font-size: large
}
h1 {
margin: 8px 0 10px 20px;
padding: 0;
font-variant: small-caps;
letter-spacing: 0.1em;
font-family: "Book Antiqua", Georgia, Palatino, Times, "Times New Roman", serif;
}
h2 {
clear: both;
}
ul, ul.outer > li {
margin: 14px 0 10px 0;
}
.z {
float:left;
background: url(img/shadowAlpha.png) no-repeat bottom right !important;
margin: -15px 0 20px -15px !important;
}
.z .logo {
color: magenta;
}
.z p {
margin: 14px 0 10px 15px !important;
}
.z .sectionInner {
width: 730px;
background: none !important;
padding: 0 !important;
}
.z .sectionInner .sectionInner2 {
border: 1px solid #a9a9a9;
padding: 4px;
margin: -6px 6px 6px -6px !important;
}
ins {
background-color: #FFF;
color: #F0F;
text-decoration: none;
}
dl.compact {
margin-bottom: 1em;
margin-top: 1em;
}
dl.compact > dt {
clear: left;
float: left;
margin-bottom: 0;
padding-right: 8px;
margin-top: 0;
list-style-type: none;
}
dl.compact > dd {
margin-bottom: 0;
margin-top: 0;
margin-left: 10em;
}
th, td {
font-size: small;
}
window.onload=function() {
var vindex = document.URL.lastIndexOf('venus/');
if (vindex<0) vindex = document.URL.lastIndexOf('planet/');
var base = document.URL.substring(0,vindex+6);
var body = document.getElementsByTagName('body')[0];
var div = document.createElement('div');
div.setAttribute('class','z');
var h1 = document.createElement('h1');
var span = document.createElement('span');
span.appendChild(document.createTextNode('\u2640'));
span.setAttribute('class','logo');
h1.appendChild(span);
h1.appendChild(document.createTextNode(' Planet Venus'));
var inner2=document.createElement('div');
inner2.setAttribute('class','sectionInner2');
inner2.appendChild(h1);
var p = document.createElement('p');
p.appendChild(document.createTextNode("Planet Venus is an awesome \u2018river of news\u2019 feed reader. It downloads news feeds published by web sites and aggregates their content together into a single combined feed, latest news first."));
inner2.appendChild(p);
p = document.createElement('p');
var a = document.createElement('a');
a.setAttribute('href',base);
a.appendChild(document.createTextNode('Download'));
p.appendChild(a);
p.appendChild(document.createTextNode(" \u00b7 "));
a = document.createElement('a');
a.setAttribute('href',base+'docs/index.html');
a.appendChild(document.createTextNode('Documentation'));
p.appendChild(a);
p.appendChild(document.createTextNode(" \u00b7 "));
a = document.createElement('a');
a.setAttribute('href',base+'tests/');
a.appendChild(document.createTextNode('Unit tests'));
p.appendChild(a);
p.appendChild(document.createTextNode(" \u00b7 "));
a = document.createElement('a');
a.setAttribute('href','http://lists.planetplanet.org/mailman/listinfo/devel');
a.appendChild(document.createTextNode('Mailing list'));
p.appendChild(a);
inner2.appendChild(p);
var inner1=document.createElement('div');
inner1.setAttribute('class','sectionInner');
inner1.setAttribute('id','inner1');
inner1.appendChild(inner2);
div.appendChild(inner1);
body.insertBefore(div, body.firstChild);
}
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN"
"http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script type="text/javascript" src="docs.js"></script>
<link rel="stylesheet" type="text/css" href="docs.css"/>
<title>Etiquette</title>
</head>
<body>
<h2>Etiquette</h2>
<p>You would think that people who publish syndication feeds do it with the
intent to be syndicated. But the truth is that we live in a world where
<a href="http://en.wikipedia.org/wiki/Deep_linking">deep linking</a> can
cause people to complain. Nothing is safe. But that doesn&#8217;t
stop us from doing links.</p>
<p>These concerns tend to increase when you profit, either directly via ads or
indirectly via search engine rankings, from the content of others.</p>
<p>While there are no hard and fast rules that apply here, here&#8217;s are a
few things you can do to mitigate the concern:</p>
<ul>
<li>Aggressively use robots.txt, meta tags, and the google/livejournal
atom namespace to mark your pages as not to be indexed by search
engines.</li>
<blockquote><p><dl>
<dt><a href="http://www.robotstxt.org/">robots.txt</a>:</dt>
<dd><p><code>User-agent: *<br/>
Disallow: /</code></p></dd>
<dt>index.html:</dt>
<dd><p><code>&lt;<a href="http://www.robotstxt.org/wc/meta-user.html">meta name="robots"</a> content="noindex,nofollow"/&gt;</code></p></dd>
<dt>atom.xml:</dt>
<dd><p><code>&lt;feed xmlns:indexing="<a href="http://community.livejournal.com/lj_dev/696793.html">urn:atom-extension:indexing</a>" indexing:index="no"&gt;</code></p>
<p><code>&lt;access:restriction xmlns:access="<a href="http://www.bloglines.com/about/specs/fac-1.0">http://www.bloglines.com/about/specs/fac-1.0</a>" relationship="deny"/&gt;</code></p></dd>
</dl></p></blockquote>
<li><p>Ensure that all <a href="http://nightly.feedparser.org/docs/reference-entry-source.html#reference.entry.source.rights">copyright</a> and <a href="http://nightly.feedparser.org/docs/reference-entry-license.html">licensing</a> information is propagated to the
combined feed(s) that you produce.</p></li>
<li><p>Add no advertising. Consider filtering out ads, lest you
be accused of using someone&#8217;s content to help your friends profit.</p></li>
<li><p>Most importantly, if anyone does object to their content being included,
quickly and without any complaint, remove them.</p></li>
</ul>
</body>
</html>
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN"
"http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script type="text/javascript" src="docs.js"></script>
<link rel="stylesheet" type="text/css" href="docs.css"/>
<title>Venus Filters</title>
</head>
<body>
<h2>Filters and Plugins</h2>
<p>Filters and plugins are simple Unix pipes. Input comes in
<code>stdin</code>, parameters come from the config file, and output goes to
<code>stdout</code>. Anything written to <code>stderr</code> is logged as an
ERROR message. If no <code>stdout</code> is produced, the entry is not written
to the cache or processed further; in fact, if the entry had previously been
written to the cache, it will be removed.</p>
<p>There are two types of filters supported by Venus, input and template.</p>
<p>Input to an input filter is a aggressively
<a href="normalization.html">normalized</a> entry. For
example, if a feed is RSS 1.0 with 10 items, the filter will be called ten
times, each with a single Atom 1.0 entry, with all textConstructs
expressed as XHTML, and everything encoded as UTF-8.</p>
<p>Input to a template filter will be the output produced by the template.</p>
<p>You will find a small set of example filters in the <a
href="../filters">filters</a> directory. The <a
href="../filters/coral_cdn_filter.py">coral cdn filter</a> will change links
to images in the entry itself. The filters in the <a
href="../filters/stripAd/">stripAd</a> subdirectory will strip specific
types of advertisements that you may find in feeds.</p>
<p>The <a href="../filters/excerpt.py">excerpt</a> filter adds metadata (in
the form of a <code>planet:excerpt</code> element) to the feed itself. You
can see examples of how parameters are passed to this program in either
<a href="../tests/data/filter/excerpt-images.ini">excerpt-images</a> or
<a href="../examples/opml-top100.ini">opml-top100.ini</a>.
Alternately parameters may be passed
<abbr title="Uniform Resource Identifier">URI</abbr> style, for example:
<a href="../tests/data/filter/excerpt-images2.ini">excerpt-images2</a>.
</p>
<p>The <a href="../filters/xpath_sifter.py">xpath sifter</a> is a variation of
the above, including or excluding feeds based on the presence (or absence) of
data specified by <a href="http://www.w3.org/TR/xpath20/">xpath</a>
expressions. Again, parameters can be passed as
<a href="../tests/data/filter/xpath-sifter.ini">config options</a> or
<a href="../tests/data/filter/xpath-sifter2.ini">URI style</a>.
</p>
<p>The <a href="../filters/regexp_sifter.py">regexp sifter</a> operates just
like the xpath sifter, except it uses
<a href="http://docs.python.org/lib/re-syntax.html">regular expressions</a>
instead of XPath expressions.</p>
<h3>Notes</h3>
<ul>
<li>Any filters listed in the <code>[planet]</code> section of your config.ini
will be invoked on all feeds. Filters listed in individual
<code>[feed]</code> sections will only be invoked on those feeds.
Filters listed in <code>[template]</code> sections will be invoked on the
output of that template.</li>
<li>Input filters are executed when a feed is fetched, and the results are
placed into the cache. Changing a configuration file alone is not sufficient to
change the contents of the cache &mdash; typically that only occurs after
a feed is modified.</li>
<li>Filters are simply invoked in the order they are listed in the
configuration file (think unix pipes). Planet wide filters are executed before
feed specific filters.</li>
<li>The file extension of the filter is significant. <code>.py</code> invokes
python. <code>.xslt</code> involkes XSLT. <code>.sed</code> and
<code>.tmpl</code> (a.k.a. htmltmp) are also options. Other languages, like
perl or ruby or class/jar (java), aren't supported at the moment, but these
would be easy to add.</li>
<li>If the filter name contains a redirection character (<code>&gt;</code>),
then the output stream is
<a href="http://en.wikipedia.org/wiki/Tee_(Unix)">tee</a>d; one branch flows
through the specified filter and the output is planced into the named file; the
other unmodified branch continues onto the next filter, if any.
One use case for this function is to use
<a href="../filters/xhtml2html.plugin">xhtml2html</a> to produce both an XHTML
and an HTML output stream from one source.</li>
<li>Templates written using htmltmpl or django currently only have access to a
fixed set of fields, whereas XSLT and genshi templates have access to
everything.</li>
<li>Plugins differ from filters in that while filters are forked, plugins are
<a href="http://docs.python.org/lib/module-imp.html">imported</a>. This
means that plugins are limited to Python and are run in-process. Plugins
therefore have direct access to planet internals like configuration and
logging facitilies, as well as access to the bundled libraries like the
<a href="http://feedparser.org/docs/">Universal Feed Parser</a> and
<a href="http://code.google.com/p/html5lib/">html5lib</a>; but it also
means that functions like <code>os.abort()</code> can't be recovered
from.</li>
</ul>
</body>
</html>
lib/venus/docs/img/shadowAlpha.png

3.32 KiB