PageRenderTime 66ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 1ms

/Bugzilla/Config/Common.pm

https://code.google.com/p/bugzilla4intranet/
Perl | 465 lines | 305 code | 51 blank | 109 comment | 61 complexity | 817571242c0b624b3c38b6aaf6e1c224 MD5 | raw file
  1. # -*- Mode: perl; indent-tabs-mode: nil -*-
  2. #
  3. # The contents of this file are subject to the Mozilla Public
  4. # License Version 1.1 (the "License"); you may not use this file
  5. # except in compliance with the License. You may obtain a copy of
  6. # the License at http://www.mozilla.org/MPL/
  7. #
  8. # Software distributed under the License is distributed on an "AS
  9. # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  10. # implied. See the License for the specific language governing
  11. # rights and limitations under the License.
  12. #
  13. # The Original Code is the Bugzilla Bug Tracking System.
  14. #
  15. # The Initial Developer of the Original Code is Netscape Communications
  16. # Corporation. Portions created by Netscape are
  17. # Copyright (C) 1998 Netscape Communications Corporation. All
  18. # Rights Reserved.
  19. #
  20. # Contributor(s): Terry Weissman <terry@mozilla.org>
  21. # Dawn Endico <endico@mozilla.org>
  22. # Dan Mosedale <dmose@mozilla.org>
  23. # Joe Robins <jmrobins@tgix.com>
  24. # Jacob Steenhagen <jake@bugzilla.org>
  25. # J. Paul Reed <preed@sigkill.com>
  26. # Bradley Baetz <bbaetz@student.usyd.edu.au>
  27. # Joseph Heenan <joseph@heenan.me.uk>
  28. # Erik Stambaugh <erik@dasbistro.com>
  29. # Fr?Šd?Šric Buclin <LpSolit@gmail.com>
  30. # Marc Schumann <wurblzap@gmail.com>
  31. #
  32. package Bugzilla::Config::Common;
  33. use strict;
  34. use Email::Address;
  35. use Socket;
  36. use Bugzilla::Util;
  37. use Bugzilla::Constants;
  38. use Bugzilla::Field;
  39. use Bugzilla::Group;
  40. use Bugzilla::Status;
  41. use base qw(Exporter);
  42. @Bugzilla::Config::Common::EXPORT =
  43. qw(check_multi check_numeric check_regexp check_url check_group
  44. check_sslbase check_priority check_severity check_platform
  45. check_opsys check_shadowdb check_urlbase check_webdotbase
  46. check_user_verify_class
  47. check_mail_delivery_method check_notification check_utf8
  48. check_bug_status check_smtp_auth check_theschwartz_available
  49. check_maxattachmentsize check_email
  50. );
  51. # Checking functions for the various values
  52. sub check_multi {
  53. my ($value, $param) = (@_);
  54. if ($param->{'type'} eq "s") {
  55. unless (scalar(grep {$_ eq $value} (@{$param->{'choices'}}))) {
  56. return "Invalid choice '$value' for single-select list param '$param->{'name'}'";
  57. }
  58. return "";
  59. }
  60. elsif ($param->{'type'} eq 'm' || $param->{'type'} eq 'o') {
  61. foreach my $chkParam (split(',', $value)) {
  62. unless (scalar(grep {$_ eq $chkParam} (@{$param->{'choices'}}))) {
  63. return "Invalid choice '$chkParam' for multi-select list param '$param->{'name'}'";
  64. }
  65. }
  66. return "";
  67. }
  68. else {
  69. return "Invalid param type '$param->{'type'}' for check_multi(); " .
  70. "contact your Bugzilla administrator";
  71. }
  72. }
  73. sub check_numeric {
  74. my ($value) = (@_);
  75. if ($value !~ /^[0-9]+$/) {
  76. return "must be a numeric value";
  77. }
  78. return "";
  79. }
  80. sub check_regexp {
  81. my ($value) = (@_);
  82. eval { qr/$value/ };
  83. return $@;
  84. }
  85. sub check_email {
  86. my ($value) = @_;
  87. if ($value !~ $Email::Address::mailbox) {
  88. return "must be a valid email address.";
  89. }
  90. return "";
  91. }
  92. sub check_sslbase {
  93. my $url = shift;
  94. if ($url ne '') {
  95. if ($url !~ m#^https://([^/]+).*/$#) {
  96. return "must be a legal URL, that starts with https and ends with a slash.";
  97. }
  98. my $host = $1;
  99. # Fall back to port 443 if for some reason getservbyname() fails.
  100. my $port = getservbyname('https', 'tcp') || 443;
  101. if ($host =~ /^(.+):(\d+)$/) {
  102. $host = $1;
  103. $port = $2;
  104. }
  105. local *SOCK;
  106. my $proto = getprotobyname('tcp');
  107. socket(SOCK, PF_INET, SOCK_STREAM, $proto);
  108. my $iaddr = inet_aton($host) || return "The host $host cannot be resolved";
  109. my $sin = sockaddr_in($port, $iaddr);
  110. if (!connect(SOCK, $sin)) {
  111. return "Failed to connect to $host:$port; unable to enable SSL";
  112. }
  113. close(SOCK);
  114. }
  115. return "";
  116. }
  117. sub check_utf8 {
  118. my $utf8 = shift;
  119. # You cannot turn off the UTF-8 parameter if you've already converted
  120. # your tables to utf-8.
  121. my $dbh = Bugzilla->dbh;
  122. if ($dbh->isa('Bugzilla::DB::Mysql') && $dbh->bz_db_is_utf8 && !$utf8) {
  123. return "You cannot disable UTF-8 support, because your MySQL database"
  124. . " is encoded in UTF-8";
  125. }
  126. return "";
  127. }
  128. sub check_priority {
  129. my ($value) = (@_);
  130. my $legal_priorities = get_legal_field_values('priority');
  131. if (!grep { $_ eq $value } @$legal_priorities)
  132. {
  133. return "Must be a legal priority value: one of " .
  134. join(", ", @$legal_priorities);
  135. }
  136. return "";
  137. }
  138. sub check_severity {
  139. my ($value) = (@_);
  140. my $legal_severities = get_legal_field_values('bug_severity');
  141. if (!grep { $_ eq $value } @$legal_severities)
  142. {
  143. return "Must be a legal severity value: one of " .
  144. join(", ", @$legal_severities);
  145. }
  146. return "";
  147. }
  148. sub check_platform {
  149. my ($value) = (@_);
  150. my $legal_platforms = get_legal_field_values('rep_platform');
  151. if (!grep { $_ eq $value } '', @$legal_platforms)
  152. {
  153. return "Must be empty or a legal platform value: one of " .
  154. join(", ", @$legal_platforms);
  155. }
  156. return "";
  157. }
  158. sub check_opsys {
  159. my ($value) = (@_);
  160. my $legal_OS = get_legal_field_values('op_sys');
  161. if (!grep { $_ eq $value } '', @$legal_OS)
  162. {
  163. return "Must be empty or a legal operating system value: one of " .
  164. join(", ", @$legal_OS);
  165. }
  166. return "";
  167. }
  168. sub check_bug_status {
  169. my $bug_status = shift;
  170. my @closed_bug_statuses = map {$_->name} closed_bug_statuses();
  171. if (!grep { $_ eq $bug_status } @closed_bug_statuses)
  172. {
  173. return "Must be a valid closed status: one of " . join(', ', @closed_bug_statuses);
  174. }
  175. return "";
  176. }
  177. sub check_group {
  178. my $group_name = shift;
  179. return "" unless $group_name;
  180. my $group = new Bugzilla::Group({'name' => $group_name});
  181. unless (defined $group) {
  182. return "Must be an existing group name";
  183. }
  184. return "";
  185. }
  186. sub check_shadowdb {
  187. my ($value) = (@_);
  188. $value = trim($value);
  189. if ($value eq "") {
  190. return "";
  191. }
  192. if (!Bugzilla->params->{'shadowdbhost'}) {
  193. return "You need to specify a host when using a shadow database";
  194. }
  195. # Can't test existence of this because ConnectToDatabase uses the param,
  196. # but we can't set this before testing....
  197. # This can really only be fixed after we can use the DBI more openly
  198. return "";
  199. }
  200. sub check_urlbase {
  201. my ($url) = (@_);
  202. if ($url && $url !~ m:^http.*/$:) {
  203. return "must be a legal URL, that starts with http and ends with a slash.";
  204. }
  205. return "";
  206. }
  207. sub check_url {
  208. my ($url) = (@_);
  209. return '' if $url eq ''; # Allow empty URLs
  210. if ($url !~ m:/$:) {
  211. return 'must be a legal URL, absolute or relative, ending with a slash.';
  212. }
  213. return '';
  214. }
  215. sub check_webdotbase {
  216. my ($value) = (@_);
  217. $value = trim($value);
  218. if ($value eq "") {
  219. return "";
  220. }
  221. if ($value !~ /^https?:/) {
  222. if (!-x $value) {
  223. return "The file path \"$value\" is not a valid executable. Please specify the complete file path to 'dot' if you intend to generate graphs locally.";
  224. }
  225. # Check .htaccess allows access to generated images
  226. my $webdotdir = bz_locations()->{'webdotdir'};
  227. if (-e "$webdotdir/.htaccess") {
  228. open HTACCESS, "$webdotdir/.htaccess";
  229. local $/ = undef;
  230. if (!grep /png/,<HTACCESS>) {
  231. return "Dependency graph images are not accessible.\nAssuming that you have not modified the file, delete $webdotdir/.htaccess and re-run checksetup.pl to rectify.\n";
  232. }
  233. close HTACCESS;
  234. }
  235. }
  236. return "";
  237. }
  238. sub check_user_verify_class {
  239. # doeditparams traverses the list of params, and for each one it checks,
  240. # then updates. This means that if one param checker wants to look at
  241. # other params, it must be below that other one. So you can't have two
  242. # params mutually dependent on each other.
  243. # This means that if someone clears the LDAP config params after setting
  244. # the login method as LDAP, we won't notice, but all logins will fail.
  245. # So don't do that.
  246. my $params = Bugzilla->params;
  247. my ($list, $entry) = @_;
  248. $list || return 'You need to specify at least one authentication mechanism';
  249. for my $class (split /,\s*/, $list) {
  250. my $res = check_multi($class, $entry);
  251. return $res if $res;
  252. if ($class eq 'RADIUS') {
  253. if (!Bugzilla->feature('auth_radius')) {
  254. return "RADIUS support is not available. Run checksetup.pl"
  255. . " for more details";
  256. }
  257. return "RADIUS servername (RADIUS_server) is missing"
  258. if !$params->{"RADIUS_server"};
  259. return "RADIUS_secret is empty" if !$params->{"RADIUS_secret"};
  260. }
  261. elsif ($class eq 'LDAP') {
  262. if (!Bugzilla->feature('auth_ldap')) {
  263. return "LDAP support is not available. Run checksetup.pl"
  264. . " for more details";
  265. }
  266. return "LDAP servername (LDAPserver) is missing"
  267. if !$params->{"LDAPserver"};
  268. return "LDAPBaseDN is empty" if !$params->{"LDAPBaseDN"};
  269. }
  270. }
  271. return "";
  272. }
  273. sub check_mail_delivery_method {
  274. my $check = check_multi(@_);
  275. return $check if $check;
  276. my $mailer = shift;
  277. if ($mailer eq 'sendmail' and ON_WINDOWS) {
  278. # look for sendmail.exe
  279. return "Failed to locate " . SENDMAIL_EXE
  280. unless -e SENDMAIL_EXE;
  281. }
  282. return "";
  283. }
  284. sub check_maxattachmentsize {
  285. my $check = check_numeric(@_);
  286. return $check if $check;
  287. my $size = shift;
  288. my $dbh = Bugzilla->dbh;
  289. if ($dbh->isa('Bugzilla::DB::Mysql')) {
  290. my (undef, $max_packet) = $dbh->selectrow_array(
  291. q{SHOW VARIABLES LIKE 'max\_allowed\_packet'});
  292. my $byte_size = $size * 1024;
  293. if ($max_packet < $byte_size) {
  294. return "You asked for a maxattachmentsize of $byte_size bytes,"
  295. . " but the max_allowed_packet setting in MySQL currently"
  296. . " only allows packets up to $max_packet bytes";
  297. }
  298. }
  299. return "";
  300. }
  301. sub check_notification {
  302. my $option = shift;
  303. my @current_version =
  304. (BUGZILLA_VERSION =~ m/^(\d+)\.(\d+)(?:(rc|\.)(\d+))?\+?$/);
  305. if ($current_version[1] % 2 && $option eq 'stable_branch_release') {
  306. return "You are currently running a development snapshot, and so your " .
  307. "installation is not based on a branch. If you want to be notified " .
  308. "about the next stable release, you should select " .
  309. "'latest_stable_release' instead";
  310. }
  311. if ($option ne 'disabled' && !Bugzilla->feature('updates')) {
  312. return "Some Perl modules are missing to get notifications about " .
  313. "new releases. See the output of checksetup.pl for more information";
  314. }
  315. return "";
  316. }
  317. sub check_smtp_auth {
  318. my $username = shift;
  319. if ($username and !Bugzilla->feature('smtp_auth')) {
  320. return "SMTP Authentication is not available. Run checksetup.pl for"
  321. . " more details";
  322. }
  323. return "";
  324. }
  325. sub check_theschwartz_available {
  326. my $use_queue = shift;
  327. if ($use_queue && !Bugzilla->feature('jobqueue')) {
  328. return "Using the job queue requires that you have certain Perl"
  329. . " modules installed. See the output of checksetup.pl"
  330. . " for more information";
  331. }
  332. return "";
  333. }
  334. # OK, here are the parameter definitions themselves.
  335. #
  336. # Each definition is a hash with keys:
  337. #
  338. # name - name of the param
  339. # desc - description of the param (for editparams.cgi)
  340. # type - see below
  341. # choices - (optional) see below
  342. # default - default value for the param
  343. # checker - (optional) checking function for validating parameter entry
  344. # It is called with the value of the param as the first arg and a
  345. # reference to the param's hash as the second argument
  346. #
  347. # The type value can be one of the following:
  348. #
  349. # t -- A short text entry field (suitable for a single line)
  350. # p -- A short text entry field (as with type = 't'), but the string is
  351. # replaced by asterisks (appropriate for passwords)
  352. # l -- A long text field (suitable for many lines)
  353. # b -- A boolean value (either 1 or 0)
  354. # m -- A list of values, with many selectable (shows up as a select box)
  355. # To specify the list of values, make the 'choices' key be an array
  356. # reference of the valid choices. The 'default' key should be a string
  357. # with a list of selected values (as a comma-separated list), i.e.:
  358. # {
  359. # name => 'multiselect',
  360. # desc => 'A list of options, choose many',
  361. # type => 'm',
  362. # choices => [ 'a', 'b', 'c', 'd' ],
  363. # default => [ 'a', 'd' ],
  364. # checker => \&check_multi
  365. # }
  366. #
  367. # Here, 'a' and 'd' are the default options, and the user may pick any
  368. # combination of a, b, c, and d as valid options.
  369. #
  370. # &check_multi should always be used as the param verification function
  371. # for list (single and multiple) parameter types.
  372. #
  373. # o -- A list of values, orderable, and with many selectable (shows up as a
  374. # JavaScript-enhanced select box if JavaScript is enabled, and a text
  375. # entry field if not)
  376. # Set up in the same way as type m.
  377. #
  378. # s -- A list of values, with one selectable (shows up as a select box)
  379. # To specify the list of values, make the 'choices' key be an array
  380. # reference of the valid choices. The 'default' key should be one of
  381. # those values, i.e.:
  382. # {
  383. # name => 'singleselect',
  384. # desc => 'A list of options, choose one',
  385. # type => 's',
  386. # choices => [ 'a', 'b', 'c' ],
  387. # default => 'b',
  388. # checker => \&check_multi
  389. # }
  390. #
  391. # Here, 'b' is the default option, and 'a' and 'c' are other possible
  392. # options, but only one at a time!
  393. #
  394. # &check_multi should always be used as the param verification function
  395. # for list (single and multiple) parameter types.
  396. sub get_param_list {
  397. return;
  398. }
  399. 1;
  400. __END__
  401. =head1 NAME
  402. Bugzilla::Config::Common - Parameter checking functions
  403. =head1 DESCRIPTION
  404. All parameter checking functions are called with two parameters:
  405. =head2 Functions
  406. =over
  407. =item C<check_multi>
  408. Checks that a multi-valued parameter (ie types C<s>, C<o> or C<m>) satisfies
  409. its contraints.
  410. =item C<check_numeric>
  411. Checks that the value is a valid number
  412. =item C<check_regexp>
  413. Checks that the value is a valid regexp
  414. =back