Делаем FTP и Web FileShare
Не знаю как вас, но меня уже давно задолбал глючный падающий FTP протокол. Периодически слышу от друзей: не работает, не загружает, а как им пользоваться? В общем я серьезно задумался над запуском какого нибудь простенького Web файл менеджера. К сожалению такой подход не давал простой загрузки папок и файлов. И отказаться полностью от FTP не получилось. Так что моей задачей стала объедение пользователей двух систем. Чем мы и займемся :-)
FTP у меня стоит Pure-FTPd. Man здесь. Мне просто он больше всего нравится.Встроенная поддержка многих СУБД. Контроль над uid gid пользователя, ограничения его по IP и а главное он сам умет создать директорию пользователя, если такой нет. Там еще есть ограничения по скорости и еще что-то. Но я этим не пользуюсь. Хотя при желании можно использовать и другие FTP сервера, лишь бы в них можно было выбирать информацию из MySql своими запросами.
С Web File Share все намного сложнее. Их очень много, и я даже нашел пару красивых движков на Ajax, например AjaxPlorer. Но вскоре отказался в пользу File Thingie. Потому что они все жутко тормозные и глючные, и еще хранят гигами какой-то непонятный cache. Да и с мобильных на них заходить бесполезно. Ну и в конце концов простой php file manager проще под себя переписывать.
Давай те посмотрим, а что же нам надо реализовать. Для прав записи в FTP нам нужен соответствующий пользователь в системе, а точнее его ID. Для Web Share нам просто нужны флаги на право изменять, создавать, закачивать. плюс к обоим еще и на просмотр. Не плохо правда? Совершенно разные поля, и если посчитать то шесть штук. Многовато конечно, зато при таком раскладе можно будет по отдельности вкл/выкл протоколы.
Поговорим о модели. В силу того что в File Thingie есть пользователи и группы, я решил не отходить от этой структуры. Если с группой все просто, там будет храниться только путь к домашней папке. То с пользователями посложнее. Тут я еще добавил статус пользователя и список IP. А так как у нас получилось прилично прав, то мы их тоже вынесем в отдельную таблицу. Так мы сможем в дальнейшем добавлять какие-то свои флаги без особых проблем и потери наглядности. Давайте уже напишем сами таблицы.
CREATE TABLE `Users` (
`id` INT(10) NOT NULL AUTO_INCREMENT,
`Pid` INT(10) NOT NULL COMMENT 'Permission ID',
`Gid` INT(10) NOT NULL COMMENT 'Goup ID',
`Status` ENUM('0','1') NOT NULL DEFAULT '0',
`User` VARCHAR(16) NOT NULL,
`Password` VARCHAR(16) NOT NULL,
`IPAccess` VARCHAR(512) NOT NULL DEFAULT '*' COMMENT 'Split IP by \';\'',
`Comment` TEXT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `User` (`User`)
)
COLLATE='utf8_general_ci'
ENGINE=MyISAM
ROW_FORMAT=DEFAULT
CREATE TABLE `Groups` (
`id` INT(10) NOT NULL AUTO_INCREMENT,
`Name` VARCHAR(16) NOT NULL DEFAULT '',
`Dir` VARCHAR(64) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `id` (`id`)
)
COLLATE='utf8_general_ci'
ENGINE=MyISAM
ROW_FORMAT=DEFAULT
CREATE TABLE `Permissions` (
`id` INT(10) NOT NULL AUTO_INCREMENT,
`Name` VARCHAR(32) NOT NULL DEFAULT '',
`FTP_Read` ENUM('0','1') NOT NULL DEFAULT '1' COMMENT 'All privileges for FTP',
`FTP_Write` ENUM('0','1') NOT NULL DEFAULT '0' COMMENT 'All privileges for FTP',
`Share_Read` ENUM('0','1') NOT NULL DEFAULT '1' COMMENT 'To view data via Share',
`Share_Action` ENUM('0','1') NOT NULL DEFAULT '0' COMMENT 'Set to FALSE if you want to disable file actions (rename, move, delete, edit, duplicate)',
`Share_Create` ENUM('0','1') NOT NULL DEFAULT '1' COMMENT 'Set to FALSE if you want to disable file/folder/url creation',
`Share_Upload` ENUM('0','1') NOT NULL DEFAULT '1' COMMENT 'Set to FALSE if you want to disable file uploads',
PRIMARY KEY (`id`),
UNIQUE INDEX `id` (`id`)
)
COLLATE='utf8_general_ci'
ENGINE=MyISAM
ROW_FORMAT=DEFAULT
Ну и вставим пользователя
INSERT INTO `Permissions` (`Name`, `FTP_Read`, `FTP_Write`, `Share_Read`, `Share_Action`, `Share_Create`, `Share_Upload`) VALUES ('All Permission', '1', '1', '1', '1', '1', '1');
INSERT INTO `Groups` (`Name`, `Dir`) VALUES ('Sample Share', 'share/Sample_Share');
INSERT INTO `Users` (`Pid`, `Gid`, `Status`, `User`, `Password`, `IPAccess`, `Comment`) VALUES (1, 1, '1', 'B7W', '123', '*', 'Первый юзвер!');
Я думаю тут более менее все понятно. Кроме пару моментов. Начнем с IPAccess
. Сюда можно писать IP адреса с которых возможен вход или '*' для любого. Выбирать эти адреса мы будем с помощью LIKE "%UserIP%"
, поэтому нам плевать чем мы будем разделять их между собой.
Поле Dir
, скажете что здесь может быть?! Очень даже может. Самая подлянка зарыта как раз тут. У нас FTP хочет абсолютный путь к папке, а File Thingie подавай относительный от его места установки. Я решил эту проблему монтированием данных к папке с File Thingie. И в базе пишем относительный путь, для FTP просто прибавим недостающий кусок. И FTP_Write
. А штука здесь в том, что нам то надо системный ID пользователя, а у нас 0/1
. Я извернулся и сделал выборку так:
SELECT IF( Permissions.FTP_Write=1,"2001","33") as UiD...
Если 1, то вернеться 33, в противном случае 2001. 33 - это www-data, чьи и все данные, что бы можно было изменять через файлы из php. 2001 - пользователь FTP, просто что бы просматривать файлы. Немножко заморочено, но мне так больше нравиться.
Собственно давайте напишем sql запросы для FTP, все что было раньше в конфигурационном файле связанное с ними надо закоментить.
MYSQLGetPW SELECT Password FROM Users LEFT JOIN Permissions ON Users.Pid = Permissions.id WHERE User="\L" AND Status="1" AND FTP_Read="1" AND (IPAccess = "*" OR IPAccess LIKE "%\R%");
MYSQLGetUID SELECT IF( Permissions.FTP_Write=1,"2001","33") as UiD FROM Users LEFT JOIN Permissions ON Users.Pid = Permissions.id WHERE User="\L" AND status="1" AND FTP_Read="1" AND (ipaccess = "*" OR ipaccess LIKE "%\R%");
MYSQLGetGID SELECT IF( Permissions.FTP_Write=1,"2001","33") as UiD FROM Users LEFT JOIN Permissions ON Users.Pid = Permissions.id WHERE User="\L" AND status="1" AND FTP_Read="1" AND (ipaccess = "*" OR ipaccess LIKE "%\R%");
MYSQLGetDir SELECT CONCAT("/Path/to/Share/",Dir) as Dir FROM Users LEFT JOIN Groups ON Users.Gid = Groups.id LEFT JOIN Permissions ON Users.Pid = Permissions.id WHERE User="\L" AND Status="1" AND FTP_Read="1" AND (IPAccess = "*" OR IPAccess LIKE "%\R%")
Вот, не забудьте поменять путь /Path/to/Share/
на ваш в последней стр. FTP настроен. Можно зайти и проверить. Переходим к Web GUI. Прям напишем сразу код.
# Rewrite Users data
$ft['groups'] = array();
$ft['users'] = array();
# Create dir in new groups if not exist.
$ft["settings"]["CreateDIR"] = TRUE;
$ft["settings"]["DBHost"] = "localhost";
$ft["settings"]["DBUser"] = "";
$ft["settings"]["DBPass"] = "";
$ft["settings"]["DBName"] = "";
$ft["settings"]["Sql"] = "SELECT `User`,`Password`,`Groups`.`Name`,`Dir`,`Share_Action`,`Share_Create`,`Share_Upload`
FROM `Users`
LEFT JOIN Groups ON Users.Gid = Groups.id
LEFT JOIN Permissions ON Users.Pid = Permissions.id
WHERE `Users`.`Status` = '1' AND `Permissions`.`Share_Read` = '1';";
mysql_connect($ft["settings"]["DBHost"],$ft["settings"]["DBUser"],$ft["settings"]["DBPass"]) OR DIE("Can not connect!");
mysql_select_db($ft["settings"]["DBName"]) or die("Can not select DB");
$sql_res = mysql_query($ft["settings"]["Sql"]) or die("Can not execute query");
while ($row=mysql_fetch_array($sql_res)) {
$ft['users'][ $row['User'] ] = array(
'password' => $row['Password'],
'group' => $row['Name']
);
$FA = False; if($row['Share_Action']) $FA = True;
$CR = False; if($row['Share_Create']) $CR = True;
$UP = False; if($row['Share_Upload']) $UP = True;
if ( (file_exists($row['Dir']) == false) && $ft["settings"]["CreateDIR"] ) mkdir($row['Dir'], 0700,true);
$ft['groups'][$row['Name']] = array(
'DIR' => $row['Dir'],
'FILEACTIONS' => $FA,
'CREATE' => $CR,
'UPLOAD' => $UP
);
}
Вот и весь скрипт. Не забудте прописать Базу Данных. После надо в основной файл добавить include('name.php');
после всех настроек. Еще на всякий в FILEBLACKLIST добавить этоже имя файла.
Можно конечно закончить, если бы не одна "хня". А именно, когда мы удаляем пользователя, в то время как он листает каталоги, Он по волшебству ни черта не отрубается, а получает глобальные права. Клева - да? Я написал автору, так что ждем обновлений. А пока мы сделаем хак. По отключаем все глобальные права и поставим неправильный путь. Так что максимум что увидит отключеный пользователь - это "Не могу отобразить каталог".
# turn off admin
$ft["settings"]["DIR"] = "LoL";
$ft["settings"]["UPLOAD"] = FALSE;
$ft["settings"]["CREATE"] = FALSE;
$ft["settings"]["FILEACTIONS"] = FALSE;
Вот теперь полный порядок. Думаю на этом можно и закончить статью.