1<?php
2namespace LAM\PROFILES;
3use LAM\TYPES\TypeManager;
4use \LAMException;
5/*
6
7  This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
8  Copyright (C) 2003 - 2020  Roland Gruber
9
10  This program is free software; you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation; either version 2 of the License, or
13  (at your option) any later version.
14
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  GNU General Public License for more details.
19
20  You should have received a copy of the GNU General Public License
21  along with this program; if not, write to the Free Software
22  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23
24*/
25
26/**
27* This file provides functions to load and save account profiles.
28*
29* @package profiles
30* @author Roland Gruber
31*/
32
33
34/**
35* Returns an array of string with all available profiles for the given account type
36*
37* @param string $typeId account type
38* @param string $profile server profile name
39* @return array profile names
40*/
41function getAccountProfiles($typeId, $profile = null) {
42	if (!isset($profile)) {
43		$profile = $_SESSION['config']->getName();
44	}
45
46	$dir = @dir(dirname(__FILE__) . "/../config/profiles/" . $profile);
47
48	$ret = array();
49	if ($dir) {
50		$entry = $dir->read();
51		while ($entry){
52			// check if filename ends with .<typeId>
53			if (strrpos($entry, '.')) {
54				$pos = strrpos($entry, '.');
55				if (substr($entry, $pos + 1) == $typeId) {
56					$name = substr($entry, 0, $pos);
57					$ret[] = $name;
58				}
59			}
60			$entry = $dir->read();
61		}
62	}
63	return $ret;
64}
65
66/**
67 * Returns if the given profile exists.
68 *
69 * @param string name profile name
70 * @param string $typeId type id
71 * @return bool exists
72 */
73function profileExists($name, $typeId) {
74	if (!isValidProfileName($name) || !preg_match("/^[a-z0-9_]+$/i", $typeId) || ($typeId == null)) {
75		return false;
76	}
77	$file = substr(__FILE__, 0, strlen(__FILE__) - 17) . "/config/profiles/"  . $_SESSION['config']->getName() . '/' . $name . "." . $typeId;
78	return is_file($file);
79}
80
81/**
82 * Loads an profile of the given account type
83 *
84 * @param string $profile name of the profile (without .<scope> extension)
85 * @param string $typeId account type
86 * @param string $serverProfileName server profile name
87 * @return array hash array (attribute => value)
88 */
89function loadAccountProfile($profile, $typeId, $serverProfileName) {
90	if (!isValidProfileName($profile) || !preg_match("/^[a-z0-9_]+$/i", $typeId)) {
91		logNewMessage(LOG_NOTICE, "Invalid account profile name: $serverProfileName:$profile:$typeId");
92		return array();
93	}
94	$file = substr(__FILE__, 0, strlen(__FILE__) - 17) . "/config/profiles/"  . $serverProfileName . '/' . $profile . "." . $typeId;
95	try {
96		return readAccountProfileFile($file);
97	} catch (LAMException $e) {
98		StatusMessage('ERROR', $e->getTitle(), $e->getMessage());
99	}
100	return array();
101}
102
103/**
104 * Reads an account profile from the given file name.
105 *
106 * @param string $fileName file name
107 * @return array hash array (attribute => value)
108 * @throws LAMException error reading file
109 */
110function readAccountProfileFile($fileName) {
111	$settings = array();
112	if (is_file($fileName)) {
113		$file = @fopen($fileName, "r");
114		if ($file) {
115			while (!feof($file)) {
116				$line = fgets($file, 1024);
117				if (($line === false) || ($line == '') || ($line == "\n") || ($line[0] == "#")) {
118					continue; // ignore comments
119				}
120				// search keywords
121				$parts = explode(": ", $line);
122				if (sizeof($parts) == 2) {
123					$option = $parts[0];
124					$value = $parts[1];
125					// remove line ends
126					$value = chop($value);
127					$settings[$option] = explode("+::+", $value);
128				}
129			}
130			fclose($file);
131			return $settings;
132		}
133		else {
134			throw new LAMException(_("Unable to load profile!"), $fileName);
135		}
136	}
137	else {
138		throw new LAMException(_("Unable to load profile!"), $fileName);
139	}
140}
141
142/**
143* Saves an hash array (attribute => value) to an account profile
144*
145* file is created, if needed
146*
147* @param array $attributes hash array (attribute => value)
148* @param string $profile name of the account profile (without .<scope> extension)
149* @param string $typeId account type
150 * @param \LAMConfig $serverProfile server profile
151* @return boolean true, if saving succeeded
152*/
153function saveAccountProfile($attributes, $profile, $typeId, $serverProfile) {
154	// check profile name and type id
155	$typeManager = new TypeManager($serverProfile);
156	$type = $typeManager->getConfiguredType($typeId);
157	if (!isValidProfileName($profile) || !preg_match("/^[a-z0-9_]+$/i", $typeId) || ($type == null)) {
158		logNewMessage(LOG_NOTICE, 'Invalid account profile name: ' . $profile . ':' . $typeId);
159		return false;
160	}
161	if (!is_array($attributes)) {
162		logNewMessage(LOG_NOTICE, 'Invalid account profile data');
163		return false;
164	}
165	$path = substr(__FILE__, 0, strlen(__FILE__) - 17) . "/config/profiles/" . $serverProfile->getName() . '/' . $profile . "." . $typeId;
166	return writeProfileDataToFile($path, $attributes);
167}
168
169/**
170 * Writes the profile data to the given file.
171 *
172 * @param string $fileName file name
173 * @param array $data profile data
174 * @return bool writing was ok
175 */
176function writeProfileDataToFile($fileName, $data) {
177	$file = @fopen($fileName, "w");
178	if ($file) {
179		// write attributes
180		$keys = array_keys($data);
181		for ($i = 0; $i < sizeof($keys); $i++) {
182			if (isset($data[$keys[$i]])) {
183				$line = $keys[$i] . ": " . implode("+::+", $data[$keys[$i]]) . "\n";
184			}
185			else {
186				$line = $keys[$i] . ": \n";
187			}
188			fputs($file, $line);
189		}
190		// close file
191		fclose($file);
192	}
193	else {
194		logNewMessage(LOG_NOTICE, 'Unable to open account profile file: ' . $fileName);
195		return false;
196	}
197	return true;
198}
199
200/**
201* Deletes an account profile
202*
203* @param string $file name of profile (Without .<scope> extension)
204* @param string $typeId account type
205* @return boolean true if profile was deleted
206*/
207function delAccountProfile($file, $typeId) {
208	if (!isLoggedIn()) {
209		return false;
210	}
211	$typeManager = new TypeManager();
212	$type = $typeManager->getConfiguredType($typeId);
213	if (!isValidProfileName($file) || !preg_match("/^[a-z0-9_]+$/i", $typeId) || ($type == null)) {
214		return false;
215	}
216	$prof = substr(__FILE__, 0, strlen(__FILE__) - 16) . "config/profiles/". $_SESSION['config']->getName() . '/' . $file . "." . $typeId;
217	if (is_file($prof)) {
218		return @unlink($prof);
219	}
220	return false;
221}
222
223/**
224 * Returns if the given profile name is valid.
225 *
226 * @param string $name profile name
227 */
228function isValidProfileName($name) {
229	return preg_match("/^[0-9a-z _-]+$/i", $name);
230}
231
232/**
233 * Copies an account profile from the given source to target.
234 *
235 * @param \LAM\TYPES\ConfiguredType $sourceType source type
236 * @param string $sourceProfileName profile name
237 * @param \LAM\TYPES\ConfiguredType $targetType target type
238 * @throws LAMException error during copy
239 */
240function copyAccountProfile($sourceType, $sourceProfileName, $targetType) {
241	if (!isValidProfileName($sourceProfileName)) {
242		throw new LAMException(_('Failed to copy'));
243	}
244	$sourceConfig = $sourceType->getTypeManager()->getConfig()->getName();
245	$sourceTypeId = $sourceType->getId();
246	$targetConfig = $targetType->getTypeManager()->getConfig()->getName();
247	$targetTypeId = $targetType->getId();
248	$profilePath = dirname(__FILE__) . '/../config/profiles/';
249	$src = $profilePath . $sourceConfig . '/' . $sourceProfileName . '.' . $sourceTypeId;
250	$dst = $profilePath . $targetConfig . '/' . $sourceProfileName . '.' . $targetTypeId;
251	if (!@copy($src, $dst)) {
252		throw new LAMException(_('Failed to copy'), $sourceConfig . ': ' . $sourceProfileName);
253	}
254}
255
256/**
257 * Copies an account profile from the given source to global templates.
258 *
259 * @param \LAM\TYPES\ConfiguredType $sourceType source type
260 * @param string $sourceProfileName profile name
261 * @throws LAMException error during copy
262 */
263function copyAccountProfileToTemplates($sourceType, $sourceProfileName) {
264	if (!isValidProfileName($sourceProfileName)) {
265		throw new LAMException(_('Failed to copy'));
266	}
267	$sourceConfig = $sourceType->getTypeManager()->getConfig()->getName();
268	$sourceTypeId = $sourceType->getId();
269	$profilePath = dirname(__FILE__) . '/../config/profiles/';
270	$templatePath = dirname(__FILE__) . '/../config/templates/profiles/';
271	$src = $profilePath . $sourceConfig . '/' . $sourceProfileName . '.' . $sourceTypeId;
272	$dst = $templatePath . $sourceProfileName . '.' . $sourceType->getScope();
273	if (!@copy($src, $dst)) {
274		throw new LAMException(_('Failed to copy'), $sourceConfig . ': ' . $sourceProfileName);
275	}
276}
277
278/**
279 * Installs template profiles to the current server profile.
280 */
281function installProfileTemplates() {
282	$allTemplates = getProfileTemplateNames();
283	$basePath = dirname(__FILE__) . '/../config/profiles/' . $_SESSION['config']->getName();
284	if (!file_exists($basePath)) {
285		mkdir($basePath, 0700, true);
286	}
287	$typeManager = new TypeManager();
288	foreach ($typeManager->getConfiguredTypes() as $type) {
289		if (empty($allTemplates[$type->getScope()])) {
290			continue;
291		}
292		foreach ($allTemplates[$type->getScope()] as $templateName) {
293			$path = $basePath . '/' . $templateName . '.' . $type->getId();
294			if (!is_file($path)) {
295				$template = getProfileTemplateFileName($type->getScope(), $templateName);
296				logNewMessage(LOG_DEBUG, 'Copy template ' . $template . ' to ' . $path);
297				@copy($template, $path);
298			}
299		}
300	}
301}
302
303/**
304 * Returns a list of all global profile templates.
305 *
306 * @return array names (array('user' => array('default', 'extra')))
307 */
308function getProfileTemplateNames() {
309	$templatePath = __DIR__ . '/../config/templates/profiles';
310	$templateDir = @dir($templatePath);
311	$allTemplates = array();
312	if ($templateDir) {
313		$entry = $templateDir->read();
314		while ($entry){
315			$parts = explode('.', $entry);
316			if ((strlen($entry) > 3) && (sizeof($parts) == 2)) {
317				$name = $parts[0];
318				$scope = $parts[1];
319				$allTemplates[$scope][] = $name;
320			}
321			$entry = $templateDir->read();
322		}
323	}
324	return $allTemplates;
325}
326
327/**
328 * Returns the file name of a global template.
329 *
330 * @param string $scope e.g. user
331 * @param string $name profile name
332 * @return string file name
333 */
334function getProfileTemplateFileName($scope, $name) {
335	return __DIR__ . '/../config/templates/profiles' . '/' . $name . '.' . $scope;
336}
337
338/**
339 * Loads a template profile of the given account scope.
340 *
341 * @param string $profile name of the profile (without .<scope> extension)
342 * @param string $scope account type
343 * @return array hash array (attribute => value)
344 * @throws LAMException error reading profile template
345 */
346function loadTemplateAccountProfile($profile, $scope) {
347	if (!isValidProfileName($profile) || !preg_match("/^[a-z0-9_]+$/i", $scope)) {
348		logNewMessage(LOG_NOTICE, "Invalid account profile name: $profile:$scope");
349		return array();
350	}
351	$fileName = getProfileTemplateFileName($scope, $profile);
352	return readAccountProfileFile($fileName);
353}
354
355/**
356 * Installs a single template from the given data.
357 *
358 * @param string $scope account type (e.g. user)
359 * @param string $name template name
360 * @param array $data profile data
361 * @throws LAMException error saving file
362 */
363function installTemplateAccountProfile($scope, $name, $data) {
364	if (!isValidProfileName($name) || !preg_match("/^[a-z0-9_]+$/i", $scope)) {
365		logNewMessage(LOG_NOTICE, "Invalid account profile name: $name:$scope");
366		return;
367	}
368	$fileName = getProfileTemplateFileName($scope, $name);
369	$success = writeProfileDataToFile($fileName, $data);
370	if (!$success) {
371		throw new LAMException('Unable to write account profile template: ' . $fileName);
372	}
373}
374
375