Common.pm 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  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 Socket;
  35. use Time::Zone;
  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_netmask check_user_verify_class check_image_converter
  47. check_mail_delivery_method check_notification check_timezone check_utf8
  48. check_bug_status check_smtp_auth
  49. check_maxattachmentsize
  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_sslbase {
  86. my $url = shift;
  87. if ($url ne '') {
  88. if ($url !~ m#^https://([^/]+).*/$#) {
  89. return "must be a legal URL, that starts with https and ends with a slash.";
  90. }
  91. my $host = $1;
  92. # Fall back to port 443 if for some reason getservbyname() fails.
  93. my $port = getservbyname('https', 'tcp') || 443;
  94. if ($host =~ /^(.+):(\d+)$/) {
  95. $host = $1;
  96. $port = $2;
  97. }
  98. local *SOCK;
  99. my $proto = getprotobyname('tcp');
  100. socket(SOCK, PF_INET, SOCK_STREAM, $proto);
  101. my $iaddr = inet_aton($host) || return "The host $host cannot be resolved";
  102. my $sin = sockaddr_in($port, $iaddr);
  103. if (!connect(SOCK, $sin)) {
  104. return "Failed to connect to $host:$port; unable to enable SSL";
  105. }
  106. close(SOCK);
  107. }
  108. return "";
  109. }
  110. sub check_utf8 {
  111. my $utf8 = shift;
  112. # You cannot turn off the UTF-8 parameter if you've already converted
  113. # your tables to utf-8.
  114. my $dbh = Bugzilla->dbh;
  115. if ($dbh->isa('Bugzilla::DB::Mysql') && $dbh->bz_db_is_utf8 && !$utf8) {
  116. return "You cannot disable UTF-8 support, because your MySQL database"
  117. . " is encoded in UTF-8";
  118. }
  119. return "";
  120. }
  121. sub check_priority {
  122. my ($value) = (@_);
  123. my $legal_priorities = get_legal_field_values('priority');
  124. if (lsearch($legal_priorities, $value) < 0) {
  125. return "Must be a legal priority value: one of " .
  126. join(", ", @$legal_priorities);
  127. }
  128. return "";
  129. }
  130. sub check_severity {
  131. my ($value) = (@_);
  132. my $legal_severities = get_legal_field_values('bug_severity');
  133. if (lsearch($legal_severities, $value) < 0) {
  134. return "Must be a legal severity value: one of " .
  135. join(", ", @$legal_severities);
  136. }
  137. return "";
  138. }
  139. sub check_platform {
  140. my ($value) = (@_);
  141. my $legal_platforms = get_legal_field_values('rep_platform');
  142. if (lsearch(['', @$legal_platforms], $value) < 0) {
  143. return "Must be empty or a legal platform value: one of " .
  144. join(", ", @$legal_platforms);
  145. }
  146. return "";
  147. }
  148. sub check_opsys {
  149. my ($value) = (@_);
  150. my $legal_OS = get_legal_field_values('op_sys');
  151. if (lsearch(['', @$legal_OS], $value) < 0) {
  152. return "Must be empty or a legal operating system value: one of " .
  153. join(", ", @$legal_OS);
  154. }
  155. return "";
  156. }
  157. sub check_bug_status {
  158. my $bug_status = shift;
  159. my @closed_bug_statuses = map {$_->name} closed_bug_statuses();
  160. if (lsearch(\@closed_bug_statuses, $bug_status) < 0) {
  161. return "Must be a valid closed status: one of " . join(', ', @closed_bug_statuses);
  162. }
  163. return "";
  164. }
  165. sub check_group {
  166. my $group_name = shift;
  167. return "" unless $group_name;
  168. my $group = new Bugzilla::Group({'name' => $group_name});
  169. unless (defined $group) {
  170. return "Must be an existing group name";
  171. }
  172. return "";
  173. }
  174. sub check_shadowdb {
  175. my ($value) = (@_);
  176. $value = trim($value);
  177. if ($value eq "") {
  178. return "";
  179. }
  180. if (!Bugzilla->params->{'shadowdbhost'}) {
  181. return "You need to specify a host when using a shadow database";
  182. }
  183. # Can't test existence of this because ConnectToDatabase uses the param,
  184. # but we can't set this before testing....
  185. # This can really only be fixed after we can use the DBI more openly
  186. return "";
  187. }
  188. sub check_urlbase {
  189. my ($url) = (@_);
  190. if ($url && $url !~ m:^http.*/$:) {
  191. return "must be a legal URL, that starts with http and ends with a slash.";
  192. }
  193. return "";
  194. }
  195. sub check_url {
  196. my ($url) = (@_);
  197. return '' if $url eq ''; # Allow empty URLs
  198. if ($url !~ m:/$:) {
  199. return 'must be a legal URL, absolute or relative, ending with a slash.';
  200. }
  201. return '';
  202. }
  203. sub check_webdotbase {
  204. my ($value) = (@_);
  205. $value = trim($value);
  206. if ($value eq "") {
  207. return "";
  208. }
  209. if($value !~ /^https?:/) {
  210. if(! -x $value) {
  211. 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.";
  212. }
  213. # Check .htaccess allows access to generated images
  214. my $webdotdir = bz_locations()->{'webdotdir'};
  215. if(-e "$webdotdir/.htaccess") {
  216. open HTACCESS, "$webdotdir/.htaccess";
  217. if(! grep(/ \\\.png\$/,<HTACCESS>)) {
  218. 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";
  219. }
  220. close HTACCESS;
  221. }
  222. }
  223. return "";
  224. }
  225. sub check_netmask {
  226. my ($mask) = @_;
  227. my $res = check_numeric($mask);
  228. return $res if $res;
  229. if ($mask < 0 || $mask > 32) {
  230. return "an IPv4 netmask must be between 0 and 32 bits";
  231. }
  232. # Note that if we changed the netmask from anything apart from 32, then
  233. # existing logincookies which aren't for a single IP won't work
  234. # any more. We can't know which ones they are, though, so they'll just
  235. # take space until they're periodically cleared, later.
  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 ($list, $entry) = @_;
  247. $list || return 'You need to specify at least one authentication mechanism';
  248. for my $class (split /,\s*/, $list) {
  249. my $res = check_multi($class, $entry);
  250. return $res if $res;
  251. if ($class eq 'DB') {
  252. # No params
  253. }
  254. elsif ($class eq 'RADIUS') {
  255. eval "require Authen::Radius";
  256. return "Error requiring Authen::Radius: '$@'" if $@;
  257. return "RADIUS servername (RADIUS_server) is missing" unless Bugzilla->params->{"RADIUS_server"};
  258. return "RADIUS_secret is empty" unless Bugzilla->params->{"RADIUS_secret"};
  259. }
  260. elsif ($class eq 'LDAP') {
  261. eval "require Net::LDAP";
  262. return "Error requiring Net::LDAP: '$@'" if $@;
  263. return "LDAP servername (LDAPserver) is missing" unless Bugzilla->params->{"LDAPserver"};
  264. return "LDAPBaseDN is empty" unless Bugzilla->params->{"LDAPBaseDN"};
  265. }
  266. else {
  267. return "Unknown user_verify_class '$class' in check_user_verify_class";
  268. }
  269. }
  270. return "";
  271. }
  272. sub check_image_converter {
  273. my ($value, $hash) = @_;
  274. if ($value == 1){
  275. eval "require Image::Magick";
  276. return "Error requiring Image::Magick: '$@'" if $@;
  277. }
  278. return "";
  279. }
  280. sub check_mail_delivery_method {
  281. my $check = check_multi(@_);
  282. return $check if $check;
  283. my $mailer = shift;
  284. if ($mailer eq 'sendmail' && $^O =~ /MSWin32/i) {
  285. # look for sendmail.exe
  286. return "Failed to locate " . SENDMAIL_EXE
  287. unless -e SENDMAIL_EXE;
  288. }
  289. return "";
  290. }
  291. sub check_maxattachmentsize {
  292. my $check = check_numeric(@_);
  293. return $check if $check;
  294. my $size = shift;
  295. my $dbh = Bugzilla->dbh;
  296. if ($dbh->isa('Bugzilla::DB::Mysql')) {
  297. my (undef, $max_packet) = $dbh->selectrow_array(
  298. q{SHOW VARIABLES LIKE 'max\_allowed\_packet'});
  299. my $byte_size = $size * 1024;
  300. if ($max_packet < $byte_size) {
  301. return "You asked for a maxattachmentsize of $byte_size bytes,"
  302. . " but the max_allowed_packet setting in MySQL currently"
  303. . " only allows packets up to $max_packet bytes";
  304. }
  305. }
  306. return "";
  307. }
  308. sub check_notification {
  309. my $option = shift;
  310. my @current_version =
  311. (BUGZILLA_VERSION =~ m/^(\d+)\.(\d+)(?:(rc|\.)(\d+))?\+?$/);
  312. if ($current_version[1] % 2 && $option eq 'stable_branch_release') {
  313. return "You are currently running a development snapshot, and so your " .
  314. "installation is not based on a branch. If you want to be notified " .
  315. "about the next stable release, you should select " .
  316. "'latest_stable_release' instead";
  317. }
  318. return "";
  319. }
  320. sub check_timezone {
  321. my $tz = shift;
  322. unless (defined(tz_offset($tz))) {
  323. return "must be empty or a legal timezone name, such as PDT or JST";
  324. }
  325. return "";
  326. }
  327. sub check_smtp_auth {
  328. my $username = shift;
  329. if ($username) {
  330. eval "require Authen::SASL";
  331. return "Error requiring Authen::SASL: '$@'" if $@;
  332. }
  333. return "";
  334. }
  335. # OK, here are the parameter definitions themselves.
  336. #
  337. # Each definition is a hash with keys:
  338. #
  339. # name - name of the param
  340. # desc - description of the param (for editparams.cgi)
  341. # type - see below
  342. # choices - (optional) see below
  343. # default - default value for the param
  344. # checker - (optional) checking function for validating parameter entry
  345. # It is called with the value of the param as the first arg and a
  346. # reference to the param's hash as the second argument
  347. #
  348. # The type value can be one of the following:
  349. #
  350. # t -- A short text entry field (suitable for a single line)
  351. # p -- A short text entry field (as with type = 't'), but the string is
  352. # replaced by asterisks (appropriate for passwords)
  353. # l -- A long text field (suitable for many lines)
  354. # b -- A boolean value (either 1 or 0)
  355. # m -- A list of values, with many selectable (shows up as a select box)
  356. # To specify the list of values, make the 'choices' key be an array
  357. # reference of the valid choices. The 'default' key should be a string
  358. # with a list of selected values (as a comma-separated list), i.e.:
  359. # {
  360. # name => 'multiselect',
  361. # desc => 'A list of options, choose many',
  362. # type => 'm',
  363. # choices => [ 'a', 'b', 'c', 'd' ],
  364. # default => [ 'a', 'd' ],
  365. # checker => \&check_multi
  366. # }
  367. #
  368. # Here, 'a' and 'd' are the default options, and the user may pick any
  369. # combination of a, b, c, and d as valid options.
  370. #
  371. # &check_multi should always be used as the param verification function
  372. # for list (single and multiple) parameter types.
  373. #
  374. # o -- A list of values, orderable, and with many selectable (shows up as a
  375. # JavaScript-enhanced select box if JavaScript is enabled, and a text
  376. # entry field if not)
  377. # Set up in the same way as type m.
  378. #
  379. # s -- A list of values, with one selectable (shows up as a select box)
  380. # To specify the list of values, make the 'choices' key be an array
  381. # reference of the valid choices. The 'default' key should be one of
  382. # those values, i.e.:
  383. # {
  384. # name => 'singleselect',
  385. # desc => 'A list of options, choose one',
  386. # type => 's',
  387. # choices => [ 'a', 'b', 'c' ],
  388. # default => 'b',
  389. # checker => \&check_multi
  390. # }
  391. #
  392. # Here, 'b' is the default option, and 'a' and 'c' are other possible
  393. # options, but only one at a time!
  394. #
  395. # &check_multi should always be used as the param verification function
  396. # for list (single and multiple) parameter types.
  397. sub get_param_list {
  398. return;
  399. }
  400. 1;
  401. __END__
  402. =head1 NAME
  403. Bugzilla::Config::Common - Parameter checking functions
  404. =head1 DESCRIPTION
  405. All parameter checking functions are called with two parameters:
  406. =head2 Functions
  407. =over
  408. =item C<check_multi>
  409. Checks that a multi-valued parameter (ie types C<s>, C<o> or C<m>) satisfies
  410. its contraints.
  411. =item C<check_numeric>
  412. Checks that the value is a valid number
  413. =item C<check_regexp>
  414. Checks that the value is a valid regexp
  415. =back